@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.
@@ -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:
@@ -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.143";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.144";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.143";
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 getWorkspaceMemoryNamespace;
94
+ private resolveMemoryNamespace;
94
95
  private buildRuntimeMemoryContext;
95
96
  private persistRuntimeMemoryCandidates;
96
97
  private appendMemoryDigest;
@@ -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
- getWorkspaceMemoryNamespace(binding) {
373
+ resolveMemoryNamespace(scope, binding, options = {}) {
371
374
  const workspaceRoot = binding.harnessRuntime.workspaceRoot ?? this.workspace.workspaceRoot;
372
- return ["memories", "workspaces", path.basename(workspaceRoot) || "default"];
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(["memories", "threads", threadId], "durable-summary.md"),
377
- this.runtimeMemoryStore.get(["memories", "threads", threadId], "tool-memory.md"),
378
- this.runtimeMemoryStore.get(this.getWorkspaceMemoryNamespace(binding), "tool-memory.md"),
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 parts = docs
381
- .map((doc) => doc?.value)
382
- .map((value) => (typeof value?.content === "string" ? value.content.trim() : ""))
383
- .filter((value) => value.length > 0);
384
- if (parts.length === 0) {
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 parts.join("\n\n");
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(["memories", "threads", threadId], "tool-memory.md", threadCandidates, 12, "Thread Tool Memory"));
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.getWorkspaceMemoryNamespace(binding), "tool-memory.md", workspaceCandidates, 20, "Workspace Tool Memory"));
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(["memories", "agents", binding.agent.id], "tool-memory.md", agentCandidates, 20, "Agent Tool Memory"));
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.144",
3
+ "version": "0.0.145",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",