pi-advisor-flow 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/commands.ts CHANGED
@@ -2,8 +2,9 @@ import {
2
2
  type ExtensionAPI,
3
3
  type ExtensionContext,
4
4
  getMarkdownTheme,
5
+ type Theme,
5
6
  } from "@earendil-works/pi-coding-agent";
6
- import { Box, Markdown, Text } from "@earendil-works/pi-tui";
7
+ import { Box, type Component, Markdown, Text } from "@earendil-works/pi-tui";
7
8
  import {
8
9
  advisorEffortRef,
9
10
  advisorRef,
@@ -46,9 +47,12 @@ import {
46
47
  setContextMaxCharsRef,
47
48
  setExecutorEffortRef,
48
49
  setExecutorRef,
50
+ setShowUsageDetailsRef,
51
+ setShowUsageFooterRef,
49
52
  setSimpleModeRef,
50
53
  splitRef,
51
54
  } from "./config.js";
55
+ import type { GitContextLevel } from "./git.js";
52
56
  import { herdrAdvisorActivity, notifyHerdrAdvisorFailure } from "./herdr.js";
53
57
  import type { ScoutLifecycleEvent } from "./scout.js";
54
58
  import type { AdvisorSessionState } from "./session-state.js";
@@ -60,13 +64,18 @@ import {
60
64
  hasSoundVerdict,
61
65
  renderAdvisorCallBox,
62
66
  renderAdvisorResponseHeader,
67
+ renderScoutDetails,
63
68
  resolveAdvisorRequest,
64
69
  ScoutStatusManager,
70
+ type ScoutToolDetails,
71
+ SPINNER_FRAMES,
65
72
  } from "./tools.js";
66
73
  import {
67
74
  type AdvisorSettings,
68
75
  AdvisorSettingsSelector,
69
76
  type ContextPreset,
77
+ ManualAdvisorDialog,
78
+ type ManualAdvisorRequest,
70
79
  SearchableModelSelector,
71
80
  } from "./ui.js";
72
81
  import {
@@ -143,7 +152,8 @@ type ManualConsult = (
143
152
  question?: string,
144
153
  signal?: AbortSignal,
145
154
  onChunk?: (thinking: string, text: string) => void,
146
- onScout?: (event: ScoutLifecycleEvent) => void
155
+ onScout?: (event: ScoutLifecycleEvent) => void,
156
+ gitContext?: GitContextLevel
147
157
  ) => Promise<{
148
158
  markdown: string;
149
159
  thinkingText: string;
@@ -163,11 +173,157 @@ const notify = (
163
173
  }
164
174
  };
165
175
 
176
+ type ManualAdvisorProgressPhase =
177
+ | "preparing"
178
+ | "active"
179
+ | "complete"
180
+ | "cancelled"
181
+ | "error";
182
+ interface ManualAdvisorProgressState {
183
+ phase: ManualAdvisorProgressPhase;
184
+ scout?: ScoutToolDetails;
185
+ text?: string;
186
+ thinking?: string;
187
+ }
188
+
189
+ class ManualAdvisorProgressComponent implements Component {
190
+ private readonly question: string | undefined;
191
+ private readonly state: ManualAdvisorProgressState;
192
+ private readonly expanded: boolean;
193
+ private readonly theme: Theme;
194
+
195
+ constructor(
196
+ question: string | undefined,
197
+ state: ManualAdvisorProgressState,
198
+ expanded: boolean,
199
+ theme: Theme
200
+ ) {
201
+ this.question = question;
202
+ this.state = state;
203
+ this.expanded = expanded;
204
+ this.theme = theme;
205
+ }
206
+
207
+ render(width: number): string[] {
208
+ const box = renderAdvisorCallBox(this.question, this.theme);
209
+ if (this.state.phase === "complete" || this.state.phase === "cancelled") {
210
+ return box.render(width);
211
+ }
212
+
213
+ const { scout } = this.state;
214
+ const scoutActive =
215
+ scout?.status === "calling" || scout?.status === "streaming";
216
+ if (scoutActive) {
217
+ renderScoutDetails(box, scout, this.expanded, this.theme);
218
+ return box.render(width);
219
+ }
220
+ if (scout?.status === "cancelled") {
221
+ return box.render(width);
222
+ }
223
+ if (this.state.phase === "error") {
224
+ box.addChild(
225
+ new Text(
226
+ this.theme.fg("error", this.theme.bold("◆ ADVISOR · FAILED")),
227
+ 0,
228
+ 0
229
+ )
230
+ );
231
+ return box.render(width);
232
+ }
233
+
234
+ const frame =
235
+ SPINNER_FRAMES[Math.floor(Date.now() / 80) % SPINNER_FRAMES.length];
236
+ let status = "Working…";
237
+ if (this.state.phase === "preparing") {
238
+ status = "Preparing…";
239
+ } else if (this.state.text?.trim()) {
240
+ status = "Responding…";
241
+ }
242
+ box.addChild(
243
+ new Text(
244
+ `${this.theme.fg("warning", this.theme.bold(`◆ ADVISOR ${frame}`))} ${this.theme.fg("dim", `· ${status}`)}`,
245
+ 0,
246
+ 0
247
+ )
248
+ );
249
+ if (this.state.thinking) {
250
+ box.addChild(
251
+ new Text(
252
+ this.theme.fg(
253
+ "thinkingText",
254
+ ` 💭 ${this.state.thinking.replace(/\n/g, " ").slice(-200)}`
255
+ ),
256
+ 0,
257
+ 0
258
+ )
259
+ );
260
+ }
261
+ if (this.state.text) {
262
+ box.addChild(
263
+ new Markdown(
264
+ adviceForDisplay(this.state.text, this.expanded),
265
+ 0,
266
+ 0,
267
+ getMarkdownTheme()
268
+ )
269
+ );
270
+ }
271
+ return box.render(width);
272
+ }
273
+
274
+ invalidate(): void {
275
+ // The live progress state is read during each render.
276
+ }
277
+ }
278
+
166
279
  const findConfiguredModel = (ctx: ExtensionContext, ref: string) => {
167
280
  const [provider, modelId] = splitRef(ref);
168
281
  return ctx.modelRegistry.find(provider, modelId);
169
282
  };
170
283
 
284
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one settings form maps every persisted control.
285
+ const applyAdvisorSettings = (settings: AdvisorSettings) => {
286
+ setAdvisorEffortRef(
287
+ settings.effort === "Default (Model Default)" ? undefined : settings.effort
288
+ );
289
+ setContextMaxCharsRef(settings.contextMaxChars);
290
+ setAdvisorPlanGateRef(settings.planGate);
291
+ setAdvisorFailureGateRef(settings.failureGate);
292
+ setAdvisorCompletionGateRef(settings.completionGate);
293
+ setAdvisorCollapseResponsesRef(settings.collapseResponses);
294
+ setAdvisorCustomInvocationRef(settings.customRule);
295
+ setAdvisorBlockOnBlockedRef(settings.blockOnBlocked ?? true);
296
+ setAdvisorAutoLoopGateRef(settings.autoLoopGate ?? true);
297
+ setAdvisorLoopThresholdRef(settings.loopThreshold ?? 3);
298
+ setAdvisorMaxCallsPerSessionRef(settings.maxCallsPerSession);
299
+ setAdvisorSessionSummaryRef(settings.sessionSummary ?? false);
300
+ setAdvisorScoutEnabledRef(settings.scoutEnabled ?? false);
301
+ setShowUsageDetailsRef(settings.showUsageDetails ?? true);
302
+ setShowUsageFooterRef(settings.showUsageFooter ?? false);
303
+ setSimpleModeRef(settings.simpleMode ?? false);
304
+ setAlwaysOnRef(settings.alwaysOn ?? false);
305
+ setAdvisorFailureModeRef(settings.failureMode ?? "block-session");
306
+ setAdvisorHerdrIntegrationRef(settings.herdrIntegration ?? true);
307
+ setAdvisorToolResultMaxLinesRef(settings.toolResultMaxLines ?? 2000);
308
+ setAdvisorToolResultMaxBytesRef(settings.toolResultMaxBytes ?? 50 * 1024);
309
+ setAdvisorRedactSecretsRef(settings.redactSecrets ?? false);
310
+ setAdvisorGitContextRef(settings.gitContext ?? "summary");
311
+ setAdvisorGitContextMaxCharsRef(settings.gitContextMaxChars ?? 20_000);
312
+ setAdvisorToolPoliciesRef(settings.toolPolicies ?? {});
313
+ setAdvisorTrackedFileContentRef(settings.trackedFileContent ?? false);
314
+ setAdvisorUntrackedContentRef(settings.untrackedContent ?? false);
315
+ setAdvisorOutcomeLoggingRef(settings.outcomeLogging ?? false);
316
+ };
317
+
318
+ const saveAdvisorSettings = (
319
+ ctx: ExtensionContext,
320
+ settings: AdvisorSettings
321
+ ) => {
322
+ applyAdvisorSettings(settings);
323
+ saveConfig(ctx);
324
+ saveGlobalOutcomeLogging(settings.outcomeLogging ?? false);
325
+ };
326
+
171
327
  export const registerCommands = (
172
328
  pi: ExtensionAPI,
173
329
  dependencies: {
@@ -178,18 +334,19 @@ export const registerCommands = (
178
334
  ) => {
179
335
  const advisorSessionState =
180
336
  dependencies.sessionState ?? defaultAdvisorSessionState;
181
- const scoutStatus = dependencies.statusManager ?? new ScoutStatusManager();
337
+ const scoutStatus =
338
+ dependencies.statusManager ?? new ScoutStatusManager(false);
182
339
  const flowEnabled = () => pi.getActiveTools().includes("ask_advisor");
183
340
  const requestAdvisor =
184
341
  dependencies.consult ??
185
- ((ctx, question, signal, onChunk, onScout) =>
342
+ ((ctx, question, signal, onChunk, onScout, gitContext) =>
186
343
  consultAdvisor(
187
344
  ctx,
188
345
  question,
189
346
  signal,
190
347
  onChunk,
191
348
  "manual",
192
- undefined,
349
+ gitContext,
193
350
  undefined,
194
351
  undefined,
195
352
  undefined,
@@ -197,23 +354,34 @@ export const registerCommands = (
197
354
  undefined
198
355
  ));
199
356
  const manualConsultations = new Map<AbortController, symbol>();
357
+ const manualProgressTimers = new Map<
358
+ AbortController,
359
+ ReturnType<typeof setInterval>
360
+ >();
361
+ const manualProgress = new Map<string, ManualAdvisorProgressState>();
362
+ let manualProgressSequence = 0;
200
363
  const updateAdvisorUsageStatus = (ctx: ExtensionContext) => {
201
364
  if (ctx.hasUI) {
202
- ctx.ui.setStatus("advisor-usage", advisorSessionState.usageStatus());
365
+ ctx.ui.setStatus(
366
+ "advisor-usage",
367
+ getAdvisorSettings().showUsageFooter
368
+ ? advisorSessionState.usageStatus()
369
+ : undefined
370
+ );
203
371
  }
204
372
  };
205
- const setManualStatus = (
206
- ctx: ExtensionContext,
207
- controller: AbortController,
208
- token: symbol,
209
- status: string | undefined
210
- ) => {
211
- if (
212
- ctx.hasUI &&
213
- manualConsultations.get(controller) === token &&
214
- !controller.signal.aborted
215
- ) {
216
- ctx.ui.setStatus("advisor-manual", status);
373
+ const reportManualBudgetExhausted = (ctx: ExtensionContext) => {
374
+ const message = "Advisor call budget exhausted for this session.";
375
+ notify(ctx, message, "warning");
376
+ notifyHerdrAdvisorFailure("Advisor budget exhausted", message);
377
+ };
378
+
379
+ // A status update with an undefined value is an invisible render pulse. The
380
+ // transcript owns manual progress now, so no consultation text is kept in the
381
+ // footer while this pulse still drives the live spinner.
382
+ const requestManualRender = (ctx: ExtensionContext) => {
383
+ if (ctx.hasUI) {
384
+ ctx.ui.setStatus("advisor-manual", undefined);
217
385
  }
218
386
  };
219
387
 
@@ -221,10 +389,26 @@ export const registerCommands = (
221
389
  ctx: ExtensionContext,
222
390
  question: string | undefined,
223
391
  controller: AbortController,
224
- scoutStatusToken: symbol
392
+ scoutStatusToken: symbol,
393
+ progress: ManualAdvisorProgressState,
394
+ gitContext?: GitContextLevel
225
395
  ) => {
226
396
  herdrAdvisorActivity.start();
227
- setManualStatus(ctx, controller, scoutStatusToken, "Advisor preparing…");
397
+ progress.phase = "preparing";
398
+ requestManualRender(ctx);
399
+ if (ctx.hasUI) {
400
+ const timer = setInterval(() => {
401
+ if (
402
+ controller.signal.aborted ||
403
+ manualConsultations.get(controller) !== scoutStatusToken
404
+ ) {
405
+ clearInterval(timer);
406
+ return;
407
+ }
408
+ requestManualRender(ctx);
409
+ }, 80);
410
+ manualProgressTimers.set(controller, timer);
411
+ }
228
412
  let scoutDetails: Parameters<typeof appendScoutLifecycleEntry>[2];
229
413
  return requestAdvisor(
230
414
  ctx,
@@ -234,41 +418,27 @@ export const registerCommands = (
234
418
  if (controller.signal.aborted) {
235
419
  return;
236
420
  }
237
- let status = "Advisor working…";
238
- if (thinking.trim()) {
239
- status = "Advisor thinking…";
240
- }
241
- if (text.trim()) {
242
- status = "Advisor responding…";
243
- }
244
- setManualStatus(ctx, controller, scoutStatusToken, status);
421
+ progress.phase = "active";
422
+ progress.thinking = thinking;
423
+ progress.text = text;
424
+ requestManualRender(ctx);
245
425
  },
246
426
  (event) => {
247
427
  if (!controller.signal.aborted) {
248
428
  scoutStatus.update(ctx, scoutStatusToken, event);
249
- if (event.type === "call" || event.type === "chunk") {
250
- setManualStatus(
251
- ctx,
252
- controller,
253
- scoutStatusToken,
254
- "Advisor Scout curating…"
255
- );
256
- } else if (event.type === "success" || event.type === "fallback") {
257
- setManualStatus(
258
- ctx,
259
- controller,
260
- scoutStatusToken,
261
- "Advisor working…"
262
- );
263
- }
264
429
  scoutDetails = appendScoutLifecycleEntry(pi, event, scoutDetails);
430
+ progress.scout = scoutDetails;
431
+ progress.phase = "active";
432
+ requestManualRender(ctx);
265
433
  }
266
- }
434
+ },
435
+ gitContext
267
436
  )
268
437
  .then(({ markdown, usage }) => {
269
438
  if (controller.signal.aborted) {
270
439
  return;
271
440
  }
441
+ progress.phase = "complete";
272
442
  advisorSessionState.recordInvocation({
273
443
  cost: advisorUsageCost(usage),
274
444
  executionEffect: "continued",
@@ -303,6 +473,7 @@ export const registerCommands = (
303
473
  if (controller.signal.aborted) {
304
474
  return;
305
475
  }
476
+ progress.phase = "error";
306
477
  const message = error instanceof Error ? error.message : String(error);
307
478
  advisorSessionState.recordInvocation({
308
479
  executionEffect: "continued",
@@ -328,12 +499,15 @@ export const registerCommands = (
328
499
  notifyHerdrAdvisorFailure("Advisor consultation failed", message);
329
500
  })
330
501
  .finally(() => {
331
- if (
332
- ctx.hasUI &&
333
- manualConsultations.get(controller) === scoutStatusToken
334
- ) {
335
- ctx.ui.setStatus("advisor-manual", undefined);
502
+ if (controller.signal.aborted) {
503
+ progress.phase = "cancelled";
336
504
  }
505
+ const timer = manualProgressTimers.get(controller);
506
+ if (timer) {
507
+ clearInterval(timer);
508
+ manualProgressTimers.delete(controller);
509
+ }
510
+ requestManualRender(ctx);
337
511
  scoutStatus.release(ctx, scoutStatusToken);
338
512
  manualConsultations.delete(controller);
339
513
  herdrAdvisorActivity.finish();
@@ -432,9 +606,20 @@ export const registerCommands = (
432
606
 
433
607
  pi.registerEntryRenderer?.(
434
608
  "advisor-manual-call",
435
- (entry, _options, theme) => {
436
- const { question } = (entry.data ?? {}) as { question?: string };
437
- return renderAdvisorCallBox(question, theme);
609
+ (entry, { expanded }, theme) => {
610
+ const { progressId, question } = (entry.data ?? {}) as {
611
+ progressId?: string;
612
+ question?: string;
613
+ };
614
+ const progress = progressId ? manualProgress.get(progressId) : undefined;
615
+ return progress
616
+ ? new ManualAdvisorProgressComponent(
617
+ question,
618
+ progress,
619
+ Boolean(expanded),
620
+ theme
621
+ )
622
+ : renderAdvisorCallBox(question, theme);
438
623
  }
439
624
  );
440
625
 
@@ -461,9 +646,11 @@ export const registerCommands = (
461
646
  if (details?.advisor) {
462
647
  box.addChild(new Text(theme.fg("dim", ` ${details.advisor}`), 0, 0));
463
648
  }
464
- const usage = formatAdvisorUsage(details?.usage);
465
- if (usage) {
466
- box.addChild(new Text(theme.fg("dim", ` Usage: ${usage}`), 0, 0));
649
+ if (getAdvisorSettings().showUsageDetails) {
650
+ const usage = formatAdvisorUsage(details?.usage);
651
+ if (usage) {
652
+ box.addChild(new Text(theme.fg("dim", ` Usage: ${usage}`), 0, 0));
653
+ }
467
654
  }
468
655
  box.addChild(
469
656
  new Markdown(
@@ -507,24 +694,30 @@ export const registerCommands = (
507
694
 
508
695
  pi.on("session_shutdown", (_event, ctx) => {
509
696
  if (ctx.hasUI) {
510
- ctx.ui.setStatus("advisor-manual", undefined);
511
697
  ctx.ui.setStatus("advisor-usage", undefined);
512
698
  }
513
699
  for (const [controller, token] of manualConsultations) {
514
700
  controller.abort();
701
+ const timer = manualProgressTimers.get(controller);
702
+ if (timer) {
703
+ clearInterval(timer);
704
+ manualProgressTimers.delete(controller);
705
+ }
515
706
  scoutStatus.release(ctx, token);
516
707
  }
517
708
  scoutStatus.clear(ctx);
518
709
  manualConsultations.clear();
710
+ manualProgressTimers.clear();
711
+ manualProgress.clear();
519
712
  herdrAdvisorActivity.clear();
520
713
  });
521
714
 
522
715
  pi.registerCommand("advisor-manual", {
523
716
  description:
524
717
  "Consult the Advisor in parallel; accepts an optional focused question and fans its response out to the Executor",
525
- handler: (args, ctx) => {
718
+ handler: async (args, ctx) => {
526
719
  if (!loadCommandConfig(ctx)) {
527
- return Promise.resolve();
720
+ return;
528
721
  }
529
722
  if (
530
723
  !(
@@ -532,15 +725,57 @@ export const registerCommands = (
532
725
  advisorSessionState.canConsult(getAdvisorMaxCallsPerSession())
533
726
  )
534
727
  ) {
535
- const message = "Advisor call budget exhausted for this session.";
536
- notify(ctx, message, "warning");
537
- notifyHerdrAdvisorFailure("Advisor budget exhausted", message);
538
- return Promise.resolve();
728
+ reportManualBudgetExhausted(ctx);
729
+ return;
539
730
  }
731
+
732
+ let gitContext: GitContextLevel | undefined;
733
+ let question: string | undefined;
734
+ if (ctx.mode === "tui") {
735
+ const request = await ctx.ui.custom<ManualAdvisorRequest | undefined>(
736
+ (tui, theme, keybindings, done) =>
737
+ new ManualAdvisorDialog({
738
+ gitContext: getAdvisorSettings().gitContext,
739
+ initialMessage: args,
740
+ keybindings,
741
+ onCancel: () => done(undefined),
742
+ onSubmit: done,
743
+ theme,
744
+ tui,
745
+ }),
746
+ {
747
+ overlay: true,
748
+ overlayOptions: {
749
+ anchor: "center",
750
+ margin: 2,
751
+ maxHeight: "80%",
752
+ minWidth: 56,
753
+ width: 76,
754
+ },
755
+ }
756
+ );
757
+ if (!request) {
758
+ return;
759
+ }
760
+ if (
761
+ !(
762
+ isSimpleMode() ||
763
+ advisorSessionState.canConsult(getAdvisorMaxCallsPerSession())
764
+ )
765
+ ) {
766
+ reportManualBudgetExhausted(ctx);
767
+ return;
768
+ }
769
+ const { gitContext: selectedGitContext, message } = request;
770
+ gitContext = selectedGitContext;
771
+ question = resolveAdvisorRequest(message);
772
+ } else {
773
+ question = resolveAdvisorRequest(args);
774
+ }
775
+
540
776
  if (!isSimpleMode()) {
541
777
  advisorSessionState.consumeCall();
542
778
  }
543
- const question = resolveAdvisorRequest(args);
544
779
  // A single visible progress surface avoids competing consultations overwriting
545
780
  // each other's streamed state. A newer manual request replaces the previous one.
546
781
  for (const [pending, token] of manualConsultations) {
@@ -550,11 +785,21 @@ export const registerCommands = (
550
785
  manualConsultations.clear();
551
786
  const controller = new AbortController();
552
787
  const scoutStatusToken = Symbol("manual-scout");
788
+ manualProgressSequence += 1;
789
+ const progressId = `manual-${manualProgressSequence}`;
790
+ const progress: ManualAdvisorProgressState = { phase: "preparing" };
791
+ manualProgress.set(progressId, progress);
553
792
  scoutStatus.register(scoutStatusToken);
554
793
  manualConsultations.set(controller, scoutStatusToken);
555
- pi.appendEntry?.("advisor-manual-call", { question });
556
- startManualConsultation(ctx, question, controller, scoutStatusToken);
557
- return Promise.resolve();
794
+ pi.appendEntry?.("advisor-manual-call", { progressId, question });
795
+ startManualConsultation(
796
+ ctx,
797
+ question,
798
+ controller,
799
+ scoutStatusToken,
800
+ progress,
801
+ gitContext
802
+ );
558
803
  },
559
804
  });
560
805
 
@@ -639,68 +884,37 @@ export const registerCommands = (
639
884
  });
640
885
 
641
886
  pi.registerCommand("advisor-settings", {
642
- description: "Configure Advisor context and reasoning effort",
643
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one settings form maps every persisted control.
887
+ description: "Configure Advisor settings",
644
888
  handler: async (_args, ctx) => {
645
889
  if (!(loadCommandConfig(ctx) && ctx.hasUI)) {
646
890
  return;
647
891
  }
648
892
 
649
893
  const initial: AdvisorSettings = getAdvisorSettings();
650
- const settings = await ctx.ui.custom<AdvisorSettings | undefined>(
894
+ await ctx.ui.custom<void>(
651
895
  (tui, theme, _keybindings, done) =>
652
896
  new AdvisorSettingsSelector({
653
897
  effortLevels: EFFORT_LEVELS,
654
898
  initial,
655
- onCancel: () => done(undefined),
656
- onSave: done,
899
+ onCancel: () => done(),
900
+ onChange: (settings) => {
901
+ try {
902
+ saveAdvisorSettings(ctx, settings);
903
+ updateAdvisorUsageStatus(ctx);
904
+ } catch (error) {
905
+ const message =
906
+ error instanceof Error ? error.message : String(error);
907
+ ctx.ui.notify(
908
+ `Could not save Advisor settings: ${message}`,
909
+ "error"
910
+ );
911
+ }
912
+ },
657
913
  presets: CONTEXT_PRESETS,
658
914
  theme,
659
915
  tui,
660
916
  })
661
917
  );
662
- if (!settings) {
663
- return;
664
- }
665
-
666
- setAdvisorEffortRef(
667
- settings.effort === "Default (Model Default)"
668
- ? undefined
669
- : settings.effort
670
- );
671
- setContextMaxCharsRef(settings.contextMaxChars);
672
- setAdvisorPlanGateRef(settings.planGate);
673
- setAdvisorFailureGateRef(settings.failureGate);
674
- setAdvisorCompletionGateRef(settings.completionGate);
675
- setAdvisorCollapseResponsesRef(settings.collapseResponses);
676
- setAdvisorCustomInvocationRef(settings.customRule);
677
- setAdvisorBlockOnBlockedRef(settings.blockOnBlocked ?? true);
678
- setAdvisorAutoLoopGateRef(settings.autoLoopGate ?? true);
679
- setAdvisorLoopThresholdRef(settings.loopThreshold ?? 3);
680
- setAdvisorMaxCallsPerSessionRef(settings.maxCallsPerSession);
681
- setAdvisorSessionSummaryRef(settings.sessionSummary ?? false);
682
- setAdvisorScoutEnabledRef(settings.scoutEnabled ?? false);
683
- setSimpleModeRef(settings.simpleMode ?? false);
684
- setAlwaysOnRef(settings.alwaysOn ?? false);
685
- setAdvisorFailureModeRef(settings.failureMode ?? "block-session");
686
- setAdvisorHerdrIntegrationRef(settings.herdrIntegration ?? true);
687
- setAdvisorToolResultMaxLinesRef(settings.toolResultMaxLines ?? 2000);
688
- setAdvisorToolResultMaxBytesRef(settings.toolResultMaxBytes ?? 50 * 1024);
689
- setAdvisorRedactSecretsRef(settings.redactSecrets ?? false);
690
- setAdvisorGitContextRef(settings.gitContext ?? "summary");
691
- setAdvisorGitContextMaxCharsRef(settings.gitContextMaxChars ?? 20_000);
692
- setAdvisorToolPoliciesRef(settings.toolPolicies ?? {});
693
- setAdvisorTrackedFileContentRef(settings.trackedFileContent ?? false);
694
- setAdvisorUntrackedContentRef(settings.untrackedContent ?? false);
695
- setAdvisorOutcomeLoggingRef(settings.outcomeLogging ?? false);
696
- const path = saveConfig(ctx);
697
- const globalPath = saveGlobalOutcomeLogging(
698
- settings.outcomeLogging ?? false
699
- );
700
- ctx.ui.notify(
701
- `Saved Advisor settings to ${path}; outcome logging globally to ${globalPath}`,
702
- "info"
703
- );
704
918
  },
705
919
  });
706
920