@oh-my-pi/pi-coding-agent 15.2.3 → 15.3.0

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 (72) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/types/config/settings-schema.d.ts +34 -1
  3. package/dist/types/config/settings.d.ts +6 -0
  4. package/dist/types/discovery/helpers.d.ts +1 -0
  5. package/dist/types/goals/runtime.d.ts +4 -0
  6. package/dist/types/hashline/constants.d.ts +0 -2
  7. package/dist/types/hashline/hash.d.ts +13 -39
  8. package/dist/types/hashline/parser.d.ts +2 -6
  9. package/dist/types/modes/components/status-line/types.d.ts +10 -0
  10. package/dist/types/modes/components/status-line.d.ts +10 -0
  11. package/dist/types/modes/interactive-mode.d.ts +3 -1
  12. package/dist/types/modes/shared.d.ts +9 -0
  13. package/dist/types/modes/theme/shimmer.d.ts +6 -3
  14. package/dist/types/modes/types.d.ts +3 -1
  15. package/dist/types/modes/utils/context-usage.d.ts +17 -0
  16. package/dist/types/modes/utils/ui-helpers.d.ts +5 -1
  17. package/dist/types/session/agent-session.d.ts +9 -0
  18. package/dist/types/task/executor.d.ts +3 -1
  19. package/dist/types/task/types.d.ts +35 -0
  20. package/dist/types/tools/bash-command-fixup.d.ts +0 -5
  21. package/dist/types/utils/clipboard.d.ts +3 -1
  22. package/dist/types/utils/image-resize.d.ts +4 -1
  23. package/package.json +7 -7
  24. package/src/config/prompt-templates.ts +1 -8
  25. package/src/config/settings-schema.ts +29 -1
  26. package/src/config/settings.ts +19 -0
  27. package/src/discovery/helpers.ts +5 -1
  28. package/src/edit/index.ts +1 -1
  29. package/src/edit/renderer.ts +5 -7
  30. package/src/edit/streaming.ts +24 -12
  31. package/src/extensibility/plugins/legacy-pi-compat.ts +27 -5
  32. package/src/goals/runtime.ts +35 -13
  33. package/src/hashline/constants.ts +0 -3
  34. package/src/hashline/diff.ts +1 -1
  35. package/src/hashline/execute.ts +2 -2
  36. package/src/hashline/grammar.lark +7 -8
  37. package/src/hashline/hash.ts +21 -43
  38. package/src/hashline/input.ts +15 -13
  39. package/src/hashline/parser.ts +62 -161
  40. package/src/internal-urls/docs-index.generated.ts +2 -2
  41. package/src/main.ts +1 -1
  42. package/src/modes/components/model-selector.ts +53 -22
  43. package/src/modes/components/status-line/segments.ts +53 -0
  44. package/src/modes/components/status-line/types.ts +4 -0
  45. package/src/modes/components/status-line.ts +147 -12
  46. package/src/modes/controllers/command-controller.ts +9 -0
  47. package/src/modes/controllers/event-controller.ts +10 -1
  48. package/src/modes/interactive-mode.ts +74 -18
  49. package/src/modes/shared.ts +16 -0
  50. package/src/modes/theme/shimmer.ts +15 -6
  51. package/src/modes/theme/theme.ts +1 -1
  52. package/src/modes/types.ts +1 -1
  53. package/src/modes/utils/context-usage.ts +25 -2
  54. package/src/modes/utils/ui-helpers.ts +11 -1
  55. package/src/prompts/agents/frontmatter.md +1 -0
  56. package/src/prompts/tools/hashline.md +62 -81
  57. package/src/sdk.ts +24 -0
  58. package/src/session/agent-session.ts +58 -0
  59. package/src/session/session-manager.ts +54 -1
  60. package/src/slash-commands/builtin-registry.ts +10 -0
  61. package/src/task/executor.ts +50 -1
  62. package/src/task/index.ts +11 -0
  63. package/src/task/render.ts +26 -2
  64. package/src/task/types.ts +35 -0
  65. package/src/tools/bash-command-fixup.ts +0 -10
  66. package/src/tools/bash.ts +1 -9
  67. package/src/utils/clipboard.ts +68 -3
  68. package/src/utils/commit-message-generator.ts +6 -1
  69. package/src/utils/image-resize.ts +51 -26
  70. package/src/utils/title-generator.ts +45 -13
  71. package/dist/types/modes/components/status-line-segment-editor.d.ts +0 -24
  72. package/src/modes/components/status-line-segment-editor.ts +0 -359
