@oh-my-pi/pi-coding-agent 17.0.8 → 17.0.9

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 (90) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/dist/cli.js +4850 -4774
  3. package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
  4. package/dist/types/config/model-registry.d.ts +27 -0
  5. package/dist/types/config/settings-schema.d.ts +34 -3
  6. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
  7. package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
  8. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +1 -1
  9. package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
  10. package/dist/types/lsp/client.d.ts +2 -0
  11. package/dist/types/mcp/manager.d.ts +6 -2
  12. package/dist/types/mcp/render.d.ts +2 -2
  13. package/dist/types/mcp/tool-bridge.d.ts +7 -3
  14. package/dist/types/mcp/types.d.ts +6 -0
  15. package/dist/types/modes/components/oauth-selector.d.ts +8 -0
  16. package/dist/types/modes/components/settings-defs.d.ts +1 -0
  17. package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
  18. package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
  19. package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
  20. package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
  21. package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
  22. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
  23. package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
  24. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
  25. package/dist/types/session/agent-session.d.ts +8 -1
  26. package/dist/types/session/session-loader.d.ts +6 -4
  27. package/dist/types/task/types.d.ts +14 -0
  28. package/dist/types/tools/hub/jobs.d.ts +1 -1
  29. package/dist/types/tools/index.d.ts +3 -0
  30. package/dist/types/tools/report-tool-issue.d.ts +24 -9
  31. package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
  32. package/dist/types/web/search/types.d.ts +1 -1
  33. package/package.json +13 -12
  34. package/scripts/legacy-pi-virtual-module.ts +1 -1
  35. package/src/cli/grievances-cli.ts +2 -2
  36. package/src/cli/usage-cli.ts +1 -1
  37. package/src/config/__tests__/model-registry.test.ts +147 -0
  38. package/src/config/model-registry.ts +40 -0
  39. package/src/config/model-resolver.ts +0 -1
  40. package/src/config/settings-schema.ts +41 -3
  41. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
  42. package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
  43. package/src/extensibility/plugins/legacy-pi-compat.ts +851 -310
  44. package/src/internal-urls/registry-helpers.ts +40 -0
  45. package/src/lsp/client.ts +23 -0
  46. package/src/lsp/clients/biome-client.ts +3 -4
  47. package/src/lsp/index.ts +25 -7
  48. package/src/main.ts +1 -0
  49. package/src/mcp/manager.ts +39 -8
  50. package/src/mcp/render.ts +94 -35
  51. package/src/mcp/tool-bridge.ts +107 -11
  52. package/src/mcp/types.ts +7 -0
  53. package/src/modes/components/agent-hub.ts +26 -9
  54. package/src/modes/components/oauth-selector.ts +24 -7
  55. package/src/modes/components/settings-defs.ts +5 -2
  56. package/src/modes/components/settings-selector.ts +9 -5
  57. package/src/modes/components/tool-execution.test.ts +63 -2
  58. package/src/modes/controllers/command-controller.ts +14 -7
  59. package/src/modes/controllers/mcp-command-controller.ts +70 -40
  60. package/src/modes/interactive-mode.ts +3 -0
  61. package/src/modes/rpc/rpc-client.ts +94 -3
  62. package/src/modes/rpc/rpc-frame.ts +164 -4
  63. package/src/modes/rpc/rpc-messages.ts +127 -0
  64. package/src/modes/rpc/rpc-mode.ts +79 -7
  65. package/src/modes/rpc/rpc-types.ts +34 -2
  66. package/src/modes/setup-wizard/scenes/model.ts +5 -7
  67. package/src/modes/setup-wizard/scenes/providers.ts +3 -2
  68. package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
  69. package/src/modes/setup-wizard/scenes/theme.ts +13 -3
  70. package/src/modes/setup-wizard/scenes/types.ts +9 -1
  71. package/src/modes/setup-wizard/scenes/web-search.ts +6 -1
  72. package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
  73. package/src/prompts/goals/guided-goal-system.md +24 -3
  74. package/src/prompts/tools/task.md +12 -2
  75. package/src/sdk.ts +45 -3
  76. package/src/session/agent-session.ts +63 -32
  77. package/src/session/session-context.test.ts +224 -1
  78. package/src/session/session-context.ts +41 -2
  79. package/src/session/session-loader.ts +10 -5
  80. package/src/slash-commands/helpers/usage-report.ts +3 -1
  81. package/src/task/executor.ts +3 -0
  82. package/src/task/index.ts +52 -8
  83. package/src/task/structured-subagent.ts +3 -1
  84. package/src/task/types.ts +16 -0
  85. package/src/tools/hub/index.ts +1 -1
  86. package/src/tools/hub/jobs.ts +67 -8
  87. package/src/tools/index.ts +3 -0
  88. package/src/tools/report-tool-issue.ts +79 -28
  89. package/src/web/search/providers/firecrawl.ts +46 -13
  90. package/src/web/search/types.ts +5 -1
