@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
package/dist/command/cli.js
CHANGED
|
@@ -8,34 +8,32 @@ import { openFailureNote, openLaunchFailureNote } from './cli/answers/open-note.
|
|
|
8
8
|
import { realpathSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { installCrashGuard } from './setup/setup-command.js';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
import { attachStatusFields } from '../surface/mcp/attach-memory.js';
|
|
14
|
-
import { splitBrainFields, withNextAction } from './cli/status-fields.js';
|
|
11
|
+
import { handleStatus } from './cli/status-fields.js';
|
|
12
|
+
export { handleStatus, reportStatus } from './cli/status-fields.js';
|
|
15
13
|
import { reticleStateHome } from './daemon/daemon.js';
|
|
16
14
|
import { handleMcp } from './cli/mcp-command.js';
|
|
17
15
|
import { handleReport } from './cli/report-command.js';
|
|
18
16
|
import { handleContext } from './cli/context-command.js';
|
|
17
|
+
import { handleHook } from './cli/hook/hook-command.js';
|
|
18
|
+
import { HOOK_COMMAND } from './cli/cli-parse.js';
|
|
19
19
|
import { daemonProjectAt, readDaemonRegistry, resolveDaemonForProject, } from './daemon/daemon-resolve.js';
|
|
20
20
|
import { pickDaemonPortToBind } from './daemon/binding/free-port.js';
|
|
21
21
|
import { isSameOrAbove, portForInit, portFromEnv } from './setup/init/init-port.js';
|
|
22
22
|
import { daemonStartOptions } from './cli/daemon-start-options.js';
|
|
23
23
|
import { handleGate, loadNamedFlows, resolveChangedFiles } from './cli/cli-flow-commands.js';
|
|
24
|
-
import { RETICLE_DEFAULT_PORT, ReticleDir, ReticleEnv
|
|
24
|
+
import { RETICLE_DEFAULT_PORT, ReticleDir, ReticleEnv } from '@reticlehq/core';
|
|
25
25
|
import { loadDotEnv } from '../telemetry/dev-repo.js';
|
|
26
26
|
import { licenseKeyFromEnvFiles } from '../features/license/license-env.js';
|
|
27
27
|
import { LICENSE_KEY_ENV } from '../features/license/license.js';
|
|
28
28
|
import { createNodeFileSystem } from '../memory/project/fs/fs-port.js';
|
|
29
|
-
import { localLinkOf, workspaceAppLinks } from './cli/status/workspace-links.js';
|
|
30
29
|
import { affectedSavedFlows } from '../language/flows/change/flow-sources.js';
|
|
31
|
-
import { availableUpdate } from './update/update-nudge.js';
|
|
32
30
|
import { handleUpdate, handleRollback } from './cli/cli-update-commands.js';
|
|
33
31
|
import { headlessByDefault } from './cli/daemon-start-options.js';
|
|
34
32
|
import { startDaemon } from '../index.js';
|
|
35
|
-
import {
|
|
33
|
+
import { cmdLink, connectInstalls, isCloudCommand, runCloudCommand } from './cli/cloud-cli.js';
|
|
36
34
|
import { SERVER_VERSION } from './version/identity/server-version.js';
|
|
37
35
|
import { log } from '../log.js';
|
|
38
|
-
import {
|
|
36
|
+
import { isAlive, removePid, spawnDaemon, discoverDaemonPort, writeDaemonRegistry, } from './daemon/daemon.js';
|
|
39
37
|
import { PortPresence, probePresence, presenceIsUsable, describePresence, } from './daemon/binding/port-presence.js';
|
|
40
38
|
import { waitForDaemon, probeDaemon } from '../surface/mcp/mcp-proxy.js';
|
|
41
39
|
import { installDaemonResilience, recordExitReason, DaemonExitReason, } from './daemon/daemon-resilience.js';
|
|
@@ -44,6 +42,7 @@ import { DaemonHeartbeat, resolveHeartbeatMs } from './daemon/lifetime/heartbeat
|
|
|
44
42
|
import { everServedToolCall } from './daemon/lifetime/daemon-usefulness.js';
|
|
45
43
|
import { DAEMON_START_FAILED_EVENT } from './daemon/lifetime/startup-failure.js';
|
|
46
44
|
import { summarizeStatus, warnOnDaemonSkew, decideOpen, openInBrowser, openCommand, } from './cli/launch/cli-launch.js';
|
|
45
|
+
import { NAVIGATE_NO_SESSION_ID, navigateLeftTab, openCandidates } from './cli/open-navigate.js';
|
|
47
46
|
import { fetchStatus } from './daemon/binding/daemon-status-probe.js';
|
|
48
47
|
import { handleDrive } from './cli/drive/drive-command.js';
|
|
49
48
|
import { handleVerify } from './cli/cli-verify.js';
|
|
@@ -79,10 +78,12 @@ export const MSG_CONNECTED_DEPS_PENDING = '⚠ Connected, but the dependency ins
|
|
|
79
78
|
* else. `initUnfinished` is why init's setup did not finish, or undefined when it did.
|
|
80
79
|
*/
|
|
81
80
|
export function connectExit(cloudCode, initUnfinished) {
|
|
82
|
-
if (0 !== cloudCode)
|
|
81
|
+
if (0 !== cloudCode) {
|
|
83
82
|
return { code: cloudCode };
|
|
84
|
-
|
|
83
|
+
}
|
|
84
|
+
if (initUnfinished === undefined) {
|
|
85
85
|
return { code: 0 };
|
|
86
|
+
}
|
|
86
87
|
if (InitFailure.DEPENDENCY_INSTALL === initUnfinished) {
|
|
87
88
|
return { code: 0, note: MSG_CONNECTED_DEPS_PENDING };
|
|
88
89
|
}
|
|
@@ -98,8 +99,9 @@ async function handleInit(parsed) {
|
|
|
98
99
|
// projects its connected pages announced.
|
|
99
100
|
daemonProjects: async (p) => {
|
|
100
101
|
const claimed = daemonProjectAt(p, reticleStateHome());
|
|
101
|
-
if (claimed !== undefined && claimed.length > 0)
|
|
102
|
+
if (claimed !== undefined && claimed.length > 0) {
|
|
102
103
|
return [claimed];
|
|
104
|
+
}
|
|
103
105
|
const { sessions } = summarizeStatus(await fetchStatus(p));
|
|
104
106
|
return [
|
|
105
107
|
...new Set(sessions.flatMap((s) => (s.projectId === undefined ? [] : [s.projectId]))),
|
|
@@ -125,6 +127,8 @@ async function handleInit(parsed) {
|
|
|
125
127
|
...(parsed.url === undefined ? {} : { url: parsed.url }),
|
|
126
128
|
captureBodies: true === parsed.captureBodies,
|
|
127
129
|
hooks: true === parsed.hooks,
|
|
130
|
+
newId: true === parsed.newId,
|
|
131
|
+
promptCapture: false !== parsed.promptCapture,
|
|
128
132
|
// The outcome is reported by confirmInstall instead, once it knows whether an app connected —
|
|
129
133
|
// `init` writing files was never the same thing as `init` working (#269).
|
|
130
134
|
deferOutcome: true,
|
|
@@ -136,138 +140,6 @@ async function handleInit(parsed) {
|
|
|
136
140
|
// `serve`, `stop` and `restart` live in `cli/lifecycle/daemon-lifecycle.ts`: one idea, and the
|
|
137
141
|
// largest group this file could give up without splitting something that belongs together.
|
|
138
142
|
import { handleServe, handleStop, handleRestart } from './cli/lifecycle/daemon-lifecycle.js';
|
|
139
|
-
import { statusLines } from './cli/status/status-lines.js';
|
|
140
|
-
/**
|
|
141
|
-
* Report status to the terminal. `--json` writes one structured line to STDOUT so it can be piped
|
|
142
|
-
* into `jq`; the human-readable block goes to stdout too. `log()` stays on stderr (the MCP
|
|
143
|
-
* transport lives on stdout), so `--json` must not go through it.
|
|
144
|
-
*/
|
|
145
|
-
export function reportStatus(fields, json) {
|
|
146
|
-
if (json) {
|
|
147
|
-
const line = JSON.stringify({
|
|
148
|
-
t: new Date().toISOString(),
|
|
149
|
-
event: 'reticle_status',
|
|
150
|
-
...fields,
|
|
151
|
-
});
|
|
152
|
-
process.stdout.write(`${line}\n`);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
process.stdout.write(`${statusLines(fields).join('\n')}\n`);
|
|
156
|
-
}
|
|
157
|
-
export async function handleStatus(port, json = false) {
|
|
158
|
-
// A broken session file must not cost the reader the rest of the status.
|
|
159
|
-
const account = await accountStatusFields().catch(() => ({}));
|
|
160
|
-
// At a workspace root that is no app of its own, each app and its link: "not linked" named none.
|
|
161
|
-
const linked = account.linkedProject;
|
|
162
|
-
if ('string' !== typeof linked && readProjectId(process.cwd()) === undefined)
|
|
163
|
-
Object.assign(account, {
|
|
164
|
-
workspaceApps: await workspaceAppLinks(process.cwd(), localLinkOf).catch(() => []),
|
|
165
|
-
});
|
|
166
|
-
const report = (fields) => reportStatus({ ...fields, ...account }, json);
|
|
167
|
-
const pid = readPid(port);
|
|
168
|
-
// Durable, so it survives the daemon idling out — which is the state `status` is most often run in.
|
|
169
|
-
const projectId = readProjectId(process.cwd());
|
|
170
|
-
const previouslyConnected = hasAnyAppConnectedBefore(reticleStateHome(), port, projectId);
|
|
171
|
-
// The narrow twin. Every sentence that says "this project", and the decision about whether THIS
|
|
172
|
-
// app is instrumented, needs the question the wide one cannot answer without a project id.
|
|
173
|
-
const projectPreviouslyConnected = hasProjectConnectedBefore(reticleStateHome(), port, projectId);
|
|
174
|
-
// Whether `init` has run HERE. Registering the MCP server does not wire the app, and more than one
|
|
175
|
-
// path does the first without the second — so this is the commonest reason `status` has nothing to
|
|
176
|
-
// report, and it was not among the facts this command could state.
|
|
177
|
-
const initialized = projectId !== undefined;
|
|
178
|
-
// The OTHER half of the install, and the half this command could not see. A host that registers
|
|
179
|
-
// Reticle and never lists its tools looks exactly like an abandoned install from here — same idle
|
|
180
|
-
// daemon, same zero sessions — and the user sees a tool that does nothing. See attach-memory.ts.
|
|
181
|
-
const client = attachStatusFields(reticleStateHome(), port);
|
|
182
|
-
// What the dev servers themselves said. The reason this command could previously only guess at
|
|
183
|
-
// whether the app was running is that it had no way to see one; now the ones carrying Reticle
|
|
184
|
-
// announce, and the advice below stops contradicting the terminal the reader is looking at.
|
|
185
|
-
// Scoped to THIS project: the registry is machine-wide, and an unrelated app's dev server must
|
|
186
|
-
// not make this project look like it is running.
|
|
187
|
-
const devServerPorts = devServersForProject(readDevServers(reticleStateHome()), {
|
|
188
|
-
projectId,
|
|
189
|
-
root: process.cwd(),
|
|
190
|
-
}).map((d) => d.port);
|
|
191
|
-
// The failure where every individual check is green and the chain is broken: the agent's proxy and
|
|
192
|
-
// the app resolved their ports independently and landed on two different daemons. Reported from
|
|
193
|
-
// BOTH stances, because neither one can see it alone.
|
|
194
|
-
const split = splitBrainFields(port, projectId);
|
|
195
|
-
// Doctor and status must answer the same liveness question. A live pid only says that some
|
|
196
|
-
// process exists; a Reticle daemon is running only when the shared port probe reaches /status.
|
|
197
|
-
const presence = await probePresence(port, { tcpOpen: probeDaemon, status: fetchStatus });
|
|
198
|
-
if (!presenceIsUsable(presence)) {
|
|
199
|
-
report({
|
|
200
|
-
port,
|
|
201
|
-
running: false,
|
|
202
|
-
presence,
|
|
203
|
-
...(presence === PortPresence.FOREIGN ? { reason: describePresence(presence, port) } : {}),
|
|
204
|
-
// `init` promises this command says why the app has not connected. Without it the answer was
|
|
205
|
-
// `running: false` and nothing else, which reads as "Reticle is broken" for what is usually
|
|
206
|
-
// just a daemon that has not been asked to do anything yet.
|
|
207
|
-
...withNextAction({
|
|
208
|
-
running: false,
|
|
209
|
-
sessionCount: 0,
|
|
210
|
-
previouslyConnected,
|
|
211
|
-
projectPreviouslyConnected,
|
|
212
|
-
initialized,
|
|
213
|
-
devServerPorts,
|
|
214
|
-
}),
|
|
215
|
-
...client,
|
|
216
|
-
...split,
|
|
217
|
-
});
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
// The daemon answered the same probe doctor trusts — ask it for live sessions + health.
|
|
221
|
-
const payload = await fetchStatus(port);
|
|
222
|
-
// `status` is the second-most-run command and the one a HUMAN types. The update nudge otherwise
|
|
223
|
-
// only rides a tool result, which the majority of daemons never produce — so the people most in
|
|
224
|
-
// need of an upgrade were the ones with no path to hearing about it.
|
|
225
|
-
const update = availableUpdate();
|
|
226
|
-
const nudge = update === undefined ? {} : { updateAvailable: update };
|
|
227
|
-
if (payload === undefined) {
|
|
228
|
-
report({
|
|
229
|
-
port,
|
|
230
|
-
running: true,
|
|
231
|
-
pid,
|
|
232
|
-
...nudge,
|
|
233
|
-
...withNextAction({
|
|
234
|
-
running: true,
|
|
235
|
-
sessionCount: 0,
|
|
236
|
-
previouslyConnected,
|
|
237
|
-
projectPreviouslyConnected,
|
|
238
|
-
initialized,
|
|
239
|
-
devServerPorts,
|
|
240
|
-
}),
|
|
241
|
-
...client,
|
|
242
|
-
...split,
|
|
243
|
-
});
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const summary = summarizeStatus(payload);
|
|
247
|
-
// Only when the daemon did NOT already explain itself. It has the whole diagnosis in-process and
|
|
248
|
-
// puts it on the wire as `why`; printing a second, thinner opinion beside it risks two confident
|
|
249
|
-
// answers pointing different ways, which is worse than one.
|
|
250
|
-
const next = summary.why === undefined
|
|
251
|
-
? withNextAction({
|
|
252
|
-
running: true,
|
|
253
|
-
...summary,
|
|
254
|
-
previouslyConnected,
|
|
255
|
-
projectPreviouslyConnected,
|
|
256
|
-
initialized,
|
|
257
|
-
devServerPorts,
|
|
258
|
-
})
|
|
259
|
-
: {};
|
|
260
|
-
report({
|
|
261
|
-
port,
|
|
262
|
-
running: true,
|
|
263
|
-
pid,
|
|
264
|
-
...summary,
|
|
265
|
-
...next,
|
|
266
|
-
...client,
|
|
267
|
-
...split,
|
|
268
|
-
...nudge,
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
143
|
/**
|
|
272
144
|
* Print the running package version (resolved once in server-version.ts), and nothing else.
|
|
273
145
|
*
|
|
@@ -313,18 +185,21 @@ function ensureDaemon(port) {
|
|
|
313
185
|
// upgrade does NOT take effect until that daemon dies, and nothing used to say so. Say it here,
|
|
314
186
|
// where both versions are in hand, and keep attaching: killing another agent's daemon on a
|
|
315
187
|
// version bump is worse than a loud warning.
|
|
316
|
-
if (presenceIsUsable(presence))
|
|
188
|
+
if (presenceIsUsable(presence)) {
|
|
317
189
|
return warnOnDaemonSkew(port);
|
|
190
|
+
}
|
|
318
191
|
// Not free, not a daemon: a stranger holds the port. Spawning here is guaranteed to fail —
|
|
319
192
|
// the child cannot bind — and `waitForDaemon` would then report READY anyway, because its
|
|
320
193
|
// probe is the same bare TCP connect and the stranger accepts. That is the "serve reports
|
|
321
194
|
// success for a daemon that never bound" shape. Refuse instead, with the sentence `doctor`
|
|
322
195
|
// says.
|
|
323
|
-
if (PortPresence.FREE !== presence)
|
|
196
|
+
if (PortPresence.FREE !== presence) {
|
|
324
197
|
throw new Error(describePresence(presence, port));
|
|
198
|
+
}
|
|
325
199
|
const scriptPath = process.argv[1];
|
|
326
|
-
if (scriptPath === undefined)
|
|
200
|
+
if (scriptPath === undefined) {
|
|
327
201
|
throw new Error('cannot locate the reticle daemon script');
|
|
202
|
+
}
|
|
328
203
|
spawnDaemon(process.execPath, scriptPath, [DAEMON_INNER_COMMAND, PORT_FLAG, String(port)], port);
|
|
329
204
|
return waitForDaemon(port);
|
|
330
205
|
});
|
|
@@ -348,12 +223,15 @@ async function waitForNewSession(port, before) {
|
|
|
348
223
|
while (Date.now() < deadline) {
|
|
349
224
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
350
225
|
const { sessions } = summarizeStatus(await fetchStatus(port));
|
|
351
|
-
if (sessions.length > before)
|
|
226
|
+
if (sessions.length > before) {
|
|
352
227
|
return true;
|
|
228
|
+
}
|
|
353
229
|
}
|
|
354
230
|
return false;
|
|
355
231
|
}
|
|
356
|
-
|
|
232
|
+
/** What to do about a same-origin tab `open` left where it was. */
|
|
233
|
+
const OPEN_LEFT_AS_IS_REMEDY = 'Pass --navigate to move it, drive it with reticle_navigate, or open the url in the browser yourself.';
|
|
234
|
+
function handleOpen(requestedPort, url, navigate = false) {
|
|
357
235
|
// Our project's daemon first: `discoverDaemonPort` returns the LOWEST live daemon on the machine,
|
|
358
236
|
// whoever it belongs to, so on a machine running two projects `reticle open` could drive the other
|
|
359
237
|
// one's browser. It stays as the last resort for a caller with no project id, where any daemon is
|
|
@@ -368,7 +246,10 @@ function handleOpen(requestedPort, url) {
|
|
|
368
246
|
.then(async (port) => {
|
|
369
247
|
await ensureDaemon(port);
|
|
370
248
|
const { sessions } = summarizeStatus(await fetchStatus(port));
|
|
371
|
-
|
|
249
|
+
// `--navigate` MOVES a tab, so it may only pick one of ours: on a daemon serving two projects,
|
|
250
|
+
// the origin match alone could hand it the other project's tab. Reuse and the note are read-only.
|
|
251
|
+
const candidates = openCandidates(sessions, navigate, myProject);
|
|
252
|
+
const decision = decideOpen(candidates, url);
|
|
372
253
|
if ('need-url' === decision.action) {
|
|
373
254
|
log('reticle_open', {
|
|
374
255
|
port,
|
|
@@ -384,15 +265,33 @@ function handleOpen(requestedPort, url) {
|
|
|
384
265
|
// A tab on the right origin, on the WRONG page. Kept (that is what stops a tab piling up per
|
|
385
266
|
// run) but never reported as done: `reusing` here read as "your url is open" for a page nobody
|
|
386
267
|
// had opened.
|
|
268
|
+
if ('left-as-is' === decision.action && navigate && decision.sessionId !== undefined) {
|
|
269
|
+
const moved = await navigateLeftTab({
|
|
270
|
+
port,
|
|
271
|
+
sessionId: decision.sessionId,
|
|
272
|
+
url: decision.requested,
|
|
273
|
+
});
|
|
274
|
+
log('reticle_open', { port, ...moved });
|
|
275
|
+
// A loop or CI step reads the exit code, so a failed move must not exit 0.
|
|
276
|
+
if (moved['error'] !== undefined) {
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
387
281
|
if ('left-as-is' === decision.action) {
|
|
282
|
+
// `--navigate` was asked for and could not be honoured: the tab never reported a session id
|
|
283
|
+
// to move. Suggesting `--navigate` back to someone who just passed it reads as being ignored.
|
|
284
|
+
const remedy = navigate ? NAVIGATE_NO_SESSION_ID : OPEN_LEFT_AS_IS_REMEDY;
|
|
388
285
|
log('reticle_open', {
|
|
389
286
|
port,
|
|
390
287
|
reusing: decision.url,
|
|
391
288
|
requested: decision.requested,
|
|
392
289
|
note: `a tab is connected on this origin but sitting on ${decision.url} — it was LEFT THERE, ` +
|
|
393
|
-
`not navigated to ${decision.requested}.
|
|
394
|
-
'in the browser yourself.',
|
|
290
|
+
`not navigated to ${decision.requested}. ${remedy}`,
|
|
395
291
|
});
|
|
292
|
+
if (navigate) {
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
396
295
|
return;
|
|
397
296
|
}
|
|
398
297
|
const launchError = await openInBrowser(decision.url);
|
|
@@ -500,6 +399,8 @@ function handleDaemonInner(parsed) {
|
|
|
500
399
|
// argument, which would otherwise arrive as the reason.
|
|
501
400
|
process.on('SIGTERM', () => shutdown(DaemonExitReason.SIGNAL));
|
|
502
401
|
process.on('SIGINT', () => shutdown(DaemonExitReason.SIGNAL));
|
|
402
|
+
// `reticle stop` asks here first: on Windows a signal ends the process with no shutdown path.
|
|
403
|
+
server.onShutdownRequest?.(() => shutdown(DaemonExitReason.SIGNAL));
|
|
503
404
|
// Self-shut-down when idle so a detached daemon (and any headless Chromium it launched) never
|
|
504
405
|
// lingers on the user's machine after the editor closes. Reuses the same clean shutdown path.
|
|
505
406
|
const attachedGraceEnv = process.env[ReticleEnv.IDLE_ATTACHED];
|
|
@@ -553,9 +454,22 @@ export function main() {
|
|
|
553
454
|
// run then reported the licence as `missing`, which is indistinguishable from having none at all.
|
|
554
455
|
// Only the key is taken; see license-env.ts for why nothing else is.
|
|
555
456
|
const licenseKey = licenseKeyFromEnvFiles(process.cwd());
|
|
556
|
-
if (licenseKey !== undefined)
|
|
457
|
+
if (licenseKey !== undefined) {
|
|
557
458
|
process.env[LICENSE_KEY_ENV] = licenseKey;
|
|
459
|
+
}
|
|
558
460
|
const argv = process.argv.slice(2);
|
|
461
|
+
// The agent's prompt hook, FIRST: before the version re-exec (it must not start a second CLI on
|
|
462
|
+
// every prompt) and before telemetry, which must never see that a prompt existed, let alone it.
|
|
463
|
+
// `--help` falls through to the parser, which prints what `hook` does.
|
|
464
|
+
if (HOOK_COMMAND === argv[0] && !argv.some((arg) => '--help' === arg || '-h' === arg)) {
|
|
465
|
+
// `tool` too: a hook that fails must never be reported to the agent as its tool failing.
|
|
466
|
+
const agentHook = ['prompt', 'prompt-write', 'tool', 'tool-write'].includes(argv[1] ?? '');
|
|
467
|
+
void handleHook(argv.slice(1))
|
|
468
|
+
.then((code) => process.exit(code))
|
|
469
|
+
// A hook that fails must still let the prompt through, so `prompt` exits 0 whatever happens.
|
|
470
|
+
.catch(() => process.exit(agentHook ? 0 : 1));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
559
473
|
// Before anything reports or writes: a CLI a major away from the project's SDK makes every verdict
|
|
560
474
|
// `version_skew`, and the unpinned `npx @reticlehq/server` every agent entry uses resolves the
|
|
561
475
|
// LATEST major whatever the project installed. Hand the same arguments to the matching release and
|
|
@@ -639,8 +553,9 @@ export function main() {
|
|
|
639
553
|
}
|
|
640
554
|
}
|
|
641
555
|
const exit = connectExit(await runCloudCommand(argv), unfinished);
|
|
642
|
-
if (exit.note !== undefined)
|
|
556
|
+
if (exit.note !== undefined) {
|
|
643
557
|
process.stderr.write(`${exit.note}\n`);
|
|
558
|
+
}
|
|
644
559
|
return exit.code;
|
|
645
560
|
})()
|
|
646
561
|
.then((code) => process.exit(code))
|
|
@@ -665,8 +580,9 @@ export function main() {
|
|
|
665
580
|
const portConflict = projectPort === undefined && envPort === undefined && !argv.includes(PORT_FLAG)
|
|
666
581
|
? workspacePortConflict(process.cwd())
|
|
667
582
|
: undefined;
|
|
668
|
-
if (portConflict !== undefined)
|
|
583
|
+
if (portConflict !== undefined) {
|
|
669
584
|
process.stderr.write(`${portConflict}\n`);
|
|
585
|
+
}
|
|
670
586
|
// Registry BEFORE the default, and after both explicit sources.
|
|
671
587
|
//
|
|
672
588
|
// This is the line that ends the split brain. Build plugins have always asked the registry which
|
|
@@ -708,8 +624,9 @@ export function main() {
|
|
|
708
624
|
break;
|
|
709
625
|
case 'kill':
|
|
710
626
|
void runKill(parsed.port, parsed.force).then((freed) => {
|
|
711
|
-
if (!freed)
|
|
627
|
+
if (!freed) {
|
|
712
628
|
process.exit(1);
|
|
629
|
+
}
|
|
713
630
|
});
|
|
714
631
|
break;
|
|
715
632
|
case 'restart':
|
|
@@ -725,7 +642,7 @@ export function main() {
|
|
|
725
642
|
handleTelemetry(parsed.action);
|
|
726
643
|
break;
|
|
727
644
|
case 'feedback':
|
|
728
|
-
void handleFeedback(parsed.text, parsed.rating, parsed.bug, parsed.feedbackKind, parsed.agent);
|
|
645
|
+
void handleFeedback(parsed.text, parsed.rating, parsed.bug, parsed.feedbackKind, parsed.agent, parsed.contact);
|
|
729
646
|
break;
|
|
730
647
|
case 'version':
|
|
731
648
|
handleVersion();
|
|
@@ -751,7 +668,7 @@ export function main() {
|
|
|
751
668
|
}, reportStepFromCli);
|
|
752
669
|
break;
|
|
753
670
|
case 'open':
|
|
754
|
-
handleOpen(parsed.port, parsed.url);
|
|
671
|
+
handleOpen(parsed.port, parsed.url, parsed.navigate);
|
|
755
672
|
break;
|
|
756
673
|
case 'drive':
|
|
757
674
|
handleDrive(parsed, readProjectId(process.cwd()));
|
|
@@ -793,10 +710,12 @@ export function main() {
|
|
|
793
710
|
*/
|
|
794
711
|
function isEntryPoint() {
|
|
795
712
|
const argv1 = process.argv[1];
|
|
796
|
-
if (argv1 === undefined)
|
|
713
|
+
if (argv1 === undefined) {
|
|
797
714
|
return false;
|
|
798
|
-
|
|
715
|
+
}
|
|
716
|
+
if (import.meta.url === pathToFileURL(argv1).href) {
|
|
799
717
|
return true;
|
|
718
|
+
}
|
|
800
719
|
try {
|
|
801
720
|
return import.meta.url === pathToFileURL(realpathSync(argv1)).href;
|
|
802
721
|
}
|
|
@@ -82,5 +82,10 @@ export interface ProxyResilience {
|
|
|
82
82
|
* `onCrash` is for reporting only — it must not end the process. `onGone` is the ONE exception, and
|
|
83
83
|
* it is the subject of DISCONNECT_STORM below.
|
|
84
84
|
*/
|
|
85
|
-
export declare function installProxyResilience(proc: ProcessLike, log: LogFn, onCrash?: (kind: CrashKind, cause: unknown) => void, onGone?: () => void
|
|
85
|
+
export declare function installProxyResilience(proc: ProcessLike, log: LogFn, onCrash?: (kind: CrashKind, cause: unknown) => void, onGone?: () => void,
|
|
86
|
+
/**
|
|
87
|
+
* The pipes to the client (stdin and stdout). Only THEIR errors count toward DISCONNECT_STORM.
|
|
88
|
+
* Empty by default, so a test that wires a fake process never touches the real stdio.
|
|
89
|
+
*/
|
|
90
|
+
clientPipes?: readonly ProcessLike[]): ProxyResilience;
|
|
86
91
|
export {};
|
|
@@ -284,34 +284,47 @@ export function installDaemonResilience(proc, log, onFatal) {
|
|
|
284
284
|
* `onCrash` is for reporting only — it must not end the process. `onGone` is the ONE exception, and
|
|
285
285
|
* it is the subject of DISCONNECT_STORM below.
|
|
286
286
|
*/
|
|
287
|
-
export function installProxyResilience(proc, log, onCrash = reportCrash, onGone = () => process.exit(0)
|
|
287
|
+
export function installProxyResilience(proc, log, onCrash = reportCrash, onGone = () => process.exit(0),
|
|
288
|
+
/**
|
|
289
|
+
* The pipes to the client (stdin and stdout). Only THEIR errors count toward DISCONNECT_STORM.
|
|
290
|
+
* Empty by default, so a test that wires a fake process never touches the real stdio.
|
|
291
|
+
*/
|
|
292
|
+
clientPipes = []) {
|
|
288
293
|
const expected = {
|
|
289
294
|
disconnected: 'reticle_mcp_proxy_client_disconnected',
|
|
290
295
|
unreachable: 'reticle_mcp_proxy_daemon_unreachable',
|
|
291
296
|
};
|
|
292
297
|
// "Absorb the disconnect and keep serving" is right for ONE disconnect and catastrophic for a
|
|
293
298
|
// stream of them — see DISCONNECT_STORM. Counted since the last USABLE SESSION rather than since
|
|
294
|
-
// this process started
|
|
295
|
-
//
|
|
296
|
-
// which restarts routinely and is the thing the proxy exists to survive.
|
|
299
|
+
// this process started, and counted ONLY on the client pipes. An error that reaches the process
|
|
300
|
+
// comes from a socket with no listener, and in this process those are the sockets to the DAEMON,
|
|
301
|
+
// which restarts routinely and is the thing the proxy exists to survive. Counting them made the
|
|
302
|
+
// proxy leave a healthy daemon, and the agent lost its tools.
|
|
297
303
|
let disconnects = 0;
|
|
298
304
|
let gone = false;
|
|
299
|
-
const absorb = (value) =>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
305
|
+
const absorb = (value) => absorbExpected(value, log, expected);
|
|
306
|
+
const onClientPipeError = (err) => {
|
|
307
|
+
if (!absorb(err)) {
|
|
308
|
+
log('reticle_mcp_proxy_client_pipe_error', { error: describe(err) });
|
|
309
|
+
onCrash(CrashKind.UNCAUGHT_EXCEPTION, err);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (true === expectedCode(err)?.unreachable) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
disconnects += 1;
|
|
316
|
+
if (disconnects >= DISCONNECT_STORM && !gone) {
|
|
317
|
+
gone = true;
|
|
318
|
+
log('reticle_mcp_proxy_client_gone', {
|
|
319
|
+
disconnects,
|
|
320
|
+
note: 'the client end is gone for good, not between reconnects — exiting instead of retrying forever',
|
|
321
|
+
});
|
|
322
|
+
onGone();
|
|
312
323
|
}
|
|
313
|
-
return true;
|
|
314
324
|
};
|
|
325
|
+
for (const pipe of clientPipes) {
|
|
326
|
+
pipe.on('error', onClientPipeError);
|
|
327
|
+
}
|
|
315
328
|
const noteUsableSession = () => {
|
|
316
329
|
// Not after `gone`: the process is already leaving, and a late frame must not look like a
|
|
317
330
|
// reason to stay. Nothing brings the proxy back from `onGone` by design.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type EscapeRequest, type EscapeResult } from './win32-escape.js';
|
|
1
2
|
export { MAX_DAEMON_LOG_BYTES, recoverOversizedLog } from './lifetime/log-cap.js';
|
|
2
3
|
/** Env override for the whole state directory — see ReticleEnv.STATE_DIR. */
|
|
3
4
|
export declare const STATE_DIR_ENV: "RETICLE_STATE_DIR";
|
|
@@ -72,8 +73,18 @@ export interface SpawnDaemonDeps {
|
|
|
72
73
|
spawnChild(command: string, args: readonly string[], options: {
|
|
73
74
|
detached: boolean;
|
|
74
75
|
stdio: readonly ('ignore' | number)[];
|
|
76
|
+
env?: Record<string, string | undefined>;
|
|
75
77
|
}): SpawnedChild;
|
|
76
78
|
pidAlive(pid: number): boolean;
|
|
79
|
+
/** `process.platform`. Optional so a test that does not care runs the detached start. */
|
|
80
|
+
readonly platform?: string;
|
|
81
|
+
/** Windows only: start the daemon outside the Job Object of this process. See win32-escape.ts. */
|
|
82
|
+
readonly escapeJob?: (request: EscapeRequest) => EscapeResult;
|
|
83
|
+
/**
|
|
84
|
+
* A predecessor died without a clean stop; `ageMs` is how long it had run. Optional so a test that
|
|
85
|
+
* does not care reports nothing. The default sends `daemon_died_unclean`.
|
|
86
|
+
*/
|
|
87
|
+
onUncleanDeath?(ageMs: number): void;
|
|
77
88
|
}
|
|
78
89
|
/**
|
|
79
90
|
* Spawn the reticle daemon as a detached background process, redirecting output to the log file.
|
|
@@ -2,10 +2,12 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { mkdirSync, readFileSync, writeFileSync, existsSync, unlinkSync, openSync, closeSync, readdirSync, statSync, renameSync, writeSync, } from 'node:fs';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { daemonRegistryFileName, ReticleEnv } from '@reticlehq/core';
|
|
5
|
+
import { daemonRegistryFileName, DaemonSpawnMethod, ReticleEnv, } from '@reticlehq/core';
|
|
6
6
|
import { log } from '../../log.js';
|
|
7
7
|
import { uncleanPredecessor } from './lifetime/unclean-predecessor.js';
|
|
8
|
+
import { reportDaemonDiedUnclean } from '../../telemetry/proxy-telemetry.js';
|
|
8
9
|
import { MAX_DAEMON_LOG_BYTES, pruneOldDaemonLogs } from './lifetime/log-cap.js';
|
|
10
|
+
import { escapeJobViaWmi } from './win32-escape.js';
|
|
9
11
|
// Re-exported from their owner so callers outside this directory — the proxy log, the rotation
|
|
10
12
|
// tests — keep reaching `daemon` rather than `lifetime`, which is a sink. See lifetime/log-cap.ts.
|
|
11
13
|
export { MAX_DAEMON_LOG_BYTES, recoverOversizedLog } from './lifetime/log-cap.js';
|
|
@@ -196,10 +198,48 @@ function defaultSpawnDaemonDeps() {
|
|
|
196
198
|
closeFile: closeSync,
|
|
197
199
|
fileSize: (path) => (existsSync(path) ? statSync(path).size : 0),
|
|
198
200
|
renameFile: renameSync,
|
|
199
|
-
spawnChild: (command, args, options) => spawn(command, [...args], {
|
|
201
|
+
spawnChild: (command, args, options) => spawn(command, [...args], {
|
|
202
|
+
detached: options.detached,
|
|
203
|
+
stdio: [...options.stdio],
|
|
204
|
+
...(options.env === undefined ? {} : { env: options.env }),
|
|
205
|
+
}),
|
|
200
206
|
pidAlive: isAlive,
|
|
207
|
+
platform: process.platform,
|
|
208
|
+
escapeJob: escapeJobViaWmi,
|
|
209
|
+
onUncleanDeath: (ageMs) => void reportDaemonDiedUnclean(ageMs),
|
|
201
210
|
};
|
|
202
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Report a dead predecessor, aged from its pidfile: the parent writes that file at spawn time.
|
|
214
|
+
* Read before the unlink, because after it nothing is left to age. A report must never stop a start.
|
|
215
|
+
*/
|
|
216
|
+
function reportUncleanAge(pidFilePath, deps) {
|
|
217
|
+
try {
|
|
218
|
+
// The clock is read at this I/O boundary, the same as the log timestamps in this file.
|
|
219
|
+
deps.onUncleanDeath?.(Math.max(0, Date.now() - statSync(pidFilePath).mtimeMs));
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// An unreadable pidfile has no age to report.
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const WINDOWS = 'win32';
|
|
226
|
+
/** The WMI start must never stop a daemon start: a throw reads as "nothing launched". */
|
|
227
|
+
function tryEscapeJob(escape, request) {
|
|
228
|
+
try {
|
|
229
|
+
return escape(request);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return { launched: false };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function removeQuietly(path) {
|
|
236
|
+
try {
|
|
237
|
+
unlinkSync(path);
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
// racing another reclaimer — fine
|
|
241
|
+
}
|
|
242
|
+
}
|
|
203
243
|
/**
|
|
204
244
|
* Spawn the reticle daemon as a detached background process, redirecting output to the log file.
|
|
205
245
|
* Writes the PID file from the parent before returning so callers can call isRunning
|
|
@@ -242,6 +282,9 @@ export function spawnDaemon(nodeExec, scriptPath, args, port, deps = defaultSpaw
|
|
|
242
282
|
// a crashed daemon is reclaimed"), it just never said it out loud. Captured before the unlink,
|
|
243
283
|
// because after it there is no trace left that the previous daemon died at all.
|
|
244
284
|
orphanPid = uncleanPredecessor(existing, false, process.pid);
|
|
285
|
+
if (null !== orphanPid) {
|
|
286
|
+
reportUncleanAge(pidFilePath, deps);
|
|
287
|
+
}
|
|
245
288
|
try {
|
|
246
289
|
unlinkSync(pidFilePath);
|
|
247
290
|
lockFd = deps.openFile(pidFilePath, 'wx');
|
|
@@ -289,11 +332,36 @@ export function spawnDaemon(nodeExec, scriptPath, args, port, deps = defaultSpaw
|
|
|
289
332
|
// A log we cannot write is not a reason to refuse to start the daemon.
|
|
290
333
|
}
|
|
291
334
|
}
|
|
335
|
+
let method = DaemonSpawnMethod.DETACHED;
|
|
336
|
+
if (WINDOWS === deps.platform && deps.escapeJob !== undefined) {
|
|
337
|
+
const escaped = tryEscapeJob(deps.escapeJob, {
|
|
338
|
+
command: nodeExec,
|
|
339
|
+
args: [scriptPath, ...args],
|
|
340
|
+
cwd: process.cwd(),
|
|
341
|
+
env: { ...process.env, [ReticleEnv.SPAWN_METHOD]: DaemonSpawnMethod.WMI },
|
|
342
|
+
logPath: logFilePath,
|
|
343
|
+
specPath: join(deps.home, `daemon-${port}.spawn.json`),
|
|
344
|
+
});
|
|
345
|
+
if (escaped.launched) {
|
|
346
|
+
deps.closeFile(logFd);
|
|
347
|
+
// No pid in time: release the lock, so the pidfile never names a process that is not ours.
|
|
348
|
+
if (escaped.pid === undefined) {
|
|
349
|
+
deps.closeFile(lockFd);
|
|
350
|
+
removeQuietly(pidFilePath);
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
writeFileSync(lockFd, String(escaped.pid), 'utf8');
|
|
354
|
+
deps.closeFile(lockFd);
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
method = DaemonSpawnMethod.WMI_FALLBACK;
|
|
358
|
+
}
|
|
292
359
|
let child;
|
|
293
360
|
try {
|
|
294
361
|
child = deps.spawnChild(nodeExec, [scriptPath, ...args], {
|
|
295
362
|
detached: true,
|
|
296
363
|
stdio: ['ignore', logFd, logFd],
|
|
364
|
+
env: { ...process.env, [ReticleEnv.SPAWN_METHOD]: method },
|
|
297
365
|
});
|
|
298
366
|
}
|
|
299
367
|
catch {
|
|
@@ -27,6 +27,8 @@ export declare function everServedToolCall(): boolean;
|
|
|
27
27
|
*/
|
|
28
28
|
export declare const SETUP_HOLD_MS = 60000;
|
|
29
29
|
export declare function noteSetupHold(now: number): void;
|
|
30
|
+
/** Call on each attach and each detach of an agent. */
|
|
31
|
+
export declare function noteAgentPresence(now: number): void;
|
|
30
32
|
/** Tests only. */
|
|
31
33
|
export declare function resetDaemonUsefulness(): void;
|
|
32
34
|
interface UsefulnessFacts {
|