autokap 1.0.8 → 1.1.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.
Files changed (58) hide show
  1. package/assets/skill/OPCODE-REFERENCE.md +29 -1
  2. package/assets/skill/SKILL.md +2 -1
  3. package/dist/auth-capture.js +35 -2
  4. package/dist/billing-operation-logging.d.ts +4 -3
  5. package/dist/billing-operation-logging.js +3 -2
  6. package/dist/browser.d.ts +10 -10
  7. package/dist/browser.js +32 -28
  8. package/dist/capture-encryption.d.ts +3 -1
  9. package/dist/capture-encryption.js +21 -6
  10. package/dist/capture-strategy.js +3 -2
  11. package/dist/cli-config.d.ts +2 -1
  12. package/dist/cli-config.js +51 -2
  13. package/dist/cli-contract.d.ts +5 -1
  14. package/dist/cli-contract.js +7 -1
  15. package/dist/cli-runner-local.js +16 -3
  16. package/dist/cli-runner.js +165 -18
  17. package/dist/cli.js +25 -19
  18. package/dist/clip-begin-frame-recorder.d.ts +44 -0
  19. package/dist/clip-begin-frame-recorder.js +250 -0
  20. package/dist/clip-capture-backend.d.ts +25 -0
  21. package/dist/clip-capture-backend.js +189 -0
  22. package/dist/clip-capture-loop.d.ts +61 -0
  23. package/dist/clip-capture-loop.js +111 -0
  24. package/dist/clip-frame-recorder.d.ts +63 -0
  25. package/dist/clip-frame-recorder.js +305 -0
  26. package/dist/clip-postprocess.d.ts +31 -2
  27. package/dist/clip-postprocess.js +174 -57
  28. package/dist/clip-runtime.d.ts +18 -0
  29. package/dist/clip-runtime.js +67 -0
  30. package/dist/clip-scale.d.ts +10 -0
  31. package/dist/clip-scale.js +21 -0
  32. package/dist/clip-screencast-recorder.d.ts +42 -0
  33. package/dist/clip-screencast-recorder.js +242 -0
  34. package/dist/clip-sidecar.d.ts +54 -0
  35. package/dist/clip-sidecar.js +208 -0
  36. package/dist/cost-logging.d.ts +1 -1
  37. package/dist/env-validation.js +38 -4
  38. package/dist/execution-schema.d.ts +690 -360
  39. package/dist/execution-schema.js +98 -42
  40. package/dist/execution-types.d.ts +53 -3
  41. package/dist/execution-types.js +2 -1
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.js +1 -0
  44. package/dist/llm-healer.d.ts +2 -10
  45. package/dist/llm-healer.js +109 -62
  46. package/dist/llm-provider.js +3 -0
  47. package/dist/opcode-actions.js +13 -0
  48. package/dist/opcode-runner.js +21 -12
  49. package/dist/program-signing.d.ts +1094 -0
  50. package/dist/program-signing.js +140 -0
  51. package/dist/provider-config.d.ts +5 -0
  52. package/dist/provider-config.js +28 -1
  53. package/dist/recovery-chain.js +40 -16
  54. package/dist/server-credit-usage.d.ts +1 -1
  55. package/dist/types.d.ts +8 -2
  56. package/dist/web-playwright-local.d.ts +31 -1
  57. package/dist/web-playwright-local.js +207 -37
  58. package/package.json +12 -2
