@ashx-j/lunr 0.2.13 → 0.2.15
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 +27 -1
- package/README.md +10 -7
- package/dist/builtin-extensions/ashxj-tui.d.ts +16 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +48 -24
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +2 -0
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-settings-tools.d.ts +4 -0
- package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +1 -0
- package/dist/builtin-extensions/lunr-settings-tools.js +120 -0
- package/dist/builtin-extensions/lunr-settings-tools.js.map +1 -0
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.d.ts +2 -2
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js +3 -3
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.d.ts +2 -2
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +29 -23
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +5 -11
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +12 -15
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +3 -3
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +14 -27
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +9 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +16 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js +7 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.js +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +3 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js +2 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +3 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js +3 -35
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +7 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +20 -21
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +12 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +42 -17
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +23 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +3 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +15 -4
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +14 -3
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts +3 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js +7 -6
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +6 -4
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +15 -10
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js +5 -4
- package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -2
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +86 -52
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/amazon-bedrock.json +1 -1
- package/dist/catalog/providers/anthropic.json +1 -1
- package/dist/catalog/providers/azure-openai-responses.json +1 -1
- package/dist/catalog/providers/cloudflare-ai-gateway.json +1 -1
- package/dist/catalog/providers/fireworks.json +1 -1
- package/dist/catalog/providers/github-copilot.json +1 -1
- package/dist/catalog/providers/google-vertex.json +1 -1
- package/dist/catalog/providers/google.json +1 -1
- package/dist/catalog/providers/groq.json +1 -1
- package/dist/catalog/providers/huggingface.json +1 -1
- package/dist/catalog/providers/nvidia.json +1 -1
- package/dist/catalog/providers/openai-codex.json +1 -1
- package/dist/catalog/providers/openai.json +1 -1
- package/dist/catalog/providers/opencode-go.json +1 -1
- package/dist/catalog/providers/opencode.json +1 -1
- package/dist/catalog/providers/openrouter.json +1 -1
- package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
- package/dist/catalog/publication.json +131 -3
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +1 -0
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +52 -27
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +5 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +7 -2
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +18 -6
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/catalog-cache-scope.d.ts +3 -0
- package/dist/core/catalog-cache-scope.d.ts.map +1 -0
- package/dist/core/catalog-cache-scope.js +12 -0
- package/dist/core/catalog-cache-scope.js.map +1 -0
- package/dist/core/catalog-merge.d.ts +2 -0
- package/dist/core/catalog-merge.d.ts.map +1 -1
- package/dist/core/catalog-merge.js +29 -2
- package/dist/core/catalog-merge.js.map +1 -1
- package/dist/core/catalog-refresh-polling.d.ts +8 -0
- package/dist/core/catalog-refresh-polling.d.ts.map +1 -0
- package/dist/core/catalog-refresh-polling.js +30 -0
- package/dist/core/catalog-refresh-polling.js.map +1 -0
- package/dist/core/codex-catalog.d.ts +3 -0
- package/dist/core/codex-catalog.d.ts.map +1 -0
- package/dist/core/codex-catalog.js +15 -0
- package/dist/core/codex-catalog.js.map +1 -0
- package/dist/core/context-breakdown.d.ts +2 -0
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +6 -2
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/customize.d.ts +2 -4
- package/dist/core/customize.d.ts.map +1 -1
- package/dist/core/customize.js +2 -8
- package/dist/core/customize.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +3 -4
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +54 -82
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/injected-prompt.d.ts +3 -3
- package/dist/core/injected-prompt.d.ts.map +1 -1
- package/dist/core/injected-prompt.js +2 -7
- package/dist/core/injected-prompt.js.map +1 -1
- package/dist/core/large-subagent-launch.d.ts +13 -0
- package/dist/core/large-subagent-launch.d.ts.map +1 -0
- package/dist/core/large-subagent-launch.js +54 -0
- package/dist/core/large-subagent-launch.js.map +1 -0
- package/dist/core/live-catalog.d.ts +5 -2
- package/dist/core/live-catalog.d.ts.map +1 -1
- package/dist/core/live-catalog.js +108 -27
- package/dist/core/live-catalog.js.map +1 -1
- package/dist/core/model-instructions.d.ts +24 -0
- package/dist/core/model-instructions.d.ts.map +1 -0
- package/dist/core/model-instructions.js +123 -0
- package/dist/core/model-instructions.js.map +1 -0
- package/dist/core/model-runtime.d.ts +8 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +64 -12
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/official-catalog.d.ts +4 -0
- package/dist/core/official-catalog.d.ts.map +1 -1
- package/dist/core/official-catalog.js +86 -21
- package/dist/core/official-catalog.js.map +1 -1
- package/dist/core/permissions.d.ts +10 -11
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +107 -40
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +10 -1
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/resource-loader.d.ts +3 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +13 -4
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-bridges.d.ts +4 -0
- package/dist/core/runtime-bridges.d.ts.map +1 -0
- package/dist/core/runtime-bridges.js +16 -0
- package/dist/core/runtime-bridges.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -6
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +32 -17
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-tools-bridge.d.ts +35 -0
- package/dist/core/settings-tools-bridge.d.ts.map +1 -0
- package/dist/core/settings-tools-bridge.js +56 -0
- package/dist/core/settings-tools-bridge.js.map +1 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +3 -2
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/approval.d.ts.map +1 -1
- package/dist/gateway/approval.js +4 -3
- package/dist/gateway/approval.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -1
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +1 -18
- package/dist/gateway/commands.js.map +1 -1
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +3 -2
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/router.d.ts +1 -1
- package/dist/gateway/router.d.ts.map +1 -1
- package/dist/gateway/router.js +1 -1
- package/dist/gateway/router.js.map +1 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +108 -58
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +2 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +18 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -3
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +4 -32
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +2 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.d.ts +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.js +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/plan-message.d.ts +1 -3
- package/dist/modes/interactive/components/plan-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/plan-message.js +1 -16
- package/dist/modes/interactive/components/plan-message.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +8 -5
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +71 -31
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -3
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -19
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +163 -177
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/moon.json +0 -1
- package/dist/modes/interactive/theme/theme-schema.json +3 -3
- package/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +95 -90
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/startup/interactive-view.d.ts +55 -0
- package/dist/startup/interactive-view.d.ts.map +1 -0
- package/dist/startup/interactive-view.js +332 -0
- package/dist/startup/interactive-view.js.map +1 -0
- package/dist/startup/launch-routing.d.ts +8 -0
- package/dist/startup/launch-routing.d.ts.map +1 -0
- package/dist/startup/launch-routing.js +29 -0
- package/dist/startup/launch-routing.js.map +1 -0
- package/dist/startup/startup-milestones.d.ts +4 -0
- package/dist/startup/startup-milestones.d.ts.map +1 -0
- package/dist/startup/startup-milestones.js +14 -0
- package/dist/startup/startup-milestones.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +10 -4
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/docs/features.md +9 -9
- package/docs/interactive-startup.md +58 -0
- package/docs/keybindings.md +1 -1
- package/docs/model-catalog.md +29 -0
- package/docs/quickstart.md +3 -2
- package/docs/settings.md +4 -4
- package/docs/terminal-setup.md +2 -2
- package/docs/termux.md +1 -1
- package/docs/usage.md +4 -3
- package/docs/windows.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +16 -16
- package/dist/core/swarm.d.ts +0 -54
- package/dist/core/swarm.d.ts.map +0 -1
- package/dist/core/swarm.js +0 -119
- package/dist/core/swarm.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,mBAAmB,CAmDrE,CAAC","sourcesContent":["import { APP_NAME } from \"../config.ts\";\nimport type { SourceInfo } from \"./source-info.ts\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\", argumentHint: \"<provider/model>\" },\n\t{ name: \"refresh\", description: \"Refresh model lists for all providers\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"title\", description: \"Set session display name (alias of /name)\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"usage\", description: \"Show token totals, context, and plan usage\" },\n\t{ name: \"fast\", description: \"Toggle OpenAI Codex subscription Fast mode\", argumentHint: \"[on|off|status]\" },\n\t{ name: \"context\", description: \"Show estimated context window breakdown\" },\n\t{\n\t\tname: \"plan\",\n\t\tdescription: \"Switch to plan permission mode, or /plan <task> to plan a task\",\n\t\targumentHint: \"[on|off|status|<task>]\",\n\t},\n\t{\n\t\tname: \"mode\",\n\t\tdescription: \"Set permission mode: manual, yolo, plan, or auto\",\n\t\targumentHint: \"[manual|yolo|plan|auto]\",\n\t},\n\t{ name: \"manual\", description: \"Activate manual permission mode (approve every action)\" },\n\t{ name: \"yolo\", description: \"Activate YOLO permission mode (auto-approve tools)\" },\n\t{ name: \"auto\", description: \"Activate auto permission mode (fully autonomous, no questions)\" },\n\t{ name: \"processes\", description: \"View and manage background processes started this session\" },\n\t{ name: \"rollback\", description: \"Undo the last turn's file changes and rewind the conversation\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous user message\" },\n\t{ name: \"clone\", description: \"Duplicate the current session at the current position\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"undo\", description: \"Rewind the last turn (same session; /redo restores)\" },\n\t{ name: \"edit\", description: \"Rewind the last turn and put its text in the chat box\" },\n\t{ name: \"redo\", description: \"Restore a turn undone with /undo\" },\n\t{ name: \"trust\", description: \"Save project trust decision for future sessions\" },\n\t{ name: \"login\", description: \"Configure provider authentication\", argumentHint: \"<provider>\" },\n\t{ name: \"auth\", description: \"Configure provider authentication (alias of /login)\", argumentHint: \"<provider>\" },\n\t{ name: \"logout\", description: \"Remove provider authentication\" },\n\t{ name: \"deauth\", description: \"Remove provider authentication (alias of /logout)\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"init\", description: \"Generate a starter AGENTS.md for this project\" },\n\t{ name: \"
|
|
1
|
+
{"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,mBAAmB,CAmDrE,CAAC","sourcesContent":["import { APP_NAME } from \"../config.ts\";\nimport type { SourceInfo } from \"./source-info.ts\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\", argumentHint: \"<provider/model>\" },\n\t{ name: \"refresh\", description: \"Refresh model lists for all providers\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"paste-image\", description: \"Paste an image from the clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"title\", description: \"Set session display name (alias of /name)\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"usage\", description: \"Show token totals, context, and plan usage\" },\n\t{ name: \"fast\", description: \"Toggle OpenAI Codex subscription Fast mode\", argumentHint: \"[on|off|status]\" },\n\t{ name: \"context\", description: \"Show estimated context window breakdown\" },\n\t{\n\t\tname: \"plan\",\n\t\tdescription: \"Switch to plan permission mode, or /plan <task> to plan a task\",\n\t\targumentHint: \"[on|off|status|<task>]\",\n\t},\n\t{\n\t\tname: \"mode\",\n\t\tdescription: \"Set permission mode: manual, yolo, plan, or auto\",\n\t\targumentHint: \"[manual|yolo|plan|auto]\",\n\t},\n\t{ name: \"manual\", description: \"Activate manual permission mode (approve every action)\" },\n\t{ name: \"yolo\", description: \"Activate YOLO permission mode (auto-approve tools)\" },\n\t{ name: \"auto\", description: \"Activate auto permission mode (fully autonomous, no questions)\" },\n\t{ name: \"processes\", description: \"View and manage background processes started this session\" },\n\t{ name: \"rollback\", description: \"Undo the last turn's file changes and rewind the conversation\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous user message\" },\n\t{ name: \"clone\", description: \"Duplicate the current session at the current position\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"undo\", description: \"Rewind the last turn (same session; /redo restores)\" },\n\t{ name: \"edit\", description: \"Rewind the last turn and put its text in the chat box\" },\n\t{ name: \"redo\", description: \"Restore a turn undone with /undo\" },\n\t{ name: \"trust\", description: \"Save project trust decision for future sessions\" },\n\t{ name: \"login\", description: \"Configure provider authentication\", argumentHint: \"<provider>\" },\n\t{ name: \"auth\", description: \"Configure provider authentication (alias of /login)\", argumentHint: \"<provider>\" },\n\t{ name: \"logout\", description: \"Remove provider authentication\" },\n\t{ name: \"deauth\", description: \"Remove provider authentication (alias of /logout)\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"init\", description: \"Generate a starter AGENTS.md for this project\" },\n\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"sessions\", description: \"Browse and resume sessions\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, themes, and context files\" },\n\t{ name: \"quit\", description: `Quit ${APP_NAME}` },\n\t{ name: \"exit\", description: `Quit ${APP_NAME} (alias of /quit)` },\n];\n"]}
|
|
@@ -8,6 +8,7 @@ export const BUILTIN_SLASH_COMMANDS = [
|
|
|
8
8
|
{ name: "import", description: "Import and resume a session from a JSONL file" },
|
|
9
9
|
{ name: "share", description: "Share session as a secret GitHub gist" },
|
|
10
10
|
{ name: "copy", description: "Copy last agent message to clipboard" },
|
|
11
|
+
{ name: "paste-image", description: "Paste an image from the clipboard" },
|
|
11
12
|
{ name: "name", description: "Set session display name" },
|
|
12
13
|
{ name: "title", description: "Set session display name (alias of /name)" },
|
|
13
14
|
{ name: "session", description: "Show session info and stats" },
|
|
@@ -43,7 +44,6 @@ export const BUILTIN_SLASH_COMMANDS = [
|
|
|
43
44
|
{ name: "deauth", description: "Remove provider authentication (alias of /logout)" },
|
|
44
45
|
{ name: "new", description: "Start a new session" },
|
|
45
46
|
{ name: "init", description: "Generate a starter AGENTS.md for this project" },
|
|
46
|
-
{ name: "swarm", description: "Orchestrate parallel subagents for a complex task", argumentHint: "<task>" },
|
|
47
47
|
{ name: "compact", description: "Manually compact the session context" },
|
|
48
48
|
{ name: "resume", description: "Resume a different session" },
|
|
49
49
|
{ name: "sessions", description: "Browse and resume sessions" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"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","sourcesContent":["import { APP_NAME } from \"../config.ts\";\nimport type { SourceInfo } from \"./source-info.ts\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\", argumentHint: \"<provider/model>\" },\n\t{ name: \"refresh\", description: \"Refresh model lists for all providers\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"paste-image\", description: \"Paste an image from the clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"title\", description: \"Set session display name (alias of /name)\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"usage\", description: \"Show token totals, context, and plan usage\" },\n\t{ name: \"fast\", description: \"Toggle OpenAI Codex subscription Fast mode\", argumentHint: \"[on|off|status]\" },\n\t{ name: \"context\", description: \"Show estimated context window breakdown\" },\n\t{\n\t\tname: \"plan\",\n\t\tdescription: \"Switch to plan permission mode, or /plan <task> to plan a task\",\n\t\targumentHint: \"[on|off|status|<task>]\",\n\t},\n\t{\n\t\tname: \"mode\",\n\t\tdescription: \"Set permission mode: manual, yolo, plan, or auto\",\n\t\targumentHint: \"[manual|yolo|plan|auto]\",\n\t},\n\t{ name: \"manual\", description: \"Activate manual permission mode (approve every action)\" },\n\t{ name: \"yolo\", description: \"Activate YOLO permission mode (auto-approve tools)\" },\n\t{ name: \"auto\", description: \"Activate auto permission mode (fully autonomous, no questions)\" },\n\t{ name: \"processes\", description: \"View and manage background processes started this session\" },\n\t{ name: \"rollback\", description: \"Undo the last turn's file changes and rewind the conversation\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous user message\" },\n\t{ name: \"clone\", description: \"Duplicate the current session at the current position\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"undo\", description: \"Rewind the last turn (same session; /redo restores)\" },\n\t{ name: \"edit\", description: \"Rewind the last turn and put its text in the chat box\" },\n\t{ name: \"redo\", description: \"Restore a turn undone with /undo\" },\n\t{ name: \"trust\", description: \"Save project trust decision for future sessions\" },\n\t{ name: \"login\", description: \"Configure provider authentication\", argumentHint: \"<provider>\" },\n\t{ name: \"auth\", description: \"Configure provider authentication (alias of /login)\", argumentHint: \"<provider>\" },\n\t{ name: \"logout\", description: \"Remove provider authentication\" },\n\t{ name: \"deauth\", description: \"Remove provider authentication (alias of /logout)\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"init\", description: \"Generate a starter AGENTS.md for this project\" },\n\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"sessions\", description: \"Browse and resume sessions\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, themes, and context files\" },\n\t{ name: \"quit\", description: `Quit ${APP_NAME}` },\n\t{ name: \"exit\", description: `Quit ${APP_NAME} (alias of /quit)` },\n];\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAgH3E","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Current model in provider/id form. */\n\tmodelSlug?: string;\n\t/** Active tools; used to decide whether read-dependent skill metadata is visible. */\n\tselectedTools?: string[];\n\t/** Legacy extension metadata retained for API compatibility; tool definitions are sent separately. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Legacy extension metadata retained for API compatibility; not appended to the default prompt. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. */\n\tcwd: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the base system prompt and append configured context. */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tmodelSlug,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\tconst currentModel = modelSlug?.trim() || \"no model selected\";\n\tconst hasRead = !selectedTools || selectedTools.includes(\"read\");\n\n\tlet prompt = `You are an expert coding assistant currently running ''${currentModel}'', operating inside lunR, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\nCurrent working directory: ''${promptCwd}''\n\nBehavior guidelines:\n- Default to writing no comments. Only add one if the why is non-obvious: a hidden constraint, a subtle invariant, a workaround for a specific bug, something that would surprise the reader. If removing the comment wouldn't confuse a future reader, don't write it.\n- Keep comments up to date! When making changes, it's important to keep things in sync. An outdated comment is worse than no comment at all.\n- Carefully consider the reversibility and blast radius of actions. Generally you can freely take local, reversible actions like editing files or running tests. But for actions that are hard to reverse, affect shared systems beyond your local environment, or could otherwise be risky or destructive, check with the user before proceeding.\n- Orchestrate subagents with intent! Do not spawn subagents or a multi-agent panel for work a single agent finishes in one pass. Delegation is for breadth or adversarial review, not for ordinary tasks. Use subagents as a tool when there is genuine benefit to it or the user explicitly asks for it.\n- When several agents work in parallel, state file ownership up front so they do not collide.\n- Tests are good! Endless smoke tests, regressions tests for feature deletions, etc, much less good. Tests should be focused, not slop.\n- Prefer editing existing files to creating new ones.\n- Only use emojis if the user explicitly requests it.\n- Never exfil private data on public platforms like github or any other services under any circumstances.\n\n\nGuidelines:\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Use todo for meaningful multi-step work. Every update must send the complete list, with exactly one item in progress at a time.\n- Use subagents for independent parallel work, specialist analysis, or substantial research. Subagents start with fresh sessions. Use intercom instead to coordinate with an existing lunR session.\n- Use ast_search for structural code matches.\n- Use cron only when the user asks to schedule or manage unattended prompts.\n- Memory stores established, durable facts and stable preferences. Do not store behavior instructions, transient task details, transcripts, guesses, or secrets. Change memory only with the memory tools.\n- ~/.lunr/agent/
|
|
1
|
+
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAgH3E","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Current model in provider/id form. */\n\tmodelSlug?: string;\n\t/** Active tools; used to decide whether read-dependent skill metadata is visible. */\n\tselectedTools?: string[];\n\t/** Legacy extension metadata retained for API compatibility; tool definitions are sent separately. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Legacy extension metadata retained for API compatibility; not appended to the default prompt. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. */\n\tcwd: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the base system prompt and append configured context. */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tmodelSlug,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\tconst currentModel = modelSlug?.trim() || \"no model selected\";\n\tconst hasRead = !selectedTools || selectedTools.includes(\"read\");\n\n\tlet prompt = `You are an expert coding assistant currently running ''${currentModel}'', operating inside lunR, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\nCurrent working directory: ''${promptCwd}''\n\nBehavior guidelines:\n- Default to writing no comments. Only add one if the why is non-obvious: a hidden constraint, a subtle invariant, a workaround for a specific bug, something that would surprise the reader. If removing the comment wouldn't confuse a future reader, don't write it.\n- Keep comments up to date! When making changes, it's important to keep things in sync. An outdated comment is worse than no comment at all.\n- Carefully consider the reversibility and blast radius of actions. Generally you can freely take local, reversible actions like editing files or running tests. But for actions that are hard to reverse, affect shared systems beyond your local environment, or could otherwise be risky or destructive, check with the user before proceeding.\n- Orchestrate subagents with intent! Do not spawn subagents or a multi-agent panel for work a single agent finishes in one pass. Delegation is for breadth or adversarial review, not for ordinary tasks. Use subagents as a tool when there is genuine benefit to it or the user explicitly asks for it.\n- When several agents work in parallel, state file ownership up front so they do not collide.\n- Tests are good! Endless smoke tests, regressions tests for feature deletions, etc, much less good. Tests should be focused, not slop.\n- Prefer editing existing files to creating new ones.\n- Only use emojis if the user explicitly requests it.\n- Never exfil private data on public platforms like github or any other services under any circumstances.\n\n\nGuidelines:\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Use todo for meaningful multi-step work. Every update must send the complete list, with exactly one item in progress at a time.\n- Use subagents for independent parallel work, specialist analysis, or substantial research. Subagents start with fresh sessions. Use intercom instead to coordinate with an existing lunR session.\n- Use ast_search for structural code matches.\n- Use cron only when the user asks to schedule or manage unattended prompts.\n- Memory stores established, durable facts and stable preferences. Do not store behavior instructions, transient task details, transcripts, guesses, or secrets. Change memory only with the memory tools.\n- ~/.lunr/agent/agents/ contains optional global and per-model AGENTS.md instructions written by the user. Never modify this tree, including through shell commands.\n- Use web search when information is current, uncertain, externally referenced, or research-heavy, and cite the sources used.\n- Use MCP only for capabilities exposed by configured MCP servers. Call native lunR tools directly.\n\n\nlunR documentation (read only when the user asks about lunR itself, or the task concerns lunR itself, its SDK, extensions, themes, skills, or TUI):\n- README: ${readmePath}\n- Documentation: ${docsPath}\n- Examples: ${examplesPath}\n- Always read lunR .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t}\n\t\tprompt += \"</project_context>\\n\";\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\treturn prompt;\n}\n"]}
|
|
@@ -66,7 +66,7 @@ Guidelines:
|
|
|
66
66
|
- Use ast_search for structural code matches.
|
|
67
67
|
- Use cron only when the user asks to schedule or manage unattended prompts.
|
|
68
68
|
- Memory stores established, durable facts and stable preferences. Do not store behavior instructions, transient task details, transcripts, guesses, or secrets. Change memory only with the memory tools.
|
|
69
|
-
- ~/.lunr/agent/
|
|
69
|
+
- ~/.lunr/agent/agents/ contains optional global and per-model AGENTS.md instructions written by the user. Never modify this tree, including through shell commands.
|
|
70
70
|
- Use web search when information is current, uncertain, externally referenced, or research-heavy, and cite the sources used.
|
|
71
71
|
- Use MCP only for capabilities exposed by configured MCP servers. Call native lunR tools directly.
|
|
72
72
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Current model in provider/id form. */\n\tmodelSlug?: string;\n\t/** Active tools; used to decide whether read-dependent skill metadata is visible. */\n\tselectedTools?: string[];\n\t/** Legacy extension metadata retained for API compatibility; tool definitions are sent separately. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Legacy extension metadata retained for API compatibility; not appended to the default prompt. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. */\n\tcwd: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the base system prompt and append configured context. */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tmodelSlug,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\tconst currentModel = modelSlug?.trim() || \"no model selected\";\n\tconst hasRead = !selectedTools || selectedTools.includes(\"read\");\n\n\tlet prompt = `You are an expert coding assistant currently running ''${currentModel}'', operating inside lunR, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\nCurrent working directory: ''${promptCwd}''\n\nBehavior guidelines:\n- Default to writing no comments. Only add one if the why is non-obvious: a hidden constraint, a subtle invariant, a workaround for a specific bug, something that would surprise the reader. If removing the comment wouldn't confuse a future reader, don't write it.\n- Keep comments up to date! When making changes, it's important to keep things in sync. An outdated comment is worse than no comment at all.\n- Carefully consider the reversibility and blast radius of actions. Generally you can freely take local, reversible actions like editing files or running tests. But for actions that are hard to reverse, affect shared systems beyond your local environment, or could otherwise be risky or destructive, check with the user before proceeding.\n- Orchestrate subagents with intent! Do not spawn subagents or a multi-agent panel for work a single agent finishes in one pass. Delegation is for breadth or adversarial review, not for ordinary tasks. Use subagents as a tool when there is genuine benefit to it or the user explicitly asks for it.\n- When several agents work in parallel, state file ownership up front so they do not collide.\n- Tests are good! Endless smoke tests, regressions tests for feature deletions, etc, much less good. Tests should be focused, not slop.\n- Prefer editing existing files to creating new ones.\n- Only use emojis if the user explicitly requests it.\n- Never exfil private data on public platforms like github or any other services under any circumstances.\n\n\nGuidelines:\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Use todo for meaningful multi-step work. Every update must send the complete list, with exactly one item in progress at a time.\n- Use subagents for independent parallel work, specialist analysis, or substantial research. Subagents start with fresh sessions. Use intercom instead to coordinate with an existing lunR session.\n- Use ast_search for structural code matches.\n- Use cron only when the user asks to schedule or manage unattended prompts.\n- Memory stores established, durable facts and stable preferences. Do not store behavior instructions, transient task details, transcripts, guesses, or secrets. Change memory only with the memory tools.\n- ~/.lunr/agent/
|
|
1
|
+
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Current model in provider/id form. */\n\tmodelSlug?: string;\n\t/** Active tools; used to decide whether read-dependent skill metadata is visible. */\n\tselectedTools?: string[];\n\t/** Legacy extension metadata retained for API compatibility; tool definitions are sent separately. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Legacy extension metadata retained for API compatibility; not appended to the default prompt. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. */\n\tcwd: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the base system prompt and append configured context. */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tmodelSlug,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\tconst currentModel = modelSlug?.trim() || \"no model selected\";\n\tconst hasRead = !selectedTools || selectedTools.includes(\"read\");\n\n\tlet prompt = `You are an expert coding assistant currently running ''${currentModel}'', operating inside lunR, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\nCurrent working directory: ''${promptCwd}''\n\nBehavior guidelines:\n- Default to writing no comments. Only add one if the why is non-obvious: a hidden constraint, a subtle invariant, a workaround for a specific bug, something that would surprise the reader. If removing the comment wouldn't confuse a future reader, don't write it.\n- Keep comments up to date! When making changes, it's important to keep things in sync. An outdated comment is worse than no comment at all.\n- Carefully consider the reversibility and blast radius of actions. Generally you can freely take local, reversible actions like editing files or running tests. But for actions that are hard to reverse, affect shared systems beyond your local environment, or could otherwise be risky or destructive, check with the user before proceeding.\n- Orchestrate subagents with intent! Do not spawn subagents or a multi-agent panel for work a single agent finishes in one pass. Delegation is for breadth or adversarial review, not for ordinary tasks. Use subagents as a tool when there is genuine benefit to it or the user explicitly asks for it.\n- When several agents work in parallel, state file ownership up front so they do not collide.\n- Tests are good! Endless smoke tests, regressions tests for feature deletions, etc, much less good. Tests should be focused, not slop.\n- Prefer editing existing files to creating new ones.\n- Only use emojis if the user explicitly requests it.\n- Never exfil private data on public platforms like github or any other services under any circumstances.\n\n\nGuidelines:\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Use todo for meaningful multi-step work. Every update must send the complete list, with exactly one item in progress at a time.\n- Use subagents for independent parallel work, specialist analysis, or substantial research. Subagents start with fresh sessions. Use intercom instead to coordinate with an existing lunR session.\n- Use ast_search for structural code matches.\n- Use cron only when the user asks to schedule or manage unattended prompts.\n- Memory stores established, durable facts and stable preferences. Do not store behavior instructions, transient task details, transcripts, guesses, or secrets. Change memory only with the memory tools.\n- ~/.lunr/agent/agents/ contains optional global and per-model AGENTS.md instructions written by the user. Never modify this tree, including through shell commands.\n- Use web search when information is current, uncertain, externally referenced, or research-heavy, and cite the sources used.\n- Use MCP only for capabilities exposed by configured MCP servers. Call native lunR tools directly.\n\n\nlunR documentation (read only when the user asks about lunR itself, or the task concerns lunR itself, its SDK, extensions, themes, skills, or TUI):\n- README: ${readmePath}\n- Documentation: ${docsPath}\n- Examples: ${examplesPath}\n- Always read lunR .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t}\n\t\tprompt += \"</project_context>\\n\";\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\treturn prompt;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; editorImages?: ImageContent[]; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ loadAllBuiltinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ getModelTiersBridge, registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tgetModelTiersBridge()?.setParentThinkingProvider(() => session.thinkingLevel);\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\tasync reset(key: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\t// lunr: /new while busy must stop the live turn and drop queued follow-ups\n\t\t\t// before dispose, otherwise drainQueue can still fire on the torn-down entry.\n\t\t\tentry.queue.length = 0;\n\t\t\tentry.dropped = 0;\n\t\t\ttry {\n\t\t\t\tawait entry.session.abort();\n\t\t\t} catch {\n\t\t\t\t// best-effort; dispose still tears the session down\n\t\t\t}\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; editorImages?: ImageContent[]; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ loadAllBuiltinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t\t{ bindRuntimeBridges },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t\timport(\"../core/runtime-bridges.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tbindRuntimeBridges({ session, services });\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\tasync reset(key: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\t// lunr: /new while busy must stop the live turn and drop queued follow-ups\n\t\t\t// before dispose, otherwise drainQueue can still fire on the torn-down entry.\n\t\t\tentry.queue.length = 0;\n\t\t\tentry.dropped = 0;\n\t\t\ttry {\n\t\t\t\tawait entry.session.abort();\n\t\t\t} catch {\n\t\t\t\t// best-effort; dispose still tears the session down\n\t\t\t}\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
|
|
@@ -59,7 +59,7 @@ export function gatewayRunDepth() {
|
|
|
59
59
|
* import light and matches how main.ts loads it in the real CLI.
|
|
60
60
|
*/
|
|
61
61
|
async function defaultSessionFactory(key, reopen) {
|
|
62
|
-
const [{ loadAllBuiltinExtensions }, { getAgentDir }, { registerCustomizeBridge }, { registerMemoryCapBridge }, {
|
|
62
|
+
const [{ loadAllBuiltinExtensions }, { getAgentDir }, { registerCustomizeBridge }, { registerMemoryCapBridge }, { registerModelTierBridge }, { createAgentSessionFromServices, createAgentSessionServices }, { SessionManager }, { SettingsManager }, { bindRuntimeBridges },] = await Promise.all([
|
|
63
63
|
import("../builtin-extensions/index.js"),
|
|
64
64
|
import("../config.js"),
|
|
65
65
|
import("../core/customize.js"),
|
|
@@ -68,6 +68,7 @@ async function defaultSessionFactory(key, reopen) {
|
|
|
68
68
|
import("../core/agent-session-services.js"),
|
|
69
69
|
import("../core/session-manager.js"),
|
|
70
70
|
import("../core/settings-manager.js"),
|
|
71
|
+
import("../core/runtime-bridges.js"),
|
|
71
72
|
]);
|
|
72
73
|
const cwd = process.cwd();
|
|
73
74
|
const agentDir = getAgentDir();
|
|
@@ -98,7 +99,7 @@ async function defaultSessionFactory(key, reopen) {
|
|
|
98
99
|
resourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },
|
|
99
100
|
});
|
|
100
101
|
const { session } = await createAgentSessionFromServices({ services, sessionManager });
|
|
101
|
-
|
|
102
|
+
bindRuntimeBridges({ session, services });
|
|
102
103
|
await session.bindExtensions({
|
|
103
104
|
mode: "print",
|
|
104
105
|
onError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMrC,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAE1F,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGjF,SAAS,kBAAkB,CAAC,KAAuB,EAAgC;IAClF,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AAAA,CAChC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,MAAM,GAAG,yBAAyB,CAAC;AAEhD,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB,yDAAyD;AACzD,MAAM,UAAU,aAAa,GAAY;IACxC,OAAO,QAAQ,GAAG,CAAC,CAAC;AAAA,CACpB;AAED,MAAM,UAAU,eAAe,GAAW;IACzC,OAAO,QAAQ,CAAC;AAAA,CAChB;AA8DD;;;;;;;;;;GAUG;AACH,KAAK,UAAU,qBAAqB,CAAC,GAAW,EAAE,MAA2C,EAAyB;IACrH,MAAM,CACL,EAAE,wBAAwB,EAAE,EAC5B,EAAE,WAAW,EAAE,EACf,EAAE,uBAAuB,EAAE,EAC3B,EAAE,uBAAuB,EAAE,EAC3B,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,EAChD,EAAE,8BAA8B,EAAE,0BAA0B,EAAE,EAC9D,EAAE,cAAc,EAAE,EAClB,EAAE,eAAe,EAAE,EACnB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACrB,MAAM,CAAC,gCAAgC,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC;QACtB,MAAM,CAAC,sBAAsB,CAAC;QAC9B,MAAM,CAAC,uBAAuB,CAAC;QAC/B,MAAM,CAAC,wBAAwB,CAAC;QAChC,MAAM,CAAC,mCAAmC,CAAC;QAC3C,MAAM,CAAC,4BAA4B,CAAC;QACpC,MAAM,CAAC,6BAA6B,CAAC;KACrC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;IACzF,yDAAyD;IACzD,uBAAuB,CAAC,eAAe,CAAC,CAAC;IACzC,uBAAuB,CAAC,eAAe,CAAC,CAAC;IACzC,uBAAuB,CAAC,eAAe,CAAC,CAAC;IAEzC,IAAI,cAA8B,CAAC;IACnC,IAAI,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9C,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACP,IAAI,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,6CAA6C,GAAG,4BAA4B,CAAC,CAAC;QAC5F,CAAC;QACD,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,yEAAyE;IACzE,oEAAoE;IACpE,2EAAyE;IACzE,uEAAuE;IACvE,mEAAiE;IACjE,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC;QACjD,GAAG;QACH,QAAQ;QACR,eAAe;QACf,qBAAqB,EAAE,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,EAAE,EAAE;KAC/E,CAAC,CAAC;IACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,8BAA8B,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;IACvF,mBAAmB,EAAE,EAAE,yBAAyB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9E,MAAM,OAAO,CAAC,cAAc,CAAC;QAC5B,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,8BAA8B,GAAG,CAAC,aAAa,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;KACjG,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AAAA,CACf;AAED,4EAA4E;AAC5E,SAAS,gBAAgB,CAAC,OAAsB,EAAU;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAwB,CAAC;IAC3C,IAAI,SAAS,CAAC,UAAU,KAAK,OAAO,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,YAAY,IAAI,WAAW,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;YAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,OAAO,WAAW;IACf,GAAG,CAAS;IACH,cAAc,CAAiB;IAC/B,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtC,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IACxC,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IAEnE,YAAY,OAAO,GAA2D,EAAE,EAAE;QACjF,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC;QACzC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,qBAAqB,CAAC;IAAA,CACtE;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,KAAmB,EAAE,SAAS,GAAkB,EAAE,EAAmB;QAC/F,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChB,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;gBACrC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACpB,KAAK,CAAC,OAAO,EAAE,CAAC;YACjB,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,OAAO,MAAM,CAAC;QACf,CAAC;QACD,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YACnB,MAAM,GAAG,CAAC;QACX,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,CAAC;QAClB,yEAAyE;QACzE,sEAAsE;QACtE,+DAA+D;QAC/D,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACpD,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;QAAA,CACnB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAAA,CACd;IAED,KAAK,CAAC,KAAK,CAAC,GAAW,EAAiB;QACvC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;IAAA,CAC3C;IAED,uEAAuE;IACvE,KAAK,CAAC,UAAU,CAAC,GAAW,EAAiC;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC;QAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;IAAA,CAC7C;IAED,6DAA6D;IAC7D,KAAK,CAAC,aAAa,CAAC,GAAW,EAAE,WAAmB,EAAiB;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,yCAAuC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACX,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;YAClE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1B,IAAI,YAAY;gBAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;QACzD,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAE3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;QACzD,MAAM,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;QAC5D,IAAI,OAAO,EAAE,CAAC;YACb,UAAU,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,YAAY,IAAI,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YAClB,uBAAuB,CAAC,YAAY,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,QAAQ,GAAe,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC7E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3B,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAAA,CAC/D;IAED,8EAA8E;IAC9E,KAAK,CAAC,IAAI,CAAC,GAAW,EAAiC;QACtD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACzD,IAAI,aAAa,GAAG,CAAC,CAAC,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,kBAAkB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAChE,aAAa,GAAG,CAAC,CAAC;gBAClB,MAAM;YACP,CAAC;QACF,CAAC;QACD,IAAI,aAAa,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;QACvC,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAEtE,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC;QAC3D,IAAI,QAAgB,CAAC;QACrB,IAAI,QAAQ,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC3D,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QAC9B,CAAC;aAAM,CAAC;YACP,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC;QACxB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,MAAM,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE9D,IAAI,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IAAA,CACvD;IAED,sDAAsD;IACtD,KAAK,CAAC,IAAI,CAAC,GAAW,EAAiB;QACtC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACpF,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAClD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACzC,CAAC;IAAA,CACD;IAED,kEAAkE;IAClE,kBAAkB,CAAC,GAAW,EAAU;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAAA,CAC5C;IAED,wFAAwF;IACxF,KAAK,CAAC,KAAK,CAAC,GAAW,EAAiB;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,EAAE,CAAC;YACX,2EAA2E;YAC3E,8EAA8E;YAC9E,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;YAClB,IAAI,CAAC;gBACJ,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACR,oDAAoD;YACrD,CAAC;YACD,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAAA,CAC3B;IAED,SAAS,CAAC,GAAW,EAAuB;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO;YACN,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK;YAC1B,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;YACpC,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,SAAS;SACrC,CAAC;IAAA,CACF;IAED,iFAA+E;IACvE,KAAK,CAAC,SAAS,CACtB,KAAiB,EACjB,IAAY,EACZ,SAAwB,EACxB,MAAqB,EACrB,WAAiC,EACf;QAClB,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QAC1B,IAAI,WAAqC,CAAC;QAC1C,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;YAClC,IAAI,WAAW,GAAG,CAAC,CAAC;YACpB,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAwB,EAAE,EAAE,CAAC;gBAC7D,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;oBAAE,OAAO;gBAC5C,MAAM,cAAc,GAAG,KAAK,CAAC,qBAAqB,CAAC;gBACnD,IAAI,cAAc,CAAC,IAAI,KAAK,YAAY;oBAAE,OAAO;gBACjD,IAAI,IAAI,GAAG,EAAE,CAAC;gBACd,KAAK,MAAM,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;oBACtD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;wBAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;gBACnD,CAAC;gBACD,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW;oBAAE,WAAW,GAAG,CAAC,CAAC,CAAC,6CAA6C;gBAC7F,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;oBAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;oBACjC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC3B,CAAC;YAAA,CACD,CAAC,CAAC;QACJ,CAAC;QACD,QAAQ,EAAE,CAAC;QACX,IAAI,CAAC;YACJ,yEAAyE;YACzE,yEAAyE;YACzE,sEAAsE;YACtE,MAAM,MAAM,GAAmB,EAAE,CAAC;YAClC,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,KAAK,MAAM,UAAU,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;gBAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACrD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACjF,IAAI,SAAS,CAAC,EAAE,EAAE,CAAC;oBAClB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;oBACnF,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK;wBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtD,CAAC;qBAAM,CAAC;oBACP,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAC/B,CAAC;YACF,CAAC;YACD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7E,uEAAuE;YACvE,mEAAmE;YACnE,0CAA0C;YAC1C,MAAM,aAAa,CAClB;gBACC,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aACzB,EACD,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAC/F,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,QAAQ,EAAE,CAAC;YACX,WAAW,EAAE,EAAE,CAAC;QACjB,CAAC;QACD,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAAA,CACjC;IAED,8EAA8E;IACtE,KAAK,CAAC,UAAU,CAAC,KAAiB,EAAE,SAAwB,EAAiB;QACpF,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;YACpE,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,OAAO,CAAC,aAAa,KAAK,CAAC,OAAO,0DAAwD,CAAC,CAAC;gBAClG,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,IAAI,CAAC;gBACJ,qEAAqE;gBACrE,4CAA4C;gBAC5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAChC,KAAK,EACL,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAClB,EAAE,EACF,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,EAC9B,WAAW,CACX,CAAC;gBACF,SAAS,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBACtE,OAAO;YACR,CAAC;QACF,CAAC;IAAA,CACD;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW,EAAuB;QAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC;YACZ,cAAc;YACd,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,MAAM,CAAC;QACf,CAAC;QACD,iDAAiD;QACjD,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,OAAO,CAAC;YACf,CAAC;oBAAS,CAAC;gBACV,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC;QACF,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW,EAAuB;QAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACzG,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;QAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;QACzD,IAAI,WAAW,EAAE,CAAC;YACjB,UAAU,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,SAAS,IAAI,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACf,uBAAuB,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,KAAK,GAAe,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;QAClG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3B,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,KAAK,CAAC;IAAA,CACb;IAEO,gBAAgB,CAAC,YAAqB,EAAQ;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACnC,IAAI,SAA6B,CAAC;YAClC,IAAI,MAA8B,CAAC;YACnC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACvC,IAAI,GAAG,KAAK,YAAY;oBAAE,SAAS;gBACnC,IAAI,KAAK,CAAC,IAAI;oBAAE,SAAS;gBACzB,SAAS,GAAG,GAAG,CAAC;gBAChB,MAAM,GAAG,KAAK,CAAC;gBACf,MAAM;YACP,CAAC;YACD,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,gEAAgE;gBAChE,kEAAkE;gBAClE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC3B,MAAM;YACP,CAAC;YACD,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC3B,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;YACvE,IAAI,gBAAgB;gBAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;QACjE,CAAC;IAAA,CACD;IAEO,mBAAmB,CAAC,GAAW,EAAE,KAAiB,EAAc;QACvE,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAAA,CAChF;IAEO,mBAAmB,CAAC,GAAW,EAAE,KAAwB,EAAQ;QACxE,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;YACtE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IAAA,CACD;CACD;AAED,SAAS,eAAe,CAAC,OAAqB,EAAU;IACvD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,SAAS,IAAI,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,IACC,KAAK;gBACL,OAAO,KAAK,KAAK,QAAQ;gBACzB,MAAM,IAAI,KAAK;gBACf,KAAK,CAAC,IAAI,KAAK,MAAM;gBACrB,MAAM,IAAI,KAAK;gBACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC7B,CAAC;gBACF,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;YACpB,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; editorImages?: ImageContent[]; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ loadAllBuiltinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ getModelTiersBridge, registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tgetModelTiersBridge()?.setParentThinkingProvider(() => session.thinkingLevel);\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\tasync reset(key: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\t// lunr: /new while busy must stop the live turn and drop queued follow-ups\n\t\t\t// before dispose, otherwise drainQueue can still fire on the torn-down entry.\n\t\t\tentry.queue.length = 0;\n\t\t\tentry.dropped = 0;\n\t\t\ttry {\n\t\t\t\tawait entry.session.abort();\n\t\t\t} catch {\n\t\t\t\t// best-effort; dispose still tears the session down\n\t\t\t}\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; editorImages?: ImageContent[]; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ loadAllBuiltinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t\t{ bindRuntimeBridges },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t\timport(\"../core/runtime-bridges.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tbindRuntimeBridges({ session, services });\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\tasync reset(key: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\t// lunr: /new while busy must stop the live turn and drop queued follow-ups\n\t\t\t// before dispose, otherwise drainQueue can still fire on the torn-down entry.\n\t\t\tentry.queue.length = 0;\n\t\t\tentry.dropped = 0;\n\t\t\ttry {\n\t\t\t\tawait entry.session.abort();\n\t\t\t} catch {\n\t\t\t\t// best-effort; dispose still tears the session down\n\t\t\t}\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../src/gateway/approval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAGrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAc,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAO5F,UAAU,eAAe;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AA0CD,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhG;
|
|
1
|
+
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../src/gateway/approval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAGrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAc,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAO5F,UAAU,eAAe;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AA0CD,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhG;AA2DD,mDAAmD;AACnD,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAMlG;AAED,2EAA2E;AAC3E,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CA4B9G;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,IAAI,IAAI,CAU5C;AAED,yDAAyD;AACzD,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C","sourcesContent":["/**\n * lunR: gateway approval prompt for manual permission mode.\n *\n * The gateway runs headless sessions. When a mutating tool call arrives in\n * manual mode, this module posts an inline-button approve/reject prompt to\n * the originating chat and awaits the callback. If no chat context is present\n * the request is rejected (fail-closed).\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomBytes } from \"node:crypto\";\nimport {\n\ttype ApprovalRequest,\n\ttype ApprovalResponse,\n\tNO_HANDLER_REASON,\n\tregisterApprovalHandler,\n} from \"../core/permissions.ts\";\nimport type { ButtonSpec, CallbackEvent, PlatformAdapter, SessionSource } from \"./types.ts\";\n\nconst CALLBACK_PREFIX = \"ap:\";\nconst ID_LENGTH = 8;\nconst CODE_ALPHABET = \"ABCDEFGHJKMNPQRSTUVWXYZ23456789\";\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\n\ninterface ApprovalContext {\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tsource: SessionSource;\n\ttimeoutMs?: number;\n}\n\ninterface PendingApproval {\n\tresolve: (value: ApprovalResponse) => void;\n\treject: (reason?: unknown) => void;\n\ttimeout: ReturnType<typeof setTimeout>;\n\tmessageId?: string;\n\tadapter: PlatformAdapter;\n\tchatId: string;\n\tthreadId?: string;\n\tuserId: string;\n}\n\nconst approvalContext = new AsyncLocalStorage<ApprovalContext>();\nconst pending = new Map<string, PendingApproval>();\n\nfunction generateId(): string {\n\tlet id = \"\";\n\tconst bytes = randomBytes(ID_LENGTH);\n\tfor (let i = 0; i < ID_LENGTH; i++) {\n\t\tid += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];\n\t}\n\treturn id;\n}\n\nfunction makeCallbackData(id: string, action: ApprovalResponse | \"cancel\"): string {\n\treturn `${CALLBACK_PREFIX}${id}:${action}`;\n}\n\nfunction parseCallbackData(data: string): { id: string; action: ApprovalResponse | \"cancel\" } | null {\n\tif (!data.startsWith(CALLBACK_PREFIX)) return null;\n\tconst rest = data.slice(CALLBACK_PREFIX.length);\n\tconst colon = rest.indexOf(\":\");\n\tif (colon === -1) return null;\n\tconst id = rest.slice(0, colon);\n\tconst action = rest.slice(colon + 1);\n\tif (action === \"once\" || action === \"session\" || action === \"reject\" || action === \"cancel\") {\n\t\treturn { id, action };\n\t}\n\treturn null;\n}\n\n/** Run code with the gateway approval context available to tool-call gates. */\nexport function runWithApprovalContext<T>(ctx: ApprovalContext, fn: () => Promise<T>): Promise<T> {\n\treturn approvalContext.run(ctx, fn);\n}\n\nasync function createApprovalPrompt(\n\tadapter: PlatformAdapter,\n\tsource: SessionSource,\n\treq: ApprovalRequest,\n\ttimeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<ApprovalResponse> {\n\tconst id = generateId();\n\t// Large child launches get a dedicated title; inline buttons cannot capture feedback.\n\tconst title =\n\t\treq.kind === \"large-subagent-launch\"\n\t\t\t? `Approve large subagent launch?\\n${req.detail}`\n\t\t\t: `Approve ${req.toolName}?\\n${req.detail}`;\n\tconst rows: ButtonSpec[][] = [\n\t\t[\n\t\t\t{ label: \"✓ Approve once\", data: makeCallbackData(id, \"once\") },\n\t\t\t{ label: \"✓ Approve session\", data: makeCallbackData(id, \"session\") },\n\t\t],\n\t\t[{ label: \"✗ Reject\", data: makeCallbackData(id, \"reject\") }],\n\t];\n\n\treturn new Promise<ApprovalResponse>((resolve, reject) => {\n\t\tlet messageId: string | undefined;\n\t\tconst timeout = setTimeout(() => {\n\t\t\tpending.delete(id);\n\t\t\treject(new Error(\"Approval request timed out\"));\n\t\t\tif (messageId) {\n\t\t\t\tvoid adapter.editMessage(source.chatId, messageId, \"⏱ Approval expired.\", []).catch(() => {});\n\t\t\t}\n\t\t}, timeoutMs);\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst result = await adapter.sendButtons(source.chatId, title, rows, { threadId: source.threadId });\n\t\t\t\tif (!result.success || !result.messageId) {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\treject(new Error(result.error ?? \"Failed to send approval prompt\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tmessageId = result.messageId;\n\t\t\t\tpending.set(id, {\n\t\t\t\t\tresolve,\n\t\t\t\t\treject,\n\t\t\t\t\ttimeout,\n\t\t\t\t\tmessageId,\n\t\t\t\t\tadapter,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tuserId: source.userId,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(err);\n\t\t\t}\n\t\t})();\n\t});\n}\n\n/** Approval handler passed to core permissions. */\nexport async function createGatewayApprovalRequest(req: ApprovalRequest): Promise<ApprovalResponse> {\n\tconst ctx = approvalContext.getStore();\n\tif (!ctx) {\n\t\tthrow new Error(NO_HANDLER_REASON);\n\t}\n\treturn createApprovalPrompt(ctx.adapter, ctx.source, req, ctx.timeoutMs);\n}\n\n/** Register the gateway approval handler with the core permission gate. */\nexport function registerGatewayApprovalHandler(): void {\n\tregisterApprovalHandler(createGatewayApprovalRequest);\n}\n\n/**\n * Dispatch a callback event to an outstanding approval prompt.\n * Returns true when the data matched the approval prefix (whether or not a\n * pending prompt existed).\n */\nexport async function handleApprovalCallback(event: CallbackEvent, adapter?: PlatformAdapter): Promise<boolean> {\n\tconst parsed = parseCallbackData(event.data);\n\tif (!parsed) return false;\n\n\tconst entry = pending.get(parsed.id);\n\tif (!entry) {\n\t\t// Best-effort answer for stale callbacks.\n\t\tawait adapter?.answerCallback(event.id, \"Approval expired.\").catch(() => {});\n\t\treturn true;\n\t}\n\n\tif (event.userId !== entry.userId) {\n\t\tawait entry.adapter.answerCallback(event.id, \"⛔ Not your approval.\").catch(() => {});\n\t\treturn true;\n\t}\n\n\tclearTimeout(entry.timeout);\n\tpending.delete(parsed.id);\n\n\tconst response: ApprovalResponse =\n\t\tparsed.action === \"reject\" || parsed.action === \"cancel\" ? \"reject\" : parsed.action;\n\tentry.resolve(response);\n\n\tconst label =\n\t\tparsed.action === \"reject\" || parsed.action === \"cancel\" ? \"Rejected.\" : `Approved (${parsed.action}).`;\n\tawait entry.adapter.editMessage(entry.chatId, entry.messageId ?? \"\", label, []).catch(() => {});\n\tawait entry.adapter.answerCallback(event.id).catch(() => {});\n\treturn true;\n}\n\n/** Test hook: clear all pending approvals and stop their timeouts. */\nexport function resetApprovalRegistry(): void {\n\tfor (const entry of pending.values()) {\n\t\tclearTimeout(entry.timeout);\n\t\ttry {\n\t\t\tentry.reject(new Error(\"Approval registry reset\"));\n\t\t} catch {\n\t\t\t// ignore settled promise\n\t\t}\n\t}\n\tpending.clear();\n}\n\n/** Test hook: number of outstanding approval prompts. */\nexport function activeApprovalCount(): number {\n\treturn pending.size;\n}\n"]}
|