@oh-my-pi/pi-coding-agent 16.3.5 → 16.3.6

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 (48) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cli.js +3388 -3374
  3. package/dist/types/edit/file-snapshot-store.d.ts +4 -2
  4. package/dist/types/edit/renderer.d.ts +0 -1
  5. package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
  6. package/dist/types/extensibility/shared-events.d.ts +8 -1
  7. package/dist/types/modes/components/assistant-message.d.ts +15 -10
  8. package/dist/types/modes/components/bash-execution.d.ts +6 -0
  9. package/dist/types/modes/components/eval-execution.d.ts +6 -0
  10. package/dist/types/modes/components/tool-execution.d.ts +3 -13
  11. package/dist/types/modes/components/transcript-container.d.ts +26 -28
  12. package/dist/types/modes/utils/transcript-render-helpers.d.ts +15 -6
  13. package/dist/types/session/agent-session.d.ts +2 -0
  14. package/dist/types/tools/bash.d.ts +0 -2
  15. package/dist/types/tools/browser/tab-supervisor.d.ts +10 -0
  16. package/dist/types/tools/eval-render.d.ts +0 -2
  17. package/dist/types/tools/renderers.d.ts +0 -20
  18. package/dist/types/tools/ssh.d.ts +0 -2
  19. package/dist/types/tools/write.d.ts +1 -1
  20. package/package.json +12 -12
  21. package/src/edit/file-snapshot-store.ts +5 -2
  22. package/src/edit/renderer.ts +0 -5
  23. package/src/extensibility/custom-tools/types.ts +2 -0
  24. package/src/extensibility/shared-events.ts +9 -1
  25. package/src/modes/components/assistant-message.ts +134 -82
  26. package/src/modes/components/bash-execution.ts +9 -0
  27. package/src/modes/components/chat-transcript-builder.ts +8 -4
  28. package/src/modes/components/eval-execution.ts +9 -0
  29. package/src/modes/components/tool-execution.ts +4 -50
  30. package/src/modes/components/transcript-container.ts +82 -432
  31. package/src/modes/components/tree-selector.ts +9 -3
  32. package/src/modes/controllers/command-controller.ts +0 -3
  33. package/src/modes/controllers/event-controller.ts +74 -14
  34. package/src/modes/controllers/extension-ui-controller.ts +0 -1
  35. package/src/modes/controllers/input-controller.ts +4 -10
  36. package/src/modes/interactive-mode.ts +12 -8
  37. package/src/modes/utils/transcript-render-helpers.ts +40 -13
  38. package/src/modes/utils/ui-helpers.ts +12 -7
  39. package/src/sdk.ts +1 -0
  40. package/src/session/agent-session.ts +148 -1
  41. package/src/session/session-context.ts +7 -0
  42. package/src/tools/bash.ts +0 -4
  43. package/src/tools/browser/tab-supervisor.ts +47 -3
  44. package/src/tools/eval-render.ts +0 -20
  45. package/src/tools/read.ts +63 -20
  46. package/src/tools/renderers.ts +0 -20
  47. package/src/tools/ssh.ts +0 -16
  48. package/src/tools/write.ts +13 -6
@@ -42,9 +42,11 @@ export declare function canonicalSnapshotKey(absolutePath: string): string;
42
42
  * Producers that only displayed a slice of the file (range reads, search hits)
43
43
  * use this to mint a whole-file tag: the displayed lines stay partial, but the
44
44
  * tag fingerprints the entire file so a follow-up edit anchored at any line
45
- * validates whenever the live file is byte-identical to what was read.
45
+ * validates whenever the live file is byte-identical to what was read. Raw
46
+ * reads pass `seenLines` even though they do not emit a header, letting a prior
47
+ * or later same-content hashline tag inherit the raw range's provenance.
46
48
  */
