@factory/droid-sdk 0.2.0 → 0.3.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 +734 -611
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1649 -206
- package/dist/index.d.ts +1649 -206
- package/dist/index.js +729 -624
- package/dist/index.js.map +1 -1
- package/docs/sdk-usage-guide.md +678 -0
- package/examples/abort-session-stream.ts +19 -55
- package/examples/droid-dev-structured-output.ts +4 -5
- package/examples/fork-session.ts +25 -6
- package/examples/hook-execution.ts +62 -0
- package/examples/init-metadata.ts +9 -66
- package/examples/interrupt-session.ts +19 -54
- package/examples/list-sessions.ts +9 -55
- package/examples/multi-turn-session.ts +21 -53
- package/examples/permission-handler.ts +26 -84
- package/examples/readme-structured-output.ts +25 -0
- package/examples/result-metadata.ts +1 -1
- package/examples/run.ts +1 -1
- package/examples/sdk-mcp-tool.ts +11 -7
- package/examples/session-stream.ts +54 -0
- package/examples/spec-mode-new-session.ts +38 -99
- package/examples/spec-mode-same-session.ts +49 -111
- package/examples/structured-output.ts +16 -10
- package/examples/test-compact.ts +5 -3
- package/examples/test-rewind.ts +49 -117
- package/examples/tool-controls.ts +22 -55
- package/package.json +1 -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
|
@@ -1,67 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Starts a long-running streaming turn, aborts it after a short delay, then
|
|
5
|
-
* verifies the stream rejects with the abort reason.
|
|
2
|
+
* Abort a running turn with AbortController.
|
|
6
3
|
*
|
|
7
4
|
* Usage:
|
|
8
5
|
* npx tsx examples/abort-session-stream.ts
|
|
9
|
-
* npx tsx examples/abort-session-stream.ts 3000
|
|
10
6
|
*/
|
|
11
7
|
|
|
12
|
-
import { createSession
|
|
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);
|
|
8
|
+
import { createSession } from '@factory/droid-sdk';
|
|
28
9
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
const session = await createSession({ cwd: process.cwd() });
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
const timeout = setTimeout(
|
|
13
|
+
() => controller.abort(new Error('Stopped by AbortController')),
|
|
14
|
+
1000
|
|
15
|
+
);
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for await (const message of session.stream(prompt, {
|
|
17
|
+
try {
|
|
18
|
+
for await (const _msg of session.stream(
|
|
19
|
+
'Write a long explanation of how compilers work.',
|
|
20
|
+
{
|
|
38
21
|
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
22
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
} finally {
|
|
58
|
-
clearTimeout(timeout);
|
|
59
|
-
await session.close();
|
|
60
|
-
console.log('Session closed.');
|
|
23
|
+
)) {
|
|
24
|
+
void _msg;
|
|
61
25
|
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
28
|
+
} finally {
|
|
29
|
+
clearTimeout(timeout);
|
|
30
|
+
await session.close();
|
|
62
31
|
}
|
|
63
|
-
|
|
64
|
-
main().catch((err: unknown) => {
|
|
65
|
-
console.error('Error:', err);
|
|
66
|
-
process.exit(1);
|
|
67
|
-
});
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One-off integration smoke test for JSON output against
|
|
2
|
+
* One-off integration smoke test for JSON output against Droid.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
5
|
* npx tsx examples/droid-dev-structured-output.ts
|
|
6
|
-
* DROID_EXEC_PATH=/path/to/droid
|
|
6
|
+
* DROID_EXEC_PATH=/path/to/droid npx tsx examples/droid-dev-structured-output.ts
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { run } from '@factory/droid-sdk';
|
|
9
10
|
import { z } from 'zod';
|
|
10
11
|
|
|
11
|
-
import { run } from '../src/index.js';
|
|
12
|
-
|
|
13
12
|
const PersonSchema = z.object({
|
|
14
13
|
name: z.literal('Ada Lovelace'),
|
|
15
14
|
language: z.literal('TypeScript'),
|
|
@@ -17,7 +16,7 @@ const PersonSchema = z.object({
|
|
|
17
16
|
});
|
|
18
17
|
|
|
19
18
|
async function main(): Promise<void> {
|
|
20
|
-
const execPath = process.env['DROID_EXEC_PATH'] ?? 'droid
|
|
19
|
+
const execPath = process.env['DROID_EXEC_PATH'] ?? 'droid';
|
|
21
20
|
|
|
22
21
|
const result = await run(
|
|
23
22
|
[
|
package/examples/fork-session.ts
CHANGED
|
@@ -10,7 +10,24 @@
|
|
|
10
10
|
* npx tsx examples/fork-session.ts
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
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
|
+
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
26
|
+
text += msg.text;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
14
31
|
|
|
15
32
|
async function main(): Promise<void> {
|
|
16
33
|
const session = await createSession({ cwd: process.cwd() });
|
|
@@ -18,17 +35,19 @@ async function main(): Promise<void> {
|
|
|
18
35
|
|
|
19
36
|
try {
|
|
20
37
|
console.log(`Original session: ${session.sessionId}\n`);
|
|
21
|
-
|
|
22
|
-
await session.send('Remember this phrase exactly: mango sunrise');
|
|
38
|
+
await streamText(session, 'Remember this phrase exactly: mango sunrise');
|
|
23
39
|
|
|
24
40
|
const { newSessionId } = await session.forkSession();
|
|
25
41
|
console.log(`Forked session: ${newSessionId}\n`);
|
|
26
42
|
|
|
27
|
-
fork = await resumeSession(newSessionId
|
|
43
|
+
fork = await resumeSession(newSessionId);
|
|
28
44
|
|
|
29
|
-
const result = await
|
|
45
|
+
const result = await streamText(
|
|
46
|
+
fork,
|
|
47
|
+
'What phrase did I ask you to remember?'
|
|
48
|
+
);
|
|
30
49
|
console.log('Fork response:');
|
|
31
|
-
console.log(result
|
|
50
|
+
console.log(result);
|
|
32
51
|
} finally {
|
|
33
52
|
await fork?.close();
|
|
34
53
|
await session.close();
|
|
@@ -0,0 +1,62 @@
|
|
|
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 session = await createSession({ cwd: process.cwd() });
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
for await (const msg of session.stream(prompt)) {
|
|
22
|
+
switch (msg.type) {
|
|
23
|
+
case DroidMessageType.Assistant:
|
|
24
|
+
process.stdout.write(msg.text);
|
|
25
|
+
break;
|
|
26
|
+
|
|
27
|
+
case DroidMessageType.ToolCall:
|
|
28
|
+
console.log(`\n[Tool Call] ${msg.toolUse.name} (${msg.toolUse.id})`);
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
case DroidMessageType.Hook:
|
|
32
|
+
if (msg.status === 'started') {
|
|
33
|
+
console.log(
|
|
34
|
+
` [Hook Started] ID: ${msg.hookId}, Event: ${msg.eventName}, Command: ${msg.command}`
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
console.log(
|
|
38
|
+
` [Hook ${msg.status}] ID: ${msg.hookId}, Exit Code: ${msg.exitCode}`
|
|
39
|
+
);
|
|
40
|
+
if (msg.stdout) console.log(` stdout: ${msg.stdout.trim()}`);
|
|
41
|
+
if (msg.stderr) console.log(` stderr: ${msg.stderr.trim()}`);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
|
|
45
|
+
case DroidMessageType.ToolResult:
|
|
46
|
+
console.log(`[Tool Result] ${msg.isError ? 'Error' : 'OK'}`);
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case DroidMessageType.Result:
|
|
50
|
+
console.log('\n\n--- Turn complete ---');
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} finally {
|
|
55
|
+
await session.close();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
main().catch((err: unknown) => {
|
|
60
|
+
console.error('Error:', err);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
|
@@ -1,76 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Initialization metadata example.
|
|
3
3
|
*
|
|
4
|
-
* Demonstrates:
|
|
5
|
-
* - `query().sessionId`, `query().initResult`, and `query().initialized`
|
|
6
|
-
* - `session.initResult` from `createSession()`
|
|
7
|
-
* - `resumed.initResult` from `resumeSession()`
|
|
8
|
-
*
|
|
9
4
|
* Usage:
|
|
10
5
|
* npx tsx examples/init-metadata.ts
|
|
11
6
|
*/
|
|
12
7
|
|
|
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
|
-
);
|
|
8
|
+
import { createSession, resumeSession } from '@factory/droid-sdk';
|
|
54
9
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
10
|
+
const session = await createSession({ cwd: process.cwd() });
|
|
11
|
+
const resumed = await resumeSession(session.sessionId);
|
|
58
12
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
}
|
|
13
|
+
console.log(`created session: ${session.sessionId}`);
|
|
14
|
+
console.log(`resumed session: ${resumed.sessionId}`);
|
|
15
|
+
console.log(`created model: ${String(session.initResult.settings.modelId)}`);
|
|
16
|
+
console.log(`resumed cwd: ${String(resumed.initResult.cwd)}`);
|
|
72
17
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
process.exit(1);
|
|
76
|
-
});
|
|
18
|
+
await resumed.close();
|
|
19
|
+
await session.close();
|
|
@@ -1,67 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Interrupt
|
|
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`.
|
|
2
|
+
* Interrupt a running turn.
|
|
8
3
|
*
|
|
9
4
|
* Usage:
|
|
10
5
|
* npx tsx examples/interrupt-session.ts
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
|
-
import { createSession, DroidMessageType } from '
|
|
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`);
|
|
8
|
+
import { createSession, DroidMessageType } from '@factory/droid-sdk';
|
|
24
9
|
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
const session = await createSession({ cwd: process.cwd() });
|
|
11
|
+
let deltaCount = 0;
|
|
27
12
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
13
|
+
try {
|
|
14
|
+
for await (const msg of session.stream(
|
|
15
|
+
'Write a long history of computing.'
|
|
16
|
+
)) {
|
|
17
|
+
if (msg.type !== DroidMessageType.AssistantTextDelta) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
33
20
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
console.log('\n\n>>> Sending interrupt after 5 text deltas...\n');
|
|
37
|
-
await session.interrupt();
|
|
38
|
-
}
|
|
39
|
-
break;
|
|
21
|
+
process.stdout.write(msg.text);
|
|
22
|
+
deltaCount++;
|
|
40
23
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
}
|
|
24
|
+
if (deltaCount === 5) {
|
|
25
|
+
await session.interrupt();
|
|
57
26
|
}
|
|
58
|
-
} finally {
|
|
59
|
-
await session.close();
|
|
60
|
-
console.log('\nSession closed.');
|
|
61
27
|
}
|
|
62
|
-
}
|
|
63
28
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
29
|
+
console.log('\nInterrupted turn.');
|
|
30
|
+
} finally {
|
|
31
|
+
await session.close();
|
|
32
|
+
}
|
|
@@ -1,65 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* List sessions example.
|
|
3
|
-
*
|
|
4
|
-
* Demonstrates:
|
|
5
|
-
* - scanning ~/.factory/sessions/ for saved droid sessions
|
|
6
|
-
* - filtering by current working directory
|
|
7
|
-
* - including sessions from other projects with a cap
|
|
8
|
-
*
|
|
9
|
-
* This helper reads the local filesystem directly — no droid process is
|
|
10
|
-
* spawned — so it's safe to call even when no droid is running.
|
|
2
|
+
* List saved sessions example.
|
|
11
3
|
*
|
|
12
4
|
* Usage:
|
|
13
5
|
* npx tsx examples/list-sessions.ts
|
|
14
6
|
*/
|
|
15
7
|
|
|
16
|
-
import { listSessions } from '
|
|
17
|
-
|
|
18
|
-
function formatDate(date: Date): string {
|
|
19
|
-
return date.toISOString().replace('T', ' ').slice(0, 19);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function main(): Promise<void> {
|
|
23
|
-
console.log(`=== Sessions for ${process.cwd()} ===\n`);
|
|
8
|
+
import { listSessions } from '@factory/droid-sdk';
|
|
24
9
|
|
|
25
|
-
|
|
26
|
-
const currentProject = await listSessions({ numSessions: 10 });
|
|
10
|
+
const sessions = await listSessions({ numSessions: 5 });
|
|
27
11
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
console.log(`10 most recent for this project:\n`);
|
|
32
|
-
for (const session of currentProject) {
|
|
33
|
-
const fav = session.isFavorite ? '*' : ' ';
|
|
34
|
-
const title = session.sessionTitle ?? session.title ?? '(untitled)';
|
|
35
|
-
console.log(
|
|
36
|
-
` ${fav} [${session.id.slice(0, 8)}] ${formatDate(session.modifiedTime)} ${session.messageCount} msgs — ${title}`
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log('\n=== 5 most recent sessions across all projects ===\n');
|
|
42
|
-
|
|
43
|
-
const allSessions = await listSessions({
|
|
44
|
-
fetchOutsideCWD: true,
|
|
45
|
-
numSessions: 5,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (allSessions.length === 0) {
|
|
49
|
-
console.log('(no sessions found)');
|
|
50
|
-
} else {
|
|
51
|
-
for (const session of allSessions) {
|
|
52
|
-
const title = session.sessionTitle ?? session.title ?? '(untitled)';
|
|
53
|
-
const cwd = session.cwd ?? '(no cwd)';
|
|
54
|
-
console.log(
|
|
55
|
-
` [${session.id.slice(0, 8)}] ${formatDate(session.modifiedTime)} — ${title}`
|
|
56
|
-
);
|
|
57
|
-
console.log(` cwd: ${cwd}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
12
|
+
for (const session of sessions) {
|
|
13
|
+
const title = session.sessionTitle ?? session.title ?? '(untitled)';
|
|
14
|
+
console.log(`${session.id}: ${title}`);
|
|
60
15
|
}
|
|
61
16
|
|
|
62
|
-
|
|
63
|
-
console.
|
|
64
|
-
|
|
65
|
-
});
|
|
17
|
+
if (sessions.length === 0) {
|
|
18
|
+
console.log('No sessions found for this project.');
|
|
19
|
+
}
|
|
@@ -1,67 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-turn session example.
|
|
3
3
|
*
|
|
4
|
-
* Demonstrates `createSession()` for a persistent session, multiple
|
|
5
|
-
* `stream()` calls for streaming turns, `send()` for a non-streaming
|
|
6
|
-
* turn, and `close()` for cleanup.
|
|
7
|
-
*
|
|
8
4
|
* Usage:
|
|
9
5
|
* npx tsx examples/multi-turn-session.ts
|
|
10
6
|
*/
|
|
11
7
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
async function main(): Promise<void> {
|
|
15
|
-
const session = await createSession({ cwd: process.cwd() });
|
|
16
|
-
try {
|
|
17
|
-
console.log(`Session created: ${session.sessionId}\n`);
|
|
18
|
-
|
|
19
|
-
console.log('=== Turn 1 (streaming) ===');
|
|
20
|
-
console.log('Prompt: "List the TypeScript files in this project"\n');
|
|
8
|
+
import { DroidMessageType, createSession } from '@factory/droid-sdk';
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
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
|
+
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
17
|
+
text += msg.text;
|
|
31
18
|
}
|
|
19
|
+
}
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
console.log('Prompt: "How many lines of code total?"\n');
|
|
35
|
-
|
|
36
|
-
for await (const msg of session.stream('How many lines of code total?')) {
|
|
37
|
-
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
38
|
-
process.stdout.write(msg.text);
|
|
39
|
-
}
|
|
40
|
-
if (msg.type === DroidMessageType.TurnComplete) {
|
|
41
|
-
console.log('\n');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
23
|
+
const session = await createSession({ cwd: process.cwd() });
|
|
44
24
|
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
try {
|
|
26
|
+
console.log(`Session: ${session.sessionId}\n`);
|
|
47
27
|
|
|
48
|
-
|
|
28
|
+
const first = await streamText(session, 'What is this project?');
|
|
29
|
+
console.log(first);
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`Tokens — input: ${result.tokenUsage.inputTokens}, ` +
|
|
55
|
-
`output: ${result.tokenUsage.outputTokens}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
} finally {
|
|
59
|
-
await session.close();
|
|
60
|
-
console.log('\nSession closed.');
|
|
61
|
-
}
|
|
31
|
+
const second = await streamText(session, 'What should I test first?');
|
|
32
|
+
console.log('\n', second);
|
|
33
|
+
} finally {
|
|
34
|
+
await session.close();
|
|
62
35
|
}
|
|
63
|
-
|
|
64
|
-
main().catch((err: unknown) => {
|
|
65
|
-
console.error('Error:', err);
|
|
66
|
-
process.exit(1);
|
|
67
|
-
});
|