@reticlehq/server 3.1.0 → 3.2.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 +17 -18
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-doctor.js +13 -1
- package/dist/command/cli/cli-flow-commands.d.ts +2 -1
- package/dist/command/cli/cli-kill.js +1 -1
- package/dist/command/cli/cli-parse.d.ts +2 -1
- package/dist/command/cli/cli-parse.js +5 -2
- package/dist/command/cli/cli-verify.js +2 -2
- package/dist/command/cli/cloud-cli.js +7 -6
- package/dist/command/cli/cloud-kit.js +2 -6
- package/dist/command/cli/config/config-discovery.d.ts +12 -0
- package/dist/command/cli/config/config-discovery.js +19 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
- package/dist/command/cli/doctor/doctor-rows.js +12 -0
- package/dist/command/cli/drive/drive-command.js +1 -1
- package/dist/command/cli/launch/cli-launch.d.ts +10 -2
- package/dist/command/cli/launch/cli-launch.js +32 -39
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
- package/dist/command/cli/mcp-command.js +5 -3
- package/dist/command/cli/ports/resolve/cli-port.d.ts +2 -1
- package/dist/command/cli/ports/resolve/cli-port.js +5 -1
- package/dist/command/cli/status/status-lines.d.ts +28 -0
- package/dist/command/cli/status/status-lines.js +117 -0
- package/dist/command/cli.d.ts +1 -1
- package/dist/command/cli.js +24 -177
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +48 -0
- package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
- package/dist/command/daemon/binding/loopback-reach.js +63 -0
- package/dist/command/daemon/daemon-resilience.d.ts +13 -1
- package/dist/command/daemon/daemon-resilience.js +12 -1
- package/dist/command/daemon/daemon.d.ts +2 -11
- package/dist/command/daemon/daemon.js +8 -11
- package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
- package/dist/command/daemon/lifetime/heartbeat.js +10 -0
- package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
- package/dist/command/daemon/lifetime/log-cap.js +115 -0
- package/dist/command/setup/agent-notice-memory.d.ts +16 -0
- package/dist/command/setup/agent-notice-memory.js +65 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
- package/dist/command/setup/bringup/dev-server-wait.js +16 -0
- package/dist/command/setup/bringup/ensure-daemon.js +1 -1
- package/dist/command/setup/init/init-runtime.js +13 -1
- package/dist/command/setup/node-effects.js +1 -1
- package/dist/command/setup/probe/page-probe.js +9 -2
- package/dist/command/setup/run-setup.d.ts +25 -7
- package/dist/command/setup/run-setup.js +51 -9
- package/dist/command/setup/setup-command.js +31 -3
- package/dist/command/setup/terminal/confirm.js +2 -1
- package/dist/command/update/updater.d.ts +27 -0
- package/dist/command/update/updater.js +35 -2
- package/dist/features/crawl/crawl-tools.js +3 -1
- package/dist/features/crawl/crawl.js +2 -1
- package/dist/features/harness/drive-report.d.ts +73 -0
- package/dist/features/harness/drive-report.js +196 -0
- package/dist/features/harness/driver.js +29 -8
- package/dist/features/harness/drivers.d.ts +19 -0
- package/dist/features/harness/drivers.js +19 -0
- package/dist/features/harness/fill-values.d.ts +67 -0
- package/dist/features/harness/fill-values.js +153 -0
- package/dist/features/harness/jev-driver.d.ts +188 -0
- package/dist/features/harness/jev-driver.js +664 -0
- package/dist/features/harness/openai-driver.d.ts +46 -0
- package/dist/features/harness/openai-driver.js +197 -0
- package/dist/features/harness/platform-config.d.ts +52 -0
- package/dist/features/harness/platform-config.js +89 -0
- package/dist/features/visual/visual-tools.js +26 -2
- package/dist/index.js +60 -55
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/verification-sync.d.ts +2 -2
- package/dist/judgement/runs/verification-sync.js +9 -2
- package/dist/language/flows/affected-tools.js +6 -3
- package/dist/language/flows/flow-replay-run.d.ts +25 -2
- package/dist/language/flows/flow-replay-run.js +73 -27
- package/dist/language/flows/flow-replay.d.ts +17 -0
- package/dist/language/flows/flow-replay.js +148 -100
- package/dist/language/flows/flow-result.d.ts +18 -1
- package/dist/language/flows/flow-result.js +18 -2
- package/dist/language/flows/flow-store-for-session.d.ts +2 -1
- package/dist/language/flows/flow-tools.js +8 -4
- package/dist/language/flows/flows.d.ts +8 -7
- package/dist/language/flows/flows.js +7 -3
- package/dist/language/flows/narrow-write.d.ts +4 -4
- package/dist/language/flows/replay.js +1 -1
- package/dist/language/flows/suite-selection.d.ts +15 -1
- package/dist/language/flows/suite-selection.js +1 -1
- package/dist/language/flows/verify-change-tools.js +7 -4
- package/dist/memory/cloud/account-state.d.ts +1 -1
- package/dist/memory/cloud/account-state.js +5 -4
- package/dist/memory/cloud/cloud-config.d.ts +9 -0
- package/dist/memory/cloud/cloud-config.js +9 -0
- package/dist/memory/cloud/cloud-sync.d.ts +7 -2
- package/dist/memory/cloud/cloud-sync.js +10 -5
- package/dist/memory/cloud/harness-config.d.ts +10 -0
- package/dist/memory/cloud/harness-config.js +58 -0
- package/dist/memory/cloud/harness-offer.d.ts +44 -0
- package/dist/memory/cloud/harness-offer.js +146 -0
- package/dist/memory/cloud/harness-switch.d.ts +11 -0
- package/dist/memory/cloud/harness-switch.js +53 -0
- package/dist/memory/cloud/sync-daemon.d.ts +22 -2
- package/dist/memory/cloud/sync-daemon.js +24 -0
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +2 -1
- package/dist/memory/impact/impact-store.d.ts +5 -0
- package/dist/memory/impact/impact-store.js +19 -0
- package/dist/memory/intent/intent-shard-store.js +2 -2
- package/dist/memory/intent/intent-shard.d.ts +12 -12
- package/dist/memory/journal/attach-journal.d.ts +11 -0
- package/dist/memory/journal/attach-journal.js +1 -1
- package/dist/memory/journal/journal-query.d.ts +35 -1
- package/dist/memory/journal/journal-query.js +46 -0
- package/dist/memory/journal/journal-recorder.d.ts +50 -2
- package/dist/memory/journal/journal-recorder.js +45 -7
- package/dist/memory/journal/on-disk/retention.d.ts +87 -2
- package/dist/memory/journal/on-disk/retention.js +177 -8
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +9 -0
- package/dist/memory/journal/on-disk/workspace-gitignore.js +9 -2
- package/dist/memory/journal/self-observation.d.ts +3 -0
- package/dist/memory/journal/self-observation.js +67 -0
- package/dist/memory/journal/session-end.d.ts +13 -3
- package/dist/memory/journal/session-end.js +24 -4
- package/dist/memory/journal/session-journal.d.ts +115 -5
- package/dist/memory/journal/session-journal.js +479 -32
- package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
- package/dist/memory/project/artifact-root-resolver.js +89 -0
- package/dist/memory/project/artifact-root.d.ts +39 -2
- package/dist/memory/project/artifact-root.js +58 -1
- package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
- package/dist/memory/project/dir/fill-value-store.js +88 -0
- package/dist/memory/project/dir/reticle-dir.d.ts +14 -4
- package/dist/memory/project/dir/reticle-dir.js +11 -0
- package/dist/memory/project/fs/fs-port.d.ts +22 -1
- package/dist/memory/project/fs/fs-port.js +33 -5
- package/dist/memory/project/learned-routes.d.ts +16 -2
- package/dist/memory/project/learned-routes.js +26 -10
- package/dist/memory/project/project-for-root.d.ts +30 -0
- package/dist/memory/project/project-for-root.js +43 -0
- package/dist/memory/project/project-tools.js +10 -5
- package/dist/memory/project/session-root.d.ts +35 -6
- package/dist/memory/project/session-root.js +70 -8
- package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
- package/dist/portal/bridge/auth-failure-reason.js +15 -1
- package/dist/portal/bridge/bridge.d.ts +6 -1
- package/dist/portal/bridge/bridge.js +36 -2
- package/dist/portal/session/facts/session-replaced.d.ts +19 -0
- package/dist/portal/session/facts/session-replaced.js +22 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
- package/dist/portal/session/no-session-diagnosis.js +67 -60
- package/dist/portal/session/no-session-next-action.js +3 -2
- package/dist/portal/session/no-session-watch.js +14 -10
- package/dist/portal/session/rebind.d.ts +18 -0
- package/dist/portal/session/rebind.js +38 -0
- package/dist/portal/session/session-manager.d.ts +4 -0
- package/dist/portal/session/session-manager.js +19 -1
- package/dist/portal/session/session.d.ts +21 -10
- package/dist/portal/session/session.js +29 -42
- package/dist/status-payload.d.ts +12 -1
- package/dist/status-payload.js +4 -1
- package/dist/surface/http-server.js +3 -5
- package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
- package/dist/surface/mcp/faults/mcp-outage.js +20 -4
- package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
- package/dist/surface/mcp/mcp-proxy.js +20 -9
- package/dist/surface/mcp/mcp.d.ts +10 -1
- package/dist/surface/mcp/mcp.js +59 -29
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
- package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
- package/dist/surface/mcp/proxy/proxy-log.js +7 -22
- package/dist/surface/tools/act/act-preflight.js +6 -1
- package/dist/surface/tools/act/sequence-grade.js +13 -0
- package/dist/surface/tools/act-capsule.js +27 -1
- package/dist/surface/tools/assert/assert-verdict.js +44 -17
- package/dist/surface/tools/coverage-tools.js +5 -2
- package/dist/surface/tools/dynamic-tools.js +37 -46
- package/dist/surface/tools/error-recovery.d.ts +6 -0
- package/dist/surface/tools/error-recovery.js +15 -0
- package/dist/surface/tools/explore-tools.js +80 -7
- package/dist/surface/tools/feedback-tools.js +49 -2
- package/dist/surface/tools/first-sentence.d.ts +24 -0
- package/dist/surface/tools/first-sentence.js +0 -0
- package/dist/surface/tools/harness-explore.d.ts +125 -1
- package/dist/surface/tools/harness-explore.js +401 -15
- package/dist/surface/tools/harness-plan.d.ts +76 -0
- package/dist/surface/tools/harness-plan.js +156 -0
- package/dist/surface/tools/harness-toolset.js +12 -1
- package/dist/surface/tools/invoke-tool.js +65 -5
- package/dist/surface/tools/live-call-text.d.ts +6 -0
- package/dist/surface/tools/live-call-text.js +36 -2
- package/dist/surface/tools/observe-tools.js +55 -9
- package/dist/surface/tools/read/query-windowed.d.ts +1 -1
- package/dist/surface/tools/read/query-windowed.js +1 -1
- package/dist/surface/tools/read-tools.js +1 -2
- package/dist/surface/tools/tool-kit.d.ts +18 -3
- package/dist/surface/tools/tools-helpers.d.ts +8 -7
- package/dist/surface/tools/tools-helpers.js +11 -13
- package/dist/surface/tools/tools.js +6 -1
- package/dist/telemetry/bug-found.js +32 -2
- package/dist/telemetry/feedback-context.js +3 -3
- package/dist/telemetry/harness-drive.d.ts +6 -0
- package/dist/telemetry/harness-drive.js +39 -0
- package/dist/telemetry/mcp-connection.d.ts +14 -1
- package/dist/telemetry/mcp-connection.js +17 -2
- package/dist/telemetry/onboarding-firsts.d.ts +30 -1
- package/dist/telemetry/onboarding-firsts.js +54 -12
- package/dist/telemetry/verification-of.js +31 -3
- package/docs/agent-cheatsheet.md +7 -3
- package/docs/autodrive.md +42 -2
- package/docs/benchmarks.md +1 -1
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/cloud.mdx +3 -3
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/status.mdx +35 -20
- package/docs/cli.mdx +8 -18
- package/docs/desktop.mdx +1 -1
- package/docs/faq.mdx +1 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/predicates.mdx +16 -0
- package/docs/system-map.md +6 -4
- package/docs/telemetry.md +2 -1
- package/docs/token-efficiency.md +2 -2
- package/docs/tools/overview.mdx +1 -1
- package/docs/troubleshooting.mdx +8 -2
- package/docs/usage.md +1 -1
- package/package.json +6 -6
|
@@ -6,6 +6,7 @@ import { dirname, join } from 'node:path';
|
|
|
6
6
|
import { loadManifest, saveManifest } from './update-checker.js';
|
|
7
7
|
import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../version/identity/server-version.js';
|
|
8
8
|
import { log } from '../../log.js';
|
|
9
|
+
import { windowsShellArg } from '@reticlehq/init';
|
|
9
10
|
import { TelemetryEventKind } from '@reticlehq/core/telemetry';
|
|
10
11
|
import { getTelemetry } from '../../telemetry/telemetry.js';
|
|
11
12
|
import { wasNudged } from './nudge-credit.js';
|
|
@@ -34,7 +35,6 @@ export async function reportVersionChange(from, to, direction) {
|
|
|
34
35
|
/* best-effort, like every other send */
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
const NPM_BIN = NodePlatform.WINDOWS === platform() ? 'npm.cmd' : 'npm';
|
|
38
38
|
const NPM_TIMEOUT_MS = 120_000;
|
|
39
39
|
/** How this reticle process was launched — determines which npm strategy to use for updates. */
|
|
40
40
|
const ExecutionKind = {
|
|
@@ -91,9 +91,42 @@ function findLocalProjectRoot() {
|
|
|
91
91
|
dir = parent;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Spawn npm, through a shell on Windows because `npm.cmd` cannot be spawned without one.
|
|
96
|
+
*
|
|
97
|
+
* Since the CVE-2024-27980 fix -- in every supported Node -- spawning a `.cmd` with no shell throws
|
|
98
|
+
* EINVAL. This repo has already paid for that lesson once, at `init/src/node-io.ts`: `reticle setup
|
|
99
|
+
* mcp` probed for the Claude CLI with a bare `execFileSync`, the probe threw, and the installer
|
|
100
|
+
* told the user their machine had no Claude Code on it. The same shape sat here, in the SELF-UPDATE
|
|
101
|
+
* path -- the channel every other fix reaches a user through, and the one place a silent failure
|
|
102
|
+
* strands somebody on an old version indefinitely.
|
|
103
|
+
*
|
|
104
|
+
* Arguments are quoted with init's own rule rather than a second copy of it. They are internal
|
|
105
|
+
* today (`install`, `-g`, and a package spec whose version comes from the registry), but a shell
|
|
106
|
+
* turns "internal" into "whatever the registry said", and the quoting is one import.
|
|
107
|
+
*/
|
|
108
|
+
export function npmSpawn(args,
|
|
109
|
+
/**
|
|
110
|
+
* Injected so the Windows branch is testable on every platform.
|
|
111
|
+
*
|
|
112
|
+
* The bug this fixes could only be OBSERVED on Windows, and a check that can only fail on a
|
|
113
|
+
* machine nobody here runs is a check that fails in CI at the earliest and in the field at the
|
|
114
|
+
* latest. The rule is a pure function of the platform string, so it is one.
|
|
115
|
+
*/
|
|
116
|
+
on = platform()) {
|
|
117
|
+
const bin = NodePlatform.WINDOWS === on ? 'npm.cmd' : 'npm';
|
|
118
|
+
if (NodePlatform.WINDOWS !== on)
|
|
119
|
+
return { file: bin, argv: args, shell: false };
|
|
120
|
+
return { file: [bin, ...args.map(windowsShellArg)].join(' '), argv: [], shell: true };
|
|
121
|
+
}
|
|
94
122
|
function runNpm(args, opts = {}) {
|
|
123
|
+
const spawn = npmSpawn(args);
|
|
95
124
|
return new Promise((resolve, reject) => {
|
|
96
|
-
execFile(
|
|
125
|
+
execFile(spawn.file, spawn.argv, {
|
|
126
|
+
timeout: NPM_TIMEOUT_MS,
|
|
127
|
+
...(spawn.shell ? { shell: true } : {}),
|
|
128
|
+
...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
|
|
129
|
+
}, (err, _stdout, stderr) => {
|
|
97
130
|
if (err !== null) {
|
|
98
131
|
reject(new Error(`npm ${args.join(' ')} failed: ${stderr !== '' ? stderr : err.message}`));
|
|
99
132
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { sessionRoot } from '../../memory/project/session-root.js';
|
|
3
|
+
import { projectForRoot } from '../../memory/project/project-for-root.js';
|
|
2
4
|
import { ReticleTool } from '@reticlehq/core';
|
|
3
5
|
import { asNumber, asString } from '@reticlehq/core';
|
|
4
6
|
import { stepCountSchema, timeoutMsSchema } from '../../surface/tools/args/numeric-bounds.js';
|
|
@@ -80,7 +82,7 @@ export const CRAWL_TOOLS = [
|
|
|
80
82
|
...routesFromEvents(session.eventsSince(since)),
|
|
81
83
|
];
|
|
82
84
|
if (routes.length > 0)
|
|
83
|
-
await deps.
|
|
85
|
+
await projectForRoot(deps, sessionRoot(deps, session.id)).recordRoutes(routes);
|
|
84
86
|
return report;
|
|
85
87
|
},
|
|
86
88
|
},
|
|
@@ -8,7 +8,8 @@ import { span } from '../../trace.js';
|
|
|
8
8
|
import { ActionType, CRAWL_DEFAULTS, CrawlAnomalyKind, DANGEROUS_ACTION_CONFIRM_ARG, EventType, ReticleCommand, } from '@reticlehq/core';
|
|
9
9
|
import { crawlEmptyNote } from './crawl-empty.js';
|
|
10
10
|
import { asNumber, asRecord, asString } from '@reticlehq/core';
|
|
11
|
-
import { parseInteractive
|
|
11
|
+
import { parseInteractive } from '@reticlehq/core';
|
|
12
|
+
import { sourceOf } from '../../surface/tools/tools-helpers.js';
|
|
12
13
|
import { isSessionReplacedError } from '../../portal/session/facts/session-replaced.js';
|
|
13
14
|
import { ReticleTool } from '@reticlehq/core';
|
|
14
15
|
import { findContradictions } from '@reticlehq/engine/disagreement/contradictions.js';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a drive did, written from the evidence rather than narrated by the model that drove.
|
|
3
|
+
*
|
|
4
|
+
* `HarnessResult.summary` is the driver's own account, and it has always carried a warning that
|
|
5
|
+
* nothing downstream grades it. That was a caveat when the driver was a language model. It became a
|
|
6
|
+
* hole the moment a System One model could drive: Jev answers typed questions and cannot produce a
|
|
7
|
+
* sentence, so `summary` came back EMPTY, and an agent calling `explore` got a result with no
|
|
8
|
+
* account of what had happened in it at all.
|
|
9
|
+
*
|
|
10
|
+
* Restoring prose would have been the obvious fix and the wrong one. The drive already records
|
|
11
|
+
* every call it made, with the arguments it used and the verdict the engine returned — so the
|
|
12
|
+
* account can be DERIVED. That is strictly better than a narration: a model summarising its own
|
|
13
|
+
* drive is the one witness with a reason to round "unknown" up to "worked", and this cannot, because
|
|
14
|
+
* it is not writing about the drive, it is reading it.
|
|
15
|
+
*
|
|
16
|
+
* Written for the agent on the other end of the MCP call. It leads with what was proved, because
|
|
17
|
+
* that is what a coding agent has to gate on, and it says "not proved" in those words rather than
|
|
18
|
+
* leaving a verdict of `unknown` to be read as a pass.
|
|
19
|
+
*/
|
|
20
|
+
import type { ToolOutcome } from './harness.js';
|
|
21
|
+
/** One driven action, reduced to what a reader needs: what was done, and what it proved. */
|
|
22
|
+
export interface DrivenStep {
|
|
23
|
+
/** The action verb, e.g. `click`. */
|
|
24
|
+
action: string;
|
|
25
|
+
/** The element, named the way a person would say it, falling back to the ref. */
|
|
26
|
+
target: string;
|
|
27
|
+
/** The engine's verdict for this step, when the step declared anything. */
|
|
28
|
+
verified: string | undefined;
|
|
29
|
+
/** The one sentence naming the deciding evidence. */
|
|
30
|
+
because: string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* What the drive CLAIMED would happen, in the words it declared before acting.
|
|
33
|
+
*
|
|
34
|
+
* Carried because "3 action(s) FAILED" is not triageable without it. A failure is either a defect
|
|
35
|
+
* in the app or a wrong guess by the driver, those need opposite responses, and the declared
|
|
36
|
+
* consequence is the only thing that tells them apart. Shown on failures only: on a pass it is
|
|
37
|
+
* noise, and the report is read every drive.
|
|
38
|
+
*/
|
|
39
|
+
claimed: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Replays this drive ran, and what each came back with.
|
|
43
|
+
*
|
|
44
|
+
* The verdict is `status`, not `passed`. Reading the wrong key reported ten replays as "undecided"
|
|
45
|
+
* on a run where every one of them had answered — which is the same false-nothing this report was
|
|
46
|
+
* written to stop, made by the report itself.
|
|
47
|
+
*
|
|
48
|
+
* Three outcomes, kept apart because they mean different things to whoever reads this. `ok` is the
|
|
49
|
+
* journey still holding. `error` is it failing. `drift` is the flow's own anchors no longer
|
|
50
|
+
* resolving — the app moved under a recording, which is a finding about the RECORDING rather than
|
|
51
|
+
* proof the feature broke, and collapsing it into "failed" would send somebody to fix working code.
|
|
52
|
+
*/
|
|
53
|
+
export declare const ReplayOutcome: {
|
|
54
|
+
readonly OK: "ok";
|
|
55
|
+
readonly DRIFT: "drift";
|
|
56
|
+
readonly ERROR: "error";
|
|
57
|
+
};
|
|
58
|
+
export declare function replayedFlows(toolCalls: readonly ToolOutcome[]): {
|
|
59
|
+
name: string;
|
|
60
|
+
status: string | undefined;
|
|
61
|
+
}[];
|
|
62
|
+
/** Reduce the raw call log to the actions that actually drove the app. */
|
|
63
|
+
export declare function drivenSteps(toolCalls: readonly ToolOutcome[]): DrivenStep[];
|
|
64
|
+
/**
|
|
65
|
+
* The account, as lines.
|
|
66
|
+
*
|
|
67
|
+
* Proved and not-proved are counted separately and both are stated, because the failure mode this
|
|
68
|
+
* whole product exists to prevent is a reader taking "we did twelve things" for "twelve things
|
|
69
|
+
* work". `unknown` is deliberately grouped under NOT proved and labelled as undecided evidence
|
|
70
|
+
* rather than as a failure — it calls for a better check, not a code change, and collapsing the two
|
|
71
|
+
* would send an agent to rewrite working code.
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeDrive(toolCalls: readonly ToolOutcome[], savedFlows: readonly string[]): string;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a drive did, written from the evidence rather than narrated by the model that drove.
|
|
3
|
+
*
|
|
4
|
+
* `HarnessResult.summary` is the driver's own account, and it has always carried a warning that
|
|
5
|
+
* nothing downstream grades it. That was a caveat when the driver was a language model. It became a
|
|
6
|
+
* hole the moment a System One model could drive: Jev answers typed questions and cannot produce a
|
|
7
|
+
* sentence, so `summary` came back EMPTY, and an agent calling `explore` got a result with no
|
|
8
|
+
* account of what had happened in it at all.
|
|
9
|
+
*
|
|
10
|
+
* Restoring prose would have been the obvious fix and the wrong one. The drive already records
|
|
11
|
+
* every call it made, with the arguments it used and the verdict the engine returned — so the
|
|
12
|
+
* account can be DERIVED. That is strictly better than a narration: a model summarising its own
|
|
13
|
+
* drive is the one witness with a reason to round "unknown" up to "worked", and this cannot, because
|
|
14
|
+
* it is not writing about the drive, it is reading it.
|
|
15
|
+
*
|
|
16
|
+
* Written for the agent on the other end of the MCP call. It leads with what was proved, because
|
|
17
|
+
* that is what a coding agent has to gate on, and it says "not proved" in those words rather than
|
|
18
|
+
* leaving a verdict of `unknown` to be read as a pass.
|
|
19
|
+
*/
|
|
20
|
+
import { asRecord, asString, ReticleTool, Verified } from '@reticlehq/core';
|
|
21
|
+
/** How many steps are listed before the account starts counting instead of naming. */
|
|
22
|
+
const MAX_LISTED = 12;
|
|
23
|
+
/** Actions that change the app. Reads are not part of the story of what was driven. */
|
|
24
|
+
const DRIVING_TOOLS = new Set([
|
|
25
|
+
ReticleTool.ACT,
|
|
26
|
+
ReticleTool.ACT_AND_WAIT,
|
|
27
|
+
ReticleTool.ACT_SEQUENCE,
|
|
28
|
+
ReticleTool.NAVIGATE,
|
|
29
|
+
]);
|
|
30
|
+
/**
|
|
31
|
+
* The human-readable name of what was acted on.
|
|
32
|
+
*
|
|
33
|
+
* The ref (`e42`) is meaningless to the agent reading this — it is a handle that expired when the
|
|
34
|
+
* page changed. A description that says `button "Create"` survives being read tomorrow, so the ref
|
|
35
|
+
* is only the fallback.
|
|
36
|
+
*/
|
|
37
|
+
function targetOf(args, result) {
|
|
38
|
+
const described = asString(result['element']) ?? asString(result['name']);
|
|
39
|
+
if (described !== undefined && 0 < described.length)
|
|
40
|
+
return described;
|
|
41
|
+
const target = asRecord(args['target']);
|
|
42
|
+
for (const key of ['testid', 'text', 'label', 'name']) {
|
|
43
|
+
const value = asString(target[key]);
|
|
44
|
+
if (value !== undefined && 0 < value.length)
|
|
45
|
+
return `${key}=${value}`;
|
|
46
|
+
}
|
|
47
|
+
const url = asString(args['url']);
|
|
48
|
+
if (url !== undefined && 0 < url.length)
|
|
49
|
+
return url;
|
|
50
|
+
return asString(args['ref']) ?? 'the page';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Replays this drive ran, and what each came back with.
|
|
54
|
+
*
|
|
55
|
+
* The verdict is `status`, not `passed`. Reading the wrong key reported ten replays as "undecided"
|
|
56
|
+
* on a run where every one of them had answered — which is the same false-nothing this report was
|
|
57
|
+
* written to stop, made by the report itself.
|
|
58
|
+
*
|
|
59
|
+
* Three outcomes, kept apart because they mean different things to whoever reads this. `ok` is the
|
|
60
|
+
* journey still holding. `error` is it failing. `drift` is the flow's own anchors no longer
|
|
61
|
+
* resolving — the app moved under a recording, which is a finding about the RECORDING rather than
|
|
62
|
+
* proof the feature broke, and collapsing it into "failed" would send somebody to fix working code.
|
|
63
|
+
*/
|
|
64
|
+
export const ReplayOutcome = { OK: 'ok', DRIFT: 'drift', ERROR: 'error' };
|
|
65
|
+
export function replayedFlows(toolCalls) {
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const call of toolCalls) {
|
|
68
|
+
if (ReticleTool.FLOW_REPLAY !== call.name)
|
|
69
|
+
continue;
|
|
70
|
+
const name = asString(asRecord(call.args)['flowName']) ?? 'a flow';
|
|
71
|
+
out.push({
|
|
72
|
+
name,
|
|
73
|
+
status: call.isError ? ReplayOutcome.ERROR : asString(asRecord(call.result)['status']),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The declared consequence, as a short phrase rather than a JSON dump.
|
|
80
|
+
*
|
|
81
|
+
* One line per kind, because a reader triaging a red needs "it claimed a POST" and not the
|
|
82
|
+
* predicate's wire shape. Unknown kinds fall back to the kind name, which is still more than the
|
|
83
|
+
* nothing this used to say.
|
|
84
|
+
*/
|
|
85
|
+
function describeClaim(until) {
|
|
86
|
+
const p = asRecord(until);
|
|
87
|
+
const kind = asString(p['kind']);
|
|
88
|
+
if (kind === undefined)
|
|
89
|
+
return undefined;
|
|
90
|
+
if ('signal' === kind) {
|
|
91
|
+
const name = asString(p['name']);
|
|
92
|
+
return name === undefined ? 'a signal' : `signal ${name}`;
|
|
93
|
+
}
|
|
94
|
+
if ('net' === kind) {
|
|
95
|
+
const method = asString(p['method']);
|
|
96
|
+
const url = asString(p['urlContains']);
|
|
97
|
+
return `a ${method ?? ''} request${url === undefined ? '' : ` to ${url}`}`.replace(' ', ' ');
|
|
98
|
+
}
|
|
99
|
+
if ('route' === kind)
|
|
100
|
+
return `a route containing ${asString(p['contains']) ?? '?'}`;
|
|
101
|
+
if ('not' === kind) {
|
|
102
|
+
const inner = describeClaim(p['predicate']);
|
|
103
|
+
return inner === undefined ? 'not something' : `to leave ${inner}`;
|
|
104
|
+
}
|
|
105
|
+
if ('anyOf' === kind)
|
|
106
|
+
return 'a request or a signal';
|
|
107
|
+
return kind;
|
|
108
|
+
}
|
|
109
|
+
/** Reduce the raw call log to the actions that actually drove the app. */
|
|
110
|
+
export function drivenSteps(toolCalls) {
|
|
111
|
+
const steps = [];
|
|
112
|
+
for (const call of toolCalls) {
|
|
113
|
+
if (!DRIVING_TOOLS.has(call.name))
|
|
114
|
+
continue;
|
|
115
|
+
const args = asRecord(call.args);
|
|
116
|
+
const result = asRecord(call.result);
|
|
117
|
+
const action = ReticleTool.NAVIGATE === call.name ? 'navigate' : (asString(args['action']) ?? 'act');
|
|
118
|
+
steps.push({
|
|
119
|
+
action,
|
|
120
|
+
target: targetOf(args, result),
|
|
121
|
+
verified: call.isError ? 'error' : asString(result['verified']),
|
|
122
|
+
because: call.isError ? asString(result['error']) : asString(result['because']),
|
|
123
|
+
claimed: describeClaim(args['until']),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return steps;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The account, as lines.
|
|
130
|
+
*
|
|
131
|
+
* Proved and not-proved are counted separately and both are stated, because the failure mode this
|
|
132
|
+
* whole product exists to prevent is a reader taking "we did twelve things" for "twelve things
|
|
133
|
+
* work". `unknown` is deliberately grouped under NOT proved and labelled as undecided evidence
|
|
134
|
+
* rather than as a failure — it calls for a better check, not a code change, and collapsing the two
|
|
135
|
+
* would send an agent to rewrite working code.
|
|
136
|
+
*/
|
|
137
|
+
export function describeDrive(toolCalls, savedFlows) {
|
|
138
|
+
const steps = drivenSteps(toolCalls);
|
|
139
|
+
const replays = replayedFlows(toolCalls);
|
|
140
|
+
/*
|
|
141
|
+
* A run that only REPLAYED is not a run that did nothing.
|
|
142
|
+
*
|
|
143
|
+
* "Nothing was driven" was true of the actions and wrong about the run: sixteen recorded journeys
|
|
144
|
+
* replayed deterministically, for zero model tokens, and the report called it empty. That is the
|
|
145
|
+
* cheap half of the plan working exactly as intended, and it has to read as a result.
|
|
146
|
+
*/
|
|
147
|
+
const replayLine = 0 === replays.length
|
|
148
|
+
? undefined
|
|
149
|
+
: (() => {
|
|
150
|
+
const held = replays.filter((r) => ReplayOutcome.OK === r.status);
|
|
151
|
+
const failed = replays.filter((r) => ReplayOutcome.ERROR === r.status);
|
|
152
|
+
const drifted = replays.filter((r) => ReplayOutcome.DRIFT === r.status);
|
|
153
|
+
const lines = [
|
|
154
|
+
`Replayed ${String(replays.length)} recorded journey(s) with NO model in the loop: ` +
|
|
155
|
+
`${String(held.length)} still hold, ${String(failed.length)} failed, ${String(drifted.length)} drifted.`,
|
|
156
|
+
];
|
|
157
|
+
if (0 < failed.length)
|
|
158
|
+
lines.push(` FAILED: ${failed.map((r) => r.name).join(', ')} — regressions in journeys that used to pass.`);
|
|
159
|
+
if (0 < drifted.length)
|
|
160
|
+
lines.push(` DRIFTED: ${drifted.map((r) => r.name).join(', ')} — the app moved under these recordings; the flow needs re-anchoring, not the app fixing.`);
|
|
161
|
+
return lines.join('\n');
|
|
162
|
+
})();
|
|
163
|
+
if (0 === steps.length)
|
|
164
|
+
return (replayLine ??
|
|
165
|
+
'Nothing was driven and nothing was replayed: the run made no action against the app.');
|
|
166
|
+
const proved = steps.filter((step) => Verified.YES === step.verified);
|
|
167
|
+
const failed = steps.filter((step) => Verified.NO === step.verified);
|
|
168
|
+
const undecided = steps.filter((step) => Verified.YES !== step.verified && Verified.NO !== step.verified);
|
|
169
|
+
const lines = [
|
|
170
|
+
...(replayLine === undefined ? [] : [replayLine]),
|
|
171
|
+
`Drove ${String(steps.length)} action(s): ${String(proved.length)} proved, ` +
|
|
172
|
+
`${String(failed.length)} failed, ${String(undecided.length)} not decided.`,
|
|
173
|
+
];
|
|
174
|
+
for (const step of steps.slice(0, MAX_LISTED)) {
|
|
175
|
+
const verdict = step.verified ?? 'nothing declared';
|
|
176
|
+
const because = step.because === undefined ? '' : ` — ${step.because}`;
|
|
177
|
+
// The claim, on failures only: it is what separates "the app is broken" from "the drive guessed
|
|
178
|
+
// wrong", and those two readings need opposite responses from whoever reads this.
|
|
179
|
+
// Shown on anything that did not pass, not just on a red. `no-fault` means "the consequence was
|
|
180
|
+
// already true, so this proved nothing" -- which is unreadable without knowing WHAT was claimed,
|
|
181
|
+
// and is the line that exposed a GET offer being satisfied by a dashboard's own polling.
|
|
182
|
+
const claimed = Verified.YES !== step.verified && step.claimed !== undefined
|
|
183
|
+
? ` (claimed ${step.claimed})`
|
|
184
|
+
: '';
|
|
185
|
+
lines.push(` ${step.action} ${step.target}: ${verdict}${claimed}${because}`);
|
|
186
|
+
}
|
|
187
|
+
if (MAX_LISTED < steps.length)
|
|
188
|
+
lines.push(` … and ${String(steps.length - MAX_LISTED)} more.`);
|
|
189
|
+
if (0 < failed.length)
|
|
190
|
+
lines.push(`${String(failed.length)} action(s) FAILED — the app did not do what the drive declared it would. These are findings.`);
|
|
191
|
+
if (0 < undecided.length)
|
|
192
|
+
lines.push(`${String(undecided.length)} action(s) were NOT PROVED. That is undecided evidence, not a failure: it calls for a better check, not a code change.`);
|
|
193
|
+
if (0 < savedFlows.length)
|
|
194
|
+
lines.push(`Replay any of this without a model: reticle_verify { action: "flows" } — saved ${savedFlows.join(', ')}.`);
|
|
195
|
+
return lines.join('\n');
|
|
196
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* purpose.
|
|
12
12
|
*/
|
|
13
13
|
import { z } from 'zod';
|
|
14
|
-
import { ReticleEnv } from '@reticlehq/core';
|
|
14
|
+
import { ReticleEnv, apiKeyFrom } from '@reticlehq/core';
|
|
15
15
|
/**
|
|
16
16
|
* The default driver model.
|
|
17
17
|
*
|
|
@@ -67,16 +67,37 @@ const MessageResponse = z.object({
|
|
|
67
67
|
* routine answer a caller offers a way out of, not a failure.
|
|
68
68
|
*/
|
|
69
69
|
export function harnessOptionsFromEnv(env) {
|
|
70
|
-
const apiKey = env[ReticleEnv.HARNESS_KEY];
|
|
71
|
-
if (apiKey === undefined || 0 === apiKey.length)
|
|
72
|
-
return undefined;
|
|
73
70
|
const model = env[ReticleEnv.HARNESS_MODEL];
|
|
74
71
|
const baseUrl = env[ReticleEnv.HARNESS_BASE_URL];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
const withOptions = (apiKey, fallbackUrl) => {
|
|
73
|
+
const url = baseUrl === undefined || 0 === baseUrl.length ? fallbackUrl : baseUrl;
|
|
74
|
+
return {
|
|
75
|
+
apiKey,
|
|
76
|
+
...(model === undefined || 0 === model.length ? {} : { model }),
|
|
77
|
+
...(url === undefined || 0 === url.length ? {} : { baseUrl: url }),
|
|
78
|
+
};
|
|
79
79
|
};
|
|
80
|
+
const apiKey = env[ReticleEnv.HARNESS_KEY];
|
|
81
|
+
if (apiKey !== undefined && 0 < apiKey.length)
|
|
82
|
+
return withOptions(apiKey);
|
|
83
|
+
/**
|
|
84
|
+
* The platform key, against the platform.
|
|
85
|
+
*
|
|
86
|
+
* Reticle's own service answers the Messages API at `/v1/messages` and forwards with ITS provider
|
|
87
|
+
* key, so a user who has run `reticle link` can drive with a frontier model having never held an
|
|
88
|
+
* Anthropic key at all. Without this they would be told to set `ANTHROPIC_API_KEY` to a value
|
|
89
|
+
* beginning `rk_live_` — which works, and reads like a mistake, and is the sort of instruction
|
|
90
|
+
* people correct on the way past and then cannot explain why nothing runs.
|
|
91
|
+
*
|
|
92
|
+
* An explicit `ANTHROPIC_API_KEY` still wins: someone who exported one meant it.
|
|
93
|
+
*/
|
|
94
|
+
const cloudKey = apiKeyFrom(env);
|
|
95
|
+
const cloudUrl = env[ReticleEnv.CLOUD_URL];
|
|
96
|
+
if (cloudKey === undefined || 0 === cloudKey.length)
|
|
97
|
+
return undefined;
|
|
98
|
+
if (cloudUrl === undefined || 0 === cloudUrl.length)
|
|
99
|
+
return undefined;
|
|
100
|
+
return withOptions(cloudKey, cloudUrl);
|
|
80
101
|
}
|
|
81
102
|
/** Build a driver backed by the Messages API. */
|
|
82
103
|
export function harnessDriver(options) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names of the drivers the harness can be asked for.
|
|
3
|
+
*
|
|
4
|
+
* A leaf module on purpose. These used to be re-exported from `harness-explore.ts`, which sits in
|
|
5
|
+
* the tool surface and is reached, through the toolset and the tool table, from the very tool that
|
|
6
|
+
* needs to declare them as an enum — a cycle that left the list `[undefined, undefined]` at the
|
|
7
|
+
* moment zod read it, and failed inside a JSON-schema parser rather than anywhere near the cause.
|
|
8
|
+
* Nothing here imports anything, so nothing can be half-initialised when it is read.
|
|
9
|
+
*/
|
|
10
|
+
/** Generates its tool calls as text, and names the journeys it records. The default. */
|
|
11
|
+
export declare const ANTHROPIC_DRIVER_NAME = "anthropic";
|
|
12
|
+
/** Answers typed questions; picks from the elements on the page and cannot invent one. */
|
|
13
|
+
export declare const JEV_DRIVER_NAME = "jev";
|
|
14
|
+
/** Generates its tool calls, over Chat Completions. Here so an A/B has a third arm. */
|
|
15
|
+
export declare const OPENAI_DRIVER_NAME = "openai";
|
|
16
|
+
/** What a caller may name. Order is the order a reader sees them in the tool description. */
|
|
17
|
+
export declare const DRIVER_NAMES: readonly ["anthropic", "jev", "openai"];
|
|
18
|
+
/** An injected driver: not selectable, because only a caller in-process can supply one. */
|
|
19
|
+
export declare const CUSTOM_DRIVER_NAME = "custom";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names of the drivers the harness can be asked for.
|
|
3
|
+
*
|
|
4
|
+
* A leaf module on purpose. These used to be re-exported from `harness-explore.ts`, which sits in
|
|
5
|
+
* the tool surface and is reached, through the toolset and the tool table, from the very tool that
|
|
6
|
+
* needs to declare them as an enum — a cycle that left the list `[undefined, undefined]` at the
|
|
7
|
+
* moment zod read it, and failed inside a JSON-schema parser rather than anywhere near the cause.
|
|
8
|
+
* Nothing here imports anything, so nothing can be half-initialised when it is read.
|
|
9
|
+
*/
|
|
10
|
+
/** Generates its tool calls as text, and names the journeys it records. The default. */
|
|
11
|
+
export const ANTHROPIC_DRIVER_NAME = 'anthropic';
|
|
12
|
+
/** Answers typed questions; picks from the elements on the page and cannot invent one. */
|
|
13
|
+
export const JEV_DRIVER_NAME = 'jev';
|
|
14
|
+
/** Generates its tool calls, over Chat Completions. Here so an A/B has a third arm. */
|
|
15
|
+
export const OPENAI_DRIVER_NAME = 'openai';
|
|
16
|
+
/** What a caller may name. Order is the order a reader sees them in the tool description. */
|
|
17
|
+
export const DRIVER_NAMES = [ANTHROPIC_DRIVER_NAME, JEV_DRIVER_NAME, OPENAI_DRIVER_NAME];
|
|
18
|
+
/** An injected driver: not selectable, because only a caller in-process can supply one. */
|
|
19
|
+
export const CUSTOM_DRIVER_NAME = 'custom';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to type into a field, and the one place in a drive worth paying a generating model for.
|
|
3
|
+
*
|
|
4
|
+
* A System One model answers typed questions against a state; it cannot write a string. That is not
|
|
5
|
+
* a shortcoming to route around everywhere — it is why the drive is two orders of magnitude cheaper,
|
|
6
|
+
* and every decision it makes (which tool, which element, what should follow) is a decision, not a
|
|
7
|
+
* composition. But a text field is a composition. "A card number", "a search term that returns
|
|
8
|
+
* results", "a description a human would write" cannot be enumerated from the page, and a label-keyed
|
|
9
|
+
* table of guesses is what stands in for them today.
|
|
10
|
+
*
|
|
11
|
+
* ## Why this is escalation rather than partnership
|
|
12
|
+
*
|
|
13
|
+
* Put a generating model in the per-turn loop and you pay its per-turn tax on every step, which is
|
|
14
|
+
* the cost the cheap driver exists to avoid. Typing is a small minority of what a drive does —
|
|
15
|
+
* clicks and navigations are the overwhelming majority — so the model is called for the few steps
|
|
16
|
+
* that compose text, never for the many that select, and a drive with no text fields never calls it
|
|
17
|
+
* at all.
|
|
18
|
+
*
|
|
19
|
+
* ## Why it is cached, and cached where it is
|
|
20
|
+
*
|
|
21
|
+
* A generated value is paid for ONCE and then belongs to the project: the same field on the same
|
|
22
|
+
* app wants the same value tomorrow, and a replay must send exactly what the recording sent or it
|
|
23
|
+
* is not a replay. Writing them to `.reticle` makes the second drive free, makes the values
|
|
24
|
+
* reviewable by a human who can correct a bad one by hand, and keeps them out of the model's way
|
|
25
|
+
* entirely. It is the same economics as the flows themselves: pay once, replay forever.
|
|
26
|
+
*
|
|
27
|
+
* ## What it must never do
|
|
28
|
+
*
|
|
29
|
+
* Fill a slot, never propose a call. The driver decides what to click and what to claim; this
|
|
30
|
+
* answers one question — "what text goes in this box" — and a wrong answer costs one act, not a
|
|
31
|
+
* wrong verdict. Nothing here touches what is judged.
|
|
32
|
+
*/
|
|
33
|
+
import { type HarnessDriverOptions } from './driver.js';
|
|
34
|
+
/** What the caller must be able to do for values to survive past this drive. */
|
|
35
|
+
export interface FillCache {
|
|
36
|
+
get(label: string): string | undefined;
|
|
37
|
+
set(label: string, value: string): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The fallback, and still the answer for most fields.
|
|
41
|
+
*
|
|
42
|
+
* An exploration drive needs PLAUSIBLE input, not creative input: the engine is judging what the app
|
|
43
|
+
* did with the value, not the value. A field that rejects everything here is itself worth finding,
|
|
44
|
+
* and shows up as an act that did not settle.
|
|
45
|
+
*/
|
|
46
|
+
export declare function heuristicFillValue(label: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Whether a label is worth spending a model call on.
|
|
49
|
+
*
|
|
50
|
+
* The heuristic is RIGHT about an email box and an amount box, and a generated "john@example.com"
|
|
51
|
+
* is not an improvement on `harness@reticle.dev`. What it cannot do is a field whose label it does
|
|
52
|
+
* not recognise at all, where it falls back to typing the words "reticle harness" into whatever the
|
|
53
|
+
* app asked for. That fallback is the whole of what generation is for.
|
|
54
|
+
*/
|
|
55
|
+
export declare function needsGeneration(label: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The seam the driver calls: a label in, a value out, always.
|
|
58
|
+
*
|
|
59
|
+
* Three layers, cheapest first, and the expensive one is skipped entirely unless the two above it
|
|
60
|
+
* have nothing better than "type the words reticle harness into this box".
|
|
61
|
+
*/
|
|
62
|
+
export declare function fillValues(deps: {
|
|
63
|
+
cache?: FillCache;
|
|
64
|
+
generator?: HarnessDriverOptions;
|
|
65
|
+
/** One line about where the drive is, so a value suits the page it is typed on. */
|
|
66
|
+
context?: () => string;
|
|
67
|
+
}): (label: string) => Promise<string>;
|