autokap 1.8.6 → 1.8.8

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.
Files changed (43) hide show
  1. package/dist/action-verifier.d.ts +6 -0
  2. package/dist/action-verifier.js +30 -17
  3. package/dist/browser.d.ts +59 -0
  4. package/dist/browser.js +259 -0
  5. package/dist/cli-config.js +7 -12
  6. package/dist/cli-contract.d.ts +5 -9
  7. package/dist/cli-contract.js +11 -38
  8. package/dist/cli-runner.d.ts +0 -1
  9. package/dist/cli-runner.js +74 -59
  10. package/dist/cli.js +7 -7
  11. package/dist/clip-capture-loop.d.ts +28 -7
  12. package/dist/clip-capture-loop.js +102 -19
  13. package/dist/engine-version.d.ts +24 -0
  14. package/dist/engine-version.js +25 -0
  15. package/dist/execution-schema.d.ts +22 -0
  16. package/dist/execution-schema.js +59 -8
  17. package/dist/execution-types.d.ts +116 -0
  18. package/dist/opcode-runner.d.ts +8 -1
  19. package/dist/opcode-runner.js +120 -29
  20. package/dist/postcondition.d.ts +18 -3
  21. package/dist/postcondition.js +75 -27
  22. package/dist/program-hash.d.ts +11 -0
  23. package/dist/program-hash.js +28 -0
  24. package/dist/program-migrations.d.ts +31 -0
  25. package/dist/program-migrations.js +93 -0
  26. package/dist/program-signing.d.ts +11 -0
  27. package/dist/program-signing.js +1 -0
  28. package/dist/recovery-chain.js +8 -11
  29. package/dist/scenario-cookie.d.ts +36 -0
  30. package/dist/scenario-cookie.js +62 -0
  31. package/dist/security.d.ts +21 -0
  32. package/dist/security.js +46 -8
  33. package/dist/server-credit-usage.d.ts +1 -1
  34. package/dist/version.d.ts +1 -0
  35. package/dist/version.js +1 -0
  36. package/dist/video-narration-schema.d.ts +3 -0
  37. package/dist/video-narration-schema.js +3 -0
  38. package/dist/wait-contract.d.ts +104 -0
  39. package/dist/wait-contract.js +144 -0
  40. package/dist/web-playwright-local.d.ts +9 -1
  41. package/dist/web-playwright-local.js +0 -0
  42. package/package.json +2 -2
  43. package/readme.md +9 -15
@@ -1092,6 +1092,7 @@ export declare const PreconditionSpecSchema: z.ZodObject<{
1092
1092
  domain: z.ZodString;
1093
1093
  path: z.ZodOptional<z.ZodString>;
1094
1094
  }, z.core.$strict>>>;
1095
+ scenario: z.ZodOptional<z.ZodString>;
1095
1096
  }, z.core.$strict>;