@@ -21,7 +21,7 @@ export const PostconditionSpecSchema = z.object({
21
21
  text: z.string().optional(),
22
22
  threshold: z.number().min(0).max(1).optional(),
23
23
  waitMs: z.number().int().positive().optional(),
24
- }).superRefine((value, ctx) => {
24
+ }).strict().superRefine((value, ctx) => {
25
25
  if (value.type === 'route_matches' && !value.pattern) {
26
26
  ctx.addIssue({
27
27
  code: z.ZodIssueCode.custom,
@@ -51,7 +51,7 @@ export const RecoveryPolicySchema = z.object({
51
51
  useAltInteraction: z.boolean(),
52
52
  allowReload: z.boolean(),
53
53
  allowHealer: z.boolean(),
54
- });
54
+ }).strict();
55
55
  // ── Opcode base fields ──────────────────────────────────────────────
56
56
  const opcodeBase = {
57
57
  description: z.string().min(1),
@@ -68,7 +68,7 @@ const opcodeBase = {
68
68
  const StrictUrlSchema = z.string().min(1).refine((value) => {
69
69
  try {
70
70
  const parsed = new URL(value);
71
- return Boolean(parsed.host);
71
+ return (parsed.protocol === 'http:' || parsed.protocol === 'https:') && Boolean(parsed.host);
72
72
  }
73
73
  catch {
74
74
  return false;
@@ -81,22 +81,22 @@ const NavigateOpcodeSchema = z.object({
81
81
  kind: z.literal('NAVIGATE'),
82
82
  ...opcodeBase,
83
83
  url: StrictUrlSchema,
84
- });
84
+ }).strict();
85
85
  const DismissOverlaysOpcodeSchema = z.object({
86
86
  kind: z.literal('DISMISS_OVERLAYS'),
87
87
  ...opcodeBase,
88
- });
88
+ }).strict();
89
89
  const AssertRouteOpcodeSchema = z.object({
90
90
  kind: z.literal('ASSERT_ROUTE'),
91
91
  ...opcodeBase,
92
92
  urlPattern: z.string().min(1),
93
- });
93
+ }).strict();
94
94
  const AssertSurfaceOpcodeSchema = z.object({
95
95
  kind: z.literal('ASSERT_SURFACE'),
96
96
  ...opcodeBase,
97
97
  selectors: z.array(z.string().min(1)).min(1),
98
98
  matchAll: z.boolean(),
99
- });
99
+ }).strict();
100
100
  const SemanticTargetSchema = z.object({
101
101
  text: z.string().optional(),
102
102
  role: z.string().optional(),
@@ -104,7 +104,7 @@ const SemanticTargetSchema = z.object({
104
104
  near: z.string().optional(),
105
105
  placeholder: z.string().optional(),
106
106
  exact: z.boolean().optional(),
107
- }).refine((value) => Boolean(value.text || value.role || value.label || value.near || value.placeholder), 'semantic targets need at least one identifying field');
107
+ }).strict().refine((value) => Boolean(value.text || value.role || value.label || value.near || value.placeholder), 'semantic targets need at least one identifying field');
108
108
  const ClickOpcodeSchema = z.object({
109
109
  kind: z.literal('CLICK'),
110
110
  ...opcodeBase,
@@ -113,7 +113,7 @@ const ClickOpcodeSchema = z.object({
113
113
  button: z.enum(['right', 'middle']).optional(),
114
114
  fingerprint: z.string().optional(),
115
115
  selectorAlternates: z.array(z.string().min(1)).optional(),
116
- });
116
+ }).strict();
117
117
  const TypeOpcodeSchema = z.object({
118
118
  kind: z.literal('TYPE'),
119
119
  ...opcodeBase,
@@ -124,19 +124,19 @@ const TypeOpcodeSchema = z.object({
124
124
  clearFirst: z.boolean(),
125
125
  fingerprint: z.string().optional(),
126
126
  selectorAlternates: z.array(z.string().min(1)).optional(),
127
- });
127
+ }).strict();
128
128
  const PressKeyOpcodeSchema = z.object({
129
129
  kind: z.literal('PRESS_KEY'),
130
130
  ...opcodeBase,
131
131
  key: z.string().min(1),
132
- });
132
+ }).strict();
133
133
  const WaitForOpcodeSchema = z.object({
134
134
  kind: z.literal('WAIT_FOR'),
135
135
  ...opcodeBase,
136
136
  selector: z.string().min(1).optional(),
137
137
  target: SemanticTargetSchema.optional(),
138
138
  state: z.enum(['visible', 'attached']),
139
- }).superRefine((value, ctx) => {
139
+ }).strict().superRefine((value, ctx) => {
140
140
  if (!value.selector && !value.target) {
141
141
  ctx.addIssue({
142
142
  code: z.ZodIssueCode.custom,
@@ -149,7 +149,7 @@ const storageHintSchema = z.object({
149
149
  storage: z.enum(['localStorage', 'sessionStorage', 'cookie']),
150
150
  key: z.string().min(1),
151
151
  value: z.string(),
152
- });
152
+ }).strict();
153
153
  const SetLocaleOpcodeSchema = z.object({
154
154
  kind: z.literal('SET_LOCALE'),
155
155
  ...opcodeBase,
@@ -157,7 +157,7 @@ const SetLocaleOpcodeSchema = z.object({
157
157
  method: z.enum(['browser_context', 'ui_interaction', 'storage']),
158
158
  selector: z.string().optional(),
159
159
  storageHints: z.array(storageHintSchema).optional(),
160
- }).superRefine((value, ctx) => {
160
+ }).strict().superRefine((value, ctx) => {
161
161
  if (value.method === 'ui_interaction' && !value.selector) {
162
162
  ctx.addIssue({
163
163
  code: z.ZodIssueCode.custom,
@@ -180,7 +180,7 @@ const SetThemeOpcodeSchema = z.object({
180
180
  method: z.enum(['color_scheme', 'ui_interaction', 'storage']),
181
181
  selector: z.string().optional(),
182
182
  storageHints: z.array(storageHintSchema).optional(),
183
- }).superRefine((value, ctx) => {
183
+ }).strict().superRefine((value, ctx) => {
184
184
  if (value.method === 'ui_interaction' && !value.selector) {
185
185
  ctx.addIssue({
186
186
  code: z.ZodIssueCode.custom,
@@ -203,20 +203,20 @@ const ScrollOpcodeSchema = z.object({
203
203
  amount: z.number().int().positive().optional(),
204
204
  targetSelector: z.string().optional(),
205
205
  target: SemanticTargetSchema.optional(),
206
- });
206
+ }).strict();
207
207
  const CaptureScreenshotOpcodeSchema = z.object({
208
208
  kind: z.literal('CAPTURE_SCREENSHOT'),
209
209
  ...opcodeBase,
210
210
  captureId: z.string().optional(),
211
211
  captureName: z.string().optional(),
212
212
  elementSelector: z.string().optional(),
213
- });
213
+ }).strict();
214
214
  const CaptureDomOpcodeSchema = z.object({
215
215
  kind: z.literal('CAPTURE_DOM'),
216
216
  ...opcodeBase,
217
217
  stateName: z.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, 'stateName must be kebab-case'),
218
218
  selector: z.string().min(1).optional(),
219
- });
219
+ }).strict();
220
220
  const CaptureFragmentOpcodeSchema = z.object({
221
221
  kind: z.literal('CAPTURE_FRAGMENT'),
222
222
  ...opcodeBase,
@@ -231,19 +231,19 @@ const CaptureFragmentOpcodeSchema = z.object({
231
231
  triggerSelector: z.string().min(1).optional(),
232
232
  mountStrategy: z.string().min(1).optional(),
233
233
  mountTargetSelector: z.string().min(1).optional(),
234
- });
234
+ }).strict();
235
235
  const BeginClipOpcodeSchema = z.object({
236
236
  kind: z.literal('BEGIN_CLIP'),
237
237
  ...opcodeBase,
238
238
  clipId: z.string().optional(),
239
239
  clipName: z.string().optional(),
240
- });
240
+ }).strict();
241
241
  const EndClipOpcodeSchema = z.object({
242
242
  kind: z.literal('END_CLIP'),
243
243
  ...opcodeBase,
244
244
  clipId: z.string().optional(),
245
245
  clipName: z.string().optional(),
246
- });
246
+ }).strict();
247
247
  const HoverOpcodeSchema = z.object({
248
248
  kind: z.literal('HOVER'),
249
249
  ...opcodeBase,
@@ -251,7 +251,7 @@ const HoverOpcodeSchema = z.object({
251
251
  target: SemanticTargetSchema.optional(),
252
252
  fingerprint: z.string().optional(),
253
253
  selectorAlternates: z.array(z.string().min(1)).optional(),
254
- });
254
+ }).strict();
255
255
  const SelectOptionOpcodeSchema = z.object({
256
256
  kind: z.literal('SELECT_OPTION'),
257
257
  ...opcodeBase,
@@ -262,7 +262,7 @@ const SelectOptionOpcodeSchema = z.object({
262
262
  optionIndex: z.number().int().min(0).optional(),
263
263
  fingerprint: z.string().optional(),
264
264
  selectorAlternates: z.array(z.string().min(1)).optional(),
265
- }).superRefine((value, ctx) => {
265
+ }).strict().superRefine((value, ctx) => {
266
266
  if (value.optionLabel === undefined && value.optionValue === undefined && value.optionIndex === undefined) {
267
267
  ctx.addIssue({
268
268
  code: z.ZodIssueCode.custom,
@@ -278,7 +278,7 @@ const CheckOpcodeSchema = z.object({
278
278
  checked: z.boolean(),
279
279
  fingerprint: z.string().optional(),
280
280
  selectorAlternates: z.array(z.string().min(1)).optional(),
281
- });
281
+ }).strict();
282
282
  const DoubleClickOpcodeSchema = z.object({
283
283
  kind: z.literal('DOUBLE_CLICK'),
284
284
  ...opcodeBase,
@@ -286,6 +286,38 @@ const DoubleClickOpcodeSchema = z.object({
286
286
  target: SemanticTargetSchema.optional(),
287
287
  fingerprint: z.string().optional(),
288
288
  selectorAlternates: z.array(z.string().min(1)).optional(),
289
+ }).strict();
290
+ const DragOpcodeSchema = z.object({
291
+ kind: z.literal('DRAG'),
292
+ ...opcodeBase,
293
+ selector: z.string().min(1),
294
+ target: SemanticTargetSchema.optional(),
295
+ fingerprint: z.string().optional(),
296
+ selectorAlternates: z.array(z.string().min(1)).optional(),
297
+ toSelector: z.string().min(1).optional(),
298
+ toTarget: SemanticTargetSchema.optional(),
299
+ toSelectorAlternates: z.array(z.string().min(1)).optional(),
300
+ offset: z.object({
301
+ dx: z.number(),
302
+ dy: z.number(),
303
+ }).strict().optional(),
304
+ }).strict().superRefine((value, ctx) => {
305
+ const hasElementDest = Boolean(value.toSelector || value.toTarget);
306
+ const hasOffsetDest = Boolean(value.offset);
307
+ if (!hasElementDest && !hasOffsetDest) {
308
+ ctx.addIssue({
309
+ code: z.ZodIssueCode.custom,
310
+ message: 'DRAG requires a destination: provide `toSelector` / `toTarget` (element drag) or `offset` (relative drag)',
311
+ path: ['toSelector'],
312
+ });
313
+ }
314
+ if (hasElementDest && hasOffsetDest) {
315
+ ctx.addIssue({
316
+ code: z.ZodIssueCode.custom,
317
+ message: 'DRAG destination must be either an element (`toSelector` / `toTarget`) or an `offset`, not both',
318
+ path: ['offset'],
319
+ });
320
+ }
289
321
  });
290
322
  // ── Mock data opcodes (soft / non-blocking) ─────────────────────────
291
323
  const CloneElementOpcodeSchema = z.object({
@@ -295,7 +327,7 @@ const CloneElementOpcodeSchema = z.object({
295
327
  containerSelector: z.string().min(1),
296
328
  count: z.number().int().min(1).max(500),
297
329
  removeSource: z.boolean().optional(),
298
- });
330
+ }).strict();
299
331
  const InjectMockDataOpcodeSchema = z.object({
300
332
  kind: z.literal('INJECT_MOCK_DATA'),
301
333
  ...opcodeBase,
@@ -309,11 +341,11 @@ const InjectMockDataOpcodeSchema = z.object({
309
341
  slot: z.string().min(1),
310
342
  selector: z.string().min(1),
311
343
  attribute: z.string().min(1).optional(),
312
- })).min(1).optional(),
344
+ }).strict()).min(1).optional(),
313
345
  // Trigger fields
314
346
  inputSelector: z.string().min(1).optional(),
315
347
  triggerSelector: z.string().min(1).optional(),
316
- }).superRefine((value, ctx) => {
348
+ }).strict().superRefine((value, ctx) => {
317
349
  // The two delivery mechanisms are independent and additive. An opcode must
318
350
  // declare AT LEAST ONE complete mechanism — typically both — but if only
319
351
  // one is provided that's still valid.
@@ -363,14 +395,14 @@ const RemoveElementOpcodeSchema = z.object({
363
395
  kind: z.literal('REMOVE_ELEMENT'),
364
396
  ...opcodeBase,
365
397
  selector: z.string().min(1),
366
- });
398
+ }).strict();
367
399
  const SetAttributeOpcodeSchema = z.object({
368
400
  kind: z.literal('SET_ATTRIBUTE'),
369
401
  ...opcodeBase,
370
402
  selector: z.string().min(1),
371
403
  attribute: z.string().min(1),
372
404
  value: z.string(),
373
- });
405
+ }).strict();
374
406
  // ── Discriminated union ─────────────────────────────────────────────
375
407
  export const ExecutionOpcodeSchema = z.discriminatedUnion('kind', [
376
408
  NavigateOpcodeSchema,
@@ -393,6 +425,7 @@ export const ExecutionOpcodeSchema = z.discriminatedUnion('kind', [
393
425
  SelectOptionOpcodeSchema,
394
426
  CheckOpcodeSchema,
395
427
  DoubleClickOpcodeSchema,
428
+ DragOpcodeSchema,
396
429
  CloneElementOpcodeSchema,
397
430
  InjectMockDataOpcodeSchema,
398
431
  RemoveElementOpcodeSchema,
@@ -408,7 +441,7 @@ export const MockDataSlotSchema = z.object({
408
441
  // descriptive label is safe here. See `inputTypeForHint()` in
409
442
  // web/components/mock-data-editor.tsx.
410
443
  hint: z.string().optional(),
411
- });
444
+ }).strict();
412
445
  export const MockDataRowSchema = z.record(z.string(), z.string());
413
446
  export const MockDataGroupSchema = z.object({
414
447
  name: z.string().min(1),
@@ -416,27 +449,48 @@ export const MockDataGroupSchema = z.object({
416
449
  slots: z.array(MockDataSlotSchema).min(1),
417
450
  defaultValues: z.array(MockDataRowSchema),
418
451
  replaceExisting: z.boolean().optional(),
419
- });
452
+ }).strict();
420
453
  // ── Variant & precondition ──────────────────────────────────────────
421
454
  export const VariantSpecSchema = z.object({
422
455
  id: z.string().min(1),
423
456
  viewport: z.object({
424
457
  width: z.number().int().positive(),
425
458
  height: z.number().int().positive(),
426
- }),
459
+ }).strict(),
427
460
  deviceScaleFactor: z.number().positive().optional(),
428
461
  locale: z.string().optional(),
429
462
  theme: z.enum(['light', 'dark']).optional(),
430
463
  targetId: z.string().optional(),
431
464
  targetLabel: z.string().optional(),
432
465
  deviceFrame: z.string().optional(),
433
- });
466
+ }).strict();
434
467
  const cookieSchema = z.object({
435
468
  name: z.string().min(1),
436
469
  value: z.string(),
437
470
  domain: z.string().min(1),
438
471
  path: z.string().optional(),
439
- });
472
+ }).strict();
473
+ const StorageStateCookieSchema = z.object({
474
+ name: z.string().min(1),
475
+ value: z.string(),
476
+ domain: z.string().min(1),
477
+ path: z.string().min(1),
478
+ expires: z.number(),
479
+ httpOnly: z.boolean(),
480
+ secure: z.boolean(),
481
+ sameSite: z.enum(['Strict', 'Lax', 'None']),
482
+ }).strict();
483
+ const StorageStateOriginSchema = z.object({
484
+ origin: StrictUrlSchema,
485
+ localStorage: z.array(z.object({
486
+ name: z.string().min(1),
487
+ value: z.string(),
488
+ }).strict()),
489
+ }).strict();
490
+ const StorageStateSchema = z.object({
491
+ cookies: z.array(StorageStateCookieSchema),
492
+ origins: z.array(StorageStateOriginSchema),
493
+ }).strict().refine((value) => value.cookies.length > 0 || value.origins.length > 0, 'storageState must contain at least one cookie or origin');
440
494
  // `auth` enum removed: bootstrap is auto-detected from cookies / storageState /
441
495
  // sessionStorage (injected into the browser context) and/or credentials
442
496
  // (substituted into {{email}}/{{password}}/{{loginUrl}} placeholders inside
@@ -448,26 +502,26 @@ export const PreconditionSpecSchema = z.object({
448
502
  email: z.string().optional(),
449
503
  password: z.string().optional(),
450
504
  loginUrl: z.string().optional(),
451
- }).optional(),
452
- storageState: z.any().optional(),
505
+ }).strict().optional(),
506
+ storageState: StorageStateSchema.optional(),
453
507
  sessionStorage: z.record(z.string(), z.record(z.string(), z.string())).optional(),