47
- export declare function recordFileSnapshot(session: FileSnapshotStoreOwner, absolutePath: string): Promise<string | undefined>;
49
+ export declare function recordFileSnapshot(session: FileSnapshotStoreOwner, absolutePath: string, seenLines?: Iterable<number>): Promise<string | undefined>;
48
50
  /**
49
51
  * The 1-indexed file lines a hashline-formatted body actually displayed.
50
52
  * Single `NN:` rows contribute that line; a collapsed summary `NN-MM:` row
@@ -102,7 +102,6 @@ export interface EditRenderContext {
102
102
  }
103
103
  export declare const editToolRenderer: {
104
104
  mergeCallAndResult: boolean;
105
- provisionalPendingPreview: boolean;
106
105
  renderCall(args: EditRenderArgs, options: RenderResultOptions & {
107
106
  renderContext?: EditRenderContext;
108
107
  }, uiTheme: Theme): Component;
@@ -20,6 +20,7 @@ import type * as PiCodingAgent from "../../index";
20
20
  import type { Theme } from "../../modes/theme/theme";
21
21
  import type { ReadonlySessionManager } from "../../session/session-manager";
22
22
  import type { TodoItem } from "../../tools/todo";
23
+ import type { RecoveredRetryError } from "../shared-events";
23
24
  import type * as TypeBox from "../typebox";
24
25
  /** Alias for clarity */
25
26
  export type CustomToolUIContext = HookUIContext;
