@oh-my-pi/pi-coding-agent 10.6.2 → 11.0.1

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 (86) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +80 -79
  3. package/docs/compaction.md +182 -149
  4. package/docs/config-usage.md +141 -78
  5. package/docs/custom-tools.md +45 -16
  6. package/docs/extension-loading.md +56 -954
  7. package/docs/extensions.md +192 -51
  8. package/docs/hooks.md +109 -70
  9. package/docs/python-repl.md +52 -19
  10. package/docs/rpc.md +43 -19
  11. package/docs/sdk.md +270 -211
  12. package/docs/session-tree-plan.md +60 -417
  13. package/docs/session.md +104 -39
  14. package/docs/skills.md +59 -95
  15. package/docs/theme.md +139 -110
  16. package/docs/tree.md +42 -33
  17. package/docs/tui.md +226 -80
  18. package/package.json +8 -9
  19. package/src/capability/index.ts +3 -4
  20. package/src/cli/args.ts +4 -4
  21. package/src/cli/grep-cli.ts +1 -1
  22. package/src/commit/agentic/index.ts +4 -3
  23. package/src/commit/git/index.ts +2 -3
  24. package/src/commit/map-reduce/index.ts +2 -1
  25. package/src/config/prompt-templates.ts +2 -0
  26. package/src/config/settings-schema.ts +30 -7
  27. package/src/config/settings.ts +0 -14
  28. package/src/config.ts +2 -2
  29. package/src/discovery/agents.ts +36 -0
  30. package/src/discovery/index.ts +1 -0
  31. package/src/exa/mcp-client.ts +3 -3
  32. package/src/ipy/executor.ts +5 -7
  33. package/src/ipy/gateway-coordinator.ts +1 -1
  34. package/src/ipy/kernel.ts +20 -15
  35. package/src/ipy/prelude.py +1 -1
  36. package/src/ipy/runtime.ts +7 -6
  37. package/src/lsp/lspmux.ts +3 -3
  38. package/src/main.ts +6 -8
  39. package/src/mcp/tool-bridge.ts +19 -9
  40. package/src/modes/components/assistant-message.ts +2 -2
  41. package/src/modes/components/hook-editor.ts +4 -4
  42. package/src/modes/components/settings-defs.ts +37 -2
  43. package/src/modes/components/tool-execution.ts +7 -7
  44. package/src/modes/controllers/command-controller.ts +2 -2
  45. package/src/modes/controllers/event-controller.ts +4 -7
  46. package/src/modes/controllers/input-controller.ts +4 -4
  47. package/src/modes/controllers/selector-controller.ts +1 -0
  48. package/src/modes/interactive-mode.ts +3 -5
  49. package/src/modes/rpc/rpc-mode.ts +8 -9
  50. package/src/patch/index.ts +6 -6
  51. package/src/prompts/agents/explore.md +2 -2
  52. package/src/prompts/agents/frontmatter.md +5 -5
  53. package/src/prompts/agents/plan.md +3 -2
  54. package/src/prompts/agents/reviewer.md +1 -1
  55. package/src/prompts/system/system-prompt.md +1 -3
  56. package/src/sdk.ts +13 -9
  57. package/src/session/agent-session.ts +6 -4
  58. package/src/session/compaction/compaction.ts +3 -3
  59. package/src/session/session-manager.ts +8 -9
  60. package/src/ssh/connection-manager.ts +4 -4
  61. package/src/system-prompt.ts +2 -6
  62. package/src/task/agents.ts +1 -1
  63. package/src/task/executor.ts +31 -8
  64. package/src/task/index.ts +14 -35
  65. package/src/task/omp-command.ts +3 -1
  66. package/src/task/output-manager.ts +20 -6
  67. package/src/task/parallel.ts +3 -3
  68. package/src/task/render.ts +16 -2
  69. package/src/task/types.ts +13 -20
  70. package/src/task/worktree.ts +3 -3
  71. package/src/tools/ask.ts +3 -8
  72. package/src/tools/fetch.ts +2 -2
  73. package/src/tools/gemini-image.ts +5 -6
  74. package/src/tools/grep.ts +5 -5
  75. package/src/tools/index.ts +12 -5
  76. package/src/tools/read.ts +1 -1
  77. package/src/tools/todo-write.ts +2 -3
  78. package/src/utils/frontmatter.ts +1 -1
  79. package/src/utils/image-resize.ts +1 -1
  80. package/src/utils/timings.ts +3 -2
  81. package/src/web/scrapers/github.ts +2 -2
  82. package/src/web/scrapers/utils.ts +2 -3
  83. package/src/web/scrapers/youtube.ts +2 -3
  84. package/src/web/search/auth.ts +5 -6
  85. package/src/web/search/providers/anthropic.ts +3 -2
  86. package/src/utils/terminal-notify.ts +0 -37
