@oh-my-pi/pi-coding-agent 16.2.13 → 16.3.0

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 (166) hide show
  1. package/CHANGELOG.md +108 -7
  2. package/dist/cli.js +6033 -5982
  3. package/dist/types/advisor/config.d.ts +4 -2
  4. package/dist/types/collab/host.d.ts +16 -0
  5. package/dist/types/collab/protocol.d.ts +9 -3
  6. package/dist/types/commit/model-selection.d.ts +5 -0
  7. package/dist/types/config/model-resolver.d.ts +12 -10
  8. package/dist/types/config/settings-schema.d.ts +28 -5
  9. package/dist/types/edit/modes/patch.d.ts +11 -0
  10. package/dist/types/eval/agent-bridge.d.ts +5 -1
  11. package/dist/types/exec/bash-executor.d.ts +1 -0
  12. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +86 -2
  13. package/dist/types/extensibility/skills.d.ts +2 -1
  14. package/dist/types/mnemopi/state.d.ts +12 -0
  15. package/dist/types/modes/components/agent-hub.d.ts +9 -5
  16. package/dist/types/modes/components/status-line/component.test.d.ts +1 -0
  17. package/dist/types/modes/components/usage-row.d.ts +1 -1
  18. package/dist/types/modes/controllers/command-controller.d.ts +0 -1
  19. package/dist/types/modes/controllers/extension-ui-controller.d.ts +6 -0
  20. package/dist/types/modes/controllers/streaming-reveal.d.ts +17 -1
  21. package/dist/types/modes/controllers/tool-args-reveal.d.ts +30 -3
  22. package/dist/types/modes/interactive-mode.d.ts +12 -7
  23. package/dist/types/modes/rpc/rpc-client.d.ts +5 -0
  24. package/dist/types/modes/rpc/rpc-mode.d.ts +64 -1
  25. package/dist/types/modes/session-teardown.d.ts +63 -0
  26. package/dist/types/modes/session-teardown.test.d.ts +1 -0
  27. package/dist/types/modes/theme/theme.d.ts +9 -4
  28. package/dist/types/modes/types.d.ts +2 -3
  29. package/dist/types/modes/utils/copy-targets.d.ts +2 -0
  30. package/dist/types/plan-mode/approved-plan-prompt.test.d.ts +1 -0
  31. package/dist/types/sdk.d.ts +9 -0
  32. package/dist/types/session/agent-session.d.ts +46 -13
  33. package/dist/types/session/exit-diagnostics.d.ts +48 -0
  34. package/dist/types/session/session-loader.d.ts +5 -0
  35. package/dist/types/task/executor.d.ts +12 -5
  36. package/dist/types/task/parallel.d.ts +1 -0
  37. package/dist/types/task/render.test.d.ts +1 -0
  38. package/dist/types/thinking.d.ts +2 -0
  39. package/dist/types/tools/checkpoint.d.ts +8 -0
  40. package/dist/types/tools/eval-render.d.ts +1 -0
  41. package/dist/types/tools/fetch.d.ts +11 -1
  42. package/dist/types/tools/index.d.ts +3 -1
  43. package/dist/types/tools/irc.d.ts +1 -0
  44. package/dist/types/tools/output-meta.d.ts +7 -0
  45. package/dist/types/tools/output-schema-validator.d.ts +7 -4
  46. package/dist/types/tools/path-utils.d.ts +16 -0
  47. package/dist/types/tools/render-utils.d.ts +4 -1
  48. package/dist/types/utils/git.d.ts +47 -2
  49. package/dist/types/web/search/provider.d.ts +7 -0
  50. package/dist/types/web/search/types.d.ts +1 -1
  51. package/package.json +12 -12
  52. package/src/advisor/config.ts +4 -2
  53. package/src/cli/bench-cli.ts +7 -2
  54. package/src/collab/guest.ts +94 -5
  55. package/src/collab/host.ts +80 -3
  56. package/src/collab/protocol.ts +9 -2
  57. package/src/commit/model-selection.ts +8 -2
  58. package/src/config/keybindings.ts +3 -1
  59. package/src/config/model-discovery.ts +66 -2
  60. package/src/config/model-registry.ts +7 -3
  61. package/src/config/model-resolver.ts +51 -23
  62. package/src/config/settings-schema.ts +44 -14
  63. package/src/edit/hashline/diff.ts +13 -2
  64. package/src/edit/index.ts +58 -8
  65. package/src/edit/modes/patch.ts +53 -18
  66. package/src/edit/streaming.ts +7 -6
  67. package/src/eval/__tests__/agent-bridge.test.ts +57 -1
  68. package/src/eval/agent-bridge.ts +15 -5
  69. package/src/exec/bash-executor.ts +7 -12
  70. package/src/extensibility/legacy-pi-coding-agent-shim.ts +534 -16
  71. package/src/extensibility/skills.ts +29 -6
  72. package/src/goals/guided-setup.ts +4 -3
  73. package/src/internal-urls/docs-index.generated.txt +1 -1
  74. package/src/lsp/client.ts +11 -14
  75. package/src/main.ts +38 -10
  76. package/src/mnemopi/state.ts +43 -7
  77. package/src/modes/acp/acp-agent.ts +1 -1
  78. package/src/modes/components/agent-hub.ts +10 -2
  79. package/src/modes/components/agent-transcript-viewer.ts +39 -7
  80. package/src/modes/components/assistant-message.ts +16 -10
  81. package/src/modes/components/chat-transcript-builder.ts +11 -1
  82. package/src/modes/components/custom-editor.test.ts +20 -0
  83. package/src/modes/components/hook-selector.ts +44 -25
  84. package/src/modes/components/model-selector.ts +1 -1
  85. package/src/modes/components/status-line/component.test.ts +44 -0
  86. package/src/modes/components/status-line/component.ts +9 -1
  87. package/src/modes/components/status-line/segments.ts +3 -3
  88. package/src/modes/components/usage-row.ts +16 -2
  89. package/src/modes/controllers/command-controller.ts +6 -21
  90. package/src/modes/controllers/event-controller.ts +11 -9
  91. package/src/modes/controllers/extension-ui-controller.ts +84 -3
  92. package/src/modes/controllers/input-controller.ts +16 -13
  93. package/src/modes/controllers/selector-controller.ts +3 -8
  94. package/src/modes/controllers/streaming-reveal.ts +75 -53
  95. package/src/modes/controllers/tool-args-reveal.ts +340 -10
  96. package/src/modes/interactive-mode.ts +122 -79
  97. package/src/modes/rpc/rpc-client.ts +21 -0
  98. package/src/modes/rpc/rpc-mode.ts +197 -46
  99. package/src/modes/session-teardown.test.ts +219 -0
  100. package/src/modes/session-teardown.ts +82 -0
  101. package/src/modes/setup-wizard/scenes/theme.ts +2 -2
  102. package/src/modes/skill-command.ts +7 -20
  103. package/src/modes/theme/theme.ts +29 -15
  104. package/src/modes/types.ts +2 -3
  105. package/src/modes/utils/copy-targets.ts +12 -0
  106. package/src/modes/utils/ui-helpers.ts +19 -2
  107. package/src/plan-mode/approved-plan-prompt.test.ts +36 -0
  108. package/src/prompts/advisor/system.md +1 -1
  109. package/src/prompts/agents/tester.md +6 -2
  110. package/src/prompts/skills/autoload.md +8 -0
  111. package/src/prompts/skills/user-invocation.md +11 -0
  112. package/src/prompts/steering/parent-irc.md +5 -0
  113. package/src/prompts/system/irc-incoming.md +2 -0
  114. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  115. package/src/prompts/system/plan-mode-approved.md +7 -10
  116. package/src/prompts/system/plan-mode-compact-instructions.md +2 -1
  117. package/src/prompts/system/plan-mode-reference.md +3 -4
  118. package/src/prompts/system/rewind-report.md +6 -0
  119. package/src/prompts/system/subagent-system-prompt.md +3 -0
  120. package/src/prompts/tools/irc.md +2 -1
  121. package/src/prompts/tools/job.md +2 -2
  122. package/src/prompts/tools/rewind.md +3 -2
  123. package/src/prompts/tools/task.md +4 -0
  124. package/src/sdk.ts +18 -4
  125. package/src/session/agent-session.ts +660 -114
  126. package/src/session/exit-diagnostics.ts +202 -0
  127. package/src/session/session-context.ts +25 -13
  128. package/src/session/session-loader.ts +58 -25
  129. package/src/session/session-manager.ts +0 -1
  130. package/src/session/session-persistence.ts +30 -4
  131. package/src/session/settings-stream-fn.ts +14 -0
  132. package/src/slash-commands/builtin-registry.ts +35 -3
  133. package/src/system-prompt.ts +15 -1
  134. package/src/task/executor.ts +31 -8
  135. package/src/task/index.ts +31 -9
  136. package/src/task/isolation-runner.ts +18 -2
  137. package/src/task/parallel.ts +7 -2
  138. package/src/task/render.test.ts +121 -0
  139. package/src/task/render.ts +48 -2
  140. package/src/task/worktree.ts +12 -13
  141. package/src/task/yield-assembly.ts +8 -5
  142. package/src/thinking.ts +5 -0
  143. package/src/tools/ask.ts +188 -9
  144. package/src/tools/ast-edit.ts +7 -0
  145. package/src/tools/ast-grep.ts +11 -0
  146. package/src/tools/bash.ts +6 -30
  147. package/src/tools/checkpoint.ts +15 -1
  148. package/src/tools/eval-render.ts +15 -0
  149. package/src/tools/fetch.ts +82 -18
  150. package/src/tools/gh.ts +1 -1
  151. package/src/tools/grep.ts +45 -27
  152. package/src/tools/index.ts +3 -1
  153. package/src/tools/irc.ts +24 -9
  154. package/src/tools/output-meta.ts +50 -0
  155. package/src/tools/output-schema-validator.ts +152 -15
  156. package/src/tools/path-utils.ts +55 -10
  157. package/src/tools/read.ts +1 -1
  158. package/src/tools/render-utils.ts +5 -3
  159. package/src/tools/yield.ts +41 -1
  160. package/src/utils/commit-message-generator.ts +2 -2
  161. package/src/utils/git.ts +271 -29
  162. package/src/utils/thinking-display.ts +13 -0
  163. package/src/web/search/index.ts +9 -28
  164. package/src/web/search/provider.ts +26 -1
  165. package/src/web/search/providers/duckduckgo.ts +1 -1
  166. package/src/web/search/types.ts +5 -1
