@reticlehq/server 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +63 -13
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +1 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +9 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +2 -11
- package/dist/{cli-parse.js → cli/cli-parse.js} +113 -60
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +174 -92
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.js +24 -9
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +53 -7
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.js +4 -1
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +3 -2
- package/dist/init/detect.js +16 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +23 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +132 -8
- package/dist/init/plan.d.ts +73 -8
- package/dist/init/plan.js +133 -34
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +5 -0
- package/dist/init/run.js +210 -36
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +52 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +14 -2
- package/dist/session/session.js +69 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -20,7 +20,7 @@ export interface InstrumentationGap {
|
|
|
20
20
|
/** Why this gap matters (e.g. "checkout flow asserts presence only"). */
|
|
21
21
|
context?: string;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
interface InstrumentationProposal {
|
|
24
24
|
file: string;
|
|
25
25
|
line: number;
|
|
26
26
|
/** The code to insert. */
|
|
@@ -29,3 +29,4 @@ export interface InstrumentationProposal {
|
|
|
29
29
|
}
|
|
30
30
|
/** Turn located capability gaps into ready-to-apply instrumentation proposals. Deterministic. */
|
|
31
31
|
export declare function proposeInstrumentation(gaps: readonly InstrumentationGap[]): InstrumentationProposal[];
|
|
32
|
+
export {};
|
|
@@ -12,7 +12,7 @@ export const InstrumentationGapKind = {
|
|
|
12
12
|
};
|
|
13
13
|
/** PascalCase a store name for a `useX.getState` hint (cart → Cart). */
|
|
14
14
|
function pascal(name) {
|
|
15
|
-
return name.length
|
|
15
|
+
return 0 === name.length ? name : name[0]?.toUpperCase() + name.slice(1);
|
|
16
16
|
}
|
|
17
17
|
function insertFor(gap) {
|
|
18
18
|
switch (gap.kind) {
|
|
@@ -28,14 +28,14 @@ export function detectHidden500(events) {
|
|
|
28
28
|
for (const event of events) {
|
|
29
29
|
if (event.type === EventType.PAGE_HEALTH) {
|
|
30
30
|
const h = event.data['hidden'];
|
|
31
|
-
if (typeof h
|
|
31
|
+
if ('boolean' === typeof h)
|
|
32
32
|
hidden = h;
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
35
|
if (event.type !== EventType.NET_REQUEST)
|
|
36
36
|
continue;
|
|
37
37
|
const status = event.data['status'];
|
|
38
|
-
if (hidden && typeof status
|
|
38
|
+
if (hidden && 'number' === typeof status && status >= 500) {
|
|
39
39
|
findings.push({
|
|
40
40
|
phenomenon: PhenomenonType.HIDDEN_500,
|
|
41
41
|
evidence: { url: event.data['url'], status, method: event.data['method'] },
|
|
@@ -83,7 +83,7 @@ export function detectSwallowedErrors(events) {
|
|
|
83
83
|
const detail = event.data['data'];
|
|
84
84
|
findings.push({
|
|
85
85
|
phenomenon: PhenomenonType.SWALLOWED_ERROR,
|
|
86
|
-
evidence: typeof detail
|
|
86
|
+
evidence: 'object' === typeof detail && detail !== null ? detail : {},
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `process.platform` values this daemon actually branches on, named once.
|
|
3
|
+
*
|
|
4
|
+
* They were spelled inline in five files — `cli-launch`, `cloud-cli`, `init/node-io`, `update/updater`
|
|
5
|
+
* — and every one of them gates real behaviour rather than cosmetics: whether a spawn needs a shell,
|
|
6
|
+
* whether the npm binary is `npm.cmd`, which command opens a browser. A typo in any of those reads as
|
|
7
|
+
* "Windows is broken" and is invisible on the machine of whoever wrote it, because the wrong branch
|
|
8
|
+
* simply never runs there.
|
|
9
|
+
*
|
|
10
|
+
* Node-side only, so deliberately NOT in `@reticlehq/core` — core is the wire contract, and a
|
|
11
|
+
* platform name never crosses the wire.
|
|
12
|
+
*/
|
|
13
|
+
export declare const NodePlatform: {
|
|
14
|
+
readonly WINDOWS: "win32";
|
|
15
|
+
readonly MACOS: "darwin";
|
|
16
|
+
};
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `process.platform` values this daemon actually branches on, named once.
|
|
3
|
+
*
|
|
4
|
+
* They were spelled inline in five files — `cli-launch`, `cloud-cli`, `init/node-io`, `update/updater`
|
|
5
|
+
* — and every one of them gates real behaviour rather than cosmetics: whether a spawn needs a shell,
|
|
6
|
+
* whether the npm binary is `npm.cmd`, which command opens a browser. A typo in any of those reads as
|
|
7
|
+
* "Windows is broken" and is invisible on the machine of whoever wrote it, because the wrong branch
|
|
8
|
+
* simply never runs there.
|
|
9
|
+
*
|
|
10
|
+
* Node-side only, so deliberately NOT in `@reticlehq/core` — core is the wire contract, and a
|
|
11
|
+
* platform name never crosses the wire.
|
|
12
|
+
*/
|
|
13
|
+
export const NodePlatform = {
|
|
14
|
+
WINDOWS: 'win32',
|
|
15
|
+
MACOS: 'darwin',
|
|
16
|
+
};
|
|
@@ -6,8 +6,21 @@
|
|
|
6
6
|
* Headless by default — the pool exists for fault-tolerant headless multi-agent testing.
|
|
7
7
|
*/
|
|
8
8
|
import type { Launcher } from './browser-pool.js';
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* How a leased tab navigates. Pure, and exported, because the decision in it is worth a test while
|
|
11
|
+
* the rest of this file needs a real Chromium.
|
|
12
|
+
*
|
|
13
|
+
* `waitUntil: 'domcontentloaded'` — NOT Playwright's default of `load`. `load` waits for every
|
|
14
|
+
* subresource, so an app with one that never finishes burns the whole nav budget and the lease then
|
|
15
|
+
* reports "is the app running?" about an app that is running. Measured on the sveltekit fixture:
|
|
16
|
+
* 30,501ms and a false diagnosis. The SDK connect is a module script and runs before
|
|
17
|
+
* DOMContentLoaded, and the pool separately waits for the session to register, so the load event was
|
|
18
|
+
* never what proved the page was usable.
|
|
19
|
+
*/
|
|
20
|
+
export declare function gotoOptions(timeoutMs: number | undefined): {
|
|
21
|
+
waitUntil: 'domcontentloaded';
|
|
22
|
+
timeout?: number;
|
|
23
|
+
};
|
|
11
24
|
/** A Launcher that boots a real headless Chromium and adapts it to the pool's interface. */
|
|
12
25
|
export declare function playwrightLauncher(opts?: {
|
|
13
26
|
headless?: boolean;
|
|
@@ -8,6 +8,25 @@
|
|
|
8
8
|
import { BrowserLaunchKind } from '@reticlehq/core';
|
|
9
9
|
import { getSessionMetrics } from '../telemetry/session-metrics.js';
|
|
10
10
|
import { classifyConnectFailure } from '../telemetry/connect-failure.js';
|
|
11
|
+
/**
|
|
12
|
+
* How a leased tab navigates. Pure, and exported, because the decision in it is worth a test while
|
|
13
|
+
* the rest of this file needs a real Chromium.
|
|
14
|
+
*
|
|
15
|
+
* `waitUntil: 'domcontentloaded'` — NOT Playwright's default of `load`. `load` waits for every
|
|
16
|
+
* subresource, so an app with one that never finishes burns the whole nav budget and the lease then
|
|
17
|
+
* reports "is the app running?" about an app that is running. Measured on the sveltekit fixture:
|
|
18
|
+
* 30,501ms and a false diagnosis. The SDK connect is a module script and runs before
|
|
19
|
+
* DOMContentLoaded, and the pool separately waits for the session to register, so the load event was
|
|
20
|
+
* never what proved the page was usable.
|
|
21
|
+
*/
|
|
22
|
+
export function gotoOptions(timeoutMs) {
|
|
23
|
+
return {
|
|
24
|
+
waitUntil: 'domcontentloaded',
|
|
25
|
+
// Absent, not zero: zero means "no timeout" to Playwright, which would turn a slow page into a
|
|
26
|
+
// lease that never returns.
|
|
27
|
+
...(timeoutMs === undefined ? {} : { timeout: timeoutMs }),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
11
30
|
function wrapBrowser(browser) {
|
|
12
31
|
return {
|
|
13
32
|
isConnected: () => browser.isConnected(),
|
|
@@ -17,7 +36,7 @@ function wrapBrowser(browser) {
|
|
|
17
36
|
newPage: async () => {
|
|
18
37
|
const page = await context.newPage();
|
|
19
38
|
return {
|
|
20
|
-
goto: (url, opts) => page.goto(url, opts?.timeoutMs
|
|
39
|
+
goto: (url, opts) => page.goto(url, gotoOptions(opts?.timeoutMs)),
|
|
21
40
|
close: () => page.close(),
|
|
22
41
|
onCrash: (handler) => page.on('crash', handler),
|
|
23
42
|
};
|
|
@@ -30,7 +49,7 @@ function wrapBrowser(browser) {
|
|
|
30
49
|
};
|
|
31
50
|
}
|
|
32
51
|
/** The fix for the most common first-run failure: Chromium isn't installed for Playwright. */
|
|
33
|
-
|
|
52
|
+
const CHROMIUM_MISSING_HINT = 'Chromium is not installed for Playwright. Run: npx playwright install chromium';
|
|
34
53
|
/** A Launcher that boots a real headless Chromium and adapts it to the pool's interface. */
|
|
35
54
|
export function playwrightLauncher(opts = {}) {
|
|
36
55
|
const headless = opts.headless ?? true;
|
package/dist/project/fs-port.js
CHANGED
|
@@ -36,7 +36,7 @@ function numericDelta(before, after) {
|
|
|
36
36
|
*/
|
|
37
37
|
async function cloudRegression(deps, sessionId) {
|
|
38
38
|
const config = resolveCloudConfig(process.env);
|
|
39
|
-
if (
|
|
39
|
+
if (null === config)
|
|
40
40
|
return undefined;
|
|
41
41
|
let projectId;
|
|
42
42
|
try {
|
|
@@ -173,7 +173,7 @@ export const PROJECT_TOOLS = [
|
|
|
173
173
|
const kindArg = args['kind'];
|
|
174
174
|
const summary = asString(args['summary']);
|
|
175
175
|
await deps.project.recordRun({
|
|
176
|
-
kind: typeof kindArg
|
|
176
|
+
kind: 'string' === typeof kindArg ? kindArg : RunKind.MANUAL,
|
|
177
177
|
name,
|
|
178
178
|
status,
|
|
179
179
|
...(summary !== undefined ? { summary } : {}),
|
|
@@ -46,7 +46,7 @@ export function computeVerdict(input) {
|
|
|
46
46
|
status = VerdictStatus.PASS;
|
|
47
47
|
const ran = input.flows.length + input.checks.length;
|
|
48
48
|
let confidence;
|
|
49
|
-
if (
|
|
49
|
+
if (0 === ran)
|
|
50
50
|
confidence = RunConfidence.LOW;
|
|
51
51
|
else if (input.flows.some((f) => f.oracle !== undefined) || input.checks.length > 0)
|
|
52
52
|
confidence = RunConfidence.HIGH;
|
|
@@ -5,12 +5,6 @@
|
|
|
5
5
|
* RepairPacket shape. This is the OEM self-heal value — a verdict that also says how to fix.
|
|
6
6
|
*/
|
|
7
7
|
import { type FlowReplayResult, type RepairPacket } from '@reticlehq/core';
|
|
8
|
-
/**
|
|
9
|
-
* Build a repair packet for a failed replay, or undefined when the flow passed. The actual cause is
|
|
10
|
-
* the decision's whatChanged, falling back to the replay error or a generic message.
|
|
11
|
-
*/
|
|
12
|
-
/** The deviation report's headline when it flagged something — narrows the replay's `unknown` deviation. */
|
|
13
|
-
export declare function deviationHeadline(deviation: unknown): string | undefined;
|
|
14
8
|
export declare function buildRepairPacket(replay: FlowReplayResult): RepairPacket | undefined;
|
|
15
9
|
/** Build the repair packets for a set of replays (failed ones only). */
|
|
16
10
|
export declare function buildRepairPackets(replays: ReadonlyArray<FlowReplayResult>): RepairPacket[];
|
|
@@ -36,11 +36,11 @@ function suggestedPrompt(name, actual, where, nextAction) {
|
|
|
36
36
|
* the decision's whatChanged, falling back to the replay error or a generic message.
|
|
37
37
|
*/
|
|
38
38
|
/** The deviation report's headline when it flagged something — narrows the replay's `unknown` deviation. */
|
|
39
|
-
|
|
40
|
-
if (typeof deviation !== 'object' ||
|
|
39
|
+
function deviationHeadline(deviation) {
|
|
40
|
+
if (typeof deviation !== 'object' || null === deviation)
|
|
41
41
|
return undefined;
|
|
42
42
|
const d = deviation;
|
|
43
|
-
if (Array.isArray(d.deviations) && d.deviations.length > 0 && typeof d.headline
|
|
43
|
+
if (Array.isArray(d.deviations) && d.deviations.length > 0 && 'string' === typeof d.headline) {
|
|
44
44
|
return d.headline;
|
|
45
45
|
}
|
|
46
46
|
return undefined;
|
|
@@ -62,7 +62,7 @@ export function buildRisks(changedFiles, policy = {}) {
|
|
|
62
62
|
const risks = [];
|
|
63
63
|
for (const { surface } of RISK_PATTERNS) {
|
|
64
64
|
const files = filesBySurface.get(surface);
|
|
65
|
-
if (files === undefined || files.length
|
|
65
|
+
if (files === undefined || 0 === files.length)
|
|
66
66
|
continue;
|
|
67
67
|
risks.push({
|
|
68
68
|
surface,
|
package/dist/runs/run-diff.d.ts
CHANGED
|
@@ -22,8 +22,6 @@ export interface RunDiff {
|
|
|
22
22
|
};
|
|
23
23
|
headline: string;
|
|
24
24
|
}
|
|
25
|
-
/** Below this percent, a duration change is noise (machine jitter), not a regression. */
|
|
26
|
-
export declare const DEFAULT_NOISE_FLOOR_PCT = 10;
|
|
27
25
|
/**
|
|
28
26
|
* Compare two verification runs — the concrete answer to "what changed between run A and B". Flow
|
|
29
27
|
* durations are diffed with a noise floor (jitter below it is ignored); status changes and new/removed
|
package/dist/runs/run-diff.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Below this percent, a duration change is noise (machine jitter), not a regression. */
|
|
2
|
-
|
|
2
|
+
const DEFAULT_NOISE_FLOOR_PCT = 10;
|
|
3
3
|
function pct(before, delta) {
|
|
4
4
|
return before > 0 ? Math.round((delta / before) * 100) : 0;
|
|
5
5
|
}
|
|
@@ -11,7 +11,7 @@ function headlineFor(diff) {
|
|
|
11
11
|
const regressions = diff.flows.filter((f) => f.regressed);
|
|
12
12
|
if (regressions.length > 0) {
|
|
13
13
|
const worst = regressions[0];
|
|
14
|
-
parts.push(`${String(regressions.length)} regression${regressions.length
|
|
14
|
+
parts.push(`${String(regressions.length)} regression${1 === regressions.length ? '' : 's'} (worst: ${worst?.name ?? ''} ${String(worst?.durationDeltaPct ?? 0)}%)`);
|
|
15
15
|
}
|
|
16
16
|
if (diff.newFlows.length > 0)
|
|
17
17
|
parts.push(`+${String(diff.newFlows.length)} flow`);
|
|
@@ -20,7 +20,7 @@ function headlineFor(diff) {
|
|
|
20
20
|
if (diff.verdictChange !== undefined) {
|
|
21
21
|
parts.push(`verdict ${diff.verdictChange.from}→${diff.verdictChange.to}`);
|
|
22
22
|
}
|
|
23
|
-
return parts.length
|
|
23
|
+
return 0 === parts.length ? 'no significant change between runs' : parts.join(', ');
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Compare two verification runs — the concrete answer to "what changed between run A and B". Flow
|
package/dist/runs/run-tools.js
CHANGED
|
@@ -38,7 +38,7 @@ export const RUN_TOOLS = [
|
|
|
38
38
|
handler: async (deps, args) => {
|
|
39
39
|
const store = new RunStore(deps.fs, deps.reticleRoot);
|
|
40
40
|
// format:"diff" is a whole-history operation (two most-recent runs), not a single-run read.
|
|
41
|
-
if (asString(args['format'])
|
|
41
|
+
if ('diff' === asString(args['format'])) {
|
|
42
42
|
const pair = await store.latestTwo();
|
|
43
43
|
if (pair === undefined) {
|
|
44
44
|
return {
|
|
@@ -75,7 +75,7 @@ export const RUN_TOOLS = [
|
|
|
75
75
|
}
|
|
76
76
|
run = latest;
|
|
77
77
|
}
|
|
78
|
-
return asString(args['format'])
|
|
78
|
+
return 'report' === asString(args['format']) ? { report: renderRunReport(run) } : { run };
|
|
79
79
|
},
|
|
80
80
|
},
|
|
81
81
|
];
|
|
@@ -41,7 +41,7 @@ function assembleRun(deps, timed, projectId) {
|
|
|
41
41
|
* unaffected. Returns the runId (for logging/tests); undefined if nothing was produced (empty suite).
|
|
42
42
|
*/
|
|
43
43
|
export async function persistAndSyncVerificationRun(deps, timed, projectId) {
|
|
44
|
-
if (timed.length
|
|
44
|
+
if (0 === timed.length)
|
|
45
45
|
return undefined;
|
|
46
46
|
let run;
|
|
47
47
|
try {
|
|
@@ -56,7 +56,7 @@ export async function persistAndSyncVerificationRun(deps, timed, projectId) {
|
|
|
56
56
|
}
|
|
57
57
|
// Per-project cloud: only push when THIS project has cloud attached AND its policy allows runs.
|
|
58
58
|
const cloud = await resolveProjectCloud(deps.fs, deps.reticleRoot, homedir(), process.env);
|
|
59
|
-
if (cloud.config
|
|
59
|
+
if (null === cloud.config || !cloud.policy.runs)
|
|
60
60
|
return run.runId; // not attached / runs disabled → local only
|
|
61
61
|
const result = await syncRunToCloud(run, cloud.config, (url, init) => fetch(url, init));
|
|
62
62
|
if (result.outcome !== SyncOutcome.SYNCED) {
|
package/dist/runs/verify-http.js
CHANGED
|
@@ -67,7 +67,7 @@ function toVerifyOptions(body) {
|
|
|
67
67
|
}
|
|
68
68
|
/** Constant-time token check. Empty expected token ⇒ open (localhost-only deployments). */
|
|
69
69
|
export function tokenOk(provided, expected) {
|
|
70
|
-
if (expected.length
|
|
70
|
+
if (0 === expected.length)
|
|
71
71
|
return true;
|
|
72
72
|
if (provided === undefined)
|
|
73
73
|
return false;
|
|
@@ -91,7 +91,7 @@ export function startVerifyServer(opts, port) {
|
|
|
91
91
|
return new Promise((resolve) => {
|
|
92
92
|
server.listen(port, LOCALHOST, () => {
|
|
93
93
|
const address = server.address();
|
|
94
|
-
const boundPort = typeof address
|
|
94
|
+
const boundPort = 'object' === typeof address && address !== null ? address.port : port;
|
|
95
95
|
resolve({ server, port: boundPort });
|
|
96
96
|
});
|
|
97
97
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AppRuntime } from '@reticlehq/core';
|
|
1
2
|
/**
|
|
2
3
|
* Turn a bare command timeout into something the reader can act on.
|
|
3
4
|
*
|
|
@@ -21,9 +22,15 @@
|
|
|
21
22
|
* The advice is ADDED, never substituted — the original fact still leads — and only for a runtime
|
|
22
23
|
* that can actually suffer it, since misdirecting an Electron user costs them the same hour.
|
|
23
24
|
*/
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* What the page told us about its shell, via PAGE_HEALTH. Undefined before the first report.
|
|
27
|
+
*
|
|
28
|
+
* An alias of core's AppRuntime rather than a second list of the same three strings: the SDK reports
|
|
29
|
+
* this value, so it is a wire value, and two independent spellings of it is how one side gains a
|
|
30
|
+
* runtime the other silently refuses to recognise.
|
|
31
|
+
*/
|
|
32
|
+
export type PageRuntime = AppRuntime;
|
|
33
|
+
interface TimeoutContext {
|
|
27
34
|
url: string;
|
|
28
35
|
/** The page's own last visibility report. */
|
|
29
36
|
hidden: boolean;
|
|
@@ -33,3 +40,4 @@ export interface TimeoutContext {
|
|
|
33
40
|
lastSeenMs?: number;
|
|
34
41
|
}
|
|
35
42
|
export declare function commandTimeoutMessage(name: string, timeoutMs: number, context: TimeoutContext): string;
|
|
43
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isOpaqueOrigin } from '@reticlehq/core';
|
|
1
|
+
import { AppRuntime, isOpaqueOrigin } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* Inbound silence beyond this means the page may genuinely be wedged; inside it, the SDK has spoken
|
|
4
4
|
* recently enough that "the page is hung" is provably the wrong story. A page that is streaming
|
|
@@ -18,7 +18,7 @@ const HIDDEN_ADVICE = 'The page last reported itself hidden, and this is a WKWeb
|
|
|
18
18
|
'loaded. If neither fits, the page is reachable but not executing, which is worth reporting.';
|
|
19
19
|
/** True when this session is a WebKit desktop shell — the only runtime that suffers this. */
|
|
20
20
|
function isWebKitDesktop(context) {
|
|
21
|
-
if (
|
|
21
|
+
if (AppRuntime.TAURI === context.runtime)
|
|
22
22
|
return true;
|
|
23
23
|
// A `tauri://` origin is unambiguous even before the first health report lands.
|
|
24
24
|
if (context.runtime !== undefined)
|
|
@@ -12,6 +12,12 @@ interface ControlEnvelope {
|
|
|
12
12
|
* recovery path (address the guidance, then reticle_session{action:"resume"}) lives here in exactly one place.
|
|
13
13
|
*/
|
|
14
14
|
export declare const PAUSE_HINT = "Paused by the human. Address the guidance, then call reticle_session{action:\"resume\"} (or wait for the human to resume).";
|
|
15
|
+
/**
|
|
16
|
+
* `because` for the verdict a paused verification returns. Nothing was driven and nothing was
|
|
17
|
+
* asserted, which is the definition of unknown — never `no`, which would report the human's own
|
|
18
|
+
* pause as the app failing.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PAUSED_NO_VERDICT = "the human paused this session, so nothing was driven and nothing was asserted";
|
|
15
21
|
/** Shape returned by the short-circuit when an action tool refuses while paused. */
|
|
16
22
|
interface PausedResult {
|
|
17
23
|
paused: true;
|
|
@@ -5,6 +5,12 @@ import { z } from 'zod';
|
|
|
5
5
|
* recovery path (address the guidance, then reticle_session{action:"resume"}) lives here in exactly one place.
|
|
6
6
|
*/
|
|
7
7
|
export const PAUSE_HINT = 'Paused by the human. Address the guidance, then call reticle_session{action:"resume"} (or wait for the human to resume).';
|
|
8
|
+
/**
|
|
9
|
+
* `because` for the verdict a paused verification returns. Nothing was driven and nothing was
|
|
10
|
+
* asserted, which is the definition of unknown — never `no`, which would report the human's own
|
|
11
|
+
* pause as the app failing.
|
|
12
|
+
*/
|
|
13
|
+
export const PAUSED_NO_VERDICT = 'the human paused this session, so nothing was driven and nothing was asserted';
|
|
8
14
|
/**
|
|
9
15
|
* outputSchema fields every action tool that can short-circuit on pause MUST declare, or a validating
|
|
10
16
|
* profile strips the WHOLE pause payload — and because `guidance` is drained-once, that loses the
|
|
@@ -28,7 +34,7 @@ export const pausedOutputShape = {
|
|
|
28
34
|
export function buildControlEnvelope(session) {
|
|
29
35
|
const state = session.getState();
|
|
30
36
|
const guidance = session.drainInbox().map((m) => m.text);
|
|
31
|
-
if (state === SessionState.ACTIVE && guidance.length
|
|
37
|
+
if (state === SessionState.ACTIVE && 0 === guidance.length)
|
|
32
38
|
return undefined;
|
|
33
39
|
return { state, guidance };
|
|
34
40
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which of the usual dev-server ports have something listening on them.
|
|
3
|
+
*
|
|
4
|
+
* The one fact that separates "the app is not running" from "the app is running and never dialled
|
|
5
|
+
* us" — the difference between two completely different next actions for the agent, which the
|
|
6
|
+
* no-session message could not tell apart before. See no-session-diagnosis.ts.
|
|
7
|
+
*
|
|
8
|
+
* Kept out of the hot path: `SessionManager.resolve` is synchronous and runs on every tool call, so
|
|
9
|
+
* the probe refreshes in the background and the resolve path reads a cached answer. A stale answer
|
|
10
|
+
* is fine here — it is a hint in an error message, not a decision.
|
|
11
|
+
*/
|
|
12
|
+
/** Every candidate port serving a document, in ascending order. Never rejects. */
|
|
13
|
+
export declare function probeDevServers(ports?: readonly number[], probe?: (port: number) => Promise<boolean>): Promise<number[]>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which of the usual dev-server ports have something listening on them.
|
|
3
|
+
*
|
|
4
|
+
* The one fact that separates "the app is not running" from "the app is running and never dialled
|
|
5
|
+
* us" — the difference between two completely different next actions for the agent, which the
|
|
6
|
+
* no-session message could not tell apart before. See no-session-diagnosis.ts.
|
|
7
|
+
*
|
|
8
|
+
* Kept out of the hot path: `SessionManager.resolve` is synchronous and runs on every tool call, so
|
|
9
|
+
* the probe refreshes in the background and the resolve path reads a cached answer. A stale answer
|
|
10
|
+
* is fine here — it is a hint in an error message, not a decision.
|
|
11
|
+
*/
|
|
12
|
+
import { request } from 'node:http';
|
|
13
|
+
import { DEV_SERVER_PORTS } from '../cli/cli-port.js';
|
|
14
|
+
import { looksLikeDevServer } from './looks-like-dev-server.js';
|
|
15
|
+
/** Give up fast: an HTTP answer from localhost either lands quickly or nothing is there. */
|
|
16
|
+
const HTTP_PROBE_TIMEOUT_MS = 400;
|
|
17
|
+
/**
|
|
18
|
+
* `localhost`, not `127.0.0.1` — so BOTH address families are tried.
|
|
19
|
+
*
|
|
20
|
+
* Measured: a plain `vite --port 4311` on this machine listens on `[::1]` only, and a probe pinned to
|
|
21
|
+
* the IPv4 loopback cannot see it. The old TCP probe had the same blind spot, which meant the
|
|
22
|
+
* diagnostic could miss the very dev server it exists to find while still reporting an unrelated
|
|
23
|
+
* service that happens to bind both stacks (macOS AirPlay does).
|
|
24
|
+
*/
|
|
25
|
+
const PROBE_HOST = 'localhost';
|
|
26
|
+
/**
|
|
27
|
+
* Does this port serve a DOCUMENT — i.e. is it a dev server rather than merely something listening?
|
|
28
|
+
*
|
|
29
|
+
* A bare TCP connect reported macOS ControlCenter (AirPlay Receiver, on by default on every Mac,
|
|
30
|
+
* port 5000) as the user's dev server. See looks-like-dev-server for what it actually answers and
|
|
31
|
+
* why "a socket accepted" is the wrong question.
|
|
32
|
+
*/
|
|
33
|
+
function servesDocument(port, host = PROBE_HOST) {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
const req = request({ host, port, path: '/', method: 'GET', timeout: HTTP_PROBE_TIMEOUT_MS }, (res) => {
|
|
36
|
+
const answer = looksLikeDevServer(res.statusCode ?? 0, res.headers['content-type']);
|
|
37
|
+
res.resume(); // drain, so the socket closes rather than lingering
|
|
38
|
+
resolve(answer);
|
|
39
|
+
});
|
|
40
|
+
req.once('timeout', () => {
|
|
41
|
+
req.destroy();
|
|
42
|
+
resolve(false);
|
|
43
|
+
});
|
|
44
|
+
req.once('error', () => resolve(false));
|
|
45
|
+
req.end();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** Every candidate port serving a document, in ascending order. Never rejects. */
|
|
49
|
+
export async function probeDevServers(ports = [...DEV_SERVER_PORTS], probe = (p) => servesDocument(p)) {
|
|
50
|
+
const results = await Promise.all(ports.map((p) => probe(p).then((up) => (up ? p : null))));
|
|
51
|
+
return results.filter((p) => p !== null).sort((a, b) => a - b);
|
|
52
|
+
}
|
|
@@ -14,18 +14,32 @@
|
|
|
14
14
|
* the "this click did nothing" check is unreachable on the ordinary act-then-observe flow: an
|
|
15
15
|
* empty-window test is a statement about the PAGE, and no real app has a quiet page.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
interface ActEffect {
|
|
18
18
|
action?: string | undefined;
|
|
19
19
|
/** DOM mutations inside the acted element's own subtree. Undefined means nobody measured. */
|
|
20
20
|
mutatedWithin?: number | undefined;
|
|
21
21
|
}
|
|
22
22
|
export declare class LastAct {
|
|
23
23
|
#private;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Record an action that ACTUALLY DISPATCHED — cursor and effect together, never one without the
|
|
26
|
+
* other.
|
|
27
|
+
*
|
|
28
|
+
* It used to be two setters called BEFORE the ref was resolved, so a refused act (stale ref, a
|
|
29
|
+
* disabled control, a paused page) left a cursor and an empty effect standing. The next observe
|
|
30
|
+
* saw a cursor inside its window, asked for the effect, found `mutatedWithin: undefined` over a
|
|
31
|
+
* quiet window and reported "the click was dispatched and the page settled … the target does not
|
|
32
|
+
* react" — about a click nobody dispatched, blaming a control that was fine, on exactly the
|
|
33
|
+
* re-query-and-retry path the stale-ref message sends the agent down.
|
|
34
|
+
*
|
|
35
|
+
* Marking only on the success path is what makes that unrepeatable: a failure mode added later
|
|
36
|
+
* cannot forget to clean up state that was never written.
|
|
37
|
+
*/
|
|
38
|
+
markActed(cursor: number, action: string | undefined, mutatedWithin: number | undefined): void;
|
|
25
39
|
cursor(): number | undefined;
|
|
26
40
|
markSource(source: string | undefined): void;
|
|
27
41
|
source(): string | undefined;
|
|
28
|
-
markEffect(action: string | undefined, mutatedWithin: number | undefined): void;
|
|
29
42
|
/** Empty when nothing has acted — callers then fall back to checks that need no action context. */
|
|
30
43
|
effect(): ActEffect;
|
|
31
44
|
}
|
|
45
|
+
export {};
|
package/dist/session/last-act.js
CHANGED
|
@@ -2,8 +2,23 @@ export class LastAct {
|
|
|
2
2
|
#cursor;
|
|
3
3
|
#source;
|
|
4
4
|
#effect;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Record an action that ACTUALLY DISPATCHED — cursor and effect together, never one without the
|
|
7
|
+
* other.
|
|
8
|
+
*
|
|
9
|
+
* It used to be two setters called BEFORE the ref was resolved, so a refused act (stale ref, a
|
|
10
|
+
* disabled control, a paused page) left a cursor and an empty effect standing. The next observe
|
|
11
|
+
* saw a cursor inside its window, asked for the effect, found `mutatedWithin: undefined` over a
|
|
12
|
+
* quiet window and reported "the click was dispatched and the page settled … the target does not
|
|
13
|
+
* react" — about a click nobody dispatched, blaming a control that was fine, on exactly the
|
|
14
|
+
* re-query-and-retry path the stale-ref message sends the agent down.
|
|
15
|
+
*
|
|
16
|
+
* Marking only on the success path is what makes that unrepeatable: a failure mode added later
|
|
17
|
+
* cannot forget to clean up state that was never written.
|
|
18
|
+
*/
|
|
19
|
+
markActed(cursor, action, mutatedWithin) {
|
|
6
20
|
this.#cursor = cursor;
|
|
21
|
+
this.#effect = { action, mutatedWithin };
|
|
7
22
|
}
|
|
8
23
|
cursor() {
|
|
9
24
|
return this.#cursor;
|
|
@@ -14,9 +29,6 @@ export class LastAct {
|
|
|
14
29
|
source() {
|
|
15
30
|
return this.#source;
|
|
16
31
|
}
|
|
17
|
-
markEffect(action, mutatedWithin) {
|
|
18
|
-
this.#effect = { action, mutatedWithin };
|
|
19
|
-
}
|
|
20
32
|
/** Empty when nothing has acted — callers then fall back to checks that need no action context. */
|
|
21
33
|
effect() {
|
|
22
34
|
return this.#effect ?? {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { noteEmptyRead } from '../tools/observed-nothing.js';
|
|
2
3
|
import { AGENT_ASK_NOTICE, AGENT_WAITING_NOTICE, PresenterTone, SessionState, } from '@reticlehq/core';
|
|
3
4
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
4
5
|
import { sessionIdShape } from '../tools/tool-kit.js';
|
|
@@ -95,7 +96,11 @@ export const LIVE_CONTROL_TOOLS = [
|
|
|
95
96
|
outputSchema: { messages: z.array(z.unknown()) },
|
|
96
97
|
handler: (deps, args) => {
|
|
97
98
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
98
|
-
return
|
|
99
|
+
// An empty inbox and a panel that is not wired both return `[]`. The first means the human has
|
|
100
|
+
// said nothing; the second means an agent is waiting on a channel that does not exist.
|
|
101
|
+
return Promise.resolve(noteEmptyRead({ messages: session.drainInbox() }, 'messages', {
|
|
102
|
+
noun: 'messages from the human since the last poll',
|
|
103
|
+
}));
|
|
99
104
|
},
|
|
100
105
|
},
|
|
101
106
|
{
|
|
@@ -135,7 +140,7 @@ export const LIVE_CONTROL_TOOLS = [
|
|
|
135
140
|
session.pushNarration(`✓ fixed: ${mark.note}`);
|
|
136
141
|
}
|
|
137
142
|
}
|
|
138
|
-
const source = args['all']
|
|
143
|
+
const source = true === args['all'] ? session.allMarks() : session.pendingMarks();
|
|
139
144
|
const marks = source.map((m) => ({ ...m, fix: buildFixHint(m) }));
|
|
140
145
|
const out = {
|
|
141
146
|
marks,
|
|
@@ -27,7 +27,7 @@ export class LiveControl {
|
|
|
27
27
|
/** Queue a human note. Empty/whitespace-only text is ignored so a stray enter cannot spam the agent. */
|
|
28
28
|
push(text, t) {
|
|
29
29
|
const trimmed = text.trim();
|
|
30
|
-
if (trimmed.length
|
|
30
|
+
if (0 === trimmed.length)
|
|
31
31
|
return;
|
|
32
32
|
this.#inbox.push({ text: trimmed, t });
|
|
33
33
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the thing on this port a DEV SERVER serving the app, or just something on a port?
|
|
3
|
+
*
|
|
4
|
+
* The probe used to answer "something accepted a TCP connection", which on macOS means every Mac
|
|
5
|
+
* reports Apple's AirPlay Receiver on port 5000 as the user's dev server. Reproduced here: it
|
|
6
|
+
* answers `HTTP/1.1 403 Forbidden`, `Server: AirTunes/950.7.1`, no body. The agent was then told
|
|
7
|
+
* "something IS listening on port 5000 and this project is wired for Reticle" while the app was on
|
|
8
|
+
* 3100 — a diagnostic confidently naming a service with nothing to do with the project.
|
|
9
|
+
*
|
|
10
|
+
* A dev server serving an app answers `GET /` with a document. That is the whole test. A false
|
|
11
|
+
* negative costs a less specific message; a false positive costs trust in every diagnostic Reticle
|
|
12
|
+
* prints, which is far more expensive.
|
|
13
|
+
*/
|
|
14
|
+
export declare function looksLikeDevServer(status: number, contentType: string | undefined): boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the thing on this port a DEV SERVER serving the app, or just something on a port?
|
|
3
|
+
*
|
|
4
|
+
* The probe used to answer "something accepted a TCP connection", which on macOS means every Mac
|
|
5
|
+
* reports Apple's AirPlay Receiver on port 5000 as the user's dev server. Reproduced here: it
|
|
6
|
+
* answers `HTTP/1.1 403 Forbidden`, `Server: AirTunes/950.7.1`, no body. The agent was then told
|
|
7
|
+
* "something IS listening on port 5000 and this project is wired for Reticle" while the app was on
|
|
8
|
+
* 3100 — a diagnostic confidently naming a service with nothing to do with the project.
|
|
9
|
+
*
|
|
10
|
+
* A dev server serving an app answers `GET /` with a document. That is the whole test. A false
|
|
11
|
+
* negative costs a less specific message; a false positive costs trust in every diagnostic Reticle
|
|
12
|
+
* prints, which is far more expensive.
|
|
13
|
+
*/
|
|
14
|
+
/** Statuses that mean "I will not serve you", which no local dev server does for its own app root. */
|
|
15
|
+
const REFUSES = new Set([401, 403, 407]);
|
|
16
|
+
export function looksLikeDevServer(status, contentType) {
|
|
17
|
+
if (REFUSES.has(status))
|
|
18
|
+
return false;
|
|
19
|
+
// A redirect IS a server routing: a dev server on a base path answers `/` with one.
|
|
20
|
+
if (status >= 300 && status < 400)
|
|
21
|
+
return true;
|
|
22
|
+
// Otherwise it has to be serving a document. A 404 that renders HTML is still a dev server — the
|
|
23
|
+
// server is there, only the route is missing.
|
|
24
|
+
return contentType !== undefined && contentType.toLowerCase().includes('text/html');
|
|
25
|
+
}
|