@ddwang/magnitude-core 0.3.1-ddwang.1 → 0.3.1-ddwang.2

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 (99) hide show
  1. package/dist/actions/desktopActions.js +2 -1
  2. package/dist/actions/index.d.ts +10 -9
  3. package/dist/actions/memoryActions.d.ts +9 -0
  4. package/dist/actions/memoryActions.js +34 -0
  5. package/dist/actions/types.d.ts +5 -1
  6. package/dist/actions/util.d.ts +2 -1
  7. package/dist/actions/util.js +6 -23
  8. package/dist/actions/webActions.d.ts +9 -1
  9. package/dist/actions/webActions.js +29 -5
  10. package/dist/actions/webActions.test.d.ts +1 -0
  11. package/dist/actions/webActions.test.js +43 -0
  12. package/dist/agent/browserAgent.d.ts +4 -2
  13. package/dist/agent/browserAgent.js +18 -4
  14. package/dist/agent/errors.d.ts +14 -0
  15. package/dist/agent/errors.js +29 -0
  16. package/dist/agent/index.d.ts +17 -6
  17. package/dist/agent/index.js +151 -23
  18. package/dist/ai/baml_client/async_client.d.ts +17 -26
  19. package/dist/ai/baml_client/async_client.js +175 -43
  20. package/dist/ai/baml_client/async_request.d.ts +13 -9
  21. package/dist/ai/baml_client/async_request.js +55 -19
  22. package/dist/ai/baml_client/index.d.ts +2 -1
  23. package/dist/ai/baml_client/index.js +6 -5
  24. package/dist/ai/baml_client/inlinedbaml.js +2 -2
  25. package/dist/ai/baml_client/parser.js +18 -18
  26. package/dist/ai/baml_client/sync_client.d.ts +12 -5
  27. package/dist/ai/baml_client/sync_client.js +64 -22
  28. package/dist/ai/baml_client/sync_request.d.ts +11 -9
  29. package/dist/ai/baml_client/sync_request.js +55 -19
  30. package/dist/ai/baml_client/type_builder.d.ts +2 -0
  31. package/dist/ai/baml_client/type_builder.js +25 -1
  32. package/dist/ai/baml_client/watchers.d.ts +32 -0
  33. package/dist/ai/baml_client/watchers.js +50 -0
  34. package/dist/ai/baseten.d.ts +4 -0
  35. package/dist/ai/baseten.js +10 -0
  36. package/dist/ai/modelHarness.d.ts +9 -9
  37. package/dist/ai/modelHarness.js +204 -69
  38. package/dist/ai/modelResponseError.d.ts +4 -0
  39. package/dist/ai/modelResponseError.js +8 -0
  40. package/dist/ai/multiModelHarness.d.ts +2 -5
  41. package/dist/ai/plannerResponse.d.ts +34 -0
  42. package/dist/ai/plannerResponse.js +96 -0
  43. package/dist/ai/plannerResponse.test.d.ts +1 -0
  44. package/dist/ai/plannerResponse.test.js +112 -0
  45. package/dist/ai/structuredOutput.d.ts +11 -0
  46. package/dist/ai/structuredOutput.js +94 -0
  47. package/dist/ai/structuredOutput.test.d.ts +1 -0
  48. package/dist/ai/structuredOutput.test.js +91 -0
  49. package/dist/ai/types.d.ts +30 -1
  50. package/dist/ai/util.js +38 -3
  51. package/dist/ai/util.test.d.ts +1 -0
  52. package/dist/ai/util.test.js +95 -0
  53. package/dist/common/events.d.ts +2 -0
  54. package/dist/common/operation.d.ts +31 -0
  55. package/dist/common/operation.js +102 -0
  56. package/dist/common/operation.test.d.ts +1 -0
  57. package/dist/common/operation.test.js +53 -0
  58. package/dist/common/retry.js +7 -2
  59. package/dist/common/util.js +9 -2
  60. package/dist/connectors/browserConnector.d.ts +13 -0
  61. package/dist/connectors/browserConnector.js +167 -4
  62. package/dist/connectors/index.d.ts +6 -2
  63. package/dist/index.cjs +1568 -355
  64. package/dist/index.d.cts +259 -74
  65. package/dist/index.d.ts +4 -0
  66. package/dist/index.js +2 -0
  67. package/dist/index.mjs +1563 -358
  68. package/dist/memory/agentMemory.d.ts +10 -0
  69. package/dist/memory/agentMemory.js +60 -14
  70. package/dist/memory/agentMemory.test.d.ts +1 -0
  71. package/dist/memory/agentMemory.test.js +150 -0
  72. package/dist/memory/image.d.ts +2 -0
  73. package/dist/memory/image.js +24 -35
  74. package/dist/memory/image.test.d.ts +1 -0
  75. package/dist/memory/image.test.js +46 -0
  76. package/dist/memory/notebook.d.ts +58 -0
  77. package/dist/memory/notebook.js +75 -0
  78. package/dist/memory/notebook.test.d.ts +1 -0
  79. package/dist/memory/notebook.test.js +99 -0
  80. package/dist/memory/rendering/renderJsonParts.test.js +2 -2
  81. package/dist/memory/serde.js +4 -18
  82. package/dist/memory/serde.test.d.ts +1 -0
  83. package/dist/memory/serde.test.js +64 -0
  84. package/dist/web/browserProvider.d.ts +1 -0
  85. package/dist/web/browserProvider.js +11 -0
  86. package/dist/web/browserProvider.test.d.ts +1 -0
  87. package/dist/web/browserProvider.test.js +44 -0
  88. package/dist/web/harness.d.ts +7 -0
  89. package/dist/web/harness.js +80 -19
  90. package/dist/web/recovery.d.ts +46 -0
  91. package/dist/web/recovery.js +137 -0
  92. package/dist/web/recovery.test.d.ts +1 -0
  93. package/dist/web/recovery.test.js +182 -0
  94. package/dist/web/stability.js +10 -3
  95. package/dist/web/types.d.ts +6 -1
  96. package/dist/web/visualizer/cursor.js +2 -1
  97. package/dist/web/zodReexport.test.d.ts +1 -0
  98. package/dist/web/zodReexport.test.js +14 -0
  99. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { createAction } from ".";
