@botbotgo/agent-harness 0.0.153 → 0.0.155

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.
@@ -0,0 +1,371 @@
1
+ import path from "node:path";
2
+ import { extractMessageText } from "../../../utils/message-content.js";
3
+ import { createResolvedModel } from "../../adapter/model/model-providers.js";
4
+ import { FileBackedStore } from "./store.js";
5
+ import { compileModel } from "../../../workspace/resource-compilers.js";
6
+ import { resolveRefId } from "../../../workspace/support/workspace-ref-utils.js";
7
+ const FORMATION_EVENT_TYPES = new Set(["run.state.changed", "approval.resolved"]);
8
+ function asRecord(value) {
9
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
10
+ }
11
+ function asBoolean(value) {
12
+ return typeof value === "boolean" ? value : undefined;
13
+ }
14
+ function asPositiveInteger(value) {
15
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
16
+ }
17
+ function asNonEmptyString(value) {
18
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
19
+ }
20
+ function asMemoryScopes(value) {
21
+ if (!Array.isArray(value)) {
22
+ return undefined;
23
+ }
24
+ const scopes = value
25
+ .filter((item) => typeof item === "string")
26
+ .filter((item) => ["thread", "agent", "workspace", "user", "project"].includes(item));
27
+ return scopes.length > 0 ? Array.from(new Set(scopes)) : undefined;
28
+ }
29
+ function normalizeScopeList(scopes) {
30
+ return Array.from(new Set(scopes));
31
+ }
32
+ function asStrategy(value) {
33
+ return value === "rules" || value === "model" ? value : undefined;
34
+ }
35
+ export function readRuntimeMemoryFormationConfig(runtimeMemory, workspaceRoot) {
36
+ if (runtimeMemory?.enabled !== true) {
37
+ return undefined;
38
+ }
39
+ const formation = asRecord(runtimeMemory.formation);
40
+ const hotPath = asRecord(formation?.hotPath);
41
+ const manager = asRecord(formation?.manager);
42
+ const background = asRecord(formation?.background);
43
+ const ingestion = asRecord(runtimeMemory.ingestion);
44
+ const workspaceBaseName = path.basename(workspaceRoot) || "agent-harness";
45
+ const resolved = {
46
+ enabled: true,
47
+ hotPath: {
48
+ enabled: asBoolean(hotPath?.enabled) ?? true,
49
+ },
50
+ manager: {
51
+ enabled: asBoolean(manager?.enabled) ?? true,
52
+ strategy: asStrategy(manager?.strategy) ?? "rules",
53
+ modelRef: asNonEmptyString(manager?.modelRef),
54
+ maxContextRecords: asPositiveInteger(manager?.maxContextRecords) ?? 12,
55
+ },
56
+ background: {
57
+ enabled: asBoolean(background?.enabled) ?? true,
58
+ maxMessagesPerRun: asPositiveInteger(background?.maxMessagesPerRun) ?? asPositiveInteger(ingestion?.maxMessagesPerRun) ?? 40,
59
+ scopes: normalizeScopeList(asMemoryScopes(background?.scopes) ?? ["thread"]),
60
+ stateStorePath: asNonEmptyString(background?.stateStorePath) ?? `${workspaceBaseName}-memory-formation-state.json`,
61
+ writeOnApprovalResolution: asBoolean(background?.writeOnApprovalResolution) ?? asBoolean(ingestion?.writeOnApprovalResolution) ?? true,
62
+ writeOnRunCompletion: asBoolean(background?.writeOnRunCompletion) ?? asBoolean(ingestion?.writeOnRunCompletion) ?? true,
63
+ },
64
+ };
65
+ if (!resolved.hotPath.enabled && !resolved.background.enabled) {
66
+ return undefined;
67
+ }
68
+ return resolved;
69
+ }
70
+ function excerpt(message) {
71
+ if (!message?.content) {
72
+ return undefined;
73
+ }
74
+ const normalized = extractMessageText(message.content).replace(/\s+/g, " ").trim();
75
+ if (!normalized) {
76
+ return undefined;
77
+ }
78
+ return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
79
+ }
80
+ function summarizeApprovals(approvals) {
81
+ if (approvals.length === 0) {
82
+ return "No approvals were recorded in this run.";
83
+ }
84
+ return approvals
85
+ .map((approval) => `${approval.toolName} (${approval.status})`)
86
+ .join(", ");
87
+ }
88
+ export function createBackgroundMemoryCandidates(input) {
89
+ const latestUser = excerpt(input.messages.filter((message) => message.role === "user").at(-1));
90
+ const latestAssistant = excerpt(input.messages.filter((message) => message.role === "assistant").at(-1));
91
+ const userContext = latestUser ?? "No durable user context captured.";
92
+ const assistantContext = latestAssistant ?? "No durable assistant response captured.";
93
+ const approvals = summarizeApprovals(input.approvals);
94
+ const summarySeed = latestUser ?? latestAssistant ?? `Run ${input.runId}`;
95
+ const baseSummary = summarySeed.length > 120 ? `${summarySeed.slice(0, 117)}...` : summarySeed;
96
+ const sourceRefBase = `runtime://threads/${input.thread.threadId}/runs/${input.runId}/background-reflection`;
97
+ return input.scopes.map((scope) => ({
98
+ kind: "episodic",
99
+ scope,
100
+ sourceType: "runtime-reflection",
101
+ sourceRef: `${sourceRefBase}#${scope}`,
102
+ summary: `${baseSummary} (${scope})`,
103
+ content: [
104
+ `Run ${input.runId} completed for thread ${input.thread.threadId}.`,
105
+ `Trigger: ${input.trigger}.`,
106
+ `Latest user context: ${userContext}`,
107
+ `Latest assistant context: ${assistantContext}`,
108
+ `Approvals: ${approvals}`,
109
+ ].join("\n"),
110
+ confidence: 0.72,
111
+ observedAt: input.recordedAt,
112
+ tags: ["background-reflection", "langmem-aligned", scope, input.trigger],
113
+ }));
114
+ }
115
+ function normalizeKind(value, fallback) {
116
+ if (value === "semantic" || value === "episodic" || value === "procedural") {
117
+ return value;
118
+ }
119
+ return fallback;
120
+ }
121
+ function normalizeScope(value, fallback) {
122
+ if (value === "thread" || value === "agent" || value === "workspace" || value === "user" || value === "project") {
123
+ return value;
124
+ }
125
+ return fallback;
126
+ }
127
+ function normalizeTags(value, fallback) {
128
+ if (!Array.isArray(value)) {
129
+ return fallback;
130
+ }
131
+ const tags = value.filter((item) => typeof item === "string" && item.trim().length > 0);
132
+ return tags.length > 0 ? tags : fallback;
133
+ }
134
+ function normalizeConfidence(value, fallback) {
135
+ if (typeof value !== "number" || Number.isNaN(value)) {
136
+ return fallback;
137
+ }
138
+ return Math.min(1, Math.max(0, value));
139
+ }
140
+ function extractText(value) {
141
+ if (typeof value === "string") {
142
+ return value;
143
+ }
144
+ if (typeof value === "object" && value !== null && "content" in value && typeof value.content === "string") {
145
+ return String(value.content);
146
+ }
147
+ return undefined;
148
+ }
149
+ function tryParseJsonObject(text) {
150
+ try {
151
+ const parsed = JSON.parse(text);
152
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
153
+ }
154
+ catch {
155
+ const match = text.match(/\{[\s\S]*\}/);
156
+ if (!match) {
157
+ return null;
158
+ }
159
+ try {
160
+ const parsed = JSON.parse(match[0]);
161
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ }
167
+ }
168
+ function renderManagerPrompt(input) {
169
+ const existing = input.existingRecords.length === 0
170
+ ? "(none)"
171
+ : input.existingRecords
172
+ .map((record) => `- scope=${record.scope}; kind=${record.kind}; summary=${record.summary}; status=${record.status}; content=${record.content.replace(/\s+/g, " ").slice(0, 220)}`)
173
+ .join("\n");
174
+ return [
175
+ "You are the runtime memory manager.",
176
+ "Decide whether a candidate should be stored as durable memory and refine it if appropriate.",
177
+ "Return JSON only.",
178
+ "",
179
+ "Rules:",
180
+ '- Store only durable reusable knowledge. Reject transient chatter, scratchpad, or duplication without added value.',
181
+ '- Prefer semantic/episodic/procedural kinds only.',
182
+ '- Prefer scopes thread/agent/workspace/user/project only.',
183
+ '- If the candidate should not be stored, return {"store": false, "reason": "..."}',
184
+ '- If it should be stored, return {"store": true, "content": "...", "summary": "...", "kind": "...", "scope": "...", "tags": ["..."], "confidence": 0.0}',
185
+ "",
186
+ `threadId=${input.threadId}`,
187
+ `runId=${input.runId}`,
188
+ "",
189
+ "Candidate:",
190
+ JSON.stringify(input.candidate, null, 2),
191
+ "",
192
+ "Existing relevant records:",
193
+ existing,
194
+ ].join("\n");
195
+ }
196
+ export async function runModelMemoryManager(input) {
197
+ const resolvedModel = await createResolvedModel(input.model, input.modelResolver);
198
+ const invoker = resolvedModel;
199
+ if (typeof invoker.invoke !== "function") {
200
+ return input.candidates;
201
+ }
202
+ const transformed = [];
203
+ for (const candidate of input.candidates) {
204
+ const prompt = renderManagerPrompt({
205
+ candidate,
206
+ threadId: input.threadId,
207
+ runId: input.runId,
208
+ existingRecords: input.existingRecords,
209
+ });
210
+ const response = await invoker.invoke(prompt, {});
211
+ const parsed = tryParseJsonObject(extractText(response) ?? "");
212
+ if (!parsed) {
213
+ transformed.push(candidate);
214
+ continue;
215
+ }
216
+ if (parsed.store === false) {
217
+ continue;
218
+ }
219
+ transformed.push({
220
+ ...candidate,
221
+ content: typeof parsed.content === "string" && parsed.content.trim().length > 0 ? parsed.content.trim() : candidate.content,
222
+ summary: typeof parsed.summary === "string" && parsed.summary.trim().length > 0 ? parsed.summary.trim() : candidate.summary,
223
+ kind: normalizeKind(parsed.kind, normalizeKind(candidate.kind, undefined)),
224
+ scope: normalizeScope(parsed.scope, normalizeScope(candidate.scope, undefined)),
225
+ tags: normalizeTags(parsed.tags, candidate.tags),
226
+ confidence: normalizeConfidence(parsed.confidence, candidate.confidence),
227
+ observedAt: candidate.observedAt ?? input.recordedAt,
228
+ });
229
+ }
230
+ return transformed;
231
+ }
232
+ export function createRuntimeMemoryManager(input) {
233
+ return {
234
+ async transform({ candidates, binding, threadId, runId, recordedAt, existingRecords }) {
235
+ if (input.config.manager.enabled !== true || candidates.length === 0) {
236
+ return candidates;
237
+ }
238
+ const contextRecords = existingRecords
239
+ .filter((record) => record.status === "active")
240
+ .slice(-input.config.manager.maxContextRecords);
241
+ if (input.config.manager.strategy !== "model") {
242
+ return candidates;
243
+ }
244
+ if (!binding.langchainAgentParams?.model) {
245
+ return candidates;
246
+ }
247
+ const primaryModel = (() => {
248
+ if (!input.config.manager.modelRef) {
249
+ return binding.langchainAgentParams.model;
250
+ }
251
+ const configured = input.workspace.models.get(resolveRefId(input.config.manager.modelRef));
252
+ return configured ? compileModel(configured) : binding.langchainAgentParams.model;
253
+ })();
254
+ return runModelMemoryManager({
255
+ workspace: input.workspace,
256
+ binding,
257
+ model: primaryModel,
258
+ candidates,
259
+ threadId,
260
+ runId,
261
+ recordedAt,
262
+ existingRecords: contextRecords,
263
+ modelResolver: input.modelResolver,
264
+ });
265
+ },
266
+ };
267
+ }
268
+ function fingerprintMessages(messages, scopes) {
269
+ const serialized = messages
270
+ .map((message) => `${message.role}\n${message.createdAt}\n${extractMessageText(message.content)}`)
271
+ .join("\n---\n");
272
+ return `${scopes.join(",")}::${serialized}`;
273
+ }
274
+ export class RuntimeMemoryFormationSync {
275
+ persistence;
276
+ config;
277
+ writer;
278
+ stateStore;
279
+ options;
280
+ pending = new Set();
281
+ syncChain = Promise.resolve();
282
+ name = "runtime-memory-formation-sync";
283
+ constructor(persistence, config, writer, runRoot, stateStore = new FileBackedStore(path.join(runRoot, config.background.stateStorePath)), options = {}) {
284
+ this.persistence = persistence;
285
+ this.config = config;
286
+ this.writer = writer;
287
+ this.stateStore = stateStore;
288
+ this.options = options;
289
+ }
290
+ shouldHandle(event) {
291
+ if (!this.config.background.enabled || !FORMATION_EVENT_TYPES.has(event.eventType)) {
292
+ return false;
293
+ }
294
+ if (event.eventType === "approval.resolved") {
295
+ return this.config.background.writeOnApprovalResolution;
296
+ }
297
+ return this.config.background.writeOnRunCompletion && event.payload.state === "completed";
298
+ }
299
+ async handleEvent(event) {
300
+ if (!this.shouldHandle(event)) {
301
+ return;
302
+ }
303
+ const trigger = event.eventType === "approval.resolved" ? "approval.resolved" : "run.completed";
304
+ const task = this.syncChain
305
+ .then(() => this.reflectRun(event.threadId, event.runId, trigger, event.timestamp))
306
+ .catch(() => {
307
+ // Fail open: reflection should not block runtime progress.
308
+ });
309
+ this.syncChain = task
310
+ .catch(() => {
311
+ // Fail open: reflection should not block runtime progress.
312
+ })
313
+ .finally(() => {
314
+ this.pending.delete(task);
315
+ });
316
+ this.pending.add(task);
317
+ }
318
+ async reflectRun(threadId, runId, trigger, recordedAt) {
319
+ const [thread, run, allMessages, approvals] = await Promise.all([
320
+ this.persistence.getSession(threadId),
321
+ this.persistence.getRun(runId),
322
+ this.persistence.listThreadMessages(threadId, this.config.background.maxMessagesPerRun),
323
+ this.persistence.getRunApprovals(threadId, runId),
324
+ ]);
325
+ if (!thread || !run) {
326
+ return;
327
+ }
328
+ const messages = allMessages.filter((message) => message.runId === runId);
329
+ if (messages.length === 0) {
330
+ return;
331
+ }
332
+ const fingerprint = fingerprintMessages(messages, this.config.background.scopes);
333
+ const namespace = ["memories", "formation", "threads", threadId, "runs"];
334
+ const cursor = await this.stateStore.get(namespace, runId);
335
+ const existing = cursor?.value;
336
+ if (existing?.fingerprint === fingerprint && existing.trigger === trigger) {
337
+ return;
338
+ }
339
+ const candidates = createBackgroundMemoryCandidates({
340
+ thread,
341
+ runId,
342
+ agentId: run.agentId ?? thread.agentId,
343
+ trigger,
344
+ recordedAt,
345
+ messages,
346
+ approvals,
347
+ scopes: this.config.background.scopes,
348
+ });
349
+ if (candidates.length === 0) {
350
+ return;
351
+ }
352
+ await this.writer({
353
+ candidates,
354
+ threadId,
355
+ runId,
356
+ agentId: run.agentId ?? thread.agentId,
357
+ userId: this.options.userId,
358
+ projectId: this.options.projectId,
359
+ recordedAt,
360
+ });
361
+ await this.stateStore.put(namespace, runId, {
362
+ fingerprint,
363
+ candidateCount: candidates.length,
364
+ syncedAt: new Date().toISOString(),
365
+ trigger,
366
+ });
367
+ }
368
+ async close() {
369
+ await Promise.allSettled(Array.from(this.pending));
370
+ }
371
+ }
@@ -6,11 +6,15 @@ type PersistMemoryRecordsOptions = {
6
6
  threadId: string;
7
7
  runId: string;
8
8
  agentId: string;
9
+ workspaceId: string;
10
+ userId: string;
11
+ projectId: string;
9
12
  recordedAt: string;
10
13
  };
11
14
  export declare function renderMemoryRecordsMarkdown(title: string, records: MemoryRecord[]): string;
12
15
  export declare function rebuildStructuredMemoryProjections(store: StoreLike, namespace: string[], title: string, scope: MemoryScope, maxEntries: number): Promise<void>;
13
16
  export declare function listMemoryRecordsForScopes(store: StoreLike, scopes: MemoryScope[]): Promise<MemoryRecord[]>;
17
+ export declare function getMemoryRecord(store: StoreLike, scope: MemoryScope, recordId: string): Promise<MemoryRecord | null>;
14
18
  export declare function persistStructuredMemoryRecords(options: PersistMemoryRecordsOptions): Promise<{
15
19
  records: MemoryRecord[];
16
20
  decisions: MemoryDecision[];
@@ -118,6 +118,9 @@ function createMemoryRecord(candidate, options) {
118
118
  threadId: options.threadId,
119
119
  runId: options.runId,
120
120
  agentId: options.agentId,
121
+ workspaceId: options.workspaceId,
122
+ userId: options.userId,
123
+ projectId: options.projectId,
121
124
  ...(candidate.provenance ?? {}),
122
125
  },
123
126
  revision: 1,
@@ -372,6 +375,15 @@ export async function listMemoryRecordsForScopes(store, scopes) {
372
375
  const all = await Promise.all(scopes.map((scope) => listStoredRecords(store, scope)));
373
376
  return all.flat();
374
377
  }
378
+ export async function getMemoryRecord(store, scope, recordId) {
379
+ const entry = await store.get(buildScopeNamespace(scope), `${recordId}.json`);
380
+ const record = entry?.value;
381
+ if (typeof record !== "object" || !record || Array.isArray(record)) {
382
+ return null;
383
+ }
384
+ const candidate = record;
385
+ return typeof candidate.id === "string" && typeof candidate.content === "string" ? candidate : null;
386
+ }
375
387
  export async function persistStructuredMemoryRecords(options) {
376
388
  const existingRecords = await listStoredRecords(options.store);
377
389
  const persistedRecords = [];
@@ -49,4 +49,31 @@ export declare class FileBackedStore {
49
49
  delete(namespace: string[], key: string): Promise<void>;
50
50
  listNamespaces(options?: Parameters<InMemoryStore["listNamespaces"]>[0]): Promise<string[][]>;
51
51
  }
52
+ export declare class SqliteBackedStore {
53
+ readonly filePath: string;
54
+ private readonly db;
55
+ constructor(filePath: string);
56
+ batch(operations: Array<Record<string, unknown>>): Promise<readonly unknown[]>;
57
+ get(namespace: string[], key: string): Promise<{
58
+ value: unknown;
59
+ key: string;
60
+ namespace: string[];
61
+ createdAt: Date;
62
+ updatedAt: Date;
63
+ } | null>;
64
+ private getSync;
65
+ search(namespacePrefix: string[]): Promise<Array<{
66
+ value: unknown;
67
+ key: string;
68
+ namespace: string[];
69
+ createdAt: Date;
70
+ updatedAt: Date;
71
+ score?: number;
72
+ }>>;
73
+ put(namespace: string[], key: string, value: Record<string, any>, _index?: false | string[]): Promise<void>;
74
+ private putSync;
75
+ delete(namespace: string[], key: string): Promise<void>;
76
+ private deleteSync;
77
+ listNamespaces(): Promise<string[][]>;
78
+ }
52
79
  export declare function createInMemoryStore(): StoreLike;
@@ -1,6 +1,9 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { mkdirSync } from "node:fs";
2
3
  import path from "node:path";
3
4
  import { InMemoryStore } from "@langchain/langgraph";
5
+ import Database from "better-sqlite3";
6
+ const NAMESPACE_SEPARATOR = "\u001f";
4
7
  function encodeValue(value) {
5
8
  if (value instanceof Date) {
6
9
  return {
@@ -50,6 +53,18 @@ function decodeValue(value) {
50
53
  }
51
54
  return value;
52
55
  }
56
+ function serializeNamespace(namespace) {
57
+ return namespace.join(NAMESPACE_SEPARATOR);
58
+ }
59
+ function parseRow(row) {
60
+ return {
61
+ value: decodeValue(JSON.parse(row.value_json)),
62
+ key: row.key,
63
+ namespace: JSON.parse(row.namespace_json),
64
+ createdAt: new Date(row.created_at),
65
+ updatedAt: new Date(row.updated_at),
66
+ };
67
+ }
53
68
  export class FileBackedStore {
54
69
  filePath;
55
70
  delegate = new InMemoryStore();
@@ -131,6 +146,87 @@ export class FileBackedStore {
131
146
  });
132
147
  }
133
148
  }
149
+ export class SqliteBackedStore {
150
+ filePath;
151
+ db;
152
+ constructor(filePath) {
153
+ this.filePath = filePath;
154
+ mkdirSync(path.dirname(filePath), { recursive: true });
155
+ this.db = new Database(filePath);
156
+ this.db.pragma("journal_mode = WAL");
157
+ this.db.exec(`
158
+ CREATE TABLE IF NOT EXISTS store_entries (
159
+ namespace TEXT NOT NULL,
160
+ namespace_json TEXT NOT NULL,
161
+ key TEXT NOT NULL,
162
+ value_json TEXT NOT NULL,
163
+ created_at TEXT NOT NULL,
164
+ updated_at TEXT NOT NULL,
165
+ PRIMARY KEY (namespace, key)
166
+ );
167
+ CREATE INDEX IF NOT EXISTS idx_store_entries_namespace ON store_entries(namespace);
168
+ `);
169
+ }
170
+ async batch(operations) {
171
+ const results = this.db.transaction((items) => items.map((operation) => {
172
+ const namespace = Array.isArray(operation.namespace) ? operation.namespace.filter((item) => typeof item === "string") : [];
173
+ const key = typeof operation.key === "string" ? operation.key : "";
174
+ if ("value" in operation) {
175
+ this.putSync(namespace, key, operation.value);
176
+ return undefined;
177
+ }
178
+ if (operation.delete === true) {
179
+ this.deleteSync(namespace, key);
180
+ return undefined;
181
+ }
182
+ return this.getSync(namespace, key);
183
+ }))(operations);
184
+ return results;
185
+ }
186
+ async get(namespace, key) {
187
+ return this.getSync(namespace, key);
188
+ }
189
+ getSync(namespace, key) {
190
+ const row = this.db.prepare(`SELECT namespace, namespace_json, key, value_json, created_at, updated_at
191
+ FROM store_entries
192
+ WHERE namespace = ? AND key = ?`).get(serializeNamespace(namespace), key);
193
+ return row ? parseRow(row) : null;
194
+ }
195
+ async search(namespacePrefix) {
196
+ const prefix = serializeNamespace(namespacePrefix);
197
+ const rows = this.db.prepare(`SELECT namespace, namespace_json, key, value_json, created_at, updated_at
198
+ FROM store_entries
199
+ WHERE namespace = ? OR namespace LIKE ?
200
+ ORDER BY namespace ASC, key ASC`).all(prefix, `${prefix}${NAMESPACE_SEPARATOR}%`);
201
+ return rows.map((row) => parseRow(row));
202
+ }
203
+ async put(namespace, key, value, _index) {
204
+ this.putSync(namespace, key, value);
205
+ }
206
+ putSync(namespace, key, value) {
207
+ const now = new Date().toISOString();
208
+ const serializedNamespace = serializeNamespace(namespace);
209
+ const encodedValue = JSON.stringify(encodeValue(value));
210
+ this.db.prepare(`INSERT INTO store_entries (namespace, namespace_json, key, value_json, created_at, updated_at)
211
+ VALUES (?, ?, ?, ?, ?, ?)
212
+ ON CONFLICT(namespace, key) DO UPDATE SET
213
+ namespace_json = excluded.namespace_json,
214
+ value_json = excluded.value_json,
215
+ updated_at = excluded.updated_at`).run(serializedNamespace, JSON.stringify(namespace), key, encodedValue, now, now);
216
+ }
217
+ async delete(namespace, key) {
218
+ this.deleteSync(namespace, key);
219
+ }
220
+ deleteSync(namespace, key) {
221
+ this.db.prepare(`DELETE FROM store_entries WHERE namespace = ? AND key = ?`).run(serializeNamespace(namespace), key);
222
+ }
223
+ async listNamespaces() {
224
+ const rows = this.db.prepare(`SELECT DISTINCT namespace_json
225
+ FROM store_entries
226
+ ORDER BY namespace ASC`).all();
227
+ return rows.map((row) => JSON.parse(row.namespace_json));
228
+ }
229
+ }
134
230
  export function createInMemoryStore() {
135
231
  return new InMemoryStore();
136
232
  }
@@ -1,4 +1,4 @@
1
- import type { ApprovalRecord, CancelOptions, HarnessEvent, HarnessStreamItem, RuntimeHealthSnapshot, MessageContent, RunRecord, RunStartOptions, RestartConversationOptions, RuntimeAdapterOptions, ResumeOptions, RunOptions, RunResult, RunSummary, ThreadSummary, ThreadRecord, WorkspaceBundle } from "../contracts/types.js";
1
+ import type { ApprovalRecord, CancelOptions, HarnessEvent, HarnessStreamItem, RuntimeHealthSnapshot, MessageContent, RunRecord, RunStartOptions, RestartConversationOptions, RuntimeAdapterOptions, ResumeOptions, RunOptions, RunResult, RunSummary, MemorizeInput, MemorizeResult, RecallInput, RecallResult, ThreadSummary, ThreadRecord, WorkspaceBundle } from "../contracts/types.js";
2
2
  import { type ToolMcpServerOptions } from "../mcp.js";
3
3
  import { type InventoryAgentRecord, type InventorySkillRecord } from "./harness/system/inventory.js";
4
4
  import type { RequirementAssessmentOptions } from "./harness/system/skill-requirements.js";
@@ -29,6 +29,11 @@ export declare class AgentHarnessRuntime {
29
29
  private readonly unregisterRuntimeMemorySync;
30
30
  private readonly mem0IngestionSync;
31
31
  private readonly unregisterMem0IngestionSync;
32
+ private readonly mem0SemanticRecall;
33
+ private readonly runtimeMemoryFormationConfig;
34
+ private readonly runtimeMemoryManager;
35
+ private readonly runtimeMemoryFormationSync;
36
+ private readonly unregisterRuntimeMemoryFormationSync;
32
37
  private readonly resolvedRuntimeAdapterOptions;
33
38
  private readonly healthMonitor;
34
39
  private readonly recoveryConfig;
@@ -43,6 +48,7 @@ export declare class AgentHarnessRuntime {
43
48
  private closed;
44
49
  private readonly backgroundEventRuntime;
45
50
  private readonly runtimeEventOperations;
51
+ private resolveRuntimeMemoryVectorStore;
46
52
  private defaultRunRoot;
47
53
  private getDefaultRuntimeEntryAgentId;
48
54
  private resolveSelectedAgentId;
@@ -62,6 +68,8 @@ export declare class AgentHarnessRuntime {
62
68
  threadId?: string;
63
69
  state?: RunSummary["state"];
64
70
  }): Promise<RunSummary[]>;
71
+ memorize(input: MemorizeInput): Promise<MemorizeResult>;
72
+ recall(input: RecallInput): Promise<RecallResult>;
65
73
  getRun(runId: string): Promise<RunRecord | null>;
66
74
  private getSession;
67
75
  getThread(threadId: string): Promise<ThreadRecord | null>;
@@ -93,8 +101,21 @@ export declare class AgentHarnessRuntime {
93
101
  private finalizeCancelledRun;
94
102
  private invokeWithHistory;
95
103
  private resolveMemoryNamespace;
104
+ private getWorkspaceId;
105
+ private resolveRecallScopes;
106
+ private matchesRecallScope;
107
+ private getMemoryScopeBoost;
108
+ private memoryFreshnessBoost;
109
+ private scoreStructuredRecord;
110
+ private normalizeMemoryDedupKey;
111
+ private inferMem0MemoryKind;
112
+ private inferMem0MemoryScope;
113
+ private createMem0MemoryRecord;
114
+ private rankRecallCandidates;
115
+ private rebuildRuntimeMemoryVectorIndex;
96
116
  private buildRuntimeMemoryContext;
97
117
  private persistRuntimeMemoryCandidates;
118
+ private persistStructuredMemoryCandidates;
98
119
  private appendMemoryDigest;
99
120
  private resolvePersistedRunPriority;
100
121
  private enqueuePendingRunSlot;