@reticlehq/server 3.1.0 → 3.3.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 +30 -28
- package/dist/command/cli/adhoc-suite.d.ts +19 -0
- package/dist/command/cli/adhoc-suite.js +88 -0
- package/dist/command/cli/adhoc-verdict.d.ts +23 -2
- package/dist/command/cli/adhoc-verdict.js +18 -10
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-doctor.js +37 -5
- package/dist/command/cli/cli-flow-commands.d.ts +2 -1
- package/dist/command/cli/cli-kill.js +1 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
- package/dist/command/cli/cli-parse-grammar.js +7 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -5
- package/dist/command/cli/cli-parse-verify.js +86 -13
- package/dist/command/cli/cli-parse.d.ts +8 -1
- package/dist/command/cli/cli-parse.js +38 -7
- package/dist/command/cli/cli-verify.d.ts +59 -0
- package/dist/command/cli/cli-verify.js +131 -19
- package/dist/command/cli/cloud-cli.js +7 -6
- package/dist/command/cli/cloud-kit.d.ts +0 -2
- package/dist/command/cli/cloud-kit.js +3 -7
- package/dist/command/cli/config/config-discovery.d.ts +12 -0
- package/dist/command/cli/config/config-discovery.js +19 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
- package/dist/command/cli/doctor/doctor-rows.js +12 -0
- package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +1 -1
- package/dist/command/cli/launch/cli-launch.d.ts +3 -23
- package/dist/command/cli/launch/cli-launch.js +29 -70
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
- package/dist/command/cli/mcp-command.js +5 -3
- package/dist/command/cli/ports/resolve/cli-port.d.ts +15 -1
- package/dist/command/cli/ports/resolve/cli-port.js +50 -2
- package/dist/command/cli/report-command.d.ts +16 -0
- package/dist/command/cli/report-command.js +97 -0
- package/dist/command/cli/status/status-lines.d.ts +28 -0
- package/dist/command/cli/status/status-lines.js +117 -0
- package/dist/command/cli.d.ts +1 -1
- package/dist/command/cli.js +29 -177
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +32 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +93 -0
- package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
- package/dist/command/daemon/binding/loopback-reach.js +63 -0
- package/dist/command/daemon/binding/port-presence.d.ts +1 -1
- package/dist/command/daemon/daemon-resilience.d.ts +13 -1
- package/dist/command/daemon/daemon-resilience.js +12 -1
- package/dist/command/daemon/daemon.d.ts +2 -11
- package/dist/command/daemon/daemon.js +8 -11
- package/dist/command/daemon/idle-shutdown.d.ts +2 -1
- package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
- package/dist/command/daemon/lifetime/heartbeat.js +10 -0
- package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
- package/dist/command/daemon/lifetime/log-cap.js +115 -0
- package/dist/command/setup/agent-notice-memory.d.ts +16 -0
- package/dist/command/setup/agent-notice-memory.js +65 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
- package/dist/command/setup/bringup/dev-server-wait.js +16 -0
- package/dist/command/setup/bringup/ensure-daemon.js +1 -1
- package/dist/command/setup/init/init-host.js +8 -0
- package/dist/command/setup/init/init-runtime.js +13 -1
- package/dist/command/setup/node-effects.js +1 -1
- package/dist/command/setup/probe/page-probe.js +9 -2
- package/dist/command/setup/run-setup.d.ts +25 -7
- package/dist/command/setup/run-setup.js +51 -9
- package/dist/command/setup/setup-command.js +31 -3
- package/dist/command/setup/terminal/confirm.js +2 -1
- package/dist/command/update/updater.d.ts +27 -0
- package/dist/command/update/updater.js +35 -2
- package/dist/features/crawl/crawl-tools.js +3 -1
- package/dist/features/crawl/crawl.d.ts +2 -0
- package/dist/features/crawl/crawl.js +3 -1
- package/dist/features/harness/drive-report.d.ts +73 -0
- package/dist/features/harness/drive-report.js +196 -0
- package/dist/features/harness/driver.js +29 -8
- package/dist/features/harness/drivers.d.ts +19 -0
- package/dist/features/harness/drivers.js +19 -0
- package/dist/features/harness/fill-values.d.ts +67 -0
- package/dist/features/harness/fill-values.js +153 -0
- package/dist/features/harness/jev-driver.d.ts +188 -0
- package/dist/features/harness/jev-driver.js +661 -0
- package/dist/features/harness/openai-driver.d.ts +46 -0
- package/dist/features/harness/openai-driver.js +197 -0
- package/dist/features/harness/platform-config.d.ts +62 -0
- package/dist/features/harness/platform-config.js +97 -0
- package/dist/features/visual/visual-tools.js +26 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +80 -130
- package/dist/judgement/capsule/capsule-store.d.ts +31 -2
- package/dist/judgement/capsule/capsule-store.js +58 -6
- package/dist/judgement/capsule/capsule.js +5 -0
- package/dist/judgement/capsule/causal-summary.d.ts +13 -1
- package/dist/judgement/capsule/causal-summary.js +7 -0
- package/dist/judgement/domain/domain-model.js +38 -9
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
- package/dist/judgement/phenomena/phenomena.d.ts +2 -1
- package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
- package/dist/judgement/runs/artifact/gap-report.js +46 -0
- package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
- package/dist/judgement/runs/artifact/gap-summary.js +85 -0
- package/dist/judgement/runs/artifact/render-report.js +4 -1
- package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
- package/dist/judgement/runs/artifact/to-artifact.js +1 -1
- package/dist/judgement/runs/context-tools.js +21 -3
- package/dist/judgement/runs/drive-run.js +7 -2
- package/dist/judgement/runs/verification-sync.d.ts +2 -2
- package/dist/judgement/runs/verification-sync.js +9 -2
- package/dist/language/flows/affected-tools.js +6 -3
- package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
- package/dist/language/flows/annotate-notes/annotate.js +66 -18
- package/dist/language/flows/flow-anchor.d.ts +12 -0
- package/dist/language/flows/flow-anchor.js +20 -0
- package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
- package/dist/language/flows/flow-expect-grammar.js +92 -11
- package/dist/language/flows/flow-intent.js +1 -1
- package/dist/language/flows/flow-learning.d.ts +3 -0
- package/dist/language/flows/flow-learning.js +39 -8
- package/dist/language/flows/flow-replay-run.d.ts +55 -10
- package/dist/language/flows/flow-replay-run.js +217 -58
- package/dist/language/flows/flow-replay-types.d.ts +9 -0
- package/dist/language/flows/flow-replay.d.ts +29 -11
- package/dist/language/flows/flow-replay.js +221 -203
- package/dist/language/flows/flow-result.d.ts +18 -1
- package/dist/language/flows/flow-result.js +18 -2
- package/dist/language/flows/flow-step-runners.d.ts +26 -10
- package/dist/language/flows/flow-step-runners.js +20 -4
- package/dist/language/flows/flow-store-for-session.d.ts +2 -1
- package/dist/language/flows/flow-success.d.ts +21 -6
- package/dist/language/flows/flow-success.js +50 -138
- package/dist/language/flows/flow-tools.js +8 -4
- package/dist/language/flows/flows.d.ts +15 -10
- package/dist/language/flows/flows.js +17 -4
- package/dist/language/flows/heal.d.ts +9 -1
- package/dist/language/flows/heal.js +24 -2
- package/dist/language/flows/learned-sources.d.ts +21 -0
- package/dist/language/flows/learned-sources.js +84 -0
- package/dist/language/flows/narrow-write.d.ts +4 -4
- package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
- package/dist/language/flows/recording/recording-backtrack.js +1 -1
- package/dist/language/flows/recording/replay-halt.d.ts +2 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +2 -2
- package/dist/language/flows/replay.js +13 -9
- package/dist/language/flows/stores/annotation-store.d.ts +5 -5
- package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
- package/dist/language/flows/stores/flake-store.js +2 -3
- package/dist/language/flows/suite/coverage.d.ts +1 -1
- package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
- package/dist/language/flows/suite-selection.d.ts +15 -1
- package/dist/language/flows/suite-selection.js +1 -1
- package/dist/language/flows/verify-change-tools.js +21 -4
- package/dist/memory/cloud/account-state.d.ts +1 -1
- package/dist/memory/cloud/account-state.js +5 -4
- package/dist/memory/cloud/cloud-config.d.ts +9 -0
- package/dist/memory/cloud/cloud-config.js +9 -0
- package/dist/memory/cloud/cloud-sync.d.ts +7 -4
- package/dist/memory/cloud/cloud-sync.js +11 -6
- package/dist/memory/cloud/harness-config.d.ts +12 -0
- package/dist/memory/cloud/harness-config.js +58 -0
- package/dist/memory/cloud/harness-offer.d.ts +44 -0
- package/dist/memory/cloud/harness-offer.js +146 -0
- package/dist/memory/cloud/harness-switch.d.ts +11 -0
- package/dist/memory/cloud/harness-switch.js +53 -0
- package/dist/memory/cloud/sync-cycle.d.ts +12 -0
- package/dist/memory/cloud/sync-cycle.js +29 -4
- package/dist/memory/cloud/sync-daemon.d.ts +35 -2
- package/dist/memory/cloud/sync-daemon.js +45 -0
- package/dist/memory/cloud/sync-disk.js +11 -6
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +2 -1
- package/dist/memory/impact/impact-store.d.ts +5 -0
- package/dist/memory/impact/impact-store.js +25 -0
- package/dist/memory/intent/inline-intent.js +8 -3
- package/dist/memory/intent/intent-shard-store.d.ts +33 -47
- package/dist/memory/intent/intent-shard-store.js +253 -141
- package/dist/memory/intent/intent-shard.d.ts +22 -23
- package/dist/memory/intent/intent-shard.js +1 -1
- package/dist/memory/intent/intent-store.d.ts +13 -8
- package/dist/memory/intent/intent-store.js +46 -90
- package/dist/memory/intent/intent-tools.js +34 -23
- package/dist/memory/journal/ambient-store.js +2 -3
- package/dist/memory/journal/attach-journal.d.ts +11 -0
- package/dist/memory/journal/attach-journal.js +1 -1
- package/dist/memory/journal/deviation-report.js +4 -1
- package/dist/memory/journal/deviation-service.js +10 -2
- package/dist/memory/journal/drive-flow.d.ts +2 -2
- package/dist/memory/journal/drive-flow.js +21 -12
- package/dist/memory/journal/envelope-store.js +2 -3
- package/dist/memory/journal/journal-query.d.ts +35 -1
- package/dist/memory/journal/journal-query.js +46 -0
- package/dist/memory/journal/journal-recorder.d.ts +71 -2
- package/dist/memory/journal/journal-recorder.js +45 -7
- package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
- package/dist/memory/journal/on-disk/retain-policy.js +44 -0
- package/dist/memory/journal/on-disk/retention.d.ts +91 -2
- package/dist/memory/journal/on-disk/retention.js +181 -11
- package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -2
- package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -38
- package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +88 -0
- package/dist/memory/journal/route-template.d.ts +22 -0
- package/dist/memory/journal/route-template.js +44 -0
- package/dist/memory/journal/self-observation.d.ts +3 -0
- package/dist/memory/journal/self-observation.js +67 -0
- package/dist/memory/journal/session-end.d.ts +17 -4
- package/dist/memory/journal/session-end.js +85 -7
- package/dist/memory/journal/session-journal.d.ts +122 -5
- package/dist/memory/journal/session-journal.js +489 -32
- package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
- package/dist/memory/project/artifact-root-resolver.js +89 -0
- package/dist/memory/project/artifact-root.d.ts +39 -2
- package/dist/memory/project/artifact-root.js +58 -1
- package/dist/memory/project/baselines.d.ts +1 -1
- package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
- package/dist/memory/project/dir/fill-value-store.js +88 -0
- package/dist/memory/project/dir/reticle-dir.d.ts +15 -5
- package/dist/memory/project/dir/reticle-dir.js +11 -0
- package/dist/memory/project/fs/fs-port.d.ts +22 -1
- package/dist/memory/project/fs/fs-port.js +72 -5
- package/dist/memory/project/fs/write-atomic.d.ts +26 -0
- package/dist/memory/project/fs/write-atomic.js +48 -0
- package/dist/memory/project/learned-routes.d.ts +16 -2
- package/dist/memory/project/learned-routes.js +26 -10
- package/dist/memory/project/project-for-root.d.ts +30 -0
- package/dist/memory/project/project-for-root.js +43 -0
- package/dist/memory/project/project-tools.js +10 -5
- package/dist/memory/project/session-root.d.ts +35 -6
- package/dist/memory/project/session-root.js +70 -8
- package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
- package/dist/memory/recall/prior/connection-memory.js +8 -0
- package/dist/memory/recall/registered-projects.d.ts +2 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
- package/dist/portal/bridge/auth-failure-reason.js +20 -3
- package/dist/portal/bridge/bridge.d.ts +6 -1
- package/dist/portal/bridge/bridge.js +36 -2
- package/dist/portal/input/real-input.js +2 -1
- package/dist/portal/pool/browser-pool.d.ts +10 -0
- package/dist/portal/pool/browser-pool.js +10 -0
- package/dist/portal/pool/playwright-launcher.js +1 -0
- package/dist/portal/pool/pool-contract.d.ts +2 -0
- package/dist/portal/pool/storage-seed.d.ts +2 -1
- package/dist/portal/pool/zero-install.d.ts +27 -0
- package/dist/portal/pool/zero-install.js +54 -0
- package/dist/portal/realm/web-realm.js +6 -0
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
- package/dist/portal/session/dev-server/served-document.d.ts +21 -0
- package/dist/portal/session/dev-server/served-document.js +57 -0
- package/dist/portal/session/facts/background-traffic.d.ts +9 -0
- package/dist/portal/session/facts/background-traffic.js +25 -0
- package/dist/portal/session/facts/session-replaced.d.ts +19 -0
- package/dist/portal/session/facts/session-replaced.js +22 -0
- package/dist/portal/session/live-control-tools.js +48 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
- package/dist/portal/session/no-session-diagnosis.js +67 -60
- package/dist/portal/session/no-session-next-action.js +3 -2
- package/dist/portal/session/no-session-watch.js +14 -10
- package/dist/portal/session/presence/attachment-history.d.ts +13 -2
- package/dist/portal/session/rebind.d.ts +18 -0
- package/dist/portal/session/rebind.js +38 -0
- package/dist/portal/session/session-health.d.ts +61 -0
- package/dist/portal/session/session-health.js +40 -1
- package/dist/portal/session/session-info.d.ts +6 -0
- package/dist/portal/session/session-manager.d.ts +13 -0
- package/dist/portal/session/session-manager.js +42 -3
- package/dist/portal/session/session-successor.d.ts +11 -0
- package/dist/portal/session/session-successor.js +14 -1
- package/dist/portal/session/session-verdict-facts.d.ts +28 -0
- package/dist/portal/session/session-verdict-facts.js +15 -0
- package/dist/portal/session/session.d.ts +26 -10
- package/dist/portal/session/session.js +48 -49
- package/dist/status-payload.d.ts +12 -1
- package/dist/status-payload.js +4 -1
- package/dist/surface/http-server.js +3 -5
- package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
- package/dist/surface/mcp/faults/mcp-outage.js +20 -4
- package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
- package/dist/surface/mcp/mcp-proxy.js +23 -12
- package/dist/surface/mcp/mcp.d.ts +10 -1
- package/dist/surface/mcp/mcp.js +59 -29
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
- package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
- package/dist/surface/mcp/proxy/proxy-log.js +7 -22
- package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
- package/dist/surface/mcp/server-instructions.js +12 -1
- package/dist/surface/tools/act/act-preflight.js +6 -1
- package/dist/surface/tools/act/already-true.d.ts +40 -0
- package/dist/surface/tools/act/already-true.js +36 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +6 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +13 -0
- package/dist/surface/tools/act/sequence-grade.js +13 -0
- package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
- package/dist/surface/tools/act/settle-in-flight.js +29 -6
- package/dist/surface/tools/act-capsule.js +49 -8
- package/dist/surface/tools/act-tools.js +25 -24
- package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
- package/dist/surface/tools/args/numeric-bounds.js +30 -3
- package/dist/surface/tools/assert/assert-grade.js +14 -2
- package/dist/surface/tools/assert/assert-verdict.js +74 -21
- package/dist/surface/tools/browser-tools.js +4 -3
- package/dist/surface/tools/coverage-tools.js +5 -2
- package/dist/surface/tools/dynamic-tools.js +43 -46
- package/dist/surface/tools/error-recovery.d.ts +6 -0
- package/dist/surface/tools/error-recovery.js +15 -0
- package/dist/surface/tools/explore-tools.js +80 -7
- package/dist/surface/tools/feedback-tools.d.ts +2 -1
- package/dist/surface/tools/feedback-tools.js +50 -3
- package/dist/surface/tools/first-sentence.d.ts +24 -0
- package/dist/surface/tools/first-sentence.js +0 -0
- package/dist/surface/tools/harness-explore.d.ts +125 -1
- package/dist/surface/tools/harness-explore.js +401 -15
- package/dist/surface/tools/harness-plan.d.ts +76 -0
- package/dist/surface/tools/harness-plan.js +156 -0
- package/dist/surface/tools/harness-toolset.js +12 -1
- package/dist/surface/tools/invoke-tool.js +82 -9
- package/dist/surface/tools/lease-hint.d.ts +8 -0
- package/dist/surface/tools/lease-hint.js +10 -0
- package/dist/surface/tools/lease-tools.d.ts +16 -28
- package/dist/surface/tools/lease-tools.js +53 -5
- package/dist/surface/tools/lineage-tools.js +1 -1
- package/dist/surface/tools/live-call-text.d.ts +6 -0
- package/dist/surface/tools/live-call-text.js +36 -2
- package/dist/surface/tools/observe-tools.js +69 -15
- package/dist/surface/tools/read/query-windowed.d.ts +1 -1
- package/dist/surface/tools/read/query-windowed.js +1 -1
- package/dist/surface/tools/read-tools.js +1 -2
- package/dist/surface/tools/real-input-attempt.d.ts +0 -5
- package/dist/surface/tools/real-input-attempt.js +1 -1
- package/dist/surface/tools/tool-kit.d.ts +23 -3
- package/dist/surface/tools/tool-kit.js +6 -1
- package/dist/surface/tools/tool-surface.js +17 -5
- package/dist/surface/tools/tools-helpers.d.ts +8 -7
- package/dist/surface/tools/tools-helpers.js +11 -13
- package/dist/surface/tools/tools.js +70 -30
- package/dist/telemetry/bug-found.js +32 -2
- package/dist/telemetry/feedback-context.js +3 -3
- package/dist/telemetry/feedback-local.d.ts +1 -0
- package/dist/telemetry/feedback-local.js +1 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/harness-drive.d.ts +6 -0
- package/dist/telemetry/harness-drive.js +39 -0
- package/dist/telemetry/hud-metrics.d.ts +32 -0
- package/dist/telemetry/hud-metrics.js +95 -0
- package/dist/telemetry/mcp-connection.d.ts +14 -1
- package/dist/telemetry/mcp-connection.js +17 -2
- package/dist/telemetry/onboarding-firsts.d.ts +30 -1
- package/dist/telemetry/onboarding-firsts.js +54 -12
- package/dist/telemetry/session-metrics.d.ts +5 -0
- package/dist/telemetry/session-metrics.js +15 -1
- package/dist/telemetry/verification-of.js +31 -3
- package/dist/wire-journal.d.ts +31 -0
- package/dist/wire-journal.js +100 -0
- package/docs/README.md +1 -0
- package/docs/agent-cheatsheet.md +7 -3
- package/docs/autodrive.md +42 -2
- package/docs/benchmarks.md +1 -1
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/cloud.mdx +11 -9
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/init.mdx +1 -0
- package/docs/cli/report.mdx +44 -0
- package/docs/cli/status.mdx +35 -20
- package/docs/cli.mdx +8 -18
- package/docs/desktop.mdx +1 -1
- package/docs/faq.mdx +1 -1
- package/docs/flows.md +11 -0
- package/docs/frameworks.mdx +113 -1
- package/docs/install-agentic.mdx +36 -0
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/predicates.mdx +144 -4
- package/docs/system-map.md +6 -4
- package/docs/telemetry.md +2 -1
- package/docs/token-efficiency.md +2 -2
- package/docs/tools/overview.mdx +1 -1
- package/docs/tools/query.mdx +10 -10
- package/docs/troubleshooting.mdx +8 -2
- package/docs/usage.md +2 -2
- package/docs/what-is-recorded.md +62 -0
- package/package.json +5 -5
- package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
- package/dist/judgement/outcome/predicate-to-expect.js +0 -148
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICATE_PREFIX, flowExpectHasConsequence, flowExpectIsPresenceOnly, } from '@reticlehq/core';
|
|
1
|
+
import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICATE_PREFIX, flowExpectHasConsequence, flowExpectIsPresenceOnly, PredicateKind, } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* The PURE compiler at the heart of the annotation facet. A structured annotation +
|
|
4
4
|
* the current captured-step count → an AnnotateOutcome (a result envelope + the patch to apply to
|
|
@@ -14,35 +14,71 @@ import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICA
|
|
|
14
14
|
* compilation is explicitly FUTURE — an NL string never reaches here (AnnotationSchema
|
|
15
15
|
* rejects it upstream; the tool maps that to UNKNOWN_KIND). No NL parser exists or is faked.
|
|
16
16
|
*/
|
|
17
|
+
/**
|
|
18
|
+
* A network annotation as a predicate, `count` included.
|
|
19
|
+
*
|
|
20
|
+
* Written once because it is compiled on two paths — a step gate and a flow's success oracle — and
|
|
21
|
+
* the first version of this migration wrote it twice and dropped `count` from both. The schema's own
|
|
22
|
+
* comment says why that matters: presence says the request fired, cardinality catches the
|
|
23
|
+
* double-submit that fired it twice, and `count` is the point of the annotation.
|
|
24
|
+
*
|
|
25
|
+
* A counted assertion also gates on `settled`. A wait-until-true evaluator satisfies `count: 1` the
|
|
26
|
+
* instant the FIRST request lands, which is before a duplicate arrives — so without the gate the
|
|
27
|
+
* guard passes on exactly the bug it exists to catch.
|
|
28
|
+
*/
|
|
29
|
+
function netPredicate(net) {
|
|
30
|
+
const clause = {
|
|
31
|
+
kind: PredicateKind.NET,
|
|
32
|
+
};
|
|
33
|
+
if (net.method !== undefined)
|
|
34
|
+
clause.method = net.method;
|
|
35
|
+
if (net.urlContains !== undefined)
|
|
36
|
+
clause.urlContains = net.urlContains;
|
|
37
|
+
if (net.status !== undefined)
|
|
38
|
+
clause.status = net.status;
|
|
39
|
+
if (net.count === undefined)
|
|
40
|
+
return clause;
|
|
41
|
+
clause.count = net.count;
|
|
42
|
+
return { kind: PredicateKind.ALL_OF, predicates: [{ kind: PredicateKind.SETTLED }, clause] };
|
|
43
|
+
}
|
|
17
44
|
export function compileAnnotation(a, stepCount) {
|
|
18
45
|
switch (a.kind) {
|
|
19
46
|
case AnnotationKind.ASSERT_SIGNAL: {
|
|
20
47
|
if (0 === stepCount)
|
|
21
48
|
return noStep();
|
|
22
|
-
const
|
|
49
|
+
const signal = {
|
|
50
|
+
kind: PredicateKind.SIGNAL,
|
|
51
|
+
name: a.name,
|
|
52
|
+
};
|
|
23
53
|
if (a.dataMatches !== undefined)
|
|
24
|
-
|
|
25
|
-
return stepPatch(a, stepCount,
|
|
54
|
+
signal.dataMatches = a.dataMatches;
|
|
55
|
+
return stepPatch(a, stepCount, signal);
|
|
26
56
|
}
|
|
27
57
|
case AnnotationKind.ASSERT_VISIBLE: {
|
|
28
58
|
if (0 === stepCount)
|
|
29
59
|
return noStep();
|
|
30
|
-
return stepPatch(a, stepCount, {
|
|
60
|
+
return stepPatch(a, stepCount, {
|
|
61
|
+
kind: PredicateKind.ELEMENT,
|
|
62
|
+
query: { testid: a.testid },
|
|
63
|
+
});
|
|
31
64
|
}
|
|
32
65
|
case AnnotationKind.ASSERT_STATE: {
|
|
33
66
|
if (0 === stepCount)
|
|
34
67
|
return noStep();
|
|
35
|
-
const state = {
|
|
68
|
+
const state = {
|
|
69
|
+
kind: PredicateKind.STATE,
|
|
70
|
+
path: a.statePath,
|
|
71
|
+
};
|
|
36
72
|
if (a.store !== undefined)
|
|
37
73
|
state.store = a.store;
|
|
38
74
|
if (a.equals !== undefined)
|
|
39
75
|
state.equals = a.equals;
|
|
40
|
-
return stepPatch(a, stepCount,
|
|
76
|
+
return stepPatch(a, stepCount, state);
|
|
41
77
|
}
|
|
42
78
|
case AnnotationKind.ASSERT_NET: {
|
|
43
79
|
if (0 === stepCount)
|
|
44
80
|
return noStep();
|
|
45
|
-
return stepPatch(a, stepCount,
|
|
81
|
+
return stepPatch(a, stepCount, netPredicate(a.net));
|
|
46
82
|
}
|
|
47
83
|
case AnnotationKind.MARK_DYNAMIC:
|
|
48
84
|
return {
|
|
@@ -53,39 +89,51 @@ export function compileAnnotation(a, stepCount) {
|
|
|
53
89
|
// Precedence: signal > state > net > console > testid (a consequence end-condition beats a
|
|
54
90
|
// presence check). None of them → MISSING_FIELD.
|
|
55
91
|
if (a.signal !== undefined) {
|
|
56
|
-
return flowSuccess(a, {
|
|
92
|
+
return flowSuccess(a, { kind: PredicateKind.SIGNAL, name: a.signal });
|
|
57
93
|
}
|
|
58
94
|
if (a.statePath !== undefined) {
|
|
59
|
-
const state = {
|
|
95
|
+
const state = {
|
|
96
|
+
kind: PredicateKind.STATE,
|
|
97
|
+
path: a.statePath,
|
|
98
|
+
};
|
|
60
99
|
if (a.store !== undefined)
|
|
61
100
|
state.store = a.store;
|
|
62
101
|
if (a.equals !== undefined)
|
|
63
102
|
state.equals = a.equals;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return flowSuccess(a,
|
|
103
|
+
// `hold` was a v1 flag meaning "this must STILL be true after the page settles". A predicate
|
|
104
|
+
// says that by composing: the settle gate is a clause, not a boolean on another clause.
|
|
105
|
+
return flowSuccess(a, true === a.hold
|
|
106
|
+
? { kind: PredicateKind.ALL_OF, predicates: [{ kind: PredicateKind.SETTLED }, state] }
|
|
107
|
+
: state);
|
|
67
108
|
}
|
|
68
109
|
if (a.net !== undefined) {
|
|
69
|
-
return flowSuccess(a,
|
|
110
|
+
return flowSuccess(a, netPredicate(a.net));
|
|
70
111
|
}
|
|
71
112
|
if (a.console !== undefined) {
|
|
72
|
-
return flowSuccess(a, {
|
|
113
|
+
return flowSuccess(a, {
|
|
114
|
+
kind: PredicateKind.CONSOLE,
|
|
115
|
+
...(a.console.level === undefined ? {} : { level: a.console.level }),
|
|
116
|
+
...(a.console.absent === undefined ? {} : { absent: a.console.absent }),
|
|
117
|
+
});
|
|
73
118
|
}
|
|
74
119
|
if (a.testid !== undefined) {
|
|
75
|
-
return flowSuccess(a, {
|
|
120
|
+
return flowSuccess(a, { kind: PredicateKind.ELEMENT, query: { testid: a.testid } });
|
|
76
121
|
}
|
|
77
122
|
// Last, and above only MISSING_FIELD. An element is a locator and text is content; where a
|
|
78
123
|
// caller offered both, the locator is the more stable anchor. But text beats nothing at all,
|
|
79
124
|
// which is what an app whose only observable is a rendered value used to get (#811).
|
|
80
125
|
if (a.text !== undefined) {
|
|
81
|
-
const text = {
|
|
126
|
+
const text = {
|
|
127
|
+
kind: PredicateKind.TEXT,
|
|
128
|
+
contains: a.text.contains,
|
|
129
|
+
};
|
|
82
130
|
if (a.text.scope !== undefined)
|
|
83
131
|
text.scope = a.text.scope;
|
|
84
132
|
if (a.text.absent !== undefined)
|
|
85
133
|
text.absent = a.text.absent;
|
|
86
134
|
if (a.text.visible !== undefined)
|
|
87
135
|
text.visible = a.text.visible;
|
|
88
|
-
return flowSuccess(a,
|
|
136
|
+
return flowSuccess(a, text);
|
|
89
137
|
}
|
|
90
138
|
return { result: { ok: false, code: AnnotationErrorCode.MISSING_FIELD } };
|
|
91
139
|
}
|
|
@@ -28,6 +28,18 @@ export declare function nearestTestid(missing: string, present: string[]): strin
|
|
|
28
28
|
export declare function nearestIsAmbiguous(missing: string, present: string[]): boolean;
|
|
29
29
|
/** Build the legible-drift record for a testid anchor that resolved to zero live elements. */
|
|
30
30
|
export declare function testidDrift(value: string, hint: QueryEmptyHint | undefined): Drift;
|
|
31
|
+
/**
|
|
32
|
+
* Build the drift for a step whose anchor matched MORE THAN ONE live element.
|
|
33
|
+
*
|
|
34
|
+
* `ambiguous: true` is set because that is exactly what it is, and it is the field heal already
|
|
35
|
+
* reads to refuse an auto-rebind -- an ambiguous drift must never be healed to an arbitrary pick,
|
|
36
|
+
* which is the same reasoning that made this a drift instead of a note in the first place.
|
|
37
|
+
*
|
|
38
|
+
* `nearest` is null on purpose. There is no nearest: the anchor was found, several times over, and
|
|
39
|
+
* proposing one of the matches as "the" match would re-make the guess this reason exists to stop.
|
|
40
|
+
* The fix is a NARROWER anchor, and only a human or an agent looking at the page can choose it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ambiguousAnchorDrift(value: string, matches: number): Drift;
|
|
31
43
|
/**
|
|
32
44
|
* Build the drift for a step whose `expect.element` testid was absent after the action ran.
|
|
33
45
|
*
|
|
@@ -139,6 +139,26 @@ export function testidDrift(value, hint) {
|
|
|
139
139
|
drift.ambiguous = true;
|
|
140
140
|
return drift;
|
|
141
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Build the drift for a step whose anchor matched MORE THAN ONE live element.
|
|
144
|
+
*
|
|
145
|
+
* `ambiguous: true` is set because that is exactly what it is, and it is the field heal already
|
|
146
|
+
* reads to refuse an auto-rebind -- an ambiguous drift must never be healed to an arbitrary pick,
|
|
147
|
+
* which is the same reasoning that made this a drift instead of a note in the first place.
|
|
148
|
+
*
|
|
149
|
+
* `nearest` is null on purpose. There is no nearest: the anchor was found, several times over, and
|
|
150
|
+
* proposing one of the matches as "the" match would re-make the guess this reason exists to stop.
|
|
151
|
+
* The fix is a NARROWER anchor, and only a human or an agent looking at the page can choose it.
|
|
152
|
+
*/
|
|
153
|
+
export function ambiguousAnchorDrift(value, matches) {
|
|
154
|
+
return {
|
|
155
|
+
reasonKind: DriftReason.ANCHOR_AMBIGUOUS,
|
|
156
|
+
reason: `testid "${value}" matched ${String(matches)} live elements — which one the recording meant is not decidable; anchor this step to something unique`,
|
|
157
|
+
anchor: value,
|
|
158
|
+
nearest: null,
|
|
159
|
+
ambiguous: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
142
162
|
/**
|
|
143
163
|
* Build the drift for a step whose `expect.element` testid was absent after the action ran.
|
|
144
164
|
*
|
|
@@ -18,9 +18,8 @@ export declare const FlowParseNote: {
|
|
|
18
18
|
readonly NOT_JSON: "flow file is not valid JSON — fix the syntax or regenerate it with reticle_flow_save";
|
|
19
19
|
readonly MALFORMED: "flow file is malformed — fix or regenerate it with reticle_flow_save";
|
|
20
20
|
readonly UNSUPPORTED_SHAPE: "valid JSON, unsupported expect shape";
|
|
21
|
-
readonly UNENFORCED: "valid JSON, but this expect uses a predicate kind a saved flow cannot enforce (settled, route, animation, anyOf, not)";
|
|
22
21
|
};
|
|
23
|
-
|
|
22
|
+
type CoerceExpectResult = {
|
|
24
23
|
ok: true;
|
|
25
24
|
value: unknown;
|
|
26
25
|
} | {
|
|
@@ -31,3 +30,4 @@ export declare function coerceFlowExpect(raw: unknown): CoerceExpectResult;
|
|
|
31
30
|
export declare function coerceFlowFileExpects(raw: unknown): CoerceExpectResult;
|
|
32
31
|
export declare function describeFlowZodFailure(error: ZodError): string;
|
|
33
32
|
export declare function parseFlowFileText(text: string): FlowResult<FlowFile>;
|
|
33
|
+
export {};
|
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
* re-save writes the canonical shape. A kind a saved flow cannot enforce is refused, not stripped
|
|
12
12
|
* to an empty expect that would grade asserted-while-unchecked.
|
|
13
13
|
*/
|
|
14
|
-
import { FlowErrorCode, FlowFileSchema } from '@reticlehq/core';
|
|
15
|
-
import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
16
|
-
import { predicateToExpect } from '../../judgement/outcome/predicate-to-expect.js';
|
|
14
|
+
import { FlowExpectSchema, FlowPredicateSchema, READABLE_FLOW_VERSIONS, FlowErrorCode, FlowFileSchema, } from '@reticlehq/core';
|
|
17
15
|
export const FlowParseNote = {
|
|
18
16
|
NOT_JSON: 'flow file is not valid JSON — fix the syntax or regenerate it with reticle_flow_save',
|
|
19
17
|
MALFORMED: 'flow file is malformed — fix or regenerate it with reticle_flow_save',
|
|
20
18
|
UNSUPPORTED_SHAPE: 'valid JSON, unsupported expect shape',
|
|
21
|
-
UNENFORCED: 'valid JSON, but this expect uses a predicate kind a saved flow cannot enforce (settled, route, animation, anyOf, not)',
|
|
22
19
|
};
|
|
23
20
|
function isRecord(value) {
|
|
24
21
|
return 'object' === typeof value && null !== value && !Array.isArray(value);
|
|
@@ -43,20 +40,44 @@ function flattenSignalObject(raw) {
|
|
|
43
40
|
}
|
|
44
41
|
export function coerceFlowExpect(raw) {
|
|
45
42
|
if (isRecord(raw) && 'kind' in raw) {
|
|
46
|
-
const parsed =
|
|
43
|
+
const parsed = FlowPredicateSchema.safeParse(raw);
|
|
47
44
|
if (!parsed.success) {
|
|
48
45
|
return { ok: false, detail: describePredicateExpectFailure(parsed.error) };
|
|
49
46
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return { ok: true, value: expect };
|
|
47
|
+
// Kept AS the predicate. There is nothing to convert to any more, so nothing to refuse for
|
|
48
|
+
// being inexpressible — the shape an agent wrote is the shape the file holds.
|
|
49
|
+
return { ok: true, value: parsed.data };
|
|
54
50
|
}
|
|
55
51
|
if (!isRecord(raw))
|
|
56
52
|
return { ok: true, value: raw };
|
|
57
|
-
|
|
53
|
+
const flattened = flattenSignalObject(raw);
|
|
54
|
+
/*
|
|
55
|
+
* A v1 expect is validated HERE so a misspelling is named.
|
|
56
|
+
*
|
|
57
|
+
* The schema accepts both formats as a union, and a union rejection reports every branch's
|
|
58
|
+
* complaint at once — so `{ signal: 'x', signl: 'typo' }` came back as "flow file is malformed"
|
|
59
|
+
* without the word `signl` in it. The file was still refused, which is the half that matters, but
|
|
60
|
+
* the reader was left to find a one-character typo by eye. Checking the v1 shape on its own, on
|
|
61
|
+
* the path that already knows this is a v1 expect, puts the key back in the message.
|
|
62
|
+
*/
|
|
63
|
+
const asV1 = FlowExpectSchema.safeParse(flattened);
|
|
64
|
+
if (!asV1.success) {
|
|
65
|
+
return { ok: false, detail: describePredicateExpectFailure(asV1.error) };
|
|
66
|
+
}
|
|
67
|
+
return { ok: true, value: flattened };
|
|
58
68
|
}
|
|
59
69
|
function describePredicateExpectFailure(error) {
|
|
70
|
+
const refused = error.issues.find((one) => 'custom' === one.code);
|
|
71
|
+
if (refused !== undefined)
|
|
72
|
+
return `${FlowParseNote.UNSUPPORTED_SHAPE}: ${refused.message}`;
|
|
73
|
+
// An unrecognized key FIRST: a strict object reports it on the object's own path with the names
|
|
74
|
+
// in `keys`, so reading `path[0]` misses the one thing worth saying. A misspelling is the failure
|
|
75
|
+
// this whole check exists to catch, and naming it is the difference between a fix and a hunt.
|
|
76
|
+
const unknownKey = error.issues.find((one) => 'unrecognized_keys' === one.code);
|
|
77
|
+
if (unknownKey !== undefined && 'unrecognized_keys' === unknownKey.code) {
|
|
78
|
+
const named = unknownKey.keys.map((k) => `"${k}"`).join(', ');
|
|
79
|
+
return `${FlowParseNote.UNSUPPORTED_SHAPE} key ${named}`;
|
|
80
|
+
}
|
|
60
81
|
const issue = error.issues[0];
|
|
61
82
|
if (undefined === issue)
|
|
62
83
|
return FlowParseNote.UNSUPPORTED_SHAPE;
|
|
@@ -102,8 +123,48 @@ export function coerceFlowFileExpects(raw) {
|
|
|
102
123
|
}
|
|
103
124
|
return { ok: true, value: next };
|
|
104
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* The issues a union rejection buries.
|
|
128
|
+
*
|
|
129
|
+
* `expect` accepts a predicate OR a v1 expect, and zod reports a union failure as one
|
|
130
|
+
* `invalid_union` issue whose branch errors live inside `unionErrors`. The one that matters is the
|
|
131
|
+
* v1 branch's `unrecognized_keys` — the misspelled field. Without this the file was still correctly
|
|
132
|
+
* REFUSED and the message said only "malformed", which sends the author back to hunt a
|
|
133
|
+
* one-character typo we had already identified.
|
|
134
|
+
*/
|
|
135
|
+
function flatten(issues) {
|
|
136
|
+
return issues.flatMap((issue) => 'invalid_union' === issue.code
|
|
137
|
+
? [issue, ...flatten(issue.unionErrors.flatMap((e) => e.issues))]
|
|
138
|
+
: [issue]);
|
|
139
|
+
}
|
|
105
140
|
export function describeFlowZodFailure(error) {
|
|
106
|
-
const
|
|
141
|
+
const all = flatten(error.issues);
|
|
142
|
+
/*
|
|
143
|
+
* The most SPECIFIC complaint wins, wherever in the union it was reported.
|
|
144
|
+
*
|
|
145
|
+
* An unrecognized key names the typo. Failing that, an issue whose path reaches INTO an expect
|
|
146
|
+
* names the field that was the wrong type — `{ signal: { count: 1 } }`, a signal object with no
|
|
147
|
+
* name, is reported on `steps.0.expect.signal` and that path is the whole answer. The union's own
|
|
148
|
+
* top-level `invalid_union` says only "malformed", which is true and useless.
|
|
149
|
+
*/
|
|
150
|
+
const named = (one) => {
|
|
151
|
+
// NOT the discriminator complaint. `invalid_union_discriminator` on `expect.kind` is the
|
|
152
|
+
// PREDICATE branch objecting that a v1 expect has no `kind` — which is true of every v1 expect
|
|
153
|
+
// ever written and tells the author nothing. The useful issue is the one from the branch they
|
|
154
|
+
// were actually writing.
|
|
155
|
+
if ('invalid_union_discriminator' === one.code)
|
|
156
|
+
return false;
|
|
157
|
+
return (('steps' === one.path[0] && 'expect' === one.path[2] && one.path.length > 3) ||
|
|
158
|
+
('success' === one.path[0] && one.path.length > 1));
|
|
159
|
+
};
|
|
160
|
+
// A refinement carries its own reason (a session ref, named by field), and that reason IS the fix.
|
|
161
|
+
const refused = all.find((one) => 'custom' === one.code);
|
|
162
|
+
if (refused !== undefined && 'steps' === refused.path[0] && 'number' === typeof refused.path[1]) {
|
|
163
|
+
return `${FlowParseNote.UNSUPPORTED_SHAPE} at step ${String(refused.path[1])}: ${refused.message}`;
|
|
164
|
+
}
|
|
165
|
+
if (refused !== undefined)
|
|
166
|
+
return `${FlowParseNote.UNSUPPORTED_SHAPE}: ${refused.message}`;
|
|
167
|
+
const issue = all.find((one) => 'unrecognized_keys' === one.code) ?? all.find(named) ?? error.issues[0];
|
|
107
168
|
if (undefined === issue)
|
|
108
169
|
return FlowParseNote.MALFORMED;
|
|
109
170
|
const path = issue.path;
|
|
@@ -139,6 +200,26 @@ export function parseFlowFileText(text) {
|
|
|
139
200
|
catch {
|
|
140
201
|
return { ok: false, code: FlowErrorCode.PARSE_FAILED, detail: FlowParseNote.NOT_JSON };
|
|
141
202
|
}
|
|
203
|
+
/*
|
|
204
|
+
* Version BEFORE schema. When the format does not match, nothing else in the file can be trusted
|
|
205
|
+
* to mean what this reader thinks it means, so the first unrelated field that happens to fail
|
|
206
|
+
* would be noise about the wrong thing -- and `version` is a `z.literal`, so the schema's own
|
|
207
|
+
* complaint about it is indistinguishable from a typo.
|
|
208
|
+
*
|
|
209
|
+
* Only a NUMBER that is not ours counts. A missing or non-numeric `version` is a damaged field
|
|
210
|
+
* rather than a different format, and stays PARSE_FAILED.
|
|
211
|
+
*/
|
|
212
|
+
const declared = isRecord(parsed) ? parsed['version'] : undefined;
|
|
213
|
+
if ('number' === typeof declared && !READABLE_FLOW_VERSIONS.has(declared)) {
|
|
214
|
+
return {
|
|
215
|
+
ok: false,
|
|
216
|
+
code: FlowErrorCode.WRONG_VERSION,
|
|
217
|
+
detail: `this flow file is version ${String(declared)} and this Reticle reads versions ` +
|
|
218
|
+
`${[...READABLE_FLOW_VERSIONS].join(' and ')}. The file is not damaged, the reader is the ` +
|
|
219
|
+
'wrong one. ' +
|
|
220
|
+
'Upgrade or downgrade Reticle rather than editing the flow.',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
142
223
|
const coerced = coerceFlowFileExpects(parsed);
|
|
143
224
|
if (!coerced.ok) {
|
|
144
225
|
return { ok: false, code: FlowErrorCode.PARSE_FAILED, detail: coerced.detail };
|
|
@@ -6,7 +6,7 @@ import { classifyFlowAssertions } from './flow-classify.js';
|
|
|
6
6
|
*
|
|
7
7
|
* A replay used to report only in the language of the DOM: step 3 drifted, this assertion failed.
|
|
8
8
|
* True, actionable, and illegible — the reader still had to reconstruct what stopped working. The
|
|
9
|
-
* ledger in `.reticle/intent
|
|
9
|
+
* ledger in `.reticle/intent/` already holds the other half (prose statements, declared → bound
|
|
10
10
|
* → proved, and the verdict that discharged each one), so a flow does not need its own notion of
|
|
11
11
|
* intent. It needs an id.
|
|
12
12
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FlowReplayResult } from '@reticlehq/core';
|
|
2
2
|
import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
3
|
+
import { withLearnedSources } from './learned-sources.js';
|
|
3
4
|
/**
|
|
4
5
|
* Write back what a replay taught the flow, once the replay has finished writing its own.
|
|
5
6
|
*
|
|
@@ -50,3 +51,5 @@ export declare function replayAndLearn(deps: ToolDeps,
|
|
|
50
51
|
* caught it. `seed` and anything added later travel the same path, so the whole object goes.
|
|
51
52
|
*/
|
|
52
53
|
args: Record<string, unknown>): Promise<FlowReplayResult>;
|
|
54
|
+
/** Write back the files a clean replay's anchors resolved to. Best-effort, as `persistLearning` is. */
|
|
55
|
+
export declare function persistSources(deps: ToolDeps, args: Record<string, unknown>, sources: Parameters<typeof withLearnedSources>[1]): Promise<void>;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { asString } from '@reticlehq/core';
|
|
2
2
|
import { flowsForSession } from './flow-store-for-session.js';
|
|
3
3
|
import { replayNamedFlow } from './flow-replay-run.js';
|
|
4
|
+
import { recordingSources, withLearnedSources } from './learned-sources.js';
|
|
5
|
+
import { ReplayStatus } from '@reticlehq/core';
|
|
6
|
+
function projectOf(deps, args) {
|
|
7
|
+
try {
|
|
8
|
+
return deps.sessions.resolve(asString(args['sessionId'])).projectId;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
4
14
|
/**
|
|
5
15
|
* Write back what a replay taught the flow, once the replay has finished writing its own.
|
|
6
16
|
*
|
|
@@ -26,13 +36,7 @@ export async function persistLearning(deps, args, result) {
|
|
|
26
36
|
if (0 === name.length)
|
|
27
37
|
return result;
|
|
28
38
|
try {
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
projectId = deps.sessions.resolve(asString(args['sessionId'])).projectId;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
projectId = undefined;
|
|
35
|
-
}
|
|
39
|
+
const projectId = projectOf(deps, args);
|
|
36
40
|
await flowsForSession(deps, projectId).flows.recordLearned(name, learned, projectId);
|
|
37
41
|
}
|
|
38
42
|
catch {
|
|
@@ -72,5 +76,32 @@ export async function replayAndLearn(deps,
|
|
|
72
76
|
* caught it. `seed` and anything added later travel the same path, so the whole object goes.
|
|
73
77
|
*/
|
|
74
78
|
args) {
|
|
75
|
-
|
|
79
|
+
let recorded;
|
|
80
|
+
const result = await replayNamedFlow(deps, args, (session) => {
|
|
81
|
+
recorded = recordingSources(session);
|
|
82
|
+
return recorded.session;
|
|
83
|
+
});
|
|
84
|
+
// Only a clean run teaches a file: a drifted step may have resolved to the wrong element.
|
|
85
|
+
if (result.status === ReplayStatus.OK && recorded !== undefined && recorded.sources.size > 0) {
|
|
86
|
+
await persistSources(deps, args, recorded.sources);
|
|
87
|
+
}
|
|
88
|
+
return await persistLearning(deps, args, result);
|
|
89
|
+
}
|
|
90
|
+
/** Write back the files a clean replay's anchors resolved to. Best-effort, as `persistLearning` is. */
|
|
91
|
+
export async function persistSources(deps, args, sources) {
|
|
92
|
+
const name = asString(args['flowName']) ?? '';
|
|
93
|
+
if (0 === name.length)
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
const projectId = projectOf(deps, args);
|
|
97
|
+
const store = flowsForSession(deps, projectId).flows;
|
|
98
|
+
const current = await store.load(name, projectId);
|
|
99
|
+
// Read first so a flow that already names every file is never rewritten.
|
|
100
|
+
if (!current.ok || withLearnedSources(current.value, sources) === undefined)
|
|
101
|
+
return;
|
|
102
|
+
await store.recordSources(name, sources, projectId);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Bookkeeping only.
|
|
106
|
+
}
|
|
76
107
|
}
|
|
@@ -2,6 +2,7 @@ import { FlowErrorCode, type CommandResult, type FlowFile, type FlowReplayResult
|
|
|
2
2
|
import type { ArrivalClock } from '../../surface/tools/act/navigation/navigate-arrival.js';
|
|
3
3
|
import type { SessionManager } from '../../portal/session/session-manager.js';
|
|
4
4
|
import type { Session } from '../../portal/session/session.js';
|
|
5
|
+
import { DocumentLostDuringReplay, type FlowReplaySession } from './flow-replay.js';
|
|
5
6
|
import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
6
7
|
export declare function latestRecordedFlow(events: ReticleEvent[]): {
|
|
7
8
|
name: string;
|
|
@@ -24,21 +25,39 @@ interface StartPathSession {
|
|
|
24
25
|
* navigation was refused or the SDK never reconnected in the window.
|
|
25
26
|
*/
|
|
26
27
|
export declare function startPathMismatchHint(flow: FlowFile, session: StartPathSession): string | undefined;
|
|
28
|
+
/** What replay learned on its way to step 0. Both fields absent = the tab was left exactly as it was. */
|
|
29
|
+
export interface StartPathArrival {
|
|
30
|
+
/** The successor session replay must continue on. Absent when nothing was navigated, or arrival timed out. */
|
|
31
|
+
session?: Session;
|
|
32
|
+
/** Set when the RESET itself is why step 1 cannot start — the reason a step-1 red is not drift. */
|
|
33
|
+
resetCost?: string;
|
|
34
|
+
}
|
|
27
35
|
/**
|
|
28
|
-
* Replay's half of the FlowFile contract:
|
|
36
|
+
* Replay's half of the FlowFile contract: put the tab on the flow's `startPath` before step 1, and
|
|
37
|
+
* RELOAD it if it is already there.
|
|
38
|
+
*
|
|
39
|
+
* The reload is the state contract. Replaying "add a task" twice against a tab nobody reset gives
|
|
40
|
+
* the second run a world the recording never saw, and its assertions then describe the first run
|
|
41
|
+
* rather than the app. Our own `replay-determinism.mjs` has always refreshed before every run — the
|
|
42
|
+
* determinism we measured was never the determinism a user got.
|
|
43
|
+
*
|
|
44
|
+
* `requires` is the declared opt-out, and its first caller. A flow that says it starts from state
|
|
45
|
+
* another flow established is the one flow a page load would destroy, so it is never reset — here
|
|
46
|
+
* or from the wrong page. Silence is not an opt-out: a flow that depends on leftovers without
|
|
47
|
+
* saying so is a flow whose green means nothing on a fresh machine, and CI is a fresh machine.
|
|
29
48
|
*
|
|
30
49
|
* A full-page load tears down the session socket, so the navigation must happen here — before any
|
|
31
50
|
* step runs — and the replay continues on the session the SDK reconnects as (found via the same
|
|
32
51
|
* tombstone rebind that lets `resolve(oldId)` answer after any navigation). Best-effort by design:
|
|
33
|
-
* when the flow carries no startPath, the
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
52
|
+
* when the flow carries no startPath, the current route is unobservable, the navigation is refused,
|
|
53
|
+
* or the SDK never reconnects in the window, replay proceeds on the connected session as before —
|
|
54
|
+
* with startPathMismatchHint turning any resulting drift into an actionable next move rather than a
|
|
55
|
+
* mystifying one.
|
|
37
56
|
*/
|
|
38
57
|
export declare function arriveAtStartPath(sessions: SessionManager, session: StartPathSession & {
|
|
39
58
|
id: string;
|
|
40
59
|
command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
|
|
41
|
-
}, flow: FlowFile, timeoutMs?: number, clock?: ArrivalClock): Promise<
|
|
60
|
+
}, flow: FlowFile, timeoutMs?: number, clock?: ArrivalClock): Promise<StartPathArrival>;
|
|
42
61
|
/**
|
|
43
62
|
* Send a tab somewhere and wait for the session that comes back.
|
|
44
63
|
*
|
|
@@ -56,13 +75,39 @@ export declare function navigateAndAwait(sessions: SessionManager, session: {
|
|
|
56
75
|
/** Optional because a session's URL is, and `carryReticleIdentity` already handles its absence. */
|
|
57
76
|
url?: string;
|
|
58
77
|
command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
|
|
59
|
-
}, destination: string, expectedPath: string, timeoutMs?: number, clock?: ArrivalClock
|
|
60
|
-
|
|
78
|
+
}, destination: string, expectedPath: string, timeoutMs?: number, clock?: ArrivalClock,
|
|
79
|
+
/** True when the destination is the page we are already on — see `arrivedSuccessor`. */
|
|
80
|
+
isReload?: boolean): Promise<Session | undefined>;
|
|
81
|
+
/**
|
|
82
|
+
* The honest answer when the document replay was driving went away mid-run.
|
|
83
|
+
*
|
|
84
|
+
* Not a pass: nothing graded the journey, and the steps in hand are only the ones the departed
|
|
85
|
+
* document answered. Not a failure either — the app was never observed doing anything wrong, and
|
|
86
|
+
* reporting one would be the false red that sends a reader into product code that is fine. So it
|
|
87
|
+
* lands in the bucket this engine already has for "nothing was proved", the same one an unmet
|
|
88
|
+
* precondition uses, with the steps that DID answer attached and no row invented for the one that
|
|
89
|
+
* did not.
|
|
90
|
+
*
|
|
91
|
+
* Why replay reports rather than follows: the successor cannot be identified at the moment the
|
|
92
|
+
* socket closes. `SessionManager.remove` runs from the close handler, writes the tombstone a
|
|
93
|
+
* successor is later matched against, and has nothing to match yet — so the rejection is a plain
|
|
94
|
+
* transport failure by design, and a step in the middle of a journey has no budget to sit and poll
|
|
95
|
+
* with. `navigateAndAwait` CAN wait, because it knows it asked for the navigation, and it does.
|
|
96
|
+
*/
|
|
97
|
+
export declare function lostDocumentResult(name: string, lost: DocumentLostDuringReplay): FlowReplayResult;
|
|
98
|
+
export declare function replayNamedFlow(deps: ToolDeps, args: Record<string, unknown>,
|
|
99
|
+
/** Wraps the session the steps run on, so a caller can watch what they resolved. */
|
|
100
|
+
observe?: (session: FlowReplaySession) => FlowReplaySession): Promise<FlowReplayResult>;
|
|
61
101
|
export declare function crossStepOnly(whole: readonly Contradiction[], steps: readonly FlowStepResult[]): Contradiction[];
|
|
62
102
|
/**
|
|
63
103
|
* The connecting session's project, or undefined when no browser is attached. Flow tools use it to
|
|
64
104
|
* scope storage to the current app on a shared daemon; resolving must NOT throw here (list/load are
|
|
65
105
|
* documented to work headless), so a missing/unknown session degrades to the global/legacy store.
|
|
106
|
+
*
|
|
107
|
+
* Re-exported, not re-implemented. This file carried a BYTE-IDENTICAL second copy, and the two
|
|
108
|
+
* drifted the moment `ProjectId` was threaded: `session-root`'s copy returned the brand while this
|
|
109
|
+
* one returned `string`, so every flow tool importing from here was handed a widened value and lost
|
|
110
|
+
* the brand one line after it was minted. Exactly the weak-annotation failure the brand's own doc
|
|
111
|
+
* records against `RunStore.list()`.
|
|
66
112
|
*/
|
|
67
|
-
export
|
|
68
|
-
export {};
|
|
113
|
+
export { sessionProjectId } from '../../memory/project/session-root.js';
|