@oh-my-pi/pi-coding-agent 13.16.0 → 13.16.2

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.
package/src/sdk.ts CHANGED
@@ -9,8 +9,17 @@ import {
9
9
  import type { Message, Model } from "@oh-my-pi/pi-ai";
10
10
 
11
11
  import { prewarmOpenAICodexResponses } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
12
+ import { SearchDb } from "@oh-my-pi/pi-natives";
12
13
  import type { Component } from "@oh-my-pi/pi-tui";
13
- import { $env, getAgentDbPath, getAgentDir, getProjectDir, logger, postmortem } from "@oh-my-pi/pi-utils";
14
+ import {
15
+ $env,
16
+ getAgentDbPath,
17
+ getAgentDir,
18
+ getProjectDir,
19
+ getSearchDbDir,
20
+ logger,
21
+ postmortem,
22
+ } from "@oh-my-pi/pi-utils";
14
23
  import chalk from "chalk";
15
24
  import { AsyncJobManager } from "./async";
16
25
  import { createAutoresearchExtension } from "./autoresearch";
@@ -131,6 +140,8 @@ export interface CreateAgentSessionOptions {
131
140
  authStorage?: AuthStorage;
132
141
  /** Model registry. Default: discoverModels(authStorage, agentDir) */
133
142
  modelRegistry?: ModelRegistry;
143
+ /** Shared native search DB for grep/glob/fuzzyFind-backed workflows. */
144
+ searchDb?: SearchDb;
134
145
 
135
146
  /** Model to use. Default: from settings, else first available */
136
147
  model?: Model;
@@ -381,6 +392,7 @@ function createCustomToolContext(ctx: ExtensionContext): CustomToolContext {
381
392
  sessionManager: ctx.sessionManager,
382
393
  modelRegistry: ctx.modelRegistry,
383
394
  model: ctx.model,
395
+ searchDb: ctx.searchDb,
384
396
  isIdle: ctx.isIdle,
385
397
  hasQueuedMessages: ctx.hasPendingMessages,
386
398
  abort: ctx.abort,
@@ -862,6 +874,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
862
874
  })
863
875
  : undefined;
864
876
 
877
+ const searchDb = options.searchDb ?? new SearchDb(getSearchDbDir(agentDir));
865
878
  const pendingActionStore = new PendingActionStore();
866
879
  const toolSession: ToolSession = {
867
880
  cwd,
@@ -911,6 +924,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
911
924
  modelRegistry,
912
925
  asyncJobManager,
913
926
  pendingActionStore,
927
+ searchDb,
914
928
  };
915
929
 
916
930
  // Initialize internal URL router for internal protocols (agent://, artifact://, memory://, skill://, rule://, mcp://, local://)
@@ -1173,6 +1187,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1173
1187
  sessionManager,
1174
1188
  modelRegistry,
1175
1189
  model: agent?.state.model,
1190
+ searchDb,
1176
1191
  isIdle: () => !session?.isStreaming,
1177
1192
  hasQueuedMessages: () => (session?.queuedMessageCount ?? 0) > 0,
1178
1193
  abort: () => session?.abort(),
@@ -1539,6 +1554,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1539
1554
  obfuscator,
1540
1555
  asyncJobManager,
1541
1556
  pendingActionStore,
1557
+ searchDb,
1542
1558
  });
1543
1559
 
