@timurproko/a1 0.1.8-dev.3b54f21 → 0.1.8-dev.407
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 +159 -65
- package/bin/cli.js +28 -10
- package/bin/guardian.js +12 -6
- package/bin/module-identity.js +69 -35
- package/bin/pi-tui.d.ts +6 -0
- package/bin/pi-tui.js +28 -0
- package/bin/release-cleanup.js +8 -0
- package/bin/supervisor.js +2 -2
- package/bin/sync-pi-tui-proxy.js +97 -0
- package/bin/ui.js +62 -21
- package/bin/update-recovery.js +274 -0
- package/bin/warmup.js +27 -0
- package/dist/cli/capabilities.d.ts +6 -0
- package/dist/{src/cli → cli}/capabilities.js +1 -1
- package/dist/{src/cli → cli}/dispatch.d.ts +15 -1
- package/dist/cli/dispatch.js +261 -0
- package/dist/cli/package-messages.d.ts +26 -0
- package/dist/cli/package-messages.js +71 -0
- package/dist/{src/cli → cli}/packages.d.ts +11 -2
- package/dist/{src/cli → cli}/packages.js +24 -27
- package/dist/{src/cli → cli}/version-stats.d.ts +6 -0
- package/dist/cli/version-stats.js +112 -0
- package/dist/composition/agent-engine-bridge.d.ts +3 -0
- package/dist/composition/agent-engine-bridge.js +108 -0
- package/dist/composition/index.d.ts +3 -0
- package/dist/composition/index.js +3 -0
- package/dist/composition/owned-ui.d.ts +35 -0
- package/dist/composition/owned-ui.js +118 -0
- package/dist/composition/process.d.ts +16 -0
- package/dist/composition/process.js +45 -0
- package/dist/composition/settings-route-host.d.ts +8 -0
- package/dist/composition/settings-route-host.js +75 -0
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.d.ts +2 -2
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.js +22 -4
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.d.ts +17 -0
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.d.ts +6 -0
- package/dist/contracts/owned-ui/image-attachments.d.ts +36 -0
- package/dist/contracts/owned-ui/image-attachments.js +67 -0
- package/dist/contracts/owned-ui/index.d.ts +8 -0
- package/dist/contracts/owned-ui/index.js +8 -0
- package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.d.ts +123 -0
- package/dist/contracts/owned-ui/prompt-history.d.ts +55 -0
- package/dist/contracts/owned-ui/prompt-history.js +23 -0
- package/dist/contracts/owned-ui/prompt-suggestions.d.ts +3 -0
- package/dist/contracts/owned-ui/prompt-suggestions.js +44 -0
- package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +22 -0
- package/dist/contracts/owned-ui/suggestion-diagnostics.js +9 -0
- package/dist/contracts/owned-ui/transcript-lifecycle.d.ts +5 -0
- package/dist/contracts/owned-ui/transcript-lifecycle.js +33 -0
- package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.d.ts +5 -1
- package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.js +129 -4
- package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.d.ts +8 -0
- package/dist/{src/features → features}/launch/development-launch.js +11 -3
- package/dist/{src/features → features}/launch/index.d.ts +0 -1
- package/dist/{src/features → features}/launch/index.js +0 -1
- package/dist/features/launch/intent.d.ts +10 -0
- package/dist/features/launch/intent.js +8 -0
- package/dist/{src/features → features}/launch/prepare-launch.d.ts +0 -2
- package/dist/{src/features → features}/launch/prepare-launch.js +2 -9
- package/dist/{src/features → features}/launch/profile-paths.d.ts +3 -2
- package/dist/{src/features → features}/launch/profile-paths.js +13 -8
- package/dist/{src/features → features}/launch/runtime-selection.d.ts +2 -5
- package/dist/{src/features → features}/owned-ui/customization.d.ts +3 -1
- package/dist/{src/features → features}/owned-ui/customization.js +3 -1
- package/dist/{src/features → features}/owned-ui/diagnostics.d.ts +2 -1
- package/dist/{src/features → features}/owned-ui/diagnostics.js +1 -0
- package/dist/{src/features → features}/owned-ui/index.d.ts +2 -0
- package/dist/{src/features → features}/owned-ui/index.js +2 -0
- package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
- package/dist/features/owned-ui/project-trust-prompt.js +149 -0
- package/dist/{src/features → features}/owned-ui/run.d.ts +2 -2
- package/dist/features/owned-ui/run.js +32 -0
- package/dist/features/owned-ui/session-fork-prompt.d.ts +8 -0
- package/dist/features/owned-ui/session-fork-prompt.js +34 -0
- package/dist/{src/features → features}/owned-ui/settings-app.d.ts +3 -3
- package/dist/{src/features → features}/owned-ui/settings-app.js +105 -56
- package/dist/features/prompt-history/image-sidecar.d.ts +32 -0
- package/dist/features/prompt-history/image-sidecar.js +130 -0
- package/dist/features/prompt-history/index.d.ts +4 -0
- package/dist/features/prompt-history/index.js +3 -0
- package/dist/features/prompt-history/paths.d.ts +5 -0
- package/dist/features/prompt-history/paths.js +17 -0
- package/dist/features/prompt-history/service.d.ts +54 -0
- package/dist/features/prompt-history/service.js +425 -0
- package/dist/features/prompt-history/store.d.ts +16 -0
- package/dist/features/prompt-history/store.js +223 -0
- package/dist/features/prompt-history/worker.d.ts +9 -0
- package/dist/features/prompt-history/worker.js +45 -0
- package/dist/features/prompt-suggestions/diagnostics.d.ts +18 -0
- package/dist/features/prompt-suggestions/diagnostics.js +82 -0
- package/dist/features/prompt-suggestions/index.d.ts +1 -0
- package/dist/features/prompt-suggestions/index.js +1 -0
- package/dist/{src/features → features}/workspace/capabilities.d.ts +1 -1
- package/dist/{src/features → features}/workspace/reducer.d.ts +2 -1
- package/dist/{src/features → features}/workspace/reducer.js +2 -1
- package/dist/{src/features → features}/workspace/router.d.ts +2 -1
- package/dist/{src/features → features}/workspace/router.js +1 -0
- package/dist/{src/features → features}/workspace/store.d.ts +2 -1
- package/dist/{src/features → features}/workspace/store.js +1 -0
- package/dist/foundation/launch-context/index.d.ts +59 -0
- package/dist/foundation/launch-context/index.js +136 -0
- package/dist/{src/foundation → foundation}/launch-guardian/main.d.ts +2 -0
- package/dist/{src/foundation → foundation}/launch-guardian/main.js +23 -8
- package/dist/{src/foundation → foundation}/lifecycle/index.d.ts +2 -0
- package/dist/{src/foundation → foundation}/lifecycle/index.js +2 -0
- package/dist/{src/foundation → foundation}/lifecycle/model.d.ts +1 -1
- package/dist/{src/foundation → foundation}/lifecycle/model.js +1 -1
- package/dist/foundation/lifecycle/paths.d.ts +30 -0
- package/dist/{src/foundation → foundation}/lifecycle/paths.js +29 -1
- package/dist/foundation/lifecycle/session-selection.d.ts +9 -0
- package/dist/foundation/lifecycle/session-selection.js +39 -0
- package/dist/foundation/lifecycle/supervisor-startup.d.ts +30 -0
- package/dist/foundation/lifecycle/supervisor-startup.js +106 -0
- package/dist/{src/foundation → foundation}/native-host-protocol/codec.d.ts +2 -0
- package/dist/{src/foundation → foundation}/native-host-protocol/codec.js +2 -0
- package/dist/{src/foundation → foundation}/native-host-protocol/messages.d.ts +1 -1
- package/dist/{src/foundation → foundation}/native-host-protocol/messages.js +1 -1
- package/dist/foundation/process-containment/darwin-process-inspector.d.ts +5 -0
- package/dist/foundation/process-containment/darwin-process-inspector.js +7 -0
- package/dist/{src/foundation → foundation}/process-containment/index.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/index.js +1 -0
- package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.js +1 -0
- package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.js +1 -0
- package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.d.ts +3 -1
- package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.js +5 -2
- package/dist/{src/foundation → foundation}/protocol/client.d.ts +1 -0
- package/dist/{src/foundation → foundation}/protocol/client.js +1 -0
- package/dist/{src/foundation → foundation}/protocol/messages.d.ts +1 -0
- package/dist/{src/foundation → foundation}/protocol/messages.js +1 -0
- package/dist/foundation/release/bootstrap.d.ts +55 -0
- package/dist/{src/foundation → foundation}/release/bootstrap.js +219 -46
- package/dist/foundation/release/cohort-state.d.ts +157 -0
- package/dist/foundation/release/cohort-state.js +472 -0
- package/dist/foundation/release/dependency-certification-retention.d.ts +7 -0
- package/dist/foundation/release/dependency-certification-retention.js +88 -0
- package/dist/foundation/release/dependency-certification.d.ts +24 -0
- package/dist/foundation/release/dependency-certification.js +266 -0
- package/dist/foundation/release/dependency-layer.d.ts +81 -0
- package/dist/foundation/release/dependency-layer.js +466 -0
- package/dist/foundation/release/endpoints.d.ts +20 -0
- package/dist/foundation/release/endpoints.js +50 -0
- package/dist/foundation/release/immutable-platform.d.ts +6 -0
- package/dist/foundation/release/immutable-platform.js +13 -0
- package/dist/{src/foundation → foundation}/release/index.d.ts +6 -0
- package/dist/{src/foundation → foundation}/release/index.js +6 -0
- package/dist/foundation/release/release-gc.d.ts +71 -0
- package/dist/foundation/release/release-gc.js +764 -0
- package/dist/{src/foundation → foundation}/release/release-store.d.ts +9 -3
- package/dist/{src/foundation → foundation}/release/release-store.js +74 -14
- package/dist/{src/foundation → foundation}/release/release.d.ts +6 -1
- package/dist/{src/foundation → foundation}/release/release.js +15 -4
- package/dist/foundation/release/restart-certification.d.ts +68 -0
- package/dist/foundation/release/restart-certification.js +227 -0
- package/dist/foundation/release/update-launch.d.ts +6 -0
- package/dist/foundation/release/update-launch.js +17 -0
- package/dist/foundation/release/update-recovery.d.ts +76 -0
- package/dist/foundation/release/update-recovery.js +356 -0
- package/dist/{src/foundation → foundation}/release/update-transaction.d.ts +11 -0
- package/dist/{src/foundation → foundation}/release/update-transaction.js +13 -1
- package/dist/{src/foundation → foundation}/release/update.d.ts +40 -4
- package/dist/{src/foundation → foundation}/release/update.js +196 -78
- package/dist/foundation/release/warmup.d.ts +3 -0
- package/dist/foundation/release/warmup.js +47 -0
- package/dist/foundation/startup/index.d.ts +1 -0
- package/dist/foundation/startup/index.js +1 -0
- package/dist/foundation/startup/startup-runtime.d.ts +36 -0
- package/dist/foundation/startup/startup-runtime.js +142 -0
- package/dist/{src/foundation → foundation}/storage/control-store.d.ts +2 -1
- package/dist/{src/foundation → foundation}/storage/control-store.js +39 -4
- package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.js +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.js +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.d.ts +1 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.js +1 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.js +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/state.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/state.js +2 -1
- package/dist/foundation/supervision/index.d.ts +3 -0
- package/dist/foundation/supervision/index.js +3 -0
- package/dist/foundation/supervision/main.d.ts +1 -0
- package/dist/foundation/supervision/main.js +84 -0
- package/dist/foundation/supervision/paths.d.ts +1 -0
- package/dist/foundation/supervision/paths.js +1 -0
- package/dist/foundation/supervision/server.d.ts +51 -0
- package/dist/{src/foundation → foundation}/supervision/server.js +142 -20
- package/dist/foundation/terminal-cleanup/fatal-exit.d.ts +12 -0
- package/dist/foundation/terminal-cleanup/fatal-exit.js +85 -0
- package/dist/foundation/terminal-cleanup/index.d.ts +2 -0
- package/dist/foundation/terminal-cleanup/index.js +2 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +4 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.js +40 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.d.ts +1 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.js +3 -2
- package/dist/integrations/pi/components/editor-interaction.d.ts +38 -0
- package/dist/integrations/pi/components/history-editor-loader.d.ts +5 -0
- package/dist/integrations/pi/components/history-editor-loader.js +5 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.d.ts +2 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.js +2 -1
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +74 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +1093 -0
- package/dist/integrations/pi/components/path-word-ranges.d.ts +2 -0
- package/dist/integrations/pi/components/path-word-ranges.js +37 -0
- package/dist/integrations/pi/components/prompt-input-port.d.ts +18 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.d.ts +1 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.js +1 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +297 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.d.ts +4 -4
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.js +58 -23
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +43 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +114 -0
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +29 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-presenters-transcript.js +174 -170
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.d.ts +8 -4
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.js +10 -6
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +213 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.js +5 -2
- package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +15 -0
- package/dist/integrations/pi/components/submitted-prompt-adapter.js +43 -0
- package/dist/integrations/pi/components/tool-image-presentation.d.ts +21 -0
- package/dist/integrations/pi/components/tool-image-presentation.js +86 -0
- package/dist/integrations/pi/components/tool-result-adapter.d.ts +6 -0
- package/dist/integrations/pi/components/tool-result-adapter.js +27 -0
- package/dist/integrations/pi/components/transcript-image-resolver.d.ts +7 -0
- package/dist/integrations/pi/components/transcript-image-resolver.js +19 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.d.ts +11 -2
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.js +41 -3
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +12 -0
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +42 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +40 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +166 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.js +3 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.d.ts +3 -2
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.js +12 -7
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +61 -0
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +344 -0
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +280 -0
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2062 -0
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +32 -0
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +48 -0
- package/dist/integrations/pi/components/upstream/history/printable-key.d.ts +1 -0
- package/dist/integrations/pi/components/upstream/history/printable-key.js +40 -0
- package/dist/integrations/pi/components/upstream/history/text-helpers.d.ts +14 -0
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +28 -0
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +22 -0
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +36 -0
- package/dist/integrations/pi/components/upstream/history/word-navigation.d.ts +24 -0
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +100 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.d.ts +74 -6
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.js +1340 -228
- package/dist/integrations/pi/engine/changelog.d.ts +2 -0
- package/dist/integrations/pi/engine/changelog.js +63 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.d.ts +4 -2
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.js +15 -5
- package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
- package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.d.ts +4 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.js +4 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.d.ts +2 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.js +1 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.d.ts +1 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.js +32 -26
- package/dist/integrations/pi/engine/pending-delivery.d.ts +30 -0
- package/dist/integrations/pi/engine/pending-delivery.js +158 -0
- package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
- package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.d.ts +2 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.js +1 -0
- package/dist/integrations/pi/engine/resources/changelog.json +12 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.d.ts +19 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.js +56 -5
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.d.ts +4 -2
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.js +19 -4
- package/dist/integrations/pi/engine/session-selection.d.ts +28 -0
- package/dist/integrations/pi/engine/session-selection.js +90 -0
- package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
- package/dist/integrations/pi/engine/settings-effects.js +249 -0
- package/dist/integrations/pi/engine/settings-integration.d.ts +43 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/settings-integration.js +102 -108
- package/dist/integrations/pi/engine/tool-rendering.d.ts +13 -0
- package/dist/integrations/pi/engine/tool-rendering.js +145 -0
- package/dist/integrations/pi/engine/transcript-image-assets.d.ts +11 -0
- package/dist/integrations/pi/engine/transcript-image-assets.js +86 -0
- package/dist/integrations/pi/engine/windows-filesystem-hygiene.d.ts +21 -0
- package/dist/integrations/pi/engine/windows-filesystem-hygiene.js +55 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.d.ts +3 -2
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.js +1 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.d.ts +19 -2
- package/dist/integrations/pi/session-ui/clipboard-diagnostics.d.ts +16 -0
- package/dist/integrations/pi/session-ui/clipboard-diagnostics.js +65 -0
- package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
- package/dist/integrations/pi/session-ui/clipboard-image.js +38 -0
- package/dist/integrations/pi/session-ui/editor-hyperlink-budget.d.ts +9 -0
- package/dist/integrations/pi/session-ui/editor-hyperlink-budget.js +23 -0
- package/dist/integrations/pi/session-ui/image-preparation-client.d.ts +20 -0
- package/dist/integrations/pi/session-ui/image-preparation-client.js +145 -0
- package/dist/integrations/pi/session-ui/image-preparation.d.ts +18 -0
- package/dist/integrations/pi/session-ui/image-preparation.js +126 -0
- package/dist/integrations/pi/session-ui/image-source.d.ts +13 -0
- package/dist/integrations/pi/session-ui/image-source.js +171 -0
- package/dist/integrations/pi/session-ui/image-worker.d.ts +17 -0
- package/dist/integrations/pi/session-ui/image-worker.js +23 -0
- package/dist/integrations/pi/session-ui/index.d.ts +5 -0
- package/dist/integrations/pi/session-ui/index.js +5 -0
- package/dist/integrations/pi/session-ui/paste-executor.d.ts +10 -0
- package/dist/integrations/pi/session-ui/paste-executor.js +232 -0
- package/dist/integrations/pi/session-ui/paste-helper.d.ts +1 -0
- package/dist/integrations/pi/session-ui/paste-helper.js +173 -0
- package/dist/integrations/pi/session-ui/paste-preparation-client.d.ts +16 -0
- package/dist/integrations/pi/session-ui/paste-preparation-client.js +159 -0
- package/dist/integrations/pi/session-ui/paste-protocol.d.ts +78 -0
- package/dist/integrations/pi/session-ui/paste-protocol.js +18 -0
- package/dist/integrations/pi/session-ui/paste-text-preparation.d.ts +20 -0
- package/dist/integrations/pi/session-ui/paste-text-preparation.js +134 -0
- package/dist/integrations/pi/session-ui/paste-text-worker.d.ts +1 -0
- package/dist/integrations/pi/session-ui/paste-text-worker.js +12 -0
- package/dist/integrations/pi/session-ui/path-chip-presentation.d.ts +7 -0
- package/dist/integrations/pi/session-ui/path-chip-presentation.js +24 -0
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +72 -0
- package/dist/integrations/pi/session-ui/prompt-chips.js +503 -0
- package/dist/integrations/pi/session-ui/prompt-history-controller.d.ts +36 -0
- package/dist/integrations/pi/session-ui/prompt-history-controller.js +170 -0
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.d.ts +35 -0
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.js +207 -0
- package/dist/integrations/pi/session-ui/response-copy-coordinator.d.ts +27 -0
- package/dist/integrations/pi/session-ui/response-copy-coordinator.js +189 -0
- package/dist/integrations/pi/session-ui/response-copy-helper.d.ts +1 -0
- package/dist/integrations/pi/session-ui/response-copy-helper.js +122 -0
- package/dist/integrations/pi/session-ui/response-copy-protocol.d.ts +56 -0
- package/dist/integrations/pi/session-ui/response-copy-protocol.js +5 -0
- package/dist/integrations/pi/session-ui/response-copy-transport.d.ts +31 -0
- package/dist/integrations/pi/session-ui/response-copy-transport.js +187 -0
- package/dist/integrations/pi/session-ui/route-host.d.ts +5 -0
- package/dist/integrations/pi/session-ui/route-host.js +1 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +221 -0
- package/dist/integrations/pi/session-ui/session-shell-root.js +1305 -0
- package/dist/integrations/pi/session-ui/session-shell.d.ts +55 -0
- package/dist/integrations/pi/session-ui/session-shell.js +1792 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +56 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +569 -0
- package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
- package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
- package/dist/integrations/pi/session-ui/system-clipboard.d.ts +20 -0
- package/dist/integrations/pi/session-ui/system-clipboard.js +156 -0
- package/dist/integrations/pi/session-ui/text-paste.d.ts +5 -0
- package/dist/integrations/pi/session-ui/text-paste.js +16 -0
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.d.ts +10 -1
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.js +255 -11
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.d.ts +32 -6
- package/dist/integrations/pi/tui-runtime/contracts.js +1 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +81 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +424 -0
- package/dist/integrations/pi/tui-runtime/index.d.ts +7 -0
- package/dist/integrations/pi/tui-runtime/index.js +7 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
- package/dist/integrations/pi/tui-runtime/mouse-report-input.d.ts +13 -0
- package/dist/integrations/pi/tui-runtime/mouse-report-input.js +59 -0
- package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +32 -0
- package/dist/integrations/pi/tui-runtime/overlay-geometry.js +159 -0
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.d.ts +1 -1
- package/dist/native/darwin-arm64/manifest.json +4 -4
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/manifest.json +3 -3
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/manifest.json +3 -3
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/{src/product-identity.d.ts → product-identity.d.ts} +4 -4
- package/dist/{src/product-identity.js → product-identity.js} +7 -38
- package/dist/{src/product-identity.json → product-identity.json} +13 -27
- package/dist/runtime-payload-inventory.json +122333 -0
- package/dist/{src/foundation/ui-apps → ui/apps}/contracts.d.ts +23 -1
- package/dist/{src/foundation/ui-apps → ui/apps}/host.d.ts +1 -1
- package/dist/{src/foundation/ui-apps → ui/apps}/host.js +14 -10
- package/dist/{src/foundation/ui-apps → ui/apps}/registry.d.ts +1 -0
- package/dist/{src/foundation/ui-apps → ui/apps}/registry.js +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.js +5 -3
- package/dist/{src/foundation/ui-components → ui/components}/frame.d.ts +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/frame.js +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/index.d.ts +7 -0
- package/dist/{src/foundation/ui-components → ui/components}/index.js +7 -0
- package/dist/{src/foundation/ui-components → ui/components}/line-input.d.ts +2 -8
- package/dist/{src/foundation/ui-components → ui/components}/line-input.js +7 -28
- package/dist/{src/foundation/ui-components → ui/components}/list-view.d.ts +0 -6
- package/dist/{src/foundation/ui-components → ui/components}/list-view.js +14 -7
- package/dist/{src/foundation/ui-components → ui/components}/mouse.d.ts +10 -0
- package/dist/{src/foundation/ui-components → ui/components}/mouse.js +24 -0
- package/dist/ui/components/progress-status.d.ts +2 -0
- package/dist/ui/components/progress-status.js +4 -0
- package/dist/ui/components/prompt-input.d.ts +32 -0
- package/dist/ui/components/prompt-input.js +54 -0
- package/dist/ui/components/prompt-row.d.ts +14 -0
- package/dist/ui/components/prompt-row.js +20 -0
- package/dist/{src/foundation/ui-components → ui/components}/scrollbar.d.ts +26 -0
- package/dist/{src/foundation/ui-components → ui/components}/scrollbar.js +35 -0
- package/dist/ui/components/selection-copy.d.ts +24 -0
- package/dist/ui/components/selection-copy.js +39 -0
- package/dist/{src/foundation/ui-components → ui/components}/shortcuts.d.ts +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/shortcuts.js +1 -0
- package/dist/ui/components/spans.d.ts +28 -0
- package/dist/ui/components/spans.js +266 -0
- package/dist/ui/components/submitted-prompt.d.ts +23 -0
- package/dist/ui/components/submitted-prompt.js +50 -0
- package/dist/{src/foundation/ui-components → ui/components}/surface.d.ts +0 -5
- package/dist/{src/foundation/ui-components → ui/components}/surface.js +1 -5
- package/dist/ui/components/text-selection.d.ts +62 -0
- package/dist/ui/components/text-selection.js +210 -0
- package/dist/{src/foundation/ui-components → ui/components}/text.d.ts +14 -0
- package/dist/{src/foundation/ui-components → ui/components}/text.js +67 -5
- package/dist/ui/components/transcript-viewport.d.ts +173 -0
- package/dist/ui/components/transcript-viewport.js +753 -0
- package/dist/ui/components/visible-hyperlinks.d.ts +26 -0
- package/dist/ui/components/visible-hyperlinks.js +97 -0
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.d.ts +8 -1
- package/dist/ui/settings/declarations.js +94 -0
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.d.ts +1 -1
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.js +28 -2
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.d.ts +6 -19
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.js +41 -22
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.d.ts +9 -17
- package/dist/ui/settings/session.js +164 -0
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.d.ts +1 -0
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.js +2 -1
- package/docs/architecture/boundaries.md +14 -10
- package/docs/architecture/code-documentation.md +77 -0
- package/docs/architecture/history-editor-provenance.md +100 -0
- package/docs/architecture/internal-naming.md +91 -0
- package/docs/architecture/process-guardian-provenance.md +3 -1
- package/docs/architecture/project-structure.md +69 -30
- package/docs/architecture/prompt-suggestions.md +68 -0
- package/docs/architecture/resource-and-data-policy.md +28 -2
- package/docs/architecture/response-copy-delivery.md +83 -0
- package/docs/architecture/tool-image-presentation.md +62 -0
- package/docs/architecture/toolchain.md +24 -10
- package/docs/architecture/ui-reference-provenance.md +59 -5
- package/docs/ci-release-runbook.md +236 -79
- package/docs/features/launch-profiles.md +24 -17
- package/docs/features/modal-content-interaction.md +37 -0
- package/docs/features/prompt-history.md +149 -0
- package/docs/local-worktree-cleanup.md +128 -0
- package/docs/manual-code-streaming-cleanup.md +88 -0
- package/docs/manual-ghost-link-baseline.md +89 -0
- package/docs/manual-launch-instance-acceptance.md +12 -35
- package/docs/manual-owned-ui-checkpoint.md +78 -0
- package/docs/manual-pi-boundary-candidate.md +4 -5
- package/docs/manual-terminal-colour-check.md +4 -3
- package/docs/openspec-archive-automation.md +128 -0
- package/docs/repository-governance-live-acceptance.md +77 -0
- package/package.json +45 -28
- package/dist/src/cli/capabilities.d.ts +0 -15
- package/dist/src/cli/dispatch.js +0 -142
- package/dist/src/cli/version-stats.js +0 -72
- package/dist/src/composition/index.d.ts +0 -48
- package/dist/src/composition/index.js +0 -258
- package/dist/src/features/launch/intent.d.ts +0 -7
- package/dist/src/features/launch/intent.js +0 -4
- package/dist/src/features/launch/profiles.d.ts +0 -12
- package/dist/src/features/launch/profiles.js +0 -26
- package/dist/src/features/owned-ui/run.js +0 -15
- package/dist/src/foundation/lifecycle/paths.d.ts +0 -10
- package/dist/src/foundation/owned-ui-contracts/index.d.ts +0 -3
- package/dist/src/foundation/owned-ui-contracts/index.js +0 -3
- package/dist/src/foundation/owned-ui-settings/declarations.js +0 -39
- package/dist/src/foundation/owned-ui-settings/session.js +0 -145
- package/dist/src/foundation/pi-component-adapter/shell-editor-autocomplete.js +0 -162
- package/dist/src/foundation/pi-component-adapter/shell-presenters-transcript.d.ts +0 -57
- package/dist/src/foundation/pi-component-adapter/shell-shared-facade.d.ts +0 -116
- package/dist/src/foundation/pi-component-adapter/upstream/components/earendil-announcement.js +0 -19
- package/dist/src/foundation/pi-component-adapter/upstream/components/owned-editor.d.ts +0 -18
- package/dist/src/foundation/pi-component-adapter/upstream/components/owned-editor.js +0 -55
- package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +0 -55
- package/dist/src/foundation/pi-owned-ui-integration/index.d.ts +0 -2
- package/dist/src/foundation/pi-owned-ui-integration/index.js +0 -2
- package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +0 -29
- package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +0 -106
- package/dist/src/foundation/pi-owned-ui-integration/session-shell.js +0 -1518
- package/dist/src/foundation/pi-tui-runtime-adapter/index.d.ts +0 -4
- package/dist/src/foundation/pi-tui-runtime-adapter/index.js +0 -4
- package/dist/src/foundation/release/bootstrap.d.ts +0 -29
- package/dist/src/foundation/release/cohort-state.d.ts +0 -70
- package/dist/src/foundation/release/cohort-state.js +0 -202
- package/dist/src/foundation/release/release-gc.d.ts +0 -5
- package/dist/src/foundation/release/release-gc.js +0 -16
- package/dist/src/foundation/supervision/index.d.ts +0 -3
- package/dist/src/foundation/supervision/index.js +0 -3
- package/dist/src/foundation/supervision/main.d.ts +0 -1
- package/dist/src/foundation/supervision/main.js +0 -41
- package/dist/src/foundation/supervision/paths.d.ts +0 -1
- package/dist/src/foundation/supervision/paths.js +0 -1
- package/dist/src/foundation/supervision/server.d.ts +0 -22
- package/dist/src/foundation/ui-components/spans.d.ts +0 -7
- package/dist/src/foundation/ui-components/spans.js +0 -59
- package/docs/manual-transparent-checkpoint.md +0 -53
- /package/dist/{src/cli → cli}/index.d.ts +0 -0
- /package/dist/{src/cli → cli}/index.js +0 -0
- /package/dist/{src/cli → cli}/version.d.ts +0 -0
- /package/dist/{src/cli → cli}/version.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.js +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.js +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.js +0 -0
- /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.js +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.d.ts +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.js +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.d.ts +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.js +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.d.ts +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.js +0 -0
- /package/dist/{src/features → features}/launch/development-launch.d.ts +0 -0
- /package/dist/{src/features → features}/launch/initialize-profile.d.ts +0 -0
- /package/dist/{src/features → features}/launch/initialize-profile.js +0 -0
- /package/dist/{src/features → features}/launch/runtime-selection.js +0 -0
- /package/dist/{src/features → features}/workspace/capabilities.js +0 -0
- /package/dist/{src/features → features}/workspace/index.d.ts +0 -0
- /package/dist/{src/features → features}/workspace/index.js +0 -0
- /package/dist/{src/features → features}/workspace/presentation.d.ts +0 -0
- /package/dist/{src/features → features}/workspace/presentation.js +0 -0
- /package/dist/{src/features → features}/workspace/reconciliation.d.ts +0 -0
- /package/dist/{src/features → features}/workspace/reconciliation.js +0 -0
- /package/dist/{src/foundation → foundation}/launch-guardian/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/launch-guardian/index.js +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/commands.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/commands.js +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.js +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.js +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/index.js +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.js +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/artifact.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/artifact.js +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/contracts.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/contracts.js +0 -0
- /package/dist/{src/foundation → foundation}/protocol/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/protocol/index.js +0 -0
- /package/dist/{src/foundation → foundation}/release/cohort-selection.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/cohort-selection.js +0 -0
- /package/dist/{src/foundation → foundation}/release/concurrency.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/concurrency.js +0 -0
- /package/dist/{src/foundation → foundation}/release/process-cleanup.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/process-cleanup.js +0 -0
- /package/dist/{src/foundation → foundation}/release/stable-release.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/stable-release.js +0 -0
- /package/dist/{src/foundation → foundation}/storage/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/storage/index.js +0 -0
- /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.js +0 -0
- /package/dist/{src/foundation/pi-owned-ui-integration/route-host.js → integrations/pi/components/editor-interaction.js} +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter/contracts.js → integrations/pi/components/prompt-input-port.js} +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.js +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/pi-settings-metadata.json +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.d.ts +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.js +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.js +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.d.ts +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.js +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.js +0 -0
- /package/dist/{src/foundation/ui-apps → ui/apps}/contracts.js +0 -0
- /package/dist/{src/foundation/ui-apps → ui/apps}/index.d.ts +0 -0
- /package/dist/{src/foundation/ui-apps → ui/apps}/index.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/label.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/label.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/list-block.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/list-block.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/pane.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/pane.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/revision.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/revision.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/status-line.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/status-line.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/stepper.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/stepper.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/theme.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/theme.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/value-menu.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/value-menu.js +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.js +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.js +0 -0
|
@@ -1,31 +1,99 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
2
3
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { dirname, join, resolve } from "node:path";
|
|
5
6
|
import { promisify } from "node:util";
|
|
6
|
-
import { PRODUCT_IDENTITY } from "
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { PRODUCT_IDENTITY } from "../../../product-identity.js";
|
|
8
|
+
import { configureOwnedHttpDispatcher } from "./http-dispatcher.js";
|
|
9
|
+
import { readPinnedCommandChangelog } from "./changelog.js";
|
|
10
|
+
import { toolRenderingInput } from "./tool-rendering.js";
|
|
11
|
+
import { TranscriptImageAssets } from "./transcript-image-assets.js";
|
|
12
|
+
import { copyToClipboard, CredentialSynchronizationError, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, VERSION, } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, acceptsTranscriptUpdate, transcriptToolState, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiSnapshot, normalizePromptSuggestionCandidate, } from "../../../contracts/owned-ui/index.js";
|
|
9
14
|
import { PINNED_PI_SETTINGS_CALLBACKS, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "./workflows.js";
|
|
10
15
|
import { createPiRuntimeIntegration } from "./runtime-integration.js";
|
|
11
16
|
import { PiSessionCommandIntegration } from "./session-integration.js";
|
|
12
17
|
import { PiSettingsIntegration } from "./settings-integration.js";
|
|
18
|
+
import { PendingEngineDelivery } from "./pending-delivery.js";
|
|
19
|
+
/** Explicit flush failure when required delivery was interrupted rather than completed. */
|
|
20
|
+
export class EngineDeliveryError extends Error {
|
|
21
|
+
constructor() { super("Engine delivery did not complete"); this.name = "EngineDeliveryError"; }
|
|
22
|
+
}
|
|
13
23
|
const execFileAsync = promisify(execFile);
|
|
24
|
+
/**
|
|
25
|
+
* Engine events delivered before the queue hands the event loop a turn. Small enough
|
|
26
|
+
* that a streaming burst never holds input, large enough that an ordinary turn is one
|
|
27
|
+
* batch.
|
|
28
|
+
*/
|
|
29
|
+
// Performance: deliver at most one engine event per event-loop turn. Transcript updates can
|
|
30
|
+
// be expensive in long sessions; a larger synchronous batch starves terminal
|
|
31
|
+
// input and makes an in-progress mouse selection appear frozen.
|
|
32
|
+
const EVENT_DELIVERY_BATCH = 1;
|
|
33
|
+
const AUTH_REFRESH_TIMEOUT_MS = 15_000;
|
|
34
|
+
// Provenance: Pi 0.84.2 core/model-resolver.ts defaultModelPerProvider.
|
|
35
|
+
const PINNED_DEFAULT_MODEL_BY_PROVIDER = Object.freeze({
|
|
36
|
+
"amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
|
|
37
|
+
"ant-ling": "Ring-2.6-1T",
|
|
38
|
+
anthropic: "claude-opus-4-8",
|
|
39
|
+
openai: "gpt-5.5",
|
|
40
|
+
"azure-openai-responses": "gpt-5.4",
|
|
41
|
+
"openai-codex": "gpt-5.5",
|
|
42
|
+
radius: "auto",
|
|
43
|
+
nvidia: "nvidia/nemotron-3-super-120b-a12b",
|
|
44
|
+
deepseek: "deepseek-v4-pro",
|
|
45
|
+
google: "gemini-3.1-pro-preview",
|
|
46
|
+
"google-vertex": "gemini-3.1-pro-preview",
|
|
47
|
+
"github-copilot": "gpt-5.4",
|
|
48
|
+
openrouter: "moonshotai/kimi-k2.6",
|
|
49
|
+
"vercel-ai-gateway": "zai/glm-5.1",
|
|
50
|
+
xai: "grok-4.5",
|
|
51
|
+
groq: "openai/gpt-oss-120b",
|
|
52
|
+
cerebras: "zai-glm-4.7",
|
|
53
|
+
zai: "glm-5.1",
|
|
54
|
+
"zai-coding-cn": "glm-5.1",
|
|
55
|
+
mistral: "devstral-medium-latest",
|
|
56
|
+
minimax: "MiniMax-M2.7",
|
|
57
|
+
"minimax-cn": "MiniMax-M2.7",
|
|
58
|
+
moonshotai: "kimi-k2.6",
|
|
59
|
+
"moonshotai-cn": "kimi-k2.6",
|
|
60
|
+
huggingface: "moonshotai/Kimi-K2.6",
|
|
61
|
+
fireworks: "accounts/fireworks/models/kimi-k2p6",
|
|
62
|
+
together: "moonshotai/Kimi-K2.6",
|
|
63
|
+
baseten: "zai-org/GLM-5.2",
|
|
64
|
+
opencode: "kimi-k2.6",
|
|
65
|
+
"opencode-go": "kimi-k2.6",
|
|
66
|
+
"kimi-coding": "kimi-for-coding",
|
|
67
|
+
"cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6",
|
|
68
|
+
"cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6",
|
|
69
|
+
"qwen-token-plan": "qwen3.7-max",
|
|
70
|
+
"qwen-token-plan-cn": "qwen3.7-max",
|
|
71
|
+
"qwen-token-plan-individual": "qwen3.8-max",
|
|
72
|
+
xiaomi: "mimo-v2.5-pro",
|
|
73
|
+
"xiaomi-token-plan-cn": "mimo-v2.5-pro",
|
|
74
|
+
"xiaomi-token-plan-ams": "mimo-v2.5-pro",
|
|
75
|
+
"xiaomi-token-plan-sgp": "mimo-v2.5-pro",
|
|
76
|
+
});
|
|
14
77
|
const DEFAULT_SURFACE = {
|
|
15
78
|
columns: 100,
|
|
16
79
|
rows: 32,
|
|
17
80
|
focusedRegion: "editor",
|
|
18
81
|
hardwareCursor: false,
|
|
19
82
|
};
|
|
83
|
+
/** Owns the pinned Pi session lifecycle and translates its events into the neutral agent-engine contract. */
|
|
20
84
|
export class PiEngineAdapter {
|
|
21
85
|
#runtimeFactory;
|
|
22
86
|
#checkPackageUpdates;
|
|
23
87
|
#cwd;
|
|
24
88
|
#agentDir;
|
|
25
89
|
#sessionId;
|
|
90
|
+
#sessionPath;
|
|
91
|
+
#sessionSelection;
|
|
92
|
+
#sessionForkPrompt;
|
|
26
93
|
#workflowHost;
|
|
94
|
+
#clipboardWriter;
|
|
27
95
|
#workflowInteraction;
|
|
28
|
-
#listeners = new
|
|
96
|
+
#listeners = new Map();
|
|
29
97
|
#runtime;
|
|
30
98
|
#session;
|
|
31
99
|
#unsubscribe;
|
|
@@ -41,6 +109,7 @@ export class PiEngineAdapter {
|
|
|
41
109
|
submitEnabled: false,
|
|
42
110
|
};
|
|
43
111
|
#sessionGeneration = 0;
|
|
112
|
+
#sessionBindingGeneration = 0;
|
|
44
113
|
#status = {
|
|
45
114
|
title: "Pi",
|
|
46
115
|
workingMessage: null,
|
|
@@ -53,26 +122,48 @@ export class PiEngineAdapter {
|
|
|
53
122
|
#activeCommandIds = [];
|
|
54
123
|
#completedCommands = new Map();
|
|
55
124
|
#transcript = [];
|
|
125
|
+
#transcriptIndex = new Map();
|
|
126
|
+
#transcriptSnapshot;
|
|
56
127
|
#messageBlockIds = new WeakMap();
|
|
57
128
|
#messageFallbackIds = new Map();
|
|
58
129
|
#toolBlockIds = new Map();
|
|
130
|
+
#transcriptImageAssets = new TranscriptImageAssets();
|
|
131
|
+
#usageCache;
|
|
59
132
|
#nextBlockSequence = 0;
|
|
60
133
|
#diagnostics = [];
|
|
61
|
-
#eventQueue =
|
|
134
|
+
#eventQueue = new PendingEngineDelivery(event => this.#transcriptImageAssets.retainEvent(event));
|
|
62
135
|
#eventQueueProcessing;
|
|
63
|
-
#
|
|
136
|
+
#overload;
|
|
137
|
+
#overloads = 0;
|
|
138
|
+
#deliveryFailed = false;
|
|
139
|
+
#admissionStopped = false;
|
|
140
|
+
#runningCommands = 0;
|
|
141
|
+
#invalidatedEvents = 0;
|
|
142
|
+
#pendingCommands = new Map();
|
|
143
|
+
#reservedOutcomes = new Map();
|
|
144
|
+
#pendingWorkflows = new Set();
|
|
145
|
+
#agentRunActive = false;
|
|
146
|
+
#agentRunSequence = 0;
|
|
147
|
+
#assistantResponseSequence = 0;
|
|
148
|
+
#statusKind = null;
|
|
64
149
|
#sessionCommands;
|
|
65
150
|
#gitBranch = null;
|
|
66
151
|
#extensionUi;
|
|
67
152
|
#extensionShutdown;
|
|
68
153
|
#extensionBound = false;
|
|
69
154
|
#disposed = false;
|
|
70
|
-
|
|
155
|
+
#settingsProductMode;
|
|
156
|
+
#projectTrustPrompt;
|
|
157
|
+
#settingsIntegration;
|
|
158
|
+
#settingsIntegrationManager;
|
|
71
159
|
#availableThemes;
|
|
72
160
|
constructor(options = {}) {
|
|
73
161
|
this.#cwd = options.cwd ?? process.cwd();
|
|
74
162
|
this.#agentDir = options.agentDir ?? getAgentDir();
|
|
75
163
|
this.#sessionId = options.sessionId ?? "owned-session-1";
|
|
164
|
+
this.#sessionPath = options.sessionPath;
|
|
165
|
+
this.#sessionSelection = options.sessionSelection;
|
|
166
|
+
this.#sessionForkPrompt = options.sessionForkPrompt;
|
|
76
167
|
this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
|
|
77
168
|
this.#checkPackageUpdates = options.checkPackageUpdates
|
|
78
169
|
?? (options.createRuntime
|
|
@@ -80,6 +171,8 @@ export class PiEngineAdapter {
|
|
|
80
171
|
: settingsManager => checkDefaultPiPackageUpdates(this.#cwd, this.#agentDir, settingsManager));
|
|
81
172
|
this.#workflowHost = options.workflowHost ?? defaultWorkflowHost();
|
|
82
173
|
this.#availableThemes = options.availableThemes ?? null;
|
|
174
|
+
this.#settingsProductMode = options.settingsProductMode ?? "bare";
|
|
175
|
+
this.#projectTrustPrompt = options.projectTrustPrompt;
|
|
83
176
|
this.#workflowInteraction = { prompt: async () => null, notify() { } };
|
|
84
177
|
}
|
|
85
178
|
setWorkflowInteractionHost(interaction) {
|
|
@@ -91,12 +184,112 @@ export class PiEngineAdapter {
|
|
|
91
184
|
get sessionGeneration() {
|
|
92
185
|
return this.#sessionGeneration;
|
|
93
186
|
}
|
|
187
|
+
/** Actual session replacements, excluding delivery-only invalidation of callbacks. */
|
|
188
|
+
get sessionBindingGeneration() { return this.#sessionBindingGeneration; }
|
|
189
|
+
get cwd() {
|
|
190
|
+
return this.#runtime?.cwd ?? this.#cwd;
|
|
191
|
+
}
|
|
94
192
|
get agentDir() {
|
|
95
193
|
return this.#agentDir;
|
|
96
194
|
}
|
|
195
|
+
resolveTranscriptImage(assetId) {
|
|
196
|
+
return this.#transcriptImageAssets.resolve(assetId);
|
|
197
|
+
}
|
|
198
|
+
currentSessionFile() {
|
|
199
|
+
const value = this.#session?.sessionManager?.getSessionFile?.();
|
|
200
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
201
|
+
}
|
|
202
|
+
currentSessionResumeMetadata() {
|
|
203
|
+
const manager = this.#session?.sessionManager;
|
|
204
|
+
if (manager === undefined
|
|
205
|
+
|| typeof manager.isPersisted !== "function"
|
|
206
|
+
|| typeof manager.getSessionId !== "function"
|
|
207
|
+
|| typeof manager.getSessionDir !== "function"
|
|
208
|
+
|| typeof manager.usesDefaultSessionDir !== "function"
|
|
209
|
+
|| manager.isPersisted() !== true
|
|
210
|
+
|| !existsSync(this.currentSessionFile() ?? ""))
|
|
211
|
+
return null;
|
|
212
|
+
const sessionId = manager.getSessionId();
|
|
213
|
+
const sessionDir = manager.getSessionDir();
|
|
214
|
+
if (!sessionId || !sessionDir)
|
|
215
|
+
return null;
|
|
216
|
+
return { sessionId, sessionDir, usesDefaultSessionDir: manager.usesDefaultSessionDir() };
|
|
217
|
+
}
|
|
97
218
|
get disposed() {
|
|
98
219
|
return this.#disposed;
|
|
99
220
|
}
|
|
221
|
+
suggestionReasoningPolicy() {
|
|
222
|
+
const session = this.#session;
|
|
223
|
+
if (!session?.model)
|
|
224
|
+
return "unavailable";
|
|
225
|
+
if (!session.model.reasoning)
|
|
226
|
+
return "ordinary";
|
|
227
|
+
const levels = session.getAvailableThinkingLevels?.() ?? [];
|
|
228
|
+
// Compatibility: capability order, not the user's current setting or a provider-name heuristic.
|
|
229
|
+
return ["off", "minimal", "low", "medium", "high", "xhigh", "max"]
|
|
230
|
+
.find(level => levels.includes(level)) ?? "unavailable";
|
|
231
|
+
}
|
|
232
|
+
async generate(request) {
|
|
233
|
+
assertOwnedUiPromptSuggestionRequest(request);
|
|
234
|
+
const identity = request.identity;
|
|
235
|
+
const session = this.#session;
|
|
236
|
+
const runtime = this.#runtime;
|
|
237
|
+
const activeModel = this.#activeModel;
|
|
238
|
+
if (this.#disposed || this.#overload !== undefined || this.#admissionStopped || session === undefined || runtime === undefined || request.signal.aborted
|
|
239
|
+
|| identity.sessionId !== this.#sessionId
|
|
240
|
+
|| identity.sessionGeneration !== this.#sessionGeneration
|
|
241
|
+
|| identity.runSequence !== this.#agentRunSequence
|
|
242
|
+
|| identity.responseSequence !== this.#assistantResponseSequence
|
|
243
|
+
|| activeModel === null
|
|
244
|
+
|| identity.model.providerId !== activeModel.providerId
|
|
245
|
+
|| identity.model.modelId !== activeModel.modelId) {
|
|
246
|
+
return { identity, outcome: request.signal.aborted ? "cancelled" : "unavailable", text: null };
|
|
247
|
+
}
|
|
248
|
+
const model = session.model;
|
|
249
|
+
const agentState = session.agent.state;
|
|
250
|
+
const policy = this.suggestionReasoningPolicy();
|
|
251
|
+
if (model === undefined || policy === "unavailable" || typeof runtime.services.modelRuntime.completeSimple !== "function") {
|
|
252
|
+
return { identity, outcome: "unavailable", text: null };
|
|
253
|
+
}
|
|
254
|
+
const messages = agentState.messages.filter(message => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
|
|
255
|
+
const reasoning = policy === "ordinary" || policy === "off" ? undefined : policy;
|
|
256
|
+
let response;
|
|
257
|
+
try {
|
|
258
|
+
response = await runtime.services.modelRuntime.completeSimple(model, {
|
|
259
|
+
systemPrompt: agentState.systemPrompt,
|
|
260
|
+
messages: [
|
|
261
|
+
...messages,
|
|
262
|
+
{ role: "user", content: CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, timestamp: Date.now() },
|
|
263
|
+
],
|
|
264
|
+
tools: agentState.tools,
|
|
265
|
+
}, {
|
|
266
|
+
signal: request.signal,
|
|
267
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return { identity, outcome: request.signal.aborted ? "cancelled" : "provider-failure", text: null };
|
|
272
|
+
}
|
|
273
|
+
if (request.signal.aborted || (isRecord(response) && response.stopReason === "aborted")) {
|
|
274
|
+
return { identity, outcome: "cancelled", text: null };
|
|
275
|
+
}
|
|
276
|
+
if (!isRecord(response) || stringValue(response.errorMessage) !== undefined || response.stopReason === "error") {
|
|
277
|
+
return { identity, outcome: "provider-failure", text: null };
|
|
278
|
+
}
|
|
279
|
+
const content = Array.isArray(response.content) ? response.content : [];
|
|
280
|
+
if (response.stopReason === "length" || content.some(block => isRecord(block) && block.type === "toolCall")) {
|
|
281
|
+
return { identity, outcome: "rejected", text: null };
|
|
282
|
+
}
|
|
283
|
+
// Security: validate all text, not just the first block of a multi-part response.
|
|
284
|
+
const rawText = content.filter(block => isRecord(block) && block.type === "text")
|
|
285
|
+
.map(block => stringValue(block.text) ?? "").join("\n");
|
|
286
|
+
const text = normalizePromptSuggestionCandidate(rawText);
|
|
287
|
+
const result = text === null
|
|
288
|
+
? { identity, outcome: rawText.trim() ? "rejected" : "empty", text: null }
|
|
289
|
+
: { identity, outcome: "candidate", text };
|
|
290
|
+
assertOwnedUiPromptSuggestionResult(result);
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
100
293
|
async start() {
|
|
101
294
|
if (this.#runtime)
|
|
102
295
|
return this.view();
|
|
@@ -104,18 +297,25 @@ export class PiEngineAdapter {
|
|
|
104
297
|
cwd: this.#cwd,
|
|
105
298
|
agentDir: this.#agentDir,
|
|
106
299
|
sessionId: this.#sessionId,
|
|
300
|
+
...(this.#sessionPath === undefined ? {} : { sessionPath: this.#sessionPath }),
|
|
301
|
+
...(this.#sessionSelection === undefined ? {} : { sessionSelection: this.#sessionSelection }),
|
|
302
|
+
...(this.#sessionForkPrompt === undefined ? {} : { sessionForkPrompt: this.#sessionForkPrompt }),
|
|
303
|
+
...(this.#projectTrustPrompt === undefined ? {} : { projectTrustPrompt: this.#projectTrustPrompt }),
|
|
107
304
|
}).catch(error => {
|
|
108
305
|
this.#lifecycle = "failed";
|
|
109
306
|
this.#addDiagnostic("error", "engine-startup", error instanceof Error ? error.message : String(error), false);
|
|
110
307
|
throw error;
|
|
111
308
|
});
|
|
112
309
|
this.#runtime = runtime;
|
|
310
|
+
this.#cwd = runtime.cwd ?? this.#cwd;
|
|
113
311
|
this.#gitBranch = await readGitBranch(this.#cwd);
|
|
114
312
|
this.#terminal = {
|
|
115
313
|
...this.#terminal,
|
|
116
314
|
hardwareCursor: runtime.services.settingsManager?.getShowHardwareCursor?.() ?? this.#terminal.hardwareCursor,
|
|
117
315
|
};
|
|
118
316
|
runtime.setRebindSession(async (session) => {
|
|
317
|
+
if (this.#overload !== undefined || this.#admissionStopped || this.#disposed)
|
|
318
|
+
return;
|
|
119
319
|
this.#bindSession(session);
|
|
120
320
|
this.#emitView();
|
|
121
321
|
});
|
|
@@ -123,6 +323,7 @@ export class PiEngineAdapter {
|
|
|
123
323
|
this.#addDiagnostic(diagnostic.type === "error" ? "error" : diagnostic.type === "warning" ? "warning" : "info", "engine-startup", diagnostic.message, diagnostic.type !== "error");
|
|
124
324
|
}
|
|
125
325
|
this.#bindSession(runtime.session);
|
|
326
|
+
await this.#announceChangelog(runtime.services.settingsManager);
|
|
126
327
|
this.#lifecycle = "ready";
|
|
127
328
|
this.#editor = { ...this.#editor, submitEnabled: true };
|
|
128
329
|
this.#emitEvent({ type: "session-lifecycle", lifecycle: "ready", reason: null });
|
|
@@ -130,6 +331,27 @@ export class PiEngineAdapter {
|
|
|
130
331
|
void this.#announcePackageUpdates(runtime.services.settingsManager);
|
|
131
332
|
return this.view();
|
|
132
333
|
}
|
|
334
|
+
async #announceChangelog(settingsManager) {
|
|
335
|
+
if (!settingsManager || typeof settingsManager.getLastChangelogVersion !== "function"
|
|
336
|
+
|| typeof settingsManager.setLastChangelogVersion !== "function")
|
|
337
|
+
return;
|
|
338
|
+
if ((this.#session?.messages.length ?? 0) > 0)
|
|
339
|
+
return;
|
|
340
|
+
const lastVersion = settingsManager.getLastChangelogVersion();
|
|
341
|
+
if (lastVersion === VERSION)
|
|
342
|
+
return;
|
|
343
|
+
if (!lastVersion) {
|
|
344
|
+
settingsManager.setLastChangelogVersion(VERSION);
|
|
345
|
+
await settingsManager.flush();
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const markdown = await this.#workflowHost.readChangelog(lastVersion).catch(() => "");
|
|
349
|
+
if (markdown.trim().length > 0) {
|
|
350
|
+
this.#addDiagnostic("info", settingsManager.getCollapseChangelog() ? "changelog-collapsed" : "changelog-expanded", markdown, true);
|
|
351
|
+
settingsManager.setLastChangelogVersion(VERSION);
|
|
352
|
+
await settingsManager.flush();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
133
355
|
async #announcePackageUpdates(settingsManager) {
|
|
134
356
|
if (process.env.PI_OFFLINE)
|
|
135
357
|
return;
|
|
@@ -143,17 +365,28 @@ export class PiEngineAdapter {
|
|
|
143
365
|
if (this.#disposed || updates.length === 0)
|
|
144
366
|
return;
|
|
145
367
|
const packages = updates.map(name => `- ${name}`).join("\n");
|
|
146
|
-
this.#addDiagnostic("info", "package-updates", `Package updates are available. Run pi update --extensions\nPackages:\n${packages}`, true);
|
|
368
|
+
this.#addDiagnostic("info", "package-updates", `Package updates are available. Run ${PRODUCT_IDENTITY.commandName} pi update --extensions\nPackages:\n${packages}`, true);
|
|
147
369
|
this.#emitView();
|
|
148
370
|
}
|
|
149
371
|
onEvent(listener) {
|
|
150
|
-
this.#
|
|
151
|
-
|
|
372
|
+
const initial = this.#event({ type: "session-view", view: this.view() });
|
|
373
|
+
this.#listeners.set(listener, initial.sequence);
|
|
374
|
+
listener(initial);
|
|
152
375
|
return () => this.#listeners.delete(listener);
|
|
153
376
|
}
|
|
377
|
+
/** Developer-only pressure evidence; never mirrored into visible diagnostic/status arrays. */
|
|
378
|
+
deliveryDiagnostics() {
|
|
379
|
+
return { ...this.#eventQueue.diagnostics(), overloads: this.#overloads, recovering: this.#overload !== undefined,
|
|
380
|
+
pendingCommands: this.#pendingCommands.size + this.#pendingWorkflows.size, reservedOutcomes: this.#reservedOutcomes.size, invalidatedEvents: this.#invalidatedEvents };
|
|
381
|
+
}
|
|
154
382
|
async flushEvents() {
|
|
383
|
+
const failed = this.#deliveryFailed;
|
|
155
384
|
while (this.#eventQueueProcessing)
|
|
156
385
|
await this.#eventQueueProcessing;
|
|
386
|
+
if (failed || this.#deliveryFailed || this.#disposed && this.#lifecycle !== "stopped") {
|
|
387
|
+
this.#deliveryFailed = false;
|
|
388
|
+
throw new EngineDeliveryError();
|
|
389
|
+
}
|
|
157
390
|
}
|
|
158
391
|
nonVisualResources() {
|
|
159
392
|
const loader = this.#runtime?.services.resourceLoader;
|
|
@@ -278,6 +511,7 @@ export class PiEngineAdapter {
|
|
|
278
511
|
id: `extension-${resources.length}`,
|
|
279
512
|
sourcePath: extension.path,
|
|
280
513
|
resolvedPath: extension.resolvedPath,
|
|
514
|
+
sourceInfo: extensionSourceSummary(extension.sourceInfo),
|
|
281
515
|
loaded: true,
|
|
282
516
|
hidden: extension.hidden === true,
|
|
283
517
|
diagnostic: null,
|
|
@@ -373,9 +607,10 @@ export class PiEngineAdapter {
|
|
|
373
607
|
}
|
|
374
608
|
const extensionCommands = this.#session?.extensionRunner?.getRegisteredCommands?.();
|
|
375
609
|
if (Array.isArray(extensionCommands)) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
610
|
+
const registered = extensionCommands.filter(isRecord);
|
|
611
|
+
for (const command of registered) {
|
|
612
|
+
const name = stringProperty(command, "invocationName") ?? stringProperty(command, "name");
|
|
613
|
+
if (!name || usedNames.has(name) || isPiPrefixedCompatibilityAlias(command, registered))
|
|
379
614
|
continue;
|
|
380
615
|
commands.push({ name, description: stringProperty(command, "description") ?? "Extension command", source: "extension" });
|
|
381
616
|
usedNames.add(name);
|
|
@@ -388,7 +623,8 @@ export class PiEngineAdapter {
|
|
|
388
623
|
const session = this.#requireWorkflowSession();
|
|
389
624
|
const result = await requireCapability(session.cycleModel, "cycleModel").call(session, direction);
|
|
390
625
|
if (!isRecord(result) || !isRecord(result.model)) {
|
|
391
|
-
|
|
626
|
+
const scoped = Array.isArray(session.scopedModels) && session.scopedModels.length > 0;
|
|
627
|
+
return workflowResult("model", "completed", scoped ? "Only one model in scope" : "Only one model available", undefined, "status");
|
|
392
628
|
}
|
|
393
629
|
const model = readModel(result.model);
|
|
394
630
|
if (model)
|
|
@@ -396,7 +632,11 @@ export class PiEngineAdapter {
|
|
|
396
632
|
if (result.thinkingLevel !== undefined)
|
|
397
633
|
this.#thinkingLevel = readThinkingLevel(result.thinkingLevel);
|
|
398
634
|
this.#emitView();
|
|
399
|
-
|
|
635
|
+
const modelName = stringProperty(result.model, "name") ?? stringProperty(result.model, "id") ?? "model";
|
|
636
|
+
const reasoning = result.model.reasoning === true;
|
|
637
|
+
const thinking = this.#thinkingLevel;
|
|
638
|
+
const suffix = reasoning && thinking !== "off" ? ` (thinking: ${thinking})` : "";
|
|
639
|
+
return workflowResult("model", "completed", `Switched to ${modelName}${suffix}`, undefined, "status");
|
|
400
640
|
}
|
|
401
641
|
catch (error) {
|
|
402
642
|
return workflowResult("model", "failed", error instanceof Error ? error.message : String(error));
|
|
@@ -412,16 +652,107 @@ export class PiEngineAdapter {
|
|
|
412
652
|
/** Settings port for the live runtime, or null before the runtime is available. */
|
|
413
653
|
settingsPort() {
|
|
414
654
|
const settings = this.#runtime?.services.settingsManager;
|
|
415
|
-
if (!settings)
|
|
655
|
+
if (!settings || typeof settings.getCompactionEnabled !== "function")
|
|
416
656
|
return null;
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
657
|
+
if (this.#settingsIntegration === undefined || this.#settingsIntegrationManager !== settings) {
|
|
658
|
+
this.#settingsIntegrationManager = settings;
|
|
659
|
+
configureOwnedHttpDispatcher(settings.getHttpIdleTimeoutMs());
|
|
660
|
+
this.#settingsIntegration = new PiSettingsIntegration(settings, {
|
|
661
|
+
...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
|
|
662
|
+
thinkingLevels: () => {
|
|
663
|
+
const levels = this.#runtime?.session?.getAvailableThinkingLevels?.();
|
|
664
|
+
return Array.isArray(levels) ? levels.map(level => String(level)) : [];
|
|
665
|
+
},
|
|
666
|
+
productMode: this.#settingsProductMode,
|
|
667
|
+
});
|
|
668
|
+
this.#settingsIntegration.bindOwner("shell", {
|
|
669
|
+
enableSkillCommands: { apply: value => {
|
|
670
|
+
if (typeof value !== "boolean")
|
|
671
|
+
throw new TypeError("Skill commands value is invalid");
|
|
672
|
+
settings.setEnableSkillCommands(value);
|
|
673
|
+
} },
|
|
674
|
+
doubleEscapeAction: { apply: value => {
|
|
675
|
+
if (value !== "tree" && value !== "fork" && value !== "none")
|
|
676
|
+
throw new TypeError("Double-Escape action is invalid");
|
|
677
|
+
settings.setDoubleEscapeAction(value);
|
|
678
|
+
} },
|
|
679
|
+
treeFilterMode: { apply: value => {
|
|
680
|
+
if (value !== "default" && value !== "no-tools" && value !== "user-only" && value !== "labeled-only" && value !== "all")
|
|
681
|
+
throw new TypeError("Tree filter mode is invalid");
|
|
682
|
+
settings.setTreeFilterMode(value);
|
|
683
|
+
} },
|
|
684
|
+
showCacheMissNotices: { apply: value => {
|
|
685
|
+
if (typeof value !== "boolean")
|
|
686
|
+
throw new TypeError("Cache-miss notice setting is invalid");
|
|
687
|
+
settings.setShowCacheMissNotices(value);
|
|
688
|
+
} },
|
|
689
|
+
});
|
|
690
|
+
this.#settingsIntegration.bindOwner("startup", {
|
|
691
|
+
// Invariant: deferred application is the owner operation: the next preflight reads
|
|
692
|
+
// the persisted default before constructing project-backed services.
|
|
693
|
+
defaultProjectTrust: { apply() { } },
|
|
694
|
+
collapseChangelog: { apply() { } },
|
|
695
|
+
});
|
|
696
|
+
this.#settingsIntegration.bindOwner("agent", {
|
|
697
|
+
autoCompact: { apply: value => {
|
|
698
|
+
if (typeof value !== "boolean")
|
|
699
|
+
throw new TypeError("Auto compact value is invalid");
|
|
700
|
+
this.#requireWorkflowSession().setAutoCompactionEnabled(value);
|
|
701
|
+
} },
|
|
702
|
+
autoResizeImages: { apply: value => {
|
|
703
|
+
if (typeof value !== "boolean")
|
|
704
|
+
throw new TypeError("Auto-resize images value is invalid");
|
|
705
|
+
settings.setImageAutoResize(value);
|
|
706
|
+
} },
|
|
707
|
+
blockImages: { apply: value => {
|
|
708
|
+
if (typeof value !== "boolean")
|
|
709
|
+
throw new TypeError("Block images value is invalid");
|
|
710
|
+
settings.setBlockImages(value);
|
|
711
|
+
} },
|
|
712
|
+
steeringMode: { apply: value => {
|
|
713
|
+
if (value !== "all" && value !== "one-at-a-time")
|
|
714
|
+
throw new TypeError("Steering mode is invalid");
|
|
715
|
+
this.#requireWorkflowSession().setSteeringMode(value);
|
|
716
|
+
} },
|
|
717
|
+
followUpMode: { apply: value => {
|
|
718
|
+
if (value !== "all" && value !== "one-at-a-time")
|
|
719
|
+
throw new TypeError("Follow-up mode is invalid");
|
|
720
|
+
this.#requireWorkflowSession().setFollowUpMode(value);
|
|
721
|
+
} },
|
|
722
|
+
transport: { apply: value => {
|
|
723
|
+
if (value !== "sse" && value !== "websocket" && value !== "websocket-cached" && value !== "auto")
|
|
724
|
+
throw new TypeError("Transport is invalid");
|
|
725
|
+
this.#requireWorkflowSession().agent.transport = value;
|
|
726
|
+
} },
|
|
727
|
+
httpIdleTimeoutMs: { apply: value => {
|
|
728
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
|
|
729
|
+
throw new TypeError("HTTP idle timeout is invalid");
|
|
730
|
+
// Invariant: provider streaming reads the manager per request; global fetch uses
|
|
731
|
+
// the matching owned dispatcher and zero maps to disabled semantics.
|
|
732
|
+
configureOwnedHttpDispatcher(value);
|
|
733
|
+
settings.setHttpIdleTimeoutMs(value);
|
|
734
|
+
} },
|
|
735
|
+
thinkingLevel: { apply: value => {
|
|
736
|
+
if (!isThinkingLevel(value))
|
|
737
|
+
throw new TypeError("Thinking level is invalid");
|
|
738
|
+
this.#requireWorkflowSession().setThinkingLevel(value);
|
|
739
|
+
this.#thinkingLevel = readThinkingLevel(this.#requireWorkflowSession().thinkingLevel);
|
|
740
|
+
this.#emitView();
|
|
741
|
+
} },
|
|
742
|
+
warnings: { apply: value => {
|
|
743
|
+
if (!isRecord(value) || Object.values(value).some(flag => typeof flag !== "boolean"))
|
|
744
|
+
throw new TypeError("Warnings setting is invalid");
|
|
745
|
+
settings.setWarnings(value);
|
|
746
|
+
} },
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
return this.#settingsIntegration;
|
|
750
|
+
}
|
|
751
|
+
bindSettingsOwner(owner, handlers) {
|
|
752
|
+
const settings = this.settingsPort();
|
|
753
|
+
if (settings === null)
|
|
754
|
+
return () => { };
|
|
755
|
+
return settings.bindOwner(owner, handlers);
|
|
425
756
|
}
|
|
426
757
|
pinnedModelSelectorContext() {
|
|
427
758
|
const session = this.#requireWorkflowSession();
|
|
@@ -454,17 +785,25 @@ export class PiEngineAdapter {
|
|
|
454
785
|
if (!runtime)
|
|
455
786
|
throw new Error("engine runtime is unavailable");
|
|
456
787
|
const cwd = resolve(this.#cwd);
|
|
457
|
-
|
|
788
|
+
let trustPath = cwd;
|
|
789
|
+
try {
|
|
790
|
+
trustPath = realpathSync(cwd);
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
// Compatibility: pinned Pi retains the resolved path when real-path lookup fails.
|
|
794
|
+
}
|
|
795
|
+
// Security: trust the target's parent, not the parent containing a directory alias.
|
|
796
|
+
const parent = dirname(trustPath);
|
|
458
797
|
const trustStore = new ProjectTrustStore(this.#agentDir);
|
|
459
798
|
const trustOptions = [
|
|
460
|
-
{ label: "Trust", trusted: true, updates: [{ path:
|
|
461
|
-
...(parent ===
|
|
799
|
+
{ label: "Trust", trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },
|
|
800
|
+
...(parent === trustPath ? [] : [{
|
|
462
801
|
label: `Trust parent folder (${parent})`,
|
|
463
802
|
trusted: true,
|
|
464
|
-
updates: [{ path: parent, decision: true }, { path:
|
|
803
|
+
updates: [{ path: parent, decision: true }, { path: trustPath, decision: null }],
|
|
465
804
|
savedPath: parent,
|
|
466
805
|
}]),
|
|
467
|
-
{ label: "Do not trust", trusted: false, updates: [{ path:
|
|
806
|
+
{ label: "Do not trust", trusted: false, updates: [{ path: trustPath, decision: false }], savedPath: trustPath },
|
|
468
807
|
];
|
|
469
808
|
return {
|
|
470
809
|
cwd,
|
|
@@ -591,6 +930,17 @@ export class PiEngineAdapter {
|
|
|
591
930
|
}));
|
|
592
931
|
return { title: `Select authentication method for ${providerName}:`, options };
|
|
593
932
|
}
|
|
933
|
+
pinnedAmbientAuthentication(selection) {
|
|
934
|
+
if (!selection.startsWith("api_key:"))
|
|
935
|
+
return null;
|
|
936
|
+
const providerId = selection.slice("api_key:".length);
|
|
937
|
+
const provider = this.#runtime?.services.modelRuntime.getProvider?.(providerId);
|
|
938
|
+
const method = dynamicObject(dynamicObject(provider, "auth"), "apiKey");
|
|
939
|
+
if (!method || typeof method.login === "function")
|
|
940
|
+
return null;
|
|
941
|
+
const providerName = stringProperty(provider, "name") ?? providerId;
|
|
942
|
+
return { providerId, providerName, title: `${providerName} setup`, message: `${stringProperty(method, "name") ?? "Authentication"} is configured outside pi.` };
|
|
943
|
+
}
|
|
594
944
|
pinnedLogoutOptions() {
|
|
595
945
|
return this.#logoutOptions();
|
|
596
946
|
}
|
|
@@ -671,9 +1021,27 @@ export class PiEngineAdapter {
|
|
|
671
1021
|
pinnedMessageRenderer(customType) {
|
|
672
1022
|
return this.#requireWorkflowSession().extensionRunner?.getMessageRenderer?.(customType);
|
|
673
1023
|
}
|
|
1024
|
+
pinnedShortcutDescriptions(bindings) {
|
|
1025
|
+
const shortcuts = this.#requireWorkflowSession().extensionRunner?.getShortcuts?.(bindings);
|
|
1026
|
+
return shortcuts === undefined ? [] : [...shortcuts].map(([key, shortcut]) => ({ key, description: shortcut.description ?? shortcut.extensionPath }));
|
|
1027
|
+
}
|
|
674
1028
|
pinnedToolDefinition(toolName) {
|
|
675
1029
|
return this.#requireWorkflowSession().extensionRunner?.getToolDefinition?.(toolName);
|
|
676
1030
|
}
|
|
1031
|
+
/** Bind the owned UI's clipboard lifecycle without changing the comparison host. True means acknowledged delivery. */
|
|
1032
|
+
bindClipboardWriter(writer) {
|
|
1033
|
+
const previous = this.#clipboardWriter;
|
|
1034
|
+
this.#clipboardWriter = writer;
|
|
1035
|
+
return () => { if (this.#clipboardWriter === writer)
|
|
1036
|
+
this.#clipboardWriter = previous; };
|
|
1037
|
+
}
|
|
1038
|
+
/** Workflow and tree copying share the active owner's write fence and delivery acknowledgment. */
|
|
1039
|
+
async copyWorkflowText(text) {
|
|
1040
|
+
if (this.#clipboardWriter !== undefined)
|
|
1041
|
+
return this.#clipboardWriter(text);
|
|
1042
|
+
await this.#workflowHost.copyText(text);
|
|
1043
|
+
return true;
|
|
1044
|
+
}
|
|
677
1045
|
clearQueuedWorkflows() {
|
|
678
1046
|
const session = this.#requireWorkflowSession();
|
|
679
1047
|
const result = session.clearQueue?.();
|
|
@@ -698,23 +1066,55 @@ export class PiEngineAdapter {
|
|
|
698
1066
|
excludeFromContext,
|
|
699
1067
|
};
|
|
700
1068
|
}
|
|
701
|
-
applyPinnedSettingValue(callback, value) {
|
|
1069
|
+
async applyPinnedSettingValue(callback, value) {
|
|
702
1070
|
try {
|
|
703
|
-
return this.#applyPinnedSetting(callback, value, true);
|
|
1071
|
+
return await this.#applyPinnedSetting(callback, value, true);
|
|
704
1072
|
}
|
|
705
1073
|
catch (error) {
|
|
706
1074
|
return workflowResult("settings", "failed", error instanceof Error ? error.message : String(error));
|
|
707
1075
|
}
|
|
708
1076
|
}
|
|
1077
|
+
reloadBlockedResult() {
|
|
1078
|
+
const session = this.#requireWorkflowSession();
|
|
1079
|
+
if (session.isStreaming)
|
|
1080
|
+
return workflowResult("reload", "failed", "Wait for the current response to finish before reloading.", undefined, "warning");
|
|
1081
|
+
if (session.isCompacting)
|
|
1082
|
+
return workflowResult("reload", "failed", "Wait for compaction to finish before reloading.", undefined, "warning");
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
709
1085
|
async executeWorkflow(request) {
|
|
1086
|
+
const cancelled = workflowResult(request.command, "cancelled", "", undefined, "silent");
|
|
1087
|
+
if (this.#overload !== undefined || this.#admissionStopped || this.#disposed
|
|
1088
|
+
|| this.#pendingCommands.size + this.#pendingWorkflows.size >= 32)
|
|
1089
|
+
return cancelled;
|
|
1090
|
+
let cancel;
|
|
1091
|
+
const cancellation = new Promise(resolve => { cancel = () => resolve(cancelled); });
|
|
1092
|
+
const pending = { command: request.command, cancel };
|
|
1093
|
+
this.#pendingWorkflows.add(pending);
|
|
1094
|
+
this.#runningCommands++;
|
|
1095
|
+
const operation = this.#runWorkflow(request).finally(() => { this.#runningCommands--; });
|
|
1096
|
+
try {
|
|
1097
|
+
return await Promise.race([operation, cancellation]);
|
|
1098
|
+
}
|
|
1099
|
+
finally {
|
|
1100
|
+
this.#pendingWorkflows.delete(pending);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async #runWorkflow(request) {
|
|
710
1104
|
try {
|
|
711
1105
|
return await this.#performWorkflow(request);
|
|
712
1106
|
}
|
|
713
1107
|
catch (error) {
|
|
714
1108
|
const message = error instanceof Error ? error.message : String(error);
|
|
715
1109
|
const contextualMessage = request.command === "export"
|
|
716
|
-
? `Failed to export session: ${
|
|
717
|
-
: request.command === "
|
|
1110
|
+
? `Failed to export session: ${errorMessage(error, "Unknown error")}`
|
|
1111
|
+
: request.command === "import"
|
|
1112
|
+
? `Failed to import session: ${message}`
|
|
1113
|
+
: request.command === "new"
|
|
1114
|
+
? `Failed to create session: ${message}`
|
|
1115
|
+
: request.command === "resume"
|
|
1116
|
+
? `Failed to resume session: ${message}`
|
|
1117
|
+
: request.command === "reload" ? `Reload failed: ${message}` : message;
|
|
718
1118
|
return workflowResult(request.command, "failed", contextualMessage);
|
|
719
1119
|
}
|
|
720
1120
|
}
|
|
@@ -724,13 +1124,13 @@ export class PiEngineAdapter {
|
|
|
724
1124
|
sessionId: this.#sessionId,
|
|
725
1125
|
revision: this.#viewRevision,
|
|
726
1126
|
lifecycle: this.#lifecycle,
|
|
727
|
-
transcript: this.#
|
|
1127
|
+
transcript: this.#transcriptSnapshot ??= Object.freeze([...this.#transcript]),
|
|
728
1128
|
editor: { ...this.#editor, queuedSubmissions: [...this.#editor.queuedSubmissions] },
|
|
729
1129
|
status: {
|
|
730
1130
|
...this.#status,
|
|
731
1131
|
diagnostics: [...this.#status.diagnostics],
|
|
732
1132
|
badges: [...this.#status.badges],
|
|
733
|
-
usage: this.#readUsage(),
|
|
1133
|
+
usage: this.#usageCache ??= this.#readUsage(),
|
|
734
1134
|
footer: {
|
|
735
1135
|
branch: this.#gitBranch,
|
|
736
1136
|
sessionName: this.#session?.sessionManager?.getSessionName() ?? null,
|
|
@@ -767,6 +1167,9 @@ export class PiEngineAdapter {
|
|
|
767
1167
|
if (this.#disposed || !this.#runtime || !this.#session) {
|
|
768
1168
|
return this.#finishCommand(command, "rejected", "engine adapter is not running");
|
|
769
1169
|
}
|
|
1170
|
+
// Invariant: the bounded out-of-band cancellation path has one slot per admitted command.
|
|
1171
|
+
if (this.#overload !== undefined || this.#admissionStopped || this.#pendingCommands.size + this.#pendingWorkflows.size >= 32)
|
|
1172
|
+
return { outcome: "rejected", diagnostic: null };
|
|
770
1173
|
const existing = this.#completedCommands.get(command.correlationId);
|
|
771
1174
|
if (existing)
|
|
772
1175
|
return existing;
|
|
@@ -774,28 +1177,65 @@ export class PiEngineAdapter {
|
|
|
774
1177
|
return this.#finishCommand(command, "rejected", "duplicate engine command correlation id");
|
|
775
1178
|
}
|
|
776
1179
|
this.#activeCommandIds.push(command.correlationId);
|
|
777
|
-
this.#
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1180
|
+
const generation = this.#sessionGeneration;
|
|
1181
|
+
let cancelled = false;
|
|
1182
|
+
const cancellation = new Promise(resolve => {
|
|
1183
|
+
this.#pendingCommands.set(command.correlationId, { type: command.type, cancel: () => {
|
|
1184
|
+
if (cancelled)
|
|
1185
|
+
return;
|
|
1186
|
+
cancelled = true;
|
|
1187
|
+
resolve(this.#recordCommand(command, "failed", null));
|
|
1188
|
+
} });
|
|
782
1189
|
});
|
|
1190
|
+
this.#emitEvent({ type: "command-outcome", correlationId: command.correlationId, outcome: "accepted", diagnostic: null });
|
|
1191
|
+
const operation = async () => {
|
|
1192
|
+
try {
|
|
1193
|
+
if (cancelled)
|
|
1194
|
+
return { outcome: "failed", diagnostic: null };
|
|
1195
|
+
this.#runningCommands++;
|
|
1196
|
+
try {
|
|
1197
|
+
await this.#perform(command);
|
|
1198
|
+
}
|
|
1199
|
+
finally {
|
|
1200
|
+
this.#runningCommands--;
|
|
1201
|
+
}
|
|
1202
|
+
if (cancelled)
|
|
1203
|
+
return { outcome: "failed", diagnostic: null };
|
|
1204
|
+
if (generation === this.#sessionGeneration)
|
|
1205
|
+
this.#emitView();
|
|
1206
|
+
if (cancelled)
|
|
1207
|
+
return { outcome: "failed", diagnostic: null };
|
|
1208
|
+
return this.#recordCommand(command, "completed", null);
|
|
1209
|
+
}
|
|
1210
|
+
catch (error) {
|
|
1211
|
+
if (cancelled)
|
|
1212
|
+
return { outcome: "failed", diagnostic: null };
|
|
1213
|
+
const diagnostic = error instanceof Error ? error.message : String(error);
|
|
1214
|
+
this.#addDiagnostic("error", "engine-command", diagnostic, true);
|
|
1215
|
+
if (generation === this.#sessionGeneration)
|
|
1216
|
+
this.#emitView();
|
|
1217
|
+
return this.#recordCommand(command, "failed", diagnostic);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
783
1220
|
try {
|
|
784
|
-
await
|
|
785
|
-
this.#emitView();
|
|
786
|
-
return this.#recordCommand(command, "completed", null);
|
|
1221
|
+
return await Promise.race([operation(), cancellation]);
|
|
787
1222
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
this.#addDiagnostic("error", "engine-command", diagnostic, true);
|
|
791
|
-
this.#emitView();
|
|
792
|
-
return this.#recordCommand(command, "failed", diagnostic);
|
|
1223
|
+
finally {
|
|
1224
|
+
this.#pendingCommands.delete(command.correlationId);
|
|
793
1225
|
}
|
|
794
1226
|
}
|
|
795
1227
|
async dispose() {
|
|
796
1228
|
if (this.#disposed)
|
|
797
1229
|
return;
|
|
798
1230
|
this.#disposed = true;
|
|
1231
|
+
for (const pending of this.#pendingCommands.values())
|
|
1232
|
+
if (pending.type !== "shutdown")
|
|
1233
|
+
pending.cancel();
|
|
1234
|
+
// Compatibility: /quit owns normal disposal and must report its actual completion, not cancel itself.
|
|
1235
|
+
for (const pending of this.#pendingWorkflows)
|
|
1236
|
+
if (pending.command !== "quit")
|
|
1237
|
+
pending.cancel();
|
|
1238
|
+
this.#transcriptImageAssets.clear();
|
|
799
1239
|
this.#extensionBound = false;
|
|
800
1240
|
this.#extensionUi = undefined;
|
|
801
1241
|
this.#extensionShutdown = undefined;
|
|
@@ -807,7 +1247,13 @@ export class PiEngineAdapter {
|
|
|
807
1247
|
this.#lifecycle = "stopped";
|
|
808
1248
|
this.#emitEvent({ type: "session-lifecycle", lifecycle: "stopped", reason: null });
|
|
809
1249
|
this.#emitView();
|
|
810
|
-
|
|
1250
|
+
try {
|
|
1251
|
+
await this.flushEvents();
|
|
1252
|
+
}
|
|
1253
|
+
catch (error) {
|
|
1254
|
+
if (!(error instanceof EngineDeliveryError))
|
|
1255
|
+
throw error;
|
|
1256
|
+
}
|
|
811
1257
|
}
|
|
812
1258
|
async #performWorkflow(request) {
|
|
813
1259
|
const session = this.#requireWorkflowSession();
|
|
@@ -821,22 +1267,88 @@ export class PiEngineAdapter {
|
|
|
821
1267
|
case "settings": {
|
|
822
1268
|
if (!selection)
|
|
823
1269
|
return workflowResult(request.command, "failed", "Settings requires the owned settings controller");
|
|
824
|
-
return this.#applyPinnedSetting(selection);
|
|
1270
|
+
return await this.#applyPinnedSetting(selection);
|
|
825
1271
|
}
|
|
826
1272
|
case "model": {
|
|
827
1273
|
const reference = selection ?? argument;
|
|
828
1274
|
if (!reference)
|
|
829
1275
|
return workflowResult(request.command, "failed", "Model requires the owned model controller");
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1276
|
+
const scopedModels = Array.isArray(session.scopedModels)
|
|
1277
|
+
? session.scopedModels.map(item => item.model)
|
|
1278
|
+
: [];
|
|
1279
|
+
let availableModels = scopedModels.length > 0
|
|
1280
|
+
? scopedModels
|
|
1281
|
+
: [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
|
|
1282
|
+
let model = findExactWorkflowModel(reference, availableModels);
|
|
1283
|
+
const messages = [];
|
|
1284
|
+
const generation = this.#sessionGeneration;
|
|
1285
|
+
const current = () => !this.#disposed && generation === this.#sessionGeneration;
|
|
1286
|
+
const publish = (message) => {
|
|
1287
|
+
if (!current())
|
|
1288
|
+
return;
|
|
1289
|
+
if (this.#workflowInteraction.publish)
|
|
1290
|
+
this.#workflowInteraction.publish(message);
|
|
1291
|
+
else
|
|
1292
|
+
messages.push(message);
|
|
1293
|
+
};
|
|
1294
|
+
if (model === undefined && scopedModels.length === 0) {
|
|
1295
|
+
publish({ kind: "status", message: "Refreshing model catalogs…" });
|
|
1296
|
+
const controller = new AbortController();
|
|
1297
|
+
let timedOut = false;
|
|
1298
|
+
const timeout = setTimeout(() => {
|
|
1299
|
+
timedOut = true;
|
|
1300
|
+
controller.abort();
|
|
1301
|
+
}, AUTH_REFRESH_TIMEOUT_MS);
|
|
1302
|
+
try {
|
|
1303
|
+
const refreshed = await runtime.services.modelRuntime.refresh?.({ signal: controller.signal });
|
|
1304
|
+
if (isRecord(refreshed) && refreshed.aborted === true && timedOut) {
|
|
1305
|
+
publish({ kind: "warning", message: "Model refresh timed out; searching cached models." });
|
|
1306
|
+
}
|
|
1307
|
+
else if (isRecord(refreshed) && refreshed.errors instanceof Map && refreshed.errors.size > 0) {
|
|
1308
|
+
publish({ kind: "warning", message: `Could not refresh ${[...refreshed.errors.keys()].join(", ")}; searching cached models.` });
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
catch (error) {
|
|
1312
|
+
publish({
|
|
1313
|
+
kind: "warning",
|
|
1314
|
+
message: timedOut
|
|
1315
|
+
? "Model refresh timed out; searching cached models."
|
|
1316
|
+
: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`,
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
finally {
|
|
1320
|
+
clearTimeout(timeout);
|
|
1321
|
+
}
|
|
1322
|
+
availableModels = [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
|
|
1323
|
+
model = findExactWorkflowModel(reference, availableModels);
|
|
1324
|
+
}
|
|
1325
|
+
if (!current())
|
|
1326
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
1327
|
+
if (model === undefined) {
|
|
1328
|
+
return {
|
|
1329
|
+
...workflowResult(request.command, "requires-selection", "Select a model", reference, "silent"),
|
|
1330
|
+
...(messages.length === 0 ? {} : { messages: Object.freeze(messages) }),
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
try {
|
|
1334
|
+
await session.setModel(model);
|
|
1335
|
+
}
|
|
1336
|
+
catch (error) {
|
|
1337
|
+
if (!current())
|
|
1338
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
1339
|
+
const failure = { kind: "error", message: error instanceof Error ? error.message : String(error) };
|
|
1340
|
+
return workflowResult(request.command, "failed", failure.message, undefined, "error", messages.length === 0 ? undefined : [...messages, failure]);
|
|
1341
|
+
}
|
|
1342
|
+
if (!current())
|
|
1343
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
1344
|
+
const providerId = stringProperty(model, "provider") ?? "unknown";
|
|
1345
|
+
const modelId = stringProperty(model, "id") ?? reference;
|
|
837
1346
|
this.#activeModel = { providerId, modelId, displayName: stringProperty(model, "name") ?? modelId };
|
|
838
1347
|
this.#emitView();
|
|
839
|
-
|
|
1348
|
+
const resultMessage = { kind: "status", message: `Model: ${modelId}` };
|
|
1349
|
+
return messages.length === 0
|
|
1350
|
+
? workflowResult(request.command, "completed", resultMessage.message)
|
|
1351
|
+
: workflowResult(request.command, "completed", resultMessage.message, undefined, "status", [...messages, resultMessage]);
|
|
840
1352
|
}
|
|
841
1353
|
case "scoped-models": {
|
|
842
1354
|
if (!selection)
|
|
@@ -863,27 +1375,64 @@ export class PiEngineAdapter {
|
|
|
863
1375
|
return workflowConfirmation(request.command, `Replace current session with ${path}?`);
|
|
864
1376
|
}
|
|
865
1377
|
if (!request.confirmed)
|
|
866
|
-
return workflowResult(request.command, "cancelled", "Import cancelled");
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1378
|
+
return workflowResult(request.command, "cancelled", "Import cancelled", undefined, "status");
|
|
1379
|
+
try {
|
|
1380
|
+
const result = await requireCapability(runtime.importFromJsonl, "importFromJsonl").call(runtime, path, request.cwdOverride);
|
|
1381
|
+
if (isRecord(result) && result.cancelled === true)
|
|
1382
|
+
return workflowResult(request.command, "cancelled", "Import cancelled", undefined, "status");
|
|
1383
|
+
return workflowResult(request.command, "completed", `Session imported from: ${path}`);
|
|
1384
|
+
}
|
|
1385
|
+
catch (error) {
|
|
1386
|
+
const issue = isRecord(error) && isRecord(error.issue) ? error.issue : undefined;
|
|
1387
|
+
const fallbackCwd = issue === undefined ? undefined : stringProperty(issue, "fallbackCwd");
|
|
1388
|
+
if (fallbackCwd !== undefined && request.cwdOverride === undefined) {
|
|
1389
|
+
const sessionCwd = stringProperty(issue, "sessionCwd") ?? "the session working directory";
|
|
1390
|
+
return workflowConfirmation(request.command, `cwd from session file does not exist\n${sessionCwd}\n\ncontinue in current cwd\n${fallbackCwd}`, fallbackCwd);
|
|
1391
|
+
}
|
|
1392
|
+
throw error;
|
|
1393
|
+
}
|
|
871
1394
|
}
|
|
872
1395
|
case "share": {
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1396
|
+
const signal = request.signal;
|
|
1397
|
+
if (signal?.aborted)
|
|
1398
|
+
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1399
|
+
try {
|
|
1400
|
+
await this.#workflowHost.runCommand("gh", ["auth", "status"], signal === undefined ? undefined : { signal });
|
|
1401
|
+
}
|
|
1402
|
+
catch (error) {
|
|
1403
|
+
if (signal?.aborted || isAbortError(error))
|
|
1404
|
+
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1405
|
+
const message = commandMissing(error)
|
|
1406
|
+
? "GitHub CLI (gh) is not installed. Install it from https://cli.github.com/"
|
|
1407
|
+
: "GitHub CLI is not logged in. Run 'gh auth login' first.";
|
|
1408
|
+
return workflowResult(request.command, "failed", message);
|
|
1409
|
+
}
|
|
878
1410
|
const temporary = join(tmpdir(), `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}pi-session-${process.pid}.html`);
|
|
879
1411
|
try {
|
|
880
|
-
|
|
881
|
-
|
|
1412
|
+
try {
|
|
1413
|
+
await requireCapability(session.exportToHtml, "exportToHtml").call(session, temporary);
|
|
1414
|
+
}
|
|
1415
|
+
catch (error) {
|
|
1416
|
+
return workflowResult(request.command, "failed", `Failed to export session: ${errorMessage(error, "Unknown error")}`);
|
|
1417
|
+
}
|
|
1418
|
+
if (signal?.aborted)
|
|
1419
|
+
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1420
|
+
let gist;
|
|
1421
|
+
try {
|
|
1422
|
+
gist = await this.#workflowHost.runCommand("gh", ["gist", "create", "--public=false", temporary], signal === undefined ? undefined : { signal });
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
if (signal?.aborted || isAbortError(error))
|
|
1426
|
+
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1427
|
+
return workflowResult(request.command, "failed", `Failed to create gist: ${shareCommandFailureDetail(error)}`);
|
|
1428
|
+
}
|
|
1429
|
+
if (signal?.aborted)
|
|
1430
|
+
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
882
1431
|
const gistUrl = gist.stdout.trim();
|
|
883
1432
|
const gistId = gistUrl.split("/").at(-1);
|
|
884
1433
|
if (!gistId)
|
|
885
|
-
|
|
886
|
-
return workflowResult(request.command, "completed", `Share URL:
|
|
1434
|
+
return workflowResult(request.command, "failed", "Failed to parse gist ID from gh output");
|
|
1435
|
+
return workflowResult(request.command, "completed", `Share URL: ${shareViewerUrl(gistId)}`, gistUrl);
|
|
887
1436
|
}
|
|
888
1437
|
finally {
|
|
889
1438
|
await rm(temporary, { force: true });
|
|
@@ -893,8 +1442,8 @@ export class PiEngineAdapter {
|
|
|
893
1442
|
const text = requireCapability(session.getLastAssistantText, "getLastAssistantText").call(session);
|
|
894
1443
|
if (typeof text !== "string" || text.length === 0)
|
|
895
1444
|
return workflowResult(request.command, "failed", "No agent messages to copy yet.");
|
|
896
|
-
await this
|
|
897
|
-
return workflowResult(request.command, "completed", "Copied last agent message to clipboard");
|
|
1445
|
+
const acknowledged = await this.copyWorkflowText(text);
|
|
1446
|
+
return workflowResult(request.command, "completed", acknowledged ? "Copied last agent message to clipboard" : "Submitted last agent message to clipboard");
|
|
898
1447
|
}
|
|
899
1448
|
case "name": {
|
|
900
1449
|
const manager = session.sessionManager;
|
|
@@ -902,7 +1451,7 @@ export class PiEngineAdapter {
|
|
|
902
1451
|
const current = manager?.getSessionName();
|
|
903
1452
|
return typeof current === "string"
|
|
904
1453
|
? workflowResult(request.command, "completed", `Session name: ${current}`)
|
|
905
|
-
: workflowResult(request.command, "failed", "Usage: /name <name>");
|
|
1454
|
+
: workflowResult(request.command, "failed", "Usage: /name <name>", undefined, "warning");
|
|
906
1455
|
}
|
|
907
1456
|
requireCapability(session.setSessionName, "setSessionName").call(session, argument);
|
|
908
1457
|
const normalized = manager?.getSessionName();
|
|
@@ -929,17 +1478,17 @@ export class PiEngineAdapter {
|
|
|
929
1478
|
return workflowResult(request.command, "failed", "Fork requires the owned user-message controller");
|
|
930
1479
|
const result = await requireCapability(runtime.fork, "fork").call(runtime, selection, { position: "before" });
|
|
931
1480
|
if (isRecord(result) && result.cancelled === true)
|
|
932
|
-
return workflowResult(request.command, "cancelled", "Fork cancelled");
|
|
1481
|
+
return workflowResult(request.command, "cancelled", "Fork cancelled", undefined, "silent");
|
|
933
1482
|
return workflowResult(request.command, "completed", "Forked to new session");
|
|
934
1483
|
}
|
|
935
1484
|
case "clone": {
|
|
936
1485
|
const manager = session.sessionManager;
|
|
937
1486
|
const leaf = manager?.getLeafId?.();
|
|
938
1487
|
if (typeof leaf !== "string")
|
|
939
|
-
return workflowResult(request.command, "
|
|
1488
|
+
return workflowResult(request.command, "completed", "Nothing to clone yet", undefined, "status");
|
|
940
1489
|
const result = await requireCapability(runtime.fork, "fork").call(runtime, leaf, { position: "at" });
|
|
941
1490
|
if (isRecord(result) && result.cancelled === true)
|
|
942
|
-
return workflowResult(request.command, "cancelled", "Clone cancelled");
|
|
1491
|
+
return workflowResult(request.command, "cancelled", "Clone cancelled", undefined, "silent");
|
|
943
1492
|
return workflowResult(request.command, "completed", "Cloned to new session");
|
|
944
1493
|
}
|
|
945
1494
|
case "tree": {
|
|
@@ -953,9 +1502,9 @@ export class PiEngineAdapter {
|
|
|
953
1502
|
...(request.treeSummary.customInstructions === undefined ? {} : { customInstructions: request.treeSummary.customInstructions }),
|
|
954
1503
|
});
|
|
955
1504
|
if (isRecord(result) && result.aborted === true)
|
|
956
|
-
return workflowResult(request.command, "cancelled", "Branch summarization cancelled");
|
|
1505
|
+
return workflowResult(request.command, "cancelled", "Branch summarization cancelled", undefined, "status");
|
|
957
1506
|
if (isRecord(result) && result.cancelled === true)
|
|
958
|
-
return workflowResult(request.command, "cancelled", "Navigation cancelled");
|
|
1507
|
+
return workflowResult(request.command, "cancelled", "Navigation cancelled", undefined, "status");
|
|
959
1508
|
return workflowResult(request.command, "completed", "Navigated to selected point");
|
|
960
1509
|
}
|
|
961
1510
|
case "trust": {
|
|
@@ -990,6 +1539,7 @@ export class PiEngineAdapter {
|
|
|
990
1539
|
const authType = authTypeValue === "api_key" ? "api_key" : "oauth";
|
|
991
1540
|
const provider = modelRuntime.getProvider?.(providerId);
|
|
992
1541
|
const providerName = stringProperty(provider, "name") ?? providerId;
|
|
1542
|
+
const previousModel = session.model;
|
|
993
1543
|
this.#workflowInteraction.startLogin?.({ providerId, providerName, authType });
|
|
994
1544
|
try {
|
|
995
1545
|
await requireCapability(modelRuntime.login, "login").call(modelRuntime, providerId, authType, {
|
|
@@ -1027,25 +1577,40 @@ export class PiEngineAdapter {
|
|
|
1027
1577
|
});
|
|
1028
1578
|
}
|
|
1029
1579
|
catch (error) {
|
|
1030
|
-
if (error instanceof Error && error.message === "Login cancelled")
|
|
1031
|
-
return workflowResult(request.command, "cancelled", "Login cancelled");
|
|
1032
|
-
|
|
1580
|
+
if (error instanceof Error && error.message === "Login cancelled") {
|
|
1581
|
+
return workflowResult(request.command, "cancelled", "Login cancelled", undefined, "silent");
|
|
1582
|
+
}
|
|
1583
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1584
|
+
const actionLabel = authType === "api_key" ? `Saved API key for ${providerName}` : `Logged in to ${providerName}`;
|
|
1585
|
+
const message = error instanceof CredentialSynchronizationError
|
|
1586
|
+
? `${actionLabel}, but local model state could not be synchronized: ${detail}`
|
|
1587
|
+
: authType === "api_key"
|
|
1588
|
+
? `Failed to save API key for ${providerName}: ${detail}`
|
|
1589
|
+
: `Failed to login to ${providerName}: ${detail}`;
|
|
1590
|
+
return workflowResult(request.command, "failed", message);
|
|
1033
1591
|
}
|
|
1034
1592
|
finally {
|
|
1035
1593
|
this.#workflowInteraction.finishLogin?.();
|
|
1036
1594
|
}
|
|
1037
|
-
this.#
|
|
1038
|
-
this.#emitView();
|
|
1039
|
-
return workflowResult(request.command, "completed", `Logged in to ${providerName}. Credentials saved to ${join(this.#agentDir, "auth.json")}`);
|
|
1595
|
+
return await this.#completeProviderAuthentication(providerId, providerName, authType, previousModel);
|
|
1040
1596
|
}
|
|
1041
1597
|
case "logout": {
|
|
1042
1598
|
if (!selection)
|
|
1043
1599
|
return workflowResult(request.command, "failed", "Logout requires the owned authentication controller");
|
|
1044
1600
|
const [credentialType = "oauth", providerId = selection] = selection.includes(":") ? selection.split(":", 2) : ["oauth", selection];
|
|
1045
1601
|
const modelRuntime = runtime.services.modelRuntime;
|
|
1046
|
-
await requireCapability(modelRuntime.logout, "logout").call(modelRuntime, providerId, { signal: AbortSignal.timeout(15_000) });
|
|
1047
1602
|
const provider = modelRuntime.getProvider?.(providerId);
|
|
1048
1603
|
const providerName = stringProperty(provider, "name") ?? providerId;
|
|
1604
|
+
try {
|
|
1605
|
+
await requireCapability(modelRuntime.logout, "logout").call(modelRuntime, providerId, { signal: AbortSignal.timeout(15_000) });
|
|
1606
|
+
}
|
|
1607
|
+
catch (error) {
|
|
1608
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1609
|
+
const message = error instanceof CredentialSynchronizationError
|
|
1610
|
+
? `Credentials removed for ${providerName}, but local model state could not be synchronized: ${detail}`
|
|
1611
|
+
: `Logout failed: ${detail}`;
|
|
1612
|
+
return workflowResult(request.command, "failed", message);
|
|
1613
|
+
}
|
|
1049
1614
|
this.#reconcileActiveModelAvailability();
|
|
1050
1615
|
this.#emitView();
|
|
1051
1616
|
return workflowResult(request.command, "completed", credentialType === "api_key"
|
|
@@ -1055,12 +1620,17 @@ export class PiEngineAdapter {
|
|
|
1055
1620
|
case "new": {
|
|
1056
1621
|
const result = await runtime.newSession();
|
|
1057
1622
|
if (isRecord(result) && result.cancelled === true)
|
|
1058
|
-
return workflowResult(request.command, "cancelled", "New session cancelled");
|
|
1059
|
-
return workflowResult(request.command, "completed", "✓ New session started");
|
|
1623
|
+
return workflowResult(request.command, "cancelled", "New session cancelled", undefined, "silent");
|
|
1624
|
+
return workflowResult(request.command, "completed", "✓ New session started", undefined, "accent");
|
|
1060
1625
|
}
|
|
1061
1626
|
case "compact": {
|
|
1062
|
-
|
|
1063
|
-
|
|
1627
|
+
try {
|
|
1628
|
+
await session.compact(argument || undefined);
|
|
1629
|
+
return workflowResult(request.command, "completed", "Compaction requested", undefined, "silent");
|
|
1630
|
+
}
|
|
1631
|
+
catch (error) {
|
|
1632
|
+
return workflowResult(request.command, "failed", error instanceof Error ? error.message : String(error), undefined, "silent");
|
|
1633
|
+
}
|
|
1064
1634
|
}
|
|
1065
1635
|
case "resume": {
|
|
1066
1636
|
if (!selection && !argument)
|
|
@@ -1069,7 +1639,7 @@ export class PiEngineAdapter {
|
|
|
1069
1639
|
try {
|
|
1070
1640
|
const result = await runtime.switchSession(sessionPath);
|
|
1071
1641
|
if (isRecord(result) && result.cancelled === true)
|
|
1072
|
-
return workflowResult(request.command, "cancelled", "Resume cancelled");
|
|
1642
|
+
return workflowResult(request.command, "cancelled", "Resume cancelled", undefined, "silent");
|
|
1073
1643
|
return workflowResult(request.command, "completed", "Resumed session");
|
|
1074
1644
|
}
|
|
1075
1645
|
catch (error) {
|
|
@@ -1082,21 +1652,25 @@ export class PiEngineAdapter {
|
|
|
1082
1652
|
return workflowConfirmation(request.command, `cwd from session file does not exist\n${sessionCwd}\n\ncontinue in current cwd\n${fallbackCwd}`);
|
|
1083
1653
|
}
|
|
1084
1654
|
if (!request.confirmed)
|
|
1085
|
-
return workflowResult(request.command, "cancelled", "Resume cancelled");
|
|
1655
|
+
return workflowResult(request.command, "cancelled", "Resume cancelled", undefined, "status");
|
|
1086
1656
|
const result = await runtime.switchSession(sessionPath, { cwdOverride: fallbackCwd });
|
|
1087
1657
|
if (isRecord(result) && result.cancelled === true)
|
|
1088
|
-
return workflowResult(request.command, "cancelled", "Resume cancelled");
|
|
1658
|
+
return workflowResult(request.command, "cancelled", "Resume cancelled", undefined, "silent");
|
|
1089
1659
|
return workflowResult(request.command, "completed", "Resumed session in current cwd");
|
|
1090
1660
|
}
|
|
1091
1661
|
}
|
|
1092
1662
|
case "reload": {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
return workflowResult(request.command, "failed", "Wait for compaction to finish before reloading.");
|
|
1663
|
+
const blocked = this.reloadBlockedResult();
|
|
1664
|
+
if (blocked)
|
|
1665
|
+
return blocked;
|
|
1097
1666
|
await requireCapability(session.reload, "reload").call(session);
|
|
1098
1667
|
await this.#bindExtensionUiToSession();
|
|
1099
|
-
|
|
1668
|
+
const message = "Reloaded keybindings, extensions, skills, prompts, themes, and context files";
|
|
1669
|
+
const modelError = runtime.services.modelRuntime.getError?.();
|
|
1670
|
+
return workflowResult(request.command, "completed", message, undefined, "status", modelError ? [
|
|
1671
|
+
{ kind: "error", message: `models.json error: ${modelError}` },
|
|
1672
|
+
{ kind: "status", message },
|
|
1673
|
+
] : undefined);
|
|
1100
1674
|
}
|
|
1101
1675
|
case "quit": {
|
|
1102
1676
|
await this.dispose();
|
|
@@ -1114,6 +1688,91 @@ export class PiEngineAdapter {
|
|
|
1114
1688
|
return workflowResult(request.command, "completed", "Demented elves announcement");
|
|
1115
1689
|
}
|
|
1116
1690
|
}
|
|
1691
|
+
async #completeProviderAuthentication(providerId, providerName, authType, previousModel) {
|
|
1692
|
+
const session = this.#requireWorkflowSession();
|
|
1693
|
+
const modelRuntime = this.#runtime?.services.modelRuntime;
|
|
1694
|
+
if (!modelRuntime)
|
|
1695
|
+
throw new Error("engine runtime is unavailable");
|
|
1696
|
+
const actionLabel = authType === "oauth" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;
|
|
1697
|
+
let selectedModelId;
|
|
1698
|
+
let selectionError;
|
|
1699
|
+
if (isUnknownModel(previousModel)) {
|
|
1700
|
+
const available = modelRuntime.getAvailableSnapshot?.() ?? [];
|
|
1701
|
+
const providerModels = available.filter(model => stringProperty(model, "provider") === providerId);
|
|
1702
|
+
const defaultModelId = PINNED_DEFAULT_MODEL_BY_PROVIDER[providerId];
|
|
1703
|
+
if (defaultModelId === undefined) {
|
|
1704
|
+
selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
|
|
1705
|
+
}
|
|
1706
|
+
else if (providerModels.length === 0) {
|
|
1707
|
+
selectionError = `${actionLabel}, but no models are available for that provider. Use /model to select a model.`;
|
|
1708
|
+
}
|
|
1709
|
+
else {
|
|
1710
|
+
const selectedModel = providerModels.find(model => stringProperty(model, "id") === defaultModelId);
|
|
1711
|
+
if (selectedModel === undefined) {
|
|
1712
|
+
selectionError = `${actionLabel}, but its default model "${defaultModelId}" is not available. Use /model to select a model.`;
|
|
1713
|
+
}
|
|
1714
|
+
else {
|
|
1715
|
+
try {
|
|
1716
|
+
await session.setModel(selectedModel);
|
|
1717
|
+
selectedModelId = stringProperty(selectedModel, "id") ?? defaultModelId;
|
|
1718
|
+
this.#activeModel = {
|
|
1719
|
+
providerId,
|
|
1720
|
+
modelId: selectedModelId,
|
|
1721
|
+
displayName: stringProperty(selectedModel, "name") ?? selectedModelId,
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
catch (error) {
|
|
1725
|
+
selectionError = `${actionLabel}, but selecting its default model failed: ${error instanceof Error ? error.message : String(error)}. Use /model to select a model.`;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
this.#reconcileActiveModelAvailability();
|
|
1731
|
+
this.#emitView();
|
|
1732
|
+
const status = `${actionLabel}.${selectedModelId ? ` Selected ${selectedModelId}.` : ""} Credentials saved to ${join(this.#agentDir, "auth.json")}`;
|
|
1733
|
+
const messages = [
|
|
1734
|
+
{ kind: "status", message: status },
|
|
1735
|
+
...(selectionError === undefined ? [] : [{ kind: "error", message: selectionError }]),
|
|
1736
|
+
];
|
|
1737
|
+
this.#scheduleAuthenticatedProviderRefresh(providerId, actionLabel);
|
|
1738
|
+
return workflowResult("login", "completed", status, undefined, "status", messages);
|
|
1739
|
+
}
|
|
1740
|
+
#scheduleAuthenticatedProviderRefresh(providerId, actionLabel) {
|
|
1741
|
+
const runtime = this.#runtime;
|
|
1742
|
+
const refresh = runtime?.services.modelRuntime.refresh;
|
|
1743
|
+
if (!runtime || typeof refresh !== "function")
|
|
1744
|
+
return;
|
|
1745
|
+
const generation = this.#sessionGeneration;
|
|
1746
|
+
const publish = this.#workflowInteraction.publish;
|
|
1747
|
+
const controller = new AbortController();
|
|
1748
|
+
const timeout = setTimeout(() => controller.abort(), AUTH_REFRESH_TIMEOUT_MS);
|
|
1749
|
+
// Compatibility: post-authentication refresh starts on the next turn so its notices follow the saved-credential status.
|
|
1750
|
+
void new Promise(resolve => setTimeout(resolve, 0))
|
|
1751
|
+
.then(() => refresh.call(runtime.services.modelRuntime, { providers: [providerId], signal: controller.signal }))
|
|
1752
|
+
.then(result => {
|
|
1753
|
+
if (this.#disposed || this.#sessionGeneration !== generation)
|
|
1754
|
+
return;
|
|
1755
|
+
if (isRecord(result) && result.aborted === true) {
|
|
1756
|
+
publish?.({ kind: "warning", message: `${actionLabel}, but its model catalog refresh timed out; using cached models.` });
|
|
1757
|
+
}
|
|
1758
|
+
else if (isRecord(result) && result.errors instanceof Map && result.errors.size > 0) {
|
|
1759
|
+
publish?.({ kind: "warning", message: `${actionLabel}, but its model catalog could not be refreshed; using cached models.` });
|
|
1760
|
+
}
|
|
1761
|
+
this.#reconcileActiveModelAvailability();
|
|
1762
|
+
this.#emitView();
|
|
1763
|
+
})
|
|
1764
|
+
.catch(error => {
|
|
1765
|
+
if (this.#disposed || this.#sessionGeneration !== generation)
|
|
1766
|
+
return;
|
|
1767
|
+
publish?.({
|
|
1768
|
+
kind: "warning",
|
|
1769
|
+
message: controller.signal.aborted
|
|
1770
|
+
? `${actionLabel}, but its model catalog refresh timed out; using cached models.`
|
|
1771
|
+
: `${actionLabel}, but its model catalog could not be refreshed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1772
|
+
});
|
|
1773
|
+
})
|
|
1774
|
+
.finally(() => clearTimeout(timeout));
|
|
1775
|
+
}
|
|
1117
1776
|
#requireWorkflowSession() {
|
|
1118
1777
|
if (this.#disposed || !this.#runtime || !this.#session)
|
|
1119
1778
|
throw new Error("engine adapter is not running");
|
|
@@ -1211,7 +1870,7 @@ export class PiEngineAdapter {
|
|
|
1211
1870
|
return [];
|
|
1212
1871
|
return sessionInfoOptions(await SessionManager.list(cwd, typeof sessionDir === "string" ? sessionDir : undefined));
|
|
1213
1872
|
}
|
|
1214
|
-
#applyPinnedSetting(selection, selectedValue, hasSelectedValue = false) {
|
|
1873
|
+
async #applyPinnedSetting(selection, selectedValue, hasSelectedValue = false) {
|
|
1215
1874
|
if (!PINNED_PI_SETTINGS_CALLBACKS.includes(selection)) {
|
|
1216
1875
|
return workflowResult("settings", "failed", `Unknown setting callback: ${selection}`);
|
|
1217
1876
|
}
|
|
@@ -1258,33 +1917,24 @@ export class PiEngineAdapter {
|
|
|
1258
1917
|
selectedValue = currentValues[callback];
|
|
1259
1918
|
hasSelectedValue = true;
|
|
1260
1919
|
}
|
|
1261
|
-
const
|
|
1262
|
-
if (
|
|
1920
|
+
const port = this.settingsPort();
|
|
1921
|
+
if (port === null)
|
|
1263
1922
|
return workflowResult("settings", "failed", "Settings are unavailable");
|
|
1264
|
-
const
|
|
1265
|
-
if (
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
else {
|
|
1271
|
-
const key = settingKeyForCallback(callback);
|
|
1272
|
-
if (key === null)
|
|
1273
|
-
return workflowResult("settings", "failed", `${settingLabel(callback)} is unavailable in this runtime`);
|
|
1274
|
-
const value = agentJsonValue(selectedValue);
|
|
1275
|
-
if (callback === "onAutoCompactChange") {
|
|
1276
|
-
if (typeof value !== "boolean")
|
|
1277
|
-
return workflowResult("settings", "failed", "Auto compact value is invalid");
|
|
1278
|
-
session.setAutoCompactionEnabled?.(value);
|
|
1279
|
-
}
|
|
1280
|
-
new PiSettingsIntegration(settings).writeSettingNow(key, value);
|
|
1923
|
+
const key = settingKeyForCallback(callback);
|
|
1924
|
+
if (key === null)
|
|
1925
|
+
return workflowResult("settings", "failed", `${settingLabel(callback)} is unavailable in this runtime`);
|
|
1926
|
+
const result = await port.writeSetting(key, agentJsonValue(selectedValue));
|
|
1927
|
+
if (result.status === "failed" || result.status === "unavailable") {
|
|
1928
|
+
return workflowResult("settings", "failed", result.failure ?? result.limitationReason ?? `${settingLabel(callback)} is unavailable`);
|
|
1281
1929
|
}
|
|
1282
1930
|
this.#emitView();
|
|
1283
|
-
|
|
1931
|
+
const suffix = result.status === "deferred" ? ` (${result.application})` : "";
|
|
1932
|
+
return workflowResult("settings", "completed", `${settingLabel(callback)}: ${String(selectedValue)}${suffix}`);
|
|
1284
1933
|
}
|
|
1285
1934
|
async #perform(command) {
|
|
1286
1935
|
const runtime = this.#runtime;
|
|
1287
1936
|
const session = this.#session;
|
|
1937
|
+
const generation = this.#sessionGeneration;
|
|
1288
1938
|
if (!runtime || !session)
|
|
1289
1939
|
throw new Error("engine session is unavailable");
|
|
1290
1940
|
switch (command.type) {
|
|
@@ -1295,7 +1945,11 @@ export class PiEngineAdapter {
|
|
|
1295
1945
|
case "retry":
|
|
1296
1946
|
case "compact": {
|
|
1297
1947
|
const result = await this.#sessionCommands?.execute(command.type === "prompt" || command.type === "steer" || command.type === "follow-up"
|
|
1298
|
-
? {
|
|
1948
|
+
? {
|
|
1949
|
+
type: command.type,
|
|
1950
|
+
text: command.text,
|
|
1951
|
+
...(command.images === undefined ? {} : { images: [...command.images] }),
|
|
1952
|
+
}
|
|
1299
1953
|
: { type: command.type });
|
|
1300
1954
|
if (!result || result.outcome === "rejected" || result.outcome === "failed") {
|
|
1301
1955
|
throw new Error(command.type === "retry" ? "no previous prompt is available to retry" : `Pi session command failed: ${command.type}`);
|
|
@@ -1308,7 +1962,8 @@ export class PiEngineAdapter {
|
|
|
1308
1962
|
throw new Error(`model is unavailable: ${command.model.providerId}/${command.model.modelId}`);
|
|
1309
1963
|
}
|
|
1310
1964
|
await session.setModel(model);
|
|
1311
|
-
this.#
|
|
1965
|
+
if (generation === this.#sessionGeneration && this.#overload === undefined && !this.#admissionStopped)
|
|
1966
|
+
this.#activeModel = { ...command.model };
|
|
1312
1967
|
return;
|
|
1313
1968
|
}
|
|
1314
1969
|
case "set-thinking-level":
|
|
@@ -1336,7 +1991,13 @@ export class PiEngineAdapter {
|
|
|
1336
1991
|
}
|
|
1337
1992
|
#bindSession(session) {
|
|
1338
1993
|
this.#unsubscribe?.();
|
|
1994
|
+
for (const pending of this.#pendingCommands.values()) {
|
|
1995
|
+
if (pending.type !== "new-session" && pending.type !== "resume-session")
|
|
1996
|
+
pending.cancel();
|
|
1997
|
+
}
|
|
1339
1998
|
this.#sessionGeneration += 1;
|
|
1999
|
+
this.#sessionBindingGeneration += 1;
|
|
2000
|
+
this.#invalidatedEvents += this.#eventQueue.discardObsolete(this.#sessionGeneration);
|
|
1340
2001
|
this.#session = session;
|
|
1341
2002
|
this.#activeCommandIds = [];
|
|
1342
2003
|
this.#completedCommands.clear();
|
|
@@ -1350,11 +2011,22 @@ export class PiEngineAdapter {
|
|
|
1350
2011
|
submitEnabled: true,
|
|
1351
2012
|
};
|
|
1352
2013
|
this.#status = { ...this.#status, workingMessage: null, badges: [] };
|
|
2014
|
+
this.#statusKind = null;
|
|
2015
|
+
this.#agentRunActive = false;
|
|
2016
|
+
this.#agentRunSequence = 0;
|
|
2017
|
+
this.#assistantResponseSequence = 0;
|
|
1353
2018
|
this.#activeModel = readModel(session.model);
|
|
1354
2019
|
this.#reconcileActiveModelAvailability();
|
|
1355
2020
|
this.#thinkingLevel = readThinkingLevel(session.thinkingLevel);
|
|
2021
|
+
this.#transcriptImageAssets.clear();
|
|
2022
|
+
// Invariant: a new binding cannot inherit arguments/results from a reused invocation id.
|
|
2023
|
+
this.#setTranscript([]);
|
|
1356
2024
|
this.#rebuildTranscript(session.messages, "finalized");
|
|
1357
|
-
|
|
2025
|
+
const generation = this.#sessionGeneration;
|
|
2026
|
+
this.#unsubscribe = session.subscribe(event => {
|
|
2027
|
+
if (generation === this.#sessionGeneration && !this.#disposed)
|
|
2028
|
+
this.#handlePiEvent(event);
|
|
2029
|
+
});
|
|
1358
2030
|
if (this.#extensionUi !== undefined)
|
|
1359
2031
|
void this.#bindExtensionUiToSession();
|
|
1360
2032
|
}
|
|
@@ -1402,33 +2074,122 @@ export class PiEngineAdapter {
|
|
|
1402
2074
|
this.#emitView();
|
|
1403
2075
|
}
|
|
1404
2076
|
}
|
|
2077
|
+
// Performance: replacing the transcript rebuilds its index so block lookup stays constant-time.
|
|
2078
|
+
#setTranscript(blocks) {
|
|
2079
|
+
// Invariant: lazy delivery snapshots must freeze before their authoritative source can be removed.
|
|
2080
|
+
if (!this.#eventQueue.seal()) {
|
|
2081
|
+
this.#beginOverload();
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
for (const block of blocks)
|
|
2085
|
+
this.#transcriptImageAssets.retain(block);
|
|
2086
|
+
for (const block of this.#transcript)
|
|
2087
|
+
this.#transcriptImageAssets.release(block);
|
|
2088
|
+
this.#transcript = blocks;
|
|
2089
|
+
this.#transcriptIndex.clear();
|
|
2090
|
+
for (const [index, block] of blocks.entries())
|
|
2091
|
+
this.#transcriptIndex.set(block.id, index);
|
|
2092
|
+
this.#transcriptImageAssets.discardUnowned();
|
|
2093
|
+
this.#transcriptSnapshot = undefined;
|
|
2094
|
+
}
|
|
2095
|
+
#transcriptBlock(id) {
|
|
2096
|
+
const index = this.#transcriptIndex.get(id);
|
|
2097
|
+
return index === undefined ? undefined : this.#transcript[index];
|
|
2098
|
+
}
|
|
2099
|
+
// Invariant: the named work state is the only state a matching end may clear.
|
|
2100
|
+
#enterWorkState(kind, message) {
|
|
2101
|
+
const wasBusy = this.#lifecycle === "busy";
|
|
2102
|
+
this.#statusKind = kind;
|
|
2103
|
+
this.#lifecycle = "busy";
|
|
2104
|
+
this.#status = { ...this.#status, workingMessage: message };
|
|
2105
|
+
if (!wasBusy)
|
|
2106
|
+
this.#emitEvent({ type: "session-lifecycle", lifecycle: "busy", reason: null });
|
|
2107
|
+
this.#emitEvent({ type: "status", status: this.#status });
|
|
2108
|
+
}
|
|
2109
|
+
// Invariant: ending retry or compaction cannot clear a different active work state.
|
|
2110
|
+
#endWorkState(kind) {
|
|
2111
|
+
if (this.#statusKind !== kind)
|
|
2112
|
+
return;
|
|
2113
|
+
if (this.#agentRunActive) {
|
|
2114
|
+
this.#enterWorkState("working", "Working");
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
this.#leaveWorkStates();
|
|
2118
|
+
}
|
|
2119
|
+
#leaveWorkStates() {
|
|
2120
|
+
this.#statusKind = null;
|
|
2121
|
+
this.#lifecycle = "ready";
|
|
2122
|
+
this.#status = { ...this.#status, workingMessage: null };
|
|
2123
|
+
this.#emitEvent({ type: "session-lifecycle", lifecycle: "ready", reason: null });
|
|
2124
|
+
this.#emitEvent({ type: "status", status: this.#status });
|
|
2125
|
+
}
|
|
1405
2126
|
#handlePiEvent(event) {
|
|
2127
|
+
try {
|
|
2128
|
+
this.#applyPiEvent(event);
|
|
2129
|
+
}
|
|
2130
|
+
finally {
|
|
2131
|
+
this.#transcriptImageAssets.discardUnowned();
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
#applyPiEvent(event) {
|
|
1406
2135
|
if (!isRecord(event) || typeof event.type !== "string")
|
|
1407
2136
|
return;
|
|
2137
|
+
// Invariant: usage moves at message and lifecycle boundaries, not with stream chunks, so the
|
|
2138
|
+
// two streaming event kinds keep the memo and everything else drops it.
|
|
2139
|
+
if (event.type !== "message_update" && event.type !== "tool_execution_update")
|
|
2140
|
+
this.#usageCache = undefined;
|
|
1408
2141
|
switch (event.type) {
|
|
1409
2142
|
case "agent_start":
|
|
1410
|
-
this.#
|
|
1411
|
-
this.#
|
|
1412
|
-
this.#emitEvent({ type: "
|
|
1413
|
-
this.#
|
|
2143
|
+
this.#agentRunActive = true;
|
|
2144
|
+
this.#agentRunSequence += 1;
|
|
2145
|
+
this.#emitEvent({ type: "agent-run-started" });
|
|
2146
|
+
this.#enterWorkState("working", "Working");
|
|
1414
2147
|
return;
|
|
1415
2148
|
case "message_start":
|
|
1416
2149
|
this.#upsertMessageBlock(event.message, "live");
|
|
1417
2150
|
return;
|
|
1418
2151
|
case "message_update": {
|
|
1419
|
-
const
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
2152
|
+
const delta = isRecord(event.assistantMessageEvent) && typeof event.assistantMessageEvent.delta === "string"
|
|
2153
|
+
? event.assistantMessageEvent.delta
|
|
2154
|
+
: undefined;
|
|
2155
|
+
// Invariant: the delta is folded in before the block is stored, so a chunk is one update to
|
|
2156
|
+
// one block rather than a store without the delta followed by a store with it.
|
|
2157
|
+
const blocks = this.#messageBlocks(event.message, "live", this.#transcript.length);
|
|
2158
|
+
for (const [index, block] of blocks.entries()) {
|
|
2159
|
+
this.#upsertTranscriptBlock(index === 0 && delta !== undefined && !block.text.endsWith(delta)
|
|
2160
|
+
? { ...block, text: `${block.text}${delta}` }
|
|
2161
|
+
: block);
|
|
1427
2162
|
}
|
|
1428
2163
|
return;
|
|
1429
2164
|
}
|
|
1430
2165
|
case "message_end":
|
|
1431
2166
|
this.#upsertMessageBlock(event.message, "finalized");
|
|
2167
|
+
this.#settleFailedDeclarations(event.message);
|
|
2168
|
+
// Compatibility: preserve the same semantic boundary v2 counted. Transcript block
|
|
2169
|
+
// finalization is intentionally not a substitute: rebuilds, retries,
|
|
2170
|
+
// thinking parts, and tool rows can all finalize independently.
|
|
2171
|
+
if (isRecord(event.message) && event.message.role === "assistant") {
|
|
2172
|
+
this.#assistantResponseSequence += 1;
|
|
2173
|
+
const content = Array.isArray(event.message.content) ? event.message.content : [];
|
|
2174
|
+
const stopReason = stringValue(event.message.stopReason) ?? null;
|
|
2175
|
+
const toolContinuation = stopReason === "toolUse"
|
|
2176
|
+
|| content.some(item => isRecord(item) && item.type === "toolCall");
|
|
2177
|
+
const successful = stringValue(event.message.errorMessage) === undefined
|
|
2178
|
+
&& stopReason !== "error"
|
|
2179
|
+
&& stopReason !== "aborted"
|
|
2180
|
+
&& textFromContent(content).trim().length > 0;
|
|
2181
|
+
this.#emitEvent({
|
|
2182
|
+
type: "assistant-message-completed",
|
|
2183
|
+
sessionGeneration: this.#sessionGeneration,
|
|
2184
|
+
runSequence: this.#agentRunSequence,
|
|
2185
|
+
responseSequence: this.#assistantResponseSequence,
|
|
2186
|
+
model: this.#activeModel,
|
|
2187
|
+
assistantMessageCount: this.#transcript.filter(block => block.kind === "assistant").length,
|
|
2188
|
+
successful,
|
|
2189
|
+
stopReason,
|
|
2190
|
+
toolContinuation,
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
1432
2193
|
return;
|
|
1433
2194
|
case "turn_end":
|
|
1434
2195
|
this.#upsertMessageBlock(event.message, "finalized");
|
|
@@ -1438,26 +2199,58 @@ export class PiEngineAdapter {
|
|
|
1438
2199
|
}
|
|
1439
2200
|
return;
|
|
1440
2201
|
case "tool_execution_start":
|
|
2202
|
+
case "tool_execution_end": {
|
|
2203
|
+
this.#upsertToolExecutionBlock(event);
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
1441
2206
|
case "tool_execution_update":
|
|
1442
|
-
case "tool_execution_end":
|
|
1443
2207
|
this.#upsertToolExecutionBlock(event);
|
|
1444
2208
|
return;
|
|
1445
2209
|
case "agent_settled":
|
|
1446
2210
|
case "agent_end": {
|
|
1447
2211
|
if (event.type === "agent_end" && event.willRetry === true)
|
|
1448
2212
|
return;
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
2213
|
+
// Protocol: agent_end is run-local; only settlement reads the complete session scope.
|
|
2214
|
+
const finalMessages = event.type === "agent_settled"
|
|
2215
|
+
? this.#session?.messages ?? []
|
|
2216
|
+
: Array.isArray(event.messages) ? event.messages : [];
|
|
2217
|
+
if (event.type === "agent_end")
|
|
2218
|
+
this.#mergeRunTranscript(finalMessages);
|
|
2219
|
+
else if (finalMessages.length > 0)
|
|
1453
2220
|
this.#rebuildTranscript(finalMessages, "finalized");
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
2221
|
+
// Invariant: missing final messages must not erase accumulated content or invent tool outcomes.
|
|
2222
|
+
if (finalMessages.length === 0)
|
|
2223
|
+
this.#setTranscript(this.#transcript.map(block => block.status === "live" && transcriptToolState(block) === undefined
|
|
2224
|
+
? { ...block, status: "finalized", revision: block.revision + 1 } : block));
|
|
2225
|
+
// Compatibility: ending a turn leaves the working state, as the recorded pinned baseline does, but
|
|
2226
|
+
// it leaves only that state: a compaction or retry being shown outlives the turn
|
|
2227
|
+
// that ended under it. Settlement ends the run, and with it every state — the
|
|
2228
|
+
// engine ends a turn for each continuation it makes and settles once.
|
|
2229
|
+
if (event.type === "agent_settled") {
|
|
2230
|
+
this.#agentRunActive = false;
|
|
2231
|
+
this.#leaveWorkStates();
|
|
2232
|
+
}
|
|
2233
|
+
else if (this.#statusKind === null || this.#statusKind === "working") {
|
|
2234
|
+
this.#leaveWorkStates();
|
|
2235
|
+
}
|
|
1460
2236
|
this.#emitView();
|
|
2237
|
+
if (event.type === "agent_settled") {
|
|
2238
|
+
const assistants = finalMessages.filter(message => isRecord(message) && message.role === "assistant");
|
|
2239
|
+
const lastAssistant = assistants.at(-1);
|
|
2240
|
+
const successful = lastAssistant !== undefined
|
|
2241
|
+
&& stringValue(lastAssistant.errorMessage) === undefined
|
|
2242
|
+
&& stringValue(lastAssistant.stopReason) !== "error"
|
|
2243
|
+
&& stringValue(lastAssistant.stopReason) !== "aborted";
|
|
2244
|
+
this.#emitEvent({
|
|
2245
|
+
type: "agent-run-settled",
|
|
2246
|
+
sessionGeneration: this.#sessionGeneration,
|
|
2247
|
+
runSequence: this.#agentRunSequence,
|
|
2248
|
+
responseSequence: this.#assistantResponseSequence,
|
|
2249
|
+
model: this.#activeModel,
|
|
2250
|
+
assistantMessageCount: assistants.length,
|
|
2251
|
+
successful,
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
1461
2254
|
return;
|
|
1462
2255
|
}
|
|
1463
2256
|
case "queue_update": {
|
|
@@ -1472,20 +2265,16 @@ export class PiEngineAdapter {
|
|
|
1472
2265
|
return;
|
|
1473
2266
|
}
|
|
1474
2267
|
case "auto_retry_start":
|
|
1475
|
-
this.#
|
|
1476
|
-
this.#status = { ...this.#status, workingMessage: "Retrying…" };
|
|
1477
|
-
this.#emitEvent({ type: "status", status: this.#status });
|
|
2268
|
+
this.#enterWorkState("retry", "Retrying");
|
|
1478
2269
|
return;
|
|
1479
2270
|
case "auto_retry_end":
|
|
1480
|
-
|
|
1481
|
-
this.#lifecycle = "ready";
|
|
1482
|
-
this.#status = { ...this.#status, workingMessage: null };
|
|
1483
|
-
this.#emitEvent({ type: "status", status: this.#status });
|
|
2271
|
+
this.#endWorkState("retry");
|
|
1484
2272
|
return;
|
|
1485
2273
|
case "compaction_start":
|
|
1486
|
-
this.#
|
|
1487
|
-
|
|
1488
|
-
|
|
2274
|
+
this.#enterWorkState("compaction", "Compacting");
|
|
2275
|
+
return;
|
|
2276
|
+
case "compaction_end":
|
|
2277
|
+
this.#endWorkState("compaction");
|
|
1489
2278
|
return;
|
|
1490
2279
|
case "thinking_level_changed":
|
|
1491
2280
|
this.#thinkingLevel = readThinkingLevel(event.level);
|
|
@@ -1548,6 +2337,29 @@ export class PiEngineAdapter {
|
|
|
1548
2337
|
autoCompactEnabled: this.#runtime?.services.settingsManager?.getCompactionEnabled?.() ?? true,
|
|
1549
2338
|
};
|
|
1550
2339
|
}
|
|
2340
|
+
// Invariant: run-local completion may restate messages, but never owns transcript membership.
|
|
2341
|
+
#mergeRunTranscript(messages) {
|
|
2342
|
+
const positions = new Map();
|
|
2343
|
+
const sessionOccurrences = new Map();
|
|
2344
|
+
for (const [index, message] of (this.#session?.messages ?? []).entries()) {
|
|
2345
|
+
const key = messageFallbackKey(message, index);
|
|
2346
|
+
const occurrence = sessionOccurrences.get(key) ?? 0;
|
|
2347
|
+
sessionOccurrences.set(key, occurrence + 1);
|
|
2348
|
+
positions.set(message, { index, occurrence });
|
|
2349
|
+
}
|
|
2350
|
+
const runOccurrences = new Map();
|
|
2351
|
+
for (const [index, message] of messages.entries()) {
|
|
2352
|
+
const key = messageFallbackKey(message, index);
|
|
2353
|
+
const occurrence = runOccurrences.get(key) ?? 0;
|
|
2354
|
+
runOccurrences.set(key, occurrence + 1);
|
|
2355
|
+
const position = positions.get(message) ?? { index, occurrence };
|
|
2356
|
+
for (const block of this.#messageBlocks(message, "finalized", position.index, position.occurrence)) {
|
|
2357
|
+
this.#upsertTranscriptBlock(block);
|
|
2358
|
+
}
|
|
2359
|
+
this.#settleFailedDeclarations(message);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
// Invariant: full replacement is reserved for session-authoritative scope (bind/settlement).
|
|
1551
2363
|
#rebuildTranscript(messages, status) {
|
|
1552
2364
|
const blocks = [];
|
|
1553
2365
|
const blockIndexes = new Map();
|
|
@@ -1556,7 +2368,7 @@ export class PiEngineAdapter {
|
|
|
1556
2368
|
const key = messageFallbackKey(message, index);
|
|
1557
2369
|
const occurrence = occurrences.get(key) ?? 0;
|
|
1558
2370
|
occurrences.set(key, occurrence + 1);
|
|
1559
|
-
for (const block of this.#messageBlocks(message, status, index, occurrence)) {
|
|
2371
|
+
for (const block of this.#messageBlocks(message, status, index, occurrence, id => blocks[blockIndexes.get(id) ?? -1] ?? this.#transcriptBlock(id))) {
|
|
1560
2372
|
const existingIndex = blockIndexes.get(block.id);
|
|
1561
2373
|
if (existingIndex === undefined) {
|
|
1562
2374
|
blockIndexes.set(block.id, blocks.length);
|
|
@@ -1575,7 +2387,14 @@ export class PiEngineAdapter {
|
|
|
1575
2387
|
}
|
|
1576
2388
|
}
|
|
1577
2389
|
}
|
|
1578
|
-
|
|
2390
|
+
// Performance: an authoritative rebuild restates most of what is already there. Reusing the block
|
|
2391
|
+
// that already says it keeps its revision, and with it the rows the shell rendered for
|
|
2392
|
+
// it — otherwise every turn that ends re-renders the whole session.
|
|
2393
|
+
this.#setTranscript(blocks.map(block => {
|
|
2394
|
+
const existing = this.#transcriptBlock(block.id);
|
|
2395
|
+
const next = existing === undefined ? block : retainCompletedArguments(existing, block);
|
|
2396
|
+
return existing !== undefined && sameBlockContent(existing, next) ? existing : next;
|
|
2397
|
+
}));
|
|
1579
2398
|
}
|
|
1580
2399
|
#upsertMessageBlock(message, status) {
|
|
1581
2400
|
const blocks = this.#messageBlocks(message, status, this.#transcript.length);
|
|
@@ -1584,7 +2403,7 @@ export class PiEngineAdapter {
|
|
|
1584
2403
|
this.#upsertTranscriptBlock(block);
|
|
1585
2404
|
return first;
|
|
1586
2405
|
}
|
|
1587
|
-
#messageBlocks(message, status, fallbackIndex, occurrence) {
|
|
2406
|
+
#messageBlocks(message, status, fallbackIndex, occurrence, currentBlock = id => this.#transcriptBlock(id)) {
|
|
1588
2407
|
if (!isRecord(message) || typeof message.role !== "string")
|
|
1589
2408
|
return [];
|
|
1590
2409
|
const baseId = this.#messageBlockId(message, fallbackIndex, status, occurrence);
|
|
@@ -1596,7 +2415,12 @@ export class PiEngineAdapter {
|
|
|
1596
2415
|
revision: this.#nextBlockRevision(baseId),
|
|
1597
2416
|
title: "User",
|
|
1598
2417
|
text: textFromContent(message.content),
|
|
1599
|
-
|
|
2418
|
+
imageReferences: this.#imageReferences(message.content, "user"),
|
|
2419
|
+
payload: {
|
|
2420
|
+
role: "user",
|
|
2421
|
+
imageCount: contentImageCount(message.content),
|
|
2422
|
+
timestamp: typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : null,
|
|
2423
|
+
},
|
|
1600
2424
|
}];
|
|
1601
2425
|
}
|
|
1602
2426
|
if (message.role === "bashExecution") {
|
|
@@ -1660,24 +2484,24 @@ export class PiEngineAdapter {
|
|
|
1660
2484
|
: this.#toolBlockIds.get(toolCallId) ?? `tool-${toolCallId}`;
|
|
1661
2485
|
if (toolCallId !== undefined)
|
|
1662
2486
|
this.#toolBlockIds.set(toolCallId, blockId);
|
|
1663
|
-
const existing =
|
|
2487
|
+
const existing = currentBlock(blockId);
|
|
1664
2488
|
const existingPayload = isRecord(existing?.payload) ? existing.payload : undefined;
|
|
2489
|
+
const payload = {
|
|
2490
|
+
role: "toolResult", toolCallId: toolCallId ?? null,
|
|
2491
|
+
toolName: stringValue(message.toolName) ?? stringValue(existingPayload?.toolName) ?? "unknown",
|
|
2492
|
+
argsComplete: true, partialResult: status === "live", isError: message.isError === true,
|
|
2493
|
+
};
|
|
2494
|
+
const rendering = toolRenderingInput({ args: undefined, previousArgs: existing?.toolRendering,
|
|
2495
|
+
result: message, payload, image: part => this.#imageReferences([part], "tool-result")[0] });
|
|
1665
2496
|
return [{
|
|
1666
2497
|
id: blockId,
|
|
1667
2498
|
kind: "tool-result",
|
|
1668
2499
|
status,
|
|
2500
|
+
toolState: { argsComplete: true, execution: status === "live" ? "running" : message.isError === true ? "failed" : "succeeded" },
|
|
1669
2501
|
revision: this.#nextBlockRevision(blockId),
|
|
1670
2502
|
title: stringValue(message.toolName) ?? existing?.title ?? "Tool result",
|
|
1671
|
-
|
|
1672
|
-
payload
|
|
1673
|
-
...(existingPayload ?? {}),
|
|
1674
|
-
role: "toolResult",
|
|
1675
|
-
toolCallId: toolCallId ?? null,
|
|
1676
|
-
toolName: stringValue(message.toolName) ?? stringValue(existingPayload?.toolName) ?? "unknown",
|
|
1677
|
-
argsComplete: true,
|
|
1678
|
-
isError: message.isError === true,
|
|
1679
|
-
details: jsonSummary(message.details),
|
|
1680
|
-
},
|
|
2503
|
+
...rendering,
|
|
2504
|
+
payload,
|
|
1681
2505
|
}];
|
|
1682
2506
|
}
|
|
1683
2507
|
if (message.role !== "assistant" || !Array.isArray(message.content))
|
|
@@ -1707,23 +2531,74 @@ export class PiEngineAdapter {
|
|
|
1707
2531
|
const toolCallId = stringValue(item.id) ?? `${baseId}:${blocks.length}`;
|
|
1708
2532
|
const blockId = this.#toolBlockIds.get(toolCallId) ?? `tool-${toolCallId}`;
|
|
1709
2533
|
this.#toolBlockIds.set(toolCallId, blockId);
|
|
2534
|
+
const failure = status === "finalized" ? this.#declarationFailure(message) : undefined;
|
|
2535
|
+
const payload = { toolCallId, toolName: stringValue(item.name) ?? "unknown",
|
|
2536
|
+
argsComplete: failure === undefined && status === "finalized",
|
|
2537
|
+
...(failure === undefined ? {} : { isError: true }) };
|
|
2538
|
+
const rendering = toolRenderingInput({ args: item.arguments, payload,
|
|
2539
|
+
...(failure === undefined ? {} : { result: { content: [{ type: "text", text: failure.text }] } }),
|
|
2540
|
+
image: () => undefined });
|
|
1710
2541
|
blocks.push({
|
|
1711
2542
|
id: blockId,
|
|
1712
|
-
kind: "tool-call",
|
|
1713
|
-
status,
|
|
2543
|
+
kind: failure === undefined ? "tool-call" : "tool-result",
|
|
2544
|
+
status: failure === undefined ? "live" : "finalized",
|
|
2545
|
+
toolState: {
|
|
2546
|
+
argsComplete: failure === undefined && status === "finalized",
|
|
2547
|
+
execution: failure?.execution ?? "pending",
|
|
2548
|
+
},
|
|
1714
2549
|
revision: this.#nextBlockRevision(blockId),
|
|
1715
2550
|
title: stringValue(item.name) ?? "Tool",
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
toolName: stringValue(item.name) ?? "unknown",
|
|
1720
|
-
arguments: jsonSummary(item.arguments),
|
|
1721
|
-
argsComplete: status === "finalized",
|
|
1722
|
-
},
|
|
2551
|
+
...rendering,
|
|
2552
|
+
text: failure?.text ?? jsonSummary(item.arguments).summary,
|
|
2553
|
+
payload,
|
|
1723
2554
|
});
|
|
1724
2555
|
}
|
|
1725
2556
|
return blocks;
|
|
1726
2557
|
}
|
|
2558
|
+
#declarationFailure(message) {
|
|
2559
|
+
// Provenance: pinned interactive-mode message_end and history reconstruction settle pending tools.
|
|
2560
|
+
if (message.role !== "assistant")
|
|
2561
|
+
return undefined;
|
|
2562
|
+
if (message.stopReason === "aborted") {
|
|
2563
|
+
const attempts = this.#session?.retryAttempt ?? 0;
|
|
2564
|
+
return { execution: "aborted", text: attempts > 0
|
|
2565
|
+
? `Aborted after ${attempts} retry attempt${attempts > 1 ? "s" : ""}` : "Operation aborted" };
|
|
2566
|
+
}
|
|
2567
|
+
return message.stopReason === "error"
|
|
2568
|
+
? { execution: "failed", text: stringValue(message.errorMessage) || "Error" } : undefined;
|
|
2569
|
+
}
|
|
2570
|
+
#settleFailedDeclarations(message) {
|
|
2571
|
+
if (!isRecord(message))
|
|
2572
|
+
return;
|
|
2573
|
+
const failure = this.#declarationFailure(message);
|
|
2574
|
+
if (failure === undefined)
|
|
2575
|
+
return;
|
|
2576
|
+
for (const block of this.#transcript) {
|
|
2577
|
+
const state = transcriptToolState(block);
|
|
2578
|
+
if (state === undefined || state.execution !== "pending" && state.execution !== "running")
|
|
2579
|
+
continue;
|
|
2580
|
+
const payload = { ...(isRecord(block.payload) ? block.payload : {}), partialResult: false, isError: true };
|
|
2581
|
+
this.#upsertTranscriptBlock({
|
|
2582
|
+
...block, kind: "tool-result", status: "finalized", revision: block.revision + 1,
|
|
2583
|
+
...toolRenderingInput({ args: undefined, previousArgs: block.toolRendering, payload,
|
|
2584
|
+
result: { content: [{ type: "text", text: failure.text }] }, image: () => undefined }),
|
|
2585
|
+
toolState: { ...state, execution: failure.execution }, payload,
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
#imageReferences(content, source) {
|
|
2590
|
+
if (!Array.isArray(content))
|
|
2591
|
+
return [];
|
|
2592
|
+
const references = [];
|
|
2593
|
+
for (const item of content) {
|
|
2594
|
+
if (references.length >= 16)
|
|
2595
|
+
break;
|
|
2596
|
+
const reference = this.#transcriptImageAssets.reference(item, source);
|
|
2597
|
+
if (reference !== undefined)
|
|
2598
|
+
references.push(reference);
|
|
2599
|
+
}
|
|
2600
|
+
return references;
|
|
2601
|
+
}
|
|
1727
2602
|
#upsertToolExecutionBlock(event) {
|
|
1728
2603
|
const toolCallId = stringValue(event.toolCallId);
|
|
1729
2604
|
if (!toolCallId)
|
|
@@ -1731,31 +2606,52 @@ export class PiEngineAdapter {
|
|
|
1731
2606
|
const blockId = this.#toolBlockIds.get(toolCallId) ?? `tool-${toolCallId}`;
|
|
1732
2607
|
this.#toolBlockIds.set(toolCallId, blockId);
|
|
1733
2608
|
const ended = event.type === "tool_execution_end";
|
|
2609
|
+
const existing = this.#transcriptBlock(blockId);
|
|
2610
|
+
const state = existing === undefined ? undefined : transcriptToolState(existing);
|
|
2611
|
+
// Invariant: duplicate starts cannot blank accumulated output; late events cannot reopen a settled invocation.
|
|
2612
|
+
if (state !== undefined && (state.execution !== "pending" && state.execution !== "running"
|
|
2613
|
+
|| event.type === "tool_execution_start" && state.execution === "running"))
|
|
2614
|
+
return;
|
|
1734
2615
|
const source = ended ? event.result : event.partialResult;
|
|
2616
|
+
const payload = { toolCallId, toolName: stringValue(event.toolName) ?? "unknown",
|
|
2617
|
+
partialResult: event.type === "tool_execution_update", argsComplete: true, isError: event.isError === true };
|
|
2618
|
+
const rendering = toolRenderingInput({ args: event.args, previousArgs: existing?.toolRendering, result: source,
|
|
2619
|
+
payload, image: part => this.#imageReferences([part], "tool-result")[0] });
|
|
1735
2620
|
this.#upsertTranscriptBlock({
|
|
1736
2621
|
id: blockId,
|
|
1737
2622
|
kind: ended ? "tool-result" : "tool-call",
|
|
1738
2623
|
status: ended ? "finalized" : "live",
|
|
2624
|
+
toolState: { argsComplete: true, execution: ended ? event.isError === true ? "failed" : "succeeded" : "running" },
|
|
1739
2625
|
revision: this.#nextBlockRevision(blockId),
|
|
1740
2626
|
title: stringValue(event.toolName) ?? "Tool",
|
|
1741
|
-
|
|
1742
|
-
payload
|
|
1743
|
-
toolCallId,
|
|
1744
|
-
toolName: stringValue(event.toolName) ?? "unknown",
|
|
1745
|
-
arguments: jsonSummary(event.args),
|
|
1746
|
-
result: jsonSummary(source),
|
|
1747
|
-
partialResult: event.type === "tool_execution_update",
|
|
1748
|
-
argsComplete: ended,
|
|
1749
|
-
isError: event.isError === true,
|
|
1750
|
-
},
|
|
2627
|
+
...rendering,
|
|
2628
|
+
payload,
|
|
1751
2629
|
});
|
|
1752
2630
|
}
|
|
1753
2631
|
#upsertTranscriptBlock(block) {
|
|
1754
|
-
const index = this.#
|
|
1755
|
-
if (index
|
|
2632
|
+
const index = this.#transcriptIndex.get(block.id);
|
|
2633
|
+
if (index !== undefined) {
|
|
2634
|
+
const existing = this.#transcript[index];
|
|
2635
|
+
// Invariant: argument completion is not execution finality; stale phases still stay rejected.
|
|
2636
|
+
if (existing !== undefined && !acceptsTranscriptUpdate(existing, block))
|
|
2637
|
+
return;
|
|
2638
|
+
if (existing !== undefined)
|
|
2639
|
+
block = retainCompletedArguments(existing, block);
|
|
2640
|
+
// Performance: nothing is emitted for a block that repeats itself, and keeping the
|
|
2641
|
+
// revision keeps the rows it already rendered.
|
|
2642
|
+
if (existing !== undefined && sameBlockContent(existing, block))
|
|
2643
|
+
return;
|
|
2644
|
+
this.#transcriptImageAssets.retain(block);
|
|
2645
|
+
if (existing !== undefined)
|
|
2646
|
+
this.#transcriptImageAssets.release(existing);
|
|
1756
2647
|
this.#transcript[index] = block;
|
|
1757
|
-
|
|
2648
|
+
}
|
|
2649
|
+
else {
|
|
2650
|
+
this.#transcriptImageAssets.retain(block);
|
|
2651
|
+
this.#transcriptIndex.set(block.id, this.#transcript.length);
|
|
1758
2652
|
this.#transcript.push(block);
|
|
2653
|
+
}
|
|
2654
|
+
this.#transcriptSnapshot = undefined;
|
|
1759
2655
|
this.#emitEvent({ type: "transcript-block", block });
|
|
1760
2656
|
}
|
|
1761
2657
|
#messageBlockId(message, fallbackIndex, status, occurrence) {
|
|
@@ -1773,7 +2669,7 @@ export class PiEngineAdapter {
|
|
|
1773
2669
|
}
|
|
1774
2670
|
}
|
|
1775
2671
|
else {
|
|
1776
|
-
id = [...cached].reverse().find(candidate => this.#
|
|
2672
|
+
id = [...cached].reverse().find(candidate => this.#transcriptBlock(candidate)?.status === "live");
|
|
1777
2673
|
if (id === undefined && status === "finalized")
|
|
1778
2674
|
id = cached.at(-1);
|
|
1779
2675
|
if (id === undefined) {
|
|
@@ -1786,7 +2682,7 @@ export class PiEngineAdapter {
|
|
|
1786
2682
|
return id;
|
|
1787
2683
|
}
|
|
1788
2684
|
#nextBlockRevision(id) {
|
|
1789
|
-
const existing = this.#
|
|
2685
|
+
const existing = this.#transcriptBlock(id);
|
|
1790
2686
|
if (existing)
|
|
1791
2687
|
return existing.revision + 1;
|
|
1792
2688
|
this.#nextBlockSequence += 1;
|
|
@@ -1817,6 +2713,7 @@ export class PiEngineAdapter {
|
|
|
1817
2713
|
this.#emitEvent({ type: "diagnostic", diagnostic });
|
|
1818
2714
|
}
|
|
1819
2715
|
#emitView() {
|
|
2716
|
+
this.#usageCache = undefined;
|
|
1820
2717
|
this.#viewRevision += 1;
|
|
1821
2718
|
this.#emitEvent({ type: "session-view", view: this.view() });
|
|
1822
2719
|
}
|
|
@@ -1827,43 +2724,127 @@ export class PiEngineAdapter {
|
|
|
1827
2724
|
this.#enqueueEvent(event);
|
|
1828
2725
|
}
|
|
1829
2726
|
#enqueueEvent(event) {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
this.#recordDiagnostic("warning", "event-backpressure", `owned UI coalesced ${this.#droppedEventCount} engine events under backpressure`, true);
|
|
2727
|
+
if (this.#overload !== undefined) {
|
|
2728
|
+
if (event.type === "command-outcome" && event.outcome !== "accepted" && this.#pendingCommands.has(event.correlationId)) {
|
|
2729
|
+
this.#reservedOutcomes.set(event.correlationId, { outcome: event.outcome, diagnostic: event.diagnostic });
|
|
2730
|
+
}
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
if (!this.#eventQueue.push(event, this.#sessionGeneration, event.type === "transcript-block"
|
|
2734
|
+
? this.#blockReconciliation(event.block.id, event.sequence) : undefined)) {
|
|
2735
|
+
this.#beginOverload();
|
|
2736
|
+
if (event.type === "command-outcome" && event.outcome !== "accepted" && this.#pendingCommands.has(event.correlationId)
|
|
2737
|
+
&& !this.#reservedOutcomes.has(event.correlationId)) {
|
|
2738
|
+
this.#reservedOutcomes.set(event.correlationId, { outcome: event.outcome, diagnostic: event.diagnostic });
|
|
1843
2739
|
}
|
|
1844
2740
|
}
|
|
1845
|
-
this.#eventQueue.push(event);
|
|
1846
2741
|
this.#eventQueueProcessing ??= Promise.resolve().then(() => this.#processEventQueue());
|
|
1847
2742
|
}
|
|
2743
|
+
// Performance: capture only identity, not the complete block/event retained by an earlier revision.
|
|
2744
|
+
#blockReconciliation(id, sequence) {
|
|
2745
|
+
return () => ({ type: "transcript-block", sessionId: this.#sessionId, sequence, block: this.#transcriptBlock(id) });
|
|
2746
|
+
}
|
|
2747
|
+
#beginOverload() {
|
|
2748
|
+
if (this.#overload !== undefined)
|
|
2749
|
+
return;
|
|
2750
|
+
this.#overloads = Math.min(Number.MAX_SAFE_INTEGER, this.#overloads + 1);
|
|
2751
|
+
this.#deliveryFailed = true;
|
|
2752
|
+
// Concurrency: reserve before cancellation; outcomes produced reentrantly must not enter the saturated queue.
|
|
2753
|
+
this.#overload = Promise.resolve(false);
|
|
2754
|
+
for (const pending of this.#pendingCommands.values())
|
|
2755
|
+
pending.cancel();
|
|
2756
|
+
for (const pending of this.#pendingWorkflows)
|
|
2757
|
+
pending.cancel();
|
|
2758
|
+
const session = this.#session;
|
|
2759
|
+
this.#overload = new Promise(resolve => {
|
|
2760
|
+
const timer = setTimeout(() => resolve(false), 2000);
|
|
2761
|
+
void Promise.resolve().then(() => session?.abort()).then(() => {
|
|
2762
|
+
clearTimeout(timer);
|
|
2763
|
+
resolve(true);
|
|
2764
|
+
}, () => { clearTimeout(timer); resolve(false); });
|
|
2765
|
+
});
|
|
2766
|
+
}
|
|
2767
|
+
#deliver(event) {
|
|
2768
|
+
for (const [listener, subscribedAt] of this.#listeners) {
|
|
2769
|
+
if (event.sequence <= subscribedAt)
|
|
2770
|
+
continue;
|
|
2771
|
+
try {
|
|
2772
|
+
listener(event);
|
|
2773
|
+
}
|
|
2774
|
+
catch (error) {
|
|
2775
|
+
this.#deliveryFailed = true;
|
|
2776
|
+
this.#recordDiagnostic("warning", "event-listener", error instanceof Error ? error.message : String(error), true);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
async #reconcileOverload() {
|
|
2781
|
+
const cancelled = await this.#overload;
|
|
2782
|
+
const canResume = cancelled === true && this.#runningCommands === 0;
|
|
2783
|
+
this.#admissionStopped = !canResume;
|
|
2784
|
+
this.#unsubscribe?.();
|
|
2785
|
+
this.#unsubscribe = undefined;
|
|
2786
|
+
++this.#sessionGeneration;
|
|
2787
|
+
this.#agentRunActive = false;
|
|
2788
|
+
this.#statusKind = null;
|
|
2789
|
+
this.#status = { ...this.#status, workingMessage: null };
|
|
2790
|
+
this.#lifecycle = this.#disposed ? "stopped" : canResume ? "ready" : "failed";
|
|
2791
|
+
this.#editor = { ...this.#editor, submitEnabled: canResume && !this.#disposed };
|
|
2792
|
+
this.#viewRevision += 1;
|
|
2793
|
+
// Invariant: overload publishes one authoritative view; settle tool phases before finalizing other live blocks.
|
|
2794
|
+
this.#settleFailedDeclarations({ role: "assistant", stopReason: cancelled ? "aborted" : "error",
|
|
2795
|
+
errorMessage: "Tool result unavailable after UI delivery overload" });
|
|
2796
|
+
this.#setTranscript(this.#transcript.map(block => block.status === "live" ? { ...block, status: "finalized", revision: block.revision + 1 } : block));
|
|
2797
|
+
for (const [correlationId, result] of this.#reservedOutcomes) {
|
|
2798
|
+
this.#deliver(this.#event({ type: "command-outcome", correlationId, ...result }));
|
|
2799
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
2800
|
+
}
|
|
2801
|
+
this.#reservedOutcomes.clear();
|
|
2802
|
+
// Invariant: one out-of-band authoritative reconciliation, not an emergency transcript/event log.
|
|
2803
|
+
this.#deliver(this.#event({ type: "session-view", view: this.view() }));
|
|
2804
|
+
if (this.#disposed)
|
|
2805
|
+
this.#deliver(this.#event({ type: "session-lifecycle", lifecycle: "stopped", reason: null }));
|
|
2806
|
+
if (canResume && !this.#disposed && this.#session !== undefined) {
|
|
2807
|
+
const generation = this.#sessionGeneration;
|
|
2808
|
+
this.#unsubscribe = this.#session.subscribe(event => {
|
|
2809
|
+
if (generation === this.#sessionGeneration && !this.#disposed)
|
|
2810
|
+
this.#handlePiEvent(event);
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
this.#overload = undefined;
|
|
2814
|
+
}
|
|
1848
2815
|
async #processEventQueue() {
|
|
1849
2816
|
try {
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2817
|
+
let deliveredSinceYield = 0;
|
|
2818
|
+
while (this.#eventQueue.size > 0) {
|
|
2819
|
+
const pending = this.#eventQueue.shift();
|
|
2820
|
+
if (pending === undefined)
|
|
1853
2821
|
continue;
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
2822
|
+
if (pending.generation !== this.#sessionGeneration && pending.event.type !== "command-outcome") {
|
|
2823
|
+
this.#invalidatedEvents = Math.min(Number.MAX_SAFE_INTEGER, this.#invalidatedEvents + 1);
|
|
2824
|
+
pending.release?.();
|
|
2825
|
+
continue;
|
|
2826
|
+
}
|
|
2827
|
+
try {
|
|
2828
|
+
this.#deliver(pending.event);
|
|
2829
|
+
}
|
|
2830
|
+
finally {
|
|
2831
|
+
pending.release?.();
|
|
2832
|
+
}
|
|
2833
|
+
deliveredSinceYield += 1;
|
|
2834
|
+
// Concurrency: a microtask chain runs to exhaustion before the loop turns, so a streaming
|
|
2835
|
+
// burst would hold typed input, pointer reports, and timed indicators until it
|
|
2836
|
+
// drained. Yielding on a macrotask hands those their turn between batches.
|
|
2837
|
+
if (deliveredSinceYield >= EVENT_DELIVERY_BATCH && this.#eventQueue.size > 0) {
|
|
2838
|
+
deliveredSinceYield = 0;
|
|
2839
|
+
await new Promise(resolve => { setImmediate(resolve); });
|
|
1861
2840
|
}
|
|
1862
2841
|
}
|
|
2842
|
+
if (this.#overload !== undefined)
|
|
2843
|
+
await this.#reconcileOverload();
|
|
1863
2844
|
}
|
|
1864
2845
|
finally {
|
|
1865
2846
|
this.#eventQueueProcessing = undefined;
|
|
1866
|
-
if (this.#eventQueue.
|
|
2847
|
+
if (this.#eventQueue.size > 0) {
|
|
1867
2848
|
this.#eventQueueProcessing = Promise.resolve().then(() => this.#processEventQueue());
|
|
1868
2849
|
}
|
|
1869
2850
|
}
|
|
@@ -1896,7 +2877,14 @@ export async function createPiEngineAdapter(options = {}) {
|
|
|
1896
2877
|
return adapter;
|
|
1897
2878
|
}
|
|
1898
2879
|
async function createDefaultPiRuntime(input) {
|
|
1899
|
-
return createPiRuntimeIntegration({
|
|
2880
|
+
return createPiRuntimeIntegration({
|
|
2881
|
+
cwd: input.cwd,
|
|
2882
|
+
agentDir: input.agentDir,
|
|
2883
|
+
...(input.sessionPath === undefined ? {} : { sessionPath: input.sessionPath }),
|
|
2884
|
+
...(input.sessionSelection === undefined ? {} : { sessionSelection: input.sessionSelection }),
|
|
2885
|
+
...(input.sessionForkPrompt === undefined ? {} : { sessionForkPrompt: input.sessionForkPrompt }),
|
|
2886
|
+
...(input.projectTrustPrompt === undefined ? {} : { projectTrustPrompt: input.projectTrustPrompt }),
|
|
2887
|
+
});
|
|
1900
2888
|
}
|
|
1901
2889
|
async function checkDefaultPiPackageUpdates(cwd, agentDir, settingsManager) {
|
|
1902
2890
|
const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
|
|
@@ -2019,11 +3007,11 @@ function pinnedCacheWaste(entries, modelRuntime) {
|
|
|
2019
3007
|
function defaultWorkflowHost() {
|
|
2020
3008
|
return {
|
|
2021
3009
|
copyText: copyToClipboard,
|
|
2022
|
-
async runCommand(command, arguments_) {
|
|
2023
|
-
const result = await execFileAsync(command, [...arguments_], { encoding: "utf8" });
|
|
3010
|
+
async runCommand(command, arguments_, options) {
|
|
3011
|
+
const result = await execFileAsync(command, [...arguments_], { encoding: "utf8", signal: options?.signal });
|
|
2024
3012
|
return { stdout: result.stdout, stderr: result.stderr };
|
|
2025
3013
|
},
|
|
2026
|
-
readChangelog:
|
|
3014
|
+
readChangelog: readPinnedCommandChangelog,
|
|
2027
3015
|
};
|
|
2028
3016
|
}
|
|
2029
3017
|
function workflowLoginNotification(event) {
|
|
@@ -2056,14 +3044,22 @@ function workflowLoginNotification(event) {
|
|
|
2056
3044
|
}
|
|
2057
3045
|
return { type: event.type === "waiting" ? "waiting" : "progress", message };
|
|
2058
3046
|
}
|
|
2059
|
-
function workflowResult(command, outcome, message, detail) {
|
|
2060
|
-
return {
|
|
3047
|
+
function workflowResult(command, outcome, message, detail, messageKind, messages) {
|
|
3048
|
+
return {
|
|
3049
|
+
command,
|
|
3050
|
+
outcome,
|
|
3051
|
+
message,
|
|
3052
|
+
...(detail === undefined ? {} : { detail }),
|
|
3053
|
+
...(messageKind === undefined ? {} : { messageKind }),
|
|
3054
|
+
...(messages === undefined ? {} : { messages: Object.freeze([...messages]) }),
|
|
3055
|
+
};
|
|
2061
3056
|
}
|
|
2062
|
-
function workflowConfirmation(command, message) {
|
|
3057
|
+
function workflowConfirmation(command, message, detail) {
|
|
2063
3058
|
return {
|
|
2064
3059
|
command,
|
|
2065
3060
|
outcome: "requires-confirmation",
|
|
2066
3061
|
message,
|
|
3062
|
+
...(detail === undefined ? {} : { detail }),
|
|
2067
3063
|
selectorTitle: "Confirm",
|
|
2068
3064
|
options: [
|
|
2069
3065
|
{ id: "yes", label: "Yes" },
|
|
@@ -2071,6 +3067,26 @@ function workflowConfirmation(command, message) {
|
|
|
2071
3067
|
],
|
|
2072
3068
|
};
|
|
2073
3069
|
}
|
|
3070
|
+
function errorMessage(error, fallback) {
|
|
3071
|
+
return error instanceof Error ? error.message : fallback;
|
|
3072
|
+
}
|
|
3073
|
+
function shareCommandFailureDetail(error) {
|
|
3074
|
+
// Compatibility: Pi reports a failed child's stderr, not execFile's command wrapper.
|
|
3075
|
+
if (isRecord(error) && (typeof error.code === "number" || typeof error.signal === "string") && typeof error.stderr === "string") {
|
|
3076
|
+
return error.stderr.trim() || "Unknown error";
|
|
3077
|
+
}
|
|
3078
|
+
return errorMessage(error, "Unknown error");
|
|
3079
|
+
}
|
|
3080
|
+
function commandMissing(error) {
|
|
3081
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
3082
|
+
}
|
|
3083
|
+
function isAbortError(error) {
|
|
3084
|
+
return isRecord(error) && (error.name === "AbortError" || error.code === "ABORT_ERR");
|
|
3085
|
+
}
|
|
3086
|
+
function shareViewerUrl(gistId) {
|
|
3087
|
+
const baseUrl = process.env.PI_SHARE_VIEWER_URL || "https://pi.dev/session/";
|
|
3088
|
+
return `${baseUrl}#${gistId}`;
|
|
3089
|
+
}
|
|
2074
3090
|
function workflowOptions(value, idKey, labelKey) {
|
|
2075
3091
|
if (!Array.isArray(value))
|
|
2076
3092
|
return [];
|
|
@@ -2122,7 +3138,7 @@ function settingKeyForCallback(callback) {
|
|
|
2122
3138
|
onAutoCompactChange: "autoCompact", onShowImagesChange: "showImages", onImageWidthCellsChange: "imageWidthCells",
|
|
2123
3139
|
onAutoResizeImagesChange: "autoResizeImages", onBlockImagesChange: "blockImages", onEnableSkillCommandsChange: "enableSkillCommands",
|
|
2124
3140
|
onSteeringModeChange: "steeringMode", onFollowUpModeChange: "followUpMode", onTransportChange: "transport",
|
|
2125
|
-
onHttpIdleTimeoutMsChange: "httpIdleTimeoutMs", onThemeChange: "theme", onThemePreview: "theme",
|
|
3141
|
+
onHttpIdleTimeoutMsChange: "httpIdleTimeoutMs", onThinkingLevelChange: "thinkingLevel", onThemeChange: "theme", onThemePreview: "theme",
|
|
2126
3142
|
onHideThinkingBlockChange: "hideThinkingBlock", onMermaidRenderingModeChange: "mermaidRenderingMode",
|
|
2127
3143
|
onShowCacheMissNoticesChange: "showCacheMissNotices", onCollapseChangelogChange: "collapseChangelog",
|
|
2128
3144
|
onEnableInstallTelemetryChange: "enableInstallTelemetry", onQuietStartupChange: "quietStartup",
|
|
@@ -2245,11 +3261,26 @@ function extensionResourceDiagnostic(index, sourcePath, diagnostic) {
|
|
|
2245
3261
|
id: `extension-diagnostic-${index}`,
|
|
2246
3262
|
sourcePath,
|
|
2247
3263
|
resolvedPath: null,
|
|
3264
|
+
sourceInfo: null,
|
|
2248
3265
|
loaded: false,
|
|
2249
3266
|
hidden: false,
|
|
2250
3267
|
diagnostic,
|
|
2251
3268
|
};
|
|
2252
3269
|
}
|
|
3270
|
+
function extensionSourceSummary(value) {
|
|
3271
|
+
if (!isRecord(value))
|
|
3272
|
+
return null;
|
|
3273
|
+
const source = stringProperty(value, "source");
|
|
3274
|
+
const scope = value.scope;
|
|
3275
|
+
const origin = value.origin;
|
|
3276
|
+
const baseDir = value.baseDir;
|
|
3277
|
+
if (!source
|
|
3278
|
+
|| (scope !== "user" && scope !== "project" && scope !== "temporary")
|
|
3279
|
+
|| (origin !== "package" && origin !== "top-level")
|
|
3280
|
+
|| (baseDir !== undefined && typeof baseDir !== "string"))
|
|
3281
|
+
return null;
|
|
3282
|
+
return { source, scope, origin, baseDir: baseDir ?? null };
|
|
3283
|
+
}
|
|
2253
3284
|
function collectionResult(value, key) {
|
|
2254
3285
|
if (!isRecord(value))
|
|
2255
3286
|
return { values: [], diagnostics: [] };
|
|
@@ -2274,10 +3305,91 @@ function stringProperty(value, key) {
|
|
|
2274
3305
|
const item = value[key];
|
|
2275
3306
|
return typeof item === "string" && item.length > 0 ? item : undefined;
|
|
2276
3307
|
}
|
|
3308
|
+
function isUnknownModel(value) {
|
|
3309
|
+
return isRecord(value)
|
|
3310
|
+
&& value.provider === "unknown"
|
|
3311
|
+
&& value.id === "unknown"
|
|
3312
|
+
&& value.api === "unknown";
|
|
3313
|
+
}
|
|
3314
|
+
function findExactWorkflowModel(reference, models) {
|
|
3315
|
+
const normalized = reference.trim().toLowerCase();
|
|
3316
|
+
const canonical = models.filter(model => {
|
|
3317
|
+
const provider = stringProperty(model, "provider");
|
|
3318
|
+
const id = stringProperty(model, "id");
|
|
3319
|
+
return provider !== undefined && id !== undefined && `${provider}/${id}`.toLowerCase() === normalized;
|
|
3320
|
+
});
|
|
3321
|
+
if (canonical.length === 1)
|
|
3322
|
+
return canonical[0];
|
|
3323
|
+
const bare = models.filter(model => stringProperty(model, "id")?.toLowerCase() === normalized);
|
|
3324
|
+
return bare.length === 1 ? bare[0] : undefined;
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* Some ecosystem extensions retain a `pi-<name>` slash-command alias beside
|
|
3328
|
+
* their unprefixed command. A1 presents the product-neutral command once while
|
|
3329
|
+
* leaving Pi's runner free to accept the compatibility alias when typed.
|
|
3330
|
+
*/
|
|
3331
|
+
function isPiPrefixedCompatibilityAlias(command, commands) {
|
|
3332
|
+
const name = stringProperty(command, "name");
|
|
3333
|
+
if (!name?.startsWith("pi-") || name.length === 3)
|
|
3334
|
+
return false;
|
|
3335
|
+
const canonicalName = name.slice(3);
|
|
3336
|
+
const description = stringProperty(command, "description");
|
|
3337
|
+
const sourcePath = extensionCommandSourcePath(command);
|
|
3338
|
+
return commands.some(candidate => candidate !== command
|
|
3339
|
+
&& stringProperty(candidate, "name") === canonicalName
|
|
3340
|
+
&& stringProperty(candidate, "description") === description
|
|
3341
|
+
&& extensionCommandSourcePath(candidate) === sourcePath);
|
|
3342
|
+
}
|
|
3343
|
+
function extensionCommandSourcePath(command) {
|
|
3344
|
+
return isRecord(command) ? stringProperty(command.sourceInfo, "path") : undefined;
|
|
3345
|
+
}
|
|
2277
3346
|
function compactResourceLabel(path) {
|
|
2278
3347
|
const segments = path.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
2279
3348
|
return segments.at(-1) ?? path;
|
|
2280
3349
|
}
|
|
3350
|
+
/** A later error may restate a declaration, but cannot make completed arguments incomplete. */
|
|
3351
|
+
function retainCompletedArguments(current, next) {
|
|
3352
|
+
if (current.toolState?.argsComplete !== true || next.toolState === undefined || next.toolState.argsComplete)
|
|
3353
|
+
return next;
|
|
3354
|
+
return { ...next, toolState: { ...next.toolState, argsComplete: true },
|
|
3355
|
+
...(next.toolRendering === undefined || current.toolRendering === undefined ? {} : {
|
|
3356
|
+
toolRendering: { ...next.toolRendering, arguments: current.toolRendering.arguments },
|
|
3357
|
+
}),
|
|
3358
|
+
payload: { ...(isRecord(next.payload) ? next.payload : {}), argsComplete: true } };
|
|
3359
|
+
}
|
|
3360
|
+
/**
|
|
3361
|
+
* Whether two blocks say the same thing. A block that says what it already said is not a
|
|
3362
|
+
* new revision: the shell renders a block once per revision, so bumping one it did not
|
|
3363
|
+
* need re-renders it for nothing.
|
|
3364
|
+
*/
|
|
3365
|
+
function sameBlockContent(left, right) {
|
|
3366
|
+
return left.kind === right.kind
|
|
3367
|
+
&& left.status === right.status
|
|
3368
|
+
&& left.title === right.title
|
|
3369
|
+
&& left.text === right.text
|
|
3370
|
+
&& sameValue(left.toolState, right.toolState)
|
|
3371
|
+
&& sameValue(left.toolRendering, right.toolRendering)
|
|
3372
|
+
&& sameValue(left.payload, right.payload)
|
|
3373
|
+
&& sameValue(left.imageReferences ?? [], right.imageReferences ?? []);
|
|
3374
|
+
}
|
|
3375
|
+
function sameValue(left, right) {
|
|
3376
|
+
if (left === right)
|
|
3377
|
+
return true;
|
|
3378
|
+
if (typeof left !== typeof right || left === null || right === null)
|
|
3379
|
+
return false;
|
|
3380
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
3381
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length)
|
|
3382
|
+
return false;
|
|
3383
|
+
return left.every((value, index) => sameValue(value, right[index]));
|
|
3384
|
+
}
|
|
3385
|
+
if (typeof left !== "object")
|
|
3386
|
+
return false;
|
|
3387
|
+
const leftKeys = Object.keys(left);
|
|
3388
|
+
const rightRecord = right;
|
|
3389
|
+
if (leftKeys.length !== Object.keys(rightRecord).length)
|
|
3390
|
+
return false;
|
|
3391
|
+
return leftKeys.every(key => Object.hasOwn(rightRecord, key) && sameValue(left[key], rightRecord[key]));
|
|
3392
|
+
}
|
|
2281
3393
|
function textFromContent(content) {
|
|
2282
3394
|
if (typeof content === "string")
|
|
2283
3395
|
return content;
|