pi-advisor-flow 0.9.2-dev.4 → 0.10.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 (40) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +16 -7
  3. package/dist/index.js +938 -287
  4. package/package.json +2 -2
  5. package/src/commands/activation-preparation.ts +4 -0
  6. package/src/commands/manual-consultation.ts +20 -3
  7. package/src/commands/model-commands.ts +5 -1
  8. package/src/commands/model-picker.ts +44 -2
  9. package/src/commands/outcome-stats.ts +98 -0
  10. package/src/commands/registration.ts +2 -0
  11. package/src/commands/renderers.ts +51 -0
  12. package/src/commands/runtime.ts +9 -1
  13. package/src/commands/settings-persistence.ts +4 -0
  14. package/src/commands/types.ts +5 -2
  15. package/src/config/defaults.ts +13 -0
  16. package/src/config/schema.ts +14 -0
  17. package/src/config/state.ts +10 -0
  18. package/src/config/types.ts +2 -0
  19. package/src/config.ts +4 -0
  20. package/src/follow-up.ts +85 -0
  21. package/src/outcome-stats.ts +154 -0
  22. package/src/outcomes.ts +8 -1
  23. package/src/preferences.ts +90 -12
  24. package/src/session-state.ts +33 -0
  25. package/src/tools/consult-context.ts +12 -1
  26. package/src/tools/consultation.ts +344 -47
  27. package/src/tools/jev-turn-gate.ts +2 -0
  28. package/src/tools/loop-gate.ts +2 -0
  29. package/src/tools/model-access.ts +7 -3
  30. package/src/tools/prompts.ts +28 -2
  31. package/src/tools/register-ask-advisor.ts +110 -23
  32. package/src/tools/register-lifecycle.ts +11 -0
  33. package/src/tools/register-renderers.ts +40 -6
  34. package/src/tools/render-advisor-result.ts +6 -0
  35. package/src/tools/render-common.ts +6 -2
  36. package/src/tools/types.ts +5 -0
  37. package/src/ui/settings-items.ts +43 -0
  38. package/src/ui/settings-mutations.ts +9 -0
  39. package/src/ui/settings-selector.ts +9 -0
  40. package/src/ui/types.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-advisor-flow",
3
- "version": "0.9.2-dev.4",
3
+ "version": "0.10.0",
4
4
  "description": "Advanced Executor/Advisor flow for Pi, fully configurable and extendable.",
