mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 13:03:42 +00:00
Add customTools option back to createAgentSession SDK
- Accepts ToolDefinition[] directly (simplified from old { path?, tool } format)
- Tools are combined with extension-registered tools
- Updated sdk.md documentation
- Updated CHANGELOG
This commit is contained in:
parent
6bd5e419a6
commit
8da793b1ba
4 changed files with 56 additions and 101 deletions
|
|
@ -163,11 +163,14 @@ pi --extension ./safety.ts -e ./todo.ts
|
|||
**Runner and wrapper:**
|
||||
- `HookRunner` → `ExtensionRunner`
|
||||
- `wrapToolsWithHooks()` → `wrapToolsWithExtensions()`
|
||||
- `wrapToolWithHook()` → `wrapToolWithExtensions()`
|
||||
- `wrapToolWithHooks()` → `wrapToolWithExtensions()`
|
||||
|
||||
**CreateAgentSessionOptions:**
|
||||
- `.hooks` → `.extensions`
|
||||
- `.customTools` → merged into `.extensions`
|
||||
- `.hooks` → removed (use `.additionalExtensionPaths` for paths)
|
||||
- `.additionalHookPaths` → `.additionalExtensionPaths`
|
||||
- `.preloadedHooks` → `.preloadedExtensions`
|
||||
- `.customTools` type changed: `Array<{ path?; tool: CustomTool }>` → `ToolDefinition[]`
|
||||
- `.additionalCustomToolPaths` → merged into `.additionalExtensionPaths`
|
||||
- `.slashCommands` → `.promptTemplates`
|
||||
|
||||
**AgentSession:**
|
||||
|
|
@ -194,6 +197,8 @@ pi --extension ./safety.ts -e ./todo.ts
|
|||
- Documentation: `docs/hooks.md` and `docs/custom-tools.md` merged into `docs/extensions.md`
|
||||
- Examples: `examples/hooks/` and `examples/custom-tools/` merged into `examples/extensions/`
|
||||
- README: Extensions section expanded with custom tools, commands, events, state persistence, shortcuts, flags, and UI examples
|
||||
- SDK: `customTools` option now accepts `ToolDefinition[]` directly (simplified from `Array<{ path?, tool }>`)
|
||||
- SDK: `additionalExtensionPaths` replaces both `additionalHookPaths` and `additionalCustomToolPaths`
|
||||
|
||||
## [0.34.2] - 2026-01-04
|
||||
|
||||
|
|
|
|||
|
|
@ -811,8 +811,8 @@ pi.registerTool({
|
|||
details: { progress: 50 },
|
||||
});
|
||||
|
||||
// Run commands with cancellation support
|
||||
const result = await ctx.exec("some-command", [], { signal });
|
||||
// Run commands via pi.exec (captured from extension closure)
|
||||
const result = await pi.exec("some-command", [], { signal });
|
||||
|
||||
// Return result
|
||||
return {
|
||||
|
|
@ -941,9 +941,9 @@ ctx.ui.notify("Done!", "info"); // "info" | "warning" | "error"
|
|||
ctx.ui.setStatus("my-ext", "Processing...");
|
||||
ctx.ui.setStatus("my-ext", undefined); // Clear
|
||||
|
||||
// Widget above editor (string array or Component)
|
||||
// Widget above editor (string array or factory function)
|
||||
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"]);
|
||||
ctx.ui.setWidget("my-widget", new Text(theme.fg("accent", "Custom"), 0, 0));
|
||||
ctx.ui.setWidget("my-widget", (tui, theme) => new Text(theme.fg("accent", "Custom"), 0, 0));
|
||||
ctx.ui.setWidget("my-widget", undefined); // Clear
|
||||
|
||||
// Terminal title
|
||||
|
|
|
|||
|
|
@ -440,125 +440,60 @@ const { session } = await createAgentSession({
|
|||
|
||||
```typescript
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { createAgentSession, discoverCustomTools, type CustomTool } from "@mariozechner/pi-coding-agent";
|
||||
import { createAgentSession, type ToolDefinition } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Inline custom tool
|
||||
const myTool: CustomTool = {
|
||||
const myTool: ToolDefinition = {
|
||||
name: "my_tool",
|
||||
label: "My Tool",
|
||||
description: "Does something useful",
|
||||
parameters: Type.Object({
|
||||
input: Type.String({ description: "Input value" }),
|
||||
}),
|
||||
execute: async (toolCallId, params) => ({
|
||||
execute: async (toolCallId, params, onUpdate, ctx, signal) => ({
|
||||
content: [{ type: "text", text: `Result: ${params.input}` }],
|
||||
details: {},
|
||||
}),
|
||||
};
|
||||
|
||||
// Replace discovery with inline tools
|
||||
// Pass custom tools directly
|
||||
const { session } = await createAgentSession({
|
||||
customTools: [{ tool: myTool }],
|
||||
});
|
||||
|
||||
// Merge with discovered tools (share eventBus for tool.events communication)
|
||||
import { createEventBus } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const eventBus = createEventBus();
|
||||
const discovered = await discoverCustomTools(eventBus);
|
||||
const { session } = await createAgentSession({
|
||||
customTools: [...discovered, { tool: myTool }],
|
||||
eventBus,
|
||||
});
|
||||
|
||||
// Add paths without replacing discovery
|
||||
const { session } = await createAgentSession({
|
||||
additionalCustomToolPaths: ["/extra/tools"],
|
||||
customTools: [myTool],
|
||||
});
|
||||
```
|
||||
|
||||
Custom tools passed via `customTools` are combined with extension-registered tools. Extensions discovered from `~/.pi/agent/extensions/` and `.pi/extensions/` can also register tools via `pi.registerTool()`.
|
||||
|
||||
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
||||
|
||||
### Hooks
|
||||
### Extensions
|
||||
|
||||
Extensions are discovered from `~/.pi/agent/extensions/` and `.pi/extensions/`. Use `additionalExtensionPaths` to add extra paths:
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, discoverHooks, type HookFactory } from "@mariozechner/pi-coding-agent";
|
||||
import { createAgentSession } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Inline hook
|
||||
const loggingHook: HookFactory = (api) => {
|
||||
// Log tool calls
|
||||
api.on("tool_call", async (event) => {
|
||||
console.log(`Tool: ${event.toolName}`);
|
||||
return undefined; // Don't block
|
||||
});
|
||||
|
||||
// Block dangerous commands
|
||||
api.on("tool_call", async (event) => {
|
||||
if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
|
||||
return { block: true, reason: "Dangerous command" };
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// Register custom prompt template
|
||||
api.registerCommand("stats", {
|
||||
description: "Show session stats",
|
||||
handler: async (ctx) => {
|
||||
const entries = ctx.sessionManager.getEntries();
|
||||
ctx.ui.notify(`${entries.length} entries`, "info");
|
||||
},
|
||||
});
|
||||
|
||||
// Inject messages
|
||||
api.sendMessage({
|
||||
customType: "my-hook",
|
||||
content: "Hook initialized",
|
||||
display: false, // Hidden from TUI
|
||||
}, false); // Don't trigger agent turn
|
||||
|
||||
// Persist hook state
|
||||
api.appendEntry("my-hook", { initialized: true });
|
||||
};
|
||||
|
||||
// Replace discovery
|
||||
// Add extension paths (merged with discovery)
|
||||
const { session } = await createAgentSession({
|
||||
hooks: [{ factory: loggingHook }],
|
||||
});
|
||||
|
||||
// Disable all hooks
|
||||
const { session } = await createAgentSession({
|
||||
hooks: [],
|
||||
});
|
||||
|
||||
// Merge with discovered (share eventBus for pi.events communication)
|
||||
import { createEventBus } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const eventBus = createEventBus();
|
||||
const discovered = await discoverHooks(eventBus);
|
||||
const { session } = await createAgentSession({
|
||||
hooks: [...discovered, { factory: loggingHook }],
|
||||
eventBus,
|
||||
});
|
||||
|
||||
// Add paths without replacing
|
||||
const { session } = await createAgentSession({
|
||||
additionalHookPaths: ["/extra/hooks"],
|
||||
additionalExtensionPaths: ["/path/to/my-extension.ts"],
|
||||
});
|
||||
```
|
||||
|
||||
**Event Bus:** If hooks or tools use `pi.events` for inter-component communication, pass the same `eventBus` to `discoverHooks()`, `discoverCustomTools()`, and `createAgentSession()`. Otherwise each gets an isolated bus and events won't be shared.
|
||||
Extensions can register tools, subscribe to events, add commands, and more. See [extensions.md](extensions.md) for the full API.
|
||||
|
||||
Hook API methods:
|
||||
- `api.on(event, handler)` - Subscribe to lifecycle events
|
||||
- `api.events.emit(channel, data)` - Emit to shared event bus
|
||||
- `api.events.on(channel, handler)` - Listen on shared event bus
|
||||
- `api.sendMessage(message, triggerTurn?)` - Inject message (creates `CustomMessageEntry`)
|
||||
- `api.appendEntry(customType, data?)` - Persist hook state (not in LLM context)
|
||||
- `api.registerCommand(name, options)` - Register custom command
|
||||
- `api.registerMessageRenderer(customType, renderer)` - Custom TUI rendering
|
||||
- `api.exec(command, args, options?)` - Execute shell commands
|
||||
**Event Bus:** Extensions can communicate via `pi.events`. Pass a shared `eventBus` to `createAgentSession()` if you need to emit/listen from outside:
|
||||
|
||||
> See [examples/sdk/06-hooks.ts](../examples/sdk/06-hooks.ts) and [docs/hooks.md](hooks.md)
|
||||
```typescript
|
||||
import { createAgentSession, createEventBus } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const eventBus = createEventBus();
|
||||
const { session } = await createAgentSession({ eventBus });
|
||||
|
||||
// Listen for events from extensions
|
||||
eventBus.on("my-extension:status", (data) => console.log(data));
|
||||
```
|
||||
|
||||
> See [examples/sdk/06-extensions.ts](../examples/sdk/06-extensions.ts) and [docs/extensions.md](extensions.md)
|
||||
|
||||
### Skills
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import {
|
|||
ExtensionRunner,
|
||||
type LoadExtensionsResult,
|
||||
type LoadedExtension,
|
||||
type ToolDefinition,
|
||||
wrapRegisteredTools,
|
||||
wrapToolsWithExtensions,
|
||||
} from "./extensions/index.js";
|
||||
|
|
@ -96,6 +97,8 @@ export interface CreateAgentSessionOptions {
|
|||
|
||||
/** Built-in tools to use. Default: codingTools [read, bash, edit, write] */
|
||||
tools?: Tool[];
|
||||
/** Custom tools to register (in addition to built-in tools). */
|
||||
customTools?: ToolDefinition[];
|
||||
/** Additional extension paths to load (merged with discovery). */
|
||||
additionalExtensionPaths?: string[];
|
||||
/** Pre-loaded extensions (skips loading, used when extensions were loaded early for CLI flags). */
|
||||
|
|
@ -130,7 +133,13 @@ export interface CreateAgentSessionResult {
|
|||
|
||||
// Re-exports
|
||||
|
||||
export type { ExtensionAPI, ExtensionCommandContext, ExtensionContext, ExtensionFactory } from "./extensions/index.js";
|
||||
export type {
|
||||
ExtensionAPI,
|
||||
ExtensionCommandContext,
|
||||
ExtensionContext,
|
||||
ExtensionFactory,
|
||||
ToolDefinition,
|
||||
} from "./extensions/index.js";
|
||||
export type { PromptTemplate } from "./prompt-templates.js";
|
||||
export type { Settings, SkillsSettings } from "./settings-manager.js";
|
||||
export type { Skill } from "./skills.js";
|
||||
|
|
@ -444,11 +453,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|||
extensionRunner = new ExtensionRunner(extensionsResult.extensions, cwd, sessionManager, modelRegistry);
|
||||
}
|
||||
|
||||
// Wrap extension-registered tools with context getter (agent/session assigned below, accessed at execute time)
|
||||
// Wrap extension-registered tools and SDK-provided custom tools with context getter
|
||||
// (agent/session assigned below, accessed at execute time)
|
||||
let agent: Agent;
|
||||
let session: AgentSession;
|
||||
const registeredTools = extensionRunner?.getAllRegisteredTools() ?? [];
|
||||
const wrappedExtensionTools = wrapRegisteredTools(registeredTools, () => ({
|
||||
// Combine extension-registered tools with SDK-provided custom tools
|
||||
const allCustomTools = [
|
||||
...registeredTools,
|
||||
...(options.customTools?.map((def) => ({ definition: def, extensionPath: "<sdk>" })) ?? []),
|
||||
];
|
||||
const wrappedExtensionTools = wrapRegisteredTools(allCustomTools, () => ({
|
||||
ui: extensionRunner?.getUIContext() ?? {
|
||||
select: async () => undefined,
|
||||
confirm: async () => false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue