@oh-my-pi/pi-coding-agent 16.1.15 → 16.1.17

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 (143) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/dist/cli.js +3531 -3812
  3. package/dist/types/auto-thinking/classifier.d.ts +4 -2
  4. package/dist/types/cli/args.d.ts +2 -5
  5. package/dist/types/cli/flag-tables.d.ts +2 -2
  6. package/dist/types/cli/session-picker.d.ts +4 -2
  7. package/dist/types/commands/launch.d.ts +1 -1
  8. package/dist/types/config/model-discovery.d.ts +1 -0
  9. package/dist/types/config/model-registry.d.ts +4 -0
  10. package/dist/types/config/settings-schema.d.ts +12 -1
  11. package/dist/types/eval/agent-bridge.d.ts +19 -0
  12. package/dist/types/eval/js/shared/helpers.d.ts +1 -13
  13. package/dist/types/eval/js/shared/types.d.ts +1 -1
  14. package/dist/types/eval/js/worker-protocol.d.ts +1 -1
  15. package/dist/types/eval/py/executor.d.ts +1 -1
  16. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +22 -7
  17. package/dist/types/extensibility/plugins/loader.d.ts +16 -9
  18. package/dist/types/extensibility/tool-event-input.d.ts +2 -0
  19. package/dist/types/hindsight/content.d.ts +2 -10
  20. package/dist/types/internal-urls/local-protocol.d.ts +18 -1
  21. package/dist/types/main.d.ts +2 -0
  22. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  23. package/dist/types/modes/components/custom-editor.d.ts +3 -0
  24. package/dist/types/modes/components/plugin-settings.d.ts +5 -0
  25. package/dist/types/modes/components/session-selector.d.ts +25 -0
  26. package/dist/types/modes/components/status-line/types.d.ts +1 -0
  27. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  28. package/dist/types/modes/controllers/input-controller.d.ts +14 -0
  29. package/dist/types/modes/controllers/selector-controller.d.ts +11 -0
  30. package/dist/types/session/agent-session.d.ts +6 -6
  31. package/dist/types/session/provider-image-budget.d.ts +3 -0
  32. package/dist/types/session/session-context.d.ts +6 -5
  33. package/dist/types/task/isolation-runner.d.ts +128 -0
  34. package/dist/types/task/parallel.d.ts +4 -0
  35. package/dist/types/task/worktree.d.ts +14 -1
  36. package/dist/types/thinking.d.ts +23 -1
  37. package/dist/types/tiny/title-client.d.ts +2 -2
  38. package/dist/types/tools/eval-render.d.ts +3 -0
  39. package/dist/types/tools/eval.d.ts +11 -17
  40. package/dist/types/tools/todo.d.ts +26 -28
  41. package/dist/types/tui/output-block.d.ts +8 -0
  42. package/dist/types/utils/image-resize.d.ts +2 -0
  43. package/dist/types/utils/tools-manager.d.ts +2 -0
  44. package/dist/types/web/search/providers/exa.d.ts +2 -0
  45. package/package.json +12 -12
  46. package/scripts/build-binary.ts +18 -4
  47. package/src/auto-thinking/classifier.ts +7 -2
  48. package/src/cli/args.ts +4 -5
  49. package/src/cli/flag-tables.ts +3 -3
  50. package/src/cli/gallery-fixtures/interaction.ts +6 -9
  51. package/src/cli/gallery-fixtures/shell.ts +15 -23
  52. package/src/cli/profile-alias.ts +38 -7
  53. package/src/cli/session-picker.ts +17 -3
  54. package/src/cli/usage-cli.ts +5 -1
  55. package/src/commands/launch.ts +3 -3
  56. package/src/config/model-discovery.ts +59 -8
  57. package/src/config/model-registry.ts +74 -3
  58. package/src/config/settings-schema.ts +13 -1
  59. package/src/discovery/omp-extension-roots.ts +1 -3
  60. package/src/edit/renderer.ts +34 -12
  61. package/src/eval/__tests__/agent-bridge.test.ts +462 -3
  62. package/src/eval/__tests__/helpers-local-roots.test.ts +2 -5
  63. package/src/eval/__tests__/julia-prelude.test.ts +1 -30
  64. package/src/eval/__tests__/prelude-agent.test.ts +42 -8
  65. package/src/eval/agent-bridge.ts +301 -71
  66. package/src/eval/jl/prelude.jl +32 -227
  67. package/src/eval/jl/runner.jl +38 -12
  68. package/src/eval/js/shared/helpers.ts +1 -114
  69. package/src/eval/js/shared/prelude.txt +13 -27
  70. package/src/eval/js/shared/runtime.ts +0 -6
  71. package/src/eval/js/shared/types.ts +1 -1
  72. package/src/eval/js/worker-protocol.ts +1 -1
  73. package/src/eval/py/__tests__/prelude.test.ts +13 -0
  74. package/src/eval/py/executor.ts +1 -1
  75. package/src/eval/py/prelude.py +47 -105
  76. package/src/eval/py/runner.py +0 -6
  77. package/src/eval/rb/prelude.rb +21 -189
  78. package/src/eval/rb/runner.rb +116 -9
  79. package/src/export/html/tool-views.generated.js +29 -29
  80. package/src/extensibility/extensions/wrapper.ts +3 -2
  81. package/src/extensibility/hooks/tool-wrapper.ts +4 -3
  82. package/src/extensibility/plugins/legacy-pi-compat.ts +40 -8
  83. package/src/extensibility/plugins/loader.ts +71 -23
  84. package/src/extensibility/plugins/marketplace/manager.ts +134 -0
  85. package/src/extensibility/tool-event-input.ts +57 -0
  86. package/src/hindsight/content.ts +21 -12
  87. package/src/internal-urls/docs-index.generated.txt +1 -1
  88. package/src/internal-urls/local-protocol.ts +100 -53
  89. package/src/main.ts +15 -4
  90. package/src/mcp/tool-bridge.ts +27 -2
  91. package/src/mnemopi/state.ts +5 -2
  92. package/src/modes/acp/acp-event-mapper.ts +7 -2
  93. package/src/modes/components/agent-transcript-viewer.ts +193 -41
  94. package/src/modes/components/chat-transcript-builder.ts +6 -0
  95. package/src/modes/components/custom-editor.test.ts +18 -1
  96. package/src/modes/components/custom-editor.ts +77 -45
  97. package/src/modes/components/hook-editor.ts +15 -2
  98. package/src/modes/components/plugin-settings.ts +7 -1
  99. package/src/modes/components/session-selector.ts +143 -29
  100. package/src/modes/components/settings-selector.ts +2 -2
  101. package/src/modes/components/status-line/component.ts +52 -8
  102. package/src/modes/components/status-line/segments.ts +5 -1
  103. package/src/modes/components/status-line/types.ts +1 -0
  104. package/src/modes/components/welcome.ts +12 -14
  105. package/src/modes/controllers/command-controller.ts +21 -5
  106. package/src/modes/controllers/input-controller.ts +115 -3
  107. package/src/modes/controllers/selector-controller.ts +19 -1
  108. package/src/modes/interactive-mode.ts +3 -3
  109. package/src/modes/rpc/rpc-mode.ts +6 -0
  110. package/src/modes/utils/copy-targets.ts +7 -2
  111. package/src/modes/utils/ui-helpers.ts +3 -3
  112. package/src/prompts/system/system-prompt.md +3 -3
  113. package/src/prompts/system/workflow-notice.md +3 -3
  114. package/src/prompts/tools/bash.md +16 -0
  115. package/src/prompts/tools/eval.md +19 -19
  116. package/src/prompts/tools/todo.md +1 -1
  117. package/src/sdk.ts +8 -10
  118. package/src/session/agent-session.ts +422 -97
  119. package/src/session/provider-image-budget.ts +86 -0
  120. package/src/session/session-context.ts +14 -7
  121. package/src/session/session-storage.ts +24 -2
  122. package/src/session/snapcompact-inline.ts +19 -3
  123. package/src/slash-commands/builtin-registry.ts +0 -22
  124. package/src/slash-commands/helpers/usage-report.ts +9 -1
  125. package/src/task/index.ts +61 -207
  126. package/src/task/isolation-runner.ts +354 -0
  127. package/src/task/parallel.ts +6 -1
  128. package/src/task/worktree.ts +46 -9
  129. package/src/thinking.ts +29 -2
  130. package/src/tiny/title-client.ts +75 -21
  131. package/src/tools/ask.ts +44 -38
  132. package/src/tools/bash.ts +9 -2
  133. package/src/tools/browser/tab-worker.ts +1 -1
  134. package/src/tools/eval-render.ts +34 -27
  135. package/src/tools/eval.ts +100 -103
  136. package/src/tools/index.ts +8 -1
  137. package/src/tools/read.ts +136 -60
  138. package/src/tools/todo.ts +60 -64
  139. package/src/tui/code-cell.ts +1 -1
  140. package/src/tui/output-block.ts +11 -0
  141. package/src/utils/image-resize.ts +30 -0
  142. package/src/utils/tools-manager.ts +67 -10
  143. package/src/web/search/providers/exa.ts +85 -1
