@oh-my-pi/pi-coding-agent 15.1.8 → 15.2.1
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 +52 -1
- package/dist/types/cli/update-cli.d.ts +18 -0
- package/dist/types/config/settings-schema.d.ts +10 -0
- package/dist/types/eval/py/kernel.d.ts +6 -0
- package/dist/types/goals/state.d.ts +1 -1
- package/dist/types/goals/tools/goal-tool.d.ts +4 -0
- package/dist/types/hashline/parser.d.ts +6 -2
- package/dist/types/internal-urls/memory-protocol.d.ts +6 -0
- package/dist/types/main.d.ts +25 -1
- package/dist/types/modes/theme/shimmer.d.ts +27 -0
- package/dist/types/slash-commands/helpers/format.d.ts +4 -1
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/find.d.ts +3 -0
- package/dist/types/tools/search.d.ts +3 -0
- package/dist/types/tui/file-list.d.ts +6 -0
- package/dist/types/tui/hyperlink.d.ts +42 -0
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/utils/tool-choice.d.ts +2 -1
- package/dist/types/web/search/providers/utils.d.ts +27 -1
- package/package.json +7 -7
- package/src/cli/update-cli.ts +78 -36
- package/src/config/model-registry.ts +23 -12
- package/src/config/settings-schema.ts +12 -0
- package/src/config/settings.ts +28 -5
- package/src/edit/renderer.ts +5 -3
- package/src/eval/py/executor.ts +12 -1
- package/src/eval/py/kernel.ts +24 -8
- package/src/extensibility/plugins/legacy-pi-compat.ts +2 -2
- package/src/goals/runtime.ts +9 -3
- package/src/goals/state.ts +1 -1
- package/src/goals/tools/goal-tool.ts +12 -2
- package/src/hashline/diff.ts +1 -1
- package/src/hashline/execute.ts +2 -2
- package/src/hashline/parser.ts +87 -12
- package/src/internal-urls/memory-protocol.ts +1 -1
- package/src/main.ts +13 -2
- package/src/modes/interactive-mode.ts +29 -1
- package/src/modes/theme/shimmer.ts +79 -0
- package/src/prompts/agents/oracle.md +15 -16
- package/src/prompts/tools/goal.md +7 -2
- package/src/session/agent-session.ts +12 -75
- package/src/slash-commands/helpers/format.ts +23 -3
- package/src/task/executor.ts +115 -19
- package/src/tools/ast-edit.ts +39 -6
- package/src/tools/ast-grep.ts +38 -6
- package/src/tools/find.ts +13 -2
- package/src/tools/read.ts +46 -6
- package/src/tools/search.ts +447 -265
- package/src/tui/file-list.ts +10 -2
- package/src/tui/hyperlink.ts +126 -0
- package/src/tui/index.ts +1 -0
- package/src/utils/tool-choice.ts +7 -7
- package/src/web/kagi.ts +2 -2
- package/src/web/parallel.ts +3 -3
- package/src/web/search/index.ts +20 -9
- package/src/web/search/providers/anthropic.ts +4 -2
- package/src/web/search/providers/brave.ts +4 -2
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/exa.ts +4 -1
- package/src/web/search/providers/gemini.ts +4 -1
- package/src/web/search/providers/jina.ts +4 -2
- package/src/web/search/providers/kagi.ts +5 -1
- package/src/web/search/providers/kimi.ts +4 -2
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +7 -2
- package/src/web/search/providers/searxng.ts +4 -1
- package/src/web/search/providers/synthetic.ts +4 -2
- package/src/web/search/providers/tavily.ts +4 -2
- package/src/web/search/providers/utils.ts +63 -1
- package/src/web/search/providers/zai.ts +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.2.1] - 2026-05-21
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed compaction routing to the wrong provider when `modelRoles.default` is set to a different model than the active chat. Auto- and manual compaction now prefer the active session's model and only fall back to role-based candidates when the current model has no usable credentials. Previously, an Anthropic chat with `modelRoles.default = openai/gpt-5` would compact through OpenAI (including the remote-compaction endpoint), even though the live conversation never used OpenAI.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed context overflow not being detected when the session's model (e.g. `hf:zai-org/GLM-5.1` via `synthetic` provider) returns a 400 with the upstream "400 status code (no body)" message wrapped inside a JSON envelope. The `isContextOverflow` check now matches the no-body status phrase anywhere in the error string rather than requiring it at the very start, so auto-compaction fires correctly instead of leaving the session silently stuck ([#1251](https://github.com/can1357/oh-my-pi/issues/1251)).
|
|
14
|
+
- Fixed `formatCapturedHttpError` not extracting the error text from `{"error":"string"}` response bodies (only object-valued `error` fields were previously handled), resulting in raw JSON in error messages instead of the human-readable string.
|
|
15
|
+
|
|
16
|
+
## [15.2.0] - 2026-05-21
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Changed the interactive working loader and slash-command progress bars to use the active theme's accent shimmer instead of static muted text.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Fixed false-positive hashline `~ TEXT` separator-padding warning firing on YAML, JSON, Python, Markdown, TOML, and other indent-sensitive file edits. The padding check is now skipped entirely for indentation-sensitive extensions (`.py`, `.yml`/`.yaml`, `.md`/`.mdx`, `.json`/`.jsonc`/`.json5`, `.toml`, `.rst`, `.tf`, `.nix`, `.coffee`, `.haml`/`.slim`/`.pug`, `.sass`/`.styl`, `.nim`, `.cr`, `.elm`, `.fs`, …), and tightened on every other extension to flag only the `~ beta` typo shape (exactly one leading space before non-space content) rather than any leading-space payload.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Fixed goal state machine: `get` now returns paused goals (was returning null for `enabled=false`), `complete` now works on paused goals after interrupts, `create` is allowed after a previous goal reaches `complete` status, and the goal tool is re-added to the active tool set on session reload when a paused goal is persisted. Added `resume` and `drop` ops to the goal tool so the agent can re-engage or discard a paused goal without requiring user slash commands. ([#1249](https://github.com/can1357/oh-my-pi/issues/1249))
|
|
29
|
+
|
|
30
|
+
## [15.1.9] - 2026-05-21
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Fixed `disabledProviders` still probing local discovery endpoints for Ollama, llama.cpp, and LM Studio during background model refresh. Disabled providers are now excluded before implicit and built-in discovery managers are created. ([#1232](https://github.com/can1357/oh-my-pi/issues/1232))
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Fixed `omp acp` auto-discovering host `.mcp.json` servers in parallel with the ACP client's `session/new.mcpServers`, which shadowed client-supplied MCP tools in `search_tool_bm25` and the session tool registry. The ACP session factory now forces `enableMCP: false`, so MCP ownership stays with `AcpAgent#configureMcpServers`. Non-ACP modes keep on-disk discovery. ([#1234](https://github.com/can1357/oh-my-pi/issues/1234))
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Fixed binary `omp update` rollbacks so a downloaded replacement that fails post-install version verification no longer remains installed over the previous working binary. ([#1240](https://github.com/can1357/oh-my-pi/issues/1240))
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- Fixed `/force <tool>` rejecting Ollama/local models before the requested tool could run; Ollama now receives a named forced choice that the provider transport narrows to the selected tool. ([#1236](https://github.com/can1357/oh-my-pi/issues/1236))
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Fixed `web_search` freezing the session when an upstream provider stalled. Bun's WinHTTP backend on Windows can silently drop `AbortSignal` once a TCP/TLS connection hangs (oven-sh/bun#15275, oven-sh/bun#18536), so Esc never reached the in-flight fetch and the only recovery was Ctrl+C + `omp --resume`. Every web-search provider's outbound `fetch` (anthropic, brave, codex, exa, gemini, jina, kagi, kimi, parallel, perplexity, searxng, synthetic, tavily, z.ai) now composes the caller signal with a 60s hard timeout via a shared `withHardTimeout` helper, guaranteeing the request settles within a minute even when Bun's abort fails to propagate. Independently, `executeSearch`'s provider-fallback loop was masking real cancellations as ordinary provider errors and returning "All web search providers failed"; it now re-throws as `ToolAbortError` the moment the caller's signal aborts, so the session sees a clean cancel on every platform. ([#1221](https://github.com/can1357/oh-my-pi/issues/1221))
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- Added OSC 8 terminal hyperlink support for file paths in tool output. When the terminal supports hyperlinks (kitty, Ghostty, WezTerm, iTerm2, Alacritty, VS Code) and the new `tui.hyperlinks` setting is `auto` (default) or `always`, OMP wraps file paths emitted by `read`, `find`, `search`, `edit`, `ast_grep`, and `ast_edit` renderers in `file:///abs/path` hyperlinks. `local://` and other fs-backed internal URLs resolve to their backing path. Set `tui.hyperlinks: off` to disable. ([#1244](https://github.com/can1357/oh-my-pi/issues/1244))
|
|
55
|
+
|
|
5
56
|
## [15.1.8] - 2026-05-20
|
|
6
57
|
|
|
7
58
|
### Fixed
|
|
@@ -8454,4 +8505,4 @@ Initial public release.
|
|
|
8454
8505
|
- Git branch display in footer
|
|
8455
8506
|
- Message queueing during streaming responses
|
|
8456
8507
|
- OAuth integration for Gmail and Google Calendar access
|
|
8457
|
-
- HTML export with syntax highlighting and collapsible sections
|
|
8508
|
+
- HTML export with syntax highlighting and collapsible sections
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/** Result from running the installed binary and parsing its reported version. */
|
|
2
|
+
export interface InstalledVersionVerification {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
actual?: string;
|
|
5
|
+
path?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Paths and verifier used while replacing a downloaded binary update. */
|
|
8
|
+
export interface BinaryReplacementOptions {
|
|
9
|
+
targetPath: string;
|
|
10
|
+
tempPath: string;
|
|
11
|
+
backupPath: string;
|
|
12
|
+
expectedVersion: string;
|
|
13
|
+
verifyInstalledVersion: (expectedVersion: string) => Promise<InstalledVersionVerification>;
|
|
14
|
+
}
|
|
1
15
|
/**
|
|
2
16
|
* Parse update subcommand arguments.
|
|
3
17
|
* Returns undefined if not an update command.
|
|
@@ -7,6 +21,10 @@ export declare function parseUpdateArgs(args: string[]): {
|
|
|
7
21
|
check: boolean;
|
|
8
22
|
} | undefined;
|
|
9
23
|
export declare function resolveUpdateMethodForTest(ompPath: string, bunBinDir: string | undefined): "bun" | "binary";
|
|
24
|
+
/**
|
|
25
|
+
* Atomically replace the installed binary and roll back if version verification fails.
|
|
26
|
+
*/
|
|
27
|
+
export declare function replaceBinaryForUpdate(options: BinaryReplacementOptions): Promise<InstalledVersionVerification>;
|
|
10
28
|
/**
|
|
11
29
|
* Run the update command.
|
|
12
30
|
*/
|
|
@@ -634,6 +634,16 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
634
634
|
readonly default: 20;
|
|
635
635
|
readonly description: "Maximum height in terminal rows for inline images (default 20). Set to 0 to use only the viewport-based limit (60% of terminal height).";
|
|
636
636
|
};
|
|
637
|
+
readonly "tui.hyperlinks": {
|
|
638
|
+
readonly type: "enum";
|
|
639
|
+
readonly values: readonly ["off", "auto", "always"];
|
|
640
|
+
readonly default: "auto";
|
|
641
|
+
readonly ui: {
|
|
642
|
+
readonly tab: "appearance";
|
|
643
|
+
readonly label: "Terminal Hyperlinks";
|
|
644
|
+
readonly description: "Wrap file paths in OSC 8 hyperlinks for terminal-native click-to-open (auto: detect support; off: never; always: unconditional)";
|
|
645
|
+
};
|
|
646
|
+
};
|
|
637
647
|
readonly "display.tabWidth": {
|
|
638
648
|
readonly type: "number";
|
|
639
649
|
readonly default: 3;
|
|
@@ -21,6 +21,12 @@ export interface KernelExecuteResult {
|
|
|
21
21
|
cancelled: boolean;
|
|
22
22
|
timedOut: boolean;
|
|
23
23
|
stdinRequested: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* True when the kernel subprocess was killed as part of settling this
|
|
26
|
+
* execution (e.g. SIGINT was ignored and we escalated to shutdown, or the
|
|
27
|
+
* kernel died unexpectedly). When false, the kernel remains reusable.
|
|
28
|
+
*/
|
|
29
|
+
kernelKilled?: boolean;
|
|
24
30
|
}
|
|
25
31
|
export interface KernelShutdownResult {
|
|
26
32
|
confirmed: boolean;
|
|
@@ -17,7 +17,7 @@ export interface GoalModeState {
|
|
|
17
17
|
goal: Goal;
|
|
18
18
|
}
|
|
19
19
|
export interface GoalToolDetails {
|
|
20
|
-
op: "create" | "get" | "complete";
|
|
20
|
+
op: "create" | "get" | "complete" | "resume" | "drop";
|
|
21
21
|
goal?: Goal | null;
|
|
22
22
|
remainingTokens?: number | null;
|
|
23
23
|
completionBudgetReport?: string | null;
|
|
@@ -9,7 +9,9 @@ declare const goalSchema: z.ZodObject<{
|
|
|
9
9
|
op: z.ZodEnum<{
|
|
10
10
|
complete: "complete";
|
|
11
11
|
create: "create";
|
|
12
|
+
drop: "drop";
|
|
12
13
|
get: "get";
|
|
14
|
+
resume: "resume";
|
|
13
15
|
}>;
|
|
14
16
|
objective: z.ZodOptional<z.ZodString>;
|
|
15
17
|
token_budget: z.ZodOptional<z.ZodNumber>;
|
|
@@ -32,7 +34,9 @@ export declare class GoalTool implements AgentTool<typeof goalSchema, GoalToolDe
|
|
|
32
34
|
op: z.ZodEnum<{
|
|
33
35
|
complete: "complete";
|
|
34
36
|
create: "create";
|
|
37
|
+
drop: "drop";
|
|
35
38
|
get: "get";
|
|
39
|
+
resume: "resume";
|
|
36
40
|
}>;
|
|
37
41
|
objective: z.ZodOptional<z.ZodString>;
|
|
38
42
|
token_budget: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { HashlineCursor, HashlineEdit } from "./types";
|
|
2
2
|
export declare function cloneCursor(cursor: HashlineCursor): HashlineCursor;
|
|
3
|
-
export declare function parseHashline(diff: string): HashlineEdit[];
|
|
4
|
-
export
|
|
3
|
+
export declare function parseHashline(diff: string, opts?: ParseHashlineOptions): HashlineEdit[];
|
|
4
|
+
export interface ParseHashlineOptions {
|
|
5
|
+
/** File path the diff targets. Used to suppress indent-sensitive false-positive warnings. */
|
|
6
|
+
path?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseHashlineWithWarnings(diff: string, opts?: ParseHashlineOptions): {
|
|
5
9
|
edits: HashlineEdit[];
|
|
6
10
|
warnings: string[];
|
|
7
11
|
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Snapshot of memory roots for every registered session, deduped.
|
|
4
|
+
* Each session has its own cwd (possibly a worktree), so subagents and main
|
|
5
|
+
* may see different roots.
|
|
6
|
+
*/
|
|
7
|
+
export declare function memoryRootsFromRegistry(): string[];
|
|
2
8
|
/**
|
|
3
9
|
* Resolve a memory:// URL to an absolute filesystem path under memory root.
|
|
4
10
|
*/
|
package/dist/types/main.d.ts
CHANGED
|
@@ -5,16 +5,40 @@
|
|
|
5
5
|
* createAgentSession() options. The SDK does the heavy lifting.
|
|
6
6
|
*/
|
|
7
7
|
import type { Args } from "./cli/args";
|
|
8
|
+
import { ModelRegistry } from "./config/model-registry";
|
|
8
9
|
import { Settings } from "./config/settings";
|
|
9
10
|
import { InteractiveMode, runAcpMode } from "./modes";
|
|
10
11
|
import type { SubmittedUserInput } from "./modes/types";
|
|
11
|
-
import { createAgentSession, discoverAuthStorage } from "./sdk";
|
|
12
|
+
import { type CreateAgentSessionOptions, type CreateAgentSessionResult, createAgentSession, discoverAuthStorage } from "./sdk";
|
|
12
13
|
import type { AgentSession } from "./session/agent-session";
|
|
14
|
+
import type { AuthStorage } from "./session/auth-storage";
|
|
13
15
|
export interface InteractiveModeNotify {
|
|
14
16
|
kind: "warn" | "error" | "info";
|
|
15
17
|
message: string;
|
|
16
18
|
}
|
|
17
19
|
export declare function submitInteractiveInput(mode: Pick<InteractiveMode, "markPendingSubmissionStarted" | "finishPendingSubmission" | "showError" | "checkShutdownRequested">, session: Pick<AgentSession, "prompt" | "promptCustomMessage">, input: SubmittedUserInput): Promise<void>;
|
|
20
|
+
type AcpSessionFactory = (cwd: string) => Promise<AgentSession>;
|
|
21
|
+
export interface AcpSessionFactoryOptions {
|
|
22
|
+
baseOptions: CreateAgentSessionOptions;
|
|
23
|
+
settings: Settings;
|
|
24
|
+
sessionDir?: string;
|
|
25
|
+
authStorage: AuthStorage;
|
|
26
|
+
modelRegistry: ModelRegistry;
|
|
27
|
+
parsedArgs: Pick<Args, "apiKey">;
|
|
28
|
+
rawArgs: string[];
|
|
29
|
+
createSession: (options: CreateAgentSessionOptions) => Promise<CreateAgentSessionResult>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the per-`session/new` factory used by ACP mode.
|
|
33
|
+
*
|
|
34
|
+
* MCP servers in ACP sessions are owned exclusively by the ACP client, which
|
|
35
|
+
* supplies them through `session/new.mcpServers` and re-applies them via
|
|
36
|
+
* {@link AcpAgent#configureMcpServers}. We therefore force `enableMCP: false`
|
|
37
|
+
* on every session created here so {@link createAgentSession} skips the on-disk
|
|
38
|
+
* `.mcp.json` discovery path — otherwise host MCP tools land in the session's
|
|
39
|
+
* tool registry and shadow the client-supplied servers (issue #1234).
|
|
40
|
+
*/
|
|
41
|
+
export declare function createAcpSessionFactory(args: AcpSessionFactoryOptions): AcpSessionFactory;
|
|
18
42
|
interface RunRootCommandDependencies {
|
|
19
43
|
createAgentSession?: typeof createAgentSession;
|
|
20
44
|
discoverAuthStorage?: typeof discoverAuthStorage;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Theme, ThemeColor } from "./theme";
|
|
2
|
+
type ShimmerTheme = Pick<Theme, "bold" | "fg">;
|
|
3
|
+
/** Three-tier color stack a shimmer character cycles through as the band sweeps. */
|
|
4
|
+
export interface ShimmerPalette {
|
|
5
|
+
/** Color for chars outside / at the edge of the band (intensity < 0.2). */
|
|
6
|
+
low: ThemeColor;
|
|
7
|
+
/** Color for chars approaching the crest (0.2 <= intensity < 0.6). */
|
|
8
|
+
mid: ThemeColor;
|
|
9
|
+
/** Color at the band's crest (intensity >= 0.6). */
|
|
10
|
+
high: ThemeColor;
|
|
11
|
+
/** Whether to bold the crest tier. Default `false`. */
|
|
12
|
+
bold?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** One run of text that shares a palette inside a larger shimmer sweep. */
|
|
15
|
+
export interface ShimmerSegment {
|
|
16
|
+
text: string;
|
|
17
|
+
palette?: ShimmerPalette;
|
|
18
|
+
}
|
|
19
|
+
export declare const DEFAULT_SHIMMER_PALETTE: ShimmerPalette;
|
|
20
|
+
/**
|
|
21
|
+
* Apply a shimmer sweep across one or more segments, treating them as a single
|
|
22
|
+
* continuous string for band positioning. Each segment can supply its own
|
|
23
|
+
* palette so the gradient stays in lockstep while the colors differ.
|
|
24
|
+
*/
|
|
25
|
+
export declare function shimmerSegments(segments: readonly ShimmerSegment[], theme: ShimmerTheme): string;
|
|
26
|
+
export declare function shimmerText(text: string, theme: ShimmerTheme, palette?: ShimmerPalette): string;
|
|
27
|
+
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { type Theme } from "../../modes/theme/theme";
|
|
1
2
|
/** Format a millisecond duration as a coarse-grained human label. */
|
|
2
3
|
export declare function formatDuration(ms: number): string;
|
|
4
|
+
type ProgressBarTheme = Pick<Theme, "bold" | "fg">;
|
|
3
5
|
/**
|
|
4
6
|
* Render an ASCII progress bar with a trailing percent label.
|
|
5
7
|
* `fraction` is clamped to `[0, 1]`. `undefined` renders a dotted placeholder.
|
|
6
8
|
*/
|
|
7
|
-
export declare function renderAsciiBar(fraction: number | undefined, width?: number): string;
|
|
9
|
+
export declare function renderAsciiBar(fraction: number | undefined, width?: number, uiTheme?: ProgressBarTheme): string;
|
|
10
|
+
export {};
|
|
@@ -31,6 +31,9 @@ export interface AstEditToolDetails {
|
|
|
31
31
|
/** Pre-formatted text for the user-visible TUI render. Mirrors `result.text` lines but uses
|
|
32
32
|
* a `│` gutter (no model-only hashline anchors). The TUI uses this directly so it never parses model-facing text. */
|
|
33
33
|
displayContent?: string;
|
|
34
|
+
/** Absolute base directory used during the edit. Used by the renderer to resolve
|
|
35
|
+
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
36
|
+
searchPath?: string;
|
|
34
37
|
}
|
|
35
38
|
export declare class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolDetails> {
|
|
36
39
|
private readonly session;
|
|
@@ -28,6 +28,9 @@ export interface AstGrepToolDetails {
|
|
|
28
28
|
/** Pre-formatted text for the user-visible TUI render. Mirrors `result.text` lines but uses
|
|
29
29
|
* a `│` gutter and `*` to mark match lines. The TUI uses this directly so it never parses model-facing text. */
|
|
30
30
|
displayContent?: string;
|
|
31
|
+
/** Absolute base directory used during search. Used by the renderer to resolve
|
|
32
|
+
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
33
|
+
searchPath?: string;
|
|
31
34
|
}
|
|
32
35
|
export declare class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolDetails> {
|
|
33
36
|
private readonly session;
|
|
@@ -23,6 +23,9 @@ export interface FindToolDetails {
|
|
|
23
23
|
files?: string[];
|
|
24
24
|
truncated?: boolean;
|
|
25
25
|
error?: string;
|
|
26
|
+
/** Working directory at search time. Used by the renderer to resolve relative
|
|
27
|
+
* file paths to absolute paths for OSC 8 hyperlinks. */
|
|
28
|
+
cwd?: string;
|
|
26
29
|
/** User-supplied paths whose base directory was missing on disk. The tool
|
|
27
30
|
* skipped these and continued with the surviving entries; surfaced as a
|
|
28
31
|
* non-fatal warning in the renderer and in the model-facing text. */
|
|
@@ -43,6 +43,9 @@ export interface SearchToolDetails {
|
|
|
43
43
|
* `result.text` lines but uses a `│` gutter and `*` to mark match lines (vs space for
|
|
44
44
|
* context). The TUI uses this directly so it never parses model-facing hashline anchors. */
|
|
45
45
|
displayContent?: string;
|
|
46
|
+
/** Absolute base directory used during search. Used by the renderer to resolve
|
|
47
|
+
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
48
|
+
searchPath?: string;
|
|
46
49
|
/** User-supplied paths whose base directory was missing on disk. The tool
|
|
47
50
|
* skipped these and continued with the surviving entries; surfaced as a
|
|
48
51
|
* non-fatal warning in the renderer and in the model-facing text. */
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import type { Theme } from "../modes/theme/theme";
|
|
5
5
|
export interface FileEntry {
|
|
6
6
|
path: string;
|
|
7
|
+
/** Absolute filesystem path. When provided together with {@link FileListOptions.hyperlinkFn}, the
|
|
8
|
+
* rendered path text is wrapped in an OSC 8 hyperlink. */
|
|
9
|
+
absPath?: string;
|
|
7
10
|
isDirectory?: boolean;
|
|
8
11
|
meta?: string;
|
|
9
12
|
}
|
|
@@ -12,5 +15,8 @@ export interface FileListOptions {
|
|
|
12
15
|
expanded?: boolean;
|
|
13
16
|
maxCollapsed?: number;
|
|
14
17
|
showIcons?: boolean;
|
|
18
|
+
/** When provided, called with the entry's absolute path and the ANSI-styled display string to
|
|
19
|
+
* optionally wrap the path in an OSC 8 hyperlink. Only invoked when {@link FileEntry.absPath} is set. */
|
|
20
|
+
hyperlinkFn?: (absPath: string, displayText: string) => string;
|
|
15
21
|
}
|
|
16
22
|
export declare function renderFileList(options: FileListOptions, theme: Theme): string[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when OSC 8 hyperlinks should be emitted.
|
|
3
|
+
*
|
|
4
|
+
* Respects `tui.hyperlinks` setting:
|
|
5
|
+
* - `"off"`: never
|
|
6
|
+
* - `"auto"`: when `process.stdout.isTTY`, `NO_COLOR` is unset, and the detected terminal reports hyperlink support
|
|
7
|
+
* - `"always"`: unconditionally (useful for viewers that support OSC 8 without advertising it)
|
|
8
|
+
*/
|
|
9
|
+
export declare function isHyperlinkEnabled(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Wrap `displayText` in an OSC 8 hyperlink pointing at the given absolute file path.
|
|
12
|
+
*
|
|
13
|
+
* Returns `displayText` unchanged when hyperlinks are disabled or when
|
|
14
|
+
* the text already contains an OSC 8 sequence (prevents double-wrapping).
|
|
15
|
+
*
|
|
16
|
+
* The caller is responsible for passing an absolute path. Relative paths
|
|
17
|
+
* produce invalid `file://` URIs and are accepted silently to avoid runtime
|
|
18
|
+
* errors in renderer hot paths.
|
|
19
|
+
*
|
|
20
|
+
* @param absPath - Absolute filesystem path
|
|
21
|
+
* @param displayText - Text to render as the hyperlink anchor (may contain ANSI codes)
|
|
22
|
+
* @param opts - Optional line/col position appended as `?line=N&col=M` query params
|
|
23
|
+
*/
|
|
24
|
+
export declare function fileHyperlink(absPath: string, displayText: string, opts?: {
|
|
25
|
+
line?: number;
|
|
26
|
+
col?: number;
|
|
27
|
+
}): string;
|
|
28
|
+
/**
|
|
29
|
+
* Synchronously resolve a filesystem-backed internal URL (e.g. `local://foo.md`,
|
|
30
|
+
* `memory://root/notes.md`) to its absolute filesystem path. Returns `undefined`
|
|
31
|
+
* for inputs that aren't fs-backed, aren't resolvable in the current session
|
|
32
|
+
* registry, or fail to parse.
|
|
33
|
+
*
|
|
34
|
+
* Used by renderers to wrap fs-backed internal URLs in OSC 8 hyperlinks even
|
|
35
|
+
* when the resolved path isn't yet available from tool result details (e.g.
|
|
36
|
+
* during the call/streaming phase before a result lands).
|
|
37
|
+
*
|
|
38
|
+
* Async-resolved schemes (`artifact://`, `agent://`, `skill://`, `rule://`,
|
|
39
|
+
* `omp://`) are not handled here — those rely on `details.resolvedPath` set
|
|
40
|
+
* by the read tool's router resolution.
|
|
41
|
+
*/
|
|
42
|
+
export declare function tryResolveInternalUrlSync(input: string): string | undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Api, Model, ToolChoice } from "@oh-my-pi/pi-ai";
|
|
2
2
|
/**
|
|
3
3
|
* Build a provider-aware tool choice that targets one specific tool when supported.
|
|
4
|
-
*
|
|
4
|
+
* Providers that only expose required/any forcing may still honor named choices by
|
|
5
|
+
* narrowing their request tool list before transport.
|
|
5
6
|
*/
|
|
6
7
|
export declare function buildNamedToolChoice(toolName: string, model?: Model<Api>): ToolChoice | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { SearchProviderError, type SearchProviderId, type SearchSource } from "../../../web/search/types";
|
|
2
2
|
/**
|
|
3
3
|
* Search for an API credential by checking an env-derived key first,
|
|
4
4
|
* then falling back to agent.db stored credentials for the given providers.
|
|
@@ -12,6 +12,31 @@ export declare function findCredential(envKey: string | null | undefined, ...sto
|
|
|
12
12
|
* Swallows lookup errors and reports unavailability.
|
|
13
13
|
*/
|
|
14
14
|
export declare function isApiKeyAvailable(findApiKey: () => string | null | Promise<string | null>): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Default hard ceiling for a single web-search round-trip. 60s tolerates
|
|
17
|
+
* legitimate slow LLM-mediated responses (anthropic web_search_20250305,
|
|
18
|
+
* perplexity, gemini, codex) while still guaranteeing the session unfreezes
|
|
19
|
+
* within a minute if Bun's `AbortSignal` fails to propagate on Windows.
|
|
20
|
+
*
|
|
21
|
+
* Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
|
|
22
|
+
* settle far faster in practice; reusing the same ceiling keeps the wiring
|
|
23
|
+
* uniform without compromising correctness.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SEARCH_HARD_TIMEOUT_MS = 60000;
|
|
26
|
+
/**
|
|
27
|
+
* Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
|
|
28
|
+
* outbound `fetch()` is guaranteed to settle within `ms` even when the
|
|
29
|
+
* runtime fails to propagate cancellation to the underlying transport.
|
|
30
|
+
*
|
|
31
|
+
* Bun's WinHTTP backend on Windows is known to ignore `AbortSignal` once a
|
|
32
|
+
* TCP/TLS connection stalls (oven-sh/bun#15275, oven-sh/bun#18536); without
|
|
33
|
+
* this safety net a stalled web-search request freezes the entire session
|
|
34
|
+
* because the user's Esc is never delivered to the native layer.
|
|
35
|
+
*
|
|
36
|
+
* @param signal - Caller cancellation signal, if any.
|
|
37
|
+
* @param ms - Hard timeout in milliseconds. Defaults to {@link SEARCH_HARD_TIMEOUT_MS}.
|
|
38
|
+
*/
|
|
39
|
+
export declare function withHardTimeout(signal: AbortSignal | undefined, ms?: number): AbortSignal;
|
|
15
40
|
/**
|
|
16
41
|
* Map a provider's raw source list to the unified SearchSource shape,
|
|
17
42
|
* clamped to the requested result count and annotated with ageSeconds.
|
|
@@ -22,3 +47,4 @@ export declare function toSearchSources(sources: ReadonlyArray<{
|
|
|
22
47
|
snippet?: string;
|
|
23
48
|
publishedDate?: string;
|
|
24
49
|
}>, numResults: number): SearchSource[];
|
|
50
|
+
export declare function classifyProviderHttpError(provider: SearchProviderId, status: number, body: string): SearchProviderError | null;
|
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": "15.1
|
|
4
|
+
"version": "15.2.1",
|
|
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",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
48
48
|
"@babel/parser": "^7.29.3",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/omp-stats": "15.1
|
|
51
|
-
"@oh-my-pi/pi-agent-core": "15.1
|
|
52
|
-
"@oh-my-pi/pi-ai": "15.1
|
|
53
|
-
"@oh-my-pi/pi-natives": "15.1
|
|
54
|
-
"@oh-my-pi/pi-tui": "15.1
|
|
55
|
-
"@oh-my-pi/pi-utils": "15.1
|
|
50
|
+
"@oh-my-pi/omp-stats": "15.2.1",
|
|
51
|
+
"@oh-my-pi/pi-agent-core": "15.2.1",
|
|
52
|
+
"@oh-my-pi/pi-ai": "15.2.1",
|
|
53
|
+
"@oh-my-pi/pi-natives": "15.2.1",
|
|
54
|
+
"@oh-my-pi/pi-tui": "15.2.1",
|
|
55
|
+
"@oh-my-pi/pi-utils": "15.2.1",
|
|
56
56
|
"@puppeteer/browsers": "^2.13.0",
|
|
57
57
|
"@types/turndown": "5.0.6",
|
|
58
58
|
"@xterm/headless": "^6.0.0",
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -20,6 +20,22 @@ interface ReleaseInfo {
|
|
|
20
20
|
version: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** Result from running the installed binary and parsing its reported version. */
|
|
24
|
+
export interface InstalledVersionVerification {
|
|
25
|
+
ok: boolean;
|
|
26
|
+
actual?: string;
|
|
27
|
+
path?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Paths and verifier used while replacing a downloaded binary update. */
|
|
31
|
+
export interface BinaryReplacementOptions {
|
|
32
|
+
targetPath: string;
|
|
33
|
+
tempPath: string;
|
|
34
|
+
backupPath: string;
|
|
35
|
+
expectedVersion: string;
|
|
36
|
+
verifyInstalledVersion: (expectedVersion: string) => Promise<InstalledVersionVerification>;
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
/**
|
|
24
40
|
* Parse update subcommand arguments.
|
|
25
41
|
* Returns undefined if not an update command.
|
|
@@ -197,9 +213,7 @@ function resolveOmpPath(): string | undefined {
|
|
|
197
213
|
/**
|
|
198
214
|
* Run the resolved omp binary and check if it reports the expected version.
|
|
199
215
|
*/
|
|
200
|
-
async function verifyInstalledVersion(
|
|
201
|
-
expectedVersion: string,
|
|
202
|
-
): Promise<{ ok: boolean; actual?: string; path?: string }> {
|
|
216
|
+
async function verifyInstalledVersion(expectedVersion: string): Promise<InstalledVersionVerification> {
|
|
203
217
|
const ompPath = resolveOmpPath();
|
|
204
218
|
if (!ompPath) return { ok: false };
|
|
205
219
|
try {
|
|
@@ -215,29 +229,69 @@ async function verifyInstalledVersion(
|
|
|
215
229
|
}
|
|
216
230
|
}
|
|
217
231
|
|
|
232
|
+
function printVerifiedVersion(expectedVersion: string): void {
|
|
233
|
+
console.log(chalk.green(`\n${theme.status.success} Updated to ${expectedVersion}`));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function formatVerificationFailure(result: InstalledVersionVerification, expectedVersion: string): string {
|
|
237
|
+
if (result.actual) {
|
|
238
|
+
return `${APP_NAME} at ${result.path} still reports ${result.actual} (expected ${expectedVersion})`;
|
|
239
|
+
}
|
|
240
|
+
return `could not verify updated version${result.path ? ` at ${result.path}` : ""}`;
|
|
241
|
+
}
|
|
242
|
+
|
|
218
243
|
/**
|
|
219
244
|
* Print post-update verification result.
|
|
220
245
|
*/
|
|
221
246
|
async function printVerification(expectedVersion: string): Promise<void> {
|
|
222
247
|
const result = await verifyInstalledVersion(expectedVersion);
|
|
223
248
|
if (result.ok) {
|
|
224
|
-
|
|
249
|
+
printVerifiedVersion(expectedVersion);
|
|
225
250
|
return;
|
|
226
251
|
}
|
|
227
|
-
|
|
228
|
-
console.log(
|
|
229
|
-
chalk.yellow(
|
|
230
|
-
`\nWarning: ${APP_NAME} at ${result.path} still reports ${result.actual} (expected ${expectedVersion})`,
|
|
231
|
-
),
|
|
232
|
-
);
|
|
233
|
-
} else {
|
|
234
|
-
console.log(
|
|
235
|
-
chalk.yellow(`\nWarning: could not verify updated version${result.path ? ` at ${result.path}` : ""}`),
|
|
236
|
-
);
|
|
237
|
-
}
|
|
252
|
+
console.log(chalk.yellow(`\nWarning: ${formatVerificationFailure(result, expectedVersion)}`));
|
|
238
253
|
console.log(chalk.yellow(`You may need to reinstall: curl -fsSL https://omp.sh/install | sh`));
|
|
239
254
|
}
|
|
240
255
|
|
|
256
|
+
async function unlinkIfExists(filePath: string): Promise<void> {
|
|
257
|
+
try {
|
|
258
|
+
await fs.promises.unlink(filePath);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
if (!isEnoent(err)) throw err;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Atomically replace the installed binary and roll back if version verification fails.
|
|
266
|
+
*/
|
|
267
|
+
export async function replaceBinaryForUpdate(options: BinaryReplacementOptions): Promise<InstalledVersionVerification> {
|
|
268
|
+
let backupReady = false;
|
|
269
|
+
try {
|
|
270
|
+
await unlinkIfExists(options.backupPath);
|
|
271
|
+
await fs.promises.rename(options.targetPath, options.backupPath);
|
|
272
|
+
backupReady = true;
|
|
273
|
+
await fs.promises.rename(options.tempPath, options.targetPath);
|
|
274
|
+
|
|
275
|
+
const verification = await options.verifyInstalledVersion(options.expectedVersion);
|
|
276
|
+
if (!verification.ok) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`${formatVerificationFailure(verification, options.expectedVersion)}; restored previous ${APP_NAME} binary`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
backupReady = false;
|
|
283
|
+
await unlinkIfExists(options.backupPath);
|
|
284
|
+
return verification;
|
|
285
|
+
} catch (err) {
|
|
286
|
+
if (backupReady) {
|
|
287
|
+
await unlinkIfExists(options.targetPath);
|
|
288
|
+
await fs.promises.rename(options.backupPath, options.targetPath);
|
|
289
|
+
}
|
|
290
|
+
await unlinkIfExists(options.tempPath);
|
|
291
|
+
throw err;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
241
295
|
/**
|
|
242
296
|
* Update via bun package manager.
|
|
243
297
|
*/
|
|
@@ -271,27 +325,15 @@ async function updateViaBinaryAt(targetPath: string, expectedVersion: string): P
|
|
|
271
325
|
await pipeline(response.body, fileStream);
|
|
272
326
|
|
|
273
327
|
console.log(chalk.dim("Installing update..."));
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
await printVerification(expectedVersion);
|
|
285
|
-
console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
|
|
286
|
-
} catch (err) {
|
|
287
|
-
if (fs.existsSync(backupPath) && !fs.existsSync(targetPath)) {
|
|
288
|
-
await fs.promises.rename(backupPath, targetPath);
|
|
289
|
-
}
|
|
290
|
-
if (fs.existsSync(tempPath)) {
|
|
291
|
-
await fs.promises.unlink(tempPath);
|
|
292
|
-
}
|
|
293
|
-
throw err;
|
|
294
|
-
}
|
|
328
|
+
await replaceBinaryForUpdate({
|
|
329
|
+
targetPath,
|
|
330
|
+
tempPath,
|
|
331
|
+
backupPath,
|
|
332
|
+
expectedVersion,
|
|
333
|
+
verifyInstalledVersion,
|
|
334
|
+
});
|
|
335
|
+
printVerifiedVersion(expectedVersion);
|
|
336
|
+
console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
|
|
295
337
|
}
|
|
296
338
|
|
|
297
339
|
/**
|