@oh-my-pi/pi-coding-agent 15.5.7 → 15.5.9

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 (63) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/dist/types/cli/auth-gateway-cli.d.ts +8 -0
  3. package/dist/types/commands/auth-gateway.d.ts +3 -0
  4. package/dist/types/config/settings-schema.d.ts +10 -10
  5. package/dist/types/edit/file-snapshot-store.d.ts +9 -6
  6. package/dist/types/edit/hashline/diff.d.ts +4 -5
  7. package/dist/types/edit/streaming.d.ts +2 -1
  8. package/dist/types/eval/py/index.d.ts +1 -0
  9. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  10. package/dist/types/extensibility/shared-events.d.ts +1 -1
  11. package/dist/types/internal-urls/index.d.ts +1 -0
  12. package/dist/types/internal-urls/vault-protocol.d.ts +93 -0
  13. package/dist/types/mcp/transports/http.d.ts +9 -0
  14. package/dist/types/modes/components/tool-execution.d.ts +2 -1
  15. package/dist/types/session/agent-session.d.ts +3 -1
  16. package/dist/types/tools/match-line-format.d.ts +2 -2
  17. package/dist/types/tools/render-utils.d.ts +3 -1
  18. package/dist/types/tools/write.d.ts +2 -0
  19. package/dist/types/utils/file-mentions.d.ts +2 -0
  20. package/package.json +8 -8
  21. package/src/cli/args.ts +2 -0
  22. package/src/cli/auth-broker-cli.ts +2 -1
  23. package/src/cli/auth-gateway-cli.ts +210 -9
  24. package/src/commands/auth-gateway.ts +7 -1
  25. package/src/config/settings-schema.ts +12 -11
  26. package/src/edit/file-snapshot-store.ts +9 -6
  27. package/src/edit/hashline/diff.ts +26 -13
  28. package/src/edit/hashline/execute.ts +13 -9
  29. package/src/edit/renderer.ts +9 -9
  30. package/src/edit/streaming.ts +4 -6
  31. package/src/eval/py/index.ts +1 -1
  32. package/src/extensibility/custom-tools/types.ts +1 -1
  33. package/src/extensibility/shared-events.ts +1 -1
  34. package/src/internal-urls/docs-index.generated.ts +7 -7
  35. package/src/internal-urls/index.ts +1 -0
  36. package/src/internal-urls/router.ts +2 -0
  37. package/src/internal-urls/vault-protocol.ts +936 -0
  38. package/src/main.ts +1 -2
  39. package/src/mcp/transports/http.ts +29 -2
  40. package/src/modes/components/tool-execution.ts +6 -4
  41. package/src/modes/controllers/event-controller.ts +10 -3
  42. package/src/modes/interactive-mode.ts +10 -2
  43. package/src/modes/utils/ui-helpers.ts +2 -1
  44. package/src/prompts/system/system-prompt.md +3 -0
  45. package/src/prompts/tools/ast-edit.md +1 -1
  46. package/src/prompts/tools/ast-grep.md +1 -1
  47. package/src/prompts/tools/read.md +3 -3
  48. package/src/prompts/tools/search.md +1 -1
  49. package/src/sdk.ts +26 -1
  50. package/src/session/agent-session.ts +82 -11
  51. package/src/system-prompt.ts +2 -0
  52. package/src/tools/ast-edit.ts +10 -7
  53. package/src/tools/ast-grep.ts +12 -11
  54. package/src/tools/eval.ts +28 -3
  55. package/src/tools/match-line-format.ts +2 -2
  56. package/src/tools/path-utils.ts +2 -0
  57. package/src/tools/plan-mode-guard.ts +6 -1
  58. package/src/tools/read.ts +70 -55
  59. package/src/tools/render-utils.ts +15 -0
  60. package/src/tools/search.ts +12 -12
  61. package/src/tools/write.ts +61 -6
  62. package/src/utils/file-mentions.ts +11 -5
  63. package/src/web/search/providers/codex.ts +2 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,58 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.5.8] - 2026-05-28
