@f5-sales-demo/xcsh 19.51.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 +7073 -0
- package/README.md +14 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +109 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/todo/index.ts +206 -0
- package/examples/extensions/README.md +143 -0
- package/examples/extensions/api-demo.ts +89 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +32 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +551 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/todo.ts +296 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +16 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +48 -0
- package/examples/hooks/confirm-destructive.ts +58 -0
- package/examples/hooks/custom-compaction.ts +115 -0
- package/examples/hooks/dirty-repo-guard.ts +51 -0
- package/examples/hooks/file-trigger.ts +40 -0
- package/examples/hooks/git-checkpoint.ts +52 -0
- package/examples/hooks/handoff.ts +149 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +118 -0
- package/examples/hooks/status-line.ts +39 -0
- package/examples/sdk/01-minimal.ts +21 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +43 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +80 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +35 -0
- package/examples/sdk/08-prompt-templates.ts +41 -0
- package/examples/sdk/08-slash-commands.ts +46 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
- package/examples/sdk/11-sessions.ts +47 -0
- package/examples/sdk/README.md +172 -0
- package/package.json +539 -0
- package/scripts/build-info/resolvers.ts +51 -0
- package/scripts/capture-ax-fixture.ts +84 -0
- package/scripts/capture-login-fixture.ts +30 -0
- package/scripts/check-workflow-field-coverage.ts +101 -0
- package/scripts/extension-uat-harness.ts +340 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-api-spec-index.ts +580 -0
- package/scripts/generate-branding-index.ts +81 -0
- package/scripts/generate-build-info.ts +106 -0
- package/scripts/generate-console-catalog.ts +88 -0
- package/scripts/generate-console-field-metadata.ts +74 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +32 -0
- package/scripts/generate-terraform-index.ts +81 -0
- package/scripts/mermaid-gallery.ts +132 -0
- package/scripts/uat-matrix-modalities.ts +451 -0
- package/scripts/uat-matrix-report.ts +175 -0
- package/scripts/uat-matrix.ts +353 -0
- package/src/async/index.ts +2 -0
- package/src/async/job-manager.ts +416 -0
- package/src/async/support.ts +5 -0
- package/src/autoresearch/apply-contract-to-state.ts +1 -0
- package/src/autoresearch/command-resume.md +17 -0
- package/src/autoresearch/contract.ts +205 -0
- package/src/autoresearch/dashboard.ts +374 -0
- package/src/autoresearch/git.ts +191 -0
- package/src/autoresearch/helpers.ts +328 -0
- package/src/autoresearch/index.ts +402 -0
- package/src/autoresearch/prompt.md +236 -0
- package/src/autoresearch/resume-message.md +16 -0
- package/src/autoresearch/state.ts +303 -0
- package/src/autoresearch/tools/init-experiment.ts +400 -0
- package/src/autoresearch/tools/log-experiment.ts +793 -0
- package/src/autoresearch/tools/run-experiment.ts +679 -0
- package/src/autoresearch/types.ts +114 -0
- package/src/browser/acquire.ts +249 -0
- package/src/browser/actions.ts +126 -0
- package/src/browser/auth.ts +176 -0
- package/src/browser/ax.ts +134 -0
- package/src/browser/cdp-core.ts +45 -0
- package/src/browser/cdp-page-actions.ts +51 -0
- package/src/browser/chrome-locate.ts +66 -0
- package/src/browser/dom-context.ts +89 -0
- package/src/browser/dt-context.ts +100 -0
- package/src/browser/extension-bridge.ts +182 -0
- package/src/browser/extension-page-actions.ts +397 -0
- package/src/browser/extension-provider.ts +373 -0
- package/src/browser/index.ts +17 -0
- package/src/browser/input-commit.ts +74 -0
- package/src/browser/native-messaging.ts +20 -0
- package/src/browser/page-actions.ts +18 -0
- package/src/browser/provider.ts +155 -0
- package/src/browser/resolver.ts +126 -0
- package/src/browser/selector.ts +40 -0
- package/src/bun-imports.d.ts +22 -0
- package/src/capability/context-file.ts +43 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +107 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +438 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +74 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +224 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +50 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +168 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +286 -0
- package/src/cli/chrome-cli.ts +88 -0
- package/src/cli/classify-install-target.ts +50 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/config-cli.ts +418 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/grep-cli.ts +160 -0
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/jupyter-cli.ts +106 -0
- package/src/cli/list-models.ts +128 -0
- package/src/cli/plugin-cli.ts +964 -0
- package/src/cli/read-cli.ts +68 -0
- package/src/cli/session-picker.ts +52 -0
- package/src/cli/setup-cli.ts +479 -0
- package/src/cli/shell-cli.ts +174 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/stats-cli.ts +182 -0
- package/src/cli/update-cli.ts +446 -0
- package/src/cli/web-search-cli.ts +203 -0
- package/src/cli.ts +123 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/chrome-host.ts +71 -0
- package/src/commands/chrome.ts +28 -0
- package/src/commands/commit.ts +36 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +20 -0
- package/src/commands/jupyter.ts +32 -0
- package/src/commands/launch.ts +144 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +33 -0
- package/src/commands/setup.ts +42 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/ssh.ts +60 -0
- package/src/commands/stats.ts +29 -0
- package/src/commands/update.ts +21 -0
- package/src/commands/web-search.ts +46 -0
- package/src/commit/agentic/agent.ts +314 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +354 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +25 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +38 -0
- package/src/commit/agentic/state.ts +60 -0
- package/src/commit/agentic/tools/analyze-file.ts +127 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +81 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +139 -0
- package/src/commit/agentic/tools/propose-commit.ts +122 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +251 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +183 -0
- package/src/commit/analysis/conventional.ts +122 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +105 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +101 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +85 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +69 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +108 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +66 -0
- package/src/commit/pipeline.ts +242 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +44 -0
- package/src/config/auto-config.ts +698 -0
- package/src/config/context-schema.json +115 -0
- package/src/config/file-lock.ts +121 -0
- package/src/config/keybindings.ts +493 -0
- package/src/config/mcp-schema.json +230 -0
- package/src/config/model-equivalence.ts +675 -0
- package/src/config/model-registry.ts +2265 -0
- package/src/config/model-resolver.ts +1289 -0
- package/src/config/prompt-templates.ts +271 -0
- package/src/config/resolve-config-value.ts +110 -0
- package/src/config/settings-schema.ts +2155 -0
- package/src/config/settings.ts +730 -0
- package/src/config.ts +418 -0
- package/src/cursor.ts +323 -0
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/index.ts +440 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +908 -0
- package/src/debug/profiler.ts +158 -0
- package/src/debug/report-bundle.ts +365 -0
- package/src/debug/system-info.ts +107 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +219 -0
- package/src/discovery/builtin.ts +851 -0
- package/src/discovery/claude-plugins.ts +297 -0
- package/src/discovery/claude.ts +543 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +535 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +396 -0
- package/src/discovery/github.ts +118 -0
- package/src/discovery/helpers.ts +934 -0
- package/src/discovery/index.ts +79 -0
- package/src/discovery/language.ts +46 -0
- package/src/discovery/mcp-json.ts +171 -0
- package/src/discovery/opencode.ts +393 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +29 -0
- package/src/discovery/vscode.ts +105 -0
- package/src/discovery/windsurf.ts +147 -0
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +481 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +762 -0
- package/src/edit/modes/hashline.ts +1366 -0
- package/src/edit/modes/patch.ts +1791 -0
- package/src/edit/modes/replace.ts +1115 -0
- package/src/edit/normalize.ts +375 -0
- package/src/edit/renderer.ts +627 -0
- package/src/exa/factory.ts +61 -0
- package/src/exa/index.ts +27 -0
- package/src/exa/mcp-client.ts +293 -0
- package/src/exa/render.ts +232 -0
- package/src/exa/researcher.ts +47 -0
- package/src/exa/search.ts +86 -0
- package/src/exa/types.ts +166 -0
- package/src/exa/websets.ts +247 -0
- package/src/exec/bash-executor.ts +332 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +48 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +161 -0
- package/src/export/html/template.css +1001 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +46 -0
- package/src/export/html/template.js +1950 -0
- package/src/export/html/template.macro.ts +24 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/ttsr.ts +434 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +25 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +456 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +236 -0
- package/src/extensibility/custom-commands/types.ts +111 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +238 -0
- package/src/extensibility/custom-tools/types.ts +251 -0
- package/src/extensibility/custom-tools/wrapper.ts +47 -0
- package/src/extensibility/extensions/index.ts +15 -0
- package/src/extensibility/extensions/loader.ts +572 -0
- package/src/extensibility/extensions/runner.ts +848 -0
- package/src/extensibility/extensions/types.ts +1394 -0
- package/src/extensibility/extensions/wrapper.ts +191 -0
- package/src/extensibility/hooks/index.ts +5 -0
- package/src/extensibility/hooks/loader.ts +255 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +108 -0
- package/src/extensibility/hooks/types.ts +826 -0
- package/src/extensibility/plugins/doctor.ts +66 -0
- package/src/extensibility/plugins/git-url.ts +281 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +192 -0
- package/src/extensibility/plugins/loader.ts +267 -0
- package/src/extensibility/plugins/manager.ts +731 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +318 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +781 -0
- package/src/extensibility/plugins/marketplace/prerequisites.ts +255 -0
- package/src/extensibility/plugins/marketplace/registry.ts +196 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +201 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/skills.ts +269 -0
- package/src/extensibility/slash-commands.ts +246 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +61 -0
- package/src/internal-urls/agent-protocol.ts +136 -0
- package/src/internal-urls/api-catalog-resolve.ts +495 -0
- package/src/internal-urls/api-catalog-types.ts +94 -0
- package/src/internal-urls/api-spec-resolve.ts +1036 -0
- package/src/internal-urls/api-spec-types.ts +239 -0
- package/src/internal-urls/artifact-protocol.ts +97 -0
- package/src/internal-urls/branding-index.generated.ts +88 -0
- package/src/internal-urls/build-info-runtime.ts +302 -0
- package/src/internal-urls/build-info.generated.ts +33 -0
- package/src/internal-urls/computer-profile.ts +692 -0
- package/src/internal-urls/console-catalog-types.ts +21 -0
- package/src/internal-urls/console-catalog.generated.ts +532 -0
- package/src/internal-urls/console-field-metadata-types.ts +59 -0
- package/src/internal-urls/console-field-metadata.generated.ts +4841 -0
- package/src/internal-urls/console-resolve.ts +178 -0
- package/src/internal-urls/docs-index.generated.ts +698 -0
- package/src/internal-urls/extension-api.md +147 -0
- package/src/internal-urls/index.ts +46 -0
- package/src/internal-urls/jobs-protocol.ts +119 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +206 -0
- package/src/internal-urls/mcp-protocol.ts +156 -0
- package/src/internal-urls/memory-protocol.ts +133 -0
- package/src/internal-urls/parse.ts +72 -0
- package/src/internal-urls/profile-collectors.ts +106 -0
- package/src/internal-urls/router.ts +55 -0
- package/src/internal-urls/rule-protocol.ts +55 -0
- package/src/internal-urls/skill-protocol.ts +111 -0
- package/src/internal-urls/terraform-index.generated.ts +1852 -0
- package/src/internal-urls/terraform-resolve.ts +184 -0
- package/src/internal-urls/terraform-types.ts +45 -0
- package/src/internal-urls/types.ts +52 -0
- package/src/internal-urls/user-profile.ts +363 -0
- package/src/internal-urls/xcsh-protocol.ts +644 -0
- package/src/ipy/cancellation.ts +28 -0
- package/src/ipy/executor.ts +1155 -0
- package/src/ipy/gateway-coordinator.ts +424 -0
- package/src/ipy/kernel.ts +1091 -0
- package/src/ipy/modules.ts +144 -0
- package/src/ipy/prelude.py +849 -0
- package/src/ipy/prelude.ts +3 -0
- package/src/ipy/runtime.ts +221 -0
- package/src/locales/ar.json +512 -0
- package/src/locales/de.json +512 -0
- package/src/locales/en.json +549 -0
- package/src/locales/es.json +512 -0
- package/src/locales/fr.json +512 -0
- package/src/locales/hi.json +512 -0
- package/src/locales/index.ts +29 -0
- package/src/locales/it.json +512 -0
- package/src/locales/ja.json +512 -0
- package/src/locales/ko.json +512 -0
- package/src/locales/pt-br.json +512 -0
- package/src/locales/th.json +512 -0
- package/src/locales/zh-cn.json +512 -0
- package/src/locales/zh-tw.json +512 -0
- package/src/lsp/client.ts +941 -0
- package/src/lsp/clients/biome-client.ts +202 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +93 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +418 -0
- package/src/lsp/defaults.json +999 -0
- package/src/lsp/edits.ts +109 -0
- package/src/lsp/index.ts +1746 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +692 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +444 -0
- package/src/lsp/utils.ts +691 -0
- package/src/main.ts +944 -0
- package/src/mcp/client.ts +482 -0
- package/src/mcp/config-writer.ts +225 -0
- package/src/mcp/config.ts +365 -0
- package/src/mcp/discoverable-tool-metadata.ts +202 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +84 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +1152 -0
- package/src/mcp/oauth-discovery.ts +349 -0
- package/src/mcp/oauth-flow.ts +387 -0
- package/src/mcp/render.ts +157 -0
- package/src/mcp/smithery-auth.ts +104 -0
- package/src/mcp/smithery-connect.ts +145 -0
- package/src/mcp/smithery-registry.ts +477 -0
- package/src/mcp/tool-bridge.ts +417 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/http.ts +475 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +325 -0
- package/src/mcp/types.ts +423 -0
- package/src/memories/index.ts +1115 -0
- package/src/memories/storage.ts +577 -0
- package/src/modes/acp/acp-agent.ts +1361 -0
- package/src/modes/acp/acp-event-mapper.ts +538 -0
- package/src/modes/acp/acp-mode.ts +15 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/components/agent-dashboard.ts +1124 -0
- package/src/modes/components/assistant-message.ts +213 -0
- package/src/modes/components/bash-execution.ts +345 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/branch-summary-message.ts +45 -0
- package/src/modes/components/btw-panel.ts +103 -0
- package/src/modes/components/compaction-summary-message.ts +51 -0
- package/src/modes/components/context-add-wizard.ts +534 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +238 -0
- package/src/modes/components/custom-message.ts +91 -0
- package/src/modes/components/diff.ts +210 -0
- package/src/modes/components/dynamic-border.ts +25 -0
- package/src/modes/components/extensions/extension-dashboard.ts +326 -0
- package/src/modes/components/extensions/extension-list.ts +492 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +317 -0
- package/src/modes/components/extensions/state-manager.ts +587 -0
- package/src/modes/components/extensions/types.ts +191 -0
- package/src/modes/components/footer.ts +262 -0
- package/src/modes/components/gutter-block.ts +315 -0
- package/src/modes/components/history-search.ts +158 -0
- package/src/modes/components/hook-editor.ts +151 -0
- package/src/modes/components/hook-input.ts +81 -0
- package/src/modes/components/hook-message.ts +100 -0
- package/src/modes/components/hook-selector.ts +192 -0
- package/src/modes/components/index.ts +36 -0
- package/src/modes/components/keybinding-hints.ts +65 -0
- package/src/modes/components/login-dialog.ts +164 -0
- package/src/modes/components/mcp-add-wizard.ts +1314 -0
- package/src/modes/components/model-selector.ts +946 -0
- package/src/modes/components/oauth-selector.ts +211 -0
- package/src/modes/components/plugin-selector.ts +95 -0
- package/src/modes/components/plugin-settings.ts +477 -0
- package/src/modes/components/plugins/index.ts +5 -0
- package/src/modes/components/plugins/plugin-dashboard.ts +523 -0
- package/src/modes/components/plugins/plugin-inspector-pane.ts +84 -0
- package/src/modes/components/plugins/plugin-list-pane.ts +109 -0
- package/src/modes/components/plugins/state-manager.ts +229 -0
- package/src/modes/components/plugins/types.ts +51 -0
- package/src/modes/components/plugins/utils.ts +6 -0
- package/src/modes/components/python-execution.ts +244 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +137 -0
- package/src/modes/components/session-observer-overlay.ts +483 -0
- package/src/modes/components/session-selector.ts +343 -0
- package/src/modes/components/settings-defs.ts +568 -0
- package/src/modes/components/settings-selector.ts +641 -0
- package/src/modes/components/show-images-selector.ts +45 -0
- package/src/modes/components/skill-message.ts +90 -0
- package/src/modes/components/status-line/context-gradient.ts +64 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/hex-ansi.ts +29 -0
- package/src/modes/components/status-line/index.ts +4 -0
- package/src/modes/components/status-line/presets.ts +174 -0
- package/src/modes/components/status-line/segments.ts +494 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/token-rate.ts +66 -0
- package/src/modes/components/status-line/types.ts +96 -0
- package/src/modes/components/status-line-segment-editor.ts +361 -0
- package/src/modes/components/status-line.ts +729 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +52 -0
- package/src/modes/components/todo-reminder.ts +54 -0
- package/src/modes/components/tool-execution.ts +781 -0
- package/src/modes/components/tree-selector.ts +908 -0
- package/src/modes/components/ttsr-notification.ts +80 -0
- package/src/modes/components/user-message-selector.ts +141 -0
- package/src/modes/components/user-message.ts +100 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome-checks.ts +313 -0
- package/src/modes/components/welcome.ts +270 -0
- package/src/modes/controllers/btw-controller.ts +193 -0
- package/src/modes/controllers/chord-routing.ts +37 -0
- package/src/modes/controllers/command-controller.ts +1293 -0
- package/src/modes/controllers/context-command-controller.ts +61 -0
- package/src/modes/controllers/event-controller.ts +743 -0
- package/src/modes/controllers/extension-ui-controller.ts +983 -0
- package/src/modes/controllers/input-controller.ts +877 -0
- package/src/modes/controllers/mcp-command-controller.ts +1940 -0
- package/src/modes/controllers/selector-controller.ts +1292 -0
- package/src/modes/controllers/ssh-command-controller.ts +421 -0
- package/src/modes/index.ts +34 -0
- package/src/modes/interactive-mode.ts +1781 -0
- package/src/modes/oauth-manual-input.ts +42 -0
- package/src/modes/print-mode.ts +196 -0
- package/src/modes/prompt-action-autocomplete.ts +231 -0
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +762 -0
- package/src/modes/rpc/rpc-mode.ts +877 -0
- package/src/modes/rpc/rpc-types.ts +322 -0
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +30 -0
- package/src/modes/theme/defaults/index.ts +7 -0
- package/src/modes/theme/defaults/xcsh-dark.json +113 -0
- package/src/modes/theme/defaults/xcsh-light.json +115 -0
- package/src/modes/theme/mermaid-cache.ts +151 -0
- package/src/modes/theme/mermaid-palette.ts +39 -0
- package/src/modes/theme/theme-schema.json +553 -0
- package/src/modes/theme/theme.ts +2563 -0
- package/src/modes/types.ts +273 -0
- package/src/modes/utils/hotkeys-markdown.ts +60 -0
- package/src/modes/utils/keybinding-matchers.ts +30 -0
- package/src/modes/utils/read-group-outcome-aggregator.ts +55 -0
- package/src/modes/utils/sanitize-error-message.ts +60 -0
- package/src/modes/utils/tools-markdown.ts +28 -0
- package/src/modes/utils/ui-helpers.ts +734 -0
- package/src/plan-mode/approved-plan.ts +55 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +37 -0
- package/src/prompts/agents/designer.md +67 -0
- package/src/prompts/agents/explore.md +61 -0
- package/src/prompts/agents/frontmatter.md +10 -0
- package/src/prompts/agents/init.md +36 -0
- package/src/prompts/agents/librarian.md +121 -0
- package/src/prompts/agents/plan.md +49 -0
- package/src/prompts/agents/reviewer.md +127 -0
- package/src/prompts/agents/task.md +16 -0
- package/src/prompts/ci-green-request.md +35 -0
- package/src/prompts/compaction/branch-summary-context.md +5 -0
- package/src/prompts/compaction/branch-summary-preamble.md +2 -0
- package/src/prompts/compaction/branch-summary.md +30 -0
- package/src/prompts/compaction/compaction-short-summary.md +9 -0
- package/src/prompts/compaction/compaction-summary-context.md +5 -0
- package/src/prompts/compaction/compaction-summary.md +38 -0
- package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
- package/src/prompts/compaction/compaction-update-summary.md +45 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/read-path.md +11 -0
- package/src/prompts/memories/stage_one_input.md +6 -0
- package/src/prompts/memories/stage_one_system.md +21 -0
- package/src/prompts/review-request.md +70 -0
- package/src/prompts/system/agent-creation-architect.md +65 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-handoff-threshold-focus.md +1 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +2 -0
- package/src/prompts/system/custom-system-prompt.md +82 -0
- package/src/prompts/system/eager-todo.md +13 -0
- package/src/prompts/system/file-operations.md +10 -0
- package/src/prompts/system/handoff-document.md +48 -0
- package/src/prompts/system/plan-mode-active.md +114 -0
- package/src/prompts/system/plan-mode-approved.md +23 -0
- package/src/prompts/system/plan-mode-reference.md +14 -0
- package/src/prompts/system/plan-mode-subagent.md +34 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/subagent-submit-reminder.md +7 -0
- package/src/prompts/system/subagent-system-prompt.md +36 -0
- package/src/prompts/system/subagent-user-prompt.md +12 -0
- package/src/prompts/system/summarization-system.md +3 -0
- package/src/prompts/system/system-prompt.md +698 -0
- package/src/prompts/system/title-system.md +2 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/web-search.md +28 -0
- package/src/prompts/tools/ask.md +28 -0
- package/src/prompts/tools/ast-edit.md +48 -0
- package/src/prompts/tools/ast-grep.md +54 -0
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +67 -0
- package/src/prompts/tools/browser.md +26 -0
- package/src/prompts/tools/calculator.md +10 -0
- package/src/prompts/tools/cancel-job.md +5 -0
- package/src/prompts/tools/catalog-workflow-runner.md +13 -0
- package/src/prompts/tools/checkpoint.md +16 -0
- package/src/prompts/tools/chunk-edit.md +279 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/display-image.md +23 -0
- package/src/prompts/tools/exit-plan-mode.md +41 -0
- package/src/prompts/tools/find.md +26 -0
- package/src/prompts/tools/gemini-image.md +7 -0
- package/src/prompts/tools/grep.md +29 -0
- package/src/prompts/tools/hashline.md +131 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +32 -0
- package/src/prompts/tools/lsp.md +33 -0
- package/src/prompts/tools/patch.md +77 -0
- package/src/prompts/tools/poll.md +5 -0
- package/src/prompts/tools/python.md +60 -0
- package/src/prompts/tools/read-chunk.md +35 -0
- package/src/prompts/tools/read.md +63 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/prompts/tools/replace.md +36 -0
- package/src/prompts/tools/resolve.md +8 -0
- package/src/prompts/tools/rewind.md +13 -0
- package/src/prompts/tools/search-tool-bm25.md +34 -0
- package/src/prompts/tools/ssh.md +44 -0
- package/src/prompts/tools/task-summary.md +28 -0
- package/src/prompts/tools/task.md +131 -0
- package/src/prompts/tools/todo-write.md +89 -0
- package/src/prompts/tools/vim.md +98 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/prompts/tools/xcsh-api.md +283 -0
- package/src/resource-management/index.ts +42 -0
- package/src/sdk.ts +2123 -0
- package/src/secrets/index.ts +146 -0
- package/src/secrets/obfuscator.ts +290 -0
- package/src/secrets/regex.ts +21 -0
- package/src/services/context-env.ts +102 -0
- package/src/services/xcsh-api-client.ts +230 -0
- package/src/services/xcsh-context-command.ts +803 -0
- package/src/services/xcsh-context-display.ts +49 -0
- package/src/services/xcsh-context-indicators.ts +17 -0
- package/src/services/xcsh-context-segment.ts +39 -0
- package/src/services/xcsh-context.ts +1410 -0
- package/src/services/xcsh-env.ts +107 -0
- package/src/services/xcsh-knowledge.ts +177 -0
- package/src/services/xcsh-table.ts +164 -0
- package/src/session/agent-session.ts +6690 -0
- package/src/session/agent-storage.ts +451 -0
- package/src/session/artifacts.ts +132 -0
- package/src/session/auth-storage.ts +17 -0
- package/src/session/blob-store.ts +135 -0
- package/src/session/compaction/branch-summarization.ts +322 -0
- package/src/session/compaction/compaction.ts +1388 -0
- package/src/session/compaction/index.ts +7 -0
- package/src/session/compaction/pruning.ts +91 -0
- package/src/session/compaction/utils.ts +184 -0
- package/src/session/history-storage.ts +253 -0
- package/src/session/messages.ts +597 -0
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +2816 -0
- package/src/session/session-storage.ts +370 -0
- package/src/session/streaming-output.ts +770 -0
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +1614 -0
- package/src/slash-commands/export-command.ts +178 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/resource-commands.ts +250 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +445 -0
- package/src/ssh/ssh-executor.ts +131 -0
- package/src/ssh/sshfs-mount.ts +137 -0
- package/src/ssh/utils.ts +8 -0
- package/src/stt/downloader.ts +71 -0
- package/src/stt/index.ts +3 -0
- package/src/stt/recorder.ts +351 -0
- package/src/stt/setup.ts +52 -0
- package/src/stt/stt-controller.ts +160 -0
- package/src/stt/transcribe.py +70 -0
- package/src/stt/transcriber.ts +91 -0
- package/src/system-prompt.ts +688 -0
- package/src/task/agents.ts +166 -0
- package/src/task/commands.ts +131 -0
- package/src/task/discovery.ts +126 -0
- package/src/task/executor.ts +1324 -0
- package/src/task/index.ts +1197 -0
- package/src/task/isolation-backend.ts +72 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/output-manager.ts +107 -0
- package/src/task/parallel.ts +116 -0
- package/src/task/render.ts +1035 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/template.ts +33 -0
- package/src/task/types.ts +230 -0
- package/src/task/worktree.ts +580 -0
- package/src/task/xcsh-command.ts +26 -0
- package/src/thinking.ts +87 -0
- package/src/tools/action-renderer.ts +198 -0
- package/src/tools/archive-reader.ts +315 -0
- package/src/tools/ask.ts +791 -0
- package/src/tools/ast-edit.ts +492 -0
- package/src/tools/ast-grep.ts +453 -0
- package/src/tools/auto-generated-guard.ts +305 -0
- package/src/tools/bash-interactive.ts +383 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-normalize.ts +107 -0
- package/src/tools/bash-skill-urls.ts +246 -0
- package/src/tools/bash.ts +878 -0
- package/src/tools/browser-renderer.ts +129 -0
- package/src/tools/browser.ts +1366 -0
- package/src/tools/calculator.ts +543 -0
- package/src/tools/cancel-job.ts +100 -0
- package/src/tools/catalog-workflow-runner.ts +718 -0
- package/src/tools/checkpoint.ts +133 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/debug.ts +1004 -0
- package/src/tools/display-image-renderer.ts +96 -0
- package/src/tools/display-image.ts +109 -0
- package/src/tools/exit-plan-mode.ts +101 -0
- package/src/tools/fetch.ts +1462 -0
- package/src/tools/find.ts +437 -0
- package/src/tools/fs-cache-invalidation.ts +33 -0
- package/src/tools/gemini-image.ts +1004 -0
- package/src/tools/grep.ts +705 -0
- package/src/tools/index.ts +442 -0
- package/src/tools/inspect-image-renderer.ts +101 -0
- package/src/tools/inspect-image.ts +176 -0
- package/src/tools/json-tree.ts +266 -0
- package/src/tools/jtd-to-json-schema.ts +199 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/mermaid-renderer.ts +127 -0
- package/src/tools/notebook.ts +288 -0
- package/src/tools/output-meta.ts +588 -0
- package/src/tools/path-utils.ts +560 -0
- package/src/tools/plan-mode-guard.ts +41 -0
- package/src/tools/poll-tool.ts +177 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
- package/src/tools/python.ts +1184 -0
- package/src/tools/read.ts +1591 -0
- package/src/tools/render-mermaid.ts +93 -0
- package/src/tools/render-utils.ts +677 -0
- package/src/tools/renderers.ts +82 -0
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +196 -0
- package/src/tools/review.ts +238 -0
- package/src/tools/search-tool-bm25.ts +288 -0
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/ssh.ts +322 -0
- package/src/tools/submit-result.ts +229 -0
- package/src/tools/todo-render.ts +33 -0
- package/src/tools/todo-write.ts +440 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +93 -0
- package/src/tools/tool-timeouts.ts +30 -0
- package/src/tools/vim.ts +981 -0
- package/src/tools/write.ts +671 -0
- package/src/tools/xcsh-api-renderer.ts +402 -0
- package/src/tools/xcsh-api.ts +887 -0
- package/src/tui/code-cell.ts +101 -0
- package/src/tui/file-list.ts +47 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +175 -0
- package/src/tui/status-line.ts +39 -0
- package/src/tui/tree-list.ts +84 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/utils/changelog.ts +98 -0
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/command-args.ts +76 -0
- package/src/utils/commit-message-generator.ts +137 -0
- package/src/utils/edit-mode.ts +50 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +65 -0
- package/src/utils/file-display-mode.ts +37 -0
- package/src/utils/file-mentions.ts +376 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/git.ts +1495 -0
- package/src/utils/gitstatus.ts +140 -0
- package/src/utils/image-convert.ts +27 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/image-passthrough.ts +118 -0
- package/src/utils/image-resize.ts +272 -0
- package/src/utils/image-viewer.ts +17 -0
- package/src/utils/lang-from-path.ts +239 -0
- package/src/utils/markit.ts +81 -0
- package/src/utils/open.ts +20 -0
- package/src/utils/session-color.ts +55 -0
- package/src/utils/shell-snapshot.ts +188 -0
- package/src/utils/title-generator.ts +193 -0
- package/src/utils/tool-choice.ts +28 -0
- package/src/utils/tools-manager.ts +356 -0
- package/src/vim/buffer.ts +309 -0
- package/src/vim/commands.ts +382 -0
- package/src/vim/engine.ts +2426 -0
- package/src/vim/parser.ts +151 -0
- package/src/vim/render.ts +252 -0
- package/src/vim/types.ts +197 -0
- package/src/web/kagi.ts +174 -0
- package/src/web/parallel.ts +346 -0
- package/src/web/scrapers/artifacthub.ts +207 -0
- package/src/web/scrapers/arxiv.ts +83 -0
- package/src/web/scrapers/aur.ts +162 -0
- package/src/web/scrapers/biorxiv.ts +133 -0
- package/src/web/scrapers/bluesky.ts +262 -0
- package/src/web/scrapers/brew.ts +172 -0
- package/src/web/scrapers/cheatsh.ts +68 -0
- package/src/web/scrapers/chocolatey.ts +196 -0
- package/src/web/scrapers/choosealicense.ts +95 -0
- package/src/web/scrapers/cisa-kev.ts +87 -0
- package/src/web/scrapers/clojars.ts +154 -0
- package/src/web/scrapers/coingecko.ts +177 -0
- package/src/web/scrapers/crates-io.ts +110 -0
- package/src/web/scrapers/crossref.ts +136 -0
- package/src/web/scrapers/devto.ts +147 -0
- package/src/web/scrapers/discogs.ts +306 -0
- package/src/web/scrapers/discourse.ts +197 -0
- package/src/web/scrapers/dockerhub.ts +138 -0
- package/src/web/scrapers/docs-rs.ts +653 -0
- package/src/web/scrapers/fdroid.ts +134 -0
- package/src/web/scrapers/firefox-addons.ts +191 -0
- package/src/web/scrapers/flathub.ts +223 -0
- package/src/web/scrapers/github-gist.ts +58 -0
- package/src/web/scrapers/github.ts +452 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +140 -0
- package/src/web/scrapers/hackernews.ts +189 -0
- package/src/web/scrapers/hex.ts +105 -0
- package/src/web/scrapers/huggingface.ts +321 -0
- package/src/web/scrapers/iacr.ts +89 -0
- package/src/web/scrapers/index.ts +252 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
- package/src/web/scrapers/lemmy.ts +203 -0
- package/src/web/scrapers/lobsters.ts +175 -0
- package/src/web/scrapers/mastodon.ts +292 -0
- package/src/web/scrapers/maven.ts +138 -0
- package/src/web/scrapers/mdn.ts +169 -0
- package/src/web/scrapers/metacpan.ts +222 -0
- package/src/web/scrapers/musicbrainz.ts +250 -0
- package/src/web/scrapers/npm.ts +98 -0
- package/src/web/scrapers/nuget.ts +183 -0
- package/src/web/scrapers/nvd.ts +222 -0
- package/src/web/scrapers/ollama.ts +239 -0
- package/src/web/scrapers/open-vsx.ts +106 -0
- package/src/web/scrapers/opencorporates.ts +292 -0
- package/src/web/scrapers/openlibrary.ts +336 -0
- package/src/web/scrapers/orcid.ts +286 -0
- package/src/web/scrapers/osv.ts +176 -0
- package/src/web/scrapers/packagist.ts +160 -0
- package/src/web/scrapers/pub-dev.ts +143 -0
- package/src/web/scrapers/pubmed.ts +211 -0
- package/src/web/scrapers/pypi.ts +112 -0
- package/src/web/scrapers/rawg.ts +110 -0
- package/src/web/scrapers/readthedocs.ts +120 -0
- package/src/web/scrapers/reddit.ts +95 -0
- package/src/web/scrapers/repology.ts +251 -0
- package/src/web/scrapers/rfc.ts +201 -0
- package/src/web/scrapers/rubygems.ts +103 -0
- package/src/web/scrapers/searchcode.ts +190 -0
- package/src/web/scrapers/sec-edgar.ts +261 -0
- package/src/web/scrapers/semantic-scholar.ts +171 -0
- package/src/web/scrapers/snapcraft.ts +187 -0
- package/src/web/scrapers/sourcegraph.ts +336 -0
- package/src/web/scrapers/spdx.ts +108 -0
- package/src/web/scrapers/spotify.ts +198 -0
- package/src/web/scrapers/stackoverflow.ts +120 -0
- package/src/web/scrapers/terraform.ts +277 -0
- package/src/web/scrapers/tldr.ts +47 -0
- package/src/web/scrapers/twitter.ts +93 -0
- package/src/web/scrapers/types.ts +267 -0
- package/src/web/scrapers/utils.ts +109 -0
- package/src/web/scrapers/vimeo.ts +133 -0
- package/src/web/scrapers/vscode-marketplace.ts +187 -0
- package/src/web/scrapers/w3c.ts +156 -0
- package/src/web/scrapers/wikidata.ts +344 -0
- package/src/web/scrapers/wikipedia.ts +84 -0
- package/src/web/scrapers/youtube.ts +313 -0
- package/src/web/search/errors.ts +120 -0
- package/src/web/search/index.ts +346 -0
- package/src/web/search/params.ts +129 -0
- package/src/web/search/provider.ts +97 -0
- package/src/web/search/providers/anthropic.ts +339 -0
- package/src/web/search/providers/base.ts +35 -0
- package/src/web/search/providers/brave.ts +150 -0
- package/src/web/search/providers/codex.ts +402 -0
- package/src/web/search/providers/exa.ts +264 -0
- package/src/web/search/providers/firecrawl.ts +115 -0
- package/src/web/search/providers/gemini.ts +566 -0
- package/src/web/search/providers/jina.ts +99 -0
- package/src/web/search/providers/kagi.ts +70 -0
- package/src/web/search/providers/kimi.ts +157 -0
- package/src/web/search/providers/parallel.ts +63 -0
- package/src/web/search/providers/perplexity.ts +546 -0
- package/src/web/search/providers/synthetic.ts +113 -0
- package/src/web/search/providers/tavily.ts +162 -0
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +311 -0
- package/src/web/search/render.ts +348 -0
- package/src/web/search/types.ts +438 -0
- package/src/web/search/utils.ts +17 -0
|
@@ -0,0 +1,1155 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { getAgentDir, getProjectDir, isBunTestRuntime, isEnoent, logger } from "@f5xc-salesdemos/pi-utils";
|
|
3
|
+
import { OutputSink } from "../session/streaming-output";
|
|
4
|
+
import { shutdownSharedGateway } from "./gateway-coordinator";
|
|
5
|
+
import {
|
|
6
|
+
checkPythonKernelAvailability,
|
|
7
|
+
type KernelDisplayOutput,
|
|
8
|
+
type KernelExecuteOptions,
|
|
9
|
+
type KernelExecuteResult,
|
|
10
|
+
type PreludeHelper,
|
|
11
|
+
PythonKernel,
|
|
12
|
+
} from "./kernel";
|
|
13
|
+
import { discoverPythonModules } from "./modules";
|
|
14
|
+
import { PYTHON_PRELUDE } from "./prelude";
|
|
15
|
+
|
|
16
|
+
const IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
17
|
+
const MAX_KERNEL_SESSIONS = 4;
|
|
18
|
+
const CLEANUP_INTERVAL_MS = 30 * 1000; // 30 seconds
|
|
19
|
+
const OWNER_CLEANUP_KERNEL_SHUTDOWN_TIMEOUT_MS = 2_000;
|
|
20
|
+
|
|
21
|
+
export type PythonKernelMode = "session" | "per-call";
|
|
22
|
+
|
|
23
|
+
export interface PythonExecutorOptions {
|
|
24
|
+
/** Working directory for command execution */
|
|
25
|
+
cwd?: string;
|
|
26
|
+
/** Timeout in milliseconds */
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
/** Absolute wall-clock deadline in milliseconds since epoch */
|
|
29
|
+
deadlineMs?: number;
|
|
30
|
+
/** Callback for streaming output chunks (already sanitized) */
|
|
31
|
+
onChunk?: (chunk: string) => Promise<void> | void;
|
|
32
|
+
/** AbortSignal for cancellation */
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
/** Session identifier for kernel reuse */
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
/** Logical owner identifier for retained kernel cleanup */
|
|
37
|
+
kernelOwnerId?: string;
|
|
38
|
+
/** Kernel mode (session reuse vs per-call) */
|
|
39
|
+
kernelMode?: PythonKernelMode;
|
|
40
|
+
/** Restart the kernel before executing */
|
|
41
|
+
reset?: boolean;
|
|
42
|
+
/** Use shared gateway across pi instances (default: true) */
|
|
43
|
+
useSharedGateway?: boolean;
|
|
44
|
+
/** Session file path for accessing task outputs */
|
|
45
|
+
sessionFile?: string;
|
|
46
|
+
/** Artifact path/id for full output storage */
|
|
47
|
+
artifactPath?: string;
|
|
48
|
+
artifactId?: string;
|
|
49
|
+
/** Optional function to mask secrets in output */
|
|
50
|
+
maskSecrets?: ((text: string) => string) | false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PythonKernelExecutor {
|
|
54
|
+
execute: (code: string, options?: KernelExecuteOptions) => Promise<KernelExecuteResult>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface PythonResult {
|
|
58
|
+
/** Combined stdout + stderr output (sanitized, possibly truncated) */
|
|
59
|
+
output: string;
|
|
60
|
+
/** Execution exit code (0 ok, 1 error, undefined if cancelled) */
|
|
61
|
+
exitCode: number | undefined;
|
|
62
|
+
/** Whether the execution was cancelled via signal */
|
|
63
|
+
cancelled: boolean;
|
|
64
|
+
/** Whether the output was truncated */
|
|
65
|
+
truncated: boolean;
|
|
66
|
+
/** Artifact ID if full output was saved to artifact storage */
|
|
67
|
+
artifactId?: string;
|
|
68
|
+
/** Total number of lines in the output stream */
|
|
69
|
+
totalLines: number;
|
|
70
|
+
/** Total number of bytes in the output stream */
|
|
71
|
+
totalBytes: number;
|
|
72
|
+
/** Number of lines included in the output text */
|
|
73
|
+
outputLines: number;
|
|
74
|
+
/** Number of bytes included in the output text */
|
|
75
|
+
outputBytes: number;
|
|
76
|
+
/** Rich display outputs captured from display_data/execute_result */
|
|
77
|
+
displayOutputs: KernelDisplayOutput[];
|
|
78
|
+
/** Whether stdin was requested */
|
|
79
|
+
stdinRequested: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface KernelSession {
|
|
83
|
+
id: string;
|
|
84
|
+
kernel: PythonKernel;
|
|
85
|
+
queue: Promise<void>;
|
|
86
|
+
restartCount: number;
|
|
87
|
+
dead: boolean;
|
|
88
|
+
needsRestart: boolean;
|
|
89
|
+
kernelInvalidatedByRecovery: boolean;
|
|
90
|
+
disposing: boolean;
|
|
91
|
+
disposeCapacityPromise?: Promise<void>;
|
|
92
|
+
resolveDisposeCapacity?: () => void;
|
|
93
|
+
disposeAttemptPromise?: Promise<void>;
|
|
94
|
+
resolveDisposeAttempt?: () => void;
|
|
95
|
+
disposeResultPromise?: Promise<KernelDisposalResult>;
|
|
96
|
+
disposeResultTimeoutMs?: number;
|
|
97
|
+
nextDisposalRetryAt?: number;
|
|
98
|
+
lastUsedAt: number;
|
|
99
|
+
ownerIds: Set<string>;
|
|
100
|
+
hasFallbackOwner: boolean;
|
|
101
|
+
heartbeatTimer?: NodeJS.Timeout;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const kernelSessions = new Map<string, KernelSession>();
|
|
105
|
+
const disposingKernelSessions = new Set<KernelSession>();
|
|
106
|
+
let cachedPreludeDocs: PreludeHelper[] | null = null;
|
|
107
|
+
let cleanupTimer: NodeJS.Timeout | null = null;
|
|
108
|
+
|
|
109
|
+
interface KernelSessionExecutionOptions {
|
|
110
|
+
useSharedGateway?: boolean;
|
|
111
|
+
sessionFile?: string;
|
|
112
|
+
signal?: AbortSignal;
|
|
113
|
+
deadlineMs?: number;
|
|
114
|
+
kernelOwnerId?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class PythonExecutionCancelledError extends Error {
|
|
118
|
+
readonly timedOut: boolean;
|
|
119
|
+
|
|
120
|
+
constructor(timedOut: boolean) {
|
|
121
|
+
super(timedOut ? "Command timed out" : "Command aborted");
|
|
122
|
+
this.name = timedOut ? "TimeoutError" : "AbortError";
|
|
123
|
+
this.timedOut = timedOut;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function getExecutionDeadlineMs(options?: Pick<PythonExecutorOptions, "deadlineMs" | "timeoutMs">): number | undefined {
|
|
128
|
+
if (options?.deadlineMs !== undefined) return options.deadlineMs;
|
|
129
|
+
if (options?.timeoutMs === undefined) return undefined;
|
|
130
|
+
return Date.now() + options.timeoutMs;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
134
|
+
if (deadlineMs === undefined) return undefined;
|
|
135
|
+
return deadlineMs - Date.now();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function requireRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
139
|
+
const remainingMs = getRemainingTimeoutMs(deadlineMs);
|
|
140
|
+
if (remainingMs === undefined) return undefined;
|
|
141
|
+
if (remainingMs <= 0) {
|
|
142
|
+
throw new PythonExecutionCancelledError(true);
|
|
143
|
+
}
|
|
144
|
+
return remainingMs;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isCancellationError(error: unknown): boolean {
|
|
148
|
+
return (
|
|
149
|
+
error instanceof PythonExecutionCancelledError ||
|
|
150
|
+
(error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) ||
|
|
151
|
+
(error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isTimedOutCancellation(error: unknown, signal?: AbortSignal): boolean {
|
|
156
|
+
if (error instanceof PythonExecutionCancelledError) return error.timedOut;
|
|
157
|
+
if (error instanceof DOMException) return error.name === "TimeoutError";
|
|
158
|
+
if (error instanceof Error && error.name === "TimeoutError") return true;
|
|
159
|
+
const reason = signal?.reason;
|
|
160
|
+
if (reason instanceof DOMException) return reason.name === "TimeoutError";
|
|
161
|
+
return reason instanceof Error ? reason.name === "TimeoutError" : false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function waitForPromiseWithCancellation<T>(
|
|
165
|
+
promise: Promise<T>,
|
|
166
|
+
options: Pick<KernelSessionExecutionOptions, "signal" | "deadlineMs">,
|
|
167
|
+
): Promise<T> {
|
|
168
|
+
if (options.signal?.aborted) {
|
|
169
|
+
throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const remainingMs = getRemainingTimeoutMs(options.deadlineMs);
|
|
173
|
+
if (remainingMs !== undefined && remainingMs <= 0) {
|
|
174
|
+
throw new PythonExecutionCancelledError(true);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!options.signal && remainingMs === undefined) {
|
|
178
|
+
return await promise;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return await new Promise<T>((resolve, reject) => {
|
|
182
|
+
const cleanups: Array<() => void> = [];
|
|
183
|
+
const finish = (callback: () => void) => {
|
|
184
|
+
while (cleanups.length > 0) {
|
|
185
|
+
cleanups.pop()?.();
|
|
186
|
+
}
|
|
187
|
+
callback();
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const onAbort = () => {
|
|
191
|
+
finish(() =>
|
|
192
|
+
reject(new PythonExecutionCancelledError(isTimedOutCancellation(options.signal?.reason, options.signal))),
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
if (options.signal) {
|
|
197
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
198
|
+
cleanups.push(() => options.signal?.removeEventListener("abort", onAbort));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (remainingMs !== undefined) {
|
|
202
|
+
const timeout = setTimeout(() => {
|
|
203
|
+
finish(() => reject(new PythonExecutionCancelledError(true)));
|
|
204
|
+
}, remainingMs);
|
|
205
|
+
timeout.unref();
|
|
206
|
+
cleanups.push(() => clearTimeout(timeout));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
promise.then(
|
|
210
|
+
value => finish(() => resolve(value)),
|
|
211
|
+
error => finish(() => reject(error)),
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function waitForQueueTurn(
|
|
217
|
+
queue: Promise<void>,
|
|
218
|
+
options: Pick<KernelSessionExecutionOptions, "signal" | "deadlineMs">,
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
await waitForPromiseWithCancellation(queue, options);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function formatTimeoutAnnotation(timeoutMs?: number): string | undefined {
|
|
224
|
+
if (timeoutMs === undefined) return "Command timed out";
|
|
225
|
+
const secs = Math.max(1, Math.round(timeoutMs / 1000));
|
|
226
|
+
return `Command timed out after ${secs} seconds`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function createCancelledPythonResult(timedOut: boolean, timeoutMs?: number): PythonResult {
|
|
230
|
+
const output = timedOut ? (formatTimeoutAnnotation(timeoutMs) ?? "Command timed out") : "";
|
|
231
|
+
const outputBytes = Buffer.byteLength(output, "utf-8");
|
|
232
|
+
const outputLines = output.length > 0 ? 1 : 0;
|
|
233
|
+
return {
|
|
234
|
+
output,
|
|
235
|
+
exitCode: undefined,
|
|
236
|
+
cancelled: true,
|
|
237
|
+
truncated: false,
|
|
238
|
+
totalLines: outputLines,
|
|
239
|
+
totalBytes: outputBytes,
|
|
240
|
+
outputLines,
|
|
241
|
+
outputBytes,
|
|
242
|
+
displayOutputs: [],
|
|
243
|
+
stdinRequested: false,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildKernelStartOptions(
|
|
248
|
+
cwd: string,
|
|
249
|
+
env: Record<string, string> | undefined,
|
|
250
|
+
options: KernelSessionExecutionOptions,
|
|
251
|
+
) {
|
|
252
|
+
return {
|
|
253
|
+
cwd,
|
|
254
|
+
env,
|
|
255
|
+
useSharedGateway: options.useSharedGateway,
|
|
256
|
+
signal: options.signal,
|
|
257
|
+
deadlineMs: options.deadlineMs,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
interface PreludeCacheSource {
|
|
262
|
+
path: string;
|
|
263
|
+
hash: string;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
interface PreludeCachePayload {
|
|
267
|
+
helpers: PreludeHelper[];
|
|
268
|
+
sources: PreludeCacheSource[];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
interface PreludeCacheState {
|
|
272
|
+
cacheKey: string;
|
|
273
|
+
cachePath: string;
|
|
274
|
+
sources: PreludeCacheSource[];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const PRELUDE_CACHE_DIR = "pycache";
|
|
278
|
+
|
|
279
|
+
function hashPreludeContent(content: string): string {
|
|
280
|
+
return Bun.hash(content).toString(16);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function buildPreludeCacheState(cwd: string): Promise<PreludeCacheState> {
|
|
284
|
+
const modules = await discoverPythonModules({ cwd });
|
|
285
|
+
const moduleSources = modules
|
|
286
|
+
.map(module => ({ path: module.path, hash: hashPreludeContent(module.content) }))
|
|
287
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
288
|
+
const sources: PreludeCacheSource[] = [
|
|
289
|
+
{ path: "omp:prelude", hash: hashPreludeContent(PYTHON_PRELUDE) },
|
|
290
|
+
...moduleSources,
|
|
291
|
+
];
|
|
292
|
+
const composite = sources.map(source => `${source.path}:${source.hash}`).join("|");
|
|
293
|
+
const cacheKey = Bun.hash(composite).toString(16);
|
|
294
|
+
const cachePath = path.join(getAgentDir(), PRELUDE_CACHE_DIR, `${cacheKey}.json`);
|
|
295
|
+
return { cacheKey, cachePath, sources };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function readPreludeCache(state: PreludeCacheState): Promise<PreludeHelper[] | null> {
|
|
299
|
+
let raw: string;
|
|
300
|
+
try {
|
|
301
|
+
raw = await Bun.file(state.cachePath).text();
|
|
302
|
+
} catch (err) {
|
|
303
|
+
if (isEnoent(err)) return null;
|
|
304
|
+
logger.warn("Failed to read Python prelude cache", { path: state.cachePath, error: String(err) });
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const parsed = JSON.parse(raw) as PreludeCachePayload | PreludeHelper[];
|
|
309
|
+
const helpers = Array.isArray(parsed) ? parsed : parsed.helpers;
|
|
310
|
+
if (!Array.isArray(helpers) || helpers.length === 0) return null;
|
|
311
|
+
return helpers;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
logger.warn("Failed to parse Python prelude cache", { path: state.cachePath, error: String(err) });
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function writePreludeCache(state: PreludeCacheState, helpers: PreludeHelper[]): Promise<void> {
|
|
319
|
+
const payload: PreludeCachePayload = { helpers, sources: state.sources };
|
|
320
|
+
try {
|
|
321
|
+
await Bun.write(state.cachePath, JSON.stringify(payload));
|
|
322
|
+
} catch (err) {
|
|
323
|
+
logger.warn("Failed to write Python prelude cache", { path: state.cachePath, error: String(err) });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function getPreludeIntrospectionOptions(
|
|
328
|
+
options: KernelSessionExecutionOptions = {},
|
|
329
|
+
): Pick<KernelExecuteOptions, "signal" | "timeoutMs"> {
|
|
330
|
+
return {
|
|
331
|
+
signal: options.signal,
|
|
332
|
+
timeoutMs: requireRemainingTimeoutMs(options.deadlineMs),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function cachePreludeDocs(
|
|
337
|
+
cwd: string,
|
|
338
|
+
docs: PreludeHelper[],
|
|
339
|
+
cacheState?: PreludeCacheState | null,
|
|
340
|
+
): Promise<PreludeHelper[]> {
|
|
341
|
+
cachedPreludeDocs = docs;
|
|
342
|
+
if (!isBunTestRuntime() && docs.length > 0) {
|
|
343
|
+
const state = cacheState ?? (await buildPreludeCacheState(cwd));
|
|
344
|
+
await writePreludeCache(state, docs);
|
|
345
|
+
}
|
|
346
|
+
return docs;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function ensurePreludeDocsLoaded(
|
|
350
|
+
kernel: PythonKernel,
|
|
351
|
+
cwd: string,
|
|
352
|
+
options: KernelSessionExecutionOptions = {},
|
|
353
|
+
cacheState?: PreludeCacheState | null,
|
|
354
|
+
): Promise<PreludeHelper[]> {
|
|
355
|
+
if (cachedPreludeDocs && cachedPreludeDocs.length > 0) {
|
|
356
|
+
return cachedPreludeDocs;
|
|
357
|
+
}
|
|
358
|
+
const docs = await kernel.introspectPrelude(getPreludeIntrospectionOptions(options));
|
|
359
|
+
if (docs.length === 0) {
|
|
360
|
+
throw new Error("Python prelude helpers unavailable");
|
|
361
|
+
}
|
|
362
|
+
return cachePreludeDocs(cwd, docs, cacheState);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function startCleanupTimer(): void {
|
|
366
|
+
if (cleanupTimer) return;
|
|
367
|
+
cleanupTimer = setInterval(() => {
|
|
368
|
+
void cleanupIdleSessions();
|
|
369
|
+
}, CLEANUP_INTERVAL_MS);
|
|
370
|
+
cleanupTimer.unref();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function stopCleanupTimer(): void {
|
|
374
|
+
if (cleanupTimer) {
|
|
375
|
+
clearInterval(cleanupTimer);
|
|
376
|
+
cleanupTimer = null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function attachKernelOwner(sessionId: string, ownerId?: string): boolean {
|
|
381
|
+
const session = kernelSessions.get(sessionId);
|
|
382
|
+
if (!session || session.disposing) return false;
|
|
383
|
+
if (ownerId !== undefined) {
|
|
384
|
+
if (session.hasFallbackOwner) {
|
|
385
|
+
session.ownerIds.delete(sessionId);
|
|
386
|
+
session.hasFallbackOwner = false;
|
|
387
|
+
}
|
|
388
|
+
session.ownerIds.add(ownerId);
|
|
389
|
+
} else if (session.hasFallbackOwner || session.ownerIds.size === 0) {
|
|
390
|
+
session.ownerIds.add(sessionId);
|
|
391
|
+
session.hasFallbackOwner = true;
|
|
392
|
+
}
|
|
393
|
+
session.lastUsedAt = Date.now();
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getRetainedKernelSessionCount(): number {
|
|
398
|
+
return kernelSessions.size + disposingKernelSessions.size;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function syncCleanupTimer(): void {
|
|
402
|
+
if (kernelSessions.size === 0 && disposingKernelSessions.size === 0) {
|
|
403
|
+
stopCleanupTimer();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
startCleanupTimer();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function retryPendingKernelSessionDisposals(now: number = Date.now()): void {
|
|
410
|
+
for (const session of disposingKernelSessions.values()) {
|
|
411
|
+
if (session.disposeResultPromise) continue;
|
|
412
|
+
if (session.nextDisposalRetryAt !== undefined && session.nextDisposalRetryAt > now) continue;
|
|
413
|
+
session.nextDisposalRetryAt = undefined;
|
|
414
|
+
void disposeKernelSession(session);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function beginDisposingKernelSession(session: KernelSession): boolean {
|
|
419
|
+
if (session.disposing) return false;
|
|
420
|
+
session.disposing = true;
|
|
421
|
+
disposingKernelSessions.add(session);
|
|
422
|
+
if (kernelSessions.get(session.id) === session) {
|
|
423
|
+
kernelSessions.delete(session.id);
|
|
424
|
+
}
|
|
425
|
+
if (session.heartbeatTimer) {
|
|
426
|
+
clearInterval(session.heartbeatTimer);
|
|
427
|
+
session.heartbeatTimer = undefined;
|
|
428
|
+
}
|
|
429
|
+
syncCleanupTimer();
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function finishDisposingKernelSession(session: KernelSession): void {
|
|
434
|
+
disposingKernelSessions.delete(session);
|
|
435
|
+
session.resolveDisposeCapacity?.();
|
|
436
|
+
session.resolveDisposeCapacity = undefined;
|
|
437
|
+
session.disposeCapacityPromise = undefined;
|
|
438
|
+
session.resolveDisposeAttempt = undefined;
|
|
439
|
+
session.disposeAttemptPromise = undefined;
|
|
440
|
+
session.disposeResultPromise = undefined;
|
|
441
|
+
session.disposeResultTimeoutMs = undefined;
|
|
442
|
+
session.nextDisposalRetryAt = undefined;
|
|
443
|
+
session.kernelInvalidatedByRecovery = false;
|
|
444
|
+
syncCleanupTimer();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function waitForDisposalCapacity(
|
|
448
|
+
options: Pick<KernelSessionExecutionOptions, "signal" | "deadlineMs">,
|
|
449
|
+
): Promise<void> {
|
|
450
|
+
retryPendingKernelSessionDisposals();
|
|
451
|
+
|
|
452
|
+
const disposalPromises: Promise<void>[] = [];
|
|
453
|
+
let nextRetryAt: number | undefined;
|
|
454
|
+
for (const session of disposingKernelSessions.values()) {
|
|
455
|
+
if (session.disposeCapacityPromise) {
|
|
456
|
+
disposalPromises.push(
|
|
457
|
+
session.disposeCapacityPromise.then(
|
|
458
|
+
() => undefined,
|
|
459
|
+
() => undefined,
|
|
460
|
+
),
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
if (session.disposeAttemptPromise) {
|
|
464
|
+
disposalPromises.push(
|
|
465
|
+
session.disposeAttemptPromise.then(
|
|
466
|
+
() => undefined,
|
|
467
|
+
() => undefined,
|
|
468
|
+
),
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
if (session.nextDisposalRetryAt !== undefined) {
|
|
472
|
+
nextRetryAt =
|
|
473
|
+
nextRetryAt === undefined
|
|
474
|
+
? session.nextDisposalRetryAt
|
|
475
|
+
: Math.min(nextRetryAt, session.nextDisposalRetryAt);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (disposalPromises.length > 0) {
|
|
479
|
+
await waitForPromiseWithCancellation(Promise.race(disposalPromises), options);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (nextRetryAt === undefined) return;
|
|
483
|
+
await waitForPromiseWithCancellation(
|
|
484
|
+
Bun.sleep(Math.max(0, nextRetryAt - Date.now())).then(() => undefined),
|
|
485
|
+
options,
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function ensureKernelSessionCapacity(
|
|
490
|
+
options: Pick<KernelSessionExecutionOptions, "signal" | "deadlineMs">,
|
|
491
|
+
): Promise<void> {
|
|
492
|
+
while (getRetainedKernelSessionCount() >= MAX_KERNEL_SESSIONS) {
|
|
493
|
+
if (disposingKernelSessions.size > 0) {
|
|
494
|
+
await waitForDisposalCapacity(options);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (kernelSessions.size === 0) {
|
|
498
|
+
await waitForDisposalCapacity(options);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
await evictOldestSession();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async function cleanupIdleSessions(): Promise<void> {
|
|
506
|
+
const now = Date.now();
|
|
507
|
+
const toDispose: KernelSession[] = [];
|
|
508
|
+
|
|
509
|
+
for (const session of kernelSessions.values()) {
|
|
510
|
+
if (session.dead || now - session.lastUsedAt > IDLE_TIMEOUT_MS) {
|
|
511
|
+
toDispose.push(session);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (toDispose.length > 0) {
|
|
516
|
+
logger.debug("Cleaning up idle kernel sessions", { count: toDispose.length });
|
|
517
|
+
await Promise.allSettled(toDispose.map(session => disposeKernelSession(session)));
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
retryPendingKernelSessionDisposals(now);
|
|
521
|
+
syncCleanupTimer();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async function evictOldestSession(): Promise<void> {
|
|
525
|
+
let oldest: KernelSession | null = null;
|
|
526
|
+
for (const session of kernelSessions.values()) {
|
|
527
|
+
if (!oldest || session.lastUsedAt < oldest.lastUsedAt) {
|
|
528
|
+
oldest = session;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (oldest) {
|
|
532
|
+
logger.debug("Evicting oldest kernel session", { id: oldest.id });
|
|
533
|
+
await disposeKernelSession(oldest);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export async function disposeAllKernelSessions(): Promise<void> {
|
|
538
|
+
stopCleanupTimer();
|
|
539
|
+
const sessions = Array.from(new Set([...kernelSessions.values(), ...disposingKernelSessions.values()]));
|
|
540
|
+
await Promise.allSettled(sessions.map(session => disposeKernelSession(session)));
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export async function disposeKernelSessionsByOwner(ownerId: string): Promise<void> {
|
|
544
|
+
const sessionsToDispose: KernelSession[] = [];
|
|
545
|
+
for (const session of new Set([...kernelSessions.values(), ...disposingKernelSessions.values()])) {
|
|
546
|
+
if (!session.ownerIds.delete(ownerId)) continue;
|
|
547
|
+
if (session.ownerIds.size === 0) {
|
|
548
|
+
sessionsToDispose.push(session);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
await Promise.allSettled(
|
|
552
|
+
sessionsToDispose.map(session => disposeKernelSession(session, OWNER_CLEANUP_KERNEL_SHUTDOWN_TIMEOUT_MS)),
|
|
553
|
+
);
|
|
554
|
+
syncCleanupTimer();
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async function ensureKernelAvailable(
|
|
558
|
+
cwd: string,
|
|
559
|
+
options: Pick<KernelSessionExecutionOptions, "signal" | "deadlineMs"> = {},
|
|
560
|
+
): Promise<void> {
|
|
561
|
+
const availability = await waitForPromiseWithCancellation(checkPythonKernelAvailability(cwd), options);
|
|
562
|
+
if (!availability.ok) {
|
|
563
|
+
throw new Error(availability.reason ?? "Python kernel unavailable");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export async function warmPythonEnvironment(
|
|
568
|
+
cwd: string,
|
|
569
|
+
sessionId?: string,
|
|
570
|
+
useSharedGateway?: boolean,
|
|
571
|
+
sessionFile?: string,
|
|
572
|
+
kernelOwnerId?: string,
|
|
573
|
+
signal?: AbortSignal,
|
|
574
|
+
): Promise<{ ok: boolean; reason?: string; docs: PreludeHelper[] }> {
|
|
575
|
+
let cacheState: PreludeCacheState | null = null;
|
|
576
|
+
const resolvedSessionId = sessionId ?? `session:${cwd}`;
|
|
577
|
+
try {
|
|
578
|
+
await logger.time("warmPython:ensureKernelAvailable", ensureKernelAvailable, cwd, { signal });
|
|
579
|
+
} catch (err: unknown) {
|
|
580
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
581
|
+
cachedPreludeDocs = [];
|
|
582
|
+
return { ok: false, reason, docs: [] };
|
|
583
|
+
}
|
|
584
|
+
if (!isBunTestRuntime()) {
|
|
585
|
+
try {
|
|
586
|
+
cacheState = await buildPreludeCacheState(cwd);
|
|
587
|
+
const cached = await readPreludeCache(cacheState);
|
|
588
|
+
if (cached) {
|
|
589
|
+
cachedPreludeDocs = cached;
|
|
590
|
+
attachKernelOwner(resolvedSessionId, kernelOwnerId);
|
|
591
|
+
return { ok: true, docs: cached };
|
|
592
|
+
}
|
|
593
|
+
} catch (err) {
|
|
594
|
+
logger.warn("Failed to resolve Python prelude cache", { error: String(err) });
|
|
595
|
+
cacheState = null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (cachedPreludeDocs && cachedPreludeDocs.length > 0) {
|
|
599
|
+
attachKernelOwner(resolvedSessionId, kernelOwnerId);
|
|
600
|
+
return { ok: true, docs: cachedPreludeDocs };
|
|
601
|
+
}
|
|
602
|
+
try {
|
|
603
|
+
const docs = await logger.time(
|
|
604
|
+
"warmPython:withKernelSession",
|
|
605
|
+
withKernelSession,
|
|
606
|
+
resolvedSessionId,
|
|
607
|
+
cwd,
|
|
608
|
+
kernel => ensurePreludeDocsLoaded(kernel, cwd, { useSharedGateway, sessionFile, signal }, cacheState),
|
|
609
|
+
{
|
|
610
|
+
useSharedGateway,
|
|
611
|
+
sessionFile,
|
|
612
|
+
kernelOwnerId,
|
|
613
|
+
signal,
|
|
614
|
+
},
|
|
615
|
+
);
|
|
616
|
+
return { ok: true, docs };
|
|
617
|
+
} catch (err: unknown) {
|
|
618
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
619
|
+
cachedPreludeDocs = [];
|
|
620
|
+
return { ok: false, reason, docs: [] };
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export function getPreludeDocs(): PreludeHelper[] {
|
|
625
|
+
return cachedPreludeDocs ?? [];
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export function setPreludeDocsCache(docs: PreludeHelper[]): void {
|
|
629
|
+
cachedPreludeDocs = docs;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export function resetPreludeDocsCache(): void {
|
|
633
|
+
cachedPreludeDocs = null;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function isResourceExhaustionError(error: unknown): boolean {
|
|
637
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
638
|
+
return (
|
|
639
|
+
message.includes("Too many open files") ||
|
|
640
|
+
message.includes("EMFILE") ||
|
|
641
|
+
message.includes("ENFILE") ||
|
|
642
|
+
message.includes("resource temporarily unavailable")
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function clearSharedGatewayDisposingKernelSessionTracking(): void {
|
|
647
|
+
for (const session of Array.from(disposingKernelSessions.values())) {
|
|
648
|
+
if (!session.kernel.isSharedGateway) continue;
|
|
649
|
+
if (session.heartbeatTimer) {
|
|
650
|
+
clearInterval(session.heartbeatTimer);
|
|
651
|
+
session.heartbeatTimer = undefined;
|
|
652
|
+
}
|
|
653
|
+
disposingKernelSessions.delete(session);
|
|
654
|
+
session.resolveDisposeCapacity?.();
|
|
655
|
+
session.resolveDisposeCapacity = undefined;
|
|
656
|
+
session.disposeCapacityPromise = undefined;
|
|
657
|
+
session.resolveDisposeAttempt?.();
|
|
658
|
+
session.resolveDisposeAttempt = undefined;
|
|
659
|
+
session.disposeAttemptPromise = undefined;
|
|
660
|
+
session.disposeResultPromise = undefined;
|
|
661
|
+
session.disposeResultTimeoutMs = undefined;
|
|
662
|
+
session.nextDisposalRetryAt = undefined;
|
|
663
|
+
session.kernelInvalidatedByRecovery = false;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function markLiveKernelSessionsForRecovery(): void {
|
|
668
|
+
for (const session of kernelSessions.values()) {
|
|
669
|
+
if (session.heartbeatTimer) {
|
|
670
|
+
clearInterval(session.heartbeatTimer);
|
|
671
|
+
session.heartbeatTimer = undefined;
|
|
672
|
+
}
|
|
673
|
+
session.needsRestart = true;
|
|
674
|
+
session.kernelInvalidatedByRecovery = session.kernel.isSharedGateway;
|
|
675
|
+
session.restartCount = 0;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async function recoverFromResourceExhaustion(): Promise<void> {
|
|
680
|
+
logger.warn("Resource exhaustion detected, recovering by restarting shared gateway");
|
|
681
|
+
stopCleanupTimer();
|
|
682
|
+
markLiveKernelSessionsForRecovery();
|
|
683
|
+
clearSharedGatewayDisposingKernelSessionTracking();
|
|
684
|
+
await shutdownSharedGateway();
|
|
685
|
+
syncCleanupTimer();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function ensureKernelHeartbeat(session: KernelSession): void {
|
|
689
|
+
if (session.heartbeatTimer) return;
|
|
690
|
+
session.heartbeatTimer = setInterval(() => {
|
|
691
|
+
if (session.dead || session.needsRestart) return;
|
|
692
|
+
if (!session.kernel.isAlive()) {
|
|
693
|
+
session.dead = true;
|
|
694
|
+
}
|
|
695
|
+
}, 5000);
|
|
696
|
+
session.heartbeatTimer.unref();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
async function createKernelSession(
|
|
700
|
+
sessionId: string,
|
|
701
|
+
cwd: string,
|
|
702
|
+
options: KernelSessionExecutionOptions = {},
|
|
703
|
+
isRetry?: boolean,
|
|
704
|
+
): Promise<KernelSession> {
|
|
705
|
+
requireRemainingTimeoutMs(options.deadlineMs);
|
|
706
|
+
const env: Record<string, string> | undefined = options.sessionFile
|
|
707
|
+
? { PI_SESSION_FILE: options.sessionFile }
|
|
708
|
+
: undefined;
|
|
709
|
+
const startOptions = buildKernelStartOptions(cwd, env, options);
|
|
710
|
+
|
|
711
|
+
let kernel: PythonKernel;
|
|
712
|
+
try {
|
|
713
|
+
kernel = await logger.time("createKernelSession:PythonKernel.start", PythonKernel.start, startOptions);
|
|
714
|
+
} catch (err) {
|
|
715
|
+
if (!isRetry && isResourceExhaustionError(err)) {
|
|
716
|
+
await recoverFromResourceExhaustion();
|
|
717
|
+
return createKernelSession(sessionId, cwd, options, true);
|
|
718
|
+
}
|
|
719
|
+
throw err;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const hasFallbackOwner = options.kernelOwnerId === undefined;
|
|
723
|
+
const initialOwnerId = options.kernelOwnerId ?? sessionId;
|
|
724
|
+
const session: KernelSession = {
|
|
725
|
+
id: sessionId,
|
|
726
|
+
kernel,
|
|
727
|
+
queue: Promise.resolve(),
|
|
728
|
+
restartCount: 0,
|
|
729
|
+
dead: false,
|
|
730
|
+
needsRestart: false,
|
|
731
|
+
kernelInvalidatedByRecovery: false,
|
|
732
|
+
disposing: false,
|
|
733
|
+
disposeResultPromise: undefined,
|
|
734
|
+
nextDisposalRetryAt: undefined,
|
|
735
|
+
lastUsedAt: Date.now(),
|
|
736
|
+
ownerIds: new Set([initialOwnerId]),
|
|
737
|
+
hasFallbackOwner,
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
ensureKernelHeartbeat(session);
|
|
741
|
+
|
|
742
|
+
return session;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
async function restartKernelSession(
|
|
746
|
+
session: KernelSession,
|
|
747
|
+
cwd: string,
|
|
748
|
+
options: KernelSessionExecutionOptions = {},
|
|
749
|
+
): Promise<void> {
|
|
750
|
+
session.restartCount += 1;
|
|
751
|
+
if (session.restartCount > 1) {
|
|
752
|
+
throw new Error("Python kernel restarted too many times in this session");
|
|
753
|
+
}
|
|
754
|
+
requireRemainingTimeoutMs(options.deadlineMs);
|
|
755
|
+
try {
|
|
756
|
+
if (!session.kernelInvalidatedByRecovery) {
|
|
757
|
+
const deadKernel = session.dead || !session.kernel.isAlive();
|
|
758
|
+
const shutdownTimeoutMs = requireRemainingTimeoutMs(options.deadlineMs);
|
|
759
|
+
const shutdownResult = await session.kernel.shutdown({ signal: options.signal, timeoutMs: shutdownTimeoutMs });
|
|
760
|
+
if (!shutdownResult.confirmed && !deadKernel) {
|
|
761
|
+
throw new Error("Failed to confirm crashed kernel shutdown before restart");
|
|
762
|
+
}
|
|
763
|
+
if (!shutdownResult.confirmed) {
|
|
764
|
+
logger.warn("Proceeding with retained kernel restart after unconfirmed dead-kernel shutdown", {
|
|
765
|
+
sessionId: session.id,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const env: Record<string, string> | undefined = options.sessionFile
|
|
770
|
+
? { PI_SESSION_FILE: options.sessionFile }
|
|
771
|
+
: undefined;
|
|
772
|
+
const startOptions = buildKernelStartOptions(cwd, env, options);
|
|
773
|
+
const kernel = await PythonKernel.start(startOptions);
|
|
774
|
+
session.kernel = kernel;
|
|
775
|
+
session.dead = false;
|
|
776
|
+
session.needsRestart = false;
|
|
777
|
+
session.kernelInvalidatedByRecovery = false;
|
|
778
|
+
session.lastUsedAt = Date.now();
|
|
779
|
+
ensureKernelHeartbeat(session);
|
|
780
|
+
} catch (err) {
|
|
781
|
+
session.restartCount = 0;
|
|
782
|
+
logger.warn("Failed to restart kernel", { error: err instanceof Error ? err.message : String(err) });
|
|
783
|
+
throw err;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
type KernelDisposalResult = { status: "confirmed" } | { status: "unconfirmed" } | { status: "failed"; err: unknown };
|
|
788
|
+
type KernelDisposalWaitResult = KernelDisposalResult | { status: "timedOut" };
|
|
789
|
+
|
|
790
|
+
function createKernelDisposalResultPromise(session: KernelSession, timeoutMs?: number): Promise<KernelDisposalResult> {
|
|
791
|
+
if (session.kernelInvalidatedByRecovery) {
|
|
792
|
+
return Promise.resolve({ status: "confirmed" as const });
|
|
793
|
+
}
|
|
794
|
+
return Promise.resolve()
|
|
795
|
+
.then(() => session.kernel.shutdown(timeoutMs === undefined ? undefined : { timeoutMs }))
|
|
796
|
+
.then(
|
|
797
|
+
result => (result.confirmed ? { status: "confirmed" as const } : { status: "unconfirmed" as const }),
|
|
798
|
+
(err: unknown) => ({ status: "failed" as const, err }),
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function getOrStartKernelDisposalResultPromise(
|
|
803
|
+
session: KernelSession,
|
|
804
|
+
timeoutMs?: number,
|
|
805
|
+
): Promise<KernelDisposalResult> {
|
|
806
|
+
if (!session.disposeResultPromise) {
|
|
807
|
+
session.disposeResultTimeoutMs = timeoutMs;
|
|
808
|
+
const releaseDisposalAttempt = Promise.withResolvers<void>();
|
|
809
|
+
session.disposeAttemptPromise = releaseDisposalAttempt.promise;
|
|
810
|
+
session.resolveDisposeAttempt = releaseDisposalAttempt.resolve;
|
|
811
|
+
const disposeResultPromise = createKernelDisposalResultPromise(session, timeoutMs);
|
|
812
|
+
void disposeResultPromise.then(result => {
|
|
813
|
+
if (result.status === "confirmed") {
|
|
814
|
+
finishDisposingKernelSession(session);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
if (session.disposing) {
|
|
818
|
+
session.nextDisposalRetryAt = Date.now() + CLEANUP_INTERVAL_MS;
|
|
819
|
+
syncCleanupTimer();
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
const disposalAttemptPromise = disposeResultPromise.finally(() => {
|
|
823
|
+
releaseDisposalAttempt.resolve();
|
|
824
|
+
if (session.disposeResultPromise === disposalAttemptPromise) {
|
|
825
|
+
session.disposeResultPromise = undefined;
|
|
826
|
+
session.disposeResultTimeoutMs = undefined;
|
|
827
|
+
}
|
|
828
|
+
if (session.disposeAttemptPromise === releaseDisposalAttempt.promise) {
|
|
829
|
+
session.disposeAttemptPromise = undefined;
|
|
830
|
+
session.resolveDisposeAttempt = undefined;
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
session.disposeResultPromise = disposalAttemptPromise;
|
|
834
|
+
}
|
|
835
|
+
return session.disposeResultPromise;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
async function waitForKernelSessionDisposal(
|
|
839
|
+
session: KernelSession,
|
|
840
|
+
timeoutMs?: number,
|
|
841
|
+
): Promise<KernelDisposalWaitResult | undefined> {
|
|
842
|
+
const disposeResultPromise = session.disposeResultPromise;
|
|
843
|
+
if (!disposeResultPromise) {
|
|
844
|
+
return undefined;
|
|
845
|
+
}
|
|
846
|
+
if (timeoutMs === undefined) {
|
|
847
|
+
return await disposeResultPromise;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
851
|
+
const result = await Promise.race([
|
|
852
|
+
disposeResultPromise,
|
|
853
|
+
new Promise<{ status: "timedOut" }>(resolve => {
|
|
854
|
+
timeoutId = setTimeout(() => resolve({ status: "timedOut" }), timeoutMs);
|
|
855
|
+
timeoutId.unref();
|
|
856
|
+
}),
|
|
857
|
+
]);
|
|
858
|
+
|
|
859
|
+
if (timeoutId) {
|
|
860
|
+
clearTimeout(timeoutId);
|
|
861
|
+
}
|
|
862
|
+
return result;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function retryKernelSessionDisposalInBackground(session: KernelSession): void {
|
|
866
|
+
session.nextDisposalRetryAt = undefined;
|
|
867
|
+
void disposeKernelSession(session);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
async function disposeKernelSession(session: KernelSession, shutdownTimeoutMs?: number): Promise<void> {
|
|
871
|
+
if (!session.disposing) {
|
|
872
|
+
if (!beginDisposingKernelSession(session)) return;
|
|
873
|
+
const releaseDisposalCapacity = Promise.withResolvers<void>();
|
|
874
|
+
session.disposeCapacityPromise = releaseDisposalCapacity.promise;
|
|
875
|
+
session.resolveDisposeCapacity = releaseDisposalCapacity.resolve;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (
|
|
879
|
+
shutdownTimeoutMs === undefined &&
|
|
880
|
+
session.disposeResultPromise &&
|
|
881
|
+
session.disposeResultTimeoutMs !== undefined
|
|
882
|
+
) {
|
|
883
|
+
const inheritedResult = await session.disposeResultPromise;
|
|
884
|
+
if (inheritedResult.status === "confirmed") {
|
|
885
|
+
finishDisposingKernelSession(session);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
session.disposeResultPromise = undefined;
|
|
889
|
+
session.disposeResultTimeoutMs = undefined;
|
|
890
|
+
logger.warn("Retained kernel shutdown was not confirmed during owner cleanup; retrying without timeout", {
|
|
891
|
+
sessionId: session.id,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const inheritedBackgroundRetryTimeoutMs =
|
|
896
|
+
shutdownTimeoutMs === undefined && session.disposeResultPromise && session.disposeResultTimeoutMs === undefined
|
|
897
|
+
? OWNER_CLEANUP_KERNEL_SHUTDOWN_TIMEOUT_MS
|
|
898
|
+
: shutdownTimeoutMs;
|
|
899
|
+
|
|
900
|
+
getOrStartKernelDisposalResultPromise(session, shutdownTimeoutMs);
|
|
901
|
+
const result = await waitForKernelSessionDisposal(session, inheritedBackgroundRetryTimeoutMs);
|
|
902
|
+
if (!result) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (result.status === "timedOut") {
|
|
906
|
+
logger.warn(
|
|
907
|
+
shutdownTimeoutMs === undefined
|
|
908
|
+
? "Timed out waiting for retained kernel shutdown during global cleanup; retained capacity remains reserved"
|
|
909
|
+
: "Timed out shutting down retained kernel during owner cleanup",
|
|
910
|
+
{
|
|
911
|
+
sessionId: session.id,
|
|
912
|
+
timeoutMs: inheritedBackgroundRetryTimeoutMs,
|
|
913
|
+
},
|
|
914
|
+
);
|
|
915
|
+
if (shutdownTimeoutMs !== undefined) {
|
|
916
|
+
retryKernelSessionDisposalInBackground(session);
|
|
917
|
+
}
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (result.status === "confirmed") {
|
|
921
|
+
finishDisposingKernelSession(session);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (result.status === "unconfirmed") {
|
|
925
|
+
logger.warn(
|
|
926
|
+
shutdownTimeoutMs === undefined
|
|
927
|
+
? "Kernel shutdown was not confirmed; retained capacity remains reserved"
|
|
928
|
+
: "Retained kernel shutdown was not confirmed during owner cleanup",
|
|
929
|
+
{ sessionId: session.id },
|
|
930
|
+
);
|
|
931
|
+
if (shutdownTimeoutMs !== undefined) {
|
|
932
|
+
retryKernelSessionDisposalInBackground(session);
|
|
933
|
+
}
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
logger.warn(
|
|
937
|
+
shutdownTimeoutMs === undefined
|
|
938
|
+
? "Failed to shutdown kernel"
|
|
939
|
+
: "Failed to shutdown retained kernel during owner cleanup",
|
|
940
|
+
{
|
|
941
|
+
sessionId: session.id,
|
|
942
|
+
error: result.err instanceof Error ? result.err.message : String(result.err),
|
|
943
|
+
},
|
|
944
|
+
);
|
|
945
|
+
if (shutdownTimeoutMs !== undefined) {
|
|
946
|
+
retryKernelSessionDisposalInBackground(session);
|
|
947
|
+
}
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
async function withKernelSession<T>(
|
|
952
|
+
sessionId: string,
|
|
953
|
+
cwd: string,
|
|
954
|
+
handler: (kernel: PythonKernel) => Promise<T>,
|
|
955
|
+
options: KernelSessionExecutionOptions = {},
|
|
956
|
+
): Promise<T> {
|
|
957
|
+
let session = kernelSessions.get(sessionId);
|
|
958
|
+
if (session?.disposing) {
|
|
959
|
+
session = undefined;
|
|
960
|
+
}
|
|
961
|
+
if (!session) {
|
|
962
|
+
await ensureKernelSessionCapacity(options);
|
|
963
|
+
requireRemainingTimeoutMs(options.deadlineMs);
|
|
964
|
+
if (options.signal?.aborted) {
|
|
965
|
+
throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
|
966
|
+
}
|
|
967
|
+
session = await logger.time("kernel:createKernelSession", createKernelSession, sessionId, cwd, options);
|
|
968
|
+
kernelSessions.set(sessionId, session);
|
|
969
|
+
startCleanupTimer();
|
|
970
|
+
}
|
|
971
|
+
attachKernelOwner(sessionId, options.kernelOwnerId);
|
|
972
|
+
|
|
973
|
+
if (session.disposing) {
|
|
974
|
+
return await withKernelSession(sessionId, cwd, handler, options);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const run = async (): Promise<T> => {
|
|
978
|
+
session!.lastUsedAt = Date.now();
|
|
979
|
+
if (session!.dead || session!.needsRestart || !session!.kernel.isAlive()) {
|
|
980
|
+
await logger.time("kernel:restartKernelSession", restartKernelSession, session!, cwd, options);
|
|
981
|
+
}
|
|
982
|
+
try {
|
|
983
|
+
const result = await logger.time("kernel:withSession:handler", handler, session!.kernel);
|
|
984
|
+
session!.restartCount = 0;
|
|
985
|
+
return result;
|
|
986
|
+
} catch (err) {
|
|
987
|
+
if (!session!.dead && !session!.needsRestart && session!.kernel.isAlive()) {
|
|
988
|
+
throw err;
|
|
989
|
+
}
|
|
990
|
+
await logger.time("kernel:restartKernelSession", restartKernelSession, session!, cwd, options);
|
|
991
|
+
const result = await logger.time("kernel:postRestart:handler", handler, session!.kernel);
|
|
992
|
+
session!.restartCount = 0;
|
|
993
|
+
return result;
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const queue = session.queue;
|
|
998
|
+
let releaseTurn: (() => void) | undefined;
|
|
999
|
+
const turn = new Promise<void>(resolve => {
|
|
1000
|
+
releaseTurn = resolve;
|
|
1001
|
+
});
|
|
1002
|
+
session.queue = queue
|
|
1003
|
+
.then(
|
|
1004
|
+
() => turn,
|
|
1005
|
+
() => turn,
|
|
1006
|
+
)
|
|
1007
|
+
.then(
|
|
1008
|
+
() => undefined,
|
|
1009
|
+
() => undefined,
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
try {
|
|
1013
|
+
await waitForQueueTurn(queue, options);
|
|
1014
|
+
if (session.disposing) {
|
|
1015
|
+
return await withKernelSession(sessionId, cwd, handler, options);
|
|
1016
|
+
}
|
|
1017
|
+
return await run();
|
|
1018
|
+
} finally {
|
|
1019
|
+
releaseTurn?.();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
async function executeWithKernel(
|
|
1024
|
+
kernel: PythonKernelExecutor,
|
|
1025
|
+
code: string,
|
|
1026
|
+
options: PythonExecutorOptions | undefined,
|
|
1027
|
+
): Promise<PythonResult> {
|
|
1028
|
+
const sink = new OutputSink({
|
|
1029
|
+
onChunk: options?.onChunk,
|
|
1030
|
+
artifactPath: options?.artifactPath,
|
|
1031
|
+
artifactId: options?.artifactId,
|
|
1032
|
+
});
|
|
1033
|
+
const displayOutputs: KernelDisplayOutput[] = [];
|
|
1034
|
+
const deadlineMs = getExecutionDeadlineMs(options);
|
|
1035
|
+
let executionTimeoutMs: number | undefined;
|
|
1036
|
+
|
|
1037
|
+
try {
|
|
1038
|
+
executionTimeoutMs = requireRemainingTimeoutMs(deadlineMs);
|
|
1039
|
+
const result = await kernel.execute(code, {
|
|
1040
|
+
signal: options?.signal,
|
|
1041
|
+
timeoutMs: executionTimeoutMs,
|
|
1042
|
+
onChunk: text => sink.push(text),
|
|
1043
|
+
onDisplay: output => void displayOutputs.push(output),
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
if (result.cancelled) {
|
|
1047
|
+
const annotation = result.timedOut ? formatTimeoutAnnotation(executionTimeoutMs) : undefined;
|
|
1048
|
+
return {
|
|
1049
|
+
exitCode: undefined,
|
|
1050
|
+
cancelled: true,
|
|
1051
|
+
displayOutputs,
|
|
1052
|
+
stdinRequested: result.stdinRequested,
|
|
1053
|
+
...(await sink.dump(annotation)),
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
if (result.stdinRequested) {
|
|
1058
|
+
return {
|
|
1059
|
+
exitCode: 1,
|
|
1060
|
+
cancelled: false,
|
|
1061
|
+
displayOutputs,
|
|
1062
|
+
stdinRequested: true,
|
|
1063
|
+
...(await sink.dump("Kernel requested stdin; interactive input is not supported.")),
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
const exitCode = result.status === "ok" ? 0 : 1;
|
|
1068
|
+
return {
|
|
1069
|
+
exitCode,
|
|
1070
|
+
cancelled: false,
|
|
1071
|
+
displayOutputs,
|
|
1072
|
+
stdinRequested: false,
|
|
1073
|
+
...(await sink.dump()),
|
|
1074
|
+
};
|
|
1075
|
+
} catch (err) {
|
|
1076
|
+
if (isCancellationError(err) || options?.signal?.aborted) {
|
|
1077
|
+
const timedOut = isTimedOutCancellation(err, options?.signal);
|
|
1078
|
+
return {
|
|
1079
|
+
exitCode: undefined,
|
|
1080
|
+
cancelled: true,
|
|
1081
|
+
displayOutputs,
|
|
1082
|
+
stdinRequested: false,
|
|
1083
|
+
...(await sink.dump(timedOut ? formatTimeoutAnnotation(executionTimeoutMs) : undefined)),
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1087
|
+
logger.error("Python execution failed", { error: error.message });
|
|
1088
|
+
throw error;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
export async function executePythonWithKernel(
|
|
1093
|
+
kernel: PythonKernelExecutor,
|
|
1094
|
+
code: string,
|
|
1095
|
+
options?: PythonExecutorOptions,
|
|
1096
|
+
): Promise<PythonResult> {
|
|
1097
|
+
return await executeWithKernel(kernel, code, options);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
export async function executePython(code: string, options?: PythonExecutorOptions): Promise<PythonResult> {
|
|
1101
|
+
const cwd = options?.cwd ?? getProjectDir();
|
|
1102
|
+
const deadlineMs = getExecutionDeadlineMs(options);
|
|
1103
|
+
const executionOptions: PythonExecutorOptions = {
|
|
1104
|
+
...(options ?? {}),
|
|
1105
|
+
deadlineMs,
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
try {
|
|
1109
|
+
requireRemainingTimeoutMs(deadlineMs);
|
|
1110
|
+
if (executionOptions.signal?.aborted) {
|
|
1111
|
+
throw new PythonExecutionCancelledError(
|
|
1112
|
+
isTimedOutCancellation(executionOptions.signal.reason, executionOptions.signal),
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
await ensureKernelAvailable(cwd);
|
|
1117
|
+
|
|
1118
|
+
const kernelMode = executionOptions.kernelMode ?? "session";
|
|
1119
|
+
const sessionFile = executionOptions.sessionFile;
|
|
1120
|
+
|
|
1121
|
+
if (kernelMode === "per-call") {
|
|
1122
|
+
const env: Record<string, string> | undefined = sessionFile ? { PI_SESSION_FILE: sessionFile } : undefined;
|
|
1123
|
+
requireRemainingTimeoutMs(deadlineMs);
|
|
1124
|
+
const startOptions = buildKernelStartOptions(cwd, env, executionOptions);
|
|
1125
|
+
const kernel = await PythonKernel.start(startOptions);
|
|
1126
|
+
try {
|
|
1127
|
+
return await executeWithKernel(kernel, code, executionOptions);
|
|
1128
|
+
} finally {
|
|
1129
|
+
await kernel.shutdown();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const sessionId = executionOptions.sessionId ?? `session:${cwd}`;
|
|
1134
|
+
if (executionOptions.reset) {
|
|
1135
|
+
const existing = kernelSessions.get(sessionId);
|
|
1136
|
+
if (existing) {
|
|
1137
|
+
await disposeKernelSession(existing);
|
|
1138
|
+
if (existing.disposing && existing.nextDisposalRetryAt !== undefined) {
|
|
1139
|
+
retryKernelSessionDisposalInBackground(existing);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return await withKernelSession(
|
|
1144
|
+
sessionId,
|
|
1145
|
+
cwd,
|
|
1146
|
+
async kernel => executeWithKernel(kernel, code, executionOptions),
|
|
1147
|
+
executionOptions,
|
|
1148
|
+
);
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
if (isCancellationError(err) || executionOptions.signal?.aborted) {
|
|
1151
|
+
return createCancelledPythonResult(isTimedOutCancellation(err, executionOptions.signal));
|
|
1152
|
+
}
|
|
1153
|
+
throw err;
|
|
1154
|
+
}
|
|
1155
|
+
}
|