@oh-my-pi/pi-coding-agent 15.8.3 → 15.9.1

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 (172) hide show
  1. package/CHANGELOG.md +113 -31
  2. package/dist/types/capability/skill.d.ts +7 -0
  3. package/dist/types/cli/update-cli.d.ts +15 -1
  4. package/dist/types/config/append-only-context-mode.d.ts +8 -0
  5. package/dist/types/config/model-registry.d.ts +3 -0
  6. package/dist/types/config/models-config-schema.d.ts +15 -0
  7. package/dist/types/config/settings-schema.d.ts +38 -24
  8. package/dist/types/debug/protocol-probe.d.ts +38 -0
  9. package/dist/types/debug/terminal-info.d.ts +34 -0
  10. package/dist/types/exa/mcp-client.d.ts +2 -1
  11. package/dist/types/export/html/template.generated.d.ts +1 -1
  12. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  13. package/dist/types/extensibility/extensions/types.d.ts +11 -0
  14. package/dist/types/extensibility/shared-events.d.ts +1 -1
  15. package/dist/types/index.d.ts +1 -0
  16. package/dist/types/mcp/json-rpc.d.ts +6 -1
  17. package/dist/types/mcp/tool-bridge.d.ts +4 -0
  18. package/dist/types/mcp/transports/stdio.d.ts +16 -7
  19. package/dist/types/mnemopi/state.d.ts +2 -2
  20. package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
  21. package/dist/types/modes/components/assistant-message.d.ts +3 -2
  22. package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -0
  23. package/dist/types/modes/components/hook-selector.d.ts +11 -0
  24. package/dist/types/modes/components/plugin-settings.d.ts +40 -8
  25. package/dist/types/modes/components/session-selector.d.ts +8 -3
  26. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  27. package/dist/types/modes/components/todo-reminder.d.ts +1 -1
  28. package/dist/types/modes/components/transcript-container.d.ts +36 -0
  29. package/dist/types/modes/interactive-mode.d.ts +2 -1
  30. package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
  31. package/dist/types/modes/theme/theme.d.ts +20 -1
  32. package/dist/types/modes/utils/keybinding-matchers.d.ts +4 -0
  33. package/dist/types/plan-mode/plan-handoff.d.ts +20 -0
  34. package/dist/types/session/agent-session.d.ts +5 -2
  35. package/dist/types/session/history-storage.d.ts +3 -4
  36. package/dist/types/session/indexed-session-storage.d.ts +59 -0
  37. package/dist/types/session/messages.d.ts +1 -0
  38. package/dist/types/session/redis-session-storage.d.ts +12 -85
  39. package/dist/types/session/session-manager.d.ts +21 -0
  40. package/dist/types/session/session-storage.d.ts +5 -7
  41. package/dist/types/session/sql-session-storage.d.ts +16 -85
  42. package/dist/types/slash-commands/types.d.ts +17 -4
  43. package/dist/types/task/executor.d.ts +9 -0
  44. package/dist/types/task/index.d.ts +3 -1
  45. package/dist/types/telemetry-export.d.ts +19 -0
  46. package/dist/types/tiny/compiled-runtime.d.ts +35 -0
  47. package/dist/types/tiny/text.d.ts +17 -0
  48. package/dist/types/tools/ask.d.ts +1 -0
  49. package/dist/types/tools/index.d.ts +4 -2
  50. package/dist/types/tools/path-utils.d.ts +1 -1
  51. package/dist/types/tools/search.d.ts +2 -2
  52. package/dist/types/tools/{todo-write.d.ts → todo.d.ts} +18 -18
  53. package/dist/types/utils/session-color.d.ts +7 -2
  54. package/dist/types/web/search/index.d.ts +1 -1
  55. package/dist/types/web/search/provider.d.ts +2 -2
  56. package/dist/types/web/search/providers/base.d.ts +14 -0
  57. package/dist/types/web/search/providers/exa.d.ts +9 -0
  58. package/dist/types/web/search/providers/perplexity.d.ts +2 -2
  59. package/dist/types/web/search/types.d.ts +66 -1
  60. package/package.json +15 -9
  61. package/scripts/build-binary.ts +12 -0
  62. package/src/capability/skill.ts +7 -0
  63. package/src/cli/args.ts +1 -1
  64. package/src/cli/session-picker.ts +1 -0
  65. package/src/cli/update-cli.ts +54 -2
  66. package/src/commands/completions.ts +1 -1
  67. package/src/config/append-only-context-mode.ts +37 -0
  68. package/src/config/models-config-schema.ts +1 -0
  69. package/src/config/settings-schema.ts +24 -57
  70. package/src/debug/index.ts +67 -1
  71. package/src/debug/protocol-probe.ts +267 -0
  72. package/src/debug/terminal-info.ts +127 -0
  73. package/src/exa/mcp-client.ts +11 -5
  74. package/src/export/html/template.generated.ts +1 -1
  75. package/src/export/html/template.js +3 -3
  76. package/src/extensibility/custom-tools/types.ts +1 -1
  77. package/src/extensibility/extensions/types.ts +11 -0
  78. package/src/extensibility/shared-events.ts +1 -1
  79. package/src/extensibility/skills.ts +3 -3
  80. package/src/index.ts +1 -0
  81. package/src/internal-urls/docs-index.generated.ts +7 -7
  82. package/src/main.ts +16 -2
  83. package/src/mcp/json-rpc.ts +8 -0
  84. package/src/mcp/render.ts +3 -0
  85. package/src/mcp/tool-bridge.ts +10 -2
  86. package/src/mcp/transports/http.ts +33 -16
  87. package/src/mcp/transports/stdio.ts +37 -12
  88. package/src/mnemopi/state.ts +4 -4
  89. package/src/modes/acp/acp-agent.ts +168 -3
  90. package/src/modes/acp/acp-event-mapper.ts +7 -7
  91. package/src/modes/components/agent-dashboard.ts +103 -31
  92. package/src/modes/components/assistant-message.ts +3 -2
  93. package/src/modes/components/extensions/extension-dashboard.ts +56 -10
  94. package/src/modes/components/history-search.ts +128 -14
  95. package/src/modes/components/hook-selector.ts +149 -14
  96. package/src/modes/components/plugin-settings.ts +270 -36
  97. package/src/modes/components/session-selector.ts +81 -19
  98. package/src/modes/components/settings-selector.ts +1 -1
  99. package/src/modes/components/status-line/segments.ts +2 -1
  100. package/src/modes/components/status-line.ts +1 -1
  101. package/src/modes/components/tips.txt +6 -2
  102. package/src/modes/components/todo-reminder.ts +1 -1
  103. package/src/modes/components/tool-execution.ts +9 -4
  104. package/src/modes/components/transcript-container.ts +109 -0
  105. package/src/modes/controllers/command-controller.ts +4 -3
  106. package/src/modes/controllers/event-controller.ts +12 -7
  107. package/src/modes/controllers/extension-ui-controller.ts +3 -0
  108. package/src/modes/controllers/input-controller.ts +10 -5
  109. package/src/modes/controllers/selector-controller.ts +30 -19
  110. package/src/modes/controllers/todo-command-controller.ts +1 -1
  111. package/src/modes/interactive-mode.ts +56 -9
  112. package/src/modes/print-mode.ts +5 -0
  113. package/src/modes/rpc/rpc-types.ts +1 -1
  114. package/src/modes/theme/theme.ts +48 -8
  115. package/src/modes/utils/keybinding-matchers.ts +10 -0
  116. package/src/modes/utils/ui-helpers.ts +1 -0
  117. package/src/plan-mode/plan-handoff.ts +37 -0
  118. package/src/priority.json +4 -0
  119. package/src/prompts/goals/goal-continuation.md +1 -1
  120. package/src/prompts/steering/user-interjection.md +10 -0
  121. package/src/prompts/system/agent-creation-architect.md +1 -26
  122. package/src/prompts/system/eager-todo.md +3 -3
  123. package/src/prompts/system/orchestrate-notice.md +5 -5
  124. package/src/prompts/system/plan-mode-approved.md +14 -17
  125. package/src/prompts/system/plan-mode-reference.md +3 -6
  126. package/src/prompts/system/subagent-system-prompt.md +11 -0
  127. package/src/prompts/system/system-prompt.md +143 -145
  128. package/src/prompts/system/title-system.md +3 -2
  129. package/src/prompts/system/workflow-notice.md +1 -1
  130. package/src/prompts/tools/browser.md +33 -30
  131. package/src/prompts/tools/render-mermaid.md +2 -2
  132. package/src/prompts/tools/search.md +1 -1
  133. package/src/prompts/tools/task.md +3 -1
  134. package/src/prompts/tools/{todo-write.md → todo.md} +5 -5
  135. package/src/sdk.ts +6 -21
  136. package/src/session/agent-session.ts +44 -21
  137. package/src/session/history-storage.ts +11 -18
  138. package/src/session/indexed-session-storage.ts +430 -0
  139. package/src/session/messages.ts +80 -0
  140. package/src/session/redis-session-storage.ts +66 -377
  141. package/src/session/session-manager.ts +109 -23
  142. package/src/session/session-storage.ts +148 -68
  143. package/src/session/sql-session-storage.ts +131 -382
  144. package/src/slash-commands/helpers/todo.ts +2 -2
  145. package/src/slash-commands/types.ts +27 -10
  146. package/src/task/executor.ts +9 -1
  147. package/src/task/index.ts +51 -1
  148. package/src/telemetry-export.ts +126 -0
  149. package/src/tiny/compiled-runtime.ts +179 -0
  150. package/src/tiny/text.ts +112 -1
  151. package/src/tiny/worker.ts +24 -2
  152. package/src/tools/ask.ts +133 -87
  153. package/src/tools/fetch.ts +17 -4
  154. package/src/tools/find.ts +2 -2
  155. package/src/tools/index.ts +6 -4
  156. package/src/tools/memory-recall.ts +1 -1
  157. package/src/tools/memory-reflect.ts +1 -1
  158. package/src/tools/path-utils.ts +16 -7
  159. package/src/tools/renderers.ts +2 -2
  160. package/src/tools/search.ts +6 -3
  161. package/src/tools/ssh.ts +26 -10
  162. package/src/tools/{todo-write.ts → todo.ts} +32 -35
  163. package/src/tools/write.ts +14 -2
  164. package/src/tui/status-line.ts +15 -4
  165. package/src/utils/session-color.ts +39 -14
  166. package/src/utils/title-generator.ts +9 -2
  167. package/src/web/search/index.ts +4 -2
  168. package/src/web/search/provider.ts +19 -35
  169. package/src/web/search/providers/base.ts +17 -0
  170. package/src/web/search/providers/exa.ts +111 -7
  171. package/src/web/search/providers/perplexity.ts +8 -4
  172. package/src/web/search/types.ts +74 -32
