@code-yeongyu/senpi 2026.8.29 → 2026.8.30-2
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 +122 -0
- package/dist/core/agent-session.d.ts +45 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +329 -29
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +155 -37
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/compaction-execution.d.ts +14 -0
- package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
- package/dist/core/compaction/compaction-execution.js +121 -0
- package/dist/core/compaction/compaction-execution.js.map +1 -0
- package/dist/core/compaction/compaction-settings.d.ts +17 -0
- package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
- package/dist/core/compaction/compaction-settings.js +17 -0
- package/dist/core/compaction/compaction-settings.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +13 -26
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +7 -136
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
- package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
- package/dist/core/compaction/ideal-compaction-settings.js +7 -0
- package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
- package/dist/core/compaction-settings-access.d.ts +19 -0
- package/dist/core/compaction-settings-access.d.ts.map +1 -0
- package/dist/core/compaction-settings-access.js +10 -0
- package/dist/core/compaction-settings-access.js.map +1 -0
- package/dist/core/compaction-settings-resolver.d.ts +22 -0
- package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
- package/dist/core/compaction-settings-resolver.js +46 -0
- package/dist/core/compaction-settings-resolver.js.map +1 -0
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +23 -9
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
- package/dist/core/credential-pool/env-slots.js +2 -0
- package/dist/core/credential-pool/env-slots.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +17 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js +28 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +163 -48
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/idle.d.ts +1 -0
- package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/idle.js +13 -2
- package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +121 -147
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.js +2 -0
- package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
- package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
- package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts +56 -0
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js +136 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/policy.d.ts +5 -2
- package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.js +43 -26
- package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +18 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +18 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +36 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js +92 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
- package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +31 -3
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
- package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/model-resolver.js +2 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +10 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +15 -85
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -17
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-public-types.d.ts +11 -0
- package/dist/core/settings-public-types.d.ts.map +1 -0
- package/dist/core/settings-public-types.js +2 -0
- package/dist/core/settings-public-types.js.map +1 -0
- package/dist/core/settings-shapes.d.ts +36 -0
- package/dist/core/settings-shapes.d.ts.map +1 -0
- package/dist/core/settings-shapes.js +2 -0
- package/dist/core/settings-shapes.js.map +1 -0
- package/dist/core/shared-host-policy.d.ts +6 -0
- package/dist/core/shared-host-policy.d.ts.map +1 -0
- package/dist/core/shared-host-policy.js +7 -0
- package/dist/core/shared-host-policy.js.map +1 -0
- package/dist/core/terminal-settings.d.ts +22 -0
- package/dist/core/terminal-settings.d.ts.map +1 -0
- package/dist/core/terminal-settings.js +2 -0
- package/dist/core/terminal-settings.js.map +1 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -15
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +76 -34
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -5
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +7 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +34 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/grok/chrome.d.ts +2 -0
- package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
- package/dist/modes/interactive/grok/chrome.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts +121 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +336 -65
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +114 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +8 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +145 -49
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +19 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +79 -15
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +96 -25
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +2 -0
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +23 -12
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +6 -4
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +10 -4
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +47 -10
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
- package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.js +66 -0
- package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +86 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.d.ts.map +1 -1
- package/dist/modes/rpc/session-binding.js +21 -1
- package/dist/modes/rpc/session-binding.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +1 -0
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +54 -7
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
- package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
- package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +10 -2
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +58 -4
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
- package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
- package/dist/modes/rpc/socket-event-fanout.js +84 -0
- package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
- package/docs/rpc.md +18 -2
- package/docs/settings.md +34 -0
- package/docs/windows.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +50 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
- package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { rm } from "node:fs/promises";
|
|
17
18
|
import { basename, dirname } from "node:path";
|
|
18
19
|
import { isDeepStrictEqual } from "node:util";
|
|
19
20
|
import { ProviderRetryWatchdogAbortError, prepareAgentToolCall } from "@earendil-works/pi-agent-core";
|
|
20
|
-
import { contentText, SERVER_FALLBACK_ABORTED_DIAGNOSTIC } from "@earendil-works/pi-ai";
|
|
21
|
+
import { contentText, measureCursorHistorySerializedBytes, SERVER_FALLBACK_ABORTED_DIAGNOSTIC, } from "@earendil-works/pi-ai";
|
|
21
22
|
import { cleanupSessionResources, cursorOverflowCompactionSettings, isClassifierRefusal, isContextOverflow, isCursorPayloadResourceExhausted, isCursorQuotaResourceExhausted, isCursorZeroTokenResourceExhausted, isProviderStreamStallError, isProviderTimeoutError, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, shouldRetryOverflowWithoutCompact, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
22
23
|
import { extract429RetryAfterMs, parseRetryAfterMsMarker } from "@earendil-works/pi-ai/utils/retry-hint";
|
|
23
24
|
import { retryBackoffDelayMs } from "@earendil-works/pi-ai/utils/retry-profile/backoff";
|
|
@@ -40,6 +41,7 @@ import { buildDynamicSystemPrompt } from "./dynamic-prompt/index.js";
|
|
|
40
41
|
import { areExperimentalFeaturesEnabled } from "./experimental.js";
|
|
41
42
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
42
43
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
44
|
+
import { resolveReserveTokens } from "./extensions/builtin/compaction/policy.js";
|
|
43
45
|
import { CODEX_RESPONSES_API } from "./extensions/builtin/service-tier.js";
|
|
44
46
|
import { deriveExtensionRegistrationId } from "./extensions/builtin/tool-search/engine/marker.js";
|
|
45
47
|
import { getToolSearchService } from "./extensions/builtin/tool-search/service.js";
|
|
@@ -47,7 +49,7 @@ import { ExecuteToolError, ExtensionRunner, wrapRegisteredTools, } from "./exten
|
|
|
47
49
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
48
50
|
import { normalizeToolExposure, RUNTIME_EXTENSION_PATH } from "./extensions/types.js";
|
|
49
51
|
import { shouldWarnHighReasoning } from "./high-reasoning-warning.js";
|
|
50
|
-
import { filterContextExcludedMessages } from "./messages.js";
|
|
52
|
+
import { convertToLlm, filterContextExcludedMessages, } from "./messages.js";
|
|
51
53
|
import { ModelRegistry } from "./model-registry.js";
|
|
52
54
|
import { getModelNarrowingPatterns, resolveModelScope } from "./model-resolver.js";
|
|
53
55
|
import { PROMPT_CACHE_SAFE_WAIT_ENV, resolvePromptCacheSafeWaitSeconds } from "./prompt-cache-budget.js";
|
|
@@ -70,9 +72,21 @@ import { getSupportedThinkingLevels, supportsMax, supportsXhigh } from "./thinki
|
|
|
70
72
|
import { resetTimings, time } from "./timings.js";
|
|
71
73
|
import { createLocalBashOperations } from "./tools/bash.js";
|
|
72
74
|
import { composeFilesystemPolicies } from "./tools/filesystem-policy.js";
|
|
73
|
-
import { createAllToolDefinitions } from "./tools/index.js";
|
|
75
|
+
import { createAllToolDefinitions, temporarilyDisabledToolNames } from "./tools/index.js";
|
|
74
76
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
75
77
|
import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
|
|
78
|
+
/** Built-in shell tools routed exclusively through eval when experimental.bashEvalOnly is enabled. */
|
|
79
|
+
const EVAL_ONLY_SHELL_TOOL_NAMES = ["bash", "powershell"];
|
|
80
|
+
/** Workflow tool routed exclusively through eval when experimental.workflowEvalOnly is enabled. */
|
|
81
|
+
const EVAL_ONLY_WORKFLOW_TOOL_NAMES = ["workflow"];
|
|
82
|
+
/** Sample eval-cell call for an eval-only tool, using the argument name that tool actually takes. */
|
|
83
|
+
function evalHelperCall(name) {
|
|
84
|
+
if (EVAL_ONLY_SHELL_TOOL_NAMES.includes(name))
|
|
85
|
+
return `tool.${name}({ command: "..." })`;
|
|
86
|
+
if (EVAL_ONLY_WORKFLOW_TOOL_NAMES.includes(name))
|
|
87
|
+
return `tool.${name}({ action: "..." })`;
|
|
88
|
+
return `tool.${name}({ ... })`;
|
|
89
|
+
}
|
|
76
90
|
const TURN_RETRY_SUPPRESSION_PREFIX = "senpi:no-turn-retry:";
|
|
77
91
|
const DEFERRED_RETRY_QUEUE_OWNERS = new WeakSet();
|
|
78
92
|
/** Format the user-attributed payload for one or more explicit skill invocations. */
|
|
@@ -310,6 +324,121 @@ function isSameOverflowSource(message, model, upstreamModelId) {
|
|
|
310
324
|
const THINKING_LEVELS_WITH_MAX = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
311
325
|
/** Caps explicit skill expansion so one prompt cannot consume unbounded context. */
|
|
312
326
|
export const MAX_SKILL_EXPANSIONS_PER_PROMPT = 5;
|
|
327
|
+
/** Cursor ingest rejects large verbatim tool payloads. The bound is UTF-8 bytes. */
|
|
328
|
+
export const CURSOR_TOOL_RESULT_MAX_CHARS = 2000;
|
|
329
|
+
export const CURSOR_TOOL_RESULT_MAX_BYTES = 50_000;
|
|
330
|
+
const CURSOR_TRUNCATION_MARKER = "\n...[truncated]";
|
|
331
|
+
export function truncateToolResultBodies(messages, maxChars = CURSOR_TOOL_RESULT_MAX_CHARS, maxBytes = CURSOR_TOOL_RESULT_MAX_BYTES, convert = (candidate) => convertToLlm(candidate)) {
|
|
332
|
+
if (!Array.isArray(messages) || messages.length === 0)
|
|
333
|
+
return { messages, changed: false };
|
|
334
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
335
|
+
const markerChars = [...segmenter.segment(CURSOR_TRUNCATION_MARKER)].length;
|
|
336
|
+
const result = messages.slice();
|
|
337
|
+
let changed = false;
|
|
338
|
+
// Apply the per-result character cap first, independent of the aggregate wire cap.
|
|
339
|
+
for (let messageIndex = result.length - 1; messageIndex >= 0; messageIndex--) {
|
|
340
|
+
const message = result[messageIndex];
|
|
341
|
+
if (message.role !== "toolResult" || !Array.isArray(message.content))
|
|
342
|
+
continue;
|
|
343
|
+
let content = message.content;
|
|
344
|
+
for (let partIndex = content.length - 1; partIndex >= 0; partIndex--) {
|
|
345
|
+
const part = content[partIndex];
|
|
346
|
+
if (part.type === "image" && typeof part.data === "string")
|
|
347
|
+
continue;
|
|
348
|
+
if (part.type !== "text" || typeof part.text !== "string")
|
|
349
|
+
continue;
|
|
350
|
+
const graphemes = [...segmenter.segment(part.text)].map((item) => item.segment);
|
|
351
|
+
if (graphemes.length <= maxChars)
|
|
352
|
+
continue;
|
|
353
|
+
const kept = graphemes.slice(0, Math.max(0, maxChars - markerChars)).join("");
|
|
354
|
+
const nextText = kept + CURSOR_TRUNCATION_MARKER;
|
|
355
|
+
content = content === message.content ? content.slice() : content;
|
|
356
|
+
content[partIndex] = { ...part, text: nextText };
|
|
357
|
+
result[messageIndex] = { ...message, content };
|
|
358
|
+
changed = true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const measure = (candidate) => {
|
|
362
|
+
const converted = convert(candidate);
|
|
363
|
+
const activeUserMessageIndex = converted.at(-1)?.role === "user" ? converted.length - 1 : -1;
|
|
364
|
+
return measureCursorHistorySerializedBytes(converted, activeUserMessageIndex);
|
|
365
|
+
};
|
|
366
|
+
const fits = (candidate = result) => measure(candidate) <= maxBytes;
|
|
367
|
+
if (fits())
|
|
368
|
+
return { messages: changed ? result : messages, changed };
|
|
369
|
+
// Empty the oldest result bodies. Search the monotonic prefix of candidates
|
|
370
|
+
// rather than serializing once for every result (admission must stay bounded).
|
|
371
|
+
const emptyToolResult = (message) => {
|
|
372
|
+
if (message.role !== "toolResult" || !Array.isArray(message.content))
|
|
373
|
+
return message;
|
|
374
|
+
const emptiedContent = message.content.map((part) => part.type === "text" ? { ...part, text: "" } : part.type === "image" ? { ...part, data: "" } : part);
|
|
375
|
+
const content = emptiedContent.filter((part, index) => {
|
|
376
|
+
if (index === 0)
|
|
377
|
+
return true;
|
|
378
|
+
const previous = emptiedContent[index - 1];
|
|
379
|
+
const empty = part.type === "text" ? part.text === "" : part.type === "image" && part.data === "";
|
|
380
|
+
const previousEmpty = previous.type === "text" ? previous.text === "" : previous.type === "image" && previous.data === "";
|
|
381
|
+
return !empty || !previousEmpty;
|
|
382
|
+
});
|
|
383
|
+
return { ...message, content };
|
|
384
|
+
};
|
|
385
|
+
const toolResultIndexes = result.flatMap((message, index) => message.role === "toolResult" && Array.isArray(message.content) ? [index] : []);
|
|
386
|
+
const withEmptyPrefix = (count) => {
|
|
387
|
+
const candidate = result.slice();
|
|
388
|
+
for (let i = 0; i < count; i++)
|
|
389
|
+
candidate[toolResultIndexes[i]] = emptyToolResult(candidate[toolResultIndexes[i]]);
|
|
390
|
+
return candidate;
|
|
391
|
+
};
|
|
392
|
+
let low = 0;
|
|
393
|
+
let high = toolResultIndexes.length;
|
|
394
|
+
while (low < high) {
|
|
395
|
+
const middle = Math.floor((low + high) / 2);
|
|
396
|
+
if (fits(withEmptyPrefix(middle + 1)))
|
|
397
|
+
high = middle;
|
|
398
|
+
else
|
|
399
|
+
low = middle + 1;
|
|
400
|
+
}
|
|
401
|
+
const emptyCount = Math.min(low + 1, toolResultIndexes.length);
|
|
402
|
+
if (emptyCount > 0) {
|
|
403
|
+
result.splice(0, result.length, ...withEmptyPrefix(emptyCount));
|
|
404
|
+
changed = true;
|
|
405
|
+
}
|
|
406
|
+
if (fits())
|
|
407
|
+
return { messages: changed ? result : messages, changed };
|
|
408
|
+
// If metadata alone exceeds the cap, discard the oldest complete turns. This
|
|
409
|
+
// search is also monotonic and avoids quadratic whole-history reserialization.
|
|
410
|
+
const turnRanges = [];
|
|
411
|
+
const isConvertedUser = (message) => convert([message])[0]?.role === "user";
|
|
412
|
+
for (let index = 0; index < result.length; index++) {
|
|
413
|
+
if (!isConvertedUser(result[index]))
|
|
414
|
+
continue;
|
|
415
|
+
const nextUser = result.findIndex((message, nextIndex) => nextIndex > index && isConvertedUser(message));
|
|
416
|
+
turnRanges.push([index, nextUser < 0 ? result.length : nextUser]);
|
|
417
|
+
}
|
|
418
|
+
const withoutTurns = (count) => {
|
|
419
|
+
if (count === 0)
|
|
420
|
+
return result;
|
|
421
|
+
const start = turnRanges[0]?.[0] ?? 0;
|
|
422
|
+
const end = turnRanges[count - 1]?.[1] ?? start;
|
|
423
|
+
return [...result.slice(0, start), ...result.slice(end)];
|
|
424
|
+
};
|
|
425
|
+
low = 0;
|
|
426
|
+
high = turnRanges.length;
|
|
427
|
+
while (low < high) {
|
|
428
|
+
const middle = Math.floor((low + high) / 2);
|
|
429
|
+
if (fits(withoutTurns(middle + 1)))
|
|
430
|
+
high = middle;
|
|
431
|
+
else
|
|
432
|
+
low = middle + 1;
|
|
433
|
+
}
|
|
434
|
+
const turnCount = Math.min(low + 1, turnRanges.length);
|
|
435
|
+
if (turnCount > 0) {
|
|
436
|
+
const next = withoutTurns(turnCount);
|
|
437
|
+
result.splice(0, result.length, ...next);
|
|
438
|
+
changed = true;
|
|
439
|
+
}
|
|
440
|
+
return { messages: changed ? result : messages, changed };
|
|
441
|
+
}
|
|
313
442
|
// ============================================================================
|
|
314
443
|
// AgentSession Class
|
|
315
444
|
// ============================================================================
|
|
@@ -395,6 +524,10 @@ export class AgentSession {
|
|
|
395
524
|
this._pendingBashMessages = [];
|
|
396
525
|
this._turnIndex = 0;
|
|
397
526
|
this._baseToolDefinitions = new Map();
|
|
527
|
+
/** Policy tools withheld from the model, retained so disarming can restore direct access. */
|
|
528
|
+
this._withheldEvalOnlyToolNames = new Set();
|
|
529
|
+
/** Eval-only hint names this session published, so a disarm can withdraw exactly those. */
|
|
530
|
+
this._publishedEvalOnlyHintNames = new Set();
|
|
398
531
|
this._extensionMode = "print";
|
|
399
532
|
// Tool registry for extension getTools/setTools
|
|
400
533
|
this._toolRegistry = new Map();
|
|
@@ -516,6 +649,8 @@ export class AgentSession {
|
|
|
516
649
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
517
650
|
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
518
651
|
this._defaultToolNames = config.defaultToolNames ? new Set(config.defaultToolNames) : undefined;
|
|
652
|
+
this._evalOnlyToolNamesOverride = config.evalOnlyToolNames ? new Set(config.evalOnlyToolNames) : undefined;
|
|
653
|
+
this._evalOnlyToolNames = this._resolveEvalOnlyToolNames();
|
|
519
654
|
this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
|
|
520
655
|
this._excludedToolNames = config.excludedToolNames ? new Set(config.excludedToolNames) : undefined;
|
|
521
656
|
this._baseToolsOverride = config.baseToolsOverride;
|
|
@@ -727,6 +862,14 @@ export class AgentSession {
|
|
|
727
862
|
(this.agent.prepareNextTurn
|
|
728
863
|
? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
|
|
729
864
|
: undefined);
|
|
865
|
+
const previousTransformContext = this.agent.transformContext;
|
|
866
|
+
this.agent.transformContext = async (messages, signal) => {
|
|
867
|
+
const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
|
|
868
|
+
if (this.model?.provider === "cursor" || this.model?.provider === "cursor-cli-oauth") {
|
|
869
|
+
return ((await truncateToolResultBodies(transformed, CURSOR_TOOL_RESULT_MAX_CHARS, CURSOR_TOOL_RESULT_MAX_BYTES, (candidate) => this.agent.convertToLlm(candidate))).messages ?? transformed);
|
|
870
|
+
}
|
|
871
|
+
return transformed;
|
|
872
|
+
};
|
|
730
873
|
this.agent.prepareNextTurnWithContext = async (turn, signal) => {
|
|
731
874
|
// Enforce compaction only when this prepare precedes an actual provider
|
|
732
875
|
// admission: a tool continuation or queued steer/follow-up messages. A
|
|
@@ -735,7 +878,8 @@ export class AgentSession {
|
|
|
735
878
|
const compactBeforeNextAdmission = async () => {
|
|
736
879
|
const provider = this.model?.provider;
|
|
737
880
|
// Cursor rebuilds the full conversation each hop. Compacting here
|
|
738
|
-
// mutates rootPrompt mid-run and poisons conversationId.
|
|
881
|
+
// mutates rootPrompt mid-run and poisons conversationId (#984).
|
|
882
|
+
// Still truncate verbatim toolResult bodies so the skip cannot send MB-scale payloads (#1043).
|
|
739
883
|
if (provider === "cursor" || provider === "cursor-cli-oauth") {
|
|
740
884
|
return false;
|
|
741
885
|
}
|
|
@@ -816,6 +960,19 @@ export class AgentSession {
|
|
|
816
960
|
// Event Subscription
|
|
817
961
|
// =========================================================================
|
|
818
962
|
/** Emit an event to all listeners */
|
|
963
|
+
_emitEntryAppended(entryId) {
|
|
964
|
+
if (this._extensionMode !== "rpc")
|
|
965
|
+
return;
|
|
966
|
+
const entry = this.sessionManager.getEntry(entryId);
|
|
967
|
+
if (entry)
|
|
968
|
+
this._emit({ type: "entry_appended", entry });
|
|
969
|
+
}
|
|
970
|
+
/** Append a transport-provided entry and publish it on the RPC event stream. */
|
|
971
|
+
appendSessionEntry(entry) {
|
|
972
|
+
this.sessionManager.appendEntry(entry);
|
|
973
|
+
this.agent.state.messages = this.sessionManager.buildSessionContext().messages;
|
|
974
|
+
this._emitEntryAppended(entry.id);
|
|
975
|
+
}
|
|
819
976
|
_emit(event) {
|
|
820
977
|
this._logSessionEvent(event);
|
|
821
978
|
for (const l of this._eventListeners) {
|
|
@@ -976,6 +1133,7 @@ export class AgentSession {
|
|
|
976
1133
|
_invalidateCompactionForModelSelection() {
|
|
977
1134
|
this.abortCompaction();
|
|
978
1135
|
this.abortBranchSummary();
|
|
1136
|
+
this._delegatedCompactionKey = undefined;
|
|
979
1137
|
this._incrementMessageRevision();
|
|
980
1138
|
}
|
|
981
1139
|
_getIdleWaitPromise() {
|
|
@@ -1367,7 +1525,7 @@ export class AgentSession {
|
|
|
1367
1525
|
event.message.role === "assistant" ||
|
|
1368
1526
|
event.message.role === "toolResult") {
|
|
1369
1527
|
// Regular LLM message - persist as SessionMessageEntry
|
|
1370
|
-
this.sessionManager.appendMessage(event.message);
|
|
1528
|
+
this._emitEntryAppended(this.sessionManager.appendMessage(event.message));
|
|
1371
1529
|
this._incrementMessageRevision();
|
|
1372
1530
|
}
|
|
1373
1531
|
// Other message types (bashExecution, compactionSummary, branchSummary) are persisted elsewhere
|
|
@@ -2001,6 +2159,9 @@ export class AgentSession {
|
|
|
2001
2159
|
async executeTool(toolName, params, options) {
|
|
2002
2160
|
let activeTools = this.getActiveToolNames();
|
|
2003
2161
|
let tool = this.agent.state.tools.find((candidate) => candidate.name === toolName);
|
|
2162
|
+
if (!tool && this._isEvalOnlyPolicyArmed() && this._evalOnlyToolNames?.has(toolName)) {
|
|
2163
|
+
tool = this._toolRegistry.get(toolName);
|
|
2164
|
+
}
|
|
2004
2165
|
if (!tool &&
|
|
2005
2166
|
options?.activateInactiveTool === true &&
|
|
2006
2167
|
this._toolDefinitions.has(toolName) &&
|
|
@@ -2075,6 +2236,45 @@ export class AgentSession {
|
|
|
2075
2236
|
return false;
|
|
2076
2237
|
return this._lazyToolActivators.some((activate) => activate(toolName));
|
|
2077
2238
|
}
|
|
2239
|
+
_isEvalOnlyPolicyArmed() {
|
|
2240
|
+
return this._evalOnlyToolNames !== undefined && this._toolRegistry.has("eval");
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Resolve the eval-only policy from settings so a reload picks up a flag change.
|
|
2244
|
+
* An explicitly configured set stays authoritative for SDK embedders and tests.
|
|
2245
|
+
*/
|
|
2246
|
+
_resolveEvalOnlyToolNames() {
|
|
2247
|
+
if (this._evalOnlyToolNamesOverride !== undefined)
|
|
2248
|
+
return this._evalOnlyToolNamesOverride;
|
|
2249
|
+
const names = [
|
|
2250
|
+
...(this.settingsManager.getExperimentalBashEvalOnly() ? EVAL_ONLY_SHELL_TOOL_NAMES : []),
|
|
2251
|
+
...(this.settingsManager.getExperimentalWorkflowEvalOnly() ? EVAL_ONLY_WORKFLOW_TOOL_NAMES : []),
|
|
2252
|
+
];
|
|
2253
|
+
return names.length > 0 ? new Set(names) : undefined;
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Publish per-tool eval-only redirect hints so a direct call names its own eval helper.
|
|
2257
|
+
*
|
|
2258
|
+
* Hints published here are tracked so a disarm - or a shrinking armed set, e.g. bash+workflow
|
|
2259
|
+
* down to workflow only - withdraws the stale ones. Only tracked names are ever deleted, so
|
|
2260
|
+
* hints owned by other publishers survive.
|
|
2261
|
+
*/
|
|
2262
|
+
_publishEvalOnlyToolHints() {
|
|
2263
|
+
const armed = this._isEvalOnlyPolicyArmed() ? (this._evalOnlyToolNames ?? new Set()) : undefined;
|
|
2264
|
+
for (const name of this._publishedEvalOnlyHintNames) {
|
|
2265
|
+
if (armed?.has(name))
|
|
2266
|
+
continue;
|
|
2267
|
+
delete this.agent.removedToolHints[name];
|
|
2268
|
+
}
|
|
2269
|
+
this._publishedEvalOnlyHintNames.clear();
|
|
2270
|
+
if (!armed)
|
|
2271
|
+
return;
|
|
2272
|
+
for (const name of armed) {
|
|
2273
|
+
this.agent.removedToolHints[name] =
|
|
2274
|
+
`Run ${name} inside an eval cell via ${evalHelperCall(name)}; hooks and permissions still apply.`;
|
|
2275
|
+
this._publishedEvalOnlyHintNames.add(name);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2078
2278
|
/**
|
|
2079
2279
|
* Set active tools by name.
|
|
2080
2280
|
* Only tools in the registry can be enabled. Unknown tool names are ignored.
|
|
@@ -2102,7 +2302,23 @@ export class AgentSession {
|
|
|
2102
2302
|
setActiveToolsByName(toolNames) {
|
|
2103
2303
|
const tools = [];
|
|
2104
2304
|
const validToolNames = [];
|
|
2105
|
-
|
|
2305
|
+
const policyArmed = this._isEvalOnlyPolicyArmed();
|
|
2306
|
+
this._publishEvalOnlyToolHints();
|
|
2307
|
+
const policyNames = this._evalOnlyToolNames;
|
|
2308
|
+
let filteredToolNames = toolNames;
|
|
2309
|
+
if (policyArmed && policyNames) {
|
|
2310
|
+
for (const name of toolNames) {
|
|
2311
|
+
if (policyNames.has(name))
|
|
2312
|
+
this._withheldEvalOnlyToolNames.add(name);
|
|
2313
|
+
}
|
|
2314
|
+
filteredToolNames = toolNames.filter((name) => !policyNames.has(name));
|
|
2315
|
+
}
|
|
2316
|
+
else {
|
|
2317
|
+
this._withheldEvalOnlyToolNames.clear();
|
|
2318
|
+
}
|
|
2319
|
+
// Remember the unfiltered request so a later disarm can restore withheld shell tools.
|
|
2320
|
+
this._requestedActiveToolNames = [...new Set([...toolNames, ...this._withheldEvalOnlyToolNames])];
|
|
2321
|
+
for (const name of filteredToolNames) {
|
|
2106
2322
|
const tool = this._toolRegistry.get(name);
|
|
2107
2323
|
if (tool) {
|
|
2108
2324
|
tools.push(tool);
|
|
@@ -2116,8 +2332,14 @@ export class AgentSession {
|
|
|
2116
2332
|
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
2117
2333
|
this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
|
|
2118
2334
|
if (activeToolNamesChanged) {
|
|
2119
|
-
|
|
2120
|
-
|
|
2335
|
+
// A tool change emitted while extensions are still binding belongs to the
|
|
2336
|
+
// binding itself, not to a mid-session change. The session was already
|
|
2337
|
+
// committed to the client, so cancelling or invalidating work it started
|
|
2338
|
+
// against that session would be spurious.
|
|
2339
|
+
if (this._extensionBindingPromptReadiness === undefined) {
|
|
2340
|
+
this.abortCompaction();
|
|
2341
|
+
this._incrementMessageRevision();
|
|
2342
|
+
}
|
|
2121
2343
|
}
|
|
2122
2344
|
}
|
|
2123
2345
|
/** Whether compaction or branch summarization is currently running */
|
|
@@ -2259,9 +2481,26 @@ export class AgentSession {
|
|
|
2259
2481
|
appendSystemPrompt: loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined,
|
|
2260
2482
|
};
|
|
2261
2483
|
const basePrompt = loaderSystemPrompt ?? buildDynamicSystemPrompt(this._baseSystemPromptOptions);
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2484
|
+
const prompt = loaderAppendSystemPrompt.length > 0 ? `${basePrompt}\n\n${loaderAppendSystemPrompt.join("\n\n")}` : basePrompt;
|
|
2485
|
+
if (!this._isEvalOnlyPolicyArmed())
|
|
2486
|
+
return prompt;
|
|
2487
|
+
const armed = this._evalOnlyToolNames ?? new Set();
|
|
2488
|
+
const sentences = [];
|
|
2489
|
+
const shellHelpers = EVAL_ONLY_SHELL_TOOL_NAMES.filter((name) => armed.has(name)).map(evalHelperCall);
|
|
2490
|
+
if (shellHelpers.length > 0) {
|
|
2491
|
+
sentences.push(`Shell commands run ONLY inside eval cells via ${shellHelpers.join(" or ")}; hooks and permissions still apply.`);
|
|
2492
|
+
}
|
|
2493
|
+
if (armed.has("workflow")) {
|
|
2494
|
+
sentences.push(`The workflow tool runs ONLY inside eval cells via ${evalHelperCall("workflow")}; hooks and permissions still apply.`);
|
|
2495
|
+
}
|
|
2496
|
+
// SDK embedders can arm names outside the built-in groups; they still need eval guidance.
|
|
2497
|
+
const otherHelpers = [...armed]
|
|
2498
|
+
.filter((name) => !EVAL_ONLY_SHELL_TOOL_NAMES.includes(name) && name !== "workflow")
|
|
2499
|
+
.map(evalHelperCall);
|
|
2500
|
+
if (otherHelpers.length > 0) {
|
|
2501
|
+
sentences.push(`These tools run ONLY inside eval cells via ${otherHelpers.join(" or ")}; hooks and permissions still apply.`);
|
|
2502
|
+
}
|
|
2503
|
+
return sentences.length > 0 ? `${prompt}\n\n${sentences.join("\n\n")}` : prompt;
|
|
2265
2504
|
}
|
|
2266
2505
|
/**
|
|
2267
2506
|
* Send a prompt to the agent.
|
|
@@ -3053,7 +3292,7 @@ export class AgentSession {
|
|
|
3053
3292
|
}
|
|
3054
3293
|
_appendCustomMessage(appMessage) {
|
|
3055
3294
|
this.agent.state.messages.push(appMessage);
|
|
3056
|
-
this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details);
|
|
3295
|
+
this._emitEntryAppended(this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details));
|
|
3057
3296
|
this._incrementMessageRevision();
|
|
3058
3297
|
this._emit({ type: "message_start", message: appMessage });
|
|
3059
3298
|
this._emit({ type: "message_end", message: appMessage });
|
|
@@ -3336,7 +3575,10 @@ export class AgentSession {
|
|
|
3336
3575
|
}
|
|
3337
3576
|
async _switchActiveModel(model, opts) {
|
|
3338
3577
|
const previousModel = this.model;
|
|
3339
|
-
if (opts.invalidateCompaction &&
|
|
3578
|
+
if (opts.invalidateCompaction &&
|
|
3579
|
+
(this._modelSelectionChangesContext(previousModel, model) ||
|
|
3580
|
+
previousModel?.provider !== model.provider ||
|
|
3581
|
+
previousModel?.id !== model.id)) {
|
|
3340
3582
|
this._invalidateCompactionForModelSelection();
|
|
3341
3583
|
}
|
|
3342
3584
|
const thinking = this._getThinkingForModelSwitch(model, opts.ephemeralThinkingLevel);
|
|
@@ -3408,7 +3650,9 @@ export class AgentSession {
|
|
|
3408
3650
|
nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
3409
3651
|
}
|
|
3410
3652
|
const next = favoriteModels[nextIndex];
|
|
3411
|
-
const invalidatesCompaction = this._modelSelectionChangesContext(currentModel, next.model)
|
|
3653
|
+
const invalidatesCompaction = this._modelSelectionChangesContext(currentModel, next.model) ||
|
|
3654
|
+
currentModel?.provider !== next.model.provider ||
|
|
3655
|
+
currentModel?.id !== next.model.id;
|
|
3412
3656
|
if (invalidatesCompaction) {
|
|
3413
3657
|
this._invalidateCompactionForModelSelection();
|
|
3414
3658
|
}
|
|
@@ -3610,6 +3854,7 @@ export class AgentSession {
|
|
|
3610
3854
|
setSteeringMode(mode) {
|
|
3611
3855
|
this.agent.steeringMode = mode;
|
|
3612
3856
|
this.settingsManager.setSteeringMode(mode);
|
|
3857
|
+
this._emitSessionSettingsChanged();
|
|
3613
3858
|
}
|
|
3614
3859
|
/**
|
|
3615
3860
|
* Set follow-up message mode.
|
|
@@ -3618,6 +3863,7 @@ export class AgentSession {
|
|
|
3618
3863
|
setFollowUpMode(mode) {
|
|
3619
3864
|
this.agent.followUpMode = mode;
|
|
3620
3865
|
this.settingsManager.setFollowUpMode(mode);
|
|
3866
|
+
this._emitSessionSettingsChanged();
|
|
3621
3867
|
}
|
|
3622
3868
|
// =========================================================================
|
|
3623
3869
|
// Compaction
|
|
@@ -4100,7 +4346,7 @@ export class AgentSession {
|
|
|
4100
4346
|
if (sourceChanged) {
|
|
4101
4347
|
return await this._rejectCompaction(request, requestId, operationId, "stale-revision", false);
|
|
4102
4348
|
}
|
|
4103
|
-
if (this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
|
|
4349
|
+
if (await this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
|
|
4104
4350
|
return await this._rejectCompaction(request, requestId, operationId, "would-overflow", false);
|
|
4105
4351
|
}
|
|
4106
4352
|
const compactionEntryId = this.sessionManager.appendCompaction(compactionResult.summary, compactionResult.firstKeptEntryId, compactionResult.tokensBefore, compactionResult.details, fromExtension, compactionResult.usage);
|
|
@@ -4141,6 +4387,7 @@ export class AgentSession {
|
|
|
4141
4387
|
})) {
|
|
4142
4388
|
throw new CompactionCancelledError();
|
|
4143
4389
|
}
|
|
4390
|
+
this._delegatedCompactionKey = undefined;
|
|
4144
4391
|
if (request.owner === "compaction" && this._compactionAbortController === request.controller) {
|
|
4145
4392
|
this._compactionAbortController = undefined;
|
|
4146
4393
|
}
|
|
@@ -4191,7 +4438,7 @@ export class AgentSession {
|
|
|
4191
4438
|
finishCompactionWork();
|
|
4192
4439
|
}
|
|
4193
4440
|
}
|
|
4194
|
-
_wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model) {
|
|
4441
|
+
async _wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model) {
|
|
4195
4442
|
const currentLeaf = pathEntries[pathEntries.length - 1];
|
|
4196
4443
|
if (!currentLeaf)
|
|
4197
4444
|
return false;
|
|
@@ -4206,10 +4453,18 @@ export class AgentSession {
|
|
|
4206
4453
|
details: compactionResult.details,
|
|
4207
4454
|
fromHook: fromExtension,
|
|
4208
4455
|
};
|
|
4209
|
-
|
|
4456
|
+
let simulatedMessages = buildSessionContext([...pathEntries, simulatedCompactionEntry], simulatedCompactionEntry.id).messages;
|
|
4457
|
+
// Size the same retained context that will be admitted to Cursor. Persisted JSONL
|
|
4458
|
+
// remains verbatim, but the in-memory request representation is bounded first.
|
|
4459
|
+
if (model.provider === "cursor" || model.provider === "cursor-cli-oauth") {
|
|
4460
|
+
simulatedMessages = truncateToolResultBodies(simulatedMessages).messages ?? simulatedMessages;
|
|
4461
|
+
}
|
|
4210
4462
|
const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(simulatedMessages));
|
|
4211
4463
|
const settings = this.settingsManager.getCompactionSettings();
|
|
4212
|
-
|
|
4464
|
+
const reserveTokens = settings.reserveScalingEnabled === false
|
|
4465
|
+
? settings.reserveTokens
|
|
4466
|
+
: resolveReserveTokens(model.contextWindow, settings.reserveTokens);
|
|
4467
|
+
return contextTokens > model.contextWindow - reserveTokens;
|
|
4213
4468
|
}
|
|
4214
4469
|
/**
|
|
4215
4470
|
* Replaces agent state with the canonical session context while keeping exact
|
|
@@ -4237,6 +4492,9 @@ export class AgentSession {
|
|
|
4237
4492
|
// also emitted with accepted:false so the compaction extension's circuit-breaker
|
|
4238
4493
|
// bookkeeping stops being dead code; other builtin session_compact handlers guard
|
|
4239
4494
|
// on event.accepted.
|
|
4495
|
+
if (rejectionCause === "external-owner" && this.model) {
|
|
4496
|
+
this._delegatedCompactionKey = { provider: this.model.provider, id: this.model.id };
|
|
4497
|
+
}
|
|
4240
4498
|
const trimmedExtensionReason = extensionReason?.trim();
|
|
4241
4499
|
const detailedMessage = trimmedExtensionReason
|
|
4242
4500
|
? `Compaction rejected: ${trimmedExtensionReason}`
|
|
@@ -4370,6 +4628,9 @@ export class AgentSession {
|
|
|
4370
4628
|
if (!model)
|
|
4371
4629
|
return;
|
|
4372
4630
|
const settings = this.settingsManager.getCompactionSettings();
|
|
4631
|
+
const reserveTokens = settings.reserveScalingEnabled === false
|
|
4632
|
+
? settings.reserveTokens
|
|
4633
|
+
: resolveReserveTokens(model.contextWindow, settings.reserveTokens);
|
|
4373
4634
|
const isOversized = () => {
|
|
4374
4635
|
const providerMessages = filterContextExcludedMessages([...this.agent.state.messages, ...messages]);
|
|
4375
4636
|
const estimate = estimateContextTokens(providerMessages);
|
|
@@ -4380,7 +4641,7 @@ export class AgentSession {
|
|
|
4380
4641
|
const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
|
|
4381
4642
|
? estimateMessagesTokens(providerMessages)
|
|
4382
4643
|
: estimate.tokens;
|
|
4383
|
-
return contextTokens > model.contextWindow -
|
|
4644
|
+
return contextTokens > model.contextWindow - reserveTokens;
|
|
4384
4645
|
};
|
|
4385
4646
|
if (!settings.enabled || !isOversized())
|
|
4386
4647
|
return;
|
|
@@ -4573,15 +4834,15 @@ export class AgentSession {
|
|
|
4573
4834
|
return claimant !== undefined && !claimant.signal.aborted;
|
|
4574
4835
|
}
|
|
4575
4836
|
_isCompactionDelegated() {
|
|
4576
|
-
const state = this._compactionLifecycle.state;
|
|
4577
4837
|
const model = this.model;
|
|
4578
|
-
return (
|
|
4579
|
-
state.rejectionCause === "external-owner" &&
|
|
4580
|
-
state.model !== undefined &&
|
|
4838
|
+
return (this._delegatedCompactionKey !== undefined &&
|
|
4581
4839
|
model !== undefined &&
|
|
4582
|
-
|
|
4840
|
+
this._delegatedCompactionKey.provider === model.provider &&
|
|
4841
|
+
this._delegatedCompactionKey.id === model.id);
|
|
4583
4842
|
}
|
|
4584
4843
|
async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false, allowSummaryOnly = false) {
|
|
4844
|
+
if (this._isCompactionDelegated())
|
|
4845
|
+
return false;
|
|
4585
4846
|
const controller = new AbortController();
|
|
4586
4847
|
const requestId = randomUUID();
|
|
4587
4848
|
this._claimCompactionController(controller, "compaction");
|
|
@@ -4754,6 +5015,8 @@ export class AgentSession {
|
|
|
4754
5015
|
* @returns Whether the post-run loop should call `agent.continue()`
|
|
4755
5016
|
*/
|
|
4756
5017
|
async _runAutoCompaction(reason, willRetry) {
|
|
5018
|
+
if (this._isCompactionDelegated())
|
|
5019
|
+
return false;
|
|
4757
5020
|
const finishCompactionWork = this._sessionWorkBarrier.begin();
|
|
4758
5021
|
const agentMessagesAtStart = this.agent.state.messages.slice();
|
|
4759
5022
|
const autoCompactionController = new AbortController();
|
|
@@ -4887,6 +5150,15 @@ export class AgentSession {
|
|
|
4887
5150
|
*/
|
|
4888
5151
|
setAutoCompactionEnabled(enabled) {
|
|
4889
5152
|
this.settingsManager.setCompactionEnabled(enabled);
|
|
5153
|
+
this._emitSessionSettingsChanged();
|
|
5154
|
+
}
|
|
5155
|
+
_emitSessionSettingsChanged() {
|
|
5156
|
+
this._emit({
|
|
5157
|
+
type: "session_settings_changed",
|
|
5158
|
+
steeringMode: this.steeringMode,
|
|
5159
|
+
followUpMode: this.followUpMode,
|
|
5160
|
+
autoCompactionEnabled: this.autoCompactionEnabled,
|
|
5161
|
+
});
|
|
4890
5162
|
}
|
|
4891
5163
|
/** Whether auto-compaction is enabled */
|
|
4892
5164
|
get autoCompactionEnabled() {
|
|
@@ -4984,6 +5256,7 @@ export class AgentSession {
|
|
|
4984
5256
|
: undefined;
|
|
4985
5257
|
}
|
|
4986
5258
|
_refreshCurrentModelFromRegistry() {
|
|
5259
|
+
this._delegatedCompactionKey = undefined;
|
|
4987
5260
|
const currentModel = this.model;
|
|
4988
5261
|
if (!currentModel) {
|
|
4989
5262
|
return;
|
|
@@ -5308,8 +5581,11 @@ export class AgentSession {
|
|
|
5308
5581
|
}),
|
|
5309
5582
|
})),
|
|
5310
5583
|
].filter((tool) => isAllowedTool(tool.definition.name));
|
|
5584
|
+
// Withheld tools stay in _baseToolDefinitions (and therefore in _toolRegistry, which
|
|
5585
|
+
// getRegisteredTool serves to the Cursor exec bridge) but are dropped from the model-facing
|
|
5586
|
+
// definitions so they never reach the prompt. See temporarilyDisabledToolNames.
|
|
5311
5587
|
const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries())
|
|
5312
|
-
.filter(([name]) => isAllowedTool(name))
|
|
5588
|
+
.filter(([name]) => isAllowedTool(name) && !temporarilyDisabledToolNames.has(name))
|
|
5313
5589
|
.map(([name, definition]) => [
|
|
5314
5590
|
name,
|
|
5315
5591
|
{
|
|
@@ -5351,13 +5627,22 @@ export class AgentSession {
|
|
|
5351
5627
|
toolRegistry.set(tool.name, tool);
|
|
5352
5628
|
}
|
|
5353
5629
|
this._toolRegistry = toolRegistry;
|
|
5630
|
+
this._publishEvalOnlyToolHints();
|
|
5354
5631
|
const isDirectlyExposed = (name) => {
|
|
5355
5632
|
const entry = this._toolDefinitions.get(name);
|
|
5356
5633
|
return entry !== undefined && normalizeToolExposure(entry.definition).exposure === "direct";
|
|
5357
5634
|
};
|
|
5358
|
-
|
|
5635
|
+
// A withheld tool is dropped from the DEFAULT selection only. An explicit activeToolNames
|
|
5636
|
+
// request names the tool deliberately, and callers that do so (tests, SDK embedders,
|
|
5637
|
+
// filesystem-policy wiring) still expect it to activate.
|
|
5638
|
+
const hasExplicitActiveToolNames = options?.activeToolNames !== undefined;
|
|
5639
|
+
const nextActiveToolNames = (options?.activeToolNames
|
|
5640
|
+
? [...options.activeToolNames]
|
|
5641
|
+
: [...(this._requestedActiveToolNames ?? previousActiveToolNames)]).filter((name) => {
|
|
5359
5642
|
if (!isAllowedTool(name))
|
|
5360
5643
|
return false;
|
|
5644
|
+
if (!hasExplicitActiveToolNames && temporarilyDisabledToolNames.has(name))
|
|
5645
|
+
return false;
|
|
5361
5646
|
const previousRegistrationIds = options?.previousActiveToolRegistrationIds;
|
|
5362
5647
|
if (!previousRegistrationIds)
|
|
5363
5648
|
return true;
|
|
@@ -5388,6 +5673,7 @@ export class AgentSession {
|
|
|
5388
5673
|
this.setActiveToolsByName([...new Set(nextActiveToolNames)]);
|
|
5389
5674
|
}
|
|
5390
5675
|
_buildRuntime(options) {
|
|
5676
|
+
this._delegatedCompactionKey = undefined;
|
|
5391
5677
|
const autoResizeImages = this.settingsManager.getImageAutoResize();
|
|
5392
5678
|
const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
|
|
5393
5679
|
const shellPath = this.settingsManager.getShellPath();
|
|
@@ -5439,7 +5725,9 @@ export class AgentSession {
|
|
|
5439
5725
|
const oldExtensionIdentities = oldExtensionRunner.getExtensionIdentities();
|
|
5440
5726
|
const previousFlagValues = oldExtensionRunner.getFlagValues();
|
|
5441
5727
|
const previousActiveToolRegistrationIds = new Map();
|
|
5442
|
-
|
|
5728
|
+
// Cover withheld eval-only tools too: the rebuild drops any seeded name missing from this
|
|
5729
|
+
// map, which would strand shell tools when the policy disarms during this reload.
|
|
5730
|
+
for (const name of this._requestedActiveToolNames ?? this.getActiveToolNames()) {
|
|
5443
5731
|
const entry = this._toolDefinitions.get(name);
|
|
5444
5732
|
if (entry)
|
|
5445
5733
|
previousActiveToolRegistrationIds.set(name, deriveExtensionRegistrationId(entry.sourceInfo, name));
|
|
@@ -5450,6 +5738,12 @@ export class AgentSession {
|
|
|
5450
5738
|
});
|
|
5451
5739
|
time("shutdown", "reload");
|
|
5452
5740
|
await this.settingsManager.reload();
|
|
5741
|
+
this._delegatedCompactionKey = undefined;
|
|
5742
|
+
// Capture the unfiltered request BEFORE the rebuild reassigns it, so a policy that
|
|
5743
|
+
// disarms during this reload can still restore its withheld shell tools.
|
|
5744
|
+
const requestedActiveToolNamesBeforeRebuild = [...(this._requestedActiveToolNames ?? this.getActiveToolNames())];
|
|
5745
|
+
// A flag change must take effect on reload, matching documented settings hot-reload behavior.
|
|
5746
|
+
this._evalOnlyToolNames = this._resolveEvalOnlyToolNames();
|
|
5453
5747
|
this.syncQueueModesFromSettings();
|
|
5454
5748
|
resetApiProviders();
|
|
5455
5749
|
time("settings", "reload");
|
|
@@ -5474,7 +5768,7 @@ export class AgentSession {
|
|
|
5474
5768
|
time("resources", "reload");
|
|
5475
5769
|
try {
|
|
5476
5770
|
this._buildRuntime({
|
|
5477
|
-
activeToolNames:
|
|
5771
|
+
activeToolNames: requestedActiveToolNamesBeforeRebuild,
|
|
5478
5772
|
flagValues: previousFlagValues,
|
|
5479
5773
|
includeAllExtensionTools: true,
|
|
5480
5774
|
previousActiveToolRegistrationIds,
|
|
@@ -6179,12 +6473,13 @@ export class AgentSession {
|
|
|
6179
6473
|
try {
|
|
6180
6474
|
const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
|
|
6181
6475
|
onChunk: (delta) => {
|
|
6182
|
-
onChunk?.(delta);
|
|
6476
|
+
const callbackResult = onChunk?.(delta);
|
|
6183
6477
|
this._emit({
|
|
6184
6478
|
type: "bash_execution_update",
|
|
6185
6479
|
id: options?.id,
|
|
6186
6480
|
delta,
|
|
6187
6481
|
});
|
|
6482
|
+
return callbackResult;
|
|
6188
6483
|
},
|
|
6189
6484
|
signal: abortController.signal,
|
|
6190
6485
|
});
|
|
@@ -6232,6 +6527,10 @@ export class AgentSession {
|
|
|
6232
6527
|
abortController.abort();
|
|
6233
6528
|
}
|
|
6234
6529
|
}
|
|
6530
|
+
/** Remove a host-owned full-output spill after a remote observer failed. */
|
|
6531
|
+
async cleanupBashOutput(path) {
|
|
6532
|
+
await rm(path, { force: true });
|
|
6533
|
+
}
|
|
6235
6534
|
/** Whether a bash command is currently running */
|
|
6236
6535
|
get isBashRunning() {
|
|
6237
6536
|
return this._bashAbortControllers.size > 0;
|
|
@@ -6433,6 +6732,7 @@ export class AgentSession {
|
|
|
6433
6732
|
}
|
|
6434
6733
|
// Update agent state (preserving exact messages still awaiting persistence)
|
|
6435
6734
|
this._restoreAgentMessagesFromSession();
|
|
6735
|
+
this._delegatedCompactionKey = undefined;
|
|
6436
6736
|
this._incrementMessageRevision();
|
|
6437
6737
|
// Emit session_tree event
|
|
6438
6738
|
await this._extensionRunner.emit({
|