@reticlehq/server 2.4.0 → 2.5.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 +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +63 -13
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +1 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +9 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +2 -11
- package/dist/{cli-parse.js → cli/cli-parse.js} +113 -60
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +174 -92
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.js +24 -9
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +53 -7
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.js +4 -1
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +3 -2
- package/dist/init/detect.js +16 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +23 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +132 -8
- package/dist/init/plan.d.ts +73 -8
- package/dist/init/plan.js +133 -34
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +5 -0
- package/dist/init/run.js +210 -36
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +52 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +14 -2
- package/dist/session/session.js +69 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ReticleTool } from './tool-names.js';
|
|
2
|
+
/**
|
|
3
|
+
* There is ONE tool surface. This is not a menu.
|
|
4
|
+
*
|
|
5
|
+
* The advertised tool DEFINITIONS are re-sent to the model on every turn, so the surface is a
|
|
6
|
+
* per-turn cost that compounds across a loop, and fewer tools also makes the model wander less.
|
|
7
|
+
* What ships is the verify loop (navigate→look→act→observe→assert, with direct network + console +
|
|
8
|
+
* state) advertised directly, plus two meta-tools — `reticle_tools` to discover and `reticle_run` to
|
|
9
|
+
* invoke — that keep every other tool exactly one call away. Nothing is unreachable; the cold tail
|
|
10
|
+
* just is not re-sent every turn.
|
|
11
|
+
*
|
|
12
|
+
* Five profiles were tried. Four are gone, each for a measured reason rather than a taste:
|
|
13
|
+
*
|
|
14
|
+
* `core` was BYTE-IDENTICAL to the default — 16 tools, 18,183 bytes, the same list. One behaviour
|
|
15
|
+
* behind two names is how "I set it and nothing changed" becomes a support question with no answer.
|
|
16
|
+
*
|
|
17
|
+
* `standard` advertised 17 more tools directly for ~3,500 extra tokens EVERY TURN, buying only the
|
|
18
|
+
* removal of an occasional `reticle_run` hop. Nothing about that was characterisable as a use case.
|
|
19
|
+
*
|
|
20
|
+
* `dynamic` advertised the 2 meta-tools and nothing else. Not one harness, app or bench ever
|
|
21
|
+
* selected it, and this repo's own measurement (bench/agent-loop-and-replay.md) says a pure
|
|
22
|
+
* on-demand surface does NOT hold accuracy with a generic model: "it needs the hot-set schemas.
|
|
23
|
+
* On-demand is for the cold tail, not the hot path." An option that is unused and measured-worse is
|
|
24
|
+
* a trap, not a choice — it looks like a 4,000-token saving and costs correctness.
|
|
25
|
+
*
|
|
26
|
+
* ALL is what remains, and it is deliberately NOT a profile: it is a verification switch. It is the
|
|
27
|
+
* only mode that advertises `outputSchema`, which is what makes the MCP layer validate tool OUTPUT —
|
|
28
|
+
* the check that caught `reticle_verify_change` returning a payload its own schema rejected. It
|
|
29
|
+
* cannot be folded into the default: measured, carrying output schemas on the 16-tool surface costs
|
|
30
|
+
* 18,183 -> 41,117 bytes, 2.26x, +5,733 tokens per turn. And it cannot simply be deleted without
|
|
31
|
+
* losing that defect class. So it stays, named for what it is, and no user is asked to pick it.
|
|
32
|
+
*
|
|
33
|
+
* Sizes are NOT restated here — not as a current figure, not as a historical one. Four generations
|
|
34
|
+
* of this comment got them wrong. They live in surface-sizes.test.ts, which reads them off the real
|
|
35
|
+
* surface. (The numbers above are the two measurements that JUSTIFY a decision, which is a different
|
|
36
|
+
* job from documenting the current size; both were taken with a fresh daemon per reading.)
|
|
37
|
+
*/
|
|
38
|
+
export const TOOL_SURFACE = {
|
|
39
|
+
/** What every user gets: the verify loop, plus the 2 meta-tools that reach everything else. */
|
|
40
|
+
DEFAULT: 'default',
|
|
41
|
+
/** Every tool advertised directly, WITH output schemas. A verification switch — see above. */
|
|
42
|
+
ALL: 'all',
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The switch that turns on the full, output-schema-carrying surface. A boolean, not a menu.
|
|
46
|
+
*
|
|
47
|
+
* Named for what it does rather than which "profile" it selects, because the previous name invited
|
|
48
|
+
* users to shop among alternatives that did not meaningfully differ.
|
|
49
|
+
*/
|
|
50
|
+
export const ADVERTISE_ALL_ENV = 'RETICLE_ADVERTISE_ALL_TOOLS';
|
|
51
|
+
/**
|
|
52
|
+
* The retired setting, still read so nobody's shell profile breaks.
|
|
53
|
+
*
|
|
54
|
+
* Every value it ever accepted resolves to something sensible: `full` to the ALL surface, everything
|
|
55
|
+
* else to the default. Silently honouring them would repeat the original sin, so `describeToolSurface`
|
|
56
|
+
* says the setting retired and what was used instead.
|
|
57
|
+
*/
|
|
58
|
+
export const TOOL_PROFILE_ENV = 'RETICLE_TOOL_PROFILE';
|
|
59
|
+
const RETIRED_PROFILE_VALUES = {
|
|
60
|
+
full: TOOL_SURFACE.ALL,
|
|
61
|
+
hybrid: TOOL_SURFACE.DEFAULT,
|
|
62
|
+
core: TOOL_SURFACE.DEFAULT,
|
|
63
|
+
standard: TOOL_SURFACE.DEFAULT,
|
|
64
|
+
dynamic: TOOL_SURFACE.DEFAULT,
|
|
65
|
+
};
|
|
66
|
+
// The set an agent needs to verify a change end-to-end. Tool DEFINITIONS are re-sent every turn, so
|
|
67
|
+
// this set is a per-turn cost, and every name in it has to earn its place.
|
|
68
|
+
//
|
|
69
|
+
// MEASURED per-turn `tools/list` cost, off the real wire (spawn `mcp`, read tools/list, measure the
|
|
70
|
+
// serialized result), with a FRESH DAEMON per reading — the setting is read by the daemon at startup,
|
|
71
|
+
// so a loop that reuses one daemon measures the first surface every time and looks like proof that
|
|
72
|
+
// the setting does nothing. That happened while taking this very reading.
|
|
73
|
+
//
|
|
74
|
+
// default 18,183 B ~4,546 tok/turn 16 tools
|
|
75
|
+
// all 127,903 B ~31,976 tok/turn 48 tools
|
|
76
|
+
//
|
|
77
|
+
// Treat these as the SHAPE of the gap and re-measure before quoting one; counts are asserted in
|
|
78
|
+
// surface-sizes.test.ts, never from this comment.
|
|
79
|
+
//
|
|
80
|
+
// Where the cost sits on the default surface: inputSchema is 76% of the payload (parameter
|
|
81
|
+
// descriptions are half of that), tool descriptions are 12%, outputSchema ~0 because it is dropped.
|
|
82
|
+
// So the next real saving is in parameter prose, not in dropping more tools.
|
|
83
|
+
//
|
|
84
|
+
// There is a floor, though: an 8-tool cut (dropping act/navigate/wait_for/sessions) was MEASURED to
|
|
85
|
+
// regress real-agent accuracy 5/5 -> 3/5, because the model loses scaffolding and wanders on harder
|
|
86
|
+
// flows. Direct network/console stay (far more discoverable than observe-with-filters -> fewer turns,
|
|
87
|
+
// better verdicts).
|
|
88
|
+
//
|
|
89
|
+
// Evidence status: the 5/5 figure came from a single gpt-4o run and is STALE as a justification.
|
|
90
|
+
// Current-model evidence is indirect but real — the cost-delta run (bench/fix-loop/COST-DELTA.md)
|
|
91
|
+
// drove this surface on a current model and fixed 4/4 cells with ~25% FEWER tool calls than the
|
|
92
|
+
// baseline. A formal A/B against a leaner surface on a current model is still UNRUN.
|
|
93
|
+
// See bench/agent-loop-and-replay.md.
|
|
94
|
+
export const CORE_TOOL_NAMES = new Set([
|
|
95
|
+
ReticleTool.SESSIONS,
|
|
96
|
+
ReticleTool.NAVIGATE,
|
|
97
|
+
ReticleTool.SNAPSHOT,
|
|
98
|
+
ReticleTool.QUERY,
|
|
99
|
+
ReticleTool.ACT,
|
|
100
|
+
ReticleTool.ACT_AND_WAIT,
|
|
101
|
+
ReticleTool.OBSERVE,
|
|
102
|
+
ReticleTool.NETWORK,
|
|
103
|
+
ReticleTool.CONSOLE,
|
|
104
|
+
ReticleTool.WAIT_FOR,
|
|
105
|
+
ReticleTool.ASSERT,
|
|
106
|
+
ReticleTool.STATE,
|
|
107
|
+
// INSPECT is what turns a finding into an EDIT: it maps a DOM node to `src/App.tsx:104`. Finding a
|
|
108
|
+
// bug is half the job; knowing which file to open is the half that makes the agent useful, and it
|
|
109
|
+
// is the one capability here with no substitute in any other verification tool. It sat in
|
|
110
|
+
// `standard`, so under the default profile an agent had to already know it existed and reach it
|
|
111
|
+
// through reticle_run — which, observed over a drive of the whole surface, means it never gets called.
|
|
112
|
+
// The measured floor that justifies a lean core was about CUTTING to 8 (accuracy 5/5 → 3/5), not
|
|
113
|
+
// about holding at 12; one tool of schema tax to close the find→fix loop is the right trade.
|
|
114
|
+
ReticleTool.INSPECT,
|
|
115
|
+
// FEEDBACK is in every profile for the same reason INSPECT is: a tool an agent has to already know
|
|
116
|
+
// about, and reach through reticle_run, is a tool that never gets called. That is fatal here in a way
|
|
117
|
+
// it is not elsewhere — an unadvertised feedback channel collects nothing, which is indistinguishable
|
|
118
|
+
// from not having built one. It is also the cheapest tool on the surface to carry (three params) and
|
|
119
|
+
// the only one whose whole purpose is telling us which of the other fifteen are failing.
|
|
120
|
+
ReticleTool.FEEDBACK,
|
|
121
|
+
// SESSION is here because the product ORDERS the agent to call it. The session lease block is
|
|
122
|
+
// spliced onto the first result of every session ("call reticle_session {action:'yield'}"), and
|
|
123
|
+
// the pause hint is spliced onto every refusal while a human has the session paused, where
|
|
124
|
+
// {action:"resume"} is the only exit. Both were naming a tool the default surface did not
|
|
125
|
+
// advertise: an agent that obeyed got `unknown tool`, and an agent that did not left the panel
|
|
126
|
+
// reading "live" after it had stopped driving — which is the state the whole handback protocol
|
|
127
|
+
// exists to prevent.
|
|
128
|
+
//
|
|
129
|
+
// Measured cost: ~1.3 KB of prose (455 B description + ~780 B of parameter descriptions) on a
|
|
130
|
+
// surface whose prose is ~23.7 KB, so roughly +5%, a few hundred tokens a turn. The alternative —
|
|
131
|
+
// deleting the instruction from the lease and the pause hint — deletes the handback protocol,
|
|
132
|
+
// because there is nowhere else those two calls are ever named.
|
|
133
|
+
ReticleTool.SESSION,
|
|
134
|
+
]);
|
|
135
|
+
/** Is the truthy form of a boolean env var set? `1`, `true`, `yes` — anything else is off. */
|
|
136
|
+
function envFlagOn(raw) {
|
|
137
|
+
if (raw === undefined)
|
|
138
|
+
return false;
|
|
139
|
+
const value = raw.trim().toLowerCase();
|
|
140
|
+
return '1' === value || 'true' === value || 'yes' === value || 'on' === value;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Which surface is live.
|
|
144
|
+
*
|
|
145
|
+
* `explicit` is the programmatic override (tests, `advertisedTools` callers). Otherwise the ALL
|
|
146
|
+
* switch decides, and the retired setting is honoured last so an old shell profile still works.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveToolSurface(explicit) {
|
|
149
|
+
if (explicit === TOOL_SURFACE.ALL)
|
|
150
|
+
return TOOL_SURFACE.ALL;
|
|
151
|
+
if (explicit === TOOL_SURFACE.DEFAULT)
|
|
152
|
+
return TOOL_SURFACE.DEFAULT;
|
|
153
|
+
const retiredExplicit = explicit === undefined ? undefined : RETIRED_PROFILE_VALUES[explicit];
|
|
154
|
+
if (retiredExplicit !== undefined)
|
|
155
|
+
return retiredExplicit;
|
|
156
|
+
if (envFlagOn(process.env[ADVERTISE_ALL_ENV]))
|
|
157
|
+
return TOOL_SURFACE.ALL;
|
|
158
|
+
const retiredEnv = RETIRED_PROFILE_VALUES[process.env[TOOL_PROFILE_ENV] ?? ''];
|
|
159
|
+
return retiredEnv ?? TOOL_SURFACE.DEFAULT;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Which surface is live, and what chose it.
|
|
163
|
+
*
|
|
164
|
+
* These settings are read by the DAEMON at startup, never by the client, so exporting one in an
|
|
165
|
+
* agent's environment while a daemon is already running changes nothing at all — which is exactly
|
|
166
|
+
* the observation that produced a "standard and full advertise the same tools" report. Documenting
|
|
167
|
+
* that was not enough; the setting failing to take has to be VISIBLE, so this rides along in the
|
|
168
|
+
* reticle_tools catalog.
|
|
169
|
+
*/
|
|
170
|
+
export function describeToolSurface(active, requested) {
|
|
171
|
+
const retired = requested ?? process.env[TOOL_PROFILE_ENV];
|
|
172
|
+
if (retired !== undefined && retired in RETIRED_PROFILE_VALUES) {
|
|
173
|
+
return {
|
|
174
|
+
active,
|
|
175
|
+
source: `${TOOL_PROFILE_ENV}=${retired} is RETIRED — there is one tool surface now; using '${active}' (set ${ADVERTISE_ALL_ENV}=1 for the full, schema-carrying surface)`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (retired !== undefined && 0 < retired.length) {
|
|
179
|
+
return {
|
|
180
|
+
active,
|
|
181
|
+
source: `${TOOL_PROFILE_ENV} is RETIRED and '${retired}' was never one of its values; using '${active}'`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const flag = process.env[ADVERTISE_ALL_ENV];
|
|
185
|
+
if (envFlagOn(flag)) {
|
|
186
|
+
return { active, source: `${ADVERTISE_ALL_ENV} set in the DAEMON's environment at startup` };
|
|
187
|
+
}
|
|
188
|
+
// "unset" and "set to 0" are different facts, and only one of them means "you did not ask for it".
|
|
189
|
+
// Reporting the second as the first is how somebody spends an afternoon on a switch that IS being
|
|
190
|
+
// read and is simply off.
|
|
191
|
+
return flag === undefined || 0 === flag.length
|
|
192
|
+
? {
|
|
193
|
+
active,
|
|
194
|
+
source: `the one tool surface (${ADVERTISE_ALL_ENV} unset when the daemon started)`,
|
|
195
|
+
}
|
|
196
|
+
: { active, source: `the one tool surface (${ADVERTISE_ALL_ENV}='${flag}' is off)` };
|
|
197
|
+
}
|
|
198
|
+
export function filterTools(tools, surface) {
|
|
199
|
+
// CORE_TOOL_NAMES is what it always really was: the set advertised directly. It was never the
|
|
200
|
+
// interesting thing about the `core` PROFILE, whose only distinction was a second name for this.
|
|
201
|
+
if (surface === TOOL_SURFACE.DEFAULT)
|
|
202
|
+
return tools.filter((t) => CORE_TOOL_NAMES.has(t.name));
|
|
203
|
+
return tools;
|
|
204
|
+
}
|
|
@@ -11,13 +11,13 @@ export function parseInteractive(tree) {
|
|
|
11
11
|
return items;
|
|
12
12
|
}
|
|
13
13
|
export function asString(value) {
|
|
14
|
-
return typeof value
|
|
14
|
+
return 'string' === typeof value ? value : undefined;
|
|
15
15
|
}
|
|
16
16
|
export function asNumber(value) {
|
|
17
|
-
return typeof value
|
|
17
|
+
return 'number' === typeof value ? value : undefined;
|
|
18
18
|
}
|
|
19
19
|
export function asRecord(value) {
|
|
20
|
-
return typeof value
|
|
20
|
+
return 'object' === typeof value && value !== null ? value : {};
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* A `{ file, line }` source location off an untrusted result payload, or undefined.
|
|
@@ -27,15 +27,15 @@ export function asRecord(value) {
|
|
|
27
27
|
* worse than no location at all — it looks like an answer.
|
|
28
28
|
*/
|
|
29
29
|
export function sourceOf(value) {
|
|
30
|
-
if (
|
|
30
|
+
if (null === value || typeof value !== 'object')
|
|
31
31
|
return undefined;
|
|
32
32
|
const record = value;
|
|
33
33
|
const file = record['file'];
|
|
34
34
|
const line = record['line'];
|
|
35
|
-
if (typeof file !== 'string' || file.length
|
|
35
|
+
if (typeof file !== 'string' || 0 === file.length || typeof line !== 'number')
|
|
36
36
|
return undefined;
|
|
37
37
|
const out = { file, line };
|
|
38
|
-
if (typeof record['column']
|
|
38
|
+
if ('number' === typeof record['column'])
|
|
39
39
|
out.column = record['column'];
|
|
40
40
|
return out;
|
|
41
41
|
}
|
package/dist/tools/tools.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
1
|
import { type ToolDef } from './tool-kit.js';
|
|
2
|
+
import { type MergePlan } from './merge-tools.js';
|
|
2
3
|
export type { ToolDef, ToolDeps } from './tool-kit.js';
|
|
4
|
+
/**
|
|
5
|
+
* surface consolidation. Tool defs are re-sent EVERY turn, so the advertised count is a per-turn
|
|
6
|
+
* tax multiplied by loop length. Sibling families collapse into one action-dispatched tool; the member
|
|
7
|
+
* handlers stay defined above and are simply no longer advertised separately.
|
|
8
|
+
*
|
|
9
|
+
* Guardrail (from the plan): the 12-tool core hot-set keeps its EXACT names and shapes and is never
|
|
10
|
+
* merged — sessions/navigate/snapshot/query/act/act_and_wait/observe/network/console/wait_for/assert/state.
|
|
11
|
+
*/
|
|
12
|
+
export declare const MERGE_PLANS: MergePlan[];
|
|
13
|
+
/**
|
|
14
|
+
* Retired from the MCP surface entirely (capability preserved elsewhere, per the plan):
|
|
15
|
+
* - run_record: auto-recording on flow_replay already persists run outcomes.
|
|
16
|
+
*/
|
|
17
|
+
export declare const RETIRED_FROM_SURFACE: string[];
|
|
3
18
|
export declare const TOOLS: ToolDef[];
|
package/dist/tools/tools.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ReticleCommand, SnapshotMode } from '@reticlehq/core';
|
|
2
|
+
import { QueryBy, ReticleCommand, SnapshotMode } from '@reticlehq/core';
|
|
3
3
|
import { ReticleTool } from './tool-names.js';
|
|
4
4
|
import { withSizeCost } from '../session/output-budget.js';
|
|
5
5
|
import { applySnapshotDelta, SnapshotCache } from './snapshot-delta.js';
|
|
6
6
|
import { asString, asNumber } from './tools-helpers.js';
|
|
7
|
+
import { normalizeQueryArgs } from './query-shape.js';
|
|
7
8
|
import { paginateQueryResult } from './query-paginate.js';
|
|
9
|
+
/**
|
|
10
|
+
* The query strategies, derived from the enum in core — never retyped.
|
|
11
|
+
*
|
|
12
|
+
* The description used to hand-list them and had already drifted: it named six and omitted
|
|
13
|
+
* `component`, which is real and works. Deriving both the schema and the prose from one source is
|
|
14
|
+
* the repo's own rule, and here it is also the difference between refusing `by:'css'` and silently
|
|
15
|
+
* answering "0 matches" to it.
|
|
16
|
+
*/
|
|
17
|
+
const QUERY_BY_VALUES = Object.values(QueryBy);
|
|
18
|
+
const QUERY_BY_LIST = QUERY_BY_VALUES.join(' | ');
|
|
19
|
+
const queryByEnum = z.enum(QUERY_BY_VALUES);
|
|
8
20
|
import { CONTRACT_TOOLS } from './contract-tools.js';
|
|
9
21
|
import { DOMAIN_TOOLS } from '../domain/domain-tools.js';
|
|
10
22
|
import { BROWSER_TOOLS } from './browser-tools.js';
|
|
@@ -49,6 +61,10 @@ const RAW_TOOLS = [
|
|
|
49
61
|
title: z.string().optional(),
|
|
50
62
|
adapters: z.array(z.string()),
|
|
51
63
|
hasCapabilities: z.boolean(),
|
|
64
|
+
versionSkew: z
|
|
65
|
+
.string()
|
|
66
|
+
.optional()
|
|
67
|
+
.describe("Present only when this page's SDK version differs from the daemon's. A skewed pair connects and then disagrees about tool behaviour — usually surfacing as a bare -32000. Fix it before trusting any verdict from this session."),
|
|
52
68
|
lastSeenMs: z.number(),
|
|
53
69
|
throttled: z.boolean(),
|
|
54
70
|
focused: z.boolean(),
|
|
@@ -151,19 +167,24 @@ const RAW_TOOLS = [
|
|
|
151
167
|
sessionId: resolved.id,
|
|
152
168
|
scope: asString(args['scope']) ?? '',
|
|
153
169
|
mode,
|
|
154
|
-
diff: args['diff']
|
|
170
|
+
diff: true === args['diff'],
|
|
155
171
|
}, SNAPSHOT_CACHE)));
|
|
156
172
|
},
|
|
157
173
|
},
|
|
158
174
|
{
|
|
159
175
|
name: ReticleTool.QUERY,
|
|
160
176
|
example: { by: 'testid', value: 'todo-list' },
|
|
161
|
-
description:
|
|
177
|
+
description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot.`,
|
|
162
178
|
inputSchema: {
|
|
163
|
-
|
|
179
|
+
// Constrained to the enum, NOT z.string(). A free string let `by:'css'` through to the
|
|
180
|
+
// browser's `default: return []`, so an unsupported strategy answered "0 matches" — which
|
|
181
|
+
// reads as "the element is not on the page". Measured on a live page: by:'css' value:'body'
|
|
182
|
+
// returned count 0. A false negative is the one answer this product must never invent.
|
|
183
|
+
by: queryByEnum.optional().describe(`Query strategy: ${QUERY_BY_LIST}`),
|
|
164
184
|
value: z
|
|
165
185
|
.string()
|
|
166
|
-
.
|
|
186
|
+
.optional()
|
|
187
|
+
.describe('Query value for the selected strategy (e.g. by=role value=button, or by=testid value=submit-btn). Or use the predicate spelling directly: { testid: "submit" }, { text: "Deploy" }.'),
|
|
167
188
|
name: z
|
|
168
189
|
.string()
|
|
169
190
|
.optional()
|
|
@@ -185,6 +206,14 @@ const RAW_TOOLS = [
|
|
|
185
206
|
.boolean()
|
|
186
207
|
.optional()
|
|
187
208
|
.describe('Return just { count } (no element descriptors) — use when you only need "how many match?" and not their refs.'),
|
|
209
|
+
// The predicate's spelling, accepted as an alias for by/value so the shape an agent learns from
|
|
210
|
+
// act_and_wait/assert also works here. See query-shape.ts.
|
|
211
|
+
testid: z.string().optional().describe('Alias for by="testid" value=… (predicate spelling).'),
|
|
212
|
+
text: z.string().optional().describe('Alias for by="text" value=… (predicate spelling).'),
|
|
213
|
+
role: z.string().optional().describe('Alias for by="role" value=… (predicate spelling).'),
|
|
214
|
+
label: z.string().optional().describe('Alias for by="label" value=… (predicate spelling).'),
|
|
215
|
+
placeholder: z.string().optional().describe('Alias for by="placeholder" value=… .'),
|
|
216
|
+
alt: z.string().optional().describe('Alias for by="alt" value=… (predicate spelling).'),
|
|
188
217
|
...sessionIdShape,
|
|
189
218
|
},
|
|
190
219
|
outputSchema: {
|
|
@@ -257,16 +286,20 @@ const RAW_TOOLS = [
|
|
|
257
286
|
.optional()
|
|
258
287
|
.describe('True when a `scope` was given but resolved to nothing — zero matches then means the scope is gone, NOT that the element is absent. The search did not widen to the whole page. Re-check the scope before concluding anything.'),
|
|
259
288
|
},
|
|
260
|
-
handler: (deps,
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
289
|
+
handler: (deps, rawArgs) => {
|
|
290
|
+
// Accept the predicate's named-field spelling too — see query-shape.ts.
|
|
291
|
+
const args = normalizeQueryArgs(rawArgs);
|
|
292
|
+
return commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.QUERY, {
|
|
293
|
+
by: args['by'],
|
|
294
|
+
value: args['value'],
|
|
295
|
+
name: args['name'],
|
|
296
|
+
scope: args['scope'],
|
|
297
|
+
// The handler forwards an explicit allowlist, so a new input is silently dropped unless it is
|
|
298
|
+
// added here — the browser saw no `attrs` and returned undefined while the unit tests, which
|
|
299
|
+
// call matchQuery directly, passed. Schema plus implementation is not the whole wire.
|
|
300
|
+
attrs: args['attrs'],
|
|
301
|
+
}).then((result) => withSizeCost(paginateQueryResult(result, asNumber(args['limit']), true === args['count_only'])));
|
|
302
|
+
},
|
|
270
303
|
},
|
|
271
304
|
{
|
|
272
305
|
name: ReticleTool.INSPECT,
|
|
@@ -383,7 +416,7 @@ const RAW_TOOLS = [
|
|
|
383
416
|
* Guardrail (from the plan): the 12-tool core hot-set keeps its EXACT names and shapes and is never
|
|
384
417
|
* merged — sessions/navigate/snapshot/query/act/act_and_wait/observe/network/console/wait_for/assert/state.
|
|
385
418
|
*/
|
|
386
|
-
const MERGE_PLANS = [
|
|
419
|
+
export const MERGE_PLANS = [
|
|
387
420
|
{
|
|
388
421
|
name: ReticleTool.BASELINE,
|
|
389
422
|
description: 'Semantic-state baselines: { action: "save" } snapshots the current state under a name, "list" returns saved names, "diff" compares the live page against a saved baseline (REMOVED/ADDED elements + console-error count).',
|
|
@@ -419,6 +452,9 @@ const MERGE_PLANS = [
|
|
|
419
452
|
review: ReticleTool.REVIEW,
|
|
420
453
|
narrate: ReticleTool.NARRATE,
|
|
421
454
|
},
|
|
455
|
+
// The handback the lease block orders on every session — the one call an agent MUST make, so it
|
|
456
|
+
// is the one that must not need a schema lookup first.
|
|
457
|
+
example: { action: 'yield', mode: 'waiting' },
|
|
422
458
|
},
|
|
423
459
|
{
|
|
424
460
|
name: ReticleTool.LEASE,
|
|
@@ -430,7 +466,7 @@ const MERGE_PLANS = [
|
|
|
430
466
|
* Retired from the MCP surface entirely (capability preserved elsewhere, per the plan):
|
|
431
467
|
* - run_record: auto-recording on flow_replay already persists run outcomes.
|
|
432
468
|
*/
|
|
433
|
-
const RETIRED_FROM_SURFACE = [
|
|
469
|
+
export const RETIRED_FROM_SURFACE = [
|
|
434
470
|
ReticleTool.RUN_RECORD, // auto-recording on flow_replay already persists run outcomes
|
|
435
471
|
ReticleTool.REFRESH, // absorbed into reticle_navigate { reload: true }
|
|
436
472
|
ReticleTool.WAIT_READY, // server-internal: the first live call already blocks for the session
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guidance for `name`, or undefined when there is nothing useful to add — the tool IS advertised (so
|
|
3
|
+
* any error belongs to the call itself), or the name is not ours to explain.
|
|
4
|
+
*/
|
|
5
|
+
export declare function unadvertisedToolHelp(name: string, advertised: ReadonlySet<string>, known: ReadonlySet<string>): string | undefined;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to say when an agent calls a real Reticle tool that this profile does not advertise.
|
|
3
|
+
*
|
|
4
|
+
* The default profile advertises 16 of 46 tools. That is deliberate — every advertised tool's schema
|
|
5
|
+
* is re-sent on every turn, and the trim is most of why `hybrid` is cheap — and the other 30 stay
|
|
6
|
+
* fully callable through `reticle_run`, which `profile-reachability.test.ts` guards.
|
|
7
|
+
*
|
|
8
|
+
* The gap was in the ERROR. The MCP SDK answers `Tool <name> not found`, which is indistinguishable
|
|
9
|
+
* from "this tool does not exist", so an agent that trusts it stops trying. Reported from a real
|
|
10
|
+
* sweep of a Next app-router project: the first pass scored 25 failures that were nothing of the
|
|
11
|
+
* kind — every one of those tools worked through `reticle_run` seconds later.
|
|
12
|
+
*
|
|
13
|
+
* So: a name Reticle owns never comes back as "not found". It comes back with the call that works
|
|
14
|
+
* and the switch that makes it stop being necessary.
|
|
15
|
+
*/
|
|
16
|
+
import { ReticleTool } from './tool-names.js';
|
|
17
|
+
import { TOOL_SURFACE, TOOL_PROFILE_ENV } from './tool-surface.js';
|
|
18
|
+
import { mergedNameRedirect, mergedNameMessage } from './merged-name-redirect.js';
|
|
19
|
+
/**
|
|
20
|
+
* Guidance for `name`, or undefined when there is nothing useful to add — the tool IS advertised (so
|
|
21
|
+
* any error belongs to the call itself), or the name is not ours to explain.
|
|
22
|
+
*/
|
|
23
|
+
export function unadvertisedToolHelp(name, advertised, known) {
|
|
24
|
+
if (advertised.has(name))
|
|
25
|
+
return undefined;
|
|
26
|
+
// A name that MOVED gets the move, not a profile lecture — it is not un-advertised, it is gone.
|
|
27
|
+
const moved = mergedNameRedirect(name);
|
|
28
|
+
if (moved !== undefined)
|
|
29
|
+
return mergedNameMessage(name, moved);
|
|
30
|
+
if (!known.has(name))
|
|
31
|
+
return undefined;
|
|
32
|
+
return (`${name} exists and works, but is not advertised under this tool profile — the schemas for all ` +
|
|
33
|
+
`tools are re-sent every turn, so the default advertises a subset and keeps the rest one call ` +
|
|
34
|
+
`away. It is NOT missing: invoke it with ` +
|
|
35
|
+
`${ReticleTool.RUN} { tool: "${name}", args: { ... } }. ` +
|
|
36
|
+
`Call ${ReticleTool.TOOLS} { names: ["${name}"] } for its parameters. ` +
|
|
37
|
+
`If you need it repeatedly, set ${TOOL_PROFILE_ENV}=${TOOL_SURFACE.ALL} to advertise ` +
|
|
38
|
+
`every tool directly.`);
|
|
39
|
+
}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verbose internal flow tracing, for people working ON Reticle.
|
|
3
|
+
*
|
|
4
|
+
* The gap it fills: the journal records what the AGENT did to the app, and telemetry records
|
|
5
|
+
* aggregates. Neither says which of OUR code a tool call went through, in what order, or where the
|
|
6
|
+
* time went — so every "why is this flow slow" and "which path produced this verdict" was
|
|
7
|
+
* reconstructed by hand from reading source. One line per stage answers both.
|
|
8
|
+
*
|
|
9
|
+
* Design, in order of what it cost to get wrong elsewhere in this repo:
|
|
10
|
+
*
|
|
11
|
+
* - OFF by default, and ~126ns per site when off (measured) — `span` calls the function and
|
|
12
|
+
* returns. A trace on every tool call is a real cost on the hot path, and the hot path is a
|
|
13
|
+
* verification loop of 50–200 calls.
|
|
14
|
+
* - ONE line per stage, emitted when it ENDS, carrying its own duration. A start line as well
|
|
15
|
+
* would double the volume to say something the end line already implies.
|
|
16
|
+
* - Correlated by a call id and a depth, carried in AsyncLocalStorage rather than threaded through
|
|
17
|
+
* every signature. Several agents are inside `runTool` at once — interleaving is the normal case
|
|
18
|
+
* — so without an id the output is a pile of unrelated lines, and instrumenting anything would
|
|
19
|
+
* mean changing its parameters, which is how instrumentation stops being added.
|
|
20
|
+
* - Through the existing `log()`, so it lands in the same stderr stream the daemon already
|
|
21
|
+
* redirects to `~/.reticle/daemon-<port>.log`. No new sink, no new file, nothing else to tail.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Whether tracing is on.
|
|
25
|
+
*
|
|
26
|
+
* Read per call, not cached at import, and the reason is `loadDotEnv`: the CLI populates
|
|
27
|
+
* `process.env` from the project's .env AFTER every module has been imported, so a value captured at
|
|
28
|
+
* import time would miss a `RETICLE_TRACE` set there — silently, which is the worst way for a
|
|
29
|
+
* diagnostic switch to fail. (The reason originally given here — "so it can be turned on while the
|
|
30
|
+
* daemon runs" — was wrong: nothing outside a process can change its environment.)
|
|
31
|
+
*
|
|
32
|
+
* Measured cost of reading it per call: 126ns per traced site when tracing is OFF, against a ~9ns
|
|
33
|
+
* bare call. With a handful of spans per tool call that is under a microsecond on a call that takes
|
|
34
|
+
* milliseconds — cheap enough to be worth the correctness, and not the literal zero this comment
|
|
35
|
+
* used to imply.
|
|
36
|
+
*/
|
|
37
|
+
export declare function traceEnabled(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Bind a callback to the call that is running NOW, so it reports inside that call when it fires later.
|
|
40
|
+
*
|
|
41
|
+
* `waitForPredicate` re-checks its predicate from a WebSocket event listener and an interval. Neither
|
|
42
|
+
* is in the awaited chain, so AsyncLocalStorage does not reach them and every re-check used to open a
|
|
43
|
+
* brand new call at depth 0. Measured on one healthy run: 165 spans across 60 callIds, 25 of them a
|
|
44
|
+
* `browser.command` with no matching `tool.handler`.
|
|
45
|
+
*
|
|
46
|
+
* That is not cosmetic. The documented signature of a HUNG tool call is exactly "a browser.command
|
|
47
|
+
* with no tool.handler for that callId, ever" — the invariant that located a real hang. A clean run
|
|
48
|
+
* emitting 23 of them makes the invariant unusable and hides the next hang in its own noise.
|
|
49
|
+
*
|
|
50
|
+
* Returns `fn` unchanged when there is no call to bind to, so a caller outside a span is not given a
|
|
51
|
+
* fabricated parent.
|
|
52
|
+
*/
|
|
53
|
+
export declare function bindSpanContext<A extends unknown[]>(fn: (...args: A) => void): (...args: A) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Run `fn` as a named stage, recording how long it took and how it sat inside the call around it.
|
|
56
|
+
*
|
|
57
|
+
* Wrap a stage worth a line in a bottleneck hunt — a session resolve, a browser round-trip, a
|
|
58
|
+
* predicate evaluation — not every function. The value of the trace is that reading it is faster
|
|
59
|
+
* than reading the code, and that stops being true somewhere around one line per statement.
|
|
60
|
+
*/
|
|
61
|
+
export declare function span<T>(name: string, fields: Record<string, unknown>, fn: () => Promise<T> | T): Promise<T>;
|
|
62
|
+
/**
|
|
63
|
+
* The synchronous form. Same line, same tree, same cost when off.
|
|
64
|
+
*
|
|
65
|
+
* It exists because `reticle init` — the flow every user hits before anything else, and the one
|
|
66
|
+
* whose slowness they experience personally — is synchronous end to end. An async-only span left
|
|
67
|
+
* exactly that flow untraceable.
|
|
68
|
+
*/
|
|
69
|
+
export declare function spanSync<T>(name: string, fields: Record<string, unknown>, fn: () => T): T;
|