pi-advisor-flow 0.5.6 → 0.6.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.js +1371 -1128
  3. package/extensions/index.ts +9 -9
  4. package/package.json +3 -1
  5. package/src/{untracked.ts → attachments.ts} +2 -5
  6. package/src/commands/activation-preparation.ts +6 -6
  7. package/src/commands/activation.ts +7 -7
  8. package/src/commands/lifecycle.ts +5 -5
  9. package/src/commands/manual-command.ts +13 -9
  10. package/src/commands/manual-consultation.ts +8 -8
  11. package/src/commands/manual-progress.ts +3 -3
  12. package/src/commands/model-commands.ts +6 -6
  13. package/src/commands/model-options.ts +4 -4
  14. package/src/commands/model-picker.ts +2 -2
  15. package/src/commands/registration.ts +8 -8
  16. package/src/commands/renderers.ts +5 -5
  17. package/src/commands/runtime.ts +8 -8
  18. package/src/commands/settings-commands.ts +8 -8
  19. package/src/commands/settings-persistence.ts +4 -4
  20. package/src/commands/types.ts +5 -13
  21. package/src/commands.ts +2 -2
  22. package/src/config/args.ts +3 -3
  23. package/src/config/defaults.ts +3 -3
  24. package/src/config/schema.ts +322 -0
  25. package/src/config/state.ts +2 -2
  26. package/src/config/storage.ts +20 -117
  27. package/src/config/types.ts +1 -1
  28. package/src/config/validation.ts +61 -181
  29. package/src/config.ts +7 -7
  30. package/src/content-utils.ts +30 -0
  31. package/src/conversation.ts +13 -168
  32. package/src/git.ts +4 -18
  33. package/src/herdr.ts +6 -8
  34. package/src/model-stream.ts +5 -8
  35. package/src/outcomes.ts +2 -5
  36. package/src/pi-settings.ts +59 -0
  37. package/src/preferences.ts +2 -2
  38. package/src/redaction.ts +50 -0
  39. package/src/scout-context.ts +121 -485
  40. package/src/scout-curation.ts +84 -0
  41. package/src/scout-groups.ts +485 -0
  42. package/src/scout-protocol.ts +122 -0
  43. package/src/scout-reconstruct.ts +44 -0
  44. package/src/scout-types.ts +66 -0
  45. package/src/scout.ts +53 -63
  46. package/src/session-state.ts +119 -76
  47. package/src/tool-result-cap.ts +88 -0
  48. package/src/tools/consult-context.ts +155 -0
  49. package/src/tools/consultation.ts +71 -220
  50. package/src/tools/gate-policy.ts +5 -5
  51. package/src/tools/gate-protocol.ts +2 -2
  52. package/src/tools/loop-gate.ts +70 -61
  53. package/src/tools/prompts.ts +4 -7
  54. package/src/tools/register-ask-advisor.ts +9 -14
  55. package/src/tools/register-lifecycle.ts +7 -7
  56. package/src/tools/register-outcome.ts +5 -5
  57. package/src/tools/register-renderers.ts +5 -5
  58. package/src/tools/registration.ts +10 -10
  59. package/src/tools/render-advisor-result.ts +11 -8
  60. package/src/tools/render-common.ts +25 -13
  61. package/src/tools/scout-status.ts +5 -5
  62. package/src/tools/session.ts +1 -1
  63. package/src/tools/types.ts +7 -7
  64. package/src/tools.ts +9 -9
  65. package/src/ui/manual-dialog-render.ts +2 -2
  66. package/src/ui/manual-dialog.ts +4 -9
  67. package/src/ui/model-selector.ts +1 -1
  68. package/src/ui/settings-formatting.ts +2 -2
  69. package/src/ui/settings-items.ts +4 -4
  70. package/src/ui/settings-mutations.ts +2 -2
  71. package/src/ui/settings-selector.ts +5 -5
  72. package/src/ui/text-setting-submenu.ts +1 -1
  73. package/src/ui/types.ts +2 -2
  74. package/src/ui.ts +4 -4
  75. package/src/usage.ts +1 -6
