@oh-my-pi/pi-coding-agent 17.2.11 → 17.2.12

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 (152) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/{CHANGELOG-d9xenpn9.md → CHANGELOG-k9ghy5sn.md} +13 -0
  3. package/dist/cli.js +10795 -10791
  4. package/dist/types/advisor/runtime.d.ts +1 -1
  5. package/dist/types/config/custom-models.d.ts +31 -0
  6. package/dist/types/config/model-config-values.d.ts +19 -0
  7. package/dist/types/config/model-patch.d.ts +93 -0
  8. package/dist/types/config/model-provider-discovery.d.ts +51 -0
  9. package/dist/types/config/model-registry.d.ts +5 -52
  10. package/dist/types/config/settings.d.ts +5 -3
  11. package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
  12. package/dist/types/discovery/agent-plugin-format.d.ts +6 -13
  13. package/dist/types/discovery/contained-path.d.ts +2 -3
  14. package/dist/types/eval/executor-base.d.ts +8 -2
  15. package/dist/types/eval/kernel-session-registry.d.ts +60 -0
  16. package/dist/types/export/share.d.ts +1 -1
  17. package/dist/types/lsp/diagnostics.d.ts +96 -0
  18. package/dist/types/lsp/index.d.ts +7 -128
  19. package/dist/types/lsp/servers.d.ts +72 -0
  20. package/dist/types/lsp/tool.d.ts +42 -0
  21. package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
  22. package/dist/types/lsp/writethrough.d.ts +33 -0
  23. package/dist/types/mcp/transports/header-policy.d.ts +2 -1
  24. package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
  25. package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
  26. package/dist/types/modes/theme/color.d.ts +19 -0
  27. package/dist/types/modes/theme/loader.d.ts +19 -0
  28. package/dist/types/modes/theme/schema.d.ts +175 -0
  29. package/dist/types/modes/theme/symbols.d.ts +28 -0
  30. package/dist/types/modes/theme/theme-class.d.ts +244 -0
  31. package/dist/types/modes/theme/theme.d.ts +9 -280
  32. package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
  33. package/dist/types/registry/agent-registry.d.ts +1 -3
  34. package/dist/types/secrets/index.d.ts +3 -1
  35. package/dist/types/secrets/message-transform.d.ts +40 -0
  36. package/dist/types/secrets/obfuscator.d.ts +0 -108
  37. package/dist/types/secrets/placeholder-scan.d.ts +95 -0
  38. package/dist/types/secrets/placeholder.d.ts +94 -0
  39. package/dist/types/secrets/replacement.d.ts +82 -0
  40. package/dist/types/session/agent-session-types.d.ts +6 -0
  41. package/dist/types/session/agent-session.d.ts +1 -1
  42. package/dist/types/session/session-handoff.d.ts +3 -3
  43. package/dist/types/session/session-manager.d.ts +32 -0
  44. package/dist/types/session/session-provider-boundary.d.ts +1 -1
  45. package/dist/types/session/turn-recovery.d.ts +2 -2
  46. package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
  47. package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
  48. package/dist/types/slash-commands/builtin-control.d.ts +2 -0
  49. package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
  50. package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
  51. package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
  52. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  53. package/dist/types/slash-commands/builtin-session.d.ts +2 -0
  54. package/dist/types/tools/gh-common.d.ts +69 -0
  55. package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
  56. package/dist/types/tools/gh-pr-diff.d.ts +102 -0
  57. package/dist/types/tools/gh-run-watch.d.ts +78 -0
  58. package/dist/types/tools/gh-search.d.ts +61 -0
  59. package/dist/types/tools/gh-types.d.ts +340 -0
  60. package/dist/types/tools/gh-view.d.ts +88 -0
  61. package/dist/types/tools/gh.d.ts +4 -201
  62. package/dist/types/tools/read-archive.d.ts +16 -0
  63. package/dist/types/tools/read-format.d.ts +104 -0
  64. package/dist/types/tools/read-path-resolution.d.ts +19 -0
  65. package/dist/types/tools/read-pdf-images.d.ts +12 -0
  66. package/dist/types/tools/read-renderer.d.ts +24 -0
  67. package/dist/types/tools/read-selector.d.ts +27 -0
  68. package/dist/types/tools/read-sqlite.d.ts +16 -0
  69. package/dist/types/tools/read-summary.d.ts +19 -0
  70. package/dist/types/tools/read.d.ts +1 -23
  71. package/dist/types/tools/shell-tokenize.d.ts +2 -1
  72. package/dist/types/utils/changelog.d.ts +0 -5
  73. package/package.json +13 -13
  74. package/src/advisor/runtime.ts +12 -7
  75. package/src/config/custom-models.ts +188 -0
  76. package/src/config/model-config-values.ts +128 -0
  77. package/src/config/model-patch.ts +252 -0
  78. package/src/config/model-provider-discovery.ts +132 -0
  79. package/src/config/model-registry.ts +64 -704
  80. package/src/config/settings.ts +7 -3
  81. package/src/debug/raw-sse-buffer.ts +20 -0
  82. package/src/discovery/agent-plugin-format.ts +7 -7
  83. package/src/discovery/contained-path.ts +2 -5
  84. package/src/edit/hashline/diff.ts +5 -1
  85. package/src/eval/executor-base.ts +39 -6
  86. package/src/eval/jl/executor.ts +82 -371
  87. package/src/eval/kernel-session-registry.ts +398 -0
  88. package/src/eval/py/executor.ts +53 -261
  89. package/src/eval/rb/executor.ts +36 -279
  90. package/src/export/share.ts +2 -1
  91. package/src/lsp/diagnostics.ts +516 -0
  92. package/src/lsp/index.ts +20 -2819
  93. package/src/lsp/servers.ts +296 -0
  94. package/src/lsp/tool.ts +1352 -0
  95. package/src/lsp/workspace-diagnostics.ts +170 -0
  96. package/src/lsp/writethrough.ts +561 -0
  97. package/src/mcp/transports/header-policy.ts +1 -1
  98. package/src/modes/components/agent-hub-projection.ts +2 -2
  99. package/src/modes/components/agent-hub-renderer.ts +3 -7
  100. package/src/modes/controllers/command-controller.ts +4 -1
  101. package/src/modes/theme/color.ts +133 -0
  102. package/src/modes/theme/loader.ts +178 -0
  103. package/src/modes/theme/schema.ts +263 -0
  104. package/src/modes/theme/symbols.ts +1000 -0
  105. package/src/modes/theme/theme-class.ts +611 -0
  106. package/src/modes/theme/theme.ts +27 -2453
  107. package/src/modes/theme/tui-adapters.ts +279 -0
  108. package/src/registry/agent-registry.ts +2 -2
  109. package/src/secrets/index.ts +6 -12
  110. package/src/secrets/message-transform.ts +287 -0
  111. package/src/secrets/obfuscator.ts +39 -1303
  112. package/src/secrets/placeholder-scan.ts +506 -0
  113. package/src/secrets/placeholder.ts +309 -0
  114. package/src/secrets/replacement.ts +216 -0
  115. package/src/session/agent-session-types.ts +6 -0
  116. package/src/session/agent-session.ts +113 -7
  117. package/src/session/indexed-session-storage.ts +7 -2
  118. package/src/session/session-advisors.ts +32 -34
  119. package/src/session/session-handoff.ts +39 -20
  120. package/src/session/session-manager.ts +67 -3
  121. package/src/session/session-provider-boundary.ts +3 -6
  122. package/src/session/turn-recovery.ts +21 -12
  123. package/src/slash-commands/builtin-collaboration.ts +504 -0
  124. package/src/slash-commands/builtin-completions.ts +291 -0
  125. package/src/slash-commands/builtin-control.ts +78 -0
  126. package/src/slash-commands/builtin-lifecycle.ts +506 -0
  127. package/src/slash-commands/builtin-marketplace.ts +567 -0
  128. package/src/slash-commands/builtin-modes.ts +518 -0
  129. package/src/slash-commands/builtin-registry.ts +21 -3000
  130. package/src/slash-commands/builtin-session.ts +592 -0
  131. package/src/task/executor.ts +17 -14
  132. package/src/tools/gh-common.ts +288 -0
  133. package/src/tools/gh-pr-checkout.ts +679 -0
  134. package/src/tools/gh-pr-diff.ts +473 -0
  135. package/src/tools/gh-run-watch.ts +1015 -0
  136. package/src/tools/gh-search.ts +500 -0
  137. package/src/tools/gh-types.ts +379 -0
  138. package/src/tools/gh-view.ts +612 -0
  139. package/src/tools/gh.ts +109 -3821
  140. package/src/tools/read-archive.ts +209 -0
  141. package/src/tools/read-format.ts +593 -0
  142. package/src/tools/read-path-resolution.ts +36 -0
  143. package/src/tools/read-pdf-images.ts +250 -0
  144. package/src/tools/read-renderer.ts +289 -0
  145. package/src/tools/read-selector.ts +84 -0
  146. package/src/tools/read-sqlite.ts +215 -0
  147. package/src/tools/read-summary.ts +199 -0
  148. package/src/tools/read.ts +77 -1820
  149. package/src/tools/shell-tokenize.ts +1 -1
  150. package/src/utils/changelog.ts +1 -1
  151. package/src/utils/title-generator.ts +3 -1
  152. package/src/web/search/providers/zai.ts +12 -3
