@botbotgo/agent-harness 0.0.144 → 0.0.145
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/dist/config/runtime/runtime-memory.yaml +5 -3
- package/dist/init-project.js +5 -3
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/harness/system/runtime-memory-policy.d.ts +18 -0
- package/dist/runtime/harness/system/runtime-memory-policy.js +73 -0
- package/dist/runtime/harness.d.ts +2 -1
- package/dist/runtime/harness.js +61 -17
- package/package.json +1 -1
|
@@ -28,9 +28,11 @@ spec:
|
|
|
28
28
|
|
|
29
29
|
# agent-harness feature: stable namespace roots used by the runtime memory policy layer.
|
|
30
30
|
namespaces:
|
|
31
|
-
users: memories/users
|
|
32
|
-
projects: memories/projects
|
|
33
|
-
threads: memories/threads
|
|
31
|
+
users: memories/users/{userId}
|
|
32
|
+
projects: memories/projects/{projectId}
|
|
33
|
+
threads: memories/threads/{threadId}
|
|
34
|
+
agents: memories/agents/{agentId}
|
|
35
|
+
workspaces: memories/workspaces/{workspaceId}
|
|
34
36
|
|
|
35
37
|
# agent-harness feature: retrieval defaults for selecting a bounded number of relevant memories per turn.
|
|
36
38
|
retrieval:
|
package/dist/init-project.js
CHANGED
|
@@ -139,9 +139,11 @@ metadata:
|
|
|
139
139
|
spec:
|
|
140
140
|
enabled: true
|
|
141
141
|
namespaces:
|
|
142
|
-
users: memories/users
|
|
143
|
-
projects: memories/projects
|
|
144
|
-
threads: memories/threads
|
|
142
|
+
users: memories/users/{userId}
|
|
143
|
+
projects: memories/projects/{projectId}
|
|
144
|
+
threads: memories/threads/{threadId}
|
|
145
|
+
agents: memories/agents/{agentId}
|
|
146
|
+
workspaces: memories/workspaces/{workspaceId}
|
|
145
147
|
retrieval:
|
|
146
148
|
defaultTopK: 5
|
|
147
149
|
maxPromptMemories: 8
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.144";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.144";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type ResolvedRuntimeMemoryPolicyConfig = {
|
|
2
|
+
enabled: true;
|
|
3
|
+
retrieval: {
|
|
4
|
+
defaultTopK: number;
|
|
5
|
+
maxPromptMemories: number;
|
|
6
|
+
};
|
|
7
|
+
namespaces: {
|
|
8
|
+
thread: string;
|
|
9
|
+
workspace: string;
|
|
10
|
+
agent: string;
|
|
11
|
+
user: string;
|
|
12
|
+
project: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare function normalizeLangMemMemoryKind(kind: string | undefined): "semantic" | "episodic" | "procedural";
|
|
16
|
+
export declare function readRuntimeMemoryPolicyConfig(runtimeMemory: Record<string, unknown> | undefined, workspaceRoot: string): ResolvedRuntimeMemoryPolicyConfig | undefined;
|
|
17
|
+
export declare function resolveMemoryNamespace(template: string, values: Record<string, string | undefined>): string[];
|
|
18
|
+
export declare function scoreMemoryText(query: string, content: string, scopeBoost?: number): number;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
function asRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
function asString(value) {
|
|
6
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
7
|
+
}
|
|
8
|
+
function asPositiveInteger(value) {
|
|
9
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
export function normalizeLangMemMemoryKind(kind) {
|
|
12
|
+
const normalized = kind?.trim().toLowerCase();
|
|
13
|
+
if (!normalized) {
|
|
14
|
+
return "semantic";
|
|
15
|
+
}
|
|
16
|
+
if (["episode", "episodic", "summary", "experience", "reflection"].includes(normalized)) {
|
|
17
|
+
return "episodic";
|
|
18
|
+
}
|
|
19
|
+
if (["procedural", "workflow", "procedure", "runbook", "playbook"].includes(normalized)) {
|
|
20
|
+
return "procedural";
|
|
21
|
+
}
|
|
22
|
+
return "semantic";
|
|
23
|
+
}
|
|
24
|
+
export function readRuntimeMemoryPolicyConfig(runtimeMemory, workspaceRoot) {
|
|
25
|
+
if (runtimeMemory?.enabled !== true) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const retrieval = asRecord(runtimeMemory.retrieval);
|
|
29
|
+
const namespaces = asRecord(runtimeMemory.namespaces);
|
|
30
|
+
const workspaceId = path.basename(workspaceRoot) || "default";
|
|
31
|
+
return {
|
|
32
|
+
enabled: true,
|
|
33
|
+
retrieval: {
|
|
34
|
+
defaultTopK: asPositiveInteger(retrieval?.defaultTopK) ?? 5,
|
|
35
|
+
maxPromptMemories: asPositiveInteger(retrieval?.maxPromptMemories) ?? 8,
|
|
36
|
+
},
|
|
37
|
+
namespaces: {
|
|
38
|
+
thread: asString(namespaces?.threads) ?? "memories/threads/{threadId}",
|
|
39
|
+
workspace: asString(namespaces?.workspaces) ?? `memories/workspaces/${workspaceId}`,
|
|
40
|
+
agent: asString(namespaces?.agents) ?? "memories/agents/{agentId}",
|
|
41
|
+
user: asString(namespaces?.users) ?? "memories/users/{userId}",
|
|
42
|
+
project: asString(namespaces?.projects) ?? "memories/projects/{projectId}",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function resolveMemoryNamespace(template, values) {
|
|
47
|
+
const rendered = template.replace(/\{([a-zA-Z0-9_]+)\}/g, (_match, key) => values[key] ?? key);
|
|
48
|
+
return rendered
|
|
49
|
+
.split("/")
|
|
50
|
+
.map((part) => part.trim())
|
|
51
|
+
.filter((part) => part.length > 0);
|
|
52
|
+
}
|
|
53
|
+
function tokenize(text) {
|
|
54
|
+
return text
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.split(/[^a-z0-9_]+/i)
|
|
57
|
+
.map((token) => token.trim())
|
|
58
|
+
.filter((token) => token.length > 2);
|
|
59
|
+
}
|
|
60
|
+
export function scoreMemoryText(query, content, scopeBoost = 0) {
|
|
61
|
+
const queryTokens = new Set(tokenize(query));
|
|
62
|
+
if (queryTokens.size === 0) {
|
|
63
|
+
return scopeBoost;
|
|
64
|
+
}
|
|
65
|
+
const contentTokens = tokenize(content);
|
|
66
|
+
let overlap = 0;
|
|
67
|
+
for (const token of contentTokens) {
|
|
68
|
+
if (queryTokens.has(token)) {
|
|
69
|
+
overlap += 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return overlap + scopeBoost;
|
|
73
|
+
}
|
|
@@ -19,6 +19,7 @@ export declare class AgentHarnessRuntime {
|
|
|
19
19
|
private readonly defaultRunRootValue;
|
|
20
20
|
private readonly defaultStore;
|
|
21
21
|
private readonly runtimeMemoryStore;
|
|
22
|
+
private readonly runtimeMemoryPolicy;
|
|
22
23
|
private readonly routingRules;
|
|
23
24
|
private readonly routingDefaultAgentId?;
|
|
24
25
|
private readonly threadMemorySync;
|
|
@@ -90,7 +91,7 @@ export declare class AgentHarnessRuntime {
|
|
|
90
91
|
private getRunCancellation;
|
|
91
92
|
private finalizeCancelledRun;
|
|
92
93
|
private invokeWithHistory;
|
|
93
|
-
private
|
|
94
|
+
private resolveMemoryNamespace;
|
|
94
95
|
private buildRuntimeMemoryContext;
|
|
95
96
|
private persistRuntimeMemoryCandidates;
|
|
96
97
|
private appendMemoryDigest;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -30,6 +30,7 @@ import { describeWorkspaceInventory, getAgentInventoryRecord, listAgentSkills as
|
|
|
30
30
|
import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
|
|
31
31
|
import { Mem0IngestionSync, readMem0RuntimeConfig } from "./harness/system/mem0-ingestion-sync.js";
|
|
32
32
|
import { renderMemoryCandidatesMarkdown } from "./harness/system/runtime-memory-candidates.js";
|
|
33
|
+
import { normalizeLangMemMemoryKind, readRuntimeMemoryPolicyConfig, resolveMemoryNamespace, scoreMemoryText, } from "./harness/system/runtime-memory-policy.js";
|
|
33
34
|
import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
|
|
34
35
|
import { initializeHarnessRuntime, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
|
|
35
36
|
import { streamHarnessRun } from "./harness/run/stream-run.js";
|
|
@@ -57,6 +58,7 @@ export class AgentHarnessRuntime {
|
|
|
57
58
|
defaultRunRootValue;
|
|
58
59
|
defaultStore;
|
|
59
60
|
runtimeMemoryStore;
|
|
61
|
+
runtimeMemoryPolicy;
|
|
60
62
|
routingRules;
|
|
61
63
|
routingDefaultAgentId;
|
|
62
64
|
threadMemorySync;
|
|
@@ -120,6 +122,7 @@ export class AgentHarnessRuntime {
|
|
|
120
122
|
? this.defaultRuntimeEntryBinding.harnessRuntime.runtimeMemory.store
|
|
121
123
|
: undefined;
|
|
122
124
|
this.runtimeMemoryStore = resolveStoreFromConfig(this.stores, runtimeMemoryStoreConfig, runRoot) ?? this.defaultStore;
|
|
125
|
+
this.runtimeMemoryPolicy = readRuntimeMemoryPolicyConfig(this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory, this.workspace.workspaceRoot) ?? null;
|
|
123
126
|
this.resolvedRuntimeAdapterOptions = resolveRuntimeAdapterOptions({
|
|
124
127
|
workspace,
|
|
125
128
|
runtimeAdapterOptions,
|
|
@@ -351,7 +354,7 @@ export class AgentHarnessRuntime {
|
|
|
351
354
|
}
|
|
352
355
|
async invokeWithHistory(binding, input, threadId, runId, resumePayload, priorHistory, options = {}) {
|
|
353
356
|
const history = priorHistory ?? await this.loadPriorHistory(threadId, runId);
|
|
354
|
-
const memoryContext = options.memoryContext ?? await this.buildRuntimeMemoryContext(binding, threadId);
|
|
357
|
+
const memoryContext = options.memoryContext ?? await this.buildRuntimeMemoryContext(binding, threadId, input);
|
|
355
358
|
const startedAt = Date.now();
|
|
356
359
|
try {
|
|
357
360
|
const result = await this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, history, {
|
|
@@ -367,24 +370,52 @@ export class AgentHarnessRuntime {
|
|
|
367
370
|
throw error;
|
|
368
371
|
}
|
|
369
372
|
}
|
|
370
|
-
|
|
373
|
+
resolveMemoryNamespace(scope, binding, options = {}) {
|
|
371
374
|
const workspaceRoot = binding.harnessRuntime.workspaceRoot ?? this.workspace.workspaceRoot;
|
|
372
|
-
|
|
375
|
+
const workspaceId = path.basename(workspaceRoot) || "default";
|
|
376
|
+
const template = this.runtimeMemoryPolicy?.namespaces[scope] ?? `memories/${scope}s/{${scope}Id}`;
|
|
377
|
+
return resolveMemoryNamespace(template, {
|
|
378
|
+
threadId: options.threadId,
|
|
379
|
+
agentId: binding.agent.id,
|
|
380
|
+
workspaceId,
|
|
381
|
+
userId: options.userId ?? "default",
|
|
382
|
+
projectId: options.projectId ?? workspaceId,
|
|
383
|
+
});
|
|
373
384
|
}
|
|
374
|
-
async buildRuntimeMemoryContext(binding, threadId) {
|
|
385
|
+
async buildRuntimeMemoryContext(binding, threadId, input) {
|
|
386
|
+
const threadNamespace = this.resolveMemoryNamespace("thread", binding, { threadId });
|
|
387
|
+
const agentNamespace = this.resolveMemoryNamespace("agent", binding);
|
|
388
|
+
const workspaceNamespace = this.resolveMemoryNamespace("workspace", binding);
|
|
375
389
|
const docs = await Promise.all([
|
|
376
|
-
this.runtimeMemoryStore.get(
|
|
377
|
-
this.runtimeMemoryStore.get(
|
|
378
|
-
this.runtimeMemoryStore.get(
|
|
390
|
+
this.runtimeMemoryStore.get(threadNamespace, "durable-summary.md"),
|
|
391
|
+
this.runtimeMemoryStore.get(threadNamespace, "tool-memory.md"),
|
|
392
|
+
this.runtimeMemoryStore.get(threadNamespace, "semantic.md"),
|
|
393
|
+
this.runtimeMemoryStore.get(threadNamespace, "episodic.md"),
|
|
394
|
+
this.runtimeMemoryStore.get(threadNamespace, "procedural.md"),
|
|
395
|
+
this.runtimeMemoryStore.get(agentNamespace, "procedural.md"),
|
|
396
|
+
this.runtimeMemoryStore.get(workspaceNamespace, "semantic.md"),
|
|
397
|
+
this.runtimeMemoryStore.get(workspaceNamespace, "procedural.md"),
|
|
379
398
|
]);
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
.map((
|
|
383
|
-
|
|
384
|
-
|
|
399
|
+
const query = typeof input === "string" ? input : JSON.stringify(input ?? "");
|
|
400
|
+
const ranked = docs
|
|
401
|
+
.map((doc, index) => {
|
|
402
|
+
const content = doc?.value?.content;
|
|
403
|
+
if (typeof content !== "string" || content.trim().length === 0) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
const scopeBoost = index < 5 ? 4 : index === 5 ? 2 : 1;
|
|
407
|
+
return {
|
|
408
|
+
content: content.trim(),
|
|
409
|
+
score: scoreMemoryText(query, content, scopeBoost),
|
|
410
|
+
};
|
|
411
|
+
})
|
|
412
|
+
.filter((value) => Boolean(value))
|
|
413
|
+
.sort((left, right) => right.score - left.score)
|
|
414
|
+
.slice(0, this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? 8);
|
|
415
|
+
if (ranked.length === 0) {
|
|
385
416
|
return undefined;
|
|
386
417
|
}
|
|
387
|
-
return
|
|
418
|
+
return ranked.map((entry) => entry.content).join("\n\n");
|
|
388
419
|
}
|
|
389
420
|
async persistRuntimeMemoryCandidates(binding, threadId, runId, value) {
|
|
390
421
|
if (!Array.isArray(value) || value.length === 0) {
|
|
@@ -407,13 +438,13 @@ export class AgentHarnessRuntime {
|
|
|
407
438
|
});
|
|
408
439
|
const writes = [];
|
|
409
440
|
if (threadCandidates.length > 0) {
|
|
410
|
-
writes.push(this.appendMemoryDigest(
|
|
441
|
+
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("thread", binding, { threadId }), "tool-memory.md", threadCandidates, 12, "Thread Tool Memory"));
|
|
411
442
|
}
|
|
412
443
|
if (workspaceCandidates.length > 0) {
|
|
413
|
-
writes.push(this.appendMemoryDigest(this.
|
|
444
|
+
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("workspace", binding), "tool-memory.md", workspaceCandidates, 20, "Workspace Tool Memory"));
|
|
414
445
|
}
|
|
415
446
|
if (agentCandidates.length > 0) {
|
|
416
|
-
writes.push(this.appendMemoryDigest(
|
|
447
|
+
writes.push(this.appendMemoryDigest(this.resolveMemoryNamespace("agent", binding), "tool-memory.md", agentCandidates, 20, "Agent Tool Memory"));
|
|
417
448
|
}
|
|
418
449
|
await Promise.all(writes);
|
|
419
450
|
}
|
|
@@ -430,10 +461,23 @@ export class AgentHarnessRuntime {
|
|
|
430
461
|
merged.push(candidate);
|
|
431
462
|
}
|
|
432
463
|
const recent = merged.slice(-maxEntries);
|
|
464
|
+
const taxonomyGroups = new Map();
|
|
465
|
+
for (const candidate of recent) {
|
|
466
|
+
const kind = normalizeLangMemMemoryKind(candidate.kind);
|
|
467
|
+
const existing = taxonomyGroups.get(kind) ?? [];
|
|
468
|
+
existing.push({ ...candidate, kind });
|
|
469
|
+
taxonomyGroups.set(kind, existing);
|
|
470
|
+
}
|
|
433
471
|
await this.runtimeMemoryStore.put(namespace, key, {
|
|
434
472
|
content: `${renderMemoryCandidatesMarkdown(title, recent)}\n`,
|
|
435
473
|
items: recent,
|
|
436
474
|
});
|
|
475
|
+
await Promise.all(Array.from(taxonomyGroups.entries()).map(async ([kind, items]) => {
|
|
476
|
+
await this.runtimeMemoryStore.put(namespace, `${kind}.md`, {
|
|
477
|
+
content: `${renderMemoryCandidatesMarkdown(`${title} (${kind})`, items)}\n`,
|
|
478
|
+
items,
|
|
479
|
+
});
|
|
480
|
+
}));
|
|
437
481
|
}
|
|
438
482
|
async resolvePersistedRunPriority(threadId, runId) {
|
|
439
483
|
const persisted = await this.persistence.getRunRequest(threadId, runId);
|
|
@@ -591,7 +635,7 @@ export class AgentHarnessRuntime {
|
|
|
591
635
|
input: options.input,
|
|
592
636
|
invocation: {
|
|
593
637
|
...invocation,
|
|
594
|
-
memoryContext: await this.buildRuntimeMemoryContext(binding, threadId),
|
|
638
|
+
memoryContext: await this.buildRuntimeMemoryContext(binding, threadId, options.input),
|
|
595
639
|
},
|
|
596
640
|
threadId,
|
|
597
641
|
runId,
|