pi-advisor-flow 0.1.9 → 0.2.0

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/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 { CONFIG_DIR_NAME, type ExtensionContext, getAgentDir } from "@earendil-works/pi-coding-agent";
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 = undefined;
14
- export let advisorEffortRef: string | undefined = 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 = 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 = 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) => { executorRef = ref; };
28
- export const setAdvisorRef = (ref: string) => { advisorRef = ref; };
29
- export const setExecutorEffortRef = (effort: string | undefined) => { executorEffortRef = effort; };
30
- export const setAdvisorEffortRef = (effort: string | undefined) => { advisorEffortRef = effort; };
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" && Number.isSafeInteger(value) && value >= 0 && value <= MAX_CONTEXT_MAX_CHARS;
33
- export const setContextMaxCharsRef = (value: number) => { contextMaxCharsRef = value; };
34
- export const setAdvisorPlanGateRef = (enabled: boolean) => { advisorPlanGateRef = enabled; };
35
- export const setAdvisorFailureGateRef = (enabled: boolean) => { advisorFailureGateRef = enabled; };
36
- export const setAdvisorCompletionGateRef = (enabled: boolean) => { advisorCompletionGateRef = enabled; };
37
- export const setAdvisorCustomInvocationRef = (rule: string | undefined) => { advisorCustomInvocationRef = rule?.trim() || undefined; };
38
- export const setAdvisorCollapseResponsesRef = (enabled: boolean) => { advisorCollapseResponsesRef = enabled; };
39
- export const setAdvisorBlockOnBlockedRef = (enabled: boolean) => { advisorBlockOnBlockedRef = enabled; };
40
- export const setAdvisorAutoLoopGateRef = (enabled: boolean) => { advisorAutoLoopGateRef = enabled; };
41
- export const isValidLoopThreshold = (value: unknown): value is number => typeof value === "number" && Number.isSafeInteger(value) && value >= 2;
42
- export const setAdvisorLoopThresholdRef = (value: number) => { advisorLoopThresholdRef = value; };
43
- export const isValidMaxCallsPerSession = (value: unknown): value is number => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
44
- export const setAdvisorMaxCallsPerSessionRef = (value: number | undefined) => { advisorMaxCallsPerSessionRef = value; };
45
- export const setAdvisorSessionSummaryRef = (enabled: boolean) => { advisorSessionSummaryRef = enabled; };
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,160 @@ export const splitRef = (ref: string): [string, string] => {
50
126
  };
51
127
 
