@oh-my-pi/pi-coding-agent 15.7.5 → 15.7.6

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 (74) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/dist/types/config/settings-schema.d.ts +9 -0
  3. package/dist/types/eval/__tests__/heartbeat.test.d.ts +1 -0
  4. package/dist/types/eval/heartbeat.d.ts +45 -0
  5. package/dist/types/extensibility/extensions/loader.d.ts +2 -2
  6. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  7. package/dist/types/extensibility/extensions/types.d.ts +24 -5
  8. package/dist/types/lsp/diagnostics-ledger.d.ts +10 -0
  9. package/dist/types/lsp/index.d.ts +2 -0
  10. package/dist/types/lsp/utils.d.ts +4 -0
  11. package/dist/types/modes/acp/acp-agent.d.ts +1 -1
  12. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  13. package/dist/types/modes/components/custom-editor.d.ts +0 -1
  14. package/dist/types/modes/components/hook-selector.d.ts +7 -1
  15. package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -2
  16. package/dist/types/modes/interactive-mode.d.ts +2 -2
  17. package/dist/types/modes/rpc/rpc-mode.d.ts +1 -1
  18. package/dist/types/modes/theme/theme.d.ts +1 -1
  19. package/dist/types/modes/types.d.ts +2 -2
  20. package/dist/types/session/agent-session.d.ts +8 -1
  21. package/dist/types/tools/ask.d.ts +8 -6
  22. package/dist/types/tools/eval-backends.d.ts +12 -0
  23. package/dist/types/tools/eval-render.d.ts +52 -0
  24. package/dist/types/tools/eval.d.ts +2 -35
  25. package/dist/types/tools/index.d.ts +4 -11
  26. package/dist/types/tui/output-block.d.ts +4 -3
  27. package/examples/extensions/README.md +1 -0
  28. package/examples/extensions/thinking-note.ts +13 -0
  29. package/package.json +9 -9
  30. package/src/config/model-registry.ts +33 -4
  31. package/src/config/settings-schema.ts +10 -0
  32. package/src/discovery/claude.ts +41 -22
  33. package/src/edit/index.ts +23 -3
  34. package/src/eval/__tests__/agent-bridge.test.ts +90 -0
  35. package/src/eval/__tests__/heartbeat.test.ts +84 -0
  36. package/src/eval/__tests__/llm-bridge.test.ts +30 -0
  37. package/src/eval/agent-bridge.ts +44 -38
  38. package/src/eval/heartbeat.ts +74 -0
  39. package/src/eval/js/executor.ts +13 -9
  40. package/src/eval/llm-bridge.ts +20 -14
  41. package/src/eval/py/executor.ts +14 -18
  42. package/src/exec/bash-executor.ts +31 -5
  43. package/src/extensibility/extensions/loader.ts +16 -18
  44. package/src/extensibility/extensions/runner.ts +22 -17
  45. package/src/extensibility/extensions/types.ts +39 -5
  46. package/src/internal-urls/docs-index.generated.ts +3 -3
  47. package/src/lsp/diagnostics-ledger.ts +51 -0
  48. package/src/lsp/index.ts +9 -22
  49. package/src/lsp/utils.ts +21 -0
  50. package/src/modes/acp/acp-agent.ts +8 -4
  51. package/src/modes/components/assistant-message.ts +28 -1
  52. package/src/modes/components/custom-editor.ts +9 -7
  53. package/src/modes/components/hook-selector.ts +159 -32
  54. package/src/modes/components/tool-execution.ts +20 -4
  55. package/src/modes/controllers/event-controller.ts +5 -2
  56. package/src/modes/controllers/extension-ui-controller.ts +3 -2
  57. package/src/modes/controllers/input-controller.ts +0 -15
  58. package/src/modes/interactive-mode.ts +2 -1
  59. package/src/modes/rpc/rpc-mode.ts +17 -6
  60. package/src/modes/theme/theme-schema.json +30 -0
  61. package/src/modes/theme/theme.ts +39 -2
  62. package/src/modes/types.ts +2 -1
  63. package/src/modes/utils/ui-helpers.ts +5 -2
  64. package/src/prompts/tools/ask.md +2 -1
  65. package/src/prompts/tools/eval.md +1 -1
  66. package/src/session/agent-session.ts +65 -11
  67. package/src/tools/ask.ts +74 -32
  68. package/src/tools/eval-backends.ts +38 -0
  69. package/src/tools/eval-render.ts +750 -0
  70. package/src/tools/eval.ts +27 -754
  71. package/src/tools/index.ts +7 -37
  72. package/src/tools/renderers.ts +1 -1
  73. package/src/tools/write.ts +9 -1
  74. package/src/tui/output-block.ts +5 -4
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ### Fixed
6
+
7
+ - Fixed a module-load crash (`ReferenceError: Cannot access 'evalToolRenderer' before initialization`) triggered whenever `tools/eval` was imported before `tools/renderers`. The eval JS backend statically pulls the agent/task/sdk/extension chain, which re-enters the root barrel → `modes/components` → `tool-execution` → `renderers` while `eval.ts` was still initializing, so `renderers.ts` read `evalToolRenderer` in its TDZ. The eval TUI renderer is now split into a dependency-light `tools/eval-render.ts` that `renderers.ts` imports directly (decoupling pure rendering from the eval runtime); `eval.ts` re-exports `evalToolRenderer`/`EVAL_DEFAULT_PREVIEW_LINES` for compatibility.
8
+
9
+ ## [15.7.6] - 2026-06-01
10
+ ### Added
11
+
12
+ - Added `ask` option descriptions so agents can keep short labels and render explanatory text as separate muted rows in the selector.
13
+ - Added an extension API for rendering supplemental UI below visible assistant thinking blocks.
14
+ - Added default-on `lsp.diagnosticsDeduplicate` support so post-edit LSP diagnostics already shown for a file are suppressed within the session and only new or changed diagnostics are surfaced.
15
+
16
+ ### Fixed
17
+
18
+ - Fixed a single ESC press both dismissing the @/slash autocomplete popup and aborting the running agent operation. ESC now drains the popup first; only when no popup is visible does it route to the global interrupt handler (matching the standard TUI/IDE pattern). The `shouldBypassAutocompleteOnEscape` editor hook is removed — it had become the trigger for this bug ([#1655](https://github.com/can1357/oh-my-pi/issues/1655)).
19
+ - Fixed Claude Code slash command discovery to load subdirectory commands recursively while preserving basename commands (e.g. `/apply`) and adding namespace aliases (e.g. `/opsx:apply`) for tools that install colon-namespaced workflows ([#1523](https://github.com/can1357/oh-my-pi/issues/1523)).
20
+ - Fixed the `eval` tool's per-cell `timeout` killing cells that were not stalled. The timeout is now a plain wall-clock budget on the cell's **own** work that is **paused only while a host-side `agent()`/`parallel()`/`llm()` bridge call is in flight** — those calls pump a heartbeat that re-arms the watchdog, so a long fanout or a slow (e.g. reasoning-tier) completion runs to completion instead of being aborted mid-flight (a subagent's time-to-first-token, a long quiet nested tool, or an entire oneshot `llm()` request no longer trip it). Nothing else re-arms the budget: ordinary compute, `print`/stdout, `log()`/`phase()`, and non-agent tool calls all count against it, so a cell that is not delegating to an agent/llm is bounded by the regular wall-clock timeout (and the timeout message no longer says "of inactivity"). The heartbeat is a pure keepalive — never persisted or rendered.
21
+
5
22
  ## [15.7.5] - 2026-06-01
6
23
  ### Fixed
7
24
 
@@ -31,6 +48,10 @@
31
48
 
32
49
  - Fixed unbounded MCP reconnect loop that could fork-bomb the host when a stdio MCP server completes the `initialize`/`tools/list` handshake and then exits. `MCPManager` now enforces a per-server crash circuit breaker (5 reconnects per 30 s window) on the automatic `transport.onClose` path; manual `/mcp reconnect` resets the window so users can recover after fixing the misconfiguration ([#1592](https://github.com/can1357/oh-my-pi/issues/1592)).
33
50
 
51
+ ### Fixed
52
+
53
+ - Fixed auto context maintenance to include the pending prompt in the pre-send token estimate, so large user turns compact history before the provider rejects an over-limit request ([#1618](https://github.com/can1357/oh-my-pi/issues/1618)).
54
+
34
55
  ## [15.7.4] - 2026-05-31
35
56
 
36
57
  ### Removed
@@ -78,6 +99,10 @@
78
99
 
79
100
  ### Removed
80
101
 
102
+ ### Fixed
103
+
104
+ - Fixed auto-discovered OpenAI-compatible / Ollama / llama.cpp / new-api proxy models defaulting to `maxTokens: 8192`, which made providers drop the streaming connection mid-response on large `write`/`edit` tool calls and surfaced as Bun's opaque `socket connection was closed unexpectedly`. The discovery cap is now `32_768` (`DISCOVERY_DEFAULT_MAX_TOKENS` in `packages/coding-agent/src/config/model-registry.ts`) and `min(contextWindow, …)` still honors smaller advertised context windows ([#1528](https://github.com/can1357/oh-my-pi/issues/1528)).
105
+
81
106
  - Removed the `/drop-images` slash command; use `/shake images`, which strips every image from the session through the same `dropImages()` path.
82
107
 
83
108
  ### Fixed
@@ -151,6 +176,10 @@
151
176
 
152
177
  - Removed the `recipe` tool and its `recipe.enabled` setting. Task-runner targets (just/package.json/Cargo/make/Taskfile) are invoked directly through `bash`.
153
178
 
179
+ ### Added
180
+
181
+ - Added a `symbols.spinnerFrames` field to custom theme JSON so themes can override the loader/tool-execution spinner. Accepts either a flat `string[]` (used for both spinner types) or `{ "status"?: string[], "activity"?: string[] }` to override each independently; anything not specified falls back to the symbol preset. Documented in `docs/theme.md` and validated by `theme-schema.json`. ([#1553](https://github.com/can1357/oh-my-pi/issues/1553))
182
+
154
183
  ## [15.6.0] - 2026-05-30
155
184
  ### Added
156
185
 
@@ -9238,4 +9267,4 @@ Initial public release.
9238
9267
  - Git branch display in footer
9239
9268
  - Message queueing during streaming responses
9240
9269
  - OAuth integration for Gmail and Google Calendar access
9241
- - HTML export with syntax highlighting and collapsible sections
9270
+ - HTML export with syntax highlighting and collapsible sections
@@ -2299,6 +2299,15 @@ export declare const SETTINGS_SCHEMA: {
2299
2299
  readonly description: "Return LSP diagnostics after editing code files";
2300
2300
  };
2301
2301
  };
2302
+ readonly "lsp.diagnosticsDeduplicate": {
2303
+ readonly type: "boolean";
2304
+ readonly default: true;
2305
+ readonly ui: {
2306
+ readonly tab: "editing";
2307
+ readonly label: "Deduplicate Diagnostics";
2308
+ readonly description: "Suppress post-edit LSP diagnostics already shown for a file; only surface new or changed ones";
2309
+ };
2310
+ };
2302
2311
  readonly "bashInterceptor.enabled": {
2303
2312
  readonly type: "boolean";
2304
2313
  readonly default: false;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Keepalive for in-flight host-side eval bridge calls.
3
+ *
4
+ * The eval watchdog ({@link ../tools/eval IdleTimeout}) caps a cell's `timeout`
5
+ * as a wall-clock budget on the cell's *own* work, but pauses that budget while
6
+ * a host-side `agent()`/`parallel()` (via `runSubprocess`) or `llm()` (a single
7
+ * completion) call is in flight. Those calls are the only thing that re-arms the
8
+ * watchdog — and they can run for long stretches with **no** status of their own
9
+ * (a subagent's time-to-first-token on a reasoning model, a long quiet nested
10
+ * tool, or the entire body of a oneshot `llm()` call). Without a keepalive the
11
+ * watchdog would mistake that delegated work for the cell stalling and abort it
12
+ * mid-flight, killing the subagent.
13
+ *
14
+ * {@link withBridgeHeartbeat} bridges that gap by emitting a synthetic
15
+ * {@link EVAL_HEARTBEAT_OP} status event immediately when the call begins and
16
+ * then on a fixed cadence until it settles. The event rides the same
17
+ * `emitStatus → onStatus` channel both runtimes already forward, so it re-arms
18
+ * the watchdog without any new plumbing. The heartbeat is the *sole* signal that
19
+ * extends the budget: consumers MUST treat it as a pure keepalive — bump the
20
+ * watchdog and drop it (never persist or render it) — see the executor display
21
+ * sinks and the eval tool's `onStatus` handler. Every other status event
22
+ * (compute helpers, `log()`/`phase()`, tool results) counts against the budget.
23
+ */
24
+ import type { JsStatusEvent } from "./js/shared/types";
25
+ /**
26
+ * Synthetic status op emitted purely to keep the eval idle watchdog alive while
27
+ * a host-side bridge call is in flight. Carries no payload.
28
+ */
29
+ export declare const EVAL_HEARTBEAT_OP = "heartbeat";
30
+ /**
31
+ * Test seam: override the heartbeat cadence so integration tests can exercise
32
+ * the keepalive within a sub-second idle budget. Pass no value to restore the
33
+ * production default.
34
+ */
35
+ export declare function setBridgeHeartbeatIntervalMs(ms?: number): void;
36
+ /**
37
+ * Run {@link operation}, pumping {@link EVAL_HEARTBEAT_OP} status events through
38
+ * {@link emitStatus} — one immediately, then on a fixed cadence — until it
39
+ * settles. The immediate beat pauses the watchdog the instant the call begins,
40
+ * so a bridge call that starts close to the budget edge (after the cell already
41
+ * spent most of it computing) is not aborted before the first interval tick. A
42
+ * no-op wrapper when no `emitStatus` sink is wired (the heartbeat would reach
43
+ * nobody).
44
+ */
45
+ export declare function withBridgeHeartbeat<T>(emitStatus: ((event: JsStatusEvent) => void) | undefined, operation: () => Promise<T>): Promise<T>;
@@ -1,6 +1,6 @@
1
1
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import { EventBus } from "../../utils/event-bus";
3
- import type { Extension, ExtensionFactory, ExtensionRuntime as IExtensionRuntime, LoadExtensionsResult } from "./types";
3
+ import type { Extension, ExtensionFactory, ExtensionRuntime as IExtensionRuntime, LoadExtensionsResult, ProviderConfig } from "./types";
4
4
  export declare class ExtensionRuntimeNotInitializedError extends Error {
5
5
  constructor();
6
6
  }
@@ -12,7 +12,7 @@ export declare class ExtensionRuntime implements IExtensionRuntime {
12
12
  flagValues: Map<string, string | boolean>;
13
13
  pendingProviderRegistrations: Array<{
14
14
  name: string;
15
- config: import("./types").ProviderConfig;
15
+ config: ProviderConfig;
16
16
  sourceId: string;
17
17
  }>;
18
18
  sendMessage(): void;
@@ -6,7 +6,7 @@ import type { CredentialDisabledEvent, ImageContent, Model, ProviderResponseMeta
6
6
  import type { KeyId } from "@oh-my-pi/pi-tui";
7
7
  import type { ModelRegistry } from "../../config/model-registry";
8
8
  import type { SessionManager } from "../../session/session-manager";
9
- import type { AfterProviderResponseEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, ContextEvent, Extension, ExtensionActions, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFlag, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, InputEvent, InputEventResult, MessageRenderer, RegisteredCommand, RegisteredTool, ResourcesDiscoverEvent, SessionBeforeBranchResult, SessionBeforeCompactResult, SessionBeforeSwitchResult, SessionBeforeTreeResult, SessionCompactingResult, ToolCallEvent, ToolCallEventResult, ToolResultEvent, ToolResultEventResult, UserBashEvent, UserBashEventResult, UserPythonEvent, UserPythonEventResult } from "./types";
9
+ import type { AfterProviderResponseEvent, AssistantThinkingRenderer, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, ContextEvent, Extension, ExtensionActions, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFlag, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, InputEvent, InputEventResult, MessageRenderer, RegisteredCommand, RegisteredTool, ResourcesDiscoverEvent, SessionBeforeBranchResult, SessionBeforeCompactResult, SessionBeforeSwitchResult, SessionBeforeTreeResult, SessionCompactingResult, ToolCallEvent, ToolCallEventResult, ToolResultEvent, ToolResultEventResult, UserBashEvent, UserBashEventResult, UserPythonEvent, UserPythonEventResult } from "./types";
10
10
  /** Combined result from all before_agent_start handlers */
11
11
  interface BeforeAgentStartCombinedResult {
12
12
  messages?: NonNullable<BeforeAgentStartEventResult["message"]>[];
@@ -98,6 +98,7 @@ export declare class ExtensionRunner {
98
98
  emitError(error: ExtensionError): void;
99
99
  hasHandlers(eventType: string): boolean;
100
100
  getMessageRenderer(customType: string): MessageRenderer | undefined;
101
+ getAssistantThinkingRenderers(): AssistantThinkingRenderer[];
101
102
  getRegisteredCommands(reserved?: Set<string>): RegisteredCommand[];
102
103
  getCommandDiagnostics(): Array<{
103
104
  type: string;
@@ -13,6 +13,8 @@ import type { Api, AssistantMessageEvent, AssistantMessageEventStream, Context,
13
13
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/utils/oauth/types";
14
14
  import type * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
15
15
  import type { AutocompleteItem, Component, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
16
+ import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
17
+ import type * as Zod from "zod/v4";
16
18
  import type { KeybindingsManager } from "../../config/keybindings";
17
19
  import type { ModelRegistry } from "../../config/model-registry";
18
20
  import type { EditToolDetails } from "../../edit";
@@ -27,9 +29,16 @@ import type { BashToolDetails, BashToolInput, FindToolDetails, FindToolInput, Re
27
29
  import type { EventBus } from "../../utils/event-bus";
28
30
  import type { AgentEndEvent, AgentStartEvent, AutoCompactionEndEvent, AutoCompactionStartEvent, AutoRetryEndEvent, AutoRetryStartEvent, ContextEvent, GoalUpdatedEvent, SessionBeforeBranchEvent, SessionBeforeBranchResult, SessionBeforeCompactEvent, SessionBeforeCompactResult, SessionBeforeSwitchEvent, SessionBeforeSwitchResult, SessionBeforeTreeEvent, SessionBeforeTreeResult, SessionBranchEvent, SessionCompactEvent, SessionCompactingEvent, SessionCompactingResult, SessionEvent, SessionShutdownEvent, SessionStartEvent, SessionSwitchEvent, SessionTreeEvent, TodoReminderEvent, ToolCallEventResult, ToolResultEventResult, TtsrTriggeredEvent, TurnEndEvent, TurnStartEvent } from "../shared-events";
29
31
  import type { SlashCommandInfo } from "../slash-commands";
32
+ import type * as TypeBox from "../typebox";
30
33
  export type { AppKeybinding, KeybindingsManager } from "../../config/keybindings";
31
34
  export type { ExecOptions, ExecResult } from "../../exec/exec";
32
35
  export type { AgentToolResult, AgentToolUpdateCallback };
36
+ export interface ExtensionUISelectOption {
37
+ label: string;
38
+ description?: string;
39
+ }
40
+ export type ExtensionUISelectItem = string | ExtensionUISelectOption;
41
+ export declare function getExtensionUISelectOptionLabel(option: ExtensionUISelectItem): string;
33
42
  /**
34
43
  * UI dialog options for extensions.
35
44
  */
@@ -70,8 +79,8 @@ export type ExtensionWidgetContent = string[] | ExtensionUiComponentFactory | un
70
79
  * Each mode (interactive, RPC, print) provides its own implementation.
71
80
  */
72
81
  export interface ExtensionUIContext {
73
- /** Show a selector and return the user's choice. */
74
- select(title: string, options: string[], dialogOptions?: ExtensionUIDialogOptions): Promise<string | undefined>;
82
+ /** Show a selector and return the selected label, even when an option also includes a description. */
83
+ select(title: string, options: ExtensionUISelectItem[], dialogOptions?: ExtensionUIDialogOptions): Promise<string | undefined>;
75
84
  /** Show a confirmation dialog. */
76
85
  confirm(title: string, message: string, dialogOptions?: ExtensionUIDialogOptions): Promise<boolean>;
77
86
  /** Show a text input dialog. */
@@ -508,6 +517,13 @@ export interface MessageRenderOptions {
508
517
  expanded: boolean;
509
518
  }
510
519
  export type MessageRenderer<T = unknown> = (message: CustomMessage<T>, options: MessageRenderOptions, theme: Theme) => Component | undefined;
520
+ export interface AssistantThinkingRenderContext {
521
+ contentIndex: number;
522
+ thinkingIndex: number;
523
+ text: string;
524
+ requestRender(): void;
525
+ }
526
+ export type AssistantThinkingRenderer = (context: AssistantThinkingRenderContext, theme: Theme) => Component | undefined;
511
527
  export interface RegisteredCommand {
512
528
  name: string;
513
529
  description?: string;
@@ -521,11 +537,11 @@ export type ExtensionHandler<E, R = undefined> = (event: E, ctx: ExtensionContex
521
537
  */
522
538
  export interface ExtensionAPI {
523
539
  /** File logger for error/warning/debug messages */
524
- logger: typeof import("@oh-my-pi/pi-utils").logger;
540
+ logger: typeof PiLogger;
525
541
  /** Injected zod-backed typebox shim for legacy `Type.Object(...)` parameter authoring. */
526
- typebox: typeof import("../typebox");
542
+ typebox: typeof TypeBox;
527
543
  /** Injected zod module for Zod-authored extension tools (canonical going forward). */
528
- zod: typeof import("zod/v4");
544
+ zod: typeof Zod;
529
545
  /** Injected pi-coding-agent exports for accessing SDK utilities */
530
546
  pi: typeof piCodingAgent;
531
547
  on(event: "resources_discover", handler: ExtensionHandler<ResourcesDiscoverEvent, ResourcesDiscoverResult>): void;
@@ -592,6 +608,8 @@ export interface ExtensionAPI {
592
608
  getFlag(name: string): boolean | string | undefined;
593
609
  /** Register a custom renderer for CustomMessageEntry. */
594
610
  registerMessageRenderer<T = unknown>(customType: string, renderer: MessageRenderer<T>): void;
611
+ /** Register a renderer for assistant thinking blocks. Rendered after the original thinking text. */
612
+ registerAssistantThinkingRenderer(renderer: AssistantThinkingRenderer): void;
595
613
  /**
596
614
  * Send a custom message to the session.
597
615
  *
@@ -840,6 +858,7 @@ export interface Extension {
840
858
  label?: string;
841
859
  handlers: Map<string, HandlerFn[]>;
842
860
  tools: Map<string, RegisteredTool<any, any>>;
861
+ assistantThinkingRenderers: AssistantThinkingRenderer[];
843
862
  messageRenderers: Map<string, MessageRenderer>;
844
863
  commands: Map<string, RegisteredCommand>;
845
864
  flags: Map<string, ExtensionFlag>;
@@ -0,0 +1,10 @@
1
+ import type { FileDiagnosticsResult } from "./index";
2
+ export declare function diagnosticIdentity(message: string): string;
3
+ export declare class DiagnosticsLedger {
4
+ #private;
5
+ reduce(absPath: string, result: FileDiagnosticsResult): FileDiagnosticsResult;
6
+ }
7
+ export interface DiagnosticsLedgerOwner {
8
+ diagnosticsLedger?: DiagnosticsLedger;
9
+ }
10
+ export declare function getDiagnosticsLedger(owner: DiagnosticsLedgerOwner): DiagnosticsLedger;
@@ -71,6 +71,8 @@ export interface WritethroughOptions {
71
71
  onDeferredDiagnostics?: (diagnostics: FileDiagnosticsResult) => void;
72
72
  /** Signal to cancel a pending deferred diagnostics fetch. */
73
73
  deferredSignal?: AbortSignal;
74
+ /** Transform diagnostics before surfacing them after a successful fetch. */
75
+ transformDiagnostics?: (absPath: string, result: FileDiagnosticsResult) => FileDiagnosticsResult;
74
76
  }
75
77
  /** Per-file deferred LSP diagnostics wiring for {@link WritethroughCallback}. */
76
78
  export type WritethroughDeferredHandle = {
@@ -39,6 +39,10 @@ export declare function formatGroupedDiagnosticMessages(messages: string[]): str
39
39
  * Format diagnostics grouped by severity.
40
40
  */
41
41
  export declare function formatDiagnosticsSummary(diagnostics: Diagnostic[]): string;
42
+ export declare function summarizeDiagnosticMessages(messages: string[]): {
43
+ summary: string;
44
+ errored: boolean;
45
+ };
42
46
  /**
43
47
  * Format a location as file:line:col relative to cwd.
44
48
  */
@@ -1,5 +1,5 @@
1
1
  import { type Agent, type AgentSideConnection, type AuthenticateRequest, type AuthenticateResponse, type ClientCapabilities, type CloseSessionRequest, type CloseSessionResponse, type ForkSessionRequest, type ForkSessionResponse, type InitializeRequest, type InitializeResponse, type ListSessionsRequest, type ListSessionsResponse, type LoadSessionRequest, type LoadSessionResponse, type NewSessionRequest, type NewSessionResponse, type PromptRequest, type PromptResponse, type ResumeSessionRequest, type ResumeSessionResponse, type SetSessionConfigOptionRequest, type SetSessionConfigOptionResponse, type SetSessionModelRequest, type SetSessionModelResponse, type SetSessionModeRequest, type SetSessionModeResponse } from "@agentclientprotocol/sdk";
2
- import type { ExtensionUIContext } from "../../extensibility/extensions";
2
+ import { type ExtensionUIContext } from "../../extensibility/extensions";
3
3
  import type { AgentSession } from "../../session/agent-session";
4
4
  /**
5
5
  * Delay between `session/new` (or `session/load` / `session/resume` /
@@ -1,5 +1,6 @@
1
1
  import type { AssistantMessage, ImageContent, Usage } from "@oh-my-pi/pi-ai";
2
2
  import { Container } from "@oh-my-pi/pi-tui";
3
+ import type { AssistantThinkingRenderer } from "../../extensibility/extensions/types";
3
4
  /**
4
5
  * Component that renders a complete assistant message
5
6
  */
@@ -7,7 +8,8 @@ export declare class AssistantMessageComponent extends Container {
7
8
  #private;
8
9
  private hideThinkingBlock;
9
10
  private readonly onImageUpdate?;
10
- constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: (() => void) | undefined);
11
+ private readonly thinkingRenderers;
12
+ constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: (() => void) | undefined, thinkingRenderers?: readonly AssistantThinkingRenderer[]);
11
13
  invalidate(): void;
12
14
  setHideThinkingBlock(hide: boolean): void;
13
15
  setToolResultImages(toolCallId: string, images: ImageContent[]): void;
@@ -10,7 +10,6 @@ export declare class CustomEditor extends Editor {
10
10
  * them, skipping any occurrence inside code spans, fenced blocks, or XML sections. */
11
11
  decorateText: (text: string) => string;
12
12
  onEscape?: () => void;
13
- shouldBypassAutocompleteOnEscape?: () => boolean;
14
13
  onClear?: () => void;
15
14
  onExit?: () => void;
16
15
  onCycleThinkingLevel?: () => void;
@@ -42,9 +42,15 @@ export interface HookSelectorOptions {
42
42
  helpText?: string;
43
43
  slider?: HookSelectorSlider;
44
44
  }
45
+ export interface HookSelectorOption {
46
+ label: string;
47
+ description?: string;
48
+ }
49
+ export type HookSelectorOptionInput = string | HookSelectorOption;
45
50
  export declare class HookSelectorComponent extends Container {
46
51
  #private;
47
- constructor(title: string, options: string[], onSelect: (option: string) => void, onCancel: () => void, opts?: HookSelectorOptions);
52
+ constructor(title: string, options: HookSelectorOptionInput[], onSelect: (option: string) => void, onCancel: () => void, opts?: HookSelectorOptions);
48
53
  handleInput(keyData: string): void;
54
+ render(width: number): string[];
49
55
  dispose(): void;
50
56
  }
@@ -1,6 +1,6 @@
1
1
  import type { Component, TUI } from "@oh-my-pi/pi-tui";
2
2
  import { KeybindingsManager } from "../../config/keybindings";
3
- import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetContent, ExtensionWidgetOptions, TerminalInputHandler } from "../../extensibility/extensions";
3
+ import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions, TerminalInputHandler } from "../../extensibility/extensions";
4
4
  import { type HookSelectorSlider } from "../../modes/components/hook-selector";
5
5
  import { type Theme } from "../../modes/theme/theme";
6
6
  import type { InteractiveModeContext } from "../../modes/types";
@@ -30,7 +30,7 @@ export declare class ExtensionUiController {
30
30
  /**
31
31
  * Show a selector for hooks.
32
32
  */
33
- showHookSelector(title: string, options: string[], dialogOptions?: ExtensionUIDialogOptions, extra?: {
33
+ showHookSelector(title: string, options: ExtensionUISelectItem[], dialogOptions?: ExtensionUIDialogOptions, extra?: {
34
34
  slider?: HookSelectorSlider;
35
35
  }): Promise<string | undefined>;
36
36
  /**
@@ -5,7 +5,7 @@ import type { Component, EditorTheme } from "@oh-my-pi/pi-tui";
5
5
  import { Container, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
6
6
  import { KeybindingsManager } from "../config/keybindings";
7
7
  import { Settings } from "../config/settings";
8
- import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
8
+ import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
9
9
  import type { CompactOptions } from "../extensibility/extensions/types";
10
10
  import { type PlanApprovalDetails } from "../plan-mode/approved-plan";
11
11
  import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
@@ -253,7 +253,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
253
253
  emitCustomToolSessionEvent(reason: "start" | "switch" | "branch" | "tree" | "shutdown", previousSessionFile?: string): Promise<void>;
254
254
  setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void;
255
255
  setHookStatus(key: string, text: string | undefined): void;
256
- showHookSelector(title: string, options: string[], dialogOptions?: ExtensionUIDialogOptions, extra?: {
256
+ showHookSelector(title: string, options: ExtensionUISelectItem[], dialogOptions?: ExtensionUIDialogOptions, extra?: {
257
257
  slider?: HookSelectorSlider;
258
258
  }): Promise<string | undefined>;
259
259
  hideHookSelector(): void;
@@ -1,4 +1,4 @@
1
- import type { ExtensionUIContext, ExtensionUIDialogOptions } from "../../extensibility/extensions";
1
+ import { type ExtensionUIContext, type ExtensionUIDialogOptions } from "../../extensibility/extensions";
2
2
  import type { AgentSession } from "../../session/agent-session";
3
3
  import type { RpcExtensionUIRequest, RpcExtensionUIResponse, RpcHostToolCallRequest, RpcHostToolCancelRequest, RpcHostUriCancelRequest, RpcHostUriRequest, RpcResponse } from "./rpc-types";
4
4
  export type * from "./rpc-types";
@@ -17,7 +17,7 @@ export declare class Theme {
17
17
  #private;
18
18
  private readonly mode;
19
19
  private readonly symbolPreset;
20
- constructor(fgColors: Record<ThemeColor, string | number>, bgColors: Record<ThemeBg, string | number>, mode: ColorMode, symbolPreset: SymbolPreset, symbolOverrides: Partial<Record<SymbolKey, string>>);
20
+ constructor(fgColors: Record<ThemeColor, string | number>, bgColors: Record<ThemeBg, string | number>, mode: ColorMode, symbolPreset: SymbolPreset, symbolOverrides: Partial<Record<SymbolKey, string>>, spinnerFramesOverrides?: Partial<Record<SpinnerType, string[]>>);
21
21
  fg(color: ThemeColor, text: string): string;
22
22
  bg(color: ThemeBg, text: string): string;
23
23
  bold(text: string): string;
@@ -4,7 +4,7 @@ import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-m
4
4
  import type { Component, Container, EditorTheme, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
5
5
  import type { KeybindingsManager } from "../config/keybindings";
6
6
  import type { Settings } from "../config/settings";
7
- import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
7
+ import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
8
8
  import type { CompactOptions } from "../extensibility/extensions/types";
9
9
  import type { MCPManager } from "../mcp";
10
10
  import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
@@ -264,7 +264,7 @@ export interface InteractiveModeContext {
264
264
  emitCustomToolSessionEvent(reason: "start" | "switch" | "branch" | "tree" | "shutdown", previousSessionFile?: string): Promise<void>;
265
265
  setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void;
266
266
  setHookStatus(key: string, text: string | undefined): void;
267
- showHookSelector(title: string, options: string[], dialogOptions?: ExtensionUIDialogOptions): Promise<string | undefined>;
267
+ showHookSelector(title: string, options: ExtensionUISelectItem[], dialogOptions?: ExtensionUIDialogOptions): Promise<string | undefined>;
268
268
  hideHookSelector(): void;
269
269
  showHookInput(title: string, placeholder?: string): Promise<string | undefined>;
270
270
  hideHookInput(): void;
@@ -438,8 +438,15 @@ export declare class AgentSession {
438
438
  /**
439
439
  * Replace MCP tools in the registry and recompute the visible MCP tool set immediately.
440
440
  * This allows /mcp add/remove/reauth to take effect without restarting the session.
441
+ *
442
+ * @param mcpTools The new MCP tools to register.
443
+ * @param options.activateAll When true, force-activates every newly registered MCP tool
444
+ * regardless of prior selection state. Used when an ACP client provisions MCP servers
445
+ * for a session where MCP discovery is disabled.
441
446
  */
442
- refreshMCPTools(mcpTools: CustomTool[]): Promise<void>;
447
+ refreshMCPTools(mcpTools: CustomTool[], options?: {
448
+ activateAll?: boolean;
449
+ }): Promise<void>;
443
450
  /**
444
451
  * Replace RPC host-owned tools and refresh the active tool set before the next model call.
445
452
  */
@@ -26,6 +26,7 @@ declare const askSchema: z.ZodObject<{
26
26
  question: z.ZodString;
27
27
  options: z.ZodArray<z.ZodObject<{
28
28
  label: z.ZodString;
29
+ description: z.ZodOptional<z.ZodString>;
29
30
  }, z.core.$strip>>;
30
31
  multi: z.ZodOptional<z.ZodBoolean>;
31
32
  recommended: z.ZodOptional<z.ZodNumber>;
@@ -71,6 +72,7 @@ export declare class AskTool implements AgentTool<typeof askSchema, AskToolDetai
71
72
  question: z.ZodString;
72
73
  options: z.ZodArray<z.ZodObject<{
73
74
  label: z.ZodString;
75
+ description: z.ZodOptional<z.ZodString>;
74
76
  }, z.core.$strip>>;
75
77
  multi: z.ZodOptional<z.ZodBoolean>;
76
78
  recommended: z.ZodOptional<z.ZodNumber>;
@@ -82,18 +84,18 @@ export declare class AskTool implements AgentTool<typeof askSchema, AskToolDetai
82
84
  static createIf(session: ToolSession): AskTool | null;
83
85
  execute(_toolCallId: string, params: AskParams, signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<AskToolDetails>, context?: AgentToolContext): Promise<AgentToolResult<AskToolDetails>>;
84
86
  }
87
+ interface AskRenderOption {
88
+ label: string;
89
+ description?: string;
90
+ }
85
91
  interface AskRenderArgs {
86
92
  question?: string;
87
- options?: Array<{
88
- label: string;
89
- }>;
93
+ options?: AskRenderOption[];
90
94
  multi?: boolean;
91
95
  questions?: Array<{
92
96
  id: string;
93
97
  question: string;
94
- options: Array<{
95
- label: string;
96
- }>;
98
+ options: AskRenderOption[];
97
99
  multi?: boolean;
98
100
  }>;
99
101
  }
@@ -0,0 +1,12 @@
1
+ import type { ToolSession } from ".";
2
+ export interface EvalBackendsAllowance {
3
+ python: boolean;
4
+ js: boolean;
5
+ }
6
+ /** Read per-backend allowance from settings (defaults true). */
7
+ export declare function readEvalBackendsAllowance(session: ToolSession): EvalBackendsAllowance;
8
+ /**
9
+ * Materialize the active eval backend allowance: PI_PY / PI_JS env flags
10
+ * override the per-key settings; otherwise settings (defaults true) win.
11
+ */
12
+ export declare function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * TUI rendering for the eval tool.
3
+ *
4
+ * Split out from `eval.ts` so the renderer can be imported by `renderers.ts`
5
+ * without dragging the eval *runtime* (JS/Python backends -> agent bridge ->
6
+ * task executor -> sdk -> extension loader -> root barrel) into the renderer
7
+ * module graph. That transitive chain re-enters `renderers.ts` while `eval.ts`
8
+ * is still initializing, which previously crashed module load with a TDZ
9
+ * `Cannot access 'evalToolRenderer' before initialization`.
10
+ */
11
+ import type { Component } from "@oh-my-pi/pi-tui";
12
+ import type { EvalStatusEvent, EvalToolDetails } from "../eval/types";
13
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
14
+ import { type Theme } from "../modes/theme/theme";
15
+ export declare const EVAL_DEFAULT_PREVIEW_LINES = 10;
16
+ interface EvalRenderCellArg {
17
+ language?: string;
18
+ code?: string;
19
+ title?: string;
20
+ }
21
+ interface EvalRenderArgs {
22
+ cells?: EvalRenderCellArg[];
23
+ __partialJson?: string;
24
+ }
25
+ interface EvalRenderContext {
26
+ output?: string;
27
+ expanded?: boolean;
28
+ previewLines?: number;
29
+ timeout?: number;
30
+ }
31
+ /**
32
+ * Append or replace a status event. `agent` events are progress snapshots keyed
33
+ * by `id`, so they coalesce in place (preserving first-seen order); every other
34
+ * op is a discrete action and simply appends. Keeps the persisted event list
35
+ * bounded even when a subagent emits hundreds of throttled progress ticks.
36
+ */
37
+ export declare function upsertStatusEvent(events: EvalStatusEvent[], event: EvalStatusEvent): void;
38
+ export declare const evalToolRenderer: {
39
+ renderCall(args: EvalRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
40
+ renderResult(result: {
41
+ content: Array<{
42
+ type: string;
43
+ text?: string;
44
+ }>;
45
+ details?: EvalToolDetails;
46
+ }, options: RenderResultOptions & {
47
+ renderContext?: EvalRenderContext;
48
+ }, uiTheme: Theme, _args?: EvalRenderArgs): Component;
49
+ mergeCallAndResult: boolean;
50
+ inline: boolean;
51
+ };
52
+ export {};
@@ -1,11 +1,8 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
- import type { Component } from "@oh-my-pi/pi-tui";
3
2
  import * as z from "zod/v4";
4
3
  import type { EvalToolDetails } from "../eval/types";
5
- import type { RenderResultOptions } from "../extensibility/custom-tools/types";
6
- import { type Theme } from "../modes/theme/theme";
7
- import { type ToolSession } from ".";
8
- export declare const EVAL_DEFAULT_PREVIEW_LINES = 10;
4
+ import type { ToolSession } from ".";
5
+ export { EVAL_DEFAULT_PREVIEW_LINES, evalToolRenderer } from "./eval-render";
9
6
  /**
10
7
  * Per-cell input. Each cell runs in order; state persists within a language
11
8
  * across cells and across tool calls.
@@ -78,33 +75,3 @@ export declare class EvalTool implements AgentTool<typeof evalSchema> {
78
75
  constructor(session: ToolSession | null, options?: EvalToolOptions);
79
76
  execute(_toolCallId: string, params: z.infer<typeof evalSchema>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback, _ctx?: AgentToolContext): Promise<AgentToolResult<EvalToolDetails | undefined>>;
80
77
  }
81
- interface EvalRenderCellArg {
82
- language?: string;
83
- code?: string;
84
- title?: string;
85
- }
86
- interface EvalRenderArgs {
87
- cells?: EvalRenderCellArg[];
88
- __partialJson?: string;
89
- }
90
- interface EvalRenderContext {
91
- output?: string;
92
- expanded?: boolean;
93
- previewLines?: number;
94
- timeout?: number;
95
- }
96
- export declare const evalToolRenderer: {
97
- renderCall(args: EvalRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
98
- renderResult(result: {
99
- content: Array<{
100
- type: string;
101
- text?: string;
102
- }>;
103
- details?: EvalToolDetails;
104
- }, options: RenderResultOptions & {
105
- renderContext?: EvalRenderContext;
106
- }, uiTheme: Theme, _args?: EvalRenderArgs): Component;
107
- mergeCallAndResult: boolean;
108
- inline: boolean;
109
- };
110
- export {};
@@ -37,6 +37,7 @@ export * from "./browser";
37
37
  export * from "./checkpoint";
38
38
  export * from "./debug";
39
39
  export * from "./eval";
40
+ export * from "./eval-backends";
40
41
  export * from "./find";
41
42
  export * from "./gh";
42
43
  export * from "./image-gen";
@@ -218,6 +219,9 @@ export interface ToolSession {
218
219
  * to splice the recorded region with replacement content. Lazily initialized
219
220
  * by `getConflictHistory`. */
220
221
  conflictHistory?: import("./conflict-detect").ConflictHistory;
222
+ /** Per-session ledger of post-edit LSP diagnostics already surfaced to the
223
+ * model for each file. Lazily initialized by `getDiagnosticsLedger`. */
224
+ diagnosticsLedger?: import("../lsp/diagnostics-ledger").DiagnosticsLedger;
221
225
  /** Queue a hidden message to be injected at the next agent turn. */
222
226
  queueDeferredMessage?(message: CustomMessage): void;
223
227
  /** Get the active OpenTelemetry config so subagent dispatch can forward
@@ -241,17 +245,6 @@ export declare function computeEssentialBuiltinNames(settings: Settings): string
241
245
  export declare const BUILTIN_TOOLS: Record<string, ToolFactory>;
242
246
  export declare const HIDDEN_TOOLS: Record<string, ToolFactory>;
243
247
  export type ToolName = keyof typeof BUILTIN_TOOLS;
244
- export interface EvalBackendsAllowance {
245
- python: boolean;
246
- js: boolean;
247
- }
248
- /** Read per-backend allowance from settings (defaults true). */
249
- export declare function readEvalBackendsAllowance(session: ToolSession): EvalBackendsAllowance;
250
- /**
251
- * Materialize the active eval backend allowance: PI_PY / PI_JS env flags
252
- * override the per-key settings; otherwise settings (defaults true) win.
253
- */
254
- export declare function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance;
255
248
  /**
256
249
  * Create tools from BUILTIN_TOOLS registry.
257
250
  */