@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 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
+ }