@@ -8,7 +8,6 @@ import type { ToolSession } from "../sdk";
8
8
  import type { AgentStorage } from "../session/agent-storage";
9
9
  import { type OutputMeta } from "./output-meta";
10
10
  import { type LineRange } from "./path-utils";
11
- export declare function isReadableUrlPath(value: string): boolean;
12
11
  export interface ParsedReadUrlTarget {
13
12
  path: string;
14
13
  raw: boolean;
@@ -56,9 +55,12 @@ export interface ReadUrlToolDetails {
56
55
  }
57
56
  interface ReadUrlCacheEntry {
58
57
  artifactId?: string;
58
+ artifactPath?: string;
59
+ contentPath?: string;
59
60
  details: ReadUrlToolDetails;
60
61
  image?: FetchImagePayload;
61
62
  output: string;
63
+ content: string;
62
64
  }
63
65
  export declare function loadReadUrlCacheEntry(session: ToolSession, params: {
64
66
  path: string;
@@ -67,6 +69,14 @@ export declare function loadReadUrlCacheEntry(session: ToolSession, params: {
67
69
  ensureArtifact?: boolean;
68
70
  preferCached?: boolean;
69
71
  }): Promise<ReadUrlCacheEntry>;
72
+ /** Materialize rendered URL body text to a local file for tools that require filesystem paths. */
73
+ export declare function materializeReadUrlToFile(session: ToolSession, params: {
74
+ path: string;
75
+ raw?: boolean;
76
+ }, signal?: AbortSignal): Promise<{
77
+ path: string;
78
+ details: ReadUrlToolDetails;
79
+ }>;
70
80
  export declare function executeReadUrl(session: ToolSession, params: {
71
81
  path: string;
72
82
  raw?: boolean;
@@ -24,7 +24,7 @@ import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-disc
24
24
  import type { EventBus } from "../utils/event-bus";
25
25
  import type { WorkspaceTree } from "../workspace-tree";
26
26
  import { type BuiltinToolName } from "./builtin-names";
27
- import { type CheckpointState } from "./checkpoint";
27
+ import { type CheckpointState, type CompletedRewindState } from "./checkpoint";
28
28
  import { type TodoPhase } from "./todo";
29
29
  export * from "../edit";
30
30
  export * from "../goals";
@@ -290,6 +290,8 @@ export interface ToolSession {
290
290
  getCheckpointState?: () => CheckpointState | undefined;
291
291
  /** Set or clear active checkpoint state. */
292
292
  setCheckpointState?: (state: CheckpointState | null) => void;
293
+ /** Get the most recent completed rewind, if this session just rewound a checkpoint. */
294
+ getLastCompletedRewind?: () => CompletedRewindState | undefined;
293
295
  /** Per-session snapshot store of file contents as last shown to the model
294
296
  * by `read`/`search`. Used by hashline anchor-stale recovery to
295
297
  * reconstruct the version the model authored anchors against when the
@@ -73,6 +73,7 @@ export declare class IrcTool implements AgentTool<typeof ircSchema, IrcDetails>
73
73
  peek?: boolean | undefined;
74
74
  }, {}>;
75
75
  readonly strict = true;
76
+ readonly interruptible = true;
76
77
  readonly examples: readonly ToolExample<typeof ircSchema.infer>[];
77
78
  readonly loadMode = "discoverable";
78
79
  constructor(session: ToolSession);
@@ -157,6 +157,13 @@ export declare class OutputMetaBuilder {
157
157
  /** Create a new OutputMetaBuilder. */
158
158
  export declare function outputMeta(): OutputMetaBuilder;
159
159
  export declare function formatFullOutputReference(artifactId: string): string;
160
+ /** Remove the trailing bash raw-output artifact footer while preserving its artifact id. */
161
+ export declare function stripRawOutputArtifactNotice(text: string): {
162
+ text: string;
163
+ artifactId?: string;
164
+ };
165
+ /** Remove a trailing generated output notice when metadata is unavailable. */
166
+ export declare function stripGeneratedOutputNotice(text: string): string;
160
167
  export declare function formatTruncationMetaNotice(truncation: TruncationMeta): string;
161
168
  /**
162
169
  * Format styled artifact reference with warning color and brackets.
@@ -18,12 +18,15 @@ export interface OutputValidator {
18
18
  * Per-label validators for incremental yields (`type: ["<label>"]`). Each entry validates the
19
19
  * `data` payload of a single section against the matching top-level property's sub-schema —
20
20
  * array-typed properties (e.g. `findings`) use the items schema since each yield contributes
21
- * one element, while scalar properties use the property schema directly. Unknown labels (not
22
- * top-level properties) have no entry and skip per-call validation. Lets the yield tool give
23
- * the model retry feedback on a section as soon as it arrives, instead of deferring every
24
- * mismatch to the parent's post-mortem `schema_violation`.
21
+ * one element, while scalar properties use the property schema directly.
25
22
  */
26
23
  readonly validateSection: ReadonlyMap<string, (value: unknown) => JsonSchemaValidationResult>;
24
+ /** Whether top-level schema closure makes unknown incremental yield labels invalid. */
25
+ readonly rejectUnknownSections: boolean;
26
+ /** Finite top-level section labels declared directly by the schema. Pattern-backed labels are accepted via `isKnownSection`. */
27
+ readonly knownSectionLabels: readonly string[];
28
+ /** Whether an incremental yield label is accepted by the top-level schema declaration. */
29
+ isKnownSection(label: string): boolean;
27
30
  }
28
31
  export interface BuildOutputValidatorResult {
29
32
  /** Present when the schema produced a usable validator (i.e. constraining schemas). Absent for missing/unconstrained schemas. */
@@ -89,6 +89,13 @@ export declare function pathTargetsSsh(path: string): boolean;
89
89
  * side-effecting `InternalUrlRouter.resolve` in tools that need a local file.
90
90
  */
91
91
  export declare function isSshUrl(path: string): boolean;
92
+ /**
93
+ * True when the read tool's URL parser (`parseReadUrlTarget` in fetch.ts) would
94
+ * recognize this path as a readable external URL: a strict `http(s)://`, a
95
+ * collapsed `http(s):/host` (Node path normalization folds `//` → `/`), or a
96
+ * scheme-less `www.` spelling. Keep in sync with `parseReadUrlTarget`.
97
+ */
98
+ export declare function isReadableUrlPath(value: string): boolean;
92
99
  /**
93
100
  * Resolve a path relative to the given cwd.
94
101
  * Handles ~ expansion and absolute paths.
@@ -198,6 +205,13 @@ export declare function partitionExistingPaths(items: string[], cwd: string, spl
198
205
  basePath: string;
199
206
  }): Promise<PartitionedPaths>;
200
207
  export declare function resolveReadPath(filePath: string, cwd: string): string;
208
+ /** Local file materialized from a readable external URL for shared tool-scope resolution. */
209
+ export interface ResolvedExternalSearchUrl {
210
+ /** Absolute or cwd-relative file path to search. */
211
+ sourcePath: string;
212
+ /** True when the materialized file must not mint editable anchors. */
213
+ immutable?: boolean;
214
+ }
201
215
  export interface ToolScopeOptions {
202
216
  rawPaths: string[];
203
217
  cwd: string;
@@ -221,6 +235,8 @@ export interface ToolScopeOptions {
221
235
  localProtocolOptions?: LocalProtocolOptions;
222
236
  /** Calling session's loaded skills — lets skill:// resolve without process-global state. */
223
237
  skills?: readonly Skill[];
238
+ /** Materialize readable external URLs to local text files before scope derivation. */
239
+ resolveExternalUrl?: (rawPath: string) => Promise<ResolvedExternalSearchUrl | undefined>;
224
240
  }
225
241
  export interface ToolScopeResolution {
226
242
  searchPath: string;
@@ -96,12 +96,15 @@ export declare function previewWindowRows(): number;
96
96
  * (ctrl+o) uncaps it.
97
97
  *
98
98
  * `prefix` (raw, e.g. a dim tree gutter) is prepended to the marker line so
99
- * nested previews stay aligned.
99
+ * nested previews stay aligned. `expandHint: false` drops the "ctrl+o: Expand"
100
+ * suffix for callers that cap even inside the expanded view (task recent
101
+ * output), where the hint would point the wrong way.
100
102
  */
101
103
  export declare function capPreviewLines(lines: string[], theme: Theme, options?: {
102
104
  max?: number;
103
105
  expanded?: boolean;
104
106
  prefix?: string;
107
+ expandHint?: boolean;
105
108
  }): string[];
106
109
  export declare function formatMeta(meta: string[], theme: Theme): string;
107
110
  export declare function formatErrorMessage(message: string | undefined, theme: Theme): string;
@@ -88,6 +88,7 @@ export interface PatchOptions {
88
88
  readonly cached?: boolean;
89
89
  readonly check?: boolean;
90
90
  readonly env?: Record<string, string | undefined>;
91
+ readonly reverse?: boolean;
91
92
  readonly threeWay?: boolean;
92
93
  readonly signal?: AbortSignal;
93
94
  }
@@ -98,10 +99,17 @@ export interface RestoreOptions {
98
99
  readonly staged?: boolean;
99
100
  readonly worktree?: boolean;
100
101
  }
102
+ export interface FetchOptions {
103
+ readonly signal?: AbortSignal;
104
+ /** Deadline for the network transfer. Defaults to {@link GIT_NETWORK_TIMEOUT_MS}. */
105
+ readonly timeoutMs?: number;
106
+ }
101
107
  export interface CloneOptions {
102
108
  readonly ref?: string;
103
109
  readonly sha?: string;
104
110
  readonly signal?: AbortSignal;
111
+ /** Deadline for the network transfer. Defaults to {@link GIT_NETWORK_TIMEOUT_MS}. */
112
+ readonly timeoutMs?: number;
105
113
  }
106
114
  interface GitHeadBase extends GitRepository {
107
115
  headContent: string;
@@ -128,11 +136,24 @@ export declare class GitCommandError extends Error {
128
136
  readonly result: GitCommandResult;
129
137
  constructor(args: readonly string[], result: GitCommandResult);
130
138
  }
139
+ /** Default deadline for git and gh subprocesses spawned by the coding agent. */
140
+ export declare const GIT_COMMAND_TIMEOUT_MS: number;
141
+ /**
142
+ * Default deadline for git subprocesses that perform network transfers
143
+ * (`clone`/`fetch`). Large-repo transfers legitimately outlive
144
+ * {@link GIT_COMMAND_TIMEOUT_MS}, so they get a wider deadline; local plumbing
145
+ * commands keep the short one.
146
+ */
147
+ export declare const GIT_NETWORK_TIMEOUT_MS: number;
148
+ /** Maximum captured stdout or stderr bytes retained from git and gh subprocesses. */
149
+ export declare const GIT_COMMAND_OUTPUT_LIMIT_BYTES: number;
131
150
  interface CommandOptions {
132
151
  readonly env?: Record<string, string | undefined>;
152
+ readonly maxOutputBytes?: number;
133
153
  readonly readOnly?: boolean;
134
154
  readonly signal?: AbortSignal;
135
155
  readonly stdin?: string | Uint8Array | ArrayBuffer | SharedArrayBuffer;
156
+ readonly timeoutMs?: number;
136
157
  }
137
158
  /**
138
159
  * Serialize an async block that mutates a git repository against other
@@ -192,8 +213,8 @@ export declare function commit(cwd: string, message: string, options?: CommitOpt
192
213
  export declare function push(cwd: string, options?: PushOptions): Promise<void>;
193
214
  /** Checkout a ref. */
194
215
  export declare function checkout(cwd: string, ref: string, signal?: AbortSignal): Promise<void>;
195
- /** Fetch a specific refspec from a remote. */
196
- export declare function fetch(cwd: string, remote: string, source: string, target: string, signal?: AbortSignal): Promise<void>;
216
+ /** Fetch a specific refspec from a remote. Network transfer: defaults to the {@link GIT_NETWORK_TIMEOUT_MS} deadline. */
217
+ export declare function fetch(cwd: string, remote: string, source: string, target: string, options?: FetchOptions): Promise<void>;
197
218
  /** Read a tree-ish into the index. */
198
219
  export declare function readTree(cwd: string, treeish: string, options?: Pick<CommandOptions, "env" | "signal">): Promise<void>;
199
220
  /** Write the current index as a tree and return its object id. */
@@ -311,6 +332,28 @@ export declare const stash: {
311
332
  pop(cwd: string, options?: {
312
333
  index?: boolean;
313
334
  }): Promise<void>;
335
+ /**
336
+ * Return the working-tree patch that `stash@{0}` would apply, in a form
337
+ * that `git apply --check` can consume. Empty string when no stash entry
338
+ * exists or the stash contains no diffable working-tree changes.
339
+ */
340
+ showPatch(cwd: string): Promise<string>;
341
+ /** Return untracked paths stored in the top stash entry. */
342
+ untrackedFiles(cwd: string): Promise<string[]>;
343
+ /**
344
+ * Attempt to restore the top stash entry. On success returns `true` and
345
+ * git drops the stash entry. On conflict returns `false`, leaves the stash
346
+ * entry preserved for manual resolution, and guarantees the failed restore
347
+ * leaves no unmerged index entries or partially-restored untracked files.
348
+ *
349
+ * The historical raw `pop` catches the failure in a `finally` block and
350
+ * only logs — it leaves `.git/index` with stage 1/2/3 unmerged entries
351
+ * that survive indefinitely, corrupting every subsequent overlay-isolated
352
+ * task that reads through this repo's `.git/`. See issue #4175.
353
+ */
354
+ tryPop(cwd: string, options?: {
355
+ index?: boolean;
356
+ }): Promise<boolean>;
314
357
  };
315
358
  export declare function clone(url: string, targetDir: string, options?: CloneOptions): Promise<void>;
316
359
  export declare function restore(cwd: string, options?: RestoreOptions): Promise<void>;
@@ -328,6 +371,8 @@ export declare function reset(cwd: string, options?: {
328
371
  }): Promise<void>;
329
372
  export declare function clean(cwd: string, options?: {
330
373
  ignoredOnly?: boolean;
374
+ includeIgnored?: boolean;
375
+ literalPathspecs?: boolean;
331
376
  paths?: readonly string[];
332
377
  signal?: AbortSignal;
333
378
  }): Promise<void>;
@@ -6,6 +6,13 @@ export { SearchProvider } from "./providers/base";
6
6
  export { SEARCH_PROVIDER_ORDER } from "./types";
7
7
  /** Cheap, sync metadata accessor — never triggers a provider load. */
8
8
  export declare function getSearchProviderLabel(id: SearchProviderId): string;
9
+ /** Format one provider failure for the user-facing fallback summary. */
10
+ export declare function formatSearchProviderFailure(error: unknown, provider: Pick<SearchProvider, "id" | "label">): string;
11
+ /** Format the ordered provider fallback failures for terminal/tool output. */
12
+ export declare function formatSearchProviderFailures(failures: readonly {
13
+ provider: Pick<SearchProvider, "id" | "label">;
14
+ error: unknown;
15
+ }[]): string;
9
16
  /**
10
17
  * Resolve and cache a provider instance. First call for a given id loads the
11
18
  * underlying module; subsequent calls return the cached singleton.
@@ -78,7 +78,7 @@ export declare const SEARCH_PROVIDER_OPTIONS: readonly [{
78
78
  }, {
79
79
  readonly value: "duckduckgo";
80
80
  readonly label: "DuckDuckGo";
81
- readonly description: "Scrapes the DuckDuckGo HTML frontend (no API key)";
81
+ readonly description: "Credential-free best-effort fallback; may be bot-challenged on datacenter/shared-egress IPs";
82
82
  }];
83
83
  /** Supported web search providers (every option except `auto`). */
84
84
  export type SearchProviderId = Exclude<(typeof SEARCH_PROVIDER_OPTIONS)[number]["value"], "auto">;
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": "16.2.13",
4
+ "version": "16.3.0",
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",
@@ -56,17 +56,17 @@
56
56
  "@agentclientprotocol/sdk": "0.25.0",
57
57
  "@babel/parser": "^7.29.7",
58
58
  "@mozilla/readability": "^0.6.0",
59
- "@oh-my-pi/hashline": "16.2.13",
60
- "@oh-my-pi/omp-stats": "16.2.13",
61
- "@oh-my-pi/pi-agent-core": "16.2.13",
62
- "@oh-my-pi/pi-ai": "16.2.13",
63
- "@oh-my-pi/pi-catalog": "16.2.13",
64
- "@oh-my-pi/pi-mnemopi": "16.2.13",
65
- "@oh-my-pi/pi-natives": "16.2.13",
66
- "@oh-my-pi/pi-tui": "16.2.13",
67
- "@oh-my-pi/pi-utils": "16.2.13",
68
- "@oh-my-pi/pi-wire": "16.2.13",
69
- "@oh-my-pi/snapcompact": "16.2.13",
59
+ "@oh-my-pi/hashline": "16.3.0",
60
+ "@oh-my-pi/omp-stats": "16.3.0",
61
+ "@oh-my-pi/pi-agent-core": "16.3.0",
62
+ "@oh-my-pi/pi-ai": "16.3.0",
63
+ "@oh-my-pi/pi-catalog": "16.3.0",
64
+ "@oh-my-pi/pi-mnemopi": "16.3.0",
65
+ "@oh-my-pi/pi-natives": "16.3.0",
66
+ "@oh-my-pi/pi-tui": "16.3.0",
67
+ "@oh-my-pi/pi-utils": "16.3.0",
68
+ "@oh-my-pi/pi-wire": "16.3.0",
69
+ "@oh-my-pi/snapcompact": "16.3.0",
70
70
  "@opentelemetry/api": "^1.9.1",
71
71
  "@opentelemetry/context-async-hooks": "^2.7.1",
72
72
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -10,8 +10,10 @@ import { collectConfigCandidates } from "./watchdog";
10
10
  /**
11
11
  * One advisor declared in a `WATCHDOG.yml` file. `model` is a model selector
12
12
  * with an optional `:level` thinking suffix (e.g. `x-ai/grok-code-fast:high`),
13
- * resolved exactly like any other model override; `tools` is a subset of the
14
- * read-only investigative tool names (default: all). `instructions` is the
13
+ * resolved exactly like any other model override; `tools` is a subset of
14
+ * `BUILTIN_TOOL_NAMES` any built-in name, including mutating tools such as
15
+ * `edit`/`write`/`bash` (the advisor is a full agent). Omitted or empty falls
16
+ * back to the default `read`/`grep`/`glob` subset. `instructions` is the
15
17
  * advisor's specialization, appended to the shared baseline.
16
18
  */
17
19
  export interface AdvisorConfig {
@@ -30,7 +30,12 @@ import { buildServiceTierByFamily, serviceTierForAllFamilies, serviceTierSetting
30
30
  import { Settings } from "../config/settings";
31
31
  import benchPrompt from "../prompts/bench.md" with { type: "text" };
32
32
  import { discoverAuthStorage, loadCliExtensionProviders } from "../sdk";
33
- import { resolveThinkingLevelForModel, shouldDisableReasoning, toReasoningEffort } from "../thinking";
33
+ import {
34
+ concreteThinkingLevel,
35
+ resolveThinkingLevelForModel,
36
+ shouldDisableReasoning,
37
+ toReasoningEffort,
38
+ } from "../thinking";
34
39
 
35
40
  const DEFAULT_RUNS = 10;
36
41
  const DEFAULT_PAR = 4;
@@ -477,7 +482,7 @@ function resolveBenchModels(
477
482
  resolved.push({
478
483
  selector,
479
484
  model,
480
- thinking: resolveThinkingLevelForModel(model, result.thinkingLevel),
485
+ thinking: resolveThinkingLevelForModel(model, concreteThinkingLevel(result.thinkingLevel)),
481
486
  });
482
487
  }
483
488
  if (errors.length > 0) {
@@ -9,6 +9,9 @@
9
9
  * ecosystem is mirrored too: agent snapshots populate a local AgentRegistry
10
10
  * (Agent Hub), EventBus traffic (observer HUD) is republished, and hub
11
11
  * actions (chat/kill/revive/transcript reads) round-trip over the wire.
12
+ * Host ask dialogs (`ui-request` select/editor) present through the same
13
+ * hook selector/editor seam and answer with `ui-response`; `ui-request-end`
14
+ * dismisses a pending presentation without responding.
12
15
  * Everything renders through the same components, so ctrl+o, theming, and
13
16
  * transcript behavior are native by construction.
14
17
  */
@@ -16,7 +19,7 @@ import * as path from "node:path";
16
19
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
17
20
  import type { ImageContent } from "@oh-my-pi/pi-ai";
18
21
  import { getConfigRootDir, logger } from "@oh-my-pi/pi-utils";
19
- import type { AgentHubRemote } from "../modes/components/agent-hub";
22
+ import type { AgentHubRemote, AgentHubRemoteTranscript } from "../modes/components/agent-hub";
20
23
  import type { InteractiveModeContext } from "../modes/types";
21
24
  import { AgentRegistry } from "../registry/agent-registry";
22
25
  import type { AgentSessionEvent } from "../session/agent-session";
@@ -30,6 +33,7 @@ import {
30
33
  COLLAB_PROTO,
31
34
  type CollabFrame,
32
35
  type CollabSessionState,
36
+ type CollabUiRequest,
33
37
  parseCollabLink,
34
38
  } from "./protocol";
35
39
  import { CollabSocket } from "./relay-client";
@@ -155,7 +159,9 @@ export class CollabGuestLink {
155
159
  readonly agentRegistry = new AgentRegistry();
156
160
  /** Per-agent `hasSessionFile` from the last snapshot; gates remote transcript fetches. */
157
161
  #agentHasTranscript = new Map<string, boolean>();
158
- #pendingTranscripts = new Map<number, (r: { text: string; newSize: number } | null) => void>();
162
+ #pendingTranscripts = new Map<number, (r: AgentHubRemoteTranscript | null) => void>();
163
+ /** Host `ui-request`s presented (or queued) locally, keyed by reqId; aborting dismisses. */
164
+ #pendingUiRequests = new Map<number, AbortController>();
159
165
  #nextReqId = 1;
160
166
  readonly #hubRemote: AgentHubRemote = {
161
167
  chat: (id, text) => {
@@ -176,7 +182,7 @@ export class CollabGuestLink {
176
182
  return Promise.resolve(null);
177
183
  }
178
184
  const reqId = this.#nextReqId++;
179
- const { promise, resolve } = Promise.withResolvers<{ text: string; newSize: number } | null>();
185
+ const { promise, resolve } = Promise.withResolvers<AgentHubRemoteTranscript | null>();
180
186
  const timer = setTimeout(() => {
181
187
  this.#pendingTranscripts.delete(reqId);
182
188
  resolve(null);
@@ -269,6 +275,16 @@ export class CollabGuestLink {
269
275
  }
270
276
  return;
271
277
  }
278
+ if (frame.t === "error" && !this.#welcomed && !this.#left) {
279
+ // Pre-welcome errors are the host's targeted reply to our
280
+ // hello (e.g. protocol mismatch): no welcome will follow.
281
+ // Fail the join with the host's message instead of hanging
282
+ // until the welcome timeout.
283
+ this.#clearWelcomeTimer();
284
+ if (joined) this.#ctx.showError(`Collab host: ${frame.message}`);
285
+ else firstWelcome.reject(new Error(frame.message));
286
+ return;
287
+ }
272
288
  if (!this.#welcomed || this.#left) return;
273
289
  this.#applyFrame(frame);
274
290
  })
@@ -480,11 +496,17 @@ export class CollabGuestLink {
480
496
  this.#applyAgentSnapshots(frame.agents);
481
497
  this.#ctx.syncRunningSubagentBadge();
482
498
  break;
499
+ case "ui-request":
500
+ this.#presentUiRequest(frame.request);
501
+ break;
502
+ case "ui-request-end":
503
+ this.#endUiRequest(frame.reqId);
504
+ break;
483
505
  case "transcript": {
484
506
  const resolve = this.#pendingTranscripts.get(frame.reqId);
485
507
  if (resolve) {
486
508
  this.#pendingTranscripts.delete(frame.reqId);
487
- resolve(frame.error ? null : { text: frame.text, newSize: frame.newSize });
509
+ resolve({ text: frame.text, newSize: frame.newSize, error: frame.error });
488
510
  }
489
511
  break;
490
512
  }
@@ -591,7 +613,74 @@ export class CollabGuestLink {
591
613
  this.#pendingTranscripts.clear();
592
614
  }
593
615
 
616
+ /**
617
+ * Surface a host `ui-request` (ask select/editor) through the local
618
+ * hook-dialog seam. The dialog settles on user submit/cancel — both send a
619
+ * `ui-response` (cancel carries `value: undefined`, mirroring the web
620
+ * client's Cancel button) — or when {@link #endUiRequest} aborts it because
621
+ * the host settled the request elsewhere; that path must NOT respond.
622
+ */
623
+ #presentUiRequest(request: CollabUiRequest): void {
624
+ // The host only targets writable peers; drop defensively on a read-only link.
625
+ if (this.#readOnly || this.#pendingUiRequests.has(request.reqId)) return;
626
+ const abort = new AbortController();
627
+ this.#pendingUiRequests.set(request.reqId, abort);
628
+ const dialog =
629
+ request.kind === "select"
630
+ ? this.#ctx.showHookSelector(request.title, request.options, {
631
+ signal: abort.signal,
632
+ initialIndex: request.initialIndex,
633
+ selectionMarker: request.selectionMarker,
634
+ checkedIndices: request.checkedIndices,
635
+ markableCount: request.markableCount,
636
+ helpText: request.helpText,
637
+ })
638
+ : this.#ctx.showHookEditor(request.title, request.prefill, { signal: abort.signal });
639
+ dialog
640
+ .then(value => {
641
+ // Identity check: only the presentation that still owns the reqId
642
+ // may respond. An abort from #endUiRequest / #clearUiRequests
643
+ // removes (or replaces, on resync replay) the entry before this
644
+ // microtask runs, so a dismissed dialog stays silent.
645
+ if (this.#pendingUiRequests.get(request.reqId) !== abort) return;
646
+ this.#pendingUiRequests.delete(request.reqId);
647
+ this.#socket?.send({ t: "ui-response", reqId: request.reqId, value });
648
+ })
649
+ .catch(err => {
650
+ if (this.#pendingUiRequests.get(request.reqId) === abort) {
651
+ this.#pendingUiRequests.delete(request.reqId);
652
+ }
653
+ logger.warn("collab guest ui-request presentation failed", {
654
+ reqId: request.reqId,
655
+ error: String(err),
656
+ });
657
+ });
658
+ }
659
+
660
+ /** Host settled the request (answered elsewhere or aborted): dismiss without responding. */
661
+ #endUiRequest(reqId: number): void {
662
+ const abort = this.#pendingUiRequests.get(reqId);
663
+ if (!abort) return;
664
+ this.#pendingUiRequests.delete(reqId);
665
+ abort.abort();
666
+ }
667
+
668
+ /**
669
+ * Dismiss every locally presented `ui-request` without responding: on
670
+ * resync the host replays the ones still pending, and on leave they are no
671
+ * longer ours to answer. Queued dialogs abort before the presented one
672
+ * (reverse insertion order) so settling the active dialog cannot flash the
673
+ * next queued one onto the surface first.
674
+ */
675
+ #clearUiRequests(): void {
676
+ if (this.#pendingUiRequests.size === 0) return;
677
+ const aborts = [...this.#pendingUiRequests.values()];
678
+ this.#pendingUiRequests.clear();
679
+ for (const abort of aborts.reverse()) abort.abort();
680
+ }
681
+
594
682
  #clearTransientUi(): void {
683
+ this.#clearUiRequests();
595
684
  this.#ctx.statusContainer.clear();
596
685
  this.#ctx.pendingMessagesContainer.clear();
597
686
  this.#ctx.compactionQueuedMessages = [];
@@ -625,7 +714,7 @@ export class CollabGuestLink {
625
714
  setSessionTerminalTitle(this.#ctx.sessionManager.getSessionName(), this.#ctx.sessionManager.getCwd());
626
715
  this.#ctx.statusLine.invalidate();
627
716
  this.#ctx.statusLine.resetActiveTime();
628
- this.#ctx.updateEditorTopBorder();
717
+ this.#ctx.ui.requestRender();
629
718
  this.#ctx.updateEditorBorderColor();
630
719
  this.#ctx.renderInitialMessages({ clearTerminalHistory: true });
631
720
  await this.#ctx.reloadTodos();