lsd-pi 1.3.2 → 1.3.7
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/cli.js +2 -1
- package/dist/lsd-settings-manager.d.ts +2 -0
- package/dist/lsd-settings-manager.js +5 -0
- package/dist/resource-loader.js +33 -3
- 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/cache-timer/index.js +3 -2
- 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/dist/welcome-screen.js +2 -2
- 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.collapse-tool-calls.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js +35 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +12 -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 +24 -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/core/tools/edit-diff.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +21 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- 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 +34 -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/diff.d.ts +7 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +86 -28
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +4 -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 +23 -10
- 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 +8 -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 +52 -6
- 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 +19 -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 +127 -14
- 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 +14 -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 +93 -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 +328 -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 +123 -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/extension-ui-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-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 +4 -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 +9 -1
- 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 +103 -23
- 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/dist/modes/interactive/theme/themes.js +4 -4
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.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.collapse-tool-calls.test.ts +46 -0
- 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 +36 -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/core/tools/edit-diff.test.ts +20 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +26 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +41 -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/diff.ts +105 -28
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +21 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1262 -1138
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +120 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +396 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +530 -398
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
- 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 +4 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +109 -23
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -4
- package/packages/pi-tui/dist/components/editor.js +3 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +3 -3
- package/pkg/dist/modes/interactive/theme/themes.js +4 -4
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -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/cache-timer/index.ts +3 -2
- 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,93 +3,36 @@ import { Type } from "@sinclair/typebox";
|
|
|
3
3
|
import type { ToolDeps } from "../state.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* State persistence
|
|
6
|
+
* State persistence — save/restore cookies, localStorage, sessionStorage.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const STATE_DIR = ".gsd/browser-state";
|
|
10
10
|
|
|
11
11
|
export function registerStatePersistenceTools(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
12
|
-
// -------------------------------------------------------------------------
|
|
13
|
-
// browser_save_state
|
|
14
|
-
// -------------------------------------------------------------------------
|
|
15
12
|
pi.registerTool({
|
|
16
|
-
name: "
|
|
17
|
-
label: "Browser
|
|
13
|
+
name: "browser_state",
|
|
14
|
+
label: "Browser State",
|
|
18
15
|
description:
|
|
19
|
-
"Save cookies, localStorage,
|
|
20
|
-
"State files
|
|
21
|
-
"Never displays secret values in output.",
|
|
16
|
+
"Save or restore browser state (cookies, localStorage, sessionStorage) to persist sessions across browser restarts. " +
|
|
17
|
+
"State files written to .gsd/browser-state/ (should be gitignored).",
|
|
22
18
|
parameters: Type.Object({
|
|
23
|
-
|
|
24
|
-
Type.
|
|
25
|
-
|
|
19
|
+
action: Type.Union([
|
|
20
|
+
Type.Literal("save"),
|
|
21
|
+
Type.Literal("restore"),
|
|
22
|
+
], { description: "'save' — persist current state, 'restore' — load previously saved state" }),
|
|
23
|
+
name: Type.Optional(Type.String({ description: "State file name (default: 'default'). Used as filename stem." })),
|
|
26
24
|
}),
|
|
27
25
|
|
|
28
26
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
29
27
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const path = await import("node:path");
|
|
35
|
-
const stateDir = path.resolve(process.cwd(), STATE_DIR);
|
|
36
|
-
await mkdir(stateDir, { recursive: true });
|
|
37
|
-
|
|
38
|
-
// 1. Playwright storageState: cookies + localStorage
|
|
39
|
-
const storageState = await ctx.storageState();
|
|
40
|
-
|
|
41
|
-
// 2. sessionStorage: must be extracted per-origin via page.evaluate
|
|
42
|
-
const sessionStorageData: Record<string, Record<string, string>> = {};
|
|
43
|
-
try {
|
|
44
|
-
const origin = new URL(p.url()).origin;
|
|
45
|
-
const ssData = await p.evaluate(() => {
|
|
46
|
-
const data: Record<string, string> = {};
|
|
47
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
48
|
-
const key = sessionStorage.key(i);
|
|
49
|
-
if (key) data[key] = sessionStorage.getItem(key) ?? "";
|
|
50
|
-
}
|
|
51
|
-
return data;
|
|
52
|
-
});
|
|
53
|
-
if (Object.keys(ssData).length > 0) {
|
|
54
|
-
sessionStorageData[origin] = ssData;
|
|
55
|
-
}
|
|
56
|
-
} catch {
|
|
57
|
-
// Page may not have a valid origin (about:blank, etc.)
|
|
28
|
+
if (params.action === "save") {
|
|
29
|
+
return await saveState(deps, params.name ?? "default");
|
|
30
|
+
} else {
|
|
31
|
+
return await restoreState(deps, params.name ?? "default");
|
|
58
32
|
}
|
|
59
|
-
|
|
60
|
-
const combined = {
|
|
61
|
-
storageState,
|
|
62
|
-
sessionStorage: sessionStorageData,
|
|
63
|
-
savedAt: new Date().toISOString(),
|
|
64
|
-
url: p.url(),
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const filePath = path.join(stateDir, `${name}.json`);
|
|
68
|
-
await writeFile(filePath, JSON.stringify(combined, null, 2));
|
|
69
|
-
|
|
70
|
-
// Ensure .gitignore covers the state dir
|
|
71
|
-
const gitignorePath = path.resolve(process.cwd(), STATE_DIR, ".gitignore");
|
|
72
|
-
await writeFile(gitignorePath, "*\n!.gitignore\n").catch(() => { /* best-effort — .gitignore may already exist or dir may be read-only */ });
|
|
73
|
-
|
|
74
|
-
const cookieCount = storageState.cookies?.length ?? 0;
|
|
75
|
-
const localStorageOrigins = storageState.origins?.length ?? 0;
|
|
76
|
-
const sessionStorageOrigins = Object.keys(sessionStorageData).length;
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
content: [{
|
|
80
|
-
type: "text",
|
|
81
|
-
text: `State saved: ${filePath}\nCookies: ${cookieCount}\nlocalStorage origins: ${localStorageOrigins}\nsessionStorage origins: ${sessionStorageOrigins}`,
|
|
82
|
-
}],
|
|
83
|
-
details: {
|
|
84
|
-
path: filePath,
|
|
85
|
-
cookieCount,
|
|
86
|
-
localStorageOrigins,
|
|
87
|
-
sessionStorageOrigins,
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
33
|
} catch (err: any) {
|
|
91
34
|
return {
|
|
92
|
-
content: [{ type: "text", text: `
|
|
35
|
+
content: [{ type: "text" as const, text: `State '${params.action}' failed: ${err.message}` }],
|
|
93
36
|
details: { error: err.message },
|
|
94
37
|
isError: true,
|
|
95
38
|
};
|
|
@@ -97,106 +40,85 @@ export function registerStatePersistenceTools(pi: ExtensionAPI, deps: ToolDeps):
|
|
|
97
40
|
},
|
|
98
41
|
});
|
|
99
42
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const { context: ctx, page: p } = await deps.ensureBrowser();
|
|
119
|
-
const name = deps.sanitizeArtifactName(params.name ?? "default", "default");
|
|
120
|
-
|
|
121
|
-
const { readFile } = await import("node:fs/promises");
|
|
122
|
-
const path = await import("node:path");
|
|
123
|
-
const filePath = path.join(process.cwd(), STATE_DIR, `${name}.json`);
|
|
124
|
-
|
|
125
|
-
let raw: string;
|
|
126
|
-
try {
|
|
127
|
-
raw = await readFile(filePath, "utf-8");
|
|
128
|
-
} catch {
|
|
129
|
-
return {
|
|
130
|
-
content: [{ type: "text", text: `State file not found: ${filePath}` }],
|
|
131
|
-
details: { error: "file_not_found", path: filePath },
|
|
132
|
-
isError: true,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const combined = JSON.parse(raw);
|
|
137
|
-
const storageState = combined.storageState;
|
|
138
|
-
const sessionStorageData: Record<string, Record<string, string>> = combined.sessionStorage ?? {};
|
|
139
|
-
|
|
140
|
-
// 1. Restore cookies
|
|
141
|
-
let cookieCount = 0;
|
|
142
|
-
if (storageState?.cookies?.length) {
|
|
143
|
-
await ctx.addCookies(storageState.cookies);
|
|
144
|
-
cookieCount = storageState.cookies.length;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// 2. Restore localStorage via page.evaluate
|
|
148
|
-
let localStorageOrigins = 0;
|
|
149
|
-
if (storageState?.origins?.length) {
|
|
150
|
-
for (const origin of storageState.origins) {
|
|
151
|
-
try {
|
|
152
|
-
await p.evaluate((items: Array<{ name: string; value: string }>) => {
|
|
153
|
-
for (const { name, value } of items) {
|
|
154
|
-
localStorage.setItem(name, value);
|
|
155
|
-
}
|
|
156
|
-
}, origin.localStorage ?? []);
|
|
157
|
-
localStorageOrigins++;
|
|
158
|
-
} catch {
|
|
159
|
-
// Origin mismatch — localStorage can only be set on matching origin
|
|
160
|
-
}
|
|
161
|
-
}
|
|
43
|
+
async function saveState(deps: ToolDeps, name: string) {
|
|
44
|
+
const { context: ctx, page: p } = await deps.ensureBrowser();
|
|
45
|
+
name = deps.sanitizeArtifactName(name, "default");
|
|
46
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
47
|
+
const path = await import("node:path");
|
|
48
|
+
const stateDir = path.resolve(process.cwd(), STATE_DIR);
|
|
49
|
+
await mkdir(stateDir, { recursive: true });
|
|
50
|
+
|
|
51
|
+
const storageState = await ctx.storageState();
|
|
52
|
+
const sessionStorageData: Record<string, Record<string, string>> = {};
|
|
53
|
+
try {
|
|
54
|
+
const origin = new URL(p.url()).origin;
|
|
55
|
+
const ssData = await p.evaluate(() => {
|
|
56
|
+
const data: Record<string, string> = {};
|
|
57
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
58
|
+
const key = sessionStorage.key(i);
|
|
59
|
+
if (key) data[key] = sessionStorage.getItem(key) ?? "";
|
|
162
60
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
61
|
+
return data;
|
|
62
|
+
});
|
|
63
|
+
if (Object.keys(ssData).length > 0) sessionStorageData[origin] = ssData;
|
|
64
|
+
} catch { /* Page may not have a valid origin */ }
|
|
65
|
+
|
|
66
|
+
const combined = { storageState, sessionStorage: sessionStorageData, savedAt: new Date().toISOString(), url: p.url() };
|
|
67
|
+
const filePath = path.join(stateDir, `${name}.json`);
|
|
68
|
+
await writeFile(filePath, JSON.stringify(combined, null, 2));
|
|
69
|
+
|
|
70
|
+
const gitignorePath = path.resolve(process.cwd(), STATE_DIR, ".gitignore");
|
|
71
|
+
await writeFile(gitignorePath, "*\n!.gitignore\n").catch(() => { /* best-effort */ });
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: "text" as const, text: `State saved: ${filePath}\nCookies: ${storageState.cookies?.length ?? 0}\nlocalStorage origins: ${storageState.origins?.length ?? 0}\nsessionStorage origins: ${Object.keys(sessionStorageData).length}` }],
|
|
75
|
+
details: { path: filePath, cookieCount: storageState.cookies?.length ?? 0, localStorageOrigins: storageState.origins?.length ?? 0, sessionStorageOrigins: Object.keys(sessionStorageData).length },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function restoreState(deps: ToolDeps, name: string) {
|
|
80
|
+
const { context: ctx, page: p } = await deps.ensureBrowser();
|
|
81
|
+
name = deps.sanitizeArtifactName(name, "default");
|
|
82
|
+
const { readFile } = await import("node:fs/promises");
|
|
83
|
+
const path = await import("node:path");
|
|
84
|
+
const filePath = path.join(process.cwd(), STATE_DIR, `${name}.json`);
|
|
85
|
+
|
|
86
|
+
let raw: string;
|
|
87
|
+
try { raw = await readFile(filePath, "utf-8"); } catch {
|
|
88
|
+
return { content: [{ type: "text" as const, text: `State file not found: ${filePath}` }], details: { error: "file_not_found", path: filePath }, isError: true };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const combined = JSON.parse(raw);
|
|
92
|
+
const storageState = combined.storageState;
|
|
93
|
+
const sessionStorageData: Record<string, Record<string, string>> = combined.sessionStorage ?? {};
|
|
94
|
+
|
|
95
|
+
let cookieCount = 0;
|
|
96
|
+
if (storageState?.cookies?.length) {
|
|
97
|
+
await ctx.addCookies(storageState.cookies);
|
|
98
|
+
cookieCount = storageState.cookies.length;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let localStorageOrigins = 0;
|
|
102
|
+
if (storageState?.origins?.length) {
|
|
103
|
+
for (const origin of storageState.origins) {
|
|
104
|
+
try {
|
|
105
|
+
await p.evaluate((items: Array<{ name: string; value: string }>) => { for (const { name, value } of items) localStorage.setItem(name, value); }, origin.localStorage ?? []);
|
|
106
|
+
localStorageOrigins++;
|
|
107
|
+
} catch { /* Origin mismatch */ }
|
|
199
108
|
}
|
|
200
|
-
}
|
|
201
|
-
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let sessionStorageOrigins = 0;
|
|
112
|
+
for (const [_origin, data] of Object.entries(sessionStorageData)) {
|
|
113
|
+
try {
|
|
114
|
+
await p.evaluate((items: Record<string, string>) => { for (const [key, value] of Object.entries(items)) sessionStorage.setItem(key, value); }, data);
|
|
115
|
+
sessionStorageOrigins++;
|
|
116
|
+
} catch { /* Origin mismatch */ }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text" as const, text: `State restored from: ${filePath}\nCookies: ${cookieCount}\nlocalStorage origins: ${localStorageOrigins}\nsessionStorage origins: ${sessionStorageOrigins}` }],
|
|
121
|
+
details: { path: filePath, cookieCount, localStorageOrigins, sessionStorageOrigins, savedAt: combined.savedAt, savedUrl: combined.url },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
202
124
|
}
|
|
@@ -625,7 +625,7 @@ export function formatVersionedRef(version: number, key: string): string {
|
|
|
625
625
|
}
|
|
626
626
|
|
|
627
627
|
export function staleRefGuidance(refDisplay: string, reason: string): string {
|
|
628
|
-
return `Ref ${refDisplay} could not be resolved (${reason}). The ref is likely stale after DOM/navigation changes. Call
|
|
628
|
+
return `Ref ${refDisplay} could not be resolved (${reason}). The ref is likely stale after DOM/navigation changes. Call browser_ref with action='snapshot' to refresh refs.`;
|
|
629
629
|
}
|
|
630
630
|
|
|
631
631
|
// ---------------------------------------------------------------------------
|
|
@@ -22,6 +22,7 @@ const IS_CACHE_TIMER_FORCED_OFF = process.env.LSD_DISABLE_CACHE_TIMER === "1" ||
|
|
|
22
22
|
|
|
23
23
|
// ANSI color codes for timer display
|
|
24
24
|
const ANSI_RESET = "\x1b[0m";
|
|
25
|
+
const ANSI_GREEN = "\x1b[32m";
|
|
25
26
|
const ANSI_YELLOW = "\x1b[33m";
|
|
26
27
|
const ANSI_RED = "\x1b[31m";
|
|
27
28
|
|
|
@@ -68,8 +69,8 @@ function formatElapsed(ms: number): string {
|
|
|
68
69
|
// 5–10 minutes: yellow
|
|
69
70
|
return `${ANSI_YELLOW}⏱ ${time}${ANSI_RESET}`;
|
|
70
71
|
}
|
|
71
|
-
// Under 5 minutes:
|
|
72
|
-
return
|
|
72
|
+
// Under 5 minutes: green
|
|
73
|
+
return `${ANSI_GREEN}⏱ ${time}${ANSI_RESET}`;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
export default function cacheTimerExtension(pi: ExtensionAPI) {
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"id": "slash-commands",
|
|
3
3
|
"name": "Slash Commands",
|
|
4
4
|
"version": "1.0.0",
|
|
5
|
-
"description": "Bundled slash commands for context inspection, planning, and lazy tool search",
|
|
5
|
+
"description": "Bundled slash commands for context inspection, planning, fast mode, and lazy tool search",
|
|
6
6
|
"tier": "bundled",
|
|
7
7
|
"requires": { "platform": ">=2.29.0" },
|
|
8
8
|
"provides": {
|
|
9
|
-
"commands": ["audit", "clear", "context", "plan", "execute", "cancel-plan", "tools"],
|
|
9
|
+
"commands": ["audit", "clear", "context", "fast", "plan", "execute", "cancel-plan", "tools"],
|
|
10
10
|
"tools": ["tool_search", "tool_enable"],
|
|
11
11
|
"flags": ["plan"]
|
|
12
12
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getAgentDir, SettingsManager, type ExtensionAPI, type ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
type FastCommandAction = "toggle" | "on" | "off" | "status" | "invalid";
|
|
4
|
+
|
|
5
|
+
function parseFastCommandAction(args: string): FastCommandAction {
|
|
6
|
+
const normalized = args.trim().toLowerCase();
|
|
7
|
+
if (!normalized) return "toggle";
|
|
8
|
+
if (normalized === "on") return "on";
|
|
9
|
+
if (normalized === "off") return "off";
|
|
10
|
+
if (normalized === "status") return "status";
|
|
11
|
+
return "invalid";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function supportsFastMode(model: ExtensionCommandContext["model"]): boolean {
|
|
15
|
+
if (!model) return false;
|
|
16
|
+
if (model.api === "openai-codex-responses") return true;
|
|
17
|
+
if (model.api !== "openai-responses") return false;
|
|
18
|
+
if (model.provider !== "openai") return false;
|
|
19
|
+
return model.capabilities?.supportsServiceTier === true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getModelLabel(model: ExtensionCommandContext["model"]): string {
|
|
23
|
+
if (!model) return "no active model";
|
|
24
|
+
return `${model.provider}/${model.id}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSettingsManager(): SettingsManager {
|
|
28
|
+
return SettingsManager.create(process.cwd(), getAgentDir());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const __testing = {
|
|
32
|
+
parseFastCommandAction,
|
|
33
|
+
supportsFastMode,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default function fastCommand(pi: ExtensionAPI) {
|
|
37
|
+
pi.registerCommand("fast", {
|
|
38
|
+
description: "Toggle fast mode for OpenAI/Codex models (service_tier=priority)",
|
|
39
|
+
getArgumentCompletions(prefix: string) {
|
|
40
|
+
const options = [
|
|
41
|
+
{ value: "on", label: "on", description: "Enable fast mode" },
|
|
42
|
+
{ value: "off", label: "off", description: "Disable fast mode" },
|
|
43
|
+
{ value: "status", label: "status", description: "Show current fast-mode status" },
|
|
44
|
+
];
|
|
45
|
+
const query = prefix.trim().toLowerCase();
|
|
46
|
+
return options.filter((item) => item.value.startsWith(query));
|
|
47
|
+
},
|
|
48
|
+
async handler(args: string, ctx: ExtensionCommandContext) {
|
|
49
|
+
const settings = getSettingsManager() as SettingsManager & {
|
|
50
|
+
getFastMode: () => boolean;
|
|
51
|
+
setFastMode: (enabled: boolean) => void;
|
|
52
|
+
};
|
|
53
|
+
const current = settings.getFastMode();
|
|
54
|
+
const action = parseFastCommandAction(args);
|
|
55
|
+
const model = ctx.model;
|
|
56
|
+
const supported = supportsFastMode(model);
|
|
57
|
+
const modelLabel = getModelLabel(model);
|
|
58
|
+
|
|
59
|
+
if (action === "invalid") {
|
|
60
|
+
ctx.ui.notify("Usage: /fast [on|off|status]", "warning");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (action === "status") {
|
|
65
|
+
ctx.ui.notify(
|
|
66
|
+
`Fast mode: ${current ? "ON" : "OFF"} · model ${modelLabel} is ${supported ? "supported" : "unsupported"}`,
|
|
67
|
+
"info",
|
|
68
|
+
);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const next = action === "toggle" ? !current : action === "on";
|
|
73
|
+
settings.setFastMode(next);
|
|
74
|
+
|
|
75
|
+
if (!supported) {
|
|
76
|
+
ctx.ui.notify(
|
|
77
|
+
`Fast mode: ${next ? "ON" : "OFF"} (saved). Current model ${modelLabel} does not support fast mode.`,
|
|
78
|
+
"warning",
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ctx.ui.notify(
|
|
84
|
+
`Fast mode: ${next ? "ON" : "OFF"} (saved). ${next ? "Requests will include service_tier=priority." : "Requests will omit service_tier."}`,
|
|
85
|
+
"info",
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
|
2
2
|
import auditCommand from "./audit.js";
|
|
3
3
|
import clearCommand from "./clear.js";
|
|
4
4
|
import contextCommand from "./context.js";
|
|
5
|
+
import fastCommand from "./fast.js";
|
|
5
6
|
import initCommand from "./init.js";
|
|
6
7
|
import planCommand from "./plan.js";
|
|
7
8
|
import toolSearchExtension from "./tools.js";
|
|
@@ -10,6 +11,7 @@ export default function slashCommands(pi: ExtensionAPI) {
|
|
|
10
11
|
auditCommand(pi);
|
|
11
12
|
clearCommand(pi);
|
|
12
13
|
contextCommand(pi);
|
|
14
|
+
fastCommand(pi);
|
|
13
15
|
initCommand(pi);
|
|
14
16
|
planCommand(pi);
|
|
15
17
|
toolSearchExtension(pi);
|
|
@@ -38,9 +38,8 @@ const BLOCKED_TOOLS = new Set([
|
|
|
38
38
|
"async_bash",
|
|
39
39
|
"bg_shell",
|
|
40
40
|
"browser_navigate",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"browser_reload",
|
|
41
|
+
"browser_pages",
|
|
42
|
+
"browser_frames",
|
|
44
43
|
"browser_click",
|
|
45
44
|
"browser_drag",
|
|
46
45
|
"browser_type",
|
|
@@ -51,18 +50,13 @@ const BLOCKED_TOOLS = new Set([
|
|
|
51
50
|
"browser_select_option",
|
|
52
51
|
"browser_set_checked",
|
|
53
52
|
"browser_set_viewport",
|
|
54
|
-
"
|
|
55
|
-
"browser_hover_ref",
|
|
56
|
-
"browser_fill_ref",
|
|
53
|
+
"browser_ref",
|
|
57
54
|
"browser_act",
|
|
58
55
|
"browser_batch",
|
|
59
56
|
"browser_fill_form",
|
|
60
|
-
"
|
|
61
|
-
"browser_block_urls",
|
|
62
|
-
"browser_clear_routes",
|
|
57
|
+
"browser_network",
|
|
63
58
|
"browser_emulate_device",
|
|
64
|
-
"
|
|
65
|
-
"browser_restore_state",
|
|
59
|
+
"browser_state",
|
|
66
60
|
"browser_generate_test",
|
|
67
61
|
"browser_verify",
|
|
68
62
|
"write",
|
|
@@ -80,6 +74,7 @@ const REVISE_LABEL = "Revise plan";
|
|
|
80
74
|
const CANCEL_LABEL = "Cancel";
|
|
81
75
|
const DEFAULT_PLAN_REVIEW_AGENT = "generic";
|
|
82
76
|
const DEFAULT_PLAN_CODING_AGENT = "worker";
|
|
77
|
+
const MIN_PLAN_CONFIDENCE = 8;
|
|
83
78
|
|
|
84
79
|
type PlanApprovalStatus = "pending" | "approved" | "revising" | "cancelled";
|
|
85
80
|
type RestorablePermissionMode = Exclude<PermissionMode, "plan">;
|
|
@@ -455,6 +450,9 @@ function buildPlanModeSystemPrompt(): string {
|
|
|
455
450
|
"You are currently in plan mode.",
|
|
456
451
|
"Investigate, clarify scope, and produce a persisted execution plan before making source changes.",
|
|
457
452
|
"If requirements are ambiguous or constraints are missing, ask concise clarifying questions before drafting or saving a plan.",
|
|
453
|
+
`Before writing or updating a plan artifact, make sure your confidence is at least ${MIN_PLAN_CONFIDENCE}/10. If confidence is lower, investigate more or ask clarifying questions first.`,
|
|
454
|
+
"Include an explicit confidence line in every saved plan, for example: \"Confidence: 8/10\" or higher.",
|
|
455
|
+
"When adjusting an existing saved plan, prefer the edit tool for targeted changes. Rewrite the whole file only when the structure changes substantially or an exact edit is impractical.",
|
|
458
456
|
"Do not modify source files or run side-effect commands while plan mode is active.",
|
|
459
457
|
"Persist plan artifacts under .lsd/plan/.",
|
|
460
458
|
];
|
|
@@ -489,7 +487,7 @@ function buildApprovalActionInstructions(): string {
|
|
|
489
487
|
|
|
490
488
|
return [
|
|
491
489
|
"Ask for plan approval now via exactly one ask_user_questions tool call.",
|
|
492
|
-
`Question 1 (single-select) id \"${PLAN_APPROVAL_ACTION_QUESTION_ID}\": ask what to do next with the plan.`,
|
|
490
|
+
`Question 1 (single-select) id \"${PLAN_APPROVAL_ACTION_QUESTION_ID}\": ask what to do next with the plan. In the question text, tell the user that if they choose \"${REVISE_LABEL}\" they should type exact requested changes in the dialog notes field before submitting.`,
|
|
493
491
|
`Question 1 options: ${APPROVE_LABEL}, ${REVIEW_LABEL}, ${REVISE_LABEL}. Put "${APPROVE_LABEL}" first with a "(Recommended)" suffix in the description, not in the label.`,
|
|
494
492
|
`Do not include \"${CANCEL_LABEL}\" as an explicit option — if the user wants to cancel they should choose \"None of the above\" and type \"${CANCEL_LABEL}\" in the note.`,
|
|
495
493
|
`Question 2 (single-select) id \"${PLAN_APPROVAL_PERMISSION_QUESTION_ID}\": ask which execution mode to use.`,
|
|
@@ -560,6 +558,25 @@ function buildReviewSteeringMessage(planPath: string, planMarkdown?: string): st
|
|
|
560
558
|
return details.join("\n\n");
|
|
561
559
|
}
|
|
562
560
|
|
|
561
|
+
function buildRevisionSteeringMessage(planPath: string, requestedChanges?: string): string {
|
|
562
|
+
const details = [
|
|
563
|
+
`The user selected \"${REVISE_LABEL}\" for ${planPath}.`,
|
|
564
|
+
"Revise the existing saved plan instead of drafting a fresh replacement unless a full rewrite is genuinely necessary.",
|
|
565
|
+
"Prefer the edit tool for targeted adjustments to the current plan artifact. Use write only if the structure changes substantially or an exact edit is impractical.",
|
|
566
|
+
`Before saving the revised plan, make sure confidence is at least ${MIN_PLAN_CONFIDENCE}/10. If lower, investigate more or ask clarifying questions first.`,
|
|
567
|
+
"Keep an explicit confidence line in the plan, for example: \"Confidence: 8/10\" or higher.",
|
|
568
|
+
];
|
|
569
|
+
|
|
570
|
+
if (requestedChanges) {
|
|
571
|
+
details.push(`User-requested changes: ${requestedChanges}`);
|
|
572
|
+
} else {
|
|
573
|
+
details.push("No concrete revision note was provided yet. Ask one concise clarifying question about what should change before editing the plan.");
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
details.push("After revising the saved plan artifact, ask for approval again.");
|
|
577
|
+
return details.join("\n\n");
|
|
578
|
+
}
|
|
579
|
+
|
|
563
580
|
function approvalSelectionToExecutionMode(
|
|
564
581
|
selected: string | undefined,
|
|
565
582
|
): { permissionMode: RestorablePermissionMode; executeWithSubagent: boolean } | undefined {
|
|
@@ -589,6 +606,12 @@ function getAnswerValues(answer: AskUserAnswer | undefined): string[] {
|
|
|
589
606
|
return values;
|
|
590
607
|
}
|
|
591
608
|
|
|
609
|
+
function getAnswerNote(answer: AskUserAnswer | undefined): string | undefined {
|
|
610
|
+
if (typeof answer?.notes !== "string") return undefined;
|
|
611
|
+
const note = answer.notes.trim();
|
|
612
|
+
return note.length > 0 ? note : undefined;
|
|
613
|
+
}
|
|
614
|
+
|
|
592
615
|
function selectionRequestsCancel(selected: string[]): boolean {
|
|
593
616
|
return selected.some((value) => {
|
|
594
617
|
if (typeof value !== "string") return false;
|
|
@@ -612,6 +635,8 @@ export const __testing = {
|
|
|
612
635
|
buildApprovalSteeringMessage,
|
|
613
636
|
buildPlanPreviewMessage,
|
|
614
637
|
buildReviewSteeringMessage,
|
|
638
|
+
buildRevisionSteeringMessage,
|
|
639
|
+
buildPlanModeSystemPrompt,
|
|
615
640
|
buildAutoSuggestPlanModeSystemPrompt,
|
|
616
641
|
readAutoSuggestPlanModeSetting,
|
|
617
642
|
PLAN_SUGGEST_QUESTION_ID,
|
|
@@ -830,9 +855,14 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
830
855
|
}
|
|
831
856
|
|
|
832
857
|
if (actionSelection.includes(REVISE_LABEL)) {
|
|
858
|
+
const requestedChanges = getAnswerNote(actionAnswer);
|
|
833
859
|
enablePlanMode(pi, ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined, {
|
|
834
860
|
approvalStatus: "revising",
|
|
835
861
|
});
|
|
862
|
+
pi.sendUserMessage(
|
|
863
|
+
buildRevisionSteeringMessage(state.latestPlanPath ?? "the latest plan", requestedChanges),
|
|
864
|
+
{ deliverAs: "steer" },
|
|
865
|
+
);
|
|
836
866
|
}
|
|
837
867
|
});
|
|
838
868
|
|
|
@@ -31,6 +31,11 @@ type BackgroundJobMetadata = {
|
|
|
31
31
|
model?: string;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
type InProcessHandleLike = {
|
|
35
|
+
abort: () => void;
|
|
36
|
+
dispose: () => void;
|
|
37
|
+
};
|
|
38
|
+
|
|
34
39
|
export class BackgroundJobManager {
|
|
35
40
|
private jobs = new Map<string, BackgroundSubagentJob>();
|
|
36
41
|
private evictionTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
@@ -80,6 +85,29 @@ export class BackgroundJobManager {
|
|
|
80
85
|
return this.attachJob(agentName, task, cwd, abortController, resultPromise, metadata);
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Adopt an in-process subagent handle into background tracking.
|
|
90
|
+
*/
|
|
91
|
+
adoptHandle(
|
|
92
|
+
agentName: string,
|
|
93
|
+
task: string,
|
|
94
|
+
cwd: string,
|
|
95
|
+
handle: InProcessHandleLike,
|
|
96
|
+
resultPromise: Promise<BackgroundJobResult>,
|
|
97
|
+
metadata?: BackgroundJobMetadata,
|
|
98
|
+
): string {
|
|
99
|
+
const abortController = new AbortController();
|
|
100
|
+
const onAbort = () => handle.abort();
|
|
101
|
+
abortController.signal.addEventListener("abort", onAbort, { once: true });
|
|
102
|
+
|
|
103
|
+
const wrapped = resultPromise.finally(() => {
|
|
104
|
+
abortController.signal.removeEventListener("abort", onAbort);
|
|
105
|
+
handle.dispose();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return this.attachJob(agentName, task, cwd, abortController, wrapped, metadata);
|
|
109
|
+
}
|
|
110
|
+
|
|
83
111
|
/**
|
|
84
112
|
* Cancel a running job.
|
|
85
113
|
*/
|