@@ -0,0 +1,104 @@
1
+ import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
+ import type { ToolSession } from "../sdk.js";
3
+ import { type LineEntry } from "../utils/block-context.js";
4
+ import { type LineRange } from "./path-utils.js";
5
+ import type { ReadToolDetails } from "./read.js";
6
+ export interface HashlineHeaderContext {
7
+ header: string;
8
+ tag: string;
9
+ fullText?: string;
10
+ }
11
+ export declare function formatReadHashlineHeader(displayPath: string, tag: string): string;
12
+ export declare function readHashlineHeaderContext(session: ToolSession, absolutePath: string, cwd: string): Promise<HashlineHeaderContext>;
13
+ export declare function hashlineHeaderContext(displayPath: string, tag: string): HashlineHeaderContext;
14
+ export declare function prependHashlineHeader(text: string, context: HashlineHeaderContext | undefined): string;
15
+ export declare function formatTextWithMode(text: string, startNum: number, shouldAddHashLines: boolean, shouldAddLineNumbers: boolean): string;
16
+ export declare const BRACKET_CONTEXT_ELLIPSIS = "\u2026";
17
+ export declare function formatLineEntriesWithMode(entries: readonly LineEntry[], shouldAddHashLines: boolean, shouldAddLineNumbers: boolean): string;
18
+ /**
19
+ * Decide whether the kept lines surrounding an elided range collapse to a
20
+ * single brace-pair line in the rendered summary. Returns true when the head
21
+ * line ends with `{` / `(` / `[` and the tail line is the matching closer
22
+ * (optionally followed by terminating punctuation like `;`, `,`, or further
23
+ * closers — e.g. `};`, `})`, `]);`).
24
+ */
25
+ export declare function canMergeBracePair(headLine: string, tailLine: string): boolean;
26
+ export declare function formatSingleLine(line: number, text: string, shouldAddHashLines: boolean, shouldAddLineNumbers: boolean): string;
27
+ export declare function formatMergedBraceLine(startLine: number, endLine: number, headText: string, tailText: string, shouldAddHashLines: boolean, shouldAddLineNumbers: boolean): {
28
+ model: string;
29
+ display: string;
30
+ };
31
+ export declare function countTextLines(text: string): number;
32
+ export declare function contiguousLineNumbers(startLine: number, count: number): number[];
33
+ export declare function lineNumbersFromSpans(spans: readonly {
34
+ startLine: number;
35
+ endLine: number;
36
+ }[]): number[];
37
+ /** Inclusive line range describing one elided span in a structural summary. */
38
+ export interface ElidedRange {
39
+ start: number;
40
+ end: number;
41
+ }
42
+ /**
43
+ * Footer appended to summarized reads telling the model how to recover the
44
+ * elided body. Without this hint, agents either ignore the `…`/`{ … }`
45
+ * markers or burn a turn guessing the right selector (see issue #1046). The
46
+ * footer demonstrates the multi-range selector syntax with concrete sample
47
+ * ranges drawn from the actual elision so the model re-reads only what it
48
+ * needs instead of falling back to `:raw` or whole-file reads.
49
+ */
50
+ export declare function formatSummaryElisionFooter(readPath: string, elidedRanges: ReadonlyArray<ElidedRange>, elidedLines: number): string;
51
+ export declare const READ_CHUNK_SIZE: number;
52
+ /**
53
+ * Context lines added around an explicit range read. Anchor-stale failures
54
+ * cluster on edits whose anchors land just outside the most recent read
55
+ * window, but the data (`scripts/session-stats/analyze_selector_reads.py`)
56
+ * shows most follow-up reads are disjoint hops, not adjacent extensions —
57
+ * so symmetric padding rarely pays for itself.
58
+ *
59
+ * Leading=1 catches accidental single-line reads where the anchor is the
60
+ * line immediately above the requested start. Trailing=3 buffers the
61
+ * common case where the agent asks for a narrow range and then needs the
62
+ * next few lines to disambiguate an anchor.
63
+ */
64
+ export declare const RANGE_LEADING_CONTEXT_LINES = 1;
65
+ export declare const RANGE_TRAILING_CONTEXT_LINES = 3;
66
+ export declare function buildInMemoryTextResult(session: ToolSession, text: string, offset: number | undefined, limit: number | undefined, options: {
67
+ details?: ReadToolDetails;
68
+ sourcePath?: string;
69
+ sourceUrl?: string;
70
+ sourceInternal?: string;
71
+ entityLabel: string;
72
+ ignoreResultLimits?: boolean;
73
+ raw?: boolean;
74
+ immutable?: boolean;
75
+ }): AgentToolResult<ReadToolDetails>;
76
+ /**
77
+ * Render a multi-range read against in-memory text. Each range emits a
78
+ * formatted block with its own anchors / line numbers, blocks are joined
79
+ * with an elision separator, and ranges past EOF surface as `[…]` notices
80
+ * so the model can correct the next call. No leading/trailing context is
81
+ * added — multi-range callers always specify exact bounds.
82
+ */
83
+ export declare function buildInMemoryMultiRangeResult(session: ToolSession, text: string, ranges: readonly LineRange[], options: {
84
+ details?: ReadToolDetails;
85
+ sourcePath?: string;
86
+ sourceUrl?: string;
87
+ sourceInternal?: string;
88
+ entityLabel: string;
89
+ raw?: boolean;
90
+ immutable?: boolean;
91
+ }): AgentToolResult<ReadToolDetails>;
92
+ export declare function decodeUtf8Text(bytes: Uint8Array): string | null;
93
+ export declare function prependSuffixResolutionNotice(text: string, suffixResolution?: {
94
+ from: string;
95
+ to: string;
96
+ }): string;
97
+ /**
98
+ * Tag Markdown reads for the TUI's formatted preview, gated on the opt-in
99
+ * `read.renderMarkdown` setting. Off by default; when disabled, no local
100
+ * read is tagged `text/markdown`, so the renderer output is identical to
101
+ * the pre-setting behavior. Internal-URL reads keep their protocol-supplied
102
+ * `contentType` and render as Markdown regardless of the setting.
103
+ */
104
+ export declare function markMarkdownContentType(session: ToolSession, details: ReadToolDetails, filePath: string): ReadToolDetails;
@@ -0,0 +1,19 @@
1
+ import type { ToolSession } from "../sdk.js";
2
+ export declare function isRemoteMountPath(absolutePath: string): boolean;
3
+ export declare function isNotFoundError(error: unknown): boolean;
4
+ /** Per-execute memo of suffix-glob lookups; `null` records a confirmed miss. */
5
+ export type SuffixMatchCache = Map<string, {
6
+ absolutePath: string;
7
+ displayPath: string;
8
+ } | null>;
9
+ /**
10
+ * Memoized {@link findUniqueWorkspaceSuffix} for a single read call. A missing
11
+ * path with archive/sqlite extensions probes the workspace once per stage
12
+ * (archive candidates, sqlite candidates, plain path) — each glob carries a
13
+ * 5s timeout, so repeated lookups of the same string stack into a long
14
+ * stall before erroring. The cache collapses repeats within one execute().
15
+ */
16
+ export declare function findSuffixMatchCached(session: ToolSession, cache: SuffixMatchCache, rawPath: string, signal?: AbortSignal): Promise<{
17
+ absolutePath: string;
18
+ displayPath: string;
19
+ } | null>;
@@ -0,0 +1,12 @@
1
+ import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
+ import type { ToolSession } from "../sdk.js";
3
+ import type { ReadToolDetails } from "./read.js";
4
+ export declare function rewritePdfImagePlaceholders(markdown: string, pdfPath: string): string;
5
+ export declare function splitPdfImageMemberReadPath(readPath: string): {
6
+ pdfPath: string;
7
+ member: string;
8
+ } | null;
9
+ export declare function readPdfImageMember(session: ToolSession, autoResizeImages: boolean, absolutePdfPath: string, pdfDisplayPath: string, member: string, suffixResolution: {
10
+ from: string;
11
+ to: string;
12
+ } | undefined, signal?: AbortSignal): Promise<AgentToolResult<ReadToolDetails>>;
@@ -0,0 +1,24 @@
1
+ import type { Component } from "@oh-my-pi/pi-tui";
2
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types.js";
3
+ import { type Theme } from "../modes/theme/theme.js";
4
+ import type { ReadToolDetails } from "./read.js";
5
+ interface ReadRenderArgs {
6
+ path?: unknown;
7
+ file_path?: unknown;
8
+ offset?: number;
9
+ limit?: number;
10
+ raw?: boolean;
11
+ }
12
+ export declare const readToolRenderer: {
13
+ renderCall(args: ReadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
14
+ renderResult(result: {
15
+ content: Array<{
16
+ type: string;
17
+ text?: string;
18
+ }>;
19
+ details?: ReadToolDetails;
20
+ isError?: boolean;
21
+ }, options: RenderResultOptions, uiTheme: Theme, args?: ReadRenderArgs): Component;
22
+ mergeCallAndResult: boolean;
23
+ };
24
+ export {};
@@ -0,0 +1,27 @@
1
+ import type { LineRange } from "./path-utils.js";
2
+ /** Parsed representation of a path-embedded selector. */
3
+ export type ParsedSelector = {
4
+ kind: "none";
5
+ } | {
6
+ kind: "raw";
7
+ } | {
8
+ kind: "conflicts";
9
+ } | {
10
+ kind: "lines";
11
+ ranges: [LineRange, ...LineRange[]];
12
+ raw?: boolean;
13
+ };
14
+ /** Returns true when the selector requested verbatim/raw output (alone or combined with a range). */
15
+ export declare function isRawSelector(parsed: ParsedSelector): boolean;
16
+ /** Returns true when the selector requested multiple line ranges. */
17
+ export declare function isMultiRange(parsed: ParsedSelector): boolean;
18
+ export declare function parseSel(sel: string | undefined): ParsedSelector;
19
+ /**
20
+ * Convert a single-range selector to the offset/limit pair used by internal pagination.
21
+ * Returns the FIRST range only — multi-range callers MUST branch on `isMultiRange` before
22
+ * calling this helper.
23
+ */
24
+ export declare function selToOffsetLimit(parsed: ParsedSelector): {
25
+ offset?: number;
26
+ limit?: number;
27
+ };
@@ -0,0 +1,16 @@
1
+ import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
+ import type { ToolSession } from "../sdk.js";
3
+ import type { ReadToolDetails } from "./read.js";
4
+ import { type SuffixMatchCache } from "./read-path-resolution.js";
5
+ interface ResolvedSqliteReadPath {
6
+ absolutePath: string;
7
+ sqliteSubPath: string;
8
+ queryString: string;
9
+ suffixResolution?: {
10
+ from: string;
11
+ to: string;
12
+ };
13
+ }
14
+ export declare function resolveSqliteReadPath(session: ToolSession, readPath: string, suffixCache: SuffixMatchCache, signal?: AbortSignal): Promise<ResolvedSqliteReadPath | null>;
15
+ export declare function readSqlite(resolvedSqlitePath: ResolvedSqliteReadPath, signal?: AbortSignal): Promise<AgentToolResult<ReadToolDetails>>;
16
+ export {};
@@ -0,0 +1,19 @@
1
+ import { type SummaryResult } from "@oh-my-pi/pi-natives";
2
+ import type { ToolSession } from "../sdk.js";
3
+ import { type ElidedRange } from "./read-format.js";
4
+ /**
5
+ * Prose files (Markdown flavors and plain text) skip code-block summarization
6
+ * unless `read.summarize.prose` opts them in.
7
+ */
8
+ export declare function isProseSummaryPath(filePath: string): boolean;
9
+ export declare function routeReadThroughBridge(session: ToolSession, absolutePath: string, options?: {
10
+ line?: number;
11
+ limit?: number;
12
+ }): Promise<string> | undefined;
13
+ export declare function trySummarize(session: ToolSession, absolutePath: string, fileSize: number, signal?: AbortSignal): Promise<SummaryResult | null>;
14
+ export declare function renderSummary(session: ToolSession, summary: SummaryResult): {
15
+ text: string;
16
+ displayText: string;
17
+ elidedRanges: ElidedRange[];
18
+ elidedLines: number;
19
+ };
@@ -1,10 +1,8 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback, ToolTier } from "@oh-my-pi/pi-agent-core";
2
- import type { Component } from "@oh-my-pi/pi-tui";
3
- import type { RenderResultOptions } from "../extensibility/custom-tools/types.js";
4
- import { type Theme } from "../modes/theme/theme.js";
5
2
  import type { ToolSession } from "../sdk.js";