1544
1560
  if (model?.api === "openai-codex-responses") {
@@ -50,6 +50,7 @@ import {
50
50
  modelsAreEqual,
51
51
  parseRateLimitReason,
52
52
  } from "@oh-my-pi/pi-ai";
53
+ import type { SearchDb } from "@oh-my-pi/pi-natives";
53
54
  import { abortableSleep, getAgentDbPath, isEnoent, logger } from "@oh-my-pi/pi-utils";
54
55
  import type { AsyncJob, AsyncJobManager } from "../async";
55
56
  import type { Rule } from "../capability/rule";
@@ -237,6 +238,8 @@ export interface AgentSessionConfig {
237
238
  obfuscator?: SecretObfuscator;
238
239
  /** Pending action store for preview/apply workflows */
239
240
  pendingActionStore?: PendingActionStore;
241
+ /** Shared native search DB for grep/glob/fuzzyFind-backed workflows. */
242
+ searchDb?: SearchDb;
240
243
  }
241
244
 
242
245
  /** Options for AgentSession.prompt() */
@@ -348,6 +351,7 @@ export class AgentSession {
348
351
  readonly agent: Agent;
349
352
  readonly sessionManager: SessionManager;
350
353
  readonly settings: Settings;
354
+ readonly searchDb: SearchDb | undefined;
351
355
 
352
356
  #asyncJobManager: AsyncJobManager | undefined = undefined;
353
357
  #scopedModels: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
@@ -462,6 +466,7 @@ export class AgentSession {
462
466
  this.agent = config.agent;
463
467
  this.sessionManager = config.sessionManager;
464
468
  this.settings = config.settings;
469
+ this.searchDb = config.searchDb;
465
470
  this.#asyncJobManager = config.asyncJobManager;
466
471
  this.#scopedModels = config.scopedModels ?? [];
467
472
  this.#thinkingLevel = config.thinkingLevel;
@@ -1888,6 +1893,7 @@ export class AgentSession {
1888
1893
  sessionManager: this.sessionManager,
1889
1894
  modelRegistry: this.#modelRegistry,
1890
1895
  model: this.model,
1896
+ searchDb: this.searchDb,
1891
1897
  isIdle: () => !this.isStreaming,
1892
1898
  hasQueuedMessages: () => this.queuedMessageCount > 0,
1893
1899
  abort: () => {
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import path from "node:path";
7
7
  import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
8
+ import type { SearchDb } from "@oh-my-pi/pi-natives";
8
9
  import { logger, untilAborted } from "@oh-my-pi/pi-utils";
9
10
  import type { TSchema } from "@sinclair/typebox";
10
11
  import Ajv, { type ValidateFunction } from "ajv";
@@ -147,6 +148,7 @@ export interface ExecutorOptions {
147
148
  mcpManager?: MCPManager;
148
149
  authStorage?: AuthStorage;
149
150
  modelRegistry?: ModelRegistry;
151
+ searchDb?: SearchDb;
150
152
  settings?: Settings;
151
153
  }
152
154
 
@@ -950,6 +952,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
950
952
  cwd: worktree ?? cwd,
951
953
  authStorage,
952
954
  modelRegistry,
955
+ searchDb: options.searchDb,
953
956
  settings: subagentSettings,
954
957
  model,
955
958
  thinkingLevel: effectiveThinkingLevel,
@@ -1042,6 +1045,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1042
1045
  },
1043
1046
  {
1044
1047
  getModel: () => session.model,
1048
+ getSearchDb: () => session.searchDb,
1045
1049
  isIdle: () => !session.isStreaming,
1046
1050
  abort: () => session.abort(),
1047
1051
  hasPendingMessages: () => session.queuedMessageCount > 0,
package/src/task/index.ts CHANGED
@@ -775,6 +775,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
775
775
  },
776
776
  authStorage: this.session.authStorage,
777
777
  modelRegistry: this.session.modelRegistry,
778
+ searchDb: this.session.searchDb,
778
779
  settings: this.session.settings,
779
780
  mcpManager: this.session.mcpManager,
780
781
  contextFiles,
@@ -828,6 +829,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
828
829
  },
829
830
  authStorage: this.session.authStorage,
830
831
  modelRegistry: this.session.modelRegistry,
832
+ searchDb: this.session.searchDb,
831
833
  settings: this.session.settings,
832
834
  mcpManager: this.session.mcpManager,
833
835
  contextFiles,
@@ -1,4 +1,4 @@
1
- import * as fs from "node:fs/promises";
1
+ import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { Readability } from "@mozilla/readability";
@@ -61,6 +61,54 @@ async function loadPuppeteer(): Promise<typeof Puppeteer> {
61
61
  }
62
62
  }
63
63
 
64
+ /**
65
+ * On NixOS, Puppeteer's bundled Chromium is a dynamically-linked FHS binary and
66
+ * cannot run as-is. Detect the platform and resolve a system-installed Chromium
67
+ * so `puppeteer.launch()` can use it instead of the bundled one.
68
+ *
69
+ * Detection order:
70
+ * 1. `chromium` on PATH
71
+ * 2. `chromium-browser` on PATH
72
+ * 3. ~/.nix-profile/bin/chromium (user profile)
73
+ * 4. /run/current-system/sw/bin/chromium (system profile)
74
+ *
75
+ * Returns `undefined` on non-NixOS systems or when no binary is found, which
76
+ * causes Puppeteer to fall back to its default resolution.
77
+ */
78
+ let _resolvedChromium: string | null | undefined; // undefined = unchecked; null = not found
79
+ function resolveSystemChromium(): string | undefined {
80
+ if (_resolvedChromium !== undefined) return _resolvedChromium ?? undefined;
81
+ try {
82
+ if (!fs.existsSync("/etc/NIXOS")) {
83
+ _resolvedChromium = null;
84
+ return undefined;
85
+ }
86
+ } catch {
87
+ _resolvedChromium = null;
88
+ return undefined;
89
+ }
90
+ const candidates = [
91
+ Bun.which("chromium"),
92
+ Bun.which("chromium-browser"),
93
+ path.join(os.homedir(), ".nix-profile/bin/chromium"),
94
+ "/run/current-system/sw/bin/chromium",
95
+ ];
96
+ for (const candidate of candidates) {
97
+ if (candidate) {
98
+ try {
99
+ if (fs.existsSync(candidate)) {
100
+ _resolvedChromium = candidate;
101
+ logger.debug("NixOS: using system Chromium", { path: candidate });
102
+ return candidate;
103
+ }
104
+ } catch {}
105
+ }
106
+ }
107
+ _resolvedChromium = null;
108
+ logger.debug("NixOS detected but no Chromium binary found; Puppeteer may fail to launch");
109
+ return undefined;
110
+ }
111
+
64
112
  const DEFAULT_VIEWPORT = { width: 1365, height: 768, deviceScaleFactor: 1.25 };
65
113
  const STEALTH_IGNORE_DEFAULT_ARGS = [
66
114
  "--disable-extensions",
@@ -548,6 +596,7 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
548
596
  this.#browser = await puppeteer.launch({
549
597
  headless: this.#currentHeadless,
550
598
  defaultViewport: this.#currentHeadless ? initialViewport : null,
599
+ executablePath: resolveSystemChromium(),
551
600
  args: launchArgs,
552
601
  ignoreDefaultArgs: [...STEALTH_IGNORE_DEFAULT_ARGS],
553
602
  });
@@ -1382,7 +1431,7 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
1382
1431
  } else {
1383
1432
  dest = path.join(os.tmpdir(), `omp-sshots-${Snowflake.next()}.png`);
1384
1433
  }