@@ -0,0 +1,322 @@
1
+ import { GIT_CONTEXT_LEVELS, isValidGitContextLevel } from "../git.ts";
2
+ import {
3
+ advisorAutoLoopGateRef,
4
+ advisorBlockOnBlockedRef,
5
+ advisorCollapseResponsesRef,
6
+ advisorCompletionGateRef,
7
+ advisorCustomInvocationRef,
8
+ advisorEffortRef,
9
+ advisorFailureGateRef,
10
+ advisorFailureModeRef,
11
+ advisorGitContextMaxCharsRef,
12
+ advisorGitContextRef,
13
+ advisorHerdrIntegrationRef,
14
+ advisorLoopThresholdRef,
15
+ advisorMaxCallsPerSessionRef,
16
+ advisorOutcomeLoggingRef,
17
+ advisorPlanGateRef,
18
+ advisorRedactSecretsRef,
19
+ advisorRef,
20
+ advisorScoutEnabledRef,
21
+ advisorSessionSummaryRef,
22
+ advisorToolPoliciesRef,
23
+ advisorToolResultMaxBytesRef,
24
+ advisorToolResultMaxLinesRef,
25
+ advisorTrackedFileContentRef,
26
+ advisorUntrackedContentRef,
27
+ alwaysOnRef,
28
+ contextMaxCharsRef,
29
+ executorEffortRef,
30
+ executorRef,
31
+ showUsageDetailsRef,
32
+ showUsageFooterRef,
33
+ simpleModeRef,
34
+ } from "./state.ts";
35
+ import {
36
+ ADVISOR_TOOL_POLICIES,
37
+ type AdvisorConfig,
38
+ GATE_FAILURE_MODES,
39
+ MAX_CONTEXT_MAX_CHARS,
40
+ } from "./types.ts";
41
+
42
+ /** An empty ref means no model has been selected yet. */
43
+ const configuredModelRef = (value: string | undefined): string | undefined =>
44
+ value?.trim() || undefined;
45
+
46
+ export const isValidAdvisorToolPolicies = (
47
+ value: unknown
48
+ ): value is Record<string, (typeof ADVISOR_TOOL_POLICIES)[number]> => {
49
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
50
+ return false;
51
+ }
52
+ return Object.entries(value).every(
53
+ ([toolName, policy]) =>
54
+ toolName.trim().length > 0 &&
55
+ typeof policy === "string" &&
56
+ ADVISOR_TOOL_POLICIES.includes(
57
+ policy as (typeof ADVISOR_TOOL_POLICIES)[number]
58
+ )
59
+ );
60
+ };
61
+
62
+ const nonNegativeSafeInteger = (value: unknown): value is number =>
63
+ typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
64
+
65
+ export const isValidContextMaxChars = (value: unknown): value is number =>
66
+ typeof value === "number" &&
67
+ Number.isSafeInteger(value) &&
68
+ value >= 0 &&
69
+ value <= MAX_CONTEXT_MAX_CHARS;
70
+
71
+ export const isValidLoopThreshold = (value: unknown): value is number =>
72
+ typeof value === "number" && Number.isSafeInteger(value) && value >= 2;
73
+
74
+ export const isValidMaxCallsPerSession = (value: unknown): value is number =>
75
+ nonNegativeSafeInteger(value);
76
+
77
+ export const isValidGateFailureMode = (
78
+ value: unknown
79
+ ): value is (typeof GATE_FAILURE_MODES)[number] =>
80
+ typeof value === "string" &&
81
+ GATE_FAILURE_MODES.includes(value as (typeof GATE_FAILURE_MODES)[number]);
82
+
83
+ export const isValidToolResultMaxLines = (value: unknown): value is number =>
84
+ nonNegativeSafeInteger(value);
85
+
86
+ export const isValidToolResultMaxBytes = (value: unknown): value is number =>
87
+ nonNegativeSafeInteger(value);
88
+
89
+ /** One declarative entry per AdvisorConfig key: JSON type, persistence, and
90
+ * the live runtime value behind it. Validation and storage derive from this
91
+ * table; adding a setting means adding exactly one entry here. */
92
+ export interface ConfigKeySchema {
93
+ /** Human-readable accepted-value phrase for error messages. */
94
+ accepted: string;
95
+ /** Live runtime value captured by saveConfig. */
96
+ current: () => unknown;
97
+ /** Included in the state captured and diffed by saveConfig. */
98
+ persisted: boolean;
99
+ /** JSON value type used for the base type check. */
100
+ type: "string" | "boolean" | "number" | "enum" | "object";
101
+ /** Type beyond the JSON type, for enum and object keys. */
102
+ validate?: (value: unknown) => boolean;
103
+ }
104
+
105
+ export const CONFIG_SCHEMA = {
106
+ advisor: {
107
+ accepted: "a provider/model string",
108
+ current: () => configuredModelRef(advisorRef),
109
+ persisted: true,
110
+ type: "string",
111
+ },
112
+ advisorAutoLoopGate: {
113
+ accepted: "true or false",
114
+ current: () => advisorAutoLoopGateRef,
115
+ persisted: true,
116
+ type: "boolean",
117
+ },
118
+ advisorBlockOnBlocked: {
119
+ accepted: "true or false",
120
+ current: () => advisorBlockOnBlockedRef,
121
+ persisted: true,
122
+ type: "boolean",
123
+ },
124
+ advisorCollapseResponses: {
125
+ accepted: "true or false",
126
+ current: () => advisorCollapseResponsesRef,
127
+ persisted: true,
128
+ type: "boolean",
129
+ },
130
+ advisorCompletionGate: {
131
+ accepted: "true or false",
132
+ current: () => advisorCompletionGateRef,
133
+ persisted: true,
134
+ type: "boolean",
135
+ },
136
+ advisorCustomInvocation: {
137
+ accepted: "a string",
138
+ current: () => advisorCustomInvocationRef,
139
+ persisted: true,
140
+ type: "string",
141
+ },
142
+ advisorEffort: {
143
+ accepted: "a string",
144
+ current: () => advisorEffortRef,
145
+ persisted: true,
146
+ type: "string",
147
+ },
148
+ advisorFailureGate: {
149
+ accepted: "true or false",
150
+ current: () => advisorFailureGateRef,
151
+ persisted: true,
152
+ type: "boolean",
153
+ },
154
+ advisorGitContext: {
155
+ accepted: GIT_CONTEXT_LEVELS.join(", "),
156
+ current: () => advisorGitContextRef,
157
+ persisted: true,
158
+ type: "enum",
159
+ validate: isValidGitContextLevel,
160
+ },
161
+ advisorGitContextMaxChars: {
162
+ accepted: "a non-negative safe integer",
163
+ current: () => advisorGitContextMaxCharsRef,
164
+ persisted: true,
165
+ type: "number",
166
+ validate: nonNegativeSafeInteger,
167
+ },
168
+ advisorHerdrIntegration: {
169
+ accepted: "true or false",
170
+ current: () => advisorHerdrIntegrationRef,
171
+ persisted: true,
172
+ type: "boolean",
173
+ },
174
+ advisorLoopThreshold: {
175
+ accepted: "a safe integer of at least 2",
176
+ current: () => advisorLoopThresholdRef,
177
+ persisted: true,
178
+ type: "number",
179
+ validate: isValidLoopThreshold,
180
+ },
181
+ advisorMaxCallsPerSession: {
182
+ accepted: "a non-negative safe integer",
183
+ current: () => advisorMaxCallsPerSessionRef,
184
+ persisted: true,
185
+ type: "number",
186
+ validate: isValidMaxCallsPerSession,
187
+ },
188
+ advisorOutcomeLogging: {
189
+ accepted: "true or false",
190
+ current: () => advisorOutcomeLoggingRef,
191
+ persisted: false,
192
+ type: "boolean",
193
+ },
194
+ advisorPlanGate: {
195
+ accepted: "true or false",
196
+ current: () => advisorPlanGateRef,
197
+ persisted: true,
198
+ type: "boolean",
199
+ },
200
+ advisorRedactSecrets: {
201
+ accepted: "true or false",
202
+ current: () => advisorRedactSecretsRef,
203
+ persisted: true,
204
+ type: "boolean",
205
+ },
206
+ advisorScoutEnabled: {
207
+ accepted: "true or false",
208
+ current: () => advisorScoutEnabledRef,
209
+ persisted: true,
210
+ type: "boolean",
211
+ },
212
+ advisorSessionSummary: {
213
+ accepted: "true or false",
214
+ current: () => advisorSessionSummaryRef,
215
+ persisted: true,
216
+ type: "boolean",
217
+ },
218
+ advisorToolPolicies: {
219
+ accepted:
220
+ "a JSON object with non-empty tool names and full, summary, or exclude values",
221
+ current: () => ({ ...advisorToolPoliciesRef }),
222
+ persisted: true,
223
+ type: "object",
224
+ validate: isValidAdvisorToolPolicies,
225
+ },
226
+ advisorToolResultMaxBytes: {
227
+ accepted: "a non-negative safe integer",
228
+ current: () => advisorToolResultMaxBytesRef,
229
+ persisted: true,
230
+ type: "number",
231
+ validate: isValidToolResultMaxBytes,
232
+ },
233
+ advisorToolResultMaxLines: {
234
+ accepted: "a non-negative safe integer",
235
+ current: () => advisorToolResultMaxLinesRef,
236
+ persisted: true,
237
+ type: "number",
238
+ validate: isValidToolResultMaxLines,
239
+ },
240
+ advisorTrackedFileContent: {
241
+ accepted: "true or false",
242
+ current: () => advisorTrackedFileContentRef,
243
+ persisted: true,
244
+ type: "boolean",
245
+ },
246
+ advisorUntrackedContent: {
247
+ accepted: "true or false",
248
+ current: () => advisorUntrackedContentRef,
249
+ persisted: true,
250
+ type: "boolean",
251
+ },
252
+ alwaysOn: {
253
+ accepted: "true or false",
254
+ current: () => alwaysOnRef,
255
+ persisted: true,
256
+ type: "boolean",
257
+ },
258
+ contextMaxChars: {
259
+ accepted: `a safe integer from 0 through ${MAX_CONTEXT_MAX_CHARS}`,
260
+ current: () => contextMaxCharsRef,
261
+ persisted: true,
262
+ type: "number",
263
+ validate: isValidContextMaxChars,
264
+ },
265
+ executor: {
266
+ accepted: "a provider/model string",
267
+ current: () => configuredModelRef(executorRef),
268
+ persisted: true,
269
+ type: "string",
270
+ },
271
+ executorEffort: {
272
+ accepted: "a string",
273
+ current: () => executorEffortRef,
274
+ persisted: true,
275
+ type: "string",
276
+ },
277
+ gateFailureMode: {
278
+ accepted: GATE_FAILURE_MODES.join(", "),
279
+ current: () => advisorFailureModeRef,
280
+ persisted: true,
281
+ type: "enum",
282
+ validate: isValidGateFailureMode,
283
+ },
284
+ showUsageDetails: {
285
+ accepted: "true or false",
286
+ current: () => showUsageDetailsRef,
287
+ persisted: true,
288
+ type: "boolean",
289
+ },
290
+ showUsageFooter: {
291
+ accepted: "true or false",
292
+ current: () => showUsageFooterRef,
293
+ persisted: true,
294
+ type: "boolean",
295
+ },
296
+ simpleMode: {
297
+ accepted: "true or false",
298
+ current: () => simpleModeRef,
299
+ persisted: true,
300
+ type: "boolean",
301
+ },
302
+ } as const satisfies Record<keyof AdvisorConfig, ConfigKeySchema>;
303
+
304
+ export type ConfigKey = keyof typeof CONFIG_SCHEMA;
305
+
306
+ /** Keys saveConfig captures, diffs, and writes to advisor.json. */
307
+ export type PersistedConfigKey = {
308
+ [Key in ConfigKey]: (typeof CONFIG_SCHEMA)[Key]["persisted"] extends true
309
+ ? Key
310
+ : never;
311
+ }[ConfigKey];
312
+
313
+ export const SAVED_CONFIG_KEYS = (
314
+ Object.keys(CONFIG_SCHEMA) as ConfigKey[]
315
+ ).filter(
316
+ (key) => CONFIG_SCHEMA[key].persisted
317
+ ) as readonly PersistedConfigKey[];
318
+
319
+ /** Widened per-key view for consumers that index by dynamic key. */
320
+ const SCHEMA_BY_KEY: Record<ConfigKey, ConfigKeySchema> = CONFIG_SCHEMA;
321
+
322
+ export { configuredModelRef, SCHEMA_BY_KEY };
@@ -1,4 +1,4 @@
1
- import type { GitContextLevel } from "../git.js";
1
+ import type { GitContextLevel } from "../git.ts";
2
2
  import {
3
3
  type AdvisorToolPolicies,
4
4
  DEFAULT_ADVISOR_GIT_CONTEXT_MAX_CHARS,
@@ -6,7 +6,7 @@ import {
6
6
  DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES,
7
7
  DEFAULT_CONTEXT_MAX_CHARS,
8
8
  type GateFailureMode,
9
- } from "./types.js";
9
+ } from "./types.ts";
10
10
 
