@factory/droid-sdk 0.1.3 → 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,53 @@
1
+ /**
2
+ * Structured output example.
3
+ *
4
+ * Runs a prompt with the actual Droid CLI and requests a JSON object
5
+ * matching the provided schema.
6
+ *
7
+ * Usage:
8
+ * npx tsx examples/structured-output.ts
9
+ * npx tsx examples/structured-output.ts "Return a TypeScript pioneer"
10
+ */
11
+
12
+ import assert from 'node:assert/strict';
13
+
14
+ import { OutputFormatType, run } from '../src/index.js';
15
+
16
+ async function main(): Promise<void> {
17
+ const prompt =
18
+ process.argv.slice(2).join(' ') ||
19
+ 'Return exactly this person: name Ada Lovelace, language TypeScript.';
20
+
21
+ const outputFormat = {
22
+ type: OutputFormatType.JsonSchema,
23
+ schema: {
24
+ type: 'object',
25
+ properties: {
26
+ name: { type: 'string' },
27
+ language: { type: 'string' },
28
+ },
29
+ required: ['name', 'language'],
30
+ },
31
+ };
32
+
33
+ console.log(`Sending prompt: "${prompt}"\n`);
34
+
35
+ const result = await run(prompt, {
36
+ cwd: process.cwd(),
37
+ outputFormat,
38
+ });
39
+
40
+ assert.ok(result.structuredOutput, 'Expected structuredOutput to be set');
41
+ assert.equal(typeof result.structuredOutput['name'], 'string');
42
+ assert.equal(typeof result.structuredOutput['language'], 'string');
43
+
44
+ console.log('=== Structured output ===');
45
+ console.log(JSON.stringify(result.structuredOutput, null, 2));
46
+
47
+ console.log('\nStructured output example passed');
48
+ }
49
+
50
+ main().catch((error: unknown) => {
51
+ console.error('Structured output example failed:', error);
52
+ process.exit(1);
53
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Compact session example.
3
+ *
4
+ * Demonstrates `session.compactSession()` to summarize and compact
5
+ * session history. Creates a session, sends several messages to build
6
+ * up conversation history, then compacts and verifies newSessionId
7
+ * and removedCount.
8
+ *
9
+ * Usage:
10
+ * npx tsx examples/test-compact.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 prompts = [
21
+ 'Say "one" and nothing else',
22
+ 'Say "two" and nothing else',
23
+ 'Say "three" and nothing else',
24
+ ];
25
+
26
+ for (let i = 0; i < prompts.length; i++) {
27
+ console.log(`=== Turn ${i + 1} ===`);
28
+ console.log(`Prompt: "${prompts[i]}"\n`);
29
+
30
+ for await (const msg of session.stream(prompts[i])) {
31
+ if (msg.type === DroidMessageType.AssistantTextDelta) {
32
+ process.stdout.write(msg.text);
33
+ }
34
+ if (msg.type === DroidMessageType.TurnComplete) {
35
+ console.log('\n');
36
+ }
37
+ }
38
+ }
39
+
40
+ console.log('=== Compacting session ===');
41
+ const compactResult = await session.compactSession({});
42
+ console.log(`Original session: ${session.sessionId}`);
43
+ console.log(`New session: ${compactResult.newSessionId}`);
44
+ console.log(`Removed messages: ${compactResult.removedCount}`);
45
+
46
+ if (compactResult.newSessionId && compactResult.removedCount > 0) {
47
+ console.log('\nCompaction succeeded.');
48
+ } else {
49
+ console.log('\nCompaction returned unexpected results.');
50
+ }
51
+ } finally {
52
+ await session.close();
53
+ console.log('Session closed.');
54
+ }
55
+ }
56
+
57
+ main().catch((err: unknown) => {
58
+ console.error('Error:', err);
59
+ process.exit(1);
60
+ });
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Rewind example (getRewindInfo + executeRewind).
3
+ *
4
+ * Demonstrates the low-level DroidClient API for rewind operations.
5
+ * Uses explicit user message IDs so they can be referenced as rewind
6
+ * targets — the snapshot system uses user message IDs as boundary
7
+ * markers.
8
+ *
9
+ * Usage:
10
+ * npx tsx examples/test-rewind.ts
11
+ */
12
+
13
+ import crypto from 'node:crypto';
14
+
15
+ import {
16
+ DroidClient,
17
+ ProcessTransport,
18
+ AutonomyLevel,
19
+ DroidWorkingState,
20
+ SessionNotificationType,
21
+ ToolConfirmationOutcome,
22
+ } from '../src/index.js';
23
+
24
+ function uuid(): string {
25
+ return crypto.randomUUID();
26
+ }
27
+
28
+ /**
29
+ * Send a user message with an explicit messageId and wait for the
30
+ * agent turn to complete by observing working-state notifications.
31
+ */
32
+ async function sendAndWait(
33
+ client: DroidClient,
34
+ text: string,
35
+ messageId: string
36
+ ): Promise<void> {
37
+ return new Promise<void>((resolve) => {
38
+ let wasActive = false;
39
+
40
+ const unsub = client.onNotification((notification) => {
41
+ const params = notification['params'] as
42
+ | Record<string, unknown>
43
+ | undefined;
44
+ const inner = params?.['notification'] as
45
+ | Record<string, unknown>
46
+ | undefined;
47
+ if (!inner) return;
48
+
49
+ if (inner['type'] === SessionNotificationType.ASSISTANT_TEXT_DELTA) {
50
+ const delta = inner['textDelta'] as string | undefined;
51
+ if (delta) process.stdout.write(delta);
52
+ }
53
+
54
+ if (
55
+ inner['type'] === SessionNotificationType.DROID_WORKING_STATE_CHANGED
56
+ ) {
57
+ const newState = inner['newState'] as string;
58
+ if (newState !== DroidWorkingState.Idle) {
59
+ wasActive = true;
60
+ }
61
+ if (newState === DroidWorkingState.Idle && wasActive) {
62
+ unsub();
63
+ resolve();
64
+ }
65
+ }
66
+ });
67
+
68
+ void client.addUserMessage({ text, messageId });
69
+ });
70
+ }
71
+
72
+ async function main(): Promise<void> {
73
+ console.log('=== Creating session ===');
74
+ const transport = new ProcessTransport({ cwd: process.cwd() });
75
+ await transport.connect();
76
+ const client = new DroidClient({ transport });
77
+
78
+ const initResult = await client.initializeSession({
79
+ machineId: 'default',
80
+ cwd: process.cwd(),
81
+ autonomyLevel: AutonomyLevel.High,
82
+ });
83
+ console.log(`Session ID: ${initResult.sessionId}\n`);
84
+
85
+ client.setPermissionHandler(() => ToolConfirmationOutcome.ProceedOnce);
86
+
87
+ const userMsgId1 = uuid();
88
+ console.log(`=== Turn 1: "hello" (msgId: ${userMsgId1.slice(0, 12)}...) ===`);
89
+ await sendAndWait(client, 'Say "hello" and nothing else.', userMsgId1);
90
+ console.log('\n');
91
+
92
+ const userMsgId2 = uuid();
93
+ console.log(
94
+ `=== Turn 2: create file (msgId: ${userMsgId2.slice(0, 12)}...) ===`
95
+ );
96
+ await sendAndWait(
97
+ client,
98
+ 'Create a file called /tmp/droid-sdk-rewind-test.txt with the content ' +
99
+ '"rewind test data". Do not explain, just create it.',
100
+ userMsgId2
101
+ );
102
+ console.log('\n');
103
+
104
+ console.log(`=== getRewindInfo (msgId: ${userMsgId2.slice(0, 12)}...) ===`);
105
+ try {
106
+ const rewindInfo = await client.getRewindInfo({ messageId: userMsgId2 });
107
+ console.log(
108
+ ` Available files to restore: ${rewindInfo.availableFiles.length}`
109
+ );
110
+ for (const f of rewindInfo.availableFiles) {
111
+ console.log(` - ${f.filePath} (${f.size} bytes)`);
112
+ }
113
+ console.log(` Created files to delete: ${rewindInfo.createdFiles.length}`);
114
+ for (const f of rewindInfo.createdFiles) {
115
+ console.log(` - ${f.filePath}`);
116
+ }
117
+ console.log(` Evicted files: ${rewindInfo.evictedFiles.length}`);
118
+ } catch (err) {
119
+ console.log(
120
+ ` Error (expected — server does not yet support this): ${err instanceof Error ? err.message : String(err)}`
121
+ );
122
+ }
123
+ console.log();
124
+
125
+ console.log(
126
+ `=== executeRewind (to msgId: ${userMsgId1.slice(0, 12)}...) ===`
127
+ );
128
+ try {
129
+ const rewindResult = await client.executeRewind({
130
+ messageId: userMsgId1,
131
+ filesToRestore: [],
132
+ filesToDelete: [],
133
+ forkTitle: 'Rewind to first hello',
134
+ });
135
+ console.log(` New session ID: ${rewindResult.newSessionId}`);
136
+ console.log(` Restored: ${rewindResult.restoredCount}`);
137
+ console.log(` Deleted: ${rewindResult.deletedCount}`);
138
+ console.log(` Failed restores: ${rewindResult.failedRestoreCount}`);
139
+ console.log(` Failed deletes: ${rewindResult.failedDeleteCount}`);
140
+ } catch (err) {
141
+ console.log(
142
+ ` Error (expected — server does not yet support this): ${err instanceof Error ? err.message : String(err)}`
143
+ );
144
+ }
145
+ console.log();
146
+
147
+ console.log('=== Closing ===');
148
+ await client.close();
149
+ console.log('Done.');
150
+ }
151
+
152
+ main().catch((err: unknown) => {
153
+ console.error('Fatal error:', err);
154
+ process.exit(1);
155
+ });
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Tool controls example.
3
+ *
4
+ * Demonstrates:
5
+ * - configuring enabled and disabled tool IDs at session creation
6
+ * - inspecting the current tool catalog with `listTools()`
7
+ * - updating tool overrides with `updateSettings()`
8
+ *
9
+ * Usage:
10
+ * npx tsx examples/tool-controls.ts
11
+ */
12
+
13
+ import { createSession } from '../src/index.js';
14
+ import type { ExecToolInfo } from '../src/index.js';
15
+
16
+ function printToolState(label: string, tool: ExecToolInfo | undefined): void {
17
+ if (!tool) {
18
+ console.log(`${label}: not present in tool catalog`);
19
+ return;
20
+ }
21
+
22
+ console.log(
23
+ `${label}: defaultAllowed=${tool.defaultAllowed}, currentlyAllowed=${tool.currentlyAllowed}`
24
+ );
25
+ }
26
+
27
+ async function main(): Promise<void> {
28
+ const session = await createSession({
29
+ cwd: process.cwd(),
30
+ enabledToolIds: ['Read', 'Glob', 'Grep'],
31
+ disabledToolIds: ['Execute'],
32
+ });
33
+
34
+ try {
35
+ console.log(`Session created: ${session.sessionId}\n`);
36
+
37
+ const initial = await session.listTools();
38
+ const initialRead = initial.tools.find((tool) => tool.llmId === 'Read');
39
+ const initialExecute = initial.tools.find(
40
+ (tool) => tool.llmId === 'Execute'
41
+ );
42
+
43
+ console.log('=== Initial tool state ===');
44
+ printToolState('Read', initialRead);
45
+ printToolState('Execute', initialExecute);
46
+
47
+ await session.updateSettings({
48
+ disabledToolIds: ['Read', 'Execute'],
49
+ });
50
+
51
+ const updated = await session.listTools();
52
+ const updatedRead = updated.tools.find((tool) => tool.llmId === 'Read');
53
+ const updatedExecute = updated.tools.find(
54
+ (tool) => tool.llmId === 'Execute'
55
+ );
56
+
57
+ console.log('\n=== Updated tool state ===');
58
+ printToolState('Read', updatedRead);
59
+ printToolState('Execute', updatedExecute);
60
+ } finally {
61
+ await session.close();
62
+ console.log('\nSession closed.');
63
+ }
64
+ }
65
+
66
+ main().catch((err: unknown) => {
67
+ console.error('Error:', err);
68
+ process.exit(1);
69
+ });
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "@factory/droid-sdk",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Factory Droid CLI",
6
- "keywords": ["factory", "droid", "cli", "ai", "agent", "sdk"],
6
+ "keywords": [
7
+ "factory",
8
+ "droid",
9
+ "cli",
10
+ "ai",
11
+ "agent",
12
+ "sdk"
13
+ ],
7
14
  "license": "Apache-2.0",
