claude-flow 3.62.0 → 3.63.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/README.md +1 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/README.md +1 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +3 -3
- package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-agent-tools.d.ts +1 -20
- package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-agent-tools.js +344 -108
- package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-learning-tool.d.ts +4 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-learning-tool.js +46 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +6 -3
- package/v3/@claude-flow/cli/dist/src/missions/schemas.d.ts +12 -12
- package/v3/@claude-flow/cli/dist/src/services/managed-learning-loop.d.ts +79 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-learning-loop.js +76 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-learning-store.d.ts +187 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-learning-store.js +238 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-learning.d.ts +108 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-learning.js +197 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-memory-client.d.ts +72 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-memory-client.js +236 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-memory-worker.d.ts +56 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-memory-worker.js +161 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-run-collector.d.ts +93 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-run-collector.js +181 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-run-store.d.ts +52 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-run-store.js +119 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-workflows.d.ts +101 -0
- package/v3/@claude-flow/cli/dist/src/services/managed-workflows.js +183 -0
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/** Native memory runs behind a bounded child boundary, never in the MCP process. */
|
|
2
|
+
import { fork } from 'node:child_process';
|
|
3
|
+
import { existsSync, realpathSync, statSync } from 'node:fs';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
export const MANAGED_MEMORY_NAMESPACE = 'managed-workflow-evidence';
|
|
8
|
+
export const MANAGED_MEMORY_LIMITS = { workers: 1, requestBytes: 80_000, resultBytes: 200_000, rows: 20, rowBytes: 8192, timeoutMs: 15_000, idleMs: 5000 };
|
|
9
|
+
const HASH = /^[a-f0-9]{64}$/;
|
|
10
|
+
const TYPES = ['code-review', 'migration', 'research', 'test-generation', 'implementation', 'other'];
|
|
11
|
+
function requestOf(value) {
|
|
12
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
13
|
+
throw new Error('Invalid managed memory request');
|
|
14
|
+
const r = value;
|
|
15
|
+
const keys = r.operation === 'index' ? ['operation', 'id', 'value', 'taskType'] : ['operation', 'taskType', 'allowedIds'];
|
|
16
|
+
if (!['index', 'recall'].includes(String(r.operation)) || Object.keys(r).length !== keys.length || Object.keys(r).some(k => !keys.includes(k)) || !TYPES.includes(String(r.taskType)))
|
|
17
|
+
throw new Error('Invalid managed memory request');
|
|
18
|
+
if (r.operation === 'index' && (typeof r.id !== 'string' || !HASH.test(r.id) || typeof r.value !== 'string' || Buffer.byteLength(r.value) > MANAGED_MEMORY_LIMITS.rowBytes))
|
|
19
|
+
throw new Error('Invalid managed index request');
|
|
20
|
+
if (r.operation === 'recall' && (!Array.isArray(r.allowedIds) || r.allowedIds.length > 500 || r.allowedIds.some(id => typeof id !== 'string' || !HASH.test(id))))
|
|
21
|
+
throw new Error('Invalid managed recall request');
|
|
22
|
+
if (Buffer.byteLength(JSON.stringify(r)) > MANAGED_MEMORY_LIMITS.requestBytes)
|
|
23
|
+
throw new Error('Managed memory request limit exceeded');
|
|
24
|
+
return JSON.parse(JSON.stringify(r));
|
|
25
|
+
}
|
|
26
|
+
function resultOf(value, operation, requestId) {
|
|
27
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Buffer.byteLength(JSON.stringify(value)) > MANAGED_MEMORY_LIMITS.resultBytes)
|
|
28
|
+
throw new Error('Invalid managed memory worker response');
|
|
29
|
+
const r = value;
|
|
30
|
+
if (r.v !== 1 || r.ok !== true || r.operation !== operation || r.requestId !== requestId)
|
|
31
|
+
throw new Error('Managed native memory worker did not complete');
|
|
32
|
+
if (operation === 'index') {
|
|
33
|
+
if (Object.keys(r).some(k => !['v', 'ok', 'requestId', 'operation', 'durable', 'hasEmbedding'].includes(k)) || r.durable !== true || typeof r.hasEmbedding !== 'boolean')
|
|
34
|
+
throw new Error('Unconfirmed managed memory persistence');
|
|
35
|
+
return { operation, durable: true, hasEmbedding: r.hasEmbedding };
|
|
36
|
+
}
|
|
37
|
+
if (Object.keys(r).some(k => !['v', 'ok', 'requestId', 'operation', 'available', 'rows'].includes(k)) || typeof r.available !== 'boolean' || !Array.isArray(r.rows) || r.rows.length > MANAGED_MEMORY_LIMITS.rows)
|
|
38
|
+
throw new Error('Invalid managed recall response');
|
|
39
|
+
for (const raw of r.rows) {
|
|
40
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
41
|
+
throw new Error('Invalid managed recall row');
|
|
42
|
+
const row = raw;
|
|
43
|
+
if (Object.keys(row).length !== 4 || Object.keys(row).some(k => !['key', 'namespace', 'score', 'content'].includes(k)) || typeof row.key !== 'string' || !HASH.test(row.key) || row.namespace !== MANAGED_MEMORY_NAMESPACE || typeof row.score !== 'number' || !Number.isFinite(row.score) || row.score < -1 || row.score > 1 || typeof row.content !== 'string' || Buffer.byteLength(row.content) > MANAGED_MEMORY_LIMITS.rowBytes)
|
|
44
|
+
throw new Error('Invalid managed recall row');
|
|
45
|
+
}
|
|
46
|
+
return { operation, available: r.available, rows: r.rows };
|
|
47
|
+
}
|
|
48
|
+
function sourceLoader() {
|
|
49
|
+
try {
|
|
50
|
+
return createRequire(import.meta.url).resolve('tsx');
|
|
51
|
+
}
|
|
52
|
+
catch { /* npx tsx can live outside project node_modules */ }
|
|
53
|
+
for (let i = 0; i < process.execArgv.length; i++) {
|
|
54
|
+
const arg = process.execArgv[i];
|
|
55
|
+
const path = arg === '--import' ? process.execArgv[i + 1] : arg.startsWith('--import=') ? arg.slice(9) : undefined;
|
|
56
|
+
if (!path)
|
|
57
|
+
continue;
|
|
58
|
+
const file = path.startsWith('file:') ? fileURLToPath(path) : path;
|
|
59
|
+
if (/[/\\]node_modules[/\\]tsx[/\\]dist[/\\](?:loader\.mjs|esm[/\\]index\.mjs)$/.test(file) && existsSync(file))
|
|
60
|
+
return file;
|
|
61
|
+
}
|
|
62
|
+
throw new Error('Source managed memory worker requires tsx');
|
|
63
|
+
}
|
|
64
|
+
function launch(root) {
|
|
65
|
+
const js = new URL('./managed-memory-worker.js', import.meta.url), ts = new URL('./managed-memory-worker.ts', import.meta.url);
|
|
66
|
+
const compiled = existsSync(fileURLToPath(js));
|
|
67
|
+
const entry = compiled ? js : ts;
|
|
68
|
+
if (!existsSync(fileURLToPath(entry)))
|
|
69
|
+
throw new Error('Managed memory worker is not installed');
|
|
70
|
+
// Production uses emitted JS. Source development uses the project's declared
|
|
71
|
+
// tsx dependency so internal .js imports resolve to TypeScript sources.
|
|
72
|
+
const args = compiled ? [] : ['--import', sourceLoader()];
|
|
73
|
+
const env = {};
|
|
74
|
+
for (const key of ['PATH', 'HOME', 'USERPROFILE', 'SystemRoot', 'SYSTEMROOT', 'WINDIR', 'TMPDIR', 'TMP', 'TEMP', 'LANG', 'LC_ALL', 'CLAUDE_FLOW_DISABLE_BRIDGE', 'CLAUDE_FLOW_ENABLE_NATIVE_BRIDGE_ON_WINDOWS'])
|
|
75
|
+
if (process.env[key] !== undefined)
|
|
76
|
+
env[key] = process.env[key];
|
|
77
|
+
// No provider credentials or startup hooks are inherited. Existing local
|
|
78
|
+
// models may be used; indexing never initiates a model download.
|
|
79
|
+
env.CLAUDE_FLOW_CWD = root;
|
|
80
|
+
env.HF_HUB_OFFLINE = '1';
|
|
81
|
+
env.TRANSFORMERS_OFFLINE = '1';
|
|
82
|
+
return fork(entry, [], { cwd: root, env, execArgv: args, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], serialization: 'json' });
|
|
83
|
+
}
|
|
84
|
+
/** The optional fork callback is an in-process host test seam, not a serialized tool argument. */
|
|
85
|
+
export class ManagedMemoryClient {
|
|
86
|
+
slots = new Map();
|
|
87
|
+
timeoutMs;
|
|
88
|
+
idleMs;
|
|
89
|
+
forkWorker;
|
|
90
|
+
constructor(options = {}) {
|
|
91
|
+
this.timeoutMs = options.timeoutMs ?? MANAGED_MEMORY_LIMITS.timeoutMs;
|
|
92
|
+
this.idleMs = options.idleMs ?? MANAGED_MEMORY_LIMITS.idleMs;
|
|
93
|
+
this.forkWorker = options.forkWorker ?? launch;
|
|
94
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 20 || this.timeoutMs > 60_000 || !Number.isFinite(this.idleMs) || this.idleMs < 20 || this.idleMs > 30_000)
|
|
95
|
+
throw new Error('Invalid managed memory worker limits');
|
|
96
|
+
}
|
|
97
|
+
run(root, raw, authorize = () => true) {
|
|
98
|
+
let input, cwd;
|
|
99
|
+
try {
|
|
100
|
+
input = requestOf(raw);
|
|
101
|
+
cwd = realpathSync(root);
|
|
102
|
+
if (!statSync(cwd).isDirectory())
|
|
103
|
+
throw new Error('Invalid learning project root');
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return Promise.reject(new Error('Invalid managed memory operation or project root'));
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
if (!authorize())
|
|
110
|
+
return Promise.reject(new Error('Managed memory policy denied'));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return Promise.reject(new Error('Managed memory policy denied'));
|
|
114
|
+
}
|
|
115
|
+
let slot = this.slots.get(cwd);
|
|
116
|
+
if (slot?.busy)
|
|
117
|
+
return Promise.reject(new Error('Managed memory worker is busy; retry later'));
|
|
118
|
+
if (!slot) {
|
|
119
|
+
if (this.slots.size >= MANAGED_MEMORY_LIMITS.workers) {
|
|
120
|
+
const idle = [...this.slots.values()].filter(s => !s.busy).sort((a, b) => a.lastUsed - b.lastUsed)[0];
|
|
121
|
+
if (!idle)
|
|
122
|
+
return Promise.reject(new Error('Managed memory worker limit reached; retry later'));
|
|
123
|
+
this.remove(idle);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
slot = { child: this.forkWorker(cwd), root: cwd, busy: false, lastUsed: Date.now() };
|
|
127
|
+
this.slots.set(cwd, slot);
|
|
128
|
+
const owned = slot;
|
|
129
|
+
slot.child.once('error', () => { owned.fail?.(); this.remove(owned); });
|
|
130
|
+
slot.child.once('exit', () => { owned.fail?.(); this.remove(owned); });
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return Promise.reject(new Error('Managed native memory worker unavailable'));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const owned = slot;
|
|
137
|
+
const requestId = randomUUID();
|
|
138
|
+
if (owned.timer)
|
|
139
|
+
clearTimeout(owned.timer);
|
|
140
|
+
owned.busy = true;
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
let settled = false, policyDenied = false, ready = false, writeGranted = false;
|
|
143
|
+
const complete = (result) => {
|
|
144
|
+
if (settled)
|
|
145
|
+
return;
|
|
146
|
+
settled = true;
|
|
147
|
+
clearTimeout(timeout);
|
|
148
|
+
owned.child.off('message', onMessage);
|
|
149
|
+
owned.fail = undefined;
|
|
150
|
+
owned.busy = false;
|
|
151
|
+
owned.lastUsed = Date.now();
|
|
152
|
+
if (result) {
|
|
153
|
+
owned.timer = setTimeout(() => this.remove(owned), this.idleMs);
|
|
154
|
+
owned.timer.unref();
|
|
155
|
+
owned.child.unref();
|
|
156
|
+
owned.child.channel?.unref();
|
|
157
|
+
resolve(result);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this.remove(owned);
|
|
161
|
+
reject(new Error(policyDenied ? (writeGranted ? 'Managed memory policy changed; admitted write may have completed' : 'Managed memory policy denied before dispatch') : 'Managed native memory operation failed or timed out; parent remains available'));
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const onMessage = (value) => {
|
|
165
|
+
try {
|
|
166
|
+
if (!authorize()) {
|
|
167
|
+
policyDenied = true;
|
|
168
|
+
complete();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const message = value;
|
|
172
|
+
if (message?.phase !== undefined) {
|
|
173
|
+
if (Object.keys(message).length !== 3 || message.v !== 1 || message.requestId !== requestId || !['ready', 'before-write'].includes(String(message.phase)))
|
|
174
|
+
throw new Error('Invalid worker grant request');
|
|
175
|
+
if (message.phase === 'ready') {
|
|
176
|
+
if (ready)
|
|
177
|
+
throw new Error('Repeated worker ready');
|
|
178
|
+
ready = true;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
if (!ready || writeGranted || input.operation !== 'index')
|
|
182
|
+
throw new Error('Invalid worker write grant');
|
|
183
|
+
writeGranted = true;
|
|
184
|
+
}
|
|
185
|
+
owned.child.send({ v: 1, requestId, grant: message.phase === 'ready' ? 'execute' : 'write' }, error => { if (error)
|
|
186
|
+
complete(); });
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (!ready)
|
|
190
|
+
throw new Error('Worker was not ready');
|
|
191
|
+
complete(resultOf(value, input.operation, requestId));
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
complete();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const timeout = setTimeout(() => complete(), this.timeoutMs);
|
|
198
|
+
owned.fail = () => complete();
|
|
199
|
+
owned.child.on('message', onMessage);
|
|
200
|
+
try {
|
|
201
|
+
if (!authorize()) {
|
|
202
|
+
policyDenied = true;
|
|
203
|
+
complete();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
owned.child.send({ v: 1, requestId, ...input }, error => { if (error)
|
|
207
|
+
complete(); });
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
policyDenied = true;
|
|
211
|
+
complete();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
stats() { return { workers: this.slots.size, busy: [...this.slots.values()].filter(s => s.busy).length }; }
|
|
216
|
+
close() { for (const slot of [...this.slots.values()]) {
|
|
217
|
+
slot.fail?.();
|
|
218
|
+
this.remove(slot);
|
|
219
|
+
} }
|
|
220
|
+
remove(slot) {
|
|
221
|
+
if (slot.timer)
|
|
222
|
+
clearTimeout(slot.timer);
|
|
223
|
+
if (this.slots.get(slot.root) === slot)
|
|
224
|
+
this.slots.delete(slot.root);
|
|
225
|
+
if (!slot.child.killed)
|
|
226
|
+
slot.child.kill('SIGKILL');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
export const managedMemoryClient = new ManagedMemoryClient();
|
|
230
|
+
export async function nativeMemoryIndex(root, input, authorize) {
|
|
231
|
+
return await managedMemoryClient.run(root, { operation: 'index', ...input }, authorize);
|
|
232
|
+
}
|
|
233
|
+
export async function nativeMemoryRecall(root, input, authorize) {
|
|
234
|
+
return await managedMemoryClient.run(root, { operation: 'recall', ...input }, authorize);
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=managed-memory-client.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
type Row = {
|
|
2
|
+
key: string;
|
|
3
|
+
namespace: string;
|
|
4
|
+
content: string;
|
|
5
|
+
hasEmbedding?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type NativeMemoryAdapter = {
|
|
8
|
+
getEntry: (options: Record<string, unknown>) => Promise<{
|
|
9
|
+
success: boolean;
|
|
10
|
+
found: boolean;
|
|
11
|
+
entry?: Row;
|
|
12
|
+
} | null>;
|
|
13
|
+
storeEntry: (options: Record<string, unknown>) => Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
persistWarning?: unknown;
|
|
16
|
+
embedding?: {
|
|
17
|
+
dimensions?: number;
|
|
18
|
+
};
|
|
19
|
+
error?: unknown;
|
|
20
|
+
} | null>;
|
|
21
|
+
searchEntries: (options: Record<string, unknown>) => Promise<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
results: Array<{
|
|
24
|
+
key: string;
|
|
25
|
+
namespace: string;
|
|
26
|
+
score: number;
|
|
27
|
+
}>;
|
|
28
|
+
} | null>;
|
|
29
|
+
};
|
|
30
|
+
/** Pure operation seam for host tests. Serialized callers cannot supply an adapter. */
|
|
31
|
+
export declare function executeManagedMemory(raw: unknown, adapter: NativeMemoryAdapter, dbPath: string, beforeWrite?: () => Promise<void>): Promise<{
|
|
32
|
+
v: number;
|
|
33
|
+
requestId: string;
|
|
34
|
+
ok: boolean;
|
|
35
|
+
operation: string;
|
|
36
|
+
durable: boolean;
|
|
37
|
+
hasEmbedding: boolean;
|
|
38
|
+
available?: undefined;
|
|
39
|
+
rows?: undefined;
|
|
40
|
+
} | {
|
|
41
|
+
v: number;
|
|
42
|
+
requestId: string;
|
|
43
|
+
ok: boolean;
|
|
44
|
+
operation: string;
|
|
45
|
+
available: boolean;
|
|
46
|
+
rows: {
|
|
47
|
+
key: string;
|
|
48
|
+
namespace: string;
|
|
49
|
+
score: number;
|
|
50
|
+
content: string;
|
|
51
|
+
}[];
|
|
52
|
+
durable?: undefined;
|
|
53
|
+
hasEmbedding?: undefined;
|
|
54
|
+
}>;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=managed-memory-worker.d.ts.map
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/** Fixed child entry. Native dependency loading must never move into the MCP parent. */
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
const NAMESPACE = 'managed-workflow-evidence';
|
|
8
|
+
const HASH = /^[a-f0-9]{64}$/;
|
|
9
|
+
const UUID = /^[a-f0-9-]{36}$/;
|
|
10
|
+
const TYPES = ['code-review', 'migration', 'research', 'test-generation', 'implementation', 'other'];
|
|
11
|
+
function requestOf(raw) {
|
|
12
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw) || Buffer.byteLength(JSON.stringify(raw)) > 80_000)
|
|
13
|
+
throw new Error('invalid-request');
|
|
14
|
+
const r = raw;
|
|
15
|
+
const keys = r.operation === 'index' ? ['v', 'requestId', 'operation', 'id', 'value', 'taskType'] : ['v', 'requestId', 'operation', 'taskType', 'allowedIds'];
|
|
16
|
+
if (r.v !== 1 || typeof r.requestId !== 'string' || !UUID.test(r.requestId) || !['index', 'recall'].includes(String(r.operation)) || Object.keys(r).length !== keys.length || Object.keys(r).some(k => !keys.includes(k)) || !TYPES.includes(String(r.taskType)))
|
|
17
|
+
throw new Error('invalid-request');
|
|
18
|
+
if (r.operation === 'index' && (typeof r.id !== 'string' || !HASH.test(r.id) || typeof r.value !== 'string' || Buffer.byteLength(r.value) > 8192))
|
|
19
|
+
throw new Error('invalid-request');
|
|
20
|
+
if (r.operation === 'recall' && (!Array.isArray(r.allowedIds) || r.allowedIds.length > 500 || r.allowedIds.some(id => typeof id !== 'string' || !HASH.test(id))))
|
|
21
|
+
throw new Error('invalid-request');
|
|
22
|
+
return r;
|
|
23
|
+
}
|
|
24
|
+
const validRow = (row, key) => !!row && row.key === key && row.namespace === NAMESPACE && typeof row.content === 'string' && Buffer.byteLength(row.content) <= 8192;
|
|
25
|
+
/** Pure operation seam for host tests. Serialized callers cannot supply an adapter. */
|
|
26
|
+
export async function executeManagedMemory(raw, adapter, dbPath, beforeWrite) {
|
|
27
|
+
const r = requestOf(raw);
|
|
28
|
+
if (r.operation === 'index') {
|
|
29
|
+
const existing = await adapter.getEntry({ key: r.id, namespace: NAMESPACE, dbPath });
|
|
30
|
+
if (!existing?.success)
|
|
31
|
+
throw new Error('backend-unavailable');
|
|
32
|
+
if (existing.found) {
|
|
33
|
+
if (!validRow(existing.entry, r.id) || existing.entry.content !== r.value)
|
|
34
|
+
throw new Error('conflicting-index');
|
|
35
|
+
return { v: 1, requestId: r.requestId, ok: true, operation: 'index', durable: true, hasEmbedding: existing.entry.hasEmbedding === true };
|
|
36
|
+
}
|
|
37
|
+
await beforeWrite?.();
|
|
38
|
+
const result = await adapter.storeEntry({ key: r.id, namespace: NAMESPACE, value: r.value, tags: ['managed-workflow', r.taskType], generateEmbeddingFlag: true, upsert: false, requireNative: true, appendOnly: true, dbPath, provenanceType: 'system_observation' });
|
|
39
|
+
if (!result?.success || result.persistWarning)
|
|
40
|
+
throw new Error('persistence-unconfirmed');
|
|
41
|
+
// Observe the exact durable row after the append. Neither success nor a
|
|
42
|
+
// signed local ledger alone establishes that the native row exists.
|
|
43
|
+
const stored = await adapter.getEntry({ key: r.id, namespace: NAMESPACE, dbPath });
|
|
44
|
+
if (!stored?.success || !stored.found || !validRow(stored.entry, r.id) || stored.entry.content !== r.value)
|
|
45
|
+
throw new Error('persistence-unconfirmed');
|
|
46
|
+
return { v: 1, requestId: r.requestId, ok: true, operation: 'index', durable: true, hasEmbedding: stored.entry.hasEmbedding === true };
|
|
47
|
+
}
|
|
48
|
+
const result = await adapter.searchEntries({ query: `managed workflow ${r.taskType}`, namespace: NAMESPACE, limit: 20, threshold: 0, dbPath });
|
|
49
|
+
if (!result?.success || !Array.isArray(result.results))
|
|
50
|
+
return { v: 1, requestId: r.requestId, ok: true, operation: 'recall', available: false, rows: [] };
|
|
51
|
+
const allowed = new Set(r.allowedIds), seen = new Set();
|
|
52
|
+
const rows = [];
|
|
53
|
+
for (const hit of result.results.slice(0, 20)) {
|
|
54
|
+
if (!hit || hit.namespace !== NAMESPACE || typeof hit.key !== 'string' || !HASH.test(hit.key) || !allowed.has(hit.key) || seen.has(hit.key) || !Number.isFinite(hit.score) || hit.score < -1 || hit.score > 1)
|
|
55
|
+
continue;
|
|
56
|
+
seen.add(hit.key);
|
|
57
|
+
const hydrated = await adapter.getEntry({ key: hit.key, namespace: NAMESPACE, dbPath });
|
|
58
|
+
if (hydrated?.success && hydrated.found && validRow(hydrated.entry, hit.key))
|
|
59
|
+
rows.push({ key: hit.key, namespace: NAMESPACE, score: hit.score, content: hydrated.entry.content });
|
|
60
|
+
}
|
|
61
|
+
return { v: 1, requestId: r.requestId, ok: true, operation: 'recall', available: true, rows };
|
|
62
|
+
}
|
|
63
|
+
function databasePath() {
|
|
64
|
+
const root = realpathSync(process.cwd()), dir = join(root, '.swarm');
|
|
65
|
+
if (!existsSync(dir))
|
|
66
|
+
mkdirSync(dir, { mode: 0o700 });
|
|
67
|
+
if (lstatSync(dir).isSymbolicLink() || !lstatSync(dir).isDirectory())
|
|
68
|
+
throw new Error('backend-unavailable');
|
|
69
|
+
const path = join(dir, 'memory.db');
|
|
70
|
+
if (existsSync(path) && (lstatSync(path).isSymbolicLink() || !lstatSync(path).isFile()))
|
|
71
|
+
throw new Error('backend-unavailable');
|
|
72
|
+
return path;
|
|
73
|
+
}
|
|
74
|
+
async function loadNative(dbPath) {
|
|
75
|
+
// Transformers.js does not consistently honor the Python offline env flags.
|
|
76
|
+
// Configure the exact module identity AgentDB loads, before registry init.
|
|
77
|
+
const require = createRequire(import.meta.url);
|
|
78
|
+
const memoryEntry = require.resolve('@claude-flow/memory');
|
|
79
|
+
const agentEntry = createRequire(memoryEntry).resolve('agentdb');
|
|
80
|
+
const owner = createRequire(agentEntry);
|
|
81
|
+
for (const name of ['@huggingface/transformers', '@xenova/transformers']) {
|
|
82
|
+
let path;
|
|
83
|
+
try {
|
|
84
|
+
path = owner.resolve(name);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const modulePath = pathToFileURL(path).href;
|
|
90
|
+
const model = await import(modulePath);
|
|
91
|
+
if (model.env)
|
|
92
|
+
model.env.allowRemoteModels = false;
|
|
93
|
+
}
|
|
94
|
+
const js = new URL('../memory/memory-bridge.js', import.meta.url);
|
|
95
|
+
const moduleUrl = existsSync(fileURLToPath(js)) ? js : new URL('../memory/memory-bridge.ts', import.meta.url);
|
|
96
|
+
// Deliberately nonliteral import: targeted boundary builds do not pull the
|
|
97
|
+
// optional native dependency graph into the parent compilation.
|
|
98
|
+
const modulePath = moduleUrl.href;
|
|
99
|
+
const bridge = await import(modulePath);
|
|
100
|
+
const registry = await bridge.getControllerRegistry(dbPath);
|
|
101
|
+
const agentdb = registry?.getAgentDB?.(), db = agentdb?.database;
|
|
102
|
+
if (!db || agentdb.isWasm === true || typeof db.inTransaction !== 'boolean' || typeof db.transaction !== 'function' || db.memory !== false || typeof db.name !== 'string' || resolve(db.name) !== resolve(dbPath))
|
|
103
|
+
throw new Error('backend-unavailable');
|
|
104
|
+
return { getEntry: bridge.bridgeGetEntry, searchEntries: bridge.bridgeSearchEntries, storeEntry: bridge.bridgeStoreEntry };
|
|
105
|
+
}
|
|
106
|
+
if (typeof process.send === 'function' && process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
107
|
+
let busy = false;
|
|
108
|
+
let idle;
|
|
109
|
+
let adapter;
|
|
110
|
+
let pendingGrant;
|
|
111
|
+
const ask = (id, phase) => new Promise(resolve => {
|
|
112
|
+
pendingGrant = { id, grant: phase === 'ready' ? 'execute' : 'write', resolve };
|
|
113
|
+
process.send?.({ v: 1, requestId: id, phase });
|
|
114
|
+
});
|
|
115
|
+
process.once('disconnect', () => process.exit(0));
|
|
116
|
+
process.on('message', async (value) => {
|
|
117
|
+
if (value && typeof value === 'object' && 'grant' in value) {
|
|
118
|
+
const message = value;
|
|
119
|
+
if (Object.keys(message).length === 3 && message.v === 1 && message.requestId === pendingGrant?.id && message.grant === pendingGrant?.grant) {
|
|
120
|
+
const grant = pendingGrant;
|
|
121
|
+
pendingGrant = undefined;
|
|
122
|
+
grant?.resolve();
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (idle)
|
|
127
|
+
clearTimeout(idle);
|
|
128
|
+
let request;
|
|
129
|
+
try {
|
|
130
|
+
request = requestOf(value);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
process.send?.({ v: 1, ok: false, error: 'invalid-request' });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (busy) {
|
|
137
|
+
process.send?.({ v: 1, requestId: request.requestId, ok: false, error: 'worker-busy' });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
busy = true;
|
|
141
|
+
try {
|
|
142
|
+
const path = databasePath();
|
|
143
|
+
adapter ??= loadNative(path);
|
|
144
|
+
const native = await adapter;
|
|
145
|
+
await ask(request.requestId, 'ready');
|
|
146
|
+
const result = await executeManagedMemory(request, native, path, () => ask(request.requestId, 'before-write'));
|
|
147
|
+
if (Buffer.byteLength(JSON.stringify(result)) > 200_000)
|
|
148
|
+
throw new Error('result-limit');
|
|
149
|
+
process.send?.(result);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Upstream errors may carry prompts, SQL, paths or credentials.
|
|
153
|
+
process.send?.({ v: 1, requestId: request.requestId, ok: false, error: 'native-operation-unavailable' });
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
busy = false;
|
|
157
|
+
idle = setTimeout(() => process.exit(0), 5000);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=managed-memory-worker.js.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type ManagedEvent } from './managed-workflows.js';
|
|
2
|
+
export type ManagedSessionFacts = {
|
|
3
|
+
status?: string;
|
|
4
|
+
budget?: {
|
|
5
|
+
max_list_cost?: {
|
|
6
|
+
amount?: string;
|
|
7
|
+
currency?: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
usage?: {
|
|
11
|
+
list_cost?: {
|
|
12
|
+
amount?: string;
|
|
13
|
+
currency?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type ManagedCollectorTransport = {
|
|
18
|
+
readEvents: (since?: string) => Promise<ManagedEvent[]>;
|
|
19
|
+
readSession: () => Promise<ManagedSessionFacts>;
|
|
20
|
+
};
|
|
21
|
+
type Phase = {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
status: string;
|
|
25
|
+
};
|
|
26
|
+
type Thread = {
|
|
27
|
+
id: string;
|
|
28
|
+
runId: string | null;
|
|
29
|
+
status: string;
|
|
30
|
+
phaseId?: string;
|
|
31
|
+
};
|
|
32
|
+
export type ManagedRunSnapshot = {
|
|
33
|
+
sessionId: string;
|
|
34
|
+
status: string;
|
|
35
|
+
stopReason: string | null;
|
|
36
|
+
budgetCents: number | null;
|
|
37
|
+
listCostCents: number | null;
|
|
38
|
+
providerFinished: boolean;
|
|
39
|
+
workersInspected: false;
|
|
40
|
+
acceptancePassed: false;
|
|
41
|
+
verification: 'unverified';
|
|
42
|
+
runs: Array<{
|
|
43
|
+
id: string;
|
|
44
|
+
status: string;
|
|
45
|
+
result?: string;
|
|
46
|
+
phases: Phase[];
|
|
47
|
+
}>;
|
|
48
|
+
threads: Thread[];
|
|
49
|
+
threadOffset: number;
|
|
50
|
+
threadCount: number;
|
|
51
|
+
threadCounts: {
|
|
52
|
+
running: number;
|
|
53
|
+
ended: number;
|
|
54
|
+
failed: number;
|
|
55
|
+
idle: number;
|
|
56
|
+
};
|
|
57
|
+
blockedEventIds: string[];
|
|
58
|
+
observedFailures: number;
|
|
59
|
+
updatedMs: number;
|
|
60
|
+
};
|
|
61
|
+
export type ManagedCollection = {
|
|
62
|
+
events: ManagedEvent[];
|
|
63
|
+
snapshot: ManagedRunSnapshot;
|
|
64
|
+
};
|
|
65
|
+
export declare const COLLECTOR_LIMITS: {
|
|
66
|
+
readonly sessions: 6;
|
|
67
|
+
readonly events: 10000;
|
|
68
|
+
readonly bytes: 16000000;
|
|
69
|
+
readonly shownThreads: 60;
|
|
70
|
+
readonly staleMs: 300000;
|
|
71
|
+
};
|
|
72
|
+
/** This cache is an observation aid. Cached facts never authorize an action. */
|
|
73
|
+
export declare class ManagedRunCollector {
|
|
74
|
+
private readonly states;
|
|
75
|
+
private readonly now;
|
|
76
|
+
private readonly ttlMs;
|
|
77
|
+
constructor(options?: {
|
|
78
|
+
now?: () => number;
|
|
79
|
+
ttlMs?: number;
|
|
80
|
+
});
|
|
81
|
+
/** Singleflight includes event and session reads; a failed refresh is never cached as success. */
|
|
82
|
+
refresh(sessionId: string, transport: ManagedCollectorTransport): Promise<ManagedCollection>;
|
|
83
|
+
/** Pure observation, suitable for disk snapshot publication. Never invokes a transport. */
|
|
84
|
+
snapshots(): ManagedRunSnapshot[];
|
|
85
|
+
/** A local worker page only. Aggregate counts and observation timestamp do not change. */
|
|
86
|
+
page(sessionId: string, threadOffset?: number): ManagedRunSnapshot | undefined;
|
|
87
|
+
/** A collector doing work cannot be forgotten midflight. No remote work is interrupted. */
|
|
88
|
+
forget(sessionId: string): boolean;
|
|
89
|
+
private observe;
|
|
90
|
+
}
|
|
91
|
+
export declare const sharedManagedCollector: ManagedRunCollector;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=managed-run-collector.d.ts.map
|