52
128
  export const configPaths = (ctx: ExtensionContext) => [
53
- ctx.isProjectTrusted() ? join(ctx.cwd, CONFIG_DIR_NAME, "advisor.json") : null,
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
- type AdvisorConfig = {
58
- executor?: string;
135
+ export interface AdvisorConfig {
59
136
  advisor?: string;
60
- executorEffort?: string;
61
- advisorEffort?: string;
62
- contextMaxChars?: number;
63
- advisorPlanGate?: boolean;
64
- advisorFailureGate?: boolean;
137
+ advisorAutoLoopGate?: boolean;
138
+ advisorBlockOnBlocked?: boolean;
139
+ advisorCollapseResponses?: boolean;
65
140
  advisorCompletionGate?: boolean;
66
141
  advisorCustomInvocation?: string;
67
- advisorCollapseResponses?: boolean;
68
- advisorBlockOnBlocked?: boolean;
69
- advisorAutoLoopGate?: boolean;
142
+ advisorEffort?: string;
143
+ advisorFailureGate?: boolean;
144
+ advisorHerdrIntegration?: boolean;
70
145
  advisorLoopThreshold?: number;
71
146
  advisorMaxCallsPerSession?: number;
147
+ advisorPlanGate?: boolean;
72
148
  advisorSessionSummary?: boolean;
73
- };
149
+ advisorToolResultMaxBytes?: number;
150
+ advisorToolResultMaxLines?: number;
151
+ contextMaxChars?: number;
152
+ executor?: string;
153
+ executorEffort?: string;
154
+ gateFailureMode?: GateFailureMode;
155
+ }
74
156
 
75
- const isValidConfig = (value: unknown): value is AdvisorConfig => {
76
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
157
+ const CONFIG_KEYS = new Set(
158
+ Object.keys({
159
+ advisor: true,
160
+ advisorAutoLoopGate: true,
161
+ advisorBlockOnBlocked: true,
162
+ advisorCollapseResponses: true,
163
+ advisorCompletionGate: true,
164
+ advisorCustomInvocation: true,
165
+ advisorEffort: true,
166
+ advisorFailureGate: true,
167
+ advisorHerdrIntegration: true,
168
+ advisorLoopThreshold: true,
169
+ advisorMaxCallsPerSession: true,
170
+ advisorPlanGate: true,
171
+ advisorSessionSummary: true,
172
+ advisorToolResultMaxBytes: true,
173
+ advisorToolResultMaxLines: true,
174
+ contextMaxChars: true,
175
+ executor: true,
176
+ executorEffort: true,
177
+ gateFailureMode: true,
178
+ })
179
+ );
180
+
181
+ export const validateConfig = (
182
+ value: unknown,
183
+ path = "advisor.json"
184
+ ): value is AdvisorConfig => {
185
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
186
+ throw new TypeError(
187
+ `Invalid advisor configuration at ${path}: expected a JSON object.`
188
+ );
189
+ }
77
190
  const config = value as Record<string, unknown>;
78
- return (config.executor === undefined || typeof config.executor === "string")
79
- && (config.advisor === undefined || typeof config.advisor === "string")
80
- && (config.executorEffort === undefined || typeof config.executorEffort === "string")
81
- && (config.advisorEffort === undefined || typeof config.advisorEffort === "string")
82
- && (config.contextMaxChars === undefined || isValidContextMaxChars(config.contextMaxChars))
83
- && (config.advisorPlanGate === undefined || typeof config.advisorPlanGate === "boolean")
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");
191
+ const unknown = Object.keys(config).filter((key) => !CONFIG_KEYS.has(key));
192
+ if (unknown.length) {
193
+ throw new TypeError(
194
+ `Invalid advisor configuration at ${path}: unknown key(s) ${unknown.map((key) => JSON.stringify(key)).join(", ")}. Remove them or upgrade pi-advisor.`
195
+ );
196
+ }
197
+ const invalid = (key: string, accepted: string) => {
198
+ throw new TypeError(
199
+ `Invalid advisor configuration at ${path}, key ${JSON.stringify(key)}: expected ${accepted}.`
200
+ );
201
+ };
202
+ if (config.executor !== undefined && typeof config.executor !== "string") {
203
+ invalid("executor", "a provider/model string");
204
+ }
205
+ if (config.advisor !== undefined && typeof config.advisor !== "string") {
206
+ invalid("advisor", "a provider/model string");
207
+ }
208
+ if (
209
+ config.executorEffort !== undefined &&
210
+ typeof config.executorEffort !== "string"
211
+ ) {
212
+ invalid("executorEffort", "a string");
213
+ }
214
+ if (
215
+ config.advisorEffort !== undefined &&
216
+ typeof config.advisorEffort !== "string"
217
+ ) {
218
+ invalid("advisorEffort", "a string");
219
+ }
220
+ if (
221
+ config.contextMaxChars !== undefined &&
222
+ !isValidContextMaxChars(config.contextMaxChars)
223
+ ) {
224
+ invalid(
225
+ "contextMaxChars",
226
+ `a safe integer from 0 through ${MAX_CONTEXT_MAX_CHARS}`
227
+ );
228
+ }
229
+ for (const key of [
230
+ "advisorPlanGate",
231
+ "advisorFailureGate",
232
+ "advisorCompletionGate",
233
+ "advisorCollapseResponses",
234
+ "advisorBlockOnBlocked",
235
+ "advisorAutoLoopGate",
236
+ "advisorSessionSummary",
237
+ "advisorHerdrIntegration",
238
+ ]) {
239
+ if (config[key] !== undefined && typeof config[key] !== "boolean") {
240
+ invalid(key, "true or false");
241
+ }
242
+ }
243
+ if (
244
+ config.advisorCustomInvocation !== undefined &&
245
+ typeof config.advisorCustomInvocation !== "string"
246
+ ) {
247
+ invalid("advisorCustomInvocation", "a string");
248
+ }
249
+ if (
250
+ config.advisorLoopThreshold !== undefined &&
251
+ !isValidLoopThreshold(config.advisorLoopThreshold)
252
+ ) {
253
+ invalid("advisorLoopThreshold", "a safe integer of at least 2");
254
+ }
255
+ if (
256
+ config.advisorMaxCallsPerSession !== undefined &&
257
+ !isValidMaxCallsPerSession(config.advisorMaxCallsPerSession)
258
+ ) {
259
+ invalid("advisorMaxCallsPerSession", "a non-negative safe integer");
260
+ }
261
+ if (
262
+ config.gateFailureMode !== undefined &&
263
+ !isValidGateFailureMode(config.gateFailureMode)
264
+ ) {
265
+ invalid("gateFailureMode", GATE_FAILURE_MODES.join(", "));
266
+ }
267
+ if (
268
+ config.advisorToolResultMaxLines !== undefined &&
269
+ !isValidToolResultMaxLines(config.advisorToolResultMaxLines)
270
+ ) {
271
+ invalid("advisorToolResultMaxLines", "a non-negative safe integer");
272
+ }
273
+ if (
274
+ config.advisorToolResultMaxBytes !== undefined &&
275
+ !isValidToolResultMaxBytes(config.advisorToolResultMaxBytes)
276
+ ) {
277
+ invalid("advisorToolResultMaxBytes", "a non-negative safe integer");
278
+ }
279
+ return true;
93
280
  };
94
281
 
95
- export const loadConfig = (ctx: ExtensionContext) => {
282
+ const resetDefaults = () => {
96
283
  executorRef = FALLBACK_EXECUTOR;
97
284
  advisorRef = FALLBACK_ADVISOR;
98
285
  executorEffortRef = undefined;
@@ -108,62 +295,125 @@ export const loadConfig = (ctx: ExtensionContext) => {
108
295
  advisorLoopThresholdRef = 3;
109
296
  advisorMaxCallsPerSessionRef = undefined;
110
297
  advisorSessionSummaryRef = true;
298
+ advisorFailureModeRef = "block-session";
299
+ advisorHerdrIntegrationRef = true;
300
+ advisorToolResultMaxLinesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES;
301
+ advisorToolResultMaxBytesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES;
302
+ };
303
+
304
+ export const loadConfig = (ctx: ExtensionContext) => {
305
+ resetDefaults();
111
306
  for (const path of configPaths(ctx)) {
112
- if (!path || !existsSync(path)) continue;
307
+ if (!(path && existsSync(path))) {
308
+ continue;
309
+ }
310
+ let config: AdvisorConfig;
113
311
  try {
114
- const config = JSON.parse(readFileSync(path, "utf8"));
115
- if (!isValidConfig(config)) throw new TypeError("Invalid advisor configuration");
116
- if (config.executor) executorRef = config.executor;
117
- if (config.advisor) advisorRef = config.advisor;
118
- if (config.executorEffort) executorEffortRef = config.executorEffort;
119
- if (config.advisorEffort) advisorEffortRef = config.advisorEffort;
120
- if (isValidContextMaxChars(config.contextMaxChars)) contextMaxCharsRef = config.contextMaxChars;
121
- if (typeof config.advisorPlanGate === "boolean") advisorPlanGateRef = config.advisorPlanGate;
122
- if (typeof config.advisorFailureGate === "boolean") advisorFailureGateRef = config.advisorFailureGate;
123
- if (typeof config.advisorCompletionGate === "boolean") advisorCompletionGateRef = config.advisorCompletionGate;
124
- if (typeof config.advisorCustomInvocation === "string") advisorCustomInvocationRef = config.advisorCustomInvocation || undefined;
125
- if (typeof config.advisorCollapseResponses === "boolean") advisorCollapseResponsesRef = config.advisorCollapseResponses;
126
- if (typeof config.advisorBlockOnBlocked === "boolean") advisorBlockOnBlockedRef = config.advisorBlockOnBlocked;
127
- if (typeof config.advisorAutoLoopGate === "boolean") advisorAutoLoopGateRef = config.advisorAutoLoopGate;
128
- if (isValidLoopThreshold(config.advisorLoopThreshold)) advisorLoopThresholdRef = config.advisorLoopThreshold;
129
- if (isValidMaxCallsPerSession(config.advisorMaxCallsPerSession)) advisorMaxCallsPerSessionRef = config.advisorMaxCallsPerSession;
130
- if (typeof config.advisorSessionSummary === "boolean") advisorSessionSummaryRef = config.advisorSessionSummary;
131
- return path;
132
- } catch {
133
- // Ignore malformed config and keep looking for a valid fallback.
312
+ config = JSON.parse(readFileSync(path, "utf8")) as AdvisorConfig;
313
+ validateConfig(config, path);
314
+ } catch (error) {
315
+ throw error instanceof Error ? error : new Error(String(error));
316
+ }
317
+ if (config.executor) {
318
+ executorRef = config.executor;
319
+ }
320
+ if (config.advisor) {
321
+ advisorRef = config.advisor;
322
+ }
323
+ if (config.executorEffort) {
324
+ executorEffortRef = config.executorEffort;
325
+ }
326
+ if (config.advisorEffort) {
327
+ advisorEffortRef = config.advisorEffort;
328
+ }
329
+ if (config.contextMaxChars !== undefined) {
330
+ contextMaxCharsRef = config.contextMaxChars;
331
+ }
332
+ if (config.advisorPlanGate !== undefined) {
333
+ advisorPlanGateRef = config.advisorPlanGate;
334
+ }
335
+ if (config.advisorFailureGate !== undefined) {
336
+ advisorFailureGateRef = config.advisorFailureGate;
337
+ }
338
+ if (config.advisorCompletionGate !== undefined) {
339
+ advisorCompletionGateRef = config.advisorCompletionGate;
340
+ }
341
+ if (config.advisorCustomInvocation !== undefined) {
342
+ advisorCustomInvocationRef = config.advisorCustomInvocation || undefined;
343
+ }
344
+ if (config.advisorCollapseResponses !== undefined) {
345
+ advisorCollapseResponsesRef = config.advisorCollapseResponses;
346
+ }
347
+ if (config.advisorBlockOnBlocked !== undefined) {
348
+ advisorBlockOnBlockedRef = config.advisorBlockOnBlocked;
349
+ }
350
+ if (config.advisorAutoLoopGate !== undefined) {
351
+ advisorAutoLoopGateRef = config.advisorAutoLoopGate;
352
+ }
353
+ if (config.advisorLoopThreshold !== undefined) {
354
+ advisorLoopThresholdRef = config.advisorLoopThreshold;
355
+ }
356
+ if (config.advisorMaxCallsPerSession !== undefined) {
357
+ advisorMaxCallsPerSessionRef = config.advisorMaxCallsPerSession;
358
+ }
359
+ if (config.advisorSessionSummary !== undefined) {
360
+ advisorSessionSummaryRef = config.advisorSessionSummary;
361
+ }
362
+ if (config.gateFailureMode !== undefined) {
363
+ advisorFailureModeRef = config.gateFailureMode;
364
+ }
365
+ if (config.advisorHerdrIntegration !== undefined) {
366
+ advisorHerdrIntegrationRef = config.advisorHerdrIntegration;
134
367
  }
368
+ if (config.advisorToolResultMaxLines !== undefined) {
369
+ advisorToolResultMaxLinesRef = config.advisorToolResultMaxLines;
370
+ }
371
+ if (config.advisorToolResultMaxBytes !== undefined) {
372
+ advisorToolResultMaxBytesRef = config.advisorToolResultMaxBytes;
373
+ }
374
+ return path;
135
375
  }
136
376
  return null;
137
377
  };
138
378
 
139
379
  export const saveConfig = (ctx: ExtensionContext) => {
140
380
  const project = join(ctx.cwd, CONFIG_DIR_NAME, "advisor.json");
141
- const path = ctx.isProjectTrusted() && existsSync(project) ? project : join(getAgentDir(), "advisor.json");
142
- // Preserve settings owned by future versions or other tools that share this file.
381
+ const path =
382
+ ctx.isProjectTrusted() && existsSync(project)
383
+ ? project
384
+ : join(getAgentDir(), "advisor.json");
143
385
  let existing: Record<string, unknown> = {};
144
386
  try {
145
387
  const parsed = JSON.parse(readFileSync(path, "utf8"));
146
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) existing = parsed as Record<string, unknown>;
388
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
389
+ existing = parsed as Record<string, unknown>;
390
+ }
147
391
  } catch {
148
- // A missing or malformed file is safely replaced with the current settings.
392
+ /* replace a missing or malformed file */
149
393
  }
150
394
  const data = {
151
395
  ...existing,
152
- executor: executorRef,
153
396
  advisor: advisorRef,
154
- executorEffort: executorEffortRef,
155
- advisorEffort: advisorEffortRef,
156
- contextMaxChars: contextMaxCharsRef,
157
- advisorPlanGate: advisorPlanGateRef,
158
- advisorFailureGate: advisorFailureGateRef,
397
+ advisorAutoLoopGate: advisorAutoLoopGateRef,
398
+ advisorBlockOnBlocked: advisorBlockOnBlockedRef,
399
+ advisorCollapseResponses: advisorCollapseResponsesRef,
159
400
  advisorCompletionGate: advisorCompletionGateRef,
160
401
  advisorCustomInvocation: advisorCustomInvocationRef,
161
- advisorCollapseResponses: advisorCollapseResponsesRef,
162
- advisorBlockOnBlocked: advisorBlockOnBlockedRef,
163
- advisorAutoLoopGate: advisorAutoLoopGateRef,
402
+ advisorEffort: advisorEffortRef,
403
+ advisorFailureGate: advisorFailureGateRef,
164
404
  advisorLoopThreshold: advisorLoopThresholdRef,
165
- ...(advisorMaxCallsPerSessionRef === undefined ? {} : { advisorMaxCallsPerSession: advisorMaxCallsPerSessionRef }),
405
+ advisorPlanGate: advisorPlanGateRef,
406
+ contextMaxChars: contextMaxCharsRef,
407
+ executor: executorRef,
408
+ executorEffort: executorEffortRef,
409
+ ...(advisorMaxCallsPerSessionRef === undefined
410
+ ? {}
411
+ : { advisorMaxCallsPerSession: advisorMaxCallsPerSessionRef }),
412
+ advisorHerdrIntegration: advisorHerdrIntegrationRef,
166
413
  advisorSessionSummary: advisorSessionSummaryRef,
414
+ advisorToolResultMaxBytes: advisorToolResultMaxBytesRef,
415
+ advisorToolResultMaxLines: advisorToolResultMaxLinesRef,
416
+ gateFailureMode: advisorFailureModeRef,
167
417
  };
168
418
  writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`);
169
419
  return path;
@@ -173,11 +423,14 @@ export const parseArgs = (args: string): string | undefined => {
173
423
  let nextExecutor = executorRef;
174
424
  let nextAdvisor = advisorRef;
175
425
  let nextContextMaxChars = contextMaxCharsRef;
176
-
177
426
  for (const token of args.trim().split(/\s+/).filter(Boolean)) {
178
427
  const [key, value] = token.split("=");
179
- if (key === "executor" && value) nextExecutor = value;
180
- if (key === "advisor" && value) nextAdvisor = value;
428
+ if (key === "executor" && value) {
429
+ nextExecutor = value;
430
+ }
431
+ if (key === "advisor" && value) {
432
+ nextAdvisor = value;
433
+ }
181
434
  if (key === "contextMaxChars") {
182
435
  const parsed = Number(value);
183
436
  if (!isValidContextMaxChars(parsed)) {
@@ -186,9 +439,7 @@ export const parseArgs = (args: string): string | undefined => {
186
439
  nextContextMaxChars = parsed;
187
440
  }
188
441
  }
189
-
190
442
  executorRef = nextExecutor;
191
443
  advisorRef = nextAdvisor;
192
444
  contextMaxCharsRef = nextContextMaxChars;
193
- return undefined;
194
445
  };