@botbotgo/agent-harness 0.0.151 → 0.0.152

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.
@@ -66,6 +66,10 @@ spec:
66
66
  writeOnApprovalResolution: true
67
67
  writeOnRunCompletion: true
68
68
 
69
+ # agent-harness feature: optional thread snapshot projection for operational state and pending approvals.
70
+ threadMemorySync:
71
+ enabled: true
72
+
69
73
  # agent-harness feature: optional Mem0 OSS ingestion engine for automatic long-term knowledge extraction.
70
74
  mem0:
71
75
  enabled: false
@@ -162,6 +162,8 @@ spec:
162
162
  backgroundConsolidation: true
163
163
  writeOnApprovalResolution: true
164
164
  writeOnRunCompletion: true
165
+ threadMemorySync:
166
+ enabled: true
165
167
  mem0:
166
168
  enabled: false
167
169
  apiKeyEnv: MEM0_API_KEY
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.150";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.151";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.150";
1
+ export const AGENT_HARNESS_VERSION = "0.0.151";
@@ -8,17 +8,25 @@ export type ResolvedRuntimeMemorySyncConfig = {
8
8
  backgroundConsolidation: boolean;
9
9
  maxMessagesPerRun: number;
10
10
  };
11
+ type RuntimeMemorySyncOptions = {
12
+ resolveThreadNamespace?: (threadId: string) => string[];
13
+ resolveRunNamespace?: (threadId: string) => string[];
14
+ };
11
15
  export declare function readRuntimeMemorySyncConfig(runtimeMemory: Record<string, unknown> | undefined): ResolvedRuntimeMemorySyncConfig | undefined;
12
16
  export declare class RuntimeMemorySync implements HarnessEventProjection {
13
17
  private readonly persistence;
14
18
  readonly store: StoreLike;
15
19
  private readonly config;
20
+ private readonly options;
16
21
  private readonly pending;
17
22
  private syncChain;
18
23
  readonly name = "runtime-memory-sync";
19
- constructor(persistence: RuntimePersistence, store: StoreLike, config: ResolvedRuntimeMemorySyncConfig);
24
+ constructor(persistence: RuntimePersistence, store: StoreLike, config: ResolvedRuntimeMemorySyncConfig, options?: RuntimeMemorySyncOptions);
20
25
  shouldHandle(event: HarnessEvent): boolean;
21
26
  handleEvent(event: HarnessEvent): Promise<void>;
22
27
  private syncRun;
28
+ private resolveThreadNamespace;
29
+ private resolveRunNamespace;
23
30
  close(): Promise<void>;
24
31
  }
