@oh-my-pi/pi-coding-agent 12.7.6 → 12.8.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 (56) hide show
  1. package/CHANGELOG.md +37 -37
  2. package/README.md +9 -1052
  3. package/package.json +7 -7
  4. package/src/cli/args.ts +1 -0
  5. package/src/cli/update-cli.ts +49 -35
  6. package/src/cli/web-search-cli.ts +3 -2
  7. package/src/commands/web-search.ts +1 -0
  8. package/src/config/model-registry.ts +6 -0
  9. package/src/config/settings-schema.ts +25 -3
  10. package/src/config/settings.ts +1 -0
  11. package/src/extensibility/extensions/wrapper.ts +20 -13
  12. package/src/extensibility/slash-commands.ts +12 -91
  13. package/src/lsp/client.ts +24 -27
  14. package/src/lsp/index.ts +92 -42
  15. package/src/mcp/config-writer.ts +33 -0
  16. package/src/mcp/config.ts +6 -1
  17. package/src/mcp/types.ts +1 -0
  18. package/src/modes/components/custom-editor.ts +8 -5
  19. package/src/modes/components/settings-defs.ts +2 -1
  20. package/src/modes/controllers/command-controller.ts +12 -6
  21. package/src/modes/controllers/input-controller.ts +21 -186
  22. package/src/modes/controllers/mcp-command-controller.ts +60 -3
  23. package/src/modes/interactive-mode.ts +2 -2
  24. package/src/modes/types.ts +1 -1
  25. package/src/sdk.ts +23 -1
  26. package/src/secrets/index.ts +116 -0
  27. package/src/secrets/obfuscator.ts +269 -0
  28. package/src/secrets/regex.ts +21 -0
  29. package/src/session/agent-session.ts +143 -21
  30. package/src/session/compaction/branch-summarization.ts +2 -2
  31. package/src/session/compaction/compaction.ts +10 -3
  32. package/src/session/compaction/utils.ts +25 -1
  33. package/src/slash-commands/builtin-registry.ts +419 -0
  34. package/src/web/scrapers/github.ts +50 -12
  35. package/src/web/search/index.ts +5 -5
  36. package/src/web/search/provider.ts +13 -2
  37. package/src/web/search/providers/brave.ts +165 -0
  38. package/src/web/search/types.ts +1 -1
  39. package/docs/compaction.md +0 -436
  40. package/docs/config-usage.md +0 -176
  41. package/docs/custom-tools.md +0 -585
  42. package/docs/environment-variables.md +0 -257
  43. package/docs/extension-loading.md +0 -106
  44. package/docs/extensions.md +0 -1342
  45. package/docs/fs-scan-cache-architecture.md +0 -50
  46. package/docs/hooks.md +0 -906
  47. package/docs/models.md +0 -234
  48. package/docs/python-repl.md +0 -110
  49. package/docs/rpc.md +0 -1173
  50. package/docs/sdk.md +0 -1039
  51. package/docs/session-tree-plan.md +0 -84
  52. package/docs/session.md +0 -368
  53. package/docs/skills.md +0 -254
  54. package/docs/theme.md +0 -696
  55. package/docs/tree.md +0 -206
  56. package/docs/tui.md +0 -487
