agentic-pi 0.2.2 → 0.2.4

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 (46) hide show
  1. package/README.md +140 -29
  2. package/dist/args.d.ts +27 -0
  3. package/dist/args.js +46 -0
  4. package/dist/args.js.map +1 -1
  5. package/dist/extensions/file-search/index.d.ts +66 -0
  6. package/dist/extensions/file-search/index.js +86 -0
  7. package/dist/extensions/file-search/index.js.map +1 -0
  8. package/dist/extensions/web-search/extract.d.ts +18 -0
  9. package/dist/extensions/web-search/extract.js +110 -0
  10. package/dist/extensions/web-search/extract.js.map +1 -0
  11. package/dist/extensions/web-search/index.d.ts +43 -0
  12. package/dist/extensions/web-search/index.js +86 -0
  13. package/dist/extensions/web-search/index.js.map +1 -0
  14. package/dist/extensions/web-search/providers/brave.d.ts +21 -0
  15. package/dist/extensions/web-search/providers/brave.js +73 -0
  16. package/dist/extensions/web-search/providers/brave.js.map +1 -0
  17. package/dist/extensions/web-search/providers/exa.d.ts +16 -0
  18. package/dist/extensions/web-search/providers/exa.js +85 -0
  19. package/dist/extensions/web-search/providers/exa.js.map +1 -0
  20. package/dist/extensions/web-search/providers/tavily.d.ts +18 -0
  21. package/dist/extensions/web-search/providers/tavily.js +85 -0
  22. package/dist/extensions/web-search/providers/tavily.js.map +1 -0
  23. package/dist/extensions/web-search/rate-limit.d.ts +14 -0
  24. package/dist/extensions/web-search/rate-limit.js +24 -0
  25. package/dist/extensions/web-search/rate-limit.js.map +1 -0
  26. package/dist/extensions/web-search/safe-fetch.d.ts +54 -0
  27. package/dist/extensions/web-search/safe-fetch.js +172 -0
  28. package/dist/extensions/web-search/safe-fetch.js.map +1 -0
  29. package/dist/extensions/web-search/selection.d.ts +42 -0
  30. package/dist/extensions/web-search/selection.js +64 -0
  31. package/dist/extensions/web-search/selection.js.map +1 -0
  32. package/dist/extensions/web-search/tools.d.ts +13 -0
  33. package/dist/extensions/web-search/tools.js +136 -0
  34. package/dist/extensions/web-search/tools.js.map +1 -0
  35. package/dist/extensions/web-search/types.d.ts +65 -0
  36. package/dist/extensions/web-search/types.js +10 -0
  37. package/dist/extensions/web-search/types.js.map +1 -0
  38. package/dist/run.d.ts +45 -0
  39. package/dist/run.js +24 -0
  40. package/dist/run.js.map +1 -1
  41. package/dist/runner.js +85 -2
  42. package/dist/runner.js.map +1 -1
  43. package/dist/sandbox/gondolin.d.ts +13 -4
  44. package/dist/sandbox/gondolin.js +10 -3
  45. package/dist/sandbox/gondolin.js.map +1 -1
  46. package/package.json +2 -1
package/README.md CHANGED
@@ -42,8 +42,8 @@ single line you parse.
42
42
 
43
43
  Pi explicitly does not support MCP. agentic-pi ships a native Pi extension
44
44
  exposing **31 GitHub tools** ported from lastlight's `mcp-github-app`:
45
- clone/push, issues, PRs, reviews, labels, search. Tools are registered with
46
- the `github_` prefix to match opencode's MCP-server-name convention.
45
+ clone/push, issues, PRs, reviews, labels, search. Tool names are prefixed
46
+ with `github_`.
47
47
 
48
48
  Auth is opinionated: **GitHub App credentials preferred**, static
49
49
  `GITHUB_TOKEN` only as a low-trust fallback. JWT-minted installation tokens
@@ -81,38 +81,29 @@ The `extension_status` JSONL event always reports `status`, `reason`,
81
81
  `message`, `profile`, and `toolCount` so the orchestrator can log the
82
82
  outcome programmatically without parsing stderr.
83
83
 
84
- ### 5. Models named the way opencode names them
84
+ ### 5. Model selection
85
85
 
