@ai-sdk/harness-deepagents 1.0.93 → 1.0.94
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/CHANGELOG.md +11 -0
- package/dist/bridge/index.mjs +160 -22
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/index.ts +49 -28
- package/src/bridge/persistent-memory-saver.ts +137 -0
- package/src/deepagents-harness.ts +6 -7
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import type { MemorySaver } from '@langchain/langgraph';
|
|
4
|
+
|
|
5
|
+
const SNAPSHOT_HEADER = 'deepagents-memory-saver-v1';
|
|
6
|
+
|
|
7
|
+
function encodeString(value: string): string {
|
|
8
|
+
return Buffer.from(value, 'utf8').toString('base64');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function decodeString(value: string): string {
|
|
12
|
+
return Buffer.from(value, 'base64').toString('utf8');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function encodeBytes(value: Uint8Array): string {
|
|
16
|
+
return Buffer.from(value).toString('base64');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function decodeBytes(value: string): Uint8Array {
|
|
20
|
+
return Buffer.from(value, 'base64');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function loadMemorySaver({
|
|
24
|
+
path,
|
|
25
|
+
saver,
|
|
26
|
+
}: {
|
|
27
|
+
path: string;
|
|
28
|
+
saver: MemorySaver;
|
|
29
|
+
}): Promise<void> {
|
|
30
|
+
let snapshot: string;
|
|
31
|
+
try {
|
|
32
|
+
snapshot = await readFile(path, 'utf8');
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const [header, ...lines] = snapshot.split('\n');
|
|
39
|
+
if (header !== SNAPSHOT_HEADER) {
|
|
40
|
+
throw new Error('Unsupported Deep Agents conversation checkpoint format');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const storage: MemorySaver['storage'] = Object.create(null);
|
|
44
|
+
const writes: MemorySaver['writes'] = Object.create(null);
|
|
45
|
+
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
if (line === '') continue;
|
|
48
|
+
const fields = line.split('\t');
|
|
49
|
+
if (fields[0] === 'S' && fields.length === 7) {
|
|
50
|
+
const [, threadIdValue, namespaceValue, checkpointIdValue] = fields;
|
|
51
|
+
const threadId = decodeString(threadIdValue);
|
|
52
|
+
const namespace = decodeString(namespaceValue);
|
|
53
|
+
const checkpointId = decodeString(checkpointIdValue);
|
|
54
|
+
storage[threadId] ??= Object.create(null);
|
|
55
|
+
storage[threadId][namespace] ??= Object.create(null);
|
|
56
|
+
storage[threadId][namespace][checkpointId] = [
|
|
57
|
+
decodeBytes(fields[4]),
|
|
58
|
+
decodeBytes(fields[5]),
|
|
59
|
+
fields[6] === '' ? undefined : decodeString(fields[6]),
|
|
60
|
+
];
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (fields[0] === 'W' && fields.length === 6) {
|
|
64
|
+
const [, keyValue, indexValue, taskIdValue, channelValue, value] = fields;
|
|
65
|
+
const key = decodeString(keyValue);
|
|
66
|
+
const index = decodeString(indexValue);
|
|
67
|
+
writes[key] ??= Object.create(null);
|
|
68
|
+
writes[key][index] = [
|
|
69
|
+
decodeString(taskIdValue),
|
|
70
|
+
decodeString(channelValue),
|
|
71
|
+
decodeBytes(value),
|
|
72
|
+
];
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
throw new Error('Invalid Deep Agents conversation checkpoint');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
saver.storage = storage;
|
|
79
|
+
saver.writes = writes;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function saveMemorySaver({
|
|
83
|
+
path,
|
|
84
|
+
saver,
|
|
85
|
+
}: {
|
|
86
|
+
path: string;
|
|
87
|
+
saver: MemorySaver;
|
|
88
|
+
}): Promise<void> {
|
|
89
|
+
const lines = [SNAPSHOT_HEADER];
|
|
90
|
+
|
|
91
|
+
for (const [threadId, namespaces] of Object.entries(saver.storage)) {
|
|
92
|
+
for (const [namespace, checkpoints] of Object.entries(namespaces)) {
|
|
93
|
+
for (const [
|
|
94
|
+
checkpointId,
|
|
95
|
+
[checkpoint, metadata, parentCheckpointId],
|
|
96
|
+
] of Object.entries(checkpoints)) {
|
|
97
|
+
lines.push(
|
|
98
|
+
[
|
|
99
|
+
'S',
|
|
100
|
+
encodeString(threadId),
|
|
101
|
+
encodeString(namespace),
|
|
102
|
+
encodeString(checkpointId),
|
|
103
|
+
encodeBytes(checkpoint),
|
|
104
|
+
encodeBytes(metadata),
|
|
105
|
+
parentCheckpointId == null ? '' : encodeString(parentCheckpointId),
|
|
106
|
+
].join('\t'),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const [key, indexedWrites] of Object.entries(saver.writes)) {
|
|
113
|
+
for (const [index, [taskId, channel, value]] of Object.entries(
|
|
114
|
+
indexedWrites,
|
|
115
|
+
)) {
|
|
116
|
+
lines.push(
|
|
117
|
+
[
|
|
118
|
+
'W',
|
|
119
|
+
encodeString(key),
|
|
120
|
+
encodeString(index),
|
|
121
|
+
encodeString(taskId),
|
|
122
|
+
encodeString(channel),
|
|
123
|
+
encodeBytes(value),
|
|
124
|
+
].join('\t'),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await mkdir(dirname(path), { recursive: true });
|
|
130
|
+
const temporaryPath = `${path}.${process.pid}.tmp`;
|
|
131
|
+
await writeFile(temporaryPath, `${lines.join('\n')}\n`, 'utf8');
|
|
132
|
+
await rename(temporaryPath, path);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function removeMemorySaverSnapshot(path: string): Promise<void> {
|
|
136
|
+
await rm(path, { force: true });
|
|
137
|
+
}
|
|
@@ -222,7 +222,6 @@ export function createDeepAgents(
|
|
|
222
222
|
lifecycleStateSchema: deepAgentsResumeStateSchema,
|
|
223
223
|
getBootstrap: getDeepAgentsBootstrap,
|
|
224
224
|
doStart: async startOpts => {
|
|
225
|
-
const model = startOpts.model ?? settings.model;
|
|
226
225
|
const permissionMode = startOpts.permissionMode;
|
|
227
226
|
const sandboxSession = startOpts.sandboxSession;
|
|
228
227
|
const toolSafeSandboxSession =
|
|
@@ -355,7 +354,7 @@ export function createDeepAgents(
|
|
|
355
354
|
sessionId: startOpts.sessionId,
|
|
356
355
|
channel: attachChannel,
|
|
357
356
|
proc: undefined,
|
|
358
|
-
model,
|
|
357
|
+
model: settings.model,
|
|
359
358
|
thinking: settings.thinking,
|
|
360
359
|
effort: settings.effort,
|
|
361
360
|
bridgePort: coords.port,
|
|
@@ -413,7 +412,7 @@ export function createDeepAgents(
|
|
|
413
412
|
});
|
|
414
413
|
|
|
415
414
|
const proc = await toolSafeSandboxSession.spawn({
|
|
416
|
-
command: `node ${shellQuote(`${bootstrapDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --bootstrap-dir ${shellQuote(bootstrapDir)}`,
|
|
415
|
+
command: `node ${shellQuote(`${bootstrapDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --bootstrap-dir ${shellQuote(bootstrapDir)}${isResume ? ' --resume true' : ''}`,
|
|
417
416
|
env,
|
|
418
417
|
abortSignal: startOpts.abortSignal,
|
|
419
418
|
});
|
|
@@ -470,7 +469,7 @@ export function createDeepAgents(
|
|
|
470
469
|
sessionId: startOpts.sessionId,
|
|
471
470
|
channel,
|
|
472
471
|
proc,
|
|
473
|
-
model,
|
|
472
|
+
model: settings.model,
|
|
474
473
|
thinking: settings.thinking,
|
|
475
474
|
effort: settings.effort,
|
|
476
475
|
bridgePort: boundPort,
|
|
@@ -785,7 +784,6 @@ function createSession({
|
|
|
785
784
|
return {
|
|
786
785
|
sessionId,
|
|
787
786
|
isResume,
|
|
788
|
-
modelId: model,
|
|
789
787
|
doPromptTurn: async promptOpts => {
|
|
790
788
|
if (
|
|
791
789
|
promptOpts.responseFormat?.type === 'json' &&
|
|
@@ -822,7 +820,9 @@ function createSession({
|
|
|
822
820
|
...(promptOpts.responseFormat == null
|
|
823
821
|
? {}
|
|
824
822
|
: { responseFormat: promptOpts.responseFormat }),
|
|
825
|
-
...(model
|
|
823
|
+
...((promptOpts.model ?? model)
|
|
824
|
+
? { model: promptOpts.model ?? model }
|
|
825
|
+
: {}),
|
|
826
826
|
...(thinking ? { thinking } : {}),
|
|
827
827
|
...(effort ? { effort } : {}),
|
|
828
828
|
...(skillsPaths?.length ? { skillsPaths } : {}),
|
|
@@ -915,7 +915,6 @@ function createSession({
|
|
|
915
915
|
}
|
|
916
916
|
stopped = true;
|
|
917
917
|
await teardown({ channel, proc, operation: 'stop' });
|
|
918
|
-
// In-memory conversation is lost on teardown; the sandbox snapshot preserves the workspace files, not the conversation.
|
|
919
918
|
const payload: HarnessV1ResumeSessionState = {
|
|
920
919
|
type: 'resume-session',
|
|
921
920
|
harnessId: 'deepagents',
|