@ap3x/browser-agent 0.1.0 → 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.
@@ -9,6 +9,70 @@ function isDoneResult(result) {
9
9
  return result.isDone === true;
10
10
  }
11
11
 
12
+ // src/agent/postconditions.ts
13
+ import { Type } from "@sinclair/typebox";
14
+ var BrowserPostconditionSchema = Type.Union([
15
+ Type.Object(
16
+ { kind: Type.Literal("url"), equals: Type.String() },
17
+ { additionalProperties: false }
18
+ ),
19
+ Type.Object(
20
+ {
21
+ kind: Type.Literal("page_identity"),
22
+ title: Type.Optional(Type.String()),
23
+ textIncludes: Type.Optional(Type.String())
24
+ },
25
+ { additionalProperties: false }
26
+ ),
27
+ Type.Object(
28
+ { kind: Type.Literal("visible"), selector: Type.String() },
29
+ { additionalProperties: false }
30
+ ),
31
+ Type.Object(
32
+ { kind: Type.Literal("selected_value"), selector: Type.String(), value: Type.String() },
33
+ { additionalProperties: false }
34
+ )
35
+ ]);
36
+ var PostconditionError = class extends Error {
37
+ constructor(condition) {
38
+ super(`Browser postcondition failed: ${condition.kind}`);
39
+ this.condition = condition;
40
+ this.name = "PostconditionError";
41
+ }
42
+ condition;
43
+ reasonCode = "postcondition_failed";
44
+ };
45
+ async function verifyPostconditions(conditions, driver, session, signal) {
46
+ if (!conditions.length) return;
47
+ const state = await driver.getState({ signal });
48
+ for (const condition of conditions) {
49
+ if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
50
+ let passed = false;
51
+ switch (condition.kind) {
52
+ case "url":
53
+ passed = state.url === condition.equals;
54
+ break;
55
+ case "page_identity":
56
+ passed = (condition.title !== void 0 || condition.textIncludes !== void 0) && (condition.title === void 0 || state.title === condition.title) && (condition.textIncludes === void 0 || state.serializedText.includes(condition.textIncludes));
57
+ break;
58
+ case "visible":
59
+ passed = await session.evaluate(
60
+ `(()=>{const elements=document.querySelectorAll(${JSON.stringify(condition.selector)});if(elements.length!==1)return false;const e=elements[0];const s=getComputedStyle(e);const r=e.getBoundingClientRect();return s.display!=='none'&&s.visibility!=='hidden'&&r.width>0&&r.height>0;})()`,
61
+ { signal }
62
+ );
63
+ break;
64
+ case "selected_value":
65
+ passed = await session.evaluate(
66
+ `(()=>{const elements=document.querySelectorAll(${JSON.stringify(condition.selector)});return elements.length===1&&elements[0].value===${JSON.stringify(condition.value)};})()`,
67
+ { signal }
68
+ );
69
+ break;
70
+ }
71
+ if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
72
+ if (!passed) throw new PostconditionError(condition);
73
+ }
74
+ }
75
+
12
76
  // src/tools/sensitive.ts
13
77
  import { createHmac } from "crypto";
14
78
  import { matchUrlWithDomainPattern } from "@ap3x/browser";
@@ -124,8 +188,9 @@ function totp(seed, options = {}) {
124
188
  }
125
189
 
126
190
  // src/tools/registry.ts
191
+ import { authorizeTool, currentRunContext, immutableArguments, uuidv7 } from "@ap3x/agent-core";
127
192
  import { matchUrlWithDomainPattern as matchUrlWithDomainPattern2 } from "@ap3x/browser";
128
- import { Type } from "@sinclair/typebox";
193
+ import { Type as Type2 } from "@sinclair/typebox";
129
194
  import { Value } from "@sinclair/typebox/value";
