@witqq/agent-sdk 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
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,310 @@
1
+ # agent-sdk
2
+
3
+ Multi-backend AI agent abstraction layer for Node.js. Switch between Copilot CLI, Claude CLI, and Vercel AI SDK backends with a unified API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @witqq/agent-sdk zod
9
+ ```
10
+
11
+ ## Backends
12
+
13
+ | Backend | Peer dependency | Type |
14
+ |---|---|---|
15
+ | `copilot` | `@github/copilot-sdk` | CLI subprocess |
16
+ | `claude` | `@anthropic-ai/claude-agent-sdk` | CLI subprocess |
17
+ | `vercel-ai` | `ai` + `@ai-sdk/openai-compatible` | API-based |
18
+
19
+ Install only the backend you need:
20
+
21
+ ```bash
22
+ npm install @github/copilot-sdk # copilot
23
+ npm install @anthropic-ai/claude-agent-sdk # claude
24
+ npm install ai @ai-sdk/openai-compatible # vercel-ai
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```typescript
30
+ import { createAgentService } from "@witqq/agent-sdk";
31
+ import { z } from "zod";
32
+
33
+ const service = await createAgentService("copilot", { useLoggedInUser: true });
34
+
35
+ const agent = service.createAgent({
36
+ systemPrompt: "You are a helpful assistant.",
37
+ tools: [
38
+ {
39
+ name: "search",
40
+ description: "Search the web",
41
+ parameters: z.object({ query: z.string() }),
42
+ execute: async ({ query }) => ({ results: [`Result for: ${query}`] }),
43
+ },
44
+ ],
45
+ });
46
+
47
+ const result = await agent.run("Find news about AI");
48
+ console.log(result.output);
49
+
50
+ agent.dispose();
51
+ await service.dispose();
52
+ ```
53
+
54
+ ## Tool Definition
55
+
56
+ Tools are defined with a Zod schema for parameters and an `execute` function:
57
+
58
+ ```typescript
59
+ import { z } from "zod";
60
+ import type { ToolDefinition } from "@witqq/agent-sdk";
61
+
62
+ // Basic tool
63
+ const searchTool: ToolDefinition = {
64
+ name: "search",
65
+ description: "Search the web",
66
+ parameters: z.object({ query: z.string() }),
67
+ execute: async ({ query }) => ({ results: [`Result for: ${query}`] }),
68
+ };
69
+
70
+ // Tool requiring user approval before execution
71
+ const writeFileTool: ToolDefinition = {
72
+ name: "write_file",
73
+ description: "Write content to a file",
74
+ parameters: z.object({ path: z.string(), content: z.string() }),
75
+ needsApproval: true,
76
+ execute: async ({ path, content }) => ({ written: true, path }),
77
+ };
78
+ ```
79
+
80
+ When `needsApproval: true`, the `supervisor.onPermission` callback is invoked before execution. Without a supervisor, approval-required tools are denied by default.
81
+
82
+ ## Permission Handling
83
+
84
+ The `supervisor` hooks intercept permission requests and user-facing questions:
85
+
86
+ ```typescript
87
+ const agent = service.createAgent({
88
+ systemPrompt: "File assistant",
89
+ tools: [writeFileTool],
90
+ supervisor: {
91
+ onPermission: async (req, signal) => {
92
+ // req.toolName, req.toolArgs, req.suggestedScope
93
+ console.log(`${req.toolName} wants to run with`, req.toolArgs);
94
+ return {
95
+ allowed: true,
96
+ scope: "session", // "once" | "session" | "project" | "always"
97
+ // modifiedInput: { ... }, // optionally modify args before execution
98
+ // reason: "...", // denial reason (if allowed: false)
99
+ };
100
+ },
101
+ onAskUser: async (req, signal) => {
102
+ // req.question, req.choices, req.allowFreeform
103
+ return { answer: "yes", wasFreeform: false };
104
+ },
105
+ },
106
+ });
107
+ ```
108
+
109
+ ## Permission Store
110
+
111
+ Persist permission decisions across runs so approved tools don't re-prompt:
112
+
113
+ ```typescript
114
+ import { createDefaultPermissionStore } from "@witqq/agent-sdk";
115
+
116
+ const store = createDefaultPermissionStore("./my-project");
117
+ const agent = service.createAgent({
118
+ systemPrompt: "File assistant",
119
+ permissionStore: store,
120
+ tools: [writeFileTool],
121
+ supervisor: {
122
+ onPermission: async (req) => ({ allowed: true, scope: "project" }),
123
+ },
124
+ });
125
+ ```
126
+
127
+ Scopes control persistence:
128
+ - `"once"` — not stored, one-time approval
129
+ - `"session"` — in-memory, cleared on dispose
130
+ - `"project"` — persisted to `<projectDir>/.agent-sdk/permissions.json`
131
+ - `"always"` — persisted to `~/.agent-sdk/permissions.json`
132
+
133
+ Custom stores implement `IPermissionStore`:
134
+
135
+ ```typescript
136
+ interface IPermissionStore {
137
+ isApproved(toolName: string): Promise<boolean>;
138
+ approve(toolName: string, scope: PermissionScope): Promise<void>;
139
+ revoke(toolName: string): Promise<void>;
140
+ clear(): Promise<void>;
141
+ dispose(): Promise<void>;
142
+ }
143
+ ```
144
+
145
+ ## Structured Output
146
+
147
+ Extract typed data from LLM responses using `runStructured`:
148
+
149
+ ```typescript
150
+ import { z } from "zod";
151
+
152
+ const result = await agent.runStructured(
153
+ "What is the capital of France?",
154
+ {
155
+ schema: z.object({
156
+ city: z.string(),
157
+ country: z.string(),
158
+ population: z.number(),
159
+ }),
160
+ name: "city_info", // optional, helps the LLM
161
+ description: "City details", // optional
162
+ },
163
+ );
164
+
165
+ console.log(result.structuredOutput);
166
+ // { city: "Paris", country: "France", population: 2161000 }
167
+ ```
168
+
169
+ The Vercel AI backend uses `generateObject()` for structured output. Copilot and Claude backends extract structured data from the LLM text response.
170
+
171
+ ## Streaming Events
172
+
173
+ All backends emit the same event types:
174
+
175
+ ```typescript
176
+ for await (const event of agent.stream("Tell me a story")) {
177
+ switch (event.type) {
178
+ case "text_delta":
179
+ process.stdout.write(event.text);
180
+ break;
181
+ case "tool_call_start":
182
+ console.log(`Calling ${event.toolName}`, event.args);
183
+ break;
184
+ case "tool_call_end":
185
+ console.log(`${event.toolName} returned`, event.result);
186
+ break;
187
+ case "error":
188
+ console.error(event.error, "recoverable:", event.recoverable);
189
+ break;
190
+ case "done":
191
+ console.log("Final:", event.finalOutput);
192
+ break;
193
+ }
194
+ }
195
+ ```
196
+
197
+ | Event | Fields | Description |
198
+ |-------|--------|-------------|
199
+ | `text_delta` | `text` | Incremental text output |
200
+ | `thinking_start` | — | Model started reasoning |
201
+ | `thinking_end` | — | Model finished reasoning |
202
+ | `tool_call_start` | `toolName`, `args` | Tool invocation began |
203
+ | `tool_call_end` | `toolName`, `result` | Tool invocation completed |
204
+ | `permission_request` | `request` | Permission check initiated |
205
+ | `permission_response` | `toolName`, `decision` | Permission decision made |
206
+ | `ask_user` | `request` | User input requested |
207
+ | `ask_user_response` | `answer` | User response received |
208
+ | `usage_update` | `promptTokens`, `completionTokens` | Token usage |
209
+ | `error` | `error`, `recoverable` | Error during execution |
210
+ | `done` | `finalOutput`, `structuredOutput?` | Execution completed |
211
+
212
+ ## Backend-Specific Options
213
+
214
+ ### Copilot
215
+
216
+ ```typescript
217
+ import { createCopilotService } from "@witqq/agent-sdk/copilot";
218
+
219
+ const service = createCopilotService({
220
+ useLoggedInUser: true, // use GitHub CLI auth
221
+ cliPath: "/path/to/copilot", // optional custom CLI path
222
+ workingDirectory: process.cwd(),
223
+ githubToken: "ghp_...", // optional, alternative to useLoggedInUser
224
+ });
225
+ ```
226
+
227
+ ### Claude
228
+
229
+ ```typescript
230
+ import { createClaudeService } from "@witqq/agent-sdk/claude";
231
+
232
+ const service = createClaudeService({
233
+ cliPath: "/path/to/claude", // optional custom CLI path
234
+ workingDirectory: process.cwd(),
235
+ maxTurns: 10,
236
+ });
237
+ ```
238
+
239
+ `supervisor.onAskUser` is not supported by the Claude backend; a warning is emitted if set.
240
+
241
+ ### Vercel AI (OpenRouter / OpenAI-compatible)
242
+
243
+ ```typescript
244
+ import { createVercelAIService } from "@witqq/agent-sdk/vercel-ai";
245
+
246
+ const service = createVercelAIService({
247
+ apiKey: process.env.OPENROUTER_API_KEY!,
248
+ baseUrl: "https://openrouter.ai/api/v1", // default
249
+ provider: "openrouter", // default
250
+ });
251
+
252
+ const agent = service.createAgent({
253
+ model: "anthropic/claude-sonnet-4-5",
254
+ systemPrompt: "You are a helpful assistant.",
255
+ tools: [searchTool],
256
+ });
257
+ ```
258
+
259
+ Uses `generateText()` for runs, `generateObject()` for structured output, `streamText()` for streaming. Supports `supervisor.onAskUser` via an injected `ask_user` tool.
260
+
261
+ ## Switching Backends
262
+
263
+ All backends share the same `AgentConfig` and return the same `AgentResult`. To switch backends, change only the service creation:
264
+
265
+ ```typescript
266
+ import { createAgentService } from "@witqq/agent-sdk";
267
+ import { z } from "zod";
268
+
269
+ const tools = [
270
+ {
271
+ name: "greet",
272
+ description: "Greet a user",
273
+ parameters: z.object({ name: z.string() }),
274
+ execute: async ({ name }) => ({ message: `Hello, ${name}!` }),
275
+ },
276
+ ];
277
+
278
+ const config = {
279
+ systemPrompt: "You are a helpful assistant.",
280
+ tools,
281
+ };
282
+
283
+ // Switch backend by changing the first argument:
284
+ const service = await createAgentService("copilot", { useLoggedInUser: true });
285
+ // const service = await createAgentService("claude", { workingDirectory: "." });
286
+ // const service = await createAgentService("vercel-ai", { apiKey: "..." });
287
+
288
+ const agent = service.createAgent(config);
289
+ const result = await agent.run("Greet Alice");
290
+ ```
291
+
292
+ Or use direct backend imports to avoid lazy loading:
293
+
294
+ ```typescript
295
+ import { createCopilotService } from "@witqq/agent-sdk/copilot";
296
+ import { createClaudeService } from "@witqq/agent-sdk/claude";
297
+ import { createVercelAIService } from "@witqq/agent-sdk/vercel-ai";
298
+ ```
299
+
300
+ ## Build
301
+
302
+ ```bash
303
+ npm run build # tsup → ESM + CJS
304
+ npm run test # vitest
305
+ npm run typecheck # tsc --noEmit
306
+ ```
307
+
308
+ ## License
309
+
310
+ MIT