@oh-my-pi/pi-coding-agent 15.7.2 → 15.7.5

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 (188) hide show
  1. package/CHANGELOG.md +114 -6
  2. package/dist/types/cli/args.d.ts +1 -1
  3. package/dist/types/cli/extension-flags.d.ts +36 -0
  4. package/dist/types/config/config-file.d.ts +4 -0
  5. package/dist/types/config/file-lock.d.ts +23 -0
  6. package/dist/types/config/keybindings.d.ts +2 -1
  7. package/dist/types/config/model-registry.d.ts +6 -0
  8. package/dist/types/config/settings-schema.d.ts +69 -65
  9. package/dist/types/edit/hashline/diff.d.ts +3 -3
  10. package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
  11. package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
  12. package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
  13. package/dist/types/eval/agent-bridge.d.ts +25 -0
  14. package/dist/types/eval/backend.d.ts +17 -2
  15. package/dist/types/eval/budget-bridge.d.ts +29 -0
  16. package/dist/types/eval/idle-timeout.d.ts +28 -0
  17. package/dist/types/eval/js/executor.d.ts +8 -0
  18. package/dist/types/eval/js/tool-bridge.d.ts +2 -1
  19. package/dist/types/eval/py/executor.d.ts +13 -0
  20. package/dist/types/exec/bash-executor.d.ts +1 -0
  21. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  22. package/dist/types/extensibility/extensions/runner.d.ts +7 -0
  23. package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
  24. package/dist/types/extensibility/plugins/manager.d.ts +12 -1
  25. package/dist/types/extensibility/shared-events.d.ts +2 -2
  26. package/dist/types/internal-urls/local-protocol.d.ts +19 -9
  27. package/dist/types/internal-urls/types.d.ts +14 -0
  28. package/dist/types/lsp/client.d.ts +3 -0
  29. package/dist/types/mcp/manager.d.ts +14 -5
  30. package/dist/types/memory-backend/index.d.ts +1 -1
  31. package/dist/types/memory-backend/resolve.d.ts +1 -1
  32. package/dist/types/memory-backend/types.d.ts +3 -3
  33. package/dist/types/mnemopi/backend.d.ts +4 -0
  34. package/dist/types/mnemopi/config.d.ts +29 -0
  35. package/dist/types/mnemopi/state.d.ts +72 -0
  36. package/dist/types/modes/components/custom-editor.d.ts +2 -2
  37. package/dist/types/modes/components/omfg-panel.d.ts +19 -0
  38. package/dist/types/modes/controllers/command-controller.d.ts +6 -0
  39. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  40. package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
  41. package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
  42. package/dist/types/modes/gradient-highlight.d.ts +5 -1
  43. package/dist/types/modes/interactive-mode.d.ts +7 -3
  44. package/dist/types/modes/magic-keywords.d.ts +14 -0
  45. package/dist/types/modes/markdown-prose.d.ts +27 -0
  46. package/dist/types/modes/orchestrate.d.ts +7 -2
  47. package/dist/types/modes/shared.d.ts +1 -1
  48. package/dist/types/modes/turn-budget.d.ts +18 -0
  49. package/dist/types/modes/types.d.ts +7 -3
  50. package/dist/types/modes/ultrathink.d.ts +7 -2
  51. package/dist/types/modes/workflow.d.ts +15 -0
  52. package/dist/types/sdk.d.ts +13 -3
  53. package/dist/types/session/agent-session.d.ts +36 -17
  54. package/dist/types/session/session-manager.d.ts +18 -0
  55. package/dist/types/session/session-storage.d.ts +6 -0
  56. package/dist/types/session/shake-types.d.ts +24 -0
  57. package/dist/types/task/executor.d.ts +2 -2
  58. package/dist/types/task/repair-args.d.ts +52 -0
  59. package/dist/types/tiny/models.d.ts +1 -1
  60. package/dist/types/tiny/title-client.d.ts +28 -2
  61. package/dist/types/tiny/title-protocol.d.ts +8 -5
  62. package/dist/types/tools/find.d.ts +1 -1
  63. package/dist/types/tools/index.d.ts +19 -3
  64. package/dist/types/tools/memory-edit.d.ts +1 -1
  65. package/dist/types/tools/path-utils.d.ts +7 -0
  66. package/dist/types/tui/output-block.d.ts +7 -7
  67. package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
  68. package/package.json +10 -10
  69. package/scripts/build-binary.ts +0 -1
  70. package/src/autoresearch/tools/run-experiment.ts +45 -113
  71. package/src/cli/args.ts +39 -16
  72. package/src/cli/extension-flags.ts +48 -0
  73. package/src/cli/plugin-cli.ts +11 -2
  74. package/src/cli.ts +59 -0
  75. package/src/config/config-file.ts +98 -13
  76. package/src/config/file-lock.ts +60 -17
  77. package/src/config/keybindings.ts +78 -27
  78. package/src/config/model-registry.ts +7 -1
  79. package/src/config/settings-schema.ts +73 -67
  80. package/src/config/settings.ts +22 -0
  81. package/src/edit/hashline/diff.ts +81 -24
  82. package/src/edit/renderer.ts +16 -12
  83. package/src/eval/__tests__/agent-bridge.test.ts +433 -0
  84. package/src/eval/__tests__/budget-bridge.test.ts +69 -0
  85. package/src/eval/__tests__/idle-timeout.test.ts +66 -0
  86. package/src/eval/__tests__/shared-executors.test.ts +21 -0
  87. package/src/eval/agent-bridge.ts +295 -0
  88. package/src/eval/backend.ts +17 -2
  89. package/src/eval/budget-bridge.ts +48 -0
  90. package/src/eval/idle-timeout.ts +80 -0
  91. package/src/eval/js/executor.ts +35 -7
  92. package/src/eval/js/index.ts +2 -1
  93. package/src/eval/js/shared/prelude.txt +85 -1
  94. package/src/eval/js/tool-bridge.ts +9 -0
  95. package/src/eval/py/executor.ts +41 -14
  96. package/src/eval/py/index.ts +2 -1
  97. package/src/eval/py/prelude.py +132 -1
  98. package/src/exec/bash-executor.ts +2 -3
  99. package/src/extensibility/custom-tools/types.ts +2 -2
  100. package/src/extensibility/extensions/runner.ts +12 -2
  101. package/src/extensibility/plugins/git-url.ts +90 -4
  102. package/src/extensibility/plugins/manager.ts +103 -7
  103. package/src/extensibility/shared-events.ts +2 -2
  104. package/src/internal-urls/docs-index.generated.ts +88 -88
  105. package/src/internal-urls/local-protocol.ts +23 -11
  106. package/src/internal-urls/types.ts +15 -0
  107. package/src/lsp/client.ts +28 -5
  108. package/src/main.ts +44 -55
  109. package/src/mcp/manager.ts +87 -4
  110. package/src/memory-backend/index.ts +1 -1
  111. package/src/memory-backend/resolve.ts +3 -3
  112. package/src/memory-backend/types.ts +3 -3
  113. package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
  114. package/src/{mnemosyne → mnemopi}/config.ts +36 -36
  115. package/src/{mnemosyne → mnemopi}/state.ts +67 -67
  116. package/src/modes/components/agent-dashboard.ts +6 -6
  117. package/src/modes/components/custom-editor.ts +4 -11
  118. package/src/modes/components/extensions/state-manager.ts +3 -4
  119. package/src/modes/components/footer.ts +8 -9
  120. package/src/modes/components/hook-selector.ts +86 -20
  121. package/src/modes/components/oauth-selector.ts +93 -21
  122. package/src/modes/components/omfg-panel.ts +141 -0
  123. package/src/modes/components/settings-defs.ts +2 -2
  124. package/src/modes/components/tips.txt +2 -1
  125. package/src/modes/components/tool-execution.ts +38 -19
  126. package/src/modes/components/tree-selector.ts +4 -3
  127. package/src/modes/components/user-message-selector.ts +94 -19
  128. package/src/modes/components/user-message.ts +8 -1
  129. package/src/modes/controllers/command-controller.ts +25 -0
  130. package/src/modes/controllers/event-controller.ts +68 -3
  131. package/src/modes/controllers/input-controller.ts +14 -11
  132. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  133. package/src/modes/controllers/omfg-controller.ts +283 -0
  134. package/src/modes/controllers/omfg-rule.ts +647 -0
  135. package/src/modes/controllers/selector-controller.ts +1 -0
  136. package/src/modes/gradient-highlight.ts +23 -6
  137. package/src/modes/interactive-mode.ts +41 -7
  138. package/src/modes/magic-keywords.ts +20 -0
  139. package/src/modes/markdown-prose.ts +247 -0
  140. package/src/modes/orchestrate.ts +17 -11
  141. package/src/modes/shared.ts +3 -11
  142. package/src/modes/turn-budget.ts +31 -0
  143. package/src/modes/types.ts +7 -1
  144. package/src/modes/ultrathink.ts +16 -10
  145. package/src/modes/utils/hotkeys-markdown.ts +1 -1
  146. package/src/modes/workflow.ts +42 -0
  147. package/src/prompts/system/omfg-user.md +51 -0
  148. package/src/prompts/system/project-prompt.md +3 -2
  149. package/src/prompts/system/subagent-system-prompt.md +12 -8
  150. package/src/prompts/system/system-prompt.md +9 -6
  151. package/src/prompts/system/workflow-notice.md +70 -0
  152. package/src/prompts/tools/eval.md +13 -1
  153. package/src/prompts/tools/memory-edit.md +1 -1
  154. package/src/sdk.ts +63 -33
  155. package/src/session/agent-session.ts +290 -55
  156. package/src/session/session-manager.ts +32 -0
  157. package/src/session/session-storage.ts +68 -8
  158. package/src/session/shake-types.ts +43 -0
  159. package/src/slash-commands/builtin-registry.ts +39 -16
  160. package/src/task/executor.ts +17 -7
  161. package/src/task/index.ts +9 -8
  162. package/src/task/repair-args.ts +117 -0
  163. package/src/tiny/models.ts +2 -2
  164. package/src/tiny/title-client.ts +133 -43
  165. package/src/tiny/title-protocol.ts +10 -5
  166. package/src/tiny/worker.ts +3 -46
  167. package/src/tools/ast-edit.ts +3 -0
  168. package/src/tools/ast-grep.ts +3 -0
  169. package/src/tools/eval.ts +202 -26
  170. package/src/tools/find.ts +20 -6
  171. package/src/tools/gh.ts +1 -0
  172. package/src/tools/grouped-file-output.ts +9 -2
  173. package/src/tools/index.ts +17 -5
  174. package/src/tools/memory-edit.ts +4 -4
  175. package/src/tools/memory-recall.ts +5 -5
  176. package/src/tools/memory-reflect.ts +5 -5
  177. package/src/tools/memory-retain.ts +4 -4
  178. package/src/tools/path-utils.ts +13 -2
  179. package/src/tools/read.ts +1 -0
  180. package/src/tools/render-utils.ts +2 -1
  181. package/src/tools/search.ts +491 -76
  182. package/src/tui/output-block.ts +37 -75
  183. package/src/utils/git.ts +9 -3
  184. package/dist/types/mnemosyne/backend.d.ts +0 -4
  185. package/dist/types/mnemosyne/config.d.ts +0 -29
  186. package/dist/types/mnemosyne/state.d.ts +0 -72
  187. /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
  188. /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
