@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
|
@@ -6,72 +6,325 @@
|
|
|
6
6
|
* word for it is not evidence, so after every drive the harness checks each goal itself, with the
|
|
7
7
|
* same assert any agent would make, and reports one verdict per goal.
|
|
8
8
|
*/
|
|
9
|
-
import { PredicateKind, ReticleTool, Verified, asRecord } from '@reticlehq/core';
|
|
9
|
+
import { PredicateKind, QueryBy, ReticleTool, Verified, VerifiedReason, asRecord, } from '@reticlehq/core';
|
|
10
10
|
/** More than this and the persona is a document, not a set of goals. */
|
|
11
11
|
const MAX_GOALS = 10;
|
|
12
12
|
const MAX_GOAL_LENGTH = 200;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
/**
|
|
14
|
+
* A string the persona quoted: "straight" or “curly”. Single quotes are apostrophes too often.
|
|
15
|
+
*
|
|
16
|
+
* A straight quote opens only after a space, a bracket or the start, and closes only before one or
|
|
17
|
+
* before sentence punctuation, so a goal that quotes text with quotes in it — the issue title
|
|
18
|
+
* `"Check failed: an element matching {"testid":"pay"}"` — stays one goal. Matching any `"` split
|
|
19
|
+
* that title into `{`, `:` and `}`, and graded the fragments.
|
|
20
|
+
*/
|
|
21
|
+
const QUOTED = /(?<![^\s([])"([^\n]{1,200}?)"(?![^\s.,;!?)\]])|“([^”\n]{1,200})”/g;
|
|
22
|
+
/**
|
|
23
|
+
* A negation in the same clause just before a quote: "must not show "payments"". The window stops
|
|
24
|
+
* at the previous quote or clause end, so `doesn't show "X" until you click "Y"` negates X only. A
|
|
25
|
+
* comma ends the clause too: "place the order without the coupon, ending on "Order confirmed"" read
|
|
26
|
+
* its end as text that must be absent, and the journey failed with no goal to reach.
|
|
27
|
+
*/
|
|
28
|
+
const NEGATED_BEFORE = /(?:\b(?:not|never|no|nor|without|no longer)|n't)\b[^"“”.,;!?]{0,40}$/i;
|
|
29
|
+
/** Said of a goal the page showed before the drive did anything. */
|
|
30
|
+
const ALREADY_SHOWN = `${VerifiedReason.ALREADY_TRUE}: it was on the page before the drive started, so seeing it proves nothing`;
|
|
31
|
+
/**
|
|
32
|
+
* Said of a goal the page the drive ended on never showed, even scrolled. Undecided, never `no`: the
|
|
33
|
+
* drive may not have reached the page that shows it, and a "no" there blamed the app for where the
|
|
34
|
+
* Harness stopped.
|
|
35
|
+
*/
|
|
36
|
+
const NOT_REACHED = `${VerifiedReason.INCONCLUSIVE}: not on the page the drive ended on, even scrolled; the drive may not have reached the page that shows it`;
|
|
37
|
+
/** Said of a goal the page showed when looked at, which the text check nonetheless did not find. */
|
|
38
|
+
const SHOWN_NOT_HELD = `${VerifiedReason.INCONCLUSIVE}: shown on the page when looked at, but the text check did not find it (a field's value, or text it cannot read)`;
|
|
39
|
+
/** Said of an absence goal beside a goal the drive never reached: absent from the wrong page proves nothing. */
|
|
40
|
+
const ABSENCE_UNREACHED = `${VerifiedReason.INCONCLUSIVE}: the drive did not reach the page it ends on, so its absence there proves nothing`;
|
|
41
|
+
/** How the snapshot and the element reader end a text they cut short. */
|
|
42
|
+
const CUT = '…';
|
|
43
|
+
const collapse = (text) => text.replace(/\s+/g, ' ').trim();
|
|
44
|
+
/**
|
|
45
|
+
* Whether `shown` — one element's text, maybe cut short with "…" — is text the Harness wrote itself
|
|
46
|
+
* rather than the app: one of `own` (its journey, the names of the plan and the flows it saved), in
|
|
47
|
+
* full or cut short, holding `goal`. A Harness asked to rename a plan stopped on the /plans list and
|
|
48
|
+
* was graded PROVED on every quote: the plan it had just saved was named after the journey, and the
|
|
49
|
+
* journey quoted them all. An element showing exactly the goal is the app's, whoever else wrote it.
|
|
50
|
+
*/
|
|
51
|
+
export function writtenByHarness(shown, goal, own) {
|
|
52
|
+
const full = collapse(shown);
|
|
53
|
+
const cut = full.endsWith(CUT);
|
|
54
|
+
const text = cut ? full.slice(0, -CUT.length).trimEnd() : full;
|
|
55
|
+
const wanted = collapse(goal);
|
|
56
|
+
if (text.length <= wanted.length)
|
|
57
|
+
return false;
|
|
58
|
+
return own.some((o) => {
|
|
59
|
+
const mine = collapse(o);
|
|
60
|
+
if (0 === mine.length || !mine.includes(wanted))
|
|
61
|
+
return false;
|
|
62
|
+
if (cut && mine.startsWith(text))
|
|
63
|
+
return true;
|
|
64
|
+
// Its own text first ("<journey> and 5 more"): what follows it is not the Harness's.
|
|
65
|
+
return text.startsWith(mine) && !text.slice(mine.length).includes(wanted);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Whether a snapshot shows `goal` anywhere but inside text the Harness wrote itself. Read as text,
|
|
70
|
+
* not parsed: the journey quotes its goals, so its quotes cut a parsed name short.
|
|
71
|
+
*/
|
|
72
|
+
function shownByApp(tree, goal, own) {
|
|
73
|
+
const wanted = collapse(goal);
|
|
74
|
+
const mine = own.map(collapse).filter((o) => wanted.length < o.length && o.includes(wanted));
|
|
75
|
+
if (0 === mine.length)
|
|
76
|
+
return tree.includes(goal);
|
|
77
|
+
return tree.split('\n').some((line) => {
|
|
78
|
+
let rest = collapse(line);
|
|
79
|
+
for (const o of mine) {
|
|
80
|
+
rest = rest.split(o).join('');
|
|
81
|
+
rest = withoutCutStart(rest, o, wanted.length);
|
|
82
|
+
}
|
|
83
|
+
return rest.includes(wanted);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** `line` without each start of `own` the snapshot cut short with "…" (longer than `min`). */
|
|
87
|
+
function withoutCutStart(line, own, min) {
|
|
88
|
+
let rest = line;
|
|
89
|
+
for (let at = rest.indexOf(CUT); -1 !== at; at = rest.indexOf(CUT, at + 1)) {
|
|
90
|
+
const before = rest.slice(0, at);
|
|
91
|
+
for (let n = Math.min(own.length, before.length); min < n; n -= 1) {
|
|
92
|
+
if (!before.endsWith(own.slice(0, n)))
|
|
93
|
+
continue;
|
|
94
|
+
rest = `${rest.slice(0, at - n)}${rest.slice(at + CUT.length)}`;
|
|
95
|
+
at -= n;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return rest;
|
|
100
|
+
}
|
|
101
|
+
/** The end state a goal quotes: its last quoted text that it does not negate. */
|
|
102
|
+
export function endOf(goal) {
|
|
103
|
+
return parseGoals(goal)
|
|
104
|
+
.filter((g) => true !== g.absent)
|
|
105
|
+
.at(-1)?.text;
|
|
106
|
+
}
|
|
107
|
+
/** The observable goals in a persona: whatever it quoted, each marked absent when it was negated. */
|
|
108
|
+
export function parseGoals(persona) {
|
|
17
109
|
if (persona === undefined)
|
|
18
110
|
return [];
|
|
19
|
-
const
|
|
20
|
-
|
|
111
|
+
const out = new Map();
|
|
112
|
+
for (const m of persona.matchAll(QUOTED)) {
|
|
113
|
+
const text = (m[1] ?? m[2] ?? '').trim();
|
|
114
|
+
if (0 === text.length || out.has(text))
|
|
115
|
+
continue;
|
|
116
|
+
const absent = NEGATED_BEFORE.test(persona.slice(0, m.index));
|
|
117
|
+
out.set(text, absent ? { text, absent } : { text });
|
|
118
|
+
}
|
|
119
|
+
return [...out.values()].slice(0, MAX_GOALS);
|
|
120
|
+
}
|
|
121
|
+
/** The texts a persona says must be on the page: the quoted goals it did not negate. */
|
|
122
|
+
export function goalsIn(persona) {
|
|
123
|
+
return parseGoals(persona).flatMap((g) => (true === g.absent ? [] : [g.text]));
|
|
21
124
|
}
|
|
22
125
|
/**
|
|
23
126
|
* Check each goal on the page the drive ended on, recording ONE assert for the drive.
|
|
24
127
|
*
|
|
25
128
|
* Each quote used to be its own assert, and every assert is a check in the drive's run, so a goal
|
|
26
129
|
* that quotes where it starts ("from "Count is 0" to "Count is 1"") recorded a "no" for the start
|
|
27
|
-
* text a working app had rightly replaced, and the run synced as a failing flow. The page is
|
|
28
|
-
* once
|
|
29
|
-
*
|
|
30
|
-
*
|
|
130
|
+
* text a working app had rightly replaced, and the run synced as a failing flow. The page is let
|
|
131
|
+
* settle, read once, and each goal is scrolled to by its text — one below the fold read `unknown`
|
|
132
|
+
* on a drive that never scrolled. The one recorded assert covers the goals found. A goal not found
|
|
133
|
+
* is undecided, not failed: nothing here knows the drive reached the page that would show it. A
|
|
134
|
+
* check that cannot run is `unknown`, never skipped.
|
|
31
135
|
*/
|
|
32
|
-
export async function checkGoals(invoke, goals
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
136
|
+
export async function checkGoals(invoke, goals,
|
|
137
|
+
/** The page as it read before the drive: a goal it already showed proves nothing at the end. */
|
|
138
|
+
opening,
|
|
139
|
+
/** Text the Harness wrote itself (its journey, its plan's and flows' names): never evidence. */
|
|
140
|
+
own = []) {
|
|
141
|
+
const byText = new Map();
|
|
142
|
+
for (const goal of goals.slice(0, MAX_GOALS)) {
|
|
143
|
+
const g = 'string' === typeof goal ? { text: goal } : goal;
|
|
144
|
+
const text = g.text.trim().slice(0, MAX_GOAL_LENGTH);
|
|
145
|
+
if (0 < text.length && !byText.has(text))
|
|
146
|
+
byText.set(text, { ...g, text });
|
|
147
|
+
}
|
|
148
|
+
const all = [...byText.values()];
|
|
149
|
+
const before = new Set(all.filter((g) => true !== g.absent && true === opening?.includes(g.text)).map((g) => g.text));
|
|
150
|
+
const unchecked = (g, reason) => ({
|
|
151
|
+
text: g.text,
|
|
152
|
+
...(true === g.absent ? { absent: true } : {}),
|
|
153
|
+
verified: Verified.UNKNOWN,
|
|
154
|
+
reason,
|
|
155
|
+
});
|
|
156
|
+
const todo = all.filter((g) => !before.has(g.text));
|
|
157
|
+
if (0 === todo.length)
|
|
158
|
+
return all.map((g) => unchecked(g, ALREADY_SHOWN));
|
|
159
|
+
await settle(invoke);
|
|
160
|
+
const page = await readOpening(invoke);
|
|
161
|
+
const found = new Set();
|
|
162
|
+
for (const g of todo) {
|
|
163
|
+
if (true === g.absent)
|
|
164
|
+
continue;
|
|
165
|
+
if ((await scrolledTo(invoke, g.text, own)) || shownByApp(page ?? '', g.text, own))
|
|
166
|
+
found.add(g.text);
|
|
167
|
+
}
|
|
168
|
+
const unreached = todo.some((g) => true !== g.absent && !found.has(g.text));
|
|
169
|
+
const absences = unreached ? [] : todo.filter((g) => true === g.absent);
|
|
170
|
+
const recorded = [...todo.filter((g) => found.has(g.text)), ...absences];
|
|
171
|
+
const verified = 0 < recorded.length ? await assertGoals(invoke, recorded) : Verified.UNKNOWN;
|
|
172
|
+
// One joint check that failed says only that SOMETHING did not hold: each goal is asked on its own,
|
|
173
|
+
// so a goal that held is not graded "no" for a neighbour that did not.
|
|
174
|
+
const each = new Map();
|
|
175
|
+
if (Verified.NO === verified && 1 < recorded.length)
|
|
176
|
+
for (const g of recorded)
|
|
177
|
+
each.set(g.text, await assertGoals(invoke, [g]));
|
|
178
|
+
const verdictOf = (g) => each.get(g.text) ?? verified;
|
|
179
|
+
return all.map((g) => {
|
|
180
|
+
if (before.has(g.text))
|
|
181
|
+
return unchecked(g, ALREADY_SHOWN);
|
|
182
|
+
if (true === g.absent) {
|
|
183
|
+
return unreached
|
|
184
|
+
? unchecked(g, ABSENCE_UNREACHED)
|
|
185
|
+
: { text: g.text, absent: true, verified: verdictOf(g) };
|
|
186
|
+
}
|
|
187
|
+
if (!found.has(g.text))
|
|
188
|
+
return unchecked(g, NOT_REACHED);
|
|
189
|
+
// Seen on the page yet the text check says no: a field's value, or text the check cannot read.
|
|
190
|
+
return Verified.NO === verdictOf(g)
|
|
191
|
+
? unchecked(g, SHOWN_NOT_HELD)
|
|
192
|
+
: { text: g.text, verified: verdictOf(g) };
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/** The verdict of one assert over `goals`, or unknown when it could not be asked. */
|
|
196
|
+
async function assertGoals(invoke, goals) {
|
|
197
|
+
try {
|
|
198
|
+
const result = asRecord(await invoke(ReticleTool.ASSERT, { predicate: allOf(goals) }));
|
|
199
|
+
return 'string' === typeof result['verified'] ? result['verified'] : Verified.UNKNOWN;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return Verified.UNKNOWN;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Whether the page shows `text` once it has settled, scrolled to if need be — how a person looks
|
|
207
|
+
* for something before saying it is not there. Records no check.
|
|
208
|
+
*/
|
|
209
|
+
export async function lookFor(invoke, text,
|
|
210
|
+
/** Text the Harness wrote itself: a match inside it is not the page showing the goal. */
|
|
211
|
+
own = []) {
|
|
212
|
+
await settle(invoke);
|
|
213
|
+
if (shownByApp((await readOpening(invoke)) ?? '', text, own))
|
|
214
|
+
return true;
|
|
215
|
+
return scrolledTo(invoke, text, own);
|
|
216
|
+
}
|
|
217
|
+
/** Long enough for a lazy route to render; short enough that a page that never quiets costs little. */
|
|
218
|
+
const SETTLE_MS = 3000;
|
|
219
|
+
/** Scrolls spent looking for one goal: a long page, not an endless feed. */
|
|
220
|
+
const GOAL_SCROLLS = 10;
|
|
221
|
+
/** Let a page that is still changing finish, so a view still swapping in is not read half-drawn. */
|
|
222
|
+
async function settle(invoke) {
|
|
223
|
+
try {
|
|
224
|
+
await invoke(ReticleTool.WAIT_FOR, {
|
|
225
|
+
predicate: { kind: PredicateKind.SETTLED },
|
|
226
|
+
timeout_ms: SETTLE_MS,
|
|
227
|
+
// From the session's start, not from this call: a request the drive's last click opened is
|
|
228
|
+
// in flight BEFORE this wait begins, and a window that starts now cannot see it. Driven over
|
|
229
|
+
// MCP: the drive ended on "Generating…", the page read as settled, and a journey whose end
|
|
230
|
+
// appeared a second later was failed. SETTLE_MS still bounds a request that never answers.
|
|
231
|
+
since: 0,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
/* a page that never settles is still read as it stands */
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Bring a goal's text into view, the way a person scrolls to it. True when it was found, and not
|
|
240
|
+
* inside text the Harness wrote itself.
|
|
241
|
+
*/
|
|
242
|
+
async function scrolledTo(invoke, text, own) {
|
|
243
|
+
try {
|
|
244
|
+
const result = asRecord(await invoke(ReticleTool.SCROLL_TO, {
|
|
245
|
+
by: QueryBy.TEXT,
|
|
246
|
+
value: text,
|
|
247
|
+
maxScrolls: GOAL_SCROLLS,
|
|
248
|
+
}));
|
|
249
|
+
const element = asRecord(result['element']);
|
|
250
|
+
const shown = element['text'] ?? element['name'];
|
|
251
|
+
return (true === result['found'] && !('string' === typeof shown && writtenByHarness(shown, text, own)));
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const SNAPSHOT_FULL = 'full';
|
|
258
|
+
/** The page as it reads now, or undefined when it cannot be read. Taken before a drive starts. */
|
|
259
|
+
export async function readOpening(invoke) {
|
|
39
260
|
try {
|
|
40
261
|
const tree = asRecord(await invoke(ReticleTool.SNAPSHOT, { mode: SNAPSHOT_FULL }))['tree'];
|
|
41
|
-
|
|
42
|
-
page = tree;
|
|
262
|
+
return 'string' === typeof tree ? tree : undefined;
|
|
43
263
|
}
|
|
44
264
|
catch {
|
|
45
|
-
|
|
265
|
+
return undefined;
|
|
46
266
|
}
|
|
47
|
-
|
|
48
|
-
|
|
267
|
+
}
|
|
268
|
+
function allOf(goals) {
|
|
269
|
+
const each = goals.map((g) => {
|
|
270
|
+
const shown = { kind: PredicateKind.TEXT, contains: g.text };
|
|
271
|
+
return true === g.absent ? { kind: PredicateKind.NOT, predicate: shown } : shown;
|
|
272
|
+
});
|
|
273
|
+
return 1 === each.length ? (each[0] ?? {}) : { kind: PredicateKind.ALL_OF, predicates: each };
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* The end state the caller declared, checked like any assert.
|
|
277
|
+
*
|
|
278
|
+
* Quoted text only proves some words are on the last page, which is how a drive that never used the
|
|
279
|
+
* app could pass: from the field, "it only checks that I have certain titles on my page". An `expect`
|
|
280
|
+
* names an OUTCOME — a route reached, a request made, state changed — in the same predicate grammar
|
|
281
|
+
* as reticle_assert, and is graded the same way. A check that cannot run is `unknown`.
|
|
282
|
+
*/
|
|
283
|
+
export async function checkExpect(invoke, predicate) {
|
|
49
284
|
let verified = Verified.UNKNOWN;
|
|
50
285
|
try {
|
|
51
|
-
const result = asRecord(await invoke(ReticleTool.ASSERT, { predicate
|
|
286
|
+
const result = asRecord(await invoke(ReticleTool.ASSERT, { predicate }));
|
|
52
287
|
if ('string' === typeof result['verified'])
|
|
53
288
|
verified = result['verified'];
|
|
54
289
|
}
|
|
55
290
|
catch {
|
|
56
|
-
/* stays unknown: the
|
|
291
|
+
/* stays unknown: the outcome was not proved */
|
|
57
292
|
}
|
|
58
|
-
|
|
59
|
-
// quote rests on the recorded assert.
|
|
60
|
-
return texts.map((text) => ({
|
|
61
|
-
text,
|
|
62
|
-
verified: 0 === shown.length || shown.includes(text) ? verified : Verified.NO,
|
|
63
|
-
}));
|
|
293
|
+
return { text: `expect ${JSON.stringify(predicate).slice(0, MAX_GOAL_LENGTH)}`, verified };
|
|
64
294
|
}
|
|
65
|
-
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
295
|
+
/** Whether the drive met the goals it was given: undefined when it was given none. */
|
|
296
|
+
export function goalsMet(checks) {
|
|
297
|
+
return 0 === checks.length ? undefined : checks.every((c) => Verified.YES === c.verified);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Whether the drive met its goal, as the answer and the run report it. The driver's own `goalMet`
|
|
301
|
+
* is its word, never evidence: a run answered `goalMet: true` after zero actions on a tab that
|
|
302
|
+
* already showed the end state, and another with both its goals `unknown`. With goals, met exactly
|
|
303
|
+
* when every one was proved; with none, the driver's word stands, and only after an action ran.
|
|
304
|
+
* Undefined when nothing judged it.
|
|
305
|
+
*/
|
|
306
|
+
export function goalOfDrive(claimed, checks, acted) {
|
|
307
|
+
// Proved goals are the evidence: a `yes` was not on the page before the drive (that reads
|
|
308
|
+
// already_true), so the run reached it, by the drive or by the hunt after it.
|
|
309
|
+
if (0 < checks.length)
|
|
310
|
+
return true === goalsMet(checks);
|
|
311
|
+
return claimed === undefined ? undefined : acted && claimed;
|
|
69
312
|
}
|
|
70
313
|
/** One line naming the goals the drive did not prove, or undefined when it proved them all. */
|
|
71
314
|
export function unprovedGoals(checks) {
|
|
72
315
|
const missed = checks.filter((c) => Verified.YES !== c.verified);
|
|
73
316
|
if (0 === missed.length)
|
|
74
317
|
return undefined;
|
|
75
|
-
return (`Not proved: ${missed.map((c) =>
|
|
318
|
+
return (`Not proved: ${missed.map((c) => `${true === c.absent ? 'no ' : ''}"${c.text}" (${c.reason ?? c.verified})`).join(', ')}. ` +
|
|
76
319
|
'The drive did not finish the job it was given, whatever its own account says.');
|
|
77
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* The goals a drive is graded on: every quote, less a value the drive typed — an input, never
|
|
323
|
+
* evidence — except the journey's end (its last text to show), which stays even when typed: saving
|
|
324
|
+
* "Order 7" and then seeing it listed is the journey. Absence goals always stay.
|
|
325
|
+
*/
|
|
326
|
+
export function endGoalsOf(goals, typed) {
|
|
327
|
+
const end = goals.filter((g) => true !== g.absent).at(-1);
|
|
328
|
+
const inputs = new Set(typed.map((t) => t.trim()));
|
|
329
|
+
return goals.filter((g) => true === g.absent || g === end || !inputs.has(g.text.trim()));
|
|
330
|
+
}
|
|
@@ -133,6 +133,8 @@ export interface HarnessResult {
|
|
|
133
133
|
* held are not the goal reached: "open each section" passed on two presses of Sign in.
|
|
134
134
|
*/
|
|
135
135
|
goalMet?: boolean;
|
|
136
|
+
/** The credits the platform says the drive spent; absent for a local model or an older platform. */
|
|
137
|
+
creditsSpent?: number;
|
|
136
138
|
}
|
|
137
139
|
/**
|
|
138
140
|
* The answer to a `finish` that came before anything was proved. Sent once per drive: a real drive
|
|
@@ -140,6 +142,17 @@ export interface HarnessResult {
|
|
|
140
142
|
* verifies nothing, which is the paid feature producing an empty pass.
|
|
141
143
|
*/
|
|
142
144
|
export declare const MSG_FINISH_UNPROVED: string;
|
|
145
|
+
/**
|
|
146
|
+
* The answer to a click repeated after it changed nothing. A persona drive clicked the logo four
|
|
147
|
+
* times on the page the logo leads to, each a wasted action and a wrong route guess.
|
|
148
|
+
*/
|
|
149
|
+
export declare const MSG_REPEAT_CHANGED_NOTHING: string;
|
|
150
|
+
/**
|
|
151
|
+
* Said on a click whose route guess missed while it opened something on the page. A persona drive
|
|
152
|
+
* clicked the account button expecting to leave /overview; it opened a menu, the miss read as a
|
|
153
|
+
* failure, and the drive abandoned the route its plan named instead of choosing "Settings" in it.
|
|
154
|
+
*/
|
|
155
|
+
export declare const MSG_OPENED_IN_PLACE: string;
|
|
143
156
|
/**
|
|
144
157
|
* The one tool the harness adds to Reticle's surface.
|
|
145
158
|
*
|
|
@@ -155,6 +168,22 @@ export interface HarnessOptions {
|
|
|
155
168
|
focus?: string;
|
|
156
169
|
/** How long one model turn may take. Injected so a test can prove the bound without waiting for it. */
|
|
157
170
|
turnTimeoutMs?: number;
|
|
171
|
+
/** Asked before every turn: true ends the drive as STOPPED. Whoever started it may stop it. */
|
|
172
|
+
stopped?: () => boolean;
|
|
173
|
+
/** Asked before every turn: what somebody said to the drive since, handed over as user turns. */
|
|
174
|
+
inbox?: () => readonly string[];
|
|
175
|
+
/** The pages the app's map knows: named up front, and offered after a route guess misses. */
|
|
176
|
+
routes?: readonly string[];
|
|
177
|
+
/**
|
|
178
|
+
* The end the goal quotes, and the text the Harness wrote itself (never the app showing it): the
|
|
179
|
+
* drive stops once a check has held and the page shows it. Driven over MCP: a model that reached
|
|
180
|
+
* "Generated script for" kept going, navigated to a start page that signs the app out, and ended
|
|
181
|
+
* on a page without the result — a working journey failed by its own wandering.
|
|
182
|
+
*/
|
|
183
|
+
endsAt?: {
|
|
184
|
+
readonly text: string;
|
|
185
|
+
readonly own: readonly string[];
|
|
186
|
+
};
|
|
158
187
|
}
|
|
159
188
|
export declare const DEFAULT_MAX_STEPS = 40;
|
|
160
189
|
/**
|
|
@@ -170,7 +199,7 @@ export declare const DEFAULT_MAX_STEPS = 40;
|
|
|
170
199
|
* deterministically for a few hundred tokens, forever. That is the whole economics of this feature,
|
|
171
200
|
* so it is an instruction and not a hope.
|
|
172
201
|
*/
|
|
173
|
-
export declare function systemPrompt(focus?: string): string;
|
|
202
|
+
export declare function systemPrompt(focus?: string, routes?: readonly string[]): string;
|
|
174
203
|
/**
|
|
175
204
|
* Run the harness to completion.
|
|
176
205
|
*
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
* 3. **Everything is injected** — the model, the tools, the clock. The loop below has no network in
|
|
18
18
|
* it, which is what lets the whole thing be tested against a scripted model with no API key.
|
|
19
19
|
*/
|
|
20
|
-
import { ReticleTool, asRecord } from '@reticlehq/core';
|
|
20
|
+
import { ReticleTool, Verified, WRITING_RULE, asRecord } from '@reticlehq/core';
|
|
21
|
+
import { claimsOnlyRoute, knownPagesLine, routeHint } from './route-guess.js';
|
|
21
22
|
import { withTimeout } from './with-timeout.js';
|
|
23
|
+
import { lookFor } from './goals.js';
|
|
22
24
|
/** Why a run stopped. Every one of these is a real outcome; none of them is an error. */
|
|
23
25
|
export const StopReason = {
|
|
24
26
|
/** The model called `finish` — it believes it is done. */
|
|
@@ -35,10 +37,14 @@ export const StopReason = {
|
|
|
35
37
|
/** Thrown by a driver when a person, not a fault, ended the drive. */
|
|
36
38
|
export class DriveStoppedError extends Error {
|
|
37
39
|
}
|
|
38
|
-
/** The finish's judgement of the goal, when it carried
|
|
40
|
+
/** The finish's judgement of the goal, and what the drive cost, when it carried them. */
|
|
39
41
|
function goalMetOf(args) {
|
|
40
42
|
const met = args['goalMet'];
|
|
41
|
-
|
|
43
|
+
const spent = args['creditsSpent'];
|
|
44
|
+
return {
|
|
45
|
+
...('boolean' === typeof met ? { goalMet: met } : {}),
|
|
46
|
+
...('number' === typeof spent ? { creditsSpent: spent } : {}),
|
|
47
|
+
};
|
|
42
48
|
}
|
|
43
49
|
/**
|
|
44
50
|
* The answer to a `finish` that came before anything was proved. Sent once per drive: a real drive
|
|
@@ -48,6 +54,61 @@ function goalMetOf(args) {
|
|
|
48
54
|
export const MSG_FINISH_UNPROVED = `Not finished: nothing has been proved yet. Before finishing, end the journey with ` +
|
|
49
55
|
`${ReticleTool.ACT_AND_WAIT} and an \`until\` naming the end state it must reach, or ` +
|
|
50
56
|
`${ReticleTool.ASSERT} on it. A drive that proves nothing replays as "verified nothing".`;
|
|
57
|
+
/**
|
|
58
|
+
* The answer to a click repeated after it changed nothing. A persona drive clicked the logo four
|
|
59
|
+
* times on the page the logo leads to, each a wasted action and a wrong route guess.
|
|
60
|
+
*/
|
|
61
|
+
export const MSG_REPEAT_CHANGED_NOTHING = 'Not done again: the same action on the same control just changed nothing on the page — no ' +
|
|
62
|
+
'content, request, route or signal. Doing it again will not either. Look at the page and take ' +
|
|
63
|
+
'the next step the journey names.';
|
|
64
|
+
/**
|
|
65
|
+
* Said on a click whose route guess missed while it opened something on the page. A persona drive
|
|
66
|
+
* clicked the account button expecting to leave /overview; it opened a menu, the miss read as a
|
|
67
|
+
* failure, and the drive abandoned the route its plan named instead of choosing "Settings" in it.
|
|
68
|
+
*/
|
|
69
|
+
export const MSG_OPENED_IN_PLACE = 'This click opened something on this page (a menu, a panel, a dialog) instead of going where you ' +
|
|
70
|
+
'guessed. That is progress, not a dead end: look at what it opened and act on the item the ' +
|
|
71
|
+
'journey names next.';
|
|
72
|
+
/** A route-only guess that missed, on a click that added content: it opened something in place. */
|
|
73
|
+
function openedInPlace(outcome) {
|
|
74
|
+
if (outcome.isError || ReticleTool.ACT_AND_WAIT !== outcome.name)
|
|
75
|
+
return false;
|
|
76
|
+
if (!claimsOnlyRoute(asRecord(outcome.args)['until']))
|
|
77
|
+
return false;
|
|
78
|
+
const result = asRecord(outcome.result);
|
|
79
|
+
if (Verified.YES === result['verified'])
|
|
80
|
+
return false;
|
|
81
|
+
const summary = asRecord(asRecord(result['trace'])['summary']);
|
|
82
|
+
return 0 < (Number(summary['domAdded']) || 0);
|
|
83
|
+
}
|
|
84
|
+
/** A route-only guess that missed: the known page to go to instead, when the map knows any. */
|
|
85
|
+
function missedRouteHint(outcome, routes) {
|
|
86
|
+
if (outcome.isError || ReticleTool.ACT_AND_WAIT !== outcome.name)
|
|
87
|
+
return undefined;
|
|
88
|
+
const until = asRecord(outcome.args)['until'];
|
|
89
|
+
if (!claimsOnlyRoute(until) || Verified.YES === asRecord(outcome.result)['verified'])
|
|
90
|
+
return undefined;
|
|
91
|
+
return routeHint(until, routes);
|
|
92
|
+
}
|
|
93
|
+
/** The tools that act on one control, keyed by what they did to which control. */
|
|
94
|
+
const ACTING_TOOLS = new Set([ReticleTool.ACT, ReticleTool.ACT_AND_WAIT]);
|
|
95
|
+
/** What an action did to which control, or undefined for a call that does not act on one. */
|
|
96
|
+
function actKey(name, args) {
|
|
97
|
+
if (!ACTING_TOOLS.has(name))
|
|
98
|
+
return undefined;
|
|
99
|
+
const target = args['ref'] ?? args['target'];
|
|
100
|
+
if (target === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
return JSON.stringify([target, args['action'] ?? null, args['value'] ?? null]);
|
|
103
|
+
}
|
|
104
|
+
/** Whether a result's reaction digest says the page did nothing at all; undefined when it has none. */
|
|
105
|
+
function changedNothing(result) {
|
|
106
|
+
const summary = asRecord(asRecord(asRecord(result)['trace'])['summary']);
|
|
107
|
+
if ('number' !== typeof summary['total'])
|
|
108
|
+
return undefined;
|
|
109
|
+
const moved = ['network', 'domAdded', 'domRemoved', 'domChanged', 'routeChanges', 'signals'].some((k) => 0 < (Number(summary[k]) || 0));
|
|
110
|
+
return !moved;
|
|
111
|
+
}
|
|
51
112
|
/** A check that ran: the only thing that turns a drive into evidence. */
|
|
52
113
|
function isProof(outcome) {
|
|
53
114
|
if (outcome.isError)
|
|
@@ -78,6 +139,10 @@ export const FINISH_TOOL = {
|
|
|
78
139
|
additionalProperties: false,
|
|
79
140
|
},
|
|
80
141
|
};
|
|
142
|
+
/** Why a drive ended before the model said so. */
|
|
143
|
+
const msgReachedEnd = (end) => `Reached the end the goal names ("${end}") after a check held, so the drive stopped there.`;
|
|
144
|
+
/** A check in this turn that came back yes: the moment worth looking for the end. */
|
|
145
|
+
const heldCheck = (outcome) => isProof(outcome) && Verified.YES === asRecord(outcome.result)['verified'];
|
|
81
146
|
export const DEFAULT_MAX_STEPS = 40;
|
|
82
147
|
/**
|
|
83
148
|
* How long a single model turn may take before the run gives up on it.
|
|
@@ -106,7 +171,7 @@ const OPENING = 'The app is loaded and connected. Begin by looking at the page,
|
|
|
106
171
|
* deterministically for a few hundred tokens, forever. That is the whole economics of this feature,
|
|
107
172
|
* so it is an instruction and not a hope.
|
|
108
173
|
*/
|
|
109
|
-
export function systemPrompt(focus) {
|
|
174
|
+
export function systemPrompt(focus, routes = []) {
|
|
110
175
|
return [
|
|
111
176
|
'You are driving a real web application through a verification tool, to find out whether it actually works.',
|
|
112
177
|
'',
|
|
@@ -121,7 +186,9 @@ export function systemPrompt(focus) {
|
|
|
121
186
|
'the app is broken underneath.',
|
|
122
187
|
'',
|
|
123
188
|
'Drive the app the way a user would: complete whole journeys, not single clicks. Submit the forms.',
|
|
124
|
-
'Follow the navigation. If something looks interactive, try it
|
|
189
|
+
'Follow the navigation. If something looks interactive, try it — unless the focus below says',
|
|
190
|
+
'not to: "without opening a run", "changes nothing" and the like bind the whole drive. Read the',
|
|
191
|
+
'list a journey says to read; do not open what it says to leave closed.',
|
|
125
192
|
'',
|
|
126
193
|
'RECORD WHAT YOU DRIVE. Before each journey call reticle_record { action: "start" }, drive it, then',
|
|
127
194
|
'reticle_flow_save with a short descriptive name. A saved flow replays deterministically on every',
|
|
@@ -137,6 +204,14 @@ export function systemPrompt(focus) {
|
|
|
137
204
|
'expectations as you go. Do not report bugs; drive the app.',
|
|
138
205
|
'',
|
|
139
206
|
'Call `finish` when you have covered the app or cannot get further.',
|
|
207
|
+
'',
|
|
208
|
+
WRITING_RULE,
|
|
209
|
+
...(0 === routes.length
|
|
210
|
+
? []
|
|
211
|
+
: [
|
|
212
|
+
'',
|
|
213
|
+
`Pages this app has: ${knownPagesLine(routes)}. To reach one, press its link or reticle_navigate to it; do not guess which control leads there.`,
|
|
214
|
+
]),
|
|
140
215
|
...(focus === undefined ? [] : ['', `Focus for this run: ${focus}`]),
|
|
141
216
|
].join('\n');
|
|
142
217
|
}
|
|
@@ -149,13 +224,29 @@ export function systemPrompt(focus) {
|
|
|
149
224
|
export async function runHarness(driver, toolset, options = {}) {
|
|
150
225
|
const maxSteps = options.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
151
226
|
const tools = [...toolset.tools, FINISH_TOOL];
|
|
152
|
-
const
|
|
227
|
+
const routes = options.routes ?? [];
|
|
228
|
+
const system = systemPrompt(options.focus, routes);
|
|
153
229
|
const history = [{ role: 'user', text: OPENING }];
|
|
154
230
|
const toolCalls = [];
|
|
155
231
|
const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
156
232
|
const proved = () => toolCalls.some(isProof);
|
|
157
233
|
let askedToProve = false;
|
|
234
|
+
// Actions that just changed nothing; forgotten as soon as any action moves the page.
|
|
235
|
+
const inert = new Set();
|
|
158
236
|
for (let step = 0; step < maxSteps; step += 1) {
|
|
237
|
+
if (true === options.stopped?.()) {
|
|
238
|
+
return {
|
|
239
|
+
stopReason: StopReason.STOPPED,
|
|
240
|
+
summary: '',
|
|
241
|
+
steps: step,
|
|
242
|
+
toolCalls,
|
|
243
|
+
usage,
|
|
244
|
+
proved: proved(),
|
|
245
|
+
goalMet: false,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
for (const text of options.inbox?.() ?? [])
|
|
249
|
+
history.push({ role: 'user', text });
|
|
159
250
|
let turn;
|
|
160
251
|
try {
|
|
161
252
|
turn = await withTimeout(driver.turn({ system, tools, history }), options.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS, 'the model did not answer');
|
|
@@ -168,6 +259,9 @@ export async function runHarness(driver, toolset, options = {}) {
|
|
|
168
259
|
toolCalls,
|
|
169
260
|
usage,
|
|
170
261
|
proved: proved(),
|
|
262
|
+
// Cut off is not done: the checks that held before a refusal (credits gone, a 5xx) prove
|
|
263
|
+
// the steps before it, never the journey, and every verdict downstream reads this.
|
|
264
|
+
goalMet: false,
|
|
171
265
|
error: error instanceof Error ? error.message : String(error),
|
|
172
266
|
};
|
|
173
267
|
}
|
|
@@ -233,9 +327,49 @@ export async function runHarness(driver, toolset, options = {}) {
|
|
|
233
327
|
// Every call the model asked for is answered, including ones that fail. A dropped result leaves
|
|
234
328
|
// the model waiting on an answer that never comes, and it will usually re-ask — burning the
|
|
235
329
|
// budget on a call that already ran, against an app it already changed.
|
|
236
|
-
const outcomes = await Promise.all(turn.calls.map((call) =>
|
|
330
|
+
const outcomes = await Promise.all(turn.calls.map((call) => {
|
|
331
|
+
const key = actKey(call.name, call.args);
|
|
332
|
+
return key !== undefined && inert.has(key)
|
|
333
|
+
? Promise.resolve({
|
|
334
|
+
id: call.id,
|
|
335
|
+
name: call.name,
|
|
336
|
+
args: call.args,
|
|
337
|
+
result: { error: MSG_REPEAT_CHANGED_NOTHING },
|
|
338
|
+
isError: true,
|
|
339
|
+
})
|
|
340
|
+
: invokeOne(toolset, call);
|
|
341
|
+
}));
|
|
342
|
+
for (const [i, outcome] of outcomes.entries()) {
|
|
343
|
+
const hint = openedInPlace(outcome) ? MSG_OPENED_IN_PLACE : missedRouteHint(outcome, routes);
|
|
344
|
+
if (hint !== undefined)
|
|
345
|
+
outcomes[i] = { ...outcome, result: { ...asRecord(outcome.result), next: hint } };
|
|
346
|
+
}
|
|
347
|
+
for (const outcome of outcomes) {
|
|
348
|
+
const key = actKey(outcome.name, outcome.args);
|
|
349
|
+
if (key === undefined || outcome.isError)
|
|
350
|
+
continue;
|
|
351
|
+
const nothing = changedNothing(outcome.result);
|
|
352
|
+
if (true === nothing)
|
|
353
|
+
inert.add(key);
|
|
354
|
+
else if (false === nothing)
|
|
355
|
+
inert.clear();
|
|
356
|
+
}
|
|
237
357
|
toolCalls.push(...outcomes);
|
|
238
358
|
history.push({ role: 'tool', outcomes });
|
|
359
|
+
const end = options.endsAt;
|
|
360
|
+
if (end !== undefined &&
|
|
361
|
+
outcomes.some(heldCheck) &&
|
|
362
|
+
(await lookFor((name, args) => toolset.invoke(name, args), end.text, end.own))) {
|
|
363
|
+
return {
|
|
364
|
+
stopReason: StopReason.FINISHED,
|
|
365
|
+
summary: msgReachedEnd(end.text),
|
|
366
|
+
steps: step + 1,
|
|
367
|
+
toolCalls,
|
|
368
|
+
usage,
|
|
369
|
+
proved: proved(),
|
|
370
|
+
goalMet: true,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
239
373
|
}
|
|
240
374
|
return {
|
|
241
375
|
stopReason: StopReason.BUDGET,
|