@@ -25,10 +25,12 @@ export interface ClassifyDifficultyDeps {
25
25
  metadataResolver?: (provider: string) => Record<string, unknown> | undefined;
26
26
  }
27
27
  /**
28
- * Classify `promptText` and return a concrete effort clamped to `deps.model`.
28
+ * Classify `promptText` and return a concrete effort clamped to `deps.model`,
29
+ * or `undefined` when the model has no controllable effort surface (auto has
30
+ * nothing to pick — the caller leaves the prior reasoning level in place).
29
31
  * @throws when the backend cannot produce a usable classification.
30
32
  */
31
- export declare function classifyDifficulty(promptText: string, deps: ClassifyDifficultyDeps): Promise<Effort>;
33
+ export declare function classifyDifficulty(promptText: string, deps: ClassifyDifficultyDeps): Promise<Effort | undefined>;
32
34
  /** Map the online 4-way level keyword to an {@link Effort}; earliest match wins. */
33
35
  export declare function parseDifficultyLevel(text: string): Effort | undefined;
34
36
  /** Map the local 3-way bucket keyword to an {@link Effort}; earliest match wins. */
@@ -1,7 +1,4 @@
1
- /**
2
- * CLI argument parsing and help display
3
- */
4
- import { type Effort } from "@oh-my-pi/pi-catalog/effort";
1
+ import { type ConfiguredThinkingLevel } from "../thinking";
5
2
  export type Mode = "text" | "json" | "rpc" | "acp" | "rpc-ui";