8
15
  "type": "module",
9
16
  "main": "./dist/index.cjs",
@@ -23,16 +30,22 @@
23
30
  },
24
31
  "files": [
25
32
  "dist",
33
+ "docs",
34
+ "examples",
26
35
  "README.md",
27
36
  "LICENSE"
28
37
  ],
29
38
  "scripts": {
30
39
  "build": "tsup",
40
+ "prepack": "npm run build",
31
41
  "typecheck": "tsc --noEmit",
42
+ "typecheck:examples": "tsc --noEmit -p tsconfig.examples.json",
32
43
  "test": "vitest run",
33
44
  "test:coverage": "vitest run --coverage",
34
- "lint": "eslint src/",
35
- "fix": "eslint --fix src/"
45
+ "lint": "eslint src/ tests/ examples/",
46
+ "fix": "eslint --fix src/ tests/ examples/",
47
+ "format": "prettier --write .",
48
+ "format:check": "prettier --check ."
36
49
  },
37
50
  "repository": {
38
51
  "type": "git",
@@ -42,6 +55,7 @@
42
55
  "node": ">=18.0.0"
43
56
  },
44
57
  "dependencies": {
58
+ "@modelcontextprotocol/sdk": "^1.29.0",
45
59
  "uuid": "^11.1.0",
46
60
  "zod": "^3.24.0"
47
61
  },
@@ -51,9 +65,17 @@
51
65
  "@types/uuid": "^10.0.0",
52
66
  "@vitest/coverage-v8": "^3.1.0",
53
67
  "eslint": "^9.24.0",
68
+ "eslint-import-resolver-typescript": "^4.4.4",
69
+ "eslint-plugin-import": "^2.32.0",
70
+ "eslint-plugin-unused-imports": "^4.4.1",
71
+ "prettier": "^3.8.1",
54
72
  "tsup": "^8.4.0",
55
73
  "typescript": "^5.8.0",
56
74
  "typescript-eslint": "^8.57.2",
57
75
  "vitest": "^3.1.0"
76
+ },
77
+ "homepage": "https://github.com/Factory-AI/droid-sdk-typescript#readme",
78
+ "bugs": {
79
+ "url": "https://github.com/Factory-AI/droid-sdk-typescript/issues"
58
80
  }
59
81
  }