@shareai-lab/kode-sdk 1.0.0-beta.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/README.md +312 -0
- package/dist/core/agent.d.ts +85 -0
- package/dist/core/agent.js +687 -0
- package/dist/core/events.d.ts +19 -0
- package/dist/core/events.js +121 -0
- package/dist/core/hooks.d.ts +23 -0
- package/dist/core/hooks.js +71 -0
- package/dist/core/pool.d.ts +33 -0
- package/dist/core/pool.js +91 -0
- package/dist/core/room.d.ts +15 -0
- package/dist/core/room.js +57 -0
- package/dist/core/scheduler.d.ts +26 -0
- package/dist/core/scheduler.js +184 -0
- package/dist/core/types.d.ts +192 -0
- package/dist/core/types.js +13 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +57 -0
- package/dist/infra/provider.d.ts +58 -0
- package/dist/infra/provider.js +118 -0
- package/dist/infra/sandbox.d.ts +39 -0
- package/dist/infra/sandbox.js +77 -0
- package/dist/infra/store.d.ts +32 -0
- package/dist/infra/store.js +132 -0
- package/dist/tools/bash.d.ts +63 -0
- package/dist/tools/bash.js +99 -0
- package/dist/tools/builtin.d.ts +15 -0
- package/dist/tools/builtin.js +96 -0
- package/dist/tools/fs.d.ts +96 -0
- package/dist/tools/fs.js +96 -0
- package/dist/tools/task.d.ts +38 -0
- package/dist/tools/task.js +45 -0
- package/dist/utils/session-id.d.ts +21 -0
- package/dist/utils/session-id.js +64 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Kode SDK v1.5.1
|
|
2
|
+
|
|
3
|
+
Event-driven Agent Model Client SDK for building long-running, collaborative AI agents.
|
|
4
|
+
|
|
5
|
+
## Vision
|
|
6
|
+
|
|
7
|
+
Transform the experience of collaborating with colleagues into a minimal yet sufficient API for **sending messages, giving instructions, interrupting, forking, and resuming** with long-running online Agents.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Event-Driven First**: Subscribe to data plane events (text/tools/usage), control plane callbacks (approvals/hooks)
|
|
12
|
+
- **Multi-Agent Ready**: Long-running independent agents with colleague-style collaboration
|
|
13
|
+
- **Strong Recovery**: 7-type breakpoint recovery; seals without inserting system text; defaults to READY state
|
|
14
|
+
- **Forkable**: Safe-Fork-Points (SFP) naturally exist at tool results and text-only assistant messages
|
|
15
|
+
- **Tool Safety**: Denial doesn't throw exceptions; rejected tool results are logged and auditable
|
|
16
|
+
- **High Performance**: Concurrent tool execution (rate-limited), streaming model completion, incremental events (cursor/since)
|
|
17
|
+
- **Extensible**: MCP tools, Sandbox drivers, Provider adapters, Store backends, Scheduler DSL
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install kode-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Agent, JSONStore, LocalSandbox, AnthropicProvider, builtin } from 'kode-sdk';
|
|
27
|
+
|
|
28
|
+
const agent = new Agent({
|
|
29
|
+
sessionId: 'agent:assistant/session:demo',
|
|
30
|
+
provider: new AnthropicProvider(process.env.ANTHROPIC_API_KEY),
|
|
31
|
+
store: new JSONStore('./data'),
|
|
32
|
+
sandbox: LocalSandbox.local({ workDir: './workspace' }),
|
|
33
|
+
tools: [...builtin.fs({ workDir: './workspace' }), ...builtin.bash()],
|
|
34
|
+
system: 'You are a helpful assistant.',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Send message
|
|
38
|
+
await agent.send('Please list all files in the workspace');
|
|
39
|
+
|
|
40
|
+
// Subscribe to events
|
|
41
|
+
for await (const event of agent.subscribe()) {
|
|
42
|
+
if (event.type === 'text') {
|
|
43
|
+
console.log('Agent:', event.text);
|
|
44
|
+
}
|
|
45
|
+
if (event.type === 'state' && event.state === 'READY') {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Core Concepts
|
|
52
|
+
|
|
53
|
+
### 1. Agent States
|
|
54
|
+
|
|
55
|
+
- **READY**: Waiting for user input
|
|
56
|
+
- **BUSY**: Processing request or executing tools
|
|
57
|
+
- **PAUSED**: Waiting for permission approval
|
|
58
|
+
|
|
59
|
+
### 2. Safe-Fork-Points (SFP)
|
|
60
|
+
|
|
61
|
+
SFPs are created when:
|
|
62
|
+
- Tool results are written (`tool_result` blocks)
|
|
63
|
+
- Assistant provides text-only response (no tools)
|
|
64
|
+
|
|
65
|
+
Use SFPs to:
|
|
66
|
+
- Fork sessions at safe states
|
|
67
|
+
- Create bookmarks for rollback
|
|
68
|
+
- Branch conversations
|
|
69
|
+
|
|
70
|
+
### 3. Event System
|
|
71
|
+
|
|
72
|
+
**MINIMAL Event Kinds** (default subscription):
|
|
73
|
+
- `text_chunk`: Streaming text delta
|
|
74
|
+
- `text`: Complete text content
|
|
75
|
+
- `tool_use`: Tool invocation
|
|
76
|
+
- `tool_result`: Tool execution result
|
|
77
|
+
- `usage`: Token/cost metrics
|
|
78
|
+
- `error`: Typed errors
|
|
79
|
+
- `messages_update`: Message history changed
|
|
80
|
+
|
|
81
|
+
**Additional Events** (opt-in):
|
|
82
|
+
- `state`: Agent state changes
|
|
83
|
+
- `commit`: SFP created
|
|
84
|
+
- `permission_ask`: Approval required
|
|
85
|
+
- `permission_decision`: Approval result
|
|
86
|
+
- `resume`: Recovery from crash
|
|
87
|
+
- `forked`: New session created
|
|
88
|
+
|
|
89
|
+
### 4. Hooks
|
|
90
|
+
|
|
91
|
+
Intercept and modify tool execution:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
agent.use({
|
|
95
|
+
preToolUse(call, ctx) {
|
|
96
|
+
// Validate, modify args, or deny
|
|
97
|
+
if (!ctx.sandbox.fs.isInside(call.args.file)) {
|
|
98
|
+
return { decision: 'deny', reason: 'path out of sandbox' };
|
|
99
|
+
}
|
|
100
|
+
// Request approval
|
|
101
|
+
return { decision: 'ask', meta: { title: 'File Access', path: call.args.file } };
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
postToolUse(outcome, ctx) {
|
|
105
|
+
// Trim large results
|
|
106
|
+
if (String(outcome.content).length > 100_000) {
|
|
107
|
+
const path = ctx.sandbox.fs.temp(`tool-${outcome.id}.log`);
|
|
108
|
+
ctx.sandbox.fs.write(path, outcome.content);
|
|
109
|
+
return { update: { content: `[Full output at ./${path}]` } };
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 5. Scheduler
|
|
116
|
+
|
|
117
|
+
Time-based and step-based triggers:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
agent.schedule()
|
|
121
|
+
.every('10m', () => agent.send('Status check'))
|
|
122
|
+
.everySteps(20, () => agent.send('Reminder: review security guidelines'))
|
|
123
|
+
.daily('09:00', () => agent.send('Daily report'))
|
|
124
|
+
.weekly('Mon 09:00', () => agent.send('Weekly summary'));
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 6. AgentPool
|
|
128
|
+
|
|
129
|
+
Manage multiple agent instances:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const pool = new AgentPool({
|
|
133
|
+
store: new JSONStore('./data'),
|
|
134
|
+
maxAgents: 50,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const agent = pool.create(sessionId, template, options);
|
|
138
|
+
const existing = pool.get(sessionId);
|
|
139
|
+
const agents = pool.list({ prefix: 'org:acme/' });
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 7. Room (Group Chat)
|
|
143
|
+
|
|
144
|
+
Multi-agent collaboration:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const room = new Room(pool);
|
|
148
|
+
room.join('alice', 'agent:pm/session:alice');
|
|
149
|
+
room.join('bob', 'agent:dev/session:bob');
|
|
150
|
+
|
|
151
|
+
// Direct mention
|
|
152
|
+
await room.say('alice', '@bob Please review the PR');
|
|
153
|
+
|
|
154
|
+
// Broadcast (excludes sender)
|
|
155
|
+
await room.say('alice', 'Meeting at 3pm');
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Built-in Tools
|
|
159
|
+
|
|
160
|
+
### File System
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
builtin.fs({ base: './workspace' })
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
- `Fs.Read`: Read file contents
|
|
167
|
+
- `Fs.Write`: Write/create files
|
|
168
|
+
- `Fs.Edit`: Replace text in files
|
|
169
|
+
|
|
170
|
+
### Bash Commands
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
builtin.bash({
|
|
174
|
+
allow: [/^git /, /^npm /],
|
|
175
|
+
block: [/rm -rf/, /sudo/],
|
|
176
|
+
approval: true,
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
- `Bash.Run`: Execute commands (foreground/background)
|
|
181
|
+
- `Bash.Logs`: Get output from background shell
|
|
182
|
+
- `Bash.Kill`: Terminate background shell
|
|
183
|
+
|
|
184
|
+
### Task Delegation
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
builtin.task({ subAgents: [FrontendAssistant, BackendAssistant] })
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- `Task.Run`: Delegate work to specialized sub-agents
|
|
191
|
+
|
|
192
|
+
## API Reference
|
|
193
|
+
|
|
194
|
+
### Agent
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Send message (non-blocking)
|
|
198
|
+
send(text: string): Promise<string>
|
|
199
|
+
|
|
200
|
+
// Subscribe to events
|
|
201
|
+
subscribe(opts?: { since?: number; kinds?: AgentEventKind[] }): AsyncIterable<AgentEvent>
|
|
202
|
+
|
|
203
|
+
// Convenience: send + subscribe
|
|
204
|
+
chat(text: string): AsyncIterable<AgentEvent>
|
|
205
|
+
|
|
206
|
+
// Blocking: wait for complete response
|
|
207
|
+
reply(text: string): Promise<string>
|
|
208
|
+
|
|
209
|
+
// One-off LLM query
|
|
210
|
+
askLLM(text: string, opts?): Promise<{ text: string; sessionId: string }>
|
|
211
|
+
|
|
212
|
+
// Control
|
|
213
|
+
interrupt(reason?: string): Promise<void>
|
|
214
|
+
decide(permId: string, decision: 'allow' | 'deny', note?: string): Promise<void>
|
|
215
|
+
|
|
216
|
+
// Snapshot & Fork
|
|
217
|
+
snapshot(label?: string): Promise<SnapshotId>
|
|
218
|
+
fork(sel?: SnapshotId | { at?: string }): Agent
|
|
219
|
+
|
|
220
|
+
// Introspection
|
|
221
|
+
status(): Promise<AgentStatus>
|
|
222
|
+
info(): Promise<AgentInfo>
|
|
223
|
+
history(opts?: { since?: number; limit?: number }): Promise<AgentEvent[]>
|
|
224
|
+
|
|
225
|
+
// Extension
|
|
226
|
+
use(hooks: Hooks): this
|
|
227
|
+
getHooks(): ReadonlyArray<RegisteredHook>
|
|
228
|
+
registerTools(tools: Tool[]): this
|
|
229
|
+
schedule(): AgentSchedulerHandle
|
|
230
|
+
on(event: 'permission_ask' | 'error' | 'messages_update', handler: Function): this
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Session ID Format
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
[org:{orgId}/][team:{teamId}/][user:{userId}/]agent:{template}/session:{rootId}[/fork:{forkId}]*
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Examples:
|
|
240
|
+
- `agent:assistant/session:abc123`
|
|
241
|
+
- `org:acme/team:eng/user:42/agent:pm/session:xyz789`
|
|
242
|
+
- `agent:dev/session:main/fork:branch1/fork:branch2`
|
|
243
|
+
|
|
244
|
+
Snapshots:
|
|
245
|
+
- `{sessionId}@sfp:{index}`
|
|
246
|
+
- `{sessionId}@label:{slug}`
|
|
247
|
+
|
|
248
|
+
## Examples
|
|
249
|
+
|
|
250
|
+
See `examples/` directory:
|
|
251
|
+
|
|
252
|
+
- **U1**: Next.js backend (send + subscribe via SSE)
|
|
253
|
+
- **U2**: Permission approval flow
|
|
254
|
+
- **U3**: Hook for path guard and result trimming
|
|
255
|
+
- **U4**: Scheduler with time and step triggers
|
|
256
|
+
- **U5**: Sub-agent task delegation
|
|
257
|
+
- **U6**: Room group chat
|
|
258
|
+
- **U7**: ChatDev team collaboration
|
|
259
|
+
|
|
260
|
+
## Architecture
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
Core
|
|
264
|
+
├─ Agent (推进引擎;事件管道;SFP 记录;Hook 执行)
|
|
265
|
+
├─ Events (cursor/since;增量持久)
|
|
266
|
+
├─ Scheduler (时间与 Steps 触发)
|
|
267
|
+
├─ Hooks (pre/post tool;pre/post model)
|
|
268
|
+
└─ API (send/subscribe/chat/reply/askLLM/interrupt/decide/snapshot/fork/resume)
|
|
269
|
+
|
|
270
|
+
Infra
|
|
271
|
+
├─ Providers (Anthropic 直通;其余适配)
|
|
272
|
+
├─ Sandbox (local/docker/k8s/remote/vfs)
|
|
273
|
+
├─ Store (json/sqlite/postgres)
|
|
274
|
+
├─ Tools (内置 FS/Bash/Task;MCP 适配)
|
|
275
|
+
└─ Pool (实例容器;限额;显式 resume)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Design Philosophy
|
|
279
|
+
|
|
280
|
+
### Event-Driven First
|
|
281
|
+
|
|
282
|
+
Default push **MINIMAL events only**. Other events require explicit opt-in via `kinds` parameter.
|
|
283
|
+
|
|
284
|
+
This forces event-driven patterns and prevents "chaotic operations" on subscription interfaces.
|
|
285
|
+
|
|
286
|
+
### Tool Safety
|
|
287
|
+
|
|
288
|
+
Denial doesn't throw exceptions. Instead:
|
|
289
|
+
- Returns `tool_result` with `ok: false`
|
|
290
|
+
- Content explains reason for denial
|
|
291
|
+
- Fully auditable trail
|
|
292
|
+
|
|
293
|
+
### Strong Recovery
|
|
294
|
+
|
|
295
|
+
7 breakpoint types (A-G):
|
|
296
|
+
- A: Before model request
|
|
297
|
+
- B: After model gives tool_use, before approval
|
|
298
|
+
- C: During approval wait
|
|
299
|
+
- D: In preToolUse hook
|
|
300
|
+
- E: During tool execution
|
|
301
|
+
- F: In postToolUse hook
|
|
302
|
+
- G: During streaming response
|
|
303
|
+
|
|
304
|
+
All recover by sealing incomplete operations (no system text injection) and returning to READY state.
|
|
305
|
+
|
|
306
|
+
## Contributing
|
|
307
|
+
|
|
308
|
+
Contributions welcome! Please see PRD and TDD in `Kode_SDK_v1.5.1.md` for detailed specifications.
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
MIT
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { AgentStatus, AgentInfo, AgentEvent, SubscribeOptions, SnapshotId } from './types';
|
|
2
|
+
import { Hooks } from './hooks';
|
|
3
|
+
import { Scheduler } from './scheduler';
|
|
4
|
+
import { Provider } from '../infra/provider';
|
|
5
|
+
import { Store } from '../infra/store';
|
|
6
|
+
import { Sandbox } from '../infra/sandbox';
|
|
7
|
+
import { Tool } from '../tools/fs';
|
|
8
|
+
import { AgentTemplate } from '../tools/task';
|
|
9
|
+
export interface AgentOptions {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
provider: Provider;
|
|
12
|
+
store: Store;
|
|
13
|
+
sandbox: Sandbox;
|
|
14
|
+
tools?: Tool[];
|
|
15
|
+
system?: string;
|
|
16
|
+
maxTokens?: number;
|
|
17
|
+
temperature?: number;
|
|
18
|
+
maxConcurrency?: number;
|
|
19
|
+
templateId?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class Agent {
|
|
22
|
+
private sessionId;
|
|
23
|
+
private provider;
|
|
24
|
+
private store;
|
|
25
|
+
private sandbox;
|
|
26
|
+
private tools;
|
|
27
|
+
private system?;
|
|
28
|
+
private maxTokens;
|
|
29
|
+
private temperature;
|
|
30
|
+
private maxConcurrency;
|
|
31
|
+
private templateId;
|
|
32
|
+
private messages;
|
|
33
|
+
private state;
|
|
34
|
+
private lastSfpIndex;
|
|
35
|
+
private stepCount;
|
|
36
|
+
private events;
|
|
37
|
+
private hooks;
|
|
38
|
+
private scheduler?;
|
|
39
|
+
private pendingPermissions;
|
|
40
|
+
private interrupted;
|
|
41
|
+
constructor(templateOrOpts: AgentTemplate | AgentOptions, overrides?: Partial<AgentOptions>);
|
|
42
|
+
send(text: string): Promise<string>;
|
|
43
|
+
subscribe(opts?: SubscribeOptions): AsyncIterable<AgentEvent>;
|
|
44
|
+
chat(text: string): AsyncIterable<AgentEvent>;
|
|
45
|
+
reply(text: string): Promise<string>;
|
|
46
|
+
askLLM(text: string, opts?: {
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
provider?: Provider;
|
|
49
|
+
useTools?: boolean;
|
|
50
|
+
system?: string;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
text: string;
|
|
53
|
+
sessionId: string;
|
|
54
|
+
}>;
|
|
55
|
+
interrupt(opts?: {
|
|
56
|
+
note?: string;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
decide(permId: string, decision: 'allow' | 'deny', note?: string): Promise<void>;
|
|
59
|
+
snapshot(label?: string): Promise<SnapshotId>;
|
|
60
|
+
fork(sel?: SnapshotId | {
|
|
61
|
+
at?: string;
|
|
62
|
+
}): Promise<Agent>;
|
|
63
|
+
static resume(sessionId: string, opts: AgentOptions & {
|
|
64
|
+
autoRun?: boolean;
|
|
65
|
+
strategy?: 'crash' | 'manual';
|
|
66
|
+
}): Promise<Agent>;
|
|
67
|
+
private findLastSfp;
|
|
68
|
+
private findSealedTools;
|
|
69
|
+
history(opts?: {
|
|
70
|
+
since?: number;
|
|
71
|
+
limit?: number;
|
|
72
|
+
}): Promise<AgentEvent[]>;
|
|
73
|
+
status(): Promise<AgentStatus>;
|
|
74
|
+
info(): Promise<AgentInfo>;
|
|
75
|
+
use(hooks: Hooks): this;
|
|
76
|
+
getHooks(): ReadonlyArray<import('./hooks').RegisteredHook>;
|
|
77
|
+
registerTools(tools: Tool[]): this;
|
|
78
|
+
schedule(): Scheduler;
|
|
79
|
+
on(event: 'permission_ask' | 'error' | 'messages_update', handler: (...args: any[]) => void): this;
|
|
80
|
+
private step;
|
|
81
|
+
private executeTools;
|
|
82
|
+
private requestPermission;
|
|
83
|
+
private getToolSchemas;
|
|
84
|
+
private persistMessages;
|
|
85
|
+
}
|