@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
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* point: the action window must open and close identically no matter which anchor found the element,
|
|
7
7
|
* or a step's events land in the wrong window depending on how it was addressed.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { expectedElementTestid } from '@reticlehq/core';
|
|
10
|
+
import { AnchorKind, DEGRADED_ANCHOR_ROLE, DriftReason, PredicateKind, QueryBy, ReticleCommand, } from '@reticlehq/core';
|
|
10
11
|
import { ReticleTool } from '@reticlehq/core';
|
|
11
12
|
import { replayActionArgs } from './replay.js';
|
|
12
13
|
import { isStaleRefError } from '../../surface/tools/act/act-sequence-retry.js';
|
|
@@ -102,7 +103,7 @@ export async function runRoleStep(session, step, index, anchor, confirmDangerous
|
|
|
102
103
|
* to recognise the raw refusal still finds it, plus the fix for this specific caller.
|
|
103
104
|
*/
|
|
104
105
|
const DESTRUCTIVE_ACTION_PATTERN = /potentially destructive (?:\w+ )*(?:action|tool) blocked/i;
|
|
105
|
-
|
|
106
|
+
function replayDestructiveActionHint(rawError) {
|
|
106
107
|
if (!DESTRUCTIVE_ACTION_PATTERN.test(rawError))
|
|
107
108
|
return rawError;
|
|
108
109
|
return (`${rawError} — this is a flow REPLAY: a step's own args.confirmDangerous is stripped before ` +
|
|
@@ -118,7 +119,7 @@ export function replayDestructiveActionHint(rawError) {
|
|
|
118
119
|
* Dispatch one already-resolved step. Shared so every anchor kind runs the action the same way —
|
|
119
120
|
* including the action window, whose open/close must not depend on which anchor found the element.
|
|
120
121
|
*/
|
|
121
|
-
async function actOnResolvedRef(session, step, index, label, ref, confirmDangerous,
|
|
122
|
+
export async function actOnResolvedRef(session, step, index, label, ref, confirmDangerous,
|
|
122
123
|
/**
|
|
123
124
|
* The field name a redacted fill is supplied under, from `anchorFieldName` — the SAME function
|
|
124
125
|
* redaction uses to decide what to hide. Without it a role-anchored secret is redacted at save
|
|
@@ -252,6 +253,21 @@ export function anchorQueryArgs(anchor) {
|
|
|
252
253
|
return roleQueryArgs(anchor);
|
|
253
254
|
return null;
|
|
254
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* The anchor as a PRECONDITION a flow file can store: "this element is on the page".
|
|
258
|
+
*
|
|
259
|
+
* What `requires` means for a flow that starts from state a page load discards, in the shape the
|
|
260
|
+
* flow's own `requires` holds. No tool writes `requires`, so the reset's hint hands over this line.
|
|
261
|
+
*/
|
|
262
|
+
export function anchorPrecondition(anchor) {
|
|
263
|
+
if (anchor.kind === AnchorKind.TESTID) {
|
|
264
|
+
return { kind: PredicateKind.ELEMENT, query: { testid: anchor.value } };
|
|
265
|
+
}
|
|
266
|
+
if (anchor.kind === AnchorKind.ROLE && !isDegradedAnchor(anchor)) {
|
|
267
|
+
return { kind: PredicateKind.ELEMENT, query: { role: anchor.role, name: anchor.name } };
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
255
271
|
/**
|
|
256
272
|
* Run an act_sequence step: resolve every sub-step's OWN anchor, then dispatch the whole thing as one
|
|
257
273
|
* ACT_SEQUENCE — the same shape `replayProgram` already uses, so a recorded sequence and a saved one
|
|
@@ -321,7 +337,7 @@ dynamic = new Set()) {
|
|
|
321
337
|
};
|
|
322
338
|
/** The sub-step's own expectation, unless its testid is deliberately unasserted. */
|
|
323
339
|
const declaredBy = (sub) => {
|
|
324
|
-
const testid = sub.expect
|
|
340
|
+
const testid = expectedElementTestid(sub.expect);
|
|
325
341
|
return testid === undefined || dynamic.has(testid) ? undefined : testid;
|
|
326
342
|
};
|
|
327
343
|
/**
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
* returned unchanged.
|
|
25
25
|
*/
|
|
26
26
|
import { FlowStore } from './flows.js';
|
|
27
|
+
import type { ProjectId } from '@reticlehq/core';
|
|
27
28
|
import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
28
|
-
export declare function flowsForSession(deps: ToolDeps, projectId:
|
|
29
|
+
export declare function flowsForSession(deps: ToolDeps, projectId: ProjectId | undefined): {
|
|
29
30
|
flows: FlowStore;
|
|
30
31
|
root: string;
|
|
31
32
|
};
|
|
@@ -12,21 +12,36 @@
|
|
|
12
12
|
* tool and the `@reticlehq/test` spec runner share one implementation — no divergent oracle.
|
|
13
13
|
* Pure: no IO, no clock.
|
|
14
14
|
*/
|
|
15
|
-
import { type FlowExpect, type FlowFile } from '@reticlehq/core';
|
|
16
|
-
import type { EvalResult
|
|
15
|
+
import { type FlowExpect, type FlowFile, type Predicate } from '@reticlehq/core';
|
|
16
|
+
import type { EvalResult } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
17
17
|
import type { FlowReplaySession, WaitForSignal } from './flow-replay-types.js';
|
|
18
18
|
/** The dynamic (LLM-output) testids whose presence is never asserted — same rule replay uses. */
|
|
19
19
|
export declare function dynamicTestids(flow: FlowFile): Set<string>;
|
|
20
20
|
/** The synthetic step.tool a replay appends when it asserts the flow's success oracle. */
|
|
21
21
|
export declare const SUCCESS_STEP_TOOL = "success";
|
|
22
22
|
/** A short human label for the success end-condition, for result rows. */
|
|
23
|
-
export declare function successLabel(success:
|
|
24
|
-
/**
|
|
25
|
-
|
|
23
|
+
export declare function successLabel(success: Predicate | undefined): string;
|
|
24
|
+
/**
|
|
25
|
+
* What a flow's success oracle actually asserts, once the file has been read and the dynamic
|
|
26
|
+
* (LLM-output) testids have been taken out of it.
|
|
27
|
+
*
|
|
28
|
+
* Most of what this function used to do is gone, and that is the migration working as intended: it
|
|
29
|
+
* compiled a flat `FlowExpect` into a `Predicate`, and a step's `expect` IS a predicate now, lifted
|
|
30
|
+
* by the schema when the file is read. It keeps the name and the signature because it is on
|
|
31
|
+
* `@reticlehq/test`'s published surface, and it still accepts the flat shape for the same reason a
|
|
32
|
+
* v1 file still loads — a spec written against the old spelling is not wrong, it is old.
|
|
33
|
+
*
|
|
34
|
+
* What remains is the DYNAMIC SKIP, and it is not cosmetic. A testid whose content a model writes is
|
|
35
|
+
* asserted for PRESENCE by the step layer and never for content; a success oracle bound to one would
|
|
36
|
+
* assert exactly what the flow has already declared unassertable. Dropping every such clause can
|
|
37
|
+
* leave nothing at all, and `undefined` is the honest answer — vacuously met, the same as declaring
|
|
38
|
+
* no success, because nothing here was checkable.
|
|
39
|
+
*/
|
|
40
|
+
export declare function successToPredicate(success: Predicate | FlowExpect | undefined, dynamic: ReadonlySet<string>): Predicate | undefined;
|
|
26
41
|
/**
|
|
27
42
|
* Assert a flow's success end-condition after replay. Delegates to the injected waitForSignal (the
|
|
28
43
|
* real waitForPredicate in production, a fake in unit tests). Passes when: no success was declared,
|
|
29
44
|
* OR every success field was dynamic-skipped (vacuously met), OR the compiled predicate held within
|
|
30
45
|
* the injected timeout. Never reads the wall clock.
|
|
31
46
|
*/
|
|
32
|
-
export declare function assertSuccess(session: FlowReplaySession, success:
|
|
47
|
+
export declare function assertSuccess(session: FlowReplaySession, success: Predicate | undefined, dynamic: ReadonlySet<string>, waitForSignal: WaitForSignal, timeoutMs: number, since?: number): Promise<EvalResult>;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* tool and the `@reticlehq/test` spec runner share one implementation — no divergent oracle.
|
|
13
13
|
* Pure: no IO, no clock.
|
|
14
14
|
*/
|
|
15
|
-
import { AnchorKind, PredicateKind } from '@reticlehq/core';
|
|
15
|
+
import { AnchorKind, PredicateKind, clauseOfKind, flowExpectToPredicate, withoutClauses, } from '@reticlehq/core';
|
|
16
16
|
/** The dynamic (LLM-output) testids whose presence is never asserted — same rule replay uses. */
|
|
17
17
|
export function dynamicTestids(flow) {
|
|
18
18
|
return new Set((flow.dynamic ?? [])
|
|
@@ -23,145 +23,59 @@ export function dynamicTestids(flow) {
|
|
|
23
23
|
export const SUCCESS_STEP_TOOL = 'success';
|
|
24
24
|
/** A short human label for the success end-condition, for result rows. */
|
|
25
25
|
export function successLabel(success) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
/*
|
|
27
|
+
* NOT `expectLabel`, and the difference is the reader.
|
|
28
|
+
*
|
|
29
|
+
* That one names a clause for a drift row, where `signal:` tells you which channel failed. This
|
|
30
|
+
* one is `mustHold` in the domain model an agent reads BEFORE testing — a list of what each flow
|
|
31
|
+
* guarantees — and there the prefix is noise on the commonest case. A signal's own name is the
|
|
32
|
+
* sentence: "order:placed" is what must hold.
|
|
33
|
+
*/
|
|
34
|
+
const signal = clauseOfKind(success, PredicateKind.SIGNAL);
|
|
35
|
+
if (signal?.name !== undefined)
|
|
36
|
+
return signal.name;
|
|
37
|
+
const net = clauseOfKind(success, PredicateKind.NET);
|
|
38
|
+
if (net !== undefined)
|
|
39
|
+
return net.urlContains ?? net.method ?? 'net';
|
|
40
|
+
const console_ = clauseOfKind(success, PredicateKind.CONSOLE);
|
|
41
|
+
if (console_ !== undefined) {
|
|
42
|
+
return `console:${true === console_.absent ? 'clean' : (console_.level ?? 'error')}`;
|
|
32
43
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
const state = clauseOfKind(success, PredicateKind.STATE);
|
|
45
|
+
if (state !== undefined)
|
|
46
|
+
return `state:${state.path}`;
|
|
47
|
+
const text = clauseOfKind(success, PredicateKind.TEXT);
|
|
48
|
+
if (text?.contains !== undefined)
|
|
49
|
+
return `text:${text.contains}`;
|
|
50
|
+
const element = clauseOfKind(success, PredicateKind.ELEMENT);
|
|
51
|
+
const q = element?.query;
|
|
52
|
+
return q?.testid ?? q?.name ?? q?.role ?? 'success';
|
|
38
53
|
}
|
|
39
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* What a flow's success oracle actually asserts, once the file has been read and the dynamic
|
|
56
|
+
* (LLM-output) testids have been taken out of it.
|
|
57
|
+
*
|
|
58
|
+
* Most of what this function used to do is gone, and that is the migration working as intended: it
|
|
59
|
+
* compiled a flat `FlowExpect` into a `Predicate`, and a step's `expect` IS a predicate now, lifted
|
|
60
|
+
* by the schema when the file is read. It keeps the name and the signature because it is on
|
|
61
|
+
* `@reticlehq/test`'s published surface, and it still accepts the flat shape for the same reason a
|
|
62
|
+
* v1 file still loads — a spec written against the old spelling is not wrong, it is old.
|
|
63
|
+
*
|
|
64
|
+
* What remains is the DYNAMIC SKIP, and it is not cosmetic. A testid whose content a model writes is
|
|
65
|
+
* asserted for PRESENCE by the step layer and never for content; a success oracle bound to one would
|
|
66
|
+
* assert exactly what the flow has already declared unassertable. Dropping every such clause can
|
|
67
|
+
* leave nothing at all, and `undefined` is the honest answer — vacuously met, the same as declaring
|
|
68
|
+
* no success, because nothing here was checkable.
|
|
69
|
+
*/
|
|
40
70
|
export function successToPredicate(success, dynamic) {
|
|
41
|
-
|
|
42
|
-
if (success.signal !== undefined) {
|
|
43
|
-
const signal = {
|
|
44
|
-
kind: PredicateKind.SIGNAL,
|
|
45
|
-
name: success.signal,
|
|
46
|
-
};
|
|
47
|
-
if (success.signalData !== undefined)
|
|
48
|
-
signal.dataMatches = success.signalData;
|
|
49
|
-
if (success.signalCount !== undefined) {
|
|
50
|
-
signal.count = success.signalCount;
|
|
51
|
-
// Same post-settle reasoning as net.count: the success waiter is wait-until-true, so an exact
|
|
52
|
-
// count is transiently satisfied the instant the FIRST matching signal fires — before a
|
|
53
|
-
// double-fire's duplicate arrives. Gating on `settled` forces the count to be read only after
|
|
54
|
-
// the app goes quiet, by which point the duplicate IS counted and the over-count fails.
|
|
55
|
-
parts.push({ kind: PredicateKind.SETTLED });
|
|
56
|
-
}
|
|
57
|
-
parts.push(signal);
|
|
58
|
-
}
|
|
59
|
-
if (success.route !== undefined) {
|
|
60
|
-
// The mirror of `predicateToExpect`'s ROUTE case. Replay evaluates predicates, so carrying the
|
|
61
|
-
// route back here is the whole of what makes a recorded navigation able to fail.
|
|
62
|
-
const route = {
|
|
63
|
-
kind: PredicateKind.ROUTE,
|
|
64
|
-
};
|
|
65
|
-
if (success.route.pathname !== undefined)
|
|
66
|
-
route.pathname = success.route.pathname;
|
|
67
|
-
if (success.route.contains !== undefined)
|
|
68
|
-
route.contains = success.route.contains;
|
|
69
|
-
return route;
|
|
70
|
-
}
|
|
71
|
-
if (success.net !== undefined) {
|
|
72
|
-
const net = { kind: PredicateKind.NET };
|
|
73
|
-
if (success.net.method !== undefined)
|
|
74
|
-
net.method = success.net.method;
|
|
75
|
-
if (success.net.urlContains !== undefined)
|
|
76
|
-
net.urlContains = success.net.urlContains;
|
|
77
|
-
if (success.net.status !== undefined)
|
|
78
|
-
net.status = success.net.status;
|
|
79
|
-
if (success.net.count !== undefined) {
|
|
80
|
-
net.count = success.net.count;
|
|
81
|
-
// A cardinality assertion is inherently POST-SETTLE. The success waiter is wait-until-true, so an
|
|
82
|
-
// exact count (e.g. 1) is transiently satisfied the instant the FIRST matching request lands —
|
|
83
|
-
// before a double-submit's duplicate arrives. Gating on `settled` forces the count to be read
|
|
84
|
-
// only after the network has gone quiet, so the duplicate IS counted and the over-count fails.
|
|
85
|
-
parts.push({ kind: PredicateKind.SETTLED });
|
|
86
|
-
}
|
|
87
|
-
parts.push(net);
|
|
88
|
-
}
|
|
89
|
-
if (success.console !== undefined) {
|
|
90
|
-
const con = {
|
|
91
|
-
kind: PredicateKind.CONSOLE,
|
|
92
|
-
};
|
|
93
|
-
if (success.console.level !== undefined)
|
|
94
|
-
con.level = success.console.level;
|
|
95
|
-
if (success.console.absent !== undefined) {
|
|
96
|
-
con.absent = success.console.absent;
|
|
97
|
-
// An `absent` assertion is post-settle, same as net.count: a wait-until-true waiter sees "no
|
|
98
|
-
// error yet" at the first poll and passes BEFORE the action's error fires. Gate on `settled` so
|
|
99
|
-
// the console is read only after the page quiets, by which point any error is in the buffer.
|
|
100
|
-
if (success.console.absent)
|
|
101
|
-
parts.push({ kind: PredicateKind.SETTLED });
|
|
102
|
-
}
|
|
103
|
-
parts.push(con);
|
|
104
|
-
}
|
|
105
|
-
const state = success.state;
|
|
106
|
-
if (state !== undefined) {
|
|
107
|
-
const part = {
|
|
108
|
-
kind: PredicateKind.STATE,
|
|
109
|
-
path: state.path,
|
|
110
|
-
};
|
|
111
|
-
if (state.store !== undefined)
|
|
112
|
-
part.store = state.store;
|
|
113
|
-
if (state.equals !== undefined)
|
|
114
|
-
part.equals = state.equals;
|
|
115
|
-
// `hold` = an INVARIANT ("this state must still hold after the action settles"), vs the default
|
|
116
|
-
// wait-for-change. A wait-until-true waiter would pass the instant the path already equals the
|
|
117
|
-
// value — which, for "an unrelated path stayed put", is true BEFORE a side-effect leak fires. Gate
|
|
118
|
-
// on `settled` so the read happens after the page quiets, by which point the leak has landed.
|
|
119
|
-
if (true === state.hold)
|
|
120
|
-
parts.push({ kind: PredicateKind.SETTLED });
|
|
121
|
-
parts.push(part);
|
|
122
|
-
}
|
|
123
|
-
const element = success.element;
|
|
124
|
-
if (element !== undefined) {
|
|
125
|
-
const testid = element.testid;
|
|
126
|
-
// A dynamic-marked testid is NOT asserted as a success condition (presence-only).
|
|
127
|
-
if (testid === undefined || !dynamic.has(testid)) {
|
|
128
|
-
const query = {};
|
|
129
|
-
if (testid !== undefined)
|
|
130
|
-
query['testid'] = testid;
|
|
131
|
-
if (element.role !== undefined)
|
|
132
|
-
query['role'] = element.role;
|
|
133
|
-
if (element.name !== undefined)
|
|
134
|
-
query['name'] = element.name;
|
|
135
|
-
if (Object.keys(query).length > 0)
|
|
136
|
-
parts.push({ kind: PredicateKind.ELEMENT, query });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const text = success.text;
|
|
140
|
-
if (text !== undefined) {
|
|
141
|
-
const part = {
|
|
142
|
-
kind: PredicateKind.TEXT,
|
|
143
|
-
contains: text.contains,
|
|
144
|
-
};
|
|
145
|
-
if (text.scope !== undefined)
|
|
146
|
-
part.scope = text.scope;
|
|
147
|
-
if (text.visible !== undefined)
|
|
148
|
-
part.visible = text.visible;
|
|
149
|
-
if (true === text.absent) {
|
|
150
|
-
part.absent = true;
|
|
151
|
-
// Same post-settle reasoning as `console.absent` and `state.hold`: a wait-until-true waiter
|
|
152
|
-
// reads "not there yet" on the first poll and passes BEFORE the text it is meant to see
|
|
153
|
-
// disappear has even been rendered. Gate on `settled` so the read happens after the page
|
|
154
|
-
// quiets, by which point a text that was going to appear has.
|
|
155
|
-
parts.push({ kind: PredicateKind.SETTLED });
|
|
156
|
-
}
|
|
157
|
-
parts.push(part);
|
|
158
|
-
}
|
|
159
|
-
const [first] = parts;
|
|
160
|
-
if (0 === parts.length)
|
|
71
|
+
if (success === undefined)
|
|
161
72
|
return undefined;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
73
|
+
const predicate = 'kind' in success ? success : flowExpectToPredicate(success);
|
|
74
|
+
if (predicate === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
return withoutClauses(predicate, (clause) => PredicateKind.ELEMENT === clause.kind &&
|
|
77
|
+
clause.query.testid !== undefined &&
|
|
78
|
+
dynamic.has(clause.query.testid));
|
|
165
79
|
}
|
|
166
80
|
/**
|
|
167
81
|
* Assert a flow's success end-condition after replay. Delegates to the injected waitForSignal (the
|
|
@@ -170,8 +84,6 @@ export function successToPredicate(success, dynamic) {
|
|
|
170
84
|
* the injected timeout. Never reads the wall clock.
|
|
171
85
|
*/
|
|
172
86
|
export async function assertSuccess(session, success, dynamic, waitForSignal, timeoutMs, since = 0) {
|
|
173
|
-
if (success === undefined)
|
|
174
|
-
return { pass: true };
|
|
175
87
|
const predicate = successToPredicate(success, dynamic);
|
|
176
88
|
if (predicate === undefined)
|
|
177
89
|
return { pass: true };
|
|
@@ -20,6 +20,7 @@ import { recordingBacktrackWarning } from './recording/recording-backtrack.js';
|
|
|
20
20
|
import { isValidFlowName, flowPath } from '../../memory/project/dir/reticle-dir.js';
|
|
21
21
|
import {} from './flows.js';
|
|
22
22
|
import { flowsForSession } from './flow-store-for-session.js';
|
|
23
|
+
import { rootForProjectId } from '../../memory/project/session-root.js';
|
|
23
24
|
import { resolveSuiteSelection } from './suite-selection.js';
|
|
24
25
|
import { replayNamedFlow, flowErrorMessage, latestRecordedFlow, sessionProjectId, } from './flow-replay-run.js';
|
|
25
26
|
import { persistAndSyncVerificationRun, } from '../../judgement/runs/verification-sync.js';
|
|
@@ -34,7 +35,10 @@ import { replayAndLearn } from './flow-learning.js';
|
|
|
34
35
|
*/
|
|
35
36
|
async function syncSavedFlowToCloud(deps, flow, projectId) {
|
|
36
37
|
// Per-project cloud: sync a saved flow only when cloud is attached AND flow sync is enabled.
|
|
37
|
-
|
|
38
|
+
// The link file of the project this flow belongs to — the flow itself was already saved through
|
|
39
|
+
// `flowsForSession`, and reading the credential from a different tree is how one project's work
|
|
40
|
+
// reaches another project's dashboard.
|
|
41
|
+
const cloud = await resolveProjectCloud(deps.fs, rootForProjectId(deps, projectId), homedir(), process.env);
|
|
38
42
|
if (null === cloud.config || !cloud.policy.flows)
|
|
39
43
|
return; // not attached / flows disabled → local only
|
|
40
44
|
const result = await syncFlowToCloud(flow, cloud.config, projectId, cloudFetch);
|
|
@@ -573,7 +577,7 @@ export const FLOW_TOOLS = [
|
|
|
573
577
|
...(selected.unsatisfied === undefined ? {} : { unsatisfied: selected.unsatisfied }),
|
|
574
578
|
};
|
|
575
579
|
// verify:server — hand the whole suite to the hosted runner; it records the verification itself.
|
|
576
|
-
const cloud = await resolveProjectCloud(deps.fs, deps
|
|
580
|
+
const cloud = await resolveProjectCloud(deps.fs, rootForProjectId(deps, projectId), homedir(), process.env);
|
|
577
581
|
const server = await runServerVerify(deps, cloud, sessionId, requested);
|
|
578
582
|
if (server !== null)
|
|
579
583
|
return server;
|
|
@@ -637,7 +641,7 @@ export const FLOW_TOOLS = [
|
|
|
637
641
|
: leaseFailureReplay(requested[i] ?? '', o.error),
|
|
638
642
|
durationMs: o.ok && o.value !== undefined ? o.value.durationMs : 0,
|
|
639
643
|
}));
|
|
640
|
-
const flaky = await recordSuiteFlakes(deps.fs, deps
|
|
644
|
+
const flaky = await recordSuiteFlakes(deps.fs, rootForProjectId(deps, projectId), parallelRuns);
|
|
641
645
|
await persistAndSyncVerificationRun(deps, timed, projectId);
|
|
642
646
|
const verdict = buildSuiteVerdict(parallelRuns, selected.knownRoutes, SuiteIsolation.PER_FLOW_LEASE);
|
|
643
647
|
return {
|
|
@@ -673,7 +677,7 @@ export const FLOW_TOOLS = [
|
|
|
673
677
|
// `reticle flow` on the command line, so an AGENT running this tool a hundred times learned
|
|
674
678
|
// nothing about which flows are intermittent. Same product, same ledger, two surfaces, and the
|
|
675
679
|
// one an agent uses was the blind half.
|
|
676
|
-
const flaky = await recordSuiteFlakes(deps.fs, deps
|
|
680
|
+
const flaky = await recordSuiteFlakes(deps.fs, rootForProjectId(deps, projectId), runs);
|
|
677
681
|
// Emit the consolidated run artifact (Runs tab) + best-effort cloud push. Never blocks the verdict.
|
|
678
682
|
await persistAndSyncVerificationRun(deps, timed, projectId);
|
|
679
683
|
const verdict = buildSuiteVerdict(runs, selected.knownRoutes, SuiteIsolation.SHARED_SESSION);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type Predicate, type ProjectId } from '@reticlehq/core';
|
|
1
2
|
export { REDACTED_FILL } from './fields/flow-secret-field.js';
|
|
2
3
|
import { type FlowResult } from './flow-result.js';
|
|
3
4
|
export type { FlowResult } from './flow-result.js';
|
|
4
|
-
import type { FlowAnchor,
|
|
5
|
+
import type { FlowAnchor, FlowFile, FlowStep, HealChange, InstrumentationGap } from '@reticlehq/core';
|
|
5
6
|
import type { CompiledProgram, RecordedStep } from './recording/tape/recordings.js';
|
|
6
7
|
import type { FileSystemPort } from '../../memory/project/fs/fs-port.js';
|
|
7
8
|
import type { Clock } from '../../machine/clock.js';
|
|
@@ -48,9 +49,9 @@ interface SaveSummary {
|
|
|
48
49
|
* the same bytes as before.
|
|
49
50
|
*/
|
|
50
51
|
export interface FlowAnnotations {
|
|
51
|
-
stepExpect: Map<number,
|
|
52
|
+
stepExpect: Map<number, Predicate>;
|
|
52
53
|
dynamic: string[];
|
|
53
|
-
success?:
|
|
54
|
+
success?: Predicate;
|
|
54
55
|
/** The flow's declared business goal (intent annotation). */
|
|
55
56
|
intent?: string;
|
|
56
57
|
}
|
|
@@ -63,15 +64,19 @@ export declare class FlowStore {
|
|
|
63
64
|
* Optionally fold structured annotations (per-step expect, dynamic[], success) onto
|
|
64
65
|
* the flow before writing. Omitting `annotations` reproduces the same bytes.
|
|
65
66
|
*/
|
|
66
|
-
save(program: CompiledProgram, annotations?: FlowAnnotations, projectId?:
|
|
67
|
+
save(program: CompiledProgram, annotations?: FlowAnnotations, projectId?: ProjectId): Promise<FlowResult<SaveSummary>>;
|
|
67
68
|
/**
|
|
68
69
|
* Persist an already-anchored FlowFile captured in-page (no recompilation). The
|
|
69
70
|
* browser resolved every semantic anchor at capture time; here we only validate the name +
|
|
70
71
|
* re-run FlowFileSchema before writing. save is left untouched.
|
|
71
72
|
*/
|
|
72
|
-
saveFlow(flow: FlowFile, projectId?:
|
|
73
|
+
saveFlow(flow: FlowFile, projectId?: ProjectId): Promise<FlowResult<SaveSummary>>;
|
|
73
74
|
/** Write back only what a replay LEARNED. See `#changeInPlace` for why it is not `saveFlow`. */
|
|
74
|
-
recordLearned(name: string, learned: NonNullable<FlowFile['learned']>, projectId?:
|
|
75
|
+
recordLearned(name: string, learned: NonNullable<FlowFile['learned']>, projectId?: ProjectId): Promise<FlowResult<{
|
|
76
|
+
name: string;
|
|
77
|
+
}>>;
|
|
78
|
+
/** Give sourceless steps the files a clean replay resolved them to, merged onto the file as it is now. */
|
|
79
|
+
recordSources(name: string, sources: ReadonlyMap<string, NonNullable<FlowStep['source']>>, projectId?: ProjectId): Promise<FlowResult<{
|
|
75
80
|
name: string;
|
|
76
81
|
}>>;
|
|
77
82
|
/**
|
|
@@ -83,7 +88,7 @@ export declare class FlowStore {
|
|
|
83
88
|
* it with proposals that already cleared HEAL_CONFIDENCE_MIN. A change whose `from` no longer
|
|
84
89
|
* matches the step's anchor is skipped — idempotent and defensive, never throwing.
|
|
85
90
|
*/
|
|
86
|
-
heal(name: string, changes: HealChange[], projectId?:
|
|
91
|
+
heal(name: string, changes: HealChange[], projectId?: ProjectId): Promise<FlowResult<{
|
|
87
92
|
name: string;
|
|
88
93
|
changed: HealChange[];
|
|
89
94
|
}>>;
|
|
@@ -97,17 +102,17 @@ export declare class FlowStore {
|
|
|
97
102
|
* the caller as a reportable error, never be silently filtered away. Callers that build a path from a
|
|
98
103
|
* name must validate first; `flowPath` takes a branded FlowName precisely so the compiler insists.
|
|
99
104
|
*/
|
|
100
|
-
list(projectId?:
|
|
105
|
+
list(projectId?: ProjectId): Promise<string[]>;
|
|
101
106
|
/**
|
|
102
107
|
* Read + zod-validate a flow by name. With a `projectId`, prefers the per-project copy and falls
|
|
103
108
|
* back to a legacy flat (untagged) flow of the same name — so pre-existing flows keep loading.
|
|
104
109
|
*/
|
|
105
|
-
load(name: string, projectId?:
|
|
110
|
+
load(name: string, projectId?: ProjectId): Promise<FlowResult<FlowFile>>;
|
|
106
111
|
/**
|
|
107
112
|
* Delete a flow's file so a renamed/obsolete flow stops lingering in the replay list. Resolves the
|
|
108
113
|
* same path `load` would (per-project copy, else legacy flat, else a subdir scan for the no-pid
|
|
109
114
|
* caller), then removes it. NOT_FOUND when nothing resolves — deleting an absent flow is an error,
|
|
110
115
|
* not a silent no-op, so a typo doesn't read as success.
|
|
111
116
|
*/
|
|
112
|
-
remove(name: string, projectId?:
|
|
117
|
+
remove(name: string, projectId?: ProjectId): Promise<FlowResult<void>>;
|
|
113
118
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { asFlowName } from '@reticlehq/core';
|
|
1
|
+
import { asFlowName, asProjectId, } from '@reticlehq/core';
|
|
2
2
|
import { REDACTED_FILL } from './fields/flow-secret-field.js';
|
|
3
3
|
export { REDACTED_FILL } from './fields/flow-secret-field.js';
|
|
4
4
|
import { safeProjectId } from './flow-result.js';
|
|
@@ -7,6 +7,7 @@ import { AnchorKind, DEGRADED_ANCHOR_ROLE, FLOW_FILE_VERSION, FlowErrorCode, Flo
|
|
|
7
7
|
import { ReticleTool } from '@reticlehq/core';
|
|
8
8
|
import { asRecord, asString } from '@reticlehq/core';
|
|
9
9
|
import { applyHealChanges } from './heal.js';
|
|
10
|
+
import { withLearnedSources } from './learned-sources.js';
|
|
10
11
|
import { flowIntentGap, linkFlowIntent } from './flow-intent.js';
|
|
11
12
|
import { IntentStore } from '../../memory/intent/intent-store.js';
|
|
12
13
|
import { flowDir, flowParentDir, flowPath, reticleDirPaths, isValidFlowName, } from '../../memory/project/dir/reticle-dir.js';
|
|
@@ -351,6 +352,13 @@ export class FlowStore {
|
|
|
351
352
|
value: { name },
|
|
352
353
|
}));
|
|
353
354
|
}
|
|
355
|
+
/** Give sourceless steps the files a clean replay resolved them to, merged onto the file as it is now. */
|
|
356
|
+
async recordSources(name, sources, projectId) {
|
|
357
|
+
return await this.#changeInPlace(name, projectId, (flow) => ({
|
|
358
|
+
next: withLearnedSources(flow, sources) ?? flow,
|
|
359
|
+
value: { name },
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
354
362
|
/**
|
|
355
363
|
* Apply confident testid rebinds to an on-disk flow (the `reticle_flow_heal` apply path),
|
|
356
364
|
* rewriting ONLY the named steps' anchors and preserving every other field. Loading, the name
|
|
@@ -362,7 +370,8 @@ export class FlowStore {
|
|
|
362
370
|
*/
|
|
363
371
|
async heal(name, changes, projectId) {
|
|
364
372
|
return await this.#changeInPlace(name, projectId, (flow) => {
|
|
365
|
-
|
|
373
|
+
// The store's injected clock, so a written flow records when the rebind actually happened.
|
|
374
|
+
const { flow: next, applied } = applyHealChanges(flow, changes, () => this.#clock.now());
|
|
366
375
|
return { next, value: { name, changed: applied } };
|
|
367
376
|
});
|
|
368
377
|
}
|
|
@@ -408,8 +417,11 @@ export class FlowStore {
|
|
|
408
417
|
catch {
|
|
409
418
|
return legacy.sort();
|
|
410
419
|
}
|
|
420
|
+
// Every subdirectory of `.reticle/flows/` is a project's, by construction: this class is the
|
|
421
|
+
// only thing that creates one, and it creates it from a projectId that already passed
|
|
422
|
+
// `safeProjectId`. Reading the name back is the third provenance boundary, not a cast.
|
|
411
423
|
const subdirs = entries.filter((e) => !e.endsWith(FLOW_SUFFIX));
|
|
412
|
-
const nested = (await Promise.all(subdirs.map((d) => this.#namesIn(flowDir(this.#root, d))))).flat();
|
|
424
|
+
const nested = (await Promise.all(subdirs.map((d) => this.#namesIn(flowDir(this.#root, asProjectId(d)))))).flat();
|
|
413
425
|
return [...new Set([...legacy, ...nested])].sort();
|
|
414
426
|
}
|
|
415
427
|
/** The path a flow actually lives at: nested (per-project) if present, else legacy flat, else null. */
|
|
@@ -441,8 +453,9 @@ export class FlowStore {
|
|
|
441
453
|
catch {
|
|
442
454
|
return null;
|
|
443
455
|
}
|
|
456
|
+
// Same provenance as `#namesIn`'s scan above: a subdir of `.reticle/flows/` is a projectId.
|
|
444
457
|
for (const dir of entries.filter((e) => !e.endsWith(FLOW_SUFFIX))) {
|
|
445
|
-
const nested = flowPath(this.#root, name, dir);
|
|
458
|
+
const nested = flowPath(this.#root, name, asProjectId(dir));
|
|
446
459
|
if (await this.#fs.exists(nested))
|
|
447
460
|
return nested;
|
|
448
461
|
}
|
|
@@ -25,7 +25,15 @@ export declare function confidenceFor(from: string, to: string): number;
|
|
|
25
25
|
* skipped (idempotent / defensive), never throwing. Shared by the on-disk writer (FlowStore.heal)
|
|
26
26
|
* and the in-memory pre-write verification in reticle_flow_heal, so both rewrite identically.
|
|
27
27
|
*/
|
|
28
|
-
export declare function applyHealChanges(flow: FlowFile, changes: HealChange[]
|
|
28
|
+
export declare function applyHealChanges(flow: FlowFile, changes: HealChange[],
|
|
29
|
+
/**
|
|
30
|
+
* When the rebind happened. Injected rather than read here, because this function is pure and a
|
|
31
|
+
* clock inside it would make the flow it returns depend on when a test ran.
|
|
32
|
+
*
|
|
33
|
+
* Optional so every existing caller keeps working; without it the step still records WHICH anchor
|
|
34
|
+
* was replaced, which is the half a reader actually acts on.
|
|
35
|
+
*/
|
|
36
|
+
now?: () => number): {
|
|
29
37
|
flow: FlowFile;
|
|
30
38
|
applied: HealChange[];
|
|
31
39
|
};
|
|
@@ -38,7 +38,15 @@ export function confidenceFor(from, to) {
|
|
|
38
38
|
* skipped (idempotent / defensive), never throwing. Shared by the on-disk writer (FlowStore.heal)
|
|
39
39
|
* and the in-memory pre-write verification in reticle_flow_heal, so both rewrite identically.
|
|
40
40
|
*/
|
|
41
|
-
export function applyHealChanges(flow, changes
|
|
41
|
+
export function applyHealChanges(flow, changes,
|
|
42
|
+
/**
|
|
43
|
+
* When the rebind happened. Injected rather than read here, because this function is pure and a
|
|
44
|
+
* clock inside it would make the flow it returns depend on when a test ran.
|
|
45
|
+
*
|
|
46
|
+
* Optional so every existing caller keeps working; without it the step still records WHICH anchor
|
|
47
|
+
* was replaced, which is the half a reader actually acts on.
|
|
48
|
+
*/
|
|
49
|
+
now) {
|
|
42
50
|
const byStep = new Map();
|
|
43
51
|
for (const change of changes)
|
|
44
52
|
byStep.set(change.step, change);
|
|
@@ -51,7 +59,21 @@ export function applyHealChanges(flow, changes) {
|
|
|
51
59
|
return step;
|
|
52
60
|
}
|
|
53
61
|
applied.push(change);
|
|
54
|
-
return {
|
|
62
|
+
return {
|
|
63
|
+
...step,
|
|
64
|
+
anchor: { kind: AnchorKind.TESTID, value: change.to },
|
|
65
|
+
/*
|
|
66
|
+
* `from` is the ORIGINAL recorded anchor, kept across later heals.
|
|
67
|
+
*
|
|
68
|
+
* A step healed twice has an intermediate name nobody ever chose, so overwriting with the
|
|
69
|
+
* newest would throw away the only anchor a reader wants — the one that was recorded by
|
|
70
|
+
* driving. The timestamp is the LATEST heal, because that is when this anchor arrived.
|
|
71
|
+
*/
|
|
72
|
+
healed: {
|
|
73
|
+
from: step.healed?.from ?? change.from,
|
|
74
|
+
at: now?.() ?? step.healed?.at ?? 0,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
55
77
|
});
|
|
56
78
|
return { flow: { ...flow, steps }, applied };
|
|
57
79
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learn a flow step's source file from the element its anchor resolved to on a replay.
|
|
3
|
+
*
|
|
4
|
+
* A step recorded before the build stamped the app names no file, so a scoped re-verify cannot say
|
|
5
|
+
* which edits it covers. Every replay already asks the page for each anchor's element, and that
|
|
6
|
+
* element carries its stamp now: this records the answer at the one place every resolution passes
|
|
7
|
+
* through (the session's QUERY), rather than threading it through each step runner.
|
|
8
|
+
*/
|
|
9
|
+
import { type FlowFile, type FlowStep } from '@reticlehq/core';
|
|
10
|
+
import type { FlowReplaySession } from './flow-replay-types.js';
|
|
11
|
+
type StepSource = NonNullable<FlowStep['source']>;
|
|
12
|
+
/** `src/Pay.tsx:12:4` or `src/Pay.tsx:12` → `{file, line}`; anything else is not a location. */
|
|
13
|
+
export declare function parseCompactSource(value: string): StepSource | undefined;
|
|
14
|
+
/** Wrap a replay session so every anchor QUERY that resolves to one stamped element is remembered. */
|
|
15
|
+
export declare function recordingSources(inner: FlowReplaySession): {
|
|
16
|
+
session: FlowReplaySession;
|
|
17
|
+
sources: ReadonlyMap<string, StepSource>;
|
|
18
|
+
};
|
|
19
|
+
/** The flow with every sourceless step given the file its anchor resolved to; undefined if none was. */
|
|
20
|
+
export declare function withLearnedSources(flow: FlowFile, sources: ReadonlyMap<string, StepSource>): FlowFile | undefined;
|
|
21
|
+
export {};
|