@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
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A screenshot after every step: every page-touching action, every assert, every replayed step.
|
|
3
|
+
*
|
|
4
|
+
* The agent gets a PATH, never the image: reading it is the agent's choice, and an image forced
|
|
5
|
+
* into every result would cost more than every other field together. The path is named the moment
|
|
6
|
+
* the step's outcome is decided and the file lands right after, so a capture never holds a verdict
|
|
7
|
+
* back. The capture itself STARTS before this returns, which is what makes it a picture of the
|
|
8
|
+
* moment the verdict was decided rather than of whatever the next call did.
|
|
9
|
+
*
|
|
10
|
+
* Two sources, best first. `browser` is a real screenshot of a page this daemon drives (a leased
|
|
11
|
+
* tab, or the CDP provider). `rendered` is the page drawn by the SDK in-page — approximate, and
|
|
12
|
+
* labelled so. Which one the result names is a prediction from what worked last on this tab; the
|
|
13
|
+
* run's own record (`shots.jsonl`) says what actually happened. When the last attempt produced
|
|
14
|
+
* nothing at all, the next one is awaited, so the agent is told `{ error }` instead of a path to a
|
|
15
|
+
* file that will never exist.
|
|
16
|
+
*
|
|
17
|
+
* ponytail: the desktop shell's own window capture is not a source here. It returns a device-pixel
|
|
18
|
+
* PNG, and the daemon ships no JPEG encoder to shrink it to the step format; an Electron renderer
|
|
19
|
+
* draws itself fine in-page. Add it when the shells can hand back a JPEG.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { asRecord, ReticleCommand, ScreenshotSource, STEP_SCREENSHOT, ReticleTool, } from '@reticlehq/core';
|
|
24
|
+
import { runIdOfCall } from '../../judgement/runs/drive-run.js';
|
|
25
|
+
import { noteLanding } from '../../judgement/runs/artifact/run-store.js';
|
|
26
|
+
import { isValidRunId, stepShotDir } from '../../memory/project/dir/reticle-dir.js';
|
|
27
|
+
import { UPLOAD_TEXT_MAX } from '@reticlehq/core/artifacts';
|
|
28
|
+
const JPEG_EXT = '.jpg';
|
|
29
|
+
const TOOL_PREFIX = /^reticle_/;
|
|
30
|
+
const SEQ_WIDTH = 4;
|
|
31
|
+
/** A prior capture on this tab produced nothing — see the header. */
|
|
32
|
+
const NOTHING = 'none';
|
|
33
|
+
/** CDP's quality scale is 0–100. */
|
|
34
|
+
const CDP_QUALITY = Math.round(STEP_SCREENSHOT.JPEG_QUALITY * 100);
|
|
35
|
+
/** What worked last on each tab: the next result names that source. */
|
|
36
|
+
const lastSource = new Map();
|
|
37
|
+
/** The next sequence number per run directory, read from its record the first time. */
|
|
38
|
+
const nextSeq = new Map();
|
|
39
|
+
/**
|
|
40
|
+
* Width and height from a JPEG's frame header, or undefined when the bytes are not a JPEG.
|
|
41
|
+
* Read rather than trusted: the run record and the upload both state the size.
|
|
42
|
+
*/
|
|
43
|
+
export function jpegSize(bytes) {
|
|
44
|
+
if (bytes.length < 4 || 0xff !== bytes[0] || 0xd8 !== bytes[1])
|
|
45
|
+
return undefined;
|
|
46
|
+
let i = 2;
|
|
47
|
+
while (i + 9 < bytes.length) {
|
|
48
|
+
if (0xff !== bytes[i])
|
|
49
|
+
return undefined;
|
|
50
|
+
const marker = bytes[i + 1] ?? 0;
|
|
51
|
+
const length = ((bytes[i + 2] ?? 0) << 8) | (bytes[i + 3] ?? 0);
|
|
52
|
+
// The start-of-frame markers carry the size; 0xC4, 0xC8 and 0xCC share the range and are not frames.
|
|
53
|
+
if (marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker)) {
|
|
54
|
+
const height = ((bytes[i + 5] ?? 0) << 8) | (bytes[i + 6] ?? 0);
|
|
55
|
+
const width = ((bytes[i + 7] ?? 0) << 8) | (bytes[i + 8] ?? 0);
|
|
56
|
+
return width > 0 && height > 0 ? { width, height } : undefined;
|
|
57
|
+
}
|
|
58
|
+
i += 2 + length;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
/** A real screenshot of a page this daemon drives, or undefined when it drives none for this tab. */
|
|
63
|
+
async function browserPixels(deps, session) {
|
|
64
|
+
const opts = { jpegQuality: CDP_QUALITY, cssScale: true };
|
|
65
|
+
const leased = await deps.pool?.screenshotLease(session.id, opts);
|
|
66
|
+
if (leased !== undefined)
|
|
67
|
+
return leased;
|
|
68
|
+
// Only a provider driving THIS tab: a launched provider photographs the one page it owns whatever
|
|
69
|
+
// url it is handed, which is how a screenshot of one tab was once filed under another (#1407).
|
|
70
|
+
if (true !== (await deps.realInput?.isAvailableFor(session.url)))
|
|
71
|
+
return undefined;
|
|
72
|
+
return deps.realInput?.screenshot?.(session.url, opts);
|
|
73
|
+
}
|
|
74
|
+
/** The page drawn by the SDK. The answer is base64 in chunks — see `ReticleCommand.RENDER`. */
|
|
75
|
+
async function renderedPixels(session) {
|
|
76
|
+
const res = await session.command(ReticleCommand.RENDER, {});
|
|
77
|
+
if (!res.ok)
|
|
78
|
+
return res.error ?? 'the page did not answer';
|
|
79
|
+
const r = asRecord(res.result);
|
|
80
|
+
if (true !== r['ok'])
|
|
81
|
+
return 'string' === typeof r['reason'] ? r['reason'] : 'render failed';
|
|
82
|
+
const chunks = Array.isArray(r['chunks']) ? r['chunks'] : [];
|
|
83
|
+
return new Uint8Array(Buffer.from(chunks.filter((c) => 'string' === typeof c).join(''), 'base64'));
|
|
84
|
+
}
|
|
85
|
+
/** Best source first; the first that yields a readable JPEG wins. Never throws. */
|
|
86
|
+
async function capture(deps, session) {
|
|
87
|
+
const reasons = [];
|
|
88
|
+
try {
|
|
89
|
+
const real = await browserPixels(deps, session);
|
|
90
|
+
const size = real === undefined ? undefined : jpegSize(real);
|
|
91
|
+
if (real !== undefined && size !== undefined)
|
|
92
|
+
return { bytes: real, source: ScreenshotSource.BROWSER, ...size };
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
reasons.push(error instanceof Error ? error.message : String(error));
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const drawn = await renderedPixels(session);
|
|
99
|
+
if ('string' === typeof drawn)
|
|
100
|
+
reasons.push(`the page could not draw itself: ${drawn}`);
|
|
101
|
+
else {
|
|
102
|
+
const size = jpegSize(drawn);
|
|
103
|
+
if (size !== undefined)
|
|
104
|
+
return { bytes: drawn, source: ScreenshotSource.RENDERED, ...size };
|
|
105
|
+
reasons.push('the page drew something that is not a JPEG');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
reasons.push(error instanceof Error ? error.message : String(error));
|
|
110
|
+
}
|
|
111
|
+
return `no screenshot: ${reasons.join('; ')}`;
|
|
112
|
+
}
|
|
113
|
+
/** Which source this tab's next result names, or undefined when the last capture got nothing. */
|
|
114
|
+
async function predict(deps, session) {
|
|
115
|
+
const last = lastSource.get(session.id);
|
|
116
|
+
if (NOTHING === last)
|
|
117
|
+
return undefined;
|
|
118
|
+
if (last !== undefined)
|
|
119
|
+
return last;
|
|
120
|
+
if (true === deps.pool?.leasedSessionIds().includes(session.id))
|
|
121
|
+
return ScreenshotSource.BROWSER;
|
|
122
|
+
const driven = deps.realInput?.screenshot !== undefined &&
|
|
123
|
+
true === (await deps.realInput.isAvailableFor(session.url));
|
|
124
|
+
return driven ? ScreenshotSource.BROWSER : ScreenshotSource.RENDERED;
|
|
125
|
+
}
|
|
126
|
+
/** Highest sequence number this run has used, from its record — files can outlive a restart. */
|
|
127
|
+
async function allocate(fs, dir) {
|
|
128
|
+
let held = nextSeq.get(dir);
|
|
129
|
+
if (held === undefined) {
|
|
130
|
+
held = fs
|
|
131
|
+
.readFile(join(dir, STEP_SCREENSHOT.MANIFEST_FILE))
|
|
132
|
+
.then((text) => {
|
|
133
|
+
const seqs = text.split('\n').map((line) => {
|
|
134
|
+
try {
|
|
135
|
+
const seq = asRecord(JSON.parse(line))['seq'];
|
|
136
|
+
return 'number' === typeof seq ? seq : 0;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return { n: Math.max(0, ...seqs) };
|
|
143
|
+
})
|
|
144
|
+
.catch(() => ({ n: 0 }));
|
|
145
|
+
nextSeq.set(dir, held);
|
|
146
|
+
}
|
|
147
|
+
const counter = await held;
|
|
148
|
+
counter.n += 1;
|
|
149
|
+
return counter.n;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Photograph the tab a step just ran on. Returns the result field; never throws, because a
|
|
153
|
+
* screenshot is evidence about a step and must never be the reason the step's answer is lost.
|
|
154
|
+
*
|
|
155
|
+
* `step` names the flow step a replay was on, as `<flow>#<index>`, so the run can pin the picture
|
|
156
|
+
* to it; `verdict` says the call produced a verdict, so the run can pair the picture with its check.
|
|
157
|
+
*/
|
|
158
|
+
export async function takeStepShot(deps, sessionId, tool, opts = {}) {
|
|
159
|
+
try {
|
|
160
|
+
return await shoot(deps, deps.sessions.resolve(sessionId), tool, opts);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return { error: `no screenshot: ${error instanceof Error ? error.message : String(error)}` };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function shoot(deps, session, tool, { step, verdict }) {
|
|
167
|
+
const root = session.artifactRoot ?? deps.reticleRoot;
|
|
168
|
+
// Read before anything is awaited: the action this call's verdict was recorded on.
|
|
169
|
+
const actionId = true === verdict ? session.lastActionId : undefined;
|
|
170
|
+
const runId = runIdOfCall(session.id);
|
|
171
|
+
if (!isValidRunId(runId))
|
|
172
|
+
return { error: `no screenshot: unusable run id ${runId}` };
|
|
173
|
+
const dir = stepShotDir(root, runId);
|
|
174
|
+
const seq = await allocate(deps.fs, dir);
|
|
175
|
+
if (seq > STEP_SCREENSHOT.MAX_PER_RUN) {
|
|
176
|
+
return {
|
|
177
|
+
error: `no screenshot: this run already holds ${String(STEP_SCREENSHOT.MAX_PER_RUN)}, the most one run keeps`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// The first capture of a tab is one nothing has vouched for yet (the in-page renderer loads on
|
|
181
|
+
// first use): it is tried again once and waited for, so its answer is a path that landed or the
|
|
182
|
+
// reason. A first act was handed a path whose file never landed.
|
|
183
|
+
const fresh = !lastSource.has(session.id);
|
|
184
|
+
// Started before anything else is awaited: the picture is of THIS moment.
|
|
185
|
+
const taken = capture(deps, session).then((pixels) => fresh && 'string' === typeof pixels ? capture(deps, session) : pixels);
|
|
186
|
+
const predicted = fresh ? undefined : await predict(deps, session);
|
|
187
|
+
const file = `${String(seq).padStart(SEQ_WIDTH, '0')}-${tool.replace(TOOL_PREFIX, '')}${JPEG_EXT}`;
|
|
188
|
+
const path = join(dir, file);
|
|
189
|
+
const land = taken.then(async (pixels) => {
|
|
190
|
+
if ('string' === typeof pixels) {
|
|
191
|
+
lastSource.set(session.id, NOTHING);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
lastSource.set(session.id, pixels.source);
|
|
195
|
+
try {
|
|
196
|
+
await deps.fs.mkdir(dir);
|
|
197
|
+
await deps.fs.writeFileBytes(path, pixels.bytes);
|
|
198
|
+
const record = {
|
|
199
|
+
seq,
|
|
200
|
+
file,
|
|
201
|
+
tool,
|
|
202
|
+
...(step === undefined ? {} : { step: step.slice(0, UPLOAD_TEXT_MAX) }),
|
|
203
|
+
...(true === verdict ? { verdict } : {}),
|
|
204
|
+
...(actionId === undefined ? {} : { actionId }),
|
|
205
|
+
source: pixels.source,
|
|
206
|
+
width: pixels.width,
|
|
207
|
+
height: pixels.height,
|
|
208
|
+
sha256: createHash('sha256').update(pixels.bytes).digest('hex'),
|
|
209
|
+
};
|
|
210
|
+
await deps.fs.appendFile(join(dir, STEP_SCREENSHOT.MANIFEST_FILE), `${JSON.stringify(record)}\n`);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// A disk that cannot take the file loses one picture, never the step.
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
noteLanding(dir, land);
|
|
217
|
+
if (predicted !== undefined)
|
|
218
|
+
return { path, source: predicted };
|
|
219
|
+
// Nothing worked last time: wait, and tell the truth either way.
|
|
220
|
+
const pixels = await taken;
|
|
221
|
+
await land;
|
|
222
|
+
return 'string' === typeof pixels ? { error: pixels } : { path, source: pixels.source };
|
|
223
|
+
}
|
|
224
|
+
/** The replay hook that gives every reported flow step its screenshot, filed as `<flow>#<step>`. */
|
|
225
|
+
export function replayStepShooter(deps, sessionId) {
|
|
226
|
+
return async (result, flow) => {
|
|
227
|
+
result.screenshot = await takeStepShot(deps, sessionId, ReticleTool.REPLAY, {
|
|
228
|
+
step: `${flow}#${String(result.step)}`,
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { AppRuntime, ReticleCommand, RETICLE_CAPTURE_FILE_PREFIX, VISUAL_NO_PROVIDER_RECOMMENDATION, VisualReason, } from '@reticlehq/core';
|
|
2
|
+
import { AppRuntime, ReticleCommand, RETICLE_CAPTURE_FILE_PREFIX, VISUAL_NO_PROVIDER_RECOMMENDATION, RETICLE_TAURI_INERT_MARKER, TAURI_INERT_RECOMMENDATION, VisualReason, } from '@reticlehq/core';
|
|
3
3
|
import { ReticleTool } from '@reticlehq/core';
|
|
4
4
|
import { sessionIdShape } from '../../surface/tools/tool-kit.js';
|
|
5
5
|
import { ratioSchema } from '../../surface/tools/args/numeric-bounds.js';
|
|
6
|
-
import { asNumber, asRecord, asString } from '@reticlehq/core';
|
|
6
|
+
import { VISUAL_PIXEL_THRESHOLD, asNumber, asRecord, asString } from '@reticlehq/core';
|
|
7
7
|
import { diffPng } from './visual-diff.js';
|
|
8
8
|
import { VisualStore } from './visual-store.js';
|
|
9
9
|
/**
|
|
@@ -43,6 +43,12 @@ const noProvider = {
|
|
|
43
43
|
reason: VisualReason.NO_PROVIDER,
|
|
44
44
|
recommendation: VISUAL_NO_PROVIDER_RECOMMENDATION,
|
|
45
45
|
};
|
|
46
|
+
/** The answer when no pixels came back: the shell's own recommendation over the web one. */
|
|
47
|
+
function unavailable(reason, recommendation) {
|
|
48
|
+
if (recommendation !== undefined)
|
|
49
|
+
return { ok: false, reason, recommendation };
|
|
50
|
+
return reason === VisualReason.NO_PROVIDER ? noProvider : { ok: false, reason };
|
|
51
|
+
}
|
|
46
52
|
function asBox(value) {
|
|
47
53
|
const b = asRecord(asRecord(value)['box']);
|
|
48
54
|
const x = asNumber(b['x']);
|
|
@@ -94,8 +100,11 @@ async function desktopCapture(deps, sessionId, fullPage) {
|
|
|
94
100
|
if (r['ok'] !== true) {
|
|
95
101
|
// The shell refused a full-page capture it cannot do. That is a different answer from "capture
|
|
96
102
|
// broke", and the caller has to hear it — otherwise they read `fullPage` as honoured.
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
if (r['reason'] === VisualReason.FULL_PAGE_UNSUPPORTED)
|
|
104
|
+
return { reason: VisualReason.FULL_PAGE_UNSUPPORTED };
|
|
105
|
+
// A Tauri release build with the capture compiled out: say that, never the web answer.
|
|
106
|
+
return true === asString(r['reason'])?.includes(RETICLE_TAURI_INERT_MARKER)
|
|
107
|
+
? { reason: VisualReason.NO_PROVIDER, recommendation: TAURI_INERT_RECOMMENDATION }
|
|
99
108
|
: {};
|
|
100
109
|
}
|
|
101
110
|
const path = asString(r['path']);
|
|
@@ -232,7 +241,7 @@ async function capture(deps, sessionId, args) {
|
|
|
232
241
|
if (desktop.png !== undefined)
|
|
233
242
|
return { png: desktop.png, runtime: runtimeOf(deps, sessionId) };
|
|
234
243
|
if (desktop.reason !== undefined)
|
|
235
|
-
return
|
|
244
|
+
return desktop;
|
|
236
245
|
/*
|
|
237
246
|
* A LEASED page is a real browser page, so the pixels were always there — the visual tools simply
|
|
238
247
|
* had no route to them. Without this, `reticle_screenshot` answered "no provider" for every context
|
|
@@ -300,10 +309,9 @@ export const VISUAL_TOOLS = [
|
|
|
300
309
|
},
|
|
301
310
|
handler: async (deps, args) => {
|
|
302
311
|
const sessionId = asString(args['sessionId']);
|
|
303
|
-
const { png, reason, runtime } = await capture(deps, sessionId, args);
|
|
304
|
-
if (png === undefined)
|
|
305
|
-
return reason
|
|
306
|
-
}
|
|
312
|
+
const { png, reason, recommendation, runtime } = await capture(deps, sessionId, args);
|
|
313
|
+
if (png === undefined)
|
|
314
|
+
return unavailable(reason, recommendation);
|
|
307
315
|
const name = asString(args['name']) ?? 'default';
|
|
308
316
|
// Beside the app that was captured. The tool RETURNS this path to the agent, so a wrong root
|
|
309
317
|
// is not only a stray PNG — it is a path the agent is told to look at in a tree that has
|
|
@@ -337,7 +345,7 @@ export const VISUAL_TOOLS = [
|
|
|
337
345
|
maxRatio: ratioSchema.optional(),
|
|
338
346
|
threshold: ratioSchema
|
|
339
347
|
.optional()
|
|
340
|
-
.describe(
|
|
348
|
+
.describe(`Pixel difference threshold (0–1). Default: ${String(VISUAL_PIXEL_THRESHOLD)}.`),
|
|
341
349
|
...sessionIdShape,
|
|
342
350
|
},
|
|
343
351
|
outputSchema: {
|
|
@@ -360,10 +368,10 @@ export const VISUAL_TOOLS = [
|
|
|
360
368
|
// not the desktop one. Comparing across renderers is a confident wrong diff in one direction
|
|
361
369
|
// and a silently overwritten baseline in the other.
|
|
362
370
|
const sessionId = asString(args['sessionId']);
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
371
|
+
const captured = await capture(deps, sessionId, args);
|
|
372
|
+
const { png: current, runtime } = captured;
|
|
373
|
+
if (current === undefined)
|
|
374
|
+
return unavailable(captured.reason, captured.recommendation);
|
|
367
375
|
// A baseline from another runtime is not missing by accident — it is a different rendering, and
|
|
368
376
|
// "no baseline for this one" is the honest answer.
|
|
369
377
|
const baselineBytes = await store.readBaseline(baseline, runtime);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
/** The calling client's project directory, or undefined when this call did not name one. */
|
|
3
|
+
export declare function callingClientDirectory(): string | undefined;
|
|
4
|
+
export declare function runWithClientDirectory<T>(directory: string, fn: () => T): T;
|
|
5
|
+
/**
|
|
6
|
+
* The absolute directory a client named, or undefined when the header is absent or not a path.
|
|
7
|
+
*
|
|
8
|
+
* Relative values are dropped rather than resolved against the daemon's cwd: that would quietly
|
|
9
|
+
* turn "the client's project" back into "wherever this process was started".
|
|
10
|
+
*/
|
|
11
|
+
export declare function clientDirectoryFromHeader(headers: IncomingHttpHeaders): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* The directory this peer may name, or undefined.
|
|
14
|
+
*
|
|
15
|
+
* A remote client can present the pairing token, so the header is honored only from loopback.
|
|
16
|
+
* Otherwise it could point `init` at a path it chose.
|
|
17
|
+
*/
|
|
18
|
+
export declare function clientDirectoryFromPeer(loopback: boolean, headers: IncomingHttpHeaders): string | undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which project the MCP client that is calling right now is standing in.
|
|
3
|
+
*
|
|
4
|
+
* One daemon serves every project on the machine. `everConnected` and the first-run wiring used to
|
|
5
|
+
* read the directory the daemon was started in, so a second project's first `reticle_session` was
|
|
6
|
+
* answered with the first project's closed tab. The proxy knows its own cwd, which is the project
|
|
7
|
+
* the agent launched it from, and the HTTP door puts it on this context for the call.
|
|
8
|
+
*
|
|
9
|
+
* Same shape as `driven-by`: set around the request, read wherever the call is answered, gone when
|
|
10
|
+
* the call is. Absent means an in-process caller that has no separate project, and the daemon's own
|
|
11
|
+
* directory is still the answer.
|
|
12
|
+
*/
|
|
13
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
14
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
15
|
+
import { MCP_CLIENT_DIRECTORY_HEADER } from '@reticlehq/core';
|
|
16
|
+
const current = new AsyncLocalStorage();
|
|
17
|
+
/** The calling client's project directory, or undefined when this call did not name one. */
|
|
18
|
+
export function callingClientDirectory() {
|
|
19
|
+
return current.getStore();
|
|
20
|
+
}
|
|
21
|
+
export function runWithClientDirectory(directory, fn) {
|
|
22
|
+
return current.run(resolve(directory), fn);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The absolute directory a client named, or undefined when the header is absent or not a path.
|
|
26
|
+
*
|
|
27
|
+
* Relative values are dropped rather than resolved against the daemon's cwd: that would quietly
|
|
28
|
+
* turn "the client's project" back into "wherever this process was started".
|
|
29
|
+
*/
|
|
30
|
+
export function clientDirectoryFromHeader(headers) {
|
|
31
|
+
const raw = headers[MCP_CLIENT_DIRECTORY_HEADER];
|
|
32
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
33
|
+
if ('string' !== typeof value)
|
|
34
|
+
return undefined;
|
|
35
|
+
// The proxy percent-encodes the path. A raw absolute path still decodes to itself.
|
|
36
|
+
let decoded;
|
|
37
|
+
try {
|
|
38
|
+
decoded = decodeURIComponent(value);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
if (!isAbsolute(decoded))
|
|
44
|
+
return undefined;
|
|
45
|
+
return resolve(decoded);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The directory this peer may name, or undefined.
|
|
49
|
+
*
|
|
50
|
+
* A remote client can present the pairing token, so the header is honored only from loopback.
|
|
51
|
+
* Otherwise it could point `init` at a path it chose.
|
|
52
|
+
*/
|
|
53
|
+
export function clientDirectoryFromPeer(loopback, headers) {
|
|
54
|
+
if (!loopback)
|
|
55
|
+
return undefined;
|
|
56
|
+
return clientDirectoryFromHeader(headers);
|
|
57
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AgentClient } from '@reticlehq/core/telemetry';
|
|
2
|
+
export declare function agentLabel(name: string): string;
|
|
3
|
+
/** The closed telemetry value for a client name: never the name itself, which is the client's text. */
|
|
4
|
+
export declare function agentClientOf(name: string | undefined): AgentClient | undefined;
|
|
5
|
+
/** An MCP connection finished its handshake as `name`. */
|
|
6
|
+
export declare function noteAgentAttached(attachId: string, name: string): void;
|
|
7
|
+
/** That connection closed. */
|
|
8
|
+
export declare function noteAgentDetached(attachId: string): void;
|
|
9
|
+
/** The friendly names of the agents attached now, each once. */
|
|
10
|
+
export declare function connectedAgents(): string[];
|
|
11
|
+
/** Hear an agent attach or leave. Returns the unsubscribe. */
|
|
12
|
+
export declare function onAgentsChange(listener: () => void): () => void;
|
|
13
|
+
/** Run a tool call as the agent on connection `attachId`, so what it reads is credited to it. */
|
|
14
|
+
export declare function runAsAgent<T>(attachId: string | undefined, fn: () => Promise<T>): Promise<T>;
|
|
15
|
+
/**
|
|
16
|
+
* The agent the current call came from; else the only agent attached; else undefined. Two agents
|
|
17
|
+
* and no call to say which is unknown, never a guess.
|
|
18
|
+
*/
|
|
19
|
+
export declare function callingAgent(): string | undefined;
|
|
20
|
+
/** The MCP connection the current call came over, or undefined outside one. */
|
|
21
|
+
export declare function callingAttachId(): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The client whose call this is, and nobody else: no fallback to the only agent attached. A run or
|
|
24
|
+
* a journaled action is credited by this — a dashboard Re-verify, which no agent made, was credited
|
|
25
|
+
* to `claude-code` because it was the client last connected.
|
|
26
|
+
*/
|
|
27
|
+
export declare function callerClientName(): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* The raw client name behind `callingAgent()` — the id a run is credited to, the same string
|
|
30
|
+
* `mcpClientIdentity()` reports, so one agent is one facet on the dashboard.
|
|
31
|
+
*/
|
|
32
|
+
export declare function callingClientName(): string | undefined;
|
|
33
|
+
/** Tests only. */
|
|
34
|
+
export declare function forgetAgents(): void;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which coding agents are attached to this daemon, and which one the current tool call came from.
|
|
3
|
+
*
|
|
4
|
+
* Read from MCP `initialize` (`clientInfo.name`), the only honest source: see `client-identity.ts`.
|
|
5
|
+
* Kept per connection, so the HUD can say "Connected: Claude Code" and a note it sent can be marked
|
|
6
|
+
* as seen by the agent whose call actually took it. A pure sink, like the rest of `hooks/`.
|
|
7
|
+
*/
|
|
8
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
9
|
+
import { AgentClient } from '@reticlehq/core/telemetry';
|
|
10
|
+
/**
|
|
11
|
+
* Friendly names for the clients people know, matched as a lowercase substring of the name the
|
|
12
|
+
* client sent, and the closed telemetry value each one reports as. Ordered: `cursor-vscode` is
|
|
13
|
+
* Cursor, not VS Code. Anything else keeps its own name, and reports as `other`.
|
|
14
|
+
*/
|
|
15
|
+
const AGENT_LABELS = [
|
|
16
|
+
['claude-code', 'Claude Code', AgentClient.CLAUDE_CODE],
|
|
17
|
+
['claude-ai', 'Claude Desktop', AgentClient.CLAUDE_DESKTOP],
|
|
18
|
+
['codex', 'Codex', AgentClient.CODEX],
|
|
19
|
+
['cursor', 'Cursor', AgentClient.CURSOR],
|
|
20
|
+
['gemini', 'Gemini CLI', AgentClient.GEMINI_CLI],
|
|
21
|
+
['antigravity', 'Antigravity', AgentClient.ANTIGRAVITY],
|
|
22
|
+
['windsurf', 'Windsurf', AgentClient.WINDSURF],
|
|
23
|
+
['cline', 'Cline', AgentClient.CLINE],
|
|
24
|
+
['opencode', 'OpenCode', AgentClient.OPENCODE],
|
|
25
|
+
['zed', 'Zed', AgentClient.ZED],
|
|
26
|
+
['visual studio code', 'VS Code', AgentClient.VSCODE],
|
|
27
|
+
['vscode', 'VS Code', AgentClient.VSCODE],
|
|
28
|
+
];
|
|
29
|
+
const MAX_LABEL = 64;
|
|
30
|
+
/** The name each connection's handshake sent, verbatim; labels are derived on read. */
|
|
31
|
+
const byAttach = new Map();
|
|
32
|
+
const listeners = new Set();
|
|
33
|
+
const calling = new AsyncLocalStorage();
|
|
34
|
+
function knownAgent(name) {
|
|
35
|
+
const lower = name.toLowerCase();
|
|
36
|
+
return AGENT_LABELS.find(([needle]) => lower.includes(needle));
|
|
37
|
+
}
|
|
38
|
+
export function agentLabel(name) {
|
|
39
|
+
return (knownAgent(name)?.[1] ?? name).slice(0, MAX_LABEL);
|
|
40
|
+
}
|
|
41
|
+
/** The closed telemetry value for a client name: never the name itself, which is the client's text. */
|
|
42
|
+
export function agentClientOf(name) {
|
|
43
|
+
if (name === undefined || 0 === name.trim().length)
|
|
44
|
+
return undefined;
|
|
45
|
+
return knownAgent(name)?.[2] ?? AgentClient.OTHER;
|
|
46
|
+
}
|
|
47
|
+
/** An MCP connection finished its handshake as `name`. */
|
|
48
|
+
export function noteAgentAttached(attachId, name) {
|
|
49
|
+
byAttach.set(attachId, name);
|
|
50
|
+
changed();
|
|
51
|
+
}
|
|
52
|
+
/** That connection closed. */
|
|
53
|
+
export function noteAgentDetached(attachId) {
|
|
54
|
+
if (byAttach.delete(attachId))
|
|
55
|
+
changed();
|
|
56
|
+
}
|
|
57
|
+
/** The friendly names of the agents attached now, each once. */
|
|
58
|
+
export function connectedAgents() {
|
|
59
|
+
return [...new Set([...byAttach.values()].map(agentLabel))];
|
|
60
|
+
}
|
|
61
|
+
/** Hear an agent attach or leave. Returns the unsubscribe. */
|
|
62
|
+
export function onAgentsChange(listener) {
|
|
63
|
+
listeners.add(listener);
|
|
64
|
+
return () => listeners.delete(listener);
|
|
65
|
+
}
|
|
66
|
+
/** Run a tool call as the agent on connection `attachId`, so what it reads is credited to it. */
|
|
67
|
+
export function runAsAgent(attachId, fn) {
|
|
68
|
+
return attachId === undefined ? fn() : calling.run(attachId, fn);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The agent the current call came from; else the only agent attached; else undefined. Two agents
|
|
72
|
+
* and no call to say which is unknown, never a guess.
|
|
73
|
+
*/
|
|
74
|
+
export function callingAgent() {
|
|
75
|
+
const name = callingClientName();
|
|
76
|
+
return name === undefined ? undefined : agentLabel(name);
|
|
77
|
+
}
|
|
78
|
+
/** The MCP connection the current call came over, or undefined outside one. */
|
|
79
|
+
export function callingAttachId() {
|
|
80
|
+
return calling.getStore();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The client whose call this is, and nobody else: no fallback to the only agent attached. A run or
|
|
84
|
+
* a journaled action is credited by this — a dashboard Re-verify, which no agent made, was credited
|
|
85
|
+
* to `claude-code` because it was the client last connected.
|
|
86
|
+
*/
|
|
87
|
+
export function callerClientName() {
|
|
88
|
+
const own = calling.getStore();
|
|
89
|
+
return own === undefined ? undefined : byAttach.get(own);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The raw client name behind `callingAgent()` — the id a run is credited to, the same string
|
|
93
|
+
* `mcpClientIdentity()` reports, so one agent is one facet on the dashboard.
|
|
94
|
+
*/
|
|
95
|
+
export function callingClientName() {
|
|
96
|
+
const own = calling.getStore();
|
|
97
|
+
const name = own === undefined ? undefined : byAttach.get(own);
|
|
98
|
+
if (name !== undefined)
|
|
99
|
+
return name;
|
|
100
|
+
const all = [...new Set(byAttach.values())];
|
|
101
|
+
return 1 === all.length ? all[0] : undefined;
|
|
102
|
+
}
|
|
103
|
+
/** Tests only. */
|
|
104
|
+
export function forgetAgents() {
|
|
105
|
+
byAttach.clear();
|
|
106
|
+
listeners.clear();
|
|
107
|
+
}
|
|
108
|
+
function changed() {
|
|
109
|
+
for (const listener of listeners) {
|
|
110
|
+
try {
|
|
111
|
+
listener();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* a listener never breaks the connection it listens to */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export declare function runDrivenBy<T>(by: DrivenBy, fn: () => Promise<T
|
|
1
|
+
import { type DrivenBy, type OutwardAction } from '@reticlehq/core';
|
|
2
|
+
export declare function runDrivenBy<T>(by: DrivenBy, fn: () => Promise<T>,
|
|
3
|
+
/** What the PERSON asked for, in their own words: the only text that allows an outward action. */
|
|
4
|
+
asked?: string): Promise<T>;
|
|
3
5
|
/** The Harness drive this call is part of, or undefined when the connected agent made it. */
|
|
4
6
|
export declare function currentDrivenBy(): DrivenBy | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Bind a callback to the Harness drive running NOW, so it is still the Harness's when it fires later.
|
|
9
|
+
*
|
|
10
|
+
* A wait re-checks its predicate from a page-event listener and a timer, outside the awaited chain,
|
|
11
|
+
* where this context does not reach. Those re-checks went to the page untagged and the HUD filed the
|
|
12
|
+
* Harness's own "Finding…" rows in the Agent Log. Returns `fn` unchanged outside a drive.
|
|
13
|
+
*/
|
|
14
|
+
export declare function bindDrivenBy<A extends unknown[]>(fn: (...args: A) => void): (...args: A) => void;
|
|
15
|
+
/** Replay a saved flow: its outward steps are the journey, not a model's choice. */
|
|
16
|
+
export declare function runSavedFlow<T>(fn: () => Promise<T>): Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* The outward actions (invite, send, delete, pay…) this call may take, or undefined when it is not
|
|
19
|
+
* gated: an agent's call, or a saved flow's step. A Harness call a model or a planner chose may take
|
|
20
|
+
* only what its journey asks for in so many words — the Harness pressed Invite in a customer's
|
|
21
|
+
* workspace when its journey said "she does not send an invite".
|
|
22
|
+
*/
|
|
23
|
+
export declare function harnessOutwardAllowed(): OutwardAction[] | undefined;
|
package/dist/hooks/driven-by.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { outwardActionsAsked } from '@reticlehq/core';
|
|
2
3
|
/**
|
|
3
4
|
* Which Harness drive the current tool call belongs to, carried across every await under it.
|
|
4
5
|
*
|
|
@@ -7,10 +8,49 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
7
8
|
* The context is set once around each Harness call and read where the journal writes the action.
|
|
8
9
|
*/
|
|
9
10
|
const current = new AsyncLocalStorage();
|
|
10
|
-
export function runDrivenBy(by, fn
|
|
11
|
-
|
|
11
|
+
export function runDrivenBy(by, fn,
|
|
12
|
+
/** What the PERSON asked for, in their own words: the only text that allows an outward action. */
|
|
13
|
+
asked) {
|
|
14
|
+
return current.run(by, () => (asked === undefined ? fn() : askedStore.run(asked, fn)));
|
|
12
15
|
}
|
|
16
|
+
/** The person's own request for the Harness drive running now, when they made one. */
|
|
17
|
+
const askedStore = new AsyncLocalStorage();
|
|
13
18
|
/** The Harness drive this call is part of, or undefined when the connected agent made it. */
|
|
14
19
|
export function currentDrivenBy() {
|
|
15
20
|
return current.getStore();
|
|
16
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Bind a callback to the Harness drive running NOW, so it is still the Harness's when it fires later.
|
|
24
|
+
*
|
|
25
|
+
* A wait re-checks its predicate from a page-event listener and a timer, outside the awaited chain,
|
|
26
|
+
* where this context does not reach. Those re-checks went to the page untagged and the HUD filed the
|
|
27
|
+
* Harness's own "Finding…" rows in the Agent Log. Returns `fn` unchanged outside a drive.
|
|
28
|
+
*/
|
|
29
|
+
export function bindDrivenBy(fn) {
|
|
30
|
+
const by = current.getStore();
|
|
31
|
+
if (by === undefined)
|
|
32
|
+
return fn;
|
|
33
|
+
return (...args) => {
|
|
34
|
+
current.run(by, () => fn(...args));
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Set while a SAVED flow replays: its steps were asked for by whoever recorded them. */
|
|
38
|
+
const savedFlow = new AsyncLocalStorage();
|
|
39
|
+
/** Replay a saved flow: its outward steps are the journey, not a model's choice. */
|
|
40
|
+
export function runSavedFlow(fn) {
|
|
41
|
+
return savedFlow.run(true, fn);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The outward actions (invite, send, delete, pay…) this call may take, or undefined when it is not
|
|
45
|
+
* gated: an agent's call, or a saved flow's step. A Harness call a model or a planner chose may take
|
|
46
|
+
* only what its journey asks for in so many words — the Harness pressed Invite in a customer's
|
|
47
|
+
* workspace when its journey said "she does not send an invite".
|
|
48
|
+
*/
|
|
49
|
+
export function harnessOutwardAllowed() {
|
|
50
|
+
const by = current.getStore();
|
|
51
|
+
if (by === undefined || true === savedFlow.getStore())
|
|
52
|
+
return undefined;
|
|
53
|
+
// The person's words, never a planner's: a plan the platform invented ("press Invite") is not a
|
|
54
|
+
// request anybody made.
|
|
55
|
+
return outwardActionsAsked(askedStore.getStore() ?? '');
|
|
56
|
+
}
|
package/dist/hooks/hook-bus.d.ts
CHANGED
|
@@ -51,3 +51,7 @@ export declare function emitHook(payload: HookPayload): void;
|
|
|
51
51
|
export declare function hookListenerCount(event: HookEventName): number;
|
|
52
52
|
/** Drop every listener. Tests, and a daemon tearing down its own wiring. */
|
|
53
53
|
export declare function resetHooks(): void;
|
|
54
|
+
/** Register "write what is pending now". Returns the unregister. */
|
|
55
|
+
export declare function onPendingWrites(flush: () => Promise<void>): () => void;
|
|
56
|
+
/** Write everything held back, now. */
|
|
57
|
+
export declare function flushPendingWrites(): Promise<void>;
|
package/dist/hooks/hook-bus.js
CHANGED
|
@@ -104,3 +104,21 @@ export function resetHooks() {
|
|
|
104
104
|
everyEvent.clear();
|
|
105
105
|
reported.clear();
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Writes held back on a timer, which a push must see through before it sends anything.
|
|
109
|
+
*
|
|
110
|
+
* A drive's run is written a few seconds after its last verdict, so a burst is folded once. A push
|
|
111
|
+
* asked for straight after a drive (`session end`, then `reticle_project { push: true }`) found no
|
|
112
|
+
* run to send and answered "0 of 0"; the run landed seconds later. A pure sink, like the rest of
|
|
113
|
+
* `hooks/`: the writer registers, the pusher flushes, neither imports the other.
|
|
114
|
+
*/
|
|
115
|
+
const flushers = new Set();
|
|
116
|
+
/** Register "write what is pending now". Returns the unregister. */
|
|
117
|
+
export function onPendingWrites(flush) {
|
|
118
|
+
flushers.add(flush);
|
|
119
|
+
return () => flushers.delete(flush);
|
|
120
|
+
}
|
|
121
|
+
/** Write everything held back, now. */
|
|
122
|
+
export async function flushPendingWrites() {
|
|
123
|
+
await Promise.all([...flushers].map((flush) => flush()));
|
|
124
|
+
}
|