@oh-my-pi/pi-coding-agent 15.10.2 → 15.10.4

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 (95) hide show
  1. package/CHANGELOG.md +66 -1
  2. package/dist/types/cli/gallery-fixtures/types.d.ts +7 -1
  3. package/dist/types/edit/index.d.ts +0 -1
  4. package/dist/types/eval/__tests__/js-context-manager.test.d.ts +1 -0
  5. package/dist/types/eval/bridge-timeout.d.ts +1 -1
  6. package/dist/types/eval/{llm-bridge.d.ts → completion-bridge.d.ts} +8 -8
  7. package/dist/types/eval/idle-timeout.d.ts +1 -1
  8. package/dist/types/lsp/index.d.ts +0 -5
  9. package/dist/types/main.d.ts +11 -0
  10. package/dist/types/modes/components/assistant-message.d.ts +0 -9
  11. package/dist/types/modes/components/late-diagnostics-message.d.ts +20 -0
  12. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  13. package/dist/types/modes/components/session-selector.d.ts +16 -7
  14. package/dist/types/modes/components/tool-execution.d.ts +0 -18
  15. package/dist/types/modes/types.d.ts +4 -0
  16. package/dist/types/session/messages.d.ts +11 -8
  17. package/dist/types/session/yield-queue.d.ts +10 -1
  18. package/dist/types/tools/eval-render.d.ts +0 -1
  19. package/dist/types/tools/index.d.ts +31 -0
  20. package/dist/types/tools/path-utils.d.ts +5 -1
  21. package/dist/types/tools/read.d.ts +2 -1
  22. package/dist/types/tools/render-utils.d.ts +3 -1
  23. package/dist/types/tools/renderers.d.ts +0 -15
  24. package/dist/types/tools/write.d.ts +0 -2
  25. package/dist/types/tui/code-cell.d.ts +0 -2
  26. package/dist/types/tui/hyperlink.d.ts +5 -7
  27. package/dist/types/tui/output-block.d.ts +0 -18
  28. package/package.json +9 -9
  29. package/src/cli/gallery-cli.ts +4 -0
  30. package/src/cli/gallery-fixtures/codeintel.ts +0 -1
  31. package/src/cli/gallery-fixtures/fs.ts +68 -1
  32. package/src/cli/gallery-fixtures/types.ts +8 -1
  33. package/src/commit/agentic/agent.ts +1 -0
  34. package/src/edit/hashline/diff.ts +86 -0
  35. package/src/edit/hashline/execute.ts +14 -1
  36. package/src/edit/index.ts +31 -17
  37. package/src/edit/renderer.ts +116 -31
  38. package/src/eval/__tests__/agent-bridge.test.ts +13 -0
  39. package/src/eval/__tests__/{llm-bridge.test.ts → completion-bridge.test.ts} +60 -54
  40. package/src/eval/__tests__/js-context-manager.test.ts +241 -0
  41. package/src/eval/agent-bridge.ts +6 -1
  42. package/src/eval/bridge-timeout.ts +1 -1
  43. package/src/eval/{llm-bridge.ts → completion-bridge.ts} +30 -27
  44. package/src/eval/idle-timeout.ts +1 -1
  45. package/src/eval/js/context-manager.ts +66 -6
  46. package/src/eval/js/shared/prelude.txt +28 -12
  47. package/src/eval/js/tool-bridge.ts +3 -3
  48. package/src/eval/js/worker-entry.ts +6 -0
  49. package/src/eval/py/prelude.py +3 -3
  50. package/src/internal-urls/docs-index.generated.ts +8 -7
  51. package/src/lsp/index.ts +128 -52
  52. package/src/main.ts +54 -14
  53. package/src/modes/components/assistant-message.ts +3 -15
  54. package/src/modes/components/late-diagnostics-message.ts +60 -0
  55. package/src/modes/components/plan-review-overlay.ts +26 -5
  56. package/src/modes/components/read-tool-group.ts +415 -35
  57. package/src/modes/components/session-selector.ts +89 -35
  58. package/src/modes/components/tips.txt +1 -1
  59. package/src/modes/components/tool-execution.ts +7 -49
  60. package/src/modes/components/transcript-container.ts +108 -32
  61. package/src/modes/controllers/event-controller.ts +6 -1
  62. package/src/modes/controllers/input-controller.ts +10 -2
  63. package/src/modes/types.ts +4 -0
  64. package/src/modes/utils/ui-helpers.ts +26 -5
  65. package/src/prompts/system/manual-continue.md +7 -0
  66. package/src/prompts/system/plan-mode-active.md +56 -72
  67. package/src/prompts/system/tiny-title-system.md +1 -1
  68. package/src/prompts/system/title-system.md +16 -3
  69. package/src/prompts/system/workflow-notice.md +1 -1
  70. package/src/prompts/tools/eval.md +6 -4
  71. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  72. package/src/sdk.ts +59 -1
  73. package/src/session/agent-session.ts +5 -3
  74. package/src/session/messages.ts +21 -14
  75. package/src/session/session-manager.ts +2 -2
  76. package/src/session/yield-queue.ts +20 -2
  77. package/src/task/executor.ts +1 -0
  78. package/src/tiny/title-client.ts +6 -1
  79. package/src/tools/bash.ts +0 -7
  80. package/src/tools/eval-render.ts +6 -25
  81. package/src/tools/eval.ts +1 -1
  82. package/src/tools/find.ts +148 -106
  83. package/src/tools/index.ts +32 -0
  84. package/src/tools/path-utils.ts +19 -22
  85. package/src/tools/read.ts +16 -8
  86. package/src/tools/render-utils.ts +3 -1
  87. package/src/tools/renderers.ts +0 -15
  88. package/src/tools/ssh.ts +0 -1
  89. package/src/tools/todo.ts +1 -0
  90. package/src/tools/write.ts +3 -12
  91. package/src/tui/code-cell.ts +1 -6
  92. package/src/tui/hyperlink.ts +13 -23
  93. package/src/tui/output-block.ts +2 -97
  94. package/src/utils/title-generator.ts +2 -2
  95. /package/dist/types/eval/__tests__/{llm-bridge.test.d.ts → completion-bridge.test.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,7 +2,72 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.10.4] - 2026-06-08