package/docs/hooks.md CHANGED
@@ -24,10 +24,10 @@ See [examples/hooks/](../examples/hooks/) for working implementations, including
24
24
 
25
25
  ## Quick Start
26
26
 
27
- Create `~/.omp/agent/hooks/my-hook.ts`:
27
+ Create `~/.omp/agent/hooks/pre/my-hook.ts` (or project-local `.omp/hooks/pre/`):
28
28
 
29
29
  ```typescript
30
- import type { HookAPI } from "@oh-my-pi/pi-coding-agent";
30
+ import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
31
31
 
32
32
  export default function (pi: HookAPI) {
33
33
  pi.on("session_start", async (_event, ctx) => {
@@ -51,29 +51,30 @@ omp --hook ./my-hook.ts
51
51
 
52
52
  ## Hook Locations
53
53
 
54
- Hooks are auto-discovered from:
54
+ Hooks are auto-discovered from config directories under `hooks/`:
55
55
 
56
- | Location | Scope |
57
- | ------------------------- | --------------------- |
58
- | `~/.omp/agent/hooks/*.ts` | Global (all projects) |
59
- | `.omp/hooks/*.ts` | Project-local |
56
+ Native (`.omp`, `.pi`) and Claude (`.claude`) use subdirectory structure:
60
57
 
61
- Additional paths via `settings.json`:
58
+ - User-level:
59
+ - Native: `~/.omp/agent/hooks/{pre,post}/*.ts` (or `~/.pi/agent`)
60
+ - Claude: `~/.claude/hooks/{pre,post}/*.ts`
61
+ - Project-level: `.omp/hooks/{pre,post}/*.ts` (or `.pi`, `.claude`)
62
62
 
63
- ```json
64
- {
65
- "hooks": ["/path/to/hook.ts"]
66
- }
67
- ```
63
+ Codex (`.codex`) uses flat structure with filename prefixes (`pre-*.ts`, `post-*.ts`):
64
+
65
+ - User-level: `~/.codex/hooks/*.ts`
66
+ - Project-level: `.codex/hooks/*.ts`
67
+
68
+ Hooks can also be loaded from plugin manifests or explicitly via `--hook`.
68
69
 
69
70
  ## Available Imports
70
71
 
71
- | Package | Purpose |
72
- | --------------------------------- | --------------------------------------------- |
73
- | `@oh-my-pi/pi-coding-agent/hooks` | Hook types (`HookAPI`, `HookContext`, events) |
74
- | `@oh-my-pi/pi-coding-agent` | Additional types if needed |
75
- | `@oh-my-pi/pi-ai` | AI utilities |
76
- | `@oh-my-pi/pi-tui` | TUI components |
72
+ | Package | Purpose |
73
+ | --------------------------------- | ---------------------------------------------------- |
74
+ | `@oh-my-pi/pi-coding-agent/hooks` | Hook types (`HookAPI`, `HookContext`, events) |
75
+ | `@oh-my-pi/pi-coding-agent` | Components (`BorderedLoader`), utilities, type re-exports |
76
+ | `@oh-my-pi/pi-ai` | AI utilities (`complete`, message types) |
77
+ | `@oh-my-pi/pi-tui` | TUI components (`CancellableLoader`, etc.) |
77
78
 
78
79
  Node.js built-ins (`node:fs`, `node:path`, etc.) are also available.
79
80
 
@@ -82,7 +83,7 @@ Node.js built-ins (`node:fs`, `node:path`, etc.) are also available.
82
83
  A hook exports a default function that receives `HookAPI`:
83
84
 
84
85
  ```typescript
85
- import type { HookAPI } from "@oh-my-pi/pi-coding-agent";
86
+ import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
86
87
 
87
88
  export default function (pi: HookAPI) {
88
89
  // Subscribe to events
@@ -92,7 +93,7 @@ export default function (pi: HookAPI) {
92
93
  }
93
94
  ```
94
95
 
95
- Hooks are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
96
+ Hooks are loaded via native Bun import, so TypeScript works without compilation.
96
97
 
97
98
  ## Events
98
99
 
@@ -125,9 +126,9 @@ user sends prompt ────────────────────
125
126
 
126
127
  user sends another prompt ◄────────────────────────────────┘
127
128
 
128
- /new (new session) or /resume (switch session)
129
- ├─► session_before_switch (can cancel, has reason: "new" | "resume")
130
- └─► session_switch (has reason: "new" | "resume")
129
+ /new, /resume, or /fork
130
+ ├─► session_before_switch (can cancel, has reason: "new" | "resume" | "fork")
131
+ └─► session_switch (has reason: "new" | "resume" | "fork")
131
132
 
132
133
  /branch
133
134
  ├─► session_before_branch (can cancel)
@@ -135,6 +136,7 @@ user sends another prompt ◄─────────────────
135
136
 
136
137
  /compact or auto-compaction
137
138
  ├─► session_before_compact (can cancel or customize)
139
+ ├─► session.compacting (customize prompt/context)
138
140
  └─► session_compact
139
141
 
140
142
  /tree navigation
@@ -159,11 +161,11 @@ pi.on("session_start", async (_event, ctx) => {
159
161
 
160
162
  #### session_before_switch / session_switch
161
163
 
162
- Fired when starting a new session (`/new`) or switching sessions (`/resume`).
164
+ Fired when starting a new session (`/new`), resuming (`/resume`), or forking (`/fork`).
163
165
 
164
166
  ```typescript
165
167
  pi.on("session_before_switch", async (event, ctx) => {
166
- // event.reason - "new" (starting fresh) or "resume" (switching to existing)
168
+ // event.reason - "new" (starting fresh), "resume" (switching to existing), or "fork" (branch switch)
167
169
  // event.targetSessionFile - session we're switching to (only for "resume")
168
170
 
169
171
  if (event.reason === "new") {
@@ -175,7 +177,7 @@ pi.on("session_before_switch", async (event, ctx) => {
175
177
  });
176
178
 
177
179
  pi.on("session_switch", async (event, ctx) => {
178
- // event.reason - "new" or "resume"
180
+ // event.reason - "new", "resume", or "fork"
179
181
  // event.previousSessionFile - session we came from
180
182
  });
181
183
  ```
@@ -200,7 +202,7 @@ pi.on("session_branch", async (event, ctx) => {
200
202
 
201
203
  The `skipConversationRestore` option is useful for checkpoint hooks that restore code state separately.
202
204
 
203
- #### session_before_compact / session_compact
205
+ #### session_before_compact / session.compacting / session_compact
204
206
 
205
207
  Fired on compaction. See [compaction.md](compaction.md) for details.
206
208
 
@@ -221,9 +223,30 @@ pi.on("session_before_compact", async (event, ctx) => {
221
223
  };
222
224
  });
223
225
 
226
+ ```
227
+
228
+ #### session.compacting
229
+
230
+ Fired after preparation but before the default summarizer runs. Use it to customize the prompt or add context
231
+ when you are not returning a full compaction result from `session_before_compact`.
232
+
233
+ ```typescript
234
+ pi.on("session.compacting", async (event, ctx) => {
235
+ // event.sessionId
236
+ // event.messages - messages about to be summarized
237
+
238
+ return {
239
+ context: ["Additional context line"],
240
+ prompt: "Custom compaction prompt...",
241
+ preserveData: { source: "my-hook" },
242
+ };
243
+ });
244
+ ```
245
+
246
+ ```typescript
224
247
  pi.on("session_compact", async (event, ctx) => {
225
248
  // event.compactionEntry - the saved compaction
226
- // event.fromHook - whether hook provided it
249
+ // event.fromExtension - whether hook provided it
227
250
  });
228
251
  ```
229
252
 
@@ -243,7 +266,7 @@ pi.on("session_before_tree", async (event, ctx) => {
243
266
  });
244
267
 
245
268
  pi.on("session_tree", async (event, ctx) => {
246
- // event.newLeafId, oldLeafId, summaryEntry, fromHook
269
+ // event.newLeafId, oldLeafId, summaryEntry, fromExtension
247
270
  });
248
271
  ```
249
272
 
@@ -340,15 +363,21 @@ pi.on("tool_call", async (event, ctx) => {
340
363
  });
341
364
  ```
342
365
 
343
- Tool inputs:
366
+ Tool inputs (common built-ins):
344
367
 
345
- - `bash`: `{ command, timeout? }`
346
- - `read`: `{ path, offset?, limit? }`
368
+ - `bash`: `{ command, timeout?, cwd?, head?, tail? }`
369
+ - `read`: `{ path, offset?, limit?, lines? }`
347
370
  - `write`: `{ path, content }`
348
- - `edit`: `{ path, old_text, new_text }`
349
- - `ls`: `{ path?, limit? }`
350
- - `find`: `{ pattern, path?, limit? }`
351
- - `grep`: `{ pattern, path?, glob?, ignore_case?, literal?, context?, limit? }`
371
+ - `edit` (replace mode): `{ path, old_text, new_text, all? }`
372
+ - `edit` (patch mode): `{ path, op?, rename?, diff? }`
373
+ - `find`: `{ pattern, hidden?, limit? }`
374
+ - `grep`: `{ pattern, path?, glob?, type?, i?, pre?, post?, multiline?, limit?, offset? }`
375
+
376
+ The edit input shape depends on the current edit variant (replace vs patch). Inspect `event.input` to
377
+ see which schema is active.
378
+
379
+ Other tools (ask, browser, task, todo_write, fetch, web_search, python, notebook, lsp, ssh, calc) use
380
+ their own schemas; inspect the tool prompt or `src/tools/*.ts` for details.
352
381
 
353
382
  #### tool_result
354
383
 
@@ -372,23 +401,20 @@ pi.on("tool_result", async (event, ctx) => {
372
401
  });
373
402
  ```
374
403
 
375
- Use type guards for typed details:
404
+ Use `event.toolName` to narrow tool-specific details:
376
405
 
377
406
  ```typescript
378
- import { isBashToolResult } from "@oh-my-pi/pi-coding-agent";
379
-
380
407
  pi.on("tool_result", async (event, ctx) => {
381
- if (isBashToolResult(event)) {
408
+ if (event.toolName === "bash") {
382
409
  // event.details is BashToolDetails | undefined
383
- if (event.details?.truncation?.truncated) {
384
- // Full output at event.details.fullOutputPath
410
+ const artifactId = event.details?.meta?.truncation?.artifactId;
411
+ if (artifactId) {
412
+ // Full output is stored under the artifact ID
385
413
  }
386
414
  }
387
415
  });
388
416
  ```
389
417
 
390
- Available guards: `isBashToolResult`, `isReadToolResult`, `isEditToolResult`, `isWriteToolResult`, `isGrepToolResult`, `isFindToolResult`, `isLsToolResult`.
391
-
392
418
  ## HookContext
393
419
 
394
420
  Every handler receives `ctx: HookContext`:
@@ -488,7 +514,8 @@ See [examples/hooks/qna.ts](../examples/hooks/qna.ts) for a loader pattern and [
488
514
 
489
515
  ### ctx.hasUI
490
516
 
491
- `false` in print mode (`-p`), JSON print mode, and RPC mode. Always check before using `ctx.ui`:
517
+ `false` in print mode (`-p`) and JSON print mode. RPC mode provides UI via the host, so `ctx.hasUI` is true.
518
+ Always check before using `ctx.ui`:
492
519
 
493
520
  ```typescript
494
521
  if (ctx.hasUI) {
@@ -504,7 +531,7 @@ Current working directory.
504
531
 
505
532
  ### ctx.sessionManager
506
533
 
507
- Read-only access to session state. See `ReadonlySessionManager` in [`src/core/session-manager.ts`](../src/core/session-manager.ts).
534
+ Read-only access to session state. See `ReadonlySessionManager` in [`src/session/session-manager.ts`](../src/session/session-manager.ts).
508
535
 
509
536
  ```typescript
510
537
  // Session info
@@ -538,7 +565,7 @@ Access to models and API keys:
538
565
  const apiKey = await ctx.modelRegistry.getApiKey(model);
539
566
 
540
567
  // Get available models
541
- const models = ctx.modelRegistry.getAvailableModels();
568
+ const models = ctx.modelRegistry.getAvailable();
542
569
  ```
543
570
 
544
571
  ### ctx.model
@@ -567,7 +594,7 @@ if (ctx.isIdle()) {
567
594
  Abort the current agent operation (fire-and-forget, does not wait):
568
595
 
569
596
  ```typescript
570
- await ctx.abort();
597
+ ctx.abort();
571
598
  ```
572
599
 
573
600
  ### ctx.hasQueuedMessages()
@@ -643,24 +670,28 @@ const result = await ctx.navigateTree("entry-id-456", {
643
670
 
644
671
  Subscribe to events. See [Events](#events) for all event types.
645
672
 
646
- ### pi.sendMessage(message, triggerTurn?)
673
+ ### pi.sendMessage(message, options?)
647
674
 
648
675
  Inject a message into the session. Creates a `CustomMessageEntry` that participates in the LLM context.
649
676
 
650
677
  ```typescript
651
- pi.sendMessage({
652
- customType: "my-hook", // Your hook's identifier
653
- content: "Message text", // string or (TextContent | ImageContent)[]
654
- display: true, // Show in TUI
655
- details: { ... }, // Optional metadata (not sent to LLM)
656
- }, triggerTurn); // If true, triggers LLM response
678
+ pi.sendMessage(
679
+ {
680
+ customType: "my-hook", // Your hook's identifier
681
+ content: "Message text", // string or (TextContent | ImageContent)[]
682
+ display: true, // Show in TUI
683
+ details: { ... }, // Optional metadata (not sent to LLM)
684
+ },
685
+ { triggerTurn: true }, // Trigger a new LLM response if idle
686
+ );
657
687
  ```
658
688
 
659
689
  **Storage and timing:**
660
690
 
661
691
  - The message is appended to the session file immediately as a `CustomMessageEntry`
662
692
  - If the agent is currently streaming, the message is queued and appended after the current turn
663
- - If `triggerTurn` is true and the agent is idle, a new agent loop starts
693
+ - If `options.triggerTurn` is true and the agent is idle, a new agent loop starts
694
+ - `options.deliverAs` chooses how to enqueue the message (`"steer"` or `"followUp"`)
664
695
 
665
696
  **LLM context:**
666
697
 
@@ -708,7 +739,7 @@ pi.registerCommand("stats", {
708
739
 
709
740
  For long-running commands (e.g., LLM calls), use `ctx.ui.custom()` with a loader. See [examples/hooks/qna.ts](../examples/hooks/qna.ts).
710
741
 
711
- To trigger LLM after command, call `pi.sendMessage(..., true)`.
742
+ To trigger the LLM after a command, call `pi.sendMessage(..., { triggerTurn: true })`.
712
743
 
713
744
  ### pi.registerMessageRenderer(customType, renderer)
714
745
 
@@ -736,13 +767,13 @@ pi.registerMessageRenderer("my-hook", (message, options, theme) => {
736
767
 
737
768
  ```typescript
738
769
  type HookMessageRenderer = (
739
- message: CustomMessageEntry,
770
+ message: HookMessage,
740
771
  options: { expanded: boolean },
741
772
  theme: Theme
742
- ) => Component | null;
773
+ ) => Component | undefined;
743
774
  ```
744
775
 
745
- Return `null` to use default rendering. The returned component is wrapped in a styled Box by the TUI. See [tui.md](tui.md) for component details.
776
+ Return `undefined` to use default rendering. The returned component is wrapped in a styled Box by the TUI. See [tui.md](tui.md) for component details.
746
777
 
747
778
  ### pi.exec(command, args, options?)
748
779
 
@@ -757,12 +788,18 @@ const result = await pi.exec("git", ["status"], {
757
788
  // result.stdout, result.stderr, result.code, result.killed
758
789
  ```
759
790
 
791
+ ### pi.logger / pi.typebox / pi.pi
792
+
793
+ - `pi.logger` is the shared logger (avoid `console.*` to keep the TUI clean)
794
+ - `pi.typebox` exposes `@sinclair/typebox` for schema definitions
795
+ - `pi.pi` exposes `@oh-my-pi/pi-coding-agent` exports (components, helpers)
796
+
760
797
  ## Examples
761
798
 
762
799
  ### Permission Gate
763
800
 
764
801
  ```typescript
765
- import type { HookAPI } from "@oh-my-pi/pi-coding-agent";
802
+ import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
766
803
 
767
804
  export default function (pi: HookAPI) {
768
805
  const dangerous = [/\brm\s+(-rf?|--recursive)/i, /\bsudo\b/i];
@@ -785,7 +822,7 @@ export default function (pi: HookAPI) {
785
822
  ### Protected Paths
786
823
 
787
824
  ```typescript
788
- import type { HookAPI } from "@oh-my-pi/pi-coding-agent";
825
+ import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
789
826
 
790
827
  export default function (pi: HookAPI) {
791
828
  const protectedPaths = [".env", ".git/", "node_modules/"];
@@ -805,7 +842,7 @@ export default function (pi: HookAPI) {
805
842
  ### Git Checkpoint
806
843
 
807
844
  ```typescript
808
- import type { HookAPI } from "@oh-my-pi/pi-coding-agent";
845
+ import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
809
846
 
810
847
  export default function (pi: HookAPI) {
811
848
  const checkpoints = new Map<string, string>();
@@ -844,13 +881,15 @@ See [examples/hooks/snake.ts](../examples/hooks/snake.ts) for a complete example
844
881
 
845
882
  ## Mode Behavior
846
883
 
847
- | Mode | UI Methods | Notes |
848
- | ------------ | -------------------------- | -------------------------- |
849
- | Interactive | Full TUI | Normal operation |
850
- | RPC | JSON protocol | Host handles UI |
851
- | Print (`-p`) | No-op (returns null/false) | Hooks run but can't prompt |
884
+ | Mode | UI Methods | Notes |
885
+ | --------------- | -------------------------- | ------------------------------------------ |
886
+ | Interactive | Full TUI | Normal operation |
887
+ | RPC | UI via RPC | Host handles UI, `ctx.hasUI` is true |
888
+ | Print (`-p`) | No-op (returns undefined/false) | Hooks run but can't prompt (`ctx.hasUI`=false) |
852
889
 
853
- In print mode, `select()` returns `undefined`, `confirm()` returns `false`, `input()` returns `undefined`, `getEditorText()` returns `""`, and `setEditorText()`/`setStatus()` are no-ops. Design hooks to handle this by checking `ctx.hasUI`.
890
+ In print mode (including JSON output), `select()` returns `undefined`, `confirm()` returns `false`, `input()` returns
891
+ `undefined`, `getEditorText()` returns `""`, and `setEditorText()`/`setStatus()` are no-ops. Design hooks to handle this
892
+ by checking `ctx.hasUI`.
854
893
 
855
894
  ## Error Handling
856
895
 
@@ -6,20 +6,24 @@
6
6
  - `jupyter-kernel-gateway` (`kernel_gateway` module) and `ipykernel` installed in the selected Python environment
7
7
 
8
8
  Install:
9
+
9
10
  ```bash
10
11
  python -m pip install jupyter_kernel_gateway ipykernel
11
12
  ```
12
13
 
13
14
  ## How It Works
14
15
 
15
- The Python tool starts a Jupyter Kernel Gateway process locally, which manages an IPython kernel. All code execution goes through the gateway's REST and WebSocket APIs.
16
+ The Python tool uses a Jupyter Kernel Gateway and talks to it over REST and WebSocket APIs.
17
+ By default it uses a shared local gateway so multiple pi instances reuse the same gateway process.
18
+
19
+ Shared-gateway startup flow:
16
20
 
17
- Startup flow:
18
- 1. Spawn `python -m kernel_gateway` on a random available port
19
- 2. Wait for gateway to become ready (`GET /api/kernelspecs`)
20
- 3. Create a kernel (`POST /api/kernels`)
21
- 4. Connect WebSocket for execution messages
22
- 5. Run prelude code (helper functions)
21
+ 1. Filter the environment and resolve the Python runtime (including venv detection)
22
+ 2. Acquire the shared gateway (reuse a healthy gateway or spawn `python -m kernel_gateway` on 127.0.0.1:PORT)
23
+ 3. Wait for gateway readiness (`GET /api/kernelspecs`)
24
+ 4. Create a kernel (`POST /api/kernels`)
25
+ 5. Connect WebSocket for execution messages
26
+ 6. Initialize kernel environment, run prelude helpers, and load extension modules
23
27
 
24
28
  ## External Gateway Support
25
29
 
@@ -27,19 +31,21 @@ Instead of spawning a local gateway, you can connect to an already-running Jupyt
27
31
 
28
32
  ```bash
29
33
  # Connect to external gateway
30
- export OMP_PYTHON_GATEWAY_URL="http://127.0.0.1:8888"
34
+ export PI_PYTHON_GATEWAY_URL="http://127.0.0.1:8888"
31
35
 
32
36
  # Optional: auth token if gateway requires it (KG_AUTH_TOKEN)
33
- export OMP_PYTHON_GATEWAY_TOKEN="your-token-here"
37
+ export PI_PYTHON_GATEWAY_TOKEN="your-token-here"
34
38
  ```
35
39
 
36
- When `OMP_PYTHON_GATEWAY_URL` is set:
40
+ When `PI_PYTHON_GATEWAY_URL` is set:
41
+
37
42
  - No local gateway process is spawned
38
43
  - Kernels are created on the external gateway
39
44
  - The gateway process is not killed on shutdown
40
45
  - Availability check uses `/api/kernelspecs` endpoint instead of local module check
41
46
 
42
47
  This is useful for:
48
+
43
49
  - Remote kernel execution
44
50
  - Shared kernel environments
45
51
  - Pre-configured gateway setups
@@ -47,31 +53,58 @@ This is useful for:
47
53
  ## Environment Propagation
48
54
 
49
55
  - The kernel inherits a filtered environment (explicit allowlist + denylist)
50
- - `PYTHONPATH` includes the working directory and any existing `PYTHONPATH` value
56
+ - Allowlisted prefixes include `LC_`, `XDG_`, and `PI_`; known API-key vars are removed
57
+ - `PYTHONPATH` is passed through if present
51
58
  - Virtual environments are detected via `VIRTUAL_ENV`, `.venv/`, or `venv/` and preferred when present
52
59
 
60
+ ## Prelude Extensions
61
+
62
+ Optional `.py` modules are loaded after the prelude from:
63
+
64
+ - `~/.omp/agent/modules` and `~/.pi/agent/modules`
65
+ - `<project>/.omp/modules` and `<project>/.pi/modules`
66
+
67
+ Project modules override user modules with the same filename.
68
+
53
69
  ## Kernel Modes
54
70
 
55
71
  Settings under `python` control exposure and reuse:
56
- - `toolMode`: `ipy-only` (default), `bash-only`, `both`
57
- - `kernelMode`: `session` (default, queued), `per-call`
58
72
 
59
- ## Shell Bridge
73
+ - `toolMode`: `both` (default), `ipy-only`, `bash-only`
74
+ - `kernelMode`: `session` (default) or `per-call`
75
+ - `sharedGateway`: `true` (default). Setting to `false` throws an error because local (per-process) gateways are not supported; the shared gateway is required.
76
+
77
+ Mode behavior:
78
+
79
+ - `session`: reuse kernels per session id, serialize execution, evict after 5 minutes of idle time (max 4 sessions)
80
+ - `per-call`: create a fresh kernel per tool call and shut it down afterward
81
+
82
+ Environment override:
83
+
84
+ - `PI_PY=0|bash` → `bash-only`
85
+ - `PI_PY=1|py` → `ipy-only`
86
+ - `PI_PY=mix|both` → `both`
87
+
88
+ ## Shell Helper
60
89
 
61
- The Python prelude exposes `bash()` which:
62
- - Sources the shell snapshot when `OMP_SHELL_SNAPSHOT` is set
63
- - Runs via `bash -lc` when available, with OS fallbacks
90
+ The Python prelude exposes `run()` which executes a shell command via `bash -c` (or `sh -c` fallback)
91
+ and returns a `ShellResult` with `stdout`, `stderr`, and `code`.
64
92
 
65
93
  ## Output Handling
66
94
 
67
95
  - Streams `stdout`/`stderr` as text
96
+ - `application/x-omp-status` emits structured status events for the TUI
68
97
  - `image/png` display data renders inline in TUI
69
98
  - `application/json` display data renders as a collapsible tree
99
+ - `text/markdown` is rendered as-is, `text/plain` is used as a fallback
70
100
  - `text/html` display data is converted to basic markdown
71
101
 
72
102
  ## Troubleshooting
73
103
 
74
104
  - **Kernel unavailable**: Ensure `python` + `jupyter-kernel-gateway` + `ipykernel` are installed; the session will fall back to bash-only.
75
- - **External gateway unreachable**: Check the URL is correct and the gateway is running. If auth is required, set `OMP_PYTHON_GATEWAY_TOKEN`.
76
- - **IPC tracing**: Set `OMP_PYTHON_IPC_TRACE=1` to log kernel message flow.
105
+ - **Python mode override**: Check `python.toolMode` or `PI_PY` if the Python tool is missing.
106
+ - **Shared gateway disabled**: `python.sharedGateway=false` causes the Python tool to error because local (per-process) gateways are not supported.
107
+ - **Skip preflight checks**: Set `PI_PYTHON_SKIP_CHECK=1` to bypass kernel availability checks.
108
+ - **External gateway unreachable**: Check the URL is correct and the gateway is running. If auth is required, set `PI_PYTHON_GATEWAY_TOKEN`.
109
+ - **IPC tracing**: Set `PI_PYTHON_IPC_TRACE=1` to log kernel message flow.
77
110
  - **Stdin requests**: Interactive input is not supported; refactor code to avoid `input()` or provide data programmatically.