32
+ export {};
@@ -100,13 +100,15 @@ export class RuntimeMemorySync {
100
100
  persistence;
101
101
  store;
102
102
  config;
103
+ options;
103
104
  pending = new Set();
104
105
  syncChain = Promise.resolve();
105
106
  name = "runtime-memory-sync";
106
- constructor(persistence, store, config) {
107
+ constructor(persistence, store, config, options = {}) {
107
108
  this.persistence = persistence;
108
109
  this.store = store;
109
110
  this.config = config;
111
+ this.options = options;
110
112
  }
111
113
  shouldHandle(event) {
112
114
  if (!RUNTIME_MEMORY_EVENT_TYPES.has(event.eventType)) {
@@ -161,8 +163,8 @@ export class RuntimeMemorySync {
161
163
  approvals,
162
164
  });
163
165
  await Promise.all([
164
- this.store.put(["memories", "runs", threadId], `${runId}.summary.md`, { content: `${summaryMarkdown}\n` }),
165
- this.store.put(["memories", "runs", threadId], `${runId}.record.json`, {
166
+ this.store.put(this.resolveRunNamespace(threadId), `${runId}.summary.md`, { content: `${summaryMarkdown}\n` }),
167
+ this.store.put(this.resolveRunNamespace(threadId), `${runId}.record.json`, {
166
168
  kind: "summary",
167
169
  scope: "thread",
168
170
  threadId,
@@ -185,7 +187,13 @@ export class RuntimeMemorySync {
185
187
  capturedAt,
186
188
  messages,
187
189
  });
188
- await this.store.put(["memories", "threads", threadId], "durable-summary.md", { content: `${digestMarkdown}\n` });
190
+ await this.store.put(this.resolveThreadNamespace(threadId), "durable-summary.md", { content: `${digestMarkdown}\n` });
191
+ }
192
+ resolveThreadNamespace(threadId) {
193
+ return this.options.resolveThreadNamespace ? this.options.resolveThreadNamespace(threadId) : ["memories", "threads", threadId];
194
+ }
195
+ resolveRunNamespace(threadId) {
196
+ return this.options.resolveRunNamespace ? this.options.resolveRunNamespace(threadId) : ["memories", "runs", threadId];
189
197
  }
190
198
  async close() {
191
199
  await Promise.allSettled(Array.from(this.pending));
@@ -1,14 +1,18 @@
1
1
  import type { HarnessEvent, HarnessEventProjection } from "../../../contracts/types.js";
2
2
  import type { RuntimePersistence } from "../../../persistence/types.js";
3
+ type ThreadMemorySyncOptions = {
4
+ resolveThreadNamespace?: (threadId: string) => string[];
5
+ };
3
6
  export declare class ThreadMemorySync implements HarnessEventProjection {
4
7
  private readonly persistence;
5
8
  private readonly store?;
9
+ private readonly options;
6
10
  private readonly pending;
7
11
  private syncChain;
8
12
  readonly name = "thread-memory-sync";
9
13
  constructor(persistence: RuntimePersistence, store?: {
10
14
  put: (namespace: string[], key: string, value: Record<string, any>) => Promise<void>;
11
- } | undefined);
15
+ } | undefined, options?: ThreadMemorySyncOptions);
12
16
  shouldHandle(event: HarnessEvent): boolean;
13
17
  handleEvent(event: HarnessEvent): Promise<void>;
14
18
  private syncThread;
@@ -6,11 +6,11 @@ function excerpt(message) {
6
6
  const normalized = extractMessageText(message.content).replace(/\s+/g, " ").trim();
7
7
  return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
8
8
  }
9
- function renderStatusMarkdown(thread, messages) {
9
+ function renderThreadSnapshotMarkdown(thread, messages, approvals) {
10
10
  const userMessages = messages.filter((message) => message.role === "user");
11
11
  const assistantMessages = messages.filter((message) => message.role === "assistant");
12
12
  return [
13
- "# Thread Status",
13
+ "# Thread Snapshot",
14
14
  "",
15
15
  `- thread_id: ${thread.threadId}`,
16
16
  `- latest_run_id: ${thread.latestRunId}`,
@@ -24,15 +24,17 @@ function renderStatusMarkdown(thread, messages) {
24
24
  "## Recent Assistant Message",
25
25
  excerpt(assistantMessages.at(-1)),
26
26
  "",
27
+ ...formatOpenApprovalsSection(approvals),
27
28
  ].join("\n");
28
29
  }
29
- function renderOpenApprovalsMarkdown(approvals) {
30
+ function formatOpenApprovalsSection(approvals) {
31
+ const lines = ["## Open Approvals", ""];
30
32
  if (approvals.length === 0) {
31
- return ["# Open Approvals", "", "(none)", ""].join("\n");
33
+ lines.push("(none)", "");
34
+ return lines;
32
35
  }
33
- const lines = ["# Open Approvals", ""];
34
36
  for (const approval of approvals) {
35
- lines.push(`## ${approval.approvalId}`);
37
+ lines.push(`### ${approval.approvalId}`);
36
38
  lines.push(`- pending_action_id: ${approval.pendingActionId}`);
37
39
  lines.push(`- tool: ${approval.toolName}`);
38
40
  lines.push(`- run_id: ${approval.runId}`);
@@ -40,7 +42,10 @@ function renderOpenApprovalsMarkdown(approvals) {
40
42
  lines.push(`- allowed: ${approval.allowedDecisions.join(", ")}`);
41
43
  lines.push("");
42
44
  }
43
- return lines.join("\n");
45
+ return lines;
46
+ }
47
+ function resolveThreadNamespace(threadId, resolver) {
48
+ return resolver ? resolver(threadId) : ["memories", "threads", threadId];
44
49
  }
45
50
  const THREAD_MEMORY_EVENT_TYPES = new Set([
46
51
  "run.state.changed",
@@ -52,12 +57,14 @@ const THREAD_MEMORY_EVENT_TYPES = new Set([
52
57
  export class ThreadMemorySync {
53
58
  persistence;
54
59
  store;
60
+ options;
55
61
  pending = new Set();
56
62
  syncChain = Promise.resolve();
57
63
  name = "thread-memory-sync";
58
- constructor(persistence, store) {
64
+ constructor(persistence, store, options = {}) {
59
65
  this.persistence = persistence;
60
66
  this.store = store;
67
+ this.options = options;
61
68
  }
62
69
  shouldHandle(event) {
63
70
  return THREAD_MEMORY_EVENT_TYPES.has(event.eventType);
@@ -93,10 +100,9 @@ export class ThreadMemorySync {
93
100
  if (!this.store) {
94
101
  return;
95
102
  }
96
- await Promise.all([
97
- this.store.put(["memories", "threads", threadId], "status.md", { content: `${renderStatusMarkdown(thread, messages)}\n` }),
98
- this.store.put(["memories", "threads", threadId], "open-approvals.md", { content: `${renderOpenApprovalsMarkdown(pendingApprovals)}\n` }),
99
- ]);
103
+ await this.store.put(resolveThreadNamespace(threadId, this.options.resolveThreadNamespace), "snapshot.md", {
104
+ content: `${renderThreadSnapshotMarkdown(thread, messages, pendingApprovals)}\n`,
105
+ });
100
106
  }
101
107
  async close() {
102
108
  await Promise.allSettled(Array.from(this.pending));
@@ -144,7 +144,15 @@ export class AgentHarnessRuntime {
144
144
  this.routingRules = getRoutingRules(workspace.refs);
145
145
  this.routingDefaultAgentId = getRoutingDefaultAgentId(workspace.refs);
146
146
  if (isThreadMemorySyncEnabled(workspace)) {
147
- this.threadMemorySync = new ThreadMemorySync(this.persistence, this.runtimeMemoryStore);
147
+ this.threadMemorySync = new ThreadMemorySync(this.persistence, this.runtimeMemoryStore, {
148
+ resolveThreadNamespace: (threadId) => {
149
+ const binding = this.defaultRuntimeEntryBinding;
150
+ if (!binding) {
151
+ return ["memories", "threads", threadId];
152
+ }
153
+ return this.resolveMemoryNamespace("thread", binding, { threadId });
154
+ },
155
+ });
148
156
  this.unregisterThreadMemorySync = this.eventBus.registerProjection(this.threadMemorySync);
149
157
  }
150
158
  else {
@@ -153,7 +161,15 @@ export class AgentHarnessRuntime {
153
161
  }
154
162
  const runtimeMemorySyncConfig = readRuntimeMemorySyncConfig(this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory);
155
163
  if (runtimeMemorySyncConfig) {
156
- this.runtimeMemorySync = new RuntimeMemorySync(this.persistence, this.runtimeMemoryStore, runtimeMemorySyncConfig);
164
+ this.runtimeMemorySync = new RuntimeMemorySync(this.persistence, this.runtimeMemoryStore, runtimeMemorySyncConfig, {
165
+ resolveThreadNamespace: (threadId) => {
166
+ const binding = this.defaultRuntimeEntryBinding;
167
+ if (!binding) {
168
+ return ["memories", "threads", threadId];
169
+ }
170
+ return this.resolveMemoryNamespace("thread", binding, { threadId });
171
+ },
172
+ });
157
173
  this.unregisterRuntimeMemorySync = this.eventBus.registerProjection(this.runtimeMemorySync);
158
174
  }
159
175
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.151",
3
+ "version": "0.0.152",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",