harness-mix 0.1.0
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/LICENSE +202 -0
- package/NOTICE +14 -0
- package/README.md +184 -0
- package/Start-Codex.cmd +5 -0
- package/config/codex-desktop-compatibility.json +23 -0
- package/docs/chatgpt-web-sidebar.md +27 -0
- package/docs/harness-management.md +41 -0
- package/docs/images/harness-mix-home.png +0 -0
- package/docs/images/harness-mix-session.png +0 -0
- package/docs/multi-agent-collaboration.md +89 -0
- package/docs/native-codex.md +72 -0
- package/licenses/codex-host-MIT.txt +21 -0
- package/output/native-build/desktop-controller.mjs +1460 -0
- package/output/native-build/harness-mix-appx.exe +0 -0
- package/output/native-build/harness-mix-shim.exe +0 -0
- package/output/native-build/renderer-extension.js +28884 -0
- package/package.json +142 -0
- package/scripts/acp-capabilities-probe.cjs +100 -0
- package/scripts/adapters-test.cjs +17 -0
- package/scripts/antigravity-adapter-test.cjs +174 -0
- package/scripts/architecture-test.cjs +62 -0
- package/scripts/build-native.cjs +46 -0
- package/scripts/catalog-test.cjs +25 -0
- package/scripts/cdp-eval.cjs +40 -0
- package/scripts/cdp-watch.cjs +36 -0
- package/scripts/check.cjs +23 -0
- package/scripts/codex-adapter-test.cjs +83 -0
- package/scripts/collaboration-recovery-test.cjs +91 -0
- package/scripts/collaboration-test.cjs +102 -0
- package/scripts/collaboration-ui-smoke.cjs +52 -0
- package/scripts/concurrency-layers-test.cjs +179 -0
- package/scripts/contracts-test.cjs +105 -0
- package/scripts/core-concurrency-test.cjs +103 -0
- package/scripts/core-replay-test.cjs +179 -0
- package/scripts/core-runtime-test.cjs +104 -0
- package/scripts/core-services-test.cjs +50 -0
- package/scripts/determinism-test.cjs +36 -0
- package/scripts/docs-command-test.cjs +49 -0
- package/scripts/dsh-adapter-test.cjs +87 -0
- package/scripts/dsh-native.cjs +9 -0
- package/scripts/dsh-native.cmd +2 -0
- package/scripts/e2e-codex.cjs +82 -0
- package/scripts/e2e-collaboration.cjs +58 -0
- package/scripts/e2e-core-files.cjs +58 -0
- package/scripts/e2e-core-fork.cjs +49 -0
- package/scripts/e2e-core-resume.cjs +49 -0
- package/scripts/e2e-delegate.cjs +38 -0
- package/scripts/e2e-dsh.cjs +23 -0
- package/scripts/e2e-native-commands.cjs +54 -0
- package/scripts/e2e-native-host.cjs +144 -0
- package/scripts/e2e-openclaw-image.cjs +59 -0
- package/scripts/e2e-openclaw.cjs +21 -0
- package/scripts/e2e-pi.cjs +23 -0
- package/scripts/e2e-shadow.cjs +52 -0
- package/scripts/fixture-capture.cjs +103 -0
- package/scripts/git-test.cjs +33 -0
- package/scripts/launch-codex.cjs +5 -0
- package/scripts/native-compatibility-test.cjs +42 -0
- package/scripts/native-config-test.cjs +25 -0
- package/scripts/native-file-change-test.cjs +94 -0
- package/scripts/native-host.cjs +8 -0
- package/scripts/native-images-test.cjs +56 -0
- package/scripts/native-protocol-test.cjs +233 -0
- package/scripts/native-rollback-test.cjs +46 -0
- package/scripts/native-sidebar-test.cjs +88 -0
- package/scripts/native-ui-smoke.cjs +94 -0
- package/scripts/native-updater-test.cjs +100 -0
- package/scripts/native-vendor-adapters-test.cjs +40 -0
- package/scripts/openclaw-acp-probe.cjs +39 -0
- package/scripts/openclaw-adapter-test.cjs +180 -0
- package/scripts/openclaw-models-probe.cjs +13 -0
- package/scripts/projector-test.cjs +144 -0
- package/scripts/sequence-test.cjs +57 -0
- package/scripts/shadow-test.cjs +154 -0
- package/scripts/support/legacy-projection.cjs +95 -0
- package/scripts/support/legacy-transcript.cjs +48 -0
- package/scripts/support/parity-observer.cjs +27 -0
- package/scripts/support/shadow-comparator.cjs +62 -0
- package/scripts/support/shadow.cjs +114 -0
- package/scripts/switch-harness-test.cjs +145 -0
- package/scripts/tool-concurrency-regression-test.cjs +118 -0
- package/scripts/transcript-test.cjs +24 -0
- package/scripts/turn-manager-test.cjs +91 -0
- package/scripts/workspace-test.cjs +98 -0
- package/src/assets/brand-harness-mix.png +0 -0
- package/src/assets/icons/activity-compact.svg +1 -0
- package/src/assets/icons/activity-edit.svg +1 -0
- package/src/assets/icons/activity-terminal.svg +1 -0
- package/src/assets/icons/antigravity-color.svg +1 -0
- package/src/assets/icons/claude-color.svg +1 -0
- package/src/assets/icons/codex-color.svg +1 -0
- package/src/assets/icons/codex-harness.svg +1 -0
- package/src/assets/icons/commands.svg +1 -0
- package/src/assets/icons/deepseek-color.svg +1 -0
- package/src/assets/icons/grok-color.svg +1 -0
- package/src/assets/icons/hermes-color.svg +1 -0
- package/src/assets/icons/home-chat.svg +1 -0
- package/src/assets/icons/home-folder.svg +1 -0
- package/src/assets/icons/model-astra.svg +1 -0
- package/src/assets/icons/model-claude.svg +1 -0
- package/src/assets/icons/model-deepseek.svg +1 -0
- package/src/assets/icons/model-gemini.svg +1 -0
- package/src/assets/icons/model-hunyuan.svg +1 -0
- package/src/assets/icons/model-kimi.svg +1 -0
- package/src/assets/icons/model-minimax.svg +1 -0
- package/src/assets/icons/model-openai.svg +1 -0
- package/src/assets/icons/model-qwen-color.svg +1 -0
- package/src/assets/icons/model-xiaomimimo.svg +1 -0
- package/src/assets/icons/model-zai.svg +1 -0
- package/src/assets/icons/omp-color.svg +1 -0
- package/src/assets/icons/openclaw-color.svg +1 -0
- package/src/assets/icons/opencode-color.svg +1 -0
- package/src/assets/icons/pi.svg +1 -0
- package/src/assets/icons/pinumber1_80899.svg +48 -0
- package/src/assets/icons/qoder-color.svg +1 -0
- package/src/assets/icons/thread-fork.svg +1 -0
- package/src/assets/icons/trae-color.svg +1 -0
- package/src/assets/icons/workbuddy-color.svg +1 -0
- package/src/assets/icons/zcode-color.svg +1 -0
- package/src/main/adapters/acp.js +171 -0
- package/src/main/adapters/antigravity.js +895 -0
- package/src/main/adapters/claude.js +399 -0
- package/src/main/adapters/codex-app-server.js +119 -0
- package/src/main/adapters/codex.js +547 -0
- package/src/main/adapters/dsh-web-host.js +207 -0
- package/src/main/adapters/dsh.js +415 -0
- package/src/main/adapters/grok.js +161 -0
- package/src/main/adapters/hermes.js +24 -0
- package/src/main/adapters/index.js +30 -0
- package/src/main/adapters/omp.js +12 -0
- package/src/main/adapters/openclaw-gateway.js +215 -0
- package/src/main/adapters/openclaw.js +337 -0
- package/src/main/adapters/opencode-server.js +72 -0
- package/src/main/adapters/opencode.js +166 -0
- package/src/main/adapters/pi-family.js +315 -0
- package/src/main/adapters/pi-usage.js +24 -0
- package/src/main/adapters/pi.js +10 -0
- package/src/main/adapters/qoder.js +35 -0
- package/src/main/adapters/trae.js +19 -0
- package/src/main/adapters/workbuddy.js +67 -0
- package/src/main/adapters/zcode.js +38 -0
- package/src/main/harness-adapter/contract-test.js +53 -0
- package/src/main/harness-adapter/contract.js +34 -0
- package/src/main/harness-adapter/event-normalizer.js +233 -0
- package/src/main/harness-adapter/fixture-recorder.js +56 -0
- package/src/main/harness-adapter/index.js +6 -0
- package/src/main/harness-adapter/manifest.js +44 -0
- package/src/main/host/collaboration-mcp.cjs +35 -0
- package/src/main/host/collaboration-pi.mjs +17 -0
- package/src/main/host/collaboration-tools.js +16 -0
- package/src/main/host/collaboration-worktree.js +82 -0
- package/src/main/host/collaboration.js +238 -0
- package/src/main/host/core-session.js +81 -0
- package/src/main/host/handoff.js +46 -0
- package/src/main/host/history-import.js +55 -0
- package/src/main/host/jsonl.js +115 -0
- package/src/main/host/native-history.js +74 -0
- package/src/main/host/runtime.js +927 -0
- package/src/main/host/session-history.js +63 -0
- package/src/main/host/store.js +42 -0
- package/src/main/native/compatibility.js +55 -0
- package/src/main/native/config.js +49 -0
- package/src/main/native/host.js +108 -0
- package/src/main/native/icons.js +101 -0
- package/src/main/native/input.js +43 -0
- package/src/main/native/launcher.js +100 -0
- package/src/main/native/protocol.js +552 -0
- package/src/main/native/rs/Cargo.lock +11 -0
- package/src/main/native/rs/Cargo.toml +9 -0
- package/src/main/native/rs/crates/appx/Cargo.toml +13 -0
- package/src/main/native/rs/crates/appx/src/main.rs +309 -0
- package/src/main/native/rs/crates/shim/Cargo.toml +10 -0
- package/src/main/native/rs/crates/shim/src/main.rs +207 -0
- package/src/main/native/thread-list.js +38 -0
- package/src/main/native/updater.js +102 -0
- package/src/main/native/usage.js +29 -0
- package/src/main/protocol-core/capability-manager.js +16 -0
- package/src/main/protocol-core/event-router.js +32 -0
- package/src/main/protocol-core/file-change-projector.js +45 -0
- package/src/main/protocol-core/index.js +8 -0
- package/src/main/protocol-core/interaction-router.js +30 -0
- package/src/main/protocol-core/projector.js +238 -0
- package/src/main/protocol-core/protocol-core.js +132 -0
- package/src/main/protocol-core/sequence-validator.js +44 -0
- package/src/main/protocol-core/thread-manager.js +50 -0
- package/src/main/protocol-core/turn-manager.js +63 -0
- package/src/main/shared-contracts/capability.js +35 -0
- package/src/main/shared-contracts/event.js +55 -0
- package/src/main/shared-contracts/ids.js +12 -0
- package/src/main/shared-contracts/index.js +11 -0
- package/src/main/shared-contracts/interaction.js +37 -0
- package/src/main/shared-contracts/item.js +50 -0
- package/src/main/shared-contracts/native-ref.js +24 -0
- package/src/main/shared-contracts/thread.js +36 -0
- package/src/main/shared-contracts/turn.js +54 -0
- package/src/main/workspace/core-review.js +53 -0
- package/src/main/workspace/diff.js +27 -0
- package/src/main/workspace/file-changes.js +21 -0
- package/src/main/workspace/files.js +65 -0
- package/src/main/workspace/git.js +73 -0
- package/src/main/workspace/ipc.js +50 -0
- package/src/main/workspace/review-controller.js +102 -0
- package/src/main/workspace/review.js +83 -0
- package/src/main/workspace/terminal.js +36 -0
- package/src/native-ui/LICENSE +21 -0
- package/src/native-ui/README.md +12 -0
- package/src/native-ui/desktop-control/dist/cdp-client.d.ts +53 -0
- package/src/native-ui/desktop-control/dist/cdp-client.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/cdp-client.js +258 -0
- package/src/native-ui/desktop-control/dist/cdp-client.js.map +1 -0
- package/src/native-ui/desktop-control/dist/contract-audit.d.ts +73 -0
- package/src/native-ui/desktop-control/dist/contract-audit.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/contract-audit.js +152 -0
- package/src/native-ui/desktop-control/dist/contract-audit.js.map +1 -0
- package/src/native-ui/desktop-control/dist/controller-attachment-server.d.ts +10 -0
- package/src/native-ui/desktop-control/dist/controller-attachment-server.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/controller-attachment-server.js +77 -0
- package/src/native-ui/desktop-control/dist/controller-attachment-server.js.map +1 -0
- package/src/native-ui/desktop-control/dist/index.d.ts +21 -0
- package/src/native-ui/desktop-control/dist/index.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/index.js +14 -0
- package/src/native-ui/desktop-control/dist/index.js.map +1 -0
- package/src/native-ui/desktop-control/dist/main-process-title-policy.d.ts +24 -0
- package/src/native-ui/desktop-control/dist/main-process-title-policy.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/main-process-title-policy.js +246 -0
- package/src/native-ui/desktop-control/dist/main-process-title-policy.js.map +1 -0
- package/src/native-ui/desktop-control/dist/production-controller.d.ts +32 -0
- package/src/native-ui/desktop-control/dist/production-controller.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/production-controller.js +291 -0
- package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -0
- package/src/native-ui/desktop-control/dist/release-main.d.ts +2 -0
- package/src/native-ui/desktop-control/dist/release-main.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/release-main.js +17 -0
- package/src/native-ui/desktop-control/dist/release-main.js.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.d.ts +49 -0
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js +214 -0
- package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-control-session.d.ts +79 -0
- package/src/native-ui/desktop-control/dist/renderer-control-session.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-control-session.js +366 -0
- package/src/native-ui/desktop-control/dist/renderer-control-session.js.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-dom.d.ts +24 -0
- package/src/native-ui/desktop-control/dist/renderer-dom.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-dom.js +110 -0
- package/src/native-ui/desktop-control/dist/renderer-dom.js.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.d.ts +19 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.js +162 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-policy.js.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.d.ts +38 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.d.ts.map +1 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +633 -0
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -0
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -0
- package/src/native-ui/desktop-control/package.json +22 -0
- package/src/native-ui/desktop-control/scripts/build-release.mjs +94 -0
- package/src/native-ui/desktop-control/src/cdp-client.ts +347 -0
- package/src/native-ui/desktop-control/src/contract-audit.ts +291 -0
- package/src/native-ui/desktop-control/src/controller-attachment-server.ts +93 -0
- package/src/native-ui/desktop-control/src/index.ts +91 -0
- package/src/native-ui/desktop-control/src/main-process-title-policy.ts +334 -0
- package/src/native-ui/desktop-control/src/production-controller.ts +344 -0
- package/src/native-ui/desktop-control/src/release-main.ts +18 -0
- package/src/native-ui/desktop-control/src/renderer-cdp-control-session.ts +358 -0
- package/src/native-ui/desktop-control/src/renderer-control-session.ts +595 -0
- package/src/native-ui/desktop-control/src/renderer-dom.ts +144 -0
- package/src/native-ui/desktop-control/src/renderer-draft-prewarm-policy.ts +209 -0
- package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +746 -0
- package/src/native-ui/desktop-control/test/cdp-client.test.ts +157 -0
- package/src/native-ui/desktop-control/test/contract-audit.test.ts +56 -0
- package/src/native-ui/desktop-control/test/controller-attachment-server.test.ts +98 -0
- package/src/native-ui/desktop-control/test/index.test.ts +11 -0
- package/src/native-ui/desktop-control/test/main-process-title-policy.test.ts +166 -0
- package/src/native-ui/desktop-control/test/production-controller.test.ts +389 -0
- package/src/native-ui/desktop-control/test/renderer-cdp-control-session.test.ts +205 -0
- package/src/native-ui/desktop-control/test/renderer-control-session.test.ts +451 -0
- package/src/native-ui/desktop-control/test/renderer-dom.test.ts +62 -0
- package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +998 -0
- package/src/native-ui/desktop-control/tsconfig.json +12 -0
- package/src/native-ui/renderer-extension/dist/types/agent-group-preference.d.ts +39 -0
- package/src/native-ui/renderer-extension/dist/types/agent-group-preference.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +83 -0
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/audit-entry.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/audit-entry.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/codex-locale-adapter.d.ts +23 -0
- package/src/native-ui/renderer-extension/dist/types/codex-locale-adapter.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/collaboration-icon.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/collaboration-icon.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/contract-audit.d.ts +37 -0
- package/src/native-ui/renderer-extension/dist/types/contract-audit.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/harness-mix-icons.d.ts +3 -0
- package/src/native-ui/renderer-extension/dist/types/harness-mix-icons.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts +3 -0
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/index.d.ts +49 -0
- package/src/native-ui/renderer-extension/dist/types/index.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/install-renderer-binding.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/install-renderer-binding.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/probe-entry.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/probe-entry.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/production-entry.d.ts +9 -0
- package/src/native-ui/renderer-extension/dist/types/production-entry.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-icon.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-icon.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts +62 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts +70 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-options.d.ts +34 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-options.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts +19 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts +82 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts +21 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-external-steering.d.ts +7 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-external-steering.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-fork-control.d.ts +34 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-fork-control.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-claim.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-claim.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-control.d.ts +15 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-control.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-localization.d.ts +25 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-localization.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts +23 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +94 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-picker-positioning.d.ts +22 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-picker-positioning.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-picker.d.ts +54 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-picker.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts +18 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-picker.d.ts +42 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-picker.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-preference.d.ts +9 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-preference.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-request-sender.d.ts +9 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-request-sender.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-session-import-client.d.ts +12 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-session-import-client.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +18 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts +45 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-transcript-dom.d.ts +23 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-transcript-dom.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-trigger-chip-style.d.ts +3 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-trigger-chip-style.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts +72 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts +24 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts +18 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-usage.d.ts +27 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-usage.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts +42 -0
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/core.d.ts +38 -0
- package/src/native-ui/renderer-extension/dist/types/settings/core.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts +14 -0
- package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +6 -0
- package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts +231 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +22 -0
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/release-notes.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/settings/release-notes.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-list-controls.d.ts +12 -0
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-list-controls.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-page.d.ts +8 -0
- package/src/native-ui/renderer-extension/dist/types/settings/session-import-page.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts +24 -0
- package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/trigger.d.ts +45 -0
- package/src/native-ui/renderer-extension/dist/types/settings/trigger.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/update-request.d.ts +6 -0
- package/src/native-ui/renderer-extension/dist/types/settings/update-request.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -0
- package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts +143 -0
- package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/package.json +22 -0
- package/src/native-ui/renderer-extension/src/agent-group-preference.ts +174 -0
- package/src/native-ui/renderer-extension/src/agent-selection-state.ts +610 -0
- package/src/native-ui/renderer-extension/src/assets/README.md +43 -0
- package/src/native-ui/renderer-extension/src/assets/antigravity-agent.svg +1 -0
- package/src/native-ui/renderer-extension/src/assets/claude-agent.svg +3 -0
- package/src/native-ui/renderer-extension/src/assets/codex-agent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codex-logo-bright.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codex-logo-transparent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codex-logo.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-icon.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-logo-transparent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-logo.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-readme.svg +8 -0
- package/src/native-ui/renderer-extension/src/assets/grok-agent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/kiro-agent.svg +11 -0
- package/src/native-ui/renderer-extension/src/assets/omp-agent.svg +14 -0
- package/src/native-ui/renderer-extension/src/assets/opencode-agent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/pi-agent.svg +4 -0
- package/src/native-ui/renderer-extension/src/assets.d.ts +26 -0
- package/src/native-ui/renderer-extension/src/audit-entry.ts +5 -0
- package/src/native-ui/renderer-extension/src/codex-locale-adapter.ts +321 -0
- package/src/native-ui/renderer-extension/src/collaboration-icon.ts +13 -0
- package/src/native-ui/renderer-extension/src/contract-audit.ts +102 -0
- package/src/native-ui/renderer-extension/src/harness-mix-icons.ts +24 -0
- package/src/native-ui/renderer-extension/src/harness-mix-settings.ts +80 -0
- package/src/native-ui/renderer-extension/src/index.ts +266 -0
- package/src/native-ui/renderer-extension/src/install-renderer-binding.ts +30 -0
- package/src/native-ui/renderer-extension/src/probe-entry.ts +4 -0
- package/src/native-ui/renderer-extension/src/production-entry.ts +23 -0
- package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +145 -0
- package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +813 -0
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +2936 -0
- package/src/native-ui/renderer-extension/src/renderer-codex-account-options.ts +281 -0
- package/src/native-ui/renderer-extension/src/renderer-codex-account-state.ts +53 -0
- package/src/native-ui/renderer-extension/src/renderer-composer-dom.ts +782 -0
- package/src/native-ui/renderer-extension/src/renderer-credits-control.ts +500 -0
- package/src/native-ui/renderer-extension/src/renderer-external-steering.ts +339 -0
- package/src/native-ui/renderer-extension/src/renderer-fork-control.ts +273 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-command-claim.ts +64 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-command-control.ts +417 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-localization.ts +144 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +213 -0
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +567 -0
- package/src/native-ui/renderer-extension/src/renderer-model-picker-positioning.ts +65 -0
- package/src/native-ui/renderer-extension/src/renderer-model-picker.ts +640 -0
- package/src/native-ui/renderer-extension/src/renderer-new-thread-preference.ts +175 -0
- package/src/native-ui/renderer-extension/src/renderer-permission-mode-picker.ts +413 -0
- package/src/native-ui/renderer-extension/src/renderer-permission-mode-preference.ts +49 -0
- package/src/native-ui/renderer-extension/src/renderer-request-sender.ts +43 -0
- package/src/native-ui/renderer-extension/src/renderer-session-import-client.ts +68 -0
- package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +239 -0
- package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +456 -0
- package/src/native-ui/renderer-extension/src/renderer-transcript-dom.ts +47 -0
- package/src/native-ui/renderer-extension/src/renderer-trigger-chip-style.ts +52 -0
- package/src/native-ui/renderer-extension/src/renderer-usage-control.ts +602 -0
- package/src/native-ui/renderer-extension/src/settings/accounts-list.ts +236 -0
- package/src/native-ui/renderer-extension/src/settings/accounts-page.ts +585 -0
- package/src/native-ui/renderer-extension/src/settings/accounts-quota-interactive.prototype.html +1418 -0
- package/src/native-ui/renderer-extension/src/settings/accounts-usage.ts +221 -0
- package/src/native-ui/renderer-extension/src/settings/accounts.css +607 -0
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +1354 -0
- package/src/native-ui/renderer-extension/src/settings/core.ts +158 -0
- package/src/native-ui/renderer-extension/src/settings/harness-accounts.ts +134 -0
- package/src/native-ui/renderer-extension/src/settings/icons.ts +141 -0
- package/src/native-ui/renderer-extension/src/settings/localization.ts +752 -0
- package/src/native-ui/renderer-extension/src/settings/pages.ts +615 -0
- package/src/native-ui/renderer-extension/src/settings/release-notes.ts +222 -0
- package/src/native-ui/renderer-extension/src/settings/session-import-list-controls.ts +128 -0
- package/src/native-ui/renderer-extension/src/settings/session-import-page.ts +461 -0
- package/src/native-ui/renderer-extension/src/settings/shell.css +2149 -0
- package/src/native-ui/renderer-extension/src/settings/shell.ts +329 -0
- package/src/native-ui/renderer-extension/src/settings/trigger.ts +323 -0
- package/src/native-ui/renderer-extension/src/settings/update-notes.preview.html +491 -0
- package/src/native-ui/renderer-extension/src/settings/update-request.ts +49 -0
- package/src/native-ui/renderer-extension/src/settings/windows-update.preview.html +2135 -0
- package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +1278 -0
- package/src/native-ui/renderer-extension/test/agent-selection-state.test.ts +611 -0
- package/src/native-ui/renderer-extension/test/codex-locale-adapter.test.ts +152 -0
- package/src/native-ui/renderer-extension/test/contract-audit.test.ts +71 -0
- package/src/native-ui/renderer-extension/test/index.test.ts +9 -0
- package/src/native-ui/renderer-extension/test/renderer-agent-icon.test.ts +98 -0
- package/src/native-ui/renderer-extension/test/renderer-agent-picker.test.ts +226 -0
- package/src/native-ui/renderer-extension/test/renderer-binding-probe-host-catalog.test.ts +728 -0
- package/src/native-ui/renderer-extension/test/renderer-binding-probe.test.ts +1290 -0
- package/src/native-ui/renderer-extension/test/renderer-codex-account-state.test.ts +65 -0
- package/src/native-ui/renderer-extension/test/renderer-composer-permissions.test.ts +75 -0
- package/src/native-ui/renderer-extension/test/renderer-credits-control.test.ts +70 -0
- package/src/native-ui/renderer-extension/test/renderer-external-steering-rpc.test.ts +184 -0
- package/src/native-ui/renderer-extension/test/renderer-external-steering.test.ts +279 -0
- package/src/native-ui/renderer-extension/test/renderer-fork-control.test.ts +363 -0
- package/src/native-ui/renderer-extension/test/renderer-harness-command-claim.test.ts +167 -0
- package/src/native-ui/renderer-extension/test/renderer-harness-command-control.test.ts +54 -0
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +708 -0
- package/src/native-ui/renderer-extension/test/renderer-model-picker.test.ts +302 -0
- package/src/native-ui/renderer-extension/test/renderer-native-thread-ownership.test.ts +84 -0
- package/src/native-ui/renderer-extension/test/renderer-new-thread-preference.test.ts +81 -0
- package/src/native-ui/renderer-extension/test/renderer-permission-mode-picker.test.ts +269 -0
- package/src/native-ui/renderer-extension/test/renderer-permission-mode-preference.test.ts +48 -0
- package/src/native-ui/renderer-extension/test/renderer-settings-lifecycle.test.ts +156 -0
- package/src/native-ui/renderer-extension/test/renderer-sidebar-agent-icons.test.ts +575 -0
- package/src/native-ui/renderer-extension/test/renderer-unsupported-methods.test.ts +167 -0
- package/src/native-ui/renderer-extension/test/renderer-usage-control.test.ts +96 -0
- package/src/native-ui/renderer-extension/test/settings/accounts-usage.test.ts +208 -0
- package/src/native-ui/renderer-extension/test/settings/core.test.ts +145 -0
- package/src/native-ui/renderer-extension/test/settings/harness-settings.test.ts +88 -0
- package/src/native-ui/renderer-extension/test/settings/localization.test.ts +80 -0
- package/src/native-ui/renderer-extension/test/settings/pages.test.ts +2000 -0
- package/src/native-ui/renderer-extension/test/settings/release-notes.test.ts +163 -0
- package/src/native-ui/renderer-extension/test/settings/shell.test.ts +74 -0
- package/src/native-ui/renderer-extension/test/settings/trigger.test.ts +270 -0
- package/src/native-ui/renderer-extension/test/settings/update-request.test.ts +48 -0
- package/src/native-ui/renderer-extension/test/versioned-renderer-adapter.test.ts +682 -0
- package/src/native-ui/renderer-extension/tsconfig.json +15 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.d.ts +246 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.js +99 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/deepseek-modern-sessions.d.ts +37 -0
- package/src/native-ui/shared-contracts/dist/deepseek-modern-sessions.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/deepseek-modern-sessions.js +36 -0
- package/src/native-ui/shared-contracts/dist/deepseek-modern-sessions.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/errors.d.ts +17 -0
- package/src/native-ui/shared-contracts/dist/errors.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/errors.js +14 -0
- package/src/native-ui/shared-contracts/dist/errors.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/external-thread-fork.d.ts +11 -0
- package/src/native-ui/shared-contracts/dist/external-thread-fork.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/external-thread-fork.js +14 -0
- package/src/native-ui/shared-contracts/dist/external-thread-fork.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-accounts.d.ts +64 -0
- package/src/native-ui/shared-contracts/dist/harness-accounts.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-accounts.js +24 -0
- package/src/native-ui/shared-contracts/dist/harness-accounts.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-commands.d.ts +46 -0
- package/src/native-ui/shared-contracts/dist/harness-commands.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-commands.js +61 -0
- package/src/native-ui/shared-contracts/dist/harness-commands.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-models.d.ts +278 -0
- package/src/native-ui/shared-contracts/dist/harness-models.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-models.js +317 -0
- package/src/native-ui/shared-contracts/dist/harness-models.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-permission-modes.d.ts +30 -0
- package/src/native-ui/shared-contracts/dist/harness-permission-modes.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-permission-modes.js +57 -0
- package/src/native-ui/shared-contracts/dist/harness-permission-modes.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-plugins.d.ts +56 -0
- package/src/native-ui/shared-contracts/dist/harness-plugins.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-plugins.js +75 -0
- package/src/native-ui/shared-contracts/dist/harness-plugins.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-route.d.ts +16 -0
- package/src/native-ui/shared-contracts/dist/harness-route.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-route.js +42 -0
- package/src/native-ui/shared-contracts/dist/harness-route.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-session-import.d.ts +54 -0
- package/src/native-ui/shared-contracts/dist/harness-session-import.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/harness-session-import.js +72 -0
- package/src/native-ui/shared-contracts/dist/harness-session-import.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/ids.d.ts +12 -0
- package/src/native-ui/shared-contracts/dist/ids.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/ids.js +10 -0
- package/src/native-ui/shared-contracts/dist/ids.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/index.d.ts +47 -0
- package/src/native-ui/shared-contracts/dist/index.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/index.js +29 -0
- package/src/native-ui/shared-contracts/dist/index.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/json-rpc.d.ts +84 -0
- package/src/native-ui/shared-contracts/dist/json-rpc.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/json-rpc.js +65 -0
- package/src/native-ui/shared-contracts/dist/json-rpc.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/json-value.d.ts +13 -0
- package/src/native-ui/shared-contracts/dist/json-value.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/json-value.js +60 -0
- package/src/native-ui/shared-contracts/dist/json-value.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/native-refs.d.ts +43 -0
- package/src/native-ui/shared-contracts/dist/native-refs.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/native-refs.js +35 -0
- package/src/native-ui/shared-contracts/dist/native-refs.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/reasoning-transcript.d.ts +7 -0
- package/src/native-ui/shared-contracts/dist/reasoning-transcript.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/reasoning-transcript.js +7 -0
- package/src/native-ui/shared-contracts/dist/reasoning-transcript.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/thread-delegate.d.ts +22 -0
- package/src/native-ui/shared-contracts/dist/thread-delegate.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/thread-delegate.js +30 -0
- package/src/native-ui/shared-contracts/dist/thread-delegate.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts +16 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js +19 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/thread-usage.d.ts +106 -0
- package/src/native-ui/shared-contracts/dist/thread-usage.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/thread-usage.js +103 -0
- package/src/native-ui/shared-contracts/dist/thread-usage.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -0
- package/src/native-ui/shared-contracts/dist/updates.d.ts +128 -0
- package/src/native-ui/shared-contracts/dist/updates.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/updates.js +58 -0
- package/src/native-ui/shared-contracts/dist/updates.js.map +1 -0
- package/src/native-ui/shared-contracts/dist/version.d.ts +2 -0
- package/src/native-ui/shared-contracts/dist/version.d.ts.map +1 -0
- package/src/native-ui/shared-contracts/dist/version.js +2 -0
- package/src/native-ui/shared-contracts/dist/version.js.map +1 -0
- package/src/native-ui/shared-contracts/package.json +25 -0
- package/src/native-ui/shared-contracts/src/codex-accounts.ts +140 -0
- package/src/native-ui/shared-contracts/src/deepseek-modern-sessions.ts +67 -0
- package/src/native-ui/shared-contracts/src/errors.ts +25 -0
- package/src/native-ui/shared-contracts/src/external-thread-fork.ts +20 -0
- package/src/native-ui/shared-contracts/src/harness-accounts.ts +29 -0
- package/src/native-ui/shared-contracts/src/harness-commands.ts +81 -0
- package/src/native-ui/shared-contracts/src/harness-models.ts +401 -0
- package/src/native-ui/shared-contracts/src/harness-permission-modes.ts +73 -0
- package/src/native-ui/shared-contracts/src/harness-plugins.ts +96 -0
- package/src/native-ui/shared-contracts/src/harness-route.ts +47 -0
- package/src/native-ui/shared-contracts/src/harness-session-import.ts +90 -0
- package/src/native-ui/shared-contracts/src/ids.ts +20 -0
- package/src/native-ui/shared-contracts/src/index.ts +320 -0
- package/src/native-ui/shared-contracts/src/json-rpc.ts +80 -0
- package/src/native-ui/shared-contracts/src/json-value.ts +78 -0
- package/src/native-ui/shared-contracts/src/native-refs.ts +58 -0
- package/src/native-ui/shared-contracts/src/reasoning-transcript.ts +6 -0
- package/src/native-ui/shared-contracts/src/thread-delegate.ts +40 -0
- package/src/native-ui/shared-contracts/src/thread-harness-switch.ts +25 -0
- package/src/native-ui/shared-contracts/src/thread-usage.ts +126 -0
- package/src/native-ui/shared-contracts/src/updates.ts +78 -0
- package/src/native-ui/shared-contracts/src/version.ts +1 -0
- package/src/native-ui/shared-contracts/test/browser-bundle.test.ts +36 -0
- package/src/native-ui/shared-contracts/test/deepseek-modern-sessions.test.ts +120 -0
- package/src/native-ui/shared-contracts/test/errors.test.ts +72 -0
- package/src/native-ui/shared-contracts/test/harness-commands.test.ts +64 -0
- package/src/native-ui/shared-contracts/test/harness-models.test.ts +390 -0
- package/src/native-ui/shared-contracts/test/harness-permission-modes.test.ts +134 -0
- package/src/native-ui/shared-contracts/test/harness-plugins.test.ts +113 -0
- package/src/native-ui/shared-contracts/test/harness-session-import.test.ts +117 -0
- package/src/native-ui/shared-contracts/test/ids.test.ts +69 -0
- package/src/native-ui/shared-contracts/test/index.test.ts +80 -0
- package/src/native-ui/shared-contracts/test/json-rpc.test.ts +177 -0
- package/src/native-ui/shared-contracts/test/json-value.test.ts +62 -0
- package/src/native-ui/shared-contracts/test/native-refs.test.ts +95 -0
- package/src/native-ui/shared-contracts/test/thread-usage.test.ts +115 -0
- package/src/native-ui/shared-contracts/test/updates.test.ts +120 -0
- package/src/native-ui/shared-contracts/tsconfig.json +10 -0
- package/src/tsconfig.base.json +19 -0
|
@@ -0,0 +1,1460 @@
|
|
|
1
|
+
// src/native-ui/desktop-control/src/production-controller.ts
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
// src/native-ui/desktop-control/src/controller-attachment-server.ts
|
|
6
|
+
import { createServer } from "node:net";
|
|
7
|
+
var MAX_REQUEST_BYTES = 96;
|
|
8
|
+
function validPort(value) {
|
|
9
|
+
return Number.isInteger(value) && value >= 1 && value <= 65535;
|
|
10
|
+
}
|
|
11
|
+
function validNonce(value) {
|
|
12
|
+
return /^[0-9a-f]{32}$/.test(value);
|
|
13
|
+
}
|
|
14
|
+
function closeServer(server) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
server.close((error) => {
|
|
17
|
+
if (error) reject(error);
|
|
18
|
+
else resolve();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function respond(socket, value) {
|
|
23
|
+
socket.end(`${value}
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
async function startControllerAttachmentServer(options) {
|
|
27
|
+
if (!validPort(options.port)) throw new Error("attachment port must be a valid TCP port");
|
|
28
|
+
if (!validNonce(options.nonce)) {
|
|
29
|
+
throw new Error("attachment nonce must be 32 lowercase hexadecimal characters");
|
|
30
|
+
}
|
|
31
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
32
|
+
const server = createServer((socket) => {
|
|
33
|
+
sockets.add(socket);
|
|
34
|
+
socket.once("close", () => sockets.delete(socket));
|
|
35
|
+
socket.setEncoding("utf8");
|
|
36
|
+
socket.setTimeout(5e3, () => socket.destroy());
|
|
37
|
+
let request = "";
|
|
38
|
+
let handled = false;
|
|
39
|
+
socket.on("data", (chunk) => {
|
|
40
|
+
if (handled) return;
|
|
41
|
+
request += chunk;
|
|
42
|
+
if (request.length > MAX_REQUEST_BYTES) {
|
|
43
|
+
handled = true;
|
|
44
|
+
respond(socket, "rejected");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const newline = request.indexOf("\n");
|
|
48
|
+
if (newline < 0) return;
|
|
49
|
+
handled = true;
|
|
50
|
+
const line = request.slice(0, newline).replace(/\r$/, "");
|
|
51
|
+
if (line === `ATTACH ${options.nonce}`) {
|
|
52
|
+
void options.attach().then(
|
|
53
|
+
() => respond(socket, "ready"),
|
|
54
|
+
() => respond(socket, "failed")
|
|
55
|
+
);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
respond(socket, "rejected");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
await new Promise((resolve, reject) => {
|
|
62
|
+
const onError = (error) => reject(error);
|
|
63
|
+
server.once("error", onError);
|
|
64
|
+
server.listen(options.port, "127.0.0.1", () => {
|
|
65
|
+
server.off("error", onError);
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
let closed = false;
|
|
70
|
+
return {
|
|
71
|
+
async close() {
|
|
72
|
+
if (closed) return;
|
|
73
|
+
closed = true;
|
|
74
|
+
for (const socket of sockets) socket.destroy();
|
|
75
|
+
await closeServer(server);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/native-ui/desktop-control/src/cdp-client.ts
|
|
81
|
+
function isRecord(value) {
|
|
82
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
}
|
|
84
|
+
function nonEmptyString(value, field) {
|
|
85
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
86
|
+
throw new Error(`CDP target '${field}' must be non-empty text`);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
function loopbackUrl(value, protocols) {
|
|
91
|
+
const url = new URL(value);
|
|
92
|
+
if (!protocols.includes(url.protocol)) {
|
|
93
|
+
throw new Error(`CDP endpoint must use ${protocols.join(" or ")}`);
|
|
94
|
+
}
|
|
95
|
+
if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)) {
|
|
96
|
+
throw new Error("CDP endpoint must use a loopback host");
|
|
97
|
+
}
|
|
98
|
+
return url;
|
|
99
|
+
}
|
|
100
|
+
function parseTarget(value) {
|
|
101
|
+
if (!isRecord(value)) throw new Error("CDP target must be an object");
|
|
102
|
+
const target = {
|
|
103
|
+
id: nonEmptyString(value.id, "id"),
|
|
104
|
+
type: nonEmptyString(value.type, "type"),
|
|
105
|
+
title: typeof value.title === "string" ? value.title : "",
|
|
106
|
+
url: nonEmptyString(value.url, "url"),
|
|
107
|
+
webSocketDebuggerUrl: nonEmptyString(value.webSocketDebuggerUrl, "webSocketDebuggerUrl")
|
|
108
|
+
};
|
|
109
|
+
loopbackUrl(target.webSocketDebuggerUrl, ["ws:", "wss:"]);
|
|
110
|
+
return target;
|
|
111
|
+
}
|
|
112
|
+
function defaultFetch(url) {
|
|
113
|
+
return fetch(url);
|
|
114
|
+
}
|
|
115
|
+
function defaultSocketFactory(url) {
|
|
116
|
+
return new WebSocket(url);
|
|
117
|
+
}
|
|
118
|
+
function messageText(value) {
|
|
119
|
+
if (typeof value === "string") return value;
|
|
120
|
+
if (value instanceof ArrayBuffer) return new TextDecoder().decode(value);
|
|
121
|
+
if (ArrayBuffer.isView(value)) {
|
|
122
|
+
return new TextDecoder().decode(
|
|
123
|
+
new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
throw new Error("CDP WebSocket returned a non-text message");
|
|
127
|
+
}
|
|
128
|
+
function parseResponse(value) {
|
|
129
|
+
if (!isRecord(value) || typeof value.id !== "number") return null;
|
|
130
|
+
const response = { id: value.id };
|
|
131
|
+
if ("result" in value) response.result = value.result;
|
|
132
|
+
if (isRecord(value.error)) {
|
|
133
|
+
response.error = {
|
|
134
|
+
...typeof value.error.code === "number" ? { code: value.error.code } : {},
|
|
135
|
+
...typeof value.error.message === "string" ? { message: value.error.message } : {}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return response;
|
|
139
|
+
}
|
|
140
|
+
async function listCdpTargets(endpoint, fetchImpl = defaultFetch) {
|
|
141
|
+
const baseUrl = loopbackUrl(endpoint, ["http:", "https:"]);
|
|
142
|
+
const targetsUrl = new URL("/json/list", baseUrl).toString();
|
|
143
|
+
const response = await fetchImpl(targetsUrl);
|
|
144
|
+
if (!response.ok) throw new Error(`CDP target discovery failed with HTTP ${response.status}`);
|
|
145
|
+
const value = await response.json();
|
|
146
|
+
if (!Array.isArray(value)) throw new Error("CDP target discovery did not return an array");
|
|
147
|
+
return value.map(parseTarget);
|
|
148
|
+
}
|
|
149
|
+
var CdpClient = class _CdpClient {
|
|
150
|
+
#commandTimeoutMs;
|
|
151
|
+
#socket;
|
|
152
|
+
#closed = false;
|
|
153
|
+
#nextId = 1;
|
|
154
|
+
#pending = /* @__PURE__ */ new Map();
|
|
155
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
156
|
+
constructor(socket, commandTimeoutMs) {
|
|
157
|
+
this.#socket = socket;
|
|
158
|
+
this.#commandTimeoutMs = commandTimeoutMs;
|
|
159
|
+
socket.addEventListener("message", (event) => this.#onMessage(event));
|
|
160
|
+
socket.addEventListener("error", () => this.#fail(new Error("CDP WebSocket failed")));
|
|
161
|
+
socket.addEventListener("close", () => this.#fail(new Error("CDP WebSocket closed")));
|
|
162
|
+
}
|
|
163
|
+
static async connect(url, options = {}) {
|
|
164
|
+
loopbackUrl(url, ["ws:", "wss:"]);
|
|
165
|
+
const socketFactory = options.socketFactory ?? defaultSocketFactory;
|
|
166
|
+
const socket = socketFactory(url);
|
|
167
|
+
const connectTimeoutMs = options.connectTimeoutMs ?? 1e4;
|
|
168
|
+
await new Promise((resolve, reject) => {
|
|
169
|
+
const timeout = setTimeout(() => {
|
|
170
|
+
cleanup();
|
|
171
|
+
socket.close();
|
|
172
|
+
reject(new Error("CDP WebSocket connection timed out"));
|
|
173
|
+
}, connectTimeoutMs);
|
|
174
|
+
const onOpen = () => {
|
|
175
|
+
cleanup();
|
|
176
|
+
resolve();
|
|
177
|
+
};
|
|
178
|
+
const onError = () => {
|
|
179
|
+
cleanup();
|
|
180
|
+
reject(new Error("CDP WebSocket connection failed"));
|
|
181
|
+
};
|
|
182
|
+
const cleanup = () => {
|
|
183
|
+
clearTimeout(timeout);
|
|
184
|
+
socket.removeEventListener("open", onOpen);
|
|
185
|
+
socket.removeEventListener("error", onError);
|
|
186
|
+
};
|
|
187
|
+
socket.addEventListener("open", onOpen);
|
|
188
|
+
socket.addEventListener("error", onError);
|
|
189
|
+
});
|
|
190
|
+
return new _CdpClient(socket, options.commandTimeoutMs ?? 1e4);
|
|
191
|
+
}
|
|
192
|
+
command(method, params = {}) {
|
|
193
|
+
return this.#send(method, params);
|
|
194
|
+
}
|
|
195
|
+
sessionCommand(sessionId, method, params = {}) {
|
|
196
|
+
if (sessionId.length === 0) return Promise.reject(new Error("CDP session ID is required"));
|
|
197
|
+
return this.#send(method, params, sessionId);
|
|
198
|
+
}
|
|
199
|
+
on(method, listener) {
|
|
200
|
+
const listeners = this.#listeners.get(method) ?? /* @__PURE__ */ new Set();
|
|
201
|
+
listeners.add(listener);
|
|
202
|
+
this.#listeners.set(method, listeners);
|
|
203
|
+
return () => {
|
|
204
|
+
listeners.delete(listener);
|
|
205
|
+
if (listeners.size === 0) this.#listeners.delete(method);
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
#send(method, params, sessionId) {
|
|
209
|
+
if (this.#closed) return Promise.reject(new Error("CDP client is closed"));
|
|
210
|
+
const id = this.#nextId++;
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
const timeout = setTimeout(() => {
|
|
213
|
+
this.#pending.delete(id);
|
|
214
|
+
reject(new Error(`CDP command '${method}' timed out`));
|
|
215
|
+
}, this.#commandTimeoutMs);
|
|
216
|
+
this.#pending.set(id, { resolve, reject, timeout });
|
|
217
|
+
try {
|
|
218
|
+
this.#socket.send(
|
|
219
|
+
JSON.stringify({ id, method, params, ...sessionId ? { sessionId } : {} })
|
|
220
|
+
);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
clearTimeout(timeout);
|
|
223
|
+
this.#pending.delete(id);
|
|
224
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async evaluate(expression) {
|
|
229
|
+
const response = await this.command("Runtime.evaluate", {
|
|
230
|
+
expression,
|
|
231
|
+
awaitPromise: true,
|
|
232
|
+
returnByValue: true
|
|
233
|
+
});
|
|
234
|
+
if (!isRecord(response)) throw new Error("Runtime.evaluate returned an invalid result");
|
|
235
|
+
if (isRecord(response.exceptionDetails)) {
|
|
236
|
+
const text = typeof response.exceptionDetails.text === "string" ? response.exceptionDetails.text : "Renderer evaluation failed";
|
|
237
|
+
throw new Error(text);
|
|
238
|
+
}
|
|
239
|
+
if (!isRecord(response.result) || !("value" in response.result)) {
|
|
240
|
+
throw new Error("Runtime.evaluate did not return a value");
|
|
241
|
+
}
|
|
242
|
+
return response.result.value;
|
|
243
|
+
}
|
|
244
|
+
close() {
|
|
245
|
+
if (this.#closed) return;
|
|
246
|
+
this.#closed = true;
|
|
247
|
+
this.#fail(new Error("CDP client closed"));
|
|
248
|
+
this.#listeners.clear();
|
|
249
|
+
this.#socket.close();
|
|
250
|
+
}
|
|
251
|
+
#onMessage(event) {
|
|
252
|
+
try {
|
|
253
|
+
const value = JSON.parse(messageText(event.data));
|
|
254
|
+
const response = parseResponse(value);
|
|
255
|
+
if (!response) {
|
|
256
|
+
if (!isRecord(value) || typeof value.method !== "string") return;
|
|
257
|
+
const sessionId = typeof value.sessionId === "string" ? value.sessionId : void 0;
|
|
258
|
+
for (const listener of this.#listeners.get(value.method) ?? []) {
|
|
259
|
+
listener(value.params, sessionId);
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const pending = this.#pending.get(response.id);
|
|
264
|
+
if (!pending) return;
|
|
265
|
+
clearTimeout(pending.timeout);
|
|
266
|
+
this.#pending.delete(response.id);
|
|
267
|
+
if (response.error) {
|
|
268
|
+
const code = response.error.code === void 0 ? "unknown" : String(response.error.code);
|
|
269
|
+
pending.reject(
|
|
270
|
+
new Error(response.error.message ?? `CDP command failed with error ${code}`)
|
|
271
|
+
);
|
|
272
|
+
} else {
|
|
273
|
+
pending.resolve(response.result);
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
this.#fail(error instanceof Error ? error : new Error(String(error)));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
#fail(error) {
|
|
280
|
+
for (const pending of this.#pending.values()) {
|
|
281
|
+
clearTimeout(pending.timeout);
|
|
282
|
+
pending.reject(error);
|
|
283
|
+
}
|
|
284
|
+
this.#pending.clear();
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts
|
|
289
|
+
function installDraftPrewarmPolicyBridge(manager, bridge, hostId, target, prewarmedThreadManager) {
|
|
290
|
+
const existing = target.__codexhostDraftPrewarmPolicyV1;
|
|
291
|
+
if (existing?.owns?.length === 4 && existing.owns(manager, bridge, hostId, prewarmedThreadManager) === true) {
|
|
292
|
+
return { state: "ready", reason: "owned-request-bridge" };
|
|
293
|
+
}
|
|
294
|
+
existing?.dispose?.();
|
|
295
|
+
const originalSend = bridge.sendRequest;
|
|
296
|
+
const originalPrewarm = bridge.prewarmThreadStart;
|
|
297
|
+
const originalOnNotification = manager.onNotification;
|
|
298
|
+
const originalDispatchAppServerResponse = manager.dispatchAppServerResponse;
|
|
299
|
+
let selectedModel = null;
|
|
300
|
+
let selectedCodexAccountId = null;
|
|
301
|
+
const isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
302
|
+
const isRemoteControlHost = hostId.startsWith("remote-control:");
|
|
303
|
+
const knownExternalThreadIds = /* @__PURE__ */ new Set();
|
|
304
|
+
const knownOfficialThreadIds = /* @__PURE__ */ new Set();
|
|
305
|
+
const threadOwnershipResolutions = /* @__PURE__ */ new Map();
|
|
306
|
+
const createBridgeProcessHandle = () => `codexhost-${typeof crypto?.randomUUID === "function" ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}`}`;
|
|
307
|
+
let bridgeProcessHandle = createBridgeProcessHandle();
|
|
308
|
+
const bridgeReadyMethod = "codexhost/remote-control-bridge/ready";
|
|
309
|
+
const bridgeRequests = /* @__PURE__ */ new Map();
|
|
310
|
+
const bridgeServerRequestIdPrefix = "codexhost/remote-control-bridge/server-request/";
|
|
311
|
+
const bridgeServerRequests = /* @__PURE__ */ new Map();
|
|
312
|
+
let nextBridgeServerRequestOrdinal = 1;
|
|
313
|
+
let outputDecoders = {
|
|
314
|
+
stdout: new TextDecoder(),
|
|
315
|
+
stderr: new TextDecoder()
|
|
316
|
+
};
|
|
317
|
+
const outputBuffers = { stdout: "", stderr: "" };
|
|
318
|
+
let bridgeState = "idle";
|
|
319
|
+
let bridgeReadyPromise = null;
|
|
320
|
+
let bridgeReadyResolve = null;
|
|
321
|
+
let bridgeReadyReject = null;
|
|
322
|
+
let bridgeReadyTimeout = null;
|
|
323
|
+
let bridgeInitialization = null;
|
|
324
|
+
let writeTail = Promise.resolve();
|
|
325
|
+
const transportError = (message, cause) => {
|
|
326
|
+
const error = new Error(`codexhost Remote Control bridge: ${message}`);
|
|
327
|
+
if (cause !== void 0) Object.assign(error, { cause });
|
|
328
|
+
return error;
|
|
329
|
+
};
|
|
330
|
+
const utf8Base64 = (value) => {
|
|
331
|
+
const bytes = new TextEncoder().encode(value);
|
|
332
|
+
let binary = "";
|
|
333
|
+
for (let index = 0; index < bytes.length; index += 32768) {
|
|
334
|
+
binary += String.fromCharCode(...bytes.subarray(index, index + 32768));
|
|
335
|
+
}
|
|
336
|
+
return btoa(binary);
|
|
337
|
+
};
|
|
338
|
+
const utf16LeBase64 = (value) => {
|
|
339
|
+
let binary = "";
|
|
340
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
341
|
+
const codeUnit = value.charCodeAt(index);
|
|
342
|
+
binary += String.fromCharCode(codeUnit & 255, codeUnit >>> 8);
|
|
343
|
+
}
|
|
344
|
+
return btoa(binary);
|
|
345
|
+
};
|
|
346
|
+
const failBridge = (cause, terminateProcess = true) => {
|
|
347
|
+
if (bridgeState === "failed" || bridgeState === "disposed") return;
|
|
348
|
+
const failedProcessHandle = bridgeProcessHandle;
|
|
349
|
+
const terminate = terminateProcess && (bridgeState === "starting" || bridgeState === "ready");
|
|
350
|
+
bridgeState = "failed";
|
|
351
|
+
if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
|
|
352
|
+
bridgeReadyTimeout = null;
|
|
353
|
+
const stderr = outputBuffers.stderr.trim().slice(-1e3);
|
|
354
|
+
const error = transportError(
|
|
355
|
+
`${cause instanceof Error ? cause.message : String(cause)}${stderr ? `; ${stderr}` : ""}`,
|
|
356
|
+
cause
|
|
357
|
+
);
|
|
358
|
+
bridgeReadyReject?.(error);
|
|
359
|
+
bridgeReadyReject = null;
|
|
360
|
+
bridgeReadyResolve = null;
|
|
361
|
+
for (const requestId of bridgeRequests.keys()) bridge.onError(requestId, error);
|
|
362
|
+
bridgeRequests.clear();
|
|
363
|
+
if (isRemoteControlHost && terminate) {
|
|
364
|
+
void Promise.resolve(
|
|
365
|
+
originalSend.call(bridge, "process/kill", { processHandle: failedProcessHandle })
|
|
366
|
+
).catch(() => void 0);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
const resetFailedBridge = () => {
|
|
370
|
+
if (bridgeState !== "failed") return;
|
|
371
|
+
bridgeProcessHandle = createBridgeProcessHandle();
|
|
372
|
+
outputDecoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
|
373
|
+
outputBuffers.stdout = "";
|
|
374
|
+
outputBuffers.stderr = "";
|
|
375
|
+
bridgeReadyPromise = null;
|
|
376
|
+
bridgeReadyResolve = null;
|
|
377
|
+
bridgeReadyReject = null;
|
|
378
|
+
bridgeInitialization = null;
|
|
379
|
+
writeTail = Promise.resolve();
|
|
380
|
+
bridgeServerRequests.clear();
|
|
381
|
+
nextBridgeServerRequestOrdinal = 1;
|
|
382
|
+
bridgeState = "idle";
|
|
383
|
+
};
|
|
384
|
+
const rememberExternalThread = (value) => {
|
|
385
|
+
if (!isRecord4(value) || typeof value.id !== "string") return;
|
|
386
|
+
if (value.modelProvider === "codexhost" || value.cliVersion === "codexhost") {
|
|
387
|
+
knownExternalThreadIds.add(value.id);
|
|
388
|
+
knownOfficialThreadIds.delete(value.id);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
const observeBridgeResult = (request, result) => {
|
|
392
|
+
if (!request || !isRecord4(result)) return;
|
|
393
|
+
if (request.method === "thread/list" && Array.isArray(result.data)) {
|
|
394
|
+
for (const thread of result.data) rememberExternalThread(thread);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (request.method === "thread/start" || request.method === "thread/read" || request.method === "thread/resume") {
|
|
398
|
+
rememberExternalThread(result.thread);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (request.method === "codexhost/thread/inspect" && isRecord4(request.parameters) && typeof request.parameters.threadId === "string") {
|
|
402
|
+
if (result.owner === "external") {
|
|
403
|
+
knownExternalThreadIds.add(request.parameters.threadId);
|
|
404
|
+
knownOfficialThreadIds.delete(request.parameters.threadId);
|
|
405
|
+
} else if (result.owner === "codex") {
|
|
406
|
+
knownOfficialThreadIds.add(request.parameters.threadId);
|
|
407
|
+
knownExternalThreadIds.delete(request.parameters.threadId);
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (request.method === "thread/delete" && isRecord4(request.parameters) && typeof request.parameters.threadId === "string") {
|
|
412
|
+
knownExternalThreadIds.delete(request.parameters.threadId);
|
|
413
|
+
knownOfficialThreadIds.delete(request.parameters.threadId);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const handleBridgeFrame = (value) => {
|
|
417
|
+
if (!isRecord4(value)) {
|
|
418
|
+
failBridge("received a non-object app-server frame");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (value.method === bridgeReadyMethod && value.id === void 0) {
|
|
422
|
+
if (isRecord4(value.params) && value.params.protocolVersion === 1) {
|
|
423
|
+
bridgeState = "ready";
|
|
424
|
+
if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
|
|
425
|
+
bridgeReadyTimeout = null;
|
|
426
|
+
bridgeReadyResolve?.();
|
|
427
|
+
bridgeReadyResolve = null;
|
|
428
|
+
bridgeReadyReject = null;
|
|
429
|
+
} else {
|
|
430
|
+
failBridge("reported an unsupported protocol version");
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (typeof value.method === "string" && value.id === void 0) {
|
|
435
|
+
if (value.method === "thread/started" && isRecord4(value.params)) {
|
|
436
|
+
rememberExternalThread(value.params.thread);
|
|
437
|
+
}
|
|
438
|
+
originalOnNotification.call(manager, value.method, value.params);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (typeof value.method === "string" && value.id !== void 0) {
|
|
442
|
+
const outerRequestId = `${bridgeServerRequestIdPrefix}${bridgeProcessHandle}/${nextBridgeServerRequestOrdinal}`;
|
|
443
|
+
nextBridgeServerRequestOrdinal += 1;
|
|
444
|
+
bridgeServerRequests.set(outerRequestId, value.id);
|
|
445
|
+
manager.onRequest({ ...value, id: outerRequestId });
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (value.id === void 0) {
|
|
449
|
+
failBridge("received an app-server response without an id");
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const request = bridgeRequests.get(value.id);
|
|
453
|
+
bridgeRequests.delete(value.id);
|
|
454
|
+
if ("error" in value) {
|
|
455
|
+
bridge.onError(value.id, value.error, value.metrics);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
observeBridgeResult(request, value.result);
|
|
459
|
+
bridge.onResult(value.id, value.result, value.metrics);
|
|
460
|
+
};
|
|
461
|
+
const consumeBridgeOutput = (stream, base64) => {
|
|
462
|
+
const binary = atob(base64);
|
|
463
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
464
|
+
outputBuffers[stream] += outputDecoders[stream].decode(bytes, { stream: true });
|
|
465
|
+
if (stream === "stderr") {
|
|
466
|
+
outputBuffers.stderr = outputBuffers.stderr.slice(-4e3);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
while (true) {
|
|
470
|
+
const newline = outputBuffers.stdout.indexOf("\n");
|
|
471
|
+
if (newline < 0) break;
|
|
472
|
+
const line = outputBuffers.stdout.slice(0, newline).trim();
|
|
473
|
+
outputBuffers.stdout = outputBuffers.stdout.slice(newline + 1);
|
|
474
|
+
if (!line) continue;
|
|
475
|
+
try {
|
|
476
|
+
handleBridgeFrame(JSON.parse(line));
|
|
477
|
+
} catch (error) {
|
|
478
|
+
failBridge(
|
|
479
|
+
`emitted invalid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
480
|
+
);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const handleOuterNotification = (method, parameters) => {
|
|
486
|
+
if (method === "process/exited" && isRecord4(parameters) && parameters.processHandle === bridgeProcessHandle) {
|
|
487
|
+
const exitCode = typeof parameters.exitCode === "number" ? parameters.exitCode : "unknown";
|
|
488
|
+
const stderr = typeof parameters.stderr === "string" ? parameters.stderr.trim() : "";
|
|
489
|
+
failBridge(
|
|
490
|
+
`process exited unexpectedly with code ${exitCode}${stderr ? `: ${stderr}` : ""}`,
|
|
491
|
+
false
|
|
492
|
+
);
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
if (method !== "process/outputDelta" || !isRecord4(parameters) || parameters.processHandle !== bridgeProcessHandle || parameters.stream !== "stdout" && parameters.stream !== "stderr" || typeof parameters.deltaBase64 !== "string") {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
if (parameters.capReached === true) {
|
|
499
|
+
failBridge(`${parameters.stream} was truncated`);
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
consumeBridgeOutput(parameters.stream, parameters.deltaBase64);
|
|
503
|
+
return true;
|
|
504
|
+
};
|
|
505
|
+
const startBridge = () => {
|
|
506
|
+
if (!isRemoteControlHost) return Promise.resolve();
|
|
507
|
+
if (bridgeReadyPromise) return bridgeReadyPromise;
|
|
508
|
+
bridgeState = "starting";
|
|
509
|
+
bridgeReadyPromise = new Promise((resolve, reject) => {
|
|
510
|
+
bridgeReadyResolve = resolve;
|
|
511
|
+
bridgeReadyReject = reject;
|
|
512
|
+
});
|
|
513
|
+
bridgeReadyTimeout = globalThis.setTimeout(
|
|
514
|
+
() => failBridge("startup timed out after 15000ms"),
|
|
515
|
+
15e3
|
|
516
|
+
);
|
|
517
|
+
const powershellScript = "$ErrorActionPreference = 'Stop'; $descriptorPath = Join-Path $env:LOCALAPPDATA 'codexhost\\remote-control-bridge-v1.json'; $descriptor = Get-Content -LiteralPath $descriptorPath -Raw | ConvertFrom-Json; if ($descriptor.schemaVersion -ne 1) { throw 'Unsupported codexhost Remote Control descriptor' }; $nodePath = [string]$descriptor.nodePath; $runtimePath = [string]$descriptor.runtimePath; if (-not [System.IO.Path]::IsPathRooted($nodePath) -or -not [System.IO.Path]::IsPathRooted($runtimePath)) { throw 'Invalid codexhost Remote Control runtime path' }; $owner = Get-Process -Id ([int]$descriptor.ownerPid) -ErrorAction SilentlyContinue; if ($null -eq $owner) { throw 'CodexHost Remote Control runtime is not running' }; $env:CODEXHOST_REMOTE_CONTROL_BRIDGE_PIPE = [string]$descriptor.pipePath; & $nodePath $runtimePath '--codexhost-remote-control-bridge'; exit $LASTEXITCODE";
|
|
518
|
+
const command = [
|
|
519
|
+
"powershell.exe",
|
|
520
|
+
"-NoLogo",
|
|
521
|
+
"-NoProfile",
|
|
522
|
+
"-NonInteractive",
|
|
523
|
+
"-EncodedCommand",
|
|
524
|
+
utf16LeBase64(powershellScript)
|
|
525
|
+
];
|
|
526
|
+
const startedProcessHandle = bridgeProcessHandle;
|
|
527
|
+
try {
|
|
528
|
+
const started = originalSend.call(bridge, "process/spawn", {
|
|
529
|
+
command,
|
|
530
|
+
processHandle: startedProcessHandle,
|
|
531
|
+
cwd: "C:\\",
|
|
532
|
+
streamStdin: true,
|
|
533
|
+
streamStdoutStderr: true,
|
|
534
|
+
outputBytesCap: null,
|
|
535
|
+
timeoutMs: null
|
|
536
|
+
});
|
|
537
|
+
void Promise.resolve(started).catch((error) => {
|
|
538
|
+
if (bridgeProcessHandle === startedProcessHandle) failBridge(error, false);
|
|
539
|
+
});
|
|
540
|
+
} catch (error) {
|
|
541
|
+
failBridge(error);
|
|
542
|
+
}
|
|
543
|
+
return bridgeReadyPromise;
|
|
544
|
+
};
|
|
545
|
+
const writeBridgeFrame = (value) => {
|
|
546
|
+
const operation = async () => {
|
|
547
|
+
await startBridge();
|
|
548
|
+
if (bridgeState !== "ready") throw transportError("is not ready");
|
|
549
|
+
await originalSend.call(bridge, "process/writeStdin", {
|
|
550
|
+
processHandle: bridgeProcessHandle,
|
|
551
|
+
deltaBase64: utf8Base64(`${JSON.stringify(value)}
|
|
552
|
+
`)
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
const next = writeTail.then(operation, operation);
|
|
556
|
+
writeTail = next.catch(() => void 0);
|
|
557
|
+
return next;
|
|
558
|
+
};
|
|
559
|
+
const enqueueBridgeRequest = (method, parameters, options) => bridge.enqueueRequest(method, parameters, options, (request) => {
|
|
560
|
+
bridgeRequests.set(request.id, { method, parameters });
|
|
561
|
+
void writeBridgeFrame(request).catch((error) => failBridge(error));
|
|
562
|
+
});
|
|
563
|
+
const initializeBridgeProtocol = () => {
|
|
564
|
+
const initialization = enqueueBridgeRequest("initialize", {
|
|
565
|
+
clientInfo: {
|
|
566
|
+
name: "codexhost_remote_control_bridge",
|
|
567
|
+
title: "codexhost Remote Control bridge",
|
|
568
|
+
version: "1"
|
|
569
|
+
},
|
|
570
|
+
capabilities: {
|
|
571
|
+
experimentalApi: true,
|
|
572
|
+
mcpServerOpenaiFormElicitation: true
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
return new Promise((resolve, reject) => {
|
|
576
|
+
const timeout = globalThis.setTimeout(() => {
|
|
577
|
+
const message = "initialization timed out after 15000ms";
|
|
578
|
+
failBridge(message);
|
|
579
|
+
reject(transportError(message));
|
|
580
|
+
}, 15e3);
|
|
581
|
+
void Promise.resolve(initialization).then(
|
|
582
|
+
(value) => {
|
|
583
|
+
globalThis.clearTimeout(timeout);
|
|
584
|
+
resolve(value);
|
|
585
|
+
},
|
|
586
|
+
(error) => {
|
|
587
|
+
globalThis.clearTimeout(timeout);
|
|
588
|
+
reject(error);
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
});
|
|
592
|
+
};
|
|
593
|
+
const initializeBridge = () => {
|
|
594
|
+
resetFailedBridge();
|
|
595
|
+
if (bridgeInitialization) return bridgeInitialization;
|
|
596
|
+
bridgeInitialization = startBridge().then(() => initializeBridgeProtocol()).then(() => writeBridgeFrame({ method: "initialized", params: {} }));
|
|
597
|
+
return bridgeInitialization;
|
|
598
|
+
};
|
|
599
|
+
const threadIdFromParameters = (parameters) => {
|
|
600
|
+
if (!isRecord4(parameters)) return null;
|
|
601
|
+
const value = parameters.threadId ?? parameters.conversationId;
|
|
602
|
+
return typeof value === "string" ? value : null;
|
|
603
|
+
};
|
|
604
|
+
const isThreadScopedMethod = (method) => method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("review/");
|
|
605
|
+
const resolveThreadOwnership = (threadId) => {
|
|
606
|
+
if (knownExternalThreadIds.has(threadId)) return Promise.resolve("external");
|
|
607
|
+
if (knownOfficialThreadIds.has(threadId)) return Promise.resolve("codex");
|
|
608
|
+
const pending = threadOwnershipResolutions.get(threadId);
|
|
609
|
+
if (pending) return pending;
|
|
610
|
+
const resolution = initializeBridge().then(
|
|
611
|
+
() => enqueueBridgeRequest("codexhost/thread/ownership/list", {
|
|
612
|
+
threadIds: [threadId]
|
|
613
|
+
})
|
|
614
|
+
).then((value) => {
|
|
615
|
+
const ownerships = isRecord4(value) && Array.isArray(value.threads) ? value.threads : null;
|
|
616
|
+
const ownership = ownerships?.[0];
|
|
617
|
+
if (!ownerships || !isRecord4(ownership) || ownerships.length !== 1 || ownership.threadId !== threadId || ownership.owner !== "external" && ownership.owner !== "codex") {
|
|
618
|
+
throw transportError("Thread ownership lookup returned an invalid result");
|
|
619
|
+
}
|
|
620
|
+
if (ownership.owner === "external") {
|
|
621
|
+
knownExternalThreadIds.add(threadId);
|
|
622
|
+
knownOfficialThreadIds.delete(threadId);
|
|
623
|
+
return "external";
|
|
624
|
+
}
|
|
625
|
+
knownOfficialThreadIds.add(threadId);
|
|
626
|
+
knownExternalThreadIds.delete(threadId);
|
|
627
|
+
return "codex";
|
|
628
|
+
});
|
|
629
|
+
threadOwnershipResolutions.set(threadId, resolution);
|
|
630
|
+
const clearResolution = () => {
|
|
631
|
+
if (threadOwnershipResolutions.get(threadId) === resolution) {
|
|
632
|
+
threadOwnershipResolutions.delete(threadId);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
void resolution.then(clearResolution, clearResolution);
|
|
636
|
+
return resolution;
|
|
637
|
+
};
|
|
638
|
+
const shouldResolveThreadOwnership = (method, parameters) => {
|
|
639
|
+
if (!isRemoteControlHost || method.startsWith("codexhost/") || !isThreadScopedMethod(method)) {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
const threadId = threadIdFromParameters(parameters);
|
|
643
|
+
if (!threadId || knownExternalThreadIds.has(threadId) || knownOfficialThreadIds.has(threadId)) {
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
return threadId;
|
|
647
|
+
};
|
|
648
|
+
const shouldUseBridge = (method, parameters) => {
|
|
649
|
+
if (!isRemoteControlHost) return false;
|
|
650
|
+
if (method.startsWith("codexhost/")) return true;
|
|
651
|
+
if (method === "thread/list") return true;
|
|
652
|
+
if (method === "thread/start") {
|
|
653
|
+
return isRecord4(parameters) && (typeof parameters.model === "string" && parameters.model.startsWith("codexhost/") || typeof parameters.__codexhostAccountId === "string");
|
|
654
|
+
}
|
|
655
|
+
const threadId = threadIdFromParameters(parameters);
|
|
656
|
+
if (threadId && knownExternalThreadIds.has(threadId)) return true;
|
|
657
|
+
if (threadId && knownOfficialThreadIds.has(threadId)) return false;
|
|
658
|
+
return selectedModel !== null && (method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("review/"));
|
|
659
|
+
};
|
|
660
|
+
const routeThreadStart = (parameters) => {
|
|
661
|
+
if (!isRecord4(parameters) || parameters.ephemeral === true) {
|
|
662
|
+
return parameters;
|
|
663
|
+
}
|
|
664
|
+
const routed = {
|
|
665
|
+
...parameters,
|
|
666
|
+
...selectedModel === null ? {} : { model: selectedModel },
|
|
667
|
+
...selectedCodexAccountId === null ? {} : { __codexhostAccountId: selectedCodexAccountId }
|
|
668
|
+
};
|
|
669
|
+
selectedCodexAccountId = null;
|
|
670
|
+
return routed;
|
|
671
|
+
};
|
|
672
|
+
const routedSend = (method, parameters, options) => {
|
|
673
|
+
const routedParameters = method === "thread/start" ? routeThreadStart(parameters) : parameters;
|
|
674
|
+
const sendBridged = () => initializeBridge().then(
|
|
675
|
+
() => enqueueBridgeRequest(method, routedParameters, options)
|
|
676
|
+
);
|
|
677
|
+
const sendDirect = () => options === void 0 ? originalSend.call(bridge, method, routedParameters) : originalSend.call(bridge, method, routedParameters, options);
|
|
678
|
+
const unresolvedThreadId = shouldResolveThreadOwnership(method, routedParameters);
|
|
679
|
+
if (unresolvedThreadId) {
|
|
680
|
+
return resolveThreadOwnership(unresolvedThreadId).then(
|
|
681
|
+
(owner) => owner === "external" ? sendBridged() : sendDirect()
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
return shouldUseBridge(method, routedParameters) ? sendBridged() : sendDirect();
|
|
685
|
+
};
|
|
686
|
+
const routedPrewarm = (parameters, options) => {
|
|
687
|
+
const routed = routeThreadStart(parameters);
|
|
688
|
+
const routedParameters = isRecord4(routed) && typeof routed.model === "string" && routed.model.startsWith("codexhost/") ? { ...routed, ephemeral: true } : routed;
|
|
689
|
+
if (shouldUseBridge("thread/start", routedParameters)) {
|
|
690
|
+
return routedSend("thread/start", routedParameters, options);
|
|
691
|
+
}
|
|
692
|
+
return options === void 0 ? originalPrewarm.call(bridge, routedParameters) : originalPrewarm.call(bridge, routedParameters, options);
|
|
693
|
+
};
|
|
694
|
+
bridge.sendRequest = routedSend;
|
|
695
|
+
bridge.prewarmThreadStart = routedPrewarm;
|
|
696
|
+
const routedWindowMessage = (event) => {
|
|
697
|
+
const message = event.data;
|
|
698
|
+
if (!isRecord4(message) || message.type !== "mcp-notification" || message.hostId !== hostId || typeof message.method !== "string") {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
handleOuterNotification(message.method, message.params);
|
|
702
|
+
};
|
|
703
|
+
const observesWindowNotifications = isRemoteControlHost && typeof target.addEventListener === "function" && typeof target.removeEventListener === "function";
|
|
704
|
+
if (observesWindowNotifications) target.addEventListener?.("message", routedWindowMessage);
|
|
705
|
+
const routedOnNotification = (method, parameters) => {
|
|
706
|
+
if (!handleOuterNotification(method, parameters)) {
|
|
707
|
+
originalOnNotification.call(manager, method, parameters);
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
const routedDispatchAppServerResponse = (method, response) => {
|
|
711
|
+
if (isRemoteControlHost && typeof response.id === "string") {
|
|
712
|
+
const innerRequestId = bridgeServerRequests.get(response.id);
|
|
713
|
+
if (innerRequestId !== void 0 || bridgeServerRequests.has(response.id)) {
|
|
714
|
+
bridgeServerRequests.delete(response.id);
|
|
715
|
+
void writeBridgeFrame({ ...response, id: innerRequestId }).catch(
|
|
716
|
+
(error) => failBridge(error)
|
|
717
|
+
);
|
|
718
|
+
return void 0;
|
|
719
|
+
}
|
|
720
|
+
if (response.id.startsWith(bridgeServerRequestIdPrefix)) return void 0;
|
|
721
|
+
}
|
|
722
|
+
return originalDispatchAppServerResponse.call(manager, method, response);
|
|
723
|
+
};
|
|
724
|
+
if (!observesWindowNotifications) manager.onNotification = routedOnNotification;
|
|
725
|
+
manager.dispatchAppServerResponse = routedDispatchAppServerResponse;
|
|
726
|
+
const policy = Object.freeze({
|
|
727
|
+
state: "ready",
|
|
728
|
+
hostId,
|
|
729
|
+
owns(candidateManager, candidate, candidateHostId, candidatePrewarmedThreadManager) {
|
|
730
|
+
return candidateManager === manager && candidate === bridge && candidateHostId === hostId && candidatePrewarmedThreadManager === prewarmedThreadManager;
|
|
731
|
+
},
|
|
732
|
+
requestTarget() {
|
|
733
|
+
return manager;
|
|
734
|
+
},
|
|
735
|
+
select(model) {
|
|
736
|
+
if (model !== null && (typeof model !== "string" || !model.startsWith("codexhost/"))) {
|
|
737
|
+
throw new Error("Draft route Model must be a codexhost transport carrier");
|
|
738
|
+
}
|
|
739
|
+
if (selectedModel === model) return false;
|
|
740
|
+
selectedModel = model;
|
|
741
|
+
return true;
|
|
742
|
+
},
|
|
743
|
+
selectAccount(accountId) {
|
|
744
|
+
if (accountId !== null && !/^[A-Za-z0-9._~-]+$/u.test(accountId)) {
|
|
745
|
+
throw new Error("Draft Codex Account ID must be filename-safe");
|
|
746
|
+
}
|
|
747
|
+
if (selectedCodexAccountId === accountId) return false;
|
|
748
|
+
selectedCodexAccountId = accountId;
|
|
749
|
+
return true;
|
|
750
|
+
},
|
|
751
|
+
clear() {
|
|
752
|
+
prewarmedThreadManager.discardAllPrewarmedThreads();
|
|
753
|
+
return Promise.resolve();
|
|
754
|
+
},
|
|
755
|
+
dispose() {
|
|
756
|
+
if (bridge.sendRequest === routedSend) bridge.sendRequest = originalSend;
|
|
757
|
+
if (bridge.prewarmThreadStart === routedPrewarm) {
|
|
758
|
+
bridge.prewarmThreadStart = originalPrewarm;
|
|
759
|
+
}
|
|
760
|
+
if (observesWindowNotifications) {
|
|
761
|
+
target.removeEventListener?.("message", routedWindowMessage);
|
|
762
|
+
} else if (manager.onNotification === routedOnNotification) {
|
|
763
|
+
manager.onNotification = originalOnNotification;
|
|
764
|
+
}
|
|
765
|
+
if (manager.dispatchAppServerResponse === routedDispatchAppServerResponse) {
|
|
766
|
+
manager.dispatchAppServerResponse = originalDispatchAppServerResponse;
|
|
767
|
+
}
|
|
768
|
+
if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
|
|
769
|
+
bridgeReadyTimeout = null;
|
|
770
|
+
if (isRemoteControlHost && (bridgeState === "starting" || bridgeState === "ready")) {
|
|
771
|
+
void Promise.resolve(
|
|
772
|
+
originalSend.call(bridge, "process/kill", { processHandle: bridgeProcessHandle })
|
|
773
|
+
).catch(() => void 0);
|
|
774
|
+
}
|
|
775
|
+
bridgeState = "disposed";
|
|
776
|
+
const disposedError = transportError("was disposed");
|
|
777
|
+
bridgeReadyReject?.(disposedError);
|
|
778
|
+
for (const requestId of bridgeRequests.keys()) bridge.onError(requestId, disposedError);
|
|
779
|
+
bridgeRequests.clear();
|
|
780
|
+
bridgeServerRequests.clear();
|
|
781
|
+
knownExternalThreadIds.clear();
|
|
782
|
+
knownOfficialThreadIds.clear();
|
|
783
|
+
threadOwnershipResolutions.clear();
|
|
784
|
+
selectedModel = null;
|
|
785
|
+
selectedCodexAccountId = null;
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
Object.defineProperty(target, "__codexhostDraftPrewarmPolicyV1", {
|
|
789
|
+
configurable: true,
|
|
790
|
+
value: policy
|
|
791
|
+
});
|
|
792
|
+
if (typeof target.dispatchEvent === "function" && typeof CustomEvent === "function") {
|
|
793
|
+
target.dispatchEvent(new CustomEvent("codexhost:draft-prewarm-policy-changed"));
|
|
794
|
+
}
|
|
795
|
+
return { state: "ready", reason: "owned-request-bridge" };
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/native-ui/desktop-control/src/renderer-draft-prewarm-policy.ts
|
|
799
|
+
function selectRendererRequestManager(candidates, activeHostIds) {
|
|
800
|
+
const unique = /* @__PURE__ */ new Map();
|
|
801
|
+
for (const candidate of candidates) unique.set(candidate.manager, candidate);
|
|
802
|
+
const hosts = new Set(
|
|
803
|
+
activeHostIds.filter((value) => typeof value === "string" && value.length > 0)
|
|
804
|
+
);
|
|
805
|
+
if (hosts.size > 1) return null;
|
|
806
|
+
const activeHostId = hosts.values().next().value;
|
|
807
|
+
const eligible = [...unique.values()].filter(
|
|
808
|
+
(candidate) => activeHostId === void 0 || candidate.hostId === activeHostId
|
|
809
|
+
);
|
|
810
|
+
return eligible.length === 1 ? eligible[0] ?? null : null;
|
|
811
|
+
}
|
|
812
|
+
function isRecord2(value) {
|
|
813
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
814
|
+
}
|
|
815
|
+
var FIND_REQUEST_MANAGER_EXPRESSION = `(() => {
|
|
816
|
+
const editors = [...document.querySelectorAll(
|
|
817
|
+
'[data-codex-composer], [contenteditable="true"][role="textbox"]',
|
|
818
|
+
)];
|
|
819
|
+
if (editors.length !== 1) {
|
|
820
|
+
return { candidateCount: 0, hostId: null, sendRequest: null };
|
|
821
|
+
}
|
|
822
|
+
let element = editors[0];
|
|
823
|
+
let fiber = null;
|
|
824
|
+
while (element != null && fiber == null) {
|
|
825
|
+
const key = Object.getOwnPropertyNames(element).find((name) =>
|
|
826
|
+
name.startsWith('__reactFiber$'),
|
|
827
|
+
);
|
|
828
|
+
if (key != null) fiber = element[key];
|
|
829
|
+
element = element.parentElement;
|
|
830
|
+
}
|
|
831
|
+
const managers = new Set();
|
|
832
|
+
const activeHostIds = new Set();
|
|
833
|
+
for (let depth = 0; fiber != null && depth < 200; depth += 1, fiber = fiber.return) {
|
|
834
|
+
const props = fiber.memoizedProps;
|
|
835
|
+
if (props != null && typeof props === 'object') {
|
|
836
|
+
for (const name of ['executionTargetHostId', 'permissionsHostId']) {
|
|
837
|
+
const value = props[name];
|
|
838
|
+
if (typeof value === 'string' && value.length > 0) activeHostIds.add(value);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
let hook = fiber.memoizedState;
|
|
842
|
+
for (let index = 0; hook != null && index < 120; index += 1, hook = hook.next) {
|
|
843
|
+
const value = hook.memoizedState;
|
|
844
|
+
if (
|
|
845
|
+
value != null &&
|
|
846
|
+
typeof value === 'object' &&
|
|
847
|
+
value.requestClient != null &&
|
|
848
|
+
typeof value.requestClient.prewarmThreadStart === 'function' &&
|
|
849
|
+
typeof value.requestClient.sendRequest === 'function' &&
|
|
850
|
+
typeof value.requestClient.enqueueRequest === 'function' &&
|
|
851
|
+
typeof value.prewarmedThreadManager?.discardAllPrewarmedThreads === 'function' &&
|
|
852
|
+
typeof value.sendRequest === 'function'
|
|
853
|
+
) {
|
|
854
|
+
managers.add(value);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
const candidates = [...managers].map((manager) => {
|
|
859
|
+
const requestClient =
|
|
860
|
+
typeof manager.requestClient?.sendRequest === 'function' &&
|
|
861
|
+
typeof manager.requestClient?.prewarmThreadStart === 'function' &&
|
|
862
|
+
typeof manager.requestClient?.enqueueRequest === 'function'
|
|
863
|
+
? manager.requestClient
|
|
864
|
+
: manager;
|
|
865
|
+
return {
|
|
866
|
+
manager,
|
|
867
|
+
requestClient,
|
|
868
|
+
hostId: manager?.getHostId?.() ?? requestClient?.hostId ?? null,
|
|
869
|
+
prewarmedThreadManager: manager?.prewarmedThreadManager ?? null,
|
|
870
|
+
};
|
|
871
|
+
});
|
|
872
|
+
const selected = (${selectRendererRequestManager.toString()})(candidates, [...activeHostIds]);
|
|
873
|
+
return {
|
|
874
|
+
candidateCount: selected == null ? candidates.length : 1,
|
|
875
|
+
hostId: selected?.hostId ?? null,
|
|
876
|
+
manager: selected?.manager ?? null,
|
|
877
|
+
requestClient: selected?.requestClient ?? null,
|
|
878
|
+
prewarmedThreadManager: selected?.prewarmedThreadManager ?? null,
|
|
879
|
+
};
|
|
880
|
+
})()`;
|
|
881
|
+
var INSTALL_RENDERER_POLICY_FUNCTION = `function(requestClient, hostId, prewarmedThreadManager) {
|
|
882
|
+
return (${installDraftPrewarmPolicyBridge.toString()})(
|
|
883
|
+
this,
|
|
884
|
+
requestClient,
|
|
885
|
+
hostId,
|
|
886
|
+
window,
|
|
887
|
+
prewarmedThreadManager,
|
|
888
|
+
);
|
|
889
|
+
}`;
|
|
890
|
+
var REQUEST_MANAGER_WAIT_TIMEOUT_MS = 6e4;
|
|
891
|
+
var REQUEST_MANAGER_POLL_INTERVAL_MS = 25;
|
|
892
|
+
function directRendererInstaller() {
|
|
893
|
+
return `(async () => {
|
|
894
|
+
const selected = ${FIND_REQUEST_MANAGER_EXPRESSION};
|
|
895
|
+
if (
|
|
896
|
+
selected.candidateCount !== 1 ||
|
|
897
|
+
typeof selected.hostId !== 'string' ||
|
|
898
|
+
selected.hostId.length === 0 ||
|
|
899
|
+
selected.manager == null ||
|
|
900
|
+
selected.requestClient == null
|
|
901
|
+
) {
|
|
902
|
+
throw new Error('Renderer request manager is ambiguous');
|
|
903
|
+
}
|
|
904
|
+
if (selected.prewarmedThreadManager == null) {
|
|
905
|
+
throw new Error('Renderer prewarmed Thread manager is unavailable');
|
|
906
|
+
}
|
|
907
|
+
return (${installDraftPrewarmPolicyBridge.toString()})(
|
|
908
|
+
selected.manager,
|
|
909
|
+
selected.requestClient,
|
|
910
|
+
selected.hostId,
|
|
911
|
+
window,
|
|
912
|
+
selected.prewarmedThreadManager,
|
|
913
|
+
);
|
|
914
|
+
})()`;
|
|
915
|
+
}
|
|
916
|
+
async function waitForDraftPrewarmPolicy(evaluate, expression) {
|
|
917
|
+
const deadline = Date.now() + REQUEST_MANAGER_WAIT_TIMEOUT_MS;
|
|
918
|
+
while (true) {
|
|
919
|
+
try {
|
|
920
|
+
const value = await evaluate(expression);
|
|
921
|
+
if (!isRecord2(value) || value.state !== "ready" || value.reason !== "owned-request-bridge") {
|
|
922
|
+
throw new Error("Renderer draft prewarm policy returned an invalid status");
|
|
923
|
+
}
|
|
924
|
+
return value;
|
|
925
|
+
} catch (error) {
|
|
926
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
927
|
+
const remaining = deadline - Date.now();
|
|
928
|
+
if (!message.includes("Renderer request manager is ambiguous") || remaining <= 0) throw error;
|
|
929
|
+
await new Promise((resolve) => {
|
|
930
|
+
setTimeout(resolve, Math.min(REQUEST_MANAGER_POLL_INTERVAL_MS, remaining));
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function installRendererDraftPrewarmPolicyDirect(renderer) {
|
|
936
|
+
return waitForDraftPrewarmPolicy(
|
|
937
|
+
(expression) => renderer.evaluate(expression),
|
|
938
|
+
directRendererInstaller()
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/native-ui/desktop-control/src/renderer-cdp-control-session.ts
|
|
943
|
+
function isRecord3(value) {
|
|
944
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
945
|
+
}
|
|
946
|
+
function sleep(milliseconds) {
|
|
947
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
948
|
+
}
|
|
949
|
+
function sameAgents(actual, expected) {
|
|
950
|
+
return actual.length === expected.length && actual.every((agent, index) => agent === expected[index]);
|
|
951
|
+
}
|
|
952
|
+
function isPrimaryRendererUrl(value) {
|
|
953
|
+
try {
|
|
954
|
+
const url = new URL(value);
|
|
955
|
+
return url.protocol === "app:" && url.hostname === "-" && url.pathname === "/index.html" && url.search === "" && url.hash === "";
|
|
956
|
+
} catch {
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function selectPrimaryRendererTarget(targets, preferredTargetId) {
|
|
961
|
+
const candidates = targets.filter(
|
|
962
|
+
(target) => target.type === "page" && isPrimaryRendererUrl(target.url)
|
|
963
|
+
);
|
|
964
|
+
return candidates.find((target) => target.id === preferredTargetId) ?? candidates.at(0) ?? null;
|
|
965
|
+
}
|
|
966
|
+
var RendererAdapterReadinessError = class extends Error {
|
|
967
|
+
constructor(state, reason) {
|
|
968
|
+
super(`Production Renderer Adapter is ${state}: ${reason}`);
|
|
969
|
+
this.state = state;
|
|
970
|
+
this.reason = reason;
|
|
971
|
+
this.name = "RendererAdapterReadinessError";
|
|
972
|
+
}
|
|
973
|
+
state;
|
|
974
|
+
reason;
|
|
975
|
+
};
|
|
976
|
+
function validateBindingStatus(value, expectedAgents) {
|
|
977
|
+
if (!isRecord3(value) || value.version !== 2 || !Array.isArray(value.enabledAgents) || value.enabledAgents.some((agent) => typeof agent !== "string") || !sameAgents(value.enabledAgents, expectedAgents) || !isRecord3(value.adapter)) {
|
|
978
|
+
throw new Error("Production Renderer binding returned an invalid status");
|
|
979
|
+
}
|
|
980
|
+
if (value.adapter.state !== "ready" || typeof value.adapter.reason !== "string") {
|
|
981
|
+
const state = typeof value.adapter.state === "string" ? value.adapter.state : "invalid";
|
|
982
|
+
const reason = typeof value.adapter.reason === "string" ? value.adapter.reason : "unknown";
|
|
983
|
+
throw new RendererAdapterReadinessError(state, reason);
|
|
984
|
+
}
|
|
985
|
+
return value;
|
|
986
|
+
}
|
|
987
|
+
async function waitForPrimaryTarget(endpoint, operations, timeoutMs, pollIntervalMs, preferredTargetId) {
|
|
988
|
+
const deadline = Date.now() + timeoutMs;
|
|
989
|
+
let lastError;
|
|
990
|
+
while (Date.now() < deadline) {
|
|
991
|
+
try {
|
|
992
|
+
const target = selectPrimaryRendererTarget(
|
|
993
|
+
await operations.listTargets(endpoint),
|
|
994
|
+
preferredTargetId
|
|
995
|
+
);
|
|
996
|
+
if (target) return target;
|
|
997
|
+
lastError = new Error("Renderer CDP has no primary app://-/index.html page target");
|
|
998
|
+
} catch (error) {
|
|
999
|
+
lastError = error;
|
|
1000
|
+
}
|
|
1001
|
+
await sleep(pollIntervalMs);
|
|
1002
|
+
}
|
|
1003
|
+
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
1004
|
+
throw new Error(`Primary Codex Renderer CDP target did not become ready${detail}`);
|
|
1005
|
+
}
|
|
1006
|
+
async function evaluateSource(renderer, source) {
|
|
1007
|
+
const response = await renderer.command("Runtime.evaluate", {
|
|
1008
|
+
expression: source,
|
|
1009
|
+
awaitPromise: true
|
|
1010
|
+
});
|
|
1011
|
+
if (!isRecord3(response)) throw new Error("Renderer source evaluation returned an invalid result");
|
|
1012
|
+
if (isRecord3(response.exceptionDetails)) {
|
|
1013
|
+
const text = typeof response.exceptionDetails.text === "string" ? response.exceptionDetails.text : "Renderer source evaluation failed";
|
|
1014
|
+
throw new Error(text);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
async function readBinding(renderer) {
|
|
1018
|
+
return renderer.evaluate("window.__codexhostRendererBindingProbeV1?.status() ?? null");
|
|
1019
|
+
}
|
|
1020
|
+
async function waitForBinding(renderer, enabledAgents, timeoutMs, pollIntervalMs) {
|
|
1021
|
+
const deadline = Date.now() + timeoutMs;
|
|
1022
|
+
let lastError;
|
|
1023
|
+
while (Date.now() < deadline) {
|
|
1024
|
+
try {
|
|
1025
|
+
const value = await readBinding(renderer);
|
|
1026
|
+
if (value !== null) return validateBindingStatus(value, enabledAgents);
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
lastError = error;
|
|
1029
|
+
if (error instanceof RendererAdapterReadinessError && error.state !== "installing") {
|
|
1030
|
+
throw error;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
await sleep(pollIntervalMs);
|
|
1034
|
+
}
|
|
1035
|
+
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
1036
|
+
throw new Error(`Production Renderer binding did not become ready${detail}`);
|
|
1037
|
+
}
|
|
1038
|
+
async function installTarget(target, rendererSource, enabledAgents, timeoutMs, pollIntervalMs, operations) {
|
|
1039
|
+
const renderer = await operations.connect(target.webSocketDebuggerUrl);
|
|
1040
|
+
try {
|
|
1041
|
+
await renderer.command("Runtime.enable");
|
|
1042
|
+
await renderer.command("Page.enable");
|
|
1043
|
+
await renderer.command("Page.addScriptToEvaluateOnNewDocument", { source: rendererSource });
|
|
1044
|
+
await evaluateSource(renderer, rendererSource);
|
|
1045
|
+
const draftPrewarmPolicy = await operations.installDraftPrewarmPolicy(renderer);
|
|
1046
|
+
const binding = await waitForBinding(renderer, enabledAgents, timeoutMs, pollIntervalMs);
|
|
1047
|
+
return { renderer, snapshot: { target, draftPrewarmPolicy, binding } };
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
renderer.close();
|
|
1050
|
+
throw error;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
var InstalledRendererCdpControlSession = class {
|
|
1054
|
+
constructor(renderer, rendererCdpEndpoint2, rendererSource, enabledAgents, timeoutMs, pollIntervalMs, operations, currentSnapshot) {
|
|
1055
|
+
this.renderer = renderer;
|
|
1056
|
+
this.rendererCdpEndpoint = rendererCdpEndpoint2;
|
|
1057
|
+
this.rendererSource = rendererSource;
|
|
1058
|
+
this.enabledAgents = enabledAgents;
|
|
1059
|
+
this.timeoutMs = timeoutMs;
|
|
1060
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
1061
|
+
this.operations = operations;
|
|
1062
|
+
this.currentSnapshot = currentSnapshot;
|
|
1063
|
+
}
|
|
1064
|
+
renderer;
|
|
1065
|
+
rendererCdpEndpoint;
|
|
1066
|
+
rendererSource;
|
|
1067
|
+
enabledAgents;
|
|
1068
|
+
timeoutMs;
|
|
1069
|
+
pollIntervalMs;
|
|
1070
|
+
operations;
|
|
1071
|
+
currentSnapshot;
|
|
1072
|
+
#closed = false;
|
|
1073
|
+
get snapshot() {
|
|
1074
|
+
return this.currentSnapshot;
|
|
1075
|
+
}
|
|
1076
|
+
async ensureInstalled() {
|
|
1077
|
+
if (this.#closed) throw new Error("Renderer CDP Control Session is closed");
|
|
1078
|
+
const target = await waitForPrimaryTarget(
|
|
1079
|
+
this.rendererCdpEndpoint,
|
|
1080
|
+
this.operations,
|
|
1081
|
+
this.timeoutMs,
|
|
1082
|
+
this.pollIntervalMs,
|
|
1083
|
+
this.currentSnapshot.target.id
|
|
1084
|
+
);
|
|
1085
|
+
if (target.id !== this.currentSnapshot.target.id) {
|
|
1086
|
+
const replacement = await installTarget(
|
|
1087
|
+
target,
|
|
1088
|
+
this.rendererSource,
|
|
1089
|
+
this.enabledAgents,
|
|
1090
|
+
this.timeoutMs,
|
|
1091
|
+
this.pollIntervalMs,
|
|
1092
|
+
this.operations
|
|
1093
|
+
);
|
|
1094
|
+
this.renderer.close();
|
|
1095
|
+
this.renderer = replacement.renderer;
|
|
1096
|
+
this.currentSnapshot = replacement.snapshot;
|
|
1097
|
+
return this.currentSnapshot;
|
|
1098
|
+
}
|
|
1099
|
+
try {
|
|
1100
|
+
const existing = await readBinding(this.renderer);
|
|
1101
|
+
if (existing === null) await evaluateSource(this.renderer, this.rendererSource);
|
|
1102
|
+
else validateBindingStatus(existing, this.enabledAgents);
|
|
1103
|
+
const draftPrewarmPolicy = await this.operations.installDraftPrewarmPolicy(this.renderer);
|
|
1104
|
+
const binding = await waitForBinding(
|
|
1105
|
+
this.renderer,
|
|
1106
|
+
this.enabledAgents,
|
|
1107
|
+
this.timeoutMs,
|
|
1108
|
+
this.pollIntervalMs
|
|
1109
|
+
);
|
|
1110
|
+
this.currentSnapshot = { target, draftPrewarmPolicy, binding };
|
|
1111
|
+
return this.currentSnapshot;
|
|
1112
|
+
} catch {
|
|
1113
|
+
const replacement = await installTarget(
|
|
1114
|
+
target,
|
|
1115
|
+
this.rendererSource,
|
|
1116
|
+
this.enabledAgents,
|
|
1117
|
+
this.timeoutMs,
|
|
1118
|
+
this.pollIntervalMs,
|
|
1119
|
+
this.operations
|
|
1120
|
+
);
|
|
1121
|
+
this.renderer.close();
|
|
1122
|
+
this.renderer = replacement.renderer;
|
|
1123
|
+
this.currentSnapshot = replacement.snapshot;
|
|
1124
|
+
return this.currentSnapshot;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
async activateDesktop() {
|
|
1128
|
+
if (this.#closed) throw new Error("Renderer CDP Control Session is closed");
|
|
1129
|
+
await this.renderer.command("Page.bringToFront");
|
|
1130
|
+
return 1;
|
|
1131
|
+
}
|
|
1132
|
+
executeRenderer(expression) {
|
|
1133
|
+
if (this.#closed) return Promise.reject(new Error("Renderer CDP Control Session is closed"));
|
|
1134
|
+
return this.renderer.evaluate(expression);
|
|
1135
|
+
}
|
|
1136
|
+
close() {
|
|
1137
|
+
if (this.#closed) return;
|
|
1138
|
+
this.#closed = true;
|
|
1139
|
+
this.renderer.close();
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
var defaultOperations = {
|
|
1143
|
+
listTargets: (endpoint) => listCdpTargets(endpoint),
|
|
1144
|
+
connect: (webSocketDebuggerUrl) => CdpClient.connect(webSocketDebuggerUrl),
|
|
1145
|
+
installDraftPrewarmPolicy: installRendererDraftPrewarmPolicyDirect
|
|
1146
|
+
};
|
|
1147
|
+
async function createRendererCdpControlSession(options) {
|
|
1148
|
+
const enabledAgents = options.enabledAgents ?? ["codex", "pi"];
|
|
1149
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
1150
|
+
const pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
1151
|
+
const operations = options.operations ?? defaultOperations;
|
|
1152
|
+
const target = await waitForPrimaryTarget(
|
|
1153
|
+
options.rendererCdpEndpoint,
|
|
1154
|
+
operations,
|
|
1155
|
+
timeoutMs,
|
|
1156
|
+
pollIntervalMs
|
|
1157
|
+
);
|
|
1158
|
+
const installed = await installTarget(
|
|
1159
|
+
target,
|
|
1160
|
+
options.rendererSource,
|
|
1161
|
+
enabledAgents,
|
|
1162
|
+
timeoutMs,
|
|
1163
|
+
pollIntervalMs,
|
|
1164
|
+
operations
|
|
1165
|
+
);
|
|
1166
|
+
return new InstalledRendererCdpControlSession(
|
|
1167
|
+
installed.renderer,
|
|
1168
|
+
options.rendererCdpEndpoint,
|
|
1169
|
+
options.rendererSource,
|
|
1170
|
+
enabledAgents,
|
|
1171
|
+
timeoutMs,
|
|
1172
|
+
pollIntervalMs,
|
|
1173
|
+
operations,
|
|
1174
|
+
installed.snapshot
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
function installRendererCdpControlSession(options) {
|
|
1178
|
+
return createRendererCdpControlSession(options);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/native-ui/desktop-control/src/production-controller.ts
|
|
1182
|
+
var PRODUCTION_INSTALL_TIMEOUT_MS = 9e4;
|
|
1183
|
+
var RENDERER_CSP_BOOTSTRAP = "globalThis.__zod_globalConfig ??= {}; globalThis.__zod_globalConfig.jitless = true;";
|
|
1184
|
+
var DESKTOP_CONTROLLER_READINESS_MAX_BYTES = 512;
|
|
1185
|
+
var TRANSIENT_INSTALL_ATTEMPTS = 3;
|
|
1186
|
+
var TRANSIENT_INSTALL_RETRY_MS = 250;
|
|
1187
|
+
var RECOVERY_RETRY_INITIAL_MS = 3e4;
|
|
1188
|
+
var RECOVERY_RETRY_MAX_MS = 3e5;
|
|
1189
|
+
var startupTraceStartedAt = Date.now();
|
|
1190
|
+
function startupTrace(stage, detail) {
|
|
1191
|
+
if (process.env.CODEXHOST_STARTUP_TRACE !== "1") return;
|
|
1192
|
+
const suffix = detail === void 0 ? "" : `: ${detail instanceof Error ? detail.message : String(detail)}`;
|
|
1193
|
+
console.error(
|
|
1194
|
+
`[codexhost startup +${Date.now() - startupTraceStartedAt}ms] controller: ${stage}${suffix}`
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
function serializeDesktopControllerReadiness(readiness) {
|
|
1198
|
+
if (readiness.schemaVersion !== 2 || readiness.state !== "compatible" || !Array.isArray(readiness.issues) || readiness.issues.length !== 0 || Object.keys(readiness).length !== 3) {
|
|
1199
|
+
throw new Error("Desktop Controller readiness is invalid");
|
|
1200
|
+
}
|
|
1201
|
+
const line = JSON.stringify(readiness);
|
|
1202
|
+
if (Buffer.byteLength(line, "utf8") > DESKTOP_CONTROLLER_READINESS_MAX_BYTES) {
|
|
1203
|
+
throw new Error("Desktop Controller readiness exceeds its size limit");
|
|
1204
|
+
}
|
|
1205
|
+
return line;
|
|
1206
|
+
}
|
|
1207
|
+
var defaultDependencies = {
|
|
1208
|
+
readRenderer: (filePath) => readFile(filePath, "utf8"),
|
|
1209
|
+
install: installRendererCdpControlSession,
|
|
1210
|
+
startAttachmentServer: startControllerAttachmentServer,
|
|
1211
|
+
ready: (readiness) => {
|
|
1212
|
+
process.stdout.write(`${serializeDesktopControllerReadiness(readiness)}
|
|
1213
|
+
`);
|
|
1214
|
+
},
|
|
1215
|
+
sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
1216
|
+
monitorIntervalMs: 500
|
|
1217
|
+
};
|
|
1218
|
+
function rendererCdpEndpoint(value) {
|
|
1219
|
+
const url = new URL(value);
|
|
1220
|
+
if (url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]"].includes(url.hostname) || !url.port || url.username || url.password || url.pathname !== "/" && url.pathname !== "" || url.search || url.hash) {
|
|
1221
|
+
throw new Error("--renderer-cdp-endpoint must be a loopback HTTP origin with an explicit port");
|
|
1222
|
+
}
|
|
1223
|
+
return url.origin;
|
|
1224
|
+
}
|
|
1225
|
+
function parseDesktopControllerArguments(arguments_) {
|
|
1226
|
+
let endpoint;
|
|
1227
|
+
let rendererPath;
|
|
1228
|
+
let defaultAgent;
|
|
1229
|
+
let attachmentPort;
|
|
1230
|
+
let attachmentNonce;
|
|
1231
|
+
for (let index = 0; index < arguments_.length; index += 1) {
|
|
1232
|
+
const argument = arguments_[index];
|
|
1233
|
+
const value = arguments_[index + 1];
|
|
1234
|
+
if (argument === "--renderer-cdp-endpoint") {
|
|
1235
|
+
if (endpoint !== void 0) {
|
|
1236
|
+
throw new Error("--renderer-cdp-endpoint may only be provided once");
|
|
1237
|
+
}
|
|
1238
|
+
if (!value) throw new Error("--renderer-cdp-endpoint requires a value");
|
|
1239
|
+
endpoint = rendererCdpEndpoint(value);
|
|
1240
|
+
index += 1;
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
if (argument === "--renderer") {
|
|
1244
|
+
if (rendererPath !== void 0) throw new Error("--renderer may only be provided once");
|
|
1245
|
+
if (!value) throw new Error("--renderer requires a value");
|
|
1246
|
+
if (!path.isAbsolute(value)) throw new Error("--renderer must be an absolute path");
|
|
1247
|
+
rendererPath = path.normalize(value);
|
|
1248
|
+
index += 1;
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
if (argument === "--default-agent") {
|
|
1252
|
+
if (defaultAgent !== void 0) throw new Error("--default-agent may only be provided once");
|
|
1253
|
+
if (value !== "codex" && value !== "pi") {
|
|
1254
|
+
throw new Error("--default-agent must be 'codex' or 'pi'");
|
|
1255
|
+
}
|
|
1256
|
+
defaultAgent = value;
|
|
1257
|
+
index += 1;
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
if (argument === "--attachment-port") {
|
|
1261
|
+
if (attachmentPort !== void 0) {
|
|
1262
|
+
throw new Error("--attachment-port may only be provided once");
|
|
1263
|
+
}
|
|
1264
|
+
const port = Number(value);
|
|
1265
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
1266
|
+
throw new Error("--attachment-port must be a valid TCP port");
|
|
1267
|
+
}
|
|
1268
|
+
attachmentPort = port;
|
|
1269
|
+
index += 1;
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (argument === "--attachment-nonce") {
|
|
1273
|
+
if (attachmentNonce !== void 0) {
|
|
1274
|
+
throw new Error("--attachment-nonce may only be provided once");
|
|
1275
|
+
}
|
|
1276
|
+
if (value === void 0 || !/^[0-9a-f]{32}$/.test(value)) {
|
|
1277
|
+
throw new Error("--attachment-nonce must be 32 lowercase hexadecimal characters");
|
|
1278
|
+
}
|
|
1279
|
+
attachmentNonce = value;
|
|
1280
|
+
index += 1;
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
throw new Error(`unknown Desktop Controller option: ${argument}`);
|
|
1284
|
+
}
|
|
1285
|
+
if (endpoint === void 0) throw new Error("--renderer-cdp-endpoint is required");
|
|
1286
|
+
if (rendererPath === void 0) throw new Error("--renderer is required");
|
|
1287
|
+
if (defaultAgent === void 0) throw new Error("--default-agent is required");
|
|
1288
|
+
if (attachmentPort === void 0) throw new Error("--attachment-port is required");
|
|
1289
|
+
if (attachmentNonce === void 0) throw new Error("--attachment-nonce is required");
|
|
1290
|
+
return {
|
|
1291
|
+
rendererCdpEndpoint: endpoint,
|
|
1292
|
+
rendererPath,
|
|
1293
|
+
defaultAgent,
|
|
1294
|
+
attachmentPort,
|
|
1295
|
+
attachmentNonce
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
function isTransientRendererInstallError(error) {
|
|
1299
|
+
let current = error;
|
|
1300
|
+
for (let depth = 0; depth < 4; depth += 1) {
|
|
1301
|
+
const message = current instanceof Error ? current.message : String(current);
|
|
1302
|
+
if (message.includes("Execution context was destroyed") || message.includes("Promise was collected")) {
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
current = current instanceof Error ? current.cause : void 0;
|
|
1306
|
+
if (current === void 0) break;
|
|
1307
|
+
}
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
async function installProductionSession(options, dependencies) {
|
|
1311
|
+
for (let attempt = 1; attempt <= TRANSIENT_INSTALL_ATTEMPTS; attempt += 1) {
|
|
1312
|
+
try {
|
|
1313
|
+
return await dependencies.install(options);
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
if (attempt === TRANSIENT_INSTALL_ATTEMPTS || !isTransientRendererInstallError(error)) {
|
|
1316
|
+
throw error;
|
|
1317
|
+
}
|
|
1318
|
+
await dependencies.sleep(TRANSIENT_INSTALL_RETRY_MS);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
throw new Error("Desktop Controller exhausted Renderer installation attempts");
|
|
1322
|
+
}
|
|
1323
|
+
async function runDesktopController(options, signal, dependencies = defaultDependencies) {
|
|
1324
|
+
const configuration = `Object.defineProperty(window, "__codexhostProductionConfigV1", { configurable: true, value: { defaultAgent: ${JSON.stringify(options.defaultAgent)} } });`;
|
|
1325
|
+
const now = dependencies.now ?? Date.now;
|
|
1326
|
+
let session;
|
|
1327
|
+
let nextRecoveryAt = 0;
|
|
1328
|
+
let recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
|
|
1329
|
+
const recordRecoveryFailure = () => {
|
|
1330
|
+
nextRecoveryAt = now() + recoveryDelayMs;
|
|
1331
|
+
recoveryDelayMs = Math.min(recoveryDelayMs * 2, RECOVERY_RETRY_MAX_MS);
|
|
1332
|
+
};
|
|
1333
|
+
const recordRecoverySuccess = () => {
|
|
1334
|
+
nextRecoveryAt = 0;
|
|
1335
|
+
recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
|
|
1336
|
+
};
|
|
1337
|
+
const createSession = async () => {
|
|
1338
|
+
startupTrace("reading Renderer bundle");
|
|
1339
|
+
const rendererSource = await dependencies.readRenderer(options.rendererPath);
|
|
1340
|
+
if (rendererSource.trim().length === 0) throw new Error("production Renderer Bundle is empty");
|
|
1341
|
+
startupTrace("installing Renderer Session");
|
|
1342
|
+
const installed = await installProductionSession(
|
|
1343
|
+
{
|
|
1344
|
+
rendererCdpEndpoint: options.rendererCdpEndpoint,
|
|
1345
|
+
rendererSource: `${RENDERER_CSP_BOOTSTRAP}
|
|
1346
|
+
${configuration}
|
|
1347
|
+
${rendererSource}`,
|
|
1348
|
+
enabledAgents: [
|
|
1349
|
+
"codex",
|
|
1350
|
+
"pi",
|
|
1351
|
+
"claude-code",
|
|
1352
|
+
"deepseek-harness",
|
|
1353
|
+
"omp",
|
|
1354
|
+
"opencode",
|
|
1355
|
+
"grok",
|
|
1356
|
+
"openclaw",
|
|
1357
|
+
"hermes",
|
|
1358
|
+
"antigravity",
|
|
1359
|
+
"qoder",
|
|
1360
|
+
"workbuddy",
|
|
1361
|
+
"zcode",
|
|
1362
|
+
"trae"
|
|
1363
|
+
],
|
|
1364
|
+
timeoutMs: PRODUCTION_INSTALL_TIMEOUT_MS
|
|
1365
|
+
},
|
|
1366
|
+
dependencies
|
|
1367
|
+
);
|
|
1368
|
+
startupTrace("Renderer Session installed");
|
|
1369
|
+
return installed;
|
|
1370
|
+
};
|
|
1371
|
+
startupTrace("initialization started");
|
|
1372
|
+
try {
|
|
1373
|
+
session = await createSession();
|
|
1374
|
+
recordRecoverySuccess();
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
startupTrace("initial Renderer Session unavailable", error);
|
|
1377
|
+
session = void 0;
|
|
1378
|
+
recordRecoveryFailure();
|
|
1379
|
+
}
|
|
1380
|
+
let operation = Promise.resolve(void 0);
|
|
1381
|
+
const useSession = (callback) => {
|
|
1382
|
+
const next = operation.then(callback, callback);
|
|
1383
|
+
operation = next.then(
|
|
1384
|
+
() => void 0,
|
|
1385
|
+
() => void 0
|
|
1386
|
+
);
|
|
1387
|
+
return next;
|
|
1388
|
+
};
|
|
1389
|
+
const resetSession = () => {
|
|
1390
|
+
session?.close();
|
|
1391
|
+
session = void 0;
|
|
1392
|
+
};
|
|
1393
|
+
const ensureSession = async () => {
|
|
1394
|
+
if (!session) session = await createSession();
|
|
1395
|
+
else await session.ensureInstalled();
|
|
1396
|
+
return session;
|
|
1397
|
+
};
|
|
1398
|
+
const recoverSession = async () => {
|
|
1399
|
+
try {
|
|
1400
|
+
const current = await ensureSession();
|
|
1401
|
+
recordRecoverySuccess();
|
|
1402
|
+
return current;
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
resetSession();
|
|
1405
|
+
recordRecoveryFailure();
|
|
1406
|
+
throw error;
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
let attachmentServer;
|
|
1410
|
+
try {
|
|
1411
|
+
startupTrace("starting attachment server");
|
|
1412
|
+
attachmentServer = await dependencies.startAttachmentServer({
|
|
1413
|
+
port: options.attachmentPort,
|
|
1414
|
+
nonce: options.attachmentNonce,
|
|
1415
|
+
attach: () => useSession(async () => {
|
|
1416
|
+
const current = await recoverSession();
|
|
1417
|
+
await current.activateDesktop();
|
|
1418
|
+
})
|
|
1419
|
+
});
|
|
1420
|
+
startupTrace("attachment server ready");
|
|
1421
|
+
startupTrace("publishing readiness");
|
|
1422
|
+
dependencies.ready({
|
|
1423
|
+
schemaVersion: 2,
|
|
1424
|
+
state: "compatible",
|
|
1425
|
+
issues: []
|
|
1426
|
+
});
|
|
1427
|
+
while (!signal.aborted) {
|
|
1428
|
+
await dependencies.sleep(dependencies.monitorIntervalMs);
|
|
1429
|
+
if (signal.aborted) continue;
|
|
1430
|
+
await useSession(async () => {
|
|
1431
|
+
if (!session && now() < nextRecoveryAt) return;
|
|
1432
|
+
try {
|
|
1433
|
+
await recoverSession();
|
|
1434
|
+
} catch {
|
|
1435
|
+
}
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
} finally {
|
|
1439
|
+
await attachmentServer?.close();
|
|
1440
|
+
await operation;
|
|
1441
|
+
resetSession();
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// src/native-ui/desktop-control/src/release-main.ts
|
|
1446
|
+
var abort = new AbortController();
|
|
1447
|
+
var stop = () => abort.abort();
|
|
1448
|
+
process.once("SIGINT", stop);
|
|
1449
|
+
process.once("SIGTERM", stop);
|
|
1450
|
+
try {
|
|
1451
|
+
await runDesktopController(parseDesktopControllerArguments(process.argv.slice(2)), abort.signal);
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
console.error(
|
|
1454
|
+
`codexhost Desktop Controller: ${error instanceof Error ? error.message : String(error)}`
|
|
1455
|
+
);
|
|
1456
|
+
process.exitCode = 1;
|
|
1457
|
+
} finally {
|
|
1458
|
+
process.removeListener("SIGINT", stop);
|
|
1459
|
+
process.removeListener("SIGTERM", stop);
|
|
1460
|
+
}
|