@oh-my-pi/pi-coding-agent 16.2.1 → 16.2.2

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 (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cli.js +2648 -2662
  3. package/dist/types/advisor/runtime.d.ts +15 -1
  4. package/dist/types/config/model-roles.d.ts +1 -1
  5. package/dist/types/config/settings-schema.d.ts +32 -12
  6. package/dist/types/discovery/omp-extension-roots.d.ts +3 -3
  7. package/dist/types/edit/index.d.ts +18 -0
  8. package/dist/types/edit/streaming.d.ts +30 -0
  9. package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
  10. package/dist/types/extensibility/shared-events.d.ts +1 -0
  11. package/dist/types/mcp/oauth-discovery.d.ts +0 -11
  12. package/dist/types/modes/components/status-line/component.d.ts +0 -2
  13. package/dist/types/sdk.d.ts +1 -1
  14. package/dist/types/session/agent-session.d.ts +26 -2
  15. package/dist/types/session/messages.d.ts +6 -7
  16. package/dist/types/session/settings-stream-fn.d.ts +21 -0
  17. package/dist/types/session/turn-persistence.d.ts +88 -0
  18. package/package.json +12 -12
  19. package/src/advisor/__tests__/advisor.test.ts +196 -0
  20. package/src/advisor/runtime.ts +65 -2
  21. package/src/auto-thinking/classifier.ts +2 -2
  22. package/src/config/model-resolver.ts +5 -1
  23. package/src/config/model-roles.ts +3 -3
  24. package/src/config/settings-schema.ts +30 -8
  25. package/src/discovery/omp-extension-roots.ts +38 -13
  26. package/src/edit/index.ts +21 -0
  27. package/src/edit/streaming.ts +170 -0
  28. package/src/extensibility/custom-tools/types.ts +1 -0
  29. package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +18 -1
  30. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +59 -2
  31. package/src/extensibility/plugins/manager.ts +74 -4
  32. package/src/extensibility/shared-events.ts +1 -0
  33. package/src/internal-urls/docs-index.generated.txt +1 -1
  34. package/src/mcp/oauth-discovery.ts +5 -29
  35. package/src/mcp/transports/http.ts +3 -1
  36. package/src/mnemopi/backend.ts +2 -2
  37. package/src/modes/acp/acp-agent.ts +1 -1
  38. package/src/modes/components/assistant-message.ts +5 -5
  39. package/src/modes/components/status-line/component.ts +1 -9
  40. package/src/modes/components/status-line/segments.ts +1 -1
  41. package/src/modes/controllers/event-controller.ts +8 -11
  42. package/src/modes/interactive-mode.ts +0 -5
  43. package/src/modes/print-mode.ts +1 -1
  44. package/src/modes/utils/transcript-render-helpers.ts +2 -2
  45. package/src/modes/utils/ui-helpers.ts +5 -4
  46. package/src/sdk.ts +12 -26
  47. package/src/session/agent-session.ts +319 -219
  48. package/src/session/messages.ts +20 -12
  49. package/src/session/session-persistence.ts +1 -2
  50. package/src/session/settings-stream-fn.ts +49 -0
  51. package/src/session/turn-persistence.ts +142 -0
  52. package/src/session/unexpected-stop-classifier.ts +2 -2
  53. package/src/slash-commands/helpers/mcp.ts +2 -1
  54. package/src/tiny/models.ts +8 -6
  55. package/src/tiny/text.ts +14 -7
  56. package/src/tools/image-gen.ts +2 -1
  57. package/src/tools/tts.ts +2 -1
  58. package/src/utils/title-generator.ts +1 -1
  59. package/src/web/search/providers/tavily.ts +36 -19
@@ -4,6 +4,7 @@
4
4
  * Automatically detects OAuth requirements from MCP server responses
5
5
  * and extracts authentication endpoints.
6
6
  */
7
+ import * as AIError from "@oh-my-pi/pi-ai/error";
7
8
  import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
8
9
 
9
10
  export interface OAuthEndpoints {
@@ -23,8 +24,8 @@ export interface AuthDetectionResult {
23
24
  message?: string;
24
25
  }
25
26
 
26
- function parseMcpAuthServerUrl(errorMessage: string, serverUrl?: string): string | undefined {
27
- const match = errorMessage.match(/Mcp-Auth-Server:\s*([^;\]\s]+)/i);
27
+ export function extractMcpAuthServerUrl(error: Error, serverUrl?: string): string | undefined {
28
+ const match = error.message.match(/Mcp-Auth-Server:\s*([^;\]\s]+)/i);
28
29
  if (!match?.[1]) return undefined;
29
30
 
30
31
  try {
@@ -34,32 +35,6 @@ function parseMcpAuthServerUrl(errorMessage: string, serverUrl?: string): string
34
35
  }
35
36
  }
36
37
 
37
- export function extractMcpAuthServerUrl(error: Error, serverUrl?: string): string | undefined {
38
- return parseMcpAuthServerUrl(error.message, serverUrl);
39
- }
40
-
41
- /**
42
- * Detect if an error indicates authentication is required.
43
- * Checks for common auth error patterns.
44
- */
45
- export function detectAuthError(error: Error): boolean {
46
- const errorMsg = error.message.toLowerCase();
47
-
48
- // Check for HTTP auth status codes
49
- if (
50
- errorMsg.includes("401") ||
51
- errorMsg.includes("403") ||
52
- errorMsg.includes("unauthorized") ||
53
- errorMsg.includes("forbidden") ||
54
- errorMsg.includes("authentication required") ||
55
- errorMsg.includes("authentication failed")
56
- ) {
57
- return true;
58
- }
59
-
60
- return false;
61
- }
62
-
63
38
  /**
64
39
  * Extract OAuth endpoints from error response.
65
40
  * Looks for WWW-Authenticate header format or JSON error bodies.
@@ -200,7 +175,8 @@ export function extractOAuthEndpoints(error: Error): OAuthEndpoints | null {
200
175
  * Returns structured info about what auth is needed.
201
176
  */
202
177
  export function analyzeAuthError(error: Error, serverUrl?: string): AuthDetectionResult {
203
- if (!detectAuthError(error)) {
178
+ // No auth required unless the error carries an HTTP auth status / auth-failure phrasing.
179
+ if (!AIError.is(AIError.classify(error), AIError.Flag.AuthFailed)) {
204
180
  return { requiresAuth: false };
205
181
  }
206
182
 
@@ -4,6 +4,7 @@
4
4
  * Implements JSON-RPC 2.0 over HTTP POST with optional SSE streaming.
5
5
  * Based on MCP spec 2025-03-26.
6
6
  */
7
+ import * as AIError from "@oh-my-pi/pi-ai/error";
7
8
  import { logger, readSseJson, Snowflake } from "@oh-my-pi/pi-utils";
8
9
  import type {
9
10
  JsonRpcError,
@@ -186,7 +187,8 @@ export class HttpTransport implements MCPTransport {
186
187
  return await this.#executeRequest<T>(method, params, options);
187
188
  } catch (error) {
188
189
  // Retry once on auth failure if onAuthError is wired
189
- if (this.onAuthError && error instanceof Error && /^HTTP (401|403):/.test(error.message)) {
190
+ const status = error instanceof Error ? AIError.status(error) : undefined;
191
+ if (this.onAuthError && (status === 401 || status === 403)) {
190
192
  const newHeaders = await this.onAuthError();
191
193
  if (newHeaders) {
192
194
  // Persist refreshed headers so subsequent requests use them directly
@@ -511,10 +511,10 @@ async function resolveMnemopiProviderOptions(
511
511
  }
512
512
 
513
513
  try {
514
- const resolved = resolveRoleSelection(["smol"], settings, modelRegistry.getAvailable(), modelRegistry);
514
+ const resolved = resolveRoleSelection(["tiny", "smol"], settings, modelRegistry.getAvailable(), modelRegistry);
515
515
  const model = resolved?.model;
516
516
  if (!model) {
517
- logger.warn("Mnemopi: llmMode=smol but no smol model resolved; continuing without LLM.");
517
+ logger.warn("Mnemopi: llmMode=smol but no tiny/smol model resolved; continuing without LLM.");
518
518
  return base;
519
519
  }
520
520
  return {
@@ -2014,7 +2014,7 @@ export class AcpAgent implements Agent {
2014
2014
  }
2015
2015
  }
2016
2016
  }
2017
- if (notifications.length === 0 && message.errorMessage && !isSilentAbort(message.errorMessage)) {
2017
+ if (notifications.length === 0 && message.errorMessage && !isSilentAbort(message)) {
2018
2018
  notifications.push({
2019
2019
  sessionId,
2020
2020
  update: {
@@ -581,11 +581,11 @@ export class AssistantMessageComponent extends Container {
581
581
  if (content.type === "toolCall") return false;
582
582
  }
583
583
  if (this.#toolImagesByCallId.size > 0) return false;
584
- if (message.stopReason === "aborted" && shouldRenderAbortReason(message.errorMessage)) return false;
584
+ if (message.stopReason === "aborted" && shouldRenderAbortReason(message)) return false;
585
585
  if (message.stopReason === "error" && !this.#errorPinned) return false;
586
586
  if (
587
587
  message.errorMessage &&
588
- shouldRenderAbortReason(message.errorMessage) &&
588
+ shouldRenderAbortReason(message) &&
589
589
  message.stopReason !== "aborted" &&
590
590
  message.stopReason !== "error"
591
591
  )
@@ -779,8 +779,8 @@ export class AssistantMessageComponent extends Container {
779
779
  // But only if there are no tool calls (tool execution components will show the error)
780
780
  const hasToolCalls = message.content.some(c => c.type === "toolCall");
781
781
  if (!hasToolCalls) {
782
- if (message.stopReason === "aborted" && shouldRenderAbortReason(message.errorMessage)) {
783
- const abortMessage = resolveAbortLabel(message.errorMessage);
782
+ if (message.stopReason === "aborted" && shouldRenderAbortReason(message)) {
783
+ const abortMessage = resolveAbortLabel(message);
784
784
  if (hasVisibleContent) {
785
785
  this.#contentContainer.addChild(new Spacer(1));
786
786
  } else {
@@ -793,7 +793,7 @@ export class AssistantMessageComponent extends Container {
793
793
  }
794
794
  if (
795
795
  message.errorMessage &&
796
- shouldRenderAbortReason(message.errorMessage) &&
796
+ shouldRenderAbortReason(message) &&
797
797
  message.stopReason !== "aborted" &&
798
798
  message.stopReason !== "error"
799
799
  ) {
@@ -200,7 +200,6 @@ export class StatusLineComponent implements Component {
200
200
  #autoCompactEnabled: boolean = true;
201
201
  #hookStatuses: Map<string, string> = new Map();
202
202
  #subagentCount: number = 0;
203
- #subagentHubHint: string | undefined;
204
203
  #sessionStartTime: number = Date.now();
205
204
  #planModeStatus: { enabled: boolean; paused: boolean } | null = null;
206
205
  #loopModeStatus: { enabled: boolean } | null = null;
@@ -306,12 +305,6 @@ export class StatusLineComponent implements Component {
306
305
  this.#subagentCount = count;
307
306
  }
308
307
 
309
- /** Hub key label shown in the forced running-subagents badge. */
310
- setSubagentHubHint(hint: string | undefined): void {
311
- const trimmed = hint?.trim();
312
- this.#subagentHubHint = trimmed ? trimmed : undefined;
313
- }
314
-
315
308
  /** Active subagent count as currently displayed (collab state mirroring). */
316
309
  get subagentCount(): number {
317
310
  return this.#subagentCount;
@@ -925,8 +918,7 @@ export class StatusLineComponent implements Component {
925
918
  #subagentBadgeText(): string | undefined {
926
919
  if (this.#subagentCount === 0) return undefined;
927
920
  const noun = this.#subagentCount === 1 ? "agent" : "agents";
928
- const hubHint = this.#subagentHubHint ? ` — ${this.#subagentHubHint} hub` : " — Agent Hub";
929
- return theme.fg("statusLineSubagents", `${theme.icon.agents} ${this.#subagentCount} ${noun} running${hubHint}`);
921
+ return theme.fg("statusLineSubagents", `${theme.icon.agents} ${this.#subagentCount} ${noun}`);
930
922
  }
931
923
 
932
924
  #buildStatusLine(width: number): string {
@@ -21,7 +21,7 @@ function withIcon(icon: string, text: string): string {
21
21
  }
22
22
 
23
23
  function stripDisplayRoot(pwd: string): string {
24
- for (const root of ["/work", path.join(os.homedir(), "Projects")]) {
24
+ for (const root of [path.join(os.homedir(), "Projects"), "/work"]) {
25
25
  const relative = relativePathWithinRoot(root, pwd);
26
26
  if (relative) return relative;
27
27
  }
@@ -1,5 +1,6 @@
1
1
  import type { ImageContent } from "@oh-my-pi/pi-ai";
2
- import { THINKING_LOOP_ERROR_MARKER } from "@oh-my-pi/pi-ai/utils/thinking-loop";
2
+ import * as AIError from "@oh-my-pi/pi-ai/error";
3
+ import { getStreamingPartialJson } from "@oh-my-pi/pi-ai/utils/block-symbols";
3
4
  import { type Component, Loader, TERMINAL } from "@oh-my-pi/pi-tui";
4
5
  import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
5
6
  import { extractTextContent } from "../../commit/utils";
@@ -609,9 +610,9 @@ export class EventController {
609
610
  // reveal (write/edit/bash previews grow smoothly when a slow provider
610
611
  // delivers large batches); once it closes, the final args render
611
612
  // as-is — mirroring how assistant text snaps at message_end.
612
- const partialJson = "partialJson" in content ? content.partialJson : undefined;
613
613
  let renderArgs: Record<string, unknown>;
614
- if (typeof partialJson === "string") {
614
+ const partialJson = getStreamingPartialJson(content);
615
+ if (partialJson) {
615
616
  renderArgs = this.#toolArgsReveal.setTarget(
616
617
  content.id,
617
618
  partialJson,
@@ -697,7 +698,7 @@ export class EventController {
697
698
  this.#toolArgsReveal.flushAll();
698
699
  let errorMessage: string | undefined;
699
700
  const aborted = this.ctx.streamingMessage.stopReason === "aborted";
700
- const silentlyAborted = aborted && isSilentAbort(this.ctx.streamingMessage.errorMessage);
701
+ const silentlyAborted = aborted && isSilentAbort(this.ctx.streamingMessage);
701
702
  const ttsrSilenced = aborted && this.ctx.viewSession.isTtsrAbortPending;
702
703
  if (aborted && !silentlyAborted && !ttsrSilenced) {
703
704
  // Resolve the operator-facing label: a user-interrupt (Esc) abort
@@ -707,7 +708,7 @@ export class EventController {
707
708
  // AgentSession.#handleAgentEvent already stamped SILENT_ABORT_MARKER for
708
709
  // the plan-compact transition before this controller ran, so reaching
709
710
  // this branch implies the abort was NOT a silent internal transition.
710
- errorMessage = resolveAbortLabel(this.ctx.streamingMessage.errorMessage, this.ctx.viewSession.retryAttempt);
711
+ errorMessage = resolveAbortLabel(this.ctx.streamingMessage, this.ctx.viewSession.retryAttempt);
711
712
  this.ctx.streamingMessage.errorMessage = errorMessage;
712
713
  }
713
714
  if (silentlyAborted || ttsrSilenced) {
@@ -759,11 +760,7 @@ export class EventController {
759
760
  // above the editor so it survives transcript scroll. Cleared at the next
760
761
  // turn's agent_start. Suppress the transcript's inline `Error: …` line for
761
762
  // the same message while pinned so the error isn't rendered twice.
762
- if (
763
- event.message.stopReason === "error" &&
764
- event.message.errorMessage &&
765
- !isSilentAbort(event.message.errorMessage)
766
- ) {
763
+ if (event.message.stopReason === "error" && event.message.errorMessage && !isSilentAbort(event.message)) {
767
764
  this.#lastAssistantComponent?.setErrorPinned(true);
768
765
  this.#pinnedErrorComponent = this.#lastAssistantComponent;
769
766
  this.ctx.showPinnedError(event.message.errorMessage);
@@ -1157,7 +1154,7 @@ export class EventController {
1157
1154
  async #handleAutoRetryStart(event: Extract<AgentSessionEvent, { type: "auto_retry_start" }>): Promise<void> {
1158
1155
  this.#stopWorkingLoader();
1159
1156
  this.ctx.statusContainer.clear();
1160
- if (event.errorMessage?.includes(THINKING_LOOP_ERROR_MARKER)) {
1157
+ if (AIError.is(event.errorId, AIError.Flag.ThinkingLoop)) {
1161
1158
  // The retry path drops the failed assistant from runtime context. Do not
1162
1159
  // restore its inline Error row; just unpin the fixed-region banner so the
1163
1160
  // retry UI is the visible state.
@@ -858,11 +858,6 @@ export class InteractiveMode implements InteractiveModeContext {
858
858
  this.#observerRegistry.subscribeToEventBus(this.#eventBus);
859
859
  }
860
860
  this.#observerRegistry.setMainSession(this.sessionManager.getSessionFile() ?? undefined);
861
- this.statusLine.setSubagentHubHint(
862
- this.keybindings.getDisplayString("app.agents.hub") ||
863
- this.keybindings.getDisplayString("app.session.observe") ||
864
- undefined,
865
- );
866
861
  this.syncRunningSubagentBadge();
867
862
  this.#observerRegistry.onChange(() => {
868
863
  this.syncRunningSubagentBadge();
@@ -83,7 +83,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
83
83
  // Check for error/aborted — skip silent-abort (plan-mode compaction transition)
84
84
  if (
85
85
  (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") &&
86
- !isSilentAbort(assistantMsg.errorMessage)
86
+ !isSilentAbort(assistantMsg)
87
87
  ) {
88
88
  const errorLine = sanitizeText(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
89
89
  // Flush before this hard exit — it bypasses the awaited postmortem.quit()
@@ -146,11 +146,11 @@ export function resolveAssistantErrorMessage(
146
146
  message: AssistantAgentMessage,
147
147
  retryAttempt = 0,
148
148
  ): { hasErrorStop: boolean; errorMessage: string | null } {
149
- const isAbortedSilently = message.stopReason === "aborted" && isSilentAbort(message.errorMessage);
149
+ const isAbortedSilently = message.stopReason === "aborted" && isSilentAbort(message);
150
150
  const hasErrorStop = !isAbortedSilently && (message.stopReason === "aborted" || message.stopReason === "error");
151
151
  const errorMessage = hasErrorStop
152
152
  ? message.stopReason === "aborted"
153
- ? resolveAbortLabel(message.errorMessage, retryAttempt)
153
+ ? resolveAbortLabel(message, retryAttempt)
154
154
  : message.errorMessage || "Error"
155
155
  : null;
156
156
  return { hasErrorStop, errorMessage };
@@ -1,5 +1,6 @@
1
1
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
2
  import type { AssistantMessage, ImageContent, Message, Usage } from "@oh-my-pi/pi-ai";
3
+ import { getStreamingPartialJson } from "@oh-my-pi/pi-ai/utils/block-symbols";
3
4
  import { type Component, Spacer, Text, TruncatedText } from "@oh-my-pi/pi-tui";
4
5
  import type { AdvisorMessageDetails } from "../../advisor";
5
6
  import { COLLAB_PROMPT_MESSAGE_TYPE, type CollabPromptDetails } from "../../collab/protocol";
@@ -409,10 +410,10 @@ export class UiHelpers {
409
410
  readGroup?.seal();
410
411
  readGroup = null;
411
412
  const tool = this.ctx.viewSession.getToolByName(content.name);
412
- const renderArgs =
413
- "partialJson" in content
414
- ? { ...content.arguments, __partialJson: content.partialJson }
415
- : content.arguments;
413
+ const partialJson = getStreamingPartialJson(content);
414
+ const renderArgs = partialJson
415
+ ? { ...content.arguments, __partialJson: partialJson }
416
+ : content.arguments;
416
417
  const component = new ToolExecutionComponent(
417
418
  content.name,
418
419
  renderArgs,
package/src/sdk.ts CHANGED
@@ -8,14 +8,7 @@ import {
8
8
  filterProviderReplayMessages,
9
9
  type ThinkingLevel,
10
10
  } from "@oh-my-pi/pi-agent-core";
11
- import {
12
- type Context,
13
- type CredentialDisabledEvent,
14
- type Message,
15
- type Model,
16
- type SimpleStreamOptions,
17
- streamSimple,
18
- } from "@oh-my-pi/pi-ai";
11
+ import type { Context, CredentialDisabledEvent, Message, Model, SimpleStreamOptions } from "@oh-my-pi/pi-ai";
19
12
  import type { Dialect } from "@oh-my-pi/pi-ai/dialect";
20
13
  import {
21
14
  getOpenAICodexTransportDetails,
@@ -49,7 +42,7 @@ import {
49
42
  resolveModelRoleValue,
50
43
  } from "./config/model-resolver";
51
44
  import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
52
- import { Settings, type SkillsSettings, validateProviderMaxInFlightRequests } from "./config/settings";
45
+ import { Settings, type SkillsSettings } from "./config/settings";
53
46
  import { CursorExecHandlers } from "./cursor";
54
47
  import "./discovery";
55
48
  import { initializeWithSettings } from "./discovery";
@@ -125,6 +118,7 @@ import {
125
118
  import { clampProviderContextImages } from "./session/provider-image-budget";
126
119
  import { getRestorableSessionModels } from "./session/session-context";
127
120
  import { SessionManager } from "./session/session-manager";
121
+ import { createSettingsAwareStreamFn } from "./session/settings-stream-fn";
128
122
  import { SnapcompactInlineTransformer } from "./session/snapcompact-inline";
129
123
  import { createSnapcompactSavingsRecorder } from "./session/snapcompact-savings-journal";
130
124
  import { closeAllConnections } from "./ssh/connection-manager";
@@ -975,6 +969,7 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
975
969
  maxAttempts: event.maxAttempts,
976
970
  delayMs: event.delayMs,
977
971
  errorMessage: event.errorMessage,
972
+ errorId: event.errorId,
978
973
  },
979
974
  ctx,
980
975
  ),
@@ -2536,6 +2531,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2536
2531
  // One-shot launch-latency marker: fired the first time the loop dispatches
2537
2532
  // a chat request to the provider transport. See onFirstChatDispatch.
2538
2533
  let notifyFirstChatDispatch = options.onFirstChatDispatch;
2534
+ // Shared, settings-aware stream wrapper used by both the main agent and
2535
+ // the advisor (via AgentSessionConfig.streamFn). Keeps OpenRouter
2536
+ // sticky-routing variants, antigravity endpoint routing, in-flight caps,
2537
+ // and the loop guard consistent across every agent the session drives.
2538
+ const settingsAwareStreamFn = createSettingsAwareStreamFn(settings);
2539
2539
  agent = new Agent({
2540
2540
  initialState: {
2541
2541
  systemPrompt,
@@ -2586,23 +2586,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2586
2586
  });
2587
2587
  }
2588
2588
  }
2589
- const openrouterRoutingPreset = settings.get("providers.openrouterVariant");
2590
- const openrouterVariant =
2591
- openrouterRoutingPreset && openrouterRoutingPreset !== "default" ? openrouterRoutingPreset : undefined;
2592
- const antigravityEndpointMode = settings.get("providers.antigravityEndpoint");
2593
- return streamSimple(streamModel, context, {
2594
- ...streamOptions,
2595
- openrouterVariant: streamOptions?.openrouterVariant ?? openrouterVariant,
2596
- antigravityEndpointMode: streamOptions?.antigravityEndpointMode ?? antigravityEndpointMode,
2597
- maxInFlightRequests: validateProviderMaxInFlightRequests(
2598
- streamOptions?.maxInFlightRequests ?? settings.get("providers.maxInFlightRequests"),
2599
- ),
2600
- loopGuard: {
2601
- enabled: settings.get("model.loopGuard.enabled"),
2602
- checkAssistantContent: settings.get("model.loopGuard.checkAssistantContent"),
2603
- ...streamOptions?.loopGuard,
2604
- },
2605
- });
2589
+ return settingsAwareStreamFn(streamModel, context, streamOptions);
2606
2590
  },
2607
2591
  cursorExecHandlers,
2608
2592
  transformToolCallArguments: (args, _toolName) => {
@@ -2718,8 +2702,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2718
2702
  toolRegistry,
2719
2703
  builtInToolNames: builtInRegistryToolNames,
2720
2704
  transformContext,
2705
+ transformProviderContext,
2721
2706
  onPayload,
2722
2707
  onResponse,
2708
+ advisorStreamFn: settingsAwareStreamFn,
2723
2709
  convertToLlm: convertToLlmFinal,
2724
2710
  rebuildSystemPrompt,
2725
2711
  reloadSshTool,