@botbotgo/agent-harness 0.0.96 → 0.0.98
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/dist/api.d.ts +5 -5
- package/dist/contracts/core.d.ts +9 -0
- package/dist/contracts/core.js +1 -0
- package/dist/contracts/runtime.d.ts +421 -0
- package/dist/contracts/runtime.js +1 -0
- package/dist/contracts/types.d.ts +3 -653
- package/dist/contracts/types.js +3 -1
- package/dist/contracts/workspace.d.ts +229 -0
- package/dist/contracts/workspace.js +1 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/adapter/compat/deepagent-compat.d.ts +16 -0
- package/dist/runtime/adapter/compat/deepagent-compat.js +45 -0
- package/dist/runtime/adapter/compat/openai-compatible.d.ts +2 -0
- package/dist/runtime/adapter/compat/openai-compatible.js +43 -0
- package/dist/runtime/adapter/index.d.ts +15 -0
- package/dist/runtime/adapter/index.js +15 -0
- package/dist/runtime/{langgraph-profiles.d.ts → adapter/langgraph/profiles.d.ts} +1 -1
- package/dist/runtime/adapter/model/invocation-request.d.ts +10 -0
- package/dist/runtime/adapter/model/invocation-request.js +46 -0
- package/dist/runtime/adapter/model/message-assembly.d.ts +6 -0
- package/dist/runtime/adapter/model/message-assembly.js +21 -0
- package/dist/runtime/adapter/model/model-providers.d.ts +2 -0
- package/dist/runtime/adapter/model/model-providers.js +27 -0
- package/dist/runtime/adapter/resilience.d.ts +12 -0
- package/dist/runtime/adapter/resilience.js +60 -0
- package/dist/runtime/{declared-middleware.d.ts → adapter/tool/declared-middleware.d.ts} +1 -1
- package/dist/runtime/adapter/tool/interrupt-policy.d.ts +8 -0
- package/dist/runtime/adapter/tool/interrupt-policy.js +34 -0
- package/dist/runtime/adapter/tool/provider-tool.d.ts +2 -0
- package/dist/runtime/adapter/tool/provider-tool.js +25 -0
- package/dist/runtime/adapter/tool/resolved-tool.d.ts +18 -0
- package/dist/runtime/adapter/tool/resolved-tool.js +62 -0
- package/dist/runtime/adapter/tool/tool-arguments.d.ts +7 -0
- package/dist/runtime/adapter/tool/tool-arguments.js +87 -0
- package/dist/runtime/{tool-hitl.d.ts → adapter/tool/tool-hitl.d.ts} +2 -2
- package/dist/runtime/adapter/tool/tool-name-mapping.d.ts +13 -0
- package/dist/runtime/adapter/tool/tool-name-mapping.js +101 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +5 -20
- package/dist/runtime/agent-runtime-adapter.js +33 -536
- package/dist/runtime/checkpoint-maintenance.d.ts +1 -45
- package/dist/runtime/checkpoint-maintenance.js +1 -259
- package/dist/runtime/file-checkpoint-saver.d.ts +1 -20
- package/dist/runtime/file-checkpoint-saver.js +1 -106
- package/dist/runtime/{event-bus.d.ts → harness/events/event-bus.d.ts} +1 -1
- package/dist/runtime/{event-sink.d.ts → harness/events/event-sink.d.ts} +1 -1
- package/dist/runtime/{event-sink.js → harness/events/event-sink.js} +1 -1
- package/dist/runtime/harness/events/events.d.ts +23 -0
- package/dist/runtime/harness/events/events.js +61 -0
- package/dist/runtime/harness/events/streaming.d.ts +19 -0
- package/dist/runtime/harness/events/streaming.js +96 -0
- package/dist/runtime/harness/index.d.ts +16 -0
- package/dist/runtime/harness/index.js +16 -0
- package/dist/runtime/harness/run/helpers.d.ts +33 -0
- package/dist/runtime/harness/run/helpers.js +74 -0
- package/dist/runtime/harness/run/resources.d.ts +7 -0
- package/dist/runtime/harness/run/resources.js +58 -0
- package/dist/runtime/harness/run/resume.d.ts +6 -0
- package/dist/runtime/harness/run/resume.js +56 -0
- package/dist/runtime/harness/run/routing.d.ts +12 -0
- package/dist/runtime/harness/run/routing.js +47 -0
- package/dist/runtime/harness/run/run-lifecycle.d.ts +37 -0
- package/dist/runtime/harness/run/run-lifecycle.js +109 -0
- package/dist/runtime/harness/run/run-queue.d.ts +17 -0
- package/dist/runtime/harness/run/run-queue.js +43 -0
- package/dist/runtime/{health-monitor.d.ts → harness/system/health-monitor.d.ts} +3 -3
- package/dist/runtime/{health-monitor.js → harness/system/health-monitor.js} +2 -2
- package/dist/runtime/{inventory.d.ts → harness/system/inventory.d.ts} +2 -2
- package/dist/runtime/{inventory.js → harness/system/inventory.js} +4 -4
- package/dist/runtime/{policy-engine.d.ts → harness/system/policy-engine.d.ts} +1 -1
- package/dist/runtime/{policy-engine.js → harness/system/policy-engine.js} +1 -1
- package/dist/runtime/{skill-requirements.d.ts → harness/system/skill-requirements.d.ts} +1 -1
- package/dist/runtime/{skill-requirements.js → harness/system/skill-requirements.js} +1 -1
- package/dist/runtime/{thread-memory-sync.d.ts → harness/system/thread-memory-sync.d.ts} +2 -2
- package/dist/runtime/{thread-memory-sync.js → harness/system/thread-memory-sync.js} +1 -1
- package/dist/runtime/harness.d.ts +2 -8
- package/dist/runtime/harness.js +143 -493
- package/dist/runtime/index.d.ts +7 -7
- package/dist/runtime/index.js +7 -7
- package/dist/runtime/maintenance/checkpoint-maintenance.d.ts +45 -0
- package/dist/runtime/maintenance/checkpoint-maintenance.js +259 -0
- package/dist/runtime/maintenance/file-checkpoint-saver.d.ts +20 -0
- package/dist/runtime/maintenance/file-checkpoint-saver.js +106 -0
- package/dist/runtime/maintenance/index.d.ts +4 -0
- package/dist/runtime/maintenance/index.js +4 -0
- package/dist/runtime/{runtime-record-maintenance.d.ts → maintenance/runtime-record-maintenance.d.ts} +1 -1
- package/dist/runtime/{runtime-record-maintenance.js → maintenance/runtime-record-maintenance.js} +2 -2
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +9 -0
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +39 -0
- package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +1 -9
- package/dist/runtime/sqlite-maintained-checkpoint-saver.js +1 -39
- package/dist/runtime/support/harness-support.d.ts +4 -4
- package/dist/runtime/support/runtime-factories.d.ts +1 -1
- package/dist/runtime/support/runtime-factories.js +1 -1
- package/package.json +4 -4
- /package/dist/runtime/{langgraph-presets.d.ts → adapter/langgraph/presets.d.ts} +0 -0
- /package/dist/runtime/{langgraph-presets.js → adapter/langgraph/presets.js} +0 -0
- /package/dist/runtime/{langgraph-profiles.js → adapter/langgraph/profiles.js} +0 -0
- /package/dist/runtime/{declared-middleware.js → adapter/tool/declared-middleware.js} +0 -0
- /package/dist/runtime/{tool-hitl.js → adapter/tool/tool-hitl.js} +0 -0
- /package/dist/runtime/{event-bus.js → harness/events/event-bus.js} +0 -0
- /package/dist/runtime/{store.d.ts → harness/system/store.d.ts} +0 -0
- /package/dist/runtime/{store.js → harness/system/store.js} +0 -0
|
@@ -1,45 +1 @@
|
|
|
1
|
-
|
|
2
|
-
type CheckpointMaintenanceConfig = {
|
|
3
|
-
enabled: boolean;
|
|
4
|
-
schedule: {
|
|
5
|
-
intervalSeconds: number;
|
|
6
|
-
runOnStartup: boolean;
|
|
7
|
-
};
|
|
8
|
-
policies: {
|
|
9
|
-
maxAgeSeconds?: number;
|
|
10
|
-
maxBytes?: number;
|
|
11
|
-
};
|
|
12
|
-
sqlite: {
|
|
13
|
-
sweepBatchSize: number;
|
|
14
|
-
vacuum: boolean;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
type CheckpointMaintenanceTarget = {
|
|
18
|
-
agentId: string;
|
|
19
|
-
dbPath: string;
|
|
20
|
-
};
|
|
21
|
-
export type MaintenanceLoopStatus = {
|
|
22
|
-
lastStartedAt?: string;
|
|
23
|
-
lastCompletedAt?: string;
|
|
24
|
-
lastFailedAt?: string;
|
|
25
|
-
consecutiveFailures: number;
|
|
26
|
-
lastError?: string;
|
|
27
|
-
};
|
|
28
|
-
export declare function readCheckpointMaintenanceConfig(workspace: WorkspaceBundle): CheckpointMaintenanceConfig | null;
|
|
29
|
-
export declare function discoverCheckpointMaintenanceTargets(workspace: WorkspaceBundle): CheckpointMaintenanceTarget[];
|
|
30
|
-
export declare function maintainSqliteCheckpoints(dbPath: string, config: CheckpointMaintenanceConfig, nowMs?: number): Promise<{
|
|
31
|
-
deletedCount: number;
|
|
32
|
-
}>;
|
|
33
|
-
export declare class CheckpointMaintenanceLoop {
|
|
34
|
-
private readonly targets;
|
|
35
|
-
private readonly config;
|
|
36
|
-
private timer;
|
|
37
|
-
private running;
|
|
38
|
-
private status;
|
|
39
|
-
constructor(targets: CheckpointMaintenanceTarget[], config: CheckpointMaintenanceConfig);
|
|
40
|
-
runOnce(nowMs?: number): Promise<void>;
|
|
41
|
-
getStatus(): MaintenanceLoopStatus;
|
|
42
|
-
start(): Promise<void>;
|
|
43
|
-
stop(): Promise<void>;
|
|
44
|
-
}
|
|
45
|
-
export {};
|
|
1
|
+
export * from "./maintenance/checkpoint-maintenance.js";
|
|
@@ -1,259 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
|
|
3
|
-
import { listProtectedCheckpointThreadIds } from "../persistence/sqlite-store.js";
|
|
4
|
-
import { fileExists } from "../utils/fs.js";
|
|
5
|
-
import { getRuntimeDefaults } from "../workspace/support/workspace-ref-utils.js";
|
|
6
|
-
import { ManagedSqliteSaver } from "./sqlite-maintained-checkpoint-saver.js";
|
|
7
|
-
function asObject(value) {
|
|
8
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
9
|
-
}
|
|
10
|
-
function readPositiveNumber(value, label, allowUndefined = true) {
|
|
11
|
-
if (value === undefined && allowUndefined) {
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
15
|
-
throw new Error(`${label} must be a positive number`);
|
|
16
|
-
}
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
export function readCheckpointMaintenanceConfig(workspace) {
|
|
20
|
-
const runtimeDefaults = getRuntimeDefaults(workspace.refs);
|
|
21
|
-
const maintenance = asObject(runtimeDefaults?.maintenance);
|
|
22
|
-
const checkpoints = asObject(maintenance?.checkpoints);
|
|
23
|
-
if (!checkpoints || checkpoints.enabled !== true) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
const schedule = asObject(checkpoints.schedule);
|
|
27
|
-
const policies = asObject(checkpoints.policies);
|
|
28
|
-
const sqlite = asObject(checkpoints.sqlite);
|
|
29
|
-
const config = {
|
|
30
|
-
enabled: true,
|
|
31
|
-
schedule: {
|
|
32
|
-
intervalSeconds: readPositiveNumber(schedule?.intervalSeconds, "runtime.maintenance.checkpoints.schedule.intervalSeconds") ?? 3600,
|
|
33
|
-
runOnStartup: schedule?.runOnStartup !== false,
|
|
34
|
-
},
|
|
35
|
-
policies: {
|
|
36
|
-
maxAgeSeconds: readPositiveNumber(policies?.maxAgeSeconds, "runtime.maintenance.checkpoints.policies.maxAgeSeconds"),
|
|
37
|
-
maxBytes: readPositiveNumber(policies?.maxBytes, "runtime.maintenance.checkpoints.policies.maxBytes"),
|
|
38
|
-
},
|
|
39
|
-
sqlite: {
|
|
40
|
-
sweepBatchSize: readPositiveNumber(sqlite?.sweepBatchSize, "runtime.maintenance.checkpoints.sqlite.sweepBatchSize") ?? 200,
|
|
41
|
-
vacuum: sqlite?.vacuum === true,
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
if (config.policies.maxAgeSeconds === undefined && config.policies.maxBytes === undefined) {
|
|
45
|
-
throw new Error("runtime.maintenance.checkpoints.enabled requires at least one cleanup policy");
|
|
46
|
-
}
|
|
47
|
-
return config;
|
|
48
|
-
}
|
|
49
|
-
function resolveSqliteCheckpointPath(binding) {
|
|
50
|
-
const config = binding.harnessRuntime.checkpointer;
|
|
51
|
-
if (!config || typeof config === "boolean") {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
const kind = typeof config.kind === "string" ? config.kind : "FileCheckpointer";
|
|
55
|
-
if (kind !== "SqliteSaver") {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const configuredPath = typeof config.path === "string" ? String(config.path) : "checkpoints.sqlite";
|
|
59
|
-
return path.isAbsolute(configuredPath) ? configuredPath : path.join(binding.harnessRuntime.runRoot, configuredPath);
|
|
60
|
-
}
|
|
61
|
-
export function discoverCheckpointMaintenanceTargets(workspace) {
|
|
62
|
-
const deduped = new Map();
|
|
63
|
-
for (const binding of workspace.bindings.values()) {
|
|
64
|
-
const dbPath = resolveSqliteCheckpointPath(binding);
|
|
65
|
-
if (!dbPath) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
deduped.set(dbPath, {
|
|
69
|
-
agentId: binding.agent.id,
|
|
70
|
-
dbPath,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
return Array.from(deduped.values());
|
|
74
|
-
}
|
|
75
|
-
function backfillCheckpointMetadata(db, nowMs = Date.now()) {
|
|
76
|
-
db.exec(`
|
|
77
|
-
CREATE TABLE IF NOT EXISTS checkpoint_maintenance_meta (
|
|
78
|
-
thread_id TEXT NOT NULL,
|
|
79
|
-
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
|
80
|
-
checkpoint_id TEXT NOT NULL,
|
|
81
|
-
created_at_ms INTEGER NOT NULL,
|
|
82
|
-
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
|
|
83
|
-
);`);
|
|
84
|
-
db.prepare(`INSERT OR IGNORE INTO checkpoint_maintenance_meta
|
|
85
|
-
(thread_id, checkpoint_ns, checkpoint_id, created_at_ms)
|
|
86
|
-
SELECT thread_id, checkpoint_ns, checkpoint_id, ?
|
|
87
|
-
FROM checkpoints`).run(nowMs);
|
|
88
|
-
}
|
|
89
|
-
function deleteCheckpointRows(db, rows) {
|
|
90
|
-
if (rows.length === 0) {
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
const deleteCheckpoint = db.prepare(`DELETE FROM checkpoints
|
|
94
|
-
WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
|
|
95
|
-
const deleteWrites = db.prepare(`DELETE FROM writes
|
|
96
|
-
WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
|
|
97
|
-
const deleteMeta = db.prepare(`DELETE FROM checkpoint_maintenance_meta
|
|
98
|
-
WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
|
|
99
|
-
db.transaction((items) => {
|
|
100
|
-
for (const row of items) {
|
|
101
|
-
deleteCheckpoint.run(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
|
|
102
|
-
deleteWrites.run(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
|
|
103
|
-
deleteMeta.run(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
|
|
104
|
-
}
|
|
105
|
-
})(rows);
|
|
106
|
-
return rows.length;
|
|
107
|
-
}
|
|
108
|
-
function selectOldestRows(db, limit) {
|
|
109
|
-
return db
|
|
110
|
-
.prepare(`SELECT
|
|
111
|
-
meta.thread_id,
|
|
112
|
-
meta.checkpoint_ns,
|
|
113
|
-
meta.checkpoint_id,
|
|
114
|
-
meta.created_at_ms,
|
|
115
|
-
COALESCE(LENGTH(cp.checkpoint), 0) + COALESCE(LENGTH(cp.metadata), 0) + COALESCE(SUM(LENGTH(w.value)), 0) AS size_bytes
|
|
116
|
-
FROM checkpoint_maintenance_meta AS meta
|
|
117
|
-
JOIN checkpoints AS cp
|
|
118
|
-
ON cp.thread_id = meta.thread_id
|
|
119
|
-
AND cp.checkpoint_ns = meta.checkpoint_ns
|
|
120
|
-
AND cp.checkpoint_id = meta.checkpoint_id
|
|
121
|
-
LEFT JOIN writes AS w
|
|
122
|
-
ON w.thread_id = meta.thread_id
|
|
123
|
-
AND w.checkpoint_ns = meta.checkpoint_ns
|
|
124
|
-
AND w.checkpoint_id = meta.checkpoint_id
|
|
125
|
-
GROUP BY meta.thread_id, meta.checkpoint_ns, meta.checkpoint_id, meta.created_at_ms, cp.checkpoint, cp.metadata
|
|
126
|
-
ORDER BY meta.created_at_ms ASC, meta.checkpoint_id ASC
|
|
127
|
-
LIMIT ?`)
|
|
128
|
-
.all(limit);
|
|
129
|
-
}
|
|
130
|
-
function totalCheckpointBytes(db) {
|
|
131
|
-
const checkpointsBytes = db
|
|
132
|
-
.prepare(`SELECT COALESCE(SUM(LENGTH(checkpoint) + LENGTH(metadata)), 0) AS total FROM checkpoints`)
|
|
133
|
-
.get();
|
|
134
|
-
const writesBytes = db
|
|
135
|
-
.prepare(`SELECT COALESCE(SUM(LENGTH(value)), 0) AS total FROM writes`)
|
|
136
|
-
.get();
|
|
137
|
-
return Number(checkpointsBytes.total ?? 0) + Number(writesBytes.total ?? 0);
|
|
138
|
-
}
|
|
139
|
-
export function maintainSqliteCheckpoints(dbPath, config, nowMs = Date.now()) {
|
|
140
|
-
return maintainSqliteCheckpointsInternal(dbPath, config, nowMs);
|
|
141
|
-
}
|
|
142
|
-
async function maintainSqliteCheckpointsInternal(dbPath, config, nowMs) {
|
|
143
|
-
if (!(await fileExists(dbPath))) {
|
|
144
|
-
return { deletedCount: 0 };
|
|
145
|
-
}
|
|
146
|
-
const saver = new ManagedSqliteSaver(SqliteSaver.fromConnString(dbPath).db);
|
|
147
|
-
const db = saver.db;
|
|
148
|
-
try {
|
|
149
|
-
saver.prepareMaintenance();
|
|
150
|
-
backfillCheckpointMetadata(db, nowMs);
|
|
151
|
-
const protectedThreadIds = await listProtectedCheckpointThreadIds(path.join(path.dirname(dbPath), "runtime.sqlite"));
|
|
152
|
-
let deletedCount = 0;
|
|
153
|
-
if (config.policies.maxAgeSeconds !== undefined) {
|
|
154
|
-
const cutoffMs = nowMs - config.policies.maxAgeSeconds * 1000;
|
|
155
|
-
const expired = db
|
|
156
|
-
.prepare(`SELECT
|
|
157
|
-
meta.thread_id,
|
|
158
|
-
meta.checkpoint_ns,
|
|
159
|
-
meta.checkpoint_id,
|
|
160
|
-
meta.created_at_ms,
|
|
161
|
-
0 AS size_bytes
|
|
162
|
-
FROM checkpoint_maintenance_meta AS meta
|
|
163
|
-
WHERE meta.created_at_ms <= ?
|
|
164
|
-
ORDER BY meta.created_at_ms ASC, meta.checkpoint_id ASC
|
|
165
|
-
LIMIT ?`)
|
|
166
|
-
.all(cutoffMs, config.sqlite.sweepBatchSize * 4);
|
|
167
|
-
deletedCount += deleteCheckpointRows(db, expired.filter((row) => !protectedThreadIds.has(row.thread_id)).slice(0, config.sqlite.sweepBatchSize));
|
|
168
|
-
}
|
|
169
|
-
if (config.policies.maxBytes !== undefined) {
|
|
170
|
-
let currentBytes = totalCheckpointBytes(db);
|
|
171
|
-
while (currentBytes > config.policies.maxBytes) {
|
|
172
|
-
const oldest = selectOldestRows(db, config.sqlite.sweepBatchSize * 4).filter((row) => !protectedThreadIds.has(row.thread_id));
|
|
173
|
-
if (oldest.length === 0) {
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
let reclaimed = 0;
|
|
177
|
-
const toDelete = [];
|
|
178
|
-
for (const row of oldest) {
|
|
179
|
-
toDelete.push(row);
|
|
180
|
-
reclaimed += Number(row.size_bytes ?? 0);
|
|
181
|
-
if (currentBytes - reclaimed <= config.policies.maxBytes) {
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
deletedCount += deleteCheckpointRows(db, toDelete);
|
|
186
|
-
currentBytes = totalCheckpointBytes(db);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (deletedCount > 0 && config.sqlite.vacuum) {
|
|
190
|
-
db.exec("VACUUM");
|
|
191
|
-
}
|
|
192
|
-
return { deletedCount };
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
db.close();
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
export class CheckpointMaintenanceLoop {
|
|
199
|
-
targets;
|
|
200
|
-
config;
|
|
201
|
-
timer = null;
|
|
202
|
-
running = false;
|
|
203
|
-
status = {
|
|
204
|
-
consecutiveFailures: 0,
|
|
205
|
-
};
|
|
206
|
-
constructor(targets, config) {
|
|
207
|
-
this.targets = targets;
|
|
208
|
-
this.config = config;
|
|
209
|
-
}
|
|
210
|
-
async runOnce(nowMs = Date.now()) {
|
|
211
|
-
this.status = {
|
|
212
|
-
...this.status,
|
|
213
|
-
lastStartedAt: new Date(nowMs).toISOString(),
|
|
214
|
-
};
|
|
215
|
-
try {
|
|
216
|
-
for (const target of this.targets) {
|
|
217
|
-
await maintainSqliteCheckpoints(target.dbPath, this.config, nowMs);
|
|
218
|
-
}
|
|
219
|
-
this.status = {
|
|
220
|
-
...this.status,
|
|
221
|
-
lastCompletedAt: new Date(nowMs).toISOString(),
|
|
222
|
-
consecutiveFailures: 0,
|
|
223
|
-
lastError: undefined,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
catch (error) {
|
|
227
|
-
this.status = {
|
|
228
|
-
...this.status,
|
|
229
|
-
lastFailedAt: new Date(nowMs).toISOString(),
|
|
230
|
-
consecutiveFailures: this.status.consecutiveFailures + 1,
|
|
231
|
-
lastError: error instanceof Error ? error.message : String(error),
|
|
232
|
-
};
|
|
233
|
-
throw error;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
getStatus() {
|
|
237
|
-
return { ...this.status };
|
|
238
|
-
}
|
|
239
|
-
async start() {
|
|
240
|
-
if (this.running) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
this.running = true;
|
|
244
|
-
if (this.config.schedule.runOnStartup) {
|
|
245
|
-
await this.runOnce();
|
|
246
|
-
}
|
|
247
|
-
this.timer = setInterval(() => {
|
|
248
|
-
void this.runOnce();
|
|
249
|
-
}, this.config.schedule.intervalSeconds * 1000);
|
|
250
|
-
this.timer.unref?.();
|
|
251
|
-
}
|
|
252
|
-
async stop() {
|
|
253
|
-
if (this.timer) {
|
|
254
|
-
clearInterval(this.timer);
|
|
255
|
-
this.timer = null;
|
|
256
|
-
}
|
|
257
|
-
this.running = false;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
1
|
+
export * from "./maintenance/checkpoint-maintenance.js";
|
|
@@ -1,20 +1 @@
|
|
|
1
|
-
|
|
2
|
-
type MemorySaverConfig = Parameters<MemorySaver["getTuple"]>[0];
|
|
3
|
-
type MemorySaverListOptions = Parameters<MemorySaver["list"]>[1];
|
|
4
|
-
type MemorySaverPutCheckpoint = Parameters<MemorySaver["put"]>[1];
|
|
5
|
-
type MemorySaverPutMetadata = Parameters<MemorySaver["put"]>[2];
|
|
6
|
-
type MemorySaverPutWrites = Parameters<MemorySaver["putWrites"]>[1];
|
|
7
|
-
type MemorySaverPutResult = ReturnType<MemorySaver["put"]>;
|
|
8
|
-
export declare class FileCheckpointSaver extends MemorySaver {
|
|
9
|
-
private readonly filePath;
|
|
10
|
-
private loaded;
|
|
11
|
-
constructor(filePath: string);
|
|
12
|
-
private ensureLoaded;
|
|
13
|
-
private persist;
|
|
14
|
-
getTuple(config: MemorySaverConfig): Promise<import("@langchain/langgraph").CheckpointTuple | undefined>;
|
|
15
|
-
list(config: MemorySaverConfig, options?: MemorySaverListOptions): AsyncGenerator<import("@langchain/langgraph").CheckpointTuple, void, unknown>;
|
|
16
|
-
put(config: MemorySaverConfig, checkpoint: MemorySaverPutCheckpoint, metadata: MemorySaverPutMetadata): MemorySaverPutResult;
|
|
17
|
-
putWrites(config: MemorySaverConfig, writes: MemorySaverPutWrites, taskId: string): Promise<void>;
|
|
18
|
-
deleteThread(threadId: string): Promise<void>;
|
|
19
|
-
}
|
|
20
|
-
export { FileCheckpointSaver as FileCheckpointer };
|
|
1
|
+
export * from "./maintenance/file-checkpoint-saver.js";
|
|
@@ -1,106 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { MemorySaver } from "@langchain/langgraph";
|
|
4
|
-
function encodeBinary(value) {
|
|
5
|
-
if (value instanceof Uint8Array) {
|
|
6
|
-
return {
|
|
7
|
-
__type: "Uint8Array",
|
|
8
|
-
data: Array.from(value),
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
if (Array.isArray(value)) {
|
|
12
|
-
return value.map((item) => encodeBinary(item));
|
|
13
|
-
}
|
|
14
|
-
if (typeof value === "object" && value) {
|
|
15
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, encodeBinary(entry)]));
|
|
16
|
-
}
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
function decodeBinary(value) {
|
|
20
|
-
if (Array.isArray(value)) {
|
|
21
|
-
return value.map((item) => decodeBinary(item));
|
|
22
|
-
}
|
|
23
|
-
if (typeof value === "object" && value) {
|
|
24
|
-
const typed = value;
|
|
25
|
-
if (typed.__type === "Uint8Array" && Array.isArray(typed.data)) {
|
|
26
|
-
return new Uint8Array(typed.data.map((item) => Number(item)));
|
|
27
|
-
}
|
|
28
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, decodeBinary(entry)]));
|
|
29
|
-
}
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
function pruneThreadEntries(record, threadId) {
|
|
33
|
-
for (const key of Object.keys(record)) {
|
|
34
|
-
if (key.includes(threadId)) {
|
|
35
|
-
delete record[key];
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
const value = record[key];
|
|
39
|
-
if (typeof value === "object" && value && !Array.isArray(value)) {
|
|
40
|
-
pruneThreadEntries(value, threadId);
|
|
41
|
-
if (Object.keys(value).length === 0) {
|
|
42
|
-
delete record[key];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
export class FileCheckpointSaver extends MemorySaver {
|
|
48
|
-
filePath;
|
|
49
|
-
loaded = false;
|
|
50
|
-
constructor(filePath) {
|
|
51
|
-
super();
|
|
52
|
-
this.filePath = filePath;
|
|
53
|
-
}
|
|
54
|
-
async ensureLoaded() {
|
|
55
|
-
if (this.loaded) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
const raw = await readFile(this.filePath, "utf8");
|
|
60
|
-
const parsed = JSON.parse(raw);
|
|
61
|
-
this.storage = decodeBinary(parsed.storage ?? {});
|
|
62
|
-
this.writes = decodeBinary(parsed.writes ?? {});
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
this.storage = {};
|
|
66
|
-
this.writes = {};
|
|
67
|
-
}
|
|
68
|
-
this.loaded = true;
|
|
69
|
-
}
|
|
70
|
-
async persist() {
|
|
71
|
-
await mkdir(path.dirname(this.filePath), { recursive: true });
|
|
72
|
-
await writeFile(this.filePath, JSON.stringify({
|
|
73
|
-
storage: this.storage,
|
|
74
|
-
writes: this.writes,
|
|
75
|
-
}, (_, value) => encodeBinary(value), 2), "utf8");
|
|
76
|
-
}
|
|
77
|
-
async getTuple(config) {
|
|
78
|
-
await this.ensureLoaded();
|
|
79
|
-
return super.getTuple(config);
|
|
80
|
-
}
|
|
81
|
-
async *list(config, options) {
|
|
82
|
-
await this.ensureLoaded();
|
|
83
|
-
for await (const item of super.list(config, options)) {
|
|
84
|
-
yield item;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
async put(config, checkpoint, metadata) {
|
|
88
|
-
await this.ensureLoaded();
|
|
89
|
-
const result = await super.put(config, checkpoint, metadata);
|
|
90
|
-
await this.persist();
|
|
91
|
-
return result;
|
|
92
|
-
}
|
|
93
|
-
async putWrites(config, writes, taskId) {
|
|
94
|
-
await this.ensureLoaded();
|
|
95
|
-
const result = await super.putWrites(config, writes, taskId);
|
|
96
|
-
await this.persist();
|
|
97
|
-
return result;
|
|
98
|
-
}
|
|
99
|
-
async deleteThread(threadId) {
|
|
100
|
-
await this.ensureLoaded();
|
|
101
|
-
pruneThreadEntries(this.storage, threadId);
|
|
102
|
-
pruneThreadEntries(this.writes, threadId);
|
|
103
|
-
await this.persist();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
export { FileCheckpointSaver as FileCheckpointer };
|
|
1
|
+
export * from "./maintenance/file-checkpoint-saver.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HarnessEvent, HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "
|
|
1
|
+
import type { HarnessEvent, HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "../../../contracts/types.js";
|
|
2
2
|
export declare class EventBus implements RuntimeEventSink {
|
|
3
3
|
private readonly sink;
|
|
4
4
|
publish(event: HarnessEvent): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HarnessEvent, HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "
|
|
1
|
+
import type { HarnessEvent, HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "../../../contracts/types.js";
|
|
2
2
|
export declare class RuntimeEventSinkImpl implements RuntimeEventSink {
|
|
3
3
|
private readonly emitter;
|
|
4
4
|
private readonly projections;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HarnessEvent, InternalApprovalRecord, MessageContent, RunResult } from "../../../contracts/types.js";
|
|
2
|
+
import type { RuntimePersistence } from "../../../persistence/types.js";
|
|
3
|
+
type EventPersistence = Pick<RuntimePersistence, "appendEvent" | "setRunState" | "createApproval" | "createArtifact">;
|
|
4
|
+
type EventRuntime = {
|
|
5
|
+
persistence: EventPersistence;
|
|
6
|
+
publishEvent: (event: HarnessEvent) => void;
|
|
7
|
+
trackBackgroundTask: (task: Promise<void>) => void;
|
|
8
|
+
backgroundEventTypes: ReadonlySet<string>;
|
|
9
|
+
};
|
|
10
|
+
export declare function emitHarnessEvent(runtime: EventRuntime, threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>, source?: HarnessEvent["source"]): Promise<HarnessEvent>;
|
|
11
|
+
export declare function emitRunCreatedEvent(runtime: EventRuntime, threadId: string, runId: string, payload: Record<string, unknown>): Promise<HarnessEvent>;
|
|
12
|
+
export declare function setRunStateAndEmitEvent(runtime: EventRuntime, threadId: string, runId: string, sequence: number, state: RunResult["state"], options: {
|
|
13
|
+
previousState: string | null;
|
|
14
|
+
checkpointRef?: string | null;
|
|
15
|
+
error?: string;
|
|
16
|
+
}): Promise<HarnessEvent>;
|
|
17
|
+
export declare function persistApproval(runtime: EventRuntime, threadId: string, runId: string, checkpointRef: string, input: MessageContent, interruptContent?: string): Promise<InternalApprovalRecord>;
|
|
18
|
+
export declare function requestApprovalAndEmitEvent(runtime: EventRuntime, threadId: string, runId: string, input: MessageContent, interruptContent: string | undefined, checkpointRef: string, sequence: number): Promise<{
|
|
19
|
+
approval: InternalApprovalRecord;
|
|
20
|
+
event: HarnessEvent;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function emitSyntheticFallbackEvent(runtime: EventRuntime, threadId: string, runId: string, selectedAgentId: string, error: unknown, sequence?: number): Promise<void>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { extractMessageText } from "../../../utils/message-content.js";
|
|
2
|
+
import { createHarnessEvent, createPendingApproval } from "../../support/harness-support.js";
|
|
3
|
+
export async function emitHarnessEvent(runtime, threadId, runId, sequence, eventType, payload, source = "runtime") {
|
|
4
|
+
const event = createHarnessEvent(threadId, runId, sequence, eventType, payload, source);
|
|
5
|
+
if (runtime.backgroundEventTypes.has(event.eventType)) {
|
|
6
|
+
runtime.trackBackgroundTask(runtime.persistence.appendEvent(event).catch(() => {
|
|
7
|
+
// Fail open for telemetry-style event persistence.
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
await runtime.persistence.appendEvent(event);
|
|
12
|
+
}
|
|
13
|
+
runtime.publishEvent(event);
|
|
14
|
+
return event;
|
|
15
|
+
}
|
|
16
|
+
export async function emitRunCreatedEvent(runtime, threadId, runId, payload) {
|
|
17
|
+
return emitHarnessEvent(runtime, threadId, runId, 1, "run.created", payload);
|
|
18
|
+
}
|
|
19
|
+
export async function setRunStateAndEmitEvent(runtime, threadId, runId, sequence, state, options) {
|
|
20
|
+
await runtime.persistence.setRunState(threadId, runId, state, options.checkpointRef ?? null);
|
|
21
|
+
return emitHarnessEvent(runtime, threadId, runId, sequence, "run.state.changed", {
|
|
22
|
+
previousState: options.previousState,
|
|
23
|
+
state,
|
|
24
|
+
checkpointRef: options.checkpointRef ?? null,
|
|
25
|
+
...(options.error ? { error: options.error } : {}),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export async function persistApproval(runtime, threadId, runId, checkpointRef, input, interruptContent) {
|
|
29
|
+
const approval = createPendingApproval(threadId, runId, checkpointRef, extractMessageText(input), interruptContent);
|
|
30
|
+
await runtime.persistence.createApproval(approval);
|
|
31
|
+
const artifact = await runtime.persistence.createArtifact(threadId, runId, {
|
|
32
|
+
artifactId: `artifact-approval-${runId}`,
|
|
33
|
+
kind: "approval-packet",
|
|
34
|
+
path: `artifacts/approval-${runId}.json`,
|
|
35
|
+
createdAt: approval.requestedAt,
|
|
36
|
+
}, approval);
|
|
37
|
+
await emitHarnessEvent(runtime, threadId, runId, 5, "artifact.created", {
|
|
38
|
+
artifactId: artifact.artifactId,
|
|
39
|
+
kind: artifact.kind,
|
|
40
|
+
path: artifact.path,
|
|
41
|
+
});
|
|
42
|
+
return approval;
|
|
43
|
+
}
|
|
44
|
+
export async function requestApprovalAndEmitEvent(runtime, threadId, runId, input, interruptContent, checkpointRef, sequence) {
|
|
45
|
+
const approval = await persistApproval(runtime, threadId, runId, checkpointRef, input, interruptContent);
|
|
46
|
+
const event = await emitHarnessEvent(runtime, threadId, runId, sequence, "approval.requested", {
|
|
47
|
+
approvalId: approval.approvalId,
|
|
48
|
+
pendingActionId: approval.pendingActionId,
|
|
49
|
+
toolName: approval.toolName,
|
|
50
|
+
toolCallId: approval.toolCallId,
|
|
51
|
+
allowedDecisions: approval.allowedDecisions,
|
|
52
|
+
checkpointRef,
|
|
53
|
+
});
|
|
54
|
+
return { approval, event };
|
|
55
|
+
}
|
|
56
|
+
export async function emitSyntheticFallbackEvent(runtime, threadId, runId, selectedAgentId, error, sequence = 3) {
|
|
57
|
+
await emitHarnessEvent(runtime, threadId, runId, sequence, "runtime.synthetic_fallback", {
|
|
58
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
59
|
+
selectedAgentId,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HarnessEvent, HarnessStreamItem, RunListeners, RunResult } from "../../../contracts/types.js";
|
|
2
|
+
export declare function emitOutputDeltaAndCreateItem(emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<HarnessEvent>, threadId: string, runId: string, agentId: string, content: string): Promise<HarnessStreamItem>;
|
|
3
|
+
export declare function createContentBlocksItem(threadId: string, runId: string, agentId: string, contentBlocks: unknown[]): HarnessStreamItem;
|
|
4
|
+
export declare function createToolResultKey(toolName: string, output: unknown, isError?: boolean): string;
|
|
5
|
+
export declare function dispatchRunListeners(stream: AsyncGenerator<HarnessStreamItem>, listeners: RunListeners, options: {
|
|
6
|
+
notifyListener: <T>(listener: ((value: T) => void | Promise<void>) | undefined, value: T) => Promise<void>;
|
|
7
|
+
getThread: (threadId: string) => Promise<{
|
|
8
|
+
currentState: RunResult["state"];
|
|
9
|
+
latestRunId: string;
|
|
10
|
+
entryAgentId: string;
|
|
11
|
+
runs: Array<{
|
|
12
|
+
agentId: string;
|
|
13
|
+
}>;
|
|
14
|
+
pendingDecision?: {
|
|
15
|
+
approvalId?: string;
|
|
16
|
+
pendingActionId?: string;
|
|
17
|
+
};
|
|
18
|
+
} | null>;
|
|
19
|
+
}): Promise<RunResult>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createFallbackRunResultFromLatestEvent, mergeRunResultOutput } from "../run/helpers.js";
|
|
2
|
+
export async function emitOutputDeltaAndCreateItem(emit, threadId, runId, agentId, content) {
|
|
3
|
+
await emit(threadId, runId, 3, "output.delta", {
|
|
4
|
+
content,
|
|
5
|
+
});
|
|
6
|
+
return {
|
|
7
|
+
type: "content",
|
|
8
|
+
threadId,
|
|
9
|
+
runId,
|
|
10
|
+
agentId,
|
|
11
|
+
content,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function createContentBlocksItem(threadId, runId, agentId, contentBlocks) {
|
|
15
|
+
return {
|
|
16
|
+
type: "content-blocks",
|
|
17
|
+
threadId,
|
|
18
|
+
runId,
|
|
19
|
+
agentId,
|
|
20
|
+
contentBlocks,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function createToolResultKey(toolName, output, isError) {
|
|
24
|
+
let serializedOutput = "";
|
|
25
|
+
try {
|
|
26
|
+
serializedOutput = JSON.stringify(output);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
serializedOutput = String(output);
|
|
30
|
+
}
|
|
31
|
+
return JSON.stringify([toolName, serializedOutput, isError === true]);
|
|
32
|
+
}
|
|
33
|
+
export async function dispatchRunListeners(stream, listeners, options) {
|
|
34
|
+
let latestEvent;
|
|
35
|
+
let latestResult;
|
|
36
|
+
let output = "";
|
|
37
|
+
for await (const item of stream) {
|
|
38
|
+
if (item.type === "event") {
|
|
39
|
+
latestEvent = item.event;
|
|
40
|
+
await options.notifyListener(listeners.onEvent, item.event);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (item.type === "upstream-event") {
|
|
44
|
+
await options.notifyListener(listeners.onUpstreamEvent, item.event);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (item.type === "result") {
|
|
48
|
+
latestResult = item.result;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (item.type === "content") {
|
|
52
|
+
output += item.content;
|
|
53
|
+
await options.notifyListener(listeners.onChunk, item.content);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (item.type === "content-blocks") {
|
|
57
|
+
await options.notifyListener(listeners.onContentBlocks, item.contentBlocks);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (item.type === "reasoning") {
|
|
61
|
+
await options.notifyListener(listeners.onReasoning, item.content);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (item.type === "step") {
|
|
65
|
+
await options.notifyListener(listeners.onStep, item.content);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (item.type === "tool-result") {
|
|
69
|
+
await options.notifyListener(listeners.onToolResult, {
|
|
70
|
+
toolName: item.toolName,
|
|
71
|
+
output: item.output,
|
|
72
|
+
isError: item.isError,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!latestEvent) {
|
|
77
|
+
throw new Error("run did not emit any events");
|
|
78
|
+
}
|
|
79
|
+
if (latestResult) {
|
|
80
|
+
return mergeRunResultOutput(latestResult, output);
|
|
81
|
+
}
|
|
82
|
+
const thread = await options.getThread(latestEvent.threadId);
|
|
83
|
+
if (!thread) {
|
|
84
|
+
throw new Error(`Unknown thread ${latestEvent.threadId}`);
|
|
85
|
+
}
|
|
86
|
+
return createFallbackRunResultFromLatestEvent({
|
|
87
|
+
latestEvent,
|
|
88
|
+
currentState: thread.currentState,
|
|
89
|
+
latestRunId: thread.latestRunId,
|
|
90
|
+
entryAgentId: thread.entryAgentId,
|
|
91
|
+
latestAgentId: thread.runs[0]?.agentId,
|
|
92
|
+
approvalId: thread.pendingDecision?.approvalId,
|
|
93
|
+
pendingActionId: thread.pendingDecision?.pendingActionId,
|
|
94
|
+
output,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from "./events/event-bus.js";
|
|
2
|
+
export * from "./events/event-sink.js";
|
|
3
|
+
export * from "./events/events.js";
|
|
4
|
+
export * from "./system/health-monitor.js";
|
|
5
|
+
export * from "./run/helpers.js";
|
|
6
|
+
export * from "./system/inventory.js";
|
|
7
|
+
export * from "./system/policy-engine.js";
|
|
8
|
+
export * from "./run/resources.js";
|
|
9
|
+
export * from "./run/resume.js";
|
|
10
|
+
export * from "./run/routing.js";
|
|
11
|
+
export * from "./run/run-lifecycle.js";
|
|
12
|
+
export * from "./run/run-queue.js";
|
|
13
|
+
export * from "./system/skill-requirements.js";
|
|
14
|
+
export * from "./system/store.js";
|
|
15
|
+
export * from "./events/streaming.js";
|
|
16
|
+
export * from "./system/thread-memory-sync.js";
|