@reticlehq/server 2.11.0 → 2.13.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 +93 -111
- package/dist/bridge/auth-failure-reason.d.ts +6 -2
- package/dist/bridge/auth-failure-reason.js +15 -8
- package/dist/bridge/bridge.d.ts +18 -14
- package/dist/bridge/bridge.js +248 -165
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/bridge/pairing-token.d.ts +5 -0
- package/dist/bridge/pairing-token.js +28 -0
- package/dist/bridge/protocol-skew.d.ts +37 -0
- package/dist/bridge/protocol-skew.js +45 -0
- package/dist/capsule/capsule-store.d.ts +8 -7
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/capsule/causal-summary.js +7 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +39 -1
- package/dist/cli/cli-flow-commands.d.ts +3 -1
- package/dist/cli/cli-flow-commands.js +34 -4
- package/dist/cli/cli-kill.d.ts +2 -1
- package/dist/cli/cli-launch.d.ts +11 -1
- package/dist/cli/cli-launch.js +59 -10
- package/dist/cli/cli-parse.d.ts +17 -1
- package/dist/cli/cli-parse.js +238 -12
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +2 -1
- package/dist/cli/cli-verify.js +5 -4
- package/dist/cli/cloud-cli.js +428 -253
- package/dist/cli/cloud-keystore.d.ts +37 -0
- package/dist/cli/cloud-keystore.js +97 -0
- package/dist/cli/cloud-kit.d.ts +37 -0
- package/dist/cli/cloud-kit.js +121 -0
- package/dist/cli/cloud-login.d.ts +17 -0
- package/dist/cli/cloud-login.js +183 -0
- package/dist/cli/cloud-session.d.ts +48 -0
- package/dist/cli/cloud-session.js +53 -0
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/config-discovery.js +1 -0
- package/dist/cli/daemon-start-options.d.ts +32 -0
- package/dist/cli/daemon-start-options.js +38 -0
- package/dist/cli/device-flow.d.ts +76 -0
- package/dist/cli/device-flow.js +56 -0
- package/dist/cli/doctor-rows.d.ts +12 -1
- package/dist/cli/doctor-rows.js +12 -1
- package/dist/cli/drive-attach.d.ts +13 -0
- package/dist/cli/drive-attach.js +18 -0
- package/dist/cli/drive-command.js +7 -3
- package/dist/cli/gate-exit.d.ts +21 -0
- package/dist/cli/gate-exit.js +21 -0
- package/dist/cli/gate-hook-message.d.ts +33 -0
- package/dist/cli/gate-hook-message.js +46 -0
- package/dist/cli/mcp-command.d.ts +25 -0
- package/dist/cli/mcp-command.js +174 -0
- package/dist/cli/project-name.d.ts +30 -0
- package/dist/cli/project-name.js +44 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli/status-next-action.d.ts +14 -0
- package/dist/cli/status-next-action.js +4 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +167 -186
- package/dist/cloud/cloud-config.d.ts +24 -2
- package/dist/cloud/cloud-config.js +64 -5
- package/dist/cloud/cloud-sync.d.ts +16 -5
- package/dist/cloud/cloud-sync.js +64 -1
- package/dist/cloud/sync-cycle.d.ts +110 -0
- package/dist/cloud/sync-cycle.js +270 -0
- package/dist/cloud/sync-daemon.d.ts +69 -0
- package/dist/cloud/sync-daemon.js +260 -0
- package/dist/cloud/sync-disk.d.ts +9 -0
- package/dist/cloud/sync-disk.js +191 -0
- package/dist/cloud/sync-hash.d.ts +2 -0
- package/dist/cloud/sync-hash.js +35 -0
- package/dist/crawl/crawl-tools.js +5 -3
- package/dist/crawl/crawl.d.ts +12 -13
- package/dist/crawl/crawl.js +25 -3
- package/dist/daemon/daemon-resolve.d.ts +122 -0
- package/dist/daemon/daemon-resolve.js +187 -0
- package/dist/daemon/dev-servers.d.ts +15 -0
- package/dist/daemon/dev-servers.js +68 -0
- package/dist/daemon/free-port.d.ts +29 -0
- package/dist/daemon/free-port.js +55 -0
- package/dist/daemon/heartbeat.d.ts +0 -4
- package/dist/daemon/heartbeat.js +2 -2
- package/dist/daemon/wake-decision.d.ts +32 -0
- package/dist/daemon/wake-decision.js +38 -0
- package/dist/dev/stale-issue-guard.d.ts +0 -2
- package/dist/dev/stale-issue-guard.js +1 -1
- package/dist/domain/domain-tools.js +2 -2
- package/dist/events/contradictions.d.ts +59 -0
- package/dist/events/contradictions.js +253 -27
- package/dist/events/declared.d.ts +17 -0
- package/dist/events/declared.js +43 -1
- package/dist/events/echo-mismatch.js +65 -1
- package/dist/events/event-filters.d.ts +8 -0
- package/dist/events/event-filters.js +19 -2
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +17 -0
- package/dist/events/predicate-asks.d.ts +13 -0
- package/dist/events/predicate-asks.js +22 -0
- package/dist/events/predicate-eval.js +117 -27
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +43 -3
- package/dist/events/predicate-precheck.d.ts +8 -0
- package/dist/events/predicate-precheck.js +41 -0
- package/dist/events/predicate-route.d.ts +14 -0
- package/dist/events/predicate-route.js +36 -8
- package/dist/events/predicate-schema.d.ts +44 -0
- package/dist/events/predicate-schema.js +126 -23
- package/dist/events/predicate.d.ts +20 -0
- package/dist/events/predicate.js +77 -6
- package/dist/events/split-text-miss.d.ts +30 -0
- package/dist/events/split-text-miss.js +44 -0
- package/dist/flows/coverage.d.ts +0 -8
- package/dist/flows/coverage.js +1 -1
- package/dist/flows/decision.d.ts +5 -1
- package/dist/flows/decision.js +33 -7
- package/dist/flows/flake.d.ts +6 -8
- package/dist/flows/flake.js +1 -1
- package/dist/flows/flow-intent.d.ts +52 -0
- package/dist/flows/flow-intent.js +123 -0
- package/dist/flows/flow-memory-consult.d.ts +55 -0
- package/dist/flows/flow-memory-consult.js +38 -0
- package/dist/flows/flow-replay-run.d.ts +18 -7
- package/dist/flows/flow-replay-run.js +263 -23
- package/dist/flows/flow-replay.js +11 -2
- package/dist/flows/flow-step-runners.d.ts +8 -0
- package/dist/flows/flow-step-runners.js +8 -2
- package/dist/flows/flow-store-for-session.d.ts +31 -0
- package/dist/flows/flow-store-for-session.js +33 -0
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +87 -33
- package/dist/flows/flows.d.ts +14 -1
- package/dist/flows/flows.js +502 -24
- package/dist/flows/heal-run.js +5 -2
- package/dist/flows/predicate-to-expect.d.ts +17 -12
- package/dist/flows/predicate-to-expect.js +36 -15
- package/dist/flows/recordings.d.ts +13 -1
- package/dist/flows/recordings.js +17 -2
- package/dist/flows/replay.d.ts +22 -2
- package/dist/flows/replay.js +28 -2
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +9 -3
- package/dist/honesty/blind-spots.d.ts +51 -15
- package/dist/honesty/blind-spots.js +64 -4
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/honesty.d.ts +24 -0
- package/dist/honesty/honesty.js +25 -1
- package/dist/honesty/instrumentation-gaps.d.ts +39 -6
- package/dist/honesty/instrumentation-gaps.js +88 -5
- package/dist/honesty/tool-hit-rate.d.ts +86 -0
- package/dist/honesty/tool-hit-rate.js +90 -0
- package/dist/honesty/undeclared-change.d.ts +51 -0
- package/dist/honesty/undeclared-change.js +56 -0
- package/dist/honesty/verified.d.ts +16 -0
- package/dist/honesty/verified.js +84 -5
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +13 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-recorder.d.ts +31 -8
- package/dist/impact/impact-recorder.js +104 -21
- package/dist/impact/impact-store.d.ts +13 -4
- package/dist/impact/impact-store.js +44 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +113 -7
- package/dist/init/agent-rules.d.ts +30 -6
- package/dist/init/agent-rules.js +54 -13
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +28 -0
- package/dist/init/capabilities.d.ts +4 -3
- package/dist/init/capabilities.js +23 -3
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +27 -28
- package/dist/init/confirm.js +51 -11
- package/dist/init/cra.d.ts +34 -5
- package/dist/init/cra.js +96 -25
- package/dist/init/csp-check.js +17 -2
- package/dist/init/csp-doctor.d.ts +8 -1
- package/dist/init/csp-doctor.js +8 -3
- package/dist/init/dev-script.d.ts +50 -0
- package/dist/init/dev-script.js +66 -0
- package/dist/init/format-generated.d.ts +18 -0
- package/dist/init/format-generated.js +39 -0
- package/dist/init/install-hint.d.ts +9 -0
- package/dist/init/install-hint.js +29 -0
- package/dist/init/mcp-clients.d.ts +7 -0
- package/dist/init/mcp-clients.js +7 -0
- package/dist/init/mcp.d.ts +17 -6
- package/dist/init/mcp.js +27 -11
- package/dist/init/node-io.js +11 -1
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +147 -37
- package/dist/init/plan.d.ts +30 -37
- package/dist/init/plan.js +50 -60
- package/dist/init/preflight.d.ts +28 -0
- package/dist/init/preflight.js +38 -0
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +32 -35
- package/dist/init/run.js +151 -202
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/snippets.js +90 -31
- package/dist/init/vite-config.d.ts +1 -1
- package/dist/init/vite-config.js +39 -25
- package/dist/init/workspace-apps.d.ts +17 -10
- package/dist/init/workspace-apps.js +110 -0
- package/dist/input/network-mock-tools.js +3 -6
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +28 -7
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/input/scroll-tools.js +4 -6
- package/dist/input/viewport-tools.js +5 -4
- package/dist/intent/inline-intent.d.ts +37 -0
- package/dist/intent/inline-intent.js +193 -0
- package/dist/intent/intent-shard-store.d.ts +72 -0
- package/dist/intent/intent-shard-store.js +203 -0
- package/dist/intent/intent-shard.d.ts +411 -0
- package/dist/intent/intent-shard.js +176 -0
- package/dist/intent/intent-store.d.ts +49 -0
- package/dist/intent/intent-store.js +133 -0
- package/dist/intent/intent-subject.d.ts +45 -0
- package/dist/intent/intent-subject.js +177 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +143 -0
- package/dist/intent/open-intents.d.ts +42 -0
- package/dist/intent/open-intents.js +40 -0
- package/dist/journal/envelope.d.ts +26 -26
- package/dist/journal/journal-recorder.d.ts +23 -0
- package/dist/journal/journal-recorder.js +35 -0
- package/dist/license/license-env.d.ts +29 -0
- package/dist/license/license-env.js +148 -0
- package/dist/loopback-agent.d.ts +31 -0
- package/dist/loopback-agent.js +57 -0
- package/dist/mcp/attach-memory.d.ts +83 -0
- package/dist/mcp/attach-memory.js +185 -0
- package/dist/mcp/client-identity.d.ts +31 -0
- package/dist/mcp/client-identity.js +51 -0
- package/dist/mcp/mcp-post-transport.d.ts +26 -0
- package/dist/mcp/mcp-post-transport.js +109 -0
- package/dist/mcp/mcp-proxy.d.ts +2 -8
- package/dist/mcp/mcp-proxy.js +64 -62
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +52 -24
- package/dist/mcp/proxy-backoff.d.ts +11 -0
- package/dist/mcp/proxy-backoff.js +13 -0
- package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
- package/dist/mcp/proxy-daemon-probe.js +7 -1
- package/dist/mcp/proxy-handshake.d.ts +23 -0
- package/dist/mcp/proxy-handshake.js +31 -0
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +16 -0
- package/dist/mcp/tool-catalog-cache.js +22 -0
- package/dist/memory/memory-tools.d.ts +2 -0
- package/dist/memory/memory-tools.js +82 -0
- package/dist/memory/project-memory.d.ts +34 -0
- package/dist/memory/project-memory.js +96 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/propose-consequences.js +12 -3
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +75 -5
- package/dist/pool/browser-pool.js +144 -5
- package/dist/pool/playwright-launcher.js +12 -1
- package/dist/project/fs-port.d.ts +3 -0
- package/dist/project/fs-port.js +3 -2
- package/dist/project/memory-fs.js +6 -1
- package/dist/project/project-tools.js +6 -35
- package/dist/project/reticle-dir.d.ts +17 -2
- package/dist/project/reticle-dir.js +22 -4
- package/dist/runs/context-tools.d.ts +2 -0
- package/dist/runs/context-tools.js +89 -0
- package/dist/runs/run-context.d.ts +40 -0
- package/dist/runs/run-context.js +158 -0
- package/dist/runs/run-diff.d.ts +2 -1
- package/dist/runs/run-store.d.ts +10 -0
- package/dist/runs/run-store.js +10 -0
- package/dist/runs/verification-sync.d.ts +0 -11
- package/dist/runs/verification-sync.js +24 -3
- package/dist/runs/verify-server.d.ts +8 -1
- package/dist/runs/verify-server.js +14 -2
- package/dist/session/command-timeout.d.ts +5 -2
- package/dist/session/command-timeout.js +5 -4
- package/dist/session/dev-server-probe.d.ts +0 -8
- package/dist/session/dev-server-probe.js +1 -1
- package/dist/session/last-act.d.ts +7 -1
- package/dist/session/last-act.js +2 -2
- package/dist/session/lease-visibility.d.ts +67 -0
- package/dist/session/lease-visibility.js +76 -0
- package/dist/session/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +33 -0
- package/dist/session/no-session-diagnosis.js +82 -24
- package/dist/session/no-session-next-action.d.ts +12 -0
- package/dist/session/no-session-next-action.js +77 -6
- package/dist/session/no-session-watch.d.ts +18 -6
- package/dist/session/no-session-watch.js +125 -75
- package/dist/session/output-budget.js +4 -1
- package/dist/session/pending-commands.d.ts +11 -1
- package/dist/session/pending-commands.js +12 -2
- package/dist/session/resolve-within.d.ts +34 -0
- package/dist/session/resolve-within.js +53 -0
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-info.d.ts +18 -0
- package/dist/session/session-info.js +11 -0
- package/dist/session/session-manager.d.ts +14 -0
- package/dist/session/session-manager.js +111 -4
- package/dist/session/session-mirror.d.ts +16 -0
- package/dist/session/session-mirror.js +22 -0
- package/dist/session/session-recommendation.d.ts +19 -5
- package/dist/session/session-recommendation.js +25 -9
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +113 -0
- package/dist/session/session-tools.js +2 -2
- package/dist/session/session.d.ts +53 -3
- package/dist/session/session.js +162 -9
- package/dist/session/wall-clock.d.ts +12 -0
- package/dist/session/wall-clock.js +14 -0
- package/dist/setup/agent-configs.d.ts +96 -0
- package/dist/setup/agent-configs.js +333 -0
- package/dist/setup/agent-io.d.ts +9 -0
- package/dist/setup/agent-io.js +16 -0
- package/dist/setup/agent-writer.d.ts +51 -0
- package/dist/setup/agent-writer.js +115 -0
- package/dist/setup/approval-migration.d.ts +47 -0
- package/dist/setup/approval-migration.js +68 -0
- package/dist/setup/auto-approve.d.ts +85 -0
- package/dist/setup/auto-approve.js +150 -0
- package/dist/setup/bridge-port.d.ts +15 -0
- package/dist/setup/bridge-port.js +24 -0
- package/dist/setup/desktop-shape.d.ts +57 -0
- package/dist/setup/desktop-shape.js +63 -0
- package/dist/setup/dev-server-wait.d.ts +60 -0
- package/dist/setup/dev-server-wait.js +65 -0
- package/dist/setup/drive-agent.d.ts +71 -0
- package/dist/setup/drive-agent.js +171 -0
- package/dist/setup/drive-plan.d.ts +68 -0
- package/dist/setup/drive-plan.js +130 -0
- package/dist/setup/ensure-daemon.d.ts +29 -0
- package/dist/setup/ensure-daemon.js +58 -0
- package/dist/setup/init-runtime.d.ts +47 -0
- package/dist/setup/init-runtime.js +160 -0
- package/dist/setup/interrupt.d.ts +25 -0
- package/dist/setup/interrupt.js +40 -0
- package/dist/setup/license-key.d.ts +39 -0
- package/dist/setup/license-key.js +72 -0
- package/dist/setup/listeners.d.ts +37 -0
- package/dist/setup/listeners.js +85 -0
- package/dist/setup/node-effects.d.ts +60 -0
- package/dist/setup/node-effects.js +210 -0
- package/dist/setup/page-probe.d.ts +39 -0
- package/dist/setup/page-probe.js +49 -0
- package/dist/setup/relaunch.d.ts +36 -0
- package/dist/setup/relaunch.js +57 -0
- package/dist/setup/remaining-steps.d.ts +19 -0
- package/dist/setup/remaining-steps.js +31 -0
- package/dist/setup/run-setup.d.ts +99 -0
- package/dist/setup/run-setup.js +187 -0
- package/dist/setup/session-pick.d.ts +47 -0
- package/dist/setup/session-pick.js +51 -0
- package/dist/setup/setup-command.d.ts +45 -0
- package/dist/setup/setup-command.js +199 -0
- package/dist/setup/setup-options.d.ts +74 -0
- package/dist/setup/setup-options.js +67 -0
- package/dist/setup/transcripts.d.ts +16 -0
- package/dist/setup/transcripts.js +43 -0
- package/dist/status-payload.d.ts +14 -1
- package/dist/status-payload.js +23 -1
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/daemon-telemetry.d.ts +8 -1
- package/dist/telemetry/daemon-telemetry.js +52 -5
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +9 -1
- package/dist/telemetry/feedback-context.js +60 -29
- package/dist/telemetry/feedback-local.d.ts +15 -0
- package/dist/telemetry/feedback-local.js +56 -0
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/feedback.d.ts +11 -0
- package/dist/telemetry/feedback.js +19 -6
- package/dist/telemetry/init-telemetry.d.ts +2 -0
- package/dist/telemetry/init-telemetry.js +2 -0
- package/dist/telemetry/install-source.d.ts +14 -0
- package/dist/telemetry/install-source.js +30 -0
- package/dist/telemetry/license-activation.d.ts +7 -1
- package/dist/telemetry/license-activation.js +9 -5
- package/dist/telemetry/mcp-connection.js +7 -0
- package/dist/telemetry/project-profile.d.ts +12 -9
- package/dist/telemetry/project-profile.js +12 -7
- package/dist/telemetry/proxy-telemetry.d.ts +3 -0
- package/dist/telemetry/proxy-telemetry.js +21 -0
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +12 -0
- package/dist/telemetry/session-metrics.js +35 -1
- package/dist/telemetry/telemetry.js +17 -2
- package/dist/telemetry/tool-refused.d.ts +3 -1
- package/dist/telemetry/tool-refused.js +34 -2
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-capsule.d.ts +10 -0
- package/dist/tools/act-capsule.js +8 -2
- package/dist/tools/act-danger.d.ts +2 -1
- package/dist/tools/act-danger.js +11 -3
- package/dist/tools/act-observation.d.ts +41 -0
- package/dist/tools/act-observation.js +36 -0
- package/dist/tools/act-preflight.d.ts +20 -0
- package/dist/tools/act-preflight.js +98 -0
- package/dist/tools/act-sequence-retry.d.ts +45 -0
- package/dist/tools/act-sequence-retry.js +58 -0
- package/dist/tools/act-sequence-tool.d.ts +13 -0
- package/dist/tools/act-sequence-tool.js +135 -0
- package/dist/tools/act-target.d.ts +14 -0
- package/dist/tools/act-target.js +47 -0
- package/dist/tools/act-tools.d.ts +21 -1
- package/dist/tools/act-tools.js +196 -204
- package/dist/tools/assert-source.d.ts +22 -0
- package/dist/tools/assert-source.js +53 -0
- package/dist/tools/assert-verdict.d.ts +20 -3
- package/dist/tools/assert-verdict.js +58 -20
- package/dist/tools/browser-tools.js +6 -22
- package/dist/tools/complete-snapshot.d.ts +48 -0
- package/dist/tools/complete-snapshot.js +75 -0
- package/dist/tools/coverage-tools.d.ts +10 -1
- package/dist/tools/coverage-tools.js +123 -69
- package/dist/tools/dynamic-tools.js +53 -13
- package/dist/tools/error-recovery.d.ts +25 -14
- package/dist/tools/error-recovery.js +78 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +121 -11
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +35 -5
- package/dist/tools/lease-tools.js +191 -13
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +22 -0
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/numeric-bounds.d.ts +100 -0
- package/dist/tools/numeric-bounds.js +125 -0
- package/dist/tools/observe-tools.js +109 -65
- package/dist/tools/read-tools.js +31 -8
- package/dist/tools/real-input-attempt.d.ts +32 -0
- package/dist/tools/real-input-attempt.js +212 -2
- package/dist/tools/reconcile-tools.js +22 -8
- package/dist/tools/resolve-target.d.ts +0 -7
- package/dist/tools/sdk-marker-probe.d.ts +29 -0
- package/dist/tools/sdk-marker-probe.js +57 -0
- package/dist/tools/snapshot-delta.d.ts +1 -1
- package/dist/tools/tool-kit.d.ts +24 -0
- package/dist/tools/tool-kit.js +21 -0
- package/dist/tools/tool-names.d.ts +5 -0
- package/dist/tools/tool-names.js +5 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools-helpers.d.ts +19 -0
- package/dist/tools/tools-helpers.js +46 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +78 -12
- package/dist/tools/unadvertised-help.js +4 -4
- package/dist/tools/verify-next-baton.d.ts +53 -0
- package/dist/tools/verify-next-baton.js +52 -0
- package/dist/update/reticle-deps.d.ts +9 -0
- package/dist/update/reticle-deps.js +27 -0
- package/dist/update/sdk-sync.d.ts +1 -2
- package/dist/update/sdk-sync.js +1 -21
- package/dist/update/update-nudge.d.ts +25 -10
- package/dist/update/update-nudge.js +32 -3
- package/dist/update/updater.d.ts +1 -1
- package/dist/version/peer-announce.js +4 -2
- package/dist/version/sdk-fix.d.ts +47 -0
- package/dist/version/sdk-fix.js +127 -0
- package/dist/version/version-nudge.d.ts +14 -0
- package/dist/version/version-nudge.js +21 -0
- package/dist/version/version-skew.d.ts +38 -3
- package/dist/version/version-skew.js +83 -7
- package/dist/visual/visual-store.d.ts +6 -6
- package/dist/visual/visual-store.js +18 -16
- package/dist/visual/visual-tools.js +57 -12
- package/docs/README.md +2 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -0
- package/docs/cli/serve.mdx +4 -2
- package/docs/cli/verify.mdx +4 -5
- package/docs/cli.mdx +7 -3
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +2 -2
- package/docs/gates.md +1 -1
- package/docs/getting-started.md +19 -3
- package/docs/index.mdx +9 -6
- package/docs/install-agentic.mdx +38 -12
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages.mdx +8 -6
- package/docs/platform-integration.md +17 -0
- package/docs/predicates.mdx +8 -0
- package/docs/reticle-manual.md +106 -0
- package/docs/skill-file.mdx +3 -3
- package/docs/telemetry-contract.md +108 -4
- package/docs/tools-act-sequence.mdx +16 -0
- package/docs/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +4 -3
- package/package.json +4 -3
- package/dist/init/cursor.d.ts +0 -24
- package/dist/init/cursor.js +0 -80
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { CORE_TOOL_NAMES, EXTENDED_TOOL_NAMES } from '../tools/tool-surface.js';
|
|
2
|
+
/**
|
|
3
|
+
* Which tools this session called, and how reachable each one was.
|
|
4
|
+
*
|
|
5
|
+
* The generalisation of the capture ledger: it recorded a short list of read tools to answer one
|
|
6
|
+
* feature's disproof, and the question that followed is the same question asked of the whole table —
|
|
7
|
+
* most of the surface has never been observed being called at all, and a decision about what to cut
|
|
8
|
+
* or consolidate is about to be made. This is the instrument for that decision, and nothing else.
|
|
9
|
+
*
|
|
10
|
+
* ## Why the count alone is not enough
|
|
11
|
+
*
|
|
12
|
+
* A tool that goes uncalled because nobody was told it exists and a tool that goes uncalled because
|
|
13
|
+
* nobody wants it look identical in a call count. They are opposite conclusions. So every count is
|
|
14
|
+
* reported beside the tool's REACHABILITY — advertised on the default surface, advertised only on
|
|
15
|
+
* the extended one, or reachable solely by naming it to `reticle_run` — and the interesting cell is
|
|
16
|
+
* the one where an agent found a run-only tool anyway.
|
|
17
|
+
*
|
|
18
|
+
* ## The tiers are derived, never restated
|
|
19
|
+
*
|
|
20
|
+
* `tierOfTool` reads `CORE_TOOL_NAMES` and `EXTENDED_TOOL_NAMES` directly. A tool promoted or
|
|
21
|
+
* demoted between surfaces changes tier here with nothing edited. A local copy of either list would
|
|
22
|
+
* go stale on the first move and would then be wrong about the exact axis this exists to measure —
|
|
23
|
+
* and it would be wrong silently, in a report used to justify deleting things.
|
|
24
|
+
*
|
|
25
|
+
* ## What these numbers do NOT establish
|
|
26
|
+
*
|
|
27
|
+
* - **One session is not a hit rate.** This is evidence about ONE run by ONE agent against ONE app.
|
|
28
|
+
* A hit rate is a distribution over many sessions; a single session quoted as one is an anecdote
|
|
29
|
+
* with a denominator drawn on it.
|
|
30
|
+
* - **Never-called is not useless.** A tool one team reaches for once a month in an emergency and a
|
|
31
|
+
* tool nobody has ever called are the same zero here. Frequency alone cannot separate them, so a
|
|
32
|
+
* cut decided on frequency alone will delete the wrong things.
|
|
33
|
+
* - **Absent is not zero.** A session with no ledger reports `observed: false`. It has not
|
|
34
|
+
* established that no tools were called; it has established that nothing was watching.
|
|
35
|
+
* - The tools that ran BEFORE this instrument existed in the process, or against a session this
|
|
36
|
+
* daemon adopted after the fact, are simply missing rather than counted low.
|
|
37
|
+
*/
|
|
38
|
+
/** How an agent could have reached a tool. Derived from the surface sets — see above. */
|
|
39
|
+
export const ToolTier = {
|
|
40
|
+
/** Advertised directly on the surface every user gets. */
|
|
41
|
+
DEFAULT: 'default',
|
|
42
|
+
/** Advertised only when the full schema-carrying surface is switched on. */
|
|
43
|
+
EXTENDED: 'extended',
|
|
44
|
+
/** Advertised nowhere: reachable only by naming it to `reticle_run`. */
|
|
45
|
+
RUN_ONLY: 'run-only',
|
|
46
|
+
};
|
|
47
|
+
/** Which surface would have put this tool in front of an agent, read off that surface's own set. */
|
|
48
|
+
export function tierOfTool(tool) {
|
|
49
|
+
if (CORE_TOOL_NAMES.has(tool))
|
|
50
|
+
return ToolTier.DEFAULT;
|
|
51
|
+
if (EXTENDED_TOOL_NAMES.has(tool))
|
|
52
|
+
return ToolTier.EXTENDED;
|
|
53
|
+
return ToolTier.RUN_ONLY;
|
|
54
|
+
}
|
|
55
|
+
function tally(tools) {
|
|
56
|
+
return {
|
|
57
|
+
default: tools.filter((tier) => ToolTier.DEFAULT === tier).length,
|
|
58
|
+
extended: tools.filter((tier) => ToolTier.EXTENDED === tier).length,
|
|
59
|
+
runOnly: tools.filter((tier) => ToolTier.RUN_ONLY === tier).length,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Cross one session's per-tool counts with the tool table.
|
|
64
|
+
*
|
|
65
|
+
* `allTools` is the DISPATCHABLE table — the names `reticle_run` will accept — passed in rather than
|
|
66
|
+
* imported, because the table is assembled in the module that owns this tool and importing it back
|
|
67
|
+
* would close a cycle. A tool counted but absent from the table still appears, so a name the table
|
|
68
|
+
* lost is visible rather than dropped.
|
|
69
|
+
*/
|
|
70
|
+
export function foldToolHitRate(input) {
|
|
71
|
+
const { calls } = input;
|
|
72
|
+
if (calls === undefined)
|
|
73
|
+
return { observed: false };
|
|
74
|
+
const called = [...calls]
|
|
75
|
+
.map(([tool, count]) => ({ tool, tier: tierOfTool(tool), calls: count }))
|
|
76
|
+
.sort((a, b) => b.calls - a.calls || a.tool.localeCompare(b.tool));
|
|
77
|
+
const neverCalled = input.allTools
|
|
78
|
+
.filter((tool) => !calls.has(tool))
|
|
79
|
+
.map((tool) => ({ tool, tier: tierOfTool(tool) }))
|
|
80
|
+
.sort((a, b) => a.tool.localeCompare(b.tool));
|
|
81
|
+
return {
|
|
82
|
+
observed: true,
|
|
83
|
+
cells: {
|
|
84
|
+
called: tally(called.map((entry) => entry.tier)),
|
|
85
|
+
neverCalled: tally(neverCalled.map((entry) => entry.tier)),
|
|
86
|
+
},
|
|
87
|
+
called,
|
|
88
|
+
neverCalled,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this verdict being drawn over code nobody said anything about?
|
|
3
|
+
*
|
|
4
|
+
* Declaring intent is opt-in, and nothing has ever noticed that a change landed and nobody said what
|
|
5
|
+
* it was supposed to make true. That is the moment it matters: a change with no declared intent can
|
|
6
|
+
* only be verified against itself, which is the definition of a false green.
|
|
7
|
+
*
|
|
8
|
+
* ## Why this fires on EVERY verdict, not once per change
|
|
9
|
+
*
|
|
10
|
+
* The house rule is that a gap nobody hit is a backlog, and a backlog reported as a finding is how
|
|
11
|
+
* an agent learns to stop reading findings. This clears that bar on the same test every other
|
|
12
|
+
* instrumentation gap clears it on: **did the absence make THIS verdict weaker?** An unwatched store
|
|
13
|
+
* weakens every state assertion made over it, not merely the first, so it is reported on every one.
|
|
14
|
+
* An undeclared change is the same shape — while nothing says what the edit was for, each verdict
|
|
15
|
+
* drawn after it is checked against nothing but itself, and that is true of the fifth one exactly as
|
|
16
|
+
* it was of the first. Reporting it once and then falling silent would leave four verdicts carrying
|
|
17
|
+
* a weakness they no longer disclose, which is the failure mode this whole surface exists to prevent.
|
|
18
|
+
*
|
|
19
|
+
* It is also not a backlog, because it is not a survey of the project: it is answered from what this
|
|
20
|
+
* session observed (an epoch that moved) about the verdict being returned right now, and it is
|
|
21
|
+
* closed by one call the agent can make immediately. A finding that stays open only because the
|
|
22
|
+
* agent has not made that call is a finding about the current verdict, not a chore.
|
|
23
|
+
*
|
|
24
|
+
* ## The silences, and why each one is required
|
|
25
|
+
*
|
|
26
|
+
* - **An edit was actually OBSERVED.** The epoch degrades to `NO_EDITS_OBSERVED` outside Vite, and
|
|
27
|
+
* that means "no edits observed", never "no edits happened" — a finding built on an absence
|
|
28
|
+
* Reticle cannot see would be a fabrication.
|
|
29
|
+
* - **The ledger holds nothing undischarged.** An open intent IS the declaration; silence is the
|
|
30
|
+
* correct response to it, and declaring one is what turns this off from the next verdict on.
|
|
31
|
+
*
|
|
32
|
+
* ## Why "covers the work" is answered as "anything open at all"
|
|
33
|
+
*
|
|
34
|
+
* The tempting answer is to match the intent's `surface` against the files that changed. Reticle
|
|
35
|
+
* cannot do that honestly: it observes that an epoch advanced, not WHAT advanced — the hot-update
|
|
36
|
+
* channel reports a counter, not a diff — so any file-level matching would be inference dressed as a
|
|
37
|
+
* fact, and it would fire on an agent that had declared its intent perfectly well. Over-reporting on
|
|
38
|
+
* a correct agent is the direction of error that trains it to stop reading findings.
|
|
39
|
+
*
|
|
40
|
+
* So the question asked is the coarse one, and it is asked honestly: has the agent declared anything
|
|
41
|
+
* that is still outstanding? If it has, it is working to a stated purpose and this stays quiet.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* `openIntents` is a thunk rather than a list because reading the ledger touches disk, and the epoch
|
|
45
|
+
* check answers without it for every page that never reported a hot update — which is most of them.
|
|
46
|
+
* Once an edit HAS been observed the read happens per verdict, and that is the intended cost: the
|
|
47
|
+
* ledger is the only thing that can end it, so it has to be re-asked each time.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isChangeUndeclared(
|
|
50
|
+
/** The epoch the session is under now. Undefined when the page never stamped one. */
|
|
51
|
+
currentEpoch: number | undefined, openIntents: () => Promise<readonly unknown[]>): Promise<boolean>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { NO_EDITS_OBSERVED } from '@reticlehq/core';
|
|
2
|
+
/**
|
|
3
|
+
* Is this verdict being drawn over code nobody said anything about?
|
|
4
|
+
*
|
|
5
|
+
* Declaring intent is opt-in, and nothing has ever noticed that a change landed and nobody said what
|
|
6
|
+
* it was supposed to make true. That is the moment it matters: a change with no declared intent can
|
|
7
|
+
* only be verified against itself, which is the definition of a false green.
|
|
8
|
+
*
|
|
9
|
+
* ## Why this fires on EVERY verdict, not once per change
|
|
10
|
+
*
|
|
11
|
+
* The house rule is that a gap nobody hit is a backlog, and a backlog reported as a finding is how
|
|
12
|
+
* an agent learns to stop reading findings. This clears that bar on the same test every other
|
|
13
|
+
* instrumentation gap clears it on: **did the absence make THIS verdict weaker?** An unwatched store
|
|
14
|
+
* weakens every state assertion made over it, not merely the first, so it is reported on every one.
|
|
15
|
+
* An undeclared change is the same shape — while nothing says what the edit was for, each verdict
|
|
16
|
+
* drawn after it is checked against nothing but itself, and that is true of the fifth one exactly as
|
|
17
|
+
* it was of the first. Reporting it once and then falling silent would leave four verdicts carrying
|
|
18
|
+
* a weakness they no longer disclose, which is the failure mode this whole surface exists to prevent.
|
|
19
|
+
*
|
|
20
|
+
* It is also not a backlog, because it is not a survey of the project: it is answered from what this
|
|
21
|
+
* session observed (an epoch that moved) about the verdict being returned right now, and it is
|
|
22
|
+
* closed by one call the agent can make immediately. A finding that stays open only because the
|
|
23
|
+
* agent has not made that call is a finding about the current verdict, not a chore.
|
|
24
|
+
*
|
|
25
|
+
* ## The silences, and why each one is required
|
|
26
|
+
*
|
|
27
|
+
* - **An edit was actually OBSERVED.** The epoch degrades to `NO_EDITS_OBSERVED` outside Vite, and
|
|
28
|
+
* that means "no edits observed", never "no edits happened" — a finding built on an absence
|
|
29
|
+
* Reticle cannot see would be a fabrication.
|
|
30
|
+
* - **The ledger holds nothing undischarged.** An open intent IS the declaration; silence is the
|
|
31
|
+
* correct response to it, and declaring one is what turns this off from the next verdict on.
|
|
32
|
+
*
|
|
33
|
+
* ## Why "covers the work" is answered as "anything open at all"
|
|
34
|
+
*
|
|
35
|
+
* The tempting answer is to match the intent's `surface` against the files that changed. Reticle
|
|
36
|
+
* cannot do that honestly: it observes that an epoch advanced, not WHAT advanced — the hot-update
|
|
37
|
+
* channel reports a counter, not a diff — so any file-level matching would be inference dressed as a
|
|
38
|
+
* fact, and it would fire on an agent that had declared its intent perfectly well. Over-reporting on
|
|
39
|
+
* a correct agent is the direction of error that trains it to stop reading findings.
|
|
40
|
+
*
|
|
41
|
+
* So the question asked is the coarse one, and it is asked honestly: has the agent declared anything
|
|
42
|
+
* that is still outstanding? If it has, it is working to a stated purpose and this stays quiet.
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* `openIntents` is a thunk rather than a list because reading the ledger touches disk, and the epoch
|
|
46
|
+
* check answers without it for every page that never reported a hot update — which is most of them.
|
|
47
|
+
* Once an edit HAS been observed the read happens per verdict, and that is the intended cost: the
|
|
48
|
+
* ledger is the only thing that can end it, so it has to be re-asked each time.
|
|
49
|
+
*/
|
|
50
|
+
export async function isChangeUndeclared(
|
|
51
|
+
/** The epoch the session is under now. Undefined when the page never stamped one. */
|
|
52
|
+
currentEpoch, openIntents) {
|
|
53
|
+
if (undefined === currentEpoch || NO_EDITS_OBSERVED === currentEpoch)
|
|
54
|
+
return false;
|
|
55
|
+
return 0 === (await openIntents()).length;
|
|
56
|
+
}
|
|
@@ -76,6 +76,22 @@ interface VerifiedInputs {
|
|
|
76
76
|
* Empty means nothing went unread.
|
|
77
77
|
*/
|
|
78
78
|
outcomeUnread?: readonly string[];
|
|
79
|
+
/**
|
|
80
|
+
* The declared consequence that held does not depend on the response body — an exact string
|
|
81
|
+
* rendered, a store path, a signal, a route, or an element located by role / name / testid. See
|
|
82
|
+
* `declaresBodyIndependentChannel`.
|
|
83
|
+
*
|
|
84
|
+
* The unread-body clause exists for the case where the body is the ONLY channel that could have
|
|
85
|
+
* contradicted the screen (a 200 with batch/GraphQL errors inside). When a body-independent
|
|
86
|
+
* declaration held, that is no longer true, and grading `unknown` costs a real verdict. The unread
|
|
87
|
+
* labels still ride out in `because`, the way absence-derived contradictions still ride out in
|
|
88
|
+
* `contradictions`.
|
|
89
|
+
*
|
|
90
|
+
* Narrow: it does not soften a 202, an observed contradiction, a dirty capture, or a net-only
|
|
91
|
+
* declaration. `declaredConsequence` alone is not enough — a `{ kind: "net", status: 200 }` is a
|
|
92
|
+
* declaration whose remaining channel IS the body.
|
|
93
|
+
*/
|
|
94
|
+
independentOfBody?: boolean;
|
|
79
95
|
/**
|
|
80
96
|
* What the wait was for and what the window held when it ended — read ONLY by the two clauses that
|
|
81
97
|
* answer UNSETTLED, which is the commonest reason a verdict comes back `unknown` and was also the
|
package/dist/honesty/verified.js
CHANGED
|
@@ -31,6 +31,24 @@ export function decideVerified(inputs) {
|
|
|
31
31
|
'to repeat',
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
+
// ABOVE the failure clause, for the third time and the same reason: absence is not evidence.
|
|
35
|
+
//
|
|
36
|
+
// Omitting `until` waits for the page to SETTLE. Nobody declared a consequence, so a settle that
|
|
37
|
+
// times out says "the page was still busy when I stopped looking" — the definition of an
|
|
38
|
+
// absence-derived finding. The failure clause below took it at face value and returned
|
|
39
|
+
// `verified: "no"` with `because: 'the declared consequence did not hold'`, naming a consequence
|
|
40
|
+
// that does not exist. Measured on the hard fixture, whose push updates mean it never goes idle:
|
|
41
|
+
// a healthy pagination click was graded NO. Any app with polling or a live feed gets accused of a
|
|
42
|
+
// defect for being alive, which is the false NEGATIVE the ABSENCE_DERIVED doctrine exists to stop.
|
|
43
|
+
if (false === pass && false === inputs.declaredConsequence) {
|
|
44
|
+
return {
|
|
45
|
+
verified: Verified.UNKNOWN,
|
|
46
|
+
verifiedReason: VerifiedReason.UNSETTLED,
|
|
47
|
+
because: 'no consequence was declared, so this only waited for the page to go idle and it never ' +
|
|
48
|
+
'did — that is a statement about when Reticle stopped looking, not about the app. Pass ' +
|
|
49
|
+
'`until` naming what the action should cause (a signal, a request, a route, or store state)',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
34
52
|
// A failed assertion is the most actionable fact there is; it leads — including over
|
|
35
53
|
// `alreadyTrue`, because a condition that held before AND fails now is a real regression.
|
|
36
54
|
if (false === pass) {
|
|
@@ -82,6 +100,27 @@ export function decideVerified(inputs) {
|
|
|
82
100
|
inputs.unsettled !== undefined &&
|
|
83
101
|
!openWrite &&
|
|
84
102
|
contradictions.every((c) => c.kind === ContradictionKind.REQUEST_NEVER_SETTLED);
|
|
103
|
+
// `signal-without-consequence` gets its own sentence, because the generic one is FALSE for it.
|
|
104
|
+
//
|
|
105
|
+
// That sentence says the window "closed before the app finished" and then explains that a poll or
|
|
106
|
+
// a timer kept the page busy. Nothing kept this page busy — nothing happened at all, which is the
|
|
107
|
+
// entire finding. Measured on the bench fixture: the correct UNKNOWN arrived wearing an
|
|
108
|
+
// explanation that told the agent to wait longer or assert something else, when the fact to act on
|
|
109
|
+
// was that the app announced a consequence it did not deliver. A right verdict with a wrong reason
|
|
110
|
+
// sends an agent to the wrong place, which costs as much as the wrong verdict did.
|
|
111
|
+
const signalOnly = contradictions.length > 0 &&
|
|
112
|
+
contradictions.every((c) => c.kind === ContradictionKind.SIGNAL_WITHOUT_CONSEQUENCE);
|
|
113
|
+
if (signalOnly) {
|
|
114
|
+
return {
|
|
115
|
+
verified: Verified.UNKNOWN,
|
|
116
|
+
verifiedReason: VerifiedReason.EVIDENCE_INCOMPLETE,
|
|
117
|
+
because: 'the app fired the signal you waited for and NOTHING else moved — no DOM, no store, no ' +
|
|
118
|
+
'route, no request — so the only evidence is the app saying so. A signal emitted from the ' +
|
|
119
|
+
'value the app was ASKED for, rather than the one it committed, looks exactly like this. ' +
|
|
120
|
+
'Check the consequence directly (reticle_snapshot for what rendered, reticle_state for what ' +
|
|
121
|
+
'the store holds) before trusting it',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
85
124
|
if (contradictions.length > 0 && !settlementOnly) {
|
|
86
125
|
const kinds = contradictions.map((c) => c.kind).join(', ');
|
|
87
126
|
return {
|
|
@@ -113,9 +152,24 @@ export function decideVerified(inputs) {
|
|
|
113
152
|
// with routeChanges 0, because the text was the nav link already on screen — the real navigation
|
|
114
153
|
// landed 1.8s later. UNKNOWN rather than NO on purpose: the app may well be fine, and reporting a
|
|
115
154
|
// failure we did not observe would be its own false claim.
|
|
155
|
+
//
|
|
156
|
+
// `no-fault` rather than `unknown`, once the window settled — the same distinction
|
|
157
|
+
// `nothing_declared` already draws, gated the same way.
|
|
158
|
+
//
|
|
159
|
+
// The two words ask for opposite things. `unknown` means LOOK AGAIN WITH BETTER COVERAGE, so an
|
|
160
|
+
// agent reading it reasonably concludes the engine could not see, and spends its remaining turns
|
|
161
|
+
// enabling capture, widening timeouts and re-driving — none of which can help, because the engine
|
|
162
|
+
// saw the whole window and there was nothing wrong with the evidence. The fault is in the
|
|
163
|
+
// ASSERTION: it named something that was true before the action, so it cannot tell success from a
|
|
164
|
+
// no-op. `no-fault` says exactly that, and `because` already carried the remedy that `unknown`
|
|
165
|
+
// was burying.
|
|
166
|
+
//
|
|
167
|
+
// Gated on `true === settled` for the same reason `nothing_declared` is: no-fault claims the whole
|
|
168
|
+
// window was seen, and a call that returned before the app stopped moving has not earned that.
|
|
169
|
+
// Unsettled stays `unknown`, which is then the honest word for it.
|
|
116
170
|
if (true === inputs.alreadyTrue) {
|
|
117
171
|
return {
|
|
118
|
-
verified: Verified.UNKNOWN,
|
|
172
|
+
verified: true === settled ? Verified.NO_FAULT : Verified.UNKNOWN,
|
|
119
173
|
verifiedReason: VerifiedReason.ALREADY_TRUE,
|
|
120
174
|
because: 'the declared consequence was already true before this action, so it proves nothing about it — assert something the action CHANGES (a signal, a request, a route, or store state)',
|
|
121
175
|
};
|
|
@@ -142,7 +196,21 @@ export function decideVerified(inputs) {
|
|
|
142
196
|
// Gated on `true === settled` rather than on a caller's say-so. A call that returned before the app
|
|
143
197
|
// stopped moving has not earned this, whatever it declared — which is what stops `no-fault` from
|
|
144
198
|
// becoming the green-forever button an always-available "nothing was wrong" would be.
|
|
145
|
-
|
|
199
|
+
// Two ways to have proved nothing, and only one of them used to be caught. Grade NONE is the
|
|
200
|
+
// engine's own reading — no signal, no request, no state change, nothing pinned. An explicit
|
|
201
|
+
// `declaredConsequence: false` is the CALLER saying it asserted nothing, which `act_and_wait`
|
|
202
|
+
// reports whenever `until` is omitted (an omitted `until` becomes `{kind:"settled"}` — a
|
|
203
|
+
// sleep-replacement, not an assertion).
|
|
204
|
+
//
|
|
205
|
+
// Grading alone missed the second case, because a click that merely moves the DOM lifts the grade
|
|
206
|
+
// to PRESENCE and walks past this branch into a full green. Measured live: clicking a submit
|
|
207
|
+
// button, and an inert text input, with no `until` both returned `verified:"yes"` /
|
|
208
|
+
// `verifiedReason:"proved"`. The claim it printed — "assertion held at presence grade" — was true
|
|
209
|
+
// about a grade and false about an assertion, because there was no assertion.
|
|
210
|
+
//
|
|
211
|
+
// It compounds: `reticle_context` persists proven claims, so an undeclared green becomes
|
|
212
|
+
// established fact for the agent that re-enters after a compaction.
|
|
213
|
+
if (honesty.grade === HonestyGrade.NONE || false === inputs.declaredConsequence) {
|
|
146
214
|
if (true === settled) {
|
|
147
215
|
return {
|
|
148
216
|
verified: Verified.NO_FAULT,
|
|
@@ -178,7 +246,15 @@ export function decideVerified(inputs) {
|
|
|
178
246
|
//
|
|
179
247
|
// UNKNOWN, not NO: nothing is known to have failed. The remedy is in the sentence, because an
|
|
180
248
|
// agent that cannot act on a caveat will learn to skip it.
|
|
181
|
-
|
|
249
|
+
//
|
|
250
|
+
// Skipped when the caller declared a consequence that does not depend on the body and it held.
|
|
251
|
+
// The clause was written for the body being the ONLY remaining channel; a unique row on screen, a
|
|
252
|
+
// store path, or a signal that held is a different channel, and grading unknown there cost a real
|
|
253
|
+
// verdict (a 201 plus the exact message text, agent went to enable capture instead of finishing).
|
|
254
|
+
// The unread write still rides out in `because`. A net-only declaration does not skip: then the
|
|
255
|
+
// body is still the remaining channel.
|
|
256
|
+
const unreadHeldIndependently = declaredHeld && true === inputs.independentOfBody;
|
|
257
|
+
if (outcomeUnread !== undefined && outcomeUnread.length > 0 && !unreadHeldIndependently) {
|
|
182
258
|
return {
|
|
183
259
|
verified: Verified.UNKNOWN,
|
|
184
260
|
verifiedReason: VerifiedReason.OUTCOME_UNREAD,
|
|
@@ -214,11 +290,14 @@ export function decideVerified(inputs) {
|
|
|
214
290
|
const notIdle = false === settled
|
|
215
291
|
? ', though the page never went idle — this rests on the consequence you declared, not on the page going quiet'
|
|
216
292
|
: '';
|
|
293
|
+
const unreadCaveat = unreadHeldIndependently && outcomeUnread !== undefined && outcomeUnread.length > 0
|
|
294
|
+
? `, though a write returned 2xx with a response body that was never recorded (${outcomeUnread.join('; ')}) — a 200 describes the transport, not the result`
|
|
295
|
+
: '';
|
|
217
296
|
return {
|
|
218
297
|
verified: Verified.YES,
|
|
219
298
|
verifiedReason: VerifiedReason.PROVED,
|
|
220
299
|
because: true === honesty.coverage?.partial
|
|
221
|
-
? `assertion held at ${honesty.grade} grade with no channel disagreeing, but coverage was PARTIAL — see \`coverage\` for what went unobserved${notIdle}`
|
|
222
|
-
: `assertion held at ${honesty.grade} grade over a clean capture with no channel disagreeing${notIdle}`,
|
|
300
|
+
? `assertion held at ${honesty.grade} grade with no channel disagreeing, but coverage was PARTIAL — see \`coverage\` for what went unobserved${notIdle}${unreadCaveat}`
|
|
301
|
+
: `assertion held at ${honesty.grade} grade over a clean capture with no channel disagreeing${notIdle}${unreadCaveat}`,
|
|
223
302
|
};
|
|
224
303
|
}
|
package/dist/http-server.d.ts
CHANGED
package/dist/http-server.js
CHANGED
|
@@ -6,6 +6,7 @@ import { noteAgentPeer, PEER_VERSION_PARAM, PEER_CONTRACT_PARAM } from './versio
|
|
|
6
6
|
import { log } from './log.js';
|
|
7
7
|
import { reportMcpConnected } from './telemetry/mcp-connection.js';
|
|
8
8
|
import { isLoopbackHost, isLocalWebOrigin, isLoopbackPeer, requestToken, tokensMatch, } from './bridge/token-auth.js';
|
|
9
|
+
import { LOOPBACK_IDLE_MS } from './loopback-agent.js';
|
|
9
10
|
/**
|
|
10
11
|
* Creates a shared HTTP server that handles both the WebSocket bridge (browser SDK) and the
|
|
11
12
|
* SSE MCP transport (Claude/agent). Does NOT call listen — caller controls that.
|
|
@@ -258,6 +259,17 @@ export function createSharedServer(options = {}) {
|
|
|
258
259
|
});
|
|
259
260
|
});
|
|
260
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* The daemon's half of the keep-alive pact with `loopbackAgent`.
|
|
264
|
+
*
|
|
265
|
+
* Node's default is 5 seconds, and the proxy's POST leg is one request per JSON-RPC message. An
|
|
266
|
+
* agent thinks for longer than 5s between tool calls essentially always, so with the default every
|
|
267
|
+
* tool call had to open a fresh socket however the client was configured — which on Windows ends
|
|
268
|
+
* in `ENOBUFS`. This has to stay ABOVE `LOOPBACK_IDLE_MS` so the CLIENT is always the side that
|
|
269
|
+
* retires an idle socket: if the server closed first, the proxy would keep picking up sockets that
|
|
270
|
+
* were already gone. It also has to stay below Node's 60s default `headersTimeout`.
|
|
271
|
+
*/
|
|
272
|
+
httpServer.keepAliveTimeout = LOOPBACK_IDLE_MS + 10_000;
|
|
261
273
|
return {
|
|
262
274
|
httpServer,
|
|
263
275
|
attachMcp,
|
|
@@ -330,7 +342,7 @@ function schemeOf(url) {
|
|
|
330
342
|
return undefined;
|
|
331
343
|
}
|
|
332
344
|
}
|
|
333
|
-
|
|
345
|
+
function driveSchemeRefusal(url) {
|
|
334
346
|
const scheme = schemeOf(url);
|
|
335
347
|
if (scheme !== undefined && DRIVEABLE_SCHEMES.includes(scheme))
|
|
336
348
|
return undefined;
|
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
* Pure functions over already-collected reports — the driving is `reticle_crawl`'s job, and running
|
|
19
19
|
* it across a commit range is a shell loop. This is only the part that must not be done by eye.
|
|
20
20
|
*/
|
|
21
|
-
/** The contradiction kinds — cross-channel disagreement, as opposed to a single-channel fault. */
|
|
22
|
-
export declare const CONTRADICTION_KINDS: readonly string[];
|
|
23
21
|
export interface HuntAnomaly {
|
|
24
22
|
kind: string;
|
|
25
23
|
ref?: string;
|
package/dist/hunt/hunt-report.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* it across a commit range is a shell loop. This is only the part that must not be done by eye.
|
|
20
20
|
*/
|
|
21
21
|
/** The contradiction kinds — cross-channel disagreement, as opposed to a single-channel fault. */
|
|
22
|
-
|
|
22
|
+
const CONTRADICTION_KINDS = [
|
|
23
23
|
'ui-advanced-request-failed',
|
|
24
24
|
'signal-contradicted',
|
|
25
25
|
'response-ignored',
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import { type ImpactCounts, type ImpactSnapshot } from '@reticlehq/core';
|
|
1
|
+
import { type ImpactCounts, type ImpactDefect, type ImpactSnapshot } from '@reticlehq/core';
|
|
2
2
|
import { ImpactStore, type ImpactFoldMeta } from './impact-store.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Wire the DEFAULT root — the daemon's own. Idempotent; the first one wins.
|
|
5
|
+
*
|
|
6
|
+
* Still idempotent, and still first-wins, because the daemon's own root genuinely does not change
|
|
7
|
+
* while it runs. What changed is that this is no longer the ONLY root: a call that knows its
|
|
8
|
+
* project passes one, and this is the fallback for calls that do not.
|
|
9
|
+
*/
|
|
4
10
|
export declare function initImpact(opts: {
|
|
5
11
|
reticleRoot: string | undefined;
|
|
6
12
|
projectName?: string;
|
|
7
13
|
now?: () => number;
|
|
8
14
|
}): ImpactStore | undefined;
|
|
9
|
-
/** The live store, if one has been initialised. */
|
|
10
|
-
export declare function impactStore(): ImpactStore | undefined;
|
|
11
|
-
/** Test seam: drop the
|
|
15
|
+
/** The live store for a root (default when omitted), if one has been initialised. */
|
|
16
|
+
export declare function impactStore(root?: string): ImpactStore | undefined;
|
|
17
|
+
/** Test seam: drop the stores so a suite can start from a clean record. */
|
|
12
18
|
export declare function resetImpactForTest(): void;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Record one delta against a project's ledger. Never throws: stats must not break a tool call.
|
|
21
|
+
*
|
|
22
|
+
* `root` is the `.reticle` directory this call's evidence belongs to. Omitted means "could not tell
|
|
23
|
+
* which project", which falls back to the daemon's own root — the old behaviour, kept for the paths
|
|
24
|
+
* that genuinely have no session to ask.
|
|
25
|
+
*/
|
|
26
|
+
export declare function recordImpact(delta: Partial<ImpactCounts>, meta?: ImpactFoldMeta, root?: string): void;
|
|
27
|
+
export declare function impactSnapshot(root?: string): ImpactSnapshot | undefined;
|
|
16
28
|
/**
|
|
17
29
|
* What one tool call did, as counters.
|
|
18
30
|
*
|
|
@@ -21,3 +33,14 @@ export declare function impactSnapshot(): ImpactSnapshot | undefined;
|
|
|
21
33
|
* the same rule the product states to agents - everything else moves or reads the app.
|
|
22
34
|
*/
|
|
23
35
|
export declare function deltaForToolResult(raw: unknown, durationMs: number, refused: boolean): Partial<ImpactCounts>;
|
|
36
|
+
/**
|
|
37
|
+
* What one FAILED verdict was, in a line.
|
|
38
|
+
*
|
|
39
|
+
* Only a verified:"no" produces one. That is the same rule the counters follow and the same rule
|
|
40
|
+
* the product states to agents: an "unknown" is not a defect, it is Reticle admitting it could not
|
|
41
|
+
* tell — recording those here would fill the user's short list with the tool's own blind spots.
|
|
42
|
+
*
|
|
43
|
+
* Everything is read from the result the tool already returned, so this costs nothing extra and
|
|
44
|
+
* cannot disagree with the verdict it describes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function defectForToolResult(raw: unknown, at: number): ImpactDefect | undefined;
|
|
@@ -1,43 +1,90 @@
|
|
|
1
|
-
import { Verified } from '@reticlehq/core';
|
|
1
|
+
import { Verified, } from '@reticlehq/core';
|
|
2
2
|
import { estimateTokens } from '../session/output-budget.js';
|
|
3
3
|
import { ImpactStore } from './impact-store.js';
|
|
4
4
|
/**
|
|
5
|
-
* The daemon's
|
|
5
|
+
* The daemon's impact recorders — ONE PER PROJECT ROOT.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* This was a single module-level store, and "the first root wins for the daemon's lifetime" was
|
|
8
|
+
* written down as if it were a design decision. It is only correct for a daemon that serves one
|
|
9
|
+
* project, and this daemon has never been that: `artifactRootFor` exists precisely because one
|
|
10
|
+
* daemon serves many, and nine modules already route their artifacts by the SESSION's project.
|
|
11
|
+
*
|
|
12
|
+
* Measured, not theorised: with a daemon rooted at repo A, two verdicts driven against app B landed
|
|
13
|
+
* in A's `impact.json` — B's own ledger never moved — and A's cloud link then carried them to a
|
|
14
|
+
* PRODUCTION dashboard belonging to a different account than B was linked to. The impact ledger is
|
|
15
|
+
* what the whole dashboard is computed from, so this was the one artifact whose misrouting was
|
|
16
|
+
* guaranteed to be visible and billed.
|
|
17
|
+
*
|
|
18
|
+
* Keyed by root path, so two projects driven through one daemon keep separate ledgers, and the
|
|
19
|
+
* daemon's own root remains the answer for anything that cannot name a project.
|
|
20
|
+
*/
|
|
21
|
+
/** One store per `.reticle` root. */
|
|
22
|
+
const stores = new Map();
|
|
23
|
+
/** The daemon's own root — used when a call cannot say which project it was for. */
|
|
24
|
+
let defaultRoot;
|
|
25
|
+
/** Held so a per-root store is built with the same clock and project name as the default. */
|
|
26
|
+
let storeOpts = {};
|
|
27
|
+
/** Get-or-create the store for one root. */
|
|
28
|
+
function storeFor(root) {
|
|
29
|
+
const key = root ?? defaultRoot;
|
|
30
|
+
if (key === undefined || 0 === key.length)
|
|
31
|
+
return undefined;
|
|
32
|
+
let found = stores.get(key);
|
|
33
|
+
if (found === undefined) {
|
|
34
|
+
found = new ImpactStore({ ...storeOpts, reticleRoot: key });
|
|
35
|
+
stores.set(key, found);
|
|
36
|
+
}
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wire the DEFAULT root — the daemon's own. Idempotent; the first one wins.
|
|
41
|
+
*
|
|
42
|
+
* Still idempotent, and still first-wins, because the daemon's own root genuinely does not change
|
|
43
|
+
* while it runs. What changed is that this is no longer the ONLY root: a call that knows its
|
|
44
|
+
* project passes one, and this is the fallback for calls that do not.
|
|
11
45
|
*/
|
|
12
|
-
let store;
|
|
13
|
-
/** Wire the recorder to a project. Idempotent; the first root wins for the daemon's lifetime. */
|
|
14
46
|
export function initImpact(opts) {
|
|
15
47
|
// No root, no record. Programmatic callers and test doubles build their own deps and are not
|
|
16
48
|
// obliged to carry one, and a courtesy counter must never be the reason a tool call throws.
|
|
17
49
|
if (opts.reticleRoot === undefined || 0 === opts.reticleRoot.length)
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
50
|
+
return storeFor(undefined);
|
|
51
|
+
if (defaultRoot === undefined) {
|
|
52
|
+
defaultRoot = opts.reticleRoot;
|
|
53
|
+
const { projectName, now } = opts;
|
|
54
|
+
storeOpts = {
|
|
55
|
+
...(projectName === undefined ? {} : { projectName }),
|
|
56
|
+
...(now === undefined ? {} : { now }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return storeFor(opts.reticleRoot);
|
|
21
60
|
}
|
|
22
|
-
/** The live store, if one has been initialised. */
|
|
23
|
-
export function impactStore() {
|
|
24
|
-
return
|
|
61
|
+
/** The live store for a root (default when omitted), if one has been initialised. */
|
|
62
|
+
export function impactStore(root) {
|
|
63
|
+
return storeFor(root);
|
|
25
64
|
}
|
|
26
|
-
/** Test seam: drop the
|
|
65
|
+
/** Test seam: drop the stores so a suite can start from a clean record. */
|
|
27
66
|
export function resetImpactForTest() {
|
|
28
|
-
|
|
67
|
+
stores.clear();
|
|
68
|
+
defaultRoot = undefined;
|
|
69
|
+
storeOpts = {};
|
|
29
70
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Record one delta against a project's ledger. Never throws: stats must not break a tool call.
|
|
73
|
+
*
|
|
74
|
+
* `root` is the `.reticle` directory this call's evidence belongs to. Omitted means "could not tell
|
|
75
|
+
* which project", which falls back to the daemon's own root — the old behaviour, kept for the paths
|
|
76
|
+
* that genuinely have no session to ask.
|
|
77
|
+
*/
|
|
78
|
+
export function recordImpact(delta, meta = {}, root) {
|
|
32
79
|
try {
|
|
33
|
-
|
|
80
|
+
storeFor(root)?.record(delta, meta);
|
|
34
81
|
}
|
|
35
82
|
catch {
|
|
36
83
|
// Counting is a courtesy to the user; failing to count is not worth failing their call over.
|
|
37
84
|
}
|
|
38
85
|
}
|
|
39
|
-
export function impactSnapshot() {
|
|
40
|
-
return
|
|
86
|
+
export function impactSnapshot(root) {
|
|
87
|
+
return storeFor(root)?.snapshot();
|
|
41
88
|
}
|
|
42
89
|
/**
|
|
43
90
|
* What one tool call did, as counters.
|
|
@@ -71,6 +118,42 @@ export function deltaForToolResult(raw, durationMs, refused) {
|
|
|
71
118
|
}
|
|
72
119
|
return delta;
|
|
73
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* What one FAILED verdict was, in a line.
|
|
123
|
+
*
|
|
124
|
+
* Only a verified:"no" produces one. That is the same rule the counters follow and the same rule
|
|
125
|
+
* the product states to agents: an "unknown" is not a defect, it is Reticle admitting it could not
|
|
126
|
+
* tell — recording those here would fill the user's short list with the tool's own blind spots.
|
|
127
|
+
*
|
|
128
|
+
* Everything is read from the result the tool already returned, so this costs nothing extra and
|
|
129
|
+
* cannot disagree with the verdict it describes.
|
|
130
|
+
*/
|
|
131
|
+
export function defectForToolResult(raw, at) {
|
|
132
|
+
if (!isRecord(raw) || raw['verified'] !== Verified.NO)
|
|
133
|
+
return undefined;
|
|
134
|
+
const effect = isRecord(raw['effect']) ? raw['effect'] : undefined;
|
|
135
|
+
const verdict = isRecord(raw['verdict']) ? raw['verdict'] : undefined;
|
|
136
|
+
const named = text(effect?.['name']) ?? text(effect?.['testid']) ?? text(effect?.['component']);
|
|
137
|
+
const why = text(verdict?.['failureReason']) ?? text(raw['because']);
|
|
138
|
+
const defect = {
|
|
139
|
+
at,
|
|
140
|
+
// The control that was acted on, when the result names one — "Sign In did not sign in" is a
|
|
141
|
+
// sentence somebody can act on; "a verdict failed" is not.
|
|
142
|
+
title: named === undefined ? (why ?? 'a declared consequence did not hold') : named,
|
|
143
|
+
};
|
|
144
|
+
if (named !== undefined && why !== undefined)
|
|
145
|
+
defect.detail = why;
|
|
146
|
+
const source = text(raw['source']);
|
|
147
|
+
if (source !== undefined)
|
|
148
|
+
defect.source = source;
|
|
149
|
+
return defect;
|
|
150
|
+
}
|
|
151
|
+
function text(value) {
|
|
152
|
+
if ('string' !== typeof value)
|
|
153
|
+
return undefined;
|
|
154
|
+
const trimmed = value.trim();
|
|
155
|
+
return 0 === trimmed.length ? undefined : trimmed;
|
|
156
|
+
}
|
|
74
157
|
function isRecord(value) {
|
|
75
158
|
return 'object' === typeof value && null !== value;
|
|
76
159
|
}
|