@reticlehq/server 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +13 -12
- package/dist/command/cli/adhoc-suite.js +2 -0
- package/dist/command/cli/adhoc-verdict.d.ts +2 -43
- package/dist/command/cli/adhoc-verdict.js +50 -187
- package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
- package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
- package/dist/command/cli/cli-doctor.js +1 -1
- package/dist/command/cli/cli-flow-commands.js +2 -9
- package/dist/command/cli/cli-kill.js +1 -1
- package/dist/command/cli/cli-parse-compare.d.ts +22 -0
- package/dist/command/cli/cli-parse-compare.js +75 -0
- package/dist/command/cli/cli-parse-grade.d.ts +36 -0
- package/dist/command/cli/cli-parse-grade.js +75 -0
- package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
- package/dist/command/cli/cli-parse-grammar.js +18 -20
- package/dist/command/cli/cli-parse-verify.d.ts +10 -0
- package/dist/command/cli/cli-parse-verify.js +63 -14
- package/dist/command/cli/cli-parse.d.ts +17 -15
- package/dist/command/cli/cli-parse.js +44 -27
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +29 -20
- package/dist/command/cli/cli-usage.js +58 -29
- package/dist/command/cli/cli-verify.d.ts +11 -2
- package/dist/command/cli/cli-verify.js +27 -8
- package/dist/command/cli/cloud-cli.js +2 -119
- package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
- package/dist/command/cli/daemon-link/daemon-client.js +205 -0
- package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor-repairs.js +1 -1
- package/dist/command/cli/ensure-daemon.d.ts +26 -0
- package/dist/command/cli/ensure-daemon.js +57 -0
- package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
- package/dist/command/cli/finished-run/grade-command.js +180 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +1 -1
- package/dist/command/cli/ports/port-holder.d.ts +9 -0
- package/dist/command/cli/ports/port-holder.js +27 -3
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +22 -0
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +7 -3
- package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
- package/dist/command/cli/two-builds/compare-command.js +314 -0
- package/dist/command/cli/verify-against.d.ts +61 -0
- package/dist/command/cli/verify-against.js +187 -0
- package/dist/command/cli/verify-no-session.d.ts +2 -0
- package/dist/command/cli/verify-no-session.js +13 -0
- package/dist/command/cli/verify-results.d.ts +35 -2
- package/dist/command/cli/verify-results.js +76 -10
- package/dist/command/cli.js +46 -41
- package/dist/command/daemon/daemon-resolve.d.ts +8 -1
- package/dist/command/daemon/daemon-resolve.js +27 -1
- package/dist/command/daemon/daemon.d.ts +12 -1
- package/dist/command/daemon/daemon.js +35 -4
- package/dist/command/daemon/lifetime/death-cause.js +21 -2
- package/dist/command/daemon/lifetime/heartbeat.js +7 -2
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
- package/dist/command/setup/bringup/dev-server-files.js +17 -0
- package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
- package/dist/command/setup/bringup/static-page-server.js +43 -9
- package/dist/command/setup/init/init-runtime.js +6 -0
- package/dist/command/setup/node-effects.d.ts +3 -2
- package/dist/command/setup/node-effects.js +9 -3
- package/dist/command/setup/remaining-steps.d.ts +8 -0
- package/dist/command/setup/remaining-steps.js +10 -0
- package/dist/command/setup/run-setup.d.ts +2 -0
- package/dist/command/setup/run-setup.js +9 -1
- package/dist/command/setup/setup-command.js +6 -3
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/version-skew.js +1 -1
- package/dist/features/compare/action-plan.d.ts +260 -0
- package/dist/features/compare/action-plan.js +543 -0
- package/dist/features/compare/behaviour-compare.d.ts +24 -0
- package/dist/features/compare/behaviour-compare.js +85 -0
- package/dist/features/compare/behaviour-diff.d.ts +97 -0
- package/dist/features/compare/behaviour-diff.js +202 -0
- package/dist/features/compare/behaviour-report.d.ts +40 -0
- package/dist/features/compare/behaviour-report.js +128 -0
- package/dist/features/compare/build-readings.d.ts +106 -0
- package/dist/features/compare/build-readings.js +250 -0
- package/dist/features/compare/compare-diff.d.ts +62 -0
- package/dist/features/compare/compare-diff.js +177 -0
- package/dist/features/compare/compare-report-html.d.ts +6 -0
- package/dist/features/compare/compare-report-html.js +102 -0
- package/dist/features/compare/compare-report.d.ts +15 -0
- package/dist/features/compare/compare-report.js +73 -0
- package/dist/features/compare/compare-run.d.ts +60 -0
- package/dist/features/compare/compare-run.js +123 -0
- package/dist/features/compare/outside/effect-lines.d.ts +26 -0
- package/dist/features/compare/outside/effect-lines.js +103 -0
- package/dist/features/compare/outside/state-sources.d.ts +76 -0
- package/dist/features/compare/outside/state-sources.js +164 -0
- package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
- package/dist/features/compare/outside/value-dictionary.js +227 -0
- package/dist/features/compare/visible-content.d.ts +20 -0
- package/dist/features/compare/visible-content.js +125 -0
- package/dist/features/crawl/crawl-empty.js +2 -1
- package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
- package/dist/features/crawl/crawl-exhaustive.js +138 -0
- package/dist/features/crawl/crawl-tools.d.ts +9 -2
- package/dist/features/crawl/crawl-tools.js +28 -12
- package/dist/features/crawl/crawl.d.ts +8 -9
- package/dist/features/crawl/crawl.js +61 -65
- package/dist/features/exhaust/app-coverage.d.ts +5 -1
- package/dist/features/exhaust/app-coverage.js +3 -1
- package/dist/features/exhaust/code-coverage.js +8 -4
- package/dist/features/exhaust/edge-cases.d.ts +133 -0
- package/dist/features/exhaust/edge-cases.js +310 -0
- package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
- package/dist/features/exhaust/edges/edge-drive.js +364 -0
- package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
- package/dist/features/exhaust/edges/edge-run.js +169 -0
- package/dist/features/exhaust/explorer.d.ts +87 -14
- package/dist/features/exhaust/explorer.js +159 -25
- package/dist/features/exhaust/failure-branches.d.ts +5 -0
- package/dist/features/exhaust/failure-branches.js +29 -11
- package/dist/features/exhaust/invariants.d.ts +112 -0
- package/dist/features/exhaust/invariants.js +188 -0
- package/dist/features/exhaust/ledger.d.ts +68 -0
- package/dist/features/exhaust/ledger.js +121 -13
- package/dist/features/exhaust/session-fold.d.ts +10 -0
- package/dist/features/exhaust/session-fold.js +46 -3
- package/dist/features/exhaust/session-port.d.ts +3 -1
- package/dist/features/exhaust/session-port.js +97 -28
- package/dist/features/grade/grade-answer.d.ts +99 -0
- package/dist/features/grade/grade-answer.js +112 -0
- package/dist/features/grade/grade-noise.d.ts +27 -0
- package/dist/features/grade/grade-noise.js +55 -0
- package/dist/features/grade/grade-renumber.d.ts +12 -0
- package/dist/features/grade/grade-renumber.js +86 -0
- package/dist/features/grade/grade-state.d.ts +50 -0
- package/dist/features/grade/grade-state.js +93 -0
- package/dist/features/grade/grade-suggest.d.ts +33 -0
- package/dist/features/grade/grade-suggest.js +98 -0
- package/dist/features/visual/visual-tools.js +66 -19
- package/dist/index.js +7 -4
- package/dist/judgement/capsule/causal-summary.js +9 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +16 -2
- package/dist/judgement/runs/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- package/dist/language/flows/affected-tools.d.ts +1 -1
- package/dist/language/flows/affected-tools.js +49 -30
- package/dist/language/flows/change/git-changed.d.ts +2 -1
- package/dist/language/flows/change/git-changed.js +19 -2
- package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
- package/dist/language/flows/fields/flow-secret-field.js +77 -21
- package/dist/language/flows/flow-anchor.d.ts +60 -2
- package/dist/language/flows/flow-anchor.js +125 -20
- package/dist/language/flows/flow-classify.d.ts +20 -0
- package/dist/language/flows/flow-classify.js +90 -12
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +25 -1
- package/dist/language/flows/flow-journey.d.ts +38 -4
- package/dist/language/flows/flow-journey.js +138 -8
- package/dist/language/flows/flow-replay-run.d.ts +0 -16
- package/dist/language/flows/flow-replay-run.js +76 -14
- package/dist/language/flows/flow-replay-types.d.ts +4 -2
- package/dist/language/flows/flow-replay.d.ts +19 -1
- package/dist/language/flows/flow-replay.js +169 -52
- package/dist/language/flows/flow-replayable.js +7 -1
- package/dist/language/flows/flow-start-path.d.ts +34 -5
- package/dist/language/flows/flow-start-path.js +202 -15
- package/dist/language/flows/flow-step-runners.d.ts +14 -13
- package/dist/language/flows/flow-step-runners.js +184 -109
- package/dist/language/flows/flow-store-for-session.d.ts +26 -25
- package/dist/language/flows/flow-store-for-session.js +44 -0
- package/dist/language/flows/flow-tools.js +46 -33
- package/dist/language/flows/flows.js +176 -76
- package/dist/language/flows/recording/tape/recordings.d.ts +19 -3
- package/dist/language/flows/recording/tape/recordings.js +71 -5
- package/dist/language/flows/replay.d.ts +19 -5
- package/dist/language/flows/replay.js +99 -7
- package/dist/language/flows/steps/page-steps.d.ts +68 -0
- package/dist/language/flows/steps/page-steps.js +198 -0
- package/dist/language/flows/suite/sequential-run.d.ts +68 -0
- package/dist/language/flows/suite/sequential-run.js +96 -0
- package/dist/language/flows/verify-change-tools.js +26 -15
- package/dist/memory/cloud/background-sync.d.ts +13 -0
- package/dist/memory/cloud/background-sync.js +24 -3
- package/dist/memory/cloud/sync-daemon.js +26 -27
- package/dist/memory/journal/attach-journal.js +2 -1
- package/dist/memory/journal/drive-flow.d.ts +7 -0
- package/dist/memory/journal/drive-flow.js +44 -13
- package/dist/memory/journal/journal-recorder.d.ts +4 -2
- package/dist/memory/journal/session-journal.d.ts +7 -0
- package/dist/memory/journal/session-journal.js +58 -10
- package/dist/memory/project/root-claims.d.ts +9 -0
- package/dist/memory/project/root-claims.js +9 -4
- package/dist/portal/input/download-capture.d.ts +7 -0
- package/dist/portal/input/download-capture.js +92 -0
- package/dist/portal/input/network-detail.d.ts +6 -0
- package/dist/portal/input/network-detail.js +12 -1
- package/dist/portal/input/network-mock-tools.js +49 -13
- package/dist/portal/input/network-mock.d.ts +8 -0
- package/dist/portal/input/network-mock.js +28 -9
- package/dist/portal/input/page-capture.d.ts +24 -0
- package/dist/portal/input/page-capture.js +33 -0
- package/dist/portal/input/real-input.d.ts +22 -20
- package/dist/portal/input/real-input.js +41 -31
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +75 -18
- package/dist/portal/pool/browser-pool.d.ts +11 -29
- package/dist/portal/pool/browser-pool.js +35 -17
- package/dist/portal/pool/context-permissions.d.ts +17 -3
- package/dist/portal/pool/context-permissions.js +36 -5
- package/dist/portal/pool/determinism.d.ts +52 -0
- package/dist/portal/pool/determinism.js +259 -0
- package/dist/portal/pool/lease-emulation.d.ts +34 -1
- package/dist/portal/pool/lease-emulation.js +18 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
- package/dist/portal/pool/lease-marker-stamp.js +15 -1
- package/dist/portal/pool/lease-page-error.d.ts +44 -17
- package/dist/portal/pool/lease-page-error.js +147 -5
- package/dist/portal/pool/page-error-journal.d.ts +7 -1
- package/dist/portal/pool/page-error-journal.js +27 -0
- package/dist/portal/pool/playwright-launcher.js +51 -8
- package/dist/portal/pool/pool-contract.d.ts +27 -1
- package/dist/portal/pool/pool-lease.d.ts +36 -1
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +23 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +7 -6
- package/dist/portal/session/dev-server/dev-command.js +61 -15
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
- package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
- package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
- package/dist/portal/session/dev-server/project-ports.js +62 -0
- package/dist/portal/session/facts/departed-network.d.ts +66 -0
- package/dist/portal/session/facts/departed-network.js +142 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
- package/dist/portal/session/no-session-diagnosis.js +67 -25
- package/dist/portal/session/no-session-next-action.d.ts +2 -0
- package/dist/portal/session/no-session-next-action.js +12 -3
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +38 -3
- package/dist/portal/session/serving-directory.d.ts +10 -0
- package/dist/portal/session/serving-directory.js +39 -8
- package/dist/portal/session/session-manager.d.ts +10 -0
- package/dist/portal/session/session-manager.js +174 -63
- package/dist/start-wiring.d.ts +2 -1
- package/dist/start-wiring.js +2 -1
- package/dist/surface/mcp/mcp.js +3 -2
- package/dist/surface/mcp/shared-params.d.ts +2 -0
- package/dist/surface/mcp/shared-params.js +31 -0
- package/dist/surface/tools/act/act-observation.d.ts +40 -2
- package/dist/surface/tools/act/act-observation.js +118 -12
- package/dist/surface/tools/act/act-preflight.js +1 -1
- package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
- package/dist/surface/tools/act/act-sequence-retry.js +61 -5
- package/dist/surface/tools/act/after-match.d.ts +4 -1
- package/dist/surface/tools/act/after-match.js +8 -1
- package/dist/surface/tools/act/already-true.js +1 -1
- package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
- package/dist/surface/tools/act/navigation/reload-result.js +25 -2
- package/dist/surface/tools/act/resolve-target.d.ts +15 -1
- package/dist/surface/tools/act/resolve-target.js +24 -1
- package/dist/surface/tools/act/sequence-grade.d.ts +12 -0
- package/dist/surface/tools/act/sequence-grade.js +37 -26
- package/dist/surface/tools/act/settle-in-flight.js +27 -15
- package/dist/surface/tools/act-command.d.ts +23 -0
- package/dist/surface/tools/act-command.js +50 -0
- package/dist/surface/tools/act-sequence-tool.js +78 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +74 -86
- package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
- package/dist/surface/tools/agent/build-staleness.js +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +16 -37
- package/dist/surface/tools/browser-tools.js +12 -6
- package/dist/surface/tools/compare-tools.d.ts +11 -0
- package/dist/surface/tools/compare-tools.js +94 -0
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.js +9 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +91 -21
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +3 -2
- package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
- package/dist/surface/tools/lease-coverage-tools.js +128 -0
- package/dist/surface/tools/lease-hint.d.ts +35 -1
- package/dist/surface/tools/lease-hint.js +42 -0
- package/dist/surface/tools/lease-readiness.d.ts +26 -0
- package/dist/surface/tools/lease-readiness.js +79 -0
- package/dist/surface/tools/lease-seed.d.ts +14 -1
- package/dist/surface/tools/lease-seed.js +35 -3
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +62 -110
- package/dist/surface/tools/lease-url-identity.d.ts +46 -0
- package/dist/surface/tools/lease-url-identity.js +97 -0
- package/dist/surface/tools/merge-tools.js +99 -2
- package/dist/surface/tools/merged-name-redirect.js +5 -1
- package/dist/surface/tools/observe-tools.js +15 -9
- package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
- package/dist/surface/tools/read/console-pre-attach.js +23 -0
- package/dist/surface/tools/read-tools.js +33 -30
- package/dist/surface/tools/real-input-attempt.d.ts +0 -8
- package/dist/surface/tools/real-input-attempt.js +143 -28
- package/dist/surface/tools/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +6 -0
- package/dist/surface/tools/tool-kit.js +2 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +28 -5
- package/dist/surface/tools/type-spelling.d.ts +13 -0
- package/dist/surface/tools/type-spelling.js +36 -0
- package/dist/telemetry/agent-client.d.ts +6 -0
- package/dist/telemetry/agent-client.js +10 -0
- package/dist/telemetry/argument-shape.js +1 -0
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +8 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/capabilities.mdx +61 -1
- package/docs/cli/affected.mdx +9 -7
- package/docs/cli/cloud.mdx +3 -3
- package/docs/cli/compare.mdx +94 -0
- package/docs/cli/doctor.mdx +1 -1
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/license.mdx +9 -7
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/update.mdx +2 -2
- package/docs/cli/verify.mdx +23 -1
- package/docs/cli.mdx +33 -23
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +2 -2
- package/docs/environment.mdx +1 -1
- package/docs/flows.md +11 -1
- package/docs/frameworks.mdx +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/predicates.mdx +1 -1
- package/docs/quickstart.mdx +68 -49
- package/docs/skill-setup.md +1 -1
- package/docs/skill.md +14 -13
- package/docs/telemetry.md +2 -2
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +1 -1
- package/docs/usage.md +17 -14
- package/package.json +13 -13
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -6,6 +6,7 @@ import { AttachmentHistory } from './presence/attachment-history.js';
|
|
|
6
6
|
import { claimedSessionOf, openedBeforeDeparture, pickDocumentSuccessor, } from './session-successor.js';
|
|
7
7
|
import { SESSION_DISCONNECTED_REASON, SESSION_PARKED_REASON } from './facts/session-replaced.js';
|
|
8
8
|
import { isErrorDocument } from './session-info.js';
|
|
9
|
+
import { carryOnNewDocument, departedNetworkOf, } from './facts/departed-network.js';
|
|
9
10
|
/** Core's `originOf`, re-exported for this module's existing importers. */
|
|
10
11
|
export { originOf };
|
|
11
12
|
/**
|
|
@@ -37,18 +38,10 @@ function scopeSessions(sessions, scope) {
|
|
|
37
38
|
* target it: both facts are already in hand here.
|
|
38
39
|
*/
|
|
39
40
|
export function scopeMissError(connected, scope) {
|
|
40
|
-
const who = scope
|
|
41
|
-
? `project '${scope.projectId}'`
|
|
42
|
-
: scope?.url !== undefined
|
|
43
|
-
? `your app at ${scope.url}`
|
|
44
|
-
: 'the active project';
|
|
41
|
+
const who = scopeName(scope);
|
|
45
42
|
const anyUntagged = connected.some((s) => s.projectId === undefined);
|
|
46
43
|
const anyTagged = connected.some((s) => s.projectId !== undefined);
|
|
47
|
-
const how = anyUntagged
|
|
48
|
-
? 'with no projectId'
|
|
49
|
-
: anyUntagged && anyTagged
|
|
50
|
-
? 'under a different project or with no projectId'
|
|
51
|
-
: 'under a different project';
|
|
44
|
+
const how = whereConnected(anyUntagged, anyTagged);
|
|
52
45
|
const listed = connected
|
|
53
46
|
.map((s) => s.projectId !== undefined
|
|
54
47
|
? `'${s.projectId}' (${s.url}, sessionId '${s.id}')`
|
|
@@ -62,6 +55,48 @@ export function scopeMissError(connected, scope) {
|
|
|
62
55
|
`${listed}. The daemon scopes to the .reticle.json of the directory it was started in, so ` +
|
|
63
56
|
`this is a scope mismatch, not a dead app. ${advice}`);
|
|
64
57
|
}
|
|
58
|
+
/** What the scope names, in the words of the refusal. */
|
|
59
|
+
function scopeName(scope) {
|
|
60
|
+
if (scope?.projectId !== undefined) {
|
|
61
|
+
return `project '${scope.projectId}'`;
|
|
62
|
+
}
|
|
63
|
+
if (scope?.url !== undefined) {
|
|
64
|
+
return `your app at ${scope.url}`;
|
|
65
|
+
}
|
|
66
|
+
return 'the active project';
|
|
67
|
+
}
|
|
68
|
+
/** Where the connected sessions are, given which of them carry a projectId. */
|
|
69
|
+
function whereConnected(anyUntagged, anyTagged) {
|
|
70
|
+
if (!anyUntagged) {
|
|
71
|
+
return 'under a different project';
|
|
72
|
+
}
|
|
73
|
+
if (!anyTagged) {
|
|
74
|
+
return 'with no projectId';
|
|
75
|
+
}
|
|
76
|
+
return 'under a different project or with no projectId';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A session's rank for auto-selection: lower is better.
|
|
80
|
+
*
|
|
81
|
+
* 0 = non-throttled (visible + recently-heard), 1 = throttled (hidden or stale heartbeat),
|
|
82
|
+
* 2 = an error document, 3 = attached but not answering commands. A wedged tab keeps streaming
|
|
83
|
+
* events, so recency RATES IT HIGHEST — it was beating a healthy sibling on the one signal scoring
|
|
84
|
+
* looked at, and every call auto-targeted the one tab guaranteed to time out. An error document (a
|
|
85
|
+
* dev server's 404 that still mounted the SDK) ranks below any working page, hidden or not: it is
|
|
86
|
+
* never the app the agent was asked about.
|
|
87
|
+
*/
|
|
88
|
+
function rankOf(session) {
|
|
89
|
+
if (session.unresponsive()) {
|
|
90
|
+
return 3;
|
|
91
|
+
}
|
|
92
|
+
if (isErrorDocument(session.page)) {
|
|
93
|
+
return 2;
|
|
94
|
+
}
|
|
95
|
+
if (session.throttled()) {
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
65
100
|
/**
|
|
66
101
|
* Owns the set of connected browser sessions and the smart auto-selection that resolves which one a
|
|
67
102
|
* tool targets when the agent omits an explicit sessionId. Extracted from session.ts so each file
|
|
@@ -96,6 +131,11 @@ export class SessionManager {
|
|
|
96
131
|
#tombstones = new Map();
|
|
97
132
|
/** The tabs already open when each tombstoned session departed: siblings, never its successor. */
|
|
98
133
|
#siblingsAtDeparture = new Map();
|
|
134
|
+
/**
|
|
135
|
+
* The network record each departed document made after the agent's last act, kept until the same
|
|
136
|
+
* tab's next document connects (#1944). Bounded with the tombstones. See departed-network.ts.
|
|
137
|
+
*/
|
|
138
|
+
#departedNetwork = new Map();
|
|
99
139
|
/**
|
|
100
140
|
* The active project's scope, set once from the daemon's .reticle.json. When a tool resolves a session
|
|
101
141
|
* without passing its own scope, this is applied — so auto-selection is project-scoped by default
|
|
@@ -145,10 +185,24 @@ export class SessionManager {
|
|
|
145
185
|
// that silently fails to register is a leak nothing would report.
|
|
146
186
|
declareDrivenRedactionKeys(session.id, session.redactKeys);
|
|
147
187
|
this.#attachment.attached(session.id);
|
|
188
|
+
this.#carryDepartedNetwork(session, previous);
|
|
148
189
|
this.#tombstones.delete(session.id);
|
|
149
190
|
this.#siblingsAtDeparture.delete(session.id);
|
|
150
191
|
return previous;
|
|
151
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Give a new document the requests its tab's previous document made after the last act (#1944).
|
|
195
|
+
* `previous` is that document when its socket has not closed yet; else the record kept at removal.
|
|
196
|
+
*/
|
|
197
|
+
#carryDepartedNetwork(session, previous) {
|
|
198
|
+
const departed = previous !== undefined && previous !== session
|
|
199
|
+
? departedNetworkOf(previous)
|
|
200
|
+
: this.#departedNetwork.get(session.id);
|
|
201
|
+
this.#departedNetwork.delete(session.id);
|
|
202
|
+
if (departed !== undefined) {
|
|
203
|
+
carryOnNewDocument(session, departed);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
152
206
|
/**
|
|
153
207
|
* The other tabs of the same app as `driven`.
|
|
154
208
|
*
|
|
@@ -173,6 +227,7 @@ export class SessionManager {
|
|
|
173
227
|
session.disconnect(SESSION_PARKED_REASON);
|
|
174
228
|
this.remove(session);
|
|
175
229
|
this.#tombstones.delete(session.id);
|
|
230
|
+
this.#departedNetwork.delete(session.id);
|
|
176
231
|
}
|
|
177
232
|
isParked(sessionId) {
|
|
178
233
|
return this.#parked.has(sessionId);
|
|
@@ -188,6 +243,13 @@ export class SessionManager {
|
|
|
188
243
|
// the reconnect is exactly where that matters.
|
|
189
244
|
this.#attachment.detached(session.id);
|
|
190
245
|
this.#rememberTombstone(session);
|
|
246
|
+
// Kept only beside a tombstone, so the tombstone cap bounds it too.
|
|
247
|
+
const departedNetwork = this.#tombstones.has(session.id)
|
|
248
|
+
? departedNetworkOf(session)
|
|
249
|
+
: undefined;
|
|
250
|
+
if (departedNetwork !== undefined) {
|
|
251
|
+
this.#departedNetwork.set(session.id, departedNetwork);
|
|
252
|
+
}
|
|
191
253
|
return this.#sessions.delete(session.id);
|
|
192
254
|
}
|
|
193
255
|
#rememberTombstone(session) {
|
|
@@ -216,6 +278,7 @@ export class SessionManager {
|
|
|
216
278
|
}
|
|
217
279
|
this.#tombstones.delete(oldest);
|
|
218
280
|
this.#siblingsAtDeparture.delete(oldest);
|
|
281
|
+
this.#departedNetwork.delete(oldest);
|
|
219
282
|
}
|
|
220
283
|
}
|
|
221
284
|
#successorOf(sessionId) {
|
|
@@ -239,6 +302,10 @@ export class SessionManager {
|
|
|
239
302
|
get(sessionId) {
|
|
240
303
|
return this.#sessions.get(sessionId);
|
|
241
304
|
}
|
|
305
|
+
/** The id the same tab connected as after this session's page went away, if one has. */
|
|
306
|
+
successorId(sessionId) {
|
|
307
|
+
return this.#successorOf(sessionId)?.id;
|
|
308
|
+
}
|
|
242
309
|
/**
|
|
243
310
|
* Did this id belong to a session that has since disconnected?
|
|
244
311
|
*
|
|
@@ -515,33 +582,11 @@ export class SessionManager {
|
|
|
515
582
|
return (`no connected session with id '${sessionId}'.${ended} Your app's tabs: ${named}. ` +
|
|
516
583
|
'Retry with one of those, or omit sessionId entirely and let Reticle scope to your project.');
|
|
517
584
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
found.markAgentActivity(); // liveness — a targeted tool keeps the session alive / revives it
|
|
525
|
-
return found;
|
|
526
|
-
}
|
|
527
|
-
if (0 === this.#sessions.size) {
|
|
528
|
-
const closure = this.lastClosure();
|
|
529
|
-
// A diagnosis beats a checklist: it names which of the three causes this actually is.
|
|
530
|
-
const hint = this.#noSessionHint?.();
|
|
531
|
-
// BOTH, when there are both — the two answer different questions and the pair is strictly
|
|
532
|
-
// more than either. This used to be an either/or, and a recorded refusal DISCARDED the whole
|
|
533
|
-
// diagnosis: the one case where the daemon has hard evidence about why nothing is connected
|
|
534
|
-
// was also the one case where it threw away everything else it had learned, including the
|
|
535
|
-
// port scan and the next action. The refusal leads, because it is the fact.
|
|
536
|
-
const refusal = closure === undefined
|
|
537
|
-
? ''
|
|
538
|
-
: ` NOTE: the bridge REFUSED or closed a connection recently — "${closure.reason}". The app is probably still running and trying to connect: it was turned away, and the SDK does not retry after a policy close. The reason above names the fix — do not go looking for a stopped dev server.`;
|
|
539
|
-
// Hand the branch code to the refusal that is about to be reported for this throw. The
|
|
540
|
-
// diagnosis is computed HERE and the refusal is classified from the message downstream, so
|
|
541
|
-
// without this every no-session refusal lands in one undifferentiated bucket (#615).
|
|
542
|
-
notePendingNoSessionReason(this.#noSessionReason?.());
|
|
543
|
-
throw new Error(`${hint ?? NO_SESSION_CONNECTED_ERROR}${refusal}${NO_SESSION_NAVIGATE_FIX}`);
|
|
544
|
-
}
|
|
585
|
+
/**
|
|
586
|
+
* The sessions an id-less call may choose from: in the project scope, then in the caller's own
|
|
587
|
+
* checkout. Throws when those scopes leave nothing, or leave tabs of more than one project.
|
|
588
|
+
*/
|
|
589
|
+
#candidates(scope) {
|
|
545
590
|
// Scope to the agent's active project FIRST, so a stray tab from another app/origin (e.g. a
|
|
546
591
|
// leftover dashboard on a different port) is structurally unselectable — it never enters the
|
|
547
592
|
// candidate set, no matter how recently it was heard from. This is the anti-cross-talk guard.
|
|
@@ -564,28 +609,20 @@ export class SessionManager {
|
|
|
564
609
|
// exists to be. Naming the tab and its sessionId costs the agent one extra argument.
|
|
565
610
|
throw new Error(scopeMissError(connected, effectiveScope));
|
|
566
611
|
}
|
|
567
|
-
if (1 === all.length) {
|
|
568
|
-
const [only] = all;
|
|
569
|
-
if (only === undefined) {
|
|
570
|
-
throw new Error('session lookup failed');
|
|
571
|
-
}
|
|
572
|
-
only.markAgentActivity();
|
|
573
|
-
return only;
|
|
574
|
-
}
|
|
575
612
|
// Two projects and nothing to choose between them: refuse, BEFORE any scoring.
|
|
576
613
|
//
|
|
577
614
|
// Reported from the field: a call from `apps/next-app-router` was answered by a `rowy-*`
|
|
578
615
|
// session, and `act_and_wait` spent 16.9s returning `verified:'no'` about an app never under
|
|
579
616
|
// test — a confident false verdict, with a source pointer, about code the agent never touched.
|
|
580
617
|
//
|
|
581
|
-
// The recency rule
|
|
582
|
-
// the right question for two tabs of ONE app and the wrong question for two apps: the
|
|
583
|
-
// tab of the wrong project is still the wrong project. The reported case had a clear
|
|
584
|
-
// so the ambiguity check passed and the wrong app was selected.
|
|
618
|
+
// The recency rule in resolve() cannot prevent it. It asks "is one of these clearly fresher",
|
|
619
|
+
// which is the right question for two tabs of ONE app and the wrong question for two apps: the
|
|
620
|
+
// fresher tab of the wrong project is still the wrong project. The reported case had a clear
|
|
621
|
+
// winner, so the ambiguity check passed and the wrong app was selected.
|
|
585
622
|
//
|
|
586
623
|
// This sits ahead of scoring so freshness cannot rescue it. It only fires when nothing scoped
|
|
587
|
-
// the call — an explicit `sessionId`
|
|
588
|
-
// already narrowed `all` to one project by here.
|
|
624
|
+
// the call — an explicit `sessionId` never reaches here, and an explicit or default scope has
|
|
625
|
+
// already narrowed `all` to one project by here. One session is one project, so it never fires.
|
|
589
626
|
const projects = new Set(all.map((s) => s.projectId).filter((p) => p !== undefined));
|
|
590
627
|
if (projects.size > 1) {
|
|
591
628
|
const listed = all
|
|
@@ -598,18 +635,46 @@ export class SessionManager {
|
|
|
598
635
|
'never touched — the daemon scopes to the directory it was started in, so running it above ' +
|
|
599
636
|
'several apps leaves it unable to tell them apart.');
|
|
600
637
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
//
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
638
|
+
return all;
|
|
639
|
+
}
|
|
640
|
+
resolve(sessionId, scope) {
|
|
641
|
+
if (sessionId !== undefined) {
|
|
642
|
+
const found = this.#sessions.get(sessionId) ?? this.#successorOf(sessionId);
|
|
643
|
+
if (found === undefined) {
|
|
644
|
+
throw new Error(this.#unknownSessionError(sessionId));
|
|
645
|
+
}
|
|
646
|
+
found.markAgentActivity(); // liveness — a targeted tool keeps the session alive / revives it
|
|
647
|
+
return found;
|
|
648
|
+
}
|
|
649
|
+
if (0 === this.#sessions.size) {
|
|
650
|
+
const closure = this.lastClosure();
|
|
651
|
+
// A diagnosis beats a checklist: it names which of the three causes this actually is.
|
|
652
|
+
const hint = this.#noSessionHint?.();
|
|
653
|
+
// BOTH, when there are both — the two answer different questions and the pair is strictly
|
|
654
|
+
// more than either. This used to be an either/or, and a recorded refusal DISCARDED the whole
|
|
655
|
+
// diagnosis: the one case where the daemon has hard evidence about why nothing is connected
|
|
656
|
+
// was also the one case where it threw away everything else it had learned, including the
|
|
657
|
+
// port scan and the next action. The refusal leads, because it is the fact.
|
|
658
|
+
const refusal = closure === undefined
|
|
659
|
+
? ''
|
|
660
|
+
: ` NOTE: the bridge REFUSED or closed a connection recently — "${closure.reason}". The app is probably still running and trying to connect: it was turned away, and the SDK does not retry after a policy close. The reason above names the fix — do not go looking for a stopped dev server.`;
|
|
661
|
+
// Hand the branch code to the refusal that is about to be reported for this throw. The
|
|
662
|
+
// diagnosis is computed HERE and the refusal is classified from the message downstream, so
|
|
663
|
+
// without this every no-session refusal lands in one undifferentiated bucket (#615).
|
|
664
|
+
notePendingNoSessionReason(this.#noSessionReason?.());
|
|
665
|
+
throw new Error(`${hint ?? NO_SESSION_CONNECTED_ERROR}${refusal}${NO_SESSION_NAVIGATE_FIX}`);
|
|
666
|
+
}
|
|
667
|
+
const all = this.#candidates(scope);
|
|
668
|
+
if (1 === all.length) {
|
|
669
|
+
const [only] = all;
|
|
670
|
+
if (only === undefined) {
|
|
671
|
+
throw new Error('session lookup failed');
|
|
672
|
+
}
|
|
673
|
+
only.markAgentActivity();
|
|
674
|
+
return only;
|
|
675
|
+
}
|
|
676
|
+
// Multiple sessions: score each (lower = better candidate for auto-selection). See rankOf().
|
|
677
|
+
const scored = all.map((s) => ({ s, score: rankOf(s), ms: s.lastSeenMs() }));
|
|
613
678
|
const bestScore = Math.min(...scored.map((x) => x.score));
|
|
614
679
|
const candidates = scored.filter((x) => x.score === bestScore);
|
|
615
680
|
// Sort candidates by recency (ascending lastSeenMs = most recently active first).
|
|
@@ -651,6 +716,44 @@ export class SessionManager {
|
|
|
651
716
|
best.s.markAgentActivity();
|
|
652
717
|
return best.s;
|
|
653
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* Resolve the session for a call that only some sessions can serve.
|
|
721
|
+
*
|
|
722
|
+
* With no sessionId, resolve() chooses by health and recency alone. A lease that could resize the
|
|
723
|
+
* page lost to a focused tab that could not, and the call was refused (#1959). Here, when exactly
|
|
724
|
+
* one candidate can serve the call, that candidate is chosen.
|
|
725
|
+
*/
|
|
726
|
+
async resolveServing(sessionId, canServe) {
|
|
727
|
+
// A named session, or none connected: resolve() already gives the answer and its refusals.
|
|
728
|
+
if (sessionId !== undefined || 0 === this.#sessions.size) {
|
|
729
|
+
return this.resolve(sessionId);
|
|
730
|
+
}
|
|
731
|
+
// The same candidates resolve() chooses from, so the project and checkout scopes still apply.
|
|
732
|
+
// canServe can ask a browser, so each answer is awaited in turn.
|
|
733
|
+
const serving = [];
|
|
734
|
+
for (const candidate of this.#candidates()) {
|
|
735
|
+
if (await canServe(candidate)) {
|
|
736
|
+
serving.push(candidate);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
// Exactly one can serve the call: there is nothing to guess.
|
|
740
|
+
const [onlyServing, secondServing] = serving;
|
|
741
|
+
if (onlyServing !== undefined && secondServing === undefined) {
|
|
742
|
+
onlyServing.markAgentActivity();
|
|
743
|
+
return onlyServing;
|
|
744
|
+
}
|
|
745
|
+
// None can serve: return the usual choice, so the tool gives its own refusal and its advice.
|
|
746
|
+
// Several can serve, and the usual choice cannot: choosing among them is a guess, so refuse
|
|
747
|
+
// and name them. canServe admits only the caller's own sessions, so no other agent's lease is
|
|
748
|
+
// named here.
|
|
749
|
+
const chosen = this.resolve();
|
|
750
|
+
if (0 < serving.length && !serving.includes(chosen)) {
|
|
751
|
+
const ids = serving.map((s) => `'${s.id}'`).join(', ');
|
|
752
|
+
throw new Error(`no sessionId was given, and ${String(serving.length)} sessions can serve this call: ` +
|
|
753
|
+
`${ids}. Pass sessionId to choose one.`);
|
|
754
|
+
}
|
|
755
|
+
return chosen;
|
|
756
|
+
}
|
|
654
757
|
}
|
|
655
758
|
/**
|
|
656
759
|
* Which of a project's tabs belong to the agent's own git checkout.
|
|
@@ -679,6 +782,14 @@ export function scopeToCallerCheckout(sessions, caller, checkoutOfTab) {
|
|
|
679
782
|
if (0 === siblings.length) {
|
|
680
783
|
return { sessions: [] };
|
|
681
784
|
}
|
|
785
|
+
// The caller is the folder the MCP proxy started in, not always where the agent works. Claude Code
|
|
786
|
+
// starts the proxy in the main checkout and then moves the agent into a linked worktree (#1916).
|
|
787
|
+
// So from the main checkout, one linked worktree that serves every tab is the agent's own: drive it.
|
|
788
|
+
// The tab names its `worktree`. Tabs of two worktrees, or a proxy in a linked worktree, stay refused.
|
|
789
|
+
const roots = new Set(siblings.map((p) => p.checkout?.root));
|
|
790
|
+
if (!caller.linked && 1 === roots.size && true === siblings[0]?.checkout?.linked) {
|
|
791
|
+
return { sessions: siblings.map((p) => p.session) };
|
|
792
|
+
}
|
|
682
793
|
const listed = siblings
|
|
683
794
|
.map((p) => `${describeCheckout(p.checkout)} (sessionId '${p.session.id}', ${p.session.url})`)
|
|
684
795
|
.join(', ');
|
package/dist/start-wiring.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { RealInputProvider } from './portal/input/real-input.js';
|
|
|
5
5
|
import { BrowserPool } from './portal/pool/browser-pool.js';
|
|
6
6
|
import { type PageErrorJournal } from './portal/pool/page-error-journal.js';
|
|
7
7
|
import type { PooledPageError } from './portal/pool/pool-contract.js';
|
|
8
|
+
import type { BrowserPoolOptions } from './portal/pool/pool-lease.js';
|
|
8
9
|
import { type InjectedConnect } from './portal/pool/zero-install.js';
|
|
9
10
|
import type { NonceMinter } from './portal/production/production-activation.js';
|
|
10
11
|
/**
|
|
@@ -26,7 +27,7 @@ export declare function readerConnect(port: number, token: string | undefined):
|
|
|
26
27
|
* dials with a dead nonce; mint per navigation if that is ever seen.
|
|
27
28
|
*/
|
|
28
29
|
export declare function zeroInstallLoader(reader: InjectedConnect, nonces?: Pick<NonceMinter, 'mint'>): (url?: string) => string | undefined;
|
|
29
|
-
export declare function createBrowserPool(headless: boolean, reader: InjectedConnect, onPageError?: (leaseId: string, error: PooledPageError) => void, onLeaseClosed?: (leaseId: string) => void, nonces?: Pick<NonceMinter, 'mint'
|
|
30
|
+
export declare function createBrowserPool(headless: boolean, reader: InjectedConnect, onPageError?: (leaseId: string, error: PooledPageError) => void, onLeaseClosed?: (leaseId: string) => void, nonces?: Pick<NonceMinter, 'mint'>, onDialog?: BrowserPoolOptions['onDialog']): BrowserPool;
|
|
30
31
|
/**
|
|
31
32
|
* Journal the uncaught page errors the pool hears into the session that connects for that lease.
|
|
32
33
|
*
|
package/dist/start-wiring.js
CHANGED
|
@@ -68,7 +68,7 @@ export function zeroInstallLoader(reader, nonces) {
|
|
|
68
68
|
return script ?? undefined;
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
-
export function createBrowserPool(headless, reader, onPageError, onLeaseClosed, nonces) {
|
|
71
|
+
export function createBrowserPool(headless, reader, onPageError, onLeaseClosed, nonces, onDialog) {
|
|
72
72
|
const maxContexts = resolveMaxContexts(process.env[ReticleEnv.MAX_CONTEXTS], cpus().length);
|
|
73
73
|
const genSessionId = () => `lease-${'function' === typeof globalThis.crypto?.randomUUID
|
|
74
74
|
? globalThis.crypto.randomUUID()
|
|
@@ -82,6 +82,7 @@ export function createBrowserPool(headless, reader, onPageError, onLeaseClosed,
|
|
|
82
82
|
launchHeaded: playwrightLauncher({ headless: false }),
|
|
83
83
|
...(onPageError === undefined ? {} : { onPageError }),
|
|
84
84
|
...(onLeaseClosed === undefined ? {} : { onLeaseClosed }),
|
|
85
|
+
...(onDialog === undefined ? {} : { onDialog }),
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
88
|
/**
|
package/dist/surface/mcp/mcp.js
CHANGED
|
@@ -7,7 +7,7 @@ import { isToonable, resultToToon, MCP_SERVER_NAME } from '@reticlehq/core';
|
|
|
7
7
|
import { tableForSurface } from '../tools/tools.js';
|
|
8
8
|
import { filterTools, describeToolSurface, TOOL_SURFACE, toolAnnotations, } from '../tools/tool-surface.js';
|
|
9
9
|
import { ReticleTool } from '@reticlehq/core';
|
|
10
|
-
import { sharedParamShort } from './shared-params.js';
|
|
10
|
+
import { refusedKeyRedirect, sharedParamShort } from './shared-params.js';
|
|
11
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';
|
|
@@ -132,7 +132,7 @@ const PREDICATE_FIELD_GRAMMAR_HINT = ' Bug-catching options: net.count (exact re
|
|
|
132
132
|
'compare (page text vs server answer, no expected value: { left:{from:"text",scope}, ' +
|
|
133
133
|
'right:{from:"net",urlContains,path}, as:"number" }), ' +
|
|
134
134
|
'console.absent:true (action completed with a CLEAN ' +
|
|
135
|
-
'console), absent:true on element/text (
|
|
135
|
+
'console), absent:true on element/text (gone from the DOM; add visible:true for "not shown"). Call reticle_tools for the full field ' +
|
|
136
136
|
'grammar of a kind.';
|
|
137
137
|
const COMPACT_PREDICATE_DESCRIPTION = `${PREDICATE_KINDS}${PREDICATE_FIELD_GRAMMAR_HINT}`;
|
|
138
138
|
/**
|
|
@@ -367,6 +367,7 @@ export function installFriendlyArgErrors(server, examples, allowedKeys = new Map
|
|
|
367
367
|
const declared = [...(allowedKeys.get(toolName) ?? [])].join(', ');
|
|
368
368
|
throw new McpError(ErrorCode.InvalidParams, `Unknown ${1 === unknown.length ? 'parameter' : 'parameters'} for ${toolName}: ${unknown.join(', ')}. ` +
|
|
369
369
|
`They were NOT applied — a result computed without them would look like an answer. ` +
|
|
370
|
+
refusedKeyRedirect(toolName, unknown) +
|
|
370
371
|
(0 === declared.length ? '' : `${toolName} declares: ${declared}. `) +
|
|
371
372
|
(example === undefined
|
|
372
373
|
? `Call reticle_tools { names: ["${toolName}"] } for its parameters.`
|
|
@@ -33,3 +33,5 @@ export declare const SHARED_PARAM_GUIDANCE: string;
|
|
|
33
33
|
export declare function sharedParamShort(name: string, schema: z.ZodTypeAny): string | undefined;
|
|
34
34
|
/** Is this a parameter whose guidance now lives in the instructions block? */
|
|
35
35
|
export declare function isSharedParam(name: string): boolean;
|
|
36
|
+
/** The sentences that name the tool for each refused key, or an empty string. */
|
|
37
|
+
export declare function refusedKeyRedirect(toolName: string, refused: readonly string[]): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReticleTool } from '@reticlehq/core';
|
|
1
2
|
/**
|
|
2
3
|
* Parameters that mean the same thing on every tool, documented ONCE instead of every turn.
|
|
3
4
|
*
|
|
@@ -48,3 +49,33 @@ export function sharedParamShort(name, schema) {
|
|
|
48
49
|
export function isSharedParam(name) {
|
|
49
50
|
return Object.hasOwn(SHARED_PARAM_SHORT, name);
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Where to go when a tool refuses a parameter that another tool takes (#1944).
|
|
54
|
+
*
|
|
55
|
+
* An agent proved a click with `reticle_act_and_wait`, saw a request missing from the act's window,
|
|
56
|
+
* and asked for a wider window with `since`. The refusal listed what the tool declares and stopped
|
|
57
|
+
* there, so the agent did not learn that `reticle_assert` reads any window it is given. The refusal
|
|
58
|
+
* now names that tool.
|
|
59
|
+
*
|
|
60
|
+
* A lookup table, keyed by tool and then by the refused parameter. Add a row only where another
|
|
61
|
+
* tool really does what the refused parameter asked for.
|
|
62
|
+
*/
|
|
63
|
+
const REDIRECTS = {
|
|
64
|
+
[ReticleTool.ACT_AND_WAIT]: {
|
|
65
|
+
since: `${ReticleTool.ACT_AND_WAIT} always reads the window that its own action opens. To check a ` +
|
|
66
|
+
`window that starts earlier, call ${ReticleTool.ASSERT} { predicate, since } with the cursor ` +
|
|
67
|
+
'from an earlier result.',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
/** The sentences that name the tool for each refused key, or an empty string. */
|
|
71
|
+
export function refusedKeyRedirect(toolName, refused) {
|
|
72
|
+
const forTool = REDIRECTS[toolName];
|
|
73
|
+
if (forTool === undefined) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
return refused
|
|
77
|
+
.map((key) => (Object.hasOwn(forTool, key) ? forTool[key] : undefined))
|
|
78
|
+
.filter((line) => line !== undefined)
|
|
79
|
+
.map((line) => `${line} `)
|
|
80
|
+
.join('');
|
|
81
|
+
}
|
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
* used from the two points where observation can be lost: a write rejected at dispatch, and a wait
|
|
11
11
|
* cut off mid-predicate.
|
|
12
12
|
*/
|
|
13
|
-
import { type ReticleEvent } from '@reticlehq/core';
|
|
13
|
+
import { type Drift, type ReticleEvent } from '@reticlehq/core';
|
|
14
14
|
import type { Session } from '../../../portal/session/session.js';
|
|
15
|
-
import { type SuccessorRegistry } from '../../../portal/session/session-successor.js';
|
|
15
|
+
import { type SuccessorClock, type SuccessorRegistry } from '../../../portal/session/session-successor.js';
|
|
16
16
|
import type { EvalResult } from '@reticlehq/engine/question/predicate/predicate-eval.js';
|
|
17
|
+
/** How long an act whose page navigated away waits for the lease's successor to connect. */
|
|
18
|
+
export declare const NAVIGATION_FOLLOW_GRACE_MS = 8000;
|
|
17
19
|
interface FollowSuccessor {
|
|
18
20
|
sessions: SuccessorRegistry;
|
|
19
21
|
session: Session;
|
|
@@ -24,12 +26,27 @@ interface FollowSuccessor {
|
|
|
24
26
|
predicateStarted: number;
|
|
25
27
|
/** Re-ask the declared consequence on `next`, with whatever budget is left. */
|
|
26
28
|
reevaluate: (next: Session, budgetMs: number) => Promise<EvalResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Grace for the OTHER navigation shape (#2009): a FAILURE graded while the action's own form
|
|
31
|
+
* POST / link navigation was still in flight. The wait spent its budget on the document BEFORE
|
|
32
|
+
* it left, so nothing was "lost" and no successor was looked for — and the verdict then reads
|
|
33
|
+
* "still on /login" for a sign-in that worked. When set, a failed verdict waits this long for
|
|
34
|
+
* the document's successor and re-asks there; when it never connects, the verdict says the page
|
|
35
|
+
* navigated instead of leaving the failure standing. Undefined keeps the old behaviour.
|
|
36
|
+
*/
|
|
37
|
+
failedFollowGraceMs?: number;
|
|
38
|
+
/** The act's own event cursor: the window the departure evidence is read from. */
|
|
39
|
+
since?: number;
|
|
40
|
+
/** Injected so tests do not sleep; defaults to the real clock. */
|
|
41
|
+
clock?: SuccessorClock;
|
|
27
42
|
}
|
|
28
43
|
interface FollowedOutcome {
|
|
29
44
|
session: Session;
|
|
30
45
|
verdict: EvalResult;
|
|
31
46
|
/** True when the wait moved to a new document, so the caller must reset its event cursor. */
|
|
32
47
|
followed: boolean;
|
|
48
|
+
/** The re-ask ran on the caller's grace — a budget extension declared in the result (#2009). */
|
|
49
|
+
budgetExtendedMs?: number;
|
|
33
50
|
}
|
|
34
51
|
/**
|
|
35
52
|
* Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
|
|
@@ -56,8 +73,29 @@ export declare function disabledTargetNote(pass: boolean, actResult: unknown): s
|
|
|
56
73
|
* failure, whichever way the consequence went.
|
|
57
74
|
*/
|
|
58
75
|
export declare function unreachableTargetNote(actResult: unknown): string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* The drift for the first sub-step of an `ACT_SEQUENCE` result whose target no person could have
|
|
78
|
+
* clicked, or undefined. The page reports one effect for each sub-step it ran, in order. Synthetic
|
|
79
|
+
* dispatch reaches an unreachable target all the same, so a sequence is read sub-step by sub-step, as
|
|
80
|
+
* a single act is. `labelOf` names the sub-step, by its index in this result.
|
|
81
|
+
*/
|
|
82
|
+
export declare function unreachableSubStepDrift(sequenceResult: unknown, labelOf: (at: number) => string): Drift | undefined;
|
|
59
83
|
/** Why the acted target leaves the verdict undecided: no person could reach it, or it was disabled. */
|
|
60
84
|
export declare function actTargetNote(pass: boolean, actResult: unknown): string | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* The cause and the one fix for a read that came back `unknown`, in place of a bare fact (#1990).
|
|
87
|
+
* A page whose SDK is behind this daemon may not serve `match` at all, so the fix is the same
|
|
88
|
+
* SDK-behind sentence the listing shows. On a current page the read ran out of the assertion's own
|
|
89
|
+
* budget, and the fix is more of it. Any reason that is not a command timeout is returned as it came.
|
|
90
|
+
*/
|
|
91
|
+
export declare function diagnoseTimeout(reason: string | undefined, session: {
|
|
92
|
+
readonly sdkVersion?: string | undefined;
|
|
93
|
+
}): string | undefined;
|
|
94
|
+
/** Why an act's verdict is `unknown`: a read that timed out, else an unreachable or disabled target. */
|
|
95
|
+
export declare const actInconclusive: (verdict: {
|
|
96
|
+
pass: boolean;
|
|
97
|
+
inconclusive?: string | undefined;
|
|
98
|
+
}, actResult: unknown, session: Session) => string | undefined;
|
|
61
99
|
/**
|
|
62
100
|
* The requests of a failed check's window, as its record carries them: method, url, status and the
|
|
63
101
|
* start of the response body (redacted in the page before it was ever sent). Dev tooling is left out.
|