lsd-pi 1.3.2 → 1.3.6
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/resources/extensions/browser-tools/tools/codegen.js +5 -5
- package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
- package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
- package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
- package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
- package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
- package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
- package/dist/resources/extensions/browser-tools/utils.js +1 -1
- package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
- package/dist/resources/extensions/slash-commands/fast.js +73 -0
- package/dist/resources/extensions/slash-commands/index.js +2 -0
- package/dist/resources/extensions/slash-commands/plan.js +37 -12
- package/dist/resources/extensions/subagent/background-job-manager.js +13 -0
- package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
- package/dist/resources/extensions/subagent/index.js +278 -626
- package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
- package/dist/resources/extensions/voice/index.js +96 -36
- package/dist/resources/extensions/voice/push-to-talk.js +26 -0
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +19 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +16 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.ts +32 -2
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +34 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -4
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts +8 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +2 -0
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +5 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +47 -4
- package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
- package/packages/pi-ai/src/providers/openai-responses.ts +26 -3
- package/packages/pi-ai/src/providers/simple-options.ts +2 -0
- package/packages/pi-ai/src/types.ts +5 -0
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -2
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js +12 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +26 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +7 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +26 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +18 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +49 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +197 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +97 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +35 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +4 -2
- package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +18 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -1
- package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
- package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +31 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +8 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +32 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1154 -1136
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +64 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +228 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +494 -398
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
- package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
- package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
- package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
- package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
- package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
- package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
- package/src/resources/extensions/browser-tools/utils.ts +1 -1
- package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
- package/src/resources/extensions/slash-commands/fast.ts +89 -0
- package/src/resources/extensions/slash-commands/index.ts +2 -0
- package/src/resources/extensions/slash-commands/plan.ts +42 -12
- package/src/resources/extensions/subagent/background-job-manager.ts +28 -0
- package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
- package/src/resources/extensions/subagent/index.ts +489 -799
- package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
- package/src/resources/extensions/voice/index.ts +308 -238
- package/src/resources/extensions/voice/push-to-talk.ts +42 -0
- package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
|
@@ -3,82 +3,41 @@ import { registryGetActive, registryListPages, registrySetActive, } from "../cor
|
|
|
3
3
|
import { getPageRegistry, getActiveFrame, setActiveFrame, } from "../state.js";
|
|
4
4
|
export function registerPageTools(pi, deps) {
|
|
5
5
|
// -------------------------------------------------------------------------
|
|
6
|
-
//
|
|
6
|
+
// browser_pages — list, switch, close tabs
|
|
7
7
|
// -------------------------------------------------------------------------
|
|
8
8
|
pi.registerTool({
|
|
9
|
-
name: "
|
|
10
|
-
label: "Browser
|
|
11
|
-
description: "
|
|
12
|
-
|
|
13
|
-
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
14
|
-
try {
|
|
15
|
-
await deps.ensureBrowser();
|
|
16
|
-
const pageRegistry = getPageRegistry();
|
|
17
|
-
for (const entry of pageRegistry.pages) {
|
|
18
|
-
try {
|
|
19
|
-
entry.title = await entry.page.title();
|
|
20
|
-
entry.url = entry.page.url();
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
// Page may have been closed
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const pages = registryListPages(pageRegistry);
|
|
27
|
-
if (pages.length === 0) {
|
|
28
|
-
return {
|
|
29
|
-
content: [{ type: "text", text: "No pages open." }],
|
|
30
|
-
details: { pages: [], count: 0 },
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
const lines = pages.map((p) => {
|
|
34
|
-
const active = p.isActive ? " ← active" : "";
|
|
35
|
-
const opener = p.opener !== null ? ` (opener: ${p.opener})` : "";
|
|
36
|
-
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${opener}${active}`;
|
|
37
|
-
});
|
|
38
|
-
return {
|
|
39
|
-
content: [{ type: "text", text: `${pages.length} page(s):\n${lines.join("\n")}` }],
|
|
40
|
-
details: { pages, count: pages.length },
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
return {
|
|
45
|
-
content: [{ type: "text", text: `List pages failed: ${err.message}` }],
|
|
46
|
-
details: { error: err.message },
|
|
47
|
-
isError: true,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
// -------------------------------------------------------------------------
|
|
53
|
-
// browser_switch_page
|
|
54
|
-
// -------------------------------------------------------------------------
|
|
55
|
-
pi.registerTool({
|
|
56
|
-
name: "browser_switch_page",
|
|
57
|
-
label: "Browser Switch Page",
|
|
58
|
-
description: "Switch the active browser page/tab by page ID. Use browser_list_pages to see available IDs. Clears any active frame selection.",
|
|
9
|
+
name: "browser_pages",
|
|
10
|
+
label: "Browser Pages",
|
|
11
|
+
description: "Manage browser tabs: list open pages, switch to a page by ID, or close a page by ID. " +
|
|
12
|
+
"Cannot close the last remaining page — use browser_close for that.",
|
|
59
13
|
parameters: Type.Object({
|
|
60
|
-
|
|
14
|
+
action: Type.Union([
|
|
15
|
+
Type.Literal("list"),
|
|
16
|
+
Type.Literal("switch"),
|
|
17
|
+
Type.Literal("close"),
|
|
18
|
+
], { description: "'list' — show all pages, 'switch' — activate a page by ID, 'close' — close a page by ID" }),
|
|
19
|
+
id: Type.Optional(Type.Number({ description: "Page ID (required for switch/close, from list action)." })),
|
|
61
20
|
}),
|
|
62
21
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
63
22
|
try {
|
|
64
23
|
await deps.ensureBrowser();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
24
|
+
if (params.action === "list") {
|
|
25
|
+
return await listPages();
|
|
26
|
+
}
|
|
27
|
+
else if (params.action === "switch") {
|
|
28
|
+
if (!params.id)
|
|
29
|
+
return { content: [{ type: "text", text: "Switch requires 'id' parameter." }], details: { error: "missing_id" }, isError: true };
|
|
30
|
+
return await switchPage(params.id);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (!params.id)
|
|
34
|
+
return { content: [{ type: "text", text: "Close requires 'id' parameter." }], details: { error: "missing_id" }, isError: true };
|
|
35
|
+
return await closePage(params.id);
|
|
36
|
+
}
|
|
78
37
|
}
|
|
79
38
|
catch (err) {
|
|
80
39
|
return {
|
|
81
|
-
content: [{ type: "text", text: `
|
|
40
|
+
content: [{ type: "text", text: `Pages action '${params.action}' failed: ${err.message}` }],
|
|
82
41
|
details: { error: err.message },
|
|
83
42
|
isError: true,
|
|
84
43
|
};
|
|
@@ -86,195 +45,184 @@ export function registerPageTools(pi, deps) {
|
|
|
86
45
|
},
|
|
87
46
|
});
|
|
88
47
|
// -------------------------------------------------------------------------
|
|
89
|
-
//
|
|
48
|
+
// browser_frames — list and select iframes
|
|
90
49
|
// -------------------------------------------------------------------------
|
|
91
50
|
pi.registerTool({
|
|
92
|
-
name: "
|
|
93
|
-
label: "Browser
|
|
94
|
-
description: "
|
|
51
|
+
name: "browser_frames",
|
|
52
|
+
label: "Browser Frames",
|
|
53
|
+
description: "Manage browser frames (iframes): list all frames in the active page, or select a frame to operate on. " +
|
|
54
|
+
"Pass action='select' with name, urlPattern, or index. Use name='main' to reset to main page.",
|
|
95
55
|
parameters: Type.Object({
|
|
96
|
-
|
|
56
|
+
action: Type.Union([
|
|
57
|
+
Type.Literal("list"),
|
|
58
|
+
Type.Literal("select"),
|
|
59
|
+
], { description: "'list' — show all frames, 'select' — activate a frame" }),
|
|
60
|
+
name: Type.Optional(Type.String({ description: "Frame name to select. Use 'main' to reset to main frame." })),
|
|
61
|
+
urlPattern: Type.Optional(Type.String({ description: "URL substring to match for frame selection." })),
|
|
62
|
+
index: Type.Optional(Type.Number({ description: "Frame index from list action." })),
|
|
97
63
|
}),
|
|
98
64
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
99
65
|
try {
|
|
100
66
|
await deps.ensureBrowser();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
content: [{ type: "text", text: `Cannot close the last remaining page. Use browser_close to close the entire browser.` }],
|
|
105
|
-
details: { error: "last_page", pageCount: pageRegistry.pages.length },
|
|
106
|
-
isError: true,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
const entry = pageRegistry.pages.find((e) => e.id === params.id);
|
|
110
|
-
if (!entry) {
|
|
111
|
-
const available = pageRegistry.pages.map((e) => e.id);
|
|
112
|
-
return {
|
|
113
|
-
content: [{ type: "text", text: `Page ${params.id} not found. Available page IDs: [${available.join(", ")}].` }],
|
|
114
|
-
details: { error: "not_found", available },
|
|
115
|
-
isError: true,
|
|
116
|
-
};
|
|
67
|
+
if (params.action === "list") {
|
|
68
|
+
return await listFrames();
|
|
117
69
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for (const remaining of pageRegistry.pages) {
|
|
121
|
-
try {
|
|
122
|
-
remaining.title = await remaining.page.title();
|
|
123
|
-
remaining.url = remaining.page.url();
|
|
124
|
-
}
|
|
125
|
-
catch { /* non-fatal — page may have been closed or navigated away */ }
|
|
70
|
+
else {
|
|
71
|
+
return await selectFrame(params);
|
|
126
72
|
}
|
|
127
|
-
const pages = registryListPages(pageRegistry);
|
|
128
|
-
const lines = pages.map((p) => {
|
|
129
|
-
const active = p.isActive ? " ← active" : "";
|
|
130
|
-
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${active}`;
|
|
131
|
-
});
|
|
132
|
-
return {
|
|
133
|
-
content: [{ type: "text", text: `Closed page ${params.id}. ${pages.length} page(s) remaining:\n${lines.join("\n")}` }],
|
|
134
|
-
details: { closedId: params.id, pages, count: pages.length },
|
|
135
|
-
};
|
|
136
73
|
}
|
|
137
74
|
catch (err) {
|
|
138
75
|
return {
|
|
139
|
-
content: [{ type: "text", text: `
|
|
76
|
+
content: [{ type: "text", text: `Frames action '${params.action}' failed: ${err.message}` }],
|
|
140
77
|
details: { error: err.message },
|
|
141
78
|
isError: true,
|
|
142
79
|
};
|
|
143
80
|
}
|
|
144
81
|
},
|
|
145
82
|
});
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
name: "browser_list_frames",
|
|
151
|
-
label: "Browser List Frames",
|
|
152
|
-
description: "List all frames in the active page, including the main frame and any iframes. Shows frame name, URL, and parent frame name. Use before browser_select_frame to identify available frames.",
|
|
153
|
-
parameters: Type.Object({}),
|
|
154
|
-
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
83
|
+
// ── page helpers ──
|
|
84
|
+
async function listPages() {
|
|
85
|
+
const pageRegistry = getPageRegistry();
|
|
86
|
+
for (const entry of pageRegistry.pages) {
|
|
155
87
|
try {
|
|
156
|
-
await
|
|
157
|
-
|
|
158
|
-
const frames = p.frames();
|
|
159
|
-
const mainFrame = p.mainFrame();
|
|
160
|
-
const activeFrame = getActiveFrame();
|
|
161
|
-
const frameList = frames.map((f, index) => {
|
|
162
|
-
const isMain = f === mainFrame;
|
|
163
|
-
const parentName = f.parentFrame()?.name() || (f.parentFrame() === mainFrame ? "main" : "");
|
|
164
|
-
return {
|
|
165
|
-
index,
|
|
166
|
-
name: f.name() || (isMain ? "main" : `(unnamed-${index})`),
|
|
167
|
-
url: f.url(),
|
|
168
|
-
isMain,
|
|
169
|
-
parentName: isMain ? null : (parentName || "main"),
|
|
170
|
-
isActive: f === activeFrame,
|
|
171
|
-
};
|
|
172
|
-
});
|
|
173
|
-
const lines = frameList.map((f) => {
|
|
174
|
-
const main = f.isMain ? " [main]" : "";
|
|
175
|
-
const active = f.isActive ? " ← selected" : "";
|
|
176
|
-
const parent = f.parentName ? ` (parent: ${f.parentName})` : "";
|
|
177
|
-
return ` [${f.index}] "${f.name}" — ${f.url}${main}${parent}${active}`;
|
|
178
|
-
});
|
|
179
|
-
const activeInfo = activeFrame ? `Active frame: "${activeFrame.name() || "(unnamed)"}"` : "No frame selected (operating on main page)";
|
|
180
|
-
return {
|
|
181
|
-
content: [{ type: "text", text: `${frameList.length} frame(s) in active page:\n${lines.join("\n")}\n\n${activeInfo}` }],
|
|
182
|
-
details: { frames: frameList, count: frameList.length, activeFrame: activeFrame?.name() ?? null },
|
|
183
|
-
};
|
|
88
|
+
entry.title = await entry.page.title();
|
|
89
|
+
entry.url = entry.page.url();
|
|
184
90
|
}
|
|
185
|
-
catch
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
91
|
+
catch { /* Page may have been closed */ }
|
|
92
|
+
}
|
|
93
|
+
const pages = registryListPages(pageRegistry);
|
|
94
|
+
if (pages.length === 0) {
|
|
95
|
+
return { content: [{ type: "text", text: "No pages open." }], details: { pages: [], count: 0 } };
|
|
96
|
+
}
|
|
97
|
+
const lines = pages.map((p) => {
|
|
98
|
+
const active = p.isActive ? " ← active" : "";
|
|
99
|
+
const opener = p.opener !== null ? ` (opener: ${p.opener})` : "";
|
|
100
|
+
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${opener}${active}`;
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: "text", text: `${pages.length} page(s):\n${lines.join("\n")}` }],
|
|
104
|
+
details: { pages, count: pages.length },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function switchPage(id) {
|
|
108
|
+
const pageRegistry = getPageRegistry();
|
|
109
|
+
registrySetActive(pageRegistry, id);
|
|
110
|
+
setActiveFrame(null);
|
|
111
|
+
const entry = registryGetActive(pageRegistry);
|
|
112
|
+
await entry.page.bringToFront();
|
|
113
|
+
const title = await entry.page.title().catch(() => "");
|
|
114
|
+
const url = entry.page.url();
|
|
115
|
+
entry.title = title;
|
|
116
|
+
entry.url = url;
|
|
117
|
+
return {
|
|
118
|
+
content: [{ type: "text", text: `Switched to page ${id}: ${title || "(untitled)"} — ${url}` }],
|
|
119
|
+
details: { id, title, url },
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async function closePage(id) {
|
|
123
|
+
const pageRegistry = getPageRegistry();
|
|
124
|
+
if (pageRegistry.pages.length <= 1) {
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text: "Cannot close the last remaining page. Use browser_close to close the entire browser." }],
|
|
127
|
+
details: { error: "last_page", pageCount: pageRegistry.pages.length },
|
|
128
|
+
isError: true,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const entry = pageRegistry.pages.find((e) => e.id === id);
|
|
132
|
+
if (!entry) {
|
|
133
|
+
const available = pageRegistry.pages.map((e) => e.id);
|
|
134
|
+
return {
|
|
135
|
+
content: [{ type: "text", text: `Page ${id} not found. Available page IDs: [${available.join(", ")}].` }],
|
|
136
|
+
details: { error: "not_found", available },
|
|
137
|
+
isError: true,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
await entry.page.close();
|
|
141
|
+
setActiveFrame(null);
|
|
142
|
+
for (const remaining of pageRegistry.pages) {
|
|
207
143
|
try {
|
|
208
|
-
await
|
|
209
|
-
|
|
210
|
-
const frames = p.frames();
|
|
211
|
-
if (params.name === "main" || params.name === "null" || params.name === null) {
|
|
212
|
-
setActiveFrame(null);
|
|
213
|
-
return {
|
|
214
|
-
content: [{ type: "text", text: "Reset to main page frame. Tools will operate on the main page." }],
|
|
215
|
-
details: { activeFrame: null },
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
if (params.name) {
|
|
219
|
-
const frame = frames.find((f) => f.name() === params.name);
|
|
220
|
-
if (!frame) {
|
|
221
|
-
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
222
|
-
return {
|
|
223
|
-
content: [{ type: "text", text: `Frame with name "${params.name}" not found.\nAvailable frames:\n ${available.join("\n ")}` }],
|
|
224
|
-
details: { error: "frame_not_found", available },
|
|
225
|
-
isError: true,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
setActiveFrame(frame);
|
|
229
|
-
return {
|
|
230
|
-
content: [{ type: "text", text: `Selected frame "${frame.name()}" — ${frame.url()}` }],
|
|
231
|
-
details: { name: frame.name(), url: frame.url() },
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
if (params.urlPattern) {
|
|
235
|
-
const frame = frames.find((f) => f.url().includes(params.urlPattern));
|
|
236
|
-
if (!frame) {
|
|
237
|
-
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
238
|
-
return {
|
|
239
|
-
content: [{ type: "text", text: `No frame URL matches "${params.urlPattern}".\nAvailable frames:\n ${available.join("\n ")}` }],
|
|
240
|
-
details: { error: "frame_not_found", available },
|
|
241
|
-
isError: true,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
setActiveFrame(frame);
|
|
245
|
-
return {
|
|
246
|
-
content: [{ type: "text", text: `Selected frame "${frame.name() || "(unnamed)"}" — ${frame.url()}` }],
|
|
247
|
-
details: { name: frame.name(), url: frame.url() },
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
if (params.index !== undefined) {
|
|
251
|
-
if (params.index < 0 || params.index >= frames.length) {
|
|
252
|
-
return {
|
|
253
|
-
content: [{ type: "text", text: `Frame index ${params.index} out of range. ${frames.length} frame(s) available (0-${frames.length - 1}).` }],
|
|
254
|
-
details: { error: "index_out_of_range", count: frames.length },
|
|
255
|
-
isError: true,
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
const frame = frames[params.index];
|
|
259
|
-
setActiveFrame(frame);
|
|
260
|
-
return {
|
|
261
|
-
content: [{ type: "text", text: `Selected frame [${params.index}] "${frame.name() || "(unnamed)"}" — ${frame.url()}` }],
|
|
262
|
-
details: { index: params.index, name: frame.name(), url: frame.url() },
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
return {
|
|
266
|
-
content: [{ type: "text", text: "Provide name, urlPattern, or index to select a frame. Use name='main' to reset to main frame." }],
|
|
267
|
-
details: { error: "no_criteria" },
|
|
268
|
-
isError: true,
|
|
269
|
-
};
|
|
144
|
+
remaining.title = await remaining.page.title();
|
|
145
|
+
remaining.url = remaining.page.url();
|
|
270
146
|
}
|
|
271
|
-
catch
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
147
|
+
catch { /* non-fatal */ }
|
|
148
|
+
}
|
|
149
|
+
const pages = registryListPages(pageRegistry);
|
|
150
|
+
const lines = pages.map((p) => {
|
|
151
|
+
const active = p.isActive ? " ← active" : "";
|
|
152
|
+
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${active}`;
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: "text", text: `Closed page ${id}. ${pages.length} page(s) remaining:\n${lines.join("\n")}` }],
|
|
156
|
+
details: { closedId: id, pages, count: pages.length },
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// ── frame helpers ──
|
|
160
|
+
async function listFrames() {
|
|
161
|
+
const p = deps.getActivePage();
|
|
162
|
+
const frames = p.frames();
|
|
163
|
+
const mainFrame = p.mainFrame();
|
|
164
|
+
const activeFrame = getActiveFrame();
|
|
165
|
+
const frameList = frames.map((f, index) => {
|
|
166
|
+
const isMain = f === mainFrame;
|
|
167
|
+
const parentName = f.parentFrame()?.name() || (f.parentFrame() === mainFrame ? "main" : "");
|
|
168
|
+
return {
|
|
169
|
+
index,
|
|
170
|
+
name: f.name() || (isMain ? "main" : `(unnamed-${index})`),
|
|
171
|
+
url: f.url(),
|
|
172
|
+
isMain,
|
|
173
|
+
parentName: isMain ? null : (parentName || "main"),
|
|
174
|
+
isActive: f === activeFrame,
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
const lines = frameList.map((f) => {
|
|
178
|
+
const main = f.isMain ? " [main]" : "";
|
|
179
|
+
const active = f.isActive ? " ← selected" : "";
|
|
180
|
+
const parent = f.parentName ? ` (parent: ${f.parentName})` : "";
|
|
181
|
+
return ` [${f.index}] "${f.name}" — ${f.url}${main}${parent}${active}`;
|
|
182
|
+
});
|
|
183
|
+
const activeInfo = activeFrame ? `Active frame: "${activeFrame.name() || "(unnamed)"}"` : "No frame selected (operating on main page)";
|
|
184
|
+
return {
|
|
185
|
+
content: [{ type: "text", text: `${frameList.length} frame(s) in active page:\n${lines.join("\n")}\n\n${activeInfo}` }],
|
|
186
|
+
details: { frames: frameList, count: frameList.length, activeFrame: activeFrame?.name() ?? null },
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async function selectFrame(params) {
|
|
190
|
+
const p = deps.getActivePage();
|
|
191
|
+
const frames = p.frames();
|
|
192
|
+
if (params.name === "main" || params.name === "null" || params.name === null) {
|
|
193
|
+
setActiveFrame(null);
|
|
194
|
+
return { content: [{ type: "text", text: "Reset to main page frame." }], details: { activeFrame: null } };
|
|
195
|
+
}
|
|
196
|
+
if (params.name) {
|
|
197
|
+
const frame = frames.find((f) => f.name() === params.name);
|
|
198
|
+
if (!frame) {
|
|
199
|
+
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
200
|
+
return { content: [{ type: "text", text: `Frame "${params.name}" not found.\n${available.join("\n ")}` }], details: { error: "not_found" }, isError: true };
|
|
277
201
|
}
|
|
278
|
-
|
|
279
|
-
|
|
202
|
+
setActiveFrame(frame);
|
|
203
|
+
return { content: [{ type: "text", text: `Selected frame "${frame.name()}" — ${frame.url()}` }], details: { name: frame.name(), url: frame.url() } };
|
|
204
|
+
}
|
|
205
|
+
if (params.urlPattern) {
|
|
206
|
+
const frame = frames.find((f) => f.url().includes(params.urlPattern));
|
|
207
|
+
if (!frame) {
|
|
208
|
+
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
209
|
+
return { content: [{ type: "text", text: `No frame URL matches "${params.urlPattern}".\n${available.join("\n ")}` }], details: { error: "not_found" }, isError: true };
|
|
210
|
+
}
|
|
211
|
+
setActiveFrame(frame);
|
|
212
|
+
return { content: [{ type: "text", text: `Selected frame "${frame.name() || "(unnamed)"}" — ${frame.url()}` }], details: { name: frame.name(), url: frame.url() } };
|
|
213
|
+
}
|
|
214
|
+
if (params.index !== undefined) {
|
|
215
|
+
if (params.index < 0 || params.index >= frames.length) {
|
|
216
|
+
return { content: [{ type: "text", text: `Frame index ${params.index} out of range (0-${frames.length - 1}).` }], details: { error: "index_out_of_range" }, isError: true };
|
|
217
|
+
}
|
|
218
|
+
const frame = frames[params.index];
|
|
219
|
+
setActiveFrame(frame);
|
|
220
|
+
return { content: [{ type: "text", text: `Selected frame [${params.index}] "${frame.name() || "(unnamed)"}" — ${frame.url()}` }], details: { index: params.index, name: frame.name(), url: frame.url() } };
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
content: [{ type: "text", text: "Provide name, urlPattern, or index to select a frame. Use name='main' to reset." }],
|
|
224
|
+
details: { error: "no_criteria" },
|
|
225
|
+
isError: true,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
280
228
|
}
|