86
- `--model provider/id` accepts the exact string format opencode used
87
- (`openai/gpt-5.5`, `anthropic/claude-opus-4-5`, etc.). Credentials come from
88
- environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`,
89
- `OPENROUTER_API_KEY`) or Pi's `~/.pi/agent/auth.json` if you've logged in
90
- interactively. Provider/id mapping is delegated to `@earendil-works/pi-ai`'s
91
- `getModel()`.
86
+ `--model provider/id` (e.g. `anthropic/claude-opus-4-5`, `openai/gpt-4o`).
87
+ Credentials come from environment variables (`OPENAI_API_KEY`,
88
+ `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`) or Pi's `~/.pi/agent/auth.json`
89
+ if you've logged in interactively. Provider/id mapping is delegated to
90
+ `@earendil-works/pi-ai`'s `getModel()`.
92
91
 
93
92
  `--thinking <level>` maps directly to Pi's `thinkingLevel`
94
93
  (`off`/`minimal`/`low`/`medium`/`high`/`xhigh`). Per-provider effort is
95
94
  handled by Pi.
96
95
 
97
- ### 6. Things accepted but ignored for caller-side compatibility
98
-
99
- - `--dangerously-skip-permissions` — Pi has no permission prompts to skip
100
- ("run in a container" is Pi's design stance). The flag is accepted so a
101
- caller that previously spawned opencode does not need to strip it.
102
- - `--variant <level>` — alias for `--thinking`.
103
-
104
- ### 7. Defaults that match a containerized sandbox
96
+ ### 6. Defaults that match a containerized sandbox
105
97
 
106
98
  - **`--no-session`** is intended to be the default in sandboxed runs (state
107
99
  lives outside the container).
108
100
  - **Built-in tools** (read, write, edit, bash, grep, find, ls) are enabled
109
101
  by default. Add `--no-builtin-tools` if you want a GitHub-only agent.
110
102
  - **`AGENTS.md`** in the working directory is auto-loaded as the agent's
111
- system prompt — same convention Pi and opencode share. Drop your
112
- workflow's `AGENTS.md` into the mounted workspace and the agent picks it
113
- up.
103
+ system prompt — same convention Pi uses. Drop your workflow's
104
+ `AGENTS.md` into the mounted workspace and the agent picks it up.
114
105
 
115
- ### 8. Optional micro-VM sandboxing via `--sandbox gondolin`
106
+ ### 7. Optional micro-VM sandboxing via `--sandbox gondolin`
116
107
 
117
108
  By default Pi's file and bash tools run on the host. Pass `--sandbox gondolin`
118
109
  and they get routed through a per-run [Gondolin](https://github.com/earendil-works/gondolin)
@@ -195,14 +186,120 @@ The **App PEM is never copied into the VM** — only the resulting token,
195
186
  which is short-lived. User-supplied `--sandbox-env GITHUB_TOKEN=…`
196
187
  overrides the auto-injected value if you need to scope down further.
197
188
 
189
+ ### 8. Safe web search via the `web-search` extension
190
+
191
+ agentic-pi can register two native Pi tools — `web_search` and `web_fetch` —
192
+ so the agent can do general-purpose research. Backed by a configurable
193
+ provider:
194
+
195
+ | Provider | API key env var | Native content extraction |
196
+ | --- | --- | --- |
197
+ | Tavily (default) | `TAVILY_API_KEY` | yes (search + extract) |
198
+ | Exa | `EXA_API_KEY` | yes (search + contents) |
199
+ | Brave Search | `BRAVE_SEARCH_API_KEY` | no — `web_fetch` falls back to a safe HTML→text extractor |
200
+
201
+ **Auto-enable.** When at least one API key env var is present, the
202
+ extension is configured automatically. With multiple keys set, priority is
203
+ **Tavily → Exa → Brave**; override with `--web-search-provider` or the
204
+ `WEB_SEARCH_PROVIDER` env var. Pass `--no-web-search` to suppress the
205
+ tools entirely.
206
+
207
+ **Host-process egress.** Both tools run in the agentic-pi process, **not**
208
+ inside the Gondolin guest. That means:
209
+
210
+ - The provider API host is **not** added to the Gondolin egress
211
+ allowlist, and the API key is **never** injected into the VM.
212
+ - Behavior is identical under `--sandbox=none`, `--sandbox=gondolin`, and
213
+ when agentic-pi itself is containerized. The host's own network policy
214
+ controls reachability to the provider + arbitrary http(s) URLs.
215
+
216
+ **Safety rails (built-in, non-configurable in v1).**
217
+
218
+ | Rail | Default |
219
+ | --- | --- |
220
+ | URL scheme allowlist | `http`, `https` only (`web_fetch`) |
221
+ | Request timeout | 15 s |
222
+ | Max response bytes | 1 MiB (streamed, aborted on overflow) |
223
+ | Max redirects | 3 (scheme re-checked at each hop) |
224
+ | Content-type gate (`web_fetch`) | `text/*`, `application/(xhtml+xml\|xml\|json)` |
225
+ | Max search results | 10 (regardless of `max_results` arg) |
226
+ | Extracted text cap | ~200 KiB |
227
+ | HTML cleaning | `<script>`, `<style>`, `<noscript>`, `<iframe>`, comments stripped before extraction |
228
+ | Per-run call budget | 30 combined `web_search` + `web_fetch` calls (override with `--web-search-max-calls`) |
229
+
230
+ When the call budget is hit, further invocations return a structured
231
+ rate-limit error result so the agent can recover; the run is **not**
232
+ aborted.
233
+
234
+ **No SSRF blocking.** Loopback / private IP ranges are **not** blocked by
235
+ default. Operators who care should run agentic-pi behind their own
236
+ egress firewall.
237
+
238
+ **Event stream.** A second `extension_status` event mirrors GitHub's:
239
+
240
+ ```jsonl
241
+ {"type":"extension_status","extension":"web-search","status":"configured","provider":"tavily","toolCount":2,"maxCalls":30,"sessionId":"…","timestamp":"…"}
242
+ ```
243
+
244
+ When skipped (no keys / `--no-web-search`), `status: "skipped"` carries a
245
+ `reason` of `disabled-by-flag` or `no-credentials`. Misconfigurations
246
+ (explicit provider whose key is missing, or an unknown provider name)
247
+ surface as a warning before the run starts.
248
+
249
+ ### 9. Default file search via FFF
250
+
251
+ agentic-pi bundles [`@ff-labs/pi-fff`](https://github.com/dmtrKovalenko/fff/tree/main/packages/pi-fff)
252
+ — a Rust-backed, git-aware, frecency-ranked, SIMD-accelerated fuzzy file/content
253
+ search — as the **default** file-search backend. It ships as a dependency and is
254
+ loaded for **every** run with no per-host `pi install` required.
255
+
256
+ **`override` mode by default.** FFF registers under Pi's built-in tool names
257
+ (`find`, `grep`, `multi_grep`), transparently replacing the built-ins. The agent
258
+ gets faster, git-aware search with zero prompt changes. Switch behaviour with
259
+ `--file-search-mode`:
260
+
261
+ | Mode | Tool names | Notes |
262
+ | --- | --- | --- |
263
+ | `override` (default) | `find`, `grep`, `multi_grep` | Transparent replacement of Pi's built-ins. |
264
+ | `tools-only` | `fffind`, `ffgrep`, `fff-multi-grep` | Added alongside Pi's built-ins; the agent chooses. |
265
+ | `tools-and-ui` | same as `tools-only` | Adds `@`-mention autocomplete — useless headless; not recommended. |
266
+
267
+ The CLI flag maps to FFF's `PI_FFF_MODE` env var. An explicit `PI_FFF_MODE` in the
268
+ environment wins over the flag. Pass `--no-file-search` to disable FFF entirely and
269
+ fall back to Pi's built-in `find`/`grep`.
270
+
271
+ **Host-process execution.** Like web search, FFF runs in the agentic-pi process,
272
+ **not** inside the Gondolin guest. Under `--sandbox gondolin`, `read`/`write`/`edit`/
273
+ `bash` route through the VM while `find`/`grep` (FFF) run host-side against the
274
+ bind-mounted workspace. Paths align (cwd is the mount), and FFF only touches the
275
+ local filesystem — no egress or secret exposure.
276
+
277
+ **Native binary.** FFF is a native Rust library (`@ff-labs/fff-node`) shipped as
278
+ prebuilt per-platform binaries (`fff-bin-linux-x64-gnu`/`-musl`, `darwin`, `win32`).
279
+ npm auto-selects the correct one at install time. In containers, run `npm install`
280
+ on the target platform — do **not** copy `node_modules` across glibc↔musl.
281
+
282
+ **Safe by default.** If pi-fff can't be resolved or its native binary fails to load
283
+ on the platform, the run is **not** aborted — file search skips with
284
+ `reason: "resolve-failed"` (surfaced as a warning) and the agent falls back to Pi's
285
+ built-in `find`/`grep`.
286
+
287
+ **Event stream.** A third `extension_status` event mirrors the others:
288
+
289
+ ```jsonl
290
+ {"type":"extension_status","extension":"file-search","status":"configured","mode":"override","toolCount":3,"sessionId":"…","timestamp":"…"}
291
+ ```
292
+
293
+ When disabled or unavailable, `status: "skipped"` carries a `reason` of
294
+ `disabled-by-flag` or `resolve-failed`.
295
+
198
296
  ## When to use this
199
297
 
200
298
  - You have an orchestrator that calls a coding agent once per workflow
201
299
  phase, in a container, and parses a JSONL stream.
202
- - You used to call `opencode run --format json` and want a less-opaque
203
- replacement built on a more hackable substrate.
204
300
  - You need GitHub repo operations available to the agent without standing
205
301
  up an MCP server.
302
+ - You want safe, sandbox-mode-agnostic web search available to the agent.
206
303
 
207
304
  ## When **not** to use this
208
305
 
@@ -211,8 +308,9 @@ overrides the auto-injected value if you need to scope down further.
211
308
  - You want generic MCP support. Pi has none by design and agentic-pi inherits
212
309
  that decision; only the GitHub tool surface is built-in.
213
310
  - You want a different tool surface (Linear, GitLab, internal APIs). Fork the
214
- `extensions/github/` directory as a template, not as a runtime plugin
215
- system agentic-pi does not (yet) load arbitrary external extensions.
311
+ `extensions/github/` directory as a template. agentic-pi bundles specific Pi
312
+ extensions (GitHub, web search, FFF file search) but does not (yet) load
313
+ arbitrary operator-supplied extensions as a runtime plugin system.
216
314
 
217
315
  ## Usage
218
316
 
@@ -245,16 +343,21 @@ GITHUB_TOKEN=ghp_…
245
343
  | --- | --- |
246
344
  | `--model <provider/id>` | Required. e.g. `anthropic/claude-opus-4-5`, `openai/gpt-4o`. |
247
345
  | `--thinking <level>` | `off` \| `minimal` \| `low` \| `medium` \| `high` \| `xhigh`. |
248
- | `--variant <level>` | Alias for `--thinking`. |
249
346
  | `--profile <name>` | `read` \| `issues-write` \| `review-write` \| `repo-write`. Omit to disable GitHub tools entirely. |
250
347
  | `--cwd <path>` | Working directory for the agent. Default: `$PWD`. |
251
348
  | `--no-session` | Ephemeral run — do not persist session jsonl. Recommended in sandboxed containers. |
252
349
  | `--session-dir <path>` | Override session storage location. |
253
350
  | `--no-builtin-tools` | Disable Pi's `read,write,edit,bash,grep,find,ls`. |
254
351
  | `--tools <a,b,c>` | Explicit tool allowlist (combined with profile if set). |
255
- | `--sandbox <none\|gondolin>` | Route `read`/`write`/`edit`/`bash` through a sandbox backend. Default `none`. `gondolin` boots a QEMU micro-VM mounting cwd at `/workspace`. Requires QEMU on the host; native-only (not Docker-in-Docker). See section 8. |
352
+ | `--sandbox <none\|gondolin>` | Route `read`/`write`/`edit`/`bash` through a sandbox backend. Default `none`. `gondolin` boots a QEMU micro-VM mounting cwd at `/workspace`. Requires QEMU on the host; native-only (not Docker-in-Docker). See section 7. |
256
353
  | `--sandbox-env KEY=VAL` | Inject env var into the sandbox VM (repeatable). Ignored when `--sandbox=none`. Auto-injects a minted `GITHUB_TOKEN`/`GH_TOKEN` when `--profile` is also active. |
257
- | `--dangerously-skip-permissions` | Accepted for caller-side compatibility. No-op. |
354
+ | `--allow-host <host>` | Add host to the sandbox HTTP egress allowlist (repeatable). Ignored when `--sandbox=none`. |
355
+ | `--no-network` | Disable sandbox HTTP egress entirely. Ignored when `--sandbox=none`. |
356
+ | `--web-search-provider <p>` | Force web-search provider: `tavily` \| `brave` \| `exa`. Default: auto-detect by env. See section 8. |
357
+ | `--no-web-search` | Disable the web-search extension (no `web_search`/`web_fetch` tools). |
358
+ | `--no-file-search` | Disable the bundled FFF file-search extension; fall back to Pi's built-in `find`/`grep`. |
359
+ | `--file-search-mode <m>` | FFF mode: `override` (default) \| `tools-only` \| `tools-and-ui`. Overridden by the `PI_FFF_MODE` env var. See section 9. |
360
+ | `--web-search-max-calls <n>` | Cap combined `web_search` + `web_fetch` calls per run. Default: 30. |
258
361
 
259
362
  Reads the prompt from stdin. Emits JSONL on stdout. Exits 0 on `agent_end`,
260
363
  1 on fatal error.
@@ -265,6 +368,8 @@ Reads the prompt from stdin. Emits JSONL on stdout. Exits 0 on `agent_end`,
265
368
  {"type":"session","version":3,"id":"<uuid>","timestamp":"…","cwd":"…"}
266
369
  {"type":"sandbox_status","backend":"none","status":{"backend":"none"},"sessionId":"<uuid>","timestamp":"…"}
267
370
  {"type":"extension_status","extension":"github","status":"configured","profile":"read","toolCount":18,"sessionId":"<uuid>","timestamp":"…"}
371
+ {"type":"extension_status","extension":"web-search","status":"configured","provider":"tavily","toolCount":2,"maxCalls":30,"sessionId":"<uuid>","timestamp":"…"}
372
+ {"type":"extension_status","extension":"file-search","status":"configured","mode":"override","toolCount":3,"sessionId":"<uuid>","timestamp":"…"}
268
373
  {"type":"agent_start","sessionId":"<uuid>","timestamp":"…"}
269
374
  {"type":"turn_start","sessionId":"<uuid>","timestamp":"…"}
270
375
  {"type":"message_start","message":{…},"sessionId":"<uuid>","timestamp":"…"}
@@ -343,7 +448,9 @@ console.log(result.records.length); // full event log
343
448
  | `messages` | `unknown[]` | Full Pi message array from `agent_end`. |
344
449
  | `stats` | `{userMessages, assistantMessages, toolCalls, toolResults, tokens: {input, output, cacheRead, cacheWrite, total}, cost}` \| `undefined` | Token + cost rollup. |
345
450
  | `sandbox` | `{backend, status}` \| `undefined` | Mirror of the `sandbox_status` event. |
346
- | `github` | `{status, reason, profile, toolCount}` \| `undefined` | Mirror of the `extension_status` event. |
451
+ | `github` | `{status, reason, profile, toolCount}` \| `undefined` | Mirror of the GitHub `extension_status` event. |
452
+ | `webSearch` | `{status, reason, provider, toolCount, maxCalls}` \| `undefined` | Mirror of the web-search `extension_status` event. |
453
+ | `fileSearch` | `{status, reason, mode, toolCount}` \| `undefined` | Mirror of the FFF file-search `extension_status` event. |
347
454
  | `records` | `EmitterRecord[]` | Every JSONL record in order. Same shape that the CLI writes. |
348
455
  | `warnings` | `string[]` | Warnings that would have gone to stderr in CLI mode. |
349
456
 
@@ -394,6 +501,8 @@ which walks `test/` for `*.test.ts`.
394
501
  | `test/models.test.ts` | `provider/id` parsing including openrouter triple-slash | — |
395
502
  | `test/extensions/github/profiles.test.ts` | Profile → tool allowlist (counts, superset structure, scope tiering) | — |
396
503
  | `test/extensions/github/credentials.test.ts` | `assertSafeToken` and `credentialsFilePath` validation | — |
504
+ | `test/extensions/web-search/*.test.ts` | Provider selection, extension wiring, safe-fetch rails, HTML extraction, rate limiter, per-provider normalization (all with injected `fetchImpl`) | — |
505
+ | `test/extensions/file-search/index.test.ts` | FFF extension wiring: mode → tool names, package resolution, disabled-by-flag + resolve-failed skips | — |
397
506
  | `test/sandbox/preflight.test.ts` | Preflight returns a structured ok\|error result | — |
398
507
  | `test/run.integration.test.ts` | Programmatic `run()`: RunResult populated, onEvent fires for every record, **child-process check confirms zero stdout/stderr leak from library** | `OPENAI_API_KEY` not set |
399
508
  | `test/run-sandbox.integration.test.ts` | `run({ sandbox: "gondolin" })` boots a VM, agent's `write` tool produces a host file via the mount | `OPENAI_API_KEY` not set OR QEMU/preflight unavailable |
@@ -465,6 +574,8 @@ src/
465
574
  credentials.ts git credential-store file writer (mode 600)
466
575
  profiles.ts 4 profiles → tool name allowlists
467
576
  tools.ts 31 defineTool() registrations
577
+ extensions/file-search/
578
+ index.ts loadFileSearchExtension() — resolves bundled @ff-labs/pi-fff
468
579
  sandbox/
469
580
  index.ts buildSandbox(backend) dispatcher
470
581
  preflight.ts QEMU + accelerator detection (refuses to start if hung)
package/dist/args.d.ts CHANGED
@@ -60,6 +60,33 @@ export interface RunConfig {
60
60
  * entirely. Ignored when `sandbox === "none"`.
61
61
  */