454
508
  cookies: z.array(cookieSchema).optional(),
455
- });
509
+ }).strict();
456
510
  // ── Artifact spec ───────────────────────────────────────────────────
457
511
  export const ArtifactSpecSchema = z.object({
458
512
  mediaMode: z.enum(['screenshot', 'clip', 'dom']),
459
513
  format: z.object({
460
514
  clipFormat: z.enum(['gif', 'mp4', 'both']).optional(),
461
515
  screenshotFormat: z.enum(['png', 'jpeg']).optional(),
462
- }).optional(),
516
+ }).strict().optional(),
463
517
  cursorTheme: z.enum(['minimal', 'macos', 'windows']).optional(),
464
518
  maxClipDurationSec: z.number().positive().optional(),
465
519
  applyMockup: z.boolean().optional(),
466
520
  applyStatusBar: z.boolean().optional(),
467
521
  domOptions: z.object({
468
522
  sanitize: z.boolean().optional(),
469
- }).optional(),
470
- });
523
+ }).strict().optional(),
524
+ }).strict();
471
525
  // ── Full program ────────────────────────────────────────────────────
472
526
  export const ExecutionProgramSchema = z.object({
473
527
  presetId: z.string().min(1),
@@ -485,15 +539,17 @@ export const ExecutionProgramSchema = z.object({
485
539
  compiledAt: z.string().datetime(),
486
540
  compiledWith: z.string().optional(),
487
541
  mockDataGroups: z.array(MockDataGroupSchema).optional(),
488
- });
542
+ }).strict();
489
543
  // ── Healer patch ────────────────────────────────────────────────────