@@ -1,4 +1,4 @@
1
- import type { SessionStorage, SessionStorageStat, SessionStorageWriter } from "./session-storage";
1
+ import { IndexedSessionStorage } from "./indexed-session-storage";
2
2
  /**
3
3
  * Minimal subset of the `bun:redis` `RedisClient` surface used by
4
4
  * {@link RedisSessionStorage}. Keeping the contract narrow (and accepting any
@@ -7,6 +7,8 @@ import type { SessionStorage, SessionStorageStat, SessionStorageWriter } from ".
7
7
  */
8
8
  export interface RedisSessionStorageClient {
9
9
  get(key: string): Promise<string | null>;
10
+ getrange(key: string, start: number, end: number): Promise<string>;
11
+ strlen(key: string): Promise<number>;
10
12
  set(key: string, value: string): Promise<unknown>;
11
13
  append(key: string, value: string): Promise<number>;
12
14
  del(...keys: string[]): Promise<number>;
@@ -26,7 +28,7 @@ export interface RedisSessionStorageOptions {
26
28
  */
27
29
  prefix?: string;
28
30
  /**
29
- * Maximum number of keys returned per SCAN batch when warming the mirror.
31
+ * Maximum number of keys returned per SCAN batch when warming the metadata index.
30
32
  * Default 500.
31
33
  */
32
34
  scanCount?: number;
@@ -34,91 +36,16 @@ export interface RedisSessionStorageOptions {
34
36
  /**
35
37
  * Redis-backed implementation of {@link SessionStorage}. Each session JSONL
36
38
  * file maps to a Redis STRING key, with per-key metadata (mtime) tracked in a
37
- * single sibling HASH. An in-memory mirror is loaded on construction so the
38
- * interface's synchronous methods (`existsSync`, `statSync`, `listFilesSync`,
39
- * `readTextSync`, `writeTextSync`) keep their contracts Bun's Redis client
40
- * is async only, and the persist hot path (`writer.writeLineSync`) cannot
41
- * wait on a network round-trip.
42
- *
43
- * Trade-offs vs `FileSessionStorage`:
44
- * - Mirror state is process-local. Two processes writing the same session key
45
- * will diverge until one of them reloads via {@link refresh}. This matches
46
- * `FileSessionStorage`'s existing single-writer assumption.
47
- * - `writeLineSync` updates the mirror synchronously and queues an async
48
- * `APPEND`. The promise is awaited by `flush()` / `close()` / {@link drain}.
49
- * A SIGKILL landing between the sync mirror update and the network round
50
- * trip loses the last line; the file-backed implementation survives that
51
- * window because bytes are handed to the kernel page cache before
52
- * returning.
53
- * - Blobs (image data) and tool artifact files still live on disk via
54
- * `BlobStore` / `ArtifactManager`. Those are out of scope for this storage.
39
+ * single sibling HASH. This process keeps only a metadata index (`size`,
40
+ * `mtimeMs`) in memory so synchronous `existsSync` / `statSync` /
41
+ * `listFilesSync` calls remain available without mirroring full content.
55
42
  */
56
- export declare class RedisSessionStorage implements SessionStorage {
57
- #private;
58
- private constructor();
43
+ export declare class RedisSessionStorage extends IndexedSessionStorage {
59
44
  /**
60
- * Warm the in-memory mirror with every existing session key under the
61
- * configured prefix and return the ready-to-use storage. Must be awaited
62
- * before passing the storage into `SessionManager.create()` so synchronous
63
- * lookups (session resume, recent sessions, EPERM-backup recovery) see
64
- * the existing keyspace.
45
+ * Warm the metadata index with every existing session key under the configured
46
+ * prefix and return the ready-to-use storage. Must be awaited before passing
47
+ * the storage into `SessionManager.create()` so synchronous lookups (session
48
+ * resume, recent sessions, EPERM-backup recovery) see the existing keyspace.
65
49
  */
66
50
  static create(options: RedisSessionStorageOptions): Promise<RedisSessionStorage>;
67
- /**
68
- * Re-scan Redis and replace the mirror's contents. Call this from a
69
- * different process that took over a session keyspace, or after an
70
- * out-of-band write made by another agent.
71
- */
72
- refresh(): Promise<void>;
73
- /**
74
- * Resolve once every pending background write (issued via `writeTextSync`
75
- * or `writer.writeLineSync`) has been acknowledged by Redis. Throws if any
76
- * background write failed since the last drain.
77
- *
78
- * Call this on graceful shutdown to avoid losing the last unflushed line.
79
- * The session-manager's own `flush()` / `close()` already drain through
80
- * the writer chain — this method exists for callers (test harnesses,
81
- * subprocess-style consumers) that bypass the writer.
82
- */
83
- drain(): Promise<void>;
84
- ensureDirSync(_dir: string): void;
85
- existsSync(path: string): boolean;
86
- writeTextSync(path: string, content: string): void;
87
- readTextSync(path: string): string;
88
- statSync(path: string): SessionStorageStat;
89
- listFilesSync(dir: string, pattern: string): string[];
90
- exists(path: string): Promise<boolean>;
91
- readText(path: string): Promise<string>;
92
- readTextPrefix(path: string, maxBytes: number): Promise<string>;
93
- writeText(path: string, content: string): Promise<void>;
94
- rename(src: string, dst: string): Promise<void>;
95
- unlink(path: string): Promise<void>;
96
- deleteSessionWithArtifacts(sessionPath: string): Promise<void>;
97
- openWriter(path: string, options?: {
98
- flags?: "a" | "w";
99
- onError?: (err: Error) => void;
100
- }): SessionStorageWriter;
101
- _writerClosed(writer: RedisSessionStorageWriter): void;
102
- /** Mirror-only mutation, no Redis call. Used by writers to update local state synchronously. */
103
- _mirrorAppend(path: string, line: string): void;
104
- /** Mirror-only mutation, no Redis call. Used by writers opened with `flags: "w"` to truncate. */
105
- _mirrorTruncate(path: string): void;
106
- _remoteTruncate(path: string): Promise<void>;
107
- _remoteAppend(path: string, line: string): Promise<void>;
108
- /** Record a writer's pending promise on the storage-level tail so `drain()` waits for it. */
109
- _attachPending(promise: Promise<void>): void;
110
- }
111
- declare class RedisSessionStorageWriter implements SessionStorageWriter {
112
- #private;
113
- constructor(storage: RedisSessionStorage, path: string, options?: {
114
- flags?: "a" | "w";
115
- onError?: (err: Error) => void;
116
- });
117
- writeLineSync(line: string): void;
118
- writeLine(line: string): Promise<void>;
119
- flush(): Promise<void>;
120
- fsync(): Promise<void>;
121
- close(): Promise<void>;
122
- getError(): Error | undefined;
123
51
  }
124
- export {};
@@ -169,8 +169,24 @@ export interface SessionContext {
169
169
  /** Mode-specific data from the last mode_change entry */
170
170
  modeData?: Record<string, unknown>;
171
171
  }
172
+ export declare const EPHEMERAL_MODEL_CHANGE_ROLE = "fallback";
172
173
  /** Lists session model strings to try when restoring, in fallback order. */
173
174
  export declare function getRestorableSessionModels(models: Readonly<Record<string, string>>, lastModelChangeRole: string | undefined): string[];
175
+ /**
176
+ * Coarse lifecycle status of a session, derived from its last persisted message.
177
+ *
178
+ * - `complete` — the last assistant turn ended with no unanswered tool calls, i.e.
179
+ * the agent yielded control back to the user.
180
+ * - `interrupted` — work was cut off mid-flight: a trailing assistant turn with
181
+ * pending tool calls, a trailing tool result the agent never continued from, or
182
+ * a length-truncated turn.
183
+ * - `aborted` — the last assistant turn was cancelled by the user.
184
+ * - `error` — the last assistant turn ended in an error.
185
+ * - `pending` — a trailing user message with no assistant reply persisted after it.
186
+ * - `unknown` — status could not be determined (empty/header-only session, or the
187
+ * final message was larger than the tail window that was read).
188
+ */
189
+ export type SessionStatus = "complete" | "interrupted" | "aborted" | "error" | "pending" | "unknown";
174
190
  export interface SessionInfo {
175
191
  path: string;
176
192
  id: string;
@@ -186,6 +202,11 @@ export interface SessionInfo {
186
202
  size: number;
187
203
  firstMessage: string;
188
204
  allMessagesText: string;
205
+ /**
206
+ * Coarse lifecycle status from the session's last persisted message. Optional:
207
+ * synthesized {@link SessionInfo}s (cross-project stubs, tests) leave it unset.
208
+ */
209
+ status?: SessionStatus;
189
210
  }
190
211
  export type ReadonlySessionManager = Pick<SessionManager, "getCwd" | "getSessionDir" | "getSessionId" | "getSessionFile" | "getSessionName" | "getArtifactsDir" | "getArtifactManager" | "allocateArtifactPath" | "saveArtifact" | "getArtifactPath" | "getLeafId" | "getLeafEntry" | "getEntry" | "getLabel" | "getBranch" | "getHeader" | "getEntries" | "getTree" | "getUsageStatistics" | "putBlob">;
191
212
  /** Exported for testing */
@@ -22,12 +22,12 @@ export interface SessionStorage {
22
22
  ensureDirSync(dir: string): void;
23
23
  existsSync(path: string): boolean;
24
24
  writeTextSync(path: string, content: string): void;
25
- readTextSync(path: string): string;
26
25
  statSync(path: string): SessionStorageStat;
27
26
  listFilesSync(dir: string, pattern: string): string[];
28
27
  exists(path: string): Promise<boolean>;
29
28
  readText(path: string): Promise<string>;
30
- readTextPrefix(path: string, maxBytes: number): Promise<string>;
29
+ /** Read the requested UTF-8 byte windows from the head and tail of the file. */
30
+ readTextSlices(path: string, prefixBytes: number, suffixBytes: number): Promise<[string, string]>;
31
31
  writeText(path: string, content: string): Promise<void>;
32
32
  rename(path: string, nextPath: string): Promise<void>;
33
33
  unlink(path: string): Promise<void>;
@@ -41,12 +41,11 @@ export declare class FileSessionStorage implements SessionStorage {
41
41
  ensureDirSync(dir: string): void;
42
42
  existsSync(path: string): boolean;
43
43
  writeTextSync(fpath: string, content: string): void;
44
- readTextSync(fpath: string): string;
45
44
  statSync(path: string): SessionStorageStat;
46
45
  listFilesSync(dir: string, pattern: string): string[];
47
46
  exists(path: string): Promise<boolean>;
48
47
  readText(path: string): Promise<string>;
49
- readTextPrefix(path: string, maxBytes: number): Promise<string>;
48
+ readTextSlices(path: string, prefixBytes: number, suffixBytes: number): Promise<[string, string]>;
50
49
  writeText(path: string, content: string): Promise<void>;
51
50
  rename(path: string, nextPath: string): Promise<void>;
52
51
  unlink(path: string): Promise<void>;
@@ -70,13 +69,12 @@ export declare class MemorySessionStorage implements SessionStorage {
70
69
  * creates the entry. External callers should go through `openWriter()`
71
70
  * rather than touching the mirror directly.
72
71
  */
73
- appendChunkSync(path: string, chunk: string): void;
74
- readTextSync(path: string): string;
72
+ appendSync(path: string, chunk: string): void;
75
73
  statSync(path: string): SessionStorageStat;
76
74
  listFilesSync(dir: string, pattern: string): string[];
77
75
  exists(path: string): Promise<boolean>;
78
76
  readText(path: string): Promise<string>;
79
- readTextPrefix(path: string, maxBytes: number): Promise<string>;
77
+ readTextSlices(path: string, prefixBytes: number, suffixBytes: number): Promise<[string, string]>;
80
78
  writeText(path: string, content: string): Promise<void>;
81
79
  rename(path: string, nextPath: string): Promise<void>;
82
80
  unlink(path: string): Promise<void>;
@@ -1,16 +1,16 @@
1
- import type { SessionStorage, SessionStorageStat, SessionStorageWriter } from "./session-storage";
1
+ import { IndexedSessionStorage, type SessionStorageBackend } from "./indexed-session-storage";
2
2
  /**
3
3
  * Supported `bun:sql` adapter dialects. `Bun.SQL` reports this string on
4
4
  * `client.options.adapter`; we detect it once at construction and pick the
5
- * correct DDL / upsert / concat syntax for the underlying engine.
5
+ * correct DDL / upsert / concat / byte-slice syntax for the underlying engine.
6
6
  */
7
7
  export type SqlSessionStorageAdapter = "postgres" | "mysql" | "sqlite";
8
8
  /**
9
9
  * Minimal subset of the `Bun.SQL` instance surface used by
10
- * {@link SqlSessionStorage}. The real client exposes a callable
11
- * tagged-template too; we only ever call `unsafe()` so the contract here is
12
- * narrow making it trivial to swap in a test double or wrap a pooled
13
- * client.
10
+ * {@link SqlSessionStorage}. Bun's SQL client exposes a tagged-template API too,
11
+ * but this implementation intentionally uses `unsafe(query, values)` because
12
+ * the table identifier is validated and then inlined while values remain bound
13
+ * parameters.
14
14
  */
15
15
  export interface SqlSessionStorageClient {
16
16
  unsafe(query: string, values?: unknown[]): Promise<unknown[]>;
@@ -50,92 +50,23 @@ export interface SqlSessionStorageOptions {
50
50
  }
51
51
  /**
52
52
  * SQL-backed implementation of {@link SessionStorage} using `bun:sql`. Each
53
- * session JSONL file maps to a row keyed by `path`; one table stores
54
- * everything.
53
+ * session JSONL file maps to a row keyed by `path`; one table stores the file
54
+ * contents while this process keeps only a metadata index (`size`, `mtimeMs`) in
55
+ * memory for synchronous `existsSync` / `statSync` / `listFilesSync` calls.
55
56
  *
56
57
  * Works against PostgreSQL, MySQL/MariaDB, and SQLite by selecting the
57
- * dialect-correct DDL, upsert, and string-concat syntax at construction.
58
- *
59
- * Trade-offs vs `FileSessionStorage`:
60
- * - An in-memory mirror is loaded on construction so the interface's
61
- * synchronous methods (`existsSync`, `statSync`, `listFilesSync`, …) keep
62
- * their contracts; `bun:sql` is async only. Mirror state is process-local,
63
- * matching `FileSessionStorage`'s existing single-writer assumption — peer
64
- * processes need {@link refresh} to pick up out-of-band writes.
65
- * - `writeLineSync` updates the mirror synchronously and queues an async
66
- * upsert that appends the line to the existing row (or inserts it as the
67
- * first chunk). The promise is awaited by `flush()` / `close()` /
68
- * {@link drain}. A SIGKILL between the sync mirror update and the network
69
- * round-trip loses the last line.
70
- * - Blobs (image data) and tool artifact files still live on disk via
71
- * `BlobStore` / `ArtifactManager`. Those are out of scope for this storage.
58
+ * dialect-correct DDL, upsert, string-concat, byte-length, and byte-slice syntax
59
+ * at construction.
72
60
  */
73
- export declare class SqlSessionStorage implements SessionStorage {
61
+ export declare class SqlSessionStorage extends IndexedSessionStorage {
74
62
  #private;
75
- private constructor();
63
+ constructor(backend: SessionStorageBackend, adapter: SqlSessionStorageAdapter, table: string);
76
64
  /**
77
- * Apply the dialect-correct DDL (unless `createTable: false` is set) and
78
- * warm the in-memory mirror with every existing row. Must be awaited
79
- * before passing the storage into `SessionManager.create()`.
65
+ * Apply the dialect-correct DDL (unless `createTable: false` is set) and warm
66
+ * the metadata index with every existing row. Must be awaited before passing
67
+ * the storage into `SessionManager.create()`.
80
68
  */
81
69
  static create(options: SqlSessionStorageOptions): Promise<SqlSessionStorage>;
82
70
  get adapter(): SqlSessionStorageAdapter;
83
71
  get table(): string;
84
- /**
85
- * Re-load the mirror from the database. Call this from a different
86
- * process that took over the table, or after an out-of-band write made
87
- * by another agent.
88
- */
89
- refresh(): Promise<void>;
90
- /**
91
- * Resolve once every pending background write (issued via `writeTextSync`
92
- * or `writer.writeLineSync`) has been acknowledged by the database.
93
- * Throws if any background write failed since the last drain. Call on
94
- * graceful shutdown to avoid losing the last unflushed line.
95
- */
96
- drain(): Promise<void>;
97
- ensureDirSync(_dir: string): void;
98
- existsSync(path: string): boolean;
99
- writeTextSync(path: string, content: string): void;
100
- readTextSync(path: string): string;
101
- statSync(path: string): SessionStorageStat;
102
- listFilesSync(dir: string, pattern: string): string[];
103
- exists(path: string): Promise<boolean>;
104
- readText(path: string): Promise<string>;
105
- readTextPrefix(path: string, maxBytes: number): Promise<string>;
106
- writeText(path: string, content: string): Promise<void>;
107
- rename(src: string, dst: string): Promise<void>;
108
- unlink(path: string): Promise<void>;
109
- deleteSessionWithArtifacts(sessionPath: string): Promise<void>;
110
- openWriter(path: string, options?: {
111
- flags?: "a" | "w";
112
- onError?: (err: Error) => void;
113
- }): SessionStorageWriter;
114
- _writerClosed(writer: SqlSessionStorageWriter): void;
115
- _mirrorAppend(path: string, line: string): {
116
- content: string;
117
- mtimeMs: number;
118
- };
119
- _mirrorTruncate(path: string): void;
120
- _remoteTruncate(path: string): Promise<void>;
121
- /**
122
- * Append a chunk to the row at `path`, inserting if the row doesn't
123
- * exist yet. Single round-trip via the dialect-specific `upsertAppend`.
124
- */
125
- _remoteAppend(path: string, line: string, mtimeMs: number): Promise<void>;
126
- _attachPending(promise: Promise<void>): void;
127
- }
128
- declare class SqlSessionStorageWriter implements SessionStorageWriter {
129
- #private;
130
- constructor(storage: SqlSessionStorage, path: string, options?: {
131
- flags?: "a" | "w";
132
- onError?: (err: Error) => void;
133
- });
134
- writeLineSync(line: string): void;
135
- writeLine(line: string): Promise<void>;
136
- flush(): Promise<void>;
137
- fsync(): Promise<void>;
138
- close(): Promise<void>;
139
- getError(): Error | undefined;
140
72
  }
141
- export {};
@@ -27,7 +27,11 @@ export interface ParsedSlashCommand {
27
27
  /**
28
28
  * Result returned by a slash-command handler.
29
29
  *
30
- * - `void` / `undefined` — command was handled and consumed; no further input.
30
+ * - `undefined` (and the implicit `void` return) — command was handled and
31
+ * consumed; no further input. Handlers may simply omit a `return` rather than
32
+ * building `{ consumed: true }`; `void` is accepted in the handler signatures
33
+ * below so the contract typechecks under TypeScript 5.x (which does not
34
+ * coerce `() => void` to `() => T | undefined`) as well as 6.x / tsgo.
31
35
  * - `{ consumed: true }` — explicit equivalent of the above (ACP shape).
32
36
  * - `{ prompt: string }` — command handled, pass `prompt` through as the new
33
37
  * user input (e.g. `/force <tool> <prompt>` keeps `<prompt>` as the message).
@@ -97,14 +101,23 @@ export interface SlashCommandSpec extends BuiltinSlashCommand {
97
101
  /**
98
102
  * Text/ACP-mode handler. The same body is invoked from the ACP dispatcher
99
103
  * and, via the TUI adapter, when no `handleTui` override is provided.
104
+ *
105
+ * Expressed as a union of two function types — one returning a
106
+ * `SlashCommandResult`, one returning `void` — so handlers that simply
107
+ * `return` (or omit the return) typecheck under TypeScript 5.x as well as
108
+ * 6.x / tsgo. TS 5.x does not coerce a `() => void` value into a
109
+ * `() => T | undefined` slot, so the two return shapes must be siblings
110
+ * rather than a `T | void` union inside one function type (which Biome's
111
+ * `noConfusingVoidType` also rejects).
100
112
  */
101
- handle?: (command: ParsedSlashCommand, runtime: SlashCommandRuntime) => Promise<SlashCommandResult> | SlashCommandResult;
113
+ handle?: ((command: ParsedSlashCommand, runtime: SlashCommandRuntime) => SlashCommandResult | Promise<SlashCommandResult>) | ((command: ParsedSlashCommand, runtime: SlashCommandRuntime) => void | Promise<void>);
102
114
  /**
103
115
  * TUI-only handler that supersedes `handle` when both are present. Use for
104
116
  * selectors, wizards, dashboards, and anything else that requires
105
- * `InteractiveModeContext`.
117
+ * `InteractiveModeContext`. See `handle` for the rationale behind the
118
+ * function-type union shape.
106
119
  */
107
- handleTui?: (command: ParsedSlashCommand, runtime: TuiSlashCommandRuntime) => Promise<SlashCommandResult> | SlashCommandResult;
120
+ handleTui?: ((command: ParsedSlashCommand, runtime: TuiSlashCommandRuntime) => SlashCommandResult | Promise<SlashCommandResult>) | ((command: ParsedSlashCommand, runtime: TuiSlashCommandRuntime) => void | Promise<void>);
108
121
  }
109
122
  /** Result returned by `executeAcpBuiltinSlashCommand`. */
110
123
  export type AcpBuiltinSlashCommandResult = false | {
@@ -26,6 +26,15 @@ export interface ExecutorOptions {
26
26
  task: string;
27
27
  assignment?: string;
28
28
  context?: string;
29
+ /**
30
+ * The session's active overall plan, handed off so subagents spawned during
31
+ * plan execution share the same plan context as the main agent. Omitted when
32
+ * the session did not start with a plan (or while plan mode is still active).
33
+ */
34
+ planReference?: {
35
+ path: string;
36
+ content: string;
37
+ };
29
38
  description?: string;
30
39
  index: number;
31
40
  id: string;
@@ -1,7 +1,7 @@
1
1
  import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import type { ToolSession } from "..";
3
3
  import type { Theme } from "../modes/theme/theme";
4
- import { type TaskToolDetails, type TaskToolSchemaInstance } from "./types";
4
+ import { type AgentDefinition, type TaskToolDetails, type TaskToolSchemaInstance } from "./types";
5
5
  import "../tools/review";
6
6
  import { renderResult, renderCall as renderTaskCall } from "./render";
7
7
  export { loadBundledAgents as BUNDLED_AGENTS } from "./agents";
@@ -10,6 +10,8 @@ export { discoverAgents, getAgent } from "./discovery";
10
10
  export { AgentOutputManager } from "./output-manager";
11
11
  export type { AgentDefinition, AgentProgress, SingleResult, SubagentLifecyclePayload, SubagentProgressPayload, TaskParams, TaskToolDetails, } from "./types";
12
12
  export { TASK_SUBAGENT_EVENT_CHANNEL, TASK_SUBAGENT_LIFECYCLE_CHANNEL, TASK_SUBAGENT_PROGRESS_CHANNEL, taskSchema, } from "./types";
13
+ export declare const READ_ONLY_TOOL_NAMES: ReadonlySet<string>;
14
+ export declare function isReadOnlyAgent(agent: AgentDefinition): boolean;
13
15
  /**
14
16
  * Task tool - Delegate tasks to specialized agents.
15
17
  *
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Whether {@link initTelemetryExport} registered a real provider. The CLI uses
3
+ * this to decide whether to switch on the agent loop's telemetry config — there
4
+ * is no point emitting spans into a no-op tracer.
5
+ */
6
+ export declare function isTelemetryExportEnabled(): boolean;
7
+ /**
8
+ * Register the global TracerProvider + OTLP exporter when an OTLP endpoint is
9
+ * configured via env. Idempotent, and a no-op when no endpoint is set (or when
10
+ * the OTEL kill-switches are engaged), so it is safe to call unconditionally at
11
+ * startup.
12
+ */
13
+ export declare function initTelemetryExport(): void;
14
+ /**
15
+ * Flush any buffered spans to the exporter. No-op when export is disabled.
16
+ * Hosts embedding the agent can call this at natural boundaries (e.g. the end
17
+ * of a turn) so traces surface promptly rather than on the batch interval.
18
+ */
19
+ export declare function flushTelemetryExport(): Promise<void>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Walk a conditional `exports` target (string, array of fallbacks, or a
3
+ * condition object) and return the first relative path that matches a runtime
4
+ * condition in declaration order. Returns `null` when nothing applies (e.g.
5
+ * `import`-only targets).
6
+ */
7
+ export declare function selectConditionalTarget(target: unknown): string | null;
8
+ /**
9
+ * Split a bare specifier into its package name and optional subpath, handling
10
+ * scoped packages (`@scope/name/sub` → `@scope/name` + `sub`).
11
+ */
12
+ export declare function splitBareSpecifier(specifier: string): {
13
+ packageName: string;
14
+ subpath: string | undefined;
15
+ };
16
+ /**
17
+ * Resolve a bare specifier against an installed `node_modules` directory,
18
+ * honoring `exports` (CommonJS conditions), then `main`, then `index.js`.
19
+ * Returns an absolute file path, or `null` when the package/entry is absent.
20
+ */
21
+ export declare function resolveRuntimeModule(runtimeNodeModules: string, specifier: string): string | null;
22
+ interface ResolverOptions {
23
+ /** Absolute path to the runtime cache's `node_modules`. */
24
+ runtimeNodeModules: string;
25
+ /** Bare specifier → absolute file path overrides (e.g. `sharp` → no-op stub). */
26
+ stubs?: Record<string, string>;
27
+ }
28
+ /**
29
+ * Patch `node:module`'s resolver (idempotently) so bare specifiers that the
30
+ * stock compiled-binary resolver cannot find fall back to the runtime cache.
31
+ * Stock resolution is tried first, so this never changes behavior for modules
32
+ * that already resolve (the worker's own bundled imports, node builtins).
33
+ */
34
+ export declare function installRuntimeModuleResolver({ runtimeNodeModules, stubs }: ResolverOptions): void;
35
+ export {};
@@ -16,4 +16,21 @@ export declare function stripCodeBlocks(message: string): string;
16
16
  /** Prepare a raw user message for titling: drop code blocks, then bound length. */
17
17
  export declare function prepareTitleInput(message: string): string;
18
18
  export declare function formatTitleUserMessage(message: string): string;
19
+ /**
20
+ * True when a first user message is too low-signal to title (greeting, ack,
21
+ * bare number, or empty once code/punctuation/emoji are stripped).
22
+ *
23
+ * Deterministic pre-filter: the default tiny title model (~350M local) cannot
24
+ * reliably follow a "respond with none" instruction and tends to hallucinate a
25
+ * title for trivial input, so we never ask it — the caller defers titling to
26
+ * the next message instead.
27
+ */
28
+ export declare function isLowSignalTitleInput(message: string): boolean;
29
+ /**
30
+ * Sentinel a capable title model may emit when a message carries no concrete
31
+ * task. Treated as "no title yet" so the caller can defer titling. Backstop for
32
+ * the deterministic {@link isLowSignalTitleInput} filter; kept in sync with the
33
+ * `none` instruction in `prompts/system/title-system.md`.
34
+ */
35
+ export declare const NO_TITLE_SENTINEL = "none";
19
36
  export declare function normalizeGeneratedTitle(value: string | null | undefined): string | null;
@@ -100,6 +100,7 @@ interface AskRenderArgs {
100
100
  }>;
101
101
  }
102
102
  export declare const askToolRenderer: {
103
+ mergeCallAndResult: boolean;
103
104
  renderCall(args: AskRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
104
105
  renderResult(result: {
105
106
  content: Array<{
@@ -20,7 +20,7 @@ import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-disc
20
20
  import type { EventBus } from "../utils/event-bus";
21
21
  import type { WorkspaceTree } from "../workspace-tree";
22
22
  import { type CheckpointState } from "./checkpoint";
23
- import { type TodoPhase } from "./todo-write";
23
+ import { type TodoPhase } from "./todo";
24
24
  export * from "../edit";
25
25
  export * from "../exa";
26
26
  export type * from "../exa/types";
@@ -56,7 +56,7 @@ export * from "./review";
56
56
  export * from "./search";
57
57
  export * from "./search-tool-bm25";
58
58
  export * from "./ssh";
59
- export * from "./todo-write";
59
+ export * from "./todo";
60
60
  export * from "./tts";
61
61
  export * from "./write";
62
62
  export * from "./yield";
@@ -147,6 +147,8 @@ export interface ToolSession {
147
147
  settings: Settings;
148
148
  /** Plan mode state (if active) */
149
149
  getPlanModeState?: () => PlanModeState | undefined;
150
+ /** Path of the session's active plan reference (e.g. `local://<title>.md`); defaults to `local://PLAN.md`. */
151
+ getPlanReferencePath?: () => string;
150
152
  /** Goal mode state (if active or paused) */
151
153
  getGoalModeState?: () => GoalModeState | undefined;
152
154
  /** Goal runtime for the active agent session. */
@@ -9,7 +9,7 @@ export interface LineRange {
9
9
  startLine: number;
10
10
  endLine: number | undefined;
11
11
  }
12
- /** Parse a single `N`, `N-M`, `N-`, or `N+K` chunk. Throws via {@link ToolError} on invalid bounds. */
12
+ /** Parse a single `N`, `N-M`, `N-`, `N+K`, or `..`-aliased (`N..M`, `N..`) chunk. Throws via {@link ToolError} on invalid bounds. */
13
13
  export declare function parseLineRangeChunk(sel: string): LineRange | null;
14
14
  /**
15
15
  * Parse a comma-separated list of line ranges (e.g. `5-16,960-973`). Returns
@@ -8,7 +8,7 @@ import type { ToolSession } from ".";
8
8
  import { type OutputMeta } from "./output-meta";
9
9
  declare const searchSchema: z.ZodObject<{
10
10
  pattern: z.ZodString;
11
- paths: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
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
14
  skip: z.ZodOptional<z.ZodNumber>;
@@ -63,7 +63,7 @@ export declare class SearchTool implements AgentTool<typeof searchSchema, Search
63
63
  readonly description: string;
64
64
  readonly parameters: z.ZodObject<{
65
65
  pattern: z.ZodString;
66
- paths: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
66
+ paths: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
67
67
  i: z.ZodOptional<z.ZodBoolean>;
68
68
  gitignore: z.ZodOptional<z.ZodBoolean>;
69
69
  skip: z.ZodOptional<z.ZodNumber>;
@@ -25,12 +25,12 @@ export interface TodoCompletionTransition {
25
25
  phase: string;
26
26
  content: string;
27
27
  }
28
- export interface TodoWriteToolDetails {
28
+ export interface TodoToolDetails {
29
29
  phases: TodoPhase[];
30
30
  storage: "session" | "memory";
31
31
  completedTasks?: TodoCompletionTransition[];
32
32
  }
33
- declare const todoWriteSchema: z.ZodObject<{
33
+ declare const todoSchema: z.ZodObject<{
34
34
  ops: z.ZodArray<z.ZodObject<{
35
35
  op: z.ZodEnum<{
36
36
  append: "append";
@@ -51,7 +51,7 @@ declare const todoWriteSchema: z.ZodObject<{
51
51
  text: z.ZodOptional<z.ZodString>;
52
52
  }, z.core.$strip>>;
53
53
  }, z.core.$strip>;
54
- type TodoWriteParams = z.infer<typeof todoWriteSchema>;
54
+ type TodoParams = z.infer<typeof todoSchema>;
55
55
  export declare const USER_TODO_EDIT_CUSTOM_TYPE = "user_todo_edit";
56
56
  export declare function getLatestTodoPhasesFromEntries(entries: SessionEntry[]): TodoPhase[];
57
57
  /**
@@ -62,7 +62,7 @@ export declare function getLatestTodoPhasesFromEntries(entries: SessionEntry[]):
62
62
  * the caller can render a `+N more` hint. When every task in `tasks` is
63
63
  * closed (completed or abandoned), returns the trailing `maxVisible` tasks
64
64
  * with `hiddenOpenCount = 0`, so the panel keeps useful context until the
65
- * active-phase pointer advances on the next `todo_write`.
65
+ * active-phase pointer advances on the next `todo`.
66
66
  *
67
67
  * Task identity and order are preserved — this is a slice, never a sort.
68
68
  */
@@ -84,8 +84,8 @@ export declare function selectStickyTodoWindow(tasks: TodoItem[], maxVisible?: n
84
84
  * the contained side.
85
85
  */
86
86
  export declare function todoMatchesAnyDescription(content: string, descriptions: readonly string[]): boolean;
87
- /** Apply an array of `todo_write`-style ops to existing phases. Used by /todo slash command. */
88
- export declare function applyOpsToPhases(currentPhases: TodoPhase[], ops: TodoWriteParams["ops"]): {
87
+ /** Apply an array of `todo`-style ops to existing phases. Used by /todo slash command. */
88
+ export declare function applyOpsToPhases(currentPhases: TodoPhase[], ops: TodoParams["ops"]): {
89
89
  phases: TodoPhase[];
90
90
  errors: string[];
91
91
  };
@@ -96,11 +96,11 @@ export declare function markdownToPhases(md: string): {
96
96
  phases: TodoPhase[];
97
97
  errors: string[];
98
98
  };
99
- export declare class TodoWriteTool implements AgentTool<typeof todoWriteSchema, TodoWriteToolDetails> {
99
+ export declare class TodoTool implements AgentTool<typeof todoSchema, TodoToolDetails> {
100
100
  private readonly session;
101
- readonly name = "todo_write";
101
+ readonly name = "todo";
102
102
  readonly approval: "read";
103
- readonly label = "Todo Write";
103
+ readonly label = "Todo";
104
104
  readonly summary = "Write a structured todo list to track progress within a session";
105
105
  readonly description: string;
106
106
  readonly parameters: z.ZodObject<{
@@ -128,9 +128,9 @@ export declare class TodoWriteTool implements AgentTool<typeof todoWriteSchema,
128
128
  readonly strict = true;
129
129
  readonly loadMode = "discoverable";
130
130
  constructor(session: ToolSession);
131
- execute(_toolCallId: string, params: TodoWriteParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<TodoWriteToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<TodoWriteToolDetails>>;
131
+ execute(_toolCallId: string, params: TodoParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<TodoToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<TodoToolDetails>>;
132
132
  }
133
- type TodoWriteRenderArgs = {
133
+ type TodoRenderArgs = {
134
134
  ops?: Array<{
135
135
  op?: string;
136
136
  task?: string;
@@ -142,18 +142,18 @@ type TodoWriteRenderArgs = {
142
142
  export declare function phaseRomanNumeral(oneBasedIndex: number): string;
143
143
  /** Display-only phase header: `I. Foundation`. State and prompts never see this. */
144
144
  export declare function formatPhaseDisplayName(name: string, oneBasedIndex: number): string;
145
- export declare const TODO_WRITE_STRIKE_HOLD_FRAMES = 2;
146
- export declare const TODO_WRITE_STRIKE_REVEAL_FRAMES = 12;
147
- export declare const TODO_WRITE_STRIKE_TOTAL_FRAMES: number;
148
- export declare const todoWriteToolRenderer: {
149
- renderCall(args: TodoWriteRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
145
+ export declare const TODO_STRIKE_HOLD_FRAMES = 2;
146
+ export declare const TODO_STRIKE_REVEAL_FRAMES = 12;
147
+ export declare const TODO_STRIKE_TOTAL_FRAMES: number;
148
+ export declare const todoToolRenderer: {
149
+ renderCall(args: TodoRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
150
150
  renderResult(result: {
151
151
  content: Array<{
152
152
  type: string;
153
153
  text?: string;
154
154
  }>;
155
- details?: TodoWriteToolDetails;
156
- }, options: RenderResultOptions, uiTheme: Theme, _args?: TodoWriteRenderArgs): Component;
155
+ details?: TodoToolDetails;
156
+ }, options: RenderResultOptions, uiTheme: Theme, _args?: TodoRenderArgs): Component;
157
157
  mergeCallAndResult: boolean;
158
158
  };
159
159
  export {};