62
62
  allowedHttpHosts?: string[] | null;
63
+ /**
64
+ * Web-search extension toggle. Default: true (auto-enables when a
65
+ * provider API key env var is present). Pass `--no-web-search` to
66
+ * force-disable.
67
+ */
68
+ webSearch: boolean;
69
+ /**
70
+ * Explicit web-search provider. Overrides auto-detection by env var.
71
+ * Set via `--web-search-provider <tavily|brave|exa>`.
72
+ */
73
+ webSearchProvider?: string;
74
+ /**
75
+ * Per-run cap on combined web_search + web_fetch calls. Default: 30.
76
+ * Set via `--web-search-max-calls <n>`.
77
+ */
78
+ webSearchMaxCalls?: number;
79
+ /**
80
+ * File-search extension (FFF) toggle. Default: true — bundled and
81
+ * enabled for every run. Pass `--no-file-search` to fall back to Pi's
82
+ * built-in find/grep.
83
+ */
84
+ fileSearch: boolean;
85
+ /**
86
+ * FFF mode. Default: "override" (FFF replaces built-in find/grep under
87
+ * the same names). Set via `--file-search-mode <override|tools-only|tools-and-ui>`.
88
+ */
89
+ fileSearchMode?: "override" | "tools-only" | "tools-and-ui";
63
90
  }
