@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.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 +78 -0
- package/README.md +1 -1
- package/dist/cli.js +4685 -4614
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/model-registry.d.ts +3 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +374 -47
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
- package/dist/types/live/attestation.d.ts +2 -0
- package/dist/types/live/controller.d.ts +48 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +35 -0
- package/dist/types/live/visualizer.d.ts +30 -0
- package/dist/types/lsp/client.d.ts +16 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -0
- package/dist/types/modes/components/custom-message.d.ts +1 -1
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/message-frame.d.ts +8 -4
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/session-account-selector.d.ts +11 -0
- package/dist/types/modes/components/settings-defs.d.ts +7 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +6 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +321 -0
- package/dist/types/session/agent-session.d.ts +135 -753
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +51 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +24 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +158 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/streaming-output.d.ts +8 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
- package/dist/types/stt/index.d.ts +0 -2
- package/dist/types/stt/stt-controller.d.ts +7 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/tiny/title-client.d.ts +10 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/builtin-names.d.ts +1 -1
- package/dist/types/tools/computer/protocol.d.ts +43 -0
- package/dist/types/tools/computer/supervisor.d.ts +32 -0
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +15 -0
- package/dist/types/tools/computer-renderer.d.ts +22 -0
- package/dist/types/tools/computer.d.ts +71 -0
- package/dist/types/tools/context.d.ts +2 -0
- package/dist/types/tools/default-renderer.d.ts +21 -0
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +7 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +10 -29
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/utils/tools-manager.d.ts +1 -2
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/types.d.ts +94 -0
- package/package.json +12 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +3 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/setup-cli.ts +2 -14
- package/src/cli/usage-cli.ts +10 -1
- package/src/cli.ts +8 -0
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/model-registry.ts +16 -2
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +270 -51
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/executor-base.ts +1 -0
- package/src/eval/js/executor.ts +2 -0
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +90 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/extensions/wrapper.ts +68 -12
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/attestation.ts +91 -0
- package/src/live/controller.ts +517 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +422 -0
- package/src/live/visualizer.ts +214 -0
- package/src/lsp/client.ts +29 -9
- package/src/lsp/index.ts +19 -4
- package/src/main.ts +8 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +1 -1
- package/src/modes/components/assistant-message.ts +7 -2
- package/src/modes/components/custom-message.ts +4 -1
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/message-frame.ts +14 -8
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/session-account-selector.ts +62 -0
- package/src/modes/components/settings-defs.ts +23 -1
- package/src/modes/components/settings-selector.ts +169 -1
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.ts +28 -112
- package/src/modes/controllers/command-controller.ts +56 -15
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/input-controller.ts +47 -39
- package/src/modes/controllers/live-command-controller.ts +255 -0
- package/src/modes/controllers/mcp-command-controller.ts +10 -1
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +80 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +176 -38
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/system/computer-safety.md +14 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/computer.md +26 -0
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +215 -136
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +343 -0
- package/src/session/agent-session.ts +1940 -12218
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/auth-storage.ts +1 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +217 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +266 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.ts +6 -3
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +0 -46
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +980 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/streaming-output.ts +52 -5
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +257 -20
- package/src/slash-commands/helpers/session-pin.ts +44 -0
- package/src/slash-commands/helpers/usage-report.ts +22 -3
- package/src/stt/downloader.ts +0 -2
- package/src/stt/index.ts +0 -2
- package/src/stt/stt-controller.ts +57 -146
- package/src/system-prompt.ts +45 -17
- package/src/task/executor.ts +285 -70
- package/src/task/index.ts +98 -105
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +1 -0
- package/src/tiny/title-client.ts +22 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +116 -86
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/builtin-names.ts +1 -0
- package/src/tools/computer/protocol.ts +28 -0
- package/src/tools/computer/supervisor.ts +258 -0
- package/src/tools/computer/worker-entry.ts +25 -0
- package/src/tools/computer/worker.ts +135 -0
- package/src/tools/computer-renderer.ts +108 -0
- package/src/tools/computer.ts +433 -0
- package/src/tools/context.ts +2 -0
- package/src/tools/default-renderer.ts +139 -0
- package/src/tools/essential-tools.ts +1 -0
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +14 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +9 -1
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +121 -29
- package/src/tts/streaming-player.ts +82 -283
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/clipboard.ts +1 -30
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/mac-file-urls.applescript +37 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/utils/tool-choice.ts +14 -0
- package/src/utils/tools-manager.ts +1 -19
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +3 -0
- package/dist/types/stt/recorder.d.ts +0 -30
- package/dist/types/stt/transcriber.d.ts +0 -14
- package/dist/types/stt/wav.d.ts +0 -29
- package/dist/types/tts/player.d.ts +0 -32
- package/src/stt/recorder.ts +0 -551
- package/src/stt/transcriber.ts +0 -60
- package/src/stt/wav.ts +0 -173
- package/src/tts/player.ts +0 -137
|
@@ -834,45 +834,6 @@ export class InputController {
|
|
|
834
834
|
// First, move any pending bash components to chat
|
|
835
835
|
this.ctx.flushPendingBashComponents();
|
|
836
836
|
|
|
837
|
-
// AgentSession.prompt() consumes registered extension commands locally.
|
|
838
|
-
// Classify them here because title generation starts before prompt dispatch.
|
|
839
|
-
const extensionCommandSpace = text.indexOf(" ");
|
|
840
|
-
const isLocalExtensionCommand =
|
|
841
|
-
text.startsWith("/") &&
|
|
842
|
-
runner?.getCommand(extensionCommandSpace === -1 ? text.slice(1) : text.slice(1, extensionCommandSpace)) !==
|
|
843
|
-
undefined;
|
|
844
|
-
|
|
845
|
-
// Auto-generate a session title while the session is still unnamed.
|
|
846
|
-
// Greetings / acknowledgements / empty input carry no task, so they are
|
|
847
|
-
// skipped deterministically (no model invoked, no download-progress UI)
|
|
848
|
-
// and the session stays unnamed — the next user message gets a fresh
|
|
849
|
-
// chance, so titling defers past "hi" instead of latching onto it.
|
|
850
|
-
if (
|
|
851
|
-
!isLocalExtensionCommand &&
|
|
852
|
-
!this.ctx.sessionManager.getSessionName() &&
|
|
853
|
-
!$env.PI_NO_TITLE &&
|
|
854
|
-
!isLowSignalTitleInput(text)
|
|
855
|
-
) {
|
|
856
|
-
this.#showTinyTitleDownloadProgress(this.ctx.settings.get("providers.tinyModel"));
|
|
857
|
-
this.ctx.session
|
|
858
|
-
.generateTitle(text)
|
|
859
|
-
.then(async title => {
|
|
860
|
-
// Re-check: a concurrent attempt for an earlier message may have
|
|
861
|
-
// already named the session. Don't clobber it. Terminal title and
|
|
862
|
-
// accent updates fire from the onSessionNameChanged listener.
|
|
863
|
-
if (title && !this.ctx.sessionManager.getSessionName()) {
|
|
864
|
-
await this.ctx.sessionManager.setSessionName(title, "auto");
|
|
865
|
-
}
|
|
866
|
-
})
|
|
867
|
-
.catch(err => {
|
|
868
|
-
logger.warn("title-generator: uncaught auto-title error", {
|
|
869
|
-
sessionId: this.ctx.session.sessionId,
|
|
870
|
-
reason: "uncaught-auto-title-error",
|
|
871
|
-
error: err instanceof Error ? err.message : String(err),
|
|
872
|
-
});
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
|
|
876
837
|
if (this.ctx.onInputCallback) {
|
|
877
838
|
// Include any pending images from clipboard paste
|
|
878
839
|
this.ctx.editor.imageLinks = undefined;
|
|
@@ -892,6 +853,9 @@ export class InputController {
|
|
|
892
853
|
imageLinks: inputImageLinks,
|
|
893
854
|
streamingBehavior: "steer",
|
|
894
855
|
});
|
|
856
|
+
// Start titling only after the optimistic row painted, so the local
|
|
857
|
+
// tiny-title worker's subprocess spawn never blocks the first frame.
|
|
858
|
+
this.#maybeStartTitleGeneration(text);
|
|
895
859
|
|
|
896
860
|
this.ctx.onInputCallback(submission);
|
|
897
861
|
} else {
|
|
@@ -906,6 +870,7 @@ export class InputController {
|
|
|
906
870
|
const images = inputImages && inputImages.length > 0 ? [...inputImages] : undefined;
|
|
907
871
|
this.ctx.editor.pendingImages = [];
|
|
908
872
|
this.ctx.editor.pendingImageLinks = [];
|
|
873
|
+
this.#maybeStartTitleGeneration(text);
|
|
909
874
|
try {
|
|
910
875
|
await this.ctx.withLocalSubmission(
|
|
911
876
|
text,
|
|
@@ -935,6 +900,49 @@ export class InputController {
|
|
|
935
900
|
};
|
|
936
901
|
}
|
|
937
902
|
|
|
903
|
+
/**
|
|
904
|
+
* Kick off session-title generation while the session is still unnamed.
|
|
905
|
+
* Invoked AFTER the optimistic user row is painted so the local tiny-title
|
|
906
|
+
* worker's subprocess spawn never lands ahead of the first frame (issue #6462).
|
|
907
|
+
* Skips slash extension commands (consumed locally by AgentSession.prompt()),
|
|
908
|
+
* already-named sessions, PI_NO_TITLE, and low-signal greetings — no model or
|
|
909
|
+
* download UI for those.
|
|
910
|
+
*/
|
|
911
|
+
#maybeStartTitleGeneration(text: string): void {
|
|
912
|
+
const runner = this.ctx.session.extensionRunner;
|
|
913
|
+
const extensionCommandSpace = text.indexOf(" ");
|
|
914
|
+
const isLocalExtensionCommand =
|
|
915
|
+
text.startsWith("/") &&
|
|
916
|
+
runner?.getCommand(extensionCommandSpace === -1 ? text.slice(1) : text.slice(1, extensionCommandSpace)) !==
|
|
917
|
+
undefined;
|
|
918
|
+
if (
|
|
919
|
+
isLocalExtensionCommand ||
|
|
920
|
+
this.ctx.sessionManager.getSessionName() ||
|
|
921
|
+
$env.PI_NO_TITLE ||
|
|
922
|
+
isLowSignalTitleInput(text)
|
|
923
|
+
) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
this.#showTinyTitleDownloadProgress(this.ctx.settings.get("providers.tinyModel"));
|
|
927
|
+
this.ctx.session
|
|
928
|
+
.generateTitle(text)
|
|
929
|
+
.then(async title => {
|
|
930
|
+
// Re-check: a concurrent attempt for an earlier message may have
|
|
931
|
+
// already named the session. Don't clobber it. Terminal title and
|
|
932
|
+
// accent updates fire from the onSessionNameChanged listener.
|
|
933
|
+
if (title && !this.ctx.sessionManager.getSessionName()) {
|
|
934
|
+
await this.ctx.sessionManager.setSessionName(title, "auto");
|
|
935
|
+
}
|
|
936
|
+
})
|
|
937
|
+
.catch(err => {
|
|
938
|
+
logger.warn("title-generator: uncaught auto-title error", {
|
|
939
|
+
sessionId: this.ctx.session.sessionId,
|
|
940
|
+
reason: "uncaught-auto-title-error",
|
|
941
|
+
error: err instanceof Error ? err.message : String(err),
|
|
942
|
+
});
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
938
946
|
/** Submit editor text to the focused subagent session (chat-only focus policy). */
|
|
939
947
|
async #submitToFocusedSession(text: string, streamingBehavior: "steer" | "followUp"): Promise<void> {
|
|
940
948
|
const target = this.ctx.viewSession;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import { LiveSessionController, type LiveTranscript } from "../../live/controller";
|
|
4
|
+
import { LIVE_MODEL } from "../../live/protocol";
|
|
5
|
+
import { LiveVisualizer } from "../../live/visualizer";
|
|
6
|
+
import { vocalizer } from "../../tts/vocalizer";
|
|
7
|
+
import type { AssistantMessageComponent } from "../components/assistant-message";
|
|
8
|
+
import type { CustomEditor } from "../components/custom-editor";
|
|
9
|
+
import { theme } from "../theme/theme";
|
|
10
|
+
import type { InteractiveModeContext } from "../types";
|
|
11
|
+
import { createAssistantMessageComponent } from "../utils/interactive-context-helpers";
|
|
12
|
+
|
|
13
|
+
const ANIMATION_INTERVAL_MS = 80;
|
|
14
|
+
|
|
15
|
+
const LIVE_MESSAGE_USAGE: AssistantMessage["usage"] = {
|
|
16
|
+
input: 0,
|
|
17
|
+
output: 0,
|
|
18
|
+
cacheRead: 0,
|
|
19
|
+
cacheWrite: 0,
|
|
20
|
+
totalTokens: 0,
|
|
21
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
22
|
+
};
|
|
23
|
+
function errorFrom(cause: unknown): Error {
|
|
24
|
+
return cause instanceof Error ? cause : new Error(String(cause));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Owns the editor-replacing visualizer and realtime session lifecycle for `/live`. */
|
|
28
|
+
export class LiveCommandController {
|
|
29
|
+
readonly #ctx: InteractiveModeContext;
|
|
30
|
+
|
|
31
|
+
#session: LiveSessionController | undefined;
|
|
32
|
+
#settling: Promise<void> | undefined;
|
|
33
|
+
#visualizer: LiveVisualizer | undefined;
|
|
34
|
+
#detachedEditor: CustomEditor | undefined;
|
|
35
|
+
#animationInterval: NodeJS.Timeout | undefined;
|
|
36
|
+
#previousShowHardwareCursor: boolean | undefined;
|
|
37
|
+
#previousUseTerminalCursor: boolean | undefined;
|
|
38
|
+
#resumeVocalizer: (() => void) | undefined;
|
|
39
|
+
#assistantTranscriptComponent: AssistantMessageComponent | undefined;
|
|
40
|
+
#assistantTranscriptTurn = 0;
|
|
41
|
+
#assistantTranscriptStartedAt = 0;
|
|
42
|
+
|
|
43
|
+
constructor(ctx: InteractiveModeContext) {
|
|
44
|
+
this.#ctx = ctx;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Whether a live session is connected, connecting, or closing. */
|
|
48
|
+
get active(): boolean {
|
|
49
|
+
return this.#session !== undefined || this.#settling !== undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Start live mode, or stop the currently active session. */
|
|
53
|
+
async handleCommand(): Promise<void> {
|
|
54
|
+
if (this.#session) {
|
|
55
|
+
await this.stop();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this.#settling) await this.#settling;
|
|
59
|
+
await this.#start();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Stop the active live session and restore the editor. */
|
|
63
|
+
async stop(): Promise<void> {
|
|
64
|
+
const session = this.#session;
|
|
65
|
+
if (!session) {
|
|
66
|
+
if (this.#settling) await this.#settling;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
await session.stop();
|
|
71
|
+
} catch (cause) {
|
|
72
|
+
this.#finish(session, errorFrom(cause));
|
|
73
|
+
} finally {
|
|
74
|
+
this.#finish(session);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Release UI resources during synchronous InteractiveMode teardown. */
|
|
79
|
+
dispose(): void {
|
|
80
|
+
const session = this.#session;
|
|
81
|
+
if (session) {
|
|
82
|
+
this.#finish(session);
|
|
83
|
+
void session.stop().catch(cause => {
|
|
84
|
+
logger.debug("Live session teardown failed", { error: errorFrom(cause).message });
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
this.#restoreEditor();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async #start(): Promise<void> {
|
|
92
|
+
this.#assistantTranscriptTurn = 0;
|
|
93
|
+
this.#assistantTranscriptStartedAt = 0;
|
|
94
|
+
const visualizer = new LiveVisualizer({
|
|
95
|
+
onStop: () => {
|
|
96
|
+
void this.stop().catch(cause => this.#ctx.showError(errorFrom(cause).message));
|
|
97
|
+
},
|
|
98
|
+
onToggleMute: () => this.#session?.toggleMute(),
|
|
99
|
+
});
|
|
100
|
+
this.#mountVisualizer(visualizer);
|
|
101
|
+
|
|
102
|
+
let session: LiveSessionController;
|
|
103
|
+
session = new LiveSessionController({
|
|
104
|
+
session: this.#ctx.session,
|
|
105
|
+
extractAssistantText: message => this.#ctx.extractAssistantText(message),
|
|
106
|
+
callbacks: {
|
|
107
|
+
onPhase: phase => {
|
|
108
|
+
if (this.#visualizer !== visualizer) return;
|
|
109
|
+
visualizer.setPhase(phase);
|
|
110
|
+
this.#ctx.ui.requestComponentRender(visualizer);
|
|
111
|
+
},
|
|
112
|
+
onLevels: input => {
|
|
113
|
+
if (this.#visualizer !== visualizer) return;
|
|
114
|
+
visualizer.setInputLevel(input);
|
|
115
|
+
this.#ctx.ui.requestComponentRender(visualizer);
|
|
116
|
+
},
|
|
117
|
+
onTranscript: transcript => {
|
|
118
|
+
if (this.#visualizer !== visualizer) return;
|
|
119
|
+
if (!transcript) {
|
|
120
|
+
visualizer.clearTranscript();
|
|
121
|
+
this.#ctx.ui.requestComponentRender(visualizer);
|
|
122
|
+
} else if (transcript.role === "user") {
|
|
123
|
+
visualizer.setTranscript(transcript.text);
|
|
124
|
+
this.#ctx.ui.requestComponentRender(visualizer);
|
|
125
|
+
} else {
|
|
126
|
+
this.#presentAssistantTranscript(transcript);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
onTerminal: error => this.#finish(session, error),
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
this.#session = session;
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await session.start();
|
|
136
|
+
} catch (cause) {
|
|
137
|
+
if (this.#session === session) {
|
|
138
|
+
await session.stop();
|
|
139
|
+
this.#finish(session, errorFrom(cause));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#presentAssistantTranscript(transcript: LiveTranscript): void {
|
|
145
|
+
if (
|
|
146
|
+
transcript.turn < this.#assistantTranscriptTurn ||
|
|
147
|
+
(transcript.turn === this.#assistantTranscriptTurn && !this.#assistantTranscriptComponent)
|
|
148
|
+
) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (transcript.turn > this.#assistantTranscriptTurn) {
|
|
152
|
+
this.#finalizeAssistantTranscript();
|
|
153
|
+
this.#assistantTranscriptTurn = transcript.turn;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let component = this.#assistantTranscriptComponent;
|
|
157
|
+
if (!component) {
|
|
158
|
+
component = createAssistantMessageComponent(this.#ctx);
|
|
159
|
+
component.setTextColorTransform(text => theme.fg("borderAccent", text));
|
|
160
|
+
this.#assistantTranscriptComponent = component;
|
|
161
|
+
this.#assistantTranscriptStartedAt = Date.now();
|
|
162
|
+
}
|
|
163
|
+
const message: AssistantMessage = {
|
|
164
|
+
role: "assistant",
|
|
165
|
+
content: [{ type: "text", text: transcript.text }],
|
|
166
|
+
api: "openai-codex-responses",
|
|
167
|
+
provider: "openai-codex",
|
|
168
|
+
model: LIVE_MODEL,
|
|
169
|
+
usage: { ...LIVE_MESSAGE_USAGE },
|
|
170
|
+
stopReason: "stop",
|
|
171
|
+
timestamp: this.#assistantTranscriptStartedAt,
|
|
172
|
+
};
|
|
173
|
+
component.updateContent(message, { transient: !transcript.final });
|
|
174
|
+
if (transcript.final) {
|
|
175
|
+
component.markTranscriptBlockFinalized();
|
|
176
|
+
this.#assistantTranscriptComponent = undefined;
|
|
177
|
+
this.#assistantTranscriptStartedAt = 0;
|
|
178
|
+
}
|
|
179
|
+
if (!this.#ctx.chatContainer.children.includes(component)) {
|
|
180
|
+
this.#ctx.present(component);
|
|
181
|
+
} else {
|
|
182
|
+
this.#ctx.ui.requestComponentRender(component);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#finalizeAssistantTranscript(): void {
|
|
187
|
+
const component = this.#assistantTranscriptComponent;
|
|
188
|
+
if (!component) return;
|
|
189
|
+
component.markTranscriptBlockFinalized();
|
|
190
|
+
this.#assistantTranscriptComponent = undefined;
|
|
191
|
+
this.#assistantTranscriptStartedAt = 0;
|
|
192
|
+
this.#ctx.ui.requestComponentRender(component);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#mountVisualizer(visualizer: LiveVisualizer): void {
|
|
196
|
+
this.#visualizer = visualizer;
|
|
197
|
+
this.#detachedEditor = this.#ctx.editor;
|
|
198
|
+
this.#previousShowHardwareCursor = this.#ctx.ui.getShowHardwareCursor();
|
|
199
|
+
this.#previousUseTerminalCursor = this.#ctx.editor.getUseTerminalCursor();
|
|
200
|
+
this.#ctx.ui.setShowHardwareCursor(false);
|
|
201
|
+
this.#ctx.editor.setUseTerminalCursor(false);
|
|
202
|
+
this.#ctx.editorContainer.clear();
|
|
203
|
+
this.#ctx.editorContainer.addChild(visualizer);
|
|
204
|
+
this.#ctx.ui.setFocus(visualizer);
|
|
205
|
+
this.#resumeVocalizer = vocalizer.suspend();
|
|
206
|
+
let frame = 0;
|
|
207
|
+
this.#animationInterval = setInterval(() => {
|
|
208
|
+
if (this.#visualizer !== visualizer) return;
|
|
209
|
+
frame += 1;
|
|
210
|
+
visualizer.setFrame(frame);
|
|
211
|
+
this.#ctx.ui.requestComponentRender(visualizer);
|
|
212
|
+
}, ANIMATION_INTERVAL_MS);
|
|
213
|
+
this.#ctx.ui.requestRender();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#finish(session: LiveSessionController, error?: Error): void {
|
|
217
|
+
if (this.#session !== session) return;
|
|
218
|
+
this.#session = undefined;
|
|
219
|
+
this.#restoreEditor();
|
|
220
|
+
if (error) this.#ctx.showError(error.message);
|
|
221
|
+
const settling = session.stop().catch(cause => {
|
|
222
|
+
logger.debug("Live session cleanup failed", { error: errorFrom(cause).message });
|
|
223
|
+
});
|
|
224
|
+
this.#settling = settling;
|
|
225
|
+
void settling.finally(() => {
|
|
226
|
+
if (this.#settling === settling) this.#settling = undefined;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
#restoreEditor(): void {
|
|
231
|
+
this.#finalizeAssistantTranscript();
|
|
232
|
+
if (this.#animationInterval) {
|
|
233
|
+
clearInterval(this.#animationInterval);
|
|
234
|
+
this.#animationInterval = undefined;
|
|
235
|
+
}
|
|
236
|
+
this.#resumeVocalizer?.();
|
|
237
|
+
this.#resumeVocalizer = undefined;
|
|
238
|
+
const editor = this.#detachedEditor;
|
|
239
|
+
this.#detachedEditor = undefined;
|
|
240
|
+
this.#visualizer = undefined;
|
|
241
|
+
if (!editor) return;
|
|
242
|
+
this.#ctx.editorContainer.clear();
|
|
243
|
+
this.#ctx.editorContainer.addChild(editor);
|
|
244
|
+
if (this.#previousShowHardwareCursor !== undefined) {
|
|
245
|
+
this.#ctx.ui.setShowHardwareCursor(this.#previousShowHardwareCursor);
|
|
246
|
+
}
|
|
247
|
+
if (this.#previousUseTerminalCursor !== undefined) {
|
|
248
|
+
editor.setUseTerminalCursor(this.#previousUseTerminalCursor);
|
|
249
|
+
}
|
|
250
|
+
this.#previousShowHardwareCursor = undefined;
|
|
251
|
+
this.#previousUseTerminalCursor = undefined;
|
|
252
|
+
this.#ctx.ui.setFocus(editor);
|
|
253
|
+
this.#ctx.ui.requestRender();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
getSmitheryApiKey,
|
|
39
39
|
getSmitheryLoginUrl,
|
|
40
40
|
pollSmitheryCliAuthSession,
|
|
41
|
+
type SmitheryCliPollResponse,
|
|
41
42
|
saveSmitheryApiKey,
|
|
42
43
|
} from "../../mcp/smithery-auth";
|
|
43
44
|
import { SmitheryConnectError } from "../../mcp/smithery-connect";
|
|
@@ -51,6 +52,7 @@ import type { MCPAuthChallenge, MCPAuthConfig, MCPServerConfig, MCPServerConnect
|
|
|
51
52
|
import { shortenPath } from "../../tools/render-utils";
|
|
52
53
|
import { urlHyperlinkAlways } from "../../tui";
|
|
53
54
|
import { copyToClipboard } from "../../utils/clipboard";
|
|
55
|
+
import { isTimeoutError } from "../../utils/fetch-timeout";
|
|
54
56
|
import { openPath } from "../../utils/open";
|
|
55
57
|
import { ChatBlock } from "../components/chat-block";
|
|
56
58
|
import { MCPAddWizard } from "../components/mcp-add-wizard";
|
|
@@ -2121,7 +2123,14 @@ export class MCPCommandController {
|
|
|
2121
2123
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
2122
2124
|
throw new Error("Smithery authorization timed out after 5 minutes.");
|
|
2123
2125
|
}
|
|
2124
|
-
|
|
2126
|
+
let response: SmitheryCliPollResponse;
|
|
2127
|
+
try {
|
|
2128
|
+
response = await pollSmitheryCliAuthSession(sessionId, signal);
|
|
2129
|
+
} catch (error) {
|
|
2130
|
+
// A single hung/slow poll aborts with TimeoutError; retry until the deadline.
|
|
2131
|
+
if (isTimeoutError(error)) continue;
|
|
2132
|
+
throw error;
|
|
2133
|
+
}
|
|
2125
2134
|
if (response.status === "success" && response.apiKey) {
|
|
2126
2135
|
return response.apiKey;
|
|
2127
2136
|
}
|
|
@@ -192,56 +192,60 @@ export class OmfgController {
|
|
|
192
192
|
|
|
193
193
|
async #saveCandidate(request: OmfgRequest, candidate: OmfgCandidate): Promise<SaveCandidateResult> {
|
|
194
194
|
if (this.#shouldStop(request)) return { kind: "aborted" };
|
|
195
|
-
request.component.setStatus("saving", "Choose where to save or amend the TTSR rule…");
|
|
196
|
-
const location = await this.ctx.showHookSelector("Save TTSR rule where?", [
|
|
197
|
-
PROJECT_OPTION,
|
|
198
|
-
GLOBAL_OPTION,
|
|
199
|
-
AMEND_OPTION,
|
|
200
|
-
]);
|
|
201
|
-
if (!this.#isActiveRequest(request)) return { kind: "aborted" };
|
|
202
|
-
if (!location) {
|
|
203
|
-
request.component.markAborted();
|
|
204
|
-
this.#closeActiveRequest({ abort: false });
|
|
205
|
-
return { kind: "aborted" };
|
|
206
|
-
}
|
|
207
195
|
|
|
208
|
-
|
|
209
|
-
request.component.setStatus("
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
196
|
+
for (;;) {
|
|
197
|
+
request.component.setStatus("saving", "Choose where to save or amend the TTSR rule…");
|
|
198
|
+
const location = await this.ctx.showHookSelector("Save TTSR rule where?", [
|
|
199
|
+
PROJECT_OPTION,
|
|
200
|
+
GLOBAL_OPTION,
|
|
201
|
+
AMEND_OPTION,
|
|
202
|
+
]);
|
|
214
203
|
if (!this.#isActiveRequest(request)) return { kind: "aborted" };
|
|
215
|
-
|
|
216
|
-
if (!feedback) {
|
|
204
|
+
if (!location) {
|
|
217
205
|
request.component.markAborted();
|
|
218
206
|
this.#closeActiveRequest({ abort: false });
|
|
219
207
|
return { kind: "aborted" };
|
|
220
208
|
}
|
|
221
|
-
return { kind: "amend", feedback };
|
|
222
|
-
}
|
|
223
209
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return { kind: "
|
|
210
|
+
if (location === AMEND_OPTION) {
|
|
211
|
+
request.component.setStatus("confirming", "Describe how to amend the rule…");
|
|
212
|
+
const amendment = await this.ctx.showHookInput(
|
|
213
|
+
"Amend TTSR rule",
|
|
214
|
+
"e.g. Make it specific to Ruby string eval in tool:write(*.rb)",
|
|
215
|
+
);
|
|
216
|
+
if (!this.#isActiveRequest(request)) return { kind: "aborted" };
|
|
217
|
+
const feedback = amendment?.trim();
|
|
218
|
+
if (!feedback) continue;
|
|
219
|
+
return { kind: "amend", feedback };
|
|
234
220
|
}
|
|
235
|
-
}
|
|
236
221
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
222
|
+
const target = this.#resolveTarget(location, candidate.rule.name);
|
|
223
|
+
if (await Bun.file(target.filePath).exists()) {
|
|
224
|
+
const shouldOverwrite = await this.ctx.showHookConfirm(
|
|
225
|
+
"Overwrite TTSR rule?",
|
|
226
|
+
`${shortenPath(target.filePath)} already exists. Overwrite it?`,
|
|
227
|
+
);
|
|
228
|
+
if (!this.#isActiveRequest(request)) return { kind: "aborted" };
|
|
229
|
+
if (!shouldOverwrite) {
|
|
230
|
+
request.component.markRejected();
|
|
231
|
+
return { kind: "rejected" };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
240
234
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
235
|
+
request.component.setStatus("saving", `Saving ${candidate.rule.name}…`);
|
|
236
|
+
await Bun.write(target.filePath, candidate.fileContent);
|
|
237
|
+
if (!this.#isActiveRequest(request)) return { kind: "aborted" };
|
|
238
|
+
|
|
239
|
+
const savedRule = buildOmfgRuleForPath(
|
|
240
|
+
candidate.rule.name,
|
|
241
|
+
candidate.fileContent,
|
|
242
|
+
target.filePath,
|
|
243
|
+
target.level,
|
|
244
|
+
);
|
|
245
|
+
this.#registerLive(savedRule);
|
|
246
|
+
request.component.markSaved(shortenPath(target.filePath));
|
|
247
|
+
return { kind: "saved" };
|
|
248
|
+
}
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
#resolveTarget(location: string, ruleName: string): { filePath: string; level: OmfgRuleSourceLevel } {
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
theme,
|
|
41
41
|
} from "../../modes/theme/theme";
|
|
42
42
|
import type { InteractiveModeContext } from "../../modes/types";
|
|
43
|
+
import type { SessionOAuthAccountList } from "../../session/agent-session-types";
|
|
43
44
|
import type { ResetCreditAccountStatus, ResetCreditRedeemOutcome } from "../../session/auth-storage";
|
|
44
45
|
import type { SessionInfo } from "../../session/session-listing";
|
|
45
46
|
import { SessionManager } from "../../session/session-manager";
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
type ResetUsageAccount,
|
|
51
52
|
toResetUsageAccounts,
|
|
52
53
|
} from "../../slash-commands/helpers/reset-usage";
|
|
54
|
+
import { toSessionPinAccounts } from "../../slash-commands/helpers/session-pin";
|
|
53
55
|
import {
|
|
54
56
|
AUTO_THINKING,
|
|
55
57
|
type ConfiguredThinkingLevel,
|
|
@@ -57,12 +59,10 @@ import {
|
|
|
57
59
|
parseConfiguredThinkingLevel,
|
|
58
60
|
} from "../../thinking";
|
|
59
61
|
import {
|
|
60
|
-
isImageProviderPreference,
|
|
61
62
|
isSearchProviderId,
|
|
62
|
-
isSearchProviderPreference,
|
|
63
63
|
setExcludedSearchProviders,
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
setImageProviderOrder,
|
|
65
|
+
setSearchProviderOrder,
|
|
66
66
|
type ToolSession,
|
|
67
67
|
} from "../../tools";
|
|
68
68
|
import { AskTool, type AskToolDetails, type AskToolInput } from "../../tools/ask";
|
|
@@ -86,6 +86,7 @@ import { OAuthSelectorComponent } from "../components/oauth-selector";
|
|
|
86
86
|
import { PluginSelectorComponent } from "../components/plugin-selector";
|
|
87
87
|
import { ResetUsageSelectorComponent } from "../components/reset-usage-selector";
|
|
88
88
|
import { renderSegmentTrack } from "../components/segment-track";
|
|
89
|
+
import { SessionAccountSelectorComponent } from "../components/session-account-selector";
|
|
89
90
|
import { SessionSelectorComponent } from "../components/session-selector";
|
|
90
91
|
import { SettingsSelectorComponent } from "../components/settings-selector";
|
|
91
92
|
import { ToolExecutionComponent } from "../components/tool-execution";
|
|
@@ -439,6 +440,16 @@ export class SelectorController {
|
|
|
439
440
|
this.ctx.showError(`Failed to apply personality: ${err}`);
|
|
440
441
|
});
|
|
441
442
|
break;
|
|
443
|
+
case "tools.xdevDocs":
|
|
444
|
+
void this.ctx.session.refreshBaseSystemPrompt().catch(err => {
|
|
445
|
+
this.ctx.showError(`Failed to apply xd:// prompt docs setting: ${err}`);
|
|
446
|
+
});
|
|
447
|
+
break;
|
|
448
|
+
case "memory.backend":
|
|
449
|
+
void this.ctx.session.applyMemoryBackend().catch(err => {
|
|
450
|
+
this.ctx.showError(`Failed to apply memory backend: ${err}`);
|
|
451
|
+
});
|
|
452
|
+
break;
|
|
442
453
|
|
|
443
454
|
case "autocompleteMaxVisible":
|
|
444
455
|
this.ctx.editor.setAutocompleteMaxVisible(typeof value === "number" ? value : Number(value));
|
|
@@ -609,9 +620,9 @@ export class SelectorController {
|
|
|
609
620
|
}
|
|
610
621
|
|
|
611
622
|
// Provider settings - update runtime preferences
|
|
612
|
-
case "providers.
|
|
613
|
-
if (
|
|
614
|
-
|
|
623
|
+
case "providers.webSearchOrder":
|
|
624
|
+
if (Array.isArray(value)) {
|
|
625
|
+
setSearchProviderOrder(value.filter(isSearchProviderId));
|
|
615
626
|
}
|
|
616
627
|
break;
|
|
617
628
|
case "providers.webSearchExclude":
|
|
@@ -619,9 +630,9 @@ export class SelectorController {
|
|
|
619
630
|
setExcludedSearchProviders(value.filter(isSearchProviderId));
|
|
620
631
|
}
|
|
621
632
|
break;
|
|
622
|
-
case "providers.
|
|
623
|
-
if (
|
|
624
|
-
|
|
633
|
+
case "providers.imageOrder":
|
|
634
|
+
if (Array.isArray(value)) {
|
|
635
|
+
setImageProviderOrder(value.filter((entry): entry is string => typeof entry === "string"));
|
|
625
636
|
}
|
|
626
637
|
break;
|
|
627
638
|
|
|
@@ -1735,6 +1746,65 @@ export class SelectorController {
|
|
|
1735
1746
|
});
|
|
1736
1747
|
}
|
|
1737
1748
|
|
|
1749
|
+
async showSessionPinSelector(): Promise<void> {
|
|
1750
|
+
const session = this.ctx.session;
|
|
1751
|
+
if (session.isStreaming) {
|
|
1752
|
+
this.ctx.showStatus("Cannot pin an account while the session is streaming.");
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
this.ctx.showStatus("Loading provider accounts…", { dim: true });
|
|
1756
|
+
let accountList: SessionOAuthAccountList | undefined;
|
|
1757
|
+
try {
|
|
1758
|
+
accountList = await session.listCurrentProviderOAuthAccounts();
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
this.ctx.showError(
|
|
1761
|
+
`Could not load provider accounts: ${error instanceof Error ? error.message : String(error)}`,
|
|
1762
|
+
);
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
if (!accountList) {
|
|
1766
|
+
this.ctx.showStatus("Select a model before pinning a provider account.");
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
const provider = getOAuthProviders().find(candidate => candidate.id === accountList.provider);
|
|
1770
|
+
const providerName = provider?.name ?? accountList.provider;
|
|
1771
|
+
const accounts = toSessionPinAccounts(accountList.accounts);
|
|
1772
|
+
if (accounts.length === 0) {
|
|
1773
|
+
const source = session.modelRegistry.authStorage.describeCredentialSource(
|
|
1774
|
+
accountList.provider,
|
|
1775
|
+
session.sessionId,
|
|
1776
|
+
);
|
|
1777
|
+
this.ctx.showStatus(
|
|
1778
|
+
source
|
|
1779
|
+
? `No stored OAuth accounts for ${providerName}. Current auth comes from ${source}.`
|
|
1780
|
+
: `No stored OAuth accounts for ${providerName}. Use /login to add one.`,
|
|
1781
|
+
);
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
this.showSelector(done => {
|
|
1786
|
+
const selector = new SessionAccountSelectorComponent(
|
|
1787
|
+
providerName,
|
|
1788
|
+
accounts,
|
|
1789
|
+
account => {
|
|
1790
|
+
done();
|
|
1791
|
+
if (!session.pinCurrentProviderOAuthAccount(account.credentialId)) {
|
|
1792
|
+
this.ctx.showWarning(`${account.label} is no longer available to pin.`);
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
this.ctx.showStatus(`Pinned ${account.label} to this session for ${providerName}.`);
|
|
1796
|
+
this.ctx.statusLine.invalidate();
|
|
1797
|
+
this.ctx.ui.requestRender();
|
|
1798
|
+
},
|
|
1799
|
+
() => {
|
|
1800
|
+
done();
|
|
1801
|
+
this.ctx.ui.requestRender();
|
|
1802
|
+
},
|
|
1803
|
+
);
|
|
1804
|
+
return { component: selector, focus: selector };
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1738
1808
|
async showResetUsageSelector(): Promise<void> {
|
|
1739
1809
|
const session = this.ctx.session;
|
|
1740
1810
|
this.ctx.showStatus("Checking saved rate-limit resets…", { dim: true });
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { AgentLifecycleManager } from "../../registry/agent-lifecycle";
|
|
13
13
|
import { AgentRegistry, MAIN_AGENT_ID, type RegistryEvent } from "../../registry/agent-registry";
|
|
14
14
|
import type { AgentSession } from "../../session/agent-session";
|
|
15
|
+
import { setTerminalTitleState } from "../../utils/title-generator";
|
|
15
16
|
import type { InteractiveModeContext } from "../types";
|
|
16
17
|
|
|
17
18
|
export class SessionFocusController {
|
|
@@ -104,8 +105,12 @@ export class SessionFocusController {
|
|
|
104
105
|
});
|
|
105
106
|
this.ctx.statusLine.setSession(target, this.#focusedAgentId);
|
|
106
107
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
107
|
-
//
|
|
108
|
+
// Sync the run-state title to the attached target: a streaming target has no
|
|
109
|
+
// agent_start incoming, so arm the loader/working title manually; an idle
|
|
110
|
+
// target would otherwise inherit the previous session's stuck spinner, so
|
|
111
|
+
// reset it to idle (agent_end teardown already ran via clearTransientSessionUi).
|
|
108
112
|
if (target.isStreaming) await this.ctx.eventController.handleEvent({ type: "agent_start" });
|
|
113
|
+
else setTerminalTitleState("idle");
|
|
109
114
|
this.ctx.updateEditorBorderColor();
|
|
110
115
|
this.ctx.ui.requestRender();
|
|
111
116
|
}
|