@reticlehq/server 2.4.0 → 2.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/SKILL.md +719 -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} +13 -1
- package/dist/{bridge.js → bridge/bridge.js} +95 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
- 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/cli-doctor.d.ts +1 -0
- package/dist/cli/cli-doctor.js +135 -0
- 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/cli-parse-feedback.d.ts +31 -0
- package/dist/cli/cli-parse-feedback.js +56 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
- package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
- 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/doctor-daemon-line.d.ts +37 -0
- package/dist/cli/doctor-daemon-line.js +35 -0
- package/dist/cli/port-holder.d.ts +43 -0
- package/dist/cli/port-holder.js +99 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +302 -100
- 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 +73 -0
- package/dist/daemon/daemon-resilience.js +279 -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} +105 -48
- package/dist/daemon/heartbeat.d.ts +115 -0
- package/dist/daemon/heartbeat.js +141 -0
- 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} +23 -2
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
- package/dist/daemon/port-presence.d.ts +76 -0
- package/dist/daemon/port-presence.js +93 -0
- package/dist/daemon/state-dir.d.ts +7 -0
- package/dist/daemon/state-dir.js +59 -0
- package/dist/daemon/unclean-predecessor.d.ts +23 -0
- package/dist/daemon/unclean-predecessor.js +27 -0
- 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 +53 -20
- package/dist/events/predicate-eval.js +179 -55
- package/dist/events/predicate-parse.d.ts +23 -0
- package/dist/events/predicate-parse.js +54 -0
- package/dist/events/predicate.d.ts +7 -2
- package/dist/events/predicate.js +126 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +2 -17
- 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 +6 -8
- package/dist/flows/flow-replay.js +132 -48
- 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 +156 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +63 -21
- 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/role-drift-reason.d.ts +20 -0
- package/dist/flows/role-drift-reason.js +39 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/suite-flakes.d.ts +33 -0
- package/dist/flows/suite-flakes.js +32 -0
- 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 +35 -3
- package/dist/honesty/blind-spots.js +57 -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 +31 -3
- package/dist/honesty/verified.js +64 -8
- package/dist/http-server.js +23 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +9 -6
- package/dist/index.js +41 -25
- package/dist/init/agent-rules.d.ts +20 -1
- package/dist/init/agent-rules.js +64 -8
- 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 +50 -0
- package/dist/init/cra.js +106 -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 +9 -2
- package/dist/init/detect.js +24 -5
- package/dist/init/mcp-clients.d.ts +81 -0
- package/dist/init/mcp-clients.js +248 -0
- 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 +29 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +166 -8
- package/dist/init/plan.d.ts +85 -6
- package/dist/init/plan.js +196 -33
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +23 -1
- package/dist/init/run.js +338 -47
- 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/windows-quote.d.ts +38 -0
- package/dist/init/windows-quote.js +70 -0
- 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 +32 -0
- package/dist/mcp/mcp-outage.js +55 -0
- package/dist/mcp/mcp-proxy.d.ts +134 -0
- package/dist/mcp/mcp-proxy.js +720 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +139 -37
- 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/browser-pool.d.ts +9 -0
- package/dist/pool/browser-pool.js +38 -9
- 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/attachment-history.d.ts +46 -0
- package/dist/session/attachment-history.js +65 -0
- 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 +45 -0
- package/dist/session/no-session-diagnosis.js +120 -0
- package/dist/session/no-session-watch.d.ts +21 -0
- package/dist/session/no-session-watch.js +69 -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/pending-commands.js +1 -0
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +43 -8
- package/dist/session/session-info.js +46 -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 +6 -0
- package/dist/session/session-manager.js +124 -12
- 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 +15 -2
- package/dist/session/session.js +77 -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/crash-cause.d.ts +47 -0
- package/dist/telemetry/crash-cause.js +104 -0
- package/dist/telemetry/daemon-telemetry.d.ts +38 -1
- package/dist/telemetry/daemon-telemetry.js +37 -12
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-class.d.ts +26 -0
- package/dist/telemetry/error-class.js +52 -0
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +8 -3
- package/dist/telemetry/feedback-cli.js +52 -8
- package/dist/telemetry/feedback-context.d.ts +23 -6
- package/dist/telemetry/feedback-context.js +105 -5
- 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.d.ts +7 -0
- package/dist/telemetry/git-facts.js +5 -5
- package/dist/telemetry/project-profile.js +2 -1
- package/dist/telemetry/session-metrics.d.ts +47 -3
- package/dist/telemetry/session-metrics.js +315 -15
- package/dist/telemetry/telemetry.d.ts +45 -2
- package/dist/telemetry/telemetry.js +150 -37
- 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 +114 -118
- 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 +9 -1
- package/dist/tools/assert-verdict.js +21 -6
- package/dist/tools/browser-tools.js +36 -11
- package/dist/tools/contract-tools.d.ts +0 -5
- package/dist/tools/contract-tools.js +27 -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 +96 -19
- package/dist/tools/error-recovery.d.ts +34 -2
- package/dist/tools/error-recovery.js +255 -4
- package/dist/tools/feedback-invite.d.ts +48 -0
- package/dist/tools/feedback-invite.js +71 -0
- 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 -12
- package/dist/tools/invoke-tool.js +126 -33
- 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-arrival.d.ts +26 -0
- package/dist/tools/navigate-arrival.js +60 -0
- package/dist/tools/navigate-result.d.ts +31 -0
- package/dist/tools/navigate-result.js +38 -0
- package/dist/tools/observe-tools.js +72 -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 +93 -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 +16 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +217 -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/README.md +55 -0
- 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-dispatch-receiver.yml +76 -0
- package/docs/fixtures.md +45 -0
- package/docs/flows.md +166 -0
- package/docs/gate-plan.md +98 -0
- package/docs/gates.md +117 -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/matrix/2.5.0/claude-code-machine.json +19 -0
- package/docs/matrix/2.5.0/codex-machine.json +20 -0
- package/docs/matrix/2.5.0/cursor-machine.json +22 -0
- package/docs/matrix/2.5.0/gemini-machine.json +22 -0
- package/docs/matrix/2.5.0/opencode-machine.json +22 -0
- package/docs/matrix/2.5.0/vscode-machine.json +22 -0
- package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
- package/docs/matrix/MATRIX.md +26 -0
- package/docs/matrix/README.md +59 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/system-map.md +152 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +191 -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-doctor.d.ts +0 -6
- package/dist/cli-doctor.js +0 -60
- 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-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
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { navigateResult } from './navigate-result.js';
|
|
3
|
+
import { awaitArrival } from './navigate-arrival.js';
|
|
4
|
+
import { reloadResult } from './reload-result.js';
|
|
5
|
+
import { waitForReconnect, RELOAD_RECONNECT_TIMEOUT_MS } from '../session/session-reconnect.js';
|
|
2
6
|
import { ReticleCommand } from '@reticlehq/core';
|
|
3
7
|
import { ReticleTool } from './tool-names.js';
|
|
4
8
|
import { asString } from './tools-helpers.js';
|
|
@@ -7,7 +11,7 @@ export const BROWSER_TOOLS = [
|
|
|
7
11
|
{
|
|
8
12
|
name: ReticleTool.NAVIGATE,
|
|
9
13
|
example: { url: '/settings' },
|
|
10
|
-
description: 'Navigate the connected browser tab to a URL, or reload it in place with { reload: true } (add { hard: true } to bypass the cache).
|
|
14
|
+
description: 'Navigate the connected browser tab to a URL, or reload it in place with { reload: true } (add { hard: true } to bypass the cache). `ok` means the navigation was DISPATCHED — the SDK is torn down by the navigation, so the page itself cannot report on it. The daemon then waits briefly for the SDK to reconnect: `confirmed:true` with a new `sessionId` means the page arrived and you can act immediately. `confirmed:false` means it did not arrive within the window — the page may be slow, uninstrumented, or not there; check reticle_sessions before acting.',
|
|
11
15
|
inputSchema: {
|
|
12
16
|
url: z.string().optional().describe('The URL to navigate to. Omit when using reload.'),
|
|
13
17
|
reload: z
|
|
@@ -24,17 +28,38 @@ export const BROWSER_TOOLS = [
|
|
|
24
28
|
ok: z.boolean(),
|
|
25
29
|
url: z.string().optional(),
|
|
26
30
|
reason: z.string().optional(),
|
|
31
|
+
// Present on a dispatched navigation: nothing here can see the new document, so arrival is
|
|
32
|
+
// reported as unconfirmed rather than implied by `ok`. See navigate-result.ts.
|
|
33
|
+
confirmed: z.boolean().optional(),
|
|
34
|
+
/** The session the SDK reconnected as, when arrival was confirmed — it is a NEW id. */
|
|
35
|
+
sessionId: z.string().optional(),
|
|
36
|
+
note: z.string().optional(),
|
|
27
37
|
},
|
|
28
38
|
handler: async (deps, args) => {
|
|
29
39
|
// reload:true is the absorbed reticle_refresh — same command, one fewer advertised tool.
|
|
30
|
-
if (args['reload']
|
|
40
|
+
if (true === args['reload']) {
|
|
41
|
+
const before = deps.sessions.resolve(asString(args['sessionId']));
|
|
31
42
|
await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.REFRESH, {
|
|
32
|
-
hard: args['hard']
|
|
43
|
+
hard: true === args['hard'],
|
|
33
44
|
});
|
|
34
|
-
|
|
45
|
+
// WAIT for the page to come back, rather than telling the agent to. The id survives the
|
|
46
|
+
// reload, but the seconds between dispatch and the new HELLO are seconds in which every call
|
|
47
|
+
// lands in the old, disconnected session — measured as reticle_run failing 5 of 5 on a page
|
|
48
|
+
// that was healthy immediately afterwards. Returns on the first poll in the common case.
|
|
49
|
+
const back = await waitForReconnect({
|
|
50
|
+
current: () => deps.sessions.get(before.id),
|
|
51
|
+
previous: before,
|
|
52
|
+
timeoutMs: RELOAD_RECONNECT_TIMEOUT_MS,
|
|
53
|
+
now: deps.now,
|
|
54
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
55
|
+
});
|
|
56
|
+
// Not a bare `{ ok: true }`. The URL branch below already discloses that `ok` means
|
|
57
|
+
// DISPATCHED — the reload branch had identical semantics and said nothing, on the path most
|
|
58
|
+
// likely to need it. See reload-result.
|
|
59
|
+
return reloadResult(back);
|
|
35
60
|
}
|
|
36
61
|
const url = asString(args['url']);
|
|
37
|
-
if (url === undefined || url.length
|
|
62
|
+
if (url === undefined || 0 === url.length)
|
|
38
63
|
return { ok: false, reason: 'url required' };
|
|
39
64
|
// Record navigate as an action. Its window is usually empty (the page unloads and the SDK
|
|
40
65
|
// reconnects), but the action record itself — "navigated to X" — is the causal fact worth keeping.
|
|
@@ -42,11 +67,11 @@ export const BROWSER_TOOLS = [
|
|
|
42
67
|
session.beginAction(ReticleTool.NAVIGATE, { url });
|
|
43
68
|
try {
|
|
44
69
|
const result = (await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.NAVIGATE, { url }));
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
70
|
+
// `ok` is the browser accepting the instruction, not the page arriving — see navigate-result.
|
|
71
|
+
// The daemon is the only party that CAN see arrival (the SDK reconnects to it), so it looks,
|
|
72
|
+
// briefly, instead of telling the agent to go poll reticle_sessions itself.
|
|
73
|
+
const arrival = true === result.ok ? await awaitArrival(deps.sessions, url) : null;
|
|
74
|
+
return navigateResult(result, arrival);
|
|
50
75
|
}
|
|
51
76
|
finally {
|
|
52
77
|
session.finishAction();
|
|
@@ -68,7 +93,7 @@ export const BROWSER_TOOLS = [
|
|
|
68
93
|
},
|
|
69
94
|
handler: async (deps, args) => {
|
|
70
95
|
await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.REFRESH, {
|
|
71
|
-
hard: args['hard']
|
|
96
|
+
hard: true === args['hard'],
|
|
72
97
|
});
|
|
73
98
|
return { ok: true };
|
|
74
99
|
},
|
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
import type { ToolDef } from './tools.js';
|
|
2
|
-
/**
|
|
3
|
-
* The capability-contract tools. `reticle_capabilities` reads the live session, or the
|
|
4
|
-
* git-checked `.reticle/contract.json` when `{ fromDisk:true }`; `reticle_contract_save` persists the
|
|
5
|
-
* live registry to that file (pretty-printed, stable key order — diffable in PRs).
|
|
6
|
-
*/
|
|
7
2
|
export declare const CONTRACT_TOOLS: ToolDef[];
|
|
@@ -9,6 +9,31 @@ import { reticleDirPaths, readContract, writeContract } from '../project/reticle
|
|
|
9
9
|
* git-checked `.reticle/contract.json` when `{ fromDisk:true }`; `reticle_contract_save` persists the
|
|
10
10
|
* live registry to that file (pretty-printed, stable key order — diffable in PRs).
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* Refuse to persist one project's surface into another project's repo.
|
|
14
|
+
*
|
|
15
|
+
* `writeContract` writes to the DAEMON's `.reticle/`, which is the directory the daemon was started
|
|
16
|
+
* in — not the session's project. A daemon started above several apps therefore saves app A's
|
|
17
|
+
* testids into app B's checkout, reports `saved: true`, and hands back a path that looks right. A
|
|
18
|
+
* contract is git-checked and diffable, so that is a change somebody commits.
|
|
19
|
+
*
|
|
20
|
+
* Refuse rather than redirect: a session advertises a projectId, not a project DIRECTORY, so there
|
|
21
|
+
* is no honest way to work out where the correct `.reticle/` lives. Naming both ids and stopping is
|
|
22
|
+
* the whole of what can be said truthfully.
|
|
23
|
+
*
|
|
24
|
+
* Silence is not a mismatch. An unstamped build carries no projectId, and a daemon above an
|
|
25
|
+
* uninitialised directory has none either; neither is evidence of two projects.
|
|
26
|
+
*/
|
|
27
|
+
function assertSameProject(deps, sessionId) {
|
|
28
|
+
const mine = deps.projectId;
|
|
29
|
+
const theirs = deps.sessions.resolve(sessionId).projectId;
|
|
30
|
+
if (mine === undefined || theirs === undefined || mine === theirs)
|
|
31
|
+
return;
|
|
32
|
+
throw new Error(`refusing to save: this session belongs to project '${theirs}', but this daemon writes to ` +
|
|
33
|
+
`project '${mine}' (.reticle/ in the directory it was started in). Saving would put one ` +
|
|
34
|
+
"app's testable surface into another app's git-checked contract. Run a daemon from " +
|
|
35
|
+
`'${theirs}'s own directory and save there.`);
|
|
36
|
+
}
|
|
12
37
|
export const CONTRACT_TOOLS = [
|
|
13
38
|
{
|
|
14
39
|
name: ReticleTool.CAPABILITIES,
|
|
@@ -29,7 +54,7 @@ export const CONTRACT_TOOLS = [
|
|
|
29
54
|
governance: z.unknown().optional(),
|
|
30
55
|
},
|
|
31
56
|
handler: async (deps, args) => {
|
|
32
|
-
if (args[FROM_DISK_ARG]
|
|
57
|
+
if (true === args[FROM_DISK_ARG]) {
|
|
33
58
|
const r = await readContract(deps.fs, deps.reticleRoot);
|
|
34
59
|
if (!r.ok)
|
|
35
60
|
throw new Error(r.reason === ContractReadError.MISSING
|
|
@@ -54,6 +79,7 @@ export const CONTRACT_TOOLS = [
|
|
|
54
79
|
handler: async (deps, args) => {
|
|
55
80
|
const res = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.CAPABILITIES, {});
|
|
56
81
|
const caps = CapabilitiesSchema.parse(res);
|
|
82
|
+
assertSameProject(deps, asString(args['sessionId']));
|
|
57
83
|
await writeContract(deps.fs, deps.reticleRoot, caps, deps.now);
|
|
58
84
|
return {
|
|
59
85
|
saved: true,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which controls has the agent actually driven — across re-renders.
|
|
3
|
+
*
|
|
4
|
+
* Coverage was counted by REF, and a ref is invalidated whenever the DOM changes. Reported from a
|
|
5
|
+
* sweep of a Next app-router project: `total: 34, exercised: 0` after four successful acts. App
|
|
6
|
+
* router replaces a whole route segment on navigation, so every control the agent drove is a new
|
|
7
|
+
* element by the time coverage is asked. The number is then 0 forever on any framework that replaces
|
|
8
|
+
* nodes rather than reconciling in place — and an agent told it has exercised none of 34 controls
|
|
9
|
+
* re-drives ground it already covered, which is the exact opposite of what this tool is for.
|
|
10
|
+
*
|
|
11
|
+
* Bench-app hid it: it reconciles in place, so refs survive there and the number looked right.
|
|
12
|
+
*
|
|
13
|
+
* The LABEL is the identity that survives a re-render — `button "Deploy"` names the same control
|
|
14
|
+
* before and after — and it is already parsed for the untouched list.
|
|
15
|
+
*
|
|
16
|
+
* One caveat, stated because it is a real limitation: two controls sharing a label are
|
|
17
|
+
* indistinguishable here, so driving one marks both. That errs toward "covered", which is the
|
|
18
|
+
* unsafe direction for a coverage number — but it is bounded to same-labelled controls, and the
|
|
19
|
+
* alternative it replaces reported ZERO coverage on entire frameworks.
|
|
20
|
+
*/
|
|
21
|
+
interface CoverageControl {
|
|
22
|
+
ref: string;
|
|
23
|
+
label: string;
|
|
24
|
+
}
|
|
25
|
+
interface CoverageTally {
|
|
26
|
+
/** Controls on the page the agent has driven, by ref or by surviving label. */
|
|
27
|
+
exercised: number;
|
|
28
|
+
/** Controls the agent drove that are on the page no longer, and no label explains. */
|
|
29
|
+
droveGone: number;
|
|
30
|
+
/** The controls still untouched, in document order. */
|
|
31
|
+
untouched: CoverageControl[];
|
|
32
|
+
}
|
|
33
|
+
export declare function exercisedCount(controls: readonly CoverageControl[], actedRefs: ReadonlySet<string>, actedLabels: ReadonlySet<string>): CoverageTally;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which controls has the agent actually driven — across re-renders.
|
|
3
|
+
*
|
|
4
|
+
* Coverage was counted by REF, and a ref is invalidated whenever the DOM changes. Reported from a
|
|
5
|
+
* sweep of a Next app-router project: `total: 34, exercised: 0` after four successful acts. App
|
|
6
|
+
* router replaces a whole route segment on navigation, so every control the agent drove is a new
|
|
7
|
+
* element by the time coverage is asked. The number is then 0 forever on any framework that replaces
|
|
8
|
+
* nodes rather than reconciling in place — and an agent told it has exercised none of 34 controls
|
|
9
|
+
* re-drives ground it already covered, which is the exact opposite of what this tool is for.
|
|
10
|
+
*
|
|
11
|
+
* Bench-app hid it: it reconciles in place, so refs survive there and the number looked right.
|
|
12
|
+
*
|
|
13
|
+
* The LABEL is the identity that survives a re-render — `button "Deploy"` names the same control
|
|
14
|
+
* before and after — and it is already parsed for the untouched list.
|
|
15
|
+
*
|
|
16
|
+
* One caveat, stated because it is a real limitation: two controls sharing a label are
|
|
17
|
+
* indistinguishable here, so driving one marks both. That errs toward "covered", which is the
|
|
18
|
+
* unsafe direction for a coverage number — but it is bounded to same-labelled controls, and the
|
|
19
|
+
* alternative it replaces reported ZERO coverage on entire frameworks.
|
|
20
|
+
*/
|
|
21
|
+
export function exercisedCount(controls, actedRefs, actedLabels) {
|
|
22
|
+
// An empty label is not an identity — anonymous controls would otherwise collapse into one bucket
|
|
23
|
+
// and a single act would mark every one of them driven.
|
|
24
|
+
// Matched on the whole label (`button "Save"`) OR on a testid the label contains. The snapshot
|
|
25
|
+
// renders a control's testid inside its label, and a testid is the identity most likely to survive
|
|
26
|
+
// a re-render — matching only the full label meant a control with a testid but no accessible name
|
|
27
|
+
// was never recognised, and coverage read `exercised: 0` after real work.
|
|
28
|
+
const droveLabel = (label) => {
|
|
29
|
+
if (0 === label.length)
|
|
30
|
+
return false;
|
|
31
|
+
if (actedLabels.has(label))
|
|
32
|
+
return true;
|
|
33
|
+
for (const acted of actedLabels) {
|
|
34
|
+
if (acted.length > 0 && !acted.includes('"') && label.includes(acted))
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
const untouched = controls.filter((control) => !actedRefs.has(control.ref) && !droveLabel(control.label));
|
|
40
|
+
// A driven ref that is not on the page is only "gone" if no present control carries its label —
|
|
41
|
+
// otherwise it is the same control after a re-render, which is the case this exists to fix.
|
|
42
|
+
const presentRefs = new Set(controls.map((control) => control.ref));
|
|
43
|
+
const explainedByLabel = new Set(controls.filter((control) => droveLabel(control.label)).map((control) => control.label));
|
|
44
|
+
const droveGone = [...actedRefs].filter((ref) => !presentRefs.has(ref) && 0 === explainedByLabel.size).length;
|
|
45
|
+
return { exercised: controls.length - untouched.length, droveGone, untouched };
|
|
46
|
+
}
|
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { ReticleCommand, SnapshotMode } from '@reticlehq/core';
|
|
3
3
|
import { ReticleTool } from './tool-names.js';
|
|
4
4
|
import { asString } from './tools-helpers.js';
|
|
5
|
+
import { exercisedCount } from './coverage-identity.js';
|
|
5
6
|
import { commandOrThrow, sessionIdShape } from './tool-kit.js';
|
|
6
7
|
/**
|
|
7
8
|
* `reticle_coverage` — which interactive controls this session has driven, and which it has not.
|
|
@@ -28,7 +29,7 @@ export function parseControls(tree) {
|
|
|
28
29
|
const seen = new Set();
|
|
29
30
|
for (const line of tree.split('\n')) {
|
|
30
31
|
const match = LINE_WITH_REF.exec(line);
|
|
31
|
-
if (
|
|
32
|
+
if (null === match)
|
|
32
33
|
continue;
|
|
33
34
|
const [, label, ref] = match;
|
|
34
35
|
if (label === undefined || ref === undefined || seen.has(ref))
|
|
@@ -71,21 +72,16 @@ export const COVERAGE_TOOLS = [
|
|
|
71
72
|
mode: SnapshotMode.INTERACTIVE,
|
|
72
73
|
});
|
|
73
74
|
const tree = asString(result['tree']) ?? '';
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// against the actions that WORKED: archive, delete, submit and navigate all remove their own
|
|
80
|
-
// control, so clicking one and asking again reported `exercised: 0` — measured on the Electron
|
|
81
|
-
// demo right after a successful archive. An agent reading "0" concludes it has done nothing
|
|
82
|
-
// and re-drives ground it already covered.
|
|
83
|
-
const drovenGone = [...acted].filter((ref) => !present.has(ref)).length;
|
|
75
|
+
// Matched by ref AND by label — see coverage-identity. A ref dies with the next re-render, so
|
|
76
|
+
// on a framework that replaces nodes this reported `exercised: 0` however much work was done.
|
|
77
|
+
// `droveGone` keeps the other honesty: archive/delete/submit remove their own control, so a
|
|
78
|
+
// drive that WORKED must not read as no coverage at all.
|
|
79
|
+
const { exercised, droveGone, untouched } = exercisedCount(parseControls(tree), session.actedRefs(), session.actedLabels());
|
|
84
80
|
return {
|
|
85
|
-
total:
|
|
86
|
-
exercised
|
|
81
|
+
total: parseControls(tree).length,
|
|
82
|
+
exercised,
|
|
87
83
|
untouched,
|
|
88
|
-
...(
|
|
84
|
+
...(droveGone > 0 ? { alsoDroveGone: droveGone } : {}),
|
|
89
85
|
};
|
|
90
86
|
},
|
|
91
87
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ToolDef } from './tools.js';
|
|
2
|
+
import { type ToolSurfaceOrigin } from './tool-surface.js';
|
|
2
3
|
/**
|
|
3
4
|
* Build the two dynamic meta-tools over the full tool table. `reticle_run` dispatches through the same
|
|
4
5
|
* `runTool` chokepoint as a direct call, so session-health splicing and every other invariant hold.
|
|
5
6
|
*/
|
|
6
|
-
export declare function buildDynamicTools(allTools: ToolDef[]): ToolDef[];
|
|
7
|
+
export declare function buildDynamicTools(allTools: ToolDef[], profile?: ToolSurfaceOrigin): ToolDef[];
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { runTool } from './invoke-tool.js';
|
|
3
|
+
import { buildErrorPayload } from './error-recovery.js';
|
|
4
|
+
import { mergedNameRedirect, mergedNameMessage } from './merged-name-redirect.js';
|
|
3
5
|
import { ReticleTool } from './tool-names.js';
|
|
6
|
+
import { TOOL_PROFILE_ENV } from './tool-surface.js';
|
|
4
7
|
import { getSessionMetrics } from '../telemetry/session-metrics.js';
|
|
5
8
|
/**
|
|
6
9
|
* On-demand tool loading for MCP — the answer to the per-turn tool-definition tax.
|
|
@@ -36,12 +39,39 @@ function paramInfo(shape) {
|
|
|
36
39
|
description: schema.description ?? '',
|
|
37
40
|
}));
|
|
38
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The one wording for "you named a parameter that does not exist", shared by both checks in
|
|
44
|
+
* `reticle_run` — the one over the WRAPPED tool's `args`, and the one over reticle_run's own
|
|
45
|
+
* top-level keys. Two spellings of the same refusal is how one of them ends up not existing.
|
|
46
|
+
*/
|
|
47
|
+
function unknownParamsError(toolName, unknown) {
|
|
48
|
+
return `unknown ${1 === unknown.length ? 'parameter' : 'parameters'} for ${toolName}: ${unknown.join(', ')} — NOT applied, so any result would be an answer to a different question`;
|
|
49
|
+
}
|
|
50
|
+
/** The keys not declared by `shape`, in call order. */
|
|
51
|
+
function unknownKeys(args, shape) {
|
|
52
|
+
const declared = new Set(Object.keys(shape));
|
|
53
|
+
return Object.keys(args).filter((key) => !declared.has(key));
|
|
54
|
+
}
|
|
39
55
|
/**
|
|
40
56
|
* Build the two dynamic meta-tools over the full tool table. `reticle_run` dispatches through the same
|
|
41
57
|
* `runTool` chokepoint as a direct call, so session-health splicing and every other invariant hold.
|
|
42
58
|
*/
|
|
43
|
-
export function buildDynamicTools(allTools) {
|
|
59
|
+
export function buildDynamicTools(allTools, profile) {
|
|
44
60
|
const byName = new Map(allTools.map((t) => [t.name, t]));
|
|
61
|
+
// The profile is a DAEMON-startup decision, so an agent that exported RETICLE_TOOL_PROFILE into its
|
|
62
|
+
// own environment sees no change and has, until now, no way to tell. Reported with the catalog.
|
|
63
|
+
const profileBlock = profile === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: {
|
|
66
|
+
profile: {
|
|
67
|
+
...profile,
|
|
68
|
+
// NOT "every tool is reachable via reticle_run under any profile" — measured false:
|
|
69
|
+
// `full` advertises all 46 tools DIRECTLY and carries no meta-tools at all, so
|
|
70
|
+
// reticle_run does not exist there. Saying otherwise sends the agent to a tool the
|
|
71
|
+
// profile does not have.
|
|
72
|
+
note: `The profile is read once at daemon startup — change ${TOOL_PROFILE_ENV} and restart the daemon, or it has no effect. Under this profile every unadvertised tool is reachable through reticle_run; under \`full\` there are no meta-tools because every tool is advertised directly.`,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
45
75
|
const reticleTools = {
|
|
46
76
|
name: ReticleTool.TOOLS,
|
|
47
77
|
description: 'Discover Reticle tools on demand. Call with no arguments to list every tool (name + one-line summary); call with names:["reticle_network", …] to load full descriptions and parameters for specific tools. Then invoke them with reticle_run. This avoids paying for every tool definition on every turn. To make a verification REUSABLE (record once, replay free forever), the flow workflow lives here: reticle_record{action:"start"} → act → reticle_flow_save → reticle_flow_verify (and reticle_flow_heal on drift). Load those names when you want to save or re-run a flow.',
|
|
@@ -53,11 +83,12 @@ export function buildDynamicTools(allTools) {
|
|
|
53
83
|
},
|
|
54
84
|
handler: (_deps, args) => {
|
|
55
85
|
const names = Array.isArray(args['names'])
|
|
56
|
-
? args['names'].filter((n) => typeof n
|
|
86
|
+
? args['names'].filter((n) => 'string' === typeof n)
|
|
57
87
|
: undefined;
|
|
58
|
-
if (names === undefined || names.length
|
|
88
|
+
if (names === undefined || 0 === names.length) {
|
|
59
89
|
return Promise.resolve({
|
|
60
90
|
tools: allTools.map((t) => ({ name: t.name, summary: firstSentence(t.description) })),
|
|
91
|
+
...profileBlock,
|
|
61
92
|
next: 'Load params with reticle_tools { names:[…] }, then call reticle_run { tool, args }.',
|
|
62
93
|
});
|
|
63
94
|
}
|
|
@@ -71,35 +102,81 @@ export function buildDynamicTools(allTools) {
|
|
|
71
102
|
});
|
|
72
103
|
},
|
|
73
104
|
};
|
|
105
|
+
const runShape = {
|
|
106
|
+
tool: z.string().describe('Tool name to invoke, e.g. reticle_network.'),
|
|
107
|
+
args: z.record(z.unknown()).optional().describe('Arguments object for that tool.'),
|
|
108
|
+
// Accepted AND FORWARDED, not merely tolerated. reticle_run is the only way to reach an
|
|
109
|
+
// unadvertised tool, so on a machine running several projects it has to be aimable — and
|
|
110
|
+
// `sessionId` is the shape an agent already uses on every other tool. Reported across 6 of 6
|
|
111
|
+
// apps: it took this key, dropped it, resolved by the daemon's cwd project, and failed with
|
|
112
|
+
// "no browser session for project X" while naming the very session it had been given.
|
|
113
|
+
sessionId: z
|
|
114
|
+
.string()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe('Target tab for the invoked tool. Forwarded to it — an explicit args.sessionId wins.'),
|
|
117
|
+
};
|
|
74
118
|
const reticleRun = {
|
|
75
119
|
name: ReticleTool.RUN,
|
|
76
120
|
description: "Invoke any Reticle tool by name (discover names/params first with reticle_tools). On an unknown tool or bad arguments it returns the available names or the tool's params, so you can correct and retry.",
|
|
77
|
-
inputSchema:
|
|
78
|
-
tool: z.string().describe('Tool name to invoke, e.g. reticle_network.'),
|
|
79
|
-
args: z.record(z.unknown()).optional().describe('Arguments object for that tool.'),
|
|
80
|
-
},
|
|
121
|
+
inputSchema: runShape,
|
|
81
122
|
handler: async (deps, args) => {
|
|
82
|
-
|
|
83
|
-
|
|
123
|
+
// reticle_run's OWN parameters, checked first and with the same wording as the inner check.
|
|
124
|
+
// It refused an unknown key inside `args` and silently dropped one beside them: an agent that
|
|
125
|
+
// wrote `reticle_run { tool, args, sessionId }` — the shape it uses on every other tool — had
|
|
126
|
+
// its sessionId ignored and got a confident answer about whichever session auto-selection
|
|
127
|
+
// picked. Every other tool on the surface refuses this; the escape hatch has to as well.
|
|
128
|
+
const strayTop = unknownKeys(args, runShape);
|
|
129
|
+
if (strayTop.length > 0) {
|
|
130
|
+
return {
|
|
131
|
+
error: unknownParamsError(ReticleTool.RUN, strayTop),
|
|
132
|
+
hint: `pass them inside args: reticle_run { tool, args: { ${strayTop.join(', ')}: … } } if the target tool declares them`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const name = 'string' === typeof args['tool'] ? args['tool'] : '';
|
|
136
|
+
const given = 'object' === typeof args['args'] && args['args'] !== null
|
|
84
137
|
? args['args']
|
|
85
138
|
: {};
|
|
139
|
+
const aimed = args['sessionId'];
|
|
86
140
|
const target = byName.get(name);
|
|
87
141
|
if (target === undefined) {
|
|
88
142
|
// A non-zero count here means our advertised surface is confusing the agent — it reached for
|
|
89
143
|
// something it believed existed. That is a docs/naming defect, and it was invisible.
|
|
90
|
-
|
|
144
|
+
//
|
|
145
|
+
// The NAME goes with it: the count says the surface confused someone, the name says which
|
|
146
|
+
// capability they expected. It was already in hand here and thrown away, so the one place
|
|
147
|
+
// the product could learn what agents want was discarding it. Safe — a name from our own
|
|
148
|
+
// tool namespace, never app data.
|
|
149
|
+
getSessionMetrics().recordUnknownTool(name);
|
|
150
|
+
// ...and for 22 of those names the capability DOES exist, it just moved when tools merged.
|
|
151
|
+
// Answering "unknown tool" there is simply wrong; see merged-name-redirect.
|
|
152
|
+
const moved = mergedNameRedirect(name);
|
|
153
|
+
if (moved !== undefined) {
|
|
154
|
+
return {
|
|
155
|
+
error: mergedNameMessage(name, moved),
|
|
156
|
+
tool: moved.tool,
|
|
157
|
+
...(moved.action === undefined ? {} : { action: moved.action }),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
91
160
|
return { error: `unknown tool '${name}'`, available: allTools.map((t) => t.name) };
|
|
92
161
|
}
|
|
162
|
+
// The outer aim, forwarded ONLY to a tool that declares a session — injecting it into one that
|
|
163
|
+
// does not would trip the unknown-key check below and refuse a call the caller got right. An
|
|
164
|
+
// inner args.sessionId wins as the more specific instruction, and an absent aim stays absent so
|
|
165
|
+
// auto-selection still applies.
|
|
166
|
+
const callArgs = 'string' === typeof aimed &&
|
|
167
|
+
given['sessionId'] === undefined &&
|
|
168
|
+
undefined !== target.inputSchema['sessionId']
|
|
169
|
+
? { ...given, sessionId: aimed }
|
|
170
|
+
: given;
|
|
93
171
|
// The escape hatch is where a typo is MOST likely, because an unadvertised tool's parameters
|
|
94
172
|
// are not in front of the agent at all — and it is the one path the SDK's own validation never
|
|
95
173
|
// sees, since `reticle_run`'s own args (`tool`, `args`) are perfectly valid. Left unchecked,
|
|
96
174
|
// `reticle_run { tool: "reticle_clock", args: { action: "freeze" } }` returned
|
|
97
175
|
// `{"frozen":false}`: a well-formed answer to a question nobody asked.
|
|
98
|
-
const
|
|
99
|
-
const unknown = Object.keys(callArgs).filter((key) => !declared.has(key));
|
|
176
|
+
const unknown = unknownKeys(callArgs, target.inputSchema);
|
|
100
177
|
if (unknown.length > 0) {
|
|
101
178
|
return {
|
|
102
|
-
error:
|
|
179
|
+
error: unknownParamsError(name, unknown),
|
|
103
180
|
tool: name,
|
|
104
181
|
params: paramInfo(target.inputSchema),
|
|
105
182
|
...(target.example === undefined ? {} : { example: target.example }),
|
|
@@ -110,12 +187,12 @@ export function buildDynamicTools(allTools) {
|
|
|
110
187
|
return await runTool(target, deps, callArgs);
|
|
111
188
|
}
|
|
112
189
|
catch (error) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
};
|
|
190
|
+
// The SAME payload the direct tool path builds — recovery included. Answering every failure
|
|
191
|
+
// with "fix the arguments" threw that away, and under the default profile nearly every tool
|
|
192
|
+
// is reached through here: a stale ref, a paused session, a missing pairing token all came
|
|
193
|
+
// back as the agent's arguments being wrong, which is advice that spends the retry.
|
|
194
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
195
|
+
return { ...buildErrorPayload(message), tool: name };
|
|
119
196
|
}
|
|
120
197
|
},
|
|
121
198
|
};
|
|
@@ -17,11 +17,43 @@ export declare const RECOVERY: {
|
|
|
17
17
|
readonly MISSING_BASELINE: string;
|
|
18
18
|
readonly MISSING_RECORDING: string;
|
|
19
19
|
readonly STALE_REF: string;
|
|
20
|
+
readonly INVALID_NAME: string;
|
|
21
|
+
readonly NO_SUCH_OPTION: string;
|
|
20
22
|
readonly BAD_ARGUMENTS: string;
|
|
23
|
+
readonly WRONG_TARGET: string;
|
|
24
|
+
readonly NOT_EDITABLE: string;
|
|
25
|
+
readonly CONFIRM_DANGEROUS: string;
|
|
26
|
+
readonly UNSUPPORTED_SURFACE: string;
|
|
21
27
|
readonly TOKEN_REQUIRED: string;
|
|
28
|
+
readonly SCOPE_MISMATCH: string;
|
|
29
|
+
readonly SESSION_GONE: string;
|
|
30
|
+
readonly COMMAND_TIMEOUT: string;
|
|
31
|
+
readonly FLOW_STEP_MISSING: string;
|
|
32
|
+
readonly NO_POOL: string;
|
|
22
33
|
};
|
|
23
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* The actionable next move for a known error message, or undefined when none is recognized.
|
|
36
|
+
*
|
|
37
|
+
* A message that already carries its own next action gets NO second one. The no-session diagnosis
|
|
38
|
+
* inspects the machine and says which of three causes this actually is; pairing that with the
|
|
39
|
+
* generic "ask the human to start their app" produced a result that contradicted itself — the error
|
|
40
|
+
* saying a server is running and the recovery saying to go start one.
|
|
41
|
+
*/
|
|
24
42
|
export declare function recoveryFor(message: string): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* A serialized zod issue array, rendered as a sentence. Anything else is returned untouched.
|
|
45
|
+
*
|
|
46
|
+
* This is the one boundary every agent-visible tool failure crosses, which is why the rewrite lives
|
|
47
|
+
* here rather than in each tool. `parsePredicate` solved this for the three predicate tools by never
|
|
48
|
+
* producing the array; it cannot help anywhere else, because the MCP SDK validates a tool's schema
|
|
49
|
+
* BEFORE our handler runs. So `reticle_session` with no action — the commonest mistake there is —
|
|
50
|
+
* never reaches the friendly "unknown action … expected: [tune, yield, …]" its handler already
|
|
51
|
+
* builds, and the agent gets the raw array instead. Driven live against the shipped daemon.
|
|
52
|
+
*
|
|
53
|
+
* Conservative by construction: it only rewrites a JSON ARRAY whose every element carries both
|
|
54
|
+
* `code` and `message`. A tool that legitimately returns JSON is left alone.
|
|
55
|
+
*/
|
|
56
|
+
export declare function zodArrayAsSentence(message: string): string;
|
|
25
57
|
/**
|
|
26
58
|
* The ask attached to errors we do NOT recognize. An unrecognized error is, by definition, the case
|
|
27
59
|
* where Reticle failed in a way nobody anticipated — the single highest-value moment to hear from the
|
|
@@ -43,5 +75,5 @@ interface ErrorPayload {
|
|
|
43
75
|
* known; the feedback ask is added when it is NOT — the two are mutually exclusive by design, so the
|
|
44
76
|
* agent always gets exactly one next move.
|
|
45
77
|
*/
|
|
46
|
-
export declare function buildErrorPayload(
|
|
78
|
+
export declare function buildErrorPayload(rawMessage: string): ErrorPayload;
|
|
47
79
|
export {};
|