@reticlehq/server 3.7.0 → 3.8.1
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/SKILL.md +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -1,40 +1,51 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
|
-
import { authFailureReason } from './auth-failure-reason.js';
|
|
2
|
+
import { authFailureReason, originRejectedReason } from './auth-failure-reason.js';
|
|
3
3
|
import { impactSnapshot, recordImpact } from '../../memory/impact/impact-recorder.js';
|
|
4
4
|
import { WebSocketServer } from 'ws';
|
|
5
5
|
import { HookEvent } from '@reticlehq/core/hooks';
|
|
6
6
|
import { emitSessionHook } from '../../hooks/hook-emit.js';
|
|
7
|
-
import { EventType,
|
|
7
|
+
import { EventType, RETICLE_WS_PATH, ReticleMessageSchema, LOOPBACK_HOST, MessageKind, ReticleEnv, RETICLE_PROTOCOL_VERSION, TRANSPORT_LIMITS, isLocalPage, isLoopbackHostname, isOpaqueOrigin, isHighValueEvent, RATE_CAP_HIGH_VALUE_RESERVE_RATIO, CONTRACT_FINGERPRINT, CONTRACT_PARTS, } from '@reticlehq/core';
|
|
8
8
|
import { Session } from '../session/session.js';
|
|
9
9
|
import { SessionManager } from '../session/session-manager.js';
|
|
10
|
+
import { SessionNonces } from './session-nonces.js';
|
|
11
|
+
import { FollowRefusal, OriginFollower, normalizeOrigin } from './origin/origin-follow.js';
|
|
12
|
+
import { admitHello } from '../production/production-hello.js';
|
|
13
|
+
import { forgetRecordedGaps } from '../session/recorded-gaps.js';
|
|
10
14
|
import { tokensMatch } from './token-auth.js';
|
|
11
15
|
import { noteWrongPathUpgrades, wrongPathReason } from './wrong-path.js';
|
|
12
|
-
import { harnessRequest } from './harness-request.js';
|
|
16
|
+
import { harnessRequest, isSigninRequest, isSyncRequest, replayRequest, } from './harness-request.js';
|
|
13
17
|
import { pairingTokenSource } from './pairing-token.js';
|
|
14
18
|
import { log } from '../../log.js';
|
|
15
19
|
import { getSessionMetrics } from '../../telemetry/session-metrics.js';
|
|
16
20
|
import { SESSION_PARKED_REASON, sessionReplacedReason, } from '../session/facts/session-replaced.js';
|
|
17
21
|
import { describeSdkBehind, describeSkew, sdkFix, SDK_BEHIND_NARRATION, SkewPair, } from '../../command/version/version-skew.js';
|
|
18
22
|
import { noteVersionSkew } from '../../command/version/version-nudge.js';
|
|
19
|
-
import { protocolSkewReason } from './protocol-skew.js';
|
|
23
|
+
import { helloProtocolMismatch, peekFrameKind, peekHelloPage, protocolSkewReason, } from './protocol-skew.js';
|
|
20
24
|
import { SERVER_VERSION } from '../../command/version/identity/server-version.js';
|
|
21
25
|
/**
|
|
22
|
-
* Take the mutable half of a session's identity from a repeat HELLO
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* the session's public surface, read by the tools on every listing.
|
|
27
|
-
*
|
|
28
|
-
* `hasCapabilities` is the one that matters: it is announced in the hello, which the SDK sends at
|
|
29
|
-
* connect(), while capabilities are registered AFTER connect by design (registerStore needs a live
|
|
30
|
-
* SDK to subscribe through). Without this refresh, an app that declared its entire testable surface
|
|
31
|
-
* reported `hasCapabilities: false` forever.
|
|
26
|
+
* Take the mutable half of a session's identity from a repeat HELLO; the caller has established the
|
|
27
|
+
* hello belongs to this session. Capabilities and stores register AFTER connect() by design, so
|
|
28
|
+
* `hasCapabilities` and `channels` come again here. A late project id is taken by `adoptProject`.
|
|
29
|
+
* The page announced itself again, so gaps that earlier verdicts recorded are dropped (#1655).
|
|
32
30
|
*/
|
|
33
31
|
function refreshIdentity(session, hello) {
|
|
34
32
|
session.url = hello.url;
|
|
35
33
|
session.title = hello.title;
|
|
36
34
|
session.adapters = hello.adapters;
|
|
37
35
|
session.hasCapabilities = hello.hasCapabilities ?? false;
|
|
36
|
+
session.channels = hello.channels;
|
|
37
|
+
forgetRecordedGaps(session.id);
|
|
38
|
+
}
|
|
39
|
+
/** Run every handler; one that throws is not a reason to drop the session or skip the rest. */
|
|
40
|
+
function fireEach(handlers, session) {
|
|
41
|
+
for (const handler of handlers) {
|
|
42
|
+
try {
|
|
43
|
+
handler(session);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* the session stays */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
38
49
|
}
|
|
39
50
|
/**
|
|
40
51
|
* The close reasons worth telling an AGENT about, not just the SDK.
|
|
@@ -73,39 +84,28 @@ export const WS_CLOSE_REASON = {
|
|
|
73
84
|
'opening sockets to this port.',
|
|
74
85
|
/**
|
|
75
86
|
* A page's HELLO failed the wire schema for a reason other than its protocol number — the SDK and
|
|
76
|
-
* this daemon are different releases whose contracts no longer agree. The socket is
|
|
77
|
-
*
|
|
78
|
-
* diagnosis records, because "never dialled the bridge" was what `init`
|
|
79
|
-
* daemon had just turned away. Same `cause — fix` shape as the
|
|
87
|
+
* this daemon are different releases whose contracts no longer agree. The socket is closed with
|
|
88
|
+
* INVALID_HELLO_CLOSE, the short form of this that fits the 123-byte close-reason limit; this is
|
|
89
|
+
* the sentence the no-session diagnosis records, because "never dialled the bridge" was what `init`
|
|
90
|
+
* said about a page this daemon had just turned away. Same `cause — fix` shape as the
|
|
91
|
+
* protocol-skew reasons.
|
|
80
92
|
*/
|
|
81
93
|
INVALID_HELLO: "invalid message: the page's hello does not match this daemon's wire contract — put " +
|
|
82
94
|
'@reticlehq/browser, the build plugin and @reticlehq/server on one release, run `reticle stop`, ' +
|
|
83
95
|
'then restart the dev server',
|
|
96
|
+
/**
|
|
97
|
+
* The close-frame form of INVALID_HELLO. The full sentence is far over the 123-byte close-reason
|
|
98
|
+
* limit, so this is the same `cause - fix` in one line. ASCII only: a multi-byte dash would
|
|
99
|
+
* spend the budget faster than the character count suggests.
|
|
100
|
+
*/
|
|
101
|
+
INVALID_HELLO_CLOSE: 'invalid hello: version skew. Put SDK, build plugin and server on one release.',
|
|
84
102
|
};
|
|
85
|
-
/**
|
|
86
|
-
* A dial turned away at the ORIGIN gate, in words the reader can act on.
|
|
87
|
-
*
|
|
88
|
-
* This is the refusal that used to leave no trace anywhere. It happens before any HELLO, so it
|
|
89
|
-
* produced no `noteClosure`, no `session_disconnected`, nothing but an `origin_rejected` log line —
|
|
90
|
-
* and the daemon log is the file nobody opens until they have already lost the afternoon. Reported
|
|
91
|
-
* from a multi-tenant app resolving its tenant from the `Host` header: on a hosts-file alias every
|
|
92
|
-
* dial was refused here, `doctor` reported every check green, and the lease hint blamed the port.
|
|
93
|
-
*
|
|
94
|
-
* The origin is IN the message because it is the fix: it is either the alias to allow-list, or the
|
|
95
|
-
* proof that the page is not being served from where the developer thinks it is.
|
|
96
|
-
*/
|
|
97
|
-
function originRejectedReason(origin) {
|
|
98
|
-
const named = origin === undefined || 0 === origin.length ? 'a page sending no Origin' : origin;
|
|
99
|
-
return (`a browser dialled this daemon and was REFUSED at the origin gate: ${named} is not allowed. ` +
|
|
100
|
-
'Off localhost the SDK needs BOTH `allowNonLocalhost: true` AND a pairing token, and the ' +
|
|
101
|
-
'daemon needs that origin allow-listed (RETICLE_ALLOWED_ORIGINS). The app is running and ' +
|
|
102
|
-
'instrumented — it was turned away, so do not go looking for a stopped dev server.');
|
|
103
|
-
}
|
|
104
103
|
/** WS close codes + reasons the bridge sends to the SDK (1008 = policy violation, 1013 = try again later). */
|
|
105
104
|
const WS_CLOSE = {
|
|
106
105
|
TOO_MANY_HANDSHAKES: [1013, 'too many pending handshakes'],
|
|
107
106
|
HELLO_TIMEOUT: [1008, 'hello timeout'],
|
|
108
107
|
INVALID_MESSAGE: [1008, 'invalid message'],
|
|
108
|
+
INVALID_HELLO: [1008, WS_CLOSE_REASON.INVALID_HELLO_CLOSE],
|
|
109
109
|
HELLO_DUPLICATE: [1008, 'hello already received'],
|
|
110
110
|
// AUTH_FAILED's code is 1008; the reason string is chosen at close time by authFailureReason
|
|
111
111
|
// (no token vs wrong token vs different project). A reload cannot mint a credential into a CDN
|
|
@@ -117,93 +117,23 @@ const WS_CLOSE = {
|
|
|
117
117
|
};
|
|
118
118
|
/** Parse a positive integer env override; anything else (unset, zero, junk) falls through to the default. */
|
|
119
119
|
function positiveIntFromEnv(raw) {
|
|
120
|
-
if (raw === undefined)
|
|
120
|
+
if (raw === undefined) {
|
|
121
121
|
return undefined;
|
|
122
|
+
}
|
|
122
123
|
const parsed = Number.parseInt(raw, 10);
|
|
123
124
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
124
125
|
}
|
|
125
|
-
/** The flow name if this event is a panel ▶ replay request, else undefined. Pure boundary narrowing. */
|
|
126
|
-
function replayRequest(event) {
|
|
127
|
-
if (event.type !== EventType.HUMAN_CONTROL)
|
|
128
|
-
return undefined;
|
|
129
|
-
if (event.data['kind'] !== HumanControlKind.REPLAY)
|
|
130
|
-
return undefined;
|
|
131
|
-
const name = event.data['text'];
|
|
132
|
-
return 'string' === typeof name && name.length > 0 ? name : undefined;
|
|
133
|
-
}
|
|
134
|
-
/** True when this event is the panel's Sign in. Pure boundary narrowing, like the above. */
|
|
135
|
-
function isSigninRequest(event) {
|
|
136
|
-
return event.type === EventType.HUMAN_CONTROL && event.data['kind'] === HumanControlKind.SIGNIN;
|
|
137
|
-
}
|
|
138
|
-
/** True when this event is the panel's "sync now" button. Pure boundary narrowing, like the above. */
|
|
139
|
-
function isSyncRequest(event) {
|
|
140
|
-
return event.type === EventType.HUMAN_CONTROL && event.data['kind'] === HumanControlKind.SYNC;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Canonical comparable form of a WS `Origin`. Web origins collapse to scheme://host[:port]. An
|
|
144
|
-
* OPAQUE origin — a desktop webview (`tauri://localhost`, `app://.`) or `file://`, whose `URL.origin`
|
|
145
|
-
* is the string "null" — is kept VERBATIM instead, so it survives into the allow-list and can be
|
|
146
|
-
* compared. Collapsing those to "null" would both merge every desktop scheme into one bucket and
|
|
147
|
-
* feed an unparseable string to `new URL`. Returns null only for a header that is not an origin.
|
|
148
|
-
*/
|
|
149
|
-
function normalizeOrigin(origin) {
|
|
150
|
-
if (origin === OPAQUE_ORIGIN)
|
|
151
|
-
return OPAQUE_ORIGIN;
|
|
152
|
-
try {
|
|
153
|
-
const web = new URL(origin).origin;
|
|
154
|
-
return web === OPAQUE_ORIGIN ? origin : web;
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* When a message fails the strict schema, detect the specific case of a HELLO whose protocolVersion
|
|
162
|
-
* differs from ours — so we can tell the operator "upgrade @reticlehq/browser" instead of dropping the
|
|
163
|
-
* connection into the generic NO_SESSION_CONNECTED path, which misdiagnoses it as a port mismatch.
|
|
164
|
-
* Returns the mismatched version, or null when it isn't a version-mismatched HELLO.
|
|
165
|
-
*/
|
|
166
|
-
function helloProtocolMismatch(text) {
|
|
167
|
-
try {
|
|
168
|
-
const json = JSON.parse(text);
|
|
169
|
-
if (json.kind === MessageKind.HELLO &&
|
|
170
|
-
'number' === typeof json.protocolVersion &&
|
|
171
|
-
json.protocolVersion !== RETICLE_PROTOCOL_VERSION) {
|
|
172
|
-
return json.protocolVersion;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
/* not JSON — fall through to the generic invalid-message path */
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* What a HELLO says about the page it came from, read loosely enough to survive the schema failing.
|
|
182
|
-
* Null when the message is not a hello at all — only an SDK sends one, so only then is a refusal
|
|
183
|
-
* evidence about an app.
|
|
184
|
-
*/
|
|
185
|
-
function peekHelloPage(text) {
|
|
186
|
-
try {
|
|
187
|
-
const json = JSON.parse(text);
|
|
188
|
-
if (json.kind !== MessageKind.HELLO)
|
|
189
|
-
return null;
|
|
190
|
-
return {
|
|
191
|
-
...('string' === typeof json.url ? { url: json.url } : {}),
|
|
192
|
-
...('string' === typeof json.projectId ? { projectId: json.projectId } : {}),
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
126
|
/** Normalize ws RawData (string | Buffer | Buffer[] | ArrayBuffer) into a UTF-8 string. */
|
|
200
127
|
function rawToString(raw) {
|
|
201
|
-
if ('string' === typeof raw)
|
|
128
|
+
if ('string' === typeof raw) {
|
|
202
129
|
return raw;
|
|
203
|
-
|
|
130
|
+
}
|
|
131
|
+
if (Array.isArray(raw)) {
|
|
204
132
|
return Buffer.concat(raw).toString('utf8');
|
|
205
|
-
|
|
133
|
+
}
|
|
134
|
+
if (raw instanceof ArrayBuffer) {
|
|
206
135
|
return Buffer.from(raw).toString('utf8');
|
|
136
|
+
}
|
|
207
137
|
return raw.toString('utf8');
|
|
208
138
|
}
|
|
209
139
|
/**
|
|
@@ -213,6 +143,8 @@ function rawToString(raw) {
|
|
|
213
143
|
*/
|
|
214
144
|
export class Bridge {
|
|
215
145
|
sessions = new SessionManager();
|
|
146
|
+
/** Single-use HELLO tokens for pages that cannot hold the pairing token. Minted by local callers only. */
|
|
147
|
+
nonces;
|
|
216
148
|
/** Resolves with the actually-bound port once the server is listening. */
|
|
217
149
|
ready;
|
|
218
150
|
#wss;
|
|
@@ -234,6 +166,7 @@ export class Bridge {
|
|
|
234
166
|
#maxPendingConnections;
|
|
235
167
|
#helloTimeoutMs;
|
|
236
168
|
#sdkFix;
|
|
169
|
+
#follower;
|
|
237
170
|
#pendingConnections = 0;
|
|
238
171
|
#onReplay;
|
|
239
172
|
/**
|
|
@@ -256,6 +189,7 @@ export class Bridge {
|
|
|
256
189
|
* stops working, quietly" is a trap; anything that needs to run on a new session now simply runs.
|
|
257
190
|
*/
|
|
258
191
|
#onSessionCreate = [];
|
|
192
|
+
#onProjectAdopted = [];
|
|
259
193
|
/** Fired when a session is removed — flushes its journal tail + persists what it learned. */
|
|
260
194
|
#onSessionEnd;
|
|
261
195
|
/** Wired by the daemon: push to the dashboard now, because somebody asked in the panel. */
|
|
@@ -271,6 +205,7 @@ export class Bridge {
|
|
|
271
205
|
throw new Error('a pairing token is required when the Reticle bridge binds beyond localhost');
|
|
272
206
|
}
|
|
273
207
|
this.#clock = options.clock ?? (() => Date.now());
|
|
208
|
+
this.nonces = new SessionNonces({ clock: this.#clock });
|
|
274
209
|
this.#token =
|
|
275
210
|
options.token !== undefined && options.token.length > 0 ? options.token : undefined;
|
|
276
211
|
// Supplied by resolveBridgeSecurity, which is the only place that can tell an auto-provisioned
|
|
@@ -320,6 +255,10 @@ export class Bridge {
|
|
|
320
255
|
options.maxPendingConnections ?? TRANSPORT_LIMITS.MAX_PENDING_CONNECTIONS;
|
|
321
256
|
this.#helloTimeoutMs = options.helloTimeoutMs ?? TRANSPORT_LIMITS.HELLO_TIMEOUT_MS;
|
|
322
257
|
this.#sdkFix = options.sdkFix ?? (() => sdkFix(SERVER_VERSION));
|
|
258
|
+
this.#follower =
|
|
259
|
+
options.followNavigation === undefined
|
|
260
|
+
? undefined
|
|
261
|
+
: new OriginFollower(options.followNavigation);
|
|
323
262
|
if (options.server !== undefined) {
|
|
324
263
|
const srv = options.server;
|
|
325
264
|
this.#wss = new WebSocketServer({
|
|
@@ -327,9 +266,10 @@ export class Bridge {
|
|
|
327
266
|
path: RETICLE_WS_PATH,
|
|
328
267
|
maxPayload: TRANSPORT_LIMITS.MAX_MESSAGE_BYTES,
|
|
329
268
|
verifyClient: ({ origin }, done) => {
|
|
330
|
-
const allowed = this.#originAllowed(origin);
|
|
331
|
-
if (!allowed)
|
|
269
|
+
const allowed = this.#originAllowed(origin) || this.#mayFollow(origin);
|
|
270
|
+
if (!allowed) {
|
|
332
271
|
this.#noteOriginRejected(origin);
|
|
272
|
+
}
|
|
333
273
|
done(allowed, 403, 'Forbidden');
|
|
334
274
|
},
|
|
335
275
|
});
|
|
@@ -358,9 +298,10 @@ export class Bridge {
|
|
|
358
298
|
path: RETICLE_WS_PATH,
|
|
359
299
|
maxPayload: TRANSPORT_LIMITS.MAX_MESSAGE_BYTES,
|
|
360
300
|
verifyClient: ({ origin }, done) => {
|
|
361
|
-
const allowed = this.#originAllowed(origin);
|
|
362
|
-
if (!allowed)
|
|
301
|
+
const allowed = this.#originAllowed(origin) || this.#mayFollow(origin);
|
|
302
|
+
if (!allowed) {
|
|
363
303
|
this.#noteOriginRejected(origin);
|
|
304
|
+
}
|
|
364
305
|
done(allowed, 403, 'Forbidden');
|
|
365
306
|
},
|
|
366
307
|
});
|
|
@@ -394,11 +335,14 @@ export class Bridge {
|
|
|
394
335
|
this.sessions.noteClosure(wrongPathReason(path, origin), this.#clock());
|
|
395
336
|
}
|
|
396
337
|
});
|
|
397
|
-
this.#wss.on('connection', (socket) => {
|
|
398
|
-
this.#onConnection(socket);
|
|
338
|
+
this.#wss.on('connection', (socket, request) => {
|
|
339
|
+
this.#onConnection(socket, request.headers.origin);
|
|
399
340
|
});
|
|
400
341
|
}
|
|
401
|
-
#onConnection(socket) {
|
|
342
|
+
#onConnection(socket, origin) {
|
|
343
|
+
// The socket's origin, and whether it is in only on the follow path (decided at its HELLO).
|
|
344
|
+
const dialled = origin === undefined ? null : normalizeOrigin(origin);
|
|
345
|
+
const following = dialled !== null && !this.#originAllowed(origin) ? dialled : undefined;
|
|
402
346
|
if (this.#pendingConnections >= this.#maxPendingConnections) {
|
|
403
347
|
// On the channel the no-session diagnosis reads, not only into the log. This refusal happens
|
|
404
348
|
// before any HELLO, so without it the dial leaves no trace at all — the same hole the origin
|
|
@@ -416,14 +360,16 @@ export class Bridge {
|
|
|
416
360
|
let highValueInWindow = 0;
|
|
417
361
|
const highValueReserve = Math.ceil(this.#maxMessagesPerSecond * RATE_CAP_HIGH_VALUE_RESERVE_RATIO);
|
|
418
362
|
const releasePending = () => {
|
|
419
|
-
if (!awaitingHello)
|
|
363
|
+
if (!awaitingHello) {
|
|
420
364
|
return;
|
|
365
|
+
}
|
|
421
366
|
awaitingHello = false;
|
|
422
367
|
this.#pendingConnections -= 1;
|
|
423
368
|
};
|
|
424
369
|
const helloTimer = setTimeout(() => {
|
|
425
|
-
if (!awaitingHello)
|
|
370
|
+
if (!awaitingHello) {
|
|
426
371
|
return;
|
|
372
|
+
}
|
|
427
373
|
releasePending();
|
|
428
374
|
socket.close(...WS_CLOSE.HELLO_TIMEOUT);
|
|
429
375
|
}, this.#helloTimeoutMs);
|
|
@@ -492,7 +438,8 @@ export class Bridge {
|
|
|
492
438
|
});
|
|
493
439
|
return;
|
|
494
440
|
}
|
|
495
|
-
|
|
441
|
+
const [code, closeReason] = null !== refused ? WS_CLOSE.INVALID_HELLO : WS_CLOSE.INVALID_MESSAGE;
|
|
442
|
+
socket.close(code, closeReason);
|
|
496
443
|
return;
|
|
497
444
|
}
|
|
498
445
|
// Over the cap we SAMPLE — we never disconnect. Going blind is the one thing an observability
|
|
@@ -515,8 +462,9 @@ export class Bridge {
|
|
|
515
462
|
}
|
|
516
463
|
else {
|
|
517
464
|
droppedByRate += 1;
|
|
518
|
-
if (session !== undefined)
|
|
465
|
+
if (session !== undefined) {
|
|
519
466
|
session.noteRateLimited(droppedByRate);
|
|
467
|
+
}
|
|
520
468
|
return;
|
|
521
469
|
}
|
|
522
470
|
}
|
|
@@ -532,9 +480,16 @@ export class Bridge {
|
|
|
532
480
|
return;
|
|
533
481
|
}
|
|
534
482
|
refreshIdentity(session, parsed);
|
|
483
|
+
if (parsed.projectId !== undefined && session.adoptProject(parsed.projectId)) {
|
|
484
|
+
this.#servedProjects.add(parsed.projectId);
|
|
485
|
+
fireEach(this.#onProjectAdopted, session);
|
|
486
|
+
}
|
|
535
487
|
return;
|
|
536
488
|
}
|
|
537
|
-
|
|
489
|
+
const grant = this.nonces.redeem(parsed.token, parsed.sessionId);
|
|
490
|
+
if (grant === undefined &&
|
|
491
|
+
this.#token !== undefined &&
|
|
492
|
+
!tokensMatch(this.#token, parsed.token)) {
|
|
538
493
|
// A daemon left running by ANOTHER project answers this app and rejects it on token. The
|
|
539
494
|
// token is not wrong, it is somebody else's — and "authentication failed" sends the user to
|
|
540
495
|
// check the one thing that is fine. See auth-failure-reason.
|
|
@@ -557,6 +512,26 @@ export class Bridge {
|
|
|
557
512
|
socket.close(WS_CLOSE.AUTH_FAILED[0], reason);
|
|
558
513
|
return;
|
|
559
514
|
}
|
|
515
|
+
let notFollowed;
|
|
516
|
+
if (following !== undefined) {
|
|
517
|
+
notFollowed =
|
|
518
|
+
this.#follower === undefined
|
|
519
|
+
? FollowRefusal.POLICY_REFUSED
|
|
520
|
+
: this.#follower.refusal(parsed, following);
|
|
521
|
+
}
|
|
522
|
+
if (notFollowed !== undefined) {
|
|
523
|
+
log('origin_follow_refused', { sessionId: parsed.sessionId, origin: following });
|
|
524
|
+
this.sessions.noteClosure(notFollowed, this.#clock());
|
|
525
|
+
socket.close(1008, notFollowed);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
// The nonce decides whether this is a production session, never the page's own claim.
|
|
529
|
+
const admission = admitHello(parsed, grant?.production);
|
|
530
|
+
if (!admission.ok) {
|
|
531
|
+
this.sessions.noteClosure(admission.reason, this.#clock(), { url: parsed.url });
|
|
532
|
+
socket.close(1008, admission.reason);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
560
535
|
// A window the agent detached stays detached across its own reloads. See SessionManager.park.
|
|
561
536
|
if (this.sessions.isParked(parsed.sessionId)) {
|
|
562
537
|
socket.close(1008, SESSION_PARKED_REASON);
|
|
@@ -570,20 +545,21 @@ export class Bridge {
|
|
|
570
545
|
clearTimeout(helloTimer);
|
|
571
546
|
releasePending();
|
|
572
547
|
session = new Session(parsed, socket, this.#clock);
|
|
548
|
+
session.production = admission.production;
|
|
549
|
+
const drive = admission.production;
|
|
550
|
+
if (drive !== undefined) {
|
|
551
|
+
session.onEvent((event) => drive.noteEvent(event));
|
|
552
|
+
}
|
|
553
|
+
// A reload keeps the session id. Its gaps describe the old page, not this one (#1655).
|
|
554
|
+
forgetRecordedGaps(session.id);
|
|
573
555
|
// Recorded on ACCEPTANCE, so it is evidence of what this daemon really serves.
|
|
574
|
-
if (parsed.projectId !== undefined)
|
|
556
|
+
if (parsed.projectId !== undefined) {
|
|
575
557
|
this.#servedProjects.add(parsed.projectId);
|
|
558
|
+
}
|
|
576
559
|
// Attach the durable journal (and anything else registered) before any events stream in.
|
|
577
560
|
// One throwing handler must not stop the others, or the order of registration would decide
|
|
578
561
|
// which features survive a bad frame.
|
|
579
|
-
|
|
580
|
-
try {
|
|
581
|
-
handler(session);
|
|
582
|
-
}
|
|
583
|
-
catch {
|
|
584
|
-
/* a handler that failed is not a reason to drop the session */
|
|
585
|
-
}
|
|
586
|
-
}
|
|
562
|
+
fireEach(this.#onSessionCreate, session);
|
|
587
563
|
// A tab that has just connected has no impact record yet, so the report would read "nothing
|
|
588
564
|
// recorded" over a file with a month of history in it. Push what is already on disk as soon
|
|
589
565
|
// as there is somewhere to push it to; a session is also a thing that HAPPENED, so it counts.
|
|
@@ -601,6 +577,10 @@ export class Bridge {
|
|
|
601
577
|
const attached = session;
|
|
602
578
|
attached.pushImpact(() => impactSnapshot(attached.artifactRoot), true);
|
|
603
579
|
const replaced = this.sessions.add(session);
|
|
580
|
+
this.#follower?.remember(session.id, dialled);
|
|
581
|
+
if (following !== undefined) {
|
|
582
|
+
log('origin_followed', { sessionId: session.id, to: following });
|
|
583
|
+
}
|
|
604
584
|
// Said out loud once the session is actually in the registry, never before: a hook that
|
|
605
585
|
// fires on a session a consumer cannot then look up describes a moment that did not happen.
|
|
606
586
|
emitSessionHook(HookEvent.SESSION_STARTED, {
|
|
@@ -655,14 +635,9 @@ export class Bridge {
|
|
|
655
635
|
// a channel nobody declared, rather than answering it with an empty result that reads
|
|
656
636
|
// exactly like the value not being there.
|
|
657
637
|
session.channels = parsed.channels;
|
|
658
|
-
// What it will answer,
|
|
659
|
-
//
|
|
660
|
-
//
|
|
661
|
-
// `parsed.platform` is deliberately NOT kept. It is a protocol field and it is correct
|
|
662
|
-
// on the wire, but this daemon already learns the runtime from the health event and has
|
|
663
|
-
// no decision that needs the answer sooner. Storing it would be a handshake fact nobody
|
|
664
|
-
// reads, which is the shape this codebase has now produced twice in one week -- see
|
|
665
|
-
// handshake-facts-are-read.test.ts, which exists because of it.
|
|
638
|
+
// What it will answer, so a command the page does not serve is refused, not waited out.
|
|
639
|
+
// `parsed.platform` is NOT kept: the health event names the runtime, and a handshake fact
|
|
640
|
+
// nobody reads is a defect (see handshake-facts-are-read.test.ts).
|
|
666
641
|
session.commands = parsed.commands;
|
|
667
642
|
if (skew !== undefined) {
|
|
668
643
|
log('version_skew', {
|
|
@@ -687,7 +662,7 @@ export class Bridge {
|
|
|
687
662
|
// The one fact that separates a broken install from an unused one. Best-effort and wrapped:
|
|
688
663
|
// a metric must never affect whether an app can connect. See SessionSummary.appConnects.
|
|
689
664
|
try {
|
|
690
|
-
getSessionMetrics().recordAppConnected();
|
|
665
|
+
getSessionMetrics().recordAppConnected(parsed.sdkVersion);
|
|
691
666
|
}
|
|
692
667
|
catch {
|
|
693
668
|
/* never let a counter interfere with an app connecting */
|
|
@@ -703,8 +678,9 @@ export class Bridge {
|
|
|
703
678
|
}
|
|
704
679
|
return;
|
|
705
680
|
}
|
|
706
|
-
if (session === undefined)
|
|
681
|
+
if (session === undefined) {
|
|
707
682
|
return;
|
|
683
|
+
}
|
|
708
684
|
session.touch();
|
|
709
685
|
if (parsed.kind === MessageKind.EVENT) {
|
|
710
686
|
// A panel ▶ replay needs the daemon's flow store, which the Session can't reach — route it to
|
|
@@ -714,23 +690,28 @@ export class Bridge {
|
|
|
714
690
|
// test that decides whether this is a panel control at all, and calling it twice would
|
|
715
691
|
// invite the two calls to disagree.
|
|
716
692
|
const harness = harnessRequest(parsed.event);
|
|
717
|
-
if (replay !== undefined)
|
|
693
|
+
if (replay !== undefined) {
|
|
718
694
|
this.#onReplay?.(session.id, replay);
|
|
695
|
+
}
|
|
719
696
|
// Same shape as ▶ replay, and routed the same way: what to push lives in the daemon's
|
|
720
697
|
// cloud wiring, which a Session cannot reach. Nothing is awaited — the panel is telling
|
|
721
698
|
// us to hurry, not asking for a result.
|
|
722
|
-
else if (isSyncRequest(parsed.event))
|
|
699
|
+
else if (isSyncRequest(parsed.event)) {
|
|
723
700
|
this.#onSyncRequest?.();
|
|
701
|
+
}
|
|
724
702
|
// The switch is written through to the platform by the daemon, for the same reason: the
|
|
725
703
|
// Session has no cloud credential and should not grow one.
|
|
726
|
-
else if (harness !== undefined)
|
|
704
|
+
else if (harness !== undefined) {
|
|
727
705
|
this.#onHarnessRequest?.(harness, session);
|
|
706
|
+
}
|
|
728
707
|
// Signing in writes `~/.reticle`, which only the daemon may do; the panel only asks.
|
|
729
|
-
else if (isSigninRequest(parsed.event))
|
|
708
|
+
else if (isSigninRequest(parsed.event)) {
|
|
730
709
|
this.#onSigninRequest?.(session);
|
|
710
|
+
}
|
|
731
711
|
// Pass the raw frame's byte length so the buffer doesn't re-serialize every event for accounting.
|
|
732
|
-
else
|
|
712
|
+
else {
|
|
733
713
|
session.pushEvent(parsed.event, Buffer.byteLength(text, 'utf8'));
|
|
714
|
+
}
|
|
734
715
|
}
|
|
735
716
|
else if (parsed.kind === MessageKind.COMMAND_RESULT) {
|
|
736
717
|
session.handleResult(parsed);
|
|
@@ -786,22 +767,30 @@ export class Bridge {
|
|
|
786
767
|
log('origin_rejected', { origin: origin ?? 'missing' });
|
|
787
768
|
this.sessions.noteClosure(originRejectedReason(origin), this.#clock());
|
|
788
769
|
}
|
|
770
|
+
/** Admit the handshake so the HELLO can be judged. Only with a host policy and a web origin. */
|
|
771
|
+
#mayFollow(origin) {
|
|
772
|
+
return this.#follower?.mayDial(origin === undefined ? null : normalizeOrigin(origin)) ?? false;
|
|
773
|
+
}
|
|
789
774
|
#originAllowed(origin) {
|
|
790
775
|
// A real browser SDK connection always carries an Origin. A missing Origin means a non-browser
|
|
791
776
|
// local process (another app, a malicious npm postinstall, an extension's native host) — untrusted
|
|
792
777
|
// unless a pairing token is required, in which case the HELLO token check is the real gate.
|
|
793
|
-
if (origin === undefined)
|
|
778
|
+
if (origin === undefined) {
|
|
794
779
|
return this.#token !== undefined;
|
|
780
|
+
}
|
|
795
781
|
const normalized = normalizeOrigin(origin);
|
|
796
|
-
if (null === normalized)
|
|
782
|
+
if (null === normalized) {
|
|
797
783
|
return false;
|
|
798
|
-
|
|
784
|
+
}
|
|
785
|
+
if (this.#allowedOrigins.has(normalized) || this.nonces.admitsOrigin(normalized)) {
|
|
799
786
|
return true;
|
|
787
|
+
}
|
|
800
788
|
// A desktop webview (Electron, Tauri) or a file:// document sends an opaque Origin: no host to
|
|
801
789
|
// check, so it is exactly as attributable as a missing Origin — defer to the token, as above.
|
|
802
790
|
// Without this branch `new URL` throws INSIDE the WS upgrade handler, crashing the process.
|
|
803
|
-
if (isOpaqueOrigin(normalized))
|
|
791
|
+
if (isOpaqueOrigin(normalized)) {
|
|
804
792
|
return this.#token !== undefined;
|
|
793
|
+
}
|
|
805
794
|
// `isLocalPage`, not `isLoopbackHostname` — the SAME rule the SDK applies on the page side.
|
|
806
795
|
//
|
|
807
796
|
// They had drifted, and the drift made a whole platform unreachable. Tauri v2 uses an opaque
|
|
@@ -881,34 +870,18 @@ export class Bridge {
|
|
|
881
870
|
attachSessionCreate(handler) {
|
|
882
871
|
this.#onSessionCreate.push(handler);
|
|
883
872
|
}
|
|
873
|
+
/** Run `handler` when a session that connected without a project id names one in a later HELLO. */
|
|
874
|
+
attachProjectAdopted(handler) {
|
|
875
|
+
this.#onProjectAdopted.push(handler);
|
|
876
|
+
}
|
|
884
877
|
close() {
|
|
885
878
|
return new Promise((resolve) => {
|
|
886
|
-
for (const client of this.#wss.clients)
|
|
879
|
+
for (const client of this.#wss.clients) {
|
|
887
880
|
client.terminate();
|
|
881
|
+
}
|
|
888
882
|
this.#wss.close(() => {
|
|
889
883
|
resolve();
|
|
890
884
|
});
|
|
891
885
|
});
|
|
892
886
|
}
|
|
893
887
|
}
|
|
894
|
-
/**
|
|
895
|
-
* The `kind` and event `type` of a frame that failed the schema, for the log: never its payload,
|
|
896
|
-
* which can carry anything the page sent. Each is a bounded string or absent.
|
|
897
|
-
*/
|
|
898
|
-
function peekFrameKind(text) {
|
|
899
|
-
const short = (v) => 'string' === typeof v ? v.slice(0, MAX_LOGGED_FRAME_FIELD) : undefined;
|
|
900
|
-
try {
|
|
901
|
-
const json = JSON.parse(text);
|
|
902
|
-
const kind = short(json?.kind);
|
|
903
|
-
const eventType = short(json?.event?.type);
|
|
904
|
-
return {
|
|
905
|
-
...(kind === undefined ? {} : { kind }),
|
|
906
|
-
...(eventType === undefined ? {} : { eventType }),
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
catch {
|
|
910
|
-
return {};
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
/** How much of a frame's `kind` or event `type` the drop log keeps. */
|
|
914
|
-
const MAX_LOGGED_FRAME_FIELD = 64;
|
|
@@ -21,3 +21,18 @@ export declare function harnessRequest(event: {
|
|
|
21
21
|
type: string;
|
|
22
22
|
data: Record<string, unknown>;
|
|
23
23
|
}): HarnessRequest | undefined;
|
|
24
|
+
/** The flow name if this event is a panel ▶ replay request, else undefined. Pure boundary narrowing. */
|
|
25
|
+
export declare function replayRequest(event: {
|
|
26
|
+
type: string;
|
|
27
|
+
data: Record<string, unknown>;
|
|
28
|
+
}): string | undefined;
|
|
29
|
+
/** True when this event is the panel's Sign in. Pure boundary narrowing, like the above. */
|
|
30
|
+
export declare function isSigninRequest(event: {
|
|
31
|
+
type: string;
|
|
32
|
+
data: Record<string, unknown>;
|
|
33
|
+
}): boolean;
|
|
34
|
+
/** True when this event is the panel's "sync now" button. Pure boundary narrowing, like the above. */
|
|
35
|
+
export declare function isSyncRequest(event: {
|
|
36
|
+
type: string;
|
|
37
|
+
data: Record<string, unknown>;
|
|
38
|
+
}): boolean;
|
|
@@ -25,3 +25,22 @@ export function harnessRequest(event) {
|
|
|
25
25
|
return { kind, enabled: false };
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
|
+
/** The flow name if this event is a panel ▶ replay request, else undefined. Pure boundary narrowing. */
|
|
29
|
+
export function replayRequest(event) {
|
|
30
|
+
if (event.type !== EventType.HUMAN_CONTROL) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (event.data['kind'] !== HumanControlKind.REPLAY) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const name = event.data['text'];
|
|
37
|
+
return 'string' === typeof name && name.length > 0 ? name : undefined;
|
|
38
|
+
}
|
|
39
|
+
/** True when this event is the panel's Sign in. Pure boundary narrowing, like the above. */
|
|
40
|
+
export function isSigninRequest(event) {
|
|
41
|
+
return event.type === EventType.HUMAN_CONTROL && event.data['kind'] === HumanControlKind.SIGNIN;
|
|
42
|
+
}
|
|
43
|
+
/** True when this event is the panel's "sync now" button. Pure boundary narrowing, like the above. */
|
|
44
|
+
export function isSyncRequest(event) {
|
|
45
|
+
return event.type === EventType.HUMAN_CONTROL && event.data['kind'] === HumanControlKind.SYNC;
|
|
46
|
+
}
|