490
544
  export const HealerPatchSchema = z.object({
491
545
  opcodeIndex: z.number().int().min(0),
492
546
  originalOpcode: ExecutionOpcodeSchema,
493
547
  replacementOpcodes: z.array(ExecutionOpcodeSchema).min(1).max(3),
548
+ patchType: z.literal('selector_patch').optional(),
549
+ interactionMode: z.enum(['default', 'keyboard', 'js_dispatch', 'coordinates']).optional(),
494
550
  reason: z.string().min(1),
495
551
  patchedAt: z.string().datetime(),
496
- });
552
+ }).strict();
497
553
  // ── Typed parse helpers ─────────────────────────────────────────────
498
554
  export function parseProgram(data) {
499
555
  return ExecutionProgramSchema.parse(data);
@@ -7,7 +7,7 @@
7
7
  import type { AKTree, BrowserStorageState, BrowserSessionStorageState, VideoCursorTheme, VideoPageSignals } from './types.js';
8
8
  /** Sentinel value that resolves to the current variant's locale or theme at runtime */
9
9
  export declare const VARIANT_PLACEHOLDER: "$variant";
10
- export declare const OPCODE_KINDS: readonly ["NAVIGATE", "DISMISS_OVERLAYS", "ASSERT_ROUTE", "ASSERT_SURFACE", "CLICK", "TYPE", "PRESS_KEY", "WAIT_FOR", "SET_LOCALE", "SET_THEME", "SCROLL", "CAPTURE_SCREENSHOT", "CAPTURE_DOM", "CAPTURE_FRAGMENT", "BEGIN_CLIP", "END_CLIP", "HOVER", "SELECT_OPTION", "CHECK", "DOUBLE_CLICK", "CLONE_ELEMENT", "INJECT_MOCK_DATA", "REMOVE_ELEMENT", "SET_ATTRIBUTE"];
10
+ export declare const OPCODE_KINDS: readonly ["NAVIGATE", "DISMISS_OVERLAYS", "ASSERT_ROUTE", "ASSERT_SURFACE", "CLICK", "TYPE", "PRESS_KEY", "WAIT_FOR", "SET_LOCALE", "SET_THEME", "SCROLL", "CAPTURE_SCREENSHOT", "CAPTURE_DOM", "CAPTURE_FRAGMENT", "BEGIN_CLIP", "END_CLIP", "HOVER", "SELECT_OPTION", "CHECK", "DOUBLE_CLICK", "DRAG", "CLONE_ELEMENT", "INJECT_MOCK_DATA", "REMOVE_ELEMENT", "SET_ATTRIBUTE"];
11
11
  export type OpcodeKind = (typeof OPCODE_KINDS)[number];
12
12
  /**
13
13
  * Soft opcodes are non-blocking — if their action or postcondition fails at
@@ -94,7 +94,7 @@ export interface RecoveryPolicy {
94
94
  useAltInteraction: boolean;
95
95
  /** Reload the page and retry from last checkpoint. Default: false */
96
96
  allowReload: boolean;
97
- /** Allow LLM healer as last resort. Default: true */
97
+ /** Allow LLM healer as last resort. Default: false */
98
98
  allowHealer: boolean;
99
99
  }