64
91
  export declare function printHelp(): void;
65
92
  export declare function parseArgs(argv: string[]): RunConfig;
package/dist/args.js CHANGED
@@ -34,6 +34,20 @@ Flags:
34
34
  Ignored when --sandbox=none.
35
35
  --no-network Disable HTTP egress from the sandbox entirely.
36
36
  Ignored when --sandbox=none.
37
+ --web-search-provider <p> Force a web-search provider: tavily | brave | exa.
38
+ Default: auto-detect from env (Tavily > Exa > Brave).
39
+ Provider's API key env var must be set:
40
+ TAVILY_API_KEY, EXA_API_KEY, or BRAVE_SEARCH_API_KEY.
41
+ --no-web-search Disable the web-search extension entirely
42
+ (web_search / web_fetch tools not registered).
43
+ --web-search-max-calls <n> Cap combined web_search + web_fetch calls per run.
44
+ Default: 30. When exceeded, further calls return a
45
+ structured error result.
46
+ --no-file-search Disable the bundled FFF file-search extension; fall
47
+ back to Pi's built-in find/grep.
48
+ --file-search-mode <m> FFF mode: override | tools-only | tools-and-ui.
49
+ Default: override (FFF replaces built-in find/grep
50
+ under the same names). Overridden by PI_FFF_MODE env.
37
51
  --sandbox-image <name> Image to boot when --sandbox=gondolin. Values:
