@oh-my-pi/pi-coding-agent 15.10.1 → 15.10.3

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 (154) hide show
  1. package/CHANGELOG.md +113 -1
  2. package/dist/types/cli/gallery-fixtures/types.d.ts +7 -1
  3. package/dist/types/cli/startup-cwd.d.ts +2 -0
  4. package/dist/types/commands/launch.d.ts +3 -0
  5. package/dist/types/config/keybindings.d.ts +2 -2
  6. package/dist/types/config/model-provider-priority.d.ts +1 -0
  7. package/dist/types/config/model-resolver.d.ts +4 -1
  8. package/dist/types/config/settings.d.ts +7 -2
  9. package/dist/types/debug/report-bundle.d.ts +3 -0
  10. package/dist/types/edit/file-snapshot-store.d.ts +18 -10
  11. package/dist/types/edit/index.d.ts +0 -1
  12. package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
  13. package/dist/types/extensibility/extensions/types.d.ts +4 -1
  14. package/dist/types/lsp/client.d.ts +10 -0
  15. package/dist/types/lsp/index.d.ts +0 -5
  16. package/dist/types/main.d.ts +14 -9
  17. package/dist/types/mcp/tool-bridge.d.ts +2 -0
  18. package/dist/types/modes/components/assistant-message.d.ts +0 -9
  19. package/dist/types/modes/components/custom-editor.d.ts +1 -1
  20. package/dist/types/modes/components/late-diagnostics-message.d.ts +20 -0
  21. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  22. package/dist/types/modes/components/session-selector.d.ts +16 -7
  23. package/dist/types/modes/components/status-line.d.ts +2 -0
  24. package/dist/types/modes/components/tool-execution.d.ts +0 -18
  25. package/dist/types/modes/controllers/event-controller.d.ts +17 -0
  26. package/dist/types/modes/interactive-mode.d.ts +1 -0
  27. package/dist/types/modes/magic-keywords.d.ts +1 -1
  28. package/dist/types/modes/markdown-prose.d.ts +1 -1
  29. package/dist/types/modes/types.d.ts +7 -0
  30. package/dist/types/modes/workflow.d.ts +3 -3
  31. package/dist/types/session/auth-storage.d.ts +1 -1
  32. package/dist/types/session/messages.d.ts +11 -8
  33. package/dist/types/session/session-manager.d.ts +5 -2
  34. package/dist/types/session/yield-queue.d.ts +10 -1
  35. package/dist/types/task/executor.d.ts +10 -0
  36. package/dist/types/tools/eval-render.d.ts +0 -1
  37. package/dist/types/tools/eval.d.ts +8 -0
  38. package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
  39. package/dist/types/tools/github-cache.d.ts +12 -0
  40. package/dist/types/tools/index.d.ts +31 -0
  41. package/dist/types/tools/path-utils.d.ts +13 -1
  42. package/dist/types/tools/read.d.ts +2 -1
  43. package/dist/types/tools/render-utils.d.ts +3 -1
  44. package/dist/types/tools/renderers.d.ts +0 -15
  45. package/dist/types/tools/search.d.ts +2 -2
  46. package/dist/types/tools/write.d.ts +0 -2
  47. package/dist/types/tools/yield.d.ts +8 -0
  48. package/dist/types/tui/code-cell.d.ts +0 -2
  49. package/dist/types/tui/hyperlink.d.ts +5 -7
  50. package/dist/types/tui/output-block.d.ts +0 -18
  51. package/package.json +9 -9
  52. package/src/cli/args.ts +3 -1
  53. package/src/cli/dry-balance-cli.ts +2 -4
  54. package/src/cli/gallery-cli.ts +4 -0
  55. package/src/cli/gallery-fixtures/codeintel.ts +0 -1
  56. package/src/cli/gallery-fixtures/fs.ts +68 -1
  57. package/src/cli/gallery-fixtures/types.ts +8 -1
  58. package/src/cli/startup-cwd.ts +68 -0
  59. package/src/commands/launch.ts +3 -0
  60. package/src/commit/agentic/agent.ts +1 -0
  61. package/src/commit/model-selection.ts +3 -2
  62. package/src/config/model-provider-priority.ts +55 -0
  63. package/src/config/model-registry.ts +4 -22
  64. package/src/config/model-resolver.ts +39 -7
  65. package/src/config/settings.ts +86 -41
  66. package/src/debug/index.ts +8 -0
  67. package/src/debug/raw-sse-buffer.ts +7 -4
  68. package/src/debug/report-bundle.ts +9 -0
  69. package/src/edit/file-snapshot-store.ts +33 -1
  70. package/src/edit/hashline/diff.ts +86 -0
  71. package/src/edit/hashline/execute.ts +14 -1
  72. package/src/edit/hashline/filesystem.ts +2 -1
  73. package/src/edit/index.ts +31 -17
  74. package/src/edit/renderer.ts +116 -31
  75. package/src/eval/__tests__/llm-bridge.test.ts +20 -0
  76. package/src/eval/js/context-manager.ts +32 -15
  77. package/src/eval/js/shared/prelude.txt +26 -10
  78. package/src/eval/llm-bridge.ts +14 -3
  79. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  80. package/src/eval/py/executor.ts +23 -11
  81. package/src/eval/py/prelude.py +1 -1
  82. package/src/extensibility/extensions/types.ts +10 -1
  83. package/src/internal-urls/docs-index.generated.ts +7 -7
  84. package/src/lsp/client.ts +23 -11
  85. package/src/lsp/config.ts +11 -1
  86. package/src/lsp/index.ts +189 -61
  87. package/src/main.ts +144 -78
  88. package/src/mcp/tool-bridge.ts +2 -0
  89. package/src/memories/index.ts +2 -2
  90. package/src/modes/components/assistant-message.ts +3 -15
  91. package/src/modes/components/custom-editor.ts +143 -111
  92. package/src/modes/components/late-diagnostics-message.ts +60 -0
  93. package/src/modes/components/model-selector.ts +59 -13
  94. package/src/modes/components/oauth-selector.ts +33 -7
  95. package/src/modes/components/plan-review-overlay.ts +26 -5
  96. package/src/modes/components/read-tool-group.ts +415 -35
  97. package/src/modes/components/session-selector.ts +89 -35
  98. package/src/modes/components/status-line.ts +19 -4
  99. package/src/modes/components/tips.txt +1 -1
  100. package/src/modes/components/tool-execution.ts +7 -49
  101. package/src/modes/components/transcript-container.ts +108 -32
  102. package/src/modes/components/user-message.ts +1 -1
  103. package/src/modes/controllers/event-controller.ts +32 -1
  104. package/src/modes/controllers/input-controller.ts +56 -9
  105. package/src/modes/interactive-mode.ts +107 -20
  106. package/src/modes/magic-keywords.ts +1 -1
  107. package/src/modes/markdown-prose.ts +1 -1
  108. package/src/modes/theme/shimmer.ts +20 -9
  109. package/src/modes/types.ts +7 -0
  110. package/src/modes/utils/ui-helpers.ts +26 -5
  111. package/src/modes/workflow.ts +10 -10
  112. package/src/prompts/system/manual-continue.md +7 -0
  113. package/src/prompts/system/plan-mode-active.md +56 -72
  114. package/src/prompts/system/workflow-notice.md +1 -1
  115. package/src/prompts/tools/bash.md +9 -0
  116. package/src/prompts/tools/browser.md +1 -1
  117. package/src/prompts/tools/eval.md +5 -2
  118. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  119. package/src/prompts/tools/read.md +2 -2
  120. package/src/sdk.ts +85 -10
  121. package/src/session/agent-session.ts +42 -15
  122. package/src/session/auth-storage.ts +2 -0
  123. package/src/session/messages.ts +21 -14
  124. package/src/session/session-manager.ts +98 -25
  125. package/src/session/yield-queue.ts +20 -2
  126. package/src/task/executor.ts +72 -36
  127. package/src/task/render.ts +3 -4
  128. package/src/tiny/title-client.ts +6 -1
  129. package/src/tools/bash.ts +7 -7
  130. package/src/tools/browser/tab-supervisor.ts +13 -1
  131. package/src/tools/browser/tab-worker.ts +33 -4
  132. package/src/tools/eval-render.ts +4 -23
  133. package/src/tools/eval.ts +13 -2
  134. package/src/tools/find.ts +148 -99
  135. package/src/tools/gh-cache-invalidation.ts +200 -0
  136. package/src/tools/github-cache.ts +25 -0
  137. package/src/tools/index.ts +32 -0
  138. package/src/tools/inspect-image.ts +2 -2
  139. package/src/tools/path-utils.ts +47 -24
  140. package/src/tools/plan-mode-guard.ts +52 -7
  141. package/src/tools/read.ts +41 -20
  142. package/src/tools/render-utils.ts +3 -1
  143. package/src/tools/renderers.ts +0 -15
  144. package/src/tools/search.ts +38 -3
  145. package/src/tools/ssh.ts +0 -1
  146. package/src/tools/todo.ts +1 -0
  147. package/src/tools/write.ts +5 -14
  148. package/src/tools/yield.ts +10 -1
  149. package/src/tui/code-cell.ts +1 -6
  150. package/src/tui/hyperlink.ts +13 -23
  151. package/src/tui/output-block.ts +2 -97
  152. package/src/utils/commit-message-generator.ts +2 -2
  153. package/src/utils/enhanced-paste.ts +30 -2
  154. package/src/web/search/providers/codex.ts +37 -8
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Gradient-highlight every magic keyword ("ultrathink", "orchestrate",
3
- * "workflow") that appears as standalone prose, skipping any occurrence inside a
3
+ * "workflowz") that appears as standalone prose, skipping any occurrence inside a
4
4
  * code block, inline code span, or XML/HTML section. Each highlighter paints its
