fix: extension loading in Bun binary (#681)

- Remove loader exports from index.ts to break circular dependency
- Static import of index.js in loader.ts for virtualModules
- Add @mariozechner/pi-agent-core to virtualModules
- Update @mariozechner/jiti to 2.6.5 (bundles babel for Bun binary)
- Update SDK docs to use correct extension terminology
This commit is contained in:
Mario Zechner 2026-01-13 21:19:41 +01:00
parent 53d9e61bac
commit 843f235251
7 changed files with 37 additions and 67 deletions

7
package-lock.json generated
View file

@ -13,6 +13,7 @@
"packages/coding-agent/examples/extensions/with-deps" "packages/coding-agent/examples/extensions/with-deps"
], ],
"dependencies": { "dependencies": {
"@mariozechner/jiti": "^2.6.5",
"@mariozechner/pi-coding-agent": "^0.30.2", "@mariozechner/pi-coding-agent": "^0.30.2",
"get-east-asian-width": "^1.4.0" "get-east-asian-width": "^1.4.0"
}, },
@ -1781,9 +1782,9 @@
} }
}, },
"node_modules/@mariozechner/jiti": { "node_modules/@mariozechner/jiti": {
"version": "2.6.2", "version": "2.6.5",
"resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.2.tgz", "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz",
"integrity": "sha512-CcFowm/fDWcEMH/F47DQcdawpLQb0nw+WR+hZOv8mgAeACFJxE9uo3cXjUk/5Cl3j23t/oxvtxxUtlBCUIGeQg==", "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"std-env": "^3.10.0", "std-env": "^3.10.0",

View file

@ -40,6 +40,7 @@
}, },
"version": "0.0.3", "version": "0.0.3",
"dependencies": { "dependencies": {
"@mariozechner/jiti": "^2.6.5",
"@mariozechner/pi-coding-agent": "^0.30.2", "@mariozechner/pi-coding-agent": "^0.30.2",
"get-east-asian-width": "^1.4.0" "get-east-asian-width": "^1.4.0"
} }

View file

