@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.
Files changed (103) hide show
  1. package/dist/api.d.ts +5 -5
  2. package/dist/contracts/core.d.ts +9 -0
  3. package/dist/contracts/core.js +1 -0
  4. package/dist/contracts/runtime.d.ts +421 -0
  5. package/dist/contracts/runtime.js +1 -0
  6. package/dist/contracts/types.d.ts +3 -653
  7. package/dist/contracts/types.js +3 -1
  8. package/dist/contracts/workspace.d.ts +229 -0
  9. package/dist/contracts/workspace.js +1 -0
  10. package/dist/package-version.d.ts +1 -1
  11. package/dist/package-version.js +1 -1
  12. package/dist/runtime/adapter/compat/deepagent-compat.d.ts +16 -0
  13. package/dist/runtime/adapter/compat/deepagent-compat.js +45 -0
  14. package/dist/runtime/adapter/compat/openai-compatible.d.ts +2 -0
  15. package/dist/runtime/adapter/compat/openai-compatible.js +43 -0
  16. package/dist/runtime/adapter/index.d.ts +15 -0
  17. package/dist/runtime/adapter/index.js +15 -0
  18. package/dist/runtime/{langgraph-profiles.d.ts → adapter/langgraph/profiles.d.ts} +1 -1
  19. package/dist/runtime/adapter/model/invocation-request.d.ts +10 -0
  20. package/dist/runtime/adapter/model/invocation-request.js +46 -0
  21. package/dist/runtime/adapter/model/message-assembly.d.ts +6 -0
  22. package/dist/runtime/adapter/model/message-assembly.js +21 -0
  23. package/dist/runtime/adapter/model/model-providers.d.ts +2 -0
  24. package/dist/runtime/adapter/model/model-providers.js +27 -0
  25. package/dist/runtime/adapter/resilience.d.ts +12 -0
  26. package/dist/runtime/adapter/resilience.js +60 -0
  27. package/dist/runtime/{declared-middleware.d.ts → adapter/tool/declared-middleware.d.ts} +1 -1
  28. package/dist/runtime/adapter/tool/interrupt-policy.d.ts +8 -0
  29. package/dist/runtime/adapter/tool/interrupt-policy.js +34 -0
  30. package/dist/runtime/adapter/tool/provider-tool.d.ts +2 -0
  31. package/dist/runtime/adapter/tool/provider-tool.js +25 -0
  32. package/dist/runtime/adapter/tool/resolved-tool.d.ts +18 -0
  33. package/dist/runtime/adapter/tool/resolved-tool.js +62 -0
  34. package/dist/runtime/adapter/tool/tool-arguments.d.ts +7 -0
  35. package/dist/runtime/adapter/tool/tool-arguments.js +87 -0
  36. package/dist/runtime/{tool-hitl.d.ts → adapter/tool/tool-hitl.d.ts} +2 -2
  37. package/dist/runtime/adapter/tool/tool-name-mapping.d.ts +13 -0
  38. package/dist/runtime/adapter/tool/tool-name-mapping.js +101 -0
  39. package/dist/runtime/agent-runtime-adapter.d.ts +5 -20
  40. package/dist/runtime/agent-runtime-adapter.js +33 -536
  41. package/dist/runtime/checkpoint-maintenance.d.ts +1 -45
  42. package/dist/runtime/checkpoint-maintenance.js +1 -259
  43. package/dist/runtime/file-checkpoint-saver.d.ts +1 -20
  44. package/dist/runtime/file-checkpoint-saver.js +1 -106
  45. package/dist/runtime/{event-bus.d.ts → harness/events/event-bus.d.ts} +1 -1
  46. package/dist/runtime/{event-sink.d.ts → harness/events/event-sink.d.ts} +1 -1
  47. package/dist/runtime/{event-sink.js → harness/events/event-sink.js} +1 -1
  48. package/dist/runtime/harness/events/events.d.ts +23 -0
  49. package/dist/runtime/harness/events/events.js +61 -0
  50. package/dist/runtime/harness/events/streaming.d.ts +19 -0
  51. package/dist/runtime/harness/events/streaming.js +96 -0
  52. package/dist/runtime/harness/index.d.ts +16 -0
  53. package/dist/runtime/harness/index.js +16 -0
  54. package/dist/runtime/harness/run/helpers.d.ts +33 -0
  55. package/dist/runtime/harness/run/helpers.js +74 -0
  56. package/dist/runtime/harness/run/resources.d.ts +7 -0
  57. package/dist/runtime/harness/run/resources.js +58 -0
  58. package/dist/runtime/harness/run/resume.d.ts +6 -0
  59. package/dist/runtime/harness/run/resume.js +56 -0
  60. package/dist/runtime/harness/run/routing.d.ts +12 -0
  61. package/dist/runtime/harness/run/routing.js +47 -0
  62. package/dist/runtime/harness/run/run-lifecycle.d.ts +37 -0
  63. package/dist/runtime/harness/run/run-lifecycle.js +109 -0
  64. package/dist/runtime/harness/run/run-queue.d.ts +17 -0
  65. package/dist/runtime/harness/run/run-queue.js +43 -0
  66. package/dist/runtime/{health-monitor.d.ts → harness/system/health-monitor.d.ts} +3 -3
  67. package/dist/runtime/{health-monitor.js → harness/system/health-monitor.js} +2 -2
  68. package/dist/runtime/{inventory.d.ts → harness/system/inventory.d.ts} +2 -2
  69. package/dist/runtime/{inventory.js → harness/system/inventory.js} +4 -4
  70. package/dist/runtime/{policy-engine.d.ts → harness/system/policy-engine.d.ts} +1 -1
  71. package/dist/runtime/{policy-engine.js → harness/system/policy-engine.js} +1 -1
  72. package/dist/runtime/{skill-requirements.d.ts → harness/system/skill-requirements.d.ts} +1 -1
  73. package/dist/runtime/{skill-requirements.js → harness/system/skill-requirements.js} +1 -1
  74. package/dist/runtime/{thread-memory-sync.d.ts → harness/system/thread-memory-sync.d.ts} +2 -2
  75. package/dist/runtime/{thread-memory-sync.js → harness/system/thread-memory-sync.js} +1 -1
  76. package/dist/runtime/harness.d.ts +2 -8
  77. package/dist/runtime/harness.js +143 -493
  78. package/dist/runtime/index.d.ts +7 -7
  79. package/dist/runtime/index.js +7 -7
  80. package/dist/runtime/maintenance/checkpoint-maintenance.d.ts +45 -0
  81. package/dist/runtime/maintenance/checkpoint-maintenance.js +259 -0
  82. package/dist/runtime/maintenance/file-checkpoint-saver.d.ts +20 -0
  83. package/dist/runtime/maintenance/file-checkpoint-saver.js +106 -0
  84. package/dist/runtime/maintenance/index.d.ts +4 -0
  85. package/dist/runtime/maintenance/index.js +4 -0
  86. package/dist/runtime/{runtime-record-maintenance.d.ts → maintenance/runtime-record-maintenance.d.ts} +1 -1
  87. package/dist/runtime/{runtime-record-maintenance.js → maintenance/runtime-record-maintenance.js} +2 -2
  88. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +9 -0
  89. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +39 -0
  90. package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +1 -9
  91. package/dist/runtime/sqlite-maintained-checkpoint-saver.js +1 -39
  92. package/dist/runtime/support/harness-support.d.ts +4 -4
  93. package/dist/runtime/support/runtime-factories.d.ts +1 -1
  94. package/dist/runtime/support/runtime-factories.js +1 -1
  95. package/package.json +4 -4
  96. /package/dist/runtime/{langgraph-presets.d.ts → adapter/langgraph/presets.d.ts} +0 -0
  97. /package/dist/runtime/{langgraph-presets.js → adapter/langgraph/presets.js} +0 -0
  98. /package/dist/runtime/{langgraph-profiles.js → adapter/langgraph/profiles.js} +0 -0
  99. /package/dist/runtime/{declared-middleware.js → adapter/tool/declared-middleware.js} +0 -0
  100. /package/dist/runtime/{tool-hitl.js → adapter/tool/tool-hitl.js} +0 -0
  101. /package/dist/runtime/{event-bus.js → harness/events/event-bus.js} +0 -0
  102. /package/dist/runtime/{store.d.ts → harness/system/store.d.ts} +0 -0
  103. /package/dist/runtime/{store.js → harness/system/store.js} +0 -0
