@oh-my-pi/pi-coding-agent 16.1.9 → 16.1.11

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 (81) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/cli.js +3455 -3430
  3. package/dist/types/config/settings-schema.d.ts +10 -0
  4. package/dist/types/edit/streaming.d.ts +5 -5
  5. package/dist/types/export/html/index.d.ts +31 -2
  6. package/dist/types/export/html/web-palette.d.ts +117 -0
  7. package/dist/types/hindsight/content.d.ts +7 -0
  8. package/dist/types/hindsight/transcript.d.ts +1 -1
  9. package/dist/types/modes/components/hook-editor.d.ts +2 -1
  10. package/dist/types/modes/controllers/selector-controller.d.ts +0 -1
  11. package/dist/types/modes/interactive-mode.d.ts +5 -0
  12. package/dist/types/modes/types.d.ts +17 -0
  13. package/dist/types/modes/utils/keybinding-matchers.d.ts +13 -0
  14. package/dist/types/sdk.d.ts +1 -0
  15. package/dist/types/session/agent-session.d.ts +4 -0
  16. package/dist/types/session/session-context.d.ts +2 -0
  17. package/dist/types/session/session-entries.d.ts +6 -0
  18. package/dist/types/session/session-manager.d.ts +2 -1
  19. package/dist/types/system-prompt.d.ts +2 -0
  20. package/dist/types/tools/acp-bridge.d.ts +29 -0
  21. package/dist/types/tools/browser/aria/aria-snapshot.d.ts +39 -0
  22. package/dist/types/tools/browser/cmux/cmux-tab.d.ts +10 -0
  23. package/dist/types/tools/browser/tab-worker.d.ts +20 -0
  24. package/dist/types/tools/browser.d.ts +1 -0
  25. package/dist/types/tools/find.d.ts +1 -2
  26. package/dist/types/tools/write.d.ts +1 -2
  27. package/dist/types/utils/image-loading.d.ts +4 -3
  28. package/dist/types/utils/jj.d.ts +25 -0
  29. package/dist/types/utils/title-generator.d.ts +0 -2
  30. package/package.json +12 -12
  31. package/scripts/generate-aria-snapshot.ts +134 -0
  32. package/scripts/generate-share-viewer.ts +4 -2
  33. package/src/autoresearch/git.ts +12 -0
  34. package/src/config/settings-schema.ts +12 -0
  35. package/src/discovery/opencode.ts +47 -4
  36. package/src/edit/hashline/filesystem.ts +8 -0
  37. package/src/edit/modes/patch.ts +18 -2
  38. package/src/edit/modes/replace.ts +13 -10
  39. package/src/edit/streaming.ts +5 -5
  40. package/src/export/html/index.ts +50 -8
  41. package/src/export/html/web-palette.ts +142 -0
  42. package/src/hindsight/backend.ts +4 -4
  43. package/src/hindsight/content.ts +17 -1
  44. package/src/hindsight/transcript.ts +2 -2
  45. package/src/internal-urls/docs-index.generated.txt +1 -1
  46. package/src/modes/components/agent-dashboard.ts +8 -8
  47. package/src/modes/components/hook-editor.ts +13 -10
  48. package/src/modes/components/session-selector.ts +3 -0
  49. package/src/modes/controllers/event-controller.ts +29 -5
  50. package/src/modes/controllers/extension-ui-controller.ts +6 -2
  51. package/src/modes/controllers/selector-controller.ts +3 -5
  52. package/src/modes/interactive-mode.ts +69 -29
  53. package/src/modes/theme/dark.json +1 -1
  54. package/src/modes/types.ts +18 -0
  55. package/src/modes/utils/keybinding-matchers.ts +36 -1
  56. package/src/modes/utils/ui-helpers.ts +1 -0
  57. package/src/prompts/system/project-prompt.md +2 -0
  58. package/src/prompts/system/system-prompt.md +0 -1
  59. package/src/prompts/tools/browser.md +5 -2
  60. package/src/sdk.ts +20 -2
  61. package/src/session/agent-session.ts +38 -14
  62. package/src/session/session-context.ts +5 -0
  63. package/src/session/session-entries.ts +6 -0
  64. package/src/session/session-manager.ts +3 -1
  65. package/src/system-prompt.ts +15 -3
  66. package/src/task/worktree.ts +12 -4
  67. package/src/thinking.ts +1 -1
  68. package/src/tools/acp-bridge.ts +66 -0
  69. package/src/tools/bash.ts +3 -3
  70. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  71. package/src/tools/browser/aria/aria-snapshot.ts +103 -0
  72. package/src/tools/browser/cmux/cmux-tab.ts +73 -1
  73. package/src/tools/browser/tab-worker.ts +242 -47
  74. package/src/tools/browser.ts +5 -0
  75. package/src/tools/find.ts +1 -2
  76. package/src/tools/index.ts +1 -1
  77. package/src/tools/write.ts +3 -27
  78. package/src/utils/git.ts +3 -2
  79. package/src/utils/image-loading.ts +6 -3
  80. package/src/utils/jj.ts +47 -0
  81. package/src/utils/title-generator.ts +31 -99