16
16
  import { type Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
17
- import { type CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
17
+ import { type CompactionResult, type ShakeConfig } from "@oh-my-pi/pi-agent-core/compaction";
18
18
  import type { AssistantMessage, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@oh-my-pi/pi-ai";
19
19
  import { Effort } from "@oh-my-pi/pi-ai";
20
20
  import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async";
@@ -36,7 +36,7 @@ import { type FileSlashCommand } from "../extensibility/slash-commands";
36
36
  import { GoalRuntime } from "../goals/runtime";
37
37
  import type { Goal, GoalModeState } from "../goals/state";
38
38
  import type { HindsightSessionState } from "../hindsight/state";
39
- import { type MnemosyneSessionState } from "../mnemosyne/state";
39
+ import { type MnemopiSessionState } from "../mnemopi/state";
40
40
  import type { PlanModeState } from "../plan-mode/state";
41
41
  import { type AgentRegistry } from "../registry/agent-registry";
42
42
  import { type SecretObfuscator } from "../secrets/obfuscator";
@@ -47,16 +47,17 @@ import { type TodoItem, type TodoPhase } from "../tools/todo-write";
47
47
  import type { ClientBridge } from "./client-bridge";
48
48
  import { type CustomMessage } from "./messages";
49
49
  import type { BranchSummaryEntry, NewSessionOptions, SessionContext, SessionManager } from "./session-manager";
50
+ import type { ShakeMode, ShakeResult } from "./shake-types";
50
51
  import { ToolChoiceQueue } from "./tool-choice-queue";
51
52
  import { YieldQueue } from "./yield-queue";
52
53
  /** Session-specific events that extend the core AgentEvent */
53
54
  export type AgentSessionEvent = AgentEvent | {
54
55
  type: "auto_compaction_start";
55
56
  reason: "threshold" | "overflow" | "idle" | "incomplete";
56
- action: "context-full" | "handoff";
57
+ action: "context-full" | "handoff" | "shake";
57
58
  } | {
58
59
  type: "auto_compaction_end";
59
- action: "context-full" | "handoff";
60
+ action: "context-full" | "handoff" | "shake";
60
61
  result: CompactionResult | undefined;
61
62
  aborted: boolean;
62
63
  willRetry: boolean;
@@ -121,6 +122,7 @@ export interface AsyncJobSnapshot {
121
122
  recent: AsyncJobSnapshotItem[];
122
123
  delivery: AsyncJobDeliveryState;
123
124
  }
125
+ export type { ShakeMode, ShakeResult };
124
126
  export interface AgentSessionConfig {
125
127
  agent: Agent;
126
128
  sessionManager: SessionManager;
@@ -315,7 +317,7 @@ export declare class AgentSession {
315
317
  get providerSessionState(): Map<string, ProviderSessionState>;
316
318
  getHindsightSessionState(): HindsightSessionState | undefined;
317
319
  setHindsightSessionState(state: HindsightSessionState | undefined): HindsightSessionState | undefined;
318
- getMnemosyneSessionState(): MnemosyneSessionState | undefined;
320
+ getMnemopiSessionState(): MnemopiSessionState | undefined;
319
321
  /** TTSR manager for time-traveling stream rules */
320
322
  get ttsrManager(): TtsrManager | undefined;
321
323
  /** Whether a TTSR abort is pending (stream was aborted to inject rules) */
@@ -615,12 +617,13 @@ export declare class AgentSession {
615
617
  fork(): Promise<boolean>;
616
618
  /**
617
619
  * Set model directly.
618
- * Validates API key, saves to session and settings.
620
+ * Validates API key and saves to the active session. Persists settings only when requested.
619
621
  * @throws Error if no API key available for the model
620
622
  */
621
623
  setModel(model: Model, role?: string, options?: {
622
624
  selector?: string;
623
625
  thinkingLevel?: ThinkingLevel;
626
+ persist?: boolean;
624
627
  }): Promise<void>;
625
628
  /**
626
629
  * Set model temporarily (for this session only).
@@ -647,28 +650,26 @@ export declare class AgentSession {
647
650
  */
648
651
  getRoleModelCycle(roleOrder: readonly string[]): RoleModelCycle | undefined;
649
652
  /**
650
- * Apply a resolved role model as the active model, persisting the choice to
651
- * settings under its role. Mirrors the non-temporary branch of
652
- * {@link cycleRoleModels} and is shared with the plan-approval model slider.
653
+ * Apply a resolved role model as the active model without changing global
654
+ * settings. Shared with role cycling and the plan-approval model slider.
653
655
  */
654
656
  applyRoleModel(entry: ResolvedRoleModel): Promise<void>;
655
657
  /**
656
658
  * Cycle through configured role models in a fixed order.
657
- * Skips missing roles.
659
+ * Skips missing roles and changes only the active session model.
658
660
  * @param roleOrder - Order of roles to cycle through (e.g., ["slow", "default", "smol"])
659
- * @param options - Optional settings: `temporary` to not persist to settings
661
+ * @param direction - "forward" (default) or "backward"
660
662
  */
661
- cycleRoleModels(roleOrder: readonly string[], options?: {
662
- temporary?: boolean;
663
- }): Promise<RoleModelCycleResult | undefined>;
663
+ cycleRoleModels(roleOrder: readonly string[], direction?: "forward" | "backward"): Promise<RoleModelCycleResult | undefined>;
664
664
  /**
665
665
  * Get all available models with valid API keys.
666
666
  */
667
667
  getAvailableModels(): Model[];
668
668
  /**
669
- * Set the thinking level. `auto` enables per-turn classification (session-level,
670
- * never written to the session log); a concrete level clears auto. The effective
671
- * metadata-clamped level is saved to the session/settings only when it changes.
669
+ * Set the thinking level. `auto` enables per-turn classification; the selector
670
+ * itself is never written to the session log, but resolved concrete levels are
671
+ * persisted when real user turns are classified so resumed sessions keep the
672
+ * last resolved effort instead of reverting to pending auto.
672
673
  */
673
674
  setThinkingLevel(level: ConfiguredThinkingLevel | undefined, persist?: boolean): void;
674
675
  /**
@@ -730,6 +731,23 @@ export declare class AgentSession {
730
731
  dropImages(): Promise<{
731
732
  removed: number;
732
733
  }>;
734
+ /**
735
+ * Surgically reduce context by dropping heavy content ("shake").
736
+ *
737
+ * - `images` delegates to {@link dropImages}.
738
+ * - `elide` replaces whole tool-call results and large fenced/XML blocks
739
+ * with short placeholders that embed an `artifact://` recovery link.
740
+ *
741
+ * Mutates the branch in place, persists via `rewriteEntries`, replays the
742
+ * rebuilt context through the agent, and tears down provider sessions that
743
+ * cache message identity — same rewrite contract as {@link dropImages}.
744
+ *
745
+ * No-op (zero counts) when nothing is eligible.
746
+ */
747
+ shake(mode: ShakeMode, opts?: {
748
+ config?: ShakeConfig;
749
+ signal?: AbortSignal;
750
+ }): Promise<ShakeResult>;
733
751
  /**
734
752
  * Manually compact the session context.
735
753
  * Aborts current agent operation first.
@@ -880,6 +898,7 @@ export declare class AgentSession {
880
898
  promptText: string;
881
899
  onTextDelta?: (delta: string) => void;
882
900
  signal?: AbortSignal;
901
+ dedupeReply?: boolean;
883
902
  }): Promise<{
884
903
  replyText: string;
885
904
  assistantMessage: AssistantMessage;
@@ -306,6 +306,24 @@ export declare class SessionManager {
306
306
  getCwd(): string;
307
307
  /** Get usage statistics across all assistant messages in the session. */
308
308
  getUsageStatistics(): UsageStatistics;
309
+ /**
310
+ * Open a new per-turn budget window: snapshot the cumulative output baseline,
311
+ * reset the eval-subagent counter, and set the (optional) ceiling. Called once
312
+ * per real user message; `total` is null when no `+Nk` directive was present.
313
+ */
314
+ beginTurnBudget(total: number | null, hard: boolean): void;
315
+ /** Record output tokens consumed by an eval-spawned subagent in the current turn. */
316
+ recordEvalSubagentOutput(output: number): void;
317
+ /**
318
+ * Current turn budget for the eval `budget` helper: the ceiling (null = none),
319
+ * output tokens spent this turn (main loop + eval-spawned subagents, no
320
+ * double-count), and whether the ceiling is hard.
321
+ */
322
+ getTurnBudget(): {
323
+ total: number | null;
324
+ spent: number;
325
+ hard: boolean;
326
+ };
309
327
  getSessionDir(): string;
310
328
  getSessionId(): string;
311
329
  getSessionFile(): string | undefined;
@@ -65,6 +65,12 @@ export declare class MemorySessionStorage implements SessionStorage {
65
65
  ensureDirSync(_dir: string): void;
66
66
  existsSync(path: string): boolean;
67
67
  writeTextSync(path: string, content: string): void;
68
+ /**
69
+ * Internal O(1) append used by {@link MemorySessionStorageWriter}. Lazily
70
+ * creates the entry. External callers should go through `openWriter()`
71
+ * rather than touching the mirror directly.
72
+ */
73
+ appendChunkSync(path: string, chunk: string): void;
68
74
  readTextSync(path: string): string;
69
75
  statSync(path: string): SessionStorageStat;
70
76
  listFilesSync(dir: string, pattern: string): string[];
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Public shape of the `shake` operation, kept in a dependency-free leaf module
3
+ * so slash-command registries and controllers can import `formatShakeSummary`
4
+ * without pulling in the heavy `agent-session` module graph (which would form
5
+ * an import cycle through the slash-command registry).
6
+ */
7
+ /** Mode selector for `AgentSession.shake`. */
8
+ export type ShakeMode = "elide" | "images";
9
+ /** Outcome of an `AgentSession.shake` run. */
10
+ export interface ShakeResult {
11
+ mode: ShakeMode;
12
+ /** Whole tool-call results dropped. */
13
+ toolResultsDropped: number;
14
+ /** Large fenced/XML blocks dropped. */
15
+ blocksDropped: number;
16
+ /** Image blocks removed (images mode only). */
17
+ imagesDropped?: number;
18
+ /** Estimated context tokens reclaimed. */
19
+ tokensFreed: number;
20
+ /** Session artifact holding the dropped originals, when persisted. */
21
+ artifactId?: string;
22
+ }
23
+ /** One-line operator summary of a {@link ShakeResult} (shared by TUI + ACP). */
24
+ export declare function formatShakeSummary(result: ShakeResult): string;
@@ -11,7 +11,7 @@ import { type Skill } from "../extensibility/skills";
11
11
  import type { HindsightSessionState } from "../hindsight/state";
12
12
  import type { LocalProtocolOptions } from "../internal-urls";
13
13
  import type { MCPManager } from "../mcp/manager";
14
- import type { MnemosyneSessionState } from "../mnemosyne/state";
14
+ import type { MnemopiSessionState } from "../mnemopi/state";
15
15
  import type { ArtifactManager } from "../session/artifacts";
16
16
  import type { AuthStorage } from "../session/auth-storage";
17
17
  import type { ContextFileEntry } from "../tools";
@@ -65,7 +65,7 @@ export interface ExecutorOptions {
65
65
  */
66
66
  parentArtifactManager?: ArtifactManager;
67
67
  parentHindsightSessionState?: HindsightSessionState;
68
- parentMnemosyneSessionState?: MnemosyneSessionState;
68
+ parentMnemopiSessionState?: MnemopiSessionState;
69
69
  /** Parent agent's eval executor session id. Subagents reuse it so eval state is shared. */
70
70
  parentEvalSessionId?: string;
71
71
  /**
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Repair double-encoded JSON string arguments for the task tool.
3
+ *
4
+ * Models occasionally JSON-escape a string value twice when emitting a
5
+ * `task` tool call, so a `context`/`assignment` that should read
6
+ *
7
+ * # Role
8
+ * You are a judge … "describe this" … return —
9
+ *
10
+ * arrives — after the one JSON decode the provider already applied — as the
11
+ * literal text
12
+ *
13
+ * # Role\nYou are a judge … \"describe this\" … return \u2014
14
+ *
15
+ * i.e. every newline, quote, and unicode character is still backslash-escaped.
16
+ * The subagent then receives that garbled prompt, and the call preview renders
17
+ * one long blob with visible `\n` / `\"` / `\uXXXX`.
18
+ *
19
+ * The *whole-arguments* form of this quirk (the entire `arguments` blob is a
20
+ * JSON string) is already auto-corrected by the validator's JSON-string
21
+ * coercion. This module handles the *per-field* form, where the object parses
22
+ * fine but an individual string value is double-encoded — the validator never
23
+ * fires there because a double-encoded string is still a structurally valid
24
+ * string.
25
+ *
26
+ * This is deliberately scoped to the task tool's natural-language fields
27
+ * (`context`, `assignment`, `description`). It is NOT applied to code-bearing
28
+ * tools (write/edit/bash/search), where a backslash or quote is load-bearing
29
+ * and a false-positive unescape would silently corrupt a file or command.
30
+ */
31
+ import type { TaskParams } from "./types";
32
+ /**
33
+ * Return the once-unescaped string when `value` is uniformly double-encoded
34
+ * JSON (a well-formed JSON string body that decodes to a different string);
35
+ * otherwise return `value` unchanged.
36
+ *
37
+ * The `JSON.parse(\`"${value}"\`)` round-trip is the safety net: it only
38
+ * succeeds when *every* backslash begins a valid JSON escape and no bare
39
+ * double-quote exists — exactly the signature of double-encoding. Genuine
40
+ * prose with a Windows path (`C:\Users`), a regex (`\d+`), an embedded quote,
41
+ * or a real (already-decoded) newline makes the parse throw, so the value is
42
+ * returned untouched.
43
+ */
44
+ export declare function repairDoubleEncodedJsonString(value: string): string;
45
+ /**
46
+ * Repair double-encoded prose in task-tool params (`context` and each task's
47
+ * `assignment`/`description`). Returns the same reference when nothing changed
48
+ * so callers can cheaply skip work. Defensive against partially-streamed args
49
+ * (missing/undefined fields, partial task arrays) so it is safe on the render
50
+ * path as well as on execution.
51
+ */
52
+ export declare function repairTaskParams(params: TaskParams): TaskParams;
@@ -65,7 +65,7 @@ export declare const ONLINE_MEMORY_MODEL_KEY = "online";
65
65
  /** Recommended local model for memory tasks when none is named. */
66
66
  export declare const DEFAULT_MEMORY_LOCAL_MODEL_KEY = "qwen3-1.7b";
67
67
  /**
68
- * Local models for Mnemosyne memory tasks (fact extraction + consolidation).
68
+ * Local models for Mnemopi memory tasks (fact extraction + consolidation).
69
69
  * These are larger (1B-1.7B) than the title models: structured extraction and
70
70
  * faithful summarization need more capacity than 3-6 word titles. All q4.
71
71
  * Ranking/recipe rationale lives in docs/local-models.md.
@@ -1,8 +1,14 @@
1
- import type { TinyTitleProgressEvent } from "./title-protocol";
1
+ import type { Subprocess } from "bun";
2
+ import type { TinyTitleProgressEvent, TinyTitleWorkerOutbound } from "./title-protocol";
2
3
  export interface TinyTitleDownloadOptions {
3
4
  signal?: AbortSignal;
4
5
  onProgress?: (event: TinyTitleProgressEvent) => void;
5
6
  }
7
+ /**
8
+ * Hidden subcommand on the main CLI that boots the tiny-model worker in the
9
+ * spawned subprocess. Kept in sync with the dispatch in `cli.ts`.
10
+ */
11
+ export declare const TINY_WORKER_ARG = "--tiny-worker";
6
12
  /**
7
13
  * Decide which `PI_TINY_DEVICE` / `PI_TINY_DTYPE` vars to overlay onto the worker
8
14
  * env. A present env var wins (left untouched); otherwise the mapped persisted
@@ -11,7 +17,26 @@ export interface TinyTitleDownloadOptions {
11
17
  * @internal
12
18
  */
13
19
  export declare function tinyWorkerEnvOverlay(env: Record<string, string | undefined>, deviceSetting: string | undefined, dtypeSetting: string | undefined): Record<string, string>;
14
- export declare function createTinyTitleWorker(): Worker;
20
+ interface SpawnedSubprocess {
21
+ proc: Subprocess<"ignore", "inherit", "inherit">;
22
+ inbound: Set<(message: TinyTitleWorkerOutbound) => void>;
23
+ errors: Set<(error: Error) => void>;
24
+ /**
25
+ * Flipped to `true` by {@link wrapSubprocess}'s `terminate()` right
26
+ * before it SIGKILLs the child so `onExit` can distinguish the
27
+ * expected hard-kill from a crash/OOM/external signal. Only the
28
+ * latter is surfaced as a worker error.
29
+ */
30
+ intentionalExit: {
31
+ value: boolean;
32
+ };
33
+ }
34
+ /**
35
+ * Spawn the tiny-model worker as a subprocess. Exported for tests and the
36
+ * smoke probe; production callers go through {@link spawnTinyTitleWorker}
37
+ * which wraps the result in a {@link WorkerHandle}.
38
+ */
39
+ export declare function createTinyTitleSubprocess(): SpawnedSubprocess;
15
40
  export declare class TinyTitleClient {
16
41
  #private;
17
42
  onProgress(listener: (event: TinyTitleProgressEvent) => void): () => void;
@@ -30,3 +55,4 @@ export declare function shutdownTinyTitleClient(): Promise<void>;
30
55
  export declare function smokeTestTinyTitleWorker({ timeoutMs, }?: {
31
56
  timeoutMs?: number;
32
57
  }): Promise<void>;
58
+ export {};
@@ -34,8 +34,6 @@ export type TinyTitleWorkerInbound = {
34
34
  type: "download";
35
35
  id: string;
36
36
  modelKey: TinyLocalModelKey;
37
- } | {
38
- type: "close";
39
37
  };
40
38
  export type TinyTitleWorkerOutbound = {
41
39
  type: "pong";
@@ -64,11 +62,16 @@ export type TinyTitleWorkerOutbound = {
64
62
  level: "debug" | "warn" | "error";
65
63
  msg: string;
66
64
  meta?: Record<string, unknown>;
67
- } | {
68
- type: "closed";
69
65
  };
66
+ /**
67
+ * Wire transport between the parent (`TinyTitleClient`) and the tiny-model
68
+ * subprocess. The parent owns the subprocess lifecycle (graceful work, hard
69
+ * kill on shutdown); the protocol therefore carries no explicit close
70
+ * handshake — once the parent decides to terminate, it signals the OS to
71
+ * reap the child so `onnxruntime-node`'s NAPI finalizer never runs in any
72
+ * shared address space. See `title-client.ts` for the spawn/kill glue.
73
+ */
70
74
  export interface TinyTitleTransport {
71
75
  send(message: TinyTitleWorkerOutbound): void;
72
76
  onMessage(handler: (message: TinyTitleWorkerInbound) => void): () => void;
73
- close(): void;
74
77
  }
@@ -93,7 +93,7 @@ export declare class FindTool implements AgentTool<typeof findSchema, FindToolDe
93
93
  execute(_toolCallId: string, params: z.infer<typeof findSchema>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<FindToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<FindToolDetails>>;
94
94
  }
95
95
  interface FindRenderArgs {
96
- paths?: string[];
96
+ paths?: string | string[];
97
97
  limit?: number;
98
98
  }
99
99
  export declare const findToolRenderer: {
@@ -6,7 +6,9 @@ import type { Settings } from "../config/settings";
6
6
  import type { Skill } from "../extensibility/skills";
7
7
  import type { GoalModeState, GoalRuntime } from "../goals";
8
8
  import type { HindsightSessionState } from "../hindsight/state";
9
- import type { MnemosyneSessionState } from "../mnemosyne/state";
9
+ import type { LocalProtocolOptions } from "../internal-urls";
10
+ import type { MCPManager } from "../mcp";
11
+ import type { MnemopiSessionState } from "../mnemopi/state";
10
12
  import type { PlanModeState } from "../plan-mode/state";
11
13
  import { type AgentRegistry } from "../registry/agent-registry";
12
14
  import type { ArtifactManager } from "../session/artifacts";
@@ -107,8 +109,8 @@ export interface ToolSession {
107
109
  getSessionId?: () => string | null;
108
110
  /** Get Hindsight runtime state for this agent session. */
109
111
  getHindsightSessionState?: () => HindsightSessionState | undefined;
110
- /** Get Mnemosyne runtime state for this agent session. */
111
- getMnemosyneSessionState?: () => MnemosyneSessionState | undefined;
112
+ /** Get Mnemopi runtime state for this agent session. */
113
+ getMnemopiSessionState?: () => MnemopiSessionState | undefined;
112
114
  /** Agent identity used for IRC routing. Returns the registry id (e.g. "0-Main", "0-AuthLoader"). */
113
115
  getAgentId?: () => string | null;
114
116
  /** Look up a registered tool by name (used by the eval js backend's tool bridge). */
@@ -136,6 +138,10 @@ export interface ToolSession {
136
138
  modelRegistry?: import("../config/model-registry").ModelRegistry;
137
139
  /** Agent output manager for unique agent:// IDs across task invocations */
138
140
  agentOutputManager?: AgentOutputManager;
141
+ /** MCP manager visible to subagents without relying on the process-global singleton. */
142
+ mcpManager?: MCPManager;
143
+ /** Local protocol root to propagate to nested subagents and eval-created agents. */
144
+ localProtocolOptions?: LocalProtocolOptions;
139
145
  /** Settings instance for passing to subagents */
140
146
  settings: Settings;
141
147
  /** Plan mode state (if active) */
@@ -144,6 +150,16 @@ export interface ToolSession {
144
150
  getGoalModeState?: () => GoalModeState | undefined;
145
151
  /** Goal runtime for the active agent session. */
146
152
  getGoalRuntime?: () => GoalRuntime | undefined;
153
+ /** Get cumulative session usage statistics (input/output tokens, cost). */
154
+ getUsageStatistics?: () => import("../session/session-manager").UsageStatistics;
155
+ /** Current per-turn token budget {total, spent, hard} for the eval `budget` helper. */
156
+ getTurnBudget?: () => {
157
+ total: number | null;
158
+ spent: number;
159
+ hard: boolean;
160
+ };
161
+ /** Record output tokens consumed by an eval-spawned subagent toward the current turn budget. */
162
+ recordEvalSubagentUsage?: (output: number) => void;
147
163
  /** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
148
164
  getClientBridge?: () => ClientBridge | undefined;
149
165
  /** Get compact conversation context for subagents (excludes tool results, system prompts) */
@@ -32,7 +32,7 @@ export declare class MemoryEditTool implements AgentTool<typeof memoryEditSchema
32
32
  }, z.core.$strip>;
33
33
  readonly strict = true;
34
34
  readonly loadMode = "discoverable";
35
- readonly summary = "Update, forget, or invalidate Mnemosyne memories";
35
+ readonly summary = "Update, forget, or invalidate Mnemopi memories";
36
36
  constructor(session: ToolSession);
37
37
  static createIf(session: ToolSession): MemoryEditTool | null;
38
38
  execute(_id: string, params: MemoryEditParams): Promise<AgentToolResult>;
@@ -1,3 +1,4 @@
1
+ import { type LocalProtocolOptions } from "../internal-urls";
1
2
  export declare function expandTilde(filePath: string, home?: string): string;
2
3
  export declare function expandPath(filePath: string): string;
3
4
  /**
@@ -143,6 +144,12 @@ export interface ToolScopeOptions {
143
144
  surfaceExactFilePaths?: boolean;
144
145
  /** Extra hint appended to "Path not found" when stat fails and the user supplied multiple paths. */
145
146
  multipathStatHint?: string;
147
+ /** Calling session's settings — forwarded to the internal-URL router so caller-aware handlers (issue://, pr://) honor it. */
148
+ settings?: unknown;
149
+ /** Caller's abort signal — forwarded to the internal-URL router. */
150
+ signal?: AbortSignal;
151
+ /** Calling session's `local://` root mapping — pins resolutions to the calling session. */
152
+ localProtocolOptions?: LocalProtocolOptions;
146
153
  }
147
154
  export interface ToolScopeResolution {
148
155
  searchPath: string;
@@ -20,14 +20,14 @@ export interface OutputBlockOptions {
20
20
  */
21
21
  export declare function borderShimmerTick(): number;
22
22
  /**
23
- * Perimeter index of the moving segment's head for a box of inner width `W` and
24
- * height `H` at time `now`. The lap is split across the four edges in proportion
25
- * to their length (so the average speed is uniform) and each edge is eased, for a
26
- * deliberate, non-linear glide that slows at every corner. Position is derived
27
- * from the wall clock against a fixed lap duration, so a perimeter change (new
28
- * row / resize) shifts the head by at most a cell or two — no reset.
23
+ * Column of the travelling segment's center on the bottom edge for a box of
24
+ * inner width `W` at time `now`. The segment bounces left right left across
25
+ * the bottom border: a triangle wave over one full there-and-back cycle, eased
26
+ * per leg so it slows as it nears each wall before reversing. Position is
27
+ * derived from the wall clock against a fixed cycle, so a resize shifts the
28
+ * center proportionally — no reset.
29
29
  */
30
- export declare function borderSegmentHead(W: number, H: number, now: number): number;
30
+ export declare function borderSegmentHeadCol(W: number, now: number): number;
31
31
  export declare function renderOutputBlock(options: OutputBlockOptions, theme: Theme): string[];
32
32
  /**
33
33
  * Cached wrapper around `renderOutputBlock`.
@@ -26,7 +26,7 @@ console.log("Session with default auth storage and model registry");
26
26
 
27
27
  // Custom auth storage location
28
28
  const customAuthStorage = await AuthStorage.create("/tmp/my-app/agent.db");
29
- const customModelRegistry = new ModelRegistry(customAuthStorage, "/tmp/my-app/models.json");
29
+ const customModelRegistry = await ModelRegistry.create(customAuthStorage, "/tmp/my-app/models.json");
30
30
 
31
31
  await createAgentSession({
32
32
  sessionManager: SessionManager.inMemory(),
@@ -45,7 +45,7 @@ await createAgentSession({
45
45
  console.log("Session with runtime API key override");
46
46
 
47
47
  // No models.json - only built-in models
48
- const simpleRegistry = new ModelRegistry(authStorage); // null = no models.json
48
+ const simpleRegistry = await ModelRegistry.create(authStorage); // null = no models.json
49
49
  await createAgentSession({
50
50
  sessionManager: SessionManager.inMemory(),
51
51
  authStorage,
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.7.2",
4
+ "version": "15.7.5",
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",
@@ -35,7 +35,7 @@
35
35
  "check": "biome check . && bun run check:types",
36
36
  "check:types": "tsgo -p tsconfig.json --noEmit",
37
37
  "lint": "biome lint .",
38
- "test": "bun test",
38
+ "test": "bun test --parallel",
39
39
  "fix": "biome check --write --unsafe . && bun run format-prompts && bun run generate-docs-index",
40
40
  "fmt": "biome format --write . && bun run format-prompts",
41
41
  "format-prompts": "bun scripts/format-prompts.ts",
@@ -47,14 +47,14 @@
47
47
  "@agentclientprotocol/sdk": "0.22.1",
48
48
  "@babel/parser": "^7.29.7",
49
49
  "@mozilla/readability": "^0.6.0",
50
- "@oh-my-pi/hashline": "15.7.2",
51
- "@oh-my-pi/omp-stats": "15.7.2",
52
- "@oh-my-pi/pi-agent-core": "15.7.2",
53
- "@oh-my-pi/pi-ai": "15.7.2",
54
- "@oh-my-pi/pi-mnemosyne": "15.7.2",
55
- "@oh-my-pi/pi-natives": "15.7.2",
56
- "@oh-my-pi/pi-tui": "15.7.2",
57
- "@oh-my-pi/pi-utils": "15.7.2",
50
+ "@oh-my-pi/hashline": "15.7.5",
51
+ "@oh-my-pi/omp-stats": "15.7.5",
52
+ "@oh-my-pi/pi-agent-core": "15.7.5",
53
+ "@oh-my-pi/pi-ai": "15.7.5",
54
+ "@oh-my-pi/pi-mnemopi": "15.7.5",
55
+ "@oh-my-pi/pi-natives": "15.7.5",
56
+ "@oh-my-pi/pi-tui": "15.7.5",
57
+ "@oh-my-pi/pi-utils": "15.7.5",
58
58
  "@puppeteer/browsers": "^3.0.4",
59
59
  "@types/turndown": "5.0.6",
60
60
  "@xterm/headless": "^6.0.0",
@@ -56,7 +56,6 @@ async function main(): Promise<void> {
56
56
  "../stats/src/sync-worker.ts",
57
57
  "./src/tools/browser/tab-worker-entry.ts",
58
58
  "./src/eval/js/worker-entry.ts",
59
- "./src/tiny/worker.ts",
60
59
  // Legacy pi-* extension compat entrypoints served by
61
60
  // `legacy-pi-compat.ts`. These are reached via computed bunfs paths
62
61
  // (which `--compile`'s static analyzer cannot trace), so each must be