@@ -115,6 +116,7 @@ export type CustomToolSessionEvent = {
115
116
  success: boolean;
116
117
  attempt: number;
117
118
  finalError?: string;
119
+ recoveredErrors?: RecoveredRetryError[];
118
120
  } | {
119
121
  reason: "ttsr_triggered";
120
122
  rules: Rule[];
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
16
16
  import type { CompactionPreparation, CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
17
- import type { ImageContent, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
17
+ import type { AssistantRetryRecovery, ImageContent, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
18
18
  import type { Rule } from "../capability/rule";
19
19
  import type { Goal, GoalModeState } from "../goals/state";
20
20
  import type { BranchSummaryEntry, CompactionEntry, SessionEntry } from "../session/session-entries";
@@ -192,12 +192,19 @@ export interface AutoRetryStartEvent {
192
192
  errorMessage: string;
193
193
  errorId?: number;
194
194
  }
195
+ export interface RecoveredRetryError {
196
+ entryId: string;
197
+ persistenceKey?: string;
198
+ note: string;
199
+ retryRecovery: AssistantRetryRecovery;
200
+ }
195
201
  /** Fired when auto-retry ends */
196
202
  export interface AutoRetryEndEvent {
197
203
  type: "auto_retry_end";
198
204
  success: boolean;
199
205
  attempt: number;
200
206
  finalError?: string;
207
+ recoveredErrors?: RecoveredRetryError[];
201
208
  }
202
209
  /** Fired when TTSR rule matching interrupts generation */
203
210
  export interface TtsrTriggeredEvent {
@@ -23,6 +23,7 @@ export declare class AssistantMessageComponent extends Container {
23
23
  */
24
24
  setCacheInvalidation(info: CacheInvalidation | undefined): void;
25
25
  invalidate(): void;
26
+ render(width: number): readonly string[];
26
27
  setHideThinkingBlock(hide: boolean): void;
27
28
  setProseOnlyThinking(proseOnly: boolean): void;
28
29
  dispose(): void;
@@ -33,19 +34,23 @@ export declare class AssistantMessageComponent extends Container {
33
34
  setErrorPinned(pinned: boolean): void;
34
35
  isTranscriptBlockFinalized(): boolean;
35
36
  /**
36
- * Whether this still-live block's scrolled-off rows may be committed to
37
- * immutable native scrollback (the {@link TranscriptContainer} durable-
38
- * snapshot path). Reflowing Markdown a streaming mermaid diagram or a GFM
39
- * table re-lays-out its body as source arrives (the diagram reshapes, the
40
- * table re-aligns its columns), so committing an intermediate layout strands
41
- * a stale fragment in native scrollback that only a full repaint (Ctrl+L) can
42
- * clear. While such content is still streaming the block therefore stays
43
- * wholly in the repaintable live region and commits once, at its final
44
- * layout, when the turn finalizes.
37
+ * Settled leading rows for mid-stream native-scrollback commits (see
38
+ * `FinalizableBlock.getTranscriptBlockSettledRows`). Completed content
39
+ * blocks render in final form (non-transient) and settle in full; the
40
+ * actively streaming markdown contributes its rendered frozen-token
41
+ * prefix. The walk stops at the first child that is not declared
42
+ * byte-stable (the animated thinking pulse, extension components, images,
43
+ * error rows), and a cache-invalidation marker above the content defers
44
+ * settling entirely. Mermaid anywhere defers wholesale its ASCII
45
+ * rendering resolves asynchronously and can re-layout settled-looking
46
+ * rows. Reads only L1-cached child renders at the width recorded by this
47
+ * frame's render().
45
48
  */
46
- isTranscriptBlockCommitStable(): boolean;
49
+ getTranscriptBlockSettledRows(): number;
47
50
  getTranscriptBlockVersion(): number;
48
51
  markTranscriptBlockFinalized(): void;
52
+ applyRetryRecovery(retryRecovery: AssistantMessage["retryRecovery"]): void;
53
+ messagePersistenceKey(): string | undefined;
49
54
  setToolResultImages(toolCallId: string, images: ImageContent[]): void;
50
55
  updateContent(message: AssistantMessage, opts?: {
51
56
  transient?: boolean;
@@ -7,6 +7,12 @@ export declare class BashExecutionComponent extends Container {
7
7
  #private;
8
8
  private readonly command;
9
9
  constructor(command: string, ui: TUI, excludeFromContext?: boolean);
10
+ /**
11
+ * Transcript finalization contract (see `FinalizableBlock`): the collapsed
12
+ * streaming preview rewrites its tail window every chunk, so the block must
13
+ * stay out of native scrollback until the command completes.
14
+ */
15
+ isTranscriptBlockFinalized(): boolean;
10
16
  /**
11
17
  * Set whether the output is expanded (shows full output) or collapsed (preview only).
12
18
  */
@@ -11,6 +11,12 @@ export declare class EvalExecutionComponent extends Container {
11
11
  private readonly excludeFromContext;
12
12
  private readonly language;
13
13
  constructor(code: string, ui: TUI, excludeFromContext?: boolean, language?: EvalExecutionLanguage);
14
+ /**
15
+ * Transcript finalization contract (see `FinalizableBlock`): the collapsed
16
+ * streaming preview rewrites its tail window every chunk, so the block must
17
+ * stay out of native scrollback until the cell completes.
18
+ */
19
+ isTranscriptBlockFinalized(): boolean;
14
20
  setExpanded(expanded: boolean): void;
15
21
  invalidate(): void;
16
22
  appendOutput(chunk: string): void;
@@ -78,9 +78,9 @@ export declare class ToolExecutionComponent extends Container implements NativeS
78
78
  /**
79
79
  * Standalone harnesses may mount a tool component directly under `TUI`
80
80
  * instead of inside `TranscriptContainer`. In that shape the component must
81
- * report its own live-region seam for provisional previews, or the core
82
- * renderer treats it like shell output and commits tail-window edit/eval/bash
83
- * previews to immutable native scrollback before the result replaces them.
81
+ * report its own live-region seam while unfinalized, or the core renderer
82
+ * treats it like shell output and commits still-mutating preview rows to
83
+ * immutable native scrollback before the result replaces them.
84
84
  */
85
85
  getNativeScrollbackLiveRegionStart(): number | undefined;
86
86
  /**
@@ -92,16 +92,6 @@ export declare class ToolExecutionComponent extends Container implements NativeS
92
92
  * past, or an explicit {@link seal} flips it to `true`.
93
93
  */
94
94
  isTranscriptBlockFinalized(): boolean;
95
- /**
96
- * Whether this still-live block's settled rows may enter native scrollback
97
- * (see `FinalizableBlock.isTranscriptBlockCommitStable`). Renderers classify
98
- * pending views by durability instead of by tool name: a provisional view is
99
- * allowed to be useful on screen, but finalization may replace or re-anchor
100
- * it wholesale, so committing any of its rows would strand stale preview
101
- * bytes in immutable scrollback. Non-provisional views stream rows whose
102
- * committed prefix survives the remaining transitions.
103
- */
104
- isTranscriptBlockCommitStable(): boolean;
105
95
  /**
106
96
  * Mark the tool terminal even though no result arrived (the turn aborted or
107
97
  * abandoned it) and stop animating, so it can freeze and stops pinning the
@@ -1,18 +1,20 @@
1
1
  import { type Component, Container, type NativeScrollbackCommittedRows, type NativeScrollbackLiveRegion, type RenderStablePrefix, type ViewportTailProvider } from "@oh-my-pi/pi-tui";
2
2
  /**
3
3
  * Transcript container that renders every block's current content each frame
4
- * and reports the live-region seam (`NativeScrollbackLiveRegion`) that gates
5
- * the engine's append-only scrollback commits.
4
+ * and reports the native-scrollback exactness boundary
5
+ * (`NativeScrollbackLiveRegion`): the frame row below which every rendered
6
+ * row is final. The boundary covers the leading run of finalized blocks plus
7
+ * the first still-live block's declared settled rows
8
+ * ({@link FinalizableBlock.getTranscriptBlockSettledRows}). Rows below it
9
+ * commit to native scrollback as exact, audited content; rows above it that
10
+ * scroll off the window commit as frozen visual snapshots the engine never
11
+ * re-anchors or recommits (the tape records what was on screen).
6
12
  *
7
- * The engine never rewrites committed history: rows above the seam that have
8
- * entered the tape keep whatever bytes they were committed with ("let the
9
- * history be"), while the visible window always repaints from each block's
10
- * latest render — a late tool result, a post-finalize error pin, or an expand
11
- * toggle is always reflected on screen. Blocks that are still mutating (an
12
- * unfinalized tool, a streaming assistant message) stay below the seam so
13
- * their rows do not enter history while they can still change; a streaming
14
- * block whose render grows append-only deepens the seam through its settled
15
- * head so a long reply's scrolled-off rows still reach scrollback mid-stream.
13
+ * The engine never rewrites committed history: rows that have entered the
14
+ * tape keep whatever bytes they were committed with ("let the history be"),
15
+ * while the visible window always repaints from each block's latest render —
16
+ * a late tool result, a post-finalize error pin, or an expand toggle is
17
+ * always reflected on screen while it remains in the window.
16
18
  *
17
19
  * Assembly is incremental: the returned array is persistent and mutated in
18
20
  * place. Each block's render is still called every frame, but a block whose
@@ -29,28 +31,24 @@ export declare class TranscriptContainer extends Container implements NativeScro
29
31
  setNativeScrollbackCommittedRows(rows: number): void;
30
32
  getRenderStablePrefixRows(): number;
31
33
  getNativeScrollbackLiveRegionStart(): number | undefined;
32
- getNativeScrollbackCommitSafeEnd(): number | undefined;
33
- getNativeScrollbackSnapshotSafeEnd(): number | undefined;
34
34
  /**
35
- * Whether `component` sits below a still-mutating block i.e. inside the
36
- * live region, where its rows cannot have been committed to native
37
- * scrollback yet (commits are prefix-only and stop at the first
38
- * still-live block). Callers that retract ephemeral blocks (IRC cards)
39
- * must check this: removing a block whose rows may already be in history
40
- * is an interior deletion of the committed prefix, which the engine can
41
- * only repair by recommitting everything below it — duplication.
35
+ * Whether none of `component`'s rows (per the most recent render) have
36
+ * entered native scrollback. Callers that retract ephemeral blocks (IRC
37
+ * cards, displaceable todo/job snapshots) must check this: removing a
38
+ * block whose rows are already on the tape is an interior deletion of
39
+ * committed history the engine cannot express the block must be sealed
40
+ * in place as history instead. A component that has never rendered has no
41
+ * committed rows and is safely removable.
42
42
  */
43
- isWithinLiveRegion(component: Component): boolean;
43
+ isBlockUncommitted(component: Component): boolean;
44
44
  /**
45
45
  * Whether `component` is inside the live (repaintable) region exactly as
46
46
  * {@link render} computes it: at/after the first still-mutating block, or
47
- * the transcript tail when every block has finalized. Unlike
48
- * {@link isWithinLiveRegion} (strictly below a still-mutating block, i.e.
49
- * guaranteed-uncommitted), this also counts the trailing block that anchors
50
- * the live region. Self-animating finalized blocks (a detached task's
51
- * shimmering progress rows) poll this to stop animating — and settle on
52
- * static bytes — the moment they sit above the seam, where their rows
53
- * become commit-eligible native-scrollback history.
47
+ * the transcript tail when every block has finalized. Self-animating
48
+ * finalized blocks (a detached task's shimmering progress rows) poll this
49
+ * to stop animating and settle on static bytes — the moment they sit
50
+ * above the seam, where their rows become commit-eligible native-scrollback
51
+ * history.
54
52
  */
55
53
  isBlockInLiveRegion(component: Component): boolean;
56
54
  /**
@@ -43,12 +43,21 @@ export declare function assistantHasVisibleContent(message: AssistantAgentMessag
43
43
  * array values to an empty object.
44
44
  */
45
45
  export declare function normalizeToolArgs(args: unknown): Record<string, unknown>;
46
+ export type AssistantErrorPresentation = {
47
+ kind: "none";
48
+ } | {
49
+ kind: "full";
50
+ text: string;
51
+ isError: true;
52
+ } | {
53
+ kind: "compact-recovered";
54
+ text: string;
55
+ isError: false;
56
+ };
46
57
  /**
47
- * Resolve the inline error label, if any, for a turn-ending assistant message.
48
- * Silent aborts yield no label. `retryAttempt` tunes the abort label wording.
58
+ * Resolve the turn-ending assistant error presentation, if any.
59
+ * Silent and user-interrupt aborts yield no label. Recovered auto-retry errors
60
+ * collapse to a single non-error note; terminal errors keep the full red presentation.
49
61
  */
50
- export declare function resolveAssistantErrorMessage(message: AssistantAgentMessage, retryAttempt?: number): {
51
- hasErrorStop: boolean;
52
- errorMessage: string | null;
53
- };
62
+ export declare function resolveAssistantErrorPresentation(message: AssistantAgentMessage, retryAttempt?: number): AssistantErrorPresentation;
54
63
  export {};
@@ -33,6 +33,7 @@ import type { LoadedCustomCommand } from "../extensibility/custom-commands";
33
33
  import type { CustomTool } from "../extensibility/custom-tools/types";
34
34
  import type { ExtensionRunner } from "../extensibility/extensions";
35
35
  import type { CompactOptions, ContextUsage } from "../extensibility/extensions/types";
36
+ import type { RecoveredRetryError } from "../extensibility/shared-events";
36
37
  import type { Skill, SkillWarning } from "../extensibility/skills";
37
38
  import { type FileSlashCommand } from "../extensibility/slash-commands";
38
39
  import { GoalRuntime } from "../goals/runtime";
@@ -80,6 +81,7 @@ export type AgentSessionEvent = AgentEvent | {
80
81
  success: boolean;
81
82
  attempt: number;
82
83
  finalError?: string;
84
+ recoveredErrors?: RecoveredRetryError[];
83
85
  } | {
84
86
  type: "retry_fallback_applied";
85
87
  from: string;
@@ -154,7 +154,6 @@ export declare function createShellRenderer<TArgs>(config: ShellRendererConfig<T
154
154
  }, uiTheme: Theme, args?: TArgs): Component;
155
155
  mergeCallAndResult: boolean;
156
156
  inline: boolean;
157
- provisionalPendingPreview: string;
158
157
  };
159
158
  export declare const bashToolRenderer: {
160
159
  renderCall(args: BashRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
@@ -170,6 +169,5 @@ export declare const bashToolRenderer: {
170
169
  }, uiTheme: Theme, args?: BashRenderArgs | undefined): Component;
171
170
  mergeCallAndResult: boolean;
172
171
  inline: boolean;
173
- provisionalPendingPreview: string;
174
172
  };
175
173
  export {};
@@ -16,6 +16,16 @@ export interface PendingRun {
16
16
  session: ToolSession;
17
17
  signal?: AbortSignal;
18
18
  toolCalls: Map<string, AbortController>;
19
+ /**
20
+ * Fires when `releaseTab` closes the tab out from under an in-flight run
21
+ * (sibling `browser close --all`, session-scoped reap, etc.). Composed
22
+ * into the cmux run's signal so `wait(...)`, cmux socket calls, and the
23
+ * facade proxies unwind promptly instead of blocking to the run's
24
+ * timeout. `pending.reject` still fires first so the awaiting caller
25
+ * sees the tab-close error immediately; `closeAc` propagates the
26
+ * cancellation into the still-running `runCmuxCode` body (issue #4499).
27
+ */
28
+ closeAc?: AbortController;
19
29
  }
20
30
  interface TabSessionBase<TBrowser extends BrowserHandle = BrowserHandle> {
21
31
  name: string;
@@ -54,7 +54,5 @@ export declare const evalToolRenderer: {
54
54
  }, uiTheme: Theme, _args?: EvalRenderArgs): Component;
55
55
  mergeCallAndResult: boolean;
56
56
  inline: boolean;
57
- provisionalPendingPreview: string;
58
- provisionalPartialResult: boolean;
59
57
  };
60
58
  export {};
@@ -21,26 +21,6 @@ export type ToolRenderer = {
21
21
  mergeCallAndResult?: boolean;
22
22
  /** Render without background box, inline in the response flow */
23
23
  inline?: boolean;
24
- /**
25
- * Whether pending-call rows are provisional: useful on screen while a tool is
26
- * streaming, but not durable transcript history. `true` means every pending
27
- * shape is provisional. `"collapsed"` means only the collapsed pending shape
28
- * is provisional; expanded rendering is top-anchored/append-shaped enough to
29
- * let the transcript commit its settled prefix. Absent = the pending preview
30
- * streams rows the result render preserves.
31
- */
32
- provisionalPendingPreview?: boolean | "collapsed";
33
- /**
34
- * Whether the partial-result render is provisional: chrome rows (header
35
- * glyph, frame state) that change between `options.isPartial === true` and
36
- * the final result render. When `true`, the block is treated as
37
- * commit-unstable while a partial result is in flight, so the
38
- * stable-prefix ratchet in `deriveLiveCommitState` cannot promote the
39
- * partial chrome to native scrollback only to have the final render strand
40
- * it above the settled frame. Absent = the partial render is byte-stable
41
- * with the final render and may commit like any settled stream.
42
- */
43
- provisionalPartialResult?: boolean;
44
24
  /**
45
25
  * Whether the renderer's pending-call path visibly consumes
46
26
  * `options.spinnerFrame`. Used to avoid scheduling repaint ticks for live
@@ -68,8 +68,6 @@ export declare const sshToolRenderer: {
68
68
  renderContext?: SshRenderContext;
69
69
  }, uiTheme: Theme, args?: SshRenderArgs): Component;
70
70
  mergeCallAndResult: boolean;
71
- provisionalPendingPreview: boolean;
72
- provisionalPartialResult: boolean;
73
71
  forceFirstResultViewportRepaint: boolean;
74
72
  forceResultViewportRepaintOnSettle: boolean;
75
73
  };
@@ -49,7 +49,7 @@ export declare class WriteTool implements AgentTool<typeof writeSchema, WriteToo
49
49
  interface WriteRenderArgs {
50
50
  path?: string;
51
51
  file_path?: string;
52
- content?: string;
52
+ content?: unknown;
53
53
  }
54
54
  export declare const writeToolRenderer: {
55
55
  renderCall(args: WriteRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "16.3.5",
4
+ "version": "16.3.6",
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",
@@ -56,17 +56,17 @@
56
56
  "@agentclientprotocol/sdk": "0.25.0",
57
57
  "@babel/parser": "^7.29.7",
58
58
  "@mozilla/readability": "^0.6.0",
59
- "@oh-my-pi/hashline": "16.3.5",
60
- "@oh-my-pi/omp-stats": "16.3.5",
61
- "@oh-my-pi/pi-agent-core": "16.3.5",
62
- "@oh-my-pi/pi-ai": "16.3.5",
63
- "@oh-my-pi/pi-catalog": "16.3.5",
64
- "@oh-my-pi/pi-mnemopi": "16.3.5",
65
- "@oh-my-pi/pi-natives": "16.3.5",
66
- "@oh-my-pi/pi-tui": "16.3.5",
67
- "@oh-my-pi/pi-utils": "16.3.5",
68
- "@oh-my-pi/pi-wire": "16.3.5",
69
- "@oh-my-pi/snapcompact": "16.3.5",
59
+ "@oh-my-pi/hashline": "16.3.6",
60
+ "@oh-my-pi/omp-stats": "16.3.6",
61
+ "@oh-my-pi/pi-agent-core": "16.3.6",
62
+ "@oh-my-pi/pi-ai": "16.3.6",
63
+ "@oh-my-pi/pi-catalog": "16.3.6",
64
+ "@oh-my-pi/pi-mnemopi": "16.3.6",
65
+ "@oh-my-pi/pi-natives": "16.3.6",
66
+ "@oh-my-pi/pi-tui": "16.3.6",
67
+ "@oh-my-pi/pi-utils": "16.3.6",
68
+ "@oh-my-pi/pi-wire": "16.3.6",
69
+ "@oh-my-pi/snapcompact": "16.3.6",
70
70
  "@opentelemetry/api": "^1.9.1",
71
71
  "@opentelemetry/context-async-hooks": "^2.7.1",
72
72
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -74,17 +74,20 @@ export function canonicalSnapshotKey(absolutePath: string): string {
74
74
  * Producers that only displayed a slice of the file (range reads, search hits)
75
75
  * use this to mint a whole-file tag: the displayed lines stay partial, but the
76
76
  * tag fingerprints the entire file so a follow-up edit anchored at any line
77
- * validates whenever the live file is byte-identical to what was read.
77
+ * validates whenever the live file is byte-identical to what was read. Raw
78
+ * reads pass `seenLines` even though they do not emit a header, letting a prior
79
+ * or later same-content hashline tag inherit the raw range's provenance.
78
80
  */
79
81
  export async function recordFileSnapshot(
80
82
  session: FileSnapshotStoreOwner,
81
83
  absolutePath: string,
84
+ seenLines?: Iterable<number>,
82
85
  ): Promise<string | undefined> {
83
86
  try {
84
87
  const file = Bun.file(absolutePath);
85
88
  if (file.size > SNAPSHOT_MAX_BYTES) return undefined;
86
89
  const normalized = normalizeToLF(await file.text());
87
- return getFileSnapshotStore(session).record(canonicalSnapshotKey(absolutePath), normalized);
90
+ return getFileSnapshotStore(session).record(canonicalSnapshotKey(absolutePath), normalized, seenLines);
88
91
  } catch {
89
92
  return undefined;
90
93
  }
@@ -696,11 +696,6 @@ function wrapEditRendererLine(line: string, width: number): string[] {
696
696
 
697
697
  export const editToolRenderer = {
698
698
  mergeCallAndResult: true,
699
- // Pending preview is a TAIL window of the streamed diff ("… N more lines
700
- // above" + last rows); the result render re-anchors the block top-first, so
701
- // committing the preview's settled head would strand a stale call-box
702
- // fragment in native scrollback.
703
- provisionalPendingPreview: true,
704
699
 
705
700
  renderCall(
706
701
  args: EditRenderArgs,
@@ -26,6 +26,7 @@ import type * as PiCodingAgent from "../../index";
26
26
  import type { Theme } from "../../modes/theme/theme";
27
27
  import type { ReadonlySessionManager } from "../../session/session-manager";
28
28
  import type { TodoItem } from "../../tools/todo";
29
+ import type { RecoveredRetryError } from "../shared-events";
29
30
  import type * as TypeBox from "../typebox";
30
31
 
31
32
  /** Alias for clarity */
@@ -133,6 +134,7 @@ export type CustomToolSessionEvent =
133
134
  success: boolean;
134
135
  attempt: number;
135
136
  finalError?: string;
137
+ recoveredErrors?: RecoveredRetryError[];
136
138
  }
137
139
  | {
138
140
  reason: "ttsr_triggered";
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
16
16
  import type { CompactionPreparation, CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
17
- import type { ImageContent, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
17
+ import type { AssistantRetryRecovery, ImageContent, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
18
18
  import type { Rule } from "../capability/rule";
19
19
  import type { Goal, GoalModeState } from "../goals/state";
20
20
  import type { BranchSummaryEntry, CompactionEntry, SessionEntry } from "../session/session-entries";
@@ -241,12 +241,20 @@ export interface AutoRetryStartEvent {
241
241
  errorId?: number;
242
242
  }
243
243
 
244
+ export interface RecoveredRetryError {
245
+ entryId: string;
246
+ persistenceKey?: string;
247
+ note: string;
248
+ retryRecovery: AssistantRetryRecovery;
249
+ }
250
+
244
251
  /** Fired when auto-retry ends */
245
252
  export interface AutoRetryEndEvent {
246
253
  type: "auto_retry_end";
247
254
  success: boolean;
248
255
  attempt: number;
249
256
  finalError?: string;
257
+ recoveredErrors?: RecoveredRetryError[];
250
258
  }
251
259
 
252
260
  // ============================================================================