apex-code 0.0.1-alpha.8 → 0.0.4
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 +231 -0
- package/README.md +9 -8
- package/dist/cli/args.d.ts +26 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +72 -10
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/mcp-command.d.ts +9 -0
- package/dist/cli/mcp-command.d.ts.map +1 -0
- package/dist/cli/mcp-command.js +53 -0
- package/dist/cli/mcp-command.js.map +1 -0
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +3 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +2 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +94 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -18
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +3 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +183 -15
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +728 -117
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +7 -0
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/checkpoints/git-checkpoints.d.ts +39 -0
- package/dist/core/checkpoints/git-checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints/git-checkpoints.js +251 -0
- package/dist/core/checkpoints/git-checkpoints.js.map +1 -0
- package/dist/core/checkpoints/session-checkpoints.d.ts +23 -0
- package/dist/core/checkpoints/session-checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints/session-checkpoints.js +36 -0
- package/dist/core/checkpoints/session-checkpoints.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +7 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +9 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +59 -36
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/delegation/runtime.d.ts +6 -5
- package/dist/core/delegation/runtime.d.ts.map +1 -1
- package/dist/core/delegation/runtime.js +4 -3
- package/dist/core/delegation/runtime.js.map +1 -1
- package/dist/core/durable-state/sqlite.d.ts.map +1 -1
- package/dist/core/durable-state/sqlite.js +26 -2
- package/dist/core/durable-state/sqlite.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +5 -2
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +123 -50
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +45 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/source-runtime.d.ts +14 -0
- package/dist/core/extensions/source-runtime.d.ts.map +1 -0
- package/dist/core/extensions/source-runtime.js +56 -0
- package/dist/core/extensions/source-runtime.js.map +1 -0
- package/dist/core/extensions/types.d.ts +60 -11
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/formatter-lifecycle.d.ts +69 -0
- package/dist/core/formatter-lifecycle.d.ts.map +1 -0
- package/dist/core/formatter-lifecycle.js +360 -0
- package/dist/core/formatter-lifecycle.js.map +1 -0
- package/dist/core/hooks/command-handler.d.ts +19 -0
- package/dist/core/hooks/command-handler.d.ts.map +1 -0
- package/dist/core/hooks/command-handler.js +112 -0
- package/dist/core/hooks/command-handler.js.map +1 -0
- package/dist/core/hooks/http-handler.d.ts +10 -0
- package/dist/core/hooks/http-handler.d.ts.map +1 -0
- package/dist/core/hooks/http-handler.js +36 -0
- package/dist/core/hooks/http-handler.js.map +1 -0
- package/dist/core/hooks/loader.d.ts +29 -0
- package/dist/core/hooks/loader.d.ts.map +1 -0
- package/dist/core/hooks/loader.js +139 -0
- package/dist/core/hooks/loader.js.map +1 -0
- package/dist/core/hooks/runtime.d.ts +13 -0
- package/dist/core/hooks/runtime.d.ts.map +1 -0
- package/dist/core/hooks/runtime.js +63 -0
- package/dist/core/hooks/runtime.js.map +1 -0
- package/dist/core/hooks/types.d.ts +83 -0
- package/dist/core/hooks/types.d.ts.map +1 -0
- package/dist/core/hooks/types.js +24 -0
- package/dist/core/hooks/types.js.map +1 -0
- package/dist/core/keybindings.d.ts +20 -19
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/mcp/config.d.ts +16 -0
- package/dist/core/mcp/config.d.ts.map +1 -0
- package/dist/core/mcp/config.js +166 -0
- package/dist/core/mcp/config.js.map +1 -0
- package/dist/core/mcp/connector.d.ts +35 -0
- package/dist/core/mcp/connector.d.ts.map +1 -0
- package/dist/core/mcp/connector.js +105 -0
- package/dist/core/mcp/connector.js.map +1 -0
- package/dist/core/mcp/contract.d.ts +19 -0
- package/dist/core/mcp/contract.d.ts.map +1 -0
- package/dist/core/mcp/contract.js +89 -0
- package/dist/core/mcp/contract.js.map +1 -0
- package/dist/core/mcp/mcp-tool.d.ts +32 -0
- package/dist/core/mcp/mcp-tool.d.ts.map +1 -0
- package/dist/core/mcp/mcp-tool.js +171 -0
- package/dist/core/mcp/mcp-tool.js.map +1 -0
- package/dist/core/mcp/metadata-cache.d.ts +29 -0
- package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
- package/dist/core/mcp/metadata-cache.js +99 -0
- package/dist/core/mcp/metadata-cache.js.map +1 -0
- package/dist/core/mcp/oauth/authorize.d.ts +21 -0
- package/dist/core/mcp/oauth/authorize.d.ts.map +1 -0
- package/dist/core/mcp/oauth/authorize.js +32 -0
- package/dist/core/mcp/oauth/authorize.js.map +1 -0
- package/dist/core/mcp/oauth/discover.d.ts +27 -0
- package/dist/core/mcp/oauth/discover.d.ts.map +1 -0
- package/dist/core/mcp/oauth/discover.js +116 -0
- package/dist/core/mcp/oauth/discover.js.map +1 -0
- package/dist/core/mcp/oauth/flow.d.ts +31 -0
- package/dist/core/mcp/oauth/flow.d.ts.map +1 -0
- package/dist/core/mcp/oauth/flow.js +175 -0
- package/dist/core/mcp/oauth/flow.js.map +1 -0
- package/dist/core/mcp/oauth/mcp-token.d.ts +51 -0
- package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -0
- package/dist/core/mcp/oauth/mcp-token.js +129 -0
- package/dist/core/mcp/oauth/mcp-token.js.map +1 -0
- package/dist/core/mcp/oauth/pkce.d.ts +12 -0
- package/dist/core/mcp/oauth/pkce.d.ts.map +1 -0
- package/dist/core/mcp/oauth/pkce.js +11 -0
- package/dist/core/mcp/oauth/pkce.js.map +1 -0
- package/dist/core/mcp/oauth/register.d.ts +16 -0
- package/dist/core/mcp/oauth/register.d.ts.map +1 -0
- package/dist/core/mcp/oauth/register.js +42 -0
- package/dist/core/mcp/oauth/register.js.map +1 -0
- package/dist/core/mcp/oauth/token-client.d.ts +33 -0
- package/dist/core/mcp/oauth/token-client.d.ts.map +1 -0
- package/dist/core/mcp/oauth/token-client.js +65 -0
- package/dist/core/mcp/oauth/token-client.js.map +1 -0
- package/dist/core/mcp/runtime.d.ts +20 -0
- package/dist/core/mcp/runtime.d.ts.map +1 -0
- package/dist/core/mcp/runtime.js +37 -0
- package/dist/core/mcp/runtime.js.map +1 -0
- package/dist/core/mcp/schema.d.ts +22 -0
- package/dist/core/mcp/schema.d.ts.map +1 -0
- package/dist/core/mcp/schema.js +20 -0
- package/dist/core/mcp/schema.js.map +1 -0
- package/dist/core/mcp/server-manager.d.ts +43 -0
- package/dist/core/mcp/server-manager.d.ts.map +1 -0
- package/dist/core/mcp/server-manager.js +142 -0
- package/dist/core/mcp/server-manager.js.map +1 -0
- package/dist/core/mcp/types.d.ts +100 -0
- package/dist/core/mcp/types.d.ts.map +1 -0
- package/dist/core/mcp/types.js +8 -0
- package/dist/core/mcp/types.js.map +1 -0
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/gate.d.ts.map +1 -1
- package/dist/core/permissions/gate.js +45 -4
- package/dist/core/permissions/gate.js.map +1 -1
- package/dist/core/permissions/operations.d.ts +20 -0
- package/dist/core/permissions/operations.d.ts.map +1 -0
- package/dist/core/permissions/operations.js +9 -0
- package/dist/core/permissions/operations.js.map +1 -0
- package/dist/core/permissions/policy-command.d.ts +42 -0
- package/dist/core/permissions/policy-command.d.ts.map +1 -0
- package/dist/core/permissions/policy-command.js +44 -0
- package/dist/core/permissions/policy-command.js.map +1 -0
- package/dist/core/permissions/responder.d.ts +49 -2
- package/dist/core/permissions/responder.d.ts.map +1 -1
- package/dist/core/permissions/responder.js +20 -8
- package/dist/core/permissions/responder.js.map +1 -1
- package/dist/core/permissions/rules.d.ts.map +1 -1
- package/dist/core/permissions/rules.js +11 -1
- package/dist/core/permissions/rules.js.map +1 -1
- package/dist/core/permissions/store.d.ts +18 -0
- package/dist/core/permissions/store.d.ts.map +1 -1
- package/dist/core/permissions/store.js +89 -8
- package/dist/core/permissions/store.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/policy-executor.d.ts +47 -0
- package/dist/core/policy-executor.d.ts.map +1 -0
- package/dist/core/policy-executor.js +228 -0
- package/dist/core/policy-executor.js.map +1 -0
- package/dist/core/policy-loader.d.ts +72 -0
- package/dist/core/policy-loader.d.ts.map +1 -0
- package/dist/core/policy-loader.js +195 -0
- package/dist/core/policy-loader.js.map +1 -0
- package/dist/core/project-trust.d.ts +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sandbox/bwrap-arguments.d.ts +41 -0
- package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
- package/dist/core/sandbox/bwrap-arguments.js +136 -0
- package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
- package/dist/core/sandbox/child-entry.d.ts.map +1 -1
- package/dist/core/sandbox/child-entry.js +3 -1
- package/dist/core/sandbox/child-entry.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +84 -4
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +115 -16
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +50 -2
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sandbox/full-access.d.ts +28 -0
- package/dist/core/sandbox/full-access.d.ts.map +1 -0
- package/dist/core/sandbox/full-access.js +63 -0
- package/dist/core/sandbox/full-access.js.map +1 -0
- package/dist/core/sandbox/git-identity.d.ts +57 -0
- package/dist/core/sandbox/git-identity.d.ts.map +1 -0
- package/dist/core/sandbox/git-identity.js +78 -0
- package/dist/core/sandbox/git-identity.js.map +1 -0
- package/dist/core/sandbox/host-approval.d.ts +42 -0
- package/dist/core/sandbox/host-approval.d.ts.map +1 -0
- package/dist/core/sandbox/host-approval.js +99 -0
- package/dist/core/sandbox/host-approval.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +14 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
- package/dist/core/sandbox/linux-backend.js +158 -80
- package/dist/core/sandbox/linux-backend.js.map +1 -1
- package/dist/core/sandbox/macos-backend.d.ts +5 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
- package/dist/core/sandbox/macos-backend.js +164 -5
- package/dist/core/sandbox/macos-backend.js.map +1 -1
- package/dist/core/sandbox/network-proxy.d.ts +19 -0
- package/dist/core/sandbox/network-proxy.d.ts.map +1 -1
- package/dist/core/sandbox/network-proxy.js +93 -19
- package/dist/core/sandbox/network-proxy.js.map +1 -1
- package/dist/core/sandbox/policy.d.ts +8 -0
- package/dist/core/sandbox/policy.d.ts.map +1 -1
- package/dist/core/sandbox/policy.js +29 -12
- package/dist/core/sandbox/policy.js.map +1 -1
- package/dist/core/sandbox/profiles.d.ts +25 -0
- package/dist/core/sandbox/profiles.d.ts.map +1 -0
- package/dist/core/sandbox/profiles.js +23 -0
- package/dist/core/sandbox/profiles.js.map +1 -0
- package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
- package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/command-client.js +86 -0
- package/dist/core/sandbox/rpc/command-client.js.map +1 -0
- package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
- package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/command-proxy.js +120 -0
- package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -1
- package/dist/core/sandbox/rpc/credential-proxy.js +2 -96
- package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -1
- package/dist/core/sandbox/rpc/framing.d.ts +35 -0
- package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/framing.js +114 -0
- package/dist/core/sandbox/rpc/framing.js.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts +47 -0
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-helper.js +222 -0
- package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +66 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.js +209 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
- package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
- package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
- package/dist/core/sandbox/supervisor-temp.js +21 -0
- package/dist/core/sandbox/supervisor-temp.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +12 -1
- package/dist/core/sandbox/supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/supervisor.js +15 -1
- package/dist/core/sandbox/supervisor.js.map +1 -1
- package/dist/core/sandbox/terminal-handoff.d.ts +68 -0
- package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
- package/dist/core/sandbox/terminal-handoff.js +221 -0
- package/dist/core/sandbox/terminal-handoff.js.map +1 -0
- package/dist/core/sandbox/terminal-size.d.ts +13 -0
- package/dist/core/sandbox/terminal-size.d.ts.map +1 -1
- package/dist/core/sandbox/terminal-size.js +23 -2
- package/dist/core/sandbox/terminal-size.js.map +1 -1
- package/dist/core/sdk.d.ts +24 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +63 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts +13 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +34 -6
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +172 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +160 -11
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +8 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/background-shell.d.ts +39 -0
- package/dist/core/tools/background-shell.d.ts.map +1 -0
- package/dist/core/tools/background-shell.js +81 -0
- package/dist/core/tools/background-shell.js.map +1 -0
- package/dist/core/tools/bash-command-segments.d.ts.map +1 -1
- package/dist/core/tools/bash-command-segments.js +2 -0
- package/dist/core/tools/bash-command-segments.js.map +1 -1
- package/dist/core/tools/bash.d.ts +44 -16
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +264 -32
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/contract-snapshot.d.ts +58 -0
- package/dist/core/tools/contract-snapshot.d.ts.map +1 -0
- package/dist/core/tools/contract-snapshot.js +51 -0
- package/dist/core/tools/contract-snapshot.js.map +1 -0
- package/dist/core/tools/contract.d.ts +53 -5
- package/dist/core/tools/contract.d.ts.map +1 -1
- package/dist/core/tools/contract.js +22 -2
- package/dist/core/tools/contract.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +6 -4
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +21 -11
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +48 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +217 -8
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +8 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +72 -10
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +5 -2
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -2
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +14 -2
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +12 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -2
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/lsp.d.ts +19 -1
- package/dist/core/tools/lsp.d.ts.map +1 -1
- package/dist/core/tools/lsp.js +163 -10
- package/dist/core/tools/lsp.js.map +1 -1
- package/dist/core/tools/path-permission.d.ts +3 -0
- package/dist/core/tools/path-permission.d.ts.map +1 -1
- package/dist/core/tools/path-permission.js +16 -4
- package/dist/core/tools/path-permission.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +27 -2
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +221 -4
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +20 -11
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/test.d.ts +54 -9
- package/dist/core/tools/test.d.ts.map +1 -1
- package/dist/core/tools/test.js +239 -19
- package/dist/core/tools/test.js.map +1 -1
- package/dist/core/tools/write.d.ts +7 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +64 -9
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/verification-lifecycle.d.ts +77 -0
- package/dist/core/verification-lifecycle.d.ts.map +1 -0
- package/dist/core/verification-lifecycle.js +146 -0
- package/dist/core/verification-lifecycle.js.map +1 -0
- package/dist/core/workspace/artifacts.d.ts +45 -0
- package/dist/core/workspace/artifacts.d.ts.map +1 -0
- package/dist/core/workspace/artifacts.js +165 -0
- package/dist/core/workspace/artifacts.js.map +1 -0
- package/dist/core/workspace/comparison.d.ts +27 -0
- package/dist/core/workspace/comparison.d.ts.map +1 -0
- package/dist/core/workspace/comparison.js +94 -0
- package/dist/core/workspace/comparison.js.map +1 -0
- package/dist/core/workspace/git-observer.d.ts +48 -0
- package/dist/core/workspace/git-observer.d.ts.map +1 -0
- package/dist/core/workspace/git-observer.js +346 -0
- package/dist/core/workspace/git-observer.js.map +1 -0
- package/dist/core/workspace/projection.d.ts +3 -0
- package/dist/core/workspace/projection.d.ts.map +1 -0
- package/dist/core/workspace/projection.js +56 -0
- package/dist/core/workspace/projection.js.map +1 -0
- package/dist/core/workspace/state.d.ts +133 -0
- package/dist/core/workspace/state.d.ts.map +1 -0
- package/dist/core/workspace/state.js +96 -0
- package/dist/core/workspace/state.js.map +1 -0
- package/dist/extensions/llama/client.d.ts +6 -0
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +7 -0
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts +3 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +29 -4
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +6 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +58 -23
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/acp/server.d.ts +55 -0
- package/dist/modes/acp/server.d.ts.map +1 -0
- package/dist/modes/acp/server.js +244 -0
- package/dist/modes/acp/server.js.map +1 -0
- package/dist/modes/acp/translate.d.ts +37 -0
- package/dist/modes/acp/translate.d.ts.map +1 -0
- package/dist/modes/acp/translate.js +50 -0
- package/dist/modes/acp/translate.js.map +1 -0
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +29 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +182 -23
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +7 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +20 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +9 -4
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +20 -9
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +7 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +7 -3
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +19 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +70 -10
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +10 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +56 -14
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/permission-preview.d.ts +15 -0
- package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -0
- package/dist/modes/interactive/components/permission-preview.js +33 -0
- package/dist/modes/interactive/components/permission-preview.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +2 -2
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +9 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +23 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +64 -10
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +45 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +395 -43
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +12 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +20 -9
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +27 -12
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +12 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +196 -10
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +23 -9
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +6 -4
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +19 -0
- package/docs/custom-provider.md +12 -9
- package/docs/environment-variables.md +1 -1
- package/docs/extensions.md +35 -7
- package/docs/json.md +7 -2
- package/docs/keybindings.md +9 -9
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/quickstart.md +1 -1
- package/docs/rpc.md +32 -3
- package/docs/sdk.md +32 -2
- package/docs/settings.md +23 -7
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +23 -7
- package/docs/usage.md +10 -5
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +8 -4
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +24 -36
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +2 -2
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/permission-gate.ts +1 -1
- package/examples/extensions/plan-mode/index.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +439 -405
- package/package.json +17 -17
- package/dist/server/create-harness.d.ts +0 -26
- package/dist/server/create-harness.d.ts.map +0 -1
- package/dist/server/create-harness.js +0 -107
- package/dist/server/create-harness.js.map +0 -1
|
@@ -12,41 +12,51 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
16
|
-
import { basename, dirname } from "node:path";
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { basename, dirname, isAbsolute, relative } from "node:path";
|
|
17
17
|
import { contentText } from "@earendil-works/pi-ai";
|
|
18
18
|
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, } from "@earendil-works/pi-ai/compat";
|
|
19
19
|
import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
|
|
20
20
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
21
|
-
import { resolvePath } from "../utils/paths.js";
|
|
22
21
|
import { sleep } from "../utils/sleep.js";
|
|
23
22
|
import { normalizeToolResultImages } from "../utils/tool-result-images.js";
|
|
24
23
|
import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
|
|
25
24
|
import { executeBashWithOperations } from "./bash-executor.js";
|
|
25
|
+
import { createSessionCheckpoints } from "./checkpoints/session-checkpoints.js";
|
|
26
26
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
27
27
|
import { evictionBudget, installContextPipeline, isDefaultStreamFunction } from "./context/pipeline.js";
|
|
28
|
-
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
28
|
+
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
|
|
29
29
|
import { SessionEvidenceSink } from "./evidence.js";
|
|
30
30
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
31
31
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
32
32
|
import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
33
33
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
34
|
+
import { runFormatterCommand } from "./formatter-lifecycle.js";
|
|
34
35
|
import { ModelRegistry } from "./model-registry.js";
|
|
35
36
|
import { evaluateToolCall } from "./permissions/gate.js";
|
|
37
|
+
import { authorizeConfiguredCommand } from "./permissions/policy-command.js";
|
|
36
38
|
import { createInteractiveResponder } from "./permissions/responder.js";
|
|
37
39
|
import { resolveEffectiveModeWithOrigin } from "./permissions/startup.js";
|
|
40
|
+
import { loadPolicyConfiguration } from "./policy-loader.js";
|
|
38
41
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
39
|
-
import {
|
|
42
|
+
import { exportSessionToJsonl } from "./session-export.js";
|
|
43
|
+
import { getLatestCompactionEntry, TODO_CUSTOM_ENTRY_TYPE } from "./session-manager.js";
|
|
40
44
|
import { slugifySkillCommandName } from "./skills.js";
|
|
41
45
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
42
46
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
43
47
|
import { createLocalBashOperations } from "./tools/bash.js";
|
|
48
|
+
import { buildToolContractSnapshot, isUnclassifiedTool, } from "./tools/contract-snapshot.js";
|
|
44
49
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
45
50
|
import { createSkillSearchToolDefinition } from "./tools/skill-search.js";
|
|
46
51
|
import { createTodoWriteToolDefinition } from "./tools/todo-write.js";
|
|
47
52
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
48
53
|
import { createToolSchemaToolDefinition } from "./tools/tool-schema.js";
|
|
49
54
|
import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
|
|
55
|
+
import { VerificationTracker, } from "./verification-lifecycle.js";
|
|
56
|
+
import { buildWorkspaceComparison, compareWorkspaceObservations } from "./workspace/comparison.js";
|
|
57
|
+
import { observeWorkspaceGit } from "./workspace/git-observer.js";
|
|
58
|
+
import { formatWorkspaceProjection } from "./workspace/projection.js";
|
|
59
|
+
import { appendWorkspaceComparison, appendWorkspaceObservation, findLatestWorkspaceObservation, hasWorkspaceObservationAwaitingComparison, } from "./workspace/state.js";
|
|
50
60
|
/**
|
|
51
61
|
* Parse a skill block from message text.
|
|
52
62
|
* Returns null if the text doesn't contain a skill block.
|
|
@@ -77,14 +87,6 @@ function estimateMessagesTokens(messages) {
|
|
|
77
87
|
}
|
|
78
88
|
return tokens;
|
|
79
89
|
}
|
|
80
|
-
// ============================================================================
|
|
81
|
-
// Constants
|
|
82
|
-
// ============================================================================
|
|
83
|
-
/** Standard thinking levels */
|
|
84
|
-
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
|
|
85
|
-
// ============================================================================
|
|
86
|
-
// AgentSession Class
|
|
87
|
-
// ============================================================================
|
|
88
90
|
export class AgentSession {
|
|
89
91
|
agent;
|
|
90
92
|
sessionManager;
|
|
@@ -102,10 +104,13 @@ export class AgentSession {
|
|
|
102
104
|
_followUpMessages = [];
|
|
103
105
|
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
104
106
|
_pendingNextTurnMessages = [];
|
|
107
|
+
/** Context-only custom messages queued during a run, flushed once the current turn's tool results are in. */
|
|
108
|
+
_pendingCustomMessages = [];
|
|
105
109
|
// Compaction state
|
|
106
110
|
_compactionAbortController = undefined;
|
|
107
111
|
_autoCompactionAbortController = undefined;
|
|
108
112
|
_overflowRecoveryAttempted = false;
|
|
113
|
+
_midRunCompactionNeeded = false;
|
|
109
114
|
// Branch summarization state
|
|
110
115
|
_branchSummaryAbortController = undefined;
|
|
111
116
|
// Retry state
|
|
@@ -121,6 +126,21 @@ export class AgentSession {
|
|
|
121
126
|
_customTools;
|
|
122
127
|
_baseToolDefinitions = new Map();
|
|
123
128
|
_cwd;
|
|
129
|
+
/**
|
|
130
|
+
* Workspace observation adapter for compaction capture (WS.4). Injectable
|
|
131
|
+
* so tests can force capture outcomes without a real repository.
|
|
132
|
+
*/
|
|
133
|
+
_workspaceObserver = observeWorkspaceGit;
|
|
134
|
+
/**
|
|
135
|
+
* WS.5: one workspace comparison is due at the next model-turn boundary.
|
|
136
|
+
* Set after a compaction stored an observation, or on construction when
|
|
137
|
+
* the session's latest observation has no later comparison (resume).
|
|
138
|
+
*/
|
|
139
|
+
_workspaceComparePending = false;
|
|
140
|
+
/** VF.4: verification lifecycle state, built lazily from policy settings. */
|
|
141
|
+
_verificationTracker;
|
|
142
|
+
/** The workspace observation a verification result described, for staleness. */
|
|
143
|
+
_verificationBaseline;
|
|
124
144
|
_extensionRunnerRef;
|
|
125
145
|
_initialActiveToolNames;
|
|
126
146
|
_allowedToolNames;
|
|
@@ -131,6 +151,19 @@ export class AgentSession {
|
|
|
131
151
|
_evidenceSink;
|
|
132
152
|
_diagnosticsOperations;
|
|
133
153
|
_lspOperations;
|
|
154
|
+
_mcpRuntime;
|
|
155
|
+
/** Configured MCP subsystem, or undefined when no server is configured. */
|
|
156
|
+
get mcpRuntime() {
|
|
157
|
+
return this._mcpRuntime;
|
|
158
|
+
}
|
|
159
|
+
_checkpoints;
|
|
160
|
+
_hookRuntime;
|
|
161
|
+
_backgroundShellRegistry;
|
|
162
|
+
_permissionResponderFactory;
|
|
163
|
+
/** Worktree checkpoints for this session. Inert unless `checkpoints.enabled` is set. */
|
|
164
|
+
get checkpoints() {
|
|
165
|
+
return this._checkpoints;
|
|
166
|
+
}
|
|
134
167
|
_webSearchOperations;
|
|
135
168
|
_extensionUIContext;
|
|
136
169
|
_extensionMode = "print";
|
|
@@ -169,13 +202,33 @@ export class AgentSession {
|
|
|
169
202
|
this._evidenceSink = config.evidenceSink ?? new SessionEvidenceSink(this.sessionManager);
|
|
170
203
|
this._diagnosticsOperations = config.diagnosticsOperations;
|
|
171
204
|
this._lspOperations = config.lspOperations;
|
|
205
|
+
this._mcpRuntime = config.mcpRuntime;
|
|
172
206
|
this._webSearchOperations = config.webSearchOperations;
|
|
207
|
+
this._checkpoints = createSessionCheckpoints({
|
|
208
|
+
workspace: this._cwd,
|
|
209
|
+
sessionId: this.sessionManager.getSessionId(),
|
|
210
|
+
settings: config.checkpointSettings,
|
|
211
|
+
});
|
|
212
|
+
this._hookRuntime = config.hookRuntime;
|
|
213
|
+
this._backgroundShellRegistry = config.backgroundShellRegistry;
|
|
214
|
+
this._permissionResponderFactory = config.permissionResponderFactory;
|
|
215
|
+
this._workspaceComparePending = hasWorkspaceObservationAwaitingComparison(this.sessionManager);
|
|
173
216
|
// Always subscribe to agent events for internal handling
|
|
174
217
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
175
218
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
176
219
|
this._installAgentToolHooks();
|
|
177
220
|
this._installAgentNextTurnRefresh();
|
|
221
|
+
this._installMidRunCompactionHook();
|
|
178
222
|
this._installContextPipeline();
|
|
223
|
+
// Declarative hooks: session_start is observe-only and must neither delay
|
|
224
|
+
// nor fail construction, so it fires detached (emitObserve swallows
|
|
225
|
+
// handler failures already).
|
|
226
|
+
if (this._hookRuntime?.hasHandlers("session_start")) {
|
|
227
|
+
void this._hookRuntime.emitObserve("session_start", {
|
|
228
|
+
type: "session_start",
|
|
229
|
+
reason: this._sessionStartEvent.reason,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
179
232
|
this._buildRuntime({
|
|
180
233
|
activeToolNames: this._initialActiveToolNames,
|
|
181
234
|
includeAllExtensionTools: true,
|
|
@@ -266,6 +319,20 @@ export class AgentSession {
|
|
|
266
319
|
if (extensionResult?.block === true)
|
|
267
320
|
return extensionResult;
|
|
268
321
|
}
|
|
322
|
+
// Declarative hooks (spec 2026-08-31-declarative-hooks) ride the same
|
|
323
|
+
// seam, after extensions and before the gate. Restriction-only: a block
|
|
324
|
+
// short-circuits, allow and ask fall through, and the gate below still
|
|
325
|
+
// evaluates the call, so a hook can narrow authority but never widen it.
|
|
326
|
+
if (this._hookRuntime?.hasHandlers("tool_call")) {
|
|
327
|
+
const hookBlock = await this._hookRuntime.decideToolCall(toolCall.name, {
|
|
328
|
+
type: "tool_call",
|
|
329
|
+
toolName: toolCall.name,
|
|
330
|
+
toolCallId: toolCall.id,
|
|
331
|
+
input: args,
|
|
332
|
+
});
|
|
333
|
+
if (hookBlock)
|
|
334
|
+
return { block: true, reason: hookBlock.reason };
|
|
335
|
+
}
|
|
269
336
|
// Permission gate (roadmap Phase 2a, ADR 0004). Off by default — see
|
|
270
337
|
// AgentSessionConfig.permissionGate. When present, every registered tool
|
|
271
338
|
// call passes through it (contracts.md invariant 2): this is the seam,
|
|
@@ -274,9 +341,10 @@ export class AgentSession {
|
|
|
274
341
|
const decision = await evaluateToolCall(toolCall.name, args, {
|
|
275
342
|
...this._permissionGate,
|
|
276
343
|
getContract: (name) => this.getToolDefinition(name)?.contract,
|
|
277
|
-
getResponder: () => this.
|
|
278
|
-
|
|
279
|
-
|
|
344
|
+
getResponder: () => this._permissionResponderFactory?.() ??
|
|
345
|
+
(this._extensionMode === "tui" && this._extensionUIContext
|
|
346
|
+
? createInteractiveResponder(this._extensionUIContext)
|
|
347
|
+
: undefined),
|
|
280
348
|
});
|
|
281
349
|
if (decision.block)
|
|
282
350
|
return { block: true, reason: decision.reason };
|
|
@@ -314,6 +382,16 @@ export class AgentSession {
|
|
|
314
382
|
usage: result.usage,
|
|
315
383
|
})
|
|
316
384
|
: undefined;
|
|
385
|
+
// Declarative hooks observe tool results after extension rewrites and
|
|
386
|
+
// before image normalization; no decision is read from them (spec).
|
|
387
|
+
if (this._hookRuntime?.hasHandlers("tool_result")) {
|
|
388
|
+
await this._hookRuntime.emitObserve("tool_result", {
|
|
389
|
+
type: "tool_result",
|
|
390
|
+
toolName: toolCall.name,
|
|
391
|
+
toolCallId: toolCall.id,
|
|
392
|
+
input: args,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
317
395
|
const content = hookResult?.content ?? result.content ?? [];
|
|
318
396
|
// Runs after the extension hook so images injected or replaced by extensions are normalized too.
|
|
319
397
|
const normalizedContent = await normalizeToolResultImages(content, {
|
|
@@ -330,18 +408,33 @@ export class AgentSession {
|
|
|
330
408
|
};
|
|
331
409
|
};
|
|
332
410
|
}
|
|
411
|
+
async _compactBeforeNextAssistantResponse(context) {
|
|
412
|
+
const model = this.model;
|
|
413
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
414
|
+
if (!model ||
|
|
415
|
+
model.contextWindow <= 0 ||
|
|
416
|
+
!shouldCompact(estimateContextTokens(context.messages).tokens, model.contextWindow, settings)) {
|
|
417
|
+
return context;
|
|
418
|
+
}
|
|
419
|
+
await this._runAutoCompaction("threshold", false);
|
|
420
|
+
return {
|
|
421
|
+
...context,
|
|
422
|
+
messages: this.agent.state.messages.slice(),
|
|
423
|
+
};
|
|
424
|
+
}
|
|
333
425
|
_installAgentNextTurnRefresh() {
|
|
334
426
|
const previousPrepareNextTurnWithContext = this.agent.prepareNextTurnWithContext ??
|
|
335
427
|
(this.agent.prepareNextTurn
|
|
336
428
|
? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
|
|
337
429
|
: undefined);
|
|
338
430
|
this.agent.prepareNextTurnWithContext = async (turn, signal) => {
|
|
339
|
-
const
|
|
340
|
-
const
|
|
431
|
+
const context = await this._compactBeforeNextAssistantResponse(turn.context);
|
|
432
|
+
const previousSnapshot = await previousPrepareNextTurnWithContext?.({ ...turn, context }, signal);
|
|
433
|
+
const nextContext = previousSnapshot?.context ?? context;
|
|
341
434
|
return {
|
|
342
435
|
...previousSnapshot,
|
|
343
436
|
context: {
|
|
344
|
-
...
|
|
437
|
+
...nextContext,
|
|
345
438
|
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
346
439
|
tools: this.agent.state.tools.slice(),
|
|
347
440
|
},
|
|
@@ -350,6 +443,35 @@ export class AgentSession {
|
|
|
350
443
|
};
|
|
351
444
|
};
|
|
352
445
|
}
|
|
446
|
+
/** Stop an over-threshold tool loop before it sends another provider request. */
|
|
447
|
+
_installMidRunCompactionHook() {
|
|
448
|
+
const previousShouldStopAfterTurn = this.agent.shouldStopAfterTurn;
|
|
449
|
+
this.agent.shouldStopAfterTurn = async (turn, signal) => {
|
|
450
|
+
if (await previousShouldStopAfterTurn?.(turn, signal)) {
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
return this._shouldStopForMidRunCompaction(turn);
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
_shouldStopForMidRunCompaction(turn) {
|
|
457
|
+
if (!turn.hasMoreToolCalls || turn.toolResults.length === 0) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
461
|
+
if (!settings.enabled) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
const contextWindow = this.model?.contextWindow ?? 0;
|
|
465
|
+
if (contextWindow <= 0) {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
const contextTokens = estimateContextTokens(turn.context.messages).tokens;
|
|
469
|
+
if (!shouldCompact(contextTokens, contextWindow, settings)) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
this._midRunCompactionNeeded = true;
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
353
475
|
/**
|
|
354
476
|
* Installs the context pipeline settled in `docs/architecture/contracts.md` § 2:
|
|
355
477
|
* deferred-schema resolution → tool-result eviction → compaction. Compaction
|
|
@@ -406,6 +528,11 @@ export class AgentSession {
|
|
|
406
528
|
followUp: [...this._followUpMessages],
|
|
407
529
|
});
|
|
408
530
|
}
|
|
531
|
+
async _emitSessionCompactFailed(event) {
|
|
532
|
+
if (this._extensionRunner.hasHandlers("session_compact_failed")) {
|
|
533
|
+
await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
409
536
|
_getIdleWaitPromise() {
|
|
410
537
|
if (!this._idleWaitPromise) {
|
|
411
538
|
this._idleWaitPromise = new Promise((resolve) => {
|
|
@@ -496,6 +623,14 @@ export class AgentSession {
|
|
|
496
623
|
}
|
|
497
624
|
}
|
|
498
625
|
}
|
|
626
|
+
// A turn ends after its assistant message and every tool result has been appended,
|
|
627
|
+
// so this is the first point in the run where a context-only custom message can be
|
|
628
|
+
// inserted without landing between a tool call and its result. Flushing after the
|
|
629
|
+
// extension and listener dispatch above also picks up messages that turn_end
|
|
630
|
+
// handlers queued.
|
|
631
|
+
if (event.type === "turn_end") {
|
|
632
|
+
this._flushPendingCustomMessages();
|
|
633
|
+
}
|
|
499
634
|
};
|
|
500
635
|
_willRetryAfterAgentEnd(event) {
|
|
501
636
|
const settings = this.settingsManager.getRetrySettings();
|
|
@@ -545,6 +680,18 @@ export class AgentSession {
|
|
|
545
680
|
await this._extensionRunner.emit({ type: "agent_end", messages: event.messages });
|
|
546
681
|
}
|
|
547
682
|
else if (event.type === "turn_start") {
|
|
683
|
+
// Snapshot before the model gets to act, keyed to the entry `/tree` and `/fork`
|
|
684
|
+
// already navigate to, so the two agree about what an entry means.
|
|
685
|
+
//
|
|
686
|
+
// Awaited, despite the turn latency. Detached, the snapshot races the turn it is
|
|
687
|
+
// supposed to precede: a tool call can edit the worktree while `add -A` is still
|
|
688
|
+
// walking it, and the ref then holds mid-turn state that no `/fork` should ever
|
|
689
|
+
// restore. "The state before the model acted" is the whole guarantee, so paying
|
|
690
|
+
// for it here is the point. The engine's own timeout bounds the wait and a failed
|
|
691
|
+
// capture is never fatal.
|
|
692
|
+
const leafEntryId = this.sessionManager.getLeafEntry()?.id;
|
|
693
|
+
if (leafEntryId)
|
|
694
|
+
await this._checkpoints.capture(leafEntryId);
|
|
548
695
|
const extensionEvent = {
|
|
549
696
|
type: "turn_start",
|
|
550
697
|
turnIndex: this._turnIndex,
|
|
@@ -560,6 +707,9 @@ export class AgentSession {
|
|
|
560
707
|
toolResults: event.toolResults,
|
|
561
708
|
};
|
|
562
709
|
await this._extensionRunner.emit(extensionEvent);
|
|
710
|
+
if (this._hookRuntime?.hasHandlers("turn_end")) {
|
|
711
|
+
await this._hookRuntime.emitObserve("turn_end", { type: "turn_end" });
|
|
712
|
+
}
|
|
563
713
|
this._turnIndex++;
|
|
564
714
|
}
|
|
565
715
|
else if (event.type === "message_start") {
|
|
@@ -659,6 +809,9 @@ export class AgentSession {
|
|
|
659
809
|
this.abortBranchSummary();
|
|
660
810
|
this.abortBash();
|
|
661
811
|
this.agent.abort();
|
|
812
|
+
// Background shell children (spec 2026-08-31-background-shell.md) must
|
|
813
|
+
// never outlive the session that launched them.
|
|
814
|
+
this._backgroundShellRegistry?.dispose();
|
|
662
815
|
}
|
|
663
816
|
catch {
|
|
664
817
|
// Dispose must succeed even if an abort hook throws.
|
|
@@ -666,6 +819,9 @@ export class AgentSession {
|
|
|
666
819
|
this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().");
|
|
667
820
|
this._disconnectFromAgent();
|
|
668
821
|
this._eventListeners = [];
|
|
822
|
+
// Every connected MCP server is a child process this session started. Without
|
|
823
|
+
// this, one leaks per server per session, because nothing else owns them.
|
|
824
|
+
this._mcpRuntime?.close().catch(() => undefined);
|
|
669
825
|
cleanupSessionResources(this.sessionId);
|
|
670
826
|
}
|
|
671
827
|
// =========================================================================
|
|
@@ -709,6 +865,13 @@ export class AgentSession {
|
|
|
709
865
|
/**
|
|
710
866
|
* Get all configured tools with name, description, parameter schema, prompt guidelines, and source metadata.
|
|
711
867
|
*/
|
|
868
|
+
/**
|
|
869
|
+
* The ADR 0010 projection over this session's registry: the one read every surface
|
|
870
|
+
* that *describes* the tool set consumes. Never an authorization input.
|
|
871
|
+
*/
|
|
872
|
+
getToolContractSnapshot() {
|
|
873
|
+
return buildToolContractSnapshot(Array.from(this._toolDefinitions.values()).map(({ definition }) => definition));
|
|
874
|
+
}
|
|
712
875
|
getAllTools() {
|
|
713
876
|
const schemaToolActive = this.getActiveToolNames().includes("tool_schema");
|
|
714
877
|
return Array.from(this._toolDefinitions.values())
|
|
@@ -719,7 +882,7 @@ export class AgentSession {
|
|
|
719
882
|
parameters: definition.parameters,
|
|
720
883
|
promptGuidelines: definition.promptGuidelines,
|
|
721
884
|
sourceInfo,
|
|
722
|
-
unclassified:
|
|
885
|
+
unclassified: isUnclassifiedTool(definition),
|
|
723
886
|
}));
|
|
724
887
|
}
|
|
725
888
|
getToolDefinition(name) {
|
|
@@ -855,10 +1018,15 @@ export class AgentSession {
|
|
|
855
1018
|
finally {
|
|
856
1019
|
this._systemPromptOverride = undefined;
|
|
857
1020
|
this._flushPendingBashMessages();
|
|
1021
|
+
this._flushPendingCustomMessages();
|
|
858
1022
|
await this._emitAgentSettled();
|
|
859
1023
|
}
|
|
860
1024
|
}
|
|
861
1025
|
async _handlePostAgentRun() {
|
|
1026
|
+
if (this._midRunCompactionNeeded) {
|
|
1027
|
+
this._midRunCompactionNeeded = false;
|
|
1028
|
+
return await this._runAutoCompaction("threshold", true);
|
|
1029
|
+
}
|
|
862
1030
|
const msg = this._lastAssistantMessage;
|
|
863
1031
|
this._lastAssistantMessage = undefined;
|
|
864
1032
|
if (!msg) {
|
|
@@ -944,8 +1112,9 @@ export class AgentSession {
|
|
|
944
1112
|
preflightResult?.(true);
|
|
945
1113
|
return;
|
|
946
1114
|
}
|
|
947
|
-
// Flush any pending bash messages before the new prompt
|
|
1115
|
+
// Flush any pending bash and custom messages before the new prompt
|
|
948
1116
|
this._flushPendingBashMessages();
|
|
1117
|
+
this._flushPendingCustomMessages();
|
|
949
1118
|
// Validate model
|
|
950
1119
|
if (!this.model) {
|
|
951
1120
|
throw new Error(formatNoModelSelectedMessage());
|
|
@@ -1019,7 +1188,9 @@ export class AgentSession {
|
|
|
1019
1188
|
return;
|
|
1020
1189
|
}
|
|
1021
1190
|
preflightResult?.(true);
|
|
1191
|
+
await this._runWorkspaceComparisonBoundary();
|
|
1022
1192
|
await this._runAgentPrompt(messages);
|
|
1193
|
+
await this._runVerificationTurnBoundary();
|
|
1023
1194
|
}
|
|
1024
1195
|
/**
|
|
1025
1196
|
* Try to execute an extension command. Returns true if command was found and executed.
|
|
@@ -1166,8 +1337,9 @@ export class AgentSession {
|
|
|
1166
1337
|
/**
|
|
1167
1338
|
* Send a custom message to the session. Creates a CustomMessageEntry.
|
|
1168
1339
|
*
|
|
1169
|
-
* Handles
|
|
1340
|
+
* Handles four cases:
|
|
1170
1341
|
* - Streaming: queues message, processed when loop pulls from queue
|
|
1342
|
+
* - Streaming + triggerTurn false: appended to state/session once the current turn ends
|
|
1171
1343
|
* - Not streaming + triggerTurn: appends to state/session, starts new turn
|
|
1172
1344
|
* - Not streaming + no trigger: appends to state/session, no turn
|
|
1173
1345
|
*
|
|
@@ -1199,11 +1371,34 @@ export class AgentSession {
|
|
|
1199
1371
|
else if (options?.triggerTurn) {
|
|
1200
1372
|
await this._runAgentPrompt(appMessage);
|
|
1201
1373
|
}
|
|
1374
|
+
else if (this.isStreaming) {
|
|
1375
|
+
// Appending now would put the message between an assistant tool call and its
|
|
1376
|
+
// result, which providers that validate message order reject on replay. Defer
|
|
1377
|
+
// to the end of the turn. Nothing is emitted yet: message events must not
|
|
1378
|
+
// describe messages the session tree does not contain.
|
|
1379
|
+
this._pendingCustomMessages.push(appMessage);
|
|
1380
|
+
}
|
|
1202
1381
|
else {
|
|
1203
|
-
this.
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1382
|
+
this._appendCustomMessage(appMessage);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
_appendCustomMessage(appMessage) {
|
|
1386
|
+
this.agent.state.messages.push(appMessage);
|
|
1387
|
+
this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details);
|
|
1388
|
+
this._emit({ type: "message_start", message: appMessage });
|
|
1389
|
+
this._emit({ type: "message_end", message: appMessage });
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Append custom messages queued while the agent was running.
|
|
1393
|
+
* Called once the current turn's tool results are in agent state and session history.
|
|
1394
|
+
*/
|
|
1395
|
+
_flushPendingCustomMessages() {
|
|
1396
|
+
if (this._pendingCustomMessages.length === 0)
|
|
1397
|
+
return;
|
|
1398
|
+
const pending = this._pendingCustomMessages;
|
|
1399
|
+
this._pendingCustomMessages = [];
|
|
1400
|
+
for (const appMessage of pending) {
|
|
1401
|
+
this._appendCustomMessage(appMessage);
|
|
1207
1402
|
}
|
|
1208
1403
|
}
|
|
1209
1404
|
/**
|
|
@@ -1328,35 +1523,55 @@ export class AgentSession {
|
|
|
1328
1523
|
}
|
|
1329
1524
|
/**
|
|
1330
1525
|
* Set model directly.
|
|
1331
|
-
* Validates that auth is configured
|
|
1526
|
+
* Validates that auth is configured and saves to the session transcript.
|
|
1527
|
+
* Persists to global defaults only when options.persist is true.
|
|
1332
1528
|
* @throws Error if no auth is configured for the model
|
|
1333
1529
|
*/
|
|
1334
|
-
async setModel(model) {
|
|
1530
|
+
async setModel(model, options = {}) {
|
|
1335
1531
|
if (!(await this._modelRuntime.checkAuth(model.provider))) {
|
|
1336
1532
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
1337
1533
|
}
|
|
1338
1534
|
const previousModel = this.model;
|
|
1339
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1535
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
|
|
1340
1536
|
this.agent.state.model = model;
|
|
1341
1537
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1342
|
-
|
|
1343
|
-
|
|
1538
|
+
if (options.persist) {
|
|
1539
|
+
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1540
|
+
this._addPersistedDefaultToNonEmptyScope(model);
|
|
1541
|
+
}
|
|
1542
|
+
// Apply thinking level for the new model.
|
|
1543
|
+
// Per-model thinking level overrides take priority over the global default.
|
|
1544
|
+
// Model persistence does not implicitly rewrite the global thinking default.
|
|
1344
1545
|
this.setThinkingLevel(thinkingLevel);
|
|
1345
1546
|
await this._emitModelSelect(model, previousModel, "set");
|
|
1346
1547
|
}
|
|
1548
|
+
_addPersistedDefaultToNonEmptyScope(model) {
|
|
1549
|
+
if (this._scopedModels.length === 0)
|
|
1550
|
+
return;
|
|
1551
|
+
if (this._scopedModels.some((scoped) => modelsAreEqual(scoped.model, model)))
|
|
1552
|
+
return;
|
|
1553
|
+
this._scopedModels = [...this._scopedModels, { model }];
|
|
1554
|
+
const enabledModels = this.settingsManager.getEnabledModels();
|
|
1555
|
+
if (!enabledModels?.length)
|
|
1556
|
+
return;
|
|
1557
|
+
const modelReference = `${model.provider}/${model.id}`;
|
|
1558
|
+
if (enabledModels.some((pattern) => pattern.toLowerCase() === modelReference.toLowerCase()))
|
|
1559
|
+
return;
|
|
1560
|
+
this.settingsManager.setEnabledModels([...enabledModels, modelReference]);
|
|
1561
|
+
}
|
|
1347
1562
|
/**
|
|
1348
1563
|
* Cycle to next/previous model.
|
|
1349
1564
|
* Uses scoped models (from --models flag) if available, otherwise all available models.
|
|
1350
1565
|
* @param direction - "forward" (default) or "backward"
|
|
1351
1566
|
* @returns The new model info, or undefined if only one model available
|
|
1352
1567
|
*/
|
|
1353
|
-
async cycleModel(direction = "forward") {
|
|
1568
|
+
async cycleModel(direction = "forward", options = {}) {
|
|
1354
1569
|
if (this._scopedModels.length > 0) {
|
|
1355
|
-
return this._cycleScopedModel(direction);
|
|
1570
|
+
return this._cycleScopedModel(direction, options);
|
|
1356
1571
|
}
|
|
1357
|
-
return this._cycleAvailableModel(direction);
|
|
1572
|
+
return this._cycleAvailableModel(direction, options);
|
|
1358
1573
|
}
|
|
1359
|
-
async _cycleScopedModel(direction) {
|
|
1574
|
+
async _cycleScopedModel(direction, options) {
|
|
1360
1575
|
const availableIds = new Set(this._modelRuntime.getAvailableSnapshot().map((model) => `${model.provider}\0${model.id}`));
|
|
1361
1576
|
const scopedModels = this._scopedModels.filter((scoped) => availableIds.has(`${scoped.model.provider}\0${scoped.model.id}`));
|
|
1362
1577
|
if (scopedModels.length <= 1)
|
|
@@ -1368,20 +1583,24 @@ export class AgentSession {
|
|
|
1368
1583
|
const len = scopedModels.length;
|
|
1369
1584
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1370
1585
|
const next = scopedModels[nextIndex];
|
|
1371
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
|
|
1586
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
|
|
1372
1587
|
// Apply model
|
|
1373
1588
|
this.agent.state.model = next.model;
|
|
1374
1589
|
this.sessionManager.appendModelChange(next.model.provider, next.model.id);
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1590
|
+
if (options.persist) {
|
|
1591
|
+
this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
|
|
1592
|
+
this._addPersistedDefaultToNonEmptyScope(next.model);
|
|
1593
|
+
}
|
|
1594
|
+
// Apply thinking level for the new model.
|
|
1595
|
+
// - Explicit scoped model thinking level overrides defaults
|
|
1596
|
+
// - Per-model thinking level overrides take priority over the global default
|
|
1379
1597
|
// setThinkingLevel clamps to model capabilities.
|
|
1598
|
+
// Model persistence does not implicitly rewrite the global thinking default.
|
|
1380
1599
|
this.setThinkingLevel(thinkingLevel);
|
|
1381
1600
|
await this._emitModelSelect(next.model, currentModel, "cycle");
|
|
1382
1601
|
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
1383
1602
|
}
|
|
1384
|
-
async _cycleAvailableModel(direction) {
|
|
1603
|
+
async _cycleAvailableModel(direction, options) {
|
|
1385
1604
|
const availableModels = this._modelRuntime.getAvailableSnapshot();
|
|
1386
1605
|
if (availableModels.length <= 1)
|
|
1387
1606
|
return undefined;
|
|
@@ -1392,11 +1611,15 @@ export class AgentSession {
|
|
|
1392
1611
|
const len = availableModels.length;
|
|
1393
1612
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1394
1613
|
const nextModel = availableModels[nextIndex];
|
|
1395
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1614
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
|
|
1396
1615
|
this.agent.state.model = nextModel;
|
|
1397
1616
|
this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
|
|
1398
|
-
|
|
1399
|
-
|
|
1617
|
+
if (options.persist) {
|
|
1618
|
+
this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
|
|
1619
|
+
this._addPersistedDefaultToNonEmptyScope(nextModel);
|
|
1620
|
+
}
|
|
1621
|
+
// Apply thinking level for the new model.
|
|
1622
|
+
// Model persistence does not implicitly rewrite the global thinking default.
|
|
1400
1623
|
this.setThinkingLevel(thinkingLevel);
|
|
1401
1624
|
await this._emitModelSelect(nextModel, currentModel, "cycle");
|
|
1402
1625
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
@@ -1407,20 +1630,21 @@ export class AgentSession {
|
|
|
1407
1630
|
/**
|
|
1408
1631
|
* Set thinking level.
|
|
1409
1632
|
* Clamps to model capabilities based on available thinking levels.
|
|
1410
|
-
* Saves to session
|
|
1633
|
+
* Saves the clamped level to the session transcript only if the level actually changes.
|
|
1634
|
+
* Persists the requested level to global defaults only when options.persist is true.
|
|
1411
1635
|
*/
|
|
1412
|
-
setThinkingLevel(level) {
|
|
1636
|
+
setThinkingLevel(level, options = {}) {
|
|
1413
1637
|
const availableLevels = this.getAvailableThinkingLevels();
|
|
1414
1638
|
const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
|
|
1415
1639
|
// Only persist if actually changing
|
|
1416
1640
|
const previousLevel = this.agent.state.thinkingLevel;
|
|
1417
1641
|
const isChanging = effectiveLevel !== previousLevel;
|
|
1418
1642
|
this.agent.state.thinkingLevel = effectiveLevel;
|
|
1643
|
+
if (options.persist) {
|
|
1644
|
+
this.settingsManager.setDefaultThinkingLevel(level);
|
|
1645
|
+
}
|
|
1419
1646
|
if (isChanging) {
|
|
1420
1647
|
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
1421
|
-
if (this.supportsThinking() || effectiveLevel !== "off") {
|
|
1422
|
-
this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
|
|
1423
|
-
}
|
|
1424
1648
|
this._emit({ type: "thinking_level_changed", level: effectiveLevel });
|
|
1425
1649
|
void this._extensionRunner.emit({
|
|
1426
1650
|
type: "thinking_level_select",
|
|
@@ -1433,14 +1657,14 @@ export class AgentSession {
|
|
|
1433
1657
|
* Cycle to next thinking level.
|
|
1434
1658
|
* @returns New level, or undefined if model doesn't support thinking
|
|
1435
1659
|
*/
|
|
1436
|
-
cycleThinkingLevel() {
|
|
1660
|
+
cycleThinkingLevel(options = {}) {
|
|
1437
1661
|
if (!this.supportsThinking())
|
|
1438
1662
|
return undefined;
|
|
1439
1663
|
const levels = this.getAvailableThinkingLevels();
|
|
1440
1664
|
const currentIndex = levels.indexOf(this.thinkingLevel);
|
|
1441
1665
|
const nextIndex = (currentIndex + 1) % levels.length;
|
|
1442
1666
|
const nextLevel = levels[nextIndex];
|
|
1443
|
-
this.setThinkingLevel(nextLevel);
|
|
1667
|
+
this.setThinkingLevel(nextLevel, options);
|
|
1444
1668
|
return nextLevel;
|
|
1445
1669
|
}
|
|
1446
1670
|
/**
|
|
@@ -1449,7 +1673,7 @@ export class AgentSession {
|
|
|
1449
1673
|
*/
|
|
1450
1674
|
getAvailableThinkingLevels() {
|
|
1451
1675
|
if (!this.model)
|
|
1452
|
-
return
|
|
1676
|
+
return [...THINKING_LEVEL_OPTIONS];
|
|
1453
1677
|
return getSupportedThinkingLevels(this.model);
|
|
1454
1678
|
}
|
|
1455
1679
|
/**
|
|
@@ -1458,14 +1682,18 @@ export class AgentSession {
|
|
|
1458
1682
|
supportsThinking() {
|
|
1459
1683
|
return !!this.model?.reasoning;
|
|
1460
1684
|
}
|
|
1461
|
-
_getThinkingLevelForModelSwitch(explicitLevel) {
|
|
1685
|
+
_getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
|
|
1462
1686
|
if (explicitLevel !== undefined) {
|
|
1463
1687
|
return explicitLevel;
|
|
1464
1688
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1689
|
+
// Per-model default takes priority when switching to a model that has one
|
|
1690
|
+
if (targetModel) {
|
|
1691
|
+
const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
|
|
1692
|
+
if (perModel !== undefined) {
|
|
1693
|
+
return perModel;
|
|
1694
|
+
}
|
|
1467
1695
|
}
|
|
1468
|
-
return this.thinkingLevel;
|
|
1696
|
+
return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
|
|
1469
1697
|
}
|
|
1470
1698
|
_clampThinkingLevel(level, _availableLevels) {
|
|
1471
1699
|
return this.model ? clampThinkingLevel(this.model, level) : "off";
|
|
@@ -1496,15 +1724,232 @@ export class AgentSession {
|
|
|
1496
1724
|
// =========================================================================
|
|
1497
1725
|
// Compaction
|
|
1498
1726
|
// =========================================================================
|
|
1727
|
+
/** Generate Pi's built-in compaction summary for manual and automatic compaction. */
|
|
1728
|
+
/**
|
|
1729
|
+
* Attempt exactly one workspace observation for a compaction (spec
|
|
1730
|
+
* 2026-09-01-harness-correctness-and-workspace-state.md § 3 step 1).
|
|
1731
|
+
* Returns undefined when observation is impossible; never throws and
|
|
1732
|
+
* never fails the surrounding compaction.
|
|
1733
|
+
*/
|
|
1734
|
+
async _captureWorkspaceObservation(signal) {
|
|
1735
|
+
try {
|
|
1736
|
+
return await this._workspaceObserver(this._cwd, {
|
|
1737
|
+
signal,
|
|
1738
|
+
// The session's own state directory is harness bookkeeping, not
|
|
1739
|
+
// workspace content; observing it would report every turn as drift.
|
|
1740
|
+
excludePaths: this._sessionExclusionPaths(),
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
catch {
|
|
1744
|
+
return undefined;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
/** Session/artifact directories inside the workspace, relative paths. */
|
|
1748
|
+
_sessionExclusionPaths() {
|
|
1749
|
+
const exclusions = [];
|
|
1750
|
+
const sessionDir = this.sessionManager.getSessionDir();
|
|
1751
|
+
if (sessionDir) {
|
|
1752
|
+
const rel = relative(this._cwd, sessionDir);
|
|
1753
|
+
if (rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel))
|
|
1754
|
+
exclusions.push(rel);
|
|
1755
|
+
}
|
|
1756
|
+
// Harness runtime bookkeeping (auth, model store, durable state) is
|
|
1757
|
+
// not workspace content either: when the agent directory sits inside
|
|
1758
|
+
// the workspace, its runtime files would report every turn as drift.
|
|
1759
|
+
// Same rule and same name list the fork/workspace fingerprints use.
|
|
1760
|
+
const agentDir = this.settingsManager.getAgentDir();
|
|
1761
|
+
if (agentDir !== undefined) {
|
|
1762
|
+
const rel = relative(this._cwd, agentDir);
|
|
1763
|
+
if (rel === "") {
|
|
1764
|
+
exclusions.push("auth.json", "models-store.json", "state.sqlite");
|
|
1765
|
+
}
|
|
1766
|
+
else if (rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel)) {
|
|
1767
|
+
exclusions.push(rel);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
return exclusions;
|
|
1771
|
+
}
|
|
1772
|
+
// ── VF.4: verification lifecycle (spec
|
|
1773
|
+
// 2026-09-01-configured-verification-and-formatting.md § 4) ──────────
|
|
1774
|
+
/**
|
|
1775
|
+
* Build (once) and refresh the tracker from the raw per-source policy
|
|
1776
|
+
* settings. ADR 0028: the loader consumes the sources separately and the
|
|
1777
|
+
* trust gate decides whether the project source exists at all.
|
|
1778
|
+
*/
|
|
1779
|
+
/**
|
|
1780
|
+
* PS.1. Configured commands reach the same permission authority tool calls
|
|
1781
|
+
* use. Undefined only when the session has no gate at all, which is the
|
|
1782
|
+
* documented SDK default; a gated session always authorizes.
|
|
1783
|
+
*/
|
|
1784
|
+
_authorizeConfiguredCommand() {
|
|
1785
|
+
const gate = this._permissionGate;
|
|
1786
|
+
if (gate === undefined)
|
|
1787
|
+
return undefined;
|
|
1788
|
+
return (operation) => authorizeConfiguredCommand(operation, {
|
|
1789
|
+
getMode: gate.getMode,
|
|
1790
|
+
getResponder: () => this._permissionResponderFactory?.() ??
|
|
1791
|
+
(this._extensionMode === "tui" && this._extensionUIContext
|
|
1792
|
+
? createInteractiveResponder(this._extensionUIContext)
|
|
1793
|
+
: undefined),
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
_ensureVerificationTracker() {
|
|
1797
|
+
if (this._verificationTracker === undefined) {
|
|
1798
|
+
this._verificationTracker = new VerificationTracker({
|
|
1799
|
+
workspaceRoot: this._cwd,
|
|
1800
|
+
...(this._authorizeConfiguredCommand() !== undefined
|
|
1801
|
+
? { authorize: this._authorizeConfiguredCommand() }
|
|
1802
|
+
: {}),
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
const snapshot = loadPolicyConfiguration({
|
|
1806
|
+
globalSettings: this.settingsManager.getGlobalSettings().policies,
|
|
1807
|
+
projectSettings: this.settingsManager.getProjectSettings().policies,
|
|
1808
|
+
projectTrusted: this.settingsManager.isProjectTrusted(),
|
|
1809
|
+
});
|
|
1810
|
+
const boundary = this.settingsManager.getPolicySettings()?.boundary === "post-turn" ? "post-turn" : "explicit";
|
|
1811
|
+
this._verificationTracker.configure(snapshot.verification, boundary);
|
|
1812
|
+
return this._verificationTracker;
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Run one configured verification policy now and report the resulting
|
|
1816
|
+
* completion status. Unavailable when nothing is configured; the
|
|
1817
|
+
* workspace observation after the run becomes the staleness baseline.
|
|
1818
|
+
*/
|
|
1819
|
+
async requestVerification(options = {}) {
|
|
1820
|
+
const tracker = this._ensureVerificationTracker();
|
|
1821
|
+
if (tracker.policyIds().length === 0) {
|
|
1822
|
+
return "unavailable";
|
|
1823
|
+
}
|
|
1824
|
+
const record = await tracker.runExplicit(options.policyId, { signal: options.signal });
|
|
1825
|
+
if (record !== undefined) {
|
|
1826
|
+
this._verificationBaseline = await this._captureWorkspaceObservation(options.signal);
|
|
1827
|
+
}
|
|
1828
|
+
return this.verificationStatus();
|
|
1829
|
+
}
|
|
1830
|
+
/**
|
|
1831
|
+
* VF.5: run one configured formatter policy now and report what it
|
|
1832
|
+
* actually mutated. Undefined when no formatter policy matches. Any real
|
|
1833
|
+
* mutation retires a prior verification record: the workspace the
|
|
1834
|
+
* verification described no longer exists.
|
|
1835
|
+
*/
|
|
1836
|
+
async runConfiguredFormatter(options = {}) {
|
|
1837
|
+
const snapshot = loadPolicyConfiguration({
|
|
1838
|
+
globalSettings: this.settingsManager.getGlobalSettings().policies,
|
|
1839
|
+
projectSettings: this.settingsManager.getProjectSettings().policies,
|
|
1840
|
+
projectTrusted: this.settingsManager.isProjectTrusted(),
|
|
1841
|
+
});
|
|
1842
|
+
const policy = options.policyId === undefined
|
|
1843
|
+
? snapshot.formatter[0]
|
|
1844
|
+
: snapshot.formatter.find((entry) => entry.id === options.policyId);
|
|
1845
|
+
if (policy === undefined)
|
|
1846
|
+
return undefined;
|
|
1847
|
+
const authorize = this._authorizeConfiguredCommand();
|
|
1848
|
+
const outcome = await runFormatterCommand(policy, {
|
|
1849
|
+
workspaceRoot: this._cwd,
|
|
1850
|
+
signal: options.signal,
|
|
1851
|
+
...(authorize !== undefined ? { authorize } : {}),
|
|
1852
|
+
});
|
|
1853
|
+
if (outcome.mutations.changedPaths.length > 0) {
|
|
1854
|
+
this._verificationTracker?.noteWorkspaceChange(new Set(outcome.mutations.changedPaths));
|
|
1855
|
+
}
|
|
1856
|
+
return outcome;
|
|
1857
|
+
}
|
|
1858
|
+
/** The current completion status: verified, failed, unavailable, interrupted, or continued-unverified. */
|
|
1859
|
+
verificationStatus() {
|
|
1860
|
+
return this._verificationTracker?.completionStatus() ?? "unavailable";
|
|
1861
|
+
}
|
|
1862
|
+
/** The live verification record with its bounded evidence, if any. */
|
|
1863
|
+
verificationRecord() {
|
|
1864
|
+
return this._verificationTracker?.latest();
|
|
1865
|
+
}
|
|
1866
|
+
/** Record that the user chose to finish without verification. */
|
|
1867
|
+
continueWithoutVerification() {
|
|
1868
|
+
this._ensureVerificationTracker().recordContinuedWithoutVerification();
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Turn boundary: retire a verification result the workspace has moved
|
|
1872
|
+
* past (conservatively — anything not provably "same" retires it), then
|
|
1873
|
+
* run the configured post-turn boundary. Never throws: verification is
|
|
1874
|
+
* advisory and the turn already completed.
|
|
1875
|
+
*/
|
|
1876
|
+
async _runVerificationTurnBoundary() {
|
|
1877
|
+
let tracker = this._verificationTracker;
|
|
1878
|
+
if (tracker === undefined) {
|
|
1879
|
+
tracker = this._ensureVerificationTracker();
|
|
1880
|
+
}
|
|
1881
|
+
try {
|
|
1882
|
+
if (tracker.latest() !== undefined) {
|
|
1883
|
+
const fresh = await this._captureWorkspaceObservation(undefined);
|
|
1884
|
+
if (this._verificationBaseline === undefined) {
|
|
1885
|
+
tracker.noteWorkspaceChange(new Set(["unbaseline"]));
|
|
1886
|
+
}
|
|
1887
|
+
else {
|
|
1888
|
+
const comparison = compareWorkspaceObservations(this._verificationBaseline, fresh);
|
|
1889
|
+
tracker.noteWorkspaceChange(comparison.result === "same" ? new Set() : new Set(["workspace-changed"]));
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
if (tracker.boundary === "post-turn" && tracker.policyIds().length > 0) {
|
|
1893
|
+
await tracker.runExplicit();
|
|
1894
|
+
this._verificationBaseline = await this._captureWorkspaceObservation(undefined);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
catch {
|
|
1898
|
+
// Verification is advisory; the turn already completed.
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* WS.5 (spec 2026-09-01-harness-correctness-and-workspace-state.md § 3):
|
|
1903
|
+
* at the first model turn after a compaction, or after resume onto a
|
|
1904
|
+
* session with an uncompared observation, compare fresh workspace state
|
|
1905
|
+
* against the stored observation once and persist the outcome. The
|
|
1906
|
+
* observation stays historical. Never throws: comparison problems must
|
|
1907
|
+
* not fail the user's turn.
|
|
1908
|
+
*/
|
|
1909
|
+
async _runWorkspaceComparisonBoundary() {
|
|
1910
|
+
if (!this._workspaceComparePending)
|
|
1911
|
+
return;
|
|
1912
|
+
this._workspaceComparePending = false;
|
|
1913
|
+
try {
|
|
1914
|
+
const baseline = findLatestWorkspaceObservation(this.sessionManager);
|
|
1915
|
+
if (!baseline)
|
|
1916
|
+
return;
|
|
1917
|
+
const fresh = await this._captureWorkspaceObservation(undefined);
|
|
1918
|
+
const outcome = compareWorkspaceObservations(baseline.record, fresh);
|
|
1919
|
+
appendWorkspaceComparison(this.sessionManager, buildWorkspaceComparison(baseline.record.observationId, outcome));
|
|
1920
|
+
}
|
|
1921
|
+
catch {
|
|
1922
|
+
// Comparison is advisory; the turn proceeds.
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
async _runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, signal, env, reason) {
|
|
1926
|
+
// Maintenance budget (spec 2026-09-01-tool-reliability-and-execution-budgets.md):
|
|
1927
|
+
// a compaction summarization is context maintenance, not task progress. It is
|
|
1928
|
+
// recorded on the run's budget for observability and never consumes
|
|
1929
|
+
// maxProviderRequests.
|
|
1930
|
+
this.agent.recordMaintenanceRequest();
|
|
1931
|
+
return compact(preparation, requestModel, apiKey, headers, customInstructions, signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
|
|
1932
|
+
}
|
|
1499
1933
|
/**
|
|
1500
1934
|
* Manually compact the session context.
|
|
1501
|
-
*
|
|
1935
|
+
*
|
|
1936
|
+
* This is the manual entry point used by `/compact`, RPC, and extensions. It is
|
|
1937
|
+
* separate from automatic threshold/overflow compaction, which enters through
|
|
1938
|
+
* `_checkCompaction()` and `_runAutoCompaction()`. After preparation and the
|
|
1939
|
+
* `session_before_compact` hook, both paths call the lower-level `compact()`
|
|
1940
|
+
* function imported from `./compaction/index.ts`, unless the hook cancels or
|
|
1941
|
+
* supplies a custom result.
|
|
1942
|
+
*
|
|
1943
|
+
* Aborts the current agent operation first. Manual compaction never retries or
|
|
1944
|
+
* continues the interrupted agent turn.
|
|
1945
|
+
*
|
|
1502
1946
|
* @param customInstructions Optional instructions for the compaction summary
|
|
1503
1947
|
*/
|
|
1504
1948
|
async compact(customInstructions) {
|
|
1505
1949
|
await this.abort();
|
|
1506
1950
|
this._compactionAbortController = new AbortController();
|
|
1507
1951
|
this._emit({ type: "compaction_start", reason: "manual" });
|
|
1952
|
+
let fromExtension = false;
|
|
1508
1953
|
try {
|
|
1509
1954
|
if (!this.model) {
|
|
1510
1955
|
throw new Error(formatNoModelSelectedMessage());
|
|
@@ -1522,7 +1967,12 @@ export class AgentSession {
|
|
|
1522
1967
|
throw new Error("Nothing to compact (session too small)");
|
|
1523
1968
|
}
|
|
1524
1969
|
let extensionCompaction;
|
|
1525
|
-
|
|
1970
|
+
if (this._hookRuntime?.hasHandlers("session_before_compact")) {
|
|
1971
|
+
await this._hookRuntime.emitObserve("session_before_compact", {
|
|
1972
|
+
type: "session_before_compact",
|
|
1973
|
+
reason: "manual",
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1526
1976
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1527
1977
|
const result = (await this._extensionRunner.emit({
|
|
1528
1978
|
type: "session_before_compact",
|
|
@@ -1546,6 +1996,12 @@ export class AgentSession {
|
|
|
1546
1996
|
let tokensBefore;
|
|
1547
1997
|
let usage;
|
|
1548
1998
|
let details;
|
|
1999
|
+
// One workspace observation attempt per compaction (spec
|
|
2000
|
+
// 2026-09-01-harness-correctness-and-workspace-state.md § 3),
|
|
2001
|
+
// before summary generation so the bounded projection can join
|
|
2002
|
+
// the model-facing summary. Capture problems never fail the
|
|
2003
|
+
// compaction.
|
|
2004
|
+
const workspaceRecord = await this._captureWorkspaceObservation(this._compactionAbortController?.signal ?? this._autoCompactionAbortController?.signal);
|
|
1549
2005
|
if (extensionCompaction) {
|
|
1550
2006
|
// Extension provided compaction content
|
|
1551
2007
|
summary = extensionCompaction.summary;
|
|
@@ -1555,18 +2011,35 @@ export class AgentSession {
|
|
|
1555
2011
|
details = extensionCompaction.details;
|
|
1556
2012
|
}
|
|
1557
2013
|
else {
|
|
1558
|
-
//
|
|
1559
|
-
const result = await
|
|
2014
|
+
// Shared default summary generator, also used by automatic compaction.
|
|
2015
|
+
const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
|
|
1560
2016
|
summary = result.summary;
|
|
1561
2017
|
firstKeptEntryId = result.firstKeptEntryId;
|
|
1562
2018
|
tokensBefore = result.tokensBefore;
|
|
1563
2019
|
usage = result.usage;
|
|
1564
2020
|
details = result.details;
|
|
2021
|
+
if (workspaceRecord) {
|
|
2022
|
+
summary += `\n\n${formatWorkspaceProjection(workspaceRecord)}`;
|
|
2023
|
+
}
|
|
1565
2024
|
}
|
|
1566
2025
|
if (this._compactionAbortController.signal.aborted) {
|
|
1567
2026
|
throw new Error("Compaction cancelled");
|
|
1568
2027
|
}
|
|
1569
2028
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2029
|
+
if (workspaceRecord) {
|
|
2030
|
+
try {
|
|
2031
|
+
// Historical record, child of the compaction entry; never in
|
|
2032
|
+
// the model context and never a rewrite of history.
|
|
2033
|
+
appendWorkspaceObservation(this.sessionManager, workspaceRecord);
|
|
2034
|
+
// WS.5: the next model turn compares fresh state against
|
|
2035
|
+
// this observation and records the drift outcome.
|
|
2036
|
+
this._workspaceComparePending = true;
|
|
2037
|
+
}
|
|
2038
|
+
catch {
|
|
2039
|
+
// Artifact/entry persistence problems downgrade the
|
|
2040
|
+
// workspace status; they never fail transcript compaction.
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
1570
2043
|
const newEntries = this.sessionManager.getEntries();
|
|
1571
2044
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1572
2045
|
this.agent.state.messages = sessionContext.messages;
|
|
@@ -1604,6 +2077,7 @@ export class AgentSession {
|
|
|
1604
2077
|
catch (error) {
|
|
1605
2078
|
const message = error instanceof Error ? error.message : String(error);
|
|
1606
2079
|
const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
|
|
2080
|
+
const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
|
|
1607
2081
|
this._compactionAbortController = undefined;
|
|
1608
2082
|
this._emit({
|
|
1609
2083
|
type: "compaction_end",
|
|
@@ -1611,7 +2085,14 @@ export class AgentSession {
|
|
|
1611
2085
|
result: undefined,
|
|
1612
2086
|
aborted,
|
|
1613
2087
|
willRetry: false,
|
|
1614
|
-
errorMessage
|
|
2088
|
+
errorMessage,
|
|
2089
|
+
});
|
|
2090
|
+
await this._emitSessionCompactFailed({
|
|
2091
|
+
reason: "manual",
|
|
2092
|
+
errorMessage,
|
|
2093
|
+
aborted,
|
|
2094
|
+
willRetry: false,
|
|
2095
|
+
fromExtension,
|
|
1615
2096
|
});
|
|
1616
2097
|
throw error;
|
|
1617
2098
|
}
|
|
@@ -1633,16 +2114,25 @@ export class AgentSession {
|
|
|
1633
2114
|
this._branchSummaryAbortController?.abort();
|
|
1634
2115
|
}
|
|
1635
2116
|
/**
|
|
1636
|
-
*
|
|
1637
|
-
*
|
|
2117
|
+
* Dispatch automatic compaction after `agent_end` or before prompt submission.
|
|
2118
|
+
* Manual compaction does not call this method; it enters through `compact()`.
|
|
2119
|
+
*
|
|
2120
|
+
* Automatic cases:
|
|
2121
|
+
* 1. Overflow with retry: a context-overflow error or recoverable length stop;
|
|
2122
|
+
* remove the failed assistant message, compact, and retry the turn once.
|
|
2123
|
+
* 2. Overflow without retry: a successful response exceeded the configured
|
|
2124
|
+
* context window; compact but preserve the completed response.
|
|
2125
|
+
* 3. Threshold without retry: valid or estimated context usage crossed the
|
|
2126
|
+
* configured threshold; compact without retrying the completed response.
|
|
1638
2127
|
*
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
2128
|
+
* Each case calls `_runAutoCompaction()`. After preparation and the
|
|
2129
|
+
* `session_before_compact` hook, that method calls the lower-level `compact()`
|
|
2130
|
+
* function imported from `./compaction/index.ts`, unless the hook cancels or
|
|
2131
|
+
* supplies a custom result.
|
|
1643
2132
|
*
|
|
1644
2133
|
* @param assistantMessage The assistant message to check
|
|
1645
2134
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
2135
|
+
* @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
|
|
1646
2136
|
*/
|
|
1647
2137
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
1648
2138
|
const settings = this.settingsManager.getCompactionSettings();
|
|
@@ -1665,38 +2155,49 @@ export class AgentSession {
|
|
|
1665
2155
|
if (assistantIsFromBeforeCompaction) {
|
|
1666
2156
|
return false;
|
|
1667
2157
|
}
|
|
1668
|
-
//
|
|
2158
|
+
// Automatic cases 1 and 2: context overflow.
|
|
1669
2159
|
// A length stop is recoverable when output ended below the model's original desired limit,
|
|
1670
2160
|
// independent of the configured context size or any context-clamped provider request limit.
|
|
1671
|
-
|
|
1672
|
-
// assistant answer already completed and agent.continue() cannot continue from an assistant.
|
|
2161
|
+
const contextOverflow = sameModel && isContextOverflow(assistantMessage, contextWindow);
|
|
1673
2162
|
const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
|
|
1674
|
-
if (
|
|
2163
|
+
if (contextOverflow || recoverableLength) {
|
|
1675
2164
|
const willRetry = assistantMessage.stopReason !== "stop";
|
|
2165
|
+
// Case 2: the response completed successfully. Compact, but do not retry because
|
|
2166
|
+
// agent.continue() cannot continue from a completed assistant response.
|
|
1676
2167
|
if (!willRetry) {
|
|
1677
2168
|
return await this._runAutoCompaction("overflow", false);
|
|
1678
2169
|
}
|
|
1679
2170
|
if (this._overflowRecoveryAttempted) {
|
|
2171
|
+
const errorMessage = contextOverflow
|
|
2172
|
+
? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
|
|
2173
|
+
: "Truncated response recovery failed after one compact-and-retry attempt.";
|
|
1680
2174
|
this._emit({
|
|
1681
2175
|
type: "compaction_end",
|
|
1682
2176
|
reason: "overflow",
|
|
1683
2177
|
result: undefined,
|
|
1684
2178
|
aborted: false,
|
|
1685
2179
|
willRetry: false,
|
|
1686
|
-
errorMessage
|
|
2180
|
+
errorMessage,
|
|
2181
|
+
});
|
|
2182
|
+
await this._emitSessionCompactFailed({
|
|
2183
|
+
reason: "overflow",
|
|
2184
|
+
errorMessage,
|
|
2185
|
+
aborted: false,
|
|
2186
|
+
willRetry: false,
|
|
2187
|
+
fromExtension: false,
|
|
1687
2188
|
});
|
|
1688
2189
|
return false;
|
|
1689
2190
|
}
|
|
2191
|
+
// Case 1: remove the failed or truncated message from agent state, compact, and
|
|
2192
|
+
// retry once. The message remains in session history but is excluded from retry context.
|
|
1690
2193
|
this._overflowRecoveryAttempted = true;
|
|
1691
|
-
// Remove the failed or truncated message from agent state. It remains in session history,
|
|
1692
|
-
// but must not be included in the compact-and-retry context.
|
|
1693
2194
|
const messages = this.agent.state.messages;
|
|
1694
2195
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
1695
2196
|
this.agent.state.messages = messages.slice(0, -1);
|
|
1696
2197
|
}
|
|
1697
2198
|
return await this._runAutoCompaction("overflow", willRetry);
|
|
1698
2199
|
}
|
|
1699
|
-
// Case
|
|
2200
|
+
// Case 3: threshold compaction without retry.
|
|
1700
2201
|
// For error messages or all-zero usage messages, estimate from the last valid response.
|
|
1701
2202
|
// This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
|
|
1702
2203
|
// responses can still compact and do not reset context accounting.
|
|
@@ -1705,16 +2206,18 @@ export class AgentSession {
|
|
|
1705
2206
|
if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
|
|
1706
2207
|
const messages = this.agent.state.messages;
|
|
1707
2208
|
const estimate = estimateContextTokens(messages);
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
2209
|
+
// Without provider usage, estimate.tokens is the pure message-size estimate.
|
|
2210
|
+
// Only usage-backed estimates need the stale pre-compaction check.
|
|
2211
|
+
if (estimate.lastUsageIndex !== null) {
|
|
2212
|
+
// Verify the usage source is post-compaction. Kept pre-compaction messages
|
|
2213
|
+
// have stale usage reflecting the old (larger) context and would falsely
|
|
2214
|
+
// trigger compaction right after one just finished.
|
|
2215
|
+
const usageMsg = messages[estimate.lastUsageIndex];
|
|
2216
|
+
if (compactionEntry &&
|
|
2217
|
+
usageMsg.role === "assistant" &&
|
|
2218
|
+
usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
|
|
2219
|
+
return false;
|
|
2220
|
+
}
|
|
1718
2221
|
}
|
|
1719
2222
|
contextTokens = estimate.tokens;
|
|
1720
2223
|
}
|
|
@@ -1727,11 +2230,19 @@ export class AgentSession {
|
|
|
1727
2230
|
return false;
|
|
1728
2231
|
}
|
|
1729
2232
|
/**
|
|
1730
|
-
*
|
|
2233
|
+
* Execute threshold or overflow compaction. Manual compaction uses
|
|
2234
|
+
* `AgentSession.compact()` instead. Both paths call the lower-level `compact()`
|
|
2235
|
+
* function imported from `./compaction/index.ts` after preparation and extension
|
|
2236
|
+
* interception.
|
|
2237
|
+
*
|
|
2238
|
+
* @param reason Automatic trigger selected by `_checkCompaction()`
|
|
2239
|
+
* @param willRetry Whether to continue the interrupted turn after overflow compaction
|
|
2240
|
+
* @returns Whether the post-run loop should call `agent.continue()`
|
|
1731
2241
|
*/
|
|
1732
2242
|
async _runAutoCompaction(reason, willRetry) {
|
|
1733
2243
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1734
2244
|
let started = false;
|
|
2245
|
+
let fromExtension = false;
|
|
1735
2246
|
try {
|
|
1736
2247
|
if (!this.model) {
|
|
1737
2248
|
return false;
|
|
@@ -1746,7 +2257,9 @@ export class AgentSession {
|
|
|
1746
2257
|
this._autoCompactionAbortController = new AbortController();
|
|
1747
2258
|
started = true;
|
|
1748
2259
|
let extensionCompaction;
|
|
1749
|
-
|
|
2260
|
+
if (this._hookRuntime?.hasHandlers("session_before_compact")) {
|
|
2261
|
+
await this._hookRuntime.emitObserve("session_before_compact", { type: "session_before_compact", reason });
|
|
2262
|
+
}
|
|
1750
2263
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1751
2264
|
const extensionResult = (await this._extensionRunner.emit({
|
|
1752
2265
|
type: "session_before_compact",
|
|
@@ -1765,6 +2278,12 @@ export class AgentSession {
|
|
|
1765
2278
|
aborted: true,
|
|
1766
2279
|
willRetry: false,
|
|
1767
2280
|
});
|
|
2281
|
+
await this._emitSessionCompactFailed({
|
|
2282
|
+
reason,
|
|
2283
|
+
aborted: true,
|
|
2284
|
+
willRetry: false,
|
|
2285
|
+
fromExtension: false,
|
|
2286
|
+
});
|
|
1768
2287
|
return false;
|
|
1769
2288
|
}
|
|
1770
2289
|
if (extensionResult?.compaction) {
|
|
@@ -1777,6 +2296,12 @@ export class AgentSession {
|
|
|
1777
2296
|
let tokensBefore;
|
|
1778
2297
|
let usage;
|
|
1779
2298
|
let details;
|
|
2299
|
+
// One workspace observation attempt per compaction (spec
|
|
2300
|
+
// 2026-09-01-harness-correctness-and-workspace-state.md § 3),
|
|
2301
|
+
// before summary generation so the bounded projection can join
|
|
2302
|
+
// the model-facing summary. Capture problems never fail the
|
|
2303
|
+
// compaction.
|
|
2304
|
+
const workspaceRecord = await this._captureWorkspaceObservation(this._compactionAbortController?.signal ?? this._autoCompactionAbortController?.signal);
|
|
1780
2305
|
if (extensionCompaction) {
|
|
1781
2306
|
// Extension provided compaction content
|
|
1782
2307
|
summary = extensionCompaction.summary;
|
|
@@ -1786,13 +2311,16 @@ export class AgentSession {
|
|
|
1786
2311
|
details = extensionCompaction.details;
|
|
1787
2312
|
}
|
|
1788
2313
|
else {
|
|
1789
|
-
//
|
|
1790
|
-
const compactResult = await
|
|
2314
|
+
// Shared default summary generator, also used by manual compaction.
|
|
2315
|
+
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, env, reason);
|
|
1791
2316
|
summary = compactResult.summary;
|
|
1792
2317
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
1793
2318
|
tokensBefore = compactResult.tokensBefore;
|
|
1794
2319
|
usage = compactResult.usage;
|
|
1795
2320
|
details = compactResult.details;
|
|
2321
|
+
if (workspaceRecord) {
|
|
2322
|
+
summary += `\n\n${formatWorkspaceProjection(workspaceRecord)}`;
|
|
2323
|
+
}
|
|
1796
2324
|
}
|
|
1797
2325
|
if (this._autoCompactionAbortController.signal.aborted) {
|
|
1798
2326
|
this._emit({
|
|
@@ -1802,9 +2330,29 @@ export class AgentSession {
|
|
|
1802
2330
|
aborted: true,
|
|
1803
2331
|
willRetry: false,
|
|
1804
2332
|
});
|
|
2333
|
+
await this._emitSessionCompactFailed({
|
|
2334
|
+
reason,
|
|
2335
|
+
aborted: true,
|
|
2336
|
+
willRetry: false,
|
|
2337
|
+
fromExtension,
|
|
2338
|
+
});
|
|
1805
2339
|
return false;
|
|
1806
2340
|
}
|
|
1807
2341
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2342
|
+
if (workspaceRecord) {
|
|
2343
|
+
try {
|
|
2344
|
+
// Historical record, child of the compaction entry; never in
|
|
2345
|
+
// the model context and never a rewrite of history.
|
|
2346
|
+
appendWorkspaceObservation(this.sessionManager, workspaceRecord);
|
|
2347
|
+
// WS.5: the next model turn compares fresh state against
|
|
2348
|
+
// this observation and records the drift outcome.
|
|
2349
|
+
this._workspaceComparePending = true;
|
|
2350
|
+
}
|
|
2351
|
+
catch {
|
|
2352
|
+
// Artifact/entry persistence problems downgrade the
|
|
2353
|
+
// workspace status; they never fail transcript compaction.
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
1808
2356
|
const newEntries = this.sessionManager.getEntries();
|
|
1809
2357
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1810
2358
|
this.agent.state.messages = sessionContext.messages;
|
|
@@ -1848,15 +2396,23 @@ export class AgentSession {
|
|
|
1848
2396
|
catch (error) {
|
|
1849
2397
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
1850
2398
|
if (started) {
|
|
2399
|
+
const formattedErrorMessage = reason === "overflow"
|
|
2400
|
+
? `Context overflow recovery failed: ${errorMessage}`
|
|
2401
|
+
: `Auto-compaction failed: ${errorMessage}`;
|
|
1851
2402
|
this._emit({
|
|
1852
2403
|
type: "compaction_end",
|
|
1853
2404
|
reason,
|
|
1854
2405
|
result: undefined,
|
|
1855
2406
|
aborted: false,
|
|
1856
2407
|
willRetry: false,
|
|
1857
|
-
errorMessage:
|
|
1858
|
-
|
|
1859
|
-
|
|
2408
|
+
errorMessage: formattedErrorMessage,
|
|
2409
|
+
});
|
|
2410
|
+
await this._emitSessionCompactFailed({
|
|
2411
|
+
reason,
|
|
2412
|
+
errorMessage: formattedErrorMessage,
|
|
2413
|
+
aborted: false,
|
|
2414
|
+
willRetry: false,
|
|
2415
|
+
fromExtension,
|
|
1860
2416
|
});
|
|
1861
2417
|
}
|
|
1862
2418
|
return false;
|
|
@@ -2170,10 +2726,15 @@ export class AgentSession {
|
|
|
2170
2726
|
]))
|
|
2171
2727
|
: createAllToolDefinitions(this._cwd, {
|
|
2172
2728
|
read: { autoResizeImages },
|
|
2173
|
-
bash: {
|
|
2729
|
+
bash: {
|
|
2730
|
+
commandPrefix: shellCommandPrefix,
|
|
2731
|
+
shellPath,
|
|
2732
|
+
backgroundRegistry: this._backgroundShellRegistry,
|
|
2733
|
+
},
|
|
2174
2734
|
edit: { diagnosticsOperations: this._diagnosticsOperations },
|
|
2175
2735
|
write: { diagnosticsOperations: this._diagnosticsOperations },
|
|
2176
2736
|
...(this._lspOperations ? { lsp: { operations: this._lspOperations } } : {}),
|
|
2737
|
+
...(this._mcpRuntime ? { mcp: this._mcpRuntime } : {}),
|
|
2177
2738
|
...(this._webSearchOperations ? { web_search: { operations: this._webSearchOperations } } : {}),
|
|
2178
2739
|
});
|
|
2179
2740
|
baseToolDefinitions.tool_schema = createToolSchemaToolDefinition({
|
|
@@ -2485,6 +3046,13 @@ export class AgentSession {
|
|
|
2485
3046
|
if (targetId === oldLeafId) {
|
|
2486
3047
|
return { cancelled: false };
|
|
2487
3048
|
}
|
|
3049
|
+
// WS.6 (spec 2026-09-01-harness-correctness-and-workspace-state.md § 4):
|
|
3050
|
+
// files move only under an explicit policy, and a refused or cancelled
|
|
3051
|
+
// restore leaves the conversation untouched too.
|
|
3052
|
+
const workspaceStep = await this._resolveTreeWorkspaceStep(targetId, options.workspacePolicy ?? "keep");
|
|
3053
|
+
if (workspaceStep.cancelled) {
|
|
3054
|
+
return { cancelled: true, workspace: workspaceStep.outcome };
|
|
3055
|
+
}
|
|
2488
3056
|
// Model required for summarization
|
|
2489
3057
|
if (options.summarize && !this.model) {
|
|
2490
3058
|
throw new Error("No model available for summarization");
|
|
@@ -2631,12 +3199,77 @@ export class AgentSession {
|
|
|
2631
3199
|
fromExtension: summaryText ? fromExtension : undefined,
|
|
2632
3200
|
});
|
|
2633
3201
|
// Emit to custom tools
|
|
2634
|
-
return { editorText, cancelled: false, summaryEntry };
|
|
3202
|
+
return { editorText, cancelled: false, summaryEntry, workspace: workspaceStep.outcome };
|
|
2635
3203
|
}
|
|
2636
3204
|
finally {
|
|
2637
3205
|
this._branchSummaryAbortController = undefined;
|
|
2638
3206
|
}
|
|
2639
3207
|
}
|
|
3208
|
+
/**
|
|
3209
|
+
* WS.6: resolve what navigation may do to the workspace before the
|
|
3210
|
+
* conversation moves. `keep` never touches files; `restore` overwrites
|
|
3211
|
+
* only through the engine's pinned pre-restore checkpoint; `fail-if-
|
|
3212
|
+
* drifted` and `cancel` refuse when the workspace moved; a missing
|
|
3213
|
+
* checkpoint leaves the workspace unchanged and says so. Never throws.
|
|
3214
|
+
*/
|
|
3215
|
+
async _resolveTreeWorkspaceStep(targetId, policy) {
|
|
3216
|
+
const unchanged = { policy, outcome: "unchanged", warnings: [] };
|
|
3217
|
+
if (policy === "keep")
|
|
3218
|
+
return { outcome: unchanged };
|
|
3219
|
+
try {
|
|
3220
|
+
const engine = await this._checkpoints.engine();
|
|
3221
|
+
if (!engine) {
|
|
3222
|
+
return {
|
|
3223
|
+
outcome: {
|
|
3224
|
+
policy,
|
|
3225
|
+
outcome: "missing-checkpoint",
|
|
3226
|
+
warnings: ["checkpoints are unavailable; workspace left unchanged"],
|
|
3227
|
+
},
|
|
3228
|
+
};
|
|
3229
|
+
}
|
|
3230
|
+
const checkpoint = await engine.lookup(targetId);
|
|
3231
|
+
if (!checkpoint)
|
|
3232
|
+
return { outcome: { policy, outcome: "missing-checkpoint", warnings: [] } };
|
|
3233
|
+
const matches = await engine.matchesWorktree(checkpoint);
|
|
3234
|
+
if (matches === undefined) {
|
|
3235
|
+
return {
|
|
3236
|
+
outcome: {
|
|
3237
|
+
policy,
|
|
3238
|
+
outcome: "failed",
|
|
3239
|
+
warnings: ["could not compare the workspace against the checkpoint"],
|
|
3240
|
+
},
|
|
3241
|
+
};
|
|
3242
|
+
}
|
|
3243
|
+
if (matches)
|
|
3244
|
+
return { outcome: unchanged };
|
|
3245
|
+
if (policy === "fail-if-drifted" || policy === "cancel") {
|
|
3246
|
+
return { cancelled: true, outcome: { policy, outcome: "refused-drifted", warnings: [] } };
|
|
3247
|
+
}
|
|
3248
|
+
const preRestore = await engine.restore(checkpoint);
|
|
3249
|
+
if (!preRestore) {
|
|
3250
|
+
return {
|
|
3251
|
+
outcome: { policy, outcome: "failed", warnings: ["restore failed; workspace left unchanged"] },
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
return {
|
|
3255
|
+
outcome: {
|
|
3256
|
+
policy,
|
|
3257
|
+
outcome: "restored",
|
|
3258
|
+
preRestoreCheckpoint: { entryId: preRestore.entryId, commit: preRestore.commit },
|
|
3259
|
+
warnings: [],
|
|
3260
|
+
},
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
catch (error) {
|
|
3264
|
+
return {
|
|
3265
|
+
outcome: {
|
|
3266
|
+
policy,
|
|
3267
|
+
outcome: "failed",
|
|
3268
|
+
warnings: [`workspace policy check failed: ${error instanceof Error ? error.message : String(error)}`],
|
|
3269
|
+
},
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
2640
3273
|
/**
|
|
2641
3274
|
* Get all user messages from session for fork selector.
|
|
2642
3275
|
*/
|
|
@@ -2780,29 +3413,7 @@ export class AgentSession {
|
|
|
2780
3413
|
* @returns The resolved output file path.
|
|
2781
3414
|
*/
|
|
2782
3415
|
exportToJsonl(outputPath) {
|
|
2783
|
-
|
|
2784
|
-
const dir = dirname(filePath);
|
|
2785
|
-
if (!existsSync(dir)) {
|
|
2786
|
-
mkdirSync(dir, { recursive: true });
|
|
2787
|
-
}
|
|
2788
|
-
const header = {
|
|
2789
|
-
type: "session",
|
|
2790
|
-
version: CURRENT_SESSION_VERSION,
|
|
2791
|
-
id: this.sessionManager.getSessionId(),
|
|
2792
|
-
timestamp: new Date().toISOString(),
|
|
2793
|
-
cwd: this.sessionManager.getCwd(),
|
|
2794
|
-
};
|
|
2795
|
-
const branchEntries = this.sessionManager.getBranch();
|
|
2796
|
-
const lines = [JSON.stringify(header)];
|
|
2797
|
-
// Re-chain parentIds to form a linear sequence
|
|
2798
|
-
let prevId = null;
|
|
2799
|
-
for (const entry of branchEntries) {
|
|
2800
|
-
const linear = { ...entry, parentId: prevId };
|
|
2801
|
-
lines.push(JSON.stringify(linear));
|
|
2802
|
-
prevId = entry.id;
|
|
2803
|
-
}
|
|
2804
|
-
writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
2805
|
-
return filePath;
|
|
3416
|
+
return exportSessionToJsonl(this.sessionManager, outputPath);
|
|
2806
3417
|
}
|
|
2807
3418
|
// =========================================================================
|
|
2808
3419
|
// Utilities
|