@oh-my-pi/pi-coding-agent 16.1.16 → 16.1.18

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 (75) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/cli.js +16646 -16608
  3. package/dist/types/auto-thinking/classifier.d.ts +4 -2
  4. package/dist/types/cli/usage-cli.d.ts +14 -0
  5. package/dist/types/commands/token.d.ts +9 -0
  6. package/dist/types/config/model-discovery.d.ts +1 -0
  7. package/dist/types/config/model-registry.d.ts +4 -0
  8. package/dist/types/extensibility/plugins/legacy-pi-bundled-registry.d.ts +7 -0
  9. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +34 -15
  10. package/dist/types/extensibility/plugins/loader.d.ts +16 -9
  11. package/dist/types/extensibility/tool-event-input.d.ts +2 -0
  12. package/dist/types/hindsight/content.d.ts +2 -10
  13. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  14. package/dist/types/modes/components/custom-editor.d.ts +3 -0
  15. package/dist/types/modes/components/extensions/extension-dashboard.d.ts +26 -10
  16. package/dist/types/modes/components/extensions/extension-list.d.ts +13 -0
  17. package/dist/types/modes/components/status-line/types.d.ts +1 -0
  18. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  19. package/dist/types/modes/controllers/input-controller.d.ts +14 -0
  20. package/dist/types/modes/controllers/selector-controller.d.ts +11 -0
  21. package/dist/types/session/agent-session.d.ts +6 -6
  22. package/dist/types/session/provider-image-budget.d.ts +3 -0
  23. package/dist/types/session/session-context.d.ts +6 -5
  24. package/dist/types/task/parallel.d.ts +4 -0
  25. package/dist/types/thinking.d.ts +8 -1
  26. package/dist/types/tiny/title-client.d.ts +2 -2
  27. package/dist/types/utils/tools-manager.d.ts +2 -0
  28. package/package.json +12 -12
  29. package/scripts/build-binary.ts +9 -16
  30. package/src/auto-thinking/classifier.ts +7 -2
  31. package/src/cli/profile-alias.ts +38 -7
  32. package/src/cli/usage-cli.ts +40 -5
  33. package/src/commands/token.ts +54 -0
  34. package/src/config/model-discovery.ts +59 -8
  35. package/src/config/model-registry.ts +74 -3
  36. package/src/discovery/omp-extension-roots.ts +1 -3
  37. package/src/extensibility/extensions/wrapper.ts +3 -2
  38. package/src/extensibility/hooks/tool-wrapper.ts +4 -3
  39. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +40 -0
  40. package/src/extensibility/plugins/legacy-pi-compat.ts +240 -90
  41. package/src/extensibility/plugins/loader.ts +71 -23
  42. package/src/extensibility/plugins/marketplace/manager.ts +134 -0
  43. package/src/extensibility/tool-event-input.ts +57 -0
  44. package/src/hindsight/content.ts +21 -12
  45. package/src/mcp/tool-bridge.ts +27 -2
  46. package/src/mnemopi/state.ts +5 -2
  47. package/src/modes/components/agent-transcript-viewer.ts +193 -41
  48. package/src/modes/components/chat-transcript-builder.ts +6 -0
  49. package/src/modes/components/custom-editor.test.ts +18 -1
  50. package/src/modes/components/custom-editor.ts +77 -45
  51. package/src/modes/components/extensions/extension-dashboard.ts +221 -165
  52. package/src/modes/components/extensions/extension-list.ts +66 -33
  53. package/src/modes/components/hook-editor.ts +15 -2
  54. package/src/modes/components/settings-selector.ts +2 -2
  55. package/src/modes/components/status-line/component.ts +52 -8
  56. package/src/modes/components/status-line/segments.ts +5 -1
  57. package/src/modes/components/status-line/types.ts +1 -0
  58. package/src/modes/components/welcome.ts +12 -14
  59. package/src/modes/controllers/command-controller.ts +16 -5
  60. package/src/modes/controllers/input-controller.ts +115 -3
  61. package/src/modes/controllers/selector-controller.ts +23 -1
  62. package/src/modes/interactive-mode.ts +3 -3
  63. package/src/modes/utils/ui-helpers.ts +3 -3
  64. package/src/sdk.ts +8 -10
  65. package/src/session/agent-session.ts +193 -49
  66. package/src/session/provider-image-budget.ts +86 -0
  67. package/src/session/session-context.ts +14 -7
  68. package/src/session/session-storage.ts +24 -2
  69. package/src/session/snapcompact-inline.ts +19 -3
  70. package/src/slash-commands/builtin-registry.ts +0 -22
  71. package/src/slash-commands/helpers/usage-report.ts +9 -1
  72. package/src/task/parallel.ts +6 -1
  73. package/src/thinking.ts +9 -2
  74. package/src/tiny/title-client.ts +75 -21
  75. package/src/utils/tools-manager.ts +67 -10
