@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ControlResponse, type BrowserControlEnsureRequest, type BrowserControlEnsureResponse, type BrowserFramesOpenRequest, type ComputerFramesOpenRequest, type StreamChannel } from "@opengeni/agent-proto";
|
|
2
2
|
import { Manifest } from "@openai/agents/sandbox";
|
|
3
3
|
import type { ExposedPortEndpoint } from "../stream-port.js";
|
|
4
|
+
import { type BrowserControlHttpCall, type BrowserControlHttpOutcome } from "../browser-control-http.js";
|
|
4
5
|
import { type ControlRpc } from "./control-rpc.js";
|
|
5
6
|
import { type SelfhostedRetryClock } from "./retry-policy.js";
|
|
6
7
|
import type { SelfhostedOpObserver } from "./op-observer.js";
|
|
@@ -95,11 +96,16 @@ export interface SelfhostedBackgroundCommandAdoption {
|
|
|
95
96
|
connectionInstanceId: string;
|
|
96
97
|
opId: string;
|
|
97
98
|
command: string;
|
|
99
|
+
/** The exec already failed because the machine stopped answering. Record it
|
|
100
|
+
* with a stop request instead of as a running background command. */
|
|
101
|
+
abandoned?: boolean;
|
|
98
102
|
}
|
|
99
103
|
/** Exact command-admission snapshot. A worker resolves this immediately before
|
|
100
104
|
* every exec/Git admission; retries and already-started operations retain it. */
|
|
101
105
|
export interface SelfhostedOperationAdmission {
|
|
102
106
|
transactionalFsWriteSupported?: boolean;
|
|
107
|
+
/** The live runner serves in-process browser/computer controller requests. */
|
|
108
|
+
browserControlHttpSupported?: boolean;
|
|
103
109
|
/** Workspace segment of the physical agent/relay route. Personal machines may
|
|
104
110
|
* originate in a different same-organization workspace than the session. */
|
|
105
111
|
workspaceId?: string;
|
|
@@ -123,6 +129,9 @@ export interface SelfhostedOperationAdmission {
|
|
|
123
129
|
export interface SelfhostedSessionDeps {
|
|
124
130
|
/** Exact live daemon capability; absent preserves legacy small-file writes. */
|
|
125
131
|
transactionalFsWriteSupported?: boolean;
|
|
132
|
+
/** Exact live daemon capability; absent keeps controller requests on the
|
|
133
|
+
* command transport. */
|
|
134
|
+
browserControlHttpSupported?: boolean;
|
|
126
135
|
/** Authorization/session workspace. */
|
|
127
136
|
workspaceId: string;
|
|
128
137
|
/** Physical control-plane workspace. Defaults to workspaceId for legacy and
|
|
@@ -303,6 +312,7 @@ export declare class SelfhostedSession {
|
|
|
303
312
|
private readonly resourcePolicySupported;
|
|
304
313
|
private readonly operationCpuQuotaSupported;
|
|
305
314
|
private readonly transactionalFsWriteSupported;
|
|
315
|
+
private readonly browserControlHttpSupported;
|
|
306
316
|
private readonly defaultOpStream;
|
|
307
317
|
private readonly connectionInstanceId;
|
|
308
318
|
private readonly resolveOperationAdmission;
|
|
@@ -524,6 +534,14 @@ export declare class SelfhostedSession {
|
|
|
524
534
|
/** Ensure the loopback browser controller sidecar owned by this connected
|
|
525
535
|
* machine. The authority scope is supplied by the caller and remains local. */
|
|
526
536
|
ensureBrowserControl(request: BrowserControlEnsureRequest): Promise<BrowserControlEnsureResponse>;
|
|
537
|
+
/** Send one request to the agent-owned controller of an exact authority
|
|
538
|
+
* scope, served inside the runner over loopback without starting a process.
|
|
539
|
+
* `unsupported` means this connection's runner does not serve it and nothing
|
|
540
|
+
* was sent. `controller_absent` means no controller for that scope and
|
|
541
|
+
* generation accepted the request, so it was not delivered and the caller may
|
|
542
|
+
* ensure the controller and send again. A body too large for one reply is
|
|
543
|
+
* drained from the runner's spool; the request is never repeated. */
|
|
544
|
+
browserControlHttp(request: BrowserControlHttpCall): Promise<BrowserControlHttpOutcome>;
|
|
527
545
|
/** Open one browser-native frame producer and return its canonical relay
|
|
528
546
|
* endpoint. This does not route through `resolveExposedPort`: that legacy
|
|
529
547
|
* method allocates PTY/desktop resources based on fixed ports, while this op
|
|
@@ -597,6 +615,7 @@ export declare class SelfhostedSandboxClient {
|
|
|
597
615
|
private readonly operationResourcePolicySupported;
|
|
598
616
|
private readonly operationCpuQuotaSupported;
|
|
599
617
|
private readonly transactionalFsWriteSupported;
|
|
618
|
+
private readonly browserControlHttpSupported;
|
|
600
619
|
private readonly resolveOperationAdmission;
|
|
601
620
|
private readonly adoptBackgroundCommand;
|
|
602
621
|
private readonly settleBackgroundCommand;
|
|
@@ -631,6 +650,7 @@ export declare class SelfhostedSandboxClient {
|
|
|
631
650
|
operationResourcePolicySupported?: boolean;
|
|
632
651
|
operationCpuQuotaSupported?: boolean;
|
|
633
652
|
transactionalFsWriteSupported?: boolean;
|
|
653
|
+
browserControlHttpSupported?: boolean;
|
|
634
654
|
resolveOperationAdmission?: () => Promise<SelfhostedOperationAdmission | null>;
|
|
635
655
|
adoptBackgroundCommand?: SelfhostedSessionDeps["adoptBackgroundCommand"];
|
|
636
656
|
settleBackgroundCommand?: SelfhostedSessionDeps["settleBackgroundCommand"];
|
|
@@ -711,6 +731,8 @@ export interface SelfhostedSessionBuild {
|
|
|
711
731
|
/** Exact live CPU enforcement capability paired with the initial snapshot. */
|
|
712
732
|
operationCpuQuotaSupported?: boolean;
|
|
713
733
|
transactionalFsWriteSupported?: boolean;
|
|
734
|
+
/** Exact live runner capability for in-process controller requests. */
|
|
735
|
+
browserControlHttpSupported?: boolean;
|
|
714
736
|
/** Live last-boundary operation admission resolver; see SelfhostedSessionDeps. */
|
|
715
737
|
resolveOperationAdmission?: () => Promise<SelfhostedOperationAdmission | null>;
|
|
716
738
|
adoptBackgroundCommand?: SelfhostedSessionDeps["adoptBackgroundCommand"];
|
package/dist/slack-rest-mcp.js
CHANGED
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
SlackRestMcpServer,
|
|
9
9
|
isOfficialSlackMcpConfig,
|
|
10
10
|
slackRestToolIsMutation
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-KIL4FGX5.js";
|
|
12
|
+
import "./chunk-PRZPGRIK.js";
|
|
13
|
+
import "./chunk-Y5THZH5N.js";
|
|
14
14
|
import "./chunk-4A4XTKKX.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-JYDG2A7H.js";
|
|
16
|
+
import "./chunk-MIAL5I4O.js";
|
|
17
|
+
import "./chunk-4XDJ4TJA.js";
|
|
18
|
+
import "./chunk-PZQUUBEB.js";
|
|
19
|
+
import "./chunk-ERD4ICVB.js";
|
|
20
20
|
import "./chunk-LJNCGUMB.js";
|
|
21
21
|
import "./chunk-PLDDJCW6.js";
|
|
22
22
|
export {
|
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
prepareAgentTools,
|
|
4
4
|
secretArgumentMode,
|
|
5
5
|
sessionMcpApprovalConnectionId
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-3DFH3ZHU.js";
|
|
7
|
+
import "./chunk-ETTCGPBT.js";
|
|
8
8
|
import "./chunk-4KFT2G3W.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-GWRBALGQ.js";
|
|
10
10
|
import "./chunk-5ULGMFKR.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-6FQ6KEII.js";
|
|
12
|
+
import "./chunk-KIL4FGX5.js";
|
|
13
|
+
import "./chunk-PRZPGRIK.js";
|
|
14
|
+
import "./chunk-Y5THZH5N.js";
|
|
15
15
|
import "./chunk-4A4XTKKX.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-JYDG2A7H.js";
|
|
17
|
+
import "./chunk-MIAL5I4O.js";
|
|
18
|
+
import "./chunk-4XDJ4TJA.js";
|
|
19
|
+
import "./chunk-PZQUUBEB.js";
|
|
20
|
+
import "./chunk-ERD4ICVB.js";
|
|
21
21
|
import "./chunk-LJNCGUMB.js";
|
|
22
22
|
import "./chunk-PLDDJCW6.js";
|
|
23
23
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -75,19 +75,19 @@
|
|
|
75
75
|
"@openai/agents": "0.14.3",
|
|
76
76
|
"@openai/agents-core": "0.14.3",
|
|
77
77
|
"@openai/agents-extensions": "0.14.3",
|
|
78
|
-
"@opengeni/agent-proto": "3.
|
|
79
|
-
"@opengeni/capabilities": "3.
|
|
80
|
-
"@opengeni/codemode": "3.
|
|
81
|
-
"@opengeni/codex": "3.
|
|
82
|
-
"@opengeni/config": "3.
|
|
83
|
-
"@opengeni/contracts": "3.
|
|
84
|
-
"@opengeni/interaction": "3.
|
|
85
|
-
"@opengeni/network": "3.
|
|
86
|
-
"@opengeni/observability": "3.
|
|
87
|
-
"@opengeni/sdk": "3.
|
|
88
|
-
"@opengeni/tool-gateway": "3.
|
|
89
|
-
"@opengeni/vaults": "3.
|
|
90
|
-
"@opengeni/xai-subscription": "3.
|
|
78
|
+
"@opengeni/agent-proto": "3.4.0",
|
|
79
|
+
"@opengeni/capabilities": "3.4.0",
|
|
80
|
+
"@opengeni/codemode": "3.4.0",
|
|
81
|
+
"@opengeni/codex": "3.4.0",
|
|
82
|
+
"@opengeni/config": "3.4.0",
|
|
83
|
+
"@opengeni/contracts": "3.4.0",
|
|
84
|
+
"@opengeni/interaction": "3.4.0",
|
|
85
|
+
"@opengeni/network": "3.4.0",
|
|
86
|
+
"@opengeni/observability": "3.4.0",
|
|
87
|
+
"@opengeni/sdk": "3.4.0",
|
|
88
|
+
"@opengeni/tool-gateway": "3.4.0",
|
|
89
|
+
"@opengeni/vaults": "3.4.0",
|
|
90
|
+
"@opengeni/xai-subscription": "3.4.0",
|
|
91
91
|
"debug": "4.4.3",
|
|
92
92
|
"modal": "0.9.0",
|
|
93
93
|
"openai": "6.47.0",
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// The browser tab an agent is working in.
|
|
2
|
+
//
|
|
3
|
+
// Browser tools may omit browserSessionId and targetId to keep working in the
|
|
4
|
+
// tab the agent used last, the way a person keeps using the tab in front of
|
|
5
|
+
// them. A step then costs a few output tokens instead of two identifiers, and
|
|
6
|
+
// output tokens dominate a browser step's model latency. The remembered tab
|
|
7
|
+
// lives for one turn attempt; a call that omits identifiers before any browser
|
|
8
|
+
// call in that attempt resolves this session's most recently used live
|
|
9
|
+
// browser and its selected tab.
|
|
10
|
+
|
|
11
|
+
export class BrowserTargetResolutionError extends Error {
|
|
12
|
+
constructor(
|
|
13
|
+
readonly code: "browser_session_required" | "browser_target_required",
|
|
14
|
+
message: string,
|
|
15
|
+
) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "BrowserTargetResolutionError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type CurrentBrowserTabDeps = {
|
|
22
|
+
/** This agent session's live BrowserSessions. */
|
|
23
|
+
listLiveSessions: () => Promise<ReadonlyArray<{ id: string; lastUsedAt: string }>>;
|
|
24
|
+
/** The tabs of one BrowserSession. */
|
|
25
|
+
listTargets: (
|
|
26
|
+
browserSessionId: string,
|
|
27
|
+
) => Promise<ReadonlyArray<{ id: string; kind: string; selected: boolean }>>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ResolvedBrowserSession = {
|
|
31
|
+
browserSessionId: string;
|
|
32
|
+
/** True when the identifier came from memory or lookup, not the caller. */
|
|
33
|
+
implicit: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ResolvedBrowserTarget = ResolvedBrowserSession & {
|
|
37
|
+
targetId: string;
|
|
38
|
+
implicitTarget: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export class CurrentBrowserTab {
|
|
42
|
+
private current: { browserSessionId: string; targetId: string | null } | null = null;
|
|
43
|
+
|
|
44
|
+
constructor(private readonly deps: CurrentBrowserTabDeps) {}
|
|
45
|
+
|
|
46
|
+
/** Record the session and, when known, the tab a successful call used. */
|
|
47
|
+
use(browserSessionId: string, targetId?: string | null): void {
|
|
48
|
+
if (targetId === undefined) {
|
|
49
|
+
if (this.current?.browserSessionId !== browserSessionId) {
|
|
50
|
+
this.current = { browserSessionId, targetId: null };
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.current = { browserSessionId, targetId };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
forgetTarget(browserSessionId: string, targetId: string): void {
|
|
58
|
+
if (this.current?.browserSessionId === browserSessionId && this.current.targetId === targetId) {
|
|
59
|
+
this.current = { browserSessionId, targetId: null };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
forgetSession(browserSessionId: string): void {
|
|
64
|
+
if (this.current?.browserSessionId === browserSessionId) this.current = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A call that relied on remembered identifiers failed: resolve afresh next time. */
|
|
68
|
+
failed(resolved: ResolvedBrowserSession & { implicitTarget?: boolean }): void {
|
|
69
|
+
if (resolved.implicit) this.forgetSession(resolved.browserSessionId);
|
|
70
|
+
else if (
|
|
71
|
+
resolved.implicitTarget &&
|
|
72
|
+
this.current?.browserSessionId === resolved.browserSessionId
|
|
73
|
+
) {
|
|
74
|
+
this.current = { browserSessionId: resolved.browserSessionId, targetId: null };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async session(browserSessionId: string | undefined): Promise<ResolvedBrowserSession> {
|
|
79
|
+
if (browserSessionId !== undefined) return { browserSessionId, implicit: false };
|
|
80
|
+
if (this.current) return { browserSessionId: this.current.browserSessionId, implicit: true };
|
|
81
|
+
const sessions = [...(await this.deps.listLiveSessions())].sort((left, right) =>
|
|
82
|
+
right.lastUsedAt.localeCompare(left.lastUsedAt),
|
|
83
|
+
);
|
|
84
|
+
const latest = sessions[0];
|
|
85
|
+
if (!latest) {
|
|
86
|
+
throw new BrowserTargetResolutionError(
|
|
87
|
+
"browser_session_required",
|
|
88
|
+
"This session has no open browser. Call browser_open first, or pass browserSessionId.",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return { browserSessionId: latest.id, implicit: true };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async target(value: {
|
|
95
|
+
browserSessionId?: string | undefined;
|
|
96
|
+
targetId?: string | undefined;
|
|
97
|
+
}): Promise<ResolvedBrowserTarget> {
|
|
98
|
+
const session = await this.session(value.browserSessionId);
|
|
99
|
+
if (value.targetId !== undefined) {
|
|
100
|
+
return { ...session, targetId: value.targetId, implicitTarget: false };
|
|
101
|
+
}
|
|
102
|
+
if (this.current?.browserSessionId === session.browserSessionId && this.current.targetId) {
|
|
103
|
+
return { ...session, targetId: this.current.targetId, implicitTarget: true };
|
|
104
|
+
}
|
|
105
|
+
const pages = (await this.deps.listTargets(session.browserSessionId)).filter(
|
|
106
|
+
(target) => target.kind === "page" || target.kind === "popup",
|
|
107
|
+
);
|
|
108
|
+
const chosen =
|
|
109
|
+
pages.find((target) => target.selected) ?? (pages.length === 1 ? pages[0] : null);
|
|
110
|
+
if (!chosen) {
|
|
111
|
+
throw new BrowserTargetResolutionError(
|
|
112
|
+
"browser_target_required",
|
|
113
|
+
pages.length === 0
|
|
114
|
+
? "This browser has no open tab. Open one with browser_tabs operation=open."
|
|
115
|
+
: `This browser has ${pages.length} tabs and none is selected. Pass targetId from browser_tabs operation=list.`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return { ...session, targetId: chosen.id, implicitTarget: true };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** An element ref passed where a locator is expected, as a bare string. */
|
|
123
|
+
export function expandRefShorthand(action: unknown): unknown {
|
|
124
|
+
if (!action || typeof action !== "object" || Array.isArray(action)) return action;
|
|
125
|
+
const record = action as Record<string, unknown>;
|
|
126
|
+
let expanded: Record<string, unknown> | null = null;
|
|
127
|
+
for (const field of ["locator", "from", "to"] as const) {
|
|
128
|
+
const value = record[field];
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
expanded ??= { ...record };
|
|
131
|
+
expanded[field] = { kind: "ref", ref: value };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (Array.isArray(record.actions)) {
|
|
135
|
+
expanded ??= { ...record };
|
|
136
|
+
expanded.actions = record.actions.map(expandRefShorthand);
|
|
137
|
+
}
|
|
138
|
+
return expanded ?? action;
|
|
139
|
+
}
|