6
3
  export interface Args {
7
4
  cwd?: string;
@@ -18,7 +15,7 @@ export interface Args {
18
15
  apiKey?: string;
19
16
  systemPrompt?: string;
20
17
  appendSystemPrompt?: string;
21
- thinking?: Effort;
18
+ thinking?: ConfiguredThinkingLevel;
22
19
  hideThinking?: boolean;
23
20
  advisor?: boolean;
24
21
  continue?: boolean;
@@ -29,7 +29,7 @@
29
29
  * tables), they're passed in through {@link ParseDeps} and `args.ts` wires the
30
30
  * real implementations at the dispatch site.
31
31
  */
32
- import type { Effort } from "@oh-my-pi/pi-ai";
32
+ import type { ConfiguredThinkingLevel } from "../thinking";
33
33
  import type { Args } from "./args";
34
34
  /**
35
35
  * Runtime dependencies injected into setters that need to validate input or
@@ -44,7 +44,7 @@ export interface ParseDeps {
44
44
  logger: {
45
45
  warn: (message: string, meta?: Record<string, unknown>) => void;
46
46
  };
47
- parseEffort: (value: string | null | undefined) => Effort | undefined;
47
+ parseThinking: (value: string | null | undefined) => ConfiguredThinkingLevel | undefined;
48
48
  builtinToolNames: readonly string[];
49
49
  thinkingEfforts: readonly string[];
50
50
  }
@@ -1,8 +1,10 @@
1
1
  import type { SessionInfo } from "../session/session-listing";
2
2
  /**
3
3
  * Show the TUI session selector and return the selected session, or null if
4
- * cancelled. Tab toggles between current-folder and all-projects scope; the
5
- * all-projects list is loaded lazily via `SessionManager.listAll`.
4
+ * cancelled. Rendered as a fullscreen overlay on the terminal's alternate
5
+ * screen, so the list scrolls and rows are clickable with the mouse. Tab
6
+ * toggles between current-folder and all-projects scope; the all-projects list
7
+ * is loaded lazily via `SessionManager.listAll`.
6
8
  */
7
9
  export declare function selectSession(sessions: SessionInfo[], options?: {
8
10
  allSessions?: SessionInfo[];
@@ -92,7 +92,7 @@ export default class Index extends Command {
92
92
  };
93
93
  thinking: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"string"> & {
94
94
  description: string;
95
- options: import("@oh-my-pi/pi-catalog").Effort[];
95
+ options: string[];
96
96
  };
97
97
  "hide-thinking": import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
98
98
  description: string;
@@ -38,6 +38,7 @@ export interface DiscoveryContext {
38
38
  export declare function discoverModelsByProviderType(providerConfig: DiscoveryProviderConfig, ctx: DiscoveryContext): Promise<Model<Api>[]>;
39
39
  export declare function discoverOllamaModels(providerConfig: DiscoveryProviderConfig, ctx: DiscoveryContext): Promise<Model<Api>[]>;
40
40
  export declare function discoverLlamaCppModels(providerConfig: DiscoveryProviderConfig, ctx: DiscoveryContext): Promise<Model<Api>[]>;
41
+ export declare function discoverLlamaCppModelContextWindow(model: Pick<Model<Api>, "provider" | "id" | "baseUrl" | "headers">, ctx: DiscoveryContext): Promise<number | undefined>;
41
42
  export declare function discoverOpenAIModelsList(providerConfig: DiscoveryProviderConfig, ctx: DiscoveryContext): Promise<Model<Api>[]>;
42
43
  /**
43
44
  * Discover models from an Anthropic+OpenAI-compatible reseller proxy that
@@ -89,6 +89,10 @@ export declare class ModelRegistry {
89
89
  refresh(strategy?: ModelRefreshStrategy): Promise<void>;
90
90
  refreshInBackground(strategy?: ModelRefreshStrategy): void;
91
91
  refreshProvider(providerId: string, strategy?: ModelRefreshStrategy): Promise<void>;
92
+ /**
93
+ * Refresh dynamic metadata that can appear only after a local model loads.
94
+ */
95
+ refreshSelectedModelMetadata(model: Model<Api>): Promise<Model<Api>>;
92
96
  /**
93
97
  * Discover models for providers registered at runtime via `fetchDynamicModels`
94
98
  * (extension providers). Merges the discovered catalog into the existing model
@@ -3726,7 +3726,7 @@ export declare const SETTINGS_SCHEMA: {
3726
3726
  readonly tab: "tools";
3727
3727
  readonly group: "Discovery & MCP";
3728
3728
  readonly label: "Essential Tools Override";
3729
- readonly description: "Override the always-loaded built-in tools (default: read, bash, edit). Leave empty to use defaults.";
3729
+ readonly description: "Override the always-loaded built-in tools (default: read, bash, edit, write, find, eval). Leave empty to use defaults.";
3730
3730
  };
3731
3731
  };
3732
3732
  readonly "mcp.enableProjectConfig": {
@@ -4935,6 +4935,16 @@ export declare const SETTINGS_SCHEMA: {
4935
4935
  readonly description: "Enable Exa basic search, deep search, code search, and crawl tools";
4936
4936
  };
4937
4937
  };
4938
+ readonly "exa.searchDelayMs": {
4939
+ readonly type: "number";
4940
+ readonly default: 1000;
4941
+ readonly ui: {
4942
+ readonly tab: "providers";
4943
+ readonly group: "Services";
4944
+ readonly label: "Exa Search Delay";
4945
+ readonly description: "Minimum delay between Exa web search requests in milliseconds; set 0 to disable pacing";
4946
+ };
4947
+ };
4938
4948
  readonly "exa.enableResearcher": {
4939
4949
  readonly type: "boolean";
4940
4950
  readonly default: false;
@@ -5202,6 +5212,7 @@ export interface TtsrSettings {
5202
5212
  export interface ExaSettings {
5203
5213
  enabled: boolean;
5204
5214
  enableSearch: boolean;
5215
+ searchDelayMs: number;
5205
5216
  enableResearcher: boolean;
5206
5217
  enableWebsets: boolean;
5207
5218
  }
@@ -1,3 +1,4 @@
1
+ import { type NestedRepoPatch } from "../task/worktree";
1
2
  import type { ToolSession } from "../tools";
2
3
  import type { JsStatusEvent } from "./js/shared/types";
3
4
  import "../tools/review";
@@ -17,6 +18,24 @@ export interface EvalAgentResult {
17
18
  id: string;
18
19
  model?: string | string[];
19
20
  structured: boolean;
21
+ /** True iff this run executed inside an isolation worktree. */
22
+ isolated?: boolean;
23
+ /** Captured patch artifact (patch mode) — surfaced regardless of `apply`. */
24
+ patchPath?: string;
25
+ /** Captured branch (branch mode) — surfaced regardless of `apply`. */
26
+ branchName?: string;
27
+ /** Captured nested repository patches — surfaced for isolated `apply=false` manual application. */
28
+ nestedPatches?: NestedRepoPatch[];
29
+ /**
30
+ * Tri-state apply outcome for isolated runs:
31
+ * - `true` — apply ran (or had nothing to do) and left the repo clean.
32
+ * - `false` — apply attempted and failed; artifacts preserved.
33
+ * - `null` — caller opted out via `apply=false`.
34
+ * Omitted for non-isolated runs.
35
+ */
36
+ changesApplied?: boolean | null;
37
+ /** Human-readable isolation apply/merge summary; kept out of schema-backed `text`. */
38
+ isolationSummary?: string;
20
39
  };
21
40
  }
22
41
  /**
@@ -1,13 +1,7 @@
1
1
  import type { JsStatusEvent } from "./types";
2
2
  export interface HelperOptions {
3
- path?: string;
4
- hidden?: boolean;
5
- maxDepth?: number;
6
3
  limit?: number;
7
4
  offset?: number;
8
- reverse?: boolean;
9
- unique?: boolean;
10
- count?: boolean;
11
5
  }
12
6
  /**
13
7
  * Inputs the helper factory needs from its host runtime. `cwd` is a getter so the runtime
@@ -27,18 +21,12 @@ export interface HelperContext {
27
21
  }
28
22
  /**
29
23
  * The set of functions exposed to user code via `globalThis.__omp_helpers__`. The JS
30
- * prelude reads from this bag and attaches short aliases (`read`, `write`, `tree`, ...)
24
+ * prelude reads from this bag and attaches short aliases (`read`, `write`, `env`, ...)
31
25
  * onto the global scope.
32
26
  */
33
27
  export interface HelperBundle {
34
28
  read(rawPath: string, options?: HelperOptions): Promise<string>;
35
29
  writeFile(rawPath: string, data: unknown): Promise<string>;
36
- append(rawPath: string, content: string): Promise<string>;
37
- sortText(text: string, options?: HelperOptions): string;
38
- uniqText(text: string, options?: HelperOptions): string | Array<[number, string]>;
39
- counter(items: string | string[], options?: HelperOptions): Array<[number, string]>;
40
- diff(rawA: string, rawB: string): Promise<string>;
41
- tree(searchPath?: string, options?: HelperOptions): Promise<string>;
42
30
  env(key?: string, value?: string): string | Record<string, string> | undefined;
43
31
  }
44
32
  export declare function createHelpers(ctx: HelperContext): HelperBundle;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Structured status payload emitted by helpers (`read`, `write`, `tree`, etc.) and the
2
+ * Structured status payload emitted by helpers (`read`, `write`, `env`, etc.) and the
3
3
  * tool-call bridge. Surfaces to the model as part of `displays` so it has machine-readable
4
4
  * context about what side effects happened.
5
5
  */
@@ -5,7 +5,7 @@ export interface SessionSnapshot {
5
5
  sessionId: string;
6
6
  /**
7
7
  * On-disk roots the helpers substitute for internal-URL schemes
8
- * (e.g. `{ local: "/…/artifacts/local" }`). Lets `read`/`write`/`append`
8
+ * (e.g. `{ local: "/…/artifacts/local" }`). Lets `read`/`write`
9
9
  * accept `local://…` paths instead of writing a literal `local:/` directory.
10
10
  */
11
11
  localRoots?: Record<string, string>;
@@ -45,7 +45,7 @@ export interface PythonExecutorOptions {
45
45
  artifactPath?: string;
46
46
  artifactId?: string;
47
47
  /**
48
- * On-disk roots the prelude helpers (`read`/`write`/`append`) substitute for
48
+ * On-disk roots the prelude helpers (`read`/`write`) substitute for
49
49
  * internal-URL schemes (e.g. `{ local: "/…/artifacts/local" }`). Exported to
50
50
  * the kernel as `PI_EVAL_LOCAL_ROOTS` (JSON) so `write("local://x")` lands
51
51
  * where `read local://x` resolves instead of a literal `local:/` directory.
@@ -1,14 +1,22 @@
1
1
  import * as path from "node:path";
2
2
  /**
3
3
  * Compute the bunfs package root from the compiled binary's `import.meta.dir`
4
- * (or any stand-in supplied by tests). Bun 1.3 reports the bunfs mount root
5
- * (`/$bunfs/root` or `<drive>:\~BUN\root`) for imported modules as well as the
6
- * entrypoint, so the normal path is `<root>/packages`.
4
+ * (or any stand-in supplied by tests). Bun compiled binaries report one of:
7
5
  *
8
- * The suffix branch preserves correctness if a future Bun release switches to
9
- * module-specific `import.meta.dir` values inside compiled binaries, matching
10
- * the source layout:
11
- * `<bunfs>/packages/coding-agent/src/extensibility/plugins`.
6
+ * - the bunfs mount root itself `/$bunfs/root` or `<drive>:\~BUN\root` (Bun
7
+ * 1.2.x and early 1.3.x). Append `packages` for the canonical layout.
8
+ * - the bunfs mount root followed by the binary's basename — `//root/<bin>`
9
+ * on POSIX or `<drive>:\~BUN\root\<bin>.exe` on Windows (observed on Bun
10
+ * 1.3.14 with the cross-compiled `omp-darwin-arm64` release asset — issue
11
+ * #3329). The trailing segment is stripped so the result still lands on
12
+ * `<root>/packages`.
13
+ * - the module's own source directory if a future Bun release switches to
14
+ * module-specific `import.meta.dir` values:
15
+ * `<bunfs>/packages/coding-agent/src/extensibility/plugins`.
16
+ * The bunfs-root-with-binary branch slices the original `metaDir`, and
17
+ * `bunfsPath` uses a matching double-slash-preserving join, so the bunfs-native
18
+ * prefix is preserved verbatim — `path.posix.join` collapses `//root` to
19
+ * `/root`, but Bun's bunfs lookup is keyed on the exact `//root` form.
12
20
  *
13
21
  * Exported for tests; production callers use `BUNFS_PACKAGE_ROOT` below.
14
22
  */
@@ -22,6 +30,13 @@ export declare function __computeBunfsPackageRoot(metaDir: string, pathImpl?: ty
22
30
  * older cache entry, recreating mixed-runtime plugin loading.
23
31
  */
24
32
  export declare function __computeBundledSelfPackageRoot(metaDir: string, pathImpl?: typeof path): string;
33
+ /**
34
+ * Join a computed bunfs package root with descendants without collapsing
35
+ * Bun's POSIX `//root` mount prefix.
36
+ *
37
+ * Exported for tests; production callers use `bunfsPath` below.
38
+ */
39
+ export declare function __joinBunfsPath(root: string, segments: readonly string[], pathImpl?: typeof path): string;
25
40
  /**
26
41
  * Drop overrides whose targets are missing on disk so they can fall through to
27
42
  * the canonical-resolution path. Exported for the test seam in #2168.
@@ -1,19 +1,26 @@
1
1
  import type { InstalledPlugin } from "./types";
2
+ /** Installed plugin plus the root scope that supplied its runtime metadata. */
3
+ export interface ScopedInstalledPlugin extends InstalledPlugin {
4
+ scope: "user" | "project";
5
+ }
2
6
  /**
3
7
  * Get list of enabled plugins with their resolved configurations.
4
8
  *
5
- * Respects both global runtime config and project overrides. Iterates the
6
- * union of `<plugins>/package.json#dependencies` (`bun install`-installed
7
- * packages) and `<plugins>/omp-plugins.lock.json#plugins` (so locally
8
- * `plugin link`-symlinked extensions, which never get a dependency entry,
9
- * are still discovered). The optional `home` parameter pins the plugins
10
- * root for callers that need to enumerate plugins relative to a non-default
11
- * home (tests with a tempdir, discovery loaders threaded with
12
- * `LoadContext.home`).
9
+ * Enumerates two plugin roots in order: the user root
10
+ * (`getPluginsDir(home)`) and, when a project anchor (`.omp/` or `.git/`)
11
+ * exists at or above `cwd`, the project root
12
+ * (`<projectAnchor>/.omp/plugins`). Each root contributes the union of its
13
+ * `package.json#dependencies` and `omp-plugins.lock.json#plugins`. Project
14
+ * entries shadow user entries with the same package name, matching the
15
+ * shadow semantics of `MarketplaceManager.listInstalledPlugins`.
16
+ *
17
+ * The optional `home` parameter pins the user plugins root for callers that
18
+ * need to enumerate plugins relative to a non-default home (tests with a
19
+ * tempdir, discovery loaders threaded with `LoadContext.home`).
13
20
  */
14
21
  export declare function getEnabledPlugins(cwd: string, opts?: {
15
22
  home?: string;
16
- }): Promise<InstalledPlugin[]>;
23
+ }): Promise<ScopedInstalledPlugin[]>;
17
24
  /**
18
25
  * Declared manifest entries paired with their resolved file path. Returns one
19
26
  * record per declared entry — base entries first, then enabled-feature entries
@@ -0,0 +1,2 @@
1
+ /** Adds derived compatibility fields to tool event input without changing tool execution parameters. */
2
+ export declare function normalizeToolEventInput(toolName: string, input: Record<string, unknown>): Record<string, unknown>;
@@ -64,14 +64,6 @@ export interface RetentionTranscript {
64
64
  transcript: string | null;
65
65
  messageCount: number;
66
66
  }
67
- /**
68
- * Format messages into a retention transcript using `[role: ...]` markers.
69
- *
70
- * - When `retainFullWindow` is true, all messages are included (used when the
71
- * caller pre-sliced the window itself).
72
- * - Otherwise, only the last user turn (last user message → end) is retained.
73
- *
74
- * Messages are tag-stripped before framing to break the recall→retain loop.
75
- * Returns `{ transcript: null }` when nothing meaningful survives.
76
- */
77
67
  export declare function prepareRetentionTranscript(messages: HindsightMessage[], retainFullWindow?: boolean): RetentionTranscript;
68
+ /** Format only user-authored messages for memory fact/entity extraction. */
69
+ export declare function prepareUserRetentionTranscript(messages: HindsightMessage[]): RetentionTranscript;
@@ -8,7 +8,7 @@ export declare function resolveLocalRoot(options: LocalProtocolOptions, platform
8
8
  /** Resolve a local:// URL to an on-disk path under the active session's local root. */
9
9
  export declare function resolveLocalUrlToPath(input: string | InternalUrl, options: LocalProtocolOptions, platform?: NodeJS.Platform): string;
10
10
  /**
11
- * On-disk roots the eval helpers (`read`/`write`/`append`) substitute for
11
+ * On-disk roots the eval helpers (`read`/`write`) substitute for
12
12
  * internal-URL schemes so e.g. `write("local://x.md")` lands where a later
13
13
  * `read local://x.md` resolves — instead of a literal `local:/` directory under
14
14
  * the cwd (a stdlib `pathlib.Path`/`path.resolve` collapses `local://` to
@@ -17,6 +17,23 @@ export declare function resolveLocalUrlToPath(input: string | InternalUrl, optio
17
17
  * re-plumbing the worker boundary.
18
18
  */
19
19
  export declare function buildEvalUrlRoots(options: LocalProtocolOptions): Record<string, string>;
20
+ /**
21
+ * Resolve a local:// URL to a regular on-disk file, applying the same
22
+ * realpath + containment guarantees as {@link LocalProtocolHandler.resolve}
23
+ * but WITHOUT reading or UTF-8-decoding its contents. Returns null when there
24
+ * is no active session or when the URL targets the root listing or a directory;
25
+ * throws the handler's not-found and "escapes local root" errors for missing
26
+ * files and symlink escapes.
27
+ *
28
+ * Options are resolved via {@link LocalProtocolHandler.resolveOptions} so the
29
+ * caller-options → override → registry order matches router resolution exactly.
30
+ * The read tool uses this to detect and emit image files from their real path
31
+ * before the text-only resource contract would decode the binary into mojibake.
32
+ */
33
+ export declare function resolveLocalUrlToFile(input: string | InternalUrl, context?: ResolveContext): Promise<{
34
+ path: string;
35
+ size: number;
36
+ } | null>;
20
37
  /**
21
38
  * Protocol handler for local:// URLs.
22
39
  *
@@ -1,4 +1,5 @@
1
1
  import { type Args } from "./cli/args";
2
+ import { selectSession } from "./cli/session-picker";
2
3
  import { ModelRegistry } from "./config/model-registry";
3
4
  import { type ScopedModel } from "./config/model-resolver";
4
5
  import { Settings } from "./config/settings";
@@ -59,6 +60,7 @@ export declare function applyResolvedSystemPromptInputs(options: CreateAgentSess
59
60
  interface RunRootCommandDependencies {
60
61
  createAgentSession?: typeof createAgentSession;
61
62
  discoverAuthStorage?: typeof discoverAuthStorage;
63
+ selectSession?: typeof selectSession;
62
64
  runAcpMode?: RunAcpMode;
63
65
  settings?: Settings;
64
66
  forceSetupWizard?: boolean;
@@ -34,6 +34,8 @@ export declare class ChatTranscriptBuilder {
34
34
  get isEmpty(): boolean;
35
35
  /** Discard all components and rebuild the whole transcript from `entries`. */
36
36
  rebuild(entries: SessionMessageEntry[]): void;
37
+ /** Append newly persisted entries without rebuilding already rendered rows. */
38
+ append(entries: SessionMessageEntry[]): void;
37
39
  /** Toggle tool-output expansion across every expandable component. */
38
40
  setExpanded(expanded: boolean): void;
39
41
  get expanded(): boolean;
@@ -19,6 +19,7 @@ export declare const SPACE_HOLD_MECHANICAL_RUN = 2;
19
19
  /** Idle gap (ms) after the last repeated space that counts as the space bar being released, ending
20
20
  * the push-to-talk recording. Must comfortably exceed the OS key-repeat interval. */
21
21
  export declare const SPACE_HOLD_RELEASE_MS = 250;
22
+ export declare function extractBracketedPastePaths(data: string): string[] | undefined;
22
23
  export declare function extractBracketedImagePastePaths(data: string): string[] | undefined;
23
24
  export declare function extractBracketedImagePastePath(data: string): string | undefined;
24
25
  /**
@@ -86,6 +87,8 @@ export declare class CustomEditor extends Editor {
86
87
  onPasteImage?: () => Promise<boolean>;
87
88
  /** Called when a bracketed paste contains one or more image-file paths. */
88
89
  onPasteImagePath?: (path: string) => void | Promise<void>;
90
+ /** Called when a bracketed paste contains one or more non-image file paths. */
91
+ onPasteFilePath?: (path: string) => void | Promise<void>;
89
92
  /** Called when the configured raw text-paste shortcut is pressed. */
90
93
  onPasteTextRaw?: () => void;
91
94
  /** Called when the configured dequeue shortcut is pressed. */
@@ -14,6 +14,11 @@ import type { InstalledPluginSummary } from "../../extensibility/plugins/marketp
14
14
  import type { InstalledPlugin } from "../../extensibility/plugins/types";
15
15
  /**
16
16
  * Forwards a keystroke to `input`, but cancels via `onCancel` when the user presses Escape.
17
+ *
18
+ * Escape is decoded via `matchesKey` rather than a raw `\x1b` compare: inside the
19
+ * fullscreen settings overlay the kitty keyboard protocol is active (ghostty/kitty),
20
+ * where the Escape key arrives as the CSI-u sequence `\x1b[27u`, not a bare `\x1b`.
21
+ * The literal fallbacks preserve legacy single/double-escape on terminals without it.
17
22
  */
18
23
  export declare function handleInputOrEscape(data: string, input: {
19
24
  handleInput(data: string): void;
@@ -39,6 +39,12 @@ declare class SessionList implements Component {
39
39
  /** Replace the visible dataset, e.g. when toggling folder/all-projects scope. */
40
40
  setSessions(sessions: SessionInfo[], showCwd: boolean): void;
41
41
  removeSession(sessionPath: string): void;
42
+ /** Resolve a list-local rendered-line index to a filtered-session index. */
43
+ hitTestSession(line: number): number | undefined;
44
+ /** Wheel notch: move the selection one step (clamped, no wrap). */
45
+ handleWheel(delta: -1 | 1): void;
46
+ /** Mouse click: select the session under the pointer and resume it. */
47
+ selectAndConfirm(index: number): void;
42
48
  invalidate(): void;
43
49
  render(width: number): readonly string[];
44
50
  handleInput(keyData: string): void;
@@ -55,6 +61,13 @@ export interface SessionSelectorOptions {
55
61
  * Omitted only in tests; defaults to a conservative 24 rows.
56
62
  */
57
63
  getTerminalRows?: () => number;
64
+ /**
65
+ * Fill the whole viewport and pin the footer (hint + bottom border) to the
66
+ * last rows, so the footer stops drifting as the list window changes height.
67
+ * Set by the standalone `--resume` picker (fullscreen alternate screen); the
68
+ * in-editor selector leaves it off and renders compactly.
69
+ */
70
+ fillHeight?: boolean;
58
71
  }
59
72
  /**
60
73
  * Component that renders a session selector with optional confirmation dialog
@@ -63,6 +76,18 @@ export declare class SessionSelectorComponent extends Container {
63
76
  #private;
64
77
  constructor(sessions: SessionInfo[], onSelect: (session: SessionInfo) => void, onCancel: () => void, onExit: () => void, options?: SessionSelectorOptions);
65
78
  setOnRequestRender(callback: () => void): void;
79
+ /**
80
+ * Concatenate the children's renders (like {@link Container}) while recording
81
+ * the line where the session list begins, so the fullscreen picker can hit-
82
+ * test mouse rows against the live list window. SessionList rebuilds its lines
83
+ * every frame, so Container's reference-memoization never applied here.
84
+ *
85
+ * In fill-height mode the body is padded (or, on a cramped terminal, trimmed)
86
+ * to leave exactly enough room for the footer at the screen bottom, so the
87
+ * footer is always visible and never drifts as the list window resizes. The
88
+ * in-editor selector just appends the footer directly.
89
+ */
90
+ render(width: number): readonly string[];
66
91
  handleInput(keyData: string): void;
67
92
  getSessionList(): SessionList;
68
93
  }
@@ -90,6 +90,7 @@ export interface SegmentContext {
90
90
  } | null;
91
91
  };
92
92
  usage: {
93
+ tier?: string;
93
94
  fiveHour?: {
94
95
  percent: number;
95
96
  resetMinutes?: number;
@@ -3,6 +3,7 @@ import { type ProviderDetails, type UsageReport } from "@oh-my-pi/pi-ai";
3
3
  import type { CompactOptions } from "../../extensibility/extensions/types";
4
4
  import { theme } from "../../modes/theme/theme";
5
5
  import type { InteractiveModeContext } from "../../modes/types";
6
+ import type { OAuthAccountIdentity } from "../../session/auth-storage";
6
7
  import type { CompactMode } from "../../session/compact-modes";
7
8
  import { type ShakeMode } from "../../session/shake-types";
8
9
  export declare class CommandController {
@@ -43,3 +44,4 @@ export declare class CommandController {
43
44
  handleHandoffCommand(customInstructions?: string): Promise<void>;
44
45
  }
45
46
  export declare function renderProviderSection(details: ProviderDetails, uiTheme: Pick<typeof theme, "fg">): string;
47
+ export declare function renderUsageReports(reports: UsageReport[], uiTheme: typeof theme, nowMs: number, availableWidth: number, resolveActiveAccount?: (provider: string) => OAuthAccountIdentity | undefined): string;
@@ -1,6 +1,19 @@
1
1
  import { type AutocompleteProvider, type SlashCommand } from "@oh-my-pi/pi-tui";
2
2
  import type { InteractiveModeContext } from "../../modes/types";
3
3
  import { readImageFromClipboard, readTextFromClipboard } from "../../utils/clipboard";
4
+ /**
5
+ * Slash commands that may carry secrets in their arguments should never be
6
+ * persisted to history.
7
+ *
8
+ * - /login accepts three callback forms (redirect URL, query string, raw auth
9
+ * code) — all can contain OAuth code=/state= params.
10
+ * - /join <link> carries a 32-byte room key and optional write token.
11
+ * - /mcp add --token <token> carries a bearer token.
12
+ *
13
+ * The command name is extracted the same way as parseSlashCommand() — splitting
14
+ * on the earliest whitespace or colon — so /login:?code=... is correctly matched.
15
+ */
16
+ export declare function shouldSkipHistory(slashText: string): boolean;
4
17
  export declare class InputController {
5
18
  #private;
6
19
  private ctx;
@@ -25,6 +38,7 @@ export declare class InputController {
25
38
  abort?: boolean;
26
39
  currentText?: string;
27
40
  }): number;
41
+ handleFilePathPaste(filePath: string): Promise<void>;
28
42
  handleImagePathPaste(path: string): Promise<void>;
29
43
  handleImagePaste(): Promise<boolean>;
30
44
  handleClipboardTextRawPaste(): Promise<void>;
@@ -5,6 +5,17 @@ export declare class SelectorController {
5
5
  #private;
6
6
  private ctx;
7
7
  constructor(ctx: InteractiveModeContext);
8
+ /**
9
+ * Restore keyboard focus to whatever currently owns the editor slot. The
10
+ * slot can hold the editor itself or a hook selector/input/editor pushed
11
+ * in by `ExtensionUiController` — e.g. an approval prompt that fired while
12
+ * a fullscreen overlay was up. `overlayHandle.hide()` restores focus to
13
+ * the component focused when the overlay opened, which is stale in that
14
+ * case (the editor was swapped out): keys land on a hidden editor and the
15
+ * visible prompt receives nothing (issue #3349). Call this after the
16
+ * overlay hides to re-target focus at the visible slot owner.
17
+ */
18
+ focusActiveEditorArea(): void;
8
19
  /**
9
20
  * Shows a selector component in place of the editor.
10
21
  * @param create Factory that receives a `done` callback and returns the component and focus target
@@ -46,7 +46,7 @@ import type { CheckpointState } from "../tools/checkpoint";
46
46
  import { type TodoItem, type TodoPhase } from "../tools/todo";
47
47
  import type { ClientBridge } from "./client-bridge";
48
48
  import { type CustomMessage } from "./messages";
49
- import type { SessionContext } from "./session-context";
49
+ import type { BuildSessionContextOptions, SessionContext } from "./session-context";
50
50
  import type { BranchSummaryEntry, NewSessionOptions } from "./session-entries";
51
51
  import type { SessionManager } from "./session-manager";
52
52
  import type { ShakeMode, ShakeResult } from "./shake-types";
@@ -590,12 +590,12 @@ export declare class AgentSession {
590
590
  }[];
591
591
  buildDisplaySessionContext(): SessionContext;
592
592
  /**
593
- * Full-history transcript for TUI display: every path entry in
594
- * chronological order with compactions rendered inline at the point they
595
- * fired (instead of replacing prior history). Display-only — NEVER feed
596
- * the result to `agent.replaceMessages` or a provider.
593
+ * Transcript for TUI display. Full history is kept for export/resume-style
594
+ * callers; live chat can collapse compacted history to keep the hot render
595
+ * surface bounded. Display-only — NEVER feed the result to
596
+ * `agent.replaceMessages` or a provider.
597
597
  */
598
- buildTranscriptSessionContext(): SessionContext;
598
+ buildTranscriptSessionContext(options?: Pick<BuildSessionContextOptions, "collapseCompactedHistory">): SessionContext;
599
599
  /** Convert session messages using the same pre-LLM pipeline as the active session. */
600
600
  convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]>;
601
601
  /** Apply session-level stream hooks to a direct side request. */
@@ -0,0 +1,3 @@
1
+ import type { Context, Model } from "@oh-my-pi/pi-ai";
2
+ /** Drops oldest transient image blocks so outgoing vision requests fit the active provider's image cap. */
3
+ export declare function clampProviderContextImages(context: Context, model: Model): Context;
@@ -32,13 +32,14 @@ export declare function getRestorableSessionModels(models: Readonly<Record<strin
32
32
  export declare function getLatestCompactionEntry(entries: SessionEntry[]): CompactionEntry | null;
33
33
  export interface BuildSessionContextOptions {
34
34
  /**
35
- * Build the full-history display transcript instead of the LLM context:
36
- * every path entry in chronological order, with each compaction emitted
37
- * inline as a `compactionSummary` message at the position it fired rather
38
- * than replacing the history before it. Display-only — never send the
39
- * result to a provider.
35
+ * Build the display transcript instead of the LLM context. By default this
36
+ * preserves every path entry with compactions inline; set
37
+ * `collapseCompactedHistory` for the live TUI surface to render only the
38
+ * latest compacted tail.
40
39
  */
41
40
  transcript?: boolean;
41
+ /** In transcript mode, elide entries replaced by the latest compaction. */
42
+ collapseCompactedHistory?: boolean;
42
43
  }
43
44
  /**
44
45
  * Build the session context from entries using tree traversal.