@@ -1,15 +1,15 @@
1
1
  export { AgentRuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX } from "./agent-runtime-adapter.js";
2
- export { EventBus } from "./event-bus.js";
3
- export { createRuntimeEventSink, RuntimeEventSinkImpl } from "./event-sink.js";
2
+ export { EventBus } from "./harness/events/event-bus.js";
3
+ export { createRuntimeEventSink, RuntimeEventSinkImpl } from "./harness/events/event-sink.js";
4
4
  export { FileCheckpointSaver } from "./file-checkpoint-saver.js";
5
5
  export { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, maintainSqliteCheckpoints, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
6
6
  export { ManagedSqliteSaver } from "./sqlite-maintained-checkpoint-saver.js";
7
7
  export { AgentHarnessRuntime, AgentHarness } from "./harness.js";
8
- export { describeWorkspaceInventory, findAgentBinding, listAgentSkills, listAgentTools, listAvailableAgents, listSpecialists, } from "./inventory.js";
9
- export { assessOpenClawRequirements, assessSkillRequirements, } from "./skill-requirements.js";
8
+ export { describeWorkspaceInventory, findAgentBinding, listAgentSkills, listAgentTools, listAvailableAgents, listSpecialists, } from "./harness/system/inventory.js";
9
+ export { assessOpenClawRequirements, assessSkillRequirements, } from "./harness/system/skill-requirements.js";
10
10
  export * from "./parsing/index.js";
11
- export { PolicyEngine } from "./policy-engine.js";
12
- export { createInMemoryStore, FileBackedStore } from "./store.js";
11
+ export { PolicyEngine } from "./harness/system/policy-engine.js";
12
+ export { createInMemoryStore, FileBackedStore } from "./harness/system/store.js";
13
13
  export * from "./support/index.js";
14
- export { ThreadMemorySync } from "./thread-memory-sync.js";
14
+ export { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
15
15
  export type { HarnessEventListener, HarnessEventProjection, RuntimeEventSink } from "../contracts/types.js";
@@ -1,14 +1,14 @@
1
1
  export { AgentRuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX } from "./agent-runtime-adapter.js";
2
- export { EventBus } from "./event-bus.js";
3
- export { createRuntimeEventSink, RuntimeEventSinkImpl } from "./event-sink.js";
2
+ export { EventBus } from "./harness/events/event-bus.js";
3
+ export { createRuntimeEventSink, RuntimeEventSinkImpl } from "./harness/events/event-sink.js";
4
4
  export { FileCheckpointSaver } from "./file-checkpoint-saver.js";
5
5
  export { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, maintainSqliteCheckpoints, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
6
6
  export { ManagedSqliteSaver } from "./sqlite-maintained-checkpoint-saver.js";
7
7
  export { AgentHarnessRuntime, AgentHarness } from "./harness.js";
8
- export { describeWorkspaceInventory, findAgentBinding, listAgentSkills, listAgentTools, listAvailableAgents, listSpecialists, } from "./inventory.js";
9
- export { assessOpenClawRequirements, assessSkillRequirements, } from "./skill-requirements.js";
8
+ export { describeWorkspaceInventory, findAgentBinding, listAgentSkills, listAgentTools, listAvailableAgents, listSpecialists, } from "./harness/system/inventory.js";
9
+ export { assessOpenClawRequirements, assessSkillRequirements, } from "./harness/system/skill-requirements.js";
10
10
  export * from "./parsing/index.js";
11
- export { PolicyEngine } from "./policy-engine.js";
12
- export { createInMemoryStore, FileBackedStore } from "./store.js";
11
+ export { PolicyEngine } from "./harness/system/policy-engine.js";
12
+ export { createInMemoryStore, FileBackedStore } from "./harness/system/store.js";
13
13
  export * from "./support/index.js";
14
- export { ThreadMemorySync } from "./thread-memory-sync.js";
14
+ export { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
@@ -0,0 +1,45 @@
1
+ import type { WorkspaceBundle } from "../../contracts/types.js";
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 {};
@@ -0,0 +1,259 @@
1
+ import path from "node:path";
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
+ }
@@ -0,0 +1,20 @@
1
+ import { MemorySaver } from "@langchain/langgraph";
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 };
@@ -0,0 +1,106 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
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 };
@@ -0,0 +1,4 @@
1
+ export * from "./checkpoint-maintenance.js";
2
+ export * from "./file-checkpoint-saver.js";
3
+ export * from "./runtime-record-maintenance.js";
4
+ export * from "./sqlite-maintained-checkpoint-saver.js";
@@ -0,0 +1,4 @@
1
+ export * from "./checkpoint-maintenance.js";
2
+ export * from "./file-checkpoint-saver.js";
3
+ export * from "./runtime-record-maintenance.js";
4
+ export * from "./sqlite-maintained-checkpoint-saver.js";
@@ -1,4 +1,4 @@
1
- import type { WorkspaceBundle } from "../contracts/types.js";
1
+ import type { WorkspaceBundle } from "../../contracts/types.js";
2
2
  type RuntimeRecordMaintenanceConfig = {
3
3
  enabled: boolean;
4
4
  schedule: {
@@ -1,8 +1,8 @@
1
1
  import path from "node:path";
2
2
  import { rm } from "node:fs/promises";
3
3
  import { createClient } from "@libsql/client";
4
- import { fileExists } from "../utils/fs.js";
5
- import { getRuntimeDefaults } from "../workspace/support/workspace-ref-utils.js";
4
+ import { fileExists } from "../../utils/fs.js";
5
+ import { getRuntimeDefaults } from "../../workspace/support/workspace-ref-utils.js";
6
6
  function asObject(value) {
7
7
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
8
8
  }
@@ -0,0 +1,9 @@
1
+ import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
+ import type { RunnableConfig } from "@langchain/core/runnables";
3
+ export declare class ManagedSqliteSaver extends SqliteSaver {
4
+ constructor(db: ConstructorParameters<typeof SqliteSaver>[0]);
5
+ prepareMaintenance(): void;
6
+ setup(): void;
7
+ put(config: RunnableConfig, checkpoint: Parameters<SqliteSaver["put"]>[1], metadata: Parameters<SqliteSaver["put"]>[2]): Promise<RunnableConfig<Record<string, any>>>;
8
+ deleteThread(threadId: string): Promise<void>;
9
+ }
@@ -0,0 +1,39 @@
1
+ import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
+ export class ManagedSqliteSaver extends SqliteSaver {
3
+ constructor(db) {
4
+ super(db);
5
+ }
6
+ prepareMaintenance() {
7
+ this.setup();
8
+ }
9
+ setup() {
10
+ super.setup();
11
+ this.db.exec(`
12
+ CREATE TABLE IF NOT EXISTS checkpoint_maintenance_meta (
13
+ thread_id TEXT NOT NULL,
14
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
15
+ checkpoint_id TEXT NOT NULL,
16
+ created_at_ms INTEGER NOT NULL,
17
+ PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
18
+ );`);
19
+ }
20
+ async put(config, checkpoint, metadata) {
21
+ const result = await super.put(config, checkpoint, metadata);
22
+ const threadId = result.configurable?.thread_id;
23
+ const checkpointNs = result.configurable?.checkpoint_ns ?? "";
24
+ const checkpointId = result.configurable?.checkpoint_id;
25
+ if (!threadId || !checkpointId) {
26
+ throw new Error("Missing checkpoint identity after SqliteSaver.put");
27
+ }
28
+ this.db
29
+ .prepare(`INSERT OR IGNORE INTO checkpoint_maintenance_meta
30
+ (thread_id, checkpoint_ns, checkpoint_id, created_at_ms)
31
+ VALUES (?, ?, ?, ?)`)
32
+ .run(threadId, checkpointNs, checkpointId, Date.now());
33
+ return result;
34
+ }
35
+ async deleteThread(threadId) {
36
+ await super.deleteThread(threadId);
37
+ this.db.prepare(`DELETE FROM checkpoint_maintenance_meta WHERE thread_id = ?`).run(threadId);
38
+ }
39
+ }
@@ -1,9 +1 @@
1
- import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
- import type { RunnableConfig } from "@langchain/core/runnables";
3
- export declare class ManagedSqliteSaver extends SqliteSaver {
4
- constructor(db: ConstructorParameters<typeof SqliteSaver>[0]);
5
- prepareMaintenance(): void;
6
- setup(): void;
7
- put(config: RunnableConfig, checkpoint: Parameters<SqliteSaver["put"]>[1], metadata: Parameters<SqliteSaver["put"]>[2]): Promise<RunnableConfig<Record<string, any>>>;
8
- deleteThread(threadId: string): Promise<void>;
9
- }
1
+ export * from "./maintenance/sqlite-maintained-checkpoint-saver.js";
@@ -1,39 +1 @@
1
- import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
- export class ManagedSqliteSaver extends SqliteSaver {
3
- constructor(db) {
4
- super(db);
5
- }
6
- prepareMaintenance() {
7
- this.setup();
8
- }
9
- setup() {
10
- super.setup();
11
- this.db.exec(`
12
- CREATE TABLE IF NOT EXISTS checkpoint_maintenance_meta (
13
- thread_id TEXT NOT NULL,
14
- checkpoint_ns TEXT NOT NULL DEFAULT '',
15
- checkpoint_id TEXT NOT NULL,
16
- created_at_ms INTEGER NOT NULL,
17
- PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
18
- );`);
19
- }
20
- async put(config, checkpoint, metadata) {
21
- const result = await super.put(config, checkpoint, metadata);
22
- const threadId = result.configurable?.thread_id;
23
- const checkpointNs = result.configurable?.checkpoint_ns ?? "";
24
- const checkpointId = result.configurable?.checkpoint_id;
25
- if (!threadId || !checkpointId) {
26
- throw new Error("Missing checkpoint identity after SqliteSaver.put");
27
- }
28
- this.db
29
- .prepare(`INSERT OR IGNORE INTO checkpoint_maintenance_meta
30
- (thread_id, checkpoint_ns, checkpoint_id, created_at_ms)
31
- VALUES (?, ?, ?, ?)`)
32
- .run(threadId, checkpointNs, checkpointId, Date.now());
33
- return result;
34
- }
35
- async deleteThread(threadId) {
36
- await super.deleteThread(threadId);
37
- this.db.prepare(`DELETE FROM checkpoint_maintenance_meta WHERE thread_id = ?`).run(threadId);
38
- }
39
- }
1
+ export * from "./maintenance/sqlite-maintained-checkpoint-saver.js";
@@ -21,8 +21,8 @@ export declare function heuristicRoute(input: string, primaryBinding?: {
21
21
  export declare function createHarnessEvent(threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>, source?: HarnessEvent["source"]): HarnessEvent;
22
22
  export declare function createPendingApproval(threadId: string, runId: string, checkpointRef: string, input: string, interruptContent?: string): InternalApprovalRecord;
23
23
  export declare function inferRoutingBindings(workspace: WorkspaceBundle): {
24
- primaryBinding: import("../../contracts/types.js").CompiledAgentBinding;
25
- secondaryBinding: import("../../contracts/types.js").CompiledAgentBinding | undefined;
26
- researchBinding: import("../../contracts/types.js").CompiledAgentBinding | undefined;
27
- hostBindings: import("../../contracts/types.js").CompiledAgentBinding[];
24
+ primaryBinding: import("../../contracts/workspace.js").CompiledAgentBinding;
25
+ secondaryBinding: import("../../contracts/workspace.js").CompiledAgentBinding | undefined;
26
+ researchBinding: import("../../contracts/workspace.js").CompiledAgentBinding | undefined;
27
+ hostBindings: import("../../contracts/workspace.js").CompiledAgentBinding[];
28
28
  };
@@ -1,3 +1,3 @@
1
- import { type StoreLike } from "../store.js";
1
+ import { type StoreLike } from "../harness/system/store.js";
2
2
  export declare function createStoreForConfig(storeConfig: Record<string, unknown>, runRoot: string): StoreLike;
3
3
  export declare function createCheckpointerForConfig(checkpointerConfig: Record<string, unknown> | boolean, runRoot: string): unknown;
@@ -3,7 +3,7 @@ import { MemorySaver } from "@langchain/langgraph";
3
3
  import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
4
4
  import { FileCheckpointSaver } from "../file-checkpoint-saver.js";
5
5
  import { ManagedSqliteSaver } from "../sqlite-maintained-checkpoint-saver.js";
6
- import { createInMemoryStore, FileBackedStore } from "../store.js";
6
+ import { createInMemoryStore, FileBackedStore } from "../harness/system/store.js";
7
7
  export function createStoreForConfig(storeConfig, runRoot) {
8
8
  const kind = typeof storeConfig.kind === "string" ? storeConfig.kind : "FileStore";
9
9
  switch (kind) {