2
+ import { operationSleep } from '@/common/operation';
2
3
  import { z } from "zod";
3
4
  import { DesktopConnector } from "@/connectors/desktopConnector";
4
5
  // Mouse actions
@@ -136,7 +137,7 @@ export const desktopWaitAction = createAction({
136
137
  seconds: z.number().describe("Seconds to wait"),
137
138
  }),
138
139
  resolver: async ({ input: { seconds }, agent }) => {
139
- await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
140
+ await operationSleep(seconds * 1000);
140
141
  },
141
142
  render: ({ seconds }) => `◴ wait ${seconds}s`
142
143
  });
@@ -1,24 +1,25 @@
1
- import { Agent } from "@/agent";
1
+ import type { Agent } from "@/agent";
2
+ import type { AgentMemory } from '@/memory/agentMemory';
2
3
  import { RenderableContent } from "@/memory/observation";
3
4
  import { z, Schema, ZodTypeAny } from "zod";
5
+ import type { OperationOptions } from '@/common/operation';
6
+ export type ActionContext<T> = {
7
+ input: T;
8
+ agent: Agent;
9
+ memory?: AgentMemory;
10
+ } & OperationOptions;
4
11
  export interface ActionDefinition<T> {
5
12
  name: string;
6
13
  description?: string;
7
14
  schema: Schema<T>;
8
- resolver: ({ input, agent }: {
9
- input: T;
10
- agent: Agent;
11
- }) => Promise<void | RenderableContent>;
15
+ resolver: (context: ActionContext<T>) => Promise<void | RenderableContent>;
12
16
  render: (action: T) => string;
13
17
  }
14
18
  export declare function createAction<S extends ZodTypeAny>(action: {
15
19
  name: string;
16
20
  description?: string;
17
21
  schema?: S;
18
- resolver: ({ input, agent }: {
19
- input: z.infer<S>;
20
- agent: Agent;
21
- }) => Promise<void | RenderableContent>;
22
+ resolver: (context: ActionContext<z.infer<S>>) => Promise<void | RenderableContent>;
22
23
  render?: (action: z.infer<S>) => string;
23
24
  }): ActionDefinition<z.infer<S>>;
