@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,15 @@
|
|
|
1
|
+
import { type ConfigDiscovery } from '../../command/cli/config/config-discovery.js';
|
|
2
|
+
/**
|
|
3
|
+
* The declared project that encloses `cwd`, else the one project declared below it, else `cwd`
|
|
4
|
+
* itself. Several candidates below and none enclosing is a choice the caller must make, so `cwd` is
|
|
5
|
+
* kept rather than one picked silently.
|
|
6
|
+
*/
|
|
7
|
+
export declare function linkDirectoryFor(cwd: string, discovery: ConfigDiscovery): string;
|
|
8
|
+
/**
|
|
9
|
+
* The `.reticle` a command run here owns: the project's, when this folder is in or above exactly one
|
|
10
|
+
* declared project or already holds a `.reticle`. Undefined anywhere else, so a lease opened from a
|
|
11
|
+
* home directory never plants a `.reticle` in it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function callerArtifactRoot(cwd: string): string | undefined;
|
|
14
|
+
/** `linkDirectoryFor` over this machine's own discovery from `cwd`. */
|
|
15
|
+
export declare function linkDirectoryFrom(cwd: string): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directory `reticle link` binds: the project a session from here would write its runs into.
|
|
3
|
+
*
|
|
4
|
+
* Runs land in the root the artifact resolver picks for the page's project, which comes from the same
|
|
5
|
+
* config discovery used here. Binding `cwd` instead left a monorepo's `apps/web/.reticle` unlinked
|
|
6
|
+
* while every run of that app landed in it.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { join, resolve, sep } from 'node:path';
|
|
10
|
+
import { ReticleDir } from '@reticlehq/core';
|
|
11
|
+
import { discoverProjectConfigs, hasProjectConfig, } from '../../command/cli/config/config-discovery.js';
|
|
12
|
+
/** `inner` is `outer` or sits inside it. */
|
|
13
|
+
const within = (inner, outer) => inner === outer || inner.startsWith(outer.endsWith(sep) ? outer : `${outer}${sep}`);
|
|
14
|
+
/**
|
|
15
|
+
* The declared project that encloses `cwd`, else the one project declared below it, else `cwd`
|
|
16
|
+
* itself. Several candidates below and none enclosing is a choice the caller must make, so `cwd` is
|
|
17
|
+
* kept rather than one picked silently.
|
|
18
|
+
*/
|
|
19
|
+
export function linkDirectoryFor(cwd, discovery) {
|
|
20
|
+
const here = resolve(cwd);
|
|
21
|
+
const enclosing = discovery.found
|
|
22
|
+
.map((found) => found.directory)
|
|
23
|
+
.filter((directory) => within(here, directory))
|
|
24
|
+
.sort((a, b) => b.length - a.length)[0];
|
|
25
|
+
if (enclosing !== undefined)
|
|
26
|
+
return enclosing;
|
|
27
|
+
const below = discovery.found.filter((found) => within(found.directory, here));
|
|
28
|
+
const only = 1 === below.length ? below[0] : undefined;
|
|
29
|
+
return only === undefined ? here : only.directory;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The `.reticle` a command run here owns: the project's, when this folder is in or above exactly one
|
|
33
|
+
* declared project or already holds a `.reticle`. Undefined anywhere else, so a lease opened from a
|
|
34
|
+
* home directory never plants a `.reticle` in it.
|
|
35
|
+
*/
|
|
36
|
+
export function callerArtifactRoot(cwd) {
|
|
37
|
+
const dir = linkDirectoryFrom(cwd);
|
|
38
|
+
const root = join(dir, ReticleDir.ROOT);
|
|
39
|
+
return hasProjectConfig(dir) || existsSync(root) ? root : undefined;
|
|
40
|
+
}
|
|
41
|
+
/** `linkDirectoryFor` over this machine's own discovery from `cwd`. */
|
|
42
|
+
export function linkDirectoryFrom(cwd) {
|
|
43
|
+
return linkDirectoryFor(cwd, discoverProjectConfigs(cwd));
|
|
44
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Claim `root` for the session a lease registers. Refuses anything but an absolute `.reticle`. */
|
|
2
|
+
export declare function claimArtifactRoot(leaseId: string, root: string): void;
|
|
3
|
+
/** The root claimed for this session, by its id or by the lease marker on its url. */
|
|
4
|
+
export declare function claimedArtifactRoot(sessionId: string, url: string | undefined): string | undefined;
|
|
5
|
+
/** Forget a lease's claim, once the lease is released. */
|
|
6
|
+
export declare function dropArtifactRootClaim(leaseId: string): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which `.reticle` a leased session writes into, when whoever opened the lease said so.
|
|
3
|
+
*
|
|
4
|
+
* The resolver can only go on the page's project id and origin, and a page with no project id lands
|
|
5
|
+
* in `~/.reticle/unmatched/`, which nothing links or syncs. A lease opened by `reticle try`, a chat
|
|
6
|
+
* drive or `verify` from a linked folder knows better: the run belongs to the folder that asked.
|
|
7
|
+
*
|
|
8
|
+
* Keyed by the lease id, which the leased tab carries as `__reticle_session` on its url, so a
|
|
9
|
+
* reconnect after a navigation, or an app that names its own session, still finds the claim. Lives
|
|
10
|
+
* until the lease is released.
|
|
11
|
+
*/
|
|
12
|
+
import { basename, isAbsolute } from 'node:path';
|
|
13
|
+
import { RETICLE_URL_PARAM, ReticleDir } from '@reticlehq/core';
|
|
14
|
+
const claims = new Map();
|
|
15
|
+
/** Claim `root` for the session a lease registers. Refuses anything but an absolute `.reticle`. */
|
|
16
|
+
export function claimArtifactRoot(leaseId, root) {
|
|
17
|
+
if (!isAbsolute(root) || ReticleDir.ROOT !== basename(root))
|
|
18
|
+
throw new Error(`root must be an absolute path to a ${ReticleDir.ROOT} folder, got ${root}`);
|
|
19
|
+
claims.set(leaseId, root);
|
|
20
|
+
}
|
|
21
|
+
/** The root claimed for this session, by its id or by the lease marker on its url. */
|
|
22
|
+
export function claimedArtifactRoot(sessionId, url) {
|
|
23
|
+
const direct = claims.get(sessionId);
|
|
24
|
+
if (direct !== undefined)
|
|
25
|
+
return direct;
|
|
26
|
+
if (url === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
try {
|
|
29
|
+
const lease = new URL(url).searchParams.get(RETICLE_URL_PARAM.SESSION);
|
|
30
|
+
return null === lease ? undefined : claims.get(lease);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Forget a lease's claim, once the lease is released. */
|
|
37
|
+
export function dropArtifactRootClaim(leaseId) {
|
|
38
|
+
claims.delete(leaseId);
|
|
39
|
+
}
|
|
@@ -13,6 +13,8 @@ import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
|
13
13
|
export interface ProjectTarget {
|
|
14
14
|
projectId: ProjectId | undefined;
|
|
15
15
|
origin?: string | undefined;
|
|
16
|
+
/** The root whoever leased this session claimed for it (see `root-claims.ts`), which wins. */
|
|
17
|
+
claimedRoot?: string | undefined;
|
|
16
18
|
}
|
|
17
19
|
/** The target for a caller that genuinely holds no session -- the id is all there is. */
|
|
18
20
|
export declare function projectOnly(projectId: ProjectId | undefined): ProjectTarget;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { claimedArtifactRoot } from './root-claims.js';
|
|
2
3
|
/**
|
|
3
4
|
* The scheme://host:port a session's page was served from. Local rather than the session manager's
|
|
4
5
|
* copy because `memory/project` does not reach `portal/session`, and five lines are cheaper than a
|
|
@@ -57,6 +58,8 @@ export function sessionRoot(deps, sessionId) {
|
|
|
57
58
|
* cannot disagree about what a resolved root is.
|
|
58
59
|
*/
|
|
59
60
|
export function rootForTarget(deps, target) {
|
|
61
|
+
if (target.claimedRoot !== undefined)
|
|
62
|
+
return target.claimedRoot;
|
|
60
63
|
return deps.artifactRootFor?.(target.projectId, target.origin).root ?? deps.reticleRoot;
|
|
61
64
|
}
|
|
62
65
|
/**
|
|
@@ -111,7 +114,12 @@ export function safeSessionTarget(deps, sessionId) {
|
|
|
111
114
|
export function sessionTarget(deps, sessionId) {
|
|
112
115
|
try {
|
|
113
116
|
const session = deps.sessions.resolve(sessionId);
|
|
114
|
-
|
|
117
|
+
const claimedRoot = claimedArtifactRoot(session.id, session.url);
|
|
118
|
+
return {
|
|
119
|
+
projectId: session.projectId,
|
|
120
|
+
origin: originOf(session.url),
|
|
121
|
+
...(claimedRoot === undefined ? {} : { claimedRoot }),
|
|
122
|
+
};
|
|
115
123
|
}
|
|
116
124
|
catch (cause) {
|
|
117
125
|
if (sessionId === undefined)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { CloudSyncState } from '../cloud/sync-cycle.js';
|
|
2
|
+
import { type UnsyncedRoot } from '../cloud/unsynced-roots.js';
|
|
3
|
+
import type { FileSystemPort } from './fs/fs-port.js';
|
|
2
4
|
export declare const SyncStatus: {
|
|
3
5
|
readonly ON_PLATFORM: "on-platform";
|
|
4
6
|
readonly PENDING: "pending";
|
|
@@ -22,6 +24,8 @@ export interface SyncSummary {
|
|
|
22
24
|
lastPushAt?: number;
|
|
23
25
|
/** Why the last cycle failed, when it did. */
|
|
24
26
|
lastError?: string;
|
|
27
|
+
/** The newest run the platform holds in its current form: what "see it in your dashboard" opens. */
|
|
28
|
+
latestOnPlatform?: string;
|
|
25
29
|
}
|
|
26
30
|
export declare function summarizeSync(input: {
|
|
27
31
|
linked: boolean;
|
|
@@ -31,9 +35,51 @@ export declare function summarizeSync(input: {
|
|
|
31
35
|
}[];
|
|
32
36
|
state: CloudSyncState;
|
|
33
37
|
}): SyncSummary;
|
|
38
|
+
/**
|
|
39
|
+
* Where this project's dashboard lives, if `reticle link` recorded one.
|
|
40
|
+
*
|
|
41
|
+
* Read from the link file rather than derived from the API url, because the API origin and the
|
|
42
|
+
* console origin are different hosts in every deployment that is not a laptop. Synchronous and
|
|
43
|
+
* best-effort, like every other read in this file: a missing or malformed link file means no link
|
|
44
|
+
* in the HUD, never a failed tool call.
|
|
45
|
+
*/
|
|
46
|
+
export declare function readDashboardUrl(reticleRoot: string): string | undefined;
|
|
47
|
+
/** The platform project this folder is linked to, from the same link file. */
|
|
48
|
+
export declare function readLinkedProjectId(reticleRoot: string): string | undefined;
|
|
49
|
+
/** The refused credential, in words: what a person has to act on. */
|
|
50
|
+
export declare const KEY_REFUSED = "the platform refused this project's API key: it was revoked, or its owner no longer has access to the workspace";
|
|
51
|
+
/**
|
|
52
|
+
* Why a credential was refused: the platform's own sentence when it names the reason ("You were
|
|
53
|
+
* removed from Acme.", a key whose owner lost the project), else ours. Reads an answer body.
|
|
54
|
+
*/
|
|
55
|
+
export declare function refusalSaid(body: string): string;
|
|
56
|
+
/** Why the platform refused this project's key at the last sync, or undefined. Never throws. */
|
|
57
|
+
export declare function keyRefusalAtLastSync(reticleRoot: string): string | undefined;
|
|
58
|
+
/** Whether the platform refused this project's key at the last sync. Never throws. */
|
|
59
|
+
export declare function keyRefusedAtLastSync(reticleRoot: string): boolean;
|
|
60
|
+
/** The dashboard's page for one run: the console's `/runs/:runId` route, at the dashboard's origin. */
|
|
61
|
+
export declare const DASHBOARD_RUN_PATH = "/runs/";
|
|
62
|
+
export declare const DASHBOARD_PLAN_PATH = "/plans/";
|
|
63
|
+
export declare function dashboardRunUrl(dashboardUrl: string, runId: string): string;
|
|
64
|
+
/** The line a command prints once its run reached the platform. */
|
|
65
|
+
export declare function seeRunLine(dashboardUrl: string, runId: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* A saved Harness plan's page on the dashboard, beside its runs, under its project: opened under
|
|
68
|
+
* another project's sticky scope the page answered "We could not find that plan" to its owner.
|
|
69
|
+
*/
|
|
70
|
+
export declare function dashboardPlanUrl({ dashboardUrl, planId, projectId, }: {
|
|
71
|
+
dashboardUrl: string;
|
|
72
|
+
planId: string;
|
|
73
|
+
projectId?: string | undefined;
|
|
74
|
+
}): string;
|
|
34
75
|
/** This project's summary, read from its `.reticle`. Never throws: an unreadable record is empty. */
|
|
35
76
|
export declare function readSyncSummary(reticleRoot: string, linked: boolean): SyncSummary;
|
|
36
77
|
/** One status for the whole project: the worst thing true of it. */
|
|
37
78
|
export declare function overallStatus(summary: SyncSummary): SyncStatus;
|
|
38
79
|
/** The summary in one sentence an agent can repeat to a person. */
|
|
39
80
|
export declare function describeSync(summary: SyncSummary, now: number): string;
|
|
81
|
+
/**
|
|
82
|
+
* Every project folder this machine knows with runs or flows the platform never got, linked by its OWN link
|
|
83
|
+
* file or not. `reticle sync` and `doctor` print these; a daemon tracks the same through its cycle.
|
|
84
|
+
*/
|
|
85
|
+
export declare function machineUnsyncedRoots(fs: FileSystemPort, home: string, env: NodeJS.ProcessEnv): Promise<UnsyncedRoot[]>;
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { diskSource, readCloudState } from '../cloud/sync-disk.js';
|
|
14
14
|
import { hashPayload } from '../cloud/sync-hash.js';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { ReticleDir } from '@reticlehq/core';
|
|
18
|
+
import { resolveProjectCloud } from '../cloud/cloud-config.js';
|
|
19
|
+
import { unmatchedRoots, unsyncedRoots } from '../cloud/unsynced-roots.js';
|
|
20
|
+
import { knownProjectCandidates } from './artifact-root-resolver.js';
|
|
15
21
|
export const SyncStatus = {
|
|
16
22
|
ON_PLATFORM: 'on-platform',
|
|
17
23
|
PENDING: 'pending',
|
|
@@ -25,10 +31,14 @@ export function summarizeSync(input) {
|
|
|
25
31
|
let onPlatform = 0;
|
|
26
32
|
let pending = 0;
|
|
27
33
|
const refused = [];
|
|
34
|
+
let latest;
|
|
28
35
|
if (linked) {
|
|
29
36
|
for (const run of runs) {
|
|
30
37
|
const hash = hashPayload(run.payload);
|
|
31
38
|
const refusal = state.refusedRuns?.[run.runId];
|
|
39
|
+
const at = createdAtOf(run.payload);
|
|
40
|
+
if (state.sentRunHashes?.[run.runId] === hash && (latest === undefined || at > latest.at))
|
|
41
|
+
latest = { runId: run.runId, at };
|
|
32
42
|
if (state.sentRunHashes?.[run.runId] === hash)
|
|
33
43
|
onPlatform += 1;
|
|
34
44
|
else if (refusal !== undefined && refusal.payloadHash === hash)
|
|
@@ -48,8 +58,113 @@ export function summarizeSync(input) {
|
|
|
48
58
|
...(state.lastError === undefined || 0 === state.lastError.length
|
|
49
59
|
? {}
|
|
50
60
|
: { lastError: state.lastError }),
|
|
61
|
+
...(latest === undefined ? {} : { latestOnPlatform: latest.runId }),
|
|
51
62
|
};
|
|
52
63
|
}
|
|
64
|
+
/** A run artifact's `createdAt`, or 0 when it has none. */
|
|
65
|
+
function createdAtOf(payload) {
|
|
66
|
+
const at = 'object' === typeof payload && null !== payload
|
|
67
|
+
? payload['createdAt']
|
|
68
|
+
: undefined;
|
|
69
|
+
return 'number' === typeof at ? at : 0;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Where this project's dashboard lives, if `reticle link` recorded one.
|
|
73
|
+
*
|
|
74
|
+
* Read from the link file rather than derived from the API url, because the API origin and the
|
|
75
|
+
* console origin are different hosts in every deployment that is not a laptop. Synchronous and
|
|
76
|
+
* best-effort, like every other read in this file: a missing or malformed link file means no link
|
|
77
|
+
* in the HUD, never a failed tool call.
|
|
78
|
+
*/
|
|
79
|
+
export function readDashboardUrl(reticleRoot) {
|
|
80
|
+
return linkField(reticleRoot, 'dashboardUrl');
|
|
81
|
+
}
|
|
82
|
+
/** The platform project this folder is linked to, from the same link file. */
|
|
83
|
+
export function readLinkedProjectId(reticleRoot) {
|
|
84
|
+
return linkField(reticleRoot, 'projectId');
|
|
85
|
+
}
|
|
86
|
+
function linkField(reticleRoot, field) {
|
|
87
|
+
try {
|
|
88
|
+
const raw = JSON.parse(readFileSync(join(reticleRoot, ReticleDir.CLOUD_LINK_FILE), 'utf8'));
|
|
89
|
+
if ('object' !== typeof raw || null === raw)
|
|
90
|
+
return undefined;
|
|
91
|
+
const value = raw[field];
|
|
92
|
+
return 'string' === typeof value && value.length > 0 ? value : undefined;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** How the sync loop records a refused credential in its state. */
|
|
99
|
+
const REFUSED_SYNC = /^status 40[13]\b/;
|
|
100
|
+
/** What precedes the answer's body in that record: `status 401: `. */
|
|
101
|
+
const REFUSED_PREFIX = /^status \d+: /;
|
|
102
|
+
/** The refused credential, in words: what a person has to act on. */
|
|
103
|
+
export const KEY_REFUSED = "the platform refused this project's API key: it was revoked, or its owner no longer has access to the workspace";
|
|
104
|
+
/** The refusals the platform explains in its own words (its `error.code`). */
|
|
105
|
+
const EXPLAINED_REFUSALS = new Set([
|
|
106
|
+
'removed_from_workspace',
|
|
107
|
+
'key_lost_access',
|
|
108
|
+
]);
|
|
109
|
+
/**
|
|
110
|
+
* Why a credential was refused: the platform's own sentence when it names the reason ("You were
|
|
111
|
+
* removed from Acme.", a key whose owner lost the project), else ours. Reads an answer body.
|
|
112
|
+
*/
|
|
113
|
+
export function refusalSaid(body) {
|
|
114
|
+
try {
|
|
115
|
+
const error = JSON.parse(body)
|
|
116
|
+
?.error;
|
|
117
|
+
if ('string' === typeof error?.code &&
|
|
118
|
+
EXPLAINED_REFUSALS.has(error.code) &&
|
|
119
|
+
'string' === typeof error.message &&
|
|
120
|
+
0 < error.message.length)
|
|
121
|
+
return error.message;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// Not the platform's JSON: our own sentence stands.
|
|
125
|
+
}
|
|
126
|
+
return KEY_REFUSED;
|
|
127
|
+
}
|
|
128
|
+
/** Why the platform refused this project's key at the last sync, or undefined. Never throws. */
|
|
129
|
+
export function keyRefusalAtLastSync(reticleRoot) {
|
|
130
|
+
return keyRefusalIn(readCloudState(reticleRoot).lastError);
|
|
131
|
+
}
|
|
132
|
+
/** The refusal a recorded sync error carries, in words, or undefined when it is another failure. */
|
|
133
|
+
function keyRefusalIn(error) {
|
|
134
|
+
if ('string' !== typeof error || !REFUSED_SYNC.test(error))
|
|
135
|
+
return undefined;
|
|
136
|
+
return refusalSaid(error.replace(REFUSED_PREFIX, ''));
|
|
137
|
+
}
|
|
138
|
+
/** Whether the platform refused this project's key at the last sync. Never throws. */
|
|
139
|
+
export function keyRefusedAtLastSync(reticleRoot) {
|
|
140
|
+
return keyRefusalAtLastSync(reticleRoot) !== undefined;
|
|
141
|
+
}
|
|
142
|
+
/** The dashboard's page for one run: the console's `/runs/:runId` route, at the dashboard's origin. */
|
|
143
|
+
export const DASHBOARD_RUN_PATH = '/runs/';
|
|
144
|
+
export const DASHBOARD_PLAN_PATH = '/plans/';
|
|
145
|
+
export function dashboardRunUrl(dashboardUrl, runId) {
|
|
146
|
+
return `${dashboardOrigin(dashboardUrl)}${DASHBOARD_RUN_PATH}${encodeURIComponent(runId)}`;
|
|
147
|
+
}
|
|
148
|
+
function dashboardOrigin(dashboardUrl) {
|
|
149
|
+
try {
|
|
150
|
+
return new URL(dashboardUrl).origin;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return dashboardUrl.replace(/\/+$/, ''); // not a url: use it as given
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** The line a command prints once its run reached the platform. */
|
|
157
|
+
export function seeRunLine(dashboardUrl, runId) {
|
|
158
|
+
return `See it in your dashboard: ${dashboardRunUrl(dashboardUrl, runId)}`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A saved Harness plan's page on the dashboard, beside its runs, under its project: opened under
|
|
162
|
+
* another project's sticky scope the page answered "We could not find that plan" to its owner.
|
|
163
|
+
*/
|
|
164
|
+
export function dashboardPlanUrl({ dashboardUrl, planId, projectId, }) {
|
|
165
|
+
const page = `${dashboardOrigin(dashboardUrl)}${DASHBOARD_PLAN_PATH}${encodeURIComponent(planId)}`;
|
|
166
|
+
return projectId === undefined ? page : `${page}?project=${encodeURIComponent(projectId)}`;
|
|
167
|
+
}
|
|
53
168
|
/** This project's summary, read from its `.reticle`. Never throws: an unreadable record is empty. */
|
|
54
169
|
export function readSyncSummary(reticleRoot, linked) {
|
|
55
170
|
let runs = [];
|
|
@@ -84,7 +199,26 @@ export function describeSync(summary, now) {
|
|
|
84
199
|
const named = summary.refused.map((r) => `${r.runId}: ${r.reason}`);
|
|
85
200
|
if (0 < named.length)
|
|
86
201
|
parts.push(`refused by the platform: ${named.join('; ')}${0 < summary.refusedMore ? ` (+${String(summary.refusedMore)} more)` : ''}`);
|
|
87
|
-
|
|
202
|
+
const refusal = keyRefusalIn(summary.lastError);
|
|
203
|
+
if (refusal !== undefined)
|
|
204
|
+
parts.push(`the last push was refused: ${refusal}`);
|
|
205
|
+
else if (summary.lastError !== undefined)
|
|
88
206
|
parts.push(`the last push failed: ${summary.lastError}`);
|
|
89
207
|
return `${parts.join('; ')} (${ago}).`;
|
|
90
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Every project folder this machine knows with runs or flows the platform never got, linked by its OWN link
|
|
211
|
+
* file or not. `reticle sync` and `doctor` print these; a daemon tracks the same through its cycle.
|
|
212
|
+
*/
|
|
213
|
+
export async function machineUnsyncedRoots(fs, home, env) {
|
|
214
|
+
const roots = [
|
|
215
|
+
...knownProjectCandidates().map((c) => join(c.directory, ReticleDir.ROOT)),
|
|
216
|
+
// And what no project claimed: nothing sends it, and nothing named it after the first warning.
|
|
217
|
+
...unmatchedRoots(home),
|
|
218
|
+
];
|
|
219
|
+
const cloudOf = (root) => resolveProjectCloud(fs, root, home, env);
|
|
220
|
+
return unsyncedRoots(roots, async (root) => {
|
|
221
|
+
const cloud = await cloudOf(root);
|
|
222
|
+
return null !== cloud.config && null !== cloud.projectId;
|
|
223
|
+
}, async (root) => (await cloudOf(root)).policy.flows);
|
|
224
|
+
}
|
|
@@ -17,7 +17,12 @@ import { sessionIdShape } from '../../surface/tools/tool-kit.js';
|
|
|
17
17
|
import { asNumber, asString } from '@reticlehq/core';
|
|
18
18
|
import { sessionRoot } from '../project/session-root.js';
|
|
19
19
|
import { MemoryUnavailable, readProjectMemory } from './project-memory.js';
|
|
20
|
+
import { MemoryAction, addMemoryStatement, fetchPlatformFlow, pushFlow, setAsideStatement, } from './platform-memory.js';
|
|
21
|
+
import { flowsForSession } from '../../language/flows/flow-store-for-session.js';
|
|
22
|
+
import { safeSessionTarget } from '../project/session-root.js';
|
|
20
23
|
import { homedir } from 'node:os';
|
|
24
|
+
const NEEDS_NAME = 'needs `name`: the flow’s name';
|
|
25
|
+
const NEEDS_STATEMENT = 'needs `subject` and `statement`';
|
|
21
26
|
/**
|
|
22
27
|
* Enough to orient on, not enough to flood a context.
|
|
23
28
|
*
|
|
@@ -31,13 +36,18 @@ const ADVICE = {
|
|
|
31
36
|
[MemoryUnavailable.DISABLED]: 'this project is linked but memory sync is turned off in .reticle/cloud.json, so nothing is being shared. Enable `sync.memory` to use it.',
|
|
32
37
|
[MemoryUnavailable.UNREACHABLE]: 'the workspace could not be reached, or refused the key. Nothing is wrong with this project — try again, or check the link with `reticle status`.',
|
|
33
38
|
[MemoryUnavailable.UNVERIFIED]: 'the server answered, but the answer could not be shown to be about THIS project, so none of it is being reported. This is not an empty corpus: do NOT conclude this project knows nothing. Check the link with `reticle status`, and retry once the workspace is reachable.',
|
|
34
|
-
[MemoryUnavailable.UNREADABLE]: 'the workspace answered about THIS project, but the body was a shape this build cannot read entries out of. Retrying will not change it — this is not an empty corpus and not a link problem. Check the workspace version, and that `reticle memory` from the same terminal returns a `{ projectId, entries }` body.',
|
|
39
|
+
[MemoryUnavailable.UNREADABLE]: 'the workspace answered about THIS project, but the body was a shape this build cannot read entries out of. Retrying will not change it — this is not an empty corpus and not a link problem. Check the workspace version, and that `reticle runs memory` from the same terminal returns a `{ projectId, entries }` body.',
|
|
35
40
|
};
|
|
36
41
|
export const MEMORY_TOOLS = [
|
|
37
42
|
{
|
|
38
43
|
name: ReticleTool.MEMORY,
|
|
39
|
-
description: 'Ask what THIS PROJECT already knows — the shared memory its team has built up, written by every engineer who verified something and readable by every agent since. Use it BEFORE re-deriving how a feature is supposed to behave: a colleague may already have proved it, and a statement marked `proved` was established by a real verdict rather than written down. With { subject } it narrows to one area (a flow name, or a route like "checkout"); without, it returns across the project. Each entry carries the flow and the source file it is about, so a claim can be checked. The read is recorded as an agent consultation, which is what the dashboard\'s per-flow fetch counts are built from. Returns { subject, known, total } or { error, reason, advice } when this project is not linked to a workspace.'
|
|
44
|
+
description: 'Ask what THIS PROJECT already knows — the shared memory its team has built up, written by every engineer who verified something and readable by every agent since. Use it BEFORE re-deriving how a feature is supposed to behave: a colleague may already have proved it, and a statement marked `proved` was established by a real verdict rather than written down. With { subject } it narrows to one area (a flow name, or a route like "checkout"); without, it returns across the project. Each entry carries the flow and the source file it is about, so a claim can be checked. The read is recorded as an agent consultation, which is what the dashboard\'s per-flow fetch counts are built from. Returns { subject, known, total } or { error, reason, advice } when this project is not linked to a workspace. ' +
|
|
45
|
+
'Other actions, each answered by the platform: "flow" { name } reads one saved flow from the platform (a teammate\'s too); "add" { subject, statement } writes a statement (lands as proposed); "push-flow" { name } uploads this machine\'s saved flow, replacing the platform copy; "set-aside" { id } marks a statement stale so nobody relies on it (it is kept, not deleted).',
|
|
40
46
|
inputSchema: {
|
|
47
|
+
action: z.enum(Object.values(MemoryAction)).optional(),
|
|
48
|
+
name: z.string().optional().describe('flow / push-flow: the flow name.'),
|
|
49
|
+
statement: z.string().optional().describe('add: what is true, in prose.'),
|
|
50
|
+
id: z.string().optional().describe('set-aside: the statement id.'),
|
|
41
51
|
subject: z
|
|
42
52
|
.string()
|
|
43
53
|
.optional()
|
|
@@ -48,6 +58,10 @@ export const MEMORY_TOOLS = [
|
|
|
48
58
|
...sessionIdShape,
|
|
49
59
|
},
|
|
50
60
|
outputSchema: {
|
|
61
|
+
flow: z.unknown().optional(),
|
|
62
|
+
added: z.unknown().optional(),
|
|
63
|
+
pushed: z.string().optional(),
|
|
64
|
+
setAside: z.string().optional(),
|
|
51
65
|
subject: z.string().nullable().optional(),
|
|
52
66
|
known: z.array(z.unknown()).optional(),
|
|
53
67
|
total: z.number().optional(),
|
|
@@ -65,8 +79,37 @@ export const MEMORY_TOOLS = [
|
|
|
65
79
|
* has now been fixed three times in this codebase; routing through sessionRoot is what stops
|
|
66
80
|
* a fourth.
|
|
67
81
|
*/
|
|
68
|
-
const
|
|
82
|
+
const sid = asString(args['sessionId']);
|
|
83
|
+
const root = sessionRoot(deps, sid);
|
|
69
84
|
const subject = asString(args['subject']);
|
|
85
|
+
const action = asString(args['action']) ?? MemoryAction.READ;
|
|
86
|
+
const name = asString(args['name']);
|
|
87
|
+
if (MemoryAction.SET_ASIDE === action) {
|
|
88
|
+
const id = asString(args['id']);
|
|
89
|
+
if (id === undefined || 0 === id.trim().length)
|
|
90
|
+
return { error: `${action} needs \`id\`: the statement id from a read` };
|
|
91
|
+
return setAsideStatement(deps.fs, root, id);
|
|
92
|
+
}
|
|
93
|
+
if (MemoryAction.FLOW === action) {
|
|
94
|
+
if (name === undefined)
|
|
95
|
+
return { error: `${action} ${NEEDS_NAME}` };
|
|
96
|
+
return fetchPlatformFlow(deps.fs, root, name);
|
|
97
|
+
}
|
|
98
|
+
if (MemoryAction.PUSH_FLOW === action) {
|
|
99
|
+
if (name === undefined)
|
|
100
|
+
return { error: `${action} ${NEEDS_NAME}` };
|
|
101
|
+
const target = safeSessionTarget(deps, sid);
|
|
102
|
+
const saved = await flowsForSession(deps, target).flows.load(name, target.projectId);
|
|
103
|
+
if (!saved.ok)
|
|
104
|
+
return { error: `no saved flow named "${name}" on this machine`, reason: saved.code };
|
|
105
|
+
return pushFlow(deps.fs, root, saved.value, target.projectId);
|
|
106
|
+
}
|
|
107
|
+
if (MemoryAction.ADD === action) {
|
|
108
|
+
const statement = asString(args['statement']);
|
|
109
|
+
if (subject === undefined || statement === undefined || 0 === statement.trim().length)
|
|
110
|
+
return { error: `${action} ${NEEDS_STATEMENT}` };
|
|
111
|
+
return addMemoryStatement(deps.fs, root, { subject, statement });
|
|
112
|
+
}
|
|
70
113
|
const result = await readProjectMemory(deps.fs, root, homedir(), process.env, {
|
|
71
114
|
subject,
|
|
72
115
|
limit: asNumber(args['limit']) ?? DEFAULT_LIMIT,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FileSystemPort } from '../project/fs/fs-port.js';
|
|
2
|
+
import type { FlowFile } from '@reticlehq/core';
|
|
3
|
+
/** What `reticle_memory` can be asked to do. `read` is the default and needs no `action`. */
|
|
4
|
+
export declare const MemoryAction: {
|
|
5
|
+
readonly READ: "read";
|
|
6
|
+
readonly FLOW: "flow";
|
|
7
|
+
readonly ADD: "add";
|
|
8
|
+
readonly SET_ASIDE: "set-aside";
|
|
9
|
+
readonly PUSH_FLOW: "push-flow";
|
|
10
|
+
};
|
|
11
|
+
export type MemoryAction = (typeof MemoryAction)[keyof typeof MemoryAction];
|
|
12
|
+
/** Why the platform would not set a statement aside, each from its own answer. */
|
|
13
|
+
export declare const SetAsideRefused: {
|
|
14
|
+
readonly NO_SUCH_STATEMENT: "no-such-statement";
|
|
15
|
+
readonly NOT_ALLOWED: "not-allowed";
|
|
16
|
+
readonly REFUSED: "refused";
|
|
17
|
+
};
|
|
18
|
+
export type PlatformAnswer = Record<string, unknown>;
|
|
19
|
+
/** One saved flow from the platform: a teammate's, or this project's as the platform holds it. */
|
|
20
|
+
export declare function fetchPlatformFlow(fs: FileSystemPort, root: string, name: string): Promise<PlatformAnswer>;
|
|
21
|
+
/** Add one statement to the project's memory. It lands as a proposal; only a passing check proves it. */
|
|
22
|
+
export declare function addMemoryStatement(fs: FileSystemPort, root: string, entry: {
|
|
23
|
+
subject: string;
|
|
24
|
+
statement: string;
|
|
25
|
+
flowName?: string | undefined;
|
|
26
|
+
}): Promise<PlatformAnswer>;
|
|
27
|
+
/** Put this machine's saved flow on the platform now, replacing the platform's copy of that name. */
|
|
28
|
+
export declare function pushFlow(fs: FileSystemPort, root: string, flow: FlowFile, projectId: string | undefined): Promise<PlatformAnswer>;
|
|
29
|
+
/**
|
|
30
|
+
* Set one statement aside: the platform keeps it, marked stale, and stops offering it as known. The
|
|
31
|
+
* one change a project key may make to a statement; proving one takes a passing check, and new
|
|
32
|
+
* words on a settled one are the team's to agree.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setAsideStatement(fs: FileSystemPort, root: string, intentId: string): Promise<PlatformAnswer>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent's writes to the platform, and its read of one flow, beside `readProjectMemory`.
|
|
3
|
+
*
|
|
4
|
+
* Every call here is answered by the platform before the tool answers, so an agent is told whether
|
|
5
|
+
* the platform has it rather than trusting a later sync cycle to carry it. Each reads the link of the
|
|
6
|
+
* project being driven (its `.reticle`, not the daemon's) and names that project on the wire.
|
|
7
|
+
*/
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { cloudFetch, syncFlowToCloud, SyncOutcome } from '../cloud/cloud-sync.js';
|
|
10
|
+
import { resolveProjectCloud } from '../cloud/cloud-config.js';
|
|
11
|
+
import { MemoryUnavailable } from './project-memory.js';
|
|
12
|
+
/** What `reticle_memory` can be asked to do. `read` is the default and needs no `action`. */
|
|
13
|
+
export const MemoryAction = {
|
|
14
|
+
READ: 'read',
|
|
15
|
+
FLOW: 'flow',
|
|
16
|
+
ADD: 'add',
|
|
17
|
+
SET_ASIDE: 'set-aside',
|
|
18
|
+
PUSH_FLOW: 'push-flow',
|
|
19
|
+
};
|
|
20
|
+
/** Why the platform would not set a statement aside, each from its own answer. */
|
|
21
|
+
export const SetAsideRefused = {
|
|
22
|
+
NO_SUCH_STATEMENT: 'no-such-statement',
|
|
23
|
+
NOT_ALLOWED: 'not-allowed',
|
|
24
|
+
REFUSED: 'refused',
|
|
25
|
+
};
|
|
26
|
+
/** The status a statement set aside takes on the platform: kept, but no longer relied on. */
|
|
27
|
+
const STALE = 'stale';
|
|
28
|
+
const MEMORY_PATH = '/v1/memory';
|
|
29
|
+
const FLOWS_PATH = '/v1/flows';
|
|
30
|
+
const NOT_FOUND = 'not-found';
|
|
31
|
+
const HTTP_NOT_FOUND = 404;
|
|
32
|
+
const HTTP_FORBIDDEN = 403;
|
|
33
|
+
const HTTP_BAD_REQUEST = 400;
|
|
34
|
+
const unavailable = (reason, error) => ({ error, reason });
|
|
35
|
+
const UNREACHABLE = unavailable(MemoryUnavailable.UNREACHABLE, 'the platform could not be reached, or refused this project’s key');
|
|
36
|
+
/** The link, or the reason there is none. Writes honour `sync.memory` / `sync.flows` like the sync. */
|
|
37
|
+
async function linked(fs, root, allowed) {
|
|
38
|
+
const cloud = await resolveProjectCloud(fs, root, homedir(), process.env);
|
|
39
|
+
const { config } = cloud;
|
|
40
|
+
if (null === config)
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
answer: unavailable(MemoryUnavailable.NOT_LINKED, 'this project is not linked to the platform'),
|
|
44
|
+
};
|
|
45
|
+
if (!allowed(cloud))
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
answer: unavailable(MemoryUnavailable.DISABLED, 'this kind of sync is off in .reticle/cloud.json'),
|
|
49
|
+
};
|
|
50
|
+
return { ok: true, cloud: { ...cloud, config } };
|
|
51
|
+
}
|
|
52
|
+
const headers = (apiKey) => ({
|
|
53
|
+
'content-type': 'application/json',
|
|
54
|
+
authorization: `Bearer ${apiKey}`,
|
|
55
|
+
});
|
|
56
|
+
const projectQuery = (projectId) => null === projectId ? '' : `?projectId=${encodeURIComponent(projectId)}`;
|
|
57
|
+
/** One saved flow from the platform: a teammate's, or this project's as the platform holds it. */
|
|
58
|
+
export async function fetchPlatformFlow(fs, root, name) {
|
|
59
|
+
const link = await linked(fs, root, (c) => c.policy.flows);
|
|
60
|
+
if (!link.ok)
|
|
61
|
+
return link.answer;
|
|
62
|
+
const { config, projectId } = link.cloud;
|
|
63
|
+
try {
|
|
64
|
+
const res = await cloudFetch(`${config.url}${FLOWS_PATH}/${encodeURIComponent(name)}${projectQuery(projectId)}`, { method: 'GET', headers: headers(config.apiKey) });
|
|
65
|
+
if (HTTP_NOT_FOUND === res.status)
|
|
66
|
+
return unavailable(NOT_FOUND, `the platform holds no flow named "${name}" for this project`);
|
|
67
|
+
if (200 !== res.status)
|
|
68
|
+
return UNREACHABLE;
|
|
69
|
+
const flow = await res.json();
|
|
70
|
+
return { flow };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return UNREACHABLE;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Add one statement to the project's memory. It lands as a proposal; only a passing check proves it. */
|
|
77
|
+
export async function addMemoryStatement(fs, root, entry) {
|
|
78
|
+
const link = await linked(fs, root, (c) => c.policy.memory);
|
|
79
|
+
if (!link.ok)
|
|
80
|
+
return link.answer;
|
|
81
|
+
const { config, projectId } = link.cloud;
|
|
82
|
+
try {
|
|
83
|
+
const res = await cloudFetch(`${config.url}${MEMORY_PATH}`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: headers(config.apiKey),
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
subject: entry.subject,
|
|
88
|
+
statement: entry.statement,
|
|
89
|
+
...(entry.flowName === undefined ? {} : { flowName: entry.flowName }),
|
|
90
|
+
...(null === projectId ? {} : { projectId }),
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
if (res.status >= 300)
|
|
94
|
+
return UNREACHABLE;
|
|
95
|
+
const added = await res.json();
|
|
96
|
+
return { added };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return UNREACHABLE;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Put this machine's saved flow on the platform now, replacing the platform's copy of that name. */
|
|
103
|
+
export async function pushFlow(fs, root, flow, projectId) {
|
|
104
|
+
const link = await linked(fs, root, (c) => c.policy.flows);
|
|
105
|
+
if (!link.ok)
|
|
106
|
+
return link.answer;
|
|
107
|
+
const result = await syncFlowToCloud(flow, link.cloud.config, projectId, cloudFetch);
|
|
108
|
+
return SyncOutcome.SYNCED === result.outcome ? { pushed: flow.name } : UNREACHABLE;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Set one statement aside: the platform keeps it, marked stale, and stops offering it as known. The
|
|
112
|
+
* one change a project key may make to a statement; proving one takes a passing check, and new
|
|
113
|
+
* words on a settled one are the team's to agree.
|
|
114
|
+
*/
|
|
115
|
+
export async function setAsideStatement(fs, root, intentId) {
|
|
116
|
+
const link = await linked(fs, root, (c) => c.policy.memory);
|
|
117
|
+
if (!link.ok)
|
|
118
|
+
return link.answer;
|
|
119
|
+
const { config } = link.cloud;
|
|
120
|
+
try {
|
|
121
|
+
const res = await cloudFetch(`${config.url}${MEMORY_PATH}/${encodeURIComponent(intentId)}`, {
|
|
122
|
+
method: 'PATCH',
|
|
123
|
+
headers: headers(config.apiKey),
|
|
124
|
+
body: JSON.stringify({ status: STALE }),
|
|
125
|
+
});
|
|
126
|
+
if (HTTP_NOT_FOUND === res.status)
|
|
127
|
+
return unavailable(SetAsideRefused.NO_SUCH_STATEMENT, `the platform holds no statement "${intentId}" in this project. Read reticle_memory again for the current ids.`);
|
|
128
|
+
if (HTTP_FORBIDDEN === res.status)
|
|
129
|
+
return unavailable(SetAsideRefused.NOT_ALLOWED, 'the platform refused: the teammate who made this project’s key may not change flows or memory. Ask the user to set it aside on the dashboard (Memory), and meanwhile do not rely on it.');
|
|
130
|
+
if (HTTP_BAD_REQUEST === res.status)
|
|
131
|
+
return unavailable(SetAsideRefused.REFUSED, 'the platform refused the change: with a project key a statement can only be set aside.');
|
|
132
|
+
if (res.status >= 300)
|
|
133
|
+
return UNREACHABLE;
|
|
134
|
+
return { setAside: intentId };
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return UNREACHABLE;
|
|
138
|
+
}
|
|
139
|
+
}
|