100
100
  export declare const DEFAULT_RECOVERY_POLICY: RecoveryPolicy;
@@ -356,6 +356,35 @@ export interface DoubleClickOpcode extends OpcodeBase {
356
356
  fingerprint?: string;
357
357
  selectorAlternates?: string[];
358
358
  }
359
+ /**
360
+ * Drag an element from point A to point B with an animated cursor.
361
+ * During clip recordings, the cursor overlay glides from source to
362
+ * destination along a Bezier curve with press/release visual feedback.
363
+ *
364
+ * Destination is either another element (`toSelector` / `toTarget`) or a
365
+ * relative `offset` from the source center — use the offset form for
366
+ * sliders, canvas drawing, or any drag whose end point isn't a DOM node.
367
+ */
368
+ export interface DragOpcode extends OpcodeBase {
369
+ kind: 'DRAG';
370
+ /** CSS selector of the source element (the one being dragged) */
371
+ selector: string;
372
+ /** Semantic fallback for the source element */
373
+ target?: SemanticTarget;
374
+ fingerprint?: string;
375
+ selectorAlternates?: string[];
376
+ /** CSS selector of the drop target element (mutually exclusive with `offset`) */
377
+ toSelector?: string;
378
+ /** Semantic fallback for the drop target */
379
+ toTarget?: SemanticTarget;
380
+ /** Alternative destination selectors tried in order */
381
+ toSelectorAlternates?: string[];
382
+ /** Absolute pixel offset from the source center. Use when the drop point isn't a DOM element (sliders, canvas). */
383
+ offset?: {
384
+ dx: number;
385
+ dy: number;
386
+ };
387
+ }
359
388
  /**
360
389
  * Duplicate a template element N times into a container. **Soft / non-blocking.**
361
390
  * If the source or container selector is missing, the opcode is skipped and
@@ -452,7 +481,7 @@ export interface SetAttributeOpcode extends OpcodeBase {
452
481
  /** Attribute value */
