@compyle/unagent 1.0.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.
- package/LICENSE +21 -0
- package/README.md +547 -0
- package/dist/agents/agent-as-tool.d.ts +4 -0
- package/dist/agents/agent-as-tool.d.ts.map +1 -0
- package/dist/agents/agent-as-tool.js +105 -0
- package/dist/agents/agent-as-tool.js.map +1 -0
- package/dist/agents/define-agent.d.ts +101 -0
- package/dist/agents/define-agent.d.ts.map +1 -0
- package/dist/agents/define-agent.js +866 -0
- package/dist/agents/define-agent.js.map +1 -0
- package/dist/agents/index.d.ts +3 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +3 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/bridge/node-bridge.d.ts +2 -0
- package/dist/bridge/node-bridge.d.ts.map +1 -0
- package/dist/bridge/node-bridge.js +772 -0
- package/dist/bridge/node-bridge.js.map +1 -0
- package/dist/client/base-client.d.ts +86 -0
- package/dist/client/base-client.d.ts.map +1 -0
- package/dist/client/base-client.js +254 -0
- package/dist/client/base-client.js.map +1 -0
- package/dist/client/claude.d.ts +66 -0
- package/dist/client/claude.d.ts.map +1 -0
- package/dist/client/claude.js +846 -0
- package/dist/client/claude.js.map +1 -0
- package/dist/client/codex/codex-client.d.ts +78 -0
- package/dist/client/codex/codex-client.d.ts.map +1 -0
- package/dist/client/codex/codex-client.js +906 -0
- package/dist/client/codex/codex-client.js.map +1 -0
- package/dist/client/codex/codex-home.d.ts +6 -0
- package/dist/client/codex/codex-home.d.ts.map +1 -0
- package/dist/client/codex/codex-home.js +26 -0
- package/dist/client/codex/codex-home.js.map +1 -0
- package/dist/client/codex/config/config-loader.d.ts +8 -0
- package/dist/client/codex/config/config-loader.d.ts.map +1 -0
- package/dist/client/codex/config/config-loader.js +137 -0
- package/dist/client/codex/config/config-loader.js.map +1 -0
- package/dist/client/codex/config/config-types.d.ts +43 -0
- package/dist/client/codex/config/config-types.d.ts.map +1 -0
- package/dist/client/codex/config/config-types.js +2 -0
- package/dist/client/codex/config/config-types.js.map +1 -0
- package/dist/client/codex/config/config-writer.d.ts +24 -0
- package/dist/client/codex/config/config-writer.d.ts.map +1 -0
- package/dist/client/codex/config/config-writer.js +353 -0
- package/dist/client/codex/config/config-writer.js.map +1 -0
- package/dist/client/codex/config/index.d.ts +7 -0
- package/dist/client/codex/config/index.d.ts.map +1 -0
- package/dist/client/codex/config/index.js +6 -0
- package/dist/client/codex/config/index.js.map +1 -0
- package/dist/client/codex/config/mcp-config-generator.d.ts +7 -0
- package/dist/client/codex/config/mcp-config-generator.d.ts.map +1 -0
- package/dist/client/codex/config/mcp-config-generator.js +140 -0
- package/dist/client/codex/config/mcp-config-generator.js.map +1 -0
- package/dist/client/codex/config/tools-config-writer.d.ts +2 -0
- package/dist/client/codex/config/tools-config-writer.d.ts.map +1 -0
- package/dist/client/codex/config/tools-config-writer.js +21 -0
- package/dist/client/codex/config/tools-config-writer.js.map +1 -0
- package/dist/client/codex/index.d.ts +4 -0
- package/dist/client/codex/index.d.ts.map +1 -0
- package/dist/client/codex/index.js +3 -0
- package/dist/client/codex/index.js.map +1 -0
- package/dist/client/config-utils.d.ts +4 -0
- package/dist/client/config-utils.d.ts.map +1 -0
- package/dist/client/config-utils.js +32 -0
- package/dist/client/config-utils.js.map +1 -0
- package/dist/client/extra-headers.d.ts +12 -0
- package/dist/client/extra-headers.d.ts.map +1 -0
- package/dist/client/extra-headers.js +102 -0
- package/dist/client/extra-headers.js.map +1 -0
- package/dist/client/field-mappings.d.ts +3 -0
- package/dist/client/field-mappings.d.ts.map +1 -0
- package/dist/client/field-mappings.js +40 -0
- package/dist/client/field-mappings.js.map +1 -0
- package/dist/client/transformers/content-utils.d.ts +34 -0
- package/dist/client/transformers/content-utils.d.ts.map +1 -0
- package/dist/client/transformers/content-utils.js +237 -0
- package/dist/client/transformers/content-utils.js.map +1 -0
- package/dist/events/event-bus.d.ts +19 -0
- package/dist/events/event-bus.d.ts.map +1 -0
- package/dist/events/event-bus.js +134 -0
- package/dist/events/event-bus.js.map +1 -0
- package/dist/events/event-types.d.ts +53 -0
- package/dist/events/event-types.d.ts.map +1 -0
- package/dist/events/event-types.js +6 -0
- package/dist/events/event-types.js.map +1 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +4 -0
- package/dist/events/index.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/index.d.ts +3 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +3 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/messages/message-types.d.ts +60 -0
- package/dist/messages/message-types.d.ts.map +1 -0
- package/dist/messages/message-types.js +6 -0
- package/dist/messages/message-types.js.map +1 -0
- package/dist/messages/message-utils.d.ts +51 -0
- package/dist/messages/message-utils.d.ts.map +1 -0
- package/dist/messages/message-utils.js +343 -0
- package/dist/messages/message-utils.js.map +1 -0
- package/dist/tools/define-tool.d.ts +6 -0
- package/dist/tools/define-tool.d.ts.map +1 -0
- package/dist/tools/define-tool.js +83 -0
- package/dist/tools/define-tool.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/mcp-stdio-proxy.d.ts +2 -0
- package/dist/tools/mcp-stdio-proxy.d.ts.map +1 -0
- package/dist/tools/mcp-stdio-proxy.js +31 -0
- package/dist/tools/mcp-stdio-proxy.js.map +1 -0
- package/dist/tools/tool-call-context.d.ts +8 -0
- package/dist/tools/tool-call-context.d.ts.map +1 -0
- package/dist/tools/tool-call-context.js +44 -0
- package/dist/tools/tool-call-context.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +14 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +47 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/tool-result-converter.d.ts +3 -0
- package/dist/tools/tool-result-converter.d.ts.map +1 -0
- package/dist/tools/tool-result-converter.js +38 -0
- package/dist/tools/tool-result-converter.js.map +1 -0
- package/dist/tools/tool-service-manager.d.ts +12 -0
- package/dist/tools/tool-service-manager.d.ts.map +1 -0
- package/dist/tools/tool-service-manager.js +118 -0
- package/dist/tools/tool-service-manager.js.map +1 -0
- package/dist/tools/tool-stdio-mcp-service.d.ts +29 -0
- package/dist/tools/tool-stdio-mcp-service.d.ts.map +1 -0
- package/dist/tools/tool-stdio-mcp-service.js +185 -0
- package/dist/tools/tool-stdio-mcp-service.js.map +1 -0
- package/dist/tools/tool-types.d.ts +29 -0
- package/dist/tools/tool-types.d.ts.map +1 -0
- package/dist/tools/tool-types.js +2 -0
- package/dist/tools/tool-types.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Compyle
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
# Unagent - Unified AI Agent Development Framework
|
|
2
|
+
|
|
3
|
+
A unified development framework designed for multiple AI Clients (Claude Agent SDK, Codex, etc.), using a publish-subscribe pattern with support for asynchronous event handling, error isolation, and type safety.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Unified Event System** - All AI clients use the same event format
|
|
8
|
+
- ✅ **Async Event Handling** - Complete async listener support
|
|
9
|
+
- ✅ **Error Isolation** - Single listener errors don't affect other listeners
|
|
10
|
+
- ✅ **Type Safety** - Complete TypeScript type definitions
|
|
11
|
+
- ✅ **Extensibility** - Plugin system and unified Client interface
|
|
12
|
+
- ✅ **Agent SDK Integration** - Based on the latest Anthropic Claude Agent SDK
|
|
13
|
+
|
|
14
|
+
## Architecture Layers
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Application Layer (custom listeners, business logic)
|
|
18
|
+
↓
|
|
19
|
+
Event Bus (EventEmitter)
|
|
20
|
+
↓
|
|
21
|
+
Client Abstraction Layer (Claude Agent SDK, Codex)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @compyle/unagent
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Dependencies
|
|
31
|
+
|
|
32
|
+
This project uses `@anthropic-ai/claude-agent-sdk`, which requires installing [Claude Code CLI](https://code.claude.com/docs/en/setup) first:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# macOS/Linux/WSL
|
|
36
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
37
|
+
|
|
38
|
+
# Or use Homebrew
|
|
39
|
+
brew install --cask claude-code
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Tests
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm run test
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### Using Claude Agent SDK
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import {
|
|
54
|
+
ClaudeClient,
|
|
55
|
+
getEventBus,
|
|
56
|
+
} from "@compyle/unagent";
|
|
57
|
+
|
|
58
|
+
// 1. Get the event bus and set up custom listeners
|
|
59
|
+
const eventBus = getEventBus();
|
|
60
|
+
|
|
61
|
+
// Listen to all events
|
|
62
|
+
eventBus.on("event", (event) => {
|
|
63
|
+
console.log("Event:", event.type);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 2. Use Claude Agent Client
|
|
67
|
+
const claude = new ClaudeClient({
|
|
68
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
69
|
+
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
|
|
70
|
+
permissionMode: "bypassPermissions",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Stream call
|
|
74
|
+
const { events } = await claude.runStream({
|
|
75
|
+
query: "Analyze the current project structure and summarize",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
for await (const event of events) {
|
|
79
|
+
// Handle events
|
|
80
|
+
if (event.type === "item.end") {
|
|
81
|
+
console.log(event.message);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Define a Custom Tool
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { defineTool } from "@compyle/unagent";
|
|
90
|
+
import { z } from "zod";
|
|
91
|
+
|
|
92
|
+
const repoStats = defineTool({
|
|
93
|
+
name: "repo_stats",
|
|
94
|
+
description: "Summarize repository size and extensions",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
path: z.string().min(1),
|
|
97
|
+
},
|
|
98
|
+
handler: async ({ path }) => `Path: ${path}`,
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Using Custom Agents
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const customAgent = new ClaudeClient({
|
|
106
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
107
|
+
allowedTools: ["Read", "Grep", "Glob", "Task"],
|
|
108
|
+
permissionMode: "bypassPermissions",
|
|
109
|
+
// Define custom sub agents
|
|
110
|
+
agents: {
|
|
111
|
+
"code-reviewer": {
|
|
112
|
+
description: "Expert code review agent",
|
|
113
|
+
prompt: "Analyze code quality, security, and best practices.",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const result = await customAgent.run({
|
|
119
|
+
query: "Review code using code-reviewer agent",
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Read-only Agent (Safe Mode)
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const readOnlyAgent = new ClaudeClient({
|
|
127
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
128
|
+
allowedTools: ["Read", "Glob", "Grep"], // Read-only operations
|
|
129
|
+
permissionMode: "bypassPermissions",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const { events } = await readOnlyAgent.runStream({
|
|
133
|
+
query: "Analyze project structure",
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Using Codex Client with Permission Modes
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { CodexClient } from "@compyle/unagent";
|
|
141
|
+
|
|
142
|
+
// Safe mode: read-only access
|
|
143
|
+
const readOnlyCodex = new CodexClient({
|
|
144
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
145
|
+
permissionMode: "auto", // Maps to "read-only" sandbox
|
|
146
|
+
model: "gpt-4o",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Workspace write mode: can modify files in workspace
|
|
150
|
+
const writeCodex = new CodexClient({
|
|
151
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
152
|
+
permissionMode: "acceptEdits", // Maps to "workspace-write" sandbox
|
|
153
|
+
model: "gpt-4o",
|
|
154
|
+
addDirs: ["/tmp", "/var/tmp"], // Additional writable directories
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Danger mode: full system access (use with caution!)
|
|
158
|
+
const fullAccessCodex = new CodexClient({
|
|
159
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
160
|
+
permissionMode: "bypassPermissions", // Maps to "danger-full-access" sandbox
|
|
161
|
+
model: "gpt-4o",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const { events } = await writeCodex.runStream({
|
|
165
|
+
query: "Create a new file and update existing code",
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Python Usage
|
|
170
|
+
|
|
171
|
+
This repository includes a Python bridge package that calls the library through a Node.js process.
|
|
172
|
+
|
|
173
|
+
### Install with uv (Recommended)
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Install uv
|
|
177
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
178
|
+
|
|
179
|
+
# Or use Homebrew
|
|
180
|
+
brew install uv
|
|
181
|
+
|
|
182
|
+
# Install project dependencies
|
|
183
|
+
cd python
|
|
184
|
+
bash install_with_uv.sh
|
|
185
|
+
|
|
186
|
+
# Activate virtual environment
|
|
187
|
+
source .venv/bin/activate
|
|
188
|
+
|
|
189
|
+
# Run examples
|
|
190
|
+
ANTHROPIC_API_KEY=your_key python examples/python-demo/main.py
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Traditional Installation Method
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm run build
|
|
197
|
+
python -m venv .venv
|
|
198
|
+
source .venv/bin/activate
|
|
199
|
+
pip install -e ./python
|
|
200
|
+
ANTHROPIC_API_KEY=your_key python examples/python-demo/main.py
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from unagent import ClaudeClient
|
|
205
|
+
|
|
206
|
+
client = ClaudeClient({"enable_message_mode": True})
|
|
207
|
+
stream = client.run_stream({"query": "Summarize the repo"})
|
|
208
|
+
for event in stream["events"]:
|
|
209
|
+
print(event["type"])
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Examples
|
|
213
|
+
|
|
214
|
+
### Node.js Demo (Orchestration + Custom Tools + Event Bus)
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
export ANTHROPIC_API_KEY=your_claude_key
|
|
218
|
+
cd examples/nodejs-demo
|
|
219
|
+
npm install
|
|
220
|
+
npm start "Analyze src/ and generate documentation with repo stats"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Python Demos
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
export ANTHROPIC_API_KEY=your_claude_key
|
|
227
|
+
python examples/python-demo/main.py "Analyze src/ and generate documentation with repo stats"
|
|
228
|
+
|
|
229
|
+
export CODEX_API_KEY=your_codex_key
|
|
230
|
+
python examples/python-demo/multi_agent_collaboration.py "Draft an API checklist for a new service"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Core Components
|
|
234
|
+
|
|
235
|
+
### 1. Event System (`src/events/`)
|
|
236
|
+
|
|
237
|
+
#### Event Types (`event-types.ts`)
|
|
238
|
+
|
|
239
|
+
Defines all unified event types:
|
|
240
|
+
|
|
241
|
+
- `SessionStartEvent` - Session starts
|
|
242
|
+
- `SessionEndEvent` - Session ends
|
|
243
|
+
- `ItemStartEvent` - Message/item starts
|
|
244
|
+
- `ItemChunkEvent` - Streaming chunk for a message/item
|
|
245
|
+
- `ItemEndEvent` - Message/item ends with full message
|
|
246
|
+
- `ErrorEvent` - Error event
|
|
247
|
+
|
|
248
|
+
#### Event Bus (`event-bus.ts`)
|
|
249
|
+
|
|
250
|
+
Provides an event bus using the publish-subscribe pattern:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { getEventBus } from "@compyle/unagent";
|
|
254
|
+
|
|
255
|
+
const eventBus = getEventBus();
|
|
256
|
+
|
|
257
|
+
// Register listeners
|
|
258
|
+
eventBus.on("event", (event) => {
|
|
259
|
+
console.log("Event:", event);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Emit events
|
|
263
|
+
eventBus.emit({
|
|
264
|
+
type: "session.start",
|
|
265
|
+
timestamp: Date.now(),
|
|
266
|
+
clientType: ClientType.CLAUDE,
|
|
267
|
+
sessionId: "session-123",
|
|
268
|
+
params: { query: "Hello" },
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 2. Client Abstraction Layer (`src/client/`)
|
|
273
|
+
|
|
274
|
+
#### BaseClient
|
|
275
|
+
|
|
276
|
+
Base class for all AI clients, defining a unified interface:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
interface IBaseClient {
|
|
280
|
+
run(params: ClientRunParams): Promise<ClientRunResult>;
|
|
281
|
+
runStream(params: ClientRunParams): Promise<ClientStreamResult>;
|
|
282
|
+
on(listener: EventListener): string;
|
|
283
|
+
once(listener: EventListener): string;
|
|
284
|
+
off(listenerOrId: string | EventListener): void;
|
|
285
|
+
removeAllListeners(): void;
|
|
286
|
+
getClientType(): ClientType;
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### ClaudeClient
|
|
291
|
+
|
|
292
|
+
Implementation based on Anthropic Agent SDK:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const claude = new ClaudeClient({
|
|
296
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
297
|
+
allowedTools: ["Read", "Write", "Edit", "Bash"],
|
|
298
|
+
permissionMode: "bypassPermissions",
|
|
299
|
+
useProjectConfig: true, // Read .claude/ directory config
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Stream call
|
|
303
|
+
const { events } = await claude.runStream({ query: "Analyze code" });
|
|
304
|
+
|
|
305
|
+
// Non-stream call
|
|
306
|
+
const result = await claude.run({ query: "Analyze code" });
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**ClaudeClient Configuration Options:**
|
|
310
|
+
|
|
311
|
+
| Option | Type | Description |
|
|
312
|
+
|------|------|------|
|
|
313
|
+
| `apiKey` | `string` | Anthropic API key |
|
|
314
|
+
| `allowedTools` | `string[]` | List of allowed tools |
|
|
315
|
+
| `permissionMode` | `"auto" \| "bypassPermissions" \| "acceptEdits"` | Permission mode |
|
|
316
|
+
| `agents` | `Record<string, AgentDefinition>` | Custom sub agents |
|
|
317
|
+
| `mcpServers` | `Record<string, McpServerConfig>` | MCP server configuration |
|
|
318
|
+
| `hooks` | `Record<string, any>` | Hooks configuration |
|
|
319
|
+
| `useProjectConfig` | `boolean` | Whether to use project config |
|
|
320
|
+
| `workingDirectory` | `string` | Working directory |
|
|
321
|
+
|
|
322
|
+
### Using Codex Client
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { CodexClient } from "@compyle/unagent";
|
|
326
|
+
|
|
327
|
+
const codex = new CodexClient({
|
|
328
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
329
|
+
permissionMode: "acceptEdits", // Maps to "workspace-write" sandbox mode
|
|
330
|
+
model: "gpt-4o",
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Stream call
|
|
334
|
+
const { events } = await codex.runStream({ query: "Analyze code" });
|
|
335
|
+
|
|
336
|
+
// Non-stream call
|
|
337
|
+
const result = await codex.run({ query: "Analyze code" });
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**CodexClient Configuration Options:**
|
|
341
|
+
|
|
342
|
+
| Option | Type | Description |
|
|
343
|
+
|------|------|------|
|
|
344
|
+
| `apiKey` | `string` | OpenAI API key |
|
|
345
|
+
| `permissionMode` | `"auto" \| "default" \| "acceptEdits" \| "bypassPermissions"` | Permission mode (maps to sandbox mode) |
|
|
346
|
+
| `model` | `string` | Model name (e.g., "gpt-4o") |
|
|
347
|
+
| `systemPrompt` | `string` | System prompt |
|
|
348
|
+
| `mcpServers` | `Record<string, McpServerConfig>` | MCP server configuration |
|
|
349
|
+
| `addDirs` | `string[]` | Additional directories for access |
|
|
350
|
+
| `workingDirectory` | `string` | Working directory |
|
|
351
|
+
| `env` | `Record<string, string>` | Environment variables (e.g., `CODEX_HOME`) |
|
|
352
|
+
|
|
353
|
+
If `env.CODEX_HOME` is not set, a temporary directory is created under `{TMPDIR}/unagent/sessions/<id>`.
|
|
354
|
+
|
|
355
|
+
**Codex Permission Mode Mapping:**
|
|
356
|
+
|
|
357
|
+
The `permissionMode` option maps to Codex sandbox modes as follows:
|
|
358
|
+
|
|
359
|
+
| `permissionMode` | Codex `sandboxMode` | Description |
|
|
360
|
+
|-----------------|-------------------|-------------|
|
|
361
|
+
| `"auto"` / `"default"` / `"dontAsk"` / `"plan"` | `"read-only"` | Read-only access (safest) |
|
|
362
|
+
| `"acceptEdits"` | `"workspace-write"` | Can write to workspace and additional directories |
|
|
363
|
+
| `"bypassPermissions"` | `"danger-full-access"` | No sandbox, full system access (riskiest) |
|
|
364
|
+
|
|
365
|
+
**Available Tools:**
|
|
366
|
+
- `Read` - Read files
|
|
367
|
+
- `Write` - Create new files
|
|
368
|
+
- `Edit` - Edit existing files
|
|
369
|
+
- `Bash` - Run commands
|
|
370
|
+
- `Glob` - Find files
|
|
371
|
+
- `Grep` - Search file contents
|
|
372
|
+
- `WebSearch` - Web search
|
|
373
|
+
- `WebFetch` - Fetch web content
|
|
374
|
+
- `Task` - Call sub agent
|
|
375
|
+
- `AskUserQuestion` - Ask user questions
|
|
376
|
+
|
|
377
|
+
## Event Types
|
|
378
|
+
|
|
379
|
+
All events inherit from `BaseEvent` and include `timestamp`, `clientType`, and optional `sessionId`.
|
|
380
|
+
|
|
381
|
+
### SessionStartEvent
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
{
|
|
385
|
+
type: "session.start",
|
|
386
|
+
timestamp: number,
|
|
387
|
+
clientType: ClientType,
|
|
388
|
+
sessionId: string,
|
|
389
|
+
params?: { query?: string, ... }
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### SessionEndEvent
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
{
|
|
397
|
+
type: "session.end",
|
|
398
|
+
timestamp: number,
|
|
399
|
+
clientType: ClientType,
|
|
400
|
+
sessionId?: string,
|
|
401
|
+
reason?: string
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### ItemStartEvent
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
{
|
|
409
|
+
type: "item.start",
|
|
410
|
+
timestamp: number,
|
|
411
|
+
clientType: ClientType,
|
|
412
|
+
message: Message
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### ItemChunkEvent
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
{
|
|
420
|
+
type: "item.chunk",
|
|
421
|
+
timestamp: number,
|
|
422
|
+
clientType: ClientType,
|
|
423
|
+
message: Message
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### ItemEndEvent
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
{
|
|
431
|
+
type: "item.end",
|
|
432
|
+
timestamp: number,
|
|
433
|
+
clientType: ClientType,
|
|
434
|
+
message: Message
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### ErrorEvent
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
{
|
|
442
|
+
type: "error",
|
|
443
|
+
timestamp: number,
|
|
444
|
+
clientType: ClientType,
|
|
445
|
+
error: Error,
|
|
446
|
+
context?: { stage?: string, ... }
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Advanced Usage
|
|
451
|
+
|
|
452
|
+
### Adding MCP Servers
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
const claude = new ClaudeClient({
|
|
456
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
457
|
+
allowedTools: ["Read", "Bash"],
|
|
458
|
+
mcpServers: {
|
|
459
|
+
playwright: {
|
|
460
|
+
command: "npx",
|
|
461
|
+
args: ["@playwright/mcp@latest"],
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Using Hooks
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
const claude = new ClaudeClient({
|
|
471
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
472
|
+
hooks: {
|
|
473
|
+
PostToolUse: [{
|
|
474
|
+
matcher: "Edit|Write",
|
|
475
|
+
hooks: [async (input) => {
|
|
476
|
+
// Log file modifications
|
|
477
|
+
console.log("File modified:", input.tool_input?.file_path);
|
|
478
|
+
return {};
|
|
479
|
+
}],
|
|
480
|
+
}],
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Session Resumption
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
let sessionId: string | undefined;
|
|
489
|
+
|
|
490
|
+
// First query
|
|
491
|
+
for await (const message of query(...)) {
|
|
492
|
+
if (message.type === "system" && message.subtype === "init") {
|
|
493
|
+
sessionId = message.session_id;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Resume session
|
|
498
|
+
const resumed = new ClaudeClient({
|
|
499
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
500
|
+
resume: sessionId,
|
|
501
|
+
});
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Development
|
|
505
|
+
|
|
506
|
+
### Build
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
npm run build
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Examples
|
|
513
|
+
|
|
514
|
+
See `examples/basic-usage.ts` for complete usage examples.
|
|
515
|
+
|
|
516
|
+
## File Structure
|
|
517
|
+
|
|
518
|
+
```
|
|
519
|
+
src/
|
|
520
|
+
├── events/ # Event system core
|
|
521
|
+
│ ├── event-types.ts # Event type definitions
|
|
522
|
+
│ ├── event-bus.ts # Event bus implementation
|
|
523
|
+
│ └── index.ts # Export module
|
|
524
|
+
├── messages/ # Message system
|
|
525
|
+
│ ├── message-types.ts # Message type definitions
|
|
526
|
+
│ ├── message-utils.ts # Message utility functions
|
|
527
|
+
│ └── index.ts # Export module
|
|
528
|
+
├── client/ # Client abstraction layer
|
|
529
|
+
│ ├── base-client.ts # Base client interface and abstract class
|
|
530
|
+
│ ├── codex/ # Codex client implementation
|
|
531
|
+
│ └── claude.ts # Claude Agent SDK implementation
|
|
532
|
+
├── tools/ # Tool system
|
|
533
|
+
├── agents/ # Agent system
|
|
534
|
+
└── index.ts # Main entry file
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Migration Guide
|
|
538
|
+
|
|
539
|
+
If migrating from the old `@anthropic-ai/sdk`, the main changes are:
|
|
540
|
+
|
|
541
|
+
1. **No need to call `messages.create()` directly** - Agent SDK automatically handles tool execution through `query()` function
|
|
542
|
+
2. **Configuration options changed** - Use `allowedTools` and `permissionMode` instead of `model`, `maxTokens`, etc.
|
|
543
|
+
3. **Event format changed** - New SDK returns different event formats, which are automatically converted to unified format
|
|
544
|
+
|
|
545
|
+
## License
|
|
546
|
+
|
|
547
|
+
MIT
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RegisteredTool } from "../tools/tool-types.js";
|
|
2
|
+
import type { Agent, AgentDefinition } from "./define-agent.js";
|
|
3
|
+
export declare function agentAsTool(name: string, description: string, agent: AgentDefinition | Agent): RegisteredTool;
|
|
4
|
+
//# sourceMappingURL=agent-as-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-as-tool.d.ts","sourceRoot":"","sources":["../../src/agents/agent-as-tool.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAOhE,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,eAAe,GAAG,KAAK,GAC7B,cAAc,CAkDhB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineTool } from "../tools/define-tool.js";
|
|
3
|
+
import { buildAgentClientConfig, resolveAgentDefinition } from "./define-agent.js";
|
|
4
|
+
import { ClaudeClient } from "../client/claude.js";
|
|
5
|
+
import { CodexClient } from "../client/codex/index.js";
|
|
6
|
+
import { stringifyContent } from "../client/transformers/content-utils.js";
|
|
7
|
+
export function agentAsTool(name, description, agent) {
|
|
8
|
+
if (!name || typeof name !== "string") {
|
|
9
|
+
throw new Error("Tool name must be a non-empty string");
|
|
10
|
+
}
|
|
11
|
+
if (!description || typeof description !== "string") {
|
|
12
|
+
throw new Error("Tool description must be a non-empty string");
|
|
13
|
+
}
|
|
14
|
+
if (!agent || typeof agent !== "object") {
|
|
15
|
+
throw new Error("Agent must be a valid definition");
|
|
16
|
+
}
|
|
17
|
+
const definition = resolveAgentDefinition(agent);
|
|
18
|
+
const client = definition.client;
|
|
19
|
+
if (client !== "claude" && client !== "codex") {
|
|
20
|
+
throw new Error('Agent client must be "claude" or "codex" to run as a tool');
|
|
21
|
+
}
|
|
22
|
+
const config = buildAgentClientConfig(agent);
|
|
23
|
+
return defineTool({
|
|
24
|
+
name,
|
|
25
|
+
description,
|
|
26
|
+
inputSchema: {
|
|
27
|
+
input: z.string(),
|
|
28
|
+
},
|
|
29
|
+
handler: async ({ input }) => {
|
|
30
|
+
if (typeof input !== "string") {
|
|
31
|
+
throw new Error("Tool input must be a string");
|
|
32
|
+
}
|
|
33
|
+
const clientInstance = client === "claude"
|
|
34
|
+
? new ClaudeClient(config)
|
|
35
|
+
: new CodexClient(config);
|
|
36
|
+
try {
|
|
37
|
+
const result = await clientInstance.run({ query: input });
|
|
38
|
+
if (!result.success) {
|
|
39
|
+
throw result.error ?? new Error(`Agent tool "${name}" failed`);
|
|
40
|
+
}
|
|
41
|
+
return formatAgentOutput(result.data);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
const maybeCleanup = clientInstance.cleanup;
|
|
45
|
+
if (typeof maybeCleanup === "function") {
|
|
46
|
+
maybeCleanup();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function formatAgentOutput(data) {
|
|
53
|
+
if (data === undefined || data === null) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
if (typeof data === "string") {
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(data)) {
|
|
60
|
+
const parts = data.map(formatAgentOutput).filter(Boolean);
|
|
61
|
+
return parts.join("\n");
|
|
62
|
+
}
|
|
63
|
+
if (typeof data === "object") {
|
|
64
|
+
const record = data;
|
|
65
|
+
if (record.content !== undefined) {
|
|
66
|
+
return stringifyContent(record.content);
|
|
67
|
+
}
|
|
68
|
+
if (record.messageContents !== undefined) {
|
|
69
|
+
return formatMessageContents(record.messageContents);
|
|
70
|
+
}
|
|
71
|
+
if (record.text !== undefined) {
|
|
72
|
+
return stringifyContent(record.text);
|
|
73
|
+
}
|
|
74
|
+
if (record.value !== undefined) {
|
|
75
|
+
return stringifyContent(record.value);
|
|
76
|
+
}
|
|
77
|
+
return stringifyContent(record);
|
|
78
|
+
}
|
|
79
|
+
return String(data);
|
|
80
|
+
}
|
|
81
|
+
function formatMessageContents(value) {
|
|
82
|
+
if (!Array.isArray(value)) {
|
|
83
|
+
return formatAgentOutput(value);
|
|
84
|
+
}
|
|
85
|
+
const parts = [];
|
|
86
|
+
for (const item of value) {
|
|
87
|
+
if (!item || typeof item !== "object") {
|
|
88
|
+
parts.push(stringifyContent(item));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const record = item;
|
|
92
|
+
const nestedValue = record.value;
|
|
93
|
+
const legacyText = (nestedValue && typeof nestedValue === "object")
|
|
94
|
+
? nestedValue?.[0]?.text?.value
|
|
95
|
+
: undefined;
|
|
96
|
+
const itemContent = record.content ?? record.text ?? legacyText ?? record?.text?.value;
|
|
97
|
+
if (itemContent !== undefined) {
|
|
98
|
+
parts.push(stringifyContent(itemContent));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
parts.push(stringifyContent(record));
|
|
102
|
+
}
|
|
103
|
+
return parts.filter(Boolean).join("\n");
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=agent-as-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-as-tool.js","sourceRoot":"","sources":["../../src/agents/agent-as-tool.ts"],"names":[],"mappings":"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"}
|