@reticlehq/server 3.8.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 +2 -2
- 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 +1 -19
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +30 -23
- package/dist/command/cli/cli-flow-commands.js +7 -1
- 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 +1 -10
- package/dist/command/cli/cli-parse.js +62 -121
- package/dist/command/cli/cli-usage.js +5 -2
- package/dist/command/cli/cloud-link.d.ts +1 -20
- 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/ports/port-holder.js +6 -1
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/daemon/daemon.d.ts +6 -0
- package/dist/command/daemon/daemon.js +52 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- 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/version/version-skew.d.ts +2 -0
- package/dist/command/version/version-skew.js +7 -3
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/license/license.d.ts +2 -14
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/fields/flow-secret-field.js +1 -1
- package/dist/language/flows/flow-expect-grammar.js +14 -6
- 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 +88 -283
- package/dist/language/flows/flow-replayable.d.ts +1 -1
- 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-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +34 -66
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/machine/git-tracks.js +1 -0
- package/dist/memory/cloud/cloud-sync.d.ts +2 -24
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- 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/self-observation.js +3 -1
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/portal/input/viewport-tools.d.ts +19 -15
- package/dist/portal/pool/browser-pool.d.ts +5 -55
- package/dist/portal/pool/browser-pool.js +121 -100
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/production/production-activation.d.ts +5 -27
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/zero-install-first-call.d.ts +3 -0
- package/dist/portal/session/zero-install-first-call.js +33 -12
- package/dist/surface/mcp/mcp.d.ts +3 -3
- package/dist/surface/mcp/mcp.js +4 -6
- package/dist/surface/tools/act/act-danger.js +32 -17
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act-sequence-tool.js +3 -3
- package/dist/surface/tools/act-tools.js +13 -14
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- 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 +36 -16
- package/dist/surface/tools/empty-session-list.d.ts +5 -29
- package/dist/surface/tools/feedback-tools.js +1 -1
- package/dist/surface/tools/gap-novelty.d.ts +1 -1
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-gate.d.ts +1 -1
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +23 -1
- package/dist/surface/tools/lease-readiness.js +75 -9
- package/dist/surface/tools/lease-tools.d.ts +1 -52
- package/dist/surface/tools/lease-tools.js +79 -142
- package/dist/surface/tools/observe-tools.js +3 -1
- package/dist/surface/tools/read-tools.js +5 -5
- 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 +5 -3
- package/dist/surface/tools/tools.js +1 -1
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +2 -0
- package/docs/agent-cheatsheet.md +12 -0
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/verify.mdx +8 -1
- package/docs/first-drive.md +7 -0
- package/docs/multi-agent-testing.md +2 -0
- package/docs/predicates.mdx +6 -0
- package/docs/skill.md +3 -3
- package/docs/troubleshooting.mdx +10 -0
- package/docs/usage.md +1 -1
- package/package.json +8 -9
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* It lives apart from `cli.ts` because it has its own timers and its own failure rule — nothing here
|
|
6
6
|
* may ever be able to fail a daemon start — and because `cli.ts` is a dispatcher.
|
|
7
7
|
*/
|
|
8
|
-
import { type SessionSummary } from '@reticlehq/core/telemetry';
|
|
8
|
+
import { DaemonSpawnMethod, type SessionSummary } from '@reticlehq/core/telemetry';
|
|
9
9
|
/**
|
|
10
10
|
* How often a running daemon rolls up its window.
|
|
11
11
|
*
|
|
@@ -29,6 +29,8 @@ export declare const SESSION_FLUSH_MS: number;
|
|
|
29
29
|
* costs nothing on a daemon that never serves a tool, because only a NON-EMPTY window emits.
|
|
30
30
|
*/
|
|
31
31
|
export declare const FIRST_FLUSH_MS: number;
|
|
32
|
+
/** How the parent started this daemon, from the env it set. Anything else reads as absent. */
|
|
33
|
+
export declare function daemonSpawnMethod(env?: Readonly<Record<string, string | undefined>>): DaemonSpawnMethod | undefined;
|
|
32
34
|
/** Stops the timers this installed. Called from the daemon's shutdown path. */
|
|
33
35
|
interface DaemonTelemetry {
|
|
34
36
|
/**
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* It lives apart from `cli.ts` because it has its own timers and its own failure rule — nothing here
|
|
6
6
|
* may ever be able to fail a daemon start — and because `cli.ts` is a dispatcher.
|
|
7
7
|
*/
|
|
8
|
-
import { TelemetryEventKind } from '@reticlehq/core/telemetry';
|
|
8
|
+
import { DaemonSpawnMethod, TelemetryEventKind, } from '@reticlehq/core/telemetry';
|
|
9
|
+
import { ReticleEnv } from '@reticlehq/core';
|
|
9
10
|
import { getTelemetry } from './telemetry.js';
|
|
10
11
|
import { getSessionMetrics } from './session-metrics.js';
|
|
11
12
|
import { profileProject } from './project-profile.js';
|
|
@@ -78,6 +79,11 @@ function withNudgeState(summary) {
|
|
|
78
79
|
...(nudge.offered === undefined ? {} : { updateOffered: nudge.offered }),
|
|
79
80
|
};
|
|
80
81
|
}
|
|
82
|
+
/** How the parent started this daemon, from the env it set. Anything else reads as absent. */
|
|
83
|
+
export function daemonSpawnMethod(env = process.env) {
|
|
84
|
+
const value = env[ReticleEnv.SPAWN_METHOD];
|
|
85
|
+
return Object.values(DaemonSpawnMethod).find((method) => method === value);
|
|
86
|
+
}
|
|
81
87
|
/**
|
|
82
88
|
* Report the daemon as started, profile the project once, and keep the session counters flushing.
|
|
83
89
|
* Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
|
|
@@ -91,7 +97,8 @@ export function installDaemonTelemetry(cwd, now = () => Date.now(),
|
|
|
91
97
|
*/
|
|
92
98
|
port) {
|
|
93
99
|
const metrics = getSessionMetrics(now);
|
|
94
|
-
|
|
100
|
+
const spawnMethod = daemonSpawnMethod();
|
|
101
|
+
void getTelemetry().emit(TelemetryEventKind.DAEMON_STARTED, spawnMethod === undefined ? undefined : { spawnMethod });
|
|
95
102
|
// Reports a past send could not deliver. Off the boot path, and never able to throw into it.
|
|
96
103
|
void drainFeedbackOutbox().catch(() => undefined);
|
|
97
104
|
// Ask npm whether a newer Reticle exists, so the agent can tell the human. Nothing checked before
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type OnboardingStep, type InstallSource, type McpConnection, type McpOutage, type DaemonDeath, type ProxyExit, type WakeRefusal, type ToolRefusal, type AppInstrumentation, type HarnessRun, type HudEvent, type PlatformLink, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core/telemetry';
|
|
1
|
+
import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type OnboardingStep, type InstallSource, type McpConnection, type McpOutage, type DaemonDeath, type ProxyExit, type WakeRefusal, type DaemonSpawnMethod, type ToolRefusal, type AppInstrumentation, type HarnessRun, type HudEvent, type PlatformLink, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core/telemetry';
|
|
2
2
|
/**
|
|
3
3
|
* Presence of this file marks the MACHINE as the Reticle team's own: every event carries
|
|
4
4
|
* `internal: true`. The persistent twin of `RETICLE_INTERNAL=1`, for a dev machine where setting a
|
|
@@ -113,6 +113,8 @@ export interface TelemetryExtra {
|
|
|
113
113
|
daemonDeath?: DaemonDeath;
|
|
114
114
|
/** `wake_refused`: what held the port. */
|
|
115
115
|
wakeRefusal?: WakeRefusal;
|
|
116
|
+
/** `daemon_started`: how the parent started the daemon. */
|
|
117
|
+
spawnMethod?: DaemonSpawnMethod;
|
|
116
118
|
/**
|
|
117
119
|
* `platform_linked`: the workspace just linked. Overrides the envelope's own reading, which looks
|
|
118
120
|
* at the daemon's working directory and not at the project the HUD linked.
|
|
@@ -470,6 +470,8 @@ export const createTelemetry = (opts) => {
|
|
|
470
470
|
...(extra?.proxyExit !== undefined ? { proxyExit: extra.proxyExit } : {}),
|
|
471
471
|
...(extra?.daemonDeath !== undefined ? { daemonDeath: extra.daemonDeath } : {}),
|
|
472
472
|
...(extra?.wakeRefusal !== undefined ? { wakeRefusal: extra.wakeRefusal } : {}),
|
|
473
|
+
// A SCALAR: the event build and the wire schema, no `blocks` entry.
|
|
474
|
+
...(extra?.spawnMethod !== undefined ? { spawnMethod: extra.spawnMethod } : {}),
|
|
473
475
|
};
|
|
474
476
|
// The wire schema's caps are only real if something applies them. An event that fails is
|
|
475
477
|
// DROPPED, never sent and never repaired: the payload is exactly what the caps exist to stop.
|
package/docs/agent-cheatsheet.md
CHANGED
|
@@ -48,6 +48,7 @@ Two consequences worth knowing:
|
|
|
48
48
|
|
|
49
49
|
- **You can assert what a field contains.** Do not read the value with `reticle_look { action: "find" }` and compare it in prose: that produces no verdict and does not count as verification.
|
|
50
50
|
- **`{ role, text }` checks the text,** so it does not match every button with that role.
|
|
51
|
+
- **A `text` predicate reads rendered text, not a field's value.** Text typed into an `<input>` or a `<textarea>` is not on the page as text, so `{ kind: "text", contains }` does not see it. Assert it with `value` on an `element` predicate, as above.
|
|
51
52
|
|
|
52
53
|
Fields that nothing can check are refused rather than ignored: `by` without a `value`, and `label`, `placeholder`, `testid` or `component` when a higher-precedence field already selected the element. `alt` is the one that can still be checked beside another locator: on an image it is compared with the image's `alt` attribute (so `alt: ""` passes on a decorative image but not on one missing its alt), and on anything that is not an image it is refused, with a hint to use `name`. An element query is a first-match dispatch, not a conjunction.
|
|
53
54
|
|
|
@@ -164,6 +165,17 @@ Recent additions, each of which answers a question agents were previously asking
|
|
|
164
165
|
2. `reticle_run({ tool: "reticle_domain", args: {} })` learns the app BEFORE testing: the saved flows, what each asserts, and the **gaps** (declared signals/testids that no flow verifies, i.e. untested intent). Tells you what to test and where the real risk is without crawling the whole app. Falls back to `reticle_run({ tool: "reticle_capabilities", args: {} })` for the raw testable surface (`testids`, `signals`, `stores`, `flows`).
|
|
165
166
|
3. Run the loop: **look → act → observe → assert**, cross-checking the 4 layers on anything that matters.
|
|
166
167
|
|
|
168
|
+
## No MCP tools in this host
|
|
169
|
+
|
|
170
|
+
A cloud agent or a worker can have a shell and no MCP tools. Use the CLI. `verify --expect` asks a running daemon and never starts one, so start it first:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx @reticlehq/server serve --headless
|
|
174
|
+
npx @reticlehq/server verify http://localhost:5173/ --expect '{"kind":"text","contains":"Welcome"}'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
It exits `0` only on `verified: "yes"`. `--expect-file <path>` reads the predicate from a file, and `--timeout N` sets how long it waits (default `4000` ms). To move a connected tab to another page first, run `npx @reticlehq/server open <url> --navigate`. For an action that `--expect` cannot do, use the [HTTP MCP transport](/http-transport).
|
|
178
|
+
|
|
167
179
|
## Token note
|
|
168
180
|
|
|
169
181
|
- **Keep observation cheap.** Prefer `reticle_look { action: "find" }` / scoped or `interactive` `reticle_look { action: "page" }` / `reticle_assert` over dumping the full tree. A full verify loop is ~100 tokens; see [Token efficiency](/token-efficiency) (~73× leaner than full-tree snapshots).
|
package/docs/cli/gate.mdx
CHANGED
|
@@ -49,7 +49,7 @@ One `reticle_gate` line. Real capture from this repository, with the flow lists
|
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
`downgraded`, `deletedCoverage`, `unflowed`, `coverageRegressed`, `unexecuted`, `coverageStale` and `coverageAccepted` appear only when they are non-empty, and none was present in that run. The coverage categories need a coverage ledger, which a project gets from `reticle_verify { action: "crawl", exhaustive: true }` or `reticle_verify { action: "coverage", app: true }`; without one they never fire. The CLI prints this as a single line of JSON; it is pretty-printed here to be readable.
|
|
52
|
+
`downgraded`, `deletedCoverage`, `unflowed`, `coverageRegressed`, `unexecuted`, `coverageStale` and `coverageAccepted` appear only when they are non-empty, `flowsWithoutSources` only when it is `true`, and none was present in that run. The coverage categories need a coverage ledger, which a project gets from `reticle_verify { action: "crawl", exhaustive: true }` or `reticle_verify { action: "coverage", app: true }`; without one they never fire. The CLI prints this as a single line of JSON; it is pretty-printed here to be readable.
|
|
53
53
|
|
|
54
54
|
## Exit codes
|
|
55
55
|
|
package/docs/cli/verify.mdx
CHANGED
|
@@ -7,7 +7,7 @@ icon: circle-check
|
|
|
7
7
|
`reticle verify` is the CI command: no model in the loop, no MCP. On a free port it boots the engine in drive mode, waits for the in-page SDK to dial back, replays every saved flow, renders the verdict, and exits. When a Reticle daemon already owns the port, it asks that daemon to replay the flows instead.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx @reticlehq/server verify <url> [--headed] [--timeout N] [--storage-state <file>]
|
|
10
|
+
npx @reticlehq/server verify <url> [--headed] [--timeout N] [--storage-state <file>] [--expect <json> | --expect-file <path>]
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
It uses the same runner and the same verdict as the MCP and HTTP paths, so a platform agent that can only run a shell command gets a byte-identical artifact.
|
|
@@ -28,6 +28,13 @@ It uses the same runner and the same verdict as the MCP and HTTP paths, so a pla
|
|
|
28
28
|
| `--explore` / `--persona "<who does what>"` | boolean / string | off | No saved flows yet? The Harness drives the app and records them. `--persona` implies `--explore`. Needs a linked project (`reticle connect`, or `RETICLE_API_KEY` in CI). It runs at any instrumentation score |
|
|
29
29
|
| `--confirm-dangerous` | boolean | off | Let this run press the controls the replay guard holds as destructive, such as a "Place order" or "Delete" button in a saved flow. Without it such a step is refused, so a checkout flow cannot pass. It covers this run only and is never saved into a flow. |
|
|
30
30
|
|
|
31
|
+
On Windows PowerShell, write the predicate to a file and pass `--expect-file` so the JSON never goes through shell quoting:
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
'{"kind":"text","contains":"Saved"}' | Set-Content expect.json
|
|
35
|
+
npx @reticlehq/server verify http://localhost:3000 --expect-file expect.json
|
|
36
|
+
```
|
|
37
|
+
|
|
31
38
|
## What it prints
|
|
32
39
|
|
|
33
40
|
The rendered run report on stdout. On failure it names the cause on stderr instead, and there are two refusals worth knowing:
|
package/docs/first-drive.md
CHANGED
|
@@ -72,6 +72,13 @@ node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
|
|
|
72
72
|
--expect '{"kind":"text","contains":"definitely not on this page"}'
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
On Windows PowerShell, `npx.cmd` re-parses `--expect '{...}'` and strips the inner quotes. Prefer `--expect-file`:
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
'{"kind":"text","contains":"Saved"}' | Set-Content expect.json
|
|
79
|
+
npx @reticlehq/server verify http://localhost:3000 --expect-file expect.json
|
|
80
|
+
```
|
|
81
|
+
|
|
75
82
|
## 3. Drive it properly, with the tools
|
|
76
83
|
|
|
77
84
|
If your editor has the Reticle MCP server, the same app is now drivable. The four calls worth knowing, in the order you actually use them:
|
|
@@ -36,6 +36,8 @@ reticle_run { tool: "reticle_lease", args: { action: "release", sessionId } }
|
|
|
36
36
|
|
|
37
37
|
`reticle_run { tool: "reticle_lease", args: { action: "acquire" } }` opens a fresh isolated headless context against your **already-running** app, stamps the lease identity into the URL so the app's own SDK registers under a sessionId you can target, and waits until that tab has connected (`ready: true`) before returning, so the sessionId is usable immediately. Release when the flow finishes.
|
|
38
38
|
|
|
39
|
+
**Lease the exact host that the dev server binds.** `http://localhost:3000` and `http://127.0.0.1:3000` are two origins, and they are not the same address. A dev server that binds only `::1` (Vite can do this for `localhost`) refuses a lease on `127.0.0.1`, and a server that binds only `127.0.0.1` does not answer on `::1`. The two origins also have their own cookies and storage, and a lease is reused for each origin. Copy the url that the dev server prints, host and port included.
|
|
40
|
+
|
|
39
41
|
## Permissions
|
|
40
42
|
|
|
41
43
|
A lease starts with every permission undecided. Pass `permissions` to grant some on the app's origin before the first navigation, so the first render already sees them:
|
package/docs/predicates.mdx
CHANGED
|
@@ -73,6 +73,12 @@ Add `"absent": true` to assert something is **gone**. The predicate for a remova
|
|
|
73
73
|
|
|
74
74
|
`contains` is the field the published schema declares. `value`, `text` and `query` are accepted as aliases and rewritten to it. The match is case-sensitive. `ignoreCase: true` relaxes that. Curly quotes (`‘ ’ “ ”`), the en dash and the em dash match their plain forms. So `It's saved - done` matches a page that shows `It’s saved – done`. Text that Reticle reports back keeps the page's own characters. `absent: true` asserts the text is not present, and `visible: true` requires it to be on screen rather than merely in the DOM.
|
|
75
75
|
|
|
76
|
+
`text` reads the text that the page renders. It does not read what is typed into an `<input>` or a `<textarea>`, because that is the field's value, not text on the page. `value` on a `text` predicate is only an alias for `contains`, so it also reads rendered text. To assert what a field holds, use an `element` predicate with `value`:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{ "kind": "element", "query": { "role": "textbox", "name": "Notes", "value": "Ship on Friday" } }
|
|
80
|
+
```
|
|
81
|
+
|
|
76
82
|
Use `scope` to restrict the search to a CSS selector or element ref. If Reticle reports that the text is split across the scope's children, retry its generated predicate with `"self": true`; that checks the scope root's combined subtree text while still enforcing `contains`.
|
|
77
83
|
|
|
78
84
|
`satisfies` asserts a PROPERTY of the rendered text instead of its exact bytes: the same shape and the same properties [`state` accepts](#satisfies-asserting-a-property-instead-of-exact-bytes), applied where the user actually reads the value:
|
package/docs/skill.md
CHANGED
|
@@ -3,7 +3,7 @@ name: reticle-docs
|
|
|
3
3
|
description: Install, instrument and verify this running web app from the inside (DOM, network, routing, console and framework state) instead of screenshots or guessing. Drives one real flow end to end and returns a verdict with the file:line to fix. Use when the user asks to set up or install Reticle, when a user-facing change needs proving before you call it done, when a test passes but the UI is broken, or when the user types /reticle.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: 3.8.
|
|
6
|
+
version: 3.8.1
|
|
7
7
|
homepage: https://docs.reticle.sh
|
|
8
8
|
repository: https://github.com/reticlehq/reticle
|
|
9
9
|
internal: true
|
|
@@ -149,7 +149,7 @@ Stop at the first row that fits. Replaying a saved journey costs a few hundred t
|
|
|
149
149
|
| "Did my edit break anything?" | `reticle_verify { action: "change", files: ["src/App.tsx"] }` |
|
|
150
150
|
| "Does every saved journey still work?" | `reticle_verify { action: "flows" }` |
|
|
151
151
|
| "Does this new behaviour work?" | `reticle_act_and_wait` on the step that ENDS the journey |
|
|
152
|
-
| No MCP
|
|
152
|
+
| No MCP tools (a cloud agent, a worker) | `npx @reticlehq/server serve`, then `npx @reticlehq/server verify <url> --expect '<predicate JSON>'`. `open <url> --navigate` moves a connected tab to `<url>` |
|
|
153
153
|
|
|
154
154
|
`change` answers `unknown` when no saved flow covers your files: nothing ran, so drive it yourself and never read it as a pass. A locator that was renamed rather than broken: `reticle_verify { action: "heal", flowName }` proposes the rebind, and `apply: true` writes it after it proves the saved consequence again.
|
|
155
155
|
|
|
@@ -172,7 +172,7 @@ A journey in one call: `reticle_act { steps: [{ target, action, args?, expect? }
|
|
|
172
172
|
| ref no longer resolves | the DOM re-rendered: target by `testid` or `role`+`name` |
|
|
173
173
|
| predicate did not parse | it names the field: fix the shape against the table above |
|
|
174
174
|
| destructive action blocked | `args.confirmDangerous: true`, only if the user wants that press |
|
|
175
|
-
| version skew | `npx @reticlehq/server
|
|
175
|
+
| version skew | update the CLI and the app SDK (`npx @reticlehq/server update` in the app's folder), then restart the MCP host |
|
|
176
176
|
| port held by a non-Reticle process | stop that process, or run Reticle on another `--port` |
|
|
177
177
|
| tab served from another worktree | start the dev server in your own worktree; keep the port |
|
|
178
178
|
|
package/docs/troubleshooting.mdx
CHANGED
|
@@ -240,6 +240,16 @@ A dev server on `https://` with a self-signed or mkcert certificate makes `retic
|
|
|
240
240
|
|
|
241
241
|
It is off by default. When it is on, certificate checks are skipped for every site opened in that lease's browser context, including resources loaded from other sites. Other leases keep their checks. A context cannot change the setting once it is made, so acquiring again with the other value replaces the lease instead of reusing it.
|
|
242
242
|
|
|
243
|
+
## The page never connects under a strict CSP
|
|
244
|
+
|
|
245
|
+
A build that sends a strict Content-Security-Policy blocks the bridge socket. A production build that you run locally (`next start`, `vite preview`) often sends one. The app runs, and the SDK never connects, with no error in the app. Add the bridge to `connect-src`, with your bridge port if it is not `4400`:
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
connect-src 'self' ws://localhost:4400 ws://127.0.0.1:4400
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Add it only in the builds that Reticle drives, never in the policy that you deploy. If the policy also blocks inline scripts, `script-src` stops a pasted connect snippet before it runs: serve that snippet as an external module. `reticle doctor` reads the policy that the page serves and names the line to add, and a lease result says the same. For [production mode](/production-mode), add the `wss://` bridge from the ticket instead. Desktop apps have their own note in [Desktop](/desktop).
|
|
252
|
+
|
|
243
253
|
## `ERESOLVE` installing next to a prerelease
|
|
244
254
|
|
|
245
255
|
`@reticlehq/next` declares `peer next >=13` and `@reticlehq/react` declares `peer react >=18`. On a Next canary or React RC, npm refuses. See [the prerelease notes](/getting-started#installing-alongside-a-next-js-or-react-prerelease).
|
package/docs/usage.md
CHANGED
|
@@ -387,7 +387,7 @@ A **predicate** declares what should be true. `reticle_assert` / `reticle_assert
|
|
|
387
387
|
{ "kind": "state", "store": "app", "path": "deployments.0.status", "equals": "live" }
|
|
388
388
|
```
|
|
389
389
|
|
|
390
|
-
`visible` excludes elements whose boxes are fully clipped by an ancestor's `overflow:hidden` or `overflow:clip`; a partly clipped element stays visible. Expanding a clamped card can therefore prove that its previously hidden paragraph became visible. `overflow:auto` and `overflow:scroll` keep their existing visibility semantics, including content beyond the scrollport. Ordinary document content outside the window remains visible; `inViewport` checks the window intersection after any hidden/clip ancestor clipping, without introducing scrollport clipping.
|
|
390
|
+
`visible` excludes elements whose boxes are fully clipped by an ancestor's `overflow:hidden` or `overflow:clip`; a partly clipped element stays visible. It also excludes screen-reader-only text: a positioned box with a `clip: rect()` of no area, a `clip-path: inset()` whose edges meet, or a 1×1 box that clips its own overflow. That text stays `present` and still names its control. Expanding a clamped card can therefore prove that its previously hidden paragraph became visible. `overflow:auto` and `overflow:scroll` keep their existing visibility semantics, including content beyond the scrollport. Ordinary document content outside the window remains visible; `inViewport` checks the window intersection after any hidden/clip ancestor clipping, without introducing scrollport clipping.
|
|
391
391
|
|
|
392
392
|
A `state` assertion is graded as a **consequence** (a wrong element or stale render cannot fake it), and is usable the same three ways anywhere predicates flow: ad-hoc (`reticle_assert` / `reticle_act_and_wait` `until`), as a flow step invariant (`reticle_annotate { kind: "assert-state", statePath, store?, equals? }`), and as a flow's golden end-condition (`reticle_annotate { kind: "success-state", statePath, … }`). On a miss it names the real store value and the keys that were available: legible, not a blind fail.
|
|
393
393
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reticlehq/server",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"mcpName": "io.github.reticlehq/reticle",
|
|
5
5
|
"description": "Reticle CLI, bridge and MCP server: a coding agent drives your running web app from the inside and gets a verdict with evidence, for change verification, access control, accessibility, performance and SEO checks.",
|
|
6
6
|
"type": "module",
|
|
@@ -22,13 +22,12 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
24
24
|
"ws": "^8.21.3",
|
|
25
|
-
"zod": "^
|
|
26
|
-
"
|
|
27
|
-
"@reticlehq/
|
|
28
|
-
"@reticlehq/init": "3.8.
|
|
29
|
-
"
|
|
30
|
-
"@reticlehq/
|
|
31
|
-
"open-verification": "1.0.0"
|
|
25
|
+
"zod": "^4.6.6",
|
|
26
|
+
"@reticlehq/engine": "3.8.1",
|
|
27
|
+
"@reticlehq/core": "3.8.1",
|
|
28
|
+
"@reticlehq/init": "3.8.1",
|
|
29
|
+
"open-verification": "1.0.0",
|
|
30
|
+
"@reticlehq/browser": "3.8.1"
|
|
32
31
|
},
|
|
33
32
|
"devDependencies": {
|
|
34
33
|
"@types/pngjs": "^6.0.5",
|
|
@@ -65,7 +64,7 @@
|
|
|
65
64
|
"cli"
|
|
66
65
|
],
|
|
67
66
|
"reticle": {
|
|
68
|
-
"changelog": "
|
|
67
|
+
"changelog": "On Windows, the daemon now keeps running when the agent process that started it ends. A collapsed Next.js \"1 Issue\" badge no longer turns every check into unknown. Reticle moves to zod 4 for a high-severity memory advisory. reticle_lease can omit url and lease the project's own dev server. reticle doctor checks that the daemon answers, and streamed SSE response bodies can be captured. Replay waits for a start page that is still loading, a re-acquired lease moves to the page asked for, and a guarded app driven by fast clicks is no longer accused of applying a stale response. Update the SDK in your app as well as the CLI.",
|
|
69
68
|
"breakingChanges": []
|
|
70
69
|
},
|
|
71
70
|
"publishConfig": {
|