package/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.3.0] - 2026-05-25
6
+
7
+ ### Added
8
+
9
+ - Added `OMP_NO_WEBP` environment variable to disable WebP encoding in image resize, fixing HTTP 400 errors when attaching browser snapshots to vision models running on local llama.cpp (which uses STB library that lacks WebP support)
10
+ - Fixed loop mode submitting the next prompt while a background async-job delivery turn (idle flush) was still pending, which could cause the job result to be silently dropped and make the session appear to keep firing while work was ongoing ([#1294](https://github.com/can1357/oh-my-pi/issues/1294))
11
+ - Fixed clipboard image paste (Ctrl+V) silently failing on WSL2 by routing image reads through a `powershell.exe` bridge when WSL interop is detected, since `arboard` returns `ContentNotAvailable` under WSLg ([#1280](https://github.com/can1357/oh-my-pi/issues/1280))
12
+ - Fixed extension `ctx.ui.notify()` messages emitted during `session_start` being cleared before the first interactive render ([#1316](https://github.com/can1357/oh-my-pi/issues/1316)).
13
+ - Fixed append-only context mode not being recomputed after model switches — the mode was frozen at session construction time using the initial model's provider, so `provider.appendOnlyContext=auto` left append-only enabled after switching away from DeepSeek (or disabled after switching to DeepSeek) for the rest of the session
14
+
15
+ ### Fixed
16
+ - Fixed clipboard image paste (Ctrl+V) silently failing on WSL2 by routing image reads through a `powershell.exe` bridge when WSL interop is detected, since `arboard` returns `ContentNotAvailable` under WSLg ([#1280](https://github.com/can1357/oh-my-pi/issues/1280))
17
+ ## [15.2.4] - 2026-05-22
18
+ ### Breaking Changes
19
+
20
+ - Replaced the legacy `@@` header and `+`/`<`/`=`/`-` hashline syntax with the new `§PATH` header and `«`/`»`/`≔` operation format, so existing hashline scripts and prompts using old symbols must be updated
21
+
22
+ ### Added
23
+
24
+ - Added one-anchor `≔ANCHOR` shorthand equivalent to `≔ANCHOR..ANCHOR` for single-line replace/delete
25
+
26
+ ### Changed
27
+
28
+ - Changed `≔A..B` so an omitted payload now deletes the range, and added an explicit empty payload line to keep a literal blank replacement line
29
+
30
+ ### Removed
31
+
32
+ - Removed the `hsep` prompt helper and `PI_HL_SEP` payload-prefix configuration because hashline payloads are no longer line-prefixed
33
+
34
+ ### Fixed
35
+
36
+ - Fixed hashline payload handling in parser and streaming preview to preserve blank lines as actual payload text until the next op, file header, or envelope marker
37
+
5
38
  ## [15.2.3] - 2026-05-22
6
39
  ### Breaking Changes
7
40
 
@@ -93,6 +126,10 @@
93
126
  - Fixed streaming edit previews for `apply_patch` and `hashline` jittering as the model typed `+added` lines. Two root causes addressed: (1) the trailing partial line of the streaming text input is now trimmed at each tick so a half-typed `+added` line no longer flickers; (2) the preview is rendered in the model's input order during streaming instead of re-deriving a unified diff via `Diff.structuredPatch`, whose coalescing previously reshuffled existing `+added` lines downward each time a new `-removed` line arrived. Existing additions now stay put and the preview only grows at the bottom while streaming. A residual trailing `-removed`/hunk-header block whose matching `+added` companion has not yet arrived is also suppressed until the additions land.
94
127
  - Fixed Perplexity web search appearing "logged out" roughly an hour after `omp auth login perplexity`. The search provider's `findOAuthToken` was honoring the bogus `expires = login_time + 1h` written by older logins (Perplexity JWTs typically omit `exp` because sessions are server-side) and silently dropping the credential. The loader now decodes the JWT's `exp` claim directly and only skips when the JWT itself is expired; tokens without an `exp` claim are treated as non-expiring.
95
128
 
129
+ ### Fixed
130
+
131
+ - Fixed `legacy-pi-compat` failing to load plugin extensions (e.g. `pi-schedule-prompt@0.3.0`) that import `@mariozechner/pi-ai` when running from a compiled binary. `getResolvedSpecifier` called `Bun.resolveSync` against `import.meta.dir` inside `/$bunfs/root`, where the virtual FS exposes no resolvable `node_modules` tree at runtime; the throw silently dropped the plugin. The fix lets `rewriteLegacyPiImports` fall back gracefully on resolution failure so that `rewriteBareImportsForLegacyExtension` — which already runs immediately after — can resolve the original specifier against the plugin's own installed peer deps instead. The same fallback is applied to `resolveLegacyPiSpecifier` (the Bun plugin shim's `onResolve` handler) for tool/hook files loaded directly via Bun's import system. ([#1215](https://github.com/can1357/oh-my-pi/issues/1215))
132
+
96
133
  ## [15.1.7] - 2026-05-19
97
134
 
98
135
  ### Fixed
@@ -101,6 +138,9 @@
101
138
  - Fixed hashline edit payloads that use a readability space after `~` by warning on separator-padding-shaped payload blocks and tightening the model prompt. ([#1166](https://github.com/can1357/oh-my-pi/issues/1166))
102
139
  - Fixed ACP bash permission requests to include execute tool metadata and command content so clients can render command approval prompts consistently. ([#1189](https://github.com/can1357/oh-my-pi/issues/1189))
103
140
  - Fixed the status-line fast-mode indicator (`⚡`) rendering for scoped service tiers (`openai-only`, `claude-only`) even when the active model's provider didn't realize them — e.g. `serviceTier: "openai-only"` would still show the indicator next to a Claude model the wire request couldn't apply fast mode to. The indicator now consults a new `AgentSession.isFastModeActive()` predicate that runs the configured tier through `resolveServiceTier(tier, model.provider)` and only lights up when the result is `"priority"` for the current model. `isFastModeEnabled()` keeps its scope-aware semantics so `/fast on|off|toggle` and `/fast status` continue to reflect the user's configured intent.
141
+ ### Fixed
142
+
143
+ - Fixed status-line context% computation freezing the UI for ~1.1 s every 2 s on long sessions (2,000+ messages). The earlier alignment fix (which uses `computeContextBreakdown` to match the `/context` slash command) was running on every agent event via `updateEditorTopBorder()` (event-controller.ts:163), and `computeContextBreakdown` walks every message through the native `countTokens` tokenizer (~0.5 ms each) — for the user's 2,312-message session this was ~1,120 ms synchronous blocking per cache miss, producing the user-visible "jittery rendering" and "status bar disappearing during streaming". `StatusLineComponent.getCachedContextBreakdown()` now uses an incremental per-message token cache: messages are walked ONCE during warm-up, and subsequent refreshes only compute tokens for the NEW messages appended since last call (typically 0–1 per refresh during streaming). The LAST message is always recomputed because its content may still be growing mid-stream; all prior messages are immutable once a newer message exists. Compaction (messages array shrinks) resets the cache. Non-message tokens (system prompt + tools + skills) are cached separately and invalidated via a cheap identity fingerprint. Result: 2,300-message warm refresh drops from ~1,120 ms to ~0.04 ms — 28,000× faster. Functional parity with the prior `computeContextBreakdown` path is preserved.
104
144
 
105
145
  ### Added
106
146
 
@@ -135,6 +175,7 @@
135
175
  - Fixed `deferAgentInitiatedTurns` handling during ACP async-job draining so background completion follow-up turns are delivered even when agent-initiated turns are deferred
136
176
  - Fixed ACP ordinary file-editing calls (`edit`, `write`, `ast_edit`) incorrectly requesting `session/request_permission` before every call, while keeping permission prompts for edit operations that delete or move files; permission requests now report the gated tool call as `pending` so clients can render the approval UI instead of returning `Permission request cancelled` without a visible prompt. ([#1134](https://github.com/can1357/oh-my-pi/pull/1134) by [@jiwangyihao](https://github.com/jiwangyihao))
137
177
  - Fixed the session tree selector to preserve a readable message column when deeply nested branch gutters would otherwise consume the viewport. ([#1144](https://github.com/can1357/oh-my-pi/issues/1144))
178
+ - Fixed the TUI model selector to keep provider tab labels separate from provider ids, so the human-readable Ollama Cloud tab refreshes and filters `ollama-cloud` models correctly. ([#1153](https://github.com/can1357/oh-my-pi/issues/1153))
138
179
 
139
180
  ## [15.1.3] - 2026-05-17
140
181
  ### Breaking Changes
@@ -23,7 +23,7 @@ export declare const TAB_METADATA: Record<SettingTab, {
23
23
  icon: `tab.${string}`;
24
24
  }>;
25
25
  /** Status line segment identifiers */
26
- export type StatusLineSegmentId = "pi" | "model" | "mode" | "path" | "git" | "pr" | "subagents" | "token_in" | "token_out" | "token_total" | "token_rate" | "cost" | "context_pct" | "context_total" | "time_spent" | "time" | "session" | "hostname" | "cache_read" | "cache_write" | "session_name";
26
+ export type StatusLineSegmentId = "pi" | "model" | "mode" | "path" | "git" | "pr" | "subagents" | "token_in" | "token_out" | "token_total" | "token_rate" | "cost" | "context_pct" | "context_total" | "time_spent" | "time" | "session" | "hostname" | "cache_read" | "cache_write" | "session_name" | "usage";
27
27
  /** Submenu choice metadata. */
28
28
  export type SubmenuOption<V extends string = string> = {
29
29
  value: V;
@@ -991,6 +991,15 @@ export declare const SETTINGS_SCHEMA: {
991
991
  readonly type: "number";
992
992
  readonly default: 2000;
993
993
  };
994
+ readonly "retry.maxDelayMs": {
995
+ readonly type: "number";
996
+ readonly default: number;
997
+ readonly ui: {
998
+ readonly tab: "model";
999
+ readonly label: "Max Retry Delay";
1000
+ readonly description: "Maximum wait between retries, in ms. When the provider asks us to wait longer than this and no credential or model fallback succeeds, the request fails fast instead of sleeping (e.g. 3-hour Anthropic rate-limit windows).";
1001
+ };
1002
+ };
994
1003
  readonly "retry.fallbackChains": {
995
1004
  readonly type: "record";
996
1005
  readonly default: Record<string, string[]>;
@@ -3075,6 +3084,29 @@ export declare const SETTINGS_SCHEMA: {
3075
3084
  readonly description: "Use Parallel extract API for URL fetching when credentials are available";
3076
3085
  };
3077
3086
  };
3087
+ readonly "provider.appendOnlyContext": {
3088
+ readonly type: "enum";
3089
+ readonly values: readonly ["auto", "on", "off"];
3090
+ readonly default: "auto";
3091
+ readonly ui: {
3092
+ readonly tab: "providers";
3093
+ readonly label: "Append-Only Context";
3094
+ readonly description: "Cache system prompt + tool specs and keep an append-only message log so provider prefix caches (DeepSeek, Anthropic) hit at maximum rate. Auto enables for DeepSeek.";
3095
+ readonly options: readonly [{
3096
+ readonly value: "auto";
3097
+ readonly label: "Auto";
3098
+ readonly description: "Enable for DeepSeek (recommended)";
3099
+ }, {
3100
+ readonly value: "on";
3101
+ readonly label: "On";
3102
+ readonly description: "Always enable append-only context";
3103
+ }, {
3104
+ readonly value: "off";
3105
+ readonly label: "Off";
3106
+ readonly description: "Disable append-only context";
3107
+ }];
3108
+ };
3109
+ };
3078
3110
  readonly "exa.enabled": {
3079
3111
  readonly type: "boolean";
3080
3112
  readonly default: true;
@@ -3283,6 +3315,7 @@ export interface RetrySettings {
3283
3315
  enabled: boolean;
3284
3316
  maxRetries: number;
3285
3317
  baseDelayMs: number;
3318
+ maxDelayMs: number;
3286
3319
  }
3287
3320
  export interface MemoriesSettings {
3288
3321
  enabled: boolean;
@@ -113,6 +113,12 @@ export declare class Settings {
113
113
  */
114
114
  setDisabledProviders(ids: string[]): void;
115
115
  }
116
+ /**
117
+ * Subscribe to append-only mode setting changes.
118
+ * Returns an unsubscribe function. Multiple sessions (main + subagents)
119
+ * can register independently without overwriting each other.
120
+ */
121
+ export declare function onAppendOnlyModeChanged(cb: (value: string) => void): () => void;
116
122
  export declare function isSettingsInitialized(): boolean;
117
123
  /**
118
124
  * Reset the global singleton for testing.
@@ -101,6 +101,7 @@ export interface ParsedAgentFields {
101
101
  model?: string[];
102
102
  output?: unknown;
103
103
  thinkingLevel?: ThinkingLevel;
104
+ autoloadSkills?: string[];
104
105
  blocking?: boolean;
105
106
  }
106
107
  /**
@@ -54,6 +54,10 @@ export declare class GoalRuntime {
54
54
  objective: string;
55
55
  tokenBudget?: number;
56
56
  }): Promise<GoalModeState>;
57
+ replaceGoal(input: {
58
+ objective: string;
59
+ tokenBudget?: number;
60
+ }): Promise<GoalModeState>;
57
61
  resumeGoal(): Promise<GoalModeState>;
58
62
  pauseGoal(): Promise<GoalModeState | undefined>;
59
63
  dropGoal(): Promise<Goal | undefined>;
@@ -2,8 +2,6 @@
2
2
  export declare const MISMATCH_CONTEXT = 2;
3
3
  /** Filler hash used for the interior of a multi-line range; not validated. */
4
4
  export declare const RANGE_INTERIOR_HASH = "**";
5
- /** Header marker introducing a new file section in multi-section input. */
6
- export declare const FILE_HEADER_PREFIX = "@";
7
5
  /** Optional patch envelope start marker; silently consumed when present. */
8
6
  export declare const BEGIN_PATCH_MARKER = "*** Begin Patch";
9
7
  /** Optional patch envelope end marker; terminates parsing when encountered. */
@@ -64,47 +64,21 @@ export declare function resolveHashlineGrammarPlaceholders(grammar: string): str
64
64
  /** @deprecated Use {@link resolveHashlineGrammarPlaceholders}. */
65
65
  export declare const resolveLarkLidPlaceholders: typeof resolveHashlineGrammarPlaceholders;
66
66
  /**
67
- * Single source of truth for the hashline edit payload separator. This is the
68
- * configured separator that starts inserted/replacement payload lines in
69
- * hashline edit input (`<separator>TEXT`) and separates inline modify ops from
70
- * their appended/prepended text.
67
+ * Hashline edit input markers. File section headers start with {@link HL_FILE_PREFIX};
68
+ * op lines start with a direction/action sigil: {@link HL_OP_INSERT_BEFORE},
69
+ * {@link HL_OP_INSERT_AFTER}, or {@link HL_OP_REPLACE}. Payload lines are
70
+ * verbatim file content and have no per-line marker.
71
71
  *
72
- * Override at runtime with the `PI_HL_SEP` env var (e.g.
73
- * `PI_HL_SEP=">"`, `PI_HL_SEP="\\"`). The value is read once at module load;
74
- * the edit grammar, prompt helper, and edit parser derive from it.
75
- *
76
- * Default is `~`, chosen empirically. Benchmark across 8 candidate separators
77
- * x 3 models (glm-4.7:nitro, gpt-5.4-nano, claude-sonnet-4-6), 24-48 runs per
78
- * cell, hashline variant, 12 sampled tasks per run:
79
- *
80
- * sep | task ✓ | edit ✓ | patch fail | tok/run
81
- * ----|--------|--------|-----------------|--------
82
- * + | 70.8% | 78.0% | 27/125 (21.6%) | 32,127
83
- * ÷ | 70.7% | 90.6% | 22/211 (10.4%) | 31,666
84
- * ~ | 69.4% | 94.9% | 6/107 ( 5.6%) | 30,529 <-- default
85
- * > | 69.2% | 91.5% | 21/219 ( 9.6%) | 30,777
86
- * : | 66.7% | 86.4% | 20/126 (15.9%) | 33,900
87
- * | | 65.9% | 86.9% | 20/127 (15.7%) | 34,589
88
- * \ | 65.5% | 89.8% | 16/124 (12.9%) | 36,010
89
- * % | 63.9% | 92.8% | 11/125 ( 8.8%) | 36,530
90
- *
91
- * `~` wins because:
92
- * - highest edit-tool success rate (94.9%) of any tested separator
93
- * - lowest patch-failure rate (5.6%) — model rarely emits a malformed payload
94
- * - cheapest in tokens alongside `>` (no retry overhead from format collisions)
95
- * - no line-leading role in any mainstream language, markdown, diff, regex,
96
- * or shell, so payload lines are unambiguous to both the parser and models
97
- * - task-success is statistically tied with `>` and `÷` (within run-to-run
98
- * noise), so the edit-reliability win is free
99
- *
100
- * `+` and `÷` lead on raw task-success but at the cost of ~2-4x more patch
101
- * failures (the model retries until it lands a valid edit). `:`, `|`, `\`
102
- * collide with line-leading syntax (label/object-key, body separator, escape)
103
- * and degrade both edit reliability and intent-match.
72
+ * These constants are the single source of truth for the edit parser, grammar,
73
+ * renderer, and prompt.
104
74
  */
105
- export declare const HL_EDIT_SEP: string;
106
- /** Regex-escaped form of {@link HL_EDIT_SEP}, safe for regexes. */
107
- export declare const HL_EDIT_SEP_RE_RAW: string;
75
+ export declare const HL_OP_INSERT_BEFORE = "\u00AB";
76
+ export declare const HL_OP_INSERT_AFTER = "\u00BB";
77
+ export declare const HL_OP_REPLACE = "\u2254";
78
+ /** All hashline edit op sigils, concatenated for fast membership tests. */
79
+ export declare const HL_OP_CHARS = "\u00AB\u00BB\u2254";
80
+ /** Hashline edit file section header marker. */
81
+ export declare const HL_FILE_PREFIX = "\u00A7";
108
82
  /** Stable separator for read/search/hashline display output. Intentionally not configurable. */
109
83
  export declare const HL_BODY_SEP = "|";
110
84
  /** Regex-escaped form of {@link HL_BODY_SEP}, safe for embedding inside a regex. */
@@ -1,11 +1,7 @@
1
1
  import type { HashlineCursor, HashlineEdit } from "./types";
2
2
  export declare function cloneCursor(cursor: HashlineCursor): HashlineCursor;
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): {
3
+ export declare function parseHashline(diff: string): HashlineEdit[];
4
+ export declare function parseHashlineWithWarnings(diff: string): {
9
5
  edits: HashlineEdit[];
10
6
  warnings: string[];
11
7
  };
@@ -44,6 +44,16 @@ export interface SegmentContext {
44
44
  url: string;
45
45
  } | null;
46
46
  };
47
+ usage: {
48
+ fiveHour?: {
49
+ percent: number;
50
+ resetMinutes?: number;
51
+ };
52
+ sevenDay?: {
53
+ percent: number;
54
+ resetHours?: number;
55
+ };
56
+ } | null;
47
57
  }
48
58
  export interface RenderedSegment {
49
59
  content: string;
@@ -53,6 +53,16 @@ export declare class StatusLineComponent implements Component {
53
53
  watchBranch(onBranchChange: () => void): void;
54
54
  dispose(): void;
55
55
  invalidate(): void;
56
+ /**
57
+ * Compute the (cached) used-tokens / context-window totals for the
58
+ * status-line context% segment. Exposed (non-private) so unit tests can
59
+ * verify the incremental-cache invariants; not part of any external
60
+ * API.
61
+ */
62
+ getCachedContextBreakdown(): {
63
+ usedTokens: number;
64
+ contextWindow: number;
65
+ };
56
66
  getTopBorder(width: number): {
57
67
  content: string;
58
68
  width: number;
@@ -171,7 +171,9 @@ export declare class InteractiveMode implements InteractiveModeContext {
171
171
  updateFooter?: boolean;
172
172
  populateHistory?: boolean;
173
173
  }): void;
174
- renderInitialMessages(prebuiltContext?: SessionContext): void;
174
+ renderInitialMessages(prebuiltContext?: SessionContext, options?: {
175
+ preserveExistingChat?: boolean;
176
+ }): void;
175
177
  getUserMessageText(message: Message): string;
176
178
  findLastAssistantMessage(): AssistantMessage | undefined;
177
179
  extractAssistantText(message: AssistantMessage): string;
@@ -3,4 +3,13 @@ import type { TabBarTheme } from "@oh-my-pi/pi-tui";
3
3
  export declare function sanitizeStatusText(text: string): string;
4
4
  /** Shared tab bar theme used by model-selector and settings-selector. */
5
5
  export declare function getTabBarTheme(): TabBarTheme;
6
+ /**
7
+ * Suffix appended to the loader's working message to remind users they can
8
+ * abort with Esc. Rendered with the active theme's bracket glyphs so it stays
9
+ * visually consistent with badges and other bracketed UI affordances.
10
+ *
11
+ * The leading space separates the hint from the message body and is consumed
12
+ * by `endsWith`/`slice` matching in the loader renderer.
13
+ */
14
+ export declare function interruptHint(): string;
6
15
  export { parseCommandArgs } from "../utils/command-args";
@@ -1,13 +1,16 @@
1
1
  import type { Theme, ThemeColor } from "./theme";
2
2
  type ShimmerTheme = Pick<Theme, "bold" | "fg" | "getFgAnsi">;
3
+ type ShimmerPaletteTier = ThemeColor | {
4
+ ansi: string;
5
+ };
3
6
  /** Three-tier color stack a shimmer character cycles through as the band sweeps. */
4
7
  export interface ShimmerPalette {
5
8
  /** Color for chars outside / at the edge of the band (intensity < ~0.22). */
6
- low: ThemeColor;
9
+ low: ShimmerPaletteTier;
7
10
  /** Color for chars approaching the crest (~0.22 ≤ intensity < ~0.65). */
8
- mid: ThemeColor;
11
+ mid: ShimmerPaletteTier;
9
12
  /** Color at the band's crest (intensity ≥ ~0.65). */
10
- high: ThemeColor;
13
+ high: ShimmerPaletteTier;
11
14
  /** Whether to bold the crest tier. Default `false`. */
12
15
  bold?: boolean;
13
16
  }
@@ -168,7 +168,9 @@ export interface InteractiveModeContext {
168
168
  updateFooter?: boolean;
169
169
  populateHistory?: boolean;
170
170
  }): void;
171
- renderInitialMessages(prebuiltContext?: SessionContext): void;
171
+ renderInitialMessages(prebuiltContext?: SessionContext, options?: {
172
+ preserveExistingChat?: boolean;
173
+ }): void;
172
174
  getUserMessageText(message: Message): string;
173
175
  findLastAssistantMessage(): AssistantMessage | undefined;
174
176
  extractAssistantText(message: AssistantMessage): string;
@@ -1,5 +1,7 @@
1
1
  import type { Model } from "@oh-my-pi/pi-ai";
2
+ import type { Skill } from "../../extensibility/skills";
2
3
  import type { AgentSession } from "../../session/agent-session";
4
+ import type { Tool } from "../../tools";
3
5
  import type { theme as Theme } from "../theme/theme";
4
6
  type CategoryId = "systemPrompt" | "systemContext" | "systemTools" | "skills" | "messages";
5
7
  interface CategoryInfo {
@@ -17,6 +19,21 @@ export interface ContextBreakdown {
17
19
  autoCompactBufferTokens: number;
18
20
  freeTokens: number;
19
21
  }
22
+ export declare function estimateSkillsTokens(skills: readonly Skill[]): number;
23
+ export declare function estimateToolSchemaTokens(tools: ReadonlyArray<Pick<Tool, "name" | "description" | "parameters">>): number;
24
+ /**
25
+ * Compute just the NON-MESSAGE token total: system prompt (with its skills
26
+ * section subtracted, since skills are tokenized separately) + system context
27
+ * (the rest of the system-prompt array) + tools + skills.
28
+ *
29
+ * Exposed so callers like `StatusLineComponent` can cache the non-message
30
+ * total separately from the message total. Non-message inputs (skills,
31
+ * tools, system prompt) change rarely; the message list grows on every
32
+ * streaming turn. Splitting the two lets the caller refresh each on its own
33
+ * cadence — non-message recomputed only when the inputs identity changes,
34
+ * messages walked incrementally as new entries append.
35
+ */
36
+ export declare function computeNonMessageTokens(session: AgentSession): number;
20
37
  /**
21
38
  * Compute a breakdown of estimated context usage by category for the active
22
39
  * session and model.
@@ -3,6 +3,9 @@ import type { AssistantMessage, Message } from "@oh-my-pi/pi-ai";
3
3
  import { type Component } from "@oh-my-pi/pi-tui";
4
4
  import type { InteractiveModeContext } from "../../modes/types";
5
5
  import type { SessionContext } from "../../session/session-manager";
6
+ interface RenderInitialMessagesOptions {
7
+ preserveExistingChat?: boolean;
8
+ }
6
9
  export declare class UiHelpers {
7
10
  #private;
8
11
  private ctx;
@@ -31,7 +34,7 @@ export declare class UiHelpers {
31
34
  updateFooter?: boolean;
32
35
  populateHistory?: boolean;
33
36
  }): void;
34
- renderInitialMessages(prebuiltContext?: SessionContext): void;
37
+ renderInitialMessages(prebuiltContext?: SessionContext, options?: RenderInitialMessagesOptions): void;
35
38
  clearEditor(): void;
36
39
  showError(errorMessage: string): void;
37
40
  showWarning(warningMessage: string): void;
@@ -47,3 +50,4 @@ export declare class UiHelpers {
47
50
  findLastAssistantMessage(): AssistantMessage | undefined;
48
51
  extractAssistantText(message: AssistantMessage): string;
49
52
  }
53
+ export {};
@@ -417,6 +417,15 @@ export declare class AgentSession {
417
417
  refreshRpcHostTools(rpcTools: AgentTool[]): Promise<void>;
418
418
  /** Whether auto-compaction is currently running */
419
419
  get isCompacting(): boolean;
420
+ /**
421
+ * Whether idle-flush tasks, auto-continuations, or other short-lived
422
+ * post-prompt work are pending. True in the brief window after
423
+ * `session.prompt()` returns but before a scheduled background delivery
424
+ * (e.g. an async-job result) has finished its own streaming turn.
425
+ * Loop-mode and similar auto-submit paths should treat this as a block
426
+ * to avoid racing against the delivery turn.
427
+ */
428
+ get hasPostPromptWork(): boolean;
420
429
  /** All messages including custom types like BashExecutionMessage */
421
430
  get messages(): AgentMessage[];
422
431
  buildDisplaySessionContext(): SessionContext;
@@ -7,7 +7,7 @@ import type { AgentTelemetryConfig, ThinkingLevel } from "@oh-my-pi/pi-agent-cor
7
7
  import { ModelRegistry } from "../config/model-registry";
8
8
  import type { PromptTemplate } from "../config/prompt-templates";
9
9
  import { Settings } from "../config/settings";
10
- import type { Skill } from "../extensibility/skills";
10
+ import { type Skill } from "../extensibility/skills";
11
11
  import type { HindsightSessionState } from "../hindsight/state";
12
12
  import type { LocalProtocolOptions } from "../internal-urls";
13
13
  import type { MCPManager } from "../mcp/manager";
@@ -73,6 +73,8 @@ export interface ExecutorOptions {
73
73
  * transition explicitly.
74
74
  */
75
75
  parentTelemetry?: AgentTelemetryConfig;
76
+ /** Skills to autoload via sendCustomMessage before the first prompt */
77
+ autoloadSkills?: Skill[];
76
78
  }
77
79
  export interface YieldItem {
78
80
  data?: unknown;
@@ -148,6 +148,7 @@ export interface AgentDefinition {
148
148
  thinkingLevel?: ThinkingLevel;
149
149
  output?: unknown;
150
150
  blocking?: boolean;
151
+ autoloadSkills?: string[];
151
152
  source: AgentSource;
152
153
  filePath?: string;
153
154
  }
@@ -189,6 +190,30 @@ export interface AgentProgress {
189
190
  modelOverride?: string | string[];
190
191
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
191
192
  extractedToolData?: Record<string, unknown[]>;
193
+ /**
194
+ * Auto-retry state when the subagent is sleeping between provider retries
195
+ * (e.g. 429 rate-limit with retry-after). Cleared when the retry resolves
196
+ * or fails. Surfacing this to the parent prevents the task tool from
197
+ * looking indefinitely "in progress" when a child is actually blocked on
198
+ * provider quota.
199
+ */
200
+ retryState?: {
201
+ attempt: number;
202
+ maxAttempts: number;
203
+ delayMs: number;
204
+ errorMessage: string;
205
+ startedAtMs: number;
206
+ };
207
+ /**
208
+ * Terminal retry failure surfaced once the subagent gave up retrying
209
+ * (e.g. retry-after exceeded the cap, or all attempts exhausted). Carries
210
+ * the final error so the parent UI can render "blocked: rate-limited"
211
+ * instead of waiting for a status that never arrives.
212
+ */
213
+ retryFailure?: {
214
+ attempt: number;
215
+ errorMessage: string;
216
+ };
192
217
  }
193
218
  /** Result from a single agent execution */
194
219
  export interface SingleResult {
@@ -227,6 +252,16 @@ export interface SingleResult {
227
252
  nestedPatches?: NestedRepoPatch[];
228
253
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
229
254
  extractedToolData?: Record<string, unknown[]>;
255
+ /**
256
+ * Terminal retry failure, when the subagent exited because the auto-retry
257
+ * loop gave up (retry-after exceeded the cap, or all attempts exhausted).
258
+ * Lets the parent task tool surface a "blocked: rate-limited" outcome
259
+ * instead of a generic failure.
260
+ */
261
+ retryFailure?: {
262
+ attempt: number;
263
+ errorMessage: string;
264
+ };
230
265
  /** Output metadata for agent:// URL integration */
231
266
  outputMeta?: {
232
267
  lineCount: number;
@@ -9,8 +9,3 @@ export interface BashFixupResult {
9
9
  * or no-op transform, returns the input verbatim with `stripped: []`.
10
10
  */
11
11
  export declare function applyBashFixups(command: string): BashFixupResult;
12
- /**
13
- * Human-readable notice for the fixups that fired. Mirrors the shape of
14
- * `formatTimeoutClampNotice` so it can ride alongside the other bash notices.
15
- */
16
- export declare function formatBashFixupNotice(stripped: readonly string[]): string | undefined;
@@ -13,7 +13,9 @@ export declare function copyToClipboard(text: string): Promise<void>;
13
13
  * Read an image from the system clipboard.
14
14
  *
15
15
  * Returns null on Termux (no image clipboard support) or when no display
16
- * server is available (headless/SSH without forwarding).
16
+ * server is available (headless/SSH without forwarding). Under WSL the
17
+ * Windows clipboard is reached through `powershell.exe`, since WSLg's
18
+ * Wayland clipboard does not carry image payloads through to `arboard`.
17
19
  *
18
20
  * @returns PNG payload or null when no image is available.
19
21
  */
@@ -4,6 +4,7 @@ export interface ImageResizeOptions {
4
4
  maxHeight?: number;
5
5
  maxBytes?: number;
6
6
  jpegQuality?: number;
7
+ excludeWebP?: boolean;
7
8
  }
8
9
  export interface ResizedImage {
9
10
  buffer: Uint8Array;
@@ -20,11 +21,13 @@ export interface ResizedImage {
20
21
  *
21
22
  * Strategy:
22
23
  * 1. Probe metadata. If already within all limits, return original.
23
- * 2. Resize to fit max dimensions and encode at high quality across PNG/JPEG/WebP — return smallest.
24
+ * 2. Resize to fit max dimensions and encode at high quality across PNG/JPEG (+ WebP) — return smallest.
24
25
  * 3. If still too large, walk a lossy JPEG/WebP quality ladder.
25
26
  * 4. If still too large, walk a dimension-scale ladder × quality ladder.
26
27
  * 5. If still too large, return the smallest variant produced.
27
28
  *
29
+ * Set OMP_NO_WEBP to exclude WebP from encoding (llama.cpp STB doesn't decode it).
30
+ *
28
31
  * Backed by `Bun.Image`: a chainable native pipeline that runs decode/transform/encode
29
32
  * off the JS thread when the terminal (`.bytes()`) is awaited.
30
33
  */
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.2.3",
4
+ "version": "15.3.0",
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.2.3",
51
- "@oh-my-pi/pi-agent-core": "15.2.3",
52
- "@oh-my-pi/pi-ai": "15.2.3",
53
- "@oh-my-pi/pi-natives": "15.2.3",
54
- "@oh-my-pi/pi-tui": "15.2.3",
55
- "@oh-my-pi/pi-utils": "15.2.3",
50
+ "@oh-my-pi/omp-stats": "15.3.0",
51
+ "@oh-my-pi/pi-agent-core": "15.3.0",
52
+ "@oh-my-pi/pi-ai": "15.3.0",
53
+ "@oh-my-pi/pi-natives": "15.3.0",
54
+ "@oh-my-pi/pi-tui": "15.3.0",
55
+ "@oh-my-pi/pi-utils": "15.3.0",
56
56
  "@puppeteer/browsers": "^2.13.0",
57
57
  "@types/turndown": "5.0.6",
58
58
  "@xterm/headless": "^6.0.0",
@@ -8,7 +8,7 @@ import {
8
8
  parseFrontmatter,
9
9
  prompt,
10
10
  } from "@oh-my-pi/pi-utils";
11
- import { computeLineHash, HL_BODY_SEP, HL_EDIT_SEP } from "../hashline/hash";
11
+ import { computeLineHash, HL_BODY_SEP } from "../hashline/hash";
12
12
  import { jtdToTypeScript } from "../tools/jtd-to-typescript";
13
13
  import { parseCommandArgs, substituteArgs } from "../utils/command-args";
14
14
 
@@ -154,13 +154,6 @@ prompt.registerHelper("hline", function (this: unknown, ...args: unknown[]): str
154
154
  return `${ref}${HL_BODY_SEP}${text}`;
155
155
  });
156
156
 
157
- /**
158
- * {{hsep}} — emit the configured hashline payload separator character.
159
- * Stays in sync with {@link HL_EDIT_SEP} so edit prompt templates
160
- * never have to hardcode the payload separator.
161
- */
162
- prompt.registerHelper("hsep", (): string => HL_EDIT_SEP);
163
-
164
157
  const INLINE_ARG_SHELL_PATTERN = /\$(?:ARGUMENTS|@(?:\[\d+(?::\d*)?\])?|\d+)/;
165
158
  const INLINE_ARG_TEMPLATE_PATTERN = /\{\{[\s\S]*?(?:\b(?:arguments|ARGUMENTS|args)\b|\barg\s+[^}]+)[\s\S]*?\}\}/;
166
159