@reticlehq/server 3.7.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -10,19 +10,22 @@
|
|
|
10
10
|
import { log } from '../../log.js';
|
|
11
11
|
import { isAlive, spawnDaemon, reticleStateHome } from '../daemon/daemon.js';
|
|
12
12
|
import { PortPresence, probePresence, presenceIsUsable, describePresence, } from '../daemon/binding/port-presence.js';
|
|
13
|
-
import { waitForDaemon, startMcpProxy, probeDaemon, proxyLog, setProxyLogPort, } from '../../surface/mcp/mcp-proxy.js';
|
|
13
|
+
import { waitForDaemon, startMcpProxy, probeDaemon, proxyLog, setProxyLogPort, flushProxyExit, reportWakeRefused, } from '../../surface/mcp/mcp-proxy.js';
|
|
14
14
|
import { installProxyResilience } from '../daemon/daemon-resilience.js';
|
|
15
15
|
import { readProjectId, readProjectPort } from './ports/resolve/cli-port.js';
|
|
16
16
|
import { resolveMcpPort, daemonProjectAt, relocationReason, } from '../daemon/daemon-resolve.js';
|
|
17
17
|
import { daemonSpawnArgs } from './daemon-start-options.js';
|
|
18
18
|
import { WakeAction, decideWake } from '../daemon/wake-decision.js';
|
|
19
|
+
import { runKill } from './cli-kill.js';
|
|
19
20
|
import { pickDaemonPortToBind } from '../daemon/binding/free-port.js';
|
|
20
|
-
import { fetchStatus } from '../daemon/binding/daemon-status-probe.js';
|
|
21
|
+
import { fetchStatus, summarizeStatus } from '../daemon/binding/daemon-status-probe.js';
|
|
22
|
+
import { isNewerVersion } from '../update/update-nudge.js';
|
|
21
23
|
import { migrateApprovals } from '../setup/approval-migration.js';
|
|
22
|
-
import { ReticleEnv } from '@reticlehq/core';
|
|
24
|
+
import { ProxyExitReason, ReticleEnv, WakeRefusalPresence } from '@reticlehq/core';
|
|
23
25
|
import { agentIo } from '../setup/agent-io.js';
|
|
24
26
|
import { SERVER_VERSION } from '../version/identity/server-version.js';
|
|
25
27
|
import { homedir } from 'node:os';
|
|
28
|
+
import { platformKey } from '../setup/agent-configs.js';
|
|
26
29
|
/**
|
|
27
30
|
* Whether the daemon this proxy starts hides its browser. A daemon on a port nobody chose — the
|
|
28
31
|
* requested one served another project, so this one was moved — was opening a visible browser the
|
|
@@ -31,6 +34,41 @@ import { homedir } from 'node:os';
|
|
|
31
34
|
export function spawnsHeadless(headless, requested, resolved) {
|
|
32
35
|
return headless || requested !== resolved;
|
|
33
36
|
}
|
|
37
|
+
const RELEASE_VERSION = /^\d+\.\d+\.\d+/;
|
|
38
|
+
/** The first release whose `/status` names the agents attached to it (`mcpPeers`). */
|
|
39
|
+
const MCP_PEERS_SINCE = '3.6.0';
|
|
40
|
+
/**
|
|
41
|
+
* Should a proxy stop the older daemon it found, and start its own release?
|
|
42
|
+
*
|
|
43
|
+
* Only an OLDER daemon, read from `/status`, with no tab attached and no other agent attached. A
|
|
44
|
+
* tab or an agent can have a call in flight, so that daemon stays and the skew is described as
|
|
45
|
+
* before. A daemon before `MCP_PEERS_SINCE` does not report its agents, so it stays too. A status
|
|
46
|
+
* that does not parse, or a version that does not compare, keeps the daemon.
|
|
47
|
+
*/
|
|
48
|
+
export function retiresOlderDaemon(status, proxyVersion) {
|
|
49
|
+
if (typeof status !== 'object' || null === status) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const fields = status;
|
|
53
|
+
const version = fields['version'];
|
|
54
|
+
if ('string' !== typeof version) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Only a plain release number compares; anything else keeps the daemon.
|
|
58
|
+
if (!RELEASE_VERSION.test(version) || !isNewerVersion(proxyVersion, version)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// An older daemon never sends `mcpPeers`, so it cannot say that no agent is attached.
|
|
62
|
+
if (isNewerVersion(MCP_PEERS_SINCE, version)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const peers = fields['mcpPeers'];
|
|
66
|
+
// A daemon sends `mcpPeers` only when an agent is attached.
|
|
67
|
+
if (Array.isArray(peers) && 0 < peers.length) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return 0 === summarizeStatus(status).sessionCount;
|
|
71
|
+
}
|
|
34
72
|
/**
|
|
35
73
|
* MCP proxy mode: ensures the daemon is running, then bridges Claude Code's
|
|
36
74
|
* stdin/stdout to the daemon's SSE endpoint. This is the recommended way to
|
|
@@ -46,10 +84,9 @@ export async function handleMcp(opts) {
|
|
|
46
84
|
// takes the requested port when it is free or unclaimed, and relocates to an OS-assigned port
|
|
47
85
|
// rather than adopting a daemon that belongs to somebody else.
|
|
48
86
|
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
// "the MCP server disappeared mid-session".
|
|
87
|
+
// The reconnect path keeps one port. Only a WAKE, which already starts from a dormant proxy,
|
|
88
|
+
// resolves again and moves the proxy: see `ensure`. The reconnect path is the one piece whose
|
|
89
|
+
// failure reads to a user as "the MCP server disappeared mid-session", so it does not chase.
|
|
53
90
|
// Read once and reused by the wake path below: the boot resolution and every later wake must be
|
|
54
91
|
// defending the SAME identity, and computing it twice is how they drift apart.
|
|
55
92
|
// Before the port work, because it is the only moment we are guaranteed to get on a machine that
|
|
@@ -61,24 +98,29 @@ export async function handleMcp(opts) {
|
|
|
61
98
|
migrateApprovals({
|
|
62
99
|
io: agentIo,
|
|
63
100
|
home: homedir(),
|
|
64
|
-
platform: process.platform,
|
|
101
|
+
platform: platformKey(process.platform),
|
|
65
102
|
stateHome: reticleStateHome(),
|
|
66
103
|
version: SERVER_VERSION,
|
|
67
104
|
log,
|
|
68
105
|
});
|
|
69
106
|
}
|
|
70
107
|
const projectId = readProjectId(process.cwd());
|
|
71
|
-
const
|
|
108
|
+
const resolvePort = () => resolveMcpPort(opts.port, projectId, reticleStateHome(), {
|
|
72
109
|
alive: isAlive,
|
|
73
110
|
daemonPresent: async (p) => presenceIsUsable(await probePresence(p, { tcpOpen: probeDaemon, status: fetchStatus })),
|
|
74
111
|
pickPort: (p) => pickDaemonPortToBind(p),
|
|
75
112
|
}, readProjectPort(process.cwd()));
|
|
76
|
-
|
|
113
|
+
const noteRelocated = (to) => {
|
|
77
114
|
log('reticle_mcp_port_relocated', {
|
|
78
115
|
requested: opts.port,
|
|
79
|
-
port,
|
|
80
|
-
reason: relocationReason(opts.port,
|
|
116
|
+
port: to,
|
|
117
|
+
reason: relocationReason(opts.port, to, projectId, reticleStateHome(), isAlive),
|
|
81
118
|
});
|
|
119
|
+
};
|
|
120
|
+
// `let`: a wake that finds the port taken resolves again, and the proxy follows (#1651, #1652).
|
|
121
|
+
let port = await resolvePort();
|
|
122
|
+
if (port !== opts.port) {
|
|
123
|
+
noteRelocated(port);
|
|
82
124
|
}
|
|
83
125
|
// The proxy IS the MCP server the editor launched. Nothing respawns it, so an uncaught throw here
|
|
84
126
|
// is what a user experiences as "the MCP server disconnected — open /mcp and reconnect". Log it,
|
|
@@ -88,7 +130,11 @@ export async function handleMcp(opts) {
|
|
|
88
130
|
// swallows stderr, so wiring these to `log` meant a crash was handled and then unrecorded — the
|
|
89
131
|
// failure a user reports as "it disconnected" left nothing behind to read.
|
|
90
132
|
setProxyLogPort(port);
|
|
91
|
-
|
|
133
|
+
// The storm exit is a proxy exit too: report it before the process goes.
|
|
134
|
+
// Only the client pipes count toward the storm exit; see installProxyResilience.
|
|
135
|
+
const resilience = installProxyResilience(process, proxyLog, undefined, () => {
|
|
136
|
+
void flushProxyExit(ProxyExitReason.CLIENT_GONE_STORM).finally(() => process.exit(0));
|
|
137
|
+
}, [process.stdin, process.stdout]);
|
|
92
138
|
/**
|
|
93
139
|
* Make sure a daemon is on the port, spawning one if not.
|
|
94
140
|
*
|
|
@@ -97,7 +143,10 @@ export async function handleMcp(opts) {
|
|
|
97
143
|
* a dead port until the budget ran out and the agent's MCP server exited with it. Reticle simply
|
|
98
144
|
* disappeared mid-session with nothing said. Respawning here makes the reconnect self-healing.
|
|
99
145
|
*/
|
|
100
|
-
|
|
146
|
+
// Resolved when a new daemon process starts, so the proxy can answer the handshake at once.
|
|
147
|
+
let markSpawning = () => undefined;
|
|
148
|
+
const spawning = new Promise((resolve) => (markSpawning = resolve));
|
|
149
|
+
const ensure = async (mayRelocate = true) => {
|
|
101
150
|
// The same question every other surface asks. It used to be a bare TCP connect, so a stranger on
|
|
102
151
|
// the bridge port answered "a daemon is here" — the proxy connected, the stream ended, and each
|
|
103
152
|
// client request woke into the identical non-answer. Rejecting here is what puts the proxy
|
|
@@ -112,9 +161,34 @@ export async function handleMcp(opts) {
|
|
|
112
161
|
// APPLICATION. That is worse than the disconnect it was reintroduced under the cover of, because
|
|
113
162
|
// a disconnect is visible and this is a false green wearing a valid session.
|
|
114
163
|
const wake = decideWake(presence, daemonProjectAt(port, reticleStateHome()), projectId);
|
|
115
|
-
if (WakeAction.USE === wake)
|
|
116
|
-
|
|
164
|
+
if (WakeAction.USE === wake) {
|
|
165
|
+
// A proxy from a newer release stops an older daemon that nobody is using, and starts its own.
|
|
166
|
+
// Otherwise every tool refused on the skew until a person ran `reticle restart`.
|
|
167
|
+
const status = await fetchStatus(port);
|
|
168
|
+
if (!retiresOlderDaemon(status, SERVER_VERSION)) {
|
|
169
|
+
return port;
|
|
170
|
+
}
|
|
171
|
+
log('reticle_mcp_retiring_older_daemon', { port, proxyVersion: SERVER_VERSION });
|
|
172
|
+
// Not stopped: use it. The daemon describes the skew to this agent, as before.
|
|
173
|
+
if (!(await runKill(port, false))) {
|
|
174
|
+
return port;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (WakeAction.REFUSE === wake && mayRelocate) {
|
|
178
|
+
// Somebody else took the port after our daemon left it. Resolve again, as at boot: our own
|
|
179
|
+
// daemon wherever it moved, else a free port. Once per wake, so it cannot loop.
|
|
180
|
+
const moved = await resolvePort();
|
|
181
|
+
if (moved !== port) {
|
|
182
|
+
noteRelocated(moved);
|
|
183
|
+
port = moved;
|
|
184
|
+
setProxyLogPort(port);
|
|
185
|
+
return ensure(false);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
117
188
|
if (WakeAction.REFUSE === wake) {
|
|
189
|
+
void reportWakeRefused(PortPresence.DAEMON === presence
|
|
190
|
+
? WakeRefusalPresence.OTHER_PROJECT
|
|
191
|
+
: WakeRefusalPresence.FOREIGN);
|
|
118
192
|
throw new Error(PortPresence.DAEMON === presence
|
|
119
193
|
? `the daemon on port ${String(port)} belongs to a different project, so this one will ` +
|
|
120
194
|
'not adopt it. Stop it, or start this project on its own port.'
|
|
@@ -123,6 +197,7 @@ export async function handleMcp(opts) {
|
|
|
123
197
|
const scriptPath = process.argv[1];
|
|
124
198
|
if (scriptPath === undefined) {
|
|
125
199
|
log('reticle_mcp_no_script', {});
|
|
200
|
+
await flushProxyExit(ProxyExitReason.WAKE_FAILED);
|
|
126
201
|
process.exit(1);
|
|
127
202
|
}
|
|
128
203
|
const daemonArgs = daemonSpawnArgs({
|
|
@@ -133,6 +208,7 @@ export async function handleMcp(opts) {
|
|
|
133
208
|
...(httpPort !== undefined ? { httpPort } : {}),
|
|
134
209
|
...(httpToken !== undefined ? { httpToken } : {}),
|
|
135
210
|
});
|
|
211
|
+
markSpawning();
|
|
136
212
|
spawnDaemon(process.execPath, scriptPath, daemonArgs, port);
|
|
137
213
|
// Announce the daemon only once the PORT ACCEPTS. This line used to be written the instant the
|
|
138
214
|
// child was spawned, which on a Windows first bootstrap meant `reticle_mcp_daemon_started`
|
|
@@ -141,6 +217,7 @@ export async function handleMcp(opts) {
|
|
|
141
217
|
// none: a client that believes it stops waiting for the thing that has not happened.
|
|
142
218
|
await waitForDaemon(port);
|
|
143
219
|
log('reticle_mcp_daemon_started', { port, ...(driveUrl !== undefined ? { driveUrl } : {}) });
|
|
220
|
+
return port;
|
|
144
221
|
};
|
|
145
222
|
// Start the proxy WHATEVER happened to the daemon.
|
|
146
223
|
//
|
|
@@ -150,35 +227,44 @@ export async function handleMcp(opts) {
|
|
|
150
227
|
// editor shows a server that failed to start. Nothing about that is unrecoverable: the proxy
|
|
151
228
|
// answers `initialize` itself, serves the cached catalog, and its wake path retries a daemon on
|
|
152
229
|
// every client request. Present-and-complaining beats absent, because absent needs a human.
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
230
|
+
//
|
|
231
|
+
// The proxy starts NOW, beside the daemon start, and dials when the start is done. A client that
|
|
232
|
+
// does not wait for MCP servers gets the handshake and the tool list before the daemon is up.
|
|
233
|
+
const ready = ensure().catch((err) => {
|
|
157
234
|
log('reticle_mcp_daemon_unavailable', {
|
|
158
235
|
error: err instanceof Error ? err.message : String(err),
|
|
159
236
|
note: 'serving anyway — the next tool call will try to start a daemon again',
|
|
160
237
|
});
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
238
|
+
});
|
|
239
|
+
// Also `void`: the proxy runs for the life of the process and is never awaited by anyone.
|
|
240
|
+
//
|
|
241
|
+
// The `.catch` is load-bearing despite that. `startMcpProxy` rejects when its FIRST connect
|
|
242
|
+
// fails — which is precisely the case this block exists to tolerate, a daemon that is not
|
|
243
|
+
// there yet — and with nothing attached that reject became an unhandledRejection. Reported
|
|
244
|
+
// from a win32 user as a crash reading `connect ECONNREFUSED` with an EMPTY frame list,
|
|
245
|
+
// because the stack of a refused socket is entirely node internals and the privacy filter
|
|
246
|
+
// keeps only Reticle frames. So the one path we most want diagnosable arrived as an anonymous
|
|
247
|
+
// crash. The proxy itself is unaffected — it has already installed its stdin reader and goes
|
|
248
|
+
// on serving from cache, waking a daemon on the next request — which is exactly why this
|
|
249
|
+
// must be logged as the expected condition it is rather than reported as a defect.
|
|
250
|
+
void startMcpProxy(port, ensure, undefined, () => {
|
|
251
|
+
resilience.noteUsableSession();
|
|
252
|
+
}, {
|
|
253
|
+
ready,
|
|
254
|
+
spawning,
|
|
255
|
+
// Loaded only when needed: the tool table is large, and a warm start never uses it.
|
|
256
|
+
tools: async () => {
|
|
257
|
+
const [{ staticToolCatalog }, { resolveToolSurface }] = await Promise.all([
|
|
258
|
+
import('../../surface/mcp/mcp.js'),
|
|
259
|
+
import('../../surface/tools/tool-surface.js'),
|
|
260
|
+
]);
|
|
261
|
+
return staticToolCatalog(resolveToolSurface());
|
|
262
|
+
},
|
|
263
|
+
}).catch((err) => {
|
|
264
|
+
log('reticle_mcp_proxy_first_connect_failed', {
|
|
265
|
+
port,
|
|
266
|
+
error: err instanceof Error ? err.message : String(err),
|
|
267
|
+
note: 'serving from cache; the next client request will try to start a daemon',
|
|
182
268
|
});
|
|
183
269
|
});
|
|
184
270
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ToolCaller } from './adhoc-verdict.js';
|
|
2
|
+
/**
|
|
3
|
+
* `reticle open <url> --navigate`: move the tab that is already on this origin to `url`.
|
|
4
|
+
*
|
|
5
|
+
* The default leaves that tab where it is, on purpose: `open` is often run by a person whose tab it
|
|
6
|
+
* is (see `decideOpen`). But a caller with no MCP tools, a shell loop or an agent whose MCP link
|
|
7
|
+
* timed out, then had no way past the first page of a multi-page site short of restarting the
|
|
8
|
+
* daemon (#1140). This is the explicit opt-in. It sends the same `reticle_navigate` an agent would,
|
|
9
|
+
* over the daemon's own SSE transport, exactly as `verify --expect` does, and waits for arrival.
|
|
10
|
+
*/
|
|
11
|
+
export interface OpenNavigateOptions {
|
|
12
|
+
port: number;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
url: string;
|
|
15
|
+
/**
|
|
16
|
+
* The daemon's pairing token. Omitted, it is read here, the way `verify --expect` reads it, so the
|
|
17
|
+
* CLI entry point does not reach into the bridge itself. A test that injects `connect` gets none.
|
|
18
|
+
*/
|
|
19
|
+
token?: string;
|
|
20
|
+
/** Injected so a test does not need a live daemon. */
|
|
21
|
+
connect?: (endpoint: URL) => Promise<ToolCaller>;
|
|
22
|
+
}
|
|
23
|
+
/** Accepted by the browser, but no page on the url was observed: not `navigated`, and not an error. */
|
|
24
|
+
export declare const NAVIGATE_UNCONFIRMED_NOTE: string;
|
|
25
|
+
/**
|
|
26
|
+
* Navigate the session and describe the outcome as fields for the `reticle_open` line. `navigated`
|
|
27
|
+
* is set only when the tool says the page arrived; otherwise the tool's own reason is passed on,
|
|
28
|
+
* never a success the navigate did not report.
|
|
29
|
+
*/
|
|
30
|
+
export declare function navigateLeftTab(options: OpenNavigateOptions): Promise<Record<string, unknown>>;
|
|
31
|
+
/** `--navigate` found a tab on the origin, but the tab never said which session it is. */
|
|
32
|
+
export declare const NAVIGATE_NO_SESSION_ID: string;
|
|
33
|
+
/**
|
|
34
|
+
* The tabs `open` may pick from.
|
|
35
|
+
*
|
|
36
|
+
* `--navigate` MOVES a tab, so it may only pick one of this project's: on a daemon serving two
|
|
37
|
+
* projects, the origin match alone could hand it the other project's tab. A tab that reports no
|
|
38
|
+
* project is kept, since nothing says it is someone else's. Reuse and the left-as-is note only read,
|
|
39
|
+
* so without `--navigate`, or with no project to scope by, every tab stays a candidate.
|
|
40
|
+
*/
|
|
41
|
+
export declare function openCandidates<T extends {
|
|
42
|
+
projectId?: string | undefined;
|
|
43
|
+
}>(sessions: T[], navigate: boolean, myProject: string | undefined): T[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ReticleTool } from '@reticlehq/core';
|
|
2
|
+
import { connectOverSse, endpointFor, verdictOf } from './adhoc-verdict.js';
|
|
3
|
+
import { readOrCreatePairingTokenSync, defaultPairingTokenDir, } from '../../portal/bridge/pairing-token.js';
|
|
4
|
+
/** Named, as the repository asks of every user-facing string. */
|
|
5
|
+
const DAEMON_UNREACHABLE = 'could not reach the daemon to navigate';
|
|
6
|
+
const NAVIGATE_FAILED = 'navigate failed';
|
|
7
|
+
const NO_ARRIVAL_REPORTED = 'reticle_navigate did not report arrival';
|
|
8
|
+
/** Accepted by the browser, but no page on the url was observed: not `navigated`, and not an error. */
|
|
9
|
+
export const NAVIGATE_UNCONFIRMED_NOTE = 'the browser accepted the navigation, but no page on that url reconnected in the wait window, so ' +
|
|
10
|
+
'arrival was not observed. Re-run `reticle status` before relying on the tab being there.';
|
|
11
|
+
/**
|
|
12
|
+
* Navigate the session and describe the outcome as fields for the `reticle_open` line. `navigated`
|
|
13
|
+
* is set only when the tool says the page arrived; otherwise the tool's own reason is passed on,
|
|
14
|
+
* never a success the navigate did not report.
|
|
15
|
+
*/
|
|
16
|
+
export async function navigateLeftTab(options) {
|
|
17
|
+
const connect = options.connect ?? connectOverSse;
|
|
18
|
+
let caller;
|
|
19
|
+
try {
|
|
20
|
+
const token = options.token ??
|
|
21
|
+
(options.connect === undefined
|
|
22
|
+
? readOrCreatePairingTokenSync(defaultPairingTokenDir())
|
|
23
|
+
: undefined);
|
|
24
|
+
caller = await connect(endpointFor(options.port, token));
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return {
|
|
28
|
+
error: `${DAEMON_UNREACHABLE}: ${error instanceof Error ? error.message : String(error)}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const result = await caller.call(ReticleTool.NAVIGATE, {
|
|
33
|
+
url: options.url,
|
|
34
|
+
sessionId: options.sessionId,
|
|
35
|
+
});
|
|
36
|
+
// `ok` is the field every navigate answer carries, so a text-only answer is read too.
|
|
37
|
+
const report = verdictOf(result, 'ok') ?? {};
|
|
38
|
+
const landedOn = 'string' === typeof report['landedOn'] ? report['landedOn'] : undefined;
|
|
39
|
+
// A navigation reconnects as a NEW session; the id to act on next is the arrival's, not ours.
|
|
40
|
+
const arrivedAs = 'string' === typeof report['sessionId'] ? report['sessionId'] : options.sessionId;
|
|
41
|
+
if (true === report['ok'] && false === report['confirmed'] && landedOn === undefined) {
|
|
42
|
+
return { requested: options.url, sessionId: arrivedAs, note: NAVIGATE_UNCONFIRMED_NOTE };
|
|
43
|
+
}
|
|
44
|
+
if (true === report['ok']) {
|
|
45
|
+
return {
|
|
46
|
+
navigated: options.url,
|
|
47
|
+
sessionId: arrivedAs,
|
|
48
|
+
...(landedOn === undefined ? {} : { landedOn }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const reason = 'string' === typeof report['reason']
|
|
52
|
+
? report['reason']
|
|
53
|
+
: 'string' === typeof report['error']
|
|
54
|
+
? report['error']
|
|
55
|
+
: NO_ARRIVAL_REPORTED;
|
|
56
|
+
return {
|
|
57
|
+
error: `${NAVIGATE_FAILED} (${options.url}): ${reason}`,
|
|
58
|
+
sessionId: options.sessionId,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
error: `${NAVIGATE_FAILED} (${options.url}): ${error instanceof Error ? error.message : String(error)}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await caller.close().catch(() => undefined);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** `--navigate` found a tab on the origin, but the tab never said which session it is. */
|
|
71
|
+
export const NAVIGATE_NO_SESSION_ID = '--navigate could not move it: that tab reported no session id. Drive it with reticle_navigate, ' +
|
|
72
|
+
'or open the url in the browser yourself.';
|
|
73
|
+
/**
|
|
74
|
+
* The tabs `open` may pick from.
|
|
75
|
+
*
|
|
76
|
+
* `--navigate` MOVES a tab, so it may only pick one of this project's: on a daemon serving two
|
|
77
|
+
* projects, the origin match alone could hand it the other project's tab. A tab that reports no
|
|
78
|
+
* project is kept, since nothing says it is someone else's. Reuse and the left-as-is note only read,
|
|
79
|
+
* so without `--navigate`, or with no project to scope by, every tab stays a candidate.
|
|
80
|
+
*/
|
|
81
|
+
export function openCandidates(sessions, navigate, myProject) {
|
|
82
|
+
if (!navigate || myProject === undefined)
|
|
83
|
+
return sessions;
|
|
84
|
+
return sessions.filter((s) => s.projectId === undefined || s.projectId === myProject);
|
|
85
|
+
}
|
|
@@ -26,7 +26,7 @@ export interface PortHolder {
|
|
|
26
26
|
* identify the holder, rather than asserting the holder is not ours — a claim it has no evidence
|
|
27
27
|
* for, and one that was wrong for every Windows user.
|
|
28
28
|
*/
|
|
29
|
-
export declare function captureLookup(command: string, args: readonly string[]): string | null;
|
|
29
|
+
export declare function captureLookup(command: string, args: readonly string[], timeoutMs?: number): string | null;
|
|
30
30
|
/**
|
|
31
31
|
* The FIRST listener in `lsof -nP -iTCP:<port> -sTCP:LISTEN -F pc` output, or null.
|
|
32
32
|
*
|
|
@@ -53,11 +53,23 @@ ourPid?: number | null): string;
|
|
|
53
53
|
export declare function findPortHolder(port: number, exec: (command: string, args: readonly string[]) => string | null): PortHolder | null;
|
|
54
54
|
/** The path in `lsof -a -p <pid> -d cwd -Fn` output, or null when it printed none. */
|
|
55
55
|
export declare function parseProcessCwd(stdout: string): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* The folder that holds the `node_modules` a command line runs from, or null.
|
|
58
|
+
*
|
|
59
|
+
* A dev server started from a project runs a script under that project's `node_modules`, so the
|
|
60
|
+
* folder before it is the project. The path starts at the last drive letter before it, because an
|
|
61
|
+
* unquoted node path can come first. No `node_modules` is no evidence, and the answer is null.
|
|
62
|
+
* ponytail: compares as written; a drive letter in another case does not match.
|
|
63
|
+
*/
|
|
64
|
+
export declare function parseCommandLineRoot(commandLine: string): string | null;
|
|
56
65
|
/**
|
|
57
66
|
* The working directory of whatever LISTENS on `port`, or null.
|
|
58
67
|
*
|
|
59
68
|
* How the daemon tells which checkout served a page when every git worktree declares the same
|
|
60
|
-
* projectId: the dev server runs inside exactly one of them. Null wherever
|
|
61
|
-
*
|
|
69
|
+
* projectId: the dev server runs inside exactly one of them. Null wherever the lookup cannot answer
|
|
70
|
+
* — a slim container with no `lsof` — and the caller then keeps the answer it had without this.
|
|
71
|
+
*
|
|
72
|
+
* Windows has no `lsof`, and no command shows another process's working directory. There the
|
|
73
|
+
* answer is the project folder in the listener's command line, from PowerShell.
|
|
62
74
|
*/
|
|
63
|
-
export declare function findListenerCwd(port: number, exec: (command: string, args: readonly string[]) => string | null): string | null;
|
|
75
|
+
export declare function findListenerCwd(port: number, exec: (command: string, args: readonly string[], timeoutMs?: number) => string | null, platform?: NodeJS.Platform): string | null;
|
|
@@ -25,9 +25,14 @@ const LOOKUP_TIMEOUT_MS = 2_000;
|
|
|
25
25
|
* identify the holder, rather than asserting the holder is not ours — a claim it has no evidence
|
|
26
26
|
* for, and one that was wrong for every Windows user.
|
|
27
27
|
*/
|
|
28
|
-
export function captureLookup(command, args) {
|
|
28
|
+
export function captureLookup(command, args, timeoutMs = LOOKUP_TIMEOUT_MS) {
|
|
29
29
|
try {
|
|
30
|
-
|
|
30
|
+
// The daemon has no console: without windowsHide, each lookup opens a visible window on Windows.
|
|
31
|
+
const result = spawnSync(command, [...args], {
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
timeout: timeoutMs,
|
|
34
|
+
windowsHide: true,
|
|
35
|
+
});
|
|
31
36
|
return 0 === result.status && 'string' === typeof result.stdout ? result.stdout : null;
|
|
32
37
|
}
|
|
33
38
|
catch {
|
|
@@ -52,20 +57,25 @@ export function parsePortHolder(stdout) {
|
|
|
52
57
|
let command;
|
|
53
58
|
for (const raw of stdout.split('\n')) {
|
|
54
59
|
const line = raw.trim();
|
|
55
|
-
if (0 === line.length)
|
|
60
|
+
if (0 === line.length) {
|
|
56
61
|
continue;
|
|
62
|
+
}
|
|
57
63
|
const value = line.slice(1);
|
|
58
64
|
if (line.startsWith(PID_FIELD)) {
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
// A second record: the first one is the answer.
|
|
66
|
+
if (pid !== undefined) {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
61
69
|
const parsed = Number(value);
|
|
62
|
-
if (!Number.isInteger(parsed) || parsed <= 0)
|
|
70
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
63
71
|
return null;
|
|
72
|
+
}
|
|
64
73
|
pid = parsed;
|
|
65
74
|
continue;
|
|
66
75
|
}
|
|
67
|
-
if (line.startsWith(COMMAND_FIELD) && command === undefined)
|
|
76
|
+
if (line.startsWith(COMMAND_FIELD) && command === undefined) {
|
|
68
77
|
command = value;
|
|
78
|
+
}
|
|
69
79
|
}
|
|
70
80
|
return pid === undefined || command === undefined ? null : { pid, command };
|
|
71
81
|
}
|
|
@@ -126,22 +136,58 @@ const NAME_FIELD = 'n';
|
|
|
126
136
|
/** The path in `lsof -a -p <pid> -d cwd -Fn` output, or null when it printed none. */
|
|
127
137
|
export function parseProcessCwd(stdout) {
|
|
128
138
|
for (const raw of stdout.split('\n')) {
|
|
129
|
-
if (raw.startsWith(NAME_FIELD) && raw.length > NAME_FIELD.length)
|
|
139
|
+
if (raw.startsWith(NAME_FIELD) && raw.length > NAME_FIELD.length) {
|
|
130
140
|
return raw.slice(1);
|
|
141
|
+
}
|
|
131
142
|
}
|
|
132
143
|
return null;
|
|
133
144
|
}
|
|
145
|
+
/** A Windows PowerShell cold start alone can take more than the lsof bound. */
|
|
146
|
+
const WINDOWS_LOOKUP_TIMEOUT_MS = 8_000;
|
|
147
|
+
/** The listener's command line: Windows does not show another process's working directory. */
|
|
148
|
+
const windowsListenerScript = (port) => `$c = Get-NetTCPConnection -State Listen -LocalPort ${String(port)} -ErrorAction Stop | ` +
|
|
149
|
+
'Select-Object -First 1; ' +
|
|
150
|
+
'(Get-CimInstance Win32_Process -Filter "ProcessId=$($c.OwningProcess)").CommandLine';
|
|
151
|
+
const NODE_MODULES = /[\\/]node_modules[\\/]/i;
|
|
152
|
+
const DRIVE_START = /[A-Za-z]:[\\/]/g;
|
|
153
|
+
/**
|
|
154
|
+
* The folder that holds the `node_modules` a command line runs from, or null.
|
|
155
|
+
*
|
|
156
|
+
* A dev server started from a project runs a script under that project's `node_modules`, so the
|
|
157
|
+
* folder before it is the project. The path starts at the last drive letter before it, because an
|
|
158
|
+
* unquoted node path can come first. No `node_modules` is no evidence, and the answer is null.
|
|
159
|
+
* ponytail: compares as written; a drive letter in another case does not match.
|
|
160
|
+
*/
|
|
161
|
+
export function parseCommandLineRoot(commandLine) {
|
|
162
|
+
const end = commandLine.search(NODE_MODULES);
|
|
163
|
+
if (end < 0) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const head = commandLine.slice(0, end);
|
|
167
|
+
const starts = [...head.matchAll(DRIVE_START)];
|
|
168
|
+
const start = starts[starts.length - 1]?.index;
|
|
169
|
+
return start === undefined ? null : head.slice(start);
|
|
170
|
+
}
|
|
134
171
|
/**
|
|
135
172
|
* The working directory of whatever LISTENS on `port`, or null.
|
|
136
173
|
*
|
|
137
174
|
* How the daemon tells which checkout served a page when every git worktree declares the same
|
|
138
|
-
* projectId: the dev server runs inside exactly one of them. Null wherever
|
|
139
|
-
*
|
|
175
|
+
* projectId: the dev server runs inside exactly one of them. Null wherever the lookup cannot answer
|
|
176
|
+
* — a slim container with no `lsof` — and the caller then keeps the answer it had without this.
|
|
177
|
+
*
|
|
178
|
+
* Windows has no `lsof`, and no command shows another process's working directory. There the
|
|
179
|
+
* answer is the project folder in the listener's command line, from PowerShell.
|
|
140
180
|
*/
|
|
141
|
-
export function findListenerCwd(port, exec) {
|
|
181
|
+
export function findListenerCwd(port, exec, platform = process.platform) {
|
|
182
|
+
if ('win32' === platform) {
|
|
183
|
+
const args = ['-NoProfile', '-NonInteractive', '-Command', windowsListenerScript(port)];
|
|
184
|
+
const line = exec('powershell', args, WINDOWS_LOOKUP_TIMEOUT_MS);
|
|
185
|
+
return null === line ? null : parseCommandLineRoot(line.trim());
|
|
186
|
+
}
|
|
142
187
|
const holder = findPortHolder(port, exec);
|
|
143
|
-
if (null === holder)
|
|
188
|
+
if (null === holder) {
|
|
144
189
|
return null;
|
|
190
|
+
}
|
|
145
191
|
const out = exec('lsof', ['-a', '-p', String(holder.pid), '-d', 'cwd', '-Fn']);
|
|
146
192
|
return null === out ? null : parseProcessCwd(out);
|
|
147
193
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { readFileSync } from 'node:fs';
|
|
11
11
|
import { dirname, join, resolve } from 'node:path';
|
|
12
12
|
import { SILENT_HOST, buildNodeIo, deriveProjectId, findWorkspaceApps, isDesktopProject, packageName, } from '@reticlehq/init';
|
|
13
|
-
import { asProjectId } from '@reticlehq/core';
|
|
13
|
+
import { asProjectId, mainCheckoutTwin } from '@reticlehq/core';
|
|
14
14
|
/** The project config `reticle init` writes. */
|
|
15
15
|
export const RETICLE_CONFIG_BASENAME = '.reticle.json';
|
|
16
16
|
/**
|
|
@@ -35,9 +35,25 @@ const MAX_CONFIG_SEARCH_DEPTH = 6;
|
|
|
35
35
|
* Returns undefined when nothing is found, which keeps every caller's existing default intact.
|
|
36
36
|
*/
|
|
37
37
|
export function findProjectConfig(cwd) {
|
|
38
|
-
const dir = findProjectConfigDir(cwd);
|
|
38
|
+
const dir = findProjectConfigDir(cwd) ?? configDirInMainCheckout(cwd);
|
|
39
39
|
return dir === undefined ? undefined : readConfigAt(dir);
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The main checkout's config folder, for a git worktree that has no `.reticle.json` of its own (it
|
|
43
|
+
* is git-ignored, or not committed yet). Every worktree is the same project, so it keeps one id and
|
|
44
|
+
* one port. Only the config's VALUES come from here; the worktree stays the project folder.
|
|
45
|
+
*/
|
|
46
|
+
function configDirInMainCheckout(cwd) {
|
|
47
|
+
const twin = mainCheckoutTwin(resolve(cwd), (path) => {
|
|
48
|
+
try {
|
|
49
|
+
return readFileSync(path, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined; // Missing or a directory: not the `.git` file this looks for.
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return twin === undefined ? undefined : findProjectConfigDir(twin);
|
|
56
|
+
}
|
|
41
57
|
/** The directory holding the nearest `.reticle.json` at or above `cwd`, by the same walk. */
|
|
42
58
|
function findProjectConfigDir(cwd) {
|
|
43
59
|
let dir = resolve(cwd);
|
|
@@ -34,13 +34,7 @@ localProjectId?: string) => string;
|
|
|
34
34
|
export declare const CreatedProjectSchema: z.ZodObject<{
|
|
35
35
|
projectId: z.ZodString;
|
|
36
36
|
name: z.ZodString;
|
|
37
|
-
},
|
|
38
|
-
name: string;
|
|
39
|
-
projectId: string;
|
|
40
|
-
}, {
|
|
41
|
-
name: string;
|
|
42
|
-
projectId: string;
|
|
43
|
-
}>;
|
|
37
|
+
}, z.core.$strip>;
|
|
44
38
|
/** ` in workspace "<name>"`, or nothing when no name is known. */
|
|
45
39
|
export declare const inWorkspace: (name: string | undefined) => string;
|
|
46
40
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** What the Stop hook tells an agent that tried to stop while its Harness drive runs. */
|
|
2
|
+
export declare function keepAliveReason(runId: string): string;
|
|
1
3
|
export interface ReportInput {
|
|
2
4
|
cwd: string;
|
|
3
5
|
now: number;
|
|
@@ -5,12 +7,16 @@ export interface ReportInput {
|
|
|
5
7
|
treeChanged: boolean;
|
|
6
8
|
session?: string;
|
|
7
9
|
hook?: boolean;
|
|
10
|
+
/** The user's home, whose `~/.reticle/unmatched` is read too. Absent: the project's root alone. */
|
|
11
|
+
home?: string;
|
|
8
12
|
}
|
|
9
13
|
export declare function reportFor(input: ReportInput): Promise<{
|
|
10
14
|
lines: string[];
|
|
15
|
+
block?: string;
|
|
11
16
|
}>;
|
|
12
17
|
/**
|
|
13
|
-
* The command. As a hook it prints Claude Code's `systemMessage` shape and always exits 0
|
|
14
|
-
*
|
|
18
|
+
* The command. As a hook it prints Claude Code's `systemMessage` shape and always exits 0, so the
|
|
19
|
+
* human sees the gap. It blocks the stop in one case only: a Harness drive the agent started is still
|
|
20
|
+
* running, bounded by `KEEP_ALIVE_MAX_MS`.
|
|
15
21
|
*/
|
|
16
22
|
export declare function handleReport(session: string | undefined, hook: boolean): Promise<void>;
|