@@ -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. */
@@ -78,4 +78,18 @@ export declare function formatUsageBreakdown(reports: UsageReport[], accounts: U
78
78
  * peak-per-bucket sparkline per limit window plus latest/peak percentages.
79
79
  */
80
80
  export declare function formatUsageHistory(entries: UsageHistoryEntry[], sinceMs: number, nowMs: number, redaction?: Map<string, string>): string;
81
+ /**
82
+ * Keep only accounts worth a usage row: those whose provider has a usage
83
+ * provider, so a missing report is a real gap rather than the absence of any
84
+ * usage concept. Providers with no usage endpoint (web-search keys, local /
85
+ * keyless servers, inference providers without a usage API) would only ever
86
+ * render as noise, so they are dropped.
87
+ *
88
+ * `hasUsageProvider` is injected (in practice {@link AuthStorage.usageProviderFor})
89
+ * so custom/broker resolvers stay authoritative — no provider list is duplicated
90
+ * here. An explicit `--provider` request bypasses the cull, so
91
+ * `omp usage --provider xai` can still confirm the stored credential has no
92
+ * usage endpoint.
93
+ */
94
+ export declare function selectReportableAccounts(accounts: UsageAccountIdentity[], hasUsageProvider: (provider: string) => boolean, explicitProvider?: string): UsageAccountIdentity[];
81
95
  export declare function runUsageCommand(cmd: UsageCommandArgs): Promise<void>;
@@ -19,6 +19,15 @@ export default class Token extends Command {
19
19
  description: string;
20
20
  default: boolean;
21
21
  };
22
+ account: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"integer"> & {
23
+ char: string;
24
+ description: string;
25
+ };
26
+ list: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
27
+ char: string;
28
+ description: string;
29
+ default: boolean;
30
+ };
22
31
  };
23
32
  static examples: string[];
24
33
  run(): Promise<void>;
@@ -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
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Canonical specifier → live module namespace. Keys MUST match the right-hand
3
+ * side of `bundledRegistryVirtualSpecifier(...)` calls in
4
+ * `legacy-pi-compat.ts`; the synthesizer enumerates each namespace's own
5
+ * enumerable exports at extension load time.
6
+ */
7
+ export declare const BUNDLED_PI_REGISTRY: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
@@ -1,18 +1,16 @@
1
1
  import * as path from "node:path";
2
2
  /**
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`.
7
- *
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`.
12
- *
13
- * Exported for tests; production callers use `BUNFS_PACKAGE_ROOT` below.
3
+ * Test seam: builds the synthetic ES module source for a virtual specifier
4
+ * against an explicit registry. Pure (no globalThis read); the emitted source
5
+ * still routes runtime lookups through `globalThis[BUNDLED_REGISTRY_GLOBAL]`.
6
+ */
7
+ export declare function __synthesizeLegacyPiBundledSourceWithRegistry(registryKey: string, registry: Readonly<Record<string, Readonly<Record<string, unknown>>>>): string;
8
+ /**
9
+ * Test seam: returns the globalThis key the synthetic loader reads from. Tests
10
+ * assert that the emitted source addresses the exact stash key the install
11
+ * function writes to, so a rename can't break extension loads silently.
14
12
  */
