@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { type ToolDeps } from '../tools/tools.js';
|
|
4
5
|
import type { ToolDef } from '../tools/tools.js';
|
|
@@ -17,7 +18,7 @@ export declare const CALL_TOOL_METHOD = "tools/call";
|
|
|
17
18
|
* tool with a strict schema (`reticle_context`) failed the whole call on a validating client the
|
|
18
19
|
* first time one of them rode on it.
|
|
19
20
|
*/
|
|
20
|
-
export declare function withSessionEnvelope(_name: string, outputSchema: z.
|
|
21
|
+
export declare function withSessionEnvelope(_name: string, outputSchema: Record<string, z.ZodType> | undefined): Record<string, z.ZodType> | undefined;
|
|
21
22
|
/**
|
|
22
23
|
* Abbreviations whose internal period is NOT a sentence end.
|
|
23
24
|
*
|
|
@@ -92,10 +93,26 @@ tools?: readonly ToolDef[]): ToolDef[];
|
|
|
92
93
|
*/
|
|
93
94
|
export declare function advertisedConfig(tool: ToolDef, advertised: readonly ToolDef[], profile: ToolSurface): {
|
|
94
95
|
description: string;
|
|
95
|
-
inputSchema: z.
|
|
96
|
-
outputSchema?: z.
|
|
96
|
+
inputSchema: Record<string, z.ZodType>;
|
|
97
|
+
outputSchema?: Record<string, z.ZodType>;
|
|
98
|
+
annotations: ToolAnnotations;
|
|
97
99
|
};
|
|
98
100
|
export declare function installFriendlyArgErrors(server: McpServer, examples: Map<string, string>, allowedKeys?: Map<string, ReadonlySet<string>>): void;
|
|
101
|
+
/**
|
|
102
|
+
* The daemon's factory for one MCP server for each attach. A proxy's id is its agent identity, so a
|
|
103
|
+
* reconnect from the same proxy is the same owner and keeps its leases. An attach with no proxy id
|
|
104
|
+
* gets a new random id from `createMcpServer`.
|
|
105
|
+
*
|
|
106
|
+
* `instructions` is read for each attach, not once: a project that gets wired while the daemon is
|
|
107
|
+
* alive must stop being told to wire itself on the next agent that connects.
|
|
108
|
+
*/
|
|
109
|
+
export declare function mcpServerFactoryFor(deps: ToolDeps, profile: ToolSurface, instructions: () => boolean | Omit<InstructionState, 'advertised'>): (peerSkew?: string, proxyId?: string, agentSession?: string) => McpServer;
|
|
110
|
+
/**
|
|
111
|
+
* The `tools/list` result this server serves on `surface`, built with no daemon and no deps. The
|
|
112
|
+
* proxy answers with it while its daemon starts. The same table, the same per-tool config and the
|
|
113
|
+
* same SDK conversion make it the same list; `proxy-cold-start.test.ts` holds the two equal.
|
|
114
|
+
*/
|
|
115
|
+
export declare function staticToolCatalog(surface: ToolSurface): Promise<unknown[]>;
|
|
99
116
|
export declare function createMcpServer(rawDeps: ToolDeps, profile?: ToolSurface,
|
|
100
117
|
/**
|
|
101
118
|
* Has an app ever connected for this project? Decides what the instructions LEAD with.
|
package/dist/surface/mcp/mcp.js
CHANGED
|
@@ -5,14 +5,14 @@ import { z } from 'zod';
|
|
|
5
5
|
import { isPredicateParam } from '@reticlehq/engine/question/predicate/predicate-eval.js';
|
|
6
6
|
import { isToonable, resultToToon, MCP_SERVER_NAME } from '@reticlehq/core';
|
|
7
7
|
import { tableForSurface } from '../tools/tools.js';
|
|
8
|
-
import { filterTools, describeToolSurface, TOOL_SURFACE, } from '../tools/tool-surface.js';
|
|
8
|
+
import { filterTools, describeToolSurface, TOOL_SURFACE, toolAnnotations, } from '../tools/tool-surface.js';
|
|
9
9
|
import { ReticleTool } from '@reticlehq/core';
|
|
10
|
-
import {
|
|
11
|
-
import { buildDynamicTools } from '../tools/dynamic-tools.js';
|
|
10
|
+
import { sharedParamShort } from './shared-params.js';
|
|
11
|
+
import { buildDynamicTools, descriptionOf } from '../tools/dynamic-tools.js';
|
|
12
12
|
import { runTool } from '../tools/invoke-tool.js';
|
|
13
13
|
import { sessionEnvelopeShape, newSnapshotCache } from '../tools/tool-kit.js';
|
|
14
|
-
import { buildErrorPayload } from '../tools/error-recovery.js';
|
|
15
|
-
import { takeVersionSkewOnto } from '../../command/version/version-nudge.js';
|
|
14
|
+
import { buildErrorPayload, codedRefusalOf } from '../tools/error-recovery.js';
|
|
15
|
+
import { connectionSkew, takeVersionSkewOnto } from '../../command/version/version-nudge.js';
|
|
16
16
|
import { resultIsError } from './faults/mcp-is-error.js';
|
|
17
17
|
import { consumerVerdictRefusal, reservedVerdictKeysIn } from './consumer-verdict-guard.js';
|
|
18
18
|
import { buildServerInstructions } from './server-instructions.js';
|
|
@@ -38,7 +38,7 @@ import { SERVER_VERSION } from '../../command/version/identity/server-version.js
|
|
|
38
38
|
import { setMcpClientNameHook } from '../../telemetry/feedback-context.js';
|
|
39
39
|
import { getSessionMetrics } from '../../telemetry/session-metrics.js';
|
|
40
40
|
import { reportMcpConnected } from '../../telemetry/mcp-connection.js';
|
|
41
|
-
import { noteAgentAttached, noteAgentDetached, runAsAgent } from '../../hooks/coding-agents.js';
|
|
41
|
+
import { noteAgentAttached, noteAgentDetached, noteAgentSession, runAsAgent, } from '../../hooks/coding-agents.js';
|
|
42
42
|
import { parsePredicate } from '@reticlehq/engine/question/predicate/predicate-parse.js';
|
|
43
43
|
/**
|
|
44
44
|
* Merge the runtime-spliced envelope into a tool's declared outputSchema so structuredContent
|
|
@@ -51,8 +51,9 @@ import { parsePredicate } from '@reticlehq/engine/question/predicate/predicate-p
|
|
|
51
51
|
* first time one of them rode on it.
|
|
52
52
|
*/
|
|
53
53
|
export function withSessionEnvelope(_name, outputSchema) {
|
|
54
|
-
if (outputSchema === undefined)
|
|
54
|
+
if (outputSchema === undefined) {
|
|
55
55
|
return outputSchema;
|
|
56
|
+
}
|
|
56
57
|
return { ...sessionEnvelopeShape, ...outputSchema };
|
|
57
58
|
}
|
|
58
59
|
const SERVER_INFO = { name: MCP_SERVER_NAME, version: SERVER_VERSION };
|
|
@@ -124,7 +125,7 @@ const PREDICATE_KINDS = 'Predicate object: { kind, ...fields }. kind is one of e
|
|
|
124
125
|
* paying for six.
|
|
125
126
|
*/
|
|
126
127
|
const PREDICATE_FIELD_GRAMMAR_HINT = ' Bug-catching options: net.count (exact request count — catches double-submit), ' +
|
|
127
|
-
'net.bodyMatches (a JSON field match on what the SERVER answered
|
|
128
|
+
'net.bodyMatches (a JSON field match on what the SERVER answered, keyed by the field: { status: "completed" }, never { field, value }; a dotted key reaches a nested field; ' +
|
|
128
129
|
'because a substring of the body matches key names too), net.requestBodyMatches (the same on what ' +
|
|
129
130
|
'the UI SENT — the verdict for "applying this filter actually puts it in the payload"), ' +
|
|
130
131
|
'compare (page text vs server answer, no expected value: { left:{from:"text",scope}, ' +
|
|
@@ -149,16 +150,14 @@ function leanZodShape(shape, advertised, predicateAnchor) {
|
|
|
149
150
|
for (const [key, schema] of Object.entries(shape)) {
|
|
150
151
|
if (isPredicateParam(schema)) {
|
|
151
152
|
// Every tool keeps the kinds; only the navigation hint is anchored.
|
|
152
|
-
const compact = z
|
|
153
|
-
|
|
154
|
-
.describe(hintSpent ? PREDICATE_KINDS : COMPACT_PREDICATE_DESCRIPTION);
|
|
153
|
+
const compact = z.record(z.string(), z.unknown());
|
|
154
|
+
out[key] = (schema.isOptional() ? compact.optional() : compact).describe(hintSpent ? PREDICATE_KINDS : COMPACT_PREDICATE_DESCRIPTION);
|
|
155
155
|
hintSpent = true;
|
|
156
|
-
out[key] = schema.isOptional() ? compact.optional() : compact;
|
|
157
156
|
continue;
|
|
158
157
|
}
|
|
159
158
|
// Shared boilerplate is documented once in the initialize instructions instead of on every
|
|
160
159
|
// tool, every turn. `sessionId` alone was 2,076 bytes of identical text across 16 tools.
|
|
161
|
-
const shared =
|
|
160
|
+
const shared = sharedParamShort(key, schema);
|
|
162
161
|
if (shared !== undefined) {
|
|
163
162
|
// `.describe` returns the same schema with new prose, so optionality carries over. Re-wrapping
|
|
164
163
|
// in `.optional()` would nest one inside another and change the parameter's TYPE, which is a
|
|
@@ -168,7 +167,7 @@ function leanZodShape(shape, advertised, predicateAnchor) {
|
|
|
168
167
|
}
|
|
169
168
|
// Through the same rewrite as tool descriptions: a parameter that says "a ref from
|
|
170
169
|
// reticle_snapshot" names a tool this surface does not have.
|
|
171
|
-
const desc = schema
|
|
170
|
+
const desc = descriptionOf(schema);
|
|
172
171
|
out[key] =
|
|
173
172
|
'string' === typeof desc
|
|
174
173
|
? schema.describe(liveCallText(firstSentence(desc), advertised))
|
|
@@ -333,12 +332,14 @@ export function advertisedConfig(tool, advertised, profile) {
|
|
|
333
332
|
? leanZodShape(tool.inputSchema, new Set(advertised.map((each) => each.name)), tool.name === anchor ? undefined : anchor)
|
|
334
333
|
: tool.inputSchema,
|
|
335
334
|
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
335
|
+
annotations: toolAnnotations(tool.name),
|
|
336
336
|
};
|
|
337
337
|
}
|
|
338
338
|
/** Argument keys the tool never declared. Empty when the tool's shape is unknown to us. */
|
|
339
339
|
function unknownKeys(args, allowed) {
|
|
340
|
-
if (allowed === undefined || typeof args !== 'object' || null === args)
|
|
340
|
+
if (allowed === undefined || typeof args !== 'object' || null === args) {
|
|
341
341
|
return [];
|
|
342
|
+
}
|
|
342
343
|
return Object.keys(args).filter((key) => !allowed.has(key));
|
|
343
344
|
}
|
|
344
345
|
export function installFriendlyArgErrors(server, examples, allowedKeys = new Map()) {
|
|
@@ -404,12 +405,14 @@ export function installFriendlyArgErrors(server, examples, allowedKeys = new Map
|
|
|
404
405
|
* the predicate is not what broke.
|
|
405
406
|
*/
|
|
406
407
|
function predicateMessage(args) {
|
|
407
|
-
if (typeof args !== 'object' || null === args)
|
|
408
|
+
if (typeof args !== 'object' || null === args) {
|
|
408
409
|
return undefined;
|
|
410
|
+
}
|
|
409
411
|
const record = args;
|
|
410
412
|
const predicate = record['predicate'] ?? record['until'];
|
|
411
|
-
if (predicate === undefined)
|
|
413
|
+
if (predicate === undefined) {
|
|
412
414
|
return undefined;
|
|
415
|
+
}
|
|
413
416
|
try {
|
|
414
417
|
parsePredicate(predicate);
|
|
415
418
|
return undefined;
|
|
@@ -454,8 +457,9 @@ function installUnadvertisedToolHelp(server, advertised, known) {
|
|
|
454
457
|
const help = name === undefined ? undefined : unadvertisedToolHelp(name, advertised, known);
|
|
455
458
|
// A failed RESULT, not a JSON-RPC error: a client that reads only `result` rendered the error
|
|
456
459
|
// as an empty success, and the agent believed `reticle_flow_save` had saved its flow.
|
|
457
|
-
if (help !== undefined)
|
|
460
|
+
if (help !== undefined) {
|
|
458
461
|
return { content: [{ type: 'text', text: help }], isError: true };
|
|
462
|
+
}
|
|
459
463
|
return original(request, extra);
|
|
460
464
|
});
|
|
461
465
|
}
|
|
@@ -477,14 +481,32 @@ function asCallOf(request, tool, action) {
|
|
|
477
481
|
}
|
|
478
482
|
/** The requested tool name, when the request has the shape we expect. */
|
|
479
483
|
function toolNameOf(request) {
|
|
480
|
-
if (typeof request !== 'object' || null === request)
|
|
484
|
+
if (typeof request !== 'object' || null === request) {
|
|
481
485
|
return undefined;
|
|
486
|
+
}
|
|
482
487
|
const params = request.params;
|
|
483
|
-
if (typeof params !== 'object' || null === params)
|
|
488
|
+
if (typeof params !== 'object' || null === params) {
|
|
484
489
|
return undefined;
|
|
490
|
+
}
|
|
485
491
|
const name = params.name;
|
|
486
492
|
return 'string' === typeof name ? name : undefined;
|
|
487
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* The daemon's factory for one MCP server for each attach. A proxy's id is its agent identity, so a
|
|
496
|
+
* reconnect from the same proxy is the same owner and keeps its leases. An attach with no proxy id
|
|
497
|
+
* gets a new random id from `createMcpServer`.
|
|
498
|
+
*
|
|
499
|
+
* `instructions` is read for each attach, not once: a project that gets wired while the daemon is
|
|
500
|
+
* alive must stop being told to wire itself on the next agent that connects.
|
|
501
|
+
*/
|
|
502
|
+
export function mcpServerFactoryFor(deps, profile, instructions) {
|
|
503
|
+
return (peerSkew, proxyId, agentSession) => createMcpServer({
|
|
504
|
+
...deps,
|
|
505
|
+
...(peerSkew === undefined ? {} : { peerSkew: connectionSkew(peerSkew) }),
|
|
506
|
+
...(proxyId === undefined ? {} : { attachId: proxyId }),
|
|
507
|
+
...(agentSession === undefined ? {} : { agentSession }),
|
|
508
|
+
}, profile, instructions());
|
|
509
|
+
}
|
|
488
510
|
/**
|
|
489
511
|
* The tools this codebase ships. Anything outside it came from a host via the `tools` parameter, and
|
|
490
512
|
* is held to the verdict rule below.
|
|
@@ -500,6 +522,35 @@ function toolNameOf(request) {
|
|
|
500
522
|
* covered the day it is added instead of the day somebody notices.
|
|
501
523
|
*/
|
|
502
524
|
const FIRST_PARTY_TOOL_NAMES = new Set(Object.values(ReticleTool));
|
|
525
|
+
/**
|
|
526
|
+
* The `tools/list` result this server serves on `surface`, built with no daemon and no deps. The
|
|
527
|
+
* proxy answers with it while its daemon starts. The same table, the same per-tool config and the
|
|
528
|
+
* same SDK conversion make it the same list; `proxy-cold-start.test.ts` holds the two equal.
|
|
529
|
+
*/
|
|
530
|
+
export async function staticToolCatalog(surface) {
|
|
531
|
+
const [{ Client }, { InMemoryTransport }] = await Promise.all([
|
|
532
|
+
import('@modelcontextprotocol/sdk/client/index.js'),
|
|
533
|
+
import('@modelcontextprotocol/sdk/inMemory.js'),
|
|
534
|
+
]);
|
|
535
|
+
const server = new McpServer(SERVER_INFO);
|
|
536
|
+
const register = server.registerTool.bind(server);
|
|
537
|
+
const advertised = advertisedTools(surface);
|
|
538
|
+
for (const tool of advertised) {
|
|
539
|
+
// Never called: this server only lists.
|
|
540
|
+
register(tool.name, advertisedConfig(tool, advertised, surface), () => ({ content: [] }));
|
|
541
|
+
}
|
|
542
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
543
|
+
await server.connect(serverTransport);
|
|
544
|
+
const client = new Client(SERVER_INFO);
|
|
545
|
+
await client.connect(clientTransport);
|
|
546
|
+
try {
|
|
547
|
+
return (await client.listTools()).tools;
|
|
548
|
+
}
|
|
549
|
+
finally {
|
|
550
|
+
await client.close();
|
|
551
|
+
await server.close();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
503
554
|
export function createMcpServer(rawDeps, profile = TOOL_SURFACE.DEFAULT,
|
|
504
555
|
/**
|
|
505
556
|
* Has an app ever connected for this project? Decides what the instructions LEAD with.
|
|
@@ -530,6 +581,12 @@ tools = tableForSurface(profile)) {
|
|
|
530
581
|
* store keeps it.
|
|
531
582
|
*/
|
|
532
583
|
const deps = { snapshots: newSnapshotCache(), attachId: randomUUID(), ...rawDeps };
|
|
584
|
+
// A proxy that reconnects brings its own id as `attachId`, so this is the same owner coming back:
|
|
585
|
+
// take its leases out of their grace. A new random id owns no leases, so this does nothing.
|
|
586
|
+
deps.pool?.reattachOwner(deps.attachId);
|
|
587
|
+
if (deps.agentSession !== undefined) {
|
|
588
|
+
noteAgentSession(deps.attachId, deps.agentSession);
|
|
589
|
+
}
|
|
533
590
|
const encoding = (process.env[ENCODING_ENV] ?? '').toLowerCase();
|
|
534
591
|
// Which surface this daemon advertises. The 18-tool default and the 48-tool full surface are
|
|
535
592
|
// different products from inside an agent's context, so outcomes are only comparable when the
|
|
@@ -558,8 +615,9 @@ tools = tableForSurface(profile)) {
|
|
|
558
615
|
*/
|
|
559
616
|
server.server.oninitialized = () => {
|
|
560
617
|
const info = server.server.getClientVersion();
|
|
561
|
-
if (info?.name === undefined)
|
|
618
|
+
if (info?.name === undefined) {
|
|
562
619
|
return;
|
|
620
|
+
}
|
|
563
621
|
getSessionMetrics().recordClient(info.name, info.version);
|
|
564
622
|
// So the HUD can say which agent is connected, and credit a note to the one that read it.
|
|
565
623
|
noteAgentAttached(deps.attachId, info.name);
|
|
@@ -567,9 +625,13 @@ tools = tableForSurface(profile)) {
|
|
|
567
625
|
// the first moment it can carry WHO. It used to fire from the SSE `connect()` resolution, which
|
|
568
626
|
// happens before the handshake — so every connect row was anonymous, and the field meant to say
|
|
569
627
|
// which agent client converts best was empty on every event ever sent.
|
|
570
|
-
void runAsAgent(deps.attachId, () => Promise.resolve(reportMcpConnected(info.name)));
|
|
628
|
+
void runAsAgent(deps.attachId, () => Promise.resolve(reportMcpConnected(info.name, deps.attachId)));
|
|
629
|
+
};
|
|
630
|
+
server.server.onclose = () => {
|
|
631
|
+
noteAgentDetached(deps.attachId);
|
|
632
|
+
// Start the leases' short grace. A reconnect from the same proxy takes them back (see above).
|
|
633
|
+
deps.pool?.detachOwner(deps.attachId);
|
|
571
634
|
};
|
|
572
|
-
server.server.onclose = () => noteAgentDetached(deps.attachId);
|
|
573
635
|
// Which agent is on the other end, for a feedback report. Registered as a lazy hook rather than
|
|
574
636
|
// read here: the handshake has not happened yet at construction time, and a report filed twenty
|
|
575
637
|
// tool calls later is exactly when we want the answer.
|
|
@@ -578,8 +640,9 @@ tools = tableForSurface(profile)) {
|
|
|
578
640
|
// Still records, because a client that skipped the handshake path above (a replayed initialize
|
|
579
641
|
// through the proxy, a transport that reconnects) is a client we would otherwise lose entirely.
|
|
580
642
|
// `recordClient` is idempotent per name.
|
|
581
|
-
if (info?.name !== undefined)
|
|
643
|
+
if (info?.name !== undefined) {
|
|
582
644
|
getSessionMetrics().recordClient(info.name, info.version);
|
|
645
|
+
}
|
|
583
646
|
return info === undefined ? undefined : { name: info.name, version: info.version };
|
|
584
647
|
});
|
|
585
648
|
// Cast once to our bridge type so every per-tool call site is typed without `any`.
|
|
@@ -706,7 +769,7 @@ tools = tableForSurface(profile)) {
|
|
|
706
769
|
content: [
|
|
707
770
|
{
|
|
708
771
|
type: 'text',
|
|
709
|
-
text: JSON.stringify(liveCallValues(takeVersionSkewOnto(buildErrorPayload(message), deps.peerSkew), advertisedNames)),
|
|
772
|
+
text: JSON.stringify(liveCallValues(takeVersionSkewOnto(buildErrorPayload(message, codedRefusalOf(error)), deps.peerSkew), advertisedNames)),
|
|
710
773
|
},
|
|
711
774
|
],
|
|
712
775
|
};
|
|
@@ -8,3 +8,47 @@ export declare function isHarnessDriveCall(msg: {
|
|
|
8
8
|
* call as if it had not run pays for a second drive.
|
|
9
9
|
*/
|
|
10
10
|
export declare const MSG_DRIVE_MAY_BE_RUNNING: string;
|
|
11
|
+
/**
|
|
12
|
+
* JSON-RPC requests forwarded on behalf of the client that have not been answered yet.
|
|
13
|
+
*
|
|
14
|
+
* Only used at shutdown, where it is the difference between "the client closed stdin" and "the client
|
|
15
|
+
* closed stdin and we dropped its `tools/list` on the floor while reporting exit 0". A request has an
|
|
16
|
+
* id AND a method; a notification has no id and is owed nothing; a response has an id and no method.
|
|
17
|
+
*/
|
|
18
|
+
export declare class PendingRequests {
|
|
19
|
+
#private;
|
|
20
|
+
observeOutbound(line: string): void;
|
|
21
|
+
/** Whether this unanswered request asked for a Harness drive. */
|
|
22
|
+
isDrive(id: unknown): boolean;
|
|
23
|
+
observeInbound(line: string): void;
|
|
24
|
+
get unanswered(): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Claim ONE unanswered id, so exactly one path may answer it. False means somebody already did —
|
|
27
|
+
* a duplicate response to one id is a protocol violation, not a belt-and-braces retry.
|
|
28
|
+
*/
|
|
29
|
+
take(id: unknown): boolean;
|
|
30
|
+
/** Take every unanswered id and forget them, so the same request is failed at most once. */
|
|
31
|
+
drain(): string[];
|
|
32
|
+
/** Forget which requests were drives, once their replies are written. */
|
|
33
|
+
clearDrives(): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* JSON-RPC errors for every request that was in flight when the stream died.
|
|
37
|
+
*
|
|
38
|
+
* These are unrecoverable BY CONSTRUCTION: the daemon builds a fresh MCP session per SSE
|
|
39
|
+
* connection, so the reconnected session has never seen them. Re-sending them is not an option
|
|
40
|
+
* either — a `reticle_act` that already clicked would click twice, and inventing a second action in
|
|
41
|
+
* somebody's app is worse than reporting a failure.
|
|
42
|
+
*
|
|
43
|
+
* So the proxy answers them itself, under each request's own id. Found by brute force: killing the
|
|
44
|
+
* daemon under a live client left 20 of 20 concurrent calls hanging until the client's own timeout,
|
|
45
|
+
* with the MCP server still perfectly alive. An agent cannot tell that apart from "still working".
|
|
46
|
+
*/
|
|
47
|
+
export declare function streamLossReplies(pending: PendingRequests, reason: string, nextStep?: string): string[];
|
|
48
|
+
/**
|
|
49
|
+
* The one shape every "the transport ate your call" answer takes, whichever leg died.
|
|
50
|
+
*
|
|
51
|
+
* `nextStep` is appended when the caller knows one. Without it this reply describes a condition and
|
|
52
|
+
* stops there, which is all a first-run user got out of their first ever tool call.
|
|
53
|
+
*/
|
|
54
|
+
export declare function transportLossReply(id: unknown, reason: string, nextStep?: string, drive?: boolean): string;
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The proxied calls still in flight, which of them are Harness drives, and what each one is told
|
|
3
|
+
* when the transport under it is lost.
|
|
4
|
+
*/
|
|
2
5
|
import { ReticleTool } from '@reticlehq/core';
|
|
6
|
+
import { parseLine as parseJsonRpc } from './proxy-handshake.js';
|
|
3
7
|
const TOOLS_CALL = 'tools/call';
|
|
4
8
|
const EXPLORE_ACTION = 'explore';
|
|
5
9
|
/** A `tools/call` of reticle_verify explore, directly or through the reticle_run hatch. */
|
|
6
10
|
export function isHarnessDriveCall(msg) {
|
|
7
|
-
if (TOOLS_CALL !== msg.method)
|
|
11
|
+
if (TOOLS_CALL !== msg.method) {
|
|
8
12
|
return false;
|
|
13
|
+
}
|
|
9
14
|
const params = (msg.params ?? {});
|
|
10
15
|
const args = (params.arguments ?? {});
|
|
11
|
-
if (ReticleTool.VERIFY_EXPLORE === params.name)
|
|
16
|
+
if (ReticleTool.VERIFY_EXPLORE === params.name) {
|
|
12
17
|
return true;
|
|
13
|
-
|
|
18
|
+
}
|
|
19
|
+
if (ReticleTool.VERIFY === params.name) {
|
|
14
20
|
return EXPLORE_ACTION === args['action'];
|
|
15
|
-
|
|
21
|
+
}
|
|
22
|
+
if (ReticleTool.RUN !== params.name) {
|
|
16
23
|
return false;
|
|
24
|
+
}
|
|
17
25
|
const inner = (args['args'] ?? {});
|
|
18
26
|
return (ReticleTool.VERIFY_EXPLORE === args['tool'] ||
|
|
19
27
|
(ReticleTool.VERIFY === args['tool'] && EXPLORE_ACTION === inner['action']));
|
|
@@ -25,3 +33,104 @@ export function isHarnessDriveCall(msg) {
|
|
|
25
33
|
export const MSG_DRIVE_MAY_BE_RUNNING = `The Harness drive this call started or polled may still be running in the daemon. Once ` +
|
|
26
34
|
`Reticle reconnects, call ${ReticleTool.VERIFY} {action:"explore"} with no runId to get the ` +
|
|
27
35
|
`running drive back (it will not start a second), then poll it by runId.`;
|
|
36
|
+
/**
|
|
37
|
+
* JSON-RPC requests forwarded on behalf of the client that have not been answered yet.
|
|
38
|
+
*
|
|
39
|
+
* Only used at shutdown, where it is the difference between "the client closed stdin" and "the client
|
|
40
|
+
* closed stdin and we dropped its `tools/list` on the floor while reporting exit 0". A request has an
|
|
41
|
+
* id AND a method; a notification has no id and is owed nothing; a response has an id and no method.
|
|
42
|
+
*/
|
|
43
|
+
export class PendingRequests {
|
|
44
|
+
#ids = new Set();
|
|
45
|
+
/** The pending requests that start or poll a Harness drive, which outlives a lost connection. */
|
|
46
|
+
#drives = new Set();
|
|
47
|
+
observeOutbound(line) {
|
|
48
|
+
const msg = parseJsonRpc(line);
|
|
49
|
+
if (null === msg || msg.id === undefined || msg.method === undefined) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const id = JSON.stringify(msg.id);
|
|
53
|
+
this.#ids.add(id);
|
|
54
|
+
if (isHarnessDriveCall(msg)) {
|
|
55
|
+
this.#drives.add(id);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Whether this unanswered request asked for a Harness drive. */
|
|
59
|
+
isDrive(id) {
|
|
60
|
+
return this.#drives.has(JSON.stringify(id));
|
|
61
|
+
}
|
|
62
|
+
observeInbound(line) {
|
|
63
|
+
const msg = parseJsonRpc(line);
|
|
64
|
+
if (null === msg || msg.id === undefined) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
this.#ids.delete(JSON.stringify(msg.id));
|
|
68
|
+
}
|
|
69
|
+
get unanswered() {
|
|
70
|
+
return [...this.#ids];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Claim ONE unanswered id, so exactly one path may answer it. False means somebody already did —
|
|
74
|
+
* a duplicate response to one id is a protocol violation, not a belt-and-braces retry.
|
|
75
|
+
*/
|
|
76
|
+
take(id) {
|
|
77
|
+
this.#drives.delete(JSON.stringify(id));
|
|
78
|
+
return this.#ids.delete(JSON.stringify(id));
|
|
79
|
+
}
|
|
80
|
+
/** Take every unanswered id and forget them, so the same request is failed at most once. */
|
|
81
|
+
drain() {
|
|
82
|
+
const ids = [...this.#ids];
|
|
83
|
+
this.#ids.clear();
|
|
84
|
+
return ids;
|
|
85
|
+
}
|
|
86
|
+
/** Forget which requests were drives, once their replies are written. */
|
|
87
|
+
clearDrives() {
|
|
88
|
+
this.#drives.clear();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* JSON-RPC errors for every request that was in flight when the stream died.
|
|
93
|
+
*
|
|
94
|
+
* These are unrecoverable BY CONSTRUCTION: the daemon builds a fresh MCP session per SSE
|
|
95
|
+
* connection, so the reconnected session has never seen them. Re-sending them is not an option
|
|
96
|
+
* either — a `reticle_act` that already clicked would click twice, and inventing a second action in
|
|
97
|
+
* somebody's app is worse than reporting a failure.
|
|
98
|
+
*
|
|
99
|
+
* So the proxy answers them itself, under each request's own id. Found by brute force: killing the
|
|
100
|
+
* daemon under a live client left 20 of 20 concurrent calls hanging until the client's own timeout,
|
|
101
|
+
* with the MCP server still perfectly alive. An agent cannot tell that apart from "still working".
|
|
102
|
+
*/
|
|
103
|
+
export function streamLossReplies(pending, reason, nextStep) {
|
|
104
|
+
const replies = pending.drain().map((id) => {
|
|
105
|
+
const parsed = JSON.parse(id);
|
|
106
|
+
return transportLossReply(parsed, reason, nextStep, pending.isDrive(parsed));
|
|
107
|
+
});
|
|
108
|
+
pending.clearDrives();
|
|
109
|
+
return replies;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* -32001: a server-defined error. Not -32603 (internal): nothing went wrong INSIDE the call, the
|
|
113
|
+
* transport under it went away, and the distinction is what tells a caller the action may be safe
|
|
114
|
+
* to retry.
|
|
115
|
+
*/
|
|
116
|
+
const TRANSPORT_LOSS_CODE = -32001;
|
|
117
|
+
/**
|
|
118
|
+
* The one shape every "the transport ate your call" answer takes, whichever leg died.
|
|
119
|
+
*
|
|
120
|
+
* `nextStep` is appended when the caller knows one. Without it this reply describes a condition and
|
|
121
|
+
* stops there, which is all a first-run user got out of their first ever tool call.
|
|
122
|
+
*/
|
|
123
|
+
export function transportLossReply(id, reason, nextStep, drive = false) {
|
|
124
|
+
return JSON.stringify({
|
|
125
|
+
jsonrpc: '2.0',
|
|
126
|
+
id,
|
|
127
|
+
error: {
|
|
128
|
+
code: TRANSPORT_LOSS_CODE,
|
|
129
|
+
message: (drive
|
|
130
|
+
? `the daemon connection dropped (${reason}) before this call was answered. ${MSG_DRIVE_MAY_BE_RUNNING}`
|
|
131
|
+
: `the daemon connection dropped (${reason}) before this call was answered — it did NOT ` +
|
|
132
|
+
'complete. Reticle is recovering the connection; retry if the action is safe to repeat.') +
|
|
133
|
+
(nextStep === undefined ? '' : ` ${nextStep}`),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daemon that `reticle mcp` starts beside the proxy. `ready` settles when the start is done,
|
|
3
|
+
* with the port to dial. `spawning` resolves when a new daemon process is started: the port was
|
|
4
|
+
* free, so no stranger holds it, and the proxy can answer the handshake at once with `tools()`,
|
|
5
|
+
* the daemon's own tool list built in this process.
|
|
6
|
+
*/
|
|
7
|
+
export interface ProxyBoot {
|
|
8
|
+
readonly ready: Promise<number | void>;
|
|
9
|
+
readonly spawning: Promise<void>;
|
|
10
|
+
readonly tools: () => Promise<unknown[]>;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -19,11 +19,17 @@ export declare function probeDaemon(port: number, host?: string): Promise<boolea
|
|
|
19
19
|
*
|
|
20
20
|
* Probes are injected (defaulting to the real pair) so the rule is testable without a socket, which
|
|
21
21
|
* is the same shape `probePresence`'s own callers use.
|
|
22
|
+
*
|
|
23
|
+
* `alive` says if the daemon process in the pid file still runs. When it does not, no daemon can
|
|
24
|
+
* come up, so the wait stops at once and does not poll to the deadline.
|
|
22
25
|
*/
|
|
23
26
|
export declare function waitForDaemon(port: number, probes?: {
|
|
24
27
|
tcpOpen: (port: number) => Promise<boolean>;
|
|
25
28
|
status: (port: number) => Promise<unknown>;
|
|
29
|
+
alive?: () => boolean;
|
|
26
30
|
}): Promise<void>;
|
|
31
|
+
/** True while the process in the daemon pid file for `port` runs. */
|
|
32
|
+
export declare function daemonProcessAlive(port: number): boolean;
|
|
27
33
|
/**
|
|
28
34
|
* Poll until `/status` on `port` answers as a Reticle daemon; false once the bound passes.
|
|
29
35
|
*
|
|
@@ -2,6 +2,7 @@ import * as net from 'node:net';
|
|
|
2
2
|
import { LOOPBACK_HOST } from '@reticlehq/core';
|
|
3
3
|
import { describePresence, presenceIsUsable, probePresence, PortPresence, } from '../../../command/daemon/binding/port-presence.js';
|
|
4
4
|
import { fetchStatus } from '../../../command/daemon/binding/daemon-status-probe.js';
|
|
5
|
+
import { isAlive, readPid } from '../../../command/daemon/daemon.js';
|
|
5
6
|
const DEFAULT_DAEMON_READY_TIMEOUT_MS = 10_000;
|
|
6
7
|
/**
|
|
7
8
|
* How long to wait for the spawned daemon's port to accept connections before giving up. The default
|
|
@@ -55,8 +56,11 @@ export function probeDaemon(port, host = LOOPBACK_HOST) {
|
|
|
55
56
|
*
|
|
56
57
|
* Probes are injected (defaulting to the real pair) so the rule is testable without a socket, which
|
|
57
58
|
* is the same shape `probePresence`'s own callers use.
|
|
59
|
+
*
|
|
60
|
+
* `alive` says if the daemon process in the pid file still runs. When it does not, no daemon can
|
|
61
|
+
* come up, so the wait stops at once and does not poll to the deadline.
|
|
58
62
|
*/
|
|
59
|
-
export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, status: fetchStatus }) {
|
|
63
|
+
export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, status: fetchStatus, alive: () => daemonProcessAlive(port) }) {
|
|
60
64
|
const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
|
|
61
65
|
let attempt = 0;
|
|
62
66
|
let presence = PortPresence.FREE;
|
|
@@ -64,6 +68,10 @@ export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, statu
|
|
|
64
68
|
presence = await probePresence(port, probes);
|
|
65
69
|
if (presenceIsUsable(presence))
|
|
66
70
|
return;
|
|
71
|
+
if (false === probes.alive?.()) {
|
|
72
|
+
throw new Error(`the reticle daemon on port ${String(port)} exited before it was ready — ` +
|
|
73
|
+
describePresence(presence, port));
|
|
74
|
+
}
|
|
67
75
|
attempt++;
|
|
68
76
|
await delay(daemonPollDelayMs(attempt));
|
|
69
77
|
}
|
|
@@ -73,6 +81,11 @@ export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, statu
|
|
|
73
81
|
throw new Error(`reticle daemon did not become ready on port ${String(port)} within ` +
|
|
74
82
|
`${String(DAEMON_READY_TIMEOUT_MS)}ms — ${describePresence(presence, port)}`);
|
|
75
83
|
}
|
|
84
|
+
/** True while the process in the daemon pid file for `port` runs. */
|
|
85
|
+
export function daemonProcessAlive(port) {
|
|
86
|
+
const pid = readPid(port);
|
|
87
|
+
return null !== pid && isAlive(pid);
|
|
88
|
+
}
|
|
76
89
|
/** How long a spawned daemon gets to bind before `serve`, `restart` or `init` gives up on it. */
|
|
77
90
|
const BIND_TIMEOUT_MS = 15_000;
|
|
78
91
|
const BIND_POLL_MS = 150;
|
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
* connected, initialized and toolless until a human notices.
|
|
9
9
|
*/
|
|
10
10
|
export declare const TOOLS_CHANGED_NOTIFICATION = "notifications/tools/list_changed";
|
|
11
|
+
interface JsonRpcLike {
|
|
12
|
+
id?: unknown;
|
|
13
|
+
method?: unknown;
|
|
14
|
+
params?: {
|
|
15
|
+
protocolVersion?: unknown;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** A line as a JSON-RPC object, or null when it is not JSON or not an object. */
|
|
19
|
+
export declare function parseLine(line: string): JsonRpcLike | null;
|
|
11
20
|
/** True when this queued client line is the handshake we answered ourselves. */
|
|
12
21
|
export declare function isHandshakeLine(line: string): boolean;
|
|
13
22
|
export declare function localInitializeResponse(line: string,
|
|
@@ -63,8 +72,11 @@ export declare function toolsChangedNotification(catalogWasLocal: boolean): stri
|
|
|
63
72
|
* on the initialize result, which SUCCEEDED, and every host we target puts it in front of the model.
|
|
64
73
|
* A tool description cannot reach an agent that was handed no tools.
|
|
65
74
|
*
|
|
66
|
-
* The healthy block is kept underneath
|
|
67
|
-
*
|
|
75
|
+
* The healthy block is kept underneath: an agent that IS about to get a working surface (the common
|
|
76
|
+
* case — the first call wakes a daemon) still needs to know how to drive it. Only its part up to the
|
|
77
|
+
* writing rule is kept. Claude Code shows about the first 4,096 characters, and the notice plus the
|
|
78
|
+
* whole block came to about 4,600, so the client cut the writing rule. The text says what it left
|
|
79
|
+
* out, so the cut is not silent.
|
|
68
80
|
*/
|
|
69
81
|
export declare function degradedInstructions(healthy: string, port: number, reason: string): string;
|
|
70
82
|
/**
|
|
@@ -105,4 +117,24 @@ interface DrainedLines {
|
|
|
105
117
|
* the concatenation alone. Only a chunk that actually carries a newline pays for a scan.
|
|
106
118
|
*/
|
|
107
119
|
export declare function drainLines(buffer: string, chunk: string, maxLineBytes?: number, discarding?: boolean): DrainedLines;
|
|
120
|
+
/** JSON-RPC id the proxy uses for its own replayed `initialize` — never one the client could send. */
|
|
121
|
+
export declare const RECONNECT_INITIALIZE_ID = "__reticle_proxy_reinit";
|
|
122
|
+
/**
|
|
123
|
+
* Makes a reconnect invisible to the client.
|
|
124
|
+
*
|
|
125
|
+
* The daemon builds a FRESH `McpServer` per SSE connection, so a reconnected session has never seen
|
|
126
|
+
* the client's `initialize` — every subsequent `tools/call` would hit an uninitialized server. The
|
|
127
|
+
* proxy therefore remembers the handshake the client sent once and replays it into each new session.
|
|
128
|
+
* The replay is re-issued under a reserved id so the daemon's response to it can be dropped rather
|
|
129
|
+
* than reaching the client's stdout, where a duplicate JSON-RPC id would be a protocol violation.
|
|
130
|
+
*/
|
|
131
|
+
export declare class HandshakeReplay {
|
|
132
|
+
#private;
|
|
133
|
+
/** Record one outbound client line. Cheap enough to call for every stdin message. */
|
|
134
|
+
observeOutbound(line: string): void;
|
|
135
|
+
/** Lines to POST into a freshly-established session, before any queued client traffic. */
|
|
136
|
+
replayLines(): string[];
|
|
137
|
+
/** True when this inbound daemon line is the echo of our replayed handshake and must not be forwarded. */
|
|
138
|
+
shouldSuppressInbound(line: string): boolean;
|
|
139
|
+
}
|
|
108
140
|
export {};
|