claude-flow 3.61.1 → 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/mcp-tools/rure-tools.js +59 -0
- package/v3/@claude-flow/cli/dist/src/missions/schemas.d.ts +12 -12
- package/v3/@claude-flow/cli/dist/src/rure/cuda-cubin.d.ts +7 -0
- package/v3/@claude-flow/cli/dist/src/rure/cuda-cubin.js +17 -2
- package/v3/@claude-flow/cli/dist/src/rure/cuda-occupancy.d.ts +59 -0
- package/v3/@claude-flow/cli/dist/src/rure/cuda-occupancy.js +69 -0
- package/v3/@claude-flow/cli/dist/src/rure/cuda-plan.d.ts +84 -0
- package/v3/@claude-flow/cli/dist/src/rure/cuda-plan.js +197 -0
- package/v3/@claude-flow/cli/dist/src/services/headless-worker-executor.js +56 -19
- 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/dist/src/services/worker-daemon.d.ts +1 -0
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +11 -2
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { limitsFor, occupancy } from './cuda-occupancy.js';
|
|
2
|
+
export const DEFAULT_BLOCK_SIZES = [32, 64, 96, 128, 192, 256, 384, 512, 768, 1024];
|
|
3
|
+
export const MAX_BLOCK_SIZES = 24;
|
|
4
|
+
export const MAX_DYNAMIC_SHARED_BYTES = 1024 * 1024;
|
|
5
|
+
export const MAX_PLANNED_KERNELS = 200;
|
|
6
|
+
export const ASSUMPTIONS = [
|
|
7
|
+
'Theoretical occupancy: how many warps can be resident on one multiprocessor given the kernel\'s registers and static shared memory. It bounds, and does not predict, performance.',
|
|
8
|
+
'Static shared memory is the amount in the file, less the 1 KiB the driver reserves per block where the compiler folds it into that figure (sm_90 and later); a kernel with no static shared section uses none. Dynamic shared memory is not in the file, so it is the dynamicSharedBytes parameter (default 0), the same for every kernel.',
|
|
9
|
+
'Default shared-memory carveout and cache configuration, one block barrier per block (the default), no cluster launch, and no opt-in to more than 48 KiB of shared memory per block.',
|
|
10
|
+
'The registers in the file are what the compiler chose for that architecture; the driver may JIT PTX to different numbers.',
|
|
11
|
+
'Running the plan is a separate step that you take; nothing here compiles or runs GPU code.',
|
|
12
|
+
];
|
|
13
|
+
const round4 = (n) => Math.round(n * 10_000) / 10_000;
|
|
14
|
+
function cleanSizes(sizes) {
|
|
15
|
+
const wanted = sizes === undefined ? [...DEFAULT_BLOCK_SIZES] : [...sizes];
|
|
16
|
+
if (wanted.length === 0 || wanted.length > MAX_BLOCK_SIZES)
|
|
17
|
+
throw new RangeError(`blockSizes must hold 1 to ${MAX_BLOCK_SIZES} sizes`);
|
|
18
|
+
for (const size of wanted)
|
|
19
|
+
if (!Number.isInteger(size) || size < 1 || size > 1024)
|
|
20
|
+
throw new RangeError('each block size must be an integer from 1 to 1024');
|
|
21
|
+
return [...new Set(wanted)].sort((a, b) => a - b);
|
|
22
|
+
}
|
|
23
|
+
function planKernel(kernel, arch, sizes, dynamic) {
|
|
24
|
+
const limits = limitsFor(arch);
|
|
25
|
+
const sectionShared = kernel.sharedBytes ?? 0;
|
|
26
|
+
const staticShared = Math.max(0, sectionShared - (limits.cubinSharedIncludesReserve ? limits.sharedReservedPerBlock : 0));
|
|
27
|
+
const local = kernel.localBytes ?? 0;
|
|
28
|
+
const out = {
|
|
29
|
+
name: kernel.name,
|
|
30
|
+
identifier: kernel.identifier,
|
|
31
|
+
resources: { registers: kernel.registers, staticSharedBytes: staticShared, sharedSectionBytes: sectionShared, localBytes: local, constantBytes: kernel.constantBytes, codeBytes: kernel.codeBytes },
|
|
32
|
+
flags: [],
|
|
33
|
+
};
|
|
34
|
+
if (local > 0)
|
|
35
|
+
out.flags.push('local-memory');
|
|
36
|
+
if (kernel.registers === null || kernel.registers < 1) {
|
|
37
|
+
out.note = 'the file holds no register count for this kernel, so occupancy cannot be worked out';
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
out.blockSizes = sizes.map((blockSize) => {
|
|
41
|
+
const r = occupancy(limits, { registers: kernel.registers, staticSharedBytes: staticShared, dynamicSharedBytes: dynamic, blockSize });
|
|
42
|
+
return { blockSize, activeBlocksPerSM: r.blocks, activeWarpsPerSM: r.activeWarps, occupancy: round4(r.occupancy), limitedBy: r.limiters };
|
|
43
|
+
});
|
|
44
|
+
const top = Math.max(...out.blockSizes.map((b) => b.occupancy));
|
|
45
|
+
const best = out.blockSizes.filter((b) => b.occupancy === top);
|
|
46
|
+
if (top > 0) {
|
|
47
|
+
// The limiters at the smallest best size are the ones to report: a larger one only repeats them.
|
|
48
|
+
const first = best[0];
|
|
49
|
+
out.best = { blockSizes: best.map((b) => b.blockSize), occupancy: top, limitedBy: first.limitedBy };
|
|
50
|
+
if (top < 1 && first.limitedBy.includes('registers'))
|
|
51
|
+
out.flags.push('register-limited');
|
|
52
|
+
if (top < 1 && first.limitedBy.includes('sharedMemory'))
|
|
53
|
+
out.flags.push('shared-memory-limited');
|
|
54
|
+
}
|
|
55
|
+
else
|
|
56
|
+
out.note = 'no listed block size can launch with these resources';
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
export function planCuda(info, options = {}) {
|
|
60
|
+
const sizes = cleanSizes(options.blockSizes);
|
|
61
|
+
const dynamic = options.dynamicSharedBytes ?? 0;
|
|
62
|
+
if (!Number.isInteger(dynamic) || dynamic < 0 || dynamic > MAX_DYNAMIC_SHARED_BYTES)
|
|
63
|
+
throw new RangeError(`dynamicSharedBytes must be an integer from 0 to ${MAX_DYNAMIC_SHARED_BYTES}`);
|
|
64
|
+
const wanted = options.arch === undefined ? info.cubinArchs : [options.arch];
|
|
65
|
+
const archs = [];
|
|
66
|
+
let planned = 0;
|
|
67
|
+
let truncated = false;
|
|
68
|
+
for (const arch of wanted) {
|
|
69
|
+
const limits = limitsFor(arch);
|
|
70
|
+
if (limits === null) {
|
|
71
|
+
archs.push({ arch, supported: false, provenance: null, kernels: [], note: `no occupancy limits are tabulated for ${arch}` });
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const entries = info.kernels.filter((kernel) => kernel.isEntry && kernel.arch === arch);
|
|
75
|
+
const kernels = [];
|
|
76
|
+
for (const kernel of entries) {
|
|
77
|
+
if (planned >= MAX_PLANNED_KERNELS) {
|
|
78
|
+
truncated = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
kernels.push(planKernel(kernel, arch, sizes, dynamic));
|
|
82
|
+
planned++;
|
|
83
|
+
}
|
|
84
|
+
archs.push({
|
|
85
|
+
arch,
|
|
86
|
+
supported: true,
|
|
87
|
+
provenance: limits.provenance,
|
|
88
|
+
limits: { maxThreadsPerSM: limits.maxThreadsPerSM, maxBlocksPerSM: limits.maxBlocksPerSM, regsPerSM: limits.regsPerSM, sharedPerSM: limits.sharedPerSM, sharedReservedPerBlock: limits.sharedReservedPerBlock },
|
|
89
|
+
kernels,
|
|
90
|
+
...(entries.length === 0 && { note: `${arch} is in the file but holds no launchable kernel` }),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const noCubins = info.cubinArchs.length === 0;
|
|
94
|
+
return {
|
|
95
|
+
archs,
|
|
96
|
+
blockSizes: sizes,
|
|
97
|
+
dynamicSharedBytes: dynamic,
|
|
98
|
+
assumptions: [...ASSUMPTIONS, ...(noCubins ? ['The file holds no compiled code for any architecture (PTX only, or nothing readable), so there are no register counts to plan from.'] : [])],
|
|
99
|
+
...(options.includeHarness === true && { harness: HARNESS_SOURCE }),
|
|
100
|
+
truncated,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A program a person can build and run. It is text, fixed in this file: it contains nothing from the analyzed file. Run as
|
|
105
|
+
* ./occ_check <file.cubin|file.fatbin> <kernel name as listed> [dynamicSharedBytes]
|
|
106
|
+
* it asks the CUDA driver how many blocks fit per multiprocessor at each block size (compare with the plan), and, if built with -DRURE_BENCH and a
|
|
107
|
+
* rure_bench.h of your own that sets up the kernel's arguments, times launches. It loads the file's code onto the GPU; the benchmark mode runs it.
|
|
108
|
+
*/
|
|
109
|
+
export const HARNESS_SOURCE = `// occ_check.cpp: written by ruflo's rure_cuda_launch_plan. ruRE did not build or run this; building and running it is your decision.
|
|
110
|
+
// Build: nvcc -O2 -o occ_check occ_check.cpp -lcuda (add -DRURE_BENCH with your own rure_bench.h to time launches)
|
|
111
|
+
// Run: ./occ_check <file.cubin|file.fatbin> <kernel name> [dynamicSharedBytes]
|
|
112
|
+
// Without RURE_BENCH it loads the code onto device 0 and asks the driver for occupancy; it launches nothing.
|
|
113
|
+
// With RURE_BENCH it LAUNCHES THE KERNEL with the arguments your rure_bench.h gives it. Only do that for code you trust, on a GPU you can spare.
|
|
114
|
+
#include <cuda.h>
|
|
115
|
+
#include <algorithm>
|
|
116
|
+
#include <cstdio>
|
|
117
|
+
#include <cstdlib>
|
|
118
|
+
#include <fstream>
|
|
119
|
+
#include <iterator>
|
|
120
|
+
#include <vector>
|
|
121
|
+
#ifdef RURE_BENCH
|
|
122
|
+
// rure_bench.h must define:
|
|
123
|
+
// bool rure_prepare(void*** kernelParams); allocate buffers, fill an array of pointers to the kernel's arguments
|
|
124
|
+
// unsigned rure_grid(int blockSize); how many blocks to launch at this block size
|
|
125
|
+
// void rure_release();
|
|
126
|
+
#include "rure_bench.h"
|
|
127
|
+
#endif
|
|
128
|
+
|
|
129
|
+
#define CK(call) do { CUresult r_ = (call); if (r_ != CUDA_SUCCESS) { const char* m_ = nullptr; cuGetErrorString(r_, &m_); fprintf(stderr, "line %d: %s\\n", __LINE__, m_ ? m_ : "error"); return 1; } } while (0)
|
|
130
|
+
|
|
131
|
+
int main(int argc, char** argv) {
|
|
132
|
+
if (argc < 3) { fprintf(stderr, "usage: %s <file.cubin|file.fatbin> <kernel name> [dynamicSharedBytes]\\n", argv[0]); return 2; }
|
|
133
|
+
size_t dyn = argc > 3 ? strtoull(argv[3], nullptr, 10) : 0;
|
|
134
|
+
std::ifstream in(argv[1], std::ios::binary);
|
|
135
|
+
std::vector<char> image((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
|
136
|
+
if (image.empty()) { fprintf(stderr, "cannot read %s\\n", argv[1]); return 2; }
|
|
137
|
+
|
|
138
|
+
CK(cuInit(0));
|
|
139
|
+
CUdevice dev; CK(cuDeviceGet(&dev, 0));
|
|
140
|
+
CUcontext ctx; CK(cuDevicePrimaryCtxRetain(&ctx, dev)); CK(cuCtxSetCurrent(ctx));
|
|
141
|
+
CUmodule mod; CK(cuModuleLoadData(&mod, image.data()));
|
|
142
|
+
CUfunction fn; CK(cuModuleGetFunction(&fn, mod, argv[2]));
|
|
143
|
+
|
|
144
|
+
int regs, shared, local, maxThreads, maxThreadsSM, major, minor, blocksSM, regsSM, sharedSM, reserved, optin;
|
|
145
|
+
CK(cuFuncGetAttribute(®s, CU_FUNC_ATTRIBUTE_NUM_REGS, fn));
|
|
146
|
+
CK(cuFuncGetAttribute(&shared, CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, fn));
|
|
147
|
+
CK(cuFuncGetAttribute(&local, CU_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES, fn));
|
|
148
|
+
CK(cuFuncGetAttribute(&maxThreads, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, fn));
|
|
149
|
+
CK(cuDeviceGetAttribute(&maxThreadsSM, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR, dev));
|
|
150
|
+
CK(cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, dev));
|
|
151
|
+
CK(cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, dev));
|
|
152
|
+
CK(cuDeviceGetAttribute(&blocksSM, CU_DEVICE_ATTRIBUTE_MAX_BLOCKS_PER_MULTIPROCESSOR, dev));
|
|
153
|
+
CK(cuDeviceGetAttribute(®sSM, CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_MULTIPROCESSOR, dev));
|
|
154
|
+
CK(cuDeviceGetAttribute(&sharedSM, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_MULTIPROCESSOR, dev));
|
|
155
|
+
CK(cuDeviceGetAttribute(&reserved, CU_DEVICE_ATTRIBUTE_RESERVED_SHARED_MEMORY_PER_BLOCK, dev));
|
|
156
|
+
CK(cuDeviceGetAttribute(&optin, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK_OPTIN, dev));
|
|
157
|
+
printf("device sm_%d%d: maxThreadsPerSM %d, maxBlocksPerSM %d, regsPerSM %d, sharedPerSM %d, sharedReservedPerBlock %d, sharedPerBlockOptin %d\\n", major, minor, maxThreadsSM, blocksSM, regsSM, sharedSM, reserved, optin);
|
|
158
|
+
printf("kernel: registers %d, static shared %d, local %d, max threads per block %d\\n", regs, shared, local, maxThreads);
|
|
159
|
+
printf("blockSize activeBlocksPerSM activeWarpsPerSM occupancy%s\\n",
|
|
160
|
+
#ifdef RURE_BENCH
|
|
161
|
+
" medianMs");
|
|
162
|
+
#else
|
|
163
|
+
"");
|
|
164
|
+
#endif
|
|
165
|
+
|
|
166
|
+
const int sizes[] = {32, 64, 96, 128, 192, 256, 384, 512, 768, 1024};
|
|
167
|
+
for (int b : sizes) {
|
|
168
|
+
int blocks = 0;
|
|
169
|
+
if (cuOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, fn, b, dyn) != CUDA_SUCCESS) blocks = 0;
|
|
170
|
+
int warps = blocks * ((b + 31) / 32);
|
|
171
|
+
printf("%d %d %d %.4f", b, blocks, warps, (double)warps / (maxThreadsSM / 32));
|
|
172
|
+
#ifdef RURE_BENCH
|
|
173
|
+
double ms = -1;
|
|
174
|
+
if (blocks > 0) {
|
|
175
|
+
void** params = nullptr;
|
|
176
|
+
if (!rure_prepare(¶ms)) { fprintf(stderr, "rure_prepare failed\\n"); return 1; }
|
|
177
|
+
CUevent t0, t1; CK(cuEventCreate(&t0, CU_EVENT_DEFAULT)); CK(cuEventCreate(&t1, CU_EVENT_DEFAULT));
|
|
178
|
+
std::vector<float> times;
|
|
179
|
+
for (int i = 0; i < 23; i++) {
|
|
180
|
+
CK(cuEventRecord(t0, 0));
|
|
181
|
+
CK(cuLaunchKernel(fn, rure_grid(b), 1, 1, b, 1, 1, (unsigned)dyn, 0, params, 0));
|
|
182
|
+
CK(cuEventRecord(t1, 0)); CK(cuEventSynchronize(t1));
|
|
183
|
+
float t; CK(cuEventElapsedTime(&t, t0, t1));
|
|
184
|
+
if (i >= 3) times.push_back(t);
|
|
185
|
+
}
|
|
186
|
+
std::sort(times.begin(), times.end());
|
|
187
|
+
ms = times[times.size() / 2];
|
|
188
|
+
rure_release();
|
|
189
|
+
}
|
|
190
|
+
printf(" %.4f", ms);
|
|
191
|
+
#endif
|
|
192
|
+
printf("\\n");
|
|
193
|
+
}
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
//# sourceMappingURL=cuda-plan.js.map
|
|
@@ -584,7 +584,12 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
584
584
|
return false;
|
|
585
585
|
}
|
|
586
586
|
clearTimeout(entry.timeout);
|
|
587
|
-
|
|
587
|
+
if (entry.abort) {
|
|
588
|
+
entry.abort();
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
this.killEntryTree(entry.process, 'SIGTERM');
|
|
592
|
+
}
|
|
588
593
|
this.processPool.delete(executionId);
|
|
589
594
|
this.emit('cancelled', { executionId });
|
|
590
595
|
// Process next in queue
|
|
@@ -600,15 +605,21 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
600
605
|
const entries = Array.from(this.processPool.entries());
|
|
601
606
|
for (const [executionId, entry] of entries) {
|
|
602
607
|
clearTimeout(entry.timeout);
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
608
|
+
if (entry.abort) {
|
|
609
|
+
// Settles the promise and owns the SIGTERM -> SIGKILL escalation (#4463).
|
|
610
|
+
entry.abort();
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
this.killEntryTree(entry.process, 'SIGTERM');
|
|
614
|
+
// SIGKILL fallback after 5s to prevent orphan processes (#1395 Bug 6)
|
|
615
|
+
setTimeout(() => {
|
|
616
|
+
try {
|
|
617
|
+
if (entry.process.exitCode === null && entry.process.signalCode === null)
|
|
618
|
+
this.killEntryTree(entry.process, 'SIGKILL');
|
|
619
|
+
}
|
|
620
|
+
catch { /* already dead */ }
|
|
621
|
+
}, 5000).unref();
|
|
622
|
+
}
|
|
612
623
|
this.emit('cancelled', { executionId });
|
|
613
624
|
cancelled++;
|
|
614
625
|
}
|
|
@@ -1075,16 +1086,31 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
1075
1086
|
}
|
|
1076
1087
|
catch { /* already dead */ }
|
|
1077
1088
|
};
|
|
1089
|
+
// #4463 — `child.killed` only means a signal was delivered, not that the
|
|
1090
|
+
// child exited, so escalation keys off the exit state instead.
|
|
1091
|
+
const hasExited = () => child.exitCode !== null || child.signalCode !== null;
|
|
1092
|
+
let escalationHandle;
|
|
1093
|
+
let deadlineHandle;
|
|
1094
|
+
const terminate = () => {
|
|
1095
|
+
if (escalationHandle)
|
|
1096
|
+
return; // already signalled; never repeat SIGTERM
|
|
1097
|
+
killTree('SIGTERM');
|
|
1098
|
+
// Give it a moment to terminate gracefully, then force it.
|
|
1099
|
+
escalationHandle = setTimeout(() => {
|
|
1100
|
+
if (!hasExited())
|
|
1101
|
+
killTree('SIGKILL');
|
|
1102
|
+
}, 5000);
|
|
1103
|
+
};
|
|
1104
|
+
const stopTimers = () => {
|
|
1105
|
+
clearTimeout(escalationHandle);
|
|
1106
|
+
clearTimeout(deadlineHandle);
|
|
1107
|
+
};
|
|
1108
|
+
child.once('exit', () => clearTimeout(escalationHandle));
|
|
1109
|
+
child.once('close', stopTimers);
|
|
1078
1110
|
// Setup timeout
|
|
1079
1111
|
const timeoutHandle = setTimeout(() => {
|
|
1080
1112
|
if (this.processPool.has(options.executionId)) {
|
|
1081
|
-
|
|
1082
|
-
// Give it a moment to terminate gracefully
|
|
1083
|
-
setTimeout(() => {
|
|
1084
|
-
if (!child.killed) {
|
|
1085
|
-
killTree('SIGKILL');
|
|
1086
|
-
}
|
|
1087
|
-
}, 5000);
|
|
1113
|
+
terminate();
|
|
1088
1114
|
}
|
|
1089
1115
|
}, options.timeoutMs);
|
|
1090
1116
|
// Track in process pool
|
|
@@ -1101,8 +1127,17 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
1101
1127
|
let resolved = false;
|
|
1102
1128
|
const cleanup = () => {
|
|
1103
1129
|
clearTimeout(timeoutHandle);
|
|
1130
|
+
clearTimeout(deadlineHandle);
|
|
1104
1131
|
this.processPool.delete(options.executionId);
|
|
1105
1132
|
};
|
|
1133
|
+
poolEntry.abort = () => {
|
|
1134
|
+
if (resolved)
|
|
1135
|
+
return;
|
|
1136
|
+
resolved = true;
|
|
1137
|
+
terminate();
|
|
1138
|
+
cleanup();
|
|
1139
|
+
resolve({ success: false, output: stdout || stderr, error: 'Execution cancelled' });
|
|
1140
|
+
};
|
|
1106
1141
|
child.stdout?.on('data', (data) => {
|
|
1107
1142
|
const chunk = data.toString();
|
|
1108
1143
|
stdout += chunk;
|
|
@@ -1152,13 +1187,15 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
1152
1187
|
});
|
|
1153
1188
|
});
|
|
1154
1189
|
// Handle timeout
|
|
1155
|
-
setTimeout(() => {
|
|
1190
|
+
deadlineHandle = setTimeout(() => {
|
|
1156
1191
|
if (resolved)
|
|
1157
1192
|
return;
|
|
1158
1193
|
if (!this.processPool.has(options.executionId))
|
|
1159
1194
|
return;
|
|
1160
1195
|
resolved = true;
|
|
1161
|
-
|
|
1196
|
+
// Normally a no-op: the timeout handler above already signalled the
|
|
1197
|
+
// tree and owns the SIGKILL escalation (no duplicate SIGTERM).
|
|
1198
|
+
terminate();
|
|
1162
1199
|
cleanup();
|
|
1163
1200
|
resolve({
|
|
1164
1201
|
success: false,
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type ManagedCycleInput = {
|
|
2
|
+
scope: unknown;
|
|
3
|
+
evidence: unknown[];
|
|
4
|
+
index?: boolean;
|
|
5
|
+
maxP95Ms?: number;
|
|
6
|
+
evaluation?: {
|
|
7
|
+
manifest: unknown;
|
|
8
|
+
baselineId: string;
|
|
9
|
+
candidateId: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
/** authorize is host-owned and rechecked across asynchronous boundaries. */
|
|
13
|
+
export declare function cycleLearning(root: string, value: unknown, publicKey: string | undefined, authorize: () => boolean): Promise<{
|
|
14
|
+
cycleId: string;
|
|
15
|
+
phases: string[];
|
|
16
|
+
recorded: string[];
|
|
17
|
+
duplicates: string[];
|
|
18
|
+
indexed: string[];
|
|
19
|
+
deferred: string[];
|
|
20
|
+
recommendation: {
|
|
21
|
+
advisory: boolean;
|
|
22
|
+
selected: {
|
|
23
|
+
strategy: {
|
|
24
|
+
id: string;
|
|
25
|
+
model: string;
|
|
26
|
+
workers: number;
|
|
27
|
+
template: string;
|
|
28
|
+
};
|
|
29
|
+
samples: number;
|
|
30
|
+
successes: number;
|
|
31
|
+
successRate: number;
|
|
32
|
+
successesPerDollar: number;
|
|
33
|
+
p95Ms: number;
|
|
34
|
+
evidenceIds: string[];
|
|
35
|
+
};
|
|
36
|
+
candidates: {
|
|
37
|
+
strategy: {
|
|
38
|
+
id: string;
|
|
39
|
+
model: string;
|
|
40
|
+
workers: number;
|
|
41
|
+
template: string;
|
|
42
|
+
};
|
|
43
|
+
samples: number;
|
|
44
|
+
successes: number;
|
|
45
|
+
successRate: number;
|
|
46
|
+
successesPerDollar: number;
|
|
47
|
+
p95Ms: number;
|
|
48
|
+
evidenceIds: string[];
|
|
49
|
+
}[];
|
|
50
|
+
abstained: boolean;
|
|
51
|
+
reason: string;
|
|
52
|
+
defaultsChanged: boolean;
|
|
53
|
+
};
|
|
54
|
+
evaluation: {
|
|
55
|
+
advisory: boolean;
|
|
56
|
+
eligible: boolean;
|
|
57
|
+
baselineP95Ms: number;
|
|
58
|
+
candidateP95Ms: number;
|
|
59
|
+
delta: number;
|
|
60
|
+
baselineEfficiency: number;
|
|
61
|
+
candidateEfficiency: number;
|
|
62
|
+
pairedResults: number;
|
|
63
|
+
corpusHash: string;
|
|
64
|
+
noRegression: boolean;
|
|
65
|
+
metaharness: import("./harness-benchmark.js").AcceptResult;
|
|
66
|
+
defaultsChanged: boolean;
|
|
67
|
+
evidenceIds: string[];
|
|
68
|
+
} | null;
|
|
69
|
+
memory: {
|
|
70
|
+
available: boolean;
|
|
71
|
+
recalled: number;
|
|
72
|
+
backend: string;
|
|
73
|
+
} | null;
|
|
74
|
+
elapsedMs: number;
|
|
75
|
+
advisory: boolean;
|
|
76
|
+
defaultsChanged: boolean;
|
|
77
|
+
promotionRequested: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
//# sourceMappingURL=managed-learning-loop.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Bounded, replayable feedback cycle. Evaluation is never promotion authority. */
|
|
2
|
+
import { digest, evidenceOf, isVerified, manifestOf, scopeOf, trustedEvidence } from './managed-learning.js';
|
|
3
|
+
import { evaluateLearning, indexLearning, recallLearning, recordLearningBatch, recommendLearning } from './managed-learning-store.js';
|
|
4
|
+
function inputOf(value) {
|
|
5
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
6
|
+
throw new Error('Expected a managed cycle');
|
|
7
|
+
const v = value;
|
|
8
|
+
if (Object.keys(v).some(k => !['scope', 'evidence', 'index', 'maxP95Ms', 'evaluation'].includes(k)) || !Array.isArray(v.evidence) || v.evidence.length > 24)
|
|
9
|
+
throw new Error('Managed cycle accepts at most 24 signed outcomes');
|
|
10
|
+
if (v.index !== undefined && typeof v.index !== 'boolean')
|
|
11
|
+
throw new Error('Invalid indexing request');
|
|
12
|
+
if (v.maxP95Ms !== undefined && (typeof v.maxP95Ms !== 'number' || !Number.isFinite(v.maxP95Ms) || v.maxP95Ms <= 0 || v.maxP95Ms > 86_400_000))
|
|
13
|
+
throw new Error('Invalid latency constraint');
|
|
14
|
+
if (v.evaluation !== undefined) {
|
|
15
|
+
const e = v.evaluation;
|
|
16
|
+
if (!e || typeof e !== 'object' || Array.isArray(e) || Object.keys(e).length !== 3 || Object.keys(e).some(k => !['manifest', 'baselineId', 'candidateId'].includes(k)) || typeof e.baselineId !== 'string' || typeof e.candidateId !== 'string' || !/^[a-z][a-z0-9-]{0,39}$/.test(e.baselineId) || !/^[a-z][a-z0-9-]{0,39}$/.test(e.candidateId) || e.baselineId === e.candidateId)
|
|
17
|
+
throw new Error('Invalid evaluation request');
|
|
18
|
+
manifestOf(e.manifest);
|
|
19
|
+
}
|
|
20
|
+
scopeOf(v.scope);
|
|
21
|
+
return structuredClone(v);
|
|
22
|
+
}
|
|
23
|
+
/** authorize is host-owned and rechecked across asynchronous boundaries. */
|
|
24
|
+
export async function cycleLearning(root, value, publicKey, authorize) {
|
|
25
|
+
const started = performance.now(), input = inputOf(value), scope = scopeOf(input.scope);
|
|
26
|
+
const rows = input.evidence.map(evidenceOf);
|
|
27
|
+
const check = () => { if (!authorize() || !publicKey)
|
|
28
|
+
throw new Error('Host learning policy or verifier changed'); };
|
|
29
|
+
check();
|
|
30
|
+
// Reject the whole batch before any write; unsigned input cannot start a cycle.
|
|
31
|
+
for (const e of rows)
|
|
32
|
+
if (!isVerified(e, publicKey) || e.observation.costCents === null || e.observation.durationMs === null || e.observation.taskType !== scope.taskType || e.observation.policyHash !== scope.policyHash || e.observation.evaluatorHash !== scope.evaluatorHash)
|
|
33
|
+
throw new Error('Every cycle outcome must be authenticated, measured and in scope');
|
|
34
|
+
const trusted = trustedEvidence(rows, publicKey).sort((a, b) => digest(a.observation).localeCompare(digest(b.observation)));
|
|
35
|
+
if (input.index && trusted.filter(e => e.observation.split === 'train').length > 6)
|
|
36
|
+
throw new Error('Indexing cycles accept at most six training outcomes; split the batch');
|
|
37
|
+
const cycleId = digest({ scope, evidence: trusted.map(e => digest(e.observation)).sort(), evaluation: input.evaluation ?? null, index: input.index ?? false, maxP95Ms: input.maxP95Ms ?? null });
|
|
38
|
+
const recorded = [], duplicates = [], indexed = [], deferred = [];
|
|
39
|
+
const phases = ['admitted'];
|
|
40
|
+
// One atomic batch avoids per-row sync/summary work. Later native failures
|
|
41
|
+
// leave a resumable signed batch, never a claimed indexed outcome.
|
|
42
|
+
check();
|
|
43
|
+
for (const r of trusted.length ? recordLearningBatch(root, trusted, publicKey) : []) {
|
|
44
|
+
(r.duplicate ? duplicates : recorded).push(r.id);
|
|
45
|
+
}
|
|
46
|
+
phases.push('recorded');
|
|
47
|
+
let memory;
|
|
48
|
+
if (input.index) {
|
|
49
|
+
const training = trusted.filter(e => e.observation.split === 'train');
|
|
50
|
+
for (const e of training.slice(0, 6)) {
|
|
51
|
+
check();
|
|
52
|
+
const r = await indexLearning(root, digest(e.observation), publicKey, authorize);
|
|
53
|
+
check();
|
|
54
|
+
if (r.indexed)
|
|
55
|
+
indexed.push(r.id);
|
|
56
|
+
else
|
|
57
|
+
deferred.push(r.id);
|
|
58
|
+
}
|
|
59
|
+
phases.push('indexed');
|
|
60
|
+
check();
|
|
61
|
+
memory = await recallLearning(root, scope, publicKey, authorize);
|
|
62
|
+
check();
|
|
63
|
+
phases.push('recalled');
|
|
64
|
+
}
|
|
65
|
+
check();
|
|
66
|
+
const recommendation = recommendLearning(root, scope, publicKey, input.maxP95Ms);
|
|
67
|
+
phases.push('recommended');
|
|
68
|
+
check();
|
|
69
|
+
const evaluation = input.evaluation ? evaluateLearning(root, { scope, ...input.evaluation }, publicKey) : null;
|
|
70
|
+
if (evaluation)
|
|
71
|
+
phases.push('evaluated');
|
|
72
|
+
return { cycleId, phases, recorded, duplicates, indexed, deferred, recommendation, evaluation,
|
|
73
|
+
memory: memory ? { available: memory.available, recalled: memory.evidence.length, backend: memory.backend } : null,
|
|
74
|
+
elapsedMs: performance.now() - started, advisory: true, defaultsChanged: false, promotionRequested: false };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=managed-learning-loop.js.map
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { type Evidence } from './managed-learning.js';
|
|
2
|
+
export declare const LEARNING_PATH = ".claude-flow/managed-learning/state.json";
|
|
3
|
+
export declare function learningStatus(root: string, publicKey?: string): {
|
|
4
|
+
version: number;
|
|
5
|
+
updatedMs: number;
|
|
6
|
+
observations: number;
|
|
7
|
+
verified: number;
|
|
8
|
+
quarantined: number;
|
|
9
|
+
training: number;
|
|
10
|
+
holdout: number;
|
|
11
|
+
verifierConfigured: boolean;
|
|
12
|
+
lastRecommendation: {
|
|
13
|
+
selected: {
|
|
14
|
+
evidenceIds: string[];
|
|
15
|
+
strategy: {
|
|
16
|
+
id: string;
|
|
17
|
+
model: string;
|
|
18
|
+
workers: number;
|
|
19
|
+
template: string;
|
|
20
|
+
};
|
|
21
|
+
samples: number;
|
|
22
|
+
successes: number;
|
|
23
|
+
successRate: number;
|
|
24
|
+
successesPerDollar: number;
|
|
25
|
+
p95Ms: number;
|
|
26
|
+
} | null;
|
|
27
|
+
candidates: {
|
|
28
|
+
evidenceIds: string[];
|
|
29
|
+
strategy: {
|
|
30
|
+
id: string;
|
|
31
|
+
model: string;
|
|
32
|
+
workers: number;
|
|
33
|
+
template: string;
|
|
34
|
+
};
|
|
35
|
+
samples: number;
|
|
36
|
+
successes: number;
|
|
37
|
+
successRate: number;
|
|
38
|
+
successesPerDollar: number;
|
|
39
|
+
p95Ms: number;
|
|
40
|
+
}[];
|
|
41
|
+
advisory: boolean;
|
|
42
|
+
abstained: boolean;
|
|
43
|
+
reason: string;
|
|
44
|
+
defaultsChanged: boolean;
|
|
45
|
+
} | null;
|
|
46
|
+
lastEvaluation: {
|
|
47
|
+
evidenceIds: string[];
|
|
48
|
+
advisory: boolean;
|
|
49
|
+
eligible: boolean;
|
|
50
|
+
baselineP95Ms: number;
|
|
51
|
+
candidateP95Ms: number;
|
|
52
|
+
delta: number;
|
|
53
|
+
baselineEfficiency: number;
|
|
54
|
+
candidateEfficiency: number;
|
|
55
|
+
pairedResults: number;
|
|
56
|
+
corpusHash: string;
|
|
57
|
+
noRegression: boolean;
|
|
58
|
+
metaharness: import("./harness-benchmark.js").AcceptResult;
|
|
59
|
+
defaultsChanged: boolean;
|
|
60
|
+
} | null;
|
|
61
|
+
advisory: boolean;
|
|
62
|
+
defaultsChanged: boolean;
|
|
63
|
+
};
|
|
64
|
+
/** Validate all input before acquiring the writer lock; publish the batch atomically. */
|
|
65
|
+
export declare function recordLearningBatch(root: string, inputs: unknown[], publicKey?: string): {
|
|
66
|
+
stored: boolean;
|
|
67
|
+
duplicate: boolean;
|
|
68
|
+
id: string;
|
|
69
|
+
verified: boolean;
|
|
70
|
+
}[];
|
|
71
|
+
export declare function recordLearning(root: string, input: unknown, publicKey?: string): {
|
|
72
|
+
stored: boolean;
|
|
73
|
+
duplicate: boolean;
|
|
74
|
+
id: string;
|
|
75
|
+
verified: boolean;
|
|
76
|
+
};
|
|
77
|
+
export declare function recommendLearning(root: string, scope: unknown, publicKey?: string, maxP95Ms?: number): {
|
|
78
|
+
advisory: boolean;
|
|
79
|
+
selected: {
|
|
80
|
+
strategy: {
|
|
81
|
+
id: string;
|
|
82
|
+
model: string;
|
|
83
|
+
workers: number;
|
|
84
|
+
template: string;
|
|
85
|
+
};
|
|
86
|
+
samples: number;
|
|
87
|
+
successes: number;
|
|
88
|
+
successRate: number;
|
|
89
|
+
successesPerDollar: number;
|
|
90
|
+
p95Ms: number;
|
|
91
|
+
evidenceIds: string[];
|
|
92
|
+
};
|
|
93
|
+
candidates: {
|
|
94
|
+
strategy: {
|
|
95
|
+
id: string;
|
|
96
|
+
model: string;
|
|
97
|
+
workers: number;
|
|
98
|
+
template: string;
|
|
99
|
+
};
|
|
100
|
+
samples: number;
|
|
101
|
+
successes: number;
|
|
102
|
+
successRate: number;
|
|
103
|
+
successesPerDollar: number;
|
|
104
|
+
p95Ms: number;
|
|
105
|
+
evidenceIds: string[];
|
|
106
|
+
}[];
|
|
107
|
+
abstained: boolean;
|
|
108
|
+
reason: string;
|
|
109
|
+
defaultsChanged: boolean;
|
|
110
|
+
};
|
|
111
|
+
export declare function evaluateLearning(root: string, input: {
|
|
112
|
+
scope: unknown;
|
|
113
|
+
manifest: unknown;
|
|
114
|
+
baselineId: string;
|
|
115
|
+
candidateId: string;
|
|
116
|
+
}, publicKey?: string): {
|
|
117
|
+
advisory: boolean;
|
|
118
|
+
eligible: boolean;
|
|
119
|
+
baselineP95Ms: number;
|
|
120
|
+
candidateP95Ms: number;
|
|
121
|
+
delta: number;
|
|
122
|
+
baselineEfficiency: number;
|
|
123
|
+
candidateEfficiency: number;
|
|
124
|
+
pairedResults: number;
|
|
125
|
+
corpusHash: string;
|
|
126
|
+
noRegression: boolean;
|
|
127
|
+
metaharness: import("./harness-benchmark.js").AcceptResult;
|
|
128
|
+
defaultsChanged: boolean;
|
|
129
|
+
evidenceIds: string[];
|
|
130
|
+
};
|
|
131
|
+
/** Export only verified training evidence to native append-only AgentDB/RuVector storage. */
|
|
132
|
+
export declare function indexLearning(root: string, id: string, publicKey?: string, authorize?: () => boolean): Promise<{
|
|
133
|
+
indexed: boolean;
|
|
134
|
+
durable: boolean;
|
|
135
|
+
namespace: string;
|
|
136
|
+
id: string;
|
|
137
|
+
hasEmbedding: boolean;
|
|
138
|
+
advisory: boolean;
|
|
139
|
+
}>;
|
|
140
|
+
/** Memory similarity supplies recall evidence, never authority or a cherry-picked quality denominator. */
|
|
141
|
+
export declare function recallLearning(root: string, input: unknown, publicKey?: string, authorize?: () => boolean): Promise<{
|
|
142
|
+
available: boolean;
|
|
143
|
+
backend: string;
|
|
144
|
+
evidence: {
|
|
145
|
+
id: string;
|
|
146
|
+
similarity: number;
|
|
147
|
+
strategy: Evidence["observation"]["strategy"];
|
|
148
|
+
sourceHash: string;
|
|
149
|
+
}[];
|
|
150
|
+
recommendation: {
|
|
151
|
+
advisory: boolean;
|
|
152
|
+
selected: {
|
|
153
|
+
strategy: {
|
|
154
|
+
id: string;
|
|
155
|
+
model: string;
|
|
156
|
+
workers: number;
|
|
157
|
+
template: string;
|
|
158
|
+
};
|
|
159
|
+
samples: number;
|
|
160
|
+
successes: number;
|
|
161
|
+
successRate: number;
|
|
162
|
+
successesPerDollar: number;
|
|
163
|
+
p95Ms: number;
|
|
164
|
+
evidenceIds: string[];
|
|
165
|
+
};
|
|
166
|
+
candidates: {
|
|
167
|
+
strategy: {
|
|
168
|
+
id: string;
|
|
169
|
+
model: string;
|
|
170
|
+
workers: number;
|
|
171
|
+
template: string;
|
|
172
|
+
};
|
|
173
|
+
samples: number;
|
|
174
|
+
successes: number;
|
|
175
|
+
successRate: number;
|
|
176
|
+
successesPerDollar: number;
|
|
177
|
+
p95Ms: number;
|
|
178
|
+
evidenceIds: string[];
|
|
179
|
+
}[];
|
|
180
|
+
abstained: boolean;
|
|
181
|
+
reason: string;
|
|
182
|
+
defaultsChanged: boolean;
|
|
183
|
+
};
|
|
184
|
+
advisory: boolean;
|
|
185
|
+
similarityIsVerification: boolean;
|
|
186
|
+
}>;
|
|
187
|
+
//# sourceMappingURL=managed-learning-store.d.ts.map
|