@e2edev/e2e 0.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/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/agent/act-validation.d.ts +27 -0
- package/dist/agent/act-validation.d.ts.map +1 -0
- package/dist/agent/act-validation.js +121 -0
- package/dist/agent/act-validation.js.map +1 -0
- package/dist/agent/act.d.ts +27 -0
- package/dist/agent/act.d.ts.map +1 -0
- package/dist/agent/act.js +775 -0
- package/dist/agent/act.js.map +1 -0
- package/dist/agent/actions.d.ts +66 -0
- package/dist/agent/actions.d.ts.map +1 -0
- package/dist/agent/actions.js +82 -0
- package/dist/agent/actions.js.map +1 -0
- package/dist/agent/ai-sdk.d.ts +16 -0
- package/dist/agent/ai-sdk.d.ts.map +1 -0
- package/dist/agent/ai-sdk.js +59 -0
- package/dist/agent/ai-sdk.js.map +1 -0
- package/dist/agent/call-options.d.ts +10 -0
- package/dist/agent/call-options.d.ts.map +1 -0
- package/dist/agent/call-options.js +28 -0
- package/dist/agent/call-options.js.map +1 -0
- package/dist/agent/default-agent.d.ts +22 -0
- package/dist/agent/default-agent.d.ts.map +1 -0
- package/dist/agent/default-agent.js +354 -0
- package/dist/agent/default-agent.js.map +1 -0
- package/dist/agent/error.d.ts +53 -0
- package/dist/agent/error.d.ts.map +1 -0
- package/dist/agent/error.js +94 -0
- package/dist/agent/error.js.map +1 -0
- package/dist/agent/executor.d.ts +238 -0
- package/dist/agent/executor.d.ts.map +1 -0
- package/dist/agent/executor.js +56 -0
- package/dist/agent/executor.js.map +1 -0
- package/dist/agent/index.d.ts +15 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +304 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/invocation.d.ts +194 -0
- package/dist/agent/invocation.d.ts.map +1 -0
- package/dist/agent/invocation.js +389 -0
- package/dist/agent/invocation.js.map +1 -0
- package/dist/agent/ledger.d.ts +24 -0
- package/dist/agent/ledger.d.ts.map +1 -0
- package/dist/agent/ledger.js +48 -0
- package/dist/agent/ledger.js.map +1 -0
- package/dist/agent/loop-guards.d.ts +35 -0
- package/dist/agent/loop-guards.d.ts.map +1 -0
- package/dist/agent/loop-guards.js +110 -0
- package/dist/agent/loop-guards.js.map +1 -0
- package/dist/agent/model/adapter.d.ts +97 -0
- package/dist/agent/model/adapter.d.ts.map +1 -0
- package/dist/agent/model/adapter.js +40 -0
- package/dist/agent/model/adapter.js.map +1 -0
- package/dist/agent/model/router.d.ts +36 -0
- package/dist/agent/model/router.d.ts.map +1 -0
- package/dist/agent/model/router.js +45 -0
- package/dist/agent/model/router.js.map +1 -0
- package/dist/agent/model/schema.d.ts +16 -0
- package/dist/agent/model/schema.d.ts.map +1 -0
- package/dist/agent/model/schema.js +37 -0
- package/dist/agent/model/schema.js.map +1 -0
- package/dist/agent/model/sdk.d.ts +28 -0
- package/dist/agent/model/sdk.d.ts.map +1 -0
- package/dist/agent/model/sdk.js +258 -0
- package/dist/agent/model/sdk.js.map +1 -0
- package/dist/agent/observation.d.ts +70 -0
- package/dist/agent/observation.d.ts.map +1 -0
- package/dist/agent/observation.js +174 -0
- package/dist/agent/observation.js.map +1 -0
- package/dist/agent/phases.d.ts +71 -0
- package/dist/agent/phases.d.ts.map +1 -0
- package/dist/agent/phases.js +124 -0
- package/dist/agent/phases.js.map +1 -0
- package/dist/agent/prompts.d.ts +40 -0
- package/dist/agent/prompts.d.ts.map +1 -0
- package/dist/agent/prompts.js +104 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/protocol.d.ts +31 -0
- package/dist/agent/protocol.d.ts.map +1 -0
- package/dist/agent/protocol.js +67 -0
- package/dist/agent/protocol.js.map +1 -0
- package/dist/agent/public.d.ts +12 -0
- package/dist/agent/public.d.ts.map +1 -0
- package/dist/agent/public.js +12 -0
- package/dist/agent/public.js.map +1 -0
- package/dist/agent/replay.d.ts +61 -0
- package/dist/agent/replay.d.ts.map +1 -0
- package/dist/agent/replay.js +185 -0
- package/dist/agent/replay.js.map +1 -0
- package/dist/agent/secrets.d.ts +22 -0
- package/dist/agent/secrets.d.ts.map +1 -0
- package/dist/agent/secrets.js +63 -0
- package/dist/agent/secrets.js.map +1 -0
- package/dist/agent/step-cache.d.ts +156 -0
- package/dist/agent/step-cache.d.ts.map +1 -0
- package/dist/agent/step-cache.js +301 -0
- package/dist/agent/step-cache.js.map +1 -0
- package/dist/agent/tool-loop.d.ts +51 -0
- package/dist/agent/tool-loop.d.ts.map +1 -0
- package/dist/agent/tool-loop.js +331 -0
- package/dist/agent/tool-loop.js.map +1 -0
- package/dist/agent/tool.d.ts +39 -0
- package/dist/agent/tool.d.ts.map +1 -0
- package/dist/agent/tool.js +52 -0
- package/dist/agent/tool.js.map +1 -0
- package/dist/backend/contract.d.ts +186 -0
- package/dist/backend/contract.d.ts.map +1 -0
- package/dist/backend/contract.js +65 -0
- package/dist/backend/contract.js.map +1 -0
- package/dist/backend/index.d.ts +316 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +219 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/session.d.ts +23 -0
- package/dist/backend/session.d.ts.map +1 -0
- package/dist/backend/session.js +213 -0
- package/dist/backend/session.js.map +1 -0
- package/dist/backend/surface.d.ts +83 -0
- package/dist/backend/surface.d.ts.map +1 -0
- package/dist/backend/surface.js +11 -0
- package/dist/backend/surface.js.map +1 -0
- package/dist/cache/anchors.d.ts +37 -0
- package/dist/cache/anchors.d.ts.map +1 -0
- package/dist/cache/anchors.js +89 -0
- package/dist/cache/anchors.js.map +1 -0
- package/dist/cache/context.d.ts +74 -0
- package/dist/cache/context.d.ts.map +1 -0
- package/dist/cache/context.js +82 -0
- package/dist/cache/context.js.map +1 -0
- package/dist/cache/decide.d.ts +42 -0
- package/dist/cache/decide.d.ts.map +1 -0
- package/dist/cache/decide.js +47 -0
- package/dist/cache/decide.js.map +1 -0
- package/dist/cache/identity.d.ts +113 -0
- package/dist/cache/identity.d.ts.map +1 -0
- package/dist/cache/identity.js +109 -0
- package/dist/cache/identity.js.map +1 -0
- package/dist/cache/recorder.d.ts +76 -0
- package/dist/cache/recorder.d.ts.map +1 -0
- package/dist/cache/recorder.js +143 -0
- package/dist/cache/recorder.js.map +1 -0
- package/dist/cache/relocate.d.ts +69 -0
- package/dist/cache/relocate.d.ts.map +1 -0
- package/dist/cache/relocate.js +125 -0
- package/dist/cache/relocate.js.map +1 -0
- package/dist/cache/store.d.ts +68 -0
- package/dist/cache/store.d.ts.map +1 -0
- package/dist/cache/store.js +106 -0
- package/dist/cache/store.js.map +1 -0
- package/dist/cache/trace.d.ts +146 -0
- package/dist/cache/trace.d.ts.map +1 -0
- package/dist/cache/trace.js +249 -0
- package/dist/cache/trace.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +4 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +108 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +6 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +101 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/collect/collect.d.ts +44 -0
- package/dist/collect/collect.d.ts.map +1 -0
- package/dist/collect/collect.js +89 -0
- package/dist/collect/collect.js.map +1 -0
- package/dist/collect/registry.d.ts +47 -0
- package/dist/collect/registry.d.ts.map +1 -0
- package/dist/collect/registry.js +262 -0
- package/dist/collect/registry.js.map +1 -0
- package/dist/collect/select.d.ts +54 -0
- package/dist/collect/select.d.ts.map +1 -0
- package/dist/collect/select.js +230 -0
- package/dist/collect/select.js.map +1 -0
- package/dist/config/agent.d.ts +91 -0
- package/dist/config/agent.d.ts.map +1 -0
- package/dist/config/agent.js +232 -0
- package/dist/config/agent.js.map +1 -0
- package/dist/config/load.d.ts +23 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +75 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/resolve.d.ts +94 -0
- package/dist/config/resolve.d.ts.map +1 -0
- package/dist/config/resolve.js +500 -0
- package/dist/config/resolve.js.map +1 -0
- package/dist/config/validate.d.ts +6 -0
- package/dist/config/validate.d.ts.map +1 -0
- package/dist/config/validate.js +21 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/credentials.d.ts +7 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +42 -0
- package/dist/credentials.js.map +1 -0
- package/dist/expect/async.d.ts +5 -0
- package/dist/expect/async.d.ts.map +1 -0
- package/dist/expect/async.js +186 -0
- package/dist/expect/async.js.map +1 -0
- package/dist/expect/index.d.ts +7 -0
- package/dist/expect/index.d.ts.map +1 -0
- package/dist/expect/index.js +23 -0
- package/dist/expect/index.js.map +1 -0
- package/dist/expect/values.d.ts +4 -0
- package/dist/expect/values.d.ts.map +1 -0
- package/dist/expect/values.js +108 -0
- package/dist/expect/values.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/ai-trace.d.ts +151 -0
- package/dist/internal/ai-trace.d.ts.map +1 -0
- package/dist/internal/ai-trace.js +360 -0
- package/dist/internal/ai-trace.js.map +1 -0
- package/dist/internal/brands.d.ts +9 -0
- package/dist/internal/brands.d.ts.map +1 -0
- package/dist/internal/brands.js +9 -0
- package/dist/internal/brands.js.map +1 -0
- package/dist/internal/debug.d.ts +43 -0
- package/dist/internal/debug.d.ts.map +1 -0
- package/dist/internal/debug.js +102 -0
- package/dist/internal/debug.js.map +1 -0
- package/dist/internal/errors.d.ts +98 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +212 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/globs.d.ts +29 -0
- package/dist/internal/globs.d.ts.map +1 -0
- package/dist/internal/globs.js +126 -0
- package/dist/internal/globs.js.map +1 -0
- package/dist/internal/ids.d.ts +32 -0
- package/dist/internal/ids.d.ts.map +1 -0
- package/dist/internal/ids.js +124 -0
- package/dist/internal/ids.js.map +1 -0
- package/dist/internal/json-value.d.ts +15 -0
- package/dist/internal/json-value.d.ts.map +1 -0
- package/dist/internal/json-value.js +61 -0
- package/dist/internal/json-value.js.map +1 -0
- package/dist/internal/package-version.d.ts +6 -0
- package/dist/internal/package-version.d.ts.map +1 -0
- package/dist/internal/package-version.js +14 -0
- package/dist/internal/package-version.js.map +1 -0
- package/dist/internal/realm-slot.d.ts +13 -0
- package/dist/internal/realm-slot.d.ts.map +1 -0
- package/dist/internal/realm-slot.js +23 -0
- package/dist/internal/realm-slot.js.map +1 -0
- package/dist/internal/redact.d.ts +33 -0
- package/dist/internal/redact.d.ts.map +1 -0
- package/dist/internal/redact.js +56 -0
- package/dist/internal/redact.js.map +1 -0
- package/dist/internal/regexp.d.ts +10 -0
- package/dist/internal/regexp.d.ts.map +1 -0
- package/dist/internal/regexp.js +27 -0
- package/dist/internal/regexp.js.map +1 -0
- package/dist/internal/text.d.ts +39 -0
- package/dist/internal/text.d.ts.map +1 -0
- package/dist/internal/text.js +66 -0
- package/dist/internal/text.js.map +1 -0
- package/dist/internal/time.d.ts +37 -0
- package/dist/internal/time.d.ts.map +1 -0
- package/dist/internal/time.js +91 -0
- package/dist/internal/time.js.map +1 -0
- package/dist/internal/trace.d.ts +18 -0
- package/dist/internal/trace.d.ts.map +1 -0
- package/dist/internal/trace.js +46 -0
- package/dist/internal/trace.js.map +1 -0
- package/dist/internal/urls.d.ts +31 -0
- package/dist/internal/urls.d.ts.map +1 -0
- package/dist/internal/urls.js +95 -0
- package/dist/internal/urls.js.map +1 -0
- package/dist/locator/engine.d.ts +65 -0
- package/dist/locator/engine.d.ts.map +1 -0
- package/dist/locator/engine.js +164 -0
- package/dist/locator/engine.js.map +1 -0
- package/dist/locator/expression.d.ts +19 -0
- package/dist/locator/expression.d.ts.map +1 -0
- package/dist/locator/expression.js +85 -0
- package/dist/locator/expression.js.map +1 -0
- package/dist/locator/screen.d.ts +36 -0
- package/dist/locator/screen.d.ts.map +1 -0
- package/dist/locator/screen.js +281 -0
- package/dist/locator/screen.js.map +1 -0
- package/dist/report/build.d.ts +214 -0
- package/dist/report/build.d.ts.map +1 -0
- package/dist/report/build.js +346 -0
- package/dist/report/build.js.map +1 -0
- package/dist/report/code-frame.d.ts +20 -0
- package/dist/report/code-frame.d.ts.map +1 -0
- package/dist/report/code-frame.js +73 -0
- package/dist/report/code-frame.js.map +1 -0
- package/dist/report/debug-steps.d.ts +14 -0
- package/dist/report/debug-steps.d.ts.map +1 -0
- package/dist/report/debug-steps.js +85 -0
- package/dist/report/debug-steps.js.map +1 -0
- package/dist/report/list.d.ts +80 -0
- package/dist/report/list.d.ts.map +1 -0
- package/dist/report/list.js +372 -0
- package/dist/report/list.js.map +1 -0
- package/dist/report/live-status.d.ts +51 -0
- package/dist/report/live-status.d.ts.map +1 -0
- package/dist/report/live-status.js +162 -0
- package/dist/report/live-status.js.map +1 -0
- package/dist/report/write.d.ts +4 -0
- package/dist/report/write.d.ts.map +1 -0
- package/dist/report/write.js +13 -0
- package/dist/report/write.js.map +1 -0
- package/dist/run/app-process.d.ts +18 -0
- package/dist/run/app-process.d.ts.map +1 -0
- package/dist/run/app-process.js +112 -0
- package/dist/run/app-process.js.map +1 -0
- package/dist/run/artifacts.d.ts +51 -0
- package/dist/run/artifacts.d.ts.map +1 -0
- package/dist/run/artifacts.js +114 -0
- package/dist/run/artifacts.js.map +1 -0
- package/dist/run/events.d.ts +100 -0
- package/dist/run/events.d.ts.map +1 -0
- package/dist/run/events.js +59 -0
- package/dist/run/events.js.map +1 -0
- package/dist/run/execute.d.ts +119 -0
- package/dist/run/execute.d.ts.map +1 -0
- package/dist/run/execute.js +555 -0
- package/dist/run/execute.js.map +1 -0
- package/dist/run/fixtures.d.ts +40 -0
- package/dist/run/fixtures.d.ts.map +1 -0
- package/dist/run/fixtures.js +408 -0
- package/dist/run/fixtures.js.map +1 -0
- package/dist/run/in-process.d.ts +24 -0
- package/dist/run/in-process.d.ts.map +1 -0
- package/dist/run/in-process.js +120 -0
- package/dist/run/in-process.js.map +1 -0
- package/dist/run/public.d.ts +19 -0
- package/dist/run/public.d.ts.map +1 -0
- package/dist/run/public.js +11 -0
- package/dist/run/public.js.map +1 -0
- package/dist/run/realm.d.ts +52 -0
- package/dist/run/realm.d.ts.map +1 -0
- package/dist/run/realm.js +101 -0
- package/dist/run/realm.js.map +1 -0
- package/dist/run/records.d.ts +87 -0
- package/dist/run/records.d.ts.map +1 -0
- package/dist/run/records.js +3 -0
- package/dist/run/records.js.map +1 -0
- package/dist/run/retry.d.ts +18 -0
- package/dist/run/retry.d.ts.map +1 -0
- package/dist/run/retry.js +33 -0
- package/dist/run/retry.js.map +1 -0
- package/dist/run/runner.d.ts +49 -0
- package/dist/run/runner.d.ts.map +1 -0
- package/dist/run/runner.js +375 -0
- package/dist/run/runner.js.map +1 -0
- package/dist/run/scheduler.d.ts +45 -0
- package/dist/run/scheduler.d.ts.map +1 -0
- package/dist/run/scheduler.js +512 -0
- package/dist/run/scheduler.js.map +1 -0
- package/dist/run/serial.d.ts +52 -0
- package/dist/run/serial.d.ts.map +1 -0
- package/dist/run/serial.js +247 -0
- package/dist/run/serial.js.map +1 -0
- package/dist/run/sessions.d.ts +54 -0
- package/dist/run/sessions.d.ts.map +1 -0
- package/dist/run/sessions.js +221 -0
- package/dist/run/sessions.js.map +1 -0
- package/dist/run/steps.d.ts +196 -0
- package/dist/run/steps.d.ts.map +1 -0
- package/dist/run/steps.js +151 -0
- package/dist/run/steps.js.map +1 -0
- package/dist/run/unit-runner.d.ts +30 -0
- package/dist/run/unit-runner.d.ts.map +1 -0
- package/dist/run/unit-runner.js +11 -0
- package/dist/run/unit-runner.js.map +1 -0
- package/dist/run/units.d.ts +50 -0
- package/dist/run/units.d.ts.map +1 -0
- package/dist/run/units.js +103 -0
- package/dist/run/units.js.map +1 -0
- package/dist/run/worker/entry.d.ts +8 -0
- package/dist/run/worker/entry.d.ts.map +1 -0
- package/dist/run/worker/entry.js +137 -0
- package/dist/run/worker/entry.js.map +1 -0
- package/dist/run/worker/handle.d.ts +10 -0
- package/dist/run/worker/handle.d.ts.map +1 -0
- package/dist/run/worker/handle.js +74 -0
- package/dist/run/worker/handle.js.map +1 -0
- package/dist/run/worker/protocol.d.ts +129 -0
- package/dist/run/worker/protocol.d.ts.map +1 -0
- package/dist/run/worker/protocol.js +18 -0
- package/dist/run/worker/protocol.js.map +1 -0
- package/dist/run/worker/session.d.ts +69 -0
- package/dist/run/worker/session.d.ts.map +1 -0
- package/dist/run/worker/session.js +123 -0
- package/dist/run/worker/session.js.map +1 -0
- package/dist/types.d.ts +630 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic target relocation (RFC0001 layer 3, cache-in decision).
|
|
3
|
+
*
|
|
4
|
+
* Re-finds a recorded target descriptor in a fresh observation: exactly one
|
|
5
|
+
* node must match, or replay diverges. This is the conservative public
|
|
6
|
+
* ReplayPolicy — no scoring, no fuzzy matching, no vision. A tuned policy may
|
|
7
|
+
* replace it behind the same seam; the fail-closed contract (one match or
|
|
8
|
+
* hand off) is not tunable.
|
|
9
|
+
*
|
|
10
|
+
* Candidates are compared through the same `describeTarget` projection the
|
|
11
|
+
* recorder used, so redaction, whitespace collapsing, and bounding cannot
|
|
12
|
+
* make a node unequal to its own recording. The matching vocabulary — which
|
|
13
|
+
* tiers a descriptor is tried in, and what "equal on the recorded fields"
|
|
14
|
+
* means — is exported so end anchors (`anchors.ts`) are checked by the same
|
|
15
|
+
* rules and can never drift from relocation.
|
|
16
|
+
*/
|
|
17
|
+
import { describeTarget } from '../agent/actions.js';
|
|
18
|
+
/**
|
|
19
|
+
* Version of the replay/relocation policy, part of every cache key. Bumping
|
|
20
|
+
* it cold-starts the cache — which is exactly right when the matching rules
|
|
21
|
+
* change, because an entry recorded under different rules could relocate to a
|
|
22
|
+
* different node.
|
|
23
|
+
*/
|
|
24
|
+
export const REPLAY_POLICY_VERSION = 'conservative/3';
|
|
25
|
+
/** Identity fields that must match whenever the recording captured them. */
|
|
26
|
+
const IDENTITY_FIELDS = ['role', 'name', 'testId', 'placeholder', 'inputPurpose'];
|
|
27
|
+
/** Text is identity too when neither a test id nor a name was recorded. */
|
|
28
|
+
const IDENTITY_FIELDS_WITH_TEXT = [...IDENTITY_FIELDS, 'text'];
|
|
29
|
+
/**
|
|
30
|
+
* Whether a descriptor can identify a node at all. Role or selector alone
|
|
31
|
+
* cannot: a wrong match acts on the wrong control, so such a descriptor is
|
|
32
|
+
* not relocatable and, as an anchor, would prove nothing.
|
|
33
|
+
*/
|
|
34
|
+
function isRelocatableDescriptor(descriptor) {
|
|
35
|
+
return (descriptor.testId !== undefined ||
|
|
36
|
+
descriptor.name !== undefined ||
|
|
37
|
+
descriptor.text !== undefined ||
|
|
38
|
+
descriptor.placeholder !== undefined);
|
|
39
|
+
}
|
|
40
|
+
/** The semantic tier of a descriptor: every identity field but the test id. */
|
|
41
|
+
function withoutTestId(descriptor) {
|
|
42
|
+
const { testId: _testId, ...semantic } = descriptor;
|
|
43
|
+
return semantic;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The tiers a recorded descriptor is matched in, strictest first:
|
|
47
|
+
*
|
|
48
|
+
* 1. **Strict** — every identity field the recording captured must match.
|
|
49
|
+
* 2. **Semantic** — only when a `testId` was recorded and the remaining
|
|
50
|
+
* fields still identify the node: the same descriptor without it. Test
|
|
51
|
+
* ids are the strongest discriminator when stable, but some apps mint
|
|
52
|
+
* them per render; a node the semantic fields still identify has not
|
|
53
|
+
* moved, its label has not changed, and refusing it would fail on
|
|
54
|
+
* cosmetics.
|
|
55
|
+
*
|
|
56
|
+
* Empty for a descriptor that identifies nothing.
|
|
57
|
+
*/
|
|
58
|
+
export function descriptorTiers(descriptor) {
|
|
59
|
+
if (!isRelocatableDescriptor(descriptor))
|
|
60
|
+
return [];
|
|
61
|
+
if (descriptor.testId === undefined)
|
|
62
|
+
return [descriptor];
|
|
63
|
+
const semantic = withoutTestId(descriptor);
|
|
64
|
+
return isRelocatableDescriptor(semantic) ? [descriptor, semantic] : [descriptor];
|
|
65
|
+
}
|
|
66
|
+
/** Every listed field the recording captured must be present and equal on the candidate. */
|
|
67
|
+
export function fieldsEqual(recorded, candidate, fields) {
|
|
68
|
+
return fields.every((field) => recorded[field] === undefined || candidate[field] === recorded[field]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Descriptor projections per observation. A replay relocates every recorded
|
|
72
|
+
* action, in two tiers, against the same node map (and again per settling
|
|
73
|
+
* retry), and the anchor check projects it once more, while the projection of
|
|
74
|
+
* a node is a pure function of the node: it is computed once per observation
|
|
75
|
+
* and shared by every lookup into it.
|
|
76
|
+
*/
|
|
77
|
+
const projections = new WeakMap();
|
|
78
|
+
/** Projects every node of an observation the way the recorder described its targets. */
|
|
79
|
+
export function describeNodes(nodes, options) {
|
|
80
|
+
const cached = projections.get(nodes);
|
|
81
|
+
if (cached !== undefined &&
|
|
82
|
+
cached.redact === options.redact &&
|
|
83
|
+
cached.testIdAttribute === options.testIdAttribute) {
|
|
84
|
+
return cached.described;
|
|
85
|
+
}
|
|
86
|
+
const described = [];
|
|
87
|
+
for (const [id, node] of nodes) {
|
|
88
|
+
const descriptor = describeTarget(node, options.redact, options.testIdAttribute);
|
|
89
|
+
if (descriptor !== undefined)
|
|
90
|
+
described.push({ id, descriptor });
|
|
91
|
+
}
|
|
92
|
+
projections.set(nodes, { redact: options.redact, testIdAttribute: options.testIdAttribute, described });
|
|
93
|
+
return described;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Relocates one descriptor against the nodes of a fresh observation, tier by
|
|
97
|
+
* tier (`descriptorTiers`), each exactly-one-or-diverge. Ambiguity at any
|
|
98
|
+
* tier diverges immediately: two candidates sharing the matched identity
|
|
99
|
+
* cannot be told apart by waiting, and acting on either would be a guess.
|
|
100
|
+
*/
|
|
101
|
+
export function relocateDescriptor(descriptor, nodes, options) {
|
|
102
|
+
const candidates = describeNodes(nodes, options);
|
|
103
|
+
for (const tier of descriptorTiers(descriptor)) {
|
|
104
|
+
const result = matchTier(tier, candidates);
|
|
105
|
+
if (result.kind === 'found' || result.failure === 'target-ambiguous')
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
return { kind: 'failed', failure: 'target-not-found' };
|
|
109
|
+
}
|
|
110
|
+
function matchTier(tier, candidates) {
|
|
111
|
+
const fields = tier.testId === undefined && tier.name === undefined ? IDENTITY_FIELDS_WITH_TEXT : IDENTITY_FIELDS;
|
|
112
|
+
const matches = [];
|
|
113
|
+
for (const candidate of candidates) {
|
|
114
|
+
if (!fieldsEqual(tier, candidate.descriptor, fields))
|
|
115
|
+
continue;
|
|
116
|
+
matches.push(candidate.id);
|
|
117
|
+
if (matches.length > 1)
|
|
118
|
+
return { kind: 'failed', failure: 'target-ambiguous' };
|
|
119
|
+
}
|
|
120
|
+
const only = matches[0];
|
|
121
|
+
if (only === undefined)
|
|
122
|
+
return { kind: 'failed', failure: 'target-not-found' };
|
|
123
|
+
return { kind: 'found', id: only };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=relocate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relocate.js","sourceRoot":"","sources":["../../src/cache/relocate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace entry storage (RFC0001 layer 3, cache-in decision).
|
|
3
|
+
*
|
|
4
|
+
* One file per key under `.e2e/cache/`, named for the key digest. Reads fail
|
|
5
|
+
* closed: an oversized or unreadable entry is a miss, never a repair. Writes go
|
|
6
|
+
* through a temporary file and an atomic rename, so a crashed or concurrent
|
|
7
|
+
* writer cannot leave a torn entry behind.
|
|
8
|
+
*
|
|
9
|
+
* There is no locking. Two writers only ever collide on a key when the same
|
|
10
|
+
* step runs concurrently on the same path, and then they are writing the same
|
|
11
|
+
* flow, so last-write-wins is the correct outcome rather than a hazard.
|
|
12
|
+
*
|
|
13
|
+
* The interface exists so a remote store — the cloud's shared cache — can
|
|
14
|
+
* replace the filesystem without the harness noticing.
|
|
15
|
+
*/
|
|
16
|
+
import { type ActionTrace, type TraceEntry } from './trace.ts';
|
|
17
|
+
/** Hard wire ceiling on one entry, on both the read and the write path. */
|
|
18
|
+
export declare const MAX_CACHE_WIRE_BYTES = 1048576;
|
|
19
|
+
export type CacheReadResult = {
|
|
20
|
+
readonly status: 'hit';
|
|
21
|
+
readonly entry: TraceEntry;
|
|
22
|
+
readonly bytes: number;
|
|
23
|
+
} | {
|
|
24
|
+
readonly status: 'miss';
|
|
25
|
+
} | {
|
|
26
|
+
readonly status: 'invalid';
|
|
27
|
+
readonly reason: string;
|
|
28
|
+
readonly bytes?: number;
|
|
29
|
+
};
|
|
30
|
+
export interface TraceCacheStore {
|
|
31
|
+
readonly writable: boolean;
|
|
32
|
+
read(keyHash: string): Promise<CacheReadResult>;
|
|
33
|
+
/** Persists one trace, returning its size, or undefined when not written. */
|
|
34
|
+
write(keyHash: string, payload: ActionTrace): Promise<{
|
|
35
|
+
bytes: number;
|
|
36
|
+
} | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Evicts one entry, best-effort. Called when a replayed or just-recorded
|
|
39
|
+
* flow is implicated in a failed attempt: the entry re-records on the next
|
|
40
|
+
* pass instead of replaying a poisoned flow forever. Optional — a store
|
|
41
|
+
* without eviction merely stays stale until the next confirmed write.
|
|
42
|
+
*/
|
|
43
|
+
delete?(keyHash: string): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export interface FileTraceCacheStoreOptions {
|
|
46
|
+
readonly directory: string;
|
|
47
|
+
/** Entry size cap for tests; production passes the wire ceiling, which clamps it. */
|
|
48
|
+
readonly maxBytes: number;
|
|
49
|
+
readonly writable: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare class FileTraceCacheStore implements TraceCacheStore {
|
|
52
|
+
readonly writable: boolean;
|
|
53
|
+
private readonly directory;
|
|
54
|
+
private readonly maxBytes;
|
|
55
|
+
constructor(options: FileTraceCacheStoreOptions);
|
|
56
|
+
read(keyHash: string): Promise<CacheReadResult>;
|
|
57
|
+
write(keyHash: string, payload: ActionTrace): Promise<{
|
|
58
|
+
bytes: number;
|
|
59
|
+
} | undefined>;
|
|
60
|
+
delete(keyHash: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Resolves one entry path. The key hash is the only caller-supplied path
|
|
63
|
+
* component and must be a bare digest, so traversal is impossible by
|
|
64
|
+
* construction rather than by canonicalization after the fact.
|
|
65
|
+
*/
|
|
66
|
+
private entryPath;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,EAAmC,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhG,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,UAAY,CAAC;AAI9C,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAChD,6EAA6E;IAC7E,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IACrF;;;;;OAKG;IACH,MAAM,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,qBAAa,mBAAoB,YAAW,eAAe;IACzD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YAAY,OAAO,EAAE,0BAA0B,EAI9C;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA6BpD;IAEK,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAsBzF;IAEK,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3C;IAED;;;;OAIG;IACH,OAAO,CAAC,SAAS;CAGlB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace entry storage (RFC0001 layer 3, cache-in decision).
|
|
3
|
+
*
|
|
4
|
+
* One file per key under `.e2e/cache/`, named for the key digest. Reads fail
|
|
5
|
+
* closed: an oversized or unreadable entry is a miss, never a repair. Writes go
|
|
6
|
+
* through a temporary file and an atomic rename, so a crashed or concurrent
|
|
7
|
+
* writer cannot leave a torn entry behind.
|
|
8
|
+
*
|
|
9
|
+
* There is no locking. Two writers only ever collide on a key when the same
|
|
10
|
+
* step runs concurrently on the same path, and then they are writing the same
|
|
11
|
+
* flow, so last-write-wins is the correct outcome rather than a hazard.
|
|
12
|
+
*
|
|
13
|
+
* The interface exists so a remote store — the cloud's shared cache — can
|
|
14
|
+
* replace the filesystem without the harness noticing.
|
|
15
|
+
*/
|
|
16
|
+
import { randomBytes } from 'node:crypto';
|
|
17
|
+
import { mkdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { errorMessage } from '../internal/errors.js';
|
|
20
|
+
import { buildTraceEntry, readTraceEntry } from './trace.js';
|
|
21
|
+
/** Hard wire ceiling on one entry, on both the read and the write path. */
|
|
22
|
+
export const MAX_CACHE_WIRE_BYTES = 1_048_576;
|
|
23
|
+
const KEY_HASH = /^[a-f0-9]{64}$/u;
|
|
24
|
+
export class FileTraceCacheStore {
|
|
25
|
+
writable;
|
|
26
|
+
directory;
|
|
27
|
+
maxBytes;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.directory = options.directory;
|
|
30
|
+
this.maxBytes = Math.min(options.maxBytes, MAX_CACHE_WIRE_BYTES);
|
|
31
|
+
this.writable = options.writable;
|
|
32
|
+
}
|
|
33
|
+
async read(keyHash) {
|
|
34
|
+
const path = this.entryPath(keyHash);
|
|
35
|
+
if (path === undefined)
|
|
36
|
+
return { status: 'invalid', reason: 'key hash is not a SHA-256 digest' };
|
|
37
|
+
let bytes;
|
|
38
|
+
try {
|
|
39
|
+
bytes = (await stat(path)).size;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return { status: 'miss' };
|
|
43
|
+
}
|
|
44
|
+
// Checked before the read so an oversized file is never pulled into memory.
|
|
45
|
+
if (bytes > this.maxBytes) {
|
|
46
|
+
return {
|
|
47
|
+
status: 'invalid',
|
|
48
|
+
reason: `cache entry is ${bytes} bytes, over the ${this.maxBytes}-byte limit`,
|
|
49
|
+
bytes,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
let entry;
|
|
53
|
+
try {
|
|
54
|
+
entry = readTraceEntry(JSON.parse(await readFile(path, 'utf8')));
|
|
55
|
+
}
|
|
56
|
+
catch (cause) {
|
|
57
|
+
return { status: 'invalid', reason: `cache entry is unreadable: ${errorMessage(cause)}`, bytes };
|
|
58
|
+
}
|
|
59
|
+
if (entry === undefined) {
|
|
60
|
+
return { status: 'invalid', reason: 'cache entry is not a replayable trace-1 entry', bytes };
|
|
61
|
+
}
|
|
62
|
+
return { status: 'hit', entry, bytes };
|
|
63
|
+
}
|
|
64
|
+
async write(keyHash, payload) {
|
|
65
|
+
if (!this.writable)
|
|
66
|
+
return undefined;
|
|
67
|
+
const path = this.entryPath(keyHash);
|
|
68
|
+
if (path === undefined)
|
|
69
|
+
return undefined;
|
|
70
|
+
const serialized = `${JSON.stringify(buildTraceEntry(payload), null, 2)}\n`;
|
|
71
|
+
const bytes = Buffer.byteLength(serialized, 'utf8');
|
|
72
|
+
if (bytes > this.maxBytes)
|
|
73
|
+
return undefined;
|
|
74
|
+
await mkdir(this.directory, { recursive: true });
|
|
75
|
+
// Random rather than pid+clock: two writers racing on one key in the same
|
|
76
|
+
// millisecond would otherwise pick the same temporary name, and the first
|
|
77
|
+
// rename would pull the file out from under the second.
|
|
78
|
+
const temporary = `${path}.${randomBytes(8).toString('hex')}.tmp`;
|
|
79
|
+
try {
|
|
80
|
+
await writeFile(temporary, serialized, { encoding: 'utf8', mode: 0o600 });
|
|
81
|
+
await rename(temporary, path);
|
|
82
|
+
}
|
|
83
|
+
catch (cause) {
|
|
84
|
+
await unlink(temporary).catch(() => undefined);
|
|
85
|
+
throw cause;
|
|
86
|
+
}
|
|
87
|
+
return { bytes };
|
|
88
|
+
}
|
|
89
|
+
async delete(keyHash) {
|
|
90
|
+
if (!this.writable)
|
|
91
|
+
return;
|
|
92
|
+
const path = this.entryPath(keyHash);
|
|
93
|
+
if (path === undefined)
|
|
94
|
+
return;
|
|
95
|
+
await unlink(path).catch(() => undefined);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolves one entry path. The key hash is the only caller-supplied path
|
|
99
|
+
* component and must be a bare digest, so traversal is impossible by
|
|
100
|
+
* construction rather than by canonicalization after the fact.
|
|
101
|
+
*/
|
|
102
|
+
entryPath(keyHash) {
|
|
103
|
+
return KEY_HASH.test(keyHash) ? join(this.directory, `${keyHash}.json`) : undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `trace-1` entry format (RFC0001 layer 3, cache-in decision).
|
|
3
|
+
*
|
|
4
|
+
* A trace is the ordered list of grammar actions one passing `agent.act()`
|
|
5
|
+
* step performed, each with a durable target descriptor and secret-free,
|
|
6
|
+
* verbatim input — never model output, never plaintext secrets. An entry is
|
|
7
|
+
* deliberately small: a schema version, when it was written, and the trace
|
|
8
|
+
* with the provenance it had. It does not repeat its own key: the file name
|
|
9
|
+
* is the key digest and a reader only ever opens the digest of the key it
|
|
10
|
+
* just computed.
|
|
11
|
+
*
|
|
12
|
+
* Actions are a discriminated union: each variant carries exactly the typed
|
|
13
|
+
* input its grammar action needs, so validation here is the one owner of
|
|
14
|
+
* replayability — a document that reads back as an entry replays verbatim,
|
|
15
|
+
* and a document this runner cannot replay verbatim comes back as
|
|
16
|
+
* `undefined`, never as a repaired or partially trusted entry.
|
|
17
|
+
*/
|
|
18
|
+
import type { ScrollDirection } from '../types.ts';
|
|
19
|
+
export declare const TRACE_SCHEMA_VERSION = "trace-1";
|
|
20
|
+
/** Mirrors the default step action budget; overflow marks the trace truncated. */
|
|
21
|
+
export declare const MAX_TRACE_ACTIONS = 50;
|
|
22
|
+
/** Prose caps keep an entry a receipt, not a transcript. */
|
|
23
|
+
export declare const MAX_TRACE_SUMMARY_CHARS = 300;
|
|
24
|
+
export declare const MAX_TRACE_DESCRIPTOR_CHARS = 300;
|
|
25
|
+
/**
|
|
26
|
+
* Cap on verbatim replay inputs (typed text, URLs, option labels). Inputs are
|
|
27
|
+
* never truncated to fit — a trimmed URL or half a typed value would replay a
|
|
28
|
+
* different action than the one recorded — so an oversized input poisons the
|
|
29
|
+
* trace (`truncated`) instead of bending the value.
|
|
30
|
+
*/
|
|
31
|
+
export declare const MAX_TRACE_INPUT_CHARS = 4096;
|
|
32
|
+
/** Caps prose at `maxChars`, marking the cut with an ellipsis. */
|
|
33
|
+
export declare function bound(text: string, maxChars: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Cap on recorded end anchors. Anchors are the step's own delta — what
|
|
36
|
+
* appeared on screen between the first observation and the passing one — so
|
|
37
|
+
* a same-screen mutation rarely has more than a handful; a step that changes
|
|
38
|
+
* the whole screen keeps the first few in document order.
|
|
39
|
+
*/
|
|
40
|
+
export declare const MAX_TRACE_ANCHORS = 8;
|
|
41
|
+
/**
|
|
42
|
+
* How a recorded action addressed its node, independent of observation ids.
|
|
43
|
+
* Ids are minted per observation, so replay re-finds the node from the
|
|
44
|
+
* semantic fields against a fresh observation, and whatever they resolve to
|
|
45
|
+
* is still checked before use. The structural `selector` is captured as
|
|
46
|
+
* provenance for tuned replay policies; the conservative policy ignores it.
|
|
47
|
+
*/
|
|
48
|
+
export interface TraceTargetDescriptor {
|
|
49
|
+
readonly role?: string;
|
|
50
|
+
readonly name?: string;
|
|
51
|
+
readonly text?: string;
|
|
52
|
+
readonly testId?: string;
|
|
53
|
+
readonly placeholder?: string;
|
|
54
|
+
readonly selector?: string;
|
|
55
|
+
readonly inputPurpose?: string;
|
|
56
|
+
}
|
|
57
|
+
interface ActionBase {
|
|
58
|
+
/** One-line prose summary — the only view a mid-step hand-off notice shows. */
|
|
59
|
+
readonly summary: string;
|
|
60
|
+
}
|
|
61
|
+
export interface TapAction extends ActionBase {
|
|
62
|
+
readonly name: 'tap';
|
|
63
|
+
readonly target: TraceTargetDescriptor;
|
|
64
|
+
}
|
|
65
|
+
export interface TypeAction extends ActionBase {
|
|
66
|
+
readonly name: 'type';
|
|
67
|
+
readonly target: TraceTargetDescriptor;
|
|
68
|
+
readonly value: string;
|
|
69
|
+
}
|
|
70
|
+
/** A secret fill, recorded by its stable name only — never the plaintext. */
|
|
71
|
+
export interface TypeSecretAction extends ActionBase {
|
|
72
|
+
readonly name: 'typeSecret';
|
|
73
|
+
readonly target: TraceTargetDescriptor;
|
|
74
|
+
readonly secret: string;
|
|
75
|
+
}
|
|
76
|
+
export interface PressAction extends ActionBase {
|
|
77
|
+
readonly name: 'press';
|
|
78
|
+
readonly target: TraceTargetDescriptor;
|
|
79
|
+
readonly key: string;
|
|
80
|
+
}
|
|
81
|
+
export interface SelectAction extends ActionBase {
|
|
82
|
+
readonly name: 'select';
|
|
83
|
+
readonly target: TraceTargetDescriptor;
|
|
84
|
+
readonly value: string;
|
|
85
|
+
}
|
|
86
|
+
export interface ScrollAction extends ActionBase {
|
|
87
|
+
readonly name: 'scroll';
|
|
88
|
+
readonly direction: ScrollDirection;
|
|
89
|
+
readonly target?: TraceTargetDescriptor;
|
|
90
|
+
}
|
|
91
|
+
export interface NavigateAction extends ActionBase {
|
|
92
|
+
readonly name: 'navigate';
|
|
93
|
+
readonly url: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A project-tool mutation the grammar cannot reproduce — a gap that ends any
|
|
97
|
+
* replay rather than silently skipping a state change.
|
|
98
|
+
*/
|
|
99
|
+
export interface ToolGapAction extends ActionBase {
|
|
100
|
+
readonly name: 'tool';
|
|
101
|
+
}
|
|
102
|
+
export type RecordedAction = TapAction | TypeAction | TypeSecretAction | PressAction | SelectAction | ScrollAction | NavigateAction | ToolGapAction;
|
|
103
|
+
export interface ActionTrace {
|
|
104
|
+
readonly actions: readonly RecordedAction[];
|
|
105
|
+
/** Executor that produced the trace — provenance, never part of the key (OQ10). */
|
|
106
|
+
readonly executor: {
|
|
107
|
+
readonly name: string;
|
|
108
|
+
readonly version?: string;
|
|
109
|
+
};
|
|
110
|
+
/** The recorded run's verdict summary. */
|
|
111
|
+
readonly summary: string;
|
|
112
|
+
/** Location path when the step began; a precondition unless the trace opens with navigate. */
|
|
113
|
+
readonly startPath?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Location path when the step passed — the trace's deterministic postcondition.
|
|
116
|
+
* A full replay self-finalizes only while the live pathname still matches;
|
|
117
|
+
* a recorded flow whose destination changed hands off instead of passing.
|
|
118
|
+
*/
|
|
119
|
+
readonly endPath?: string;
|
|
120
|
+
/**
|
|
121
|
+
* Descriptors of nodes that were on screen when the step passed and were
|
|
122
|
+
* not there when it began — the recorded run's verification, made
|
|
123
|
+
* mechanical. A full replay self-finalizes only while every anchor is
|
|
124
|
+
* present again; a flow whose actions replayed but whose effect did not
|
|
125
|
+
* (a save that never committed, an unnamed form left behind) hands off
|
|
126
|
+
* instead of passing on mechanics alone.
|
|
127
|
+
*/
|
|
128
|
+
readonly endAnchors?: readonly TraceTargetDescriptor[];
|
|
129
|
+
/** Set when recording overflowed a cap; the trace documents, never replays. */
|
|
130
|
+
readonly truncated?: boolean;
|
|
131
|
+
}
|
|
132
|
+
export interface TraceEntry {
|
|
133
|
+
readonly schemaVersion: typeof TRACE_SCHEMA_VERSION;
|
|
134
|
+
readonly createdAt: string;
|
|
135
|
+
readonly payload: ActionTrace;
|
|
136
|
+
}
|
|
137
|
+
/** Wraps one trace as a fresh entry. */
|
|
138
|
+
export declare function buildTraceEntry(payload: ActionTrace): TraceEntry;
|
|
139
|
+
/**
|
|
140
|
+
* Reads one document as a `trace-1` entry, or returns undefined when it is
|
|
141
|
+
* not one this runner can trust. Unknown fields are dropped rather than
|
|
142
|
+
* carried: the returned entry contains exactly the validated shape.
|
|
143
|
+
*/
|
|
144
|
+
export declare function readTraceEntry(document: unknown): TraceEntry | undefined;
|
|
145
|
+
export {};
|
|
146
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/cache/trace.ts"],"names":[],"mappings":"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"}
|