@@ -912,6 +912,16 @@ export declare const SETTINGS_SCHEMA: {
912
912
  readonly description: "Surface the active model identifier in the system prompt so the agent knows which model it is";
913
913
  };
914
914
  };
915
+ readonly includeWorkspaceTree: {
916
+ readonly type: "boolean";
917
+ readonly default: false;
918
+ readonly ui: {
919
+ readonly tab: "model";
920
+ readonly group: "Prompt";
921
+ readonly label: "Include Workspace Tree";
922
+ readonly description: "Render the workspace directory tree in the system prompt. WARNING: This can bust prompt caching across sessions when files are modified.";
923
+ };
924
+ };
915
925
  readonly personality: {
916
926
  readonly type: "enum";
917
927
  readonly values: readonly ["default", "friendly", "pragmatic", "none"];
@@ -63,12 +63,12 @@ export interface EditStreamingStrategy<Args = unknown> {
63
63
  }
64
64
  /**
65
65
  * Given an edits array parsed from partial JSON, drop the last entry when the
66
- * corresponding object in `partialJson` does not yet end with a closed `}`.
66
+ * corresponding object in `partialJson` has not yet closed with `}`.
67
67
  *
68
- * This guards against `partial-json` silently coercing truncated tails like
69
- * `"write":nu` / `"write":nul` into `{ write: null }`, which would make the
70
- * last entry render a spurious null-write error until the value finishes
71
- * streaming.
68
+ * The streaming parser materializes a trailing edit object from the fields seen
69
+ * so far before its closing `}` arrives, so an unfinished last entry can render
70
+ * as a (partial) edit mid-stream. Dropping it until the object closes keeps the
71
+ * preview from showing an incomplete edit.
72
72
  */
73
73
  export declare function dropIncompleteLastEdit<T>(edits: readonly T[], partialJson: string | undefined, listKey: string): T[];
74
74
  export declare const EDIT_MODE_STRATEGIES: Record<EditMode, EditStreamingStrategy<unknown>>;
@@ -5,12 +5,41 @@ import { SessionManager } from "../../session/session-manager";
5
5
  export declare function getTemplate(): string;
6
6
  export interface ExportOptions {
7
7
  outputPath?: string;
8
+ /**
9
+ * Which color palette the export ships with.
10
+ * - `"web"` (default) — the omp brand identity (collab-web pink/purple),
11
+ * so public HTML exports and the `/s/<id>` share viewer match the live
12
+ * `my.omp.sh` client. See `web-palette.ts`.
13
+ * - `"theme"` — derive from `themeName` (or the active TUI theme), preserving
14
+ * the pre-15.12 behavior where an export mirrored the user's terminal.
15
+ */
16
+ palette?: "web" | "theme";
17
+ /**
18
+ * TUI theme to derive colors from when `palette: "theme"`. Ignored for the
19
+ * default `"web"` palette. Resolves to the active TUI theme when omitted.
20
+ */
8
21
  themeName?: string;
9
22
  /** Embed subagent session transcripts found next to the session file (default true). */
10
23
  includeSubSessions?: boolean;
11
24
  }
12
- /** Generate CSS custom properties for theme. Exported for the share-viewer build script. */
13
- export declare function generateThemeVars(themeName?: string): Promise<string>;
25
+ /**
26
+ * Generate CSS custom properties for the export `:root`.
27
+ *
28
+ * Two call shapes:
29
+ * • `generateThemeVars("web" | "theme", themeName?)` — explicit palette.
30
+ * `"web"` (the default for public artifacts) returns the fixed omp brand
31
+ * palette from `web-palette.ts` — collab-web pink/purple identity, shared
32
+ * with the live `my.omp.sh` client, so exports and the share viewer render
33
+ * identically to it. `"theme"` derives from the TUI theme via
34
+ * `getResolvedThemeColors(themeName)` plus the three
35
+ * `export.{pageBg,cardBg,infoBg}` surface overrides.
36
+ * • `generateThemeVars(themeName)` — legacy single-arg form: derive from the
37
+ * named TUI theme. Kept so existing callers (and the theme-islight test)
38
+ * keep working; equivalent to `generateThemeVars("theme", themeName)`.
39
+ *
40
+ * Exported for the share-viewer build script.
41
+ */
42
+ export declare function generateThemeVars(palette?: "web" | "theme" | (string & {}), themeName?: string): Promise<string>;
14
43
  /** Embedded subagent session transcript, keyed by slash-joined agent path in `SessionData.subSessions`. */
15
44
  export interface SubSession {
16
45
  /** Bare agent id (session file stem), e.g. "ToolAsk". */
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Web/export palette — the omp brand identity shared by the collab-web live
3
+ * client (`my.omp.sh/`) and every public HTML export / share viewer (`/s/<id>`).
4
+ *
5
+ * Why this exists separately from `modes/theme/dark.json`: the `dark` theme is
6
+ * the **default TUI theme** — its amber accent (`#febc38`) drives the terminal
7
+ * status line, syntax highlighting, thinking levels, and bash/python mode
8
+ * colors for every omp user. The public web artifacts want the collab-web
9
+ * pink/purple identity instead, so they pin this palette rather than inheriting
10
+ * the TUI's. Editing `dark.json` to repurpose it for the web would repaint
11
+ * every terminal; this file keeps the two surfaces decoupled.
12
+ *
13
+ * Token layout — emitted as CSS custom properties on `:root`:
14
+ * • Legacy export names consumed by `template.css` / `template.js`
15
+ * (`--text`, `--body-bg`, `--container-bg`, `--info-bg`, `--accent`,
16
+ * `--border`, `--success`, `--error`, `--warning`, `--muted`, `--dim`,
17
+ * `--borderAccent`, `--selectedBg`, `--userMessageBg`, `--customMessageBg`,
18
+ * `--customMessageLabel`, `--mdHeading`, `--mdLink`, `--mdCode`,
19
+ * `--mdListBullet`, `--toolOutput`, `--thinkingText`, syntax*, …).
20
+ * • collab-web-native aliases consumed by the `tv-` tool-render bridge
21
+ * (`tool-render.css`: `var(--bg-inset, …)`, `var(--fg, …)`, …) so embedded
22
+ * tool cards resolve to the *real* collab-web tokens and render
23
+ * pixel-identical to the live client.
24
+ *
25
+ * Alpha-bearing tokens (`--border`, `--ring`, `--accent-muted`, …) keep their
26
+ * `oklch(… / N%)` form — flattening them to opaque hex would produce harsh
27
+ * white borders and non-matching translucent focus rings. Opaque surfaces are
28
+ * sRGB hex (the collab-web `tokens.css` OKLCH dark-theme tokens converted via
29
+ * the standard OKLab→linear-sRGB→gamma path); if the live client palette
30
+ * changes, regenerate those from there.
31
+ */
32
+ export declare const WEB_EXPORT_PALETTE: {
33
+ readonly "--bg": "#0f0b14";
34
+ readonly "--bg-raised": "#16111c";
35
+ readonly "--bg-inset": "#09060c";
36
+ readonly "--bg-overlay": "#211b28";
37
+ readonly "--fg": "#e6e3ea";
38
+ readonly "--fg-muted": "#a49faa";
39
+ readonly "--fg-faint": "#6e6974";
40
+ readonly "--accent": "#ed4abf";
41
+ readonly "--accent-muted": "oklch(0.674 0.23 341 / 18%)";
42
+ readonly "--ok": "#68ca80";
43
+ readonly "--err": "#f05653";
44
+ readonly "--warn": "#e4b33f";
45
+ readonly "--ring": "oklch(0.817 0.112 205 / 70%)";
46
+ readonly "--font-mono": 'ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", Menlo, Consolas, monospace';
47
+ readonly "--body-bg": "#0f0b14";
48
+ readonly "--container-bg": "#16111c";
49
+ readonly "--info-bg": "#09060c";
50
+ readonly "--text": "#e6e3ea";
51
+ readonly "--muted": "#a49faa";
52
+ readonly "--dim": "#6e6974";
53
+ readonly "--thinkingText": "#a49faa";
54
+ readonly "--border": "oklch(1 0 0 / 9%)";
55
+ readonly "--borderMuted": "oklch(1 0 0 / 6%)";
56
+ readonly "--borderAccent": "#945ff9";
57
+ readonly "--selectedBg": "#2d2535";
58
+ readonly "--success": "#68ca80";
59
+ readonly "--error": "#f05653";
60
+ readonly "--warning": "#e4b33f";
61
+ readonly "--userMessageBg": "oklch(0.674 0.23 341 / 6%)";
62
+ readonly "--userMessageText": "#e6e3ea";
63
+ readonly "--customMessageBg": "#211b28";
64
+ readonly "--customMessageText": "#a49faa";
65
+ readonly "--customMessageLabel": "#b281d6";
66
+ readonly "--toolPendingBg": "#16111c";
67
+ readonly "--toolSuccessBg": "#09060c";
68
+ readonly "--toolErrorBg": "oklch(0.66 0.19 25 / 14%)";
69
+ readonly "--toolTitle": "#e6e3ea";
70
+ readonly "--toolOutput": "#a49faa";
71
+ readonly "--mdHeading": "#ed4abf";
72
+ readonly "--mdLink": "#5ad8e5";
73
+ readonly "--mdLinkUrl": "#6e6974";
74
+ readonly "--mdCode": "#e6e3ea";
75
+ readonly "--mdCodeBlock": "#e6e3ea";
76
+ readonly "--mdCodeBlockBorder": "oklch(1 0 0 / 9%)";
77
+ readonly "--mdQuote": "#a49faa";
78
+ readonly "--mdQuoteBorder": "oklch(1 0 0 / 13%)";
79
+ readonly "--mdHr": "oklch(1 0 0 / 9%)";
80
+ readonly "--mdListBullet": "#ed4abf";
81
+ readonly "--toolDiffAdded": "#68ca80";
82
+ readonly "--toolDiffRemoved": "#f05653";
83
+ readonly "--toolDiffContext": "#6e6974";
84
+ readonly "--syntaxComment": "#6e6974";
85
+ readonly "--syntaxKeyword": "#945ff9";
86
+ readonly "--syntaxFunction": "#e4b33f";
87
+ readonly "--syntaxVariable": "#5ad8e5";
88
+ readonly "--syntaxString": "#68ca80";
89
+ readonly "--syntaxNumber": "#ed4abf";
90
+ readonly "--syntaxType": "#b281d6";
91
+ readonly "--syntaxOperator": "#e6e3ea";
92
+ readonly "--syntaxPunctuation": "#a49faa";
93
+ readonly "--thinkingOff": "#6e6974";
94
+ readonly "--thinkingMinimal": "#6e6974";
95
+ readonly "--thinkingLow": "#945ff9";
96
+ readonly "--thinkingMedium": "#b281d6";
97
+ readonly "--thinkingHigh": "#ed4abf";
98
+ readonly "--thinkingXhigh": "#e4b33f";
99
+ readonly "--bashMode": "#5ad8e5";
100
+ readonly "--pythonMode": "#e4b33f";
101
+ readonly "--statusLineBg": "#0f0b14";
102
+ readonly "--statusLineSep": "#6e6974";
103
+ readonly "--statusLineModel": "#ed4abf";
104
+ readonly "--statusLinePath": "#5ad8e5";
105
+ readonly "--statusLineGitClean": "#68ca80";
106
+ readonly "--statusLineGitDirty": "#e4b33f";
107
+ readonly "--statusLineContext": "#a49faa";
108
+ readonly "--statusLineSpend": "#5ad8e5";
109
+ readonly "--statusLineStaged": "#68ca80";
110
+ readonly "--statusLineDirty": "#e4b33f";
111
+ readonly "--statusLineUntracked": "#945ff9";
112
+ readonly "--statusLineOutput": "#b281d6";
113
+ readonly "--statusLineCost": "#b281d6";
114
+ readonly "--statusLineSubagents": "#ed4abf";
115
+ };
116
+ /** Serialize the palette as `--key: value;` declarations for `:root { … }`. */
117
+ export declare function webExportThemeVars(): string;
@@ -28,6 +28,13 @@ export interface RecallResultLike {
28
28
  * retaining.
29
29
  */
30
30
  export declare function stripMemoryTags(content: string): string;
31
+ /**
32
+ * True when `content` carries at least one letter or digit. Used by retain
33
+ * and recall paths to drop placeholder assistant turns ("." / "..." / pure
34
+ * whitespace) that would otherwise pollute the bank and waste tokens on
35
+ * embeddings with no semantic content.
36
+ */
37
+ export declare function hasSubstantiveContent(content: string): boolean;
31
38
  /** Format recall results into a bullet list for context injection. */
32
39
  export declare function formatMemories(results: RecallResultLike[]): string;
33
40
  /** Format current UTC time for the recall preamble. */
@@ -7,7 +7,7 @@
7
7
  * surviving message's `TextContent` parts are joined with newlines.
8
8
  */
9
9
  import type { SessionEntry } from "../session/session-entries";
10
- import type { HindsightMessage } from "./content";
10
+ import { type HindsightMessage } from "./content";
11
11
  export interface ReadonlySessionManagerLike {
12
12
  getEntries(): SessionEntry[];
13
13
  }
@@ -3,7 +3,8 @@
3
3
  * Supports Ctrl+G for external editor.
4
4
  *
5
5
  * Two modes:
6
- * - Default (hook): Enter inserts newline, Ctrl+Enter submits, bordered popup
6
+ * - Default (hook): Enter inserts newline, the `app.message.followUp` chord
7
+ * (Ctrl+Q / Ctrl+Enter) submits, bordered popup
7
8
  * - Prompt-style (ask): Enter submits, Shift+Enter inserts newline, legacy ask chrome
8
9
  */
9
10
  import { Container, type TUI } from "@oh-my-pi/pi-tui";
@@ -1,7 +1,6 @@
1
1
  import type { Component } from "@oh-my-pi/pi-tui";
2
2
  import type { InteractiveModeContext } from "../../modes/types";
3
3
  import type { SessionObserverRegistry } from "../session-observer-registry";
4
- export declare function formatTemporaryModelStatus(modelLabel: string, roleSelectorHint?: string): string;
5
4
  export declare class SelectorController {
6
5
  #private;
7
6
  private ctx;
@@ -95,6 +95,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
95
95
  hookWidgetContainerBelow: Container;
96
96
  statusLine: StatusLineComponent;
97
97
  isInitialized: boolean;
98
+ initialChatRendered: boolean;
98
99
  isBashMode: boolean;
99
100
  toolOutputExpanded: boolean;
100
101
  todoExpanded: boolean;
@@ -183,6 +184,10 @@ export declare class InteractiveMode implements InteractiveModeContext {
183
184
  withLocalSubmission<T>(text: string, fn: () => Promise<T>, options?: {
184
185
  imageCount?: number;
185
186
  }): Promise<T>;
187
+ clearOptimisticUserMessage(): void;
188
+ replaceOptimisticUserMessage(message: AgentMessage, options?: {
189
+ imageLinks?: readonly (string | undefined)[];
190
+ }): void;
186
191
  startPendingSubmission(input: {
187
192
  text: string;
188
193
  images?: ImageContent[];
@@ -118,6 +118,17 @@ export interface InteractiveModeContext {
118
118
  eventController: EventController;
119
119
  eventBus?: EventBus;
120
120
  isInitialized: boolean;
121
+ /**
122
+ * `true` once `renderInitialMessages` has rendered the session transcript
123
+ * into `chatContainer` at least once.
124
+ *
125
+ * Extension chat-rebuilds (`ExtensionUiController.#applyCustomMessageDisplay`)
126
+ * are gated on this: rebuilding before the initial render would plant a
127
+ * session-derived component into the chat that `renderInitialMessages` then
128
+ * both re-renders from session entries AND re-appends via
129
+ * `preserveExistingChat`, duplicating the message (issue #1955).
130
+ */
131
+ initialChatRendered: boolean;
121
132
  isBashMode: boolean;
122
133
  toolOutputExpanded: boolean;
123
134
  todoExpanded: boolean;
@@ -237,6 +248,12 @@ export interface InteractiveModeContext {
237
248
  withLocalSubmission<T>(text: string, fn: () => Promise<T>, options?: {
238
249
  imageCount?: number;
239
250
  }): Promise<T>;
251
+ /** Clears bookkeeping for an optimistic local user message once the matching session event arrives. */
252
+ clearOptimisticUserMessage(): void;
253
+ /** Replaces the raw optimistic user render with the canonical message emitted by the session. */
254
+ replaceOptimisticUserMessage(message: AgentMessage, options?: {
255
+ imageLinks?: readonly (string | undefined)[];
256
+ }): void;
240
257
  isKnownSlashCommand(text: string): boolean;
241
258
  addMessageToChat(message: AgentMessage, options?: {
242
259
  populateHistory?: boolean;
@@ -17,3 +17,16 @@ export declare function matchesSelectPageUp(data: string): boolean;
17
17
  /** Match the generic selector page-down keybinding. */
18
18
  export declare function matchesSelectPageDown(data: string): boolean;
19
19
  export declare function matchesAppExternalEditor(data: string): boolean;
20
+ /**
21
+ * Match the "submit multi-line text input" keybinding (`app.message.followUp`).
22
+ *
23
+ * Used by forms where plain Enter inserts a newline and a modified-Enter chord
24
+ * submits — the main editor's follow-up handler, the agent dashboard's new-agent
25
+ * description, and the hook editor's hook-style mode. The keybinding defaults to
26
+ * `["ctrl+q", "ctrl+enter"]` so Windows Terminal (which can't deliver a distinct
27
+ * Ctrl+Enter event; #1903) still has a working chord without user remapping.
28
+ *
29
+ * Also recognizes modifier-tagged LF as Ctrl+Enter only when Ctrl+Enter is an
30
+ * effective follow-up binding.
31
+ */
32
+ export declare function matchesAppFollowUp(data: string): boolean;
@@ -318,6 +318,7 @@ export interface BuildSystemPromptOptions {
318
318
  customPrompt?: string;
319
319
  appendPrompt?: string;
320
320
  inlineToolDescriptors?: boolean;
321
+ includeWorkspaceTree?: boolean;
321
322
  }
322
323
  /**
323
324
  * Build the default provider-facing system prompt blocks.
@@ -156,6 +156,8 @@ export interface AgentSessionConfig {
156
156
  modelRegistry: ModelRegistry;
157
157
  /** Tool registry for LSP and settings */
158
158
  toolRegistry?: Map<string, AgentTool>;
159
+ /** Tool names whose current registry entry is still the built-in implementation. */
160
+ builtInToolNames?: Iterable<string>;
159
161
  /** Current session pre-LLM message transform pipeline */
160
162
  transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => AgentMessage[] | Promise<AgentMessage[]>;
161
163
  /** Provider payload hook used by the active session request path */
@@ -511,6 +513,8 @@ export declare class AgentSession {
511
513
  * Get a tool by name from the registry.
512
514
  */
513
515
  getToolByName(name: string): AgentTool | undefined;
516
+ /** True when the current registry entry for `name` came from a built-in factory. */
517
+ hasBuiltInTool(name: string): boolean;
514
518
  /**
515
519
  * Get all configured tool names (built-in via --tools or default, plus custom tools).
516
520
  */
@@ -4,6 +4,8 @@ import { type CompactionEntry, type SessionEntry } from "./session-entries";
4
4
  export interface SessionContext {
5
5
  messages: AgentMessage[];
6
6
  thinkingLevel?: string;
7
+ /** Configured thinking selector (`"auto"` or a concrete level) from the latest change. */
8
+ configuredThinkingLevel?: string;
7
9
  serviceTier?: ServiceTier;
8
10
  /** Model roles: { default: "provider/modelId", small: "provider/modelId", ... } */
9
11
  models: Record<string, string>;
@@ -30,6 +30,12 @@ export interface SessionMessageEntry extends SessionEntryBase {
30
30
  export interface ThinkingLevelChangeEntry extends SessionEntryBase {
31
31
  type: "thinking_level_change";
32
32
  thinkingLevel?: string | null;
33
+ /**
34
+ * The user-configured selector at the time of this change: `"auto"` when auto
35
+ * mode was active, otherwise the concrete level. Absent on entries written
36
+ * before auto-mode persistence existed; readers fall back to `thinkingLevel`.
37
+ */
38
+ configured?: string | null;
33
39
  }
34
40
  export interface ModelChangeEntry extends SessionEntryBase {
35
41
  type: "model_change";
@@ -138,7 +138,8 @@ export declare class SessionManager {
138
138
  * top-level entries via appendCompaction()/branchWithSummary().
139
139
  */
140
140
  appendMessage(message: Message | CustomMessage | HookMessage | BashExecutionMessage | PythonExecutionMessage | FileMentionMessage): string;
141
- appendThinkingLevelChange(thinkingLevel?: string): string;
141
+ /** Append a thinking level change as child of current leaf, then advance leaf. Returns entry id. */
142
+ appendThinkingLevelChange(thinkingLevel?: string, configured?: string): string;
142
143
  appendServiceTierChange(serviceTier: ServiceTier | null): string;
143
144
  appendModeChange(mode: string, data?: Record<string, unknown>): string;
144
145
  /**
@@ -110,6 +110,8 @@ export interface BuildSystemPromptOptions {
110
110
  model?: string;
111
111
  /** Personality preset rendered into the default system prompt. "none" omits the block. Default: "default" */
112
112
  personality?: Personality;
113
+ /** Whether to include the workspace directory tree in the system prompt. Default: false */
114
+ includeWorkspaceTree?: boolean;
113
115
  }
114
116
  /** Result of building provider-facing system prompt messages. */
115
117
  export interface BuildSystemPromptResult {
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared ACP client bridge routing for file-write sites.
3
+ *
4
+ * When an ACP client (e.g. Zed) advertises the `fs.writeTextFile` capability,
5
+ * all write-mode tools must route through it so the editor's open buffer is
6
+ * updated immediately. Internal artifacts ('/Users/theo/.omp/agent/sessions/-Projects-oh-my-pi/2026-06-10T09-11-41-506Z_019eb0cd-3ec2-7000-92aa-1b82aa4d78f0/local' plan files, other scheme
7
+ * URLs) are always written directly to disk — those are OMP-owned and should
8
+ * never be pushed into the editor.
9
+ */
10
+ import type { ToolSession } from ".";
11
+ /**
12
+ * Return `true` when an ACP client bridge write is appropriate for this path.
13
+ *
14
+ * Returns `false` for internal-URL paths (e.g. `'/Users/theo/.omp/agent/sessions/-Projects-oh-my-pi/2026-06-10T09-11-41-506Z_019eb0cd-3ec2-7000-92aa-1b82aa4d78f0/local/PLAN.md'`) and for the
15
+ * active plan file while plan mode is enabled — both are OMP-internal artifacts
16
+ * that must stay off the editor's buffer.
17
+ */
18
+ export declare function shouldRouteWriteThroughBridge(session: ToolSession, requestedPath: string, absolutePath: string): boolean;
19
+ /**
20
+ * Try to route a file write through the ACP client bridge.
21
+ *
22
+ * Performs the full guard check, bridge call (wrapped in {@link ToolError}),
23
+ * FS-scan cache invalidation, and session mutation-version bump.
24
+ *
25
+ * Returns `true` when the bridge was used and the caller must skip the
26
+ * writethrough path. Returns `false` when the bridge is unavailable or the
27
+ * path should not be routed through it.
28
+ */
29
+ export declare function routeWriteThroughBridge(session: ToolSession, requestedPath: string, absolutePath: string, content: string): Promise<boolean>;
@@ -0,0 +1,39 @@
1
+ import type { ElementHandle, Page } from "puppeteer-core";
2
+ export interface AriaSnapshotOptions {
3
+ /** Maximum tree depth to render. */
4
+ depth?: number;
5
+ /** Append `[box=x,y,w,h]` bounding boxes to each node. */
6
+ boxes?: boolean;
7
+ }
8
+ /**
9
+ * Capture a Playwright-format ARIA snapshot of `root` (or the whole document when
10
+ * null). Always runs in `ai` mode so every node carries a `[ref=eN]` id; resolve
11
+ * those to elements with {@link resolveAriaRefHandle}. Ids are renumbered from e1
12
+ * on each call and remain valid until the next snapshot.
13
+ */
14
+ export declare function captureAriaSnapshot(page: Page, root: ElementHandle | null, options?: AriaSnapshotOptions): Promise<string>;
15
+ /**
16
+ * Resolve a `[ref=eN]` id from the latest snapshot to a live `ElementHandle`, or
17
+ * null when the ref no longer matches any element. Runs in the main world so it
18
+ * sees the `_ariaRef` expandos the snapshot wrote.
19
+ */
20
+ export declare function resolveAriaRefHandle(page: Page, ref: string): Promise<ElementHandle | null>;
21
+ /**
22
+ * Recognize the explicit `[ref=eN]` selector forms and return the bare ref id,
23
+ * else null. Accepts `aria-ref=e5` (Playwright-MCP style), `aria-ref/e5`, and
24
+ * `ariaref/e5` — lets `tab.click("aria-ref=e5")` etc. act on snapshot refs. A
25
+ * bare `e5` is intentionally NOT a ref selector: the cmux backend already uses
26
+ * bare `eN`/`@eN` for its own observe ids, so requiring the prefix keeps action
27
+ * selectors meaning the same thing on both backends. (`tab.ref("e5")` still
28
+ * accepts a bare id directly.)
29
+ */
30
+ export declare function parseAriaRefSelector(selector: string): string | null;
31
+ /**
32
+ * Build a self-contained expression script that runs the vendored bundle in the
33
+ * page and returns the ARIA snapshot YAML. Used by the cmux backend, whose
34
+ * `browser.eval` RPC takes a script string and returns the completion value (it
35
+ * has no ElementHandle to pass in). The script resolves `selector` via
36
+ * `document.querySelector` in-page (CSS selectors only) or falls back to the
37
+ * whole document. Like the puppeteer path it installs nothing on `window`.
38
+ */
39
+ export declare function buildAriaSnapshotScript(selector: string | undefined, options?: AriaSnapshotOptions): string;
@@ -1,5 +1,6 @@
1
1
  import { JsRuntime } from "../../../eval/js/shared/runtime";
2
2
  import type { ToolSession } from "../../../sdk";
3
+ import { type AriaSnapshotOptions } from "../aria/aria-snapshot";
3
4
  import { type ReadableFormat } from "../readable";
4
5
  import type { Observation, ReadyInfo, RunResultOk, ScreenshotResult, SessionSnapshot } from "../tab-protocol";
5
6
  import type { CmuxSocketClient } from "./socket-client";
@@ -75,6 +76,8 @@ export declare class CmuxTab {
75
76
  timeoutMs?: number;
76
77
  }): Promise<void>;
77
78
  observe(opts?: ObserveOptions): Promise<Observation>;
79
+ ariaSnapshot(selector?: string, opts?: AriaSnapshotOptions): Promise<string>;
80
+ ref(id: string): Promise<CmuxElementHandle>;
78
81
  click(selector: string): Promise<void>;
79
82
  dblclick(selector: string): Promise<void>;
80
83
  hover(selector: string): Promise<void>;
@@ -90,6 +93,9 @@ export declare class CmuxTab {
90
93
  waitFor(selector: string, opts?: {
91
94
  timeout?: number;
92
95
  }): Promise<CmuxElementHandle>;
96
+ waitForSelector(selector: string, opts?: {
97
+ timeout?: number;
98
+ }): Promise<CmuxElementHandle>;
93
99
  evaluate<TResult, TArgs extends unknown[]>(fn: string | ((...args: TArgs) => TResult | Promise<TResult>), ...args: TArgs): Promise<TResult>;
94
100
  scrollIntoView(selector: string): Promise<void>;
95
101
  select(selector: string, ...values: string[]): Promise<string[]>;
@@ -98,6 +104,10 @@ export declare class CmuxTab {
98
104
  waitForUrl(pattern: string | RegExp, opts?: {
99
105
  timeout?: number;
100
106
  }): Promise<string>;
107
+ waitForNavigation(opts?: {
108
+ waitUntil?: WaitUntil;
109
+ timeout?: number;
110
+ }): Promise<null>;
101
111
  drag(from: DragTarget, to: DragTarget): Promise<void>;
102
112
  uploadFile(selector: string, ...filePaths: string[]): Promise<void>;
103
113
  waitForResponse(pattern: string | RegExp | ((response: CmuxResponse) => boolean | Promise<boolean>), opts?: {
@@ -11,12 +11,32 @@ declare global {
11
11
  elementFromPoint(x: number, y: number): Element | null;
12
12
  };
13
13
  }
14
+ export interface OpTimeouts {
15
+ /** Largest per-op deadline allowed — strictly below the cell budget. */
16
+ budgetBound: number;
17
+ /** Ceiling for quick page reads. */
18
+ quickOpMs: number;
19
+ /** Ceiling for interactive actions + default for waits. */
20
+ actionOpMs: number;
21
+ }
22
+ /** Resolve the per-op fail-fast ceilings for a given cell budget. */
23
+ export declare function resolveOpTimeouts(cellTimeoutMs: number): OpTimeouts;
24
+ /**
25
+ * Effective timeout for a wait helper (`waitFor*`). A positive explicit `{ timeout }` is
26
+ * honored but clamped to the cell budget so it still fails fast + named; raising the tool
27
+ * `timeout` raises that cap, so a longer budget stays meaningful. No `{ timeout }` → the
28
+ * action ceiling. Puppeteer's `{ timeout: 0 }` / `Infinity` ("disable") maps to the largest
29
+ * bounded wait (`budgetBound`) — the harness never permits an unbounded wait. Garbage input
30
+ * (negative, `NaN`) falls back to the action ceiling rather than the longest wait.
31
+ */
32
+ export declare function resolveWaitTimeout(cellTimeoutMs: number, explicit?: number): number;
14
33
  interface ScreenshotOptions {
15
34
  selector?: string;
16
35
  fullPage?: boolean;
17
36
  save?: string;
18
37
  silent?: boolean;
19
38
  }
39
+ export declare function normalizeSelector(selector: string): string;
20
40
  export interface InflightOp {
21
41
  label: string;
22
42
  startedAt: number;
@@ -4,6 +4,7 @@ import type { ToolSession } from "../sdk";
4
4
  import { type BrowserKindTag } from "./browser/registry";
5
5
  import type { Observation, ScreenshotResult } from "./browser/tab-protocol";
6
6
  import type { OutputMeta } from "./output-meta";
7
+ export { type AriaSnapshotOptions, buildAriaSnapshotScript, parseAriaRefSelector, } from "./browser/aria/aria-snapshot";
7
8
  export { cmuxSnapshotToObservation, mapWaitUntil, resolveCmuxKind, serializeEval } from "./browser/cmux/rpc";
8
9
  export { CmuxSocketClient } from "./browser/cmux/socket-client";
9
10
  export { extractReadableFromHtml, type ReadableFormat, type ReadableResult } from "./browser/readable";
@@ -60,8 +60,7 @@ export declare class FindTool implements AgentTool<typeof findSchema, FindToolDe
60
60
  private readonly session;
61
61
  readonly name = "find";
62
62
  readonly approval: "read";
63
- readonly summary = "Find files and directories matching a glob pattern";
64
- readonly loadMode = "discoverable";
63
+ readonly loadMode = "essential";
65
64
  readonly label = "Find";
66
65
  readonly description: string;
67
66
  readonly parameters: import("arktype/internal/variants/object.ts").ObjectType<{
@@ -40,8 +40,7 @@ export declare class WriteTool implements AgentTool<typeof writeSchema, WriteToo
40
40
  }, {}>;
41
41
  readonly strict = true;
42
42
  readonly concurrency = "exclusive";
43
- readonly loadMode = "discoverable";
44
- readonly summary = "Write content to a file (creates or overwrites)";
43
+ readonly loadMode = "essential";
45
44
  /** Stream matchers should see the real file content, not its JSON-escaped argument encoding. */
46
45
  matcherDigest(args: unknown): string | undefined;
47
46
  constructor(session: ToolSession);
@@ -4,9 +4,10 @@ export declare const MAX_IMAGE_INPUT_BYTES: number;
4
4
  export declare const SUPPORTED_INPUT_IMAGE_MIME_TYPES: Set<string>;
5
5
  /**
6
6
  * Ollama and its local-backend family decode image input through llama.cpp /
7
- * `stb_image`, which is compiled without WebP support, so a WebP upload fails
8
- * with an opaque HTTP 400. Detect those models so the resize pipeline encodes
9
- * to PNG/JPEG instead — the automatic equivalent of `OMP_NO_WEBP=1`.
7
+ * `stb_image`, which is compiled without WebP support. The first-party Codex
8
+ * Responses backend also rejects WebP `input_image.image_url` data URLs. Detect
9
+ * those models so the resize pipeline encodes to PNG/JPEG instead — the
10
+ * automatic equivalent of `OMP_NO_WEBP=1`.
10
11
  */
11
12
  export declare function modelLacksWebpSupport(model: Pick<Model, "provider" | "api" | "imageInputDecoder"> | undefined): boolean;
12
13
  /**
@@ -47,3 +47,28 @@ export declare const repo: {
47
47
  /** Check whether `cwd` is inside a Jujutsu repository. */
48
48
  is(cwd: string): Promise<boolean>;
49
49
  };
50
+ /**
51
+ * Detect a "pure" Jujutsu workspace — one where Git-mutating automation has
52
+ * no safe Git target. Invoking `git checkout -b`, `git worktree add`, or
53
+ * `git apply` against a pure jj workspace either fails outright (no `.git/`
54
+ * present) or mutates state that jj itself cannot reconcile.
55
+ *
56
+ * `cwd` is "pure jj" iff its nearest jj workspace ancestor is **closer than**
57
+ * its nearest Git checkout ancestor (or no Git checkout is present at all).
58
+ * Both lookups walk upward from `cwd`, so the deeper ancestor is the one the
59
+ * user is actually working inside.
60
+ *
61
+ * Returns:
62
+ * - `false` for plain Git checkouts (no jj metadata anywhere up the tree).
63
+ * - `false` for colocated jj-git workspaces — `jj git init --colocate` keeps
64
+ * `.jj/` and `.git/` at the same root.
65
+ * - `false` when a nested Git checkout (e.g. a vendored repo or fixture)
66
+ * lives **under** an outer jj workspace; Git automation targets the inner
67
+ * repo and never touches the surrounding jj tree.
68
+ * - `true` when jj is the deeper ancestor — either a standalone pure jj
69
+ * workspace, or a jj workspace nested under an unrelated outer Git
70
+ * checkout, where Git automation against the outer root would silently
71
+ * bypass jj.
72
+ * - `false` for directories backed by neither tool.
73
+ */
74
+ export declare function isPureJjRepo(cwd: string): Promise<boolean>;
@@ -1,8 +1,6 @@
1
1
  import { type Api, type Model } from "@oh-my-pi/pi-ai";
2
2
  import type { ModelRegistry } from "../config/model-registry";
3
3
  import type { Settings } from "../config/settings";
4
- export declare const TITLE_LOCAL_FALLBACK_DELAY_MS = 10000;
5
- export declare function raceFirstNonNull<T>(primary: Promise<T | null>, startFallback: () => Promise<T | null>, delayMs?: number, onPrimaryWinAfterFallback?: () => void): Promise<T | null>;
6
4
  /**
7
5
  * Generate a title for a session based on the first user message.
8
6
  *