brainclaw 1.7.5 → 1.9.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 +28 -11
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +139 -13
- package/dist/commands/add-step.js +1 -1
- package/dist/commands/bootstrap.js +2 -26
- package/dist/commands/check-security-mcp.js +50 -33
- package/dist/commands/check-security.js +86 -43
- package/dist/commands/claim.js +22 -21
- package/dist/commands/confirm.js +26 -0
- package/dist/commands/context-diff.js +1 -1
- package/dist/commands/dispatch-watch.js +142 -0
- package/dist/commands/doctor.js +113 -2
- package/dist/commands/estimation-report.js +115 -16
- package/dist/commands/harvest.js +502 -16
- package/dist/commands/init.js +123 -21
- package/dist/commands/loops-handlers.js +4 -0
- package/dist/commands/mcp-read-handlers.js +198 -29
- package/dist/commands/mcp.js +615 -92
- package/dist/commands/memory.js +21 -17
- package/dist/commands/migrate.js +81 -17
- package/dist/commands/prune.js +78 -4
- package/dist/commands/reflect.js +26 -20
- package/dist/commands/register-agent.js +57 -1
- package/dist/commands/repair.js +20 -0
- package/dist/commands/session-end.js +15 -6
- package/dist/commands/session-start.js +18 -1
- package/dist/commands/setup-security.js +39 -18
- package/dist/commands/setup.js +26 -27
- package/dist/commands/stale.js +16 -2
- package/dist/commands/uninstall.js +126 -34
- package/dist/commands/update-step.js +6 -0
- package/dist/commands/worktree.js +60 -0
- package/dist/core/actions.js +12 -3
- package/dist/core/agent-capability.js +11 -13
- package/dist/core/agent-files.js +844 -547
- package/dist/core/agent-integrations.js +0 -3
- package/dist/core/agent-inventory.js +67 -0
- package/dist/core/agent-registry.js +163 -29
- package/dist/core/agentrun-reconciler.js +33 -2
- package/dist/core/agentruns.js +7 -1
- package/dist/core/ai-agent-detection.js +31 -44
- package/dist/core/archival.js +15 -9
- package/dist/core/assignment-reconciler.js +56 -0
- package/dist/core/assignment-sweeper.js +127 -4
- package/dist/core/assignments.js +69 -11
- package/dist/core/bootstrap.js +233 -67
- package/dist/core/brainclaw-version.js +22 -0
- package/dist/core/candidates.js +21 -1
- package/dist/core/claims.js +313 -150
- package/dist/core/config.js +6 -1
- package/dist/core/context-diff.js +148 -20
- package/dist/core/context.js +129 -8
- package/dist/core/coordination.js +22 -3
- package/dist/core/dispatch-status.js +109 -5
- package/dist/core/dispatcher.js +65 -11
- package/dist/core/entity-operations.js +45 -24
- package/dist/core/entity-registry.js +31 -5
- package/dist/core/event-log.js +138 -21
- package/dist/core/events/checkpoint.js +258 -0
- package/dist/core/events/genesis.js +220 -0
- package/dist/core/events/journal.js +507 -0
- package/dist/core/events/materialize.js +126 -0
- package/dist/core/events/registry-post-image.js +110 -0
- package/dist/core/events/verify.js +109 -0
- package/dist/core/execution-adapters.js +23 -0
- package/dist/core/execution.js +25 -0
- package/dist/core/facade-schema.js +48 -0
- package/dist/core/gc-semantic.js +130 -5
- package/dist/core/handoff-snapshot.js +68 -0
- package/dist/core/ids.js +19 -8
- package/dist/core/instruction-templates.js +34 -115
- package/dist/core/io.js +39 -3
- package/dist/core/json-store.js +10 -1
- package/dist/core/lock.js +153 -28
- package/dist/core/loops/bootstrap-acquire.js +25 -1
- package/dist/core/loops/facade-schema.js +2 -0
- package/dist/core/loops/hooks/survey-signals-baseline.js +36 -0
- package/dist/core/loops/index.js +1 -0
- package/dist/core/loops/presets/bootstrap.js +7 -0
- package/dist/core/loops/store.js +17 -0
- package/dist/core/loops/verbs.js +24 -1
- package/dist/core/markdown.js +8 -76
- package/dist/core/mcp-command-resolution.js +245 -0
- package/dist/core/memory-compactor.js +5 -3
- package/dist/core/memory-lifecycle.js +282 -0
- package/dist/core/merge-risk.js +150 -0
- package/dist/core/messaging.js +8 -1
- package/dist/core/migration.js +11 -1
- package/dist/core/observer-mode.js +26 -0
- package/dist/core/operations/memory-mutation.js +90 -65
- package/dist/core/operations/plan.js +27 -1
- package/dist/core/protocol-skills.js +210 -0
- package/dist/core/reflection-safety.js +6 -7
- package/dist/core/reputation.js +84 -2
- package/dist/core/runtime-signals.js +71 -9
- package/dist/core/runtime.js +84 -1
- package/dist/core/schema.js +125 -0
- package/dist/core/security-detectors.js +125 -0
- package/dist/core/security-extract.js +189 -0
- package/dist/core/security-guard.js +107 -29
- package/dist/core/security-packages.js +121 -0
- package/dist/core/security-scoring.js +76 -9
- package/dist/core/security.js +34 -2
- package/dist/core/sequence.js +11 -2
- package/dist/core/setup-flow.js +141 -13
- package/dist/core/spawn-check.js +110 -4
- package/dist/core/staleness.js +109 -1
- package/dist/core/state.js +250 -54
- package/dist/core/store-resolution.js +19 -5
- package/dist/core/worktree.js +169 -7
- package/dist/facts.js +8 -8
- package/dist/facts.json +7 -7
- package/docs/PROTOCOL.md +223 -0
- package/docs/cli.md +11 -10
- package/docs/concepts/coordinator-runbook.md +129 -0
- package/docs/concepts/dispatch-lifecycle.md +17 -0
- package/docs/concepts/event-log-store-critique-A.md +333 -0
- package/docs/concepts/event-log-store-critique-B.md +353 -0
- package/docs/concepts/event-log-store-phase0-measurements.md +58 -0
- package/docs/concepts/event-log-store-proposal-A.md +365 -0
- package/docs/concepts/event-log-store-proposal-B.md +404 -0
- package/docs/concepts/event-log-store.md +928 -0
- package/docs/concepts/identity-model-proposal.md +371 -0
- package/docs/concepts/memory.md +5 -4
- package/docs/concepts/observer-protocol.md +361 -0
- package/docs/concepts/parallel-merge-protocol.md +71 -0
- package/docs/concepts/plans-and-claims.md +43 -0
- package/docs/concepts/skills.md +78 -0
- package/docs/concepts/workspace-bootstrapping.md +61 -0
- package/docs/integrations/agents.md +4 -4
- package/docs/integrations/cline.md +10 -11
- package/docs/integrations/codex.md +2 -2
- package/docs/integrations/continue.md +5 -5
- package/docs/integrations/copilot.md +14 -12
- package/docs/integrations/openclaw.md +7 -6
- package/docs/integrations/overview.md +7 -7
- package/docs/integrations/roo.md +3 -3
- package/docs/integrations/windsurf.md +6 -6
- package/docs/mcp-schema-changelog.md +51 -20
- package/docs/quickstart.md +48 -47
- package/docs/security.md +174 -15
- package/docs/storage.md +4 -2
- package/package.json +8 -6
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
import { loadState } from '../core/state.js';
|
|
2
2
|
import { memoryExists } from '../core/io.js';
|
|
3
|
+
/** Default wall-clock outlier cutoff: a single plan's "actual" over 24h is almost certainly idle, not work. */
|
|
4
|
+
export const DEFAULT_OUTLIER_THRESHOLD_MINUTES = 1440;
|
|
5
|
+
/**
|
|
6
|
+
* Sum of per-step estimates — only when EVERY step carries one. A mixed plan
|
|
7
|
+
* (some steps estimated, some not) returns undefined so the caller falls back
|
|
8
|
+
* to the plan-level estimate rather than mixing half-measured data (pln#495).
|
|
9
|
+
*/
|
|
10
|
+
function sumStepEstimates(plan) {
|
|
11
|
+
const steps = plan.steps ?? [];
|
|
12
|
+
if (steps.length === 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (!steps.every((s) => typeof s.estimated_effort === 'number'))
|
|
15
|
+
return undefined;
|
|
16
|
+
return steps.reduce((acc, s) => acc + s.estimated_effort, 0);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sum of per-step actual durations — only when EVERY step is measurable, via an
|
|
20
|
+
* explicit `actual_effort` string OR both `started_at`+`completed_at`. Returns
|
|
21
|
+
* undefined otherwise (→ fall back to plan-level). This is the key win: summing
|
|
22
|
+
* per-step durations excludes the idle time BETWEEN steps that plan-level
|
|
23
|
+
* wall-clock wrongly counts as work (pln#495, the pln#494 step-6 bias).
|
|
24
|
+
*/
|
|
25
|
+
function sumStepActuals(plan) {
|
|
26
|
+
const steps = plan.steps ?? [];
|
|
27
|
+
if (steps.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
let total = 0;
|
|
30
|
+
for (const s of steps) {
|
|
31
|
+
let dur;
|
|
32
|
+
if (s.actual_effort) {
|
|
33
|
+
dur = parseEffortMinutes(s.actual_effort);
|
|
34
|
+
}
|
|
35
|
+
else if (s.started_at && s.completed_at) {
|
|
36
|
+
const d = (new Date(s.completed_at).getTime() - new Date(s.started_at).getTime()) / 60000;
|
|
37
|
+
dur = d > 0 ? d : undefined;
|
|
38
|
+
}
|
|
39
|
+
if (dur === undefined)
|
|
40
|
+
return undefined; // any unmeasurable step → no step-level actual
|
|
41
|
+
total += dur;
|
|
42
|
+
}
|
|
43
|
+
return total > 0 ? total : undefined;
|
|
44
|
+
}
|
|
3
45
|
/** Parse legacy actual_effort strings ("30min", "2h", "1h30m", "1d", "45m") → minutes.
|
|
4
46
|
* Still needed for actual_effort which remains a free string. */
|
|
5
47
|
export function parseEffortMinutes(effort) {
|
|
@@ -23,7 +65,7 @@ export function parseEffortMinutes(effort) {
|
|
|
23
65
|
}
|
|
24
66
|
if (!matched) {
|
|
25
67
|
const bare = parseFloat(s);
|
|
26
|
-
if (!isNaN(bare))
|
|
68
|
+
if (!isNaN(bare) && /^\d+(?:\.\d+)?$/.test(s))
|
|
27
69
|
return bare;
|
|
28
70
|
return undefined;
|
|
29
71
|
}
|
|
@@ -65,37 +107,78 @@ export function buildEstimationReport(options = {}) {
|
|
|
65
107
|
const state = loadState(options.cwd);
|
|
66
108
|
const done = state.plan_items.filter((p) => p.status === 'done' && (!options.agent || p.author === options.agent));
|
|
67
109
|
const entries = done.map((p) => {
|
|
110
|
+
// Estimate: prefer the sum of per-step estimates when ALL steps carry one,
|
|
111
|
+
// else the plan-level estimate (pln#495).
|
|
112
|
+
const stepEstimate = sumStepEstimates(p);
|
|
68
113
|
const entry = {
|
|
69
114
|
id: p.id,
|
|
70
115
|
text: p.text,
|
|
71
116
|
author: p.author,
|
|
72
|
-
estimated_minutes: p.estimated_effort,
|
|
117
|
+
estimated_minutes: stepEstimate ?? p.estimated_effort,
|
|
73
118
|
actual_effort: p.actual_effort,
|
|
74
119
|
completed_at: p.completed_at,
|
|
75
120
|
};
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
121
|
+
// Actual + source, in fallback order (pln#495):
|
|
122
|
+
// step durations (idle-gap-free) > plan.actual_effort string > plan wall-clock.
|
|
123
|
+
let actualMinutes;
|
|
124
|
+
let source;
|
|
125
|
+
const stepActual = sumStepActuals(p);
|
|
126
|
+
if (stepActual !== undefined) {
|
|
127
|
+
actualMinutes = stepActual;
|
|
128
|
+
source = 'step';
|
|
83
129
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
130
|
+
else if (p.actual_effort) {
|
|
131
|
+
actualMinutes = parseEffortMinutes(p.actual_effort);
|
|
132
|
+
source = 'plan_string';
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const endTime = p.completed_at ?? p.updated_at;
|
|
136
|
+
const startTime = p.started_at ?? p.created_at;
|
|
137
|
+
if (endTime && startTime) {
|
|
138
|
+
const elapsed = (new Date(endTime).getTime() - new Date(startTime).getTime()) / 60000;
|
|
139
|
+
if (elapsed > 0)
|
|
140
|
+
actualMinutes = elapsed;
|
|
141
|
+
}
|
|
142
|
+
source = 'plan_wallclock';
|
|
143
|
+
}
|
|
144
|
+
if (actualMinutes !== undefined) {
|
|
89
145
|
entry.elapsed_minutes = Math.round(actualMinutes);
|
|
146
|
+
entry.source = source;
|
|
147
|
+
}
|
|
90
148
|
if (entry.estimated_minutes !== undefined && actualMinutes !== undefined && actualMinutes > 0) {
|
|
91
149
|
entry.ratio = parseFloat((entry.estimated_minutes / actualMinutes).toFixed(2));
|
|
92
150
|
}
|
|
93
151
|
return entry;
|
|
94
152
|
});
|
|
153
|
+
// Wall-clock outlier filter (pln#495 step 7): a plan whose actual is plan-level
|
|
154
|
+
// wall-clock AND exceeds the threshold is flagged and dropped from the summary
|
|
155
|
+
// stats — it would otherwise drag the median/mean (the post-restoration +9900%
|
|
156
|
+
// accident). Step- and string-derived actuals are trusted at any size. A
|
|
157
|
+
// threshold of 0 disables the filter. Outliers keep their ratio and stay in
|
|
158
|
+
// `entries` so the chart still shows them, just marked.
|
|
159
|
+
const threshold = options.outlierThresholdMinutes ?? DEFAULT_OUTLIER_THRESHOLD_MINUTES;
|
|
160
|
+
if (threshold > 0) {
|
|
161
|
+
for (const e of entries) {
|
|
162
|
+
if (e.source === 'plan_wallclock' && e.elapsed_minutes !== undefined && e.elapsed_minutes > threshold) {
|
|
163
|
+
e.excluded_from_stats = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
95
167
|
const withBoth = entries.filter((e) => e.ratio !== undefined);
|
|
96
|
-
const
|
|
168
|
+
const included = withBoth.filter((e) => !e.excluded_from_stats);
|
|
169
|
+
const ratios = included.map((e) => e.ratio);
|
|
97
170
|
const medianRatio = ratios.length > 0 ? parseFloat(median(ratios).toFixed(2)) : undefined;
|
|
98
171
|
const meanRatio = ratios.length > 0 ? parseFloat(mean(ratios).toFixed(2)) : undefined;
|
|
172
|
+
// Per-source median (pln#495): exposes how much calibration noise is wall-clock
|
|
173
|
+
// contamination vs idle-gap-free step measurement. Excludes flagged outliers.
|
|
174
|
+
const bySource = {};
|
|
175
|
+
for (const src of ['step', 'plan_string', 'plan_wallclock']) {
|
|
176
|
+
const srcRatios = included.filter((e) => e.source === src).map((e) => e.ratio);
|
|
177
|
+
if (srcRatios.length > 0) {
|
|
178
|
+
bySource[src] = { count: srcRatios.length, median_ratio: parseFloat(median(srcRatios).toFixed(2)) };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const outliersExcluded = withBoth.filter((e) => e.excluded_from_stats).length;
|
|
99
182
|
return {
|
|
100
183
|
entries,
|
|
101
184
|
summary: {
|
|
@@ -105,6 +188,8 @@ export function buildEstimationReport(options = {}) {
|
|
|
105
188
|
median_ratio: medianRatio,
|
|
106
189
|
mean_ratio: meanRatio,
|
|
107
190
|
calibration_hint: medianRatio !== undefined ? buildCalibrationHint(medianRatio) : undefined,
|
|
191
|
+
by_source: Object.keys(bySource).length > 0 ? bySource : undefined,
|
|
192
|
+
outliers_excluded: outliersExcluded > 0 ? outliersExcluded : undefined,
|
|
108
193
|
},
|
|
109
194
|
};
|
|
110
195
|
}
|
|
@@ -129,6 +214,19 @@ export function runEstimationReport(options = {}) {
|
|
|
129
214
|
console.log(`\nMedian ratio (estimated÷actual): ${summary.median_ratio}x · Mean: ${summary.mean_ratio}x`);
|
|
130
215
|
console.log(`→ ${summary.calibration_hint}`);
|
|
131
216
|
}
|
|
217
|
+
if (summary.outliers_excluded) {
|
|
218
|
+
console.log(`(${summary.outliers_excluded} wall-clock outlier(s) >24h excluded from the stats above — shown ⚠ in the chart)`);
|
|
219
|
+
}
|
|
220
|
+
if (summary.by_source) {
|
|
221
|
+
const labels = {
|
|
222
|
+
step: 'step-derived', plan_string: 'plan-string', plan_wallclock: 'plan-wallclock',
|
|
223
|
+
};
|
|
224
|
+
const parts = ['step', 'plan_string', 'plan_wallclock']
|
|
225
|
+
.filter((s) => summary.by_source[s])
|
|
226
|
+
.map((s) => `${labels[s]}: ${summary.by_source[s].median_ratio}x (n=${summary.by_source[s].count})`);
|
|
227
|
+
console.log(`By measurement quality — ${parts.join(' · ')}`);
|
|
228
|
+
console.log(' (step-derived excludes inter-step idle; plan-wallclock is the noisiest)');
|
|
229
|
+
}
|
|
132
230
|
// Chart — only plans with ratio data
|
|
133
231
|
const chartable = entries.filter((e) => e.ratio !== undefined);
|
|
134
232
|
if (chartable.length > 0) {
|
|
@@ -149,7 +247,8 @@ export function runEstimationReport(options = {}) {
|
|
|
149
247
|
: ` OVER +${Math.round((1 / e.ratio - 1) * 100)}%`;
|
|
150
248
|
const est = e.estimated_minutes !== undefined ? `${e.estimated_minutes}min` : '?';
|
|
151
249
|
const act = e.elapsed_minutes !== undefined ? `${e.elapsed_minutes}min` : '?';
|
|
152
|
-
|
|
250
|
+
const srcTag = e.excluded_from_stats ? ' ⚠outlier' : e.source === 'step' ? ' ✓step' : e.source === 'plan_wallclock' ? ' ~wall' : '';
|
|
251
|
+
console.log(` ${label} ${bar} ${e.ratio}x ${est}→${act}${pct}${srcTag}`);
|
|
153
252
|
}
|
|
154
253
|
console.log('');
|
|
155
254
|
}
|