@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,67 +0,0 @@
1
- /**
2
- * Manual smoke test for the daemon SDK — multiple concurrent sessions.
3
- *
4
- * Spawns a local daemon, creates two sessions in separate /tmp directories,
5
- * and runs them concurrently over a single WebSocket connection.
6
- *
7
- * Usage:
8
- * npx tsx examples/daemon-multi-session.ts
9
- */
10
-
11
- import { connectDaemon, DroidMessageType } from '@factory/droid-sdk';
12
-
13
- async function main(): Promise<void> {
14
- console.log('Connecting to local daemon...\n');
15
- const daemon = await connectDaemon({ apiKey: process.env.FACTORY_API_KEY! });
16
- console.log('Connected!\n');
17
-
18
- const frontend = await daemon.createSession({
19
- cwd: '/tmp/daemon-test-frontend',
20
- });
21
- const backend = await daemon.createSession({
22
- cwd: '/tmp/daemon-test-backend',
23
- });
24
-
25
- console.log('Two sessions created. Running concurrently...\n');
26
-
27
- await Promise.all([
28
- (async () => {
29
- for await (const msg of frontend.stream(
30
- 'Create a file called hello.md with a short greeting message. Just a few lines.'
31
- )) {
32
- if (msg.type === DroidMessageType.Assistant) {
33
- process.stdout.write(`[frontend] ${msg.text}\n`);
34
- } else if (msg.type === DroidMessageType.ToolCall) {
35
- console.log(`[frontend] [tool] ${msg.toolUse.name}`);
36
- } else if (msg.type === DroidMessageType.Result) {
37
- console.log(`[frontend] Done in ${msg.durationMs}ms`);
38
- }
39
- }
40
- await frontend.close();
41
- })(),
42
- (async () => {
43
- for await (const msg of backend.stream(
44
- 'Create a file called notes.md with 3 random fun facts. Keep it short.'
45
- )) {
46
- if (msg.type === DroidMessageType.Assistant) {
47
- process.stdout.write(`[backend] ${msg.text}\n`);
48
- } else if (msg.type === DroidMessageType.ToolCall) {
49
- console.log(`[backend] [tool] ${msg.toolUse.name}`);
50
- } else if (msg.type === DroidMessageType.Result) {
51
- console.log(`[backend] Done in ${msg.durationMs}ms`);
52
- }
53
- }
54
- await backend.close();
55
- })(),
56
- ]);
57
-
58
- await daemon.close();
59
- console.log(
60
- '\nDone. Check /tmp/daemon-test-frontend/hello.md and /tmp/daemon-test-backend/notes.md'
61
- );
62
- }
63
-
64
- main().catch((err: unknown) => {
65
- console.error('Error:', err);
66
- process.exit(1);
67
- });
@@ -1,44 +0,0 @@
1
- /**
2
- * One-off integration smoke test for JSON output against Droid.
3
- *
4
- * Usage:
5
- * npx tsx examples/droid-dev-structured-output.ts
6
- * DROID_EXEC_PATH=/path/to/droid npx tsx examples/droid-dev-structured-output.ts
7
- */
8
-
9
- import { run } from '@factory/droid-sdk';
10
- import { z } from 'zod';
11
-
12
- const PersonSchema = z.object({
13
- name: z.literal('Ada Lovelace'),
14
- language: z.literal('TypeScript'),
15
- score: z.number(),
16
- });
17
-
18
- async function main(): Promise<void> {
19
- const execPath = process.env['DROID_EXEC_PATH'] ?? 'droid';
20
-
21
- const result = await run(
22
- [
23
- 'Return a structured object for Ada Lovelace.',
24
- 'Use name "Ada Lovelace", language "TypeScript", and score 99.',
25
- 'Return only valid JSON and do not include markdown fences.',
26
- ].join(' '),
27
- {
28
- apiKey: process.env.FACTORY_API_KEY!,
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
- });
@@ -1,68 +0,0 @@
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 {
14
- DroidMessageType,
15
- createSession,
16
- resumeSession,
17
- } from '@factory/droid-sdk';
18
-
19
- async function streamText(
20
- session: Awaited<ReturnType<typeof createSession>>,
21
- prompt: string
22
- ): Promise<string> {
23
- let text = '';
24
- for await (const msg of session.stream(prompt, {
25
- includePartialMessages: true,
26
- })) {
27
- if (msg.type === DroidMessageType.AssistantTextDelta) {
28
- text += msg.text;
29
- }
30
- }
31
- return text;
32
- }
33
-
34
- async function main(): Promise<void> {
35
- const session = await createSession({
36
- apiKey: process.env.FACTORY_API_KEY!,
37
- cwd: process.cwd(),
38
- });
39
- let fork: Awaited<ReturnType<typeof resumeSession>> | null = null;
40
-
41
- try {
42
- console.log(`Original session: ${session.sessionId}\n`);
43
- await streamText(session, 'Remember this phrase exactly: mango sunrise');
44
-
45
- const { newSessionId } = await session.forkSession();
46
- console.log(`Forked session: ${newSessionId}\n`);
47
-
48
- fork = await resumeSession(newSessionId, {
49
- apiKey: process.env.FACTORY_API_KEY!,
50
- });
51
-
52
- const result = await streamText(
53
- fork,
54
- 'What phrase did I ask you to remember?'
55
- );
56
- console.log('Fork response:');
57
- console.log(result);
58
- } finally {
59
- await fork?.close();
60
- await session.close();
61
- console.log('\nBoth sessions closed.');
62
- }
63
- }
64
-
65
- main().catch((err: unknown) => {
66
- console.error('Error:', err);
67
- process.exit(1);
68
- });
@@ -1,68 +0,0 @@
1
- /**
2
- * Hook execution streaming example.
3
- *
4
- * Demonstrates how to handle hook execution messages in a session stream.
5
- *
6
- * Usage:
7
- * npx tsx examples/hook-execution.ts
8
- */
9
-
10
- import { DroidMessageType, createSession } from '../src/index.js';
11
-
12
- async function main(): Promise<void> {
13
- const prompt = 'Run a simple shell command using Execute tool.';
14
-
15
- console.log(`Sending prompt: "${prompt}"\n`);
16
-
17
- // Note: To actually see hooks, you need to have hooks configured in your droid settings.
18
- const apiKey = process.env.FACTORY_API_KEY;
19
- if (!apiKey) {
20
- console.error('Set FACTORY_API_KEY environment variable.');
21
- process.exit(1);
22
- }
23
-
24
- const session = await createSession({ apiKey, cwd: process.cwd() });
25
-
26
- try {
27
- for await (const msg of session.stream(prompt)) {
28
- switch (msg.type) {
29
- case DroidMessageType.Assistant:
30
- process.stdout.write(msg.text);
31
- break;
32
-
33
- case DroidMessageType.ToolCall:
34
- console.log(`\n[Tool Call] ${msg.toolUse.name} (${msg.toolUse.id})`);
35
- break;
36
-
37
- case DroidMessageType.Hook:
38
- if (msg.status === 'started') {
39
- console.log(
40
- ` [Hook Started] ID: ${msg.hookId}, Event: ${msg.eventName}, Command: ${msg.command}`
41
- );
42
- } else {
43
- console.log(
44
- ` [Hook ${msg.status}] ID: ${msg.hookId}, Exit Code: ${msg.exitCode}`
45
- );
46
- if (msg.stdout) console.log(` stdout: ${msg.stdout.trim()}`);
47
- if (msg.stderr) console.log(` stderr: ${msg.stderr.trim()}`);
48
- }
49
- break;
50
-
51
- case DroidMessageType.ToolResult:
52
- console.log(`[Tool Result] ${msg.isError ? 'Error' : 'OK'}`);
53
- break;
54
-
55
- case DroidMessageType.Result:
56
- console.log('\n\n--- Turn complete ---');
57
- break;
58
- }
59
- }
60
- } finally {
61
- await session.close();
62
- }
63
- }
64
-
65
- main().catch((err: unknown) => {
66
- console.error('Error:', err);
67
- process.exit(1);
68
- });
@@ -1,24 +0,0 @@
1
- /**
2
- * Initialization metadata example.
3
- *
4
- * Usage:
5
- * npx tsx examples/init-metadata.ts
6
- */
7
-
8
- import { createSession, resumeSession } from '@factory/droid-sdk';
9
-
10
- const session = await createSession({
11
- apiKey: process.env.FACTORY_API_KEY!,
12
- cwd: process.cwd(),
13
- });
14
- const resumed = await resumeSession(session.sessionId, {
15
- apiKey: process.env.FACTORY_API_KEY!,
16
- });
17
-
18
- console.log(`created session: ${session.sessionId}`);
19
- console.log(`resumed session: ${resumed.sessionId}`);
20
- console.log(`created model: ${String(session.initResult.settings.modelId)}`);
21
- console.log(`resumed cwd: ${String(resumed.initResult.cwd)}`);
22
-
23
- await resumed.close();
24
- await session.close();
@@ -1,19 +0,0 @@
1
- /**
2
- * List saved sessions example.
3
- *
4
- * Usage:
5
- * npx tsx examples/list-sessions.ts
6
- */
7
-
8
- import { listSessions } from '@factory/droid-sdk';
9
-
10
- const sessions = await listSessions({ numSessions: 5 });
11
-
12
- for (const session of sessions) {
13
- const title = session.sessionTitle ?? session.title ?? '(untitled)';
14
- console.log(`${session.id}: ${title}`);
15
- }
16
-
17
- if (sessions.length === 0) {
18
- console.log('No sessions found for this project.');
19
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * Multi-turn session example.
3
- *
4
- * Usage:
5
- * npx tsx examples/multi-turn-session.ts
6
- */
7
-
8
- import { DroidMessageType, createSession } from '@factory/droid-sdk';
9
-
10
- async function streamText(
11
- session: Awaited<ReturnType<typeof createSession>>,
12
- prompt: string
13
- ): Promise<string> {
14
- let text = '';
15
- for await (const msg of session.stream(prompt, {
16
- includePartialMessages: true,
17
- })) {
18
- if (msg.type === DroidMessageType.AssistantTextDelta) {
19
- text += msg.text;
20
- }
21
- }
22
- return text;
23
- }
24
-
25
- const session = await createSession({
26
- apiKey: process.env.FACTORY_API_KEY!,
27
- cwd: process.cwd(),
28
- });
29
-
30
- try {
31
- console.log(`Session: ${session.sessionId}\n`);
32
-
33
- const first = await streamText(session, 'What is this project?');
34
- console.log(first);
35
-
36
- const second = await streamText(session, 'What should I test first?');
37
- console.log('\n', second);
38
- } finally {
39
- await session.close();
40
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Permission handler example.
3
- *
4
- * Usage:
5
- * npx tsx examples/permission-handler.ts
6
- */
7
-
8
- import { mkdtemp, readFile, rm } from 'node:fs/promises';
9
- import { tmpdir } from 'node:os';
10
- import { join } from 'node:path';
11
-
12
- import {
13
- ToolConfirmationOutcome,
14
- ToolConfirmationType,
15
- run,
16
- } from '@factory/droid-sdk';
17
-
18
- const tempDir = await mkdtemp(join(tmpdir(), 'droid-sdk-permission-'));
19
- const outputPath = join(tempDir, 'hello.txt');
20
-
21
- try {
22
- await run(`Create ${outputPath} with the text "Hello, World!"`, {
23
- apiKey: process.env.FACTORY_API_KEY!,
24
- cwd: process.cwd(),
25
- permissionHandler(params) {
26
- const onlyCreatesExpectedFile = params.toolUses.every(
27
- (item) =>
28
- item.details.type === ToolConfirmationType.Create &&
29
- item.details.filePath === outputPath
30
- );
31
-
32
- return onlyCreatesExpectedFile
33
- ? ToolConfirmationOutcome.ProceedOnce
34
- : ToolConfirmationOutcome.Cancel;
35
- },
36
- });
37
-
38
- console.log(await readFile(outputPath, 'utf8'));
39
- } finally {
40
- await rm(tempDir, { recursive: true, force: true });
41
- }
@@ -1,26 +0,0 @@
1
- import { OutputFormatType, run } from '@factory/droid-sdk';
2
-
3
- type FavoriteNumber = { favoriteNumber: number };
4
-
5
- const result = await run('Pick a favorite number between 1 and 42.', {
6
- apiKey: process.env.FACTORY_API_KEY!,
7
- cwd: process.cwd(),
8
- outputFormat: {
9
- type: OutputFormatType.JsonSchema,
10
- schema: {
11
- type: 'object',
12
- properties: {
13
- favoriteNumber: {
14
- type: 'number',
15
- minimum: 1,
16
- maximum: 42,
17
- },
18
- },
19
- required: ['favoriteNumber'],
20
- },
21
- },
22
- });
23
-
24
- console.log(
25
- (result.structuredOutput as FavoriteNumber | undefined)?.favoriteNumber
26
- );
@@ -1,63 +0,0 @@
1
- /**
2
- * DroidResult metadata example.
3
- *
4
- * Runs a prompt with the actual Droid CLI and prints the aggregated `run()`
5
- * result metadata: sessionId, durationMs, turnCount, success, error, and token
6
- * usage.
7
- *
8
- * Usage:
9
- * npx tsx examples/result-metadata.ts
10
- * npx tsx examples/result-metadata.ts "Reply with metadata details."
11
- */
12
-
13
- import assert from 'node:assert/strict';
14
-
15
- import { run } from '@factory/droid-sdk';
16
-
17
- async function main(): Promise<void> {
18
- const prompt =
19
- process.argv.slice(2).join(' ') ||
20
- 'Reply with "Metadata OK" and nothing else.';
21
-
22
- console.log(`Sending prompt: "${prompt}"\n`);
23
-
24
- const result = await run(prompt, {
25
- apiKey: process.env.FACTORY_API_KEY!,
26
- cwd: process.cwd(),
27
- });
28
-
29
- assert.equal(typeof result.sessionId, 'string');
30
- assert.ok(result.sessionId.length > 0);
31
- assert.equal(typeof result.durationMs, 'number');
32
- assert.ok(result.durationMs >= 0);
33
- assert.equal(typeof result.turnCount, 'number');
34
- assert.ok(result.turnCount >= 1);
35
- assert.equal(typeof result.success, 'boolean');
36
- assert.ok(Array.isArray(result.messages));
37
-
38
- console.log('=== Result ===');
39
- console.log(result.text);
40
-
41
- console.log('\n=== Metadata ===');
42
- console.log(`Session ID: ${result.sessionId}`);
43
- console.log(`Duration: ${result.durationMs}ms`);
44
- console.log(`Turn count: ${result.turnCount}`);
45
- console.log(`Success: ${String(result.success)}`);
46
- console.log(`Messages received: ${result.messages.length}`);
47
-
48
- if (result.error) {
49
- console.log(`Error: ${result.error.message}`);
50
- }
51
-
52
- if (result.tokenUsage) {
53
- console.log(
54
- `Tokens — input: ${result.tokenUsage.inputTokens}, ` +
55
- `output: ${result.tokenUsage.outputTokens}`
56
- );
57
- }
58
- }
59
-
60
- main().catch((error: unknown) => {
61
- console.error('Error:', error);
62
- process.exit(1);
63
- });
package/examples/run.ts DELETED
@@ -1,39 +0,0 @@
1
- /**
2
- * Manual smoke test for the one-shot `run()` API.
3
- *
4
- * Sends a single prompt, prints the aggregated result, and exits after the
5
- * underlying session has been closed by `run()`.
6
- *
7
- * Usage:
8
- * npx tsx examples/run.ts
9
- * npx tsx examples/run.ts "What is 2 + 2?"
10
- */
11
-
12
- import { run } from '@factory/droid-sdk';
13
-
14
- async function main(): Promise<void> {
15
- const text = process.argv.slice(2).join(' ') || 'What is 2 + 2?';
16
-
17
- console.log(`Sending prompt: "${text}"\n`);
18
-
19
- const result = await run(text, {
20
- apiKey: process.env.FACTORY_API_KEY!,
21
- cwd: process.cwd(),
22
- });
23
-
24
- console.log('=== Result ===');
25
- console.log(result.text);
26
- console.log(`Messages received: ${result.messages.length}`);
27
-
28
- if (result.tokenUsage) {
29
- console.log(
30
- `Tokens — input: ${result.tokenUsage.inputTokens}, ` +
31
- `output: ${result.tokenUsage.outputTokens}`
32
- );
33
- }
34
- }
35
-
36
- main().catch((err: unknown) => {
37
- console.error('Error:', err);
38
- process.exit(1);
39
- });
@@ -1,57 +0,0 @@
1
- /**
2
- * Simple session streaming example.
3
- *
4
- * Demonstrates using `session.stream()` to send a prompt, streaming
5
- * full assistant/tool messages, and handling the final `result`.
6
- *
7
- * Usage:
8
- * npx tsx examples/session-stream.ts
9
- */
10
-
11
- import { DroidMessageType, createSession } from '@factory/droid-sdk';
12
-
13
- async function main(): Promise<void> {
14
- const prompt = process.argv[2] ?? 'What files are in the current directory?';
15
-
16
- console.log(`Sending prompt: "${prompt}"\n`);
17
-
18
- const session = await createSession({
19
- apiKey: process.env.FACTORY_API_KEY!,
20
- cwd: process.cwd(),
21
- });
22
-
23
- try {
24
- for await (const msg of session.stream(prompt)) {
25
- switch (msg.type) {
26
- case DroidMessageType.Assistant:
27
- process.stdout.write(msg.text);
28
- break;
29
-
30
- case DroidMessageType.ToolCall:
31
- console.log(`\n[Tool] ${msg.toolUse.name}`);
32
- break;
33
-
34
- case DroidMessageType.ToolResult:
35
- console.log(`[Tool Result] ${msg.isError ? 'Error' : 'OK'}`);
36
- break;
37
-
38
- case DroidMessageType.Result:
39
- console.log('\n\n--- Turn complete ---');
40
- if (msg.tokenUsage) {
41
- console.log(
42
- `Tokens — input: ${msg.tokenUsage.inputTokens}, ` +
43
- `output: ${msg.tokenUsage.outputTokens}`
44
- );
45
- }
46
- break;
47
- }
48
- }
49
- } finally {
50
- await session.close();
51
- }
52
- }
53
-
54
- main().catch((err: unknown) => {
55
- console.error('Error:', err);
56
- process.exit(1);
57
- });
@@ -1,61 +0,0 @@
1
- /**
2
- * Spec mode: approve and hand off implementation to a new session.
3
- *
4
- * Usage:
5
- * npx tsx examples/spec-mode-new-session.ts
6
- */
7
-
8
- import { mkdtemp, readFile, rm } from 'node:fs/promises';
9
- import { tmpdir } from 'node:os';
10
- import { join } from 'node:path';
11
-
12
- import {
13
- DroidInteractionMode,
14
- ReasoningEffort,
15
- ToolConfirmationOutcome,
16
- ToolConfirmationType,
17
- createSession,
18
- } from '@factory/droid-sdk';
19
-
20
- const tempDir = await mkdtemp(join(tmpdir(), 'droid-sdk-spec-'));
21
- const outputPath = join(tempDir, 'hello.txt');
22
-
23
- try {
24
- const session = await createSession({
25
- apiKey: process.env.FACTORY_API_KEY!,
26
- cwd: process.cwd(),
27
- interactionMode: DroidInteractionMode.Spec,
28
- specModeReasoningEffort: ReasoningEffort.High,
29
- permissionHandler(params) {
30
- const canExitSpec = params.toolUses.some(
31
- (item) => item.details.type === ToolConfirmationType.ExitSpecMode
32
- );
33
- const onlyCreatesFile = params.toolUses.every(
34
- (item) =>
35
- item.details.type === ToolConfirmationType.Create &&
36
- item.details.filePath === outputPath
37
- );
38
-
39
- if (canExitSpec) {
40
- return ToolConfirmationOutcome.ProceedNewSessionHigh;
41
- }
42
- return onlyCreatesFile
43
- ? ToolConfirmationOutcome.ProceedOnce
44
- : ToolConfirmationOutcome.Cancel;
45
- },
46
- });
47
-
48
- try {
49
- for await (const _msg of session.stream(
50
- `Plan then create ${outputPath} containing "Hello from Droid".`
51
- )) {
52
- // Consume the stream until the handoff implementation finishes.
53
- }
54
- } finally {
55
- await session.close();
56
- }
57
-
58
- console.log(await readFile(outputPath, 'utf8'));
59
- } finally {
60
- await rm(tempDir, { recursive: true, force: true });
61
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * Spec mode: approve and implement in the same session.
3
- *
4
- * Usage:
5
- * npx tsx examples/spec-mode-same-session.ts
6
- */
7
-
8
- import { mkdtemp, readFile, rm } from 'node:fs/promises';
9
- import { tmpdir } from 'node:os';
10
- import { join } from 'node:path';
11
-
12
- import {
13
- DroidInteractionMode,
14
- ReasoningEffort,
15
- ToolConfirmationOutcome,
16
- ToolConfirmationType,
17
- createSession,
18
- } from '@factory/droid-sdk';
19
-
20
- const tempDir = await mkdtemp(join(tmpdir(), 'droid-sdk-spec-'));
21
- const outputPath = join(tempDir, 'hello.txt');
22
-
23
- try {
24
- const session = await createSession({
25
- apiKey: process.env.FACTORY_API_KEY!,
26
- cwd: process.cwd(),
27
- interactionMode: DroidInteractionMode.Spec,
28
- specModeReasoningEffort: ReasoningEffort.High,
29
- permissionHandler(params) {
30
- const canExitSpec = params.toolUses.some(
31
- (item) => item.details.type === ToolConfirmationType.ExitSpecMode
32
- );
33
- const onlyCreatesFile = params.toolUses.every(
34
- (item) =>
35
- item.details.type === ToolConfirmationType.Create &&
36
- item.details.filePath === outputPath
37
- );
38
- const onlyEditsTempFile = params.toolUses.every(
39
- (item) =>
40
- item.details.type === ToolConfirmationType.ApplyPatch &&
41
- item.details.filePath === outputPath
42
- );
43
- const onlyRunsTempCommand = params.toolUses.every(
44
- (item) =>
45
- item.details.type === ToolConfirmationType.Execute &&
46
- item.details.fullCommand.includes(outputPath) &&
47
- item.details.fullCommand.includes(tempDir)
48
- );
49
-
50
- return canExitSpec ||
51
- onlyCreatesFile ||
52
- onlyEditsTempFile ||
53
- onlyRunsTempCommand
54
- ? ToolConfirmationOutcome.ProceedOnce
55
- : ToolConfirmationOutcome.Cancel;
56
- },
57
- });
58
-
59
- try {
60
- for await (const _msg of session.stream(
61
- `Plan then create ${outputPath} containing "Hello from Droid".`
62
- )) {
63
- // Consume the stream until implementation finishes.
64
- }
65
- } finally {
66
- await session.close();
67
- }
68
-
69
- console.log(await readFile(outputPath, 'utf8'));
70
- } finally {
71
- await rm(tempDir, { recursive: true, force: true });
72
- }