@reticlehq/server 3.7.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -24,19 +24,26 @@ import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, } fr
|
|
|
24
24
|
import { DevServerEntrySchema, ReticleDir, ReticleEnv, devServerRegistryPort, } from '@reticlehq/core';
|
|
25
25
|
import { discoverProjectConfigs, hasProjectConfig, } from '../../command/cli/config/config-discovery.js';
|
|
26
26
|
import { log } from '../../log.js';
|
|
27
|
-
import {
|
|
27
|
+
import { isUnmatchedRoot } from './dir/reticle-dir.js';
|
|
28
|
+
import { checkoutAt, hasLinkedWorktrees } from '../../command/cli/config/checkout.js';
|
|
29
|
+
import { ArtifactRootReason, projectCandidatesFrom, rebaseIntoCheckout, resolveArtifactRoot, unmatchedRoot, } from './artifact-root.js';
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
31
|
+
* May the daemon write into this `.reticle` root? This is the one answer for every writer.
|
|
30
32
|
*
|
|
31
|
-
*
|
|
32
|
-
* two tabs connecting, and this is the IO half `unmatchedRoot` deliberately does not do.
|
|
33
|
+
* The resolver asks it ONCE for the daemon's own root: a directory does not become a project
|
|
34
|
+
* between two tabs connecting, and this is the IO half `unmatchedRoot` deliberately does not do.
|
|
35
|
+
* A writer that holds a root for the daemon's life (the impact ledger) asks it on each write, so
|
|
36
|
+
* that it starts to write into the project when `init` wires it.
|
|
33
37
|
*/
|
|
34
|
-
function
|
|
38
|
+
export function mayWriteIntoRoot(reticleRoot) {
|
|
35
39
|
try {
|
|
36
40
|
// Either mark counts. `.reticle.json` means somebody ran `init` here; an existing `.reticle/`
|
|
37
41
|
// means Reticle has been invited into this tree before, including by a version that predates
|
|
38
42
|
// the config file. A directory with neither has never agreed to hold anybody's session data.
|
|
39
|
-
|
|
43
|
+
// An unmatched bucket is in the user's own `~/.reticle`, which is Reticle's to write.
|
|
44
|
+
return (existsSync(reticleRoot) ||
|
|
45
|
+
hasProjectConfig(dirname(reticleRoot)) ||
|
|
46
|
+
isUnmatchedRoot(reticleRoot));
|
|
40
47
|
}
|
|
41
48
|
catch {
|
|
42
49
|
// Unreadable is not a licence to write. Treat it as somebody else's directory.
|
|
@@ -46,7 +53,7 @@ function daemonSitsInAProject(daemonRoot) {
|
|
|
46
53
|
/**
|
|
47
54
|
* The project a candidate list says lives at the daemon's own directory.
|
|
48
55
|
*
|
|
49
|
-
* Not the same question as `
|
|
56
|
+
* Not the same question as `mayWriteIntoRoot`: that one asks whether the tree invited Reticle in
|
|
50
57
|
* at all (either mark counts, including a bare `.reticle/` from a version that predates the config
|
|
51
58
|
* file), and this one asks WHICH project it is. The difference is the whole of #1244 — a daemon in
|
|
52
59
|
* repo A serving an app from a git worktree B belongs to A and is being asked about B.
|
|
@@ -115,12 +122,14 @@ function announcedDevServers() {
|
|
|
115
122
|
const servers = [];
|
|
116
123
|
try {
|
|
117
124
|
for (const name of readdirSync(dir)) {
|
|
118
|
-
if (null === devServerRegistryPort(name))
|
|
125
|
+
if (null === devServerRegistryPort(name)) {
|
|
119
126
|
continue;
|
|
127
|
+
}
|
|
120
128
|
try {
|
|
121
129
|
const parsed = DevServerEntrySchema.safeParse(JSON.parse(readFileSync(join(dir, name), 'utf8')));
|
|
122
|
-
if (parsed.success)
|
|
130
|
+
if (parsed.success) {
|
|
123
131
|
servers.push({ projectId: parsed.data.projectId, root: parsed.data.root });
|
|
132
|
+
}
|
|
124
133
|
}
|
|
125
134
|
catch {
|
|
126
135
|
/* one bad announcement does not hide the others */
|
|
@@ -142,8 +151,9 @@ function announcedDevServers() {
|
|
|
142
151
|
* own project id.
|
|
143
152
|
*/
|
|
144
153
|
export function projectDirectoryFor(projectId) {
|
|
145
|
-
if (projectId === undefined)
|
|
154
|
+
if (projectId === undefined) {
|
|
146
155
|
return undefined;
|
|
156
|
+
}
|
|
147
157
|
const directories = new Set(knownProjectCandidates()
|
|
148
158
|
.filter((candidate) => candidate.projectId === projectId)
|
|
149
159
|
.map((candidate) => candidate.directory));
|
|
@@ -153,7 +163,7 @@ export function projectDirectoryFor(projectId) {
|
|
|
153
163
|
}
|
|
154
164
|
export function artifactRootResolver(daemonRoot, deps) {
|
|
155
165
|
const candidates = deps.candidates ?? knownProjectCandidates;
|
|
156
|
-
const sitsInAProject = deps.daemonIsProject ??
|
|
166
|
+
const sitsInAProject = deps.daemonIsProject ?? mayWriteIntoRoot;
|
|
157
167
|
const { servingDirectory } = deps;
|
|
158
168
|
const daemonIsProject = sitsInAProject(daemonRoot);
|
|
159
169
|
// Resolved beside it and ONCE, for the same reason: a directory's `.reticle.json` does not change
|
|
@@ -164,15 +174,29 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
164
174
|
: undefined;
|
|
165
175
|
/** Said once per project, reason and folder: one session's calls repeated it 16 times in 3s. */
|
|
166
176
|
const told = new Set();
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
const checkout = deps.checkout ?? checkoutAt;
|
|
178
|
+
const hasWorktrees = deps.hasWorktrees ?? hasLinkedWorktrees;
|
|
179
|
+
/**
|
|
180
|
+
* The one folder `directories` stand for in the checkout that served the page, or undefined when
|
|
181
|
+
* they do not agree. Every worktree of a project declares the same id, and discovery usually knows
|
|
182
|
+
* only some of them, so the page's own checkout decides and a worktree's work stays on its branch.
|
|
183
|
+
*/
|
|
184
|
+
const inServingCheckout = (directories, serving) => {
|
|
185
|
+
const to = serving === undefined ? undefined : checkout(serving);
|
|
186
|
+
const moved = new Set(directories.map((d) => rebaseIntoCheckout(d, checkout(d), to)));
|
|
187
|
+
return 1 === moved.size ? [...moved][0] : undefined;
|
|
174
188
|
};
|
|
175
189
|
return (projectId, origin) => {
|
|
190
|
+
// Asked at most once for each connection, and again for the next one: it can shell out, and a
|
|
191
|
+
// port that moves to another worktree is a new connection whose work must move with it.
|
|
192
|
+
let served;
|
|
193
|
+
const serving = () => {
|
|
194
|
+
if (origin === undefined) {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
served ??= { directory: servingDirectory(origin) };
|
|
198
|
+
return served.directory;
|
|
199
|
+
};
|
|
176
200
|
const known = candidates();
|
|
177
201
|
let resolved = resolveArtifactRoot({ projectId, candidates: known, daemonRoot });
|
|
178
202
|
// Several checkouts declare this project — every git worktree carries the same committed
|
|
@@ -183,11 +207,32 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
183
207
|
projectId,
|
|
184
208
|
candidates: known,
|
|
185
209
|
daemonRoot,
|
|
186
|
-
servingDirectory:
|
|
210
|
+
servingDirectory: serving(),
|
|
187
211
|
});
|
|
212
|
+
// Worktrees nested in the main checkout (`.claude/worktrees/a`) are all candidates, and a page
|
|
213
|
+
// served from another worktree is inside none of them. It fell back to the daemon's folder.
|
|
214
|
+
const directory = resolved.reason === ArtifactRootReason.AMBIGUOUS
|
|
215
|
+
? inServingCheckout(resolved.candidates ?? [], serving())
|
|
216
|
+
: undefined;
|
|
217
|
+
if (directory !== undefined) {
|
|
218
|
+
resolved = {
|
|
219
|
+
root: join(directory, ReticleDir.ROOT),
|
|
220
|
+
reason: ArtifactRootReason.MATCHED_PROJECT,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (resolved.reason === ArtifactRootReason.MATCHED_PROJECT) {
|
|
225
|
+
const project = dirname(resolved.root);
|
|
226
|
+
const from = origin === undefined ? undefined : checkout(project);
|
|
227
|
+
// Only a repository that HAS worktrees pays for the serving lookup.
|
|
228
|
+
if (from === undefined || !hasWorktrees(from.commonDir)) {
|
|
229
|
+
return resolved;
|
|
230
|
+
}
|
|
231
|
+
const moved = inServingCheckout([project], serving());
|
|
232
|
+
return moved === undefined || moved === project
|
|
233
|
+
? resolved
|
|
234
|
+
: { ...resolved, root: join(moved, ReticleDir.ROOT) };
|
|
188
235
|
}
|
|
189
|
-
if (resolved.reason === ArtifactRootReason.MATCHED_PROJECT)
|
|
190
|
-
return resolved;
|
|
191
236
|
// Could not name the project. The old code wrote into the daemon's directory anyway and said
|
|
192
237
|
// nothing, which put `.reticle/` — journals included — into repositories nobody had
|
|
193
238
|
// instrumented. Evidence still has to live somewhere, so it goes to the user's own
|
|
@@ -219,7 +264,7 @@ export function artifactRootResolver(daemonRoot, deps) {
|
|
|
219
264
|
}
|
|
220
265
|
// Named so the agent is told WHICH checkout's work is stranded: a clone serving the app was
|
|
221
266
|
// never named, only the primary checkout's folders.
|
|
222
|
-
const
|
|
223
|
-
return { ...resolved, root, ...(
|
|
267
|
+
const servedFrom = serving();
|
|
268
|
+
return { ...resolved, root, ...(servedFrom === undefined ? {} : { serving: servedFrom }) };
|
|
224
269
|
};
|
|
225
270
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
|
|
2
|
-
import { type ProjectCandidate, type ProjectId, type ProjectRegistry } from '@reticlehq/core';
|
|
2
|
+
import { type Checkout, type ProjectCandidate, type ProjectId, type ProjectRegistry } from '@reticlehq/core';
|
|
3
3
|
import type { ConfigDiscovery } from '../../command/cli/config/config-discovery.js';
|
|
4
4
|
/**
|
|
5
5
|
* Which `.reticle/` a session's artifacts belong in.
|
|
@@ -46,11 +46,16 @@ interface ArtifactRootQuery {
|
|
|
46
46
|
daemonRoot: string;
|
|
47
47
|
/**
|
|
48
48
|
* The directory the dev server that served this page runs in, when the caller could observe it.
|
|
49
|
-
*
|
|
49
|
+
* Consulted to break an AMBIGUOUS tie: every git worktree carries the same committed
|
|
50
50
|
* `.reticle.json`, so the projectId cannot tell them apart, but the serving process runs inside
|
|
51
|
-
* exactly one of them.
|
|
51
|
+
* exactly one of them. A lone match in a different tree from it is a second checkout, refused.
|
|
52
52
|
*/
|
|
53
53
|
servingDirectory?: string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* The platform whose path rules apply. Windows paths are not case-sensitive, so `C:\app` and
|
|
56
|
+
* `c:\app` are one folder there. Defaults to this process's platform; a test names it.
|
|
57
|
+
*/
|
|
58
|
+
platform?: NodeJS.Platform;
|
|
54
59
|
}
|
|
55
60
|
export interface ArtifactRoot {
|
|
56
61
|
/** Absolute path to the `.reticle` directory. Always present, on every branch. */
|
|
@@ -71,6 +76,15 @@ export interface ArtifactRoot {
|
|
|
71
76
|
* passed in, which is what makes every branch below testable without a fixture tree.
|
|
72
77
|
*/
|
|
73
78
|
export declare function resolveArtifactRoot(query: ArtifactRootQuery): ArtifactRoot;
|
|
79
|
+
/**
|
|
80
|
+
* The same project folder, in the checkout that served the page.
|
|
81
|
+
*
|
|
82
|
+
* Every git worktree of a project is the same project, so the config the daemon found names the
|
|
83
|
+
* main checkout's folder. A page served from worktree `x` belongs in `x`'s copy of that folder, so
|
|
84
|
+
* its flows and runs travel with `x`'s branch. Unchanged unless both checkouts are known, share one
|
|
85
|
+
* main checkout, and differ.
|
|
86
|
+
*/
|
|
87
|
+
export declare function rebaseIntoCheckout(directory: string, from: Pick<Checkout, 'root' | 'commonDir'> | undefined, to: Pick<Checkout, 'root' | 'commonDir'> | undefined): string;
|
|
74
88
|
/**
|
|
75
89
|
* Everything this machine knows about where projects live, from both sources.
|
|
76
90
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
2
2
|
import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
|
|
3
|
-
import { SAFE_SEGMENT_PATTERN, ReticleDir, fnv1a, projectCandidates, } from '@reticlehq/core';
|
|
3
|
+
import { SAFE_SEGMENT_PATTERN, ReticleDir, fnv1a, projectCandidates, samePath, } from '@reticlehq/core';
|
|
4
4
|
/**
|
|
5
5
|
* Which `.reticle/` a session's artifacts belong in.
|
|
6
6
|
*
|
|
@@ -47,7 +47,7 @@ export function resolveArtifactRoot(query) {
|
|
|
47
47
|
if (projectId === undefined || 0 === projectId.length) {
|
|
48
48
|
return { root: daemonRoot, reason: ArtifactRootReason.NO_PROJECT_ID };
|
|
49
49
|
}
|
|
50
|
-
const matches = dedupeByDirectory(candidates.filter((c) => c.projectId === projectId));
|
|
50
|
+
const matches = dedupeByDirectory(candidates.filter((c) => c.projectId === projectId), query.platform ?? process.platform);
|
|
51
51
|
if (0 === matches.length) {
|
|
52
52
|
return { root: daemonRoot, reason: ArtifactRootReason.NO_MATCH };
|
|
53
53
|
}
|
|
@@ -69,8 +69,40 @@ export function resolveArtifactRoot(query) {
|
|
|
69
69
|
if (0 === directory.length) {
|
|
70
70
|
return { root: daemonRoot, reason: ArtifactRootReason.NO_MATCH };
|
|
71
71
|
}
|
|
72
|
+
// The page was served from a tree apart from the one checkout we know: a second checkout of the
|
|
73
|
+
// same project that the registry no longer names. Writing into the known one moves this app's
|
|
74
|
+
// evidence into a tree nobody drove, so it is refused like any other pair.
|
|
75
|
+
const serving = query.servingDirectory;
|
|
76
|
+
if (serving !== undefined &&
|
|
77
|
+
0 < serving.length &&
|
|
78
|
+
!isSameOrInside(directory, serving) &&
|
|
79
|
+
!isSameOrInside(serving, directory)) {
|
|
80
|
+
return {
|
|
81
|
+
root: daemonRoot,
|
|
82
|
+
reason: ArtifactRootReason.AMBIGUOUS,
|
|
83
|
+
candidates: [directory, serving],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
72
86
|
return { root: join(directory, ReticleDir.ROOT), reason: ArtifactRootReason.MATCHED_PROJECT };
|
|
73
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The same project folder, in the checkout that served the page.
|
|
90
|
+
*
|
|
91
|
+
* Every git worktree of a project is the same project, so the config the daemon found names the
|
|
92
|
+
* main checkout's folder. A page served from worktree `x` belongs in `x`'s copy of that folder, so
|
|
93
|
+
* its flows and runs travel with `x`'s branch. Unchanged unless both checkouts are known, share one
|
|
94
|
+
* main checkout, and differ.
|
|
95
|
+
*/
|
|
96
|
+
export function rebaseIntoCheckout(directory, from, to) {
|
|
97
|
+
if (from === undefined || to === undefined)
|
|
98
|
+
return directory;
|
|
99
|
+
if (!samePath(from.commonDir, to.commonDir) || samePath(from.root, to.root))
|
|
100
|
+
return directory;
|
|
101
|
+
const rel = relative(from.root, directory);
|
|
102
|
+
if ('..' === rel || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
103
|
+
return directory;
|
|
104
|
+
return join(to.root, rel);
|
|
105
|
+
}
|
|
74
106
|
/**
|
|
75
107
|
* The checkout the serving directory sits in, or undefined when it is in none of them.
|
|
76
108
|
*
|
|
@@ -83,12 +115,14 @@ function checkoutContaining(matches, servingDirectory) {
|
|
|
83
115
|
return undefined;
|
|
84
116
|
const inside = matches
|
|
85
117
|
.map((candidate) => candidate.directory)
|
|
86
|
-
.filter((directory) =>
|
|
87
|
-
const rel = relative(directory, servingDirectory);
|
|
88
|
-
return '' === rel || ('..' !== rel && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
89
|
-
});
|
|
118
|
+
.filter((directory) => isSameOrInside(directory, servingDirectory));
|
|
90
119
|
return inside.sort((a, b) => b.length - a.length)[0];
|
|
91
120
|
}
|
|
121
|
+
/** Is `path` the directory `dir` or somewhere inside it? */
|
|
122
|
+
function isSameOrInside(dir, path) {
|
|
123
|
+
const rel = relative(dir, path);
|
|
124
|
+
return '' === rel || ('..' !== rel && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
125
|
+
}
|
|
92
126
|
/**
|
|
93
127
|
* One entry per directory.
|
|
94
128
|
*
|
|
@@ -97,13 +131,17 @@ function checkoutContaining(matches, servingDirectory) {
|
|
|
97
131
|
* it twice would read as two competing checkouts and make the resolver refuse, which is the one
|
|
98
132
|
* outcome worse than either source alone.
|
|
99
133
|
*/
|
|
100
|
-
function dedupeByDirectory(matches) {
|
|
134
|
+
function dedupeByDirectory(matches, platform) {
|
|
135
|
+
// Windows paths are not case-sensitive: one checkout seen from two shells can differ by case.
|
|
136
|
+
const keyOf = (directory) => 'win32' === platform ? directory.toLowerCase() : directory;
|
|
101
137
|
const seen = new Set();
|
|
102
138
|
const out = [];
|
|
103
139
|
for (const candidate of matches) {
|
|
104
|
-
|
|
140
|
+
const key = keyOf(candidate.directory);
|
|
141
|
+
if (seen.has(key)) {
|
|
105
142
|
continue;
|
|
106
|
-
|
|
143
|
+
}
|
|
144
|
+
seen.add(key);
|
|
107
145
|
out.push(candidate);
|
|
108
146
|
}
|
|
109
147
|
return out;
|
|
@@ -66,3 +66,8 @@ export interface FileSystemPort {
|
|
|
66
66
|
}
|
|
67
67
|
/** Production adapter — the single import site of node:fs/promises in the server. */
|
|
68
68
|
export declare function createNodeFileSystem(): FileSystemPort;
|
|
69
|
+
/**
|
|
70
|
+
* A file's text, or undefined when it is missing, a directory or unreadable. Synchronous, for the
|
|
71
|
+
* few questions asked before any port exists, such as which git checkout a folder is in.
|
|
72
|
+
*/
|
|
73
|
+
export declare function readTextOrUndefined(path: string): string | undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { access, appendFile, mkdir, open, readdir, readFile, realpath, rename, rm, stat, writeFile, } from 'node:fs/promises';
|
|
2
3
|
/** The JSONL record terminator, as a byte — the only boundary a capped tail read can align to. */
|
|
3
4
|
const NEWLINE_BYTE = 0x0a;
|
|
@@ -119,3 +120,15 @@ export function createNodeFileSystem() {
|
|
|
119
120
|
isNotFound: (error) => 'ENOENT' === error?.code,
|
|
120
121
|
};
|
|
121
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* A file's text, or undefined when it is missing, a directory or unreadable. Synchronous, for the
|
|
125
|
+
* few questions asked before any port exists, such as which git checkout a folder is in.
|
|
126
|
+
*/
|
|
127
|
+
export function readTextOrUndefined(path) {
|
|
128
|
+
try {
|
|
129
|
+
return readFileSync(path, 'utf8');
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReticleEvent } from '@reticlehq/core';
|
|
1
|
+
import { pathOf as routeFromUrl, type ReticleEvent } from '@reticlehq/core';
|
|
2
2
|
interface RouteLearningSession {
|
|
3
3
|
url: string;
|
|
4
4
|
/**
|
|
@@ -21,8 +21,8 @@ interface RouteLearningStore {
|
|
|
21
21
|
export declare const ROUTE_PERSIST_DEBOUNCE_MS = 50;
|
|
22
22
|
/** Convert a validated route-change payload into a bounded route identity. */
|
|
23
23
|
export declare function routeFromEvent(event: ReticleEvent): string | undefined;
|
|
24
|
-
/** Read the same route shape from a session's absolute URL. */
|
|
25
|
-
export
|
|
24
|
+
/** Read the same route shape from a session's absolute URL: core's `pathOf`, re-exported. */
|
|
25
|
+
export { routeFromUrl };
|
|
26
26
|
export declare function routesFromEvents(events: readonly ReticleEvent[]): string[];
|
|
27
27
|
/**
|
|
28
28
|
* Persist initial and subsequent routes for existing and future browser sessions.
|
|
@@ -35,4 +35,3 @@ export declare function routesFromEvents(events: readonly ReticleEvent[]): strin
|
|
|
35
35
|
* while the driven project's own memory never filled.
|
|
36
36
|
*/
|
|
37
37
|
export declare function attachRouteLearning(bridge: RouteLearningBridge, storeFor: (root: string | undefined) => RouteLearningStore): void;
|
|
38
|
-
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventType } from '@reticlehq/core';
|
|
1
|
+
import { EventType, pathOf as routeFromUrl } from '@reticlehq/core';
|
|
2
2
|
/** Coalesce navigation bursts so one crawl does not lock and rewrite project.json per event. */
|
|
3
3
|
export const ROUTE_PERSIST_DEBOUNCE_MS = 50;
|
|
4
4
|
/** Convert a validated route-change payload into a bounded route identity. */
|
|
@@ -10,16 +10,8 @@ export function routeFromEvent(event) {
|
|
|
10
10
|
return undefined;
|
|
11
11
|
return pathname;
|
|
12
12
|
}
|
|
13
|
-
/** Read the same route shape from a session's absolute URL. */
|
|
14
|
-
export
|
|
15
|
-
try {
|
|
16
|
-
const url = new URL(value);
|
|
17
|
-
return url.pathname;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
13
|
+
/** Read the same route shape from a session's absolute URL: core's `pathOf`, re-exported. */
|
|
14
|
+
export { routeFromUrl };
|
|
23
15
|
export function routesFromEvents(events) {
|
|
24
16
|
return events.flatMap((event) => {
|
|
25
17
|
const route = routeFromEvent(event);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* while every run of that app landed in it.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync } from 'node:fs';
|
|
9
|
-
import { join, resolve, sep } from 'node:path';
|
|
9
|
+
import { basename, dirname, join, resolve, sep } from 'node:path';
|
|
10
10
|
import { ReticleDir } from '@reticlehq/core';
|
|
11
11
|
import { discoverProjectConfigs, hasProjectConfig, } from '../../command/cli/config/config-discovery.js';
|
|
12
12
|
/** `inner` is `outer` or sits inside it. */
|
|
@@ -17,7 +17,10 @@ const within = (inner, outer) => inner === outer || inner.startsWith(outer.endsW
|
|
|
17
17
|
* kept rather than one picked silently.
|
|
18
18
|
*/
|
|
19
19
|
export function linkDirectoryFor(cwd, discovery) {
|
|
20
|
-
|
|
20
|
+
// A `.reticle` is a project's, never a project: the HUD's Link passes the tab's `.reticle`, and on
|
|
21
|
+
// an app with no `.reticle.json` the link landed in `.reticle/.reticle/cloud.json`.
|
|
22
|
+
const asked = resolve(cwd);
|
|
23
|
+
const here = ReticleDir.ROOT === basename(asked) ? dirname(asked) : asked;
|
|
21
24
|
const enclosing = discovery.found
|
|
22
25
|
.map((found) => found.directory)
|
|
23
26
|
.filter((directory) => within(here, directory))
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
/** Claim `root` for the session a lease registers.
|
|
1
|
+
/** Claim `root` for the session a lease registers. Takes a `.reticle` folder or the project folder holding one. */
|
|
2
2
|
export declare function claimArtifactRoot(leaseId: string, root: string): void;
|
|
3
|
+
/** Every root a lease claimed in this process, released or not. Sync reads it to find the work. */
|
|
4
|
+
export declare function claimedArtifactRoots(): string[];
|
|
3
5
|
/** The root claimed for this session, by its id or by the lease marker on its url. */
|
|
4
6
|
export declare function claimedArtifactRoot(sessionId: string, url: string | undefined): string | undefined;
|
|
5
7
|
/** Forget a lease's claim, once the lease is released. */
|
|
@@ -9,22 +9,56 @@
|
|
|
9
9
|
* reconnect after a navigation, or an app that names its own session, still finds the claim. Lives
|
|
10
10
|
* until the lease is released.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { statSync } from 'node:fs';
|
|
13
|
+
import { basename, isAbsolute, join } from 'node:path';
|
|
13
14
|
import { RETICLE_URL_PARAM, ReticleDir } from '@reticlehq/core';
|
|
14
15
|
const claims = new Map();
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** Every root claimed in this process. Kept after release, so sync still sends what the lease wrote. */
|
|
17
|
+
const everClaimed = new Set();
|
|
18
|
+
/**
|
|
19
|
+
* The `.reticle` folder a caller meant by `root`. Agents pass the project folder, the one they work
|
|
20
|
+
* in, so a folder that holds a `.reticle` stands for it. Anything else is refused with the path to pass.
|
|
21
|
+
*/
|
|
22
|
+
function resolveArtifactRoot(root) {
|
|
23
|
+
if (!isAbsolute(root)) {
|
|
18
24
|
throw new Error(`root must be an absolute path to a ${ReticleDir.ROOT} folder, got ${root}`);
|
|
19
|
-
|
|
25
|
+
}
|
|
26
|
+
if (ReticleDir.ROOT === basename(root)) {
|
|
27
|
+
return root;
|
|
28
|
+
}
|
|
29
|
+
const inside = join(root, ReticleDir.ROOT);
|
|
30
|
+
if (isDirectory(inside)) {
|
|
31
|
+
return inside;
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`root must be the ${ReticleDir.ROOT} folder, or a project folder that contains one, got ${root}. Pass ${inside}`);
|
|
34
|
+
}
|
|
35
|
+
function isDirectory(path) {
|
|
36
|
+
try {
|
|
37
|
+
return statSync(path).isDirectory();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Claim `root` for the session a lease registers. Takes a `.reticle` folder or the project folder holding one. */
|
|
44
|
+
export function claimArtifactRoot(leaseId, root) {
|
|
45
|
+
const resolved = resolveArtifactRoot(root);
|
|
46
|
+
claims.set(leaseId, resolved);
|
|
47
|
+
everClaimed.add(resolved);
|
|
48
|
+
}
|
|
49
|
+
/** Every root a lease claimed in this process, released or not. Sync reads it to find the work. */
|
|
50
|
+
export function claimedArtifactRoots() {
|
|
51
|
+
return [...everClaimed];
|
|
20
52
|
}
|
|
21
53
|
/** The root claimed for this session, by its id or by the lease marker on its url. */
|
|
22
54
|
export function claimedArtifactRoot(sessionId, url) {
|
|
23
55
|
const direct = claims.get(sessionId);
|
|
24
|
-
if (direct !== undefined)
|
|
56
|
+
if (direct !== undefined) {
|
|
25
57
|
return direct;
|
|
26
|
-
|
|
58
|
+
}
|
|
59
|
+
if (url === undefined) {
|
|
27
60
|
return undefined;
|
|
61
|
+
}
|
|
28
62
|
try {
|
|
29
63
|
const lease = new URL(url).searchParams.get(RETICLE_URL_PARAM.SESSION);
|
|
30
64
|
return null === lease ? undefined : claims.get(lease);
|
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { originOf } from '@reticlehq/core';
|
|
2
3
|
import { claimedArtifactRoot } from './root-claims.js';
|
|
3
|
-
/**
|
|
4
|
-
* The scheme://host:port a session's page was served from. Local rather than the session manager's
|
|
5
|
-
* copy because `memory/project` does not reach `portal/session`, and five lines are cheaper than a
|
|
6
|
-
* new dependency between the two.
|
|
7
|
-
*/
|
|
8
|
-
function originOf(url) {
|
|
9
|
-
if (url === undefined)
|
|
10
|
-
return undefined;
|
|
11
|
-
try {
|
|
12
|
-
return new URL(url).origin;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
4
|
/** The target for a caller that genuinely holds no session -- the id is all there is. */
|
|
19
5
|
export function projectOnly(projectId) {
|
|
20
6
|
return { projectId };
|
|
@@ -14,7 +14,7 @@ import { diskSource, readCloudState } from '../cloud/sync-disk.js';
|
|
|
14
14
|
import { hashPayload } from '../cloud/sync-hash.js';
|
|
15
15
|
import { readFileSync } from 'node:fs';
|
|
16
16
|
import { join } from 'node:path';
|
|
17
|
-
import { ReticleDir } from '@reticlehq/core';
|
|
17
|
+
import { CONNECT_COMMAND, ReticleDir } from '@reticlehq/core';
|
|
18
18
|
import { resolveProjectCloud } from '../cloud/cloud-config.js';
|
|
19
19
|
import { unmatchedRoots, unsyncedRoots } from '../cloud/unsynced-roots.js';
|
|
20
20
|
import { knownProjectCandidates } from './artifact-root-resolver.js';
|
|
@@ -189,7 +189,7 @@ export function overallStatus(summary) {
|
|
|
189
189
|
/** The summary in one sentence an agent can repeat to a person. */
|
|
190
190
|
export function describeSync(summary, now) {
|
|
191
191
|
if (!summary.linked)
|
|
192
|
-
return `${String(summary.runs)} run(s) exist only on this machine; nothing is synced until the project is linked (
|
|
192
|
+
return `${String(summary.runs)} run(s) exist only on this machine; nothing is synced until the project is linked (${CONNECT_COMMAND}).`;
|
|
193
193
|
const ago = summary.lastPushAt === undefined
|
|
194
194
|
? 'nothing pushed yet'
|
|
195
195
|
: `last push ${String(Math.max(0, Math.round((now - summary.lastPushAt) / 1000)))}s ago`;
|
|
@@ -217,8 +217,7 @@ export async function machineUnsyncedRoots(fs, home, env) {
|
|
|
217
217
|
...unmatchedRoots(home),
|
|
218
218
|
];
|
|
219
219
|
const cloudOf = (root) => resolveProjectCloud(fs, root, home, env);
|
|
220
|
-
return unsyncedRoots(roots,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}, async (root) => (await cloudOf(root)).policy.flows);
|
|
220
|
+
return unsyncedRoots(roots,
|
|
221
|
+
// A credential is what sends: a folder with no link file sends with the environment's key.
|
|
222
|
+
async (root) => null !== (await cloudOf(root)).config, async (root) => (await cloudOf(root)).policy.flows);
|
|
224
223
|
}
|
|
@@ -17,7 +17,7 @@ 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';
|
|
20
|
+
import { MEMORY_NOT_LINKED_ADVICE, MemoryAction, addMemoryStatement, fetchPlatformFlow, pushFlow, setAsideStatement, } from './platform-memory.js';
|
|
21
21
|
import { flowsForSession } from '../../language/flows/flow-store-for-session.js';
|
|
22
22
|
import { safeSessionTarget } from '../project/session-root.js';
|
|
23
23
|
import { homedir } from 'node:os';
|
|
@@ -32,7 +32,7 @@ const NEEDS_STATEMENT = 'needs `subject` and `statement`';
|
|
|
32
32
|
const DEFAULT_LIMIT = 10;
|
|
33
33
|
/** What to do next, per reason. A refusal that does not say how to fix it is a dead end. */
|
|
34
34
|
const ADVICE = {
|
|
35
|
-
[MemoryUnavailable.NOT_LINKED]:
|
|
35
|
+
[MemoryUnavailable.NOT_LINKED]: MEMORY_NOT_LINKED_ADVICE,
|
|
36
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.',
|
|
37
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`.',
|
|
38
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.',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FileSystemPort } from '../project/fs/fs-port.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type FlowFile } from '@reticlehq/core';
|
|
3
3
|
/** What `reticle_memory` can be asked to do. `read` is the default and needs no `action`. */
|
|
4
4
|
export declare const MemoryAction: {
|
|
5
5
|
readonly READ: "read";
|
|
@@ -16,6 +16,8 @@ export declare const SetAsideRefused: {
|
|
|
16
16
|
readonly REFUSED: "refused";
|
|
17
17
|
};
|
|
18
18
|
export type PlatformAnswer = Record<string, unknown>;
|
|
19
|
+
/** What an unlinked project is told, on every memory action: the one command, and what it adds. */
|
|
20
|
+
export declare const MEMORY_NOT_LINKED_ADVICE: string;
|
|
19
21
|
/** One saved flow from the platform: a teammate's, or this project's as the platform holds it. */
|
|
20
22
|
export declare function fetchPlatformFlow(fs: FileSystemPort, root: string, name: string): Promise<PlatformAnswer>;
|
|
21
23
|
/** Add one statement to the project's memory. It lands as a proposal; only a passing check proves it. */
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
9
|
import { cloudFetch, syncFlowToCloud, SyncOutcome } from '../cloud/cloud-sync.js';
|
|
10
10
|
import { resolveProjectCloud } from '../cloud/cloud-config.js';
|
|
11
|
+
import { CONNECT_COMMAND, DEFAULT_PLATFORM_URL } from '@reticlehq/core';
|
|
11
12
|
import { MemoryUnavailable } from './project-memory.js';
|
|
12
13
|
/** What `reticle_memory` can be asked to do. `read` is the default and needs no `action`. */
|
|
13
14
|
export const MemoryAction = {
|
|
@@ -32,6 +33,10 @@ const HTTP_NOT_FOUND = 404;
|
|
|
32
33
|
const HTTP_FORBIDDEN = 403;
|
|
33
34
|
const HTTP_BAD_REQUEST = 400;
|
|
34
35
|
const unavailable = (reason, error) => ({ error, reason });
|
|
36
|
+
/** What an unlinked project is told, on every memory action: the one command, and what it adds. */
|
|
37
|
+
export const MEMORY_NOT_LINKED_ADVICE = `Run \`${CONNECT_COMMAND}\` in the project folder to link it; add \`--url <origin>\` only for a ` +
|
|
38
|
+
`Reticle platform other than ${DEFAULT_PLATFORM_URL}. Once linked, memory keeps what each run ` +
|
|
39
|
+
'proved, and every agent and teammate on the project can read it.';
|
|
35
40
|
const UNREACHABLE = unavailable(MemoryUnavailable.UNREACHABLE, 'the platform could not be reached, or refused this project’s key');
|
|
36
41
|
/** The link, or the reason there is none. Writes honour `sync.memory` / `sync.flows` like the sync. */
|
|
37
42
|
async function linked(fs, root, allowed) {
|
|
@@ -40,7 +45,10 @@ async function linked(fs, root, allowed) {
|
|
|
40
45
|
if (null === config)
|
|
41
46
|
return {
|
|
42
47
|
ok: false,
|
|
43
|
-
answer:
|
|
48
|
+
answer: {
|
|
49
|
+
...unavailable(MemoryUnavailable.NOT_LINKED, 'this project is not linked to the platform'),
|
|
50
|
+
advice: MEMORY_NOT_LINKED_ADVICE,
|
|
51
|
+
},
|
|
44
52
|
};
|
|
45
53
|
if (!allowed(cloud))
|
|
46
54
|
return {
|