@reticlehq/server 3.6.0 → 3.7.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 +122 -123
- package/dist/command/cli/adhoc-suite.d.ts +14 -0
- package/dist/command/cli/adhoc-suite.js +42 -7
- package/dist/command/cli/adhoc-verdict.d.ts +18 -2
- package/dist/command/cli/adhoc-verdict.js +70 -6
- package/dist/command/cli/answers/gate-exit.d.ts +23 -0
- package/dist/command/cli/answers/gate-exit.js +45 -0
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +3 -1
- package/dist/command/cli/auth/device-flow.d.ts +13 -2
- package/dist/command/cli/auth/device-flow.js +17 -0
- package/dist/command/cli/cli-doctor.js +14 -0
- package/dist/command/cli/cli-flow-commands.d.ts +0 -11
- package/dist/command/cli/cli-flow-commands.js +28 -105
- package/dist/command/cli/cli-kill.js +3 -2
- package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
- package/dist/command/cli/cli-parse-grammar.js +75 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -0
- package/dist/command/cli/cli-parse-verify.js +18 -0
- package/dist/command/cli/cli-parse.d.ts +19 -28
- package/dist/command/cli/cli-parse.js +82 -182
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +61 -9
- package/dist/command/cli/cli-usage.d.ts +33 -0
- package/dist/command/cli/cli-usage.js +287 -0
- package/dist/command/cli/cli-verify.d.ts +34 -4
- package/dist/command/cli/cli-verify.js +81 -12
- package/dist/command/cli/cloud-cli.d.ts +13 -0
- package/dist/command/cli/cloud-cli.js +145 -333
- package/dist/command/cli/cloud-kit.d.ts +12 -0
- package/dist/command/cli/cloud-kit.js +41 -2
- package/dist/command/cli/cloud-link.d.ts +57 -0
- package/dist/command/cli/cloud-link.js +308 -0
- package/dist/command/cli/cloud-login.d.ts +15 -1
- package/dist/command/cli/cloud-login.js +67 -13
- package/dist/command/cli/context-command.d.ts +1 -0
- package/dist/command/cli/context-command.js +33 -0
- package/dist/command/cli/daemon-start-options.d.ts +3 -0
- package/dist/command/cli/daemon-start-options.js +24 -3
- package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
- package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
- package/dist/command/cli/doctor/doctor-project-line.js +5 -4
- package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
- package/dist/command/cli/doctor/doctor-rows.js +6 -0
- package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
- package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
- package/dist/command/cli/known-regression.d.ts +13 -0
- package/dist/command/cli/known-regression.js +34 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
- package/dist/command/cli/launch/sdk-version-match.js +25 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +11 -2
- package/dist/command/cli/mcp-command.d.ts +6 -0
- package/dist/command/cli/mcp-command.js +13 -5
- package/dist/command/cli/ports/port-holder.js +1 -1
- package/dist/command/cli/project-name.d.ts +29 -1
- package/dist/command/cli/project-name.js +107 -2
- package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
- package/dist/command/cli/setup-mcp-cli.js +29 -8
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +47 -2
- package/dist/command/cli/status/workspace-links.d.ts +10 -0
- package/dist/command/cli/status/workspace-links.js +29 -0
- package/dist/command/cli/try-command.d.ts +96 -0
- package/dist/command/cli/try-command.js +362 -0
- package/dist/command/cli/tutorial.d.ts +0 -100
- package/dist/command/cli/tutorial.js +0 -178
- package/dist/command/cli/verify-results.d.ts +19 -0
- package/dist/command/cli/verify-results.js +29 -0
- package/dist/command/cli-onboarding.d.ts +0 -11
- package/dist/command/cli-onboarding.js +0 -15
- package/dist/command/cli.d.ts +14 -0
- package/dist/command/cli.js +103 -83
- package/dist/command/daemon/daemon-resilience.js +4 -4
- package/dist/command/daemon/daemon-resolve.d.ts +9 -1
- package/dist/command/daemon/daemon-resolve.js +49 -10
- package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
- package/dist/command/setup/bringup/bridge-port.js +13 -3
- package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
- package/dist/command/setup/bringup/ensure-daemon.js +9 -2
- package/dist/command/setup/init/agent-restart.d.ts +23 -0
- package/dist/command/setup/init/agent-restart.js +28 -0
- package/dist/command/setup/init/init-port.d.ts +10 -1
- package/dist/command/setup/init/init-port.js +22 -2
- package/dist/command/setup/init/init-runtime.d.ts +12 -2
- package/dist/command/setup/init/init-runtime.js +96 -37
- package/dist/command/setup/node-effects.d.ts +2 -0
- package/dist/command/setup/node-effects.js +36 -8
- package/dist/command/setup/probe/existing-app.d.ts +12 -0
- package/dist/command/setup/probe/existing-app.js +14 -0
- package/dist/command/setup/remaining-steps.js +1 -1
- package/dist/command/setup/run-setup.d.ts +29 -9
- package/dist/command/setup/run-setup.js +34 -28
- package/dist/command/setup/setup-command.d.ts +23 -0
- package/dist/command/setup/setup-command.js +57 -4
- package/dist/command/setup/setup-mcp.js +25 -2
- package/dist/command/setup/tauri-signing.d.ts +19 -0
- package/dist/command/setup/tauri-signing.js +79 -0
- package/dist/command/setup/terminal/listeners.d.ts +10 -1
- package/dist/command/setup/terminal/listeners.js +37 -6
- package/dist/command/update/update-nudge.d.ts +9 -0
- package/dist/command/update/update-nudge.js +1 -1
- package/dist/command/update/updater.js +11 -15
- package/dist/command/version/version-nudge.js +3 -0
- package/dist/command/version/version-skew.d.ts +20 -1
- package/dist/command/version/version-skew.js +38 -2
- package/dist/features/crawl/crawl.js +14 -1
- package/dist/features/exhaust/ledger.d.ts +14 -14
- package/dist/features/harness/drive-report.d.ts +39 -5
- package/dist/features/harness/drive-report.js +271 -54
- package/dist/features/harness/drive-runs.d.ts +120 -0
- package/dist/features/harness/drive-runs.js +289 -0
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/explore/console-pages.d.ts +15 -0
- package/dist/features/harness/explore/console-pages.js +146 -0
- package/dist/features/harness/explore/disparity.d.ts +19 -0
- package/dist/features/harness/explore/disparity.js +71 -0
- package/dist/features/harness/explore/fake-app.d.ts +16 -0
- package/dist/features/harness/explore/fake-app.js +49 -0
- package/dist/features/harness/explore/goal-search.d.ts +26 -0
- package/dist/features/harness/explore/goal-search.js +144 -0
- package/dist/features/harness/explore/page-tree.d.ts +49 -0
- package/dist/features/harness/explore/page-tree.js +148 -0
- package/dist/features/harness/explore/site-map.d.ts +34 -0
- package/dist/features/harness/explore/site-map.js +150 -0
- package/dist/features/harness/goals.d.ts +68 -7
- package/dist/features/harness/goals.js +289 -36
- package/dist/features/harness/harness.d.ts +30 -1
- package/dist/features/harness/harness.js +141 -7
- package/dist/features/harness/platform/local-apps.d.ts +36 -3
- package/dist/features/harness/platform/local-apps.js +81 -5
- package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
- package/dist/features/harness/platform/persona-credentials.js +69 -0
- package/dist/features/harness/platform/platform-drives.d.ts +61 -0
- package/dist/features/harness/platform/platform-drives.js +114 -0
- package/dist/features/harness/platform/refusal.d.ts +52 -0
- package/dist/features/harness/platform/refusal.js +71 -0
- package/dist/features/harness/platform/remote-drive.d.ts +8 -2
- package/dist/features/harness/platform/remote-drive.js +22 -9
- package/dist/features/harness/platform/script.d.ts +57 -11
- package/dist/features/harness/platform/script.js +144 -12
- package/dist/features/harness/platform/server-driver.d.ts +17 -4
- package/dist/features/harness/platform/server-driver.js +29 -26
- package/dist/features/harness/platform-config.d.ts +38 -5
- package/dist/features/harness/platform-config.js +93 -8
- package/dist/features/harness/route-guess.d.ts +13 -0
- package/dist/features/harness/route-guess.js +49 -0
- package/dist/features/harness/script/script-run.d.ts +30 -1
- package/dist/features/harness/script/script-run.js +355 -48
- package/dist/features/license/license.d.ts +2 -2
- package/dist/features/visual/step-shots.d.ts +31 -0
- package/dist/features/visual/step-shots.js +231 -0
- package/dist/features/visual/visual-tools.js +22 -14
- package/dist/hooks/client-directory.d.ts +18 -0
- package/dist/hooks/client-directory.js +57 -0
- package/dist/hooks/coding-agents.d.ts +34 -0
- package/dist/hooks/coding-agents.js +117 -0
- package/dist/hooks/driven-by.d.ts +21 -2
- package/dist/hooks/driven-by.js +42 -2
- package/dist/hooks/hook-bus.d.ts +4 -0
- package/dist/hooks/hook-bus.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26 -183
- package/dist/judgement/capsule/capsule-store.d.ts +2 -2
- package/dist/judgement/capsule/causal-summary.d.ts +15 -1
- package/dist/judgement/capsule/causal-summary.js +33 -0
- package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
- package/dist/judgement/runs/artifact/build-verification-run.js +20 -4
- package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
- package/dist/judgement/runs/artifact/gap-summary.js +12 -3
- package/dist/judgement/runs/artifact/replay-mapping.js +25 -9
- package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
- package/dist/judgement/runs/artifact/run-store.js +175 -8
- package/dist/judgement/runs/context-tools.js +59 -1
- package/dist/judgement/runs/default-run-id.d.ts +3 -0
- package/dist/judgement/runs/default-run-id.js +16 -0
- package/dist/judgement/runs/drive-run.d.ts +53 -5
- package/dist/judgement/runs/drive-run.js +234 -20
- package/dist/judgement/runs/resume.d.ts +44 -0
- package/dist/judgement/runs/resume.js +137 -0
- package/dist/judgement/runs/reticle-runner.js +5 -2
- package/dist/judgement/runs/runner-port.d.ts +5 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +2 -0
- package/dist/judgement/runs/verification-sync.js +36 -15
- package/dist/language/flows/decision.js +2 -1
- package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
- package/dist/language/flows/fields/flow-secret-field.js +25 -2
- package/dist/language/flows/flow-anchor.d.ts +7 -2
- package/dist/language/flows/flow-anchor.js +10 -3
- package/dist/language/flows/flow-intent.d.ts +1 -0
- package/dist/language/flows/flow-journey.d.ts +6 -0
- package/dist/language/flows/flow-journey.js +20 -0
- package/dist/language/flows/flow-learning.d.ts +4 -0
- package/dist/language/flows/flow-learning.js +37 -3
- package/dist/language/flows/flow-replay-run.js +16 -18
- package/dist/language/flows/flow-replay-types.d.ts +1 -1
- package/dist/language/flows/flow-replay.d.ts +18 -1
- package/dist/language/flows/flow-replay.js +22 -7
- package/dist/language/flows/flow-result.d.ts +22 -1
- package/dist/language/flows/flow-result.js +47 -2
- package/dist/language/flows/flow-step-runners.d.ts +2 -0
- package/dist/language/flows/flow-step-runners.js +12 -6
- package/dist/language/flows/flow-store-for-session.js +5 -1
- package/dist/language/flows/flow-tools.d.ts +10 -0
- package/dist/language/flows/flow-tools.js +61 -62
- package/dist/language/flows/flows.d.ts +16 -1
- package/dist/language/flows/flows.js +69 -10
- package/dist/language/flows/narrow-write.d.ts +7 -0
- package/dist/language/flows/narrow-write.js +36 -1
- package/dist/language/flows/recording/recording-backtrack.js +14 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
- package/dist/language/flows/recording/tape/recordings.js +60 -10
- package/dist/language/flows/replay.js +29 -6
- package/dist/language/flows/single-replay.d.ts +7 -0
- package/dist/language/flows/single-replay.js +26 -0
- package/dist/language/flows/verify-change-tools.d.ts +1 -0
- package/dist/language/flows/verify-change-tools.js +200 -193
- package/dist/memory/cloud/cloud-config.d.ts +10 -1
- package/dist/memory/cloud/cloud-config.js +19 -5
- package/dist/memory/cloud/cloud-sync.js +3 -3
- package/dist/memory/cloud/harness-config.d.ts +49 -0
- package/dist/memory/cloud/harness-config.js +16 -20
- package/dist/memory/cloud/memory-scope.d.ts +17 -0
- package/dist/memory/cloud/memory-scope.js +22 -0
- package/dist/memory/cloud/sync-cycle.d.ts +32 -4
- package/dist/memory/cloud/sync-cycle.js +113 -12
- package/dist/memory/cloud/sync-daemon.d.ts +11 -0
- package/dist/memory/cloud/sync-daemon.js +114 -4
- package/dist/memory/cloud/sync-disk.js +26 -2
- package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
- package/dist/memory/cloud/sync-screenshots.js +105 -0
- package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
- package/dist/memory/cloud/unsynced-roots.js +166 -0
- package/dist/memory/impact/account-watch.d.ts +16 -1
- package/dist/memory/impact/account-watch.js +26 -1
- package/dist/memory/impact/impact-store.d.ts +1 -4
- package/dist/memory/impact/impact-store.js +32 -42
- package/dist/memory/intent/inline-intent.d.ts +1 -0
- package/dist/memory/intent/intent-shard-store.js +16 -4
- package/dist/memory/intent/intent-shard.d.ts +12 -0
- package/dist/memory/intent/intent-store.d.ts +1 -0
- package/dist/memory/intent/intent-tools.js +42 -2
- package/dist/memory/intent/open-intents.d.ts +6 -0
- package/dist/memory/intent/open-intents.js +10 -1
- package/dist/memory/intent/settled-words.d.ts +24 -0
- package/dist/memory/intent/settled-words.js +130 -0
- package/dist/memory/journal/drive-flow.d.ts +16 -0
- package/dist/memory/journal/drive-flow.js +34 -2
- package/dist/memory/journal/drive-run-flush.d.ts +2 -0
- package/dist/memory/journal/drive-run-flush.js +20 -5
- package/dist/memory/journal/journal-recorder.js +46 -3
- package/dist/memory/journal/on-disk/retention.d.ts +5 -0
- package/dist/memory/journal/on-disk/retention.js +27 -2
- package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
- package/dist/memory/journal/session-end.d.ts +15 -1
- package/dist/memory/journal/session-end.js +38 -7
- package/dist/memory/project/artifact-root-resolver.js +33 -5
- package/dist/memory/project/artifact-root.d.ts +4 -3
- package/dist/memory/project/artifact-root.js +2 -2
- package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
- package/dist/memory/project/dir/reticle-dir.js +23 -2
- package/dist/memory/project/link-directory.d.ts +15 -0
- package/dist/memory/project/link-directory.js +44 -0
- package/dist/memory/project/root-claims.d.ts +6 -0
- package/dist/memory/project/root-claims.js +39 -0
- package/dist/memory/project/session-root.d.ts +2 -0
- package/dist/memory/project/session-root.js +9 -1
- package/dist/memory/project/sync-status.d.ts +46 -0
- package/dist/memory/project/sync-status.js +135 -1
- package/dist/memory/recall/memory-tools.js +46 -3
- package/dist/memory/recall/platform-memory.d.ts +34 -0
- package/dist/memory/recall/platform-memory.js +139 -0
- package/dist/memory/recall/project-memory.js +36 -8
- package/dist/portal/bridge/bridge.d.ts +9 -5
- package/dist/portal/bridge/bridge.js +66 -23
- package/dist/portal/bridge/harness-request.d.ts +23 -0
- package/dist/portal/bridge/harness-request.js +27 -0
- package/dist/portal/bridge/wrong-path.d.ts +17 -0
- package/dist/portal/bridge/wrong-path.js +35 -0
- package/dist/portal/input/network-mock-tools.js +11 -2
- package/dist/portal/input/real-input.d.ts +2 -0
- package/dist/portal/input/real-input.js +13 -10
- package/dist/portal/pool/browser-pool.d.ts +48 -0
- package/dist/portal/pool/browser-pool.js +120 -2
- package/dist/portal/pool/context-permissions.d.ts +33 -0
- package/dist/portal/pool/context-permissions.js +33 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
- package/dist/portal/pool/lease-marker-stamp.js +49 -0
- package/dist/portal/pool/playwright-launcher.d.ts +27 -5
- package/dist/portal/pool/playwright-launcher.js +59 -14
- package/dist/portal/pool/pool-contract.d.ts +21 -1
- package/dist/portal/session/agent-link.d.ts +8 -0
- package/dist/portal/session/agent-link.js +17 -0
- package/dist/portal/session/command-payload.js +6 -1
- package/dist/portal/session/daemon-answers.d.ts +3 -3
- package/dist/portal/session/daemon-answers.js +4 -2
- package/dist/portal/session/dev-server/dev-command.d.ts +9 -0
- package/dist/portal/session/dev-server/dev-command.js +27 -1
- package/dist/portal/session/facts/observed-state.d.ts +16 -18
- package/dist/portal/session/facts/observed-state.js +46 -3
- package/dist/portal/session/human/live-control.d.ts +9 -2
- package/dist/portal/session/human/live-control.js +12 -3
- package/dist/portal/session/live-control-tools.js +82 -13
- package/dist/portal/session/narration-args.d.ts +5 -0
- package/dist/portal/session/narration-args.js +17 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +1 -1
- package/dist/portal/session/no-session-diagnosis.js +31 -9
- package/dist/portal/session/no-session-next-action.d.ts +20 -0
- package/dist/portal/session/no-session-next-action.js +134 -61
- package/dist/portal/session/no-session-watch.js +179 -22
- package/dist/portal/session/presence/session-lease.js +1 -1
- package/dist/portal/session/recorded-gaps.d.ts +97 -0
- package/dist/portal/session/recorded-gaps.js +251 -0
- package/dist/portal/session/session-health.d.ts +11 -0
- package/dist/portal/session/session-health.js +3 -1
- package/dist/portal/session/session-info.js +2 -1
- package/dist/portal/session/session-manager.d.ts +18 -0
- package/dist/portal/session/session-manager.js +54 -3
- package/dist/portal/session/session-successor.d.ts +8 -0
- package/dist/portal/session/session-successor.js +1 -1
- package/dist/portal/session/session.d.ts +15 -28
- package/dist/portal/session/session.js +60 -61
- package/dist/portal/session/timing/command-timeout.js +25 -1
- package/dist/portal/session/timing/wall-clock.d.ts +1 -1
- package/dist/portal/session/timing/wall-clock.js +1 -1
- package/dist/portal/session/zero-install-first-call.d.ts +31 -0
- package/dist/portal/session/zero-install-first-call.js +88 -0
- package/dist/start-options.d.ts +7 -1
- package/dist/start-wiring.d.ts +63 -0
- package/dist/start-wiring.js +169 -0
- package/dist/status-payload.d.ts +13 -1
- package/dist/status-payload.js +13 -1
- package/dist/surface/http-server.js +7 -1
- package/dist/surface/mcp/attach-memory.d.ts +1 -0
- package/dist/surface/mcp/attach-memory.js +12 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +4 -0
- package/dist/surface/mcp/mcp-proxy.js +29 -8
- package/dist/surface/mcp/mcp.d.ts +23 -0
- package/dist/surface/mcp/mcp.js +34 -4
- package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
- package/dist/surface/mcp/proxy/drive-calls.js +27 -0
- package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
- package/dist/surface/mcp/server-instructions.js +22 -14
- package/dist/surface/mcp/shared-params.js +1 -1
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +1 -0
- package/dist/surface/tools/account-tool.d.ts +22 -0
- package/dist/surface/tools/account-tool.js +157 -0
- package/dist/surface/tools/act/act-danger.d.ts +11 -0
- package/dist/surface/tools/act/act-danger.js +38 -1
- package/dist/surface/tools/act/act-observation.d.ts +33 -0
- package/dist/surface/tools/act/act-observation.js +57 -0
- package/dist/surface/tools/act/act-preflight.js +25 -2
- package/dist/surface/tools/act/act-target.js +8 -3
- package/dist/surface/tools/act/resolve-target.d.ts +1 -1
- package/dist/surface/tools/act/resolve-target.js +18 -3
- package/dist/surface/tools/act/screen-request.d.ts +34 -0
- package/dist/surface/tools/act/screen-request.js +113 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
- package/dist/surface/tools/act/sequence-grade.js +13 -2
- package/dist/surface/tools/act-sequence-tool.js +136 -27
- package/dist/surface/tools/act-tools.d.ts +0 -9
- package/dist/surface/tools/act-tools.js +48 -34
- package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
- package/dist/surface/tools/agent/app-notes-learning.js +80 -0
- package/dist/surface/tools/agent/app-notes.d.ts +4 -0
- package/dist/surface/tools/agent/app-notes.js +48 -0
- package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
- package/dist/surface/tools/agent/build-staleness.js +62 -0
- package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
- package/dist/surface/tools/agent/compact-envelope.js +374 -0
- package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
- package/dist/surface/tools/agent/for-the-agent.js +44 -0
- package/dist/surface/tools/agent/notices.d.ts +50 -0
- package/dist/surface/tools/agent/notices.js +152 -0
- package/dist/surface/tools/agent/reteach.d.ts +11 -0
- package/dist/surface/tools/agent/reteach.js +50 -0
- package/dist/surface/tools/args/alias-args.d.ts +5 -0
- package/dist/surface/tools/args/alias-args.js +10 -0
- package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
- package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
- package/dist/surface/tools/assert/assert-source.d.ts +15 -0
- package/dist/surface/tools/assert/assert-source.js +40 -0
- package/dist/surface/tools/assert/assert-verdict.js +25 -15
- package/dist/surface/tools/browser-tools.js +43 -1
- package/dist/surface/tools/chat-drives.d.ts +75 -0
- package/dist/surface/tools/chat-drives.js +265 -21
- package/dist/surface/tools/coverage-tools.d.ts +2 -10
- package/dist/surface/tools/coverage-tools.js +2 -53
- package/dist/surface/tools/dynamic-tools.js +29 -1
- package/dist/surface/tools/empty-session-list.d.ts +10 -0
- package/dist/surface/tools/empty-session-list.js +45 -5
- package/dist/surface/tools/error-recovery.d.ts +8 -0
- package/dist/surface/tools/error-recovery.js +69 -5
- package/dist/surface/tools/explore-tools.d.ts +23 -2
- package/dist/surface/tools/explore-tools.js +377 -74
- package/dist/surface/tools/feedback-tools.d.ts +2 -3
- package/dist/surface/tools/feedback-tools.js +4 -3
- package/dist/surface/tools/gap-novelty.d.ts +3 -1
- package/dist/surface/tools/gap-novelty.js +8 -1
- package/dist/surface/tools/harness-explore.d.ts +52 -73
- package/dist/surface/tools/harness-explore.js +177 -146
- package/dist/surface/tools/harness-gate.d.ts +88 -0
- package/dist/surface/tools/harness-gate.js +235 -0
- package/dist/surface/tools/harness-hunt.d.ts +43 -0
- package/dist/surface/tools/harness-hunt.js +215 -0
- package/dist/surface/tools/harness-plan.d.ts +5 -0
- package/dist/surface/tools/harness-plan.js +7 -0
- package/dist/surface/tools/harness-script.d.ts +22 -0
- package/dist/surface/tools/harness-script.js +256 -38
- package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
- package/dist/surface/tools/harness-tab-guard.js +27 -0
- package/dist/surface/tools/harness-toolset.d.ts +8 -0
- package/dist/surface/tools/harness-toolset.js +27 -4
- package/dist/surface/tools/human-notes.d.ts +16 -0
- package/dist/surface/tools/human-notes.js +27 -0
- package/dist/surface/tools/invoke-tool.d.ts +5 -2
- package/dist/surface/tools/invoke-tool.js +220 -93
- package/dist/surface/tools/lease-hint.js +26 -5
- package/dist/surface/tools/lease-permissions.d.ts +33 -0
- package/dist/surface/tools/lease-permissions.js +73 -0
- package/dist/surface/tools/lease-readiness.d.ts +38 -0
- package/dist/surface/tools/lease-readiness.js +57 -0
- package/dist/surface/tools/lease-seed.d.ts +20 -0
- package/dist/surface/tools/lease-seed.js +45 -1
- package/dist/surface/tools/lease-tools.d.ts +10 -1
- package/dist/surface/tools/lease-tools.js +119 -69
- package/dist/surface/tools/live-call-text.js +2 -2
- package/dist/surface/tools/merge-tools.js +42 -2
- package/dist/surface/tools/merged-name-redirect.js +4 -0
- package/dist/surface/tools/next-step.d.ts +11 -1
- package/dist/surface/tools/next-step.js +73 -18
- package/dist/surface/tools/observe-tools.js +43 -5
- package/dist/surface/tools/platform-moment.d.ts +60 -0
- package/dist/surface/tools/platform-moment.js +132 -4
- package/dist/surface/tools/read-tools.js +65 -14
- package/dist/surface/tools/reticle-result.d.ts +120 -0
- package/dist/surface/tools/reticle-result.js +364 -0
- package/dist/surface/tools/tool-kit.d.ts +68 -2
- package/dist/surface/tools/tool-kit.js +73 -1
- package/dist/surface/tools/tools.js +25 -13
- package/dist/telemetry/account-envelope.d.ts +7 -0
- package/dist/telemetry/account-envelope.js +61 -0
- package/dist/telemetry/agent-client.d.ts +15 -0
- package/dist/telemetry/agent-client.js +57 -0
- package/dist/telemetry/app-instrumented.js +9 -2
- package/dist/telemetry/argument-shape.d.ts +7 -0
- package/dist/telemetry/argument-shape.js +86 -1
- package/dist/telemetry/automation-hint.d.ts +2 -3
- package/dist/telemetry/automation-hint.js +33 -8
- package/dist/telemetry/cli-telemetry.js +7 -2
- package/dist/telemetry/daemon-telemetry.js +3 -0
- package/dist/telemetry/error-fingerprint.d.ts +10 -1
- package/dist/telemetry/error-fingerprint.js +61 -5
- package/dist/telemetry/feedback-cli.d.ts +0 -13
- package/dist/telemetry/feedback-cli.js +0 -77
- package/dist/telemetry/feedback.d.ts +13 -0
- package/dist/telemetry/feedback.js +183 -17
- package/dist/telemetry/harness-run.d.ts +18 -0
- package/dist/telemetry/harness-run.js +80 -0
- package/dist/telemetry/hud-events.d.ts +16 -0
- package/dist/telemetry/hud-events.js +27 -0
- package/dist/telemetry/hud-metrics.d.ts +27 -6
- package/dist/telemetry/hud-metrics.js +99 -25
- package/dist/telemetry/init-telemetry.js +2 -2
- package/dist/telemetry/install-source.d.ts +13 -25
- package/dist/telemetry/install-source.js +44 -3
- package/dist/telemetry/mcp-connection.d.ts +0 -14
- package/dist/telemetry/mcp-connection.js +5 -1
- package/dist/telemetry/onboarding-firsts.d.ts +32 -0
- package/dist/telemetry/onboarding-firsts.js +81 -6
- package/dist/telemetry/platform-linked.d.ts +5 -0
- package/dist/telemetry/platform-linked.js +32 -0
- package/dist/telemetry/project-profile.js +5 -3
- package/dist/telemetry/proxy-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.d.ts +15 -4
- package/dist/telemetry/session-metrics.js +88 -52
- package/dist/telemetry/telemetry.d.ts +26 -2
- package/dist/telemetry/telemetry.js +102 -21
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +12 -4
- package/dist/wire-journal.d.ts +2 -2
- package/dist/wire-journal.js +16 -5
- package/docs/README.md +2 -2
- package/docs/actions.mdx +6 -3
- package/docs/adapters.md +1 -1
- package/docs/agent-cheatsheet.md +24 -27
- package/docs/architecture.md +5 -5
- package/docs/autodrive.md +13 -12
- package/docs/benchmarks.md +5 -5
- package/docs/best-practices.mdx +27 -9
- package/docs/capabilities.mdx +14 -16
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +54 -75
- package/docs/cli/doctor.mdx +8 -3
- package/docs/cli/drive.mdx +12 -12
- package/docs/cli/feedback.mdx +3 -3
- package/docs/cli/gate.mdx +7 -2
- package/docs/cli/help.mdx +50 -55
- package/docs/cli/init.mdx +32 -15
- package/docs/cli/license.mdx +12 -8
- package/docs/cli/mcp.mdx +4 -4
- package/docs/cli/open.mdx +3 -3
- package/docs/cli/report.mdx +4 -2
- package/docs/cli/restart.mdx +2 -2
- package/docs/cli/rollback.mdx +14 -8
- package/docs/cli/serve.mdx +4 -4
- package/docs/cli/setup.mdx +23 -0
- package/docs/cli/status.mdx +13 -4
- package/docs/cli/stop.mdx +67 -8
- package/docs/cli/try.mdx +62 -0
- package/docs/cli/update.mdx +4 -3
- package/docs/cli/verify.mdx +17 -6
- package/docs/cli/version.mdx +3 -2
- package/docs/cli.mdx +110 -41
- package/docs/debugging.md +3 -3
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +5 -3
- package/docs/enterprise.md +11 -10
- package/docs/environment.mdx +5 -2
- package/docs/faq.mdx +3 -3
- package/docs/first-drive.md +5 -4
- package/docs/flows.md +35 -27
- package/docs/for-agents.md +4 -5
- package/docs/frameworks.mdx +32 -21
- package/docs/getting-started.md +66 -64
- package/docs/human-control.md +15 -14
- package/docs/index.mdx +15 -43
- package/docs/install-agentic.mdx +22 -37
- package/docs/install-manual.mdx +22 -12
- package/docs/instrumentation.mdx +5 -3
- package/docs/integration-patterns.md +7 -7
- package/docs/license-activation.mdx +1 -1
- package/docs/multi-agent-testing.md +24 -6
- package/docs/packages/babel-plugin.mdx +4 -2
- package/docs/packages/browser.mdx +16 -10
- package/docs/packages/core.mdx +12 -5
- package/docs/packages/electron.mdx +3 -2
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/eslint-plugin.mdx +18 -12
- package/docs/packages/next.mdx +8 -7
- package/docs/packages/open-verification.mdx +1 -1
- package/docs/packages/server.mdx +5 -5
- package/docs/packages/tauri.mdx +8 -8
- package/docs/packages/test.mdx +4 -10
- package/docs/packages/vite-plugin.mdx +11 -4
- package/docs/packages.mdx +11 -15
- package/docs/platform-integration.md +6 -5
- package/docs/predicates.mdx +40 -24
- package/docs/quickstart.mdx +58 -29
- package/docs/required-checks.md +8 -7
- package/docs/reticle-manual.md +15 -5
- package/docs/skill-file.mdx +17 -79
- package/docs/skill-setup.md +9 -9
- package/docs/skill.md +200 -0
- package/docs/system-map.md +7 -7
- package/docs/telemetry.md +22 -24
- package/docs/testing.md +4 -4
- package/docs/token-efficiency.md +12 -12
- package/docs/tools/act-and-wait.mdx +9 -2
- package/docs/tools/act-sequence.mdx +6 -7
- package/docs/tools/act.mdx +2 -2
- package/docs/tools/assert.mdx +18 -17
- package/docs/tools/console.mdx +9 -9
- package/docs/tools/inspect.mdx +16 -7
- package/docs/tools/look.mdx +24 -0
- package/docs/tools/navigate.mdx +5 -3
- package/docs/tools/network.mdx +2 -2
- package/docs/tools/observe.mdx +6 -6
- package/docs/tools/overview.mdx +34 -33
- package/docs/tools/query.mdx +5 -5
- package/docs/tools/session-and-feedback.mdx +13 -4
- package/docs/tools/sessions.mdx +8 -8
- package/docs/tools/snapshot.mdx +6 -5
- package/docs/tools/state.mdx +9 -9
- package/docs/tools/tools-and-run.mdx +26 -24
- package/docs/tools/verify.mdx +28 -0
- package/docs/tools/wait-for.mdx +8 -4
- package/docs/troubleshooting.mdx +21 -6
- package/docs/usage.md +51 -45
- package/docs/use-cases.mdx +3 -3
- package/docs/vs-chrome-devtools-mcp.mdx +9 -6
- package/docs/vs-playwright-mcp.mdx +3 -1
- package/docs/what-is-recorded.md +7 -5
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +6 -5
- package/dist/command/cli/demo/demo-page.d.ts +0 -78
- package/dist/command/cli/demo/demo-page.js +0 -249
- package/dist/command/cli/demo/demo-run.d.ts +0 -38
- package/dist/command/cli/demo/demo-run.js +0 -138
- package/dist/judgement/hunt/hunt-report.d.ts +0 -49
- package/dist/judgement/hunt/hunt-report.js +0 -84
- package/dist/language/flows/buddy-status.d.ts +0 -25
- package/dist/language/flows/buddy-status.js +0 -32
- package/dist/language/flows/change/watch-batcher.d.ts +0 -18
- package/dist/language/flows/change/watch-batcher.js +0 -19
- package/dist/memory/cloud/harness-offer.d.ts +0 -44
- package/dist/memory/cloud/harness-offer.js +0 -145
- package/dist/surface/tools/feature-capture.d.ts +0 -151
- package/dist/surface/tools/feature-capture.js +0 -235
- package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
- package/dist/surface/tools/tool-hit-rate.js +0 -90
- package/dist/telemetry/harness-drive.d.ts +0 -4
- package/dist/telemetry/harness-drive.js +0 -35
- package/dist/telemetry/identify.d.ts +0 -22
- package/dist/telemetry/identify.js +0 -73
- package/docs/cli/capsules.mdx +0 -70
- package/docs/cli/hunt.mdx +0 -73
- package/docs/cli/identify.mdx +0 -76
- package/docs/cli/kill.mdx +0 -91
- package/docs/cli/watch.mdx +0 -62
|
@@ -87,6 +87,13 @@ export function driveFlowName(route, steps = []) {
|
|
|
87
87
|
const parts = [lead, ...('' !== why || 0 === leg.length ? [] : [leg])].filter((p) => '' !== p);
|
|
88
88
|
return asFlowName(`${DRIVE_FLOW_PREFIX}${0 === parts.length ? 'journey' : parts.join('-')}`);
|
|
89
89
|
}
|
|
90
|
+
/** Why session end saved no flow, as reported on the `flow_recorded` step. */
|
|
91
|
+
export const DriveFlowSkipReason = {
|
|
92
|
+
/** The drive declared no consequence, so a saved flow could never go red. */
|
|
93
|
+
NO_DECLARED_CONSEQUENCE: 'no_declared_consequence',
|
|
94
|
+
/** What it proved rested on network mocks the flow would not carry (#1459). */
|
|
95
|
+
DRIVEN_UNDER_NETWORK_MOCKS: 'driven_under_network_mocks',
|
|
96
|
+
};
|
|
90
97
|
/**
|
|
91
98
|
* Turn this session's ambient tape into a flow, when it is worth one.
|
|
92
99
|
*
|
|
@@ -143,24 +150,48 @@ function typesASecret(step) {
|
|
|
143
150
|
const field = QueryBy.TESTID === by ? step.args['value'] : step.args['name'];
|
|
144
151
|
return 'string' === typeof field && defaultIsSensitiveKey(field);
|
|
145
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The tape without the attempts an agent made again. An action that declared nothing, followed at
|
|
155
|
+
* once by the same action on the same control that did, is a retry: saved, the flow clicked sign-in
|
|
156
|
+
* twice, the second click found no form, and every replay of it failed. A step repeated with the
|
|
157
|
+
* same declaration both times (a double submit on purpose) is two steps, and stays two.
|
|
158
|
+
*/
|
|
159
|
+
function withoutRetries(steps) {
|
|
160
|
+
return steps.filter((step, i) => {
|
|
161
|
+
const next = steps[i + 1];
|
|
162
|
+
if (next === undefined || step.expect !== undefined || next.expect === undefined)
|
|
163
|
+
return true;
|
|
164
|
+
// Tried again where it was tried: a page later it is a step of the journey, not a retry.
|
|
165
|
+
if (step.route !== next.route || step.page !== next.page || step.mocked !== next.mocked)
|
|
166
|
+
return true;
|
|
167
|
+
const { expect: _declared, ...again } = next.args;
|
|
168
|
+
const { expect: _none, ...first } = step.args;
|
|
169
|
+
return step.tool !== next.tool || JSON.stringify(first) !== JSON.stringify(again);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
146
172
|
/** A flow per journey, so two journeys in one session are two regression tests rather than one. */
|
|
147
173
|
export function driveFlowsFrom(tape) {
|
|
148
174
|
if (tape === undefined || 0 === tape.steps.length)
|
|
149
175
|
return { programs: [], outcome: {} };
|
|
150
176
|
const programs = [];
|
|
151
177
|
let unproven = 0;
|
|
152
|
-
|
|
178
|
+
let mocked = 0;
|
|
179
|
+
for (const segment of segmentsByRoute(withoutRetries(tape.steps))) {
|
|
153
180
|
if (!carriesAnAssertion(segment.steps)) {
|
|
154
181
|
unproven += segment.steps.length;
|
|
155
182
|
continue;
|
|
156
183
|
}
|
|
184
|
+
if (segment.steps.some((step) => true === step.mocked)) {
|
|
185
|
+
mocked += segment.steps.length;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
157
188
|
const startPath = segment.route ?? tape.startPath;
|
|
158
189
|
programs.push({
|
|
159
190
|
name: driveFlowName(startPath, segment.steps),
|
|
160
191
|
version: REPLAY_PROGRAM_VERSION,
|
|
161
192
|
// The route is recorder-internal and has no business on disk — `startPath` is where the
|
|
162
193
|
// on-disk flow says the same thing, in the field replay actually reads.
|
|
163
|
-
steps: segment.steps.map(({ route: _route, ...step }) => step),
|
|
194
|
+
steps: segment.steps.map(({ route: _route, mocked: _mocked, ...step }) => step),
|
|
164
195
|
...(startPath === undefined ? {} : { startPath }),
|
|
165
196
|
});
|
|
166
197
|
}
|
|
@@ -169,6 +200,7 @@ export function driveFlowsFrom(tape) {
|
|
|
169
200
|
outcome: {
|
|
170
201
|
...(0 === programs.length ? {} : { saved: programs.map((p) => p.name) }),
|
|
171
202
|
...(0 === unproven ? {} : { unprovenSteps: unproven }),
|
|
203
|
+
...(0 === mocked ? {} : { mockedSteps: mocked }),
|
|
172
204
|
},
|
|
173
205
|
};
|
|
174
206
|
}
|
|
@@ -26,6 +26,8 @@ export interface FlushableSession {
|
|
|
26
26
|
export interface DriveRunFlushDeps<S extends FlushableSession> {
|
|
27
27
|
/** Find the live session a verdict came from, or undefined when it has already gone. */
|
|
28
28
|
resolve: (sessionId: string) => S | undefined;
|
|
29
|
+
/** Every live session: a Harness note rewrites them all, since it does not say which tab drove. */
|
|
30
|
+
all?: () => readonly S[];
|
|
29
31
|
/** Write this session's run artifact. The same call teardown makes. */
|
|
30
32
|
write: (session: S) => Promise<void>;
|
|
31
33
|
/** Injected so the test does not wait three real seconds. */
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* the last verdicts of a session are the ones somebody is waiting on.
|
|
21
21
|
*/
|
|
22
22
|
import { HookEvent } from '@reticlehq/core/hooks';
|
|
23
|
-
import { onHook } from '../../hooks/hook-bus.js';
|
|
23
|
+
import { onHook, onPendingWrites } from '../../hooks/hook-bus.js';
|
|
24
24
|
import { log } from '../../log.js';
|
|
25
|
+
import { onHarnessNoted } from '../../judgement/runs/drive-run.js';
|
|
25
26
|
/** Long enough to collapse a burst of verdicts, short enough that a human reads it as "live". */
|
|
26
27
|
const FLUSH_DEBOUNCE_MS = 3_000;
|
|
27
28
|
/**
|
|
@@ -55,10 +56,7 @@ export function attachDriveRunFlush(deps) {
|
|
|
55
56
|
log(`drive run flush failed: ${message}`);
|
|
56
57
|
}
|
|
57
58
|
};
|
|
58
|
-
const
|
|
59
|
-
const sessionId = 'sessionId' in payload ? payload.sessionId : undefined;
|
|
60
|
-
if (sessionId === undefined)
|
|
61
|
-
return;
|
|
59
|
+
const schedule = (sessionId) => {
|
|
62
60
|
const existing = pending.get(sessionId);
|
|
63
61
|
if (existing !== undefined)
|
|
64
62
|
clearTimeout(existing);
|
|
@@ -69,11 +67,28 @@ export function attachDriveRunFlush(deps) {
|
|
|
69
67
|
// obligation, and holding the process open for it would make `reticle` feel broken on exit.
|
|
70
68
|
timer.unref?.();
|
|
71
69
|
pending.set(sessionId, timer);
|
|
70
|
+
};
|
|
71
|
+
const off = onHook(HookEvent.VERDICT, (payload) => {
|
|
72
|
+
const sessionId = 'sessionId' in payload ? payload.sessionId : undefined;
|
|
73
|
+
if (sessionId !== undefined)
|
|
74
|
+
schedule(sessionId);
|
|
72
75
|
});
|
|
76
|
+
// A Harness drive is graded after its last verdict: rewrite the runs so the grade reaches them.
|
|
77
|
+
// ponytail: every live session, since a note names a drive, not a tab; drives end rarely.
|
|
78
|
+
const offNoted = onHarnessNoted(() => {
|
|
79
|
+
for (const session of deps.all?.() ?? [])
|
|
80
|
+
schedule(session.id);
|
|
81
|
+
});
|
|
82
|
+
const flushAll = async () => {
|
|
83
|
+
await Promise.all([...pending.keys()].map(flush));
|
|
84
|
+
};
|
|
85
|
+
const unregister = onPendingWrites(flushAll);
|
|
73
86
|
return {
|
|
74
87
|
flushNow: flush,
|
|
75
88
|
stop: () => {
|
|
76
89
|
off();
|
|
90
|
+
unregister();
|
|
91
|
+
offNoted();
|
|
77
92
|
for (const timer of pending.values())
|
|
78
93
|
clearTimeout(timer);
|
|
79
94
|
pending.clear();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { EventAttribution, JOURNAL_FILE_VERSION, } from '@reticlehq/core';
|
|
1
|
+
import { ActionType, EventAttribution, REDACTED_VALUE, JOURNAL_FILE_VERSION, } from '@reticlehq/core';
|
|
2
2
|
import { isSelfObservation } from './self-observation.js';
|
|
3
3
|
import { currentDrivenBy } from '../../hooks/driven-by.js';
|
|
4
|
+
import { callerClientName } from '../../hooks/coding-agents.js';
|
|
4
5
|
import { log } from '../../log.js';
|
|
5
6
|
/**
|
|
6
7
|
* Logged the FIRST time a journal write is lost, and never again for that recorder.
|
|
@@ -13,6 +14,36 @@ import { log } from '../../log.js';
|
|
|
13
14
|
* produce one line per batch.
|
|
14
15
|
*/
|
|
15
16
|
const JOURNAL_WRITE_LOST = 'journal_write_lost';
|
|
17
|
+
/** The actions whose arguments carry what was typed. */
|
|
18
|
+
const TYPING_ACTIONS = new Set([ActionType.FILL, ActionType.TYPE]);
|
|
19
|
+
const TYPED_KEYS = ['value', 'text'];
|
|
20
|
+
/**
|
|
21
|
+
* The arguments as the journal keeps them: every typed value replaced, at any depth (a sequence's
|
|
22
|
+
* steps too). A fill's arguments kept a typed password in clear in `actions.jsonl`; the page cannot
|
|
23
|
+
* tell the daemon which field was secret before the call is written, and nothing reads a typed value
|
|
24
|
+
* back from the journal — the run fold reads the ref, the target and the action — so none is kept.
|
|
25
|
+
*/
|
|
26
|
+
function withoutTypedInput(args) {
|
|
27
|
+
if (Array.isArray(args))
|
|
28
|
+
return args.map(withoutTypedInput);
|
|
29
|
+
if ('object' !== typeof args || null === args)
|
|
30
|
+
return args;
|
|
31
|
+
const record = args;
|
|
32
|
+
const typing = TYPING_ACTIONS.has(String(record['action']));
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [key, value] of Object.entries(record)) {
|
|
35
|
+
const typed = typing && TYPED_KEYS.includes(key) && 'string' === typeof value;
|
|
36
|
+
out[key] = typed ? REDACTED_VALUE : withoutTypedInput(value);
|
|
37
|
+
}
|
|
38
|
+
if (typing && 'object' === typeof record['args'] && null !== record['args']) {
|
|
39
|
+
const inner = { ...record['args'] };
|
|
40
|
+
for (const key of TYPED_KEYS)
|
|
41
|
+
if ('string' === typeof inner[key])
|
|
42
|
+
inner[key] = REDACTED_VALUE;
|
|
43
|
+
out['args'] = inner;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
16
47
|
const DEFAULT_FLUSH_AT = 64;
|
|
17
48
|
/**
|
|
18
49
|
* Owns action-window attribution and batched, order-preserving journaling — the durable half of the
|
|
@@ -66,7 +97,14 @@ export class JournalRecorder {
|
|
|
66
97
|
}
|
|
67
98
|
/** Open an attribution window. One action is active at a time (the agent drives sequentially). */
|
|
68
99
|
beginAction(actionId, tool, args) {
|
|
69
|
-
this.#active = {
|
|
100
|
+
this.#active = {
|
|
101
|
+
actionId,
|
|
102
|
+
tool,
|
|
103
|
+
args: withoutTypedInput(args),
|
|
104
|
+
tStart: this.#now(),
|
|
105
|
+
drivenBy: currentDrivenBy(),
|
|
106
|
+
agent: callerClientName(),
|
|
107
|
+
};
|
|
70
108
|
}
|
|
71
109
|
/** Close the active window: persist its buffered events, then the action record. No-op if none active. */
|
|
72
110
|
finishAction(effect, settled, settledInMs) {
|
|
@@ -88,6 +126,7 @@ export class JournalRecorder {
|
|
|
88
126
|
tRange: { from: active.tStart, to: this.#now() },
|
|
89
127
|
at: active.tStart,
|
|
90
128
|
...(active.drivenBy === undefined ? {} : { drivenBy: active.drivenBy }),
|
|
129
|
+
...(active.agent === undefined ? {} : { agent: active.agent }),
|
|
91
130
|
};
|
|
92
131
|
this.#enqueueFlush();
|
|
93
132
|
this.#chain = this.#chain
|
|
@@ -117,7 +156,7 @@ export class JournalRecorder {
|
|
|
117
156
|
v: JOURNAL_FILE_VERSION,
|
|
118
157
|
actionId,
|
|
119
158
|
tool,
|
|
120
|
-
args,
|
|
159
|
+
args: withoutTypedInput(args),
|
|
121
160
|
effect,
|
|
122
161
|
// No window was opened, so no event carries this id: `seqRange` is omitted rather than
|
|
123
162
|
// invented, because any range here would claim an attribution that never happened. `tRange` is
|
|
@@ -129,6 +168,10 @@ export class JournalRecorder {
|
|
|
129
168
|
const by = currentDrivenBy();
|
|
130
169
|
return by === undefined ? {} : { drivenBy: by };
|
|
131
170
|
})(),
|
|
171
|
+
...(() => {
|
|
172
|
+
const agent = callerClientName();
|
|
173
|
+
return agent === undefined ? {} : { agent };
|
|
174
|
+
})(),
|
|
132
175
|
};
|
|
133
176
|
this.#enqueueFlush();
|
|
134
177
|
this.#chain = this.#chain
|
|
@@ -13,6 +13,11 @@ export declare const DEFAULT_DIFF_RETENTION = 10;
|
|
|
13
13
|
export declare const DEFAULT_FEEDBACK_RETENTION = 20;
|
|
14
14
|
/** Harness plans kept on disk: the platform keeps every one, this machine the recent ones. */
|
|
15
15
|
export declare const DEFAULT_PLAN_RETENTION = 50;
|
|
16
|
+
/**
|
|
17
|
+
* Keep the step screenshots of at most this many runs. A run's are looked at while it is fresh, or
|
|
18
|
+
* on the platform it was synced to; the byte budget below is what bounds a single long one.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEFAULT_STEP_SHOT_RUN_RETENTION = 50;
|
|
16
21
|
/**
|
|
17
22
|
* The TOTAL the evidence tier of one `.reticle/` may occupy, across sessions, runs, overlay diffs
|
|
18
23
|
* and feedback copies together.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { basename, join } from 'node:path';
|
|
2
|
-
import { ReticleDir } from '@reticlehq/core';
|
|
2
|
+
import { ReticleDir, STEP_SCREENSHOT } from '@reticlehq/core';
|
|
3
3
|
import { reticleDirPaths, visualDir } from '../../project/dir/reticle-dir.js';
|
|
4
4
|
import { EVIDENCE_DIRS } from './workspace-tiers.js';
|
|
5
5
|
/** Keep at most this many session journals on disk; older ones are pruned by recency. */
|
|
@@ -16,6 +16,11 @@ export const DEFAULT_DIFF_RETENTION = 10;
|
|
|
16
16
|
export const DEFAULT_FEEDBACK_RETENTION = 20;
|
|
17
17
|
/** Harness plans kept on disk: the platform keeps every one, this machine the recent ones. */
|
|
18
18
|
export const DEFAULT_PLAN_RETENTION = 50;
|
|
19
|
+
/**
|
|
20
|
+
* Keep the step screenshots of at most this many runs. A run's are looked at while it is fresh, or
|
|
21
|
+
* on the platform it was synced to; the byte budget below is what bounds a single long one.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_STEP_SHOT_RUN_RETENTION = 50;
|
|
19
24
|
const DIFF_SUFFIX = '.diff.png';
|
|
20
25
|
const PNG_SUFFIX = '.png';
|
|
21
26
|
/**
|
|
@@ -173,7 +178,12 @@ export async function pruneVisualDiffs(fs, root, retention = DEFAULT_DIFF_RETENT
|
|
|
173
178
|
for (const dir of await visualDirs(fs, root)) {
|
|
174
179
|
await pruneByRecency(fs, dir, retention, isDiff);
|
|
175
180
|
}
|
|
181
|
+
// Step screenshots are evidence of one run each, read in the minutes after it or synced with it;
|
|
182
|
+
// the oldest runs' directories go whole.
|
|
183
|
+
await pruneByRecency(fs, stepShotRunsDir(root), DEFAULT_STEP_SHOT_RUN_RETENTION);
|
|
176
184
|
}
|
|
185
|
+
/** `visual/runs/`, the parent of every run's step screenshots. */
|
|
186
|
+
const stepShotRunsDir = (root) => join(root, ReticleDir.VISUAL_SUBDIR, STEP_SCREENSHOT.RUNS_DIR);
|
|
177
187
|
/** `visual/` plus its per-runtime subdirectories. Empty when nothing has been captured yet. */
|
|
178
188
|
async function visualDirs(fs, root) {
|
|
179
189
|
const top = visualDir(root);
|
|
@@ -181,7 +191,8 @@ async function visualDirs(fs, root) {
|
|
|
181
191
|
try {
|
|
182
192
|
// A per-runtime subdirectory is anything here that is not itself a PNG.
|
|
183
193
|
for (const entry of await fs.readdir(top)) {
|
|
184
|
-
|
|
194
|
+
// `runs/` holds step screenshots, not baselines, and is bounded on its own.
|
|
195
|
+
if (!entry.endsWith(PNG_SUFFIX) && STEP_SCREENSHOT.RUNS_DIR !== entry)
|
|
185
196
|
dirs.push(join(top, entry));
|
|
186
197
|
}
|
|
187
198
|
}
|
|
@@ -230,6 +241,20 @@ async function collectEvidence(fs, root) {
|
|
|
230
241
|
for (const under of EVIDENCE_DIRS) {
|
|
231
242
|
const isVisual = ReticleDir.VISUAL_SUBDIR === under;
|
|
232
243
|
const dirs = isVisual ? await visualDirs(fs, root) : [join(root, under)];
|
|
244
|
+
// Each run's step screenshots are one evidence entry, evicted whole, oldest first.
|
|
245
|
+
if (isVisual) {
|
|
246
|
+
const runs = stepShotRunsDir(root);
|
|
247
|
+
for (const name of await fs.readdir(runs).catch(() => [])) {
|
|
248
|
+
const path = join(runs, name);
|
|
249
|
+
try {
|
|
250
|
+
const { mtimeMs } = await fs.stat(path);
|
|
251
|
+
entries.push({ path, under, sizeBytes: await sizeOf(fs, path), mtimeMs });
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
/* unreadable entry — skip */
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
233
258
|
for (const dir of dirs) {
|
|
234
259
|
let names;
|
|
235
260
|
try {
|
|
@@ -56,6 +56,8 @@ export const WORKSPACE_TIERS = {
|
|
|
56
56
|
[ReticleDir.TIERS_FILE]: localFile,
|
|
57
57
|
// The user's request in their own words: it leaves only with `shareRequests`, never by git push.
|
|
58
58
|
[ReticleDir.REQUEST_FILE]: localFile,
|
|
59
|
+
// How to target this app, learned from corrected failures: a teammate's agent needs it too.
|
|
60
|
+
[ReticleDir.APP_NOTES_FILE]: sharedFile,
|
|
59
61
|
[ReticleDir.PLATFORM_MOMENTS_FILE]: localFile,
|
|
60
62
|
// Notes a human pinned in the HUD: free text about the page. The team reads them on the platform.
|
|
61
63
|
[ReticleDir.NOTES_FILE]: localFile,
|
|
@@ -62,7 +62,8 @@ interface SessionEndDeps {
|
|
|
62
62
|
* directories into `language/flows/recording/tape/` for a constant and two types was a dependency
|
|
63
63
|
* nobody needed. `directory-reach.test.ts` refused it, and its advice was right.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
/** The tape of THIS tab's steps: another app's drive is never saved into this project. */
|
|
66
|
+
takeAmbientTape?: (sessionId: string) => {
|
|
66
67
|
steps: readonly TapeStep[];
|
|
67
68
|
startPath?: string;
|
|
68
69
|
} | undefined;
|
|
@@ -120,4 +121,17 @@ export declare function makeSessionEnd(deps: SessionEndDeps): (session: SessionE
|
|
|
120
121
|
* dashboard.
|
|
121
122
|
*/
|
|
122
123
|
export declare function recordDriveRun(deps: SessionEndDeps, session: SessionEndTarget): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Tear down every session still open, and wait for it.
|
|
126
|
+
*
|
|
127
|
+
* Teardown is what saves a tab's driven flow and its last run, and it normally fires from the
|
|
128
|
+
* socket's close handler, unawaited. A daemon closing with tabs open terminated those sockets after
|
|
129
|
+
* its final sync, so the flow was written too late to be sent, or not at all once the process
|
|
130
|
+
* exited. Called before that final sync instead: each session leaves the registry first, so the
|
|
131
|
+
* socket closing later finds nothing to tear down a second time.
|
|
132
|
+
*/
|
|
133
|
+
export declare function endLiveSessions<S>(sessions: {
|
|
134
|
+
all: () => S[];
|
|
135
|
+
remove: (session: S) => boolean;
|
|
136
|
+
}, teardown: (session: S) => Promise<void>): Promise<void>;
|
|
123
137
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DRIVE_FLOW_PREFIX, driveFlowsFrom, } from './drive-flow.js';
|
|
1
|
+
import { DRIVE_FLOW_PREFIX, DriveFlowSkipReason, driveFlowsFrom, } from './drive-flow.js';
|
|
2
|
+
import { basename, dirname } from 'node:path';
|
|
2
3
|
import { log } from '../../log.js';
|
|
3
4
|
/** What a session's teardown writes to the daemon log when a by-product fails to save. */
|
|
4
5
|
const SESSION_END_LOG = {
|
|
@@ -7,13 +8,13 @@ const SESSION_END_LOG = {
|
|
|
7
8
|
};
|
|
8
9
|
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
9
10
|
import { journalActionsPath, sessionDirPath } from '../project/dir/reticle-dir.js';
|
|
10
|
-
import { asSessionId } from '@reticlehq/core';
|
|
11
|
+
import { asRunId, asSessionId } from '@reticlehq/core';
|
|
11
12
|
import { OnboardingPhase, OnboardingStepStatus, } from '@reticlehq/core/telemetry';
|
|
12
13
|
import { AmbientStore } from './ambient-store.js';
|
|
13
14
|
import { subjectOf } from '@reticlehq/core';
|
|
14
15
|
import { pruneWorkspace } from './on-disk/startup-maintenance.js';
|
|
15
16
|
import { buildVerificationRun } from '../../judgement/runs/artifact/build-verification-run.js';
|
|
16
|
-
import { driveRunId, driveRunsFrom } from '../../judgement/runs/drive-run.js';
|
|
17
|
+
import { agentEndClaims, agentEndCounts, driveRunId, driveRunsFrom, } from '../../judgement/runs/drive-run.js';
|
|
17
18
|
import { RunStore } from '../../judgement/runs/artifact/run-store.js';
|
|
18
19
|
/**
|
|
19
20
|
* Build the teardown handler the bridge fires when a session is removed. Flushes the journal first (so no
|
|
@@ -173,6 +174,7 @@ export async function recordDriveRun(deps, session) {
|
|
|
173
174
|
if (session.readJournalActions === undefined)
|
|
174
175
|
return;
|
|
175
176
|
const actions = await session.readJournalActions();
|
|
177
|
+
const root = session.artifactRoot ?? deps.reticleRoot;
|
|
176
178
|
const inputs = driveRunsFrom(actions, {
|
|
177
179
|
// Derived from the session, NOT random. Teardown fires on every socket close, and a reconnecting
|
|
178
180
|
// tab keeps its session id and appends to the same ledger — so a drive across two page reloads
|
|
@@ -180,7 +182,11 @@ export async function recordDriveRun(deps, session) {
|
|
|
180
182
|
// the first. A stable id makes the artifact idempotent: the same session rewrites its own run,
|
|
181
183
|
// and the cloud diffs by runId, so a re-push supersedes rather than duplicates.
|
|
182
184
|
runId: driveRunId(session.id),
|
|
185
|
+
// Each agent session in the tab, as `reticle_session {end}` cut them, is its own run.
|
|
186
|
+
agentEnds: agentEndCounts(session.id),
|
|
187
|
+
agentClaims: agentEndClaims(session.id),
|
|
183
188
|
...(session.projectId === undefined ? {} : { projectId: session.projectId }),
|
|
189
|
+
projectName: basename(dirname(root)),
|
|
184
190
|
});
|
|
185
191
|
if (0 === inputs.length)
|
|
186
192
|
return;
|
|
@@ -196,13 +202,18 @@ export async function recordDriveRun(deps, session) {
|
|
|
196
202
|
currentDocumentId: session.currentDocumentId,
|
|
197
203
|
currentEditEpoch: session.currentEditEpoch,
|
|
198
204
|
});
|
|
199
|
-
const store = new RunStore(deps.fs,
|
|
205
|
+
const store = new RunStore(deps.fs, root,
|
|
200
206
|
// Tell the sync daemon a run landed, rather than letting it find out on its next tick. Teardown
|
|
201
207
|
// used to write this artifact with no callback at all, so the evidence from a whole session sat
|
|
202
208
|
// on disk until a timer noticed — which, for the last session of the day, could be never.
|
|
203
209
|
deps.onRunPersisted === undefined ? undefined : { onWrote: deps.onRunPersisted });
|
|
210
|
+
const clock = deps.now ?? (() => Date.now());
|
|
204
211
|
for (const input of inputs) {
|
|
205
|
-
|
|
212
|
+
// A reload rewrites this session's run under the same id; the first stamp is when it ran, and
|
|
213
|
+
// restamping on every rewrite showed the tab's close as the drive's time.
|
|
214
|
+
const held = await store.read(asRunId(input.runId));
|
|
215
|
+
const firstAt = held.ok ? held.run.createdAt : undefined;
|
|
216
|
+
await store.write(buildVerificationRun({ ...input, ...(subject === undefined ? {} : { subject }) }, firstAt === undefined ? clock : () => firstAt));
|
|
206
217
|
}
|
|
207
218
|
}
|
|
208
219
|
/** Persist what this session drove as a replayable flow, when it declared anything provable. */
|
|
@@ -212,7 +223,7 @@ async function saveDrivenFlow(deps, session) {
|
|
|
212
223
|
if (takeTape === undefined || flows === undefined)
|
|
213
224
|
return;
|
|
214
225
|
// One flow per journey the session contained, not one flow per session — see drive-flow.ts.
|
|
215
|
-
const { programs, outcome } = driveFlowsFrom(takeTape());
|
|
226
|
+
const { programs, outcome } = driveFlowsFrom(takeTape(session.id));
|
|
216
227
|
const author = deps.author?.();
|
|
217
228
|
for (const program of programs) {
|
|
218
229
|
// Folded into any saved copy of the same journey, so each session does not add a near-copy.
|
|
@@ -228,7 +239,13 @@ async function saveDrivenFlow(deps, session) {
|
|
|
228
239
|
phase: OnboardingPhase.FIRST_RUN,
|
|
229
240
|
step: 'flow_recorded',
|
|
230
241
|
status: 0 === programs.length ? OnboardingStepStatus.SKIPPED : OnboardingStepStatus.COMPLETED,
|
|
231
|
-
...(outcome.unprovenSteps === undefined
|
|
242
|
+
...(outcome.unprovenSteps === undefined
|
|
243
|
+
? {}
|
|
244
|
+
: { reason: DriveFlowSkipReason.NO_DECLARED_CONSEQUENCE }),
|
|
245
|
+
// Not saved, with the reason: proved against mocked responses, which replay cannot see (#1459).
|
|
246
|
+
...(0 < programs.length || outcome.mockedSteps === undefined
|
|
247
|
+
? {}
|
|
248
|
+
: { reason: DriveFlowSkipReason.DRIVEN_UNDER_NETWORK_MOCKS }),
|
|
232
249
|
});
|
|
233
250
|
}
|
|
234
251
|
/**
|
|
@@ -249,3 +266,17 @@ async function dropUndrivenJournal(fs, root, sessionId) {
|
|
|
249
266
|
// tidying is never the reason a teardown fails
|
|
250
267
|
}
|
|
251
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Tear down every session still open, and wait for it.
|
|
271
|
+
*
|
|
272
|
+
* Teardown is what saves a tab's driven flow and its last run, and it normally fires from the
|
|
273
|
+
* socket's close handler, unawaited. A daemon closing with tabs open terminated those sockets after
|
|
274
|
+
* its final sync, so the flow was written too late to be sent, or not at all once the process
|
|
275
|
+
* exited. Called before that final sync instead: each session leaves the registry first, so the
|
|
276
|
+
* socket closing later finds nothing to tear down a second time.
|
|
277
|
+
*/
|
|
278
|
+
export async function endLiveSessions(sessions, teardown) {
|
|
279
|
+
const ending = sessions.all().filter((session) => sessions.remove(session));
|
|
280
|
+
// Teardown is best-effort everywhere it runs: one that fails must not stop the others.
|
|
281
|
+
await Promise.all(ending.map((session) => teardown(session).catch(() => undefined)));
|
|
282
|
+
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* what lets a test say "these are the projects on this machine" without one.
|
|
19
19
|
*/
|
|
20
20
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
21
|
-
import { dirname, join } from 'node:path';
|
|
21
|
+
import { dirname, isAbsolute, join, relative } from 'node:path';
|
|
22
22
|
import { homedir } from 'node:os';
|
|
23
23
|
import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, } from '@reticlehq/core/artifacts';
|
|
24
24
|
import { DevServerEntrySchema, ReticleDir, ReticleEnv, devServerRegistryPort, } from '@reticlehq/core';
|
|
@@ -60,7 +60,19 @@ function daemonSitsInAProject(daemonRoot) {
|
|
|
60
60
|
*/
|
|
61
61
|
function daemonOwnProjectId(daemonRoot, candidates) {
|
|
62
62
|
const directory = dirname(daemonRoot);
|
|
63
|
-
|
|
63
|
+
const own = candidates.find((candidate) => candidate.directory === directory)?.projectId;
|
|
64
|
+
// A monorepo root carries the id of the app `init --app` wired, as a pointer for an agent started
|
|
65
|
+
// there. When an app inside the root declares the same id, the root is pointing at it, not being
|
|
66
|
+
// it: answering with the id handed a second checkout's flows to this root with no warning.
|
|
67
|
+
const pointer = candidates.some((candidate) => candidate.projectId === own &&
|
|
68
|
+
candidate.directory !== directory &&
|
|
69
|
+
isInside(directory, candidate.directory));
|
|
70
|
+
return pointer ? undefined : own;
|
|
71
|
+
}
|
|
72
|
+
/** Is `dir` strictly inside `root`? */
|
|
73
|
+
function isInside(root, dir) {
|
|
74
|
+
const rel = relative(root, dir);
|
|
75
|
+
return 0 < rel.length && !rel.startsWith('..') && !isAbsolute(rel);
|
|
64
76
|
}
|
|
65
77
|
/**
|
|
66
78
|
* Every project this machine knows the directory of: discovered `.reticle.json` files first, then
|
|
@@ -150,6 +162,16 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
150
162
|
const daemonProjectId = daemonIsProject
|
|
151
163
|
? daemonOwnProjectId(daemonRoot, candidates())
|
|
152
164
|
: undefined;
|
|
165
|
+
/** Said once per project, reason and folder: one session's calls repeated it 16 times in 3s. */
|
|
166
|
+
const told = new Set();
|
|
167
|
+
// ponytail: asked once per origin (it shells out); a dev server moved to another folder on the
|
|
168
|
+
// same port is named stale until the daemon restarts.
|
|
169
|
+
const servedFrom = new Map();
|
|
170
|
+
const servingOf = (origin) => {
|
|
171
|
+
if (!servedFrom.has(origin))
|
|
172
|
+
servedFrom.set(origin, servingDirectory(origin));
|
|
173
|
+
return servedFrom.get(origin);
|
|
174
|
+
};
|
|
153
175
|
return (projectId, origin) => {
|
|
154
176
|
const known = candidates();
|
|
155
177
|
let resolved = resolveArtifactRoot({ projectId, candidates: known, daemonRoot });
|
|
@@ -161,7 +183,7 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
161
183
|
projectId,
|
|
162
184
|
candidates: known,
|
|
163
185
|
daemonRoot,
|
|
164
|
-
servingDirectory:
|
|
186
|
+
servingDirectory: servingOf(origin),
|
|
165
187
|
});
|
|
166
188
|
}
|
|
167
189
|
if (resolved.reason === ArtifactRootReason.MATCHED_PROJECT)
|
|
@@ -182,7 +204,9 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
182
204
|
// app's learned expectations become another's.
|
|
183
205
|
...(origin === undefined ? {} : { origin }),
|
|
184
206
|
});
|
|
185
|
-
|
|
207
|
+
const key = `${projectId ?? ''}\u0000${resolved.reason}\u0000${root}`;
|
|
208
|
+
if (root !== daemonRoot && !told.has(key)) {
|
|
209
|
+
told.add(key);
|
|
186
210
|
log('artifact_root_unmatched', {
|
|
187
211
|
projectId: projectId ?? null,
|
|
188
212
|
// The other half of the comparison, so a reader can tell "the daemon was a guest" from
|
|
@@ -190,8 +214,12 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
190
214
|
daemonProjectId: daemonProjectId ?? null,
|
|
191
215
|
reason: resolved.reason,
|
|
192
216
|
root,
|
|
217
|
+
...(resolved.candidates === undefined ? {} : { candidates: resolved.candidates }),
|
|
193
218
|
});
|
|
194
219
|
}
|
|
195
|
-
|
|
220
|
+
// Named so the agent is told WHICH checkout's work is stranded: a clone serving the app was
|
|
221
|
+
// never named, only the primary checkout's folders.
|
|
222
|
+
const serving = origin === undefined ? undefined : servingOf(origin);
|
|
223
|
+
return { ...resolved, root, ...(serving === undefined ? {} : { serving }) };
|
|
196
224
|
};
|
|
197
225
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
|
|
1
2
|
import { type ProjectCandidate, type ProjectId, type ProjectRegistry } from '@reticlehq/core';
|
|
2
3
|
import type { ConfigDiscovery } from '../../command/cli/config/config-discovery.js';
|
|
3
4
|
/**
|
|
@@ -60,6 +61,8 @@ export interface ArtifactRoot {
|
|
|
60
61
|
* needs to name them; a caller that does not can ignore the field.
|
|
61
62
|
*/
|
|
62
63
|
candidates?: string[];
|
|
64
|
+
/** The folder whose dev server served the page, when the evidence went unmatched and it is known. */
|
|
65
|
+
serving?: string;
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
65
68
|
* Resolve the artifact root for one session.
|
|
@@ -79,8 +82,7 @@ export declare function projectCandidatesFrom(discovery: ConfigDiscovery, regist
|
|
|
79
82
|
projectId?: string | undefined;
|
|
80
83
|
root: string;
|
|
81
84
|
}[]): ProjectCandidate[];
|
|
82
|
-
|
|
83
|
-
export declare const UNMATCHED_SUBDIR = "unmatched";
|
|
85
|
+
export { UNMATCHED_SUBDIR };
|
|
84
86
|
/**
|
|
85
87
|
* The fallback root for a session whose project could not be resolved.
|
|
86
88
|
*
|
|
@@ -143,4 +145,3 @@ export declare function unmatchedRoot(query: {
|
|
|
143
145
|
*/
|
|
144
146
|
origin?: string | undefined;
|
|
145
147
|
}): string;
|
|
146
|
-
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
2
|
+
import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
|
|
2
3
|
import { SAFE_SEGMENT_PATTERN, ReticleDir, fnv1a, projectCandidates, } from '@reticlehq/core';
|
|
3
4
|
/**
|
|
4
5
|
* Which `.reticle/` a session's artifacts belong in.
|
|
@@ -130,8 +131,7 @@ export function projectCandidatesFrom(discovery, registry, devServers = []) {
|
|
|
130
131
|
: [{ projectId: server.projectId, directory: server.root }]);
|
|
131
132
|
return [...known, ...announced];
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
-
export const UNMATCHED_SUBDIR = 'unmatched';
|
|
134
|
+
export { UNMATCHED_SUBDIR };
|
|
135
135
|
/** What a project with NO identity at all is called on disk. Never blank, so the path is always real. */
|
|
136
136
|
const UNNAMED_PROJECT = 'unnamed';
|
|
137
137
|
/**
|
|
@@ -41,6 +41,15 @@ export interface ReticleDirPaths {
|
|
|
41
41
|
plans: string;
|
|
42
42
|
}
|
|
43
43
|
export declare function reticleDirPaths(root: string): ReticleDirPaths;
|
|
44
|
+
/**
|
|
45
|
+
* A Harness drive's own record beside its run —.reticle/runs/<runId>.drive.json. Not a run: every
|
|
46
|
+
* reader that counts or syncs run files skips it.
|
|
47
|
+
*/
|
|
48
|
+
export declare const DRIVE_RECORD_SUFFIX = ".drive.json";
|
|
49
|
+
/** Where evidence goes when no project could be named: `~/.reticle/unmatched/<id>`. */
|
|
50
|
+
export declare const UNMATCHED_SUBDIR = "unmatched";
|
|
51
|
+
/** Whether a root is one of those buckets: evidence from a page that never said its project. */
|
|
52
|
+
export declare function isUnmatchedRoot(root: string): boolean;
|
|
44
53
|
/** The verification-run artifact path for `runId`.reticle/runs/<runId>.json). */
|
|
45
54
|
export declare function runPath(root: string, runId: RunId): string;
|
|
46
55
|
/** The journal directory for `sessionId` (.reticle/sessions/<id>). Guard the id first. */
|
|
@@ -88,6 +97,11 @@ export declare function visualPath(root: string, name: string, runtime?: string)
|
|
|
88
97
|
* every pre-existing baseline came from a driven browser, and moving them would orphan them.
|
|
89
98
|
*/
|
|
90
99
|
export declare function visualDir(root: string, runtime?: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Where one run's step screenshots live: `.reticle/visual/runs/<runId>/`. Throws on an unsafe run id,
|
|
102
|
+
* like every other path built from one.
|
|
103
|
+
*/
|
|
104
|
+
export declare function stepShotDir(root: string, runId: string): string;
|
|
91
105
|
/** The overlay-diff PNG path for `name`.reticle/visual/<name>.diff.png). */
|
|
92
106
|
export declare function visualDiffPath(root: string, name: string, runtime?: string): string;
|
|
93
107
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { dirname, join } from 'node:path';
|
|
1
|
+
import { basename, dirname, join } from 'node:path';
|
|
2
2
|
import { realmOf } from '@reticlehq/core';
|
|
3
|
-
import { CONTRACT_FILE_VERSION, ContractFileSchema, ContractReadError, FLOW_NAME_PATTERN, SAFE_SEGMENT_PATTERN, ReticleDir, } from '@reticlehq/core';
|
|
3
|
+
import { CONTRACT_FILE_VERSION, ContractFileSchema, ContractReadError, FLOW_NAME_PATTERN, SAFE_SEGMENT_PATTERN, ReticleDir, STEP_SCREENSHOT, } from '@reticlehq/core';
|
|
4
4
|
export function reticleDirPaths(root) {
|
|
5
5
|
return {
|
|
6
6
|
root,
|
|
@@ -24,6 +24,18 @@ export function reticleDirPaths(root) {
|
|
|
24
24
|
plans: join(root, ReticleDir.PLANS_SUBDIR),
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* A Harness drive's own record beside its run —.reticle/runs/<runId>.drive.json. Not a run: every
|
|
29
|
+
* reader that counts or syncs run files skips it.
|
|
30
|
+
*/
|
|
31
|
+
export const DRIVE_RECORD_SUFFIX = '.drive.json';
|
|
32
|
+
/** Where evidence goes when no project could be named: `~/.reticle/unmatched/<id>`. */
|
|
33
|
+
export const UNMATCHED_SUBDIR = 'unmatched';
|
|
34
|
+
/** Whether a root is one of those buckets: evidence from a page that never said its project. */
|
|
35
|
+
export function isUnmatchedRoot(root) {
|
|
36
|
+
const parent = dirname(root);
|
|
37
|
+
return UNMATCHED_SUBDIR === basename(parent) && ReticleDir.ROOT === basename(dirname(parent));
|
|
38
|
+
}
|
|
27
39
|
/** The verification-run artifact path for `runId`.reticle/runs/<runId>.json). */
|
|
28
40
|
export function runPath(root, runId) {
|
|
29
41
|
return join(root, ReticleDir.RUNS_SUBDIR, `${runId}.json`);
|
|
@@ -92,6 +104,15 @@ export function visualDir(root, runtime) {
|
|
|
92
104
|
? join(visual, runtime)
|
|
93
105
|
: visual;
|
|
94
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Where one run's step screenshots live: `.reticle/visual/runs/<runId>/`. Throws on an unsafe run id,
|
|
109
|
+
* like every other path built from one.
|
|
110
|
+
*/
|
|
111
|
+
export function stepShotDir(root, runId) {
|
|
112
|
+
if (!isValidRunId(runId))
|
|
113
|
+
throw new Error(`invalid run id: ${JSON.stringify(runId)}`);
|
|
114
|
+
return join(root, ReticleDir.VISUAL_SUBDIR, STEP_SCREENSHOT.RUNS_DIR, runId);
|
|
115
|
+
}
|
|
95
116
|
/** The overlay-diff PNG path for `name`.reticle/visual/<name>.diff.png). */
|
|
96
117
|
export function visualDiffPath(root, name, runtime) {
|
|
97
118
|
return join(visualDir(root, runtime), `${name}.diff.png`);
|