@botbotgo/agent-harness 0.0.154 → 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.
package/README.md CHANGED
@@ -291,6 +291,7 @@ If you want the shortest possible mental model:
291
291
  - structured output and multimodal content preservation in run results
292
292
  - MCP bridge support for agent-declared MCP servers
293
293
  - MCP server support for exposing harness tools outward
294
+ - optional `mem0` semantic recall augmentation over canonical SQLite durable memory
294
295
 
295
296
  ### Runtime Strengths
296
297
 
@@ -666,8 +667,8 @@ kind: Stores
666
667
  spec:
667
668
  - kind: Store
668
669
  name: default
669
- storeKind: FileStore
670
- path: store.json
670
+ storeKind: SqliteStore
671
+ path: store.sqlite
671
672
  - kind: Checkpointer
672
673
  name: default
673
674
  checkpointerKind: MemorySaver
@@ -683,6 +684,12 @@ Use this singleton file for runtime-owned durable long-term memory defaults.
683
684
 
684
685
  Keep bootstrap context in `config/agent-context.md`. Keep resumable execution state in the checkpointer. Use `RuntimeMemory` for durable memory policy and retrieval defaults.
685
686
 
687
+ `RuntimeMemory` also carries LangMem-style formation defaults:
688
+
689
+ - `formation.hotPath` for immediate write-time capture
690
+ - `formation.manager` for rule-driven or model-driven candidate normalization before persistence
691
+ - `formation.background` for reflection after run completion or approval resolution
692
+
686
693
  ### `config/catalogs/backends.yaml`
687
694
 
688
695
  Use reusable DeepAgents backend presets so filesystem and `/memories/*` topology stays in YAML:
