@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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical comparable form of a WS `Origin`. Web origins collapse to scheme://host[:port]. An
|
|
3
|
+
* OPAQUE origin — a desktop webview (`tauri://localhost`, `app://.`) or `file://`, whose `URL.origin`
|
|
4
|
+
* is the string "null" — is kept VERBATIM instead, so it survives into the allow-list and can be
|
|
5
|
+
* compared. Collapsing those to "null" would both merge every desktop scheme into one bucket and
|
|
6
|
+
* feed an unparseable string to `new URL`. Returns null only for a header that is not an origin.
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeOrigin(origin: string): string | null;
|
|
9
|
+
/** One move the host is asked to allow. */
|
|
10
|
+
export interface OriginFollowRequest {
|
|
11
|
+
/** The session the new page claims, as its HELLO names it. */
|
|
12
|
+
sessionId: string;
|
|
13
|
+
/** The origin this session was last admitted from. */
|
|
14
|
+
from: string;
|
|
15
|
+
/** The origin the new page dialled from. */
|
|
16
|
+
to: string;
|
|
17
|
+
/** The page address the new page reports. Its origin is `to`. */
|
|
18
|
+
url: string;
|
|
19
|
+
}
|
|
20
|
+
/** The host's decision. Return `true` only for a navigation the host's own drive caused. */
|
|
21
|
+
export type FollowNavigation = (request: OriginFollowRequest) => boolean;
|
|
22
|
+
/** True for an http(s) origin. Only a web page can follow; opaque and other schemes cannot. */
|
|
23
|
+
export declare function isWebOrigin(origin: string): boolean;
|
|
24
|
+
export declare class OriginFollower {
|
|
25
|
+
#private;
|
|
26
|
+
constructor(policy: FollowNavigation);
|
|
27
|
+
/** True when a page on this normalized origin may be judged at its HELLO: web origins only. */
|
|
28
|
+
mayDial(origin: string | null): boolean;
|
|
29
|
+
/** Record the origin a session was admitted from. Non-web origins are never followed. */
|
|
30
|
+
remember(sessionId: string, origin: string | null): void;
|
|
31
|
+
/**
|
|
32
|
+
* Why this HELLO may not follow to `to`, or undefined when it may.
|
|
33
|
+
*
|
|
34
|
+
* A policy that throws refuses: a failure to decide is never permission.
|
|
35
|
+
*/
|
|
36
|
+
refusal(hello: {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
url: string;
|
|
39
|
+
}, to: string): string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Close reasons for a refused follow. Each is under the 123-byte limit of a close frame. */
|
|
42
|
+
export declare const FollowRefusal: {
|
|
43
|
+
readonly UNKNOWN_SESSION: "refused: a page on a new origin may only continue a session this bridge admitted";
|
|
44
|
+
readonly URL_MISMATCH: "refused: the page address is not on the origin the socket dialled from";
|
|
45
|
+
readonly POLICY_REFUSED: "refused: the host did not allow this session to follow to the new origin";
|
|
46
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Let a session follow its own top-level navigation to another origin, when the host says so.
|
|
3
|
+
*
|
|
4
|
+
* The bridge checks a WebSocket's `Origin` against an allow-list fixed when the bridge is built. A
|
|
5
|
+
* daemon on a developer's machine never needs more: the app is on localhost. A hosted verifier does.
|
|
6
|
+
* A journey that starts on `www.wikipedia.org` and clicks through to `en.wikipedia.org` loads a new
|
|
7
|
+
* page on a new origin, and the injected SDK in it dials the same bridge with the same session id.
|
|
8
|
+
* The bridge refused it, so the drive lost its session on the step it was there to check.
|
|
9
|
+
*
|
|
10
|
+
* Nothing is followed by default. The host passes `followNavigation`, and the bridge asks it only
|
|
11
|
+
* after every other gate has passed. A page on an origin nobody admitted may follow only when ALL of
|
|
12
|
+
* these hold:
|
|
13
|
+
*
|
|
14
|
+
* - its HELLO names a session this bridge already admitted: first from an allowed origin, then from
|
|
15
|
+
* each origin it followed to. A new origin can never START a session, so an arbitrary page cannot
|
|
16
|
+
* open one of its own;
|
|
17
|
+
* - its HELLO passed the token or nonce check, the same as any other page;
|
|
18
|
+
* - the page address in the HELLO is on the origin the socket came from;
|
|
19
|
+
* - the host returns `true` for this move. The host is the one party that knows whether its own
|
|
20
|
+
* drive caused the navigation (its browser's top frame is now on `to`), so it decides.
|
|
21
|
+
*/
|
|
22
|
+
import { OPAQUE_ORIGIN } from '@reticlehq/core';
|
|
23
|
+
/**
|
|
24
|
+
* Canonical comparable form of a WS `Origin`. Web origins collapse to scheme://host[:port]. An
|
|
25
|
+
* OPAQUE origin — a desktop webview (`tauri://localhost`, `app://.`) or `file://`, whose `URL.origin`
|
|
26
|
+
* is the string "null" — is kept VERBATIM instead, so it survives into the allow-list and can be
|
|
27
|
+
* compared. Collapsing those to "null" would both merge every desktop scheme into one bucket and
|
|
28
|
+
* feed an unparseable string to `new URL`. Returns null only for a header that is not an origin.
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeOrigin(origin) {
|
|
31
|
+
if (origin === OPAQUE_ORIGIN)
|
|
32
|
+
return OPAQUE_ORIGIN;
|
|
33
|
+
try {
|
|
34
|
+
const web = new URL(origin).origin;
|
|
35
|
+
return web === OPAQUE_ORIGIN ? origin : web;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Origins remembered per session. Bounded so a long-lived bridge does not grow without end. */
|
|
42
|
+
const MAX_REMEMBERED = 1000;
|
|
43
|
+
const WEB_PROTOCOLS = new Set(['http:', 'https:']);
|
|
44
|
+
/** True for an http(s) origin. Only a web page can follow; opaque and other schemes cannot. */
|
|
45
|
+
export function isWebOrigin(origin) {
|
|
46
|
+
try {
|
|
47
|
+
return WEB_PROTOCOLS.has(new URL(origin).protocol);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function originOf(url) {
|
|
54
|
+
try {
|
|
55
|
+
return new URL(url).origin;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class OriginFollower {
|
|
62
|
+
#policy;
|
|
63
|
+
/** The origin each session was last admitted from. Insertion order is age, for the bound. */
|
|
64
|
+
#admitted = new Map();
|
|
65
|
+
constructor(policy) {
|
|
66
|
+
this.#policy = policy;
|
|
67
|
+
}
|
|
68
|
+
/** True when a page on this normalized origin may be judged at its HELLO: web origins only. */
|
|
69
|
+
mayDial(origin) {
|
|
70
|
+
return origin !== null && isWebOrigin(origin);
|
|
71
|
+
}
|
|
72
|
+
/** Record the origin a session was admitted from. Non-web origins are never followed. */
|
|
73
|
+
remember(sessionId, origin) {
|
|
74
|
+
if (!this.mayDial(origin) || null === origin)
|
|
75
|
+
return;
|
|
76
|
+
this.#admitted.delete(sessionId);
|
|
77
|
+
this.#admitted.set(sessionId, origin);
|
|
78
|
+
if (this.#admitted.size > MAX_REMEMBERED) {
|
|
79
|
+
const oldest = this.#admitted.keys().next().value;
|
|
80
|
+
if (oldest !== undefined)
|
|
81
|
+
this.#admitted.delete(oldest);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Why this HELLO may not follow to `to`, or undefined when it may.
|
|
86
|
+
*
|
|
87
|
+
* A policy that throws refuses: a failure to decide is never permission.
|
|
88
|
+
*/
|
|
89
|
+
refusal(hello, to) {
|
|
90
|
+
const from = this.#admitted.get(hello.sessionId);
|
|
91
|
+
if (from === undefined)
|
|
92
|
+
return FollowRefusal.UNKNOWN_SESSION;
|
|
93
|
+
if (originOf(hello.url) !== to)
|
|
94
|
+
return FollowRefusal.URL_MISMATCH;
|
|
95
|
+
let allowed = false;
|
|
96
|
+
try {
|
|
97
|
+
allowed = this.#policy({ sessionId: hello.sessionId, from, to, url: hello.url });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
allowed = false;
|
|
101
|
+
}
|
|
102
|
+
return allowed ? undefined : FollowRefusal.POLICY_REFUSED;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Close reasons for a refused follow. Each is under the 123-byte limit of a close frame. */
|
|
106
|
+
export const FollowRefusal = {
|
|
107
|
+
UNKNOWN_SESSION: 'refused: a page on a new origin may only continue a session this bridge admitted',
|
|
108
|
+
URL_MISMATCH: 'refused: the page address is not on the origin the socket dialled from',
|
|
109
|
+
POLICY_REFUSED: 'refused: the host did not allow this session to follow to the new origin',
|
|
110
|
+
};
|
|
@@ -42,6 +42,12 @@ export declare function pairingTokenSource(input: {
|
|
|
42
42
|
* the daemon refuse the only app it was started to verify (#1251).
|
|
43
43
|
*/
|
|
44
44
|
export declare function configuredPairingToken(env: NodeJS.ProcessEnv, deps: PairingTokenDeps): Promise<string | undefined>;
|
|
45
|
+
/**
|
|
46
|
+
* The token a local command shows the daemon: `RETICLE_TOKEN`, else the stored file. Never creates
|
|
47
|
+
* one, because a new token is not the token the running daemon holds.
|
|
48
|
+
*/
|
|
49
|
+
export declare function storedPairingToken(env?: NodeJS.ProcessEnv): string | undefined;
|
|
50
|
+
/** The token file in `dir`, with `dir` resolved by the same rule as `RETICLE_PAIRING_TOKEN_DIR`. */
|
|
45
51
|
export declare function pairingTokenPath(dir: string): string;
|
|
46
52
|
/** Production IO adapter: 0600 file + 0700 dir so only the owner can read the secret. */
|
|
47
53
|
export declare function nodePairingTokenDeps(): PairingTokenDeps;
|
|
@@ -2,18 +2,29 @@ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { ReticleDir, ReticleEnv } from '@reticlehq/core';
|
|
5
|
+
import { isAbsolute, join, resolve, sep } from 'node:path';
|
|
6
|
+
import { ReticleDir, ReticleEnv, resolveStateDir } from '@reticlehq/core';
|
|
7
|
+
import { log } from '../../log.js';
|
|
7
8
|
/** 24 random bytes → 48 hex chars: well under MAX_TOKEN_LENGTH, ample entropy for a local secret. */
|
|
8
9
|
const TOKEN_BYTES = 24;
|
|
9
10
|
/** Env override for the token directory (`RETICLE_PAIRING_TOKEN_DIR`) — relocate the secret off $HOME. */
|
|
10
11
|
export const PAIRING_TOKEN_DIR_ENV = ReticleEnv.PAIRING_TOKEN_DIR;
|
|
12
|
+
/** The warnings already written, so that each one is written once for each process. */
|
|
13
|
+
const warnedOnce = new Set();
|
|
14
|
+
function warnOnce(warning) {
|
|
15
|
+
if (warnedOnce.has(warning)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
warnedOnce.add(warning);
|
|
19
|
+
log('state_dir_warning', { warning });
|
|
20
|
+
}
|
|
21
|
+
/** The token folder for `override`, by the shared rule. A setting that cannot be used warns and never throws. */
|
|
22
|
+
function tokenDir(override) {
|
|
23
|
+
return resolveStateDir(override, join(homedir(), ReticleDir.ROOT), { isAbsolute, resolve, sep }, warnOnce);
|
|
24
|
+
}
|
|
11
25
|
/** The default directory that holds the token — the per-user ~/.reticle so it's shared across projects. */
|
|
12
26
|
export function defaultPairingTokenDir() {
|
|
13
|
-
|
|
14
|
-
if (override !== undefined && override.length > 0)
|
|
15
|
-
return override;
|
|
16
|
-
return join(homedir(), ReticleDir.ROOT);
|
|
27
|
+
return tokenDir(process.env[PAIRING_TOKEN_DIR_ENV]);
|
|
17
28
|
}
|
|
18
29
|
/**
|
|
19
30
|
* Which of the three sources supplied the token this process is using.
|
|
@@ -58,8 +69,26 @@ export async function configuredPairingToken(env, deps) {
|
|
|
58
69
|
return undefined;
|
|
59
70
|
return readOrCreatePairingToken(dir, deps);
|
|
60
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* The token a local command shows the daemon: `RETICLE_TOKEN`, else the stored file. Never creates
|
|
74
|
+
* one, because a new token is not the token the running daemon holds.
|
|
75
|
+
*/
|
|
76
|
+
export function storedPairingToken(env = process.env) {
|
|
77
|
+
const fromEnv = env[ReticleEnv.TOKEN]?.trim();
|
|
78
|
+
if (fromEnv !== undefined && 0 < fromEnv.length) {
|
|
79
|
+
return fromEnv;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const stored = readFileSync(pairingTokenPath(defaultPairingTokenDir()), 'utf8').trim();
|
|
83
|
+
return 0 < stored.length ? stored : undefined;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** The token file in `dir`, with `dir` resolved by the same rule as `RETICLE_PAIRING_TOKEN_DIR`. */
|
|
61
90
|
export function pairingTokenPath(dir) {
|
|
62
|
-
return join(dir, ReticleDir.PAIRING_TOKEN_FILE);
|
|
91
|
+
return join(tokenDir(dir), ReticleDir.PAIRING_TOKEN_FILE);
|
|
63
92
|
}
|
|
64
93
|
/** Production IO adapter: 0600 file + 0700 dir so only the owner can read the secret. */
|
|
65
94
|
export function nodePairingTokenDeps() {
|
|
@@ -82,9 +111,10 @@ export function nodePairingTokenDeps() {
|
|
|
82
111
|
* fails, so the caller degrades to the prior tokenless behavior rather than bricking the daemon.
|
|
83
112
|
*/
|
|
84
113
|
export async function readOrCreatePairingToken(dir, deps) {
|
|
85
|
-
const
|
|
114
|
+
const folder = tokenDir(dir);
|
|
115
|
+
const file = join(folder, ReticleDir.PAIRING_TOKEN_FILE);
|
|
86
116
|
try {
|
|
87
|
-
const existing = (await deps.readFile(
|
|
117
|
+
const existing = (await deps.readFile(file)).trim();
|
|
88
118
|
if (existing.length > 0)
|
|
89
119
|
return existing;
|
|
90
120
|
}
|
|
@@ -95,8 +125,8 @@ export async function readOrCreatePairingToken(dir, deps) {
|
|
|
95
125
|
const token = deps.randomToken().trim();
|
|
96
126
|
if (0 === token.length)
|
|
97
127
|
return undefined;
|
|
98
|
-
await deps.mkdir(
|
|
99
|
-
await deps.writeFile(
|
|
128
|
+
await deps.mkdir(folder);
|
|
129
|
+
await deps.writeFile(file, token);
|
|
100
130
|
return token;
|
|
101
131
|
}
|
|
102
132
|
catch {
|
|
@@ -108,9 +138,10 @@ export async function readOrCreatePairingToken(dir, deps) {
|
|
|
108
138
|
* synchronous, and a snippet printed with no token is a page that can never authenticate.
|
|
109
139
|
*/
|
|
110
140
|
export function readOrCreatePairingTokenSync(dir) {
|
|
111
|
-
const
|
|
141
|
+
const folder = tokenDir(dir);
|
|
142
|
+
const file = join(folder, ReticleDir.PAIRING_TOKEN_FILE);
|
|
112
143
|
try {
|
|
113
|
-
const existing = readFileSync(
|
|
144
|
+
const existing = readFileSync(file, 'utf8').trim();
|
|
114
145
|
if (existing.length > 0)
|
|
115
146
|
return existing;
|
|
116
147
|
}
|
|
@@ -121,9 +152,9 @@ export function readOrCreatePairingTokenSync(dir) {
|
|
|
121
152
|
const token = randomBytes(TOKEN_BYTES).toString('hex');
|
|
122
153
|
if (0 === token.length)
|
|
123
154
|
return undefined;
|
|
124
|
-
mkdirSync(
|
|
125
|
-
writeFileSync(
|
|
126
|
-
chmodSync(
|
|
155
|
+
mkdirSync(folder, { recursive: true, mode: 0o700 });
|
|
156
|
+
writeFileSync(file, token, { encoding: 'utf8', mode: 0o600 });
|
|
157
|
+
chmodSync(file, 0o600);
|
|
127
158
|
return token;
|
|
128
159
|
}
|
|
129
160
|
catch {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RefusedPage } from '../session/session-manager.js';
|
|
1
2
|
/**
|
|
2
3
|
* Which side of a protocol mismatch is the stale one, and therefore what to tell the user to do.
|
|
3
4
|
*
|
|
@@ -22,8 +23,11 @@
|
|
|
22
23
|
*/
|
|
23
24
|
/** The page is older than the daemon: its SDK cannot speak the protocol the daemon expects. */
|
|
24
25
|
export declare const BROWSER_IS_OLDER = "protocol mismatch \u2014 upgrade @reticlehq/browser in this app";
|
|
25
|
-
/**
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The daemon is older than the page. Usually an npx cache serving a stale @reticlehq/server. The
|
|
28
|
+
* MCP server starts the daemon, so it is named first: a stop alone brings the same old daemon back.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DAEMON_IS_OLDER = "protocol mismatch \u2014 the daemon is older; update the MCP server or plugin, then `reticle stop`";
|
|
27
31
|
/** The versions disagree but not in a direction we can read. */
|
|
28
32
|
export declare const SKEW_UNKNOWN = "protocol version mismatch \u2014 @reticlehq/browser and server disagree";
|
|
29
33
|
/** The cap a WebSocket close reason must fit inside, in bytes. */
|
|
@@ -35,3 +39,24 @@ export declare const CLOSE_REASON_MAX_BYTES = 123;
|
|
|
35
39
|
* all falls back to naming both, which is vague but never actively misleading.
|
|
36
40
|
*/
|
|
37
41
|
export declare const protocolSkewReason: (got: number | null, expected: number) => string;
|
|
42
|
+
/**
|
|
43
|
+
* When a message fails the strict schema, detect the specific case of a HELLO whose protocolVersion
|
|
44
|
+
* differs from ours — so we can tell the operator "upgrade @reticlehq/browser" instead of dropping the
|
|
45
|
+
* connection into the generic NO_SESSION_CONNECTED path, which misdiagnoses it as a port mismatch.
|
|
46
|
+
* Returns the mismatched version, or null when it isn't a version-mismatched HELLO.
|
|
47
|
+
*/
|
|
48
|
+
export declare function helloProtocolMismatch(text: string): number | null;
|
|
49
|
+
/**
|
|
50
|
+
* What a HELLO says about the page it came from, read loosely enough to survive the schema failing.
|
|
51
|
+
* Null when the message is not a hello at all — only an SDK sends one, so only then is a refusal
|
|
52
|
+
* evidence about an app.
|
|
53
|
+
*/
|
|
54
|
+
export declare function peekHelloPage(text: string): RefusedPage | null;
|
|
55
|
+
/**
|
|
56
|
+
* The `kind` and event `type` of a frame that failed the schema, for the log: never its payload,
|
|
57
|
+
* which can carry anything the page sent. Each is a bounded string or absent.
|
|
58
|
+
*/
|
|
59
|
+
export declare function peekFrameKind(text: string): {
|
|
60
|
+
kind?: string;
|
|
61
|
+
eventType?: string;
|
|
62
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MessageKind, RETICLE_PROTOCOL_VERSION } from '@reticlehq/core';
|
|
1
2
|
/**
|
|
2
3
|
* Which side of a protocol mismatch is the stale one, and therefore what to tell the user to do.
|
|
3
4
|
*
|
|
@@ -22,8 +23,11 @@
|
|
|
22
23
|
*/
|
|
23
24
|
/** The page is older than the daemon: its SDK cannot speak the protocol the daemon expects. */
|
|
24
25
|
export const BROWSER_IS_OLDER = 'protocol mismatch — upgrade @reticlehq/browser in this app';
|
|
25
|
-
/**
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The daemon is older than the page. Usually an npx cache serving a stale @reticlehq/server. The
|
|
28
|
+
* MCP server starts the daemon, so it is named first: a stop alone brings the same old daemon back.
|
|
29
|
+
*/
|
|
30
|
+
export const DAEMON_IS_OLDER = 'protocol mismatch — the daemon is older; update the MCP server or plugin, then `reticle stop`';
|
|
27
31
|
/** The versions disagree but not in a direction we can read. */
|
|
28
32
|
export const SKEW_UNKNOWN = 'protocol version mismatch — @reticlehq/browser and server disagree';
|
|
29
33
|
/** The cap a WebSocket close reason must fit inside, in bytes. */
|
|
@@ -43,3 +47,63 @@ export const protocolSkewReason = (got, expected) => {
|
|
|
43
47
|
return DAEMON_IS_OLDER;
|
|
44
48
|
return SKEW_UNKNOWN;
|
|
45
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* When a message fails the strict schema, detect the specific case of a HELLO whose protocolVersion
|
|
52
|
+
* differs from ours — so we can tell the operator "upgrade @reticlehq/browser" instead of dropping the
|
|
53
|
+
* connection into the generic NO_SESSION_CONNECTED path, which misdiagnoses it as a port mismatch.
|
|
54
|
+
* Returns the mismatched version, or null when it isn't a version-mismatched HELLO.
|
|
55
|
+
*/
|
|
56
|
+
export function helloProtocolMismatch(text) {
|
|
57
|
+
try {
|
|
58
|
+
const json = JSON.parse(text);
|
|
59
|
+
if (json.kind === MessageKind.HELLO &&
|
|
60
|
+
'number' === typeof json.protocolVersion &&
|
|
61
|
+
json.protocolVersion !== RETICLE_PROTOCOL_VERSION) {
|
|
62
|
+
return json.protocolVersion;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* not JSON — fall through to the generic invalid-message path */
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What a HELLO says about the page it came from, read loosely enough to survive the schema failing.
|
|
72
|
+
* Null when the message is not a hello at all — only an SDK sends one, so only then is a refusal
|
|
73
|
+
* evidence about an app.
|
|
74
|
+
*/
|
|
75
|
+
export function peekHelloPage(text) {
|
|
76
|
+
try {
|
|
77
|
+
const json = JSON.parse(text);
|
|
78
|
+
if (json.kind !== MessageKind.HELLO)
|
|
79
|
+
return null;
|
|
80
|
+
return {
|
|
81
|
+
...('string' === typeof json.url ? { url: json.url } : {}),
|
|
82
|
+
...('string' === typeof json.projectId ? { projectId: json.projectId } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The `kind` and event `type` of a frame that failed the schema, for the log: never its payload,
|
|
91
|
+
* which can carry anything the page sent. Each is a bounded string or absent.
|
|
92
|
+
*/
|
|
93
|
+
export function peekFrameKind(text) {
|
|
94
|
+
const short = (v) => 'string' === typeof v ? v.slice(0, MAX_LOGGED_FRAME_FIELD) : undefined;
|
|
95
|
+
try {
|
|
96
|
+
const json = JSON.parse(text);
|
|
97
|
+
const kind = short(json?.kind);
|
|
98
|
+
const eventType = short(json?.event?.type);
|
|
99
|
+
return {
|
|
100
|
+
...(kind === undefined ? {} : { kind }),
|
|
101
|
+
...(eventType === undefined ? {} : { eventType }),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** How much of a frame's `kind` or event `type` the drop log keeps. */
|
|
109
|
+
const MAX_LOGGED_FRAME_FIELD = 64;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ProductionDrive, type ProductionGrant } from '../production/production-drive.js';
|
|
2
|
+
/**
|
|
3
|
+
* Single-use HELLO tokens with a time limit, accepted beside the machine's pairing token.
|
|
4
|
+
*
|
|
5
|
+
* The pairing token is a machine-local secret, so it can never ship in a page that somebody else
|
|
6
|
+
* serves. A page on a production origin gets a nonce instead: the daemon mints it for one drive,
|
|
7
|
+
* the page presents it in its HELLO, and it stops working when the drive's time is up.
|
|
8
|
+
*
|
|
9
|
+
* Only a local caller (the MCP tools, the CLI) can mint one. Nothing on the bridge socket reaches
|
|
10
|
+
* `mint`, so a page cannot give itself a token.
|
|
11
|
+
*/
|
|
12
|
+
/** How long a nonce stays valid when the caller does not say: one drive. */
|
|
13
|
+
export declare const SESSION_NONCE_TTL_MS: number;
|
|
14
|
+
/** What a redeemed nonce grants the session that presented it. */
|
|
15
|
+
export interface NonceGrant {
|
|
16
|
+
/** The page origin this nonce was minted for. The bridge admits a WebSocket from it. */
|
|
17
|
+
origin?: string;
|
|
18
|
+
expiresAt: number;
|
|
19
|
+
/** Set when the nonce was minted for a production drive. Its session runs under the policy. */
|
|
20
|
+
production?: ProductionDrive;
|
|
21
|
+
}
|
|
22
|
+
export interface MintOptions {
|
|
23
|
+
origin?: string;
|
|
24
|
+
ttlMs?: number;
|
|
25
|
+
production?: ProductionGrant;
|
|
26
|
+
}
|
|
27
|
+
export declare class SessionNonces {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(deps: {
|
|
30
|
+
clock: () => number;
|
|
31
|
+
random?: () => string;
|
|
32
|
+
});
|
|
33
|
+
mint(options: MintOptions): {
|
|
34
|
+
nonce: string;
|
|
35
|
+
expiresAt: number;
|
|
36
|
+
production?: ProductionDrive;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The grant for this token, or undefined when it is not a live nonce or another session holds it.
|
|
40
|
+
* The first redeem binds the nonce to `sessionId`. The same tab may present it again (a reload
|
|
41
|
+
* sends a new HELLO); a second tab, or a replay under another id, may not.
|
|
42
|
+
*/
|
|
43
|
+
redeem(token: string | undefined, sessionId: string): NonceGrant | undefined;
|
|
44
|
+
/** True while a live nonce was minted for this origin. */
|
|
45
|
+
admitsOrigin(origin: string): boolean;
|
|
46
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { ProductionDrive } from '../production/production-drive.js';
|
|
3
|
+
/**
|
|
4
|
+
* Single-use HELLO tokens with a time limit, accepted beside the machine's pairing token.
|
|
5
|
+
*
|
|
6
|
+
* The pairing token is a machine-local secret, so it can never ship in a page that somebody else
|
|
7
|
+
* serves. A page on a production origin gets a nonce instead: the daemon mints it for one drive,
|
|
8
|
+
* the page presents it in its HELLO, and it stops working when the drive's time is up.
|
|
9
|
+
*
|
|
10
|
+
* Only a local caller (the MCP tools, the CLI) can mint one. Nothing on the bridge socket reaches
|
|
11
|
+
* `mint`, so a page cannot give itself a token.
|
|
12
|
+
*/
|
|
13
|
+
/** How long a nonce stays valid when the caller does not say: one drive. */
|
|
14
|
+
export const SESSION_NONCE_TTL_MS = 60 * 60 * 1000;
|
|
15
|
+
/** 24 random bytes: the same entropy as the pairing token. */
|
|
16
|
+
const NONCE_BYTES = 24;
|
|
17
|
+
export class SessionNonces {
|
|
18
|
+
#entries = new Map();
|
|
19
|
+
#clock;
|
|
20
|
+
#random;
|
|
21
|
+
constructor(deps) {
|
|
22
|
+
this.#clock = deps.clock;
|
|
23
|
+
this.#random = deps.random ?? (() => randomBytes(NONCE_BYTES).toString('hex'));
|
|
24
|
+
}
|
|
25
|
+
mint(options) {
|
|
26
|
+
this.#sweep();
|
|
27
|
+
const nonce = this.#random();
|
|
28
|
+
const expiresAt = this.#clock() + (options.ttlMs ?? SESSION_NONCE_TTL_MS);
|
|
29
|
+
// One drive per nonce, shared by every reconnect of its tab, so what it created survives a reload.
|
|
30
|
+
const production = options.production === undefined
|
|
31
|
+
? undefined
|
|
32
|
+
: new ProductionDrive({ ...options.production, expiresAt }, this.#clock);
|
|
33
|
+
this.#entries.set(nonce, {
|
|
34
|
+
...(options.origin === undefined ? {} : { origin: options.origin }),
|
|
35
|
+
...(production === undefined ? {} : { production }),
|
|
36
|
+
expiresAt,
|
|
37
|
+
});
|
|
38
|
+
return { nonce, expiresAt, ...(production === undefined ? {} : { production }) };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The grant for this token, or undefined when it is not a live nonce or another session holds it.
|
|
42
|
+
* The first redeem binds the nonce to `sessionId`. The same tab may present it again (a reload
|
|
43
|
+
* sends a new HELLO); a second tab, or a replay under another id, may not.
|
|
44
|
+
*/
|
|
45
|
+
redeem(token, sessionId) {
|
|
46
|
+
if (token === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
this.#sweep();
|
|
49
|
+
const entry = this.#entries.get(token);
|
|
50
|
+
if (entry === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
if (entry.boundTo !== undefined && entry.boundTo !== sessionId)
|
|
53
|
+
return undefined;
|
|
54
|
+
entry.boundTo = sessionId;
|
|
55
|
+
return entry;
|
|
56
|
+
}
|
|
57
|
+
/** True while a live nonce was minted for this origin. */
|
|
58
|
+
admitsOrigin(origin) {
|
|
59
|
+
this.#sweep();
|
|
60
|
+
for (const entry of this.#entries.values())
|
|
61
|
+
if (entry.origin === origin)
|
|
62
|
+
return true;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
#sweep() {
|
|
66
|
+
const now = this.#clock();
|
|
67
|
+
for (const [nonce, entry] of this.#entries) {
|
|
68
|
+
if (entry.expiresAt <= now)
|
|
69
|
+
this.#entries.delete(nonce);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1,2 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import type { ToolDef } from '../../surface/tools/tool-kit.js';
|
|
3
|
+
import { type LeaseEmulation, type LeaseMedia } from '../pool/lease-emulation.js';
|
|
4
|
+
export declare const COLOR_SCHEME_ARG: z.ZodOptional<z.ZodEnum<{
|
|
5
|
+
light: "light";
|
|
6
|
+
dark: "dark";
|
|
7
|
+
"no-preference": "no-preference";
|
|
8
|
+
}>>;
|
|
9
|
+
export declare const REDUCED_MOTION_ARG: z.ZodOptional<z.ZodEnum<{
|
|
10
|
+
reduce: "reduce";
|
|
11
|
+
"no-preference": "no-preference";
|
|
12
|
+
}>>;
|
|
13
|
+
export declare const LEASE_EMULATE_ARG: z.ZodOptional<z.ZodObject<{
|
|
14
|
+
colorScheme: z.ZodOptional<z.ZodEnum<{
|
|
15
|
+
light: "light";
|
|
16
|
+
dark: "dark";
|
|
17
|
+
"no-preference": "no-preference";
|
|
18
|
+
}>>;
|
|
19
|
+
reducedMotion: z.ZodOptional<z.ZodEnum<{
|
|
20
|
+
reduce: "reduce";
|
|
21
|
+
"no-preference": "no-preference";
|
|
22
|
+
}>>;
|
|
23
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
}, z.core.$strict>>;
|
|
26
|
+
/** The media part of what a caller asked for, or undefined when it asked for none. */
|
|
27
|
+
export declare function mediaOf(raw: {
|
|
28
|
+
colorScheme?: unknown;
|
|
29
|
+
reducedMotion?: unknown;
|
|
30
|
+
}): LeaseMedia | undefined;
|
|
31
|
+
/** A lease's `emulate` argument, read; a value it does not know is refused by name. */
|
|
32
|
+
export declare function leaseEmulationOf(raw: unknown): LeaseEmulation | undefined;
|
|
2
33
|
export declare const VIEWPORT_TOOLS: ToolDef[];
|