@botbotgo/agent-harness 0.0.281 → 0.0.283
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 +20 -9
- package/README.zh.md +20 -9
- package/dist/config/catalogs/stores.yaml +1 -1
- package/dist/config/catalogs/vector-stores.yaml +1 -1
- package/dist/config/knowledge/knowledge-runtime.yaml +60 -0
- package/dist/config/runtime/runtime-memory.yaml +3 -3
- package/dist/contracts/runtime.d.ts +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/init-project.js +50 -4
- package/dist/knowledge/config.d.ts +11 -0
- package/dist/knowledge/config.js +32 -0
- package/dist/knowledge/contracts.d.ts +45 -0
- package/dist/knowledge/contracts.js +1 -0
- package/dist/knowledge/index.d.ts +4 -0
- package/dist/knowledge/index.js +2 -0
- package/dist/knowledge/module.d.ts +21 -0
- package/dist/knowledge/module.js +594 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +1 -0
- package/dist/persistence/file-store.js +29 -25
- package/dist/persistence/sqlite-store.js +1 -1
- package/dist/runtime/harness/run/resources.js +4 -2
- package/dist/runtime/harness/system/runtime-memory-policy.js +1 -1
- package/dist/runtime/harness/system/runtime-memory-records.js +4 -0
- package/dist/runtime/harness/system/runtime-memory-sync.js +2 -2
- package/dist/runtime/harness/system/thread-memory-sync.js +1 -1
- package/dist/runtime/harness.d.ts +1 -16
- package/dist/runtime/harness.js +95 -613
- package/dist/runtime/maintenance/checkpoint-maintenance.js +2 -1
- package/dist/runtime/maintenance/runtime-record-maintenance.js +2 -1
- package/dist/runtime/support/llamaindex.js +1 -1
- package/dist/runtime/support/runtime-factories.js +6 -3
- package/dist/runtime/support/runtime-layout.d.ts +6 -0
- package/dist/runtime/support/runtime-layout.js +19 -0
- package/package.json +1 -1
package/dist/runtime/harness.js
CHANGED
|
@@ -10,7 +10,7 @@ import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getR
|
|
|
10
10
|
import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, } from "./support/harness-support.js";
|
|
11
11
|
import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
|
|
12
12
|
import { RuntimeMemorySync, readRuntimeMemorySyncConfig } from "./harness/system/runtime-memory-sync.js";
|
|
13
|
-
import {
|
|
13
|
+
import { SqliteBackedStore } from "./harness/system/store.js";
|
|
14
14
|
import { HealthMonitor, readHealthMonitorConfig, } from "./harness/system/health-monitor.js";
|
|
15
15
|
import { normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
|
|
16
16
|
import { emitHarnessEvent, } from "./harness/events/events.js";
|
|
@@ -32,17 +32,16 @@ import { describeWorkspaceInventory, getAgentInventoryRecord, listAgentSkills as
|
|
|
32
32
|
import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
|
|
33
33
|
import { Mem0IngestionSync, Mem0SemanticRecall, readMem0RuntimeConfig, } from "./harness/system/mem0-ingestion-sync.js";
|
|
34
34
|
import { createRuntimeMemoryManager, RuntimeMemoryFormationSync, readRuntimeMemoryFormationConfig, } from "./harness/system/runtime-memory-manager.js";
|
|
35
|
-
import {
|
|
36
|
-
import { findMemoryRecordById, getMemoryRecord, listMemoryRecordsForScopes, persistStructuredMemoryRecords, removeMemoryRecord, updateMemoryRecord, } from "./harness/system/runtime-memory-records.js";
|
|
37
|
-
import { consolidateStructuredMemoryScope } from "./harness/system/runtime-memory-consolidation.js";
|
|
38
|
-
import { normalizeLangMemMemoryKind, readRuntimeMemoryMaintenanceConfig, readRuntimeMemoryPolicyConfig, resolveMemoryNamespace, scoreMemoryText, } from "./harness/system/runtime-memory-policy.js";
|
|
35
|
+
import { readRuntimeMemoryMaintenanceConfig, readRuntimeMemoryPolicyConfig, resolveMemoryNamespace, } from "./harness/system/runtime-memory-policy.js";
|
|
39
36
|
import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
|
|
37
|
+
import { resolveKnowledgeStorePath } from "./support/runtime-layout.js";
|
|
40
38
|
import { initializeHarnessRuntime, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
|
|
41
39
|
import { traceStartupStage } from "./startup-tracing.js";
|
|
42
40
|
import { normalizeProcessExecutablePath } from "./support/runtime-env.js";
|
|
43
41
|
import { streamHarnessRun } from "./harness/run/stream-run.js";
|
|
44
42
|
import { defaultRequestedAgentId, prepareRunStart } from "./harness/run/start-run.js";
|
|
45
43
|
import { buildRequestInspectionRecord, buildSessionInspectionRecord, deleteSessionRecord, deleteThreadRecord, getPublicApproval, listPublicApprovals, } from "./harness/run/thread-records.js";
|
|
44
|
+
import { createKnowledgeModule } from "../knowledge/index.js";
|
|
46
45
|
const ACTIVE_RUN_STATES = [
|
|
47
46
|
"queued",
|
|
48
47
|
"claimed",
|
|
@@ -196,6 +195,7 @@ export class AgentHarnessRuntime {
|
|
|
196
195
|
mem0SemanticRecall;
|
|
197
196
|
runtimeMemoryFormationConfig;
|
|
198
197
|
runtimeMemoryManager;
|
|
198
|
+
knowledgeModule;
|
|
199
199
|
runtimeMemoryFormationSync;
|
|
200
200
|
unregisterRuntimeMemoryFormationSync;
|
|
201
201
|
resolvedRuntimeAdapterOptions;
|
|
@@ -257,11 +257,11 @@ export class AgentHarnessRuntime {
|
|
|
257
257
|
normalizeProcessExecutablePath();
|
|
258
258
|
this.runtimeEntryBindings = inferRoutingBindings(this.workspace).runtimeEntryBindings;
|
|
259
259
|
this.defaultRuntimeEntryBinding = this.runtimeEntryBindings[0];
|
|
260
|
-
this.defaultRunRootValue = this.defaultRuntimeEntryBinding?.harnessRuntime.runRoot ?? `${this.workspace.workspaceRoot}
|
|
260
|
+
this.defaultRunRootValue = this.defaultRuntimeEntryBinding?.harnessRuntime.runRoot ?? `${this.workspace.workspaceRoot}/.agent`;
|
|
261
261
|
const runRoot = this.defaultRunRoot();
|
|
262
262
|
this.persistence = new SqlitePersistence(runRoot);
|
|
263
263
|
const defaultStoreConfig = this.defaultRuntimeEntryBinding?.harnessRuntime.store;
|
|
264
|
-
this.defaultStore = resolveStoreFromConfig(this.stores, defaultStoreConfig, runRoot) ?? new
|
|
264
|
+
this.defaultStore = resolveStoreFromConfig(this.stores, defaultStoreConfig, runRoot) ?? new SqliteBackedStore(resolveKnowledgeStorePath(runRoot));
|
|
265
265
|
const runtimeMemoryStoreConfig = typeof this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory?.store === "object" &&
|
|
266
266
|
this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory?.store
|
|
267
267
|
? this.defaultRuntimeEntryBinding.harnessRuntime.runtimeMemory.store
|
|
@@ -292,12 +292,12 @@ export class AgentHarnessRuntime {
|
|
|
292
292
|
this.routingDefaultAgentId = getRoutingDefaultAgentId(workspace.refs);
|
|
293
293
|
if (isThreadMemorySyncEnabled(workspace)) {
|
|
294
294
|
this.threadMemorySync = new ThreadMemorySync(this.persistence, this.runtimeMemoryStore, {
|
|
295
|
-
resolveThreadNamespace: (
|
|
295
|
+
resolveThreadNamespace: (sessionId) => {
|
|
296
296
|
const binding = this.defaultRuntimeEntryBinding;
|
|
297
297
|
if (!binding) {
|
|
298
|
-
return ["memories", "
|
|
298
|
+
return ["memories", "sessions", sessionId];
|
|
299
299
|
}
|
|
300
|
-
return this.resolveMemoryNamespace("thread", binding, {
|
|
300
|
+
return this.resolveMemoryNamespace("thread", binding, { sessionId });
|
|
301
301
|
},
|
|
302
302
|
});
|
|
303
303
|
this.unregisterThreadMemorySync = this.eventBus.registerProjection(this.threadMemorySync);
|
|
@@ -309,12 +309,12 @@ export class AgentHarnessRuntime {
|
|
|
309
309
|
const runtimeMemorySyncConfig = readRuntimeMemorySyncConfig(this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory);
|
|
310
310
|
if (runtimeMemorySyncConfig) {
|
|
311
311
|
this.runtimeMemorySync = new RuntimeMemorySync(this.persistence, this.runtimeMemoryStore, runtimeMemorySyncConfig, {
|
|
312
|
-
resolveThreadNamespace: (
|
|
312
|
+
resolveThreadNamespace: (sessionId) => {
|
|
313
313
|
const binding = this.defaultRuntimeEntryBinding;
|
|
314
314
|
if (!binding) {
|
|
315
|
-
return ["memories", "
|
|
315
|
+
return ["memories", "sessions", sessionId];
|
|
316
316
|
}
|
|
317
|
-
return this.resolveMemoryNamespace("thread", binding, {
|
|
317
|
+
return this.resolveMemoryNamespace("thread", binding, { sessionId });
|
|
318
318
|
},
|
|
319
319
|
});
|
|
320
320
|
this.unregisterRuntimeMemorySync = this.eventBus.registerProjection(this.runtimeMemorySync);
|
|
@@ -345,17 +345,54 @@ export class AgentHarnessRuntime {
|
|
|
345
345
|
modelResolver: this.resolvedRuntimeAdapterOptions.modelResolver,
|
|
346
346
|
})
|
|
347
347
|
: null;
|
|
348
|
+
this.knowledgeModule = createKnowledgeModule({
|
|
349
|
+
store: this.runtimeMemoryStore,
|
|
350
|
+
policy: this.runtimeMemoryPolicy,
|
|
351
|
+
maintenanceConfig: this.runtimeMemoryMaintenanceConfig,
|
|
352
|
+
resolveNamespace: (scope, context) => {
|
|
353
|
+
const binding = this.defaultRuntimeEntryBinding;
|
|
354
|
+
if (!binding) {
|
|
355
|
+
const identifier = scope === "thread"
|
|
356
|
+
? context.sessionId
|
|
357
|
+
: scope === "agent"
|
|
358
|
+
? context.agentId
|
|
359
|
+
: scope === "workspace"
|
|
360
|
+
? context.workspaceId
|
|
361
|
+
: scope === "user"
|
|
362
|
+
? context.userId ?? "default"
|
|
363
|
+
: context.projectId ?? context.workspaceId;
|
|
364
|
+
return ["memories", `${scope}s`, identifier ?? "default"];
|
|
365
|
+
}
|
|
366
|
+
return this.resolveMemoryNamespace(scope, binding, {
|
|
367
|
+
sessionId: context.sessionId,
|
|
368
|
+
agentId: context.agentId,
|
|
369
|
+
userId: context.userId,
|
|
370
|
+
projectId: context.projectId,
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
resolveVectorStore: () => this.resolveRuntimeMemoryVectorStore(),
|
|
374
|
+
transformCandidates: this.runtimeMemoryManager && this.defaultRuntimeEntryBinding
|
|
375
|
+
? ({ candidates, context, existingRecords }) => this.runtimeMemoryManager.transform({
|
|
376
|
+
candidates,
|
|
377
|
+
binding: this.defaultRuntimeEntryBinding,
|
|
378
|
+
threadId: context.sessionId ?? `memory-api-${context.requestId ?? "unknown"}`,
|
|
379
|
+
runId: context.requestId ?? createPersistentId(new Date(context.recordedAt ?? new Date().toISOString())),
|
|
380
|
+
recordedAt: context.recordedAt ?? new Date().toISOString(),
|
|
381
|
+
existingRecords,
|
|
382
|
+
})
|
|
383
|
+
: undefined,
|
|
384
|
+
getMem0SemanticRecall: () => this.mem0SemanticRecall,
|
|
385
|
+
});
|
|
348
386
|
if (runtimeMemoryFormationConfig) {
|
|
349
|
-
this.runtimeMemoryFormationSync = new RuntimeMemoryFormationSync(this.persistence, runtimeMemoryFormationConfig, (input) => this.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
runId: input.runId,
|
|
387
|
+
this.runtimeMemoryFormationSync = new RuntimeMemoryFormationSync(this.persistence, runtimeMemoryFormationConfig, (input) => this.knowledgeModule.memorizeCandidates(input.candidates, {
|
|
388
|
+
sessionId: input.threadId,
|
|
389
|
+
requestId: input.runId,
|
|
353
390
|
agentId: input.agentId,
|
|
391
|
+
workspaceId: this.getWorkspaceId(this.defaultRuntimeEntryBinding),
|
|
354
392
|
userId: input.userId,
|
|
355
393
|
projectId: input.projectId,
|
|
356
394
|
recordedAt: input.recordedAt,
|
|
357
|
-
|
|
358
|
-
}).then(() => undefined), runRoot);
|
|
395
|
+
}, { storeCandidateLog: false }).then(() => undefined), runRoot);
|
|
359
396
|
this.unregisterRuntimeMemoryFormationSync = this.eventBus.registerProjection(this.runtimeMemoryFormationSync);
|
|
360
397
|
}
|
|
361
398
|
else {
|
|
@@ -480,35 +517,17 @@ export class AgentHarnessRuntime {
|
|
|
480
517
|
if (!binding) {
|
|
481
518
|
throw new Error("memorize requires a runtime entry binding.");
|
|
482
519
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
.
|
|
488
|
-
|
|
489
|
-
if (candidates.length === 0) {
|
|
490
|
-
return { records: [], decisions: [] };
|
|
491
|
-
}
|
|
492
|
-
if (candidates.some((record) => typeof record.content !== "string" || record.content.trim().length === 0)) {
|
|
493
|
-
throw new Error("memorize requires every record to include non-empty content.");
|
|
494
|
-
}
|
|
495
|
-
const sessionId = input.sessionId;
|
|
496
|
-
const requestId = input.requestId;
|
|
497
|
-
if (candidates.some((record) => (record.scope ?? "thread") === "thread") && !sessionId) {
|
|
498
|
-
throw new Error("memorize requires sessionId when storing thread-scoped memory.");
|
|
499
|
-
}
|
|
500
|
-
const recordedAt = input.recordedAt ?? new Date().toISOString();
|
|
501
|
-
const runId = requestId ?? createPersistentId(new Date(recordedAt));
|
|
502
|
-
const threadId = sessionId ?? `memory-api-${runId}`;
|
|
503
|
-
return this.persistStructuredMemoryCandidates(binding, {
|
|
504
|
-
candidates: candidates.map((record) => ({ ...record, content: record.content.trim() })),
|
|
505
|
-
threadId,
|
|
506
|
-
runId,
|
|
520
|
+
return this.knowledgeModule.memorize({
|
|
521
|
+
records: input.records,
|
|
522
|
+
storeCandidateLog: true,
|
|
523
|
+
}, {
|
|
524
|
+
sessionId: input.sessionId,
|
|
525
|
+
requestId: input.requestId,
|
|
507
526
|
agentId: input.agentId ?? binding.agent.id,
|
|
527
|
+
workspaceId: this.getWorkspaceId(binding),
|
|
508
528
|
userId: input.userId,
|
|
509
529
|
projectId: input.projectId,
|
|
510
|
-
recordedAt,
|
|
511
|
-
storeCandidateLog: true,
|
|
530
|
+
recordedAt: input.recordedAt,
|
|
512
531
|
});
|
|
513
532
|
}
|
|
514
533
|
async recall(input) {
|
|
@@ -516,129 +535,46 @@ export class AgentHarnessRuntime {
|
|
|
516
535
|
if (!binding) {
|
|
517
536
|
throw new Error("recall requires a runtime entry binding.");
|
|
518
537
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const projectId = input.projectId ?? workspaceId;
|
|
527
|
-
const scopes = this.resolveRecallScopes(input);
|
|
528
|
-
const topK = typeof input.topK === "number" && Number.isInteger(input.topK) && input.topK > 0
|
|
529
|
-
? input.topK
|
|
530
|
-
: (this.runtimeMemoryPolicy?.retrieval.defaultTopK ?? 5);
|
|
531
|
-
const kinds = input.kinds?.length ? new Set(input.kinds) : null;
|
|
532
|
-
const items = (await this.rankRecallCandidates(binding, {
|
|
533
|
-
query: input.query.trim(),
|
|
534
|
-
scopes,
|
|
535
|
-
kinds,
|
|
536
|
-
topK,
|
|
537
|
-
includeStale: input.includeStale === true,
|
|
538
|
-
filters: {
|
|
539
|
-
threadId: sessionId,
|
|
540
|
-
agentId,
|
|
541
|
-
workspaceId: input.workspaceId ?? workspaceId,
|
|
542
|
-
userId,
|
|
543
|
-
projectId,
|
|
544
|
-
},
|
|
545
|
-
}))
|
|
546
|
-
.slice(0, topK)
|
|
547
|
-
.map(({ record }) => record);
|
|
548
|
-
return { items };
|
|
538
|
+
return this.knowledgeModule.recall(input, {
|
|
539
|
+
sessionId: input.sessionId,
|
|
540
|
+
agentId: input.agentId ?? binding.agent.id,
|
|
541
|
+
workspaceId: input.workspaceId ?? this.getWorkspaceId(binding),
|
|
542
|
+
userId: input.userId,
|
|
543
|
+
projectId: input.projectId,
|
|
544
|
+
});
|
|
549
545
|
}
|
|
550
546
|
async listMemories(input = {}) {
|
|
551
547
|
const binding = this.defaultRuntimeEntryBinding;
|
|
552
548
|
if (!binding) {
|
|
553
549
|
throw new Error("listMemories requires a runtime entry binding.");
|
|
554
550
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const scopes = Array.isArray(input.scopes) && input.scopes.length > 0
|
|
558
|
-
? Array.from(new Set(input.scopes))
|
|
559
|
-
: ["thread", "agent", "workspace", "user", "project"];
|
|
560
|
-
const kinds = input.kinds?.length ? new Set(input.kinds) : null;
|
|
561
|
-
const statuses = input.status?.length ? new Set(input.status) : new Set(["active"]);
|
|
562
|
-
const limit = typeof input.limit === "number" && Number.isInteger(input.limit) && input.limit > 0
|
|
563
|
-
? input.limit
|
|
564
|
-
: Number.POSITIVE_INFINITY;
|
|
565
|
-
const items = (await listMemoryRecordsForScopes(this.runtimeMemoryStore, scopes))
|
|
566
|
-
.filter((record) => statuses.has(record.status))
|
|
567
|
-
.filter((record) => !kinds || kinds.has(record.kind))
|
|
568
|
-
.filter((record) => this.matchesMemoryFilters(record, {
|
|
569
|
-
threadId: sessionId,
|
|
551
|
+
return this.knowledgeModule.list(input, {
|
|
552
|
+
sessionId: input.sessionId,
|
|
570
553
|
agentId: input.agentId,
|
|
571
|
-
workspaceId: input.workspaceId ??
|
|
554
|
+
workspaceId: input.workspaceId ?? this.getWorkspaceId(binding),
|
|
572
555
|
userId: input.userId,
|
|
573
556
|
projectId: input.projectId,
|
|
574
|
-
})
|
|
575
|
-
.sort((left, right) => right.lastConfirmedAt.localeCompare(left.lastConfirmedAt))
|
|
576
|
-
.slice(0, limit);
|
|
577
|
-
return { items };
|
|
557
|
+
});
|
|
578
558
|
}
|
|
579
559
|
async updateMemory(input) {
|
|
580
560
|
const binding = this.defaultRuntimeEntryBinding;
|
|
581
561
|
if (!binding) {
|
|
582
562
|
throw new Error("updateMemory requires a runtime entry binding.");
|
|
583
563
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
if (!existing) {
|
|
589
|
-
throw new Error(`Memory record not found: ${input.memoryId}`);
|
|
590
|
-
}
|
|
591
|
-
const updatedAt = new Date().toISOString();
|
|
592
|
-
const nextContent = typeof input.content === "string"
|
|
593
|
-
? input.content.trim()
|
|
594
|
-
: existing.content;
|
|
595
|
-
if (nextContent.length === 0) {
|
|
596
|
-
throw new Error("updateMemory requires content to remain non-empty.");
|
|
597
|
-
}
|
|
598
|
-
const next = {
|
|
599
|
-
...existing,
|
|
600
|
-
content: nextContent,
|
|
601
|
-
summary: typeof input.summary === "string"
|
|
602
|
-
? input.summary.trim() || this.summarizeMemoryContent(nextContent)
|
|
603
|
-
: (typeof input.content === "string" ? this.summarizeMemoryContent(nextContent) : existing.summary),
|
|
604
|
-
status: input.status ?? existing.status,
|
|
605
|
-
confidence: typeof input.confidence === "number" ? Math.max(0, Math.min(1, input.confidence)) : existing.confidence,
|
|
606
|
-
expiresAt: input.expiresAt === undefined ? existing.expiresAt : (input.expiresAt ?? undefined),
|
|
607
|
-
sourceType: typeof input.sourceType === "string" && input.sourceType.trim().length > 0 ? input.sourceType.trim() : existing.sourceType,
|
|
608
|
-
sourceRefs: Array.isArray(input.sourceRefs)
|
|
609
|
-
? Array.from(new Set(input.sourceRefs.map((item) => item.trim()).filter((item) => item.length > 0)))
|
|
610
|
-
: existing.sourceRefs,
|
|
611
|
-
tags: Array.isArray(input.tags)
|
|
612
|
-
? Array.from(new Set(input.tags.map((item) => item.trim()).filter((item) => item.length > 0)))
|
|
613
|
-
: existing.tags,
|
|
614
|
-
observedAt: typeof input.observedAt === "string" && input.observedAt.trim().length > 0 ? input.observedAt : existing.observedAt,
|
|
615
|
-
lastConfirmedAt: typeof input.lastConfirmedAt === "string" && input.lastConfirmedAt.trim().length > 0
|
|
616
|
-
? input.lastConfirmedAt
|
|
617
|
-
: updatedAt,
|
|
618
|
-
provenance: input.provenance ? { ...existing.provenance, ...input.provenance } : existing.provenance,
|
|
619
|
-
revision: existing.revision + 1,
|
|
620
|
-
};
|
|
621
|
-
await updateMemoryRecord(this.runtimeMemoryStore, next, updatedAt);
|
|
622
|
-
await this.rebuildRuntimeMemoryVectorIndex();
|
|
623
|
-
await this.refreshStructuredMemoryScope(binding, next);
|
|
624
|
-
return next;
|
|
564
|
+
return this.knowledgeModule.update(input, {
|
|
565
|
+
agentId: binding.agent.id,
|
|
566
|
+
workspaceId: this.getWorkspaceId(binding),
|
|
567
|
+
});
|
|
625
568
|
}
|
|
626
569
|
async removeMemory(input) {
|
|
627
570
|
const binding = this.defaultRuntimeEntryBinding;
|
|
628
571
|
if (!binding) {
|
|
629
572
|
throw new Error("removeMemory requires a runtime entry binding.");
|
|
630
573
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if (!existing) {
|
|
636
|
-
throw new Error(`Memory record not found: ${input.memoryId}`);
|
|
637
|
-
}
|
|
638
|
-
await removeMemoryRecord(this.runtimeMemoryStore, existing.scope, existing.id);
|
|
639
|
-
await this.rebuildRuntimeMemoryVectorIndex();
|
|
640
|
-
await this.refreshStructuredMemoryScope(binding, existing);
|
|
641
|
-
return existing;
|
|
574
|
+
return this.knowledgeModule.remove(input, {
|
|
575
|
+
agentId: binding.agent.id,
|
|
576
|
+
workspaceId: this.getWorkspaceId(binding),
|
|
577
|
+
});
|
|
642
578
|
}
|
|
643
579
|
async getRequest(requestId) {
|
|
644
580
|
const request = await this.persistence.getRun(requestId);
|
|
@@ -1002,7 +938,7 @@ export class AgentHarnessRuntime {
|
|
|
1002
938
|
const workspaceId = this.getWorkspaceId(binding);
|
|
1003
939
|
const template = this.runtimeMemoryPolicy?.namespaces[scope] ?? `memories/${scope}s/{${scope}Id}`;
|
|
1004
940
|
return resolveMemoryNamespace(template, {
|
|
1005
|
-
|
|
941
|
+
sessionId: options.sessionId,
|
|
1006
942
|
agentId: options.agentId ?? binding.agent.id,
|
|
1007
943
|
workspaceId,
|
|
1008
944
|
userId: options.userId ?? "default",
|
|
@@ -1013,339 +949,16 @@ export class AgentHarnessRuntime {
|
|
|
1013
949
|
const workspaceRoot = binding.harnessRuntime.workspaceRoot ?? this.workspace.workspaceRoot;
|
|
1014
950
|
return path.basename(workspaceRoot) || "default";
|
|
1015
951
|
}
|
|
1016
|
-
summarizeMemoryContent(content) {
|
|
1017
|
-
return (content.trim().split("\n")[0] || content.trim()).slice(0, 240);
|
|
1018
|
-
}
|
|
1019
|
-
matchesMemoryFilters(record, filters) {
|
|
1020
|
-
const sessionId = String(record.provenance.sessionId ?? record.provenance.threadId ?? "");
|
|
1021
|
-
if (filters.threadId && sessionId !== filters.threadId) {
|
|
1022
|
-
return false;
|
|
1023
|
-
}
|
|
1024
|
-
if (filters.agentId && record.provenance.agentId !== filters.agentId) {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
if (filters.workspaceId && record.provenance.workspaceId !== filters.workspaceId) {
|
|
1028
|
-
return false;
|
|
1029
|
-
}
|
|
1030
|
-
if (filters.userId && record.provenance.userId !== filters.userId) {
|
|
1031
|
-
return false;
|
|
1032
|
-
}
|
|
1033
|
-
if (filters.projectId && record.provenance.projectId !== filters.projectId) {
|
|
1034
|
-
return false;
|
|
1035
|
-
}
|
|
1036
|
-
return true;
|
|
1037
|
-
}
|
|
1038
|
-
resolveRecallScopes(input) {
|
|
1039
|
-
if (Array.isArray(input.scopes) && input.scopes.length > 0) {
|
|
1040
|
-
return Array.from(new Set(input.scopes));
|
|
1041
|
-
}
|
|
1042
|
-
const scopes = new Set(["thread", "agent", "workspace"]);
|
|
1043
|
-
if (input.userId) {
|
|
1044
|
-
scopes.add("user");
|
|
1045
|
-
}
|
|
1046
|
-
if (input.projectId) {
|
|
1047
|
-
scopes.add("project");
|
|
1048
|
-
}
|
|
1049
|
-
return Array.from(scopes);
|
|
1050
|
-
}
|
|
1051
|
-
matchesRecallScope(record, filters) {
|
|
1052
|
-
if (record.scope === "thread") {
|
|
1053
|
-
return typeof filters.threadId === "string"
|
|
1054
|
-
&& String(record.provenance.sessionId ?? record.provenance.threadId ?? "") === filters.threadId;
|
|
1055
|
-
}
|
|
1056
|
-
if (record.scope === "agent") {
|
|
1057
|
-
return String(record.provenance.agentId ?? "") === filters.agentId;
|
|
1058
|
-
}
|
|
1059
|
-
if (record.scope === "workspace") {
|
|
1060
|
-
return String(record.provenance.workspaceId ?? filters.workspaceId) === filters.workspaceId;
|
|
1061
|
-
}
|
|
1062
|
-
if (record.scope === "user") {
|
|
1063
|
-
return String(record.provenance.userId ?? "default") === filters.userId;
|
|
1064
|
-
}
|
|
1065
|
-
return String(record.provenance.projectId ?? filters.workspaceId) === filters.projectId;
|
|
1066
|
-
}
|
|
1067
|
-
getMemoryScopeBoost(scope) {
|
|
1068
|
-
if (scope === "thread") {
|
|
1069
|
-
return 4;
|
|
1070
|
-
}
|
|
1071
|
-
if (scope === "agent") {
|
|
1072
|
-
return 2;
|
|
1073
|
-
}
|
|
1074
|
-
if (scope === "workspace") {
|
|
1075
|
-
return 1;
|
|
1076
|
-
}
|
|
1077
|
-
return 0;
|
|
1078
|
-
}
|
|
1079
|
-
memoryFreshnessBoost(value) {
|
|
1080
|
-
return Math.max(0, 1 - ((Date.now() - Date.parse(value)) / (1000 * 60 * 60 * 24 * 365)));
|
|
1081
|
-
}
|
|
1082
|
-
scoreStructuredRecord(query, record) {
|
|
1083
|
-
return (scoreMemoryText(query, `${record.summary}\n${record.content}`, this.getMemoryScopeBoost(record.scope)) +
|
|
1084
|
-
this.memoryFreshnessBoost(record.lastConfirmedAt) +
|
|
1085
|
-
record.confidence);
|
|
1086
|
-
}
|
|
1087
|
-
normalizeMemoryDedupKey(record) {
|
|
1088
|
-
return `${record.scope}::${record.summary.toLowerCase().replace(/\s+/g, " ").trim()}::${record.content.toLowerCase().replace(/\s+/g, " ").trim()}`;
|
|
1089
|
-
}
|
|
1090
|
-
inferMem0MemoryKind(hit) {
|
|
1091
|
-
const candidates = [
|
|
1092
|
-
...hit.categories,
|
|
1093
|
-
typeof hit.metadata.kind === "string" ? hit.metadata.kind : undefined,
|
|
1094
|
-
typeof hit.metadata.memoryType === "string" ? hit.metadata.memoryType : undefined,
|
|
1095
|
-
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
1096
|
-
for (const candidate of candidates) {
|
|
1097
|
-
const kind = normalizeLangMemMemoryKind(candidate);
|
|
1098
|
-
if (kind === "semantic" || kind === "episodic" || kind === "procedural") {
|
|
1099
|
-
return kind;
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
return "semantic";
|
|
1103
|
-
}
|
|
1104
|
-
inferMem0MemoryScope(hit, filters) {
|
|
1105
|
-
const metadataScope = typeof hit.metadata.scope === "string" ? hit.metadata.scope : undefined;
|
|
1106
|
-
if (metadataScope === "thread" || metadataScope === "agent" || metadataScope === "workspace" || metadataScope === "user" || metadataScope === "project") {
|
|
1107
|
-
return metadataScope;
|
|
1108
|
-
}
|
|
1109
|
-
if (typeof hit.metadata.threadId === "string" && filters.threadId && hit.metadata.threadId === filters.threadId) {
|
|
1110
|
-
return "thread";
|
|
1111
|
-
}
|
|
1112
|
-
if (hit.agentId === filters.agentId || hit.metadata.agentId === filters.agentId) {
|
|
1113
|
-
return "agent";
|
|
1114
|
-
}
|
|
1115
|
-
return "workspace";
|
|
1116
|
-
}
|
|
1117
|
-
createMem0MemoryRecord(hit, filters) {
|
|
1118
|
-
const scope = this.inferMem0MemoryScope(hit, filters);
|
|
1119
|
-
const kind = this.inferMem0MemoryKind(hit);
|
|
1120
|
-
const summary = hit.memory.split("\n")[0]?.trim() || hit.memory;
|
|
1121
|
-
return {
|
|
1122
|
-
id: `mem0:${hit.id}`,
|
|
1123
|
-
canonicalKey: `mem0:${hit.id}`,
|
|
1124
|
-
kind,
|
|
1125
|
-
scope,
|
|
1126
|
-
content: hit.memory,
|
|
1127
|
-
summary: summary.slice(0, 240),
|
|
1128
|
-
status: "active",
|
|
1129
|
-
confidence: Math.max(0, Math.min(1, hit.score || 0.5)),
|
|
1130
|
-
createdAt: hit.createdAt,
|
|
1131
|
-
observedAt: hit.createdAt,
|
|
1132
|
-
lastConfirmedAt: hit.updatedAt,
|
|
1133
|
-
sourceType: "mem0-search",
|
|
1134
|
-
sourceRefs: [`mem0:${hit.id}`],
|
|
1135
|
-
tags: hit.categories,
|
|
1136
|
-
provenance: {
|
|
1137
|
-
source: "mem0",
|
|
1138
|
-
sessionId: typeof hit.metadata.sessionId === "string"
|
|
1139
|
-
? hit.metadata.sessionId
|
|
1140
|
-
: typeof hit.metadata.threadId === "string"
|
|
1141
|
-
? hit.metadata.threadId
|
|
1142
|
-
: filters.threadId,
|
|
1143
|
-
requestId: (typeof hit.metadata.requestId === "string"
|
|
1144
|
-
? hit.metadata.requestId
|
|
1145
|
-
: typeof hit.metadata.runId === "string"
|
|
1146
|
-
? hit.metadata.runId
|
|
1147
|
-
: undefined),
|
|
1148
|
-
agentId: hit.agentId ?? (typeof hit.metadata.agentId === "string" ? hit.metadata.agentId : filters.agentId),
|
|
1149
|
-
workspaceId: filters.workspaceId,
|
|
1150
|
-
userId: filters.userId,
|
|
1151
|
-
projectId: filters.projectId,
|
|
1152
|
-
...hit.metadata,
|
|
1153
|
-
},
|
|
1154
|
-
revision: 1,
|
|
1155
|
-
supersedes: [],
|
|
1156
|
-
conflictsWith: [],
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
async rankRecallCandidates(binding, input) {
|
|
1160
|
-
const structuredRecords = await listMemoryRecordsForScopes(this.runtimeMemoryStore, input.scopes);
|
|
1161
|
-
const ranked = structuredRecords
|
|
1162
|
-
.filter((record) => this.matchesRecallScope(record, input.filters))
|
|
1163
|
-
.filter((record) => (input.includeStale ? record.status === "active" || record.status === "stale" : record.status === "active"))
|
|
1164
|
-
.filter((record) => (input.kinds ? input.kinds.has(record.kind) : true))
|
|
1165
|
-
.map((record) => ({
|
|
1166
|
-
record,
|
|
1167
|
-
score: this.scoreStructuredRecord(input.query, record),
|
|
1168
|
-
}));
|
|
1169
|
-
const deduped = new Map();
|
|
1170
|
-
for (const item of ranked) {
|
|
1171
|
-
deduped.set(this.normalizeMemoryDedupKey(item.record), item);
|
|
1172
|
-
}
|
|
1173
|
-
const vectorStore = await this.resolveRuntimeMemoryVectorStore();
|
|
1174
|
-
if (vectorStore) {
|
|
1175
|
-
try {
|
|
1176
|
-
const vectorHits = await vectorStore.similaritySearch(input.query, Math.max(input.topK, this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? input.topK));
|
|
1177
|
-
for (const hit of vectorHits) {
|
|
1178
|
-
const metadata = typeof hit.metadata === "object" && hit.metadata && !Array.isArray(hit.metadata)
|
|
1179
|
-
? hit.metadata
|
|
1180
|
-
: {};
|
|
1181
|
-
const recordId = typeof metadata.recordId === "string" ? metadata.recordId : undefined;
|
|
1182
|
-
const scope = metadata.scope;
|
|
1183
|
-
if (!recordId || (scope !== "thread" && scope !== "agent" && scope !== "workspace" && scope !== "user" && scope !== "project")) {
|
|
1184
|
-
continue;
|
|
1185
|
-
}
|
|
1186
|
-
const canonical = await getMemoryRecord(this.runtimeMemoryStore, scope, recordId);
|
|
1187
|
-
if (!canonical) {
|
|
1188
|
-
continue;
|
|
1189
|
-
}
|
|
1190
|
-
if (!this.matchesRecallScope(canonical, input.filters)) {
|
|
1191
|
-
continue;
|
|
1192
|
-
}
|
|
1193
|
-
if (input.includeStale ? canonical.status !== "active" && canonical.status !== "stale" : canonical.status !== "active") {
|
|
1194
|
-
continue;
|
|
1195
|
-
}
|
|
1196
|
-
if (input.kinds && !input.kinds.has(canonical.kind)) {
|
|
1197
|
-
continue;
|
|
1198
|
-
}
|
|
1199
|
-
const key = this.normalizeMemoryDedupKey(canonical);
|
|
1200
|
-
const score = this.scoreStructuredRecord(input.query, canonical) + (typeof hit.score === "number" ? hit.score : 0);
|
|
1201
|
-
const existing = deduped.get(key);
|
|
1202
|
-
if (!existing || score > existing.score) {
|
|
1203
|
-
deduped.set(key, { record: canonical, score });
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
catch {
|
|
1208
|
-
// Fail open to lexical/runtime ranking.
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
const supportsMem0Scope = input.scopes.some((scope) => scope === "thread" || scope === "agent" || scope === "workspace");
|
|
1212
|
-
if (!this.mem0SemanticRecall || !supportsMem0Scope) {
|
|
1213
|
-
return Array.from(deduped.values()).sort((left, right) => right.score - left.score);
|
|
1214
|
-
}
|
|
1215
|
-
try {
|
|
1216
|
-
const hits = await this.mem0SemanticRecall.search({
|
|
1217
|
-
query: input.query,
|
|
1218
|
-
topK: Math.max(input.topK, this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? input.topK),
|
|
1219
|
-
agentId: input.filters.agentId,
|
|
1220
|
-
threadId: input.filters.threadId,
|
|
1221
|
-
});
|
|
1222
|
-
for (const hit of hits) {
|
|
1223
|
-
const record = this.createMem0MemoryRecord(hit, input.filters);
|
|
1224
|
-
if (!input.scopes.includes(record.scope)) {
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1227
|
-
if (input.kinds && !input.kinds.has(record.kind)) {
|
|
1228
|
-
continue;
|
|
1229
|
-
}
|
|
1230
|
-
const key = this.normalizeMemoryDedupKey(record);
|
|
1231
|
-
if (deduped.has(key)) {
|
|
1232
|
-
continue;
|
|
1233
|
-
}
|
|
1234
|
-
deduped.set(key, {
|
|
1235
|
-
record,
|
|
1236
|
-
score: this.scoreStructuredRecord(input.query, record) + Math.max(0, Math.min(1, hit.score)) * 4,
|
|
1237
|
-
});
|
|
1238
|
-
}
|
|
1239
|
-
return Array.from(deduped.values()).sort((left, right) => right.score - left.score);
|
|
1240
|
-
}
|
|
1241
|
-
catch {
|
|
1242
|
-
return Array.from(deduped.values()).sort((left, right) => right.score - left.score);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
async rebuildRuntimeMemoryVectorIndex() {
|
|
1246
|
-
const vectorStore = await this.resolveRuntimeMemoryVectorStore();
|
|
1247
|
-
if (!vectorStore) {
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
const records = (await listMemoryRecordsForScopes(this.runtimeMemoryStore, ["thread", "agent", "workspace", "user", "project"]))
|
|
1251
|
-
.filter((record) => record.status === "active");
|
|
1252
|
-
try {
|
|
1253
|
-
await vectorStore.delete({ deleteAll: true });
|
|
1254
|
-
if (records.length === 0) {
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
await vectorStore.addDocuments(records.map((record) => ({
|
|
1258
|
-
pageContent: `${record.summary}\n${record.content}`,
|
|
1259
|
-
metadata: {
|
|
1260
|
-
recordId: record.id,
|
|
1261
|
-
scope: record.scope,
|
|
1262
|
-
kind: record.kind,
|
|
1263
|
-
status: record.status,
|
|
1264
|
-
sessionId: record.provenance.sessionId ?? record.provenance.threadId,
|
|
1265
|
-
agentId: record.provenance.agentId,
|
|
1266
|
-
workspaceId: record.provenance.workspaceId,
|
|
1267
|
-
userId: record.provenance.userId,
|
|
1268
|
-
projectId: record.provenance.projectId,
|
|
1269
|
-
confidence: record.confidence,
|
|
1270
|
-
lastConfirmedAt: record.lastConfirmedAt,
|
|
1271
|
-
},
|
|
1272
|
-
})));
|
|
1273
|
-
}
|
|
1274
|
-
catch {
|
|
1275
|
-
// Fail open: vector indexing must not break durable memory writes.
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
async refreshStructuredMemoryScope(binding, record) {
|
|
1279
|
-
const provenance = record.provenance;
|
|
1280
|
-
const maxEntries = record.scope === "thread" ? 12 : 20;
|
|
1281
|
-
const titleByScope = {
|
|
1282
|
-
thread: "Thread Structured Memory",
|
|
1283
|
-
agent: "Agent Structured Memory",
|
|
1284
|
-
workspace: "Workspace Structured Memory",
|
|
1285
|
-
user: "User Structured Memory",
|
|
1286
|
-
project: "Project Structured Memory",
|
|
1287
|
-
};
|
|
1288
|
-
await consolidateStructuredMemoryScope({
|
|
1289
|
-
store: this.runtimeMemoryStore,
|
|
1290
|
-
namespace: this.resolveMemoryNamespace(record.scope, binding, {
|
|
1291
|
-
threadId: provenance.sessionId,
|
|
1292
|
-
agentId: provenance.agentId,
|
|
1293
|
-
userId: provenance.userId,
|
|
1294
|
-
projectId: provenance.projectId,
|
|
1295
|
-
}),
|
|
1296
|
-
title: titleByScope[record.scope],
|
|
1297
|
-
scope: record.scope,
|
|
1298
|
-
maxEntries,
|
|
1299
|
-
config: this.runtimeMemoryMaintenanceConfig ?? undefined,
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
952
|
async buildRuntimeMemoryContext(binding, threadId, input) {
|
|
1303
953
|
const query = typeof input === "string" ? input : JSON.stringify(input ?? "");
|
|
1304
|
-
|
|
1305
|
-
const ranked = (await this.rankRecallCandidates(binding, {
|
|
954
|
+
return this.knowledgeModule.buildPromptContext({
|
|
1306
955
|
query,
|
|
1307
|
-
scopes: ["thread", "agent", "workspace", "user", "project"],
|
|
1308
|
-
kinds: null,
|
|
1309
956
|
topK: this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? 8,
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
userId: "default",
|
|
1316
|
-
projectId: workspaceId,
|
|
1317
|
-
},
|
|
1318
|
-
}))
|
|
1319
|
-
.map((record) => {
|
|
1320
|
-
const scopeBoost = record.record.scope === "thread"
|
|
1321
|
-
? 4
|
|
1322
|
-
: record.record.scope === "agent"
|
|
1323
|
-
? 3
|
|
1324
|
-
: record.record.scope === "user"
|
|
1325
|
-
? 2
|
|
1326
|
-
: 1;
|
|
1327
|
-
return {
|
|
1328
|
-
content: [
|
|
1329
|
-
`# Durable ${record.record.scope[0].toUpperCase()}${record.record.scope.slice(1)} Memory`,
|
|
1330
|
-
"",
|
|
1331
|
-
`- summary: ${record.record.summary}`,
|
|
1332
|
-
`- kind: ${record.record.kind}`,
|
|
1333
|
-
`- scope: ${record.record.scope}`,
|
|
1334
|
-
`- confidence: ${record.record.confidence.toFixed(2)}`,
|
|
1335
|
-
"",
|
|
1336
|
-
record.record.content,
|
|
1337
|
-
].join("\n"),
|
|
1338
|
-
score: scoreMemoryText(query, `${record.record.summary}\n${record.record.content}`, scopeBoost) +
|
|
1339
|
-
this.memoryFreshnessBoost(record.record.lastConfirmedAt) +
|
|
1340
|
-
record.record.confidence,
|
|
1341
|
-
};
|
|
1342
|
-
})
|
|
1343
|
-
.sort((left, right) => right.score - left.score)
|
|
1344
|
-
.slice(0, this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? 8);
|
|
1345
|
-
if (ranked.length === 0) {
|
|
1346
|
-
return undefined;
|
|
1347
|
-
}
|
|
1348
|
-
return ranked.map((entry) => entry.content).join("\n\n");
|
|
957
|
+
}, {
|
|
958
|
+
sessionId: threadId,
|
|
959
|
+
agentId: binding.agent.id,
|
|
960
|
+
workspaceId: this.getWorkspaceId(binding),
|
|
961
|
+
});
|
|
1349
962
|
}
|
|
1350
963
|
async persistRuntimeMemoryCandidates(binding, threadId, runId, value) {
|
|
1351
964
|
if (!Array.isArray(value) || value.length === 0) {
|
|
@@ -1360,144 +973,13 @@ export class AgentHarnessRuntime {
|
|
|
1360
973
|
if (candidates.length === 0) {
|
|
1361
974
|
return;
|
|
1362
975
|
}
|
|
1363
|
-
await this.
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
runId,
|
|
976
|
+
await this.knowledgeModule.memorizeCandidates(candidates, {
|
|
977
|
+
sessionId: threadId,
|
|
978
|
+
requestId: runId,
|
|
1367
979
|
agentId: binding.agent.id,
|
|
980
|
+
workspaceId: this.getWorkspaceId(binding),
|
|
1368
981
|
recordedAt: new Date().toISOString(),
|
|
1369
|
-
|
|
1370
|
-
});
|
|
1371
|
-
}
|
|
1372
|
-
async persistStructuredMemoryCandidates(binding, input) {
|
|
1373
|
-
const workspaceId = this.getWorkspaceId(binding);
|
|
1374
|
-
const userId = input.userId ?? "default";
|
|
1375
|
-
const projectId = input.projectId ?? workspaceId;
|
|
1376
|
-
const existingRecords = await listMemoryRecordsForScopes(this.runtimeMemoryStore, ["thread", "agent", "workspace", "user", "project"]);
|
|
1377
|
-
const transformedCandidates = this.runtimeMemoryManager
|
|
1378
|
-
? await this.runtimeMemoryManager.transform({
|
|
1379
|
-
candidates: input.candidates,
|
|
1380
|
-
binding,
|
|
1381
|
-
threadId: input.threadId,
|
|
1382
|
-
runId: input.runId,
|
|
1383
|
-
recordedAt: input.recordedAt,
|
|
1384
|
-
existingRecords,
|
|
1385
|
-
})
|
|
1386
|
-
: input.candidates;
|
|
1387
|
-
const threadCandidates = transformedCandidates.filter((candidate) => (candidate.scope ?? "thread") === "thread");
|
|
1388
|
-
const workspaceCandidates = transformedCandidates.filter((candidate) => (candidate.scope ?? "thread") === "workspace");
|
|
1389
|
-
const agentCandidates = transformedCandidates.filter((candidate) => (candidate.scope ?? "thread") === "agent");
|
|
1390
|
-
const userCandidates = transformedCandidates.filter((candidate) => (candidate.scope ?? "thread") === "user");
|
|
1391
|
-
const projectCandidates = transformedCandidates.filter((candidate) => (candidate.scope ?? "thread") === "project");
|
|
1392
|
-
if (input.storeCandidateLog) {
|
|
1393
|
-
await this.runtimeMemoryStore.put(["memories", "candidates", input.threadId], `${input.runId}.json`, {
|
|
1394
|
-
runId: input.runId,
|
|
1395
|
-
threadId: input.threadId,
|
|
1396
|
-
storedAt: input.recordedAt,
|
|
1397
|
-
candidates: transformedCandidates,
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
const persisted = await persistStructuredMemoryRecords({
|
|
1401
|
-
store: this.runtimeMemoryStore,
|
|
1402
|
-
candidates: transformedCandidates,
|
|
1403
|
-
threadId: input.threadId,
|
|
1404
|
-
runId: input.runId,
|
|
1405
|
-
agentId: input.agentId,
|
|
1406
|
-
workspaceId,
|
|
1407
|
-
userId,
|
|
1408
|
-
projectId,
|
|
1409
|
-
recordedAt: input.recordedAt,
|
|
1410
|
-
});
|
|
1411
|
-
await this.rebuildRuntimeMemoryVectorIndex();
|
|
1412
|
-
const writes = [];
|
|
1413
|
-
if (threadCandidates.length > 0) {
|
|
1414
|
-
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("thread", binding, { threadId: input.threadId }), "tool-memory.md", threadCandidates, 12, "Thread Tool Memory"));
|
|
1415
|
-
writes.push(consolidateStructuredMemoryScope({
|
|
1416
|
-
store: this.runtimeMemoryStore,
|
|
1417
|
-
namespace: this.resolveMemoryNamespace("thread", binding, { threadId: input.threadId }),
|
|
1418
|
-
title: "Thread Structured Memory",
|
|
1419
|
-
scope: "thread",
|
|
1420
|
-
maxEntries: 12,
|
|
1421
|
-
config: this.runtimeMemoryMaintenanceConfig ?? undefined,
|
|
1422
|
-
}));
|
|
1423
|
-
}
|
|
1424
|
-
if (workspaceCandidates.length > 0) {
|
|
1425
|
-
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("workspace", binding), "tool-memory.md", workspaceCandidates, 20, "Workspace Tool Memory"));
|
|
1426
|
-
writes.push(consolidateStructuredMemoryScope({
|
|
1427
|
-
store: this.runtimeMemoryStore,
|
|
1428
|
-
namespace: this.resolveMemoryNamespace("workspace", binding),
|
|
1429
|
-
title: "Workspace Structured Memory",
|
|
1430
|
-
scope: "workspace",
|
|
1431
|
-
maxEntries: 20,
|
|
1432
|
-
config: this.runtimeMemoryMaintenanceConfig ?? undefined,
|
|
1433
|
-
}));
|
|
1434
|
-
}
|
|
1435
|
-
if (agentCandidates.length > 0) {
|
|
1436
|
-
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("agent", binding), "tool-memory.md", agentCandidates, 20, "Agent Tool Memory"));
|
|
1437
|
-
writes.push(consolidateStructuredMemoryScope({
|
|
1438
|
-
store: this.runtimeMemoryStore,
|
|
1439
|
-
namespace: this.resolveMemoryNamespace("agent", binding),
|
|
1440
|
-
title: "Agent Structured Memory",
|
|
1441
|
-
scope: "agent",
|
|
1442
|
-
maxEntries: 20,
|
|
1443
|
-
config: this.runtimeMemoryMaintenanceConfig ?? undefined,
|
|
1444
|
-
}));
|
|
1445
|
-
}
|
|
1446
|
-
if (userCandidates.length > 0) {
|
|
1447
|
-
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("user", binding, { userId }), "tool-memory.md", userCandidates, 20, "User Tool Memory"));
|
|
1448
|
-
writes.push(consolidateStructuredMemoryScope({
|
|
1449
|
-
store: this.runtimeMemoryStore,
|
|
1450
|
-
namespace: this.resolveMemoryNamespace("user", binding, { userId }),
|
|
1451
|
-
title: "User Structured Memory",
|
|
1452
|
-
scope: "user",
|
|
1453
|
-
maxEntries: 20,
|
|
1454
|
-
config: this.runtimeMemoryMaintenanceConfig ?? undefined,
|
|
1455
|
-
}));
|
|
1456
|
-
}
|
|
1457
|
-
if (projectCandidates.length > 0) {
|
|
1458
|
-
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("project", binding, { projectId }), "tool-memory.md", projectCandidates, 20, "Project Tool Memory"));
|
|
1459
|
-
writes.push(consolidateStructuredMemoryScope({
|
|
1460
|
-
store: this.runtimeMemoryStore,
|
|
1461
|
-
namespace: this.resolveMemoryNamespace("project", binding, { projectId }),
|
|
1462
|
-
title: "Project Structured Memory",
|
|
1463
|
-
scope: "project",
|
|
1464
|
-
maxEntries: 20,
|
|
1465
|
-
config: this.runtimeMemoryMaintenanceConfig ?? undefined,
|
|
1466
|
-
}));
|
|
1467
|
-
}
|
|
1468
|
-
await Promise.all(writes);
|
|
1469
|
-
return persisted;
|
|
1470
|
-
}
|
|
1471
|
-
async appendMemoryDigest(namespace, key, candidates, maxEntries, title) {
|
|
1472
|
-
const existing = await this.runtimeMemoryStore.get(namespace, key);
|
|
1473
|
-
const existingItems = Array.isArray(existing?.value?.items)
|
|
1474
|
-
? ((existing?.value).items ?? [])
|
|
1475
|
-
: [];
|
|
1476
|
-
const merged = [...existingItems];
|
|
1477
|
-
for (const candidate of candidates) {
|
|
1478
|
-
if (merged.some((entry) => entry.content === candidate.content && (entry.scope ?? "thread") === (candidate.scope ?? "thread"))) {
|
|
1479
|
-
continue;
|
|
1480
|
-
}
|
|
1481
|
-
merged.push(candidate);
|
|
1482
|
-
}
|
|
1483
|
-
const recent = merged.slice(-maxEntries);
|
|
1484
|
-
const taxonomyGroups = new Map();
|
|
1485
|
-
for (const candidate of recent) {
|
|
1486
|
-
const kind = normalizeLangMemMemoryKind(candidate.kind);
|
|
1487
|
-
const existing = taxonomyGroups.get(kind) ?? [];
|
|
1488
|
-
existing.push({ ...candidate, kind });
|
|
1489
|
-
taxonomyGroups.set(kind, existing);
|
|
1490
|
-
}
|
|
1491
|
-
await this.runtimeMemoryStore.put(namespace, key, {
|
|
1492
|
-
content: `${renderMemoryCandidatesMarkdown(title, recent)}\n`,
|
|
1493
|
-
items: recent,
|
|
1494
|
-
});
|
|
1495
|
-
await Promise.all(Array.from(taxonomyGroups.entries()).map(async ([kind, items]) => {
|
|
1496
|
-
await this.runtimeMemoryStore.put(namespace, `${kind}.md`, {
|
|
1497
|
-
content: `${renderMemoryCandidatesMarkdown(`${title} (${kind})`, items)}\n`,
|
|
1498
|
-
items,
|
|
1499
|
-
});
|
|
1500
|
-
}));
|
|
982
|
+
}, { storeCandidateLog: true });
|
|
1501
983
|
}
|
|
1502
984
|
async resolvePersistedRunPriority(threadId, runId) {
|
|
1503
985
|
const persisted = await this.persistence.getRunRequest(threadId, runId);
|