pi-advisor-flow 0.1.9 → 0.2.1
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 +15 -11
- package/extensions/index.ts +26 -1
- package/package.json +33 -21
- package/src/commands.ts +396 -123
- package/src/config.ts +405 -99
- package/src/conversation.ts +217 -47
- package/src/herdr.ts +142 -30
- package/src/session-state.ts +190 -41
- package/src/tools.ts +804 -199
- package/src/ui.ts +376 -106
package/src/config.ts
CHANGED
|
@@ -1,48 +1,124 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CONFIG_DIR_NAME,
|
|
5
|
+
DEFAULT_MAX_BYTES,
|
|
6
|
+
DEFAULT_MAX_LINES,
|
|
7
|
+
type ExtensionContext,
|
|
8
|
+
getAgentDir,
|
|
9
|
+
} from "@earendil-works/pi-coding-agent";
|
|
4
10
|
|
|
5
11
|
export const FALLBACK_EXECUTOR = "aikeys/claude-sonnet-5";
|
|
6
12
|
export const FALLBACK_ADVISOR = "aikeys/claude-fable-5";
|
|
7
13
|
export const DEFAULT_CONTEXT_MAX_CHARS = 15_000;
|
|
8
|
-
// MAX_SAFE_INTEGER represents the complete reconstructed branch (the ALL preset).
|
|
9
14
|
export const MAX_CONTEXT_MAX_CHARS = Number.MAX_SAFE_INTEGER;
|
|
15
|
+
export const DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES = DEFAULT_MAX_LINES;
|
|
16
|
+
export const DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES = DEFAULT_MAX_BYTES;
|
|
17
|
+
export type GateFailureMode =
|
|
18
|
+
| "block-session"
|
|
19
|
+
| "block-tool"
|
|
20
|
+
| "warn-and-continue";
|
|
21
|
+
export const GATE_FAILURE_MODES: GateFailureMode[] = [
|
|
22
|
+
"block-session",
|
|
23
|
+
"block-tool",
|
|
24
|
+
"warn-and-continue",
|
|
25
|
+
];
|
|
10
26
|
|
|
11
27
|
export let executorRef = FALLBACK_EXECUTOR;
|
|
12
28
|
export let advisorRef = FALLBACK_ADVISOR;
|
|
13
|
-
export let executorEffortRef: string | undefined
|
|
14
|
-
export let advisorEffortRef: string | undefined
|
|
29
|
+
export let executorEffortRef: string | undefined;
|
|
30
|
+
export let advisorEffortRef: string | undefined;
|
|
15
31
|
export let contextMaxCharsRef = DEFAULT_CONTEXT_MAX_CHARS;
|
|
16
32
|
export let advisorPlanGateRef = true;
|
|
17
33
|
export let advisorFailureGateRef = true;
|
|
18
34
|
export let advisorCompletionGateRef = true;
|
|
19
|
-
export let advisorCustomInvocationRef: string | undefined
|
|
35
|
+
export let advisorCustomInvocationRef: string | undefined;
|
|
20
36
|
export let advisorCollapseResponsesRef = false;
|
|
21
37
|
export let advisorBlockOnBlockedRef = true;
|
|
22
38
|
export let advisorAutoLoopGateRef = true;
|
|
23
39
|
export let advisorLoopThresholdRef = 3;
|
|
24
|
-
export let advisorMaxCallsPerSessionRef: number | undefined
|
|
40
|
+
export let advisorMaxCallsPerSessionRef: number | undefined;
|
|
25
41
|
export let advisorSessionSummaryRef = true;
|
|
42
|
+
export let advisorFailureModeRef: GateFailureMode = "block-session";
|
|
43
|
+
export let advisorHerdrIntegrationRef = true;
|
|
44
|
+
export let advisorToolResultMaxLinesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES;
|
|
45
|
+
export let advisorToolResultMaxBytesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES;
|
|
26
46
|
|
|
27
|
-
export const setExecutorRef = (ref: string) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export const
|
|
47
|
+
export const setExecutorRef = (ref: string) => {
|
|
48
|
+
executorRef = ref;
|
|
49
|
+
};
|
|
50
|
+
export const setAdvisorRef = (ref: string) => {
|
|
51
|
+
advisorRef = ref;
|
|
52
|
+
};
|
|
53
|
+
export const setExecutorEffortRef = (effort: string | undefined) => {
|
|
54
|
+
executorEffortRef = effort;
|
|
55
|
+
};
|
|
56
|
+
export const setAdvisorEffortRef = (effort: string | undefined) => {
|
|
57
|
+
advisorEffortRef = effort;
|
|
58
|
+
};
|
|
31
59
|
export const isValidContextMaxChars = (value: unknown): value is number =>
|
|
32
|
-
typeof value === "number" &&
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export const
|
|
60
|
+
typeof value === "number" &&
|
|
61
|
+
Number.isSafeInteger(value) &&
|
|
62
|
+
value >= 0 &&
|
|
63
|
+
value <= MAX_CONTEXT_MAX_CHARS;
|
|
64
|
+
export const setContextMaxCharsRef = (value: number) => {
|
|
65
|
+
contextMaxCharsRef = value;
|
|
66
|
+
};
|
|
67
|
+
export const setAdvisorPlanGateRef = (enabled: boolean) => {
|
|
68
|
+
advisorPlanGateRef = enabled;
|
|
69
|
+
};
|
|
70
|
+
export const setAdvisorFailureGateRef = (enabled: boolean) => {
|
|
71
|
+
advisorFailureGateRef = enabled;
|
|
72
|
+
};
|
|
73
|
+
export const setAdvisorCompletionGateRef = (enabled: boolean) => {
|
|
74
|
+
advisorCompletionGateRef = enabled;
|
|
75
|
+
};
|
|
76
|
+
export const setAdvisorCustomInvocationRef = (rule: string | undefined) => {
|
|
77
|
+
advisorCustomInvocationRef = rule?.trim() || undefined;
|
|
78
|
+
};
|
|
79
|
+
export const setAdvisorCollapseResponsesRef = (enabled: boolean) => {
|
|
80
|
+
advisorCollapseResponsesRef = enabled;
|
|
81
|
+
};
|
|
82
|
+
export const setAdvisorBlockOnBlockedRef = (enabled: boolean) => {
|
|
83
|
+
advisorBlockOnBlockedRef = enabled;
|
|
84
|
+
};
|
|
85
|
+
export const setAdvisorAutoLoopGateRef = (enabled: boolean) => {
|
|
86
|
+
advisorAutoLoopGateRef = enabled;
|
|
87
|
+
};
|
|
88
|
+
export const isValidLoopThreshold = (value: unknown): value is number =>
|
|
89
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 2;
|
|
90
|
+
export const setAdvisorLoopThresholdRef = (value: number) => {
|
|
91
|
+
advisorLoopThresholdRef = value;
|
|
92
|
+
};
|
|
93
|
+
export const isValidMaxCallsPerSession = (value: unknown): value is number =>
|
|
94
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
95
|
+
export const setAdvisorMaxCallsPerSessionRef = (value: number | undefined) => {
|
|
96
|
+
advisorMaxCallsPerSessionRef = value;
|
|
97
|
+
};
|
|
98
|
+
export const setAdvisorSessionSummaryRef = (enabled: boolean) => {
|
|
99
|
+
advisorSessionSummaryRef = enabled;
|
|
100
|
+
};
|
|
101
|
+
export const isValidGateFailureMode = (
|
|
102
|
+
value: unknown
|
|
103
|
+
): value is GateFailureMode =>
|
|
104
|
+
typeof value === "string" &&
|
|
105
|
+
GATE_FAILURE_MODES.includes(value as GateFailureMode);
|
|
106
|
+
export const setAdvisorFailureModeRef = (value: GateFailureMode) => {
|
|
107
|
+
advisorFailureModeRef = value;
|
|
108
|
+
};
|
|
109
|
+
export const setAdvisorHerdrIntegrationRef = (enabled: boolean) => {
|
|
110
|
+
advisorHerdrIntegrationRef = enabled;
|
|
111
|
+
};
|
|
112
|
+
export const isValidToolResultMaxLines = (value: unknown): value is number =>
|
|
113
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
114
|
+
export const isValidToolResultMaxBytes = (value: unknown): value is number =>
|
|
115
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
116
|
+
export const setAdvisorToolResultMaxLinesRef = (value: number) => {
|
|
117
|
+
advisorToolResultMaxLinesRef = value;
|
|
118
|
+
};
|
|
119
|
+
export const setAdvisorToolResultMaxBytesRef = (value: number) => {
|
|
120
|
+
advisorToolResultMaxBytesRef = value;
|
|
121
|
+
};
|
|
46
122
|
|
|
47
123
|
export const splitRef = (ref: string): [string, string] => {
|
|
48
124
|
const i = ref.indexOf("/");
|
|
@@ -50,49 +126,182 @@ export const splitRef = (ref: string): [string, string] => {
|
|
|
50
126
|
};
|
|
51
127
|
|
|
52
128
|
export const configPaths = (ctx: ExtensionContext) => [
|
|
53
|
-
ctx.isProjectTrusted()
|
|
129
|
+
ctx.isProjectTrusted()
|
|
130
|
+
? join(ctx.cwd, CONFIG_DIR_NAME, "advisor.json")
|
|
131
|
+
: null,
|
|
54
132
|
join(getAgentDir(), "advisor.json"),
|
|
55
133
|
];
|
|
56
134
|
|
|
57
|
-
|
|
58
|
-
executor?: string;
|
|
135
|
+
export interface AdvisorConfig {
|
|
59
136
|
advisor?: string;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
advisorPlanGate?: boolean;
|
|
64
|
-
advisorFailureGate?: boolean;
|
|
137
|
+
advisorAutoLoopGate?: boolean;
|
|
138
|
+
advisorBlockOnBlocked?: boolean;
|
|
139
|
+
advisorCollapseResponses?: boolean;
|
|
65
140
|
advisorCompletionGate?: boolean;
|
|
66
141
|
advisorCustomInvocation?: string;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
142
|
+
advisorEffort?: string;
|
|
143
|
+
advisorFailureGate?: boolean;
|
|
144
|
+
advisorHerdrIntegration?: boolean;
|
|
70
145
|
advisorLoopThreshold?: number;
|
|
71
146
|
advisorMaxCallsPerSession?: number;
|
|
147
|
+
advisorPlanGate?: boolean;
|
|
72
148
|
advisorSessionSummary?: boolean;
|
|
149
|
+
advisorToolResultMaxBytes?: number;
|
|
150
|
+
advisorToolResultMaxLines?: number;
|
|
151
|
+
contextMaxChars?: number;
|
|
152
|
+
executor?: string;
|
|
153
|
+
executorEffort?: string;
|
|
154
|
+
gateFailureMode?: GateFailureMode;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const CONFIG_KEYS = new Set<keyof AdvisorConfig>([
|
|
158
|
+
"advisor",
|
|
159
|
+
"advisorAutoLoopGate",
|
|
160
|
+
"advisorBlockOnBlocked",
|
|
161
|
+
"advisorCollapseResponses",
|
|
162
|
+
"advisorCompletionGate",
|
|
163
|
+
"advisorCustomInvocation",
|
|
164
|
+
"advisorEffort",
|
|
165
|
+
"advisorFailureGate",
|
|
166
|
+
"advisorHerdrIntegration",
|
|
167
|
+
"advisorLoopThreshold",
|
|
168
|
+
"advisorMaxCallsPerSession",
|
|
169
|
+
"advisorPlanGate",
|
|
170
|
+
"advisorSessionSummary",
|
|
171
|
+
"advisorToolResultMaxBytes",
|
|
172
|
+
"advisorToolResultMaxLines",
|
|
173
|
+
"contextMaxChars",
|
|
174
|
+
"executor",
|
|
175
|
+
"executorEffort",
|
|
176
|
+
"gateFailureMode",
|
|
177
|
+
]);
|
|
178
|
+
const BOOLEAN_CONFIG_KEYS = [
|
|
179
|
+
"advisorPlanGate",
|
|
180
|
+
"advisorFailureGate",
|
|
181
|
+
"advisorCompletionGate",
|
|
182
|
+
"advisorCollapseResponses",
|
|
183
|
+
"advisorBlockOnBlocked",
|
|
184
|
+
"advisorAutoLoopGate",
|
|
185
|
+
"advisorSessionSummary",
|
|
186
|
+
"advisorHerdrIntegration",
|
|
187
|
+
] as const;
|
|
188
|
+
const STRING_CONFIG_KEYS = [
|
|
189
|
+
"executor",
|
|
190
|
+
"advisor",
|
|
191
|
+
"executorEffort",
|
|
192
|
+
"advisorEffort",
|
|
193
|
+
"advisorCustomInvocation",
|
|
194
|
+
] as const;
|
|
195
|
+
const ARGUMENT_WHITESPACE = /\s+/;
|
|
196
|
+
|
|
197
|
+
type ConfigRecord = Record<string, unknown>;
|
|
198
|
+
|
|
199
|
+
const invalidConfigValue = (
|
|
200
|
+
path: string,
|
|
201
|
+
key: string,
|
|
202
|
+
accepted: string
|
|
203
|
+
): never => {
|
|
204
|
+
throw new TypeError(
|
|
205
|
+
`Invalid advisor configuration at ${path}, key ${JSON.stringify(key)}: expected ${accepted}.`
|
|
206
|
+
);
|
|
73
207
|
};
|
|
74
208
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
&& (config.advisorFailureGate === undefined || typeof config.advisorFailureGate === "boolean")
|
|
85
|
-
&& (config.advisorCompletionGate === undefined || typeof config.advisorCompletionGate === "boolean")
|
|
86
|
-
&& (config.advisorCustomInvocation === undefined || typeof config.advisorCustomInvocation === "string")
|
|
87
|
-
&& (config.advisorCollapseResponses === undefined || typeof config.advisorCollapseResponses === "boolean")
|
|
88
|
-
&& (config.advisorBlockOnBlocked === undefined || typeof config.advisorBlockOnBlocked === "boolean")
|
|
89
|
-
&& (config.advisorAutoLoopGate === undefined || typeof config.advisorAutoLoopGate === "boolean")
|
|
90
|
-
&& (config.advisorLoopThreshold === undefined || isValidLoopThreshold(config.advisorLoopThreshold))
|
|
91
|
-
&& (config.advisorMaxCallsPerSession === undefined || isValidMaxCallsPerSession(config.advisorMaxCallsPerSession))
|
|
92
|
-
&& (config.advisorSessionSummary === undefined || typeof config.advisorSessionSummary === "boolean");
|
|
209
|
+
const validateKnownKeys = (config: ConfigRecord, path: string) => {
|
|
210
|
+
const unknownKeys = Object.keys(config).filter(
|
|
211
|
+
(key) => !CONFIG_KEYS.has(key as keyof AdvisorConfig)
|
|
212
|
+
);
|
|
213
|
+
if (unknownKeys.length > 0) {
|
|
214
|
+
throw new TypeError(
|
|
215
|
+
`Invalid advisor configuration at ${path}: unknown key(s) ${unknownKeys.map((key) => JSON.stringify(key)).join(", ")}. Remove them or upgrade pi-advisor.`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
93
218
|
};
|
|
94
219
|
|
|
95
|
-
|
|
220
|
+
const validateStringValues = (config: ConfigRecord, path: string) => {
|
|
221
|
+
for (const key of STRING_CONFIG_KEYS) {
|
|
222
|
+
if (config[key] !== undefined && typeof config[key] !== "string") {
|
|
223
|
+
invalidConfigValue(
|
|
224
|
+
path,
|
|
225
|
+
key,
|
|
226
|
+
key === "executor" || key === "advisor"
|
|
227
|
+
? "a provider/model string"
|
|
228
|
+
: "a string"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const validateBooleanValues = (config: ConfigRecord, path: string) => {
|
|
235
|
+
for (const key of BOOLEAN_CONFIG_KEYS) {
|
|
236
|
+
if (config[key] !== undefined && typeof config[key] !== "boolean") {
|
|
237
|
+
invalidConfigValue(path, key, "true or false");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const validateNumericValues = (config: ConfigRecord, path: string) => {
|
|
243
|
+
const numericRules: [
|
|
244
|
+
keyof AdvisorConfig,
|
|
245
|
+
(value: unknown) => boolean,
|
|
246
|
+
string,
|
|
247
|
+
][] = [
|
|
248
|
+
[
|
|
249
|
+
"contextMaxChars",
|
|
250
|
+
isValidContextMaxChars,
|
|
251
|
+
`a safe integer from 0 through ${MAX_CONTEXT_MAX_CHARS}`,
|
|
252
|
+
],
|
|
253
|
+
[
|
|
254
|
+
"advisorLoopThreshold",
|
|
255
|
+
isValidLoopThreshold,
|
|
256
|
+
"a safe integer of at least 2",
|
|
257
|
+
],
|
|
258
|
+
[
|
|
259
|
+
"advisorMaxCallsPerSession",
|
|
260
|
+
isValidMaxCallsPerSession,
|
|
261
|
+
"a non-negative safe integer",
|
|
262
|
+
],
|
|
263
|
+
[
|
|
264
|
+
"advisorToolResultMaxLines",
|
|
265
|
+
isValidToolResultMaxLines,
|
|
266
|
+
"a non-negative safe integer",
|
|
267
|
+
],
|
|
268
|
+
[
|
|
269
|
+
"advisorToolResultMaxBytes",
|
|
270
|
+
isValidToolResultMaxBytes,
|
|
271
|
+
"a non-negative safe integer",
|
|
272
|
+
],
|
|
273
|
+
];
|
|
274
|
+
for (const [key, isValid, description] of numericRules) {
|
|
275
|
+
if (config[key] !== undefined && !isValid(config[key])) {
|
|
276
|
+
invalidConfigValue(path, key, description);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export const validateConfig = (
|
|
282
|
+
value: unknown,
|
|
283
|
+
path = "advisor.json"
|
|
284
|
+
): value is AdvisorConfig => {
|
|
285
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
286
|
+
throw new TypeError(
|
|
287
|
+
`Invalid advisor configuration at ${path}: expected a JSON object.`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const config = value as ConfigRecord;
|
|
291
|
+
validateKnownKeys(config, path);
|
|
292
|
+
validateStringValues(config, path);
|
|
293
|
+
validateBooleanValues(config, path);
|
|
294
|
+
validateNumericValues(config, path);
|
|
295
|
+
if (
|
|
296
|
+
config.gateFailureMode !== undefined &&
|
|
297
|
+
!isValidGateFailureMode(config.gateFailureMode)
|
|
298
|
+
) {
|
|
299
|
+
invalidConfigValue(path, "gateFailureMode", GATE_FAILURE_MODES.join(", "));
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const resetDefaults = () => {
|
|
96
305
|
executorRef = FALLBACK_EXECUTOR;
|
|
97
306
|
advisorRef = FALLBACK_ADVISOR;
|
|
98
307
|
executorEffortRef = undefined;
|
|
@@ -108,62 +317,158 @@ export const loadConfig = (ctx: ExtensionContext) => {
|
|
|
108
317
|
advisorLoopThresholdRef = 3;
|
|
109
318
|
advisorMaxCallsPerSessionRef = undefined;
|
|
110
319
|
advisorSessionSummaryRef = true;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
320
|
+
advisorFailureModeRef = "block-session";
|
|
321
|
+
advisorHerdrIntegrationRef = true;
|
|
322
|
+
advisorToolResultMaxLinesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES;
|
|
323
|
+
advisorToolResultMaxBytesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const applyOptionalConfig = <Key extends keyof AdvisorConfig>(
|
|
327
|
+
config: AdvisorConfig,
|
|
328
|
+
key: Key,
|
|
329
|
+
apply: (value: NonNullable<AdvisorConfig[Key]>) => void
|
|
330
|
+
) => {
|
|
331
|
+
const value = config[key];
|
|
332
|
+
if (value !== undefined) {
|
|
333
|
+
apply(value as NonNullable<AdvisorConfig[Key]>);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const applyNonEmptyStringConfig = (
|
|
338
|
+
value: string | undefined,
|
|
339
|
+
apply: (value: string) => void
|
|
340
|
+
) => {
|
|
341
|
+
if (value) {
|
|
342
|
+
apply(value);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const applyConfig = (config: AdvisorConfig) => {
|
|
347
|
+
applyNonEmptyStringConfig(config.executor, setExecutorRef);
|
|
348
|
+
applyNonEmptyStringConfig(config.advisor, setAdvisorRef);
|
|
349
|
+
applyNonEmptyStringConfig(config.executorEffort, setExecutorEffortRef);
|
|
350
|
+
applyNonEmptyStringConfig(config.advisorEffort, setAdvisorEffortRef);
|
|
351
|
+
applyOptionalConfig(config, "contextMaxChars", setContextMaxCharsRef);
|
|
352
|
+
applyOptionalConfig(config, "advisorPlanGate", setAdvisorPlanGateRef);
|
|
353
|
+
applyOptionalConfig(config, "advisorFailureGate", setAdvisorFailureGateRef);
|
|
354
|
+
applyOptionalConfig(
|
|
355
|
+
config,
|
|
356
|
+
"advisorCompletionGate",
|
|
357
|
+
setAdvisorCompletionGateRef
|
|
358
|
+
);
|
|
359
|
+
applyOptionalConfig(
|
|
360
|
+
config,
|
|
361
|
+
"advisorCustomInvocation",
|
|
362
|
+
setAdvisorCustomInvocationRef
|
|
363
|
+
);
|
|
364
|
+
applyOptionalConfig(
|
|
365
|
+
config,
|
|
366
|
+
"advisorCollapseResponses",
|
|
367
|
+
setAdvisorCollapseResponsesRef
|
|
368
|
+
);
|
|
369
|
+
applyOptionalConfig(
|
|
370
|
+
config,
|
|
371
|
+
"advisorBlockOnBlocked",
|
|
372
|
+
setAdvisorBlockOnBlockedRef
|
|
373
|
+
);
|
|
374
|
+
applyOptionalConfig(config, "advisorAutoLoopGate", setAdvisorAutoLoopGateRef);
|
|
375
|
+
applyOptionalConfig(
|
|
376
|
+
config,
|
|
377
|
+
"advisorLoopThreshold",
|
|
378
|
+
setAdvisorLoopThresholdRef
|
|
379
|
+
);
|
|
380
|
+
applyOptionalConfig(
|
|
381
|
+
config,
|
|
382
|
+
"advisorMaxCallsPerSession",
|
|
383
|
+
setAdvisorMaxCallsPerSessionRef
|
|
384
|
+
);
|
|
385
|
+
applyOptionalConfig(
|
|
386
|
+
config,
|
|
387
|
+
"advisorSessionSummary",
|
|
388
|
+
setAdvisorSessionSummaryRef
|
|
389
|
+
);
|
|
390
|
+
applyOptionalConfig(config, "gateFailureMode", setAdvisorFailureModeRef);
|
|
391
|
+
applyOptionalConfig(
|
|
392
|
+
config,
|
|
393
|
+
"advisorHerdrIntegration",
|
|
394
|
+
setAdvisorHerdrIntegrationRef
|
|
395
|
+
);
|
|
396
|
+
applyOptionalConfig(
|
|
397
|
+
config,
|
|
398
|
+
"advisorToolResultMaxLines",
|
|
399
|
+
setAdvisorToolResultMaxLinesRef
|
|
400
|
+
);
|
|
401
|
+
applyOptionalConfig(
|
|
402
|
+
config,
|
|
403
|
+
"advisorToolResultMaxBytes",
|
|
404
|
+
setAdvisorToolResultMaxBytesRef
|
|
405
|
+
);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const readConfig = (path: string): AdvisorConfig => {
|
|
409
|
+
try {
|
|
410
|
+
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
411
|
+
validateConfig(config, path);
|
|
412
|
+
return config;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
export const loadConfig = (ctx: ExtensionContext) => {
|
|
419
|
+
resetDefaults();
|
|
420
|
+
const path = configPaths(ctx).find(
|
|
421
|
+
(candidate): candidate is string =>
|
|
422
|
+
candidate !== null && existsSync(candidate)
|
|
423
|
+
);
|
|
424
|
+
if (!path) {
|
|
425
|
+
return null;
|
|
135
426
|
}
|
|
136
|
-
|
|
427
|
+
applyConfig(readConfig(path));
|
|
428
|
+
return path;
|
|
137
429
|
};
|
|
138
430
|
|
|
139
431
|
export const saveConfig = (ctx: ExtensionContext) => {
|
|
140
432
|
const project = join(ctx.cwd, CONFIG_DIR_NAME, "advisor.json");
|
|
141
|
-
const path =
|
|
142
|
-
|
|
433
|
+
const path =
|
|
434
|
+
ctx.isProjectTrusted() && existsSync(project)
|
|
435
|
+
? project
|
|
436
|
+
: join(getAgentDir(), "advisor.json");
|
|
143
437
|
let existing: Record<string, unknown> = {};
|
|
144
438
|
try {
|
|
145
439
|
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
146
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
440
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
441
|
+
existing = parsed as Record<string, unknown>;
|
|
442
|
+
}
|
|
147
443
|
} catch {
|
|
148
|
-
|
|
444
|
+
/* replace a missing or malformed file */
|
|
445
|
+
}
|
|
446
|
+
if (advisorMaxCallsPerSessionRef === undefined) {
|
|
447
|
+
existing.advisorMaxCallsPerSession = undefined;
|
|
149
448
|
}
|
|
150
449
|
const data = {
|
|
151
450
|
...existing,
|
|
152
|
-
executor: executorRef,
|
|
153
451
|
advisor: advisorRef,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
advisorPlanGate: advisorPlanGateRef,
|
|
158
|
-
advisorFailureGate: advisorFailureGateRef,
|
|
452
|
+
advisorAutoLoopGate: advisorAutoLoopGateRef,
|
|
453
|
+
advisorBlockOnBlocked: advisorBlockOnBlockedRef,
|
|
454
|
+
advisorCollapseResponses: advisorCollapseResponsesRef,
|
|
159
455
|
advisorCompletionGate: advisorCompletionGateRef,
|
|
160
456
|
advisorCustomInvocation: advisorCustomInvocationRef,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
advisorAutoLoopGate: advisorAutoLoopGateRef,
|
|
457
|
+
advisorEffort: advisorEffortRef,
|
|
458
|
+
advisorFailureGate: advisorFailureGateRef,
|
|
164
459
|
advisorLoopThreshold: advisorLoopThresholdRef,
|
|
165
|
-
|
|
460
|
+
advisorPlanGate: advisorPlanGateRef,
|
|
461
|
+
contextMaxChars: contextMaxCharsRef,
|
|
462
|
+
executor: executorRef,
|
|
463
|
+
executorEffort: executorEffortRef,
|
|
464
|
+
...(advisorMaxCallsPerSessionRef === undefined
|
|
465
|
+
? {}
|
|
466
|
+
: { advisorMaxCallsPerSession: advisorMaxCallsPerSessionRef }),
|
|
467
|
+
advisorHerdrIntegration: advisorHerdrIntegrationRef,
|
|
166
468
|
advisorSessionSummary: advisorSessionSummaryRef,
|
|
469
|
+
advisorToolResultMaxBytes: advisorToolResultMaxBytesRef,
|
|
470
|
+
advisorToolResultMaxLines: advisorToolResultMaxLinesRef,
|
|
471
|
+
gateFailureMode: advisorFailureModeRef,
|
|
167
472
|
};
|
|
168
473
|
writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`);
|
|
169
474
|
return path;
|
|
@@ -173,11 +478,14 @@ export const parseArgs = (args: string): string | undefined => {
|
|
|
173
478
|
let nextExecutor = executorRef;
|
|
174
479
|
let nextAdvisor = advisorRef;
|
|
175
480
|
let nextContextMaxChars = contextMaxCharsRef;
|
|
176
|
-
|
|
177
|
-
for (const token of args.trim().split(/\s+/).filter(Boolean)) {
|
|
481
|
+
for (const token of args.trim().split(ARGUMENT_WHITESPACE).filter(Boolean)) {
|
|
178
482
|
const [key, value] = token.split("=");
|
|
179
|
-
if (key === "executor" && value)
|
|
180
|
-
|
|
483
|
+
if (key === "executor" && value) {
|
|
484
|
+
nextExecutor = value;
|
|
485
|
+
}
|
|
486
|
+
if (key === "advisor" && value) {
|
|
487
|
+
nextAdvisor = value;
|
|
488
|
+
}
|
|
181
489
|
if (key === "contextMaxChars") {
|
|
182
490
|
const parsed = Number(value);
|
|
183
491
|
if (!isValidContextMaxChars(parsed)) {
|
|
@@ -186,9 +494,7 @@ export const parseArgs = (args: string): string | undefined => {
|
|
|
186
494
|
nextContextMaxChars = parsed;
|
|
187
495
|
}
|
|
188
496
|
}
|
|
189
|
-
|
|
190
497
|
executorRef = nextExecutor;
|
|
191
498
|
advisorRef = nextAdvisor;
|
|
192
499
|
contextMaxCharsRef = nextContextMaxChars;
|
|
193
|
-
return undefined;
|
|
194
500
|
};
|