6
3
  import { type TruncationResult } from "../session/streaming-output.js";
7
4
  import { type OutputMeta } from "./output-meta.js";
5
+ export { readToolRenderer } from "./read-renderer.js";
8
6
  declare const readSchema: import("@oh-my-pi/omptype").FluentType<{
9
7
  path: string;
10
8
  }, {
@@ -76,23 +74,3 @@ export declare class ReadTool implements AgentTool<typeof readSchema, ReadToolDe
76
74
  syncInspectImageState(availableOverride?: boolean): boolean;
77
75
  execute(_toolCallId: string, params: ReadParams, signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<ReadToolDetails>, _toolContext?: AgentToolContext): Promise<AgentToolResult<ReadToolDetails>>;
78
76
  }
79
- interface ReadRenderArgs {
80
- path?: unknown;
81
- file_path?: unknown;
82
- offset?: number;
83
- limit?: number;
84
- raw?: boolean;
85
- }
86
- export declare const readToolRenderer: {
87
- renderCall(args: ReadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
88
- renderResult(result: {
89
- content: Array<{
90
- type: string;
91
- text?: string;
92
- }>;
93
- details?: ReadToolDetails;
94
- isError?: boolean;
95
- }, options: RenderResultOptions, uiTheme: Theme, args?: ReadRenderArgs): Component;
96
- mergeCallAndResult: boolean;
97
- };
98
- export {};
@@ -17,7 +17,7 @@ export declare function tokenizeShellSegments(command: string): string[][];
17
17
  *
18
18
  * @see extractFlatShellCommandSegments
19
19
  */
20
- export interface FlatShellCommandSegment {
20
+ interface FlatShellCommandSegment {
21
21
  /** Original segment text with quoting and escaping preserved. */
22
22
  text: string;
23
23
  /**
@@ -57,3 +57,4 @@ export declare function extractLeadingCdTarget(command: string): {
57
57
  path: string;
58
58
  rest: string;
59
59
  } | null;
60
+ export {};
@@ -38,11 +38,6 @@ export declare function formatStartupChangelogSummary(selection: StartupChangelo
38
38
  */
39
39
  export declare function resolveBundledChangelogPath(assetPath: string, moduleUrl: string | URL): string | URL;
40
40
  export declare function parseChangelog(changelogPath: string | undefined): Promise<ChangelogEntry[]>;
41
- /**
42
- * Compare changelog entries by their parsed version parts.
43
- * Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
44
- */
45
- export declare function compareChangelogEntries(v1: ChangelogEntry, v2: ChangelogEntry): number;
46
41
  /**
47
42
  * Parse an omp changelog marker version into comparable parts.
48
43
  */
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": "17.2.11",
4
+ "version": "17.2.12",
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",
@@ -50,18 +50,18 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@babel/parser": "^7.29.7",
53
- "@oh-my-pi/hashline": "17.2.11",
54
- "@oh-my-pi/omp-stats": "17.2.11",
55
- "@oh-my-pi/omptype": "17.2.11",
56
- "@oh-my-pi/pi-agent-core": "17.2.11",
57
- "@oh-my-pi/pi-ai": "17.2.11",
58
- "@oh-my-pi/pi-catalog": "17.2.11",
59
- "@oh-my-pi/pi-mnemopi": "17.2.11",
60
- "@oh-my-pi/pi-natives": "17.2.11",
61
- "@oh-my-pi/pi-tui": "17.2.11",
62
- "@oh-my-pi/pi-utils": "17.2.11",
63
- "@oh-my-pi/pi-wire": "17.2.11",
64
- "@oh-my-pi/snapcompact": "17.2.11",
53
+ "@oh-my-pi/hashline": "17.2.12",
54
+ "@oh-my-pi/omp-stats": "17.2.12",
55
+ "@oh-my-pi/omptype": "17.2.12",
56
+ "@oh-my-pi/pi-agent-core": "17.2.12",
57
+ "@oh-my-pi/pi-ai": "17.2.12",
58
+ "@oh-my-pi/pi-catalog": "17.2.12",
59
+ "@oh-my-pi/pi-mnemopi": "17.2.12",
60
+ "@oh-my-pi/pi-natives": "17.2.12",
61
+ "@oh-my-pi/pi-tui": "17.2.12",
62
+ "@oh-my-pi/pi-utils": "17.2.12",
63
+ "@oh-my-pi/pi-wire": "17.2.12",
64
+ "@oh-my-pi/snapcompact": "17.2.12",
65
65
  "@opentelemetry/api": "^1.9.1",
66
66
  "@opentelemetry/api-logs": "^0.220.0",
67
67
  "@opentelemetry/context-async-hooks": "^2.9.0",
@@ -5,7 +5,8 @@ import * as AIError from "@oh-my-pi/pi-ai/error";
5
5
  import { raceWithSignal } from "@oh-my-pi/pi-ai/utils/abort";
6
6
  import { type CursorExecResolvedCarrier, kCursorExecResolved } from "@oh-my-pi/pi-ai/utils/block-symbols";
7
7
  import { logger } from "@oh-my-pi/pi-utils";
8
- import { obfuscateToolArguments, type SecretObfuscator } from "../secrets/obfuscator";
8
+ import { obfuscateToolArguments } from "../secrets/message-transform";
9
+ import type { SecretObfuscator } from "../secrets/obfuscator";
9
10
  import {
10
11
  formatExecutionSourcePreview,
11
12
  formatSessionHistoryMarkdown,
@@ -970,14 +971,16 @@ export class AdvisorRuntime {
970
971
  this.#wakeAllWaiters();
971
972
  const failedMessages = this.agent.state.messages.slice(messageSnapshot);
972
973
  const terminalFailure = this.#terminalAssistantFailure(messageSnapshot);
973
- const classifierRefusal =
974
- (terminalFailure !== undefined && isClassifierRefusal(terminalFailure)) ||
975
- AIError.is(AIError.classify(err), AIError.Flag.ContentBlocked);
974
+ const rawErrorId = AIError.classify(err);
976
975
  const terminalFailureId =
977
976
  terminalFailure === undefined ? undefined : AIError.classifyMessage(terminalFailure);
977
+ const classifierRefusal =
978
+ (terminalFailure !== undefined && isClassifierRefusal(terminalFailure)) ||
979
+ (!AIError.is(rawErrorId, AIError.Flag.AccountPolicy) &&
980
+ AIError.is(rawErrorId, AIError.Flag.ContentBlocked));
978
981
  const contextOverflow =
979
982
  (terminalFailureId !== undefined && AIError.is(terminalFailureId, AIError.Flag.ContextOverflow)) ||
980
- AIError.is(AIError.classify(err), AIError.Flag.ContextOverflow);
983
+ AIError.is(rawErrorId, AIError.Flag.ContextOverflow);
981
984
  // A terminal provider failure that is neither retriable nor an
982
985
  // overflow (e.g. a blocked prompt) will fail identically on every
983
986
  // retry — classify it before rollback so the batch is dropped after
@@ -1208,12 +1211,14 @@ export class AdvisorRuntime {
1208
1211
  }
1209
1212
  }
1210
1213
 
1211
- /** Mirrors turn recovery's refusal classification and retains AIError's provider-neutral content-block fallback. */
1214
+ /** Mirrors turn recovery's refusal classification without treating account eligibility as a model refusal. */
1212
1215
  function isClassifierRefusal(message: AssistantMessage): boolean {
1213
1216
  if (message.stopReason !== "error") return false;
1217
+ const id = AIError.classifyMessage(message);
1218
+ if (AIError.is(id, AIError.Flag.AccountPolicy)) return false;
1214
1219
  const stopType = message.stopDetails?.type;
1215
1220
  if (stopType === "refusal" || stopType === "sensitive") return true;
1216
- return AIError.is(AIError.classifyMessage(message), AIError.Flag.ContentBlocked);
1221
+ return AIError.is(id, AIError.Flag.ContentBlocked);
1217
1222
  }
1218
1223
 
1219
1224
  /**
@@ -0,0 +1,188 @@
1
+ import type { Api, Model, ModelSpec, RemoteCompactionConfig } from "@oh-my-pi/pi-ai/types";
2
+ import { buildModel } from "@oh-my-pi/pi-catalog/build";
3
+ import {
4
+ getBundledModelReferenceIndex,
5
+ inheritReferenceThinking,
6
+ resolveModelReference,
7
+ } from "@oh-my-pi/pi-catalog/identity";
8
+ import { getVariantAliasSources, resolveVariantAlias } from "@oh-my-pi/pi-catalog/variant-collapse";
9
+ import { logger } from "@oh-my-pi/pi-utils";
10
+ import { createLiveConfigHeaders, type HeaderSource } from "./model-config-values";
11
+ import { type ModelPatch, mergeCompat, mergeRemoteCompactionConfig } from "./model-patch";
12
+ import { parseModelString } from "./model-resolver";
13
+ import type { ModelOverride, ProviderAuthMode } from "./models-config-schema";
14
+ export interface CustomModelDefinitionLike extends ModelPatch {
15
+ id: string;
16
+ api?: Api;
17
+ baseUrl?: string;
18
+ cost?: Model<Api>["cost"];
19
+ }
20
+
21
+ export interface CustomModelBuildOptions {
22
+ useDefaults: boolean;
23
+ }
24
+
25
+ export interface CustomModelOverlay extends ModelPatch {
26
+ id: string;
27
+ provider: string;
28
+ api: Api;
29
+ baseUrl: string;
30
+ cost?: Model<Api>["cost"];
31
+ isOAuth?: boolean;
32
+ }
33
+
34
+ function mergeCustomModelHeaders(
35
+ providerHeaders: Record<string, string> | undefined,
36
+ modelHeaders: Record<string, string> | undefined,
37
+ authHeader: boolean | undefined,
38
+ apiKeyConfig: string | undefined,
39
+ ): Record<string, string> | undefined {
40
+ return createLiveConfigHeaders([providerHeaders, modelHeaders], { authHeader, apiKeyConfig });
41
+ }
42
+
43
+ export function mergeAuthHeaderSources(
44
+ sources: readonly HeaderSource[],
45
+ authHeader: boolean | undefined,
46
+ apiKeyConfig: string | undefined,
47
+ ): Record<string, string> | undefined {
48
+ return createLiveConfigHeaders(sources, { authHeader, apiKeyConfig });
49
+ }
50
+
51
+ /**
52
+ * Decide whether a custom-yaml model should force OAuth-style request shaping.
53
+ * - Explicit `auth: oauth` → force on.
54
+ * endpoints are typically Claude-Code-style proxies (e.g. CLIProxyAPI) that expect
55
+ * the cloaked request shape regardless of how the proxy itself is authenticated.
56
+ * - Otherwise → unset.
57
+ */
58
+ function resolveCustomModelIsOAuth(api: Api, providerAuth: ProviderAuthMode | undefined): boolean | undefined {
59
+ if (providerAuth === "oauth") return true;
60
+ if (providerAuth !== undefined) return undefined;
61
+ if (api === "anthropic-messages") return true;
62
+ return undefined;
63
+ }
64
+
65
+ export function buildCustomModelOverlay(
66
+ providerName: string,
67
+ providerBaseUrl: string,
68
+ providerApi: Api | undefined,
69
+ providerHeaders: Record<string, string> | undefined,
70
+ providerApiKey: string | undefined,
71
+ authHeader: boolean | undefined,
72
+ providerCompat: ModelSpec<Api>["compat"] | undefined,
73
+ providerAuth: ProviderAuthMode | undefined,
74
+ providerRemoteCompaction: RemoteCompactionConfig<Api> | undefined,
75
+ modelDef: CustomModelDefinitionLike,
76
+ ): CustomModelOverlay | undefined {
77
+ const api = modelDef.api ?? providerApi;
78
+ if (!api) return undefined;
79
+ return {
80
+ id: modelDef.id,
81
+ provider: providerName,
82
+ api,
83
+ baseUrl: modelDef.baseUrl ?? providerBaseUrl,
84
+ name: modelDef.name,
85
+ reasoning: modelDef.reasoning,
86
+ thinking: modelDef.thinking,
87
+ input: modelDef.input,
88
+ supportsTools: modelDef.supportsTools,
89
+ cost: modelDef.cost,
90
+ contextWindow: modelDef.contextWindow,
91
+ maxTokens: modelDef.maxTokens,
92
+ omitMaxOutputTokens: modelDef.omitMaxOutputTokens,
93
+ headers: mergeCustomModelHeaders(providerHeaders, modelDef.headers, authHeader, providerApiKey),
94
+ compat: mergeCompat(providerCompat, modelDef.compat),
95
+ contextPromotionTarget: modelDef.contextPromotionTarget,
96
+ compactionModel: modelDef.compactionModel,
97
+ remoteCompaction: mergeRemoteCompactionConfig(providerRemoteCompaction, modelDef.remoteCompaction),
98
+ premiumMultiplier: modelDef.premiumMultiplier,
99
+ isOAuth: resolveCustomModelIsOAuth(api, providerAuth),
100
+ };
101
+ }
102
+
103
+ function applyStandaloneCustomModelPolicies(model: CustomModelOverlay): CustomModelOverlay {
104
+ if (model.id !== "gpt-5.4" || model.provider === "github-copilot" || model.contextWindow !== undefined) {
105
+ return model;
106
+ }
107
+ return { ...model, contextWindow: 1_000_000 };
108
+ }
109
+
110
+ export function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuildOptions): Model<Api> {
111
+ const resolvedModel = options.useDefaults ? applyStandaloneCustomModelPolicies(model) : model;
112
+ const reference = options.useDefaults
113
+ ? resolveModelReference(resolvedModel.id, getBundledModelReferenceIndex())
114
+ : undefined;
115
+ const cost =
116
+ resolvedModel.cost ??
117
+ reference?.cost ??
118
+ (options.useDefaults ? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } : undefined);
119
+ const input = resolvedModel.input ?? reference?.input ?? (options.useDefaults ? ["text"] : undefined);
120
+ const supportsTools = resolvedModel.supportsTools ?? reference?.supportsTools;
121
+ return buildModel({
122
+ id: resolvedModel.id,
123
+ name: resolvedModel.name ?? (options.useDefaults ? resolvedModel.id : undefined),
124
+ api: resolvedModel.api,
125
+ provider: resolvedModel.provider,
126
+ baseUrl: resolvedModel.baseUrl,
127
+ reasoning: resolvedModel.reasoning ?? reference?.reasoning ?? (options.useDefaults ? false : undefined),
128
+ thinking: inheritReferenceThinking(resolvedModel.thinking, reference, resolvedModel.provider),
129
+ input: input as ("text" | "image")[],
130
+ ...(supportsTools !== undefined ? { supportsTools } : {}),
131
+ cost,
132
+ contextWindow: resolvedModel.contextWindow ?? reference?.contextWindow ?? (options.useDefaults ? 128000 : null),
133
+ maxTokens: resolvedModel.maxTokens ?? reference?.maxTokens ?? (options.useDefaults ? 16384 : null),
134
+ headers: resolvedModel.headers,
135
+ omitMaxOutputTokens: resolvedModel.omitMaxOutputTokens ?? reference?.omitMaxOutputTokens,
136
+ compat: mergeCompat(reference?.compatConfig, resolvedModel.compat),
137
+ contextPromotionTarget: resolvedModel.contextPromotionTarget,
138
+ compactionModel: resolvedModel.compactionModel,
139
+ remoteCompaction: resolvedModel.remoteCompaction,
140
+ premiumMultiplier: resolvedModel.premiumMultiplier,
141
+ isOAuth: resolvedModel.isOAuth,
142
+ } as ModelSpec<Api>);
143
+ }
144
+
145
+ export function normalizeSuppressedSelector(
146
+ selector: string,
147
+ hasLiveModel?: (provider: string, id: string) => boolean,
148
+ ): string {
149
+ const trimmed = selector.trim();
150
+ if (!trimmed) return trimmed;
151
+ const parsed = parseModelString(trimmed, {
152
+ allowMaxSuffix: true,
153
+ allowAutoAlias: true,
154
+ isLiteralModelId: (provider, id) => hasLiveModel?.(provider, id) === true,
155
+ });
156
+ if (!parsed) return trimmed;
157
+ // Retired effort-tier variant ids normalize to their collapsed logical id
158
+ // so persisted suppressions keyed by raw member ids still bind.
159
+ const aliasId = resolveVariantAlias(parsed.provider, parsed.id);
160
+ return `${parsed.provider}/${aliasId ?? parsed.id}`;
161
+ }
162
+
163
+ /**
164
+ * Look up a model's override, falling back to entries keyed by retired
165
+ * effort-tier variant ids (models.yml authored before collapsing). A raw key
166
+ * only re-binds when no live model holds that id.
167
+ */
168
+ export function resolveModelOverrideWithAliases(
169
+ overrides: Map<string, ModelOverride>,
170
+ model: Model<Api>,
171
+ hasLiveModel: (provider: string, id: string) => boolean,
172
+ ): ModelOverride | undefined {
173
+ const direct = overrides.get(model.id);
174
+ if (direct) return direct;
175
+ for (const rawId of getVariantAliasSources(model.provider, model.id)) {
176
+ if (hasLiveModel(model.provider, rawId)) continue;
177
+ const remapped = overrides.get(rawId);
178
+ if (remapped) {
179
+ logger.debug("model override re-keyed through variant alias", {
180
+ provider: model.provider,
181
+ from: rawId,
182
+ to: model.id,
183
+ });
184
+ return remapped;
185
+ }
186
+ }
187
+ return undefined;
188
+ }