5
5
  "keywords": [
6
6
  "advisor",
@@ -55,7 +55,7 @@
55
55
  "@earendil-works/pi-coding-agent": "1.0.0",
56
56
  "@earendil-works/pi-tui": "1.0.0",
57
57
  "@oxlint/plugins": "1.86.0",
58
- "@types/node": "^26.6.3",
58
+ "@types/node": "^26.6.4",
59
59
  "@typesafe-ai/sdk": "^0.6.0",
60
60
  "bun-types": "1.4.2",
61
61
  "husky": "^9.1.7",
@@ -2,11 +2,13 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  import {
4
4
  advisorEffortRef,
5
+ advisorFallbackModelRef,
5
6
  advisorRef,
6
7
  executorEffortRef,
7
8
  executorRef,
8
9
  getPersistedModelRefs,
9
10
  setAdvisorEffortRef,
11
+ setAdvisorFallbackModelRef,
10
12
  setAdvisorRef,
11
13
  setExecutorEffortRef,
12
14
  setExecutorRef,
@@ -99,6 +101,7 @@ export const prepareActivationModels = async (
99
101
  const selection = await selectAdvisorModels(ctx, {
100
102
  advisor: advisorOverride || persisted.advisor ? advisorRef : "",
101
103
  advisorEffort: advisorEffortRef,
104
+ advisorFallbackModel: advisorFallbackModelRef,
102
105
  executor:
103
106
  executorOverride || plan.pendingExecutor || persisted.executor
104
107
  ? executorRef
@@ -111,6 +114,7 @@ export const prepareActivationModels = async (
111
114
  return;
112
115
  }
113
116
  setAdvisorRef(selection.advisor);
117
+ setAdvisorFallbackModelRef(selection.advisorFallbackModel);
114
118
  setAdvisorEffortRef(selection.advisorEffort);
115
119
  setExecutorRef(selection.executor);
116
120
  setExecutorEffortRef(selection.executorEffort);
@@ -14,7 +14,11 @@ import { notify } from "./runtime.ts";
14
14
  import type { CommandRuntime, ManualAdvisorProgressState } from "./types.ts";
15
15
 
16
16
  interface ManualResultDetails {
17
+ adviceId?: string;
17
18
  advisor: string;
19
+ agentRulesBytes?: number;
20
+ followUp?: boolean;
21
+ preferenceBytes?: number;
18
22
  question?: string;
19
23
  text: string;
20
24
  usage?: AdvisorUsageSnapshot;
@@ -48,7 +52,15 @@ export const startManualConsultation = async (
48
52
  const finishHerdrActivity = runtime.herdrActivity.start();
49
53
  let scoutDetails: ScoutToolDetails | undefined;
50
54
  try {
51
- const { adviceId, markdown, usage } = await runtime.requestAdvisor(
55
+ const {
56
+ adviceId,
57
+ agentRulesBytes,
58
+ followUp,
59
+ markdown,
60
+ model,
61
+ preferenceBytes,
62
+ usage,
63
+ } = await runtime.requestAdvisor(
52
64
  ctx,
53
65
  question,
54
66
  controller.signal,
@@ -84,8 +96,9 @@ export const startManualConsultation = async (
84
96
  runtime.advisorSessionState.recordInvocation({
85
97
  cost: advisorUsageCost(usage),
86
98
  executionEffect: "continued",
99
+ followUp,
87
100
  kind: "markdown",
88
- model: advisorRef,
101
+ model: model ?? advisorRef,
89
102
  trigger: "manual",
90
103
  usage,
91
104
  });
@@ -102,7 +115,11 @@ export const startManualConsultation = async (
102
115
  }
103
116
  runtime.updateAdvisorUsageStatus(ctx);
104
117
  const details: ManualResultDetails = {
105
- advisor: advisorRef,
118
+ adviceId,
119
+ advisor: model ?? advisorRef,
120
+ agentRulesBytes,
121
+ followUp,
122
+ preferenceBytes,
106
123
  question,
107
124
  text: markdown,
108
125
  };
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  advisorEffortRef,
3
+ advisorFallbackModelRef,
3
4
  advisorRef,
4
5
  executorEffortRef,
5
6
  executorRef,
6
7
  getPersistedModelRefs,
7
8
  setAdvisorEffortRef,
9
+ setAdvisorFallbackModelRef,
8
10
  setAdvisorRef,
9
11
  setExecutorEffortRef,
10
12
  setExecutorRef,
@@ -24,7 +26,7 @@ export const registerModelCommands = (runtime: CommandRuntime) => {
24
26
 
25
27
  runtime.pi.registerCommand("advisor-models", {
26
28
  description:
27
- "Select and persist the Executor and Advisor models with reasoning levels",
29
+ "Select and persist the Executor, Advisor, and optional fallback models with reasoning levels",
28
30
  handler: async (_args, ctx) => {
29
31
  if (!(loadCommandConfig(ctx) && ctx.hasUI)) {
30
32
  return;
@@ -36,6 +38,7 @@ export const registerModelCommands = (runtime: CommandRuntime) => {
36
38
  const selection = await selectAdvisorModels(ctx, {
37
39
  advisor: persisted.advisor ? advisorRef : "",
38
40
  advisorEffort: advisorEffortRef,
41
+ advisorFallbackModel: advisorFallbackModelRef,
39
42
  executor:
40
43
  runtime.pendingExecutorModelRef ??
41
44
  (persisted.executor ? executorRef : ""),
@@ -49,6 +52,7 @@ export const registerModelCommands = (runtime: CommandRuntime) => {
49
52
 
50
53
  setExecutorRef(selection.executor);
51
54
  setAdvisorRef(selection.advisor);
55
+ setAdvisorFallbackModelRef(selection.advisorFallbackModel);
52
56
  setExecutorEffortRef(selection.executorEffort);
53
57
  setAdvisorEffortRef(selection.advisorEffort);
54
58
 
@@ -1,6 +1,7 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  import { SearchableModelSelector } from "../ui/model-selector.ts";
4
+ import { FALLBACK_ADVISOR_MODEL_DISABLED } from "../ui/types.ts";
4
5
  import {
5
6
  effortChoices,
6
7
  getAvailableModelRefs,
@@ -10,6 +11,7 @@ import {
10
11
  export interface AdvisorModelSelection {
11
12
  advisor: string;
12
13
  advisorEffort: string | undefined;
14
+ advisorFallbackModel: string | undefined;
13
15
  executor: string;
14
16
  executorEffort: string | undefined;
15
17
  }
@@ -17,6 +19,7 @@ export interface AdvisorModelSelection {
17
19
  export interface AdvisorModelPickerOptions {
18
20
  advisor: string | undefined;
19
21
  advisorEffort: string | undefined;
22
+ advisorFallbackModel: string | undefined;
20
23
  executor: string | undefined;
21
24
  executorEffort: string | undefined;
22
25
  selectAdvisor: boolean;
@@ -46,7 +49,13 @@ export const selectAdvisorModels = async (
46
49
  )
47
50
  ),
48
51
  ];
49
- let { advisor, advisorEffort, executor, executorEffort } = options;
52
+ let {
53
+ advisor,
54
+ advisorEffort,
55
+ advisorFallbackModel,
56
+ executor,
57
+ executorEffort,
58
+ } = options;
50
59
 
51
60
  if (options.selectExecutor) {
52
61
  const selectedExecutor = await ctx.ui.custom<string | undefined>(
@@ -104,8 +113,41 @@ export const selectAdvisorModels = async (
104
113
  advisorEffort = selectedEffort(selectedAdvisorEffort);
105
114
  }
106
115
 
116
+ const selectedFallback = await ctx.ui.custom<string | undefined>(
117
+ (tui, theme, keybindings, done) =>
118
+ new SearchableModelSelector({
119
+ allOptions: [
120
+ FALLBACK_ADVISOR_MODEL_DISABLED,
121
+ ...new Set([
122
+ ...allOptions,
123
+ ...(refs || !advisorFallbackModel ? [] : [advisorFallbackModel]),
124
+ ]),
125
+ ],
126
+ currentOption: advisorFallbackModel ?? FALLBACK_ADVISOR_MODEL_DISABLED,
127
+ keybindings,
128
+ onCancel: () => done(undefined),
129
+ onSelect: done,
130
+ theme,
131
+ title: "Select Fallback Advisor Model (optional)",
132
+ tui,
133
+ })
134
+ );
135
+ if (selectedFallback === undefined) {
136
+ return undefined;
137
+ }
138
+ advisorFallbackModel =
139
+ selectedFallback === FALLBACK_ADVISOR_MODEL_DISABLED
140
+ ? undefined
141
+ : selectedFallback;
142
+
107
143
  if (!(advisor && executor)) {
108
144
  return undefined;
109
145
  }
110
- return { advisor, advisorEffort, executor, executorEffort };
146
+ return {
147
+ advisor,
148
+ advisorEffort,
149
+ advisorFallbackModel,
150
+ executor,
151
+ executorEffort,
152
+ };
111
153
  };
@@ -0,0 +1,98 @@
1
+ import { aggregateOutcomeRecords, readOutcomeLog } from "../outcome-stats.ts";
2
+ import type { OutcomeStats } from "../outcome-stats.ts";
3
+ import { OUTCOME_LOG_MAX_BYTES } from "../outcomes.ts";
4
+ import type { CommandRuntime } from "./types.ts";
5
+
6
+ export interface AdvisorStatsDetails {
7
+ error?: string;
8
+ malformedLines: number;
9
+ stats?: OutcomeStats;
10
+ }
11
+
12
+ const percent = (rate: number | undefined) =>
13
+ rate === undefined ? "n/a" : `${(rate * 100).toFixed(1)}%`;
14
+
15
+ const validatedCount = (value: { failed: number; passed: number }) =>
16
+ value.failed + value.passed;
17
+
18
+ const timestamp = (value: string | undefined) =>
19
+ value ? new Date(value).toLocaleString() : "n/a";
20
+
21
+ export const formatAdvisorStats = ({
22
+ error,
23
+ malformedLines,
24
+ stats,
25
+ }: AdvisorStatsDetails): string => {
26
+ if (error) {
27
+ return `**Could not read Advisor outcomes.**\n\n${error}`;
28
+ }
29
+ if (!stats || stats.total === 0) {
30
+ const ignored = malformedLines
31
+ ? `\n\nIgnored ${malformedLines} malformed ledger line${malformedLines === 1 ? "" : "s"}.`
32
+ : "";
33
+ return `**No Advisor outcomes recorded yet.**\n\nEnable outcome logging and record an outcome after an Advisor response to build this report.${ignored}`;
34
+ }
35
+ const { validationByAdoption } = stats;
36
+ const { followed } = validationByAdoption;
37
+ const notFollowed = validationByAdoption["not-followed"];
38
+ const triggerLines = Object.entries(stats.byTrigger).map(
39
+ ([trigger, count]) => `- ${trigger}: ${count}`
40
+ );
41
+ const adoptionLines = Object.entries(stats.adoption).map(
42
+ ([adoption, count]) => `- ${adoption}: ${count}`
43
+ );
44
+ const validationLines = [
45
+ `- followed: ${percent(followed.passRate)} passed (${followed.passed}/${validatedCount(followed)})`,
46
+ `- not-followed: ${percent(notFollowed.passRate)} passed (${notFollowed.passed}/${validatedCount(notFollowed)})`,
47
+ ];
48
+ const ignored = malformedLines
49
+ ? `\n\nIgnored ${malformedLines} malformed ledger line${malformedLines === 1 ? "" : "s"}.`
50
+ : "";
51
+ return [
52
+ `**${stats.total} Advisor outcome${stats.total === 1 ? "" : "s"}**`,
53
+ "",
54
+ "**By trigger**",
55
+ ...triggerLines,
56
+ "",
57
+ "**Adoption**",
58
+ ...adoptionLines,
59
+ "",
60
+ "**Validation passed rate (passed / passed + failed)**",
61
+ ...validationLines,
62
+ "",
63
+ `Distinct advices seen: ${stats.distinctAdvices}`,
64
+ `Time window: ${timestamp(stats.firstTimestamp)} → ${timestamp(stats.lastTimestamp)}`,
65
+ `Ledger window: capped at ${OUTCOME_LOG_MAX_BYTES / (1024 * 1024)} MiB and rewritten on overflow; this report covers only retained records.${ignored}`,
66
+ ].join("\n");
67
+ };
68
+
69
+ const readAdvisorStats = async (): Promise<AdvisorStatsDetails> => {
70
+ try {
71
+ const log = await readOutcomeLog();
72
+ const stats = aggregateOutcomeRecords(log.records, log.malformedLines);
73
+ return { malformedLines: log.malformedLines, stats };
74
+ } catch (error) {
75
+ return {
76
+ error: error instanceof Error ? error.message : String(error),
77
+ malformedLines: 0,
78
+ };
79
+ }
80
+ };
81
+
82
+ export const registerOutcomeStatsCommand = (runtime: CommandRuntime) => {
83
+ runtime.pi.registerCommand("advisor-stats", {
84
+ description: "Show retained Advisor outcome adoption and validation stats",
85
+ handler: async (_args, _ctx) => {
86
+ const details = await readAdvisorStats();
87
+ runtime.pi.sendMessage(
88
+ {
89
+ content: [],
90
+ customType: "advisor-stats-result",
91
+ details,
92
+ display: true,
93
+ },
94
+ { deliverAs: "steer", triggerTurn: false }
95
+ );
96
+ },
97
+ });
98
+ };
@@ -7,6 +7,7 @@ import { activateAdvisor } from "./activation.ts";
7
7
  import { registerCommandLifecycle } from "./lifecycle.ts";
8
8
  import { registerManualCommand } from "./manual-command.ts";
9
9
  import { registerModelCommands } from "./model-commands.ts";
10
+ import { registerOutcomeStatsCommand } from "./outcome-stats.ts";
10
11
  import { registerCommandRenderers } from "./renderers.ts";
11
12
  import { createCommandRuntime } from "./runtime.ts";
12
13
  import { registerSettingsCommands } from "./settings-commands.ts";
@@ -24,5 +25,6 @@ export const registerCommands = (
24
25
  registerCommandLifecycle(runtime, activate);
25
26
  registerManualCommand(runtime);
26
27
  registerModelCommands(runtime);
28
+ registerOutcomeStatsCommand(runtime);
27
29
  registerSettingsCommands(runtime);
28
30
  };
@@ -15,6 +15,8 @@ import {
15
15
  } from "../tools/render-common.ts";
16
16
  import { formatAdvisorUsage } from "../usage.ts";
17
17
  import { ManualAdvisorProgressComponent } from "./manual-progress.ts";
18
+ import { formatAdvisorStats } from "./outcome-stats.ts";
19
+ import type { AdvisorStatsDetails } from "./outcome-stats.ts";
18
20
  import type { CommandRuntime } from "./types.ts";
19
21
 
20
22
  interface ManualCallEntryDetails {
@@ -24,10 +26,22 @@ interface ManualCallEntryDetails {
24
26
 
25
27
  interface ManualResultDetails {
26
28
  advisor?: string;
29
+ agentRulesBytes?: number;
30
+ preferenceBytes?: number;
27
31
  text?: string;
28
32
  usage?: unknown;
29
33
  }
30
34
 
35
+ const formatByteSize = (bytes: number) => {
36
+ if (bytes >= 1024 * 1024) {
37
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
38
+ }
39
+ if (bytes >= 1024) {
40
+ return `${Math.round(bytes / 1024)} KiB`;
41
+ }
42
+ return `${bytes} B`;
43
+ };
44
+
31
45
  const manualCallRenderer =
32
46
  (runtime: CommandRuntime): EntryRenderer<ManualCallEntryDetails> =>
33
47
  (entry, { expanded }, theme) => {
@@ -70,12 +84,45 @@ const manualResultRenderer: MessageRenderer<ManualResultDetails> = (
70
84
  box.addChild(new Text(theme.fg("dim", ` Usage: ${usage}`), 0, 0));
71
85
  }
72
86
  }
87
+ const attachments = [
88
+ details?.agentRulesBytes
89
+ ? `AGENTS.md rules attached · ${formatByteSize(details.agentRulesBytes)}`
90
+ : undefined,
91
+ details?.preferenceBytes
92
+ ? `Project preferences attached · ${formatByteSize(details.preferenceBytes)}`
93
+ : undefined,
94
+ ].filter((value): value is string => value !== undefined);
95
+ if (attachments.length) {
96
+ box.addChild(
97
+ new Text(theme.fg("dim", ` ${attachments.join(" · ")}`), 0, 0)
98
+ );
99
+ }
73
100
  box.addChild(
74
101
  new Markdown(adviceForDisplay(advice, expanded), 0, 0, getMarkdownTheme())
75
102
  );
76
103
  return box;
77
104
  };
78
105
 
106
+ const outcomeStatsRenderer: MessageRenderer<AdvisorStatsDetails> = (
107
+ message,
108
+ _options,
109
+ theme
110
+ ) => {
111
+ const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
112
+ box.addChild(
113
+ new Text(theme.fg("accent", theme.bold("◆ ADVISOR · OUTCOME STATS")), 0, 0)
114
+ );
115
+ box.addChild(
116
+ new Markdown(
117
+ formatAdvisorStats(message.details ?? { malformedLines: 0 }),
118
+ 0,
119
+ 0,
120
+ getMarkdownTheme()
121
+ )
122
+ );
123
+ return box;
124
+ };
125
+
79
126
  export const registerCommandRenderers = (runtime: CommandRuntime) => {
80
127
  runtime.pi.registerEntryRenderer?.(
81
128
  "advisor-manual-call",
@@ -85,4 +132,8 @@ export const registerCommandRenderers = (runtime: CommandRuntime) => {
85
132
  "advisor-manual-result",
86
133
  manualResultRenderer
87
134
  );
135
+ runtime.pi.registerMessageRenderer?.(
136
+ "advisor-stats-result",
137
+ outcomeStatsRenderer
138
+ );
88
139
  };
@@ -77,7 +77,15 @@ class CommandRuntime implements CommandRuntimeContract {
77
77
  undefined,
78
78
  undefined,
79
79
  undefined,
80
- onScout
80
+ onScout,
81
+ undefined,
82
+ undefined,
83
+ (adviceId, payload, replacesAdviceId) =>
84
+ this.advisorSessionState.captureFollowUp(
85
+ adviceId,
86
+ payload,
87
+ replacesAdviceId
88
+ )
81
89
  ));
82
90
  }
83
91
 
@@ -3,6 +3,7 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
3
  import {
4
4
  getAdvisorSettings,
5
5
  getPersistedModelRefs,
6
+ setAdvisorAgentsMdContextRef,
6
7
  setAdvisorAutoLoopGateRef,
7
8
  setAdvisorBlockOnBlockedRef,
8
9
  setAdvisorCollapseResponsesRef,
@@ -10,6 +11,7 @@ import {
10
11
  setAdvisorCustomInvocationRef,
11
12
  setAdvisorDisableSameModelRef,
12
13
  setAdvisorEffortRef,
14
+ setAdvisorFallbackModelRef,
13
15
  setAdvisorFailureGateRef,
14
16
  setAdvisorFailureModeRef,
15
17
  setAdvisorGitContextMaxCharsRef,
@@ -68,6 +70,7 @@ const applySessionSettings = (settings: AdvisorSettings) => {
68
70
  );
69
71
  setContextMaxCharsRef(settings.contextMaxChars);
70
72
  setAdvisorPlanGateRef(settings.planGate);
73
+ setAdvisorFallbackModelRef(settings.fallbackModel);
71
74
  setAdvisorFailureGateRef(settings.failureGate);
72
75
  setAdvisorCompletionGateRef(settings.completionGate);
73
76
  setAdvisorDisableSameModelRef(settings.disableSameModel ?? true);
@@ -126,6 +129,7 @@ const applyDisclosureSettings = (settings: AdvisorSettings) => {
126
129
  setAdvisorGitContextRef(settings.gitContext ?? "summary");
127
130
  setAdvisorGitContextMaxCharsRef(settings.gitContextMaxChars ?? 20_000);
128
131
  setAdvisorToolPoliciesRef(settings.toolPolicies ?? {});
132
+ setAdvisorAgentsMdContextRef(settings.agentsMdContext ?? true);
129
133
  setAdvisorTrackedFileContentRef(settings.trackedFileContent ?? false);
130
134
  setAdvisorUntrackedContentRef(settings.untrackedContent ?? false);
131
135
  setAdvisorOutcomeLoggingRef(settings.outcomeLogging ?? false);
@@ -19,10 +19,13 @@ export type ManualConsult = (
19
19
  gitContext?: GitContextLevel
20
20
  ) => Promise<{
21
21
  adviceId?: string;
22
- markdown: string;
23
- thinkingText: string;
22
+ agentRulesBytes?: number;
24
23
  draftBytes?: number;
24
+ followUp?: boolean;
25
+ markdown: string;
26
+ model?: string;
25
27
  preferenceBytes?: number;
28
+ thinkingText: string;
26
29
  usage?: unknown;
27
30
  }>;
28
31
 
@@ -1,4 +1,5 @@
1
1
  import {
2
+ setAdvisorAgentsMdContextRef,
2
3
  setAdvisorAutoLoopGateRef,
3
4
  setAdvisorBlockOnBlockedRef,
4
5
  setAdvisorCollapseResponsesRef,
@@ -29,6 +30,7 @@ import {
29
30
  setAdvisorPlanGateRef,
30
31
  setAdvisorRedactSecretsRef,
31
32
  setAdvisorRef,
33
+ setAdvisorFallbackModelRef,
32
34
  setAdvisorScoutEnabledRef,
33
35
  setAdvisorScoutTimeoutMsRef,
34
36
  setAdvisorSessionSummaryRef,
@@ -68,6 +70,8 @@ import {
68
70
  export const resetDefaults = () => {
69
71
  setExecutorRef("");
70
72
  setAdvisorRef("");
73
+ setAdvisorFallbackModelRef(undefined);
74
+ setAdvisorAgentsMdContextRef(true);
71
75
  setPersistedModelRefs(undefined, undefined);
72
76
  setExecutorEffortRef(undefined);
73
77
  setAdvisorEffortRef(undefined);
@@ -137,8 +141,17 @@ const applyNonEmptyStringConfig = (
137
141
  export const applyConfig = (config: AdvisorConfig) => {
138
142
  applyNonEmptyStringConfig(config.executor, setExecutorRef);
139
143
  applyNonEmptyStringConfig(config.advisor, setAdvisorRef);
144
+ applyNonEmptyStringConfig(
145
+ config.advisorFallbackModel,
146
+ setAdvisorFallbackModelRef
147
+ );
140
148
  applyNonEmptyStringConfig(config.executorEffort, setExecutorEffortRef);
141
149
  applyNonEmptyStringConfig(config.advisorEffort, setAdvisorEffortRef);
150
+ applyOptionalConfig(
151
+ config,
152
+ "advisorAgentsMdContext",
153
+ setAdvisorAgentsMdContextRef
154
+ );
142
155
  applyOptionalConfig(config, "contextMaxChars", setContextMaxCharsRef);
143
156
  applyOptionalConfig(config, "advisorPlanGate", setAdvisorPlanGateRef);
144
157
  applyOptionalConfig(config, "advisorFailureGate", setAdvisorFailureGateRef);
@@ -2,6 +2,7 @@ import { isString } from "../content-utils.ts";
2
2
  import type { JsonValue } from "../content-utils.ts";
3
3
  import { GIT_CONTEXT_LEVELS, isValidGitContextLevel } from "../git.ts";
4
4
  import {
5
+ advisorAgentsMdContextRef,
5
6
  advisorAutoLoopGateRef,
6
7
  advisorBlockOnBlockedRef,
7
8
  advisorCollapseResponsesRef,
@@ -9,6 +10,7 @@ import {
9
10
  advisorCustomInvocationRef,
10
11
  advisorDisableSameModelRef,
11
12
  advisorEffortRef,
13
+ advisorFallbackModelRef,
12
14
  advisorFailureGateRef,
13
15
  advisorFailureModeRef,
14
16
  advisorGitContextMaxCharsRef,
@@ -171,6 +173,12 @@ export const CONFIG_SCHEMA = {
171
173
  persisted: true,
172
174
  type: "string",
173
175
  },
176
+ advisorAgentsMdContext: {
177
+ accepted: "true or false",
178
+ current: () => advisorAgentsMdContextRef,
179
+ persisted: true,
180
+ type: "boolean",
181
+ },
174
182
  advisorAutoLoopGate: {
175
183
  accepted: "true or false",
176
184
  current: () => advisorAutoLoopGateRef,
@@ -219,6 +227,12 @@ export const CONFIG_SCHEMA = {
219
227
  persisted: true,
220
228
  type: "boolean",
221
229
  },
230
+ advisorFallbackModel: {
231
+ accepted: "a provider/model string",
232
+ current: () => configuredModelRef(advisorFallbackModelRef),
233
+ persisted: true,
234
+ type: "string",
235
+ },
222
236
  advisorGitContext: {
223
237
  accepted: GIT_CONTEXT_LEVELS.join(", "),
224
238
  current: () => advisorGitContextRef,
@@ -25,6 +25,8 @@ import type {
25
25
  // An empty ref means no model has been selected yet.
26
26
  export let executorRef = "";
27
27
  export let advisorRef = "";
28
+ export let advisorFallbackModelRef: string | undefined;
29
+ export let advisorAgentsMdContextRef = true;
28
30
  let persistedExecutorRef: string | undefined;
29
31
  let persistedAdvisorRef: string | undefined;
30
32
  export let executorEffortRef: string | undefined;
@@ -80,6 +82,12 @@ export const setExecutorRef = (ref: string) => {
80
82
  export const setAdvisorRef = (ref: string) => {
81
83
  advisorRef = ref;
82
84
  };
85
+ export const setAdvisorFallbackModelRef = (ref: string | undefined) => {
86
+ advisorFallbackModelRef = ref?.trim() || undefined;
87
+ };
88
+ export const setAdvisorAgentsMdContextRef = (enabled: boolean) => {
89
+ advisorAgentsMdContextRef = enabled;
90
+ };
83
91
  /** Returns model refs explicitly persisted in the global Advisor config. */
84
92
  export const getPersistedModelRefs = () => ({
85
93
  advisor: persistedAdvisorRef,
@@ -234,6 +242,7 @@ export const setShowUsageFooterRef = (enabled: boolean) => {
234
242
  * imported mutable bindings, which can be snapshotted by extension loaders.
235
243
  */
236
244
  export const getAdvisorSettings = () => ({
245
+ agentsMdContext: advisorAgentsMdContextRef,
237
246
  alwaysOn: alwaysOnRef,
238
247
  autoLoopGate: advisorAutoLoopGateRef,
239
248
  blockOnBlocked: advisorBlockOnBlockedRef,
@@ -245,6 +254,7 @@ export const getAdvisorSettings = () => ({
245
254
  effort: advisorEffortRef,
246
255
  failureGate: advisorFailureGateRef,
247
256
  failureMode: advisorFailureModeRef,
257
+ fallbackModel: advisorFallbackModelRef,
248
258
  gitContext: advisorGitContextRef,
249
259
  gitContextMaxChars: advisorGitContextMaxCharsRef,
250
260
  herdrIntegration: advisorHerdrIntegrationRef,
@@ -60,6 +60,8 @@ export const GATE_FAILURE_MODES: GateFailureMode[] = [
60
60
 
61
61
  export interface AdvisorConfig {
62
62
  advisor?: string;
63
+ advisorFallbackModel?: string;
64
+ advisorAgentsMdContext?: boolean;
63
65
  advisorAutoLoopGate?: boolean;
64
66
  advisorBlockOnBlocked?: boolean;
65
67
  advisorCollapseResponses?: boolean;
package/src/config.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  // biome-ignore lint/performance/noBarrelFile: this facade intentionally preserves the public config module contract.
2
2
  export { parseArgs } from "./config/args.ts";
3
3
  export {
4
+ advisorAgentsMdContextRef,
4
5
  advisorAutoLoopGateRef,
5
6
  advisorBlockOnBlockedRef,
6
7
  advisorCollapseResponsesRef,
7
8
  advisorCompletionGateRef,
8
9
  advisorCustomInvocationRef,
9
10
  advisorEffortRef,
11
+ advisorFallbackModelRef,
10
12
  advisorFailureGateRef,
11
13
  advisorFailureModeRef,
12
14
  advisorGitContextMaxCharsRef,
@@ -34,12 +36,14 @@ export {
34
36
  getAdvisorSettings,
35
37
  getPersistedModelRefs,
36
38
  isSimpleMode,
39
+ setAdvisorAgentsMdContextRef,
37
40
  setAdvisorAutoLoopGateRef,
38
41
  setAdvisorBlockOnBlockedRef,
39
42
  setAdvisorCollapseResponsesRef,
40
43
  setAdvisorCompletionGateRef,
41
44
  setAdvisorCustomInvocationRef,
42
45
  setAdvisorEffortRef,
46
+ setAdvisorFallbackModelRef,
43
47
  setAdvisorFailureGateRef,
44
48
  setAdvisorFailureModeRef,
45
49
  setAdvisorGitContextMaxCharsRef,