6
+
7
+ ### Breaking Changes
8
+
9
+ - Changed hashline edit parsing to require wrapped hunk headers such as `@@ A..B @@` (including `@@ BOF @@` and `@@ EOF @@`), with empty `@@ A..B @@` blocks deleting the anchored range and legacy inline payload forms treated as malformed
10
+
11
+ ### Added
12
+
13
+ - Added `vault.enabled` setting (Tools → Obsidian Vault, default `false`) gating the `vault://` internal URL. When disabled, `VaultProtocolHandler.resolve` / `write`, `resolveVaultUrlToPath`, and `hasObsidian()` all refuse — the latter hides the `vault://` entry from the system prompt's Handlebars `{{#if hasObsidian}}` block. Tests can opt in via `vi.spyOn(vaultProtocol, "isVaultEnabled").mockReturnValue(true)`.
14
+
15
+ - Added support for `vault://` URLs in path resolution utilities, including plan mode and internal selector parsing so `read` and edit paths can target Obsidian vault files directly
16
+ - Added `vault://` internal URLs for editable Obsidian vault files, with filesystem-backed read/write/listing and CLI-backed vault index operations.
17
+ - Added strict-mode indicators to `omp auth-gateway check` output by appending `[strict]` to strict-mode text headers and adding a top-level `strict` field in `--json` output
18
+ - `omp auth-gateway check --strict` exercises each broker-supplied credential against its provider's chat-completion endpoint (cheapest bundled chat model per provider, with 15s/attempt timeout and up to 4 catalog fall-throughs on "model not found / invalid model" errors). Surfaces failures where the usage endpoint reports 200 but the chat endpoint 401s the same bearer (revoked OAuth scope, mislabeled provider row, …). Output gains a `[chat: ok|FAIL|skip]` column in text mode and a `completion` field on each credential in `--json` mode; the chat-failed count contributes to the non-zero exit code.
19
+
20
+ ### Changed
21
+
22
+ - Changed hashline apply behavior to preserve duplicated boundary and context lines in replacement and insert payloads instead of auto-absorbing or dropping them
23
+ - Updated hashline syntax: replaced `↑`/`↓` payload sigils with `^` repeat syntax and `|` literal rows for clearer edit semantics
24
+ - Changed hashline delete syntax from bare `A:` or `A-B:` to explicit `A-B:-` inline delete marker
25
+ - Modified hashline anchor syntax to require explicit range notation `A-B:` instead of shorthand `A:` for single-line operations
26
+ - Updated hashline description in settings to clarify pure insert context behavior without arrow notation
27
+
28
+ ### Removed
29
+
30
+ - Removed the `edit.hashlineAutoDropPureInsertDuplicates` setting
31
+ - Removed the `edit.hashlineAutoDropPureInsertDuplicates` setting from configuration and execution paths
32
+ - Removed the `edit.hashlineAutoDropPureInsertDuplicates` setting
33
+ - Removed the `edit.hashlineAutoDropPureInsertDuplicates` setting from configuration and execution paths
34
+
35
+ ### Fixed
36
+
37
+ - Fixed agent yielding silently on `response.incomplete` (OpenAI Responses / Codex `stopReason: "length"`). The agent now treats output-side incompletion as a recovery case: drops the truncated/reasoning-only assistant turn, attempts context promotion to a larger model, and falls back to compaction or handoff. `AutoCompactionStartEvent.reason` and the custom-tool `auto_compaction_start.trigger` discriminator gain an `"incomplete"` value. The handoff strategy is honored for `"incomplete"` (unlike `"overflow"`, where the input is broken and handoff would hit the same wall).
38
+ - Fixed `eval` tool to resize large displayed images and append dimension notes to text output
39
+ - Fixed `write` tool to strip malformed or loose hashline section headers before writing file content
40
+ - Fixed `eval` tool image rendering to resize displayed images before returning them and append image-dimension notes to text output
41
+ - Fixed `write` tool output sanitation to strip malformed or loose hashline section headers before writing file content
42
+ - Fixed `omp auth-broker serve` crashing at startup with `logger.setTransports is not a function` — switched the call site to `import { setTransports } from "@oh-my-pi/pi-utils/logger"`, bypassing the `logger` namespace re-export that some Bun versions failed to expose at runtime
43
+ - Fixed `omp auth-gateway` returning `502 upstream_error` and refusing to rotate credentials when a provider responded with a non-401 usage-limit error (Codex `usage_limit_reached`, Anthropic `usage_limit_reached`, Google `resource_exhausted`). `classifyGatewayError` now reuses `pi-ai`'s central `isUsageLimitError` heuristic and reports those failures as `429 rate_limit_error`. `streamSimple`'s pre-emit retry hook fires on usage-limit phrasing in addition to HTTP 401; the gateway's refresh callback branches on the error type and calls `AuthStorage.markUsageLimitReached(provider, sessionId, { retryAfterMs })` — temporarily blocking just the exhausted credential and surfacing the next sibling — instead of `invalidateCredentialMatching`, which would have suspect/deleted the row. The same branching is wired into the coding-agent `streamFn` callback so subscription multi-account rotation works the same on both surfaces.
44
+ - Fixed `extractRetryHint` not recognising Codex's `Try again in ~N min.` / `… hour` / `… hours` phrasing, which left the gateway and TUI without a server-suggested retry window when an upstream account hit its usage cap. The shared `try again in` pattern now accepts `min`, `minutes`, `mins`, `h`, `hr`, `hour`, `hours` units in addition to `ms` / `s` / `sec`, and tolerates a leading `~` and embedded whitespace.
45
+ - Fixed the auth-gateway threading `sessionId: undefined` into `AuthStorage.getApiKey`, which left `#sessionLastCredential` empty and made `markUsageLimitReached` a no-op for gateway-mediated requests. Both `/v1/chat/completions`-style endpoints and the `/v1/pi/stream` fast path now derive a stable `sessionId` from the client's `prompt_cache_key` (or the existing model+system+tools+first-message hash when absent) and reuse the same identity for credential-stickiness and prefix-cache routing.
46
+ - Fixed `eval` tool to resize large displayed images and append dimension notes to text output
47
+ - Fixed `write` tool to strip malformed or loose hashline section headers before writing file content
48
+ - Fixed `eval` tool image rendering to resize displayed images before returning them and append image-dimension notes to text output
49
+ - Fixed `write` tool output sanitation to strip malformed or loose hashline section headers before writing file content
50
+ - Fixed `omp auth-broker serve` crashing at startup with `logger.setTransports is not a function` — switched the call site to `import { setTransports } from "@oh-my-pi/pi-utils/logger"`, bypassing the `logger` namespace re-export that some Bun versions failed to expose at runtime
51
+ - Fixed user shortcut Python execution to namespace session IDs like eval, so both paths share one kernel
52
+
53
+ ### Security
54
+
55
+ - Secured `vault://` reads and writes by validating URL paths and blocking traversal, absolute paths, and symlink escapes outside the selected vault root
56
+
5
57
  ## [15.5.7] - 2026-05-27
