@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
|
@@ -0,0 +1,84 @@
|
|
|
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 { ReticleCommand, asRecord, asString } from '@reticlehq/core';
|
|
10
|
+
import { anchorQueryArgs } from './flow-step-runners.js';
|
|
11
|
+
/** `src/Pay.tsx:12:4` or `src/Pay.tsx:12` → `{file, line}`; anything else is not a location. */
|
|
12
|
+
export function parseCompactSource(value) {
|
|
13
|
+
const match = /^(.+?):(\d+)(?::\d+)?$/.exec(value);
|
|
14
|
+
const file = match?.[1];
|
|
15
|
+
const line = match?.[2];
|
|
16
|
+
if (file === undefined || line === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
return { file, line: Number(line) };
|
|
19
|
+
}
|
|
20
|
+
function keyOf(args) {
|
|
21
|
+
if (args === undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
const by = asString(args['by']);
|
|
24
|
+
const value = asString(args['value']);
|
|
25
|
+
if (by === undefined || value === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
return JSON.stringify([by, value, asString(args['name']) ?? null]);
|
|
28
|
+
}
|
|
29
|
+
/** The one element a query found, when exactly one was found and it carries a stamp. */
|
|
30
|
+
function soleSource(result) {
|
|
31
|
+
const elements = asRecord(result)?.['elements'];
|
|
32
|
+
if (!Array.isArray(elements) || elements.length !== 1)
|
|
33
|
+
return undefined;
|
|
34
|
+
const source = asString(asRecord(elements[0])?.['source']);
|
|
35
|
+
return source === undefined ? undefined : parseCompactSource(source);
|
|
36
|
+
}
|
|
37
|
+
/** Wrap a replay session so every anchor QUERY that resolves to one stamped element is remembered. */
|
|
38
|
+
export function recordingSources(inner) {
|
|
39
|
+
const sources = new Map();
|
|
40
|
+
const session = new Proxy(inner, {
|
|
41
|
+
// Bound to the target, never the proxy: a real Session keeps `#private` state, and a method
|
|
42
|
+
// called with the proxy as `this` throws reading it.
|
|
43
|
+
get(target, prop) {
|
|
44
|
+
if (prop !== 'command') {
|
|
45
|
+
const value = Reflect.get(target, prop, target);
|
|
46
|
+
return 'function' === typeof value ? value.bind(target) : value;
|
|
47
|
+
}
|
|
48
|
+
return async (name, args) => {
|
|
49
|
+
const result = await target.command(name, args);
|
|
50
|
+
const key = name === ReticleCommand.QUERY ? keyOf(args) : undefined;
|
|
51
|
+
const source = result.ok ? soleSource(result.result) : undefined;
|
|
52
|
+
if (key !== undefined && source !== undefined)
|
|
53
|
+
sources.set(key, source);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
return { session, sources };
|
|
59
|
+
}
|
|
60
|
+
function learnSteps(steps, sources) {
|
|
61
|
+
let changed = false;
|
|
62
|
+
const next = steps.map((step) => {
|
|
63
|
+
const nested = step.steps === undefined ? undefined : learnSteps(step.steps, sources);
|
|
64
|
+
const learned = step.source === undefined
|
|
65
|
+
? sources.get(keyOf(anchorQueryArgs(step.anchor) ?? undefined) ?? '')
|
|
66
|
+
: undefined;
|
|
67
|
+
if (learned === undefined && nested?.changed !== true)
|
|
68
|
+
return step;
|
|
69
|
+
changed = true;
|
|
70
|
+
return {
|
|
71
|
+
...step,
|
|
72
|
+
...(learned === undefined ? {} : { source: learned }),
|
|
73
|
+
...(nested === undefined ? {} : { steps: nested.steps }),
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
return { steps: next, changed };
|
|
77
|
+
}
|
|
78
|
+
/** The flow with every sourceless step given the file its anchor resolved to; undefined if none was. */
|
|
79
|
+
export function withLearnedSources(flow, sources) {
|
|
80
|
+
if (0 === sources.size)
|
|
81
|
+
return undefined;
|
|
82
|
+
const { steps, changed } = learnSteps(flow.steps, sources);
|
|
83
|
+
return changed ? { ...flow, steps } : undefined;
|
|
84
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FlowFile } from '@reticlehq/core';
|
|
1
|
+
import { type ProjectId, type FlowFile } from '@reticlehq/core';
|
|
2
2
|
import { type FlowResult } from './flow-result.js';
|
|
3
3
|
/**
|
|
4
4
|
* Change one thing about a saved flow, and write the same file that load resolved.
|
|
@@ -21,12 +21,12 @@ import { type FlowResult } from './flow-result.js';
|
|
|
21
21
|
* every rule the document has ever been subject to.
|
|
22
22
|
*/
|
|
23
23
|
export interface NarrowWritePort {
|
|
24
|
-
load: (name: string, projectId?:
|
|
25
|
-
resolvePath: (name: string, projectId?:
|
|
24
|
+
load: (name: string, projectId?: ProjectId) => Promise<FlowResult<FlowFile>>;
|
|
25
|
+
resolvePath: (name: string, projectId?: ProjectId) => Promise<string | null>;
|
|
26
26
|
write: (path: string, contents: string) => Promise<void>;
|
|
27
27
|
serialize: (flow: FlowFile) => string;
|
|
28
28
|
}
|
|
29
|
-
export declare function changeInPlace<T>(port: NarrowWritePort, name: string, projectId:
|
|
29
|
+
export declare function changeInPlace<T>(port: NarrowWritePort, name: string, projectId: ProjectId | undefined, change: (flow: FlowFile) => {
|
|
30
30
|
next: FlowFile;
|
|
31
31
|
value: T;
|
|
32
32
|
}): Promise<FlowResult<T>>;
|
|
@@ -7,13 +7,6 @@
|
|
|
7
7
|
* stop/save can warn instead of writing a flow that fails on the next click.
|
|
8
8
|
*/
|
|
9
9
|
import { type ReticleEvent } from '@reticlehq/core';
|
|
10
|
-
/**
|
|
11
|
-
* The page a recording step sat on, comparable across path routers and hash routers.
|
|
12
|
-
*
|
|
13
|
-
* A startPath is stored as pathname+hash (`/#/search`); a ROUTE_CHANGE carries them separately.
|
|
14
|
-
* Without this, every hash-router page compares as `/` and a backtrack is invisible.
|
|
15
|
-
*/
|
|
16
|
-
export declare function recordingRouteOf(pathname: string, hash?: string): string;
|
|
17
10
|
/**
|
|
18
11
|
* Warn when the journey left a page and came back. Linear A→B→C is silent: that still cannot
|
|
19
12
|
* replay either if a later click needs a previous page, but the reported failure is specifically
|
|
@@ -17,7 +17,7 @@ function asString(value) {
|
|
|
17
17
|
* A startPath is stored as pathname+hash (`/#/search`); a ROUTE_CHANGE carries them separately.
|
|
18
18
|
* Without this, every hash-router page compares as `/` and a backtrack is invisible.
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
function recordingRouteOf(pathname, hash = '') {
|
|
21
21
|
const hashIdx = pathname.indexOf('#/');
|
|
22
22
|
if (hashIdx >= 0) {
|
|
23
23
|
return routePathOf(pathname.slice(0, hashIdx) || '/', pathname.slice(hashIdx));
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { FlowStepResult } from '@reticlehq/core';
|
|
14
14
|
/** Set on a replay that stopped early; omitted entirely otherwise, so a clean pass stays flat. */
|
|
15
|
-
|
|
15
|
+
interface ReplayHalt {
|
|
16
16
|
/** Index of the step that stopped the run. */
|
|
17
17
|
atStep: number;
|
|
18
18
|
/** How many of the flow's steps were never attempted because of it. */
|
|
@@ -24,3 +24,4 @@ export interface ReplayHalt {
|
|
|
24
24
|
* defect restated as its own fix.
|
|
25
25
|
*/
|
|
26
26
|
export declare function haltedFrom(results: readonly FlowStepResult[], total: number): ReplayHalt | undefined;
|
|
27
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Predicate } from '@reticlehq/core';
|
|
2
2
|
/** One captured agent action, normalized for replay. */
|
|
3
3
|
export interface RecordedStep {
|
|
4
4
|
/** ReticleTool.ACT | ReticleTool.ACT_SEQUENCE */
|
|
@@ -8,7 +8,7 @@ export interface RecordedStep {
|
|
|
8
8
|
/** false if any ref could not be normalized to a testid (replay only valid in-session). */
|
|
9
9
|
stable: boolean;
|
|
10
10
|
/** Optional post-condition annotation carried into the on-disk flow's expect. */
|
|
11
|
-
expect?:
|
|
11
|
+
expect?: Predicate;
|
|
12
12
|
/**
|
|
13
13
|
* The route this step ran on. RECORDER-INTERNAL: it never reaches the on-disk flow.
|
|
14
14
|
*
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { REDACTED_FILL, secretEnvKey } from './fields/flow-secret-field.js';
|
|
2
|
+
import { FlowPredicateSchema } from '@reticlehq/core';
|
|
2
3
|
import { DANGEROUS_ACTION_CONFIRM_ARG, ReticleCommand, QueryBy, } from '@reticlehq/core';
|
|
3
4
|
import { ReticleTool } from '@reticlehq/core';
|
|
4
5
|
import { formatStepAddress } from 'open-verification';
|
|
5
|
-
import { predicateToExpect, enforcedOnReplay } from '../../judgement/outcome/predicate-to-expect.js';
|
|
6
|
-
import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
7
6
|
import { asRecord, asString } from '@reticlehq/core';
|
|
8
7
|
/**
|
|
9
8
|
* The note attached when a testid resolves to multiple live elements (the first match is used).
|
|
@@ -93,7 +92,7 @@ route) {
|
|
|
93
92
|
// that is never recorded cannot contribute to either.
|
|
94
93
|
const step = compileActStep(args, res);
|
|
95
94
|
// Keep the assertion the agent actually made. `act_and_wait { until }` IS the agent saying what
|
|
96
|
-
// success means —
|
|
95
|
+
// success means — the large majority of calls carry one — and dropping it produced a flow graded
|
|
97
96
|
// "assertion-free: it will pass even if the feature is broken", which is the regression-suite
|
|
98
97
|
// story failing at its last step. Only kinds FlowExpect can express survive; see
|
|
99
98
|
// predicate-to-expect.ts.
|
|
@@ -105,16 +104,21 @@ route) {
|
|
|
105
104
|
/**
|
|
106
105
|
* A declared predicate, as a recorded expectation — or nothing.
|
|
107
106
|
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
107
|
+
* It is now the SAME OBJECT. A step's `expect` is a `Predicate`, so what the agent declared in
|
|
108
|
+
* `until` is what the flow file stores, and there is no conversion left to lose anything in.
|
|
109
|
+
*
|
|
110
|
+
* What was here before was a lossy round trip through a flat struct with one slot per kind. An
|
|
111
|
+
* `allOf[netA, netB]` saved as one of the two with nothing said; anything the struct could not
|
|
112
|
+
* express was dropped, and a filter then removed the kinds replay would not have checked anyway.
|
|
113
|
+
* Every one of those was a claim the agent made and the file did not keep.
|
|
112
114
|
*/
|
|
113
115
|
function enforceableExpect(raw) {
|
|
114
116
|
if (raw === undefined)
|
|
115
117
|
return undefined;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
// The flow file's own schema, so capture keeps exactly what a load would accept — a predicate
|
|
119
|
+
// holding a session ref is refused by both, rather than recorded here and rejected on read.
|
|
120
|
+
const parsed = FlowPredicateSchema.safeParse(raw);
|
|
121
|
+
return parsed.success ? parsed.data : undefined;
|
|
118
122
|
}
|
|
119
123
|
/**
|
|
120
124
|
* Compile a single reticle_act invocation into a normalized RecordedStep using the action result. Anchor
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Predicate } from '@reticlehq/core';
|
|
2
2
|
export declare class AnnotationStore {
|
|
3
3
|
#private;
|
|
4
4
|
/** Testids whose CONTENT must not be asserted at replay (mark-dynamic). Copy, never the live array. */
|
|
5
5
|
dynamic(name: string): string[];
|
|
6
6
|
/** The flow's golden end-condition (success-state), or undefined. */
|
|
7
|
-
success(name: string):
|
|
7
|
+
success(name: string): Predicate | undefined;
|
|
8
8
|
/** The flow's declared business goal (intent annotation), or undefined. */
|
|
9
9
|
intent(name: string): string | undefined;
|
|
10
10
|
/** Per-step expect predicates compiled from assert-* annotations, keyed by step index. */
|
|
11
|
-
stepExpect(name: string): Map<number,
|
|
11
|
+
stepExpect(name: string): Map<number, Predicate>;
|
|
12
12
|
/** Append a dynamic testid (mark-dynamic). De-duped so repeated marks stay idempotent. */
|
|
13
13
|
addDynamic(name: string, testid: string): void;
|
|
14
14
|
/** Set the flow's success expectation (success-state); a later set overwrites. */
|
|
15
|
-
setSuccess(name: string, expect:
|
|
15
|
+
setSuccess(name: string, expect: Predicate): void;
|
|
16
16
|
/** Set the flow's declared business goal (intent); a later set overwrites. */
|
|
17
17
|
setIntent(name: string, text: string): void;
|
|
18
18
|
/** Set one step's expect (assert-signal / assert-visible); a later set on the same index wins. */
|
|
19
|
-
setStepExpect(name: string, index: number, expect:
|
|
19
|
+
setStepExpect(name: string, index: number, expect: Predicate): void;
|
|
20
20
|
/** Drop a recording's annotations (called after reticle_flow_save folds them onto disk). */
|
|
21
21
|
clear(name: string): void;
|
|
22
22
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { writeFileAtomic } from '../../../memory/project/fs/write-atomic.js';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { reticleDirPaths } from '../../../memory/project/dir/reticle-dir.js';
|
|
4
5
|
import { withFileLock } from '../../../memory/project/file-lock.js';
|
|
@@ -77,9 +78,7 @@ export class AssertionTiersStore {
|
|
|
77
78
|
async save(tiers) {
|
|
78
79
|
await this.#fs.mkdir(dirname(this.#path));
|
|
79
80
|
const body = `${JSON.stringify({ version: ASSERTION_TIERS_VERSION, flows: tiers }, null, 2)}\n`;
|
|
80
|
-
|
|
81
|
-
await this.#fs.writeFile(tmp, body);
|
|
82
|
-
await this.#fs.rename(tmp, this.#path);
|
|
81
|
+
await writeFileAtomic(this.#fs, this.#path, body);
|
|
83
82
|
}
|
|
84
83
|
/** Record one flow's assertion shape + covered sources after it PASSED. Never breaks a replay. */
|
|
85
84
|
async recordPassing(name, steps, sources = []) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { writeFileAtomic } from '../../../memory/project/fs/write-atomic.js';
|
|
2
3
|
import { reticleDirPaths } from '../../../memory/project/dir/reticle-dir.js';
|
|
3
4
|
import { FlakeFileSchema, emptyRecord, isFlaky, recordOutcome, } from '../recording/flake.js';
|
|
4
5
|
const FLAKE_FILE_VERSION = 1;
|
|
@@ -38,9 +39,7 @@ export class FlakeStore {
|
|
|
38
39
|
async save(ledger) {
|
|
39
40
|
await this.#fs.mkdir(dirname(this.#path));
|
|
40
41
|
const body = `${JSON.stringify({ version: FLAKE_FILE_VERSION, flows: ledger }, null, 2)}\n`;
|
|
41
|
-
|
|
42
|
-
await this.#fs.writeFile(tmp, body);
|
|
43
|
-
await this.#fs.rename(tmp, this.#path);
|
|
42
|
+
await writeFileAtomic(this.#fs, this.#path, body);
|
|
44
43
|
}
|
|
45
44
|
/** Accrue one unchanged-code replay outcome for a flow and persist. */
|
|
46
45
|
async record(flow, passed) {
|
|
@@ -24,7 +24,7 @@ interface CoverageDimension {
|
|
|
24
24
|
/** Declared-but-never-exercised members — what "everything" is silently missing. */
|
|
25
25
|
uncovered: string[];
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
interface Coverage {
|
|
28
28
|
testids: CoverageDimension;
|
|
29
29
|
signals: CoverageDimension;
|
|
30
30
|
flows: CoverageDimension;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - one flow throwing never sinks the run — it is captured as that item's outcome, because a suite that
|
|
10
10
|
* aborts on the first crash tells you nothing about the other 199 flows.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
interface Outcome<T> {
|
|
13
13
|
ok: boolean;
|
|
14
14
|
value?: T;
|
|
15
15
|
error?: string;
|
|
@@ -24,3 +24,4 @@ export declare function mapWithConcurrency<I, T>(items: readonly I[], limit: num
|
|
|
24
24
|
* and never below 1. Explicit `requested` (the tool's `parallel` arg) wins when it is a sane number.
|
|
25
25
|
*/
|
|
26
26
|
export declare function resolveConcurrency(flowCount: number, poolCapacity: number, requested?: number): number;
|
|
27
|
+
export {};
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which flows a suite run replays, resolved against the store.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the MCP tool and the `reticle verify` command so the two cannot disagree about what a
|
|
5
|
+
* selection means.
|
|
6
|
+
*
|
|
7
|
+
* Lives beside the flow store rather than under `suite/` on purpose: it needs `flowsForSession`, and
|
|
8
|
+
* from `suite/` that import made `suite` and `flows` need EACH OTHER -- a mutual pair, which the
|
|
9
|
+
* reach guard forbids growing because two directories that need each other cannot be read, moved or
|
|
10
|
+
* tested apart. Here it adds no edge at all: `flows` reaching itself is nothing, and `cli -> flows`
|
|
11
|
+
* already existed. The pure rule lives in core (`selectFlows`); this is the part that has to read
|
|
12
|
+
* the files, because a label lives in one and `list` returns only names.
|
|
13
|
+
*/
|
|
14
|
+
import { type ProjectId } from '@reticlehq/core';
|
|
1
15
|
import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
2
16
|
/**
|
|
3
17
|
* Resolve what a suite run should replay, and what it is holding back.
|
|
@@ -9,7 +23,7 @@ import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
|
9
23
|
* would turn a corrupt file into missing coverage nobody is told about, and replay reports the parse
|
|
10
24
|
* failure far better than a silent omission does.
|
|
11
25
|
*/
|
|
12
|
-
export declare function resolveSuiteSelection(deps: ToolDeps, projectId:
|
|
26
|
+
export declare function resolveSuiteSelection(deps: ToolDeps, projectId: ProjectId | undefined, args: Record<string, unknown>): Promise<{
|
|
13
27
|
run: string[];
|
|
14
28
|
quarantined: string[];
|
|
15
29
|
unmatched: string[];
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* already existed. The pure rule lives in core (`selectFlows`); this is the part that has to read
|
|
12
12
|
* the files, because a label lives in one and `list` returns only names.
|
|
13
13
|
*/
|
|
14
|
-
import { FLOW_FILE_VERSION, orderFlows, selectFlows } from '@reticlehq/core';
|
|
14
|
+
import { FLOW_FILE_VERSION, orderFlows, selectFlows, } from '@reticlehq/core';
|
|
15
15
|
import { flowsForSession } from './flow-store-for-session.js';
|
|
16
16
|
/**
|
|
17
17
|
* Resolve what a suite run should replay, and what it is holding back.
|
|
@@ -7,6 +7,7 @@ import { ReticleTool } from '@reticlehq/core';
|
|
|
7
7
|
import { asNumber, asRecord, asString } from '@reticlehq/core';
|
|
8
8
|
import { workerCountSchema } from '../../surface/tools/args/numeric-bounds.js';
|
|
9
9
|
import { loadNamedFlows, resolveChangedFiles } from '../../command/cli/cli-flow-commands.js';
|
|
10
|
+
import { projectDirFor } from '../../memory/project/session-root.js';
|
|
10
11
|
import { affectedSavedFlows } from './change/flow-sources.js';
|
|
11
12
|
import { FLOW_TOOLS } from './flow-tools.js';
|
|
12
13
|
import { findContradictions } from '@reticlehq/engine/disagreement/contradictions.js';
|
|
@@ -62,10 +63,12 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
62
63
|
? args['files'].filter((f) => 'string' === typeof f)
|
|
63
64
|
: [];
|
|
64
65
|
const since = asString(args['since']);
|
|
65
|
-
// The
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
66
|
+
// The tree to diff is the SESSION's project, and it is neither of the two things this line
|
|
67
|
+
// used to say it was: `deps.reticleRoot` is the daemon's `.reticle` SUBDIRECTORY, and the
|
|
68
|
+
// daemon's cwd is wherever the MCP host started it — `/` or `$HOME` for a globally-registered
|
|
69
|
+
// server. Both answer about the wrong repository while reporting a clean "nothing changed",
|
|
70
|
+
// which reads as a pass. `projectDirFor` is the directory holding the resolved root.
|
|
71
|
+
const changed = await resolveChangedFiles(files, since, projectDirFor(deps, asString(args['sessionId'])));
|
|
69
72
|
const changedFiles = changed.files;
|
|
70
73
|
// Same root flow_save wrote to. A read that resolved differently would report "no flows
|
|
71
74
|
// covered this change" over flows that exist, which reads as a clean result rather than
|
|
@@ -181,6 +184,19 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
181
184
|
measured: extra.measured,
|
|
182
185
|
};
|
|
183
186
|
}
|
|
187
|
+
// The mirror of `earnedNo`: a green from flows that ran only because their sources are unknown
|
|
188
|
+
// says nothing about these files either. At least one attributed flow has to have passed.
|
|
189
|
+
if (0 === attributedFailures(affected, unknownProvenance).length) {
|
|
190
|
+
return {
|
|
191
|
+
verified: Verified.UNKNOWN,
|
|
192
|
+
because: `the flows that ran passed, but Reticle cannot tell which sources any of them cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
|
|
193
|
+
changedFiles,
|
|
194
|
+
flowsRun: affected,
|
|
195
|
+
suite,
|
|
196
|
+
unknownProvenance,
|
|
197
|
+
measured: extra.measured,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
184
200
|
return {
|
|
185
201
|
verified: Verified.YES,
|
|
186
202
|
because: `all ${String(passed)} flows covering these files passed${provenanceNote}${extra.untouched === undefined ? '' : `, with ${String(extra.untouched.length)} controls left undriven`}`,
|
|
@@ -223,6 +239,7 @@ async function inspectAfterReplay(deps, args, cursor) {
|
|
|
223
239
|
currentDocumentId: session.currentDocumentId,
|
|
224
240
|
currentEditEpoch: session.currentEditEpoch,
|
|
225
241
|
appOrigin: session.url,
|
|
242
|
+
background: session.background,
|
|
226
243
|
// The replay is the action, and the cursor is where it started.
|
|
227
244
|
actionSince: cursor,
|
|
228
245
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Is this machine signed in to a Reticle workspace?
|
|
3
3
|
*
|
|
4
4
|
* Asked by the HUD, and there are two sources that must not be reimplemented per caller: the session
|
|
5
|
-
* file `reticle login` writes to `~/.reticle/session.json`, and `
|
|
5
|
+
* file `reticle login` writes to `~/.reticle/session.json`, and `RETICLE_API_KEY` for an agent that
|
|
6
6
|
* was handed a key instead. Everything that gates on login — the defect sync button, the prompt to sign
|
|
7
7
|
* in — resolves through here.
|
|
8
8
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Is this machine signed in to a Reticle workspace?
|
|
3
3
|
*
|
|
4
4
|
* Asked by the HUD, and there are two sources that must not be reimplemented per caller: the session
|
|
5
|
-
* file `reticle login` writes to `~/.reticle/session.json`, and `
|
|
5
|
+
* file `reticle login` writes to `~/.reticle/session.json`, and `RETICLE_API_KEY` for an agent that
|
|
6
6
|
* was handed a key instead. Everything that gates on login — the defect sync button, the prompt to sign
|
|
7
7
|
* in — resolves through here.
|
|
8
8
|
*
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { readFileSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import { ReticleDir } from '@reticlehq/core';
|
|
14
|
+
import { ReticleDir, apiKeyFrom } from '@reticlehq/core';
|
|
15
15
|
import { CloudEnv } from './cloud-sync.js';
|
|
16
16
|
import { SESSION_FILE } from '../../command/cli/cloud-kit.js';
|
|
17
17
|
/** Signed OUT is the safe answer: it shows the way in, where a wrong "signed in" hides it. */
|
|
@@ -57,9 +57,10 @@ function fromSessionFile(home) {
|
|
|
57
57
|
* acting.
|
|
58
58
|
*/
|
|
59
59
|
export function readAccountState(home, env) {
|
|
60
|
-
|
|
60
|
+
// Through the resolver, so a machine still exporting the key's previous name is still signed in.
|
|
61
|
+
const key = apiKeyFrom(env);
|
|
61
62
|
const url = env[CloudEnv.URL];
|
|
62
|
-
if (
|
|
63
|
+
if (key !== undefined) {
|
|
63
64
|
return {
|
|
64
65
|
signedIn: true,
|
|
65
66
|
...('string' === typeof url && url.length > 0 ? { host: url.replace(/\/+$/, '') } : {}),
|
|
@@ -67,4 +67,13 @@ export declare const credentialSlot: (url: string, projectId: string, orgId?: st
|
|
|
67
67
|
* so the single-project / CI flow is unchanged. `homeDir` is injected (testable; `os.homedir()` at call).
|
|
68
68
|
*/
|
|
69
69
|
export declare function resolveProjectCloud(fs: FileSystemPort, reticleRoot: string, homeDir: string, env: NodeJS.ProcessEnv): Promise<ProjectCloud>;
|
|
70
|
+
/**
|
|
71
|
+
* The linked credential, as a port the tool surface can be handed.
|
|
72
|
+
*
|
|
73
|
+
* Lives here, beside the resolver it wraps, rather than in the daemon bootstrap: `index.ts` sits
|
|
74
|
+
* exactly on the thousand-line cap, so a caller that needs this in three lines has to put those
|
|
75
|
+
* three lines somewhere they belong. The harness reads it so a machine that has run `reticle link`
|
|
76
|
+
* needs no exported key — see `withLinkedCredential`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const linkedCloudPort: (fs: FileSystemPort, reticleRoot: string, homeDir: string, env: NodeJS.ProcessEnv) => () => Promise<CloudConfig | null>;
|
|
70
79
|
export {};
|
|
@@ -145,3 +145,12 @@ export async function resolveProjectCloud(fs, reticleRoot, homeDir, env) {
|
|
|
145
145
|
const config = key !== null ? { url: link.url.replace(/\/+$/, ''), apiKey: key } : null;
|
|
146
146
|
return { config, policy, verify: link.verify, projectId: link.projectId };
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* The linked credential, as a port the tool surface can be handed.
|
|
150
|
+
*
|
|
151
|
+
* Lives here, beside the resolver it wraps, rather than in the daemon bootstrap: `index.ts` sits
|
|
152
|
+
* exactly on the thousand-line cap, so a caller that needs this in three lines has to put those
|
|
153
|
+
* three lines somewhere they belong. The harness reads it so a machine that has run `reticle link`
|
|
154
|
+
* needs no exported key — see `withLinkedCredential`.
|
|
155
|
+
*/
|
|
156
|
+
export const linkedCloudPort = (fs, reticleRoot, homeDir, env) => async () => (await resolveProjectCloud(fs, reticleRoot, homeDir, env)).config;
|
|
@@ -10,10 +10,15 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
import { type FlowFile, type ReticleVerificationRun, type RunRecord, type VerifyProgressEvent } from '@reticlehq/core';
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Env var names — the presence of BOTH is what "logged in" means for cloud sync.
|
|
15
|
+
*
|
|
16
|
+
* `KEY` names the CURRENT spelling only. The previous one is still honoured, through `apiKeyFrom`
|
|
17
|
+
* below, which is the one place that knows there are two.
|
|
18
|
+
*/
|
|
14
19
|
export declare const CloudEnv: {
|
|
15
20
|
readonly URL: "RETICLE_CLOUD_URL";
|
|
16
|
-
readonly KEY: "
|
|
21
|
+
readonly KEY: "RETICLE_API_KEY";
|
|
17
22
|
};
|
|
18
23
|
export interface CloudConfig {
|
|
19
24
|
url: string;
|
|
@@ -170,8 +175,6 @@ export declare function submitServerVerification(body: {
|
|
|
170
175
|
* allowed to influence a verdict or an exit code.
|
|
171
176
|
*/
|
|
172
177
|
export declare function syncProgressToCloud(runId: string, events: readonly VerifyProgressEvent[], config: CloudConfig | null, fetchImpl: FetchLike): Promise<SyncResult>;
|
|
173
|
-
/** How long events accumulate before a flush. See the batching note above. */
|
|
174
|
-
export declare const PROGRESS_FLUSH_MS = 2000;
|
|
175
178
|
/**
|
|
176
179
|
* A listener that buffers progress and ships it on a timer.
|
|
177
180
|
*
|
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
* cloud copy is an enhancement, so a push error is logged and swallowed.
|
|
10
10
|
*/
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
-
import { VERIFY_PROGRESS_MAX_EVENTS, } from '@reticlehq/core';
|
|
13
|
-
/**
|
|
12
|
+
import { VERIFY_PROGRESS_MAX_EVENTS, apiKeyFrom, } from '@reticlehq/core';
|
|
13
|
+
/**
|
|
14
|
+
* Env var names — the presence of BOTH is what "logged in" means for cloud sync.
|
|
15
|
+
*
|
|
16
|
+
* `KEY` names the CURRENT spelling only. The previous one is still honoured, through `apiKeyFrom`
|
|
17
|
+
* below, which is the one place that knows there are two.
|
|
18
|
+
*/
|
|
14
19
|
export const CloudEnv = {
|
|
15
20
|
URL: 'RETICLE_CLOUD_URL',
|
|
16
|
-
KEY: '
|
|
21
|
+
KEY: 'RETICLE_API_KEY',
|
|
17
22
|
};
|
|
18
23
|
/** Paths the OSS server pushes to (match the cloud app's contract). */
|
|
19
24
|
const CLOUD_FLOWS_PATH = '/v1/flows';
|
|
@@ -25,10 +30,10 @@ const CLOUD_PROGRESS_PATH = '/v1/connect/progress';
|
|
|
25
30
|
/** Resolve cloud credentials from the environment, or null when not logged in (sync disabled). */
|
|
26
31
|
export function resolveCloudConfig(env) {
|
|
27
32
|
const url = env[CloudEnv.URL];
|
|
28
|
-
const apiKey = env
|
|
33
|
+
const apiKey = apiKeyFrom(env);
|
|
29
34
|
if (typeof url !== 'string' || 0 === url.length)
|
|
30
35
|
return null;
|
|
31
|
-
if (
|
|
36
|
+
if (apiKey === undefined)
|
|
32
37
|
return null;
|
|
33
38
|
return { url: url.replace(/\/+$/, ''), apiKey };
|
|
34
39
|
}
|
|
@@ -288,7 +293,7 @@ export async function syncProgressToCloud(runId, events, config, fetchImpl) {
|
|
|
288
293
|
}
|
|
289
294
|
}
|
|
290
295
|
/** How long events accumulate before a flush. See the batching note above. */
|
|
291
|
-
|
|
296
|
+
const PROGRESS_FLUSH_MS = 2_000;
|
|
292
297
|
/**
|
|
293
298
|
* A listener that buffers progress and ships it on a timer.
|
|
294
299
|
*
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Narrowed to what this file stores: the caller's own type is richer and irrelevant here. */
|
|
2
|
+
export interface HarnessConfigView {
|
|
3
|
+
provider: string;
|
|
4
|
+
harnessEnabled: boolean;
|
|
5
|
+
harnessEntitled: boolean;
|
|
6
|
+
/** Whether the platform holds a key for `provider`. Entitlement is not readiness — see the offer. */
|
|
7
|
+
providerReady: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ConfigSource {
|
|
10
|
+
read(): HarnessConfigView | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare function harnessConfigSource(load: () => Promise<HarnessConfigView | undefined>, now?: () => number): ConfigSource;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The harness switch, cached for a HUD that reads it inline.
|
|
3
|
+
*
|
|
4
|
+
* `fetchPlatformConfig` already asks the platform and parses the answer; this adds the one thing the
|
|
5
|
+
* impact snapshot needs and the drive path does not — a SYNCHRONOUS read. The snapshot is rebuilt
|
|
6
|
+
* dozens of times a session, the answer changes about as often as somebody opens a settings page,
|
|
7
|
+
* and a panel that awaited the network on every repaint would be a panel that stutters.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately the same shape as `harnessOfferSource` next door rather than a shared abstraction:
|
|
10
|
+
* two callers with the same caching problem is not yet a pattern, and the day one of them needs a
|
|
11
|
+
* different staleness rule the shared version becomes the awkward one.
|
|
12
|
+
*
|
|
13
|
+
* Every failure answers `undefined`, which the HUD must read as "we have not heard" and render as NO
|
|
14
|
+
* control — never as "off". Showing a switch that cannot be honoured is worse than showing none.
|
|
15
|
+
*
|
|
16
|
+
* The loader arrives as a PORT rather than an import. Reading the platform lives in
|
|
17
|
+
* `features/harness`, and a cloud-memory file reaching into a feature is the reach the directory
|
|
18
|
+
* guard refuses — correctly, and for the same reason it refused the tool surface reaching the other
|
|
19
|
+
* way. Whoever wires the daemon owns both sides and can hand one to the other.
|
|
20
|
+
*/
|
|
21
|
+
/** How long a cached answer is trusted. A person who just changed it expects the panel to notice. */
|
|
22
|
+
const FRESH_MS = 30 * 1_000;
|
|
23
|
+
export function harnessConfigSource(load, now = () => Date.now()) {
|
|
24
|
+
let cached;
|
|
25
|
+
// `undefined` rather than 0: "never asked" is a different state from "asked at the epoch", and
|
|
26
|
+
// conflating them made a source with an injected clock never take its first read.
|
|
27
|
+
let fetchedAt;
|
|
28
|
+
let inFlight = false;
|
|
29
|
+
const refresh = () => {
|
|
30
|
+
if (inFlight)
|
|
31
|
+
return;
|
|
32
|
+
inFlight = true;
|
|
33
|
+
void load()
|
|
34
|
+
.then((cfg) => {
|
|
35
|
+
// A failed read keeps the LAST good answer rather than blanking the control mid-session:
|
|
36
|
+
// one dropped request is not evidence that somebody changed their mind.
|
|
37
|
+
if (cfg !== undefined)
|
|
38
|
+
cached = cfg;
|
|
39
|
+
fetchedAt = now();
|
|
40
|
+
})
|
|
41
|
+
.catch(() => {
|
|
42
|
+
fetchedAt = now();
|
|
43
|
+
})
|
|
44
|
+
.finally(() => {
|
|
45
|
+
inFlight = false;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
// Asked at construction, not first read: the daemon pushes a snapshot the instant a session
|
|
49
|
+
// attaches, and on a page nobody drives that is the only push there will ever be.
|
|
50
|
+
refresh();
|
|
51
|
+
return {
|
|
52
|
+
read() {
|
|
53
|
+
if (fetchedAt === undefined || FRESH_MS <= now() - fetchedAt)
|
|
54
|
+
refresh();
|
|
55
|
+
return cached;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|