@oh-my-pi/pi-coding-agent 17.3.7 → 17.3.8

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 (109) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/{CHANGELOG-1hmwwt45.md → CHANGELOG-vr9cckb4.md} +60 -0
  3. package/dist/cli.js +2993 -3001
  4. package/dist/docs-index.generated.txt +1 -1
  5. package/dist/{tool-views.generated-jdfmzwmn.js → tool-views.generated-dd2km5r2.js} +19 -19
  6. package/dist/types/advisor/advise-tool.d.ts +4 -2
  7. package/dist/types/cli/auth-broker-cli.d.ts +15 -0
  8. package/dist/types/cli/update-cli.d.ts +8 -0
  9. package/dist/types/cli-commands.d.ts +10 -2
  10. package/dist/types/config/settings-schema.d.ts +28 -0
  11. package/dist/types/config/settings.d.ts +9 -0
  12. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  13. package/dist/types/launch/presence.d.ts +4 -1
  14. package/dist/types/mcp/oauth-credentials.d.ts +23 -0
  15. package/dist/types/mcp/oauth-flow.d.ts +11 -0
  16. package/dist/types/mnemopi/backend.d.ts +12 -0
  17. package/dist/types/modes/components/tool-execution.d.ts +12 -0
  18. package/dist/types/modes/controllers/input-controller.d.ts +2 -0
  19. package/dist/types/modes/interactive-mode.d.ts +25 -3
  20. package/dist/types/modes/types.d.ts +10 -0
  21. package/dist/types/session/agent-session.d.ts +3 -0
  22. package/dist/types/session/prewalk.d.ts +4 -0
  23. package/dist/types/session/session-entries.d.ts +0 -1
  24. package/dist/types/session/session-manager.d.ts +12 -0
  25. package/dist/types/session/session-stats.d.ts +13 -1
  26. package/dist/types/session/skill-title-input.d.ts +13 -0
  27. package/dist/types/subprocess/worker-client.d.ts +7 -4
  28. package/dist/types/task/label.d.ts +2 -0
  29. package/dist/types/task/render.d.ts +2 -0
  30. package/dist/types/tiny/completion-prompt.d.ts +2 -0
  31. package/dist/types/tiny/title-client.d.ts +6 -4
  32. package/dist/types/tiny/title-protocol.d.ts +1 -0
  33. package/dist/types/tiny/worker.d.ts +27 -0
  34. package/dist/types/tools/bash.d.ts +1 -1
  35. package/dist/types/tools/read-format.d.ts +6 -0
  36. package/dist/types/tools/read-summary.d.ts +7 -1
  37. package/dist/types/utils/block-context.d.ts +14 -0
  38. package/dist/types/utils/fetch-timeout.d.ts +15 -0
  39. package/dist/types/utils/git.d.ts +25 -1
  40. package/dist/types/web/search/providers/tinyfish.d.ts +4 -0
  41. package/package.json +13 -13
  42. package/src/advisor/advise-tool.ts +5 -3
  43. package/src/cli/auth-broker-cli.ts +36 -1
  44. package/src/cli/profile-bootstrap.ts +2 -6
  45. package/src/cli/update-cli.ts +63 -11
  46. package/src/cli-commands.ts +61 -7
  47. package/src/commands/completions.ts +2 -1
  48. package/src/commit/agentic/index.ts +15 -2
  49. package/src/commit/git/diff.ts +6 -2
  50. package/src/config/model-resolver.ts +52 -6
  51. package/src/config/models-config.ts +2 -2
  52. package/src/config/settings-schema.ts +33 -0
  53. package/src/config/settings.ts +159 -30
  54. package/src/discovery/helpers.ts +45 -2
  55. package/src/discovery/omp-plugins.ts +2 -1
  56. package/src/discovery/opencode.ts +56 -3
  57. package/src/eval/js/process-entry.ts +4 -4
  58. package/src/export/html/tool-views.generated.js +19 -19
  59. package/src/extensibility/extensions/runner.ts +3 -2
  60. package/src/extensibility/plugins/legacy-pi-compat.ts +47 -0
  61. package/src/launch/client.ts +9 -4
  62. package/src/launch/presence.ts +19 -4
  63. package/src/lsp/defaults.json +1 -1
  64. package/src/mcp/manager.ts +41 -20
  65. package/src/mcp/oauth-credentials.ts +38 -0
  66. package/src/mcp/oauth-flow.ts +21 -0
  67. package/src/mcp/tool-bridge.ts +32 -16
  68. package/src/mnemopi/backend.ts +35 -3
  69. package/src/modes/components/model-hub.ts +37 -4
  70. package/src/modes/components/settings-selector.ts +17 -11
  71. package/src/modes/components/tool-execution.ts +97 -29
  72. package/src/modes/components/tree-selector.ts +7 -2
  73. package/src/modes/controllers/event-controller.ts +12 -2
  74. package/src/modes/controllers/input-controller.ts +64 -27
  75. package/src/modes/controllers/mcp-command-controller.ts +13 -4
  76. package/src/modes/interactive-mode.ts +79 -11
  77. package/src/modes/types.ts +11 -0
  78. package/src/prompts/system/memory-extraction-system.md +5 -22
  79. package/src/prompts/system/system-prompt.md +1 -1
  80. package/src/session/agent-session.ts +52 -6
  81. package/src/session/messages.ts +6 -0
  82. package/src/session/prewalk.ts +25 -7
  83. package/src/session/session-entries.ts +0 -1
  84. package/src/session/session-maintenance.ts +10 -1
  85. package/src/session/session-manager.ts +15 -0
  86. package/src/session/session-stats.ts +24 -3
  87. package/src/session/settings-stream-fn.ts +7 -0
  88. package/src/session/skill-title-input.ts +32 -0
  89. package/src/session/turn-recovery.ts +23 -18
  90. package/src/subprocess/worker-client.ts +8 -5
  91. package/src/task/executor.ts +11 -0
  92. package/src/task/index.ts +2 -0
  93. package/src/task/label.ts +14 -1
  94. package/src/task/persisted-revive.ts +13 -0
  95. package/src/task/render.ts +1 -1
  96. package/src/task/structured-subagent.ts +5 -2
  97. package/src/tiny/completion-prompt.ts +16 -0
  98. package/src/tiny/title-client.ts +15 -6
  99. package/src/tiny/title-protocol.ts +8 -1
  100. package/src/tiny/worker.ts +21 -19
  101. package/src/tools/bash.ts +7 -1
  102. package/src/tools/read-format.ts +16 -2
  103. package/src/tools/read-summary.ts +9 -4
  104. package/src/tools/read.ts +306 -72
  105. package/src/utils/block-context.ts +15 -1
  106. package/src/utils/fetch-timeout.ts +33 -0
  107. package/src/utils/git.ts +54 -11
  108. package/src/web/search/providers/browser-page.ts +21 -3
  109. package/src/web/search/providers/tinyfish.ts +26 -0
