@reticlehq/server 3.5.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/README.md +1 -1
- package/SKILL.md +123 -116
- package/dist/chromium-launch-options.d.ts +3 -1
- package/dist/chromium-launch-options.js +2 -1
- 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 +19 -2
- package/dist/command/cli/adhoc-verdict.js +76 -9
- 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/answers/open-note.d.ts +2 -0
- package/dist/command/cli/answers/open-note.js +22 -0
- 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 +31 -112
- 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 +35 -5
- package/dist/command/cli/cli-verify.js +114 -35
- package/dist/command/cli/cloud-cli.d.ts +13 -0
- package/dist/command/cli/cloud-cli.js +192 -341
- 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 +22 -0
- package/dist/command/cli/cloud-login.js +96 -8
- 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/browser/chromium-hint.d.ts +46 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
- 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/drive/drive-attach.d.ts +27 -2
- package/dist/command/cli/drive/drive-attach.js +71 -4
- package/dist/command/cli/drive/drive-command.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +13 -7
- 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 +15 -3
- 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.d.ts +10 -0
- package/dist/command/cli/ports/port-holder.js +25 -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 +35 -12
- 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 -181
- 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 +20 -0
- package/dist/command/cli.js +130 -104
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
- package/dist/command/daemon/daemon-resilience.js +4 -4
- package/dist/command/daemon/daemon-resolve.d.ts +23 -1
- package/dist/command/daemon/daemon-resolve.js +79 -9
- package/dist/command/setup/agent-writer.d.ts +1 -1
- package/dist/command/setup/agent-writer.js +3 -2
- 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/connect-lease.d.ts +1 -0
- package/dist/command/setup/bringup/connect-lease.js +1 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +8 -0
- package/dist/command/setup/bringup/dev-server-wait.js +19 -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 +14 -1
- package/dist/command/setup/init/init-port.js +32 -2
- package/dist/command/setup/init/init-runtime.d.ts +22 -2
- package/dist/command/setup/init/init-runtime.js +114 -38
- package/dist/command/setup/node-effects.d.ts +2 -0
- package/dist/command/setup/node-effects.js +38 -9
- 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 +2 -1
- package/dist/command/setup/run-setup.d.ts +34 -9
- package/dist/command/setup/run-setup.js +65 -51
- package/dist/command/setup/setup-command.d.ts +25 -1
- package/dist/command/setup/setup-command.js +59 -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 +49 -4
- package/dist/features/crawl/crawl-tools.js +4 -0
- package/dist/features/crawl/crawl.d.ts +6 -0
- package/dist/features/crawl/crawl.js +23 -2
- package/dist/features/exhaust/ledger.d.ts +21 -14
- package/dist/features/exhaust/ledger.js +35 -9
- package/dist/features/harness/app-findings.d.ts +2 -0
- package/dist/features/harness/app-findings.js +52 -0
- package/dist/features/harness/drive-report.d.ts +61 -1
- package/dist/features/harness/drive-report.js +394 -26
- 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 +10 -14
- package/dist/features/harness/drivers.js +10 -14
- 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/fill-values.d.ts +3 -73
- package/dist/features/harness/fill-values.js +3 -169
- package/dist/features/harness/goals.d.ts +82 -0
- package/dist/features/harness/goals.js +330 -0
- package/dist/features/harness/harness.d.ts +51 -1
- package/dist/features/harness/harness.js +207 -6
- package/dist/features/harness/platform/drive-target.d.ts +30 -0
- package/dist/features/harness/platform/drive-target.js +81 -0
- package/dist/features/harness/platform/local-apps.d.ts +127 -0
- package/dist/features/harness/platform/local-apps.js +280 -0
- 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 +186 -0
- package/dist/features/harness/platform/remote-drive.js +378 -0
- package/dist/features/harness/platform/script.d.ts +94 -0
- package/dist/features/harness/platform/script.js +260 -0
- package/dist/features/harness/platform/server-driver.d.ts +54 -0
- package/dist/features/harness/platform/server-driver.js +182 -0
- package/dist/features/harness/platform-config.d.ts +41 -1
- package/dist/features/harness/platform-config.js +106 -9
- package/dist/features/harness/route-guess.d.ts +13 -0
- package/dist/features/harness/route-guess.js +49 -0
- package/dist/features/harness/script/app-controls.d.ts +5 -0
- package/dist/features/harness/script/app-controls.js +40 -0
- package/dist/features/harness/script/lane-ids.d.ts +14 -0
- package/dist/features/harness/script/lane-ids.js +16 -0
- package/dist/features/harness/script/script-run.d.ts +70 -0
- package/dist/features/harness/script/script-run.js +606 -0
- 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.d.ts +10 -1
- package/dist/features/visual/visual-tools.js +62 -17
- 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 +23 -0
- package/dist/hooks/driven-by.js +56 -0
- package/dist/hooks/hook-bus.d.ts +4 -0
- package/dist/hooks/hook-bus.js +18 -0
- package/dist/hooks/hook-commands.js +15 -4
- package/dist/hooks/tool-log.d.ts +9 -0
- package/dist/hooks/tool-log.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +80 -243
- 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 +25 -3
- 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.d.ts +1 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +37 -11
- package/dist/judgement/runs/artifact/run-store.d.ts +14 -2
- package/dist/judgement/runs/artifact/run-store.js +195 -3
- 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 +71 -1
- package/dist/judgement/runs/drive-run.js +317 -5
- package/dist/judgement/runs/repair-prompt.js +1 -1
- package/dist/judgement/runs/resume.d.ts +44 -0
- package/dist/judgement/runs/resume.js +137 -0
- package/dist/judgement/runs/reticle-runner.js +13 -3
- 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 +5 -2
- package/dist/judgement/runs/verification-sync.js +40 -18
- package/dist/language/flows/change/gate.d.ts +10 -0
- package/dist/language/flows/change/gate.js +22 -0
- package/dist/language/flows/decision.js +36 -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 +61 -10
- package/dist/language/flows/flow-anchor.js +98 -6
- package/dist/language/flows/flow-cross-step.d.ts +15 -0
- package/dist/language/flows/flow-cross-step.js +21 -0
- 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 +45 -14
- package/dist/language/flows/flow-mutate-tools.js +5 -3
- package/dist/language/flows/flow-replay-run.d.ts +23 -9
- package/dist/language/flows/flow-replay-run.js +115 -110
- package/dist/language/flows/flow-replay-types.d.ts +1 -1
- package/dist/language/flows/flow-replay.d.ts +40 -1
- package/dist/language/flows/flow-replay.js +110 -22
- package/dist/language/flows/flow-result.d.ts +22 -1
- package/dist/language/flows/flow-result.js +47 -2
- package/dist/language/flows/flow-scope.js +5 -1
- package/dist/language/flows/flow-step-runners.d.ts +22 -2
- package/dist/language/flows/flow-step-runners.js +247 -47
- package/dist/language/flows/flow-store-for-session.d.ts +2 -2
- package/dist/language/flows/flow-store-for-session.js +7 -3
- package/dist/language/flows/flow-tools.d.ts +10 -0
- package/dist/language/flows/flow-tools.js +112 -89
- package/dist/language/flows/flows.d.ts +16 -1
- package/dist/language/flows/flows.js +74 -12
- package/dist/language/flows/heal-run.js +6 -4
- package/dist/language/flows/narrow-write.d.ts +7 -0
- package/dist/language/flows/narrow-write.js +36 -1
- package/dist/language/flows/recording/flake.d.ts +2 -2
- package/dist/language/flows/recording/recording-backtrack.js +14 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +58 -4
- package/dist/language/flows/recording/tape/recordings.js +95 -13
- package/dist/language/flows/replay-from-hud.d.ts +20 -0
- package/dist/language/flows/replay-from-hud.js +51 -0
- package/dist/language/flows/replay.d.ts +9 -5
- package/dist/language/flows/replay.js +56 -15
- package/dist/language/flows/single-replay.d.ts +7 -0
- package/dist/language/flows/single-replay.js +26 -0
- package/dist/language/flows/suite/suite-flakes.js +2 -0
- package/dist/language/flows/suite-selection.d.ts +2 -15
- package/dist/language/flows/suite-selection.js +4 -3
- package/dist/language/flows/verify-change-tools.d.ts +1 -0
- package/dist/language/flows/verify-change-tools.js +202 -191
- package/dist/launch-chromium.d.ts +27 -5
- package/dist/launch-chromium.js +38 -8
- package/dist/memory/cloud/cloud-config.d.ts +22 -1
- package/dist/memory/cloud/cloud-config.js +34 -5
- package/dist/memory/cloud/cloud-sync.d.ts +17 -14
- package/dist/memory/cloud/cloud-sync.js +15 -4
- package/dist/memory/cloud/harness-config.d.ts +75 -1
- package/dist/memory/cloud/harness-config.js +115 -24
- package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
- package/dist/memory/cloud/hud-notices-source.js +128 -0
- package/dist/memory/cloud/memory-scope.d.ts +161 -0
- package/dist/memory/cloud/memory-scope.js +227 -0
- package/dist/memory/cloud/sync-cycle.d.ts +44 -7
- package/dist/memory/cloud/sync-cycle.js +129 -15
- package/dist/memory/cloud/sync-daemon.d.ts +11 -0
- package/dist/memory/cloud/sync-daemon.js +121 -9
- package/dist/memory/cloud/sync-disk.d.ts +1 -1
- package/dist/memory/cloud/sync-disk.js +31 -10
- 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 +34 -0
- package/dist/memory/impact/account-watch.js +59 -0
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +12 -2
- package/dist/memory/impact/impact-store.d.ts +17 -4
- package/dist/memory/impact/impact-store.js +169 -63
- 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 +14 -2
- package/dist/memory/intent/intent-store.d.ts +1 -0
- package/dist/memory/intent/intent-tools.js +122 -4
- 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/envelope.d.ts +8 -8
- package/dist/memory/journal/journal-recorder.js +52 -3
- package/dist/memory/journal/on-disk/retention.d.ts +8 -0
- package/dist/memory/journal/on-disk/retention.js +32 -2
- package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
- package/dist/memory/journal/on-disk/workspace-tiers.js +9 -4
- package/dist/memory/journal/session-end.d.ts +15 -1
- package/dist/memory/journal/session-end.js +42 -9
- package/dist/memory/project/artifact-root-resolver.d.ts +26 -1
- package/dist/memory/project/artifact-root-resolver.js +125 -16
- package/dist/memory/project/artifact-root.d.ts +46 -8
- package/dist/memory/project/artifact-root.js +89 -10
- package/dist/memory/project/dir/reticle-dir.d.ts +18 -2
- package/dist/memory/project/dir/reticle-dir.js +26 -3
- package/dist/memory/project/link-directory.d.ts +15 -0
- package/dist/memory/project/link-directory.js +44 -0
- package/dist/memory/project/project-tools.js +22 -3
- 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 +26 -1
- package/dist/memory/project/session-root.js +49 -5
- package/dist/memory/project/sync-status.d.ts +85 -0
- package/dist/memory/project/sync-status.js +224 -0
- package/dist/memory/recall/memory-tools.js +47 -2
- 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.d.ts +19 -0
- package/dist/memory/recall/project-memory.js +80 -13
- package/dist/portal/bridge/bridge.d.ts +9 -3
- package/dist/portal/bridge/bridge.js +82 -22
- package/dist/portal/bridge/harness-request.d.ts +23 -0
- package/dist/portal/bridge/harness-request.js +27 -0
- package/dist/portal/bridge/pairing-token.d.ts +9 -0
- package/dist/portal/bridge/pairing-token.js +17 -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 +83 -10
- package/dist/portal/input/real-input.js +218 -30
- package/dist/portal/input/viewport-tools.js +3 -3
- package/dist/portal/pool/browser-pool.d.ts +70 -9
- package/dist/portal/pool/browser-pool.js +190 -37
- 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 +35 -0
- package/dist/portal/pool/playwright-launcher.js +64 -3
- package/dist/portal/pool/pool-contract.d.ts +28 -5
- 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.d.ts +8 -0
- package/dist/portal/session/command-payload.js +24 -0
- 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 +21 -0
- package/dist/portal/session/dev-server/dev-command.js +87 -12
- package/dist/portal/session/facts/departure-note.d.ts +22 -0
- package/dist/portal/session/facts/departure-note.js +47 -0
- 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/facts/session-replaced.d.ts +5 -0
- package/dist/portal/session/facts/session-replaced.js +5 -0
- package/dist/portal/session/first-run-wiring.d.ts +42 -0
- package/dist/portal/session/first-run-wiring.js +132 -0
- 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/human/notes-ledger.d.ts +20 -0
- package/dist/portal/session/human/notes-ledger.js +55 -0
- package/dist/portal/session/human/review-store.d.ts +12 -0
- package/dist/portal/session/human/review-store.js +18 -10
- package/dist/portal/session/live-control-tools.d.ts +0 -13
- package/dist/portal/session/live-control-tools.js +107 -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 +8 -1
- package/dist/portal/session/no-session-diagnosis.js +46 -9
- package/dist/portal/session/no-session-next-action.d.ts +31 -0
- package/dist/portal/session/no-session-next-action.js +159 -60
- package/dist/portal/session/no-session-watch.d.ts +14 -1
- package/dist/portal/session/no-session-watch.js +227 -17
- package/dist/portal/session/presence/session-lease.js +1 -1
- package/dist/portal/session/presence/session-mirror.js +3 -0
- package/dist/portal/session/recorded-gaps.d.ts +97 -0
- package/dist/portal/session/recorded-gaps.js +251 -0
- package/dist/portal/session/serving-directory.d.ts +27 -0
- package/dist/portal/session/serving-directory.js +45 -0
- package/dist/portal/session/session-health.d.ts +18 -0
- package/dist/portal/session/session-health.js +9 -1
- package/dist/portal/session/session-info.d.ts +8 -1
- package/dist/portal/session/session-info.js +8 -1
- package/dist/portal/session/session-manager.d.ts +39 -0
- package/dist/portal/session/session-manager.js +98 -11
- package/dist/portal/session/session-reaper.js +4 -1
- package/dist/portal/session/session-successor.d.ts +13 -0
- package/dist/portal/session/session-successor.js +1 -1
- package/dist/portal/session/session-tools.js +26 -3
- package/dist/portal/session/session.d.ts +22 -30
- package/dist/portal/session/session.js +81 -79
- 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 +12 -0
- package/dist/start-wiring.d.ts +63 -0
- package/dist/start-wiring.js +169 -0
- package/dist/status-payload.d.ts +23 -1
- package/dist/status-payload.js +36 -1
- package/dist/surface/http-server.js +18 -2
- 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 +9 -0
- package/dist/surface/mcp/mcp-proxy.js +65 -13
- package/dist/surface/mcp/mcp.d.ts +34 -6
- package/dist/surface/mcp/mcp.js +61 -17
- 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.d.ts +4 -2
- package/dist/surface/mcp/server-instructions.js +32 -14
- package/dist/surface/mcp/shared-params.js +2 -2
- 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/navigation/navigate-arrival.d.ts +8 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
- 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/settle-in-flight.js +25 -3
- package/dist/surface/tools/act-sequence-tool.js +138 -29
- package/dist/surface/tools/act-tools.d.ts +0 -9
- package/dist/surface/tools/act-tools.js +71 -41
- 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.d.ts +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +42 -17
- package/dist/surface/tools/browser-tools.js +51 -2
- package/dist/surface/tools/chat-drives.d.ts +101 -0
- package/dist/surface/tools/chat-drives.js +409 -0
- 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 +51 -0
- package/dist/surface/tools/empty-session-list.js +120 -0
- package/dist/surface/tools/error-recovery.d.ts +14 -0
- package/dist/surface/tools/error-recovery.js +105 -8
- package/dist/surface/tools/explore-tools.d.ts +31 -1
- package/dist/surface/tools/explore-tools.js +438 -66
- package/dist/surface/tools/feedback-tools.d.ts +2 -3
- package/dist/surface/tools/feedback-tools.js +18 -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 +118 -57
- package/dist/surface/tools/harness-explore.js +344 -292
- 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 +17 -0
- package/dist/surface/tools/harness-plan.js +38 -0
- package/dist/surface/tools/harness-script.d.ts +30 -0
- package/dist/surface/tools/harness-script.js +560 -0
- 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 +22 -1
- package/dist/surface/tools/harness-toolset.js +101 -7
- 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 +263 -85
- 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 +49 -0
- package/dist/surface/tools/lease-seed.js +167 -0
- package/dist/surface/tools/lease-tools.d.ts +13 -24
- package/dist/surface/tools/lease-tools.js +146 -197
- package/dist/surface/tools/live-call-text.js +4 -4
- 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 +29 -0
- package/dist/surface/tools/next-step.js +214 -0
- package/dist/surface/tools/observe-tools.js +45 -9
- package/dist/surface/tools/platform-moment.d.ts +72 -0
- package/dist/surface/tools/platform-moment.js +296 -0
- package/dist/surface/tools/query-hint-schema.d.ts +7 -0
- package/dist/surface/tools/query-hint-schema.js +10 -0
- package/dist/surface/tools/read-tools.js +71 -14
- package/dist/surface/tools/real-input-attempt.js +96 -13
- 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 +83 -3
- package/dist/surface/tools/tool-kit.js +77 -7
- package/dist/surface/tools/tools.js +43 -59
- 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 +29 -6
- package/dist/telemetry/hud-metrics.js +116 -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 +38 -2
- package/dist/telemetry/telemetry.js +144 -34
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +13 -4
- package/dist/wire-journal.d.ts +2 -2
- package/dist/wire-journal.js +20 -6
- package/docs/README.md +3 -3
- package/docs/actions.mdx +6 -3
- package/docs/adapters.md +1 -1
- package/docs/agent-cheatsheet.md +25 -28
- package/docs/architecture.md +5 -5
- package/docs/autodrive.md +33 -28
- 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 +14 -3
- package/docs/cli/drive.mdx +20 -14
- 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 +43 -28
- 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 +18 -5
- 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 +5 -4
- package/docs/cli.mdx +111 -42
- package/docs/debugging.md +3 -3
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +9 -3
- package/docs/enterprise.md +11 -10
- package/docs/environment.mdx +7 -8
- 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 +65 -61
- package/docs/human-control.md +16 -16
- package/docs/index.mdx +15 -42
- package/docs/install-agentic.mdx +22 -37
- package/docs/install-manual.mdx +23 -13
- 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 +9 -9
- package/docs/packages/test.mdx +4 -10
- package/docs/packages/vite-plugin.mdx +32 -4
- package/docs/packages.mdx +11 -15
- package/docs/platform-integration.md +6 -5
- package/docs/predicates.mdx +40 -24
- package/docs/quickstart.mdx +69 -30
- 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 +23 -25
- 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 +22 -13
- 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 +55 -46
- 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 +17 -5
- package/docs/what-is-reticle.mdx +5 -3
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/cli/demo/demo-page.d.ts +0 -78
- package/dist/command/cli/demo/demo-page.js +0 -241
- package/dist/command/cli/demo/demo-run.d.ts +0 -38
- package/dist/command/cli/demo/demo-run.js +0 -138
- package/dist/features/harness/driver.d.ts +0 -50
- package/dist/features/harness/driver.js +0 -236
- package/dist/features/harness/jev-driver.d.ts +0 -188
- package/dist/features/harness/jev-driver.js +0 -678
- package/dist/features/harness/openai-driver.d.ts +0 -58
- package/dist/features/harness/openai-driver.js +0 -206
- 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/memory/project/dir/fill-value-store.d.ts +0 -32
- package/dist/memory/project/dir/fill-value-store.js +0 -88
- 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
- package/docs/local-registry.md +0 -143
|
@@ -30,19 +30,21 @@
|
|
|
30
30
|
* Every dependency is injected — the clock, the fetch, the reads and writes — because this is the
|
|
31
31
|
* one piece of Reticle that talks to a network AND to a disk, and it must be provable without either.
|
|
32
32
|
*/
|
|
33
|
-
import { FlowErrorCode, ReticleDir, SYNC_BATCH_LIMITS } from '@reticlehq/core';
|
|
33
|
+
import { FlowErrorCode, ReticleDir, SYNC_BATCH_LIMITS, flowJsonReplacer } from '@reticlehq/core';
|
|
34
34
|
import { hashPayload } from './sync-hash.js';
|
|
35
|
+
import { syncScreenshots } from './sync-screenshots.js';
|
|
35
36
|
/** The server's own doors. Kept beside the code that calls them, like the other cloud paths. */
|
|
36
37
|
const SYNC_PATH = '/v1/sync';
|
|
37
38
|
const SYNC_STATUS_PATH = '/v1/sync/status';
|
|
38
39
|
const SYNC_PULL_PATH = '/v1/sync/pull';
|
|
40
|
+
const FLOWS_PATH = '/v1/flows';
|
|
39
41
|
/**
|
|
40
42
|
* The derived records that ride along with runs, and the file each one lives in.
|
|
41
43
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
+
* One list, so adding a record is one line and cannot be half-done: the bundle, the hashing, the
|
|
45
|
+
* reporting and the disk source (`sync-disk.ts`) all read it.
|
|
44
46
|
*/
|
|
45
|
-
const DERIVED_RECORDS = [
|
|
47
|
+
export const DERIVED_RECORDS = [
|
|
46
48
|
{ kind: 'impact', file: ReticleDir.IMPACT_FILE },
|
|
47
49
|
{ kind: 'flake', file: ReticleDir.FLAKE_FILE },
|
|
48
50
|
{ kind: 'intent', file: ReticleDir.INTENT_FILE },
|
|
@@ -50,6 +52,10 @@ const DERIVED_RECORDS = [
|
|
|
50
52
|
// cannot tell a server a page that drifted from one that always behaved that way.
|
|
51
53
|
{ kind: 'envelopes', file: ReticleDir.ENVELOPES_FILE },
|
|
52
54
|
{ kind: 'assertion-tiers', file: ReticleDir.TIERS_FILE },
|
|
55
|
+
// Reticle Coverage: what the platform's Overview shows as controls proved and routes reached.
|
|
56
|
+
{ kind: 'coverage', file: ReticleDir.COVERAGE_FILE },
|
|
57
|
+
// The notes a human pinned in the HUD, pending and resolved: the review trail a team reads.
|
|
58
|
+
{ kind: 'notes', file: ReticleDir.NOTES_FILE },
|
|
53
59
|
];
|
|
54
60
|
const asJson = (text) => {
|
|
55
61
|
try {
|
|
@@ -75,6 +81,8 @@ const flowName = (flow, index) => {
|
|
|
75
81
|
const FLOW_VERSION_FIX = 'update the platform or remove `compare` from the flow';
|
|
76
82
|
const RUN_VERSION_FIX = 'update the platform';
|
|
77
83
|
/** The server's per-part answer: accepted count and rejection list, each read defensively. */
|
|
84
|
+
/** A 200 that never confirmed the runs it was sent. Kept queued, and said, rather than dropped. */
|
|
85
|
+
export const MSG_RUNS_UNCONFIRMED = 'sync 200 without a runs result: the server did not confirm the runs it was sent, so they stay queued';
|
|
78
86
|
function partResult(body, part) {
|
|
79
87
|
const raw = isRecord(body[part]) ? body[part] : {};
|
|
80
88
|
const accepted = 'number' === typeof raw['accepted'] ? raw['accepted'] : 0;
|
|
@@ -125,7 +133,29 @@ function batchRuns(runs) {
|
|
|
125
133
|
* Run one cycle. Never throws: the local record is already safe on disk, and a sync that can take
|
|
126
134
|
* down the thing it is backing up is worse than no sync.
|
|
127
135
|
*/
|
|
128
|
-
|
|
136
|
+
/** The command that moves a folder's earlier runs to the link it has now. */
|
|
137
|
+
export const MOVE_HISTORY_COMMAND = 'reticle sync --move-history';
|
|
138
|
+
/**
|
|
139
|
+
* The record this cycle keeps, for this link. Re-linked to another cloud, workspace or project, it
|
|
140
|
+
* starts afresh from now: what the old link was sent says nothing about the new one, and the runs
|
|
141
|
+
* made under the old link are that workspace's (a person's email can be in their steps), so they go
|
|
142
|
+
* only when the person moves them. A first link, or a record from before links were kept, is
|
|
143
|
+
* adopted as it is.
|
|
144
|
+
*/
|
|
145
|
+
function stateForLink(deps) {
|
|
146
|
+
const { state, link } = deps;
|
|
147
|
+
if (link === undefined)
|
|
148
|
+
return state;
|
|
149
|
+
if (state.link === undefined || state.link === link) {
|
|
150
|
+
const { linkedAt, ...rest } = state;
|
|
151
|
+
return true === deps.moveHistory || linkedAt === undefined
|
|
152
|
+
? { ...rest, link }
|
|
153
|
+
: { ...rest, link, linkedAt };
|
|
154
|
+
}
|
|
155
|
+
return { link, ...(true === deps.moveHistory ? {} : { linkedAt: deps.now() }) };
|
|
156
|
+
}
|
|
157
|
+
export async function runSyncCycle(given) {
|
|
158
|
+
const deps = { ...given, state: stateForLink(given) };
|
|
129
159
|
const empty = {
|
|
130
160
|
ok: false,
|
|
131
161
|
runsSent: 0,
|
|
@@ -231,7 +261,14 @@ export async function runSyncCycle(deps) {
|
|
|
231
261
|
notRetried.push({ runId: run.runId, reason: prior.reason });
|
|
232
262
|
return false;
|
|
233
263
|
});
|
|
264
|
+
// Made before this folder was linked here: the previous link's, kept until the person moves it.
|
|
265
|
+
const linkedAt = deps.state.linkedAt;
|
|
266
|
+
const earlier = unsent.filter((run) => linkedAt !== undefined && !((numberAt(run.payload, 'createdAt') ?? 0) >= linkedAt));
|
|
267
|
+
if (0 < earlier.length)
|
|
268
|
+
heldBack.push(`${String(earlier.length)} run${1 === earlier.length ? '' : 's'} made before this folder was linked to this workspace stay${1 === earlier.length ? 's' : ''} on this machine (recorded under its previous link): run \`${MOVE_HISTORY_COMMAND}\` to send ${1 === earlier.length ? 'it' : 'them'} here`);
|
|
234
269
|
const sendable = unsent.filter((run) => {
|
|
270
|
+
if (earlier.includes(run))
|
|
271
|
+
return false;
|
|
235
272
|
const version = numberAt(run.payload, 'schemaVersion');
|
|
236
273
|
if (runVersions === undefined || version === undefined || runVersions.includes(version))
|
|
237
274
|
return true;
|
|
@@ -242,25 +279,51 @@ export async function runSyncCycle(deps) {
|
|
|
242
279
|
const derivedOffered = [];
|
|
243
280
|
const unsupported = [];
|
|
244
281
|
const bundle = {};
|
|
282
|
+
// A folder re-linked elsewhere keeps what it derived under the old link (its counts, defects,
|
|
283
|
+
// notes) here with the runs: those records add up the whole history and cannot be split by time.
|
|
284
|
+
// ponytail: held until `--move-history`; a per-link reset of the counters would let new ones go.
|
|
285
|
+
const derivedHeld = [];
|
|
245
286
|
for (const { kind } of DERIVED_RECORDS) {
|
|
246
287
|
const payload = deps.source.derived(kind);
|
|
247
288
|
if (payload === undefined)
|
|
248
289
|
continue;
|
|
290
|
+
if (linkedAt !== undefined) {
|
|
291
|
+
derivedHeld.push(kind);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
249
294
|
if (!acceptedKinds.has(kind)) {
|
|
250
295
|
unsupported.push(kind);
|
|
251
296
|
continue;
|
|
252
297
|
}
|
|
253
|
-
// The one comparison the whole protocol rests on. Same hash, do not send it
|
|
254
|
-
|
|
298
|
+
// The one comparison the whole protocol rests on. Same hash, do not send it: the platform's,
|
|
299
|
+
// or the one it accepted from here while it still holds the record (a null means it does not).
|
|
300
|
+
const hash = hashPayload(payload);
|
|
301
|
+
if (hash === hashes[kind])
|
|
302
|
+
continue;
|
|
303
|
+
if ('string' === typeof hashes[kind] && hash === deps.state.sentDerivedHashes?.[kind])
|
|
255
304
|
continue;
|
|
256
305
|
bundle[kind] = payload;
|
|
257
306
|
derivedOffered.push(kind);
|
|
258
307
|
}
|
|
259
308
|
if (unsupported.length > 0)
|
|
260
309
|
heldBack.push(`this platform does not accept ${unsupported.join(', ')} yet`);
|
|
310
|
+
if (0 < derivedHeld.length)
|
|
311
|
+
heldBack.push(`the ${derivedHeld.join(', ')} record${1 === derivedHeld.length ? '' : 's'} made under this folder's previous link stay${1 === derivedHeld.length ? 's' : ''} on this machine: run \`${MOVE_HISTORY_COMMAND}\` to send ${1 === derivedHeld.length ? 'it' : 'them'} here`);
|
|
261
312
|
// Flows and capsules are upserted by name/id, so the whole set goes whenever it differs from the
|
|
262
|
-
// set the platform last answered for
|
|
263
|
-
|
|
313
|
+
// set the platform last answered for — and only then. Riding along with every run push re-sent
|
|
314
|
+
// ~1.3 MB per push on a real repo, and a POST that size timed out and failed the run with it.
|
|
315
|
+
// Made before this folder was linked here, like the runs: the previous link's, kept until moved.
|
|
316
|
+
// A flow carries the steps of whoever drove it, so it is that workspace's as much as a run is.
|
|
317
|
+
// One read for both halves: the disk source rebuilds every flow per read, so a second read's
|
|
318
|
+
// objects are never `includes` of the first's, and every held-back flow went out anyway.
|
|
319
|
+
const flowsAll = deps.source.flows();
|
|
320
|
+
const madeBefore = (flow) => linkedAt !== undefined && !((numberAt(flow, 'createdAt') ?? 0) >= linkedAt);
|
|
321
|
+
const flowsBefore = flowsAll.filter(madeBefore);
|
|
322
|
+
if (0 < flowsBefore.length)
|
|
323
|
+
heldBack.push(`${String(flowsBefore.length)} flow${1 === flowsBefore.length ? '' : 's'} made before this folder was linked to this workspace stay${1 === flowsBefore.length ? 's' : ''} on this machine: run \`${MOVE_HISTORY_COMMAND}\` to send ${1 === flowsBefore.length ? 'it' : 'them'} here`);
|
|
324
|
+
const flows = flowsAll.filter((flow, index) => {
|
|
325
|
+
if (madeBefore(flow))
|
|
326
|
+
return false;
|
|
264
327
|
const version = numberAt(flow, 'version');
|
|
265
328
|
if (flowVersions === undefined || version === undefined || flowVersions.includes(version))
|
|
266
329
|
return true;
|
|
@@ -290,10 +353,9 @@ export async function runSyncCycle(deps) {
|
|
|
290
353
|
};
|
|
291
354
|
const flowsChanged = due('flow', flowsHash, flows.length, deps.state.sentFlowsHash);
|
|
292
355
|
const capsulesChanged = due('capsule', capsulesHash, capsulesAll.length, deps.state.sentCapsulesHash);
|
|
293
|
-
|
|
294
|
-
if (ridesAlong && flows.length > 0)
|
|
356
|
+
if (flowsChanged)
|
|
295
357
|
bundle['flows'] = flows;
|
|
296
|
-
const capsules =
|
|
358
|
+
const capsules = capsulesChanged ? capsulesAll : [];
|
|
297
359
|
if (capsules.length > 0)
|
|
298
360
|
bundle['capsules'] = capsules;
|
|
299
361
|
let runsSent = 0;
|
|
@@ -315,12 +377,23 @@ export async function runSyncCycle(deps) {
|
|
|
315
377
|
const body = 0 === i ? { ...bundle } : {};
|
|
316
378
|
if (batch.length > 0)
|
|
317
379
|
body['runs'] = batch.map((r) => r.payload);
|
|
318
|
-
|
|
380
|
+
// The flows ride here too: a default flag stays out of them, as on disk.
|
|
381
|
+
const pushed = await call(SYNC_PATH, {
|
|
382
|
+
method: 'POST',
|
|
383
|
+
body: JSON.stringify(body, flowJsonReplacer),
|
|
384
|
+
});
|
|
319
385
|
if (200 !== pushed.status) {
|
|
320
386
|
pushError = `sync ${String(pushed.status)}: ${pushed.text.slice(0, 200)}`;
|
|
321
387
|
break;
|
|
322
388
|
}
|
|
323
389
|
const answer = isRecord(pushed.json) ? pushed.json : {};
|
|
390
|
+
// Runs were sent and the answer says nothing about them: a proxy's 200 page, an empty body.
|
|
391
|
+
// Delivered means the server said so. Recording them as sent here dropped them for good while
|
|
392
|
+
// the cycle reported ok; they stay queued, and the reason is the cycle's error.
|
|
393
|
+
if (batch.length > 0 && !isRecord(answer['runs'])) {
|
|
394
|
+
pushError = MSG_RUNS_UNCONFIRMED;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
324
397
|
const runs = partResult(answer, 'runs');
|
|
325
398
|
runsSent += runs.accepted;
|
|
326
399
|
// Accepted means the server has it. A rejected run was refused by index, so it is exactly as
|
|
@@ -375,18 +448,40 @@ export async function runSyncCycle(deps) {
|
|
|
375
448
|
const state = isRecord(answer['state']) ? answer['state'] : {};
|
|
376
449
|
for (const kind of derivedOffered) {
|
|
377
450
|
const verdict = state[kind];
|
|
378
|
-
if ('accepted' === verdict)
|
|
451
|
+
if ('accepted' === verdict) {
|
|
379
452
|
derivedSent.push(kind);
|
|
453
|
+
nextState.sentDerivedHashes = {
|
|
454
|
+
...nextState.sentDerivedHashes,
|
|
455
|
+
[kind]: hashPayload(bundle[kind]),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
380
458
|
else if ('string' === typeof verdict)
|
|
381
459
|
refused.push({ part: kind, reason: `${kind}: ${verdict}` });
|
|
382
460
|
}
|
|
383
461
|
}
|
|
462
|
+
// A flow the platform took from here and that is gone from disk is deleted there by name: it
|
|
463
|
+
// does not infer a deletion from a missing name, since another clone may still hold the flow.
|
|
464
|
+
const onDisk = new Set(deps.source.flows().map(flowName));
|
|
465
|
+
const kept = [];
|
|
466
|
+
for (const name of deps.projectId === undefined ? [] : (deps.state.sentFlowNames ?? [])) {
|
|
467
|
+
if (onDisk.has(name)) {
|
|
468
|
+
kept.push(name);
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
const gone = await call(`${FLOWS_PATH}/${encodeURIComponent(name)}?projectId=${encodeURIComponent(deps.projectId ?? '')}`, { method: 'DELETE' });
|
|
472
|
+
if (2 !== Math.floor(gone.status / 100) && 404 !== gone.status)
|
|
473
|
+
kept.push(name);
|
|
474
|
+
}
|
|
475
|
+
if (deps.projectId !== undefined)
|
|
476
|
+
nextState.sentFlowNames = kept;
|
|
384
477
|
if (requests > 0 && pushError === undefined) {
|
|
385
478
|
nextState.lastPushAt = deps.now();
|
|
386
479
|
// The platform answered for these sets, refusals included (reported above, and not re-offered
|
|
387
480
|
// until the set changes — the same rule as a refused run).
|
|
388
|
-
if (
|
|
481
|
+
if (flowsChanged)
|
|
389
482
|
nextState.sentFlowsHash = flowsHash;
|
|
483
|
+
if (flowsChanged && deps.projectId !== undefined)
|
|
484
|
+
nextState.sentFlowNames = [...new Set([...kept, ...flows.map(flowName)])];
|
|
390
485
|
if (capsules.length > 0)
|
|
391
486
|
nextState.sentCapsulesHash = capsulesHash;
|
|
392
487
|
}
|
|
@@ -416,6 +511,22 @@ export async function runSyncCycle(deps) {
|
|
|
416
511
|
*/
|
|
417
512
|
nextState.sentRunIds = allRuns.map((r) => r.runId).filter((id) => known.has(id));
|
|
418
513
|
nextState.sentRunHashes = heldHashes(allRuns, recorded);
|
|
514
|
+
// 2b. The step screenshots of every run the platform now holds, once each.
|
|
515
|
+
let screenshotsSent = 0;
|
|
516
|
+
const readImage = deps.source.screenshot;
|
|
517
|
+
if (readImage !== undefined) {
|
|
518
|
+
const shots = await syncScreenshots({
|
|
519
|
+
runs: allRuns,
|
|
520
|
+
accepted: (runId) => known.has(runId),
|
|
521
|
+
readImage,
|
|
522
|
+
state: deps.state,
|
|
523
|
+
post: (path, body) => call(path, { method: 'POST', body }),
|
|
524
|
+
});
|
|
525
|
+
screenshotsSent = shots.sent;
|
|
526
|
+
Object.assign(nextState, shots.state);
|
|
527
|
+
if (shots.error !== undefined)
|
|
528
|
+
heldBack.push(shots.error);
|
|
529
|
+
}
|
|
419
530
|
// 3. COLLECT — always, even when there was nothing to send.
|
|
420
531
|
const query = nextState.cursor === undefined ? '' : `?since=${encodeURIComponent(nextState.cursor)}`;
|
|
421
532
|
const pull = await call(`${SYNC_PULL_PATH}${query}`);
|
|
@@ -464,6 +575,7 @@ export async function runSyncCycle(deps) {
|
|
|
464
575
|
0 === setsNotRetried.length &&
|
|
465
576
|
!heldArtifacts,
|
|
466
577
|
runsSent,
|
|
578
|
+
...(0 === screenshotsSent ? {} : { screenshotsSent }),
|
|
467
579
|
runsRejected,
|
|
468
580
|
flowsSent,
|
|
469
581
|
capsulesSent,
|
|
@@ -537,6 +649,8 @@ export function describeSync(report) {
|
|
|
537
649
|
sent.push(`${String(report.flowsSent)} flow(s)`);
|
|
538
650
|
if (report.capsulesSent > 0)
|
|
539
651
|
sent.push(`${String(report.capsulesSent)} capsule(s)`);
|
|
652
|
+
if ((report.screenshotsSent ?? 0) > 0)
|
|
653
|
+
sent.push(`${String(report.screenshotsSent)} screenshot(s)`);
|
|
540
654
|
if (report.derivedSent.length > 0)
|
|
541
655
|
sent.push(report.derivedSent.join(', '));
|
|
542
656
|
/*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type SyncReport } from './sync-cycle.js';
|
|
2
2
|
import { type ProjectCloud } from './cloud-config.js';
|
|
3
|
+
import { type UnsyncedRoot } from './unsynced-roots.js';
|
|
3
4
|
import type { FileSystemPort } from '../project/fs/fs-port.js';
|
|
4
5
|
interface SyncDaemonDeps {
|
|
5
6
|
reticleRoot: string;
|
|
@@ -20,6 +21,11 @@ interface SyncDaemonDeps {
|
|
|
20
21
|
cloudFor?: (root: string) => Promise<ProjectCloud>;
|
|
21
22
|
now?: () => number;
|
|
22
23
|
intervalMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Call `onChange` when this root's link file changes; returns how to stop. A re-link was read only
|
|
26
|
+
* on the next idle tick, so a daemon kept syncing to the old link for up to a minute.
|
|
27
|
+
*/
|
|
28
|
+
watchLink?: (onChange: () => void) => () => void;
|
|
23
29
|
/** Injected for the test; the real one is `fetch`. */
|
|
24
30
|
request?: (url: string, init: {
|
|
25
31
|
method: string;
|
|
@@ -58,6 +64,11 @@ export interface SyncDaemon {
|
|
|
58
64
|
* time — the artifact is still on disk and the next daemon for this project sends it.
|
|
59
65
|
*/
|
|
60
66
|
flush: () => Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Every root this daemon knows with runs the platform does not have, as of the last cycle — linked
|
|
69
|
+
* or not. An unlinked one is the case nothing will ever send, and it used to be skipped silently.
|
|
70
|
+
*/
|
|
71
|
+
unsyncedRoots: () => readonly UnsyncedRoot[];
|
|
61
72
|
}
|
|
62
73
|
/**
|
|
63
74
|
* Start the loop. Safe to call for an UNLINKED project: it resolves the link on every tick and does
|
|
@@ -21,11 +21,21 @@
|
|
|
21
21
|
* laptop on a train would otherwise fill the log with the same line four hundred times and teach
|
|
22
22
|
* everyone to ignore it.
|
|
23
23
|
*/
|
|
24
|
+
import { reticleDirPaths } from '../project/dir/reticle-dir.js';
|
|
25
|
+
import { syncRequest } from './cloud-sync.js';
|
|
24
26
|
import { log } from '../../log.js';
|
|
27
|
+
import { homedir } from 'node:os';
|
|
25
28
|
import { emitSyncHook } from '../../hooks/hook-emit.js';
|
|
29
|
+
import { MemoryReadHeader, MemoryReadPurpose, memoryReadUrl, scopedMemoryEntries, } from './memory-scope.js';
|
|
30
|
+
import { IntentShardStore } from '../intent/intent-shard-store.js';
|
|
31
|
+
import { applySettledWords, keptWordsInRequest, settledWordsIn, } from '../intent/settled-words.js';
|
|
32
|
+
import { createNodeFileSystem } from '../project/fs/fs-port.js';
|
|
33
|
+
import { flushPendingWrites } from '../../hooks/hook-bus.js';
|
|
26
34
|
import { describeSync, runSyncCycle } from './sync-cycle.js';
|
|
27
35
|
import { diskSink, diskSource, readCloudState } from './sync-disk.js';
|
|
28
|
-
import { resolveProjectCloud } from './cloud-config.js';
|
|
36
|
+
import { CLOUD_LINK_FILE, resolveProjectCloud } from './cloud-config.js';
|
|
37
|
+
import { watch } from 'node:fs';
|
|
38
|
+
import { unmatchedRoots, unsyncedRoots } from './unsynced-roots.js';
|
|
29
39
|
/** How often the daemon cycles. See the note above on why this is a constant and not a curve. */
|
|
30
40
|
const DAEMON_SYNC_INTERVAL_MS = 60_000;
|
|
31
41
|
/**
|
|
@@ -69,10 +79,7 @@ const movedAnything = (report) => report.runsSent > 0 ||
|
|
|
69
79
|
report.capsulesSent > 0 ||
|
|
70
80
|
report.derivedSent.length > 0 ||
|
|
71
81
|
report.pulled > 0;
|
|
72
|
-
const defaultRequest =
|
|
73
|
-
const res = await fetch(url, init);
|
|
74
|
-
return { status: res.status, text: await res.text() };
|
|
75
|
-
};
|
|
82
|
+
const defaultRequest = (url, init) => syncRequest(url, init);
|
|
76
83
|
/**
|
|
77
84
|
* Start the loop. Safe to call for an UNLINKED project: it resolves the link on every tick and does
|
|
78
85
|
* nothing until one appears, which is what lets `reticle link` take effect without a restart.
|
|
@@ -102,18 +109,62 @@ export function startSyncDaemon(deps) {
|
|
|
102
109
|
* so too. Once per transition, never per tick — a line every minute is a line people stop reading.
|
|
103
110
|
*/
|
|
104
111
|
let wasLinked;
|
|
112
|
+
/** The project the last cycle synced to: a re-link to another one is announced too. */
|
|
113
|
+
let wasProject;
|
|
114
|
+
/** The last count logged per root: a root is announced when its count changes, never per tick. */
|
|
115
|
+
const announced = new Map();
|
|
116
|
+
let unsynced = [];
|
|
117
|
+
/** Which roots pushed this cycle, and whether each is linked by its own link file. */
|
|
118
|
+
let rootsLinked = new Map();
|
|
119
|
+
/** The linked roots that send flows: a link with `sync.flows` off keeps every flow local. */
|
|
120
|
+
let rootsSendFlows = new Set();
|
|
121
|
+
/**
|
|
122
|
+
* Count what every root still owes, and say once per change where runs sit that nothing sends.
|
|
123
|
+
* Linked roots are listed too (the platform report and `doctor` read them), but only an unlinked
|
|
124
|
+
* one earns a log line: a linked root's pending runs are the next cycle's job.
|
|
125
|
+
*/
|
|
126
|
+
const refreshUnsynced = async () => {
|
|
127
|
+
const linked = rootsLinked;
|
|
128
|
+
const sendsFlows = rootsSendFlows;
|
|
129
|
+
unsynced = await unsyncedRoots(
|
|
130
|
+
// With what no project claimed, which no cycle pushes: the dashboard's unsent list left it out.
|
|
131
|
+
[...linked.keys(), ...unmatchedRoots(homedir())], (r) => Promise.resolve(true === linked.get(r)), (r) => Promise.resolve(sendsFlows.has(r)));
|
|
132
|
+
for (const entry of unsynced) {
|
|
133
|
+
const held = entry.runs + entry.flows;
|
|
134
|
+
if (entry.linked || announced.get(entry.root) === held)
|
|
135
|
+
continue;
|
|
136
|
+
announced.set(entry.root, held);
|
|
137
|
+
log('reticle_cloud_unsynced_root', {
|
|
138
|
+
root: entry.root,
|
|
139
|
+
runs: entry.runs,
|
|
140
|
+
flows: entry.flows,
|
|
141
|
+
linked: entry.linked,
|
|
142
|
+
fix: "run `reticle connect` in that folder's project: this work is on this machine only",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
105
146
|
/** One root's bundle. The state, source and sink are already per-root; only the caller was not. */
|
|
106
147
|
const pushRoot = async (root, cloud) => {
|
|
107
148
|
if (null === cloud.config)
|
|
108
149
|
return undefined;
|
|
109
150
|
const full = diskSource(root);
|
|
151
|
+
// The team's decisions come down too: an author's record kept the words a maintainer set aside,
|
|
152
|
+
// and re-sent them every cycle as a new disagreement.
|
|
153
|
+
if (cloud.policy.memory)
|
|
154
|
+
await pullSettledWords(root, cloud.config, cloud.projectId).catch(() => 0);
|
|
110
155
|
return runSyncCycle({
|
|
111
156
|
config: cloud.config,
|
|
157
|
+
...(cloud.link === undefined ? {} : { link: cloud.link }),
|
|
158
|
+
// Flows deleted here are deleted there, for the project they were sent to.
|
|
159
|
+
...(cloud.policy.flows && null !== cloud.projectId ? { projectId: cloud.projectId } : {}),
|
|
112
160
|
source: {
|
|
113
161
|
runs: () => (cloud.policy.runs ? full.runs() : []),
|
|
114
162
|
flows: () => (cloud.policy.flows ? full.flows() : []),
|
|
115
163
|
capsules: () => (cloud.policy.capsules ? full.capsules() : []),
|
|
116
164
|
derived: (kind) => (cloud.policy.memory ? full.derived(kind) : undefined),
|
|
165
|
+
...(cloud.policy.screenshots && full.screenshot !== undefined
|
|
166
|
+
? { screenshot: full.screenshot }
|
|
167
|
+
: {}),
|
|
117
168
|
},
|
|
118
169
|
sink: diskSink(root),
|
|
119
170
|
state: readCloudState(root),
|
|
@@ -121,6 +172,23 @@ export function startSyncDaemon(deps) {
|
|
|
121
172
|
request,
|
|
122
173
|
});
|
|
123
174
|
};
|
|
175
|
+
/** Bring the dashboard's memory decisions down to this root's records. Returns how many changed. */
|
|
176
|
+
const pullSettledWords = async (root, config, projectId) => {
|
|
177
|
+
const res = await request(memoryReadUrl(config.url, { projectId }), {
|
|
178
|
+
method: 'GET',
|
|
179
|
+
// The sync, not an agent: without this every cycle counted as an agent consulting memory.
|
|
180
|
+
headers: {
|
|
181
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
182
|
+
[MemoryReadHeader.PURPOSE]: MemoryReadPurpose.SYNC,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
if (200 !== res.status)
|
|
186
|
+
return 0;
|
|
187
|
+
const entries = scopedMemoryEntries(JSON.parse(res.text), projectId) ?? [];
|
|
188
|
+
const settled = settledWordsIn(entries);
|
|
189
|
+
await keptWordsInRequest(reticleDirPaths(root).request, settled);
|
|
190
|
+
return applySettledWords(new IntentShardStore(createNodeFileSystem(), root, { now }), settled);
|
|
191
|
+
};
|
|
124
192
|
/**
|
|
125
193
|
* Push every other linked root this machine knows about.
|
|
126
194
|
*
|
|
@@ -145,10 +213,19 @@ export function startSyncDaemon(deps) {
|
|
|
145
213
|
for (const root of list) {
|
|
146
214
|
if (root === deps.reticleRoot)
|
|
147
215
|
continue;
|
|
216
|
+
rootsLinked.set(root, false);
|
|
148
217
|
try {
|
|
149
218
|
const cloud = await cloudFor(root);
|
|
150
|
-
|
|
219
|
+
// Linked HERE, by its own link file. An unlinked root resolves to the environment's key,
|
|
220
|
+
// which is the right answer for the root this daemon stands in and somebody else's project
|
|
221
|
+
// for every other one: a daemon started with RETICLE_API_KEY pushed every project this
|
|
222
|
+
// machine had seen into that one, a status and a pull per root on every cycle.
|
|
223
|
+
// Never silently: `refreshUnsynced` names this root if it holds runs.
|
|
224
|
+
if (null === cloud.config || null === cloud.projectId)
|
|
151
225
|
continue;
|
|
226
|
+
rootsLinked.set(root, true);
|
|
227
|
+
if (cloud.policy.flows)
|
|
228
|
+
rootsSendFlows.add(root);
|
|
152
229
|
const report = await pushRoot(root, cloud);
|
|
153
230
|
if (report === undefined)
|
|
154
231
|
continue;
|
|
@@ -178,11 +255,18 @@ export function startSyncDaemon(deps) {
|
|
|
178
255
|
if (running)
|
|
179
256
|
return undefined;
|
|
180
257
|
running = true;
|
|
258
|
+
rootsLinked = new Map();
|
|
259
|
+
rootsSendFlows = new Set();
|
|
181
260
|
try {
|
|
182
261
|
const cloud = await deps.cloud();
|
|
183
262
|
const linked = null !== cloud.config;
|
|
184
|
-
|
|
263
|
+
const own = deps.reticleRoot;
|
|
264
|
+
rootsLinked.set(own, linked);
|
|
265
|
+
if (linked && cloud.policy.flows)
|
|
266
|
+
rootsSendFlows.add(own);
|
|
267
|
+
if (linked !== wasLinked || (linked && cloud.projectId !== wasProject)) {
|
|
185
268
|
wasLinked = linked;
|
|
269
|
+
wasProject = cloud.projectId;
|
|
186
270
|
if (linked)
|
|
187
271
|
log('reticle_cloud_linked', {
|
|
188
272
|
projectId: cloud.projectId,
|
|
@@ -193,7 +277,7 @@ export function startSyncDaemon(deps) {
|
|
|
193
277
|
else
|
|
194
278
|
log('reticle_cloud_unlinked', {
|
|
195
279
|
root: deps.reticleRoot,
|
|
196
|
-
fix: 'run `reticle
|
|
280
|
+
fix: 'run `reticle connect` in this directory, or start the daemon in the linked one — nothing is being synced from here',
|
|
197
281
|
});
|
|
198
282
|
}
|
|
199
283
|
// Every OTHER linked root, first. Their failures are logged and never abort this daemon's own
|
|
@@ -251,6 +335,7 @@ export function startSyncDaemon(deps) {
|
|
|
251
335
|
return undefined;
|
|
252
336
|
}
|
|
253
337
|
finally {
|
|
338
|
+
await refreshUnsynced().catch(() => undefined);
|
|
254
339
|
running = false;
|
|
255
340
|
}
|
|
256
341
|
};
|
|
@@ -270,20 +355,30 @@ export function startSyncDaemon(deps) {
|
|
|
270
355
|
timer.unref?.();
|
|
271
356
|
};
|
|
272
357
|
schedule(FIRST_CYCLE_DELAY_MS);
|
|
358
|
+
// A nudge, not an immediate cycle: the link file and its key are written one after the other.
|
|
359
|
+
const unwatch = deps.watchLink?.(() => schedule(NUDGE_DELAY_MS));
|
|
273
360
|
return {
|
|
274
|
-
|
|
361
|
+
// A sync somebody asked for writes what is held on a timer first: a drive's run waits a few
|
|
362
|
+
// seconds after its last verdict, and a push straight after `session end` sent "0 of 0".
|
|
363
|
+
syncNow: async () => {
|
|
364
|
+
await flushPendingWrites();
|
|
365
|
+
return cycle();
|
|
366
|
+
},
|
|
367
|
+
unsyncedRoots: () => unsynced,
|
|
275
368
|
nudge: () => {
|
|
276
369
|
// A cycle already in flight will not pick this up, so the nudge is still scheduled behind it
|
|
277
370
|
// rather than dropped — otherwise the run that arrived during a slow cycle waits a full tick.
|
|
278
371
|
schedule(NUDGE_DELAY_MS);
|
|
279
372
|
},
|
|
280
373
|
stop: () => {
|
|
374
|
+
unwatch?.();
|
|
281
375
|
stopped = true;
|
|
282
376
|
if (timer !== undefined)
|
|
283
377
|
clearTimeout(timer);
|
|
284
378
|
timer = undefined;
|
|
285
379
|
},
|
|
286
380
|
flush: async () => {
|
|
381
|
+
unwatch?.();
|
|
287
382
|
stopped = true;
|
|
288
383
|
if (timer !== undefined)
|
|
289
384
|
clearTimeout(timer);
|
|
@@ -318,5 +413,22 @@ export function attachCloudSync(deps) {
|
|
|
318
413
|
// reporting nothing — indistinguishable, on the dashboard, from nobody having verified anything.
|
|
319
414
|
otherRoots: () => Promise.resolve(deps.otherRoots()),
|
|
320
415
|
cloudFor: (root) => resolveProjectCloud(deps.fs, root, deps.homeDir, deps.env),
|
|
416
|
+
// ponytail: this root only; a sibling root's re-link is still read on the next tick.
|
|
417
|
+
watchLink: (onChange) => watchLinkFile(deps.reticleRoot, onChange),
|
|
321
418
|
});
|
|
322
419
|
}
|
|
420
|
+
/** Watch a root for its link file changing. A root with no `.reticle` yet is picked up per tick. */
|
|
421
|
+
function watchLinkFile(reticleRoot, onChange) {
|
|
422
|
+
try {
|
|
423
|
+
const watcher = watch(reticleRoot, (_event, name) => {
|
|
424
|
+
if (CLOUD_LINK_FILE === String(name))
|
|
425
|
+
onChange();
|
|
426
|
+
});
|
|
427
|
+
watcher.on('error', () => watcher.close());
|
|
428
|
+
watcher.unref();
|
|
429
|
+
return () => watcher.close();
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return () => undefined;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CloudSyncState, type PulledIssues, type SyncSink, type SyncSource } from './sync-cycle.js';
|
|
2
2
|
/** What the cycle reads, backed by a real `.reticle` directory. */
|
|
3
3
|
export declare function diskSource(reticleRoot: string): SyncSource;
|
|
4
4
|
/** What the cycle writes back into `.reticle/`. */
|
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
* one unsynced record, not a crashed sync. There is no state here worth defending against a parse
|
|
11
11
|
* error — the file will be rewritten on the next tool call anyway.
|
|
12
12
|
*/
|
|
13
|
+
import { shareableRun } from '@reticlehq/core/artifacts';
|
|
13
14
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync, } from 'node:fs';
|
|
14
|
-
import { dirname, join } from 'node:path';
|
|
15
|
+
import { basename, dirname, join } from 'node:path';
|
|
16
|
+
import { stepShotDir } from '../project/dir/reticle-dir.js';
|
|
15
17
|
import { IntentDir, ReticleDir } from '@reticlehq/core';
|
|
18
|
+
import { DERIVED_RECORDS, } from './sync-cycle.js';
|
|
16
19
|
import { subjectFor } from '../intent/intent-subject.js';
|
|
17
20
|
const JSON_SUFFIX = '.json';
|
|
18
21
|
/** Read + parse, or undefined for absent/unreadable/malformed. Never throws. */
|
|
@@ -79,13 +82,7 @@ function readFlows(root) {
|
|
|
79
82
|
return [];
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
const DERIVED_FILE =
|
|
83
|
-
impact: ReticleDir.IMPACT_FILE,
|
|
84
|
-
flake: ReticleDir.FLAKE_FILE,
|
|
85
|
-
intent: ReticleDir.INTENT_FILE,
|
|
86
|
-
envelopes: ReticleDir.ENVELOPES_FILE,
|
|
87
|
-
'assertion-tiers': ReticleDir.TIERS_FILE,
|
|
88
|
-
};
|
|
85
|
+
const DERIVED_FILE = Object.fromEntries(DERIVED_RECORDS.map((record) => [record.kind, record.file]));
|
|
89
86
|
/** The directory the sharded intent store writes into, beside the legacy flat file. */
|
|
90
87
|
const INTENT_SUBDIR = ReticleDir.INTENT_SUBDIR;
|
|
91
88
|
const INTENT_INDEX = IntentDir.INDEX_FILE;
|
|
@@ -106,6 +103,8 @@ const INTENT_INDEX = IntentDir.INDEX_FILE;
|
|
|
106
103
|
* Returns undefined when there is no memory at all, so a project with nothing to say sends nothing
|
|
107
104
|
* rather than an empty envelope the server has to store.
|
|
108
105
|
*/
|
|
106
|
+
/** The longest reason the platform keeps for a statement. */
|
|
107
|
+
const MAX_REASON = 2000;
|
|
109
108
|
function readIntent(reticleRoot) {
|
|
110
109
|
const legacy = readJson(join(reticleRoot, ReticleDir.INTENT_FILE));
|
|
111
110
|
const merged = {
|
|
@@ -143,7 +142,16 @@ function readIntent(reticleRoot) {
|
|
|
143
142
|
* answers start disagreeing — and the one with the evidence should be the one that decides.
|
|
144
143
|
* Records that already name a subject keep it: an agent's explicit choice outranks inference.
|
|
145
144
|
*/
|
|
146
|
-
const placed = Object.fromEntries(Object.entries(merged).map(([id,
|
|
145
|
+
const placed = Object.fromEntries(Object.entries(merged).map(([id, given]) => {
|
|
146
|
+
// The record's `why` is what the platform keeps as `reason`.
|
|
147
|
+
const why = given.why;
|
|
148
|
+
// The run that proved it, under the name the platform reads (it does not read `provenBy`).
|
|
149
|
+
const run = given.provenBy?.runId;
|
|
150
|
+
const record = {
|
|
151
|
+
...given,
|
|
152
|
+
...('string' === typeof why && 0 < why.length ? { reason: why.slice(0, MAX_REASON) } : {}),
|
|
153
|
+
...('string' === typeof run && 0 < run.length ? { provedBy: run } : {}),
|
|
154
|
+
};
|
|
147
155
|
const held = record;
|
|
148
156
|
if ('string' === typeof held.subject && held.subject.length > 0)
|
|
149
157
|
return [id, record];
|
|
@@ -170,7 +178,8 @@ export function diskSource(reticleRoot) {
|
|
|
170
178
|
runs: () => readJsonDir(join(reticleRoot, ReticleDir.RUNS_SUBDIR))
|
|
171
179
|
.map((payload) => {
|
|
172
180
|
const id = payload?.runId;
|
|
173
|
-
|
|
181
|
+
// The prompt context leaves the machine only when the project opted in.
|
|
182
|
+
return 'string' === typeof id ? { runId: id, payload: shareableRun(payload) } : undefined;
|
|
174
183
|
})
|
|
175
184
|
// A run artifact with no id cannot be diffed against the server's list, so sending it would
|
|
176
185
|
// mean re-sending it every cycle forever. Dropped rather than uploaded repeatedly.
|
|
@@ -181,6 +190,18 @@ export function diskSource(reticleRoot) {
|
|
|
181
190
|
capsules: () => readJsonDir(join(reticleRoot, ReticleDir.CAPSULES_SUBDIR)),
|
|
182
191
|
// Intent is the only derived record that is no longer one file. See readIntent.
|
|
183
192
|
derived: (kind) => 'intent' === kind ? readIntent(reticleRoot) : readJson(join(reticleRoot, DERIVED_FILE[kind])),
|
|
193
|
+
// The file name comes from a run artifact on disk, so it is held to one segment of the run's
|
|
194
|
+
// own directory before it is read.
|
|
195
|
+
screenshot: (runId, file) => {
|
|
196
|
+
try {
|
|
197
|
+
if (basename(file) !== file)
|
|
198
|
+
return undefined;
|
|
199
|
+
return new Uint8Array(readFileSync(join(stepShotDir(reticleRoot, runId), file)));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
},
|
|
184
205
|
};
|
|
185
206
|
}
|
|
186
207
|
/** What the cycle writes back into `.reticle/`. */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** This machine's record of what went up, kept in `.reticle/cloud-state.json`. */
|
|
2
|
+
export interface ScreenshotSyncState {
|
|
3
|
+
/** Per run, `seq:sha256` of every screenshot the platform holds or refused for good. */
|
|
4
|
+
sentScreenshots?: Record<string, string[]>;
|
|
5
|
+
/** Runs whose screenshots an older platform could not take. Kept local, never offered again. */
|
|
6
|
+
screenshotsHeldLocal?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ScreenshotSyncDeps {
|
|
9
|
+
/** Every run on disk, with the payload the sync just sent. */
|
|
10
|
+
runs: ReadonlyArray<{
|
|
11
|
+
runId: string;
|
|
12
|
+
payload: unknown;
|
|
13
|
+
}>;
|
|
14
|
+
/** Whether the platform holds this run now. */
|
|
15
|
+
accepted: (runId: string) => boolean;
|
|
16
|
+
/** One image from the run's directory, or undefined when it is gone. */
|
|
17
|
+
readImage: (runId: string, file: string) => Uint8Array | undefined;
|
|
18
|
+
state: ScreenshotSyncState;
|
|
19
|
+
/** POST a JSON body to a path on the platform, with the sync's own credentials. */
|
|
20
|
+
post: (path: string, body: string) => Promise<{
|
|
21
|
+
status: number;
|
|
22
|
+
text: string;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface ScreenshotSyncResult {
|
|
26
|
+
sent: number;
|
|
27
|
+
state: ScreenshotSyncState;
|
|
28
|
+
/** Why this cycle stopped early, when it did. Kept local; the next cycle tries again. */
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Send what is owed. Never throws; the record is bounded by the runs still on disk. */
|
|
32
|
+
export declare function syncScreenshots(deps: ScreenshotSyncDeps): Promise<ScreenshotSyncResult>;
|