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
|
@@ -14,84 +14,39 @@ import {
|
|
|
14
14
|
|
|
15
15
|
export function registerPageTools(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
16
16
|
// -------------------------------------------------------------------------
|
|
17
|
-
//
|
|
17
|
+
// browser_pages — list, switch, close tabs
|
|
18
18
|
// -------------------------------------------------------------------------
|
|
19
19
|
pi.registerTool({
|
|
20
|
-
name: "
|
|
21
|
-
label: "Browser
|
|
20
|
+
name: "browser_pages",
|
|
21
|
+
label: "Browser Pages",
|
|
22
22
|
description:
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
27
|
-
try {
|
|
28
|
-
await deps.ensureBrowser();
|
|
29
|
-
const pageRegistry = getPageRegistry();
|
|
30
|
-
for (const entry of pageRegistry.pages) {
|
|
31
|
-
try {
|
|
32
|
-
entry.title = await entry.page.title();
|
|
33
|
-
entry.url = entry.page.url();
|
|
34
|
-
} catch {
|
|
35
|
-
// Page may have been closed
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const pages = registryListPages(pageRegistry);
|
|
39
|
-
if (pages.length === 0) {
|
|
40
|
-
return {
|
|
41
|
-
content: [{ type: "text", text: "No pages open." }],
|
|
42
|
-
details: { pages: [], count: 0 },
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const lines = pages.map((p: any) => {
|
|
46
|
-
const active = p.isActive ? " ← active" : "";
|
|
47
|
-
const opener = p.opener !== null ? ` (opener: ${p.opener})` : "";
|
|
48
|
-
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${opener}${active}`;
|
|
49
|
-
});
|
|
50
|
-
return {
|
|
51
|
-
content: [{ type: "text", text: `${pages.length} page(s):\n${lines.join("\n")}` }],
|
|
52
|
-
details: { pages, count: pages.length },
|
|
53
|
-
};
|
|
54
|
-
} catch (err: any) {
|
|
55
|
-
return {
|
|
56
|
-
content: [{ type: "text", text: `List pages failed: ${err.message}` }],
|
|
57
|
-
details: { error: err.message },
|
|
58
|
-
isError: true,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// -------------------------------------------------------------------------
|
|
65
|
-
// browser_switch_page
|
|
66
|
-
// -------------------------------------------------------------------------
|
|
67
|
-
pi.registerTool({
|
|
68
|
-
name: "browser_switch_page",
|
|
69
|
-
label: "Browser Switch Page",
|
|
70
|
-
description:
|
|
71
|
-
"Switch the active browser page/tab by page ID. Use browser_list_pages to see available IDs. Clears any active frame selection.",
|
|
23
|
+
"Manage browser tabs: list open pages, switch to a page by ID, or close a page by ID. " +
|
|
24
|
+
"Cannot close the last remaining page — use browser_close for that.",
|
|
72
25
|
parameters: Type.Object({
|
|
73
|
-
|
|
26
|
+
action: Type.Union([
|
|
27
|
+
Type.Literal("list"),
|
|
28
|
+
Type.Literal("switch"),
|
|
29
|
+
Type.Literal("close"),
|
|
30
|
+
], { description: "'list' — show all pages, 'switch' — activate a page by ID, 'close' — close a page by ID" }),
|
|
31
|
+
id: Type.Optional(Type.Number({ description: "Page ID (required for switch/close, from list action)." })),
|
|
74
32
|
}),
|
|
75
33
|
|
|
76
34
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
77
35
|
try {
|
|
78
36
|
await deps.ensureBrowser();
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
content: [{ type: "text", text: `Switched to page ${params.id}: ${title || "(untitled)"} — ${url}` }],
|
|
90
|
-
details: { id: params.id, title, url },
|
|
91
|
-
};
|
|
37
|
+
|
|
38
|
+
if (params.action === "list") {
|
|
39
|
+
return await listPages();
|
|
40
|
+
} else if (params.action === "switch") {
|
|
41
|
+
if (!params.id) return { content: [{ type: "text" as const, text: "Switch requires 'id' parameter." }], details: { error: "missing_id" }, isError: true };
|
|
42
|
+
return await switchPage(params.id);
|
|
43
|
+
} else {
|
|
44
|
+
if (!params.id) return { content: [{ type: "text" as const, text: "Close requires 'id' parameter." }], details: { error: "missing_id" }, isError: true };
|
|
45
|
+
return await closePage(params.id);
|
|
46
|
+
}
|
|
92
47
|
} catch (err: any) {
|
|
93
48
|
return {
|
|
94
|
-
content: [{ type: "text", text: `
|
|
49
|
+
content: [{ type: "text" as const, text: `Pages action '${params.action}' failed: ${err.message}` }],
|
|
95
50
|
details: { error: err.message },
|
|
96
51
|
isError: true,
|
|
97
52
|
};
|
|
@@ -100,57 +55,36 @@ export function registerPageTools(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
100
55
|
});
|
|
101
56
|
|
|
102
57
|
// -------------------------------------------------------------------------
|
|
103
|
-
//
|
|
58
|
+
// browser_frames — list and select iframes
|
|
104
59
|
// -------------------------------------------------------------------------
|
|
105
60
|
pi.registerTool({
|
|
106
|
-
name: "
|
|
107
|
-
label: "Browser
|
|
61
|
+
name: "browser_frames",
|
|
62
|
+
label: "Browser Frames",
|
|
108
63
|
description:
|
|
109
|
-
"
|
|
64
|
+
"Manage browser frames (iframes): list all frames in the active page, or select a frame to operate on. " +
|
|
65
|
+
"Pass action='select' with name, urlPattern, or index. Use name='main' to reset to main page.",
|
|
110
66
|
parameters: Type.Object({
|
|
111
|
-
|
|
67
|
+
action: Type.Union([
|
|
68
|
+
Type.Literal("list"),
|
|
69
|
+
Type.Literal("select"),
|
|
70
|
+
], { description: "'list' — show all frames, 'select' — activate a frame" }),
|
|
71
|
+
name: Type.Optional(Type.String({ description: "Frame name to select. Use 'main' to reset to main frame." })),
|
|
72
|
+
urlPattern: Type.Optional(Type.String({ description: "URL substring to match for frame selection." })),
|
|
73
|
+
index: Type.Optional(Type.Number({ description: "Frame index from list action." })),
|
|
112
74
|
}),
|
|
113
75
|
|
|
114
76
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
115
77
|
try {
|
|
116
78
|
await deps.ensureBrowser();
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
return
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
isError: true,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
const entry = pageRegistry.pages.find((e: any) => e.id === params.id);
|
|
126
|
-
if (!entry) {
|
|
127
|
-
const available = pageRegistry.pages.map((e: any) => e.id);
|
|
128
|
-
return {
|
|
129
|
-
content: [{ type: "text", text: `Page ${params.id} not found. Available page IDs: [${available.join(", ")}].` }],
|
|
130
|
-
details: { error: "not_found", available },
|
|
131
|
-
isError: true,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
await entry.page.close();
|
|
135
|
-
setActiveFrame(null);
|
|
136
|
-
for (const remaining of pageRegistry.pages) {
|
|
137
|
-
try {
|
|
138
|
-
remaining.title = await remaining.page.title();
|
|
139
|
-
remaining.url = remaining.page.url();
|
|
140
|
-
} catch { /* non-fatal — page may have been closed or navigated away */ }
|
|
79
|
+
|
|
80
|
+
if (params.action === "list") {
|
|
81
|
+
return await listFrames();
|
|
82
|
+
} else {
|
|
83
|
+
return await selectFrame(params);
|
|
141
84
|
}
|
|
142
|
-
const pages = registryListPages(pageRegistry);
|
|
143
|
-
const lines = pages.map((p: any) => {
|
|
144
|
-
const active = p.isActive ? " ← active" : "";
|
|
145
|
-
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${active}`;
|
|
146
|
-
});
|
|
147
|
-
return {
|
|
148
|
-
content: [{ type: "text", text: `Closed page ${params.id}. ${pages.length} page(s) remaining:\n${lines.join("\n")}` }],
|
|
149
|
-
details: { closedId: params.id, pages, count: pages.length },
|
|
150
|
-
};
|
|
151
85
|
} catch (err: any) {
|
|
152
86
|
return {
|
|
153
|
-
content: [{ type: "text", text: `
|
|
87
|
+
content: [{ type: "text" as const, text: `Frames action '${params.action}' failed: ${err.message}` }],
|
|
154
88
|
details: { error: err.message },
|
|
155
89
|
isError: true,
|
|
156
90
|
};
|
|
@@ -158,146 +92,158 @@ export function registerPageTools(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
158
92
|
},
|
|
159
93
|
});
|
|
160
94
|
|
|
161
|
-
//
|
|
162
|
-
// browser_list_frames
|
|
163
|
-
// -------------------------------------------------------------------------
|
|
164
|
-
pi.registerTool({
|
|
165
|
-
name: "browser_list_frames",
|
|
166
|
-
label: "Browser List Frames",
|
|
167
|
-
description:
|
|
168
|
-
"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.",
|
|
169
|
-
parameters: Type.Object({}),
|
|
95
|
+
// ── page helpers ──
|
|
170
96
|
|
|
171
|
-
|
|
97
|
+
async function listPages() {
|
|
98
|
+
const pageRegistry = getPageRegistry();
|
|
99
|
+
for (const entry of pageRegistry.pages) {
|
|
172
100
|
try {
|
|
173
|
-
await
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const main = f.isMain ? " [main]" : "";
|
|
192
|
-
const active = f.isActive ? " ← selected" : "";
|
|
193
|
-
const parent = f.parentName ? ` (parent: ${f.parentName})` : "";
|
|
194
|
-
return ` [${f.index}] "${f.name}" — ${f.url}${main}${parent}${active}`;
|
|
195
|
-
});
|
|
196
|
-
const activeInfo = activeFrame ? `Active frame: "${activeFrame.name() || "(unnamed)"}"` : "No frame selected (operating on main page)";
|
|
197
|
-
return {
|
|
198
|
-
content: [{ type: "text", text: `${frameList.length} frame(s) in active page:\n${lines.join("\n")}\n\n${activeInfo}` }],
|
|
199
|
-
details: { frames: frameList, count: frameList.length, activeFrame: activeFrame?.name() ?? null },
|
|
200
|
-
};
|
|
201
|
-
} catch (err: any) {
|
|
202
|
-
return {
|
|
203
|
-
content: [{ type: "text", text: `List frames failed: ${err.message}` }],
|
|
204
|
-
details: { error: err.message },
|
|
205
|
-
isError: true,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
});
|
|
101
|
+
entry.title = await entry.page.title();
|
|
102
|
+
entry.url = entry.page.url();
|
|
103
|
+
} catch { /* Page may have been closed */ }
|
|
104
|
+
}
|
|
105
|
+
const pages = registryListPages(pageRegistry);
|
|
106
|
+
if (pages.length === 0) {
|
|
107
|
+
return { content: [{ type: "text" as const, text: "No pages open." }], details: { pages: [], count: 0 } };
|
|
108
|
+
}
|
|
109
|
+
const lines = pages.map((p: any) => {
|
|
110
|
+
const active = p.isActive ? " ← active" : "";
|
|
111
|
+
const opener = p.opener !== null ? ` (opener: ${p.opener})` : "";
|
|
112
|
+
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${opener}${active}`;
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text" as const, text: `${pages.length} page(s):\n${lines.join("\n")}` }],
|
|
116
|
+
details: { pages, count: pages.length },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
210
119
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
120
|
+
async function switchPage(id: number) {
|
|
121
|
+
const pageRegistry = getPageRegistry();
|
|
122
|
+
registrySetActive(pageRegistry, id);
|
|
123
|
+
setActiveFrame(null);
|
|
124
|
+
const entry = registryGetActive(pageRegistry);
|
|
125
|
+
await entry.page.bringToFront();
|
|
126
|
+
const title = await entry.page.title().catch(() => "");
|
|
127
|
+
const url = entry.page.url();
|
|
128
|
+
entry.title = title;
|
|
129
|
+
entry.url = url;
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: "text" as const, text: `Switched to page ${id}: ${title || "(untitled)"} — ${url}` }],
|
|
132
|
+
details: { id, title, url },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
224
135
|
|
|
225
|
-
|
|
136
|
+
async function closePage(id: number) {
|
|
137
|
+
const pageRegistry = getPageRegistry();
|
|
138
|
+
if (pageRegistry.pages.length <= 1) {
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: "text" as const, text: "Cannot close the last remaining page. Use browser_close to close the entire browser." }],
|
|
141
|
+
details: { error: "last_page", pageCount: pageRegistry.pages.length },
|
|
142
|
+
isError: true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const entry = pageRegistry.pages.find((e: any) => e.id === id);
|
|
146
|
+
if (!entry) {
|
|
147
|
+
const available = pageRegistry.pages.map((e: any) => e.id);
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: "text" as const, text: `Page ${id} not found. Available page IDs: [${available.join(", ")}].` }],
|
|
150
|
+
details: { error: "not_found", available },
|
|
151
|
+
isError: true,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
await entry.page.close();
|
|
155
|
+
setActiveFrame(null);
|
|
156
|
+
for (const remaining of pageRegistry.pages) {
|
|
226
157
|
try {
|
|
227
|
-
await
|
|
228
|
-
|
|
229
|
-
|
|
158
|
+
remaining.title = await remaining.page.title();
|
|
159
|
+
remaining.url = remaining.page.url();
|
|
160
|
+
} catch { /* non-fatal */ }
|
|
161
|
+
}
|
|
162
|
+
const pages = registryListPages(pageRegistry);
|
|
163
|
+
const lines = pages.map((p: any) => {
|
|
164
|
+
const active = p.isActive ? " ← active" : "";
|
|
165
|
+
return ` [${p.id}] ${p.title || "(untitled)"} — ${p.url}${active}`;
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text" as const, text: `Closed page ${id}. ${pages.length} page(s) remaining:\n${lines.join("\n")}` }],
|
|
169
|
+
details: { closedId: id, pages, count: pages.length },
|
|
170
|
+
};
|
|
171
|
+
}
|
|
230
172
|
|
|
231
|
-
|
|
232
|
-
setActiveFrame(null);
|
|
233
|
-
return {
|
|
234
|
-
content: [{ type: "text", text: "Reset to main page frame. Tools will operate on the main page." }],
|
|
235
|
-
details: { activeFrame: null },
|
|
236
|
-
};
|
|
237
|
-
}
|
|
173
|
+
// ── frame helpers ──
|
|
238
174
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
175
|
+
async function listFrames() {
|
|
176
|
+
const p = deps.getActivePage();
|
|
177
|
+
const frames = p.frames();
|
|
178
|
+
const mainFrame = p.mainFrame();
|
|
179
|
+
const activeFrame = getActiveFrame();
|
|
180
|
+
const frameList = frames.map((f, index) => {
|
|
181
|
+
const isMain = f === mainFrame;
|
|
182
|
+
const parentName = f.parentFrame()?.name() || (f.parentFrame() === mainFrame ? "main" : "");
|
|
183
|
+
return {
|
|
184
|
+
index,
|
|
185
|
+
name: f.name() || (isMain ? "main" : `(unnamed-${index})`),
|
|
186
|
+
url: f.url(),
|
|
187
|
+
isMain,
|
|
188
|
+
parentName: isMain ? null : (parentName || "main"),
|
|
189
|
+
isActive: f === activeFrame,
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
const lines = frameList.map((f) => {
|
|
193
|
+
const main = f.isMain ? " [main]" : "";
|
|
194
|
+
const active = f.isActive ? " ← selected" : "";
|
|
195
|
+
const parent = f.parentName ? ` (parent: ${f.parentName})` : "";
|
|
196
|
+
return ` [${f.index}] "${f.name}" — ${f.url}${main}${parent}${active}`;
|
|
197
|
+
});
|
|
198
|
+
const activeInfo = activeFrame ? `Active frame: "${activeFrame.name() || "(unnamed)"}"` : "No frame selected (operating on main page)";
|
|
199
|
+
return {
|
|
200
|
+
content: [{ type: "text" as const, text: `${frameList.length} frame(s) in active page:\n${lines.join("\n")}\n\n${activeInfo}` }],
|
|
201
|
+
details: { frames: frameList, count: frameList.length, activeFrame: activeFrame?.name() ?? null },
|
|
202
|
+
};
|
|
203
|
+
}
|
|
255
204
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
260
|
-
return {
|
|
261
|
-
content: [{ type: "text", text: `No frame URL matches "${params.urlPattern}".\nAvailable frames:\n ${available.join("\n ")}` }],
|
|
262
|
-
details: { error: "frame_not_found", available },
|
|
263
|
-
isError: true,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
setActiveFrame(frame);
|
|
267
|
-
return {
|
|
268
|
-
content: [{ type: "text", text: `Selected frame "${frame.name() || "(unnamed)"}" — ${frame.url()}` }],
|
|
269
|
-
details: { name: frame.name(), url: frame.url() },
|
|
270
|
-
};
|
|
271
|
-
}
|
|
205
|
+
async function selectFrame(params: { name?: string; urlPattern?: string; index?: number }) {
|
|
206
|
+
const p = deps.getActivePage();
|
|
207
|
+
const frames = p.frames();
|
|
272
208
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
details: { error: "index_out_of_range", count: frames.length },
|
|
278
|
-
isError: true,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
const frame = frames[params.index];
|
|
282
|
-
setActiveFrame(frame);
|
|
283
|
-
return {
|
|
284
|
-
content: [{ type: "text", text: `Selected frame [${params.index}] "${frame.name() || "(unnamed)"}" — ${frame.url()}` }],
|
|
285
|
-
details: { index: params.index, name: frame.name(), url: frame.url() },
|
|
286
|
-
};
|
|
287
|
-
}
|
|
209
|
+
if (params.name === "main" || params.name === "null" || params.name === null) {
|
|
210
|
+
setActiveFrame(null);
|
|
211
|
+
return { content: [{ type: "text" as const, text: "Reset to main page frame." }], details: { activeFrame: null } };
|
|
212
|
+
}
|
|
288
213
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
};
|
|
294
|
-
} catch (err: any) {
|
|
295
|
-
return {
|
|
296
|
-
content: [{ type: "text", text: `Select frame failed: ${err.message}` }],
|
|
297
|
-
details: { error: err.message },
|
|
298
|
-
isError: true,
|
|
299
|
-
};
|
|
214
|
+
if (params.name) {
|
|
215
|
+
const frame = frames.find((f) => f.name() === params.name);
|
|
216
|
+
if (!frame) {
|
|
217
|
+
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
218
|
+
return { content: [{ type: "text" as const, text: `Frame "${params.name}" not found.\n${available.join("\n ")}` }], details: { error: "not_found" }, isError: true };
|
|
300
219
|
}
|
|
301
|
-
|
|
302
|
-
|
|
220
|
+
setActiveFrame(frame);
|
|
221
|
+
return { content: [{ type: "text" as const, text: `Selected frame "${frame.name()}" — ${frame.url()}` }], details: { name: frame.name(), url: frame.url() } };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (params.urlPattern) {
|
|
225
|
+
const frame = frames.find((f) => f.url().includes(params.urlPattern!));
|
|
226
|
+
if (!frame) {
|
|
227
|
+
const available = frames.map((f, i) => `[${i}] "${f.name() || "(unnamed)"}" — ${f.url()}`);
|
|
228
|
+
return { content: [{ type: "text" as const, text: `No frame URL matches "${params.urlPattern}".\n${available.join("\n ")}` }], details: { error: "not_found" }, isError: true };
|
|
229
|
+
}
|
|
230
|
+
setActiveFrame(frame);
|
|
231
|
+
return { content: [{ type: "text" as const, text: `Selected frame "${frame.name() || "(unnamed)"}" — ${frame.url()}` }], details: { name: frame.name(), url: frame.url() } };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (params.index !== undefined) {
|
|
235
|
+
if (params.index < 0 || params.index >= frames.length) {
|
|
236
|
+
return { content: [{ type: "text" as const, text: `Frame index ${params.index} out of range (0-${frames.length - 1}).` }], details: { error: "index_out_of_range" }, isError: true };
|
|
237
|
+
}
|
|
238
|
+
const frame = frames[params.index];
|
|
239
|
+
setActiveFrame(frame);
|
|
240
|
+
return { content: [{ type: "text" as const, text: `Selected frame [${params.index}] "${frame.name() || "(unnamed)"}" — ${frame.url()}` }], details: { index: params.index, name: frame.name(), url: frame.url() } };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: "text" as const, text: "Provide name, urlPattern, or index to select a frame. Use name='main' to reset." }],
|
|
245
|
+
details: { error: "no_criteria" },
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
303
249
|
}
|