11
11
  // An empty ref means no model has been selected yet.
12
12
  export let executorRef = "";
@@ -5,43 +5,16 @@ import {
5
5
  type ExtensionContext,
6
6
  getAgentDir,
7
7
  } from "@earendil-works/pi-coding-agent";
8
- import { applyConfig, resetDefaults } from "./defaults.js";
8
+ import { applyConfig, resetDefaults } from "./defaults.ts";
9
9
  import {
10
- advisorAutoLoopGateRef,
11
- advisorBlockOnBlockedRef,
12
- advisorCollapseResponsesRef,
13
- advisorCompletionGateRef,
14
- advisorCustomInvocationRef,
15
- advisorEffortRef,
16
- advisorFailureGateRef,
17
- advisorFailureModeRef,
18
- advisorGitContextMaxCharsRef,
19
- advisorGitContextRef,
20
- advisorHerdrIntegrationRef,
21
- advisorLoopThresholdRef,
22
- advisorMaxCallsPerSessionRef,
23
- advisorPlanGateRef,
24
- advisorRedactSecretsRef,
25
- advisorRef,
26
- advisorScoutEnabledRef,
27
- advisorSessionSummaryRef,
28
- advisorToolPoliciesRef,
29
- advisorToolResultMaxBytesRef,
30
- advisorToolResultMaxLinesRef,
31
- advisorTrackedFileContentRef,
32
- advisorUntrackedContentRef,
33
- alwaysOnRef,
34
- contextMaxCharsRef,
35
- executorEffortRef,
36
- executorRef,
37
- setAdvisorOutcomeLoggingRef,
38
- setPersistedModelRefs,
39
- showUsageDetailsRef,
40
- showUsageFooterRef,
41
- simpleModeRef,
42
- } from "./state.js";
43
- import type { AdvisorConfig } from "./types.js";
44
- import { unknownConfigKeys, validateConfig } from "./validation.js";
10
+ CONFIG_SCHEMA,
11
+ configuredModelRef,
12
+ type PersistedConfigKey,
13
+ SAVED_CONFIG_KEYS,
14
+ } from "./schema.ts";
15
+ import { setAdvisorOutcomeLoggingRef, setPersistedModelRefs } from "./state.ts";
16
+ import type { AdvisorConfig } from "./types.ts";
17
+ import { unknownConfigKeys, validateConfig } from "./validation.ts";
45
18
 
