@factory/droid-sdk 0.1.4 → 0.2.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 (36) hide show
  1. package/README.md +239 -47
  2. package/dist/index.cjs +1869 -1296
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +6734 -4444
  5. package/dist/index.d.ts +6734 -4444
  6. package/dist/index.js +1815 -1298
  7. package/dist/index.js.map +1 -1
  8. package/docs/examples/init-metadata.md +79 -0
  9. package/docs/examples/list-sessions.md +64 -0
  10. package/docs/examples/multi-turn-session.md +77 -0
  11. package/docs/examples/one-shot-query.md +73 -0
  12. package/docs/examples/permission-handler.md +102 -0
  13. package/docs/examples/run.md +52 -0
  14. package/docs/examples/spec-mode.md +101 -0
  15. package/docs/examples/tool-controls.md +90 -0
  16. package/docs/typescript-sdk-reference.md +372 -0
  17. package/examples/.gitkeep +1 -0
  18. package/examples/abort-session-stream.ts +67 -0
  19. package/examples/droid-dev-structured-output.ts +44 -0
  20. package/examples/fork-session.ts +42 -0
  21. package/examples/init-metadata.ts +76 -0
  22. package/examples/interrupt-session.ts +67 -0
  23. package/examples/list-sessions.ts +65 -0
  24. package/examples/multi-turn-session.ts +67 -0
  25. package/examples/permission-handler.ts +98 -0
  26. package/examples/result-metadata.ts +62 -0
  27. package/examples/run.ts +38 -0
  28. package/examples/sdk-mcp-tool.ts +38 -0
  29. package/examples/simple-query.ts +53 -0
  30. package/examples/spec-mode-new-session.ts +121 -0
  31. package/examples/spec-mode-same-session.ts +133 -0
  32. package/examples/structured-output.ts +53 -0
  33. package/examples/test-compact.ts +60 -0
  34. package/examples/test-rewind.ts +155 -0
  35. package/examples/tool-controls.ts +69 -0
  36. package/package.json +26 -4