package/README.zh.md CHANGED
@@ -360,6 +360,8 @@ const result = await run(runtime, {
360
360
  - `invocation.inputs`:结构化运行时输入
361
361
  - `invocation.attachments`:当前后端可解释的类附件负载
362
362
 
363
+ 可选地,runtime memory 还可以在 SQLite canonical durable memory 之上叠加 `mem0` 语义召回增强,用于更强的长期知识命中,而不改变正式知识记录的 contract。
364
+
363
365
  ### 写入与召回 durable runtime memory
364
366
 
365
367
  ```ts
@@ -634,8 +636,8 @@ kind: Stores
634
636
  spec:
635
637
  - kind: Store
636
638
  name: default
637
- storeKind: FileStore
638
- path: store.json
639
+ storeKind: SqliteStore
640
+ path: store.sqlite
639
641
  - kind: Checkpointer
640
642
  name: default
641
643
  checkpointerKind: MemorySaver
@@ -645,6 +647,18 @@ spec:
645
647
  path: checkpoints.sqlite
646
648
  ```
647
649
 
650
+ ### `config/runtime/runtime-memory.yaml`
651
+
652
+ 这个单例文件用于配置 runtime 持有的 durable long-term memory 默认值。
653
+
654
+ 把稳定启动上下文放在 `config/agent-context.md`,把可恢复执行状态留给 checkpointer,把长期记忆策略与检索默认值放在 `RuntimeMemory`。
655
+
656
+ `RuntimeMemory` 也承载 LangMem 风格的 formation 默认值:
657
+
658
+ - `formation.hotPath`:运行中的即时捕获
659
+ - `formation.manager`:持久化前的规则式或模型式 candidate 归一化
660
+ - `formation.background`:在 run 完成或 approval resolve 后做 reflection
661
+
648
662
  ### `config/catalogs/backends.yaml`
649
663
 
650
664
  可复用的 DeepAgents 后端预设,使文件系统与 `/memories/*` 拓扑保留在 YAML 中:
@@ -6,9 +6,9 @@ spec:
6
6
  # agent-harness feature: reusable store preset for agent backends that need a durable key-value store.
7
7
  - kind: Store
8
8
  name: default
9
- description: Default file-backed store preset for runtime-managed agent state.
10
- storeKind: FileStore
11
- path: store.json
9
+ description: Default sqlite-backed store preset for runtime-managed agent state and durable memory.
10
+ storeKind: SqliteStore
11
+ path: store.sqlite
12
12
 
13
13
  # agent-harness feature: reusable checkpointer preset for resumable execution state.
14
14
  - kind: Checkpointer
@@ -9,7 +9,7 @@ spec:
9
9
  # LangChain v1 Features
10
10
  # ====================
11
11
  # LangChain aligned feature: concrete vector store implementation.
12
- # The built-in runtime currently supports `LibSQLVectorStore` for SQLite/libSQL-backed vector retrieval.
12
+ # The built-in runtime currently supports `LibSQLVectorStore`, `QdrantVectorStore`, and `LlamaIndexSimpleVectorStore`.
13
13
  storeKind: LibSQLVectorStore
14
14
  # LangChain aligned feature: libSQL connection URL.
15
15
  # Local SQLite files use the `file:` prefix.
@@ -23,3 +23,10 @@ spec:
23
23
  # ======================
24
24
  # Retrieval tools use this to resolve their default embeddings when indexing or querying this vector store.
25
25
  embeddingModelRef: embedding-model/default
26
+
27
+ - name: qdrant
28
+ storeKind: QdrantVectorStore
29
+ host: 127.0.0.1
30
+ port: 6333
31
+ collection: runtime_memory
32
+ embeddingModelRef: embedding-model/default
@@ -66,6 +66,23 @@ spec:
66
66
  writeOnApprovalResolution: true
67
67
  writeOnRunCompletion: true
68
68
 
69
+ # agent-harness feature: LangMem-style memory formation defaults for hot-path and background reflection.
70
+ formation:
71
+ hotPath:
72
+ enabled: true
73
+ manager:
74
+ enabled: true
75
+ strategy: rules
76
+ maxContextRecords: 12
77
+ background:
78
+ enabled: true
79
+ scopes:
80
+ - thread
81
+ stateStorePath: runtime-memory-formation-state.json
82
+ maxMessagesPerRun: 40
83
+ writeOnApprovalResolution: true
84
+ writeOnRunCompletion: true
85
+
69
86
  # agent-harness feature: optional thread snapshot projection for operational state and pending approvals.
70
87
  threadMemorySync:
71
88
  enabled: true
@@ -52,9 +52,12 @@ export type ParsedVectorStoreObject = {
52
52
  id: string;
53
53
  kind: string;
54
54
  url?: string;
55
+ host?: string;
56
+ port?: number;
55
57
  authToken?: string;
56
58
  table?: string;
57
59
  column?: string;
60
+ collection?: string;
58
61
  embeddingModelRef?: string;
59
62
  metadata?: Record<string, unknown>;
60
63
  sourcePath: string;
@@ -112,9 +115,12 @@ export type CompiledVectorStore = {
112
115
  id: string;
113
116
  kind: string;
114
117
  url?: string;
118
+ host?: string;
119
+ port?: number;
115
120
  authToken?: string;
116
121
  table?: string;
117
122
  column?: string;
123
+ collection?: string;
118
124
  embeddingModelRef?: string;
119
125
  runtimeValue: string;
120
126
  };
@@ -138,6 +138,9 @@ metadata:
138
138
  name: default
139
139
  spec:
140
140
  enabled: true
141
+ store:
142
+ kind: SqliteStore
143
+ path: runtime-memory.sqlite
141
144
  namespaces:
142
145
  users: memories/users/{userId}
143
146
  projects: memories/projects/{projectId}
@@ -162,6 +165,21 @@ spec:
162
165
  backgroundConsolidation: true
163
166
  writeOnApprovalResolution: true
164
167
  writeOnRunCompletion: true
168
+ formation:
169
+ hotPath:
170
+ enabled: true
171
+ manager:
172
+ enabled: true
173
+ strategy: rules
174
+ maxContextRecords: 12
175
+ background:
176
+ enabled: true
177
+ scopes:
178
+ - thread
179
+ stateStorePath: runtime-memory-formation-state.json
180
+ maxMessagesPerRun: 40
181
+ writeOnApprovalResolution: true
182
+ writeOnRunCompletion: true
165
183
  threadMemorySync:
166
184
  enabled: true
167
185
  mem0:
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.153";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.154";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.153";
1
+ export const AGENT_HARNESS_VERSION = "0.0.154";
@@ -1,4 +1,4 @@
1
- import { type Message as Mem0Message } from "mem0ai";
1
+ import { type Memory as Mem0Memory, type Message as Mem0Message } from "mem0ai";
2
2
  import type { HarnessEvent, HarnessEventProjection } from "../../../contracts/types.js";
3
3
  import type { RuntimePersistence } from "../../../persistence/types.js";
4
4
  import { type StoreLike } from "./store.js";
@@ -19,8 +19,26 @@ export type ResolvedMem0Config = {
19
19
  };
20
20
  type Mem0ClientLike = {
21
21
  add(messages: Mem0Message[], options?: Record<string, unknown>): Promise<unknown>;
22
+ search(query: string, options?: Record<string, unknown>): Promise<Mem0Memory[]>;
23
+ };
24
+ export type Mem0ClientFactory = (config: ResolvedMem0Config) => Promise<Mem0ClientLike> | Mem0ClientLike;
25
+ export type Mem0SearchInput = {
26
+ query: string;
27
+ topK: number;
28
+ agentId?: string;
29
+ threadId?: string;
30
+ };
31
+ export type Mem0SearchResult = {
32
+ id: string;
33
+ memory: string;
34
+ score: number;
35
+ categories: string[];
36
+ metadata: Record<string, unknown>;
37
+ createdAt: string;
38
+ updatedAt: string;
39
+ agentId?: string;
40
+ runId?: string;
22
41
  };
23
- type Mem0ClientFactory = (config: ResolvedMem0Config) => Promise<Mem0ClientLike> | Mem0ClientLike;
24
42
  export declare function readMem0RuntimeConfig(runtimeMemory: Record<string, unknown> | undefined, workspaceRoot: string): ResolvedMem0Config | undefined;
25
43
  export declare class Mem0IngestionSync implements HarnessEventProjection {
26
44
  private readonly persistence;
@@ -39,4 +57,12 @@ export declare class Mem0IngestionSync implements HarnessEventProjection {
39
57
  private syncRun;
40
58
  close(): Promise<void>;
41
59
  }
60
+ export declare class Mem0SemanticRecall {
61
+ private readonly config;
62
+ private readonly clientFactory;
63
+ private clientPromise;
64
+ constructor(config: ResolvedMem0Config, clientFactory?: Mem0ClientFactory);
65
+ private getClient;
66
+ search(input: Mem0SearchInput): Promise<Mem0SearchResult[]>;
67
+ }
42
68
  export {};
@@ -69,7 +69,10 @@ export function readMem0RuntimeConfig(runtimeMemory, workspaceRoot) {
69
69
  };
70
70
  }
71
71
  async function createDefaultMem0Client(config) {
72
- const apiKey = process.env[config.apiKeyEnv];
72
+ const apiKey = process.env[config.apiKeyEnv]?.trim();
73
+ if (!apiKey && config.host) {
74
+ return createSelfHostedMem0Client(config);
75
+ }
73
76
  if (!apiKey) {
74
77
  throw new Error(`runtimeMemory.mem0 is enabled but environment variable ${config.apiKeyEnv} is not set`);
75
78
  }
@@ -82,6 +85,47 @@ async function createDefaultMem0Client(config) {
82
85
  ...(config.projectId ? { projectId: config.projectId } : {}),
83
86
  });
84
87
  }
88
+ function normalizeMem0Host(host) {
89
+ return host.replace(/\/+$/, "");
90
+ }
91
+ async function selfHostedMem0Request(config, pathname, options = {}) {
92
+ if (!config.host) {
93
+ throw new Error("Self-hosted mem0 client requires runtimeMemory.mem0.host.");
94
+ }
95
+ const response = await fetch(`${normalizeMem0Host(config.host)}${pathname}`, {
96
+ method: options.method ?? "GET",
97
+ headers: {
98
+ "Content-Type": "application/json",
99
+ },
100
+ ...(options.body ? { body: JSON.stringify(options.body) } : {}),
101
+ });
102
+ if (!response.ok) {
103
+ throw new Error(`Self-hosted mem0 request failed: ${response.status} ${response.statusText}`);
104
+ }
105
+ return response.json();
106
+ }
107
+ function createSelfHostedMem0Client(config) {
108
+ return {
109
+ async add(messages, options = {}) {
110
+ return selfHostedMem0Request(config, "/v1/memories/", {
111
+ method: "POST",
112
+ body: {
113
+ messages,
114
+ ...options,
115
+ },
116
+ });
117
+ },
118
+ async search(query, options = {}) {
119
+ return selfHostedMem0Request(config, "/v1/memories/search/", {
120
+ method: "POST",
121
+ body: {
122
+ query,
123
+ ...options,
124
+ },
125
+ });
126
+ },
127
+ };
128
+ }
85
129
  export class Mem0IngestionSync {
86
130
  persistence;
87
131
  config;
@@ -179,3 +223,70 @@ export class Mem0IngestionSync {
179
223
  await Promise.allSettled(Array.from(this.pending));
180
224
  }
181
225
  }
226
+ function toIsoString(value) {
227
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
228
+ return value.toISOString();
229
+ }
230
+ if (typeof value === "string" && value.trim().length > 0) {
231
+ const parsed = new Date(value);
232
+ return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();
233
+ }
234
+ return undefined;
235
+ }
236
+ export class Mem0SemanticRecall {
237
+ config;
238
+ clientFactory;
239
+ clientPromise = null;
240
+ constructor(config, clientFactory = createDefaultMem0Client) {
241
+ this.config = config;
242
+ this.clientFactory = clientFactory;
243
+ }
244
+ async getClient() {
245
+ if (!this.clientPromise) {
246
+ this.clientPromise = Promise.resolve(this.clientFactory(this.config));
247
+ }
248
+ return this.clientPromise;
249
+ }
250
+ async search(input) {
251
+ if (input.query.trim().length === 0 || input.topK <= 0) {
252
+ return [];
253
+ }
254
+ const client = await this.getClient();
255
+ const records = await client.search(input.query, {
256
+ top_k: input.topK,
257
+ limit: input.topK,
258
+ app_id: this.config.appId,
259
+ ...(input.agentId ? { agent_id: input.agentId } : {}),
260
+ ...(input.threadId && this.config.userIdPrefix ? { user_id: `${this.config.userIdPrefix}${input.threadId}` } : {}),
261
+ });
262
+ return records
263
+ .map((record) => {
264
+ const memory = typeof record.memory === "string" && record.memory.trim().length > 0
265
+ ? record.memory.trim()
266
+ : typeof record.data?.memory === "string" && record.data.memory.trim().length > 0
267
+ ? record.data.memory.trim()
268
+ : "";
269
+ if (!memory) {
270
+ return null;
271
+ }
272
+ const createdAt = toIsoString(record.created_at) ?? new Date(0).toISOString();
273
+ const updatedAt = toIsoString(record.updated_at) ?? createdAt;
274
+ return {
275
+ id: record.id,
276
+ memory,
277
+ score: typeof record.score === "number" && Number.isFinite(record.score) ? record.score : 0,
278
+ categories: Array.isArray(record.categories)
279
+ ? record.categories.filter((item) => typeof item === "string" && item.trim().length > 0)
280
+ : [],
281
+ metadata: typeof record.metadata === "object" && record.metadata && !Array.isArray(record.metadata)
282
+ ? { ...record.metadata }
283
+ : {},
284
+ createdAt,
285
+ updatedAt,
286
+ agentId: typeof record.agent_id === "string" && record.agent_id.trim().length > 0 ? record.agent_id : undefined,
287
+ runId: typeof record.run_id === "string" && record.run_id.trim().length > 0 ? record.run_id : undefined,
288
+ };
289
+ })
290
+ .filter((record) => record !== null);
291
+ }
292
+ }
@@ -0,0 +1,90 @@
1
+ import type { CompiledAgentBinding, CompiledModel, HarnessEvent, HarnessEventProjection, InternalApprovalRecord, MemoryCandidate, MemoryRecord, MemoryScope, ThreadSummary, TranscriptMessage, WorkspaceBundle } from "../../../contracts/types.js";
2
+ import type { RuntimePersistence } from "../../../persistence/types.js";
3
+ import { type StoreLike } from "./store.js";
4
+ export type ResolvedRuntimeMemoryFormationConfig = {
5
+ enabled: true;
6
+ hotPath: {
7
+ enabled: boolean;
8
+ };
9
+ manager: {
10
+ enabled: boolean;
11
+ strategy: "rules" | "model";
12
+ modelRef?: string;
13
+ maxContextRecords: number;
14
+ };
15
+ background: {
16
+ enabled: boolean;
17
+ maxMessagesPerRun: number;
18
+ scopes: MemoryScope[];
19
+ stateStorePath: string;
20
+ writeOnApprovalResolution: boolean;
21
+ writeOnRunCompletion: boolean;
22
+ };
23
+ };
24
+ type RuntimeMemoryFormationWriter = (input: {
25
+ candidates: MemoryCandidate[];
26
+ threadId: string;
27
+ runId: string;
28
+ agentId: string;
29
+ userId?: string;
30
+ projectId?: string;
31
+ recordedAt: string;
32
+ }) => Promise<void>;
33
+ type RuntimeMemoryFormationOptions = {
34
+ userId?: string;
35
+ projectId?: string;
36
+ };
37
+ type RuntimeMemoryManagerLike = {
38
+ transform(input: {
39
+ candidates: MemoryCandidate[];
40
+ binding: CompiledAgentBinding;
41
+ threadId: string;
42
+ runId: string;
43
+ recordedAt: string;
44
+ existingRecords: MemoryRecord[];
45
+ }): Promise<MemoryCandidate[]>;
46
+ };
47
+ export declare function readRuntimeMemoryFormationConfig(runtimeMemory: Record<string, unknown> | undefined, workspaceRoot: string): ResolvedRuntimeMemoryFormationConfig | undefined;
48
+ export declare function createBackgroundMemoryCandidates(input: {
49
+ thread: ThreadSummary;
50
+ runId: string;
51
+ agentId: string;
52
+ trigger: "approval.resolved" | "run.completed";
53
+ recordedAt: string;
54
+ messages: TranscriptMessage[];
55
+ approvals: InternalApprovalRecord[];
56
+ scopes: MemoryScope[];
57
+ }): MemoryCandidate[];
58
+ export declare function runModelMemoryManager(input: {
59
+ workspace: WorkspaceBundle;
60
+ binding: CompiledAgentBinding;
61
+ model: CompiledModel;
62
+ candidates: MemoryCandidate[];
63
+ threadId: string;
64
+ runId: string;
65
+ recordedAt: string;
66
+ existingRecords: MemoryRecord[];
67
+ modelResolver?: (modelId: string) => unknown;
68
+ }): Promise<MemoryCandidate[]>;
69
+ export declare function createRuntimeMemoryManager(input: {
70
+ workspace: WorkspaceBundle;
71
+ binding: CompiledAgentBinding;
72
+ config: ResolvedRuntimeMemoryFormationConfig;
73
+ modelResolver?: (modelId: string) => unknown;
74
+ }): RuntimeMemoryManagerLike;
75
+ export declare class RuntimeMemoryFormationSync implements HarnessEventProjection {
76
+ private readonly persistence;
77
+ private readonly config;
78
+ private readonly writer;
79
+ private readonly stateStore;
80
+ private readonly options;
81
+ private readonly pending;
82
+ private syncChain;
83
+ readonly name = "runtime-memory-formation-sync";
84
+ constructor(persistence: RuntimePersistence, config: ResolvedRuntimeMemoryFormationConfig, writer: RuntimeMemoryFormationWriter, runRoot: string, stateStore?: StoreLike, options?: RuntimeMemoryFormationOptions);
85
+ shouldHandle(event: HarnessEvent): boolean;
86
+ handleEvent(event: HarnessEvent): Promise<void>;
87
+ private reflectRun;
88
+ close(): Promise<void>;
89
+ }
90
+ export {};