130
195
  function defineAction(def) {
131
196
  return def;
@@ -150,6 +215,8 @@ var Registry = class _Registry {
150
215
  throw new Error(`Action "${def.name}" is already registered`);
151
216
  }
152
217
  this.actions.set(def.name, {
218
+ replaySafe: def.replaySafe ?? false,
219
+ postconditions: def.postconditions,
153
220
  name: def.name,
154
221
  description: def.description,
155
222
  schema: def.schema,
@@ -192,9 +259,9 @@ var Registry = class _Registry {
192
259
  const cached = this.unionCache.get(key);
193
260
  if (cached) return cached;
194
261
  const members = admitted.map(
195
- (a) => Type.Object({ [a.name]: a.schema }, { additionalProperties: false, title: a.name })
262
+ (a) => Type2.Object({ [a.name]: a.schema }, { additionalProperties: false, title: a.name })
196
263
  );
197
- const union = members.length === 0 ? Type.Never() : members.length === 1 ? members[0] : Type.Union(members);
264
+ const union = members.length === 0 ? Type2.Never() : members.length === 1 ? members[0] : Type2.Union(members);
198
265
  this.unionCache.set(key, union);
199
266
  return union;
200
267
  }
@@ -242,7 +309,14 @@ var Registry = class _Registry {
242
309
  });
243
310
  }
244
311
  try {
245
- const result = await action.execute(params, ctx);
312
+ const context = currentRunContext();
313
+ const finalParams = immutableArguments(params);
314
+ await authorizeTool(context, name, uuidv7(), finalParams, ctx.signal);
315
+ context?.controller?.assertActive(ctx.signal);
316
+ context?.controller?.tool();
317
+ const result = await action.execute(finalParams, ctx);
318
+ if (result.error === void 0 && action.postconditions)
319
+ await verifyPostconditions(action.postconditions, ctx.driver, ctx.session, ctx.signal);
246
320
  return maskResultSecrets(result, resolved, allSensitiveData);
247
321
  } catch (error) {
248
322
  const message = error instanceof Error ? error.message : String(error);
@@ -363,7 +437,7 @@ async function readExternalFile(path) {
363
437
  // src/tools/actions.ts
364
438
  import { basename, join } from "path";
365
439
  import { InteractionError, PAGE_SCROLL_PX, asElementIndex, isPathContained } from "@ap3x/browser";
366
- import { Type as Type2 } from "@sinclair/typebox";
440
+ import { Type as Type3 } from "@sinclair/typebox";
367
441
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
368
442
  function nodeAt(ctx, index) {
369
443
  const node = ctx.session.getElementByIndex(index);
@@ -380,7 +454,7 @@ function nodeAt(ctx, index) {
380
454
  var navigate = defineAction({
381
455
  name: "navigate",
382
456
  description: "Navigate the current tab to a URL (opens a new tab when newTab is true).",
383
- schema: Type2.Object({ url: Type2.String(), newTab: Type2.Optional(Type2.Boolean()) }),
457
+ schema: Type3.Object({ url: Type3.String(), newTab: Type3.Optional(Type3.Boolean()) }),
384
458
  terminatesSequence: true,
385
459
  async execute(params, ctx) {
386
460
  if (params.newTab) {
@@ -394,7 +468,7 @@ var navigate = defineAction({
394
468
  var goBack = defineAction({
395
469
  name: "go_back",
396
470
  description: "Go back one entry in the browser history.",
397
- schema: Type2.Object({}),
471
+ schema: Type3.Object({}),
398
472
  terminatesSequence: true,
399
473
  async execute(_params, ctx) {
400
474
  await ctx.session.goBack();
@@ -404,7 +478,7 @@ var goBack = defineAction({
404
478
  var click = defineAction({
405
479
  name: "click",
406
480
  description: "Click an interactive element by its [index].",
407
- schema: Type2.Object({ index: Type2.Integer({ minimum: 1 }) }),
481
+ schema: Type3.Object({ index: Type3.Integer({ minimum: 1 }) }),
408
482
  async execute(params, ctx) {
409
483
  const resolved = nodeAt(ctx, params.index);
410
484
  if (!resolved.ok) return resolved.result;
@@ -415,10 +489,10 @@ var click = defineAction({
415
489
  var inputText = defineAction({
416
490
  name: "input_text",
417
491
  description: "Type text into an element by its [index]. clear=false appends; text='' clears.",
418
- schema: Type2.Object({
419
- index: Type2.Integer({ minimum: 0 }),
420
- text: Type2.String(),
421
- clear: Type2.Optional(Type2.Boolean())
492
+ schema: Type3.Object({
493
+ index: Type3.Integer({ minimum: 0 }),
494
+ text: Type3.String(),
495
+ clear: Type3.Optional(Type3.Boolean())
422
496
  }),
423
497
  async execute(params, ctx) {
424
498
  const resolved = nodeAt(ctx, params.index);
@@ -436,10 +510,10 @@ var inputText = defineAction({
436
510
  var scroll = defineAction({
437
511
  name: "scroll",
438
512
  description: "Scroll the page (or an element by [index]) up/down by a number of pages.",
439
- schema: Type2.Object({
440
- down: Type2.Optional(Type2.Boolean()),
441
- pages: Type2.Optional(Type2.Number()),
442
- index: Type2.Optional(Type2.Integer({ minimum: 1 }))
513
+ schema: Type3.Object({
514
+ down: Type3.Optional(Type3.Boolean()),
515
+ pages: Type3.Optional(Type3.Number()),
516
+ index: Type3.Optional(Type3.Integer({ minimum: 1 }))
443
517
  }),
444
518
  async execute(params, ctx) {
445
519
  const pages = params.pages ?? 1;
@@ -459,7 +533,7 @@ var scroll = defineAction({
459
533
  var scrollToText = defineAction({
460
534
  name: "scroll_to_text",
461
535
  description: "Scroll to the first occurrence of the given text.",
462
- schema: Type2.Object({ text: Type2.String() }),
536
+ schema: Type3.Object({ text: Type3.String() }),
463
537
  async execute(params, ctx) {
464
538
  try {
465
539
  await ctx.session.input.scrollToText(params.text, { signal: ctx.signal });
@@ -475,7 +549,7 @@ var scrollToText = defineAction({
475
549
  var sendKeys = defineAction({
476
550
  name: "send_keys",
477
551
  description: "Dispatch raw keyboard keys or a shortcut (e.g. 'Enter', 'Control+a').",
478
- schema: Type2.Object({ keys: Type2.String() }),
552
+ schema: Type3.Object({ keys: Type3.String() }),
479
553
  async execute(params, ctx) {
480
554
  await ctx.session.input.sendKeys(params.keys, { signal: ctx.signal });
481
555
  return stepResult({ longTermMemory: `Sent keys ${params.keys}` });
@@ -484,7 +558,7 @@ var sendKeys = defineAction({
484
558
  var selectDropdown = defineAction({
485
559
  name: "select_dropdown",
486
560
  description: "Select a dropdown option by its exact text, on the element at [index].",
487
- schema: Type2.Object({ index: Type2.Integer({ minimum: 1 }), text: Type2.String() }),
561
+ schema: Type3.Object({ index: Type3.Integer({ minimum: 1 }), text: Type3.String() }),
488
562
  async execute(params, ctx) {
489
563
  const resolved = nodeAt(ctx, params.index);
490
564
  if (!resolved.ok) return resolved.result;
@@ -501,8 +575,9 @@ var selectDropdown = defineAction({
501
575
  });
502
576
  var getDropdownOptions = defineAction({
503
577
  name: "get_dropdown_options",
578
+ replaySafe: true,
504
579
  description: "List the available options of the dropdown element at [index].",
505
- schema: Type2.Object({ index: Type2.Integer({ minimum: 1 }) }),
580
+ schema: Type3.Object({ index: Type3.Integer({ minimum: 1 }) }),
506
581
  async execute(params, ctx) {
507
582
  const resolved = nodeAt(ctx, params.index);
508
583
  if (!resolved.ok) return resolved.result;
@@ -518,7 +593,7 @@ var getDropdownOptions = defineAction({
518
593
  var switchTab = defineAction({
519
594
  name: "switch_tab",
520
595
  description: "Focus another tab by its 4-char id.",
521
- schema: Type2.Object({ tabId: Type2.String() }),
596
+ schema: Type3.Object({ tabId: Type3.String() }),
522
597
  terminatesSequence: true,
523
598
  async execute(params, ctx) {
524
599
  await ctx.driver.switchTab(params.tabId);
@@ -528,7 +603,7 @@ var switchTab = defineAction({
528
603
  var openTab = defineAction({
529
604
  name: "open_tab",
530
605
  description: "Open a new tab at a URL.",
531
- schema: Type2.Object({ url: Type2.String() }),
606
+ schema: Type3.Object({ url: Type3.String() }),
532
607
  terminatesSequence: true,
533
608
  async execute(params, ctx) {
534
609
  const tabId = await ctx.session.openTab(params.url);
@@ -538,7 +613,7 @@ var openTab = defineAction({
538
613
  var closeTab = defineAction({
539
614
  name: "close_tab",
540
615
  description: "Close a tab by its 4-char id.",
541
- schema: Type2.Object({ tabId: Type2.String() }),
616
+ schema: Type3.Object({ tabId: Type3.String() }),
542
617
  async execute(params, ctx) {
543
618
  await ctx.driver.closeTab(params.tabId);
544
619
  return stepResult({ longTermMemory: `Closed tab ${params.tabId}` });
@@ -547,7 +622,7 @@ var closeTab = defineAction({
547
622
  var uploadFile = defineAction({
548
623
  name: "upload_file",
549
624
  description: "Set a file on the file-input element at [index]. Path must be available.",
550
- schema: Type2.Object({ index: Type2.Integer({ minimum: 0 }), path: Type2.String() }),
625
+ schema: Type3.Object({ index: Type3.Integer({ minimum: 0 }), path: Type3.String() }),
551
626
  async execute(params, ctx) {
552
627
  let uploadPath;
553
628
  const base = basename(params.path);
@@ -569,8 +644,9 @@ var uploadFile = defineAction({
569
644
  });
570
645
  var wait = defineAction({
571
646
  name: "wait",
647
+ replaySafe: true,
572
648
  description: "Wait for a number of seconds (capped at 30).",
573
- schema: Type2.Object({ seconds: Type2.Optional(Type2.Number()) }),
649
+ schema: Type3.Object({ seconds: Type3.Optional(Type3.Number()) }),
574
650
  async execute(params) {
575
651
  const requested = params.seconds ?? 3;
576
652
  await sleep(Math.min(Math.max(requested - 1, 0), 30) * 1e3);
@@ -579,9 +655,10 @@ var wait = defineAction({
579
655
  });
580
656
  var extractContent = defineAction({
581
657
  name: "extract_content",
658
+ replaySafe: true,
582
659
  description: "Extract the page's readable content, guided by a query.",
583
- schema: Type2.Object({
584
- query: Type2.String()
660
+ schema: Type3.Object({
661
+ query: Type3.String()
585
662
  }),
586
663
  async execute(params, ctx) {
587
664
  const text = await ctx.driver.extract(params.query, { signal: ctx.signal });
@@ -599,10 +676,10 @@ ${content}`,
599
676
  var writeFile = defineAction({
600
677
  name: "write_file",
601
678
  description: "Write (replacing) a file in the agent workspace.",
602
- schema: Type2.Object({
603
- fileName: Type2.String(),
604
- content: Type2.String(),
605
- append: Type2.Optional(Type2.Boolean())
679
+ schema: Type3.Object({
680
+ fileName: Type3.String(),
681
+ content: Type3.String(),
682
+ append: Type3.Optional(Type3.Boolean())
606
683
  }),
607
684
  async execute(params, ctx) {
608
685
  const message = params.append ? await ctx.fileSystem.appendFile(params.fileName, params.content) : await ctx.fileSystem.writeFile(params.fileName, params.content);
@@ -612,7 +689,7 @@ var writeFile = defineAction({
612
689
  var appendFile = defineAction({
613
690
  name: "append_file",
614
691
  description: "Append content to a file in the agent workspace.",
615
- schema: Type2.Object({ fileName: Type2.String(), content: Type2.String() }),
692
+ schema: Type3.Object({ fileName: Type3.String(), content: Type3.String() }),
616
693
  async execute(params, ctx) {
617
694
  const message = await ctx.fileSystem.appendFile(params.fileName, params.content);
618
695
  return stepResult({ extractedContent: message, longTermMemory: message });
@@ -621,7 +698,7 @@ var appendFile = defineAction({
621
698
  var replaceFile = defineAction({
622
699
  name: "replace_file",
623
700
  description: "Replace a substring in a workspace file (the todo.md checkbox primitive).",
624
- schema: Type2.Object({ fileName: Type2.String(), oldStr: Type2.String(), newStr: Type2.String() }),
701
+ schema: Type3.Object({ fileName: Type3.String(), oldStr: Type3.String(), newStr: Type3.String() }),
625
702
  async execute(params, ctx) {
626
703
  const message = await ctx.fileSystem.replaceFileStr(
627
704
  params.fileName,
@@ -633,8 +710,9 @@ var replaceFile = defineAction({
633
710
  });
634
711
  var readFile = defineAction({
635
712
  name: "read_file",
713
+ replaySafe: true,
636
714
  description: "Read a workspace file, or a whitelisted external file (text, .pdf, or .docx).",
637
- schema: Type2.Object({ fileName: Type2.String() }),
715
+ schema: Type3.Object({ fileName: Type3.String() }),
638
716
  async execute(params, ctx) {
639
717
  const managed = ctx.fileSystem.getFile(params.fileName);
640
718
  if (managed !== void 0) {
@@ -657,10 +735,10 @@ ${content}` });
657
735
  var done = defineAction({
658
736
  name: "done",
659
737
  description: "Finish the task. success=true ONLY when fully complete. text must report only observed facts.",
660
- schema: Type2.Object({
661
- text: Type2.String(),
662
- success: Type2.Boolean(),
663
- filesToDisplay: Type2.Optional(Type2.Array(Type2.String()))
738
+ schema: Type3.Object({
739
+ text: Type3.String(),
740
+ success: Type3.Boolean(),
741
+ filesToDisplay: Type3.Optional(Type3.Array(Type3.String()))
664
742
  }),
665
743
  execute(params, ctx) {
666
744
  const attachments = (params.filesToDisplay ?? []).map((name) => ctx.fileSystem.displayFile(name) !== void 0 ? name : void 0).filter((n) => n !== void 0);
@@ -979,28 +1057,28 @@ function isBinaryExtension(ext) {
979
1057
  }
980
1058
 
981
1059
  // src/agent/output-schema.ts
982
- import { Type as Type3 } from "@sinclair/typebox";
1060
+ import { Type as Type4 } from "@sinclair/typebox";
983
1061
  function buildAgentOutputSchema(actionUnion, variant) {
984
- const action = Type3.Array(actionUnion, { minItems: 1 });
1062
+ const action = Type4.Array(actionUnion, { minItems: 1 });
985
1063
  if (variant === "flash") {
986
- return Type3.Object(
987
- { memory: Type3.String(), action },
1064
+ return Type4.Object(
1065
+ { memory: Type4.String(), action },
988
1066
  { additionalProperties: false, title: "AgentOutput" }
989
1067
  );
990
1068
  }
991
1069
  const fields = {
992
- evaluation_previous_goal: Type3.String(),
993
- memory: Type3.String(),
994
- next_goal: Type3.String(),
1070
+ evaluation_previous_goal: Type4.String(),
1071
+ memory: Type4.String(),
1072
+ next_goal: Type4.String(),
995
1073
  action
996
1074
  };
997
1075
  if (variant === "thinking") {
998
- return Type3.Object(
999
- { thinking: Type3.Optional(Type3.String()), ...fields },
1076
+ return Type4.Object(
1077
+ { thinking: Type4.Optional(Type4.String()), ...fields },
1000
1078
  { additionalProperties: false, title: "AgentOutput" }
1001
1079
  );
1002
1080
  }
1003
- return Type3.Object(fields, { additionalProperties: false, title: "AgentOutput" });
1081
+ return Type4.Object(fields, { additionalProperties: false, title: "AgentOutput" });
1004
1082
  }
1005
1083
  function parseAction(entry) {
1006
1084
  const keys = Object.keys(entry);
@@ -1682,6 +1760,7 @@ var CaptchaWatchdog = class {
1682
1760
  };
1683
1761
 
1684
1762
  // src/agent/step-loop.ts
1763
+ import { controlledProviderCall, currentRunContext as currentRunContext2 } from "@ap3x/agent-core";
1685
1764
  import {
1686
1765
  StructuredOutputError,
1687
1766
  generateObject
@@ -1689,7 +1768,7 @@ import {
1689
1768
  import {
1690
1769
  buildInteractedElement
1691
1770
  } from "@ap3x/browser";
1692
- import { Type as Type4 } from "@sinclair/typebox";
1771
+ import { Type as Type5 } from "@sinclair/typebox";
1693
1772
  function anySignal(...signals) {
1694
1773
  const present = signals.filter((s) => s !== void 0);
1695
1774
  if (present.length === 0) return void 0;
@@ -1743,6 +1822,13 @@ var StepLoop = class {
1743
1822
  this.stopped = true;
1744
1823
  this.resume();
1745
1824
  }
1825
+ /** Inject a mid-run course-correction: `text` rides into the NEXT step's
1826
+ * messages as a transient nudge (drained per step, like the loop-detector's).
1827
+ * Lets a caller steer a running agent without pausing or restarting it. */
1828
+ steer(text) {
1829
+ const trimmed = text.trim();
1830
+ if (trimmed) this.pendingNudges.push(trimmed);
1831
+ }
1746
1832
  get isDone() {
1747
1833
  return this.done;
1748
1834
  }
@@ -1886,10 +1972,14 @@ var StepLoop = class {
1886
1972
  let output;
1887
1973
  try {
1888
1974
  const llmSignal = anySignal(stepSignal, AbortSignal.timeout(this.deps.limits.llmTimeoutMs));
1889
- const { object, usage } = await generateObject(this.deps.model, messages, schema, {
1890
- ...this.deps.llmStreamOptions,
1891
- signal: llmSignal
1892
- });
1975
+ currentRunContext2()?.controller?.turn();
1976
+ const options = { ...this.deps.llmStreamOptions, signal: llmSignal };
1977
+ const { object, usage } = await controlledProviderCall(
1978
+ this.deps.model,
1979
+ { messages },
1980
+ options,
1981
+ () => generateObject(this.deps.model, messages, schema, options)
1982
+ );
1893
1983
  this.deps.ledger.record(this.deps.model.id, usage);
1894
1984
  output = object;
1895
1985
  } catch (error) {
@@ -2052,8 +2142,8 @@ var StepLoop = class {
2052
2142
  /** A single-key union carrying only `done` (forced-termination schema). */
2053
2143
  doneOnlyUnion() {
2054
2144
  const done2 = this.deps.registry.get("done");
2055
- const schema = done2?.schema ?? Type4.Object({ text: Type4.String(), success: Type4.Boolean() });
2056
- return Type4.Object({ done: schema }, { additionalProperties: false, title: "done" });
2145
+ const schema = done2?.schema ?? Type5.Object({ text: Type5.String(), success: Type5.Boolean() });
2146
+ return Type5.Object({ done: schema }, { additionalProperties: false, title: "done" });
2057
2147
  }
2058
2148
  };
2059
2149
  function historyState(state, sensitiveData) {
@@ -2259,17 +2349,18 @@ function generateRunGif(history, options = {}) {
2259
2349
  }
2260
2350
 
2261
2351
  // src/agent/judge.ts
2352
+ import { controlledProviderCall as controlledProviderCall2 } from "@ap3x/agent-core";
2262
2353
  import {
2263
2354
  generateObject as generateObject2
2264
2355
  } from "@ap3x/ai";
2265
2356
  import { PLACEHOLDER_SCREENSHOT as PLACEHOLDER_SCREENSHOT2 } from "@ap3x/browser";
2266
- import { Type as Type5 } from "@sinclair/typebox";
2267
- var JudgementResultSchema = Type5.Object({
2268
- reasoning: Type5.String(),
2269
- verdict: Type5.Boolean(),
2270
- failureReason: Type5.String(),
2271
- impossibleTask: Type5.Boolean(),
2272
- reachedCaptcha: Type5.Boolean()
2357
+ import { Type as Type6 } from "@sinclair/typebox";
2358
+ var JudgementResultSchema = Type6.Object({
2359
+ reasoning: Type6.String(),
2360
+ verdict: Type6.Boolean(),
2361
+ failureReason: Type6.String(),
2362
+ impossibleTask: Type6.Boolean(),
2363
+ reachedCaptcha: Type6.Boolean()
2273
2364
  });
2274
2365
  var MAX_TEXT_CHARS = 4e4;
2275
2366
  var DEFAULT_MAX_IMAGES = 10;
@@ -2341,10 +2432,13 @@ async function judgeRun(model, task, history, options = {}) {
2341
2432
  { role: "user", content: system, timestamp: Date.now() },
2342
2433
  { role: "user", content: userParts, timestamp: Date.now() }
2343
2434
  ];
2344
- const { object, usage } = await generateObject2(model, messages, JudgementResultSchema, {
2345
- ...options.streamOptions,
2346
- signal: options.signal
2347
- });
2435
+ const streamOptions = { ...options.streamOptions, signal: options.signal };
2436
+ const { object, usage } = await controlledProviderCall2(
2437
+ model,
2438
+ { messages },
2439
+ streamOptions,
2440
+ () => generateObject2(model, messages, JudgementResultSchema, streamOptions)
2441
+ );
2348
2442
  return { judgement: object, usage };
2349
2443
  }
2350
2444
 
@@ -2375,11 +2469,11 @@ import { randomUUID } from "crypto";
2375
2469
  import { tmpdir } from "os";
2376
2470
  import { join as join3 } from "path";
2377
2471
  import { BrowserDriver } from "@ap3x/browser";
2378
- import { Type as Type6 } from "@sinclair/typebox";
2472
+ import { Type as Type7 } from "@sinclair/typebox";
2379
2473
  var screenshotAction = defineAction({
2380
2474
  name: "screenshot",
2381
2475
  description: "Capture a screenshot of the current page for visual inspection.",
2382
- schema: Type6.Object({}),
2476
+ schema: Type7.Object({}),
2383
2477
  async execute(_params, ctx) {
2384
2478
  const data = await ctx.driver.screenshot({ signal: ctx.signal });
2385
2479
  return stepResult({
@@ -2435,6 +2529,7 @@ var Agent = class {
2435
2529
  stepLoop;
2436
2530
  pendingPaused = false;
2437
2531
  pendingStopped = false;
2532
+ pendingSteers = [];
2438
2533
  /** Set once, after `run()` completes, when `output.generateGif` is on. */
2439
2534
  lastGif;
2440
2535
  /** Set once, after `run()` completes, when `output.recordHar` is on. */
@@ -2533,6 +2628,13 @@ var Agent = class {
2533
2628
  if (this.stepLoop) this.stepLoop.stop();
2534
2629
  else this.pendingStopped = true;
2535
2630
  }
2631
+ /** Course-correct a running (or not-yet-started) run: `text` is delivered to
2632
+ * the agent as a nudge on its next step. Buffered until the step loop exists
2633
+ * (mirroring `pause`), then flushed in order. */
2634
+ steer(text) {
2635
+ if (this.stepLoop) this.stepLoop.steer(text);
2636
+ else this.pendingSteers.push(text);
2637
+ }
2536
2638
  // --- internals -------------------------------------------------------------
2537
2639
  /** Clone the caller's (or default) registry and apply the vision filter (E7). */
2538
2640
  buildRegistry() {
@@ -2646,6 +2748,7 @@ var Agent = class {
2646
2748
  });
2647
2749
  if (this.pendingStopped) this.stepLoop.stop();
2648
2750
  else if (this.pendingPaused) this.stepLoop.pause();
2751
+ for (const steer of this.pendingSteers.splice(0)) this.stepLoop.steer(steer);
2649
2752
  return this.stepLoop;
2650
2753
  }
2651
2754
  };
@@ -2672,7 +2775,7 @@ import {
2672
2775
  CallToolRequestSchema,
2673
2776
  ListToolsRequestSchema
2674
2777
  } from "@modelcontextprotocol/sdk/types.js";
2675
- import { Type as Type7 } from "@sinclair/typebox";
2778
+ import { Type as Type8 } from "@sinclair/typebox";
2676
2779
  var ACT_TOOLS = [
2677
2780
  { tool: "browser_navigate", action: "navigate" },
2678
2781
  { tool: "browser_click", action: "click" },
@@ -2683,12 +2786,12 @@ var ACT_TOOLS = [
2683
2786
  { tool: "browser_switch_tab", action: "switch_tab" },
2684
2787
  { tool: "browser_close_tab", action: "close_tab" }
2685
2788
  ];
2686
- var GET_STATE_SCHEMA = Type7.Object({ include_screenshot: Type7.Optional(Type7.Boolean()) });
2687
- var SCREENSHOT_SCHEMA = Type7.Object({ full_page: Type7.Optional(Type7.Boolean()) });
2688
- var LIST_TABS_SCHEMA = Type7.Object({});
2689
- var RUN_AGENT_SCHEMA = Type7.Object({
2690
- task: Type7.String(),
2691
- max_steps: Type7.Optional(Type7.Integer({ minimum: 1 }))
2789
+ var GET_STATE_SCHEMA = Type8.Object({ include_screenshot: Type8.Optional(Type8.Boolean()) });
2790
+ var SCREENSHOT_SCHEMA = Type8.Object({ full_page: Type8.Optional(Type8.Boolean()) });
2791
+ var LIST_TABS_SCHEMA = Type8.Object({});
2792
+ var RUN_AGENT_SCHEMA = Type8.Object({
2793
+ task: Type8.String(),
2794
+ max_steps: Type8.Optional(Type8.Integer({ minimum: 1 }))
2692
2795
  });
2693
2796
  function textResult(text) {
2694
2797
  return { content: [{ type: "text", text }] };
@@ -2857,6 +2960,9 @@ export {
2857
2960
  stepResult,
2858
2961
  doneResult,
2859
2962
  isDoneResult,
2963
+ BrowserPostconditionSchema,
2964
+ PostconditionError,
2965
+ verifyPostconditions,
2860
2966
  resolveSecretsForUrl,
2861
2967
  applicablePlaceholders,
2862
2968
  sensitiveDataDescription,
package/dist/index.d.ts CHANGED
@@ -34,4 +34,6 @@ export * from "./agent/judge";
34
34
  export * from "./agent/captcha-watchdog";
35
35
  export * from "./agent/video";
36
36
  export * from "./testing";
37
+ export { verifyPostconditions, PostconditionError, type BrowserPostcondition, } from "./agent/postconditions";
38
+ export { BrowserWorkflowCatalog, type BrowserWorkflow, type BrowserWorkflowStep, type BrowserWorkflowStepResult, type BrowserWorkflowResult, } from "./agent/workflow-catalog";
37
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,wBAAwB,CAAC;AAC7C,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,4BAA4B,CAAC;AAG3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,cAAc,wBAAwB,CAAC;AACvC,cAAc,OAAO,CAAC;AAGtB,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC;AAI/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAG9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,wBAAwB,CAAC;AAC7C,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,4BAA4B,CAAC;AAG3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,cAAc,wBAAwB,CAAC;AACvC,cAAc,OAAO,CAAC;AAGtB,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC;AAI/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAG9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAE1B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,GAC3B,MAAM,0BAA0B,CAAC"}