@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
|
@@ -123,6 +123,12 @@ export class SessionMetrics {
|
|
|
123
123
|
* call a tool, and of the sessions that made exactly one call, most bounced on this.
|
|
124
124
|
*/
|
|
125
125
|
#noSessionErrors = 0;
|
|
126
|
+
/** Connection-level MCP POST failures the SSE stream cannot see. See SessionSummary. */
|
|
127
|
+
#postSocketFailures = 0;
|
|
128
|
+
#lifetimePostSocketFailures = 0;
|
|
129
|
+
/** Retries of those POSTs that then delivered. */
|
|
130
|
+
#postRetriesSaved = 0;
|
|
131
|
+
#lifetimePostRetriesSaved = 0;
|
|
126
132
|
/** Longest back-to-back run per tool — a retry loop, which `toolCounts` cannot distinguish. */
|
|
127
133
|
#repeatRuns = new Map();
|
|
128
134
|
#lastTool;
|
|
@@ -325,6 +331,24 @@ export class SessionMetrics {
|
|
|
325
331
|
tool: site,
|
|
326
332
|
});
|
|
327
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* The MCP proxy could not POST a tool call because the TCP socket itself failed (`ENOBUFS`,
|
|
336
|
+
* `EMFILE`, `ECONNREFUSED` before any bytes left). The SSE stream is still up, so this is
|
|
337
|
+
* invisible to `mcp_connection_lost`, and the handler never ran, so it is invisible to
|
|
338
|
+
* `tool_refused`. Counted so a dashboard can see it without a new event kind.
|
|
339
|
+
*/
|
|
340
|
+
recordPostSocketFailure() {
|
|
341
|
+
this.#postSocketFailures += 1;
|
|
342
|
+
this.#lifetimePostSocketFailures += 1;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* A bounded retry of an unsent POST then delivered. The pair against `recordPostSocketFailure`:
|
|
346
|
+
* the retry that quietly saved a call is a different fact from the one that never ran.
|
|
347
|
+
*/
|
|
348
|
+
recordPostRetrySaved() {
|
|
349
|
+
this.#postRetriesSaved += 1;
|
|
350
|
+
this.#lifetimePostRetriesSaved += 1;
|
|
351
|
+
}
|
|
328
352
|
/** The agent's recent approach run + the call in flight — context for a crash report. */
|
|
329
353
|
get trail() {
|
|
330
354
|
return { breadcrumb: [...this.#breadcrumb], inFlight: this.#inFlight };
|
|
@@ -469,12 +493,16 @@ export class SessionMetrics {
|
|
|
469
493
|
toolErrors: this.#lifetimeToolErrors,
|
|
470
494
|
verifications: this.#lifetimeVerifications,
|
|
471
495
|
bugsFound: this.#lifetimeBugsFound,
|
|
496
|
+
postSocketFailures: this.#lifetimePostSocketFailures,
|
|
497
|
+
postRetriesSaved: this.#lifetimePostRetriesSaved,
|
|
472
498
|
}
|
|
473
499
|
: {
|
|
474
500
|
toolCalls: this.#toolCalls,
|
|
475
501
|
toolErrors: this.#toolErrors,
|
|
476
502
|
verifications: this.#verifications,
|
|
477
503
|
bugsFound: this.#bugsFound,
|
|
504
|
+
postSocketFailures: this.#postSocketFailures,
|
|
505
|
+
postRetriesSaved: this.#postRetriesSaved,
|
|
478
506
|
};
|
|
479
507
|
return {
|
|
480
508
|
durationMs: Math.max(0, this.#now() - this.#startedAt),
|
|
@@ -488,6 +516,8 @@ export class SessionMetrics {
|
|
|
488
516
|
bugsFound: scalars.bugsFound,
|
|
489
517
|
...(this.#bugKinds.size > 0 ? { bugKinds: Object.fromEntries(this.#bugKinds) } : {}),
|
|
490
518
|
...(this.#noSessionErrors > 0 ? { noSessionErrors: this.#noSessionErrors } : {}),
|
|
519
|
+
...(scalars.postSocketFailures > 0 ? { postSocketFailures: scalars.postSocketFailures } : {}),
|
|
520
|
+
...(scalars.postRetriesSaved > 0 ? { postRetriesSaved: scalars.postRetriesSaved } : {}),
|
|
491
521
|
...(this.#repeatRuns.size > 0
|
|
492
522
|
? { consecutiveRepeats: Object.fromEntries(this.#repeatRuns) }
|
|
493
523
|
: {}),
|
|
@@ -588,7 +618,9 @@ export class SessionMetrics {
|
|
|
588
618
|
0 === this.#verifications &&
|
|
589
619
|
0 === this.#toolErrors &&
|
|
590
620
|
0 === this.#bugsFound &&
|
|
591
|
-
0 === this.#sdkFailures
|
|
621
|
+
0 === this.#sdkFailures &&
|
|
622
|
+
0 === this.#postSocketFailures &&
|
|
623
|
+
0 === this.#postRetriesSaved);
|
|
592
624
|
}
|
|
593
625
|
/** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
|
|
594
626
|
reset() {
|
|
@@ -596,6 +628,8 @@ export class SessionMetrics {
|
|
|
596
628
|
this.#toolErrors = 0;
|
|
597
629
|
this.#verifications = 0;
|
|
598
630
|
this.#noSessionErrors = 0;
|
|
631
|
+
this.#postSocketFailures = 0;
|
|
632
|
+
this.#postRetriesSaved = 0;
|
|
599
633
|
this.#repeatRuns.clear();
|
|
600
634
|
this.#lastTool = undefined;
|
|
601
635
|
this.#currentRun = 0;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - it is best-effort and non-blocking: a send failure NEVER changes what the tool does.
|
|
10
10
|
*
|
|
11
11
|
* Events are `@reticlehq/core`'s `TelemetryEventSchema`, mapped at the wire into PostHog's capture
|
|
12
|
-
* format (https://posthog.com/docs/api/capture) — PostHog is the analytics backend until
|
|
12
|
+
* format (https://posthog.com/docs/api/capture) — PostHog is the analytics backend until the hosted service
|
|
13
13
|
* grows its own; a project API key is WRITE-ONLY by design, so embedding it in an OSS client is safe.
|
|
14
14
|
* Everything here is wrapped so a telemetry bug can never surface to a user — a broken metric must not
|
|
15
15
|
* break a verification.
|
|
@@ -76,8 +76,23 @@ export const FEEDBACK_RETRY_BACKOFF_MS = 750;
|
|
|
76
76
|
* (`reticle version`/`gate`) ~800ms. Long-lived daemon events still send in-process: a spawn per tool
|
|
77
77
|
* call would be far heavier than a fetch inside an already-running server.
|
|
78
78
|
*/
|
|
79
|
+
/**
|
|
80
|
+
* The budget the DISOWNED child gets, and why it is not `SEND_TIMEOUT_MS`.
|
|
81
|
+
*
|
|
82
|
+
* 2s is right for an in-process send: the daemon is waiting on it, so the budget is a latency cost
|
|
83
|
+
* somebody pays. A detached child pays nobody — the parent has already exited — so the only thing a
|
|
84
|
+
* short budget there buys is a lost event.
|
|
85
|
+
*
|
|
86
|
+
* And it loses the worst ones. Every detached send comes from a SHORT-LIVED CLI process, which pays
|
|
87
|
+
* cold DNS and a cold TLS route by definition; measured on the feedback path with a WARM cache it
|
|
88
|
+
* was already 0.694s before any payload moved. `reticle_installed` is the extreme case: it fires
|
|
89
|
+
* once per machine, on the first Reticle command that machine ever runs, which is the single coldest
|
|
90
|
+
* network call it will ever make to the collector — so the top of the funnel is the event most
|
|
91
|
+
* likely to be dropped, and it fails silently like everything else here.
|
|
92
|
+
*/
|
|
93
|
+
const DETACHED_SEND_TIMEOUT_MS = 15_000;
|
|
79
94
|
const DETACHED_SEND_SCRIPT = "fetch(process.argv[1],{method:'POST',headers:{'content-type':'application/json'}," +
|
|
80
|
-
`body:process.argv[2],signal:AbortSignal.timeout(${
|
|
95
|
+
`body:process.argv[2],signal:AbortSignal.timeout(${DETACHED_SEND_TIMEOUT_MS})})` +
|
|
81
96
|
'.catch(()=>{}).finally(()=>process.exit(0))';
|
|
82
97
|
/** Env var names — mirror cloud-sync's `RETICLE_*` convention. */
|
|
83
98
|
import { isReticleSourceCheckout } from './dev-repo.js';
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* Emitted from `runTool`, which is the one place both dispatch paths cross, so a tool added later is
|
|
11
11
|
* covered without anybody remembering to cover it.
|
|
12
12
|
*/
|
|
13
|
-
import { type
|
|
13
|
+
import { RefusalReason, type NoSessionReason } from '@reticlehq/core';
|
|
14
|
+
/** Record the branch code for the no-session error being thrown right now. */
|
|
15
|
+
export declare function notePendingNoSessionReason(reason: NoSessionReason | undefined): void;
|
|
14
16
|
/** A call that was served. Breaks the retry chain: what follows it is not a retry of anything. */
|
|
15
17
|
export declare function noteToolServed(): void;
|
|
16
18
|
/** One refused call, reported with its cause and whether it was itself a retry. */
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Emitted from `runTool`, which is the one place both dispatch paths cross, so a tool added later is
|
|
11
11
|
* covered without anybody remembering to cover it.
|
|
12
12
|
*/
|
|
13
|
-
import { TelemetryActor, TelemetryEventKind } from '@reticlehq/core';
|
|
13
|
+
import { RefusalReason, TelemetryActor, TelemetryEventKind, } from '@reticlehq/core';
|
|
14
14
|
import { getTelemetry } from './telemetry.js';
|
|
15
15
|
/**
|
|
16
16
|
* Refusal events one daemon run will send.
|
|
@@ -32,6 +32,24 @@ let sent = 0;
|
|
|
32
32
|
* describe — so the flag lands on the RETRY instead of on the first refusal.
|
|
33
33
|
*/
|
|
34
34
|
let lastRefusedTool;
|
|
35
|
+
/**
|
|
36
|
+
* The no-session branch code for the refusal about to be reported, set by whoever threw it.
|
|
37
|
+
*
|
|
38
|
+
* `no_session` is the largest refusal cohort and on its own it is a set difference: nothing
|
|
39
|
+
* connected, and no word on which of several opposite situations that was. The diagnosis that CAN
|
|
40
|
+
* say is computed where the error is thrown, and the refusal is classified downstream from the
|
|
41
|
+
* message, so the two never met (#615).
|
|
42
|
+
*
|
|
43
|
+
* Module-level and consumed once, like `lastRefusedTool` above and for the same reason: the
|
|
44
|
+
* producer and the consumer are one hop apart on the same call, and threading a field through every
|
|
45
|
+
* refusal path to carry it would touch a dozen call sites that have nothing to do with sessions.
|
|
46
|
+
* Cleared on read so a later refusal cannot inherit a stale reason.
|
|
47
|
+
*/
|
|
48
|
+
let pendingNoSessionReason;
|
|
49
|
+
/** Record the branch code for the no-session error being thrown right now. */
|
|
50
|
+
export function notePendingNoSessionReason(reason) {
|
|
51
|
+
pendingNoSessionReason = reason;
|
|
52
|
+
}
|
|
35
53
|
/** A call that was served. Breaks the retry chain: what follows it is not a retry of anything. */
|
|
36
54
|
export function noteToolServed() {
|
|
37
55
|
lastRefusedTool = undefined;
|
|
@@ -40,16 +58,30 @@ export function noteToolServed() {
|
|
|
40
58
|
export function reportToolRefused(tool, reason) {
|
|
41
59
|
const retried = lastRefusedTool === tool;
|
|
42
60
|
lastRefusedTool = tool;
|
|
61
|
+
// Read and clear whatever the throw left, whether or not this refusal can use it: a reason left
|
|
62
|
+
// sitting would attach itself to an unrelated refusal later in the session.
|
|
63
|
+
const noSessionReason = pendingNoSessionReason;
|
|
64
|
+
pendingNoSessionReason = undefined;
|
|
43
65
|
if (sent >= MAX_REFUSALS_PER_SESSION)
|
|
44
66
|
return;
|
|
45
67
|
sent += 1;
|
|
46
68
|
void getTelemetry().emit(TelemetryEventKind.TOOL_REFUSED, {
|
|
47
69
|
actor: TelemetryActor.AGENT,
|
|
48
|
-
refusal: {
|
|
70
|
+
refusal: {
|
|
71
|
+
tool,
|
|
72
|
+
reason,
|
|
73
|
+
retried,
|
|
74
|
+
// Only where it means something. On any other refusal reason this field would be answering
|
|
75
|
+
// a question nobody asked of it.
|
|
76
|
+
...(RefusalReason.NO_SESSION === reason && noSessionReason !== undefined
|
|
77
|
+
? { noSessionReason }
|
|
78
|
+
: {}),
|
|
79
|
+
},
|
|
49
80
|
});
|
|
50
81
|
}
|
|
51
82
|
/** Tests only — the counters are process-lifetime, so each case has to start from zero. */
|
|
52
83
|
export function resetToolRefusals() {
|
|
53
84
|
sent = 0;
|
|
54
85
|
lastRefusedTool = undefined;
|
|
86
|
+
pendingNoSessionReason = undefined;
|
|
55
87
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* be readable and testable on its own rather than inferred from a ternary inside a dispatcher.
|
|
14
14
|
*/
|
|
15
15
|
import { CaptureLoss, VerifiedReason } from '@reticlehq/core';
|
|
16
|
-
import {
|
|
16
|
+
import { VERDICT_TOOLS } from '../tools/feedback-tools.js';
|
|
17
17
|
import { getBrowserMode } from './browser-mode.js';
|
|
18
18
|
/**
|
|
19
19
|
* The deciding clause, read off the result `decideVerified` wrote it onto.
|
|
@@ -92,7 +92,7 @@ function passedOf(result, status) {
|
|
|
92
92
|
* it verified. That is the product's thesis reduced to a boolean.
|
|
93
93
|
*/
|
|
94
94
|
export function verificationOf(toolName, result, durationMs, brand) {
|
|
95
|
-
if (!
|
|
95
|
+
if (!VERDICT_TOOLS.has(toolName))
|
|
96
96
|
return undefined;
|
|
97
97
|
// A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
|
|
98
98
|
// verdict field so the agent never reads undefined off it, and that field must not be mistaken
|
|
@@ -17,6 +17,16 @@ interface CapsuleSaveInputs {
|
|
|
17
17
|
file: string;
|
|
18
18
|
line: number;
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The `.reticle` this capsule belongs in, from the session the act ACTUALLY drove.
|
|
22
|
+
*
|
|
23
|
+
* Passed in rather than re-resolved here, because re-resolving loses twice: `args.sessionId` is
|
|
24
|
+
* frequently absent, and `sessions.resolve(undefined)` throws whenever more than one tab is
|
|
25
|
+
* connected — which is precisely the multi-project case this routing exists for, so it failed in
|
|
26
|
+
* exactly the situation it was written to fix. The caller also knows about a session the act
|
|
27
|
+
* FOLLOWED through a navigation; this file cannot.
|
|
28
|
+
*/
|
|
29
|
+
root?: string | undefined;
|
|
20
30
|
}
|
|
21
31
|
/** Returns the capsule id when one was written, or undefined when there was nothing to save. */
|
|
22
32
|
export declare function saveFailedAssertCapsule(inputs: CapsuleSaveInputs): Promise<string | undefined>;
|
|
@@ -13,16 +13,22 @@ import { ActionType, AnchorKind } from '@reticlehq/core';
|
|
|
13
13
|
import { CapsuleStore, capsuleId, CAPSULE_VERSION } from '../capsule/capsule-store.js';
|
|
14
14
|
import { ReticleTool } from './tool-names.js';
|
|
15
15
|
import { asRecord, asString } from './tools-helpers.js';
|
|
16
|
+
import { sessionRoot } from '../project/session-root.js';
|
|
16
17
|
/** Returns the capsule id when one was written, or undefined when there was nothing to save. */
|
|
17
18
|
export async function saveFailedAssertCapsule(inputs) {
|
|
18
|
-
const { deps, verdict, capsule, links, args, actResult, actedSource } = inputs;
|
|
19
|
+
const { deps, verdict, capsule, links, args, actResult, actedSource, root: given } = inputs;
|
|
19
20
|
if (verdict.pass || capsule === undefined)
|
|
20
21
|
return undefined;
|
|
21
22
|
const id = capsuleId(deps.now(), asString(args['ref']) ?? 'assert');
|
|
22
23
|
const expectedText = links
|
|
23
24
|
.map((l) => ('name' in l ? `${l.kind} ${String(l.name)}` : l.kind))
|
|
24
25
|
.join(' AND ');
|
|
25
|
-
|
|
26
|
+
// The capsule belongs to the project whose app just failed, not to wherever the daemon stands.
|
|
27
|
+
// Same rule and same resolver as every other artifact: a capsule written into a sibling repo is
|
|
28
|
+
// evidence filed against a codebase that did not produce it, and it outlives the turn.
|
|
29
|
+
// The caller's answer wins; the resolver is the fallback for callers that have no session.
|
|
30
|
+
const root = given ?? sessionRoot(deps, asString(args['sessionId']));
|
|
31
|
+
const saved = await new CapsuleStore(deps.fs, root).save({
|
|
26
32
|
version: CAPSULE_VERSION,
|
|
27
33
|
id,
|
|
28
34
|
createdAt: deps.now(),
|
|
@@ -2,7 +2,8 @@ import { ActionType } from '@reticlehq/core';
|
|
|
2
2
|
export declare function assertNotDestructive(action: ActionType, innerArgs: Record<string, unknown>, inspected: unknown): void;
|
|
3
3
|
/**
|
|
4
4
|
* A drag is judged on BOTH ends. Dropping a harmless row onto "Delete" is destructive, and the
|
|
5
|
-
* source alone never says so.
|
|
5
|
+
* source alone never says so. Each end is classified on its own text and role, so a Payment
|
|
6
|
+
* option dragged onto Save is not a payment.
|
|
6
7
|
*/
|
|
7
8
|
export declare function assertDragNotDestructive(innerArgs: Record<string, unknown>, from: unknown, to: unknown): void;
|
|
8
9
|
/**
|
package/dist/tools/act-danger.js
CHANGED
|
@@ -22,23 +22,31 @@ function descriptorText(value) {
|
|
|
22
22
|
asString(descriptor['formText']) ?? '',
|
|
23
23
|
].join(' ');
|
|
24
24
|
}
|
|
25
|
+
function descriptorRole(value) {
|
|
26
|
+
const role = asString(asRecord(value)['role']);
|
|
27
|
+
return role !== undefined && role.length > 0 ? role : undefined;
|
|
28
|
+
}
|
|
29
|
+
function isDestructiveDescriptor(value) {
|
|
30
|
+
return isDangerousActionText(descriptorText(value), descriptorRole(value));
|
|
31
|
+
}
|
|
25
32
|
export function assertNotDestructive(action, innerArgs, inspected) {
|
|
26
33
|
if (action !== ActionType.CLICK && action !== ActionType.DBLCLICK)
|
|
27
34
|
return;
|
|
28
35
|
if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
29
36
|
return;
|
|
30
|
-
if (!
|
|
37
|
+
if (!isDestructiveDescriptor(inspected))
|
|
31
38
|
return;
|
|
32
39
|
throw new Error(`potentially destructive native action blocked; retry with args.${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
|
|
33
40
|
}
|
|
34
41
|
/**
|
|
35
42
|
* A drag is judged on BOTH ends. Dropping a harmless row onto "Delete" is destructive, and the
|
|
36
|
-
* source alone never says so.
|
|
43
|
+
* source alone never says so. Each end is classified on its own text and role, so a Payment
|
|
44
|
+
* option dragged onto Save is not a payment.
|
|
37
45
|
*/
|
|
38
46
|
export function assertDragNotDestructive(innerArgs, from, to) {
|
|
39
47
|
if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
40
48
|
return;
|
|
41
|
-
if (!
|
|
49
|
+
if (!isDestructiveDescriptor(from) && !isDestructiveDescriptor(to))
|
|
42
50
|
return;
|
|
43
51
|
throw new Error(`potentially destructive native action blocked; retry with args.${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
|
|
44
52
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to do when the observer left in the middle of an act.
|
|
3
|
+
*
|
|
4
|
+
* A full-document navigation — often the very thing the act CAUSED — tears the SDK down. The page
|
|
5
|
+
* re-announces, and whatever this call still had in flight is gone. That is a fact about the
|
|
6
|
+
* observer, never about the application, and the difference matters: every honest verdict this
|
|
7
|
+
* engine can give depends on not converting "I stopped watching" into "your app is broken".
|
|
8
|
+
*
|
|
9
|
+
* Extracted from act-tools.ts, which had reached the file-size cap. It earns its own module because
|
|
10
|
+
* it is one idea — follow the document that took over, and re-ask there — used from the two points
|
|
11
|
+
* where observation can be lost: a write rejected at dispatch, and a wait cut off mid-predicate.
|
|
12
|
+
*/
|
|
13
|
+
import type { Session } from '../session/session.js';
|
|
14
|
+
import { type SuccessorRegistry } from '../session/session-successor.js';
|
|
15
|
+
import type { EvalResult } from '../events/predicate-eval.js';
|
|
16
|
+
export interface FollowSuccessor {
|
|
17
|
+
sessions: SuccessorRegistry;
|
|
18
|
+
session: Session;
|
|
19
|
+
verdict: EvalResult;
|
|
20
|
+
/** Total budget the CALLER granted, in ms. */
|
|
21
|
+
timeout: number;
|
|
22
|
+
/** `session.elapsed()` at the moment the predicate started. */
|
|
23
|
+
predicateStarted: number;
|
|
24
|
+
/** Re-ask the declared consequence on `next`, with whatever budget is left. */
|
|
25
|
+
reevaluate: (next: Session, budgetMs: number) => Promise<EvalResult>;
|
|
26
|
+
}
|
|
27
|
+
export interface FollowedOutcome {
|
|
28
|
+
session: Session;
|
|
29
|
+
verdict: EvalResult;
|
|
30
|
+
/** True when the wait moved to a new document, so the caller must reset its event cursor. */
|
|
31
|
+
followed: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
|
|
35
|
+
*
|
|
36
|
+
* `navigate` already waits for the HELLO and returns the new id; this path used to grade
|
|
37
|
+
* `observation_lost` and leave the agent holding a DEAD id, so the next assert failed even though
|
|
38
|
+
* the new page had loaded perfectly. Two live tabs at that origin is still a guess, and
|
|
39
|
+
* `awaitDocumentSuccessor` refuses to guess — a wrong follow drives somebody else's app.
|
|
40
|
+
*/
|
|
41
|
+
export declare function followLostObservation(input: FollowSuccessor): Promise<FollowedOutcome>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to do when the observer left in the middle of an act.
|
|
3
|
+
*
|
|
4
|
+
* A full-document navigation — often the very thing the act CAUSED — tears the SDK down. The page
|
|
5
|
+
* re-announces, and whatever this call still had in flight is gone. That is a fact about the
|
|
6
|
+
* observer, never about the application, and the difference matters: every honest verdict this
|
|
7
|
+
* engine can give depends on not converting "I stopped watching" into "your app is broken".
|
|
8
|
+
*
|
|
9
|
+
* Extracted from act-tools.ts, which had reached the file-size cap. It earns its own module because
|
|
10
|
+
* it is one idea — follow the document that took over, and re-ask there — used from the two points
|
|
11
|
+
* where observation can be lost: a write rejected at dispatch, and a wait cut off mid-predicate.
|
|
12
|
+
*/
|
|
13
|
+
import { awaitDocumentSuccessor } from '../session/session-successor.js';
|
|
14
|
+
/**
|
|
15
|
+
* Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
|
|
16
|
+
*
|
|
17
|
+
* `navigate` already waits for the HELLO and returns the new id; this path used to grade
|
|
18
|
+
* `observation_lost` and leave the agent holding a DEAD id, so the next assert failed even though
|
|
19
|
+
* the new page had loaded perfectly. Two live tabs at that origin is still a guess, and
|
|
20
|
+
* `awaitDocumentSuccessor` refuses to guess — a wrong follow drives somebody else's app.
|
|
21
|
+
*/
|
|
22
|
+
export async function followLostObservation(input) {
|
|
23
|
+
const { sessions, session, verdict, timeout, predicateStarted, reevaluate } = input;
|
|
24
|
+
if (true !== verdict.observationLost || 0 >= timeout)
|
|
25
|
+
return { session, verdict, followed: false };
|
|
26
|
+
const remaining = timeout - (session.elapsed() - predicateStarted);
|
|
27
|
+
const next = remaining > 0 ? await awaitDocumentSuccessor(sessions, session, remaining) : null;
|
|
28
|
+
if (null === next)
|
|
29
|
+
return { session, verdict, followed: false };
|
|
30
|
+
// Computed on the DEPARTED session, before the caller points at the successor: the new session's
|
|
31
|
+
// elapsed() starts at 0 and would silently refund the whole budget the caller had already spent.
|
|
32
|
+
const leftover = timeout - (session.elapsed() - predicateStarted);
|
|
33
|
+
if (leftover <= 0)
|
|
34
|
+
return { session: next, verdict, followed: true };
|
|
35
|
+
return { session: next, verdict: await reevaluate(next, leftover), followed: true };
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function assertSequenceSteps(steps: readonly unknown[]): void;
|
|
2
|
+
/**
|
|
3
|
+
* Throws if this call cannot honestly be driven. Call it after the args are parsed and before the
|
|
4
|
+
* first dispatch — anything that depends on what is actually rendered belongs after the action.
|
|
5
|
+
*/
|
|
6
|
+
export declare function preflightAct(actArgs: Record<string, unknown>, until: unknown): void;
|
|
7
|
+
/**
|
|
8
|
+
* Dispatch a write, or report that its outcome went unobserved.
|
|
9
|
+
*
|
|
10
|
+
* Returns null — never throws — when the transport was displaced by a newer connection claiming the
|
|
11
|
+
* same id. That happens on any full-document navigation the act itself causes: the page unloads,
|
|
12
|
+
* re-announces, and the write in flight is rejected by the handle that just died.
|
|
13
|
+
*
|
|
14
|
+
* The write is deliberately NOT re-sent. Re-asking a READ on the successor is free; re-sending a
|
|
15
|
+
* WRITE is not, because nothing can prove the first one did not reach the page — and a duplicated
|
|
16
|
+
* click on a button that charges a card is a far worse outcome than a verdict of "I could not see".
|
|
17
|
+
* Every other failure propagates unchanged: only a replacement is an observation problem, and
|
|
18
|
+
* treating a timeout or a refusal as one would launder a real failure into a shrug.
|
|
19
|
+
*/
|
|
20
|
+
export declare function dispatchAct<T>(send: () => Promise<T>): Promise<T | null>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything refused BEFORE the action window opens.
|
|
3
|
+
*
|
|
4
|
+
* Both checks here share one property, and it is the reason they belong together: each is decidable
|
|
5
|
+
* without touching the page, and each would otherwise be discovered only after the click had landed
|
|
6
|
+
* — when the action is spent, the page has moved, and the honest answer has already been lost.
|
|
7
|
+
*
|
|
8
|
+
* Refusing early is not a convenience. `reticle_act_and_wait` promises a verdict, and a verdict that
|
|
9
|
+
* blames the app for the caller's own mistake is the most damaging thing a verification tool can
|
|
10
|
+
* produce: it sends somebody to fix code that is not broken. "Nothing was acted on" is a far better
|
|
11
|
+
* outcome than "unknown".
|
|
12
|
+
*/
|
|
13
|
+
import { SessionReplacedError } from '../session/pending-commands.js';
|
|
14
|
+
import { assertNativeInputSupported } from './act-danger.js';
|
|
15
|
+
import { unevaluablePredicateReason } from '../events/predicate-precheck.js';
|
|
16
|
+
/**
|
|
17
|
+
* Refuse a sequence whose steps cannot be addressed.
|
|
18
|
+
*
|
|
19
|
+
* `steps` is a bare array of objects, so a step with neither `ref` nor `target` used to travel on
|
|
20
|
+
* and dispatch as `ref: ''`. The browser then threw `ref '' no longer resolves to an element` —
|
|
21
|
+
* a stale-ref diagnosis for a missing locator — and the caller went looking for a re-render.
|
|
22
|
+
*
|
|
23
|
+
* `target` is the same locator `reticle_act` / `reticle_act_and_wait` take; the handler resolves it
|
|
24
|
+
* with `resolveActTarget` before dispatch. This check only asks that every step names one of the
|
|
25
|
+
* two, and it runs before the first step, so a typo in step three cannot leave one and two applied.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Keys an agent reaches for when it means "and prove this happened".
|
|
29
|
+
*
|
|
30
|
+
* A sub-step reads `ref`/`target`/`action`/`args` and nothing else, and the schema is a permissive
|
|
31
|
+
* record, so any of these was accepted and dropped. `until` is the likeliest of them by a distance:
|
|
32
|
+
* it is what act_and_wait — the tool immediately next to this one — calls its assertion.
|
|
33
|
+
*
|
|
34
|
+
* Silently ignoring one manufactures a false green. A sequence carrying an impossible `until` came
|
|
35
|
+
* back `completed: 1` with no error and nothing said about the predicate, which reads as the
|
|
36
|
+
* consequence having held. act_sequence cannot grade one; refusing and naming the tool that can is
|
|
37
|
+
* the same answer given to an unsupported native click, and for the same reason.
|
|
38
|
+
*/
|
|
39
|
+
const CONSEQUENCE_KEYS = ['until', 'expect', 'assert', 'waitFor'];
|
|
40
|
+
export function assertSequenceSteps(steps) {
|
|
41
|
+
if (0 === steps.length) {
|
|
42
|
+
throw new Error('reticle_act_sequence was given no steps. Pass steps: [{ ref, action, args? }] or [{ target, action, args? }] — nothing was acted on.');
|
|
43
|
+
}
|
|
44
|
+
steps.forEach((raw, i) => {
|
|
45
|
+
const step = 'object' === typeof raw && null !== raw ? raw : {};
|
|
46
|
+
const claimed = CONSEQUENCE_KEYS.find((k) => step[k] !== undefined);
|
|
47
|
+
if (claimed !== undefined) {
|
|
48
|
+
throw new Error(`step ${String(i)} carries \`${claimed}\`, and reticle_act_sequence cannot grade it. ` +
|
|
49
|
+
'Sub-steps are dispatched, never asserted — the key would be dropped and the sequence ' +
|
|
50
|
+
'would report success having checked nothing. Drive the steps here, then prove the ' +
|
|
51
|
+
'consequence with reticle_act_and_wait on the LAST action (its `until` is graded), or ' +
|
|
52
|
+
'with reticle_assert after this call. Nothing was acted on.');
|
|
53
|
+
}
|
|
54
|
+
if ('string' === typeof step['ref'] && step['ref'].length > 0)
|
|
55
|
+
return;
|
|
56
|
+
if (step['target'] !== undefined)
|
|
57
|
+
return;
|
|
58
|
+
throw new Error(`step ${String(i)} has no \`ref\` or \`target\`. ` +
|
|
59
|
+
'Sequence steps take `ref` (from reticle_query/reticle_snapshot) or `target` ' +
|
|
60
|
+
'(e.g. { testid } or { label }), the same locator reticle_act accepts. ' +
|
|
61
|
+
'Nothing was acted on — the whole sequence is refused so a bad step cannot leave the earlier ones half-applied.');
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Throws if this call cannot honestly be driven. Call it after the args are parsed and before the
|
|
66
|
+
* first dispatch — anything that depends on what is actually rendered belongs after the action.
|
|
67
|
+
*/
|
|
68
|
+
export function preflightAct(actArgs, until) {
|
|
69
|
+
// This path cannot honour a native-input request, and taking the argument and ignoring it told
|
|
70
|
+
// the agent its trusted click had happened. See act-danger.
|
|
71
|
+
assertNativeInputSupported(actArgs);
|
|
72
|
+
const unevaluable = unevaluablePredicateReason(until);
|
|
73
|
+
if (unevaluable !== undefined)
|
|
74
|
+
throw new Error(unevaluable);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Dispatch a write, or report that its outcome went unobserved.
|
|
78
|
+
*
|
|
79
|
+
* Returns null — never throws — when the transport was displaced by a newer connection claiming the
|
|
80
|
+
* same id. That happens on any full-document navigation the act itself causes: the page unloads,
|
|
81
|
+
* re-announces, and the write in flight is rejected by the handle that just died.
|
|
82
|
+
*
|
|
83
|
+
* The write is deliberately NOT re-sent. Re-asking a READ on the successor is free; re-sending a
|
|
84
|
+
* WRITE is not, because nothing can prove the first one did not reach the page — and a duplicated
|
|
85
|
+
* click on a button that charges a card is a far worse outcome than a verdict of "I could not see".
|
|
86
|
+
* Every other failure propagates unchanged: only a replacement is an observation problem, and
|
|
87
|
+
* treating a timeout or a refusal as one would launder a real failure into a shrug.
|
|
88
|
+
*/
|
|
89
|
+
export async function dispatchAct(send) {
|
|
90
|
+
try {
|
|
91
|
+
return await send();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof SessionReplacedError)
|
|
95
|
+
return null;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two things a sequence step needs beyond a plain act: a retry when the ref goes stale under a
|
|
3
|
+
* re-render, and a result shaped for the per-step report.
|
|
4
|
+
*
|
|
5
|
+
* ── THE RETRY ─────────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* Refs are invalidated by a re-render, and `reticle_act_sequence` exists precisely because the
|
|
8
|
+
* caller cannot re-snapshot between steps. So `fill -> fill -> submit` fails on step two the moment
|
|
9
|
+
* step one causes React to re-render — and the SAME refs then work when the caller retries the whole
|
|
10
|
+
* sequence by hand, which is the tell: this is a race with the render, not a dead element. Losing
|
|
11
|
+
* the sequence to it makes the tool unusable for the exact shape it was built for.
|
|
12
|
+
*
|
|
13
|
+
* The fix is one retry, after the app has been given the grace period it already gets elsewhere, and
|
|
14
|
+
* ONLY for staleness. A genuinely gone element fails again immediately, and a step that failed for
|
|
15
|
+
* any other reason is never retried — a tool that quietly re-runs actions would turn one click into
|
|
16
|
+
* two, which is worse than the failure it was papering over.
|
|
17
|
+
*/
|
|
18
|
+
import { waitForReaction } from './react-grace.js';
|
|
19
|
+
export declare const isStaleRefError: (error: string | undefined) => boolean;
|
|
20
|
+
/** The shape a step attempt returns. Structural so this file needs nothing from the tool layer. */
|
|
21
|
+
export interface StepOutcome {
|
|
22
|
+
ok: boolean;
|
|
23
|
+
error?: string | undefined;
|
|
24
|
+
result?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/** Just enough of a session for the grace period. Same structural contract react-grace.ts uses. */
|
|
27
|
+
type ReactionSource = Parameters<typeof waitForReaction>[0];
|
|
28
|
+
/**
|
|
29
|
+
* Run one step, and retry it once if — and only if — its ref went stale under a re-render.
|
|
30
|
+
*
|
|
31
|
+
* `attempt` is called at most twice with identical arguments, so it must be the same idempotent
|
|
32
|
+
* dispatch the caller would have made itself.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runStepWithStaleRetry(attempt: () => Promise<StepOutcome>, session: ReactionSource, since: number, budgetMs: number, opts?: {
|
|
35
|
+
sleep(ms: number): Promise<void>;
|
|
36
|
+
}): Promise<StepOutcome>;
|
|
37
|
+
/**
|
|
38
|
+
* One step's entry in the per-step report.
|
|
39
|
+
*
|
|
40
|
+
* Only the fields the underlying act actually produced. Copying them conditionally keeps a clean step
|
|
41
|
+
* from carrying a row of nulls that read as "we looked and found nothing" rather than "there was
|
|
42
|
+
* nothing to look for".
|
|
43
|
+
*/
|
|
44
|
+
export declare function describeStepResult(step: Record<string, unknown>, result: Record<string, unknown>): Record<string, unknown>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two things a sequence step needs beyond a plain act: a retry when the ref goes stale under a
|
|
3
|
+
* re-render, and a result shaped for the per-step report.
|
|
4
|
+
*
|
|
5
|
+
* ── THE RETRY ─────────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* Refs are invalidated by a re-render, and `reticle_act_sequence` exists precisely because the
|
|
8
|
+
* caller cannot re-snapshot between steps. So `fill -> fill -> submit` fails on step two the moment
|
|
9
|
+
* step one causes React to re-render — and the SAME refs then work when the caller retries the whole
|
|
10
|
+
* sequence by hand, which is the tell: this is a race with the render, not a dead element. Losing
|
|
11
|
+
* the sequence to it makes the tool unusable for the exact shape it was built for.
|
|
12
|
+
*
|
|
13
|
+
* The fix is one retry, after the app has been given the grace period it already gets elsewhere, and
|
|
14
|
+
* ONLY for staleness. A genuinely gone element fails again immediately, and a step that failed for
|
|
15
|
+
* any other reason is never retried — a tool that quietly re-runs actions would turn one click into
|
|
16
|
+
* two, which is worse than the failure it was papering over.
|
|
17
|
+
*/
|
|
18
|
+
import { waitForReaction } from './react-grace.js';
|
|
19
|
+
/** Matches the daemon's own wording, which error-recovery.ts also keys its STALE_REF hint off. */
|
|
20
|
+
const STALE_REF = /no longer resolves to an element/i;
|
|
21
|
+
export const isStaleRefError = (error) => error !== undefined && STALE_REF.test(error);
|
|
22
|
+
const realSleep = {
|
|
23
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Run one step, and retry it once if — and only if — its ref went stale under a re-render.
|
|
27
|
+
*
|
|
28
|
+
* `attempt` is called at most twice with identical arguments, so it must be the same idempotent
|
|
29
|
+
* dispatch the caller would have made itself.
|
|
30
|
+
*/
|
|
31
|
+
export async function runStepWithStaleRetry(attempt, session, since, budgetMs, opts = realSleep) {
|
|
32
|
+
const first = await attempt();
|
|
33
|
+
if (first.ok || !isStaleRefError(first.error))
|
|
34
|
+
return first;
|
|
35
|
+
await waitForReaction(session, since, budgetMs, opts);
|
|
36
|
+
return attempt();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* One step's entry in the per-step report.
|
|
40
|
+
*
|
|
41
|
+
* Only the fields the underlying act actually produced. Copying them conditionally keeps a clean step
|
|
42
|
+
* from carrying a row of nulls that read as "we looked and found nothing" rather than "there was
|
|
43
|
+
* nothing to look for".
|
|
44
|
+
*/
|
|
45
|
+
export function describeStepResult(step, result) {
|
|
46
|
+
const out = {
|
|
47
|
+
ref: result['ref'] ?? step['ref'],
|
|
48
|
+
action: result['action'] ?? step['action'],
|
|
49
|
+
dispatched: result['dispatched'] ?? true,
|
|
50
|
+
settled: result['settled'] ?? null,
|
|
51
|
+
settleReason: result['settleReason'] ?? null,
|
|
52
|
+
};
|
|
53
|
+
for (const key of ['testid', 'component', 'role', 'name', 'source', 'warning']) {
|
|
54
|
+
if (result[key] !== undefined)
|
|
55
|
+
out[key] = result[key];
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `reticle_act_sequence` — several actions compiled into one round trip.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `act-tools.ts` when that file passed the 1000-line backstop. A tool seam rather than
|
|
5
|
+
* an arbitrary one: its two neighbours drive ONE action and grade its consequence, while this one
|
|
6
|
+
* compiles a list of steps and runs them inside a single action window, reporting per step.
|
|
7
|
+
*
|
|
8
|
+
* It keeps its own `beginAction` — the dispatch and the attribution window that must wrap it stay in
|
|
9
|
+
* the same file, which is the property `dispatch-attribution.test.ts` enforces. An earlier attempt
|
|
10
|
+
* split the shared `actCommand` helper out instead, which separated the two and that guard caught it.
|
|
11
|
+
*/
|
|
12
|
+
import { type ToolDef } from './tool-kit.js';
|
|
13
|
+
export declare const ACT_SEQUENCE_TOOL: ToolDef;
|