@reticlehq/server 2.10.0 → 2.12.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/SKILL.md +73 -11
- package/dist/bridge/bridge.d.ts +0 -13
- package/dist/bridge/bridge.js +1 -1
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/capsule/capsule-store.d.ts +2 -1
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +8 -0
- package/dist/cli/cli-flow-commands.d.ts +3 -1
- package/dist/cli/cli-flow-commands.js +34 -4
- package/dist/cli/cli-kill.d.ts +2 -1
- package/dist/cli/cli-parse.d.ts +3 -1
- package/dist/cli/cli-parse.js +29 -7
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +1 -1
- package/dist/cli/cli-verify.js +2 -2
- package/dist/cli/cloud-cli.js +12 -6
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +3 -1
- package/dist/cli/doctor-rows.js +3 -1
- package/dist/cli/gate-exit.d.ts +21 -0
- package/dist/cli/gate-exit.js +21 -0
- package/dist/cli/gate-hook-message.d.ts +33 -0
- package/dist/cli/gate-hook-message.js +46 -0
- package/dist/cli/mcp-command.d.ts +25 -0
- package/dist/cli/mcp-command.js +143 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli.js +37 -110
- package/dist/cloud/cloud-config.d.ts +2 -1
- package/dist/cloud/cloud-sync.d.ts +11 -0
- package/dist/cloud/cloud-sync.js +32 -0
- package/dist/crawl/crawl.d.ts +9 -13
- package/dist/crawl/crawl.js +20 -3
- package/dist/daemon/daemon-resolve.d.ts +79 -0
- package/dist/daemon/daemon-resolve.js +118 -0
- package/dist/daemon/free-port.d.ts +29 -0
- package/dist/daemon/free-port.js +55 -0
- package/dist/daemon/heartbeat.d.ts +0 -4
- package/dist/daemon/heartbeat.js +2 -2
- package/dist/dev/stale-issue-guard.d.ts +41 -0
- package/dist/dev/stale-issue-guard.js +75 -0
- package/dist/domain/domain-tools.js +3 -2
- package/dist/events/contradictions.d.ts +41 -0
- package/dist/events/contradictions.js +125 -12
- package/dist/events/declared.d.ts +14 -0
- package/dist/events/declared.js +33 -0
- package/dist/events/echo-mismatch.js +14 -0
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +17 -0
- package/dist/events/predicate-asks.d.ts +30 -0
- package/dist/events/predicate-asks.js +48 -0
- package/dist/events/predicate-eval.js +254 -25
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +51 -5
- package/dist/events/predicate-schema.d.ts +62 -0
- package/dist/events/predicate-schema.js +162 -4
- package/dist/events/predicate.d.ts +15 -0
- package/dist/events/predicate.js +41 -2
- package/dist/flows/coverage.d.ts +0 -8
- package/dist/flows/coverage.js +1 -1
- package/dist/flows/decision.d.ts +5 -1
- package/dist/flows/decision.js +33 -7
- package/dist/flows/flake.d.ts +0 -2
- package/dist/flows/flake.js +1 -1
- package/dist/flows/flow-intent.d.ts +52 -0
- package/dist/flows/flow-intent.js +123 -0
- package/dist/flows/flow-replay-run.js +27 -4
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +65 -8
- package/dist/flows/flows.d.ts +6 -1
- package/dist/flows/flows.js +34 -22
- package/dist/flows/predicate-to-expect.js +14 -3
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +9 -2
- package/dist/honesty/blind-spots.d.ts +8 -16
- package/dist/honesty/blind-spots.js +18 -5
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/gap-ledger.d.ts +52 -0
- package/dist/honesty/gap-ledger.js +63 -0
- package/dist/honesty/instrumentation-gaps.d.ts +79 -0
- package/dist/honesty/instrumentation-gaps.js +77 -0
- package/dist/honesty/observability.d.ts +37 -0
- package/dist/honesty/observability.js +40 -0
- package/dist/honesty/tool-hit-rate.d.ts +86 -0
- package/dist/honesty/tool-hit-rate.js +90 -0
- package/dist/honesty/undeclared-change.d.ts +51 -0
- package/dist/honesty/undeclared-change.js +56 -0
- package/dist/honesty/verified.d.ts +15 -0
- package/dist/honesty/verified.js +68 -4
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +1 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-store.d.ts +0 -5
- package/dist/impact/impact-store.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +41 -0
- package/dist/init/agent-rules.d.ts +17 -0
- package/dist/init/agent-rules.js +31 -1
- package/dist/init/capabilities.d.ts +0 -2
- package/dist/init/capabilities.js +1 -1
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +0 -25
- package/dist/init/confirm.js +3 -3
- package/dist/init/cra.d.ts +14 -1
- package/dist/init/cra.js +43 -12
- package/dist/init/dev-script.d.ts +48 -0
- package/dist/init/dev-script.js +63 -0
- package/dist/init/mcp.d.ts +16 -5
- package/dist/init/mcp.js +26 -10
- package/dist/init/node-io.js +3 -0
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +83 -9
- package/dist/init/plan.d.ts +10 -30
- package/dist/init/plan.js +30 -15
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +2 -10
- package/dist/init/run.js +26 -68
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +33 -10
- package/dist/init/snippets.js +53 -10
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +3 -2
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/intent/inline-intent.d.ts +46 -0
- package/dist/intent/inline-intent.js +103 -0
- package/dist/intent/intent-store.d.ts +37 -0
- package/dist/intent/intent-store.js +112 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +80 -0
- package/dist/intent/open-intents.d.ts +21 -0
- package/dist/intent/open-intents.js +25 -0
- package/dist/journal/envelope.d.ts +2 -2
- package/dist/journal/journal-recorder.d.ts +23 -0
- package/dist/journal/journal-recorder.js +35 -0
- package/dist/license/license.d.ts +8 -2
- package/dist/license/license.js +10 -15
- package/dist/mcp/attach-memory.d.ts +67 -0
- package/dist/mcp/attach-memory.js +142 -0
- package/dist/mcp/mcp-proxy.d.ts +0 -7
- package/dist/mcp/mcp-proxy.js +14 -2
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +45 -23
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +8 -0
- package/dist/mcp/tool-catalog-cache.js +10 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +12 -1
- package/dist/pool/browser-pool.js +31 -1
- package/dist/pool/playwright-launcher.js +3 -1
- package/dist/project/artifact-root.d.ts +83 -0
- package/dist/project/artifact-root.js +112 -0
- package/dist/project/fs-port.d.ts +3 -0
- package/dist/project/fs-port.js +3 -2
- package/dist/project/memory-fs.d.ts +9 -0
- package/dist/project/memory-fs.js +93 -0
- package/dist/project/project-store.d.ts +17 -0
- package/dist/project/project-store.js +33 -0
- package/dist/project/project-tools.js +3 -32
- package/dist/project/remember-project.d.ts +24 -0
- package/dist/project/remember-project.js +40 -0
- package/dist/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- package/dist/project/session-root.d.ts +15 -0
- package/dist/project/session-root.js +31 -0
- package/dist/runs/context-tools.d.ts +2 -0
- package/dist/runs/context-tools.js +89 -0
- package/dist/runs/run-context.d.ts +40 -0
- package/dist/runs/run-context.js +158 -0
- package/dist/runs/run-diff.d.ts +2 -1
- package/dist/runs/run-tools.js +2 -1
- package/dist/runs/verification-sync.js +2 -2
- package/dist/session/dev-server-probe.d.ts +0 -8
- package/dist/session/dev-server-probe.js +1 -1
- package/dist/session/last-act.d.ts +7 -1
- package/dist/session/last-act.js +2 -2
- package/dist/session/lease-availability.d.ts +40 -0
- package/dist/session/lease-availability.js +43 -0
- package/dist/session/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +20 -0
- package/dist/session/no-session-diagnosis.js +41 -12
- package/dist/session/no-session-next-action.d.ts +5 -0
- package/dist/session/no-session-next-action.js +35 -3
- package/dist/session/no-session-watch.d.ts +8 -0
- package/dist/session/no-session-watch.js +83 -65
- package/dist/session/output-budget.js +4 -1
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-manager.js +50 -1
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +78 -0
- package/dist/session/session.d.ts +40 -1
- package/dist/session/session.js +84 -2
- package/dist/session/stall-clock.d.ts +18 -0
- package/dist/session/stall-clock.js +48 -0
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/daemon-telemetry.js +12 -23
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/project-profile.d.ts +0 -8
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +1 -1
- package/dist/telemetry/telemetry.d.ts +12 -3
- package/dist/telemetry/telemetry.js +25 -5
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-tools.js +159 -24
- package/dist/tools/assert-source.d.ts +22 -0
- package/dist/tools/assert-source.js +53 -0
- package/dist/tools/assert-verdict.d.ts +21 -2
- package/dist/tools/assert-verdict.js +59 -4
- package/dist/tools/browser-tools.js +0 -20
- package/dist/tools/complete-snapshot.d.ts +48 -0
- package/dist/tools/complete-snapshot.js +75 -0
- package/dist/tools/contract-tools.js +5 -3
- package/dist/tools/coverage-tools.d.ts +10 -1
- package/dist/tools/coverage-tools.js +124 -37
- package/dist/tools/dynamic-tools.js +29 -8
- package/dist/tools/error-recovery.d.ts +24 -14
- package/dist/tools/error-recovery.js +70 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +62 -2
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +5 -1
- package/dist/tools/lease-tools.js +22 -8
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/observe-tools.js +70 -27
- package/dist/tools/read-tools.js +5 -3
- package/dist/tools/real-input-attempt.d.ts +22 -0
- package/dist/tools/real-input-attempt.js +153 -1
- package/dist/tools/reconcile-tools.js +18 -4
- package/dist/tools/resolve-target.d.ts +0 -7
- package/dist/tools/sdk-marker-probe.d.ts +29 -0
- package/dist/tools/sdk-marker-probe.js +57 -0
- package/dist/tools/snapshot-delta.d.ts +1 -1
- package/dist/tools/tool-kit.d.ts +28 -1
- package/dist/tools/tool-kit.js +16 -0
- package/dist/tools/tool-names.d.ts +3 -0
- package/dist/tools/tool-names.js +3 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools.js +22 -4
- package/dist/tools/unadvertised-help.js +4 -4
- package/dist/tools/verify-next-baton.d.ts +53 -0
- package/dist/tools/verify-next-baton.js +52 -0
- package/dist/update/update-nudge.d.ts +7 -10
- package/dist/update/update-nudge.js +12 -3
- package/dist/update/updater.d.ts +1 -1
- package/docs/README.md +3 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/verify.mdx +4 -5
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +3 -3
- package/docs/enterprise.md +3 -1
- package/docs/gates.md +1 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/license-activation.mdx +145 -0
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages.mdx +8 -6
- package/docs/platform-integration.md +17 -0
- package/docs/predicates.mdx +8 -0
- package/docs/reticle-manual.md +106 -0
- package/docs/telemetry-contract.md +1 -12
- package/docs/telemetry-events.mdx +1 -34
- package/docs/telemetry.md +0 -1
- package/docs/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +3 -2
- package/package.json +2 -2
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
- package/dist/telemetry/instrumentation-stall.d.ts +0 -57
- package/dist/telemetry/instrumentation-stall.js +0 -103
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Activating a license key'
|
|
3
|
+
description: 'Set your key, confirm it worked, and fix it when it did not. Everything an activation needs, with no account and no call.'
|
|
4
|
+
icon: key
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
If your organisation has a Reticle license key, this page is everything you need to put it in place. There is no account to create, no login, and no call with us required.
|
|
8
|
+
|
|
9
|
+
Do not have a key? Write to [hey@reticle.sh](mailto:hey@reticle.sh).
|
|
10
|
+
|
|
11
|
+
## One key per organisation
|
|
12
|
+
|
|
13
|
+
A key covers your whole organisation. Every developer and every machine uses the same one, so share it the way you would share any other build-time credential: your secrets manager, your onboarding doc, your CI configuration.
|
|
14
|
+
|
|
15
|
+
## Set it
|
|
16
|
+
|
|
17
|
+
Reticle reads one environment variable, `RETICLE_LICENSE_KEY`. Set it everywhere Reticle runs, which is usually more than one place.
|
|
18
|
+
|
|
19
|
+
<CodeGroup>
|
|
20
|
+
|
|
21
|
+
```bash macOS / Linux
|
|
22
|
+
# In ~/.zshrc, ~/.bashrc, or your team's equivalent.
|
|
23
|
+
export RETICLE_LICENSE_KEY="your-key-here"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```powershell Windows
|
|
27
|
+
[Environment]::SetEnvironmentVariable('RETICLE_LICENSE_KEY', 'your-key-here', 'User')
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```yaml GitHub Actions
|
|
31
|
+
env:
|
|
32
|
+
RETICLE_LICENSE_KEY: ${{ secrets.RETICLE_LICENSE_KEY }}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```bash Docker
|
|
36
|
+
docker run -e RETICLE_LICENSE_KEY="your-key-here" your-image
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
</CodeGroup>
|
|
40
|
+
|
|
41
|
+
An export only affects shells started after it, so open a new terminal or `source` your profile.
|
|
42
|
+
|
|
43
|
+
### Editors and agents that launch Reticle themselves
|
|
44
|
+
|
|
45
|
+
<Warning>
|
|
46
|
+
This is the step teams most often miss. Claude Desktop, Cursor and similar tools launch from the
|
|
47
|
+
desktop rather than from a terminal, so they never read your shell profile. An `export` that
|
|
48
|
+
plainly works in your terminal is invisible to them, and Reticle reports itself unlicensed.
|
|
49
|
+
</Warning>
|
|
50
|
+
|
|
51
|
+
Put the key in the MCP server's own `env` block instead, then restart the editor. Configuration is read at startup.
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"reticle": {
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["@reticlehq/server", "mcp"],
|
|
59
|
+
"env": {
|
|
60
|
+
"RETICLE_LICENSE_KEY": "your-key-here"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Confirm it worked
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx @reticlehq/server license
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
You do not need `NODE_ENV` set, a project, a dev server, or a running app. This reads the key and nothing else.
|
|
74
|
+
|
|
75
|
+
| `status` | What it means | What to do |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `active` | Valid and in date. | Nothing. You are done. |
|
|
78
|
+
| `missing` | Reticle found no key in its environment. | The variable is not visible to this process. See below. |
|
|
79
|
+
| `expired` | The key was valid and its term has ended. | Contact us to renew. |
|
|
80
|
+
| `invalid` | The key was not accepted. | Usually a copy or paste problem. See below. |
|
|
81
|
+
| `eval` | This build has no licensing configured. | You are on a build older than licensing. Update. |
|
|
82
|
+
|
|
83
|
+
## Over time
|
|
84
|
+
|
|
85
|
+
Your key carries an expiry, shown in the `license` output. A renewal is a new key for the same organisation: replace the value everywhere you set it, and nothing else changes.
|
|
86
|
+
|
|
87
|
+
Nothing breaks without warning. An expired key reports `expired` rather than failing silently, and you can check at any time with the command above.
|
|
88
|
+
|
|
89
|
+
Activation is **offline**. Reticle verifies your key cryptographically on your own machine, so there is no license server to reach. It works behind a firewall, on an air-gapped machine, and during any outage on our side.
|
|
90
|
+
|
|
91
|
+
## When it does not work
|
|
92
|
+
|
|
93
|
+
<AccordionGroup>
|
|
94
|
+
|
|
95
|
+
<Accordion title="It says missing, but the variable is definitely set">
|
|
96
|
+
The commonest cause by a distance is that Reticle is being launched by something that does not
|
|
97
|
+
inherit your shell environment: an editor, a desktop agent, a systemd unit, a container. Run
|
|
98
|
+
`echo $RETICLE_LICENSE_KEY` in the same context Reticle runs in, not just in your own terminal. If
|
|
99
|
+
an editor launches it, use the MCP `env` block above.
|
|
100
|
+
|
|
101
|
+
Second commonest: the variable was set in a shell that was already open. Environment variables are read when a process starts, so open a new terminal and restart anything long-running.
|
|
102
|
+
|
|
103
|
+
</Accordion>
|
|
104
|
+
|
|
105
|
+
<Accordion title="It says invalid">
|
|
106
|
+
The key changed between us sending it and Reticle reading it. Almost always one of:
|
|
107
|
+
|
|
108
|
+
- truncated, most often by a line wrap when pasted into a config file or a chat client
|
|
109
|
+
- a character dropped or added while copying by hand
|
|
110
|
+
- quoted in a way that swallowed a character
|
|
111
|
+
|
|
112
|
+
Surrounding whitespace and a trailing newline are tolerated, so neither is the cause. Copy the key again in one piece. If it still reports `invalid`, write to us and we will re-issue it.
|
|
113
|
+
|
|
114
|
+
</Accordion>
|
|
115
|
+
|
|
116
|
+
<Accordion title="command not found: reticle">
|
|
117
|
+
Use the `npx @reticlehq/server ...` form shown on this page. It runs without a global install, and
|
|
118
|
+
it is the form we support.
|
|
119
|
+
</Accordion>
|
|
120
|
+
|
|
121
|
+
</AccordionGroup>
|
|
122
|
+
|
|
123
|
+
## What a licensed deployment reports
|
|
124
|
+
|
|
125
|
+
Stated plainly, because a security review will ask.
|
|
126
|
+
|
|
127
|
+
Activation itself makes **no network request**. Your key is verified on your machine with public key cryptography.
|
|
128
|
+
|
|
129
|
+
Separately, Reticle sends anonymous usage metrics, as it does for every user. On a licensed deployment these carry your **license id**, an opaque identifier that means nothing without our own records, so we can tell your organisation's usage apart from everyone else's and know when to talk to you about renewal.
|
|
130
|
+
|
|
131
|
+
Never sent, licensed or not: your source code, your application's data, your users' data, URLs, selectors, page content, file contents, or your organisation's name.
|
|
132
|
+
|
|
133
|
+
The kill switch has no exception for licensed installations:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
export RETICLE_TELEMETRY=0
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`DO_NOT_TRACK=1` is honoured identically. If your policy forbids outbound telemetry, set it. Your activation is unaffected, because activation never needed the network. To keep your own usage data without sending it, `RETICLE_TELEMETRY_FILE=/path/to/file.jsonl` records the same stream locally and sends nothing.
|
|
140
|
+
|
|
141
|
+
## Getting help
|
|
142
|
+
|
|
143
|
+
[hey@reticle.sh](mailto:hey@reticle.sh) for activation problems, renewals and re-issues.
|
|
144
|
+
|
|
145
|
+
Include the output of `npx @reticlehq/server license`. It carries your license id and no secrets, and it usually tells us the answer immediately.
|
|
@@ -6,13 +6,13 @@ icon: window
|
|
|
6
6
|
|
|
7
7
|
`@reticlehq/browser` is the Reticle SDK that runs inside your page. It installs the observers, builds semantic snapshots, executes actions, and talks to the bridge. Install it directly only if you are **not** on React or want to call `connect()` yourself, because [`@reticlehq/react`](/packages/react) already re-exports all of it.
|
|
8
8
|
|
|
9
|
-
**Apache 2.0. Depends on `@reticlehq/core
|
|
9
|
+
**Apache 2.0. Depends on `@reticlehq/core`.**
|
|
10
10
|
|
|
11
11
|
## Why it exists
|
|
12
12
|
|
|
13
13
|
This is what makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel. It never imports a Node API; that boundary is enforced by the dependency graph.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
[`reticle_query`](/tools-query) speaks role, label and testid instead of CSS selectors through its own local accessibility engine: one engine computes both what elements are called and what they match by, so a reported identity always round-trips.
|
|
16
16
|
|
|
17
17
|
## When you need it directly
|
|
18
18
|
|
package/docs/packages/server.mdx
CHANGED
|
@@ -64,7 +64,7 @@ await server.close();
|
|
|
64
64
|
| `reticleRoot` | `string` | `cwd()/.reticle` |
|
|
65
65
|
| `pairingTokenDir` | `string` | `~/.reticle` |
|
|
66
66
|
| `now` | `() => number` | `Date.now` |
|
|
67
|
-
| `toolProfile` | `string` |
|
|
67
|
+
| `toolProfile` | `string` | retired names (`core`/`full`/…) still map. Live switch is `RETICLE_ADVERTISE_ALL_TOOLS=1`. Default is the lean surface |
|
|
68
68
|
| `httpVerify` | `boolean` | `false` |
|
|
69
69
|
| `httpVerifyPort` | `number` | `RETICLE_VERIFY_DEFAULT_PORT` (`7331`) |
|
|
70
70
|
| `httpVerifyToken` | `string` | `RETICLE_VERIFY_TOKEN` |
|
package/docs/packages.mdx
CHANGED
|
@@ -8,9 +8,11 @@ icon: cubes
|
|
|
8
8
|
Reticle is eleven packages, and most people install two. This page exists so you can tell which two, and so that the ones you skip stop looking mysterious.
|
|
9
9
|
|
|
10
10
|
<Note>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Everything ships together at one version: whatever `npm view @reticlehq/core version` says, every
|
|
12
|
+
other `@reticlehq/*` package says too, and so does the `reticle-tauri` crate on crates.io.
|
|
13
|
+
|
|
14
|
+
It was versioned independently until 2.12.0, and that turned out to mean _stuck_: the crate sat at `0.1.0` from the day it was written, the publish job skipped it every release because that version already existed, and no Rust-side fix reached anybody. Lockstep costs one line in the bump script and cannot fail that way.
|
|
15
|
+
|
|
14
16
|
</Note>
|
|
15
17
|
|
|
16
18
|
## Which ones do I need?
|
|
@@ -70,7 +72,7 @@ Those rules are why the browser SDK cannot accidentally pull in a filesystem cal
|
|
|
70
72
|
|
|
71
73
|
**Why it exists:** this is the part that makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel.
|
|
72
74
|
|
|
73
|
-
**Depends on:** `@reticlehq/core
|
|
75
|
+
**Depends on:** `@reticlehq/core`. [`reticle_query`](/tools-query) speaks role, label and testid rather than CSS selectors through its own local accessibility engine, so what a snapshot reports is exactly what a query matches.
|
|
74
76
|
|
|
75
77
|
**Install it directly when** you are not on React, or you want `connect()` under your own control:
|
|
76
78
|
|
|
@@ -199,13 +201,13 @@ A lint rule makes the signal layer self-enforcing, which is the only way a conve
|
|
|
199
201
|
|
|
200
202
|
## reticle-tauri
|
|
201
203
|
|
|
202
|
-
**The Rust crate**, on crates.io. Versioned
|
|
204
|
+
**The Rust crate**, on crates.io. Versioned in lockstep with the npm packages, so `2.11` here and `2.11` there mean the same release.
|
|
203
205
|
|
|
204
206
|
**Why it exists:** Tauri IPC observation needs nothing on the Rust side; an `invoke('load_todos')` already reaches Reticle as `ipc://load_todos`. You only need this crate for screenshots and headless mode.
|
|
205
207
|
|
|
206
208
|
```toml
|
|
207
209
|
[dependencies]
|
|
208
|
-
reticle-tauri = "
|
|
210
|
+
reticle-tauri = "2.12"
|
|
209
211
|
```
|
|
210
212
|
|
|
211
213
|
<Warning>
|
|
@@ -93,6 +93,23 @@ Live, clickable demo of each: `apps/vibe-builder-demo/` (set `BUG_MODE=…`). Pr
|
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
96
|
+
## The minimal attach contract (for a harness or MCP host)
|
|
97
|
+
|
|
98
|
+
Reticle is a stdio MCP server. A host that wires it correctly does four things, and each one is a step Reticle can see:
|
|
99
|
+
|
|
100
|
+
1. **Spawn it.** `npx @reticlehq/server mcp`, over stdio, one process per host. That process is the proxy: it starts the local daemon on demand, reconnects on its own when the stream drops, and answers from cache while a daemon comes back.
|
|
101
|
+
2. **Complete `initialize`.** Reticle answers the handshake itself when the daemon is slow, so a host that waits for a reply gets one either way.
|
|
102
|
+
3. **Send `tools/list`, and send it again on `notifications/tools/list_changed`.** This is the step hosts skip. Registering a server is not the same as enumerating it: a host can register Reticle and never ask for the tool list until the client is restarted, and the model then has no Reticle tools at all while the install looks healthy from every other angle. Reticle declares `tools.listChanged` so a host that honours the notification recovers without a restart.
|
|
103
|
+
4. **Call the tools.** `tools/call`, with the tool name and its arguments.
|
|
104
|
+
|
|
105
|
+
`reticle status` reports how far a host has got. `mcpClient: "never-attached"` means no client has started the server on this port. `mcpClient: "never-enumerated"` means one started it and never asked for the tool list, which is the state a client restart fixes. `mcpClient: "enumerated"` means the client side is done, and anything still missing is the app rather than the host. The record is per port rather than per client, and it begins the first time a client starts a Reticle version that keeps it, so `status` says both of those out loud instead of naming a client it cannot see.
|
|
106
|
+
|
|
107
|
+
### Hosts that cap the number of tools
|
|
108
|
+
|
|
109
|
+
Some editors cap the total tool count across every MCP server, which is why Reticle advertises a small surface instead of everything it can do. Nothing is out of reach: `reticle_tools { names: [...] }` lists the tools and returns the full argument grammar for the ones you name, and `reticle_run { tool, args }` invokes any of them, advertised or not. A host that is short on tool slots pays for the advertised handful and still reaches the whole surface through that pair. See [`reticle_tools` and `reticle_run`](/tools-tools-and-run).
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
96
113
|
## Exact steps per platform
|
|
97
114
|
|
|
98
115
|
The shape is identical (in-app SDK in the template → verify in the sandbox → act on the verdict); the specifics differ by where each platform runs the preview.
|
package/docs/predicates.mdx
CHANGED
|
@@ -115,6 +115,14 @@ A passing route verdict quotes the transition itself:
|
|
|
115
115
|
|
|
116
116
|
The app emitted a named signal via `reticle.signal()`. `dataMatches` is shallow JSON matching, and `*` means "present, any value".
|
|
117
117
|
|
|
118
|
+
`count` works here exactly as it does on `net`:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{ "kind": "signal", "name": "order:placed", "count": 1 }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Exactly once.** A handler wired twice fires the signal twice and leaves the store in the right shape either way, so every state-only check stays green. `count: 0` is a claim of its own (the signal never fired) and is not the same as leaving `count` out, which asserts presence.
|
|
125
|
+
|
|
118
126
|
Nothing outranks this. A `200` proves the server was reachable; a rendered row proves React ran. A signal is the application itself saying the thing succeeded. See [instrumentation](/instrumentation) for how to emit them.
|
|
119
127
|
|
|
120
128
|
### state
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Reticle: what each tool actually does'
|
|
3
|
+
description: Generated from real calls against a running app. Every row is a probe that ran and the answer it got.
|
|
4
|
+
icon: table-list
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**Generated by `bench/harness/behaviour-matrix.mjs`. Do not edit by hand.**
|
|
8
|
+
|
|
9
|
+
Every row below is a call that was made against a running app and the answer that came back. Nothing here is described from the source or from intent. If a claim has no probe behind it, it is not in this file. That is the whole point: the hand-written guides drifted, and the drift was invisible because nobody ran them.
|
|
10
|
+
|
|
11
|
+
`refused (names the fix)` means the error told you what to call instead, so you can correct without reading anything else. `refused (no fix named)` means it did not.
|
|
12
|
+
|
|
13
|
+
## reticle_snapshot
|
|
14
|
+
|
|
15
|
+
| you call it with | you get back | bytes |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| an argument the tool does not declare: `{"thisArgDoesNotExist":1}` | refused (names the fix) | 273 |
|
|
18
|
+
| the documented call: `{"mode":"interactive"}` | ran | 434 |
|
|
19
|
+
| a value outside the allowed set: `{"mode":"not-a-mode"}` | refused (names the fix) | 236 |
|
|
20
|
+
|
|
21
|
+
## reticle_query
|
|
22
|
+
|
|
23
|
+
| you call it with | you get back | bytes |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| an argument the tool does not declare: `{"thisArgDoesNotExist":1}` | refused (names the fix) | 368 |
|
|
26
|
+
| the documented call: `{"by":"role","value":"button"}` | ran | 200 |
|
|
27
|
+
| a value outside the allowed set: `{"by":"telepathy","value":"x"}` | refused (names the fix) | 290 |
|
|
28
|
+
|
|
29
|
+
## reticle_network
|
|
30
|
+
|
|
31
|
+
| you call it with | you get back | bytes |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| an argument the tool does not declare: `{"thisArgDoesNotExist":1}` | refused (names the fix) | 354 |
|
|
34
|
+
| the documented call: `{"limit":5}` | ran | 1175 |
|
|
35
|
+
| a number outside its range: `{"limit":-1}` | refused (names the fix) | 222 |
|
|
36
|
+
|
|
37
|
+
## reticle_console
|
|
38
|
+
|
|
39
|
+
| you call it with | you get back | bytes |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| an argument the tool does not declare: `{"thisArgDoesNotExist":1}` | refused (names the fix) | 293 |
|
|
42
|
+
|
|
43
|
+
## reticle_state
|
|
44
|
+
|
|
45
|
+
| you call it with | you get back | bytes |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| an argument the tool does not declare: `{"thisArgDoesNotExist":1}` | refused (names the fix) | 268 |
|
|
48
|
+
| a number outside its range: `{"depth":-5}` | refused (names the fix) | 173 |
|
|
49
|
+
| the documented call: `{"depth":2}` | ran | 210 |
|
|
50
|
+
|
|
51
|
+
- a negative depth was silently ignored before v2.12.0
|
|
52
|
+
|
|
53
|
+
## reticle_observe
|
|
54
|
+
|
|
55
|
+
| you call it with | you get back | bytes |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| an argument the tool does not declare: `{"thisArgDoesNotExist":1}` | refused (names the fix) | 305 |
|
|
58
|
+
|
|
59
|
+
## reticle_act_and_wait
|
|
60
|
+
|
|
61
|
+
| you call it with | you get back | bytes |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| no expectation declared: `{"ref":"e1005","action":"click"}` | verified:no-fault | 1343 |
|
|
64
|
+
| the documented call: `{"ref":"e1105","action":"click","until":{"kind":"text","value":"Invalid email or password"},"timeout_ms":6000}` | verified:yes | 1297 |
|
|
65
|
+
| no expectation declared: `{"ref":"e1205","action":"click","until":{"kind":"settled"}}` | verified:no-fault | 1343 |
|
|
66
|
+
| a value outside the allowed set: `{"ref":"e903","action":"click","until":[{"kind":"text","value":"x"}]}` | refused (names the fix) | 399 |
|
|
67
|
+
| a ref that no longer resolves: `{"ref":"e999999","action":"click"}` | refused (names the fix) | 519 |
|
|
68
|
+
|
|
69
|
+
- no `until`: this returned verified:"yes" before v2.12.0
|
|
70
|
+
|
|
71
|
+
- a consequence the click CAUSES: the shape that earns verified:"yes"
|
|
72
|
+
|
|
73
|
+
- an explicit `{kind:"settled"}` is the idle wait, not a declaration, so it grades no-fault too
|
|
74
|
+
|
|
75
|
+
- a bare array is not a predicate
|
|
76
|
+
|
|
77
|
+
## reticle_act
|
|
78
|
+
|
|
79
|
+
| you call it with | you get back | bytes |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| a required argument omitted: `{"ref":"e901"}` | refused (names the fix) | 192 |
|
|
82
|
+
| a value outside the allowed set: `{"ref":"e901","action":"teleport"}` | refused (names the fix) | 415 |
|
|
83
|
+
| an argument the tool does not declare: `{"ref":"e901","action":"fill","value":"x"}` | refused (names the fix) | 314 |
|
|
84
|
+
| the documented call: `{"ref":"e901","action":"fill","args":{"value":"probe"}}` | refused (names the fix) | 516 |
|
|
85
|
+
|
|
86
|
+
- action arguments nest under `args`
|
|
87
|
+
|
|
88
|
+
## reticle_assert
|
|
89
|
+
|
|
90
|
+
| you call it with | you get back | bytes |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| a required argument omitted: `{}` | refused (names the fix) | 592 |
|
|
93
|
+
| the documented call: `{"predicate":{"kind":"text","value":"Sign in"},"timeout_ms":3000}` | verified:yes | 794 |
|
|
94
|
+
|
|
95
|
+
## reticle_tools
|
|
96
|
+
|
|
97
|
+
| you call it with | you get back | bytes |
|
|
98
|
+
| ------------------------- | ----------------------- | ----- |
|
|
99
|
+
| the documented call: `{}` | refused (names the fix) | 246 |
|
|
100
|
+
|
|
101
|
+
## reticle_run
|
|
102
|
+
|
|
103
|
+
| you call it with | you get back | bytes |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| a value outside the allowed set: `{"tool":"reticle_nope","args":{}}` | refused (names the fix) | 894 |
|
|
106
|
+
| the documented call: `{"tool":"reticle_context","args":{}}` | ran | 54 |
|
|
@@ -103,7 +103,6 @@ The single exception is `daemon_stopped`, which is **awaited**, because the proc
|
|
|
103
103
|
| `identified` | `reticle identify` | joins anonymous machine ids to a person who volunteered one |
|
|
104
104
|
| `mcp_client_connected` | an MCP client attached | how many sessions are agent-driven at all |
|
|
105
105
|
| `app_instrumented` | the first app carrying the SDK reached this daemon | **the funnel step everything turns on**; see below |
|
|
106
|
-
| `instrumentation_stalled` | the daemon waited and no app ever arrived | the same funnel step, seen from the failure side; see below |
|
|
107
106
|
| `mcp_connection_lost` | the proxy lost its daemon | **the transport-stability metric.** The disconnect that makes a user reopen `/mcp` is invisible without it |
|
|
108
107
|
| `init_completed` | `reticle init` finished | does install actually work, outside the fixtures gate |
|
|
109
108
|
| `bug_found` | a defect was detected in the app under test | the value delivered, as opposed to the work done |
|
|
@@ -117,17 +116,7 @@ Nothing measured the second. `daemon_started` and `mcp_client_connected` describ
|
|
|
117
116
|
|
|
118
117
|
`app_instrumented` fires **once per daemon run**, on the first session-ready only, so `daemon_started` → `app_instrumented` is a rate rather than an inference and a reloading page cannot inflate it. It carries `initialized` (had `init` run here), `agentAttached` (was an agent already waiting), and `msToFirstApp` (how long the daemon sat with nothing wired). It deliberately carries no stack and no framework: `project_profiled` already reports both for the same run, and the two join on `sessionId`.
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
`app_instrumented` fires on success and only on success, so the larger population, runs where no app ever arrives, exists in the data as an **absence**. An absence is the one thing telemetry cannot interpret: it cannot separate "nothing was ever wired" from "the process was killed before it could say so" from "we never sent the event". Since the funnel breaks at this step and nowhere else, this is the one silence worth converting into a signal.
|
|
123
|
-
|
|
124
|
-
`instrumentation_stalled` fires **once per daemon run**, only after the daemon has been up past a generous threshold, and **never once an app has connected**. A run that starts slowly and then works reports nothing at all. It carries the same three facts as its success counterpart so the two are directly comparable, with `msWaited` in place of `msToFirstApp`.
|
|
125
|
-
|
|
126
|
-
Read it against `agentAttached`, which is what makes it actionable rather than merely sad. A stalled daemon with no agent attached is somebody who installed and walked away, and is not a defect. A stalled daemon **with** an agent attached is the failure the product actually has: the tools are loaded, something is asking for them, and there is nothing on the other end to drive.
|
|
127
|
-
|
|
128
|
-
It deliberately carries no diagnosis. Whether a dev server is listening is a live probe, and running one on a timer to enrich a metric would be collecting for the metric's sake. The diagnosis belongs where a human or an agent asks for it.
|
|
129
|
-
|
|
130
|
-
What neither event can see is **why** an app never connected. Every cause for that is page-side (the non-localhost gate, a port mismatch, a stale build, a dev server never restarted), where the daemon has no visibility. That needs the SDK to report its own refusals, and it is the next thing to build.
|
|
119
|
+
The failure side is a **set difference**, not an event: `daemon_started` minus `app_instrumented`, joined on `sessionId`. There used to be an `instrumentation_stalled` event for it, and it was removed because the derivation is strictly more complete. The event could only fire on a flush tick or a graceful shutdown, so a killed daemon never produced one and landed in the absence anyway, while the set difference catches it. Two ways to compute one number, disagreeing with each other, is worse than one way.
|
|
131
120
|
|
|
132
121
|
## Sessions: `daemon_stopped` vs `session_progress`
|
|
133
122
|
|
|
@@ -55,7 +55,7 @@ Plus `installSource`, which is not a block. It is resolved once per process and
|
|
|
55
55
|
|
|
56
56
|
`isSessionScoped(kind)` decides whether `sessionId` is attached. Twelve kinds are session-scoped:
|
|
57
57
|
|
|
58
|
-
`daemon_started`, `daemon_stopped`, `session_progress`, `mcp_client_connected`, `app_instrumented`, `
|
|
58
|
+
`daemon_started`, `daemon_stopped`, `session_progress`, `mcp_client_connected`, `app_instrumented`, `project_profiled`, `verification_completed`, `bug_found`, `tool_refused`, `runtime_crashed`, `feedback_submitted`.
|
|
59
59
|
|
|
60
60
|
`mcp_connection_lost` is deliberately **not** on that list even though it feels session-shaped. It fires from the proxy process rather than the daemon, so it carries no `sessionId` and cannot be joined to a session at all.
|
|
61
61
|
|
|
@@ -341,39 +341,6 @@ Fired **once per daemon run**, on the first connect only, so `daemon_started` to
|
|
|
341
341
|
|
|
342
342
|
---
|
|
343
343
|
|
|
344
|
-
## instrumentation_stalled
|
|
345
|
-
|
|
346
|
-
**Wire name:** `instrumentation_stalled`. **Emitted from:** `packages/server/src/telemetry/instrumentation-stall.ts:73`, called from `daemon-telemetry.ts:105`. **Session-scoped:** yes. **Payload block:** `stall`.
|
|
347
|
-
|
|
348
|
-
The failure side of `app_instrumented`, and the reason the funnel has a denominator at all. A daemon that runs for ten minutes without a single SDK-carrying page is an install that half worked, and until this event existed that outcome was pure silence: indistinguishable from a machine nobody touched.
|
|
349
|
-
|
|
350
|
-
| Field | Type | Meaning |
|
|
351
|
-
| --- | --- | --- |
|
|
352
|
-
| `initialized` | boolean | Has `init` run in this working directory (`readProjectId(cwd)` resolves). False means the daemon is running somewhere `init` never wrote a project id, which is usually a wrong-directory problem rather than a wiring one |
|
|
353
|
-
| `agentAttached` | boolean | Was an agent connected at the moment the check ran. **This is the field that decides whether the row is a defect.** False means somebody installed and walked away, which is not a bug. True means an agent was sitting there with tools it could not use, which is the failure the product actually has |
|
|
354
|
-
| `msWaited` | int | Milliseconds since the daemon started, from the clock marked at `daemon-telemetry.ts:84` |
|
|
355
|
-
|
|
356
|
-
Deliberately the same three facts as `app_instrumented`, with `msWaited` standing in for `msToFirstApp`, so the success and failure rows are directly comparable without reshaping either.
|
|
357
|
-
|
|
358
|
-
**When it fires:** on every flush tick, but only once all four guards at `instrumentation-stall.ts:64` pass: it has not already been reported this run, no app has ever connected, the clock was marked, and at least `STALL_AFTER_MS` (10 minutes) has elapsed. It is checked **before** the empty-window early return in `flushWindow`, which is not an accident: a stalled install produces an empty window by definition, so checking after the return would blind the event to the exact condition it exists to report.
|
|
359
|
-
|
|
360
|
-
**The question it answers:** of the installs that never reach a verdict, how many had an agent present and waiting? That is the difference between a distribution problem and a wiring problem, and they need opposite responses.
|
|
361
|
-
|
|
362
|
-
**What it is blind to:**
|
|
363
|
-
|
|
364
|
-
- **Any diagnosis.** It never says whether a dev server is listening, because that is a live probe, and probing a user's machine to enrich a metric is collecting for the metric's sake.
|
|
365
|
-
- **Slow starts.** A run that connects at minute eleven reports nothing at all, and neither does the recovery. The event answers "did this run stall", not "how long do installs take".
|
|
366
|
-
- **The page side.** Every real cause (a non-localhost gate, a port mismatch, a stale build, a dev server never restarted) lives where the daemon cannot see. Attributing a stall needs the SDK to report its own refusals, and that is not built.
|
|
367
|
-
- **Repeats.** Once per daemon run, so a user stuck for a week contributes one row per daemon start, not one per hour.
|
|
368
|
-
|
|
369
|
-
<Warning>
|
|
370
|
-
This kind has no live check in `apps/e2e/specs/telemetry-events-test.mjs`, and neither do
|
|
371
|
-
`session_progress` and `app_instrumented`. Those three are the funnel, and the funnel is the part
|
|
372
|
-
of the wire nothing fires end to end. Adding one is the highest-value thing left in this file.
|
|
373
|
-
</Warning>
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
344
|
## mcp_connection_lost
|
|
378
345
|
|
|
379
346
|
**Wire name:** `mcp_connection_lost`. **Emitted from:** `packages/server/src/mcp/mcp-outage.ts:53`. **Session-scoped:** no. **Payload block:** `outage`.
|
package/docs/telemetry.md
CHANGED
|
@@ -31,7 +31,6 @@ Eighteen kinds of events, each a single small JSON object. This is the whole lis
|
|
|
31
31
|
| `daemon_stopped` | The local daemon stops | A summary of the session; see below |
|
|
32
32
|
| `session_progress` | Periodically, from a daemon that is still running | The same summary as `daemon_stopped`, marked `final: false`. It exists so a long session is not invisible until it exits |
|
|
33
33
|
| `app_instrumented` | The first time your app's SDK connects in a given daemon run | Nothing beyond the common fields. It is the second half of the install funnel: registering the MCP server is one thing, getting the SDK into a running page is another |
|
|
34
|
-
| `instrumentation_stalled` | Ten minutes into a daemon run in which your app has still never connected | The same three facts as `app_instrumented`, with how long it waited instead of how long it took. It fires at most once per daemon run, and a run that starts slowly and then works reports nothing at all. It carries no diagnosis: whether a dev server is listening would need a live probe, and probing your machine to enrich a metric is collecting for the metric's sake |
|
|
35
34
|
| `verification_completed` | A verification produces a verdict | Whether it passed, whether Reticle refused to call a passing check verified, and **why** the verdict came out that way; see below |
|
|
36
35
|
| `project_profiled` | Once per daemon start | The shape of the project; see below |
|
|
37
36
|
| `version_changed` | You update or roll back | The two version numbers, and which direction |
|
|
@@ -6,9 +6,7 @@ icon: boxes-stacked
|
|
|
6
6
|
|
|
7
7
|
`reticle_tools` lists every Reticle tool and, on request, returns the full argument grammar for the ones you name. `reticle_run` then invokes any of them. Reach for the pair whenever you need a capability that is not in the default advertised surface, which is most of what Reticle can do.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`reticle_tools` discovers. `reticle_run` invokes. Together they cost two tool definitions instead of twenty-nine.
|
|
9
|
+
`reticle_tools` discovers. `reticle_run` invokes. Together they cost two tool definitions instead of the rest of the catalog.
|
|
12
10
|
|
|
13
11
|
## Discovering what exists
|
|
14
12
|
|
|
@@ -41,7 +39,7 @@ The response also tells you which surface is active:
|
|
|
41
39
|
"profile": {
|
|
42
40
|
"active": "all",
|
|
43
41
|
"source": "RETICLE_ADVERTISE_ALL_TOOLS set in the DAEMON's environment at startup",
|
|
44
|
-
"note": "The
|
|
42
|
+
"note": "The surface is read once at daemon startup: set RETICLE_ADVERTISE_ALL_TOOLS=1 and restart the daemon, or it has no effect. No surface advertises every tool: the advertised count is capped because editors budget tools across all connected MCP servers. Every tool listed here is callable through reticle_run { tool, args } whether or not it is advertised."
|
|
45
43
|
},
|
|
46
44
|
"next": "Load params with reticle_tools { names:[…] }, then call reticle_run { tool, args }."
|
|
47
45
|
}
|
package/docs/troubleshooting.mdx
CHANGED
|
@@ -83,6 +83,15 @@ the URL explicitly (Vite: VITE_RETICLE_WS_URL, or reticle.connect({ url })). An
|
|
|
83
83
|
open a ws:// socket at all. Still retrying…
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
**You should not have to read this page to find that out.** If the agent opened the app with `reticle_lease`, which is the path `init` steers it to, the daemon reads that address off the page's own console, compares it with the port it is bound to, and puts the mismatch in the tool result:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
The page dialled ws://localhost:4460/reticle (port 4460) and this daemon is on
|
|
90
|
+
4400, so the dial never reached it. Nothing about the app's wiring is in question.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Neither side can work that out alone: the page cannot see the daemon, and the daemon never saw a dial that went somewhere else. The rest of this section covers what the lease cannot: a tab you opened yourself, or an app the pool cannot reach.
|
|
94
|
+
|
|
86
95
|
That message names the exact address it tried. Compare its port with `bridge port` from `doctor` and set whichever is wrong:
|
|
87
96
|
|
|
88
97
|
```bash
|
package/docs/usage.md
CHANGED
|
@@ -183,7 +183,7 @@ The timeline + summary of what happened.
|
|
|
183
183
|
|
|
184
184
|
Act, then wait for a predicate: the whole act→observe→assert loop in one hop.
|
|
185
185
|
|
|
186
|
-
- **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `sessionId?`.
|
|
186
|
+
- **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `intent?`, `sessionId?`.
|
|
187
187
|
- **returns:** `{ effect, verdict, trace, session, warning? }`. `effect` is the action result (`{ ok, ref, action }`), `verdict` is `{ pass, evidence?, failureReason? }`, `trace` is the reaction report of everything the app did after the action, and `session` is the tab-health block `{ lastSeenMs, throttled, focused }` (with a `warning` when throttled). A failing `verdict` still returns `effect` + `trace` so you can see what _did_ happen. The predicate is automatically floored at this act's cursor, so it only matches events the action actually caused.
|
|
188
188
|
|
|
189
189
|
### `reticle_wait_for`
|
|
@@ -197,9 +197,10 @@ Block until a predicate holds (or time out). Looks both backward (recent buffer)
|
|
|
197
197
|
|
|
198
198
|
Verify a predicate; optionally wait for it.
|
|
199
199
|
|
|
200
|
-
- **args:** `predicate`, `timeout_ms?` (0 = evaluate once), `since?`, `sessionId?`.
|
|
200
|
+
- **args:** `predicate`, `timeout_ms?` (0 = evaluate once), `since?`, `intent?`, `sessionId?`.
|
|
201
201
|
- Same `since` default as `reticle_wait_for`: scoped to your last act so a stale buffered event can't fake a pass; override with an explicit `since`.
|
|
202
202
|
- **returns:** `{ verified, because, pass, evidence, contradictions?, coverage?, failureReason?, session, warning? }`. On failure includes a **near-miss** (e.g. "found the dialog but not visible", or "no button named 'Submit'; saw: Cancel"). The `session` block `{ lastSeenMs, throttled, focused }` reports tab health on every assert; when throttled a `warning` is attached so you never assert against a tab that is silently no-oping.
|
|
203
|
+
- **`intent` declares what the change was FOR, inline.** Pass a sentence in your own words and it lands in `.reticle/intent.json`, the same git-checked ledger `reticle_intent` writes, before the verdict is drawn; a green verdict then marks it proved and names itself as the proof. Pass the **id** of an intent you already declared instead, to point several verdicts at one statement rather than restating it. `reticle_act_and_wait` takes the same argument. Omit it and nothing is written.
|
|
203
204
|
- **Read `verified`, not `pass`.** `pass` says the predicate held; `verified` says whether that means anything. It is `"no"` when a channel contradicts the assertion (a failed write under a green screen, a batch whose body reports per-item failures, a request still in flight), and `"unknown"` when the outcome could not be known yet: a `202 Accepted` that has not reconciled, or a write whose response body was never recorded. `because` names the deciding evidence in one sentence.
|
|
204
205
|
|
|
205
206
|
### `reticle_reconcile`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reticlehq/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "Reticle bridge + MCP server. Hosts the browser WS endpoint and exposes MCP tools to the coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
23
23
|
"ws": "^8.21.3",
|
|
24
24
|
"zod": "^3.24.1",
|
|
25
|
-
"@reticlehq/core": "2.
|
|
25
|
+
"@reticlehq/core": "2.12.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/pngjs": "^6.0.5",
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* First-run readiness: the agent often issues its first tool call in the window between `reticle init`
|
|
3
|
-
* and the app's SDK actually connecting its WebSocket — so a naive resolve throws "no session". This
|
|
4
|
-
* lets the agent (via reticle_wait_ready) BLOCK briefly until a session appears instead of failing the
|
|
5
|
-
* race, smoothing the most common first-5-minutes footgun.
|
|
6
|
-
*
|
|
7
|
-
* Pure control loop: the session count, clock, and sleep are all injected, so it is unit-tested with
|
|
8
|
-
* no real timers — and in production it polls a real `setTimeout` sleep.
|
|
9
|
-
*/
|
|
10
|
-
interface WaitForReadyOptions {
|
|
11
|
-
/** Live count of connected sessions (SessionManager.count). */
|
|
12
|
-
count: () => number;
|
|
13
|
-
/** Give up after this much elapsed time. */
|
|
14
|
-
timeoutMs: number;
|
|
15
|
-
/** Injected monotonic clock (ms). */
|
|
16
|
-
now: () => number;
|
|
17
|
-
/** Injected delay between polls. */
|
|
18
|
-
sleep: (ms: number) => Promise<void>;
|
|
19
|
-
/** Poll interval (ms). Default 25 — see DEFAULT_POLL_MS. */
|
|
20
|
-
pollMs?: number;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* One-line orientation for a FRESH agent — the Reticle loop, returned by reticle_wait_ready (the first call)
|
|
24
|
-
* so an agent learns how to drive Reticle without reading docs. Named so it is not a free string; kept
|
|
25
|
-
* terse on purpose (it rides the first response, where token budget is tightest).
|
|
26
|
-
*/
|
|
27
|
-
export declare const RETICLE_LOOP_GUIDE: string;
|
|
28
|
-
/**
|
|
29
|
-
* Resolve `true` as soon as at least one session is connected, or `false` if the timeout elapses
|
|
30
|
-
* first. Returns immediately when a session is already connected (the common, already-ready case —
|
|
31
|
-
* so this never adds latency on the happy path, including the benchmark which always has a session).
|
|
32
|
-
*/
|
|
33
|
-
export declare function waitForReady(opts: WaitForReadyOptions): Promise<boolean>;
|
|
34
|
-
export {};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Same reasoning as session-reconnect: the check is an in-memory lookup and the thing awaited is an
|
|
3
|
-
* event (a session registering), so a coarse grid only adds latency to the first call an agent
|
|
4
|
-
* makes. Four times the checks of something with no I/O costs nothing measurable.
|
|
5
|
-
*/
|
|
6
|
-
const DEFAULT_POLL_MS = 25;
|
|
7
|
-
/**
|
|
8
|
-
* One-line orientation for a FRESH agent — the Reticle loop, returned by reticle_wait_ready (the first call)
|
|
9
|
-
* so an agent learns how to drive Reticle without reading docs. Named so it is not a free string; kept
|
|
10
|
-
* terse on purpose (it rides the first response, where token budget is tightest).
|
|
11
|
-
*/
|
|
12
|
-
export const RETICLE_LOOP_GUIDE = 'Reticle loop — LOOK: reticle_snapshot / reticle_query / reticle_inspect · ACT: reticle_act (or reticle_act_and_wait) · ' +
|
|
13
|
-
'OBSERVE: reticle_observe / reticle_wait_for / reticle_network / reticle_console · ASSERT: reticle_assert over program ' +
|
|
14
|
-
'truth, not just the DOM · REGRESS: reticle_record{action:"start"} → reticle_replay, or reticle_verify{action:"flows"} for the whole ' +
|
|
15
|
-
'suite. The human can flag bugs from the panel — drain them with reticle_session{action:"review"} and resolve each once fixed. ' +
|
|
16
|
-
'MANDATORY: the moment you stop driving — finishing your reply or waiting on the human — call reticle_session {action:"yield"} ' +
|
|
17
|
-
'(mode:"waiting", or mode:"ask" with the question) so the panel shows your real state; reticle_session{action:"end"} ' +
|
|
18
|
-
'only when the whole task is done. Never leave the panel reading "live" when you have actually stopped.';
|
|
19
|
-
/**
|
|
20
|
-
* Resolve `true` as soon as at least one session is connected, or `false` if the timeout elapses
|
|
21
|
-
* first. Returns immediately when a session is already connected (the common, already-ready case —
|
|
22
|
-
* so this never adds latency on the happy path, including the benchmark which always has a session).
|
|
23
|
-
*/
|
|
24
|
-
export async function waitForReady(opts) {
|
|
25
|
-
if (opts.count() > 0)
|
|
26
|
-
return true;
|
|
27
|
-
const start = opts.now();
|
|
28
|
-
const poll = opts.pollMs ?? DEFAULT_POLL_MS;
|
|
29
|
-
for (;;) {
|
|
30
|
-
if (opts.now() - start >= opts.timeoutMs)
|
|
31
|
-
return opts.count() > 0;
|
|
32
|
-
await opts.sleep(poll);
|
|
33
|
-
if (opts.count() > 0)
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
}
|