@@ -0,0 +1,372 @@
1
+ # Agent SDK reference - TypeScript
2
+
3
+ > Public API reference for `@factory/droid-sdk`, the TypeScript SDK for the Factory Droid CLI.
4
+
5
+ ## Documentation Index
6
+
7
+ Use these pages together:
8
+
9
+ - API reference: `docs/typescript-sdk-reference.md`
10
+ - Example walkthroughs: `docs/examples/`
11
+ - Source exports: [`src/index.ts`](../src/index.ts)
12
+ - Runnable repo examples: [`examples/`](../examples)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @factory/droid-sdk
18
+ ```
19
+
20
+ ## Requirements
21
+
22
+ - Node.js `18+`
23
+ - The `droid` CLI installed and available on your `PATH`
24
+
25
+ ## What this SDK provides
26
+
27
+ The SDK wraps `droid exec` as a subprocess and exposes two main patterns:
28
+
29
+ - `query()` for one-shot prompt/response flows
30
+ - `createSession()` / `resumeSession()` for multi-turn sessions
31
+
32
+ It also includes:
33
+
34
+ - streaming message events
35
+ - permission and ask-user handlers
36
+ - MCP server management
37
+ - spec mode controls
38
+ - tool allow/deny controls
39
+ - session utilities such as rename, fork, compact, and rewind
40
+ - local session discovery with `listSessions()`
41
+ - low-level access via `ProcessTransport`, `ProtocolEngine`, and `DroidClient`
42
+
43
+ ## Example walkthroughs
44
+
45
+ For copy-pasteable walkthroughs and complete scripts, see:
46
+
47
+ - [One-shot query](./examples/one-shot-query.md)
48
+ - [Multi-turn session](./examples/multi-turn-session.md)
49
+ - [Permission handler](./examples/permission-handler.md)
50
+ - [Initialization metadata](./examples/init-metadata.md)
51
+ - [Spec mode approval flow](./examples/spec-mode.md)
52
+ - [Tool controls](./examples/tool-controls.md)
53
+ - [List saved sessions](./examples/list-sessions.md)
54
+
55
+ ## Functions
56
+
57
+ ### `query()`
58
+
59
+ Starts a one-shot prompt, streams `DroidMessage` events, and cleans up automatically when the stream ends.
60
+
61
+ ```ts
62
+ function query(options: QueryOptions): DroidQuery;
63
+ ```
64
+
65
+ #### `QueryOptions`
66
+
67
+ `QueryOptions` extends `CreateSessionOptions` and adds:
68
+
69
+ | Field | Type | Description |
70
+ | :------- | :------- | :--------------------------------------- |
71
+ | `prompt` | `string` | Prompt to send as the first user message |
72
+
73
+ #### `DroidQuery`
74
+
75
+ `DroidQuery` is an `AsyncGenerator<DroidMessage>` with extra controls:
76
+
77
+ | Member | Type | Description |
78
+ | :------------ | :--------------------------------- | :---------------------------------------- |
79
+ | `interrupt()` | `() => Promise<void>` | Gracefully interrupts the current turn |
80
+ | `abort()` | `() => void` | Forcefully closes the subprocess |
81
+ | `sessionId` | `string \| null` | Session ID after initialization completes |
82
+ | `initResult` | `InitializeSessionResult \| null` | Raw initialization result |
83
+ | `initialized` | `Promise<InitializeSessionResult>` | Resolves when initialization finishes |
84
+
85
+ #### Notes
86
+
87
+ - `query()` accepts `abortSignal`
88
+ - `query()` sends text only; image/file message attachments are supported on `DroidSession`, not `query()`
89
+
90
+ ### `createSession()`
91
+
92
+ Creates a persistent `DroidSession` for multi-turn conversations.
93
+
94
+ ```ts
95
+ function createSession(options?: CreateSessionOptions): Promise<DroidSession>;
96
+ ```
97
+
98
+ ### `resumeSession()`
99
+
100
+ Reconnects to an existing session by ID.
101
+
102
+ ```ts
103
+ function resumeSession(
104
+ sessionId: string,
105
+ options?: ResumeSessionOptions
106
+ ): Promise<DroidSession>;
107
+ ```
108
+
109
+ ### `listSessions()`
110
+
111
+ Discovers saved sessions directly from `~/.factory/sessions/` without spawning `droid`.
112
+
113
+ ```ts
114
+ function listSessions(
115
+ options?: ListSessionsOptions
116
+ ): Promise<SessionMetadata[]>;
117
+ ```
118
+
119
+ #### `ListSessionsOptions`
120
+
121
+ | Field | Type | Description |
122
+ | :---------------- | :-------- | :---------------------------------------------------------------- |
123
+ | `cwd` | `string` | Scope results to a project directory. Defaults to `process.cwd()` |
124
+ | `fetchOutsideCWD` | `boolean` | Return sessions across all projects |
125
+ | `numSessions` | `number` | Maximum number of sessions to return |
126
+ | `sessionsDir` | `string` | Override the session storage root |
127
+
128
+ #### Returned `SessionMetadata`
129
+
130
+ Common fields include:
131
+
132
+ - `id`
133
+ - `title`
134
+ - `sessionTitle`
135
+ - `owner`
136
+ - `messageCount`
137
+ - `modifiedTime`
138
+ - `createdTime`
139
+ - `isFavorite`
140
+ - `cwd`
141
+ - `decompSessionType`
142
+ - `decompMissionId`
143
+
144
+ Archived sessions are excluded automatically, and results are sorted by `modifiedTime` descending.
145
+
146
+ ## `DroidSession`
147
+
148
+ Returned by `createSession()` and `resumeSession()`.
149
+
150
+ ### Core methods
151
+
152
+ | Method | Description |
153
+ | :----------------------- | :--------------------------------------------------------------------- |
154
+ | `stream(text, options?)` | Sends a message and yields `DroidMessage` events until `turn_complete` |
155
+ | `send(text, options?)` | Sends a message and returns aggregated text, messages, and token usage |
156
+ | `interrupt()` | Gracefully interrupts the current turn |
157
+ | `close()` | Closes the underlying connection |
158
+ | `updateSettings(params)` | Updates model/session settings |
159
+ | `enterSpecMode(params?)` | Switches the current session into spec mode |
160
+
161
+ ### Session utilities
162
+
163
+ | Method | Description |
164
+ | :------------------------ | :----------------------------------------------------- |
165
+ | `forkSession()` | Creates a new server-side session from the current one |
166
+ | `renameSession(params)` | Renames the current session |
167
+ | `compactSession(params?)` | Requests server-side compaction |
168
+ | `getRewindInfo(params)` | Fetches rewind metadata |
169
+ | `executeRewind(params)` | Executes a rewind |
170
+
171
+ ### MCP and discovery helpers
172
+
173
+ | Method | Description |
174
+ | :---------------------------------- | :------------------------------------------------------- |
175
+ | `addMcpServer(params)` | Adds an MCP server |
176
+ | `removeMcpServer(params)` | Removes an MCP server |
177
+ | `toggleMcpServer(params)` | Enables or disables a server |
178
+ | `listMcpServers()` | Lists configured MCP servers |
179
+ | `listMcpTools()` | Lists tools exposed by connected MCP servers |
180
+ | `authenticateMcpServer(params)` | Starts MCP OAuth/authentication flow |
181
+ | `listSkills()` | Lists available skills |
182
+ | `listTools(params?)` | Lists the exec tool catalog and current allow/deny state |
183
+ | `onNotification(callback, filter?)` | Subscribes to raw session notifications |
184
+
185
+ ### Properties
186
+
187
+ | Property | Type | Description |
188
+ | :----------- | :--------------------------------------------- | :-------------------------- |
189
+ | `sessionId` | `string` | Active session ID |
190
+ | `initResult` | `InitializeSessionResult \| LoadSessionResult` | Raw initialize/load payload |
191
+
192
+ ### Message attachments
193
+
194
+ `session.stream()` and `session.send()` accept `MessageOptions`:
195
+
196
+ | Field | Type | Description |
197
+ | :------- | :-------------------- | :------------------------------- |
198
+ | `images` | `Base64ImageSource[]` | Inline image attachments |
199
+ | `files` | `DocumentSource[]` | Inline file/document attachments |
200
+
201
+ ### `DroidResult`
202
+
203
+ Returned by `session.send()`:
204
+
205
+ | Field | Type | Description |
206
+ | :----------- | :------------------------- | :-------------------------------- |
207
+ | `text` | `string` | Concatenated assistant text |
208
+ | `messages` | `DroidMessage[]` | All stream messages from the turn |
209
+ | `tokenUsage` | `TokenUsageUpdate \| null` | Final token usage if available |
210
+
211
+ ## Stream message model
212
+
213
+ All streamed events are part of the `DroidMessage` union and are discriminated by `type`.
214
+
215
+ ### Common message types
216
+
217
+ | Type | Description |
218
+ | :---------------------- | :------------------------------------------ |
219
+ | `assistant_text_delta` | Assistant text token |
220
+ | `thinking_text_delta` | Thinking/reasoning token |
221
+ | `tool_use` | Tool invocation |
222
+ | `tool_result` | Tool result |
223
+ | `tool_progress` | Tool progress update |
224
+ | `working_state_changed` | Agent state transition |
225
+ | `token_usage_update` | Token counter update |
226
+ | `create_message` | Full assistant message |
227
+ | `permission_resolved` | Permission outcome |
228
+ | `settings_updated` | Session settings changed |
229
+ | `session_title_updated` | Session title changed |
230
+ | `mcp_status_changed` | MCP status changed |
231
+ | `mcp_auth_required` | MCP authentication required |
232
+ | `mcp_auth_completed` | MCP authentication completed |
233
+ | `error` | Error from the process |
234
+ | `turn_complete` | End-of-turn sentinel synthesized by the SDK |
235
+
236
+ ### Mission and AGI messages
237
+
238
+ When running in `DroidInteractionMode.AGI`, the stream can also emit:
239
+
240
+ - `mission_state_changed`
241
+ - `mission_features_changed`
242
+ - `mission_progress_entry`
243
+ - `mission_heartbeat`
244
+ - `mission_worker_started`
245
+ - `mission_worker_completed`
246
+
247
+ ## Configuration and enums
248
+
249
+ High-value exported enums include:
250
+
251
+ - `DroidInteractionMode`
252
+ - `AutonomyLevel`
253
+ - `ReasoningEffort`
254
+ - `ToolConfirmationOutcome`
255
+ - `ToolConfirmationType`
256
+ - `SessionNotificationType`
257
+ - `DroidWorkingState`
258
+ - `McpServerType`
259
+ - `McpServerStatus`
260
+ - `McpAuthOutcome`
261
+ - `MissionState`
262
+ - `FeatureStatus`
263
+ - `SettingsLevel`
264
+ - `SkillLocation`
265
+
266
+ ### Common `CreateSessionOptions`
267
+
268
+ `CreateSessionOptions` supports the main runtime controls:
269
+
270
+ | Field | Description |
271
+ | :------------------------------ | :------------------------------------------------- |
272
+ | `cwd` | Working directory |
273
+ | `machineId` | Machine identifier passed during initialization |
274
+ | `modelId` | Default model |
275
+ | `autonomyLevel` | Command autonomy level |
276
+ | `interactionMode` | `Auto`, `Spec`, or `AGI` |
277
+ | `reasoningEffort` | LLM reasoning depth |
278
+ | `specModeModelId` | Spec-mode model override |
279
+ | `specModeReasoningEffort` | Spec-mode reasoning override |
280
+ | `mcpServers` | Initial MCP server configs |
281
+ | `enabledToolIds` | Explicit tool allowlist |
282
+ | `disabledToolIds` | Explicit tool denylist |
283
+ | `tags` | Session tags; the SDK also appends its own SDK tag |
284
+ | `permissionHandler` | Tool confirmation callback |
285
+ | `askUserHandler` | Structured user-input callback |
286
+ | `transport` | Custom transport override |
287
+ | `execPath` / `execArgs` / `env` | Subprocess configuration |
288
+ | `abortSignal` | External cancellation signal |
289
+
290
+ ## Permission and ask-user handling
291
+
292
+ The high-level APIs support:
293
+
294
+ - `permissionHandler` for tool approval decisions
295
+ - `askUserHandler` for structured clarification flows
296
+
297
+ For spec mode, approval flows also support fresh-session outcomes such as `ToolConfirmationOutcome.ProceedNewSessionHigh`.
298
+
299
+ ## MCP support
300
+
301
+ The SDK supports:
302
+
303
+ - configuring MCP servers at session startup
304
+ - adding/removing/toggling servers during a session
305
+ - listing MCP servers and tools
306
+ - starting authentication flows with `authenticateMcpServer()`
307
+ - receiving `mcp_auth_required` and `mcp_auth_completed` stream events
308
+
309
+ Advanced auth controls such as cancelling or clearing MCP auth are available on the low-level `DroidClient`.
310
+
311
+ ## Low-level APIs
312
+
313
+ Most users should use `query()` and `DroidSession`, but the package also exports lower layers.
314
+
315
+ ### `ProcessTransport`
316
+
317
+ Spawns `droid exec --input-format stream-jsonrpc --output-format stream-jsonrpc` and implements the transport interface used by the client.
318
+
319
+ ### `ProtocolEngine`
320
+
321
+ Handles JSON-RPC framing, request/response correlation, notifications, permission requests, and ask-user requests.
322
+
323
+ ### `DroidClient`
324
+
325
+ Low-level typed JSON-RPC client. Use it when you need direct RPC access beyond `DroidSession`.
326
+
327
+ Notable client-only capabilities include:
328
+
329
+ - `killWorkerSession()`
330
+ - `cancelMcpAuth()`
331
+ - `clearMcpAuth()`
332
+ - `submitMcpAuthCode()`
333
+ - `listMcpRegistry()`
334
+ - `toggleMcpTool()`
335
+ - `submitBugReport()`
336
+
337
+ ## Runtime validation and schemas
338
+
339
+ The package exports its Zod schema surface from `src/schemas/index.ts`, including:
340
+
341
+ - request and response schemas
342
+ - notification schemas
343
+ - enum schemas and constants
344
+ - message/content schemas
345
+ - mission schemas
346
+ - MCP schemas
347
+
348
+ This makes it possible to validate protocol payloads at runtime in advanced integrations.
349
+
350
+ ## Errors
351
+
352
+ The SDK exports these custom errors:
353
+
354
+ | Error | Description |
355
+ | :--------------------- | :---------------------------------------- |
356
+ | `ConnectionError` | Failed to connect to the Droid subprocess |
357
+ | `ProtocolError` | JSON-RPC or protocol-level failure |
358
+ | `SessionError` | Base session error |
359
+ | `SessionNotFoundError` | Requested session does not exist |
360
+ | `TimeoutError` | RPC timed out |
361
+ | `ProcessExitError` | The subprocess exited unexpectedly |
362
+
363
+ ## Feature coverage in this document
364
+
365
+ This reference intentionally documents only features confirmed in the current SDK implementation and recent shipped commits, including:
366
+
367
+ - spec mode controls
368
+ - tool controls
369
+ - initialization metadata
370
+ - session forking
371
+ - filesystem-based session discovery
372
+ - rename, compact, rewind, and MCP/session management APIs
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Manual smoke test for cancelling `session.stream(..., { abortSignal })`.
3
+ *
4
+ * Starts a long-running streaming turn, aborts it after a short delay, then
5
+ * verifies the stream rejects with the abort reason.
6
+ *
7
+ * Usage:
8
+ * npx tsx examples/abort-session-stream.ts
9
+ * npx tsx examples/abort-session-stream.ts 3000
10
+ */
11
+
12
+ import { createSession, DroidMessageType } from '../src/index.js';
13
+
14
+ async function main(): Promise<void> {
15
+ const abortAfterMs = Number(process.argv[2] ?? 2000);
16
+ if (!Number.isFinite(abortAfterMs) || abortAfterMs <= 0) {
17
+ throw new Error('Abort delay must be a positive number of milliseconds.');
18
+ }
19
+
20
+ const controller = new AbortController();
21
+ const session = await createSession({ cwd: process.cwd() });
22
+ let textDeltaCount = 0;
23
+ let receivedText = '';
24
+
25
+ const timeout = setTimeout(() => {
26
+ controller.abort(new Error(`Aborted after ${abortAfterMs}ms`));
27
+ }, abortAfterMs);
28
+
29
+ try {
30
+ console.log(`Session created: ${session.sessionId}`);
31
+ console.log(`Aborting after ${abortAfterMs}ms\n`);
32
+
33
+ const prompt =
34
+ 'Write a long, detailed explanation of how compilers work, including ' +
35
+ 'lexing, parsing, semantic analysis, optimization, and code generation.';
36
+
37
+ for await (const message of session.stream(prompt, {
38
+ abortSignal: controller.signal,
39
+ })) {
40
+ if (message.type === DroidMessageType.AssistantTextDelta) {
41
+ textDeltaCount++;
42
+ receivedText += message.text;
43
+ process.stdout.write(message.text);
44
+ }
45
+ }
46
+
47
+ throw new Error('Expected stream to abort, but it completed normally.');
48
+ } catch (error) {
49
+ if (error instanceof Error && error.message.includes('Aborted after')) {
50
+ console.log('\n\n=== Abort confirmed ===');
51
+ console.log(`Received text deltas before abort: ${textDeltaCount}`);
52
+ console.log(`Received characters before abort: ${receivedText.length}`);
53
+ return;
54
+ }
55
+
56
+ throw error;
57
+ } finally {
58
+ clearTimeout(timeout);
59
+ await session.close();
60
+ console.log('Session closed.');
61
+ }
62
+ }
63
+
64
+ main().catch((err: unknown) => {
65
+ console.error('Error:', err);
66
+ process.exit(1);
67
+ });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * One-off integration smoke test for JSON output against droid-dev.
3
+ *
4
+ * Usage:
5
+ * npx tsx examples/droid-dev-structured-output.ts
6
+ * DROID_EXEC_PATH=/path/to/droid-dev npx tsx examples/droid-dev-structured-output.ts
7
+ */
8
+
9
+ import { z } from 'zod';
10
+
11
+ import { run } from '../src/index.js';
12
+
13
+ const PersonSchema = z.object({
14
+ name: z.literal('Ada Lovelace'),
15
+ language: z.literal('TypeScript'),
16
+ score: z.number(),
17
+ });
18
+
19
+ async function main(): Promise<void> {
20
+ const execPath = process.env['DROID_EXEC_PATH'] ?? 'droid-dev';
21
+
22
+ const result = await run(
23
+ [
24
+ 'Return a structured object for Ada Lovelace.',
25
+ 'Use name "Ada Lovelace", language "TypeScript", and score 99.',
26
+ 'Return only valid JSON and do not include markdown fences.',
27
+ ].join(' '),
28
+ {
29
+ execPath,
30
+ cwd: process.cwd(),
31
+ }
32
+ );
33
+
34
+ const parsed = PersonSchema.parse(JSON.parse(result.text));
35
+
36
+ console.log(`droid executable: ${execPath}`);
37
+ console.log('structured output:', JSON.stringify(parsed, null, 2));
38
+ console.log(`messages received: ${result.messages.length}`);
39
+ }
40
+
41
+ main().catch((error: unknown) => {
42
+ console.error(error);
43
+ process.exit(1);
44
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Fork session example.
3
+ *
4
+ * Demonstrates:
5
+ * - creating a session and establishing some context
6
+ * - calling `forkSession()` to get a new server-side session ID
7
+ * - resuming the fork and continuing from the copied context
8
+ *
9
+ * Usage:
10
+ * npx tsx examples/fork-session.ts
11
+ */
12
+
13
+ import { createSession, resumeSession } from '../src/index.js';
14
+
15
+ async function main(): Promise<void> {
16
+ const session = await createSession({ cwd: process.cwd() });
17
+ let fork: Awaited<ReturnType<typeof resumeSession>> | null = null;
18
+
19
+ try {
20
+ console.log(`Original session: ${session.sessionId}\n`);
21
+
22
+ await session.send('Remember this phrase exactly: mango sunrise');
23
+
24
+ const { newSessionId } = await session.forkSession();
25
+ console.log(`Forked session: ${newSessionId}\n`);
26
+
27
+ fork = await resumeSession(newSessionId, { cwd: process.cwd() });
28
+
29
+ const result = await fork.send('What phrase did I ask you to remember?');
30
+ console.log('Fork response:');
31
+ console.log(result.text);
32
+ } finally {
33
+ await fork?.close();
34
+ await session.close();
35
+ console.log('\nBoth sessions closed.');
36
+ }
37
+ }
38
+
39
+ main().catch((err: unknown) => {
40
+ console.error('Error:', err);
41
+ process.exit(1);
42
+ });
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Initialization metadata example.
3
+ *
4
+ * Demonstrates:
5
+ * - `query().sessionId`, `query().initResult`, and `query().initialized`
6
+ * - `session.initResult` from `createSession()`
7
+ * - `resumed.initResult` from `resumeSession()`
8
+ *
9
+ * Usage:
10
+ * npx tsx examples/init-metadata.ts
11
+ */
12
+
13
+ import {
14
+ createSession,
15
+ DroidMessageType,
16
+ query,
17
+ resumeSession,
18
+ } from '../src/index.js';
19
+
20
+ async function main(): Promise<void> {
21
+ const stream = query({
22
+ prompt: 'Reply with "ready" and nothing else.',
23
+ cwd: process.cwd(),
24
+ });
25
+
26
+ console.log('=== query() metadata ===');
27
+ console.log(`Before initialization: sessionId=${String(stream.sessionId)}`);
28
+ console.log(`Before initialization: initResult=${String(stream.initResult)}`);
29
+
30
+ const initialized = await stream.initialized;
31
+ console.log(`After initialization: sessionId=${initialized.sessionId}`);
32
+ console.log(`Model: ${String(initialized.settings.modelId)}`);
33
+ console.log(
34
+ `Cached initResult matches initialized: ${String(stream.initResult === initialized)}`
35
+ );
36
+
37
+ let text = '';
38
+ for await (const msg of stream) {
39
+ if (msg.type === DroidMessageType.AssistantTextDelta) {
40
+ text += msg.text;
41
+ }
42
+ }
43
+ console.log(`Response: ${JSON.stringify(text.trim())}\n`);
44
+
45
+ console.log('=== createSession() / resumeSession() metadata ===');
46
+ const session = await createSession({ cwd: process.cwd() });
47
+ let resumed: Awaited<ReturnType<typeof resumeSession>> | null = null;
48
+
49
+ try {
50
+ console.log(`Created session: ${session.sessionId}`);
51
+ console.log(
52
+ `createSession().initResult.settings.modelId=${String(session.initResult.settings.modelId)}`
53
+ );
54
+
55
+ resumed = await resumeSession(session.sessionId, {
56
+ cwd: process.cwd(),
57
+ });
58
+
59
+ console.log(`Resumed session: ${resumed.sessionId}`);
60
+ console.log(
61
+ `resumeSession().initResult.cwd=${String(resumed.initResult.cwd)}`
62
+ );
63
+ console.log(
64
+ `resumeSession().initResult.availableModels=${String(resumed.initResult.availableModels?.length ?? 0)}`
65
+ );
66
+ } finally {
67
+ await resumed?.close();
68
+ await session.close();
69
+ console.log('\nSessions closed.');
70
+ }
71
+ }
72
+
73
+ main().catch((err: unknown) => {
74
+ console.error('Error:', err);
75
+ process.exit(1);
76
+ });
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Interrupt session example.
3
+ *
4
+ * Demonstrates using `session.interrupt()` to stop the agent mid-turn.
5
+ * Starts a streaming turn with a long-running prompt, waits for a few
6
+ * `AssistantTextDelta` messages, then sends an interrupt and continues
7
+ * consuming the stream until `TurnComplete`.
8
+ *
9
+ * Usage:
10
+ * npx tsx examples/interrupt-session.ts
11
+ */
12
+
13
+ import { createSession, DroidMessageType } from '../src/index.js';
14
+
15
+ async function main(): Promise<void> {
16
+ const session = await createSession({ cwd: process.cwd() });
17
+ try {
18
+ console.log(`Session created: ${session.sessionId}\n`);
19
+
20
+ const prompt =
21
+ 'Write a detailed essay about the history of computing, from the ' +
22
+ 'earliest mechanical calculators to modern quantum computers.';
23
+ console.log(`Prompt: "${prompt}"\n`);
24
+
25
+ let deltaCount = 0;
26
+ let interrupted = false;
27
+
28
+ for await (const msg of session.stream(prompt)) {
29
+ switch (msg.type) {
30
+ case DroidMessageType.AssistantTextDelta:
31
+ deltaCount++;
32
+ process.stdout.write(msg.text);
33
+
34
+ if (deltaCount === 5 && !interrupted) {
35
+ interrupted = true;
36
+ console.log('\n\n>>> Sending interrupt after 5 text deltas...\n');
37
+ await session.interrupt();
38
+ }
39
+ break;
40
+
41
+ case DroidMessageType.TurnComplete:
42
+ console.log('\n\n--- Turn complete ---');
43
+ console.log(`Total text deltas received: ${deltaCount}`);
44
+ console.log(
45
+ interrupted
46
+ ? 'Session was interrupted successfully.'
47
+ : 'Session completed without interruption.'
48
+ );
49
+ if (msg.tokenUsage) {
50
+ console.log(
51
+ `Tokens — input: ${msg.tokenUsage.inputTokens}, ` +
52
+ `output: ${msg.tokenUsage.outputTokens}`
53
+ );
54
+ }
55
+ break;
56
+ }
57
+ }
58
+ } finally {
59
+ await session.close();
60
+ console.log('\nSession closed.');
61
+ }
62
+ }
63
+
64
+ main().catch((err: unknown) => {
65
+ console.error('Error:', err);
66
+ process.exit(1);
67
+ });