@gajae-code/coding-agent 0.10.1 → 0.11.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/CHANGELOG.md +70 -2
- package/dist/types/append-only-mode.d.ts +1 -1
- package/dist/types/cli/args.d.ts +4 -5
- package/dist/types/cli/daemon-cli.d.ts +12 -1
- package/dist/types/cli/notify-cli.d.ts +21 -1
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/notify.d.ts +28 -1
- package/dist/types/commands/sdk.d.ts +33 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
- package/dist/types/config/model-profile-activation.d.ts +35 -15
- package/dist/types/config/model-profiles.d.ts +11 -5
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/model-resolver.d.ts +39 -6
- package/dist/types/config/model-selector-value.d.ts +17 -0
- package/dist/types/config/models-config-schema.d.ts +10 -6
- package/dist/types/config/settings-schema.d.ts +133 -6
- package/dist/types/config/settings.d.ts +63 -35
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
- package/dist/types/coordinator-mcp/server.d.ts +6 -49
- package/dist/types/daemon/builtin.d.ts +6 -5
- package/dist/types/daemon/control-types.d.ts +1 -1
- package/dist/types/discovery/mcp-json.d.ts +12 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
- package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +10 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -3
- package/dist/types/extensibility/extensions/types.d.ts +141 -16
- package/dist/types/extensibility/hooks/types.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
- package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
- package/dist/types/harness-control-plane/operate.d.ts +5 -5
- package/dist/types/harness-control-plane/owner.d.ts +18 -4
- package/dist/types/harness-control-plane/receipts.d.ts +0 -1
- package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
- package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
- package/dist/types/harness-control-plane/types.d.ts +3 -4
- package/dist/types/lsp/index.d.ts +1 -1
- package/dist/types/main.d.ts +32 -38
- package/dist/types/modes/acp/acp-agent.d.ts +224 -53
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -15
- package/dist/types/modes/acp/permission-mode.d.ts +4 -0
- package/dist/types/modes/acp/startup-options.d.ts +6 -0
- package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +1 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
- package/dist/types/modes/components/pet-capability.d.ts +37 -0
- package/dist/types/modes/components/pet-selector.d.ts +1 -1
- package/dist/types/modes/components/settings-selector.d.ts +15 -4
- package/dist/types/modes/components/welcome.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
- package/dist/types/modes/index.d.ts +0 -4
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- package/dist/types/modes/runtime-init.d.ts +2 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
- package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
- package/dist/types/modes/types.d.ts +16 -1
- package/dist/types/runtime-mcp/client.d.ts +2 -0
- package/dist/types/runtime-mcp/config.d.ts +5 -1
- package/dist/types/runtime-mcp/manager.d.ts +8 -1
- package/dist/types/runtime-mcp/types.d.ts +4 -0
- package/dist/types/sdk/acp/adapter.d.ts +67 -0
- package/dist/types/sdk/acp/index.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +53 -0
- package/dist/types/sdk/broker/discovery.d.ts +30 -0
- package/dist/types/sdk/broker/ensure.d.ts +33 -0
- package/dist/types/sdk/broker/identity.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +8 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
- package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
- package/dist/types/sdk/broker/runtime.d.ts +33 -0
- package/dist/types/sdk/broker/session-index.d.ts +57 -0
- package/dist/types/sdk/broker/state-version.d.ts +8 -0
- package/dist/types/sdk/broker/transport.d.ts +9 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
- package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
- package/dist/types/sdk/bus/config.d.ts +274 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
- package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
- package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
- package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
- package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
- package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
- package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
- package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
- package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
- package/dist/types/sdk/bus/session-control.d.ts +83 -0
- package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
- package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
- package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
- package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
- package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/session-cli.d.ts +19 -0
- package/dist/types/sdk/client/client.d.ts +1 -0
- package/dist/types/sdk/client/discovery.d.ts +27 -0
- package/dist/types/sdk/client/index.d.ts +2 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
- package/dist/types/sdk/host/control/index.d.ts +2 -0
- package/dist/types/sdk/host/control/operations.d.ts +65 -0
- package/dist/types/sdk/host/events.d.ts +35 -0
- package/dist/types/sdk/host/host.d.ts +35 -0
- package/dist/types/sdk/host/index.d.ts +4 -0
- package/dist/types/sdk/host/query/cursor.d.ts +62 -0
- package/dist/types/sdk/host/query/handlers.d.ts +70 -0
- package/dist/types/sdk/host/query/index.d.ts +3 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
- package/dist/types/sdk/host/types.d.ts +32 -0
- package/dist/types/sdk/index.d.ts +6 -0
- package/dist/types/sdk/lifecycle-session.d.ts +14 -0
- package/dist/types/sdk/mcp/index.d.ts +1 -0
- package/dist/types/sdk/mcp/server.d.ts +37 -0
- package/dist/types/sdk/models.d.ts +44 -0
- package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
- package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
- package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
- package/dist/types/sdk/startup-capability.d.ts +50 -0
- package/dist/types/session/agent-session.d.ts +114 -21
- package/dist/types/session/default-model-selection.d.ts +25 -0
- package/dist/types/session/fallback-chain-controller.d.ts +52 -0
- package/dist/types/session/session-manager.d.ts +58 -1
- package/dist/types/setup/credential-auto-import.d.ts +36 -6
- package/dist/types/tools/ask-answer-registry.d.ts +1 -1
- package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
- package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
- package/dist/types/tools/browser/launch.d.ts +22 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
- package/dist/types/tools/browser/profile-posture.d.ts +37 -0
- package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
- package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
- package/dist/types/tools/browser/registry.d.ts +19 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/index.d.ts +8 -8
- package/package.json +34 -13
- package/scripts/build-sdk-package-smoke.ts +94 -0
- package/scripts/capture-notifications-settings-showcase.ts +435 -0
- package/scripts/compile-args.ts +4 -2
- package/scripts/g005-daemon-smoke.ts +4 -5
- package/scripts/g011-daemon-path-smoke.ts +5 -5
- package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
- package/scripts/generate-sdk-operation-inventory.ts +867 -0
- package/scripts/generate-telegram-baseline-manifest.ts +128 -0
- package/scripts/manifest-schema.ts +124 -0
- package/scripts/run-test-manifest.ts +153 -0
- package/scripts/run-threat-model-check.ts +157 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
- package/scripts/verify-gjc-sdk-rename.ts +90 -0
- package/src/append-only-mode.ts +1 -1
- package/src/cli/args.ts +43 -12
- package/src/cli/auth-gateway-cli.ts +2 -2
- package/src/cli/config-cli.ts +2 -1
- package/src/cli/daemon-cli.ts +63 -14
- package/src/cli/list-models.ts +1 -1
- package/src/cli/notify-cli.ts +288 -286
- package/src/cli.ts +21 -2
- package/src/commands/daemon.ts +50 -6
- package/src/commands/harness.ts +93 -36
- package/src/commands/launch.ts +5 -2
- package/src/commands/mcp-serve.ts +19 -12
- package/src/commands/notify.ts +36 -3
- package/src/commands/sdk.ts +426 -0
- package/src/commit/agentic/agent.ts +1 -0
- package/src/config/atomic-yaml-patch.ts +423 -0
- package/src/config/model-profile-activation.ts +240 -89
- package/src/config/model-profiles.ts +37 -34
- package/src/config/model-registry.ts +51 -26
- package/src/config/model-resolver.ts +138 -51
- package/src/config/model-selector-value.ts +50 -0
- package/src/config/models-config-schema.ts +14 -21
- package/src/config/settings-schema.ts +128 -10
- package/src/config/settings.ts +1120 -158
- package/src/coordinator-mcp/model-preset.ts +74 -20
- package/src/coordinator-mcp/server.ts +1597 -3052
- package/src/daemon/builtin.ts +8 -5
- package/src/daemon/control-types.ts +1 -1
- package/src/defaults/gjc/skills/team/SKILL.md +4 -4
- package/src/discovery/mcp-json.ts +327 -20
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/compact-handler.ts +3 -3
- package/src/extensibility/extensions/get-commands-handler.ts +2 -2
- package/src/extensibility/extensions/loader.ts +69 -5
- package/src/extensibility/extensions/runner.ts +112 -4
- package/src/extensibility/extensions/types.ts +176 -17
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/slash-commands.ts +3 -3
- package/src/gjc-runtime/launch-tmux.ts +170 -86
- package/src/gjc-runtime/psmux-detect.ts +82 -4
- package/src/gjc-runtime/session-state-sidecar.ts +41 -31
- package/src/gjc-runtime/team-runtime.ts +54 -35
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
- package/src/gjc-runtime/tmux-owner-isolation.ts +5 -2
- package/src/gjc-runtime/tmux-sessions.ts +19 -13
- package/src/gjc-runtime/ultragoal-guard.ts +53 -6
- package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
- package/src/gjc-runtime/windows-powershell-command.ts +58 -0
- package/src/harness-control-plane/operate.ts +9 -9
- package/src/harness-control-plane/owner.ts +283 -39
- package/src/harness-control-plane/receipts.ts +0 -1
- package/src/harness-control-plane/sdk-transport.ts +395 -0
- package/src/harness-control-plane/session-transport.ts +94 -0
- package/src/harness-control-plane/types.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +36 -37
- package/src/lsp/client.ts +42 -6
- package/src/lsp/index.ts +2 -2
- package/src/main.ts +299 -204
- package/src/modes/DESIGN.md +258 -0
- package/src/modes/acp/acp-agent.ts +1228 -2612
- package/src/modes/acp/acp-client-bridge.ts +2 -20
- package/src/modes/acp/acp-event-mapper.ts +18 -2
- package/src/modes/acp/acp-mode.ts +8 -36
- package/src/modes/acp/permission-mode.ts +25 -0
- package/src/modes/acp/startup-options.ts +6 -0
- package/src/modes/components/agent-dashboard.ts +37 -17
- package/src/modes/components/custom-editor.ts +1 -0
- package/src/modes/components/gajae-pet-widget.ts +169 -20
- package/src/modes/components/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1379 -0
- package/src/modes/components/pet-capability.ts +115 -0
- package/src/modes/components/pet-selector.ts +4 -6
- package/src/modes/components/settings-selector.ts +86 -11
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/event-controller.ts +11 -16
- package/src/modes/controllers/extension-ui-controller.ts +357 -12
- package/src/modes/controllers/input-controller.ts +43 -13
- package/src/modes/controllers/selector-controller.ts +671 -44
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +108 -24
- package/src/modes/runtime-init.ts +298 -6
- package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
- package/src/modes/shared/agent-wire/event-contract.ts +7 -8
- package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
- package/src/modes/shared/agent-wire/event-observation.ts +16 -7
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
- package/src/modes/types.ts +16 -1
- package/src/runtime-mcp/client.ts +94 -19
- package/src/runtime-mcp/config.ts +27 -15
- package/src/runtime-mcp/manager.ts +343 -105
- package/src/runtime-mcp/oauth-flow.ts +42 -12
- package/src/runtime-mcp/transports/http.ts +49 -19
- package/src/runtime-mcp/transports/stdio.ts +47 -28
- package/src/runtime-mcp/types.ts +10 -0
- package/src/sdk/acp/adapter.ts +418 -0
- package/src/sdk/acp/index.ts +2 -0
- package/src/sdk/broker/broker.ts +642 -0
- package/src/sdk/broker/discovery.ts +94 -0
- package/src/sdk/broker/ensure.ts +249 -0
- package/src/sdk/broker/identity.ts +42 -0
- package/src/sdk/broker/index.ts +8 -0
- package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
- package/src/sdk/broker/internal-source.bunfig.toml +1 -0
- package/src/sdk/broker/lifecycle-ledger.ts +286 -0
- package/src/sdk/broker/lifecycle.ts +2157 -0
- package/src/sdk/broker/process-incarnation.ts +159 -0
- package/src/sdk/broker/runtime.ts +164 -0
- package/src/sdk/broker/session-index.ts +318 -0
- package/src/sdk/broker/state-version.ts +23 -0
- package/src/sdk/broker/transport.ts +125 -0
- package/src/sdk/bus/chat-command-policy.ts +107 -0
- package/src/sdk/bus/chat-daemon-cli.ts +214 -0
- package/src/sdk/bus/chat-daemon-control.ts +713 -0
- package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
- package/src/sdk/bus/chat-effect-journal.ts +353 -0
- package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
- package/src/sdk/bus/config.ts +433 -0
- package/src/sdk/bus/conversation-store.ts +391 -0
- package/src/sdk/bus/discord-conversation.ts +85 -0
- package/src/sdk/bus/discord-daemon.ts +2275 -0
- package/src/sdk/bus/discord-live-provider.ts +521 -0
- package/src/sdk/bus/discord-provider.ts +56 -0
- package/src/{notifications → sdk/bus}/html-format.ts +2 -2
- package/src/sdk/bus/index.ts +3943 -0
- package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
- package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
- package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
- package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
- package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
- package/src/sdk/bus/natives-augment.d.ts +38 -0
- package/src/sdk/bus/notification-orchestration.ts +535 -0
- package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
- package/src/sdk/bus/session-control.ts +333 -0
- package/src/sdk/bus/slack-conversation.ts +74 -0
- package/src/sdk/bus/slack-daemon.ts +2144 -0
- package/src/sdk/bus/slack-live-provider.ts +380 -0
- package/src/sdk/bus/slack-provider.ts +76 -0
- package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
- package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
- package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
- package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
- package/src/sdk/bus/telegram-setup.ts +663 -0
- package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/session-cli.ts +275 -0
- package/src/sdk/client/client.ts +1 -0
- package/src/sdk/client/discovery.ts +108 -0
- package/src/sdk/client/index.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +367 -0
- package/src/sdk/host/control/index.ts +10 -0
- package/src/sdk/host/control/operations.ts +63 -0
- package/src/sdk/host/events.ts +91 -0
- package/src/sdk/host/host.ts +296 -0
- package/src/sdk/host/index.ts +4 -0
- package/src/sdk/host/query/cursor.ts +189 -0
- package/src/sdk/host/query/handlers.ts +533 -0
- package/src/sdk/host/query/index.ts +3 -0
- package/src/sdk/host/query/revision-store.ts +1126 -0
- package/src/sdk/host/reverse-leases.ts +325 -0
- package/src/sdk/host/types.ts +29 -0
- package/src/sdk/index.ts +13 -0
- package/src/sdk/lifecycle-session.ts +39 -0
- package/src/sdk/mcp/index.ts +1 -0
- package/src/sdk/mcp/server.ts +332 -0
- package/src/sdk/models.ts +168 -0
- package/src/sdk/protocol/adapter-validation.ts +48 -0
- package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
- package/src/sdk/protocol/operation-registry.ts +312 -0
- package/src/{sdk.ts → sdk/session.ts} +339 -212
- package/src/sdk/startup-capability.ts +189 -0
- package/src/session/agent-session.ts +2470 -866
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +136 -0
- package/src/session/session-manager.ts +447 -13
- package/src/session/session-storage.ts +8 -1
- package/src/setup/credential-auto-import.ts +290 -35
- package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
- package/src/slash-commands/builtin-registry.ts +33 -30
- package/src/task/executor.ts +62 -13
- package/src/tools/ask-answer-registry.ts +1 -1
- package/src/tools/ask.ts +13 -8
- package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
- package/src/tools/browser/benchmark/detector-report.ts +157 -0
- package/src/tools/browser/benchmark/run-suite.ts +39 -0
- package/src/tools/browser/launch.ts +124 -19
- package/src/tools/browser/profile-discovery.ts +63 -0
- package/src/tools/browser/profile-posture.ts +59 -0
- package/src/tools/browser/profile-reuse.ts +102 -0
- package/src/tools/browser/profile-warmup.ts +109 -0
- package/src/tools/browser/registry.ts +87 -11
- package/src/tools/browser/tab-protocol.ts +1 -0
- package/src/tools/browser/tab-supervisor.ts +1 -0
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +6 -0
- package/src/tools/index.ts +8 -8
- package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
- package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
- package/src/tools/telegram-send.ts +2 -2
- package/vendor/insane-search/MANIFEST.json +2 -1
- package/vendor/insane-search/engine/bias_check.py +4 -1
- package/vendor/insane-search/engine/phase0.py +26 -1
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
- package/dist/types/modes/bridge/auth.d.ts +0 -12
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
- package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
- package/dist/types/modes/bridge/event-stream.d.ts +0 -8
- package/dist/types/modes/rpc/host-tools.d.ts +0 -1
- package/dist/types/modes/rpc/host-uris.d.ts +0 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
- package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
- package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
- package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
- package/dist/types/notifications/config.d.ts +0 -132
- package/src/harness-control-plane/rpc-adapter.ts +0 -290
- package/src/modes/bridge/auth.ts +0 -41
- package/src/modes/bridge/bridge-client-bridge.ts +0 -47
- package/src/modes/bridge/bridge-mode.ts +0 -675
- package/src/modes/bridge/bridge-ui-context.ts +0 -200
- package/src/modes/bridge/event-stream.ts +0 -70
- package/src/modes/rpc/host-tools.ts +0 -1
- package/src/modes/rpc/host-uris.ts +0 -1
- package/src/modes/rpc/rpc-client.ts +0 -1166
- package/src/modes/rpc/rpc-mode.ts +0 -1029
- package/src/modes/rpc/rpc-socket-security.ts +0 -115
- package/src/modes/rpc/rpc-types.ts +0 -637
- package/src/modes/shared/agent-wire/command-contract.ts +0 -18
- package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
- package/src/modes/shared/agent-wire/command-validation.ts +0 -181
- package/src/modes/shared/agent-wire/handshake.ts +0 -157
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
- package/src/modes/shared/agent-wire/protocol.ts +0 -46
- package/src/modes/shared/agent-wire/responses.ts +0 -17
- package/src/modes/shared/agent-wire/scopes.ts +0 -94
- package/src/modes/shared/agent-wire/session-registry.ts +0 -109
- package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
- package/src/modes/shared/agent-wire/ui-result.ts +0 -48
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
- package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
- package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
- package/src/notifications/config.ts +0 -219
- package/src/notifications/index.ts +0 -1835
- package/src/notifications/telegram-daemon-cli.ts +0 -243
- package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
- /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
- /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
- /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
- /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
- /package/src/{notifications → sdk/bus}/engine.ts +0 -0
- /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
- /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
- /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
- /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
- /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
|
@@ -14,11 +14,12 @@ import stealthWebglScript from "../puppeteer/05_stealth_webgl.txt" with { type:
|
|
|
14
14
|
import stealthScreenScript from "../puppeteer/06_stealth_screen.txt" with { type: "text" };
|
|
15
15
|
import stealthFontsScript from "../puppeteer/07_stealth_fonts.txt" with { type: "text" };
|
|
16
16
|
import stealthAudioScript from "../puppeteer/08_stealth_audio.txt" with { type: "text" };
|
|
17
|
-
import stealthLocaleScript from "../puppeteer/09_stealth_locale.txt" with { type: "text" };
|
|
18
17
|
import stealthPluginsScript from "../puppeteer/10_stealth_plugins.txt" with { type: "text" };
|
|
19
18
|
import stealthHardwareScript from "../puppeteer/11_stealth_hardware.txt" with { type: "text" };
|
|
20
19
|
import stealthCodecsScript from "../puppeteer/12_stealth_codecs.txt" with { type: "text" };
|
|
21
20
|
import stealthWorkerScript from "../puppeteer/13_stealth_worker.txt" with { type: "text" };
|
|
21
|
+
import stealthPermissionsScript from "../puppeteer/14_stealth_permissions.txt" with { type: "text" };
|
|
22
|
+
import stealthWebrtcScript from "../puppeteer/15_stealth_webrtc.txt" with { type: "text" };
|
|
22
23
|
import { ToolError } from "../tool-errors";
|
|
23
24
|
|
|
24
25
|
export const DEFAULT_VIEWPORT = { width: 1365, height: 768, deviceScaleFactor: 1.25 };
|
|
@@ -35,7 +36,6 @@ const STEALTH_IGNORE_DEFAULT_ARGS = [
|
|
|
35
36
|
"--disable-default-apps",
|
|
36
37
|
"--disable-component-extensions-with-background-pages",
|
|
37
38
|
];
|
|
38
|
-
const STEALTH_ACCEPT_LANGUAGE = "en-US,en";
|
|
39
39
|
|
|
40
40
|
const USER_AGENT_TARGET_TIMEOUT_MS = 5_000;
|
|
41
41
|
const USER_AGENT_TARGET_TYPES = new Set(["page", "webview", "background_page"]);
|
|
@@ -230,6 +230,22 @@ function resolveSystemChromium(): string | undefined {
|
|
|
230
230
|
export interface LaunchHeadlessOptions {
|
|
231
231
|
headless: boolean;
|
|
232
232
|
viewport?: { width: number; height: number; deviceScaleFactor?: number };
|
|
233
|
+
/**
|
|
234
|
+
* Optional isolated warm-up profile directory (see profile-reuse.ts). When
|
|
235
|
+
* set, the headless browser launches against this isolated copy of the user's
|
|
236
|
+
* real Chrome profile so sessions inherit real cookies/storage/cache for
|
|
237
|
+
* stronger stealth. Callers resolve this via resolveProfileReuse and MUST
|
|
238
|
+
* pass an isolated copy, never a live profile dir.
|
|
239
|
+
*/
|
|
240
|
+
profileWarmupDir?: string;
|
|
241
|
+
/**
|
|
242
|
+
* Optional opt-in geo alignment (see settings browser.geo.*). When set, the
|
|
243
|
+
* browser is launched with a matching timezone (TZ env) and/or UI language
|
|
244
|
+
* (--lang) at the source. Default unset = strict no-op: the real environment
|
|
245
|
+
* timezone/locale are preserved (never half-spoof). Only align these to a
|
|
246
|
+
* coherent value (e.g. one matching a configured proxy egress).
|
|
247
|
+
*/
|
|
248
|
+
geo?: { timezone?: string; locale?: string };
|
|
233
249
|
}
|
|
234
250
|
|
|
235
251
|
export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promise<Browser> {
|
|
@@ -244,8 +260,16 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
|
|
|
244
260
|
"--no-sandbox",
|
|
245
261
|
"--disable-setuid-sandbox",
|
|
246
262
|
"--disable-blink-features=AutomationControlled",
|
|
263
|
+
// Suppress WebRTC leaking the real local/public IP via ICE candidates
|
|
264
|
+
// (a common bot tell). Keeps WebRTC functional over the default public
|
|
265
|
+
// interface only; 15_stealth_webrtc.txt adds a candidate-level guard.
|
|
266
|
+
"--force-webrtc-ip-handling-policy=default_public_interface_only",
|
|
247
267
|
`--window-size=${initialViewport.width},${initialViewport.height}`,
|
|
248
268
|
];
|
|
269
|
+
if (opts.profileWarmupDir) {
|
|
270
|
+
// Isolated copy only (caller guarantees). Never point this at a live profile.
|
|
271
|
+
launchArgs.push(`--user-data-dir=${opts.profileWarmupDir}`);
|
|
272
|
+
}
|
|
249
273
|
const proxy = process.env.PUPPETEER_PROXY;
|
|
250
274
|
if (proxy) {
|
|
251
275
|
launchArgs.push(`--proxy-server=${proxy}`);
|
|
@@ -256,15 +280,22 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
|
|
|
256
280
|
launchArgs.push("--proxy-bypass-list=<-loopback>");
|
|
257
281
|
}
|
|
258
282
|
}
|
|
283
|
+
// Opt-in geo alignment (default no-op). Locale via --lang at the source;
|
|
284
|
+
// timezone via the TZ env passed to the Chrome subprocess.
|
|
285
|
+
if (opts.geo?.locale) {
|
|
286
|
+
launchArgs.push(`--lang=${opts.geo.locale}`);
|
|
287
|
+
}
|
|
259
288
|
const ignoreCert = process.env.PUPPETEER_PROXY_IGNORE_CERT_ERRORS?.toLowerCase();
|
|
260
289
|
if (ignoreCert === "true" || ignoreCert === "1" || ignoreCert === "yes" || ignoreCert === "on") {
|
|
261
290
|
launchArgs.push("--ignore-certificate-errors");
|
|
262
291
|
}
|
|
292
|
+
const launchEnv = opts.geo?.timezone ? { ...process.env, TZ: opts.geo.timezone } : undefined;
|
|
263
293
|
return await puppeteer.launch({
|
|
264
294
|
headless: opts.headless,
|
|
265
295
|
defaultViewport: opts.headless ? initialViewport : null,
|
|
266
296
|
executablePath: await ensureChromiumExecutable(),
|
|
267
297
|
args: launchArgs,
|
|
298
|
+
...(launchEnv ? { env: launchEnv } : {}),
|
|
268
299
|
ignoreDefaultArgs: [...STEALTH_IGNORE_DEFAULT_ARGS],
|
|
269
300
|
protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
|
|
270
301
|
});
|
|
@@ -308,6 +339,20 @@ export interface UserAgentOverride {
|
|
|
308
339
|
};
|
|
309
340
|
}
|
|
310
341
|
|
|
342
|
+
function acceptLanguageForLocale(locale: string): string {
|
|
343
|
+
const canonicalLocale = Intl.getCanonicalLocales(locale)[0]!;
|
|
344
|
+
const baseLanguage = canonicalLocale.split("-")[0]!;
|
|
345
|
+
return canonicalLocale === baseLanguage ? canonicalLocale : `${canonicalLocale},${baseLanguage}`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function resolveAcceptLanguage(page: Page, locale?: string): Promise<string> {
|
|
349
|
+
if (locale) return acceptLanguageForLocale(locale);
|
|
350
|
+
const languages = await page.evaluate(() => [...navigator.languages]);
|
|
351
|
+
const acceptLanguage = languages.filter(Boolean).join(",");
|
|
352
|
+
if (!acceptLanguage) throw new ToolError("Chromium reported no native navigator languages");
|
|
353
|
+
return acceptLanguage;
|
|
354
|
+
}
|
|
355
|
+
|
|
311
356
|
function resolvePageClient(page: Page): PuppeteerCdpClient | null {
|
|
312
357
|
const pageWithClient = page as Page & {
|
|
313
358
|
_client?: (() => PuppeteerCdpClient) | PuppeteerCdpClient;
|
|
@@ -362,7 +407,7 @@ function patchSourceUrl(page: Page): void {
|
|
|
362
407
|
};
|
|
363
408
|
}
|
|
364
409
|
|
|
365
|
-
async function resolveUserAgentOverride(page: Page): Promise<UserAgentOverride> {
|
|
410
|
+
async function resolveUserAgentOverride(page: Page, locale?: string): Promise<UserAgentOverride> {
|
|
366
411
|
const rawUserAgent = await page.browser().userAgent();
|
|
367
412
|
let userAgent = rawUserAgent.replace("HeadlessChrome/", "Chrome/");
|
|
368
413
|
if (userAgent.includes("Linux") && !userAgent.includes("Android")) {
|
|
@@ -413,11 +458,13 @@ async function resolveUserAgentOverride(page: Page): Promise<UserAgentOverride>
|
|
|
413
458
|
brands[order[0]!] = { brand: greaseyBrand, version: "99" };
|
|
414
459
|
brands[order[1]!] = { brand: "Chromium", version: String(majorVersion) };
|
|
415
460
|
brands[order[2]!] = { brand: "Google Chrome", version: String(majorVersion) };
|
|
461
|
+
const acceptLanguage = await resolveAcceptLanguage(page, locale);
|
|
416
462
|
|
|
417
463
|
return {
|
|
418
464
|
userAgent,
|
|
419
465
|
platform,
|
|
420
|
-
acceptLanguage
|
|
466
|
+
acceptLanguage,
|
|
467
|
+
|
|
421
468
|
userAgentMetadata: {
|
|
422
469
|
brands,
|
|
423
470
|
fullVersion: uaVersion,
|
|
@@ -436,7 +483,17 @@ function wrapSession(session: CDPSession): PuppeteerCdpClient {
|
|
|
436
483
|
};
|
|
437
484
|
}
|
|
438
485
|
|
|
439
|
-
async function sendUserAgentOverride(
|
|
486
|
+
async function sendUserAgentOverride(
|
|
487
|
+
client: PuppeteerCdpClient,
|
|
488
|
+
override: UserAgentOverride,
|
|
489
|
+
strict = false,
|
|
490
|
+
): Promise<void> {
|
|
491
|
+
if (strict) {
|
|
492
|
+
await client.send("Network.enable");
|
|
493
|
+
await client.send("Network.setUserAgentOverride", override as unknown as Record<string, unknown>);
|
|
494
|
+
await client.send("Emulation.setUserAgentOverride", override as unknown as Record<string, unknown>);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
440
497
|
try {
|
|
441
498
|
await client.send("Network.enable");
|
|
442
499
|
} catch {}
|
|
@@ -464,28 +521,52 @@ export interface UserAgentSession {
|
|
|
464
521
|
/** Configure UA override on the browser + auto-attach to new targets. */
|
|
465
522
|
async function configureUserAgentTargets(
|
|
466
523
|
browser: Browser,
|
|
467
|
-
state: { browserSession: CDPSession | null; override: UserAgentOverride },
|
|
524
|
+
state: { browserSession: CDPSession | null; override: UserAgentOverride; locale?: string },
|
|
525
|
+
|
|
468
526
|
targetTimeoutMs = USER_AGENT_TARGET_TIMEOUT_MS,
|
|
469
527
|
): Promise<void> {
|
|
470
528
|
if (!state.browserSession) {
|
|
471
529
|
state.browserSession = await browser.target().createCDPSession();
|
|
472
530
|
await state.browserSession.send("Target.setAutoAttach", {
|
|
473
531
|
autoAttach: true,
|
|
474
|
-
waitForDebuggerOnStart:
|
|
532
|
+
waitForDebuggerOnStart: true,
|
|
475
533
|
flatten: true,
|
|
476
534
|
});
|
|
477
535
|
state.browserSession.on(
|
|
478
536
|
"Target.attachedToTarget",
|
|
479
|
-
async (event: { sessionId: string; targetInfo?: { type?: string } }) => {
|
|
480
|
-
if (!targetInfoSupportsUserAgentOverride(event.targetInfo))
|
|
537
|
+
async (event: { sessionId: string; targetInfo?: { targetId?: string; type?: string } }) => {
|
|
538
|
+
if (!targetInfoSupportsUserAgentOverride(event.targetInfo)) {
|
|
539
|
+
const connection = state.browserSession?.connection();
|
|
540
|
+
await connection
|
|
541
|
+
?.session(event.sessionId)
|
|
542
|
+
?.send("Runtime.runIfWaitingForDebugger")
|
|
543
|
+
.catch(() => undefined);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
481
546
|
const connection = state.browserSession?.connection();
|
|
482
547
|
const session = connection?.session(event.sessionId);
|
|
483
548
|
if (!session) return;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
549
|
+
try {
|
|
550
|
+
if (state.locale) {
|
|
551
|
+
await applyClientOverrides(wrapSession(session), state.override, state.locale, true);
|
|
552
|
+
} else {
|
|
553
|
+
await withSoftTimeout(
|
|
554
|
+
sendUserAgentOverride(wrapSession(session), state.override),
|
|
555
|
+
targetTimeoutMs,
|
|
556
|
+
"new target user-agent override",
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
await session.send("Runtime.runIfWaitingForDebugger");
|
|
560
|
+
} catch (error) {
|
|
561
|
+
if (event.targetInfo?.targetId) {
|
|
562
|
+
await state.browserSession
|
|
563
|
+
?.send("Target.closeTarget", { targetId: event.targetInfo.targetId })
|
|
564
|
+
.catch(() => undefined);
|
|
565
|
+
}
|
|
566
|
+
logger.debug("Closed target after geo override failure", {
|
|
567
|
+
error: error instanceof Error ? error.message : String(error),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
489
570
|
},
|
|
490
571
|
);
|
|
491
572
|
}
|
|
@@ -511,14 +592,33 @@ function targetInfoSupportsUserAgentOverride(targetInfo: { type?: string } | und
|
|
|
511
592
|
}
|
|
512
593
|
|
|
513
594
|
async function applyTargetUserAgentOverride(target: Target, override: UserAgentOverride): Promise<void> {
|
|
595
|
+
await applyTargetOverrides(target, override);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async function applyTargetOverrides(
|
|
599
|
+
target: Target,
|
|
600
|
+
override: UserAgentOverride,
|
|
601
|
+
locale?: string,
|
|
602
|
+
strict = false,
|
|
603
|
+
): Promise<void> {
|
|
514
604
|
const session = await target.createCDPSession();
|
|
515
605
|
try {
|
|
516
|
-
await
|
|
606
|
+
await applyClientOverrides(wrapSession(session), override, locale, strict);
|
|
517
607
|
} finally {
|
|
518
608
|
await session.detach().catch(() => undefined);
|
|
519
609
|
}
|
|
520
610
|
}
|
|
521
611
|
|
|
612
|
+
async function applyClientOverrides(
|
|
613
|
+
client: PuppeteerCdpClient,
|
|
614
|
+
override: UserAgentOverride,
|
|
615
|
+
locale?: string,
|
|
616
|
+
strict = false,
|
|
617
|
+
): Promise<void> {
|
|
618
|
+
await sendUserAgentOverride(client, override, strict);
|
|
619
|
+
if (locale) await client.send("Emulation.setLocaleOverride", { locale });
|
|
620
|
+
}
|
|
621
|
+
|
|
522
622
|
async function withSoftTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T | undefined> {
|
|
523
623
|
let timeout: NodeJS.Timeout | undefined;
|
|
524
624
|
const timeoutPromise = new Promise<undefined>(resolve => {
|
|
@@ -550,11 +650,12 @@ const STEALTH_PATCH_SCRIPTS = [
|
|
|
550
650
|
stealthScreenScript,
|
|
551
651
|
stealthFontsScript,
|
|
552
652
|
stealthAudioScript,
|
|
553
|
-
stealthLocaleScript,
|
|
554
653
|
stealthPluginsScript,
|
|
555
654
|
stealthHardwareScript,
|
|
556
655
|
stealthCodecsScript,
|
|
557
656
|
stealthWorkerScript,
|
|
657
|
+
stealthPermissionsScript,
|
|
658
|
+
stealthWebrtcScript,
|
|
558
659
|
];
|
|
559
660
|
|
|
560
661
|
function buildStealthInjectionScript(scripts: readonly string[] = STEALTH_PATCH_SCRIPTS): string {
|
|
@@ -624,17 +725,21 @@ export async function applyStealthPatches(
|
|
|
624
725
|
browser: Browser,
|
|
625
726
|
page: Page,
|
|
626
727
|
state: { browserSession: CDPSession | null; override: UserAgentOverride | null },
|
|
728
|
+
geo?: { readonly timezone?: string; readonly locale?: string },
|
|
627
729
|
): Promise<void> {
|
|
628
730
|
patchSourceUrl(page);
|
|
629
731
|
if (!state.override) {
|
|
630
|
-
state.override = await resolveUserAgentOverride(page);
|
|
732
|
+
state.override = await resolveUserAgentOverride(page, geo?.locale);
|
|
631
733
|
}
|
|
632
734
|
const client = resolvePageClient(page);
|
|
633
735
|
if (client) {
|
|
634
|
-
await sendUserAgentOverride(client, state.override);
|
|
736
|
+
await sendUserAgentOverride(client, state.override, Boolean(geo?.locale));
|
|
635
737
|
}
|
|
636
|
-
const targetState = { browserSession: state.browserSession, override: state.override };
|
|
738
|
+
const targetState = { browserSession: state.browserSession, override: state.override, locale: geo?.locale };
|
|
637
739
|
await configureUserAgentTargets(browser, targetState);
|
|
740
|
+
if (client && geo?.locale) {
|
|
741
|
+
await client.send("Emulation.setLocaleOverride", { locale: geo.locale });
|
|
742
|
+
}
|
|
638
743
|
state.browserSession = targetState.browserSession;
|
|
639
744
|
await injectStealthScripts(page);
|
|
640
745
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Chrome profile discovery (plan Phase 2 wiring).
|
|
3
|
+
*
|
|
4
|
+
* Locates the user's real Chrome "Default" profile directory per OS so the
|
|
5
|
+
* default headless launch path can warm up synthetic sessions from an isolated
|
|
6
|
+
* copy of it (see profile-warmup.ts + profile-posture.ts). Pure path logic with
|
|
7
|
+
* an injectable existence check, so it is unit-testable without touching the
|
|
8
|
+
* real filesystem.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as os from "node:os";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
|
|
14
|
+
export interface DiscoveryEnv {
|
|
15
|
+
platform: NodeJS.Platform;
|
|
16
|
+
home: string;
|
|
17
|
+
/** Injectable for tests; defaults to fs.existsSync at call sites. */
|
|
18
|
+
exists: (p: string) => boolean;
|
|
19
|
+
/** Windows LOCALAPPDATA override (tests / non-default installs). */
|
|
20
|
+
localAppData?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Candidate Chrome user-data roots for the platform (most common first). */
|
|
24
|
+
export function chromeUserDataRoots(env: DiscoveryEnv): string[] {
|
|
25
|
+
switch (env.platform) {
|
|
26
|
+
case "darwin":
|
|
27
|
+
return [path.join(env.home, "Library", "Application Support", "Google", "Chrome")];
|
|
28
|
+
case "win32": {
|
|
29
|
+
const localAppData = env.localAppData ?? path.join(env.home, "AppData", "Local");
|
|
30
|
+
return [path.join(localAppData, "Google", "Chrome", "User Data")];
|
|
31
|
+
}
|
|
32
|
+
default:
|
|
33
|
+
return [path.join(env.home, ".config", "google-chrome"), path.join(env.home, ".config", "chromium")];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface DiscoveredProfile {
|
|
38
|
+
userDataDir: string;
|
|
39
|
+
profileDirectory: string;
|
|
40
|
+
profileDir: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Discover the default Chrome profile, or null when none is present.
|
|
45
|
+
* Only returns a profile whose directory actually exists.
|
|
46
|
+
*/
|
|
47
|
+
export function discoverDefaultChromeProfile(
|
|
48
|
+
env: DiscoveryEnv,
|
|
49
|
+
profileDirectory = "Default",
|
|
50
|
+
): DiscoveredProfile | null {
|
|
51
|
+
for (const userDataDir of chromeUserDataRoots(env)) {
|
|
52
|
+
const profileDir = path.join(userDataDir, profileDirectory);
|
|
53
|
+
if (env.exists(profileDir)) {
|
|
54
|
+
return { userDataDir, profileDirectory, profileDir };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Convenience wrapper using the live OS environment + fs. */
|
|
61
|
+
export function defaultDiscoveryEnv(exists: (p: string) => boolean): DiscoveryEnv {
|
|
62
|
+
return { platform: process.platform, home: os.homedir(), exists };
|
|
63
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-default profile-reuse posture decision (plan Phase 2, Intent Reconciliation).
|
|
3
|
+
*
|
|
4
|
+
* User-confirmed policy: when a usable real Chrome profile is available, the
|
|
5
|
+
* browser tool uses it automatically (AUTO), emits a warning, and falls back to
|
|
6
|
+
* synthetic stealth when unavailable. The posture is overridable via a setting.
|
|
7
|
+
* This is pure decision logic (no browser, no filesystem side effects) so the
|
|
8
|
+
* launch/attach layer can call it and it can be unit-tested deterministically.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Configured posture: `auto` (default) prefers the real profile; `opt-in` stays synthetic unless explicitly asked. */
|
|
12
|
+
export type ProfileReusePosture = "auto" | "opt-in";
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_PROFILE_REUSE_POSTURE: ProfileReusePosture = "auto";
|
|
15
|
+
|
|
16
|
+
export interface PostureInputs {
|
|
17
|
+
/** Configured posture; defaults to `auto` when unset. */
|
|
18
|
+
posture?: ProfileReusePosture;
|
|
19
|
+
/** A usable real Chrome profile was detected and is attachable/safe to copy. */
|
|
20
|
+
realProfileAvailable: boolean;
|
|
21
|
+
/** Caller explicitly requested real-profile reuse for this run (e.g. app.browser === "chrome"). */
|
|
22
|
+
explicitlyRequested?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PostureDecision {
|
|
26
|
+
/** `real` = use the (isolated copy of the) real profile; `synthetic` = synthetic stealth session. */
|
|
27
|
+
mode: "real" | "synthetic";
|
|
28
|
+
/** User-facing warning to emit when real credentials/state are about to be used. */
|
|
29
|
+
warning: string | null;
|
|
30
|
+
/** Machine-readable reason for logs/audit. */
|
|
31
|
+
reason: "auto-real" | "explicit-real" | "synthetic-fallback" | "synthetic-opt-in";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Decide whether to reuse the real profile or run synthetic.
|
|
36
|
+
*
|
|
37
|
+
* - `auto` + available -> real (with warning)
|
|
38
|
+
* - `opt-in` + explicitly asked -> real (with warning)
|
|
39
|
+
* - `opt-in` (no explicit ask) -> synthetic
|
|
40
|
+
* - not available -> synthetic (fallback), even under `auto`
|
|
41
|
+
*/
|
|
42
|
+
export function decideProfilePosture(inputs: PostureInputs): PostureDecision {
|
|
43
|
+
const posture = inputs.posture ?? DEFAULT_PROFILE_REUSE_POSTURE;
|
|
44
|
+
const warning =
|
|
45
|
+
"Using an isolated copy of your real Chrome profile (cookies/session/cache) for stealth. " +
|
|
46
|
+
"Real logged-in credentials may be exercised on visited pages. Set browser.profileReuse to 'opt-in' to disable.";
|
|
47
|
+
|
|
48
|
+
if (!inputs.realProfileAvailable) {
|
|
49
|
+
return { mode: "synthetic", warning: null, reason: "synthetic-fallback" };
|
|
50
|
+
}
|
|
51
|
+
if (posture === "auto") {
|
|
52
|
+
return { mode: "real", warning, reason: "auto-real" };
|
|
53
|
+
}
|
|
54
|
+
// opt-in
|
|
55
|
+
if (inputs.explicitlyRequested) {
|
|
56
|
+
return { mode: "real", warning, reason: "explicit-real" };
|
|
57
|
+
}
|
|
58
|
+
return { mode: "synthetic", warning: null, reason: "synthetic-opt-in" };
|
|
59
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile-reuse orchestration (plan Phase 2 wiring).
|
|
3
|
+
*
|
|
4
|
+
* Single entry point the launch path calls to decide, for a default headless
|
|
5
|
+
* session, whether to warm up from an isolated copy of the user's real Chrome
|
|
6
|
+
* profile. Ties together discovery + posture + isolated warm-up. Filesystem
|
|
7
|
+
* effects (the copy) are performed only when the posture resolves to `real`.
|
|
8
|
+
* The heavy pieces are injectable so this is unit-testable without a browser.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as os from "node:os";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { type DiscoveredProfile, type DiscoveryEnv, discoverDefaultChromeProfile } from "./profile-discovery";
|
|
15
|
+
import { decideProfilePosture, type ProfileReusePosture } from "./profile-posture";
|
|
16
|
+
import { collectWarmupArtifacts, type WarmupManifest } from "./profile-warmup";
|
|
17
|
+
|
|
18
|
+
export interface ResolveProfileReuseInputs {
|
|
19
|
+
posture?: ProfileReusePosture;
|
|
20
|
+
/** Caller explicitly requested real-profile reuse (e.g. app.browser === "chrome"). */
|
|
21
|
+
explicitlyRequested?: boolean;
|
|
22
|
+
discoveryEnv: DiscoveryEnv;
|
|
23
|
+
/** Destination for the isolated warm-up copy; defaults to an ephemeral temp dir. */
|
|
24
|
+
destDir?: string;
|
|
25
|
+
/** Injectable copier (defaults to collectWarmupArtifacts). */
|
|
26
|
+
copy?: (sourceProfileDir: string, destDir: string) => WarmupManifest;
|
|
27
|
+
/** Injectable temp-dir factory (defaults to fs.mkdtemp under os.tmpdir()). */
|
|
28
|
+
makeTempDir?: () => string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ProfileReuseResult {
|
|
32
|
+
mode: "real" | "synthetic";
|
|
33
|
+
reason: string;
|
|
34
|
+
warning: string | null;
|
|
35
|
+
discovered: DiscoveredProfile | null;
|
|
36
|
+
/** Isolated copy directory when mode === "real", else null. */
|
|
37
|
+
warmupDir: string | null;
|
|
38
|
+
manifest: WarmupManifest | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function defaultTempDir(): string {
|
|
42
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), "gjc-profile-warmup-"));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function errorMessage(error: unknown): string {
|
|
46
|
+
return error instanceof Error ? error.message : String(error);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve profile reuse for a default session. When the posture resolves to
|
|
51
|
+
* `real`, copies an allowlisted, lock-free subset of the discovered profile
|
|
52
|
+
* into an isolated dir and returns that dir for the launcher to use.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveProfileReuse(inputs: ResolveProfileReuseInputs): ProfileReuseResult {
|
|
55
|
+
const discovered = discoverDefaultChromeProfile(inputs.discoveryEnv);
|
|
56
|
+
const decision = decideProfilePosture({
|
|
57
|
+
posture: inputs.posture,
|
|
58
|
+
realProfileAvailable: discovered !== null,
|
|
59
|
+
explicitlyRequested: inputs.explicitlyRequested,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (decision.mode !== "real" || !discovered) {
|
|
63
|
+
return {
|
|
64
|
+
mode: "synthetic",
|
|
65
|
+
reason: decision.reason,
|
|
66
|
+
warning: null,
|
|
67
|
+
discovered,
|
|
68
|
+
warmupDir: null,
|
|
69
|
+
manifest: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ownsDestDir = inputs.destDir === undefined;
|
|
74
|
+
let destDir: string | undefined;
|
|
75
|
+
const copy = inputs.copy ?? collectWarmupArtifacts;
|
|
76
|
+
try {
|
|
77
|
+
destDir = inputs.destDir ?? (inputs.makeTempDir ?? defaultTempDir)();
|
|
78
|
+
const manifest = copy(discovered.profileDir, destDir);
|
|
79
|
+
return {
|
|
80
|
+
mode: "real",
|
|
81
|
+
reason: decision.reason,
|
|
82
|
+
warning: decision.warning,
|
|
83
|
+
discovered,
|
|
84
|
+
warmupDir: destDir,
|
|
85
|
+
manifest,
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (ownsDestDir && destDir) {
|
|
89
|
+
try {
|
|
90
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
91
|
+
} catch {}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
mode: "synthetic",
|
|
95
|
+
reason: "synthetic-copy-failed",
|
|
96
|
+
warning: `Could not safely copy the discovered Chrome profile; using synthetic browser state instead: ${errorMessage(error)}`,
|
|
97
|
+
discovered,
|
|
98
|
+
warmupDir: null,
|
|
99
|
+
manifest: null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolated profile warm-up (plan Phase 2, R-C3).
|
|
3
|
+
*
|
|
4
|
+
* Aggressive real-profile reuse seeds a session with the user's cookies /
|
|
5
|
+
* localStorage / cache. To stay safe (never lock or corrupt the live browser
|
|
6
|
+
* session) we NEVER bind the source profile directly: we copy an allowlisted
|
|
7
|
+
* subset of artifacts into an ephemeral, isolated directory and drive Chromium
|
|
8
|
+
* against that copy. Chromium single-instance lock files (SingletonLock/Socket/
|
|
9
|
+
* Cookie) are explicitly excluded so the isolated copy can never contend with a
|
|
10
|
+
* running browser.
|
|
11
|
+
*
|
|
12
|
+
* This module is pure filesystem logic (no browser, no network) so it is
|
|
13
|
+
* deterministically unit-testable.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as fs from "node:fs";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
|
|
19
|
+
/** Chromium single-instance lock artifacts — must never be copied. */
|
|
20
|
+
export const CHROME_LOCK_ARTIFACTS = ["SingletonLock", "SingletonSocket", "SingletonCookie"] as const;
|
|
21
|
+
const CHROME_LOCK_ARTIFACT_SET = new Set<string>(CHROME_LOCK_ARTIFACTS);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Profile-relative artifacts worth seeding for warm-up. Kept intentionally
|
|
25
|
+
* small: session state (cookies, storage) plus a light cache. Anything not
|
|
26
|
+
* listed is skipped, which also keeps the isolated copy cheap.
|
|
27
|
+
*/
|
|
28
|
+
export const WARMUP_ARTIFACTS = [
|
|
29
|
+
"Cookies",
|
|
30
|
+
"Cookies-journal",
|
|
31
|
+
"Local Storage",
|
|
32
|
+
"Session Storage",
|
|
33
|
+
"IndexedDB",
|
|
34
|
+
"Network",
|
|
35
|
+
"Preferences",
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
export interface WarmupManifest {
|
|
39
|
+
sourceProfileDir: string;
|
|
40
|
+
destDir: string;
|
|
41
|
+
copied: string[];
|
|
42
|
+
skippedMissing: string[];
|
|
43
|
+
/** Always empty unless a lock artifact was seen (never copied). */
|
|
44
|
+
excludedLocks: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function copyRecursive(src: string, dest: string, manifest: WarmupManifest): void {
|
|
48
|
+
const stat = fs.statSync(src);
|
|
49
|
+
if (stat.isDirectory()) {
|
|
50
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
51
|
+
for (const entry of fs.readdirSync(src)) {
|
|
52
|
+
if (CHROME_LOCK_ARTIFACT_SET.has(entry)) {
|
|
53
|
+
if (!manifest.excludedLocks.includes(entry)) manifest.excludedLocks.push(entry);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry), manifest);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
60
|
+
fs.copyFileSync(src, dest);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Copy warm-up artifacts from `sourceProfileDir` into an isolated `destDir`.
|
|
66
|
+
*
|
|
67
|
+
* Guarantees:
|
|
68
|
+
* - The source profile is only ever read, never written or locked.
|
|
69
|
+
* - Chromium lock artifacts are never copied (recorded in `excludedLocks`).
|
|
70
|
+
* - Missing artifacts are skipped, not fatal (recorded in `skippedMissing`).
|
|
71
|
+
*/
|
|
72
|
+
export function collectWarmupArtifacts(sourceProfileDir: string, destDir: string): WarmupManifest {
|
|
73
|
+
const manifest: WarmupManifest = {
|
|
74
|
+
sourceProfileDir,
|
|
75
|
+
destDir,
|
|
76
|
+
copied: [],
|
|
77
|
+
skippedMissing: [],
|
|
78
|
+
excludedLocks: [],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (!fs.existsSync(sourceProfileDir)) {
|
|
82
|
+
throw new Error(`source profile directory does not exist: ${sourceProfileDir}`);
|
|
83
|
+
}
|
|
84
|
+
const resolvedSource = path.resolve(sourceProfileDir);
|
|
85
|
+
const resolvedDest = path.resolve(destDir);
|
|
86
|
+
if (resolvedDest === resolvedSource || resolvedDest.startsWith(`${resolvedSource}${path.sep}`)) {
|
|
87
|
+
throw new Error("warm-up destination must be outside the source profile directory");
|
|
88
|
+
}
|
|
89
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
90
|
+
|
|
91
|
+
// Record (but never copy) any lock artifacts present in the source.
|
|
92
|
+
for (const lock of CHROME_LOCK_ARTIFACTS) {
|
|
93
|
+
if (fs.existsSync(path.join(sourceProfileDir, lock))) {
|
|
94
|
+
manifest.excludedLocks.push(lock);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const artifact of WARMUP_ARTIFACTS) {
|
|
99
|
+
const src = path.join(sourceProfileDir, artifact);
|
|
100
|
+
if (!fs.existsSync(src)) {
|
|
101
|
+
manifest.skippedMissing.push(artifact);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
copyRecursive(src, path.join(destDir, artifact), manifest);
|
|
105
|
+
manifest.copied.push(artifact);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return manifest;
|
|
109
|
+
}
|