@oh-my-pi/pi-coding-agent 16.1.16 → 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.
- package/CHANGELOG.md +30 -0
- package/dist/cli.js +2749 -2726
- package/dist/types/auto-thinking/classifier.d.ts +4 -2
- package/dist/types/config/model-discovery.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +22 -7
- package/dist/types/extensibility/plugins/loader.d.ts +16 -9
- package/dist/types/extensibility/tool-event-input.d.ts +2 -0
- package/dist/types/hindsight/content.d.ts +2 -10
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/status-line/types.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +14 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +11 -0
- package/dist/types/session/agent-session.d.ts +6 -6
- package/dist/types/session/provider-image-budget.d.ts +3 -0
- package/dist/types/session/session-context.d.ts +6 -5
- package/dist/types/task/parallel.d.ts +4 -0
- package/dist/types/thinking.d.ts +8 -1
- package/dist/types/tiny/title-client.d.ts +2 -2
- package/dist/types/utils/tools-manager.d.ts +2 -0
- package/package.json +12 -12
- package/src/auto-thinking/classifier.ts +7 -2
- package/src/cli/profile-alias.ts +38 -7
- package/src/cli/usage-cli.ts +5 -1
- package/src/config/model-discovery.ts +59 -8
- package/src/config/model-registry.ts +74 -3
- package/src/discovery/omp-extension-roots.ts +1 -3
- package/src/extensibility/extensions/wrapper.ts +3 -2
- package/src/extensibility/hooks/tool-wrapper.ts +4 -3
- package/src/extensibility/plugins/legacy-pi-compat.ts +40 -8
- package/src/extensibility/plugins/loader.ts +71 -23
- package/src/extensibility/plugins/marketplace/manager.ts +134 -0
- package/src/extensibility/tool-event-input.ts +57 -0
- package/src/hindsight/content.ts +21 -12
- package/src/mcp/tool-bridge.ts +27 -2
- package/src/mnemopi/state.ts +5 -2
- package/src/modes/components/agent-transcript-viewer.ts +193 -41
- package/src/modes/components/chat-transcript-builder.ts +6 -0
- package/src/modes/components/custom-editor.test.ts +18 -1
- package/src/modes/components/custom-editor.ts +77 -45
- package/src/modes/components/hook-editor.ts +15 -2
- package/src/modes/components/settings-selector.ts +2 -2
- package/src/modes/components/status-line/component.ts +52 -8
- package/src/modes/components/status-line/segments.ts +5 -1
- package/src/modes/components/status-line/types.ts +1 -0
- package/src/modes/components/welcome.ts +12 -14
- package/src/modes/controllers/command-controller.ts +16 -5
- package/src/modes/controllers/input-controller.ts +115 -3
- package/src/modes/controllers/selector-controller.ts +19 -1
- package/src/modes/interactive-mode.ts +3 -3
- package/src/modes/utils/ui-helpers.ts +3 -3
- package/src/sdk.ts +8 -10
- package/src/session/agent-session.ts +193 -49
- package/src/session/provider-image-budget.ts +86 -0
- package/src/session/session-context.ts +14 -7
- package/src/session/session-storage.ts +24 -2
- package/src/session/snapcompact-inline.ts +19 -3
- package/src/slash-commands/builtin-registry.ts +0 -22
- package/src/slash-commands/helpers/usage-report.ts +9 -1
- package/src/task/parallel.ts +6 -1
- package/src/thinking.ts +9 -2
- package/src/tiny/title-client.ts +75 -21
- 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. */
|
|
@@ -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
|
|
@@ -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
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the
|
|
11
|
-
* `<
|
|
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
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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<
|
|
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
|
|
@@ -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. */
|
|
@@ -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
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
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. */
|
|
@@ -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
|
|
36
|
-
* every path entry
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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;
|
package/dist/types/thinking.d.ts
CHANGED
|
@@ -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
|
|
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?: () =>
|
|
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.
|
|
4
|
+
"version": "16.1.17",
|
|
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.
|
|
52
|
-
"@oh-my-pi/omp-stats": "16.1.
|
|
53
|
-
"@oh-my-pi/pi-agent-core": "16.1.
|
|
54
|
-
"@oh-my-pi/pi-ai": "16.1.
|
|
55
|
-
"@oh-my-pi/pi-catalog": "16.1.
|
|
56
|
-
"@oh-my-pi/pi-mnemopi": "16.1.
|
|
57
|
-
"@oh-my-pi/pi-natives": "16.1.
|
|
58
|
-
"@oh-my-pi/pi-tui": "16.1.
|
|
59
|
-
"@oh-my-pi/pi-utils": "16.1.
|
|
60
|
-
"@oh-my-pi/pi-wire": "16.1.
|
|
61
|
-
"@oh-my-pi/snapcompact": "16.1.
|
|
51
|
+
"@oh-my-pi/hashline": "16.1.17",
|
|
52
|
+
"@oh-my-pi/omp-stats": "16.1.17",
|
|
53
|
+
"@oh-my-pi/pi-agent-core": "16.1.17",
|
|
54
|
+
"@oh-my-pi/pi-ai": "16.1.17",
|
|
55
|
+
"@oh-my-pi/pi-catalog": "16.1.17",
|
|
56
|
+
"@oh-my-pi/pi-mnemopi": "16.1.17",
|
|
57
|
+
"@oh-my-pi/pi-natives": "16.1.17",
|
|
58
|
+
"@oh-my-pi/pi-tui": "16.1.17",
|
|
59
|
+
"@oh-my-pi/pi-utils": "16.1.17",
|
|
60
|
+
"@oh-my-pi/pi-wire": "16.1.17",
|
|
61
|
+
"@oh-my-pi/snapcompact": "16.1.17",
|
|
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",
|
|
@@ -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(
|
|
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 =
|
package/src/cli/profile-alias.ts
CHANGED
|
@@ -198,37 +198,68 @@ export function resolveProfileAliasCommandFromProcess(
|
|
|
198
198
|
if (!runtime || !script || !/\.[cm]?[jt]s$/.test(script)) return DEFAULT_ALIAS_COMMAND;
|
|
199
199
|
|
|
200
200
|
const scriptPath = path.resolve(cwd, script);
|
|
201
|
-
|
|
201
|
+
// Normalize to forward slashes for POSIX shell fields — bash/zsh/fish
|
|
202
|
+
// can't resolve backslash-separated paths, even on Windows (Git Bash, WSL).
|
|
203
|
+
const posixScriptPath = scriptPath.replace(/\\/g, "/");
|
|
204
|
+
const posixRuntime = runtime.replace(/\\/g, "/");
|
|
205
|
+
const posix = `${quoteForShell(posixRuntime)} ${quoteForShell(posixScriptPath)}`;
|
|
202
206
|
return {
|
|
203
|
-
display: `${
|
|
207
|
+
display: `${posixRuntime} ${posixScriptPath}`,
|
|
204
208
|
posix,
|
|
205
209
|
fish: posix,
|
|
206
210
|
powerShell: `${quoteForPowerShell(runtime)} ${quoteForPowerShell(scriptPath)}`,
|
|
207
211
|
};
|
|
208
212
|
}
|
|
209
213
|
|
|
214
|
+
/** Normalize backslashes to forward slashes for POSIX-shell paths.
|
|
215
|
+
* path.posix.join only adds / separators — it preserves existing backslashes
|
|
216
|
+
* in input segments like homeDir ("C:\Users\me"), producing mixed paths.
|
|
217
|
+
* Windows UNC paths (\\server\share) become //server/share — path.posix.join
|
|
218
|
+
* would collapse the leading // to /, so we restore it after joining. */
|
|
219
|
+
function toPosix(p: string): string {
|
|
220
|
+
return p.replace(/\\/g, "/");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Like path.posix.join, but preserves leading // (UNC roots) which
|
|
224
|
+
* path.posix.join collapses to a single /. */
|
|
225
|
+
function posixJoinUnc(...segments: string[]): string {
|
|
226
|
+
const joined = path.posix.join(...segments);
|
|
227
|
+
// path.posix.join normalizes // at the start to /, breaking UNC roots.
|
|
228
|
+
// Restore it if any input segment started with // (a toPosix'd UNC path).
|
|
229
|
+
if (segments.some(s => s.startsWith("//") && !s.startsWith("///"))) {
|
|
230
|
+
return `/${joined}`;
|
|
231
|
+
}
|
|
232
|
+
return joined;
|
|
233
|
+
}
|
|
234
|
+
|
|
210
235
|
function resolveShellConfigPath(
|
|
211
236
|
shell: ProfileAliasShell,
|
|
212
237
|
homeDir: string,
|
|
213
238
|
platform: NodeJS.Platform,
|
|
214
239
|
env: NodeJS.ProcessEnv,
|
|
215
240
|
): string {
|
|
241
|
+
// POSIX shells (bash/zsh/fish) always need forward-slash config paths,
|
|
242
|
+
// even on Windows. path.posix.join adds / separators but preserves existing
|
|
243
|
+
// backslashes in input segments, so we normalize each component with toPosix.
|
|
244
|
+
// PowerShell profiles use the platform-native path.join (backslashes on
|
|
245
|
+
// Windows, forward slashes elsewhere).
|
|
246
|
+
const posixHome = toPosix(homeDir);
|
|
216
247
|
switch (shell) {
|
|
217
248
|
case "zsh":
|
|
218
|
-
return
|
|
249
|
+
return posixJoinUnc(env.ZDOTDIR ? toPosix(env.ZDOTDIR) : posixHome, ".zshrc");
|
|
219
250
|
case "bash":
|
|
220
|
-
return platform === "darwin" ?
|
|
251
|
+
return platform === "darwin" ? posixJoinUnc(posixHome, ".bash_profile") : posixJoinUnc(posixHome, ".bashrc");
|
|
221
252
|
case "fish": {
|
|
222
253
|
// fish sources conf.d from $XDG_CONFIG_HOME/fish (default ~/.config/fish);
|
|
223
254
|
// a hard-coded ~/.config would be silently ignored when the user relocates
|
|
224
255
|
// their XDG config root, leaving the alias unsourced after a restart.
|
|
225
|
-
const configHome = env.XDG_CONFIG_HOME
|
|
226
|
-
return
|
|
256
|
+
const configHome = env.XDG_CONFIG_HOME ? toPosix(env.XDG_CONFIG_HOME) : posixJoinUnc(posixHome, ".config");
|
|
257
|
+
return posixJoinUnc(configHome, "fish", "conf.d", "omp-profiles.fish");
|
|
227
258
|
}
|
|
228
259
|
case "pwsh":
|
|
229
260
|
return platform === "win32"
|
|
230
261
|
? path.join(homeDir, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1")
|
|
231
|
-
:
|
|
262
|
+
: posixJoinUnc(posixHome, ".config", "powershell", "Microsoft.PowerShell_profile.ps1");
|
|
232
263
|
case "powershell":
|
|
233
264
|
return path.join(homeDir, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
234
265
|
}
|
package/src/cli/usage-cli.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type UsageReport,
|
|
16
16
|
type UsageUnit,
|
|
17
17
|
} from "@oh-my-pi/pi-ai";
|
|
18
|
-
import { formatDuration, formatNumber } from "@oh-my-pi/pi-utils";
|
|
18
|
+
import { formatDuration, formatNumber, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
19
19
|
import chalk from "chalk";
|
|
20
20
|
import { ModelRegistry } from "../config/model-registry";
|
|
21
21
|
import { discoverAuthStorage } from "../sdk";
|
|
@@ -450,6 +450,10 @@ export function formatUsageBreakdown(
|
|
|
450
450
|
lines.push(
|
|
451
451
|
`${chalk.bold.cyan(formatProviderName(provider))} ${chalk.dim(`— ${accountCount} ${accountCount === 1 ? "account" : "accounts"}`)}`,
|
|
452
452
|
);
|
|
453
|
+
// Provider-wide disclaimers render once per provider, not per limit.
|
|
454
|
+
const providerNotes = [...new Set(providerReports.flatMap(report => report.notes ?? []))];
|
|
455
|
+
for (const note of providerNotes)
|
|
456
|
+
lines.push(` ${chalk.dim(sanitizeText(note.replace(/[\r\n]+/g, " ").replace(/\t/g, " ")))}`);
|
|
453
457
|
|
|
454
458
|
const labelWidth = providerReports
|
|
455
459
|
.flatMap(report => report.limits)
|
|
@@ -117,6 +117,11 @@ type LlamaCppDiscoveredServerMetadata = {
|
|
|
117
117
|
input?: ("text" | "image")[];
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
type LlamaCppModelListEntry = {
|
|
121
|
+
id: string;
|
|
122
|
+
contextWindow?: number;
|
|
123
|
+
};
|
|
124
|
+
|
|
120
125
|
function toPositiveNumberOrUndefined(value: unknown): number | undefined {
|
|
121
126
|
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
122
127
|
return value;
|
|
@@ -162,6 +167,26 @@ function extractLlamaCppContextWindow(payload: Record<string, unknown>): number
|
|
|
162
167
|
return toPositiveNumberOrUndefined(payload.n_ctx);
|
|
163
168
|
}
|
|
164
169
|
|
|
170
|
+
function extractLlamaCppModelContextWindow(item: Record<string, unknown>): number | undefined {
|
|
171
|
+
const meta = item.meta;
|
|
172
|
+
if (!isRecord(meta)) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
return toPositiveNumberOrUndefined(meta.n_ctx) ?? toPositiveNumberOrUndefined(meta.n_ctx_train);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function parseLlamaCppModelList(payload: unknown): LlamaCppModelListEntry[] {
|
|
179
|
+
if (!isRecord(payload) || !Array.isArray(payload.data)) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
return payload.data.flatMap(item => {
|
|
183
|
+
if (!isRecord(item) || typeof item.id !== "string" || !item.id) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
return [{ id: item.id, contextWindow: extractLlamaCppModelContextWindow(item) }];
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
165
190
|
function extractLlamaCppInputCapabilities(payload: Record<string, unknown>): ("text" | "image")[] | undefined {
|
|
166
191
|
const modalities = payload.modalities;
|
|
167
192
|
if (!isRecord(modalities)) {
|
|
@@ -338,12 +363,13 @@ export async function discoverLlamaCppModels(
|
|
|
338
363
|
const [response, serverMetadata] = apiKey
|
|
339
364
|
? await withAuth(apiKey, key => attempt({ ...baseHeaders, Authorization: `Bearer ${key}` }))
|
|
340
365
|
: await attempt(baseHeaders);
|
|
341
|
-
const payload = (await response.json()) as
|
|
342
|
-
const models = payload
|
|
366
|
+
const payload = (await response.json()) as unknown;
|
|
367
|
+
const models = parseLlamaCppModelList(payload);
|
|
343
368
|
const discovered: Model<Api>[] = [];
|
|
344
369
|
for (const item of models) {
|
|
345
|
-
const id = item
|
|
370
|
+
const { id } = item;
|
|
346
371
|
if (!id) continue;
|
|
372
|
+
const contextWindow = item.contextWindow ?? serverMetadata?.contextWindow ?? 128000;
|
|
347
373
|
discovered.push(
|
|
348
374
|
buildModel({
|
|
349
375
|
id,
|
|
@@ -355,11 +381,8 @@ export async function discoverLlamaCppModels(
|
|
|
355
381
|
input: serverMetadata?.input ?? ["text"],
|
|
356
382
|
imageInputDecoder: "stb",
|
|
357
383
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
358
|
-
contextWindow
|
|
359
|
-
maxTokens: Math.min(
|
|
360
|
-
serverMetadata?.contextWindow ?? Number.POSITIVE_INFINITY,
|
|
361
|
-
DISCOVERY_DEFAULT_MAX_TOKENS,
|
|
362
|
-
),
|
|
384
|
+
contextWindow,
|
|
385
|
+
maxTokens: Math.min(contextWindow, DISCOVERY_DEFAULT_MAX_TOKENS),
|
|
363
386
|
headers,
|
|
364
387
|
compat: {
|
|
365
388
|
supportsStore: false,
|
|
@@ -372,6 +395,34 @@ export async function discoverLlamaCppModels(
|
|
|
372
395
|
return discovered;
|
|
373
396
|
}
|
|
374
397
|
|
|
398
|
+
export async function discoverLlamaCppModelContextWindow(
|
|
399
|
+
model: Pick<Model<Api>, "provider" | "id" | "baseUrl" | "headers">,
|
|
400
|
+
ctx: DiscoveryContext,
|
|
401
|
+
): Promise<number | undefined> {
|
|
402
|
+
const baseUrl = normalizeLlamaCppBaseUrl(model.baseUrl);
|
|
403
|
+
const modelsUrl = `${baseUrl}/models`;
|
|
404
|
+
const baseHeaders: Record<string, string> = { ...(model.headers ?? {}) };
|
|
405
|
+
const attempt = async (headers: Record<string, string>) => {
|
|
406
|
+
const response = await ctx.fetch(modelsUrl, {
|
|
407
|
+
headers,
|
|
408
|
+
signal: AbortSignal.timeout(250),
|
|
409
|
+
});
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
const entries = parseLlamaCppModelList(await response.json());
|
|
414
|
+
return entries.find(entry => entry.id === model.id)?.contextWindow;
|
|
415
|
+
};
|
|
416
|
+
try {
|
|
417
|
+
const apiKey = await ctx.getBearerApiKeyResolver(model.provider);
|
|
418
|
+
return apiKey
|
|
419
|
+
? await withAuth(apiKey, key => attempt({ ...baseHeaders, Authorization: `Bearer ${key}` }))
|
|
420
|
+
: await attempt(baseHeaders);
|
|
421
|
+
} catch {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
375
426
|
export async function discoverOpenAIModelsList(
|
|
376
427
|
providerConfig: DiscoveryProviderConfig,
|
|
377
428
|
ctx: DiscoveryContext,
|