package/docs/sdk.md DELETED
@@ -1,1039 +0,0 @@
1
- > omp can help you use the SDK. Ask it to build an integration for your use case.
2
-
3
- # SDK
4
-
5
- The SDK provides programmatic access to omp's agent capabilities. Use it to embed omp in other applications, build custom interfaces, or integrate with automated workflows.
6
-
7
- **Example use cases:**
8
-
9
- - Build a custom UI (web, desktop, mobile)
10
- - Integrate agent capabilities into existing applications
11
- - Create automated pipelines with agent reasoning
12
- - Build custom tools that spawn sub-agents
13
- - Test agent behavior programmatically
14
-
15
- See [examples/sdk/](../examples/sdk/) for working examples from minimal to full control.
16
-
17
- ## Quick Start
18
-
19
- ```typescript
20
- import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@oh-my-pi/pi-coding-agent";
21
-
22
- // Set up credential storage and model registry
23
- const authStorage = await discoverAuthStorage();
24
- const modelRegistry = discoverModels(authStorage);
25
-
26
- const { session, modelFallbackMessage } = await createAgentSession({
27
- sessionManager: SessionManager.inMemory(),
28
- authStorage,
29
- modelRegistry,
30
- });
31
-
32
- if (modelFallbackMessage) {
33
- process.stderr.write(`${modelFallbackMessage}\n`);
34
- }
35
-
36
- session.subscribe((event) => {
37
- if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
38
- process.stdout.write(event.assistantMessageEvent.delta);
39
- }
40
- });
41
-
42
- await session.prompt("What files are in the current directory?");
43
- ```
44
-
45
- ## Installation
46
-
47
- ```bash
48
- bun add @oh-my-pi/pi-coding-agent
49
- ```
50
-
51
- The SDK is included in the main package. No separate installation needed.
52
-
53
- ## Core Concepts
54
-
55
- ### createAgentSession()
56
-
57
- The main factory function. Creates an `AgentSession` with configurable options.
58
-
59
- **Philosophy:** "Omit to discover, provide to override."
60
-
61
- - Omit an option → omp discovers/loads from standard locations
62
- - Provide an option → your value is used, discovery skipped for that option
63
-
64
- ```typescript
65
- import { createAgentSession } from "@oh-my-pi/pi-coding-agent";
66
- import systemPrompt from "./SYSTEM.md" with { type: "text" };
67
-
68
- // Minimal: all defaults (discovers from cwd + config dirs and ~/.omp/agent)
69
- const { session } = await createAgentSession();
70
-
71
- // Custom: override specific options
72
- const { session } = await createAgentSession({
73
- model: myModel,
74
- systemPrompt,
75
- toolNames: ["read", "bash", "edit"], // Filter to specific tools
76
- sessionManager: SessionManager.inMemory(),
77
- });
78
- ```
79
-
80
- ### AgentSession
81
-
82
- The session manages the agent lifecycle, message history, and event streaming.
83
-
84
- ```typescript
85
- interface AgentSession {
86
- // Prompting
87
- prompt(text: string, options?: PromptOptions): Promise<void>;
88
- sendUserMessage(
89
- content: string | (TextContent | ImageContent)[],
90
- options?: { deliverAs?: "steer" | "followUp" }
91
- ): Promise<void>;
92
- steer(text: string): void;
93
- followUp(text: string): void;
94
-
95
- // Subscribe to events (returns unsubscribe function)
96
- subscribe(listener: (event: AgentSessionEvent) => void): () => void;
97
-
98
- // Session info
99
- sessionFile: string | undefined; // undefined for in-memory
100
- sessionId: string;
101
- sessionName: string | undefined;
102
-
103
- // Model control
104
- setModel(model: Model, role?: ModelRole): Promise<void>;
105
- setModelTemporary(model: Model): Promise<void>;
106
- setThinkingLevel(level: ThinkingLevel): void;
107
- cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
108
- cycleRoleModels(
109
- direction?: "forward" | "backward"
110
- ): Promise<{ model: Model; thinkingLevel: ThinkingLevel; role: ModelRole } | undefined>;
111
- cycleThinkingLevel(): ThinkingLevel | undefined;
112
-
113
- // State access
114
- agent: Agent;
115
- sessionManager: SessionManager;
116
- settings: Settings;
117
- model: Model | undefined;
118
- thinkingLevel: ThinkingLevel;
119
- messages: AgentMessage[];
120
- isStreaming: boolean;
121
- isCompacting: boolean;
122
- isRetrying: boolean;
123
-
124
- // Session management
125
- newSession(options?: NewSessionOptions): Promise<boolean>; // Returns false if cancelled by extension
126
- fork(): Promise<boolean>; // Creates a new session file
127
-
128
- // Branching
129
- branch(entryId: string): Promise<{ selectedText: string; cancelled: boolean }>;
130
- navigateTree(
131
- targetId: string,
132
- options?: { summarize?: boolean; customInstructions?: string }
133
- ): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean; summaryEntry?: BranchSummaryEntry }>;
134
-
135
- // Custom message injection
136
- sendCustomMessage<T>(
137
- message: { customType: string; content: T; display?: boolean; details?: unknown },
138
- options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" }
139
- ): Promise<void>;
140
-
141
- // Compaction
142
- compact(
143
- customInstructions?: string,
144
- options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void }
145
- ): Promise<CompactionResult>;
146
- abortCompaction(): void;
147
-
148
- // Utilities
149
- getSessionStats(): SessionStats;
150
- formatSessionAsText(): string;
151
- formatCompactContext(): string;
152
- exportToHtml(outputPath?: string): Promise<string>;
153
- handoff(customInstructions?: string): Promise<{ document: string } | undefined>;
154
-
155
- // Abort current operation
156
- abort(): Promise<void>;
157
-
158
- // Cleanup
159
- dispose(): Promise<void>;
160
- }
161
- ```
162
-
163
- ### Agent and AgentState
164
-
165
- The `Agent` class (from `@oh-my-pi/pi-agent-core`) handles the core LLM interaction. Access it via `session.agent`.
166
-
167
- ```typescript
168
- // Access current state
169
- const state = session.agent.state;
170
-
171
- // state.messages: AgentMessage[] - conversation history
172
- // state.model: Model - current model
173
- // state.thinkingLevel: ThinkingLevel - current thinking level
174
- // state.systemPrompt: string - system prompt
175
- // state.tools: Tool[] - available tools
176
-
177
- // Replace messages (useful for branching, restoration)
178
- session.agent.replaceMessages(messages);
179
-
180
- // Wait for agent to finish processing
181
- await session.agent.waitForIdle();
182
- ```
183
-
184
- ### Events
185
-
186
- Subscribe to events to receive streaming output and lifecycle notifications.
187
-
188
- ```typescript
189
- session.subscribe((event) => {
190
- switch (event.type) {
191
- // Streaming text from assistant
192
- case "message_update":
193
- if (event.assistantMessageEvent.type === "text_delta") {
194
- process.stdout.write(event.assistantMessageEvent.delta);
195
- }
196
- if (event.assistantMessageEvent.type === "thinking_delta") {
197
- // Thinking output (if thinking enabled)
198
- }
199
- break;
200
-
201
- // Tool execution
202
- case "tool_execution_start":
203
- console.log(`Tool: ${event.toolName}`);
204
- break;
205
- case "tool_execution_update":
206
- // Streaming tool output
207
- break;
208
- case "tool_execution_end":
209
- console.log(`Result: ${event.isError ? "error" : "success"}`);
210
- break;
211
-
212
- // Message lifecycle
213
- case "message_start":
214
- // New message starting
215
- break;
216
- case "message_end":
217
- // Message complete
218
- break;
219
-
220
- // Agent lifecycle
221
- case "agent_start":
222
- // Agent started processing prompt
223
- break;
224
- case "agent_end":
225
- // Agent finished (event.messages contains new messages)
226
- break;
227
-
228
- // Turn lifecycle (one LLM response + tool calls)
229
- case "turn_start":
230
- break;
231
- case "turn_end":
232
- // event.message: assistant response
233
- // event.toolResults: tool results from this turn
234
- break;
235
-
236
- // Session events (auto-compaction, retry, TTSR, todo reminders)
237
- case "auto_compaction_start":
238
- case "auto_compaction_end":
239
- case "auto_retry_start":
240
- case "auto_retry_end":
241
- case "ttsr_triggered":
242
- // event.rules
243
- break;
244
- case "todo_reminder":
245
- // event.todos
246
- break;
247
- }
248
- });
249
- ```
250
-
251
- ## Options Reference
252
-
253
- ### Directories
254
-
255
- ```typescript
256
- const { session } = await createAgentSession({
257
- // Working directory for project-local discovery
258
- cwd: process.cwd(), // default
259
-
260
- // Global config directory
261
- agentDir: "~/.omp/agent", // default (expands ~)
262
- });
263
- ```
264
-
265
- `cwd` is used for:
266
-
267
- - Project config discovery (`.omp/`, `.pi/`, `.claude/`, `.codex/`, `.gemini/`)
268
- - Project extensions/tools/skills/commands (via config dirs)
269
- - Context files (`AGENTS.md` walking up from cwd)
270
- - Session directory naming (via `SessionManager.create(cwd)`)
271
-
272
- `agentDir` is used for:
273
-
274
- - Global settings (`config.yml` + `agent.db`)
275
- - Primary auth/models locations (`agent.db`, `models.yml`, `models.json`)
276
- - Prompt templates (`prompts/`)
277
- - Custom TS commands (`commands/`)
278
-
279
- ### Model
280
-
281
- ```typescript
282
- import { getModel } from "@oh-my-pi/pi-ai";
283
- import { discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent";
284
-
285
- const authStorage = await discoverAuthStorage();
286
- const modelRegistry = discoverModels(authStorage);
287
-
288
- // Find specific built-in model (doesn't check if API key exists)
289
- const opus = getModel("anthropic", "claude-opus-4-5");
290
- if (!opus) throw new Error("Model not found");
291
-
292
- // Find any model by provider/id, including custom models from models.yml
293
- // (doesn't check if API key exists)
294
- const customModel = modelRegistry.find("my-provider", "my-model");
295
-
296
- // Get all models that have valid API keys configured
297
- const available = modelRegistry.getAvailable();
298
-
299
- const { session } = await createAgentSession({
300
- model: opus,
301
- thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh
302
-
303
- // Models for cycling (Ctrl+P in interactive mode)
304
- scopedModels: [
305
- { model: opus, thinkingLevel: "high" },
306
- { model: haiku, thinkingLevel: "off" },
307
- ],
308
-
309
- authStorage,
310
- modelRegistry,
311
- });
312
- ```
313
-
314
- If no model is provided:
315
-
316
- 1. Tries to restore from session (if continuing)
317
- 2. Uses default from settings
318
- 3. Falls back to first available model
319
-
320
- > See [examples/sdk/02-custom-model.ts](../examples/sdk/02-custom-model.ts)
321
-
322
- ### API Keys and OAuth
323
-
324
- API key resolution priority (handled by AuthStorage):
325
-
326
- 1. Runtime overrides (via `setRuntimeApiKey`, not persisted)
327
- 2. Stored credentials in `agent.db` (API keys or OAuth tokens)
328
- 3. Environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.)
329
- 4. Fallback resolver (for custom provider keys from `models.yml`)
330
-
331
- `discoverAuthStorage` opens the `agent.db` SQLite database in the agent directory.
332
-
333
- ```typescript
334
- import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent";
335
-
336
- // Default: uses agentDir/agent.db and agentDir/models.yml
337
- const authStorage = await discoverAuthStorage();
338
- const modelRegistry = discoverModels(authStorage);
339
-
340
- const { session } = await createAgentSession({
341
- sessionManager: SessionManager.inMemory(),
342
- authStorage,
343
- modelRegistry,
344
- });
345
-
346
- // Runtime API key override (not persisted to disk)
347
- authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
348
-
349
- // Custom auth storage location (use create(), constructor is private)
350
- const customAuth = await AuthStorage.create("/my/app/agent.db");
351
- const customRegistry = new ModelRegistry(customAuth, "/my/app/models.yml");
352
-
353
- const { session } = await createAgentSession({
354
- sessionManager: SessionManager.inMemory(),
355
- authStorage: customAuth,
356
- modelRegistry: customRegistry,
357
- });
358
-
359
- // No custom models.yml (built-in models only)
360
- const simpleRegistry = new ModelRegistry(authStorage);
361
- ```
362
-
363
- > See [examples/sdk/09-api-keys-and-oauth.ts](../examples/sdk/09-api-keys-and-oauth.ts)
364
-
365
- ### System Prompt
366
-
367
- ```typescript
368
- import systemPrompt from "./SYSTEM.md" with { type: "text" };
369
-
370
- const { session } = await createAgentSession({
371
- // Replace entirely with a static prompt
372
- systemPrompt,
373
- });
374
-
375
- const { session: modified } = await createAgentSession({
376
- // Or modify default (receives default, returns modified)
377
- systemPrompt: (defaultPrompt) => {
378
- return `${defaultPrompt}\n\n## Additional Rules\n- Be concise`;
379
- },
380
- });
381
- ```
382
-
383
- > See [examples/sdk/03-custom-prompt.ts](../examples/sdk/03-custom-prompt.ts)
384
-
385
- ### Tools
386
-
387
- By default, `createAgentSession` creates all built-in tools automatically. You can filter which tools are available using `toolNames`:
388
-
389
- ```typescript
390
- // Use all built-in tools (default)
391
- const { session } = await createAgentSession();
392
-
393
- // Filter to specific tools
394
- const { session } = await createAgentSession({
395
- toolNames: ["read", "grep", "find"], // Read-only tools
396
- });
397
-
398
- `toolNames` is an allowlist for built-ins; custom tools are always included even if not listed.
399
- ```
400
-
401
- #### Available Built-in Tools
402
-
403
- All tools are defined in `BUILTIN_TOOLS`:
404
-
405
- - `ask` - Interactive user prompts (requires UI)
406
- - `bash` - Shell command execution
407
- - `python` - Python REPL execution
408
- - `calc` - Calculator
409
- - `ssh` - Remote SSH execution
410
- - `edit` - Surgical file editing
411
- - `find` - File search by glob patterns
412
- - `grep` - Content search with regex
413
- - `lsp` - Language server protocol integration
414
- - `notebook` - Jupyter notebook editing
415
- - `read` - File reading (text and images)
416
- - `browser` - Puppeteer-based web browser
417
- - `task` - Subagent spawning
418
- - `todo_write` - Todo file management
419
- - `fetch` - URL fetching
420
- - `web_search` - Web search
421
- - `write` - File writing
422
-
423
- Hidden tools (not in `BUILTIN_TOOLS`) are available but excluded unless requested:
424
-
425
- - `submit_result` - Required for subagent structured output (use `requireSubmitResultTool` or include in `toolNames`)
426
- - `report_finding` - Security review reporting
427
- - `exit_plan_mode` - Plan mode control
428
-
429
- #### Creating Tools Manually
430
-
431
- For advanced use cases, you can create tools directly using `createTools`:
432
-
433
- ```typescript
434
- import { createTools, Settings, type ToolSession } from "@oh-my-pi/pi-coding-agent";
435
-
436
- const settings = await Settings.init({ cwd: "/path/to/project" });
437
-
438
- const session: ToolSession = {
439
- cwd: "/path/to/project",
440
- hasUI: false,
441
- getSessionFile: () => null,
442
- getSessionSpawns: () => "*",
443
- settings,
444
- };
445
-
446
- const tools = await createTools(session);
447
- ```
448
-
449
- **When you don't need factories:**
450
-
451
- - If you omit `toolNames`, omp automatically creates them with the correct `cwd`
452
- - If you use `process.cwd()` as your `cwd`, the pre-built instances work fine
453
-
454
- **When you must use factories:**
455
-
456
- - When you specify both `cwd` (different from `process.cwd()`) AND custom tools
457
-
458
- ### Custom Tools
459
-
460
- ```typescript
461
- import { Type } from "@sinclair/typebox";
462
- import { createAgentSession, type CustomTool } from "@oh-my-pi/pi-coding-agent";
463
-
464
- // Inline custom tool
465
- const myTool: CustomTool = {
466
- name: "my_tool",
467
- label: "My Tool",
468
- description: "Does something useful",
469
- parameters: Type.Object({
470
- input: Type.String({ description: "Input value" }),
471
- }),
472
- execute: async (toolCallId, params, onUpdate, ctx, signal) => ({
473
- content: [{ type: "text", text: `Result: ${params.input}` }],
474
- }),
475
- // Optional session lifecycle handler
476
- onSession: async (event, ctx) => {
477
- if (event.reason === "shutdown") {
478
- // cleanup
479
- }
480
- },
481
- };
482
-
483
- // Add custom tools (merged with built-in tools)
484
- const { session } = await createAgentSession({
485
- customTools: [myTool],
486
- });
487
- ```
488
-
489
- ### Extensions
490
-
491
- Extensions intercept agent events and can register custom tools/commands. Hooks remain for legacy compatibility.
492
-
493
- ```typescript
494
- import { createAgentSession, discoverExtensions, type ExtensionFactory } from "@oh-my-pi/pi-coding-agent";
495
-
496
- // Inline extension
497
- const loggingExtension: ExtensionFactory = (api) => {
498
- // Log tool calls
499
- api.on("tool_call", async (event) => {
500
- console.log(`Tool: ${event.toolName}`);
501
- return undefined; // Don't block
502
- });
503
-
504
- // Block dangerous commands
505
- api.on("tool_call", async (event) => {
506
- if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
507
- return { block: true, reason: "Dangerous command" };
508
- }
509
- return undefined;
510
- });
511
-
512
- // Register custom slash command
513
- api.registerCommand("stats", {
514
- description: "Show session stats",
515
- handler: async (args, ctx) => {
516
- const entries = ctx.sessionManager.getEntries();
517
- ctx.ui.notify(`${entries.length} entries`, "info");
518
- },
519
- });
520
- };
521
-
522
- // Merge with discovery (default behavior)
523
- const { session } = await createAgentSession({
524
- extensions: [loggingExtension],
525
- });
526
-
527
- // Replace discovery
528
- const { session } = await createAgentSession({
529
- extensions: [loggingExtension],
530
- disableExtensionDiscovery: true,
531
- });
532
-
533
- // Disable all extensions
534
- const { session } = await createAgentSession({
535
- extensions: [],
536
- disableExtensionDiscovery: true,
537
- });
538
-
539
- // Use preloaded extensions (skip discovery I/O)
540
- const discovered = await discoverExtensions();
541
- const { session } = await createAgentSession({
542
- preloadedExtensions: discovered,
543
- extensions: [loggingExtension],
544
- });
545
-
546
- // Add paths without replacing discovery
547
- const { session } = await createAgentSession({
548
- additionalExtensionPaths: ["/extra/extensions"],
549
- });
550
- ```
551
-
552
- Extension API methods:
553
-
554
- - `api.on(event, handler)` - Subscribe to events
555
- - `api.registerTool(definition)` - Register a custom tool
556
- - `api.registerCommand(name, options)` - Register custom slash command
557
- - `api.registerMessageRenderer(customType, renderer)` - Custom TUI rendering
558
- - `api.exec(command, args, options?)` - Execute shell commands
559
-
560
- > See [examples/sdk/06-extensions.ts](../examples/sdk/06-extensions.ts) and [docs/extensions.md](extensions.md)
561
-
562
- ### Skills
563
-
564
- ```typescript
565
- import { createAgentSession, discoverSkills, type Skill } from "@oh-my-pi/pi-coding-agent";
566
-
567
- // Discover and filter
568
- const { skills: allSkills, warnings } = await discoverSkills();
569
- const filtered = allSkills.filter((s) => s.name.includes("search"));
570
-
571
- // Custom skill
572
- const mySkill: Skill = {
573
- name: "my-skill",
574
- description: "Custom instructions",
575
- filePath: "/path/to/SKILL.md",
576
- baseDir: "/path/to",
577
- source: "custom",
578
- };
579
-
580
- const { session } = await createAgentSession({
581
- skills: [...filtered, mySkill],
582
- });
583
-
584
- // Disable skills
585
- const { session } = await createAgentSession({
586
- skills: [],
587
- });
588
- ```
589
-
590
- > See [examples/sdk/04-skills.ts](../examples/sdk/04-skills.ts)
591
-
592
- ### Context Files
593
-
594
- ```typescript
595
- import { createAgentSession, discoverContextFiles } from "@oh-my-pi/pi-coding-agent";
596
-
597
- // Discover AGENTS.md files
598
- const discovered = await discoverContextFiles();
599
-
600
- // Add custom context
601
- const { session } = await createAgentSession({
602
- contextFiles: [
603
- ...discovered,
604
- {
605
- path: "/virtual/AGENTS.md",
606
- content: "# Guidelines\n\n- Be concise\n- Use TypeScript",
607
- },
608
- ],
609
- });
610
-
611
- // Disable context files
612
- const { session } = await createAgentSession({
613
- contextFiles: [],
614
- });
615
- ```
616
-
617
- > See [examples/sdk/07-context-files.ts](../examples/sdk/07-context-files.ts)
618
-
619
- ### Slash Commands
620
-
621
- ```typescript
622
- import { createAgentSession, discoverSlashCommands, type FileSlashCommand } from "@oh-my-pi/pi-coding-agent";
623
-
624
- const discovered = await discoverSlashCommands();
625
-
626
- const customCommand: FileSlashCommand = {
627
- name: "deploy",
628
- description: "Deploy the application",
629
- source: "(custom)",
630
- content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
631
- };
632
-
633
- const { session } = await createAgentSession({
634
- slashCommands: [...discovered, customCommand],
635
- });
636
- ```
637
-
638
- > See [examples/sdk/08-slash-commands.ts](../examples/sdk/08-slash-commands.ts)
639
-
640
- ### Session Management
641
-
642
- Sessions use a tree structure with `id`/`parentId` linking, enabling in-place branching.
643
-
644
- ```typescript
645
- import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent";
646
-
647
- // In-memory (no persistence)
648
- const { session } = await createAgentSession({
649
- sessionManager: SessionManager.inMemory(),
650
- });
651
-
652
- // New persistent session
653
- const { session } = await createAgentSession({
654
- sessionManager: SessionManager.create(process.cwd()),
655
- });
656
-
657
- // Continue most recent (async)
658
- const { session, modelFallbackMessage } = await createAgentSession({
659
- sessionManager: await SessionManager.continueRecent(process.cwd()),
660
- });
661
- if (modelFallbackMessage) {
662
- console.log("Note:", modelFallbackMessage);
663
- }
664
-
665
- // Open specific file (async)
666
- const { session } = await createAgentSession({
667
- sessionManager: await SessionManager.open("/path/to/session.jsonl"),
668
- });
669
-
670
- // List available sessions (async)
671
- const sessions = await SessionManager.list(process.cwd());
672
- for (const info of sessions) {
673
- console.log(`${info.id}: ${info.firstMessage} (${info.messageCount} messages)`);
674
- }
675
-
676
- // Custom session directory (no cwd encoding)
677
- const customDir = "/path/to/my-sessions";
678
- const { session } = await createAgentSession({
679
- sessionManager: SessionManager.create(process.cwd(), customDir),
680
- });
681
- ```
682
-
683
- **SessionManager static factories:**
684
-
685
- - `SessionManager.create(cwd, sessionDir?)` - New persistent session (sync)
686
- - `SessionManager.inMemory(cwd?)` - In-memory session (sync)
687
- - `SessionManager.open(filePath, sessionDir?)` - Open existing file (async)
688
- - `SessionManager.continueRecent(cwd, sessionDir?)` - Most recent session (async)
689
- - `SessionManager.list(cwd, sessionDir?)` - List sessions (async)
690
- - `SessionManager.listAll()` - List all sessions across cwds (async)
691
- - `SessionManager.forkFrom(sourcePath, cwd, sessionDir?)` - Fork from existing (async)
692
-
693
- **SessionManager tree API:**
694
-
695
- ```typescript
696
- const sm = await SessionManager.open("/path/to/session.jsonl");
697
-
698
- // Tree traversal
699
- const entries = sm.getEntries(); // All entries (excludes header)
700
- const tree = sm.getTree(); // Full tree structure
701
- const branch = sm.getBranch(); // Path from root to current leaf
702
- const leaf = sm.getLeafEntry(); // Current leaf entry
703
- const entry = sm.getEntry(id); // Get entry by ID
704
- const children = sm.getChildren(id); // Direct children of entry
705
-
706
- // Labels
707
- const label = sm.getLabel(id); // Get label for entry
708
- sm.appendLabelChange(id, "checkpoint"); // Set label
709
-
710
- // Branching
711
- sm.branch(entryId); // Move leaf to earlier entry
712
- sm.branchWithSummary(id, "Summary..."); // Branch with context summary
713
- sm.createBranchedSession(leafId); // Extract path to new file
714
- ```
715
-
716
- > See [examples/sdk/11-sessions.ts](../examples/sdk/11-sessions.ts) and [docs/session.md](session.md)
717
-
718
- ### Settings Management
719
-
720
- ```typescript
721
- import { createAgentSession, Settings, SessionManager } from "@oh-my-pi/pi-coding-agent";
722
-
723
- // Default: loads from files (global config.yml + project settings.json merged)
724
- const settings = await Settings.init();
725
-
726
- const { session } = await createAgentSession({
727
- settings,
728
- });
729
-
730
- // Read/write settings
731
- const enabled = settings.get("compaction.enabled");
732
- settings.set("compaction.enabled", false);
733
-
734
- // In-memory (no file I/O, for testing)
735
- const isolated = Settings.isolated({
736
- "compaction.enabled": false,
737
- "retry.enabled": true,
738
- });
739
-
740
- const { session } = await createAgentSession({
741
- settings: isolated,
742
- sessionManager: SessionManager.inMemory(),
743
- });
744
-
745
- // Custom directories
746
- const { session } = await createAgentSession({
747
- cwd: "/custom/cwd",
748
- agentDir: "/custom/agent",
749
- });
750
- ```
751
-
752
- **Settings static factories:**
753
-
754
- - `Settings.init(options?)` - Load from files (async)
755
- - `Settings.isolated(overrides?)` - In-memory, no file I/O (sync)
756
- - `Settings.instance` - Global singleton (throws if not initialized)
757
-
758
- **Settings file locations:**
759
-
760
- Settings load from two locations and merge:
761
-
762
- 1. Global: `<agentDir>/config.yml` (default `~/.omp/agent/config.yml`)
763
- 2. Project: `settings.json` from the first matching config dir (`.omp/`, `.pi/`, `.claude/`, `.codex/`, `.gemini/`)
764
-
765
- Project overrides global. Nested objects merge keys.
766
-
767
- ## Discovery Functions
768
-
769
- Discovery functions accept optional `cwd` and `agentDir` parameters where applicable.
770
-
771
- ```typescript
772
- import { getModel } from "@oh-my-pi/pi-ai";
773
- import appendPrompt from "./APPEND_SYSTEM.md" with { type: "text" };
774
- import {
775
- AuthStorage,
776
- ModelRegistry,
777
- discoverAuthStorage,
778
- discoverModels,
779
- discoverSkills,
780
- discoverExtensions,
781
- discoverContextFiles,
782
- discoverSlashCommands,
783
- discoverPromptTemplates,
784
- discoverCustomTSCommands,
785
- discoverMCPServers,
786
- buildSystemPrompt,
787
- Settings,
788
- } from "@oh-my-pi/pi-coding-agent";
789
-
790
- // Auth and Models
791
- const authStorage = await discoverAuthStorage(); // <agentDir>/agent.db
792
- const modelRegistry = discoverModels(authStorage); // + <agentDir>/models.yml (or models.json)
793
- const allModels = modelRegistry.getAll(); // All models (built-in + custom)
794
- const available = modelRegistry.getAvailable(); // Only models with API keys
795
- const model = modelRegistry.find("provider", "id"); // Find specific model
796
- const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only
797
-
798
- // Skills (async)
799
- const { skills, warnings } = await discoverSkills(cwd, agentDir, skillsSettings);
800
-
801
- // Extensions (async - loads TypeScript)
802
- const extensionsResult = await discoverExtensions(cwd);
803
-
804
- // Custom TS commands (async - loads TypeScript)
805
- const customCommands = await discoverCustomTSCommands(cwd, agentDir);
806
-
807
- // Context files (async)
808
- const contextFiles = await discoverContextFiles(cwd, agentDir);
809
-
810
- // Slash commands (async)
811
- const commands = await discoverSlashCommands(cwd);
812
-
813
- // Prompt templates (async)
814
- const promptTemplates = await discoverPromptTemplates(cwd, agentDir);
815
-
816
- // MCP servers (async)
817
- const mcp = await discoverMCPServers(cwd);
818
-
819
- // Settings (async - global + project merged)
820
- const settings = await Settings.init({ cwd, agentDir });
821
-
822
- // Build system prompt manually
823
- const prompt = await buildSystemPrompt({
824
- skills,
825
- contextFiles,
826
- appendPrompt,
827
- cwd,
828
- });
829
- ```
830
-
831
- ## Return Value
832
-
833
- `createAgentSession()` returns:
834
-
835
- ```typescript
836
- interface CreateAgentSessionResult {
837
- // The session
838
- session: AgentSession;
839
-
840
- // Extensions result (loaded extensions + runtime)
841
- extensionsResult: LoadExtensionsResult;
842
-
843
- // Update tool UI context (interactive mode)
844
- setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
845
-
846
- // MCP manager for server lifecycle management (undefined if MCP disabled)
847
- mcpManager?: MCPManager;
848
-
849
- // Warning if session model couldn't be restored
850
- modelFallbackMessage?: string;
851
-
852
- // LSP servers that were warmed up at startup
853
- lspServers?: Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string }>;
854
- }
855
- ```
856
-
857
- ## Complete Example
858
-
859
- ```typescript
860
- import { getModel } from "@oh-my-pi/pi-ai";
861
- import { Type } from "@sinclair/typebox";
862
- import {
863
- createAgentSession,
864
- discoverAuthStorage,
865
- discoverModels,
866
- SessionManager,
867
- Settings,
868
- type ExtensionFactory,
869
- type CustomTool,
870
- } from "@oh-my-pi/pi-coding-agent";
871
- import systemPrompt from "./SYSTEM.md" with { type: "text" };
872
-
873
- // Set up auth storage
874
- const authStorage = await discoverAuthStorage();
875
-
876
- // Runtime API key override (not persisted)
877
- if (Bun.env.MY_KEY) {
878
- authStorage.setRuntimeApiKey("anthropic", Bun.env.MY_KEY);
879
- }
880
-
881
- // Model registry
882
- const modelRegistry = discoverModels(authStorage);
883
-
884
- // Inline extension
885
- const auditExtension: ExtensionFactory = (api) => {
886
- api.on("tool_call", async (event) => {
887
- console.log(`[Audit] ${event.toolName}`);
888
- return undefined;
889
- });
890
- };
891
-
892
- // Inline tool
893
- const statusTool: CustomTool = {
894
- name: "status",
895
- label: "Status",
896
- description: "Get system status",
897
- parameters: Type.Object({}),
898
- execute: async (toolCallId, params, onUpdate, ctx, signal) => ({
899
- content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
900
- }),
901
- };
902
-
903
- const model = getModel("anthropic", "claude-opus-4-5");
904
- if (!model) throw new Error("Model not found");
905
-
906
- // In-memory settings with overrides
907
- const settings = Settings.isolated({
908
- "compaction.enabled": false,
909
- "retry.enabled": true,
910
- });
911
-
912
- const { session } = await createAgentSession({
913
- cwd: process.cwd(),
914
- agentDir: "/custom/agent",
915
-
916
- model,
917
- thinkingLevel: "off",
918
- authStorage,
919
- modelRegistry,
920
-
921
- systemPrompt,
922
-
923
- toolNames: ["read", "bash"],
924
- customTools: [statusTool],
925
- extensions: [auditExtension],
926
- skills: [],
927
- contextFiles: [],
928
- slashCommands: [],
929
-
930
- sessionManager: SessionManager.inMemory(),
931
- settings,
932
- });
933
-
934
- session.subscribe((event) => {
935
- if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
936
- process.stdout.write(event.assistantMessageEvent.delta);
937
- }
938
- });
939
-
940
- await session.prompt("Get status and list files.");
941
- ```
942
-
943
- ## RPC Mode Alternative
944
-
945
- For subprocess-based integration, use RPC mode instead of the SDK:
946
-
947
- ```bash
948
- omp --mode rpc --no-session
949
- ```
950
-
951
- See [RPC documentation](rpc.md) for the JSON protocol.
952
-
953
- The SDK is preferred when:
954
-
955
- - You want type safety
956
- - You're in the same Node.js process
957
- - You need direct access to agent state
958
- - You want to customize tools/extensions programmatically
959
-
960
- RPC mode is preferred when:
961
-
962
- - You're integrating from another language
963
- - You want process isolation
964
- - You're building a language-agnostic client
965
-
966
- ## Exports
967
-
968
- The main entry point exports:
969
-
970
- ```typescript
971
- // Factory
972
- createAgentSession
973
-
974
- // Auth and Models
975
- AuthStorage
976
- ModelRegistry
977
- discoverAuthStorage
978
- discoverModels
979
-
980
- // Discovery
981
- discoverSkills
982
- discoverExtensions
983
- discoverCustomTSCommands
984
- discoverContextFiles
985
- discoverSlashCommands
986
- discoverPromptTemplates
987
- discoverMCPServers
988
-
989
- // Helpers
990
- buildSystemPrompt
991
- Settings
992
-
993
- // Session management
994
- SessionManager
995
-
996
- // Tool registry and factory
997
- BUILTIN_TOOLS // Map of tool name to factory
998
- createTools // Create all tools from ToolSession
999
- type ToolSession // Session context for tool creation
1000
-
1001
- // Individual tool classes
1002
- ReadTool, BashTool, EditTool, WriteTool
1003
- GrepTool, FindTool, PythonTool
1004
- loadSshTool
1005
-
1006
- // Types
1007
- type CreateAgentSessionOptions
1008
- type CreateAgentSessionResult
1009
- type CustomTool
1010
- type ExtensionFactory
1011
- type Skill
1012
- type FileSlashCommand
1013
- type SkillsSettings
1014
- type Tool
1015
- ```
1016
-
1017
- For extension types, import from the main package:
1018
-
1019
- ```typescript
1020
- import type {
1021
- ExtensionAPI,
1022
- ExtensionFactory,
1023
- ExtensionContext,
1024
- ExtensionCommandContext,
1025
- ToolDefinition,
1026
- } from "@oh-my-pi/pi-coding-agent";
1027
- ```
1028
-
1029
- For legacy hook types (deprecated, use extensions instead):
1030
-
1031
- ```typescript
1032
- import type { HookAPI, HookFactory, HookContext, HookCommandContext } from "@oh-my-pi/pi-coding-agent/hooks";
1033
- ```
1034
-
1035
- For config utilities:
1036
-
1037
- ```typescript
1038
- import { getAgentDir } from "@oh-my-pi/pi-coding-agent";
1039
- ```