@reticlehq/server 2.12.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 +67 -109
- 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 -1
- package/dist/bridge/bridge.js +247 -164
- 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 +6 -6
- package/dist/capsule/causal-summary.js +7 -2
- package/dist/cli/cli-doctor.js +31 -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 +15 -1
- package/dist/cli/cli-parse.js +212 -8
- package/dist/cli/cli-verify.d.ts +1 -0
- package/dist/cli/cli-verify.js +3 -2
- package/dist/cli/cloud-cli.js +428 -259
- 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.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 +9 -0
- package/dist/cli/doctor-rows.js +9 -0
- 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/mcp-command.js +51 -20
- package/dist/cli/project-name.d.ts +30 -0
- package/dist/cli/project-name.js +44 -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 +134 -80
- package/dist/cloud/cloud-config.d.ts +22 -1
- package/dist/cloud/cloud-config.js +64 -5
- package/dist/cloud/cloud-sync.d.ts +5 -5
- package/dist/cloud/cloud-sync.js +33 -2
- 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 +3 -0
- package/dist/crawl/crawl.js +5 -0
- package/dist/daemon/daemon-resolve.d.ts +43 -0
- package/dist/daemon/daemon-resolve.js +69 -0
- package/dist/daemon/dev-servers.d.ts +15 -0
- package/dist/daemon/dev-servers.js +68 -0
- package/dist/daemon/wake-decision.d.ts +32 -0
- package/dist/daemon/wake-decision.js +38 -0
- package/dist/domain/domain-tools.js +2 -2
- package/dist/events/contradictions.d.ts +18 -0
- package/dist/events/contradictions.js +134 -21
- package/dist/events/declared.d.ts +7 -4
- package/dist/events/declared.js +15 -6
- package/dist/events/echo-mismatch.js +64 -14
- package/dist/events/event-filters.d.ts +8 -0
- package/dist/events/event-filters.js +19 -2
- package/dist/events/predicate-eval.js +53 -5
- 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.d.ts +5 -0
- package/dist/events/predicate.js +37 -5
- package/dist/events/split-text-miss.d.ts +30 -0
- package/dist/events/split-text-miss.js +44 -0
- package/dist/flows/flake.d.ts +6 -6
- 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 +239 -22
- 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-tools.js +58 -32
- package/dist/flows/flows.d.ts +8 -0
- package/dist/flows/flows.js +468 -2
- 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 +30 -12
- 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/verify-change-tools.js +5 -2
- package/dist/honesty/blind-spots.d.ts +55 -2
- package/dist/honesty/blind-spots.js +63 -3
- package/dist/honesty/honesty.d.ts +24 -0
- package/dist/honesty/honesty.js +25 -1
- package/dist/honesty/instrumentation-gaps.d.ts +9 -0
- package/dist/honesty/instrumentation-gaps.js +29 -2
- package/dist/honesty/verified.d.ts +2 -1
- package/dist/honesty/verified.js +16 -1
- package/dist/http-server.js +12 -0
- package/dist/impact/impact-recorder.d.ts +31 -8
- package/dist/impact/impact-recorder.js +104 -21
- package/dist/impact/impact-store.d.ts +15 -1
- package/dist/impact/impact-store.js +43 -2
- package/dist/index.js +113 -7
- package/dist/init/agent-rules.d.ts +13 -6
- package/dist/init/agent-rules.js +23 -12
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +28 -0
- package/dist/init/capabilities.d.ts +4 -1
- package/dist/init/capabilities.js +22 -2
- package/dist/init/confirm.d.ts +31 -7
- package/dist/init/confirm.js +48 -8
- package/dist/init/cra.d.ts +20 -4
- package/dist/init/cra.js +57 -17
- 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 +3 -1
- package/dist/init/dev-script.js +8 -5
- 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 +1 -1
- package/dist/init/mcp.js +1 -1
- package/dist/init/node-io.js +11 -1
- package/dist/init/plan-framework.js +81 -45
- package/dist/init/plan.d.ts +20 -7
- package/dist/init/plan.js +20 -45
- package/dist/init/preflight.d.ts +28 -0
- package/dist/init/preflight.js +38 -0
- package/dist/init/run.d.ts +32 -9
- package/dist/init/run.js +141 -119
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/snippets.js +41 -25
- 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.js +25 -5
- package/dist/input/scroll-tools.js +4 -6
- package/dist/input/viewport-tools.js +5 -4
- package/dist/intent/inline-intent.d.ts +10 -19
- package/dist/intent/inline-intent.js +91 -1
- 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 +12 -0
- package/dist/intent/intent-store.js +21 -0
- package/dist/intent/intent-subject.d.ts +45 -0
- package/dist/intent/intent-subject.js +177 -0
- package/dist/intent/intent-tools.js +65 -2
- package/dist/intent/open-intents.d.ts +21 -0
- package/dist/intent/open-intents.js +15 -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 +18 -2
- package/dist/mcp/attach-memory.js +50 -7
- 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 -1
- package/dist/mcp/mcp-proxy.js +52 -62
- package/dist/mcp/mcp.js +7 -1
- 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/tool-catalog-cache.d.ts +8 -0
- package/dist/mcp/tool-catalog-cache.js +12 -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/propose-consequences.js +12 -3
- package/dist/pool/browser-pool.d.ts +63 -4
- package/dist/pool/browser-pool.js +113 -4
- package/dist/pool/playwright-launcher.js +9 -0
- package/dist/project/project-tools.js +3 -3
- package/dist/project/reticle-dir.d.ts +15 -2
- package/dist/project/reticle-dir.js +21 -4
- 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 +22 -1
- 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/lease-visibility.d.ts +67 -0
- package/dist/session/lease-visibility.js +76 -0
- package/dist/session/no-session-diagnosis.d.ts +23 -0
- package/dist/session/no-session-diagnosis.js +58 -17
- package/dist/session/no-session-next-action.d.ts +7 -0
- package/dist/session/no-session-next-action.js +42 -3
- package/dist/session/no-session-watch.d.ts +10 -6
- package/dist/session/no-session-watch.js +51 -19
- 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-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 +61 -3
- 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.js +36 -1
- package/dist/session/session-tools.js +2 -2
- package/dist/session/session.d.ts +22 -2
- package/dist/session/session.js +87 -7
- 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/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.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 -1
- package/dist/telemetry/project-profile.js +11 -6
- package/dist/telemetry/proxy-telemetry.d.ts +3 -0
- package/dist/telemetry/proxy-telemetry.js +21 -0
- 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/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 +100 -213
- package/dist/tools/assert-verdict.js +22 -16
- package/dist/tools/browser-tools.js +6 -2
- package/dist/tools/dynamic-tools.js +24 -5
- package/dist/tools/error-recovery.d.ts +1 -0
- package/dist/tools/error-recovery.js +8 -0
- package/dist/tools/invoke-tool.js +59 -9
- package/dist/tools/lease-tools.d.ts +30 -4
- package/dist/tools/lease-tools.js +169 -5
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +22 -0
- package/dist/tools/numeric-bounds.d.ts +100 -0
- package/dist/tools/numeric-bounds.js +125 -0
- package/dist/tools/observe-tools.js +47 -40
- package/dist/tools/read-tools.js +28 -7
- package/dist/tools/real-input-attempt.d.ts +10 -0
- package/dist/tools/real-input-attempt.js +59 -1
- package/dist/tools/reconcile-tools.js +4 -4
- package/dist/tools/tool-kit.d.ts +12 -0
- package/dist/tools/tool-kit.js +6 -1
- package/dist/tools/tool-names.d.ts +2 -0
- package/dist/tools/tool-names.js +2 -0
- 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 +56 -8
- 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 +18 -0
- package/dist/update/update-nudge.js +20 -0
- 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/cli/doctor.mdx +1 -0
- package/docs/cli/serve.mdx +4 -2
- package/docs/cli.mdx +7 -3
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +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.mdx +1 -1
- package/docs/skill-file.mdx +3 -3
- package/docs/telemetry-contract.md +108 -4
- package/docs/tools-act-sequence.mdx +16 -0
- package/docs/usage.md +1 -1
- package/package.json +4 -3
- package/dist/init/cursor.d.ts +0 -24
- package/dist/init/cursor.js +0 -80
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { residualQueryChecks } from './predicate-schema.js';
|
|
2
|
+
/** Composite predicates nest; the unusable one can be at any depth. */
|
|
3
|
+
const NESTED_KEYS = ['predicates', 'predicate'];
|
|
4
|
+
const isRecord = (v) => 'object' === typeof v && null !== v;
|
|
5
|
+
/** Every element query in a predicate tree, in the order a reader would meet them. */
|
|
6
|
+
function elementQueries(predicate, found = []) {
|
|
7
|
+
if (!isRecord(predicate))
|
|
8
|
+
return found;
|
|
9
|
+
if (isRecord(predicate['query']))
|
|
10
|
+
found.push(predicate['query']);
|
|
11
|
+
for (const key of NESTED_KEYS) {
|
|
12
|
+
const nested = predicate[key];
|
|
13
|
+
if (Array.isArray(nested))
|
|
14
|
+
for (const child of nested)
|
|
15
|
+
elementQueries(child, found);
|
|
16
|
+
else if (isRecord(nested))
|
|
17
|
+
elementQueries(nested, found);
|
|
18
|
+
}
|
|
19
|
+
return found;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The reason this predicate cannot be evaluated, or undefined when it can.
|
|
23
|
+
*
|
|
24
|
+
* Only checks what is knowable WITHOUT the page — a locator that names fields the resolver drops is
|
|
25
|
+
* wrong whatever the DOM contains, which is what makes it safe to refuse before acting. Anything
|
|
26
|
+
* that depends on what is actually rendered is still decided after the action, where it belongs.
|
|
27
|
+
*/
|
|
28
|
+
export function unevaluablePredicateReason(predicate) {
|
|
29
|
+
for (const query of elementQueries(predicate)) {
|
|
30
|
+
const residual = residualQueryChecks(query);
|
|
31
|
+
if (0 === residual.unusable.length)
|
|
32
|
+
continue;
|
|
33
|
+
return (`the element locator ignores ${residual.unusable.map((f) => `\`${f}\``).join(', ')} ` +
|
|
34
|
+
`in ${JSON.stringify(query)} — it resolves by the first of by+value, component/source, role, ` +
|
|
35
|
+
'text, label, placeholder, testid, alt that is present, and nothing here can check the rest. ' +
|
|
36
|
+
'Assert them one locator at a time, or move the extra field into the locator. ' +
|
|
37
|
+
'Nothing was acted on: this predicate could never have been evaluated, so refusing it costs ' +
|
|
38
|
+
'you nothing and spending the action on it would have cost you the verdict.');
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
@@ -17,6 +17,20 @@ export declare const RouteDecidedBy: {
|
|
|
17
17
|
readonly CHANGE: "route-change";
|
|
18
18
|
readonly CURRENT: "current-route";
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The path the router is actually on.
|
|
22
|
+
*
|
|
23
|
+
* Under a hash router the document pathname is `/` on every page and the whole route lives in the
|
|
24
|
+
* fragment — which is why `contains` below matches the full route. `pathname` was left comparing the
|
|
25
|
+
* document path, so `{ pathname: '/posts/12/show' }` could never pass on a HashRouter app, the
|
|
26
|
+
* standard router for a packaged Electron/Tauri renderer. That is a guaranteed false red: Reticle
|
|
27
|
+
* telling a user their working app is broken.
|
|
28
|
+
*
|
|
29
|
+
* The fragment WINS rather than being accepted alongside the document path. Honouring both would
|
|
30
|
+
* make `{ pathname: '/' }` trivially true on every hash-routed page — a green that says nothing
|
|
31
|
+
* about where the app is, which is the failure this predicate exists to prevent.
|
|
32
|
+
*/
|
|
33
|
+
export declare function routePathOf(pathname: string, hash: string): string;
|
|
20
34
|
export declare function evalRoute(events: ReticleEvent[], p: Extract<Predicate, {
|
|
21
35
|
kind: typeof PredicateKind.ROUTE;
|
|
22
36
|
}>,
|
|
@@ -17,6 +17,26 @@ export const RouteDecidedBy = {
|
|
|
17
17
|
CHANGE: 'route-change',
|
|
18
18
|
CURRENT: 'current-route',
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The path the router is actually on.
|
|
22
|
+
*
|
|
23
|
+
* Under a hash router the document pathname is `/` on every page and the whole route lives in the
|
|
24
|
+
* fragment — which is why `contains` below matches the full route. `pathname` was left comparing the
|
|
25
|
+
* document path, so `{ pathname: '/posts/12/show' }` could never pass on a HashRouter app, the
|
|
26
|
+
* standard router for a packaged Electron/Tauri renderer. That is a guaranteed false red: Reticle
|
|
27
|
+
* telling a user their working app is broken.
|
|
28
|
+
*
|
|
29
|
+
* The fragment WINS rather than being accepted alongside the document path. Honouring both would
|
|
30
|
+
* make `{ pathname: '/' }` trivially true on every hash-routed page — a green that says nothing
|
|
31
|
+
* about where the app is, which is the failure this predicate exists to prevent.
|
|
32
|
+
*/
|
|
33
|
+
export function routePathOf(pathname, hash) {
|
|
34
|
+
if (!hash.startsWith('#/'))
|
|
35
|
+
return pathname;
|
|
36
|
+
const withoutHash = hash.slice(1);
|
|
37
|
+
const query = withoutHash.indexOf('?');
|
|
38
|
+
return -1 === query ? withoutHash : withoutHash.slice(0, query);
|
|
39
|
+
}
|
|
20
40
|
/**
|
|
21
41
|
* The session's live URL, split the way a route-change event already carries it.
|
|
22
42
|
*
|
|
@@ -30,6 +50,7 @@ function readCurrentRoute(url) {
|
|
|
30
50
|
const hash = parsed?.hash ?? '';
|
|
31
51
|
return {
|
|
32
52
|
pathname,
|
|
53
|
+
routePath: routePathOf(pathname, hash),
|
|
33
54
|
full: `${pathname}${search}${hash}`,
|
|
34
55
|
decidedBy: RouteDecidedBy.CURRENT,
|
|
35
56
|
data: { pathname, search, hash, url, decidedBy: RouteDecidedBy.CURRENT },
|
|
@@ -55,12 +76,17 @@ currentUrl) {
|
|
|
55
76
|
const routes = events.filter((e) => e.type === EventType.ROUTE_CHANGE);
|
|
56
77
|
const last = routes.at(-1);
|
|
57
78
|
const reading = last !== undefined
|
|
58
|
-
? {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
? (() => {
|
|
80
|
+
const changedPath = str(last.data['pathname']) ?? str(last.data['to']) ?? '';
|
|
81
|
+
const changedHash = str(last.data['hash']) ?? '';
|
|
82
|
+
return {
|
|
83
|
+
pathname: changedPath,
|
|
84
|
+
routePath: routePathOf(changedPath, changedHash),
|
|
85
|
+
full: `${changedPath}${str(last.data['search']) ?? ''}${changedHash}`,
|
|
86
|
+
decidedBy: RouteDecidedBy.CHANGE,
|
|
87
|
+
data: { ...last.data, decidedBy: RouteDecidedBy.CHANGE },
|
|
88
|
+
};
|
|
89
|
+
})()
|
|
64
90
|
: currentUrl === undefined || 0 === currentUrl.length
|
|
65
91
|
? undefined
|
|
66
92
|
: readCurrentRoute(currentUrl);
|
|
@@ -73,11 +99,13 @@ currentUrl) {
|
|
|
73
99
|
assertion: 'route.changed',
|
|
74
100
|
};
|
|
75
101
|
}
|
|
76
|
-
|
|
102
|
+
// The ROUTER's path, not the document's — see routePathOf.
|
|
103
|
+
const pathname = reading.routePath;
|
|
77
104
|
if (p.pathname !== undefined && pathname !== p.pathname) {
|
|
78
105
|
return {
|
|
79
106
|
pass: false,
|
|
80
|
-
|
|
107
|
+
// No ` is` after the clause: it read "route changed to is '/'".
|
|
108
|
+
failureReason: `${describeRouteSource(reading)} '${pathname}', expected '${p.pathname}'`,
|
|
81
109
|
observed: `${describeRouteSource(reading)} '${pathname}'`,
|
|
82
110
|
expected: `route '${p.pathname}'`,
|
|
83
111
|
assertion: 'route.pathname',
|
|
@@ -45,6 +45,11 @@ export interface PredicateSession {
|
|
|
45
45
|
* this hook (e.g. tests that never disconnect) simply leaves in-flight predicates until timeout.
|
|
46
46
|
*/
|
|
47
47
|
onDisconnect?(listener: () => void): () => void;
|
|
48
|
+
/**
|
|
49
|
+
* True when the tab is hidden or stale enough that the browser is throttling it.
|
|
50
|
+
* Optional: a fake that never throttles simply omits it.
|
|
51
|
+
*/
|
|
52
|
+
throttled?(): boolean;
|
|
48
53
|
}
|
|
49
54
|
export declare function evaluatePredicate(session: PredicateSession, predicate: Predicate, since?: number, diagnose?: boolean): Promise<EvalResult>;
|
|
50
55
|
/**
|
package/dist/events/predicate.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ElementState, PredicateKind, ReticleCommand, isSameDocument, } from '@reticlehq/core';
|
|
1
|
+
import { ElementState, PredicateKind, ReticleCommand, THROTTLED_STARVED_NOTE, isSameDocument, } from '@reticlehq/core';
|
|
2
2
|
import { log } from '../log.js';
|
|
3
3
|
import { bindSpanContext } from '../trace.js';
|
|
4
4
|
import { selectPath, capDepth } from '../session/state-select.js';
|
|
5
5
|
import { describeTestidMiss } from './testid-near-miss.js';
|
|
6
|
+
import { describeSplitTextMiss } from './split-text-miss.js';
|
|
6
7
|
import { predicateToExpectedLinks } from '../capsule/predicate-to-links.js';
|
|
7
8
|
import { isAmbient, ambientKeyOf } from '../journal/ambient.js';
|
|
8
9
|
import { evalRoute } from './predicate-route.js';
|
|
@@ -154,7 +155,12 @@ async function evalElement(session, query, state, absent, diagnose) {
|
|
|
154
155
|
// testid-near-miss.ts.
|
|
155
156
|
const present = match.hint?.presentTestids ?? [];
|
|
156
157
|
const alsoHere = query.testid === undefined ? undefined : describeTestidMiss(query.testid, present);
|
|
157
|
-
|
|
158
|
+
// A text miss where the string is on the page but split across children reads exactly like an
|
|
159
|
+
// element that never rendered. Naming the container is the difference between a retry and a bug
|
|
160
|
+
// report against working code. See split-text-miss.ts.
|
|
161
|
+
const splitText = describeSplitTextMiss(match.hint?.splitText);
|
|
162
|
+
const clause = splitText ?? (alsoHere === undefined || '' === alsoHere ? undefined : alsoHere);
|
|
163
|
+
const suffix = clause === undefined ? '' : ` — ${clause}`;
|
|
158
164
|
return {
|
|
159
165
|
pass: false,
|
|
160
166
|
failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}${suffix}`,
|
|
@@ -419,7 +425,33 @@ const SUPERSEDED_ASSERTION = 'window.evidence-superseded';
|
|
|
419
425
|
function predicateSince(predicate) {
|
|
420
426
|
return 'since' in predicate && 'number' === typeof predicate.since ? predicate.since : 0;
|
|
421
427
|
}
|
|
422
|
-
|
|
428
|
+
/**
|
|
429
|
+
* A miss on a throttled tab is not a missing render. The browser has starved the tab, so a timeout
|
|
430
|
+
* there may mean it never ran — which must not look like "the text is absent".
|
|
431
|
+
*
|
|
432
|
+
* Sets `inconclusive` only. The PROSE is already handled one layer up by
|
|
433
|
+
* `annotateStarvedFailure` (session-health.ts), which suffixes the same fact onto the
|
|
434
|
+
* failureReason so the concrete diagnosis still leads; writing it here as well would put the
|
|
435
|
+
* sentence in every throttled failure twice. What was missing was never the sentence — it was the
|
|
436
|
+
* FIELD an agent gates on, so a starved wait graded `assertion-failed` and sent somebody to fix
|
|
437
|
+
* working code.
|
|
438
|
+
*
|
|
439
|
+
* Idempotent, and a more specific `inconclusive` (unreadable locator, superseded window) is never
|
|
440
|
+
* overwritten.
|
|
441
|
+
*/
|
|
442
|
+
function annotateThrottledMiss(session, result) {
|
|
443
|
+
if (result.pass)
|
|
444
|
+
return result;
|
|
445
|
+
if (true !== session.throttled?.())
|
|
446
|
+
return result;
|
|
447
|
+
if (result.inconclusive !== undefined)
|
|
448
|
+
return result;
|
|
449
|
+
return { ...result, inconclusive: THROTTLED_STARVED_NOTE };
|
|
450
|
+
}
|
|
451
|
+
export async function evaluatePredicate(session, predicate, since = 0, diagnose = true) {
|
|
452
|
+
return annotateThrottledMiss(session, await evaluatePredicateRaw(session, predicate, since, diagnose));
|
|
453
|
+
}
|
|
454
|
+
async function evaluatePredicateRaw(session, predicate, since = 0,
|
|
423
455
|
// Compute the (extra-round-trip) near-miss diagnostics on element failures. Default true so a
|
|
424
456
|
// one-shot assert is fully diagnostic; the wait loop passes false on its interim polls (which read
|
|
425
457
|
// only `pass`) and true on the final timeout eval, so a flood no longer pays for a diagnostic nobody
|
|
@@ -760,11 +792,11 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
|
|
|
760
792
|
// `{ pass, evidence, failureReason }` construction DISCARDED them on every timed-out wait and
|
|
761
793
|
// assert. So the highest-value localization signal was computed and then thrown away exactly
|
|
762
794
|
// on the failure path where it matters, no matter what the schema declared.
|
|
763
|
-
finish({
|
|
795
|
+
finish(annotateThrottledMiss(session, {
|
|
764
796
|
...r,
|
|
765
797
|
pass: false,
|
|
766
798
|
failureReason: r.failureReason ?? 'timed out waiting for predicate',
|
|
767
|
-
});
|
|
799
|
+
}));
|
|
768
800
|
})
|
|
769
801
|
.catch((error) => {
|
|
770
802
|
finish(failed(error));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning "no element matched {text: …}" into something an agent can act on, when the string IS here.
|
|
3
|
+
*
|
|
4
|
+
* `by: text` matches an element's own text nodes. A label rendered across several children —
|
|
5
|
+
* `v-html`, one `<span>` per word, a highlighted substring — is on the page and matches nothing, and
|
|
6
|
+
* the verdict is byte-identical to the one for an element that never rendered.
|
|
7
|
+
*
|
|
8
|
+
* That equivalence is the expensive part. Reported from the field: `act_and_wait` returned
|
|
9
|
+
* `effect.appeared` containing `"Move to Reticle Repro Folder"`, the `until` predicate in the SAME
|
|
10
|
+
* call searched for that string and failed, and the drive ended in a bug report against an app that
|
|
11
|
+
* had rendered correctly. `appeared` concatenates an added subtree while `text` reads direct text
|
|
12
|
+
* only, so the channel an agent is most likely to copy from produces the one locator this query
|
|
13
|
+
* cannot resolve.
|
|
14
|
+
*
|
|
15
|
+
* Naming the container ends it in one step: `ref` is a locator, and `self: true` returns the scope
|
|
16
|
+
* element itself, so the recovery is a query the agent can paste rather than another guess.
|
|
17
|
+
*/
|
|
18
|
+
/** The container descriptor the browser attaches to a missed text query. */
|
|
19
|
+
export interface SplitTextOwner {
|
|
20
|
+
ref?: string;
|
|
21
|
+
role?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The "…but it IS here, split across children" clause, or undefined when there is nothing to say.
|
|
26
|
+
*
|
|
27
|
+
* Undefined — not a vaguer sentence — when the browser found no container: an ordinary miss keeps the
|
|
28
|
+
* short message it already had. A clause that fires on every failure stops carrying information.
|
|
29
|
+
*/
|
|
30
|
+
export declare function describeSplitTextMiss(owner: SplitTextOwner | undefined): string | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning "no element matched {text: …}" into something an agent can act on, when the string IS here.
|
|
3
|
+
*
|
|
4
|
+
* `by: text` matches an element's own text nodes. A label rendered across several children —
|
|
5
|
+
* `v-html`, one `<span>` per word, a highlighted substring — is on the page and matches nothing, and
|
|
6
|
+
* the verdict is byte-identical to the one for an element that never rendered.
|
|
7
|
+
*
|
|
8
|
+
* That equivalence is the expensive part. Reported from the field: `act_and_wait` returned
|
|
9
|
+
* `effect.appeared` containing `"Move to Reticle Repro Folder"`, the `until` predicate in the SAME
|
|
10
|
+
* call searched for that string and failed, and the drive ended in a bug report against an app that
|
|
11
|
+
* had rendered correctly. `appeared` concatenates an added subtree while `text` reads direct text
|
|
12
|
+
* only, so the channel an agent is most likely to copy from produces the one locator this query
|
|
13
|
+
* cannot resolve.
|
|
14
|
+
*
|
|
15
|
+
* Naming the container ends it in one step: `ref` is a locator, and `self: true` returns the scope
|
|
16
|
+
* element itself, so the recovery is a query the agent can paste rather than another guess.
|
|
17
|
+
*/
|
|
18
|
+
/** Enough of the label to recognise it; the message is read in a tool result, not a log. */
|
|
19
|
+
const MAX_TEXT = 60;
|
|
20
|
+
/** What the container is, in the fewest words that still identify it on the page. */
|
|
21
|
+
function nameOf(owner) {
|
|
22
|
+
const name = owner.name !== undefined && owner.name.length > 0 ? owner.name : undefined;
|
|
23
|
+
const role = owner.role !== undefined && owner.role.length > 0 ? owner.role : 'element';
|
|
24
|
+
return name === undefined ? role : `${role} '${truncate(name)}'`;
|
|
25
|
+
}
|
|
26
|
+
function truncate(value) {
|
|
27
|
+
return value.length > MAX_TEXT ? `${value.slice(0, MAX_TEXT)}…` : value;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The "…but it IS here, split across children" clause, or undefined when there is nothing to say.
|
|
31
|
+
*
|
|
32
|
+
* Undefined — not a vaguer sentence — when the browser found no container: an ordinary miss keeps the
|
|
33
|
+
* short message it already had. A clause that fires on every failure stops carrying information.
|
|
34
|
+
*/
|
|
35
|
+
export function describeSplitTextMiss(owner) {
|
|
36
|
+
if (owner === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
const where = nameOf(owner);
|
|
39
|
+
const scope = owner.ref !== undefined && owner.ref.length > 0 ? owner.ref : undefined;
|
|
40
|
+
const recovery = scope === undefined
|
|
41
|
+
? 'scope to that container and match it as a whole'
|
|
42
|
+
: `retry as { scope: '${scope}', self: true }`;
|
|
43
|
+
return `that text IS on the page, split across the children of ${where} — no single element's own text carries it, so no text query can match it; ${recovery}`;
|
|
44
|
+
}
|
package/dist/flows/flake.d.ts
CHANGED
|
@@ -9,11 +9,11 @@ declare const FlakeRecordSchema: z.ZodObject<{
|
|
|
9
9
|
runs: z.ZodNumber;
|
|
10
10
|
fails: z.ZodNumber;
|
|
11
11
|
}, "strip", z.ZodTypeAny, {
|
|
12
|
-
fails: number;
|
|
13
12
|
runs: number;
|
|
14
|
-
}, {
|
|
15
13
|
fails: number;
|
|
14
|
+
}, {
|
|
16
15
|
runs: number;
|
|
16
|
+
fails: number;
|
|
17
17
|
}>;
|
|
18
18
|
export type FlakeRecord = z.infer<typeof FlakeRecordSchema>;
|
|
19
19
|
/** Per-flow map (flow name → record). */
|
|
@@ -24,23 +24,23 @@ export declare const FlakeFileSchema: z.ZodObject<{
|
|
|
24
24
|
runs: z.ZodNumber;
|
|
25
25
|
fails: z.ZodNumber;
|
|
26
26
|
}, "strip", z.ZodTypeAny, {
|
|
27
|
-
fails: number;
|
|
28
27
|
runs: number;
|
|
29
|
-
}, {
|
|
30
28
|
fails: number;
|
|
29
|
+
}, {
|
|
31
30
|
runs: number;
|
|
31
|
+
fails: number;
|
|
32
32
|
}>>;
|
|
33
33
|
}, "strip", z.ZodTypeAny, {
|
|
34
34
|
version: 1;
|
|
35
35
|
flows: Record<string, {
|
|
36
|
-
fails: number;
|
|
37
36
|
runs: number;
|
|
37
|
+
fails: number;
|
|
38
38
|
}>;
|
|
39
39
|
}, {
|
|
40
40
|
version: 1;
|
|
41
41
|
flows: Record<string, {
|
|
42
|
-
fails: number;
|
|
43
42
|
runs: number;
|
|
43
|
+
fails: number;
|
|
44
44
|
}>;
|
|
45
45
|
}>;
|
|
46
46
|
export declare function emptyRecord(): FlakeRecord;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the team already knows about the flow being verified.
|
|
3
|
+
*
|
|
4
|
+
* ## Why a replay should ask at all
|
|
5
|
+
*
|
|
6
|
+
* Shared memory is written on every verification and, measured across this project's whole corpus,
|
|
7
|
+
* read essentially never: every subject on the dashboard showed `fetches: 0` and `readers: []`. Not
|
|
8
|
+
* because the data is useless — because consulting it is a SEPARATE act an agent has to remember to
|
|
9
|
+
* perform. Knowledge that must be deliberately fetched is a wiki, and wikis are not read.
|
|
10
|
+
*
|
|
11
|
+
* So verification asks on the agent's behalf. Replaying `checkout` returns what the project knows
|
|
12
|
+
* about checkout beside the verdict, which is the difference between memory the platform stores and
|
|
13
|
+
* memory the platform USES — and it is the only way the read counts on the coverage map ever become
|
|
14
|
+
* a real signal rather than a number nobody moves.
|
|
15
|
+
*
|
|
16
|
+
* ## Why this file is pure
|
|
17
|
+
*
|
|
18
|
+
* The fetch is the caller's. What a replay is ENTITLED to ask for — which subject, how much of it —
|
|
19
|
+
* is a rule worth testing without a network, and the rule is where the mistakes live.
|
|
20
|
+
*/
|
|
21
|
+
import type { FlowFile } from '@reticlehq/core';
|
|
22
|
+
/**
|
|
23
|
+
* How many records travel back with a verdict.
|
|
24
|
+
*
|
|
25
|
+
* Small on purpose. This rides on every replay, and a verdict that arrives with fifty statements
|
|
26
|
+
* attached is one whose actual result is now below the fold. The subject page is one call away for
|
|
27
|
+
* an agent that wants the rest.
|
|
28
|
+
*/
|
|
29
|
+
export declare const MEMORY_CONSULT_LIMIT = 5;
|
|
30
|
+
/**
|
|
31
|
+
* The subject a replay should ask about.
|
|
32
|
+
*
|
|
33
|
+
* The FLOW NAME, because a flow is a feature and that is the axis the store shards on — replaying
|
|
34
|
+
* `checkout` should surface what the team knows about checkout, not about the route it happens to
|
|
35
|
+
* start on. A flow whose name says nothing gets nothing: asking for `unsorted` would return the
|
|
36
|
+
* project's junk drawer on every verdict, which is worse than silence because it looks like an
|
|
37
|
+
* answer.
|
|
38
|
+
*/
|
|
39
|
+
export declare function consultSubjectFor(flow: FlowFile): string | undefined;
|
|
40
|
+
/** One thing the project knows, as it travels back with a verdict. */
|
|
41
|
+
export interface ConsultedMemory {
|
|
42
|
+
statement: string;
|
|
43
|
+
status: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Trim what came back to what is worth carrying.
|
|
47
|
+
*
|
|
48
|
+
* PROVED records first: a statement something has actually verified is stronger evidence about the
|
|
49
|
+
* feature than one somebody proposed and never checked. Beyond that the order is the server's, and
|
|
50
|
+
* anything without a statement is dropped rather than rendered as an empty line.
|
|
51
|
+
*/
|
|
52
|
+
export declare function selectConsulted(entries: readonly {
|
|
53
|
+
statement?: unknown;
|
|
54
|
+
status?: unknown;
|
|
55
|
+
}[], limit?: number): ConsultedMemory[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many records travel back with a verdict.
|
|
3
|
+
*
|
|
4
|
+
* Small on purpose. This rides on every replay, and a verdict that arrives with fifty statements
|
|
5
|
+
* attached is one whose actual result is now below the fold. The subject page is one call away for
|
|
6
|
+
* an agent that wants the rest.
|
|
7
|
+
*/
|
|
8
|
+
export const MEMORY_CONSULT_LIMIT = 5;
|
|
9
|
+
/**
|
|
10
|
+
* The subject a replay should ask about.
|
|
11
|
+
*
|
|
12
|
+
* The FLOW NAME, because a flow is a feature and that is the axis the store shards on — replaying
|
|
13
|
+
* `checkout` should surface what the team knows about checkout, not about the route it happens to
|
|
14
|
+
* start on. A flow whose name says nothing gets nothing: asking for `unsorted` would return the
|
|
15
|
+
* project's junk drawer on every verdict, which is worse than silence because it looks like an
|
|
16
|
+
* answer.
|
|
17
|
+
*/
|
|
18
|
+
export function consultSubjectFor(flow) {
|
|
19
|
+
const name = flow.name;
|
|
20
|
+
if ('string' !== typeof name || 0 === name.trim().length)
|
|
21
|
+
return undefined;
|
|
22
|
+
return name.trim();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Trim what came back to what is worth carrying.
|
|
26
|
+
*
|
|
27
|
+
* PROVED records first: a statement something has actually verified is stronger evidence about the
|
|
28
|
+
* feature than one somebody proposed and never checked. Beyond that the order is the server's, and
|
|
29
|
+
* anything without a statement is dropped rather than rendered as an empty line.
|
|
30
|
+
*/
|
|
31
|
+
export function selectConsulted(entries, limit = MEMORY_CONSULT_LIMIT) {
|
|
32
|
+
const usable = entries.flatMap((e) => 'string' === typeof e.statement && e.statement.trim().length > 0
|
|
33
|
+
? [{ statement: e.statement, status: 'string' === typeof e.status ? e.status : 'unknown' }]
|
|
34
|
+
: []);
|
|
35
|
+
const proved = usable.filter((e) => 'proved' === e.status);
|
|
36
|
+
const rest = usable.filter((e) => 'proved' !== e.status);
|
|
37
|
+
return [...proved, ...rest].slice(0, limit);
|
|
38
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { FlowErrorCode, type FlowFile, type FlowReplayResult, type ReticleEvent } from '@reticlehq/core';
|
|
1
|
+
import { FlowErrorCode, type CommandResult, type FlowFile, type FlowReplayResult, type ReticleEvent } from '@reticlehq/core';
|
|
2
|
+
import type { ArrivalClock } from '../tools/navigate-arrival.js';
|
|
3
|
+
import type { SessionManager } from '../session/session-manager.js';
|
|
4
|
+
import type { Session } from '../session/session.js';
|
|
2
5
|
import type { ToolDeps } from '../tools/tools.js';
|
|
3
6
|
export declare function latestRecordedFlow(events: ReticleEvent[]): {
|
|
4
7
|
name: string;
|
|
@@ -6,18 +9,25 @@ export declare function latestRecordedFlow(events: ReticleEvent[]): {
|
|
|
6
9
|
} | undefined;
|
|
7
10
|
/** Map a structured FlowErrorCode to a legible one-line message for the agent. */
|
|
8
11
|
export declare function flowErrorMessage(code: FlowErrorCode): string;
|
|
12
|
+
/** The slice of a session the start-path logic reads: the live URL plus the event buffer. */
|
|
13
|
+
interface StartPathSession {
|
|
14
|
+
url?: string;
|
|
15
|
+
eventsSince(cursor: number): ReticleEvent[];
|
|
16
|
+
}
|
|
9
17
|
/**
|
|
10
18
|
* When a flow records the page its journey started on (`startPath`) and the tab is currently on a
|
|
11
19
|
* different route, step 1 drifts for a reason that has nothing to do with the app regressing — the
|
|
12
20
|
* anchor simply isn't on this page yet. Detect that so the decision says "navigate there first"
|
|
13
21
|
* instead of a mystifying "a step no longer matches". Returns undefined when the routes agree or the
|
|
14
|
-
* current route is unobservable
|
|
15
|
-
*
|
|
16
|
-
*
|
|
22
|
+
* current route is unobservable — never a false alarm. Replay normally never gets here on a
|
|
23
|
+
* wrong page (arriveAtStartPath navigates before step 1); this is the fallback for when that
|
|
24
|
+
* navigation was refused or the SDK never reconnected in the window.
|
|
17
25
|
*/
|
|
18
|
-
export declare function startPathMismatchHint(flow: FlowFile, session:
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
export declare function startPathMismatchHint(flow: FlowFile, session: StartPathSession): string | undefined;
|
|
27
|
+
export declare function arriveAtStartPath(sessions: SessionManager, session: StartPathSession & {
|
|
28
|
+
id: string;
|
|
29
|
+
command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
|
|
30
|
+
}, flow: FlowFile, timeoutMs?: number, clock?: ArrivalClock): Promise<Session | undefined>;
|
|
21
31
|
/**
|
|
22
32
|
* Replay one named flow end to end: load → re-resolve+run each step → assert the success oracle →
|
|
23
33
|
* status + decision. Shared by reticle_flow_replay (single flow) and reticle_flow_verify (whole suite) so
|
|
@@ -30,3 +40,4 @@ export declare function replayNamedFlow(deps: ToolDeps, args: Record<string, unk
|
|
|
30
40
|
* documented to work headless), so a missing/unknown session degrades to the global/legacy store.
|
|
31
41
|
*/
|
|
32
42
|
export declare function sessionProjectId(deps: ToolDeps, sessionId: string | undefined): string | undefined;
|
|
43
|
+
export {};
|