@reticlehq/server 3.5.0 → 3.6.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/README.md +1 -1
- package/SKILL.md +16 -8
- package/dist/chromium-launch-options.d.ts +3 -1
- package/dist/chromium-launch-options.js +2 -1
- package/dist/command/cli/adhoc-verdict.d.ts +1 -0
- package/dist/command/cli/adhoc-verdict.js +6 -3
- package/dist/command/cli/answers/open-note.d.ts +2 -0
- package/dist/command/cli/answers/open-note.js +22 -0
- package/dist/command/cli/cli-flow-commands.js +4 -8
- package/dist/command/cli/cli-parse.d.ts +1 -1
- package/dist/command/cli/cli-parse.js +10 -10
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +35 -25
- package/dist/command/cli/cloud-cli.js +51 -12
- package/dist/command/cli/cloud-login.d.ts +8 -0
- package/dist/command/cli/cloud-login.js +36 -2
- package/dist/command/cli/demo/demo-page.js +11 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +46 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
- package/dist/command/cli/drive/drive-attach.d.ts +27 -2
- package/dist/command/cli/drive/drive-attach.js +71 -4
- package/dist/command/cli/drive/drive-command.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +13 -7
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +4 -1
- package/dist/command/cli/ports/port-holder.d.ts +10 -0
- package/dist/command/cli/ports/port-holder.js +24 -0
- package/dist/command/cli/setup-mcp-cli.js +6 -4
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +6 -9
- package/dist/command/cli.d.ts +6 -0
- package/dist/command/cli.js +28 -22
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
- package/dist/command/daemon/daemon-resolve.d.ts +14 -0
- package/dist/command/daemon/daemon-resolve.js +33 -2
- package/dist/command/setup/agent-writer.d.ts +1 -1
- package/dist/command/setup/agent-writer.js +3 -2
- package/dist/command/setup/bringup/connect-lease.d.ts +1 -0
- package/dist/command/setup/bringup/connect-lease.js +1 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +8 -0
- package/dist/command/setup/bringup/dev-server-wait.js +19 -0
- package/dist/command/setup/init/init-port.d.ts +4 -0
- package/dist/command/setup/init/init-port.js +10 -0
- package/dist/command/setup/init/init-runtime.d.ts +10 -0
- package/dist/command/setup/init/init-runtime.js +18 -1
- package/dist/command/setup/node-effects.js +2 -1
- package/dist/command/setup/remaining-steps.js +2 -1
- package/dist/command/setup/run-setup.d.ts +5 -0
- package/dist/command/setup/run-setup.js +35 -27
- package/dist/command/setup/setup-command.d.ts +2 -1
- package/dist/command/setup/setup-command.js +2 -0
- package/dist/command/version/version-skew.js +11 -2
- package/dist/features/crawl/crawl-tools.js +4 -0
- package/dist/features/crawl/crawl.d.ts +6 -0
- package/dist/features/crawl/crawl.js +9 -1
- package/dist/features/exhaust/ledger.d.ts +7 -0
- package/dist/features/exhaust/ledger.js +35 -9
- package/dist/features/harness/app-findings.d.ts +2 -0
- package/dist/features/harness/app-findings.js +52 -0
- package/dist/features/harness/drive-report.d.ts +27 -1
- package/dist/features/harness/drive-report.js +162 -11
- package/dist/features/harness/drivers.d.ts +10 -14
- package/dist/features/harness/drivers.js +10 -14
- package/dist/features/harness/fill-values.d.ts +3 -73
- package/dist/features/harness/fill-values.js +3 -169
- package/dist/features/harness/goals.d.ts +21 -0
- package/dist/features/harness/goals.js +77 -0
- package/dist/features/harness/harness.d.ts +21 -0
- package/dist/features/harness/harness.js +69 -2
- package/dist/features/harness/platform/drive-target.d.ts +30 -0
- package/dist/features/harness/platform/drive-target.js +81 -0
- package/dist/features/harness/platform/local-apps.d.ts +94 -0
- package/dist/features/harness/platform/local-apps.js +204 -0
- package/dist/features/harness/platform/remote-drive.d.ts +180 -0
- package/dist/features/harness/platform/remote-drive.js +365 -0
- package/dist/features/harness/platform/script.d.ts +48 -0
- package/dist/features/harness/platform/script.js +128 -0
- package/dist/features/harness/platform/server-driver.d.ts +41 -0
- package/dist/features/harness/platform/server-driver.js +179 -0
- package/dist/features/harness/platform-config.d.ts +7 -0
- package/dist/features/harness/platform-config.js +14 -2
- package/dist/features/harness/script/app-controls.d.ts +5 -0
- package/dist/features/harness/script/app-controls.js +40 -0
- package/dist/features/harness/script/lane-ids.d.ts +14 -0
- package/dist/features/harness/script/lane-ids.js +16 -0
- package/dist/features/harness/script/script-run.d.ts +41 -0
- package/dist/features/harness/script/script-run.js +299 -0
- package/dist/features/license/license.d.ts +2 -2
- package/dist/features/visual/visual-tools.d.ts +10 -1
- package/dist/features/visual/visual-tools.js +40 -3
- package/dist/hooks/driven-by.d.ts +4 -0
- package/dist/hooks/driven-by.js +16 -0
- package/dist/hooks/hook-commands.js +15 -4
- package/dist/hooks/tool-log.d.ts +9 -0
- package/dist/hooks/tool-log.js +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +76 -82
- package/dist/judgement/runs/artifact/build-verification-run.js +6 -0
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +1 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +16 -6
- package/dist/judgement/runs/artifact/run-store.d.ts +1 -1
- package/dist/judgement/runs/artifact/run-store.js +26 -1
- package/dist/judgement/runs/drive-run.d.ts +22 -0
- package/dist/judgement/runs/drive-run.js +99 -1
- package/dist/judgement/runs/repair-prompt.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +8 -1
- package/dist/judgement/runs/verification-sync.d.ts +3 -2
- package/dist/judgement/runs/verification-sync.js +4 -3
- package/dist/language/flows/change/gate.d.ts +10 -0
- package/dist/language/flows/change/gate.js +22 -0
- package/dist/language/flows/decision.js +34 -0
- package/dist/language/flows/flow-anchor.d.ts +54 -8
- package/dist/language/flows/flow-anchor.js +88 -3
- package/dist/language/flows/flow-cross-step.d.ts +15 -0
- package/dist/language/flows/flow-cross-step.js +21 -0
- package/dist/language/flows/flow-learning.js +8 -11
- package/dist/language/flows/flow-mutate-tools.js +5 -3
- package/dist/language/flows/flow-replay-run.d.ts +23 -9
- package/dist/language/flows/flow-replay-run.js +101 -94
- package/dist/language/flows/flow-replay.d.ts +23 -1
- package/dist/language/flows/flow-replay.js +92 -19
- package/dist/language/flows/flow-scope.js +5 -1
- package/dist/language/flows/flow-step-runners.d.ts +20 -2
- package/dist/language/flows/flow-step-runners.js +236 -42
- package/dist/language/flows/flow-store-for-session.d.ts +2 -2
- package/dist/language/flows/flow-store-for-session.js +2 -2
- package/dist/language/flows/flow-tools.js +58 -34
- package/dist/language/flows/flows.js +5 -2
- package/dist/language/flows/heal-run.js +6 -4
- package/dist/language/flows/recording/flake.d.ts +2 -2
- package/dist/language/flows/recording/tape/recordings.d.ts +30 -4
- package/dist/language/flows/recording/tape/recordings.js +40 -8
- package/dist/language/flows/replay-from-hud.d.ts +20 -0
- package/dist/language/flows/replay-from-hud.js +51 -0
- package/dist/language/flows/replay.d.ts +9 -5
- package/dist/language/flows/replay.js +27 -9
- package/dist/language/flows/suite/suite-flakes.js +2 -0
- package/dist/language/flows/suite-selection.d.ts +2 -15
- package/dist/language/flows/suite-selection.js +4 -3
- package/dist/language/flows/verify-change-tools.js +8 -4
- package/dist/launch-chromium.d.ts +27 -5
- package/dist/launch-chromium.js +38 -8
- package/dist/memory/cloud/cloud-config.d.ts +12 -0
- package/dist/memory/cloud/cloud-config.js +15 -0
- package/dist/memory/cloud/cloud-sync.d.ts +17 -14
- package/dist/memory/cloud/cloud-sync.js +12 -1
- package/dist/memory/cloud/harness-config.d.ts +26 -1
- package/dist/memory/cloud/harness-config.js +99 -4
- package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
- package/dist/memory/cloud/hud-notices-source.js +128 -0
- package/dist/memory/cloud/memory-scope.d.ts +144 -0
- package/dist/memory/cloud/memory-scope.js +205 -0
- package/dist/memory/cloud/sync-cycle.d.ts +12 -3
- package/dist/memory/cloud/sync-cycle.js +16 -3
- package/dist/memory/cloud/sync-daemon.js +7 -5
- package/dist/memory/cloud/sync-disk.d.ts +1 -1
- package/dist/memory/cloud/sync-disk.js +5 -8
- package/dist/memory/impact/account-watch.d.ts +19 -0
- package/dist/memory/impact/account-watch.js +34 -0
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +12 -2
- package/dist/memory/impact/impact-store.d.ts +16 -0
- package/dist/memory/impact/impact-store.js +142 -26
- package/dist/memory/intent/intent-shard.d.ts +2 -2
- package/dist/memory/intent/intent-tools.js +80 -2
- package/dist/memory/journal/envelope.d.ts +8 -8
- package/dist/memory/journal/journal-recorder.js +7 -1
- package/dist/memory/journal/on-disk/retention.d.ts +3 -0
- package/dist/memory/journal/on-disk/retention.js +5 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -4
- package/dist/memory/journal/session-end.js +6 -4
- package/dist/memory/project/artifact-root-resolver.d.ts +26 -1
- package/dist/memory/project/artifact-root-resolver.js +94 -13
- package/dist/memory/project/artifact-root.d.ts +42 -5
- package/dist/memory/project/artifact-root.js +87 -8
- package/dist/memory/project/dir/reticle-dir.d.ts +4 -2
- package/dist/memory/project/dir/reticle-dir.js +3 -1
- package/dist/memory/project/project-tools.js +22 -3
- package/dist/memory/project/session-root.d.ts +24 -1
- package/dist/memory/project/session-root.js +41 -5
- package/dist/memory/project/sync-status.d.ts +39 -0
- package/dist/memory/project/sync-status.js +90 -0
- package/dist/memory/recall/memory-tools.js +2 -0
- package/dist/memory/recall/project-memory.d.ts +19 -0
- package/dist/memory/recall/project-memory.js +45 -6
- package/dist/portal/bridge/bridge.d.ts +3 -1
- package/dist/portal/bridge/bridge.js +19 -2
- package/dist/portal/bridge/pairing-token.d.ts +9 -0
- package/dist/portal/bridge/pairing-token.js +17 -0
- package/dist/portal/input/real-input.d.ts +81 -10
- package/dist/portal/input/real-input.js +207 -22
- package/dist/portal/input/viewport-tools.js +3 -3
- package/dist/portal/pool/browser-pool.d.ts +22 -9
- package/dist/portal/pool/browser-pool.js +70 -35
- package/dist/portal/pool/playwright-launcher.d.ts +13 -0
- package/dist/portal/pool/playwright-launcher.js +17 -1
- package/dist/portal/pool/pool-contract.d.ts +7 -4
- package/dist/portal/session/command-payload.d.ts +8 -0
- package/dist/portal/session/command-payload.js +19 -0
- package/dist/portal/session/dev-server/dev-command.d.ts +12 -0
- package/dist/portal/session/dev-server/dev-command.js +61 -12
- package/dist/portal/session/facts/departure-note.d.ts +22 -0
- package/dist/portal/session/facts/departure-note.js +47 -0
- package/dist/portal/session/facts/session-replaced.d.ts +5 -0
- package/dist/portal/session/facts/session-replaced.js +5 -0
- package/dist/portal/session/first-run-wiring.d.ts +42 -0
- package/dist/portal/session/first-run-wiring.js +132 -0
- package/dist/portal/session/human/notes-ledger.d.ts +20 -0
- package/dist/portal/session/human/notes-ledger.js +55 -0
- package/dist/portal/session/human/review-store.d.ts +12 -0
- package/dist/portal/session/human/review-store.js +18 -10
- package/dist/portal/session/live-control-tools.d.ts +0 -13
- package/dist/portal/session/live-control-tools.js +28 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +7 -0
- package/dist/portal/session/no-session-diagnosis.js +15 -0
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +31 -5
- package/dist/portal/session/no-session-watch.d.ts +14 -1
- package/dist/portal/session/no-session-watch.js +63 -10
- package/dist/portal/session/presence/session-mirror.js +3 -0
- package/dist/portal/session/serving-directory.d.ts +27 -0
- package/dist/portal/session/serving-directory.js +45 -0
- package/dist/portal/session/session-health.d.ts +7 -0
- package/dist/portal/session/session-health.js +6 -0
- package/dist/portal/session/session-info.d.ts +8 -1
- package/dist/portal/session/session-info.js +6 -0
- package/dist/portal/session/session-manager.d.ts +21 -0
- package/dist/portal/session/session-manager.js +44 -8
- package/dist/portal/session/session-reaper.js +4 -1
- package/dist/portal/session/session-successor.d.ts +5 -0
- package/dist/portal/session/session-tools.js +26 -3
- package/dist/portal/session/session.d.ts +7 -2
- package/dist/portal/session/session.js +21 -18
- package/dist/start-options.d.ts +6 -0
- package/dist/status-payload.d.ts +10 -0
- package/dist/status-payload.js +23 -0
- package/dist/surface/http-server.js +11 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +5 -0
- package/dist/surface/mcp/mcp-proxy.js +36 -5
- package/dist/surface/mcp/mcp.d.ts +11 -6
- package/dist/surface/mcp/mcp.js +27 -13
- package/dist/surface/mcp/server-instructions.d.ts +4 -2
- package/dist/surface/mcp/server-instructions.js +14 -4
- package/dist/surface/mcp/shared-params.js +2 -2
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +8 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
- package/dist/surface/tools/act/settle-in-flight.js +25 -3
- package/dist/surface/tools/act-sequence-tool.js +2 -2
- package/dist/surface/tools/act-tools.js +23 -7
- package/dist/surface/tools/assert/assert-verdict.d.ts +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +17 -2
- package/dist/surface/tools/browser-tools.js +8 -1
- package/dist/surface/tools/chat-drives.d.ts +26 -0
- package/dist/surface/tools/chat-drives.js +165 -0
- package/dist/surface/tools/empty-session-list.d.ts +41 -0
- package/dist/surface/tools/empty-session-list.js +80 -0
- package/dist/surface/tools/error-recovery.d.ts +6 -0
- package/dist/surface/tools/error-recovery.js +37 -4
- package/dist/surface/tools/explore-tools.d.ts +10 -1
- package/dist/surface/tools/explore-tools.js +82 -13
- package/dist/surface/tools/feedback-tools.js +14 -0
- package/dist/surface/tools/harness-explore.d.ts +96 -14
- package/dist/surface/tools/harness-explore.js +227 -206
- package/dist/surface/tools/harness-plan.d.ts +12 -0
- package/dist/surface/tools/harness-plan.js +31 -0
- package/dist/surface/tools/harness-script.d.ts +8 -0
- package/dist/surface/tools/harness-script.js +342 -0
- package/dist/surface/tools/harness-toolset.d.ts +14 -1
- package/dist/surface/tools/harness-toolset.js +77 -6
- package/dist/surface/tools/invoke-tool.js +52 -1
- package/dist/surface/tools/lease-seed.d.ts +29 -0
- package/dist/surface/tools/lease-seed.js +123 -0
- package/dist/surface/tools/lease-tools.d.ts +3 -23
- package/dist/surface/tools/lease-tools.js +30 -131
- package/dist/surface/tools/live-call-text.js +2 -2
- package/dist/surface/tools/next-step.d.ts +19 -0
- package/dist/surface/tools/next-step.js +159 -0
- package/dist/surface/tools/observe-tools.js +2 -4
- package/dist/surface/tools/platform-moment.d.ts +12 -0
- package/dist/surface/tools/platform-moment.js +168 -0
- package/dist/surface/tools/query-hint-schema.d.ts +7 -0
- package/dist/surface/tools/query-hint-schema.js +10 -0
- package/dist/surface/tools/read-tools.js +8 -2
- package/dist/surface/tools/real-input-attempt.js +96 -13
- package/dist/surface/tools/tool-kit.d.ts +15 -1
- package/dist/surface/tools/tool-kit.js +4 -6
- package/dist/surface/tools/tools.js +20 -48
- package/dist/telemetry/hud-metrics.d.ts +2 -0
- package/dist/telemetry/hud-metrics.js +17 -0
- package/dist/telemetry/telemetry.d.ts +12 -0
- package/dist/telemetry/telemetry.js +43 -14
- package/dist/telemetry/verification-of.js +1 -0
- package/dist/wire-journal.js +4 -1
- package/docs/README.md +1 -1
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/autodrive.md +29 -25
- package/docs/cli/doctor.mdx +6 -0
- package/docs/cli/drive.mdx +8 -2
- package/docs/cli/init.mdx +11 -13
- package/docs/cli/status.mdx +5 -1
- package/docs/cli/version.mdx +3 -3
- package/docs/cli.mdx +1 -1
- package/docs/desktop.mdx +5 -1
- package/docs/environment.mdx +3 -7
- package/docs/getting-started.md +13 -11
- package/docs/human-control.md +1 -2
- package/docs/index.mdx +4 -3
- package/docs/install-manual.mdx +2 -2
- package/docs/packages/tauri.mdx +2 -2
- package/docs/packages/vite-plugin.mdx +22 -1
- package/docs/packages.mdx +1 -1
- package/docs/quickstart.mdx +17 -7
- package/docs/telemetry.md +1 -1
- package/docs/tools/session-and-feedback.mdx +9 -9
- package/docs/usage.md +5 -2
- package/docs/what-is-recorded.md +13 -3
- package/docs/what-is-reticle.mdx +3 -1
- package/package.json +8 -12
- package/dist/features/harness/driver.d.ts +0 -50
- package/dist/features/harness/driver.js +0 -236
- package/dist/features/harness/jev-driver.d.ts +0 -188
- package/dist/features/harness/jev-driver.js +0 -678
- package/dist/features/harness/openai-driver.d.ts +0 -58
- package/dist/features/harness/openai-driver.js +0 -206
- package/dist/memory/project/dir/fill-value-store.d.ts +0 -32
- package/dist/memory/project/dir/fill-value-store.js +0 -88
- package/docs/local-registry.md +0 -143
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type LinkCapability, type LinkNotice } from '@reticlehq/core';
|
|
2
|
+
import { type DriveCandidate } from './remote-drive.js';
|
|
3
|
+
/** How often apps are reported. The platform counts an app live for two and a half of these. */
|
|
4
|
+
export declare const LOCAL_APPS_REPORT_MS = 10000;
|
|
5
|
+
/** How long a drive waits for an app it opened to connect before it is refused. */
|
|
6
|
+
export declare const APP_OPEN_WAIT_MS = 20000;
|
|
7
|
+
export interface Platform {
|
|
8
|
+
url: string;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Machine {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
/** One connected tab, as far as reporting and picking go. */
|
|
16
|
+
export interface AppTab extends DriveCandidate {
|
|
17
|
+
title?: string;
|
|
18
|
+
projectId?: string;
|
|
19
|
+
adapters: string[];
|
|
20
|
+
runtime?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ReportedApp {
|
|
23
|
+
key: string;
|
|
24
|
+
name: string;
|
|
25
|
+
url: string;
|
|
26
|
+
title?: string;
|
|
27
|
+
stack: string[];
|
|
28
|
+
}
|
|
29
|
+
/** This machine: a stable id that is not its home path, and the name a person knows it by. */
|
|
30
|
+
export declare const machineOf: (hostname: string, home: string) => Machine;
|
|
31
|
+
/**
|
|
32
|
+
* One project checkout on one machine. Its port is not part of it. The tab's projectId is: a daemon
|
|
33
|
+
* that cannot map a project to its checkout files every such project under one shared directory.
|
|
34
|
+
*/
|
|
35
|
+
export declare const appKeyOf: (machineId: string, projectDir: string, projectId: string | undefined) => string;
|
|
36
|
+
/** What a refused open is told. */
|
|
37
|
+
export declare const NOT_AN_APP_ADDRESS = "Reticle only opens a web address on this machine or one of the apps already open here.";
|
|
38
|
+
/**
|
|
39
|
+
* Whether the daemon may open an address the platform named: a web page on this machine, or one on
|
|
40
|
+
* an origin a connected tab already has. Never a file, and never anywhere else on the network.
|
|
41
|
+
*/
|
|
42
|
+
export declare function mayOpen(url: string, openUrls: readonly string[]): boolean;
|
|
43
|
+
/** A project id as a name: the SDK stamps `<package>-<8 hex>`, and the hash means nothing to a person. */
|
|
44
|
+
export declare const nameFromProjectId: (projectId: string) => string;
|
|
45
|
+
/**
|
|
46
|
+
* The apps to report, grouped by the platform credential each one's project is linked with. The
|
|
47
|
+
* linked credential is reported even with no app open: it is how the chat knows the machine is there
|
|
48
|
+
* to open one. A tab whose project is not linked is left out: no chat can drive it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function appsByPlatform(tabs: readonly AppTab[], linked: Platform | undefined, machineId: string, describe: (tab: AppTab) => Promise<{
|
|
51
|
+
platform?: Platform;
|
|
52
|
+
dir: string;
|
|
53
|
+
name: string;
|
|
54
|
+
}>): Promise<{
|
|
55
|
+
platform: Platform;
|
|
56
|
+
apps: ReportedApp[];
|
|
57
|
+
}[]>;
|
|
58
|
+
type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
|
|
59
|
+
export interface AppReportDeps {
|
|
60
|
+
machine: Machine;
|
|
61
|
+
version: string;
|
|
62
|
+
/** What this daemon can do for the platform: it offers a person only what is here. */
|
|
63
|
+
capabilities: readonly LinkCapability[];
|
|
64
|
+
apps: () => Promise<{
|
|
65
|
+
platform: Platform;
|
|
66
|
+
apps: ReportedApp[];
|
|
67
|
+
}[]>;
|
|
68
|
+
/** Open an app so it connects: one somebody asked to bring back. A throw is logged, never fatal. */
|
|
69
|
+
open: (url: string) => Promise<void>;
|
|
70
|
+
/** A drive is waiting for the project this credential is for. */
|
|
71
|
+
drivePending: (platform: Platform) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Something the platform wants the person told ("update Reticle"), for the project this credential
|
|
74
|
+
* is for. Called once per notice for the daemon's life, however often the platform repeats it.
|
|
75
|
+
*/
|
|
76
|
+
notify?: (notice: LinkNotice, platform: Platform) => void;
|
|
77
|
+
fetch?: FetchLike;
|
|
78
|
+
intervalMs?: number;
|
|
79
|
+
log?: (line: string) => void;
|
|
80
|
+
}
|
|
81
|
+
export interface AppReports {
|
|
82
|
+
tick: () => Promise<void>;
|
|
83
|
+
stop: () => void;
|
|
84
|
+
}
|
|
85
|
+
export declare function startAppReports(deps: AppReportDeps): AppReports;
|
|
86
|
+
/**
|
|
87
|
+
* The tab a drive the chat attached to one app uses: a tab of that app and no other, even on the
|
|
88
|
+
* same port. None open: open the app, and wait for it to connect. Null when it never does.
|
|
89
|
+
*/
|
|
90
|
+
export declare function pickAppTab(app: {
|
|
91
|
+
key: string;
|
|
92
|
+
url: string | null;
|
|
93
|
+
}, goal: string, tabs: () => readonly AppTab[], keyOf: (sessionId: string) => string | undefined, open: (url: string) => Promise<void>, waitMs?: number): Promise<string | null>;
|
|
94
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The apps this machine has connected, told to the platform so its chat can list them by name.
|
|
3
|
+
*
|
|
4
|
+
* The chat used to ask for a `localhost` address, and an address names nothing: two projects on
|
|
5
|
+
* `localhost:3000` are one address, and one project restarted on another port is two. So an app here
|
|
6
|
+
* is a project checkout on a machine, keyed by a hash of both, and its address is only where it was
|
|
7
|
+
* last open. Neither the project's path nor the home directory leaves the machine, only their hash.
|
|
8
|
+
*
|
|
9
|
+
* Every few seconds, with or without an app open, each linked project's credential reports its apps.
|
|
10
|
+
* The answer can name apps somebody asked to bring back (opened here, headless) and say a drive is
|
|
11
|
+
* waiting, which is how a daemon with nothing open hears about one without polling every few seconds.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import { LinkPath, PLATFORM_LINK_VERSION, RETICLE_URL_PARAM, parseLinkNotices, } from '@reticlehq/core';
|
|
15
|
+
import { pickDriveSession } from './remote-drive.js';
|
|
16
|
+
/** How often apps are reported. The platform counts an app live for two and a half of these. */
|
|
17
|
+
export const LOCAL_APPS_REPORT_MS = 10_000;
|
|
18
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
19
|
+
/** How long a drive waits for an app it opened to connect before it is refused. */
|
|
20
|
+
export const APP_OPEN_WAIT_MS = 20_000;
|
|
21
|
+
const APP_OPEN_POLL_MS = 250;
|
|
22
|
+
const KEY_LENGTH = 16;
|
|
23
|
+
/** The shell a plain browser tab reports: not worth a word on a card that says what it is built with. */
|
|
24
|
+
const WEB_RUNTIME = 'web';
|
|
25
|
+
const hash = (text) => createHash('sha256').update(text).digest('hex').slice(0, KEY_LENGTH);
|
|
26
|
+
/** This machine: a stable id that is not its home path, and the name a person knows it by. */
|
|
27
|
+
export const machineOf = (hostname, home) => ({
|
|
28
|
+
id: hash(`${hostname}\u0000${home}`),
|
|
29
|
+
name: hostname.replace(/\.local$/i, ''),
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* One project checkout on one machine. Its port is not part of it. The tab's projectId is: a daemon
|
|
33
|
+
* that cannot map a project to its checkout files every such project under one shared directory.
|
|
34
|
+
*/
|
|
35
|
+
export const appKeyOf = (machineId, projectDir, projectId) => hash(`${machineId}\u0000${projectDir}\u0000${projectId ?? ''}`);
|
|
36
|
+
const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
37
|
+
/** What a refused open is told. */
|
|
38
|
+
export const NOT_AN_APP_ADDRESS = 'Reticle only opens a web address on this machine or one of the apps already open here.';
|
|
39
|
+
/**
|
|
40
|
+
* Whether the daemon may open an address the platform named: a web page on this machine, or one on
|
|
41
|
+
* an origin a connected tab already has. Never a file, and never anywhere else on the network.
|
|
42
|
+
*/
|
|
43
|
+
export function mayOpen(url, openUrls) {
|
|
44
|
+
let target;
|
|
45
|
+
try {
|
|
46
|
+
target = new URL(url);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if ('http:' !== target.protocol && 'https:' !== target.protocol)
|
|
52
|
+
return false;
|
|
53
|
+
if (LOOPBACK_HOSTS.has(target.hostname) || target.hostname.endsWith('.localhost'))
|
|
54
|
+
return true;
|
|
55
|
+
return openUrls.some((open) => {
|
|
56
|
+
try {
|
|
57
|
+
return new URL(open).origin === target.origin;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/** A project id as a name: the SDK stamps `<package>-<8 hex>`, and the hash means nothing to a person. */
|
|
65
|
+
export const nameFromProjectId = (projectId) => projectId.replace(/-[0-9a-f]{8}$/, '');
|
|
66
|
+
const RETICLE_MARKS = new Set(Object.values(RETICLE_URL_PARAM));
|
|
67
|
+
/** The address as the app knows it: the params Reticle put on it to adopt a tab are not the app's. */
|
|
68
|
+
const withoutMarks = (raw) => {
|
|
69
|
+
try {
|
|
70
|
+
const url = new URL(raw);
|
|
71
|
+
for (const mark of [...url.searchParams.keys()])
|
|
72
|
+
if (RETICLE_MARKS.has(mark))
|
|
73
|
+
url.searchParams.delete(mark);
|
|
74
|
+
return url.href;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return raw;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* The apps to report, grouped by the platform credential each one's project is linked with. The
|
|
82
|
+
* linked credential is reported even with no app open: it is how the chat knows the machine is there
|
|
83
|
+
* to open one. A tab whose project is not linked is left out: no chat can drive it.
|
|
84
|
+
*/
|
|
85
|
+
export async function appsByPlatform(tabs, linked, machineId, describe) {
|
|
86
|
+
const groups = new Map();
|
|
87
|
+
const group = (platform) => {
|
|
88
|
+
const id = `${platform.url}\u0000${platform.apiKey}`;
|
|
89
|
+
const found = groups.get(id) ?? { platform, apps: new Map() };
|
|
90
|
+
groups.set(id, found);
|
|
91
|
+
return found;
|
|
92
|
+
};
|
|
93
|
+
if (undefined !== linked)
|
|
94
|
+
group(linked);
|
|
95
|
+
// A visible tab speaks for its project over a hidden one.
|
|
96
|
+
const ordered = [...tabs].sort((a, b) => Number(a.hidden) - Number(b.hidden));
|
|
97
|
+
for (const tab of ordered) {
|
|
98
|
+
const about = await describe(tab).catch(() => undefined);
|
|
99
|
+
if (undefined === about?.platform)
|
|
100
|
+
continue;
|
|
101
|
+
const key = appKeyOf(machineId, about.dir, tab.projectId);
|
|
102
|
+
const apps = group(about.platform).apps;
|
|
103
|
+
if (apps.has(key))
|
|
104
|
+
continue;
|
|
105
|
+
const stack = [
|
|
106
|
+
...new Set([...tab.adapters, ...(tab.runtime === undefined ? [] : [tab.runtime])]),
|
|
107
|
+
].filter((part) => WEB_RUNTIME !== part);
|
|
108
|
+
apps.set(key, {
|
|
109
|
+
key,
|
|
110
|
+
name: about.name,
|
|
111
|
+
url: withoutMarks(tab.url),
|
|
112
|
+
...(tab.title === undefined ? {} : { title: tab.title }),
|
|
113
|
+
stack,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return [...groups.values()].map(({ platform, apps }) => ({ platform, apps: [...apps.values()] }));
|
|
117
|
+
}
|
|
118
|
+
export function startAppReports(deps) {
|
|
119
|
+
const doFetch = deps.fetch ?? ((url, init) => fetch(url, init));
|
|
120
|
+
let busy = false;
|
|
121
|
+
/** Notices already shown, by what they say: the platform repeats them on every report. */
|
|
122
|
+
const told = new Set();
|
|
123
|
+
const tick = async () => {
|
|
124
|
+
if (busy)
|
|
125
|
+
return;
|
|
126
|
+
busy = true;
|
|
127
|
+
try {
|
|
128
|
+
for (const { platform, apps } of await deps.apps()) {
|
|
129
|
+
try {
|
|
130
|
+
const res = await doFetch(`${platform.url}${LinkPath.APPS}`, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: {
|
|
133
|
+
'content-type': 'application/json',
|
|
134
|
+
authorization: `Bearer ${platform.apiKey}`,
|
|
135
|
+
},
|
|
136
|
+
body: JSON.stringify({
|
|
137
|
+
machine: deps.machine,
|
|
138
|
+
reticleVersion: deps.version,
|
|
139
|
+
protocol: PLATFORM_LINK_VERSION,
|
|
140
|
+
capabilities: deps.capabilities,
|
|
141
|
+
apps,
|
|
142
|
+
}),
|
|
143
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok)
|
|
146
|
+
continue;
|
|
147
|
+
const answer = (await res.json());
|
|
148
|
+
for (const notice of parseLinkNotices(answer.notices)) {
|
|
149
|
+
const key = `${platform.apiKey}\u0000${notice.text}`;
|
|
150
|
+
if (told.has(key))
|
|
151
|
+
continue;
|
|
152
|
+
told.add(key);
|
|
153
|
+
deps.notify?.(notice, platform);
|
|
154
|
+
}
|
|
155
|
+
const open = new Set(apps.map((app) => app.key));
|
|
156
|
+
for (const wake of Array.isArray(answer.wake) ? answer.wake : []) {
|
|
157
|
+
const { key, url } = (wake ?? {});
|
|
158
|
+
if ('string' !== typeof key || 'string' !== typeof url || open.has(key))
|
|
159
|
+
continue;
|
|
160
|
+
open.add(key);
|
|
161
|
+
deps.log?.(`reticle: opening ${url}, asked for from the platform chat`);
|
|
162
|
+
await deps.open(url).catch((error) => {
|
|
163
|
+
deps.log?.(`reticle: could not open ${url}: ${String(error)}`);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (true === answer.pendingDrive)
|
|
167
|
+
deps.drivePending(platform);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Offline, or the platform is down: the next report tries again.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// The credentials could not be read: nothing to report this time.
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
busy = false;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const timer = setInterval(() => void tick(), deps.intervalMs ?? LOCAL_APPS_REPORT_MS);
|
|
182
|
+
timer.unref();
|
|
183
|
+
return { tick, stop: () => clearInterval(timer) };
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The tab a drive the chat attached to one app uses: a tab of that app and no other, even on the
|
|
187
|
+
* same port. None open: open the app, and wait for it to connect. Null when it never does.
|
|
188
|
+
*/
|
|
189
|
+
export async function pickAppTab(app, goal, tabs, keyOf, open, waitMs = APP_OPEN_WAIT_MS) {
|
|
190
|
+
const ofApp = () => tabs().filter((tab) => keyOf(tab.sessionId) === app.key);
|
|
191
|
+
const now = pickDriveSession(ofApp(), goal);
|
|
192
|
+
if (now !== undefined)
|
|
193
|
+
return now;
|
|
194
|
+
if (null === app.url)
|
|
195
|
+
return null;
|
|
196
|
+
await open(app.url).catch(() => undefined);
|
|
197
|
+
for (let waited = 0; waited <= waitMs; waited += APP_OPEN_POLL_MS) {
|
|
198
|
+
const opened = pickDriveSession(ofApp(), goal);
|
|
199
|
+
if (opened !== undefined)
|
|
200
|
+
return opened;
|
|
201
|
+
await new Promise((resolve) => setTimeout(resolve, APP_OPEN_POLL_MS));
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A drive somebody asked for in the platform's chat, run here, on their own machine.
|
|
3
|
+
*
|
|
4
|
+
* The platform cannot open `localhost`, and it never tries: the chat files the drive on the platform,
|
|
5
|
+
* and this daemon — which already calls out to the platform for every Harness turn — asks whether one
|
|
6
|
+
* is waiting for its project, drives the app it has connected, and reports how it ended. Every call
|
|
7
|
+
* goes OUT from this machine, so nothing listens for the platform and no port is opened.
|
|
8
|
+
*
|
|
9
|
+
* It only asks while an app is connected. A drive taken with nothing to drive would fail for a reason
|
|
10
|
+
* the person can fix in ten seconds (open the app), and leaving it unclaimed lets the chat tell them
|
|
11
|
+
* exactly that. One drive at a time: a second request waits for the first to finish.
|
|
12
|
+
*/
|
|
13
|
+
import { PresenterTone, type DriveApplied, type DriveSpec, type SpecIgnored } from '@reticlehq/core';
|
|
14
|
+
/** The app the chat attached a drive to: `local-apps.ts`'s key, and where it was last open. */
|
|
15
|
+
export interface AttachedApp {
|
|
16
|
+
key: string;
|
|
17
|
+
url: string | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* How often to ask while an app is connected.
|
|
21
|
+
*
|
|
22
|
+
* ponytail: a poll, one small GET per interval per connected daemon; a long-poll or a stream on the
|
|
23
|
+
* platform side is the upgrade if the chat's wait for a claim ever needs to be shorter than this.
|
|
24
|
+
*/
|
|
25
|
+
export declare const REMOTE_DRIVE_POLL_MS = 3000;
|
|
26
|
+
/**
|
|
27
|
+
* How often the live picture is taken while a chat-requested drive runs. About one a second is what
|
|
28
|
+
* reads as video in the chat without turning a minute's drive into megabytes.
|
|
29
|
+
*/
|
|
30
|
+
export declare const REMOTE_DRIVE_FRAME_MS = 1000;
|
|
31
|
+
/** The same JPEG quality the platform's own checks film at: about 40 KB a picture. */
|
|
32
|
+
export declare const REMOTE_DRIVE_JPEG_QUALITY = 50;
|
|
33
|
+
/** What the driven tab's HUD says once a chat-requested drive is over. */
|
|
34
|
+
export declare const REMOTE_DRIVE_ENDED: {
|
|
35
|
+
readonly PROVED: "Harness drive finished: proved";
|
|
36
|
+
readonly NOT_PROVED: "Harness drive finished: not proved";
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* End the driven tab's session once its drive is over, so its HUD stops counting "planning next
|
|
40
|
+
* action" for an agent that is not there. Auto-ended, not ended: the next agent or drive revives it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function endDrivenTab(tab: {
|
|
43
|
+
autoEnd(text: string, tone: PresenterTone): void;
|
|
44
|
+
} | undefined, outcome: RemoteDriveOutcome): void;
|
|
45
|
+
type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
|
|
46
|
+
export interface RemoteDriveOutcome {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
summary: string;
|
|
49
|
+
/** The verdict on the goal (see `driveVerdict`), so the platform keeps the drive as a check. */
|
|
50
|
+
verdict?: DriveVerdict;
|
|
51
|
+
/** The address the drive was on, for the check the platform keeps. */
|
|
52
|
+
url?: string;
|
|
53
|
+
/** The runs this drive syncs as, so the platform shows the drive once. */
|
|
54
|
+
runIds?: readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* Whether any picture of the drive reached the platform. False for a drive in the person's own
|
|
57
|
+
* tab, which only the SDK reaches and no camera does, so the chat can say why there is no video.
|
|
58
|
+
* Set by `startRemoteDrives`, never by the drive.
|
|
59
|
+
*/
|
|
60
|
+
filmed?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export type DriveVerdict = 'yes' | 'no' | 'unknown';
|
|
63
|
+
export interface RemoteDriveDeps {
|
|
64
|
+
/** The environment with this machine's linked credential in it, resolved on every ask. */
|
|
65
|
+
env: () => Promise<Record<string, string | undefined>>;
|
|
66
|
+
/** Whether an app is connected to drive. */
|
|
67
|
+
connected: () => boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The tab to drive for `goal`, or undefined to let the drive choose. One tab, picked once and used
|
|
70
|
+
* for the whole drive and its pictures: with two tabs open, a drive that names none is refused on
|
|
71
|
+
* every call ("multiple sessions connected"), and the pictures would be of whichever tab was asked.
|
|
72
|
+
* Null: no connected tab belongs to the project `apiKey` is for, so the drive is refused.
|
|
73
|
+
* `app`: the app the chat attached the drive to, when it named one; only its tabs may be driven.
|
|
74
|
+
*/
|
|
75
|
+
pick?: (goal: string, apiKey: string, app?: AttachedApp) => Promise<string | null | undefined> | string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Make ready the tab a drive with a spec runs in: the person's own (as `pick` would choose it) or
|
|
78
|
+
* one opened for it, headless or in a window, with the HUD as the spec asks. Answers what it applied
|
|
79
|
+
* and what it could not. A null `sessionId` refuses the drive, as a null from `pick` does.
|
|
80
|
+
* Absent: `pick` chooses, and the spec's other fields are reported as unavailable.
|
|
81
|
+
*/
|
|
82
|
+
prepare?: (drive: {
|
|
83
|
+
goal: string;
|
|
84
|
+
apiKey: string;
|
|
85
|
+
app?: AttachedApp;
|
|
86
|
+
spec: DriveSpec;
|
|
87
|
+
}) => Promise<{
|
|
88
|
+
sessionId: string | null | undefined;
|
|
89
|
+
applied: DriveApplied;
|
|
90
|
+
ignored: SpecIgnored[];
|
|
91
|
+
/** Why the drive cannot run, said to the person instead of the no-tab refusal. */
|
|
92
|
+
refusal?: string;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Reticle's tools on the driven tab, for a drive the platform orchestrates. Absent: a platform
|
|
96
|
+
* drive is refused, and this daemon does not report the tool-session capability.
|
|
97
|
+
*/
|
|
98
|
+
session?: (sessionId: string | undefined) => {
|
|
99
|
+
/** What it can run, as the model is shown each tool: sent once, on the first ask. */
|
|
100
|
+
tools?: readonly {
|
|
101
|
+
name: string;
|
|
102
|
+
description: string;
|
|
103
|
+
inputSchema: Record<string, unknown>;
|
|
104
|
+
}[];
|
|
105
|
+
invoke: (tool: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
106
|
+
};
|
|
107
|
+
/** The clock for how long each session call took. */
|
|
108
|
+
now?: () => number;
|
|
109
|
+
/** How a session ask waits before it is sent again. */
|
|
110
|
+
sleep?: (ms: number) => Promise<void>;
|
|
111
|
+
/** Drive the app toward `goal`, in `sessionId`. A throw is reported as a failed drive, in its own words. */
|
|
112
|
+
drive: (goal: string, sessionId: string | undefined) => Promise<RemoteDriveOutcome>;
|
|
113
|
+
/**
|
|
114
|
+
* What the driven tab shows now, as a JPEG, or undefined when it cannot be captured (a tab this
|
|
115
|
+
* daemon did not launch). Absent: the drive runs with no picture, and the chat shows its steps.
|
|
116
|
+
*/
|
|
117
|
+
frame?: (sessionId: string | undefined) => Promise<Uint8Array | undefined>;
|
|
118
|
+
frameIntervalMs?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Called once the drive is over, however it ended, so the driven tab's HUD stops reading
|
|
121
|
+
* "planning next action". Nothing else tells it: the drive has no agent to end its session.
|
|
122
|
+
*/
|
|
123
|
+
settle?: (sessionId: string | undefined, outcome: RemoteDriveOutcome) => void;
|
|
124
|
+
fetch?: FetchLike;
|
|
125
|
+
intervalMs?: number;
|
|
126
|
+
/** A line for the daemon log. */
|
|
127
|
+
log?: (line: string) => void;
|
|
128
|
+
}
|
|
129
|
+
export interface RemoteDrives {
|
|
130
|
+
/**
|
|
131
|
+
* One ask, now. Resolves once any drive it took has been reported. With `platform`, ask with that
|
|
132
|
+
* credential whether or not an app is connected: the platform said a drive is waiting for it.
|
|
133
|
+
*/
|
|
134
|
+
tick: (platform?: {
|
|
135
|
+
url: string;
|
|
136
|
+
apiKey: string;
|
|
137
|
+
}) => Promise<void>;
|
|
138
|
+
stop: () => void;
|
|
139
|
+
}
|
|
140
|
+
export declare function startRemoteDrives(deps: RemoteDriveDeps): RemoteDrives;
|
|
141
|
+
/** What a connected tab says about itself, as far as picking one goes. */
|
|
142
|
+
/** The refusal a drive gets when every connected tab belongs to some other project. */
|
|
143
|
+
export declare const NO_OWN_TAB = "No tab of this project is connected to Reticle on this machine, and a request is never run in another project's app. Open this project's app, then ask again.";
|
|
144
|
+
/**
|
|
145
|
+
* `pickDriveSession` over the tabs of the project `apiKey` is for, or null when there are none. One
|
|
146
|
+
* daemon serves every project on the machine but polls with ONE credential, so without this a
|
|
147
|
+
* request made in project A drove project B's tab and sent pictures of it to A's chat.
|
|
148
|
+
*/
|
|
149
|
+
export declare function pickOwnDriveSession(tabs: readonly DriveCandidate[], goal: string, apiKey: string, keyOf: (sessionId: string) => Promise<string | undefined>): Promise<string | null>;
|
|
150
|
+
export interface DriveCandidate {
|
|
151
|
+
sessionId: string;
|
|
152
|
+
url: string;
|
|
153
|
+
lastSeenMs: number;
|
|
154
|
+
hidden: boolean;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The tab a chat-requested drive should use: one whose address the request names, then a visible
|
|
158
|
+
* one over a hidden (throttled) one, then the one heard from most recently.
|
|
159
|
+
*/
|
|
160
|
+
export declare function pickDriveSession(tabs: readonly DriveCandidate[], goal: string): string | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* The verdict on a drive's goal: the platform's judgement of the goal when it made one, else the
|
|
163
|
+
* goals the harness checked itself. A drive that broke, proved nothing, or whose goal nobody judged
|
|
164
|
+
* is `unknown`: "it ran and nothing threw" is not "the goal was reached".
|
|
165
|
+
*/
|
|
166
|
+
export declare function driveVerdict(out: {
|
|
167
|
+
goalMet?: boolean;
|
|
168
|
+
proved: boolean;
|
|
169
|
+
error?: string;
|
|
170
|
+
goals?: readonly {
|
|
171
|
+
verified: string;
|
|
172
|
+
}[];
|
|
173
|
+
/** How the drive's checks came out. `proved` only says one RAN; this says what each answered. */
|
|
174
|
+
checks?: {
|
|
175
|
+
held: number;
|
|
176
|
+
failed: number;
|
|
177
|
+
undecided: number;
|
|
178
|
+
};
|
|
179
|
+
}): DriveVerdict;
|
|
180
|
+
export {};
|