@reticlehq/server 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SKILL.md +16 -8
- package/dist/chromium-launch-options.d.ts +3 -1
- package/dist/chromium-launch-options.js +2 -1
- package/dist/command/cli/adhoc-verdict.d.ts +1 -0
- package/dist/command/cli/adhoc-verdict.js +6 -3
- package/dist/command/cli/answers/open-note.d.ts +2 -0
- package/dist/command/cli/answers/open-note.js +22 -0
- package/dist/command/cli/auth/cloud-session.d.ts +4 -4
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-flow-commands.js +4 -8
- package/dist/command/cli/cli-parse.d.ts +1 -1
- package/dist/command/cli/cli-parse.js +13 -11
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +35 -25
- package/dist/command/cli/cloud-cli.js +110 -12
- package/dist/command/cli/cloud-login.d.ts +9 -1
- package/dist/command/cli/cloud-login.js +43 -8
- package/dist/command/cli/demo/demo-page.js +11 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +46 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
- package/dist/command/cli/drive/drive-attach.d.ts +27 -2
- package/dist/command/cli/drive/drive-attach.js +71 -4
- package/dist/command/cli/drive/drive-command.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +13 -7
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +4 -1
- package/dist/command/cli/ports/port-holder.d.ts +10 -0
- package/dist/command/cli/ports/port-holder.js +24 -0
- package/dist/command/cli/setup-mcp-cli.js +15 -5
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +6 -9
- package/dist/command/cli.d.ts +6 -0
- package/dist/command/cli.js +45 -23
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
- package/dist/command/daemon/daemon-resolve.d.ts +14 -0
- package/dist/command/daemon/daemon-resolve.js +33 -2
- package/dist/command/setup/agent-writer.d.ts +1 -1
- package/dist/command/setup/agent-writer.js +3 -2
- package/dist/command/setup/bringup/connect-lease.d.ts +1 -0
- package/dist/command/setup/bringup/connect-lease.js +1 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +10 -0
- package/dist/command/setup/bringup/dev-server-wait.js +20 -1
- package/dist/command/setup/init/init-port.d.ts +4 -0
- package/dist/command/setup/init/init-port.js +10 -0
- package/dist/command/setup/init/init-runtime.d.ts +10 -0
- package/dist/command/setup/init/init-runtime.js +71 -9
- package/dist/command/setup/node-effects.js +8 -2
- package/dist/command/setup/remaining-steps.js +2 -1
- package/dist/command/setup/run-setup.d.ts +7 -0
- package/dist/command/setup/run-setup.js +38 -25
- package/dist/command/setup/setup-command.d.ts +2 -1
- package/dist/command/setup/setup-command.js +2 -0
- package/dist/command/setup/setup-mcp.d.ts +2 -0
- package/dist/command/setup/setup-mcp.js +14 -5
- package/dist/command/version/peer-announce.d.ts +6 -2
- package/dist/command/version/peer-announce.js +8 -6
- package/dist/command/version/version-nudge.d.ts +17 -3
- package/dist/command/version/version-nudge.js +25 -4
- package/dist/command/version/version-skew.js +11 -2
- package/dist/features/crawl/crawl-tools.js +4 -0
- package/dist/features/crawl/crawl.d.ts +6 -0
- package/dist/features/crawl/crawl.js +9 -1
- package/dist/features/exhaust/ledger.d.ts +7 -0
- package/dist/features/exhaust/ledger.js +35 -9
- package/dist/features/harness/app-findings.d.ts +2 -0
- package/dist/features/harness/app-findings.js +52 -0
- package/dist/features/harness/drive-report.d.ts +27 -1
- package/dist/features/harness/drive-report.js +162 -11
- package/dist/features/harness/drivers.d.ts +10 -14
- package/dist/features/harness/drivers.js +10 -14
- package/dist/features/harness/fill-values.d.ts +3 -69
- package/dist/features/harness/fill-values.js +3 -133
- package/dist/features/harness/goals.d.ts +21 -0
- package/dist/features/harness/goals.js +77 -0
- package/dist/features/harness/harness.d.ts +21 -0
- package/dist/features/harness/harness.js +69 -2
- package/dist/features/harness/platform/drive-target.d.ts +30 -0
- package/dist/features/harness/platform/drive-target.js +81 -0
- package/dist/features/harness/platform/local-apps.d.ts +94 -0
- package/dist/features/harness/platform/local-apps.js +204 -0
- package/dist/features/harness/platform/remote-drive.d.ts +180 -0
- package/dist/features/harness/platform/remote-drive.js +365 -0
- package/dist/features/harness/platform/script.d.ts +48 -0
- package/dist/features/harness/platform/script.js +128 -0
- package/dist/features/harness/platform/server-driver.d.ts +41 -0
- package/dist/features/harness/platform/server-driver.js +179 -0
- package/dist/features/harness/platform-config.d.ts +7 -0
- package/dist/features/harness/platform-config.js +14 -2
- package/dist/features/harness/script/app-controls.d.ts +5 -0
- package/dist/features/harness/script/app-controls.js +40 -0
- package/dist/features/harness/script/lane-ids.d.ts +14 -0
- package/dist/features/harness/script/lane-ids.js +16 -0
- package/dist/features/harness/script/script-run.d.ts +41 -0
- package/dist/features/harness/script/script-run.js +299 -0
- package/dist/features/license/license.d.ts +2 -2
- package/dist/features/visual/visual-tools.d.ts +10 -1
- package/dist/features/visual/visual-tools.js +40 -3
- package/dist/hooks/driven-by.d.ts +4 -0
- package/dist/hooks/driven-by.js +16 -0
- package/dist/hooks/hook-commands.js +15 -4
- package/dist/hooks/tool-log.d.ts +9 -0
- package/dist/hooks/tool-log.js +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +91 -89
- package/dist/judgement/capsule/capsule-store.d.ts +4 -4
- package/dist/judgement/runs/artifact/build-verification-run.js +6 -0
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +1 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +16 -6
- package/dist/judgement/runs/artifact/run-store.d.ts +1 -1
- package/dist/judgement/runs/artifact/run-store.js +26 -1
- package/dist/judgement/runs/drive-run.d.ts +22 -0
- package/dist/judgement/runs/drive-run.js +99 -1
- package/dist/judgement/runs/repair-prompt.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +8 -1
- package/dist/judgement/runs/verification-sync.d.ts +3 -2
- package/dist/judgement/runs/verification-sync.js +4 -3
- package/dist/language/flows/change/gate.d.ts +10 -0
- package/dist/language/flows/change/gate.js +22 -0
- package/dist/language/flows/decision.js +34 -0
- package/dist/language/flows/flow-anchor.d.ts +54 -8
- package/dist/language/flows/flow-anchor.js +88 -3
- package/dist/language/flows/flow-cross-step.d.ts +15 -0
- package/dist/language/flows/flow-cross-step.js +21 -0
- package/dist/language/flows/flow-learning.js +8 -11
- package/dist/language/flows/flow-mutate-tools.js +5 -3
- package/dist/language/flows/flow-replay-run.d.ts +23 -9
- package/dist/language/flows/flow-replay-run.js +101 -94
- package/dist/language/flows/flow-replay.d.ts +23 -1
- package/dist/language/flows/flow-replay.js +92 -19
- package/dist/language/flows/flow-scope.js +5 -1
- package/dist/language/flows/flow-step-runners.d.ts +20 -2
- package/dist/language/flows/flow-step-runners.js +236 -42
- package/dist/language/flows/flow-store-for-session.d.ts +2 -2
- package/dist/language/flows/flow-store-for-session.js +2 -2
- package/dist/language/flows/flow-tools.js +58 -34
- package/dist/language/flows/flows.js +5 -2
- package/dist/language/flows/heal-run.js +6 -4
- package/dist/language/flows/recording/flake.d.ts +2 -2
- package/dist/language/flows/recording/tape/recordings.d.ts +30 -4
- package/dist/language/flows/recording/tape/recordings.js +40 -8
- package/dist/language/flows/replay-from-hud.d.ts +20 -0
- package/dist/language/flows/replay-from-hud.js +51 -0
- package/dist/language/flows/replay.d.ts +9 -5
- package/dist/language/flows/replay.js +27 -9
- package/dist/language/flows/suite/suite-flakes.js +2 -0
- package/dist/language/flows/suite-selection.d.ts +2 -15
- package/dist/language/flows/suite-selection.js +4 -3
- package/dist/language/flows/verify-change-tools.js +8 -4
- package/dist/launch-chromium.d.ts +27 -5
- package/dist/launch-chromium.js +38 -8
- package/dist/memory/cloud/cloud-config.d.ts +12 -0
- package/dist/memory/cloud/cloud-config.js +15 -0
- package/dist/memory/cloud/cloud-sync.d.ts +17 -14
- package/dist/memory/cloud/cloud-sync.js +12 -1
- package/dist/memory/cloud/harness-config.d.ts +26 -1
- package/dist/memory/cloud/harness-config.js +99 -4
- package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
- package/dist/memory/cloud/hud-notices-source.js +128 -0
- package/dist/memory/cloud/memory-scope.d.ts +144 -0
- package/dist/memory/cloud/memory-scope.js +205 -0
- package/dist/memory/cloud/sync-cycle.d.ts +21 -3
- package/dist/memory/cloud/sync-cycle.js +45 -5
- package/dist/memory/cloud/sync-daemon.js +7 -5
- package/dist/memory/cloud/sync-disk.d.ts +1 -1
- package/dist/memory/cloud/sync-disk.js +5 -8
- package/dist/memory/impact/account-watch.d.ts +19 -0
- package/dist/memory/impact/account-watch.js +34 -0
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +12 -2
- package/dist/memory/impact/impact-store.d.ts +16 -0
- package/dist/memory/impact/impact-store.js +142 -26
- package/dist/memory/intent/intent-shard.d.ts +2 -2
- package/dist/memory/intent/intent-tools.js +88 -5
- package/dist/memory/journal/envelope.d.ts +16 -16
- package/dist/memory/journal/journal-query.d.ts +14 -0
- package/dist/memory/journal/journal-query.js +15 -0
- package/dist/memory/journal/journal-recorder.js +7 -1
- package/dist/memory/journal/on-disk/retention.d.ts +3 -0
- package/dist/memory/journal/on-disk/retention.js +5 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -4
- package/dist/memory/journal/session-end.js +6 -4
- package/dist/memory/project/artifact-root-resolver.d.ts +36 -1
- package/dist/memory/project/artifact-root-resolver.js +134 -26
- package/dist/memory/project/artifact-root.d.ts +42 -5
- package/dist/memory/project/artifact-root.js +87 -8
- package/dist/memory/project/dir/reticle-dir.d.ts +4 -2
- package/dist/memory/project/dir/reticle-dir.js +3 -1
- package/dist/memory/project/project-tools.js +22 -3
- package/dist/memory/project/session-root.d.ts +24 -1
- package/dist/memory/project/session-root.js +41 -5
- package/dist/memory/project/sync-status.d.ts +39 -0
- package/dist/memory/project/sync-status.js +90 -0
- package/dist/memory/recall/memory-tools.js +2 -0
- package/dist/memory/recall/project-memory.d.ts +19 -0
- package/dist/memory/recall/project-memory.js +45 -6
- package/dist/portal/bridge/bridge.d.ts +7 -4
- package/dist/portal/bridge/bridge.js +25 -3
- package/dist/portal/bridge/pairing-token.d.ts +9 -0
- package/dist/portal/bridge/pairing-token.js +17 -0
- package/dist/portal/input/real-input.d.ts +81 -10
- package/dist/portal/input/real-input.js +207 -22
- package/dist/portal/input/viewport-tools.js +19 -9
- package/dist/portal/pool/browser-pool.d.ts +22 -9
- package/dist/portal/pool/browser-pool.js +70 -35
- package/dist/portal/pool/playwright-launcher.d.ts +13 -0
- package/dist/portal/pool/playwright-launcher.js +17 -1
- package/dist/portal/pool/pool-contract.d.ts +7 -4
- package/dist/portal/session/command-payload.d.ts +8 -0
- package/dist/portal/session/command-payload.js +19 -0
- package/dist/portal/session/dev-server/dev-command.d.ts +12 -0
- package/dist/portal/session/dev-server/dev-command.js +61 -12
- package/dist/portal/session/facts/departure-note.d.ts +22 -0
- package/dist/portal/session/facts/departure-note.js +47 -0
- package/dist/portal/session/facts/session-replaced.d.ts +5 -0
- package/dist/portal/session/facts/session-replaced.js +5 -0
- package/dist/portal/session/first-run-wiring.d.ts +42 -0
- package/dist/portal/session/first-run-wiring.js +132 -0
- package/dist/portal/session/human/notes-ledger.d.ts +20 -0
- package/dist/portal/session/human/notes-ledger.js +55 -0
- package/dist/portal/session/human/review-store.d.ts +12 -0
- package/dist/portal/session/human/review-store.js +18 -10
- package/dist/portal/session/live-control-tools.d.ts +0 -13
- package/dist/portal/session/live-control-tools.js +28 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +7 -0
- package/dist/portal/session/no-session-diagnosis.js +15 -0
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +31 -5
- package/dist/portal/session/no-session-watch.d.ts +14 -1
- package/dist/portal/session/no-session-watch.js +63 -10
- package/dist/portal/session/presence/session-mirror.js +3 -0
- package/dist/portal/session/serving-directory.d.ts +27 -0
- package/dist/portal/session/serving-directory.js +45 -0
- package/dist/portal/session/session-health.d.ts +7 -0
- package/dist/portal/session/session-health.js +6 -0
- package/dist/portal/session/session-info.d.ts +8 -1
- package/dist/portal/session/session-info.js +6 -0
- package/dist/portal/session/session-manager.d.ts +21 -0
- package/dist/portal/session/session-manager.js +44 -8
- package/dist/portal/session/session-reaper.js +4 -1
- package/dist/portal/session/session-successor.d.ts +5 -0
- package/dist/portal/session/session-tools.js +26 -3
- package/dist/portal/session/session.d.ts +12 -10
- package/dist/portal/session/session.js +30 -31
- package/dist/start-options.d.ts +6 -0
- package/dist/status-payload.d.ts +10 -0
- package/dist/status-payload.js +23 -0
- package/dist/surface/http-server.d.ts +1 -1
- package/dist/surface/http-server.js +15 -4
- package/dist/surface/mcp/mcp-proxy.d.ts +5 -0
- package/dist/surface/mcp/mcp-proxy.js +36 -5
- package/dist/surface/mcp/mcp.d.ts +11 -6
- package/dist/surface/mcp/mcp.js +28 -14
- package/dist/surface/mcp/proxy/proxy-log.js +3 -5
- package/dist/surface/mcp/server-instructions.d.ts +4 -2
- package/dist/surface/mcp/server-instructions.js +14 -4
- package/dist/surface/mcp/shared-params.js +2 -2
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +8 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
- package/dist/surface/tools/act/settle-in-flight.js +25 -3
- package/dist/surface/tools/act-sequence-tool.js +2 -2
- package/dist/surface/tools/act-tools.js +25 -9
- package/dist/surface/tools/assert/assert-verdict.d.ts +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +17 -2
- package/dist/surface/tools/browser-tools.js +8 -1
- package/dist/surface/tools/chat-drives.d.ts +26 -0
- package/dist/surface/tools/chat-drives.js +165 -0
- package/dist/surface/tools/dynamic-tools.js +29 -2
- package/dist/surface/tools/empty-session-list.d.ts +41 -0
- package/dist/surface/tools/empty-session-list.js +80 -0
- package/dist/surface/tools/error-recovery.d.ts +6 -0
- package/dist/surface/tools/error-recovery.js +38 -6
- package/dist/surface/tools/explore-tools.d.ts +10 -1
- package/dist/surface/tools/explore-tools.js +82 -13
- package/dist/surface/tools/feedback-tools.js +14 -0
- package/dist/surface/tools/harness-explore.d.ts +96 -14
- package/dist/surface/tools/harness-explore.js +227 -202
- package/dist/surface/tools/harness-plan.d.ts +12 -0
- package/dist/surface/tools/harness-plan.js +31 -0
- package/dist/surface/tools/harness-script.d.ts +8 -0
- package/dist/surface/tools/harness-script.js +342 -0
- package/dist/surface/tools/harness-toolset.d.ts +14 -1
- package/dist/surface/tools/harness-toolset.js +77 -6
- package/dist/surface/tools/invoke-tool.js +64 -9
- package/dist/surface/tools/lease-seed.d.ts +29 -0
- package/dist/surface/tools/lease-seed.js +123 -0
- package/dist/surface/tools/lease-tools.d.ts +3 -23
- package/dist/surface/tools/lease-tools.js +30 -131
- package/dist/surface/tools/live-call-text.js +2 -2
- package/dist/surface/tools/next-step.d.ts +19 -0
- package/dist/surface/tools/next-step.js +159 -0
- package/dist/surface/tools/observe-tools.js +2 -4
- package/dist/surface/tools/platform-moment.d.ts +12 -0
- package/dist/surface/tools/platform-moment.js +168 -0
- package/dist/surface/tools/query-hint-schema.d.ts +7 -0
- package/dist/surface/tools/query-hint-schema.js +10 -0
- package/dist/surface/tools/read-tools.js +8 -2
- package/dist/surface/tools/real-input-attempt.js +96 -13
- package/dist/surface/tools/tool-kit.d.ts +25 -1
- package/dist/surface/tools/tool-kit.js +4 -6
- package/dist/surface/tools/tools.js +20 -48
- package/dist/telemetry/argument-shape.js +1 -1
- package/dist/telemetry/hud-metrics.d.ts +2 -0
- package/dist/telemetry/hud-metrics.js +17 -0
- package/dist/telemetry/onboarding-firsts.d.ts +2 -2
- package/dist/telemetry/onboarding-firsts.js +2 -2
- package/dist/telemetry/telemetry.d.ts +12 -0
- package/dist/telemetry/telemetry.js +43 -14
- package/dist/telemetry/verification-of.js +1 -0
- package/dist/wire-journal.js +4 -1
- package/docs/README.md +1 -1
- package/docs/actions.mdx +1 -2
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/autodrive.md +29 -25
- package/docs/cli/cloud.mdx +6 -4
- package/docs/cli/doctor.mdx +6 -0
- package/docs/cli/drive.mdx +8 -2
- package/docs/cli/init.mdx +11 -13
- package/docs/cli/status.mdx +5 -1
- package/docs/cli/version.mdx +3 -3
- package/docs/cli.mdx +14 -8
- package/docs/desktop.mdx +5 -1
- package/docs/environment.mdx +3 -7
- package/docs/getting-started.md +14 -2
- package/docs/human-control.md +1 -2
- package/docs/index.mdx +4 -3
- package/docs/install-agentic.mdx +1 -1
- package/docs/install-manual.mdx +2 -2
- package/docs/packages/next.mdx +3 -0
- package/docs/packages/tauri.mdx +2 -2
- package/docs/packages/vite-plugin.mdx +24 -1
- package/docs/packages.mdx +1 -1
- package/docs/quickstart.mdx +23 -11
- package/docs/telemetry.md +1 -1
- package/docs/tools/act.mdx +1 -1
- package/docs/tools/session-and-feedback.mdx +9 -9
- package/docs/usage.md +5 -3
- package/docs/what-is-recorded.md +13 -3
- package/docs/what-is-reticle.mdx +3 -1
- package/package.json +8 -12
- package/dist/features/harness/driver.d.ts +0 -50
- package/dist/features/harness/driver.js +0 -236
- package/dist/features/harness/jev-driver.d.ts +0 -188
- package/dist/features/harness/jev-driver.js +0 -678
- package/dist/features/harness/openai-driver.d.ts +0 -46
- package/dist/features/harness/openai-driver.js +0 -194
- package/dist/memory/project/dir/fill-value-store.d.ts +0 -32
- package/dist/memory/project/dir/fill-value-store.js +0 -88
- package/docs/local-registry.md +0 -143
package/README.md
CHANGED
|
@@ -27,4 +27,4 @@ See the [main README](https://github.com/reticlehq/reticle).
|
|
|
27
27
|
|
|
28
28
|
## License
|
|
29
29
|
|
|
30
|
-
[FSL-1.1-ALv2](./LICENSE) (source-available: free for any use except reselling Reticle itself; converts to Apache-2.0 after two years). Files under `dist/ee/` are enterprise features covered by the [Reticle Enterprise License](./LICENSE-ENTERPRISE) — free for development, testing, and evaluation; a license key activates them in production.
|
|
30
|
+
[FSL-1.1-ALv2](./LICENSE) (source-available: free for any use except reselling Reticle itself; converts to Apache-2.0 after two years). Files under `dist/features/ee/` are enterprise features covered by the [Reticle Enterprise License](./LICENSE-ENTERPRISE) — free for development, testing, and evaluation; a license key activates them in production.
|
package/SKILL.md
CHANGED
|
@@ -8,7 +8,13 @@ This file is the whole critical path and nothing else. Everything it leaves out
|
|
|
8
8
|
|
|
9
9
|
Do not spend a turn working out which path you are on.
|
|
10
10
|
|
|
11
|
-
**`reticle_*` tools visible?** The machine is set up
|
|
11
|
+
**`reticle_*` tools visible?** The machine is set up; installing changed no app. In the project, call:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
reticle_session { action: "list" }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On a never-connected project that call runs `init` itself and lists the files it changed under `wired`; tell the user. If it answers `run_init`, the daemon was started outside the project: run this in the app's directory:
|
|
12
18
|
|
|
13
19
|
```bash
|
|
14
20
|
RETICLE_INSTALL_SOURCE=skill_file npx @reticlehq/server@latest init
|
|
@@ -23,7 +29,7 @@ irm https://raw.githubusercontent.com/reticlehq/reticle/main/install/install.ps1
|
|
|
23
29
|
|
|
24
30
|
It registers the MCP server with the coding agents it finds and, where an agent has a per-server approval rule (Claude Code, for one), pre-approves Reticle's own tools. They reopen this client and the tools are there. If they want to choose each step instead, the manual install is `https://docs.reticle.sh/install-manual.md`. Do not register it yourself and then work around your client not having reloaded: that is the sequence that breaks.
|
|
25
31
|
|
|
26
|
-
`init
|
|
32
|
+
`init`, whether the first session call runs it or you do, is ONBOARDING, and where it stops: wire, boot, wait for a session. Idempotent, reporting `·` for what is already there. Two things it cannot do for you, in this order:
|
|
27
33
|
|
|
28
34
|
1. **Restart the dev server if one was already running when `init` ran.** It read the build config at boot; `init` edited that file afterwards, so the process keeps serving a bundle with no SDK in it. Restart, then hard-reload the tab. A 100% failure, not an intermittent one, and the largest single cause of a correct install that finds nothing connected.
|
|
29
35
|
2. **Confirm rather than assume:** `reticle_session { action: "list" }`. One session listed is the proof the SDK reached the page. An empty list carries a `why` that names which cause this is; read it before changing anything.
|
|
@@ -31,10 +37,10 @@ It registers the MCP server with the coding agents it finds and, where an agent
|
|
|
31
37
|
Then the FIRST RUN, which proves anything at all:
|
|
32
38
|
|
|
33
39
|
```
|
|
34
|
-
|
|
40
|
+
reticle_act_and_wait { ref, action, until }
|
|
35
41
|
```
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
Drive the journey that matters; `until` on its LAST step names the end state. It is saved as a flow that replays with no model. On a linked project (Free includes monthly Harness credits), `reticle_verify { action: "explore", persona }` drives it for you on the Reticle platform. Either way, name the journey: Reticle lists buttons, not which one matters.
|
|
38
44
|
|
|
39
45
|
Everything between here and there is a rule the steps assume. Read it as you go, not before you act.
|
|
40
46
|
|
|
@@ -42,7 +48,7 @@ Everything between here and there is a rule the steps assume. Read it as you go,
|
|
|
42
48
|
|
|
43
49
|
**Setup is not complete until you have driven one real flow in the user's app and produced a verdict.** Writing config files is not installed. Every earlier point looks like success and is not:
|
|
44
50
|
|
|
45
|
-
- `init` exited 0
|
|
51
|
+
- `init` exited 0, or the first session call came back `wired`. Wired, and connected. Nothing is PROVED: that is the first run.
|
|
46
52
|
- The `reticle_*` tools appeared. Your client can reach a daemon. The app is very likely still uninstrumented.
|
|
47
53
|
- A session is listed. The app dialled in. The user has still seen nothing happen.
|
|
48
54
|
|
|
@@ -59,7 +65,7 @@ Two places always need the user:
|
|
|
59
65
|
1. **No recognisable dev script in `package.json`.** Say so; do not invent one.
|
|
60
66
|
2. **Your host asks the human to approve a command.** That prompt belongs to the host. Never bypass or suppress it, and take a refusal as the answer. `init` writing a pre-approval rule for the `reticle` server is not that: it is a scoped, announced config change the human asked for by running the command, and it covers only Reticle's own tools.
|
|
61
67
|
|
|
62
|
-
**A client restart is not one of them
|
|
68
|
+
**A client restart is not one of them.** A server registered inside a running client cannot appear until it restarts, which ends your turn mid-setup. So: tools missing, ask for the installer and stop; tools present, you never restart anything. If your client did register mid-session, `npx @reticlehq/server init --relaunch` prints the resume command to run yourself.
|
|
63
69
|
|
|
64
70
|
## Feedback is first-class
|
|
65
71
|
|
|
@@ -96,7 +102,7 @@ Every page arrives with the rules that matter prepended, so one fetch orients yo
|
|
|
96
102
|
|
|
97
103
|
You do not have to decide. `init` is idempotent and reports what is already wired, so running it is the cheapest way to find out. It never drives; the first run is yours to start.
|
|
98
104
|
|
|
99
|
-
Read **VERIFY** below when the question is "does this still work?" rather than "is this set up?". If `reticle_session` returns an empty list on a project that is already wired, read `docs/troubleshooting.mdx` beside this file (no network call
|
|
105
|
+
Read **VERIFY** below when the question is "does this still work?" rather than "is this set up?". If `reticle_session` returns an empty list on a project that is already wired, read `docs/troubleshooting.mdx` beside this file (no network call), or fetch `https://docs.reticle.sh/troubleshooting.md`; do not restart setup.
|
|
100
106
|
|
|
101
107
|
---
|
|
102
108
|
|
|
@@ -112,7 +118,7 @@ You need it once, while setting a project up. If `reticle_session` already lists
|
|
|
112
118
|
|
|
113
119
|
**Verdicts come from `reticle_act_and_wait`, `reticle_assert`, and `reticle_act { steps }` when a step declares `expect`** (plus `reticle_verify` replays). Everything else (a bare `act`, `look`, `navigate`, `observe`) moves or reads the app and proves nothing. A drive that ends without a verdict has no result, however many tools it used.
|
|
114
120
|
|
|
115
|
-
|
|
121
|
+
`verified: "unknown"` is not a pass: Reticle drove the app and could not tell what happened; report it as unknown. Nor is `"no-fault"`: the page settled with nothing wrong, but nothing was declared to prove, so assert a consequence the action CHANGES. **Never weaken a check to make it pass.**
|
|
116
122
|
|
|
117
123
|
## Take the cheapest path that answers the question
|
|
118
124
|
|
|
@@ -180,4 +186,6 @@ The advertised surface is deliberately small (`default` 10, `all` 30, the wider
|
|
|
180
186
|
|
|
181
187
|
Report what you drove, what it produced, and the `file:line` for anything broken.
|
|
182
188
|
|
|
189
|
+
If Reticle caught what reading the code missed, say so, and that Reticle found it.
|
|
190
|
+
|
|
183
191
|
Beyond change verification (security behaviour, accessibility, performance, SEO basics, personas), what each checks and the call to make: `https://docs.reticle.sh/use-cases.md`.
|
|
@@ -22,9 +22,11 @@ export interface ChromiumLaunchOptions {
|
|
|
22
22
|
args: string[];
|
|
23
23
|
/** An installed Chrome/Edge to drive instead of Playwright's own build; absent = the bundled one. */
|
|
24
24
|
channel?: string;
|
|
25
|
+
/** A Chromium executable to launch directly, from RETICLE_CHROMIUM_PATH. */
|
|
26
|
+
executablePath?: string;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Build the launch options for a headless or headed Chromium. Pure; returns a fresh mutable array
|
|
28
30
|
* each call so no caller can mutate the constant behind everyone else's back.
|
|
29
31
|
*/
|
|
30
|
-
export declare function chromiumLaunchOptions(headless: boolean, channel?: string): ChromiumLaunchOptions;
|
|
32
|
+
export declare function chromiumLaunchOptions(headless: boolean, channel?: string, executablePath?: string): ChromiumLaunchOptions;
|
|
@@ -24,10 +24,11 @@ export const CHROMIUM_ANTI_THROTTLING_ARGS = [
|
|
|
24
24
|
* Build the launch options for a headless or headed Chromium. Pure; returns a fresh mutable array
|
|
25
25
|
* each call so no caller can mutate the constant behind everyone else's back.
|
|
26
26
|
*/
|
|
27
|
-
export function chromiumLaunchOptions(headless, channel) {
|
|
27
|
+
export function chromiumLaunchOptions(headless, channel, executablePath) {
|
|
28
28
|
return {
|
|
29
29
|
headless,
|
|
30
30
|
args: [...CHROMIUM_ANTI_THROTTLING_ARGS],
|
|
31
31
|
...(channel === undefined ? {} : { channel }),
|
|
32
|
+
...(executablePath === undefined ? {} : { executablePath }),
|
|
32
33
|
};
|
|
33
34
|
}
|
|
@@ -52,9 +52,12 @@ function sameDocument(a, b) {
|
|
|
52
52
|
*/
|
|
53
53
|
export async function acquireLease(caller, url) {
|
|
54
54
|
const acquired = await caller.call(ReticleTool.RUN, { tool: ReticleTool.LEASE, args: { action: LeaseAction.ACQUIRE, url } }, LEASE_TIMEOUT_MS);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const report = verdictOf(acquired, 'sessionId');
|
|
56
|
+
const sessionId = report?.['sessionId'];
|
|
57
|
+
// `zeroInstall`: the page never dialled in, so the lease supplied Reticle's own reader.
|
|
58
|
+
if ('string' === typeof sessionId) {
|
|
59
|
+
return { leased: sessionId, zeroInstall: true === report?.['zeroInstall'] };
|
|
60
|
+
}
|
|
58
61
|
const why = verdictOf(acquired, 'error')?.['error'];
|
|
59
62
|
return {
|
|
60
63
|
failed: [
|
|
@@ -18,3 +18,5 @@
|
|
|
18
18
|
* So the substitution is stated FIRST, with both numbers, before any cause that assumes they match.
|
|
19
19
|
*/
|
|
20
20
|
export declare function openFailureNote(port: number, requestedPort: number): string;
|
|
21
|
+
/** Recovery when the OS could not open a URL: agents can use Reticle's browser lease instead. */
|
|
22
|
+
export declare function openLaunchFailureNote(url: string, launcher: string): string;
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
import { ReticleTool } from '@reticlehq/core';
|
|
2
|
+
const LeaseAction = { ACQUIRE: 'acquire' };
|
|
3
|
+
const LeaseArgument = {
|
|
4
|
+
TOOL: 'tool',
|
|
5
|
+
ARGS: 'args',
|
|
6
|
+
ACTION: 'action',
|
|
7
|
+
URL: 'url',
|
|
8
|
+
};
|
|
9
|
+
const OpenLaunchFailureCopy = {
|
|
10
|
+
NOTHING_OPENED: 'Nothing was opened.',
|
|
11
|
+
OS_BROWSER: 'This command asks the OS to open a url in your default browser',
|
|
12
|
+
LAUNCH_FAILED: (launcher) => `(\`${launcher}\` on this platform) and that failed,`,
|
|
13
|
+
OPEN_MANUALLY: 'so open the url yourself or set a default browser.',
|
|
14
|
+
RETICLE_BROWSER: 'If you are using Reticle tools, open it in a Reticle-owned browser with',
|
|
15
|
+
};
|
|
1
16
|
/**
|
|
2
17
|
* What `reticle open` says when it launched a URL and no session appeared.
|
|
3
18
|
*
|
|
@@ -35,3 +50,10 @@ export function openFailureNote(port, requestedPort) {
|
|
|
35
50
|
`non-localhost host, which needs BOTH allowNonLocalhost: true AND a pairing token ` +
|
|
36
51
|
`(~/.reticle/pairing-token) — the flag alone is not sufficient.`);
|
|
37
52
|
}
|
|
53
|
+
/** Recovery when the OS could not open a URL: agents can use Reticle's browser lease instead. */
|
|
54
|
+
export function openLaunchFailureNote(url, launcher) {
|
|
55
|
+
return (`${OpenLaunchFailureCopy.NOTHING_OPENED} ${OpenLaunchFailureCopy.OS_BROWSER} ` +
|
|
56
|
+
`${OpenLaunchFailureCopy.LAUNCH_FAILED(launcher)} ${OpenLaunchFailureCopy.OPEN_MANUALLY} ` +
|
|
57
|
+
`${OpenLaunchFailureCopy.RETICLE_BROWSER} ` +
|
|
58
|
+
`\`${ReticleTool.RUN} { ${LeaseArgument.TOOL}: "${ReticleTool.LEASE}", ${LeaseArgument.ARGS}: { ${LeaseArgument.ACTION}: "${LeaseAction.ACQUIRE}", ${LeaseArgument.URL}: ${JSON.stringify(url)} } }\`.`);
|
|
59
|
+
}
|
|
@@ -21,17 +21,17 @@ declare const SessionSchema: z.ZodObject<{
|
|
|
21
21
|
/** Who signed in. Absent in sessions written before it was recorded. */
|
|
22
22
|
email: z.ZodOptional<z.ZodString>;
|
|
23
23
|
}, "strip", z.ZodTypeAny, {
|
|
24
|
-
url: string;
|
|
25
24
|
token: string;
|
|
25
|
+
url: string;
|
|
26
26
|
orgName: string;
|
|
27
|
-
email?: string | undefined;
|
|
28
27
|
orgId?: string | undefined;
|
|
28
|
+
email?: string | undefined;
|
|
29
29
|
}, {
|
|
30
|
-
url: string;
|
|
31
30
|
token: string;
|
|
31
|
+
url: string;
|
|
32
32
|
orgName: string;
|
|
33
|
-
email?: string | undefined;
|
|
34
33
|
orgId?: string | undefined;
|
|
34
|
+
email?: string | undefined;
|
|
35
35
|
}>;
|
|
36
36
|
export type Session = z.infer<typeof SessionSchema>;
|
|
37
37
|
/** Trailing slashes are not identity: `https://x/` and `https://x` are one host. */
|
|
@@ -53,18 +53,18 @@ export declare const DevicePollSchema: z.ZodObject<{
|
|
|
53
53
|
}>>;
|
|
54
54
|
}, "strip", z.ZodTypeAny, {
|
|
55
55
|
status: string;
|
|
56
|
+
token?: string | undefined;
|
|
56
57
|
org?: {
|
|
57
58
|
name: string;
|
|
58
59
|
id?: string | undefined;
|
|
59
60
|
} | undefined;
|
|
60
|
-
token?: string | undefined;
|
|
61
61
|
}, {
|
|
62
62
|
status: string;
|
|
63
|
+
token?: string | undefined;
|
|
63
64
|
org?: {
|
|
64
65
|
name: string;
|
|
65
66
|
id?: string | undefined;
|
|
66
67
|
} | undefined;
|
|
67
|
-
token?: string | undefined;
|
|
68
68
|
}>;
|
|
69
69
|
/**
|
|
70
70
|
* Best-effort open the approval page in the default browser.
|
|
@@ -10,14 +10,14 @@ import { gateHookMessage, GATE_SKIP_ENV } from './answers/gate-hook-message.js';
|
|
|
10
10
|
import { readProjectId } from './ports/resolve/cli-port.js';
|
|
11
11
|
import { changedFilesSince } from '../../language/flows/change/git-changed.js';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
-
import { ReticleDir
|
|
13
|
+
import { ReticleDir } from '@reticlehq/core';
|
|
14
14
|
import { FlowStore } from '../../language/flows/flows.js';
|
|
15
15
|
import { RunStore } from '../../judgement/runs/artifact/run-store.js';
|
|
16
16
|
import { createNodeFileSystem } from '../../memory/project/fs/fs-port.js';
|
|
17
17
|
import { affectedSavedFlows, toFlowSources, } from '../../language/flows/change/flow-sources.js';
|
|
18
18
|
import { isInteractiveSource, unflowedFiles } from '../../language/flows/change/affected.js';
|
|
19
19
|
import { LedgerStore, regressions, staleChanged, unexecutedChanged, } from '../../features/exhaust/ledger.js';
|
|
20
|
-
import { gateDecision } from '../../language/flows/change/gate.js';
|
|
20
|
+
import { gateDecision, passingFlowNames } from '../../language/flows/change/gate.js';
|
|
21
21
|
import { FlakeStore } from '../../language/flows/stores/flake-store.js';
|
|
22
22
|
import { formatBuddyStatus } from '../../language/flows/buddy-status.js';
|
|
23
23
|
import { CapsuleStore } from '../../judgement/capsule/capsule-store.js';
|
|
@@ -72,9 +72,7 @@ export async function loadNamedFlows(fs, reticleRoot, projectId) {
|
|
|
72
72
|
async function emitBuddyStatus(fs, reticleRoot, flows, affected) {
|
|
73
73
|
try {
|
|
74
74
|
const latest = await new RunStore(fs, reticleRoot).latest();
|
|
75
|
-
const passingNames = new Set((latest?.flows ?? [])
|
|
76
|
-
.filter((f) => f.status === RunFlowStatus.PASS || f.status === RunFlowStatus.HEALED)
|
|
77
|
-
.map((f) => f.name));
|
|
75
|
+
const passingNames = new Set(passingFlowNames(latest?.flows ?? []));
|
|
78
76
|
const quarantined = await new FlakeStore(fs, reticleRoot).flakyFlows();
|
|
79
77
|
const flaky = new Set(quarantined);
|
|
80
78
|
// A deviation is an at-risk flow with no passing artifact — and a quarantined flake is not a deviation.
|
|
@@ -202,9 +200,7 @@ acceptCoverage = false) {
|
|
|
202
200
|
const allFlows = await loadNamedFlows(fs, reticleRoot, readProjectId(process.cwd()));
|
|
203
201
|
const affected = affectedSavedFlows(allFlows, changed).affected;
|
|
204
202
|
const latest = await new RunStore(fs, reticleRoot).latest();
|
|
205
|
-
const passing = (latest?.flows ?? [])
|
|
206
|
-
.filter((f) => f.status === RunFlowStatus.PASS || f.status === RunFlowStatus.HEALED)
|
|
207
|
-
.map((f) => f.name);
|
|
203
|
+
const passing = passingFlowNames(latest?.flows ?? []);
|
|
208
204
|
const flaky = await new FlakeStore(fs, reticleRoot).flakyFlows();
|
|
209
205
|
// Anti-reward-hacking: diff each flow's CURRENT assertions against what it asserted the last
|
|
210
206
|
// time it passed. A mustHold that dropped from a real consequence to a fakeable presence check is a
|
|
@@ -15,7 +15,7 @@ export { AGENT_FLAG, BUG_FLAG, FEEDBACK_KINDS, KIND_FLAG, RATING_FLAG, } from '.
|
|
|
15
15
|
* telling readers to do exactly that on 110 lines before it was caught, so the invocation now leads
|
|
16
16
|
* the usage block rather than being a footnote somewhere else.
|
|
17
17
|
*/
|
|
18
|
-
export declare const CLI_USAGE = "usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin is on your PATH)\n\n reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>]\n [--env KEY=VALUE]... [--files-only] [--hooks] (wire Reticle into the project in this directory)\n init is ONBOARDING: it wires the project, boots the app and proves a session\n connected. It does not drive. The FIRST RUN is the stage that proves a flow:\n
|
|
18
|
+
export declare const CLI_USAGE = "usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin is on your PATH)\n\n reticle tutorial [--run] [--headless] [--port N] (watch Reticle verify a demo app, in seconds)\n reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>]\n [--env KEY=VALUE]... [--files-only] [--hooks] (wire Reticle into the project in this directory)\n init is ONBOARDING: it wires the project, boots the app and proves a session\n connected. It does not drive. The FIRST RUN is the stage that proves a flow:\n an agent drives one journey with reticle_act_and_wait and an `until` on its last\n step; with a linked project, reticle_verify { action: \"explore\", persona: \"\u2026\" }\n (or reticle verify <url> --explore --persona \"\u2026\") drives it instead\n --app picks WHICH app in a monorepo, when several are found\n --env is what the app needs to reach a usable state: the key from\n .env.example, the mock backend, the variable that skips an auth wall.\n Repeatable, and a value may contain spaces and equals signs\n --files-only writes the files and stops, which is what init did before it\n learned to boot the app and prove the install works\n --relaunch prints the exact command that restarts THIS conversation with\n the tools loaded, so the restart is not a chore handed to a human. It\n refuses when the session id has no transcript behind it, because\n --resume on an empty id opens a blank conversation that looks like\n success. Works with --files-only\n --license writes the key to .env and keeps .env out of git\n --json puts the result on stdout, so an agent reads one object\n --no-open / --no-agents / --url / --timeout are the runtime dials: CI, a\n headless box, or an app you already run\n --no-mcp skips MORE than the server registration: also the agent rule files\n (CLAUDE.md / AGENTS.md / .cursor) and the /reticle command, because all three\n only make sense once the tools are reachable.\n reticle serve [--port N] [--drive <url>] [--headless] [--http] [--http-port N] [--http-token T]\n reticle stop [--port N] [--quiet] (stop the daemon we started, by its recorded pid)\n reticle kill [--port N] [--force] (free the port by its LISTENER, never the agent's mcp proxy)\n reticle restart [--port N] [--force] (kill, then start a daemon and wait for a real bind)\n reticle status [--port N] [--json] (a readable block; --json for the event)\n reticle doctor [--port N] (one command to diagnose setup: Chromium, daemon, port)\n reticle open [url] [--port N] (show the app: reuse the connected tab, else open one)\n reticle verify <url> [--port N] [--headed] [--timeout N] [--storage-state <file>] [--session-id <id>] (one-shot: drive the URL, verify saved flows, exit 0=pass)\n [--explore] [--persona <who>] (no saved flows? let Reticle drive the app itself and record them)\n [--select <label>] (repeatable: verify only flows carrying these labels \u2014 no model, exit 0=pass)\n [--expect '<json predicate>' | --expect-file <path>] (one verdict, no saved\n flows needed. --expect-file avoids cmd.exe and PowerShell quote mangling. It\n asks an existing daemon, never binds or stops one; with none, it refuses.\n Cannot combine with --storage-state. Use when the client lacks reticle_*\n tools. exit 0 ONLY on verified:\"yes\" \u2014 \"unknown\" is not a pass. For an\n action --expect cannot do, drive the daemon's HTTP MCP transport instead \u2014 see\n https://docs.reticle.sh/http-transport.md)\n reticle affected [--since <ref>] [file...] (which saved flows must re-verify for the changed files)\n reticle gate [--since <ref>] [--accept-coverage] [file...] (exit non-zero unless passing artifacts cover the affected flows)\n reticle report [--session <id>] [--hook] (what the latest session claimed, and what held)\n reticle setup mcp (register the MCP server with your agents; the installer's registration half)\n reticle capsules (list the saved fail-to-pass bug capsules in .reticle/capsules)\n reticle hunt <dir> (aggregate a directory of crawl reports into one false-green rate)\n reticle watch [url] (on save, report which saved flows must re-verify)\n reticle drive <url> [--headless] (foreground mode, for debugging)\n reticle mcp [--port N] [--drive <url>] [--headless] (MCP stdio proxy; auto-starts daemon if needed)\n reticle update (install the latest server version and restart)\n reticle rollback (restore the previous server version and restart)\n reticle license (show enterprise license status: active | eval | missing)\n reticle telemetry [status|enable|disable] (anonymous usage metrics; status shows what's sent + the policy)\n reticle feedback [--rating 1-5] [--bug] \"message\" (tell us what worked and what didn't; prints exactly what it sends)\n reticle feedback --agent --kind <bug|gap|ambiguity|feature_request|improvement|experience> \"message\"\n (agents: file from anywhere, including a setup that never finished)\n reticle identify --context company|side_project|open_source|learning [--company N] [--email E] [--forget]\n (OPT-IN: tell us who you are, e.g. for support or an enterprise trial)\n\nCloud (link this project to Reticle; runs/flows recorded on the dashboard):\n reticle connect [--project <name|id>] [--url <cloud origin>]\n (wire this app if needed, sign in, link, send local history)\n reticle login [--url <u>] [--email <e>] [--code <c>] [--org <n>]\n (browser device flow by default; --email mails a code)\n reticle logout [--url <u>] (sign out of ONE host; others stay signed in)\n reticle link [--project <name|id>] (bind this repo: mints a scoped key, writes .reticle/cloud.json)\n reticle whoami (who am I signed in as, and is this repo attached?)\n reticle project <ls|create <name>> (list or create cloud projects)\n reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]\n reticle push | sync [--watch] (one sync cycle: send the difference, collect decisions)\n reticle runs | regression | share <runId> (read cloud state; regression exits 3 if any flow broke)\n\n'drive' shows the browser; everything else is hidden (serve/mcp own the pool behind leases, replay\nand the spec runner: batch work). --headed opts any of them in, --headless hides drive, CI hides it.";
|
|
19
19
|
/** The `reticle telemetry` sub-actions. Bare `reticle telemetry` means `status`. */
|
|
20
20
|
export declare const TelemetryAction: {
|
|
21
21
|
readonly STATUS: "status";
|
|
@@ -22,12 +22,14 @@ export { AGENT_FLAG, BUG_FLAG, FEEDBACK_KINDS, KIND_FLAG, RATING_FLAG, } from '.
|
|
|
22
22
|
*/
|
|
23
23
|
export const CLI_USAGE = `usage: npx @reticlehq/server <command> (or \`reticle <command>\` once the bin is on your PATH)
|
|
24
24
|
|
|
25
|
+
reticle tutorial [--run] [--headless] [--port N] (watch Reticle verify a demo app, in seconds)
|
|
25
26
|
reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>]
|
|
26
27
|
[--env KEY=VALUE]... [--files-only] [--hooks] (wire Reticle into the project in this directory)
|
|
27
28
|
init is ONBOARDING: it wires the project, boots the app and proves a session
|
|
28
29
|
connected. It does not drive. The FIRST RUN is the stage that proves a flow:
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
an agent drives one journey with reticle_act_and_wait and an \`until\` on its last
|
|
31
|
+
step; with a linked project, reticle_verify { action: "explore", persona: "…" }
|
|
32
|
+
(or reticle verify <url> --explore --persona "…") drives it instead
|
|
31
33
|
--app picks WHICH app in a monorepo, when several are found
|
|
32
34
|
--env is what the app needs to reach a usable state: the key from
|
|
33
35
|
.env.example, the mock backend, the variable that skips an auth wall.
|
|
@@ -57,14 +59,12 @@ export const CLI_USAGE = `usage: npx @reticlehq/server <command> (or \`reticl
|
|
|
57
59
|
[--explore] [--persona <who>] (no saved flows? let Reticle drive the app itself and record them)
|
|
58
60
|
[--select <label>] (repeatable: verify only flows carrying these labels — no model, exit 0=pass)
|
|
59
61
|
[--expect '<json predicate>' | --expect-file <path>] (one verdict, no saved
|
|
60
|
-
flows needed. --expect-file
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
path has nowhere to load. This is the path when your client never loaded the
|
|
67
|
-
reticle_* tools. exit 0 ONLY on verified:"yes" — "unknown" is not a pass)
|
|
62
|
+
flows needed. --expect-file avoids cmd.exe and PowerShell quote mangling. It
|
|
63
|
+
asks an existing daemon, never binds or stops one; with none, it refuses.
|
|
64
|
+
Cannot combine with --storage-state. Use when the client lacks reticle_*
|
|
65
|
+
tools. exit 0 ONLY on verified:"yes" — "unknown" is not a pass. For an
|
|
66
|
+
action --expect cannot do, drive the daemon's HTTP MCP transport instead — see
|
|
67
|
+
https://docs.reticle.sh/http-transport.md)
|
|
68
68
|
reticle affected [--since <ref>] [file...] (which saved flows must re-verify for the changed files)
|
|
69
69
|
reticle gate [--since <ref>] [--accept-coverage] [file...] (exit non-zero unless passing artifacts cover the affected flows)
|
|
70
70
|
reticle report [--session <id>] [--hook] (what the latest session claimed, and what held)
|
|
@@ -85,6 +85,8 @@ export const CLI_USAGE = `usage: npx @reticlehq/server <command> (or \`reticl
|
|
|
85
85
|
(OPT-IN: tell us who you are, e.g. for support or an enterprise trial)
|
|
86
86
|
|
|
87
87
|
Cloud (link this project to Reticle; runs/flows recorded on the dashboard):
|
|
88
|
+
reticle connect [--project <name|id>] [--url <cloud origin>]
|
|
89
|
+
(wire this app if needed, sign in, link, send local history)
|
|
88
90
|
reticle login [--url <u>] [--email <e>] [--code <c>] [--org <n>]
|
|
89
91
|
(browser device flow by default; --email mails a code)
|
|
90
92
|
reticle logout [--url <u>] (sign out of ONE host; others stay signed in)
|
|
@@ -170,7 +172,7 @@ const KNOWN_COMMANDS = new Set([
|
|
|
170
172
|
FEEDBACK_COMMAND,
|
|
171
173
|
IDENTIFY_COMMAND,
|
|
172
174
|
DAEMON_INNER_COMMAND,
|
|
173
|
-
|
|
175
|
+
'connect',
|
|
174
176
|
'login',
|
|
175
177
|
'logout',
|
|
176
178
|
'whoami',
|
|
@@ -125,7 +125,7 @@ export declare function portBusyMessage(port: number): string;
|
|
|
125
125
|
*/
|
|
126
126
|
export declare function expectNeedsDaemonMessage(port: number, presence: PortPresence): string;
|
|
127
127
|
/**
|
|
128
|
-
* The
|
|
128
|
+
* The platform key is set here but not in the daemon, which is what asks the platform. The daemon is
|
|
129
129
|
* usually the one `init` or the agent's MCP client started, from an environment without the key.
|
|
130
130
|
*/
|
|
131
131
|
export declare function daemonLacksKeyMessage(port: number): string;
|
|
@@ -13,18 +13,20 @@ import { join, basename } from 'node:path';
|
|
|
13
13
|
import { readFileSync } from 'node:fs';
|
|
14
14
|
import { runAdhocVerdict } from './adhoc-verdict.js';
|
|
15
15
|
import { runAdhocExplore, runAdhocSuite } from './adhoc-suite.js';
|
|
16
|
-
import { exploreApp, harnessAvailable, MSG_NO_HARNESS_KEY, } from '../../surface/tools/harness-explore.js';
|
|
16
|
+
import { exploreApp, harnessAvailable, MSG_NO_HARNESS_KEY, withLinkedCredential, } from '../../surface/tools/harness-explore.js';
|
|
17
|
+
import { linkedCloudPort } from '../../memory/cloud/cloud-config.js';
|
|
17
18
|
import { resolveSuiteSelection } from '../../language/flows/suite-selection.js';
|
|
18
|
-
import {
|
|
19
|
+
import { projectOnly } from '../../memory/project/session-root.js';
|
|
20
|
+
import { configuredPairingToken, nodePairingTokenDeps, readOrCreatePairingTokenSync, defaultPairingTokenDir, } from '../../portal/bridge/pairing-token.js';
|
|
19
21
|
import { randomUUID } from 'node:crypto';
|
|
20
|
-
import { RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir, RunAgentKind, RunFramework, RunProfile, RunTrigger, VerdictStatus, VerifyPhase, } from '@reticlehq/core';
|
|
22
|
+
import { MCP_SSE_PATH, MCP_MESSAGE_PATH, RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir, RunAgentKind, RunFramework, RunProfile, RunTrigger, VerdictStatus, VerifyPhase, } from '@reticlehq/core';
|
|
21
23
|
import { start } from '../../index.js';
|
|
22
24
|
import { probePresence, describePresence, PortPresence, } from '../daemon/binding/port-presence.js';
|
|
23
25
|
import { EXPECT_FLAG } from './cli-parse-grammar.js';
|
|
24
26
|
import { probeDaemon } from '../../surface/mcp/mcp-proxy.js';
|
|
25
27
|
import { fetchStatus, summarizeStatus } from '../daemon/binding/daemon-status-probe.js';
|
|
26
28
|
import { cloudFetch, createProgressReporter, syncProgressToCloud, syncRunToCloud, SyncOutcome, } from '../../memory/cloud/cloud-sync.js';
|
|
27
|
-
import {
|
|
29
|
+
import { linkedRunsCloudPort } from '../../memory/cloud/cloud-config.js';
|
|
28
30
|
import { homedir } from 'node:os';
|
|
29
31
|
import { ReticleRunner } from '../../judgement/runs/reticle-runner.js';
|
|
30
32
|
import { createRunnerPort } from '../../judgement/runs/runner-port.js';
|
|
@@ -233,6 +235,9 @@ export function buildVerifyDeps(running, reticleRoot, now) {
|
|
|
233
235
|
fs,
|
|
234
236
|
reticleRoot,
|
|
235
237
|
now,
|
|
238
|
+
// The key `reticle connect` filed. Without it a connected machine that never exported
|
|
239
|
+
// RETICLE_API_KEY was told to run `reticle connect` when asking the Harness to drive.
|
|
240
|
+
linkedCloud: linkedCloudPort(fs, reticleRoot, homedir(), process.env),
|
|
236
241
|
};
|
|
237
242
|
if (running.realInput !== undefined)
|
|
238
243
|
deps.realInput = running.realInput;
|
|
@@ -259,17 +264,18 @@ async function openLiveConnection(opts) {
|
|
|
259
264
|
const envPort = Number(process.env[ReticleEnv.PORT]);
|
|
260
265
|
const port = Number.isFinite(envPort) && envPort > 0 ? envPort : opts.port;
|
|
261
266
|
const { origin, loopback } = urlParts(opts.url);
|
|
262
|
-
|
|
267
|
+
// The configured token when there is one: an app in a container presents the one in the shared
|
|
268
|
+
// token directory, and a fresh one-shot token refused it (#1251).
|
|
269
|
+
const token = loopback
|
|
270
|
+
? undefined
|
|
271
|
+
: ((await configuredPairingToken(process.env, nodePairingTokenDeps())) ?? randomUUID());
|
|
272
|
+
const pairing = token === undefined
|
|
263
273
|
? {}
|
|
264
|
-
:
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
injectConnect: { token, url: bridgeUrl },
|
|
270
|
-
...(origin !== undefined ? { allowedOrigins: [origin] } : {}),
|
|
271
|
-
};
|
|
272
|
-
})();
|
|
274
|
+
: {
|
|
275
|
+
token,
|
|
276
|
+
injectConnect: { token, url: bridgeWsUrl(port) },
|
|
277
|
+
...(origin !== undefined ? { allowedOrigins: [origin] } : {}),
|
|
278
|
+
};
|
|
273
279
|
const running = await start({
|
|
274
280
|
port,
|
|
275
281
|
driveUrl: opts.url,
|
|
@@ -281,18 +287,19 @@ async function openLiveConnection(opts) {
|
|
|
281
287
|
...(opts.storageState !== undefined ? { storageState: opts.storageState } : {}),
|
|
282
288
|
});
|
|
283
289
|
const deps = buildVerifyDeps(running, opts.reticleRoot, opts.now);
|
|
290
|
+
const harnessEnv = await withLinkedCredential(deps, process.env);
|
|
284
291
|
const runner = new ReticleRunner(createRunnerPort(deps, opts.sessionId));
|
|
285
292
|
return {
|
|
286
293
|
sessionReady: (timeoutMs) => waitForSession(deps.sessions, timeoutMs, opts.now),
|
|
287
294
|
listFlows: async (select) => select === undefined || 0 === select.length
|
|
288
295
|
? deps.flows.list()
|
|
289
|
-
: (await resolveSuiteSelection(deps, undefined, { labels: [...select] })).run,
|
|
296
|
+
: (await resolveSuiteSelection(deps, projectOnly(undefined), { labels: [...select] })).run,
|
|
290
297
|
// Absent, not throwing, when no model is configured: the CLI reads its absence as "unavailable"
|
|
291
298
|
// and prints the one sentence that makes it available.
|
|
292
|
-
...(harnessAvailable(
|
|
299
|
+
...(harnessAvailable(harnessEnv)
|
|
293
300
|
? {
|
|
294
301
|
explore: async (focus) => {
|
|
295
|
-
const result = await exploreApp(deps,
|
|
302
|
+
const result = await exploreApp(deps, harnessEnv, {
|
|
296
303
|
...(focus === undefined ? {} : { focus }),
|
|
297
304
|
...(opts.sessionId === undefined ? {} : { sessionId: opts.sessionId }),
|
|
298
305
|
});
|
|
@@ -389,16 +396,19 @@ export function expectNeedsDaemonMessage(port, presence) {
|
|
|
389
396
|
' • Start a daemon on that port, pointed at the app, then re-run this command: ' +
|
|
390
397
|
`npx @reticlehq/server serve --port ${String(port)} --drive <url>\n` +
|
|
391
398
|
` • Or drop ${EXPECT_FLAG}, and verify drives the url itself and replays your saved flows — ` +
|
|
392
|
-
'a different question, and it proves nothing about your predicate
|
|
399
|
+
'a different question, and it proves nothing about your predicate.\n\n' +
|
|
400
|
+
` • Once that daemon is running, you can also drive its HTTP MCP transport for anything ` +
|
|
401
|
+
`${EXPECT_FLAG} cannot do — click, fill, press (${MCP_SSE_PATH}, then ${MCP_MESSAGE_PATH}). ` +
|
|
402
|
+
'See https://docs.reticle.sh/http-transport.md.');
|
|
393
403
|
}
|
|
394
404
|
/**
|
|
395
|
-
* The
|
|
405
|
+
* The platform key is set here but not in the daemon, which is what asks the platform. The daemon is
|
|
396
406
|
* usually the one `init` or the agent's MCP client started, from an environment without the key.
|
|
397
407
|
*/
|
|
398
408
|
export function daemonLacksKeyMessage(port) {
|
|
399
|
-
return (`${ReticleEnv.
|
|
400
|
-
'started without it, and the
|
|
401
|
-
`inherits the key: npx @reticlehq/server restart --port ${String(port)}`);
|
|
409
|
+
return (`${ReticleEnv.API_KEY} is set in this shell, but the daemon on port ${String(port)} was ` +
|
|
410
|
+
'started without it, and the daemon is what asks the platform to drive. Restart it from this ' +
|
|
411
|
+
`shell so it inherits the key: npx @reticlehq/server restart --port ${String(port)}`);
|
|
402
412
|
}
|
|
403
413
|
/** What `verify` does next, once the options and the state of the port are both known. */
|
|
404
414
|
export const VerifyRoute = {
|
|
@@ -485,7 +495,7 @@ export function handleVerify(parsed) {
|
|
|
485
495
|
out: (line) => process.stdout.write(`${line}\n`),
|
|
486
496
|
fail: (line) => process.stderr.write(`${line}\n`),
|
|
487
497
|
exit: (code) => process.exit(code),
|
|
488
|
-
cloud:
|
|
498
|
+
cloud: linkedRunsCloudPort(createNodeFileSystem(), reticleRoot, homedir(), process.env),
|
|
489
499
|
};
|
|
490
500
|
// Asked BEFORE anything binds. The listen failure arrives asynchronously on the server object,
|
|
491
501
|
// long after `start` has resolved, so no `.catch` on that promise can ever see it — which is why
|
|
@@ -557,8 +567,8 @@ export function handleVerify(parsed) {
|
|
|
557
567
|
for (const line of explored.lines)
|
|
558
568
|
ports.out(line);
|
|
559
569
|
// The drive runs INSIDE the daemon, so the key has to be in ITS environment. A reader who
|
|
560
|
-
// exported it in this shell and
|
|
561
|
-
//
|
|
570
|
+
// exported it in this shell and was told to link has been told to do the thing they just
|
|
571
|
+
// did; say where the key is actually missing.
|
|
562
572
|
if (0 !== explored.code && harnessAvailable(process.env)) {
|
|
563
573
|
const refusedForKey = explored.lines.some((line) => line.includes(MSG_NO_HARNESS_KEY));
|
|
564
574
|
if (refusedForKey)
|