e2e 0.16.0 → 0.18.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 +10 -0
- package/dist/.build.json +1 -1
- package/dist/agent/act.js +7 -4
- package/dist/agent/action-dispatcher.js +4 -5
- package/dist/agent/actions.d.ts +32 -23
- package/dist/agent/actions.js +38 -24
- package/dist/agent/ai-sdk.js +5 -4
- package/dist/agent/error.js +3 -3
- package/dist/agent/invocation.js +3 -4
- package/dist/agent/model/sdk.js +2 -0
- package/dist/agent/model/tool-choice.d.ts +9 -0
- package/dist/agent/model/tool-choice.js +14 -0
- package/dist/agent/observation-feed.d.ts +5 -6
- package/dist/agent/observation-feed.js +8 -11
- package/dist/agent/observation.d.ts +40 -11
- package/dist/agent/observation.js +109 -59
- package/dist/agent/phases.d.ts +7 -1
- package/dist/agent/phases.js +12 -3
- package/dist/agent/point-tap.d.ts +8 -8
- package/dist/agent/point-tap.js +1 -1
- package/dist/agent/prompts.js +3 -0
- package/dist/agent/replay.d.ts +19 -8
- package/dist/agent/replay.js +29 -29
- package/dist/agent/scroll-target.d.ts +2 -3
- package/dist/agent/step-cache.d.ts +43 -15
- package/dist/agent/step-cache.js +156 -67
- package/dist/agent/tool-loop.js +94 -5
- package/dist/cache/anchors.d.ts +72 -23
- package/dist/cache/anchors.js +233 -60
- package/dist/cache/context.d.ts +25 -14
- package/dist/cache/context.js +50 -8
- package/dist/cache/decide.d.ts +2 -2
- package/dist/cache/decide.js +4 -6
- package/dist/cache/identity.d.ts +18 -4
- package/dist/cache/identity.js +9 -4
- package/dist/cache/recorder.d.ts +15 -1
- package/dist/cache/recorder.js +36 -23
- package/dist/cache/rekeyed.d.ts +46 -0
- package/dist/cache/rekeyed.js +89 -0
- package/dist/cache/relocate.d.ts +14 -14
- package/dist/cache/relocate.js +33 -25
- package/dist/cache/route.d.ts +32 -33
- package/dist/cache/route.js +94 -50
- package/dist/cache/store.d.ts +6 -0
- package/dist/cache/store.js +21 -1
- package/dist/cache/trace.d.ts +53 -14
- package/dist/cache/trace.js +76 -30
- package/dist/cli/bin.js +2 -2
- package/dist/cli/index.js +43 -32
- package/dist/cli/init/engines.d.ts +1 -3
- package/dist/cli/init/engines.js +2 -2
- package/dist/cli/init/gateways.d.ts +1 -1
- package/dist/cli/init/gateways.js +11 -1
- package/dist/cli/init/mcp-config.d.ts +2 -1
- package/dist/cli/init/mcp-config.js +8 -3
- package/dist/cli/init/sibling-versions.json +1 -1
- package/dist/cli/init/versions.d.ts +0 -8
- package/dist/cli/init/versions.js +1 -10
- package/dist/cli/mcp.d.ts +3 -2
- package/dist/cli/mcp.js +52 -16
- package/dist/cli/run-output.d.ts +17 -0
- package/dist/cli/run-output.js +45 -0
- package/dist/collect/collect.js +8 -4
- package/dist/collect/registry.js +27 -16
- package/dist/config/compiled-files.d.ts +36 -0
- package/dist/config/compiled-files.js +50 -0
- package/dist/config/diagnose.js +21 -19
- package/dist/config/esm-hooks.d.ts +39 -44
- package/dist/config/esm-hooks.js +238 -63
- package/dist/config/load.d.ts +1 -7
- package/dist/config/load.js +23 -66
- package/dist/config/register.d.ts +8 -0
- package/dist/config/register.js +9 -0
- package/dist/config/resolve.d.ts +1 -0
- package/dist/config/resolve.js +33 -1
- package/dist/config/specifiers.d.ts +19 -0
- package/dist/config/specifiers.js +33 -0
- package/dist/config/tsconfig.d.ts +56 -0
- package/dist/config/tsconfig.js +115 -0
- package/dist/config/typescript.d.ts +19 -0
- package/dist/config/typescript.js +253 -0
- package/dist/engine/contract.d.ts +1 -0
- package/dist/engine/index.d.ts +4 -2
- package/dist/engine/session.js +2 -2
- package/dist/expect/async.js +4 -2
- package/dist/expect/poll-scope.d.ts +52 -0
- package/dist/expect/poll-scope.js +113 -0
- package/dist/expect/poll.js +33 -6
- package/dist/expect/values.js +11 -4
- package/dist/internal/abandoned.d.ts +11 -0
- package/dist/internal/abandoned.js +23 -0
- package/dist/internal/brands.d.ts +2 -0
- package/dist/internal/brands.js +2 -0
- package/dist/internal/client-identity.d.ts +9 -0
- package/dist/internal/client-identity.js +14 -0
- package/dist/internal/errors.d.ts +10 -3
- package/dist/internal/errors.js +23 -8
- package/dist/internal/node-version.d.ts +16 -7
- package/dist/internal/node-version.js +50 -15
- package/dist/internal/package-manager.js +1 -1
- package/dist/internal/realm-slot.d.ts +5 -4
- package/dist/internal/redact.d.ts +9 -4
- package/dist/internal/redact.js +120 -23
- package/dist/internal/time.d.ts +26 -4
- package/dist/internal/time.js +58 -14
- package/dist/internal/urls.d.ts +9 -5
- package/dist/internal/urls.js +19 -10
- package/dist/locator/engine.d.ts +12 -2
- package/dist/locator/engine.js +55 -14
- package/dist/locator/screen.js +59 -9
- package/dist/mcp/catalog.d.ts +3 -2
- package/dist/mcp/catalog.js +6 -6
- package/dist/mcp/output.d.ts +59 -0
- package/dist/mcp/output.js +129 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +9 -12
- package/dist/mcp/session.d.ts +8 -1
- package/dist/mcp/session.js +56 -15
- package/dist/mcp/tools.js +7 -5
- package/dist/oauth/chatgpt.js +3 -23
- package/dist/oauth/cli.js +1 -0
- package/dist/oauth/copilot.d.ts +5 -4
- package/dist/oauth/copilot.js +80 -7
- package/dist/oauth/grok.js +2 -1
- package/dist/oauth/models.js +2 -1
- package/dist/oauth/opencode-console.d.ts +10 -0
- package/dist/oauth/opencode-console.js +133 -0
- package/dist/oauth/providers/github-copilot.d.ts +12 -0
- package/dist/oauth/providers/github-copilot.js +77 -23
- package/dist/oauth/providers/opencode-console.d.ts +31 -0
- package/dist/oauth/providers/opencode-console.js +148 -0
- package/dist/oauth/providers.d.ts +2 -6
- package/dist/oauth/providers.js +3 -7
- package/dist/oauth/responses.d.ts +9 -0
- package/dist/oauth/responses.js +23 -0
- package/dist/report/build.d.ts +26 -0
- package/dist/report/build.js +16 -8
- package/dist/report/failure-text.d.ts +6 -1
- package/dist/report/failure-text.js +14 -4
- package/dist/report/format.d.ts +10 -5
- package/dist/report/format.js +12 -5
- package/dist/report/junit.js +11 -12
- package/dist/report/list.d.ts +12 -0
- package/dist/report/list.js +61 -24
- package/dist/report/markdown.d.ts +1 -1
- package/dist/report/markdown.js +37 -14
- package/dist/report/repeats.d.ts +6 -1
- package/dist/report/repeats.js +12 -3
- package/dist/run/artifacts.d.ts +22 -4
- package/dist/run/artifacts.js +71 -3
- package/dist/run/execute.d.ts +34 -5
- package/dist/run/execute.js +93 -44
- package/dist/run/extended-fixtures.js +6 -2
- package/dist/run/failure-evidence.js +10 -4
- package/dist/run/fixtures.js +6 -8
- package/dist/run/in-process.d.ts +2 -0
- package/dist/run/in-process.js +52 -10
- package/dist/run/last-run.d.ts +42 -8
- package/dist/run/last-run.js +121 -14
- package/dist/run/process-output.d.ts +73 -0
- package/dist/run/process-output.js +130 -0
- package/dist/run/provision.d.ts +2 -0
- package/dist/run/provision.js +1 -0
- package/dist/run/realm.d.ts +4 -1
- package/dist/run/realm.js +20 -6
- package/dist/run/records.d.ts +20 -4
- package/dist/run/records.js +26 -0
- package/dist/run/retry.d.ts +10 -1
- package/dist/run/retry.js +30 -12
- package/dist/run/runner.d.ts +9 -1
- package/dist/run/runner.js +101 -39
- package/dist/run/scheduler.js +255 -19
- package/dist/run/secrecy.d.ts +22 -19
- package/dist/run/secrecy.js +22 -18
- package/dist/run/serial.d.ts +75 -2
- package/dist/run/serial.js +243 -59
- package/dist/run/standalone.js +1 -1
- package/dist/run/steps.d.ts +0 -6
- package/dist/run/steps.js +3 -20
- package/dist/run/trace-redaction.d.ts +5 -6
- package/dist/run/trace-redaction.js +5 -6
- package/dist/run/worker/entry.js +40 -18
- package/dist/run/worker/handle.js +10 -5
- package/dist/run/worker/protocol.d.ts +85 -3
- package/dist/run/worker/session.d.ts +2 -0
- package/dist/run/worker/session.js +29 -0
- package/dist/telemetry/environment.d.ts +2 -1
- package/dist/telemetry/environment.js +12 -2
- package/dist/telemetry/events.js +1 -0
- package/dist/telemetry/project.d.ts +5 -6
- package/dist/telemetry/project.js +6 -11
- package/dist/telemetry/store.d.ts +5 -5
- package/dist/telemetry/store.js +5 -7
- package/dist/telemetry/telemetry.d.ts +2 -3
- package/dist/telemetry/telemetry.js +2 -4
- package/dist/types.d.ts +15 -6
- package/docs/agent-steps.mdx +1 -0
- package/docs/agents.mdx +7 -3
- package/docs/api-testing.mdx +2 -1
- package/docs/authentication.mdx +6 -3
- package/docs/browser.mdx +49 -12
- package/docs/bug-bash.mdx +2 -1
- package/docs/cache.mdx +66 -19
- package/docs/ci.mdx +14 -9
- package/docs/coding-agents.mdx +2 -1
- package/docs/core-concepts.mdx +208 -0
- package/docs/debugging.mdx +25 -12
- package/docs/decision-models.mdx +142 -0
- package/docs/example-projects.mdx +32 -0
- package/docs/executors.mdx +7 -2
- package/docs/explore.mdx +1 -0
- package/docs/github.mdx +14 -5
- package/docs/index.mdx +9 -5
- package/docs/integrations/eas.mdx +1 -0
- package/docs/integrations/index.mdx +1 -0
- package/docs/integrations/kernel.mdx +3 -2
- package/docs/migrate/cypress.mdx +26 -3
- package/docs/migrate/detox.mdx +24 -1
- package/docs/migrate/maestro.mdx +28 -4
- package/docs/migrate/playwright.mdx +44 -18
- package/docs/migrate/selenium.mdx +27 -3
- package/docs/mobile-ci.mdx +6 -5
- package/docs/mobile.mdx +96 -8
- package/docs/models.mdx +60 -9
- package/docs/quickstart.mdx +28 -16
- package/docs/reference/agent.mdx +13 -3
- package/docs/reference/app.mdx +2 -1
- package/docs/reference/cli.mdx +41 -18
- package/docs/reference/config.mdx +67 -13
- package/docs/reference/engine.mdx +25 -13
- package/docs/reference/environment.mdx +10 -2
- package/docs/reference/errors.mdx +13 -10
- package/docs/reference/expect.mdx +36 -13
- package/docs/reference/mcp.mdx +21 -8
- package/docs/reference/mobile.mdx +24 -13
- package/docs/reference/reporters.mdx +9 -2
- package/docs/reference/screen.mdx +49 -16
- package/docs/reference/test.mdx +32 -5
- package/docs/reference/web.mdx +133 -23
- package/docs/security.mdx +58 -42
- package/docs/subscriptions.mdx +92 -12
- package/docs/telemetry.mdx +9 -6
- package/docs/tools.mdx +1 -0
- package/docs/{starting-your-app.mdx → web.mdx} +157 -11
- package/docs/writing-an-engine.mdx +2 -1
- package/package.json +25 -8
- package/schema/report-v1.schema.json +14 -1
- package/skills/e2e/SKILL.md +5 -3
- package/skills/e2e/references/agent.md +33 -11
- package/skills/e2e/references/debugging.md +4 -4
- package/skills/e2e/references/mcp.md +8 -3
- package/skills/e2e/references/running.md +15 -9
- package/skills/e2e/references/setup.md +45 -16
- package/skills/e2e/references/writing-tests.md +35 -18
- package/docs/viewports.mdx +0 -146
- package/docs/writing-tests.mdx +0 -256
package/dist/cache/anchors.d.ts
CHANGED
|
@@ -1,36 +1,85 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Anchors: the step's recorded delta.
|
|
3
3
|
*
|
|
4
4
|
* A recording run does not end when its last action commits; it ends when the
|
|
5
5
|
* executor looks at the screen and judges the step done. The trace used to
|
|
6
6
|
* keep only the actions and drop that look, so a replay could run every
|
|
7
7
|
* recorded action mechanically — a fill that never committed, a save that
|
|
8
8
|
* produced an unnamed record — and self-finalize with nothing checking the
|
|
9
|
-
* effect. Anchors restore the check as data: the descriptors of nodes
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* effect. Anchors restore the check as data: the descriptors of nodes the
|
|
10
|
+
* step made appear (`endAnchors`) and of nodes it made vanish
|
|
11
|
+
* (`goneAnchors`), each with the states a step sets on a control it leaves
|
|
12
|
+
* on screen (a switch turned on, a field typed into). They are checked by
|
|
13
|
+
* the relocation vocabulary (`relocate.ts`) with two deliberate differences:
|
|
14
|
+
* every recorded field must be equal, text, value, and states included, and
|
|
15
|
+
* presence is enough, uniqueness is not asked.
|
|
16
|
+
*
|
|
17
|
+
* A replay passes on its own only when the delta happened again: everything
|
|
18
|
+
* that appeared is there, everything that vanished is gone, and at least one
|
|
19
|
+
* of those changes, or the route, actually moved during the replay
|
|
20
|
+
* (`deltaEvidenced`), so a status that already read "Submitted" before a
|
|
21
|
+
* submit that did nothing is no proof. An alert the replay raised that the
|
|
22
|
+
* recording never saw is an outcome the recording did not have, whatever
|
|
23
|
+
* else matched (`deltaHolds`).
|
|
14
24
|
*/
|
|
15
|
-
import type {
|
|
16
|
-
import { type DescriptorMatchOptions } from './relocate.ts';
|
|
25
|
+
import type { RedactedNode } from '../agent/observation.ts';
|
|
17
26
|
import { type TraceTargetDescriptor } from './trace.ts';
|
|
18
|
-
|
|
27
|
+
/** The two sides of one step's delta, each projected, deduplicated, ordered, and capped. */
|
|
28
|
+
export interface StepDelta {
|
|
29
|
+
/** On screen when the step passed, not when it began. */
|
|
30
|
+
readonly appeared: TraceTargetDescriptor[];
|
|
31
|
+
/** On screen when the step began, not when it passed. */
|
|
32
|
+
readonly gone: TraceTargetDescriptor[];
|
|
33
|
+
}
|
|
34
|
+
/** The recorded delta a replay checks, as a trace carries it. */
|
|
35
|
+
export interface RecordedDelta {
|
|
36
|
+
readonly endAnchors?: readonly TraceTargetDescriptor[];
|
|
37
|
+
readonly goneAnchors?: readonly TraceTargetDescriptor[];
|
|
38
|
+
}
|
|
19
39
|
/**
|
|
20
|
-
* Derives
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
40
|
+
* Derives one step's delta between its starting observation and its passing
|
|
41
|
+
* one. Each side is ordered announcements first, then leaves, then
|
|
42
|
+
* containers, each in document order: a container's accessible name is
|
|
43
|
+
* usually the concatenation of its children's, so it repeats what the
|
|
44
|
+
* leaves already say, and on a list-heavy screen those repeats would crowd
|
|
45
|
+
* the one status line that names the effect out of the cap. `routeMoved`
|
|
46
|
+
* says the step ended on another route than it began on, whose counts are
|
|
47
|
+
* that screen's data rather than the step's result (`COUNT_TEXT`).
|
|
48
|
+
*
|
|
49
|
+
* A node counts as gone only when no end node matches its anchor the way
|
|
50
|
+
* `deltaHolds` checks one, on the recorded fields alone: a radio label
|
|
51
|
+
* "Express" the pick replaced with a status reading "Express" vanished, but
|
|
52
|
+
* an anchor of that text would still match the status, and the recording
|
|
53
|
+
* would fail its own check on every replay.
|
|
27
54
|
*/
|
|
28
|
-
export declare function
|
|
55
|
+
export declare function describeDelta(startNodes: ReadonlyMap<string, RedactedNode>, endNodes: ReadonlyMap<string, RedactedNode>, routeMoved: boolean): StepDelta;
|
|
29
56
|
/**
|
|
30
|
-
* Whether
|
|
31
|
-
* anchor (`descriptorTiers
|
|
32
|
-
* forgiven when the remaining fields still
|
|
33
|
-
*
|
|
34
|
-
*
|
|
57
|
+
* Whether the recorded end state holds on a fresh observation: every
|
|
58
|
+
* appeared anchor present (some tier of it, `descriptorTiers`, so a test id
|
|
59
|
+
* that churned per render is forgiven when the remaining fields still
|
|
60
|
+
* identify the node, equal on every anchor field and state), no gone anchor
|
|
61
|
+
* present, and no alert on screen that was not on `beforeNodes` and is not
|
|
62
|
+
* one the recording saw appear. Two nodes matching one anchor are the effect
|
|
63
|
+
* twice over, not an ambiguity.
|
|
64
|
+
*/
|
|
65
|
+
export declare function deltaHolds(delta: RecordedDelta, nodes: ReadonlyMap<string, RedactedNode>, beforeNodes: ReadonlyMap<string, RedactedNode>): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Whether the replay itself produced some of the recorded delta, measured
|
|
68
|
+
* from `baselineNodes`, the first screen the replay saw on the route it
|
|
69
|
+
* ended on: an anchor that appeared was absent there, or a gone one was
|
|
70
|
+
* there. Without that, every check `deltaHolds` makes could have held before
|
|
71
|
+
* the actions that should have produced it: the screen already read the
|
|
72
|
+
* outcome, and nothing the replay did is proven to have done anything. A
|
|
73
|
+
* recording with no delta at all is never evidence. Undefined `baselineNodes`
|
|
74
|
+
* means the replay saw no screen on that route before its last action, so
|
|
75
|
+
* that action moved the route, which is the evidence.
|
|
76
|
+
*
|
|
77
|
+
* A control one of the trace's own actions targeted (`inputTargets`) that
|
|
78
|
+
* shows on both sides of the delta, a field typed into or a switch flipped,
|
|
79
|
+
* echoes the input: the replay typed, so the value is there, whatever the
|
|
80
|
+
* step was for. Such echoes are evidence only when the delta holds nothing
|
|
81
|
+
* else; otherwise the rest of the delta must have moved, so a subscribe
|
|
82
|
+
* form typed into on a page that already read "You're subscribed" proves
|
|
83
|
+
* nothing.
|
|
35
84
|
*/
|
|
36
|
-
export declare function
|
|
85
|
+
export declare function deltaEvidenced(delta: RecordedDelta, baselineNodes: ReadonlyMap<string, RedactedNode> | undefined, inputTargets: readonly TraceTargetDescriptor[]): boolean;
|
package/dist/cache/anchors.js
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Anchors: the step's recorded delta.
|
|
3
3
|
*
|
|
4
4
|
* A recording run does not end when its last action commits; it ends when the
|
|
5
5
|
* executor looks at the screen and judges the step done. The trace used to
|
|
6
6
|
* keep only the actions and drop that look, so a replay could run every
|
|
7
7
|
* recorded action mechanically — a fill that never committed, a save that
|
|
8
8
|
* produced an unnamed record — and self-finalize with nothing checking the
|
|
9
|
-
* effect. Anchors restore the check as data: the descriptors of nodes
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* effect. Anchors restore the check as data: the descriptors of nodes the
|
|
10
|
+
* step made appear (`endAnchors`) and of nodes it made vanish
|
|
11
|
+
* (`goneAnchors`), each with the states a step sets on a control it leaves
|
|
12
|
+
* on screen (a switch turned on, a field typed into). They are checked by
|
|
13
|
+
* the relocation vocabulary (`relocate.ts`) with two deliberate differences:
|
|
14
|
+
* every recorded field must be equal, text, value, and states included, and
|
|
15
|
+
* presence is enough, uniqueness is not asked.
|
|
16
|
+
*
|
|
17
|
+
* A replay passes on its own only when the delta happened again: everything
|
|
18
|
+
* that appeared is there, everything that vanished is gone, and at least one
|
|
19
|
+
* of those changes, or the route, actually moved during the replay
|
|
20
|
+
* (`deltaEvidenced`), so a status that already read "Submitted" before a
|
|
21
|
+
* submit that did nothing is no proof. An alert the replay raised that the
|
|
22
|
+
* recording never saw is an outcome the recording did not have, whatever
|
|
23
|
+
* else matched (`deltaHolds`).
|
|
14
24
|
*/
|
|
15
25
|
import { describeTarget } from '../agent/actions.js';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
26
|
+
import { collapseText } from '../internal/text.js';
|
|
27
|
+
import { descriptorTiers, fieldsEqual } from './relocate.js';
|
|
28
|
+
import { bound, MAX_TRACE_ANCHORS, MAX_TRACE_DESCRIPTOR_CHARS, TRACE_ANCHOR_STATES, } from './trace.js';
|
|
18
29
|
/**
|
|
19
30
|
* Every field an anchor records. Unlike target relocation, text is never
|
|
20
31
|
* optional here: for a target, text is a fallback identity and a relabeled
|
|
@@ -25,57 +36,95 @@ import { MAX_TRACE_ANCHORS } from './trace.js';
|
|
|
25
36
|
*/
|
|
26
37
|
const ANCHOR_FIELDS = ['role', 'name', 'text', 'testId', 'placeholder', 'inputPurpose'];
|
|
27
38
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* repeats what the leaves already say — and on a list-heavy screen those
|
|
33
|
-
* repeats would crowd the one status line that names the effect out of the
|
|
34
|
-
* cap.
|
|
39
|
+
* Roles an app announces an outcome in: the ARIA live regions a screen
|
|
40
|
+
* reader reads out on change, and the alert dialog. The app itself says
|
|
41
|
+
* this text reports what just happened, so these are recorded before any
|
|
42
|
+
* other anchor and the cap never crowds them out.
|
|
35
43
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
const ANNOUNCEMENT_ROLES = new Set(['alert', 'alertdialog', 'status']);
|
|
45
|
+
/**
|
|
46
|
+
* The announcements that interrupt: an error, a warning, a confirmation the
|
|
47
|
+
* user must answer. A replay that raises one the recording never saw has
|
|
48
|
+
* another outcome than the recording, so every one that appears is recorded
|
|
49
|
+
* whatever its text, and checked both ways.
|
|
50
|
+
*/
|
|
51
|
+
const ALERT_ROLES = new Set(['alert', 'alertdialog']);
|
|
52
|
+
/**
|
|
53
|
+
* Derives one step's delta between its starting observation and its passing
|
|
54
|
+
* one. Each side is ordered announcements first, then leaves, then
|
|
55
|
+
* containers, each in document order: a container's accessible name is
|
|
56
|
+
* usually the concatenation of its children's, so it repeats what the
|
|
57
|
+
* leaves already say, and on a list-heavy screen those repeats would crowd
|
|
58
|
+
* the one status line that names the effect out of the cap. `routeMoved`
|
|
59
|
+
* says the step ended on another route than it began on, whose counts are
|
|
60
|
+
* that screen's data rather than the step's result (`COUNT_TEXT`).
|
|
61
|
+
*
|
|
62
|
+
* A node counts as gone only when no end node matches its anchor the way
|
|
63
|
+
* `deltaHolds` checks one, on the recorded fields alone: a radio label
|
|
64
|
+
* "Express" the pick replaced with a status reading "Express" vanished, but
|
|
65
|
+
* an anchor of that text would still match the status, and the recording
|
|
66
|
+
* would fail its own check on every replay.
|
|
67
|
+
*/
|
|
68
|
+
export function describeDelta(startNodes, endNodes, routeMoved) {
|
|
69
|
+
const start = projectAnchors(startNodes);
|
|
70
|
+
const end = projectAnchors(endNodes);
|
|
71
|
+
const startKeys = new Set(start.map((anchor) => anchor.key));
|
|
72
|
+
const endKeys = new Set(end.map((anchor) => anchor.key));
|
|
73
|
+
return {
|
|
74
|
+
appeared: deltaSide(end, start, (anchor) => startKeys.has(anchor.key), routeMoved),
|
|
75
|
+
gone: deltaSide(start, end, (anchor) => endKeys.has(anchor.key) || anchorIn(anchor.descriptor, end), routeMoved),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** The side of the delta `nodes` holds and `other` does not (`inOther`), ordered and capped. */
|
|
79
|
+
function deltaSide(nodes, other, inOther, routeMoved) {
|
|
80
|
+
const otherShapes = new Set(other.map((anchor) => countShape(anchor.descriptor)));
|
|
81
|
+
const volatile = (anchor) => isVolatileAnchor(anchor, (count) => routeMoved || otherShapes.has(countShape(count)));
|
|
43
82
|
const seen = new Set();
|
|
83
|
+
const announcements = [];
|
|
44
84
|
const leaves = [];
|
|
45
85
|
const containers = [];
|
|
46
|
-
for (const
|
|
47
|
-
|
|
48
|
-
if (descriptor === undefined)
|
|
49
|
-
continue;
|
|
50
|
-
const key = anchorKey(descriptor);
|
|
51
|
-
if (before.has(key) || seen.has(key))
|
|
86
|
+
for (const anchor of nodes) {
|
|
87
|
+
if (seen.has(anchor.key) || inOther(anchor))
|
|
52
88
|
continue;
|
|
53
|
-
seen.add(key);
|
|
54
|
-
|
|
89
|
+
seen.add(anchor.key);
|
|
90
|
+
const role = anchor.descriptor.role ?? '';
|
|
91
|
+
(ANNOUNCEMENT_ROLES.has(role) ? announcements : anchor.leaf ? leaves : containers).push(anchor);
|
|
55
92
|
}
|
|
56
|
-
const all = [...leaves, ...containers];
|
|
57
|
-
const stable = all.filter((anchor) => !
|
|
58
|
-
return (stable.length > 0 ? stable : all).slice(0, MAX_TRACE_ANCHORS);
|
|
93
|
+
const all = [...announcements, ...leaves, ...containers];
|
|
94
|
+
const stable = all.filter((anchor) => isAlert(anchor.descriptor) || !volatile(anchor.descriptor));
|
|
95
|
+
return (stable.length > 0 ? stable : all).slice(0, MAX_TRACE_ANCHORS).map((anchor) => anchor.descriptor);
|
|
59
96
|
}
|
|
60
97
|
/**
|
|
61
98
|
* Text that cannot read the same on the next run: a minted key prefix or id,
|
|
62
|
-
* a countdown or age, a date, a clock time, a timing in milliseconds
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
99
|
+
* a countdown or age, a date, a clock time, a timing in milliseconds. An
|
|
100
|
+
* anchor made of it hands every replay off, so it is skipped while some
|
|
101
|
+
* stable anchor exists; with nothing else, the volatile ones stay, because a
|
|
102
|
+
* replay that always hands off is still safer than one that passes on
|
|
103
|
+
* mechanics alone. An alert keeps it, and is compared with these parts read
|
|
104
|
+
* as placeholders (`alertShape`).
|
|
68
105
|
*/
|
|
69
106
|
const VOLATILE_TEXT = [
|
|
70
107
|
/\b(?=[A-Za-z0-9_-]*\d)(?=[A-Za-z0-9_-]*[A-Za-z])[A-Za-z0-9_-]{12,}\b/,
|
|
71
108
|
/\b\d+\s*(?:ms|s|secs?|seconds?|m|mins?|minutes?|h|hrs?|hours?|d|days?|weeks?|months?|years?)\b/i,
|
|
72
|
-
/\b\d+\s*(?:to|-|\u2013)\s*\d+\s+of\s+\d+\b/i,
|
|
73
|
-
/\b\d+\s+(?:results?|items?|rows?|entries|records?|matches|total)\b/i,
|
|
74
109
|
/\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\.?\s+\d{1,2}(?:,?\s+\d{4})?\b/i,
|
|
75
110
|
/\b\d{1,2}\s+(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\.?(?:\s+\d{4})?\b/i,
|
|
76
111
|
/\b\d{4}-\d{2}-\d{2}\b/,
|
|
77
112
|
/\b\d{1,2}:\d{2}(?::\d{2})?\b/,
|
|
78
113
|
];
|
|
114
|
+
/**
|
|
115
|
+
* A count that names what it counts: a pagination range, `12 items`, `3
|
|
116
|
+
* records imported`. Such a count is the step's own result when the step
|
|
117
|
+
* made it appear on the screen it acted on, and must read the same again:
|
|
118
|
+
* `0 records imported` is not the import the recording saw. It is the data
|
|
119
|
+
* instead, which the next run, with what an earlier run left behind, reads
|
|
120
|
+
* differently, when the other side of the delta already showed the same
|
|
121
|
+
* text with other numbers (`41 items` became `42 items`), or when the step
|
|
122
|
+
* moved to another route and the count is part of the screen it opened.
|
|
123
|
+
*/
|
|
124
|
+
const COUNT_TEXT = [
|
|
125
|
+
/\b\d+\s*(?:to|-|\u2013)\s*\d+\s+of\s+\d+\b/i,
|
|
126
|
+
/\b\d+\s+(?:results?|items?|rows?|entries|records?|matches|total)\b/i,
|
|
127
|
+
];
|
|
79
128
|
/**
|
|
80
129
|
* A bare number is a badge or a tally, `11` unread, and reads differently
|
|
81
130
|
* once the data changes; it is volatile when it is all the anchor has to say.
|
|
@@ -86,38 +135,162 @@ function isBareNumber(anchor) {
|
|
|
86
135
|
const label = anchor.name ?? anchor.text;
|
|
87
136
|
return label !== undefined && /^\d+$/.test(label) && anchor.testId === undefined && (anchor.name === undefined || anchor.text === undefined || anchor.name === anchor.text);
|
|
88
137
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
138
|
+
/** Whether an anchor's text cannot read the same on the next run; `countIsData` decides an anchor holding a count. */
|
|
139
|
+
function isVolatileAnchor(anchor, countIsData) {
|
|
140
|
+
const texts = [anchor.text, anchor.name, anchor.value].filter((value) => value !== undefined);
|
|
141
|
+
if (isBareNumber(anchor) || texts.some((value) => VOLATILE_TEXT.some((pattern) => pattern.test(value))))
|
|
142
|
+
return true;
|
|
143
|
+
return texts.some((value) => COUNT_TEXT.some((pattern) => pattern.test(value))) && countIsData(anchor);
|
|
144
|
+
}
|
|
145
|
+
/** An anchor with every digit run read as one placeholder: the text a running count keeps while its numbers move. */
|
|
146
|
+
function countShape(anchor) {
|
|
147
|
+
return anchorKey({ ...anchor, ...digitless('name', anchor.name), ...digitless('text', anchor.text), ...digitless('value', anchor.value) });
|
|
148
|
+
}
|
|
149
|
+
function digitless(field, value) {
|
|
150
|
+
return value === undefined ? {} : { [field]: value.replace(/\d+/g, '#') };
|
|
151
|
+
}
|
|
152
|
+
function isAlert(anchor) {
|
|
153
|
+
return anchor.role !== undefined && ALERT_ROLES.has(anchor.role);
|
|
154
|
+
}
|
|
155
|
+
const VOLATILE_SPANS = VOLATILE_TEXT.map((pattern) => new RegExp(pattern.source, `${pattern.flags}g`));
|
|
156
|
+
/**
|
|
157
|
+
* An alert with its volatile parts read as one placeholder each: `Session
|
|
158
|
+
* expires at 17:42` and `Session expires at 17:45` are the same alert, which
|
|
159
|
+
* a replay must neither miss nor mistake for one the recording never saw.
|
|
160
|
+
*/
|
|
161
|
+
function alertShape(anchor) {
|
|
162
|
+
const mask = (text) => VOLATILE_SPANS.reduce((masked, span) => masked.replace(span, '#'), text);
|
|
163
|
+
return {
|
|
164
|
+
...anchor,
|
|
165
|
+
...(anchor.name === undefined ? {} : { name: mask(anchor.name) }),
|
|
166
|
+
...(anchor.text === undefined ? {} : { text: mask(anchor.text) }),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Whether the recorded end state holds on a fresh observation: every
|
|
171
|
+
* appeared anchor present (some tier of it, `descriptorTiers`, so a test id
|
|
172
|
+
* that churned per render is forgiven when the remaining fields still
|
|
173
|
+
* identify the node, equal on every anchor field and state), no gone anchor
|
|
174
|
+
* present, and no alert on screen that was not on `beforeNodes` and is not
|
|
175
|
+
* one the recording saw appear. Two nodes matching one anchor are the effect
|
|
176
|
+
* twice over, not an ambiguity.
|
|
177
|
+
*/
|
|
178
|
+
export function deltaHolds(delta, nodes, beforeNodes) {
|
|
179
|
+
const live = projectAnchors(nodes);
|
|
180
|
+
const appeared = delta.endAnchors ?? [];
|
|
181
|
+
if (!appeared.every((anchor) => anchorIn(anchor, live)))
|
|
182
|
+
return false;
|
|
183
|
+
if ((delta.goneAnchors ?? []).some((anchor) => anchorIn(anchor, live)))
|
|
184
|
+
return false;
|
|
185
|
+
const before = projectAnchors(beforeNodes).filter((anchor) => isAlert(anchor.descriptor));
|
|
186
|
+
return live.every((anchor) => !isAlert(anchor.descriptor) ||
|
|
187
|
+
before.some((earlier) => anchorIn(anchor.descriptor, [earlier])) ||
|
|
188
|
+
appeared.some((recorded) => anchorIn(recorded, [anchor])));
|
|
92
189
|
}
|
|
93
190
|
/**
|
|
94
|
-
* Whether
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
191
|
+
* Whether the replay itself produced some of the recorded delta, measured
|
|
192
|
+
* from `baselineNodes`, the first screen the replay saw on the route it
|
|
193
|
+
* ended on: an anchor that appeared was absent there, or a gone one was
|
|
194
|
+
* there. Without that, every check `deltaHolds` makes could have held before
|
|
195
|
+
* the actions that should have produced it: the screen already read the
|
|
196
|
+
* outcome, and nothing the replay did is proven to have done anything. A
|
|
197
|
+
* recording with no delta at all is never evidence. Undefined `baselineNodes`
|
|
198
|
+
* means the replay saw no screen on that route before its last action, so
|
|
199
|
+
* that action moved the route, which is the evidence.
|
|
200
|
+
*
|
|
201
|
+
* A control one of the trace's own actions targeted (`inputTargets`) that
|
|
202
|
+
* shows on both sides of the delta, a field typed into or a switch flipped,
|
|
203
|
+
* echoes the input: the replay typed, so the value is there, whatever the
|
|
204
|
+
* step was for. Such echoes are evidence only when the delta holds nothing
|
|
205
|
+
* else; otherwise the rest of the delta must have moved, so a subscribe
|
|
206
|
+
* form typed into on a page that already read "You're subscribed" proves
|
|
207
|
+
* nothing.
|
|
99
208
|
*/
|
|
100
|
-
export function
|
|
101
|
-
|
|
102
|
-
|
|
209
|
+
export function deltaEvidenced(delta, baselineNodes, inputTargets) {
|
|
210
|
+
if (baselineNodes === undefined)
|
|
211
|
+
return true;
|
|
212
|
+
const appeared = delta.endAnchors ?? [];
|
|
213
|
+
const gone = delta.goneAnchors ?? [];
|
|
214
|
+
const targets = new Set(inputTargets.map(identityKey));
|
|
215
|
+
const changed = new Set(appeared.map(identityKey).filter((key) => gone.some((anchor) => identityKey(anchor) === key)));
|
|
216
|
+
const echoes = (anchor) => changed.has(identityKey(anchor)) && targets.has(identityKey(anchor));
|
|
217
|
+
const outcome = [...appeared, ...gone].some((anchor) => !echoes(anchor));
|
|
218
|
+
const counted = (anchor) => !outcome || !echoes(anchor);
|
|
219
|
+
const baseline = projectAnchors(baselineNodes);
|
|
220
|
+
return (appeared.some((anchor) => counted(anchor) && !anchorIn(anchor, baseline)) ||
|
|
221
|
+
gone.some((anchor) => counted(anchor) && anchorIn(anchor, baseline)));
|
|
222
|
+
}
|
|
223
|
+
/** A control's key without what a step sets on it, its value and states: the same control before and after. */
|
|
224
|
+
function identityKey(descriptor) {
|
|
225
|
+
const { value: _value, states: _states, ...control } = descriptor;
|
|
226
|
+
return anchorKey(control);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Whether some tier of a recorded anchor equals some projected node on every
|
|
230
|
+
* anchor field, and on its value and states exactly: an empty field is not
|
|
231
|
+
* the field filled in, nor an unchecked switch the switch turned on. An
|
|
232
|
+
* alert is compared by its shape (`alertShape`).
|
|
233
|
+
*/
|
|
234
|
+
function anchorIn(anchor, nodes) {
|
|
235
|
+
const states = statesKey(anchor.states);
|
|
236
|
+
const shape = isAlert(anchor) ? alertShape : (descriptor) => descriptor;
|
|
237
|
+
return descriptorTiers(anchor).some((tier) => nodes.some((node) => node.descriptor.value === anchor.value &&
|
|
238
|
+
statesKey(node.descriptor.states) === states &&
|
|
239
|
+
fieldsEqual(shape(tier), shape(node.descriptor), ANCHOR_FIELDS)));
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Anchor projections per observation: a replay checks the same end screen
|
|
243
|
+
* once per poll and the start screen for every check, while the projection
|
|
244
|
+
* of a node is a pure function of the node.
|
|
245
|
+
*/
|
|
246
|
+
const projections = new WeakMap();
|
|
247
|
+
/** Every node of an observation that can serve as an anchor, in document order. */
|
|
248
|
+
function projectAnchors(nodes) {
|
|
249
|
+
const cached = projections.get(nodes);
|
|
250
|
+
if (cached !== undefined)
|
|
251
|
+
return cached;
|
|
252
|
+
const anchors = [];
|
|
253
|
+
for (const node of nodes.values()) {
|
|
254
|
+
const descriptor = anchorDescriptor(node);
|
|
255
|
+
if (descriptor === undefined)
|
|
256
|
+
continue;
|
|
257
|
+
// An alert keyed by its shape: one whose countdown ticked while the step
|
|
258
|
+
// ran stayed on screen, and is on neither side of the delta.
|
|
259
|
+
const key = anchorKey(isAlert(descriptor) ? alertShape(descriptor) : descriptor);
|
|
260
|
+
anchors.push({ descriptor, key, leaf: node.children === undefined || node.children.length === 0 });
|
|
261
|
+
}
|
|
262
|
+
projections.set(nodes, anchors);
|
|
263
|
+
return anchors;
|
|
103
264
|
}
|
|
104
265
|
/** One node's anchor projection, or undefined when it could identify nothing. */
|
|
105
|
-
function anchorDescriptor(node
|
|
106
|
-
const described = describeTarget(node
|
|
266
|
+
function anchorDescriptor(node) {
|
|
267
|
+
const described = describeTarget(node);
|
|
107
268
|
if (described === undefined || descriptorTiers(described).length === 0)
|
|
108
269
|
return undefined;
|
|
109
270
|
const { selector: _selector, ...anchor } = described;
|
|
110
|
-
|
|
271
|
+
const states = TRACE_ANCHOR_STATES.filter((state) => node.states?.[state] === true);
|
|
272
|
+
const value = node.value === undefined || node.states?.secure === true ? '' : collapseText(node.value);
|
|
273
|
+
return {
|
|
274
|
+
...anchor,
|
|
275
|
+
...(value === '' ? {} : { value: bound(value, MAX_TRACE_DESCRIPTOR_CHARS) }),
|
|
276
|
+
...(states.length === 0 ? {} : { states }),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function statesKey(states) {
|
|
280
|
+
return states === undefined ? '' : states.join(',');
|
|
111
281
|
}
|
|
112
282
|
/**
|
|
113
283
|
* Set key for one descriptor: its loosest identifying tier over the anchor
|
|
114
|
-
* fields, in fixed order. Keying on the semantic tier
|
|
115
|
-
* the node means an app that mints test ids per
|
|
116
|
-
* unchanged control look new after a re-render and
|
|
117
|
-
* of the capped list; a node only a test id
|
|
284
|
+
* fields, in fixed order, and its states. Keying on the semantic tier
|
|
285
|
+
* whenever it identifies the node means an app that mints test ids per
|
|
286
|
+
* render cannot make every unchanged control look new after a re-render and
|
|
287
|
+
* crowd the real effect out of the capped list; a node only a test id
|
|
288
|
+
* identifies keeps it. The states make a control that changed state a node
|
|
289
|
+
* of the delta on both sides: the switch as it was, gone, and as it is,
|
|
290
|
+
* appeared; the value does the same for a field typed into.
|
|
118
291
|
*/
|
|
119
292
|
function anchorKey(descriptor) {
|
|
120
293
|
const tiers = descriptorTiers(descriptor);
|
|
121
294
|
const loosest = tiers[tiers.length - 1] ?? descriptor;
|
|
122
|
-
return JSON.stringify(ANCHOR_FIELDS.map((field) => loosest[field]));
|
|
295
|
+
return JSON.stringify([...ANCHOR_FIELDS.map((field) => loosest[field]), descriptor.value, statesKey(descriptor.states)]);
|
|
123
296
|
}
|
package/dist/cache/context.d.ts
CHANGED
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
* re-records.
|
|
9
9
|
*/
|
|
10
10
|
import type { ResolvedCacheConfig } from '../config/resolve.ts';
|
|
11
|
-
import { type CacheTargetIdentity, type TraceCacheKind } from './identity.ts';
|
|
11
|
+
import { type CacheAgentIdentity, type CacheTargetIdentity, type TraceCacheKind } from './identity.ts';
|
|
12
|
+
import type { StoredRecordings } from './rekeyed.ts';
|
|
12
13
|
import { type CacheStore } from './store.ts';
|
|
13
|
-
import type { ActionTrace } from './trace.ts';
|
|
14
|
+
import type { ActionTrace, TraceProvenance } from './trace.ts';
|
|
14
15
|
import type { JsonValue } from '../types.ts';
|
|
15
16
|
/**
|
|
16
17
|
* One cache entry held back until the attempt confirms or implicates it: a
|
|
@@ -18,7 +19,9 @@ import type { JsonValue } from '../types.ts';
|
|
|
18
19
|
* stands. A kept entry carries no payload, so the replay's expansion of it
|
|
19
20
|
* (every `unique()` slot filled with this run's value) can never be written;
|
|
20
21
|
* confirmed, the file keeps its bytes and `createdAt`, and a committed cache
|
|
21
|
-
* directory stays clean
|
|
22
|
+
* directory stays clean, unless its provenance predates the step's
|
|
23
|
+
* occurrence fields (`recordedFor`, as stored), which the replay proved and
|
|
24
|
+
* are then written in. Unconfirmed, both kinds are evicted alike.
|
|
22
25
|
*/
|
|
23
26
|
export type StagedTrace = {
|
|
24
27
|
readonly keyHash: string;
|
|
@@ -29,34 +32,40 @@ export type StagedTrace = {
|
|
|
29
32
|
readonly trace: ActionTrace;
|
|
30
33
|
} | {
|
|
31
34
|
readonly kind: 'keep';
|
|
35
|
+
readonly recordedFor: TraceProvenance;
|
|
32
36
|
});
|
|
37
|
+
/** One step's claimed key: its hash, and the step it names as an entry records it. */
|
|
38
|
+
export interface ClaimedKey {
|
|
39
|
+
readonly keyHash: string;
|
|
40
|
+
/** The step's identity before redaction; the recorder redacts it on the way to disk. */
|
|
41
|
+
readonly step: TraceProvenance;
|
|
42
|
+
}
|
|
33
43
|
export interface AgentCacheContext {
|
|
34
44
|
readonly mode: 'read-only' | 'read-write';
|
|
35
45
|
readonly store: CacheStore;
|
|
36
|
-
/** Test and target a write is recorded for, as `e2e cache ls` prints them. */
|
|
37
|
-
readonly identity: {
|
|
38
|
-
readonly testId: string;
|
|
39
|
-
readonly targetId: string;
|
|
40
|
-
};
|
|
41
46
|
/** Whether this attempt may replay; writes are governed by `mode` alone. */
|
|
42
47
|
readonly replayEligible: boolean;
|
|
43
48
|
/**
|
|
44
49
|
* A recording that no longer replays fails its step (`REPLAY_STALE`)
|
|
45
50
|
* instead of handing it to the executor, with `advice` on how to re-record
|
|
46
|
-
* it: the knobs to turn off and where the entry lives.
|
|
51
|
+
* it: the knobs to turn off and where the entry lives. `recordings`, set
|
|
52
|
+
* when the store can be listed, finds the recording of a step whose key
|
|
53
|
+
* changed under it, which is stale too rather than missing.
|
|
47
54
|
*/
|
|
48
55
|
readonly strict: false | {
|
|
49
56
|
readonly advice: string;
|
|
57
|
+
readonly recordings?: StoredRecordings;
|
|
50
58
|
};
|
|
51
59
|
/**
|
|
52
|
-
* Claims one step's key
|
|
53
|
-
* the per-attempt occurrence index for its signature, which is
|
|
54
|
-
* test repeat the same instruction and cache each occurrence
|
|
60
|
+
* Claims one step's key. Not a pure derivation: each claim advances
|
|
61
|
+
* the per-attempt occurrence index for its agent and signature, which is
|
|
62
|
+
* what lets a test repeat the same instruction and cache each occurrence
|
|
63
|
+
* separately.
|
|
55
64
|
* Exactly one claim per dispatched step, in execution order — the
|
|
56
65
|
* `StepTraceSession` constructor is the sole caller and owns that
|
|
57
66
|
* invariant structurally.
|
|
58
67
|
*/
|
|
59
|
-
|
|
68
|
+
claimKey(kind: TraceCacheKind, instruction: string, params: Readonly<Record<string, JsonValue>> | undefined, agent: CacheAgentIdentity): ClaimedKey;
|
|
60
69
|
/**
|
|
61
70
|
* Trace writes staged during the attempt. A trace is not trusted the moment
|
|
62
71
|
* its own step passes — the verification step after it is what proves the
|
|
@@ -93,7 +102,7 @@ export interface AttemptSettlement {
|
|
|
93
102
|
* for an interrupted attempt: interruption implicates nothing, so it writes
|
|
94
103
|
* nothing and evicts nothing. An entry a step replayed whole is staged too,
|
|
95
104
|
* so the same rule evicts it when nothing confirmed it; when something did,
|
|
96
|
-
* it is left exactly as it was found.
|
|
105
|
+
* it is left exactly as it was found, but for provenance it lacked.
|
|
97
106
|
*/
|
|
98
107
|
export declare function flushStagedTraces(context: AgentCacheContext, settlement: AttemptSettlement): Promise<void>;
|
|
99
108
|
/**
|
|
@@ -110,4 +119,6 @@ export declare function createAgentCacheContext(options: {
|
|
|
110
119
|
readonly testId: string;
|
|
111
120
|
readonly target: CacheTargetIdentity;
|
|
112
121
|
readonly attemptIndex: number;
|
|
122
|
+
/** The file store's listing under `cache.strict`, shared by the attempts of one worker and target (`storedRecordingsFor`). */
|
|
123
|
+
readonly recordings?: StoredRecordings | undefined;
|
|
113
124
|
}): AgentCacheContext | undefined;
|