@timurproko/a1 0.2.6 → 0.2.7-dev.1222
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/README.md +41 -3
- package/bin/automatic-update.js +7 -0
- package/bin/cli.js +4 -0
- package/bin/guardian.js +5 -0
- package/bin/tab-launch-context.d.ts +27 -0
- package/bin/tab-launch-context.js +94 -0
- package/bin/ui.js +107 -59
- package/dist/app/session-shell/clipboard-diagnostics.js +1 -1
- package/dist/app/session-shell/dialog-coordinator.d.ts +42 -0
- package/dist/app/session-shell/dialog-coordinator.js +133 -0
- package/dist/app/session-shell/dock-chrome.d.ts +34 -0
- package/dist/app/session-shell/dock-chrome.js +109 -0
- package/dist/app/session-shell/exit-notice.d.ts +1 -1
- package/dist/app/session-shell/exit-notice.js +1 -1
- package/dist/app/session-shell/extension-surface.d.ts +30 -0
- package/dist/app/session-shell/extension-surface.js +105 -0
- package/dist/app/session-shell/helper-pool.d.ts +3 -5
- package/dist/app/session-shell/helper-pool.js +3 -5
- package/dist/app/session-shell/input-surface-switch.d.ts +36 -0
- package/dist/app/session-shell/input-surface-switch.js +85 -0
- package/dist/app/session-shell/login-interaction.d.ts +27 -0
- package/dist/app/session-shell/login-interaction.js +91 -0
- package/dist/app/session-shell/prompt-submission.d.ts +48 -0
- package/dist/app/session-shell/prompt-submission.js +276 -0
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +38 -0
- package/dist/app/session-shell/resident-tab-coordinator.js +77 -0
- package/dist/app/session-shell/response-copy-helper.js +21 -6
- package/dist/app/session-shell/response-copy-protocol.d.ts +1 -1
- package/dist/app/session-shell/response-copy-transport.d.ts +16 -6
- package/dist/app/session-shell/response-copy-transport.js +69 -17
- package/dist/app/session-shell/session-concern.d.ts +22 -0
- package/dist/app/session-shell/session-concern.js +29 -0
- package/dist/app/session-shell/session-lifecycle.d.ts +29 -0
- package/dist/app/session-shell/session-lifecycle.js +49 -0
- package/dist/app/session-shell/session-shell-root.d.ts +42 -6
- package/dist/app/session-shell/session-shell-root.js +197 -908
- package/dist/app/session-shell/session-shell.d.ts +3 -8
- package/dist/app/session-shell/session-shell.js +327 -835
- package/dist/app/session-shell/session-viewport-controller.d.ts +2 -1
- package/dist/app/session-shell/session-viewport-controller.js +41 -38
- package/dist/app/session-shell/settings-bindings.d.ts +17 -0
- package/dist/app/session-shell/settings-bindings.js +91 -0
- package/dist/app/session-shell/shell-editor-factory.d.ts +27 -0
- package/dist/app/session-shell/shell-editor-factory.js +105 -0
- package/dist/app/session-shell/shell-host.d.ts +6 -0
- package/dist/app/session-shell/shell-host.js +34 -7
- package/dist/app/session-shell/shell-layout.d.ts +64 -0
- package/dist/app/session-shell/shell-layout.js +18 -0
- package/dist/app/session-shell/suggestion-surface.d.ts +25 -0
- package/dist/app/session-shell/suggestion-surface.js +63 -0
- package/dist/app/session-shell/transcript-document.d.ts +78 -0
- package/dist/app/session-shell/transcript-document.js +365 -0
- package/dist/app/session-shell/viewport-layout.d.ts +31 -0
- package/dist/app/session-shell/viewport-layout.js +160 -0
- package/dist/app/session-shell/workflow-output.d.ts +33 -0
- package/dist/app/session-shell/workflow-output.js +174 -0
- package/dist/app/session-shell/workflow-router.d.ts +25 -0
- package/dist/app/session-shell/workflow-router.js +39 -0
- package/dist/cli/dispatch.d.ts +11 -1
- package/dist/cli/dispatch.js +79 -5
- package/dist/cli/tabs-request.d.ts +7 -0
- package/dist/cli/tabs-request.js +1 -0
- package/dist/cli/tabs.d.ts +14 -0
- package/dist/cli/tabs.js +151 -0
- package/dist/cli/version-stats.js +38 -3
- package/dist/composition/owned-settings.d.ts +12 -0
- package/dist/composition/owned-settings.js +36 -0
- package/dist/composition/owned-ui.d.ts +27 -28
- package/dist/composition/owned-ui.js +207 -153
- package/dist/composition/reference-selection-options.d.ts +3 -0
- package/dist/composition/reference-selection-options.js +16 -0
- package/dist/composition/resident-tab-bridge.d.ts +25 -0
- package/dist/composition/resident-tab-bridge.js +180 -0
- package/dist/composition/session-backend.d.ts +19 -0
- package/dist/composition/session-backend.js +1 -0
- package/dist/composition/session-runtime-registry.d.ts +20 -0
- package/dist/composition/session-runtime-registry.js +85 -0
- package/dist/composition/settings-route-host.d.ts +16 -2
- package/dist/composition/settings-route-host.js +42 -17
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +61 -0
- package/dist/composition/startup-route.d.ts +32 -0
- package/dist/composition/startup-route.js +72 -0
- package/dist/composition/utility-tab.d.ts +28 -0
- package/dist/composition/utility-tab.js +244 -0
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/model.d.ts +3 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +3 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +11 -4
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/index.d.ts +1 -1
- package/dist/features/launch/intent.d.ts +3 -1
- package/dist/features/launch/intent.js +7 -4
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +84 -0
- package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
- package/dist/features/owned-ui/project-trust-dialog.js +6 -8
- package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
- package/dist/features/owned-ui/project-trust-prompt.js +7 -3
- package/dist/features/owned-ui/reference-document-selection.d.ts +30 -0
- package/dist/features/owned-ui/reference-document-selection.js +179 -0
- package/dist/features/owned-ui/reference-screen-app.d.ts +6 -1
- package/dist/features/owned-ui/reference-screen-app.js +51 -10
- package/dist/features/owned-ui/settings-app.d.ts +13 -2
- package/dist/features/owned-ui/settings-app.js +136 -97
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/auto-name.d.ts +11 -0
- package/dist/features/resident-tab/auto-name.js +44 -0
- package/dist/features/resident-tab/index.d.ts +5 -0
- package/dist/features/resident-tab/index.js +7 -0
- package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
- package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
- package/dist/features/resident-tab/shortcut-declarations.d.ts +13 -0
- package/dist/features/resident-tab/shortcut-declarations.js +26 -0
- package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
- package/dist/features/resident-tab/shortcut-projection.js +64 -0
- package/dist/features/resident-tab/status-deriver.d.ts +10 -0
- package/dist/features/resident-tab/status-deriver.js +12 -0
- package/dist/foundation/launch-context/index.d.ts +12 -0
- package/dist/foundation/launch-context/index.js +26 -0
- package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
- package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
- package/dist/foundation/launch-guardian/main.d.ts +7 -1
- package/dist/foundation/launch-guardian/main.js +60 -7
- package/dist/foundation/lifecycle/session-repository-context.js +16 -2
- package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
- package/dist/foundation/process-containment/addon-containment.js +139 -0
- package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
- package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
- package/dist/foundation/process-containment/addon.d.ts +42 -0
- package/dist/foundation/process-containment/addon.js +57 -0
- package/dist/foundation/process-containment/artifact.d.ts +1 -1
- package/dist/foundation/process-containment/artifact.js +6 -5
- package/dist/foundation/process-containment/contracts.d.ts +2 -0
- package/dist/foundation/process-containment/index.d.ts +4 -4
- package/dist/foundation/process-containment/index.js +3 -3
- package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
- package/dist/foundation/process-containment/native-process-inspector.js +10 -10
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +7 -9
- package/dist/foundation/release/bootstrap.js +107 -24
- package/dist/foundation/release/cohort-state.d.ts +1 -1
- package/dist/foundation/release/cohort-state.js +1 -1
- package/dist/foundation/release/dependency-certification.js +54 -19
- package/dist/foundation/release/early-accent.d.ts +12 -0
- package/dist/foundation/release/early-accent.js +59 -0
- package/dist/foundation/release/index.d.ts +2 -2
- package/dist/foundation/release/index.js +2 -2
- package/dist/foundation/release/latest-release.d.ts +24 -2
- package/dist/foundation/release/latest-release.js +51 -1
- package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
- package/dist/foundation/release/progress-palette.generated.js +137 -3
- package/dist/foundation/release/release-gc.d.ts +3 -0
- package/dist/foundation/release/release-gc.js +59 -8
- package/dist/foundation/release/release-store.d.ts +12 -0
- package/dist/foundation/release/release-store.js +124 -2
- package/dist/foundation/release/update-execution-lease.d.ts +48 -0
- package/dist/foundation/release/update-execution-lease.js +161 -0
- package/dist/foundation/release/update.d.ts +12 -7
- package/dist/foundation/release/update.js +281 -196
- package/dist/foundation/resident-tabs/bounded-host-command.d.ts +18 -0
- package/dist/foundation/resident-tabs/bounded-host-command.js +39 -0
- package/dist/foundation/resident-tabs/bridge-client.d.ts +70 -0
- package/dist/foundation/resident-tabs/bridge-client.js +321 -0
- package/dist/foundation/resident-tabs/doctor.d.ts +28 -0
- package/dist/foundation/resident-tabs/doctor.js +166 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +36 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.js +151 -0
- package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
- package/dist/foundation/resident-tabs/fault-injection.js +26 -0
- package/dist/foundation/resident-tabs/fixture-control.d.ts +7 -0
- package/dist/foundation/resident-tabs/fixture-control.js +35 -0
- package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
- package/dist/foundation/resident-tabs/host-entry.js +17 -0
- package/dist/foundation/resident-tabs/index.d.ts +12 -0
- package/dist/foundation/resident-tabs/index.js +6 -0
- package/dist/foundation/resident-tabs/maintenance.d.ts +39 -0
- package/dist/foundation/resident-tabs/maintenance.js +75 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
- package/dist/foundation/resident-tabs/progress-observation.d.ts +4 -0
- package/dist/foundation/resident-tabs/progress-observation.js +150 -0
- package/dist/foundation/resident-tabs/prompt-journal.d.ts +63 -0
- package/dist/foundation/resident-tabs/prompt-journal.js +213 -0
- package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
- package/dist/foundation/resident-tabs/protocol.js +195 -0
- package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
- package/dist/foundation/resident-tabs/recovery.js +205 -0
- package/dist/foundation/resident-tabs/release-holds.d.ts +28 -0
- package/dist/foundation/resident-tabs/release-holds.js +75 -0
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/routing.d.ts +11 -0
- package/dist/foundation/resident-tabs/routing.js +10 -0
- package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
- package/dist/foundation/resident-tabs/session-sync.js +37 -0
- package/dist/foundation/resident-tabs/settings.d.ts +12 -0
- package/dist/foundation/resident-tabs/settings.js +58 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +14 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.js +78 -0
- package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
- package/dist/foundation/resident-tabs/zip-archive.js +56 -0
- package/dist/foundation/session-writer-lock/index.d.ts +30 -0
- package/dist/foundation/session-writer-lock/index.js +203 -0
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
- package/dist/foundation/terminal-cleanup/index.js +1 -1
- package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +6 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.js +6 -0
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +6 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +138 -11
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
- package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
- package/dist/integrations/pi/components/share-operation-dialog.d.ts +2 -0
- package/dist/integrations/pi/components/share-operation-dialog.js +2 -1
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +58 -36
- package/dist/integrations/pi/components/shell-extension-ui.js +79 -60
- package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
- package/dist/integrations/pi/components/shell-footer-status.js +21 -5
- package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +5 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +18 -4
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +12 -1
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -6
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +16 -9
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +4 -4
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +31 -25
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +8 -7
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +8 -7
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +10 -9
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +2 -6
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +103 -101
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +9 -9
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +13 -21
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/history/editor-core.js +7 -5
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +5 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +29 -2
- package/dist/integrations/pi/engine/adapter.d.ts +5 -77
- package/dist/integrations/pi/engine/adapter.js +95 -394
- package/dist/integrations/pi/engine/command-dispatch.d.ts +13 -0
- package/dist/integrations/pi/engine/command-dispatch.js +57 -0
- package/dist/integrations/pi/engine/host.js +2 -2
- package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
- package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
- package/dist/integrations/pi/engine/pi-settings-metadata.json +14 -2
- package/dist/integrations/pi/engine/ports.d.ts +56 -0
- package/dist/integrations/pi/engine/ports.js +93 -0
- package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
- package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/runtime-integration.js +102 -29
- package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
- package/dist/integrations/pi/engine/session-runtime.js +13 -5
- package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
- package/dist/integrations/pi/engine/session-selection.js +45 -9
- package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
- package/dist/integrations/pi/engine/settings-bridge.js +15 -0
- package/dist/integrations/pi/engine/settings-port.js +11 -15
- package/dist/integrations/pi/engine/view-state.d.ts +42 -0
- package/dist/integrations/pi/engine/view-state.js +126 -0
- package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
- package/dist/integrations/pi/engine/workflow-runner.js +74 -5
- package/dist/integrations/pi/engine/workflow-support.d.ts +12 -0
- package/dist/integrations/pi/engine/workflow-support.js +36 -0
- package/dist/integrations/pi/engine/workflows.d.ts +3 -1
- package/dist/integrations/pi/engine/workflows.js +1 -0
- package/dist/integrations/pi/startup-public.js +257 -310
- package/dist/integrations/pi/startup-public.manifest.json +564 -440
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +32 -1
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +5 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +12 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +31 -2
- package/dist/native/darwin-arm64/manifest.json +10 -7
- package/dist/native/darwin-arm64/process-guardian.node +0 -0
- package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
- package/dist/native/darwin-arm64/terminal-host.manifest.json +46 -0
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +23 -0
- package/dist/native/darwin-x64/process-guardian.node +0 -0
- package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
- package/dist/native/darwin-x64/terminal-host.manifest.json +46 -0
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +10 -7
- package/dist/native/linux-x64/process-guardian.node +0 -0
- package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
- package/dist/native/linux-x64/terminal-host.manifest.json +46 -0
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +10 -7
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +46 -0
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +3 -3
- package/dist/product-identity.js +4 -4
- package/dist/product-identity.json +16 -2
- package/dist/runtime-payload-inventory.json +6330 -6677
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.d.ts +17 -8
- package/dist/ui/components/dialog-panel.js +117 -41
- package/dist/ui/components/index.d.ts +5 -5
- package/dist/ui/components/index.js +4 -4
- package/dist/ui/components/line-input.d.ts +4 -1
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +11 -8
- package/dist/ui/components/prompt-input.js +15 -13
- package/dist/ui/components/scrollbar.d.ts +1 -1
- package/dist/ui/components/scrollbar.js +1 -1
- package/dist/ui/components/selection-copy.d.ts +3 -3
- package/dist/ui/components/selection-copy.js +16 -1
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/submitted-prompt.d.ts +9 -6
- package/dist/ui/components/submitted-prompt.js +20 -12
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/text-selection.d.ts +1 -2
- package/dist/ui/components/text-selection.js +5 -2
- package/dist/ui/components/text.d.ts +3 -6
- package/dist/ui/components/text.js +6 -8
- package/dist/ui/components/transcript-viewport.d.ts +9 -2
- package/dist/ui/components/transcript-viewport.js +179 -58
- package/dist/ui/settings/declarations.d.ts +90 -16
- package/dist/ui/settings/declarations.js +67 -14
- package/dist/ui/settings/early-accent.d.ts +81 -0
- package/dist/ui/settings/early-accent.js +50 -0
- package/dist/ui/settings/index.d.ts +4 -2
- package/dist/ui/settings/index.js +2 -1
- package/dist/ui/settings/manager.d.ts +16 -3
- package/dist/ui/settings/manager.js +122 -33
- package/dist/ui/settings/migrations.d.ts +6 -0
- package/dist/ui/settings/migrations.js +73 -0
- package/dist/ui/settings/profile-document.d.ts +26 -0
- package/dist/ui/settings/profile-document.js +164 -0
- package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
- package/dist/ui/settings/progress-palette.generated.js +139 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/boundaries.md +24 -9
- package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
- package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
- package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
- package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
- package/docs/architecture/internal-naming.md +3 -1
- package/docs/architecture/process-guardian-provenance.md +18 -10
- package/docs/architecture/project-structure.md +8 -0
- package/docs/architecture/resident-tabs-requirements.md +18 -0
- package/docs/architecture/resident-tabs.md +649 -0
- package/docs/architecture/response-copy-delivery.md +1 -1
- package/docs/architecture/terminal-host-proof-gate.md +2 -0
- package/docs/architecture/terminal-host-provenance.md +16 -4
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/architecture/toolchain.md +11 -9
- package/docs/ci-release-runbook.md +14 -7
- package/docs/features/launch-profiles.md +11 -8
- package/docs/features/prompt-history.md +41 -20
- package/docs/manual-resident-tabs-acceptance.md +51 -0
- package/docs/openspec-archive-automation.md +49 -22
- package/docs/validation.md +49 -9
- package/package.json +19 -6
- package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
- package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
- package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
- package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
- package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/process-guardian.exe +0 -0
|
@@ -1,27 +1,42 @@
|
|
|
1
1
|
import { ResponseCopyCoordinator } from "./response-copy-coordinator.js";
|
|
2
|
-
import { createResponseCopyExecutor,
|
|
2
|
+
import { createResponseCopyExecutor, responseCopyDestination } from "./response-copy-transport.js";
|
|
3
3
|
import { MAX_COPY_CONTROL_BYTES } from "./response-copy-protocol.js";
|
|
4
4
|
import { PromptHistoryController } from "./prompt-history-controller.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { SessionDialogCoordinator } from "./dialog-coordinator.js";
|
|
6
|
+
import { LoginInteraction } from "./login-interaction.js";
|
|
7
|
+
import { PromptSubmission } from "./prompt-submission.js";
|
|
8
|
+
import { ResidentTabCoordinator } from "./resident-tab-coordinator.js";
|
|
9
|
+
import { SessionConcerns } from "./session-concern.js";
|
|
10
|
+
import { composeExitText, SessionStopLatch } from "./session-lifecycle.js";
|
|
11
|
+
import { openWorkflowSelector } from "./workflow-router.js";
|
|
12
|
+
import { bindSessionSettings } from "./settings-bindings.js";
|
|
13
|
+
import { promptSuggestionSurface, routeResponseToSuggestions, suggestionModelKey, suggestionPresentationBlockReason, } from "./suggestion-surface.js";
|
|
14
|
+
import { CLEAR_EXIT_INTERVAL_MS, boundedCleanup } from "../../foundation/terminal-cleanup/terminal-reset.js";
|
|
15
|
+
import { OWNED_UI_HIDDEN_COMMAND_NAMES } from "../../contracts/owned-ui/index.js";
|
|
16
|
+
import { ImageAttachmentError } from "../../contracts/owned-ui/index.js";
|
|
17
|
+
import { copyAcknowledgementText } from "../../ui/components/selection-copy.js";
|
|
9
18
|
import { ContextualPromptSuggestionController } from "./prompt-suggestion-controller.js";
|
|
10
19
|
import { workflowCommandNames } from "../../integrations/pi/engine/workflows.js";
|
|
11
20
|
import { createPiExtensionUiBridge } from "../../integrations/pi/components/shell-extension-ui.js";
|
|
12
21
|
import { ProgramStatusReporter } from "../../integrations/pi/components/upstream/program-status-reporter.js";
|
|
13
22
|
import { SKILLS_COMMAND_NAME, findSkillByArgument, rewriteSkillsTunnelSubmission, skillPrompt, skillsFromCommands, } from "../../integrations/pi/components/skills-command.js";
|
|
14
|
-
import { createPiShellAuthProviderSelector,
|
|
15
|
-
import {
|
|
16
|
-
import { OwnedUiTerminalHost, sessionTerminalHostOptions, } from "./shell-host.js";
|
|
23
|
+
import { createPiShellAuthProviderSelector, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellReloadBox, createPiShellSkillsSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
|
|
24
|
+
import { OwnedUiTerminalHost, componentRuntime, sessionTerminalHostOptions, } from "./shell-host.js";
|
|
17
25
|
import { runImageWorker } from "./image-preparation-client.js";
|
|
18
26
|
import { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer, } from "./stream-presentation-coalescer.js";
|
|
19
27
|
import { writeSystemClipboardText, } from "./system-clipboard.js";
|
|
20
28
|
import { OwnedUiSessionShellRoot, shellResourceEntries, } from "./session-shell-root.js";
|
|
21
29
|
// Invariant: a disposed shell leaves the engine a host that holds no reference back to it.
|
|
22
30
|
const DETACHED_WORKFLOW_INTERACTION = { prompt: async () => null, notify() { } };
|
|
31
|
+
/** Resident Agents refuse the commands their tab bar serves instead, naming where they live. */
|
|
32
|
+
const RESIDENT_UTILITY_TAB_REFUSALS = Object.freeze({
|
|
33
|
+
settings: "Settings is available from the global Settings tab.",
|
|
34
|
+
changelog: "The changelog opens as its own tab from the command palette (Alt+/).",
|
|
35
|
+
name: "Rename the session by renaming its tab (F2).",
|
|
36
|
+
});
|
|
23
37
|
export { OwnedUiSessionShellRoot, } from "./session-shell-root.js";
|
|
24
38
|
export { OwnedUiTerminalHost, sessionTerminalHostOptions } from "./shell-host.js";
|
|
39
|
+
export { formatSessionResumeCommand, quoteCommandArgument } from "./session-lifecycle.js";
|
|
25
40
|
/**
|
|
26
41
|
* Presents one engine session: its transcript root, editor, controllers, submission, workflows, and dialogs.
|
|
27
42
|
* It reaches the terminal only through its terminal host's handle, and paints only while it is attached.
|
|
@@ -33,7 +48,10 @@ export class OwnedUiSessionPresenter {
|
|
|
33
48
|
root;
|
|
34
49
|
#host;
|
|
35
50
|
#terminal;
|
|
36
|
-
#
|
|
51
|
+
#resident;
|
|
52
|
+
#submission;
|
|
53
|
+
#login;
|
|
54
|
+
#concerns;
|
|
37
55
|
#listeners = new Set();
|
|
38
56
|
#unsubscribe;
|
|
39
57
|
#unsubscribePromptSuggestions;
|
|
@@ -44,18 +62,11 @@ export class OwnedUiSessionPresenter {
|
|
|
44
62
|
#unsubscribeSkills;
|
|
45
63
|
#installedCommandSignature = "";
|
|
46
64
|
#promptHistory = null;
|
|
47
|
-
#
|
|
65
|
+
#historyBinding;
|
|
48
66
|
#extensionBridge;
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#dialogId;
|
|
52
|
-
#dialogSource;
|
|
53
|
-
#routeHost;
|
|
54
|
-
#startupRoute;
|
|
55
|
-
#startupRouteTimer;
|
|
56
|
-
#dialogHandle;
|
|
67
|
+
#stop = new SessionStopLatch();
|
|
68
|
+
#dialogs;
|
|
57
69
|
#sequence = 0;
|
|
58
|
-
#editorRevision = 0;
|
|
59
70
|
#started = false;
|
|
60
71
|
#disposed = false;
|
|
61
72
|
#released;
|
|
@@ -77,18 +88,12 @@ export class OwnedUiSessionPresenter {
|
|
|
77
88
|
#streamPresentation;
|
|
78
89
|
#inputCoordination;
|
|
79
90
|
#unsubscribeSettings;
|
|
80
|
-
#
|
|
81
|
-
#unbindTerminalSettings;
|
|
82
|
-
#unbindShutdownSettings;
|
|
91
|
+
#unbindSessionSettings;
|
|
83
92
|
#terminalProgressEnabled = false;
|
|
84
93
|
#programStatus;
|
|
85
|
-
#showImages = true;
|
|
86
|
-
#imageWidthCells = 80;
|
|
87
94
|
#fullscreenExitOutput = "transcript";
|
|
88
|
-
#waitingImages = new Map();
|
|
89
95
|
#lastClearTime = 0;
|
|
90
96
|
#lastEscapeTime = 0;
|
|
91
|
-
#activeLoginDialog;
|
|
92
97
|
#sessionGeneration;
|
|
93
98
|
#sessionBindingGeneration;
|
|
94
99
|
#suggestionModelKey;
|
|
@@ -102,10 +107,7 @@ export class OwnedUiSessionPresenter {
|
|
|
102
107
|
this.#presenters = options.presenters;
|
|
103
108
|
this.#sessionGeneration = this.backend.identity.sessionGeneration;
|
|
104
109
|
this.#sessionBindingGeneration = this.backend.identity.sessionBindingGeneration;
|
|
105
|
-
this.#suggestionModelKey =
|
|
106
|
-
this.#cwd = cwd;
|
|
107
|
-
this.#routeHost = routeHost ?? null;
|
|
108
|
-
this.#startupRoute = options.engine.startupRoute;
|
|
110
|
+
this.#suggestionModelKey = suggestionModelKey(this.backend.session.view());
|
|
109
111
|
this.#customViewport = sessionLayout === "custom-viewport";
|
|
110
112
|
this.#inputCoordination = inputPresentation?.coordination !== false;
|
|
111
113
|
this.#host = host;
|
|
@@ -113,9 +115,6 @@ export class OwnedUiSessionPresenter {
|
|
|
113
115
|
// Invariant: the host's optional members follow the layout, so it is built only once the layout is known.
|
|
114
116
|
this.#dialogHost = this.#createDialogHost();
|
|
115
117
|
this.#lazySelectors = options.diagnostics?.lazySelectors;
|
|
116
|
-
this.#stopped = new Promise(resolve => {
|
|
117
|
-
this.#resolveStopped = resolve;
|
|
118
|
-
});
|
|
119
118
|
let promptSuggestionController = null;
|
|
120
119
|
// Invariant: owned prompt limits and collapsed skills are bare-A1 replacements; comparison profiles keep pinned behavior.
|
|
121
120
|
this.#promptImages = this.#customViewport ? options.promptImages ?? null : null;
|
|
@@ -124,31 +123,24 @@ export class OwnedUiSessionPresenter {
|
|
|
124
123
|
// throw releases them in reverse, so a shell that never finished constructing holds nothing.
|
|
125
124
|
const constructed = [() => host.disconnect(this)];
|
|
126
125
|
try {
|
|
127
|
-
const terminalCopy = terminal !== undefined || hasAsyncClipboardOutput();
|
|
128
126
|
this.#responseCopy = this.#customViewport ? new ResponseCopyCoordinator({
|
|
129
127
|
execute: responseCopy?.execute ?? (this.#copyExecutor = createResponseCopyExecutor({
|
|
130
128
|
...(sharedClipboard === undefined ? {} : { pool: sharedClipboard.copy }),
|
|
131
129
|
...(terminal === undefined && clipboard === undefined ? {} : { destination: "terminal" }),
|
|
132
130
|
...(clipboard?.writeText === undefined ? {} : { writeText: (text, signal) => clipboard.writeText(text, signal) }),
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
131
|
+
// Invariant: the owned host is the route contract. Resident and pseudoterminal children must not infer
|
|
132
|
+
// clipboard reachability from Node's view of stdout, and inactive presenters are fenced here.
|
|
133
|
+
terminal: { submit: async (control, signal) => {
|
|
134
|
+
if (signal.aborted || this.#disposed || !this.#terminal.running || !this.#terminal.attached)
|
|
135
|
+
throw new Error("Copy canceled");
|
|
136
|
+
// Performance: never grow the real terminal's pending buffer with another clipboard payload.
|
|
137
|
+
if (terminal === undefined && process.stdout.writableLength + control.length > MAX_COPY_CONTROL_BYTES) {
|
|
138
|
+
throw new Error("Clipboard terminal is busy");
|
|
139
|
+
}
|
|
140
|
+
this.#terminal.writeControl(control);
|
|
141
|
+
} },
|
|
142
142
|
})),
|
|
143
143
|
...(responseCopy?.onEvent === undefined ? {} : { onEvent: responseCopy.onEvent }),
|
|
144
|
-
onFailure: result => {
|
|
145
|
-
if (this.#disposed || !this.#terminal.running)
|
|
146
|
-
return;
|
|
147
|
-
this.root.appendWorkflowStatus(result.outcome === "timed-out" ? "Copy timed out; the clipboard did not respond."
|
|
148
|
-
: result.failure === "size" ? "Selection exceeds this clipboard route's size limit."
|
|
149
|
-
: "Could not copy the selection; the clipboard is unavailable.");
|
|
150
|
-
this.#terminal.requestRender();
|
|
151
|
-
},
|
|
152
144
|
}) : null;
|
|
153
145
|
constructed.push(() => { this.#responseCopy?.dispose(); this.#copyExecutor?.dispose(); });
|
|
154
146
|
const readClipboard = async (signal) => {
|
|
@@ -183,10 +175,12 @@ export class OwnedUiSessionPresenter {
|
|
|
183
175
|
replacementSurfaceActive: !this.root.usesDefaultInputSurface(),
|
|
184
176
|
}),
|
|
185
177
|
enableDockInputReuse: inputPresentation?.viewportReuse !== false,
|
|
178
|
+
footerSessionName: options.engine.residentTab === undefined,
|
|
179
|
+
hideUtilityTabCommands: options.engine.residentTab !== undefined,
|
|
186
180
|
persistentHistory: this.#customViewport && promptHistory !== undefined,
|
|
187
181
|
...(promptHistory === undefined ? {} : { historyEditor: promptHistory.editor }),
|
|
188
|
-
onSubmit: text => { void this.submit(text).catch(() => this.#
|
|
189
|
-
onPasteRejected: error => this.#
|
|
182
|
+
onSubmit: text => { void this.submit(text).catch(() => this.#submission.reportError()); },
|
|
183
|
+
onPasteRejected: error => this.#submission.reportError(error),
|
|
190
184
|
onInterrupt: () => { void this.interrupt(); },
|
|
191
185
|
onClear: () => { void this.clearOrExit(); },
|
|
192
186
|
onExit: () => { void this.shutdown(); },
|
|
@@ -198,10 +192,11 @@ export class OwnedUiSessionPresenter {
|
|
|
198
192
|
this.#terminal.requestRender();
|
|
199
193
|
},
|
|
200
194
|
onMessageCopy: () => { void this.runWorkflow({ command: "copy", argument: "" }); },
|
|
201
|
-
onFollowUp: () => { void this.queueFollowUp().catch(() => this.#
|
|
195
|
+
onFollowUp: () => { void this.queueFollowUp().catch(() => this.#submission.reportError()); },
|
|
202
196
|
onDequeue: () => this.restoreQueuedInput(),
|
|
203
197
|
onEditorChange: text => {
|
|
204
|
-
this.#
|
|
198
|
+
this.#submission.noteEditorChange();
|
|
199
|
+
this.#resident.checkpoint(text);
|
|
205
200
|
promptSuggestionController?.abortPending();
|
|
206
201
|
if (text.length === 0) {
|
|
207
202
|
// Concurrency: the editor synchronizes deletion-owned autocomplete after onChange.
|
|
@@ -218,9 +213,9 @@ export class OwnedUiSessionPresenter {
|
|
|
218
213
|
promptSuggestionController?.invalidate();
|
|
219
214
|
this.#promptHistory?.synchronize();
|
|
220
215
|
},
|
|
221
|
-
onCopyText: text => {
|
|
216
|
+
onCopyText: (text, cut) => {
|
|
222
217
|
if (this.#responseCopy !== null) {
|
|
223
|
-
|
|
218
|
+
this.#trackCopy(this.#responseCopy.submitText(text), cut ? undefined : text.length);
|
|
224
219
|
return;
|
|
225
220
|
}
|
|
226
221
|
const write = () => {
|
|
@@ -263,8 +258,27 @@ export class OwnedUiSessionPresenter {
|
|
|
263
258
|
// Invariant: the presenter owns its root (theme subscription and helper clients) and disposes it once the
|
|
264
259
|
// terminal no longer renders it.
|
|
265
260
|
constructed.push(() => this.root.dispose());
|
|
261
|
+
this.#resident = new ResidentTabCoordinator(options.engine.residentTab, { root: this.root });
|
|
262
|
+
constructed.push(() => this.#resident.dispose());
|
|
263
|
+
const startupRoute = options.engine.startupRoute;
|
|
264
|
+
this.#dialogs = new SessionDialogCoordinator({
|
|
265
|
+
terminal: this.#terminal,
|
|
266
|
+
root: this.root,
|
|
267
|
+
routeHost: routeHost ?? null,
|
|
268
|
+
disposed: () => this.#disposed,
|
|
269
|
+
exit: () => { void this.shutdown(); },
|
|
270
|
+
routeClosed: route => this.#resident.closeOwnedRoute(route),
|
|
271
|
+
...(startupRoute === undefined ? {} : { startupRoute }),
|
|
272
|
+
});
|
|
273
|
+
constructed.push(() => this.#dialogs.dispose());
|
|
266
274
|
const initialPiSettings = this.backend.settings.snapshot();
|
|
267
275
|
this.#programStatus = new ProgramStatusReporter(status => this.#terminal.setProgramStatus(status), () => this.view().status.footer?.sessionName ?? undefined);
|
|
276
|
+
this.#login = new LoginInteraction({
|
|
277
|
+
terminal: this.#terminal, root: this.root, programStatus: this.#programStatus,
|
|
278
|
+
syncProgress: () => this.#syncTerminalProgress(this.view()),
|
|
279
|
+
});
|
|
280
|
+
this.#submission = this.#createSubmission(cwd);
|
|
281
|
+
this.#resident.restoreInput(options.history?.imageSidecar);
|
|
268
282
|
this.#reloadPresentation = reloadPresentation;
|
|
269
283
|
const presentationInterval = streamPresentationOptions?.intervalMs ?? STREAM_PRESENTATION_INTERVAL_MS;
|
|
270
284
|
this.#streamPresentation = streamPresentationOptions?.scheduler === undefined
|
|
@@ -276,18 +290,7 @@ export class OwnedUiSessionPresenter {
|
|
|
276
290
|
generator: promptSuggestionOptions.generator,
|
|
277
291
|
enabled: promptSuggestionOptions.enabled(),
|
|
278
292
|
...(promptSuggestionOptions.diagnostics === undefined ? {} : { diagnostics: promptSuggestionOptions.diagnostics }),
|
|
279
|
-
surface:
|
|
280
|
-
canPresent: identity => this.#promptSuggestionPresentationBlockReason(identity) === null,
|
|
281
|
-
presentationBlockReason: identity => this.#promptSuggestionPresentationBlockReason(identity),
|
|
282
|
-
present: text => {
|
|
283
|
-
if (!this.root.canPresentPromptSuggestion())
|
|
284
|
-
return false;
|
|
285
|
-
this.root.setPromptSuggestion(text);
|
|
286
|
-
return true;
|
|
287
|
-
},
|
|
288
|
-
clear: () => this.root.setPromptSuggestion(null),
|
|
289
|
-
requestRender: () => this.#terminal.requestRender(),
|
|
290
|
-
},
|
|
293
|
+
surface: promptSuggestionSurface(this.root, identity => this.#promptSuggestionPresentationBlockReason(identity), () => this.#terminal.requestRender()),
|
|
291
294
|
});
|
|
292
295
|
this.#promptSuggestions = promptSuggestionController;
|
|
293
296
|
constructed.push(() => this.#promptSuggestions?.dispose());
|
|
@@ -310,35 +313,7 @@ export class OwnedUiSessionPresenter {
|
|
|
310
313
|
this.#terminal.requestRender();
|
|
311
314
|
});
|
|
312
315
|
constructed.push(this.#unsubscribeSkills);
|
|
313
|
-
this.#terminal.setHardwareCursor(initialPiSettings.showHardwareCursor);
|
|
314
|
-
this.#terminal.setClearOnShrink(initialPiSettings.clearOnShrink);
|
|
315
|
-
this.#terminalProgressEnabled = initialPiSettings.showTerminalProgress;
|
|
316
316
|
this.#fullscreenExitOutput = initialPiSettings.fullscreenExitOutput;
|
|
317
|
-
// Invariant: bare A1 prints only the resume hint at exit, so the pinned exit-output
|
|
318
|
-
// choice is hidden there and cannot be bound; the comparison profile binds and honors it.
|
|
319
|
-
this.#unbindShutdownSettings = this.backend.settings.productMode === "bare" ? () => { } : this.backend.settings.bindOwner("shutdown", {
|
|
320
|
-
fullscreenExitOutput: { apply() { } },
|
|
321
|
-
});
|
|
322
|
-
constructed.push(this.#unbindShutdownSettings);
|
|
323
|
-
this.#unbindTerminalSettings = this.backend.settings.bindOwner("terminal", {
|
|
324
|
-
showHardwareCursor: { apply: value => {
|
|
325
|
-
if (typeof value !== "boolean")
|
|
326
|
-
throw new TypeError("Hardware cursor setting is invalid");
|
|
327
|
-
this.#terminal.setHardwareCursor(value);
|
|
328
|
-
} },
|
|
329
|
-
clearOnShrink: { apply: value => {
|
|
330
|
-
if (typeof value !== "boolean")
|
|
331
|
-
throw new TypeError("Clear-on-shrink setting is invalid");
|
|
332
|
-
this.#terminal.setClearOnShrink(value);
|
|
333
|
-
} },
|
|
334
|
-
showTerminalProgress: { apply: value => {
|
|
335
|
-
if (typeof value !== "boolean")
|
|
336
|
-
throw new TypeError("Terminal progress setting is invalid");
|
|
337
|
-
this.#terminalProgressEnabled = value;
|
|
338
|
-
this.#syncTerminalProgress(this.view());
|
|
339
|
-
} },
|
|
340
|
-
});
|
|
341
|
-
constructed.push(this.#unbindTerminalSettings);
|
|
342
317
|
const applyViewportSettings = () => {
|
|
343
318
|
const snapshot = viewportSettings?.snapshot();
|
|
344
319
|
this.root.setViewportConfig(snapshot ?? {
|
|
@@ -348,74 +323,23 @@ export class OwnedUiSessionPresenter {
|
|
|
348
323
|
});
|
|
349
324
|
};
|
|
350
325
|
applyViewportSettings();
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
326
|
+
const unsubscribeViewport = this.#customViewport && viewportSettings ? viewportSettings.onChange(settings => this.root.setViewportConfig(settings)) : () => { };
|
|
327
|
+
const unsubscribeHistoryScope = this.#customViewport && promptHistory?.onScopeChange?.(scope => this.#rebindTranscript(scope));
|
|
328
|
+
this.#unsubscribeSettings = () => { unsubscribeViewport(); if (unsubscribeHistoryScope)
|
|
329
|
+
unsubscribeHistoryScope(); };
|
|
354
330
|
constructed.push(this.#unsubscribeSettings);
|
|
355
|
-
this.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
this.#
|
|
365
|
-
editorPaddingX: { apply: value => {
|
|
366
|
-
if (typeof value !== "number")
|
|
367
|
-
throw new TypeError("Editor padding is invalid");
|
|
368
|
-
this.root.setEditorPaddingX(value);
|
|
369
|
-
} },
|
|
370
|
-
autocompleteMaxVisible: { apply: value => {
|
|
371
|
-
if (typeof value !== "number")
|
|
372
|
-
throw new TypeError("Autocomplete maximum is invalid");
|
|
373
|
-
this.root.setAutocompleteMaxVisible(value);
|
|
374
|
-
} },
|
|
375
|
-
outputPad: { apply: value => {
|
|
376
|
-
if (value !== 0 && value !== 1)
|
|
377
|
-
throw new TypeError("Output padding is invalid");
|
|
378
|
-
this.root.setOutputPad(value);
|
|
379
|
-
} },
|
|
380
|
-
hideThinkingBlock: { apply: value => {
|
|
381
|
-
if (typeof value !== "boolean")
|
|
382
|
-
throw new TypeError("Thinking-block visibility is invalid");
|
|
383
|
-
this.root.setHideThinkingBlock(value);
|
|
384
|
-
} },
|
|
385
|
-
mermaidRenderingMode: { apply: value => {
|
|
386
|
-
if (value !== "off" && value !== "final" && value !== "streaming")
|
|
387
|
-
throw new TypeError("Mermaid mode is invalid");
|
|
388
|
-
this.root.setMermaidRenderingMode(value);
|
|
389
|
-
} },
|
|
390
|
-
showImages: { apply: value => {
|
|
391
|
-
if (typeof value !== "boolean")
|
|
392
|
-
throw new TypeError("Image visibility is invalid");
|
|
393
|
-
this.#showImages = value;
|
|
394
|
-
this.root.setImagePresentation(this.#showImages, this.#imageWidthCells);
|
|
395
|
-
} },
|
|
396
|
-
imageWidthCells: { apply: value => {
|
|
397
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
|
|
398
|
-
throw new TypeError("Image width is invalid");
|
|
399
|
-
this.#imageWidthCells = value;
|
|
400
|
-
this.root.setImagePresentation(this.#showImages, this.#imageWidthCells);
|
|
401
|
-
} },
|
|
402
|
-
...(this.#customViewport ? { fullscreenCopyOnSelect: { apply: (value) => {
|
|
403
|
-
if (typeof value !== "boolean")
|
|
404
|
-
throw new TypeError("Fullscreen copy-on-select setting is invalid");
|
|
405
|
-
this.root.setFullscreenCopyOnSelect(value);
|
|
406
|
-
} } } : {
|
|
407
|
-
fullscreenWheelScrollLines: { apply: (value) => {
|
|
408
|
-
if (value !== "auto" && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > 100)) {
|
|
409
|
-
throw new TypeError("Fullscreen wheel-scroll distance is invalid");
|
|
410
|
-
}
|
|
411
|
-
this.#terminal.setWheelScrollLines(value);
|
|
412
|
-
} },
|
|
413
|
-
}),
|
|
414
|
-
});
|
|
415
|
-
constructed.push(this.#unbindPiSettings);
|
|
331
|
+
this.#unbindSessionSettings = bindSessionSettings(this.backend.settings, initialPiSettings, {
|
|
332
|
+
terminal: this.#terminal,
|
|
333
|
+
root: this.root,
|
|
334
|
+
setTerminalProgress: enabled => {
|
|
335
|
+
this.#terminalProgressEnabled = enabled;
|
|
336
|
+
if (this.#terminal.running)
|
|
337
|
+
this.#syncTerminalProgress(this.view());
|
|
338
|
+
},
|
|
339
|
+
}, { customViewport: this.#customViewport });
|
|
340
|
+
constructed.push(this.#unbindSessionSettings);
|
|
416
341
|
if (this.#customViewport && promptHistory !== undefined) {
|
|
417
|
-
this.#
|
|
418
|
-
const sidecar = this.#promptHistoryImageSidecar;
|
|
342
|
+
this.#historyBinding = { options: promptHistory, scope: promptHistory.scope ?? "unique", sessionId: promptHistory.sessionId, imageSidecar: promptHistory.imageSidecar };
|
|
419
343
|
this.#promptHistory = new PromptHistoryController({
|
|
420
344
|
editor: this.root.editor,
|
|
421
345
|
store: promptHistory.store,
|
|
@@ -423,57 +347,33 @@ export class OwnedUiSessionPresenter {
|
|
|
423
347
|
fallback: this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []),
|
|
424
348
|
active: () => this.root.usesDefaultInputSurface(),
|
|
425
349
|
render: () => this.#terminal.requestRender(),
|
|
426
|
-
rehydrate: value => this.root.rehydrateHistoryText(value, id =>
|
|
350
|
+
rehydrate: value => this.root.rehydrateHistoryText(value, id => this.#historyBinding?.imageSidecar?.readAttachment(id) ?? null),
|
|
427
351
|
});
|
|
428
352
|
constructed.push(() => { void this.#promptHistory?.close().catch(() => false); });
|
|
429
353
|
}
|
|
430
|
-
this.#extensionBridge =
|
|
431
|
-
runtime: {
|
|
432
|
-
getColumns: () => this.#terminal.viewport().columns,
|
|
433
|
-
getRows: () => this.#terminal.viewport().rows,
|
|
434
|
-
requestRender: () => this.#terminal.requestRender(),
|
|
435
|
-
},
|
|
436
|
-
agentDir: this.backend.identity.agentDir,
|
|
437
|
-
setInputSurface: component => this.root.setInputSurface(component, true, "opaque"),
|
|
438
|
-
setProgramStatusBlocked: status => this.#programStatus.setBlocked("extension-dialog", status),
|
|
439
|
-
showOverlay: (component, overlayOptions) => this.#terminal.showOverlay(component, overlayOptions),
|
|
440
|
-
listenInput: handler => this.#terminal.addInputListener(handler),
|
|
441
|
-
replaceWidget: (key, component, placement) => this.root.setExtensionWidget(key, component, placement),
|
|
442
|
-
replaceHeader: component => this.root.setExtensionHeader(component),
|
|
443
|
-
replaceFooter: component => this.root.setExtensionFooter(component),
|
|
444
|
-
setStatus: (key, text) => this.root.setExtensionStatus(key, text),
|
|
445
|
-
setWorking: (message, visible) => this.root.setExtensionWorking(message, visible),
|
|
446
|
-
notify: (message, type) => this.root.addExtensionNotification(message, type),
|
|
447
|
-
setTitle: title => this.#terminal.setTitle(title),
|
|
448
|
-
getEditorText: () => this.root.editor.getText(),
|
|
449
|
-
setEditorText: text => this.root.editor.setText(text),
|
|
450
|
-
pasteToEditor: text => this.root.editor.insertText(text),
|
|
451
|
-
addAutocompleteProvider: factory => this.root.editor.addAutocompleteProvider(factory),
|
|
452
|
-
setCustomEditor: component => this.root.setInputSurface(component, true, "opaque"),
|
|
453
|
-
getFooterData: () => this.root.extensionFooterData(),
|
|
454
|
-
getToolsExpanded: () => this.root.toolsExpanded,
|
|
455
|
-
setToolsExpanded: expanded => this.root.setToolsExpanded(expanded),
|
|
456
|
-
});
|
|
354
|
+
this.#extensionBridge = this.#createExtensionBridge();
|
|
457
355
|
constructed.push(() => this.#extensionBridge.dispose());
|
|
356
|
+
this.#concerns = this.#createConcerns();
|
|
458
357
|
constructed.push(() => this.backend.workflows.setWorkflowInteractionHost(DETACHED_WORKFLOW_INTERACTION));
|
|
459
358
|
this.backend.workflows.setWorkflowInteractionHost({
|
|
460
|
-
startLogin: request => this.#
|
|
461
|
-
prompt: request => this.#
|
|
462
|
-
notify: event => this.#
|
|
359
|
+
startLogin: request => this.#login.start(request),
|
|
360
|
+
prompt: request => this.#login.requestInput(request),
|
|
361
|
+
notify: event => this.#login.notify(event),
|
|
463
362
|
publish: message => {
|
|
464
363
|
this.root.appendWorkflowMessage(message);
|
|
465
364
|
this.#terminal.requestRender();
|
|
466
365
|
},
|
|
467
|
-
finishLogin: () => this.#
|
|
366
|
+
finishLogin: () => this.#login.finish(),
|
|
468
367
|
});
|
|
469
368
|
this.#installAutocompleteCommands();
|
|
470
369
|
this.#unsubscribe = this.backend.session.onEvent(event => {
|
|
471
370
|
// Invariant: once disposal begins no event touches the view; only the stop still settles the shell.
|
|
472
371
|
if (this.#disposed) {
|
|
473
372
|
if (event.type === "session-lifecycle" && event.lifecycle === "stopped")
|
|
474
|
-
this.#
|
|
373
|
+
this.#stop.settleAfter(() => this.dispose());
|
|
475
374
|
return;
|
|
476
375
|
}
|
|
376
|
+
this.#resident.observeLifecycle(event);
|
|
477
377
|
// Performance: a streamed chunk names one block, and touching only that block is what keeps the
|
|
478
378
|
// cost of a chunk the same in a long session as in a new one. Everything else
|
|
479
379
|
// resynchronizes the view, which is cheap next to re-reading the transcript.
|
|
@@ -489,28 +389,14 @@ export class OwnedUiSessionPresenter {
|
|
|
489
389
|
}
|
|
490
390
|
if (event.type === "assistant-message-completed") {
|
|
491
391
|
this.root.noteCompletedAssistantMessage();
|
|
492
|
-
|
|
493
|
-
sessionId: event.sessionId,
|
|
494
|
-
sessionGeneration: event.sessionGeneration,
|
|
495
|
-
runSequence: event.runSequence,
|
|
496
|
-
responseSequence: event.responseSequence,
|
|
497
|
-
model: event.model,
|
|
498
|
-
};
|
|
499
|
-
if (event.model === null)
|
|
500
|
-
this.#promptSuggestions?.skip(identity, "no-model");
|
|
501
|
-
else
|
|
502
|
-
this.#promptSuggestions?.consider({ ...identity, model: event.model }, !event.successful ? "failed-response"
|
|
503
|
-
: event.toolContinuation || event.stopReason === "toolUse" ? "tool-continuation"
|
|
504
|
-
: event.stopReason !== "stop" ? "incomplete-response"
|
|
505
|
-
: event.assistantMessageCount < 2 ? "early-conversation"
|
|
506
|
-
: this.root.promptSuggestionPrepareBlockReason());
|
|
392
|
+
routeResponseToSuggestions(this.#promptSuggestions, event, () => this.root.promptSuggestionPrepareBlockReason());
|
|
507
393
|
}
|
|
508
394
|
const semanticOnly = event.type === "agent-run-started" || event.type === "assistant-message-completed";
|
|
509
395
|
const view = event.type === "transcript-block" && this.#sessionGeneration === this.backend.identity.sessionGeneration
|
|
510
396
|
? this.#syncBlock(event.block)
|
|
511
397
|
: semanticOnly ? this.view() : this.#syncView();
|
|
512
398
|
this.#syncTerminalProgress(view);
|
|
513
|
-
const currentModelKey =
|
|
399
|
+
const currentModelKey = suggestionModelKey(view);
|
|
514
400
|
if (this.backend.identity.sessionGeneration !== this.#sessionGeneration || currentModelKey !== this.#suggestionModelKey) {
|
|
515
401
|
this.#promptSuggestions?.invalidate();
|
|
516
402
|
this.#suggestionModelKey = currentModelKey;
|
|
@@ -519,24 +405,10 @@ export class OwnedUiSessionPresenter {
|
|
|
519
405
|
this.#promptSuggestions?.invalidate();
|
|
520
406
|
}
|
|
521
407
|
if (event.type === "agent-run-settled") {
|
|
522
|
-
|
|
523
|
-
sessionId: event.sessionId,
|
|
524
|
-
sessionGeneration: event.sessionGeneration,
|
|
525
|
-
runSequence: event.runSequence,
|
|
526
|
-
responseSequence: event.responseSequence,
|
|
527
|
-
model: event.model,
|
|
528
|
-
};
|
|
529
|
-
if (event.model === null)
|
|
530
|
-
this.#promptSuggestions?.skip(identity, "no-model");
|
|
531
|
-
else
|
|
532
|
-
this.#promptSuggestions?.settle({ ...identity, model: event.model }, !event.successful ? "failed-response"
|
|
533
|
-
: event.toolContinuation || event.stopReason === "toolUse" ? "tool-continuation"
|
|
534
|
-
: event.stopReason !== "stop" ? "incomplete-response"
|
|
535
|
-
: event.assistantMessageCount < 2 ? "early-conversation"
|
|
536
|
-
: this.root.promptSuggestionPrepareBlockReason());
|
|
408
|
+
routeResponseToSuggestions(this.#promptSuggestions, event, () => this.root.promptSuggestionPrepareBlockReason());
|
|
537
409
|
}
|
|
538
410
|
if (event.type === "session-lifecycle" && event.lifecycle === "stopped")
|
|
539
|
-
this.#
|
|
411
|
+
this.#stop.settleAfter(() => this.dispose());
|
|
540
412
|
});
|
|
541
413
|
constructed.push(this.#unsubscribe);
|
|
542
414
|
this.#unbindClipboardWriter = this.#responseCopy === null ? () => { } : this.backend.extensions.bindClipboardWriter(async (text) => {
|
|
@@ -549,7 +421,7 @@ export class OwnedUiSessionPresenter {
|
|
|
549
421
|
});
|
|
550
422
|
constructed.push(this.#unbindClipboardWriter);
|
|
551
423
|
if (this.backend.session.view().lifecycle === "stopped")
|
|
552
|
-
this.#
|
|
424
|
+
this.#stop.resolve();
|
|
553
425
|
}
|
|
554
426
|
catch (error) {
|
|
555
427
|
for (const release of constructed.reverse()) {
|
|
@@ -626,29 +498,25 @@ export class OwnedUiSessionPresenter {
|
|
|
626
498
|
const routed = this.root.handleViewportPreInput(data, true, Date.now(), this.root.usesDefaultInputSurface() && !this.#terminal.hasFocusedOverlay());
|
|
627
499
|
if (routed.copySelection !== undefined) {
|
|
628
500
|
const snapshot = routed.copySelection;
|
|
629
|
-
|
|
630
|
-
const intent = ++this.#copyIntentSequence;
|
|
631
|
-
void this.#responseCopy?.submit(snapshot, this.#pendingClipboardWrite).then(result => {
|
|
632
|
-
if (this.#disposed || !this.#terminal.running || intent !== this.#copyIntentSequence
|
|
633
|
-
|| result.outcome !== "delivered" && result.outcome !== "submitted-unverified")
|
|
634
|
-
return;
|
|
635
|
-
if (acknowledgement.hasNonWhitespace) {
|
|
636
|
-
this.root.showCopyAcknowledgement(`copied ${acknowledgement.characters} chars to clipboard`);
|
|
637
|
-
}
|
|
638
|
-
});
|
|
501
|
+
this.#trackCopy(this.#responseCopy?.submit(snapshot, this.#pendingClipboardWrite), copyCharacters(snapshot));
|
|
639
502
|
}
|
|
503
|
+
if (routed.copyLastMessage)
|
|
504
|
+
void this.runWorkflow({ command: "copy", argument: "" });
|
|
640
505
|
if (!routed.consumed)
|
|
641
506
|
return routed.data === data ? undefined : { data: routed.data };
|
|
642
507
|
return routed.data.length === 0 ? { consume: true } : { data: routed.data };
|
|
643
508
|
}
|
|
509
|
+
#trackCopy(delivery, count) {
|
|
510
|
+
const intent = ++this.#copyIntentSequence;
|
|
511
|
+
void delivery?.then(({ outcome }) => {
|
|
512
|
+
if (count === undefined || this.#disposed || !this.#terminal.running || intent !== this.#copyIntentSequence
|
|
513
|
+
|| outcome !== "delivered" && outcome !== "submitted-unverified")
|
|
514
|
+
return;
|
|
515
|
+
this.root.showCopyAcknowledgement(copyAcknowledgementText(count));
|
|
516
|
+
});
|
|
517
|
+
}
|
|
644
518
|
#promptSuggestionPresentationBlockReason(identity) {
|
|
645
|
-
|
|
646
|
-
if (this.#disposed)
|
|
647
|
-
return "disposed";
|
|
648
|
-
if (identity.sessionId !== view.sessionId || identity.sessionGeneration !== this.backend.identity.sessionGeneration
|
|
649
|
-
|| modelKey(view) !== `${identity.model.providerId}/${identity.model.modelId}`)
|
|
650
|
-
return "stale-identity";
|
|
651
|
-
return this.root.promptSuggestionPresentationBlockReason();
|
|
519
|
+
return suggestionPresentationBlockReason(identity, { disposed: this.#disposed, view: this.view(), sessionGeneration: this.backend.identity.sessionGeneration }, () => this.root.promptSuggestionPresentationBlockReason());
|
|
652
520
|
}
|
|
653
521
|
start() {
|
|
654
522
|
if (this.#started)
|
|
@@ -663,7 +531,7 @@ export class OwnedUiSessionPresenter {
|
|
|
663
531
|
setImmediate(() => {
|
|
664
532
|
if (this.#disposed || !this.#customViewport)
|
|
665
533
|
return;
|
|
666
|
-
this.#presentStartupRoute();
|
|
534
|
+
this.#dialogs.presentStartupRoute();
|
|
667
535
|
void this.#loadLazySelectors().then(selectors => selectors.prepare()).catch(() => { });
|
|
668
536
|
this.root.warmPastePreparation();
|
|
669
537
|
this.#copyExecutor?.warm();
|
|
@@ -675,127 +543,14 @@ export class OwnedUiSessionPresenter {
|
|
|
675
543
|
return () => this.#listeners.delete(listener);
|
|
676
544
|
}
|
|
677
545
|
waitUntilStopped() {
|
|
678
|
-
return this.#stopped;
|
|
546
|
+
return this.#stop.stopped;
|
|
679
547
|
}
|
|
680
548
|
async submit(text) {
|
|
681
|
-
return this.#
|
|
682
|
-
}
|
|
683
|
-
#submitWhenReady(draft, action) {
|
|
684
|
-
const previous = this.#waitingImages.get(draft);
|
|
685
|
-
if (previous !== undefined)
|
|
686
|
-
return previous.result;
|
|
687
|
-
if (!this.root.hasPendingPastes(draft))
|
|
688
|
-
return this.#guardSubmission(draft, action);
|
|
689
|
-
const controller = new AbortController();
|
|
690
|
-
const generation = this.backend.identity.sessionGeneration;
|
|
691
|
-
const result = this.#guardSubmission(draft, async () => {
|
|
692
|
-
await this.root.waitForPromptPastes(draft, controller.signal);
|
|
693
|
-
if (controller.signal.aborted || this.#disposed || this.backend.identity.sessionGeneration !== generation)
|
|
694
|
-
return rejected("image submission canceled");
|
|
695
|
-
return action();
|
|
696
|
-
}).finally(() => {
|
|
697
|
-
this.#waitingImages.delete(draft);
|
|
698
|
-
if (!this.#disposed)
|
|
699
|
-
this.#showWaitingImages();
|
|
700
|
-
});
|
|
701
|
-
this.#waitingImages.set(draft, { controller, result });
|
|
702
|
-
if (this.root.editor.getText() === draft)
|
|
703
|
-
this.root.editor.setText("");
|
|
704
|
-
this.#showWaitingImages();
|
|
705
|
-
return result;
|
|
706
|
-
}
|
|
707
|
-
#showWaitingImages() {
|
|
708
|
-
const count = this.#waitingImages.size;
|
|
709
|
-
this.root.setExtensionWidget("owned-image-preparation", count === 0 ? null : {
|
|
710
|
-
render: width => [...renderPiShellStatusText(`Waiting for images (${count} submission${count === 1 ? "" : "s"}) — Esc cancels; dequeue restores`, width)],
|
|
711
|
-
invalidate: () => { },
|
|
712
|
-
}, "aboveEditor");
|
|
713
|
-
this.#terminal.requestRender();
|
|
714
|
-
}
|
|
715
|
-
#cancelWaitingImages() {
|
|
716
|
-
for (const item of this.#waitingImages.values())
|
|
717
|
-
item.controller.abort();
|
|
718
|
-
}
|
|
719
|
-
#rememberInput(text, kind) {
|
|
720
|
-
this.root.editor.addToHistory(text);
|
|
721
|
-
if (this.#promptHistory !== null) {
|
|
722
|
-
const reusable = this.root.prepareHistoryText(text);
|
|
723
|
-
if (reusable.length === 0)
|
|
724
|
-
this.#promptHistory.rememberRecovery(text);
|
|
725
|
-
else {
|
|
726
|
-
this.#persistImageSidecars(text);
|
|
727
|
-
this.#promptHistory.capture(reusable, kind, this.#cwd, this.backend.identity.sessionId);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
// Rationale: write the image chip payloads referenced by an editor draft to the sidecar
|
|
732
|
-
// directory before their history row commits. Missing sidecar dependency = silent skip; the
|
|
733
|
-
// recall path treats an absent sidecar the same as a corrupt one.
|
|
734
|
-
#persistImageSidecars(text) {
|
|
735
|
-
const sidecar = this.#promptHistoryImageSidecar;
|
|
736
|
-
if (sidecar === undefined)
|
|
737
|
-
return;
|
|
738
|
-
for (const record of this.root.imageChipAttachmentsFromEditor(text)) {
|
|
739
|
-
sidecar.write(record.id, { tag: record.tag, data: record.image.data, mimeType: record.image.mimeType, savedAt: new Date().toISOString() });
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
async #submit(text) {
|
|
743
|
-
this.#promptSuggestions?.invalidate();
|
|
744
|
-
const displayInput = text.trim();
|
|
745
|
-
if (!displayInput)
|
|
746
|
-
return { outcome: "completed", diagnostic: null };
|
|
747
|
-
if (displayInput.startsWith("/"))
|
|
748
|
-
return this.#slashCommand(displayInput);
|
|
749
|
-
const prepared = this.root.preparePromptSubmission(displayInput);
|
|
750
|
-
assertPromptImages(prepared.images);
|
|
751
|
-
const input = prepared.text.trim();
|
|
752
|
-
if (input.startsWith("!")) {
|
|
753
|
-
const excludeFromContext = input.startsWith("!!");
|
|
754
|
-
const command = input.slice(excludeFromContext ? 2 : 1).trim();
|
|
755
|
-
if (command) {
|
|
756
|
-
this.#rememberInput(displayInput, "bash");
|
|
757
|
-
try {
|
|
758
|
-
const result = await this.backend.workflows.executeBashWorkflow(command, excludeFromContext);
|
|
759
|
-
const workflow = {
|
|
760
|
-
command: "debug",
|
|
761
|
-
outcome: result.cancelled ? "cancelled" : result.exitCode === 0 || result.exitCode === undefined ? "completed" : "failed",
|
|
762
|
-
message: result.cancelled ? "Bash command cancelled" : `Bash exited ${result.exitCode ?? 0}: ${command}`,
|
|
763
|
-
...(result.output ? { detail: result.output } : {}),
|
|
764
|
-
};
|
|
765
|
-
this.root.appendWorkflowResult(workflow);
|
|
766
|
-
this.#terminal.requestRender();
|
|
767
|
-
return workflow.outcome === "failed" ? rejected(workflow.message) : { outcome: "completed", diagnostic: null };
|
|
768
|
-
}
|
|
769
|
-
catch (error) {
|
|
770
|
-
const message = `Bash command failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
771
|
-
this.root.appendWorkflowResult({ command: "debug", outcome: "failed", message });
|
|
772
|
-
this.#terminal.requestRender();
|
|
773
|
-
return rejected(message);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
// Compatibility: match interactive Pi: input during compaction is queued steering; the engine
|
|
778
|
-
// shows it in the pending rows and delivers it when compaction ends.
|
|
779
|
-
const type = this.view().lifecycle === "busy" ? "steer" : "prompt";
|
|
780
|
-
this.#rememberInput(this.root.omitUnsentPromptImages(displayInput).trim(), type);
|
|
781
|
-
this.root.resumeViewportFollowing();
|
|
782
|
-
const finishSending = prepared.images.length === 0 ? () => { } : this.root.beginImageSubmissionStatus();
|
|
783
|
-
try {
|
|
784
|
-
return await this.#execute({
|
|
785
|
-
type,
|
|
786
|
-
correlationId: this.#correlation(type),
|
|
787
|
-
sessionId: this.backend.identity.sessionId,
|
|
788
|
-
text: input,
|
|
789
|
-
...(prepared.images.length === 0 ? {} : { images: prepared.images }),
|
|
790
|
-
}, displayInput);
|
|
791
|
-
}
|
|
792
|
-
finally {
|
|
793
|
-
finishSending();
|
|
794
|
-
}
|
|
549
|
+
return this.#submission.submit(text);
|
|
795
550
|
}
|
|
796
551
|
async clearOrExit(now = Date.now()) {
|
|
797
552
|
this.#promptSuggestions?.abortPending();
|
|
798
|
-
if (now - this.#lastClearTime <
|
|
553
|
+
if (now - this.#lastClearTime < CLEAR_EXIT_INTERVAL_MS)
|
|
799
554
|
return this.shutdown();
|
|
800
555
|
this.root.editor.setText("");
|
|
801
556
|
this.#lastClearTime = now;
|
|
@@ -804,8 +559,8 @@ export class OwnedUiSessionPresenter {
|
|
|
804
559
|
}
|
|
805
560
|
async interrupt(now = Date.now()) {
|
|
806
561
|
this.#promptSuggestions?.invalidate();
|
|
807
|
-
if (this.#
|
|
808
|
-
this.#
|
|
562
|
+
if (this.#submission.waiting) {
|
|
563
|
+
this.#submission.cancelWaiting();
|
|
809
564
|
return { outcome: "completed", diagnostic: null };
|
|
810
565
|
}
|
|
811
566
|
if (this.view().lifecycle === "busy")
|
|
@@ -832,23 +587,23 @@ export class OwnedUiSessionPresenter {
|
|
|
832
587
|
return { outcome: "completed", diagnostic: null };
|
|
833
588
|
}
|
|
834
589
|
async abort() {
|
|
835
|
-
return this.#execute(this.#simple("abort"));
|
|
590
|
+
return this.#submission.execute(this.#simple("abort"));
|
|
836
591
|
}
|
|
837
592
|
async retry() {
|
|
838
593
|
this.#promptSuggestions?.invalidate();
|
|
839
|
-
return this.#execute(this.#simple("retry"));
|
|
594
|
+
return this.#submission.execute(this.#simple("retry"));
|
|
840
595
|
}
|
|
841
596
|
async compact() {
|
|
842
597
|
this.#promptSuggestions?.invalidate();
|
|
843
|
-
return this.#execute(this.#simple("compact"));
|
|
598
|
+
return this.#submission.execute(this.#simple("compact"));
|
|
844
599
|
}
|
|
845
600
|
async newSession() {
|
|
846
601
|
this.#promptSuggestions?.invalidate();
|
|
847
|
-
return this.#execute(this.#simple("new-session"));
|
|
602
|
+
return this.#submission.execute(this.#simple("new-session"));
|
|
848
603
|
}
|
|
849
604
|
async resumeSession(sessionPath) {
|
|
850
605
|
this.#promptSuggestions?.invalidate();
|
|
851
|
-
return this.#execute({
|
|
606
|
+
return this.#submission.execute({
|
|
852
607
|
type: "resume-session",
|
|
853
608
|
correlationId: this.#correlation("resume"),
|
|
854
609
|
sessionId: this.backend.identity.sessionId,
|
|
@@ -857,7 +612,7 @@ export class OwnedUiSessionPresenter {
|
|
|
857
612
|
}
|
|
858
613
|
async setModel(providerId, modelId) {
|
|
859
614
|
this.#promptSuggestions?.invalidate();
|
|
860
|
-
return this.#execute({
|
|
615
|
+
return this.#submission.execute({
|
|
861
616
|
type: "set-model",
|
|
862
617
|
correlationId: this.#correlation("model"),
|
|
863
618
|
sessionId: this.backend.identity.sessionId,
|
|
@@ -865,7 +620,7 @@ export class OwnedUiSessionPresenter {
|
|
|
865
620
|
});
|
|
866
621
|
}
|
|
867
622
|
async setThinkingLevel(thinkingLevel) {
|
|
868
|
-
return this.#execute({
|
|
623
|
+
return this.#submission.execute({
|
|
869
624
|
type: "set-thinking-level",
|
|
870
625
|
correlationId: this.#correlation("thinking"),
|
|
871
626
|
sessionId: this.backend.identity.sessionId,
|
|
@@ -884,64 +639,13 @@ export class OwnedUiSessionPresenter {
|
|
|
884
639
|
return workflowAdapterResult(result);
|
|
885
640
|
}
|
|
886
641
|
async queueFollowUp() {
|
|
887
|
-
|
|
888
|
-
return this.#submitWhenReady(draft, () => this.#queueFollowUp(draft));
|
|
889
|
-
}
|
|
890
|
-
async #queueFollowUp(draft) {
|
|
891
|
-
const displayInput = draft.trim();
|
|
892
|
-
if (!displayInput)
|
|
893
|
-
return rejected("nothing to queue");
|
|
894
|
-
const prepared = this.root.preparePromptSubmission(displayInput);
|
|
895
|
-
assertPromptImages(prepared.images);
|
|
896
|
-
const text = prepared.text.trim();
|
|
897
|
-
this.#rememberInput(displayInput, "follow-up");
|
|
898
|
-
if (this.root.editor.getText() === draft)
|
|
899
|
-
this.root.editor.setText("");
|
|
900
|
-
this.root.resumeViewportFollowing();
|
|
901
|
-
return this.#execute({
|
|
902
|
-
type: "follow-up",
|
|
903
|
-
correlationId: this.#correlation("follow-up"),
|
|
904
|
-
sessionId: this.backend.identity.sessionId,
|
|
905
|
-
text,
|
|
906
|
-
...(prepared.images.length === 0 ? {} : { images: prepared.images }),
|
|
907
|
-
}, displayInput);
|
|
642
|
+
return this.#submission.queueFollowUp(this.root.editor.getText());
|
|
908
643
|
}
|
|
909
644
|
restoreQueuedInput() {
|
|
910
|
-
|
|
911
|
-
this.#cancelWaitingImages();
|
|
912
|
-
if (queued.length === 0)
|
|
913
|
-
return;
|
|
914
|
-
this.root.editor.setText(queued.join("\n"));
|
|
915
|
-
this.#terminal.requestRender();
|
|
645
|
+
this.#submission.restoreQueued();
|
|
916
646
|
}
|
|
917
647
|
showSelector(title, options, onSelect, onCancel) {
|
|
918
|
-
this.#
|
|
919
|
-
this.#dialogSource = "local";
|
|
920
|
-
this.#dialogId = undefined;
|
|
921
|
-
const component = createPiShellSelector({
|
|
922
|
-
title,
|
|
923
|
-
options,
|
|
924
|
-
onSelect: id => {
|
|
925
|
-
onSelect(id);
|
|
926
|
-
this.#dialogHandle?.hide();
|
|
927
|
-
this.#dialogHandle = undefined;
|
|
928
|
-
this.#dialogSource = undefined;
|
|
929
|
-
},
|
|
930
|
-
onCancel: () => {
|
|
931
|
-
this.#dialogHandle?.hide();
|
|
932
|
-
this.#dialogHandle = undefined;
|
|
933
|
-
this.#dialogSource = undefined;
|
|
934
|
-
onCancel?.();
|
|
935
|
-
},
|
|
936
|
-
});
|
|
937
|
-
const handle = this.#terminal.showOverlay(component, {
|
|
938
|
-
width: "70%",
|
|
939
|
-
maxHeight: "80%",
|
|
940
|
-
anchor: "center",
|
|
941
|
-
inputCoordination: "owned",
|
|
942
|
-
});
|
|
943
|
-
this.#dialogHandle = handle;
|
|
944
|
-
return handle;
|
|
648
|
+
return this.#dialogs.showSelector(title, options, onSelect, onCancel);
|
|
945
649
|
}
|
|
946
650
|
// Rationale: presenters see the shell only as a dialog host; selectors open in the input dock and run choices as workflows.
|
|
947
651
|
#createDialogHost() {
|
|
@@ -1009,15 +713,12 @@ export class OwnedUiSessionPresenter {
|
|
|
1009
713
|
}
|
|
1010
714
|
/** The Skills dialog: a replacement input like the model selector, applying the chosen skill through the prompt path. */
|
|
1011
715
|
showSkillsSelector(skills = this.#skillSummaries()) {
|
|
1012
|
-
const close =
|
|
1013
|
-
this.root.setInputSurface(null);
|
|
1014
|
-
this.#terminal.requestRender();
|
|
1015
|
-
};
|
|
716
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1016
717
|
const component = createPiShellSkillsSelector({
|
|
1017
718
|
skills,
|
|
1018
719
|
onSelect: name => {
|
|
1019
720
|
close();
|
|
1020
|
-
void this.#
|
|
721
|
+
void this.#submission.sendSlashPrompt(skillPrompt(name), skillPrompt(name)).catch(() => this.#submission.reportError());
|
|
1021
722
|
},
|
|
1022
723
|
onCancel: close,
|
|
1023
724
|
});
|
|
@@ -1034,10 +735,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1034
735
|
this.#terminal.requestRender();
|
|
1035
736
|
return;
|
|
1036
737
|
}
|
|
1037
|
-
const close =
|
|
1038
|
-
this.root.setInputSurface(null);
|
|
1039
|
-
this.#terminal.requestRender();
|
|
1040
|
-
};
|
|
738
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1041
739
|
const component = createPiShellUserMessageSelector(options, selection => {
|
|
1042
740
|
close();
|
|
1043
741
|
void this.runWorkflow({ command: "fork", argument: "", selection });
|
|
@@ -1064,10 +762,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1064
762
|
this.#terminal.requestRender();
|
|
1065
763
|
return;
|
|
1066
764
|
}
|
|
1067
|
-
const close =
|
|
1068
|
-
this.root.setInputSurface(null);
|
|
1069
|
-
this.#terminal.requestRender();
|
|
1070
|
-
};
|
|
765
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1071
766
|
const component = createPiShellAuthProviderSelector("logout", options, selection => {
|
|
1072
767
|
close();
|
|
1073
768
|
void this.runWorkflow({ command: "logout", argument: "", selection });
|
|
@@ -1087,10 +782,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1087
782
|
void this.runWorkflow({ command: "login", argument: "", selection });
|
|
1088
783
|
return;
|
|
1089
784
|
}
|
|
1090
|
-
const close =
|
|
1091
|
-
this.root.setInputSurface(null);
|
|
1092
|
-
this.#terminal.requestRender();
|
|
1093
|
-
};
|
|
785
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1094
786
|
const labels = method.options.map(option => option.label);
|
|
1095
787
|
const component = createPiShellExtensionSelector(method.title, labels, label => {
|
|
1096
788
|
const selection = method.options.find(option => option.label === label)?.id;
|
|
@@ -1108,10 +800,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1108
800
|
}, initialSelectedId);
|
|
1109
801
|
}
|
|
1110
802
|
showLoginAuthTypeSelector() {
|
|
1111
|
-
const close =
|
|
1112
|
-
this.root.setInputSurface(null);
|
|
1113
|
-
this.#terminal.requestRender();
|
|
1114
|
-
};
|
|
803
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1115
804
|
const labels = ["Sign in with an account", "Sign in with an API key"];
|
|
1116
805
|
const component = createPiShellExtensionSelector("Select authentication method:", labels, label => {
|
|
1117
806
|
close();
|
|
@@ -1127,10 +816,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1127
816
|
this.#terminal.requestRender();
|
|
1128
817
|
return;
|
|
1129
818
|
}
|
|
1130
|
-
const close =
|
|
1131
|
-
this.root.setInputSurface(null);
|
|
1132
|
-
this.#terminal.requestRender();
|
|
1133
|
-
};
|
|
819
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1134
820
|
const component = createPiShellAuthProviderSelector("login", options, id => {
|
|
1135
821
|
close();
|
|
1136
822
|
void this.runWorkflow({ command: "login", argument: "", selection: id });
|
|
@@ -1149,10 +835,22 @@ export class OwnedUiSessionPresenter {
|
|
|
1149
835
|
this.#presenters.openSettingsSelector(this.#dialogHost);
|
|
1150
836
|
}
|
|
1151
837
|
shutdown() {
|
|
1152
|
-
this.#shutdownPromise ??= this.#shutdown()
|
|
838
|
+
this.#shutdownPromise ??= this.#shutdown().finally(() => {
|
|
839
|
+
// Invariant: a rejected resident detach is retryable; ordinary shutdown has disposed the shell already.
|
|
840
|
+
if (!this.#disposed)
|
|
841
|
+
this.#shutdownPromise = undefined;
|
|
842
|
+
});
|
|
1153
843
|
return this.#shutdownPromise;
|
|
1154
844
|
}
|
|
1155
845
|
async #shutdown() {
|
|
846
|
+
const detached = await this.#resident.detach();
|
|
847
|
+
if (detached === true)
|
|
848
|
+
return { outcome: "completed", diagnostic: null };
|
|
849
|
+
if (detached !== undefined) {
|
|
850
|
+
this.root.appendWorkflowStatus(detached);
|
|
851
|
+
this.#terminal.requestRender();
|
|
852
|
+
return rejected("resident tab detach is unavailable");
|
|
853
|
+
}
|
|
1156
854
|
try {
|
|
1157
855
|
const quit = this.backend.workflows.executeWorkflow({ command: "quit", argument: "" });
|
|
1158
856
|
// Invariant: a slow or hung engine quit never holds the terminal past the cleanup deadline. The
|
|
@@ -1165,9 +863,16 @@ export class OwnedUiSessionPresenter {
|
|
|
1165
863
|
return workflowAdapterResult(result);
|
|
1166
864
|
}
|
|
1167
865
|
finally {
|
|
1168
|
-
this.#
|
|
866
|
+
this.#stop.resolve();
|
|
1169
867
|
}
|
|
1170
868
|
}
|
|
869
|
+
#currentSessionFile() {
|
|
870
|
+
const direct = this.backend.identity.currentSessionFile?.();
|
|
871
|
+
if (direct !== undefined && direct !== null)
|
|
872
|
+
return direct;
|
|
873
|
+
const resume = this.backend.identity.currentSessionResumeMetadata();
|
|
874
|
+
return resume === null ? null : `${resume.sessionDir}/${resume.sessionId}`;
|
|
875
|
+
}
|
|
1171
876
|
async runWorkflow(request) {
|
|
1172
877
|
if (request.command === "quit")
|
|
1173
878
|
return this.shutdown();
|
|
@@ -1188,68 +893,17 @@ export class OwnedUiSessionPresenter {
|
|
|
1188
893
|
if (request.command === "login" && request.selection !== undefined) {
|
|
1189
894
|
const setup = this.backend.catalog.pinnedAmbientAuthentication(request.selection);
|
|
1190
895
|
if (setup) {
|
|
1191
|
-
const close =
|
|
1192
|
-
|
|
1193
|
-
this.#terminal.requestRender();
|
|
1194
|
-
};
|
|
1195
|
-
const dialog = createPiShellLoginDialog({
|
|
1196
|
-
getColumns: () => this.#terminal.viewport().columns,
|
|
1197
|
-
getRows: () => this.#terminal.viewport().rows,
|
|
1198
|
-
requestRender: () => this.#terminal.requestRender(),
|
|
1199
|
-
}, setup.providerId, close, setup.providerName, setup.title);
|
|
896
|
+
const close = this.#dialogs.closeInputSurface;
|
|
897
|
+
const dialog = createPiShellLoginDialog(componentRuntime(this.#terminal), setup.providerId, close, setup.providerName, setup.title);
|
|
1200
898
|
dialog.showInfo(setup.message, [], true);
|
|
1201
899
|
this.root.setInputSurface(dialog);
|
|
1202
900
|
this.#terminal.requestRender();
|
|
1203
901
|
return { outcome: "completed", diagnostic: null };
|
|
1204
902
|
}
|
|
1205
903
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
return
|
|
1209
|
-
}
|
|
1210
|
-
if (request.command === "models" && request.selection === undefined) {
|
|
1211
|
-
this.showModelsDialog(request.argument.trim() || undefined);
|
|
1212
|
-
return { outcome: "completed", diagnostic: null };
|
|
1213
|
-
}
|
|
1214
|
-
if (request.command === "model" && request.selection === undefined && request.confirmed === undefined && request.argument.trim().length === 0) {
|
|
1215
|
-
this.showModelSelector();
|
|
1216
|
-
return { outcome: "completed", diagnostic: null };
|
|
1217
|
-
}
|
|
1218
|
-
if (request.command === "fork" && request.selection === undefined && request.confirmed === undefined) {
|
|
1219
|
-
this.showForkSelector();
|
|
1220
|
-
return { outcome: "completed", diagnostic: null };
|
|
1221
|
-
}
|
|
1222
|
-
if (request.command === "thinking" && request.selection === undefined && request.argument.trim().length === 0) {
|
|
1223
|
-
await this.showThinkingSelector();
|
|
1224
|
-
return { outcome: "completed", diagnostic: null };
|
|
1225
|
-
}
|
|
1226
|
-
if (request.command === "trust" && request.selection === undefined && request.confirmed === undefined) {
|
|
1227
|
-
this.showTrustSelector();
|
|
1228
|
-
return { outcome: "completed", diagnostic: null };
|
|
1229
|
-
}
|
|
1230
|
-
if (request.command === "settings" && request.selection === undefined && request.confirmed === undefined) {
|
|
1231
|
-
this.showSettingsSelector();
|
|
1232
|
-
return { outcome: "completed", diagnostic: null };
|
|
1233
|
-
}
|
|
1234
|
-
if (request.command === "resume" && request.selection === undefined && request.confirmed === undefined && request.argument.trim().length === 0) {
|
|
1235
|
-
await this.showSessionSelector();
|
|
1236
|
-
return { outcome: "completed", diagnostic: null };
|
|
1237
|
-
}
|
|
1238
|
-
if (request.command === "login" && request.selection === undefined && request.confirmed === undefined) {
|
|
1239
|
-
if (request.argument.trim().length === 0)
|
|
1240
|
-
this.showLoginAuthTypeSelector();
|
|
1241
|
-
else
|
|
1242
|
-
this.showLoginMethodSelector(request.argument);
|
|
1243
|
-
return { outcome: "completed", diagnostic: null };
|
|
1244
|
-
}
|
|
1245
|
-
if (request.command === "logout" && request.selection === undefined && request.confirmed === undefined) {
|
|
1246
|
-
await this.showLogoutSelector();
|
|
1247
|
-
return { outcome: "completed", diagnostic: null };
|
|
1248
|
-
}
|
|
1249
|
-
if (request.command === "tree" && request.selection === undefined && request.confirmed === undefined && request.argument.trim().length === 0) {
|
|
1250
|
-
await this.showTreeSelector();
|
|
1251
|
-
return { outcome: "completed", diagnostic: null };
|
|
1252
|
-
}
|
|
904
|
+
const selected = openWorkflowSelector(request, this, { residentTab: this.#resident.active });
|
|
905
|
+
if (selected !== undefined)
|
|
906
|
+
return selected;
|
|
1253
907
|
if (request.command === "reload") {
|
|
1254
908
|
const blocked = this.backend.workflows.reloadBlockedResult();
|
|
1255
909
|
if (blocked) {
|
|
@@ -1263,11 +917,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1263
917
|
? await import("./share-operation.js")
|
|
1264
918
|
: undefined;
|
|
1265
919
|
const shareSurface = shareDialog === undefined ? undefined
|
|
1266
|
-
: (this.#customViewport ? shareDialog.createPiShellShareOperationDialog : shareDialog.createPiShellOperationLoader)(
|
|
1267
|
-
getColumns: () => this.#terminal.viewport().columns,
|
|
1268
|
-
getRows: () => this.#terminal.viewport().rows,
|
|
1269
|
-
requestRender: () => this.#terminal.requestRender(),
|
|
1270
|
-
}, "Creating gist…");
|
|
920
|
+
: (this.#customViewport ? shareDialog.createPiShellShareOperationDialog : shareDialog.createPiShellOperationLoader)(componentRuntime(this.#terminal), "Creating gist…");
|
|
1271
921
|
const operationSurface = shareSurface ?? (request.command === "reload" ? createPiShellReloadBox() : undefined);
|
|
1272
922
|
const now = this.#reloadPresentation?.now ?? Date.now;
|
|
1273
923
|
const shownAt = now();
|
|
@@ -1277,7 +927,11 @@ export class OwnedUiSessionPresenter {
|
|
|
1277
927
|
}
|
|
1278
928
|
let result;
|
|
1279
929
|
try {
|
|
1280
|
-
result = await this.backend.workflows.executeWorkflow(shareSurface === undefined ? request : {
|
|
930
|
+
result = await this.backend.workflows.executeWorkflow(shareSurface === undefined ? request : {
|
|
931
|
+
...request,
|
|
932
|
+
signal: shareSurface.signal,
|
|
933
|
+
onProgress: message => { shareSurface.setMessage?.(message); this.#terminal.requestRender(); },
|
|
934
|
+
});
|
|
1281
935
|
}
|
|
1282
936
|
finally {
|
|
1283
937
|
if (operationSurface) {
|
|
@@ -1347,10 +1001,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1347
1001
|
}
|
|
1348
1002
|
showTrustSelector() {
|
|
1349
1003
|
const context = this.backend.catalog.pinnedProjectTrustContext();
|
|
1350
|
-
const close =
|
|
1351
|
-
this.root.setInputSurface(null);
|
|
1352
|
-
this.#terminal.requestRender();
|
|
1353
|
-
};
|
|
1004
|
+
const close = this.#dialogs.closeInputSurface;
|
|
1354
1005
|
const component = createPiShellTrustSelector({
|
|
1355
1006
|
...context,
|
|
1356
1007
|
onSelect: selection => {
|
|
@@ -1414,18 +1065,13 @@ export class OwnedUiSessionPresenter {
|
|
|
1414
1065
|
this.#copyIntentSequence += 1;
|
|
1415
1066
|
this.#responseCopy?.dispose();
|
|
1416
1067
|
this.#copyExecutor?.dispose();
|
|
1417
|
-
this.#
|
|
1068
|
+
this.#submission.cancelWaiting();
|
|
1418
1069
|
let pasteCleanup = Promise.resolve();
|
|
1419
1070
|
let historyCleanup = Promise.resolve(true);
|
|
1420
1071
|
attempt(() => { historyCleanup = this.#promptHistory?.close() ?? Promise.resolve(true); });
|
|
1421
1072
|
attempt(() => { pasteCleanup = this.root.disposePendingPastes(); });
|
|
1422
1073
|
attempt(() => this.root.clearViewportPointerState());
|
|
1423
1074
|
attempt(() => this.#streamPresentation.dispose());
|
|
1424
|
-
attempt(() => {
|
|
1425
|
-
if (this.#startupRouteTimer !== undefined)
|
|
1426
|
-
clearTimeout(this.#startupRouteTimer);
|
|
1427
|
-
this.#startupRouteTimer = undefined;
|
|
1428
|
-
});
|
|
1429
1075
|
attempt(() => this.#promptSuggestions?.dispose());
|
|
1430
1076
|
attempt(() => this.#unsubscribePromptSuggestions());
|
|
1431
1077
|
attempt(() => this.#unsubscribePromptImages());
|
|
@@ -1433,21 +1079,18 @@ export class OwnedUiSessionPresenter {
|
|
|
1433
1079
|
attempt(() => this.#unsubscribeSettings());
|
|
1434
1080
|
let exitText = "";
|
|
1435
1081
|
attempt(() => {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
: [this.root.exitTranscript(terminal.columns), resumeHint].filter(Boolean).join("\n\n");
|
|
1082
|
+
exitText = composeExitText({
|
|
1083
|
+
terminalMode: terminal.mode,
|
|
1084
|
+
customViewport: this.#customViewport,
|
|
1085
|
+
exitMode: this.backend.identity.disposed ? this.#fullscreenExitOutput : this.backend.settings.snapshot().fullscreenExitOutput,
|
|
1086
|
+
resume: this.backend.identity.currentSessionResumeMetadata(),
|
|
1087
|
+
transcript: () => this.root.exitTranscript(terminal.columns),
|
|
1088
|
+
});
|
|
1444
1089
|
});
|
|
1445
1090
|
attempt(() => this.#unbindClipboardWriter());
|
|
1446
|
-
attempt(() => this.#
|
|
1447
|
-
attempt(() => this.#unbindTerminalSettings());
|
|
1448
|
-
attempt(() => this.#unbindShutdownSettings());
|
|
1091
|
+
attempt(() => this.#unbindSessionSettings());
|
|
1449
1092
|
attempt(() => this.backend.workflows.setWorkflowInteractionHost(DETACHED_WORKFLOW_INTERACTION));
|
|
1450
|
-
|
|
1093
|
+
this.#concerns.dispose(attempt);
|
|
1451
1094
|
attempt(() => this.#extensionBridge.dispose());
|
|
1452
1095
|
attempt(() => this.#programStatus.clear());
|
|
1453
1096
|
// Invariant: the host settles a presenter only once the terminal is restored, so these never stall it.
|
|
@@ -1461,20 +1104,14 @@ export class OwnedUiSessionPresenter {
|
|
|
1461
1104
|
}
|
|
1462
1105
|
await historyCleanup.catch(() => false); // Security: background durability outcomes never enter terminal output.
|
|
1463
1106
|
await boundedCleanup(() => pasteCleanup).catch(error => settled.push(error));
|
|
1107
|
+
await boundedCleanup(() => this.#resident.gracefulStop(this.#currentSessionFile()))
|
|
1108
|
+
.catch(error => settled.push(error));
|
|
1464
1109
|
await boundedCleanup(() => this.backend.extensions.unbindExtensionUi()).catch(error => settled.push(error));
|
|
1465
1110
|
return settled;
|
|
1466
1111
|
};
|
|
1467
1112
|
this.#released = { exitText, failures, settle };
|
|
1468
1113
|
return this.#released;
|
|
1469
1114
|
}
|
|
1470
|
-
#settleStoppedLifecycle() {
|
|
1471
|
-
void this.dispose().then(() => this.#resolveStoppedLifecycle(), () => this.#resolveStoppedLifecycle());
|
|
1472
|
-
}
|
|
1473
|
-
#resolveStoppedLifecycle() {
|
|
1474
|
-
const resolve = this.#resolveStopped;
|
|
1475
|
-
this.#resolveStopped = undefined;
|
|
1476
|
-
resolve?.();
|
|
1477
|
-
}
|
|
1478
1115
|
// Invariant: incremental block updates notify listeners with the same complete view contract.
|
|
1479
1116
|
#syncBlock(block) {
|
|
1480
1117
|
this.root.applyTranscriptBlock(block);
|
|
@@ -1492,6 +1129,121 @@ export class OwnedUiSessionPresenter {
|
|
|
1492
1129
|
listener(view);
|
|
1493
1130
|
return view;
|
|
1494
1131
|
}
|
|
1132
|
+
// Rationale: extensions reach the shell only through this bridge; every surface they draw lands in the root.
|
|
1133
|
+
#createExtensionBridge() {
|
|
1134
|
+
return createPiExtensionUiBridge({
|
|
1135
|
+
runtime: componentRuntime(this.#terminal),
|
|
1136
|
+
agentDir: this.backend.identity.agentDir,
|
|
1137
|
+
setInputSurface: component => this.root.setInputSurface(component, true, "opaque"),
|
|
1138
|
+
setProgramStatusBlocked: status => this.#programStatus.setBlocked("extension-dialog", status),
|
|
1139
|
+
showOverlay: (component, overlayOptions) => this.#terminal.showOverlay(component, overlayOptions),
|
|
1140
|
+
listenInput: handler => this.#terminal.addInputListener(handler),
|
|
1141
|
+
replaceWidget: (key, component, placement) => this.root.setExtensionWidget(key, component, placement),
|
|
1142
|
+
replaceHeader: component => this.root.setExtensionHeader(component),
|
|
1143
|
+
replaceFooter: component => this.root.setExtensionFooter(component),
|
|
1144
|
+
setStatus: (key, text) => this.root.setExtensionStatus(key, text),
|
|
1145
|
+
setWorking: (message, visible) => this.root.setExtensionWorking(message, visible),
|
|
1146
|
+
notify: (message, type) => this.root.addExtensionNotification(message, type),
|
|
1147
|
+
setTitle: title => this.#terminal.setTitle(title),
|
|
1148
|
+
getEditorText: () => this.root.editor.getText(),
|
|
1149
|
+
setEditorText: text => this.root.editor.setText(text),
|
|
1150
|
+
pasteToEditor: text => this.root.editor.insertText(text),
|
|
1151
|
+
addAutocompleteProvider: factory => this.root.editor.addAutocompleteProvider(factory),
|
|
1152
|
+
setCustomEditor: component => this.root.setInputSurface(component, true, "opaque"),
|
|
1153
|
+
getFooterData: () => this.root.extensionFooterData(),
|
|
1154
|
+
getToolsExpanded: () => this.root.toolsExpanded,
|
|
1155
|
+
setToolsExpanded: expanded => this.root.setToolsExpanded(expanded),
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
#createSubmission(cwd) {
|
|
1159
|
+
return new PromptSubmission({
|
|
1160
|
+
backend: this.backend,
|
|
1161
|
+
root: this.root,
|
|
1162
|
+
terminal: this.#terminal,
|
|
1163
|
+
residentTab: this.#resident,
|
|
1164
|
+
cwd,
|
|
1165
|
+
promptHistory: () => this.#promptHistory,
|
|
1166
|
+
imageSidecar: () => this.#historyBinding?.imageSidecar,
|
|
1167
|
+
disposed: () => this.#disposed,
|
|
1168
|
+
correlation: prefix => this.#correlation(prefix),
|
|
1169
|
+
lifecycle: () => this.view().lifecycle,
|
|
1170
|
+
currentSessionFile: () => this.#currentSessionFile(),
|
|
1171
|
+
slashCommand: text => this.#slashCommand(text),
|
|
1172
|
+
invalidateSuggestions: () => this.#promptSuggestions?.invalidate(),
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
// Invariant: the order is the reset order on session replacement; copy intent before pastes before the input surface.
|
|
1176
|
+
#createConcerns() {
|
|
1177
|
+
return new SessionConcerns([
|
|
1178
|
+
{ onSessionReplaced: () => { this.#copyIntentSequence += 1; this.#responseCopy?.reset(); } },
|
|
1179
|
+
this.#submission,
|
|
1180
|
+
{ onSessionReplaced: () => this.root.resetPendingPastes() },
|
|
1181
|
+
{ onSessionReplaced: () => this.#promptSuggestions?.invalidate() },
|
|
1182
|
+
{ onSessionReplaced: () => { this.#sessionGeneration = this.backend.identity.sessionGeneration; this.#programStatus.reset(); } },
|
|
1183
|
+
{ onSessionReplaced: () => this.#rebindTranscript() },
|
|
1184
|
+
this.#login,
|
|
1185
|
+
{ onSessionReplaced: () => this.#extensionBridge.reset() },
|
|
1186
|
+
// Invariant: a replaced session takes its transient viewport and owned-route state with it.
|
|
1187
|
+
{ onSessionReplaced: () => this.root.resetViewport() },
|
|
1188
|
+
this.#dialogs,
|
|
1189
|
+
{ onSessionReplaced: () => this.root.setInputSurface(null) },
|
|
1190
|
+
{ onSessionReplaced: () => this.root.resetExtensionUi() },
|
|
1191
|
+
{ onSessionReplaced: () => this.root.resetWorkflowPresentation() },
|
|
1192
|
+
this.#resident,
|
|
1193
|
+
]);
|
|
1194
|
+
}
|
|
1195
|
+
// Invariant: delivery recovery does not replace a store; conversation or live scope changes can.
|
|
1196
|
+
#rebindTranscript(scope) {
|
|
1197
|
+
const replaced = this.#sessionBindingGeneration !== this.backend.identity.sessionBindingGeneration;
|
|
1198
|
+
if (!replaced && scope === undefined)
|
|
1199
|
+
return;
|
|
1200
|
+
if (replaced) {
|
|
1201
|
+
this.#sessionBindingGeneration = this.backend.identity.sessionBindingGeneration;
|
|
1202
|
+
this.root.resetTranscript();
|
|
1203
|
+
}
|
|
1204
|
+
const binding = this.#historyBinding;
|
|
1205
|
+
if (binding?.options.forSession !== undefined || binding?.options.forScope !== undefined) {
|
|
1206
|
+
const sessionId = this.backend.identity.currentHistorySessionId();
|
|
1207
|
+
const selectedScope = scope ?? binding.scope;
|
|
1208
|
+
if (selectedScope !== binding.scope || sessionId !== binding.sessionId) {
|
|
1209
|
+
if (!replaced)
|
|
1210
|
+
this.root.editor.recall?.restoreDraft?.();
|
|
1211
|
+
const previous = this.#promptHistory;
|
|
1212
|
+
this.#promptHistory = null;
|
|
1213
|
+
binding.imageSidecar = undefined;
|
|
1214
|
+
binding.scope = selectedScope;
|
|
1215
|
+
binding.sessionId = sessionId ?? undefined;
|
|
1216
|
+
void previous?.close().catch(() => false);
|
|
1217
|
+
const destination = binding.options.forScope !== undefined
|
|
1218
|
+
? binding.options.forScope(selectedScope, sessionId)
|
|
1219
|
+
: sessionId === null ? null : binding.options.forSession(sessionId);
|
|
1220
|
+
if (destination !== null) {
|
|
1221
|
+
binding.imageSidecar = destination.imageSidecar;
|
|
1222
|
+
const controller = new PromptHistoryController({
|
|
1223
|
+
editor: this.root.editor,
|
|
1224
|
+
store: destination.store,
|
|
1225
|
+
limit: binding.options.limit,
|
|
1226
|
+
fallback: replaced ? this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []) : [],
|
|
1227
|
+
active: () => this.root.usesDefaultInputSurface(),
|
|
1228
|
+
render: () => this.#terminal.requestRender(),
|
|
1229
|
+
rehydrate: value => this.root.rehydrateHistoryText(value, id => this.#historyBinding?.imageSidecar?.readAttachment(id) ?? null),
|
|
1230
|
+
});
|
|
1231
|
+
this.#promptHistory = controller;
|
|
1232
|
+
if (this.#started)
|
|
1233
|
+
controller.start();
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
// Security: missing identity must not reopen Shared or retain another session's recall.
|
|
1237
|
+
const fallback = replaced ? this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []).reverse() : [];
|
|
1238
|
+
this.root.editor.recall?.reset();
|
|
1239
|
+
this.root.editor.recall?.replace(fallback);
|
|
1240
|
+
}
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (replaced)
|
|
1245
|
+
this.#promptHistory?.reset(this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []));
|
|
1246
|
+
}
|
|
1495
1247
|
#syncTerminalProgress(view) {
|
|
1496
1248
|
if (!this.#terminal.running)
|
|
1497
1249
|
return;
|
|
@@ -1511,34 +1263,10 @@ export class OwnedUiSessionPresenter {
|
|
|
1511
1263
|
#syncView() {
|
|
1512
1264
|
this.#streamPresentation.noteImmediatePresentation();
|
|
1513
1265
|
const view = this.view();
|
|
1514
|
-
if (this.backend.identity.sessionGeneration !== this.#sessionGeneration)
|
|
1515
|
-
this.#
|
|
1516
|
-
this.#responseCopy?.reset();
|
|
1517
|
-
this.#cancelWaitingImages();
|
|
1518
|
-
this.root.resetPendingPastes();
|
|
1519
|
-
this.#promptSuggestions?.invalidate();
|
|
1520
|
-
this.#sessionGeneration = this.backend.identity.sessionGeneration;
|
|
1521
|
-
this.#programStatus.reset();
|
|
1522
|
-
// Invariant: delivery recovery invalidates callbacks, not same-session local recall or its draft.
|
|
1523
|
-
if (this.#sessionBindingGeneration !== this.backend.identity.sessionBindingGeneration) {
|
|
1524
|
-
this.#sessionBindingGeneration = this.backend.identity.sessionBindingGeneration;
|
|
1525
|
-
this.root.resetTranscript();
|
|
1526
|
-
this.#promptHistory?.reset(view.transcript.flatMap(block => block.kind === "user" ? [block.text] : []));
|
|
1527
|
-
}
|
|
1528
|
-
this.#activeLoginDialog = undefined;
|
|
1529
|
-
this.#extensionBridge.reset();
|
|
1530
|
-
// Invariant: a replaced session takes its transient viewport and owned-route state with it.
|
|
1531
|
-
this.root.resetViewport();
|
|
1532
|
-
this.#dialogHandle?.hide();
|
|
1533
|
-
this.#dialogHandle = undefined;
|
|
1534
|
-
this.#dialogId = undefined;
|
|
1535
|
-
this.#dialogSource = undefined;
|
|
1536
|
-
this.root.setInputSurface(null);
|
|
1537
|
-
this.root.resetExtensionUi();
|
|
1538
|
-
this.root.resetWorkflowPresentation();
|
|
1539
|
-
}
|
|
1266
|
+
if (this.backend.identity.sessionGeneration !== this.#sessionGeneration)
|
|
1267
|
+
this.#concerns.replaceSession();
|
|
1540
1268
|
this.root.update(view);
|
|
1541
|
-
this.#
|
|
1269
|
+
this.#dialogs.syncBackend(view.dialog);
|
|
1542
1270
|
this.#presentStartupTrustWarning(view);
|
|
1543
1271
|
this.#terminal.requestRender();
|
|
1544
1272
|
for (const listener of this.#listeners)
|
|
@@ -1556,100 +1284,34 @@ export class OwnedUiSessionPresenter {
|
|
|
1556
1284
|
this.#startupTrustHandled = true;
|
|
1557
1285
|
this.root.appendWorkflowMessage({ kind: "warning", message: diagnostic.message });
|
|
1558
1286
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
if (pending === undefined || this.#disposed || !this.#terminal.running)
|
|
1287
|
+
openOwnedRoute(route) {
|
|
1288
|
+
if (this.#disposed)
|
|
1562
1289
|
return;
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
}, 25);
|
|
1568
|
-
this.#startupRouteTimer.unref?.();
|
|
1290
|
+
// Invariant: a route requested before the runtime runs (the resident Settings bridge route arrives
|
|
1291
|
+
// before start()) is retained, latest wins, and presented through the startup-route path once started.
|
|
1292
|
+
if (!this.#terminal.running) {
|
|
1293
|
+
this.#dialogs.retainStartupRoute(route);
|
|
1569
1294
|
return;
|
|
1570
1295
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
#openOwnedRoute(route, input, onClosed) {
|
|
1575
|
-
const surface = this.#routeHost?.open(route, input) ?? null;
|
|
1576
|
-
if (surface === null)
|
|
1577
|
-
return { outcome: "failed", diagnostic: `route is unavailable: ${route}` };
|
|
1578
|
-
if (!this.#terminal.running)
|
|
1579
|
-
return { outcome: "failed", diagnostic: "runtime is not active" };
|
|
1580
|
-
this.root.dismissNotice();
|
|
1581
|
-
this.#dialogHandle?.hide();
|
|
1582
|
-
this.#dialogSource = "route";
|
|
1583
|
-
const handle = this.#terminal.openRoute(surface, {
|
|
1584
|
-
exit: () => { void this.shutdown(); },
|
|
1585
|
-
closed: rendered => {
|
|
1586
|
-
if (this.#dialogHandle === handle) {
|
|
1587
|
-
this.#dialogHandle = undefined;
|
|
1588
|
-
this.#dialogId = undefined;
|
|
1589
|
-
this.#dialogSource = undefined;
|
|
1590
|
-
}
|
|
1591
|
-
if (rendered) {
|
|
1592
|
-
try {
|
|
1593
|
-
void onClosed?.()?.catch(() => undefined);
|
|
1594
|
-
}
|
|
1595
|
-
catch { /* Rationale: a failed acknowledgement stays pending. */ }
|
|
1596
|
-
}
|
|
1597
|
-
},
|
|
1598
|
-
});
|
|
1599
|
-
this.#dialogHandle = handle;
|
|
1600
|
-
this.#dialogId = surface.id;
|
|
1601
|
-
return { outcome: "completed", diagnostic: null };
|
|
1602
|
-
}
|
|
1603
|
-
#syncDialog(dialog) {
|
|
1604
|
-
if (!this.#terminal.running)
|
|
1605
|
-
return;
|
|
1606
|
-
if (dialog === null) {
|
|
1607
|
-
// Invariant: locally owned routes (notably /settings) are independent of backend
|
|
1608
|
-
// lifecycle/status events and remain open while an agent is working.
|
|
1609
|
-
if (this.#dialogSource !== "backend")
|
|
1610
|
-
return;
|
|
1611
|
-
this.#dialogHandle?.hide();
|
|
1612
|
-
this.#dialogHandle = undefined;
|
|
1613
|
-
this.#dialogId = undefined;
|
|
1614
|
-
this.#dialogSource = undefined;
|
|
1615
|
-
return;
|
|
1296
|
+
const result = this.#dialogs.openRoute(route);
|
|
1297
|
+
if (result.outcome !== "completed" && result.diagnostic !== null) {
|
|
1298
|
+
this.root.appendWorkflowMessage({ kind: "warning", message: result.diagnostic });
|
|
1616
1299
|
}
|
|
1617
|
-
if (this.#dialogSource === "backend" && this.#dialogId === dialog.id)
|
|
1618
|
-
return;
|
|
1619
|
-
this.#dialogHandle?.hide();
|
|
1620
|
-
this.#dialogSource = "backend";
|
|
1621
|
-
const component = createPiShellDialog(dialog, {
|
|
1622
|
-
onSelect: () => {
|
|
1623
|
-
this.#dialogHandle?.hide();
|
|
1624
|
-
this.#dialogHandle = undefined;
|
|
1625
|
-
this.#dialogId = undefined;
|
|
1626
|
-
this.#dialogSource = undefined;
|
|
1627
|
-
},
|
|
1628
|
-
onCancel: () => {
|
|
1629
|
-
this.#dialogHandle?.hide();
|
|
1630
|
-
this.#dialogHandle = undefined;
|
|
1631
|
-
this.#dialogId = undefined;
|
|
1632
|
-
this.#dialogSource = undefined;
|
|
1633
|
-
},
|
|
1634
|
-
});
|
|
1635
|
-
this.#dialogHandle = this.#terminal.showOverlay(component, {
|
|
1636
|
-
width: "70%",
|
|
1637
|
-
maxHeight: "80%",
|
|
1638
|
-
anchor: "center",
|
|
1639
|
-
inputCoordination: "owned",
|
|
1640
|
-
});
|
|
1641
|
-
this.#dialogId = dialog.id;
|
|
1642
1300
|
}
|
|
1643
1301
|
hotkeysPresentation() {
|
|
1644
|
-
|
|
1302
|
+
const presentation = this.root.hotkeysPresentation();
|
|
1303
|
+
return this.#resident.active ? { ...presentation, residentTabs: true } : presentation;
|
|
1645
1304
|
}
|
|
1646
1305
|
async #slashCommand(text) {
|
|
1647
1306
|
const body = text.slice(1).trim();
|
|
1648
1307
|
const separator = body.search(/\s/);
|
|
1649
1308
|
const name = separator < 0 ? body : body.slice(0, separator);
|
|
1650
1309
|
const argument = separator < 0 ? "" : body.slice(separator + 1).trimStart();
|
|
1651
|
-
|
|
1652
|
-
|
|
1310
|
+
const utilityTab = this.#resident.active ? RESIDENT_UTILITY_TAB_REFUSALS[name] : undefined;
|
|
1311
|
+
if (utilityTab !== undefined)
|
|
1312
|
+
return rejected(utilityTab);
|
|
1313
|
+
if (this.#dialogs.claimsRoute(name))
|
|
1314
|
+
return this.#dialogs.openRoute(name);
|
|
1653
1315
|
if (this.#isWorkflowRoute(name))
|
|
1654
1316
|
return this.runWorkflow({ command: name, argument });
|
|
1655
1317
|
if (this.#skillsCollapsed()) {
|
|
@@ -1657,17 +1319,10 @@ export class OwnedUiSessionPresenter {
|
|
|
1657
1319
|
return this.#runSkillsCommand(text, argument);
|
|
1658
1320
|
const rewritten = rewriteSkillsTunnelSubmission(text);
|
|
1659
1321
|
if (rewritten !== text)
|
|
1660
|
-
return this.#
|
|
1322
|
+
return this.#submission.sendSlashPrompt(rewritten, text);
|
|
1661
1323
|
}
|
|
1662
1324
|
// Compatibility: unknown slash input, prompt templates, skills, and extension commands remain Pi prompt input.
|
|
1663
|
-
this.#
|
|
1664
|
-
this.root.resumeViewportFollowing();
|
|
1665
|
-
return this.#execute({
|
|
1666
|
-
type: this.view().lifecycle === "busy" ? "steer" : "prompt",
|
|
1667
|
-
correlationId: this.#correlation("prompt-command"),
|
|
1668
|
-
sessionId: this.backend.identity.sessionId,
|
|
1669
|
-
text,
|
|
1670
|
-
});
|
|
1325
|
+
return this.#submission.sendSlashPrompt(text);
|
|
1671
1326
|
}
|
|
1672
1327
|
#installAutocompleteCommands() {
|
|
1673
1328
|
this.root.editor.setAutocompleteCommands(this.backend.workflows.workflowAutocompleteCommands());
|
|
@@ -1701,18 +1356,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1701
1356
|
this.#terminal.requestRender();
|
|
1702
1357
|
return { outcome: "failed", diagnostic: message };
|
|
1703
1358
|
}
|
|
1704
|
-
return this.#
|
|
1705
|
-
}
|
|
1706
|
-
// Rationale: engine expands skill arguments; history retains typed input for recall.
|
|
1707
|
-
#submitSkillPrompt(prompt, typed) {
|
|
1708
|
-
this.#rememberInput(typed, "slash");
|
|
1709
|
-
this.root.resumeViewportFollowing();
|
|
1710
|
-
return this.#execute({
|
|
1711
|
-
type: this.view().lifecycle === "busy" ? "steer" : "prompt",
|
|
1712
|
-
correlationId: this.#correlation("prompt-command"),
|
|
1713
|
-
sessionId: this.backend.identity.sessionId,
|
|
1714
|
-
text: prompt,
|
|
1715
|
-
});
|
|
1359
|
+
return this.#submission.sendSlashPrompt(skillPrompt(skill.name, separator < 0 ? "" : trimmed.slice(separator + 1)), text);
|
|
1716
1360
|
}
|
|
1717
1361
|
#treeSummary() {
|
|
1718
1362
|
return new Promise(resolve => {
|
|
@@ -1753,139 +1397,6 @@ export class OwnedUiSessionPresenter {
|
|
|
1753
1397
|
if (result.diagnostic === "Branch summarization cancelled")
|
|
1754
1398
|
await this.showTreeSelector(entryId);
|
|
1755
1399
|
}
|
|
1756
|
-
#startWorkflowLogin(request) {
|
|
1757
|
-
this.#finishWorkflowLogin();
|
|
1758
|
-
const dialog = createPiShellLoginDialog({
|
|
1759
|
-
getColumns: () => this.#terminal.viewport().columns,
|
|
1760
|
-
getRows: () => this.#terminal.viewport().rows,
|
|
1761
|
-
requestRender: () => this.#terminal.requestRender(),
|
|
1762
|
-
}, request.providerId, success => {
|
|
1763
|
-
if (!success)
|
|
1764
|
-
this.#finishWorkflowLogin();
|
|
1765
|
-
}, request.providerName);
|
|
1766
|
-
this.#activeLoginDialog = dialog;
|
|
1767
|
-
this.root.setInputSurface(dialog);
|
|
1768
|
-
this.#programStatus.setBlocked("authentication", {
|
|
1769
|
-
kind: "auth",
|
|
1770
|
-
message: `Log in to ${request.providerName}`,
|
|
1771
|
-
});
|
|
1772
|
-
this.#syncTerminalProgress(this.view());
|
|
1773
|
-
this.#terminal.requestRender();
|
|
1774
|
-
}
|
|
1775
|
-
#requestWorkflowInput(request) {
|
|
1776
|
-
const dialog = this.#activeLoginDialog;
|
|
1777
|
-
if (!dialog)
|
|
1778
|
-
return Promise.resolve(null);
|
|
1779
|
-
if (request.type === "select") {
|
|
1780
|
-
return new Promise(resolve => {
|
|
1781
|
-
const options = request.options ?? [];
|
|
1782
|
-
const labels = options.map(option => option.label);
|
|
1783
|
-
const restoreDialog = () => {
|
|
1784
|
-
if (this.#activeLoginDialog === dialog)
|
|
1785
|
-
this.root.setInputSurface(dialog);
|
|
1786
|
-
this.#terminal.requestRender();
|
|
1787
|
-
};
|
|
1788
|
-
const selector = createPiShellExtensionSelector(request.message, labels, label => {
|
|
1789
|
-
const id = options.find(option => option.label === label)?.id;
|
|
1790
|
-
restoreDialog();
|
|
1791
|
-
resolve(id ?? null);
|
|
1792
|
-
}, () => {
|
|
1793
|
-
restoreDialog();
|
|
1794
|
-
resolve(null);
|
|
1795
|
-
});
|
|
1796
|
-
this.root.setInputSurface(selector, false);
|
|
1797
|
-
this.#terminal.requestRender();
|
|
1798
|
-
});
|
|
1799
|
-
}
|
|
1800
|
-
const response = request.type === "manual-code"
|
|
1801
|
-
? dialog.showManualInput(request.message)
|
|
1802
|
-
: dialog.showPrompt(request.message, request.placeholder);
|
|
1803
|
-
this.#terminal.requestRender();
|
|
1804
|
-
return response.then(value => value, () => null);
|
|
1805
|
-
}
|
|
1806
|
-
#notifyWorkflowLogin(event) {
|
|
1807
|
-
const dialog = this.#activeLoginDialog;
|
|
1808
|
-
if (!dialog)
|
|
1809
|
-
return;
|
|
1810
|
-
if (event.type === "auth_url")
|
|
1811
|
-
dialog.showAuth(event.url, event.instructions);
|
|
1812
|
-
else if (event.type === "device_code") {
|
|
1813
|
-
dialog.showDeviceCode(event);
|
|
1814
|
-
dialog.showWaiting("Waiting for authentication...");
|
|
1815
|
-
}
|
|
1816
|
-
else if (event.type === "info")
|
|
1817
|
-
dialog.showInfo(event.message, event.links);
|
|
1818
|
-
else if (event.type === "waiting")
|
|
1819
|
-
dialog.showWaiting(event.message);
|
|
1820
|
-
else
|
|
1821
|
-
dialog.showProgress(event.message);
|
|
1822
|
-
this.#terminal.requestRender();
|
|
1823
|
-
}
|
|
1824
|
-
#finishWorkflowLogin() {
|
|
1825
|
-
if (!this.#activeLoginDialog)
|
|
1826
|
-
return;
|
|
1827
|
-
this.#activeLoginDialog = undefined;
|
|
1828
|
-
this.root.setInputSurface(null);
|
|
1829
|
-
this.#programStatus.setBlocked("authentication", undefined);
|
|
1830
|
-
this.#syncTerminalProgress(this.view());
|
|
1831
|
-
this.#terminal.requestRender();
|
|
1832
|
-
}
|
|
1833
|
-
async #execute(command, draft) {
|
|
1834
|
-
if (draft === undefined)
|
|
1835
|
-
return this.backend.session.execute(command);
|
|
1836
|
-
const revision = this.#editorRevision;
|
|
1837
|
-
try {
|
|
1838
|
-
assertOwnedUiCommand(command);
|
|
1839
|
-
}
|
|
1840
|
-
catch (error) {
|
|
1841
|
-
return this.#recoverSubmission(draft, revision, error);
|
|
1842
|
-
}
|
|
1843
|
-
try {
|
|
1844
|
-
const result = await this.backend.session.execute(command);
|
|
1845
|
-
if (result.outcome === "rejected")
|
|
1846
|
-
return this.#recoverSubmission(draft, revision);
|
|
1847
|
-
return result;
|
|
1848
|
-
}
|
|
1849
|
-
catch {
|
|
1850
|
-
// Security: dispatch might already have reached the provider. Never retry automatically.
|
|
1851
|
-
this.root.editor.addToHistory(draft);
|
|
1852
|
-
this.#promptHistory?.rememberRecovery(draft);
|
|
1853
|
-
this.#reportSubmissionError(undefined, "Submission failed; delivery is uncertain. Check the conversation before retrying. Press Up to recover the draft.");
|
|
1854
|
-
return { outcome: "failed", diagnostic: "submission delivery is uncertain" };
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
async #guardSubmission(draft, action) {
|
|
1858
|
-
const revision = this.#editorRevision;
|
|
1859
|
-
try {
|
|
1860
|
-
return await action();
|
|
1861
|
-
}
|
|
1862
|
-
catch (error) {
|
|
1863
|
-
if (error instanceof ImageAttachmentError && error.code === "image-canceled")
|
|
1864
|
-
return rejected("image submission canceled");
|
|
1865
|
-
return this.#recoverSubmission(draft, revision, error);
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
#recoverSubmission(draft, revision, error) {
|
|
1869
|
-
this.root.editor.addToHistory(draft);
|
|
1870
|
-
this.#promptHistory?.rememberRecovery(draft);
|
|
1871
|
-
// Concurrency: never overwrite input typed (even typed and cleared) after this submission.
|
|
1872
|
-
if (revision === this.#editorRevision && this.root.editor.getText().length === 0)
|
|
1873
|
-
this.root.editor.setText(draft);
|
|
1874
|
-
const message = error instanceof ImageAttachmentError ? error.message : "Submission rejected. Check the prompt and attachments.";
|
|
1875
|
-
this.#reportSubmissionError(error, error instanceof ImageAttachmentError && error.code === "image-count"
|
|
1876
|
-
? message
|
|
1877
|
-
: `${message} Press Up to recover the draft.`);
|
|
1878
|
-
return rejected(message);
|
|
1879
|
-
}
|
|
1880
|
-
#reportSubmissionError(error, message) {
|
|
1881
|
-
// Security: arbitrary provider/extension error messages can contain the entire request.
|
|
1882
|
-
try {
|
|
1883
|
-
this.root.appendWorkflowResult({ command: "debug", outcome: "failed", message: message
|
|
1884
|
-
?? (error instanceof ImageAttachmentError ? error.message : "Submission failed. Check the prompt and try again.") }, error instanceof ImageAttachmentError ? error.code : undefined);
|
|
1885
|
-
this.#terminal.requestRender();
|
|
1886
|
-
}
|
|
1887
|
-
catch { /* Security: error presentation cannot create another rejected submission callback. */ }
|
|
1888
|
-
}
|
|
1889
1400
|
#simple(type) {
|
|
1890
1401
|
return { type, correlationId: this.#correlation(type), sessionId: this.backend.identity.sessionId };
|
|
1891
1402
|
}
|
|
@@ -1920,38 +1431,19 @@ export class OwnedUiSessionShell extends OwnedUiSessionPresenter {
|
|
|
1920
1431
|
function isCoalescedStreamBlock(kind) {
|
|
1921
1432
|
return kind === "assistant" || kind === "thinking" || kind === "tool-call" || kind === "tool-result";
|
|
1922
1433
|
}
|
|
1923
|
-
function
|
|
1434
|
+
function copyCharacters(snapshot) {
|
|
1924
1435
|
if (snapshot.literal !== true)
|
|
1925
|
-
return
|
|
1926
|
-
let
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
return { characters, hasNonWhitespace };
|
|
1933
|
-
}
|
|
1934
|
-
export function formatSessionResumeCommand(metadata) {
|
|
1935
|
-
const args = [PRODUCT_TEXT.commandName];
|
|
1936
|
-
if (!metadata.usesDefaultSessionDir)
|
|
1937
|
-
args.push("--session-dir", quoteCommandArgument(metadata.sessionDir));
|
|
1938
|
-
args.push("--session", metadata.sessionId);
|
|
1939
|
-
return args.join(" ");
|
|
1940
|
-
}
|
|
1941
|
-
export function quoteCommandArgument(value) {
|
|
1942
|
-
if (value.length > 0 && !/[^a-zA-Z0-9_\-./~:@]/.test(value))
|
|
1943
|
-
return value;
|
|
1944
|
-
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
1945
|
-
}
|
|
1946
|
-
function dim(value) {
|
|
1947
|
-
return `\u001b[2m${value}\u001b[22m`;
|
|
1436
|
+
return snapshot.sourceUnits;
|
|
1437
|
+
let count = Math.max(0, snapshot.rows.length - 1), hasContent = false;
|
|
1438
|
+
for (const { text } of snapshot.rows) {
|
|
1439
|
+
count += text.length;
|
|
1440
|
+
hasContent ||= /\S/u.test(text);
|
|
1441
|
+
}
|
|
1442
|
+
return hasContent ? count : undefined;
|
|
1948
1443
|
}
|
|
1949
1444
|
function rejected(diagnostic) {
|
|
1950
1445
|
return { outcome: "rejected", diagnostic };
|
|
1951
1446
|
}
|
|
1952
|
-
function modelKey(view) {
|
|
1953
|
-
return view.activeModel === null ? "" : `${view.activeModel.providerId}/${view.activeModel.modelId}`;
|
|
1954
|
-
}
|
|
1955
1447
|
function workflowAdapterResult(result) {
|
|
1956
1448
|
if (result.outcome === "completed")
|
|
1957
1449
|
return { outcome: "completed", diagnostic: null };
|