1385
- await fs.mkdir(path.dirname(dest), { recursive: true });
1434
+ await fs.promises.mkdir(path.dirname(dest), { recursive: true });
1386
1435
  // Full-res buffer when saving to a user-defined location; resized (API copy) for temp-only.
1387
1436
  const saveFullRes = !!(paramPath || screenshotDir);
1388
1437
  const savedBuffer = saveFullRes ? buffer : resized.buffer;
package/src/tools/find.ts CHANGED
@@ -261,6 +261,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
261
261
  gitignore: useGitignore,
262
262
  },
263
263
  onMatch,
264
+ this.session.searchDb,
264
265
  ),
265
266
  );
266
267
 
package/src/tools/grep.ts CHANGED
@@ -169,23 +169,27 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
169
169
  // Run grep
170
170
  let result: GrepResult;
171
171
  try {
172
- result = await grep({
173
- pattern: normalizedPattern,
174
- path: searchPath,
175
- glob: globFilter,
176
- type: type?.trim() || undefined,
177
- ignoreCase,
178
- multiline: effectiveMultiline,
179
- hidden: true,
180
- gitignore: useGitignore,
181
- cache: false,
182
- maxCount: internalLimit,
183
- offset: normalizedOffset > 0 ? normalizedOffset : undefined,
184
- contextBefore: normalizedContextBefore,
185
- contextAfter: normalizedContextAfter,
186
- maxColumns: DEFAULT_MAX_COLUMN,
187
- mode: effectiveOutputMode,
188
- });
172
+ result = await grep(
173
+ {
174
+ pattern: normalizedPattern,
175
+ path: searchPath,
176
+ glob: globFilter,
177
+ type: type?.trim() || undefined,
178
+ ignoreCase,
179
+ multiline: effectiveMultiline,
180
+ hidden: true,
181
+ gitignore: useGitignore,
182
+ cache: false,
183
+ maxCount: internalLimit,
184
+ offset: normalizedOffset > 0 ? normalizedOffset : undefined,
185
+ contextBefore: normalizedContextBefore,
186
+ contextAfter: normalizedContextAfter,
187
+ maxColumns: DEFAULT_MAX_COLUMN,
188
+ mode: effectiveOutputMode,
189
+ },
190
+ undefined,
191
+ this.session.searchDb,
192
+ );
189
193
  } catch (err) {
190
194
  if (err instanceof Error && err.message.startsWith("regex parse error")) {
191
195
  throw new ToolError(err.message);
@@ -1,4 +1,5 @@
1
1
  import type { AgentTool } from "@oh-my-pi/pi-agent-core";
2
+ import type { SearchDb } from "@oh-my-pi/pi-natives";
2
3
  import { $env, logger } from "@oh-my-pi/pi-utils";
3
4
  import type { AsyncJobManager } from "../async";
4
5
  import type { PromptTemplate } from "../config/prompt-templates";
@@ -144,6 +145,8 @@ export interface ToolSession {
144
145
  asyncJobManager?: AsyncJobManager;
145
146
  /** Settings instance for passing to subagents */
146
147
  settings: Settings;
148
+ /** Shared native search DB for grep/glob/fuzzyFind-backed workflows. */
149
+ searchDb?: SearchDb;
147
150
  /** Plan mode state (if active) */
148
151
  getPlanModeState?: () => PlanModeState | undefined;
149
152
  /** Get compact conversation context for subagents (excludes tool results, system prompts) */
@@ -180,7 +180,8 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
180
180
  // Clamp to reasonable range: 1s - 600s (10 min)
181
181
  const timeoutSec = clampTimeout("python", rawTimeout);
182
182
  const timeoutMs = timeoutSec * 1000;
183
- const timeoutSignal = AbortSignal.timeout(timeoutMs);
183
+ const deadlineMs = Date.now() + timeoutMs;
184
+ const timeoutSignal = AbortSignal.timeout(Math.max(0, deadlineMs - Date.now()));
184
185
  const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
185
186
  let outputSink: OutputSink | undefined;
186
187
  let outputSummary: OutputSummary | undefined;
@@ -267,7 +268,7 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
267
268
  const sessionId = sessionFile ? `session:${sessionFile}:cwd:${commandCwd}` : `cwd:${commandCwd}`;
268
269
  const baseExecutorOptions: Omit<PythonExecutorOptions, "reset"> = {
269
270
  cwd: commandCwd,
270
- timeoutMs,
271
+ deadlineMs,
271
272
  signal: combinedSignal,
272
273
  sessionId,
273
274
  kernelMode: this.session.settings.get("python.kernelMode"),