@opengeni/runtime 3.2.2 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-LJT5NPWT.js +9 -0
- package/dist/assets/codemode-client.json +1 -1
- package/dist/browser-current-tab.d.ts +46 -0
- package/dist/browser-model-text.d.ts +126 -0
- package/dist/{chunk-2AUDDDO2.js → chunk-3DFH3ZHU.js} +784 -121
- package/dist/chunk-3DFH3ZHU.js.map +1 -0
- package/dist/{chunk-HJSIPTFA.js → chunk-4XDJ4TJA.js} +2 -2
- package/dist/{chunk-XQ23VCZ4.js → chunk-6FQ6KEII.js} +3 -3
- package/dist/{chunk-7LJ7KDPI.js → chunk-ERD4ICVB.js} +4 -3
- package/dist/{chunk-462CH77G.js → chunk-ETTCGPBT.js} +400 -9
- package/dist/chunk-ETTCGPBT.js.map +1 -0
- package/dist/{chunk-7OUG7WBV.js → chunk-GWRBALGQ.js} +181 -13
- package/dist/chunk-GWRBALGQ.js.map +1 -0
- package/dist/{chunk-YG26XE7B.js → chunk-JYDG2A7H.js} +2 -2
- package/dist/{chunk-RSHQSLEJ.js → chunk-KIL4FGX5.js} +3 -3
- package/dist/{chunk-NWS4HPB7.js → chunk-MIAL5I4O.js} +2 -2
- package/dist/{chunk-Z5U6WE22.js → chunk-PRZPGRIK.js} +2 -2
- package/dist/{chunk-KCPW4K7L.js → chunk-PZQUUBEB.js} +2 -2
- package/dist/{chunk-5WJOPANS.js → chunk-Y5THZH5N.js} +7 -7
- package/dist/codex-tool-search.d.ts +9 -0
- package/dist/gmail-rest-mcp.js +8 -8
- package/dist/index.d.ts +3 -0
- package/dist/index.js +14 -12
- package/dist/interaction-tool-json-schema.d.ts +27 -0
- package/dist/{lazy-manifest-AJ6XPJ4F.js → lazy-manifest-BLZ3UZLX.js} +4 -4
- package/dist/lazy-tool-transport.d.ts +10 -0
- package/dist/{localSources-SSIEH5F5.js → localSources-I6SIQ6CX.js} +6 -6
- package/dist/model-call.js +3 -3
- package/dist/sandbox/browser-control-client.d.ts +87 -0
- package/dist/sandbox/browser-control-http.d.ts +33 -0
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +7 -7
- package/dist/sandbox/routing/backend-resolver.d.ts +1 -0
- package/dist/sandbox/selfhosted/session.d.ts +22 -0
- package/dist/slack-rest-mcp.js +8 -8
- package/dist/workspace-tool-gateway.js +12 -12
- package/package.json +14 -14
- package/src/browser-current-tab.ts +139 -0
- package/src/browser-model-text.ts +378 -0
- package/src/bundled_site_skills/opengeni-sites/SKILL.md +46 -12
- package/src/codex-tool-search.ts +53 -0
- package/src/index.ts +39 -1
- package/src/interaction-tool-json-schema.ts +281 -0
- package/src/interaction-tools.ts +425 -235
- package/src/lazy-tool-transport.ts +229 -10
- package/src/model-input.ts +12 -1
- package/src/sandbox/browser-control-client.ts +425 -0
- package/src/sandbox/browser-control-http.ts +32 -0
- package/src/sandbox/index.ts +4 -0
- package/src/sandbox/routing/backend-resolver.ts +2 -0
- package/src/sandbox/selfhosted/session.ts +154 -0
- package/dist/agent-CTPLA3HP.js +0 -9
- package/dist/browser-action-json-schema.d.ts +0 -4
- package/dist/chunk-2AUDDDO2.js.map +0 -1
- package/dist/chunk-462CH77G.js.map +0 -1
- package/dist/chunk-7OUG7WBV.js.map +0 -1
- package/src/browser-action-json-schema.ts +0 -10
- /package/dist/{agent-CTPLA3HP.js.map → agent-LJT5NPWT.js.map} +0 -0
- /package/dist/{chunk-HJSIPTFA.js.map → chunk-4XDJ4TJA.js.map} +0 -0
- /package/dist/{chunk-XQ23VCZ4.js.map → chunk-6FQ6KEII.js.map} +0 -0
- /package/dist/{chunk-7LJ7KDPI.js.map → chunk-ERD4ICVB.js.map} +0 -0
- /package/dist/{chunk-YG26XE7B.js.map → chunk-JYDG2A7H.js.map} +0 -0
- /package/dist/{chunk-RSHQSLEJ.js.map → chunk-KIL4FGX5.js.map} +0 -0
- /package/dist/{chunk-NWS4HPB7.js.map → chunk-MIAL5I4O.js.map} +0 -0
- /package/dist/{chunk-Z5U6WE22.js.map → chunk-PRZPGRIK.js.map} +0 -0
- /package/dist/{chunk-KCPW4K7L.js.map → chunk-PZQUUBEB.js.map} +0 -0
- /package/dist/{chunk-5WJOPANS.js.map → chunk-Y5THZH5N.js.map} +0 -0
- /package/dist/{lazy-manifest-AJ6XPJ4F.js.map → lazy-manifest-BLZ3UZLX.js.map} +0 -0
- /package/dist/{localSources-SSIEH5F5.js.map → localSources-I6SIQ6CX.js.map} +0 -0
package/src/interaction-tools.ts
CHANGED
|
@@ -90,8 +90,26 @@ import {
|
|
|
90
90
|
type InteractionTransport,
|
|
91
91
|
} from "@opengeni/sdk";
|
|
92
92
|
import { z } from "zod";
|
|
93
|
-
import {
|
|
93
|
+
import {
|
|
94
|
+
BrowserTargetResolutionError,
|
|
95
|
+
CurrentBrowserTab,
|
|
96
|
+
expandRefShorthand,
|
|
97
|
+
type ResolvedBrowserSession,
|
|
98
|
+
type ResolvedBrowserTarget,
|
|
99
|
+
} from "./browser-current-tab";
|
|
100
|
+
import {
|
|
101
|
+
describeInvalidToolArguments,
|
|
102
|
+
interactionToolInputJsonSchema,
|
|
103
|
+
withRefShorthandLocators,
|
|
104
|
+
} from "./interaction-tool-json-schema";
|
|
94
105
|
import { guardedMcpFetch } from "./mcp-network";
|
|
106
|
+
import {
|
|
107
|
+
browserActionText,
|
|
108
|
+
browserObservationText,
|
|
109
|
+
browserOpenText,
|
|
110
|
+
browserReadText,
|
|
111
|
+
browserTabsText,
|
|
112
|
+
} from "./browser-model-text";
|
|
95
113
|
|
|
96
114
|
export const INTERACTION_ATTEMPT_TOOL_NAMES = FIRST_PARTY_IN_PROCESS_TOOL_NAMES;
|
|
97
115
|
|
|
@@ -189,10 +207,22 @@ const CompactBrowserActionReceipt = z
|
|
|
189
207
|
})
|
|
190
208
|
.strict();
|
|
191
209
|
const BrowserAgentActionReceipt = z.union([BrowserActionReceipt, CompactBrowserActionReceipt]);
|
|
210
|
+
/** Browser tools default to the browser and tab the agent used last. */
|
|
211
|
+
const CurrentBrowserSessionId = z
|
|
212
|
+
.string()
|
|
213
|
+
.uuid()
|
|
214
|
+
.optional()
|
|
215
|
+
.describe("Omit to use the browser you used last.");
|
|
216
|
+
const CurrentBrowserTargetId = z
|
|
217
|
+
.string()
|
|
218
|
+
.min(1)
|
|
219
|
+
.max(512)
|
|
220
|
+
.optional()
|
|
221
|
+
.describe("Omit to use the tab you used last.");
|
|
192
222
|
const BrowserScreenshotInput = z
|
|
193
223
|
.object({
|
|
194
|
-
browserSessionId:
|
|
195
|
-
targetId:
|
|
224
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
225
|
+
targetId: CurrentBrowserTargetId,
|
|
196
226
|
fullPage: z.boolean().optional(),
|
|
197
227
|
quality: z.number().int().min(1).max(100).optional(),
|
|
198
228
|
})
|
|
@@ -217,8 +247,8 @@ const BrowserScreenshotOutput = z
|
|
|
217
247
|
.strict();
|
|
218
248
|
const BrowserReadInput = z
|
|
219
249
|
.object({
|
|
220
|
-
browserSessionId:
|
|
221
|
-
targetId:
|
|
250
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
251
|
+
targetId: CurrentBrowserTargetId,
|
|
222
252
|
mode: z.enum(["matches", "count", "subtree", "dom"]).optional(),
|
|
223
253
|
dom: z
|
|
224
254
|
.discriminatedUnion("kind", [
|
|
@@ -478,25 +508,25 @@ const BrowserOpenOutput = z
|
|
|
478
508
|
.strict();
|
|
479
509
|
|
|
480
510
|
const BrowserTabsInput = z.discriminatedUnion("operation", [
|
|
481
|
-
z.object({ operation: z.literal("list"), browserSessionId:
|
|
511
|
+
z.object({ operation: z.literal("list"), browserSessionId: CurrentBrowserSessionId }).strict(),
|
|
482
512
|
z
|
|
483
513
|
.object({
|
|
484
514
|
operation: z.literal("open"),
|
|
485
|
-
browserSessionId:
|
|
515
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
486
516
|
url: z.string().url().max(16_384).optional(),
|
|
487
517
|
})
|
|
488
518
|
.strict(),
|
|
489
519
|
z
|
|
490
520
|
.object({
|
|
491
521
|
operation: z.literal("select"),
|
|
492
|
-
browserSessionId:
|
|
522
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
493
523
|
targetId: z.string().min(1).max(512),
|
|
494
524
|
})
|
|
495
525
|
.strict(),
|
|
496
526
|
z
|
|
497
527
|
.object({
|
|
498
528
|
operation: z.literal("close"),
|
|
499
|
-
browserSessionId:
|
|
529
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
500
530
|
targetId: z.string().min(1).max(512),
|
|
501
531
|
})
|
|
502
532
|
.strict(),
|
|
@@ -511,28 +541,28 @@ const BrowserTabsOutput = z
|
|
|
511
541
|
|
|
512
542
|
const BrowserObserveInput = z
|
|
513
543
|
.object({
|
|
514
|
-
browserSessionId:
|
|
515
|
-
targetId:
|
|
544
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
545
|
+
targetId: CurrentBrowserTargetId,
|
|
516
546
|
view: z.enum(["compact", "full"]).optional(),
|
|
517
547
|
includeScreenshot: z.boolean().optional(),
|
|
518
548
|
})
|
|
519
549
|
.strict();
|
|
520
550
|
const BrowserActInput = z
|
|
521
551
|
.object({
|
|
522
|
-
browserSessionId:
|
|
523
|
-
targetId:
|
|
552
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
553
|
+
targetId: CurrentBrowserTargetId,
|
|
524
554
|
expectedTargetGeneration: z.string().min(1).max(256).optional(),
|
|
525
555
|
expectedDocumentGeneration: z.string().min(1).max(256).nullable().optional(),
|
|
526
556
|
expectedFrameId: z.string().min(1).max(256).nullable().optional(),
|
|
527
557
|
view: z.enum(["compact", "full", "none"]).optional(),
|
|
528
|
-
action: z.union([BrowserAction, BrowserActionBatch]),
|
|
558
|
+
action: z.preprocess(expandRefShorthand, z.union([BrowserAction, BrowserActionBatch])),
|
|
529
559
|
})
|
|
530
560
|
.strict();
|
|
531
561
|
const BrowserClipboardInput = z.object({ browserSessionId: z.string().uuid() }).strict();
|
|
532
562
|
const BrowserDebugInput = z
|
|
533
563
|
.object({
|
|
534
|
-
browserSessionId:
|
|
535
|
-
targetId:
|
|
564
|
+
browserSessionId: CurrentBrowserSessionId,
|
|
565
|
+
targetId: CurrentBrowserTargetId,
|
|
536
566
|
kinds: z
|
|
537
567
|
.array(z.enum(["console", "page_error", "failed_request", "download"]))
|
|
538
568
|
.max(4)
|
|
@@ -759,16 +789,67 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
759
789
|
const selected = new Set(input.selectedTools ?? DEFAULT_FIRST_PARTY_MCP_TOOLS);
|
|
760
790
|
const permissions = input.permissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS;
|
|
761
791
|
const definitions: AttemptToolDefinition[] = [];
|
|
792
|
+
const currentTab = new CurrentBrowserTab({
|
|
793
|
+
listLiveSessions: async () =>
|
|
794
|
+
filterDiscoveredSessions(
|
|
795
|
+
(
|
|
796
|
+
await input.transport.listBrowserSessions(input.workspaceId, {
|
|
797
|
+
associatedSessionId: input.sessionId,
|
|
798
|
+
includeTerminal: false,
|
|
799
|
+
})
|
|
800
|
+
).sessions,
|
|
801
|
+
input.sessionId,
|
|
802
|
+
"current_session",
|
|
803
|
+
false,
|
|
804
|
+
),
|
|
805
|
+
listTargets: async (browserSessionId) =>
|
|
806
|
+
(await input.transport.listBrowserTargets(input.workspaceId, browserSessionId)).targets,
|
|
807
|
+
});
|
|
808
|
+
/** Run one call on the requested or current tab and remember it on success. */
|
|
809
|
+
const onTab = async <T>(
|
|
810
|
+
value: { browserSessionId?: string | undefined; targetId?: string | undefined },
|
|
811
|
+
run: (tab: ResolvedBrowserTarget) => Promise<T>,
|
|
812
|
+
): Promise<T> => {
|
|
813
|
+
const tab = await currentTab.target(value);
|
|
814
|
+
try {
|
|
815
|
+
const output = await run(tab);
|
|
816
|
+
currentTab.use(tab.browserSessionId, tab.targetId);
|
|
817
|
+
return output;
|
|
818
|
+
} catch (error) {
|
|
819
|
+
if (isGoneBrowserError(error)) currentTab.failed(tab);
|
|
820
|
+
throw error;
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
/** Run one call on the requested or current browser and remember it on success. */
|
|
824
|
+
const onBrowser = async <T>(
|
|
825
|
+
browserSessionId: string | undefined,
|
|
826
|
+
run: (browser: ResolvedBrowserSession) => Promise<T>,
|
|
827
|
+
): Promise<T> => {
|
|
828
|
+
const browser = await currentTab.session(browserSessionId);
|
|
829
|
+
try {
|
|
830
|
+
const output = await run(browser);
|
|
831
|
+
currentTab.use(browser.browserSessionId);
|
|
832
|
+
return output;
|
|
833
|
+
} catch (error) {
|
|
834
|
+
if (isGoneBrowserError(error)) currentTab.failed(browser);
|
|
835
|
+
throw error;
|
|
836
|
+
}
|
|
837
|
+
};
|
|
762
838
|
const add = <TInput extends z.ZodType, TOutput extends z.ZodType>(options: {
|
|
763
839
|
name: InteractionAttemptToolName;
|
|
764
840
|
codemodePath: readonly string[];
|
|
765
841
|
title: string;
|
|
766
842
|
description: string;
|
|
767
843
|
input: TInput;
|
|
844
|
+
/** Adjusts the model's view of the input schema; validation is unchanged. */
|
|
845
|
+
modelInputSchema?: (schema: AttemptToolJsonSchema) => AttemptToolJsonSchema;
|
|
768
846
|
output: TOutput;
|
|
769
847
|
readOnly: boolean;
|
|
770
848
|
idempotent: boolean;
|
|
771
849
|
approval?: "none" | "human" | "policy";
|
|
850
|
+
/** The model's text view of a successful result. The complete typed value
|
|
851
|
+
* stays in structuredContent; without this the text is that value as JSON. */
|
|
852
|
+
modelText?: (output: z.output<TOutput>) => string;
|
|
772
853
|
execute: (
|
|
773
854
|
value: z.output<TInput>,
|
|
774
855
|
context: AttemptToolExecutionContext,
|
|
@@ -786,10 +867,9 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
786
867
|
codemodePath: options.codemodePath,
|
|
787
868
|
title: options.title,
|
|
788
869
|
description: options.description,
|
|
789
|
-
inputSchema:
|
|
790
|
-
options.
|
|
791
|
-
|
|
792
|
-
: jsonSchema(options.input),
|
|
870
|
+
inputSchema: (options.modelInputSchema ?? ((schema) => schema))(
|
|
871
|
+
interactionToolInputJsonSchema(options.input),
|
|
872
|
+
),
|
|
793
873
|
outputSchema: jsonSchema(options.output),
|
|
794
874
|
annotations: {
|
|
795
875
|
title: options.title,
|
|
@@ -808,6 +888,7 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
808
888
|
context,
|
|
809
889
|
options.execute,
|
|
810
890
|
options.readOnly,
|
|
891
|
+
options.modelText,
|
|
811
892
|
),
|
|
812
893
|
});
|
|
813
894
|
};
|
|
@@ -885,13 +966,28 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
885
966
|
name: "browser_open",
|
|
886
967
|
codemodePath: ["interaction", "browser", "open"],
|
|
887
968
|
title: "Open or reuse browser",
|
|
888
|
-
description: `Open a managed BrowserSession on the current agent placement, reuse a relevant compatible live session by default, attach to an explicit workspace BrowserSession, or open the user's own Chrome with placement={kind:'attached_device'}. Use that directly for requests about 'my browser', 'my tabs', or current Chrome: it opens the only Chrome profile connected through the Opengeni Browser extension, or else the most recently active one (reported as attachedProfile, the rest as otherAttachedProfiles). Add deviceId (from interaction_discover scope=attached_browsers or otherAttachedProfiles) only when the user wants a different profile. Omitting placement never uses the user's Chrome. If no profile is connected, explain that the Chrome extension must be connected instead of silently creating a blank managed browser (it needs a Connected Machine, then the Opengeni Browser extension: ${OPENGENI_BROWSER_EXTENSION_URL}). BrowserSessions persist across tool calls; shell-launched browser daemons do not survive remote-command cleanup. Never switch to the user's attached Chrome as a fallback for a failed managed browser unless the user requested that profile. A new attached session opens a dedicated tab. Managed Chromium defaults to headed so OAuth and later human interaction use a supported browser; request headless=true only for agent-only work that will not require sign-in or human control. Operator-enabled disposable sandbox verification may explicitly request storageMode=ephemeral_context with headless=true: no saved identity, route, Computer, checkpoint or resume; process loss ends the session. Default private_profile storage is unchanged. Returns
|
|
969
|
+
description: `Open a managed BrowserSession on the current agent placement, reuse a relevant compatible live session by default, attach to an explicit workspace BrowserSession, or open the user's own Chrome with placement={kind:'attached_device'}. Use that directly for requests about 'my browser', 'my tabs', or current Chrome: it opens the only Chrome profile connected through the Opengeni Browser extension, or else the most recently active one (reported as attachedProfile, the rest as otherAttachedProfiles). Add deviceId (from interaction_discover scope=attached_browsers or otherAttachedProfiles) only when the user wants a different profile. Omitting placement never uses the user's Chrome. If no profile is connected, explain that the Chrome extension must be connected instead of silently creating a blank managed browser (it needs a Connected Machine, then the Opengeni Browser extension: ${OPENGENI_BROWSER_EXTENSION_URL}). BrowserSessions persist across tool calls; shell-launched browser daemons do not survive remote-command cleanup. Never switch to the user's attached Chrome as a fallback for a failed managed browser unless the user requested that profile. A new attached session opens a dedicated tab. Managed Chromium defaults to headed so OAuth and later human interaction use a supported browser; request headless=true only for agent-only work that will not require sign-in or human control. Operator-enabled disposable sandbox verification may explicitly request storageMode=ephemeral_context with headless=true: no saved identity, route, Computer, checkpoint or resume; process loss ends the session. Default private_profile storage is unchanged. Returns the session, its tabs and an outline of the opened (or selected) tab when one is available, in the browser_observe format. With initialUrl, only the opened tab is listed and the others are counted (browser_tabs list shows them). Act on the outline's elements directly instead of observing again. Call it first for any browser task; browser_observe, browser_act, browser_screenshot and browser_tabs then work on the session it returns.`,
|
|
889
970
|
input: BrowserOpenInput,
|
|
890
971
|
output: BrowserOpenOutput,
|
|
891
972
|
readOnly: false,
|
|
892
973
|
idempotent: true,
|
|
893
|
-
|
|
894
|
-
|
|
974
|
+
modelText: browserOpenText,
|
|
975
|
+
execute: async (value, context) => {
|
|
976
|
+
const opened = await openBrowser(
|
|
977
|
+
input.transport,
|
|
978
|
+
input.workspaceId,
|
|
979
|
+
input.sessionId,
|
|
980
|
+
value,
|
|
981
|
+
context,
|
|
982
|
+
);
|
|
983
|
+
currentTab.use(
|
|
984
|
+
opened.session.id,
|
|
985
|
+
opened.observation?.target.id ??
|
|
986
|
+
opened.targets.find((target) => target.selected)?.id ??
|
|
987
|
+
null,
|
|
988
|
+
);
|
|
989
|
+
return opened;
|
|
990
|
+
},
|
|
895
991
|
});
|
|
896
992
|
|
|
897
993
|
add({
|
|
@@ -899,45 +995,46 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
899
995
|
codemodePath: ["interaction", "browser", "tabs"],
|
|
900
996
|
title: "Manage browser tabs",
|
|
901
997
|
description:
|
|
902
|
-
"List, open, logically select, or close tabs in one exact BrowserSession. Selection changes the BrowserSession's default target, not the visible desktop tab. New attached-Chrome tabs open in the background. Use browser_act activate only when foregrounding the owned tab is explicitly intended. Closing a tab does not release the browser process; use browser_lifecycle for session cleanup. Returns the authoritative complete tab list after the operation. Open/select also return
|
|
998
|
+
"List, open, logically select, or close tabs in one exact BrowserSession. Selection changes the BrowserSession's default target, not the visible desktop tab. New attached-Chrome tabs open in the background. Use browser_act activate only when foregrounding the owned tab is explicitly intended. Closing a tab does not release the browser process; use browser_lifecycle for session cleanup. Returns the authoritative complete tab list after the operation. Open/select also return the page outline. Act on its elements directly; observe again after later page changes.",
|
|
903
999
|
input: BrowserTabsInput,
|
|
904
1000
|
output: BrowserTabsOutput,
|
|
905
1001
|
readOnly: false,
|
|
906
1002
|
idempotent: false,
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
mutationObservation
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
value.targetId,
|
|
920
|
-
);
|
|
921
|
-
} else if (value.operation === "close") {
|
|
922
|
-
return agentTargetList(
|
|
923
|
-
await input.transport.closeBrowserTarget(
|
|
1003
|
+
modelText: browserTabsText,
|
|
1004
|
+
execute: async (value) =>
|
|
1005
|
+
await onBrowser(value.browserSessionId, async ({ browserSessionId }) => {
|
|
1006
|
+
let mutationObservation: z.infer<typeof BrowserObservation> | undefined;
|
|
1007
|
+
if (value.operation === "open") {
|
|
1008
|
+
mutationObservation = await input.transport.openBrowserTarget(
|
|
1009
|
+
input.workspaceId,
|
|
1010
|
+
browserSessionId,
|
|
1011
|
+
{ ...(value.url ? { url: value.url } : {}) },
|
|
1012
|
+
);
|
|
1013
|
+
} else if (value.operation === "select") {
|
|
1014
|
+
mutationObservation = await input.transport.selectBrowserTarget(
|
|
924
1015
|
input.workspaceId,
|
|
925
|
-
|
|
1016
|
+
browserSessionId,
|
|
926
1017
|
value.targetId,
|
|
927
|
-
)
|
|
928
|
-
)
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1018
|
+
);
|
|
1019
|
+
} else if (value.operation === "close") {
|
|
1020
|
+
const closed = await input.transport.closeBrowserTarget(
|
|
1021
|
+
input.workspaceId,
|
|
1022
|
+
browserSessionId,
|
|
1023
|
+
value.targetId,
|
|
1024
|
+
);
|
|
1025
|
+
currentTab.forgetTarget(browserSessionId, value.targetId);
|
|
1026
|
+
return agentTargetList(closed);
|
|
1027
|
+
}
|
|
1028
|
+
const tabs = await input.transport.listBrowserTargets(input.workspaceId, browserSessionId);
|
|
1029
|
+
if (mutationObservation) {
|
|
1030
|
+
currentTab.use(browserSessionId, mutationObservation.target.id);
|
|
1031
|
+
return {
|
|
936
1032
|
...agentTargetList(tabs),
|
|
937
1033
|
mutationObservation: projectBrowserObservation(mutationObservation, "compact"),
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
return agentTargetList(tabs);
|
|
1037
|
+
}),
|
|
941
1038
|
});
|
|
942
1039
|
|
|
943
1040
|
add({
|
|
@@ -945,56 +1042,54 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
945
1042
|
codemodePath: ["interaction", "browser", "observe"],
|
|
946
1043
|
title: "Observe browser tab",
|
|
947
1044
|
description:
|
|
948
|
-
"Read one tab's URL, title, viewport
|
|
1045
|
+
"Read one tab's URL, title, viewport and accessibility outline: one line per element, role \"name\" [ref] value and states, indented by nesting. Locate an element by role and name, or by ref ({kind:'ref',ref}); refs are listed on controls and other actionable elements, not on text and layout lines. The default view is selective and says how many elements it left out; it is not a complete page tree. Set view=full for every element with its ref. Set includeScreenshot=true to add a current still image as tool image content; browser_screenshot is faster when only pixels are needed.",
|
|
949
1046
|
input: BrowserObserveInput,
|
|
950
1047
|
output: BrowserAgentObservation,
|
|
951
1048
|
readOnly: true,
|
|
952
1049
|
idempotent: true,
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
value.targetId,
|
|
958
|
-
);
|
|
959
|
-
if (!value.includeScreenshot)
|
|
960
|
-
return projectBrowserObservation(await observationRequest, value.view ?? "compact");
|
|
961
|
-
// These are independent reads. Settle both before returning an error so
|
|
962
|
-
// the tool never leaves an outstanding capture behind after a failed read.
|
|
963
|
-
const [observed, captured] = await Promise.allSettled([
|
|
964
|
-
observationRequest,
|
|
965
|
-
Promise.resolve().then(() =>
|
|
966
|
-
input.transport.captureBrowserTarget(
|
|
967
|
-
input.workspaceId,
|
|
968
|
-
value.browserSessionId,
|
|
969
|
-
value.targetId,
|
|
970
|
-
),
|
|
971
|
-
),
|
|
972
|
-
]);
|
|
973
|
-
if (observed.status === "rejected") throw observed.reason;
|
|
974
|
-
if (captured.status === "rejected") throw captured.reason;
|
|
975
|
-
let observation = observed.value;
|
|
976
|
-
const frame = captured.value;
|
|
977
|
-
if (
|
|
978
|
-
observation.target.targetGeneration !== frame.targetGeneration ||
|
|
979
|
-
observation.target.documentGeneration !== frame.documentGeneration
|
|
980
|
-
) {
|
|
981
|
-
observation = await input.transport.observeBrowserTarget(
|
|
1050
|
+
modelText: agentObservationText,
|
|
1051
|
+
execute: async (value) =>
|
|
1052
|
+
await onTab(value, async ({ browserSessionId, targetId }) => {
|
|
1053
|
+
const observationRequest = input.transport.observeBrowserTarget(
|
|
982
1054
|
input.workspaceId,
|
|
983
|
-
|
|
984
|
-
|
|
1055
|
+
browserSessionId,
|
|
1056
|
+
targetId,
|
|
985
1057
|
);
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1058
|
+
if (!value.includeScreenshot)
|
|
1059
|
+
return projectBrowserObservation(await observationRequest, value.view ?? "compact");
|
|
1060
|
+
// These are independent reads. Settle both before returning an error so
|
|
1061
|
+
// the tool never leaves an outstanding capture behind after a failed read.
|
|
1062
|
+
const [observed, captured] = await Promise.allSettled([
|
|
1063
|
+
observationRequest,
|
|
1064
|
+
Promise.resolve().then(() =>
|
|
1065
|
+
input.transport.captureBrowserTarget(input.workspaceId, browserSessionId, targetId),
|
|
1066
|
+
),
|
|
1067
|
+
]);
|
|
1068
|
+
if (observed.status === "rejected") throw observed.reason;
|
|
1069
|
+
if (captured.status === "rejected") throw captured.reason;
|
|
1070
|
+
let observation = observed.value;
|
|
1071
|
+
const frame = captured.value;
|
|
1072
|
+
if (
|
|
1073
|
+
observation.target.targetGeneration !== frame.targetGeneration ||
|
|
1074
|
+
observation.target.documentGeneration !== frame.documentGeneration
|
|
1075
|
+
) {
|
|
1076
|
+
observation = await input.transport.observeBrowserTarget(
|
|
1077
|
+
input.workspaceId,
|
|
1078
|
+
browserSessionId,
|
|
1079
|
+
targetId,
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
if (
|
|
1083
|
+
observation.target.targetGeneration !== frame.targetGeneration ||
|
|
1084
|
+
observation.target.documentGeneration !== frame.documentGeneration
|
|
1085
|
+
) {
|
|
1086
|
+
throw new Error("browser target changed while its visual observation was captured");
|
|
1087
|
+
}
|
|
1088
|
+
return new InteractionExecutionResult(
|
|
1089
|
+
projectBrowserObservation(observation, value.view ?? "compact"),
|
|
1090
|
+
[browserToolImageContent(frame.data, frame.mediaType)],
|
|
1091
|
+
);
|
|
1092
|
+
}),
|
|
998
1093
|
});
|
|
999
1094
|
|
|
1000
1095
|
add({
|
|
@@ -1007,70 +1102,69 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1007
1102
|
output: BrowserReadToolOutput,
|
|
1008
1103
|
readOnly: true,
|
|
1009
1104
|
idempotent: true,
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1105
|
+
modelText: (output) =>
|
|
1106
|
+
output.source === "accessibility" ? browserReadText(output) : JSON.stringify(output),
|
|
1107
|
+
execute: async (value) =>
|
|
1108
|
+
await onTab(value, async ({ browserSessionId, targetId }) => {
|
|
1109
|
+
if (value.mode === "dom") {
|
|
1110
|
+
const state =
|
|
1111
|
+
value.expectedTargetGeneration !== undefined &&
|
|
1112
|
+
value.expectedDocumentGeneration !== undefined &&
|
|
1113
|
+
value.expectedFrameId !== undefined
|
|
1114
|
+
? null
|
|
1115
|
+
: await input.transport.getBrowserTargetState(
|
|
1116
|
+
input.workspaceId,
|
|
1117
|
+
browserSessionId,
|
|
1118
|
+
targetId,
|
|
1119
|
+
);
|
|
1120
|
+
const fences = {
|
|
1121
|
+
expectedTargetGeneration: value.expectedTargetGeneration ?? state!.targetGeneration,
|
|
1122
|
+
expectedDocumentGeneration:
|
|
1123
|
+
value.expectedDocumentGeneration === undefined
|
|
1124
|
+
? state!.documentGeneration
|
|
1125
|
+
: value.expectedDocumentGeneration,
|
|
1126
|
+
expectedFrameId:
|
|
1127
|
+
value.expectedFrameId === undefined ? state!.frameId : value.expectedFrameId,
|
|
1128
|
+
};
|
|
1129
|
+
const documentGeneration = fences.expectedDocumentGeneration;
|
|
1130
|
+
const frameId = fences.expectedFrameId;
|
|
1131
|
+
if (documentGeneration === null || frameId === null) {
|
|
1132
|
+
throw new OpenGeniApiError(409, "browser target has no inspectable document", {
|
|
1133
|
+
code: "document_unavailable",
|
|
1134
|
+
retryable: true,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
const inspectedFences = {
|
|
1138
|
+
expectedTargetGeneration: fences.expectedTargetGeneration,
|
|
1139
|
+
expectedDocumentGeneration: documentGeneration,
|
|
1140
|
+
expectedFrameId: frameId,
|
|
1141
|
+
};
|
|
1142
|
+
const dom = value.dom!;
|
|
1143
|
+
const request =
|
|
1144
|
+
dom.kind === "element"
|
|
1145
|
+
? {
|
|
1146
|
+
...inspectedFences,
|
|
1147
|
+
kind: "element" as const,
|
|
1148
|
+
locator: dom.locator,
|
|
1149
|
+
...(dom.attributes ? { attributes: dom.attributes } : {}),
|
|
1150
|
+
maxChars: dom.maxChars ?? 4_096,
|
|
1151
|
+
}
|
|
1152
|
+
: { ...inspectedFences, kind: "count" as const, selector: dom.selector };
|
|
1153
|
+
return {
|
|
1154
|
+
...(await input.transport.readBrowserDom(
|
|
1155
|
+
input.workspaceId,
|
|
1156
|
+
browserSessionId,
|
|
1157
|
+
targetId,
|
|
1158
|
+
request,
|
|
1159
|
+
)),
|
|
1160
|
+
source: "dom" as const,
|
|
1161
|
+
};
|
|
1038
1162
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
const dom = value.dom!;
|
|
1045
|
-
const request =
|
|
1046
|
-
dom.kind === "element"
|
|
1047
|
-
? {
|
|
1048
|
-
...inspectedFences,
|
|
1049
|
-
kind: "element" as const,
|
|
1050
|
-
locator: dom.locator,
|
|
1051
|
-
...(dom.attributes ? { attributes: dom.attributes } : {}),
|
|
1052
|
-
maxChars: dom.maxChars ?? 4_096,
|
|
1053
|
-
}
|
|
1054
|
-
: { ...inspectedFences, kind: "count" as const, selector: dom.selector };
|
|
1055
|
-
return {
|
|
1056
|
-
...(await input.transport.readBrowserDom(
|
|
1057
|
-
input.workspaceId,
|
|
1058
|
-
value.browserSessionId,
|
|
1059
|
-
value.targetId,
|
|
1060
|
-
request,
|
|
1061
|
-
)),
|
|
1062
|
-
source: "dom" as const,
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
return readBrowserObservation(
|
|
1066
|
-
await input.transport.observeBrowserTarget(
|
|
1067
|
-
input.workspaceId,
|
|
1068
|
-
value.browserSessionId,
|
|
1069
|
-
value.targetId,
|
|
1070
|
-
),
|
|
1071
|
-
value,
|
|
1072
|
-
);
|
|
1073
|
-
},
|
|
1163
|
+
return readBrowserObservation(
|
|
1164
|
+
await input.transport.observeBrowserTarget(input.workspaceId, browserSessionId, targetId),
|
|
1165
|
+
value,
|
|
1166
|
+
);
|
|
1167
|
+
}),
|
|
1074
1168
|
});
|
|
1075
1169
|
|
|
1076
1170
|
add({
|
|
@@ -1083,34 +1177,38 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1083
1177
|
output: BrowserScreenshotOutput,
|
|
1084
1178
|
readOnly: true,
|
|
1085
1179
|
idempotent: true,
|
|
1086
|
-
execute: async (value) =>
|
|
1087
|
-
|
|
1088
|
-
input.
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1180
|
+
execute: async (value) =>
|
|
1181
|
+
await onTab(value, async ({ browserSessionId, targetId }) => {
|
|
1182
|
+
const frame = await input.transport.captureBrowserTarget(
|
|
1183
|
+
input.workspaceId,
|
|
1184
|
+
browserSessionId,
|
|
1185
|
+
targetId,
|
|
1186
|
+
{},
|
|
1187
|
+
{
|
|
1188
|
+
fullPage: value.fullPage ?? false,
|
|
1189
|
+
...(value.quality ? { quality: value.quality } : {}),
|
|
1190
|
+
},
|
|
1191
|
+
);
|
|
1192
|
+
return new InteractionExecutionResult(
|
|
1193
|
+
{
|
|
1194
|
+
kind: "browser_screenshot" as const,
|
|
1195
|
+
browserSessionId: frame.browserSessionId,
|
|
1196
|
+
targetId: frame.targetId,
|
|
1197
|
+
frameId: frame.frameId,
|
|
1198
|
+
targetGeneration: frame.targetGeneration,
|
|
1199
|
+
documentGeneration: frame.documentGeneration,
|
|
1200
|
+
mediaType: frame.mediaType,
|
|
1201
|
+
width: frame.width,
|
|
1202
|
+
height: frame.height,
|
|
1203
|
+
deviceScaleFactor: frame.deviceScaleFactor,
|
|
1204
|
+
scrollX: frame.scrollX,
|
|
1205
|
+
scrollY: frame.scrollY,
|
|
1206
|
+
capturedAt: frame.capturedAt,
|
|
1207
|
+
fullPage: value.fullPage ?? false,
|
|
1208
|
+
},
|
|
1209
|
+
[browserToolImageContent(frame.data, frame.mediaType)],
|
|
1210
|
+
);
|
|
1211
|
+
}),
|
|
1114
1212
|
});
|
|
1115
1213
|
|
|
1116
1214
|
add({
|
|
@@ -1118,20 +1216,20 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1118
1216
|
codemodePath: ["interaction", "browser", "act"],
|
|
1119
1217
|
title: "Act in browser tab",
|
|
1120
1218
|
description:
|
|
1121
|
-
"Perform one semantic-first browser action or bounded batch. Prefer action={type:'batch',actions:[...],fenceEachAction:true} for predictable edits in the same observed document, such as filling known form fields before one authorized submit. Pause for new page state, a dialog, or a decision instead of batching steps that depend on an unseen result. Reuse a page observation, when present, from browser_open, tab open/select or a prior action; observe only when none is present or it lacks needed content. Observe again after page changes or when needed content was omitted. If an action reports outcome_unknown, inspect the outcome and do not replay the batch. Use viewport to set page width, height, desktop/mobile layout and touch emulation, then check the measured viewport in the returned observation; emulation does not prove physical mobile-browser behavior. Use history back/forward for tab navigation; keypress shortcuts are page input and may not navigate browser history. A press key is one character or a key name (Enter, Tab, Escape, Backspace, Delete, Insert, Home, End, PageUp, PageDown, ArrowUp/Down/Left/Right, Space, F1-F24), optionally prefixed by Ctrl+, Alt+, Shift+, Meta+ or Mod+. The explicit activate action foregrounds the target in the user's desktop browser; use only when that is intended. Permission actions set a managed browser's web permission for this tab's exact current top-level origin.
|
|
1219
|
+
"Perform one semantic-first browser action or bounded batch. Prefer action={type:'batch',actions:[...],fenceEachAction:true} for predictable edits in the same observed document, such as filling known form fields before one authorized submit. Pause for new page state, a dialog, or a decision instead of batching steps that depend on an unseen result. Reuse a page observation, when present, from browser_open, tab open/select or a prior action; observe only when none is present or it lacks needed content. Observe again after page changes or when needed content was omitted. If an action reports outcome_unknown, inspect the outcome and do not replay the batch. Use viewport to set page width, height, desktop/mobile layout and touch emulation, then check the measured viewport in the returned observation; emulation does not prove physical mobile-browser behavior. Use history back/forward for tab navigation; keypress shortcuts are page input and may not navigate browser history. A press key is one character or a key name (Enter, Tab, Escape, Backspace, Delete, Insert, Home, End, PageUp, PageDown, ArrowUp/Down/Left/Right, Space, F1-F24), optionally prefixed by Ctrl+, Alt+, Shift+, Meta+ or Mod+. The explicit activate action foregrounds the target in the user's desktop browser; use only when that is intended. Permission actions set a managed browser's web permission for this tab's exact current top-level origin. The default receipt includes the page outline after the action, in the browser_observe format; set view=full for every element, or view=none when no post-action outline is needed. An upload action takes workspace File IDs; the artifact.artifactId returned by sandbox_file_publish is one. Text in fill, type and clipboard write may contain {{secret:…}} references; the value (or the current TOTP code for /otp) is typed at dispatch and never shown to you. Use type with mode:'keys' for split one-time-code boxes or inputs that ignore pasted text.",
|
|
1122
1220
|
input: BrowserActInput,
|
|
1221
|
+
modelInputSchema: withRefShorthandLocators,
|
|
1123
1222
|
output: BrowserAgentActionReceipt,
|
|
1124
1223
|
readOnly: false,
|
|
1125
1224
|
idempotent: true,
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
{
|
|
1225
|
+
modelText: agentActionText,
|
|
1226
|
+
execute: async (value, context) =>
|
|
1227
|
+
await onTab(value, async ({ browserSessionId, targetId }) => {
|
|
1228
|
+
// Omitted fences are resolved by the API from the live target in the same
|
|
1229
|
+
// request, so acting on the current document costs one round trip.
|
|
1230
|
+
const receipt = await input.transport.actInBrowser(input.workspaceId, browserSessionId, {
|
|
1133
1231
|
operationId: context.operationId,
|
|
1134
|
-
targetId
|
|
1232
|
+
targetId,
|
|
1135
1233
|
...(value.expectedTargetGeneration === undefined
|
|
1136
1234
|
? {}
|
|
1137
1235
|
: { expectedTargetGeneration: value.expectedTargetGeneration }),
|
|
@@ -1143,10 +1241,9 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1143
1241
|
: { expectedFrameId: value.expectedFrameId }),
|
|
1144
1242
|
...(value.view === "none" ? { observationMode: "none" as const } : {}),
|
|
1145
1243
|
action: value.action,
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
},
|
|
1244
|
+
});
|
|
1245
|
+
return value.view === "full" ? receipt : compactActionReceipt(receipt);
|
|
1246
|
+
}),
|
|
1150
1247
|
});
|
|
1151
1248
|
|
|
1152
1249
|
add({
|
|
@@ -1174,15 +1271,19 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1174
1271
|
readOnly: true,
|
|
1175
1272
|
idempotent: true,
|
|
1176
1273
|
execute: async (value) =>
|
|
1177
|
-
await
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1274
|
+
await onTab(
|
|
1275
|
+
value,
|
|
1276
|
+
async ({ browserSessionId, targetId }) =>
|
|
1277
|
+
await input.transport.listBrowserDiagnostics(
|
|
1278
|
+
input.workspaceId,
|
|
1279
|
+
browserSessionId,
|
|
1280
|
+
targetId,
|
|
1281
|
+
{
|
|
1282
|
+
...(value.kinds ? { kinds: value.kinds } : {}),
|
|
1283
|
+
...(value.after !== undefined ? { after: value.after } : {}),
|
|
1284
|
+
...(value.limit !== undefined ? { limit: value.limit } : {}),
|
|
1285
|
+
},
|
|
1286
|
+
),
|
|
1186
1287
|
),
|
|
1187
1288
|
});
|
|
1188
1289
|
|
|
@@ -1503,6 +1604,7 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1503
1604
|
request,
|
|
1504
1605
|
);
|
|
1505
1606
|
}
|
|
1607
|
+
currentTab.forgetSession(value.browserSessionId);
|
|
1506
1608
|
return await input.transport.endBrowserSession(
|
|
1507
1609
|
input.workspaceId,
|
|
1508
1610
|
value.browserSessionId,
|
|
@@ -1516,7 +1618,7 @@ export function createInteractionAttemptToolDefinitions(
|
|
|
1516
1618
|
codemodePath: ["interaction", "computer", "open"],
|
|
1517
1619
|
title: "Open or reuse computer",
|
|
1518
1620
|
description:
|
|
1519
|
-
"Open a ComputerSession on the current agent placement, reuse a relevant live session by default, or attach to an explicit workspace ComputerSession. Returns exact apps/windows/screens.",
|
|
1621
|
+
"Open a ComputerSession on the current agent placement, reuse a relevant live session by default, or attach to an explicit workspace ComputerSession. Returns exact apps/windows/screens. Call it first for any computer task; computer_observe and computer_act then work on the session it returns.",
|
|
1520
1622
|
input: ComputerOpenInput,
|
|
1521
1623
|
output: ComputerOpenOutput,
|
|
1522
1624
|
readOnly: false,
|
|
@@ -1912,6 +2014,9 @@ async function openBrowser(
|
|
|
1912
2014
|
): Promise<z.input<typeof BrowserOpenOutput>> {
|
|
1913
2015
|
let session: z.infer<typeof BrowserSession>;
|
|
1914
2016
|
let created = false;
|
|
2017
|
+
// A create that launched the browser in this call returns its tabs and the
|
|
2018
|
+
// opened tab's observation, which replaces the two reads below.
|
|
2019
|
+
let initialView: Awaited<ReturnType<InteractionTransport["createBrowserSession"]>>["initialView"];
|
|
1915
2020
|
let profileChoice: AttachedProfileChoice | undefined;
|
|
1916
2021
|
if (value.browserSessionId) {
|
|
1917
2022
|
session = await transport.getBrowserSession(workspaceId, value.browserSessionId);
|
|
@@ -1967,23 +2072,23 @@ async function openBrowser(
|
|
|
1967
2072
|
const placement = profileChoice
|
|
1968
2073
|
? { kind: "attached_device" as const, deviceId: profileChoice.chosen.id }
|
|
1969
2074
|
: (value.placement as z.infer<typeof InteractionPlacement> | undefined);
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
2075
|
+
const createdResponse = await transport.createBrowserSession(workspaceId, {
|
|
2076
|
+
operationId: context.operationId,
|
|
2077
|
+
sessionId: sourceSessionId,
|
|
2078
|
+
...(value.name ? { name: value.name } : {}),
|
|
2079
|
+
...(value.initialUrl ? { initialUrl: value.initialUrl } : {}),
|
|
2080
|
+
headless: requestedHeadless,
|
|
2081
|
+
...(value.storageMode ? { storageMode: value.storageMode } : {}),
|
|
2082
|
+
...(placement ? { placement } : {}),
|
|
2083
|
+
...(value.identityId ? { identityId: value.identityId } : {}),
|
|
2084
|
+
...(value.baseRevisionId ? { baseRevisionId: value.baseRevisionId } : {}),
|
|
2085
|
+
...(value.networkRouteId ? { networkRouteId: value.networkRouteId } : {}),
|
|
2086
|
+
...(value.linkedComputerSessionId
|
|
2087
|
+
? { linkedComputerSessionId: value.linkedComputerSessionId }
|
|
2088
|
+
: {}),
|
|
2089
|
+
});
|
|
2090
|
+
session = createdResponse.session;
|
|
2091
|
+
initialView = createdResponse.initialView;
|
|
1987
2092
|
}
|
|
1988
2093
|
}
|
|
1989
2094
|
if (session.lifecycle === "suspended") {
|
|
@@ -2002,7 +2107,8 @@ async function openBrowser(
|
|
|
2002
2107
|
}
|
|
2003
2108
|
: {};
|
|
2004
2109
|
if (session.lifecycle !== "active") return { session, targets: [], ...chosenProfile };
|
|
2005
|
-
let targets =
|
|
2110
|
+
let targets =
|
|
2111
|
+
initialView?.targets ?? (await transport.listBrowserTargets(workspaceId, session.id)).targets;
|
|
2006
2112
|
if (
|
|
2007
2113
|
value.initialUrl &&
|
|
2008
2114
|
!created &&
|
|
@@ -2031,9 +2137,11 @@ async function openBrowser(
|
|
|
2031
2137
|
targets = inventory.targets;
|
|
2032
2138
|
}
|
|
2033
2139
|
const primary = openedBrowserTarget(targets, value.initialUrl);
|
|
2034
|
-
const observation = primary
|
|
2035
|
-
?
|
|
2036
|
-
:
|
|
2140
|
+
const observation = !primary
|
|
2141
|
+
? undefined
|
|
2142
|
+
: initialView?.observation.target.id === primary.id
|
|
2143
|
+
? projectBrowserObservation(initialView.observation, "compact")
|
|
2144
|
+
: await observeOpenedBrowserTarget(transport, workspaceId, session.id, primary);
|
|
2037
2145
|
// A browser opened on a URL is about that tab. A person's own Chrome profile
|
|
2038
2146
|
// can hold dozens of unrelated tabs; listing them all costs context on every
|
|
2039
2147
|
// later step, so only the opened tab is returned with a count of the rest
|
|
@@ -2574,6 +2682,79 @@ function readBrowserObservation(
|
|
|
2574
2682
|
};
|
|
2575
2683
|
}
|
|
2576
2684
|
|
|
2685
|
+
/** A tool's model text, or its JSON when it has none. Rendering is a pure view
|
|
2686
|
+
* of a value that already passed its output schema; should it ever fail, the
|
|
2687
|
+
* model still gets the complete value rather than an error for a call that
|
|
2688
|
+
* succeeded. */
|
|
2689
|
+
function resultModelText<T>(output: T, modelText: ((output: T) => string) | undefined): string {
|
|
2690
|
+
if (modelText) {
|
|
2691
|
+
try {
|
|
2692
|
+
return modelText(output);
|
|
2693
|
+
} catch {
|
|
2694
|
+
// Fall through to the complete value.
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return JSON.stringify(output);
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
/** The text view of a complete observation (view=full): every node, every ref. */
|
|
2701
|
+
function fullObservationText(observation: z.infer<typeof BrowserObservation>): string {
|
|
2702
|
+
const semantic = observation.semantic;
|
|
2703
|
+
const roots =
|
|
2704
|
+
semantic?.kind === "snapshot"
|
|
2705
|
+
? semantic.roots
|
|
2706
|
+
: semantic?.kind === "diff"
|
|
2707
|
+
? semantic.changed
|
|
2708
|
+
: [];
|
|
2709
|
+
return browserObservationText(
|
|
2710
|
+
{
|
|
2711
|
+
target: observation.target,
|
|
2712
|
+
viewport: observation.viewport,
|
|
2713
|
+
focusedRef: observation.focusedRef,
|
|
2714
|
+
changedRegions: observation.changedRegions,
|
|
2715
|
+
diagnostics: observation.diagnostics,
|
|
2716
|
+
dialog: observation.dialog,
|
|
2717
|
+
screenshot: observation.screenshot ?? undefined,
|
|
2718
|
+
agentView: {
|
|
2719
|
+
sourceKind: semantic?.kind ?? "none",
|
|
2720
|
+
nodes: flattenBrowserNodes(roots).map(({ node, depth }) => ({
|
|
2721
|
+
ref: node.ref,
|
|
2722
|
+
role: node.role,
|
|
2723
|
+
depth,
|
|
2724
|
+
...(node.name !== undefined ? { name: node.name } : {}),
|
|
2725
|
+
...(node.description !== undefined ? { description: node.description } : {}),
|
|
2726
|
+
...(node.value !== undefined ? { value: node.value } : {}),
|
|
2727
|
+
states: node.states,
|
|
2728
|
+
actions: node.actions,
|
|
2729
|
+
})),
|
|
2730
|
+
...(semantic?.kind === "diff" && semantic.removedRefs.length > 0
|
|
2731
|
+
? { removedRefCount: semantic.removedRefs.length }
|
|
2732
|
+
: {}),
|
|
2733
|
+
},
|
|
2734
|
+
},
|
|
2735
|
+
{ allRefs: true },
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
function agentObservationText(observation: z.infer<typeof BrowserAgentObservation>): string {
|
|
2740
|
+
return "agentView" in observation
|
|
2741
|
+
? browserObservationText(observation)
|
|
2742
|
+
: fullObservationText(observation);
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
function agentActionText(receipt: z.infer<typeof BrowserAgentActionReceipt>): string {
|
|
2746
|
+
if ("protocolVersion" in receipt) {
|
|
2747
|
+
const text = browserActionText({
|
|
2748
|
+
targetId: receipt.targetId,
|
|
2749
|
+
state: receipt.state,
|
|
2750
|
+
error: receipt.error,
|
|
2751
|
+
...(receipt.targets ? { targets: receipt.targets } : {}),
|
|
2752
|
+
});
|
|
2753
|
+
return receipt.observation ? `${text}\n${fullObservationText(receipt.observation)}` : text;
|
|
2754
|
+
}
|
|
2755
|
+
return browserActionText(receipt);
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2577
2758
|
async function safeInteractionExecution<TInput extends z.ZodType, TOutput extends z.ZodType>(
|
|
2578
2759
|
inputSchema: TInput,
|
|
2579
2760
|
outputSchema: TOutput,
|
|
@@ -2584,6 +2765,7 @@ async function safeInteractionExecution<TInput extends z.ZodType, TOutput extend
|
|
|
2584
2765
|
context: AttemptToolExecutionContext,
|
|
2585
2766
|
) => Promise<z.input<TOutput> | InteractionExecutionResult<z.input<TOutput>>>,
|
|
2586
2767
|
readOnly: boolean,
|
|
2768
|
+
modelText?: (output: z.output<TOutput>) => string,
|
|
2587
2769
|
): Promise<AttemptToolResultValue> {
|
|
2588
2770
|
try {
|
|
2589
2771
|
const value = inputSchema.parse(raw);
|
|
@@ -2593,7 +2775,7 @@ async function safeInteractionExecution<TInput extends z.ZodType, TOutput extend
|
|
|
2593
2775
|
);
|
|
2594
2776
|
const result = {
|
|
2595
2777
|
content: [
|
|
2596
|
-
{ type: "text" as const, text:
|
|
2778
|
+
{ type: "text" as const, text: resultModelText(structuredContent, modelText) },
|
|
2597
2779
|
...(executed instanceof InteractionExecutionResult ? executed.additionalContent : []),
|
|
2598
2780
|
],
|
|
2599
2781
|
structuredContent: structuredContent as NonNullable<
|
|
@@ -2603,7 +2785,10 @@ async function safeInteractionExecution<TInput extends z.ZodType, TOutput extend
|
|
|
2603
2785
|
return result;
|
|
2604
2786
|
} catch (error) {
|
|
2605
2787
|
if (error instanceof z.ZodError) {
|
|
2606
|
-
return interactionErrorResult("invalid_arguments",
|
|
2788
|
+
return interactionErrorResult("invalid_arguments", describeInvalidToolArguments(error));
|
|
2789
|
+
}
|
|
2790
|
+
if (error instanceof BrowserTargetResolutionError) {
|
|
2791
|
+
return interactionErrorResult(error.code, error.message);
|
|
2607
2792
|
}
|
|
2608
2793
|
// A failed read is still a useful tool result. Preserve the API's public
|
|
2609
2794
|
// explanation; mutations and uncertain outcomes retain their failure path.
|
|
@@ -2709,6 +2894,11 @@ function browserToolImageContent(
|
|
|
2709
2894
|
return { type: "image", data: Buffer.from(data).toString("base64"), mimeType };
|
|
2710
2895
|
}
|
|
2711
2896
|
|
|
2897
|
+
/** The remembered browser or tab no longer exists. */
|
|
2898
|
+
function isGoneBrowserError(error: unknown): boolean {
|
|
2899
|
+
return error instanceof OpenGeniApiError && (error.status === 404 || error.status === 410);
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2712
2902
|
function interactionErrorResult(
|
|
2713
2903
|
code: string,
|
|
2714
2904
|
message: string,
|