24
25
  export type ActionPayload<A extends ActionDefinition<any>> = {
@@ -0,0 +1,9 @@
1
+ export declare const memoryActions: (import(".").ActionDefinition<{
2
+ key: string;
3
+ text: string;
4
+ sources: number[];
5
+ operation: "add" | "correct";
6
+ expected_text: string | null;
7
+ }> | import(".").ActionDefinition<{
8
+ key: string;
9
+ }>)[];
@@ -0,0 +1,34 @@
1
+ import { createAction } from '.';
2
+ import { z } from 'zod';
3
+ import { NOTEBOOK_LIMITS, noteUpdateSchema } from '@/memory/notebook';
4
+ export const memoryActions = [
5
+ createAction({
6
+ name: 'memory:note',
7
+ description: 'Add a new record or explicitly correct one existing record before leaving facts needed later. Adds never overwrite. Corrections must match the targeted note\'s current text. Cite supporting observations. This consumes one action without interacting with the browser.',
8
+ schema: noteUpdateSchema,
9
+ resolver: async ({ input, agent, memory }) => {
10
+ try {
11
+ const { operation, expected_text, ...note } = input;
12
+ if (operation === 'add' ? expected_text !== null : expected_text === null) {
13
+ throw new Error('Use expected_text null for add; for correct, copy the targeted note\'s exact current text.');
14
+ }
15
+ (memory ?? agent.memory).remember(note, expected_text ?? undefined);
16
+ return { saved: input.key };
17
+ }
18
+ catch (error) {
19
+ return { saved: false, error: String(error), instruction: 'No notes changed. Review the error and notebook. New records need new keys; corrections need an exact current-text match. Preserve existing facts when addressing source or capacity errors.' };
20
+ }
21
+ },
22
+ render: input => `note: ${input.key}`,
23
+ }),
24
+ createAction({
25
+ name: 'memory:forget',
26
+ description: 'Remove an obsolete task note after consolidating any facts still needed. This does not delete the captured observation history.',
27
+ schema: z.object({ key: z.string().min(1).max(NOTEBOOK_LIMITS.key) }),
28
+ resolver: async ({ input, agent, memory }) => {
29
+ (memory ?? agent.memory).forget(input.key);
30
+ return { forgotten: input.key };
31
+ },
32
+ render: input => `forget note: ${input.key}`,
33
+ }),
34
+ ];
@@ -7,12 +7,16 @@ export interface Action {
7
7
  variant: string;
8
8
  [key: string]: any;
9
9
  }
10
- export type ActionIntent = ClickIntent | TypeIntent | ScrollIntent | SwitchTabIntent;
10
+ export type ActionIntent = ClickIntent | HoverIntent | TypeIntent | ScrollIntent | SwitchTabIntent;
11
11
  export type Intent = ActionIntent | CheckIntent;
12
12
  export interface ClickIntent {
13
13
  variant: 'click';
14
14
  target: string;
15
15
  }
16
+ export interface HoverIntent {
17
+ variant: 'hover';
18
+ target: string;
19
+ }
16
20
  export interface TypeIntent {
17
21
  variant: 'type';
18
22
  target: string;
@@ -1,7 +1,7 @@
1
1
  import { ActionDefinition } from ".";
2
2
  import TypeBuilder from "@/ai/baml_client/type_builder";
3
3
  import { FieldType } from "@boundaryml/baml/native";
4
- import { Schema } from "zod";
4
+ import { Schema, ZodSchema } from "zod";
5
5
  export interface JsonSchema {
6
6
  type?: string;
7
7
  title?: string;
@@ -18,3 +18,4 @@ export interface JsonSchema {
18
18
  }
19
19
  export declare function convertZodToBaml(tb: TypeBuilder, schema: Schema): FieldType;
20
20
  export declare function convertActionDefinitionsToBaml<T>(tb: TypeBuilder, actionVocabulary: ActionDefinition<T>[]): FieldType;
21
+ export declare function actionInputSchema<T>({ name, description, schema }: ActionDefinition<T>): ZodSchema<any>;
@@ -268,29 +268,12 @@ export function convertActionDefinitionsToBaml(tb, actionVocabulary) {
268
268
  /**
269
269
  * Convert action definitions to BAML TypeBuilder union representing any one of the actions
270
270
  */
271
- const actionTypes = [];
272
- for (const { name, description, schema } of actionVocabulary) {
273
- // Need to augment the schema a bit:
274
- // (1) if object, unpack and include variant as literal { variant: name, ...others }
275
- // (2) if primitive, add as name input { variant: name, input: <primitive> }
276
- let wrapperSchema;
277
- const baseWrapperSchema = z.object({
278
- variant: description ? z.literal(name).describe(description) : z.literal(name)
279
- });
280
- if (schema instanceof ZodObject) {
281
- // Merge the variant field with the existing object schema
282
- // The original schema's fields will be at the top level alongside 'variant'.
283
- wrapperSchema = baseWrapperSchema.merge(schema);
284
- }
285
- else {
286
- wrapperSchema = baseWrapperSchema.extend({
287
- input: schema,
288
- });
289
- }
290
- //if (description) wrapperSchema = wrapperSchema.describe(description);
291
- actionTypes.push(convertZodToBaml(tb, wrapperSchema));
292
- }
293
- return tb.union(actionTypes);
271
+ return tb.union(actionVocabulary.map(action => convertZodToBaml(tb, actionInputSchema(action))));
272
+ }
273
+ // One wire schema for BAML's prompt and provider-enforced structured output.
274
+ export function actionInputSchema({ name, description, schema }) {
275
+ const wrapper = z.object({ variant: description ? z.literal(name).describe(description) : z.literal(name) });
276
+ return schema instanceof ZodObject ? wrapper.merge(schema) : wrapper.extend({ input: schema });
294
277
  }
295
278
  //const tb = new TypeBuilder();
296
279
  //console.log(convertZodToBaml(tb, z.string()))
@@ -7,6 +7,10 @@ export declare const mouseDoubleClickAction: ActionDefinition<{
7
7
  x: number;
8
8
  y: number;
9
9
  }>;
10
+ export declare const mouseHoverAction: ActionDefinition<{
11
+ x: number;
12
+ y: number;
13
+ }>;
10
14
  export declare const mouseRightClickAction: ActionDefinition<{
11
15
  x: number;
12
16
  y: number;
@@ -28,6 +32,7 @@ export declare const keyboardEnterAction: ActionDefinition<any>;
28
32
  export declare const keyboardTabAction: ActionDefinition<any>;
29
33
  export declare const keyboardBackspaceAction: ActionDefinition<any>;
30
34
  export declare const keyboardSelectAllAction: ActionDefinition<any>;
35
+ export declare const keyboardEscapeAction: ActionDefinition<{}>;
31
36
  export declare const scrollCoordAction: ActionDefinition<{
32
37
  x: number;
33
38
  y: number;
@@ -54,6 +59,9 @@ export declare const webActions: readonly [ActionDefinition<{
54
59
  }>, ActionDefinition<{
55
60
  x: number;
56
61
  y: number;
62
+ }>, ActionDefinition<{
63
+ x: number;
64
+ y: number;
57
65
  }>, ActionDefinition<{
58
66
  x: number;
59
67
  y: number;
@@ -74,6 +82,6 @@ export declare const webActions: readonly [ActionDefinition<{
74
82
  url: string;
75
83
  }>, ActionDefinition<{
76
84
  content: string;
77
- }>, ActionDefinition<any>, ActionDefinition<any>, ActionDefinition<any>, ActionDefinition<any>, ActionDefinition<{
85
+ }>, ActionDefinition<any>, ActionDefinition<any>, ActionDefinition<any>, ActionDefinition<{}>, ActionDefinition<any>, ActionDefinition<{
78
86
  seconds: number;
79
87
  }>];
@@ -28,6 +28,19 @@ export const mouseDoubleClickAction = createAction({
28
28
  },
29
29
  render: ({ x, y }) => `⊙ double click (${x}, ${y})`
30
30
  });
31
+ export const mouseHoverAction = createAction({
32
+ name: 'mouse:hover',
33
+ description: "Hover over an element to reveal tooltips, dropdown menus, or hidden content",
34
+ schema: z.object({
35
+ x: z.number().int(),
36
+ y: z.number().int(),
37
+ }),
38
+ resolver: async ({ input: { x, y }, agent }) => {
39
+ const web = agent.require(BrowserConnector);
40
+ await web.getHarness().hover({ x, y });
41
+ },
42
+ render: ({ x, y }) => `◎ hover (${x}, ${y})`
43
+ });
31
44
  export const mouseRightClickAction = createAction({
32
45
  name: 'mouse:right_click',
33
46
  schema: z.object({
@@ -95,14 +108,23 @@ export const keyboardSelectAllAction = createAction({
95
108
  },
96
109
  render: () => `⬚ select all`
97
110
  });
111
+ export const keyboardEscapeAction = createAction({
112
+ name: 'keyboard:escape',
113
+ description: 'Press Escape to dismiss a dismissible dialog, menu, or focused interaction. This does not bypass a subscription or sign-in requirement.',
114
+ schema: z.object({}),
115
+ resolver: async ({ agent }) => {
116
+ await agent.require(BrowserConnector).getHarness().escape();
117
+ },
118
+ render: () => '⎋ press Escape'
119
+ });
98
120
  export const scrollCoordAction = createAction({
99
121
  name: 'mouse:scroll',
100
- description: "Hover mouse over target and scroll",
122
+ description: "Hover over the area to scroll. Distances are in pixels, not wheel ticks. To browse a full page, use about 500-600 pixels per scroll. Reserve small distances for fine adjustments or small scrollable areas.",
101
123
  schema: z.object({
102
124
  x: z.number().int(),
103
125
  y: z.number().int(),
104
- deltaX: z.number().int().describe("Pixels to scroll horizontally"),
105
- deltaY: z.number().int().describe("Pixels to scroll vertically"),
126
+ deltaX: z.number().int().describe("Horizontal distance in pixels: positive scrolls right, negative scrolls left. Use 0 for vertical-only scrolling."),
127
+ deltaY: z.number().int().describe("Vertical distance in pixels: 600 scrolls down, -600 scrolls up. Use about 500-600 to browse a full page; 5 moves only 5 pixels. Use smaller distances for fine adjustments and 0 for horizontal-only scrolling."),
106
128
  }),
107
129
  resolver: async ({ input: { x, y, deltaX, deltaY }, agent }) => {
108
130
  const webConnector = agent.require(BrowserConnector);
@@ -165,10 +187,10 @@ export const waitAction = createAction({
165
187
  name: 'wait',
166
188
  description: "Actions include smart waiting automatically - so only use this when a significant additional wait is clearly required.",
167
189
  schema: z.object({
168
- seconds: z.number()
190
+ seconds: z.number().finite().nonnegative()
169
191
  }),
170
192
  resolver: async ({ input: { seconds }, agent }) => {
171
- await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
193
+ await agent.require(BrowserConnector).wait(seconds * 1000);
172
194
  },
173
195
  render: ({ seconds }) => `◴ wait for ${seconds}s`
174
196
  });
@@ -176,6 +198,7 @@ export const webActions = [
176
198
  clickCoordAction,
177
199
  mouseDoubleClickAction,
178
200
  mouseRightClickAction,
201
+ mouseHoverAction,
179
202
  scrollCoordAction,
180
203
  mouseDragAction,
181
204
  newTabAction,
@@ -185,6 +208,7 @@ export const webActions = [
185
208
  keyboardEnterAction,
186
209
  keyboardTabAction,
187
210
  keyboardBackspaceAction,
211
+ keyboardEscapeAction,
188
212
  keyboardSelectAllAction,
189
213
  waitAction,
190
214
  ];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { expect, mock, test } from 'bun:test';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+ import { keyboardEscapeAction, scrollCoordAction, waitAction, webActions } from './webActions';
4
+ test('Escape is exposed and forwards the Escape command', async () => {
5
+ const escape = mock(async () => { });
6
+ const agent = { require: () => ({ getHarness: () => ({ escape }) }) };
7
+ expect(webActions).toContain(keyboardEscapeAction);
8
+ await keyboardEscapeAction.resolver({ agent, input: {} });
9
+ expect(escape).toHaveBeenCalledTimes(1);
10
+ });
11
+ test('wait validates its duration and uses browser cooldown handling', async () => {
12
+ const wait = mock(async () => { });
13
+ const agent = { require: () => ({ wait }) };
14
+ await waitAction.resolver({ agent, input: { seconds: 2 } });
15
+ expect(wait).toHaveBeenCalledWith(2000);
16
+ expect(waitAction.schema.safeParse({ seconds: -1 }).success).toBe(false);
17
+ expect(waitAction.schema.safeParse({ seconds: Infinity }).success).toBe(false);
18
+ });
19
+ test('scroll scale and precision guidance are included in the action schema', () => {
20
+ const prompt = `${scrollCoordAction.description} ${JSON.stringify(zodToJsonSchema(scrollCoordAction.schema))}`;
21
+ expect(prompt).toContain('not wheel ticks');
22
+ expect(prompt).toContain('500-600');
23
+ expect(prompt).toContain('600 scrolls down, -600 scrolls up');
24
+ expect(prompt).toContain('positive scrolls right, negative scrolls left');
25
+ expect(prompt).toContain('fine adjustments');
26
+ });
27
+ for (const deltas of [
28
+ { deltaX: 0, deltaY: 5 },
29
+ { deltaX: 0, deltaY: -5 },
30
+ { deltaX: 0, deltaY: 600 },
31
+ { deltaX: 0, deltaY: -600 },
32
+ { deltaX: 600, deltaY: 0 },
33
+ { deltaX: -5, deltaY: 0 },
34
+ ]) {
35
+ test(`scroll forwards pixel distances unchanged: ${deltas.deltaX}, ${deltas.deltaY}`, async () => {
36
+ const scroll = mock(async () => { });
37
+ const agent = { require: () => ({ getHarness: () => ({ scroll }) }) };
38
+ const input = scrollCoordAction.schema.parse({ x: 120, y: 250, ...deltas });
39
+ await scrollCoordAction.resolver({ agent, input });
40
+ expect(scroll).toHaveBeenCalledTimes(1);
41
+ expect(scroll).toHaveBeenCalledWith({ x: 120, y: 250, ...deltas });
42
+ });
43
+ }
@@ -4,6 +4,7 @@ import { BrowserConnectorOptions } from "@/connectors/browserConnector";
4
4
  import { Schema, ZodSchema } from "zod";
5
5
  import z from "zod";
6
6
  import EventEmitter from "eventemitter3";
7
+ import { type OperationOptions } from '@/common/operation';
7
8
  export declare function startBrowserAgent(options?: AgentOptions & BrowserConnectorOptions & {
8
9
  narrate?: boolean;
9
10
  }): Promise<BrowserAgent>;
@@ -23,7 +24,8 @@ export declare class BrowserAgent extends Agent {
23
24
  });
24
25
  get page(): Page;
25
26
  get context(): BrowserContext;
26
- nav(url: string): Promise<void>;
27
- extract<T extends Schema>(instructions: string, schema: T): Promise<z.infer<T>>;
27
+ nav(url: string, options?: OperationOptions): Promise<void>;
28
+ extract<T extends Schema>(instructions: string, schema: T, options?: OperationOptions): Promise<z.infer<T>>;
29
+ private _extract;
28
30
  }
29
31
  export {};
@@ -5,6 +5,7 @@ import { narrateBrowserAgent } from "./narrator";
5
5
  import { partitionHtml, serializeToMarkdown } from 'magnitude-extract';
6
6
  import EventEmitter from "eventemitter3";
7
7
  import { retry } from "@/common/retry";
8
+ import { checkOperation } from '@/common/operation';
8
9
  // export interface StartAgentWithWebOptions {
9
10
  // agentBaseOptions?: Partial<AgentOptions>;
10
11
  // webConnectorOptions?: BrowserConnectorOptions;
@@ -29,15 +30,19 @@ export async function startBrowserAgent(options //StartAgentWithWebOptions = {}
29
30
  return agent;
30
31
  }
31
32
  async function getFullPageContent(page) {
33
+ checkOperation();
32
34
  // 1. Get all iframe element handles
33
35
  const iframeHandles = await page.locator('iframe').elementHandles();
34
36
  // 2. Iterate through each iframe handle
35
37
  for (const iframeHandle of iframeHandles) {
38
+ checkOperation();
36
39
  // 3. Get the Frame object for the iframe
37
40
  const frame = await iframeHandle.contentFrame();
41
+ checkOperation();
38
42
  if (frame) {
39
43
  // 4. Get the HTML content of the iframe
40
44
  const iframeContent = await frame.content();
45
+ checkOperation();
41
46
  // 5. Use evaluate to replace the iframe element with its content.
42
47
  // We pass the content as an argument to avoid issues with string escaping.
43
48
  await iframeHandle.evaluate((iframeNode, { content }) => {
@@ -62,6 +67,7 @@ async function getFullPageContent(page) {
62
67
  }
63
68
  }
64
69
  // 6. Return the final, modified page content
70
+ checkOperation();
65
71
  return page.content();
66
72
  }
67
73
  export class BrowserAgent extends Agent {
@@ -80,11 +86,17 @@ export class BrowserAgent extends Agent {
80
86
  get context() {
81
87
  return this.require(BrowserConnector).getHarness().context;
82
88
  }
83
- async nav(url) {
84
- this.browserAgentEvents.emit('nav', url);
85
- await this.require(BrowserConnector).getHarness().navigate(url);
89
+ async nav(url, options = {}) {
90
+ return this.runOperation(options, async () => {
91
+ this.browserAgentEvents.emit('nav', url);
92
+ checkOperation();
93
+ await this.require(BrowserConnector).getHarness().navigate(url);
94
+ });
95
+ }
96
+ async extract(instructions, schema, options = {}) {
97
+ return this.runOperation(options, () => this._extract(instructions, schema));
86
98
  }
87
- async extract(instructions, schema) {
99
+ async _extract(instructions, schema) {
88
100
  this.browserAgentEvents.emit('extractStarted', instructions, schema);
89
101
  //const htmlContent = await this.page.content();
90
102
  const htmlContent = await retry(async () => await getFullPageContent(this.page), { retries: 5, delay: 200, exponential: true });
@@ -115,7 +127,9 @@ export class BrowserAgent extends Agent {
115
127
  // Convert to markdown
116
128
  const markdown = serializeToMarkdown(result, markdownOptions);
117
129
  const screenshot = await this.require(BrowserConnector).getHarness().screenshot();
130
+ checkOperation();
118
131
  const data = await this.models.extract(instructions, schema, screenshot, markdown);
132
+ checkOperation();
119
133
  this.browserAgentEvents.emit('extractDone', instructions, data);
120
134
  return data;
121
135
  }
@@ -6,3 +6,17 @@ export declare class AgentError extends Error {
6
6
  readonly options: Required<AgentErrorOptions>;
7
7
  constructor(message: string, options?: AgentErrorOptions);
8
8
  }
9
+ export declare class ActionLimitError extends AgentError {
10
+ readonly limit: number;
11
+ constructor(limit: number);
12
+ }
13
+ export declare class OperationCancelledError extends AgentError {
14
+ constructor(reason?: unknown);
15
+ }
16
+ export declare class OperationDeadlineError extends AgentError {
17
+ readonly deadline: number;
18
+ constructor(deadline: number);
19
+ }
20
+ export declare class AgentBusyError extends AgentError {
21
+ constructor();
22
+ }
@@ -26,6 +26,35 @@ export class AgentError extends Error {
26
26
  //this.failure = failure;
27
27
  }
28
28
  }
29
+ export class ActionLimitError extends AgentError {
30
+ limit;
31
+ constructor(limit) {
32
+ super(`Task did not finish within ${limit} actions`, { variant: 'action_limit' });
33
+ this.limit = limit;
34
+ this.name = 'ActionLimitError';
35
+ }
36
+ }
37
+ export class OperationCancelledError extends AgentError {
38
+ constructor(reason) {
39
+ super('Operation cancelled', { variant: 'cancelled' });
40
+ this.name = 'OperationCancelledError';
41
+ this.cause = reason;
42
+ }
43
+ }
44
+ export class OperationDeadlineError extends AgentError {
45
+ deadline;
46
+ constructor(deadline) {
47
+ super('Operation deadline exceeded', { variant: 'deadline' });
48
+ this.deadline = deadline;
49
+ this.name = 'OperationDeadlineError';
50
+ }
51
+ }
52
+ export class AgentBusyError extends AgentError {
53
+ constructor() {
54
+ super('Agent has an active operation; await whenIdle() before reusing it', { variant: 'busy' });
55
+ this.name = 'AgentBusyError';
56
+ }
57
+ }
29
58
  // // Agent will only throw these types of errors
30
59
  // import { ActionIngredient } from "./recipe/types";
31
60
  // import { WebAction } from "./web/types";
@@ -5,7 +5,8 @@ import { AgentEvents } from "@/common/events";
5
5
  import { AgentConnector } from '@/connectors';
6
6
  import { RenderableContent } from '@/memory/observation';
7
7
  import { LLMClient } from "@/ai/types";
8
- import { AgentMemory } from "@/memory";
8
+ import { type OperationOptions } from '@/common/operation';
9
+ import { AgentMemory, MemoryRenderOptions } from "@/memory";
9
10
  import { ActionDefinition } from "@/actions";
10
11
  import { MultiModelHarness } from '@/ai/multiModelHarness';
11
12
  export interface AgentOptions {
@@ -14,8 +15,9 @@ export interface AgentOptions {
14
15
  actions?: ActionDefinition<any>[];
15
16
  prompt?: string | null;
16
17
  telemetry?: boolean;
18
+ maxActions?: number;
17
19
  }
18
- export interface ActOptions {
20
+ export interface ActOptions extends OperationOptions {
19
21
  prompt?: string;
20
22
  data?: RenderableContent;
21
23
  memory?: AgentMemory;
@@ -30,20 +32,29 @@ export declare class Agent {
30
32
  private doneActing;
31
33
  private _paused;
32
34
  private _pauseResolve;
35
+ private activeOperation?;
36
+ private idle;
37
+ private stopped;
33
38
  protected latestTaskMemory: AgentMemory;
34
39
  constructor(baseConfig?: Partial<AgentOptions>);
35
40
  getConnector<C extends AgentConnector>(connectorClass: new (...args: any[]) => C): C | undefined;
36
41
  require<C extends AgentConnector>(connectorClass: new (...args: any[]) => C): C;
37
42
  start(): Promise<void>;
38
43
  identifyAction(action: Action): ActionDefinition<any>;
39
- exec(action: Action, memory?: AgentMemory): Promise<void>;
44
+ /** True until underlying work settles, including after a cancelled caller returns. */
45
+ get busy(): boolean;
46
+ whenIdle(): Promise<void>;
47
+ protected runOperation<T>(options: OperationOptions, fn: () => Promise<T>): Promise<T>;
48
+ exec(action: Action, memory?: AgentMemory, options?: OperationOptions): Promise<unknown>;
49
+ private _exec;
40
50
  protected _recordConnectorObservations(memory: AgentMemory): Promise<void>;
41
51
  get memory(): AgentMemory;
42
52
  act(taskOrSteps: string | string[], options?: ActOptions): Promise<void>;
43
- _traceAct(task: string, memory: AgentMemory, options?: ActOptions): Promise<void>;
53
+ private _runAct;
54
+ private _traceAct;
44
55
  private _buildContext;
45
- _act(description: string, memory: AgentMemory, options?: ActOptions): Promise<void>;
46
- query<T extends z.Schema>(query: string, schema: T): Promise<z.infer<T>>;
56
+ private _act;
57
+ query<T extends z.Schema>(query: string, schema: T, options?: MemoryRenderOptions & OperationOptions): Promise<z.infer<T>>;
47
58
  queueDone(): Promise<void>;
48
59
  private _waitIfPaused;
49
60
  pause(): void;