6
58
  ### Added
7
59
  - `providers.openrouterVariant` setting (Settings → Providers → "OpenRouter Routing") to default OpenRouter requests to a routing-variant suffix (`:nitro`, `:floor`, `:online`, `:exacto`). Selectors that already name a variant (e.g. `openrouter/anthropic/claude-haiku:nitro`) keep precedence.
@@ -8887,4 +8939,4 @@ Initial public release.
8887
8939
  - Git branch display in footer
8888
8940
  - Message queueing during streaming responses
8889
8941
  - OAuth integration for Gmail and Google Calendar access
8890
- - HTML export with syntax highlighting and collapsible sections
8942
+ - HTML export with syntax highlighting and collapsible sections
@@ -11,6 +11,14 @@ export interface AuthGatewayCommandArgs {
11
11
  * to wire token-paste plumbing into every local client.
12
12
  */
13
13
  noAuth?: boolean;
14
+ /**
15
+ * Strict mode for `check` — additionally exercise every credential
16
+ * against its provider's chat-completion endpoint. The usage probe (run
17
+ * unconditionally) can pass while the chat endpoint still 401s the same
18
+ * bearer, so strict mode is the definitive "is this credential
19
+ * actually usable" signal. Slower and consumes a tiny amount of quota.
20
+ */
21
+ strict?: boolean;
14
22
  };
15
23
  }
16
24
  declare const ACTIONS: readonly AuthGatewayAction[];
@@ -26,6 +26,9 @@ export default class AuthGateway extends Command {
26
26
  "no-auth": import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
27
27
  description: string;
28
28
  };
29
+ strict: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
30
+ description: string;
31
+ };
29
32
  };
30
33
  static examples: string[];
31
34
  run(): Promise<void>;