6
+
7
+ ### Added
8
+
9
+ - macOS release binaries are now signed with a Developer ID Application identity (hardened runtime + secure timestamp + JIT/library-validation entitlements) and notarized in CI when the `APPLE_*` signing secrets are configured; releases auto-fall back to ad-hoc signing until then. This makes the shipped binaries Gatekeeper-acceptable, unblocking an official Homebrew submission ([#776](https://github.com/can1357/oh-my-pi/issues/776)). See `docs/macos-signing-notarization.md`.
10
+ - Added a Homebrew install path: `brew install can1357/tap/omp`. The [can1357/homebrew-tap](https://github.com/can1357/homebrew-tap) formula installs the prebuilt release binary, and a `release_brew` CI job regenerates it (version + per-asset sha256) from each published release via `scripts/ci-update-brew-formula.ts` ([#776](https://github.com/can1357/oh-my-pi/issues/776)).
11
+
12
+ ### Changed
13
+
14
+ - Adjusted `completion()` model resolution so the `default` tier now prefers the session’s active model and falls back to the configured default role when needed
15
+ - Rewrote the session auto-title prompt (`prompts/system/title-system.md`) and the `set_title` tool description to ask for a concise, sentence-case title (3-7 words) that captures the session's topic/goal, with good/bad examples and explicit guidance to treat the first message as data (no following embedded links/instructions, no refusals, describe URL/reference asks). The local on-device title prompt (`tiny-title-system.md`) was aligned to the same 3-7 word, sentence-case convention. The deterministic greeting/low-signal filter and the `none` deferral sentinel are unchanged.
16
+ - Renamed the eval oneshot helper from `llm()` to `completion()` in both JavaScript and Python preludes, including status events, prompt docs, and runtime tests.
17
+
18
+ ### Fixed
19
+
20
+ - Fixed `completion()` to always send a non-empty default system prompt when `system` is omitted so providers that require instructions no longer reject requests
21
+ - Fixed structured `completion()` mode to return parsed JSON from plain text output when the model skips the forced `respond` tool call
22
+ - Fixed slow-tier `completion()` reasoning requests to avoid unsupported effort settings by only enabling reasoning on reasoning-capable models and capping effort to supported levels
23
+ - Fixed JS eval worker reset/dispose to close workers gracefully before forced termination, avoiding Bun 1.3.14 N-API teardown crashes with native modules such as `canvas`.
24
+
25
+ ## [15.10.3] - 2026-06-08
26
+
27
+ ### Added
28
+
29
+ - Added clickable file path hyperlinks to read tool outputs (read-call rows, grouped summaries, and inline previews) using resolved or absolute file targets with selector-based line anchors for quick navigation
30
+ - Added a resolved-span echo to `replace block`/`delete block` edits: a successful block op now prints `replace block N → resolved lines A-B (K lines)` between the section header and the diff preview, so the model can confirm tree-sitter matched the construct it intended (e.g. catch a decorator left outside the block) instead of inferring the span from the diff after the fact.
31
+
32
+ ### Changed
33
+
34
+ - Changed the `find` tool to process each explicit multi-path target separately before merging results so searches stay scoped to the requested paths
35
+ - Changed multi-path `find` handling so invalid extra targets no longer fail the whole query and now return matches from valid targets only
36
+ - Changed background-job completion and late LSP diagnostic delivery to inject at the next agent step boundary (mid-run), via the new non-interrupting "aside" channel, instead of only when the agent reaches a yield/follow-up point. The model now sees these notifications between its own requests without the turn having to end first, and in-flight tools are never interrupted; `job`-poll acknowledgement still suppresses results the agent already saw.
37
+ - Changed late LSP diagnostics after edit or write to surface in the chat transcript as `Late diagnostics` entries rendered through the same grouped tree renderer the `edit`/`write` tools use (per-file nodes, severity icons, `:line:col` locations), and to honor the global tool-output expand toggle (collapsed entries cap at 5 diagnostics with a `… N more` hint)
38
+ - Changed delayed diagnostics delivery to batch late results in one message per flush instead of a raw hidden custom payload
39
+ - Changed hidden custom messages and file-mention context to reach providers as `developer` messages instead of user-authored turns, so system reminders no longer pollute compacted user history.
40
+ - Rewrote the plan-mode active prompt (`prompts/system/plan-mode-active.md`) from scratch to stop producing shallow plans. Reframed the artifact as an **execution spec** a fresh agent runs after the planning conversation is cleared/compacted (zero design decisions for the implementer) rather than a brevity-capped summary. Folded high-consensus requirements into the existing sections as inline, conditional rules — no new boilerplate sections: ordered Approach steps that keep the build/tests green after each step (sequencing); exact signatures/literals for new or load-bearing symbols (contracts); full callsite list + clean cutover for renames/signature-changes/removals; Verification that must exercise the new behavior (input → observable output) with run preconditions, not just build/typecheck; Assumptions restricted to user-overridable choices plus pre-decided fallbacks for load-bearing assumptions; a provenance rule (plan facts must come from a read this session; unverified claims flagged inline); and bans on conversation back-references and decision-free sections (Non-Goals/Alternatives/Risks/Future Work). Kept the decision-complete self-check and the brevity-vs-completeness tiebreak (completeness wins). Render contract (Handlebars vars/conditionals) unchanged; verified across all `planExists`/`reentry`/`iterative` branch combinations.
41
+
42
+ ### Fixed
43
+
44
+ - Fixed duplicate `find` matches in multi-target queries by deduplicating overlapping paths in merged results
45
+ - Fixed `find` partial updates to avoid repeated streamed rows while scans are still running
46
+ - Fixed stale late diagnostics from older edits being shown after a file was edited again
47
+ - Fixed read output paths so selector suffixes are preserved when corrected paths were returned without selectors
48
+ - Fixed `read` surfacing a misleading red "Operation aborted" on a plain-file or directory read when a turn was interrupted mid-read. Those reads are deterministic and fast, so `execute` now runs them to completion instead of cancelling them; slower/non-deterministic reads (archive, sqlite, document, image, summary, conflict scan, URL) stay cancellable.
49
+ - Fixed edit tool headers to hide first-change line suffixes, middle-elide long paths only when the header width needs it, show compact change stats, and target encoded `file://` hyperlinks.
50
+ - Fixed Esc interrupts rendering a redundant `Interrupted by user` assistant transcript line while preserving the interrupt reason for tool-result placeholders and continuation logic.
51
+ - LSP writethrough no longer burns the full diagnostics poll on every edit/write. `typescript-language-server` never echoes the document version in `publishDiagnostics` ([upstream #983](https://github.com/typescript-language-server/typescript-language-server/issues/983)), so the exact-version gate never passed; `waitForDiagnostics` now accepts an exact version match instantly and otherwise settles on the latest publish after a short quiescence window, dropping superseded in-flight diagnostics.
52
+ - LSP writethrough no longer blocks the whole edit/write on slow diagnostics: it now waits only a short inline window (~500ms) for a settled result, then hands the in-flight fetch to the deferred channel so a slow or cold language server (e.g. a large-project `tsserver`) delivers its diagnostics as a follow-up message instead of stalling the tool 3–5s on every edit. The background fetch also gets a longer budget so slow servers still surface late rather than being dropped.
53
+ - Fixed the `c`/`.` continue shortcut making the agent second-guess itself after an Esc interrupt. Continuing used to submit an *empty* user turn, which left the model with only the aborted-turn context — so it tended to restate the halted state and ask whether to proceed rather than just continuing. The shortcut now resumes with a hidden agent-authored `developer` directive ("keep going — don't stop to summarize or re-confirm the plan") instead of an empty turn. It still produces no visible transcript entry, same as before.
54
+ - Fixed native scrollback commit boundaries to be computed generically from finalized transcript blocks and observed append-only live growth, so tall final tool results and streaming previews keep their scrolled-off heads on ED3-risk terminals without per-tool append-only predicates; live blocks that re-layout remain deferred until finalization or the next checkpoint.
55
+ - Fixed read-group summaries for multi-path `read` results to use result-provided display targets so each resolved path is shown as its own row
56
+ - Fixed read-group range summaries to abbreviate long merged selectors with ellipsis to keep repeated-file range rows readable
57
+ - Fixed read-group TUI summaries so a single delimited `read` call renders as separate read rows, and repeated reads of the same file collapse under one file with full-file/range children.
58
+ - Fixed grouped `read` rows freezing on their pending "⏳ Read <path>" preview on ED3-risk terminals (ghostty/kitty/iTerm2/…) when a parallel sibling tool closed the read run and appended a block below the group before the read's result arrived. The read-group block now stays in the repaintable live region until its entries settle, so the late success result repaints instead of being stranded; a `seal()` escape hatch (turn end / transcript rebuild) still lets a never-delivered read freeze rather than pinning the live region.
59
+ - Fixed session search to return all sessions unchanged when the query is blank
60
+ - Fixed duplicate session suggestions by deduplicating history matches by session path when merging metadata and prompt-history results
61
+ - Fixed `/resume` search ranking so sessions whose prompts or metadata match the query now prefer prompt recency and recent literal matches instead of letting older earlier-title fuzzy matches outrank a just-used session.
62
+ - Fixed `omp --resume <id>` / `--fork <id>` crashing with `[Uncaught Exception]` when the id did not match a known session. `createSessionManager` now throws a dedicated `SessionResolutionError`, which `runRootCommand` catches to print `Error: Session "..." not found.` plus a hint to stderr and exit with code 1. The same path covers `--fork` combined with `--no-session` and the non-interactive cross-project / moved-cwd prompts that previously surfaced raw stack traces ([#2084](https://github.com/can1357/oh-my-pi/issues/2084)).
63
+
64
+ ### Removed
65
+
66
+ - Removed the animated pending border ("shimmer") on running `bash`, `eval`, and `ssh` execution blocks. While pending, a block now shows a static accent border instead of sweeping a dark segment around its bottom edge; `display.shimmer` still governs the working-status line and `task` row animations.
67
+ - Removed the tool-level `nonAbortable` bypass so `write` and `edit` honor the active turn `AbortSignal`. `read` is abortable for everything that is slow or non-deterministic (URL/internal-URL reads, archive, sqlite, document conversion, image decode, structural summary, conflict scan, suffix glob); only the deterministic plain-file line/range reads and directory listings run to completion.
68
+
5
69
  ## [15.10.2] - 2026-06-08
70
+
6
71
  ### Added
7
72
 
8
73
  - Added `raw-sse.txt` to debug report bundles, exporting recent raw provider SSE diagnostics when captured
@@ -9637,4 +9702,4 @@ Initial public release.
9637
9702
  - Git branch display in footer
9638
9703
  - Message queueing during streaming responses
9639
9704
  - OAuth integration for Gmail and Google Calendar access
9640
- - HTML export with syntax highlighting and collapsible sections
9705
+ - HTML export with syntax highlighting and collapsible sections
@@ -14,14 +14,20 @@ export interface GalleryResult {
14
14
  details?: unknown;
15
15
  isError?: boolean;
16
16
  }
17
+ export type GalleryFixtureState = "streaming" | "progress" | "success" | "error";
17
18
  export interface GalleryFixture {
18
19
  /** Display label for the tool header (defaults to the tool name). */
19
20
  label?: string;
20
21
  /** Edit mode for edit-like tools so the streaming preview dispatches correctly. */
21
22
  editMode?: EditMode;
23
+ /**
24
+ * Custom gallery-only renderer for fixtures that are not one ToolExecutionComponent
25
+ * (for example the read-group transcript component).
26
+ */
27
+ renderState?: (state: GalleryFixtureState, width: number, expanded: boolean) => string[] | Promise<string[]>;
22
28
  /**
23
29
  * Set for tools whose real `AgentTool` attaches `renderCall`/`renderResult`
24
- * directly on the instance (e.g. `lsp`, `task`). The harness then attaches
30
+ * directly on the instance (e.g. `task`). The harness then attaches
25
31
  * the registry renderer onto the fake tool so the component routes through
26
32
  * the custom-tool branch — the same path production takes — instead of the
27
33
  * built-in registry branch. The two branches can diverge, so exercising the
@@ -28,7 +28,6 @@ export declare class EditTool implements AgentTool<TInput> {
28
28
  readonly name = "edit";
29
29
  readonly label = "Edit";
30
30
  readonly loadMode = "essential";
31
- readonly nonAbortable = true;
32
31
  readonly concurrency = "exclusive";
33
32
  readonly strict = true;
34
33
  constructor(session: ToolSession);
@@ -2,7 +2,7 @@
2
2
  * Timeout suspension for in-flight host-side eval bridge calls.
3
3
  *
4
4
  * The eval watchdog caps a cell's `timeout` as a budget on the cell runtime's
5
- * own work. Host-side `agent()` / `parallel()` / `llm()` bridge calls hand
5
+ * own work. Host-side `agent()` / `parallel()` / `completion()` bridge calls hand
6
6
  * control to the outer TypeScript process, where the Python kernel or JS VM is
7
7
  * only waiting for a result. While that delegated work is in flight, the cell
8
8
  * timeout must be ignored completely; once the bridge returns and the runtime is
@@ -1,25 +1,25 @@
1
1
  import type { ToolSession } from "../tools";
2
2
  import type { JsStatusEvent } from "./js/shared/types";
3
- /** Synthetic bridge name reserved for the `llm()` helper across both runtimes. */
4
- export declare const EVAL_LLM_BRIDGE_NAME = "__llm__";
5
- type LlmTier = "smol" | "default" | "slow";
6
- export interface EvalLlmBridgeOptions {
3
+ /** Synthetic bridge name reserved for the `completion()` helper across both runtimes. */
4
+ export declare const EVAL_COMPLETION_BRIDGE_NAME = "__completion__";
5
+ type CompletionTier = "smol" | "default" | "slow";
6
+ export interface EvalCompletionBridgeOptions {
7
7
  session: ToolSession;
8
8
  signal?: AbortSignal;
9
9
  emitStatus?: (event: JsStatusEvent) => void;
10
10
  }
11
- export interface EvalLlmResult {
11
+ export interface EvalCompletionResult {
12
12
  text: string;
13
13
  details: {
14
14
  model: string;
15
- tier: LlmTier;
15
+ tier: CompletionTier;
16
16
  structured: boolean;
17
17
  };
18
18
  }
19
19
  /**
20
- * Run a single stateless completion on behalf of an eval cell's `llm()` call.
20
+ * Run a single stateless completion on behalf of an eval cell's `completion()` call.
21
21
  * Returns a `{ text, details }` value shaped like a {@link callSessionTool}
22
22
  * result so the existing bridge transport carries it to either runtime.
23
23
  */
24
- export declare function runEvalLlm(args: unknown, options: EvalLlmBridgeOptions): Promise<EvalLlmResult>;
24
+ export declare function runEvalCompletion(args: unknown, options: EvalCompletionBridgeOptions): Promise<EvalCompletionResult>;
25
25
  export {};
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * A cell's `timeout` bounds time while the Python kernel or JS VM is in control.
5
5
  * Host-side bridge calls can {@link pause} the watchdog so delegated
6
- * `agent()`/`parallel()`/`llm()` work is ignored completely, then {@link resume}
6
+ * `agent()`/`parallel()`/`completion()` work is ignored completely, then {@link resume}
7
7
  * starts a fresh timeout window once the runtime gets control back.
8
8
  *
9
9
  * The active timer self-reschedules instead of being torn down on every
@@ -3,7 +3,6 @@ import type { BunFile } from "bun";
3
3
  import { type Theme } from "../modes/theme/theme";
4
4
  import type { ToolSession } from "../tools";
5
5
  import { type LspServerStatus } from "./client";
6
- import { renderCall, renderResult } from "./render";
7
6
  import { type LspParams, type LspToolDetails, lspSchema } from "./types";
8
7
  export type { LspServerStatus } from "./client";
9
8
  export type { LspToolDetails } from "./types";
@@ -129,10 +128,6 @@ export declare class LspTool implements AgentTool<typeof lspSchema, LspToolDetai
129
128
  timeout: import("zod/v4").ZodOptional<import("zod/v4").ZodNumber>;
130
129
  payload: import("zod/v4").ZodOptional<import("zod/v4").ZodString>;
131
130
  }, import("zod/v4/core").$strip>;
132
- readonly renderCall: typeof renderCall;
133
- readonly renderResult: typeof renderResult;
134
- readonly mergeCallAndResult = true;
135
- readonly inline = true;
136
131
  readonly strict = true;
137
132
  constructor(session: ToolSession);
138
133
  static createIf(session: ToolSession): LspTool | null;
@@ -37,6 +37,17 @@ export interface AcpSessionFactoryOptions {
37
37
  export declare function createAcpSessionFactory(args: AcpSessionFactoryOptions): AcpSessionFactory;
38
38
  type SessionPromptResult = "accepted" | "declined" | "unavailable";
39
39
  type SessionPrompt = (session: SessionInfo) => Promise<SessionPromptResult>;
40
+ /**
41
+ * Friendly CLI failure raised by {@link createSessionManager} when the user's
42
+ * session-resolution flags (`--resume`/`--fork`/cross-project prompts) cannot
43
+ * be satisfied. {@link runRootCommand} catches it and prints a clean stderr
44
+ * message instead of letting it surface as `[Uncaught Exception]`
45
+ * (see issue #2084).
46
+ */
47
+ export declare class SessionResolutionError extends Error {
48
+ readonly hint?: string;
49
+ constructor(message: string, hint?: string);
50
+ }
40
51
  /** Resolves CLI session flags into an existing, forked, in-memory, or cancelled session manager. */
41
52
  export declare function createSessionManager(parsed: Args, cwd: string, activeSettings?: Settings, askToForkSession?: SessionPrompt, askToMoveSession?: SessionPrompt): Promise<SessionManager | undefined>;
42
53
  interface RunRootCommandDependencies {
@@ -19,15 +19,6 @@ export declare class AssistantMessageComponent extends Container {
19
19
  */
20
20
  setErrorPinned(pinned: boolean): void;
21
21
  isTranscriptBlockFinalized(): boolean;
22
- /**
23
- * Assistant text/thinking streams in append-only: earlier rendered rows never
24
- * re-layout, new content only grows the block at the bottom. The transcript
25
- * reports this so the renderer may commit scrolled-off head rows of a long
26
- * streamed reply to native scrollback instead of dropping them (see
27
- * `NativeScrollbackLiveRegion#getNativeScrollbackCommitSafeEnd`). Volatile
28
- * blocks (tool previews that collapse) intentionally do not implement this.
29
- */
30
- isTranscriptBlockAppendOnly(): boolean;
31
22
  markTranscriptBlockFinalized(): void;
32
23
  setToolResultImages(toolCallId: string, images: ImageContent[]): void;
33
24
  setUsageInfo(usage: Usage): void;
@@ -0,0 +1,20 @@
1
+ import { Container } from "@oh-my-pi/pi-tui";
2
+ /** One file's worth of late LSP diagnostics, as carried on the transcript message. */
3
+ export interface LateDiagnosticsFile {
4
+ path?: string;
5
+ summary?: string;
6
+ errored?: boolean;
7
+ messages?: string[];
8
+ }
9
+ /**
10
+ * Renders late LSP diagnostics (arrived after edit/write returned) in the
11
+ * transcript, reusing the same tree renderer the edit/write tools use so the
12
+ * styling stays consistent. Supports the global tool-output expand toggle.
13
+ */
14
+ export declare class LateDiagnosticsMessageComponent extends Container {
15
+ #private;
16
+ private readonly files;
17
+ constructor(files: LateDiagnosticsFile[]);
18
+ setExpanded(expanded: boolean): void;
19
+ invalidate(): void;
20
+ }
@@ -16,6 +16,12 @@ export declare class ReadToolGroupComponent extends Container implements ToolExe
16
16
  constructor(options?: ReadToolGroupOptions);
17
17
  isTranscriptBlockFinalized(): boolean;
18
18
  finalize(): void;
19
+ /**
20
+ * Force the group terminal even if an entry never received its result (the
21
+ * turn aborted or ended). Lets it freeze and stop pinning the transcript live
22
+ * region instead of lingering on a pending preview until the next thaw.
23
+ */
24
+ seal(): void;
19
25
  updateArgs(args: ReadRenderArgs, toolCallId?: string): void;
20
26
  updateResult(result: {
21
27
  content: Array<{
@@ -3,17 +3,26 @@ import type { SessionInfo } from "../../session/session-manager";
3
3
  /** Returns the IDs of sessions whose recorded prompts match a query, best first. */
4
4
  export type SessionHistoryMatcher = (query: string) => string[];
5
5
  /**
6
- * Combine fuzzy session matches with prompt-history matches for ranking, using
7
- * both signals rather than replacing one with the other.
6
+ * Filter and rank session picker search results.
8
7
  *
9
- * - `fuzzy` is the ordered fuzzy-filter result over session metadata (best first).
8
+ * Resume search narrows a recency-sorted list: once every query token appears
9
+ * as a literal substring, newer sessions should beat a slightly better fuzzy
10
+ * position match. Pure fuzzy/acronym matches still sort by fuzzy score after
11
+ * literal matches.
12
+ */
13
+ export declare function rankSessionSearchMatches(allSessions: SessionInfo[], query: string): SessionInfo[];
14
+ /**
15
+ * Combine metadata matches with prompt-history matches for ranking, using both
16
+ * signals rather than replacing one with the other.
17
+ *
18
+ * - `fuzzy` is the ordered metadata/session-text result.
10
19
  * - `historyIds` are session IDs whose recorded prompts matched the query,
11
20
  * ordered by prompt-history rank (typically newest matching prompt first); duplicates are tolerated.
12
21
  *
13
- * Ranking: sessions matched by **both** signals lead (keeping fuzzy order), then
14
- * fuzzy-only matches, then history-only matches (by prompt-history order). A fuzzy match
15
- * is never dropped, and history matches not present in `allSessions` (e.g. deleted
16
- * or out-of-scope sessions) are ignored since they cannot be resumed from here.
22
+ * Ranking: prompt-history matches lead in history order, then remaining
23
+ * metadata matches keep their existing order. A metadata match is never dropped,
24
+ * and history matches not present in `allSessions` (e.g. deleted or out-of-scope
25
+ * sessions) are ignored since they cannot be resumed from here.
17
26
  */
18
27
  export declare function mergeSessionRanking(allSessions: SessionInfo[], fuzzy: SessionInfo[], historyIds: string[]): SessionInfo[];
19
28
  /**
@@ -61,24 +61,6 @@ export declare class ToolExecutionComponent extends Container {
61
61
  * past, or an explicit {@link seal} flips it to `true`.
62
62
  */
63
63
  isTranscriptBlockFinalized(): boolean;
64
- /**
65
- * While a tool's preview is still streaming, a block whose preview is
66
- * append-only (rows only grow at the bottom, never re-layout) lets the
67
- * renderer commit the scrolled-off head of an over-tall preview to native
68
- * scrollback instead of dropping it — the same anti-yank path a streaming
69
- * assistant reply uses (see {@link TranscriptContainer} +
70
- * `NativeScrollbackLiveRegion`). Covers both phases: a pre-result call preview
71
- * (a `write` whose content streams in) and a partial-result preview that
72
- * streams output below fixed input (an `eval`/`bash` whose stdout grows under
73
- * its code cell). Gated on {@link isTranscriptBlockFinalized} so the boundary
74
- * closes the instant the block reaches a terminal state — a final result that
75
- * may collapse to a compact view, a backgrounded async tool, or a seal — and
76
- * the renderer decides whether its current preview shape qualifies via
77
- * `isStreamingPreviewAppendOnly` (typically: only the expanded full view,
78
- * which is top-anchored; the collapsed tail window re-layouts but is bounded
79
- * so it never overflows anyway).
80
- */
81
- isTranscriptBlockAppendOnly(): boolean;
82
64
  /**
83
65
  * Mark the tool terminal even though no result arrived (the turn aborted or
84
66
  * abandoned it) and stop animating, so it can freeze and stops pinning the
@@ -34,6 +34,10 @@ export type SubmittedUserInput = {
34
34
  images?: ImageContent[];
35
35
  imageLinks?: (string | undefined)[];
36
36
  customType?: string;
37
+ /** Route through `session.prompt(text, { synthetic: true })` so the text lands
38
+ * as a hidden agent-authored `developer` message rather than a visible user
39
+ * turn. Used by the `c`/`.` continue shortcut. */
40
+ synthetic?: boolean;
37
41
  display?: boolean;
38
42
  cancelled: boolean;
39
43
  started: boolean;
@@ -10,6 +10,7 @@ import type { AssistantMessage, ImageContent, Message, MessageAttribution, TextC
10
10
  export { type BranchSummaryMessage, type CompactionSummaryMessage, createBranchSummaryMessage, createCompactionSummaryMessage, } from "@oh-my-pi/pi-agent-core/compaction/messages";
11
11
  import type { OutputMeta } from "../tools/output-meta";
12
12
  export declare const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
13
+ export declare const LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE = "lsp-late-diagnostic";
13
14
  export interface SkillPromptDetails {
14
15
  name: string;
15
16
  path: string;
@@ -41,16 +42,18 @@ export declare const SILENT_ABORT_MARKER = "__omp.silent_abort__";
41
42
  * namespacing changes) propagate through every consumer in lockstep. */
42
43
  export declare function isSilentAbort(errorMessage: string | undefined): boolean;
43
44
  /** Reason threaded through `AbortController.abort(reason)` when the user aborts
44
- * the turn with Esc (see `AgentSession.abort`). The agent surfaces it verbatim
45
- * on the aborted assistant message's `errorMessage`, so the transcript reads as
46
- * a deliberate user interrupt instead of an opaque failure. */
45
+ * the turn with Esc (see `AgentSession.abort`). The agent keeps it on the
46
+ * aborted assistant message's `errorMessage` so queued follow-ups/tool-result
47
+ * placeholders can distinguish a deliberate interrupt from a bare lifecycle
48
+ * abort, but interactive renderers suppress this redundant transcript line. */
47
49
  export declare const USER_INTERRUPT_LABEL = "Interrupted by user";
50
+ export declare function isUserInterruptAbort(errorMessage: string | undefined): boolean;
51
+ export declare function shouldRenderAbortReason(errorMessage: string | undefined): boolean;
48
52
  /** Resolve the operator-facing label for an aborted assistant turn. A custom
49
- * abort reason (e.g. `USER_INTERRUPT_LABEL`) threaded onto `errorMessage` is
50
- * shown verbatim; aborts with no threaded reason fall back to the retry-aware
51
- * generic label. Centralizes the live-stream (`EventController`), replay
52
- * (`ui-helpers`), and component (`AssistantMessageComponent`) render paths so
53
- * they stay in lockstep. */
53
+ * abort reason threaded onto `errorMessage` is returned verbatim; aborts with
54
+ * no threaded reason fall back to the retry-aware generic label. Call
55
+ * `shouldRenderAbortReason` before rendering when user interrupts should stay
56
+ * visually quiet. */
54
57
  export declare function resolveAbortLabel(errorMessage: string | undefined, retryAttempt?: number): string;
55
58
  /** Extract the optional `__pendingDisplayTag` field from a CustomMessage's
56
59
  * `details` blob. Safe over `unknown`; returns undefined when the field is
@@ -7,7 +7,7 @@ export interface YieldDispatcher<P> {
7
7
  }
8
8
  export interface YieldQueueOptions {
9
9
  isStreaming: () => boolean;
10
- injectStreaming(msg: AgentMessage): void;
10
+ injectStreaming?(msg: AgentMessage): void;
11
11
  injectIdle(messages: AgentMessage[]): Promise<void>;
12
12
  scheduleIdleFlush(run: () => Promise<void>): void;
13
13
  }
@@ -19,6 +19,15 @@ export declare class YieldQueue {
19
19
  enqueue<P>(kind: string, entry: P): void;
20
20
  has(kind?: string): boolean;
21
21
  flush(mode: YieldFlushMode): Promise<void>;
22
+ /**
23
+ * Snapshot and remove all queued entries, returning one lazy thunk per kind.
24
+ * Each thunk applies the dispatcher's staleness filter and builds the batched
25
+ * message only when called — so the consumer (the agent loop) decides, at the
26
+ * moment it injects, whether the message is still worth delivering (a thunk may
27
+ * return null to skip). Background-job completions and late diagnostics reach
28
+ * the model between requests without the agent having to stop.
29
+ */
30
+ drainLazy(): Array<() => AgentMessage | null>;
22
31
  clear(): void;
23
32
  }
24
33
  export {};
@@ -46,7 +46,6 @@ export declare const evalToolRenderer: {
46
46
  }, options: RenderResultOptions & {
47
47
  renderContext?: EvalRenderContext;
48
48
  }, uiTheme: Theme, _args?: EvalRenderArgs): Component;
49
- isStreamingPreviewAppendOnly(_args: EvalRenderArgs, _options: RenderResultOptions, result?: unknown): boolean;
50
49
  mergeCallAndResult: boolean;
51
50
  inline: boolean;
52
51
  };
@@ -69,6 +69,28 @@ export type ContextFileEntry = {
69
69
  depth?: number;
70
70
  };
71
71
  export type { DiscoverableTool, DiscoverableToolSearchIndex, DiscoverableToolSearchResult, DiscoverableToolSource, } from "../tool-discovery/tool-index";
72
+ /**
73
+ * A late LSP diagnostics result that arrived after the edit/write tool already
74
+ * returned. Surfaced to the model and the transcript via
75
+ * {@link ToolSession.queueDeferredDiagnostics}, batched through the session
76
+ * yield queue like background-job results.
77
+ */
78
+ export interface DeferredDiagnosticsEntry {
79
+ /** Absolute path the diagnostics belong to (the renderer shortens it). */
80
+ path: string;
81
+ /** One-line severity summary, e.g. "2 errors". */
82
+ summary: string;
83
+ /** Formatted, ready-to-display diagnostic lines. */
84
+ messages: string[];
85
+ /** True when any message is error severity. */
86
+ errored: boolean;
87
+ /**
88
+ * Evaluated at injection time (in the dispatcher's stale check): drop the entry
89
+ * when a newer mutation to the same file has superseded it, so the model never
90
+ * sees diagnostics for stale content.
91
+ */
92
+ isStale(): boolean;
93
+ }
72
94
  /** Session context for tool factories */
73
95
  export interface ToolSession {
74
96
  /** Current working directory */
@@ -242,6 +264,15 @@ export interface ToolSession {
242
264
  diagnosticsLedger?: import("../lsp/diagnostics-ledger").DiagnosticsLedger;
243
265
  /** Queue a hidden message to be injected at the next agent turn. */
244
266
  queueDeferredMessage?(message: CustomMessage): void;
267
+ /** Queue late LSP diagnostics (arrived after an edit/write returned) to be shown
268
+ * in the transcript and delivered to the model at the next yield, like background
269
+ * job results. */
270
+ queueDeferredDiagnostics?(entry: DeferredDiagnosticsEntry): void;
271
+ /** Bump and return the session-global mutation counter for `path`. Edit/write
272
+ * tools call this on every file mutation so stale late-diagnostics can be dropped. */
273
+ bumpFileMutationVersion?(path: string): number;
274
+ /** Read the current session-global mutation counter for `path` (0 if never mutated). */
275
+ getFileMutationVersion?(path: string): number;
245
276
  /** Get the active OpenTelemetry config so subagent dispatch can forward
246
277
  * the parent's tracer/hooks with the subagent's own identity stamped. */
247
278
  getTelemetry?: () => AgentTelemetryConfig | undefined;
@@ -115,9 +115,13 @@ export interface ResolvedMultiSearchPath {
115
115
  exactFilePaths?: string[];
116
116
  targets?: ResolvedSearchTarget[];
117
117
  }
118
- export interface ResolvedMultiFindPattern {
118
+ export interface ResolvedFindTarget {
119
119
  basePath: string;
120
120
  globPattern: string;
121
+ hasGlob: boolean;
122
+ }
123
+ export interface ResolvedMultiFindPattern {
124
+ targets: ResolvedFindTarget[];
121
125
  scopePath: string;
122
126
  }
123
127
  /**
@@ -39,6 +39,8 @@ export interface ReadToolDetails {
39
39
  };
40
40
  /** Number of unresolved git conflicts surfaced by this read (TUI uses for inline `⚠ N` badge). */
41
41
  conflictCount?: number;
42
+ /** Paths recovered from a delimited read argument; used only by the TUI to render one call as multiple read rows. */
43
+ displayReadTargets?: string[];
42
44
  }
43
45
  type ReadParams = ReadToolInput;
44
46
  /**
@@ -58,7 +60,6 @@ export declare class ReadTool implements AgentTool<typeof readSchema, ReadToolDe
58
60
  readonly parameters: z.ZodObject<{
59
61
  path: z.ZodString;
60
62
  }, z.core.$strict>;
61
- readonly nonAbortable = true;
62
63
  readonly strict = true;
63
64
  constructor(session: ToolSession);
64
65
  execute(_toolCallId: string, params: ReadParams, signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<ReadToolDetails>, _toolContext?: AgentToolContext): Promise<AgentToolResult<ReadToolDetails>>;
@@ -124,7 +124,9 @@ export declare function formatDiagnostics(diag: {
124
124
  errored: boolean;
125
125
  summary: string;
126
126
  messages: string[];
127
- }, expanded: boolean, theme: Theme, getLangIcon: (filePath: string) => string): string;
127
+ }, expanded: boolean, theme: Theme, getLangIcon: (filePath: string) => string, options?: {
128
+ title?: string;
129
+ }): string;
128
130
  export interface DiffStats {
129
131
  added: number;
130
132
  removed: number;
@@ -19,21 +19,6 @@ export type ToolRenderer = {
19
19
  renderContext?: Record<string, unknown>;
20
20
  }, theme: Theme, args?: unknown) => Component;
21
21
  mergeCallAndResult?: boolean;
22
- /**
23
- * While a tool's preview is still streaming, report whether the
24
- * currently-rendered preview is append-only: its rows only grow at the bottom
25
- * and never re-layout above the bottom live region (a full, top-anchored
26
- * content/code preview). The transcript reports this up to the TUI so a
27
- * streaming preview taller than the viewport commits its scrolled-off head to
28
- * native scrollback instead of dropping it (see
29
- * `ToolExecutionComponent.isTranscriptBlockAppendOnly`). `result` is the
30
- * latest (possibly partial) tool result, or `undefined` before one exists —
31
- * `eval`/`bash` use its presence to defer committing until the streamed input
32
- * (code) has finalized. Omit (or return `false`) for previews that slide a
33
- * tail window or later collapse to a compact result — committing their head
34
- * would strand stale rows.
35
- */
36
- isStreamingPreviewAppendOnly?: (args: unknown, options: RenderResultOptions, result?: unknown) => boolean;
37
22
  /** Render without background box, inline in the response flow */
38
23
  inline?: boolean;
39
24
  };
@@ -39,7 +39,6 @@ export declare class WriteTool implements AgentTool<typeof writeSchema, WriteToo
39
39
  path: z.ZodString;
40
40
  content: z.ZodString;
41
41
  }, z.core.$strip>;
42
- readonly nonAbortable = true;
43
42
  readonly strict = true;
44
43
  readonly concurrency = "exclusive";
45
44
  readonly loadMode = "discoverable";
@@ -56,7 +55,6 @@ interface WriteRenderArgs {
56
55
  }
57
56
  export declare const writeToolRenderer: {
58
57
  renderCall(args: WriteRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
59
- isStreamingPreviewAppendOnly(args: WriteRenderArgs, options: RenderResultOptions, _result?: unknown): boolean;
60
58
  renderResult(result: {
61
59
  content: Array<{
62
60
  type: string;
@@ -18,8 +18,6 @@ export interface CodeCellOptions {
18
18
  */
19
19
  codeTail?: boolean;
20
20
  expanded?: boolean;
21
- /** Animate the cell border with a sweeping segment while pending/running. */
22
- animate?: boolean;
23
21
  width: number;
24
22
  }
25
23
  export declare function renderCodeCell(options: CodeCellOptions, theme: Theme): string[];
@@ -20,20 +20,18 @@ export declare function uriHyperlink(uri: string, displayText: string): string;
20
20
  */
21
21
  export declare function urlHyperlink(url: string, displayText: string): string;
22
22
  /**
23
- * Wrap `displayText` in an OSC 8 hyperlink pointing at the given absolute file path.
23
+ * Wrap `displayText` in an OSC 8 hyperlink pointing at a filesystem path.
24
24
  *
25
25
  * Returns `displayText` unchanged when hyperlinks are disabled or when
26
26
  * the text already contains an OSC 8 sequence (prevents double-wrapping).
27
+ * Relative paths resolve against the current working directory before URI
28
+ * encoding so the OSC 8 target is always a valid `file://` URL.
27
29
  *
28
- * The caller is responsible for passing an absolute path. Relative paths
29
- * produce invalid `file://` URIs and are accepted silently to avoid runtime
30
- * errors in renderer hot paths.
31
- *
32
- * @param absPath - Absolute filesystem path
30
+ * @param filePath - Filesystem path
33
31
  * @param displayText - Text to render as the hyperlink anchor (may contain ANSI codes)
34
32
  * @param opts - Optional line/col position appended as `?line=N&col=M` query params
35
33
  */
36
- export declare function fileHyperlink(absPath: string, displayText: string, opts?: {
34
+ export declare function fileHyperlink(filePath: string, displayText: string, opts?: {
37
35
  line?: number;
38
36
  col?: number;
39
37
  }): string;
@@ -16,8 +16,6 @@ export interface OutputBlockOptions {
16
16
  width: number;
17
17
  applyBg?: boolean;
18
18
  contentPaddingLeft?: number;
19
- /** Animate the border with a sweeping dark segment (pending/running state). */
20
- animate?: boolean;
21
19
  /** Override the state-derived border color. Used for muted "legacy" tool
22
20
  * frames that should not visually compete with framed-output tools. */
23
21
  borderColor?: ThemeColor;
@@ -28,22 +26,6 @@ export type FramedBlockComponent = Component & {
28
26
  };
29
27
  export declare function markFramedBlockComponent<T extends Component>(component: T): T & FramedBlockComponent;
30
28
  export declare function isFramedBlockComponent(component: Component): boolean;
31
- /**
32
- * Monotonic frame counter for animated borders, quantized to the TUI's ~30fps
33
- * render cap so the cache key advances once per animation frame — fine enough
34
- * for a smooth segment sweep, coarse enough to coalesce multiple render passes
35
- * land inside the same frame.
36
- */
37
- export declare function borderShimmerTick(): number;
38
- /**
39
- * Column of the travelling segment's center on the bottom edge for a box of
40
- * inner width `W` at time `now`. The segment bounces left → right → left across
41
- * the bottom border: a triangle wave over one full there-and-back cycle, eased
42
- * per leg so it slows as it nears each wall before reversing. Position is
43
- * derived from the wall clock against a fixed cycle, so a resize shifts the
44
- * center proportionally — no reset.
45
- */
46
- export declare function borderSegmentHeadCol(W: number, now: number): number;
47
29
  export declare function renderOutputBlock(options: OutputBlockOptions, theme: Theme): string[];
48
30
  /**
49
31
  * Cached wrapper around `renderOutputBlock`.