453
482
  value: string;
454
483
  }
455
- export type ExecutionOpcode = NavigateOpcode | DismissOverlaysOpcode | AssertRouteOpcode | AssertSurfaceOpcode | ClickOpcode | TypeOpcode | PressKeyOpcode | WaitForOpcode | SetLocaleOpcode | SetThemeOpcode | ScrollOpcode | CaptureScreenshotOpcode | CaptureDomOpcode | CaptureFragmentOpcode | BeginClipOpcode | EndClipOpcode | HoverOpcode | SelectOptionOpcode | CheckOpcode | DoubleClickOpcode | CloneElementOpcode | InjectMockDataOpcode | RemoveElementOpcode | SetAttributeOpcode;
484
+ export type ExecutionOpcode = NavigateOpcode | DismissOverlaysOpcode | AssertRouteOpcode | AssertSurfaceOpcode | ClickOpcode | TypeOpcode | PressKeyOpcode | WaitForOpcode | SetLocaleOpcode | SetThemeOpcode | ScrollOpcode | CaptureScreenshotOpcode | CaptureDomOpcode | CaptureFragmentOpcode | BeginClipOpcode | EndClipOpcode | HoverOpcode | SelectOptionOpcode | CheckOpcode | DoubleClickOpcode | DragOpcode | CloneElementOpcode | InjectMockDataOpcode | RemoveElementOpcode | SetAttributeOpcode;
456
485
  export interface VariantSpec {
457
486
  id: string;
458
487
  viewport: {
@@ -580,6 +609,10 @@ export interface HealerPatch {
580
609
  originalOpcode: ExecutionOpcode;
581
610
  /** The replacement opcode(s). Usually 1, max 3. */
582
611
  replacementOpcodes: ExecutionOpcode[];
612
+ /** Bounded patch type used by the healer. */
613
+ patchType?: 'selector_patch';
614
+ /** Optional interaction mode requested by the healer. */
615
+ interactionMode?: 'default' | 'keyboard' | 'js_dispatch' | 'coordinates';
583
616
  /** Why the healer made this change */
584
617
  reason: string;
585
618
  /** Timestamp */
@@ -837,6 +870,23 @@ export interface RuntimeAdapter {
837
870
  }): Promise<void>;
838
871
  check?(selector: string, checked: boolean): Promise<void>;
839
872
  doubleClick?(selector: string): Promise<void>;
873
+ /**
874
+ * Drag the source element from point A to point B with an animated cursor
875
+ * when a clip is recording. Destination is either another element
876
+ * (`toSelector` / `toTarget`) or an `offset` from the source center.
877
+ */
878
+ drag?(opts: {
879
+ selector?: string;
880
+ target?: SemanticTarget;
881
+ selectorAlternates?: string[];
882
+ toSelector?: string;
883
+ toTarget?: SemanticTarget;
884
+ toSelectorAlternates?: string[];
885
+ offset?: {
886
+ dx: number;
887
+ dy: number;
888
+ };
889
+ }): Promise<void>;
840
890
  /** Clone an element N times into a container. Throws if either selector misses. */
841
891
  cloneElement?(opts: {
842
892
  sourceSelector: string;
@@ -28,6 +28,7 @@ export const OPCODE_KINDS = [
28
28
  'SELECT_OPTION',
29
29
  'CHECK',
30
30
  'DOUBLE_CLICK',
31
+ 'DRAG',
31
32
  'CLONE_ELEMENT',
32
33
  'INJECT_MOCK_DATA',
33
34
  'REMOVE_ELEMENT',
@@ -53,7 +54,7 @@ export const DEFAULT_RECOVERY_POLICY = {
53
54
  useSelectorMemory: true,
54
55
  useAltInteraction: true,
55
56
  allowReload: false,
56
- allowHealer: true,
57
+ allowHealer: false,
57
58
  };
58
59
  // ── Artifact spec ───────────────────────────────────────────────────
59
60
  export const MEDIA_MODES = ['screenshot', 'clip', 'dom'];
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { DEFAULT_RECOVERY_POLICY, DEFAULT_CIRCUIT_BREAKER, VARIANT_PLACEHOLDER,
3
3
  export { ExecutionProgramSchema, ExecutionOpcodeSchema, parseProgram, parseOpcode, safeParseProgramResult, MockDataSlotSchema, MockDataRowSchema, MockDataGroupSchema, } from './execution-schema.js';
4
4
  export { callLLM } from './llm-provider.js';
5
5
  export type { LLMProviderConfig, LLMCallResult } from './llm-provider.js';
6
+ export { normalizeAllowedOrigins, normalizeHttpOrigin, signExecutionProgramEnvelope, verifySignedExecutionProgramEnvelope, } from './program-signing.js';
7
+ export type { ProgramSecurityMetadata, SignedExecutionProgramEnvelope, } from './program-signing.js';
6
8
  export { generateAltText } from './alt-text.js';
7
9
  export type { AltTextResult, AltTextContext } from './alt-text.js';
8
10
  export { logger, setOnLog, setOnScreenshot, emitScreenshot } from './logger.js';
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export { DEFAULT_RECOVERY_POLICY, DEFAULT_CIRCUIT_BREAKER, VARIANT_PLACEHOLDER,
6
6
  export { ExecutionProgramSchema, ExecutionOpcodeSchema, parseProgram, parseOpcode, safeParseProgramResult, MockDataSlotSchema, MockDataRowSchema, MockDataGroupSchema, } from './execution-schema.js';
7
7
  // ── LLM-backed helpers (OpenAI, no browser) ─────────────────────────
8
8
  export { callLLM } from './llm-provider.js';
9
+ export { normalizeAllowedOrigins, normalizeHttpOrigin, signExecutionProgramEnvelope, verifySignedExecutionProgramEnvelope, } from './program-signing.js';
9
10
  export { generateAltText } from './alt-text.js';
10
11
  // ── Shared utilities ────────────────────────────────────────────────
11
12
  export { logger, setOnLog, setOnScreenshot, emitScreenshot } from './logger.js';
@@ -3,15 +3,7 @@
3
3
  *
4
4
  * Last-resort recovery: when all deterministic strategies fail,
5
5
  * the healer asks an LLM to analyze the current page state and
6
- * produce a replacement opcode.
7
- *
8
- * Constraints:
9
- * - Cannot change the preset's intention
10
- * - Cannot change variant order
11
- * - Cannot invent new capture targets
12
- * - Cannot skip the failed opcode's postcondition
13
- * - Max 1 LLM call per healing attempt
14
- * - Max 3 healing attempts per run
6
+ * produce a bounded selector patch.
15
7
  */
16
8
  import type { ExecutionOpcode, HealerPatch } from './execution-types.js';
17
9
  import type { LLMCallResult } from './llm-provider.js';
@@ -49,7 +41,7 @@ export interface HealerLLMProvider {
49
41
  }>;
50
42
  }
51
43
  /**
52
- * The LLM Healer — a constrained agent that repairs failed opcodes.
44
+ * The LLM Healer — a constrained agent that repairs failed selectors.
53
45
  */
54
46
  export declare class LLMHealer {
55
47
  private llmProvider;