@@ -1918,15 +1918,6 @@ export declare const SETTINGS_SCHEMA: {
1918
1918
  readonly description: "Abort streaming edit tool calls when patch preview fails";
1919
1919
  };
1920
1920
  };
1921
- readonly "edit.hashlineAutoDropPureInsertDuplicates": {
1922
- readonly type: "boolean";
1923
- readonly default: false;
1924
- readonly ui: {
1925
- readonly tab: "editing";
1926
- readonly label: "Hashline Duplicate Insert Drop";
1927
- readonly description: "Drop payload lines that duplicate adjacent file context \u2014 2+-line context echoes on `\u2191`/`\u2193` inserts, and a single boundary line at either edge of an `A-B:` replacement";
1928
- };
1929
- };
1930
1921
  readonly "edit.blockAutoGenerated": {
1931
1922
  readonly type: "boolean";
1932
1923
  readonly default: true;
@@ -1951,7 +1942,7 @@ export declare const SETTINGS_SCHEMA: {
1951
1942
  readonly ui: {
1952
1943
  readonly tab: "editing";
1953
1944
  readonly label: "Hash Lines";
1954
- readonly description: "Include file-hash headers and line numbers in read output for hashline edit mode (\u00B6PATH#hash plus LINE:content)";
1945
+ readonly description: "Include snapshot-tag headers and line numbers in read output for hashline edit mode (\u00B6PATH#tag plus LINE:content)";
1955
1946
  };
1956
1947
  };
1957
1948
  readonly "read.defaultLimit": {
@@ -2438,6 +2429,15 @@ export declare const SETTINGS_SCHEMA: {
2438
2429
  readonly description: "Allow the read tool to fetch and process URLs";
2439
2430
  };
2440
2431
  };
2432
+ readonly "vault.enabled": {
2433
+ readonly type: "boolean";
2434
+ readonly default: false;
2435
+ readonly ui: {
2436
+ readonly tab: "tools";
2437
+ readonly label: "Obsidian Vault";
2438
+ readonly description: "Enable the vault:// internal URL for reading and editing Obsidian vault content via the Obsidian CLI. When disabled, vault:// resolution is refused and the vault:// entry is omitted from the system prompt.";
2439
+ };
2440
+ };
2441
2441
  readonly "github.enabled": {
2442
2442
  readonly type: "boolean";
2443
2443
  readonly default: false;
@@ -2,17 +2,20 @@
2
2
  * Session-bound file snapshot store.
3
3
  *
4
4
  * Used by `read` and `search` to record exactly what the model saw, and by
5
- * the hashline patcher to recover from stale section hashes (file changed
6
- * externally between read and edit, or a prior in-session edit advanced
7
- * the hash). The store is the {@link InMemorySnapshotStore} implementation
5
+ * the hashline patcher to verify or recover from stale section tags (file
6
+ * changed externally between read and edit, or a prior in-session edit
7
+ * advanced the tag). The store is the {@link InMemorySnapshotStore}
8
8
  * from `@oh-my-pi/hashline`; the only coding-agent-specific concern here
9
- * is wiring it onto the per-session {@link ToolSession} object.
9
+ * is wiring it onto the per-session owner object.
10
10
  */
11
11
  import { InMemorySnapshotStore } from "@oh-my-pi/hashline";
12
- import type { ToolSession } from "../tools";
12
+ interface FileSnapshotStoreOwner {
13
+ fileSnapshotStore?: InMemorySnapshotStore;
14
+ }
13
15
  /**
14
16
  * Look up (or lazily create) the file snapshot store attached to a session.
15
17
  * Storage lives on `session.fileSnapshotStore` so it ages out exactly with
16
18
  * the session itself.
17
19
  */
18
- export declare function getFileSnapshotStore(session: ToolSession): InMemorySnapshotStore;
20
+ export declare function getFileSnapshotStore(session: FileSnapshotStoreOwner): InMemorySnapshotStore;
21
+ export {};
@@ -5,13 +5,12 @@
5
5
  * pair to {@link generateDiffString} so the renderer can show the diff
6
6
  * while the tool call is still streaming.
7
7
  *
8
- * Validation is intentionally light: only the section file hash is checked
8
+ * Validation is intentionally light: only the section snapshot tag is checked
9
9
  * (so the preview goes red when anchors are stale), no plan-mode guards
10
10
  * and no auto-generated-file refusal — those belong on the write path.
11
11
  */
12
- import { type PatchSection } from "@oh-my-pi/hashline";
12
+ import { type PatchSection, type SnapshotStore } from "@oh-my-pi/hashline";
13
13
  export interface HashlineDiffOptions {
14
- autoDropPureInsertDuplicates?: boolean;
15
14
  /**
16
15
  * Use the streaming-tolerant applier ({@link PatchSection.applyPartialTo})
17
16
  * so trailing in-flight ops do not throw or emit phantom edits. Streaming
@@ -19,7 +18,7 @@ export interface HashlineDiffOptions {
19
18
  */
20
19
  streaming?: boolean;
21
20
  }
22
- export declare function computeHashlineSectionDiff(section: PatchSection, cwd: string, options?: HashlineDiffOptions): Promise<{
21
+ export declare function computeHashlineSectionDiff(section: PatchSection, cwd: string, snapshots: SnapshotStore, options?: HashlineDiffOptions): Promise<{
23
22
  diff: string;
24
23
  firstChangedLine: number | undefined;
25
24
  } | {
@@ -27,7 +26,7 @@ export declare function computeHashlineSectionDiff(section: PatchSection, cwd: s
27
26
  }>;
28
27
  export declare function computeHashlineDiff(input: {
29
28
  input: string;
30
- }, cwd: string, options?: HashlineDiffOptions): Promise<{
29
+ }, cwd: string, snapshots: SnapshotStore, options?: HashlineDiffOptions): Promise<{
31
30
  diff: string;
32
31
  firstChangedLine: number | undefined;
33
32
  } | {
@@ -12,6 +12,7 @@
12
12
  * The shared renderer / `ToolExecutionComponent` consult the strategy via
13
13
  * the injected `editMode` rather than probing argument shape.
14
14
  */
15
+ import { type SnapshotStore } from "@oh-my-pi/hashline";
15
16
  import type { Theme } from "../modes/theme/theme";
16
17
  import { type EditMode, resolveEditMode } from "../utils/edit-mode";
17
18
  export interface PerFileDiffPreview {
@@ -23,9 +24,9 @@ export interface PerFileDiffPreview {
23
24
  export interface StreamingDiffContext {
24
25
  cwd: string;
25
26
  signal: AbortSignal;
27
+ snapshots: SnapshotStore;
26
28
  fuzzyThreshold?: number;
27
29
  allowFuzzy?: boolean;
28
- hashlineAutoDropPureInsertDuplicates?: boolean;
29
30
  /**
30
31
  * True while the tool's arguments are still streaming in. Strategies that
31
32
  * accept free-form text input (apply_patch, hashline) trim the trailing
@@ -1,5 +1,6 @@
1
1
  import type { ToolSession } from "../../tools";
2
2
  import type { ExecutorBackendExecOptions, ExecutorBackendResult } from "../backend";
3
+ export declare function namespaceSessionId(sessionId: string): string;
3
4
  declare const _default: {
4
5
  id: "python";
5
6
  label: string;
@@ -85,7 +85,7 @@ export type CustomToolSessionEvent = {
85
85
  previousSessionFile: string | undefined;
86
86
  } | {
87
87
  reason: "auto_compaction_start";
88
- trigger: "threshold" | "overflow" | "idle";
88
+ trigger: "threshold" | "overflow" | "idle" | "incomplete";
89
89
  action: "context-full" | "handoff";
90
90
  } | {
91
91
  reason: "auto_compaction_end";
@@ -159,7 +159,7 @@ export interface TurnEndEvent {
159
159
  /** Fired when auto-compaction starts */
160
160
  export interface AutoCompactionStartEvent {
161
161
  type: "auto_compaction_start";
162
- reason: "threshold" | "overflow" | "idle";
162
+ reason: "threshold" | "overflow" | "idle" | "incomplete";
163
163
  action: "context-full" | "handoff";
164
164
  }
165
165
  /** Fired when auto-compaction ends */
@@ -20,3 +20,4 @@ export * from "./router";
20
20
  export * from "./rule-protocol";
21
21
  export * from "./skill-protocol";
22
22
  export type * from "./types";
23
+ export * from "./vault-protocol";
@@ -0,0 +1,93 @@
1
+ import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext, WriteContext } from "./types";
2
+ type ContentType = InternalResource["contentType"];
3
+ type VaultParamValue = string | true;
4
+ type VaultParams = Record<string, VaultParamValue>;
5
+ type FileOp = "outline" | "backlinks" | "links" | "tags" | "properties" | "tasks" | "wordcount" | "history" | "base";
6
+ type VaultOp = "search" | "daily" | "daily-path" | "tags" | "tag" | "tasks" | "orphans" | "unresolved" | "deadends" | "bases" | "bookmarks" | "recents" | "templates" | "aliases" | "properties" | "property";
7
+ export interface VaultReference {
8
+ vault: string | null;
9
+ active: boolean;
10
+ forwardVault: boolean;
11
+ display: string;
12
+ }
13
+ export type ParsedVaultUrl = {
14
+ kind: "list-vaults";
15
+ url: string;
16
+ params: VaultParams;
17
+ } | {
18
+ kind: "vault-info";
19
+ url: string;
20
+ ref: VaultReference;
21
+ params: VaultParams;
22
+ } | {
23
+ kind: "fs-dir";
24
+ url: string;
25
+ ref: VaultReference;
26
+ relativePath: string;
27
+ params: VaultParams;
28
+ } | {
29
+ kind: "fs-file";
30
+ url: string;
31
+ ref: VaultReference;
32
+ relativePath: string;
33
+ params: VaultParams;
34
+ } | {
35
+ kind: "file-op";
36
+ url: string;
37
+ ref: VaultReference;
38
+ relativePath: string;
39
+ op: FileOp;
40
+ params: VaultParams;
41
+ } | {
42
+ kind: "vault-op";
43
+ url: string;
44
+ ref: VaultReference;
45
+ op: VaultOp;
46
+ params: VaultParams;
47
+ };
48
+ export interface ObsidianSpawnResult {
49
+ stdout: string;
50
+ stderr: string;
51
+ exitCode: number;
52
+ }
53
+ export interface VaultProtocolHandlerOptions {
54
+ spawnObsidian?: typeof spawnObsidian;
55
+ resolveObsidianBinary?: () => string | null;
56
+ }
57
+ interface CliInvocation {
58
+ args: string[];
59
+ contentType: ContentType;
60
+ opLabel: string;
61
+ }
62
+ export declare function parseVaultUrl(input: string | InternalUrl): ParsedVaultUrl;
63
+ export declare function spawnObsidian(bin: string, args: string[], signal?: AbortSignal, timeoutMs?: number): Promise<ObsidianSpawnResult>;
64
+ export declare function resolveObsidianBinary(): string | null;
65
+ /**
66
+ * Whether the `vault://` protocol is enabled in the active settings profile.
67
+ *
68
+ * Reads `vault.enabled` from the global settings singleton. Falls back to the
69
+ * schema default when settings are not yet initialized (e.g. during isolated
70
+ * unit tests that exercise the handler before the host calls `Settings.init`).
71
+ */
72
+ export declare function isVaultEnabled(): boolean;
73
+ export declare function hasObsidian(): boolean;
74
+ export declare class VaultDisabledError extends Error {
75
+ constructor();
76
+ }
77
+ export declare function resolveVaultUrlToPath(input: string | InternalUrl): string;
78
+ export declare function buildObsidianCliInvocation(parsed: Extract<ParsedVaultUrl, {
79
+ kind: "file-op" | "vault-op";
80
+ }>): CliInvocation;
81
+ export declare class VaultProtocolHandler implements ProtocolHandler {
82
+ #private;
83
+ readonly scheme = "vault";
84
+ readonly immutable = false;
85
+ constructor(options?: VaultProtocolHandlerOptions);
86
+ static resetForTests(): void;
87
+ static setObsidianBinaryForTests(value: string | null | undefined): void;
88
+ static setVaultDirectoryForTests(entries: ReadonlyMap<string, string> | Record<string, string> | undefined): void;
89
+ static setActiveVaultPathForTests(vaultPath: string | undefined): void;
90
+ resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource>;
91
+ write(url: InternalUrl, content: string, context?: WriteContext): Promise<void>;
92
+ }
93
+ export {};
@@ -1,4 +1,13 @@
1
1
  import type { MCPHttpServerConfig, MCPRequestOptions, MCPSseServerConfig, MCPTransport } from "../../mcp/types";
2
+ /**
3
+ * Best-effort startup deadline for the optional Streamable HTTP GET SSE listener.
4
+ *
5
+ * Returns `0` (disabled) when the operator has explicitly disabled MCP client-side
6
+ * timeouts via `timeout: 0` or `OMP_MCP_TIMEOUT_MS=0`, mirroring the rest of the
7
+ * MCP timeout surface. Otherwise caps the wait at one second and scales below
8
+ * short request timeouts so connect-time never exceeds the request budget.
9
+ */
10
+ export declare function resolveSSEConnectTimeoutMs(configTimeout?: number): number;
2
11
  /**
3
12
  * HTTP transport for MCP servers.
4
13
  * Uses POST for requests, supports SSE responses.
@@ -1,10 +1,11 @@
1
+ import type { SnapshotStore } from "@oh-my-pi/hashline";
1
2
  import type { AgentTool } from "@oh-my-pi/pi-agent-core";
2
3
  import { Container, type TUI } from "@oh-my-pi/pi-tui";
3
4
  export interface ToolExecutionOptions {
5
+ snapshots?: SnapshotStore;
4
6
  showImages?: boolean;
5
7
  editFuzzyThreshold?: number;
6
8
  editAllowFuzzy?: boolean;
7
- hashlineAutoDropPureInsertDuplicates?: boolean;
8
9
  }
9
10
  export interface ToolExecutionHandle {
10
11
  updateArgs(args: any, toolCallId?: string): void;
@@ -12,6 +12,7 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
+ import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
15
16
  import { type Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
16
17
  import { type CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
17
18
  import type { AssistantMessage, Effort, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@oh-my-pi/pi-ai";
@@ -48,7 +49,7 @@ import { YieldQueue } from "./yield-queue";
48
49
  /** Session-specific events that extend the core AgentEvent */
49
50
  export type AgentSessionEvent = AgentEvent | {
50
51
  type: "auto_compaction_start";
51
- reason: "threshold" | "overflow" | "idle";
52
+ reason: "threshold" | "overflow" | "idle" | "incomplete";
52
53
  action: "context-full" | "handoff";
53
54
  } | {
54
55
  type: "auto_compaction_end";
@@ -267,6 +268,7 @@ export declare class AgentSession {
267
268
  readonly sessionManager: SessionManager;
268
269
  readonly settings: Settings;
269
270
  readonly yieldQueue: YieldQueue;
271
+ fileSnapshotStore?: InMemorySnapshotStore;
270
272
  readonly configWarnings: string[];
271
273
  readonly rawSseDebugBuffer: RawSseDebugBuffer;
272
274
  constructor(config: AgentSessionConfig);
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Matched lines are prefixed with `*`; context lines are prefixed with a single
5
5
  * space so line numbers align in column. In hashline mode the line uses the
6
- * editable `LINE:content` shape under a file-hash header; in plain mode it keeps
7
- * the legacy `LINE|content` display-only shape. Line numbers are never padded.
6
+ * editable `LINE:content` shape under a snapshot-tag header; in plain mode it
7
+ * keeps the legacy `LINE|content` display-only shape. Line numbers are never padded.
8
8
  */
9
9
  export declare function formatMatchLine(lineNumber: number, line: string, isMatch: boolean, options: {
10
10
  useHashLines: boolean;
@@ -100,7 +100,9 @@ export interface DiffStats {
100
100
  }
101
101
  export declare function getDiffStats(diffText: string): DiffStats;
102
102
  export declare function formatDiffStats(added: number, removed: number, hunks: number, theme: Theme): string;
103
- export declare function truncateDiffByHunk(diffText: string, maxHunks: number, maxLines: number): {
103
+ export declare function truncateDiffByHunk(diffText: string, maxHunks: number, maxLines: number, options?: {
104
+ fromTail?: boolean;
105
+ }): {
104
106
  text: string;
105
107
  hiddenHunks: number;
106
108
  hiddenLines: number;
@@ -15,6 +15,8 @@ export type WriteToolInput = z.infer<typeof writeSchema>;
15
15
  export interface WriteToolDetails {
16
16
  diagnostics?: FileDiagnosticsResult;
17
17
  meta?: OutputMeta;
18
+ /** Set when the file was auto-chmod'd because content begins with a `#!` shebang. */
19
+ madeExecutable?: boolean;
18
20
  }
19
21
  type WriteParams = WriteToolInput;
20
22
  /**
@@ -1,3 +1,4 @@
1
+ import { type SnapshotStore } from "@oh-my-pi/hashline";
1
2
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
3
  /** Extract all @filepath mentions from text */
3
4
  export declare function extractFileMentions(text: string): string[];
@@ -8,4 +9,5 @@ export declare function extractFileMentions(text: string): string[];
8
9
  export declare function generateFileMentionMessages(filePaths: string[], cwd: string, options?: {
9
10
  autoResizeImages?: boolean;
10
11
  useHashLines?: boolean;
12
+ snapshotStore?: SnapshotStore;
11
13
  }): Promise<AgentMessage[]>;
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.5.7",
4
+ "version": "15.5.9",
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,13 +47,13 @@
47
47
  "@agentclientprotocol/sdk": "0.21.0",
48
48
  "@babel/parser": "^7.29.3",
49
49
  "@mozilla/readability": "^0.6.0",
50
- "@oh-my-pi/hashline": "15.5.7",
51
- "@oh-my-pi/omp-stats": "15.5.7",
52
- "@oh-my-pi/pi-agent-core": "15.5.7",
53
- "@oh-my-pi/pi-ai": "15.5.7",
54
- "@oh-my-pi/pi-natives": "15.5.7",
55
- "@oh-my-pi/pi-tui": "15.5.7",
56
- "@oh-my-pi/pi-utils": "15.5.7",
50
+ "@oh-my-pi/hashline": "15.5.9",
51
+ "@oh-my-pi/omp-stats": "15.5.9",
52
+ "@oh-my-pi/pi-agent-core": "15.5.9",
53
+ "@oh-my-pi/pi-ai": "15.5.9",
54
+ "@oh-my-pi/pi-natives": "15.5.9",
55
+ "@oh-my-pi/pi-tui": "15.5.9",
56
+ "@oh-my-pi/pi-utils": "15.5.9",
57
57
  "@puppeteer/browsers": "^2.13.0",
58
58
  "@types/turndown": "5.0.6",
59
59
  "@xterm/headless": "^6.0.0",
package/src/cli/args.ts CHANGED
@@ -247,6 +247,8 @@ export function getExtraHelpText(): string {
247
247
  OPENCODE_API_KEY - OpenCode Zen/OpenCode Go models
248
248
  CURSOR_ACCESS_TOKEN - Cursor AI models
249
249
  AI_GATEWAY_API_KEY - Vercel AI Gateway
250
+ WAFER_PASS_API_KEY - Wafer Pass (flat-rate subscription; GLM-5.1, Qwen3.5)
251
+ WAFER_SERVERLESS_API_KEY - Wafer Serverless (pay-as-you-go)
250
252
 
251
253
  ${chalk.dim("# Cloud Providers")}
252
254
  AWS_PROFILE - AWS Bedrock (or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY)
@@ -34,6 +34,7 @@ import {
34
34
  startAuthBroker,
35
35
  } from "@oh-my-pi/pi-ai";
36
36
  import { $which, APP_NAME, getAgentDbPath, getConfigRootDir, isEnoent, logger, VERSION } from "@oh-my-pi/pi-utils";
37
+ import { setTransports as setLoggerTransports } from "@oh-my-pi/pi-utils/logger";
37
38
  import { $ } from "bun";
38
39
  import chalk from "chalk";
39
40
  import { resolveAuthBrokerConfig } from "../session/auth-broker-config";
@@ -124,7 +125,7 @@ async function runServe(flags: AuthBrokerCommandArgs["flags"]): Promise<void> {
124
125
  // The broker is a long-running headless service: route structured logs to
125
126
  // stdout so a process supervisor (pm2, journald, k8s) captures them, and
126
127
  // skip the rotating ~/.omp/logs/ file the TUI default would have used.
127
- logger.setTransports({ console: true, file: false });
128
+ setLoggerTransports({ console: true, file: false });
128
129
 
129
130
  const bind = flags.bind ?? DEFAULT_AUTH_BROKER_BIND;
130
131
  const token = await ensureToken();