pi-advisor-flow 0.6.0 → 0.8.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/CHANGELOG.md +34 -17
- package/README.md +10 -8
- package/dist/index.js +2852 -868
- package/extensions/index.ts +2 -1
- package/package.json +55 -40
- package/src/attachments.ts +11 -10
- package/src/commands/activation-preparation.ts +1 -0
- package/src/commands/activation.ts +2 -0
- package/src/commands/lifecycle.ts +1 -0
- package/src/commands/manual-command.ts +7 -1
- package/src/commands/manual-consultation.ts +103 -79
- package/src/commands/manual-progress.ts +7 -5
- package/src/commands/model-commands.ts +1 -1
- package/src/commands/model-options.ts +14 -3
- package/src/commands/model-picker.ts +1 -0
- package/src/commands/registration.ts +1 -0
- package/src/commands/renderers.ts +67 -55
- package/src/commands/runtime.ts +16 -14
- package/src/commands/settings-commands.ts +10 -4
- package/src/commands/settings-persistence.ts +64 -2
- package/src/commands/types.ts +2 -0
- package/src/commands.ts +0 -1
- package/src/config/args.ts +1 -1
- package/src/config/defaults.ts +83 -1
- package/src/config/schema.ts +152 -15
- package/src/config/state.ts +80 -2
- package/src/config/storage.ts +38 -22
- package/src/config/types.ts +45 -3
- package/src/config/validation.ts +33 -21
- package/src/config.ts +3 -0
- package/src/content-utils.ts +24 -4
- package/src/conversation.ts +16 -15
- package/src/git.ts +10 -3
- package/src/herdr-block.ts +83 -0
- package/src/herdr-shared.ts +108 -0
- package/src/herdr.ts +30 -192
- package/src/jev/client.ts +338 -0
- package/src/jev/failure.ts +19 -0
- package/src/jev/key-store.ts +267 -0
- package/src/jev/ledger.ts +228 -0
- package/src/jev/questions.ts +122 -0
- package/src/jev/state.ts +50 -0
- package/src/jev/transport.ts +63 -0
- package/src/model-stream.ts +35 -37
- package/src/outcomes.ts +20 -12
- package/src/pi-settings.ts +4 -9
- package/src/preferences.ts +4 -2
- package/src/redaction.ts +10 -12
- package/src/scout-context.ts +59 -60
- package/src/scout-curation.ts +4 -13
- package/src/scout-groups.ts +178 -175
- package/src/scout-protocol.ts +74 -70
- package/src/scout-reconstruct.ts +1 -1
- package/src/scout.ts +115 -73
- package/src/session-state.ts +148 -39
- package/src/tool-result-cap.ts +18 -17
- package/src/tools/consult-context.ts +7 -15
- package/src/tools/consultation.ts +14 -4
- package/src/tools/gate-policy.ts +1 -0
- package/src/tools/gate-protocol.ts +36 -23
- package/src/tools/jev-filter.ts +204 -0
- package/src/tools/jev-turn-gate.ts +203 -0
- package/src/tools/loop-gate.ts +32 -20
- package/src/tools/model-access.ts +46 -0
- package/src/tools/outage-notifier.ts +30 -0
- package/src/tools/prompts.ts +20 -25
- package/src/tools/register-ask-advisor.ts +73 -17
- package/src/tools/register-lifecycle.ts +22 -0
- package/src/tools/register-outcome.ts +2 -0
- package/src/tools/register-renderers.ts +149 -73
- package/src/tools/registration.ts +15 -0
- package/src/tools/render-advisor-result.ts +77 -35
- package/src/tools/render-common.ts +16 -19
- package/src/tools/scout-status.ts +42 -27
- package/src/tools/types.ts +11 -0
- package/src/tools.ts +0 -1
- package/src/ui/jev-setup-submenu.ts +350 -0
- package/src/ui/manual-dialog-render.ts +1 -0
- package/src/ui/manual-dialog.ts +39 -43
- package/src/ui/masked-input.ts +62 -0
- package/src/ui/model-multi-selector.ts +9 -0
- package/src/ui/model-selector-adapter.ts +31 -0
- package/src/ui/model-selector.ts +14 -161
- package/src/ui/searchable-model-list.ts +230 -0
- package/src/ui/settings-formatting.ts +24 -14
- package/src/ui/settings-items.ts +200 -2
- package/src/ui/settings-list-adapter.ts +15 -12
- package/src/ui/settings-mutations.ts +169 -47
- package/src/ui/settings-selector.ts +18 -9
- package/src/ui/text-setting-submenu.ts +3 -6
- package/src/ui/types.ts +25 -0
- package/src/ui.ts +0 -1
- package/src/usage.ts +29 -20
package/src/config/schema.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isString } from "../content-utils.ts";
|
|
2
|
+
import type { JsonValue } from "../content-utils.ts";
|
|
1
3
|
import { GIT_CONTEXT_LEVELS, isValidGitContextLevel } from "../git.ts";
|
|
2
4
|
import {
|
|
3
5
|
advisorAutoLoopGateRef,
|
|
@@ -11,8 +13,20 @@ import {
|
|
|
11
13
|
advisorGitContextMaxCharsRef,
|
|
12
14
|
advisorGitContextRef,
|
|
13
15
|
advisorHerdrIntegrationRef,
|
|
16
|
+
advisorJevDigestMaxCharsRef,
|
|
17
|
+
advisorJevFilterEnabledRef,
|
|
18
|
+
advisorJevFilterNoulMarginRef,
|
|
19
|
+
advisorJevFilterOverrideWindowRef,
|
|
20
|
+
advisorJevFilterSkipConfidenceRef,
|
|
21
|
+
advisorJevModelRef,
|
|
22
|
+
advisorJevPricePerMtokRef,
|
|
23
|
+
advisorJevTimeoutMsRef,
|
|
24
|
+
advisorJevTransportRef,
|
|
25
|
+
advisorJevTurnGateEveryTurnsRef,
|
|
26
|
+
advisorJevTurnGateNoulThresholdRef,
|
|
14
27
|
advisorLoopThresholdRef,
|
|
15
28
|
advisorMaxCallsPerSessionRef,
|
|
29
|
+
advisorModelWhitelistRef,
|
|
16
30
|
advisorOutcomeLoggingRef,
|
|
17
31
|
advisorPlanGateRef,
|
|
18
32
|
advisorRedactSecretsRef,
|
|
@@ -32,10 +46,15 @@ import {
|
|
|
32
46
|
showUsageFooterRef,
|
|
33
47
|
simpleModeRef,
|
|
34
48
|
} from "./state.ts";
|
|
49
|
+
import type {
|
|
50
|
+
AdvisorConfig,
|
|
51
|
+
AdvisorConfigValue,
|
|
52
|
+
JevTransport,
|
|
53
|
+
} from "./types.ts";
|
|
35
54
|
import {
|
|
36
55
|
ADVISOR_TOOL_POLICIES,
|
|
37
|
-
type AdvisorConfig,
|
|
38
56
|
GATE_FAILURE_MODES,
|
|
57
|
+
JEV_TRANSPORTS,
|
|
39
58
|
MAX_CONTEXT_MAX_CHARS,
|
|
40
59
|
} from "./types.ts";
|
|
41
60
|
|
|
@@ -43,6 +62,15 @@ import {
|
|
|
43
62
|
const configuredModelRef = (value: string | undefined): string | undefined =>
|
|
44
63
|
value?.trim() || undefined;
|
|
45
64
|
|
|
65
|
+
const isNonEmptyModel = (model: unknown): model is string =>
|
|
66
|
+
typeof model === "string" && model.trim().length > 0;
|
|
67
|
+
|
|
68
|
+
export const isValidAdvisorModelWhitelist = (
|
|
69
|
+
value: unknown
|
|
70
|
+
): value is string[] => Array.isArray(value) && value.every(isNonEmptyModel);
|
|
71
|
+
|
|
72
|
+
const ADVISOR_TOOL_POLICY_NAMES = new Set<string>(ADVISOR_TOOL_POLICIES);
|
|
73
|
+
|
|
46
74
|
export const isValidAdvisorToolPolicies = (
|
|
47
75
|
value: unknown
|
|
48
76
|
): value is Record<string, (typeof ADVISOR_TOOL_POLICIES)[number]> => {
|
|
@@ -52,10 +80,8 @@ export const isValidAdvisorToolPolicies = (
|
|
|
52
80
|
return Object.entries(value).every(
|
|
53
81
|
([toolName, policy]) =>
|
|
54
82
|
toolName.trim().length > 0 &&
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
policy as (typeof ADVISOR_TOOL_POLICIES)[number]
|
|
58
|
-
)
|
|
83
|
+
isString(policy) &&
|
|
84
|
+
ADVISOR_TOOL_POLICY_NAMES.has(policy)
|
|
59
85
|
);
|
|
60
86
|
};
|
|
61
87
|
|
|
@@ -74,11 +100,12 @@ export const isValidLoopThreshold = (value: unknown): value is number =>
|
|
|
74
100
|
export const isValidMaxCallsPerSession = (value: unknown): value is number =>
|
|
75
101
|
nonNegativeSafeInteger(value);
|
|
76
102
|
|
|
103
|
+
const GATE_FAILURE_MODE_NAMES = new Set<string>(GATE_FAILURE_MODES);
|
|
104
|
+
|
|
77
105
|
export const isValidGateFailureMode = (
|
|
78
106
|
value: unknown
|
|
79
107
|
): value is (typeof GATE_FAILURE_MODES)[number] =>
|
|
80
|
-
|
|
81
|
-
GATE_FAILURE_MODES.includes(value as (typeof GATE_FAILURE_MODES)[number]);
|
|
108
|
+
isString(value) && GATE_FAILURE_MODE_NAMES.has(value);
|
|
82
109
|
|
|
83
110
|
export const isValidToolResultMaxLines = (value: unknown): value is number =>
|
|
84
111
|
nonNegativeSafeInteger(value);
|
|
@@ -86,6 +113,32 @@ export const isValidToolResultMaxLines = (value: unknown): value is number =>
|
|
|
86
113
|
export const isValidToolResultMaxBytes = (value: unknown): value is number =>
|
|
87
114
|
nonNegativeSafeInteger(value);
|
|
88
115
|
|
|
116
|
+
export const isValidJevTimeoutMs = (value: unknown): value is number =>
|
|
117
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
118
|
+
|
|
119
|
+
export const isValidJevDigestMaxChars = (value: unknown): value is number =>
|
|
120
|
+
nonNegativeSafeInteger(value);
|
|
121
|
+
|
|
122
|
+
export const isValidJevPricePerMtok = (value: unknown): value is number =>
|
|
123
|
+
typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
124
|
+
|
|
125
|
+
export const isValidJevSkipConfidence = (value: unknown): value is number =>
|
|
126
|
+
typeof value === "number" &&
|
|
127
|
+
Number.isFinite(value) &&
|
|
128
|
+
value >= 0.5 &&
|
|
129
|
+
value <= 1;
|
|
130
|
+
|
|
131
|
+
export const isValidJevNoulMargin = (value: unknown): value is number =>
|
|
132
|
+
typeof value === "number" &&
|
|
133
|
+
Number.isFinite(value) &&
|
|
134
|
+
value >= 0 &&
|
|
135
|
+
value <= 0.5;
|
|
136
|
+
|
|
137
|
+
const JEV_TRANSPORT_NAMES = new Set<string>(JEV_TRANSPORTS);
|
|
138
|
+
|
|
139
|
+
export const isValidJevTransport = (value: unknown): value is JevTransport =>
|
|
140
|
+
isString(value) && JEV_TRANSPORT_NAMES.has(value);
|
|
141
|
+
|
|
89
142
|
/** One declarative entry per AdvisorConfig key: JSON type, persistence, and
|
|
90
143
|
* the live runtime value behind it. Validation and storage derive from this
|
|
91
144
|
* table; adding a setting means adding exactly one entry here. */
|
|
@@ -93,13 +146,13 @@ export interface ConfigKeySchema {
|
|
|
93
146
|
/** Human-readable accepted-value phrase for error messages. */
|
|
94
147
|
accepted: string;
|
|
95
148
|
/** Live runtime value captured by saveConfig. */
|
|
96
|
-
current: () =>
|
|
149
|
+
current: () => AdvisorConfigValue;
|
|
97
150
|
/** Included in the state captured and diffed by saveConfig. */
|
|
98
151
|
persisted: boolean;
|
|
99
152
|
/** JSON value type used for the base type check. */
|
|
100
|
-
type: "string" | "boolean" | "number" | "enum" | "object";
|
|
153
|
+
type: "string" | "boolean" | "number" | "enum" | "object" | "array";
|
|
101
154
|
/** Type beyond the JSON type, for enum and object keys. */
|
|
102
|
-
validate?: (value: unknown) =>
|
|
155
|
+
validate?: (value: unknown) => value is JsonValue;
|
|
103
156
|
}
|
|
104
157
|
|
|
105
158
|
export const CONFIG_SCHEMA = {
|
|
@@ -171,6 +224,81 @@ export const CONFIG_SCHEMA = {
|
|
|
171
224
|
persisted: true,
|
|
172
225
|
type: "boolean",
|
|
173
226
|
},
|
|
227
|
+
advisorJevDigestMaxChars: {
|
|
228
|
+
accepted: "a non-negative safe integer",
|
|
229
|
+
current: () => advisorJevDigestMaxCharsRef,
|
|
230
|
+
persisted: true,
|
|
231
|
+
type: "number",
|
|
232
|
+
validate: isValidJevDigestMaxChars,
|
|
233
|
+
},
|
|
234
|
+
advisorJevFilterEnabled: {
|
|
235
|
+
accepted: "true or false",
|
|
236
|
+
current: () => advisorJevFilterEnabledRef,
|
|
237
|
+
persisted: true,
|
|
238
|
+
type: "boolean",
|
|
239
|
+
},
|
|
240
|
+
advisorJevFilterNoulMargin: {
|
|
241
|
+
accepted: "a number from 0 through 0.5",
|
|
242
|
+
current: () => advisorJevFilterNoulMarginRef,
|
|
243
|
+
persisted: true,
|
|
244
|
+
type: "number",
|
|
245
|
+
validate: isValidJevNoulMargin,
|
|
246
|
+
},
|
|
247
|
+
advisorJevFilterOverrideWindow: {
|
|
248
|
+
accepted: "a non-negative safe integer",
|
|
249
|
+
current: () => advisorJevFilterOverrideWindowRef,
|
|
250
|
+
persisted: true,
|
|
251
|
+
type: "number",
|
|
252
|
+
validate: nonNegativeSafeInteger,
|
|
253
|
+
},
|
|
254
|
+
advisorJevFilterSkipConfidence: {
|
|
255
|
+
accepted: "a number between 0.5 and 1 inclusive",
|
|
256
|
+
current: () => advisorJevFilterSkipConfidenceRef,
|
|
257
|
+
persisted: true,
|
|
258
|
+
type: "number",
|
|
259
|
+
validate: isValidJevSkipConfidence,
|
|
260
|
+
},
|
|
261
|
+
advisorJevModel: {
|
|
262
|
+
accepted: "a non-empty string",
|
|
263
|
+
current: () => advisorJevModelRef,
|
|
264
|
+
persisted: true,
|
|
265
|
+
type: "string",
|
|
266
|
+
},
|
|
267
|
+
advisorJevPricePerMtok: {
|
|
268
|
+
accepted: "a positive number",
|
|
269
|
+
current: () => advisorJevPricePerMtokRef,
|
|
270
|
+
persisted: true,
|
|
271
|
+
type: "number",
|
|
272
|
+
validate: isValidJevPricePerMtok,
|
|
273
|
+
},
|
|
274
|
+
advisorJevTimeoutMs: {
|
|
275
|
+
accepted: "a positive safe integer",
|
|
276
|
+
current: () => advisorJevTimeoutMsRef,
|
|
277
|
+
persisted: true,
|
|
278
|
+
type: "number",
|
|
279
|
+
validate: isValidJevTimeoutMs,
|
|
280
|
+
},
|
|
281
|
+
advisorJevTransport: {
|
|
282
|
+
accepted: JEV_TRANSPORTS.join(", "),
|
|
283
|
+
current: () => advisorJevTransportRef,
|
|
284
|
+
persisted: true,
|
|
285
|
+
type: "enum",
|
|
286
|
+
validate: isValidJevTransport,
|
|
287
|
+
},
|
|
288
|
+
advisorJevTurnGateEveryTurns: {
|
|
289
|
+
accepted: "a non-negative safe integer (0 disables the turn gate)",
|
|
290
|
+
current: () => advisorJevTurnGateEveryTurnsRef,
|
|
291
|
+
persisted: true,
|
|
292
|
+
type: "number",
|
|
293
|
+
validate: nonNegativeSafeInteger,
|
|
294
|
+
},
|
|
295
|
+
advisorJevTurnGateNoulThreshold: {
|
|
296
|
+
accepted: "a number between 0.5 and 1 inclusive",
|
|
297
|
+
current: () => advisorJevTurnGateNoulThresholdRef,
|
|
298
|
+
persisted: true,
|
|
299
|
+
type: "number",
|
|
300
|
+
validate: isValidJevSkipConfidence,
|
|
301
|
+
},
|
|
174
302
|
advisorLoopThreshold: {
|
|
175
303
|
accepted: "a safe integer of at least 2",
|
|
176
304
|
current: () => advisorLoopThresholdRef,
|
|
@@ -185,6 +313,13 @@ export const CONFIG_SCHEMA = {
|
|
|
185
313
|
type: "number",
|
|
186
314
|
validate: isValidMaxCallsPerSession,
|
|
187
315
|
},
|
|
316
|
+
advisorModelWhitelist: {
|
|
317
|
+
accepted: "an array of non-empty provider/model strings",
|
|
318
|
+
current: () => [...advisorModelWhitelistRef],
|
|
319
|
+
persisted: true,
|
|
320
|
+
type: "array",
|
|
321
|
+
validate: isValidAdvisorModelWhitelist,
|
|
322
|
+
},
|
|
188
323
|
advisorOutcomeLogging: {
|
|
189
324
|
accepted: "true or false",
|
|
190
325
|
current: () => advisorOutcomeLoggingRef,
|
|
@@ -303,6 +438,9 @@ export const CONFIG_SCHEMA = {
|
|
|
303
438
|
|
|
304
439
|
export type ConfigKey = keyof typeof CONFIG_SCHEMA;
|
|
305
440
|
|
|
441
|
+
// SAFETY: CONFIG_SCHEMA satisfies Record<keyof AdvisorConfig, ConfigKeySchema>, so Object.keys yields exactly ConfigKey.
|
|
442
|
+
export const configKeys = Object.keys(CONFIG_SCHEMA) as ConfigKey[];
|
|
443
|
+
|
|
306
444
|
/** Keys saveConfig captures, diffs, and writes to advisor.json. */
|
|
307
445
|
export type PersistedConfigKey = {
|
|
308
446
|
[Key in ConfigKey]: (typeof CONFIG_SCHEMA)[Key]["persisted"] extends true
|
|
@@ -310,11 +448,10 @@ export type PersistedConfigKey = {
|
|
|
310
448
|
: never;
|
|
311
449
|
}[ConfigKey];
|
|
312
450
|
|
|
313
|
-
export const SAVED_CONFIG_KEYS =
|
|
314
|
-
|
|
315
|
-
).
|
|
316
|
-
|
|
317
|
-
) as readonly PersistedConfigKey[];
|
|
451
|
+
export const SAVED_CONFIG_KEYS: readonly PersistedConfigKey[] =
|
|
452
|
+
configKeys.filter(
|
|
453
|
+
(key): key is PersistedConfigKey => CONFIG_SCHEMA[key].persisted
|
|
454
|
+
);
|
|
318
455
|
|
|
319
456
|
/** Widened per-key view for consumers that index by dynamic key. */
|
|
320
457
|
const SCHEMA_BY_KEY: Record<ConfigKey, ConfigKeySchema> = CONFIG_SCHEMA;
|
package/src/config/state.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import type { GitContextLevel } from "../git.ts";
|
|
2
2
|
import {
|
|
3
|
-
type AdvisorToolPolicies,
|
|
4
3
|
DEFAULT_ADVISOR_GIT_CONTEXT_MAX_CHARS,
|
|
5
4
|
DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES,
|
|
6
5
|
DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES,
|
|
7
6
|
DEFAULT_CONTEXT_MAX_CHARS,
|
|
8
|
-
|
|
7
|
+
DEFAULT_JEV_DIGEST_MAX_CHARS,
|
|
8
|
+
DEFAULT_JEV_FILTER_NOUL_MARGIN,
|
|
9
|
+
DEFAULT_JEV_FILTER_OVERRIDE_WINDOW,
|
|
10
|
+
DEFAULT_JEV_FILTER_SKIP_CONFIDENCE,
|
|
11
|
+
DEFAULT_JEV_MODEL,
|
|
12
|
+
DEFAULT_JEV_PRICE_PER_MTOK,
|
|
13
|
+
DEFAULT_JEV_TIMEOUT_MS,
|
|
14
|
+
DEFAULT_JEV_TRANSPORT,
|
|
15
|
+
DEFAULT_JEV_TURN_GATE_EVERY_TURNS,
|
|
16
|
+
DEFAULT_JEV_TURN_GATE_NOUL_THRESHOLD,
|
|
17
|
+
} from "./types.ts";
|
|
18
|
+
import type {
|
|
19
|
+
AdvisorToolPolicies,
|
|
20
|
+
GateFailureMode,
|
|
21
|
+
JevTransport,
|
|
9
22
|
} from "./types.ts";
|
|
10
23
|
|
|
11
24
|
// An empty ref means no model has been selected yet.
|
|
@@ -25,11 +38,26 @@ export let advisorBlockOnBlockedRef = true;
|
|
|
25
38
|
export let advisorAutoLoopGateRef = true;
|
|
26
39
|
export let advisorLoopThresholdRef = 3;
|
|
27
40
|
export let advisorMaxCallsPerSessionRef: number | undefined;
|
|
41
|
+
export let advisorModelWhitelistRef: string[] = [];
|
|
28
42
|
export let advisorSessionSummaryRef = false;
|
|
29
43
|
export let simpleModeRef = false;
|
|
30
44
|
export let alwaysOnRef = false;
|
|
31
45
|
export let advisorFailureModeRef: GateFailureMode = "block-session";
|
|
32
46
|
export let advisorHerdrIntegrationRef = true;
|
|
47
|
+
export let advisorJevFilterEnabledRef = false;
|
|
48
|
+
export let advisorJevFilterSkipConfidenceRef =
|
|
49
|
+
DEFAULT_JEV_FILTER_SKIP_CONFIDENCE;
|
|
50
|
+
export let advisorJevFilterNoulMarginRef = DEFAULT_JEV_FILTER_NOUL_MARGIN;
|
|
51
|
+
export let advisorJevFilterOverrideWindowRef =
|
|
52
|
+
DEFAULT_JEV_FILTER_OVERRIDE_WINDOW;
|
|
53
|
+
export let advisorJevModelRef = DEFAULT_JEV_MODEL;
|
|
54
|
+
export let advisorJevTimeoutMsRef = DEFAULT_JEV_TIMEOUT_MS;
|
|
55
|
+
export let advisorJevDigestMaxCharsRef = DEFAULT_JEV_DIGEST_MAX_CHARS;
|
|
56
|
+
export let advisorJevPricePerMtokRef = DEFAULT_JEV_PRICE_PER_MTOK;
|
|
57
|
+
export let advisorJevTransportRef: JevTransport = DEFAULT_JEV_TRANSPORT;
|
|
58
|
+
export let advisorJevTurnGateEveryTurnsRef = DEFAULT_JEV_TURN_GATE_EVERY_TURNS;
|
|
59
|
+
export let advisorJevTurnGateNoulThresholdRef =
|
|
60
|
+
DEFAULT_JEV_TURN_GATE_NOUL_THRESHOLD;
|
|
33
61
|
export let advisorToolResultMaxLinesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES;
|
|
34
62
|
export let advisorToolResultMaxBytesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES;
|
|
35
63
|
export let advisorRedactSecretsRef = false;
|
|
@@ -100,6 +128,11 @@ export const setAdvisorLoopThresholdRef = (value: number) => {
|
|
|
100
128
|
export const setAdvisorMaxCallsPerSessionRef = (value: number | undefined) => {
|
|
101
129
|
advisorMaxCallsPerSessionRef = value;
|
|
102
130
|
};
|
|
131
|
+
export const setAdvisorModelWhitelistRef = (models: string[]) => {
|
|
132
|
+
advisorModelWhitelistRef = [
|
|
133
|
+
...new Set(models.map((model) => model.trim()).filter(Boolean)),
|
|
134
|
+
];
|
|
135
|
+
};
|
|
103
136
|
export const setAdvisorSessionSummaryRef = (enabled: boolean) => {
|
|
104
137
|
advisorSessionSummaryRef = enabled;
|
|
105
138
|
};
|
|
@@ -117,6 +150,39 @@ export const setAdvisorFailureModeRef = (value: GateFailureMode) => {
|
|
|
117
150
|
export const setAdvisorHerdrIntegrationRef = (enabled: boolean) => {
|
|
118
151
|
advisorHerdrIntegrationRef = enabled;
|
|
119
152
|
};
|
|
153
|
+
export const setAdvisorJevFilterEnabledRef = (enabled: boolean) => {
|
|
154
|
+
advisorJevFilterEnabledRef = enabled;
|
|
155
|
+
};
|
|
156
|
+
export const setAdvisorJevFilterSkipConfidenceRef = (value: number) => {
|
|
157
|
+
advisorJevFilterSkipConfidenceRef = value;
|
|
158
|
+
};
|
|
159
|
+
export const setAdvisorJevFilterNoulMarginRef = (value: number) => {
|
|
160
|
+
advisorJevFilterNoulMarginRef = value;
|
|
161
|
+
};
|
|
162
|
+
export const setAdvisorJevFilterOverrideWindowRef = (value: number) => {
|
|
163
|
+
advisorJevFilterOverrideWindowRef = value;
|
|
164
|
+
};
|
|
165
|
+
export const setAdvisorJevModelRef = (model: string | undefined) => {
|
|
166
|
+
advisorJevModelRef = model?.trim() || DEFAULT_JEV_MODEL;
|
|
167
|
+
};
|
|
168
|
+
export const setAdvisorJevTimeoutMsRef = (value: number) => {
|
|
169
|
+
advisorJevTimeoutMsRef = value;
|
|
170
|
+
};
|
|
171
|
+
export const setAdvisorJevDigestMaxCharsRef = (value: number) => {
|
|
172
|
+
advisorJevDigestMaxCharsRef = value;
|
|
173
|
+
};
|
|
174
|
+
export const setAdvisorJevPricePerMtokRef = (value: number) => {
|
|
175
|
+
advisorJevPricePerMtokRef = value;
|
|
176
|
+
};
|
|
177
|
+
export const setAdvisorJevTransportRef = (value: JevTransport) => {
|
|
178
|
+
advisorJevTransportRef = value;
|
|
179
|
+
};
|
|
180
|
+
export const setAdvisorJevTurnGateEveryTurnsRef = (value: number) => {
|
|
181
|
+
advisorJevTurnGateEveryTurnsRef = value;
|
|
182
|
+
};
|
|
183
|
+
export const setAdvisorJevTurnGateNoulThresholdRef = (value: number) => {
|
|
184
|
+
advisorJevTurnGateNoulThresholdRef = value;
|
|
185
|
+
};
|
|
120
186
|
export const setAdvisorToolResultMaxLinesRef = (value: number) => {
|
|
121
187
|
advisorToolResultMaxLinesRef = value;
|
|
122
188
|
};
|
|
@@ -172,8 +238,20 @@ export const getAdvisorSettings = () => ({
|
|
|
172
238
|
gitContext: advisorGitContextRef,
|
|
173
239
|
gitContextMaxChars: advisorGitContextMaxCharsRef,
|
|
174
240
|
herdrIntegration: advisorHerdrIntegrationRef,
|
|
241
|
+
jevDigestMaxChars: advisorJevDigestMaxCharsRef,
|
|
242
|
+
jevFilterEnabled: advisorJevFilterEnabledRef,
|
|
243
|
+
jevFilterNoulMargin: advisorJevFilterNoulMarginRef,
|
|
244
|
+
jevFilterOverrideWindow: advisorJevFilterOverrideWindowRef,
|
|
245
|
+
jevFilterSkipConfidence: advisorJevFilterSkipConfidenceRef,
|
|
246
|
+
jevModel: advisorJevModelRef,
|
|
247
|
+
jevPricePerMtok: advisorJevPricePerMtokRef,
|
|
248
|
+
jevTimeoutMs: advisorJevTimeoutMsRef,
|
|
249
|
+
jevTransport: advisorJevTransportRef,
|
|
250
|
+
jevTurnGateEveryTurns: advisorJevTurnGateEveryTurnsRef,
|
|
251
|
+
jevTurnGateNoulThreshold: advisorJevTurnGateNoulThresholdRef,
|
|
175
252
|
loopThreshold: advisorLoopThresholdRef,
|
|
176
253
|
maxCallsPerSession: advisorMaxCallsPerSessionRef,
|
|
254
|
+
modelWhitelist: [...advisorModelWhitelistRef],
|
|
177
255
|
outcomeLogging: advisorOutcomeLoggingRef,
|
|
178
256
|
planGate: advisorPlanGateRef,
|
|
179
257
|
redactSecrets: advisorRedactSecretsRef,
|
package/src/config/storage.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "
|
|
3
|
+
|
|
4
|
+
import { CONFIG_DIR_NAME, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
|
|
7
|
+
import { isRecord, isString } from "../content-utils.ts";
|
|
8
|
+
import type { RecordValue } from "../content-utils.ts";
|
|
8
9
|
import { applyConfig, resetDefaults } from "./defaults.ts";
|
|
9
10
|
import {
|
|
10
11
|
CONFIG_SCHEMA,
|
|
11
12
|
configuredModelRef,
|
|
12
|
-
type PersistedConfigKey,
|
|
13
13
|
SAVED_CONFIG_KEYS,
|
|
14
14
|
} from "./schema.ts";
|
|
15
|
+
import type { PersistedConfigKey } from "./schema.ts";
|
|
15
16
|
import { setAdvisorOutcomeLoggingRef, setPersistedModelRefs } from "./state.ts";
|
|
16
17
|
import type { AdvisorConfig } from "./types.ts";
|
|
17
18
|
import { unknownConfigKeys, validateConfig } from "./validation.ts";
|
|
@@ -29,6 +30,7 @@ type ConfigState = {
|
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
const currentConfigState = (): ConfigState =>
|
|
33
|
+
// SAFETY: SAVED_CONFIG_KEYS enumerates exactly the ConfigState keys, so fromEntries yields ConfigState.
|
|
32
34
|
Object.fromEntries(
|
|
33
35
|
SAVED_CONFIG_KEYS.map((key) => [key, CONFIG_SCHEMA[key].current()])
|
|
34
36
|
) as ConfigState;
|
|
@@ -36,12 +38,15 @@ const currentConfigState = (): ConfigState =>
|
|
|
36
38
|
const sameConfigValue = <Value>(left: Value, right: Value) =>
|
|
37
39
|
JSON.stringify(left) === JSON.stringify(right);
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
/** Keys advisor.json may hold that are not AdvisorConfig settings but are
|
|
42
|
+
* still legitimate (hand-placed secrets). They stay out of CONFIG_SCHEMA, are
|
|
43
|
+
* preserved like any unknown key, and never trigger the typo warning. */
|
|
44
|
+
const RESERVED_ADVISOR_JSON_KEYS = new Set(["typesafe_api_key"]);
|
|
45
|
+
|
|
46
|
+
export const readExistingConfig = (path: string): RecordValue => {
|
|
40
47
|
try {
|
|
41
|
-
const parsed = JSON.parse(readFileSync(path, "
|
|
42
|
-
return parsed
|
|
43
|
-
? (parsed as Record<string, unknown>)
|
|
44
|
-
: {};
|
|
48
|
+
const parsed: unknown = JSON.parse(readFileSync(path, "utf-8"));
|
|
49
|
+
return isRecord(parsed) ? parsed : {};
|
|
45
50
|
} catch {
|
|
46
51
|
return {};
|
|
47
52
|
}
|
|
@@ -56,12 +61,14 @@ const shouldPersistConfigKey = (
|
|
|
56
61
|
(key !== "executor" || persistExecutor);
|
|
57
62
|
|
|
58
63
|
const applyChangedConfigValues = (
|
|
59
|
-
|
|
64
|
+
existing: RecordValue,
|
|
60
65
|
current: ConfigState,
|
|
61
66
|
changedKeys: readonly SavedConfigKey[],
|
|
62
67
|
persistAdvisor: boolean,
|
|
63
68
|
persistExecutor: boolean
|
|
64
|
-
) => {
|
|
69
|
+
): RecordValue => {
|
|
70
|
+
const dropped = new Set<string>();
|
|
71
|
+
const data: RecordValue = { ...existing };
|
|
65
72
|
for (const key of changedKeys) {
|
|
66
73
|
if (!shouldPersistConfigKey(key, persistAdvisor, persistExecutor)) {
|
|
67
74
|
continue;
|
|
@@ -69,11 +76,21 @@ const applyChangedConfigValues = (
|
|
|
69
76
|
const value = current[key];
|
|
70
77
|
const isEmptyModelRef = (key === "advisor" || key === "executor") && !value;
|
|
71
78
|
if (value === undefined || isEmptyModelRef) {
|
|
72
|
-
|
|
79
|
+
dropped.add(key);
|
|
73
80
|
} else {
|
|
74
81
|
data[key] = value;
|
|
75
82
|
}
|
|
76
83
|
}
|
|
84
|
+
if (dropped.size === 0) {
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
const retained: RecordValue = {};
|
|
88
|
+
for (const key of Object.keys(data)) {
|
|
89
|
+
if (!dropped.has(key)) {
|
|
90
|
+
retained[key] = data[key];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return retained;
|
|
77
94
|
};
|
|
78
95
|
|
|
79
96
|
// Tracks the last load/save state so a later save merges fresh disk contents instead of overwriting an external edit.
|
|
@@ -81,7 +98,7 @@ let loadedConfigState: ConfigState | undefined;
|
|
|
81
98
|
let loadedConfigPath: string | undefined;
|
|
82
99
|
|
|
83
100
|
const readConfig = (path: string): AdvisorConfig => {
|
|
84
|
-
const config = JSON.parse(readFileSync(path, "
|
|
101
|
+
const config = JSON.parse(readFileSync(path, "utf-8"));
|
|
85
102
|
validateConfig(config, path);
|
|
86
103
|
return config;
|
|
87
104
|
};
|
|
@@ -137,8 +154,8 @@ export const loadConfig = (_ctx: ExtensionContext) => {
|
|
|
137
154
|
);
|
|
138
155
|
if (globalConfig) {
|
|
139
156
|
applyConfig(globalConfig);
|
|
140
|
-
const unknownKeys = unknownConfigKeys(
|
|
141
|
-
|
|
157
|
+
const unknownKeys = unknownConfigKeys(globalConfig).filter(
|
|
158
|
+
(key) => !RESERVED_ADVISOR_JSON_KEYS.has(key)
|
|
142
159
|
);
|
|
143
160
|
const warningIdentity = `${global}:${configIdentity(global)}`;
|
|
144
161
|
if (
|
|
@@ -191,9 +208,8 @@ export const saveConfig = (
|
|
|
191
208
|
if (changedKeys.length === 0) {
|
|
192
209
|
return path;
|
|
193
210
|
}
|
|
194
|
-
const data =
|
|
195
|
-
|
|
196
|
-
data,
|
|
211
|
+
const data = applyChangedConfigValues(
|
|
212
|
+
existing,
|
|
197
213
|
current,
|
|
198
214
|
changedKeys,
|
|
199
215
|
persistAdvisor,
|
|
@@ -206,14 +222,14 @@ export const saveConfig = (
|
|
|
206
222
|
nextLoadedState.advisor = baseline
|
|
207
223
|
? baseline.advisor
|
|
208
224
|
: configuredModelRef(
|
|
209
|
-
|
|
225
|
+
isString(existing.advisor) ? existing.advisor : undefined
|
|
210
226
|
);
|
|
211
227
|
}
|
|
212
228
|
if (!persistExecutor) {
|
|
213
229
|
nextLoadedState.executor = baseline
|
|
214
230
|
? baseline.executor
|
|
215
231
|
: configuredModelRef(
|
|
216
|
-
|
|
232
|
+
isString(existing.executor) ? existing.executor : undefined
|
|
217
233
|
);
|
|
218
234
|
}
|
|
219
235
|
loadedConfigState = nextLoadedState;
|
package/src/config/types.ts
CHANGED
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_MAX_BYTES as PI_DEFAULT_MAX_BYTES,
|
|
3
|
+
DEFAULT_MAX_LINES as PI_DEFAULT_MAX_LINES,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
import type { GitContextLevel } from "../git.ts";
|
|
7
|
+
|
|
8
|
+
export {
|
|
2
9
|
DEFAULT_MAX_BYTES,
|
|
3
10
|
DEFAULT_MAX_LINES,
|
|
4
11
|
} from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import type { GitContextLevel } from "../git.ts";
|
|
6
12
|
|
|
7
13
|
export const DEFAULT_CONTEXT_MAX_CHARS = 15_000;
|
|
8
14
|
export const MAX_CONTEXT_MAX_CHARS = Number.MAX_SAFE_INTEGER;
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
const DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES = PI_DEFAULT_MAX_LINES;
|
|
16
|
+
const DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES = PI_DEFAULT_MAX_BYTES;
|
|
17
|
+
export {
|
|
18
|
+
DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES,
|
|
19
|
+
DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES,
|
|
20
|
+
};
|
|
11
21
|
export const DEFAULT_ADVISOR_GIT_CONTEXT_MAX_CHARS = 20_000;
|
|
22
|
+
export const DEFAULT_JEV_MODEL = "jev-latest";
|
|
23
|
+
// Provisional: the latency benchmark was dropped from scope; revisit if live
|
|
24
|
+
// measurements suggest a different bound.
|
|
25
|
+
export const DEFAULT_JEV_TIMEOUT_MS = 8000;
|
|
26
|
+
export const DEFAULT_JEV_DIGEST_MAX_CHARS = 4000;
|
|
27
|
+
export const DEFAULT_JEV_PRICE_PER_MTOK = 0.042;
|
|
28
|
+
export const DEFAULT_JEV_FILTER_SKIP_CONFIDENCE = 0.85;
|
|
29
|
+
export const DEFAULT_JEV_FILTER_NOUL_MARGIN = 0.35;
|
|
30
|
+
export const DEFAULT_JEV_FILTER_OVERRIDE_WINDOW = 10;
|
|
31
|
+
export const DEFAULT_JEV_TURN_GATE_EVERY_TURNS = 0;
|
|
32
|
+
export const DEFAULT_JEV_TURN_GATE_NOUL_THRESHOLD = 0.8;
|
|
33
|
+
export type JevTransport = "auto" | "typesafe" | "openrouter";
|
|
34
|
+
export const DEFAULT_JEV_TRANSPORT: JevTransport = "auto";
|
|
35
|
+
export const JEV_TRANSPORTS: JevTransport[] = [
|
|
36
|
+
"auto",
|
|
37
|
+
"typesafe",
|
|
38
|
+
"openrouter",
|
|
39
|
+
];
|
|
12
40
|
|
|
13
41
|
export type AdvisorToolPolicy = "full" | "summary" | "exclude";
|
|
14
42
|
export type AdvisorToolPolicies = Record<string, AdvisorToolPolicy>;
|
|
@@ -40,8 +68,20 @@ export interface AdvisorConfig {
|
|
|
40
68
|
advisorGitContext?: GitContextLevel;
|
|
41
69
|
advisorGitContextMaxChars?: number;
|
|
42
70
|
advisorHerdrIntegration?: boolean;
|
|
71
|
+
advisorJevDigestMaxChars?: number;
|
|
72
|
+
advisorJevFilterEnabled?: boolean;
|
|
73
|
+
advisorJevFilterNoulMargin?: number;
|
|
74
|
+
advisorJevFilterOverrideWindow?: number;
|
|
75
|
+
advisorJevFilterSkipConfidence?: number;
|
|
76
|
+
advisorJevModel?: string;
|
|
77
|
+
advisorJevPricePerMtok?: number;
|
|
78
|
+
advisorJevTimeoutMs?: number;
|
|
79
|
+
advisorJevTransport?: JevTransport;
|
|
80
|
+
advisorJevTurnGateEveryTurns?: number;
|
|
81
|
+
advisorJevTurnGateNoulThreshold?: number;
|
|
43
82
|
advisorLoopThreshold?: number;
|
|
44
83
|
advisorMaxCallsPerSession?: number;
|
|
84
|
+
advisorModelWhitelist?: string[];
|
|
45
85
|
advisorOutcomeLogging?: boolean;
|
|
46
86
|
advisorPlanGate?: boolean;
|
|
47
87
|
advisorRedactSecrets?: boolean;
|
|
@@ -61,3 +101,5 @@ export interface AdvisorConfig {
|
|
|
61
101
|
showUsageFooter?: boolean;
|
|
62
102
|
simpleMode?: boolean;
|
|
63
103
|
}
|
|
104
|
+
|
|
105
|
+
export type AdvisorConfigValue = AdvisorConfig[keyof AdvisorConfig];
|