46
19
  export const configPaths = (ctx: ExtensionContext) => [
47
20
  ctx.isProjectTrusted()
@@ -50,78 +23,15 @@ export const configPaths = (ctx: ExtensionContext) => [
50
23
  join(getAgentDir(), "advisor.json"),
51
24
  ];
52
25
 
53
- const configuredModelRef = (value: string | undefined): string | undefined =>
54
- value?.trim() || undefined;
55
-
56
- const SAVED_CONFIG_KEYS = [
57
- "advisor",
58
- "advisorAutoLoopGate",
59
- "advisorBlockOnBlocked",
60
- "advisorCollapseResponses",
61
- "advisorCompletionGate",
62
- "advisorCustomInvocation",
63
- "advisorEffort",
64
- "advisorFailureGate",
65
- "advisorGitContext",
66
- "advisorGitContextMaxChars",
67
- "advisorHerdrIntegration",
68
- "advisorLoopThreshold",
69
- "advisorMaxCallsPerSession",
70
- "advisorPlanGate",
71
- "advisorRedactSecrets",
72
- "advisorScoutEnabled",
73
- "advisorSessionSummary",
74
- "advisorToolPolicies",
75
- "advisorToolResultMaxBytes",
76
- "advisorToolResultMaxLines",
77
- "advisorTrackedFileContent",
78
- "advisorUntrackedContent",
79
- "alwaysOn",
80
- "contextMaxChars",
81
- "executor",
82
- "executorEffort",
83
- "gateFailureMode",
84
- "showUsageDetails",
85
- "showUsageFooter",
86
- "simpleMode",
87
- ] as const satisfies readonly (keyof AdvisorConfig)[];
88
- type SavedConfigKey = (typeof SAVED_CONFIG_KEYS)[number];
26
+ type SavedConfigKey = PersistedConfigKey;
89
27
  type ConfigState = {
90
28
  [Key in SavedConfigKey]: AdvisorConfig[Key];
91
29
  };
92
30
 
93
- const currentConfigState = (): ConfigState => ({
94
- advisor: configuredModelRef(advisorRef),
95
- advisorAutoLoopGate: advisorAutoLoopGateRef,
96
- advisorBlockOnBlocked: advisorBlockOnBlockedRef,
97
- advisorCollapseResponses: advisorCollapseResponsesRef,
98
- advisorCompletionGate: advisorCompletionGateRef,
99
- advisorCustomInvocation: advisorCustomInvocationRef,
100
- advisorEffort: advisorEffortRef,
101
- advisorFailureGate: advisorFailureGateRef,
102
- advisorGitContext: advisorGitContextRef,
103
- advisorGitContextMaxChars: advisorGitContextMaxCharsRef,
104
- advisorHerdrIntegration: advisorHerdrIntegrationRef,
105
- advisorLoopThreshold: advisorLoopThresholdRef,
106
- advisorMaxCallsPerSession: advisorMaxCallsPerSessionRef,
107
- advisorPlanGate: advisorPlanGateRef,
108
- advisorRedactSecrets: advisorRedactSecretsRef,
109
- advisorScoutEnabled: advisorScoutEnabledRef,
110
- advisorSessionSummary: advisorSessionSummaryRef,
111
- advisorToolPolicies: { ...advisorToolPoliciesRef },
112
- advisorToolResultMaxBytes: advisorToolResultMaxBytesRef,
113
- advisorToolResultMaxLines: advisorToolResultMaxLinesRef,
114
- advisorTrackedFileContent: advisorTrackedFileContentRef,
115
- advisorUntrackedContent: advisorUntrackedContentRef,
116
- alwaysOn: alwaysOnRef,
117
- contextMaxChars: contextMaxCharsRef,
118
- executor: configuredModelRef(executorRef),
119
- executorEffort: executorEffortRef,
120
- gateFailureMode: advisorFailureModeRef,
121
- showUsageDetails: showUsageDetailsRef,
122
- showUsageFooter: showUsageFooterRef,
123
- simpleMode: simpleModeRef,
124
- });
31
+ const currentConfigState = (): ConfigState =>
32
+ Object.fromEntries(
33
+ SAVED_CONFIG_KEYS.map((key) => [key, CONFIG_SCHEMA[key].current()])
34
+ ) as ConfigState;
125
35
 
126
36
  const sameConfigValue = <Value>(left: Value, right: Value) =>
127
37
  JSON.stringify(left) === JSON.stringify(right);
@@ -166,25 +76,18 @@ const applyChangedConfigValues = (
166
76
  }
167
77
  };