38
52
  'default' (recommended) — bundled agentic-pi-dev image
39
53
  with git/gh/node/python/rust baked in (auto-downloaded).
@@ -54,6 +68,8 @@ export function parseArgs(argv) {
54
68
  noBuiltinTools: false,
55
69
  dangerouslySkipPermissions: false,
56
70
  sandbox: "none",
71
+ webSearch: true,
72
+ fileSearch: true,
57
73
  };
58
74
  for (let i = 0; i < argv.length; i++) {
59
75
  const arg = argv[i];
@@ -147,6 +163,36 @@ export function parseArgs(argv) {
147
163
  case "--no-network":
148
164
  config.allowedHttpHosts = null;
149
165
  break;
166
+ case "--no-web-search":
167
+ config.webSearch = false;
168
+ break;
169
+ case "--web-search-provider": {
170
+ const v = next().trim();
171
+ if (!v)
172
+ throw new Error("--web-search-provider requires a value");
173
+ config.webSearchProvider = v;
174
+ break;
175
+ }
176
+ case "--web-search-max-calls": {
177
+ const v = next();
178
+ const n = Number(v);
179
+ if (!Number.isFinite(n) || Math.floor(n) !== n || n < 1) {
180
+ throw new Error(`--web-search-max-calls must be a positive integer (got '${v}')`);
181
+ }
182
+ config.webSearchMaxCalls = n;
183
+ break;
184
+ }
185
+ case "--no-file-search":
186
+ config.fileSearch = false;
187
+ break;
188
+ case "--file-search-mode": {
189
+ const v = next();
190
+ if (v !== "override" && v !== "tools-only" && v !== "tools-and-ui") {
191
+ throw new Error(`invalid --file-search-mode '${v}'. Expected: override | tools-only | tools-and-ui`);
192
+ }
193
+ config.fileSearchMode = v;
194
+ break;
195
+ }
150
196
  case "-h":
151
197
  case "--help":
152
198
  printHelp();
package/dist/args.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * File-search extension entry point.
3
+ *
4
+ * Bundles FFF (`@ff-labs/pi-fff`) — a Rust-backed, git-aware,
5
+ * frecency-ranked fuzzy file/content search — as agentic-pi's default
6
+ * file-search backend, so every run gets it without a per-host
7
+ * `pi install`.
8
+ *
9
+ * Unlike the `github` and `web-search` extensions, this one does NOT
10
+ * contribute a `customTools` array. pi-fff is a full Pi extension
11
+ * (registers tools + an `@`-mention enhancer via an ExtensionFactory), so
12
+ * it is loaded through Pi's resource loader, not the SDK's `customTools`
13
+ * channel. This module's job is to:
14
+ *
15
+ * 1. Resolve the installed pi-fff package directory (the value the
16
+ * runner hands to `DefaultResourceLoader.additionalExtensionPaths`).
17
+ * 2. Decide the FFF mode the runner publishes via the `PI_FFF_MODE` env.
18
+ *
19
+ * Mirrors the other extensions' "safe by default" contract: if the
20
+ * package can't be resolved (missing / incompatible native binary), we
21
+ * skip with a reason rather than aborting the run, and the agent falls
22
+ * back to Pi's built-in `find`/`grep`.
23
+ */
24
+ /** FFF mode. `override` replaces Pi's built-in find/grep under the same names. */
25
+ export type FileSearchMode = "override" | "tools-only" | "tools-and-ui";
26
+ export declare const VALID_FILE_SEARCH_MODES: FileSearchMode[];
27
+ export declare const DEFAULT_FILE_SEARCH_MODE: FileSearchMode;
28
+ export type FileSearchSkipReason = "disabled-by-flag" | "resolve-failed";
29
+ export interface FileSearchExtensionConfig {
30
+ /** When false, the extension is force-skipped (disabled-by-flag). Default: true. */
31
+ fileSearch?: boolean;
32
+ /** FFF mode. Default: "override". */
33
+ fileSearchMode?: FileSearchMode;
34
+ /**
35
+ * Resolver override (injected by tests). Returns the absolute path to the
36
+ * installed `@ff-labs/pi-fff` package directory, or throws.
37
+ */
38
+ resolvePackageDir?: () => string;
39
+ }
40
+ export interface FileSearchExtensionResult {
41
+ status: "configured" | "skipped";
42
+ reason?: FileSearchSkipReason;
43
+ message?: string;
44
+ /** The FFF mode in effect (echoed for observability). */
45
+ mode?: FileSearchMode;
46
+ /**
47
+ * Absolute path to the pi-fff package directory. The runner passes this
48
+ * to `DefaultResourceLoader.additionalExtensionPaths`. Undefined when
49
+ * skipped.
50
+ */
51
+ packageDir?: string;
52
+ /**
53
+ * Tool names the agent will see, derived from `mode` (for the
54
+ * `extension_status` event only — the tools themselves are registered by
55
+ * pi-fff, not here).
56
+ */
57
+ toolNames: string[];
58
+ }
59
+ export declare function loadFileSearchExtension(config?: FileSearchExtensionConfig): FileSearchExtensionResult;
60
+ /**
61
+ * True if the skip is something the user almost certainly wants surfaced
62
+ * as a warning. A resolve failure means file search was meant to work but
63
+ * the install/binary is broken; `disabled-by-flag` is an explicit choice
64
+ * and stays silent.
65
+ */
66
+ export declare function isMisconfigurationSkip(result: FileSearchExtensionResult): boolean;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * File-search extension entry point.
3
+ *
4
+ * Bundles FFF (`@ff-labs/pi-fff`) — a Rust-backed, git-aware,
5
+ * frecency-ranked fuzzy file/content search — as agentic-pi's default
6
+ * file-search backend, so every run gets it without a per-host
7
+ * `pi install`.
8
+ *
9
+ * Unlike the `github` and `web-search` extensions, this one does NOT
10
+ * contribute a `customTools` array. pi-fff is a full Pi extension
11
+ * (registers tools + an `@`-mention enhancer via an ExtensionFactory), so
12
+ * it is loaded through Pi's resource loader, not the SDK's `customTools`
13
+ * channel. This module's job is to:
14
+ *
15
+ * 1. Resolve the installed pi-fff package directory (the value the
16
+ * runner hands to `DefaultResourceLoader.additionalExtensionPaths`).
17
+ * 2. Decide the FFF mode the runner publishes via the `PI_FFF_MODE` env.
18
+ *
19
+ * Mirrors the other extensions' "safe by default" contract: if the
20
+ * package can't be resolved (missing / incompatible native binary), we
21
+ * skip with a reason rather than aborting the run, and the agent falls
22
+ * back to Pi's built-in `find`/`grep`.
23
+ */
24
+ import { createRequire } from "node:module";
25
+ import { dirname } from "node:path";
26
+ export const VALID_FILE_SEARCH_MODES = [
27
+ "override",
28
+ "tools-only",
29
+ "tools-and-ui",
30
+ ];
31
+ export const DEFAULT_FILE_SEARCH_MODE = "override";
32
+ /** Tool names FFF exposes per mode (see @ff-labs/pi-fff src/index.ts). */
33
+ function toolNamesForMode(mode) {
34
+ return mode === "override"
35
+ ? ["find", "grep", "multi_grep"]
36
+ : ["fffind", "ffgrep", "fff-multi-grep"];
37
+ }
38
+ function defaultResolvePackageDir() {
39
+ // Resolve relative to this module so it works regardless of the
40
+ // consumer's cwd. `@ff-labs/pi-fff/package.json` is always present; its
41
+ // directory is what the loader's package resolver reads the `pi`
42
+ // manifest from.
43
+ const require = createRequire(import.meta.url);
44
+ return dirname(require.resolve("@ff-labs/pi-fff/package.json"));
45
+ }
46
+ export function loadFileSearchExtension(config = {}) {
47
+ if (config.fileSearch === false) {
48
+ return {
49
+ status: "skipped",
50
+ reason: "disabled-by-flag",
51
+ message: "file search disabled via --no-file-search",
52
+ toolNames: [],
53
+ };
54
+ }
55
+ const mode = config.fileSearchMode ?? DEFAULT_FILE_SEARCH_MODE;
56
+ const resolve = config.resolvePackageDir ?? defaultResolvePackageDir;
57
+ let packageDir;
58
+ try {
59
+ packageDir = resolve();
60
+ }
61
+ catch (err) {
62
+ return {
63
+ status: "skipped",
64
+ reason: "resolve-failed",
65
+ message: `could not resolve @ff-labs/pi-fff: ${err.message}`,
66
+ mode,
67
+ toolNames: [],
68
+ };
69
+ }
70
+ return {
71
+ status: "configured",
72
+ mode,
73
+ packageDir,
74
+ toolNames: toolNamesForMode(mode),
75
+ };
76
+ }
77
+ /**
78
+ * True if the skip is something the user almost certainly wants surfaced
79
+ * as a warning. A resolve failure means file search was meant to work but
80
+ * the install/binary is broken; `disabled-by-flag` is an explicit choice
81
+ * and stays silent.
82
+ */
83
+ export function isMisconfigurationSkip(result) {
84
+ return result.status === "skipped" && result.reason === "resolve-failed";
85
+ }
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/file-search/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,MAAM,CAAC,MAAM,uBAAuB,GAAqB;IACvD,UAAU;IACV,YAAY;IACZ,cAAc;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAmB,UAAU,CAAC;AAoCnE,0EAA0E;AAC1E,SAAS,gBAAgB,CAAC,IAAoB;IAC5C,OAAO,IAAI,KAAK,UAAU;QACxB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC;QAChC,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,wBAAwB;IAC/B,gEAAgE;IAChE,wEAAwE;IACxE,iEAAiE;IACjE,iBAAiB;IACjB,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,SAAoC,EAAE;IAEtC,IAAI,MAAM,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;QAChC,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,2CAA2C;YACpD,SAAS,EAAE,EAAE;SACd,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,IAAI,wBAAwB,CAAC;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,iBAAiB,IAAI,wBAAwB,CAAC;IAErE,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,OAAO,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,gBAAgB;YACxB,OAAO,EAAE,sCAAuC,GAAa,CAAC,OAAO,EAAE;YACvE,IAAI;YACJ,SAAS,EAAE,EAAE;SACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,YAAY;QACpB,IAAI;QACJ,UAAU;QACV,SAAS,EAAE,gBAAgB,CAAC,IAAI,CAAC;KAClC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAiC;IACtE,OAAO,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,gBAAgB,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Minimal HTML → readable text extractor. No dependencies.
3
+ *
4
+ * Approach:
5
+ * 1. Strip <script>, <style>, <noscript>, <iframe>, and HTML comments
6
+ * so the agent never sees code or hidden trackers.
7
+ * 2. Replace block-level tags with newlines so paragraphs stay separated.
8
+ * 3. Drop all other tags.
9
+ * 4. Decode the common named entities and any &#NN; / &#xHH; numeric
10
+ * escapes.
11
+ * 5. Collapse runs of whitespace; cap output at MAX_BYTES.
12
+ *
13
+ * Not a Readability-style content-only extractor — for that, use Tavily or
14
+ * Exa's native extraction, which the provider clients invoke directly.
15
+ */
16
+ export declare const EXTRACT_MAX_BYTES: number;
17
+ export declare function extractTitle(html: string): string | undefined;
18
+ export declare function htmlToText(html: string, maxBytes?: number): string;