@factory/droid-sdk 0.6.0 → 0.7.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.
Files changed (80) hide show
  1. package/README.md +35 -482
  2. package/dist/chunk-5UXINOXG.mjs +24483 -0
  3. package/dist/chunk-5UXINOXG.mjs.map +1 -0
  4. package/dist/chunk-C3KHERVH.js +24483 -0
  5. package/dist/chunk-C3KHERVH.js.map +1 -0
  6. package/dist/{index-CwXX5IUH.d.cts → index-D_SzTnFR.d.mts} +83826 -81547
  7. package/dist/{index-CwXX5IUH.d.ts → index-D_SzTnFR.d.ts} +83826 -81547
  8. package/dist/index.d.mts +4 -0
  9. package/dist/index.d.ts +4 -93103
  10. package/dist/index.js +821 -11745
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +822 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/dist/node.d.mts +736 -0
  15. package/dist/node.d.ts +736 -0
  16. package/dist/node.js +4601 -0
  17. package/dist/node.js.map +1 -0
  18. package/dist/node.mjs +4601 -0
  19. package/dist/node.mjs.map +1 -0
  20. package/docs/typescript-sdk-reference.md +1260 -0
  21. package/examples/browser/daemon-lifecycle.ts +95 -0
  22. package/examples/browser/daemon-multi-session.ts +66 -0
  23. package/examples/browser/daemon-session.ts +73 -0
  24. package/examples/browser/index.html +68 -0
  25. package/examples/browser/list-sessions.ts +52 -0
  26. package/examples/{abort-session-stream.ts → node/abort-session-stream.ts} +8 -10
  27. package/examples/node/ask-user-elicitation.ts +35 -0
  28. package/examples/node/compact-session.ts +48 -0
  29. package/examples/node/enter-spec-mode.ts +66 -0
  30. package/examples/node/fork-session.ts +45 -0
  31. package/examples/node/hook-execution.ts +94 -0
  32. package/examples/node/image-attachment.ts +35 -0
  33. package/examples/node/init-metadata.ts +24 -0
  34. package/examples/{interrupt-session.ts → node/interrupt-session.ts} +7 -6
  35. package/examples/node/list-sessions.ts +20 -0
  36. package/examples/node/multi-turn-session.ts +33 -0
  37. package/examples/node/observability.ts +88 -0
  38. package/examples/node/permission-handler.ts +55 -0
  39. package/examples/node/rename-session.ts +29 -0
  40. package/examples/node/result-metadata.ts +34 -0
  41. package/examples/node/rewind-session.ts +69 -0
  42. package/examples/node/run.ts +25 -0
  43. package/examples/{sdk-mcp-tool.ts → node/sdk-mcp-tool.ts} +22 -12
  44. package/examples/node/session-settings.ts +35 -0
  45. package/examples/node/session-stream.ts +44 -0
  46. package/examples/node/spec-mode-new-session.ts +92 -0
  47. package/examples/node/spec-mode-same-session.ts +85 -0
  48. package/examples/node/structured-output.ts +73 -0
  49. package/examples/node/tool-controls.ts +26 -0
  50. package/examples/package.json +3 -0
  51. package/package.json +20 -42
  52. package/dist/index.cjs +0 -12135
  53. package/dist/index.cjs.map +0 -1
  54. package/dist/index.d.cts +0 -93103
  55. package/dist/protocol/index.cjs +0 -6466
  56. package/dist/protocol/index.cjs.map +0 -1
  57. package/dist/protocol/index.d.cts +0 -2
  58. package/dist/protocol/index.d.ts +0 -2
  59. package/dist/protocol/index.js +0 -6078
  60. package/dist/protocol/index.js.map +0 -1
  61. package/docs/daemon-usage-guide.md +0 -544
  62. package/docs/sdk-usage-guide.md +0 -748
  63. package/examples/daemon-multi-session.ts +0 -67
  64. package/examples/droid-dev-structured-output.ts +0 -44
  65. package/examples/fork-session.ts +0 -68
  66. package/examples/hook-execution.ts +0 -68
  67. package/examples/init-metadata.ts +0 -24
  68. package/examples/list-sessions.ts +0 -19
  69. package/examples/multi-turn-session.ts +0 -40
  70. package/examples/permission-handler.ts +0 -41
  71. package/examples/readme-structured-output.ts +0 -26
  72. package/examples/result-metadata.ts +0 -63
  73. package/examples/run.ts +0 -39
  74. package/examples/session-stream.ts +0 -57
  75. package/examples/spec-mode-new-session.ts +0 -61
  76. package/examples/spec-mode-same-session.ts +0 -72
  77. package/examples/structured-output.ts +0 -60
  78. package/examples/test-compact.ts +0 -65
  79. package/examples/test-rewind.ts +0 -87
  80. package/examples/tool-controls.ts +0 -37