168
78
 
169
- // Tracks the runtime state represented by the last successful load/save. A
170
- // save can then merge fresh disk contents without treating untouched in-memory
171
- // values as an instruction to overwrite an external edit.
79
+ // Tracks the last load/save state so a later save merges fresh disk contents instead of overwriting an external edit.
172
80
  let loadedConfigState: ConfigState | undefined;
173
81
  let loadedConfigPath: string | undefined;
174
82
 
175
83
  const readConfig = (path: string): AdvisorConfig => {
176
- try {
177
- const config = JSON.parse(readFileSync(path, "utf8"));
178
- validateConfig(config, path);
179
- return config;
180
- } catch (error) {
181
- throw error instanceof Error ? error : new Error(String(error));
182
- }
84
+ const config = JSON.parse(readFileSync(path, "utf8"));
85
+ validateConfig(config, path);
86
+ return config;
183
87
  };
184
88
 
185
- // loadConfig runs on every tool call and every consultation. Caching the parsed
186
- // file by path and stat identity removes that read and parse from the hot path
187
- // while still applying the full reset-then-apply sequence on each call.
89
+ // loadConfig runs per tool call and consultation; caching by path and stat
90
+ // identity keeps the parse off the hot path while still reset-applying each call.
188
91
  const configCache = new Map<
189
92
  string,
190
93
  { config: AdvisorConfig; identity: string }
@@ -2,7 +2,7 @@ import {
2
2
  DEFAULT_MAX_BYTES,
3
3
  DEFAULT_MAX_LINES,
4
4
  } from "@earendil-works/pi-coding-agent";
5
- import type { GitContextLevel } from "../git.js";
5
+ import type { GitContextLevel } from "../git.ts";
6
6
 
7
7
  export const DEFAULT_CONTEXT_MAX_CHARS = 15_000;
8
8
  export const MAX_CONTEXT_MAX_CHARS = Number.MAX_SAFE_INTEGER;