@botbotgo/agent-harness 0.0.474 → 0.0.476
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 +3 -1234
- package/README.zh.md +3 -1191
- package/dist/acp.js +1 -1
- package/dist/api.js +1 -404
- package/dist/benchmark/checkpoint-resume-cost-benchmark.js +1 -55
- package/dist/benchmark/deepagent-local-model-benchmark.js +2 -35
- package/dist/benchmark/upstream-runtime-ab-benchmark.js +1 -179
- package/dist/cli/chat-interactive.js +25 -244
- package/dist/cli/chat-rendering.js +6 -100
- package/dist/cli/chat-stream.js +23 -512
- package/dist/cli/chat-ui.js +21 -199
- package/dist/cli/chat-workspace.js +2 -210
- package/dist/cli/main.js +21 -428
- package/dist/cli/managed-service-commands.js +9 -63
- package/dist/cli/managed-service.js +2 -137
- package/dist/cli/options-init-chat.js +1 -108
- package/dist/cli/options-runtime.js +1 -158
- package/dist/cli/options-serve.js +1 -282
- package/dist/cli/options.js +2 -19
- package/dist/cli/process-guards.js +1 -139
- package/dist/cli/request-tree.js +7 -296
- package/dist/cli/runtime-commands.js +12 -258
- package/dist/cli/runtime-output.js +16 -155
- package/dist/cli/server-commands.js +16 -270
- package/dist/cli/workspace.js +1 -67
- package/dist/cli.js +1 -7
- package/dist/client/acp.js +1 -1
- package/dist/client/in-process.js +1 -67
- package/dist/client/index.js +1 -2
- package/dist/client/types.js +0 -1
- package/dist/client.js +1 -1
- package/dist/contracts/core.js +1 -1
- package/dist/contracts/runtime-evaluation.js +0 -1
- package/dist/contracts/runtime-memory.js +0 -1
- package/dist/contracts/runtime-observability.js +0 -1
- package/dist/contracts/runtime-requests.js +0 -1
- package/dist/contracts/runtime-scheduling.js +0 -1
- package/dist/contracts/runtime.js +1 -27
- package/dist/contracts/types.js +1 -3
- package/dist/contracts/workspace.js +0 -1
- package/dist/flow/build-flow-graph.js +1 -50
- package/dist/flow/export-mermaid.js +2 -464
- package/dist/flow/export-sequence-mermaid.js +2 -325
- package/dist/flow/flow-graph-normalization.js +1 -214
- package/dist/flow/flow-graph-runtime.js +1 -107
- package/dist/flow/flow-graph-upstream.js +1 -494
- package/dist/flow/index.js +1 -3
- package/dist/flow/types.js +0 -1
- package/dist/index.js +1 -5
- package/dist/init-project.js +1 -1
- package/dist/knowledge/config.js +1 -32
- package/dist/knowledge/contracts.js +0 -1
- package/dist/knowledge/index.js +1 -2
- package/dist/knowledge/module.js +12 -909
- package/dist/knowledge/procedural/config.js +1 -125
- package/dist/knowledge/procedural/index.js +1 -2
- package/dist/knowledge/procedural/manager.js +9 -345
- package/dist/mcp.js +1 -2
- package/dist/package-version.d.ts +2 -2
- package/dist/package-version.js +1 -2
- package/dist/persistence/file-store.js +3 -758
- package/dist/persistence/sqlite-request-context-store.js +5 -54
- package/dist/persistence/sqlite-request-queue-store.js +10 -108
- package/dist/persistence/sqlite-runtime.js +1 -86
- package/dist/persistence/sqlite-store.js +62 -810
- package/dist/persistence/types.js +0 -1
- package/dist/projections/presentation.js +37 -206
- package/dist/projections/request-events.js +2 -502
- package/dist/projections/upstream-events.js +1 -201
- package/dist/protocol/a2a/http-discovery.js +1 -178
- package/dist/protocol/a2a/http-rpc.js +6 -622
- package/dist/protocol/a2a/http.js +1 -138
- package/dist/protocol/a2a/task-state.js +3 -317
- package/dist/protocol/acp/client.js +8 -294
- package/dist/protocol/acp/harness-client.js +1 -218
- package/dist/protocol/acp/http.js +5 -130
- package/dist/protocol/acp/server.js +1 -310
- package/dist/protocol/acp/stdio.js +2 -69
- package/dist/protocol/ag-ui/http.js +3 -378
- package/dist/protocol/mcp/server.js +1 -428
- package/dist/resource/backend/workspace-scoped-backend.js +1 -319
- package/dist/resource/isolation.js +1 -237
- package/dist/resource/mcp/tool-support.js +3 -296
- package/dist/resource/mcp-tool-support.js +1 -2
- package/dist/resource/providers/resource-provider.js +1 -215
- package/dist/resource/resource-impl.js +1 -3
- package/dist/resource/resource-types.js +0 -1
- package/dist/resource/resource.js +1 -1
- package/dist/resource/sources.js +1 -247
- package/dist/resource/tools/function-tool-resolver.js +2 -272
- package/dist/runtime/adapter/compat/deepagent-compat.js +1 -29
- package/dist/runtime/adapter/compat/openai-compatible.js +1 -55
- package/dist/runtime/adapter/direct-builtin-utility.js +2 -90
- package/dist/runtime/adapter/flow/execution-context.js +1 -71
- package/dist/runtime/adapter/flow/invocation-flow.js +8 -411
- package/dist/runtime/adapter/flow/invoke-runtime.js +1 -20
- package/dist/runtime/adapter/flow/stream-runtime.js +11 -1395
- package/dist/runtime/adapter/invocation-result.js +2 -473
- package/dist/runtime/adapter/local-tool-invocation.js +6 -638
- package/dist/runtime/adapter/middleware/context-hygiene.js +1 -83
- package/dist/runtime/adapter/middleware-assembly.js +5 -477
- package/dist/runtime/adapter/model/invocation-request.js +3 -183
- package/dist/runtime/adapter/model/message-assembly.js +1 -28
- package/dist/runtime/adapter/model/model-providers.js +23 -1089
- package/dist/runtime/adapter/model/prompted-json-tool-call-capture.js +1 -40
- package/dist/runtime/adapter/model/prompted-json-tool-policy.js +1 -22
- package/dist/runtime/adapter/resilience.js +1 -104
- package/dist/runtime/adapter/runtime-adapter-support.js +3 -141
- package/dist/runtime/adapter/runtime-shell.js +5 -166
- package/dist/runtime/adapter/stream-event-projection.js +2 -622
- package/dist/runtime/adapter/stream-text-consumption.js +1 -18
- package/dist/runtime/adapter/terminal-status.js +2 -67
- package/dist/runtime/adapter/tool/builtin-middleware-tools.js +6 -627
- package/dist/runtime/adapter/tool/declared-middleware.js +1 -154
- package/dist/runtime/adapter/tool/interrupt-policy.js +1 -34
- package/dist/runtime/adapter/tool/provider-tool.js +1 -25
- package/dist/runtime/adapter/tool/resolved-tool.js +1 -225
- package/dist/runtime/adapter/tool/tool-arguments.js +3 -486
- package/dist/runtime/adapter/tool/tool-hitl.js +1 -346
- package/dist/runtime/adapter/tool/tool-name-mapping.js +1 -128
- package/dist/runtime/adapter/tool/tool-output-artifacts.js +2 -88
- package/dist/runtime/adapter/tool/tool-replay.js +1 -37
- package/dist/runtime/adapter/tool-resolution.js +1 -86
- package/dist/runtime/adapter/upstream-configurable-keys.js +1 -2
- package/dist/runtime/agent-runtime-adapter.js +60 -2338
- package/dist/runtime/agent-runtime-assembly.js +7 -249
- package/dist/runtime/env/runtime-env.js +1 -62
- package/dist/runtime/harness/background-runtime.js +1 -8
- package/dist/runtime/harness/bindings.js +1 -58
- package/dist/runtime/harness/events/event-bus.js +1 -16
- package/dist/runtime/harness/events/event-sink.js +1 -61
- package/dist/runtime/harness/events/events.js +1 -80
- package/dist/runtime/harness/events/listener-runtime.js +1 -13
- package/dist/runtime/harness/events/runtime-event-operations.js +1 -9
- package/dist/runtime/harness/events/streaming.js +1 -100
- package/dist/runtime/harness/events/timeline.js +1 -52
- package/dist/runtime/harness/public-shapes.js +1 -186
- package/dist/runtime/harness/run/artifact-paths.js +1 -15
- package/dist/runtime/harness/run/governance.js +1 -295
- package/dist/runtime/harness/run/helpers.js +1 -71
- package/dist/runtime/harness/run/inspection.js +1 -409
- package/dist/runtime/harness/run/operator-overview.js +1 -80
- package/dist/runtime/harness/run/queue-diagnostics.js +1 -15
- package/dist/runtime/harness/run/recovery.js +1 -162
- package/dist/runtime/harness/run/resources.js +1 -60
- package/dist/runtime/harness/run/resume.js +1 -56
- package/dist/runtime/harness/run/routing.js +1 -48
- package/dist/runtime/harness/run/run-lifecycle.js +1 -66
- package/dist/runtime/harness/run/run-operations.js +1 -217
- package/dist/runtime/harness/run/run-queue.js +1 -43
- package/dist/runtime/harness/run/run-slot-acquisition.js +1 -157
- package/dist/runtime/harness/run/session-records.js +1 -97
- package/dist/runtime/harness/run/start-run.js +1 -120
- package/dist/runtime/harness/run/startup-runtime.js +1 -69
- package/dist/runtime/harness/run/stream-run.js +8 -1418
- package/dist/runtime/harness/run/surface-semantics.js +1 -79
- package/dist/runtime/harness/runtime-defaults.js +1 -39
- package/dist/runtime/harness/system/boundary-analysis.js +1 -234
- package/dist/runtime/harness/system/health-monitor.js +1 -258
- package/dist/runtime/harness/system/inventory.js +1 -129
- package/dist/runtime/harness/system/mem0-ingestion-sync.js +5 -345
- package/dist/runtime/harness/system/policy-engine.js +1 -175
- package/dist/runtime/harness/system/runtime-memory-candidates.js +4 -110
- package/dist/runtime/harness/system/runtime-memory-consolidation.js +1 -51
- package/dist/runtime/harness/system/runtime-memory-manager.js +10 -693
- package/dist/runtime/harness/system/runtime-memory-policy.js +1 -155
- package/dist/runtime/harness/system/runtime-memory-records.js +11 -577
- package/dist/runtime/harness/system/runtime-memory-sync.js +5 -206
- package/dist/runtime/harness/system/session-memory-sync.js +3 -113
- package/dist/runtime/harness/system/skill-requirements.js +1 -112
- package/dist/runtime/harness/system/store.js +9 -365
- package/dist/runtime/harness/tool-gateway/index.js +1 -2
- package/dist/runtime/harness/tool-gateway/policy.js +1 -45
- package/dist/runtime/harness/tool-gateway/validation.js +1 -176
- package/dist/runtime/harness/tool-schema.js +1 -3
- package/dist/runtime/harness.js +3 -1490
- package/dist/runtime/index.js +1 -3
- package/dist/runtime/layout/runtime-layout.js +1 -31
- package/dist/runtime/maintenance/checkpoint-maintenance.js +2 -178
- package/dist/runtime/maintenance/file-checkpoint-saver.js +1 -106
- package/dist/runtime/maintenance/runtime-record-maintenance.js +2 -169
- package/dist/runtime/maintenance/sqlite-checkpoint-saver.js +4 -289
- package/dist/runtime/parsing/output-content.js +10 -550
- package/dist/runtime/parsing/output-parsing.js +1 -4
- package/dist/runtime/parsing/output-recovery.js +3 -213
- package/dist/runtime/parsing/output-tool-args.js +7 -663
- package/dist/runtime/parsing/stream-event-parsing.js +3 -362
- package/dist/runtime/prompts/runtime-prompts.js +4 -73
- package/dist/runtime/scheduling/system-schedule-manager.js +11 -532
- package/dist/runtime/skills/skill-metadata.js +1 -197
- package/dist/runtime/startup-tracing.js +2 -37
- package/dist/runtime/support/compiled-binding.js +1 -290
- package/dist/runtime/support/embedding-models.js +1 -118
- package/dist/runtime/support/harness-support.js +5 -137
- package/dist/runtime/support/llamaindex.js +1 -108
- package/dist/runtime/support/runtime-adapter-options.js +1 -29
- package/dist/runtime/support/runtime-factories.js +1 -51
- package/dist/runtime/support/vector-stores.js +9 -270
- package/dist/scaffold/init-project.js +54 -233
- package/dist/tooling/extensions.js +1 -311
- package/dist/tooling/module-loader.js +1 -55
- package/dist/tools.js +1 -176
- package/dist/utils/agent-display.js +1 -18
- package/dist/utils/bundled-text.js +4 -39
- package/dist/utils/compiled-binding.js +1 -33
- package/dist/utils/fs.js +2 -45
- package/dist/utils/id.js +1 -9
- package/dist/utils/message-content.js +1 -30
- package/dist/utils/object.js +1 -6
- package/dist/workspace/agent-binding-compiler.js +3 -613
- package/dist/workspace/compile.js +1 -472
- package/dist/workspace/framework-contract-validation.js +2 -322
- package/dist/workspace/index.js +1 -1
- package/dist/workspace/object-loader-paths.js +1 -71
- package/dist/workspace/object-loader-readers.js +1 -187
- package/dist/workspace/object-loader.js +1 -754
- package/dist/workspace/resource-compilers.js +1 -374
- package/dist/workspace/support/agent-capabilities.js +1 -37
- package/dist/workspace/support/agent-execution-config.js +1 -44
- package/dist/workspace/support/discovery.js +1 -147
- package/dist/workspace/support/source-collectors.js +1 -30
- package/dist/workspace/support/source-protocols.js +2 -192
- package/dist/workspace/support/workspace-ref-utils.js +1 -362
- package/dist/workspace/tool-hydration.js +1 -280
- package/dist/workspace/validate.js +1 -99
- package/dist/workspace/yaml-object-reader.js +1 -285
- package/package.json +7 -3
package/dist/knowledge/module.js
CHANGED
|
@@ -1,909 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
function normalizeRuntimeContext(context) {
|
|
15
|
-
if (typeof context.agentId !== "string" || context.agentId.trim().length === 0) {
|
|
16
|
-
throw new Error("knowledge operations require agentId in runtime context.");
|
|
17
|
-
}
|
|
18
|
-
return {
|
|
19
|
-
...context,
|
|
20
|
-
agentId: context.agentId,
|
|
21
|
-
userId: context.userId ?? "default",
|
|
22
|
-
projectId: context.projectId ?? context.workspaceId,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function summarizeMemoryContent(content) {
|
|
26
|
-
return (content.trim().split("\n")[0] || content.trim()).slice(0, 240);
|
|
27
|
-
}
|
|
28
|
-
function normalizeMemoryScope(scope) {
|
|
29
|
-
if (scope === "agent" || scope === "workspace" || scope === "user" || scope === "project") {
|
|
30
|
-
return scope;
|
|
31
|
-
}
|
|
32
|
-
return "session";
|
|
33
|
-
}
|
|
34
|
-
function extractCandidateScopeOptions(candidate) {
|
|
35
|
-
if (candidate.scope) {
|
|
36
|
-
return [normalizeMemoryScope(candidate.scope)];
|
|
37
|
-
}
|
|
38
|
-
const provenance = typeof candidate.provenance === "object" && candidate.provenance !== null && !Array.isArray(candidate.provenance)
|
|
39
|
-
? candidate.provenance
|
|
40
|
-
: undefined;
|
|
41
|
-
const scopeOptions = Array.isArray(provenance?.scopeOptions)
|
|
42
|
-
? provenance.scopeOptions.filter((item) => item === "session" || item === "agent" || item === "workspace" || item === "user" || item === "project")
|
|
43
|
-
: [];
|
|
44
|
-
return scopeOptions.length > 0 ? Array.from(new Set(scopeOptions)) : ["session"];
|
|
45
|
-
}
|
|
46
|
-
function resolveRecallScopes(input, context) {
|
|
47
|
-
if (Array.isArray(input.scopes) && input.scopes.length > 0) {
|
|
48
|
-
return Array.from(new Set(input.scopes));
|
|
49
|
-
}
|
|
50
|
-
const scopes = new Set(["session", "agent", "workspace"]);
|
|
51
|
-
if (context.userId) {
|
|
52
|
-
scopes.add("user");
|
|
53
|
-
}
|
|
54
|
-
if (context.projectId) {
|
|
55
|
-
scopes.add("project");
|
|
56
|
-
}
|
|
57
|
-
return Array.from(scopes);
|
|
58
|
-
}
|
|
59
|
-
function matchesMemoryFilters(record, filters) {
|
|
60
|
-
const sessionId = String(record.provenance.sessionId ?? "");
|
|
61
|
-
if (filters.sessionId && sessionId !== filters.sessionId) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
if (filters.agentId && record.provenance.agentId !== filters.agentId) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
if (filters.workspaceId && record.provenance.workspaceId !== filters.workspaceId) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
if (filters.userId && record.provenance.userId !== filters.userId) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
if (filters.projectId && record.provenance.projectId !== filters.projectId) {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
function matchesRecallScope(record, filters) {
|
|
79
|
-
if (record.scope === "session") {
|
|
80
|
-
return typeof filters.sessionId === "string"
|
|
81
|
-
&& String(record.provenance.sessionId ?? "") === filters.sessionId;
|
|
82
|
-
}
|
|
83
|
-
if (record.scope === "agent") {
|
|
84
|
-
return String(record.provenance.agentId ?? "") === filters.agentId;
|
|
85
|
-
}
|
|
86
|
-
if (record.scope === "workspace") {
|
|
87
|
-
return String(record.provenance.workspaceId ?? filters.workspaceId) === filters.workspaceId;
|
|
88
|
-
}
|
|
89
|
-
if (record.scope === "user") {
|
|
90
|
-
return String(record.provenance.userId ?? "default") === filters.userId;
|
|
91
|
-
}
|
|
92
|
-
return String(record.provenance.projectId ?? filters.workspaceId) === filters.projectId;
|
|
93
|
-
}
|
|
94
|
-
function getMemoryScopeBoost(scope) {
|
|
95
|
-
if (scope === "session") {
|
|
96
|
-
return 4;
|
|
97
|
-
}
|
|
98
|
-
if (scope === "agent") {
|
|
99
|
-
return 2;
|
|
100
|
-
}
|
|
101
|
-
if (scope === "workspace") {
|
|
102
|
-
return 1;
|
|
103
|
-
}
|
|
104
|
-
return 0;
|
|
105
|
-
}
|
|
106
|
-
function memoryFreshnessBoost(value) {
|
|
107
|
-
return Math.max(0, 1 - ((Date.now() - Date.parse(value)) / (1000 * 60 * 60 * 24 * 365)));
|
|
108
|
-
}
|
|
109
|
-
function scoreStructuredRecord(record) {
|
|
110
|
-
return (getMemoryScopeBoost(record.scope) +
|
|
111
|
-
memoryFreshnessBoost(record.lastConfirmedAt) +
|
|
112
|
-
record.confidence);
|
|
113
|
-
}
|
|
114
|
-
function characterGramSet(value) {
|
|
115
|
-
const normalized = normalizeCompareText(value).replace(/\s+/g, "");
|
|
116
|
-
const grams = new Set();
|
|
117
|
-
if (normalized.length < 2) {
|
|
118
|
-
if (normalized.length > 0) {
|
|
119
|
-
grams.add(normalized);
|
|
120
|
-
}
|
|
121
|
-
return grams;
|
|
122
|
-
}
|
|
123
|
-
for (let index = 0; index < normalized.length - 1; index += 1) {
|
|
124
|
-
grams.add(normalized.slice(index, index + 2));
|
|
125
|
-
}
|
|
126
|
-
return grams;
|
|
127
|
-
}
|
|
128
|
-
function characterGramSimilarity(left, right) {
|
|
129
|
-
const leftGrams = characterGramSet(left);
|
|
130
|
-
const rightGrams = characterGramSet(right);
|
|
131
|
-
if (leftGrams.size === 0 || rightGrams.size === 0) {
|
|
132
|
-
return 0;
|
|
133
|
-
}
|
|
134
|
-
let intersection = 0;
|
|
135
|
-
for (const gram of leftGrams) {
|
|
136
|
-
if (rightGrams.has(gram)) {
|
|
137
|
-
intersection += 1;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const union = new Set([...leftGrams, ...rightGrams]).size;
|
|
141
|
-
return union === 0 ? 0 : intersection / union;
|
|
142
|
-
}
|
|
143
|
-
function textSimilarity(left, right) {
|
|
144
|
-
return Math.max(jaccardTextSimilarity(left, right), characterGramSimilarity(left, right));
|
|
145
|
-
}
|
|
146
|
-
function scoreStructuredRecordForQuery(record, query) {
|
|
147
|
-
let score = scoreStructuredRecord(record);
|
|
148
|
-
score += textSimilarity(record.summary, query) * 5;
|
|
149
|
-
score += textSimilarity(record.content, query) * 4;
|
|
150
|
-
if (record.knowledgeIdentity) {
|
|
151
|
-
score += textSimilarity(record.knowledgeIdentity, query) * 2;
|
|
152
|
-
}
|
|
153
|
-
if (record.operationalRule) {
|
|
154
|
-
score += textSimilarity(record.operationalRule.trigger, query) * 3;
|
|
155
|
-
score += textSimilarity(record.operationalRule.target, query) * 2;
|
|
156
|
-
if (record.operationalRule.effect === "invalidate" || record.knowledgeOperation === "delete") {
|
|
157
|
-
score += 0.5;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return score;
|
|
161
|
-
}
|
|
162
|
-
function normalizeMemoryDedupKey(record) {
|
|
163
|
-
return `${record.scope}::${record.summary.toLowerCase().replace(/\s+/g, " ").trim()}::${record.content.toLowerCase().replace(/\s+/g, " ").trim()}`;
|
|
164
|
-
}
|
|
165
|
-
function normalizeCompareText(value) {
|
|
166
|
-
return value.toLowerCase().replace(/\s+/g, " ").trim();
|
|
167
|
-
}
|
|
168
|
-
function tokenizeText(value) {
|
|
169
|
-
return new Set(value
|
|
170
|
-
.toLowerCase()
|
|
171
|
-
.split(/[^a-z0-9_]+/i)
|
|
172
|
-
.map((token) => token.trim())
|
|
173
|
-
.filter((token) => token.length > 2));
|
|
174
|
-
}
|
|
175
|
-
function jaccardTextSimilarity(left, right) {
|
|
176
|
-
const leftTokens = tokenizeText(left);
|
|
177
|
-
const rightTokens = tokenizeText(right);
|
|
178
|
-
if (leftTokens.size === 0 || rightTokens.size === 0) {
|
|
179
|
-
return 0;
|
|
180
|
-
}
|
|
181
|
-
let intersection = 0;
|
|
182
|
-
for (const token of leftTokens) {
|
|
183
|
-
if (rightTokens.has(token)) {
|
|
184
|
-
intersection += 1;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
const union = new Set([...leftTokens, ...rightTokens]).size;
|
|
188
|
-
return union === 0 ? 0 : intersection / union;
|
|
189
|
-
}
|
|
190
|
-
function containsDeletionSignal(value) {
|
|
191
|
-
const normalized = normalizeCompareText(value);
|
|
192
|
-
if (!normalized) {
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
return [
|
|
196
|
-
"delete",
|
|
197
|
-
"deleted",
|
|
198
|
-
"remove",
|
|
199
|
-
"removed",
|
|
200
|
-
"revoked",
|
|
201
|
-
"forbidden",
|
|
202
|
-
"not allowed",
|
|
203
|
-
"no longer",
|
|
204
|
-
"unavailable",
|
|
205
|
-
"disabled",
|
|
206
|
-
"deprecated",
|
|
207
|
-
"取消",
|
|
208
|
-
"撤销",
|
|
209
|
-
"删除",
|
|
210
|
-
"已删除",
|
|
211
|
-
"删掉",
|
|
212
|
-
"移除",
|
|
213
|
-
"不再",
|
|
214
|
-
"不可用",
|
|
215
|
-
"不能",
|
|
216
|
-
"无法",
|
|
217
|
-
"禁用",
|
|
218
|
-
"废弃",
|
|
219
|
-
].some((signal) => normalized.includes(signal));
|
|
220
|
-
}
|
|
221
|
-
function extractSalientLiterals(value) {
|
|
222
|
-
const results = new Set();
|
|
223
|
-
const normalized = value.trim();
|
|
224
|
-
if (!normalized) {
|
|
225
|
-
return results;
|
|
226
|
-
}
|
|
227
|
-
for (const match of normalized.matchAll(/[`'"]([^`'"]{1,64})[`'"]/g)) {
|
|
228
|
-
const literal = match[1]?.trim().toLowerCase();
|
|
229
|
-
if (literal && literal.length >= 2) {
|
|
230
|
-
results.add(literal);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
for (const match of normalized.matchAll(/\b[a-z][a-z0-9._/-]{1,63}\b/gi)) {
|
|
234
|
-
const token = match[0]?.trim().toLowerCase();
|
|
235
|
-
if (token && !["system", "startup", "command", "memory", "instruction", "user", "deleted"].includes(token)) {
|
|
236
|
-
results.add(token);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return results;
|
|
240
|
-
}
|
|
241
|
-
function sharesSalientLiteral(left, right) {
|
|
242
|
-
const leftLiterals = extractSalientLiterals(left);
|
|
243
|
-
const rightLiterals = extractSalientLiterals(right);
|
|
244
|
-
if (leftLiterals.size === 0 || rightLiterals.size === 0) {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
for (const literal of leftLiterals) {
|
|
248
|
-
if (rightLiterals.has(literal)) {
|
|
249
|
-
return true;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return false;
|
|
253
|
-
}
|
|
254
|
-
function mergeScoredRecords(...groups) {
|
|
255
|
-
const merged = new Map();
|
|
256
|
-
for (const group of groups) {
|
|
257
|
-
for (const item of group) {
|
|
258
|
-
const existing = merged.get(item.record.id);
|
|
259
|
-
if (!existing || item.score > existing.score) {
|
|
260
|
-
merged.set(item.record.id, item);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return Array.from(merged.values())
|
|
265
|
-
.sort((left, right) => right.score - left.score || right.record.lastConfirmedAt.localeCompare(left.record.lastConfirmedAt))
|
|
266
|
-
.map((item) => item.record);
|
|
267
|
-
}
|
|
268
|
-
function selectRelevantExistingRecords(candidates, existingRecords) {
|
|
269
|
-
const scored = new Map();
|
|
270
|
-
for (const candidate of candidates) {
|
|
271
|
-
const candidateScopes = new Set(extractCandidateScopeOptions(candidate));
|
|
272
|
-
const content = normalizeCompareText(candidate.content);
|
|
273
|
-
const summary = normalizeCompareText(candidate.summary ?? summarizeMemoryContent(candidate.content));
|
|
274
|
-
const sourceRef = typeof candidate.sourceRef === "string" ? candidate.sourceRef.trim() : "";
|
|
275
|
-
const candidateText = `${candidate.summary ?? ""}\n${candidate.content}`;
|
|
276
|
-
const deletionSignal = containsDeletionSignal(candidateText);
|
|
277
|
-
for (const record of existingRecords) {
|
|
278
|
-
if (!candidateScopes.has(record.scope)) {
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
let score = 0;
|
|
282
|
-
let deterministicMatch = false;
|
|
283
|
-
if (sourceRef && record.sourceRefs.includes(sourceRef)) {
|
|
284
|
-
score += 10;
|
|
285
|
-
deterministicMatch = true;
|
|
286
|
-
}
|
|
287
|
-
if (normalizeCompareText(record.content) === content) {
|
|
288
|
-
score += 9;
|
|
289
|
-
deterministicMatch = true;
|
|
290
|
-
}
|
|
291
|
-
if (normalizeCompareText(record.summary) === summary) {
|
|
292
|
-
score += 8;
|
|
293
|
-
deterministicMatch = true;
|
|
294
|
-
}
|
|
295
|
-
const similarityScore = textSimilarity(record.summary, candidate.summary ?? candidate.content) * 4
|
|
296
|
-
+ textSimilarity(record.content, candidate.content) * 3;
|
|
297
|
-
score += similarityScore;
|
|
298
|
-
if (deletionSignal) {
|
|
299
|
-
const ruleTarget = record.operationalRule?.target?.trim().toLowerCase();
|
|
300
|
-
if (ruleTarget && candidateText.toLowerCase().includes(ruleTarget)) {
|
|
301
|
-
score += 12;
|
|
302
|
-
deterministicMatch = true;
|
|
303
|
-
}
|
|
304
|
-
if (sharesSalientLiteral(candidateText, `${record.summary}\n${record.content}`)) {
|
|
305
|
-
score += 8;
|
|
306
|
-
deterministicMatch = true;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
if (!deterministicMatch && similarityScore < 1) {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
if (score <= 0) {
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
const existing = scored.get(record.id);
|
|
316
|
-
if (!existing || score > existing.score) {
|
|
317
|
-
scored.set(record.id, { record, score });
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
return Array.from(scored.values())
|
|
322
|
-
.sort((left, right) => right.score - left.score || right.record.lastConfirmedAt.localeCompare(left.record.lastConfirmedAt))
|
|
323
|
-
.map((item) => item.record);
|
|
324
|
-
}
|
|
325
|
-
async function selectVectorRelevantExistingRecords(input) {
|
|
326
|
-
const vectorStore = await input.resolveVectorStore();
|
|
327
|
-
if (!vectorStore) {
|
|
328
|
-
return [];
|
|
329
|
-
}
|
|
330
|
-
const existingById = new Map(input.existingRecords.map((record) => [record.id, record]));
|
|
331
|
-
const scored = new Map();
|
|
332
|
-
try {
|
|
333
|
-
for (const candidate of input.candidates) {
|
|
334
|
-
const candidateScopes = new Set(extractCandidateScopeOptions(candidate));
|
|
335
|
-
const query = [candidate.summary?.trim(), candidate.content.trim()].filter((value) => !!value && value.length > 0).join("\n");
|
|
336
|
-
if (!query) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
const hits = await vectorStore.similaritySearch(query, 8);
|
|
340
|
-
for (const hit of hits) {
|
|
341
|
-
const metadata = typeof hit.metadata === "object" && hit.metadata !== null && !Array.isArray(hit.metadata)
|
|
342
|
-
? hit.metadata
|
|
343
|
-
: {};
|
|
344
|
-
const recordId = typeof metadata.recordId === "string" ? metadata.recordId : undefined;
|
|
345
|
-
if (!recordId) {
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
const record = existingById.get(recordId);
|
|
349
|
-
if (!record || record.status !== "active" || !candidateScopes.has(record.scope)) {
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
const score = (typeof hit.score === "number" ? hit.score : 0) + scoreStructuredRecordForQuery(record, query);
|
|
353
|
-
const existing = scored.get(record.id);
|
|
354
|
-
if (!existing || score > existing.score) {
|
|
355
|
-
scored.set(record.id, { record, score });
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
catch {
|
|
361
|
-
return [];
|
|
362
|
-
}
|
|
363
|
-
return Array.from(scored.values())
|
|
364
|
-
.sort((left, right) => right.score - left.score || right.record.lastConfirmedAt.localeCompare(left.record.lastConfirmedAt));
|
|
365
|
-
}
|
|
366
|
-
function sortPromptRecallRecords(items) {
|
|
367
|
-
return [...items].sort((left, right) => left.record.observedAt.localeCompare(right.record.observedAt)
|
|
368
|
-
|| left.record.createdAt.localeCompare(right.record.createdAt)
|
|
369
|
-
|| left.record.id.localeCompare(right.record.id));
|
|
370
|
-
}
|
|
371
|
-
function inferMem0MemoryKind(hit) {
|
|
372
|
-
const candidates = [
|
|
373
|
-
...hit.categories,
|
|
374
|
-
typeof hit.metadata.kind === "string" ? hit.metadata.kind : undefined,
|
|
375
|
-
typeof hit.metadata.memoryType === "string" ? hit.metadata.memoryType : undefined,
|
|
376
|
-
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
377
|
-
for (const candidate of candidates) {
|
|
378
|
-
const kind = normalizeLangMemMemoryKind(candidate);
|
|
379
|
-
if (kind === "semantic" || kind === "episodic" || kind === "procedural") {
|
|
380
|
-
return kind;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return "semantic";
|
|
384
|
-
}
|
|
385
|
-
function inferMem0MemoryScope(hit, filters) {
|
|
386
|
-
const metadataScope = typeof hit.metadata.scope === "string" ? hit.metadata.scope : undefined;
|
|
387
|
-
if (metadataScope === "session" || metadataScope === "agent" || metadataScope === "workspace" || metadataScope === "user" || metadataScope === "project") {
|
|
388
|
-
return metadataScope;
|
|
389
|
-
}
|
|
390
|
-
if (typeof hit.metadata.sessionId === "string" && filters.sessionId && hit.metadata.sessionId === filters.sessionId) {
|
|
391
|
-
return "session";
|
|
392
|
-
}
|
|
393
|
-
if (hit.agentId === filters.agentId || hit.metadata.agentId === filters.agentId) {
|
|
394
|
-
return "agent";
|
|
395
|
-
}
|
|
396
|
-
return "workspace";
|
|
397
|
-
}
|
|
398
|
-
function createMem0MemoryRecord(hit, filters) {
|
|
399
|
-
const scope = inferMem0MemoryScope(hit, filters);
|
|
400
|
-
const kind = inferMem0MemoryKind(hit);
|
|
401
|
-
const summary = hit.memory.split("\n")[0]?.trim() || hit.memory;
|
|
402
|
-
return {
|
|
403
|
-
id: `mem0:${hit.id}`,
|
|
404
|
-
canonicalKey: `mem0:${hit.id}`,
|
|
405
|
-
kind,
|
|
406
|
-
scope,
|
|
407
|
-
content: hit.memory,
|
|
408
|
-
summary: summary.slice(0, 240),
|
|
409
|
-
status: "active",
|
|
410
|
-
confidence: Math.max(0, Math.min(1, hit.score || 0.5)),
|
|
411
|
-
createdAt: hit.createdAt,
|
|
412
|
-
observedAt: hit.createdAt,
|
|
413
|
-
lastConfirmedAt: hit.updatedAt,
|
|
414
|
-
sourceType: "mem0-search",
|
|
415
|
-
sourceRefs: [`mem0:${hit.id}`],
|
|
416
|
-
tags: hit.categories,
|
|
417
|
-
provenance: {
|
|
418
|
-
source: "mem0",
|
|
419
|
-
sessionId: typeof hit.metadata.sessionId === "string" ? hit.metadata.sessionId : filters.sessionId,
|
|
420
|
-
requestId: typeof hit.metadata.requestId === "string" ? hit.metadata.requestId : undefined,
|
|
421
|
-
agentId: hit.agentId ?? (typeof hit.metadata.agentId === "string" ? hit.metadata.agentId : filters.agentId),
|
|
422
|
-
workspaceId: filters.workspaceId,
|
|
423
|
-
userId: filters.userId,
|
|
424
|
-
projectId: filters.projectId,
|
|
425
|
-
...hit.metadata,
|
|
426
|
-
},
|
|
427
|
-
revision: 1,
|
|
428
|
-
supersedes: [],
|
|
429
|
-
conflictsWith: [],
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
export class DefaultKnowledgeModule {
|
|
433
|
-
deps;
|
|
434
|
-
constructor(deps) {
|
|
435
|
-
this.deps = deps;
|
|
436
|
-
}
|
|
437
|
-
async memorize(input, context) {
|
|
438
|
-
if (!Array.isArray(input.records)) {
|
|
439
|
-
throw new Error("memorize requires input.records to be an array.");
|
|
440
|
-
}
|
|
441
|
-
const normalizedContext = normalizeRuntimeContext(context);
|
|
442
|
-
const candidates = input.records
|
|
443
|
-
.filter((record) => typeof record === "object" && record !== null)
|
|
444
|
-
.filter((record) => record.noStore !== true);
|
|
445
|
-
if (candidates.length === 0) {
|
|
446
|
-
return { records: [], decisions: [] };
|
|
447
|
-
}
|
|
448
|
-
if (candidates.some((record) => typeof record.content !== "string" || record.content.trim().length === 0)) {
|
|
449
|
-
throw new Error("memorize requires every record to include non-empty content.");
|
|
450
|
-
}
|
|
451
|
-
if (candidates.some((record) => (record.scope ?? "session") === "session") && !normalizedContext.sessionId) {
|
|
452
|
-
throw new Error("memorize requires sessionId when storing session-scoped memory.");
|
|
453
|
-
}
|
|
454
|
-
const recordedAt = normalizedContext.recordedAt ?? new Date().toISOString();
|
|
455
|
-
const requestId = normalizedContext.requestId ?? createPersistentId(new Date(recordedAt));
|
|
456
|
-
const sessionId = normalizedContext.sessionId ?? `memory-api-${requestId}`;
|
|
457
|
-
return this.memorizeCandidates(candidates.map((record) => ({ ...record, content: record.content.trim() })), {
|
|
458
|
-
...normalizedContext,
|
|
459
|
-
requestId,
|
|
460
|
-
sessionId,
|
|
461
|
-
recordedAt,
|
|
462
|
-
}, {
|
|
463
|
-
storeCandidateLog: input.storeCandidateLog !== false,
|
|
464
|
-
forceStore: input.forceStore === true,
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
async recall(input, context) {
|
|
468
|
-
if (typeof input.query !== "string" || input.query.trim().length === 0) {
|
|
469
|
-
throw new Error("recall requires a non-empty query.");
|
|
470
|
-
}
|
|
471
|
-
const normalizedContext = normalizeRuntimeContext(context);
|
|
472
|
-
const scopes = resolveRecallScopes(input, normalizedContext);
|
|
473
|
-
const topK = typeof input.topK === "number" && Number.isInteger(input.topK) && input.topK > 0
|
|
474
|
-
? input.topK
|
|
475
|
-
: (this.deps.policy?.retrieval.defaultTopK ?? 5);
|
|
476
|
-
const kinds = input.kinds?.length ? new Set(input.kinds) : null;
|
|
477
|
-
const items = (await this.rankRecallCandidates({
|
|
478
|
-
query: input.query.trim(),
|
|
479
|
-
scopes,
|
|
480
|
-
kinds,
|
|
481
|
-
topK,
|
|
482
|
-
includeStale: input.includeStale === true,
|
|
483
|
-
context: normalizedContext,
|
|
484
|
-
}))
|
|
485
|
-
.slice(0, topK)
|
|
486
|
-
.map(({ record }) => record);
|
|
487
|
-
return { items };
|
|
488
|
-
}
|
|
489
|
-
async buildPromptRecall(input, context) {
|
|
490
|
-
if (!shouldRecallDurableMemory(input.query)) {
|
|
491
|
-
return { items: [] };
|
|
492
|
-
}
|
|
493
|
-
const normalizedContext = normalizeRuntimeContext(context);
|
|
494
|
-
const ranked = (await this.rankRecallCandidates({
|
|
495
|
-
query: input.query,
|
|
496
|
-
scopes: input.scopes ?? ALL_SCOPES,
|
|
497
|
-
kinds: input.kinds?.length ? new Set(input.kinds) : null,
|
|
498
|
-
topK: input.topK ?? this.deps.policy?.retrieval.maxPromptMemories ?? 8,
|
|
499
|
-
includeStale: input.includeStale === true,
|
|
500
|
-
context: normalizedContext,
|
|
501
|
-
}))
|
|
502
|
-
.filter(({ record }) => !isNonDurableTranscriptWorkflowMemory(record))
|
|
503
|
-
.map((item) => {
|
|
504
|
-
const scopeBoost = item.record.scope === "session"
|
|
505
|
-
? 4
|
|
506
|
-
: item.record.scope === "agent"
|
|
507
|
-
? 3
|
|
508
|
-
: item.record.scope === "user"
|
|
509
|
-
? 2
|
|
510
|
-
: 1;
|
|
511
|
-
return {
|
|
512
|
-
record: item.record,
|
|
513
|
-
scope: item.record.scope,
|
|
514
|
-
content: [
|
|
515
|
-
`# Durable ${item.record.scope[0].toUpperCase()}${item.record.scope.slice(1)} Memory`,
|
|
516
|
-
"",
|
|
517
|
-
`- summary: ${item.record.summary}`,
|
|
518
|
-
`- kind: ${item.record.kind}`,
|
|
519
|
-
`- scope: ${item.record.scope}`,
|
|
520
|
-
`- confidence: ${item.record.confidence.toFixed(2)}`,
|
|
521
|
-
...(item.record.knowledgeIdentity
|
|
522
|
-
? [
|
|
523
|
-
`- knowledge_identity: ${item.record.knowledgeIdentity}`,
|
|
524
|
-
`- knowledge_operation: ${item.record.knowledgeOperation ?? "create"}`,
|
|
525
|
-
]
|
|
526
|
-
: []),
|
|
527
|
-
"",
|
|
528
|
-
item.record.content,
|
|
529
|
-
].join("\n"),
|
|
530
|
-
score: scopeBoost +
|
|
531
|
-
memoryFreshnessBoost(item.record.lastConfirmedAt) +
|
|
532
|
-
item.record.confidence,
|
|
533
|
-
};
|
|
534
|
-
})
|
|
535
|
-
.sort((left, right) => right.score - left.score);
|
|
536
|
-
if (ranked.length === 0) {
|
|
537
|
-
return { items: [] };
|
|
538
|
-
}
|
|
539
|
-
const maxPromptMemories = this.deps.policy?.retrieval.maxPromptMemories ?? 8;
|
|
540
|
-
const selected = ranked.slice(0, maxPromptMemories);
|
|
541
|
-
if (selected.length === 0) {
|
|
542
|
-
return { items: [] };
|
|
543
|
-
}
|
|
544
|
-
const orderedSelected = sortPromptRecallRecords(selected);
|
|
545
|
-
return {
|
|
546
|
-
items: orderedSelected.map((entry) => entry.record),
|
|
547
|
-
context: orderedSelected.map((entry) => entry.content).join("\n\n"),
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
async buildPromptContext(input, context) {
|
|
551
|
-
const promptRecall = await this.buildPromptRecall(input, context);
|
|
552
|
-
return promptRecall.context;
|
|
553
|
-
}
|
|
554
|
-
async list(input = {}, context) {
|
|
555
|
-
const normalizedContext = { ...context };
|
|
556
|
-
const scopes = Array.isArray(input.scopes) && input.scopes.length > 0
|
|
557
|
-
? Array.from(new Set(input.scopes))
|
|
558
|
-
: ALL_SCOPES;
|
|
559
|
-
const kinds = input.kinds?.length ? new Set(input.kinds) : null;
|
|
560
|
-
const statuses = input.status?.length ? new Set(input.status) : new Set(["active"]);
|
|
561
|
-
const limit = typeof input.limit === "number" && Number.isInteger(input.limit) && input.limit > 0
|
|
562
|
-
? input.limit
|
|
563
|
-
: Number.POSITIVE_INFINITY;
|
|
564
|
-
const items = (await listMemoryRecordsForScopes(this.deps.store, scopes))
|
|
565
|
-
.filter((record) => statuses.has(record.status))
|
|
566
|
-
.filter((record) => !kinds || kinds.has(record.kind))
|
|
567
|
-
.filter((record) => matchesMemoryFilters(record, {
|
|
568
|
-
sessionId: normalizedContext.sessionId,
|
|
569
|
-
agentId: normalizedContext.agentId,
|
|
570
|
-
workspaceId: normalizedContext.workspaceId,
|
|
571
|
-
userId: normalizedContext.userId,
|
|
572
|
-
projectId: normalizedContext.projectId,
|
|
573
|
-
}))
|
|
574
|
-
.sort((left, right) => right.lastConfirmedAt.localeCompare(left.lastConfirmedAt))
|
|
575
|
-
.slice(0, limit);
|
|
576
|
-
return { items };
|
|
577
|
-
}
|
|
578
|
-
async update(input, context) {
|
|
579
|
-
if (typeof input.memoryId !== "string" || input.memoryId.trim().length === 0) {
|
|
580
|
-
throw new Error("updateMemory requires a non-empty memoryId.");
|
|
581
|
-
}
|
|
582
|
-
const existing = await findMemoryRecordById(this.deps.store, input.memoryId.trim());
|
|
583
|
-
if (!existing) {
|
|
584
|
-
throw new Error(`Memory record not found: ${input.memoryId}`);
|
|
585
|
-
}
|
|
586
|
-
const updatedAt = new Date().toISOString();
|
|
587
|
-
const nextContent = typeof input.content === "string" ? input.content.trim() : existing.content;
|
|
588
|
-
if (nextContent.length === 0) {
|
|
589
|
-
throw new Error("updateMemory requires content to remain non-empty.");
|
|
590
|
-
}
|
|
591
|
-
const next = {
|
|
592
|
-
...existing,
|
|
593
|
-
content: nextContent,
|
|
594
|
-
summary: typeof input.summary === "string"
|
|
595
|
-
? input.summary.trim() || summarizeMemoryContent(nextContent)
|
|
596
|
-
: (typeof input.content === "string" ? summarizeMemoryContent(nextContent) : existing.summary),
|
|
597
|
-
status: input.status ?? existing.status,
|
|
598
|
-
confidence: typeof input.confidence === "number" ? Math.max(0, Math.min(1, input.confidence)) : existing.confidence,
|
|
599
|
-
expiresAt: input.expiresAt === undefined ? existing.expiresAt : (input.expiresAt ?? undefined),
|
|
600
|
-
sourceType: typeof input.sourceType === "string" && input.sourceType.trim().length > 0 ? input.sourceType.trim() : existing.sourceType,
|
|
601
|
-
sourceRefs: Array.isArray(input.sourceRefs)
|
|
602
|
-
? Array.from(new Set(input.sourceRefs.map((item) => item.trim()).filter((item) => item.length > 0)))
|
|
603
|
-
: existing.sourceRefs,
|
|
604
|
-
tags: Array.isArray(input.tags)
|
|
605
|
-
? Array.from(new Set(input.tags.map((item) => item.trim()).filter((item) => item.length > 0)))
|
|
606
|
-
: existing.tags,
|
|
607
|
-
knowledgeIdentity: typeof input.knowledgeIdentity === "string" && input.knowledgeIdentity.trim().length > 0
|
|
608
|
-
? input.knowledgeIdentity.trim()
|
|
609
|
-
: existing.knowledgeIdentity,
|
|
610
|
-
knowledgeOperation: input.knowledgeOperation === "create" || input.knowledgeOperation === "update" || input.knowledgeOperation === "delete"
|
|
611
|
-
? input.knowledgeOperation
|
|
612
|
-
: existing.knowledgeOperation,
|
|
613
|
-
observedAt: typeof input.observedAt === "string" && input.observedAt.trim().length > 0 ? input.observedAt : existing.observedAt,
|
|
614
|
-
lastConfirmedAt: typeof input.lastConfirmedAt === "string" && input.lastConfirmedAt.trim().length > 0
|
|
615
|
-
? input.lastConfirmedAt
|
|
616
|
-
: updatedAt,
|
|
617
|
-
provenance: input.provenance ? { ...existing.provenance, ...input.provenance } : existing.provenance,
|
|
618
|
-
revision: existing.revision + 1,
|
|
619
|
-
};
|
|
620
|
-
await updateMemoryRecord(this.deps.store, next, updatedAt);
|
|
621
|
-
await this.rebuildVectorIndex();
|
|
622
|
-
await this.refreshStructuredMemoryScope(next, context);
|
|
623
|
-
return next;
|
|
624
|
-
}
|
|
625
|
-
async remove(input, context) {
|
|
626
|
-
if (typeof input.memoryId !== "string" || input.memoryId.trim().length === 0) {
|
|
627
|
-
throw new Error("removeMemory requires a non-empty memoryId.");
|
|
628
|
-
}
|
|
629
|
-
const existing = await findMemoryRecordById(this.deps.store, input.memoryId.trim());
|
|
630
|
-
if (!existing) {
|
|
631
|
-
throw new Error(`Memory record not found: ${input.memoryId}`);
|
|
632
|
-
}
|
|
633
|
-
await removeMemoryRecord(this.deps.store, existing.scope, existing.id);
|
|
634
|
-
await this.rebuildVectorIndex();
|
|
635
|
-
await this.refreshStructuredMemoryScope(existing, context);
|
|
636
|
-
return existing;
|
|
637
|
-
}
|
|
638
|
-
async memorizeCandidates(candidates, context, options) {
|
|
639
|
-
const normalizedContext = normalizeRuntimeContext(context);
|
|
640
|
-
const validCandidates = candidates
|
|
641
|
-
.filter((candidate) => candidate.noStore !== true)
|
|
642
|
-
.filter((candidate) => typeof candidate.content === "string" && candidate.content.trim().length > 0)
|
|
643
|
-
.map((candidate) => ({ ...candidate, content: candidate.content.trim() }));
|
|
644
|
-
if (validCandidates.length === 0) {
|
|
645
|
-
return { records: [], decisions: [] };
|
|
646
|
-
}
|
|
647
|
-
const relevantScopes = Array.from(new Set(validCandidates.flatMap((candidate) => extractCandidateScopeOptions(candidate))));
|
|
648
|
-
const existingRecords = await listMemoryRecordsForScopes(this.deps.store, relevantScopes);
|
|
649
|
-
const vectorRelatedExistingRecords = await selectVectorRelevantExistingRecords({
|
|
650
|
-
candidates: validCandidates,
|
|
651
|
-
existingRecords,
|
|
652
|
-
resolveVectorStore: this.deps.resolveVectorStore,
|
|
653
|
-
});
|
|
654
|
-
const deterministicRelatedExistingRecords = selectRelevantExistingRecords(validCandidates, existingRecords)
|
|
655
|
-
.map((record) => ({
|
|
656
|
-
record,
|
|
657
|
-
score: scoreStructuredRecord(record),
|
|
658
|
-
}));
|
|
659
|
-
const relatedExistingRecords = mergeScoredRecords(vectorRelatedExistingRecords, deterministicRelatedExistingRecords);
|
|
660
|
-
const transformedCandidates = this.deps.transformCandidates
|
|
661
|
-
? await this.deps.transformCandidates({ candidates: validCandidates, context: normalizedContext, existingRecords: relatedExistingRecords })
|
|
662
|
-
: validCandidates;
|
|
663
|
-
const storedCandidates = options.forceStore === true
|
|
664
|
-
? transformedCandidates
|
|
665
|
-
: transformedCandidates.filter((candidate) => shouldStoreMemoryCandidate(this.deps.policy, candidate));
|
|
666
|
-
if (storedCandidates.length === 0) {
|
|
667
|
-
return { records: [], decisions: [] };
|
|
668
|
-
}
|
|
669
|
-
if (options.storeCandidateLog) {
|
|
670
|
-
await this.deps.store.put(["memories", "candidates", normalizedContext.sessionId ?? `memory-api-${normalizedContext.requestId ?? "unknown"}`], `${normalizedContext.requestId}.json`, {
|
|
671
|
-
requestId: normalizedContext.requestId,
|
|
672
|
-
sessionId: normalizedContext.sessionId,
|
|
673
|
-
storedAt: normalizedContext.recordedAt,
|
|
674
|
-
candidates: storedCandidates,
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
const persisted = await persistStructuredMemoryRecords({
|
|
678
|
-
store: this.deps.store,
|
|
679
|
-
candidates: storedCandidates,
|
|
680
|
-
sessionId: normalizedContext.sessionId ?? `memory-api-${normalizedContext.requestId ?? "unknown"}`,
|
|
681
|
-
requestId: normalizedContext.requestId ?? createPersistentId(new Date(normalizedContext.recordedAt ?? new Date().toISOString())),
|
|
682
|
-
agentId: normalizedContext.agentId,
|
|
683
|
-
workspaceId: normalizedContext.workspaceId,
|
|
684
|
-
userId: normalizedContext.userId ?? "default",
|
|
685
|
-
projectId: normalizedContext.projectId ?? normalizedContext.workspaceId,
|
|
686
|
-
recordedAt: normalizedContext.recordedAt ?? new Date().toISOString(),
|
|
687
|
-
});
|
|
688
|
-
await this.rebuildVectorIndex();
|
|
689
|
-
await this.refreshAfterWrite(storedCandidates, normalizedContext);
|
|
690
|
-
return persisted;
|
|
691
|
-
}
|
|
692
|
-
async refreshAfterWrite(candidates, context) {
|
|
693
|
-
const buckets = {
|
|
694
|
-
session: candidates.filter((candidate) => (candidate.scope ?? "session") === "session"),
|
|
695
|
-
agent: candidates.filter((candidate) => (candidate.scope ?? "session") === "agent"),
|
|
696
|
-
workspace: candidates.filter((candidate) => (candidate.scope ?? "session") === "workspace"),
|
|
697
|
-
user: candidates.filter((candidate) => (candidate.scope ?? "session") === "user"),
|
|
698
|
-
project: candidates.filter((candidate) => (candidate.scope ?? "session") === "project"),
|
|
699
|
-
};
|
|
700
|
-
const writes = [];
|
|
701
|
-
for (const scope of ALL_SCOPES) {
|
|
702
|
-
if (buckets[scope].length === 0) {
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
|
-
const maxEntries = scope === "session" ? 12 : 20;
|
|
706
|
-
writes.push(this.appendMemoryDigest(this.deps.resolveNamespace(scope, context), "tool-memory.md", buckets[scope], maxEntries, TITLE_BY_SCOPE[scope].replace("Structured", "Tool")));
|
|
707
|
-
writes.push(consolidateStructuredMemoryScope({
|
|
708
|
-
store: this.deps.store,
|
|
709
|
-
namespace: this.deps.resolveNamespace(scope, context),
|
|
710
|
-
title: TITLE_BY_SCOPE[scope],
|
|
711
|
-
scope,
|
|
712
|
-
maxEntries,
|
|
713
|
-
config: this.deps.maintenanceConfig ?? undefined,
|
|
714
|
-
}));
|
|
715
|
-
}
|
|
716
|
-
await Promise.all(writes);
|
|
717
|
-
}
|
|
718
|
-
async appendMemoryDigest(namespace, key, candidates, maxEntries, title) {
|
|
719
|
-
const existing = await this.deps.store.get(namespace, key);
|
|
720
|
-
const existingItems = Array.isArray(existing?.value?.items)
|
|
721
|
-
? ((existing?.value).items ?? [])
|
|
722
|
-
: [];
|
|
723
|
-
const merged = [...existingItems];
|
|
724
|
-
for (const candidate of candidates) {
|
|
725
|
-
if (merged.some((entry) => entry.content === candidate.content && (entry.scope ?? "session") === (candidate.scope ?? "session"))) {
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
merged.push(candidate);
|
|
729
|
-
}
|
|
730
|
-
const recent = merged.slice(-maxEntries);
|
|
731
|
-
const taxonomyGroups = new Map();
|
|
732
|
-
for (const candidate of recent) {
|
|
733
|
-
const kind = normalizeLangMemMemoryKind(candidate.kind);
|
|
734
|
-
const current = taxonomyGroups.get(kind) ?? [];
|
|
735
|
-
current.push({ ...candidate, kind });
|
|
736
|
-
taxonomyGroups.set(kind, current);
|
|
737
|
-
}
|
|
738
|
-
await this.deps.store.put(namespace, key, {
|
|
739
|
-
content: `${renderMemoryCandidatesMarkdown(title, recent)}\n`,
|
|
740
|
-
items: recent,
|
|
741
|
-
});
|
|
742
|
-
await Promise.all(Array.from(taxonomyGroups.entries()).map(async ([kind, items]) => {
|
|
743
|
-
await this.deps.store.put(namespace, `${kind}.md`, {
|
|
744
|
-
content: `${renderMemoryCandidatesMarkdown(`${title} (${kind})`, items)}\n`,
|
|
745
|
-
items,
|
|
746
|
-
});
|
|
747
|
-
}));
|
|
748
|
-
}
|
|
749
|
-
async refreshStructuredMemoryScope(record, context) {
|
|
750
|
-
const provenance = record.provenance;
|
|
751
|
-
const namespaceContext = {
|
|
752
|
-
sessionId: provenance.sessionId,
|
|
753
|
-
agentId: provenance.agentId ?? context.agentId,
|
|
754
|
-
workspaceId: provenance.workspaceId ?? context.workspaceId,
|
|
755
|
-
userId: provenance.userId ?? context.userId,
|
|
756
|
-
projectId: provenance.projectId ?? context.projectId,
|
|
757
|
-
};
|
|
758
|
-
const maxEntries = record.scope === "session" ? 12 : 20;
|
|
759
|
-
await consolidateStructuredMemoryScope({
|
|
760
|
-
store: this.deps.store,
|
|
761
|
-
namespace: this.deps.resolveNamespace(record.scope, normalizeRuntimeContext(namespaceContext)),
|
|
762
|
-
title: TITLE_BY_SCOPE[record.scope],
|
|
763
|
-
scope: record.scope,
|
|
764
|
-
maxEntries,
|
|
765
|
-
config: this.deps.maintenanceConfig ?? undefined,
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
async rebuildVectorIndex() {
|
|
769
|
-
const vectorStore = await this.deps.resolveVectorStore();
|
|
770
|
-
if (!vectorStore) {
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
const records = (await listMemoryRecordsForScopes(this.deps.store, ALL_SCOPES))
|
|
774
|
-
.filter((record) => record.status === "active");
|
|
775
|
-
try {
|
|
776
|
-
await vectorStore.delete({ deleteAll: true });
|
|
777
|
-
if (records.length === 0) {
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
await vectorStore.addDocuments(records.map((record) => ({
|
|
781
|
-
pageContent: `${record.summary}\n${record.content}`,
|
|
782
|
-
metadata: {
|
|
783
|
-
recordId: record.id,
|
|
784
|
-
scope: record.scope,
|
|
785
|
-
kind: record.kind,
|
|
786
|
-
status: record.status,
|
|
787
|
-
sessionId: record.provenance.sessionId,
|
|
788
|
-
agentId: record.provenance.agentId,
|
|
789
|
-
workspaceId: record.provenance.workspaceId,
|
|
790
|
-
userId: record.provenance.userId,
|
|
791
|
-
projectId: record.provenance.projectId,
|
|
792
|
-
confidence: record.confidence,
|
|
793
|
-
lastConfirmedAt: record.lastConfirmedAt,
|
|
794
|
-
},
|
|
795
|
-
})));
|
|
796
|
-
}
|
|
797
|
-
catch {
|
|
798
|
-
// Fail open: vector indexing must not break durable memory writes.
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
async rankRecallCandidates(input) {
|
|
802
|
-
const structuredRecords = await listMemoryRecordsForScopes(this.deps.store, input.scopes);
|
|
803
|
-
const ranked = structuredRecords
|
|
804
|
-
.filter((record) => matchesRecallScope(record, {
|
|
805
|
-
sessionId: input.context.sessionId,
|
|
806
|
-
agentId: input.context.agentId,
|
|
807
|
-
workspaceId: input.context.workspaceId,
|
|
808
|
-
userId: input.context.userId ?? "default",
|
|
809
|
-
projectId: input.context.projectId ?? input.context.workspaceId,
|
|
810
|
-
}))
|
|
811
|
-
.filter((record) => (input.includeStale ? record.status === "active" || record.status === "stale" : record.status === "active"))
|
|
812
|
-
.filter((record) => (input.kinds ? input.kinds.has(record.kind) : true))
|
|
813
|
-
.map((record) => ({
|
|
814
|
-
record,
|
|
815
|
-
score: scoreStructuredRecordForQuery(record, input.query),
|
|
816
|
-
}));
|
|
817
|
-
const deduped = new Map();
|
|
818
|
-
for (const item of ranked) {
|
|
819
|
-
deduped.set(normalizeMemoryDedupKey(item.record), item);
|
|
820
|
-
}
|
|
821
|
-
const vectorStore = await this.deps.resolveVectorStore();
|
|
822
|
-
if (vectorStore) {
|
|
823
|
-
try {
|
|
824
|
-
const vectorHits = await vectorStore.similaritySearch(input.query, Math.max(input.topK, this.deps.policy?.retrieval.maxPromptMemories ?? input.topK));
|
|
825
|
-
for (const hit of vectorHits) {
|
|
826
|
-
const metadata = typeof hit.metadata === "object" && hit.metadata && !Array.isArray(hit.metadata)
|
|
827
|
-
? hit.metadata
|
|
828
|
-
: {};
|
|
829
|
-
const recordId = typeof metadata.recordId === "string" ? metadata.recordId : undefined;
|
|
830
|
-
const scope = metadata.scope;
|
|
831
|
-
if (!recordId || (scope !== "session" && scope !== "agent" && scope !== "workspace" && scope !== "user" && scope !== "project")) {
|
|
832
|
-
continue;
|
|
833
|
-
}
|
|
834
|
-
const canonical = await getMemoryRecord(this.deps.store, scope, recordId);
|
|
835
|
-
if (!canonical) {
|
|
836
|
-
continue;
|
|
837
|
-
}
|
|
838
|
-
if (!matchesRecallScope(canonical, {
|
|
839
|
-
sessionId: input.context.sessionId,
|
|
840
|
-
agentId: input.context.agentId,
|
|
841
|
-
workspaceId: input.context.workspaceId,
|
|
842
|
-
userId: input.context.userId ?? "default",
|
|
843
|
-
projectId: input.context.projectId ?? input.context.workspaceId,
|
|
844
|
-
})) {
|
|
845
|
-
continue;
|
|
846
|
-
}
|
|
847
|
-
if (input.includeStale ? canonical.status !== "active" && canonical.status !== "stale" : canonical.status !== "active") {
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
if (input.kinds && !input.kinds.has(canonical.kind)) {
|
|
851
|
-
continue;
|
|
852
|
-
}
|
|
853
|
-
const key = normalizeMemoryDedupKey(canonical);
|
|
854
|
-
const score = scoreStructuredRecordForQuery(canonical, input.query) + (typeof hit.score === "number" ? hit.score : 0);
|
|
855
|
-
const existing = deduped.get(key);
|
|
856
|
-
if (!existing || score > existing.score) {
|
|
857
|
-
deduped.set(key, { record: canonical, score });
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
catch {
|
|
862
|
-
// Fail open to lexical/runtime ranking.
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
const supportsMem0Scope = input.scopes.some((scope) => scope === "session" || scope === "agent" || scope === "workspace");
|
|
866
|
-
const mem0SemanticRecall = this.deps.getMem0SemanticRecall?.() ?? null;
|
|
867
|
-
if (!mem0SemanticRecall || !supportsMem0Scope) {
|
|
868
|
-
return Array.from(deduped.values()).sort((left, right) => right.score - left.score);
|
|
869
|
-
}
|
|
870
|
-
try {
|
|
871
|
-
const hits = await mem0SemanticRecall.search({
|
|
872
|
-
query: input.query,
|
|
873
|
-
topK: Math.max(input.topK, this.deps.policy?.retrieval.maxPromptMemories ?? input.topK),
|
|
874
|
-
agentId: input.context.agentId,
|
|
875
|
-
sessionId: input.context.sessionId,
|
|
876
|
-
});
|
|
877
|
-
for (const hit of hits) {
|
|
878
|
-
const record = createMem0MemoryRecord(hit, {
|
|
879
|
-
sessionId: input.context.sessionId,
|
|
880
|
-
agentId: input.context.agentId,
|
|
881
|
-
workspaceId: input.context.workspaceId,
|
|
882
|
-
userId: input.context.userId ?? "default",
|
|
883
|
-
projectId: input.context.projectId ?? input.context.workspaceId,
|
|
884
|
-
});
|
|
885
|
-
if (!input.scopes.includes(record.scope)) {
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
if (input.kinds && !input.kinds.has(record.kind)) {
|
|
889
|
-
continue;
|
|
890
|
-
}
|
|
891
|
-
const key = normalizeMemoryDedupKey(record);
|
|
892
|
-
if (deduped.has(key)) {
|
|
893
|
-
continue;
|
|
894
|
-
}
|
|
895
|
-
deduped.set(key, {
|
|
896
|
-
record,
|
|
897
|
-
score: scoreStructuredRecordForQuery(record, input.query) + Math.max(0, Math.min(1, hit.score)) * 4,
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
return Array.from(deduped.values()).sort((left, right) => right.score - left.score);
|
|
901
|
-
}
|
|
902
|
-
catch {
|
|
903
|
-
return Array.from(deduped.values()).sort((left, right) => right.score - left.score);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
export function createKnowledgeModule(dependencies) {
|
|
908
|
-
return new DefaultKnowledgeModule(dependencies);
|
|
909
|
-
}
|
|
1
|
+
import{createPersistentId as R}from"../utils/id.js";import{findMemoryRecordById as j,getMemoryRecord as D,listMemoryRecordsForScopes as h,persistStructuredMemoryRecords as P,removeMemoryRecord as V,updateMemoryRecord as N}from"../runtime/harness/system/runtime-memory-records.js";import{consolidateStructuredMemoryScope as b}from"../runtime/harness/system/runtime-memory-consolidation.js";import{renderMemoryCandidatesMarkdown as z}from"../runtime/harness/system/runtime-memory-candidates.js";import{shouldRecallDurableMemory as _,normalizeLangMemMemoryKind as q,shouldStoreMemoryCandidate as B,isNonDurableTranscriptWorkflowMemory as F}from"../runtime/harness/system/runtime-memory-policy.js";const k=["session","agent","workspace","user","project"],v={session:"Session Structured Memory",agent:"Agent Structured Memory",workspace:"Workspace Structured Memory",user:"User Structured Memory",project:"Project Structured Memory"};function w(t){if(typeof t.agentId!="string"||t.agentId.trim().length===0)throw new Error("knowledge operations require agentId in runtime context.");return{...t,agentId:t.agentId,userId:t.userId??"default",projectId:t.projectId??t.workspaceId}}function A(t){return(t.trim().split(`
|
|
2
|
+
`)[0]||t.trim()).slice(0,240)}function G(t){return t==="agent"||t==="workspace"||t==="user"||t==="project"?t:"session"}function x(t){if(t.scope)return[G(t.scope)];const e=typeof t.provenance=="object"&&t.provenance!==null&&!Array.isArray(t.provenance)?t.provenance:void 0,o=Array.isArray(e?.scopeOptions)?e.scopeOptions.filter(r=>r==="session"||r==="agent"||r==="workspace"||r==="user"||r==="project"):[];return o.length>0?Array.from(new Set(o)):["session"]}function W(t,e){if(Array.isArray(t.scopes)&&t.scopes.length>0)return Array.from(new Set(t.scopes));const o=new Set(["session","agent","workspace"]);return e.userId&&o.add("user"),e.projectId&&o.add("project"),Array.from(o)}function U(t,e){const o=String(t.provenance.sessionId??"");return!(e.sessionId&&o!==e.sessionId||e.agentId&&t.provenance.agentId!==e.agentId||e.workspaceId&&t.provenance.workspaceId!==e.workspaceId||e.userId&&t.provenance.userId!==e.userId||e.projectId&&t.provenance.projectId!==e.projectId)}function $(t,e){return t.scope==="session"?typeof e.sessionId=="string"&&String(t.provenance.sessionId??"")===e.sessionId:t.scope==="agent"?String(t.provenance.agentId??"")===e.agentId:t.scope==="workspace"?String(t.provenance.workspaceId??e.workspaceId)===e.workspaceId:t.scope==="user"?String(t.provenance.userId??"default")===e.userId:String(t.provenance.projectId??e.workspaceId)===e.projectId}function Y(t){return t==="session"?4:t==="agent"?2:t==="workspace"?1:0}function T(t){return Math.max(0,1-(Date.now()-Date.parse(t))/(1e3*60*60*24*365))}function E(t){return Y(t.scope)+T(t.lastConfirmedAt)+t.confidence}function K(t){const e=I(t).replace(/\s+/g,""),o=new Set;if(e.length<2)return e.length>0&&o.add(e),o;for(let r=0;r<e.length-1;r+=1)o.add(e.slice(r,r+2));return o}function H(t,e){const o=K(t),r=K(e);if(o.size===0||r.size===0)return 0;let n=0;for(const m of o)r.has(m)&&(n+=1);const a=new Set([...o,...r]).size;return a===0?0:n/a}function y(t,e){return Math.max(Q(t,e),H(t,e))}function S(t,e){let o=E(t);return o+=y(t.summary,e)*5,o+=y(t.content,e)*4,t.knowledgeIdentity&&(o+=y(t.knowledgeIdentity,e)*2),t.operationalRule&&(o+=y(t.operationalRule.trigger,e)*3,o+=y(t.operationalRule.target,e)*2,(t.operationalRule.effect==="invalidate"||t.knowledgeOperation==="delete")&&(o+=.5)),o}function M(t){return`${t.scope}::${t.summary.toLowerCase().replace(/\s+/g," ").trim()}::${t.content.toLowerCase().replace(/\s+/g," ").trim()}`}function I(t){return t.toLowerCase().replace(/\s+/g," ").trim()}function L(t){return new Set(t.toLowerCase().split(/[^a-z0-9_]+/i).map(e=>e.trim()).filter(e=>e.length>2))}function Q(t,e){const o=L(t),r=L(e);if(o.size===0||r.size===0)return 0;let n=0;for(const m of o)r.has(m)&&(n+=1);const a=new Set([...o,...r]).size;return a===0?0:n/a}function J(t){const e=I(t);return e?["delete","deleted","remove","removed","revoked","forbidden","not allowed","no longer","unavailable","disabled","deprecated","\u53D6\u6D88","\u64A4\u9500","\u5220\u9664","\u5DF2\u5220\u9664","\u5220\u6389","\u79FB\u9664","\u4E0D\u518D","\u4E0D\u53EF\u7528","\u4E0D\u80FD","\u65E0\u6CD5","\u7981\u7528","\u5E9F\u5F03"].some(o=>e.includes(o)):!1}function O(t){const e=new Set,o=t.trim();if(!o)return e;for(const r of o.matchAll(/[`'"]([^`'"]{1,64})[`'"]/g)){const n=r[1]?.trim().toLowerCase();n&&n.length>=2&&e.add(n)}for(const r of o.matchAll(/\b[a-z][a-z0-9._/-]{1,63}\b/gi)){const n=r[0]?.trim().toLowerCase();n&&!["system","startup","command","memory","instruction","user","deleted"].includes(n)&&e.add(n)}return e}function X(t,e){const o=O(t),r=O(e);if(o.size===0||r.size===0)return!1;for(const n of o)if(r.has(n))return!0;return!1}function Z(...t){const e=new Map;for(const o of t)for(const r of o){const n=e.get(r.record.id);(!n||r.score>n.score)&&e.set(r.record.id,r)}return Array.from(e.values()).sort((o,r)=>r.score-o.score||r.record.lastConfirmedAt.localeCompare(o.record.lastConfirmedAt)).map(o=>o.record)}function ee(t,e){const o=new Map;for(const r of t){const n=new Set(x(r)),a=I(r.content),m=I(r.summary??A(r.content)),f=typeof r.sourceRef=="string"?r.sourceRef.trim():"",s=`${r.summary??""}
|
|
3
|
+
${r.content}`,c=J(s);for(const d of e){if(!n.has(d.scope))continue;let i=0,l=!1;f&&d.sourceRefs.includes(f)&&(i+=10,l=!0),I(d.content)===a&&(i+=9,l=!0),I(d.summary)===m&&(i+=8,l=!0);const p=y(d.summary,r.summary??r.content)*4+y(d.content,r.content)*3;if(i+=p,c){const g=d.operationalRule?.target?.trim().toLowerCase();g&&s.toLowerCase().includes(g)&&(i+=12,l=!0),X(s,`${d.summary}
|
|
4
|
+
${d.content}`)&&(i+=8,l=!0)}if(!l&&p<1||i<=0)continue;const u=o.get(d.id);(!u||i>u.score)&&o.set(d.id,{record:d,score:i})}}return Array.from(o.values()).sort((r,n)=>n.score-r.score||n.record.lastConfirmedAt.localeCompare(r.record.lastConfirmedAt)).map(r=>r.record)}async function te(t){const e=await t.resolveVectorStore();if(!e)return[];const o=new Map(t.existingRecords.map(n=>[n.id,n])),r=new Map;try{for(const n of t.candidates){const a=new Set(x(n)),m=[n.summary?.trim(),n.content.trim()].filter(s=>!!s&&s.length>0).join(`
|
|
5
|
+
`);if(!m)continue;const f=await e.similaritySearch(m,8);for(const s of f){const c=typeof s.metadata=="object"&&s.metadata!==null&&!Array.isArray(s.metadata)?s.metadata:{},d=typeof c.recordId=="string"?c.recordId:void 0;if(!d)continue;const i=o.get(d);if(!i||i.status!=="active"||!a.has(i.scope))continue;const l=(typeof s.score=="number"?s.score:0)+S(i,m),p=r.get(i.id);(!p||l>p.score)&&r.set(i.id,{record:i,score:l})}}}catch{return[]}return Array.from(r.values()).sort((n,a)=>a.score-n.score||a.record.lastConfirmedAt.localeCompare(n.record.lastConfirmedAt))}function re(t){return[...t].sort((e,o)=>e.record.observedAt.localeCompare(o.record.observedAt)||e.record.createdAt.localeCompare(o.record.createdAt)||e.record.id.localeCompare(o.record.id))}function oe(t){const e=[...t.categories,typeof t.metadata.kind=="string"?t.metadata.kind:void 0,typeof t.metadata.memoryType=="string"?t.metadata.memoryType:void 0].filter(o=>typeof o=="string"&&o.trim().length>0);for(const o of e){const r=q(o);if(r==="semantic"||r==="episodic"||r==="procedural")return r}return"semantic"}function se(t,e){const o=typeof t.metadata.scope=="string"?t.metadata.scope:void 0;return o==="session"||o==="agent"||o==="workspace"||o==="user"||o==="project"?o:typeof t.metadata.sessionId=="string"&&e.sessionId&&t.metadata.sessionId===e.sessionId?"session":t.agentId===e.agentId||t.metadata.agentId===e.agentId?"agent":"workspace"}function ne(t,e){const o=se(t,e),r=oe(t),n=t.memory.split(`
|
|
6
|
+
`)[0]?.trim()||t.memory;return{id:`mem0:${t.id}`,canonicalKey:`mem0:${t.id}`,kind:r,scope:o,content:t.memory,summary:n.slice(0,240),status:"active",confidence:Math.max(0,Math.min(1,t.score||.5)),createdAt:t.createdAt,observedAt:t.createdAt,lastConfirmedAt:t.updatedAt,sourceType:"mem0-search",sourceRefs:[`mem0:${t.id}`],tags:t.categories,provenance:{source:"mem0",sessionId:typeof t.metadata.sessionId=="string"?t.metadata.sessionId:e.sessionId,requestId:typeof t.metadata.requestId=="string"?t.metadata.requestId:void 0,agentId:t.agentId??(typeof t.metadata.agentId=="string"?t.metadata.agentId:e.agentId),workspaceId:e.workspaceId,userId:e.userId,projectId:e.projectId,...t.metadata},revision:1,supersedes:[],conflictsWith:[]}}class ae{deps;constructor(e){this.deps=e}async memorize(e,o){if(!Array.isArray(e.records))throw new Error("memorize requires input.records to be an array.");const r=w(o),n=e.records.filter(s=>typeof s=="object"&&s!==null).filter(s=>s.noStore!==!0);if(n.length===0)return{records:[],decisions:[]};if(n.some(s=>typeof s.content!="string"||s.content.trim().length===0))throw new Error("memorize requires every record to include non-empty content.");if(n.some(s=>(s.scope??"session")==="session")&&!r.sessionId)throw new Error("memorize requires sessionId when storing session-scoped memory.");const a=r.recordedAt??new Date().toISOString(),m=r.requestId??R(new Date(a)),f=r.sessionId??`memory-api-${m}`;return this.memorizeCandidates(n.map(s=>({...s,content:s.content.trim()})),{...r,requestId:m,sessionId:f,recordedAt:a},{storeCandidateLog:e.storeCandidateLog!==!1,forceStore:e.forceStore===!0})}async recall(e,o){if(typeof e.query!="string"||e.query.trim().length===0)throw new Error("recall requires a non-empty query.");const r=w(o),n=W(e,r),a=typeof e.topK=="number"&&Number.isInteger(e.topK)&&e.topK>0?e.topK:this.deps.policy?.retrieval.defaultTopK??5,m=e.kinds?.length?new Set(e.kinds):null;return{items:(await this.rankRecallCandidates({query:e.query.trim(),scopes:n,kinds:m,topK:a,includeStale:e.includeStale===!0,context:r})).slice(0,a).map(({record:s})=>s)}}async buildPromptRecall(e,o){if(!_(e.query))return{items:[]};const r=w(o),n=(await this.rankRecallCandidates({query:e.query,scopes:e.scopes??k,kinds:e.kinds?.length?new Set(e.kinds):null,topK:e.topK??this.deps.policy?.retrieval.maxPromptMemories??8,includeStale:e.includeStale===!0,context:r})).filter(({record:s})=>!F(s)).map(s=>{const c=s.record.scope==="session"?4:s.record.scope==="agent"?3:s.record.scope==="user"?2:1;return{record:s.record,scope:s.record.scope,content:[`# Durable ${s.record.scope[0].toUpperCase()}${s.record.scope.slice(1)} Memory`,"",`- summary: ${s.record.summary}`,`- kind: ${s.record.kind}`,`- scope: ${s.record.scope}`,`- confidence: ${s.record.confidence.toFixed(2)}`,...s.record.knowledgeIdentity?[`- knowledge_identity: ${s.record.knowledgeIdentity}`,`- knowledge_operation: ${s.record.knowledgeOperation??"create"}`]:[],"",s.record.content].join(`
|
|
7
|
+
`),score:c+T(s.record.lastConfirmedAt)+s.record.confidence}}).sort((s,c)=>c.score-s.score);if(n.length===0)return{items:[]};const a=this.deps.policy?.retrieval.maxPromptMemories??8,m=n.slice(0,a);if(m.length===0)return{items:[]};const f=re(m);return{items:f.map(s=>s.record),context:f.map(s=>s.content).join(`
|
|
8
|
+
|
|
9
|
+
`)}}async buildPromptContext(e,o){return(await this.buildPromptRecall(e,o)).context}async list(e={},o){const r={...o},n=Array.isArray(e.scopes)&&e.scopes.length>0?Array.from(new Set(e.scopes)):k,a=e.kinds?.length?new Set(e.kinds):null,m=e.status?.length?new Set(e.status):new Set(["active"]),f=typeof e.limit=="number"&&Number.isInteger(e.limit)&&e.limit>0?e.limit:Number.POSITIVE_INFINITY;return{items:(await h(this.deps.store,n)).filter(c=>m.has(c.status)).filter(c=>!a||a.has(c.kind)).filter(c=>U(c,{sessionId:r.sessionId,agentId:r.agentId,workspaceId:r.workspaceId,userId:r.userId,projectId:r.projectId})).sort((c,d)=>d.lastConfirmedAt.localeCompare(c.lastConfirmedAt)).slice(0,f)}}async update(e,o){if(typeof e.memoryId!="string"||e.memoryId.trim().length===0)throw new Error("updateMemory requires a non-empty memoryId.");const r=await j(this.deps.store,e.memoryId.trim());if(!r)throw new Error(`Memory record not found: ${e.memoryId}`);const n=new Date().toISOString(),a=typeof e.content=="string"?e.content.trim():r.content;if(a.length===0)throw new Error("updateMemory requires content to remain non-empty.");const m={...r,content:a,summary:typeof e.summary=="string"?e.summary.trim()||A(a):typeof e.content=="string"?A(a):r.summary,status:e.status??r.status,confidence:typeof e.confidence=="number"?Math.max(0,Math.min(1,e.confidence)):r.confidence,expiresAt:e.expiresAt===void 0?r.expiresAt:e.expiresAt??void 0,sourceType:typeof e.sourceType=="string"&&e.sourceType.trim().length>0?e.sourceType.trim():r.sourceType,sourceRefs:Array.isArray(e.sourceRefs)?Array.from(new Set(e.sourceRefs.map(f=>f.trim()).filter(f=>f.length>0))):r.sourceRefs,tags:Array.isArray(e.tags)?Array.from(new Set(e.tags.map(f=>f.trim()).filter(f=>f.length>0))):r.tags,knowledgeIdentity:typeof e.knowledgeIdentity=="string"&&e.knowledgeIdentity.trim().length>0?e.knowledgeIdentity.trim():r.knowledgeIdentity,knowledgeOperation:e.knowledgeOperation==="create"||e.knowledgeOperation==="update"||e.knowledgeOperation==="delete"?e.knowledgeOperation:r.knowledgeOperation,observedAt:typeof e.observedAt=="string"&&e.observedAt.trim().length>0?e.observedAt:r.observedAt,lastConfirmedAt:typeof e.lastConfirmedAt=="string"&&e.lastConfirmedAt.trim().length>0?e.lastConfirmedAt:n,provenance:e.provenance?{...r.provenance,...e.provenance}:r.provenance,revision:r.revision+1};return await N(this.deps.store,m,n),await this.rebuildVectorIndex(),await this.refreshStructuredMemoryScope(m,o),m}async remove(e,o){if(typeof e.memoryId!="string"||e.memoryId.trim().length===0)throw new Error("removeMemory requires a non-empty memoryId.");const r=await j(this.deps.store,e.memoryId.trim());if(!r)throw new Error(`Memory record not found: ${e.memoryId}`);return await V(this.deps.store,r.scope,r.id),await this.rebuildVectorIndex(),await this.refreshStructuredMemoryScope(r,o),r}async memorizeCandidates(e,o,r){const n=w(o),a=e.filter(u=>u.noStore!==!0).filter(u=>typeof u.content=="string"&&u.content.trim().length>0).map(u=>({...u,content:u.content.trim()}));if(a.length===0)return{records:[],decisions:[]};const m=Array.from(new Set(a.flatMap(u=>x(u)))),f=await h(this.deps.store,m),s=await te({candidates:a,existingRecords:f,resolveVectorStore:this.deps.resolveVectorStore}),c=ee(a,f).map(u=>({record:u,score:E(u)})),d=Z(s,c),i=this.deps.transformCandidates?await this.deps.transformCandidates({candidates:a,context:n,existingRecords:d}):a,l=r.forceStore===!0?i:i.filter(u=>B(this.deps.policy,u));if(l.length===0)return{records:[],decisions:[]};r.storeCandidateLog&&await this.deps.store.put(["memories","candidates",n.sessionId??`memory-api-${n.requestId??"unknown"}`],`${n.requestId}.json`,{requestId:n.requestId,sessionId:n.sessionId,storedAt:n.recordedAt,candidates:l});const p=await P({store:this.deps.store,candidates:l,sessionId:n.sessionId??`memory-api-${n.requestId??"unknown"}`,requestId:n.requestId??R(new Date(n.recordedAt??new Date().toISOString())),agentId:n.agentId,workspaceId:n.workspaceId,userId:n.userId??"default",projectId:n.projectId??n.workspaceId,recordedAt:n.recordedAt??new Date().toISOString()});return await this.rebuildVectorIndex(),await this.refreshAfterWrite(l,n),p}async refreshAfterWrite(e,o){const r={session:e.filter(a=>(a.scope??"session")==="session"),agent:e.filter(a=>(a.scope??"session")==="agent"),workspace:e.filter(a=>(a.scope??"session")==="workspace"),user:e.filter(a=>(a.scope??"session")==="user"),project:e.filter(a=>(a.scope??"session")==="project")},n=[];for(const a of k){if(r[a].length===0)continue;const m=a==="session"?12:20;n.push(this.appendMemoryDigest(this.deps.resolveNamespace(a,o),"tool-memory.md",r[a],m,v[a].replace("Structured","Tool"))),n.push(b({store:this.deps.store,namespace:this.deps.resolveNamespace(a,o),title:v[a],scope:a,maxEntries:m,config:this.deps.maintenanceConfig??void 0}))}await Promise.all(n)}async appendMemoryDigest(e,o,r,n,a){const m=await this.deps.store.get(e,o),s=[...Array.isArray(m?.value?.items)?(m?.value).items??[]:[]];for(const i of r)s.some(l=>l.content===i.content&&(l.scope??"session")===(i.scope??"session"))||s.push(i);const c=s.slice(-n),d=new Map;for(const i of c){const l=q(i.kind),p=d.get(l)??[];p.push({...i,kind:l}),d.set(l,p)}await this.deps.store.put(e,o,{content:`${z(a,c)}
|
|
10
|
+
`,items:c}),await Promise.all(Array.from(d.entries()).map(async([i,l])=>{await this.deps.store.put(e,`${i}.md`,{content:`${z(`${a} (${i})`,l)}
|
|
11
|
+
`,items:l})}))}async refreshStructuredMemoryScope(e,o){const r=e.provenance,n={sessionId:r.sessionId,agentId:r.agentId??o.agentId,workspaceId:r.workspaceId??o.workspaceId,userId:r.userId??o.userId,projectId:r.projectId??o.projectId},a=e.scope==="session"?12:20;await b({store:this.deps.store,namespace:this.deps.resolveNamespace(e.scope,w(n)),title:v[e.scope],scope:e.scope,maxEntries:a,config:this.deps.maintenanceConfig??void 0})}async rebuildVectorIndex(){const e=await this.deps.resolveVectorStore();if(!e)return;const o=(await h(this.deps.store,k)).filter(r=>r.status==="active");try{if(await e.delete({deleteAll:!0}),o.length===0)return;await e.addDocuments(o.map(r=>({pageContent:`${r.summary}
|
|
12
|
+
${r.content}`,metadata:{recordId:r.id,scope:r.scope,kind:r.kind,status:r.status,sessionId:r.provenance.sessionId,agentId:r.provenance.agentId,workspaceId:r.provenance.workspaceId,userId:r.provenance.userId,projectId:r.provenance.projectId,confidence:r.confidence,lastConfirmedAt:r.lastConfirmedAt}})))}catch{}}async rankRecallCandidates(e){const r=(await h(this.deps.store,e.scopes)).filter(s=>$(s,{sessionId:e.context.sessionId,agentId:e.context.agentId,workspaceId:e.context.workspaceId,userId:e.context.userId??"default",projectId:e.context.projectId??e.context.workspaceId})).filter(s=>e.includeStale?s.status==="active"||s.status==="stale":s.status==="active").filter(s=>e.kinds?e.kinds.has(s.kind):!0).map(s=>({record:s,score:S(s,e.query)})),n=new Map;for(const s of r)n.set(M(s.record),s);const a=await this.deps.resolveVectorStore();if(a)try{const s=await a.similaritySearch(e.query,Math.max(e.topK,this.deps.policy?.retrieval.maxPromptMemories??e.topK));for(const c of s){const d=typeof c.metadata=="object"&&c.metadata&&!Array.isArray(c.metadata)?c.metadata:{},i=typeof d.recordId=="string"?d.recordId:void 0,l=d.scope;if(!i||l!=="session"&&l!=="agent"&&l!=="workspace"&&l!=="user"&&l!=="project")continue;const p=await D(this.deps.store,l,i);if(!p||!$(p,{sessionId:e.context.sessionId,agentId:e.context.agentId,workspaceId:e.context.workspaceId,userId:e.context.userId??"default",projectId:e.context.projectId??e.context.workspaceId})||(e.includeStale?p.status!=="active"&&p.status!=="stale":p.status!=="active")||e.kinds&&!e.kinds.has(p.kind))continue;const u=M(p),g=S(p,e.query)+(typeof c.score=="number"?c.score:0),C=n.get(u);(!C||g>C.score)&&n.set(u,{record:p,score:g})}}catch{}const m=e.scopes.some(s=>s==="session"||s==="agent"||s==="workspace"),f=this.deps.getMem0SemanticRecall?.()??null;if(!f||!m)return Array.from(n.values()).sort((s,c)=>c.score-s.score);try{const s=await f.search({query:e.query,topK:Math.max(e.topK,this.deps.policy?.retrieval.maxPromptMemories??e.topK),agentId:e.context.agentId,sessionId:e.context.sessionId});for(const c of s){const d=ne(c,{sessionId:e.context.sessionId,agentId:e.context.agentId,workspaceId:e.context.workspaceId,userId:e.context.userId??"default",projectId:e.context.projectId??e.context.workspaceId});if(!e.scopes.includes(d.scope)||e.kinds&&!e.kinds.has(d.kind))continue;const i=M(d);n.has(i)||n.set(i,{record:d,score:S(d,e.query)+Math.max(0,Math.min(1,c.score))*4})}return Array.from(n.values()).sort((c,d)=>d.score-c.score)}catch{return Array.from(n.values()).sort((s,c)=>c.score-s.score)}}}function fe(t){return new ae(t)}export{ae as DefaultKnowledgeModule,fe as createKnowledgeModule};
|