@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
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The harness driver for OpenAI's Chat Completions API.
|
|
3
|
-
*
|
|
4
|
-
* Third driver, and the reason it exists is comparison rather than capability: people want to know
|
|
5
|
-
* whether the cheap typed-decision model is good enough for THEIR app, and "cheap versus one
|
|
6
|
-
* frontier model" is a weaker answer than "cheap versus the two they might otherwise reach for".
|
|
7
|
-
*
|
|
8
|
-
* A translation layer, exactly like `driver.ts` — the loop lives in `harness.ts` against
|
|
9
|
-
* `ModelDriver` and knows nothing about any wire. Chat Completions is a genuinely different shape
|
|
10
|
-
* from the Messages API, and translating OpenAI into Anthropic's wire on the platform side was
|
|
11
|
-
* considered and rejected there: it would put a second, undeclared model-shaped transformation
|
|
12
|
-
* inside the thing being measured. So this speaks the native format and the arm measures OpenAI.
|
|
13
|
-
*
|
|
14
|
-
* NOT cache-annotated, and that is a real asymmetry to hold in mind when reading a cost comparison
|
|
15
|
-
* against the Anthropic arm. OpenAI caches long prefixes automatically and reports it back in
|
|
16
|
-
* `usage.prompt_tokens_details.cached_tokens`, so the saving is not something this driver arranges;
|
|
17
|
-
* it is reported below so the number is visible rather than assumed.
|
|
18
|
-
*/
|
|
19
|
-
import type { ModelDriver } from './harness.js';
|
|
20
|
-
import type { HarnessFetch } from './driver.js';
|
|
21
|
-
/**
|
|
22
|
-
* The default model.
|
|
23
|
-
*
|
|
24
|
-
* Deliberately not the largest available, for the same reason `DEFAULT_HARNESS_MODEL` is not: the
|
|
25
|
-
* driver is choosing what to touch next, and the engine decides the verdict from evidence. A driver
|
|
26
|
-
* that needed a frontier model would be evidence the engine was not doing its job.
|
|
27
|
-
*/
|
|
28
|
-
export declare const DEFAULT_OPENAI_MODEL = "gpt-5.6-luna";
|
|
29
|
-
export declare const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
|
|
30
|
-
export interface OpenAiDriverOptions {
|
|
31
|
-
apiKey: string;
|
|
32
|
-
model?: string;
|
|
33
|
-
baseUrl?: string;
|
|
34
|
-
/** Injected for tests. Defaults to the platform `fetch`. */
|
|
35
|
-
fetch?: HarnessFetch;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Read this driver's configuration out of the environment.
|
|
39
|
-
*
|
|
40
|
-
* A real OpenAI key goes to OpenAI. The platform key goes to the platform, which forwards with its
|
|
41
|
-
* own — so somebody who has run `reticle link` can drive with this model having never held an
|
|
42
|
-
* OpenAI key, which is the entire point of the proxy.
|
|
43
|
-
*/
|
|
44
|
-
export declare function openAiOptionsFromEnv(env: Record<string, string | undefined>): OpenAiDriverOptions | undefined;
|
|
45
|
-
/** Build a driver backed by Chat Completions. */
|
|
46
|
-
export declare function openAiDriver(options: OpenAiDriverOptions): ModelDriver;
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The harness driver for OpenAI's Chat Completions API.
|
|
3
|
-
*
|
|
4
|
-
* Third driver, and the reason it exists is comparison rather than capability: people want to know
|
|
5
|
-
* whether the cheap typed-decision model is good enough for THEIR app, and "cheap versus one
|
|
6
|
-
* frontier model" is a weaker answer than "cheap versus the two they might otherwise reach for".
|
|
7
|
-
*
|
|
8
|
-
* A translation layer, exactly like `driver.ts` — the loop lives in `harness.ts` against
|
|
9
|
-
* `ModelDriver` and knows nothing about any wire. Chat Completions is a genuinely different shape
|
|
10
|
-
* from the Messages API, and translating OpenAI into Anthropic's wire on the platform side was
|
|
11
|
-
* considered and rejected there: it would put a second, undeclared model-shaped transformation
|
|
12
|
-
* inside the thing being measured. So this speaks the native format and the arm measures OpenAI.
|
|
13
|
-
*
|
|
14
|
-
* NOT cache-annotated, and that is a real asymmetry to hold in mind when reading a cost comparison
|
|
15
|
-
* against the Anthropic arm. OpenAI caches long prefixes automatically and reports it back in
|
|
16
|
-
* `usage.prompt_tokens_details.cached_tokens`, so the saving is not something this driver arranges;
|
|
17
|
-
* it is reported below so the number is visible rather than assumed.
|
|
18
|
-
*/
|
|
19
|
-
import { z } from 'zod';
|
|
20
|
-
import { ReticleEnv, platformCredentialFrom } from '@reticlehq/core';
|
|
21
|
-
/**
|
|
22
|
-
* The default model.
|
|
23
|
-
*
|
|
24
|
-
* Deliberately not the largest available, for the same reason `DEFAULT_HARNESS_MODEL` is not: the
|
|
25
|
-
* driver is choosing what to touch next, and the engine decides the verdict from evidence. A driver
|
|
26
|
-
* that needed a frontier model would be evidence the engine was not doing its job.
|
|
27
|
-
*/
|
|
28
|
-
export const DEFAULT_OPENAI_MODEL = 'gpt-5.6-luna';
|
|
29
|
-
export const DEFAULT_OPENAI_BASE_URL = 'https://api.openai.com';
|
|
30
|
-
/** The platform's own path, which forwards with Reticle's key rather than the caller's. */
|
|
31
|
-
const PLATFORM_PATH = '/v1/model/openai';
|
|
32
|
-
const OPENAI_PATH = '/v1/chat/completions';
|
|
33
|
-
/** Output cap per turn. A driving turn is a sentence and a tool call, never an essay. */
|
|
34
|
-
const MAX_TOKENS = 4096;
|
|
35
|
-
/** How much of one tool result the model is allowed to see. Same reasoning as the Anthropic driver. */
|
|
36
|
-
const MAX_RESULT_CHARS = 8000;
|
|
37
|
-
const ToolCall = z.object({
|
|
38
|
-
id: z.string(),
|
|
39
|
-
function: z.object({ name: z.string(), arguments: z.string().default('{}') }),
|
|
40
|
-
});
|
|
41
|
-
const ChatResponse = z.object({
|
|
42
|
-
choices: z
|
|
43
|
-
.array(z.object({
|
|
44
|
-
message: z.object({
|
|
45
|
-
content: z.string().nullish(),
|
|
46
|
-
tool_calls: z.array(ToolCall).nullish(),
|
|
47
|
-
}),
|
|
48
|
-
}))
|
|
49
|
-
.default([]),
|
|
50
|
-
usage: z
|
|
51
|
-
.object({
|
|
52
|
-
prompt_tokens: z.number().default(0),
|
|
53
|
-
completion_tokens: z.number().default(0),
|
|
54
|
-
prompt_tokens_details: z.object({ cached_tokens: z.number().nullish() }).nullish(),
|
|
55
|
-
})
|
|
56
|
-
.default({ prompt_tokens: 0, completion_tokens: 0 }),
|
|
57
|
-
});
|
|
58
|
-
/**
|
|
59
|
-
* Read this driver's configuration out of the environment.
|
|
60
|
-
*
|
|
61
|
-
* A real OpenAI key goes to OpenAI. The platform key goes to the platform, which forwards with its
|
|
62
|
-
* own — so somebody who has run `reticle link` can drive with this model having never held an
|
|
63
|
-
* OpenAI key, which is the entire point of the proxy.
|
|
64
|
-
*/
|
|
65
|
-
export function openAiOptionsFromEnv(env) {
|
|
66
|
-
const model = env[ReticleEnv.HARNESS_OPENAI_MODEL];
|
|
67
|
-
const withModel = (rest) => ({
|
|
68
|
-
...rest,
|
|
69
|
-
...(model === undefined || 0 === model.length ? {} : { model }),
|
|
70
|
-
});
|
|
71
|
-
const direct = env[ReticleEnv.HARNESS_OPENAI_KEY];
|
|
72
|
-
if (direct !== undefined && 0 < direct.length)
|
|
73
|
-
return withModel({ apiKey: direct });
|
|
74
|
-
const cloud = platformCredentialFrom(env);
|
|
75
|
-
if (cloud === undefined)
|
|
76
|
-
return undefined;
|
|
77
|
-
return withModel({ apiKey: cloud.apiKey, baseUrl: cloud.url });
|
|
78
|
-
}
|
|
79
|
-
function toWireTool(tool) {
|
|
80
|
-
return {
|
|
81
|
-
type: 'function',
|
|
82
|
-
function: { name: tool.name, description: tool.description, parameters: tool.inputSchema },
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function truncate(text) {
|
|
86
|
-
return text.length <= MAX_RESULT_CHARS
|
|
87
|
-
? text
|
|
88
|
-
: `${text.slice(0, MAX_RESULT_CHARS)}\n…[truncated: ${String(text.length - MAX_RESULT_CHARS)} more characters]`;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* The history, in Chat Completions' shape.
|
|
92
|
-
*
|
|
93
|
-
* The one structural difference from the Messages API, and it matters: a tool result is its OWN
|
|
94
|
-
* message carrying `tool_call_id`, where Anthropic puts every result for a turn into one user
|
|
95
|
-
* message. So a turn with three parallel calls becomes one assistant message and three tool
|
|
96
|
-
* messages. That is the format's requirement rather than a choice — an unanswered `tool_call_id`
|
|
97
|
-
* is rejected outright, so every outcome must be emitted, including the failures.
|
|
98
|
-
*/
|
|
99
|
-
function toWireMessages(history) {
|
|
100
|
-
const messages = [];
|
|
101
|
-
for (const entry of history) {
|
|
102
|
-
if ('user' === entry.role) {
|
|
103
|
-
messages.push({ role: 'user', content: entry.text });
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
if ('assistant' === entry.role) {
|
|
107
|
-
messages.push({
|
|
108
|
-
role: 'assistant',
|
|
109
|
-
content: 0 === entry.text.length ? null : entry.text,
|
|
110
|
-
...(0 === entry.calls.length
|
|
111
|
-
? {}
|
|
112
|
-
: {
|
|
113
|
-
tool_calls: entry.calls.map((call) => ({
|
|
114
|
-
id: call.id,
|
|
115
|
-
type: 'function',
|
|
116
|
-
function: { name: call.name, arguments: JSON.stringify(call.args) },
|
|
117
|
-
})),
|
|
118
|
-
}),
|
|
119
|
-
});
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
for (const outcome of entry.outcomes) {
|
|
123
|
-
messages.push({
|
|
124
|
-
role: 'tool',
|
|
125
|
-
tool_call_id: outcome.id,
|
|
126
|
-
content: truncate(JSON.stringify(outcome.result)),
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return messages;
|
|
131
|
-
}
|
|
132
|
-
/** Arguments arrive as a JSON STRING here, not an object — a malformed one is empty, never a throw. */
|
|
133
|
-
function parseArgs(raw) {
|
|
134
|
-
try {
|
|
135
|
-
const parsed = JSON.parse(raw);
|
|
136
|
-
return 'object' === typeof parsed && null !== parsed ? parsed : {};
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
return {};
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
/** Build a driver backed by Chat Completions. */
|
|
143
|
-
export function openAiDriver(options) {
|
|
144
|
-
const model = options.model ?? DEFAULT_OPENAI_MODEL;
|
|
145
|
-
const baseUrl = options.baseUrl ?? DEFAULT_OPENAI_BASE_URL;
|
|
146
|
-
const doFetch = options.fetch ?? ((url, init) => fetch(url, init));
|
|
147
|
-
// The platform does not expose `/v1/chat/completions`; it exposes its own path and forwards. A
|
|
148
|
-
// base URL that is not OpenAI's is the platform's, which is the only other thing this talks to.
|
|
149
|
-
const path = DEFAULT_OPENAI_BASE_URL === baseUrl ? OPENAI_PATH : PLATFORM_PATH;
|
|
150
|
-
return {
|
|
151
|
-
async turn(input) {
|
|
152
|
-
const response = await doFetch(`${baseUrl}${path}`, {
|
|
153
|
-
method: 'POST',
|
|
154
|
-
headers: {
|
|
155
|
-
'content-type': 'application/json',
|
|
156
|
-
authorization: `Bearer ${options.apiKey}`,
|
|
157
|
-
},
|
|
158
|
-
body: JSON.stringify({
|
|
159
|
-
model,
|
|
160
|
-
max_completion_tokens: MAX_TOKENS,
|
|
161
|
-
tools: input.tools.map(toWireTool),
|
|
162
|
-
messages: [{ role: 'system', content: input.system }, ...toWireMessages(input.history)],
|
|
163
|
-
}),
|
|
164
|
-
});
|
|
165
|
-
const body = await response.text();
|
|
166
|
-
// Thrown, not returned: a model that will not answer is a BROKEN drive, and a broken drive is
|
|
167
|
-
// never reported as clean. Swallowing it would be a verdict about an app nobody drove.
|
|
168
|
-
if (!response.ok)
|
|
169
|
-
throw new Error(`the model refused the request (HTTP ${String(response.status)}): ${body}`);
|
|
170
|
-
const parsed = ChatResponse.parse(JSON.parse(body));
|
|
171
|
-
const message = parsed.choices[0]?.message;
|
|
172
|
-
const calls = (message?.tool_calls ?? []).map((call) => ({
|
|
173
|
-
id: call.id,
|
|
174
|
-
name: call.function.name,
|
|
175
|
-
args: parseArgs(call.function.arguments),
|
|
176
|
-
}));
|
|
177
|
-
const cached = parsed.usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
178
|
-
return {
|
|
179
|
-
text: message?.content ?? '',
|
|
180
|
-
calls,
|
|
181
|
-
usage: {
|
|
182
|
-
// `prompt_tokens` INCLUDES the cached ones here, unlike the Messages API where the two are
|
|
183
|
-
// disjoint. Subtracting keeps `input` meaning the same thing in both drivers: what was
|
|
184
|
-
// processed at full price. A cost comparison that summed them would charge the arm that
|
|
185
|
-
// cached well for the tokens it did not pay full price for.
|
|
186
|
-
input: Math.max(parsed.usage.prompt_tokens - cached, 0),
|
|
187
|
-
output: parsed.usage.completion_tokens,
|
|
188
|
-
cacheRead: cached,
|
|
189
|
-
cacheWrite: 0,
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
},
|
|
193
|
-
};
|
|
194
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The values a drive types into this project's fields, kept so they are paid for once.
|
|
3
|
-
*
|
|
4
|
-
* A generated field value is not a model's opinion to be re-asked every run. It is a FIXTURE: the
|
|
5
|
-
* same label on the same app wants the same value tomorrow, a replay must send exactly what the
|
|
6
|
-
* recording sent or it is not a replay, and a human who dislikes "Acme Corp" should be able to open
|
|
7
|
-
* a file and write something else rather than argue with a model about it. So it lives beside the
|
|
8
|
-
* flows it belongs to, in git, in the project's own directory.
|
|
9
|
-
*
|
|
10
|
-
* Read once at the start of a drive and written once at the end: a drive that fills twenty fields
|
|
11
|
-
* must not write the file twenty times, and nothing else in the process is competing for it.
|
|
12
|
-
*
|
|
13
|
-
* Every failure is silent and answers "no values". A drive must never fail over a fixtures file,
|
|
14
|
-
* and the fallback -- the label heuristic -- is what every drive used before this existed.
|
|
15
|
-
*/
|
|
16
|
-
import type { FileSystemPort } from '../fs/fs-port.js';
|
|
17
|
-
/** What a drive holds while it runs: a read-through map plus whatever it learned. */
|
|
18
|
-
export interface FillValueStore {
|
|
19
|
-
get(label: string): string | undefined;
|
|
20
|
-
set(label: string, value: string): void;
|
|
21
|
-
/** Persist, if anything was learned. Silent on failure, like every read here. */
|
|
22
|
-
flush(): Promise<void>;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* A store that remembers within one drive and writes nothing.
|
|
26
|
-
*
|
|
27
|
-
* Used when there is no project directory to write to. A drive must never fail over its fixtures
|
|
28
|
-
* file, and that includes failing to FIND one: the values still de-duplicate inside the run, they
|
|
29
|
-
* are simply not carried into the next one.
|
|
30
|
-
*/
|
|
31
|
-
export declare function memoryFillValues(): FillValueStore;
|
|
32
|
-
export declare function openFillValues(fs: FileSystemPort, root: string | undefined): Promise<FillValueStore>;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The values a drive types into this project's fields, kept so they are paid for once.
|
|
3
|
-
*
|
|
4
|
-
* A generated field value is not a model's opinion to be re-asked every run. It is a FIXTURE: the
|
|
5
|
-
* same label on the same app wants the same value tomorrow, a replay must send exactly what the
|
|
6
|
-
* recording sent or it is not a replay, and a human who dislikes "Acme Corp" should be able to open
|
|
7
|
-
* a file and write something else rather than argue with a model about it. So it lives beside the
|
|
8
|
-
* flows it belongs to, in git, in the project's own directory.
|
|
9
|
-
*
|
|
10
|
-
* Read once at the start of a drive and written once at the end: a drive that fills twenty fields
|
|
11
|
-
* must not write the file twenty times, and nothing else in the process is competing for it.
|
|
12
|
-
*
|
|
13
|
-
* Every failure is silent and answers "no values". A drive must never fail over a fixtures file,
|
|
14
|
-
* and the fallback -- the label heuristic -- is what every drive used before this existed.
|
|
15
|
-
*/
|
|
16
|
-
import { ReticleDir } from '@reticlehq/core';
|
|
17
|
-
import { join } from 'node:path';
|
|
18
|
-
/** Stamped so a later format change can tell an old file from a broken one. */
|
|
19
|
-
const VERSION = 1;
|
|
20
|
-
/** A label is a UI string and can be anything; a value that is not a string is not usable. */
|
|
21
|
-
function parse(text) {
|
|
22
|
-
try {
|
|
23
|
-
const raw = JSON.parse(text);
|
|
24
|
-
if ('object' !== typeof raw || null === raw)
|
|
25
|
-
return {};
|
|
26
|
-
const values = raw.values;
|
|
27
|
-
if ('object' !== typeof values || null === values)
|
|
28
|
-
return {};
|
|
29
|
-
const out = {};
|
|
30
|
-
for (const [label, value] of Object.entries(values))
|
|
31
|
-
if ('string' === typeof value && 0 < value.length)
|
|
32
|
-
out[label] = value;
|
|
33
|
-
return out;
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* A store that remembers within one drive and writes nothing.
|
|
41
|
-
*
|
|
42
|
-
* Used when there is no project directory to write to. A drive must never fail over its fixtures
|
|
43
|
-
* file, and that includes failing to FIND one: the values still de-duplicate inside the run, they
|
|
44
|
-
* are simply not carried into the next one.
|
|
45
|
-
*/
|
|
46
|
-
export function memoryFillValues() {
|
|
47
|
-
const values = new Map();
|
|
48
|
-
return {
|
|
49
|
-
get: (label) => values.get(label),
|
|
50
|
-
set: (label, value) => void values.set(label, value),
|
|
51
|
-
flush: () => Promise.resolve(),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
export async function openFillValues(fs, root) {
|
|
55
|
-
if (root === undefined || 0 === root.length)
|
|
56
|
-
return memoryFillValues();
|
|
57
|
-
const path = join(root, ReticleDir.FILL_VALUES_FILE);
|
|
58
|
-
let values = {};
|
|
59
|
-
try {
|
|
60
|
-
if (await fs.exists(path))
|
|
61
|
-
values = parse(await fs.readFile(path));
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
/* an unreadable fixtures file is the same as an absent one */
|
|
65
|
-
}
|
|
66
|
-
let learned = false;
|
|
67
|
-
return {
|
|
68
|
-
get: (label) => values[label],
|
|
69
|
-
set: (label, value) => {
|
|
70
|
-
if (values[label] === value)
|
|
71
|
-
return;
|
|
72
|
-
values[label] = value;
|
|
73
|
-
learned = true;
|
|
74
|
-
},
|
|
75
|
-
async flush() {
|
|
76
|
-
if (!learned)
|
|
77
|
-
return;
|
|
78
|
-
try {
|
|
79
|
-
await fs.mkdir(root);
|
|
80
|
-
const file = { version: VERSION, values };
|
|
81
|
-
await fs.writeFile(path, `${JSON.stringify(file, null, 2)}\n`);
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
/* the drive already happened; losing the cache costs a regeneration, not a result */
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
package/docs/local-registry.md
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Local registry
|
|
3
|
-
description: 'Exercise local, unpublished Reticle changes in a real external app before they ship.'
|
|
4
|
-
icon: box
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
To test unpublished `@reticlehq/*` changes in a real external app, publish the workspace to a local Verdaccio with `bash scripts/local-registry.sh`, then point that app's `.npmrc` at `http://localhost:4873/` as the **default** registry, not scoped to `@reticlehq`, for the reason in step 2. That is the whole procedure; the rest of this page is the detail.
|
|
8
|
-
|
|
9
|
-
> **For normal use, Reticle is on public npm.** Just `npm i -D @reticlehq/react @reticlehq/vite-plugin` (see [Getting Started](getting-started.md)). You only need this guide to test **local, unpublished changes** to the Reticle packages in a real external app before they ship.
|
|
10
|
-
|
|
11
|
-
Because the `@reticlehq/*` packages depend on each other via the workspace protocol, plain `npm pack` tarballs don't resolve cleanly. The reliable way to exercise your in-progress changes in a real app is a tiny **local registry** (Verdaccio), the same path CI uses to validate a publish.
|
|
12
|
-
|
|
13
|
-
## 1. Publish @reticlehq/\* to a local registry
|
|
14
|
-
|
|
15
|
-
From the Reticle repo:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
bash scripts/local-registry.sh
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
This starts a **fresh** Verdaccio on `http://localhost:4873`, creates a user/token, and publishes all `@reticlehq/*` packages there at the current workspace version:
|
|
22
|
-
|
|
23
|
-
| Package | What you install it for |
|
|
24
|
-
| --- | --- |
|
|
25
|
-
| **`@reticlehq/react`** | **install this**: the browser SDK kit you embed (re-exports the browser sensor, so one install gives both `reticle` and `install`) |
|
|
26
|
-
| `@reticlehq/vite-plugin` | dev-only source mapping + `connect()` injection (Vite) |
|
|
27
|
-
| `@reticlehq/next` | Next.js build wrapper (`withReticle`) |
|
|
28
|
-
| `@reticlehq/server` | the bridge + MCP server (your agent runs it, `npx @reticlehq/server mcp`) |
|
|
29
|
-
| `@reticlehq/babel-plugin` | React 19 source stamping (Babel) |
|
|
30
|
-
| `@reticlehq/test` | write declarative, signal-bound specs (`reticleTest`) |
|
|
31
|
-
| `@reticlehq/eslint-plugin` | the `require-signal-on-mutation` lint rule |
|
|
32
|
-
| `@reticlehq/core` | shared wire contract (pulled in automatically) |
|
|
33
|
-
|
|
34
|
-
For a browser app, install `@reticlehq/react` plus the build plugin for your framework (`@reticlehq/vite-plugin` or `@reticlehq/next`); `@reticlehq/server` is what your agent runs. (Verified: an external `npm i @reticlehq/react` resolves its graph, including `@reticlehq/core`, and imports correctly.) Leave the registry running.
|
|
35
|
-
|
|
36
|
-
> Note: pre-2.0 docs used a single `@reticlehq/core` umbrella package that re-exported everything; it's been split into the audience-scoped packages above.
|
|
37
|
-
|
|
38
|
-
## 2. Point your app at the local registry
|
|
39
|
-
|
|
40
|
-
In your app's project root, add an `.npmrc` pointing the **default** registry at Verdaccio. Verdaccio proxies npm for everything it does not hold, so the rest of your dependencies still resolve normally:
|
|
41
|
-
|
|
42
|
-
```ini
|
|
43
|
-
registry=http://localhost:4873/
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**A scope-only line does not work, and fails in a way that reads as a Reticle bug.** `@reticlehq/core` depends on **`open-verification`**, the protocol package, which is deliberately _unscoped_ because the protocol is not ours to namespace. With `@reticlehq:registry=…` npm sends that one request to npmjs and the install dies:
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
npm error 404 Not Found - GET https://registry.npmjs.org/open-verification
|
|
50
|
-
npm error 404 The requested resource 'open-verification@3.1.0' could not be found
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
`reticle init` then correctly reports `⚠ Install dependencies` and skips wiring the build plugin, so the app is left un-instrumented by a registry mistake two steps earlier.
|
|
54
|
-
|
|
55
|
-
There is no scoped form that fixes this: npm's per-registry setting is `@scope:registry`, and an unscoped package has no scope to key on. The default-registry line above is the only thing that works. Point it back at npm (`npm config delete registry`, or delete the `.npmrc`) when you are done testing.
|
|
56
|
-
|
|
57
|
-
## 3. Install + wire it up
|
|
58
|
-
|
|
59
|
-
Install the SDK kit plus the Vite build plugin (source mapping + `connect()` injection):
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
npm i -D @reticlehq/react @reticlehq/vite-plugin
|
|
63
|
-
# Next.js instead of Vite? npm i -D @reticlehq/react @reticlehq/next
|
|
64
|
-
# optional: npm i -D @reticlehq/eslint-plugin # require-signal-on-mutation lint rule
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Then follow [Getting Started](getting-started.md): embed `reticle.connect()` (dev only) from `@reticlehq/react`, add the MCP server to your agent, and (React) `install()` the adapter from `@reticlehq/react`. For the fastest agent loop, also do [Step 6: make your app agent-legible](getting-started.md) (testids, `reticle.signal`, `registerStore`, `registerCapabilities`) and the [integration patterns](integration-patterns.md) (`createReticleEmitter` for zero prod-bundle cost).
|
|
68
|
-
|
|
69
|
-
> **Upgrading.** `scripts/local-registry.sh` resets Verdaccio and republishes at whatever version the workspace is on right now, so your app will not pick up a rebuild on its own. Pull it explicitly with `@latest`:
|
|
70
|
-
>
|
|
71
|
-
> ```bash
|
|
72
|
-
> npm i -D @reticlehq/react@latest @reticlehq/vite-plugin@latest @reticlehq/eslint-plugin@latest
|
|
73
|
-
> ```
|
|
74
|
-
|
|
75
|
-
**Run the MCP server** from the local registry too; `npx @reticlehq/server` _is_ the server:
|
|
76
|
-
|
|
77
|
-
```jsonc
|
|
78
|
-
// .mcp.json: point npx at the local registry so it fetches @reticlehq/server from Verdaccio
|
|
79
|
-
{
|
|
80
|
-
"mcpServers": {
|
|
81
|
-
"reticle": {
|
|
82
|
-
"command": "npx",
|
|
83
|
-
"args": ["--registry", "http://localhost:4873/", "@reticlehq/server", "mcp"],
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Next.js specifics (verified on Next 15 / React 19)
|
|
90
|
-
|
|
91
|
-
`next.config.mjs`:
|
|
92
|
-
|
|
93
|
-
```js
|
|
94
|
-
import reticleNext from '@reticlehq/next';
|
|
95
|
-
/** @type {import('next').NextConfig} */
|
|
96
|
-
const nextConfig = {};
|
|
97
|
-
export default reticleNext.withReticle(nextConfig); // dev-only; keeps SWC; adds file:line mapping
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Mount the SDK from a dev-only client component (see the Next.js section in [Getting Started](getting-started.md)).
|
|
101
|
-
|
|
102
|
-
## Real input for hover/drag (optional)
|
|
103
|
-
|
|
104
|
-
Synthetic events can't trigger native `onMouseEnter`/pointer state (hover menus, tooltips, pointer drag). Enable **real input** so the server drives genuine pointer input and `reticle_act` reports `inputMode:"real"`:
|
|
105
|
-
|
|
106
|
-
- **Easiest (`reticle drive`):** Reticle launches its own scriptable, headless-capable browser at your app URL (no flags to juggle):
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
npx --registry http://localhost:4873/ @reticlehq/server drive http://localhost:4310 # add --headed to watch
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
- **Or attach to your own browser:** launch it with `--remote-debugging-port=9222`, then point the MCP server at it via `env`:
|
|
113
|
-
|
|
114
|
-
```jsonc
|
|
115
|
-
// .mcp.json
|
|
116
|
-
{
|
|
117
|
-
"mcpServers": {
|
|
118
|
-
"reticle": {
|
|
119
|
-
"command": "npx",
|
|
120
|
-
"args": ["--registry", "http://localhost:4873/", "@reticlehq/server", "mcp"],
|
|
121
|
-
"env": { "RETICLE_CDP_URL": "http://localhost:9222" },
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
With neither set, Reticle stays synthetic (zero extra deps) and says so via `inputMode`. See [usage §18](usage.md#18-real-input-mode-native-hover--drag).
|
|
128
|
-
|
|
129
|
-
## Write replayable specs + git-checked flows
|
|
130
|
-
|
|
131
|
-
- **Specs:** with `@reticlehq/test`, turn checks into `reticleTest("…", async t => { await t.act(...); await t.expectSignal(...) })`. They are signal/testid-bound, `reticle_clock` for determinism, `t.expectInputModeReal()` to skip-with-reason when real input isn't active. Run them headless via `reticle drive` (the same path CI uses).
|
|
132
|
-
- **Flows:** record a flow once and Reticle writes it to a git-checked `.reticle/flows/<name>.json` (anchored on testid/signal); `reticle_flow_replay` re-resolves anchors at run time and reports **legible drift** with a nearest-match; `reticle_verify { action: "heal" }` proposes/applies the rebind. A fresh agent reads `.reticle/contract.json` to learn your testable surface without grepping source.
|
|
133
|
-
|
|
134
|
-
## When you're ready for real npm
|
|
135
|
-
|
|
136
|
-
The same packages publish to public npm unchanged: `pnpm -r publish --access public` after `npm login`. The Verdaccio run above is a faithful rehearsal of that.
|
|
137
|
-
|
|
138
|
-
## Cleanup
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
pkill -f verdaccio # stop the local registry
|
|
142
|
-
# remove the line from your app's .npmrc when you switch to published packages
|
|
143
|
-
```
|