Repository navigation
Expand file tree
/
Copy pathrewind-session.ts
More file actions
86 lines (76 loc) · 2.55 KB
/
Copy pathrewind-session.ts
File metadata and controls
86 lines (76 loc) · 2.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* Rewind session example.
*
* Runs a turn, inspects the files available at that point, then rewinds into a
* replacement session handle and continues from there.
*
* Usage:
* npx tsx examples/node/rewind-session.ts
*/
import { DroidMessageType, createSession } from '@factory/droid-sdk/node';
const session = await createSession();
async function ask(
activeSession: Awaited<ReturnType<typeof createSession>>,
prompt: string
): Promise<{ messageId: string; text: string }> {
let messageId: string | undefined;
let text = '';
for await (const message of activeSession.stream(prompt)) {
if (
message.type === DroidMessageType.User &&
message.message.visibility !== 'llm_only'
) {
messageId ??= message.message.id;
}
if (message.type === DroidMessageType.Assistant) {
text += message.text;
console.log(message.text);
}
if (message.type === DroidMessageType.Result && !message.success) {
throw new Error(
message.error?.message ?? `Turn ended: ${message.subtype}`
);
}
}
if (!messageId) throw new Error('The turn produced no user message ID.');
if (!text.trim()) throw new Error('The turn produced no assistant output.');
return { messageId, text };
}
try {
await ask(session, 'Remember this phrase exactly: mango sunrise');
const { messageId: secondTurn } = await ask(
session,
'Now forget it and remember: cobalt harbor'
);
const { availableFiles, createdFiles } = await session.getRewindInfo({
messageId: secondTurn,
});
console.log(
`rewind would restore ${availableFiles.length} file(s) ` +
`and delete ${createdFiles.length}`
);
const { session: successor } = await session.rewind({
messageId: secondTurn,
filesToRestore: availableFiles,
filesToDelete: createdFiles,
forkTitle: 'Before the second phrase',
});
try {
// `session` is now a retired wrapper. Its persisted ID can still be passed
// to resumeSession() later if the pre-rewind history is needed again.
console.log(`rewound into session: ${successor.id}`);
const { text: recalled } = await ask(
successor,
'What phrase did I ask you to remember?'
);
// The rewind removed the second turn, so only the first phrase remains.
if (!/mango sunrise/i.test(recalled) || /cobalt harbor/i.test(recalled)) {
throw new Error('The rewound session did not drop the second turn.');
}
} finally {
await successor.close();
}
} finally {
// Closes the active source, or does nothing if it was retired.
await session.close();
}