@@ -78,6 +78,23 @@ export declare class ModelRegistry {
78
78
  */
79
79
  refresh(strategy?: ModelRefreshStrategy): Promise<void>;
80
80
  refreshInBackground(strategy?: ModelRefreshStrategy): void;
81
+ /**
82
+ * Wait for any in-flight background model discovery to settle.
83
+ *
84
+ * Background discovery started by {@link refreshInBackground} is
85
+ * fire-and-forget; RPC consumers (e.g. `get_available_models`,
86
+ * `set_model`) and deferred `--model` resolution that read the registry
87
+ * immediately after session creation can otherwise observe a partial
88
+ * catalog before discovery-backed providers have populated `#models`.
89
+ * Awaiting the tracked promise ensures the response reflects every
90
+ * configured provider once the initial background refresh resolves.
91
+ *
92
+ * No-op when no refresh is in flight (`#backgroundRefresh` cleared in the
93
+ * `finally` of `refreshInBackground` on completion). Resolves immediately
94
+ * in that case so already-warm sessions are unaffected. Discovery errors
95
+ * remain swallowed by `refreshInBackground`'s existing `.catch`.
96
+ */
97
+ awaitBackgroundRefresh(): Promise<void>;
81
98
  refreshProvider(providerId: string, strategy?: ModelRefreshStrategy): Promise<void>;
82
99
  /**
83
100
  * Refresh dynamic metadata that can appear only after a local model loads.
@@ -132,6 +149,16 @@ export declare class ModelRegistry {
132
149
  */
133
150
  hasCommandBackedApiKey(provider: string): boolean;
134
151
  getDiscoverableProviders(): string[];
152
+ /**
153
+ * Whether `providerId` is known to the registry: it has at least one live
154
+ * model, or it is configured for dynamic discovery (models.yml `discovery:`
155
+ * or a runtime extension provider) and is not disabled. Discovery-only
156
+ * providers can hold zero models at startup — cached rows never persist
157
+ * live auth headers (#5780), so a provider whose discovered models all
158
+ * carry config headers (`authHeader: true`) only materializes models after
159
+ * the online refresh completes.
160
+ */
161
+ hasProvider(providerId: string): boolean;
135
162
  getProviderDiscoveryState(provider: string): ProviderDiscoveryState | undefined;
136
163
  /**
137
164
  * Find a model by provider and ID.
@@ -62,6 +62,8 @@ interface UiNumber extends UiBase {
62
62
  options?: ReadonlyArray<SubmenuOption>;
63
63
  }
64
64
  interface UiString extends UiBase {
65
+ /** Mask the value in both the settings row and text editor. */
66
+ secret?: boolean;
65
67
  /**
66
68
  * Submenu options.
67
69
  * - Array → submenu with these choices.
@@ -73,6 +75,7 @@ interface UiString extends UiBase {
73
75
  /** Wide ui shape exposed to consumers that walk the schema generically. */
74
76
  export type AnyUiMetadata = UiBase & {
75
77
  options?: ReadonlyArray<SubmenuOption> | "runtime";
78
+ secret?: boolean;
76
79
  };
77
80
  interface BooleanDef {
78
81
  type: "boolean";
@@ -2839,6 +2842,14 @@ export declare const SETTINGS_SCHEMA: {
2839
2842
  readonly "hindsight.apiToken": {
2840
2843
  readonly type: "string";
2841
2844
  readonly default: undefined;
2845
+ readonly ui: {
2846
+ readonly tab: "memory";
2847
+ readonly group: "Hindsight";
2848
+ readonly label: "Hindsight API Token";
2849
+ readonly description: "Bearer token for authenticated Hindsight servers";
2850
+ readonly condition: "hindsightActive";
2851
+ readonly secret: true;
2852
+ };
2842
2853
  };
2843
2854
  readonly "hindsight.bankId": {
2844
2855
  readonly type: "string";
@@ -4050,6 +4061,16 @@ export declare const SETTINGS_SCHEMA: {
4050
4061
  readonly description: "Load .mcp.json/mcp.json from project root";
4051
4062
  };
4052
4063
  };
4064
+ readonly "mcp.renderMarkdownResults": {
4065
+ readonly type: "boolean";
4066
+ readonly default: true;
4067
+ readonly ui: {
4068
+ readonly tab: "tools";
4069
+ readonly group: "Discovery & MCP";
4070
+ readonly label: "MCP Markdown Results";
4071
+ readonly description: "Render non-JSON MCP text results as Markdown in the transcript";
4072
+ };
4073
+ };
4053
4074
  readonly "mcp.notifications": {
4054
4075
  readonly type: "boolean";
4055
4076
  readonly default: false;
@@ -4183,6 +4204,16 @@ export declare const SETTINGS_SCHEMA: {
4183
4204
  }];
4184
4205
  };
4185
4206
  };
4207
+ readonly "task.isolation.apply": {
4208
+ readonly type: "boolean";
4209
+ readonly default: true;
4210
+ readonly ui: {
4211
+ readonly tab: "tasks";
4212
+ readonly group: "Isolation";
4213
+ readonly label: "Apply Isolated Changes";
4214
+ readonly description: "Automatically apply successful isolated task changes to the parent checkout; disable to retain patch or branch artifacts";
4215
+ };
4216
+ };
4186
4217
  readonly "task.isolation.merge": {
4187
4218
  readonly type: "enum";
4188
4219
  readonly values: readonly ["patch", "branch"];
@@ -4648,7 +4679,7 @@ export declare const SETTINGS_SCHEMA: {
4648
4679
  }, {
4649
4680
  readonly value: "firecrawl";
4650
4681
  readonly label: "Firecrawl";
4651
- readonly description: "Requires FIRECRAWL_API_KEY";
4682
+ readonly description: "Uses Firecrawl API when FIRECRAWL_API_KEY is set; falls back to keyless mode";
4652
4683
  }, {
4653
4684
  readonly value: "brave";
4654
4685
  readonly label: "Brave";
@@ -5486,12 +5517,12 @@ export declare const SETTINGS_SCHEMA: {
5486
5517
  };
5487
5518
  readonly "dev.autoqa": {
5488
5519
  readonly type: "boolean";
5489
- readonly default: false;
5520
+ readonly default: true;
5490
5521
  readonly ui: {
5491
5522
  readonly tab: "tools";
5492
5523
  readonly group: "Developer";
5493
5524
  readonly label: "Auto QA";
5494
- readonly description: "Enable automated tool issue reporting (report_tool_issue) for all agents";
5525
+ readonly description: "Automated tool issue reporting (xd://report_issue). On by default; the first report asks for consent, and denying it disables reporting until re-enabled explicitly";
5495
5526
  };
5496
5527
  };
5497
5528
  readonly "dev.autoqaPush.endpoint": {
@@ -371,4 +371,5 @@ export { getProjectDir } from "@oh-my-pi/pi-utils";
371
371
  export declare function getPackageDir(): string;
372
372
  export * from "../index.js";
373
373
  export { formatBytes as formatSize } from "../tools/render-utils.js";
374
+ export { copyToClipboard } from "../utils/clipboard.js";
374
375
  export { Type } from "./typebox.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Compatibility shim for legacy extensions importing the package root of
3
+ * `@earendil-works/pi-tui` or `@mariozechner/pi-tui`.
4
+ *
5
+ * The historical root exported `decodeKittyPrintable`; the canonical TUI now
6
+ * exposes the equivalent, broader `decodePrintableKey` helper. Keep the legacy
7
+ * name available without reintroducing it into the canonical package surface.
8
+ */
9
+ export * from "@oh-my-pi/pi-tui";
10
+ export { decodePrintableKey as decodeKittyPrintable } from "@oh-my-pi/pi-tui";
@@ -57,7 +57,7 @@ export declare function __validateLegacyPiPackageRootOverrides(candidates: Recor
57
57
  */
58
58
  export declare function __buildLegacyPiPackageRootOverrides(isCompiled: boolean, bundledModuleKeys?: Iterable<string>): Record<string, string>;
59
59
  /** Test seam for compiled-binary legacy extension source rewriting. */
60
- export declare function __rewriteLegacyExtensionSourceForTests(source: string, importerPath: string, mtimeTag?: string | null): Promise<string>;
60
+ export declare function __rewriteLegacyExtensionSourceForTests(source: string, importerPath: string, mtimeTag?: string | null, resolvedImportMtimeTag?: string | null): Promise<string>;
61
61
  /**
62
62
  * Load a legacy Pi extension module from its real on-disk location.
63
63
  *
@@ -31,3 +31,14 @@ export declare function artifactsDirsFromRegistry(): string[];
31
31
  * multiple dirs, the first hit wins (registry dirs are scanned first).
32
32
  */
33
33
  export declare function sessionFilesFromDisk(): Promise<Map<string, string>>;
34
+ /**
35
+ * Availability half of the `history://` resolution semantics: true when a
36
+ * transcript for `agentId` can be served from a registered ref's live session
37
+ * or retained session file, or from an on-disk `.jsonl` under a known
38
+ * artifacts dir. Hint surfaces use this so they only advertise
39
+ * `history://<agentId>` links that `HistoryProtocolHandler` can actually
40
+ * resolve. A retained sessionFile path is verified on disk before it counts,
41
+ * and probing never throws: a stale path or unreadable artifacts subtree
42
+ * reads as unavailable instead of disturbing the caller's delivery path.
43
+ */
44
+ export declare function hasResolvableTranscript(agentId: string): Promise<boolean>;
@@ -30,6 +30,8 @@ export declare const WARMUP_TIMEOUT_MS = 5000;
30
30
  * timeout/cancel instead of falling back to the internal 30s default.
31
31
  */
32
32
  export declare function getOrCreateClient(config: ServerConfig, cwd: string, initTimeoutMs?: number, signal?: AbortSignal): Promise<LspClient>;
33
+ /** Return an active or already-starting client without starting a language server. */
34
+ export declare function getActiveOrPendingClient(config: ServerConfig, cwd: string, signal?: AbortSignal): Promise<LspClient | undefined>;
33
35
  /**
34
36
  * Ensure a file is opened in the LSP client.
35
37
  * Sends didOpen notification if the file is not already tracked.
@@ -5,7 +5,7 @@ import { type AuthStorage } from "../session/auth-storage.js";
5
5
  import type { McpConnectionStatusEvent } from "./startup-events.js";
6
6
  import type { MCPToolDetails } from "./tool-bridge.js";
7
7
  import type { MCPToolCache } from "./tool-cache.js";
8
- import type { MCPGetPromptResult, MCPPrompt, MCPRequestOptions, MCPResource, MCPResourceReadResult, MCPResourceTemplate, MCPServerConfig, MCPServerConnection } from "./types.js";
8
+ import type { MCPAuthChallenge, MCPGetPromptResult, MCPPrompt, MCPRequestOptions, MCPResource, MCPResourceReadResult, MCPResourceTemplate, MCPServerConfig, MCPServerConnection } from "./types.js";
9
9
  /**
10
10
  * Stable, total ordering on MCP tools by name.
11
11
  *
@@ -42,6 +42,8 @@ export interface MCPDiscoverOptions {
42
42
  /** Called when MCP server connection state changes. */
43
43
  onStatus?: (event: McpConnectionStatusEvent) => void;
44
44
  }
45
+ /** Handles an MCP `WWW-Authenticate` challenge and returns refreshed config. */
46
+ export type MCPAuthHandler = (serverName: string, challenge: MCPAuthChallenge) => Promise<MCPServerConfig | undefined>;
45
47
  /**
46
48
  * MCP Server Manager.
47
49
  *
@@ -79,6 +81,8 @@ export declare class MCPManager {
79
81
  * Set the auth storage for resolving OAuth credentials.
80
82
  */
81
83
  setAuthStorage(authStorage: AuthStorage): void;
84
+ /** Set the callback used to complete OAuth after a tool-level auth challenge. */
85
+ setAuthHandler(handler: MCPAuthHandler | undefined): void;
82
86
  /**
83
87
  * Discover and connect to all MCP servers from .mcp.json files.
84
88
  * Returns tools and any connection errors.
@@ -149,7 +153,6 @@ export declare class MCPManager {
149
153
  * the same server share one reconnection attempt. Returns the new
150
154
  * connection, or `null` if reconnection failed or the per-server crash
151
155
  * burst limit (see {@link RECONNECT_BURST_LIMIT}) is exceeded.
152
- *
153
156
  * @param options.manual - When `true`, resets the crash-burst window so a
154
157
  * user-driven retry (e.g. `/mcp reconnect`) is never blocked by an
155
158
  * earlier storm. Defaults to `false`; the transport `onClose` callback
@@ -157,6 +160,7 @@ export declare class MCPManager {
157
160
  */
158
161
  reconnectServer(name: string, options?: {
159
162
  manual?: boolean;
163
+ authChallenge?: MCPAuthChallenge;
160
164
  }): Promise<MCPServerConnection | null>;
161
165
  /**
162
166
  * Refresh tools from a specific server.
@@ -4,9 +4,9 @@
4
4
  * Provides structured display of MCP tool calls and results,
5
5
  * showing args and output in JSON tree format similar to task tool.
6
6
  */
7
- import type { Component } from "@oh-my-pi/pi-tui";
7
+ import { type Component } from "@oh-my-pi/pi-tui";
8
8
  import type { RenderResultOptions } from "../extensibility/custom-tools/types.js";
9
- import type { Theme } from "../modes/theme/theme.js";
9
+ import { type Theme } from "../modes/theme/theme.js";
10
10
  import type { MCPToolDetails } from "./tool-bridge.js";
11
11
  /**
12
12
  * Render MCP tool call.
@@ -9,9 +9,11 @@ import type { SourceMeta } from "../capability/types.js";
9
9
  import type { CustomTool, CustomToolContext, CustomToolResult, RenderResultOptions } from "../extensibility/custom-tools/types.js";
10
10
  import type { Theme } from "../modes/theme/theme.js";
11
11
  import type { OutputMeta } from "../tools/output-meta.js";
12
- import type { MCPContent, MCPServerConnection, MCPToolDefinition } from "./types.js";
13
- /** Reconnect callback: tears down stale connection, returns new one or null. */
14
- export type MCPReconnect = () => Promise<MCPServerConnection | null>;
12
+ import type { MCPAuthChallenge, MCPContent, MCPServerConnection, MCPToolDefinition } from "./types.js";
13
+ /** Reconnect callback: tears down a stale connection, optionally authorizing first. */
14
+ export type MCPReconnect = (options?: {
15
+ authChallenge?: MCPAuthChallenge;
16
+ }) => Promise<MCPServerConnection | null>;
15
17
  export declare function isRetriableConnectionError(error: unknown): boolean;
16
18
  /** Details included in MCP tool results for rendering */
17
19
  export interface MCPToolDetails {
@@ -23,6 +25,8 @@ export interface MCPToolDetails {
23
25
  isError?: boolean;
24
26
  /** Raw content from MCP response */
25
27
  rawContent?: MCPContent[];
28
+ /** Structured metadata from the MCP response */
29
+ mcpMeta?: Record<string, unknown>;
26
30
  /** Provider ID (e.g., "claude", "mcp-json") */
27
31
  provider?: string;
28
32
  /** Provider display name (e.g., "Claude Code", "MCP Config") */
@@ -175,10 +175,16 @@ export interface MCPResourceContent {
175
175
  };
176
176
  }
177
177
  export type MCPContent = MCPTextContent | MCPImageContent | MCPResourceContent;
178
+ /** Structured authentication challenge returned in a tool result. */
179
+ export interface MCPAuthChallenge {
180
+ /** Values from `_meta["mcp/www_authenticate"]`. */
181
+ readonly wwwAuthenticate: readonly string[];
182
+ }
178
183
  /** tools/call response */
179
184
  export interface MCPToolCallResult {
180
185
  content: MCPContent[];
181
186
  isError?: boolean;
187
+ _meta?: Record<string, unknown>;
182
188
  }
183
189
  export interface MCPRequestOptions {
184
190
  /** Abort signal (e.g. Escape-to-interrupt) */
@@ -10,6 +10,14 @@ export declare class OAuthSelectorComponent extends Container {
10
10
  requestRender?: () => void;
11
11
  });
12
12
  stopValidation(): void;
13
+ /**
14
+ * Fit the selector into `lines` rendered rows by shrinking the visible list
15
+ * window (the window is centered on the selection, so the selected row is
16
+ * always visible at any height). Prefers keeping the full chrome — borders,
17
+ * spacers, title, search status — but sacrifices the trailing spacer/border
18
+ * (clipped by the host) before dropping below three visible rows.
19
+ */
20
+ setMaxHeight(lines: number): void;
13
21
  handleInput(keyData: string): void;
14
22
  /** Move the selection one step for a wheel notch (clamped, no wrap). */
15
23
  handleWheel(delta: -1 | 1): void;
@@ -40,6 +40,7 @@ export interface SubmenuSettingDef extends BaseSettingDef {
40
40
  }
41
41
  export interface TextInputSettingDef extends BaseSettingDef {
42
42
  type: "text";
43
+ secret: boolean;
43
44
  }
44
45
  export interface ProviderLimitsSettingDef extends BaseSettingDef {
45
46
  type: "providerLimits";
@@ -1,4 +1,5 @@
1
1
  import { type Component } from "@oh-my-pi/pi-tui";
2
+ import type { MCPAuthChallenge, MCPServerConfig } from "../../mcp/types.js";
2
3
  import type { InteractiveModeContext } from "../types.js";
3
4
  /**
4
5
  * Renders the MCP OAuth fallback URL. Always shows the full authorization URL
@@ -41,4 +42,6 @@ export declare class MCPCommandController {
41
42
  * Handle /mcp command and route to subcommands
42
43
  */
43
44
  handle(text: string): Promise<void>;
45
+ /** Reauthorize a server after a tool-level OAuth challenge. */
46
+ handleMCPAuthChallenge(name: string, challenge: MCPAuthChallenge): Promise<MCPServerConfig | undefined>;
44
47
  }
@@ -8,6 +8,7 @@ import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
8
8
  import type { ImageContent, Model } from "@oh-my-pi/pi-ai";
9
9
  import type { BashResult } from "../../exec/bash-executor.js";
10
10
  import type { AgentSessionEvent, SessionStats } from "../../session/agent-session.js";
11
+ import { type RpcMessagesPage, type RpcMessagesPageOptions } from "./rpc-messages.js";
11
12
  import type { RpcAvailableSlashCommand, RpcHandoffResult, RpcHostToolDefinition, RpcSessionState, RpcSubagentEventFrame, RpcSubagentLifecycleFrame, RpcSubagentMessagesResult, RpcSubagentProgressFrame, RpcSubagentSnapshot, RpcSubagentSubscriptionLevel } from "./rpc-types.js";
12
13
  export interface RpcClientOptions {
13
14
  /** Path to the CLI entry point (default: searches for dist/cli.js) */
@@ -45,6 +46,12 @@ export interface RpcClientCustomTool<TParams extends Record<string, unknown> = R
45
46
  execute(params: TParams, context: RpcClientToolContext<TDetails>): Promise<RpcClientToolResult<TDetails>> | RpcClientToolResult<TDetails>;
46
47
  }
47
48
  export declare function defineRpcClientTool<TParams extends Record<string, unknown> = Record<string, unknown>, TDetails = unknown>(tool: RpcClientCustomTool<TParams, TDetails>): RpcClientCustomTool<TParams, TDetails>;
49
+ /** Failed RPC command; `code` mirrors the server's machine-readable error code when present. */
50
+ export declare class RpcCommandError extends Error {
51
+ readonly command: string;
52
+ readonly code?: string | undefined;
53
+ constructor(message: string, command: string, code?: string | undefined);
54
+ }
48
55
  export declare class RpcClient {
49
56
  #private;
50
57
  private options;
@@ -254,8 +261,10 @@ export declare class RpcClient {
254
261
  */
255
262
  getLastAssistantText(): Promise<string | null>;
256
263
  /**
257
- * Get all messages in the session.
264
+ * Get one stable, byte-bounded message page.
258
265
  */
266
+ getMessagesPage(options?: RpcMessagesPageOptions): Promise<RpcMessagesPage>;
267
+ /** Get all messages, draining stable pages when protocol v2 is available. */
259
268
  getMessages(): Promise<AgentMessage[]>;
260
269
  /**
261
270
  * Get list of OAuth providers available for login, with their current authentication status.
@@ -1,9 +1,25 @@
1
1
  /** Maximum UTF-8 size of one newline-delimited RPC frame, including the newline. */
2
2
  export declare const MAX_RPC_FRAME_BYTES: number;
3
+ /** Maximum UTF-8 size of one logical frame reassembled by protocol v2. */
4
+ export declare const MAX_RPC_REASSEMBLED_BYTES: number;
5
+ export type RpcProtocolVersion = 1 | 2;
6
+ /** Reassemble protocol v2 chunk frames after each JSONL line has been parsed. */
7
+ export declare class RpcFrameDecoder {
8
+ #private;
9
+ push(value: unknown): object | undefined;
10
+ }
3
11
  /** Serialize a complete JSONL frame while enforcing the transport byte ceiling. */
4
12
  export declare function encodeRpcFrame(frame: object, streamedMessageCount?: number, streamedMessages?: readonly unknown[]): string;
5
13
  /** Stateful encoder that tracks which messages a client has already received. */
6
14
  export declare class RpcFrameEncoder {
7
15
  #private;
16
+ setProtocolVersion(version: number): void;
17
+ /**
18
+ * Encode one logical frame into physical JSONL lines. Encoder bookkeeping runs
19
+ * eagerly; only chunk emission is lazy, so a chunked result can be streamed to
20
+ * stdout with backpressure without holding the whole transport in memory. The
21
+ * returned iterable MUST be fully consumed exactly once.
22
+ */
23
+ encodeFrames(frame: object): Iterable<string>;
8
24
  encode(frame: object): string;
9
25
  }
@@ -0,0 +1,26 @@
1
+ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
+ export declare const RPC_MESSAGES_PAGE_BUSY_ERROR = "Cannot page messages while the session is changing";
3
+ export declare const RPC_MESSAGES_PAGE_STALE_ERROR = "RPC message cursor is stale";
4
+ /** Machine-readable reasons a `get_messages_page` request can fail; carried as `code` on the error response. */
5
+ export type RpcMessagesPageErrorCode = "session_busy" | "stale_cursor";
6
+ /** Paging failure that maps to a structured wire `code`, so clients can react without matching message text. */
7
+ export declare class RpcMessagesPageError extends Error {
8
+ readonly code: RpcMessagesPageErrorCode;
9
+ constructor(message: string, code: RpcMessagesPageErrorCode);
10
+ }
11
+ export interface RpcMessageSnapshot {
12
+ sessionId: string;
13
+ leafId: string | null;
14
+ messageCount: number;
15
+ }
16
+ export interface RpcMessagesPage {
17
+ messages: AgentMessage[];
18
+ nextCursor?: string;
19
+ totalMessages: number;
20
+ }
21
+ export interface RpcMessagesPageOptions {
22
+ cursor?: string;
23
+ limit?: number;
24
+ }
25
+ /** Page one stable in-memory message snapshot without crossing the v1 frame budget. */
26
+ export declare function pageRpcMessages(messages: readonly AgentMessage[], snapshot: RpcMessageSnapshot, options?: RpcMessagesPageOptions): RpcMessagesPage;
@@ -14,7 +14,12 @@ import type { FileEntry } from "../../session/session-entries.js";
14
14
  import type { AvailableSlashCommandSource } from "../../slash-commands/available-commands.js";
15
15
  import type { AgentProgress, SubagentEventPayload, SubagentLifecyclePayload, SubagentProgressPayload } from "../../task/index.js";
16
16
  import type { TodoPhase } from "../../tools/todo.js";
17
+ import type { RpcMessagesPage } from "./rpc-messages.js";
17
18
  export type RpcCommand = {
19
+ id?: string;
20
+ type: "negotiate_protocol";
21
+ protocolVersion: number;
22
+ } | {
18
23
  id?: string;
19
24
  type: "prompt";
20
25
  message: string;
@@ -157,6 +162,11 @@ export type RpcCommand = {
157
162
  } | {
158
163
  id?: string;
159
164
  type: "get_messages";
165
+ } | {
166
+ id?: string;
167
+ type: "get_messages_page";
168
+ cursor?: string;
169
+ limit?: number;
160
170
  } | {
161
171
  id?: string;
162
172
  type: "get_login_providers";
@@ -214,6 +224,21 @@ export interface RpcPromptResultFrame {
214
224
  id?: string;
215
225
  agentInvoked: boolean;
216
226
  }
227
+ export interface RpcReadyFrame {
228
+ type: "ready";
229
+ protocolVersion: 1;
230
+ supportedProtocolVersions: [1, 2];
231
+ maxFrameBytes: number;
232
+ maxReassembledFrameBytes: number;
233
+ }
234
+ export interface RpcChunkFrame {
235
+ type: "rpc_chunk";
236
+ chunkId: string;
237
+ index: number;
238
+ count: number;
239
+ byteLength: number;
240
+ data: string;
241
+ }
217
242
  export interface RpcHandoffResult {
218
243
  savedPath?: string;
219
244
  }
@@ -241,6 +266,14 @@ export interface RpcSubagentMessagesResult {
241
266
  messages: AgentMessage[];
242
267
  }
243
268
  export type RpcResponse = {
269
+ id?: string;
270
+ type: "response";
271
+ command: "negotiate_protocol";
272
+ success: true;
273
+ data: {
274
+ protocolVersion: 2;
275
+ };
276
+ } | {
244
277
  id?: string;
245
278
  type: "response";
246
279
  command: "prompt";
@@ -489,6 +522,12 @@ export type RpcResponse = {
489
522
  data: {
490
523
  messages: AgentMessage[];
491
524
  };
525
+ } | {
526
+ id?: string;
527
+ type: "response";
528
+ command: "get_messages_page";
529
+ success: true;
530
+ data: RpcMessagesPage;
492
531
  } | {
493
532
  id?: string;
494
533
  type: "response";
@@ -516,6 +555,7 @@ export type RpcResponse = {
516
555
  command: string;
517
556
  success: false;
518
557
  error: string;
558
+ code?: string;
519
559
  };
520
560
  export interface RpcSubagentLifecycleFrame {
521
561
  type: "subagent_lifecycle";
@@ -18,5 +18,5 @@ export declare class SignInTab implements SetupTab {
18
18
  handleInput(data: string): void;
19
19
  /** Forward mouse to the provider selector; pointer is inert during an active login or code prompt. */
20
20
  routeMouse(event: SgrMouseEvent, line: number, col: number): void;
21
- render(width: number): readonly string[];
21
+ render(width: number, maxLines?: number): readonly string[];
22
22
  }
@@ -14,6 +14,13 @@ export interface SetupSceneController extends Component {
14
14
  onMount?(): void | Promise<void>;
15
15
  onUnmount?(): void;
16
16
  dispose?(): void;
17
+ /**
18
+ * Render the scene body. `maxLines` is the number of body rows the wizard
19
+ * will actually display (header and footer already subtracted); scenes
20
+ * shrink list windows and drop decorative chrome so the selected row stays
21
+ * inside the budget. Overflow beyond `maxLines` is clipped by the wizard.
22
+ */
23
+ render(width: number, maxLines?: number): readonly string[];
17
24
  /**
18
25
  * Route an SGR mouse report (tracking is on while the wizard holds the
19
26
  * alternate screen). `line`/`col` are 0-based within this controller's
@@ -34,7 +41,8 @@ export interface SetupTab {
34
41
  * login). The parent scene MUST NOT switch tabs or finish while modal.
35
42
  */
36
43
  readonly modal: boolean;
37
- render(width: number): readonly string[];
44
+ /** See {@link SetupSceneController.render}: `maxLines` is the tab-local row budget. */
45
+ render(width: number, maxLines?: number): readonly string[];
38
46
  handleInput(data: string): void;
39
47
  invalidate(): void;
40
48
  /** Called when the tab becomes active (including initial mount). */
@@ -19,5 +19,5 @@ export declare class WebSearchTab implements SetupTab {
19
19
  routeMouse(event: SgrMouseEvent, line: number, _col: number): void;
20
20
  invalidate(): void;
21
21
  dispose(): void;
22
- render(width: number): readonly string[];
22
+ render(width: number, maxLines?: number): readonly string[];
23
23
  }
@@ -666,6 +666,8 @@ export declare class AgentSession {
666
666
  get state(): AgentState;
667
667
  /** Current model (may be undefined if not yet selected) */
668
668
  get model(): Model | undefined;
669
+ /** Resolved selector while retry routing is using a fallback model. */
670
+ get retryFallbackModel(): string | undefined;
669
671
  /** Effective thinking level applied to the agent (the resolved level when `auto`). */
670
672
  get thinkingLevel(): ThinkingLevel | undefined;
671
673
  /** The selector the user configured: `auto` when auto mode is active, else the effective level. */
@@ -1235,7 +1237,8 @@ export declare class AgentSession {
1235
1237
  setAutoRetryEnabled(enabled: boolean): void;
1236
1238
  /**
1237
1239
  * Manually retry the last failed assistant turn.
1238
- * Removes the error message from agent state and re-attempts with a fresh retry budget.
1240
+ * Removes the error message from active agent state when present and
1241
+ * re-attempts with a fresh retry budget.
1239
1242
  *
1240
1243
  * A stream that stalls or aborts mid-tool-call ends the turn with
1241
1244
  * `stopReason: "error" | "aborted"` and then appends one synthetic
@@ -1246,6 +1249,10 @@ export declare class AgentSession {
1246
1249
  * checking the assistant message; it strips both the placeholders and the
1247
1250
  * failed turn before re-attempting.
1248
1251
  *
1252
+ * A restored session deliberately omits failed assistant turns from provider
1253
+ * context. In that case, the persisted display transcript remains the source
1254
+ * of truth for whether the current branch has a retryable failed tail.
1255
+ *
1249
1256
  * @returns true if retry was initiated, false if no failed turn to retry or agent is busy
1250
1257
  */
1251
1258
  retry(): Promise<boolean>;
@@ -21,9 +21,11 @@ export declare function parseSessionEntries(content: string): FileEntry[];
21
21
  export declare function loadEntriesFromFile(filePath: string, storage?: SessionStorage): Promise<FileEntry[]>;
22
22
  export declare function resolveBlobRefsInEntries(entries: FileEntry[], blobStore: BlobStore): Promise<void>;
23
23
  /**
24
- * Read-only message view of a session file: load entries, migrate to the
25
- * current version, resolve blob refs, and build the context along the
26
- * persisted leaf path (last entry). Does NOT create a writer or take the
27
- * session lock safe to call against a file another session is writing.
24
+ * Read-only transcript view of a session file: load entries, migrate to the
25
+ * current version, resolve blob refs, and build the display transcript along
26
+ * the persisted leaf path (last entry). Uses transcript mode (collapsed to the
27
+ * latest compaction) so failed/aborted tail turns stay visible, unlike the
28
+ * provider-context builder which drops them. Does NOT create a writer or take
29
+ * the session lock — safe to call against a file another session is writing.
28
30
  */
29
31
  export declare function loadSessionMessagesReadOnly(filePath: string): Promise<AgentMessage[]>;