15
- export declare function __computeBunfsPackageRoot(metaDir: string, pathImpl?: typeof path): string;
13
+ export declare function __getLegacyPiBundledRegistryGlobal(): string;
16
14
  /**
17
15
  * Compute the package root for the npm prebuilt `dist/cli.js` bundle.
18
16
  *
@@ -23,10 +21,31 @@ export declare function __computeBunfsPackageRoot(metaDir: string, pathImpl?: ty
23
21
  */
24
22
  export declare function __computeBundledSelfPackageRoot(metaDir: string, pathImpl?: typeof path): string;
25
23
  /**
26
- * Drop overrides whose targets are missing on disk so they can fall through to
27
- * the canonical-resolution path. Exported for the test seam in #2168.
24
+ * Resolve the path the TypeBox compatibility shim ships at, then drop it when
25
+ * the source file is missing.
26
+ *
27
+ * In compiled-binary mode the shim is served through the
28
+ * `omp-legacy-pi-bundled:` virtual namespace (issue #3423) — bunfs paths are
29
+ * unreachable on Bun 1.3.14+, so the virtual specifier is always available and
30
+ * needs no filesystem probe. In dev / source-link / installed-package mode the
31
+ * shim is an on-disk source file; validation mirrors
32
+ * `__validateLegacyPiPackageRootOverrides` (#2168): if the computed candidate
33
+ * doesn't exist (e.g. an install that dropped the source — issue #3414),
34
+ * `resolveTypeBoxSpecifier` returns `undefined` and
35
+ * `rewriteLegacyExtensionSource` leaves bare `typebox` / `@sinclair/typebox`
36
+ * specifiers alone, so Bun falls through to native resolution against the
37
+ * extension's own `node_modules`.
38
+ *
39
+ * Exported for tests; production callers use `TYPEBOX_SHIM_PATH`.
40
+ */
41
+ export declare function __resolveTypeBoxShimPath(isCompiled: boolean, sourcePath: string, pathExistsSync?: (p: string) => boolean): string | null;
42
+ /**
43
+ * Drop overrides whose filesystem targets are missing so they can fall
44
+ * through to the canonical-resolution path. Virtual `omp-legacy-pi-bundled:`
45
+ * entries always pass — the bundled registry is the source of truth in
46
+ * compiled-binary mode where bunfs paths are unreachable (issue #3423).
28
47
  *
29
- * `pathExistsSync` defaults to `fs.existsSync`; the tests inject a stub to
48
+ * `pathExistsSync` defaults to `fs.existsSync`; tests inject a stub to
30
49
  * simulate the missing-entrypoint failure mode without touching the real FS.
31
50
  */
32
51
  export declare function __validateLegacyPiPackageRootOverrides(candidates: Record<string, string>, pathExistsSync?: (p: string) => boolean): Record<string, string>;
@@ -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;
@@ -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. */
@@ -1,19 +1,29 @@
1
1
  /**
2
- * ExtensionDashboard - Tabbed layout for the Extension Control Center.
2
+ * ExtensionDashboard - Fullscreen alternate-screen control center for extensions.
3
3
  *
4
- * Layout:
5
- * - Top: Horizontal tab bar for provider selection
6
- * - Body: 2-column grid (inventory list | preview panel)
4
+ * Chrome mirrors the `/settings` overlay: a titled rounded box, a shared
5
+ * {@link TabBar} for provider selection, and a two-column body (inventory list |
6
+ * inspector). Both panes are mouse-aware wheel scrolls, hover highlights, and
7
+ * clicks select/activate — routed from a single SGR-mouse handler.
7
8
  *
8
9
  * Navigation:
9
- * - TAB/Shift+TAB: Cycle through provider tabs
10
- * - Up/Down/j/k: Navigate list
11
- * - Space: Toggle selected item (or master switch)
12
- * - Esc: Close dashboard (clears search first if active)
10
+ * - Tab/Shift+Tab or ←/→: switch provider tab
11
+ * - Up/Down/j/k or wheel: move list selection
12
+ * - Space/Enter or click: toggle selected item (or provider master switch)
13
+ * - Wheel over the inspector: scroll the detail pane
14
+ * - Esc: clear search (if active) then close
13
15
  */
