pi-advisor-flow 0.5.6 → 0.7.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 +29 -0
- package/README.md +1 -0
- package/dist/index.js +3160 -1255
- package/extensions/index.ts +9 -9
- package/package.json +12 -8
- package/src/{untracked.ts → attachments.ts} +2 -5
- package/src/commands/activation-preparation.ts +6 -6
- package/src/commands/activation.ts +7 -7
- package/src/commands/lifecycle.ts +5 -5
- package/src/commands/manual-command.ts +13 -9
- package/src/commands/manual-consultation.ts +24 -10
- package/src/commands/manual-progress.ts +3 -3
- package/src/commands/model-commands.ts +6 -6
- package/src/commands/model-options.ts +4 -4
- package/src/commands/model-picker.ts +2 -2
- package/src/commands/registration.ts +8 -8
- package/src/commands/renderers.ts +5 -5
- package/src/commands/runtime.ts +8 -8
- package/src/commands/settings-commands.ts +8 -8
- package/src/commands/settings-persistence.ts +52 -4
- package/src/commands/types.ts +6 -13
- package/src/commands.ts +2 -2
- package/src/config/args.ts +3 -3
- package/src/config/defaults.ts +78 -3
- package/src/config/schema.ts +434 -0
- package/src/config/state.ts +71 -2
- package/src/config/storage.ts +27 -119
- package/src/config/types.ts +30 -1
- package/src/config/validation.ts +61 -181
- package/src/config.ts +7 -7
- package/src/content-utils.ts +30 -0
- package/src/conversation.ts +13 -168
- package/src/git.ts +4 -18
- package/src/herdr.ts +6 -8
- package/src/jev/client.ts +348 -0
- package/src/jev/key-store.ts +252 -0
- package/src/jev/ledger.ts +234 -0
- package/src/jev/questions.ts +119 -0
- package/src/jev/state.ts +47 -0
- package/src/jev/transport.ts +58 -0
- package/src/model-stream.ts +5 -8
- package/src/outcomes.ts +3 -2
- package/src/pi-settings.ts +59 -0
- package/src/preferences.ts +2 -2
- package/src/redaction.ts +50 -0
- package/src/scout-context.ts +121 -485
- package/src/scout-curation.ts +84 -0
- package/src/scout-groups.ts +485 -0
- package/src/scout-protocol.ts +122 -0
- package/src/scout-reconstruct.ts +44 -0
- package/src/scout-types.ts +66 -0
- package/src/scout.ts +53 -63
- package/src/session-state.ts +215 -78
- package/src/tool-result-cap.ts +88 -0
- package/src/tools/consult-context.ts +155 -0
- package/src/tools/consultation.ts +71 -220
- package/src/tools/gate-policy.ts +5 -5
- package/src/tools/gate-protocol.ts +2 -2
- package/src/tools/jev-filter.ts +202 -0
- package/src/tools/jev-turn-gate.ts +183 -0
- package/src/tools/loop-gate.ts +71 -61
- package/src/tools/outage-notifier.ts +30 -0
- package/src/tools/prompts.ts +4 -7
- package/src/tools/register-ask-advisor.ts +46 -15
- package/src/tools/register-lifecycle.ts +7 -7
- package/src/tools/register-outcome.ts +5 -5
- package/src/tools/register-renderers.ts +63 -5
- package/src/tools/registration.ts +27 -10
- package/src/tools/render-advisor-result.ts +54 -22
- package/src/tools/render-common.ts +25 -13
- package/src/tools/scout-status.ts +5 -5
- package/src/tools/session.ts +1 -1
- package/src/tools/types.ts +17 -7
- package/src/tools.ts +9 -9
- package/src/ui/jev-setup-submenu.ts +344 -0
- package/src/ui/manual-dialog-render.ts +2 -2
- package/src/ui/manual-dialog.ts +4 -9
- package/src/ui/masked-input.ts +68 -0
- package/src/ui/model-selector.ts +1 -1
- package/src/ui/settings-formatting.ts +2 -2
- package/src/ui/settings-items.ts +166 -5
- package/src/ui/settings-mutations.ts +37 -2
- package/src/ui/settings-selector.ts +5 -5
- package/src/ui/text-setting-submenu.ts +1 -1
- package/src/ui/types.ts +13 -2
- package/src/ui.ts +4 -4
- package/src/usage.ts +4 -7
package/src/config/storage.ts
CHANGED
|
@@ -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.
|
|
8
|
+
import { applyConfig, resetDefaults } from "./defaults.ts";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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,83 +23,25 @@ export const configPaths = (ctx: ExtensionContext) => [
|
|
|
50
23
|
join(getAgentDir(), "advisor.json"),
|
|
51
24
|
];
|
|
52
25
|
|
|
53
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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);
|
|
128
38
|
|
|
129
|
-
|
|
39
|
+
/** Keys advisor.json may hold that are not AdvisorConfig settings but are
|
|
40
|
+
* still legitimate (hand-placed secrets). They stay out of CONFIG_SCHEMA, are
|
|
41
|
+
* preserved like any unknown key, and never trigger the typo warning. */
|
|
42
|
+
const RESERVED_ADVISOR_JSON_KEYS = new Set(["typesafe_api_key"]);
|
|
43
|
+
|
|
44
|
+
export const readExistingConfig = (path: string): Record<string, unknown> => {
|
|
130
45
|
try {
|
|
131
46
|
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
132
47
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
@@ -166,25 +81,18 @@ const applyChangedConfigValues = (
|
|
|
166
81
|
}
|
|
167
82
|
};
|
|
168
83
|
|
|
169
|
-
// Tracks the
|
|
170
|
-
// save can then merge fresh disk contents without treating untouched in-memory
|
|
171
|
-
// values as an instruction to overwrite an external edit.
|
|
84
|
+
// Tracks the last load/save state so a later save merges fresh disk contents instead of overwriting an external edit.
|
|
172
85
|
let loadedConfigState: ConfigState | undefined;
|
|
173
86
|
let loadedConfigPath: string | undefined;
|
|
174
87
|
|
|
175
88
|
const readConfig = (path: string): AdvisorConfig => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return config;
|
|
180
|
-
} catch (error) {
|
|
181
|
-
throw error instanceof Error ? error : new Error(String(error));
|
|
182
|
-
}
|
|
89
|
+
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
90
|
+
validateConfig(config, path);
|
|
91
|
+
return config;
|
|
183
92
|
};
|
|
184
93
|
|
|
185
|
-
// loadConfig runs
|
|
186
|
-
//
|
|
187
|
-
// while still applying the full reset-then-apply sequence on each call.
|
|
94
|
+
// loadConfig runs per tool call and consultation; caching by path and stat
|
|
95
|
+
// identity keeps the parse off the hot path while still reset-applying each call.
|
|
188
96
|
const configCache = new Map<
|
|
189
97
|
string,
|
|
190
98
|
{ config: AdvisorConfig; identity: string }
|
|
@@ -236,7 +144,7 @@ export const loadConfig = (_ctx: ExtensionContext) => {
|
|
|
236
144
|
applyConfig(globalConfig);
|
|
237
145
|
const unknownKeys = unknownConfigKeys(
|
|
238
146
|
globalConfig as Record<string, unknown>
|
|
239
|
-
);
|
|
147
|
+
).filter((key) => !RESERVED_ADVISOR_JSON_KEYS.has(key));
|
|
240
148
|
const warningIdentity = `${global}:${configIdentity(global)}`;
|
|
241
149
|
if (
|
|
242
150
|
unknownKeys.length > 0 &&
|
package/src/config/types.ts
CHANGED
|
@@ -2,13 +2,31 @@ 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.
|
|
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;
|
|
9
9
|
export const DEFAULT_ADVISOR_TOOL_RESULT_MAX_LINES = DEFAULT_MAX_LINES;
|
|
10
10
|
export const DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES = DEFAULT_MAX_BYTES;
|
|
11
11
|
export const DEFAULT_ADVISOR_GIT_CONTEXT_MAX_CHARS = 20_000;
|
|
12
|
+
export const DEFAULT_JEV_MODEL = "jev-latest";
|
|
13
|
+
// Provisional: the latency benchmark was dropped from scope; revisit if live
|
|
14
|
+
// measurements suggest a different bound.
|
|
15
|
+
export const DEFAULT_JEV_TIMEOUT_MS = 8000;
|
|
16
|
+
export const DEFAULT_JEV_DIGEST_MAX_CHARS = 4000;
|
|
17
|
+
export const DEFAULT_JEV_PRICE_PER_MTOK = 0.042;
|
|
18
|
+
export const DEFAULT_JEV_FILTER_SKIP_CONFIDENCE = 0.85;
|
|
19
|
+
export const DEFAULT_JEV_FILTER_NOUL_MARGIN = 0.35;
|
|
20
|
+
export const DEFAULT_JEV_FILTER_OVERRIDE_WINDOW = 10;
|
|
21
|
+
export const DEFAULT_JEV_TURN_GATE_EVERY_TURNS = 0;
|
|
22
|
+
export const DEFAULT_JEV_TURN_GATE_NOUL_THRESHOLD = 0.8;
|
|
23
|
+
export type JevTransport = "auto" | "typesafe" | "openrouter";
|
|
24
|
+
export const DEFAULT_JEV_TRANSPORT: JevTransport = "auto";
|
|
25
|
+
export const JEV_TRANSPORTS: JevTransport[] = [
|
|
26
|
+
"auto",
|
|
27
|
+
"typesafe",
|
|
28
|
+
"openrouter",
|
|
29
|
+
];
|
|
12
30
|
|
|
13
31
|
export type AdvisorToolPolicy = "full" | "summary" | "exclude";
|
|
14
32
|
export type AdvisorToolPolicies = Record<string, AdvisorToolPolicy>;
|
|
@@ -40,6 +58,17 @@ export interface AdvisorConfig {
|
|
|
40
58
|
advisorGitContext?: GitContextLevel;
|
|
41
59
|
advisorGitContextMaxChars?: number;
|
|
42
60
|
advisorHerdrIntegration?: boolean;
|
|
61
|
+
advisorJevDigestMaxChars?: number;
|
|
62
|
+
advisorJevFilterEnabled?: boolean;
|
|
63
|
+
advisorJevFilterNoulMargin?: number;
|
|
64
|
+
advisorJevFilterOverrideWindow?: number;
|
|
65
|
+
advisorJevFilterSkipConfidence?: number;
|
|
66
|
+
advisorJevModel?: string;
|
|
67
|
+
advisorJevPricePerMtok?: number;
|
|
68
|
+
advisorJevTimeoutMs?: number;
|
|
69
|
+
advisorJevTransport?: JevTransport;
|
|
70
|
+
advisorJevTurnGateEveryTurns?: number;
|
|
71
|
+
advisorJevTurnGateNoulThreshold?: number;
|
|
43
72
|
advisorLoopThreshold?: number;
|
|
44
73
|
advisorMaxCallsPerSession?: number;
|
|
45
74
|
advisorOutcomeLogging?: boolean;
|
package/src/config/validation.ts
CHANGED
|
@@ -1,72 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"advisorSessionSummary",
|
|
25
|
-
"advisorScoutEnabled",
|
|
26
|
-
"showUsageDetails",
|
|
27
|
-
"showUsageFooter",
|
|
28
|
-
"simpleMode",
|
|
29
|
-
"alwaysOn",
|
|
30
|
-
"advisorToolResultMaxBytes",
|
|
31
|
-
"advisorToolResultMaxLines",
|
|
32
|
-
"advisorTrackedFileContent",
|
|
33
|
-
"advisorRedactSecrets",
|
|
34
|
-
"advisorUntrackedContent",
|
|
35
|
-
"advisorOutcomeLogging",
|
|
36
|
-
"advisorToolPolicies",
|
|
37
|
-
"contextMaxChars",
|
|
38
|
-
"executor",
|
|
39
|
-
"executorEffort",
|
|
40
|
-
"gateFailureMode",
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
const BOOLEAN_CONFIG_KEYS = [
|
|
44
|
-
"advisorPlanGate",
|
|
45
|
-
"advisorFailureGate",
|
|
46
|
-
"advisorCompletionGate",
|
|
47
|
-
"advisorCollapseResponses",
|
|
48
|
-
"advisorBlockOnBlocked",
|
|
49
|
-
"advisorAutoLoopGate",
|
|
50
|
-
"advisorSessionSummary",
|
|
51
|
-
"advisorScoutEnabled",
|
|
52
|
-
"showUsageDetails",
|
|
53
|
-
"showUsageFooter",
|
|
54
|
-
"simpleMode",
|
|
55
|
-
"alwaysOn",
|
|
56
|
-
"advisorHerdrIntegration",
|
|
57
|
-
"advisorTrackedFileContent",
|
|
58
|
-
"advisorRedactSecrets",
|
|
59
|
-
"advisorUntrackedContent",
|
|
60
|
-
"advisorOutcomeLogging",
|
|
61
|
-
] as const;
|
|
1
|
+
import type { ConfigKeySchema } from "./schema.ts";
|
|
2
|
+
import { CONFIG_SCHEMA, type ConfigKey, SCHEMA_BY_KEY } from "./schema.ts";
|
|
3
|
+
import type { AdvisorConfig } from "./types.ts";
|
|
4
|
+
|
|
5
|
+
// biome-ignore lint/performance/noBarrelFile: re-exports keep the isValid* validators on their historical module for the config facade.
|
|
6
|
+
export {
|
|
7
|
+
isValidAdvisorToolPolicies,
|
|
8
|
+
isValidContextMaxChars,
|
|
9
|
+
isValidGateFailureMode,
|
|
10
|
+
isValidLoopThreshold,
|
|
11
|
+
isValidMaxCallsPerSession,
|
|
12
|
+
isValidToolResultMaxBytes,
|
|
13
|
+
isValidToolResultMaxLines,
|
|
14
|
+
} from "./schema.ts";
|
|
15
|
+
|
|
16
|
+
const CONFIG_KEYS = new Set<ConfigKey>(
|
|
17
|
+
Object.keys(CONFIG_SCHEMA) as ConfigKey[]
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const keysOfType = (type: ConfigKeySchema["type"]): readonly ConfigKey[] =>
|
|
21
|
+
(Object.keys(CONFIG_SCHEMA) as ConfigKey[]).filter(
|
|
22
|
+
(key) => SCHEMA_BY_KEY[key].type === type
|
|
23
|
+
);
|
|
62
24
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
"advisor",
|
|
66
|
-
"executorEffort",
|
|
67
|
-
"advisorEffort",
|
|
68
|
-
"advisorCustomInvocation",
|
|
69
|
-
] as const;
|
|
25
|
+
const BOOLEAN_CONFIG_KEYS = keysOfType("boolean");
|
|
26
|
+
const STRING_CONFIG_KEYS = keysOfType("string");
|
|
70
27
|
|
|
71
28
|
type ConfigRecord = Record<string, unknown>;
|
|
72
29
|
|
|
@@ -81,20 +38,12 @@ const invalidConfigValue = (
|
|
|
81
38
|
};
|
|
82
39
|
|
|
83
40
|
export const unknownConfigKeys = (config: ConfigRecord) =>
|
|
84
|
-
Object.keys(config).filter(
|
|
85
|
-
(key) => !CONFIG_KEYS.has(key as keyof AdvisorConfig)
|
|
86
|
-
);
|
|
41
|
+
Object.keys(config).filter((key) => !CONFIG_KEYS.has(key as ConfigKey));
|
|
87
42
|
|
|
88
43
|
const validateStringValues = (config: ConfigRecord, path: string) => {
|
|
89
44
|
for (const key of STRING_CONFIG_KEYS) {
|
|
90
45
|
if (config[key] !== undefined && typeof config[key] !== "string") {
|
|
91
|
-
invalidConfigValue(
|
|
92
|
-
path,
|
|
93
|
-
key,
|
|
94
|
-
key === "executor" || key === "advisor"
|
|
95
|
-
? "a provider/model string"
|
|
96
|
-
: "a string"
|
|
97
|
-
);
|
|
46
|
+
invalidConfigValue(path, key, SCHEMA_BY_KEY[key].accepted);
|
|
98
47
|
}
|
|
99
48
|
}
|
|
100
49
|
};
|
|
@@ -102,91 +51,46 @@ const validateStringValues = (config: ConfigRecord, path: string) => {
|
|
|
102
51
|
const validateBooleanValues = (config: ConfigRecord, path: string) => {
|
|
103
52
|
for (const key of BOOLEAN_CONFIG_KEYS) {
|
|
104
53
|
if (config[key] !== undefined && typeof config[key] !== "boolean") {
|
|
105
|
-
invalidConfigValue(path, key,
|
|
54
|
+
invalidConfigValue(path, key, SCHEMA_BY_KEY[key].accepted);
|
|
106
55
|
}
|
|
107
56
|
}
|
|
108
57
|
};
|
|
109
58
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
59
|
+
const validateNumericValues = (config: ConfigRecord, path: string) => {
|
|
60
|
+
for (const key of keysOfType("number")) {
|
|
61
|
+
const isValid = SCHEMA_BY_KEY[key].validate;
|
|
62
|
+
if (
|
|
63
|
+
config[key] !== undefined &&
|
|
64
|
+
isValid !== undefined &&
|
|
65
|
+
!isValid(config[key])
|
|
66
|
+
) {
|
|
67
|
+
invalidConfigValue(path, key, SCHEMA_BY_KEY[key].accepted);
|
|
68
|
+
}
|
|
115
69
|
}
|
|
116
|
-
return Object.entries(value).every(
|
|
117
|
-
([toolName, policy]) =>
|
|
118
|
-
toolName.trim().length > 0 &&
|
|
119
|
-
typeof policy === "string" &&
|
|
120
|
-
ADVISOR_TOOL_POLICIES.includes(
|
|
121
|
-
policy as (typeof ADVISOR_TOOL_POLICIES)[number]
|
|
122
|
-
)
|
|
123
|
-
);
|
|
124
70
|
};
|
|
125
71
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
export const isValidGateFailureMode = (
|
|
139
|
-
value: unknown
|
|
140
|
-
): value is (typeof GATE_FAILURE_MODES)[number] =>
|
|
141
|
-
typeof value === "string" &&
|
|
142
|
-
GATE_FAILURE_MODES.includes(value as (typeof GATE_FAILURE_MODES)[number]);
|
|
143
|
-
|
|
144
|
-
export const isValidToolResultMaxLines = (value: unknown): value is number =>
|
|
145
|
-
typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
146
|
-
|
|
147
|
-
export const isValidToolResultMaxBytes = (value: unknown): value is number =>
|
|
148
|
-
typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
72
|
+
const validateEnumValues = (config: ConfigRecord, path: string) => {
|
|
73
|
+
for (const key of keysOfType("enum")) {
|
|
74
|
+
const isValid = SCHEMA_BY_KEY[key].validate;
|
|
75
|
+
if (
|
|
76
|
+
config[key] !== undefined &&
|
|
77
|
+
isValid !== undefined &&
|
|
78
|
+
!isValid(config[key])
|
|
79
|
+
) {
|
|
80
|
+
invalidConfigValue(path, key, SCHEMA_BY_KEY[key].accepted);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
149
84
|
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
`a safe integer from 0 through ${MAX_CONTEXT_MAX_CHARS}`,
|
|
160
|
-
],
|
|
161
|
-
[
|
|
162
|
-
"advisorLoopThreshold",
|
|
163
|
-
isValidLoopThreshold,
|
|
164
|
-
"a safe integer of at least 2",
|
|
165
|
-
],
|
|
166
|
-
[
|
|
167
|
-
"advisorMaxCallsPerSession",
|
|
168
|
-
isValidMaxCallsPerSession,
|
|
169
|
-
"a non-negative safe integer",
|
|
170
|
-
],
|
|
171
|
-
[
|
|
172
|
-
"advisorToolResultMaxLines",
|
|
173
|
-
isValidToolResultMaxLines,
|
|
174
|
-
"a non-negative safe integer",
|
|
175
|
-
],
|
|
176
|
-
[
|
|
177
|
-
"advisorToolResultMaxBytes",
|
|
178
|
-
isValidToolResultMaxBytes,
|
|
179
|
-
"a non-negative safe integer",
|
|
180
|
-
],
|
|
181
|
-
[
|
|
182
|
-
"advisorGitContextMaxChars",
|
|
183
|
-
isValidToolResultMaxBytes,
|
|
184
|
-
"a non-negative safe integer",
|
|
185
|
-
],
|
|
186
|
-
];
|
|
187
|
-
for (const [key, isValid, description] of numericRules) {
|
|
188
|
-
if (config[key] !== undefined && !isValid(config[key])) {
|
|
189
|
-
invalidConfigValue(path, key, description);
|
|
85
|
+
const validateObjectValues = (config: ConfigRecord, path: string) => {
|
|
86
|
+
for (const key of keysOfType("object")) {
|
|
87
|
+
const isValid = SCHEMA_BY_KEY[key].validate;
|
|
88
|
+
if (
|
|
89
|
+
config[key] !== undefined &&
|
|
90
|
+
isValid !== undefined &&
|
|
91
|
+
!isValid(config[key])
|
|
92
|
+
) {
|
|
93
|
+
invalidConfigValue(path, key, SCHEMA_BY_KEY[key].accepted);
|
|
190
94
|
}
|
|
191
95
|
}
|
|
192
96
|
};
|
|
@@ -204,31 +108,7 @@ export const validateConfig = (
|
|
|
204
108
|
validateStringValues(config, path);
|
|
205
109
|
validateBooleanValues(config, path);
|
|
206
110
|
validateNumericValues(config, path);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
!isValidAdvisorToolPolicies(config.advisorToolPolicies)
|
|
210
|
-
) {
|
|
211
|
-
invalidConfigValue(
|
|
212
|
-
path,
|
|
213
|
-
"advisorToolPolicies",
|
|
214
|
-
"a JSON object with non-empty tool names and full, summary, or exclude values"
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
if (
|
|
218
|
-
config.gateFailureMode !== undefined &&
|
|
219
|
-
!isValidGateFailureMode(config.gateFailureMode)
|
|
220
|
-
) {
|
|
221
|
-
invalidConfigValue(path, "gateFailureMode", GATE_FAILURE_MODES.join(", "));
|
|
222
|
-
}
|
|
223
|
-
if (
|
|
224
|
-
config.advisorGitContext !== undefined &&
|
|
225
|
-
!isValidGitContextLevel(config.advisorGitContext)
|
|
226
|
-
) {
|
|
227
|
-
invalidConfigValue(
|
|
228
|
-
path,
|
|
229
|
-
"advisorGitContext",
|
|
230
|
-
GIT_CONTEXT_LEVELS.join(", ")
|
|
231
|
-
);
|
|
232
|
-
}
|
|
111
|
+
validateObjectValues(config, path);
|
|
112
|
+
validateEnumValues(config, path);
|
|
233
113
|
return true;
|
|
234
114
|
};
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// biome-ignore lint/performance/noBarrelFile: this facade intentionally preserves the public config module contract.
|
|
2
|
-
export { parseArgs } from "./config/args.
|
|
2
|
+
export { parseArgs } from "./config/args.ts";
|
|
3
3
|
export {
|
|
4
4
|
advisorAutoLoopGateRef,
|
|
5
5
|
advisorBlockOnBlockedRef,
|
|
@@ -68,21 +68,21 @@ export {
|
|
|
68
68
|
showUsageFooterRef,
|
|
69
69
|
simpleModeRef,
|
|
70
70
|
splitRef,
|
|
71
|
-
} from "./config/state.
|
|
72
|
-
export type { SaveConfigOptions } from "./config/storage.
|
|
71
|
+
} from "./config/state.ts";
|
|
72
|
+
export type { SaveConfigOptions } from "./config/storage.ts";
|
|
73
73
|
export {
|
|
74
74
|
configPaths,
|
|
75
75
|
loadConfig,
|
|
76
76
|
resetConfigCache,
|
|
77
77
|
saveConfig,
|
|
78
78
|
saveGlobalOutcomeLogging,
|
|
79
|
-
} from "./config/storage.
|
|
79
|
+
} from "./config/storage.ts";
|
|
80
80
|
export type {
|
|
81
81
|
AdvisorConfig,
|
|
82
82
|
AdvisorToolPolicies,
|
|
83
83
|
AdvisorToolPolicy,
|
|
84
84
|
GateFailureMode,
|
|
85
|
-
} from "./config/types.
|
|
85
|
+
} from "./config/types.ts";
|
|
86
86
|
export {
|
|
87
87
|
ADVISOR_TOOL_POLICIES,
|
|
88
88
|
DEFAULT_ADVISOR_GIT_CONTEXT_MAX_CHARS,
|
|
@@ -91,7 +91,7 @@ export {
|
|
|
91
91
|
DEFAULT_CONTEXT_MAX_CHARS,
|
|
92
92
|
GATE_FAILURE_MODES,
|
|
93
93
|
MAX_CONTEXT_MAX_CHARS,
|
|
94
|
-
} from "./config/types.
|
|
94
|
+
} from "./config/types.ts";
|
|
95
95
|
export {
|
|
96
96
|
isValidAdvisorToolPolicies,
|
|
97
97
|
isValidContextMaxChars,
|
|
@@ -101,4 +101,4 @@ export {
|
|
|
101
101
|
isValidToolResultMaxBytes,
|
|
102
102
|
isValidToolResultMaxLines,
|
|
103
103
|
validateConfig,
|
|
104
|
-
} from "./config/validation.
|
|
104
|
+
} from "./config/validation.ts";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Shared untyped-content inspection helpers. */
|
|
2
|
+
|
|
3
|
+
export type RecordValue = Record<string, unknown>;
|
|
4
|
+
|
|
5
|
+
export const isRecord = (value: unknown): value is RecordValue =>
|
|
6
|
+
Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
|
|
8
|
+
export const byteLength = (value: string) => Buffer.byteLength(value, "utf8");
|
|
9
|
+
|
|
10
|
+
export const contentParts = (content: unknown): unknown[] => {
|
|
11
|
+
if (typeof content === "string") {
|
|
12
|
+
return [content];
|
|
13
|
+
}
|
|
14
|
+
return Array.isArray(content) ? content : [];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Longest UTF-8 prefix of `value` within `maxBytes`, in linear time. */
|
|
18
|
+
export const capUtf8Bytes = (value: string, maxBytes: number): string => {
|
|
19
|
+
let result = "";
|
|
20
|
+
let used = 0;
|
|
21
|
+
for (const character of value) {
|
|
22
|
+
const characterBytes = byteLength(character);
|
|
23
|
+
if (used + characterBytes > maxBytes) {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
result += character;
|
|
27
|
+
used += characterBytes;
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
};
|