lsd-pi 1.1.2 → 1.1.3
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/README.md +2 -1
- package/dist/bedrock-auth.d.ts +25 -0
- package/dist/bedrock-auth.js +59 -0
- package/dist/headless.js +8 -3
- package/dist/loader.js +1 -0
- package/dist/onboarding-llm.d.ts +37 -0
- package/dist/onboarding-llm.js +64 -0
- package/dist/onboarding.d.ts +2 -14
- package/dist/onboarding.js +146 -71
- package/dist/pi-migration.js +1 -0
- package/dist/resources/extensions/memory/auto-extract.js +21 -3
- package/dist/resources/extensions/memory/dream.js +703 -0
- package/dist/resources/extensions/memory/extension-manifest.json +2 -2
- package/dist/resources/extensions/memory/index.js +115 -8
- package/dist/resources/extensions/slash-commands/extension-manifest.json +10 -10
- package/dist/resources/extensions/slash-commands/index.js +0 -4
- package/dist/resources/extensions/slash-commands/plan.js +181 -45
- package/dist/resources/extensions/subagent/agents.js +14 -1
- package/dist/resources/extensions/subagent/configured-model.js +3 -2
- package/dist/resources/extensions/subagent/index.js +34 -28
- package/dist/resources/extensions/subagent/launch-helpers.js +24 -0
- package/dist/resources/extensions/subagent/model-resolution.js +41 -3
- package/dist/resources/extensions/usage/extension-manifest.json +11 -0
- package/dist/resources/extensions/usage/index.js +346 -0
- package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/SKILL.md +6 -6
- package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/references/custom-tools.md +1 -1
- package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extension-lifecycle.md +2 -2
- package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensioncontext-reference.md +1 -1
- package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/references/key-rules-gotchas.md +4 -4
- package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/packaging-distribution.md +6 -6
- package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/create-extension.md +3 -3
- package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/debug-extension.md +5 -5
- package/dist/resources/skills/teams-debug/SKILL.md +5 -6
- package/dist/resources/skills/teams-document/SKILL.md +1 -2
- package/dist/resources/skills/teams-plan/SKILL.md +3 -4
- package/dist/resources/skills/teams-run/SKILL.md +3 -4
- package/dist/resources/skills/teams-verify/SKILL.md +4 -5
- package/dist/startup-model-validation.js +1 -0
- package/dist/welcome-screen.js +13 -11
- package/dist/wizard.js +12 -0
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +688 -409
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +761 -488
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +40 -18
- package/packages/pi-ai/src/models.generated.ts +759 -486
- package/packages/pi-coding-agent/dist/cli/config-selector.js +1 -1
- package/packages/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +1 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +6 -30
- package/packages/pi-coding-agent/dist/core/agent-session.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.js +44 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +9 -5
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +3 -2
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +1 -1
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- 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 +15 -12
- 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 +6 -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 +25 -1
- 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.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- 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 +10 -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/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +31 -22
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +18 -5
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +139 -20
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -0
- 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/cli/config-selector.ts +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +5 -28
- package/packages/pi-coding-agent/src/core/settings-manager.ts +52 -1
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +18 -5
- package/packages/pi-coding-agent/src/core/skills.ts +3 -2
- package/packages/pi-coding-agent/src/index.ts +1 -1
- package/packages/pi-coding-agent/src/main.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +12 -13
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +39 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +46 -20
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +171 -20
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -0
- package/packages/pi-tui/dist/components/editor.d.ts +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +23 -0
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +23 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +18 -5
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +139 -20
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +4 -0
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/memory/auto-extract.ts +23 -3
- package/src/resources/extensions/memory/dream.ts +814 -0
- package/src/resources/extensions/memory/extension-manifest.json +2 -2
- package/src/resources/extensions/memory/index.ts +134 -13
- package/src/resources/extensions/memory/tests/auto-extract.test.ts +10 -2
- package/src/resources/extensions/memory/tests/dream.test.ts +142 -0
- package/src/resources/extensions/slash-commands/extension-manifest.json +10 -10
- package/src/resources/extensions/slash-commands/index.ts +3 -7
- package/src/resources/extensions/slash-commands/plan.ts +192 -46
- package/src/resources/extensions/subagent/agents.ts +11 -1
- package/src/resources/extensions/subagent/configured-model.ts +3 -2
- package/src/resources/extensions/subagent/index.ts +38 -30
- package/src/resources/extensions/subagent/launch-helpers.ts +30 -0
- package/src/resources/extensions/subagent/model-resolution.ts +40 -3
- package/src/resources/extensions/usage/extension-manifest.json +11 -0
- package/src/resources/extensions/usage/index.ts +441 -0
- package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/SKILL.md +6 -6
- package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/references/custom-tools.md +1 -1
- package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extension-lifecycle.md +2 -2
- package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensioncontext-reference.md +1 -1
- package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/references/key-rules-gotchas.md +4 -4
- package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/packaging-distribution.md +6 -6
- package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/create-extension.md +3 -3
- package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/debug-extension.md +5 -5
- package/src/resources/skills/teams-debug/SKILL.md +5 -6
- package/src/resources/skills/teams-document/SKILL.md +1 -2
- package/src/resources/skills/teams-plan/SKILL.md +3 -4
- package/src/resources/skills/teams-run/SKILL.md +3 -4
- package/src/resources/skills/teams-verify/SKILL.md +4 -5
- package/dist/resources/extensions/slash-commands/create-extension.js +0 -264
- package/dist/resources/extensions/slash-commands/create-slash-command.js +0 -208
- package/src/resources/extensions/slash-commands/create-extension.ts +0 -297
- package/src/resources/extensions/slash-commands/create-slash-command.ts +0 -234
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/compaction-session-control.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-commands.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-rendering.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-ui.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/events-reference.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensionapi-reference.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/mode-behavior.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/model-provider-management.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/remote-execution-overrides.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/state-management.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/system-prompt-modification.md +0 -0
- /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/add-capability.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/compaction-session-control.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-commands.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-rendering.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-ui.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/events-reference.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensionapi-reference.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/mode-behavior.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/model-provider-management.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/remote-execution-overrides.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/state-management.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/system-prompt-modification.md +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/templates/extension-skeleton.ts +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/templates/stateful-tool-skeleton.ts +0 -0
- /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/add-capability.md +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { getAgentDir, getPermissionMode, isToolCallEventType, setPermissionMode, type ExtensionAPI, type ExtensionCommandContext, type PermissionMode } from "@gsd/pi-coding-agent";
|
|
2
3
|
import { join } from "node:path";
|
|
3
|
-
import type { ExtensionAPI, ExtensionCommandContext, PermissionMode } from "@gsd/pi-coding-agent";
|
|
4
|
-
import { getPermissionMode, isToolCallEventType, setPermissionMode } from "@gsd/pi-coding-agent";
|
|
5
4
|
|
|
6
5
|
const PLAN_ENTRY_TYPE = "plan-mode-state";
|
|
7
6
|
const PLAN_APPROVAL_QUESTION_ID = "plan_mode_approval";
|
|
@@ -58,9 +57,15 @@ const BLOCKED_TOOLS = new Set([
|
|
|
58
57
|
"write",
|
|
59
58
|
"edit",
|
|
60
59
|
]);
|
|
60
|
+
const DEFAULT_APPROVAL_PERMISSION_MODE: RestorablePermissionMode = "auto";
|
|
61
|
+
const APPROVE_AUTO_LABEL = "Approve & switch to Auto mode";
|
|
62
|
+
const APPROVE_BYPASS_LABEL = "Approve & switch to Bypass mode";
|
|
63
|
+
const REVISE_LABEL = "Revise plan";
|
|
64
|
+
const CANCEL_LABEL = "Cancel";
|
|
61
65
|
|
|
62
66
|
type PlanApprovalStatus = "pending" | "approved" | "revising" | "cancelled";
|
|
63
67
|
type RestorablePermissionMode = Exclude<PermissionMode, "plan">;
|
|
68
|
+
type ModelRef = { provider: string; id: string };
|
|
64
69
|
|
|
65
70
|
interface PlanModeState {
|
|
66
71
|
active: boolean;
|
|
@@ -68,19 +73,65 @@ interface PlanModeState {
|
|
|
68
73
|
latestPlanPath?: string;
|
|
69
74
|
approvalStatus: PlanApprovalStatus;
|
|
70
75
|
previousMode?: RestorablePermissionMode;
|
|
76
|
+
preplanModel?: ModelRef;
|
|
77
|
+
targetPermissionMode?: PermissionMode;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
const INITIAL_STATE: PlanModeState = {
|
|
74
81
|
active: false,
|
|
75
82
|
task: "",
|
|
83
|
+
latestPlanPath: undefined,
|
|
76
84
|
approvalStatus: "cancelled",
|
|
85
|
+
previousMode: undefined,
|
|
86
|
+
preplanModel: undefined,
|
|
87
|
+
targetPermissionMode: undefined,
|
|
77
88
|
};
|
|
89
|
+
|
|
90
|
+
let state: PlanModeState = { ...INITIAL_STATE };
|
|
78
91
|
let startedFromFlag = false;
|
|
79
92
|
|
|
80
93
|
function isPlanModeActive(): boolean {
|
|
81
94
|
return getPermissionMode() === "plan";
|
|
82
95
|
}
|
|
83
96
|
|
|
97
|
+
function parseQualifiedModelRef(value: unknown): ModelRef | undefined {
|
|
98
|
+
if (typeof value !== "string") return undefined;
|
|
99
|
+
const trimmed = value.trim();
|
|
100
|
+
if (!trimmed) return undefined;
|
|
101
|
+
const parts = trimmed.split("/");
|
|
102
|
+
if (parts.length !== 2) return undefined;
|
|
103
|
+
const [provider, id] = parts.map((part) => part.trim());
|
|
104
|
+
if (!provider || !id) return undefined;
|
|
105
|
+
return { provider, id };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function readPlanModeReasoningModel(): string | undefined {
|
|
109
|
+
try {
|
|
110
|
+
const settingsPath = join(getAgentDir(), "settings.json");
|
|
111
|
+
if (!existsSync(settingsPath)) return undefined;
|
|
112
|
+
const raw = readFileSync(settingsPath, "utf-8");
|
|
113
|
+
const parsed = JSON.parse(raw) as { planModeReasoningModel?: unknown };
|
|
114
|
+
const model = parseQualifiedModelRef(parsed.planModeReasoningModel);
|
|
115
|
+
return model ? `${model.provider}/${model.id}` : undefined;
|
|
116
|
+
} catch {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function sameModel(left: ModelRef | undefined, right: ModelRef | undefined): boolean {
|
|
122
|
+
return !!left && !!right && left.provider === right.provider && left.id === right.id;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function resolveModelFromContext(ctx: any, modelRef: ModelRef): any | undefined {
|
|
126
|
+
const allModels = typeof ctx?.modelRegistry?.getAll === "function" ? ctx.modelRegistry.getAll() : [];
|
|
127
|
+
return allModels.find((model: any) => model.provider === modelRef.provider && model.id === modelRef.id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function setPermissionModeAndEnv(mode: PermissionMode): void {
|
|
131
|
+
setPermissionMode(mode);
|
|
132
|
+
process.env.LUCENT_CODE_PERMISSION_MODE = mode;
|
|
133
|
+
}
|
|
134
|
+
|
|
84
135
|
function saveState(pi: ExtensionAPI): void {
|
|
85
136
|
pi.appendEntry<PlanModeState>(PLAN_ENTRY_TYPE, { ...state });
|
|
86
137
|
}
|
|
@@ -90,15 +141,11 @@ function setState(pi: ExtensionAPI, next: PlanModeState): void {
|
|
|
90
141
|
saveState(pi);
|
|
91
142
|
}
|
|
92
143
|
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
approvalStatus: "cancelled",
|
|
99
|
-
previousMode: undefined,
|
|
100
|
-
};
|
|
101
|
-
saveState(pi);
|
|
144
|
+
function resetState(pi: ExtensionAPI, overrides: Partial<PlanModeState> = {}): void {
|
|
145
|
+
setState(pi, {
|
|
146
|
+
...INITIAL_STATE,
|
|
147
|
+
...overrides,
|
|
148
|
+
});
|
|
102
149
|
}
|
|
103
150
|
|
|
104
151
|
function ensurePlanDir(): string {
|
|
@@ -124,35 +171,73 @@ function restoreStateFromSession(ctx: ExtensionCommandContext | any): void {
|
|
|
124
171
|
|
|
125
172
|
function enablePlanMode(
|
|
126
173
|
pi: ExtensionAPI,
|
|
127
|
-
|
|
174
|
+
currentModel: ModelRef | undefined,
|
|
175
|
+
next: Partial<Pick<PlanModeState, "task" | "latestPlanPath" | "approvalStatus" | "previousMode" | "preplanModel" | "targetPermissionMode">> = {},
|
|
128
176
|
): void {
|
|
129
177
|
const currentMode = getPermissionMode();
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
178
|
+
const enteringPlanMode = currentMode !== "plan";
|
|
179
|
+
const previousMode: RestorablePermissionMode = enteringPlanMode
|
|
180
|
+
? currentMode
|
|
181
|
+
: (state.previousMode ?? "accept-on-edit");
|
|
133
182
|
|
|
134
|
-
|
|
135
|
-
process.env.LUCENT_CODE_PERMISSION_MODE = "plan";
|
|
183
|
+
setPermissionModeAndEnv("plan");
|
|
136
184
|
setState(pi, {
|
|
137
185
|
active: true,
|
|
138
186
|
task: next.task ?? state.task,
|
|
139
187
|
latestPlanPath: next.latestPlanPath ?? state.latestPlanPath,
|
|
140
188
|
approvalStatus: next.approvalStatus ?? state.approvalStatus ?? "pending",
|
|
141
189
|
previousMode: next.previousMode ?? previousMode,
|
|
190
|
+
preplanModel: next.preplanModel ?? (enteringPlanMode ? (currentModel ?? state.preplanModel) : state.preplanModel),
|
|
191
|
+
targetPermissionMode: next.targetPermissionMode ?? state.targetPermissionMode,
|
|
142
192
|
});
|
|
143
193
|
}
|
|
144
194
|
|
|
145
|
-
function
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
195
|
+
function leavePlanMode(
|
|
196
|
+
pi: ExtensionAPI,
|
|
197
|
+
approvalStatus: PlanApprovalStatus,
|
|
198
|
+
nextPermissionMode: RestorablePermissionMode,
|
|
199
|
+
clearTask = false,
|
|
200
|
+
): RestorablePermissionMode {
|
|
201
|
+
setPermissionModeAndEnv(nextPermissionMode);
|
|
149
202
|
setState(pi, {
|
|
150
203
|
active: false,
|
|
151
204
|
task: clearTask ? "" : state.task,
|
|
152
205
|
latestPlanPath: state.latestPlanPath,
|
|
153
206
|
approvalStatus,
|
|
154
|
-
previousMode:
|
|
207
|
+
previousMode: state.previousMode,
|
|
208
|
+
preplanModel: state.preplanModel,
|
|
209
|
+
targetPermissionMode: state.targetPermissionMode ?? nextPermissionMode,
|
|
155
210
|
});
|
|
211
|
+
return nextPermissionMode;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function setModelIfNeeded(pi: ExtensionAPI, ctx: any, modelRef: ModelRef | undefined): Promise<void> {
|
|
215
|
+
if (!modelRef) return;
|
|
216
|
+
const currentModel = parseQualifiedModelRef(ctx?.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined);
|
|
217
|
+
if (sameModel(currentModel, modelRef)) return;
|
|
218
|
+
const model = resolveModelFromContext(ctx, modelRef);
|
|
219
|
+
if (!model) return;
|
|
220
|
+
await pi.setModel(model, { persist: false });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function approvePlan(pi: ExtensionAPI, ctx: any, permissionMode: RestorablePermissionMode): Promise<void> {
|
|
224
|
+
const reasoningModel = parseQualifiedModelRef(readPlanModeReasoningModel());
|
|
225
|
+
if (reasoningModel) {
|
|
226
|
+
await setModelIfNeeded(pi, ctx, reasoningModel);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
state = {
|
|
230
|
+
...state,
|
|
231
|
+
targetPermissionMode: permissionMode,
|
|
232
|
+
};
|
|
233
|
+
leavePlanMode(pi, "approved", permissionMode);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function cancelPlan(pi: ExtensionAPI, ctx: any, clearTask = true): Promise<RestorablePermissionMode> {
|
|
237
|
+
const restoreMode = state.previousMode ?? "accept-on-edit";
|
|
238
|
+
await setModelIfNeeded(pi, ctx, state.preplanModel);
|
|
239
|
+
leavePlanMode(pi, "cancelled", restoreMode, clearTask);
|
|
240
|
+
resetState(pi, { approvalStatus: "cancelled" });
|
|
156
241
|
return restoreMode;
|
|
157
242
|
}
|
|
158
243
|
|
|
@@ -168,6 +253,49 @@ function buildPlanModeSystemPrompt(): string {
|
|
|
168
253
|
return details.join(" ");
|
|
169
254
|
}
|
|
170
255
|
|
|
256
|
+
function buildApprovalSteeringMessage(planPath: string): string {
|
|
257
|
+
return [
|
|
258
|
+
`Plan artifact saved at ${planPath}.`,
|
|
259
|
+
"Present approval options now using ask_user_questions with exactly one single-select question.",
|
|
260
|
+
`Use question id \"${PLAN_APPROVAL_QUESTION_ID}\" and ask the user what to do next.`,
|
|
261
|
+
"Important: ask_user_questions single-select supports only 2-3 explicit options.",
|
|
262
|
+
"Use exactly these 3 options:",
|
|
263
|
+
`1. ${APPROVE_AUTO_LABEL} (Recommended)`,
|
|
264
|
+
`2. ${APPROVE_BYPASS_LABEL}`,
|
|
265
|
+
`3. ${REVISE_LABEL}`,
|
|
266
|
+
`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 free-text note.`,
|
|
267
|
+
"If the dialog is dismissed or the user gives no answer, continue planning.",
|
|
268
|
+
].join(" ");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function approvalSelectionToPermissionMode(selected: string): RestorablePermissionMode | undefined {
|
|
272
|
+
if (selected.includes(APPROVE_AUTO_LABEL)) return "auto";
|
|
273
|
+
if (selected.includes(APPROVE_BYPASS_LABEL)) return "danger-full-access";
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function selectionRequestsCancel(selected: string | string[]): boolean {
|
|
278
|
+
const values = Array.isArray(selected) ? selected : [selected];
|
|
279
|
+
return values.some((value) => {
|
|
280
|
+
if (typeof value !== "string") return false;
|
|
281
|
+
if (value.includes(CANCEL_LABEL)) return true;
|
|
282
|
+
const normalized = value.replace(/^user_note:\s*/i, "").trim().toLowerCase();
|
|
283
|
+
return normalized === "cancel" || normalized.includes("cancel plan");
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export const __testing = {
|
|
288
|
+
getState(): PlanModeState {
|
|
289
|
+
return { ...state };
|
|
290
|
+
},
|
|
291
|
+
resetState(): void {
|
|
292
|
+
state = { ...INITIAL_STATE };
|
|
293
|
+
startedFromFlag = false;
|
|
294
|
+
},
|
|
295
|
+
parseQualifiedModelRef,
|
|
296
|
+
approvalSelectionToPermissionMode,
|
|
297
|
+
};
|
|
298
|
+
|
|
171
299
|
export default function planCommand(pi: ExtensionAPI) {
|
|
172
300
|
pi.registerFlag("plan", {
|
|
173
301
|
description: "Start the session in plan mode and require a persisted .lsd/plan markdown plan before execution",
|
|
@@ -178,8 +306,7 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
178
306
|
restoreStateFromSession(ctx);
|
|
179
307
|
startedFromFlag = false;
|
|
180
308
|
if (state.active) {
|
|
181
|
-
|
|
182
|
-
process.env.LUCENT_CODE_PERMISSION_MODE = "plan";
|
|
309
|
+
setPermissionModeAndEnv("plan");
|
|
183
310
|
}
|
|
184
311
|
});
|
|
185
312
|
|
|
@@ -190,7 +317,7 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
190
317
|
};
|
|
191
318
|
});
|
|
192
319
|
|
|
193
|
-
pi.on("input", async (event) => {
|
|
320
|
+
pi.on("input", async (event, ctx) => {
|
|
194
321
|
const planFlag = pi.getFlag("plan");
|
|
195
322
|
if (startedFromFlag || planFlag !== true || event.source !== "interactive") {
|
|
196
323
|
return { action: "continue" as const };
|
|
@@ -198,9 +325,11 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
198
325
|
|
|
199
326
|
startedFromFlag = true;
|
|
200
327
|
ensurePlanDir();
|
|
201
|
-
enablePlanMode(pi, {
|
|
328
|
+
enablePlanMode(pi, ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined, {
|
|
202
329
|
task: event.text.trim(),
|
|
203
330
|
approvalStatus: "pending",
|
|
331
|
+
latestPlanPath: undefined,
|
|
332
|
+
targetPermissionMode: undefined,
|
|
204
333
|
});
|
|
205
334
|
|
|
206
335
|
return { action: "continue" as const };
|
|
@@ -235,16 +364,24 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
235
364
|
}
|
|
236
365
|
});
|
|
237
366
|
|
|
238
|
-
pi.on("tool_result", async (event) => {
|
|
367
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
239
368
|
if (event.toolName === "write" || event.toolName === "edit") {
|
|
240
369
|
const input = event.input as { path?: string } | undefined;
|
|
241
370
|
const path = input?.path;
|
|
242
|
-
if (path && PLAN_DIR_RE.test(path)) {
|
|
371
|
+
if (path && PLAN_DIR_RE.test(path) && isPlanModeActive()) {
|
|
243
372
|
setState(pi, {
|
|
244
373
|
...state,
|
|
245
374
|
latestPlanPath: path,
|
|
246
|
-
approvalStatus:
|
|
375
|
+
approvalStatus: "pending",
|
|
376
|
+
targetPermissionMode: undefined,
|
|
247
377
|
});
|
|
378
|
+
|
|
379
|
+
if (!ctx.hasUI) {
|
|
380
|
+
await approvePlan(pi, ctx, DEFAULT_APPROVAL_PERMISSION_MODE);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
pi.sendUserMessage(buildApprovalSteeringMessage(path), { deliverAs: "steer" });
|
|
248
385
|
}
|
|
249
386
|
return;
|
|
250
387
|
}
|
|
@@ -260,22 +397,28 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
260
397
|
const answer = details.response.answers[PLAN_APPROVAL_QUESTION_ID];
|
|
261
398
|
if (!answer) return;
|
|
262
399
|
|
|
263
|
-
const selected = Array.isArray(answer.selected) ? answer.selected
|
|
264
|
-
if (typeof
|
|
400
|
+
const selected = Array.isArray(answer.selected) ? answer.selected : [answer.selected];
|
|
401
|
+
if (!selected.every((value) => typeof value === "string")) return;
|
|
265
402
|
|
|
266
|
-
|
|
267
|
-
|
|
403
|
+
const targetPermissionMode = approvalSelectionToPermissionMode(selected[0]);
|
|
404
|
+
if (targetPermissionMode) {
|
|
405
|
+
state = {
|
|
406
|
+
...state,
|
|
407
|
+
targetPermissionMode,
|
|
408
|
+
};
|
|
409
|
+
await approvePlan(pi, ctx, targetPermissionMode);
|
|
268
410
|
return;
|
|
269
411
|
}
|
|
270
412
|
|
|
271
|
-
if (selected.includes(
|
|
272
|
-
enablePlanMode(pi, {
|
|
413
|
+
if (selected.some((value) => value.includes(REVISE_LABEL))) {
|
|
414
|
+
enablePlanMode(pi, ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined, {
|
|
415
|
+
approvalStatus: "revising",
|
|
416
|
+
});
|
|
273
417
|
return;
|
|
274
418
|
}
|
|
275
419
|
|
|
276
|
-
if (selected
|
|
277
|
-
|
|
278
|
-
clearState(pi);
|
|
420
|
+
if (selectionRequestsCancel(selected)) {
|
|
421
|
+
await cancelPlan(pi, ctx, true);
|
|
279
422
|
}
|
|
280
423
|
});
|
|
281
424
|
|
|
@@ -283,16 +426,18 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
283
426
|
description: "Toggle plan mode. While active, only investigative tools and writes under .lsd/plan/ are allowed",
|
|
284
427
|
async handler(args: string, ctx: ExtensionCommandContext) {
|
|
285
428
|
if (isPlanModeActive()) {
|
|
286
|
-
|
|
429
|
+
await cancelPlan(pi, ctx, true);
|
|
287
430
|
ctx.ui.notify("Plan mode disabled.", "info");
|
|
288
431
|
return;
|
|
289
432
|
}
|
|
290
433
|
|
|
291
434
|
ensurePlanDir();
|
|
292
435
|
const task = args.trim();
|
|
293
|
-
enablePlanMode(pi, {
|
|
436
|
+
enablePlanMode(pi, ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined, {
|
|
294
437
|
task,
|
|
438
|
+
latestPlanPath: undefined,
|
|
295
439
|
approvalStatus: "pending",
|
|
440
|
+
targetPermissionMode: undefined,
|
|
296
441
|
});
|
|
297
442
|
ctx.ui.notify(
|
|
298
443
|
task
|
|
@@ -310,8 +455,8 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
310
455
|
ctx.ui.notify("Plan mode is not active.", "info");
|
|
311
456
|
return;
|
|
312
457
|
}
|
|
313
|
-
|
|
314
|
-
ctx.ui.notify(`Plan
|
|
458
|
+
await approvePlan(pi, ctx, DEFAULT_APPROVAL_PERMISSION_MODE);
|
|
459
|
+
ctx.ui.notify(`Plan approved. Permission mode switched to ${DEFAULT_APPROVAL_PERMISSION_MODE}.`, "info");
|
|
315
460
|
},
|
|
316
461
|
});
|
|
317
462
|
|
|
@@ -319,9 +464,10 @@ export default function planCommand(pi: ExtensionAPI) {
|
|
|
319
464
|
description: "Cancel the current plan-mode session without executing",
|
|
320
465
|
async handler(_args: string, ctx: ExtensionCommandContext) {
|
|
321
466
|
if (isPlanModeActive()) {
|
|
322
|
-
|
|
467
|
+
await cancelPlan(pi, ctx, true);
|
|
468
|
+
} else {
|
|
469
|
+
resetState(pi, { approvalStatus: "cancelled" });
|
|
323
470
|
}
|
|
324
|
-
clearState(pi);
|
|
325
471
|
ctx.ui.notify("Plan mode cancelled.", "info");
|
|
326
472
|
},
|
|
327
473
|
});
|
|
@@ -25,6 +25,16 @@ export interface AgentDiscoveryResult {
|
|
|
25
25
|
projectAgentsDir: string | null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function normalizeAgentModel(model: string | undefined): string | undefined {
|
|
29
|
+
const trimmed = model?.trim();
|
|
30
|
+
if (!trimmed) return undefined;
|
|
31
|
+
if (trimmed === "$budget_model") return trimmed;
|
|
32
|
+
if (trimmed.includes(" ")) return undefined;
|
|
33
|
+
if (!trimmed.includes("/")) return trimmed;
|
|
34
|
+
const parts = trimmed.split("/");
|
|
35
|
+
return parts.length === 2 && parts.every(Boolean) ? trimmed : undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
|
|
29
39
|
const agents: AgentConfig[] = [];
|
|
30
40
|
|
|
@@ -66,7 +76,7 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
|
|
|
66
76
|
name: frontmatter.name,
|
|
67
77
|
description: frontmatter.description,
|
|
68
78
|
tools: tools && tools.length > 0 ? tools : undefined,
|
|
69
|
-
model: frontmatter.model,
|
|
79
|
+
model: normalizeAgentModel(frontmatter.model),
|
|
70
80
|
systemPrompt: body,
|
|
71
81
|
source,
|
|
72
82
|
filePath,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentConfig } from "./agents.js";
|
|
2
2
|
import type { SharedPreferences } from "../shared/preferences.js";
|
|
3
|
+
import { normalizeSubagentModel } from "./model-resolution.js";
|
|
3
4
|
|
|
4
5
|
export function resolveConfiguredSubagentModel(
|
|
5
6
|
agent: AgentConfig,
|
|
@@ -9,7 +10,7 @@ export function resolveConfiguredSubagentModel(
|
|
|
9
10
|
const configuredModel = agent.model?.trim();
|
|
10
11
|
if (!configuredModel) return undefined;
|
|
11
12
|
if (configuredModel === "$budget_model") {
|
|
12
|
-
return settingsBudgetModel
|
|
13
|
+
return normalizeSubagentModel(settingsBudgetModel) ?? normalizeSubagentModel(preferences?.subagent?.budget_model);
|
|
13
14
|
}
|
|
14
|
-
return configuredModel;
|
|
15
|
+
return normalizeSubagentModel(configuredModel);
|
|
15
16
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Uses JSON mode to capture structured output from subagents.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { spawn,
|
|
15
|
+
import { spawn, execFileSync, type ChildProcess } from "node:child_process";
|
|
16
16
|
import * as crypto from "node:crypto";
|
|
17
17
|
import * as fs from "node:fs";
|
|
18
18
|
import * as os from "node:os";
|
|
@@ -31,6 +31,7 @@ import { Container, Markdown, Spacer, Text } from "@gsd/pi-tui";
|
|
|
31
31
|
import { Type } from "@sinclair/typebox";
|
|
32
32
|
import { formatTokenCount } from "../shared/mod.js";
|
|
33
33
|
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.js";
|
|
34
|
+
import { buildSubagentProcessArgs, getBundledExtensionPathsFromEnv } from "./launch-helpers.js";
|
|
34
35
|
import {
|
|
35
36
|
type IsolationEnvironment,
|
|
36
37
|
type IsolationMode,
|
|
@@ -42,7 +43,10 @@ import {
|
|
|
42
43
|
import { registerWorker, updateWorker } from "./worker-registry.js";
|
|
43
44
|
import { handleSubagentPermissionRequest, isSubagentPermissionRequest } from "./approval-proxy.js";
|
|
44
45
|
import { resolveConfiguredSubagentModel } from "./configured-model.js";
|
|
45
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
normalizeSubagentModel,
|
|
48
|
+
resolveSubagentModel,
|
|
49
|
+
} from "./model-resolution.js";
|
|
46
50
|
import { loadEffectivePreferences } from "../shared/preferences.js";
|
|
47
51
|
import { CmuxClient, shellEscape } from "../cmux/index.js";
|
|
48
52
|
|
|
@@ -51,7 +55,7 @@ const MAX_CONCURRENCY = 4;
|
|
|
51
55
|
const COLLAPSED_ITEM_COUNT = 10;
|
|
52
56
|
const liveSubagentProcesses = new Set<ChildProcess>();
|
|
53
57
|
|
|
54
|
-
async function stopLiveSubagents(): Promise<void> {
|
|
58
|
+
export async function stopLiveSubagents(): Promise<void> {
|
|
55
59
|
const active = Array.from(liveSubagentProcesses);
|
|
56
60
|
if (active.length === 0) return;
|
|
57
61
|
|
|
@@ -274,26 +278,14 @@ function readBudgetSubagentModelFromSettings(): string | undefined {
|
|
|
274
278
|
if (!fs.existsSync(settingsPath)) return undefined;
|
|
275
279
|
const raw = fs.readFileSync(settingsPath, "utf-8");
|
|
276
280
|
const parsed = JSON.parse(raw) as { budgetSubagentModel?: unknown };
|
|
277
|
-
return typeof parsed.budgetSubagentModel === "string"
|
|
281
|
+
return typeof parsed.budgetSubagentModel === "string"
|
|
282
|
+
? normalizeSubagentModel(parsed.budgetSubagentModel)
|
|
283
|
+
: undefined;
|
|
278
284
|
} catch {
|
|
279
285
|
return undefined;
|
|
280
286
|
}
|
|
281
287
|
}
|
|
282
288
|
|
|
283
|
-
function buildSubagentProcessArgs(
|
|
284
|
-
agent: AgentConfig,
|
|
285
|
-
task: string,
|
|
286
|
-
tmpPromptPath: string | null,
|
|
287
|
-
model: string | undefined,
|
|
288
|
-
): string[] {
|
|
289
|
-
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
290
|
-
if (model) args.push("--model", model);
|
|
291
|
-
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
292
|
-
if (tmpPromptPath) args.push("--append-system-prompt", tmpPromptPath);
|
|
293
|
-
args.push(`Task: ${task}`);
|
|
294
|
-
return args;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
289
|
function resolveSubagentCliPath(defaultCwd: string): string | null {
|
|
298
290
|
const candidates = [process.env.GSD_BIN_PATH, process.env.LSD_BIN_PATH, process.argv[1]]
|
|
299
291
|
.map((value) => value?.trim())
|
|
@@ -310,7 +302,7 @@ function resolveSubagentCliPath(defaultCwd: string): string | null {
|
|
|
310
302
|
|
|
311
303
|
for (const binName of ["lsd", "gsd"]) {
|
|
312
304
|
try {
|
|
313
|
-
const resolved =
|
|
305
|
+
const resolved = execFileSync("which", [binName], { encoding: "utf-8" }).trim();
|
|
314
306
|
if (resolved) return resolved;
|
|
315
307
|
} catch {
|
|
316
308
|
/* ignore */
|
|
@@ -325,13 +317,13 @@ function processSubagentEventLine(
|
|
|
325
317
|
currentResult: SingleResult,
|
|
326
318
|
emitUpdate: () => void,
|
|
327
319
|
proc?: ChildProcess,
|
|
328
|
-
):
|
|
329
|
-
if (!line.trim()) return;
|
|
320
|
+
): boolean {
|
|
321
|
+
if (!line.trim()) return false;
|
|
330
322
|
let event: any;
|
|
331
323
|
try {
|
|
332
324
|
event = JSON.parse(line);
|
|
333
325
|
} catch {
|
|
334
|
-
return;
|
|
326
|
+
return false;
|
|
335
327
|
}
|
|
336
328
|
|
|
337
329
|
if (proc && isSubagentPermissionRequest(event)) {
|
|
@@ -339,10 +331,10 @@ function processSubagentEventLine(
|
|
|
339
331
|
requestFileChangeApproval,
|
|
340
332
|
requestClassifierDecision,
|
|
341
333
|
});
|
|
342
|
-
return;
|
|
334
|
+
return false;
|
|
343
335
|
}
|
|
344
336
|
|
|
345
|
-
if (event.type === "message_end" && event.message) {
|
|
337
|
+
if ((event.type === "message_end" || event.type === "turn_end") && event.message) {
|
|
346
338
|
const msg = event.message as Message;
|
|
347
339
|
currentResult.messages.push(msg);
|
|
348
340
|
|
|
@@ -357,7 +349,7 @@ function processSubagentEventLine(
|
|
|
357
349
|
currentResult.usage.cost += usage.cost?.total || 0;
|
|
358
350
|
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
359
351
|
}
|
|
360
|
-
if (msg.model) currentResult.model = msg.model;
|
|
352
|
+
if (msg.model && (!currentResult.model || msg.model.includes("/"))) currentResult.model = msg.model;
|
|
361
353
|
if (msg.stopReason) currentResult.stopReason = msg.stopReason;
|
|
362
354
|
if (msg.errorMessage) currentResult.errorMessage = msg.errorMessage;
|
|
363
355
|
}
|
|
@@ -368,6 +360,8 @@ function processSubagentEventLine(
|
|
|
368
360
|
currentResult.messages.push(event.message as Message);
|
|
369
361
|
emitUpdate();
|
|
370
362
|
}
|
|
363
|
+
|
|
364
|
+
return event.type === "agent_end";
|
|
371
365
|
}
|
|
372
366
|
|
|
373
367
|
async function waitForFile(filePath: string, signal: AbortSignal | undefined, timeoutMs = 30 * 60 * 1000): Promise<boolean> {
|
|
@@ -453,8 +447,8 @@ async function runSingleAgent(
|
|
|
453
447
|
let wasAborted = false;
|
|
454
448
|
|
|
455
449
|
const exitCode = await new Promise<number>((resolve) => {
|
|
456
|
-
const bundledPaths = (
|
|
457
|
-
const extensionArgs = bundledPaths.flatMap(p => ["--extension", p]);
|
|
450
|
+
const bundledPaths = getBundledExtensionPathsFromEnv();
|
|
451
|
+
const extensionArgs = bundledPaths.flatMap((p) => ["--extension", p]);
|
|
458
452
|
const cliPath = resolveSubagentCliPath(cwd ?? defaultCwd);
|
|
459
453
|
if (!cliPath) {
|
|
460
454
|
currentResult.stderr += "Unable to resolve LSD/GSD CLI path for subagent launch.";
|
|
@@ -466,14 +460,25 @@ async function runSingleAgent(
|
|
|
466
460
|
[cliPath, ...extensionArgs, ...args],
|
|
467
461
|
{ cwd: cwd ?? defaultCwd, shell: false, stdio: ["pipe", "pipe", "pipe"] },
|
|
468
462
|
);
|
|
463
|
+
proc.stdin.end();
|
|
469
464
|
liveSubagentProcesses.add(proc);
|
|
470
465
|
let buffer = "";
|
|
466
|
+
let completionSeen = false;
|
|
471
467
|
|
|
472
468
|
proc.stdout.on("data", (data) => {
|
|
473
469
|
buffer += data.toString();
|
|
474
470
|
const lines = buffer.split("\n");
|
|
475
471
|
buffer = lines.pop() || "";
|
|
476
|
-
for (const line of lines)
|
|
472
|
+
for (const line of lines) {
|
|
473
|
+
if (processSubagentEventLine(line, currentResult, emitUpdate, proc)) {
|
|
474
|
+
completionSeen = true;
|
|
475
|
+
try {
|
|
476
|
+
proc.kill("SIGTERM");
|
|
477
|
+
} catch {
|
|
478
|
+
/* ignore */
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
477
482
|
});
|
|
478
483
|
|
|
479
484
|
proc.stderr.on("data", (data) => {
|
|
@@ -482,8 +487,11 @@ async function runSingleAgent(
|
|
|
482
487
|
|
|
483
488
|
proc.on("close", (code) => {
|
|
484
489
|
liveSubagentProcesses.delete(proc);
|
|
485
|
-
if (buffer.trim())
|
|
486
|
-
|
|
490
|
+
if (buffer.trim()) {
|
|
491
|
+
const completedOnFlush = processSubagentEventLine(buffer, currentResult, emitUpdate, proc);
|
|
492
|
+
completionSeen = completionSeen || completedOnFlush;
|
|
493
|
+
}
|
|
494
|
+
resolve(completionSeen && (code === null || code === 143 || code === 15) ? 0 : (code ?? 0));
|
|
487
495
|
});
|
|
488
496
|
|
|
489
497
|
proc.on("error", () => {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
import type { AgentConfig } from "./agents.js";
|
|
4
|
+
|
|
5
|
+
export function getBundledExtensionPathsFromEnv(env: NodeJS.ProcessEnv = process.env): string[] {
|
|
6
|
+
const rawPaths = [env.GSD_BUNDLED_EXTENSION_PATHS, env.LSD_BUNDLED_EXTENSION_PATHS]
|
|
7
|
+
.map((value) => value?.trim())
|
|
8
|
+
.filter((value): value is string => Boolean(value));
|
|
9
|
+
const unique = new Set<string>();
|
|
10
|
+
for (const raw of rawPaths) {
|
|
11
|
+
for (const entry of raw.split(path.delimiter).map((value) => value.trim()).filter(Boolean)) {
|
|
12
|
+
unique.add(entry);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return Array.from(unique);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function buildSubagentProcessArgs(
|
|
19
|
+
agent: AgentConfig,
|
|
20
|
+
task: string,
|
|
21
|
+
tmpPromptPath: string | null,
|
|
22
|
+
model: string | undefined,
|
|
23
|
+
): string[] {
|
|
24
|
+
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
25
|
+
if (model) args.push("--model", model);
|
|
26
|
+
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
27
|
+
if (tmpPromptPath) args.push("--append-system-prompt", tmpPromptPath);
|
|
28
|
+
args.push(`Task: ${task}`);
|
|
29
|
+
return args;
|
|
30
|
+
}
|