@gajae-code/coding-agent 0.10.2 → 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 +50 -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/model-selector.d.ts +1 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -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 +3 -0
- 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 +5 -0
- 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 +18 -15
- 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/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 +1 -1
- 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 +34 -35
- 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/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1379 -0
- package/src/modes/components/settings-selector.ts +60 -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 +348 -4
- package/src/modes/controllers/selector-controller.ts +674 -36
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +59 -20
- 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 +5 -0
- 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 +2453 -894
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +136 -0
- package/src/session/session-manager.ts +438 -11
- 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 +2 -1
- 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
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline stealth-detector benchmark model.
|
|
3
|
+
*
|
|
4
|
+
* Pure, deterministic logic for the anti-detection benchmark suite: it converts
|
|
5
|
+
* a browser-collected fingerprint probe into a normalized per-detector verdict,
|
|
6
|
+
* implements the falsifiable CI gate (see plan Phase 1 / R-T3), and renders a
|
|
7
|
+
* before/after report. No browser or network access lives here so it can be
|
|
8
|
+
* unit-tested deterministically; the harness that drives the real browser tool
|
|
9
|
+
* against vendored detector fixtures feeds its output into these functions.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type SignalStatus = "pass" | "fail";
|
|
13
|
+
|
|
14
|
+
/** A single boolean fingerprint signal, e.g. "navigator.webdriver is undefined". */
|
|
15
|
+
export interface DetectorSignal {
|
|
16
|
+
/** Stable identifier, used to diff across runs. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Human-readable label for the report. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** `pass` means the signal looks human; `fail` means it leaks automation. */
|
|
21
|
+
status: SignalStatus;
|
|
22
|
+
/** Optional observed value for debugging. */
|
|
23
|
+
detail?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type AutomatedVerdict = "human" | "bot" | "unknown";
|
|
27
|
+
|
|
28
|
+
/** Normalized result for one detector (e.g. bot.sannysoft, CreepJS). */
|
|
29
|
+
export interface DetectorResult {
|
|
30
|
+
detector: string;
|
|
31
|
+
signals: DetectorSignal[];
|
|
32
|
+
/** Overall automated/not classification when the detector exposes one. */
|
|
33
|
+
automatedVerdict: AutomatedVerdict;
|
|
34
|
+
/** Absolute trust score (observability-only, never a hard gate). */
|
|
35
|
+
trustScore: number | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Shape emitted by the offline probe fixture on `window.__stealthProbe`. */
|
|
39
|
+
export interface RawProbe {
|
|
40
|
+
detector: string;
|
|
41
|
+
signals: Array<{ id: string; label: string; pass: boolean; detail?: string }>;
|
|
42
|
+
automatedVerdict?: AutomatedVerdict;
|
|
43
|
+
trustScore?: number | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Convert a raw browser-collected probe into a normalized {@link DetectorResult}. */
|
|
47
|
+
export function parseProbe(raw: RawProbe): DetectorResult {
|
|
48
|
+
if (!raw || typeof raw.detector !== "string" || !Array.isArray(raw.signals)) {
|
|
49
|
+
throw new Error("invalid stealth probe payload");
|
|
50
|
+
}
|
|
51
|
+
const signals: DetectorSignal[] = raw.signals.map(s => ({
|
|
52
|
+
id: s.id,
|
|
53
|
+
label: s.label,
|
|
54
|
+
status: s.pass ? "pass" : "fail",
|
|
55
|
+
...(s.detail !== undefined ? { detail: s.detail } : {}),
|
|
56
|
+
}));
|
|
57
|
+
return {
|
|
58
|
+
detector: raw.detector,
|
|
59
|
+
signals,
|
|
60
|
+
automatedVerdict: raw.automatedVerdict ?? "unknown",
|
|
61
|
+
trustScore: raw.trustScore ?? null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function passCount(result: DetectorResult): number {
|
|
66
|
+
return result.signals.filter(s => s.status === "pass").length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function failingSignalIds(result: DetectorResult): Set<string> {
|
|
70
|
+
return new Set(result.signals.filter(s => s.status === "fail").map(s => s.id));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface GateOutcome {
|
|
74
|
+
pass: boolean;
|
|
75
|
+
reasons: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Falsifiable CI gate (plan Phase 1 / R-T3, R-C1, R-C2):
|
|
80
|
+
* (a) zero failing signals in the current run, AND
|
|
81
|
+
* current pass-count >= baseline pass-count (no silent regression), AND
|
|
82
|
+
* (b) either at least one signal that failed at baseline now passes,
|
|
83
|
+
* OR the baseline was already all-green (empty-gap regression lock).
|
|
84
|
+
* The absolute `trustScore` is never consulted here; it is observability-only.
|
|
85
|
+
*/
|
|
86
|
+
export function evaluateGate(baseline: DetectorResult, current: DetectorResult): GateOutcome {
|
|
87
|
+
const reasons: string[] = [];
|
|
88
|
+
const currentFail = failingSignalIds(current);
|
|
89
|
+
if (currentFail.size > 0) {
|
|
90
|
+
reasons.push(
|
|
91
|
+
`${current.detector}: ${currentFail.size} signal(s) still leak automation: ${[...currentFail].join(", ")}`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const baselinePass = passCount(baseline);
|
|
96
|
+
const currentPass = passCount(current);
|
|
97
|
+
if (currentPass < baselinePass) {
|
|
98
|
+
reasons.push(`${current.detector}: pass-count regressed (${currentPass} < baseline ${baselinePass})`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const baselineFail = failingSignalIds(baseline);
|
|
102
|
+
const currentPassIds = new Set(current.signals.filter(s => s.status === "pass").map(s => s.id));
|
|
103
|
+
const newlyPassing = [...baselineFail].filter(id => currentPassIds.has(id));
|
|
104
|
+
const baselineAllGreen = baselineFail.size === 0;
|
|
105
|
+
if (!baselineAllGreen && newlyPassing.length === 0) {
|
|
106
|
+
reasons.push(`${current.detector}: no baseline-failing signal was fixed (improvement required)`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { pass: reasons.length === 0, reasons };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Aggregate gate across every detector; passes only when all detectors pass. */
|
|
113
|
+
export function evaluateSuiteGate(
|
|
114
|
+
baseline: readonly DetectorResult[],
|
|
115
|
+
current: readonly DetectorResult[],
|
|
116
|
+
): GateOutcome {
|
|
117
|
+
const reasons: string[] = [];
|
|
118
|
+
const currentByName = new Map(current.map(r => [r.detector, r]));
|
|
119
|
+
for (const base of baseline) {
|
|
120
|
+
const now = currentByName.get(base.detector);
|
|
121
|
+
if (!now) {
|
|
122
|
+
reasons.push(`${base.detector}: missing from current run`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
reasons.push(...evaluateGate(base, now).reasons);
|
|
126
|
+
}
|
|
127
|
+
return { pass: reasons.length === 0, reasons };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Render a deterministic before/after markdown report for one run. */
|
|
131
|
+
export function renderReport(baseline: readonly DetectorResult[], current: readonly DetectorResult[]): string {
|
|
132
|
+
const lines: string[] = ["# Stealth Benchmark Report", ""];
|
|
133
|
+
const gate = evaluateSuiteGate(baseline, current);
|
|
134
|
+
lines.push(`Gate: ${gate.pass ? "PASS" : "FAIL"}`, "");
|
|
135
|
+
const currentByName = new Map(current.map(r => [r.detector, r]));
|
|
136
|
+
for (const base of baseline) {
|
|
137
|
+
const now = currentByName.get(base.detector);
|
|
138
|
+
lines.push(`## ${base.detector}`);
|
|
139
|
+
lines.push(
|
|
140
|
+
`- Automated verdict: ${base.automatedVerdict} -> ${now?.automatedVerdict ?? "n/a"}`,
|
|
141
|
+
`- Trust score (observability): ${base.trustScore ?? "n/a"} -> ${now?.trustScore ?? "n/a"}`,
|
|
142
|
+
`- Passing signals: ${passCount(base)} -> ${now ? passCount(now) : "n/a"} of ${base.signals.length}`,
|
|
143
|
+
"",
|
|
144
|
+
"| Signal | Baseline | Current |",
|
|
145
|
+
"|--------|----------|---------|",
|
|
146
|
+
);
|
|
147
|
+
for (const sig of base.signals) {
|
|
148
|
+
const nowSig = now?.signals.find(s => s.id === sig.id);
|
|
149
|
+
lines.push(`| ${sig.label} | ${sig.status} | ${nowSig?.status ?? "n/a"} |`);
|
|
150
|
+
}
|
|
151
|
+
lines.push("");
|
|
152
|
+
}
|
|
153
|
+
if (!gate.pass) {
|
|
154
|
+
lines.push("## Blocking reasons", ...gate.reasons.map(r => `- ${r}`), "");
|
|
155
|
+
}
|
|
156
|
+
return lines.join("\n");
|
|
157
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline stealth benchmark harness (plan Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* Drives a puppeteer Page (already stealth-patched by the browser tool) against
|
|
5
|
+
* self-contained offline detector fixtures and collects normalized results. The
|
|
6
|
+
* navigation/evaluation is injected so this stays unit-testable without a real
|
|
7
|
+
* browser; the integration test wires a live puppeteer Page.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Page } from "puppeteer-core";
|
|
11
|
+
import type { DetectorResult, RawProbe } from "./detector-report";
|
|
12
|
+
import { parseProbe } from "./detector-report";
|
|
13
|
+
|
|
14
|
+
/** Minimal surface of a puppeteer Page that the harness needs. */
|
|
15
|
+
export type SuitePage = Pick<Page, "goto" | "evaluate">;
|
|
16
|
+
|
|
17
|
+
export interface SuiteFixture {
|
|
18
|
+
/** file:// (or http://) URL of the offline detector fixture. */
|
|
19
|
+
url: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Read the probe the fixture placed on `window.__stealthProbe`. */
|
|
23
|
+
function readProbe(): RawProbe {
|
|
24
|
+
// Runs in the browser context.
|
|
25
|
+
const probe = (globalThis as unknown as { __stealthProbe?: RawProbe }).__stealthProbe;
|
|
26
|
+
if (!probe) throw new Error("fixture did not expose window.__stealthProbe");
|
|
27
|
+
return probe;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Navigate to each fixture and collect a normalized DetectorResult per fixture. */
|
|
31
|
+
export async function runOfflineSuite(page: SuitePage, fixtures: readonly SuiteFixture[]): Promise<DetectorResult[]> {
|
|
32
|
+
const results: DetectorResult[] = [];
|
|
33
|
+
for (const fixture of fixtures) {
|
|
34
|
+
await page.goto(fixture.url, { waitUntil: "load" });
|
|
35
|
+
const raw = await page.evaluate(readProbe);
|
|
36
|
+
results.push(parseProbe(raw));
|
|
37
|
+
}
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
@@ -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
|
+
}
|