@reticlehq/server 3.3.0 → 3.4.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/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +12 -14
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +22 -0
- package/dist/command/cli/adhoc-suite.js +90 -19
- package/dist/command/cli/adhoc-verdict.d.ts +35 -0
- package/dist/command/cli/adhoc-verdict.js +90 -3
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +4 -0
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/cli-doctor.js +7 -5
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- package/dist/command/cli/cli-parse.d.ts +8 -1
- package/dist/command/cli/cli-parse.js +21 -3
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +24 -3
- package/dist/command/cli/cli-verify.js +54 -11
- package/dist/command/cli/cloud-cli.js +5 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.js +21 -3
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/launch/cli-launch.d.ts +8 -0
- package/dist/command/cli/launch/cli-launch.js +21 -9
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +25 -0
- package/dist/command/cli/ports/resolve/cli-port.js +125 -15
- package/dist/command/cli/setup-mcp-cli.js +13 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +62 -10
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +14 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +34 -6
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +39 -2
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +80 -44
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +26 -1
- package/dist/command/setup/run-setup.js +96 -13
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.js +3 -3
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.js +171 -157
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +6 -0
- package/dist/features/harness/fill-values.js +3 -0
- package/dist/features/harness/jev-driver.js +30 -13
- package/dist/features/harness/openai-driver.js +4 -7
- package/dist/features/harness/platform-config.js +5 -6
- package/dist/index.d.ts +9 -0
- package/dist/index.js +40 -11
- package/dist/judgement/domain/domain-model.js +3 -1
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- package/dist/judgement/runs/verification-sync.d.ts +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- package/dist/language/flows/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -0
- package/dist/language/flows/flow-expect-grammar.js +1 -1
- package/dist/language/flows/flow-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- package/dist/language/flows/flow-replay-run.d.ts +2 -3
- package/dist/language/flows/flow-replay-run.js +13 -47
- package/dist/language/flows/flow-replay.d.ts +22 -8
- package/dist/language/flows/flow-replay.js +55 -9
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +10 -1
- package/dist/language/flows/flows.js +90 -14
- package/dist/language/flows/recording/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +30 -1
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +42 -6
- package/dist/language/flows/verify-change-tools.js +5 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -1
- package/dist/memory/cloud/cloud-sync.js +6 -9
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +68 -3
- package/dist/memory/cloud/sync-cycle.js +298 -55
- package/dist/memory/cloud/sync-daemon.js +24 -20
- package/dist/memory/cloud/sync-disk.js +16 -2
- package/dist/memory/intent/intent-shard.d.ts +10 -32
- package/dist/memory/intent/intent-shard.js +0 -10
- package/dist/memory/journal/drive-flow.d.ts +18 -13
- package/dist/memory/journal/drive-flow.js +45 -37
- package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
- package/dist/memory/journal/session-end.d.ts +9 -1
- package/dist/memory/journal/session-end.js +24 -12
- package/dist/memory/project/dir/reticle-dir.d.ts +2 -0
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/project-tools.js +3 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +4 -2
- package/dist/portal/bridge/bridge.d.ts +8 -0
- package/dist/portal/bridge/bridge.js +43 -2
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +29 -4
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +20 -13
- package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- package/dist/portal/session/facts/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/session-health.js +3 -2
- package/dist/portal/session/session-manager.d.ts +20 -6
- package/dist/portal/session/session-manager.js +19 -3
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- package/dist/portal/session/session.d.ts +5 -0
- package/dist/portal/session/session.js +15 -19
- package/dist/surface/http-server.d.ts +2 -1
- package/dist/surface/http-server.js +4 -2
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +2 -2
- package/dist/surface/mcp/mcp.js +4 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/server-instructions.js +9 -7
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- package/dist/surface/tools/act/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +13 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +47 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- package/dist/surface/tools/act-capsule.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +46 -1
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +26 -50
- package/dist/surface/tools/assert/assert-grade.js +12 -1
- package/dist/surface/tools/assert/assert-verdict.js +2 -2
- package/dist/surface/tools/browser-tools.js +14 -2
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/error-recovery.js +4 -4
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +7 -6
- package/dist/surface/tools/lease-tools.js +15 -6
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +9 -1
- package/dist/surface/tools/tools.js +4 -0
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +0 -4
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -3
- package/dist/telemetry/harness-drive.d.ts +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/wire-journal.d.ts +3 -0
- package/dist/wire-journal.js +4 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +4 -2
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +14 -4
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +9 -0
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +21 -2
- package/docs/frameworks.mdx +29 -10
- package/docs/getting-started.md +3 -3
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +1 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +2 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +50 -5
- package/docs/quickstart.mdx +2 -2
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +1 -1
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +7 -6
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +2 -2
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +10 -9
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/phenomena/phenomena.d.ts +0 -27
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export async function startJsCoverage(page, covering) {
|
|
2
|
+
if (covering.has(page))
|
|
3
|
+
return true;
|
|
4
|
+
try {
|
|
5
|
+
await page.coverage.startJSCoverage({ resetOnNavigation: false });
|
|
6
|
+
covering.add(page);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function takeJsCoverage(page, covering) {
|
|
14
|
+
// Nothing collects until something asks: collection slows every script on the page. The first
|
|
15
|
+
// take starts it and has nothing to report yet.
|
|
16
|
+
if (!covering.has(page)) {
|
|
17
|
+
await startJsCoverage(page, covering);
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const entries = await page.coverage.stopJSCoverage();
|
|
22
|
+
covering.delete(page);
|
|
23
|
+
await startJsCoverage(page, covering);
|
|
24
|
+
return entries;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
covering.delete(page);
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Node-only. Playwright is loaded via DYNAMIC `import('playwright')` so non-CDP users never
|
|
10
10
|
* pay for it; the type-only import is elided by `tsc`, so the build stays green without it.
|
|
11
11
|
*/
|
|
12
|
+
import { type ScriptCoverage } from './js-coverage.js';
|
|
12
13
|
import type { Browser, Page } from 'playwright';
|
|
13
14
|
import { ActionType, DriveErrorCode } from '@reticlehq/core';
|
|
14
15
|
import { type MockRule } from './network-mock.js';
|
|
@@ -77,6 +78,14 @@ export interface RealInputProvider {
|
|
|
77
78
|
* Optional: a provider with no owned browser simply omits it.
|
|
78
79
|
*/
|
|
79
80
|
setMocks?(sessionUrl: string, rules: MockRule[]): Promise<boolean>;
|
|
81
|
+
/**
|
|
82
|
+
* What of the app's own code ran since the last take (V8 coverage, Chromium only), or undefined
|
|
83
|
+
* when no driven page matches or collection had not started. The first call starts collecting,
|
|
84
|
+
* because collection slows every script on the page and a slower page can let a console error land
|
|
85
|
+
* after a clean-console check passed. Optional: a provider with no owned browser cannot see the
|
|
86
|
+
* engine's counters.
|
|
87
|
+
*/
|
|
88
|
+
takeCodeCoverage?(sessionUrl: string): Promise<ScriptCoverage[] | undefined>;
|
|
80
89
|
/**
|
|
81
90
|
* Pin the correlated page's viewport to fixed pixel dimensions so a screenshot baseline is
|
|
82
91
|
* reproducible across machines (the missing piece of CI-stable visual regression, alongside masks
|
|
@@ -144,6 +153,7 @@ interface CdpProviderOptions {
|
|
|
144
153
|
export declare class CdpRealInputProvider implements RealInputProvider {
|
|
145
154
|
#private;
|
|
146
155
|
constructor(options: CdpProviderOptions);
|
|
156
|
+
takeCodeCoverage(sessionUrl: string): Promise<ScriptCoverage[] | undefined>;
|
|
147
157
|
isAvailableFor(sessionUrl: string): Promise<boolean>;
|
|
148
158
|
perform(sessionUrl: string, action: ActionType, box: ElementBox, args: RealInputArgs): Promise<RealInputResult>;
|
|
149
159
|
/** PNG of the correlated page, or undefined if none matches. */
|
|
@@ -236,6 +246,7 @@ export declare class LaunchedRealInputProvider implements OwnedRealInputProvider
|
|
|
236
246
|
width: number;
|
|
237
247
|
height: number;
|
|
238
248
|
}): Promise<boolean>;
|
|
249
|
+
takeCodeCoverage(_sessionUrl: string): Promise<ScriptCoverage[] | undefined>;
|
|
239
250
|
/** Apply network-mock rules to the owned page; false before navigate / after dispose. */
|
|
240
251
|
setMocks(_sessionUrl: string, rules: MockRule[]): Promise<boolean>;
|
|
241
252
|
/** Close the owned browser once. Idempotent and safe before navigate. */
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional CDP/Playwright real-input mode.
|
|
3
|
+
*
|
|
4
|
+
* Synthetic `dispatchEvent` cannot drive native hover/pointer state (an onMouseEnter never
|
|
5
|
+
* fires, hit-testing never runs). When a CDP endpoint is configured, this module connects a
|
|
6
|
+
* Playwright `Browser` over CDP and drives REAL pointer/keyboard input against the element box
|
|
7
|
+
* the SDK resolves (viewport CSS px from getBoundingClientRect).
|
|
8
|
+
*
|
|
9
|
+
* Node-only. Playwright is loaded via DYNAMIC `import('playwright')` so non-CDP users never
|
|
10
|
+
* pay for it; the type-only import is elided by `tsc`, so the build stays green without it.
|
|
11
|
+
*/
|
|
12
|
+
import { takeJsCoverage } from './js-coverage.js';
|
|
1
13
|
import { stampedDriveUrl } from './drive-url-stamp.js';
|
|
2
|
-
import {
|
|
3
|
-
import { gotoOptions } from '../pool/playwright-launcher.js';
|
|
14
|
+
import { launchChromium } from '../../launch-chromium.js';
|
|
15
|
+
import { chromiumLaunchHint, gotoOptions } from '../pool/playwright-launcher.js';
|
|
4
16
|
import { BrowserLaunchKind } from '@reticlehq/core/telemetry';
|
|
5
17
|
import { getSessionMetrics } from '../../telemetry/session-metrics.js';
|
|
6
18
|
import { classifyConnectFailure } from '../../telemetry/connect-failure.js';
|
|
@@ -131,6 +143,8 @@ export class CdpRealInputProvider {
|
|
|
131
143
|
/** Pages already listening. #pageFor resolves on EVERY call, so without this each action would add
|
|
132
144
|
* another listener and every response would be emitted once per action taken so far. */
|
|
133
145
|
#listening = new WeakSet();
|
|
146
|
+
/** Pages collecting V8 coverage — see js-coverage.ts. */
|
|
147
|
+
#covering = new WeakSet();
|
|
134
148
|
#browser;
|
|
135
149
|
constructor(options) {
|
|
136
150
|
this.#cdpUrl = options.cdpUrl;
|
|
@@ -176,6 +190,10 @@ export class CdpRealInputProvider {
|
|
|
176
190
|
this.#listen(page);
|
|
177
191
|
return page;
|
|
178
192
|
}
|
|
193
|
+
async takeCodeCoverage(sessionUrl) {
|
|
194
|
+
const page = await this.#pageFor(sessionUrl);
|
|
195
|
+
return page === undefined ? undefined : takeJsCoverage(page, this.#covering);
|
|
196
|
+
}
|
|
179
197
|
async isAvailableFor(sessionUrl) {
|
|
180
198
|
try {
|
|
181
199
|
return (await this.#pageFor(sessionUrl)) !== undefined;
|
|
@@ -273,13 +291,14 @@ export const launchedChromium = async (headless) => {
|
|
|
273
291
|
}
|
|
274
292
|
const settle = getSessionMetrics().recordConnectAttempt(BrowserLaunchKind.LAUNCHED);
|
|
275
293
|
try {
|
|
276
|
-
const browser = await mod.chromium
|
|
294
|
+
const browser = await launchChromium(mod.chromium, headless);
|
|
277
295
|
settle();
|
|
278
296
|
return browser;
|
|
279
297
|
}
|
|
280
298
|
catch (e) {
|
|
281
299
|
settle(classifyConnectFailure(e));
|
|
282
|
-
|
|
300
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
301
|
+
throw new DriveError(DriveErrorCode.LAUNCH_FAILED, chromiumLaunchHint(msg) ?? msg);
|
|
283
302
|
}
|
|
284
303
|
};
|
|
285
304
|
/**
|
|
@@ -296,6 +315,8 @@ export class LaunchedRealInputProvider {
|
|
|
296
315
|
#onNetworkDetail;
|
|
297
316
|
#browser;
|
|
298
317
|
#page;
|
|
318
|
+
/** Pages collecting V8 coverage — see js-coverage.ts. */
|
|
319
|
+
#covering = new WeakSet();
|
|
299
320
|
constructor(options) {
|
|
300
321
|
this.#driveUrl = options.driveUrl;
|
|
301
322
|
this.#headless = options.headless;
|
|
@@ -442,6 +463,10 @@ export class LaunchedRealInputProvider {
|
|
|
442
463
|
await page.setViewportSize({ width: size.width, height: size.height });
|
|
443
464
|
return true;
|
|
444
465
|
}
|
|
466
|
+
takeCodeCoverage(_sessionUrl) {
|
|
467
|
+
const page = this.#livePage();
|
|
468
|
+
return page === undefined ? Promise.resolve(undefined) : takeJsCoverage(page, this.#covering);
|
|
469
|
+
}
|
|
445
470
|
/** Apply network-mock rules to the owned page; false before navigate / after dispose. */
|
|
446
471
|
async setMocks(_sessionUrl, rules) {
|
|
447
472
|
const page = this.#livePage();
|
|
@@ -25,6 +25,12 @@ export declare function gotoOptions(timeoutMs: number | undefined): {
|
|
|
25
25
|
export declare function playwrightLauncher(opts?: {
|
|
26
26
|
headless?: boolean;
|
|
27
27
|
}): Launcher;
|
|
28
|
+
/**
|
|
29
|
+
* Turn Playwright's raw launch error into the one command that fixes it, or undefined when it is
|
|
30
|
+
* not a missing-browser failure. Shared by the pooled and the drive launch, so `reticle verify` on
|
|
31
|
+
* a fresh machine gets the same advice as a lease.
|
|
32
|
+
*/
|
|
33
|
+
export declare function chromiumLaunchHint(msg: string): string | undefined;
|
|
28
34
|
/**
|
|
29
35
|
* Resolve the pool's concurrency cap. An explicit RETICLE_MAX_CONTEXTS wins (clamped to
|
|
30
36
|
* [1, hard ceiling]); otherwise scale with the machine but never above the CPU-derived ceiling.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Headless by default — the pool exists for fault-tolerant headless multi-agent testing.
|
|
7
7
|
*/
|
|
8
8
|
import { BrowserLaunchKind } from '@reticlehq/core/telemetry';
|
|
9
|
-
import {
|
|
9
|
+
import { launchChromium } from '../../launch-chromium.js';
|
|
10
10
|
import { getSessionMetrics } from '../../telemetry/session-metrics.js';
|
|
11
11
|
import { classifyConnectFailure } from '../../telemetry/connect-failure.js';
|
|
12
12
|
import { chromiumInstallCommand, chromiumInstallDepsCommand, bundledPlaywrightVersion, } from '../../command/cli/doctor/browser/chromium-hint.js';
|
|
@@ -105,27 +105,34 @@ export function playwrightLauncher(opts = {}) {
|
|
|
105
105
|
// number that actually costs memory and the one that explains a slow machine.
|
|
106
106
|
const settle = getSessionMetrics().recordConnectAttempt(BrowserLaunchKind.POOLED);
|
|
107
107
|
try {
|
|
108
|
-
const browser = wrapBrowser(await chromium
|
|
108
|
+
const browser = wrapBrowser(await launchChromium(chromium, headless));
|
|
109
109
|
settle();
|
|
110
110
|
return browser;
|
|
111
111
|
}
|
|
112
112
|
catch (err) {
|
|
113
113
|
settle(classifyConnectFailure(err));
|
|
114
|
-
// Turn Playwright's raw "Executable doesn't exist" into the one command that fixes it.
|
|
115
114
|
const msg = err instanceof Error ? err.message : String(err);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// missing binary and send the user through a fix that changes nothing.
|
|
119
|
-
if (/host system is missing dependencies/i.test(msg)) {
|
|
120
|
-
throw new Error(CHROMIUM_MISSING_DEPS_HINT);
|
|
121
|
-
}
|
|
122
|
-
if (/executable doesn.?t exist|playwright install|browsertype\.launch/i.test(msg)) {
|
|
123
|
-
throw new Error(CHROMIUM_MISSING_HINT);
|
|
124
|
-
}
|
|
125
|
-
throw err;
|
|
115
|
+
const hint = chromiumLaunchHint(msg);
|
|
116
|
+
throw hint !== undefined ? new Error(hint) : err;
|
|
126
117
|
}
|
|
127
118
|
};
|
|
128
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Turn Playwright's raw launch error into the one command that fixes it, or undefined when it is
|
|
122
|
+
* not a missing-browser failure. Shared by the pooled and the drive launch, so `reticle verify` on
|
|
123
|
+
* a fresh machine gets the same advice as a lease.
|
|
124
|
+
*/
|
|
125
|
+
export function chromiumLaunchHint(msg) {
|
|
126
|
+
// Checked FIRST: Playwright's host-requirement validation error also contains
|
|
127
|
+
// "browserType.launch", so the generic check below would otherwise misdiagnose it as a
|
|
128
|
+
// missing binary and send the user through a fix that changes nothing.
|
|
129
|
+
if (/host system is missing dependencies/i.test(msg))
|
|
130
|
+
return CHROMIUM_MISSING_DEPS_HINT;
|
|
131
|
+
if (/executable doesn.?t exist|playwright install|browsertype\.launch/i.test(msg)) {
|
|
132
|
+
return CHROMIUM_MISSING_HINT;
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
129
136
|
const MAX_CONTEXTS_CEILING = 8;
|
|
130
137
|
const MAX_CONTEXTS_FLOOR = 1;
|
|
131
138
|
/**
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
export interface DevCommand {
|
|
13
13
|
/** The literal shell command, e.g. `pnpm run dev`. */
|
|
14
14
|
command: string;
|
|
15
|
-
/** The package.json script it runs. */
|
|
16
|
-
script
|
|
15
|
+
/** The package.json script it runs. Absent when the command runs a CLI directly (`npx tauri dev`). */
|
|
16
|
+
script?: string;
|
|
17
17
|
/** The port the script PINS, when it pins one. Absent means the tool picks — never a guess. */
|
|
18
18
|
port?: number;
|
|
19
19
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { readFileSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
+
import { desktopLaunch } from '@reticlehq/init';
|
|
13
14
|
/**
|
|
14
15
|
* Script names checked, in order.
|
|
15
16
|
*
|
|
@@ -47,23 +48,26 @@ function pinnedPort(script) {
|
|
|
47
48
|
const port = Number.parseInt(digits, 10);
|
|
48
49
|
return Number.isSafeInteger(port) ? port : undefined;
|
|
49
50
|
}
|
|
50
|
-
/** The
|
|
51
|
-
function
|
|
51
|
+
/** The parsed package.json, or undefined for anything unreadable/unparseable. */
|
|
52
|
+
function readManifest(raw) {
|
|
52
53
|
if (raw === undefined)
|
|
53
|
-
return
|
|
54
|
+
return undefined;
|
|
54
55
|
try {
|
|
55
|
-
|
|
56
|
-
if (null === parsed || 'object' !== typeof parsed)
|
|
57
|
-
return {};
|
|
58
|
-
const scripts = parsed.scripts;
|
|
59
|
-
if (null === scripts || 'object' !== typeof scripts)
|
|
60
|
-
return {};
|
|
61
|
-
return scripts;
|
|
56
|
+
return JSON.parse(raw);
|
|
62
57
|
}
|
|
63
58
|
catch {
|
|
64
|
-
return
|
|
59
|
+
return undefined;
|
|
65
60
|
}
|
|
66
61
|
}
|
|
62
|
+
/** The scripts block of a parsed package.json, or an empty one. */
|
|
63
|
+
function scriptsOf(manifest) {
|
|
64
|
+
if (null === manifest || 'object' !== typeof manifest)
|
|
65
|
+
return {};
|
|
66
|
+
const scripts = manifest.scripts;
|
|
67
|
+
if (null === scripts || 'object' !== typeof scripts)
|
|
68
|
+
return {};
|
|
69
|
+
return scripts;
|
|
70
|
+
}
|
|
67
71
|
/**
|
|
68
72
|
* The dev command for the project in `directory`, or undefined when there is not one to be sure of.
|
|
69
73
|
*
|
|
@@ -71,9 +75,23 @@ function readScripts(raw) {
|
|
|
71
75
|
* default disk reader.
|
|
72
76
|
*/
|
|
73
77
|
export function detectDevCommand(directory, read = readTextFile) {
|
|
74
|
-
const
|
|
78
|
+
const manifest = readManifest(read(join(directory, PACKAGE_JSON)));
|
|
79
|
+
const scripts = scriptsOf(manifest);
|
|
75
80
|
const manager = LOCKFILES.find(({ file }) => read(join(directory, file)) !== undefined)?.manager ??
|
|
76
81
|
DEFAULT_PACKAGE_MANAGER;
|
|
82
|
+
// A desktop shell's `dev` is its renderer's dev server, which no window ever opens onto. init's
|
|
83
|
+
// rule decides which launcher it is, so the command the daemon hands an agent and the one init
|
|
84
|
+
// spawns are the same.
|
|
85
|
+
const desktop = desktopLaunch(manifest);
|
|
86
|
+
if (desktop !== undefined) {
|
|
87
|
+
if ('command' in desktop)
|
|
88
|
+
return { command: desktop.command };
|
|
89
|
+
const command = `${manager} run ${desktop.script}`;
|
|
90
|
+
return {
|
|
91
|
+
command: 0 < desktop.args.length ? `${command} ${desktop.args}` : command,
|
|
92
|
+
script: desktop.script,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
77
95
|
for (const script of DEV_SCRIPTS) {
|
|
78
96
|
const body = scripts[script];
|
|
79
97
|
if ('string' !== typeof body || 0 === body.trim().length)
|
|
@@ -64,6 +64,15 @@ export function classifyPort(port, probe) {
|
|
|
64
64
|
* accepts v4-mapped connections — so between the two, every ordinary bind is covered.
|
|
65
65
|
*/
|
|
66
66
|
export const PROBE_HOSTS = ['127.0.0.1', '::1'];
|
|
67
|
+
/**
|
|
68
|
+
* The name the probe gives in its Host header, whichever address it dialled.
|
|
69
|
+
*
|
|
70
|
+
* Node names the dialled address, so the `::1` probe sent `Host: [::1]:4200`. Angular's SSR dev
|
|
71
|
+
* server validates Host, answered 400, and printed `ERROR: Bad Request ("http://[::1]:4200/")` into
|
|
72
|
+
* the user's dev terminal on every sweep, for as long as the daemon ran. The address picks the
|
|
73
|
+
* family; the header says what a browser on this machine would.
|
|
74
|
+
*/
|
|
75
|
+
const PROBE_HOST_HEADER = 'localhost';
|
|
67
76
|
/**
|
|
68
77
|
* Does this port serve a DOCUMENT — i.e. is it a dev server rather than merely something listening?
|
|
69
78
|
*
|
|
@@ -76,7 +85,14 @@ function probeState(port, host) {
|
|
|
76
85
|
// Whether the SOCKET came up is tracked separately from whether a response did, because that is
|
|
77
86
|
// the whole difference between "nothing is there" and "something is there and still compiling".
|
|
78
87
|
let connected = false;
|
|
79
|
-
const req = request({
|
|
88
|
+
const req = request({
|
|
89
|
+
host,
|
|
90
|
+
port,
|
|
91
|
+
path: '/',
|
|
92
|
+
method: 'GET',
|
|
93
|
+
timeout: HTTP_PROBE_TIMEOUT_MS,
|
|
94
|
+
headers: { host: `${PROBE_HOST_HEADER}:${String(port)}` },
|
|
95
|
+
}, (res) => {
|
|
80
96
|
const answer = looksLikeDevServer(res.statusCode ?? 0, res.headers['content-type']);
|
|
81
97
|
res.resume(); // drain, so the socket closes rather than lingering
|
|
82
98
|
resolve(answer ? PortState.SERVES_DOCUMENT : PortState.CLOSED);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What does the route a tab died on answer right now?
|
|
3
|
+
*
|
|
4
|
+
* A route that throws server-side tears the page down and the SDK never reconnects, so the daemon
|
|
5
|
+
* is left with an empty session list and the URL it last saw (#862). The URL alone cannot separate
|
|
6
|
+
* "the route 500s" from "somebody closed the tab" — one plain GET can, and the daemon is the only
|
|
7
|
+
* party placed to make it: the page is gone, the agent is inside an MCP call, and the daemon is on
|
|
8
|
+
* the same machine as the dev server.
|
|
9
|
+
*
|
|
10
|
+
* What it refuses to do, because this runs unattended in a diagnostic loop:
|
|
11
|
+
* - only loopback hostnames — the daemon is localhost-only and a diagnostic must not reach out;
|
|
12
|
+
* - only `http:` — `https:` on localhost means a self-signed certificate, and fetching it with
|
|
13
|
+
* verification off would be a posture change for a status code;
|
|
14
|
+
* - GET only, redirects not followed, the body never read — the status is the only thing that
|
|
15
|
+
* leaves the socket, and it is drained so the connection closes rather than lingers;
|
|
16
|
+
* - an error or a timeout is `undefined`, never a status. No fact is better than a wrong one.
|
|
17
|
+
*
|
|
18
|
+
* Same shape as its neighbour `dev-server-probe.ts`, which asks a port whether it serves a document; this asks
|
|
19
|
+
* one exact path what it answers.
|
|
20
|
+
*/
|
|
21
|
+
/** Short on purpose: a dev server that has not answered a route in this long is not going to. */
|
|
22
|
+
export declare const ROUTE_STATUS_TIMEOUT_MS = 2000;
|
|
23
|
+
/** The HTTP status `url` answers a GET with, or undefined when there is no answer to report. */
|
|
24
|
+
export declare function probeRouteStatus(url: string, timeoutMs?: number): Promise<number | undefined>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What does the route a tab died on answer right now?
|
|
3
|
+
*
|
|
4
|
+
* A route that throws server-side tears the page down and the SDK never reconnects, so the daemon
|
|
5
|
+
* is left with an empty session list and the URL it last saw (#862). The URL alone cannot separate
|
|
6
|
+
* "the route 500s" from "somebody closed the tab" — one plain GET can, and the daemon is the only
|
|
7
|
+
* party placed to make it: the page is gone, the agent is inside an MCP call, and the daemon is on
|
|
8
|
+
* the same machine as the dev server.
|
|
9
|
+
*
|
|
10
|
+
* What it refuses to do, because this runs unattended in a diagnostic loop:
|
|
11
|
+
* - only loopback hostnames — the daemon is localhost-only and a diagnostic must not reach out;
|
|
12
|
+
* - only `http:` — `https:` on localhost means a self-signed certificate, and fetching it with
|
|
13
|
+
* verification off would be a posture change for a status code;
|
|
14
|
+
* - GET only, redirects not followed, the body never read — the status is the only thing that
|
|
15
|
+
* leaves the socket, and it is drained so the connection closes rather than lingers;
|
|
16
|
+
* - an error or a timeout is `undefined`, never a status. No fact is better than a wrong one.
|
|
17
|
+
*
|
|
18
|
+
* Same shape as its neighbour `dev-server-probe.ts`, which asks a port whether it serves a document; this asks
|
|
19
|
+
* one exact path what it answers.
|
|
20
|
+
*/
|
|
21
|
+
import { request } from 'node:http';
|
|
22
|
+
import { isLoopbackHostname } from '@reticlehq/core';
|
|
23
|
+
/** Short on purpose: a dev server that has not answered a route in this long is not going to. */
|
|
24
|
+
export const ROUTE_STATUS_TIMEOUT_MS = 2_000;
|
|
25
|
+
const HTTP = 'http:';
|
|
26
|
+
/** The HTTP status `url` answers a GET with, or undefined when there is no answer to report. */
|
|
27
|
+
export function probeRouteStatus(url, timeoutMs = ROUTE_STATUS_TIMEOUT_MS) {
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = new URL(url);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return Promise.resolve(undefined);
|
|
34
|
+
}
|
|
35
|
+
if (parsed.protocol !== HTTP || !isLoopbackHostname(parsed.hostname)) {
|
|
36
|
+
return Promise.resolve(undefined);
|
|
37
|
+
}
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const req = request({
|
|
40
|
+
host: parsed.hostname,
|
|
41
|
+
port: parsed.port,
|
|
42
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
43
|
+
method: 'GET',
|
|
44
|
+
timeout: timeoutMs,
|
|
45
|
+
}, (res) => {
|
|
46
|
+
const status = res.statusCode;
|
|
47
|
+
res.resume(); // drain, so the socket closes rather than lingering
|
|
48
|
+
resolve(status);
|
|
49
|
+
});
|
|
50
|
+
req.once('timeout', () => {
|
|
51
|
+
req.destroy();
|
|
52
|
+
resolve(undefined);
|
|
53
|
+
});
|
|
54
|
+
req.once('error', () => resolve(undefined));
|
|
55
|
+
req.end();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -48,6 +48,8 @@ export declare class ObservedState {
|
|
|
48
48
|
actedRefs(): ReadonlySet<string>;
|
|
49
49
|
/** Every driven control's label, for matching across re-renders. */
|
|
50
50
|
actedLabels(): ReadonlySet<string>;
|
|
51
|
+
recordProvedLabel(label: string): void;
|
|
52
|
+
provedLabels(): ReadonlySet<string>;
|
|
51
53
|
/**
|
|
52
54
|
* Record bridge-side sampling as a blind spot.
|
|
53
55
|
*
|
|
@@ -58,3 +60,12 @@ export declare class ObservedState {
|
|
|
58
60
|
noteRateLimited(dropped: number): void;
|
|
59
61
|
blindSpots(): Readonly<Record<string, number>>;
|
|
60
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The snapshot-shaped labels an act reply names its control by: the testid, and `role "name"`.
|
|
65
|
+
*
|
|
66
|
+
* The TESTID first: it is the strongest identity a control has and it survives any re-render.
|
|
67
|
+
* Coverage previously matched only on `role "name"`, so a control with a testid but no accessible
|
|
68
|
+
* name — or on a stack where the act reply carried neither — was unrecognisable after a re-render,
|
|
69
|
+
* and coverage read `exercised: 0` however much work had been done.
|
|
70
|
+
*/
|
|
71
|
+
export declare function controlLabelsOf(payload: unknown): string[];
|
|
@@ -105,6 +105,18 @@ export class ObservedState {
|
|
|
105
105
|
actedLabels() {
|
|
106
106
|
return this.#actedLabels;
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Controls whose action came back `verified: "yes"` on a declared consequence — the PROVED level
|
|
110
|
+
* of the coverage ledger. Same label identity as `actedLabels`, so the two compare directly.
|
|
111
|
+
*/
|
|
112
|
+
#provedLabels = new Set();
|
|
113
|
+
recordProvedLabel(label) {
|
|
114
|
+
if (label.length > 0)
|
|
115
|
+
this.#provedLabels.add(label);
|
|
116
|
+
}
|
|
117
|
+
provedLabels() {
|
|
118
|
+
return this.#provedLabels;
|
|
119
|
+
}
|
|
108
120
|
/**
|
|
109
121
|
* Record bridge-side sampling as a blind spot.
|
|
110
122
|
*
|
|
@@ -119,3 +131,25 @@ export class ObservedState {
|
|
|
119
131
|
return this.#blindSpots;
|
|
120
132
|
}
|
|
121
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* The snapshot-shaped labels an act reply names its control by: the testid, and `role "name"`.
|
|
136
|
+
*
|
|
137
|
+
* The TESTID first: it is the strongest identity a control has and it survives any re-render.
|
|
138
|
+
* Coverage previously matched only on `role "name"`, so a control with a testid but no accessible
|
|
139
|
+
* name — or on a stack where the act reply carried neither — was unrecognisable after a re-render,
|
|
140
|
+
* and coverage read `exercised: 0` however much work had been done.
|
|
141
|
+
*/
|
|
142
|
+
export function controlLabelsOf(payload) {
|
|
143
|
+
if (typeof payload !== 'object' || null === payload)
|
|
144
|
+
return [];
|
|
145
|
+
const record = payload;
|
|
146
|
+
const labels = [];
|
|
147
|
+
const testid = record['testid'];
|
|
148
|
+
if ('string' === typeof testid && testid.length > 0)
|
|
149
|
+
labels.push(testid);
|
|
150
|
+
const role = record['role'];
|
|
151
|
+
const name = record['name'];
|
|
152
|
+
if ('string' === typeof role && 'string' === typeof name && role.length > 0 && name.length > 0)
|
|
153
|
+
labels.push(`${role} "${name}"`);
|
|
154
|
+
return labels;
|
|
155
|
+
}
|
|
@@ -91,6 +91,20 @@ export interface NoSessionFacts {
|
|
|
91
91
|
* already read it; the message did not.
|
|
92
92
|
*/
|
|
93
93
|
authRefused?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The bridge refused the last hello for something OTHER than its token — a protocol number out of
|
|
96
|
+
* range, or a hello that failed the wire schema outright — and nothing has connected since.
|
|
97
|
+
*
|
|
98
|
+
* The same positive evidence as `authRefused`, with a different fix. Without it the daemon had
|
|
99
|
+
* recorded the refusal and `init` still ended on "the SDK is in the page and never dialled the
|
|
100
|
+
* bridge", about a page that dialled and was turned away. `reason` follows the `cause — fix`
|
|
101
|
+
* shape the bridge writes; the page and project are what the refused hello said about itself.
|
|
102
|
+
*/
|
|
103
|
+
helloRefused?: {
|
|
104
|
+
reason: string;
|
|
105
|
+
url?: string;
|
|
106
|
+
projectId?: string;
|
|
107
|
+
};
|
|
94
108
|
/**
|
|
95
109
|
* The framework `.reticle.json` declares, when there is one.
|
|
96
110
|
*
|
|
@@ -100,6 +114,14 @@ export interface NoSessionFacts {
|
|
|
100
114
|
* hint the agent reads hours later never mentioned it.
|
|
101
115
|
*/
|
|
102
116
|
framework?: string;
|
|
117
|
+
/**
|
|
118
|
+
* The project is a desktop shell (Electron, Tauri), read from the project the way `doctor` reads it.
|
|
119
|
+
*
|
|
120
|
+
* Its page exists only inside its own window, so every "open the app's URL in a browser" and
|
|
121
|
+
* every lease offer below is the wrong move there: it loads the renderer without the shell's IPC
|
|
122
|
+
* bridge or native APIs, which is not the app being verified. Absent means web.
|
|
123
|
+
*/
|
|
124
|
+
desktop?: boolean;
|
|
103
125
|
/**
|
|
104
126
|
* `.reticle.json` files found OUTSIDE this daemon's directory — a workspace app, a repo root.
|
|
105
127
|
*
|
|
@@ -144,6 +166,13 @@ export interface NoSessionFacts {
|
|
|
144
166
|
* or the ring has forgotten — inventing a URL is worse than the silence this exists to end.
|
|
145
167
|
*/
|
|
146
168
|
lastKnownUrl?: string;
|
|
169
|
+
/**
|
|
170
|
+
* What a plain GET of `lastKnownUrl` answered, done out of band by the watch after the session
|
|
171
|
+
* went. The one fact that separates a server error from a closed tab and from an install
|
|
172
|
+
* problem (#808, option 3). Present only when the fetch got an HTTP status back — a failed or
|
|
173
|
+
* timed-out fetch is no fact, and this stays absent rather than guessing.
|
|
174
|
+
*/
|
|
175
|
+
lastKnownStatus?: number;
|
|
147
176
|
}
|
|
148
177
|
/**
|
|
149
178
|
* Every branch ends with this, and `recoveryFor` keys on it to suppress the generic no-session
|