@botbotgo/agent-harness 0.0.153 → 0.0.154
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 +31 -1
- package/README.zh.md +31 -1
- package/dist/api.d.ts +4 -1
- package/dist/api.js +6 -0
- package/dist/contracts/runtime.d.ts +44 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/harness/system/runtime-memory-records.d.ts +3 -0
- package/dist/runtime/harness/system/runtime-memory-records.js +3 -0
- package/dist/runtime/harness.d.ts +8 -1
- package/dist/runtime/harness.js +150 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ That means:
|
|
|
118
118
|
|
|
119
119
|
The runtime provides:
|
|
120
120
|
|
|
121
|
-
- `createAgentHarness(workspaceRoot)`, `run(...)`, `resolveApproval(...)`, `subscribe(...)`, inspection methods, and `stop(...)`
|
|
121
|
+
- `createAgentHarness(workspaceRoot)`, `run(...)`, `memorize(...)`, `recall(...)`, `resolveApproval(...)`, `subscribe(...)`, inspection methods, and `stop(...)`
|
|
122
122
|
- YAML-defined workspace assembly for routing, models, tools, stores, backends, MCP, recovery, and maintenance
|
|
123
123
|
- backend-adapted execution with current LangChain v1 and DeepAgents adapters
|
|
124
124
|
- local `resources/tools/` `tool({...})` modules and `resources/skills/` discovery
|
|
@@ -389,6 +389,36 @@ const result = await run(
|
|
|
389
389
|
|
|
390
390
|
Use `normalizeUserChatInput(...)` when a product already has chat-style user messages and wants to project one user turn onto the stable `run(..., { input, invocation })` surface without introducing a separate harness-owned chat API.
|
|
391
391
|
|
|
392
|
+
### Store And Recall Durable Runtime Memory
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
import { memorize, recall } from "@botbotgo/agent-harness";
|
|
396
|
+
|
|
397
|
+
await memorize(runtime, {
|
|
398
|
+
threadId: "thread-123",
|
|
399
|
+
records: [
|
|
400
|
+
{
|
|
401
|
+
content: "The release checklist requires a smoke test before publish.",
|
|
402
|
+
summary: "Run a smoke test before publish",
|
|
403
|
+
scope: "workspace",
|
|
404
|
+
kind: "procedural",
|
|
405
|
+
sourceRef: "docs/release-checklist.md",
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const recalled = await recall(runtime, {
|
|
411
|
+
query: "What does the release checklist require?",
|
|
412
|
+
scopes: ["workspace"],
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Use `memorize(...)` and `recall(...)` when an application needs a stable public runtime memory surface without importing internal `runtime/harness/system/*` modules.
|
|
417
|
+
|
|
418
|
+
- `memorize(...)` returns stable `MemoryRecord` and `MemoryDecision` results while leaving merge, review, archive, and storage layout runtime-managed
|
|
419
|
+
- `recall(...)` returns ranked `MemoryRecord` items filtered by runtime memory scope and kind
|
|
420
|
+
- app-specific knowledge taxonomy, review UI, and admin surfaces still belong in the application layer
|
|
421
|
+
|
|
392
422
|
### Let The Runtime Route
|
|
393
423
|
|
|
394
424
|
```ts
|
package/README.zh.md
CHANGED
|
@@ -118,7 +118,7 @@ AI 让 agent 逻辑、工具调用和工作流代码更容易生成,真正更
|
|
|
118
118
|
|
|
119
119
|
运行时提供:
|
|
120
120
|
|
|
121
|
-
- `createAgentHarness(workspaceRoot)`、`run(...)`、`resolveApproval(...)`、`subscribe(...)`、各类查询方法,以及 `stop(...)`
|
|
121
|
+
- `createAgentHarness(workspaceRoot)`、`run(...)`、`memorize(...)`、`recall(...)`、`resolveApproval(...)`、`subscribe(...)`、各类查询方法,以及 `stop(...)`
|
|
122
122
|
- 以 YAML 描述的工作区装配:路由、模型、工具、存储、后端、MCP、恢复与维护等
|
|
123
123
|
- 通过适配器对接当前的 LangChain v1 与 DeepAgents 执行
|
|
124
124
|
- 本地 `resources/tools/` 中 `tool({...})` 工具模块与 `resources/skills/` 的发现
|
|
@@ -360,6 +360,36 @@ const result = await run(runtime, {
|
|
|
360
360
|
- `invocation.inputs`:结构化运行时输入
|
|
361
361
|
- `invocation.attachments`:当前后端可解释的类附件负载
|
|
362
362
|
|
|
363
|
+
### 写入与召回 durable runtime memory
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import { memorize, recall } from "@botbotgo/agent-harness";
|
|
367
|
+
|
|
368
|
+
await memorize(runtime, {
|
|
369
|
+
threadId: "thread-123",
|
|
370
|
+
records: [
|
|
371
|
+
{
|
|
372
|
+
content: "The release checklist requires a smoke test before publish.",
|
|
373
|
+
summary: "Run a smoke test before publish",
|
|
374
|
+
scope: "workspace",
|
|
375
|
+
kind: "procedural",
|
|
376
|
+
sourceRef: "docs/release-checklist.md",
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const recalled = await recall(runtime, {
|
|
382
|
+
query: "What does the release checklist require?",
|
|
383
|
+
scopes: ["workspace"],
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
当应用需要稳定的公开 runtime memory 接口,而不想依赖内部 `runtime/harness/system/*` 模块时,使用 `memorize(...)` 与 `recall(...)`。
|
|
388
|
+
|
|
389
|
+
- `memorize(...)` 返回稳定的 `MemoryRecord` 与 `MemoryDecision` 结果,而 merge、review、archive 与存储布局仍由 runtime 内部托管
|
|
390
|
+
- `recall(...)` 返回按相关性排序、并按 scope / kind 过滤后的 `MemoryRecord`
|
|
391
|
+
- 业务知识分类、review UI 与管理后台仍应留在应用层
|
|
392
|
+
|
|
363
393
|
### 由运行时路由
|
|
364
394
|
|
|
365
395
|
```ts
|
package/dist/api.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { CancelOptions, InvocationEnvelope, MessageContent, RequestRecord, RequestSummary, ResumeOptions, RunDecisionOptions, RunResult, RunStartOptions, RuntimeHealthSnapshot, RuntimeAdapterOptions, SessionRecord, SessionSummary, WorkspaceLoadOptions } from "./contracts/types.js";
|
|
1
|
+
import type { CancelOptions, InvocationEnvelope, MemorizeInput, MemorizeResult, MessageContent, RecallInput, RecallResult, RequestRecord, RequestSummary, ResumeOptions, RunDecisionOptions, RunResult, RunStartOptions, RuntimeHealthSnapshot, RuntimeAdapterOptions, SessionRecord, SessionSummary, WorkspaceLoadOptions } from "./contracts/types.js";
|
|
2
2
|
import { AgentHarnessRuntime } from "./runtime/harness.js";
|
|
3
3
|
import type { InventoryAgentRecord, InventorySkillRecord } from "./runtime/harness/system/inventory.js";
|
|
4
4
|
import type { RequirementAssessmentOptions } from "./runtime/harness/system/skill-requirements.js";
|
|
5
5
|
import type { ToolMcpServerOptions } from "./mcp.js";
|
|
6
6
|
export { AgentHarnessRuntime } from "./runtime/harness.js";
|
|
7
7
|
export { createUpstreamTimelineReducer } from "./upstream-events.js";
|
|
8
|
+
export type { MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, RecallInput, RecallResult, } from "./contracts/types.js";
|
|
8
9
|
type PublicApprovalRecord = {
|
|
9
10
|
approvalId: string;
|
|
10
11
|
pendingActionId: string;
|
|
@@ -54,6 +55,8 @@ export declare function createAgentHarness(): Promise<AgentHarnessRuntime>;
|
|
|
54
55
|
export declare function createAgentHarness(workspaceRoot: string, options?: CreateAgentHarnessOptions): Promise<AgentHarnessRuntime>;
|
|
55
56
|
export declare function normalizeUserChatInput(input: UserChatInput, options?: NormalizeUserChatInputOptions): Pick<RunStartOptions, "input" | "invocation">;
|
|
56
57
|
export declare function run(runtime: AgentHarnessRuntime, options: PublicRunOptions): Promise<PublicRunResult>;
|
|
58
|
+
export declare function memorize(runtime: AgentHarnessRuntime, input: MemorizeInput): Promise<MemorizeResult>;
|
|
59
|
+
export declare function recall(runtime: AgentHarnessRuntime, input: RecallInput): Promise<RecallResult>;
|
|
57
60
|
export declare function subscribe(runtime: AgentHarnessRuntime, listener: Parameters<AgentHarnessRuntime["subscribe"]>[0]): () => void;
|
|
58
61
|
export declare function listSessions(runtime: AgentHarnessRuntime, filter?: Parameters<AgentHarnessRuntime["listThreads"]>[0]): Promise<SessionSummary[]>;
|
|
59
62
|
export declare function listRequests(runtime: AgentHarnessRuntime, filter?: {
|
package/dist/api.js
CHANGED
|
@@ -128,6 +128,12 @@ export function normalizeUserChatInput(input, options = {}) {
|
|
|
128
128
|
export async function run(runtime, options) {
|
|
129
129
|
return toPublicRunResult(await runtime.run(toInternalRunOptions(options)));
|
|
130
130
|
}
|
|
131
|
+
export async function memorize(runtime, input) {
|
|
132
|
+
return runtime.memorize(input);
|
|
133
|
+
}
|
|
134
|
+
export async function recall(runtime, input) {
|
|
135
|
+
return runtime.recall(input);
|
|
136
|
+
}
|
|
131
137
|
export function subscribe(runtime, listener) {
|
|
132
138
|
return runtime.subscribe(listener);
|
|
133
139
|
}
|
|
@@ -118,13 +118,14 @@ export type MemoryCandidate = {
|
|
|
118
118
|
noStore?: boolean;
|
|
119
119
|
provenance?: Record<string, unknown>;
|
|
120
120
|
};
|
|
121
|
+
export type MemoryKind = "semantic" | "episodic" | "procedural";
|
|
121
122
|
export type MemoryScope = "thread" | "agent" | "workspace" | "user" | "project";
|
|
122
123
|
export type MemoryRecordStatus = "active" | "stale" | "conflicted" | "archived" | "pending_review";
|
|
123
124
|
export type MemoryDecisionAction = "reject" | "store" | "merge" | "refresh" | "supersede" | "archive" | "review";
|
|
124
125
|
export type MemoryRecord = {
|
|
125
126
|
id: string;
|
|
126
127
|
canonicalKey: string;
|
|
127
|
-
kind:
|
|
128
|
+
kind: MemoryKind;
|
|
128
129
|
scope: MemoryScope;
|
|
129
130
|
content: string;
|
|
130
131
|
summary: string;
|
|
@@ -152,6 +153,48 @@ export type MemoryDecision = {
|
|
|
152
153
|
maintenance?: "none" | "dedupe" | "merge" | "review";
|
|
153
154
|
reviewRequired?: boolean;
|
|
154
155
|
};
|
|
156
|
+
export type MemorizeInputRecord = {
|
|
157
|
+
content: string;
|
|
158
|
+
summary?: string;
|
|
159
|
+
kind?: MemoryKind;
|
|
160
|
+
scope?: MemoryScope;
|
|
161
|
+
confidence?: number;
|
|
162
|
+
tags?: string[];
|
|
163
|
+
sourceType?: string;
|
|
164
|
+
sourceRef?: string;
|
|
165
|
+
observedAt?: string;
|
|
166
|
+
sensitivity?: string;
|
|
167
|
+
provenance?: Record<string, unknown>;
|
|
168
|
+
noStore?: boolean;
|
|
169
|
+
};
|
|
170
|
+
export type MemorizeInput = {
|
|
171
|
+
records: MemorizeInputRecord[];
|
|
172
|
+
threadId?: string;
|
|
173
|
+
runId?: string;
|
|
174
|
+
agentId?: string;
|
|
175
|
+
userId?: string;
|
|
176
|
+
projectId?: string;
|
|
177
|
+
recordedAt?: string;
|
|
178
|
+
};
|
|
179
|
+
export type MemorizeResult = {
|
|
180
|
+
records: MemoryRecord[];
|
|
181
|
+
decisions: MemoryDecision[];
|
|
182
|
+
};
|
|
183
|
+
export type RecallInput = {
|
|
184
|
+
query: string;
|
|
185
|
+
scopes?: MemoryScope[];
|
|
186
|
+
kinds?: MemoryKind[];
|
|
187
|
+
topK?: number;
|
|
188
|
+
includeStale?: boolean;
|
|
189
|
+
threadId?: string;
|
|
190
|
+
agentId?: string;
|
|
191
|
+
workspaceId?: string;
|
|
192
|
+
userId?: string;
|
|
193
|
+
projectId?: string;
|
|
194
|
+
};
|
|
195
|
+
export type RecallResult = {
|
|
196
|
+
items: MemoryRecord[];
|
|
197
|
+
};
|
|
155
198
|
/**
|
|
156
199
|
* Operator-facing projection of tool execution policy already compiled into a binding.
|
|
157
200
|
* This summarizes existing timeout, retry, validation, and retry-safety hints without
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, getAgent, getApproval, getRequest, getHealth, getSession, listAgentSkills, listApprovals, listRequests, listSessions, normalizeUserChatInput, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
2
|
-
export type { NormalizeUserChatInputOptions, UserChatInput, UserChatMessage } from "./api.js";
|
|
1
|
+
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, getAgent, getApproval, getRequest, getHealth, getSession, listAgentSkills, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
2
|
+
export type { MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, RecallInput, RecallResult, UserChatInput, UserChatMessage, } from "./api.js";
|
|
3
3
|
export type { ToolMcpServerOptions } from "./mcp.js";
|
|
4
4
|
export { tool } from "./tools.js";
|
|
5
5
|
export type { UpstreamTimelineProjection, UpstreamTimelineReducer } from "./upstream-events.js";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, getAgent, getApproval, getRequest, getHealth, getSession, listAgentSkills, listApprovals, listRequests, listSessions, normalizeUserChatInput, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
1
|
+
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, getAgent, getApproval, getRequest, getHealth, getSession, listAgentSkills, listApprovals, listRequests, listSessions, memorize, normalizeUserChatInput, recall, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
2
2
|
export { tool } from "./tools.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.153";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.153";
|
|
@@ -6,6 +6,9 @@ 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;
|
|
@@ -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,
|
|
@@ -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";
|
|
@@ -62,6 +62,8 @@ export declare class AgentHarnessRuntime {
|
|
|
62
62
|
threadId?: string;
|
|
63
63
|
state?: RunSummary["state"];
|
|
64
64
|
}): Promise<RunSummary[]>;
|
|
65
|
+
memorize(input: MemorizeInput): Promise<MemorizeResult>;
|
|
66
|
+
recall(input: RecallInput): Promise<RecallResult>;
|
|
65
67
|
getRun(runId: string): Promise<RunRecord | null>;
|
|
66
68
|
private getSession;
|
|
67
69
|
getThread(threadId: string): Promise<ThreadRecord | null>;
|
|
@@ -93,8 +95,13 @@ export declare class AgentHarnessRuntime {
|
|
|
93
95
|
private finalizeCancelledRun;
|
|
94
96
|
private invokeWithHistory;
|
|
95
97
|
private resolveMemoryNamespace;
|
|
98
|
+
private getWorkspaceId;
|
|
99
|
+
private resolveRecallScopes;
|
|
100
|
+
private matchesRecallScope;
|
|
101
|
+
private getMemoryScopeBoost;
|
|
96
102
|
private buildRuntimeMemoryContext;
|
|
97
103
|
private persistRuntimeMemoryCandidates;
|
|
104
|
+
private persistStructuredMemoryCandidates;
|
|
98
105
|
private appendMemoryDigest;
|
|
99
106
|
private resolvePersistedRunPriority;
|
|
100
107
|
private enqueuePendingRunSlot;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -243,6 +243,78 @@ export class AgentHarnessRuntime {
|
|
|
243
243
|
async listRuns(filter) {
|
|
244
244
|
return this.persistence.listRuns(filter);
|
|
245
245
|
}
|
|
246
|
+
async memorize(input) {
|
|
247
|
+
const binding = this.defaultRuntimeEntryBinding;
|
|
248
|
+
if (!binding) {
|
|
249
|
+
throw new Error("memorize requires a runtime entry binding.");
|
|
250
|
+
}
|
|
251
|
+
if (!Array.isArray(input.records)) {
|
|
252
|
+
throw new Error("memorize requires input.records to be an array.");
|
|
253
|
+
}
|
|
254
|
+
const candidates = input.records
|
|
255
|
+
.filter((record) => typeof record === "object" && record !== null)
|
|
256
|
+
.filter((record) => record.noStore !== true);
|
|
257
|
+
if (candidates.length === 0) {
|
|
258
|
+
return { records: [], decisions: [] };
|
|
259
|
+
}
|
|
260
|
+
if (candidates.some((record) => typeof record.content !== "string" || record.content.trim().length === 0)) {
|
|
261
|
+
throw new Error("memorize requires every record to include non-empty content.");
|
|
262
|
+
}
|
|
263
|
+
if (candidates.some((record) => (record.scope ?? "thread") === "thread") && !input.threadId) {
|
|
264
|
+
throw new Error("memorize requires threadId when storing thread-scoped memory.");
|
|
265
|
+
}
|
|
266
|
+
const recordedAt = input.recordedAt ?? new Date().toISOString();
|
|
267
|
+
const runId = input.runId ?? createPersistentId(new Date(recordedAt));
|
|
268
|
+
const threadId = input.threadId ?? `memory-api-${runId}`;
|
|
269
|
+
return this.persistStructuredMemoryCandidates(binding, {
|
|
270
|
+
candidates: candidates.map((record) => ({ ...record, content: record.content.trim() })),
|
|
271
|
+
threadId,
|
|
272
|
+
runId,
|
|
273
|
+
agentId: input.agentId ?? binding.agent.id,
|
|
274
|
+
userId: input.userId,
|
|
275
|
+
projectId: input.projectId,
|
|
276
|
+
recordedAt,
|
|
277
|
+
storeCandidateLog: true,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
async recall(input) {
|
|
281
|
+
const binding = this.defaultRuntimeEntryBinding;
|
|
282
|
+
if (!binding) {
|
|
283
|
+
throw new Error("recall requires a runtime entry binding.");
|
|
284
|
+
}
|
|
285
|
+
if (typeof input.query !== "string" || input.query.trim().length === 0) {
|
|
286
|
+
throw new Error("recall requires a non-empty query.");
|
|
287
|
+
}
|
|
288
|
+
const workspaceId = this.getWorkspaceId(binding);
|
|
289
|
+
const agentId = input.agentId ?? binding.agent.id;
|
|
290
|
+
const userId = input.userId ?? "default";
|
|
291
|
+
const projectId = input.projectId ?? workspaceId;
|
|
292
|
+
const scopes = this.resolveRecallScopes(input);
|
|
293
|
+
const topK = typeof input.topK === "number" && Number.isInteger(input.topK) && input.topK > 0
|
|
294
|
+
? input.topK
|
|
295
|
+
: (this.runtimeMemoryPolicy?.retrieval.defaultTopK ?? 5);
|
|
296
|
+
const kinds = input.kinds?.length ? new Set(input.kinds) : null;
|
|
297
|
+
const items = (await listMemoryRecordsForScopes(this.runtimeMemoryStore, scopes))
|
|
298
|
+
.filter((record) => this.matchesRecallScope(record, {
|
|
299
|
+
threadId: input.threadId,
|
|
300
|
+
agentId,
|
|
301
|
+
workspaceId: input.workspaceId ?? workspaceId,
|
|
302
|
+
userId,
|
|
303
|
+
projectId,
|
|
304
|
+
}))
|
|
305
|
+
.filter((record) => (input.includeStale ? record.status === "active" || record.status === "stale" : record.status === "active"))
|
|
306
|
+
.filter((record) => (kinds ? kinds.has(record.kind) : true))
|
|
307
|
+
.map((record) => ({
|
|
308
|
+
record,
|
|
309
|
+
score: scoreMemoryText(input.query.trim(), `${record.summary}\n${record.content}`, this.getMemoryScopeBoost(record.scope)) +
|
|
310
|
+
Math.max(0, 1 - ((Date.now() - Date.parse(record.lastConfirmedAt)) / (1000 * 60 * 60 * 24 * 365))) +
|
|
311
|
+
record.confidence,
|
|
312
|
+
}))
|
|
313
|
+
.sort((left, right) => right.score - left.score)
|
|
314
|
+
.slice(0, topK)
|
|
315
|
+
.map(({ record }) => record);
|
|
316
|
+
return { items };
|
|
317
|
+
}
|
|
246
318
|
async getRun(runId) {
|
|
247
319
|
return this.persistence.getRun(runId);
|
|
248
320
|
}
|
|
@@ -392,8 +464,7 @@ export class AgentHarnessRuntime {
|
|
|
392
464
|
}
|
|
393
465
|
}
|
|
394
466
|
resolveMemoryNamespace(scope, binding, options = {}) {
|
|
395
|
-
const
|
|
396
|
-
const workspaceId = path.basename(workspaceRoot) || "default";
|
|
467
|
+
const workspaceId = this.getWorkspaceId(binding);
|
|
397
468
|
const template = this.runtimeMemoryPolicy?.namespaces[scope] ?? `memories/${scope}s/{${scope}Id}`;
|
|
398
469
|
return resolveMemoryNamespace(template, {
|
|
399
470
|
threadId: options.threadId,
|
|
@@ -403,6 +474,50 @@ export class AgentHarnessRuntime {
|
|
|
403
474
|
projectId: options.projectId ?? workspaceId,
|
|
404
475
|
});
|
|
405
476
|
}
|
|
477
|
+
getWorkspaceId(binding) {
|
|
478
|
+
const workspaceRoot = binding.harnessRuntime.workspaceRoot ?? this.workspace.workspaceRoot;
|
|
479
|
+
return path.basename(workspaceRoot) || "default";
|
|
480
|
+
}
|
|
481
|
+
resolveRecallScopes(input) {
|
|
482
|
+
if (Array.isArray(input.scopes) && input.scopes.length > 0) {
|
|
483
|
+
return Array.from(new Set(input.scopes));
|
|
484
|
+
}
|
|
485
|
+
const scopes = new Set(["thread", "agent", "workspace"]);
|
|
486
|
+
if (input.userId) {
|
|
487
|
+
scopes.add("user");
|
|
488
|
+
}
|
|
489
|
+
if (input.projectId) {
|
|
490
|
+
scopes.add("project");
|
|
491
|
+
}
|
|
492
|
+
return Array.from(scopes);
|
|
493
|
+
}
|
|
494
|
+
matchesRecallScope(record, filters) {
|
|
495
|
+
if (record.scope === "thread") {
|
|
496
|
+
return typeof filters.threadId === "string" && String(record.provenance.threadId ?? "") === filters.threadId;
|
|
497
|
+
}
|
|
498
|
+
if (record.scope === "agent") {
|
|
499
|
+
return String(record.provenance.agentId ?? "") === filters.agentId;
|
|
500
|
+
}
|
|
501
|
+
if (record.scope === "workspace") {
|
|
502
|
+
return String(record.provenance.workspaceId ?? filters.workspaceId) === filters.workspaceId;
|
|
503
|
+
}
|
|
504
|
+
if (record.scope === "user") {
|
|
505
|
+
return String(record.provenance.userId ?? "default") === filters.userId;
|
|
506
|
+
}
|
|
507
|
+
return String(record.provenance.projectId ?? filters.workspaceId) === filters.projectId;
|
|
508
|
+
}
|
|
509
|
+
getMemoryScopeBoost(scope) {
|
|
510
|
+
if (scope === "thread") {
|
|
511
|
+
return 4;
|
|
512
|
+
}
|
|
513
|
+
if (scope === "agent") {
|
|
514
|
+
return 2;
|
|
515
|
+
}
|
|
516
|
+
if (scope === "workspace") {
|
|
517
|
+
return 1;
|
|
518
|
+
}
|
|
519
|
+
return 0;
|
|
520
|
+
}
|
|
406
521
|
async buildRuntimeMemoryContext(binding, threadId, input) {
|
|
407
522
|
const query = typeof input === "string" ? input : JSON.stringify(input ?? "");
|
|
408
523
|
const ranked = (await listMemoryRecordsForScopes(this.runtimeMemoryStore, ["thread", "agent", "workspace"]))
|
|
@@ -452,30 +567,47 @@ export class AgentHarnessRuntime {
|
|
|
452
567
|
if (candidates.length === 0) {
|
|
453
568
|
return;
|
|
454
569
|
}
|
|
455
|
-
|
|
456
|
-
const workspaceCandidates = candidates.filter((candidate) => (candidate.scope ?? "thread") === "workspace");
|
|
457
|
-
const agentCandidates = candidates.filter((candidate) => (candidate.scope ?? "thread") === "agent");
|
|
458
|
-
const recordedAt = new Date().toISOString();
|
|
459
|
-
await this.runtimeMemoryStore.put(["memories", "candidates", threadId], `${runId}.json`, {
|
|
460
|
-
runId,
|
|
461
|
-
threadId,
|
|
462
|
-
storedAt: recordedAt,
|
|
463
|
-
candidates,
|
|
464
|
-
});
|
|
465
|
-
await persistStructuredMemoryRecords({
|
|
466
|
-
store: this.runtimeMemoryStore,
|
|
570
|
+
await this.persistStructuredMemoryCandidates(binding, {
|
|
467
571
|
candidates,
|
|
468
572
|
threadId,
|
|
469
573
|
runId,
|
|
470
574
|
agentId: binding.agent.id,
|
|
471
|
-
recordedAt,
|
|
575
|
+
recordedAt: new Date().toISOString(),
|
|
576
|
+
storeCandidateLog: true,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
async persistStructuredMemoryCandidates(binding, input) {
|
|
580
|
+
const workspaceId = this.getWorkspaceId(binding);
|
|
581
|
+
const userId = input.userId ?? "default";
|
|
582
|
+
const projectId = input.projectId ?? workspaceId;
|
|
583
|
+
const threadCandidates = input.candidates.filter((candidate) => (candidate.scope ?? "thread") === "thread");
|
|
584
|
+
const workspaceCandidates = input.candidates.filter((candidate) => (candidate.scope ?? "thread") === "workspace");
|
|
585
|
+
const agentCandidates = input.candidates.filter((candidate) => (candidate.scope ?? "thread") === "agent");
|
|
586
|
+
if (input.storeCandidateLog) {
|
|
587
|
+
await this.runtimeMemoryStore.put(["memories", "candidates", input.threadId], `${input.runId}.json`, {
|
|
588
|
+
runId: input.runId,
|
|
589
|
+
threadId: input.threadId,
|
|
590
|
+
storedAt: input.recordedAt,
|
|
591
|
+
candidates: input.candidates,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
const persisted = await persistStructuredMemoryRecords({
|
|
595
|
+
store: this.runtimeMemoryStore,
|
|
596
|
+
candidates: input.candidates,
|
|
597
|
+
threadId: input.threadId,
|
|
598
|
+
runId: input.runId,
|
|
599
|
+
agentId: input.agentId,
|
|
600
|
+
workspaceId,
|
|
601
|
+
userId,
|
|
602
|
+
projectId,
|
|
603
|
+
recordedAt: input.recordedAt,
|
|
472
604
|
});
|
|
473
605
|
const writes = [];
|
|
474
606
|
if (threadCandidates.length > 0) {
|
|
475
|
-
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("thread", binding, { threadId }), "tool-memory.md", threadCandidates, 12, "Thread Tool Memory"));
|
|
607
|
+
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("thread", binding, { threadId: input.threadId }), "tool-memory.md", threadCandidates, 12, "Thread Tool Memory"));
|
|
476
608
|
writes.push(consolidateStructuredMemoryScope({
|
|
477
609
|
store: this.runtimeMemoryStore,
|
|
478
|
-
namespace: this.resolveMemoryNamespace("thread", binding, { threadId }),
|
|
610
|
+
namespace: this.resolveMemoryNamespace("thread", binding, { threadId: input.threadId }),
|
|
479
611
|
title: "Thread Structured Memory",
|
|
480
612
|
scope: "thread",
|
|
481
613
|
maxEntries: 12,
|
|
@@ -505,6 +637,7 @@ export class AgentHarnessRuntime {
|
|
|
505
637
|
}));
|
|
506
638
|
}
|
|
507
639
|
await Promise.all(writes);
|
|
640
|
+
return persisted;
|
|
508
641
|
}
|
|
509
642
|
async appendMemoryDigest(namespace, key, candidates, maxEntries, title) {
|
|
510
643
|
const existing = await this.runtimeMemoryStore.get(namespace, key);
|