14
- import { Container } from "@oh-my-pi/pi-tui";
16
+ import { type Component, type Tab } from "@oh-my-pi/pi-tui";
15
17
  import { Settings } from "../../../config/settings";
16
- export declare class ExtensionDashboard extends Container {
18
+ import type { ProviderTab } from "./types";
19
+ /**
20
+ * Map dashboard provider tabs to {@link TabBar} tabs. Empty *enabled* providers
21
+ * are muted — skipped by keyboard nav and unclickable; disabled providers stay
22
+ * selectable (with a leading disabled glyph) so their master switch can be
23
+ * re-enabled from the list. The "all" tab is never muted or marked.
24
+ */
25
+ export declare function buildTabBarTabs(tabs: ProviderTab[]): Tab[];
26
+ export declare class ExtensionDashboard implements Component {
17
27
  #private;
18
28
  private readonly cwd;
19
29
  private readonly settings;
@@ -22,6 +32,12 @@ export declare class ExtensionDashboard extends Container {
22
32
  onRequestRender?: () => void;
23
33
  private constructor();
24
34
  static create(cwd: string, settings?: Settings | null, terminalHeight?: number): Promise<ExtensionDashboard>;
35
+ /**
36
+ * Fullscreen frame: titled top border, the tab row(s), a divider, the
37
+ * two-column body sized to fill the viewport, a divider, the footer hint, and
38
+ * the bottom border. Records row geometry for mouse hit-testing.
39
+ */
25
40
  render(width: number): readonly string[];
41
+ invalidate(): void;
26
42
  handleInput(data: string): void;
27
43
  }
@@ -35,5 +35,18 @@ export declare class ExtensionList implements Component {
35
35
  clearSearch(): void;
36
36
  invalidate(): void;
37
37
  render(width: number): readonly string[];
38
+ /** Highlight the row under the pointer (null clears). */
39
+ setHoverIndex(index: number | null): void;
40
+ /**
41
+ * Map a 0-based line within this component's render to the absolute list-item
42
+ * index, or null when the line is the search banner, a padding row, or outside
43
+ * the visible window. The first two lines are the search banner and a blank
44
+ * separator; item rows follow, windowed at the current scroll offset.
45
+ */
46
+ hitTest(line: number): number | null;
47
+ /** Wheel notch: move the selection (and the inspector) one row. */
48
+ handleWheel(delta: -1 | 1): void;
49
+ /** Click: select the row under the pointer, or activate it when already selected. */
50
+ handleClick(line: number): void;
38
51
  handleInput(data: string): void;
39
52
  }
@@ -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.
@@ -25,6 +25,10 @@ export interface ParallelResult<R> {
25
25
  export declare function mapWithConcurrencyLimit<T, R>(items: T[], concurrency: number, fn: (item: T, index: number, signal: AbortSignal) => Promise<R>, signal?: AbortSignal): Promise<ParallelResult<R>>;
26
26
  /**
27
27
  * Simple counting semaphore for limiting concurrency across independently-scheduled async work.
28
+ *
29
+ * `max <= 0` (or any non-finite input) means unbounded — every `acquire()` resolves
30
+ * immediately — matching `task.maxConcurrency = 0`'s "Unlimited" semantics in the
31
+ * settings UI ([#3305](https://github.com/can1357/oh-my-pi/issues/3305)).
28
32
  */
29
33
  export declare class Semaphore {
30
34
  #private;
@@ -77,8 +77,15 @@ export declare function parseCliThinkingLevel(value: string | null | undefined):
77
77
  * above Low (falling back to the full supported set only when the model maxes
78
78
  * out below Low). Within that pool the request snaps to the highest level not
79
79
  * exceeding it, or the pool minimum when the request is below the pool.
80
+ *
81
+ * Returns `undefined` for reasoning-capable models without a controllable
82
+ * effort surface (`thinking.efforts` empty — e.g. devin-agent models, where
83
+ * Cascade selects effort by routing to sibling model ids). Matches
84
+ * {@link clampThinkingLevelForModel}: with no effort to pick, `auto` must not
85
+ * forward a concrete effort that would then trip {@link requireSupportedEffort}
86
+ * downstream.
80
87
  */
81
- export declare function clampAutoThinkingEffort(model: Model | undefined, effort: Effort): Effort;
88
+ export declare function clampAutoThinkingEffort(model: Model | undefined, effort: Effort): Effort | undefined;
82
89
  /**
83
90
  * The provisional concrete level shown while `auto` is configured but before a
84
91
  * turn has been classified. Prefers the model's `defaultLevel`, otherwise High,
@@ -1,4 +1,4 @@
1
- import { type SpawnedSubprocess, type WorkerHandle } from "../subprocess/worker-client";
1
+ import { type RefCountedWorkerHandle, type SpawnedSubprocess } from "../subprocess/worker-client";
2
2
  import type { TinyTitleProgressEvent, TinyTitleWorkerInbound, TinyTitleWorkerOutbound } from "./title-protocol";
3
3
  export interface TinyTitleDownloadOptions {
4
4
  signal?: AbortSignal;
@@ -44,7 +44,7 @@ export declare function tinyWorkerEnv(): Record<string, string>;
44
44
  export declare function createTinyTitleSubprocess(): SpawnedSubprocess<TinyTitleWorkerOutbound>;
45
45
  export declare class TinyTitleClient {
46
46
  #private;
47
- constructor(spawnWorker?: () => WorkerHandle<TinyTitleWorkerInbound, TinyTitleWorkerOutbound>);
47
+ constructor(spawnWorker?: () => RefCountedWorkerHandle<TinyTitleWorkerInbound, TinyTitleWorkerOutbound>);
48
48
  onProgress(listener: (event: TinyTitleProgressEvent) => void): () => void;
49
49
  generate(modelKey: string, message: string, signal?: AbortSignal): Promise<string | null>;
50
50
  generate(modelKey: string, message: string, options?: TinyTitleGenerateOptions): Promise<string | null>;
@@ -1,6 +1,8 @@
1
1
  export declare function ffmpegAssetName(_version: string, plat: string, architecture: string): string | null;
2
2
  export type ToolName = "sd" | "sg" | "yt-dlp" | "trafilatura" | "ffmpeg";
3
3
  export declare function getToolPath(tool: ToolName): string | null;
4
+ /** Download a tool asset without handing the streaming Response to Bun.write. */
5
+ export declare function downloadFile(url: string, dest: string, signal?: AbortSignal): Promise<void>;
4
6
  type EnsureToolOptions = {
5
7
  signal?: AbortSignal;
6
8
  silent?: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "16.1.16",
4
+ "version": "16.1.18",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -48,17 +48,17 @@
48
48
  "@agentclientprotocol/sdk": "0.25.0",
49
49
  "@babel/parser": "^7.29.7",
50
50
  "@mozilla/readability": "^0.6.0",
51
- "@oh-my-pi/hashline": "16.1.16",
52
- "@oh-my-pi/omp-stats": "16.1.16",
53
- "@oh-my-pi/pi-agent-core": "16.1.16",
54
- "@oh-my-pi/pi-ai": "16.1.16",
55
- "@oh-my-pi/pi-catalog": "16.1.16",
56
- "@oh-my-pi/pi-mnemopi": "16.1.16",
57
- "@oh-my-pi/pi-natives": "16.1.16",
58
- "@oh-my-pi/pi-tui": "16.1.16",
59
- "@oh-my-pi/pi-utils": "16.1.16",
60
- "@oh-my-pi/pi-wire": "16.1.16",
61
- "@oh-my-pi/snapcompact": "16.1.16",
51
+ "@oh-my-pi/hashline": "16.1.18",
52
+ "@oh-my-pi/omp-stats": "16.1.18",
53
+ "@oh-my-pi/pi-agent-core": "16.1.18",
54
+ "@oh-my-pi/pi-ai": "16.1.18",
55
+ "@oh-my-pi/pi-catalog": "16.1.18",
56
+ "@oh-my-pi/pi-mnemopi": "16.1.18",
57
+ "@oh-my-pi/pi-natives": "16.1.18",
58
+ "@oh-my-pi/pi-tui": "16.1.18",
59
+ "@oh-my-pi/pi-utils": "16.1.18",
60
+ "@oh-my-pi/pi-wire": "16.1.18",
61
+ "@oh-my-pi/snapcompact": "16.1.18",
62
62
  "@opentelemetry/api": "^1.9.1",
63
63
  "@opentelemetry/context-async-hooks": "^2.7.1",
64
64
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -82,22 +82,15 @@ async function main(): Promise<void> {
82
82
  "--root",
83
83
  ".",
84
84
  "./packages/coding-agent/src/cli.ts",
85
- // Legacy pi-* extension compat entrypoints served by
86
- // `legacy-pi-compat.ts`. These are reached via computed bunfs paths
87
- // (which `--compile`'s static analyzer cannot trace), so each must be
88
- // listed here to land in bunfs at
89
- // `/$bunfs/root/packages/<pkg>/<entry>.js`. The coding-agent's own
90
- // `./src/index.ts` is intentionally NOT listed: bun --compile silently
91
- // breaks the CLI entry when the same package's barrel appears as an
92
- // extra entrypoint (issue #1474), so legacy `pi-coding-agent` imports
93
- // resolve through `legacy-pi-coding-agent-shim.ts` instead.
94
- "./packages/agent/src/index.ts",
95
- "./packages/natives/native/index.js",
96
- "./packages/tui/src/index.ts",
97
- "./packages/utils/src/index.ts",
98
- "./packages/coding-agent/src/extensibility/typebox.ts",
99
- "./packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts",
100
- "./packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts",
85
+ // Legacy pi-* extension compat surfaces (host packages + shims)
86
+ // were previously listed as explicit `--compile` entries so the
87
+ // rewrite path could emit `/$bunfs/root/...` URLs against them.
88
+ // Bun 1.3.14 made bunfs files unreachable at runtime (issue
89
+ // #3423), so `legacy-pi-compat.ts` now serves them through a
90
+ // virtual namespace backed by `legacy-pi-bundled-registry.ts`,
91
+ // which static-imports each surface the bundler already
92
+ // includes them via the main module graph, so no `--compile`
93
+ // extras are required.
101
94
  "--outfile",
102
95
  `packages/coding-agent/dist/${outName}`,
103
96
  ],
@@ -55,10 +55,15 @@ export interface ClassifyDifficultyDeps {
55
55
  }
56
56
 
57
57
  /**
58
- * Classify `promptText` and return a concrete effort clamped to `deps.model`.
58
+ * Classify `promptText` and return a concrete effort clamped to `deps.model`,
59
+ * or `undefined` when the model has no controllable effort surface (auto has
60
+ * nothing to pick — the caller leaves the prior reasoning level in place).
59
61
  * @throws when the backend cannot produce a usable classification.
60
62
  */
61
- export async function classifyDifficulty(promptText: string, deps: ClassifyDifficultyDeps): Promise<Effort> {
63
+ export async function classifyDifficulty(
64
+ promptText: string,
65
+ deps: ClassifyDifficultyDeps,
66
+ ): Promise<Effort | undefined> {
62
67
  const backend = deps.settings.get("providers.autoThinkingModel");
63
68
  const input = prepareClassifierInput(promptText);
64
69
  const effort =