5
5
  * own keyword with its own gradient, so chaining is order-independent — the
6
6
  * earlier passes only inject zero-width SGR escapes (no backticks or angle
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Markdown structure awareness for the magic-keyword affordances
3
- * ("ultrathink"/"orchestrate"/"workflow").
3
+ * ("ultrathink"/"orchestrate"/"workflowz").
4
4
  *
5
5
  * Keyword detection and editor/transcript highlighting must fire only on prose
6
6
  * the user is actually addressing to the model — never on a word that happens to
@@ -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;
@@ -159,6 +163,9 @@ export interface InteractiveModeContext {
159
163
  flushPendingModelSwitch(): Promise<void>;
160
164
  setWorkingMessage(message?: string): void;
161
165
  applyPendingWorkingMessage(): void;
166
+ /** Acknowledge a user interrupt (Esc) by switching the loader to an
167
+ * "Interrupting…" label until the agent turn unwinds. */
168
+ notifyInterrupting(): void;
162
169
  ensureLoadingAnimation(): void;
163
170
  startPendingSubmission(input: {
164
171
  text: string;
@@ -1,14 +1,14 @@
1
1
  import { type KeywordHighlighter } from "./gradient-highlight";
2
- /** Hidden system notice appended after a user message that mentions "workflow". */
2
+ /** Hidden system notice appended after a user message that mentions "workflowz". */
3
3
  export declare const WORKFLOW_NOTICE: string;
4
4
  /**
5
- * Whether `text` contains the standalone keyword "workflow"/"workflows"
5
+ * Whether `text` contains the standalone keyword "workflowz"
6
6
  * (lowercase, whitespace-delimited) in prose — never inside a code block, inline
7
7
  * code span, or XML/HTML section.
8
8
  */
9
9
  export declare function containsWorkflow(text: string): boolean;
10
10
  /**
11
- * Highlight every standalone "workflow"/"workflows" in `text` for editor display
11
+ * Highlight every standalone "workflowz" in `text` for editor display
12
12
  * with a warm amber→green gradient (hue 30..150), visually distinct from
13
13
  * ultrathink's rainbow and orchestrate's teal→violet.
14
14
  */
@@ -2,5 +2,5 @@
2
2
  * Re-exports from @oh-my-pi/pi-ai.
3
3
  * All credential storage types and the AuthStorage class now live in the ai package.
4
4
  */
5
- export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialStore, AuthStorageData, AuthStorageOptions, OAuthCredential, SerializedAuthStorage, SnapshotResponse, StoredAuthCredential, } from "@oh-my-pi/pi-ai";
5
+ export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialStore, AuthStorageData, AuthStorageOptions, CredentialOrigin, CredentialOriginKind, OAuthCredential, SerializedAuthStorage, SnapshotResponse, StoredAuthCredential, } from "@oh-my-pi/pi-ai";
6
6
  export { AuthBrokerClient, AuthStorage, DEFAULT_SNAPSHOT_CACHE_TTL_MS, REMOTE_REFRESH_SENTINEL, RemoteAuthCredentialStore, readAuthBrokerSnapshotCache, SqliteAuthCredentialStore, writeAuthBrokerSnapshotCache, } from "@oh-my-pi/pi-ai";
@@ -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
@@ -317,9 +317,11 @@ export declare class SessionManager {
317
317
  /**
318
318
  * Move the session to a new working directory.
319
319
  * Moves session files and artifacts on disk, updates all internal references,
320
- * and rewrites the session header with the new cwd.
320
+ * and rewrites the session header with the new cwd. When provided,
321
+ * `targetSessionDir` is used instead of deriving the default directory for
322
+ * the new cwd (for `--continue --session-dir` / `--resume --session-dir`).
321
323
  */
322
- moveTo(newCwd: string): Promise<void>;
324
+ moveTo(newCwd: string, targetSessionDir?: string): Promise<void>;
323
325
  isPersisted(): boolean;
324
326
  /**
325
327
  * Force-persist all current entries to disk, even when no assistant message exists yet.
@@ -407,6 +409,7 @@ export declare class SessionManager {
407
409
  /** The source that set the session name: "user" (manual /rename or RPC) or "auto" (generated title). */
408
410
  get titleSource(): "auto" | "user" | undefined;
409
411
  getSessionName(): string | undefined;
412
+ onSessionNameChanged(cb: () => void): () => void;
410
413
  /**
411
414
  * Set the session display name.
412
415
  * @param source - "user" for explicit renames (/rename command, RPC); "auto" for generated titles.
@@ -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 {};
@@ -102,6 +102,15 @@ export interface YieldItem {
102
102
  data?: unknown;
103
103
  status?: "success" | "aborted";
104
104
  error?: string;
105
+ /**
106
+ * Set by the in-tool yield validator when it exhausted its retry budget
107
+ * (MAX_SCHEMA_RETRIES) and accepted a schema-invalid payload anyway.
108
+ * `finalizeSubprocessOutput` honors this by serializing the payload and
109
+ * surfacing a stderr warning, instead of re-emitting `schema_violation`
110
+ * — which would silently swap the subagent's "accepted" view for a
111
+ * different, opaque error blob in the parent's view of the result.
112
+ */
113
+ schemaOverridden?: boolean;
105
114
  }
106
115
  interface FinalizeSubprocessOutputArgs {
107
116
  rawOutput: string;
@@ -120,6 +129,7 @@ interface FinalizeSubprocessOutputResult {
120
129
  abortedViaYield: boolean;
121
130
  hasYield: boolean;
122
131
  }
132
+ export declare const SUBAGENT_WARNING_SCHEMA_OVERRIDDEN = "SYSTEM WARNING: Subagent exhausted schema-retry budget; result was accepted despite failing the output schema.";
123
133
  export declare const SUBAGENT_WARNING_NULL_YIELD = "SYSTEM WARNING: Subagent called yield with null data.";
124
134
  export declare const SUBAGENT_WARNING_MISSING_YIELD = "SYSTEM WARNING: Subagent exited without calling yield tool after 3 reminders.";
125
135
  export declare function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): FinalizeSubprocessOutputResult;
@@ -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
  };
@@ -42,6 +42,14 @@ export type EvalProxyExecutor = (params: EvalToolParams, signal?: AbortSignal) =
42
42
  export interface EvalToolDescriptionOptions {
43
43
  py?: boolean;
44
44
  js?: boolean;
45
+ /**
46
+ * Whether `agent()` is allowed in this session. Driven by the parent's
47
+ * spawn policy (`getSessionSpawns`). Defaults to `true` for backward
48
+ * compatibility — when the session forbids spawning, the prelude doc
49
+ * omits the `agent()` entry so the model does not promise itself a
50
+ * helper that will only ever throw "spawns disabled".
51
+ */
52
+ spawns?: boolean;
45
53
  }
46
54
  export declare function getEvalToolDescription(options?: EvalToolDescriptionOptions): string;
47
55
  export interface EvalToolOptions {
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Drop `github-cache` rows for any `gh issue|pr <mutating-subcmd>` call
3
+ * embedded in `command`. Safe to invoke unconditionally; no-op when the
4
+ * command does not touch GitHub state.
5
+ */
6
+ export declare function invalidateGithubCacheForBashCommand(command: string): void;
@@ -55,6 +55,18 @@ export interface PutCachedInput<T = unknown> {
55
55
  export declare function putCached<T = unknown>(input: PutCachedInput<T>): void;
56
56
  /** Drop a specific cache entry. */
57
57
  export declare function invalidate(repo: string, kind: CacheKind, number: number, includeComments?: boolean, authKey?: string): void;
58
+ /**
59
+ * Drop every cached row for a given issue/PR number, regardless of repo,
60
+ * auth key, include_comments flag, or row kind ({@link CacheKind}). Best-effort:
61
+ * swallows DB failures the same way {@link invalidate} does.
62
+ *
63
+ * Used by the bash-side detector that reacts to `gh issue close` / `gh pr merge`
64
+ * style mutations. Repo + auth-key narrowing is intentionally skipped because
65
+ * the bash command often does not name the repo (defaults to cwd's `gh`
66
+ * config) and resolving the *current* repo from `cwd` for every bash call would
67
+ * be far more expensive than a write-amplified DELETE.
68
+ */
69
+ export declare function invalidateAllForNumber(number: number, repo?: string): void;
58
70
  /** Drop every cached row. Test helper. */
59
71
  export declare function clearAll(): void;
60
72
  /**
@@ -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
  /**
@@ -125,6 +129,14 @@ export interface ResolvedMultiFindPattern {
125
129
  * APIs accepting separate `path` and `glob` arguments.
126
130
  */
127
131
  export declare function parseSearchPath(filePath: string): ParsedSearchPath;
132
+ /**
133
+ * Async sibling of {@link parseSearchPath} that prefers literal interpretation
134
+ * when a path containing glob metacharacters resolves to an existing entry on
135
+ * disk. Disambiguates Next.js/SvelteKit routes like `apps/[id]/page.tsx` —
136
+ * without this, `[id]` is parsed as a glob character class and silently
137
+ * matches nothing.
138
+ */
139
+ export declare function parseSearchPathPreferringLiteral(filePath: string, cwd: string): Promise<ParsedSearchPath>;
128
140
  export declare function parseFindPattern(pattern: string): ParsedFindPattern;
129
141
  export declare function combineSearchGlobs(prefixGlob?: string, suffixGlob?: string): string | undefined;
130
142
  export declare function resolveExplicitSearchPaths(pathItems: string[], cwd: string, suffixGlob?: string): Promise<ResolvedMultiSearchPath | undefined>;
@@ -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
  };
@@ -11,7 +11,7 @@ declare const searchSchema: z.ZodObject<{
11
11
  paths: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
12
12
  i: z.ZodOptional<z.ZodBoolean>;
13
13
  gitignore: z.ZodOptional<z.ZodBoolean>;
14
- skip: z.ZodOptional<z.ZodNumber>;
14
+ skip: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
15
15
  }, z.core.$strict>;
16
16
  export type SearchToolInput = z.infer<typeof searchSchema>;
17
17
  export declare function toPathList(input: string | string[] | undefined): string[];
@@ -70,7 +70,7 @@ export declare class SearchTool implements AgentTool<typeof searchSchema, Search
70
70
  paths: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
71
71
  i: z.ZodOptional<z.ZodBoolean>;
72
72
  gitignore: z.ZodOptional<z.ZodBoolean>;
73
- skip: z.ZodOptional<z.ZodNumber>;
73
+ skip: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
74
74
  }, z.core.$strict>;
75
75
  readonly strict = true;
76
76
  constructor(session: ToolSession);
@@ -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;
@@ -10,6 +10,14 @@ export interface YieldDetails {
10
10
  data: unknown;
11
11
  status: "success" | "aborted";
12
12
  error?: string;
13
+ /**
14
+ * Set when the yield tool exhausted its in-tool schema-retry budget
15
+ * (MAX_SCHEMA_RETRIES) and accepted the data anyway. Surfaced so the
16
+ * executor's post-mortem finalizer can honor the override instead of
17
+ * re-rejecting the same payload with `schema_violation` — keeping the
18
+ * subagent's acceptance and the parent's view of the result in lockstep.
19
+ */
20
+ schemaOverridden?: boolean;
13
21
  }
14
22
  export declare class YieldTool implements AgentTool<TSchema, YieldDetails> {
15
23
  #private;
@@ -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`.
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.10.1",
4
+ "version": "15.10.3",
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,14 +47,14 @@
47
47
  "@agentclientprotocol/sdk": "0.22.1",
48
48
  "@babel/parser": "^7.29.7",
49
49
  "@mozilla/readability": "^0.6.0",
50
- "@oh-my-pi/hashline": "15.10.1",
51
- "@oh-my-pi/omp-stats": "15.10.1",
52
- "@oh-my-pi/pi-agent-core": "15.10.1",
53
- "@oh-my-pi/pi-ai": "15.10.1",
54
- "@oh-my-pi/pi-mnemopi": "15.10.1",
55
- "@oh-my-pi/pi-natives": "15.10.1",
56
- "@oh-my-pi/pi-tui": "15.10.1",
57
- "@oh-my-pi/pi-utils": "15.10.1",
50
+ "@oh-my-pi/hashline": "15.10.3",
51
+ "@oh-my-pi/omp-stats": "15.10.3",
52
+ "@oh-my-pi/pi-agent-core": "15.10.3",
53
+ "@oh-my-pi/pi-ai": "15.10.3",
54
+ "@oh-my-pi/pi-mnemopi": "15.10.3",
55
+ "@oh-my-pi/pi-natives": "15.10.3",
56
+ "@oh-my-pi/pi-tui": "15.10.3",
57
+ "@oh-my-pi/pi-utils": "15.10.3",
58
58
  "@opentelemetry/api": "^1.9.1",
59
59
  "@opentelemetry/context-async-hooks": "^2.7.1",
60
60
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
package/src/cli/args.ts CHANGED
@@ -109,6 +109,8 @@ export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { ty
109
109
  result.version = true;
110
110
  } else if (arg === "--allow-home") {
111
111
  result.allowHome = true;
112
+ } else if (arg === "--cwd" && i + 1 < args.length) {
113
+ result.cwd = args[++i];
112
114
  } else if (arg === "--mode" && i + 1 < args.length) {
113
115
  const mode = args[++i];
114
116
  if (mode === "text" || mode === "json" || mode === "rpc" || mode === "acp" || mode === "rpc-ui") {
@@ -258,7 +260,7 @@ export function getExtraHelpText(): string {
258
260
  NODE_EXTRA_CA_CERTS - CA bundle path (or inline PEM) for server certificate validation
259
261
  OPENAI_API_KEY - OpenAI GPT models
260
262
  GEMINI_API_KEY - Google Gemini models
261
- GITHUB_TOKEN - GitHub Copilot (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
263
+ COPILOT_GITHUB_TOKEN - GitHub Copilot
262
264
 
263
265
  ${chalk.dim("# Additional LLM Providers")}
264
266
  AZURE_OPENAI_API_KEY - Azure OpenAI models
@@ -19,7 +19,7 @@ import type { CanonicalModelVariant } from "../config/model-equivalence";
19
19
  import { type CanonicalModelQueryOptions, ModelRegistry } from "../config/model-registry";
20
20
  import {
21
21
  formatModelString,
22
- type ModelMatchPreferences,
22
+ getModelMatchPreferences,
23
23
  resolveAllowedModels,
24
24
  resolveCliModel,
25
25
  resolveModelRoleValue,
@@ -542,9 +542,7 @@ async function resolveDryBalanceModel(
542
542
  settings: Settings | undefined,
543
543
  randomSessionId: () => string,
544
544
  ): Promise<{ model: Model<Api>; warning?: string }> {
545
- const preferences: ModelMatchPreferences = {
546
- usageOrder: settings?.getStorage()?.getModelUsageOrder(),
547
- };
545
+ const preferences = getModelMatchPreferences(settings);
548
546
  if (modelSelector) {
549
547
  const resolved = resolveCliModel({
550
548
  cliModel: modelSelector,
@@ -105,6 +105,10 @@ export async function renderGalleryState(
105
105
  width: number,
106
106
  expanded = false,
107
107
  ): Promise<string[]> {
108
+ if (fixture.renderState) {
109
+ return await fixture.renderState(state, width, expanded);
110
+ }
111
+
108
112
  const tool = fakeToolFor(name, fixture);
109
113
  const streamingArgs = state === "streaming" ? (fixture.streamingArgs ?? fixture.args) : fixture.args;
110
114
  // The component only calls `requestRender` during a static render;
@@ -4,7 +4,6 @@ import type { GalleryFixture } from "./types";
4
4
  export const codeintelFixtures: Record<string, GalleryFixture> = {
5
5
  lsp: {
6
6
  label: "LSP",
7
- customRendered: true,
8
7
  streamingArgs: {
9
8
  action: "references",
10
9
  file: "src/server/auth.ts",