@@ -1,748 +0,0 @@
1
- # SDK Usage Guide
2
-
3
- ## Getting Started
4
-
5
- ```bash
6
- npm install @factory/droid-sdk
7
- ```
8
-
9
- Requires Node.js 18+ and the `droid` CLI on your PATH. This guide covers **exec mode** (`run()`, `createSession()`), which spawns a subprocess per session. For WebSocket-based daemon mode with concurrent sessions, see the [Daemon Usage Guide](./daemon-usage-guide.md).
10
-
11
- ```ts
12
- import { run } from '@factory/droid-sdk';
13
-
14
- const result = await run('What files are in this directory?', {
15
- apiKey: process.env.FACTORY_API_KEY!,
16
- cwd: process.cwd(),
17
- });
18
- console.log(result.text);
19
- ```
20
-
21
- ---
22
-
23
- ## One-shot Run
24
-
25
- Send a prompt, get a result, done. The session is created and closed automatically.
26
-
27
- ```ts
28
- import { run } from '@factory/droid-sdk';
29
-
30
- const result = await run('What is 2 + 2?', {
31
- apiKey: process.env.FACTORY_API_KEY!,
32
- cwd: process.cwd(),
33
- });
34
- console.log(result.text);
35
- ```
36
-
37
- ## Structured Output
38
-
39
- Force the response to match a JSON schema. The validated object is available on `result.structuredOutput`.
40
-
41
- ```ts
42
- import { OutputFormatType, run } from '@factory/droid-sdk';
43
-
44
- const result = await run('Pick a number between 1 and 42.', {
45
- apiKey: process.env.FACTORY_API_KEY!,
46
- cwd: process.cwd(),
47
- outputFormat: {
48
- type: OutputFormatType.JsonSchema,
49
- schema: {
50
- type: 'object',
51
- properties: { number: { type: 'number' } },
52
- required: ['number'],
53
- },
54
- },
55
- });
56
-
57
- console.log((result.structuredOutput as { number: number }).number);
58
- ```
59
-
60
- ## Multi-turn Session
61
-
62
- Create a session once, then call `stream()` multiple times. Context is preserved across turns.
63
-
64
- ```ts
65
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
66
-
67
- const session = await createSession({
68
- apiKey: process.env.FACTORY_API_KEY!,
69
- cwd: process.cwd(),
70
- });
71
-
72
- for await (const msg of session.stream('Remember the word "mango".')) {
73
- // consume first turn
74
- }
75
-
76
- for await (const msg of session.stream('What word did I say?')) {
77
- if (msg.type === DroidMessageType.Assistant) console.log(msg.text);
78
- }
79
-
80
- await session.close();
81
- ```
82
-
83
- ## Resume Session
84
-
85
- Reconnect to a previously created session by its ID.
86
-
87
- ```ts
88
- import { resumeSession, DroidMessageType } from '@factory/droid-sdk';
89
-
90
- const session = await resumeSession('existing-session-id', {
91
- apiKey: process.env.FACTORY_API_KEY!,
92
- });
93
-
94
- for await (const msg of session.stream('Continue where we left off.')) {
95
- if (msg.type === DroidMessageType.Assistant) console.log(msg.text);
96
- }
97
-
98
- await session.close();
99
- ```
100
-
101
- ## Full Message Streaming
102
-
103
- `stream()` yields complete messages: assistant text, tool calls, tool results, hooks, errors, and the final result.
104
-
105
- ```ts
106
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
107
-
108
- const session = await createSession({
109
- apiKey: process.env.FACTORY_API_KEY!,
110
- cwd: process.cwd(),
111
- });
112
-
113
- for await (const msg of session.stream(
114
- 'List files in the current directory.'
115
- )) {
116
- switch (msg.type) {
117
- case DroidMessageType.Assistant:
118
- console.log(msg.text);
119
- break;
120
- case DroidMessageType.ToolCall:
121
- console.log(`[Tool] ${msg.toolUse.name}`);
122
- break;
123
- case DroidMessageType.ToolResult:
124
- console.log(`[Result] ${msg.isError ? 'Error' : 'OK'}`);
125
- break;
126
- case DroidMessageType.Result:
127
- console.log(`Done in ${msg.durationMs}ms`);
128
- break;
129
- }
130
- }
131
-
132
- await session.close();
133
- ```
134
-
135
- ## Partial Message Streaming
136
-
137
- Enable `includePartialMessages` to get token-by-token deltas, thinking blocks, and tool progress as they arrive.
138
-
139
- ```ts
140
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
141
-
142
- const session = await createSession({
143
- apiKey: process.env.FACTORY_API_KEY!,
144
- cwd: process.cwd(),
145
- });
146
-
147
- for await (const msg of session.stream('Explain recursion.', {
148
- includePartialMessages: true,
149
- })) {
150
- if (msg.type === DroidMessageType.AssistantTextDelta) {
151
- process.stdout.write(msg.text);
152
- }
153
- }
154
-
155
- await session.close();
156
- ```
157
-
158
- ## Interrupt or Cancel Running Work
159
-
160
- Use `session.interrupt()` to stop the current turn server-side, or pass an `AbortSignal` to cancel from the client.
161
-
162
- ```ts
163
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
164
-
165
- // Interrupt after receiving some output
166
- const session = await createSession({
167
- apiKey: process.env.FACTORY_API_KEY!,
168
- cwd: process.cwd(),
169
- });
170
- for await (const msg of session.stream('Write a long essay.')) {
171
- if (msg.type === DroidMessageType.Assistant) {
172
- await session.interrupt();
173
- }
174
- }
175
- await session.close();
176
-
177
- // Or cancel with AbortSignal
178
- const session2 = await createSession({
179
- apiKey: process.env.FACTORY_API_KEY!,
180
- cwd: process.cwd(),
181
- });
182
- const controller = new AbortController();
183
- setTimeout(() => controller.abort(), 2000);
184
-
185
- try {
186
- for await (const msg of session2.stream('Write a long essay.', {
187
- abortSignal: controller.signal,
188
- })) {
189
- }
190
- } catch {
191
- console.log('Aborted');
192
- }
193
- await session2.close();
194
- ```
195
-
196
- ## SDK-backed MCP Tools
197
-
198
- Define custom tools that Droid can call during a session. Tools are served via a local MCP server that the SDK manages automatically.
199
-
200
- ```ts
201
- import {
202
- createSession,
203
- createSdkMcpServer,
204
- DroidMessageType,
205
- tool,
206
- ToolConfirmationOutcome,
207
- } from '@factory/droid-sdk';
208
- import { z } from 'zod';
209
-
210
- const server = createSdkMcpServer({
211
- name: 'my-tools',
212
- tools: [
213
- tool(
214
- 'lookup',
215
- 'Look up a user by name',
216
- { name: z.string() },
217
- ({ name }) => {
218
- return `${name} is user #42.`;
219
- }
220
- ),
221
- ],
222
- });
223
-
224
- const session = await createSession({
225
- apiKey: process.env.FACTORY_API_KEY!,
226
- cwd: process.cwd(),
227
- mcpServers: [server],
228
- permissionHandler: () => ToolConfirmationOutcome.ProceedOnce,
229
- });
230
-
231
- for await (const msg of session.stream('Look up Alice.')) {
232
- if (msg.type === DroidMessageType.Assistant) console.log(msg.text);
233
- }
234
-
235
- await session.close();
236
- ```
237
-
238
- ## Autonomy Levels
239
-
240
- Control what Droid can do without asking for permission. Set at session creation or change mid-session.
241
-
242
- ```ts
243
- import { createSession, AutonomyLevel } from '@factory/droid-sdk';
244
-
245
- const session = await createSession({
246
- apiKey: process.env.FACTORY_API_KEY!,
247
- cwd: process.cwd(),
248
- autonomyLevel: AutonomyLevel.High, // Off | Low | Medium | High
249
- });
250
-
251
- // Change mid-session
252
- await session.updateSettings({ autonomyLevel: AutonomyLevel.Low });
253
- await session.close();
254
- ```
255
-
256
- ## Enabled/Disabled Tools
257
-
258
- Restrict which tools Droid can use. Accepts tool IDs like `'Read'`, `'Execute'`, `'Grep'`.
259
-
260
- ```ts
261
- import { createSession } from '@factory/droid-sdk';
262
-
263
- const session = await createSession({
264
- apiKey: process.env.FACTORY_API_KEY!,
265
- cwd: process.cwd(),
266
- enabledToolIds: ['Read', 'Grep'],
267
- disabledToolIds: ['Execute'],
268
- });
269
-
270
- // Change mid-session
271
- await session.updateSettings({ disabledToolIds: ['Read', 'Execute'] });
272
- await session.close();
273
- ```
274
-
275
- ## Permission Handler
276
-
277
- Programmatically approve or reject tool calls instead of prompting a human. Receives full tool details including file paths and commands.
278
-
279
- ```ts
280
- import {
281
- run,
282
- ToolConfirmationOutcome,
283
- ToolConfirmationType,
284
- } from '@factory/droid-sdk';
285
-
286
- await run('Create hello.txt with "Hello, World!"', {
287
- apiKey: process.env.FACTORY_API_KEY!,
288
- cwd: process.cwd(),
289
- permissionHandler(params) {
290
- const safe = params.toolUses.every(
291
- (item) => item.details.type === ToolConfirmationType.Create
292
- );
293
- return safe
294
- ? ToolConfirmationOutcome.ProceedOnce
295
- : ToolConfirmationOutcome.Cancel;
296
- },
297
- });
298
- ```
299
-
300
- ## Spec Mode
301
-
302
- Start Droid in read-only planning mode. It will research and produce a plan, then request to exit spec mode for implementation.
303
-
304
- ```ts
305
- import {
306
- createSession,
307
- DroidInteractionMode,
308
- ToolConfirmationOutcome,
309
- ToolConfirmationType,
310
- } from '@factory/droid-sdk';
311
-
312
- const session = await createSession({
313
- apiKey: process.env.FACTORY_API_KEY!,
314
- cwd: process.cwd(),
315
- interactionMode: DroidInteractionMode.Spec,
316
- permissionHandler(params) {
317
- const exitsSpec = params.toolUses.some(
318
- (t) => t.details.type === ToolConfirmationType.ExitSpecMode
319
- );
320
- return exitsSpec
321
- ? ToolConfirmationOutcome.ProceedOnce
322
- : ToolConfirmationOutcome.Cancel;
323
- },
324
- });
325
-
326
- for await (const msg of session.stream('Plan a refactor of src/utils.ts')) {
327
- }
328
- await session.close();
329
- ```
330
-
331
- ## Multimodal Input
332
-
333
- Send images or documents alongside your prompt. Images must be base64-encoded.
334
-
335
- ```ts
336
- import { readFileSync } from 'node:fs';
337
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
338
-
339
- const session = await createSession({
340
- apiKey: process.env.FACTORY_API_KEY!,
341
- cwd: process.cwd(),
342
- });
343
-
344
- for await (const msg of session.stream('Describe this image.', {
345
- images: [
346
- {
347
- type: 'base64',
348
- data: readFileSync('screenshot.png').toString('base64'),
349
- mediaType: 'image/png',
350
- },
351
- ],
352
- })) {
353
- if (msg.type === DroidMessageType.Assistant) console.log(msg.text);
354
- }
355
-
356
- await session.close();
357
- ```
358
-
359
- ## Fork Session
360
-
361
- Create a copy of the current session with all context preserved. Useful for branching a conversation.
362
-
363
- ```ts
364
- import {
365
- createSession,
366
- DroidMessageType,
367
- resumeSession,
368
- } from '@factory/droid-sdk';
369
-
370
- const session = await createSession({
371
- apiKey: process.env.FACTORY_API_KEY!,
372
- cwd: process.cwd(),
373
- });
374
- for await (const msg of session.stream('Remember: the password is "banana".')) {
375
- }
376
-
377
- const { newSessionId } = await session.forkSession();
378
- const fork = await resumeSession(newSessionId, {
379
- apiKey: process.env.FACTORY_API_KEY!,
380
- });
381
-
382
- for await (const msg of fork.stream('What is the password?')) {
383
- if (msg.type === DroidMessageType.Assistant) console.log(msg.text);
384
- }
385
-
386
- await fork.close();
387
- await session.close();
388
- ```
389
-
390
- ## Compact Session
391
-
392
- Summarize and remove old messages to free up context window space.
393
-
394
- ```ts
395
- import { createSession } from '@factory/droid-sdk';
396
-
397
- const session = await createSession({
398
- apiKey: process.env.FACTORY_API_KEY!,
399
- cwd: process.cwd(),
400
- });
401
-
402
- // ... after many turns ...
403
- const result = await session.compactSession();
404
- console.log(
405
- `New session: ${result.newSessionId}, removed: ${result.removedCount} messages`
406
- );
407
-
408
- await session.close();
409
- ```
410
-
411
- ## Rewind
412
-
413
- Undo to a specific message and optionally restore files to their state at that point.
414
-
415
- ```ts
416
- import {
417
- DroidClient,
418
- ProcessTransport,
419
- AutonomyLevel,
420
- } from '@factory/droid-sdk';
421
-
422
- const transport = new ProcessTransport({
423
- cwd: process.cwd(),
424
- env: { FACTORY_API_KEY: process.env.FACTORY_API_KEY! },
425
- });
426
- await transport.connect();
427
- const client = new DroidClient({ transport });
428
-
429
- await client.initializeSession({
430
- machineId: 'default',
431
- cwd: process.cwd(),
432
- autonomyLevel: AutonomyLevel.High,
433
- });
434
-
435
- const messageId = 'target-message-id';
436
- const info = await client.getRewindInfo({ messageId });
437
- console.log(`Files available to restore: ${info.availableFiles.length}`);
438
-
439
- const result = await client.executeRewind({
440
- messageId,
441
- filesToRestore: info.availableFiles,
442
- filesToDelete: [],
443
- forkTitle: 'Rewind checkpoint',
444
- });
445
- console.log(`Rewound into session: ${result.newSessionId}`);
446
-
447
- await client.close();
448
- ```
449
-
450
- ## List Sessions
451
-
452
- Discover saved sessions on disk. Filters to the current project by default.
453
-
454
- ```ts
455
- import { listSessions } from '@factory/droid-sdk';
456
-
457
- const sessions = await listSessions({ numSessions: 10 });
458
-
459
- for (const s of sessions) {
460
- console.log(`${s.id}: ${s.sessionTitle ?? '(untitled)'}`);
461
- }
462
- ```
463
-
464
- ## Model and Reasoning Effort
465
-
466
- Choose which model to use and how much reasoning effort to apply. Configurable at creation or mid-session.
467
-
468
- ```ts
469
- import { createSession, ReasoningEffort } from '@factory/droid-sdk';
470
-
471
- const session = await createSession({
472
- apiKey: process.env.FACTORY_API_KEY!,
473
- cwd: process.cwd(),
474
- modelId: 'claude-sonnet-4-20250514',
475
- reasoningEffort: ReasoningEffort.High,
476
- });
477
-
478
- // Change mid-session
479
- await session.updateSettings({
480
- reasoningEffort: ReasoningEffort.Low,
481
- });
482
-
483
- await session.close();
484
- ```
485
-
486
- ## Hook Execution Monitoring
487
-
488
- Observe file hooks (pre/post tool execution hooks) as they run during a session.
489
-
490
- ```ts
491
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
492
-
493
- const session = await createSession({
494
- apiKey: process.env.FACTORY_API_KEY!,
495
- cwd: process.cwd(),
496
- });
497
-
498
- for await (const msg of session.stream('Create a new file.')) {
499
- if (msg.type === DroidMessageType.Hook) {
500
- if (msg.status === 'started') {
501
- console.log(`[Hook] ${msg.command}`);
502
- } else {
503
- console.log(`[Hook ${msg.status}] exit=${msg.exitCode}`);
504
- }
505
- }
506
- }
507
-
508
- await session.close();
509
- ```
510
-
511
- ## Ask-User Handler
512
-
513
- Programmatically answer questions that Droid asks the user during execution.
514
-
515
- ```ts
516
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
517
-
518
- const session = await createSession({
519
- apiKey: process.env.FACTORY_API_KEY!,
520
- cwd: process.cwd(),
521
- askUserHandler(params) {
522
- return {
523
- cancelled: false,
524
- answers: params.questions.map((q) => ({
525
- index: q.index,
526
- question: q.question,
527
- answer: q.options[0] ?? 'yes',
528
- })),
529
- };
530
- },
531
- });
532
-
533
- for await (const msg of session.stream('Help me set up this project.')) {
534
- if (msg.type === DroidMessageType.Assistant) console.log(msg.text);
535
- }
536
-
537
- await session.close();
538
- ```
539
-
540
- ## MCP Server Management
541
-
542
- Add, remove, toggle, and list MCP servers at runtime within an active session.
543
-
544
- ```ts
545
- import { createSession, McpServerType } from '@factory/droid-sdk';
546
-
547
- const session = await createSession({
548
- apiKey: process.env.FACTORY_API_KEY!,
549
- cwd: process.cwd(),
550
- });
551
-
552
- await session.addMcpServer({
553
- name: 'my-server',
554
- type: McpServerType.Http,
555
- url: 'https://mcp.example.com/mcp',
556
- });
557
-
558
- const { servers, summary } = await session.listMcpServers();
559
- console.log(`MCP status: ${summary.status}, servers: ${servers.length}`);
560
-
561
- await session.close();
562
- ```
563
-
564
- ## Context Stats
565
-
566
- Query current context window usage to understand how much capacity remains.
567
-
568
- ```ts
569
- import { createSession } from '@factory/droid-sdk';
570
-
571
- const session = await createSession({
572
- apiKey: process.env.FACTORY_API_KEY!,
573
- cwd: process.cwd(),
574
- });
575
- for await (const msg of session.stream('Hello')) {
576
- }
577
-
578
- const stats = await session.getContextStats();
579
- console.log(
580
- `Used: ${stats.used}, Remaining: ${stats.remaining}, Limit: ${stats.limit}`
581
- );
582
-
583
- await session.close();
584
- ```
585
-
586
- ## Token Usage Tracking
587
-
588
- Monitor token consumption in real-time via stream events, or read the final totals from the result.
589
-
590
- ```ts
591
- import { createSession, DroidMessageType } from '@factory/droid-sdk';
592
-
593
- const session = await createSession({
594
- apiKey: process.env.FACTORY_API_KEY!,
595
- cwd: process.cwd(),
596
- });
597
-
598
- for await (const msg of session.stream('Summarize this project.', {
599
- includePartialMessages: true,
600
- })) {
601
- if (msg.type === DroidMessageType.TokenUsageUpdate) {
602
- console.log(`Tokens — in: ${msg.inputTokens}, out: ${msg.outputTokens}`);
603
- }
604
- if (msg.type === DroidMessageType.Result && msg.tokenUsage) {
605
- console.log(
606
- `Final — in: ${msg.tokenUsage.inputTokens}, out: ${msg.tokenUsage.outputTokens}`
607
- );
608
- }
609
- }
610
-
611
- await session.close();
612
- ```
613
-
614
- ## List Skills
615
-
616
- List all available skills in the current session.
617
-
618
- ```ts
619
- import { createSession } from '@factory/droid-sdk';
620
-
621
- const session = await createSession({
622
- apiKey: process.env.FACTORY_API_KEY!,
623
- cwd: process.cwd(),
624
- });
625
- const { skills } = await session.listSkills();
626
-
627
- for (const skill of skills) {
628
- console.log(`${skill.name} (${skill.location}): ${skill.description ?? ''}`);
629
- }
630
-
631
- await session.close();
632
- ```
633
-
634
- ## Raw Notification Subscription
635
-
636
- Subscribe to raw protocol notifications for custom event handling beyond the stream API.
637
-
638
- ```ts
639
- import { createSession, SessionNotificationType } from '@factory/droid-sdk';
640
-
641
- const session = await createSession({
642
- apiKey: process.env.FACTORY_API_KEY!,
643
- cwd: process.cwd(),
644
- });
645
-
646
- const unsubscribe = session.onNotification(
647
- (notification) => {
648
- console.log('Notification:', notification);
649
- },
650
- { type: SessionNotificationType.ERROR }
651
- );
652
-
653
- // ... use the session ...
654
-
655
- unsubscribe();
656
- await session.close();
657
- ```
658
-
659
- ## Error Handling
660
-
661
- The SDK throws typed errors with structured context. Catch specific error classes to handle different failure modes.
662
-
663
- ```ts
664
- import {
665
- resumeSession,
666
- ConnectionError,
667
- TimeoutError,
668
- SessionNotFoundError,
669
- ProtocolError,
670
- } from '@factory/droid-sdk';
671
-
672
- try {
673
- const session = await resumeSession('nonexistent-id', {
674
- apiKey: process.env.FACTORY_API_KEY!,
675
- });
676
- } catch (error) {
677
- if (error instanceof SessionNotFoundError) {
678
- console.log(`Session not found: ${error.sessionId}`);
679
- } else if (error instanceof TimeoutError) {
680
- console.log('Request timed out');
681
- } else if (error instanceof ConnectionError) {
682
- console.log(`Connection failed: ${error.message}`);
683
- } else if (error instanceof ProtocolError) {
684
- console.log(`Protocol error ${error.code}: ${error.message}`);
685
- }
686
- }
687
- ```
688
-
689
- ## Low-level APIs
690
-
691
- Most users should use `run()` and `DroidSession`. For direct RPC access, the SDK also exports `ProcessTransport`, `ProtocolEngine`, and `DroidClient`.
692
-
693
- `DroidClient` exposes additional methods not on `DroidSession`: `killWorkerSession()`, `cancelMcpAuth()`, `clearMcpAuth()`, `submitMcpAuthCode()`, `listMcpRegistry()`, `toggleMcpTool()`, and `submitBugReport()`.
694
-
695
- The package also exports its full Zod schema surface from `src/schemas/index.ts` for runtime validation of protocol payloads.
696
-
697
- ---
698
-
699
- ## Configuration Reference
700
-
701
- ### `CreateSessionOptions`
702
-
703
- | Field | Type | Description |
704
- | :------------------------ | :----------------------- | :---------------------------------------------------------- |
705
- | `apiKey` | `string` | **Required.** Factory API key for authentication |
706
- | `cwd` | `string` | Working directory for the session |
707
- | `machineId` | `string` | Machine identifier for initialization |
708
- | `modelId` | `string` | LLM model identifier |
709
- | `autonomyLevel` | `AutonomyLevel` | `Off` \| `Low` \| `Medium` \| `High` |
710
- | `interactionMode` | `DroidInteractionMode` | `Auto` \| `Spec` \| `AGI` |
711
- | `reasoningEffort` | `ReasoningEffort` | `None` \| `Low` \| `Medium` \| `High` \| `Max` (and others) |
712
- | `specModeModelId` | `string` | Override model for spec mode |
713
- | `specModeReasoningEffort` | `ReasoningEffort` | Override reasoning effort for spec mode |
714
- | `mcpServers` | `DroidMcpServerConfig[]` | Initial MCP server configurations |
715
- | `enabledToolIds` | `string[]` | Tool allowlist |
716
- | `disabledToolIds` | `string[]` | Tool denylist |
717
- | `tags` | `SessionTag[]` | Session tags for categorization |
718
- | `sessionSource` | `SessionSource` | Attribution metadata (e.g., integration origin) |
719
- | `permissionHandler` | `PermissionHandler` | Tool confirmation callback |
720
- | `askUserHandler` | `AskUserHandler` | Structured user-input callback |
721
- | `execPath` | `string` | Path to `droid` executable (default: `"droid"`) |
722
- | `execArgs` | `string[]` | Extra CLI arguments for the subprocess |
723
- | `env` | `Record<string, string>` | Environment variables for the subprocess |
724
- | `transport` | `DroidClientTransport` | Custom transport (skips subprocess spawn) |
725
- | `abortSignal` | `AbortSignal` | Cancellation signal |
726
-
727
- ### `MessageOptions`
728
-
729
- Accepted by `session.stream()` and `run()`:
730
-
731
- | Field | Type | Description |
732
- | :----------------------- | :-------------------- | :------------------------------------------- |
733
- | `images` | `Base64ImageSource[]` | Base64-encoded image attachments |
734
- | `files` | `DocumentSource[]` | Document/file attachments |
735
- | `outputFormat` | `OutputFormat` | Structured output JSON schema request |
736
- | `includePartialMessages` | `boolean` | Yield token-level deltas and progress events |
737
- | `abortSignal` | `AbortSignal` | Cancellation signal for this turn |
738
-
739
- ### Error Types
740
-
741
- | Error | Description |
742
- | :--------------------- | :---------------------------------------- |
743
- | `ConnectionError` | Failed to connect to the droid subprocess |
744
- | `ProtocolError` | JSON-RPC or protocol-level failure |
745
- | `SessionError` | Base session error |
746
- | `SessionNotFoundError` | Requested session does not exist |
747
- | `TimeoutError` | RPC timed out |
748
- | `ProcessExitError` | Subprocess exited unexpectedly |