@factory/droid-sdk 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -82
- package/dist/index.cjs +2345 -692
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2570 -232
- package/dist/index.d.ts +2570 -232
- package/dist/index.js +2298 -697
- package/dist/index.js.map +1 -1
- package/docs/daemon-usage-guide.md +544 -0
- package/docs/sdk-usage-guide.md +748 -0
- package/examples/abort-session-stream.ts +23 -56
- package/examples/daemon-multi-session.ts +67 -0
- package/examples/droid-dev-structured-output.ts +5 -5
- package/examples/fork-session.ts +33 -7
- package/examples/hook-execution.ts +68 -0
- package/examples/init-metadata.ts +14 -66
- package/examples/interrupt-session.ts +23 -55
- package/examples/list-sessions.ts +9 -55
- package/examples/multi-turn-session.ts +27 -54
- package/examples/permission-handler.ts +27 -84
- package/examples/readme-structured-output.ts +26 -0
- package/examples/result-metadata.ts +2 -1
- package/examples/run.ts +2 -1
- package/examples/sdk-mcp-tool.ts +14 -8
- package/examples/session-stream.ts +57 -0
- package/examples/spec-mode-new-session.ts +39 -99
- package/examples/spec-mode-same-session.ts +50 -111
- package/examples/structured-output.ts +17 -10
- package/examples/test-compact.ts +9 -4
- package/examples/test-rewind.ts +49 -117
- package/examples/tool-controls.ts +23 -55
- package/package.json +3 -1
- package/docs/examples/init-metadata.md +0 -79
- package/docs/examples/list-sessions.md +0 -64
- package/docs/examples/multi-turn-session.md +0 -77
- package/docs/examples/one-shot-query.md +0 -73
- package/docs/examples/permission-handler.md +0 -102
- package/docs/examples/run.md +0 -52
- package/docs/examples/spec-mode.md +0 -101
- package/docs/examples/tool-controls.md +0 -90
- package/docs/typescript-sdk-reference.md +0 -372
- package/examples/simple-query.ts +0 -53
|
@@ -6,43 +6,50 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
8
|
* npx tsx examples/structured-output.ts
|
|
9
|
-
* npx tsx examples/structured-output.ts "
|
|
9
|
+
* npx tsx examples/structured-output.ts "Pick a favorite number between 1 and 42"
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import assert from 'node:assert/strict';
|
|
13
13
|
|
|
14
|
-
import { OutputFormatType, run } from '
|
|
14
|
+
import { OutputFormatType, run } from '@factory/droid-sdk';
|
|
15
|
+
|
|
16
|
+
type FavoriteNumber = { favoriteNumber: number };
|
|
15
17
|
|
|
16
18
|
async function main(): Promise<void> {
|
|
17
19
|
const prompt =
|
|
18
20
|
process.argv.slice(2).join(' ') ||
|
|
19
|
-
'
|
|
21
|
+
'Pick a favorite number between 1 and 42.';
|
|
20
22
|
|
|
21
23
|
const outputFormat = {
|
|
22
24
|
type: OutputFormatType.JsonSchema,
|
|
23
25
|
schema: {
|
|
24
26
|
type: 'object',
|
|
25
27
|
properties: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
favoriteNumber: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
minimum: 1,
|
|
31
|
+
maximum: 42,
|
|
32
|
+
},
|
|
28
33
|
},
|
|
29
|
-
required: ['
|
|
34
|
+
required: ['favoriteNumber'],
|
|
30
35
|
},
|
|
31
36
|
};
|
|
32
37
|
|
|
33
38
|
console.log(`Sending prompt: "${prompt}"\n`);
|
|
34
39
|
|
|
35
40
|
const result = await run(prompt, {
|
|
41
|
+
apiKey: process.env.FACTORY_API_KEY!,
|
|
36
42
|
cwd: process.cwd(),
|
|
37
43
|
outputFormat,
|
|
38
44
|
});
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
assert.
|
|
46
|
+
const structuredOutput = result.structuredOutput as FavoriteNumber | null;
|
|
47
|
+
|
|
48
|
+
assert.ok(structuredOutput, 'Expected structuredOutput to be set');
|
|
49
|
+
assert.equal(typeof structuredOutput.favoriteNumber, 'number');
|
|
43
50
|
|
|
44
51
|
console.log('=== Structured output ===');
|
|
45
|
-
console.log(JSON.stringify(
|
|
52
|
+
console.log(JSON.stringify(structuredOutput, null, 2));
|
|
46
53
|
|
|
47
54
|
console.log('\nStructured output example passed');
|
|
48
55
|
}
|
package/examples/test-compact.ts
CHANGED
|
@@ -10,10 +10,13 @@
|
|
|
10
10
|
* npx tsx examples/test-compact.ts
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createSession, DroidMessageType } from '
|
|
13
|
+
import { createSession, DroidMessageType } from '@factory/droid-sdk';
|
|
14
14
|
|
|
15
15
|
async function main(): Promise<void> {
|
|
16
|
-
const session = await createSession({
|
|
16
|
+
const session = await createSession({
|
|
17
|
+
apiKey: process.env.FACTORY_API_KEY!,
|
|
18
|
+
cwd: process.cwd(),
|
|
19
|
+
});
|
|
17
20
|
try {
|
|
18
21
|
console.log(`Session created: ${session.sessionId}\n`);
|
|
19
22
|
|
|
@@ -27,11 +30,13 @@ async function main(): Promise<void> {
|
|
|
27
30
|
console.log(`=== Turn ${i + 1} ===`);
|
|
28
31
|
console.log(`Prompt: "${prompts[i]}"\n`);
|
|
29
32
|
|
|
30
|
-
for await (const msg of session.stream(prompts[i]
|
|
33
|
+
for await (const msg of session.stream(prompts[i], {
|
|
34
|
+
includePartialMessages: true,
|
|
35
|
+
})) {
|
|
31
36
|
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
32
37
|
process.stdout.write(msg.text);
|
|
33
38
|
}
|
|
34
|
-
if (msg.type === DroidMessageType.
|
|
39
|
+
if (msg.type === DroidMessageType.Result) {
|
|
35
40
|
console.log('\n');
|
|
36
41
|
}
|
|
37
42
|
}
|
package/examples/test-rewind.ts
CHANGED
|
@@ -1,155 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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.
|
|
2
|
+
* Low-level rewind API example.
|
|
8
3
|
*
|
|
9
4
|
* Usage:
|
|
10
5
|
* npx tsx examples/test-rewind.ts
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
|
-
import
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
14
9
|
|
|
15
10
|
import {
|
|
16
|
-
DroidClient,
|
|
17
|
-
ProcessTransport,
|
|
18
11
|
AutonomyLevel,
|
|
12
|
+
DroidClient,
|
|
19
13
|
DroidWorkingState,
|
|
14
|
+
ProcessTransport,
|
|
20
15
|
SessionNotificationType,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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;
|
|
16
|
+
} from '@factory/droid-sdk';
|
|
17
|
+
|
|
18
|
+
function waitForTurn(client: DroidClient): Promise<void> {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
let started = false;
|
|
21
|
+
const unsubscribe = client.onNotification((notification) => {
|
|
22
|
+
const params = notification['params'];
|
|
23
|
+
if (typeof params !== 'object' || params === null) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
48
26
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
const event = (params as Record<string, unknown>)['notification'];
|
|
28
|
+
if (typeof event !== 'object' || event === null) {
|
|
29
|
+
return;
|
|
52
30
|
}
|
|
53
31
|
|
|
54
32
|
if (
|
|
55
|
-
|
|
33
|
+
(event as Record<string, unknown>)['type'] !==
|
|
34
|
+
SessionNotificationType.DROID_WORKING_STATE_CHANGED
|
|
56
35
|
) {
|
|
57
|
-
|
|
58
|
-
if (newState !== DroidWorkingState.Idle) {
|
|
59
|
-
wasActive = true;
|
|
60
|
-
}
|
|
61
|
-
if (newState === DroidWorkingState.Idle && wasActive) {
|
|
62
|
-
unsub();
|
|
63
|
-
resolve();
|
|
64
|
-
}
|
|
36
|
+
return;
|
|
65
37
|
}
|
|
66
|
-
});
|
|
67
38
|
|
|
68
|
-
|
|
39
|
+
const newState = (event as Record<string, unknown>)['newState'];
|
|
40
|
+
started ||= newState !== DroidWorkingState.Idle;
|
|
41
|
+
if (started && newState === DroidWorkingState.Idle) {
|
|
42
|
+
unsubscribe();
|
|
43
|
+
resolve();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
69
46
|
});
|
|
70
47
|
}
|
|
71
48
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const client = new DroidClient({ transport });
|
|
49
|
+
const transport = new ProcessTransport({ cwd: process.cwd() });
|
|
50
|
+
await transport.connect();
|
|
51
|
+
|
|
52
|
+
const client = new DroidClient({ transport });
|
|
77
53
|
|
|
78
|
-
|
|
54
|
+
try {
|
|
55
|
+
await client.initializeSession({
|
|
79
56
|
machineId: 'default',
|
|
80
57
|
cwd: process.cwd(),
|
|
81
58
|
autonomyLevel: AutonomyLevel.High,
|
|
82
59
|
});
|
|
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
60
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
'"rewind test data". Do not explain, just create it.',
|
|
100
|
-
userMsgId2
|
|
101
|
-
);
|
|
102
|
-
console.log('\n');
|
|
61
|
+
const messageId = randomUUID();
|
|
62
|
+
const done = waitForTurn(client);
|
|
63
|
+
await client.addUserMessage({
|
|
64
|
+
messageId,
|
|
65
|
+
text: 'Say "hello" and nothing else.',
|
|
66
|
+
});
|
|
67
|
+
await done;
|
|
103
68
|
|
|
104
|
-
console.log(`=== getRewindInfo (msgId: ${userMsgId2.slice(0, 12)}...) ===`);
|
|
105
69
|
try {
|
|
106
|
-
const rewindInfo = await client.getRewindInfo({ messageId
|
|
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) {
|
|
70
|
+
const rewindInfo = await client.getRewindInfo({ messageId });
|
|
119
71
|
console.log(
|
|
120
|
-
`
|
|
72
|
+
`Files available to restore: ${rewindInfo.availableFiles.length}`
|
|
121
73
|
);
|
|
122
|
-
}
|
|
123
|
-
console.log();
|
|
124
74
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
try {
|
|
129
|
-
const rewindResult = await client.executeRewind({
|
|
130
|
-
messageId: userMsgId1,
|
|
75
|
+
const rewind = await client.executeRewind({
|
|
76
|
+
messageId,
|
|
131
77
|
filesToRestore: [],
|
|
132
78
|
filesToDelete: [],
|
|
133
|
-
forkTitle: 'Rewind
|
|
79
|
+
forkTitle: 'Rewind example',
|
|
134
80
|
});
|
|
135
|
-
console.log(`
|
|
136
|
-
|
|
137
|
-
console.log(`
|
|
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
|
-
);
|
|
81
|
+
console.log(`Rewound into session: ${rewind.newSessionId}`);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.log(`Rewind unavailable: ${String(error)}`);
|
|
144
84
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
console.log('=== Closing ===');
|
|
85
|
+
} finally {
|
|
148
86
|
await client.close();
|
|
149
|
-
console.log('Done.');
|
|
150
87
|
}
|
|
151
|
-
|
|
152
|
-
main().catch((err: unknown) => {
|
|
153
|
-
console.error('Fatal error:', err);
|
|
154
|
-
process.exit(1);
|
|
155
|
-
});
|
|
@@ -1,69 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool controls example.
|
|
3
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
4
|
* Usage:
|
|
10
5
|
* npx tsx examples/tool-controls.ts
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
|
-
import { createSession } from '
|
|
14
|
-
import type { ExecToolInfo } from '../src/index.js';
|
|
8
|
+
import { createSession } from '@factory/droid-sdk';
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const session = await createSession({
|
|
11
|
+
apiKey: process.env.FACTORY_API_KEY!,
|
|
12
|
+
cwd: process.cwd(),
|
|
13
|
+
enabledToolIds: ['Read', 'Grep'],
|
|
14
|
+
disabledToolIds: ['Execute'],
|
|
15
|
+
});
|
|
21
16
|
|
|
17
|
+
try {
|
|
18
|
+
const before = await session.listTools();
|
|
22
19
|
console.log(
|
|
23
|
-
|
|
20
|
+
before.tools
|
|
21
|
+
.filter((tool) => ['Read', 'Execute'].includes(tool.llmId))
|
|
22
|
+
.map((tool) => `${tool.llmId}: ${String(tool.currentlyAllowed)}`)
|
|
23
|
+
.join('\n')
|
|
24
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
25
|
|
|
43
|
-
|
|
44
|
-
printToolState('Read', initialRead);
|
|
45
|
-
printToolState('Execute', initialExecute);
|
|
26
|
+
await session.updateSettings({ disabledToolIds: ['Read', 'Execute'] });
|
|
46
27
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
}
|
|
28
|
+
const after = await session.listTools();
|
|
29
|
+
console.log(
|
|
30
|
+
after.tools
|
|
31
|
+
.filter((tool) => ['Read', 'Execute'].includes(tool.llmId))
|
|
32
|
+
.map((tool) => `${tool.llmId}: ${String(tool.currentlyAllowed)}`)
|
|
33
|
+
.join('\n')
|
|
34
|
+
);
|
|
35
|
+
} finally {
|
|
36
|
+
await session.close();
|
|
64
37
|
}
|
|
65
|
-
|
|
66
|
-
main().catch((err: unknown) => {
|
|
67
|
-
console.error('Error:', err);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@factory/droid-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TypeScript SDK for the Factory Droid CLI",
|
|
6
6
|
"keywords": [
|
|
@@ -57,12 +57,14 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
59
59
|
"uuid": "^11.1.0",
|
|
60
|
+
"ws": "^8.21.0",
|
|
60
61
|
"zod": "^3.24.0"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@eslint/js": "^9.39.4",
|
|
64
65
|
"@types/node": "^22.15.0",
|
|
65
66
|
"@types/uuid": "^10.0.0",
|
|
67
|
+
"@types/ws": "^8.18.1",
|
|
66
68
|
"@vitest/coverage-v8": "^3.1.0",
|
|
67
69
|
"eslint": "^9.24.0",
|
|
68
70
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# Example: initialization metadata
|
|
2
|
-
|
|
3
|
-
Use this when you need the raw initialization payload, early session IDs, or model/settings metadata.
|
|
4
|
-
|
|
5
|
-
## What this example shows
|
|
6
|
-
|
|
7
|
-
- reading `query().sessionId` and `query().initResult`
|
|
8
|
-
- waiting for `query().initialized`
|
|
9
|
-
- reading `session.initResult` from `createSession()` and `resumeSession()`
|
|
10
|
-
|
|
11
|
-
## Key snippet: inspect a query before and after initialization
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
const stream = query({
|
|
15
|
-
prompt: 'Reply with "ready" and nothing else.',
|
|
16
|
-
cwd: process.cwd(),
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
console.log(stream.sessionId);
|
|
20
|
-
console.log(stream.initResult);
|
|
21
|
-
|
|
22
|
-
const initialized = await stream.initialized;
|
|
23
|
-
console.log(initialized.sessionId);
|
|
24
|
-
console.log(initialized.settings.modelId);
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Before initialization completes, `sessionId` and `initResult` are `null`.
|
|
28
|
-
|
|
29
|
-
## Key snippet: read metadata from sessions
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const session = await createSession({ cwd: process.cwd() });
|
|
33
|
-
const resumed = await resumeSession(session.sessionId, { cwd: process.cwd() });
|
|
34
|
-
|
|
35
|
-
console.log(session.initResult.settings.modelId);
|
|
36
|
-
console.log(resumed.initResult.cwd);
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Full script
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
import { createSession, query, resumeSession } from '@factory/droid-sdk';
|
|
43
|
-
|
|
44
|
-
async function main(): Promise<void> {
|
|
45
|
-
const stream = query({
|
|
46
|
-
prompt: 'Reply with "ready" and nothing else.',
|
|
47
|
-
cwd: process.cwd(),
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
console.log(stream.sessionId);
|
|
51
|
-
console.log(stream.initResult);
|
|
52
|
-
|
|
53
|
-
const initialized = await stream.initialized;
|
|
54
|
-
console.log(initialized.sessionId);
|
|
55
|
-
console.log(initialized.settings.modelId);
|
|
56
|
-
|
|
57
|
-
stream.abort();
|
|
58
|
-
|
|
59
|
-
const session = await createSession({ cwd: process.cwd() });
|
|
60
|
-
let resumed: Awaited<ReturnType<typeof resumeSession>> | null = null;
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
resumed = await resumeSession(session.sessionId, {
|
|
64
|
-
cwd: process.cwd(),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
console.log(session.initResult.settings.modelId);
|
|
68
|
-
console.log(resumed.initResult.cwd);
|
|
69
|
-
} finally {
|
|
70
|
-
await resumed?.close();
|
|
71
|
-
await session.close();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
main().catch((err: unknown) => {
|
|
76
|
-
console.error('Error:', err);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
});
|
|
79
|
-
```
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Example: list saved sessions
|
|
2
|
-
|
|
3
|
-
Use `listSessions()` when you need recent Droid session history without launching a subprocess.
|
|
4
|
-
|
|
5
|
-
## What this example shows
|
|
6
|
-
|
|
7
|
-
- listing recent sessions for the current project
|
|
8
|
-
- listing recent sessions across all projects
|
|
9
|
-
- formatting returned `SessionMetadata`
|
|
10
|
-
|
|
11
|
-
## Key snippet: project-scoped listing
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
const currentProject = await listSessions({ numSessions: 10 });
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Key snippet: cross-project listing
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
const allSessions = await listSessions({
|
|
21
|
-
fetchOutsideCWD: true,
|
|
22
|
-
numSessions: 5,
|
|
23
|
-
});
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
This helper reads local session files directly and returns `SessionMetadata[]`.
|
|
27
|
-
|
|
28
|
-
## Full script
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
import { listSessions } from '@factory/droid-sdk';
|
|
32
|
-
|
|
33
|
-
function formatDate(date: Date): string {
|
|
34
|
-
return date.toISOString().replace('T', ' ').slice(0, 19);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function main(): Promise<void> {
|
|
38
|
-
const currentProject = await listSessions({ numSessions: 10 });
|
|
39
|
-
|
|
40
|
-
for (const session of currentProject) {
|
|
41
|
-
const title = session.sessionTitle ?? session.title ?? '(untitled)';
|
|
42
|
-
console.log(
|
|
43
|
-
`[${session.id.slice(0, 8)}] ${formatDate(session.modifiedTime)} ${session.messageCount} msgs — ${title}`
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const allSessions = await listSessions({
|
|
48
|
-
fetchOutsideCWD: true,
|
|
49
|
-
numSessions: 5,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
for (const session of allSessions) {
|
|
53
|
-
const title = session.sessionTitle ?? session.title ?? '(untitled)';
|
|
54
|
-
console.log(
|
|
55
|
-
`[${session.id.slice(0, 8)}] ${formatDate(session.modifiedTime)} — ${title}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
main().catch((err: unknown) => {
|
|
61
|
-
console.error('Error:', err);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
});
|
|
64
|
-
```
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# Example: multi-turn session
|
|
2
|
-
|
|
3
|
-
Use `createSession()` when you want conversation state to persist across turns.
|
|
4
|
-
|
|
5
|
-
## What this example shows
|
|
6
|
-
|
|
7
|
-
- creating a persistent session
|
|
8
|
-
- streaming one turn with `session.stream()`
|
|
9
|
-
- collecting a later turn with `session.send()`
|
|
10
|
-
- closing the session cleanly
|
|
11
|
-
|
|
12
|
-
## Key snippet: create the session
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
const session = await createSession({ cwd: process.cwd() });
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Create the session once and reuse it across prompts.
|
|
19
|
-
|
|
20
|
-
## Key snippet: stream a turn
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
import { DroidMessageType } from '@factory/droid-sdk';
|
|
24
|
-
|
|
25
|
-
for await (const msg of session.stream(
|
|
26
|
-
'List the TypeScript files in this project'
|
|
27
|
-
)) {
|
|
28
|
-
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
29
|
-
process.stdout.write(msg.text);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Use `stream()` when you want incremental output.
|
|
35
|
-
|
|
36
|
-
## Key snippet: collect a full response
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
const result = await session.send('Summarize the project in one sentence');
|
|
40
|
-
console.log(result.text);
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Use `send()` when you want the SDK to aggregate the turn for you.
|
|
44
|
-
|
|
45
|
-
## Full script
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
import { createSession, DroidMessageType } from '@factory/droid-sdk';
|
|
49
|
-
|
|
50
|
-
async function main(): Promise<void> {
|
|
51
|
-
const session = await createSession({ cwd: process.cwd() });
|
|
52
|
-
|
|
53
|
-
console.log(`Session created: ${session.sessionId}\n`);
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
for await (const msg of session.stream(
|
|
57
|
-
'List the TypeScript files in this project'
|
|
58
|
-
)) {
|
|
59
|
-
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
60
|
-
process.stdout.write(msg.text);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
console.log('\n');
|
|
65
|
-
|
|
66
|
-
const result = await session.send('Summarize the project in one sentence');
|
|
67
|
-
console.log(result.text);
|
|
68
|
-
} finally {
|
|
69
|
-
await session.close();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
main().catch((err: unknown) => {
|
|
74
|
-
console.error('Error:', err);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
});
|
|
77
|
-
```
|