@open-multi-agent/core 1.4.0
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/LICENSE +21 -0
- package/README.md +373 -0
- package/dist/agent/agent.d.ts +153 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +559 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/loop-detector.d.ts +39 -0
- package/dist/agent/loop-detector.d.ts.map +1 -0
- package/dist/agent/loop-detector.js +122 -0
- package/dist/agent/loop-detector.js.map +1 -0
- package/dist/agent/pool.d.ts +158 -0
- package/dist/agent/pool.d.ts.map +1 -0
- package/dist/agent/pool.js +320 -0
- package/dist/agent/pool.js.map +1 -0
- package/dist/agent/runner.d.ts +242 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +943 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/structured-output.d.ts +33 -0
- package/dist/agent/structured-output.d.ts.map +1 -0
- package/dist/agent/structured-output.js +116 -0
- package/dist/agent/structured-output.js.map +1 -0
- package/dist/cli/oma.d.ts +30 -0
- package/dist/cli/oma.d.ts.map +1 -0
- package/dist/cli/oma.js +433 -0
- package/dist/cli/oma.js.map +1 -0
- package/dist/dashboard/layout-tasks.d.ts +23 -0
- package/dist/dashboard/layout-tasks.d.ts.map +1 -0
- package/dist/dashboard/layout-tasks.js +79 -0
- package/dist/dashboard/layout-tasks.js.map +1 -0
- package/dist/dashboard/render-team-run-dashboard.d.ts +11 -0
- package/dist/dashboard/render-team-run-dashboard.d.ts.map +1 -0
- package/dist/dashboard/render-team-run-dashboard.js +456 -0
- package/dist/dashboard/render-team-run-dashboard.js.map +1 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/adapter.d.ts +54 -0
- package/dist/llm/adapter.d.ts.map +1 -0
- package/dist/llm/adapter.js +101 -0
- package/dist/llm/adapter.js.map +1 -0
- package/dist/llm/anthropic.d.ts +57 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +432 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/azure-openai.d.ts +74 -0
- package/dist/llm/azure-openai.d.ts.map +1 -0
- package/dist/llm/azure-openai.js +267 -0
- package/dist/llm/azure-openai.js.map +1 -0
- package/dist/llm/bedrock.d.ts +41 -0
- package/dist/llm/bedrock.d.ts.map +1 -0
- package/dist/llm/bedrock.js +345 -0
- package/dist/llm/bedrock.js.map +1 -0
- package/dist/llm/copilot.d.ts +92 -0
- package/dist/llm/copilot.d.ts.map +1 -0
- package/dist/llm/copilot.js +433 -0
- package/dist/llm/copilot.js.map +1 -0
- package/dist/llm/deepseek.d.ts +21 -0
- package/dist/llm/deepseek.d.ts.map +1 -0
- package/dist/llm/deepseek.js +24 -0
- package/dist/llm/deepseek.js.map +1 -0
- package/dist/llm/gemini.d.ts +65 -0
- package/dist/llm/gemini.d.ts.map +1 -0
- package/dist/llm/gemini.js +427 -0
- package/dist/llm/gemini.js.map +1 -0
- package/dist/llm/grok.d.ts +21 -0
- package/dist/llm/grok.d.ts.map +1 -0
- package/dist/llm/grok.js +24 -0
- package/dist/llm/grok.js.map +1 -0
- package/dist/llm/minimax.d.ts +21 -0
- package/dist/llm/minimax.d.ts.map +1 -0
- package/dist/llm/minimax.js +24 -0
- package/dist/llm/minimax.js.map +1 -0
- package/dist/llm/openai-common.d.ts +65 -0
- package/dist/llm/openai-common.d.ts.map +1 -0
- package/dist/llm/openai-common.js +286 -0
- package/dist/llm/openai-common.js.map +1 -0
- package/dist/llm/openai.d.ts +63 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +256 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/qiniu.d.ts +21 -0
- package/dist/llm/qiniu.d.ts.map +1 -0
- package/dist/llm/qiniu.js +24 -0
- package/dist/llm/qiniu.js.map +1 -0
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +2 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory/shared.d.ts +162 -0
- package/dist/memory/shared.d.ts.map +1 -0
- package/dist/memory/shared.js +294 -0
- package/dist/memory/shared.js.map +1 -0
- package/dist/memory/store.d.ts +72 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +121 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +245 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +1400 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/scheduler.d.ts +112 -0
- package/dist/orchestrator/scheduler.d.ts.map +1 -0
- package/dist/orchestrator/scheduler.js +256 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/task/queue.d.ts +191 -0
- package/dist/task/queue.d.ts.map +1 -0
- package/dist/task/queue.js +408 -0
- package/dist/task/queue.js.map +1 -0
- package/dist/task/task.d.ts +90 -0
- package/dist/task/task.d.ts.map +1 -0
- package/dist/task/task.js +206 -0
- package/dist/task/task.js.map +1 -0
- package/dist/team/messaging.d.ts +106 -0
- package/dist/team/messaging.d.ts.map +1 -0
- package/dist/team/messaging.js +183 -0
- package/dist/team/messaging.js.map +1 -0
- package/dist/team/team.d.ts +141 -0
- package/dist/team/team.d.ts.map +1 -0
- package/dist/team/team.js +293 -0
- package/dist/team/team.js.map +1 -0
- package/dist/tool/built-in/bash.d.ts +12 -0
- package/dist/tool/built-in/bash.d.ts.map +1 -0
- package/dist/tool/built-in/bash.js +133 -0
- package/dist/tool/built-in/bash.js.map +1 -0
- package/dist/tool/built-in/delegate.d.ts +29 -0
- package/dist/tool/built-in/delegate.d.ts.map +1 -0
- package/dist/tool/built-in/delegate.js +92 -0
- package/dist/tool/built-in/delegate.js.map +1 -0
- package/dist/tool/built-in/file-edit.d.ts +14 -0
- package/dist/tool/built-in/file-edit.d.ts.map +1 -0
- package/dist/tool/built-in/file-edit.js +130 -0
- package/dist/tool/built-in/file-edit.js.map +1 -0
- package/dist/tool/built-in/file-read.d.ts +12 -0
- package/dist/tool/built-in/file-read.d.ts.map +1 -0
- package/dist/tool/built-in/file-read.js +82 -0
- package/dist/tool/built-in/file-read.js.map +1 -0
- package/dist/tool/built-in/file-write.d.ts +11 -0
- package/dist/tool/built-in/file-write.d.ts.map +1 -0
- package/dist/tool/built-in/file-write.js +70 -0
- package/dist/tool/built-in/file-write.js.map +1 -0
- package/dist/tool/built-in/fs-walk.d.ts +23 -0
- package/dist/tool/built-in/fs-walk.d.ts.map +1 -0
- package/dist/tool/built-in/fs-walk.js +78 -0
- package/dist/tool/built-in/fs-walk.js.map +1 -0
- package/dist/tool/built-in/glob.d.ts +12 -0
- package/dist/tool/built-in/glob.d.ts.map +1 -0
- package/dist/tool/built-in/glob.js +82 -0
- package/dist/tool/built-in/glob.js.map +1 -0
- package/dist/tool/built-in/grep.d.ts +15 -0
- package/dist/tool/built-in/grep.d.ts.map +1 -0
- package/dist/tool/built-in/grep.js +218 -0
- package/dist/tool/built-in/grep.js.map +1 -0
- package/dist/tool/built-in/index.d.ts +48 -0
- package/dist/tool/built-in/index.d.ts.map +1 -0
- package/dist/tool/built-in/index.js +56 -0
- package/dist/tool/built-in/index.js.map +1 -0
- package/dist/tool/executor.d.ts +100 -0
- package/dist/tool/executor.d.ts.map +1 -0
- package/dist/tool/executor.js +184 -0
- package/dist/tool/executor.js.map +1 -0
- package/dist/tool/framework.d.ts +167 -0
- package/dist/tool/framework.d.ts.map +1 -0
- package/dist/tool/framework.js +402 -0
- package/dist/tool/framework.js.map +1 -0
- package/dist/tool/mcp.d.ts +31 -0
- package/dist/tool/mcp.d.ts.map +1 -0
- package/dist/tool/mcp.js +175 -0
- package/dist/tool/mcp.js.map +1 -0
- package/dist/tool/text-tool-extractor.d.ts +32 -0
- package/dist/tool/text-tool-extractor.d.ts.map +1 -0
- package/dist/tool/text-tool-extractor.js +195 -0
- package/dist/tool/text-tool-extractor.js.map +1 -0
- package/dist/types.d.ts +916 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/keywords.d.ts +18 -0
- package/dist/utils/keywords.d.ts.map +1 -0
- package/dist/utils/keywords.js +32 -0
- package/dist/utils/keywords.js.map +1 -0
- package/dist/utils/semaphore.d.ts +49 -0
- package/dist/utils/semaphore.d.ts.map +1 -0
- package/dist/utils/semaphore.js +89 -0
- package/dist/utils/semaphore.js.map +1 -0
- package/dist/utils/tokens.d.ts +7 -0
- package/dist/utils/tokens.d.ts.map +1 -0
- package/dist/utils/tokens.js +30 -0
- package/dist/utils/tokens.js.map +1 -0
- package/dist/utils/trace.d.ts +12 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +30 -0
- package/dist/utils/trace.js.map +1 -0
- package/docs/DECISIONS.md +49 -0
- package/docs/cli.md +265 -0
- package/docs/context-management.md +24 -0
- package/docs/featured-partner.md +28 -0
- package/docs/observability.md +56 -0
- package/docs/providers.md +78 -0
- package/docs/shared-memory.md +27 -0
- package/docs/tool-configuration.md +152 -0
- package/package.json +96 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared memory layer for teams of cooperating agents.
|
|
3
|
+
*
|
|
4
|
+
* Each agent writes under its own namespace (`<agentName>/<key>`) so entries
|
|
5
|
+
* remain attributable, while any agent may read any entry. The
|
|
6
|
+
* {@link SharedMemory.getSummary} method produces a human-readable digest
|
|
7
|
+
* suitable for injecting into an agent's context window.
|
|
8
|
+
*/
|
|
9
|
+
import { InMemoryStore } from './store.js';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Runtime shape check
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
const STORE_METHODS = ['get', 'set', 'list', 'delete', 'clear'];
|
|
14
|
+
/**
|
|
15
|
+
* Returns true when `v` structurally implements {@link MemoryStore}.
|
|
16
|
+
*
|
|
17
|
+
* Used to defend against malformed `sharedMemoryStore` values reaching
|
|
18
|
+
* {@link SharedMemory} (e.g. a plain object deserialized from JSON that
|
|
19
|
+
* cannot actually satisfy the interface at runtime).
|
|
20
|
+
*/
|
|
21
|
+
function isMemoryStore(v) {
|
|
22
|
+
if (v === null || typeof v !== 'object')
|
|
23
|
+
return false;
|
|
24
|
+
const obj = v;
|
|
25
|
+
return STORE_METHODS.every((m) => typeof obj[m] === 'function');
|
|
26
|
+
}
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// SharedMemory
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/**
|
|
31
|
+
* Namespaced shared memory for a team of agents.
|
|
32
|
+
*
|
|
33
|
+
* Writes are namespaced as `<agentName>/<key>` so that entries from different
|
|
34
|
+
* agents never collide and are always attributable. Reads are namespace-aware
|
|
35
|
+
* but also accept fully-qualified keys, making cross-agent reads straightforward.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const mem = new SharedMemory()
|
|
40
|
+
*
|
|
41
|
+
* await mem.write('researcher', 'findings', 'TypeScript 5.5 ships const type params')
|
|
42
|
+
* await mem.write('coder', 'plan', 'Implement feature X using const type params')
|
|
43
|
+
*
|
|
44
|
+
* const entry = await mem.read('researcher/findings')
|
|
45
|
+
* const all = await mem.listByAgent('researcher')
|
|
46
|
+
* const summary = await mem.getSummary()
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export class SharedMemory {
|
|
50
|
+
store;
|
|
51
|
+
/**
|
|
52
|
+
* Monotonic turn counter used to evaluate per-entry `expiresAtTurn`.
|
|
53
|
+
* Advanced explicitly via {@link advanceTurn}; not bound to any specific
|
|
54
|
+
* unit (the orchestrator drives it once per completed task in `runTeam` /
|
|
55
|
+
* `runTasks`).
|
|
56
|
+
*/
|
|
57
|
+
turnCount = 0;
|
|
58
|
+
/**
|
|
59
|
+
* @param store - Optional custom {@link MemoryStore} backing this shared memory.
|
|
60
|
+
* Defaults to an in-process {@link InMemoryStore}. Custom stores
|
|
61
|
+
* receive namespaced keys (`<agentName>/<key>`) opaque to them.
|
|
62
|
+
* Stores that don't implement {@link MemoryStore.setWithExpiry}
|
|
63
|
+
* still work — `writeExpiring` falls back to plain `set` on
|
|
64
|
+
* them and the entry never expires.
|
|
65
|
+
*
|
|
66
|
+
* @throws {TypeError} when `store` is provided but does not structurally
|
|
67
|
+
* implement {@link MemoryStore} (fails fast on malformed
|
|
68
|
+
* values, e.g. plain objects from untrusted JSON config).
|
|
69
|
+
*/
|
|
70
|
+
constructor(store) {
|
|
71
|
+
if (store !== undefined && !isMemoryStore(store)) {
|
|
72
|
+
throw new TypeError('SharedMemory: `store` must implement the MemoryStore interface ' +
|
|
73
|
+
`(methods: ${STORE_METHODS.join(', ')}).`);
|
|
74
|
+
}
|
|
75
|
+
this.store = store ?? new InMemoryStore();
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Turn counter
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
/**
|
|
81
|
+
* Advance the turn counter by one. Entries previously written via
|
|
82
|
+
* {@link writeExpiring} with `ttlTurns: N` expire once the counter reaches
|
|
83
|
+
* `(write-time count) + N`.
|
|
84
|
+
*
|
|
85
|
+
* Called by the orchestrator after each completed task in `runTeam` and
|
|
86
|
+
* `runTasks`. Standalone `runAgent` does not advance the counter — there
|
|
87
|
+
* is no team turn boundary in single-agent runs.
|
|
88
|
+
*/
|
|
89
|
+
advanceTurn() {
|
|
90
|
+
this.turnCount++;
|
|
91
|
+
}
|
|
92
|
+
/** Current turn count. Useful for tests and observability. */
|
|
93
|
+
getTurnCount() {
|
|
94
|
+
return this.turnCount;
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Write
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* Write `value` under the namespaced key `<agentName>/<key>`.
|
|
101
|
+
*
|
|
102
|
+
* Metadata is merged with a `{ agent: agentName }` marker so consumers can
|
|
103
|
+
* identify provenance when iterating all entries.
|
|
104
|
+
*
|
|
105
|
+
* @param agentName - The writing agent's name (used as a namespace prefix).
|
|
106
|
+
* @param key - Logical key within the agent's namespace.
|
|
107
|
+
* @param value - String value to store (serialise objects before writing).
|
|
108
|
+
* @param metadata - Optional extra metadata stored alongside the entry.
|
|
109
|
+
*/
|
|
110
|
+
async write(agentName, key, value, metadata) {
|
|
111
|
+
const namespacedKey = SharedMemory.namespaceKey(agentName, key);
|
|
112
|
+
await this.store.set(namespacedKey, value, {
|
|
113
|
+
...metadata,
|
|
114
|
+
agent: agentName,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Like {@link write}, but tags the entry with a turn-count expiry so it is
|
|
119
|
+
* automatically dropped from reads once the {@link advanceTurn} counter has
|
|
120
|
+
* advanced `ttlTurns` steps.
|
|
121
|
+
*
|
|
122
|
+
* Backends that don't implement {@link MemoryStore.setWithExpiry} fall back
|
|
123
|
+
* to a plain write — the entry persists indefinitely and `ttlTurns` is
|
|
124
|
+
* effectively ignored. Custom store authors who need TTL must implement
|
|
125
|
+
* the optional method.
|
|
126
|
+
*
|
|
127
|
+
* @param ttlTurns - Number of turns the entry should remain readable for.
|
|
128
|
+
* Must be an integer ≥ 1; throws {@link RangeError}
|
|
129
|
+
* otherwise.
|
|
130
|
+
*
|
|
131
|
+
* @throws {RangeError} when `ttlTurns` is not an integer or is less than 1.
|
|
132
|
+
*
|
|
133
|
+
* @remarks
|
|
134
|
+
* In parallel batch execution (`runTasks` / `runTeam` running multiple
|
|
135
|
+
* tasks in one batch) the turn counter advances per *completed* task, not
|
|
136
|
+
* per *invoked* task. So if task A writes a TTL entry while task B is
|
|
137
|
+
* still running, B completing first will advance the counter and may
|
|
138
|
+
* expire A's entry sooner than wall-clock intuition suggests. For
|
|
139
|
+
* cross-task hand-off semantics that need stricter ordering, write the
|
|
140
|
+
* entry without TTL and delete explicitly.
|
|
141
|
+
*/
|
|
142
|
+
async writeExpiring(agentName, key, value, ttlTurns, metadata) {
|
|
143
|
+
if (!Number.isInteger(ttlTurns) || ttlTurns < 1) {
|
|
144
|
+
throw new RangeError(`SharedMemory.writeExpiring: ttlTurns must be an integer ≥ 1 (got ${ttlTurns}). ` +
|
|
145
|
+
'Use write() for entries that should never expire.');
|
|
146
|
+
}
|
|
147
|
+
const namespacedKey = SharedMemory.namespaceKey(agentName, key);
|
|
148
|
+
const fullMetadata = { ...metadata, agent: agentName };
|
|
149
|
+
if (typeof this.store.setWithExpiry === 'function') {
|
|
150
|
+
const expiresAtTurn = this.turnCount + ttlTurns;
|
|
151
|
+
await this.store.setWithExpiry(namespacedKey, value, expiresAtTurn, fullMetadata);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Custom store doesn't support TTL — degrade to plain set.
|
|
155
|
+
await this.store.set(namespacedKey, value, fullMetadata);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Read
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
/**
|
|
162
|
+
* Read an entry by its fully-qualified key (`<agentName>/<key>`).
|
|
163
|
+
*
|
|
164
|
+
* Returns `null` when the key is absent **or** when the entry has expired
|
|
165
|
+
* (per its `expiresAtTurn` against the current turn counter). Expired
|
|
166
|
+
* entries are filtered out but **not** deleted from the underlying store —
|
|
167
|
+
* deletion is left to the store impl (Redis has native TTL, Postgres a
|
|
168
|
+
* cron, etc.). Reading is therefore safe to call from concurrent processes
|
|
169
|
+
* without the risk of stomping on a fresh write to the same key.
|
|
170
|
+
*/
|
|
171
|
+
async read(key) {
|
|
172
|
+
const entry = await this.store.get(key);
|
|
173
|
+
if (entry === null)
|
|
174
|
+
return null;
|
|
175
|
+
if (this.isExpired(entry))
|
|
176
|
+
return null;
|
|
177
|
+
return entry;
|
|
178
|
+
}
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// List
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
/** Returns every non-expired entry in the shared store, regardless of agent. */
|
|
183
|
+
async listAll() {
|
|
184
|
+
return this.filterExpired(await this.store.list());
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Returns all non-expired entries written by `agentName` (i.e. those whose
|
|
188
|
+
* key starts with `<agentName>/`).
|
|
189
|
+
*/
|
|
190
|
+
async listByAgent(agentName) {
|
|
191
|
+
const prefix = SharedMemory.namespaceKey(agentName, '');
|
|
192
|
+
const all = await this.store.list();
|
|
193
|
+
const live = this.filterExpired(all);
|
|
194
|
+
return live.filter((entry) => entry.key.startsWith(prefix));
|
|
195
|
+
}
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// Summary
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
/**
|
|
200
|
+
* Produces a human-readable summary of all entries in the store.
|
|
201
|
+
*
|
|
202
|
+
* The output is structured as a markdown-style block, grouped by agent, and
|
|
203
|
+
* is designed to be prepended to an agent's system prompt or injected as a
|
|
204
|
+
* user turn so the agent has context about what its teammates know.
|
|
205
|
+
*
|
|
206
|
+
* Returns an empty string when the store is empty.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```
|
|
210
|
+
* ## Shared Team Memory
|
|
211
|
+
*
|
|
212
|
+
* ### researcher
|
|
213
|
+
* - findings: TypeScript 5.5 ships const type params
|
|
214
|
+
*
|
|
215
|
+
* ### coder
|
|
216
|
+
* - plan: Implement feature X using const type params
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
async getSummary(filter) {
|
|
220
|
+
let all = await this.store.list();
|
|
221
|
+
all = this.filterExpired(all);
|
|
222
|
+
if (filter?.taskIds && filter.taskIds.length > 0) {
|
|
223
|
+
const taskIds = new Set(filter.taskIds);
|
|
224
|
+
all = all.filter((entry) => {
|
|
225
|
+
const slashIdx = entry.key.indexOf('/');
|
|
226
|
+
const localKey = slashIdx === -1 ? entry.key : entry.key.slice(slashIdx + 1);
|
|
227
|
+
if (!localKey.startsWith('task:') || !localKey.endsWith(':result'))
|
|
228
|
+
return false;
|
|
229
|
+
const taskId = localKey.slice('task:'.length, localKey.length - ':result'.length);
|
|
230
|
+
return taskIds.has(taskId);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (all.length === 0)
|
|
234
|
+
return '';
|
|
235
|
+
// Group entries by agent name.
|
|
236
|
+
const byAgent = new Map();
|
|
237
|
+
for (const entry of all) {
|
|
238
|
+
const slashIdx = entry.key.indexOf('/');
|
|
239
|
+
const agent = slashIdx === -1 ? '_unknown' : entry.key.slice(0, slashIdx);
|
|
240
|
+
const localKey = slashIdx === -1 ? entry.key : entry.key.slice(slashIdx + 1);
|
|
241
|
+
let group = byAgent.get(agent);
|
|
242
|
+
if (!group) {
|
|
243
|
+
group = [];
|
|
244
|
+
byAgent.set(agent, group);
|
|
245
|
+
}
|
|
246
|
+
group.push({ localKey, value: entry.value });
|
|
247
|
+
}
|
|
248
|
+
const lines = ['## Shared Team Memory', ''];
|
|
249
|
+
for (const [agent, entries] of byAgent) {
|
|
250
|
+
lines.push(`### ${agent}`);
|
|
251
|
+
for (const { localKey, value } of entries) {
|
|
252
|
+
// Truncate long values so the summary stays readable in a context window.
|
|
253
|
+
const displayValue = value.length > 200 ? `${value.slice(0, 197)}…` : value;
|
|
254
|
+
lines.push(`- ${localKey}: ${displayValue}`);
|
|
255
|
+
}
|
|
256
|
+
lines.push('');
|
|
257
|
+
}
|
|
258
|
+
return lines.join('\n').trimEnd();
|
|
259
|
+
}
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// Store access
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
/**
|
|
264
|
+
* Returns the underlying {@link MemoryStore} so callers that only need the
|
|
265
|
+
* raw key-value interface can receive a properly typed reference without
|
|
266
|
+
* accessing private fields via bracket notation.
|
|
267
|
+
*/
|
|
268
|
+
getStore() {
|
|
269
|
+
return this.store;
|
|
270
|
+
}
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
// Private helpers
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
static namespaceKey(agentName, key) {
|
|
275
|
+
return `${agentName}/${key}`;
|
|
276
|
+
}
|
|
277
|
+
/** True when `entry.expiresAtTurn` is set and has been reached. */
|
|
278
|
+
isExpired(entry) {
|
|
279
|
+
return entry.expiresAtTurn !== undefined && this.turnCount >= entry.expiresAtTurn;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Drops expired entries from `entries`. **Does not delete from the
|
|
283
|
+
* underlying store** — that would race with concurrent writers in
|
|
284
|
+
* distributed backends (the entry being deleted may have been
|
|
285
|
+
* overwritten with a fresh value between our read and our delete).
|
|
286
|
+
* Stores that want active cleanup should implement their own TTL sweep
|
|
287
|
+
* (Redis: native EXPIRE; Postgres: a cron). Entries without
|
|
288
|
+
* `expiresAtTurn` are always kept.
|
|
289
|
+
*/
|
|
290
|
+
filterExpired(entries) {
|
|
291
|
+
return entries.filter((entry) => !this.isExpired(entry));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/memory/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview In-memory implementation of {@link MemoryStore}.
|
|
3
|
+
*
|
|
4
|
+
* All data lives in a plain `Map` and is never persisted to disk. This is the
|
|
5
|
+
* default store used by {@link SharedMemory} and is suitable for testing and
|
|
6
|
+
* single-process use-cases. Swap it for a Redis or SQLite-backed implementation
|
|
7
|
+
* in production by satisfying the same {@link MemoryStore} interface.
|
|
8
|
+
*/
|
|
9
|
+
import type { MemoryEntry, MemoryStore } from '../types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Synchronous-under-the-hood key/value store that exposes an `async` surface
|
|
12
|
+
* so implementations can be swapped for async-native backends without changing
|
|
13
|
+
* callers.
|
|
14
|
+
*
|
|
15
|
+
* All keys are treated as opaque strings. Values are always strings; structured
|
|
16
|
+
* data must be serialised by the caller (e.g. `JSON.stringify`).
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const store = new InMemoryStore()
|
|
21
|
+
* await store.set('config', JSON.stringify({ model: 'claude-opus-4-6' }))
|
|
22
|
+
* const entry = await store.get('config')
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class InMemoryStore implements MemoryStore {
|
|
26
|
+
private readonly data;
|
|
27
|
+
/** Returns the entry for `key`, or `null` if not present. */
|
|
28
|
+
get(key: string): Promise<MemoryEntry | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Upserts `key` with `value` and optional `metadata`.
|
|
31
|
+
*
|
|
32
|
+
* If the key already exists its `createdAt` is **preserved** so callers can
|
|
33
|
+
* detect when a value was first written.
|
|
34
|
+
*/
|
|
35
|
+
set(key: string, value: string, metadata?: Record<string, unknown>): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Like {@link set}, but also records a turn-count expiry. The entry is
|
|
38
|
+
* stored as-is — expiry filtering is the caller's responsibility (typically
|
|
39
|
+
* {@link SharedMemory}, which owns the turn counter).
|
|
40
|
+
*
|
|
41
|
+
* `createdAt` is preserved on update, matching {@link set}.
|
|
42
|
+
*/
|
|
43
|
+
setWithExpiry(key: string, value: string, expiresAtTurn: number, metadata?: Record<string, unknown>): Promise<void>;
|
|
44
|
+
/** Returns a snapshot of all entries in insertion order. */
|
|
45
|
+
list(): Promise<MemoryEntry[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Removes the entry for `key`.
|
|
48
|
+
* Deleting a non-existent key is a no-op.
|
|
49
|
+
*/
|
|
50
|
+
delete(key: string): Promise<void>;
|
|
51
|
+
/** Removes **all** entries from the store. */
|
|
52
|
+
clear(): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Returns entries whose `key` starts with `query` **or** whose `value`
|
|
55
|
+
* contains `query` (case-insensitive substring match).
|
|
56
|
+
*
|
|
57
|
+
* This is a simple linear scan; it is not suitable for very large stores
|
|
58
|
+
* without an index layer on top.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* // Find all entries related to "research"
|
|
63
|
+
* const hits = await store.search('research')
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
search(query: string): Promise<MemoryEntry[]>;
|
|
67
|
+
/** Returns the number of entries currently held in the store. */
|
|
68
|
+
get size(): number;
|
|
69
|
+
/** Returns `true` if `key` exists in the store. */
|
|
70
|
+
has(key: string): boolean;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/memory/store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAM3D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IAMtD,6DAA6D;IACvD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAInD;;;;;OAKG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;;OAMG;IACG,aAAa,CACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC;IAYhB,4DAA4D;IACtD,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAIpC;;;OAGG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,8CAA8C;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B;;;;;;;;;;;;OAYG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgBnD,iEAAiE;IACjE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,mDAAmD;IACnD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAG1B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview In-memory implementation of {@link MemoryStore}.
|
|
3
|
+
*
|
|
4
|
+
* All data lives in a plain `Map` and is never persisted to disk. This is the
|
|
5
|
+
* default store used by {@link SharedMemory} and is suitable for testing and
|
|
6
|
+
* single-process use-cases. Swap it for a Redis or SQLite-backed implementation
|
|
7
|
+
* in production by satisfying the same {@link MemoryStore} interface.
|
|
8
|
+
*/
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// InMemoryStore
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Synchronous-under-the-hood key/value store that exposes an `async` surface
|
|
14
|
+
* so implementations can be swapped for async-native backends without changing
|
|
15
|
+
* callers.
|
|
16
|
+
*
|
|
17
|
+
* All keys are treated as opaque strings. Values are always strings; structured
|
|
18
|
+
* data must be serialised by the caller (e.g. `JSON.stringify`).
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const store = new InMemoryStore()
|
|
23
|
+
* await store.set('config', JSON.stringify({ model: 'claude-opus-4-6' }))
|
|
24
|
+
* const entry = await store.get('config')
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export class InMemoryStore {
|
|
28
|
+
data = new Map();
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// MemoryStore interface
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/** Returns the entry for `key`, or `null` if not present. */
|
|
33
|
+
async get(key) {
|
|
34
|
+
return this.data.get(key) ?? null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Upserts `key` with `value` and optional `metadata`.
|
|
38
|
+
*
|
|
39
|
+
* If the key already exists its `createdAt` is **preserved** so callers can
|
|
40
|
+
* detect when a value was first written.
|
|
41
|
+
*/
|
|
42
|
+
async set(key, value, metadata) {
|
|
43
|
+
const existing = this.data.get(key);
|
|
44
|
+
const entry = {
|
|
45
|
+
key,
|
|
46
|
+
value,
|
|
47
|
+
metadata: metadata !== undefined ? { ...metadata } : undefined,
|
|
48
|
+
createdAt: existing?.createdAt ?? new Date(),
|
|
49
|
+
};
|
|
50
|
+
this.data.set(key, entry);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Like {@link set}, but also records a turn-count expiry. The entry is
|
|
54
|
+
* stored as-is — expiry filtering is the caller's responsibility (typically
|
|
55
|
+
* {@link SharedMemory}, which owns the turn counter).
|
|
56
|
+
*
|
|
57
|
+
* `createdAt` is preserved on update, matching {@link set}.
|
|
58
|
+
*/
|
|
59
|
+
async setWithExpiry(key, value, expiresAtTurn, metadata) {
|
|
60
|
+
const existing = this.data.get(key);
|
|
61
|
+
const entry = {
|
|
62
|
+
key,
|
|
63
|
+
value,
|
|
64
|
+
metadata: metadata !== undefined ? { ...metadata } : undefined,
|
|
65
|
+
createdAt: existing?.createdAt ?? new Date(),
|
|
66
|
+
expiresAtTurn,
|
|
67
|
+
};
|
|
68
|
+
this.data.set(key, entry);
|
|
69
|
+
}
|
|
70
|
+
/** Returns a snapshot of all entries in insertion order. */
|
|
71
|
+
async list() {
|
|
72
|
+
return Array.from(this.data.values());
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Removes the entry for `key`.
|
|
76
|
+
* Deleting a non-existent key is a no-op.
|
|
77
|
+
*/
|
|
78
|
+
async delete(key) {
|
|
79
|
+
this.data.delete(key);
|
|
80
|
+
}
|
|
81
|
+
/** Removes **all** entries from the store. */
|
|
82
|
+
async clear() {
|
|
83
|
+
this.data.clear();
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Extensions beyond the base MemoryStore interface
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Returns entries whose `key` starts with `query` **or** whose `value`
|
|
90
|
+
* contains `query` (case-insensitive substring match).
|
|
91
|
+
*
|
|
92
|
+
* This is a simple linear scan; it is not suitable for very large stores
|
|
93
|
+
* without an index layer on top.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* // Find all entries related to "research"
|
|
98
|
+
* const hits = await store.search('research')
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
async search(query) {
|
|
102
|
+
if (query.length === 0) {
|
|
103
|
+
return this.list();
|
|
104
|
+
}
|
|
105
|
+
const lower = query.toLowerCase();
|
|
106
|
+
return Array.from(this.data.values()).filter((entry) => entry.key.toLowerCase().includes(lower) ||
|
|
107
|
+
entry.value.toLowerCase().includes(lower));
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Convenience helpers (not part of MemoryStore)
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
/** Returns the number of entries currently held in the store. */
|
|
113
|
+
get size() {
|
|
114
|
+
return this.data.size;
|
|
115
|
+
}
|
|
116
|
+
/** Returns `true` if `key` exists in the store. */
|
|
117
|
+
has(key) {
|
|
118
|
+
return this.data.has(key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/memory/store.ts"],"names":[],"mappings":"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"}
|