@reticlehq/server 3.7.0 → 3.8.1
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 +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* No I/O and no tool surface: what to run and where to write it arrive as functions, so this stays
|
|
11
11
|
* a sink the surface hands things to.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { HarnessRunReason } from '@reticlehq/core/telemetry';
|
|
14
|
+
import { EventType, HARNESS_LINES_MAX, NetInitiator, ReticleTool, asRecord, } from '@reticlehq/core';
|
|
14
15
|
export const DriveStatus = {
|
|
15
16
|
RUNNING: 'running',
|
|
16
17
|
DONE: 'done',
|
|
@@ -24,9 +25,22 @@ export const DriveStatus = {
|
|
|
24
25
|
* such a run as "broken", which reads as a fault in the Harness; a refusal is a refusal.
|
|
25
26
|
*/
|
|
26
27
|
export class DriveRefusedError extends Error {
|
|
28
|
+
reason;
|
|
29
|
+
constructor(message,
|
|
30
|
+
/** Why, as `harness_run` reports it. */
|
|
31
|
+
reason = HarnessRunReason.OTHER) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.reason = reason;
|
|
34
|
+
}
|
|
27
35
|
}
|
|
28
36
|
/** Who asked for the drive. */
|
|
29
37
|
export const DriveOrigin = { AGENT: 'agent', HUD: 'hud', CHAT: 'chat', TRY: 'try' };
|
|
38
|
+
/** The longest line the Harness page's log is sent. */
|
|
39
|
+
const LINE_MAX = 300;
|
|
40
|
+
/** Per tab: how its last drive ended, or why none started. Cleared when a drive starts there. */
|
|
41
|
+
const lastWords = new Map();
|
|
42
|
+
/** Numbers each last word, so the page can tell a new answer from one it already showed. */
|
|
43
|
+
let wordSeq = 0;
|
|
30
44
|
/** Finished drives kept for polling; older ones are read back from disk. */
|
|
31
45
|
const MAX_FINISHED = 50;
|
|
32
46
|
const LANE_SUFFIX = /-L\d+$/;
|
|
@@ -52,7 +66,10 @@ export function startDrive(start) {
|
|
|
52
66
|
abort: new AbortController(),
|
|
53
67
|
inbox: [],
|
|
54
68
|
waiters: new Set(),
|
|
69
|
+
lines: [],
|
|
55
70
|
};
|
|
71
|
+
if (start.sessionId !== undefined)
|
|
72
|
+
lastWords.delete(start.sessionId);
|
|
56
73
|
entries.set(start.runId, entry);
|
|
57
74
|
byHarness.set(start.harness, entry);
|
|
58
75
|
const save = () => {
|
|
@@ -78,6 +95,7 @@ export function startDrive(start) {
|
|
|
78
95
|
})
|
|
79
96
|
.then(() => {
|
|
80
97
|
entry.record.endedAt = start.now();
|
|
98
|
+
noteLastWord(entry);
|
|
81
99
|
save();
|
|
82
100
|
changed(entry, true);
|
|
83
101
|
prune();
|
|
@@ -137,14 +155,71 @@ export function awaitDrive(runId, ms, signal, onChange) {
|
|
|
137
155
|
done();
|
|
138
156
|
});
|
|
139
157
|
}
|
|
140
|
-
/**
|
|
141
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Ask a running drive to stop. False when there is no such drive running. `reason` is said on the
|
|
160
|
+
* Harness page when something other than the person stopped it (the page reloaded).
|
|
161
|
+
*/
|
|
162
|
+
export function stopDrive(runId, reason) {
|
|
142
163
|
const entry = entries.get(runId);
|
|
143
164
|
if (entry === undefined || DriveStatus.RUNNING !== entry.record.status)
|
|
144
165
|
return false;
|
|
166
|
+
if (reason !== undefined)
|
|
167
|
+
entry.stopReason = reason;
|
|
145
168
|
entry.abort.abort();
|
|
146
169
|
return true;
|
|
147
170
|
}
|
|
171
|
+
/** Why no drive started on this tab (a refusal, the cloud unreachable), for its Harness page. */
|
|
172
|
+
export function noteHarnessWord(sessionId, said) {
|
|
173
|
+
wordSeq += 1;
|
|
174
|
+
lastWords.set(sessionId, { said, seq: wordSeq });
|
|
175
|
+
}
|
|
176
|
+
/** How this drive ended, for its tab's Harness page: the run's result, and why when it fell short. */
|
|
177
|
+
function noteLastWord(entry) {
|
|
178
|
+
const sessionId = entry.record.sessionId;
|
|
179
|
+
if (sessionId === undefined)
|
|
180
|
+
return;
|
|
181
|
+
const runUrl = entry.record.result?.['runUrl'];
|
|
182
|
+
const fell = DriveStatus.BROKEN === entry.record.status || DriveStatus.REFUSED === entry.record.status
|
|
183
|
+
? entry.record.error
|
|
184
|
+
: undefined;
|
|
185
|
+
const said = entry.stopReason ?? fell;
|
|
186
|
+
wordSeq += 1;
|
|
187
|
+
lastWords.set(sessionId, {
|
|
188
|
+
seq: wordSeq,
|
|
189
|
+
...('string' === typeof runUrl ? { runUrl } : {}),
|
|
190
|
+
...(said === undefined ? {} : { said }),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/** Said on the Harness page when a person reloads the page during a run. */
|
|
194
|
+
export const MSG_RELOADED = 'The page reloaded, so this run stopped. Run again.';
|
|
195
|
+
/** How long after the daemon asks a tab to reload the page's reload counts as the daemon's. */
|
|
196
|
+
const OWN_RELOAD_WINDOW_MS = 60_000;
|
|
197
|
+
const RELOAD = 'reload';
|
|
198
|
+
/** Per tab: when the daemon last sent it a reload (a replay, a durable recheck, a navigate). */
|
|
199
|
+
const ownReloads = new Map();
|
|
200
|
+
/** The daemon asked this tab to reload: the page coming back is not a person reloading it. */
|
|
201
|
+
export function noteOwnReload(sessionId, at) {
|
|
202
|
+
ownReloads.set(sessionId, at);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Read each event a tab sends. A document that arrives as a reload the daemon did not ask for is a
|
|
206
|
+
* person reloading the page, and a drive on that tab cannot go on as if the page had not started
|
|
207
|
+
* over: it is stopped, and its Harness page says why. Before, it drove on and ended "failed".
|
|
208
|
+
*/
|
|
209
|
+
export function notePageLoaded(sessionId, event, at) {
|
|
210
|
+
if (EventType.NET_REQUEST !== event.type)
|
|
211
|
+
return;
|
|
212
|
+
const data = asRecord(event.data);
|
|
213
|
+
if (NetInitiator.DOCUMENT !== data['initiator'] || RELOAD !== data['navigationType'])
|
|
214
|
+
return;
|
|
215
|
+
const asked = ownReloads.get(sessionId);
|
|
216
|
+
ownReloads.delete(sessionId);
|
|
217
|
+
if (asked !== undefined && at - asked <= OWN_RELOAD_WINDOW_MS)
|
|
218
|
+
return;
|
|
219
|
+
const running = runningDrive(sessionId);
|
|
220
|
+
if (running?.sessionId === sessionId)
|
|
221
|
+
stopDrive(running.harnessRun, MSG_RELOADED);
|
|
222
|
+
}
|
|
148
223
|
/** Queue text for the driver's next turn. False when there is no such drive running. */
|
|
149
224
|
export function sayToDrive(runId, text) {
|
|
150
225
|
const entry = entries.get(runId);
|
|
@@ -177,6 +252,9 @@ export function noteDriveLine(harness, line) {
|
|
|
177
252
|
if (entry === undefined)
|
|
178
253
|
return;
|
|
179
254
|
entry.record.lastLine = line;
|
|
255
|
+
entry.lines.push(line.length > LINE_MAX ? `${line.slice(0, LINE_MAX)}…` : line);
|
|
256
|
+
if (entry.lines.length > HARNESS_LINES_MAX)
|
|
257
|
+
entry.lines.shift();
|
|
180
258
|
changed(entry, false);
|
|
181
259
|
}
|
|
182
260
|
/** Hear every step a drive takes, so a panel showing its count can repaint. Returns the unsubscribe. */
|
|
@@ -205,6 +283,8 @@ export function forgetDrives() {
|
|
|
205
283
|
entries.clear();
|
|
206
284
|
byHarness.clear();
|
|
207
285
|
notes.clear();
|
|
286
|
+
lastWords.clear();
|
|
287
|
+
ownReloads.clear();
|
|
208
288
|
}
|
|
209
289
|
function entryFor(harness) {
|
|
210
290
|
return byHarness.get(harness) ?? byHarness.get(harness.replace(LANE_SUFFIX, ''));
|
|
@@ -279,11 +359,23 @@ function prune() {
|
|
|
279
359
|
byHarness.delete(entry.harness);
|
|
280
360
|
}
|
|
281
361
|
}
|
|
282
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* The impact snapshot, with the drive running on this tab and its step log when there is one, else
|
|
364
|
+
* how the last one on this tab ended.
|
|
365
|
+
*/
|
|
283
366
|
export function withRunningDrive(snapshot, sessionId) {
|
|
284
|
-
|
|
285
|
-
if (running === undefined || snapshot === undefined)
|
|
367
|
+
if (snapshot === undefined)
|
|
286
368
|
return snapshot;
|
|
287
|
-
const
|
|
369
|
+
const running = runningDrive(sessionId);
|
|
370
|
+
if (running === undefined) {
|
|
371
|
+
const last = lastWords.get(sessionId);
|
|
372
|
+
return last === undefined ? snapshot : { ...snapshot, lastDrive: last };
|
|
373
|
+
}
|
|
374
|
+
const lines = entries.get(running.harnessRun)?.lines ?? [];
|
|
375
|
+
const drive = {
|
|
376
|
+
runId: running.harnessRun,
|
|
377
|
+
steps: running.steps,
|
|
378
|
+
...(0 === lines.length ? {} : { lines: [...lines] }),
|
|
379
|
+
};
|
|
288
380
|
return { ...snapshot, harnessDrive: drive };
|
|
289
381
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* a module that imports nothing cannot be read half-initialised.
|
|
6
6
|
*
|
|
7
7
|
* There is one driver. Every Harness decision, its models and its planning run on the Reticle
|
|
8
|
-
* platform, on the workspace's
|
|
8
|
+
* platform, on the workspace's credits; this machine executes what it is told.
|
|
9
9
|
*/
|
|
10
10
|
/** The platform's Harness: it decides on its side, this machine executes. See server-driver.ts. */
|
|
11
11
|
export declare const SERVER_DRIVER = "server";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* a module that imports nothing cannot be read half-initialised.
|
|
6
6
|
*
|
|
7
7
|
* There is one driver. Every Harness decision, its models and its planning run on the Reticle
|
|
8
|
-
* platform, on the workspace's
|
|
8
|
+
* platform, on the workspace's credits; this machine executes what it is told.
|
|
9
9
|
*/
|
|
10
10
|
/** The platform's Harness: it decides on its side, this machine executes. See server-driver.ts. */
|
|
11
11
|
export const SERVER_DRIVER = 'server';
|
|
@@ -96,6 +96,11 @@ export interface ModelDriver {
|
|
|
96
96
|
tools: readonly HarnessTool[];
|
|
97
97
|
history: readonly HistoryEntry[];
|
|
98
98
|
}): Promise<ModelTurn>;
|
|
99
|
+
/**
|
|
100
|
+
* The credits the drive spent, asked once it ended without the driver's own `finish` saying so (a
|
|
101
|
+
* goal reached on the page, the step budget, a stop). Absent for a driver that cannot tell.
|
|
102
|
+
*/
|
|
103
|
+
spent?(): Promise<number | undefined>;
|
|
99
104
|
}
|
|
100
105
|
/** Why a run stopped. Every one of these is a real outcome; none of them is an error. */
|
|
101
106
|
export declare const StopReason: {
|
|
@@ -139,8 +139,13 @@ export const FINISH_TOOL = {
|
|
|
139
139
|
additionalProperties: false,
|
|
140
140
|
},
|
|
141
141
|
};
|
|
142
|
-
/**
|
|
143
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Said to the model once the page shows the end the goal quotes. The drive used to stop right here,
|
|
144
|
+
* before the model's `finish`, and that skipped the platform's last check: it holds the finish and
|
|
145
|
+
* asserts the claim's values on the drive's own writes. On the broken checkout the page showed
|
|
146
|
+
* "charged $133.60" while the order went out at 16700, and the drive stopped as goal met.
|
|
147
|
+
*/
|
|
148
|
+
const msgEndReached = (end) => `The page shows the end the goal names ("${end}") and a check held. Do not drive further. Call \`finish\` now.`;
|
|
144
149
|
/** A check in this turn that came back yes: the moment worth looking for the end. */
|
|
145
150
|
const heldCheck = (outcome) => isProof(outcome) && Verified.YES === asRecord(outcome.result)['verified'];
|
|
146
151
|
export const DEFAULT_MAX_STEPS = 40;
|
|
@@ -222,6 +227,15 @@ export function systemPrompt(focus, routes = []) {
|
|
|
222
227
|
* through `driver` and `toolset`, which is what makes this testable against a scripted model.
|
|
223
228
|
*/
|
|
224
229
|
export async function runHarness(driver, toolset, options = {}) {
|
|
230
|
+
const result = await driveToEnd(driver, toolset, options);
|
|
231
|
+
// A drive ended here (its goal on the page, the budget, a stop) never heard the platform's last
|
|
232
|
+
// word on spend, and a plan summed it as 0 while the workspace paid 3.5 (night 7).
|
|
233
|
+
if (result.creditsSpent !== undefined || driver.spent === undefined)
|
|
234
|
+
return result;
|
|
235
|
+
const spent = await driver.spent().catch(() => undefined);
|
|
236
|
+
return spent === undefined ? result : { ...result, creditsSpent: spent };
|
|
237
|
+
}
|
|
238
|
+
async function driveToEnd(driver, toolset, options) {
|
|
225
239
|
const maxSteps = options.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
226
240
|
const tools = [...toolset.tools, FINISH_TOOL];
|
|
227
241
|
const routes = options.routes ?? [];
|
|
@@ -231,6 +245,8 @@ export async function runHarness(driver, toolset, options = {}) {
|
|
|
231
245
|
const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
232
246
|
const proved = () => toolCalls.some(isProof);
|
|
233
247
|
let askedToProve = false;
|
|
248
|
+
// Set once the page showed the goal's end and the model was told to finish.
|
|
249
|
+
let endTold = false;
|
|
234
250
|
// Actions that just changed nothing; forgotten as soon as any action moves the page.
|
|
235
251
|
const inert = new Set();
|
|
236
252
|
for (let step = 0; step < maxSteps; step += 1) {
|
|
@@ -357,18 +373,14 @@ export async function runHarness(driver, toolset, options = {}) {
|
|
|
357
373
|
toolCalls.push(...outcomes);
|
|
358
374
|
history.push({ role: 'tool', outcomes });
|
|
359
375
|
const end = options.endsAt;
|
|
360
|
-
if (
|
|
376
|
+
if (!endTold &&
|
|
377
|
+
end !== undefined &&
|
|
361
378
|
outcomes.some(heldCheck) &&
|
|
362
379
|
(await lookFor((name, args) => toolset.invoke(name, args), end.text, end.own))) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
toolCalls,
|
|
368
|
-
usage,
|
|
369
|
-
proved: proved(),
|
|
370
|
-
goalMet: true,
|
|
371
|
-
};
|
|
380
|
+
// The verdict is the finish's, never the page's: tell the model, and let its finish (which
|
|
381
|
+
// the platform may hold to read and assert first) end the drive. The step budget bounds it.
|
|
382
|
+
history.push({ role: 'user', text: msgEndReached(end.text) });
|
|
383
|
+
endTold = true;
|
|
372
384
|
}
|
|
373
385
|
}
|
|
374
386
|
return {
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* Both answer with a value and never throw: an unreachable platform is a sentence to show, not a
|
|
6
6
|
* crash in the CLI or in the HUD's button handler.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* How long the grant is waited for. A cloud that takes the connection and never answers held the
|
|
10
|
+
* HUD's Run on "Starting…" with no bound; past this, the person is told it did not answer.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GRANT_TIMEOUT_MS = 5000;
|
|
8
13
|
/** What the grant is for: `reticle try`'s one drive, or a drive started from the HUD or an agent. */
|
|
9
14
|
export declare const FreeDriveKind: {
|
|
10
15
|
readonly TRY: "try";
|
|
@@ -16,6 +21,7 @@ export type PlatformFetch = (url: string, init: {
|
|
|
16
21
|
method: string;
|
|
17
22
|
headers: Record<string, string>;
|
|
18
23
|
body?: string;
|
|
24
|
+
signal?: AbortSignal;
|
|
19
25
|
}) => Promise<{
|
|
20
26
|
status: number;
|
|
21
27
|
text(): Promise<string>;
|
|
@@ -37,7 +43,7 @@ interface Platform {
|
|
|
37
43
|
/** The dashboard page where a trial starts, at the origin of whatever link the platform gave. */
|
|
38
44
|
export declare function planUrl(dashboard: string): string;
|
|
39
45
|
/** Ask the platform for a drive it will pay for. Every failure is an answer, never a throw. */
|
|
40
|
-
export declare function requestFreeDrive(platform: Platform, kind: FreeDriveKind, doFetch?: PlatformFetch): Promise<FreeDrive>;
|
|
46
|
+
export declare function requestFreeDrive(platform: Platform, kind: FreeDriveKind, doFetch?: PlatformFetch, timeoutMs?: number): Promise<FreeDrive>;
|
|
41
47
|
/**
|
|
42
48
|
* A synced run as the explore poll answers it, in the poll's own fields.
|
|
43
49
|
*
|
|
@@ -10,6 +10,11 @@ const RUNS_PATH = '/v1/runs';
|
|
|
10
10
|
const NEEDS_CARD = 'needs_card';
|
|
11
11
|
const HTTP_PAYMENT_REQUIRED = 402;
|
|
12
12
|
const HTTP_NOT_FOUND = 404;
|
|
13
|
+
/**
|
|
14
|
+
* How long the grant is waited for. A cloud that takes the connection and never answers held the
|
|
15
|
+
* HUD's Run on "Starting…" with no bound; past this, the person is told it did not answer.
|
|
16
|
+
*/
|
|
17
|
+
export const GRANT_TIMEOUT_MS = 5_000;
|
|
13
18
|
/** Where a workspace starts its trial: the dashboard's billing settings, at its origin. */
|
|
14
19
|
const PLAN_PATH = '/settings?group=billing';
|
|
15
20
|
/** What the grant is for: `reticle try`'s one drive, or a drive started from the HUD or an agent. */
|
|
@@ -40,12 +45,13 @@ export function planUrl(dashboard) {
|
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
/** Ask the platform for a drive it will pay for. Every failure is an answer, never a throw. */
|
|
43
|
-
export async function requestFreeDrive(platform, kind, doFetch = defaultFetch) {
|
|
48
|
+
export async function requestFreeDrive(platform, kind, doFetch = defaultFetch, timeoutMs = GRANT_TIMEOUT_MS) {
|
|
44
49
|
try {
|
|
45
50
|
const res = await doFetch(`${base(platform)}${FREE_DRIVE_PATH}`, {
|
|
46
51
|
method: 'POST',
|
|
47
52
|
headers: headers(platform),
|
|
48
53
|
body: JSON.stringify({ kind }),
|
|
54
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
49
55
|
});
|
|
50
56
|
const body = record(await res.text());
|
|
51
57
|
const driveId = str(body['driveId']);
|
|
@@ -61,14 +67,13 @@ export async function requestFreeDrive(platform, kind, doFetch = defaultFetch) {
|
|
|
61
67
|
...(hint === undefined ? {} : { hint }),
|
|
62
68
|
};
|
|
63
69
|
}
|
|
64
|
-
catch
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
needsCard: false,
|
|
68
|
-
message: `Could not reach the Reticle platform: ${error instanceof Error ? error.message : String(error)}`,
|
|
69
|
-
};
|
|
70
|
+
catch {
|
|
71
|
+
// One line the Harness page can show as it is: the person can fix a connection, not a stack.
|
|
72
|
+
return { granted: false, needsCard: false, message: msgCloudSilent(base(platform)) };
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
/** Said when the platform cannot be reached at all. */
|
|
76
|
+
const msgCloudSilent = (url) => `Reticle Cloud at ${url} did not answer, so the Harness cannot start. Check your connection, then Run again.`;
|
|
72
77
|
const DRIVE_DONE = 'done';
|
|
73
78
|
const msgFromDashboard = (verdict, checks) => `From the dashboard (the drive itself is no longer on this machine): ${verdict === undefined ? 'no verdict recorded' : `verdict ${verdict}`}, ${String(checks)} check${1 === checks ? '' : 's'}.`;
|
|
74
79
|
/**
|
|
@@ -17,7 +17,7 @@ export declare const PlatformRefusalCode: {
|
|
|
17
17
|
export declare const RefusalLead: {
|
|
18
18
|
readonly harness_locked: "Reticle Harness is locked for this project.";
|
|
19
19
|
readonly harness_off: "The Reticle Harness is off for this project.";
|
|
20
|
-
readonly needs_card: "
|
|
20
|
+
readonly needs_card: "This workspace has used all of its Reticle credits.";
|
|
21
21
|
};
|
|
22
22
|
/** A platform refusal, read from its response body. Two shapes: nested `{ error: { code, message } }` and flat `{ error, message }`. */
|
|
23
23
|
export declare function readRefusal(text: string): {
|
|
@@ -17,7 +17,7 @@ export const PlatformRefusalCode = {
|
|
|
17
17
|
export const RefusalLead = {
|
|
18
18
|
[PlatformRefusalCode.HARNESS_LOCKED]: 'Reticle Harness is locked for this project.',
|
|
19
19
|
[PlatformRefusalCode.HARNESS_OFF]: 'The Reticle Harness is off for this project.',
|
|
20
|
-
[PlatformRefusalCode.NEEDS_CARD]: '
|
|
20
|
+
[PlatformRefusalCode.NEEDS_CARD]: 'This workspace has used all of its Reticle credits.',
|
|
21
21
|
};
|
|
22
22
|
/** A platform refusal, read from its response body. Two shapes: nested `{ error: { code, message } }` and flat `{ error, message }`. */
|
|
23
23
|
export function readRefusal(text) {
|
|
@@ -55,9 +55,9 @@ export function refusalSentence(refusal, status) {
|
|
|
55
55
|
* Absent means OFF. The Harness is opt-in: a new project starts with it off, and an answer that does
|
|
56
56
|
* not say it is on is not a yes to spend credits on.
|
|
57
57
|
*/
|
|
58
|
-
export const MSG_HARNESS_DISABLED = "The Reticle Harness is off for this project.
|
|
59
|
-
'
|
|
60
|
-
'through the MCP tools.';
|
|
58
|
+
export const MSG_HARNESS_DISABLED = "The Reticle Harness is off for this project. Each drive spends the workspace's credits, so the " +
|
|
59
|
+
'user switches it on: ask them to run `npx @reticlehq/server config --harness on` in the project. ' +
|
|
60
|
+
'Until then, drive the app yourself through the MCP tools.';
|
|
61
61
|
/**
|
|
62
62
|
* The other reason a drive can be refused, and it is NOT the same reason.
|
|
63
63
|
*
|
|
@@ -44,11 +44,13 @@ export function startRemoteDrives(deps) {
|
|
|
44
44
|
let busy = false;
|
|
45
45
|
let stopped = false;
|
|
46
46
|
const tick = async (told) => {
|
|
47
|
-
if (busy || stopped || (undefined === told && !deps.connected()))
|
|
47
|
+
if (busy || stopped || (undefined === told && !deps.connected())) {
|
|
48
48
|
return;
|
|
49
|
+
}
|
|
49
50
|
const platform = told ?? serverOptionsFromEnv(await deps.env());
|
|
50
|
-
if (platform === undefined)
|
|
51
|
+
if (platform === undefined) {
|
|
51
52
|
return;
|
|
53
|
+
}
|
|
52
54
|
const headers = {
|
|
53
55
|
'content-type': 'application/json',
|
|
54
56
|
authorization: `Bearer ${platform.apiKey}`,
|
|
@@ -60,13 +62,16 @@ export function startRemoteDrives(deps) {
|
|
|
60
62
|
headers,
|
|
61
63
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
62
64
|
});
|
|
63
|
-
if (!res.ok)
|
|
65
|
+
if (!res.ok) {
|
|
64
66
|
return;
|
|
67
|
+
}
|
|
65
68
|
const drive = (await res.json()).drive;
|
|
66
|
-
if (null === drive || undefined === drive)
|
|
69
|
+
if (null === drive || undefined === drive) {
|
|
67
70
|
return;
|
|
68
|
-
|
|
71
|
+
}
|
|
72
|
+
if ('string' !== typeof drive.id || 'string' !== typeof drive.goal) {
|
|
69
73
|
return;
|
|
74
|
+
}
|
|
70
75
|
deps.log?.(`reticle: driving a request from the platform chat: ${drive.goal}`);
|
|
71
76
|
const driveId = drive.id;
|
|
72
77
|
const driveUrl = `${platform.url}${LinkPath.driveResult(driveId)}`;
|
|
@@ -197,24 +202,43 @@ async function runToolSession(post, tools, first, now, sleep) {
|
|
|
197
202
|
};
|
|
198
203
|
const res = await askWithRetries(() => post(body), sleep);
|
|
199
204
|
// Refused, or still failing after every retry: stop. The platform ends a drive that stops asking.
|
|
200
|
-
if (res === undefined || !res.ok)
|
|
205
|
+
if (res === undefined || !res.ok) {
|
|
201
206
|
return;
|
|
207
|
+
}
|
|
202
208
|
const reply = parseToolSessionReply(await res.json());
|
|
203
|
-
if (reply.done)
|
|
209
|
+
if (reply.done) {
|
|
204
210
|
return;
|
|
211
|
+
}
|
|
205
212
|
results = [];
|
|
206
213
|
for (const call of reply.calls) {
|
|
207
|
-
if (made >= TOOL_SESSION_LIMITS.MAX_CALLS)
|
|
214
|
+
if (made >= TOOL_SESSION_LIMITS.MAX_CALLS) {
|
|
208
215
|
return;
|
|
216
|
+
}
|
|
209
217
|
made += 1;
|
|
210
218
|
const at = now();
|
|
211
219
|
try {
|
|
212
220
|
const result = await tools.invoke(call.tool, call.args);
|
|
213
|
-
|
|
221
|
+
const end = now();
|
|
222
|
+
results.push({
|
|
223
|
+
seq: call.seq,
|
|
224
|
+
ok: true,
|
|
225
|
+
result,
|
|
226
|
+
ms: end - at,
|
|
227
|
+
startedAt: at,
|
|
228
|
+
endedAt: end,
|
|
229
|
+
});
|
|
214
230
|
}
|
|
215
231
|
catch (error) {
|
|
216
232
|
const message = error instanceof Error ? error.message : String(error);
|
|
217
|
-
|
|
233
|
+
const end = now();
|
|
234
|
+
results.push({
|
|
235
|
+
seq: call.seq,
|
|
236
|
+
ok: false,
|
|
237
|
+
error: message,
|
|
238
|
+
ms: end - at,
|
|
239
|
+
startedAt: at,
|
|
240
|
+
endedAt: end,
|
|
241
|
+
});
|
|
218
242
|
}
|
|
219
243
|
}
|
|
220
244
|
}
|
|
@@ -233,10 +257,12 @@ async function askWithRetries(send, sleep) {
|
|
|
233
257
|
catch {
|
|
234
258
|
res = undefined;
|
|
235
259
|
}
|
|
236
|
-
if (res !== undefined && !sessionAskRetryable(res.status))
|
|
260
|
+
if (res !== undefined && !sessionAskRetryable(res.status)) {
|
|
237
261
|
return res;
|
|
238
|
-
|
|
262
|
+
}
|
|
263
|
+
if (attempt >= TOOL_SESSION_LIMITS.RETRIES) {
|
|
239
264
|
return res;
|
|
265
|
+
}
|
|
240
266
|
await sleep(TOOL_SESSION_LIMITS.RETRY_BASE_MS * 2 ** attempt);
|
|
241
267
|
}
|
|
242
268
|
}
|
|
@@ -253,23 +279,27 @@ function film(frame, everyMs, send) {
|
|
|
253
279
|
const shoot = async () => {
|
|
254
280
|
try {
|
|
255
281
|
const shot = await frame();
|
|
256
|
-
if (shot === undefined)
|
|
282
|
+
if (shot === undefined) {
|
|
257
283
|
return;
|
|
284
|
+
}
|
|
258
285
|
const jpeg = Buffer.from(shot).toString('base64');
|
|
259
|
-
if (jpeg === last)
|
|
286
|
+
if (jpeg === last) {
|
|
260
287
|
return;
|
|
288
|
+
}
|
|
261
289
|
last = jpeg;
|
|
262
290
|
// Counted only once the platform took it: a refused picture is not a filmed drive.
|
|
263
|
-
if ((await send(jpeg)).ok)
|
|
291
|
+
if ((await send(jpeg)).ok) {
|
|
264
292
|
sent += 1;
|
|
293
|
+
}
|
|
265
294
|
}
|
|
266
295
|
catch {
|
|
267
296
|
// The next tick tries again.
|
|
268
297
|
}
|
|
269
298
|
};
|
|
270
299
|
const timer = setInterval(() => {
|
|
271
|
-
if (busy === undefined)
|
|
300
|
+
if (busy === undefined) {
|
|
272
301
|
busy = shoot().finally(() => (busy = undefined));
|
|
302
|
+
}
|
|
273
303
|
}, everyMs);
|
|
274
304
|
timer.unref();
|
|
275
305
|
return {
|
|
@@ -298,12 +328,14 @@ reopen) {
|
|
|
298
328
|
const own = [];
|
|
299
329
|
for (const tab of tabs) {
|
|
300
330
|
const key = await keyOf(tab.sessionId).catch(() => undefined);
|
|
301
|
-
if (key === apiKey)
|
|
331
|
+
if (key === apiKey) {
|
|
302
332
|
own.push(tab);
|
|
333
|
+
}
|
|
303
334
|
}
|
|
304
335
|
const picked = pickDriveSession(own, goal);
|
|
305
|
-
if (picked !== undefined)
|
|
336
|
+
if (picked !== undefined) {
|
|
306
337
|
return picked;
|
|
338
|
+
}
|
|
307
339
|
return (await reopen?.().catch(() => undefined)) ?? null;
|
|
308
340
|
}
|
|
309
341
|
/** The address of an http(s) URL, `host:port`, for matching a tab to the app a request names. */
|
|
@@ -331,15 +363,18 @@ export function pickDriveSession(tabs, goal) {
|
|
|
331
363
|
for (let i = 0; i < a.length; i += 1) {
|
|
332
364
|
const x = a[i] ?? 0;
|
|
333
365
|
const y = b[i] ?? 0;
|
|
334
|
-
if (x !== y)
|
|
366
|
+
if (x !== y) {
|
|
335
367
|
return x < y;
|
|
368
|
+
}
|
|
336
369
|
}
|
|
337
370
|
return false;
|
|
338
371
|
};
|
|
339
372
|
let best;
|
|
340
|
-
for (const tab of tabs)
|
|
341
|
-
if (best === undefined || better(rank(tab), rank(best)))
|
|
373
|
+
for (const tab of tabs) {
|
|
374
|
+
if (best === undefined || better(rank(tab), rank(best))) {
|
|
342
375
|
best = tab;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
343
378
|
return best?.sessionId;
|
|
344
379
|
}
|
|
345
380
|
/**
|
|
@@ -348,8 +383,9 @@ export function pickDriveSession(tabs, goal) {
|
|
|
348
383
|
* is `unknown`: "it ran and nothing threw" is not "the goal was reached".
|
|
349
384
|
*/
|
|
350
385
|
export function driveVerdict(out) {
|
|
351
|
-
if (out.error !== undefined)
|
|
386
|
+
if (out.error !== undefined) {
|
|
352
387
|
return 'unknown';
|
|
388
|
+
}
|
|
353
389
|
// A check that came back "no" is a failed drive whatever else held. A goal the model judged unmet
|
|
354
390
|
// is its opinion, not evidence: with no check against it the drive is not proved, never failed.
|
|
355
391
|
const graded = driveFlowStatus({
|
|
@@ -357,22 +393,28 @@ export function driveVerdict(out) {
|
|
|
357
393
|
failed: out.checks?.failed ?? 0,
|
|
358
394
|
goalMet: out.goalMet,
|
|
359
395
|
});
|
|
360
|
-
if (RunFlowStatus.FAIL === graded)
|
|
396
|
+
if (RunFlowStatus.FAIL === graded) {
|
|
361
397
|
return 'no';
|
|
398
|
+
}
|
|
362
399
|
// The quoted texts are looked for on the page the drive ENDED on. None there refutes the goal;
|
|
363
400
|
// some missing does not, because a goal that quotes where it starts ("from "Count is 0" to
|
|
364
401
|
// "Count is 1"") loses that text by working. Then the goal's own judgement decides, as below.
|
|
365
402
|
const goals = out.goals ?? [];
|
|
366
|
-
if (0 < goals.length && goals.every((g) => 'no' === g.verified))
|
|
403
|
+
if (0 < goals.length && goals.every((g) => 'no' === g.verified)) {
|
|
367
404
|
return 'no';
|
|
368
|
-
|
|
405
|
+
}
|
|
406
|
+
if (0 < goals.length && !goals.some((g) => 'yes' === g.verified)) {
|
|
369
407
|
return 'unknown';
|
|
408
|
+
}
|
|
370
409
|
// A yes needs a check that held behind it: the model saying the goal was met is not evidence.
|
|
371
|
-
if (!out.proved || RunFlowStatus.PASS !== graded)
|
|
410
|
+
if (!out.proved || RunFlowStatus.PASS !== graded) {
|
|
372
411
|
return 'unknown';
|
|
373
|
-
|
|
412
|
+
}
|
|
413
|
+
if (true === out.goalMet) {
|
|
374
414
|
return 'yes';
|
|
375
|
-
|
|
415
|
+
}
|
|
416
|
+
if (0 < goals.length && goals.every((g) => 'yes' === g.verified)) {
|
|
376
417
|
return 'yes';
|
|
418
|
+
}
|
|
377
419
|
return 'unknown';
|
|
378
420
|
}
|
|
@@ -11,16 +11,20 @@
|
|
|
11
11
|
* trial. A secret field travels by name only, and its value is filled in here before the call runs.
|
|
12
12
|
*/
|
|
13
13
|
import { PlatformRefusalCode, readRefusal, refusalSentence } from './refusal.js';
|
|
14
|
-
import { ReticleEnv, cloudUrlFrom } from '@reticlehq/core';
|
|
14
|
+
import { ReticleEnv, asRecord, cloudUrlFrom } from '@reticlehq/core';
|
|
15
15
|
import { DriveStoppedError, } from '../harness.js';
|
|
16
16
|
import { DRIVE_HEADER } from '../platform-config.js';
|
|
17
17
|
export { DRIVE_HEADER };
|
|
18
18
|
export const SERVER_DRIVER_NAME = 'server';
|
|
19
19
|
const RUNS_PATH = '/v1/harness/runs';
|
|
20
20
|
const TURN_TIMEOUT_MS = 110_000;
|
|
21
|
+
/** How long the run's spend is waited for once the drive ended. Its absence costs one number. */
|
|
22
|
+
const SPENT_TIMEOUT_MS = 5_000;
|
|
21
23
|
const RETRIES = 1;
|
|
22
24
|
/** The platform's code for a run whose project had autonomous driving switched off. */
|
|
23
25
|
const FINISH = 'finish';
|
|
26
|
+
/** The platform's status for a run it ended without finishing: the app could not be driven. */
|
|
27
|
+
const FAILED = 'failed';
|
|
24
28
|
/** The platform's url and this project's key, when the machine is linked. */
|
|
25
29
|
export function serverOptionsFromEnv(env) {
|
|
26
30
|
const url = cloudUrlFrom(env);
|
|
@@ -85,6 +89,20 @@ export function serverDriver(options) {
|
|
|
85
89
|
throw last instanceof Error ? last : new Error(String(last));
|
|
86
90
|
};
|
|
87
91
|
return {
|
|
92
|
+
// The run's own record, which carries its spend however the drive ended.
|
|
93
|
+
async spent() {
|
|
94
|
+
if (runId === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
const res = await doFetch(`${options.url}${RUNS_PATH}/${encodeURIComponent(runId)}`, {
|
|
97
|
+
method: 'GET',
|
|
98
|
+
headers: platformHeaders(options),
|
|
99
|
+
signal: AbortSignal.timeout(SPENT_TIMEOUT_MS),
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok)
|
|
102
|
+
return undefined;
|
|
103
|
+
const body = asRecord(JSON.parse(await res.text()));
|
|
104
|
+
return 'number' === typeof body['creditsSpent'] ? body['creditsSpent'] : undefined;
|
|
105
|
+
},
|
|
88
106
|
async turn(input) {
|
|
89
107
|
if (runId === undefined) {
|
|
90
108
|
const started = (await call(RUNS_PATH, {
|
|
@@ -118,6 +136,9 @@ export function serverDriver(options) {
|
|
|
118
136
|
turn = reply.turn + 1;
|
|
119
137
|
// The platform's spend, reported so the drive's cost is visible here as it is for a local one.
|
|
120
138
|
const usage = reply.usage === undefined ? {} : { usage: reply.usage };
|
|
139
|
+
// A run the platform ended as failed is a broken drive, and its summary says why.
|
|
140
|
+
if (reply.done && FAILED === reply.status)
|
|
141
|
+
throw new ServerHarnessError(reply.summary ?? reply.text, 502);
|
|
121
142
|
if (reply.done) {
|
|
122
143
|
// The platform has finished: `finish` ends the local loop the same way a local model would.
|
|
123
144
|
return {
|