brainclaw 1.14.0 → 1.16.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 +16 -263
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli/register-capture.js +209 -0
- package/dist/cli/register-code-map.js +19 -0
- package/dist/cli/register-coordination.js +472 -0
- package/dist/cli/register-federation.js +258 -0
- package/dist/cli/register-lifecycle.js +436 -0
- package/dist/cli/register-memory-context.js +502 -0
- package/dist/cli/register-planning.js +167 -0
- package/dist/cli/register-review.js +149 -0
- package/dist/cli/shared.js +5 -0
- package/dist/cli.js +212 -2015
- package/dist/commands/dispatch-watch.js +25 -2
- package/dist/commands/harvest.js +31 -6
- package/dist/commands/mcp-catalog.js +1438 -0
- package/dist/commands/mcp-contract.js +33 -0
- package/dist/commands/mcp-presentation.js +27 -0
- package/dist/commands/mcp-read-handlers.js +72 -36
- package/dist/commands/mcp-write-admin.js +328 -0
- package/dist/commands/mcp-write-claims.js +864 -0
- package/dist/commands/mcp-write-coordination.js +1825 -0
- package/dist/commands/mcp-write-entities.js +620 -0
- package/dist/commands/mcp-write-memory.js +451 -0
- package/dist/commands/mcp-write-sequences.js +116 -0
- package/dist/commands/mcp-write-support.js +367 -0
- package/dist/commands/mcp.js +261 -5570
- package/dist/commands/update-handoff.js +28 -42
- package/dist/core/agent-capability.js +31 -14
- package/dist/core/agent-files.js +1 -1
- package/dist/core/agent-registry.js +51 -3
- package/dist/core/claims.js +18 -0
- package/dist/core/coordination.js +5 -2
- package/dist/core/cross-project.js +35 -1
- package/dist/core/dispatcher.js +34 -20
- package/dist/core/entity-operations.js +335 -12
- package/dist/core/entity-registry.js +72 -9
- package/dist/core/execution.js +28 -4
- package/dist/core/facade-schema.js +30 -4
- package/dist/core/federation-cloud.js +142 -11
- package/dist/core/federation-outbox.js +292 -0
- package/dist/core/federation-signing.js +115 -0
- package/dist/core/handoff-review.js +35 -0
- package/dist/core/io.js +6 -0
- package/dist/core/protocol-tool-policy.js +113 -0
- package/dist/core/review-loop-close.js +115 -0
- package/dist/core/schema.js +25 -2
- package/dist/core/security-detectors.js +35 -6
- package/dist/core/security.js +32 -12
- package/dist/core/worktree.js +98 -9
- package/dist/facts.js +13 -11
- package/dist/facts.json +12 -10
- package/docs/PROTOCOL.md +7 -3
- package/docs/concepts/coordinator-runbook.md +3 -0
- package/docs/concepts/dispatch-lifecycle.md +4 -4
- package/docs/concepts/loop-engine.md +3 -1
- package/docs/concepts/troubleshooting.md +1 -1
- package/docs/integrations/codex.md +3 -3
- package/docs/integrations/overview.md +1 -1
- package/docs/mcp-schema-changelog.md +153 -2
- package/docs/playbooks/orchestration.md +1 -1
- package/docs/product/entity-model-audit.md +3 -2
- package/docs/security.md +22 -1
- package/package.json +3 -1
|
@@ -42,6 +42,24 @@ export function evaluateWatchTick(input) {
|
|
|
42
42
|
return 'running';
|
|
43
43
|
}
|
|
44
44
|
const AGENT_CHILD_NAMES = ['claude', 'codex', 'copilot', 'node'];
|
|
45
|
+
/**
|
|
46
|
+
* Parse `ps -A -o ppid=,comm=` output and return the lowercased command names
|
|
47
|
+
* whose parent pid equals `ppid`. Pure + exported so the ppid-filter logic is
|
|
48
|
+
* unit-tested independently of the platform the test runs on (trp_3b096bf4:
|
|
49
|
+
* BSD `ps` on macOS rejects the GNU `--ppid` flag, so we filter here instead).
|
|
50
|
+
*/
|
|
51
|
+
export function parseChildCommsByPpid(psOutput, ppid) {
|
|
52
|
+
const target = Math.floor(ppid);
|
|
53
|
+
return psOutput
|
|
54
|
+
.split('\n')
|
|
55
|
+
.map((line) => {
|
|
56
|
+
const m = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
57
|
+
if (!m || Number(m[1]) !== target)
|
|
58
|
+
return undefined;
|
|
59
|
+
return m[2].trim().toLowerCase();
|
|
60
|
+
})
|
|
61
|
+
.filter((n) => !!n);
|
|
62
|
+
}
|
|
45
63
|
/**
|
|
46
64
|
* Does a real agent process live under the wrapper pid?
|
|
47
65
|
* Returns undefined when the observation itself fails (never treated as death).
|
|
@@ -59,10 +77,15 @@ export function probeAgentChildAlive(wrapperPid) {
|
|
|
59
77
|
const names = out.split(/\r?\n/).map((l) => l.trim().toLowerCase()).filter(Boolean);
|
|
60
78
|
return names.some((n) => AGENT_CHILD_NAMES.some((a) => n.startsWith(a)));
|
|
61
79
|
}
|
|
62
|
-
|
|
80
|
+
// BSD `ps` (macOS) does NOT support the GNU `--ppid` flag — it errored on
|
|
81
|
+
// every poll on macOS and broke child-pid discovery there (trp_3b096bf4).
|
|
82
|
+
// List every process with its parent pid + command (portable across Linux
|
|
83
|
+
// and macOS — same `-A -o …=` form already used in ai-surface-inventory.ts)
|
|
84
|
+
// and filter by ppid in parseChildCommsByPpid.
|
|
85
|
+
const out = execFileSync('ps', ['-A', '-o', 'ppid=,comm='], {
|
|
63
86
|
encoding: 'utf-8', timeout: 15000,
|
|
64
87
|
});
|
|
65
|
-
const names = out
|
|
88
|
+
const names = parseChildCommsByPpid(out, wrapperPid);
|
|
66
89
|
return names.some((n) => AGENT_CHILD_NAMES.some((a) => n.includes(a)));
|
|
67
90
|
}
|
|
68
91
|
catch {
|
package/dist/commands/harvest.js
CHANGED
|
@@ -22,6 +22,7 @@ import { loadAssignment, transitionAssignment } from '../core/assignments.js';
|
|
|
22
22
|
import { loadClaim, releaseClaimsCascade, logCascadeReleaseResult } from '../core/claims.js';
|
|
23
23
|
import { getCapabilityProfile, dispatchCanCommit } from '../core/agent-capability.js';
|
|
24
24
|
import { commitWorktreeOnBehalf, worktreesBaseDir, resolveGitToplevel } from '../core/worktree.js';
|
|
25
|
+
import { closeReviewLoopFromLaneResult } from '../core/review-loop-close.js';
|
|
25
26
|
/**
|
|
26
27
|
* Auto-detect all worktree directories under the brainclaw-managed base dir.
|
|
27
28
|
* Returns subdirectories that exist on disk (may or may not have an inbox).
|
|
@@ -112,10 +113,11 @@ function collectWorktreeCandidateFiles(worktreePath) {
|
|
|
112
113
|
/**
|
|
113
114
|
* Harvest candidates from worktree inboxes into the main project store.
|
|
114
115
|
*
|
|
115
|
-
* This is the coordinator-side fix for gap 5 of E2E test n°1:
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
116
|
+
* This is the coordinator-side fix for gap 5 of E2E test n°1: a worker that
|
|
117
|
+
* cannot write to the main store — a genuinely MCP-less agent, or (post
|
|
118
|
+
* dec#133) a sandboxed codex whose `.git` is read-only so it cannot commit —
|
|
119
|
+
* leaves candidates in its worktree inbox; the coordinator calls
|
|
120
|
+
* `harvestCandidates` to sync them.
|
|
119
121
|
*
|
|
120
122
|
* @returns HarvestResult with counts of harvested, skipped, and errors.
|
|
121
123
|
*/
|
|
@@ -231,8 +233,9 @@ export function runHarvestCandidates(options = {}) {
|
|
|
231
233
|
//
|
|
232
234
|
// A dispatched worker writes a single `LANE-RESULT.json` at its worktree root
|
|
233
235
|
// as its final step. This is the standard, brief-boilerplate-free channel for a
|
|
234
|
-
// worker (
|
|
235
|
-
// outcome. The coordinator ingests it with
|
|
236
|
+
// worker (a genuinely MCP-less agent, or a sandboxed codex that cannot git
|
|
237
|
+
// commit) to report its outcome. The coordinator ingests it with
|
|
238
|
+
// `brainclaw harvest <assignment_id>`.
|
|
236
239
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
237
240
|
/** Conventional path of a worker's lane-result file at the worktree root. */
|
|
238
241
|
export function getLaneResultPath(worktreePath) {
|
|
@@ -269,6 +272,18 @@ export function harvestLaneResults(options = {}) {
|
|
|
269
272
|
// Assignment filter (when harvesting a specific lane).
|
|
270
273
|
if (options.assignmentId && lane.assignment_id !== options.assignmentId)
|
|
271
274
|
continue;
|
|
275
|
+
// pln#628 Focus 4B (Codex review of #87 BLOCKING 1) — a review lane must
|
|
276
|
+
// close/advance its loop on the plain report-only harvest path too, not only
|
|
277
|
+
// on `--integrate`. closeReviewLoopFromLaneResult is convergent + idempotent
|
|
278
|
+
// (a terminal loop is a no-op; a stuck approve is resumed), so firing it here
|
|
279
|
+
// AND in integrateLaneResults is safe — and it runs BEFORE the harvested
|
|
280
|
+
// marker short-circuits below, so a re-harvest still resumes a stuck loop.
|
|
281
|
+
try {
|
|
282
|
+
const laneAssignment = loadAssignment(lane.assignment_id, cwd);
|
|
283
|
+
if (laneAssignment)
|
|
284
|
+
closeReviewLoopFromLaneResult(laneAssignment, lane, agent, cwd);
|
|
285
|
+
}
|
|
286
|
+
catch { /* never block harvest on loop-close */ }
|
|
272
287
|
const marker = laneHarvestedMarkerPath(cwd, lane.assignment_id);
|
|
273
288
|
if (fs.existsSync(marker)) {
|
|
274
289
|
result.skipped.push(lane.assignment_id);
|
|
@@ -455,6 +470,16 @@ export function integrateLaneResults(options = {}) {
|
|
|
455
470
|
if (claimEntry && !claimEntry.released) {
|
|
456
471
|
reasons.push(`claim release ${claimEntry.reason}${claimEntry.error ? `: ${claimEntry.error}` : ''}`);
|
|
457
472
|
}
|
|
473
|
+
// pln#628 Focus 4B — if this lane is a review-loop turn carrying a
|
|
474
|
+
// verdict, map it onto the loop: record the verdict artifact + advance,
|
|
475
|
+
// which auto-closes the loop on reviewer_green (approve) without a human
|
|
476
|
+
// driving complete_turn/advance by hand. No-op for non-review lanes or
|
|
477
|
+
// lanes without a verdict; never throws (harvest is not blocked on it).
|
|
478
|
+
const loopClose = closeReviewLoopFromLaneResult(assignment, lane, actor, cwd);
|
|
479
|
+
if (loopClose) {
|
|
480
|
+
entry.review_loop = loopClose;
|
|
481
|
+
reasons.push(`review-loop ${loopClose.loop_id}: ${loopClose.action} — ${loopClose.reason}`);
|
|
482
|
+
}
|
|
458
483
|
}
|
|
459
484
|
else {
|
|
460
485
|
// blocked / failed: best-effort lifecycle (FSM may reject from offered).
|