1096
1097
  export declare const ArtifactSpecSchema: z.ZodObject<{
1097
1098
  mediaMode: z.ZodEnum<{
@@ -1131,6 +1132,8 @@ export declare const ArtifactSpecSchema: z.ZodObject<{
1131
1132
  export declare const ExecutionProgramSchema: z.ZodObject<{
1132
1133
  presetId: z.ZodString;
1133
1134
  programVersion: z.ZodNumber;
1135
+ programSchemaVersion: z.ZodOptional<z.ZodNumber>;
1136
+ engineVersion: z.ZodOptional<z.ZodNumber>;
1134
1137
  mediaMode: z.ZodEnum<{
1135
1138
  video: "video";
1136
1139
  clip: "clip";
@@ -1241,6 +1244,7 @@ export declare const ExecutionProgramSchema: z.ZodObject<{
1241
1244
  domain: z.ZodString;
1242
1245
  path: z.ZodOptional<z.ZodString>;
1243
1246
  }, z.core.$strict>>>;
1247
+ scenario: z.ZodOptional<z.ZodString>;
1244
1248
  }, z.core.$strict>;
1245
1249
  steps: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
1246
1250
  url: z.ZodString;
@@ -4202,6 +4206,7 @@ export declare function safeParseProgramResult(data: unknown): z.ZodSafeParseRes
4202
4206
  domain: string;
4203
4207
  path?: string | undefined;
4204
4208
  }[] | undefined;
4209
+ scenario?: string | undefined;
4205
4210
  };
4206
4211
  steps: ({
4207
4212
  urlPattern: string;
@@ -4911,6 +4916,8 @@ export declare function safeParseProgramResult(data: unknown): z.ZodSafeParseRes
4911
4916
  };
4912
4917
  compileFingerprint: string;
4913
4918
  compiledAt: string;
4919
+ programSchemaVersion?: number | undefined;
4920
+ engineVersion?: number | undefined;
4914
4921
  maxParallelCaptures?: number | undefined;
4915
4922
  outputScale?: number | undefined;
4916
4923
  variantFingerprint?: string | undefined;
@@ -4930,3 +4937,18 @@ export declare function safeParseProgramResult(data: unknown): z.ZodSafeParseRes
4930
4937
  publicUrl?: string | undefined;
4931
4938
  environmentHttpHeaders?: Record<string, string> | undefined;
4932
4939
  }>;
4940
+ export interface ClipNavigationViolation {
4941
+ /** Index of the offending NAVIGATE opcode in `program.steps`. */
4942
+ stepIndex: number;
4943
+ message: string;
4944
+ }
4945
+ /**
4946
+ * Returns every NAVIGATE opcode located between a BEGIN_CLIP and its END_CLIP.
4947
+ * Pure and mode-agnostic — mirrors the runtime clip window exactly.
4948
+ */
4949
+ export declare function findNavigateInClipViolations(program: Pick<ExecutionProgram, 'steps'>): ClipNavigationViolation[];
4950
+ /**
4951
+ * Hard-rejection variant for authoring boundaries (preset create/update).
4952
+ * Throws with an actionable message if any NAVIGATE sits inside a clip.
4953
+ */
4954
+ export declare function assertNoNavigateInClip(program: Pick<ExecutionProgram, 'steps'>): void;
@@ -4,6 +4,7 @@
4
4
  * Validates ExecutionProgram at compile output (server) and CLI input boundaries.
5
5
  */
6
6
  import { z } from 'zod';
7
+ import { upgradeProgram } from './program-migrations.js';
7
8
  // ── Postcondition ───────────────────────────────────────────────────
8
9
  export const PostconditionSpecSchema = z.object({
9
10
  type: z.enum([
@@ -596,6 +597,8 @@ export const PreconditionSpecSchema = z.object({
596
597
  storageState: StorageStateSchema.optional(),
597
598
  sessionStorage: z.record(z.string(), z.record(z.string(), z.string())).optional(),
598
599
  cookies: z.array(cookieSchema).optional(),
600
+ // AUT-239: active scenario id; runner injects a signed __ak_scenario cookie.
601
+ scenario: z.string().min(1).optional(),
599
602
  }).strict();
600
603
  // ── Artifact spec ───────────────────────────────────────────────────
601
604
  const ResolutionSchema = z.object({
@@ -603,7 +606,6 @@ const ResolutionSchema = z.object({
603
606
  height: z.number().int().positive(),
604
607
  }).strict();
605
608
  const DEFAULT_VIDEO_DELIVERY_RESOLUTION = { width: 1920, height: 1080 };
606
- const LEGACY_VIDEO_CAPTURE_RESOLUTION = { width: 2560, height: 1440 };
607
609
  function resolutionEquals(a, b) {
608
610
  return a.width === b.width && a.height === b.height;
609
611
  }
@@ -623,14 +625,14 @@ export const ArtifactSpecSchema = z.object({
623
625
  }).strict().superRefine((value, ctx) => {
624
626
  if (value.mediaMode === 'video') {
625
627
  const res = value.format?.captureResolution;
626
- const matchesDefault = res ? resolutionEquals(res, DEFAULT_VIDEO_DELIVERY_RESOLUTION) : false;
627
- const matchesLegacy = res ? resolutionEquals(res, LEGACY_VIDEO_CAPTURE_RESOLUTION) : false;
628
- if (!res || (!matchesDefault && !matchesLegacy)) {
628
+ // Legacy 2560×1440 programs are normalized to 1920×1080 by migrate-on-read
629
+ // (program-migrations.ts, migrate_0→1) BEFORE this schema runs, so only the
630
+ // canonical resolution reaches validation here.
631
+ if (!res || !resolutionEquals(res, DEFAULT_VIDEO_DELIVERY_RESOLUTION)) {
629
632
  ctx.addIssue({
630
633
  code: z.ZodIssueCode.custom,
631
634
  path: ['format', 'captureResolution'],
632
- message: "mediaMode='video' requires format.captureResolution = { width: 1920, height: 1080 }; " +
633
- 'legacy 2560x1440 programs are accepted and normalized at runtime',
635
+ message: "mediaMode='video' requires format.captureResolution = { width: 1920, height: 1080 }",
634
636
  });
635
637
  }
636
638
  }
@@ -638,7 +640,17 @@ export const ArtifactSpecSchema = z.object({
638
640
  // ── Full program ────────────────────────────────────────────────────
639
641
  export const ExecutionProgramSchema = z.object({
640
642
  presetId: z.string().min(1),
643
+ // Content-revision counter bumped by the healer; orthogonal to programSchemaVersion (form).
641
644
  programVersion: z.number().int().positive(),
645
+ // FORM version. migrate-on-read (upgradeProgram, run inside parseProgram)
646
+ // stamps this to the current value before validation. Intentionally optional
647
+ // and WITHOUT a Zod default: this schema is reused inside signature
648
+ // verification (program-signing.ts), where injecting a default would mutate
649
+ // the signed payload and break signature symmetry for programs signed without
650
+ // the field. Presence at runtime is guaranteed by upgradeProgram, not here.
651
+ programSchemaVersion: z.number().int().positive().optional(),
652
+ // Provenance: engine semantics the generator targeted. Informational only.
653
+ engineVersion: z.number().int().positive().optional(),
642
654
  mediaMode: z.enum(['screenshot', 'clip', 'video']),
643
655
  baseUrl: StrictUrlSchema,
644
656
  maxParallelCaptures: z.number().int().positive().optional(),
@@ -685,12 +697,51 @@ export const HealerPatchSchema = z.object({
685
697
  }).strict();
686
698
  // ── Typed parse helpers ─────────────────────────────────────────────
687
699
  export function parseProgram(data) {
688
- return ExecutionProgramSchema.parse(data);
700
+ // migrate-on-read: bring any stored FORM up to the current shape before the
701
+ // strict schema runs, so legacy presets parse instead of being rejected en bloc.
702
+ return ExecutionProgramSchema.parse(upgradeProgram(data));
689
703
  }
690
704
  export function parseOpcode(data) {
691
705
  return ExecutionOpcodeSchema.parse(data);
692
706
  }
693
707
  export function safeParseProgramResult(data) {
694
- return ExecutionProgramSchema.safeParse(data);
708
+ return ExecutionProgramSchema.safeParse(upgradeProgram(data));
709
+ }
710
+ /**
711
+ * Returns every NAVIGATE opcode located between a BEGIN_CLIP and its END_CLIP.
712
+ * Pure and mode-agnostic — mirrors the runtime clip window exactly.
713
+ */
714
+ export function findNavigateInClipViolations(program) {
715
+ const violations = [];
716
+ let insideClip = false;
717
+ program.steps.forEach((step, index) => {
718
+ if (step.kind === 'BEGIN_CLIP') {
719
+ insideClip = true;
720
+ return;
721
+ }
722
+ if (step.kind === 'END_CLIP') {
723
+ insideClip = false;
724
+ return;
725
+ }
726
+ if (insideClip && step.kind === 'NAVIGATE') {
727
+ violations.push({
728
+ stepIndex: index,
729
+ message: `NAVIGATE at step ${index} is inside a clip (between BEGIN_CLIP and END_CLIP) and would cause a ` +
730
+ `white-flash cut in the recording. Move it before BEGIN_CLIP (off-camera warmup), or CLICK an ` +
731
+ `in-app <Link> the router intercepts instead.`,
732
+ });
733
+ }
734
+ });
735
+ return violations;
736
+ }
737
+ /**
738
+ * Hard-rejection variant for authoring boundaries (preset create/update).
739
+ * Throws with an actionable message if any NAVIGATE sits inside a clip.
740
+ */
741
+ export function assertNoNavigateInClip(program) {
742
+ const violations = findNavigateInClipViolations(program);
743
+ if (violations.length > 0) {
744
+ throw new Error(violations.map((v) => v.message).join(' '));
745
+ }
695
746
  }
696
747
  //# sourceMappingURL=execution-schema.js.map
@@ -487,6 +487,14 @@ export interface PreconditionSpec {
487
487
  domain: string;
488
488
  path?: string;
489
489
  }>;
490
+ /**
491
+ * Active AutoKap Scenario id (AUT-239). When set and `AUTOKAP_SCENARIO_SECRET`
492
+ * is configured, the runner injects a signed `__ak_scenario=<id>.<sig>` cookie
493
+ * before navigation, so the client app's cooperative scenario layer reads it
494
+ * server-side (SSR-safe) and serves the named state's fixtures. Inert if the
495
+ * secret is absent.
496
+ */
497
+ scenario?: string;
490
498
  }
491
499
  export declare const MEDIA_MODES: readonly ["screenshot", "clip", "video"];
492
500
  export type MediaMode = (typeof MEDIA_MODES)[number];
@@ -534,7 +542,26 @@ export interface ArtifactSpec {
534
542
  }
535
543
  export interface ExecutionProgram {
536
544
  presetId: string;
545
+ /**
546
+ * Content-revision counter, bumped ONLY by the healer after a selector repair
547
+ * (program-patcher.ts). Orthogonal to `programSchemaVersion` — it tracks
548
+ * content changes, never the form of the program.
549
+ */
537
550
  programVersion: number;
551
+ /**
552
+ * FORM version of the program, driving migrate-on-read (program-migrations.ts).
553
+ * Optional on the type so hand-built literals (fixtures) stay terse; guaranteed
554
+ * present at runtime by `upgradeProgram` (run inside parseProgram), which stamps
555
+ * it to the current value. Absent = v0 (oldest form). Read with
556
+ * `?? CURRENT_PROGRAM_SCHEMA_VERSION` when stamping.
557
+ */
558
+ programSchemaVersion?: number;
559
+ /**
560
+ * Provenance stamp: the engine semantics version the generator compiled this
561
+ * program against. Informational only — the runtime engine always applies the
562
+ * current semantics regardless. Absent = legacy (pre-versioning) program.
563
+ */
564
+ engineVersion?: number;
538
565
  mediaMode: MediaMode;
539
566
  baseUrl: string;
540
567
  /** Server-resolved concurrency cap for this run, derived from the owner's plan. */
@@ -673,6 +700,22 @@ export interface ArtifactResult {
673
700
  /** Favicon extracted from the captured page */
674
701
  tabIconData?: Buffer;
675
702
  tabIconMimeType?: string;
703
+ /**
704
+ * AUT-240 (Layer 4): the capture was produced under a degraded signal — an
705
+ * AKTree probe that kept throwing was assumed-OK as a last resort, or the page
706
+ * never reached a visually-stable state. "Assume OK, but flag it." Q4 decision:
707
+ * produce-only for now (a downstream consumer in gallery / post-capture
708
+ * verification is a later phase); no LLM is forced off this flag.
709
+ */
710
+ lowConfidence?: boolean;
711
+ /** Why the artifact was flagged low-confidence (human-readable). */
712
+ lowConfidenceReason?: string;
713
+ /**
714
+ * AUT-241 — navigation-watcher warnings captured while this clip/video was
715
+ * recording (e.g. a full document load mid-take = white flash + cursor loss).
716
+ * Carried up to `RunResult.warnings`; diagnostic only, never fails the run.
717
+ */
718
+ warnings?: string[];
676
719
  }
677
720
  export type LLMStepType = 'capture_verification' | 'alt_text_generation' | 'healer_invocation';
678
721
  export interface LLMStepUsage {
@@ -763,6 +806,13 @@ export interface RunResult {
763
806
  * with the version actually captured on the page.
764
807
  */
765
808
  detectedAppVersion?: string | null;
809
+ /**
810
+ * AUT-241 — non-fatal warnings aggregated from every clip/video recording in
811
+ * the run (full document loads mid-take, unexpected page-side navigations).
812
+ * Empty/undefined when nothing was flagged. Anti-cut policy surfaces these
813
+ * instead of masking the cut; deployed presets are grandfathered at run.
814
+ */
815
+ warnings?: string[];
766
816
  error?: string;
767
817
  }
768
818
  export interface WaitCondition {
@@ -770,6 +820,38 @@ export interface WaitCondition {
770
820
  state: 'visible' | 'attached';
771
821
  timeoutMs: number;
772
822
  }
823
+ /**
824
+ * Cheap, side-effect-free snapshot of page activity (AUT-240, Layer C).
825
+ * Compared across polls by the runner's progress watchdog to distinguish a
826
+ * slow-but-progressing page (extend the wait) from a genuinely stuck one (cut).
827
+ */
828
+ export interface ProgressSnapshot {
829
+ /**
830
+ * Monotonic count of FIRST-PARTY network lifecycle events
831
+ * (request/finished/failed) — same site as the live page origin. Third-party
832
+ * telemetry is excluded so it cannot masquerade as progress.
833
+ */
834
+ networkEventCount: number;
835
+ /** First-party requests issued but not yet finished or failed. */
836
+ inflightRequests: number;
837
+ /** `Date.now()` of the last observed first-party network lifecycle event. */
838
+ lastNetworkActivityAtMs: number;
839
+ /** `document.readyState`, or 'unknown' if unreadable. */
840
+ readyState: string;
841
+ /** Total element count — a cheap DOM-churn signal — or -1 if unreadable. */
842
+ domNodeCount: number;
843
+ /** True when the readability probe threw (itself a sign of navigation). */
844
+ navigating?: boolean;
845
+ }
846
+ /** Result of `RuntimeAdapter.waitForVisuallyStable` (AUT-240, Layer B). */
847
+ export interface VisualStabilityResult {
848
+ /** Whether the page reached a clean, stable state before the deadline. */
849
+ stable: boolean;
850
+ /** Human-readable explanation (which signal stayed noisy, if any). */
851
+ reason: string;
852
+ /** Total time spent stabilizing (ms). */
853
+ waitedMs: number;
854
+ }
773
855
  export interface ClickOptions {
774
856
  /** Force click even if element is covered */
775
857
  force?: boolean;
@@ -822,6 +904,12 @@ export interface RecordingResult {
822
904
  durationMs: number;
823
905
  mimeType: string;
824
906
  trimStartMs?: number;
907
+ /**
908
+ * AUT-241 — human-readable warnings collected by the navigation watcher
909
+ * during this recording window (e.g. a full document load = white flash +
910
+ * cursor loss). Surfaced up to `RunResult.warnings`; never fails the run.
911
+ */
912
+ warnings?: string[];
825
913
  }
826
914
  export interface TypeOptions {
827
915
  /**
@@ -887,6 +975,14 @@ export interface RuntimeAdapter {
887
975
  * adapters that cannot resolve a title should leave this method off.
888
976
  */
889
977
  getPageTitle?(): Promise<string | null>;
978
+ /**
979
+ * Text content of the first element matching `selector`, read live from the
980
+ * DOM via Playwright (`locator().first().textContent()`). Preferred over the
981
+ * AKTree for `text_contains` postconditions (AUT-240, Layer A). Returns null
982
+ * if the selector misses or the read fails. Optional — adapters without it
983
+ * make `text_contains` fall back to the AKTree.
984
+ */
985
+ getTextContent?(selector: string): Promise<string | null>;
890
986
  /**
891
987
  * Read the captured app's version from the live page (meta tag, window
892
988
  * global, or data attribute). Mirrors `extractAppVersionFromHtml` server-side
@@ -989,4 +1085,24 @@ export interface RuntimeAdapter {
989
1085
  clickHidden?(opts: {
990
1086
  selector: string;
991
1087
  }): Promise<void>;
1088
+ /**
1089
+ * Cheap snapshot of page activity used by the runner's progress watchdog to
1090
+ * decide whether a wait is "slow-but-progressing" (keep waiting) or "stuck"
1091
+ * (cut). Must never reject — implementations catch internally and set
1092
+ * `navigating: true` when the readability probe throws. Adapters that cannot
1093
+ * provide it leave it off; the runner then falls back to fixed budgets.
1094
+ */
1095
+ getProgressSnapshot?(): Promise<ProgressSnapshot>;
1096
+ /**
1097
+ * Wait for the page to be visually stable before a screenshot (Layer B):
1098
+ * fonts ready, images loaded, no semantic loaders ([aria-busy]/progressbar)
1099
+ * visible, DOM quiet, with a bounded pixel-convergence fallback. Best-effort —
1100
+ * never throws and never blocks the capture: when it cannot reach a clean
1101
+ * state it returns `stable: false` with a reason and the runner captures
1102
+ * anyway. Adapters that cannot provide it leave it off; the runner falls back
1103
+ * to the legacy `smartWaitForStability`.
1104
+ */
1105
+ waitForVisuallyStable?(options?: {
1106
+ maxWaitMs?: number;
1107
+ }): Promise<VisualStabilityResult>;
992
1108
  }
@@ -5,7 +5,7 @@
5
5
  * Executes opcodes sequentially, verifies postconditions,
6
6
  * delegates to recovery chain on failure, and respects circuit breaker.
7
7
  */
8
- import type { ExecutionProgram, ExecutionOpcode, RuntimeAdapter, OpcodeResultStatus, RunResult, HealerPatch, VariantSpec } from './execution-types.js';
8
+ import type { ExecutionProgram, ExecutionOpcode, RuntimeAdapter, OpcodeResultStatus, RunResult, HealerPatch, VariantSpec, ProgressSnapshot } from './execution-types.js';
9
9
  import type { LLMProviderConfig, LLMCallResult } from './llm-provider.js';
10
10
  export interface RecoveryChain {
11
11
  attempt(failedOpcode: ExecutionOpcode, opcodeIndex: number, adapter: RuntimeAdapter, options?: RecoveryAttemptOptions): Promise<RecoveryAttemptResult>;
@@ -19,6 +19,13 @@ export interface RecoveryAttemptResult {
19
19
  }
20
20
  export interface RecoveryAttemptOptions {
21
21
  remainingTimeMs?: number;
22
+ /**
23
+ * AUT-240 (Phase 5): absolute global wait deadline. Lets recovery re-checks
24
+ * extend-on-progress (like the main path) instead of replaying a fixed budget.
25
+ */
26
+ globalDeadlineMs?: number;
27
+ /** Progress probe for the recovery watchdog (omitted ⇒ fixed budgets). */
28
+ getProgress?: () => Promise<ProgressSnapshot | null>;
22
29
  maxDeterministicRetries?: number;
23
30
  currentVariant?: VariantSpec;
24
31
  allowPageReload?: boolean;