@@ -70,8 +70,10 @@ export declare function isAdvisorInterruptImmuneTurnActive(opts: {
70
70
  * auto-resume anything, so it is delivered live. Parking it during an active
71
71
  * run instead strands it (it never reaches the running agent) and the withheld
72
72
  * notes dump as one burst at the next user prompt — the bug this guards.
73
- * - During the post-interrupt immune-turn window, further `concern`/`blocker`
74
- * notes are downgraded to asides; preservation still wins.
73
+ * - During the post-interrupt immune-turn window, further `concern` notes are
74
+ * downgraded to asides; preservation still wins. A `blocker` is exempt: it
75
+ * means the agent handed off broken or unexercised work, so it still steers a
76
+ * triggered turn even right after a prior interrupt (#5628).
75
77
  */
76
78
  export declare function resolveAdvisorDeliveryChannel(opts: {
77
79
  severity: AdvisorSeverity | undefined;
@@ -1,3 +1,5 @@
1
+ import { type OAuthCredential } from "@oh-my-pi/pi-ai";
2
+ import type { OAuthCredentials } from "@oh-my-pi/pi-ai/oauth/types";
1
3
  export type AuthBrokerAction = "serve" | "token" | "login" | "logout" | "status" | "import" | "migrate" | "list";
2
4
  export interface AuthBrokerCommandArgs {
3
5
  action: AuthBrokerAction;
@@ -21,5 +23,18 @@ export interface AuthBrokerCommandArgs {
21
23
  };
22
24
  }
23
25
  declare const ACTIONS: readonly AuthBrokerAction[];
26
+ /**
27
+ * OAuth refresh handler for `omp auth-broker serve`'s {@link AuthStorage}.
28
+ *
29
+ * The vault holds provider OAuth rows AND OMP-managed `mcp_oauth:*` rows.
30
+ * Provider rows refresh through the per-provider registry. MCP rows are
31
+ * self-describing — the embedded token endpoint and client credentials are the
32
+ * only refresh material — so they refresh with a generic `refresh_token` grant.
33
+ * The serve process never loads the MCP manager, so this is the only place that
34
+ * teaches the broker to refresh MCP tokens; without it
35
+ * `POST /v1/credential/:id/refresh` fails with "Unknown OAuth provider" and the
36
+ * background refresher lets MCP access tokens expire (issue #8933).
37
+ */
38
+ export declare function refreshBrokerOAuthCredential(provider: string, credential: OAuthCredential, signal?: AbortSignal): Promise<OAuthCredentials>;
24
39
  export declare function runAuthBrokerCommand(cmd: AuthBrokerCommandArgs): Promise<void>;
25
40
  export { ACTIONS as AUTH_BROKER_ACTIONS };
@@ -126,6 +126,14 @@ interface UpdateMethodResolutionOptions {
126
126
  * preserves a global package symlink instead of resolving into its checkout.
127
127
  */
128
128
  ompLinkTarget?: string;
129
+ /**
130
+ * Whether package-manager routing (bun/npm) is permitted. Binary-only
131
+ * releases pass `false`: a manager launcher then resolves to `"binary"` and
132
+ * is taken over in place rather than reinstalled through its manager. Defaults
133
+ * to `true` in {@link resolveUpdateMethod} so callers that only classify need
134
+ * not set it.
135
+ */
136
+ allowPackageManagers?: boolean;
129
137
  }
130
138
  type UpdateTarget = {
131
139
  method: "brew";
@@ -36,11 +36,19 @@ export type ResolvedCliArgv = {
36
36
  } | {
37
37
  error: string;
38
38
  };
39
+ /**
40
+ * Subcommands that share the launch flag surface, so leading global flags
41
+ * (`--cwd`, `--model`, `--approval-mode`, …) placed before them are meaningful
42
+ * and must be forwarded ({@link resolveCliArgv}, #2970). Every other subcommand
43
+ * parses only its own flags.
44
+ */
45
+ export declare const LAUNCH_FLAG_COMMANDS: Record<string, true>;
39
46
  /**
40
47
  * Decide what the CLI runner should do with raw argv: reject bare reserved
41
48
  * management words, pass help/version through untouched, route a recognized
42
49
  * subcommand (even behind leading global flags like `--approval-mode=yolo`) to
43
- * that command with the flags preserved, and forward everything else to
44
- * `launch` (#2970).
50
+ * that command, and forward everything else to `launch` (#2970). Leading
51
+ * launch-global flags are forwarded to launch-shaped commands but stripped for
52
+ * other subcommands that cannot parse them (#8891).
45
53
  */
46
54
  export declare function resolveCliArgv(argv: string[]): ResolvedCliArgv;
@@ -5775,6 +5775,34 @@ export declare const SETTINGS_SCHEMA: {
5775
5775
  }];
5776
5776
  };
5777
5777
  };
5778
+ readonly "providers.cacheRetention": {
5779
+ readonly type: "enum";
5780
+ readonly values: readonly ["auto", "short", "long", "none"];
5781
+ readonly default: "auto";
5782
+ readonly ui: {
5783
+ readonly tab: "providers";
5784
+ readonly group: "Protocol";
5785
+ readonly label: "Prompt Cache Retention";
5786
+ readonly description: "Prompt-cache retention forwarded to providers that support it (Anthropic, Bedrock, OpenRouter, OpenAI)";
5787
+ readonly options: readonly [{
5788
+ readonly value: "auto";
5789
+ readonly label: "Auto";
5790
+ readonly description: "Provider default — Anthropic uses 5m entries kept warm by idle keep-alive refreshes; PI_CACHE_RETENTION still applies";
5791
+ }, {
5792
+ readonly value: "short";
5793
+ readonly label: "Short (5m)";
5794
+ readonly description: "Cheapest cache writes; Anthropic keeps the entry warm with bounded keep-alive refreshes while idle";
5795
+ }, {
5796
+ readonly value: "long";
5797
+ readonly label: "Long (1h)";
5798
+ readonly description: "1h TTL where the provider supports it; pricier writes, no keep-alive refresh requests";
5799
+ }, {
5800
+ readonly value: "none";
5801
+ readonly label: "Off";
5802
+ readonly description: "Disable prompt caching and cache-affinity routing";
5803
+ }];
5804
+ };
5805
+ };
5778
5806
  readonly "providers.streamFirstEventTimeoutSeconds": {
5779
5807
  readonly type: "number";
5780
5808
  readonly default: -1;
@@ -103,6 +103,15 @@ export declare class Settings {
103
103
  */
104
104
  flush(): Promise<void>;
105
105
  cloneForCwd(cwd: string): Promise<Settings>;
106
+ /**
107
+ * Re-read the current global, project, and explicit overlay layers from disk
108
+ * without replacing this instance or discarding runtime overrides.
109
+ *
110
+ * All sources are loaded before any live layer is replaced, so readers never
111
+ * observe a partially refreshed configuration. Concurrent callers share the
112
+ * same reload.
113
+ */
114
+ reloadFromDisk(): Promise<void>;
106
115
  /**
107
116
  * Re-scope this instance to a new working directory *in place*: reload the
108
117
  * project layer (`.claude/settings.yml` etc.) from `cwd`, re-resolve
@@ -277,6 +277,7 @@ export declare class ExtensionRunner {
277
277
  emitContext(messages: AgentMessage[]): Promise<AgentMessage[]>;
278
278
  /** Runs request payload hooks with the model used for that provider request. */
279
279
  emitBeforeProviderRequest(payload: unknown, model?: Model): Promise<BeforeProviderRequestEventResult>;
280
- emitAfterProviderResponse(response: ProviderResponseMetadata, _model?: Model): Promise<void>;
280
+ /** Runs response hooks with the model that produced that provider response. */
281
+ emitAfterProviderResponse(response: ProviderResponseMetadata, model?: Model): Promise<void>;
281
282
  emitBeforeAgentStart(prompt: string, images: ImageContent[] | undefined, systemPrompt: string[]): Promise<BeforeAgentStartCombinedResult | undefined>;
282
283
  }
@@ -14,6 +14,9 @@ export declare function hasLiveDaemonProjectPresence(runtimeDir: string): Promis
14
14
  * Best-effort and non-throwing: a scope is deleted only when its `broker.pid`
15
15
  * is absent/dead, no live client presence remains, and it has been untouched
16
16
  * for {@link DAEMON_RUNTIME_STALE_GRACE_MS}. The caller's own `currentRuntimeDir`
17
- * and the machine-global daemon container are always skipped.
17
+ * is always skipped, and the sweep runs only inside the {@link DAEMONS_DIR}
18
+ * container over entries named like a {@link DAEMON_SCOPE_KEY} — so a runtime
19
+ * dir relocated elsewhere (e.g. the smoke test under `os.tmpdir()`) never
20
+ * reclaims unrelated neighbours (issue #8721).
18
21
  */
19
22
  export declare function pruneDeadDaemonRuntimeDirs(currentRuntimeDir: string): Promise<void>;
@@ -1,3 +1,4 @@
1
+ import type { OAuthCredentials } from "@oh-my-pi/pi-ai/oauth/types";
1
2
  import type { AuthStorage } from "../session/auth-storage.js";
2
3
  import { type MCPStoredOAuthCredential } from "./oauth-flow.js";
3
4
  import type { MCPAuthConfig, MCPServerConfig } from "./types.js";
@@ -13,5 +14,27 @@ export declare function lookupMcpOAuthCredentialForServer(authStorage: AuthStora
13
14
  }): MCPOAuthCredentialLookup | undefined;
14
15
  export declare function lookupMcpOAuthCredential(authStorage: AuthStorage | null | undefined, config: MCPServerConfig): MCPOAuthCredentialLookup | undefined;
15
16
  export declare function selectMcpOAuthRefreshMaterial(credential: MCPStoredOAuthCredential, auth: MCPAuthConfig | undefined): MCPOAuthRefreshMaterial;
17
+ /**
18
+ * Refresh a stored MCP OAuth credential via the standard `refresh_token` grant.
19
+ *
20
+ * Refresh material is taken from the credential itself (self-contained modern
21
+ * credentials embed `tokenUrl`/`clientId`/`clientSecret`/`resource`) or, for
22
+ * legacy credentials that carry none, the server's `auth` block. Shared by the
23
+ * local MCP manager and the `omp auth-broker serve` refresh path so a broker
24
+ * with no access to the MCP config can still refresh `mcp_oauth:*` credentials
25
+ * from the vault.
26
+ *
27
+ * `serverUrl` supplies the RFC 8707 fallback resource indicator when neither
28
+ * the credential nor the auth block advertised one; the manager passes the
29
+ * configured server URL, the broker recovers it from the credential id via
30
+ * {@link mcpOAuthServerUrlFromCredentialId}.
31
+ *
32
+ * @throws when no usable refresh token or token endpoint is available.
33
+ */
34
+ export declare function refreshManagedMcpOAuthCredential(credential: MCPStoredOAuthCredential, opts?: {
35
+ serverUrl?: string;
36
+ auth?: MCPAuthConfig;
37
+ signal?: AbortSignal;
38
+ }): Promise<OAuthCredentials>;
16
39
  export declare function removeManagedMcpOAuthCredential(authStorage: AuthStorage, credentialId: string | undefined): Promise<boolean>;
17
40
  export declare function removeManagedMcpOAuthCredentials(authStorage: AuthStorage, credentialIds: readonly (string | undefined)[]): Promise<boolean>;
@@ -29,6 +29,17 @@ export declare function isManagedMCPOAuthCredentialId(credentialId: string | und
29
29
  * is the profile; everything after it is the URL.
30
30
  */
31
31
  export declare function mcpOAuthCredentialProfile(credentialId: string): string | undefined;
32
+ /**
33
+ * Server URL embedded in a managed MCP OAuth credential id, or `undefined`
34
+ * for legacy random ids (`mcp_oauth_<rand>`) minted before URL-keyed ids.
35
+ *
36
+ * Inverse of {@link mcpOAuthCredentialId}. Mirrors {@link mcpOAuthCredentialProfile}:
37
+ * the URL contains `:` and `/`, so for profile-scoped ids the URL is everything
38
+ * after the profile segment; for legacy url-keyed ids (`mcp_oauth:<url>`) it is
39
+ * everything after the prefix. Lets the auth-broker — which never sees the MCP
40
+ * config — recover the server URL for the RFC 8707 fallback resource on refresh.
41
+ */
42
+ export declare function mcpOAuthServerUrlFromCredentialId(credentialId: string): string | undefined;
32
43
  /**
33
44
  * Stored MCP OAuth credential. Refresh material is embedded so token refresh
34
45
  * works without any `auth` block persisted in (possibly shared) config files.
@@ -1,4 +1,16 @@
1
+ import type { MnemopiLlmCompleteOptions } from "@oh-my-pi/pi-mnemopi/core/runtime-options";
1
2
  import type { MemoryBackend } from "../memory-backend/types.js";
2
3
  import type { AgentSession } from "../session/agent-session.js";
4
+ /** Prompt turns for one Mnemopi completion. */
5
+ export interface MemoryCompletionInput {
6
+ prompt: string;
7
+ systemPrompt?: string;
8
+ }
9
+ /** Maps a Mnemopi completion into instruction and input turns.
10
+ *
11
+ * Extraction is the only task with its own instructions, and it always supplies
12
+ * the raw text, so the instructions become the system turn and the text becomes
13
+ * the user turn. Every other task keeps the prompt Mnemopi rendered. */
14
+ export declare function resolveMemoryCompletionInput(prompt: string, options?: MnemopiLlmCompleteOptions): MemoryCompletionInput;
3
15
  export declare const mnemopiBackend: MemoryBackend;
4
16
  export declare function getMnemopiDbDirForTests(session: AgentSession): string | undefined;
@@ -66,6 +66,12 @@ export declare const SPINNER_GLYPH_ADVANCE_MS = 80;
66
66
  /** Phase-locked spinner glyph index shared by every live tool block so parallel
67
67
  * spinners advance in lockstep instead of each tracking its own start time. */
68
68
  export declare function sharedSpinnerFrame(frameCount: number, now?: number): number;
69
+ /** Stop the shared spinner ticker and drop every registered live block.
70
+ * Called on interactive-mode teardown so a stray live block cannot keep the
71
+ * process-wide 80ms interval alive past shutdown (lingering event-loop
72
+ * handles pin the process; cf. `postmortem.quit`). Test files that assert on
73
+ * ticker arming also use this to start from a clean slate. */
74
+ export declare function stopSharedSpinnerTicker(): void;
69
75
  /**
70
76
  * Component that renders a tool call with its result (updateable)
71
77
  */
@@ -96,6 +102,12 @@ export declare class ToolExecutionComponent extends Container implements NativeS
96
102
  details?: any;
97
103
  isError?: boolean;
98
104
  }, isPartial?: boolean, _toolCallId?: string): void;
105
+ /**
106
+ * Advance to the shared spinner glyph and repaint just this block. Driven by
107
+ * the single shared spinner ticker (see `registerSpinnerBlock`); the tick is
108
+ * component-scoped so the TUI reuses every other root subtree (issue #4377).
109
+ */
110
+ tickSpinner(frame: number): void;
99
111
  /**
100
112
  * Standalone harnesses may mount a tool component directly under `TUI`
101
113
  * instead of inside `TranscriptContainer`. In that shape the component must
@@ -26,6 +26,8 @@ export declare class InputController {
26
26
  readText: typeof readTextFromClipboard;
27
27
  readMacFileUrls?: typeof readMacFileUrlsFromClipboard;
28
28
  });
29
+ /** Session-level title starts (user `/skill:` via promptCustomMessage) reuse this UI. */
30
+ notifyTitleGenerationStart(): void;
29
31
  setupKeyHandlers(): void;
30
32
  setupEditorSubmitHandler(): void;
31
33
  handleCtrlC(): void;
@@ -31,7 +31,7 @@ import type { HookInputComponent } from "./components/hook-input.js";
31
31
  import type { HookSelectorComponent, HookSelectorSlider } from "./components/hook-selector.js";
32
32
  import { type PlanReviewAnnotationState } from "./components/plan-review-overlay.js";
33
33
  import { StatusLineComponent } from "./components/status-line/index.js";
34
- import type { ToolExecutionHandle } from "./components/tool-execution.js";
34
+ import { type ToolExecutionHandle } from "./components/tool-execution.js";
35
35
  import { TranscriptContainer } from "./components/transcript-container.js";
36
36
  import { EventController } from "./controllers/event-controller.js";
37
37
  import { type LoopLimitRuntime } from "./loop-limit.js";
@@ -67,8 +67,8 @@ export interface InteractiveModeOptions {
67
67
  }
68
68
  /**
69
69
  * Build the anchored subagent HUD block: a bold accent "Subagents" header plus
70
- * a bounded set of running-agent rows in the same `Id: description` shape the
71
- * inline task rows use (muted task preview when no description was given).
70
+ * a bounded set of running-agent rows in the same `Id ⟨role⟩: description` shape
71
+ * the inline task rows use (muted task preview when no description was given).
72
72
  * Layout mirrors the Todos HUD exactly: unindented header, then
73
73
  * `renderTreeList` rows (dim connectors) shifted right by one space.
74
74
  * Only detached background spawns are listed: a sync task call blocks the
@@ -150,6 +150,9 @@ export declare class InteractiveMode implements InteractiveModeContext {
150
150
  onInputCallback?: (input: SubmittedUserInput) => void;
151
151
  optimisticUserMessageSignature: string | undefined;
152
152
  locallySubmittedUserSignatures: Set<string>;
153
+ /** True while an optimistically-rendered `/skill:` row awaits its canonical
154
+ * `message_start`. Read by the event controller to reconcile the row. */
155
+ optimisticSkillMessagePending: boolean;
153
156
  lastSigintTime: number;
154
157
  lastEscapeTime: number;
155
158
  lastLeftTapTime: number;
@@ -219,6 +222,25 @@ export declare class InteractiveMode implements InteractiveModeContext {
219
222
  replaceOptimisticUserMessage(message: AgentMessage, options?: {
220
223
  imageLinks?: readonly (string | undefined)[];
221
224
  }): void;
225
+ /**
226
+ * Optimistically render a user-invoked `/skill:` row before its awaited
227
+ * dispatch so a slow preflight (memory recall, `before_agent_start` hooks,
228
+ * auto-thinking classification, pre-prompt compaction) does not leave the
229
+ * submission invisible — normal prompts paint their row via
230
+ * {@link startPendingSubmission} the same way (issue #8895). The canonical
231
+ * skill `message_start` swaps this row in place via
232
+ * {@link reconcileOptimisticSkillMessage}; a failed or bailed dispatch drops
233
+ * it via {@link clearOptimisticSkillMessage}.
234
+ */
235
+ renderOptimisticSkillMessage(message: AgentMessage, options?: {
236
+ imageLinks?: readonly (string | undefined)[];
237
+ }): void;
238
+ /** Replace the optimistic `/skill:` row with the canonical message emitted by
239
+ * the session, mirroring {@link replaceOptimisticUserMessage} for skills. */
240
+ reconcileOptimisticSkillMessage(message: AgentMessage): void;
241
+ /** Drop the optimistic `/skill:` row when dispatch fails or bails before the
242
+ * message reaches the agent (aborted preflight, streaming-race requeue). */
243
+ clearOptimisticSkillMessage(): void;
222
244
  startPendingSubmission(input: {
223
245
  text: string;
224
246
  images?: ImageContent[];
@@ -290,6 +290,16 @@ export interface InteractiveModeContext {
290
290
  replaceOptimisticUserMessage(message: AgentMessage, options?: {
291
291
  imageLinks?: readonly (string | undefined)[];
292
292
  }): void;
293
+ /** True while an optimistically-rendered `/skill:` row awaits its canonical `message_start`. */
294
+ optimisticSkillMessagePending: boolean;
295
+ /** Optimistically renders a user-invoked `/skill:` row before its awaited dispatch (issue #8895). */
296
+ renderOptimisticSkillMessage(message: AgentMessage, options?: {
297
+ imageLinks?: readonly (string | undefined)[];
298
+ }): void;
299
+ /** Swaps the optimistic `/skill:` row for the canonical message emitted by the session. */
300
+ reconcileOptimisticSkillMessage(message: AgentMessage): void;
301
+ /** Drops the optimistic `/skill:` row when dispatch fails or bails before reaching the agent. */
302
+ clearOptimisticSkillMessage(): void;
293
303
  isKnownSlashCommand(text: string): boolean;
294
304
  addMessageToChat(message: AgentMessage, options?: {
295
305
  populateHistory?: boolean;
@@ -630,6 +630,9 @@ export declare class AgentSession {
630
630
  * changes (e.g. `/move` relocation) so the next replan refresh resolves
631
631
  * against the destination project's override. */
632
632
  setTitleSystemPrompt(prompt: string | undefined): void;
633
+ /** Install the interactive title-download UI hook. Used when `/skill:` starts
634
+ * titling from {@link promptCustomMessage} without the input-controller callback. */
635
+ setTitleGenerationStart(handler: (() => void) | undefined): void;
633
636
  /**
634
637
  * Abort current operation and wait for agent to become idle.
635
638
  *
@@ -19,8 +19,12 @@ export interface PrewalkCoordinatorHost {
19
19
  ephemeral?: boolean;
20
20
  }): Promise<void>;
21
21
  setActiveToolsByName(names: string[]): Promise<void>;
22
+ setActiveToolPresentation(toolNames: string[], mountedToolNames: string[]): Promise<void>;
23
+ runToolRegistryMutation<T>(mutation: () => Promise<T>): Promise<T>;
22
24
  getActiveToolNames(): string[];
23
25
  getEnabledToolNames(): string[];
26
+ getSelectedMCPToolNames(): string[];
27
+ getMountedXdevToolNames(): string[];
24
28
  hasBuiltInTool(name: string): boolean;
25
29
  getPlanModeState(): PlanModeState | undefined;
26
30
  setPlanModeState(state: PlanModeState | undefined): void;
@@ -150,7 +150,6 @@ declare module "@oh-my-pi/pi-agent-core/compaction/entries" {
150
150
  interface CustomCompactionSessionEntries {
151
151
  titleChange: TitleChangeEntry;
152
152
  credentialPin: CredentialPinEntry;
153
- resetBoundary: ResetBoundaryEntry;
154
153
  }
155
154
  }
156
155
  /** TTSR injection entry - tracks which time-traveling rules have been injected this session. */
@@ -200,6 +200,18 @@ export declare class SessionManager {
200
200
  getSessionDir(): string;
201
201
  getSessionId(): string;
202
202
  getSessionFile(): string | undefined;
203
+ /**
204
+ * Whether the current session has actually been materialized to durable
205
+ * storage (the JSONL exists on disk / in the active storage backend).
206
+ *
207
+ * Session persistence is lazy: the file is only written once the history
208
+ * contains an assistant message (or an explicit {@link ensureOnDisk}
209
+ * caller forces it). Until then {@link getSessionFile} returns an allocated
210
+ * path that leads nowhere, so a `--resume <id>` hint built from it would
211
+ * always fail. Consumers that advertise a resume command must gate on this
212
+ * (issue #8860).
213
+ */
214
+ isSessionOnDisk(): boolean;
203
215
  getArtifactsDir(): string | null;
204
216
  adoptArtifactManager(manager: ArtifactManager): void;
205
217
  getArtifactManager(): ArtifactManager | null;
@@ -9,6 +9,12 @@ interface PendingContextSnapshot {
9
9
  promptTokens: number;
10
10
  nonMessageTokens: number;
11
11
  cutoffCount: number;
12
+ /**
13
+ * Compaction epoch at rebase time. Distinguishes a genuinely fresh in-turn
14
+ * anchor (same epoch) from a post-cutoff anchor that predates a mid-run
15
+ * compaction (older epoch) so the latter never out-ranks this snapshot.
16
+ */
17
+ epoch: number;
12
18
  }
13
19
  /** Capabilities the stats tracker borrows from its owning session. */
14
20
  export interface SessionStatsTrackerHost {
@@ -36,6 +42,12 @@ export declare class SessionStatsTracker {
36
42
  }): ContextUsage | undefined;
37
43
  /** Monotonic revision for in-flight context snapshot changes. */
38
44
  get revision(): number;
45
+ /**
46
+ * Monotonic compaction epoch, bumped whenever history is compacted. Stamped
47
+ * onto each assistant snapshot at record time so {@link getContextBreakdown}
48
+ * can reject a post-cutoff anchor whose usage predates the last compaction.
49
+ */
50
+ get compactionEpoch(): number;
39
51
  /** Non-message token count captured for the active provider request. */
40
52
  get pendingNonMessageTokens(): number | undefined;
41
53
  /**
@@ -49,7 +61,7 @@ export declare class SessionStatsTracker {
49
61
  */
50
62
  recordAnchoredHistoryRewrite(tokensRemoved: number): void;
51
63
  /** Sets or clears the in-flight context snapshot. */
52
- setPendingSnapshot(snapshot: PendingContextSnapshot | undefined): void;
64
+ setPendingSnapshot(snapshot: Omit<PendingContextSnapshot, "epoch"> | undefined): void;
53
65
  /** Recomputes an in-flight snapshot after history is compacted or rewritten. */
54
66
  rebaseAfterCompaction(): void;
55
67
  /** Records provider usage headers against the active session account. */
@@ -0,0 +1,13 @@
1
+ /** Compact title-model input for a user-invoked `/skill:<name>` prompt. */
2
+ export declare function skillPromptTitleInput(input: {
3
+ name?: string;
4
+ args?: string;
5
+ queueChipText?: string;
6
+ }): string;
7
+ /** Title text for a persisted skill-prompt custom message. Never the expanded SKILL.md body. */
8
+ export declare function titleTextFromSkillPrompt(message: {
9
+ role: string;
10
+ customType?: string;
11
+ attribution?: string;
12
+ details?: unknown;
13
+ }): string | undefined;
@@ -89,10 +89,13 @@ export declare const SMOKE_TEST_TIMEOUT_MS = 30000;
89
89
  /**
90
90
  * Resolve the command used to relaunch the agent CLI into worker mode. In a
91
91
  * compiled binary the entry point is the binary itself; otherwise re-enter the
92
- * declared worker-host entry with a cwd-relative script path (Bun's subprocess
93
- * IPC is more reliable that way under `bun test`), falling back to this
94
- * package's own `src/cli.ts` when no host entry is declared (bun test, SDK
95
- * embedding).
92
+ * declared worker-host entry by absolute path. Workers deliberately spawn
93
+ * without a pinned cwd there: they share the parent's foreground process
94
+ * group, and terminal cwd heuristics (kitty's new_tab_with_cwd) read the
95
+ * newest process in that group, so anchoring them to the install dir leaks
96
+ * into newly opened terminal tabs. With no declared host entry (bun test, SDK
97
+ * embedding) fall back to a cwd-relative `src/cli.ts`, which Bun subprocess
98
+ * IPC handles more reliably under `bun test`.
96
99
  */
97
100
  export declare function resolveWorkerSpawnCmd(workerArg: string): WorkerSpawnCommand;
98
101
  /**
@@ -1,4 +1,6 @@
1
1
  import type { ModelRegistry } from "../config/model-registry.js";
2
2
  import type { Settings } from "../config/settings.js";
3
+ /** True when a generated label is just the spawn handle, including `Name-2`. */
4
+ export declare function labelEchoesHandle(handle: string | undefined, label: string): boolean;
3
5
  /** Compresses a delegated assignment into a one-sentence UI label via the tiny title model — fired by the executor spawn path because the task wire schema no longer carries a `description`; null on empty input or failure. */
4
6
  export declare function generateTaskLabel(assignment: string, registry: ModelRegistry, settings: Settings, sessionId?: string, signal?: AbortSignal): Promise<string | null>;
@@ -23,6 +23,8 @@ type TaskRenderOptions = RenderResultOptions & {
23
23
  renderContext?: TaskRenderContext;
24
24
  };
25
25
  export declare function formatTaskId(id: string): string;
26
+ /** Dim `⟨agent⟩` badge for a non-default agent type; empty for the generic worker. */
27
+ export declare function agentTypeBadge(agent: string | undefined, theme: Theme): string;
26
28
  /**
27
29
  * Render the tool call arguments.
28
30
  */
@@ -0,0 +1,2 @@
1
+ import type { TextGenerationPipeline } from "@huggingface/transformers";
2
+ export declare function buildCompletionPrompt(tokenizer: TextGenerationPipeline["tokenizer"], promptText: string, systemPrompt?: string): string;
@@ -18,6 +18,11 @@ export interface TinyTitleGenerateOptions {
18
18
  signal?: AbortSignal;
19
19
  systemPrompt?: string;
20
20
  }
21
+ export interface TinyModelCompletionOptions {
22
+ maxTokens?: number;
23
+ signal?: AbortSignal;
24
+ systemPrompt?: string;
25
+ }
21
26
  /**
22
27
  * Hidden subcommand on the main CLI that boots the tiny-model worker in the
23
28
  * spawned subprocess. Kept in sync with the dispatch in `cli.ts`.
@@ -62,10 +67,7 @@ export declare class TinyTitleClient {
62
67
  prewarm(modelKey: string): void;
63
68
  generate(modelKey: string, message: string, signal?: AbortSignal): Promise<string | null>;
64
69
  generate(modelKey: string, message: string, options?: TinyTitleGenerateOptions): Promise<string | null>;
65
- complete(modelKey: string, prompt: string, options?: {
66
- maxTokens?: number;
67
- signal?: AbortSignal;
68
- }): Promise<string | null>;
70
+ complete(modelKey: string, prompt: string, options?: TinyModelCompletionOptions): Promise<string | null>;
69
71
  downloadModel(modelKey: string, options?: TinyTitleDownloadOptions): Promise<TinyTitleDownloadResult>;
70
72
  terminate(): Promise<void>;
71
73
  }
@@ -31,6 +31,7 @@ export type TinyTitleWorkerInbound = {
31
31
  modelKey: TinyLocalModelKey;
32
32
  prompt: string;
33
33
  maxTokens?: number;
34
+ systemPrompt?: string;
34
35
  } | {
35
36
  type: "download";
36
37
  id: string;
@@ -1,2 +1,29 @@
1
+ import type { ProgressInfo, TextGenerationPipeline, StoppingCriteria as TransformersStoppingCriteria } from "@huggingface/transformers";
2
+ import { type TransformersRuntimeMetadata } from "../subprocess/worker-runtime.js";
3
+ import { type TinyModelDevice } from "./device.js";
4
+ import { type TinyModelDtype } from "./dtype.js";
1
5
  import type { TinyTitleTransport } from "./title-protocol.js";
6
+ export interface TransformersRuntime extends TransformersRuntimeMetadata {
7
+ env: {
8
+ cacheDir?: string;
9
+ allowLocalModels?: boolean;
10
+ logLevel?: unknown;
11
+ };
12
+ LogLevel: {
13
+ ERROR: unknown;
14
+ };
15
+ StoppingCriteria: new () => TransformersStoppingCriteria;
16
+ pipeline: (task: "text-generation", model: string, options: {
17
+ device: TinyModelDevice;
18
+ dtype: TinyModelDtype;
19
+ progress_callback: (info: ProgressInfo) => void;
20
+ }) => Promise<TextGenerationPipeline>;
21
+ }
22
+ /** Stops generation at the first occurrence of `text` in the *generated* tokens.
23
+ *
24
+ * The window must be anchored to the generation boundary, not to the end of the
25
+ * whole sequence: a prompt that itself contains the stop string (chat-level
26
+ * few-shot examples ending in `</title>`, for instance) would otherwise match on
27
+ * prompt tokens and stop before the model emits anything. */
28
+ export declare function createStopOnTextCriteria(transformers: TransformersRuntime, tokenizer: TextGenerationPipeline["tokenizer"], text: string): TransformersStoppingCriteria;
2
29
  export declare function startTinyTitleWorker(transport: TinyTitleTransport): void;
@@ -40,7 +40,7 @@ export declare function wrapShellLineForClientTerminal(line: string, shellConfig
40
40
  * while false positives remain actionable through user policy control.
41
41
  * New patterns should target shapes that are virtually never legitimate in automation.
42
42
  */
43
- export declare const CRITICAL_BASH_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
43
+ export declare const CRITICAL_BASH_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
44
44
  declare const bashSchemaBase: import("@oh-my-pi/omptype").FluentType<{
45
45
  command: string;
46
46
  cwd?: string | undefined;
@@ -10,6 +10,12 @@ export interface HashlineHeaderContext {
10
10
  }
11
11
  export declare function formatReadHashlineHeader(displayPath: string, tag: string): string;
12
12
  export declare function readHashlineHeaderContext(session: ToolSession, absolutePath: string, cwd: string): Promise<HashlineHeaderContext>;
13
+ /**
14
+ * {@link readHashlineHeaderContext} for a caller that already holds the file's
15
+ * full text, so the file is not reopened just to hash it. Line endings are
16
+ * normalized here, exactly as the reading variant does.
17
+ */
18
+ export declare function hashlineHeaderContextForText(session: ToolSession, absolutePath: string, cwd: string, fullText: string): HashlineHeaderContext;
13
19
  export declare function hashlineHeaderContext(displayPath: string, tag: string): HashlineHeaderContext;
14
20
  export declare function prependHashlineHeader(text: string, context: HashlineHeaderContext | undefined): string;
15
21
  export declare function formatTextWithMode(text: string, startNum: number, shouldAddHashLines: boolean, shouldAddLineNumbers: boolean): string;
@@ -10,7 +10,13 @@ export declare function routeReadThroughBridge(session: ToolSession, absolutePat
10
10
  line?: number;
11
11
  limit?: number;
12
12
  }): Promise<string> | undefined;
13
- export declare function trySummarize(session: ToolSession, absolutePath: string, fileSize: number, signal?: AbortSignal): Promise<SummaryResult | null>;
13
+ /**
14
+ * Structural summary of `absolutePath`, or `null` when the file is too large,
15
+ * too short, or unparseable. `diskText` lets a caller that already read the file
16
+ * hand those bytes over instead of forcing a second read; an ACP bridge still
17
+ * wins, since the editor's buffer is the source of truth.
18
+ */
19
+ export declare function trySummarize(session: ToolSession, absolutePath: string, fileSize: number, signal?: AbortSignal, diskText?: string): Promise<SummaryResult | null>;
14
20
  export declare function renderSummary(session: ToolSession, summary: SummaryResult): {
15
21
  text: string;
16
22
  displayText: string;
@@ -6,6 +6,20 @@ export interface LineSpan {
6
6
  export interface BlockContextSource {
7
7
  path?: string;
8
8
  lang?: string;
9
+ /**
10
+ * The whole source `fullLines` was split from, when the caller still holds it.
11
+ * Supplying it skips re-joining every line into a fresh whole-file string on
12
+ * the way to the parser. It MUST be the same content as `fullLines`; a
13
+ * differing trailing newline is the only tolerated variation, since it moves
14
+ * no node's line number.
15
+ *
16
+ * Every current supplier derives both from one buffer in the same breath, so
17
+ * the two cannot drift. Do NOT set it on a source object that is reused
18
+ * across two different line arrays — a before/after diff pair, say — because
19
+ * the boundary lines tree-sitter reports would then be indexed into the wrong
20
+ * array and surface off-by-N context rows.
21
+ */
22
+ text?: string;
9
23
  }
10
24
  export type LineEntry = {
11
25
  kind: "line";
@@ -2,3 +2,18 @@
2
2
  export declare function withTimeoutSignal(timeoutMs: number, signal?: AbortSignal): AbortSignal;
3
3
  /** Detect a timeout raised by an abortable fetch. */
4
4
  export declare function isTimeoutError(error: unknown): boolean;
5
+ /**
6
+ * Detect Bun's `UnsupportedProxyProtocol` fetch rejection, raised when a proxy
7
+ * env var (`HTTPS_PROXY`, `ALL_PROXY`, …) points at a scheme it cannot drive —
8
+ * most commonly a SOCKS proxy (`socks5://`, `socks5h://`). The raw error tells
9
+ * the caller to "pass `verbose: true` in the second argument to fetch()", which
10
+ * is meaningless from a CLI, so callers translate it into
11
+ * {@link unsupportedProxyMessage}.
12
+ */
13
+ export declare function isUnsupportedProxyError(error: unknown): boolean;
14
+ /**
15
+ * Build an actionable CLI message for an {@link isUnsupportedProxyError} failure,
16
+ * naming any set proxy env var whose scheme is not `http(s)://` so the user can
17
+ * see exactly which variable to change.
18
+ */
19
+ export declare function unsupportedProxyMessage(env?: Record<string, string | undefined>): string;