@ashx-j/lunr-dev 0.2.16-dev.9.1 → 0.2.21-dev.12.1
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 +67 -1
- package/README.md +7 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +44 -19
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +1 -2
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-computer-use.d.ts +3 -0
- package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -0
- package/dist/builtin-extensions/lunr-computer-use.js +158 -0
- package/dist/builtin-extensions/lunr-computer-use.js.map +1 -0
- package/dist/builtin-extensions/lunr-todos.d.ts +6 -5
- package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-todos.js +9 -7
- package/dist/builtin-extensions/lunr-todos.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts +6 -4
- package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/broker/spawn.js +114 -61
- package/dist/builtin-extensions/pi-intercom/broker/spawn.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +360 -351
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +10 -3
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts +5 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js +26 -4
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +37 -27
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts +6 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +33 -8
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js +2 -2
- package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +8 -6
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +179 -79
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +18 -8
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +44 -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 +4 -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 +24 -14
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +8 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +247 -18
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts +29 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js +179 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts +89 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js +541 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +11 -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 +38 -12
- 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 +3 -2
- 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 +2 -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 +35 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +7 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +26 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +55 -38
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +100 -0
- 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 +46 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts +3 -6
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js +64 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts +15 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js +14 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +7 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.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 +21 -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 +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.d.ts +10 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.js +38 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +0 -1
- 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 +74 -41
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js +75 -18
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +13 -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 +67 -11
- 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 +1 -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/runs/shared/pi-args.d.ts +3 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +7 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js +13 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts +2 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js +5 -10
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js +3 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +3 -0
- 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 +68 -11
- 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 +12 -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 +21 -2
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +21 -4
- 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.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +7 -0
- 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 +11 -7
- 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 +6 -0
- 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 +46 -29
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +47 -4
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +428 -431
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts +2 -13
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js +5 -91
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +103 -102
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/computer-use/adapter.d.ts +23 -0
- package/dist/core/computer-use/adapter.d.ts.map +1 -0
- package/dist/core/computer-use/adapter.js +116 -0
- package/dist/core/computer-use/adapter.js.map +1 -0
- package/dist/core/computer-use/image.d.ts +26 -0
- package/dist/core/computer-use/image.d.ts.map +1 -0
- package/dist/core/computer-use/image.js +64 -0
- package/dist/core/computer-use/image.js.map +1 -0
- package/dist/core/computer-use/lease.d.ts +18 -0
- package/dist/core/computer-use/lease.d.ts.map +1 -0
- package/dist/core/computer-use/lease.js +129 -0
- package/dist/core/computer-use/lease.js.map +1 -0
- package/dist/core/computer-use/macos-runtime.d.ts +14 -0
- package/dist/core/computer-use/macos-runtime.d.ts.map +1 -0
- package/dist/core/computer-use/macos-runtime.js +148 -0
- package/dist/core/computer-use/macos-runtime.js.map +1 -0
- package/dist/core/computer-use/policy.d.ts +19 -0
- package/dist/core/computer-use/policy.d.ts.map +1 -0
- package/dist/core/computer-use/policy.js +62 -0
- package/dist/core/computer-use/policy.js.map +1 -0
- package/dist/core/computer-use/release.generated.d.ts +31 -0
- package/dist/core/computer-use/release.generated.d.ts.map +1 -0
- package/dist/core/computer-use/release.generated.js +36 -0
- package/dist/core/computer-use/release.generated.js.map +1 -0
- package/dist/core/computer-use/runtime.d.ts +13 -0
- package/dist/core/computer-use/runtime.d.ts.map +1 -0
- package/dist/core/computer-use/runtime.js +158 -0
- package/dist/core/computer-use/runtime.js.map +1 -0
- package/dist/core/computer-use/schemas.d.ts +91 -0
- package/dist/core/computer-use/schemas.d.ts.map +1 -0
- package/dist/core/computer-use/schemas.js +71 -0
- package/dist/core/computer-use/schemas.js.map +1 -0
- package/dist/core/computer-use/windows-desktop.d.ts +3 -0
- package/dist/core/computer-use/windows-desktop.d.ts.map +1 -0
- package/dist/core/computer-use/windows-desktop.js +48 -0
- package/dist/core/computer-use/windows-desktop.js.map +1 -0
- package/dist/core/computer-use/workflow.d.ts +32 -0
- package/dist/core/computer-use/workflow.d.ts.map +1 -0
- package/dist/core/computer-use/workflow.js +341 -0
- package/dist/core/computer-use/workflow.js.map +1 -0
- package/dist/core/customize.d.ts +2 -0
- package/dist/core/customize.d.ts.map +1 -1
- package/dist/core/customize.js +3 -0
- package/dist/core/customize.js.map +1 -1
- package/dist/core/keybindings.d.ts +2 -2
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-runtime.d.ts +0 -3
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +1 -10
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions.d.ts +2 -1
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +31 -11
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +9 -5
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/rollback.d.ts.map +1 -1
- package/dist/core/rollback.js +13 -90
- package/dist/core/rollback.js.map +1 -1
- package/dist/core/runtime-bridges.d.ts.map +1 -1
- package/dist/core/runtime-bridges.js +0 -2
- package/dist/core/runtime-bridges.js.map +1 -1
- package/dist/core/session-manager.d.ts +0 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +21 -79
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +30 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-cancellation.d.ts +21 -0
- package/dist/core/subagent-cancellation.d.ts.map +1 -0
- package/dist/core/subagent-cancellation.js +53 -0
- package/dist/core/subagent-cancellation.js.map +1 -0
- package/dist/core/subagent-spinner-setting.d.ts +6 -0
- package/dist/core/subagent-spinner-setting.d.ts.map +1 -0
- package/dist/core/subagent-spinner-setting.js +12 -0
- package/dist/core/subagent-spinner-setting.js.map +1 -0
- package/dist/core/subagent-spinner.d.ts +4 -0
- package/dist/core/subagent-spinner.d.ts.map +1 -0
- package/dist/core/subagent-spinner.js +6 -0
- package/dist/core/subagent-spinner.js.map +1 -0
- package/dist/dev-cli.js +0 -0
- package/dist/gateway/agent-bridge.d.ts +6 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +23 -6
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/cron.d.ts +1 -1
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +23 -7
- package/dist/gateway/cron.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +12 -18
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +4 -2
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +23 -32
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/processes-selector.d.ts +2 -3
- package/dist/modes/interactive/components/processes-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/processes-selector.js +5 -10
- package/dist/modes/interactive/components/processes-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +8 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +93 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/extension-widget-order.d.ts +8 -0
- package/dist/modes/interactive/extension-widget-order.d.ts.map +1 -0
- package/dist/modes/interactive/extension-widget-order.js +37 -0
- package/dist/modes/interactive/extension-widget-order.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +58 -26
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/thinking-level-slash.d.ts +4 -0
- package/dist/modes/interactive/thinking-level-slash.d.ts.map +1 -0
- package/dist/modes/interactive/thinking-level-slash.js +23 -0
- package/dist/modes/interactive/thinking-level-slash.js.map +1 -0
- package/dist/node-runtime/adapter-AZMY335J.js +279 -0
- package/dist/node-runtime/{agent-session-services-IFJMSX7Z.js → agent-session-services-QANFGM2V.js} +15 -11
- package/dist/node-runtime/{builtin-extensions-LYZ3BFWQ.js → builtin-extensions-SRCYQPZ6.js} +5 -3
- package/dist/node-runtime/chunk-2IDTZAG5.js +1415 -0
- package/dist/node-runtime/chunk-3ONQTOMR.js +68 -0
- package/dist/node-runtime/{chunk-TQNSPERU.js → chunk-5SQDLSZJ.js} +1 -1
- package/dist/node-runtime/chunk-5ZH3ZFIZ.js +308 -0
- package/dist/node-runtime/{chunk-PBC2Y6GS.js → chunk-6UB6NKA3.js} +29 -72
- package/dist/node-runtime/{chunk-A2KQXUJD.js → chunk-7AFOFSDY.js} +1 -1
- package/dist/node-runtime/chunk-A3KMVBZV.js +60 -0
- package/dist/node-runtime/{chunk-N77MY65L.js → chunk-BJ6H7DPI.js} +25 -25
- package/dist/node-runtime/{chunk-PGAJ5MO5.js → chunk-BYQ2MOFI.js} +6 -6
- package/dist/node-runtime/{chunk-QH42CDX3.js → chunk-CZUBYPFP.js} +234 -124
- package/dist/node-runtime/{chunk-LZAW4ZX3.js → chunk-D466UAID.js} +34 -13
- package/dist/node-runtime/{chunk-ZKMFUD3Q.js → chunk-GT5F3OKM.js} +3 -3
- package/dist/node-runtime/chunk-H2BI7G7T.js +73 -0
- package/dist/node-runtime/{chunk-5P7PZMRU.js → chunk-HRS4HMDX.js} +1 -1
- package/dist/node-runtime/{chunk-IPA627GP.js → chunk-HXJS2PJM.js} +4 -4
- package/dist/node-runtime/chunk-JZHEUQRL.js +180 -0
- package/dist/node-runtime/chunk-LISUTZS3.js +19 -0
- package/dist/node-runtime/chunk-LOYUC2ID.js +2185 -0
- package/dist/node-runtime/{chunk-2QKLKMKC.js → chunk-LTXUQJI4.js} +1 -1
- package/dist/node-runtime/{chunk-YF63P5ZT.js → chunk-LTYWTR3V.js} +1 -426
- package/dist/node-runtime/{chunk-MKKH27PH.js → chunk-LWJXTA6Z.js} +274 -2416
- package/dist/node-runtime/chunk-MW2XJZIK.js +108 -0
- package/dist/node-runtime/{chunk-CDFZEWPU.js → chunk-N3PNL74S.js} +537 -262
- package/dist/node-runtime/{chunk-4TLO4PO2.js → chunk-U6C7VTR5.js} +7 -1401
- package/dist/node-runtime/chunk-URD2SLIT.js +1019 -0
- package/dist/node-runtime/{chunk-HQCKFM2O.js → chunk-VGYZ3APQ.js} +3 -0
- package/dist/node-runtime/{chunk-AJX357CL.js → chunk-VQ4UBWSA.js} +30 -1013
- package/dist/node-runtime/{chunk-F4VQFJI2.js → chunk-VTGEKB7R.js} +2 -2
- package/dist/node-runtime/{chunk-AD6VXAO7.js → chunk-VTPM5V35.js} +5 -100
- package/dist/node-runtime/chunk-W3SILANP.js +24 -0
- package/dist/node-runtime/{chunk-WOVTOV5P.js → chunk-XAUQOKYK.js} +421 -63
- package/dist/node-runtime/{chunk-ECD5L7YI.js → chunk-XSRCAQK2.js} +390 -16
- package/dist/node-runtime/{chunk-PCGNAY2O.js → chunk-ZOIIMEN6.js} +54 -433
- package/dist/node-runtime/{chunk-EDBJNRSM.js → chunk-ZVSORYRM.js} +1 -1
- package/dist/node-runtime/cli-runtime.js +1 -1
- package/dist/node-runtime/{command-L3LAHDIH.js → command-ZURZJDXF.js} +1 -1
- package/dist/node-runtime/{commands-RL5YBW5I.js → commands-BZGSLIP7.js} +4 -2
- package/dist/node-runtime/{customize-XEXL2DGQ.js → customize-LF5VST47.js} +1 -1
- package/dist/node-runtime/{direct-tool-executor-JVDXTO7A.js → direct-tool-executor-BSZDHIYR.js} +7 -4
- package/dist/node-runtime/{export-html-KR26FOUZ.js → export-html-E4DT6ZJQ.js} +1 -1
- package/dist/node-runtime/{gateway-X2TI5X7F.js → gateway-FWHRIBK5.js} +24 -369
- package/dist/node-runtime/index.js +49 -42
- package/dist/node-runtime/{init-NJV2T55N.js → init-DVJUAOUO.js} +4 -2
- package/dist/node-runtime/{interactive-mode-KOM35YF2.js → interactive-mode-BBASNWHN.js} +23 -17
- package/dist/node-runtime/{interactive-view-U6OILKF3.js → interactive-view-N6F4KUIT.js} +3 -5
- package/dist/node-runtime/lunr-computer-use-TJNDAMWM.js +135 -0
- package/dist/node-runtime/{lunr-cron-YJ5UHYRD.js → lunr-cron-J64I72C6.js} +7 -1
- package/dist/node-runtime/main.js +25 -20
- package/dist/node-runtime/{mcp-auth-flow-QIXHW6QN.js → mcp-auth-flow-YL6RWPYG.js} +2 -1
- package/dist/node-runtime/{package-manager-cli-TZZHJCVI.js → package-manager-cli-VMYX4Y2S.js} +10 -7
- package/dist/node-runtime/{pi-intercom-EEMBGQID.js → pi-intercom-NV2LW6X7.js} +108 -87
- package/dist/node-runtime/{pi-mcp-adapter-QFB37AVN.js → pi-mcp-adapter-JLB5YL2F.js} +31 -25
- package/dist/node-runtime/{pi-subagents-7XJ4H53P.js → pi-subagents-WEHV3W7H.js} +1321 -631
- package/dist/node-runtime/{pi-web-access-DPGAVKOA.js → pi-web-access-FN5YC5T6.js} +67 -64
- package/dist/node-runtime/{proxy-modes-HEEFK4KC.js → proxy-modes-OR7ISBXK.js} +7 -4
- package/dist/node-runtime/rpc-entry.js +25 -20
- package/dist/node-runtime/runtime-3SB5ATHQ.js +21 -0
- package/dist/node-runtime/{runtime-bridges-PPHNBFWL.js → runtime-bridges-2VQTLPOZ.js} +3 -5
- package/dist/node-runtime/{session-manager-MQNBYACL.js → session-manager-QEMZBMTM.js} +3 -5
- package/dist/node-runtime/{session-selector-5AZLJS6U.js → session-selector-URVJLOTK.js} +2 -2
- package/dist/node-runtime/{settings-manager-5LF2VQWC.js → settings-manager-3CCU326G.js} +4 -1
- package/dist/node-runtime/{src-Y7JSCK64.js → src-BPQFJR76.js} +27 -7
- package/dist/node-runtime/{subagent-executor-5RNDEPIJ.js → subagent-executor-CXKXQDHH.js} +189 -284
- package/dist/node-runtime/{update-cli-A253SZ7P.js → update-cli-NM7B4CKL.js} +4 -1
- package/dist/node-runtime/workflow-C26QCUTW.js +468 -0
- package/dist/rpc.d.ts +3 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +18 -0
- package/dist/rpc.js.map +1 -0
- package/dist/startup/interactive-view.d.ts.map +1 -1
- package/dist/startup/interactive-view.js +1 -2
- package/dist/startup/interactive-view.js.map +1 -1
- package/docs/computer-use.md +242 -0
- package/docs/development.md +0 -32
- package/docs/features.md +24 -6
- package/docs/interactive-startup.md +10 -3
- package/docs/keybindings.md +5 -3
- package/docs/settings.md +3 -2
- package/docs/terminal-setup.md +1 -1
- package/docs/usage.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/hidden-thinking-label.ts +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/install-lock/package-lock.json +3827 -0
- package/install-lock/package.json +18 -0
- package/native/computer-use/LICENSE.md +21 -0
- package/npm-shrinkwrap.json +3808 -0
- package/package.json +16 -10
- package/dist/builtin-extensions/lunr-settings-tools.d.ts +0 -4
- package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +0 -1
- package/dist/builtin-extensions/lunr-settings-tools.js +0 -120
- package/dist/builtin-extensions/lunr-settings-tools.js.map +0 -1
- package/dist/core/settings-tools-bridge.d.ts +0 -35
- package/dist/core/settings-tools-bridge.d.ts.map +0 -1
- package/dist/core/settings-tools-bridge.js +0 -56
- package/dist/core/settings-tools-bridge.js.map +0 -1
- package/dist/node-runtime/chunk-AHSTJBIB.js +0 -64
- package/dist/startup/interactive-shell.d.ts +0 -55
- package/dist/startup/interactive-shell.d.ts.map +0 -1
- package/dist/startup/interactive-shell.js +0 -264
- package/dist/startup/interactive-shell.js.map +0 -1
- package/dist/node-runtime/{chunk-YSKSB3BJ.js → chunk-CH6OL6P2.js} +2 -2
package/dist/gateway/cron.js
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
import { beginCronFire, endCronFire } from "../core/cron/fire-guard.js";
|
|
33
33
|
import { setCronDeliverValidator } from "../core/cron/jobs.js";
|
|
34
34
|
import { startScheduler } from "../core/cron/scheduler.js";
|
|
35
|
+
import { createPermissionContext, deletePermissionContext } from "../core/permissions.js";
|
|
36
|
+
import { shutdownBridgeSession } from "./agent-bridge.js";
|
|
35
37
|
import { platformConfigFor } from "./config.js";
|
|
36
38
|
import { splitMessage } from "./text.js";
|
|
37
39
|
const DELIVERY_BRIDGE_SYMBOL = Symbol.for("@lunr/cron-delivery");
|
|
@@ -81,12 +83,25 @@ async function defaultCronSessionFactory(job, modelOverride) {
|
|
|
81
83
|
model = resolved;
|
|
82
84
|
}
|
|
83
85
|
const { session } = await createAgentSessionFromServices({ services, sessionManager, model });
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
const permissionId = sessionManager.getSessionId();
|
|
87
|
+
createPermissionContext(permissionId, settingsManager.getDefaultPermissionMode(), false);
|
|
88
|
+
const dispose = session.dispose.bind(session);
|
|
89
|
+
session.dispose = () => {
|
|
90
|
+
deletePermissionContext(permissionId);
|
|
91
|
+
dispose();
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
bindRuntimeBridges({ session, services });
|
|
95
|
+
await session.bindExtensions({
|
|
96
|
+
mode: "print",
|
|
97
|
+
onError: (err) => console.error(`[gateway cron] extension error (${err.extensionPath}): ${err.error}`),
|
|
98
|
+
});
|
|
99
|
+
return session;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
await shutdownBridgeSession(session, "quit");
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
90
105
|
}
|
|
91
106
|
/** Final assistant text, print-mode style; throws on error/aborted stop. */
|
|
92
107
|
function extractFinalText(session) {
|
|
@@ -308,7 +323,8 @@ export function startGatewayCron(options) {
|
|
|
308
323
|
errors.push(`${label}: ${err instanceof Error ? err.message : String(err)}`);
|
|
309
324
|
}
|
|
310
325
|
finally {
|
|
311
|
-
session
|
|
326
|
+
if (session)
|
|
327
|
+
await shutdownBridgeSession(session, "quit");
|
|
312
328
|
}
|
|
313
329
|
}
|
|
314
330
|
throw new Error(errors.join(" | ") || "no model candidates");
|
package/dist/gateway/cron.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/gateway/cron.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway cron runner + delivery (Phase 4 of the cron/gateway roadmap).\n *\n * startGatewayCron() runs the core/cron scheduler inside the `lunr gateway`\n * daemon:\n *\n * - runJob: each fire gets a FRESH headless agent session (createAgentSession\n * with SessionManager.inMemory — cron runs never write session files and\n * never touch the per-chat gateway sessions; the files under\n * <agentDir>/cron/output/ are the audit trail). The shared fire-guard\n * (core/cron/fire-guard) brackets the turn so the `cron` tool refuses to\n * run inside it (jobs cannot schedule jobs).\n * - deliverResult: replaces the `@lunr/cron-delivery` bridge on globalThis\n * with a platform deliverer. Targets come from job.deliver\n * (comma-separated): \"local\" (no-op — the output file is already written),\n * \"origin\" (the chat that created the job, homeChannel fallback), a bare\n * platform name (that platform's homeChannel), or\n * \"<platform>:<chatId>[:<threadId>]\" (explicit). Content is wrapped as\n * \"☾ Cron: <name>\" and split to the adapter's maxMessageLength.\n *\n * The bridge is re-installed after every cron session creation because loading\n * the builtin extensions (lunr-cron) re-registers its local-notify bridge per\n * session; delivery re-reads the bridge on every call, matching lunr-cron.\n *\n * Fallback models (2026-08-02): settings.json `cronFallbackModels`\n * (\"provider/modelId\" entries) are tried IN ORDER when a fire fails — timeout\n * or any error. Each attempt gets a fresh headless session and\n * jobTimeoutMs/candidates of the budget; a successful fallback run prefixes\n * \"[fell back to provider/modelId]\" to the output. TUI fires are unaffected\n * (they use the live session's model).\n */\n\nimport type { Model } from \"@ashx-j/lunr-ai\";\nimport { beginCronFire, endCronFire } from \"../core/cron/fire-guard.ts\";\nimport type { CronJob, CronJobOrigin } from \"../core/cron/jobs.ts\";\nimport { setCronDeliverValidator } from \"../core/cron/jobs.ts\";\nimport { startScheduler } from \"../core/cron/scheduler.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { type GatewayConfig, platformConfigFor } from \"./config.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { PlatformAdapter } from \"./types.ts\";\n\nconst DELIVERY_BRIDGE_SYMBOL = Symbol.for(\"@lunr/cron-delivery\");\n\ntype DeliveryBridge = (job: CronJob, content: string) => Promise<string | null>;\n\n/** Test seam / default: one fresh headless session per cron fire. */\nexport interface CronModelRef {\n\tprovider: string;\n\tmodelId: string;\n}\n\nexport type CronSessionFactory = (job: CronJob, modelOverride?: CronModelRef) => Promise<BridgeSession>;\n\nexport interface GatewayCronOptions {\n\tadapters: Map<string, PlatformAdapter>;\n\tcfg: GatewayConfig;\n\t/** Tick interval; default 60s. */\n\tintervalMs?: number;\n\t/** Session factory for cron fires; defaults to a real headless agent session. */\n\tsessionFactory?: CronSessionFactory;\n\t/** Test seam: fallback models. When omitted, read fresh from settings.json `cronFallbackModels` on every fire. */\n\tfallbackModels?: CronModelRef[];\n}\n\nconst KNOWN_PLATFORMS = [\"telegram\", \"discord\"] as const;\n\n/** Default factory: fresh in-memory headless session, mirroring agent-bridge's wiring. */\nasync function defaultCronSessionFactory(job: CronJob, modelOverride?: CronModelRef): Promise<BridgeSession> {\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 = job.workdir ?? process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\t// In-memory on purpose: cron runs must not persist session files or pollute\n\t// the per-chat gateway sessions — <agentDir>/cron/output/ is the audit trail.\n\tconst sessionManager = SessionManager.inMemory(cwd);\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.\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\t// Fallback-model attempts pin the session model explicitly; an unresolvable\n\t// or unauthenticated override fails the attempt so the next fallback runs.\n\tlet model: Model<any> | undefined;\n\tif (modelOverride) {\n\t\tconst resolved = services.modelRuntime.getModel(modelOverride.provider, modelOverride.modelId);\n\t\tif (!resolved) throw new Error(`model not found: ${modelOverride.provider}/${modelOverride.modelId}`);\n\t\tif (!services.modelRuntime.hasConfiguredAuth(resolved.provider)) {\n\t\t\tthrow new Error(`no configured auth for provider: ${modelOverride.provider}`);\n\t\t}\n\t\tmodel = resolved;\n\t}\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager, model });\n\tbindRuntimeBridges({ session, services });\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway cron] 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 {\n\t\tstopReason?: string;\n\t\terrorMessage?: string;\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t};\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\ninterface ResolvedTarget {\n\tplatform: string;\n\tchatId: string;\n\tthreadId?: string;\n}\n\nconst EXPLICIT_TARGET_RE = /^([a-z0-9_-]+):([^:]+)(?::([^:]+))?$/i;\n\n/** Resolve one deliver target to a concrete destination. Returns an error string on failure. */\nfunction resolveTarget(target: string, job: CronJob, cfg: GatewayConfig): ResolvedTarget | string {\n\tif (target === \"origin\") {\n\t\tconst origin = job.origin;\n\t\tif (origin?.platform && origin.chatId) {\n\t\t\treturn { platform: origin.platform, chatId: origin.chatId, threadId: origin.threadId };\n\t\t}\n\t\t// Missing/incomplete origin → fall back to a homeChannel: the origin's\n\t\t// platform when known, otherwise the first platform that has one.\n\t\tconst platforms = origin?.platform ? [origin.platform] : KNOWN_PLATFORMS;\n\t\tfor (const platform of platforms) {\n\t\t\tconst home = platformConfigFor(cfg, platform)?.homeChannel;\n\t\t\tif (home) return { platform, chatId: home };\n\t\t}\n\t\treturn `deliver target \"origin\": job has no origin and no homeChannel is configured`;\n\t}\n\n\tconst explicit = EXPLICIT_TARGET_RE.exec(target);\n\tconst platform = explicit ? explicit[1].toLowerCase() : target.toLowerCase();\n\tif (platformConfigFor(cfg, platform) === undefined) {\n\t\treturn `unknown platform \"${explicit ? explicit[1] : target}\"`;\n\t}\n\tif (explicit) {\n\t\treturn { platform, chatId: explicit[2], threadId: explicit[3] };\n\t}\n\t// Bare platform name → its homeChannel.\n\tconst home = platformConfigFor(cfg, platform)?.homeChannel;\n\tif (!home) return `no homeChannel configured for ${platform}`;\n\treturn { platform, chatId: home };\n}\n\n/** True when a resolved chat may receive cron output for this job. */\nexport function isAllowedDeliverChat(\n\tcfg: GatewayConfig,\n\tplatform: string,\n\tchatId: string,\n\torigin?: CronJobOrigin | null,\n): boolean {\n\tconst platformCfg = platformConfigFor(cfg, platform);\n\tif (!platformCfg) return false;\n\tif (platformCfg.homeChannel && platformCfg.homeChannel === chatId) return true;\n\tif (platformCfg.allowedChats?.includes(chatId)) return true;\n\treturn origin?.platform === platform && origin.chatId === chatId;\n}\n\nexport function createDeliverValidator(\n\tcfg: GatewayConfig,\n): (deliver: string, origin?: CronJobOrigin | null) => string | undefined {\n\treturn (deliver, origin) => {\n\t\tconst targets = deliver\n\t\t\t.split(\",\")\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t\tfor (const target of targets) {\n\t\t\tif (target === \"local\") continue;\n\t\t\tif (target === \"origin\") continue; // delivery time resolves/fails with a sensible error\n\t\t\tconst explicit = EXPLICIT_TARGET_RE.exec(target);\n\t\t\tconst platform = explicit ? explicit[1].toLowerCase() : target.toLowerCase();\n\t\t\tconst platformCfg = platformConfigFor(cfg, platform);\n\t\t\tif (!platformCfg) return `unknown platform \"${explicit ? explicit[1] : target}\"`;\n\t\t\tif (explicit) {\n\t\t\t\tif (!isAllowedDeliverChat(cfg, platform, explicit[2], origin)) {\n\t\t\t\t\treturn `deliver target \"${target}\" is not an allowed chat for ${platform}`;\n\t\t\t\t}\n\t\t\t} else if (!platformCfg.homeChannel) {\n\t\t\t\treturn `no homeChannel configured for ${platform}`;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t};\n}\n\n/** Wrap the run output with a compact cron header. */\nexport function wrapCronContent(job: CronJob, content: string): string {\n\treturn `☾ Cron: ${job.name}\\n———\\n${content}`;\n}\n\n/**\n * The platform deliverer installed at the `@lunr/cron-delivery` bridge.\n * Every target is attempted; the first error is returned (null on success).\n */\nexport function createPlatformDeliverer(adapters: Map<string, PlatformAdapter>, cfg: GatewayConfig): DeliveryBridge {\n\treturn async (job, content) => {\n\t\tconst targets = job.deliver\n\t\t\t.split(\",\")\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t\tlet firstError: string | null = null;\n\t\tfor (const target of targets) {\n\t\t\tif (target === \"local\") continue; // output file already written by the scheduler\n\t\t\tconst resolved = resolveTarget(target, job, cfg);\n\t\t\tif (typeof resolved === \"string\") {\n\t\t\t\tfirstError ??= resolved;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!isAllowedDeliverChat(cfg, resolved.platform, resolved.chatId, job.origin)) {\n\t\t\t\tfirstError ??= `deliver target \"${target}\" is not an allowed chat for ${resolved.platform}`;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst adapter = adapters.get(resolved.platform);\n\t\t\tif (!adapter) {\n\t\t\t\tfirstError ??= `no adapter connected for ${resolved.platform}`;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst wrapped = wrapCronContent(job, content);\n\t\t\tfor (const chunk of splitMessage(wrapped, adapter.maxMessageLength)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await adapter.send(resolved.chatId, chunk, {\n\t\t\t\t\t\tthreadId: resolved.threadId,\n\t\t\t\t\t});\n\t\t\t\t\tif (!result.success) {\n\t\t\t\t\t\tfirstError ??= `${resolved.platform} send failed: ${result.error ?? \"unknown error\"}`;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tfirstError ??= `${resolved.platform} send failed: ${err instanceof Error ? err.message : String(err)}`;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn firstError;\n\t};\n}\n\n/** Race one prompt attempt against its share of the job budget. */\nfunction withAttemptTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\treturn Promise.race([\n\t\tpromise,\n\t\tnew Promise<never>((_, reject) => {\n\t\t\tconst timer = setTimeout(() => reject(new Error(`attempt timed out after ${ms}ms`)), ms);\n\t\t\t// If the attempt settles first this promise is discarded; the timer must not keep the process alive.\n\t\t\ttimer.unref?.();\n\t\t}),\n\t]);\n}\n\n/**\n * Read settings.json `cronFallbackModels` (\"provider/modelId\" entries).\n * Fresh read per fire so edits apply without a daemon restart; invalid\n * entries are skipped with a log line. Never throws.\n */\nasync function readCronFallbackModels(): Promise<CronModelRef[]> {\n\ttry {\n\t\tconst [{ getAgentDir }, { SettingsManager }] = await Promise.all([\n\t\t\timport(\"../config.ts\"),\n\t\t\timport(\"../core/settings-manager.ts\"),\n\t\t]);\n\t\tconst settingsManager = SettingsManager.create(process.cwd(), getAgentDir(), { projectTrusted: false });\n\t\tconst out: CronModelRef[] = [];\n\t\tfor (const entry of settingsManager.getCronFallbackModels()) {\n\t\t\tconst idx = entry.indexOf(\"/\");\n\t\t\tif (idx <= 0 || idx === entry.length - 1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[gateway cron] ignoring invalid cronFallbackModels entry \"${entry}\" (want \"provider/modelId\")`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout.push({ provider: entry.slice(0, idx), modelId: entry.slice(idx + 1) });\n\t\t}\n\t\treturn out;\n\t} catch (err) {\n\t\tconsole.error(\n\t\t\t`[gateway cron] failed to read cronFallbackModels: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t\treturn [];\n\t}\n}\n\n/**\n * Start the cron scheduler inside the gateway daemon. Starts even with zero\n * stored jobs — jobs can be created later from chats. stop() halts the loop.\n */\nexport function startGatewayCron(options: GatewayCronOptions): { stop(): void; intervalMs: number } {\n\tconst { adapters, cfg } = options;\n\tconst sessionFactory = options.sessionFactory ?? defaultCronSessionFactory;\n\tconst platformDeliverer = createPlatformDeliverer(adapters, cfg);\n\n\t// Explicit so runJob can split it into per-attempt budgets that sum to (at\n\t// most) the scheduler's outer race (core/cron/scheduler.ts runSchedulerTick).\n\tconst jobTimeoutMs = 5 * 60 * 1000;\n\n\t// Reject deliver targets that are not local, origin, a configured home channel,\n\t// or an explicit chat in the platform allowlist / the job's origin.\n\tsetCronDeliverValidator(createDeliverValidator(cfg));\n\n\t// Replace the local-notify bridge lunr-cron registered with the platform deliverer.\n\t(globalThis as Record<symbol, unknown>)[DELIVERY_BRIDGE_SYMBOL] = platformDeliverer;\n\n\tconst runJob = async (prompt: string, job: CronJob): Promise<string> => {\n\t\tconst fallbacks = options.fallbackModels ?? (await readCronFallbackModels());\n\t\tconst candidates: Array<CronModelRef | undefined> = [undefined, ...fallbacks];\n\t\tconst attemptTimeoutMs = Math.floor(jobTimeoutMs / candidates.length);\n\t\tconst errors: string[] = [];\n\t\tbeginCronFire();\n\t\ttry {\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tconst label = candidate ? `${candidate.provider}/${candidate.modelId}` : \"default model\";\n\t\t\t\tlet session: BridgeSession | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tsession = await sessionFactory(job, candidate);\n\t\t\t\t\t// Loading the builtin extensions re-registered lunr-cron's local-notify\n\t\t\t\t\t// bridge; restore the platform deliverer before any delivery re-reads it.\n\t\t\t\t\t(globalThis as Record<symbol, unknown>)[DELIVERY_BRIDGE_SYMBOL] = platformDeliverer;\n\t\t\t\t\tawait withAttemptTimeout(session.prompt(prompt, { source: \"extension\" }), attemptTimeoutMs);\n\t\t\t\t\tconst text = extractFinalText(session);\n\t\t\t\t\t// The marker lands in the output file (the audit trail); [SILENT] as\n\t\t\t\t\t// the last line still suppresses delivery (scheduler isSilent check).\n\t\t\t\t\treturn candidate ? `[fell back to ${label}]\\n${text}` : text;\n\t\t\t\t} catch (err) {\n\t\t\t\t\terrors.push(`${label}: ${err instanceof Error ? err.message : String(err)}`);\n\t\t\t\t} finally {\n\t\t\t\t\tsession?.dispose?.();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(errors.join(\" | \") || \"no model candidates\");\n\t\t} finally {\n\t\t\tendCronFire();\n\t\t}\n\t};\n\n\tconst deliverResult = async (job: CronJob, content: string): Promise<void> => {\n\t\t// Re-read the bridge on every delivery (same pattern as lunr-cron).\n\t\tconst bridge = (globalThis as Record<symbol, unknown>)[DELIVERY_BRIDGE_SYMBOL] as DeliveryBridge | undefined;\n\t\tif (!bridge) throw new Error(\"no cron delivery bridge registered\");\n\t\tconst err = await bridge(job, content);\n\t\tif (err) throw new Error(err);\n\t};\n\n\tconst scheduler = startScheduler({ runJob, deliverResult, intervalMs: options.intervalMs, jobTimeoutMs });\n\treturn { stop: () => scheduler.stop(), intervalMs: options.intervalMs ?? 60_000 };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/gateway/cron.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway cron runner + delivery (Phase 4 of the cron/gateway roadmap).\n *\n * startGatewayCron() runs the core/cron scheduler inside the `lunr gateway`\n * daemon:\n *\n * - runJob: each fire gets a FRESH headless agent session (createAgentSession\n * with SessionManager.inMemory — cron runs never write session files and\n * never touch the per-chat gateway sessions; the files under\n * <agentDir>/cron/output/ are the audit trail). The shared fire-guard\n * (core/cron/fire-guard) brackets the turn so the `cron` tool refuses to\n * run inside it (jobs cannot schedule jobs).\n * - deliverResult: replaces the `@lunr/cron-delivery` bridge on globalThis\n * with a platform deliverer. Targets come from job.deliver\n * (comma-separated): \"local\" (no-op — the output file is already written),\n * \"origin\" (the chat that created the job, homeChannel fallback), a bare\n * platform name (that platform's homeChannel), or\n * \"<platform>:<chatId>[:<threadId>]\" (explicit). Content is wrapped as\n * \"☾ Cron: <name>\" and split to the adapter's maxMessageLength.\n *\n * The bridge is re-installed after every cron session creation because loading\n * the builtin extensions (lunr-cron) re-registers its local-notify bridge per\n * session; delivery re-reads the bridge on every call, matching lunr-cron.\n *\n * Fallback models (2026-08-02): settings.json `cronFallbackModels`\n * (\"provider/modelId\" entries) are tried IN ORDER when a fire fails — timeout\n * or any error. Each attempt gets a fresh headless session and\n * jobTimeoutMs/candidates of the budget; a successful fallback run prefixes\n * \"[fell back to provider/modelId]\" to the output. TUI fires are unaffected\n * (they use the live session's model).\n */\n\nimport type { Model } from \"@ashx-j/lunr-ai\";\nimport { beginCronFire, endCronFire } from \"../core/cron/fire-guard.ts\";\nimport type { CronJob, CronJobOrigin } from \"../core/cron/jobs.ts\";\nimport { setCronDeliverValidator } from \"../core/cron/jobs.ts\";\nimport { startScheduler } from \"../core/cron/scheduler.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport { type BridgeSession, shutdownBridgeSession } from \"./agent-bridge.ts\";\nimport { type GatewayConfig, platformConfigFor } from \"./config.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { PlatformAdapter } from \"./types.ts\";\n\nconst DELIVERY_BRIDGE_SYMBOL = Symbol.for(\"@lunr/cron-delivery\");\n\ntype DeliveryBridge = (job: CronJob, content: string) => Promise<string | null>;\n\n/** Test seam / default: one fresh headless session per cron fire. */\nexport interface CronModelRef {\n\tprovider: string;\n\tmodelId: string;\n}\n\nexport type CronSessionFactory = (job: CronJob, modelOverride?: CronModelRef) => Promise<BridgeSession>;\n\nexport interface GatewayCronOptions {\n\tadapters: Map<string, PlatformAdapter>;\n\tcfg: GatewayConfig;\n\t/** Tick interval; default 60s. */\n\tintervalMs?: number;\n\t/** Session factory for cron fires; defaults to a real headless agent session. */\n\tsessionFactory?: CronSessionFactory;\n\t/** Test seam: fallback models. When omitted, read fresh from settings.json `cronFallbackModels` on every fire. */\n\tfallbackModels?: CronModelRef[];\n}\n\nconst KNOWN_PLATFORMS = [\"telegram\", \"discord\"] as const;\n\n/** Default factory: fresh in-memory headless session, mirroring agent-bridge's wiring. */\nasync function defaultCronSessionFactory(job: CronJob, modelOverride?: CronModelRef): Promise<BridgeSession> {\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 = job.workdir ?? process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\t// In-memory on purpose: cron runs must not persist session files or pollute\n\t// the per-chat gateway sessions — <agentDir>/cron/output/ is the audit trail.\n\tconst sessionManager = SessionManager.inMemory(cwd);\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.\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\t// Fallback-model attempts pin the session model explicitly; an unresolvable\n\t// or unauthenticated override fails the attempt so the next fallback runs.\n\tlet model: Model<any> | undefined;\n\tif (modelOverride) {\n\t\tconst resolved = services.modelRuntime.getModel(modelOverride.provider, modelOverride.modelId);\n\t\tif (!resolved) throw new Error(`model not found: ${modelOverride.provider}/${modelOverride.modelId}`);\n\t\tif (!services.modelRuntime.hasConfiguredAuth(resolved.provider)) {\n\t\t\tthrow new Error(`no configured auth for provider: ${modelOverride.provider}`);\n\t\t}\n\t\tmodel = resolved;\n\t}\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager, model });\n\tconst permissionId = sessionManager.getSessionId();\n\tcreatePermissionContext(permissionId, settingsManager.getDefaultPermissionMode(), false);\n\tconst dispose = session.dispose.bind(session);\n\tsession.dispose = () => {\n\t\tdeletePermissionContext(permissionId);\n\t\tdispose();\n\t};\n\ttry {\n\t\tbindRuntimeBridges({ session, services });\n\t\tawait session.bindExtensions({\n\t\t\tmode: \"print\",\n\t\t\tonError: (err) => console.error(`[gateway cron] extension error (${err.extensionPath}): ${err.error}`),\n\t\t});\n\t\treturn session;\n\t} catch (error) {\n\t\tawait shutdownBridgeSession(session, \"quit\");\n\t\tthrow error;\n\t}\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 {\n\t\tstopReason?: string;\n\t\terrorMessage?: string;\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t};\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\ninterface ResolvedTarget {\n\tplatform: string;\n\tchatId: string;\n\tthreadId?: string;\n}\n\nconst EXPLICIT_TARGET_RE = /^([a-z0-9_-]+):([^:]+)(?::([^:]+))?$/i;\n\n/** Resolve one deliver target to a concrete destination. Returns an error string on failure. */\nfunction resolveTarget(target: string, job: CronJob, cfg: GatewayConfig): ResolvedTarget | string {\n\tif (target === \"origin\") {\n\t\tconst origin = job.origin;\n\t\tif (origin?.platform && origin.chatId) {\n\t\t\treturn { platform: origin.platform, chatId: origin.chatId, threadId: origin.threadId };\n\t\t}\n\t\t// Missing/incomplete origin → fall back to a homeChannel: the origin's\n\t\t// platform when known, otherwise the first platform that has one.\n\t\tconst platforms = origin?.platform ? [origin.platform] : KNOWN_PLATFORMS;\n\t\tfor (const platform of platforms) {\n\t\t\tconst home = platformConfigFor(cfg, platform)?.homeChannel;\n\t\t\tif (home) return { platform, chatId: home };\n\t\t}\n\t\treturn `deliver target \"origin\": job has no origin and no homeChannel is configured`;\n\t}\n\n\tconst explicit = EXPLICIT_TARGET_RE.exec(target);\n\tconst platform = explicit ? explicit[1].toLowerCase() : target.toLowerCase();\n\tif (platformConfigFor(cfg, platform) === undefined) {\n\t\treturn `unknown platform \"${explicit ? explicit[1] : target}\"`;\n\t}\n\tif (explicit) {\n\t\treturn { platform, chatId: explicit[2], threadId: explicit[3] };\n\t}\n\t// Bare platform name → its homeChannel.\n\tconst home = platformConfigFor(cfg, platform)?.homeChannel;\n\tif (!home) return `no homeChannel configured for ${platform}`;\n\treturn { platform, chatId: home };\n}\n\n/** True when a resolved chat may receive cron output for this job. */\nexport function isAllowedDeliverChat(\n\tcfg: GatewayConfig,\n\tplatform: string,\n\tchatId: string,\n\torigin?: CronJobOrigin | null,\n): boolean {\n\tconst platformCfg = platformConfigFor(cfg, platform);\n\tif (!platformCfg) return false;\n\tif (platformCfg.homeChannel && platformCfg.homeChannel === chatId) return true;\n\tif (platformCfg.allowedChats?.includes(chatId)) return true;\n\treturn origin?.platform === platform && origin.chatId === chatId;\n}\n\nexport function createDeliverValidator(\n\tcfg: GatewayConfig,\n): (deliver: string, origin?: CronJobOrigin | null) => string | undefined {\n\treturn (deliver, origin) => {\n\t\tconst targets = deliver\n\t\t\t.split(\",\")\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t\tfor (const target of targets) {\n\t\t\tif (target === \"local\") continue;\n\t\t\tif (target === \"origin\") continue; // delivery time resolves/fails with a sensible error\n\t\t\tconst explicit = EXPLICIT_TARGET_RE.exec(target);\n\t\t\tconst platform = explicit ? explicit[1].toLowerCase() : target.toLowerCase();\n\t\t\tconst platformCfg = platformConfigFor(cfg, platform);\n\t\t\tif (!platformCfg) return `unknown platform \"${explicit ? explicit[1] : target}\"`;\n\t\t\tif (explicit) {\n\t\t\t\tif (!isAllowedDeliverChat(cfg, platform, explicit[2], origin)) {\n\t\t\t\t\treturn `deliver target \"${target}\" is not an allowed chat for ${platform}`;\n\t\t\t\t}\n\t\t\t} else if (!platformCfg.homeChannel) {\n\t\t\t\treturn `no homeChannel configured for ${platform}`;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t};\n}\n\n/** Wrap the run output with a compact cron header. */\nexport function wrapCronContent(job: CronJob, content: string): string {\n\treturn `☾ Cron: ${job.name}\\n———\\n${content}`;\n}\n\n/**\n * The platform deliverer installed at the `@lunr/cron-delivery` bridge.\n * Every target is attempted; the first error is returned (null on success).\n */\nexport function createPlatformDeliverer(adapters: Map<string, PlatformAdapter>, cfg: GatewayConfig): DeliveryBridge {\n\treturn async (job, content) => {\n\t\tconst targets = job.deliver\n\t\t\t.split(\",\")\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t\tlet firstError: string | null = null;\n\t\tfor (const target of targets) {\n\t\t\tif (target === \"local\") continue; // output file already written by the scheduler\n\t\t\tconst resolved = resolveTarget(target, job, cfg);\n\t\t\tif (typeof resolved === \"string\") {\n\t\t\t\tfirstError ??= resolved;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!isAllowedDeliverChat(cfg, resolved.platform, resolved.chatId, job.origin)) {\n\t\t\t\tfirstError ??= `deliver target \"${target}\" is not an allowed chat for ${resolved.platform}`;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst adapter = adapters.get(resolved.platform);\n\t\t\tif (!adapter) {\n\t\t\t\tfirstError ??= `no adapter connected for ${resolved.platform}`;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst wrapped = wrapCronContent(job, content);\n\t\t\tfor (const chunk of splitMessage(wrapped, adapter.maxMessageLength)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await adapter.send(resolved.chatId, chunk, {\n\t\t\t\t\t\tthreadId: resolved.threadId,\n\t\t\t\t\t});\n\t\t\t\t\tif (!result.success) {\n\t\t\t\t\t\tfirstError ??= `${resolved.platform} send failed: ${result.error ?? \"unknown error\"}`;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tfirstError ??= `${resolved.platform} send failed: ${err instanceof Error ? err.message : String(err)}`;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn firstError;\n\t};\n}\n\n/** Race one prompt attempt against its share of the job budget. */\nfunction withAttemptTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\treturn Promise.race([\n\t\tpromise,\n\t\tnew Promise<never>((_, reject) => {\n\t\t\tconst timer = setTimeout(() => reject(new Error(`attempt timed out after ${ms}ms`)), ms);\n\t\t\t// If the attempt settles first this promise is discarded; the timer must not keep the process alive.\n\t\t\ttimer.unref?.();\n\t\t}),\n\t]);\n}\n\n/**\n * Read settings.json `cronFallbackModels` (\"provider/modelId\" entries).\n * Fresh read per fire so edits apply without a daemon restart; invalid\n * entries are skipped with a log line. Never throws.\n */\nasync function readCronFallbackModels(): Promise<CronModelRef[]> {\n\ttry {\n\t\tconst [{ getAgentDir }, { SettingsManager }] = await Promise.all([\n\t\t\timport(\"../config.ts\"),\n\t\t\timport(\"../core/settings-manager.ts\"),\n\t\t]);\n\t\tconst settingsManager = SettingsManager.create(process.cwd(), getAgentDir(), { projectTrusted: false });\n\t\tconst out: CronModelRef[] = [];\n\t\tfor (const entry of settingsManager.getCronFallbackModels()) {\n\t\t\tconst idx = entry.indexOf(\"/\");\n\t\t\tif (idx <= 0 || idx === entry.length - 1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[gateway cron] ignoring invalid cronFallbackModels entry \"${entry}\" (want \"provider/modelId\")`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout.push({ provider: entry.slice(0, idx), modelId: entry.slice(idx + 1) });\n\t\t}\n\t\treturn out;\n\t} catch (err) {\n\t\tconsole.error(\n\t\t\t`[gateway cron] failed to read cronFallbackModels: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t\treturn [];\n\t}\n}\n\n/**\n * Start the cron scheduler inside the gateway daemon. Starts even with zero\n * stored jobs — jobs can be created later from chats. stop() halts the loop.\n */\nexport function startGatewayCron(options: GatewayCronOptions): { stop(): void; intervalMs: number } {\n\tconst { adapters, cfg } = options;\n\tconst sessionFactory = options.sessionFactory ?? defaultCronSessionFactory;\n\tconst platformDeliverer = createPlatformDeliverer(adapters, cfg);\n\n\t// Explicit so runJob can split it into per-attempt budgets that sum to (at\n\t// most) the scheduler's outer race (core/cron/scheduler.ts runSchedulerTick).\n\tconst jobTimeoutMs = 5 * 60 * 1000;\n\n\t// Reject deliver targets that are not local, origin, a configured home channel,\n\t// or an explicit chat in the platform allowlist / the job's origin.\n\tsetCronDeliverValidator(createDeliverValidator(cfg));\n\n\t// Replace the local-notify bridge lunr-cron registered with the platform deliverer.\n\t(globalThis as Record<symbol, unknown>)[DELIVERY_BRIDGE_SYMBOL] = platformDeliverer;\n\n\tconst runJob = async (prompt: string, job: CronJob): Promise<string> => {\n\t\tconst fallbacks = options.fallbackModels ?? (await readCronFallbackModels());\n\t\tconst candidates: Array<CronModelRef | undefined> = [undefined, ...fallbacks];\n\t\tconst attemptTimeoutMs = Math.floor(jobTimeoutMs / candidates.length);\n\t\tconst errors: string[] = [];\n\t\tbeginCronFire();\n\t\ttry {\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tconst label = candidate ? `${candidate.provider}/${candidate.modelId}` : \"default model\";\n\t\t\t\tlet session: BridgeSession | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tsession = await sessionFactory(job, candidate);\n\t\t\t\t\t// Loading the builtin extensions re-registered lunr-cron's local-notify\n\t\t\t\t\t// bridge; restore the platform deliverer before any delivery re-reads it.\n\t\t\t\t\t(globalThis as Record<symbol, unknown>)[DELIVERY_BRIDGE_SYMBOL] = platformDeliverer;\n\t\t\t\t\tawait withAttemptTimeout(session.prompt(prompt, { source: \"extension\" }), attemptTimeoutMs);\n\t\t\t\t\tconst text = extractFinalText(session);\n\t\t\t\t\t// The marker lands in the output file (the audit trail); [SILENT] as\n\t\t\t\t\t// the last line still suppresses delivery (scheduler isSilent check).\n\t\t\t\t\treturn candidate ? `[fell back to ${label}]\\n${text}` : text;\n\t\t\t\t} catch (err) {\n\t\t\t\t\terrors.push(`${label}: ${err instanceof Error ? err.message : String(err)}`);\n\t\t\t\t} finally {\n\t\t\t\t\tif (session) await shutdownBridgeSession(session, \"quit\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(errors.join(\" | \") || \"no model candidates\");\n\t\t} finally {\n\t\t\tendCronFire();\n\t\t}\n\t};\n\n\tconst deliverResult = async (job: CronJob, content: string): Promise<void> => {\n\t\t// Re-read the bridge on every delivery (same pattern as lunr-cron).\n\t\tconst bridge = (globalThis as Record<symbol, unknown>)[DELIVERY_BRIDGE_SYMBOL] as DeliveryBridge | undefined;\n\t\tif (!bridge) throw new Error(\"no cron delivery bridge registered\");\n\t\tconst err = await bridge(job, content);\n\t\tif (err) throw new Error(err);\n\t};\n\n\tconst scheduler = startScheduler({ runJob, deliverResult, intervalMs: options.intervalMs, jobTimeoutMs });\n\treturn { stop: () => scheduler.stop(), intervalMs: options.intervalMs ?? 60_000 };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAkB,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AAE/G,OAAO,EAGN,KAAK,iBAAiB,EAEtB,MAAM,uBAAuB,CAAC;AAO/B;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,UAAU,UAAQ;IAClB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IAGnC,OAAO,CAAC,gBAAgB,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAqB;IAGzC,OAAO,CAAC,eAAe,CAAC,CAAsB;IAC9C,OAAO,CAAC,aAAa,CAAgB;IAGrC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAC,CAAmB;IAC1C,OAAO,CAAC,YAAY,CAAS;IAE7B,YACC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,mBAAmB,SAAgB,EACnC,SAAS,SAAI,EACb,gBAAgB,UAAQ,EAiBxB;IAEQ,UAAU,IAAI,IAAI,CAK1B;IAED,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAKxC;IAGD,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAK3C;IAGD,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAMnC;IAED,OAAO,CAAC,aAAa;IAIrB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAgBxC;IAED,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAclD;IAED,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,SAAS,GAAG,IAAI,CAEjE;IAED,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAK1C;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKlC;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvC;IAED,OAAO,CAAC,qBAAqB;IAI7B,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,gBAAgB,CAAA;KAAE,GAAG,IAAI,CAwG9F;IAED,OAAO,CAAC,iBAAiB;CA6DzB","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@ashx-j/lunr-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport {\n\tformatThoughtDuration,\n\tisThinkingRunComplete,\n\ttype ThinkingRunTiming,\n\tthinkingSnippet,\n} from \"./thinking-summary.ts\";\nimport { ThinkingTailComponent } from \"./thinking-tail.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tselectable = true;\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\t// lunr: collapsible reasoning — when true, a completed thinking run renders as\n\t// \"✻ Thought for Xs\" + its first sentence instead of the full block.\n\tprivate thinkingCollapse: boolean;\n\t// lunr: per-run expand. `expanded` still means \"all runs\" for setExpanded().\n\tprivate expanded = false;\n\tprivate expandedRuns = new Set<number>();\n\t// lunr: live per-run timings from interactive-mode; undefined = history message\n\t// (always treated as final, rendered without durations).\n\tprivate thinkingTimings?: ThinkingRunTiming[];\n\tprivate markdownTheme: MarkdownTheme;\n\t// lunr: kept for the deferred alternative hidden-thinking indicator (Phase 8 removed the label render).\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: retained for future use\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate thinkingSource?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tthinkingCollapse = false,\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingCollapse = thinkingCollapse;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).\n\tsetThinkingCollapse(collapse: boolean): void {\n\t\tthis.thinkingCollapse = collapse;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: Expandable hook; true expands every thinking run, false collapses all.\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tif (!expanded) this.expandedRuns.clear();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tprivate isRunExpanded(runIndex: number): boolean {\n\t\treturn this.expanded || this.expandedRuns.has(runIndex);\n\t}\n\n\ttoggleThinkingRun(runIndex: number): void {\n\t\tif (this.expanded) {\n\t\t\tthis.expanded = false;\n\t\t\tthis.expandedRuns.clear();\n\t\t\tconst count = this.lastMessage ? collectThinkingRuns(this.lastMessage.content).length : 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tif (i !== runIndex) this.expandedRuns.add(i);\n\t\t\t}\n\t\t} else if (this.expandedRuns.has(runIndex)) {\n\t\t\tthis.expandedRuns.delete(runIndex);\n\t\t} else {\n\t\t\tthis.expandedRuns.add(runIndex);\n\t\t}\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\thandleClick(localY: number, width: number): boolean {\n\t\tconst contentWidth = width;\n\t\tlet y = 0;\n\t\tfor (const child of this.contentContainer.children) {\n\t\t\tconst h = child.render(contentWidth).length;\n\t\t\tif (localY >= y && localY < y + h) {\n\t\t\t\tif (typeof child.handleClick === \"function\") {\n\t\t\t\t\treturn child.handleClick(localY - y, contentWidth);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ty += h;\n\t\t}\n\t\treturn false;\n\t}\n\n\tsetThinkingTimings(timings: ThinkingRunTiming[] | undefined): void {\n\t\tthis.thinkingTimings = timings;\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) return lines;\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\n\t\treturn lines;\n\t}\n\n\tprivate thinkingSourceOptions(): { thinkingSource?: AssistantMessage } | undefined {\n\t\treturn this.thinkingSource ? { thinkingSource: this.thinkingSource } : undefined;\n\t}\n\n\tupdateContent(message: AssistantMessage, options?: { thinkingSource?: AssistantMessage }): void {\n\t\tthis.lastMessage = message;\n\t\tthis.thinkingSource = options?.thinkingSource;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst sourceMessage = this.thinkingSource ?? message;\n\t\tconst sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);\n\t\tconst hasVisibleContent =\n\t\t\tmessage.content.some(\n\t\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t\t) ||\n\t\t\t(!this.hideThinkingBlock && sourceThinkingRuns.some((run) => run.length > 0));\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet isFirstTextBlock = true;\n\t\tlet thinkingRunIndex = -1;\n\t\tlet renderedThinking = false;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\t// lunr: leading ● marks the first text block (never thinking blocks). It rides\n\t\t\t\t// the defaultTextStyle below, so it renders bright white like the body.\n\t\t\t\tconst trimmed = content.text.trim();\n\t\t\t\tconst text = isFirstTextBlock ? `● ${trimmed}` : trimmed;\n\t\t\t\tisFirstTextBlock = false;\n\t\t\t\t// lunr: theme-polish — assistant message body renders bright white via the\n\t\t\t\t// shared userMessageText token (same knob as user messages).\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(text, this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (body: string) => theme.fg(\"userMessageText\", body),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tthinkingRunIndex++;\n\t\t\t\tconst displayBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tdisplayBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tconst thinkingBlocks = sourceThinkingRuns[thinkingRunIndex] ?? displayBlocks;\n\t\t\t\tif (thinkingBlocks.length === 0 || this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\trenderedThinking = true;\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, i);\n\t\t\t}\n\t\t}\n\n\t\tif (!this.hideThinkingBlock && !renderedThinking && sourceThinkingRuns.length > 0) {\n\t\t\tthinkingRunIndex = 0;\n\t\t\tconst thinkingBlocks = sourceThinkingRuns[0] ?? [];\n\t\t\tif (thinkingBlocks.length > 0) {\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, -1);\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t\t\t),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), this.outputPad, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), this.outputPad, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderThinkingRun(\n\t\tthinkingRunIndex: number,\n\t\tthinkingBlocks: string[],\n\t\tdisplayMessage: AssistantMessage,\n\t\tdisplayIndex: number,\n\t): void {\n\t\tconst hasVisibleContentAfter =\n\t\t\tdisplayIndex >= 0\n\t\t\t\t? displayMessage.content\n\t\t\t\t\t\t.slice(displayIndex + 1)\n\t\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()))\n\t\t\t\t: displayMessage.content.some((c) => c.type === \"text\" && c.text.trim());\n\n\t\tconst runComplete = isThinkingRunComplete(\n\t\t\tfalse,\n\t\t\tthis.thinkingTimings?.[thinkingRunIndex],\n\t\t\tthis.thinkingTimings !== undefined,\n\t\t);\n\t\tconst wrap = (inner: Component): void => {\n\t\t\tthis.contentContainer.addChild(new ThinkingRunBlock(this, thinkingRunIndex, inner));\n\t\t};\n\t\tif (this.thinkingCollapse && !this.isRunExpanded(thinkingRunIndex) && runComplete) {\n\t\t\tconst timing = this.thinkingTimings?.[thinkingRunIndex];\n\t\t\tconst label =\n\t\t\t\ttiming?.end !== undefined\n\t\t\t\t\t? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`\n\t\t\t\t\t: \"✻ Thought\";\n\t\t\tconst block = new Container();\n\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(label)), this.outputPad, 0));\n\t\t\tconst snippet = thinkingSnippet(thinkingBlocks.join(\"\\n\\n\"));\n\t\t\tif (snippet) {\n\t\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(snippet)), this.outputPad + 2, 0));\n\t\t\t}\n\t\t\twrap(block);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!runComplete && !this.isRunExpanded(thinkingRunIndex)) {\n\t\t\twrap(\n\t\t\t\tnew ThinkingTailComponent(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\titalic: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\twrap(\n\t\t\tnew Markdown(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\titalic: true,\n\t\t\t}),\n\t\t);\n\t\tif (hasVisibleContentAfter) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n}\n\nclass ThinkingRunBlock extends Container {\n\tprivate readonly owner: AssistantMessageComponent;\n\tprivate readonly runIndex: number;\n\n\tconstructor(owner: AssistantMessageComponent, runIndex: number, inner: Component) {\n\t\tsuper();\n\t\tthis.owner = owner;\n\t\tthis.runIndex = runIndex;\n\t\tthis.addChild(inner);\n\t}\n\n\thandleClick(_localY: number, _width: number): boolean {\n\t\tthis.owner.toggleThinkingRun(this.runIndex);\n\t\treturn true;\n\t}\n}\n\nfunction collectThinkingRuns(content: AssistantMessage[\"content\"]): string[][] {\n\tconst runs: string[][] = [];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type !== \"thinking\") continue;\n\t\tconst blocks: string[] = [];\n\t\tfor (; i < content.length; i++) {\n\t\t\tconst thinkingContent = content[i];\n\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\tif (thinking) blocks.push(thinking);\n\t\t}\n\t\tif (blocks.length > 0) runs.push(blocks);\n\t}\n\treturn runs;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAkB,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AAE/G,OAAO,EAGN,KAAK,iBAAiB,EAEtB,MAAM,uBAAuB,CAAC;AAO/B;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,UAAU,UAAQ;IAClB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IAGnC,OAAO,CAAC,gBAAgB,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAqB;IAGzC,OAAO,CAAC,eAAe,CAAC,CAAsB;IAC9C,OAAO,CAAC,aAAa,CAAgB;IAGrC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAC,CAAmB;IAC1C,OAAO,CAAC,YAAY,CAAS;IAE7B,YACC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,mBAAmB,SAAgB,EACnC,SAAS,SAAI,EACb,gBAAgB,UAAQ,EAiBxB;IAEQ,UAAU,IAAI,IAAI,CAK1B;IAED,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAKxC;IAGD,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAK3C;IAGD,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAMnC;IAED,OAAO,CAAC,aAAa;IAIrB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAgBxC;IAED,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAclD;IAED,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,SAAS,GAAG,IAAI,CAEjE;IAED,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAK1C;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKlC;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvC;IAED,OAAO,CAAC,qBAAqB;IAI7B,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,gBAAgB,CAAA;KAAE,GAAG,IAAI,CAiG9F;IAED,OAAO,CAAC,iBAAiB;CA6DzB","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@ashx-j/lunr-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport {\n\tformatThoughtDuration,\n\tisThinkingRunComplete,\n\ttype ThinkingRunTiming,\n\tthinkingSnippet,\n} from \"./thinking-summary.ts\";\nimport { ThinkingTailComponent } from \"./thinking-tail.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tselectable = true;\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\t// lunr: collapsible reasoning — when true, a completed thinking run renders as\n\t// \"✻ Thought for Xs\" + its first sentence instead of the full block.\n\tprivate thinkingCollapse: boolean;\n\t// lunr: per-run expand. `expanded` still means \"all runs\" for setExpanded().\n\tprivate expanded = false;\n\tprivate expandedRuns = new Set<number>();\n\t// lunr: live per-run timings from interactive-mode; undefined = history message\n\t// (always treated as final, rendered without durations).\n\tprivate thinkingTimings?: ThinkingRunTiming[];\n\tprivate markdownTheme: MarkdownTheme;\n\t// lunr: kept for the deferred alternative hidden-thinking indicator (Phase 8 removed the label render).\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: retained for future use\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate thinkingSource?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tthinkingCollapse = false,\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingCollapse = thinkingCollapse;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).\n\tsetThinkingCollapse(collapse: boolean): void {\n\t\tthis.thinkingCollapse = collapse;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: Expandable hook; true expands every thinking run, false collapses all.\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tif (!expanded) this.expandedRuns.clear();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tprivate isRunExpanded(runIndex: number): boolean {\n\t\treturn this.expanded || this.expandedRuns.has(runIndex);\n\t}\n\n\ttoggleThinkingRun(runIndex: number): void {\n\t\tif (this.expanded) {\n\t\t\tthis.expanded = false;\n\t\t\tthis.expandedRuns.clear();\n\t\t\tconst count = this.lastMessage ? collectThinkingRuns(this.lastMessage.content).length : 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tif (i !== runIndex) this.expandedRuns.add(i);\n\t\t\t}\n\t\t} else if (this.expandedRuns.has(runIndex)) {\n\t\t\tthis.expandedRuns.delete(runIndex);\n\t\t} else {\n\t\t\tthis.expandedRuns.add(runIndex);\n\t\t}\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\thandleClick(localY: number, width: number): boolean {\n\t\tconst contentWidth = width;\n\t\tlet y = 0;\n\t\tfor (const child of this.contentContainer.children) {\n\t\t\tconst h = child.render(contentWidth).length;\n\t\t\tif (localY >= y && localY < y + h) {\n\t\t\t\tif (typeof child.handleClick === \"function\") {\n\t\t\t\t\treturn child.handleClick(localY - y, contentWidth);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ty += h;\n\t\t}\n\t\treturn false;\n\t}\n\n\tsetThinkingTimings(timings: ThinkingRunTiming[] | undefined): void {\n\t\tthis.thinkingTimings = timings;\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) return lines;\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\n\t\treturn lines;\n\t}\n\n\tprivate thinkingSourceOptions(): { thinkingSource?: AssistantMessage } | undefined {\n\t\treturn this.thinkingSource ? { thinkingSource: this.thinkingSource } : undefined;\n\t}\n\n\tupdateContent(message: AssistantMessage, options?: { thinkingSource?: AssistantMessage }): void {\n\t\tthis.lastMessage = message;\n\t\tthis.thinkingSource = options?.thinkingSource;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst sourceMessage = this.thinkingSource ?? message;\n\t\tconst sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) =>\n\t\t\t\t(c.type === \"text\" && c.text.trim()) ||\n\t\t\t\t(!this.hideThinkingBlock && c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet isFirstTextBlock = true;\n\t\tlet thinkingRunIndex = -1;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\t// lunr: leading ● marks the first text block (never thinking blocks). It rides\n\t\t\t\t// the defaultTextStyle below, so it renders bright white like the body.\n\t\t\t\tconst trimmed = content.text.trim();\n\t\t\t\tconst text = isFirstTextBlock ? `● ${trimmed}` : trimmed;\n\t\t\t\tisFirstTextBlock = false;\n\t\t\t\t// lunr: theme-polish — assistant message body renders bright white via the\n\t\t\t\t// shared userMessageText token (same knob as user messages).\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(text, this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (body: string) => theme.fg(\"userMessageText\", body),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tthinkingRunIndex++;\n\t\t\t\tconst displayBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tdisplayBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (displayBlocks.length === 0 || this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst sourceBlocks = sourceThinkingRuns[thinkingRunIndex] ?? [];\n\t\t\t\tconst fullyRevealed =\n\t\t\t\t\tdisplayBlocks.length === sourceBlocks.length &&\n\t\t\t\t\tdisplayBlocks.every((block, index) => block === sourceBlocks[index]);\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, displayBlocks, message, i, fullyRevealed);\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t\t\t),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), this.outputPad, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), this.outputPad, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderThinkingRun(\n\t\tthinkingRunIndex: number,\n\t\tthinkingBlocks: string[],\n\t\tdisplayMessage: AssistantMessage,\n\t\tdisplayIndex: number,\n\t\tfullyRevealed: boolean,\n\t): void {\n\t\tconst hasVisibleContentAfter =\n\t\t\tdisplayIndex >= 0\n\t\t\t\t? displayMessage.content\n\t\t\t\t\t\t.slice(displayIndex + 1)\n\t\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()))\n\t\t\t\t: displayMessage.content.some((c) => c.type === \"text\" && c.text.trim());\n\n\t\t// Provider completion can precede the reveal cursor; keep animating until it catches up.\n\t\tconst runComplete =\n\t\t\tfullyRevealed &&\n\t\t\tisThinkingRunComplete(false, this.thinkingTimings?.[thinkingRunIndex], this.thinkingTimings !== undefined);\n\t\tconst wrap = (inner: Component): void => {\n\t\t\tthis.contentContainer.addChild(new ThinkingRunBlock(this, thinkingRunIndex, inner));\n\t\t};\n\t\tif (this.thinkingCollapse && !this.isRunExpanded(thinkingRunIndex) && runComplete) {\n\t\t\tconst timing = this.thinkingTimings?.[thinkingRunIndex];\n\t\t\tconst label =\n\t\t\t\ttiming?.end !== undefined\n\t\t\t\t\t? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`\n\t\t\t\t\t: \"✻ Thought\";\n\t\t\tconst block = new Container();\n\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(label)), this.outputPad, 0));\n\t\t\tconst snippet = thinkingSnippet(thinkingBlocks.join(\"\\n\\n\"));\n\t\t\tif (snippet) {\n\t\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(snippet)), this.outputPad + 2, 0));\n\t\t\t}\n\t\t\twrap(block);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!runComplete && !this.isRunExpanded(thinkingRunIndex)) {\n\t\t\twrap(\n\t\t\t\tnew ThinkingTailComponent(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\titalic: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\twrap(\n\t\t\tnew Markdown(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\titalic: true,\n\t\t\t}),\n\t\t);\n\t\tif (hasVisibleContentAfter) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n}\n\nclass ThinkingRunBlock extends Container {\n\tprivate readonly owner: AssistantMessageComponent;\n\tprivate readonly runIndex: number;\n\n\tconstructor(owner: AssistantMessageComponent, runIndex: number, inner: Component) {\n\t\tsuper();\n\t\tthis.owner = owner;\n\t\tthis.runIndex = runIndex;\n\t\tthis.addChild(inner);\n\t}\n\n\thandleClick(_localY: number, _width: number): boolean {\n\t\tthis.owner.toggleThinkingRun(this.runIndex);\n\t\treturn true;\n\t}\n}\n\nfunction collectThinkingRuns(content: AssistantMessage[\"content\"]): string[][] {\n\tconst runs: string[][] = [];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type !== \"thinking\") continue;\n\t\tconst blocks: string[] = [];\n\t\tfor (; i < content.length; i++) {\n\t\t\tconst thinkingContent = content[i];\n\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\tif (thinking) blocks.push(thinking);\n\t\t}\n\t\truns.push(blocks);\n\t}\n\treturn runs;\n}\n"]}
|
|
@@ -142,15 +142,14 @@ export class AssistantMessageComponent extends Container {
|
|
|
142
142
|
this.contentContainer.clear();
|
|
143
143
|
const sourceMessage = this.thinkingSource ?? message;
|
|
144
144
|
const sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);
|
|
145
|
-
const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) ||
|
|
146
|
-
(!this.hideThinkingBlock &&
|
|
145
|
+
const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) ||
|
|
146
|
+
(!this.hideThinkingBlock && c.type === "thinking" && c.thinking.trim()));
|
|
147
147
|
if (hasVisibleContent) {
|
|
148
148
|
this.contentContainer.addChild(new Spacer(1));
|
|
149
149
|
}
|
|
150
150
|
// Render content in order
|
|
151
151
|
let isFirstTextBlock = true;
|
|
152
152
|
let thinkingRunIndex = -1;
|
|
153
|
-
let renderedThinking = false;
|
|
154
153
|
for (let i = 0; i < message.content.length; i++) {
|
|
155
154
|
const content = message.content[i];
|
|
156
155
|
if (content.type === "text" && content.text.trim()) {
|
|
@@ -181,19 +180,13 @@ export class AssistantMessageComponent extends Container {
|
|
|
181
180
|
}
|
|
182
181
|
}
|
|
183
182
|
i--;
|
|
184
|
-
|
|
185
|
-
if (thinkingBlocks.length === 0 || this.hideThinkingBlock) {
|
|
183
|
+
if (displayBlocks.length === 0 || this.hideThinkingBlock) {
|
|
186
184
|
continue;
|
|
187
185
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (!this.hideThinkingBlock && !renderedThinking && sourceThinkingRuns.length > 0) {
|
|
193
|
-
thinkingRunIndex = 0;
|
|
194
|
-
const thinkingBlocks = sourceThinkingRuns[0] ?? [];
|
|
195
|
-
if (thinkingBlocks.length > 0) {
|
|
196
|
-
this.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, -1);
|
|
186
|
+
const sourceBlocks = sourceThinkingRuns[thinkingRunIndex] ?? [];
|
|
187
|
+
const fullyRevealed = displayBlocks.length === sourceBlocks.length &&
|
|
188
|
+
displayBlocks.every((block, index) => block === sourceBlocks[index]);
|
|
189
|
+
this.renderThinkingRun(thinkingRunIndex, displayBlocks, message, i, fullyRevealed);
|
|
197
190
|
}
|
|
198
191
|
}
|
|
199
192
|
// Check if incomplete/failed - show after partial content.
|
|
@@ -220,13 +213,15 @@ export class AssistantMessageComponent extends Container {
|
|
|
220
213
|
}
|
|
221
214
|
}
|
|
222
215
|
}
|
|
223
|
-
renderThinkingRun(thinkingRunIndex, thinkingBlocks, displayMessage, displayIndex) {
|
|
216
|
+
renderThinkingRun(thinkingRunIndex, thinkingBlocks, displayMessage, displayIndex, fullyRevealed) {
|
|
224
217
|
const hasVisibleContentAfter = displayIndex >= 0
|
|
225
218
|
? displayMessage.content
|
|
226
219
|
.slice(displayIndex + 1)
|
|
227
220
|
.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()))
|
|
228
221
|
: displayMessage.content.some((c) => c.type === "text" && c.text.trim());
|
|
229
|
-
|
|
222
|
+
// Provider completion can precede the reveal cursor; keep animating until it catches up.
|
|
223
|
+
const runComplete = fullyRevealed &&
|
|
224
|
+
isThinkingRunComplete(false, this.thinkingTimings?.[thinkingRunIndex], this.thinkingTimings !== undefined);
|
|
230
225
|
const wrap = (inner) => {
|
|
231
226
|
this.contentContainer.addChild(new ThinkingRunBlock(this, thinkingRunIndex, inner));
|
|
232
227
|
};
|
|
@@ -297,8 +292,7 @@ function collectThinkingRuns(content) {
|
|
|
297
292
|
if (thinking)
|
|
298
293
|
blocks.push(thinking);
|
|
299
294
|
}
|
|
300
|
-
|
|
301
|
-
runs.push(blocks);
|
|
295
|
+
runs.push(blocks);
|
|
302
296
|
}
|
|
303
297
|
return runs;
|
|
304
298
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@ashx-j/lunr-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport {\n\tformatThoughtDuration,\n\tisThinkingRunComplete,\n\ttype ThinkingRunTiming,\n\tthinkingSnippet,\n} from \"./thinking-summary.ts\";\nimport { ThinkingTailComponent } from \"./thinking-tail.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tselectable = true;\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\t// lunr: collapsible reasoning — when true, a completed thinking run renders as\n\t// \"✻ Thought for Xs\" + its first sentence instead of the full block.\n\tprivate thinkingCollapse: boolean;\n\t// lunr: per-run expand. `expanded` still means \"all runs\" for setExpanded().\n\tprivate expanded = false;\n\tprivate expandedRuns = new Set<number>();\n\t// lunr: live per-run timings from interactive-mode; undefined = history message\n\t// (always treated as final, rendered without durations).\n\tprivate thinkingTimings?: ThinkingRunTiming[];\n\tprivate markdownTheme: MarkdownTheme;\n\t// lunr: kept for the deferred alternative hidden-thinking indicator (Phase 8 removed the label render).\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: retained for future use\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate thinkingSource?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tthinkingCollapse = false,\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingCollapse = thinkingCollapse;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).\n\tsetThinkingCollapse(collapse: boolean): void {\n\t\tthis.thinkingCollapse = collapse;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: Expandable hook; true expands every thinking run, false collapses all.\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tif (!expanded) this.expandedRuns.clear();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tprivate isRunExpanded(runIndex: number): boolean {\n\t\treturn this.expanded || this.expandedRuns.has(runIndex);\n\t}\n\n\ttoggleThinkingRun(runIndex: number): void {\n\t\tif (this.expanded) {\n\t\t\tthis.expanded = false;\n\t\t\tthis.expandedRuns.clear();\n\t\t\tconst count = this.lastMessage ? collectThinkingRuns(this.lastMessage.content).length : 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tif (i !== runIndex) this.expandedRuns.add(i);\n\t\t\t}\n\t\t} else if (this.expandedRuns.has(runIndex)) {\n\t\t\tthis.expandedRuns.delete(runIndex);\n\t\t} else {\n\t\t\tthis.expandedRuns.add(runIndex);\n\t\t}\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\thandleClick(localY: number, width: number): boolean {\n\t\tconst contentWidth = width;\n\t\tlet y = 0;\n\t\tfor (const child of this.contentContainer.children) {\n\t\t\tconst h = child.render(contentWidth).length;\n\t\t\tif (localY >= y && localY < y + h) {\n\t\t\t\tif (typeof child.handleClick === \"function\") {\n\t\t\t\t\treturn child.handleClick(localY - y, contentWidth);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ty += h;\n\t\t}\n\t\treturn false;\n\t}\n\n\tsetThinkingTimings(timings: ThinkingRunTiming[] | undefined): void {\n\t\tthis.thinkingTimings = timings;\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) return lines;\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\n\t\treturn lines;\n\t}\n\n\tprivate thinkingSourceOptions(): { thinkingSource?: AssistantMessage } | undefined {\n\t\treturn this.thinkingSource ? { thinkingSource: this.thinkingSource } : undefined;\n\t}\n\n\tupdateContent(message: AssistantMessage, options?: { thinkingSource?: AssistantMessage }): void {\n\t\tthis.lastMessage = message;\n\t\tthis.thinkingSource = options?.thinkingSource;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst sourceMessage = this.thinkingSource ?? message;\n\t\tconst sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);\n\t\tconst hasVisibleContent =\n\t\t\tmessage.content.some(\n\t\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t\t) ||\n\t\t\t(!this.hideThinkingBlock && sourceThinkingRuns.some((run) => run.length > 0));\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet isFirstTextBlock = true;\n\t\tlet thinkingRunIndex = -1;\n\t\tlet renderedThinking = false;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\t// lunr: leading ● marks the first text block (never thinking blocks). It rides\n\t\t\t\t// the defaultTextStyle below, so it renders bright white like the body.\n\t\t\t\tconst trimmed = content.text.trim();\n\t\t\t\tconst text = isFirstTextBlock ? `● ${trimmed}` : trimmed;\n\t\t\t\tisFirstTextBlock = false;\n\t\t\t\t// lunr: theme-polish — assistant message body renders bright white via the\n\t\t\t\t// shared userMessageText token (same knob as user messages).\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(text, this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (body: string) => theme.fg(\"userMessageText\", body),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tthinkingRunIndex++;\n\t\t\t\tconst displayBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tdisplayBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tconst thinkingBlocks = sourceThinkingRuns[thinkingRunIndex] ?? displayBlocks;\n\t\t\t\tif (thinkingBlocks.length === 0 || this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\trenderedThinking = true;\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, i);\n\t\t\t}\n\t\t}\n\n\t\tif (!this.hideThinkingBlock && !renderedThinking && sourceThinkingRuns.length > 0) {\n\t\t\tthinkingRunIndex = 0;\n\t\t\tconst thinkingBlocks = sourceThinkingRuns[0] ?? [];\n\t\t\tif (thinkingBlocks.length > 0) {\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, -1);\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t\t\t),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), this.outputPad, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), this.outputPad, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderThinkingRun(\n\t\tthinkingRunIndex: number,\n\t\tthinkingBlocks: string[],\n\t\tdisplayMessage: AssistantMessage,\n\t\tdisplayIndex: number,\n\t): void {\n\t\tconst hasVisibleContentAfter =\n\t\t\tdisplayIndex >= 0\n\t\t\t\t? displayMessage.content\n\t\t\t\t\t\t.slice(displayIndex + 1)\n\t\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()))\n\t\t\t\t: displayMessage.content.some((c) => c.type === \"text\" && c.text.trim());\n\n\t\tconst runComplete = isThinkingRunComplete(\n\t\t\tfalse,\n\t\t\tthis.thinkingTimings?.[thinkingRunIndex],\n\t\t\tthis.thinkingTimings !== undefined,\n\t\t);\n\t\tconst wrap = (inner: Component): void => {\n\t\t\tthis.contentContainer.addChild(new ThinkingRunBlock(this, thinkingRunIndex, inner));\n\t\t};\n\t\tif (this.thinkingCollapse && !this.isRunExpanded(thinkingRunIndex) && runComplete) {\n\t\t\tconst timing = this.thinkingTimings?.[thinkingRunIndex];\n\t\t\tconst label =\n\t\t\t\ttiming?.end !== undefined\n\t\t\t\t\t? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`\n\t\t\t\t\t: \"✻ Thought\";\n\t\t\tconst block = new Container();\n\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(label)), this.outputPad, 0));\n\t\t\tconst snippet = thinkingSnippet(thinkingBlocks.join(\"\\n\\n\"));\n\t\t\tif (snippet) {\n\t\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(snippet)), this.outputPad + 2, 0));\n\t\t\t}\n\t\t\twrap(block);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!runComplete && !this.isRunExpanded(thinkingRunIndex)) {\n\t\t\twrap(\n\t\t\t\tnew ThinkingTailComponent(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\titalic: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\twrap(\n\t\t\tnew Markdown(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\titalic: true,\n\t\t\t}),\n\t\t);\n\t\tif (hasVisibleContentAfter) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n}\n\nclass ThinkingRunBlock extends Container {\n\tprivate readonly owner: AssistantMessageComponent;\n\tprivate readonly runIndex: number;\n\n\tconstructor(owner: AssistantMessageComponent, runIndex: number, inner: Component) {\n\t\tsuper();\n\t\tthis.owner = owner;\n\t\tthis.runIndex = runIndex;\n\t\tthis.addChild(inner);\n\t}\n\n\thandleClick(_localY: number, _width: number): boolean {\n\t\tthis.owner.toggleThinkingRun(this.runIndex);\n\t\treturn true;\n\t}\n}\n\nfunction collectThinkingRuns(content: AssistantMessage[\"content\"]): string[][] {\n\tconst runs: string[][] = [];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type !== \"thinking\") continue;\n\t\tconst blocks: string[] = [];\n\t\tfor (; i < content.length; i++) {\n\t\t\tconst thinkingContent = content[i];\n\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\tif (thinking) blocks.push(thinking);\n\t\t}\n\t\tif (blocks.length > 0) runs.push(blocks);\n\t}\n\treturn runs;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@ashx-j/lunr-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport {\n\tformatThoughtDuration,\n\tisThinkingRunComplete,\n\ttype ThinkingRunTiming,\n\tthinkingSnippet,\n} from \"./thinking-summary.ts\";\nimport { ThinkingTailComponent } from \"./thinking-tail.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tselectable = true;\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\t// lunr: collapsible reasoning — when true, a completed thinking run renders as\n\t// \"✻ Thought for Xs\" + its first sentence instead of the full block.\n\tprivate thinkingCollapse: boolean;\n\t// lunr: per-run expand. `expanded` still means \"all runs\" for setExpanded().\n\tprivate expanded = false;\n\tprivate expandedRuns = new Set<number>();\n\t// lunr: live per-run timings from interactive-mode; undefined = history message\n\t// (always treated as final, rendered without durations).\n\tprivate thinkingTimings?: ThinkingRunTiming[];\n\tprivate markdownTheme: MarkdownTheme;\n\t// lunr: kept for the deferred alternative hidden-thinking indicator (Phase 8 removed the label render).\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: retained for future use\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate thinkingSource?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tthinkingCollapse = false,\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingCollapse = thinkingCollapse;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).\n\tsetThinkingCollapse(collapse: boolean): void {\n\t\tthis.thinkingCollapse = collapse;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: Expandable hook; true expands every thinking run, false collapses all.\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tif (!expanded) this.expandedRuns.clear();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tprivate isRunExpanded(runIndex: number): boolean {\n\t\treturn this.expanded || this.expandedRuns.has(runIndex);\n\t}\n\n\ttoggleThinkingRun(runIndex: number): void {\n\t\tif (this.expanded) {\n\t\t\tthis.expanded = false;\n\t\t\tthis.expandedRuns.clear();\n\t\t\tconst count = this.lastMessage ? collectThinkingRuns(this.lastMessage.content).length : 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tif (i !== runIndex) this.expandedRuns.add(i);\n\t\t\t}\n\t\t} else if (this.expandedRuns.has(runIndex)) {\n\t\t\tthis.expandedRuns.delete(runIndex);\n\t\t} else {\n\t\t\tthis.expandedRuns.add(runIndex);\n\t\t}\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\thandleClick(localY: number, width: number): boolean {\n\t\tconst contentWidth = width;\n\t\tlet y = 0;\n\t\tfor (const child of this.contentContainer.children) {\n\t\t\tconst h = child.render(contentWidth).length;\n\t\t\tif (localY >= y && localY < y + h) {\n\t\t\t\tif (typeof child.handleClick === \"function\") {\n\t\t\t\t\treturn child.handleClick(localY - y, contentWidth);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ty += h;\n\t\t}\n\t\treturn false;\n\t}\n\n\tsetThinkingTimings(timings: ThinkingRunTiming[] | undefined): void {\n\t\tthis.thinkingTimings = timings;\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) return lines;\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\n\t\treturn lines;\n\t}\n\n\tprivate thinkingSourceOptions(): { thinkingSource?: AssistantMessage } | undefined {\n\t\treturn this.thinkingSource ? { thinkingSource: this.thinkingSource } : undefined;\n\t}\n\n\tupdateContent(message: AssistantMessage, options?: { thinkingSource?: AssistantMessage }): void {\n\t\tthis.lastMessage = message;\n\t\tthis.thinkingSource = options?.thinkingSource;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst sourceMessage = this.thinkingSource ?? message;\n\t\tconst sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) =>\n\t\t\t\t(c.type === \"text\" && c.text.trim()) ||\n\t\t\t\t(!this.hideThinkingBlock && c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet isFirstTextBlock = true;\n\t\tlet thinkingRunIndex = -1;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\t// lunr: leading ● marks the first text block (never thinking blocks). It rides\n\t\t\t\t// the defaultTextStyle below, so it renders bright white like the body.\n\t\t\t\tconst trimmed = content.text.trim();\n\t\t\t\tconst text = isFirstTextBlock ? `● ${trimmed}` : trimmed;\n\t\t\t\tisFirstTextBlock = false;\n\t\t\t\t// lunr: theme-polish — assistant message body renders bright white via the\n\t\t\t\t// shared userMessageText token (same knob as user messages).\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(text, this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (body: string) => theme.fg(\"userMessageText\", body),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tthinkingRunIndex++;\n\t\t\t\tconst displayBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tdisplayBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (displayBlocks.length === 0 || this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst sourceBlocks = sourceThinkingRuns[thinkingRunIndex] ?? [];\n\t\t\t\tconst fullyRevealed =\n\t\t\t\t\tdisplayBlocks.length === sourceBlocks.length &&\n\t\t\t\t\tdisplayBlocks.every((block, index) => block === sourceBlocks[index]);\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, displayBlocks, message, i, fullyRevealed);\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t\t\t),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), this.outputPad, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), this.outputPad, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderThinkingRun(\n\t\tthinkingRunIndex: number,\n\t\tthinkingBlocks: string[],\n\t\tdisplayMessage: AssistantMessage,\n\t\tdisplayIndex: number,\n\t\tfullyRevealed: boolean,\n\t): void {\n\t\tconst hasVisibleContentAfter =\n\t\t\tdisplayIndex >= 0\n\t\t\t\t? displayMessage.content\n\t\t\t\t\t\t.slice(displayIndex + 1)\n\t\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()))\n\t\t\t\t: displayMessage.content.some((c) => c.type === \"text\" && c.text.trim());\n\n\t\t// Provider completion can precede the reveal cursor; keep animating until it catches up.\n\t\tconst runComplete =\n\t\t\tfullyRevealed &&\n\t\t\tisThinkingRunComplete(false, this.thinkingTimings?.[thinkingRunIndex], this.thinkingTimings !== undefined);\n\t\tconst wrap = (inner: Component): void => {\n\t\t\tthis.contentContainer.addChild(new ThinkingRunBlock(this, thinkingRunIndex, inner));\n\t\t};\n\t\tif (this.thinkingCollapse && !this.isRunExpanded(thinkingRunIndex) && runComplete) {\n\t\t\tconst timing = this.thinkingTimings?.[thinkingRunIndex];\n\t\t\tconst label =\n\t\t\t\ttiming?.end !== undefined\n\t\t\t\t\t? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`\n\t\t\t\t\t: \"✻ Thought\";\n\t\t\tconst block = new Container();\n\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(label)), this.outputPad, 0));\n\t\t\tconst snippet = thinkingSnippet(thinkingBlocks.join(\"\\n\\n\"));\n\t\t\tif (snippet) {\n\t\t\t\tblock.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(snippet)), this.outputPad + 2, 0));\n\t\t\t}\n\t\t\twrap(block);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!runComplete && !this.isRunExpanded(thinkingRunIndex)) {\n\t\t\twrap(\n\t\t\t\tnew ThinkingTailComponent(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\titalic: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\twrap(\n\t\t\tnew Markdown(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\titalic: true,\n\t\t\t}),\n\t\t);\n\t\tif (hasVisibleContentAfter) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n}\n\nclass ThinkingRunBlock extends Container {\n\tprivate readonly owner: AssistantMessageComponent;\n\tprivate readonly runIndex: number;\n\n\tconstructor(owner: AssistantMessageComponent, runIndex: number, inner: Component) {\n\t\tsuper();\n\t\tthis.owner = owner;\n\t\tthis.runIndex = runIndex;\n\t\tthis.addChild(inner);\n\t}\n\n\thandleClick(_localY: number, _width: number): boolean {\n\t\tthis.owner.toggleThinkingRun(this.runIndex);\n\t\treturn true;\n\t}\n}\n\nfunction collectThinkingRuns(content: AssistantMessage[\"content\"]): string[][] {\n\tconst runs: string[][] = [];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type !== \"thinking\") continue;\n\t\tconst blocks: string[] = [];\n\t\tfor (; i < content.length; i++) {\n\t\t\tconst thinkingContent = content[i];\n\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\tif (thinking) blocks.push(thinking);\n\t\t}\n\t\truns.push(blocks);\n\t}\n\treturn runs;\n}\n"]}
|
|
@@ -6,11 +6,14 @@ import type { ReadonlyFooterDataProvider } from "../../../core/footer-data-provi
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function formatTokens(count: number): string;
|
|
8
8
|
export declare function formatCwdForFooter(cwd: string, home: string | undefined): string;
|
|
9
|
+
/**
|
|
10
|
+
* Footer component that shows pwd, token stats, and context usage.
|
|
11
|
+
* Computes token/context stats from session, gets git branch and extension statuses from provider.
|
|
12
|
+
*/
|
|
9
13
|
export declare class FooterComponent implements Component {
|
|
10
14
|
private autoCompactEnabled;
|
|
11
15
|
private session;
|
|
12
16
|
private footerData;
|
|
13
|
-
private usageCache;
|
|
14
17
|
constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider);
|
|
15
18
|
setSession(session: AgentSession): void;
|
|
16
19
|
setAutoCompactEnabled(enabled: boolean): void;
|
|
@@ -24,7 +27,6 @@ export declare class FooterComponent implements Component {
|
|
|
24
27
|
* Git watcher cleanup now handled by provider.
|
|
25
28
|
*/
|
|
26
29
|
dispose(): void;
|
|
27
|
-
private getUsageTotals;
|
|
28
30
|
render(width: number): string[];
|
|
29
31
|
}
|
|
30
32
|
//# sourceMappingURL=footer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAexF;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYhF;AAeD,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,UAAU,CAEL;IAEb,YAAY,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,0BAA0B,EAGxE;IAED,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAGtC;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,OAAO,CAAC,cAAc;IA+BtB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4I9B;CACD","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@ashx-j/lunr-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport { areExperimentalFeaturesEnabled } from \"../../../core/experimental.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts for compact footer display.\n */\nexport function formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\ntype UsageTotals = {\n\ttotalInput: number;\n\ttotalOutput: number;\n\ttotalCacheRead: number;\n\ttotalCacheWrite: number;\n\ttotalCost: number;\n\tlatestCacheHitRate: number | undefined;\n};\n\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate usageCache:\n\t\t| { manager: AgentSession[\"sessionManager\"]; leafId: string | null; totals: UsageTotals }\n\t\t| undefined;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t\tthis.usageCache = undefined;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\tprivate getUsageTotals(): UsageTotals {\n\t\tconst manager = this.session.sessionManager;\n\t\tconst leafId = manager.getLeafId();\n\t\tif (this.usageCache?.manager === manager && this.usageCache.leafId === leafId) {\n\t\t\treturn this.usageCache.totals;\n\t\t}\n\n\t\tconst totals: UsageTotals = {\n\t\t\ttotalInput: 0,\n\t\t\ttotalOutput: 0,\n\t\t\ttotalCacheRead: 0,\n\t\t\ttotalCacheWrite: 0,\n\t\t\ttotalCost: 0,\n\t\t\tlatestCacheHitRate: undefined,\n\t\t};\n\t\tfor (const entry of manager.getEntries()) {\n\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\t\ttotals.totalInput += entry.message.usage.input;\n\t\t\ttotals.totalOutput += entry.message.usage.output;\n\t\t\ttotals.totalCacheRead += entry.message.usage.cacheRead;\n\t\t\ttotals.totalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\ttotals.totalCost += entry.message.usage.cost.total;\n\t\t\tconst latestPromptTokens =\n\t\t\t\tentry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;\n\t\t\ttotals.latestCacheHitRate =\n\t\t\t\tlatestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;\n\t\t}\n\t\tthis.usageCache = { manager, leafId, totals };\n\t\treturn totals;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\t\tconst { totalInput, totalOutput, totalCacheRead, totalCacheWrite, totalCost, latestCacheHitRate } =\n\t\t\tthis.getUsageTotals();\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\t\tif ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) {\n\t\t\tstatsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);\n\t\t}\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRuntime.isUsingOAuth(state.model.provider) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\t\tif (areExperimentalFeaturesEnabled()) {\n\t\t\tstatsParts.push(`${theme.fg(\"dim\", \"•\")} ${theme.bold(theme.fg(\"warning\", \"xp\"))}`);\n\t\t}\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAexF;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYhF;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAA6B;IAE/C,YAAY,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,0BAA0B,EAGxE;IAED,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEtC;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAiK9B;CACD","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@ashx-j/lunr-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport { areExperimentalFeaturesEnabled } from \"../../../core/experimental.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts for compact footer display.\n */\nexport function formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\t\tlet latestCacheHitRate: number | undefined;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\n\t\t\t\tconst latestPromptTokens =\n\t\t\t\t\tentry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;\n\t\t\t\tlatestCacheHitRate =\n\t\t\t\t\tlatestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\t\tif ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) {\n\t\t\tstatsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);\n\t\t}\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRuntime.isUsingOAuth(state.model.provider) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\t\tif (areExperimentalFeaturesEnabled()) {\n\t\t\tstatsParts.push(`${theme.fg(\"dim\", \"•\")} ${theme.bold(theme.fg(\"warning\", \"xp\"))}`);\n\t\t}\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
|