@ -19,6 +19,7 @@
- Fix API key resolution after model switches by using provider argument ([#691](https://github.com/badlogic/pi-mono/pull/691) by [@joshp123](https://github.com/joshp123)) - Fix API key resolution after model switches by using provider argument ([#691](https://github.com/badlogic/pi-mono/pull/691) by [@joshp123](https://github.com/joshp123))
- Fixed z.ai thinking/reasoning: thinking toggle now correctly enables/disables thinking for z.ai models ([#688](https://github.com/badlogic/pi-mono/issues/688)) - Fixed z.ai thinking/reasoning: thinking toggle now correctly enables/disables thinking for z.ai models ([#688](https://github.com/badlogic/pi-mono/issues/688))
- Fixed extension loading in compiled Bun binary: extensions with local file imports now work correctly. Updated `@mariozechner/jiti` to v2.6.5 which bundles babel for Bun binary compatibility. ([#681](https://github.com/badlogic/pi-mono/issues/681))
## [0.45.3] - 2026-01-13 ## [0.45.3] - 2026-01-13

View file

@ -735,12 +735,12 @@ import {
discoverAuthStorage, discoverAuthStorage,
discoverModels, discoverModels,
discoverSkills, discoverSkills,
discoverHooks, discoverExtensions,
discoverCustomTools,
discoverContextFiles, discoverContextFiles,
discoverPromptTemplates, discoverPromptTemplates,
loadSettings, loadSettings,
buildSystemPrompt, buildSystemPrompt,
createEventBus,
} from "@mariozechner/pi-coding-agent"; } from "@mariozechner/pi-coding-agent";
// Auth and Models // Auth and Models
@ -754,19 +754,16 @@ const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only
// Skills // Skills
const { skills, warnings } = discoverSkills(cwd, agentDir, skillsSettings); const { skills, warnings } = discoverSkills(cwd, agentDir, skillsSettings);
// Hooks (async - loads TypeScript) // Extensions (async - loads TypeScript)
// Pass eventBus to share pi.events across hooks/tools // Pass eventBus to share pi.events across extensions
const eventBus = createEventBus(); const eventBus = createEventBus();
const hooks = await discoverHooks(eventBus, cwd, agentDir); const { extensions, errors } = await discoverExtensions(eventBus, cwd, agentDir);
// Custom tools (async - loads TypeScript)
const tools = await discoverCustomTools(eventBus, cwd, agentDir);
// Context files // Context files
const contextFiles = discoverContextFiles(cwd, agentDir); const contextFiles = discoverContextFiles(cwd, agentDir);
// Prompt templates // Prompt templates
const commands = discoverPromptTemplates(cwd, agentDir); const templates = discoverPromptTemplates(cwd, agentDir);
// Settings (global + project merged) // Settings (global + project merged)
const settings = loadSettings(cwd, agentDir); const settings = loadSettings(cwd, agentDir);
@ -816,8 +813,8 @@ import {
SettingsManager, SettingsManager,
readTool, readTool,
bashTool, bashTool,
type HookFactory, type ExtensionFactory,
type CustomTool, type ToolDefinition,
} from "@mariozechner/pi-coding-agent"; } from "@mariozechner/pi-coding-agent";
// Set up auth storage (custom location) // Set up auth storage (custom location)
@ -831,16 +828,16 @@ if (process.env.MY_KEY) {
// Model registry (no custom models.json) // Model registry (no custom models.json)
const modelRegistry = new ModelRegistry(authStorage); const modelRegistry = new ModelRegistry(authStorage);
// Inline hook // Inline extension
const auditHook: HookFactory = (api) => { const auditExtension: ExtensionFactory = (pi) => {
api.on("tool_call", async (event) => { pi.on("tool_call", async (event) => {
console.log(`[Audit] ${event.toolName}`); console.log(`[Audit] ${event.toolName}`);
return undefined; return undefined;
}); });
}; };
// Inline tool // Inline tool
const statusTool: CustomTool = { const statusTool: ToolDefinition = {
name: "status", name: "status",
label: "Status", label: "Status",
description: "Get system status", description: "Get system status",
@ -872,8 +869,8 @@ const { session } = await createAgentSession({
systemPrompt: "You are a minimal assistant. Be concise.", systemPrompt: "You are a minimal assistant. Be concise.",
tools: [readTool, bashTool], tools: [readTool, bashTool],
customTools: [{ tool: statusTool }], customTools: [statusTool],
hooks: [{ factory: auditHook }], extensions: [auditExtension],
skills: [], skills: [],
contextFiles: [], contextFiles: [],
promptTemplates: [], promptTemplates: [],
@ -961,7 +958,7 @@ The SDK is preferred when:
- You want type safety - You want type safety
- You're in the same Node.js process - You're in the same Node.js process
- You need direct access to agent state - You need direct access to agent state
- You want to customize tools/hooks programmatically - You want to customize tools/extensions programmatically
RPC mode is preferred when: RPC mode is preferred when:
- You're integrating from another language - You're integrating from another language
@ -984,12 +981,11 @@ discoverModels
// Discovery // Discovery
discoverSkills discoverSkills
discoverHooks discoverExtensions
discoverCustomTools
discoverContextFiles discoverContextFiles
discoverPromptTemplates discoverPromptTemplates
// Event Bus (for shared hook/tool communication) // Event Bus (for shared extension communication)
createEventBus createEventBus
// Helpers // Helpers
@ -1015,8 +1011,9 @@ createGrepTool, createFindTool, createLsTool
// Types // Types
type CreateAgentSessionOptions type CreateAgentSessionOptions
type CreateAgentSessionResult type CreateAgentSessionResult
type CustomTool type ExtensionFactory
type HookFactory type ExtensionAPI
type ToolDefinition
type Skill type Skill
type PromptTemplate type PromptTemplate
type Settings type Settings
@ -1024,28 +1021,4 @@ type SkillsSettings
type Tool type Tool
``` ```
For hook types, import from the hooks subpath: For extension types, see [extensions.md](extensions.md) for the full API.
```typescript
import type {
HookAPI,
HookMessage,
HookFactory,
HookEventContext,
HookCommandContext,
ToolCallEvent,
ToolResultEvent,
} from "@mariozechner/pi-coding-agent/hooks";
```
For message utilities:
```typescript
import { isHookMessage, createHookMessage } from "@mariozechner/pi-coding-agent";
```
For config utilities:
```typescript
import { getAgentDir } from "@mariozechner/pi-coding-agent/config";
```

View file

@ -37,9 +37,8 @@ import {
discoverModels, discoverModels,
discoverSkills, discoverSkills,
discoverExtensions, discoverExtensions,
discoverCustomTools,
discoverContextFiles, discoverContextFiles,
discoverSlashCommands, discoverPromptTemplates,
loadSettings, loadSettings,
buildSystemPrompt, buildSystemPrompt,
ModelRegistry, ModelRegistry,
@ -92,7 +91,7 @@ const { session } = await createAgentSession({
extensions: [{ factory: myExtension }], extensions: [{ factory: myExtension }],
skills: [], skills: [],
contextFiles: [], contextFiles: [],
slashCommands: [], promptTemplates: [],
sessionManager: SessionManager.inMemory(), sessionManager: SessionManager.inMemory(),
}); });
@ -123,7 +122,7 @@ await session.prompt("Hello");
| `additionalExtensionPaths` | `[]` | Merge with discovery | | `additionalExtensionPaths` | `[]` | Merge with discovery |
| `skills` | Discovered | Skills for prompt | | `skills` | Discovered | Skills for prompt |
| `contextFiles` | Discovered | AGENTS.md files | | `contextFiles` | Discovered | AGENTS.md files |
| `slashCommands` | Discovered | File commands | | `promptTemplates` | Discovered | Prompt templates (slash commands) |
| `sessionManager` | `SessionManager.create(cwd)` | Persistence | | `sessionManager` | `SessionManager.create(cwd)` | Persistence |
| `settingsManager` | From agentDir | Settings overrides | | `settingsManager` | From agentDir | Settings overrides |

View file

@ -10,6 +10,7 @@ import * as os from "node:os";
import * as path from "node:path"; import * as path from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { createJiti } from "@mariozechner/jiti"; import { createJiti } from "@mariozechner/jiti";
import * as _bundledPiAgentCore from "@mariozechner/pi-agent-core";
import * as _bundledPiAi from "@mariozechner/pi-ai"; import * as _bundledPiAi from "@mariozechner/pi-ai";
import type { KeyId } from "@mariozechner/pi-tui"; import type { KeyId } from "@mariozechner/pi-tui";
import * as _bundledPiTui from "@mariozechner/pi-tui"; import * as _bundledPiTui from "@mariozechner/pi-tui";
@ -18,6 +19,9 @@ import * as _bundledPiTui from "@mariozechner/pi-tui";
// The virtualModules option then makes them available to extensions. // The virtualModules option then makes them available to extensions.
import * as _bundledTypebox from "@sinclair/typebox"; import * as _bundledTypebox from "@sinclair/typebox";
import { getAgentDir, isBunBinary } from "../../config.js"; import { getAgentDir, isBunBinary } from "../../config.js";
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
// avoiding a circular dependency. Extensions can import from @mariozechner/pi-coding-agent.
import * as _bundledPiCodingAgent from "../../index.js";
import { createEventBus, type EventBus } from "../event-bus.js"; import { createEventBus, type EventBus } from "../event-bus.js";
import type { ExecOptions } from "../exec.js"; import type { ExecOptions } from "../exec.js";
import { execCommand } from "../exec.js"; import { execCommand } from "../exec.js";
@ -33,19 +37,12 @@ import type {
} from "./types.js"; } from "./types.js";
/** Modules available to extensions via virtualModules (for compiled Bun binary) */ /** Modules available to extensions via virtualModules (for compiled Bun binary) */
let _lazyPiCodingAgent: unknown;
const VIRTUAL_MODULES: Record<string, unknown> = { const VIRTUAL_MODULES: Record<string, unknown> = {
"@sinclair/typebox": _bundledTypebox, "@sinclair/typebox": _bundledTypebox,
"@mariozechner/pi-agent-core": _bundledPiAgentCore,
"@mariozechner/pi-tui": _bundledPiTui, "@mariozechner/pi-tui": _bundledPiTui,
"@mariozechner/pi-ai": _bundledPiAi, "@mariozechner/pi-ai": _bundledPiAi,
// Lazy-loaded to avoid circular dependency (loader.ts is part of pi-coding-agent) "@mariozechner/pi-coding-agent": _bundledPiCodingAgent,
get "@mariozechner/pi-coding-agent"() {
if (!_lazyPiCodingAgent) {
// Dynamic require after module initialization completes
_lazyPiCodingAgent = require("../../index.js");
}
return _lazyPiCodingAgent;
},
}; };
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
@ -66,6 +63,7 @@ function getAliases(): Record<string, string> {
_aliases = { _aliases = {
"@mariozechner/pi-coding-agent": packageIndex, "@mariozechner/pi-coding-agent": packageIndex,
"@mariozechner/pi-agent-core": require.resolve("@mariozechner/pi-agent-core"),
"@mariozechner/pi-tui": require.resolve("@mariozechner/pi-tui"), "@mariozechner/pi-tui": require.resolve("@mariozechner/pi-tui"),
"@mariozechner/pi-ai": require.resolve("@mariozechner/pi-ai"), "@mariozechner/pi-ai": require.resolve("@mariozechner/pi-ai"),
"@sinclair/typebox": typeboxRoot, "@sinclair/typebox": typeboxRoot,

View file

@ -88,8 +88,6 @@ export type {
UserBashEventResult, UserBashEventResult,
} from "./core/extensions/index.js"; } from "./core/extensions/index.js";
export { export {
createExtensionRuntime,
discoverAndLoadExtensions,
ExtensionRunner, ExtensionRunner,
isBashToolResult, isBashToolResult,
isEditToolResult, isEditToolResult,
@ -98,7 +96,6 @@ export {
isLsToolResult, isLsToolResult,
isReadToolResult, isReadToolResult,
isWriteToolResult, isWriteToolResult,
loadExtensions,
wrapRegisteredTool, wrapRegisteredTool,
wrapRegisteredTools, wrapRegisteredTools,
wrapToolsWithExtensions, wrapToolsWithExtensions,