brainclaw 1.8.0 → 1.9.1
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 +592 -505
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +138 -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 +286 -23
- package/dist/commands/hooks.js +73 -73
- package/dist/commands/init.js +124 -22
- package/dist/commands/install-hooks.js +78 -78
- package/dist/commands/loops-handlers.js +4 -0
- package/dist/commands/mcp-read-handlers.js +253 -41
- package/dist/commands/mcp.js +664 -102
- 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/switch.js +26 -5
- package/dist/commands/uninstall.js +126 -34
- package/dist/commands/update-step.js +6 -0
- package/dist/commands/version.js +1 -1
- package/dist/commands/worktree.js +60 -0
- package/dist/core/actions.js +12 -3
- package/dist/core/agent-capability.js +30 -17
- package/dist/core/agent-files.js +963 -666
- 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/codev-prompts.js +38 -38
- 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/default-profiles/doctor.yaml +11 -11
- package/dist/core/default-profiles/janitor.yaml +11 -11
- package/dist/core/default-profiles/onboarder.yaml +11 -11
- package/dist/core/default-profiles/reviewer.yaml +13 -13
- package/dist/core/dispatch-status.js +79 -5
- package/dist/core/dispatcher.js +65 -12
- package/dist/core/entity-operations.js +74 -27
- 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 +1 -1
- package/dist/core/facade-schema.js +38 -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 -2
- 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 +10 -3
- 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 +72 -10
- package/dist/core/runtime.js +84 -1
- package/dist/core/schema.js +114 -0
- package/dist/core/search.js +19 -2
- package/dist/core/security-detectors.js +125 -0
- package/dist/core/security-extract.js +189 -0
- package/dist/core/security-guard.js +217 -139
- 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 +16 -2
- package/dist/core/staleness.js +73 -2
- package/dist/core/state.js +250 -54
- package/dist/core/store-resolution.js +45 -12
- package/dist/core/worktree.js +90 -26
- package/dist/facts.js +8 -8
- package/dist/facts.json +7 -7
- package/docs/PROTOCOL.md +223 -0
- package/docs/adapters/openclaw.md +43 -43
- package/docs/architecture/project-refs.md +328 -328
- package/docs/cli.md +2097 -2096
- package/docs/concepts/coordination.md +52 -52
- package/docs/concepts/coordinator-runbook.md +129 -0
- package/docs/concepts/dispatch-lifecycle.md +245 -245
- package/docs/concepts/event-log-store.md +928 -0
- package/docs/concepts/ideation-loop.md +317 -317
- package/docs/concepts/loop-engine.md +520 -511
- package/docs/concepts/mcp-governance.md +268 -268
- package/docs/concepts/memory.md +89 -88
- package/docs/concepts/multi-agent-workflows.md +167 -167
- package/docs/concepts/observer-protocol.md +361 -0
- package/docs/concepts/parallel-merge-protocol.md +71 -0
- package/docs/concepts/plans-and-claims.md +217 -174
- package/docs/concepts/project-md-convention.md +35 -35
- package/docs/concepts/runtime-notes.md +38 -38
- package/docs/concepts/skills.md +78 -0
- package/docs/concepts/troubleshooting.md +254 -254
- package/docs/concepts/workspace-bootstrapping.md +142 -81
- package/docs/context-format-changelog.md +35 -35
- package/docs/context-format.md +48 -48
- package/docs/index.md +65 -65
- package/docs/integrations/agents.md +162 -162
- package/docs/integrations/claude-code.md +23 -23
- package/docs/integrations/cline.md +87 -88
- package/docs/integrations/codex.md +2 -2
- package/docs/integrations/continue.md +60 -60
- package/docs/integrations/copilot.md +82 -80
- package/docs/integrations/cursor.md +23 -23
- package/docs/integrations/kilocode.md +72 -72
- package/docs/integrations/mcp.md +377 -377
- package/docs/integrations/mistral-vibe.md +122 -122
- package/docs/integrations/openclaw.md +99 -98
- package/docs/integrations/opencode.md +84 -84
- package/docs/integrations/overview.md +122 -122
- package/docs/integrations/roo.md +74 -74
- package/docs/integrations/windsurf.md +83 -83
- package/docs/mcp-schema-changelog.md +360 -329
- package/docs/playbooks/integration/index.md +121 -121
- package/docs/playbooks/orchestration.md +37 -0
- package/docs/playbooks/productivity/index.md +99 -99
- package/docs/playbooks/team/index.md +117 -117
- package/docs/product/agent-first-model.md +184 -184
- package/docs/product/entity-model-audit.md +462 -462
- package/docs/product/positioning.md +86 -86
- package/docs/quickstart-existing-project.md +107 -107
- package/docs/quickstart.md +148 -147
- package/docs/release-maintenance.md +79 -79
- package/docs/reputation.md +52 -52
- package/docs/review.md +45 -45
- package/docs/security.md +212 -53
- package/docs/server-operations.md +118 -118
- package/docs/storage.md +110 -108
- package/package.json +86 -69
|
@@ -12,18 +12,18 @@ export function truncate(text, maxLength) {
|
|
|
12
12
|
* Round 0 — Each agent states their initial position on the topic.
|
|
13
13
|
*/
|
|
14
14
|
export function buildPositionPrompt(persona, exposition, targetDurationSeconds, responseFilePath) {
|
|
15
|
-
return `[CoDev Round 0 — Position initiale]
|
|
16
|
-
Tu es ${persona.name}: ${persona.focus}
|
|
17
|
-
|
|
18
|
-
## Sujet
|
|
19
|
-
${truncate(exposition, 800)}
|
|
20
|
-
|
|
21
|
-
## Consigne
|
|
22
|
-
Donne ta position en 3-5 phrases maximum. Sois direct et concret.
|
|
23
|
-
Durée cible: ${targetDurationSeconds}s.
|
|
24
|
-
|
|
25
|
-
## Format de réponse
|
|
26
|
-
Écris ta réponse dans le fichier: ${responseFilePath}
|
|
15
|
+
return `[CoDev Round 0 — Position initiale]
|
|
16
|
+
Tu es ${persona.name}: ${persona.focus}
|
|
17
|
+
|
|
18
|
+
## Sujet
|
|
19
|
+
${truncate(exposition, 800)}
|
|
20
|
+
|
|
21
|
+
## Consigne
|
|
22
|
+
Donne ta position en 3-5 phrases maximum. Sois direct et concret.
|
|
23
|
+
Durée cible: ${targetDurationSeconds}s.
|
|
24
|
+
|
|
25
|
+
## Format de réponse
|
|
26
|
+
Écris ta réponse dans le fichier: ${responseFilePath}
|
|
27
27
|
Format: JSON avec { "persona": "${persona.name}", "text": "<ta réponse>" }`;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
@@ -34,19 +34,19 @@ export function buildReactionPrompt(persona, previousPositions, roundNumber, tar
|
|
|
34
34
|
.filter((p) => p.persona !== persona.name)
|
|
35
35
|
.map((p) => `**${p.persona}**: ${truncate(p.text, 200)}`)
|
|
36
36
|
.join('\n');
|
|
37
|
-
return `[CoDev Round ${roundNumber} — Réaction]
|
|
38
|
-
Tu es ${persona.name}: ${persona.focus}
|
|
39
|
-
|
|
40
|
-
## Positions précédentes
|
|
41
|
-
${positionLines}
|
|
42
|
-
|
|
43
|
-
## Consigne
|
|
44
|
-
Réagis aux positions ci-dessus depuis ton point de vue de ${persona.name}.
|
|
45
|
-
Challenge, renforce ou propose une alternative. 3-5 phrases.
|
|
46
|
-
Durée cible: ${targetDurationSeconds}s.
|
|
47
|
-
|
|
48
|
-
## Format de réponse
|
|
49
|
-
Écris ta réponse dans le fichier: ${responseFilePath}
|
|
37
|
+
return `[CoDev Round ${roundNumber} — Réaction]
|
|
38
|
+
Tu es ${persona.name}: ${persona.focus}
|
|
39
|
+
|
|
40
|
+
## Positions précédentes
|
|
41
|
+
${positionLines}
|
|
42
|
+
|
|
43
|
+
## Consigne
|
|
44
|
+
Réagis aux positions ci-dessus depuis ton point de vue de ${persona.name}.
|
|
45
|
+
Challenge, renforce ou propose une alternative. 3-5 phrases.
|
|
46
|
+
Durée cible: ${targetDurationSeconds}s.
|
|
47
|
+
|
|
48
|
+
## Format de réponse
|
|
49
|
+
Écris ta réponse dans le fichier: ${responseFilePath}
|
|
50
50
|
Format: JSON avec { "persona": "${persona.name}", "text": "<ta réponse>" }`;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
@@ -56,19 +56,19 @@ export function buildConvergencePrompt(persona, allPositions, targetDurationSeco
|
|
|
56
56
|
const positionLines = allPositions
|
|
57
57
|
.map((p) => `[R${p.round}] **${p.persona}**: ${truncate(p.text, 200)}`)
|
|
58
58
|
.join('\n');
|
|
59
|
-
return `[CoDev — Convergence]
|
|
60
|
-
Tu es ${persona.name}: ${persona.focus}
|
|
61
|
-
|
|
62
|
-
## Toutes les positions
|
|
63
|
-
${positionLines}
|
|
64
|
-
|
|
65
|
-
## Consigne
|
|
66
|
-
Identifie 3 points de convergence et 2 désaccords restants.
|
|
67
|
-
Propose des actions concrètes pour avancer. 3-5 phrases.
|
|
68
|
-
Durée cible: ${targetDurationSeconds}s.
|
|
69
|
-
|
|
70
|
-
## Format de réponse
|
|
71
|
-
Écris ta réponse dans le fichier: ${responseFilePath}
|
|
59
|
+
return `[CoDev — Convergence]
|
|
60
|
+
Tu es ${persona.name}: ${persona.focus}
|
|
61
|
+
|
|
62
|
+
## Toutes les positions
|
|
63
|
+
${positionLines}
|
|
64
|
+
|
|
65
|
+
## Consigne
|
|
66
|
+
Identifie 3 points de convergence et 2 désaccords restants.
|
|
67
|
+
Propose des actions concrètes pour avancer. 3-5 phrases.
|
|
68
|
+
Durée cible: ${targetDurationSeconds}s.
|
|
69
|
+
|
|
70
|
+
## Format de réponse
|
|
71
|
+
Écris ta réponse dans le fichier: ${responseFilePath}
|
|
72
72
|
Format: JSON avec { "persona": "${persona.name}", "text": "<ta réponse>" }`;
|
|
73
73
|
}
|
|
74
74
|
//# sourceMappingURL=codev-prompts.js.map
|
package/dist/core/config.js
CHANGED
|
@@ -37,6 +37,11 @@ export function defaultConfig(projectName, options = {}) {
|
|
|
37
37
|
mode: 'warn',
|
|
38
38
|
strict_redaction: false,
|
|
39
39
|
block_sensitive_paths: true,
|
|
40
|
+
token_detection: {
|
|
41
|
+
enabled: true,
|
|
42
|
+
entropy: { enabled: true, min_length: 32, min_entropy: 4.0 },
|
|
43
|
+
detectors: {},
|
|
44
|
+
},
|
|
40
45
|
},
|
|
41
46
|
markdown: {
|
|
42
47
|
max_items_per_section: 20,
|
|
@@ -56,7 +61,7 @@ export function defaultConfig(projectName, options = {}) {
|
|
|
56
61
|
},
|
|
57
62
|
governance: {
|
|
58
63
|
approval_policy: 'review',
|
|
59
|
-
curators: [],
|
|
64
|
+
curators: options.curatorName ? [options.curatorName] : [],
|
|
60
65
|
review_sla_hours: 24,
|
|
61
66
|
},
|
|
62
67
|
reputation: {
|
|
@@ -2,10 +2,9 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { readAuditLog } from './audit.js';
|
|
4
4
|
import { listCandidates } from './candidates.js';
|
|
5
|
-
import {
|
|
6
|
-
import { memoryDir, resolveEntityDir } from './io.js';
|
|
7
|
-
import { logger } from './logger.js';
|
|
5
|
+
import { resolveEntityDir } from './io.js';
|
|
8
6
|
import { loadVersionedJsonFile } from './migration.js';
|
|
7
|
+
import { buildNotificationSummary, hasEventCursor, readUnseenEvents, seedCursorToEnd } from './event-log.js';
|
|
9
8
|
import { SessionSnapshotSchema } from './schema.js';
|
|
10
9
|
import { loadState } from './state.js';
|
|
11
10
|
export function resolveContextDiffSince(options) {
|
|
@@ -23,10 +22,10 @@ export function resolveContextDiffSince(options) {
|
|
|
23
22
|
}
|
|
24
23
|
return { since_session: options.session };
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// No global marker fallback: the "what's new" default lives on the
|
|
26
|
+
// per-agent event-log cursors (buildContextDiffFromEvents). The store-global
|
|
27
|
+
// .last-context marker cross-contaminated agents — one agent's read reset
|
|
28
|
+
// everyone's diff baseline.
|
|
30
29
|
return {};
|
|
31
30
|
}
|
|
32
31
|
function loadSessionSnapshot(sessionId, cwd) {
|
|
@@ -66,33 +65,160 @@ export function buildContextDiff(options = {}) {
|
|
|
66
65
|
decisions: decisions.length,
|
|
67
66
|
traps: traps.length,
|
|
68
67
|
handoffs: handoffs.length,
|
|
68
|
+
plans: 0,
|
|
69
69
|
pending_candidates: pendingCandidates.length,
|
|
70
70
|
total: constraints.length + decisions.length + traps.length + handoffs.length + pendingCandidates.length,
|
|
71
71
|
};
|
|
72
72
|
return {
|
|
73
73
|
since: resolved.since,
|
|
74
74
|
since_session: resolved.since_session,
|
|
75
|
+
source: 'timestamp',
|
|
75
76
|
summary: buildContextDiffSummary(counts),
|
|
76
77
|
counts,
|
|
77
78
|
changed_items: changedItems,
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
const EVENT_SECTION_BY_ITEM_TYPE = {
|
|
82
|
+
constraint: 'constraint',
|
|
83
|
+
decision: 'decision',
|
|
84
|
+
trap: 'trap',
|
|
85
|
+
handoff: 'handoff',
|
|
86
|
+
candidate: 'candidate',
|
|
87
|
+
plan: 'plan',
|
|
88
|
+
};
|
|
89
|
+
const EVENT_ACTION_LABEL = {
|
|
90
|
+
create: 'created',
|
|
91
|
+
update: 'updated',
|
|
92
|
+
delete: 'deleted',
|
|
93
|
+
accept: 'accepted',
|
|
94
|
+
reject: 'rejected',
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Build a per-agent "what's new" diff from the event-log cursors
|
|
98
|
+
* (src/core/event-log.ts). This is the converged novelty mechanism: it
|
|
99
|
+
* replaces the store-global .last-context marker for the default diff path
|
|
100
|
+
* and natively covers status transitions (logged as `update` events by
|
|
101
|
+
* bclaw_transition / bclaw_update via the audit→event bridge).
|
|
102
|
+
*
|
|
103
|
+
* NOTE: reading ADVANCES the agent's cursor — events surfaced here are
|
|
104
|
+
* considered seen. Returns undefined when there is nothing new.
|
|
105
|
+
*
|
|
106
|
+
* First contact (no cursor for this agent yet): the diff would otherwise
|
|
107
|
+
* replay the ENTIRE event log from genesis — on a mature store that means
|
|
108
|
+
* thousands of stale claim/session events summarized into noise, and the
|
|
109
|
+
* agent's single chance to triage history is consumed by the cursor advance.
|
|
110
|
+
* Instead we emit a curated arrival digest (active constraints/traps,
|
|
111
|
+
* in-progress plans, latest open handoffs) and seed the cursor at log end so
|
|
112
|
+
* subsequent diffs are genuinely incremental.
|
|
113
|
+
*/
|
|
114
|
+
export function buildContextDiffFromEvents(agent, cwd, options = {}) {
|
|
115
|
+
if (!hasEventCursor(agent, cwd)) {
|
|
116
|
+
return buildArrivalDigest(agent, cwd, options);
|
|
84
117
|
}
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
118
|
+
const events = readUnseenEvents(agent, cwd);
|
|
119
|
+
if (events.length === 0) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
// Latest relevant event per item (an item created then updated counts once).
|
|
123
|
+
const latestByItem = new Map();
|
|
124
|
+
for (const event of events) {
|
|
125
|
+
if (!event.item_id)
|
|
126
|
+
continue;
|
|
127
|
+
if (!EVENT_SECTION_BY_ITEM_TYPE[event.item_type] || !EVENT_ACTION_LABEL[event.action])
|
|
128
|
+
continue;
|
|
129
|
+
latestByItem.set(event.item_id, event);
|
|
130
|
+
}
|
|
131
|
+
const state = loadState(cwd);
|
|
132
|
+
const pendingCandidates = listCandidates('pending', cwd);
|
|
133
|
+
const textById = new Map();
|
|
134
|
+
for (const collection of [state.active_constraints, state.recent_decisions, state.known_traps, state.open_handoffs, state.plan_items, pendingCandidates]) {
|
|
135
|
+
for (const item of collection) {
|
|
136
|
+
textById.set(item.id, { text: item.text, created_at: item.created_at });
|
|
93
137
|
}
|
|
94
138
|
}
|
|
95
|
-
|
|
139
|
+
const counts = { constraints: 0, decisions: 0, traps: 0, handoffs: 0, plans: 0, pending_candidates: 0, total: 0 };
|
|
140
|
+
const sectionToCountKey = {
|
|
141
|
+
constraint: 'constraints',
|
|
142
|
+
decision: 'decisions',
|
|
143
|
+
trap: 'traps',
|
|
144
|
+
handoff: 'handoffs',
|
|
145
|
+
plan: 'plans',
|
|
146
|
+
candidate: 'pending_candidates',
|
|
147
|
+
};
|
|
148
|
+
const changedItems = [];
|
|
149
|
+
for (const [itemId, event] of latestByItem) {
|
|
150
|
+
const section = EVENT_SECTION_BY_ITEM_TYPE[event.item_type];
|
|
151
|
+
const current = textById.get(itemId);
|
|
152
|
+
counts[sectionToCountKey[section]] += 1;
|
|
153
|
+
counts.total += 1;
|
|
154
|
+
changedItems.push({
|
|
155
|
+
section,
|
|
156
|
+
id: itemId,
|
|
157
|
+
text: current?.text ?? event.summary ?? `(${EVENT_ACTION_LABEL[event.action]} — no longer in state)`,
|
|
158
|
+
created_at: event.ts,
|
|
159
|
+
action: EVENT_ACTION_LABEL[event.action],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
changedItems.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
163
|
+
const oldest = events[0]?.ts;
|
|
164
|
+
return {
|
|
165
|
+
since: oldest,
|
|
166
|
+
source: 'event_cursor',
|
|
167
|
+
summary: buildContextDiffSummary(counts),
|
|
168
|
+
counts,
|
|
169
|
+
changed_items: options.includeItems === false ? undefined : changedItems,
|
|
170
|
+
event_summary: buildNotificationSummary(events),
|
|
171
|
+
unseen_event_count: events.length,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** Hard cap on arrival-digest items — the digest informs, it must not drown. */
|
|
175
|
+
const ARRIVAL_DIGEST_MAX_ITEMS = 12;
|
|
176
|
+
const ARRIVAL_DIGEST_MAX_HANDOFFS = 3;
|
|
177
|
+
/**
|
|
178
|
+
* First-contact digest for an agent arriving on a store it has never read.
|
|
179
|
+
* Curated active state (constraints, traps, in-progress plans, latest open
|
|
180
|
+
* handoffs) instead of an event-log replay; seeds the agent's cursor at the
|
|
181
|
+
* end of the log so the next diff is incremental. Returns undefined only when
|
|
182
|
+
* there is nothing to say (empty store with no event history) — that case is
|
|
183
|
+
* the bootstrap hint's job, not the diff's.
|
|
184
|
+
*/
|
|
185
|
+
export function buildArrivalDigest(agent, cwd, options = {}) {
|
|
186
|
+
const skippedBytes = seedCursorToEnd(agent, cwd);
|
|
187
|
+
const state = loadState(cwd);
|
|
188
|
+
const constraints = state.active_constraints.filter((item) => (item.status ?? 'active') === 'active');
|
|
189
|
+
const traps = state.known_traps.filter((item) => (item.status ?? 'active') === 'active');
|
|
190
|
+
const plans = state.plan_items.filter((item) => item.status === 'in_progress');
|
|
191
|
+
const handoffs = state.open_handoffs
|
|
192
|
+
.filter((item) => (item.status ?? 'open') === 'open')
|
|
193
|
+
.sort((a, b) => b.created_at.localeCompare(a.created_at))
|
|
194
|
+
.slice(0, ARRIVAL_DIGEST_MAX_HANDOFFS);
|
|
195
|
+
const counts = {
|
|
196
|
+
constraints: constraints.length,
|
|
197
|
+
decisions: 0,
|
|
198
|
+
traps: traps.length,
|
|
199
|
+
handoffs: handoffs.length,
|
|
200
|
+
plans: plans.length,
|
|
201
|
+
pending_candidates: 0,
|
|
202
|
+
total: constraints.length + traps.length + plans.length + handoffs.length,
|
|
203
|
+
};
|
|
204
|
+
if (counts.total === 0 && skippedBytes === 0) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
const changedItems = [
|
|
208
|
+
...constraints.map((item) => toChangedItem('constraint', item)),
|
|
209
|
+
...traps.map((item) => toChangedItem('trap', item)),
|
|
210
|
+
...plans.map((item) => toChangedItem('plan', item)),
|
|
211
|
+
...handoffs.map((item) => toChangedItem('handoff', item)),
|
|
212
|
+
].slice(0, ARRIVAL_DIGEST_MAX_ITEMS);
|
|
213
|
+
const skippedKb = Math.round(skippedBytes / 1024);
|
|
214
|
+
const stateSummary = counts.total > 0 ? buildContextDiffSummary(counts) : 'no active items';
|
|
215
|
+
const summary = `First contact — arrival digest: ${stateSummary}. Event history skipped (${skippedKb} KB); cursor initialized at log end, future diffs are incremental.`;
|
|
216
|
+
return {
|
|
217
|
+
source: 'arrival_digest',
|
|
218
|
+
summary,
|
|
219
|
+
counts,
|
|
220
|
+
changed_items: options.includeItems === false ? undefined : changedItems,
|
|
221
|
+
};
|
|
96
222
|
}
|
|
97
223
|
export function buildContextDiffSummary(counts) {
|
|
98
224
|
if (counts.total === 0) {
|
|
@@ -107,6 +233,8 @@ export function buildContextDiffSummary(counts) {
|
|
|
107
233
|
parts.push(`${counts.traps} trap${counts.traps > 1 ? 's' : ''}`);
|
|
108
234
|
if (counts.handoffs > 0)
|
|
109
235
|
parts.push(`${counts.handoffs} handoff${counts.handoffs > 1 ? 's' : ''}`);
|
|
236
|
+
if (counts.plans > 0)
|
|
237
|
+
parts.push(`${counts.plans} plan${counts.plans > 1 ? 's' : ''}`);
|
|
110
238
|
if (counts.pending_candidates > 0)
|
|
111
239
|
parts.push(`${counts.pending_candidates} pending candidate${counts.pending_candidates > 1 ? 's' : ''}`);
|
|
112
240
|
return parts.join(', ');
|
package/dist/core/context.js
CHANGED
|
@@ -7,7 +7,7 @@ import { checkBrainclawInstallableUpdate, renderBrainclawInstallableUpdateNotice
|
|
|
7
7
|
import { loadConfig } from './config.js';
|
|
8
8
|
import { loadCurrentSession, loadAllSessions } from './identity.js';
|
|
9
9
|
import { resolveCrossProjectLinks, loadCrossProjectState } from './cross-project.js';
|
|
10
|
-
import { buildContextDiff } from './context-diff.js';
|
|
10
|
+
import { buildContextDiff, buildContextDiffFromEvents } from './context-diff.js';
|
|
11
11
|
import { resolveContextStoreCwd, resolveStoreChain } from './store-resolution.js';
|
|
12
12
|
import { findAgentIdentityByName, resolveCurrentAgentIdentity } from './agent-registry.js';
|
|
13
13
|
import { hasReusableBootstrapProfile, runBootstrapProfile, selectDerivedSignals } from './bootstrap.js';
|
|
@@ -17,11 +17,13 @@ import { getVisibleMemoryVersion } from './freshness.js';
|
|
|
17
17
|
import { resolveCurrentHostId } from './host.js';
|
|
18
18
|
import { inferProjectFromTarget, loadInstructions, resolveInstructions } from './instructions.js';
|
|
19
19
|
import { buildCurrentAgentResumeSummary, buildReputationRankingLookup } from './reputation.js';
|
|
20
|
+
import { buildMemoryLifecycleMetricsForState, getLifecycleStats } from './memory-lifecycle.js';
|
|
20
21
|
import { loadState } from './state.js';
|
|
21
22
|
import { readAuditLog } from './audit.js';
|
|
22
23
|
import { listCandidates } from './candidates.js';
|
|
23
24
|
import { listClaims, isClaimExpired, assessClaimLiveness } from './claims.js';
|
|
24
25
|
import { listAssignments } from './assignments.js';
|
|
26
|
+
import { reconcileOrphanedLoopAssignmentsFromList } from './assignment-reconciler.js';
|
|
25
27
|
import { listRuntimeNotes } from './runtime.js';
|
|
26
28
|
import { isTrapActive, listOperationalTraps } from './traps.js';
|
|
27
29
|
import { buildEstimationReport } from '../commands/estimation-report.js';
|
|
@@ -102,6 +104,16 @@ export function buildContext(options = {}) {
|
|
|
102
104
|
host_id: c.host_id,
|
|
103
105
|
session_id: c.session_id,
|
|
104
106
|
},
|
|
107
|
+
lifecycle: {
|
|
108
|
+
entity: 'constraint',
|
|
109
|
+
created_at: c.created_at,
|
|
110
|
+
last_confirmed_at: c.last_confirmed_at,
|
|
111
|
+
last_infirmed_at: c.last_infirmed_at,
|
|
112
|
+
confirmation_count: c.confirmation_count,
|
|
113
|
+
infirmation_count: c.infirmation_count,
|
|
114
|
+
saved_me_count: c.saved_me_count,
|
|
115
|
+
misled_me_count: c.misled_me_count,
|
|
116
|
+
},
|
|
105
117
|
});
|
|
106
118
|
}
|
|
107
119
|
for (const d of state.recent_decisions) {
|
|
@@ -122,6 +134,16 @@ export function buildContext(options = {}) {
|
|
|
122
134
|
host_id: d.host_id,
|
|
123
135
|
session_id: d.session_id,
|
|
124
136
|
},
|
|
137
|
+
lifecycle: {
|
|
138
|
+
entity: 'decision',
|
|
139
|
+
created_at: d.created_at,
|
|
140
|
+
last_confirmed_at: d.last_confirmed_at,
|
|
141
|
+
last_infirmed_at: d.last_infirmed_at,
|
|
142
|
+
confirmation_count: d.confirmation_count,
|
|
143
|
+
infirmation_count: d.infirmation_count,
|
|
144
|
+
saved_me_count: d.saved_me_count,
|
|
145
|
+
misled_me_count: d.misled_me_count,
|
|
146
|
+
},
|
|
125
147
|
});
|
|
126
148
|
}
|
|
127
149
|
for (const t of state.known_traps.filter((trap) => isTrapActive(trap))) {
|
|
@@ -142,6 +164,16 @@ export function buildContext(options = {}) {
|
|
|
142
164
|
host_id: t.host_id,
|
|
143
165
|
session_id: t.session_id,
|
|
144
166
|
},
|
|
167
|
+
lifecycle: {
|
|
168
|
+
entity: 'trap',
|
|
169
|
+
created_at: t.created_at,
|
|
170
|
+
last_confirmed_at: t.last_confirmed_at,
|
|
171
|
+
last_infirmed_at: t.last_infirmed_at,
|
|
172
|
+
confirmation_count: t.confirmation_count,
|
|
173
|
+
infirmation_count: t.infirmation_count,
|
|
174
|
+
saved_me_count: t.saved_me_count,
|
|
175
|
+
misled_me_count: t.misled_me_count,
|
|
176
|
+
},
|
|
145
177
|
});
|
|
146
178
|
}
|
|
147
179
|
for (const trap of listOperationalTraps({ hostId: options.host, includeAllHosts: options.allHosts }, contextCwd).filter((entry) => isTrapActive(entry))) {
|
|
@@ -371,6 +403,21 @@ export function buildContext(options = {}) {
|
|
|
371
403
|
item.reasons = uniqueReasons([...item.reasons, `reputation signal:+${trustBonus.toFixed(2)}`]);
|
|
372
404
|
}
|
|
373
405
|
}
|
|
406
|
+
// pln#544 — memory lifecycle: items confirmed-recent + reinforced rise;
|
|
407
|
+
// stale-unconfirmed and explicitly-infirmed sink in the same ranking.
|
|
408
|
+
// Only items we tagged with a lifecycle payload (primary-store decision/
|
|
409
|
+
// constraint/trap) carry a delta — parent-store items rank by other signals.
|
|
410
|
+
if (item.score >= 0 && item.lifecycle) {
|
|
411
|
+
const stats = getLifecycleStats(item.lifecycle);
|
|
412
|
+
if (stats.ranking_delta !== 0) {
|
|
413
|
+
item.score += stats.ranking_delta;
|
|
414
|
+
const sign = stats.ranking_delta >= 0 ? '+' : '';
|
|
415
|
+
item.reasons = uniqueReasons([
|
|
416
|
+
...item.reasons,
|
|
417
|
+
`lifecycle ${stats.classification}:${sign}${stats.ranking_delta}`,
|
|
418
|
+
]);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
374
421
|
// Layer 3: boost machine-scoped items for broader visibility (+1)
|
|
375
422
|
if (item.score >= 0) {
|
|
376
423
|
const itemScope = item.scope;
|
|
@@ -393,7 +440,10 @@ export function buildContext(options = {}) {
|
|
|
393
440
|
runtimeNotes,
|
|
394
441
|
pendingCandidates: listCandidates('pending', contextCwd),
|
|
395
442
|
});
|
|
396
|
-
|
|
443
|
+
// Density reflects what the store HAS, not what the char budget keeps:
|
|
444
|
+
// classify pre-budget so a tight budget_tokens on a rich store never
|
|
445
|
+
// misreads as 'low' and triggers a bootstrap re-scan (pln#542 interaction).
|
|
446
|
+
const memoryDensity = classifyMemoryDensity(ranked.length);
|
|
397
447
|
const bootstrapEnabled = options.bootstrap !== false;
|
|
398
448
|
const testMode = process.env.BRAINCLAW_TEST_MODE === '1';
|
|
399
449
|
let bootstrapAvailable = false;
|
|
@@ -445,7 +495,19 @@ export function buildContext(options = {}) {
|
|
|
445
495
|
const currentSession = loadCurrentSession(contextCwd);
|
|
446
496
|
if (currentAgentIdentity || agent) {
|
|
447
497
|
const claimPlanIds = new Set(myClaims.map((c) => c.plan_id).filter(Boolean));
|
|
448
|
-
|
|
498
|
+
// pln#563 layer B: converge review-loop assignments orphaned in
|
|
499
|
+
// offered/accepted/started whose loop is already terminal, before listing
|
|
500
|
+
// active work. Reuse one assignment scan for reconcile and rendering;
|
|
501
|
+
// best-effort (swallow errors); steady state is zero writes.
|
|
502
|
+
const allAssignments = listAssignments(contextCwd);
|
|
503
|
+
let reconciledAssignmentIds = new Set();
|
|
504
|
+
try {
|
|
505
|
+
reconciledAssignmentIds = new Set(reconcileOrphanedLoopAssignmentsFromList(allAssignments, contextCwd));
|
|
506
|
+
}
|
|
507
|
+
catch { /* read path must not break on reconcile */ }
|
|
508
|
+
const activeAssignments = allAssignments.filter((assignment) => assignment.agent === agentName
|
|
509
|
+
&& !reconciledAssignmentIds.has(assignment.id)
|
|
510
|
+
&& !['completed', 'failed', 'cancelled', 'expired', 'rerouted', 'timed_out'].includes(assignment.status));
|
|
449
511
|
const inProgressPlans = state.plan_items.filter((p) => p.status === 'in_progress' &&
|
|
450
512
|
(p.assignee === agentName || claimPlanIds.has(p.id)));
|
|
451
513
|
if (myClaims.length > 0 || activeAssignments.length > 0 || inProgressPlans.length > 0) {
|
|
@@ -524,13 +586,46 @@ export function buildContext(options = {}) {
|
|
|
524
586
|
let staleWarnings;
|
|
525
587
|
try {
|
|
526
588
|
const pendingCandidatesForStaleness = listCandidates('pending', contextCwd);
|
|
527
|
-
|
|
528
|
-
|
|
589
|
+
// pln#564 step A — reuse the runtime notes already loaded above (line ~316)
|
|
590
|
+
// instead of a second unfiltered full scan of the runtime-note tree. On a
|
|
591
|
+
// store with thousands of notes that 2nd scan dominated buildContext cost
|
|
592
|
+
// (readAgentNotes ~11s / 6166 files, trp_439fec51). The earlier list is the
|
|
593
|
+
// broader one (honours options.host/allHosts), so staleness is at least as
|
|
594
|
+
// complete as before.
|
|
595
|
+
const runtimeNotesForStaleness = runtimeNotes;
|
|
596
|
+
const staleReport = detectStaleness(state.plan_items, state.known_traps, state.open_handoffs, pendingCandidatesForStaleness, Date.now(), runtimeNotesForStaleness,
|
|
597
|
+
// pln#557 step 2 — dead related_paths detection. Surfaces "confident
|
|
598
|
+
// but wrong" memory (paths deleted by a refactor) in stale_warnings,
|
|
599
|
+
// which both the steady-state context and the arrival digest carry.
|
|
600
|
+
{
|
|
601
|
+
decisions: state.recent_decisions,
|
|
602
|
+
constraints: state.active_constraints,
|
|
603
|
+
projectRoot: contextCwd,
|
|
604
|
+
});
|
|
529
605
|
if (staleReport.warnings.length > 0) {
|
|
530
606
|
staleWarnings = staleReport.warnings.slice(0, 5);
|
|
531
607
|
}
|
|
532
608
|
}
|
|
533
609
|
catch { /* non-fatal */ }
|
|
610
|
+
// pln#544 — memory lifecycle metrics. Best-effort: build once, surface in
|
|
611
|
+
// the result + thread to workflow_hints so the curation hint can quote the
|
|
612
|
+
// oldest unconfirmed id. Empty stores still pass through (total_items=0).
|
|
613
|
+
let memoryLifecycleMetrics;
|
|
614
|
+
try {
|
|
615
|
+
memoryLifecycleMetrics = buildMemoryLifecycleMetricsForState(state);
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
memoryLifecycleMetrics = {
|
|
619
|
+
total_items: 0,
|
|
620
|
+
confirmed_items: 0,
|
|
621
|
+
confirmed_ratio: 0,
|
|
622
|
+
average_age_days: 0,
|
|
623
|
+
total_saved_me: 0,
|
|
624
|
+
total_misled_me: 0,
|
|
625
|
+
total_infirmed_active: 0,
|
|
626
|
+
recall_precision_proxy: 0,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
534
629
|
const result = {
|
|
535
630
|
context_schema: CONTEXT_SCHEMA_VERSION,
|
|
536
631
|
profile,
|
|
@@ -552,13 +647,19 @@ export function buildContext(options = {}) {
|
|
|
552
647
|
execution_context: executionContext,
|
|
553
648
|
agent_tooling: agentTooling,
|
|
554
649
|
scoped_activity: scopedActivity,
|
|
650
|
+
// "What's new" — explicit session reference keeps the timestamp diff;
|
|
651
|
+
// otherwise the per-agent event-log cursor is the converged default
|
|
652
|
+
// (covers status transitions, no store-global marker). Note: the cursor
|
|
653
|
+
// read marks the surfaced events as seen for this agent.
|
|
555
654
|
context_diff: options.sinceSession
|
|
556
655
|
? buildContextDiff({
|
|
557
656
|
session: options.sinceSession,
|
|
558
657
|
cwd: contextCwd,
|
|
559
658
|
includeItems: true,
|
|
560
659
|
})
|
|
561
|
-
:
|
|
660
|
+
: agent
|
|
661
|
+
? buildContextDiffFromEvents(agent, contextCwd, { includeItems: true })
|
|
662
|
+
: undefined,
|
|
562
663
|
resolved_instructions: resolvedInstructions,
|
|
563
664
|
resume_summary: resumeSummary,
|
|
564
665
|
open_work: openWork,
|
|
@@ -578,8 +679,9 @@ export function buildContext(options = {}) {
|
|
|
578
679
|
active_project: findActiveProjectInChain(contextCwd, storeChain),
|
|
579
680
|
cross_project_items: crossProjectItems.length > 0 ? crossProjectItems : undefined,
|
|
580
681
|
claim_conflicts: detectClaimConflicts(myClaims, otherActiveClaims),
|
|
581
|
-
workflow_hints: buildWorkflowHints(myClaims, openWork, state.plan_items),
|
|
682
|
+
workflow_hints: buildWorkflowHints(myClaims, openWork, state.plan_items, memoryLifecycleMetrics),
|
|
582
683
|
stale_warnings: staleWarnings,
|
|
684
|
+
memory_lifecycle: memoryLifecycleMetrics.total_items > 0 ? memoryLifecycleMetrics : undefined,
|
|
583
685
|
selected,
|
|
584
686
|
};
|
|
585
687
|
if (options.digest) {
|
|
@@ -972,6 +1074,7 @@ export function renderContextPromptTemplate(result, compact = false) {
|
|
|
972
1074
|
lines.push(` decisions: ${result.context_diff.counts.decisions}`);
|
|
973
1075
|
lines.push(` traps: ${result.context_diff.counts.traps}`);
|
|
974
1076
|
lines.push(` handoffs: ${result.context_diff.counts.handoffs}`);
|
|
1077
|
+
lines.push(` plans: ${result.context_diff.counts.plans}`);
|
|
975
1078
|
lines.push(` pending_candidates: ${result.context_diff.counts.pending_candidates}`);
|
|
976
1079
|
lines.push(` total: ${result.context_diff.counts.total}`);
|
|
977
1080
|
}
|
|
@@ -1724,7 +1827,7 @@ function findActiveProjectInChain(contextCwd, _storeChain) {
|
|
|
1724
1827
|
return undefined;
|
|
1725
1828
|
}
|
|
1726
1829
|
// --- Workflow hints ---
|
|
1727
|
-
function buildWorkflowHints(myClaims, openWork, plans) {
|
|
1830
|
+
function buildWorkflowHints(myClaims, openWork, plans, memoryLifecycle) {
|
|
1728
1831
|
const hints = [];
|
|
1729
1832
|
// No claims — suggest claiming before editing
|
|
1730
1833
|
if (myClaims.length === 0) {
|
|
@@ -1744,6 +1847,24 @@ function buildWorkflowHints(myClaims, openWork, plans) {
|
|
|
1744
1847
|
hints.push(`${unclaimedInProgress.length} in-progress plan(s) without a claim — consider claiming the scope you're editing`);
|
|
1745
1848
|
}
|
|
1746
1849
|
}
|
|
1850
|
+
// pln#544 — memory curation surfacing. Pick the strongest signal:
|
|
1851
|
+
// - an oldest unconfirmed item past the half-life-ish horizon, OR
|
|
1852
|
+
// - an item that an agent flagged as misleading (still active).
|
|
1853
|
+
if (memoryLifecycle && memoryLifecycle.total_items > 0) {
|
|
1854
|
+
if (memoryLifecycle.total_infirmed_active > 0) {
|
|
1855
|
+
hints.push(`${memoryLifecycle.total_infirmed_active} active memory item(s) were infirmed after their last confirmation — review with bclaw_find(status:'active') and consider archiving via bclaw_transition`);
|
|
1856
|
+
}
|
|
1857
|
+
else if (memoryLifecycle.oldest_unconfirmed_id
|
|
1858
|
+
&& memoryLifecycle.oldest_unconfirmed_age_days !== undefined
|
|
1859
|
+
&& memoryLifecycle.oldest_unconfirmed_age_days >= 30) {
|
|
1860
|
+
const ent = memoryLifecycle.oldest_unconfirmed_entity ?? 'item';
|
|
1861
|
+
hints.push(`Oldest unconfirmed ${ent} ${memoryLifecycle.oldest_unconfirmed_id} is ${memoryLifecycle.oldest_unconfirmed_age_days}d old — confirm or retire when you next encounter it`);
|
|
1862
|
+
}
|
|
1863
|
+
else if (memoryLifecycle.confirmed_ratio < 0.2
|
|
1864
|
+
&& memoryLifecycle.total_items >= 10) {
|
|
1865
|
+
hints.push(`Memory health: only ${Math.round(memoryLifecycle.confirmed_ratio * 100)}% of ${memoryLifecycle.total_items} item(s) have been confirmed — start attesting in passing to age memory honestly`);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1747
1868
|
return hints.length > 0 ? hints : undefined;
|
|
1748
1869
|
}
|
|
1749
1870
|
//# sourceMappingURL=context.js.map
|
|
@@ -16,6 +16,7 @@ import { loadAllSessions } from './identity.js';
|
|
|
16
16
|
import { countActionable } from './messaging.js';
|
|
17
17
|
import { listCandidates } from './candidates.js';
|
|
18
18
|
import { pullSignalsFromLinkedProjects } from './federation-transport.js';
|
|
19
|
+
import { isObserverMode } from './observer-mode.js';
|
|
19
20
|
export function buildCoordinationSnapshot(options = {}) {
|
|
20
21
|
const config = loadConfig(options.cwd);
|
|
21
22
|
const state = loadState(options.cwd);
|
|
@@ -58,8 +59,13 @@ export function buildCoordinationSnapshot(options = {}) {
|
|
|
58
59
|
const filteredHandoffs = agent
|
|
59
60
|
? openHandoffs.filter((h) => (!project || !h.project || h.project === project) && (h.to === agent || h.from === agent))
|
|
60
61
|
: (project ? openHandoffs.filter((h) => !h.project || h.project === project) : openHandoffs);
|
|
61
|
-
// perf.2: auto-acknowledge shown handoffs
|
|
62
|
-
|
|
62
|
+
// perf.2: auto-acknowledge shown handoffs.
|
|
63
|
+
// Observer mode (BRAINCLAW_OBSERVER=1) suppresses this — a dashboard reading
|
|
64
|
+
// the board must never mutate the store it observes. The 2026-06-10 lock
|
|
65
|
+
// storm was caused by the VS Code extension polling kind='board' (which sets
|
|
66
|
+
// autoAcknowledge=true) and triggering persistState → full store rewrite +
|
|
67
|
+
// git commit on every refresh.
|
|
68
|
+
if (options.autoAcknowledge && filteredHandoffs.length > 0 && !isObserverMode()) {
|
|
63
69
|
const toAckIds = new Set(filteredHandoffs.map((h) => h.id));
|
|
64
70
|
let changed = false;
|
|
65
71
|
for (const h of state.open_handoffs) {
|
|
@@ -88,7 +94,7 @@ export function buildCoordinationSnapshot(options = {}) {
|
|
|
88
94
|
: filteredClaims,
|
|
89
95
|
active_assignments: (agent
|
|
90
96
|
? listAssignments(options.cwd, { agent })
|
|
91
|
-
: listAssignments(options.cwd)).filter((assignment) => !['completed', 'failed', 'cancelled', 'expired', 'rerouted'].includes(assignment.status) &&
|
|
97
|
+
: listAssignments(options.cwd)).filter((assignment) => !['completed', 'failed', 'cancelled', 'expired', 'rerouted', 'timed_out'].includes(assignment.status) &&
|
|
92
98
|
(!project || !assignment.plan_id || filteredPlans.some((plan) => plan.id === assignment.plan_id))),
|
|
93
99
|
active_runs: (agent
|
|
94
100
|
? listAgentRuns(options.cwd, { agent })
|
|
@@ -229,6 +235,19 @@ function buildOtherAgentsSummary(claims, notes, currentAgent, cwd) {
|
|
|
229
235
|
const result = [...agentMap.values()];
|
|
230
236
|
return result.length > 0 ? result : undefined;
|
|
231
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Lightweight cross-project snapshot — linked_projects + incoming_signals only.
|
|
240
|
+
* Used by the VS Code extension's SYSTEM section so it does not have to fetch
|
|
241
|
+
* the full coordination snapshot (pln#558 step 3). Loads two linked-project
|
|
242
|
+
* states plus the incoming-signals scan; never builds the agent/handoff/claim
|
|
243
|
+
* summaries.
|
|
244
|
+
*/
|
|
245
|
+
export function buildCrossProjectSnapshot(cwd) {
|
|
246
|
+
return {
|
|
247
|
+
linked_projects: buildLinkedProjectsSummary(cwd),
|
|
248
|
+
incoming_signals: buildIncomingSignalsSummary(cwd),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
232
251
|
function buildLinkedProjectsSummary(cwd) {
|
|
233
252
|
const links = resolveCrossProjectLinks(cwd);
|
|
234
253
|
if (links.length === 0)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
name: doctor
|
|
2
|
-
description: Run diagnostics and fix issues automatically
|
|
3
|
-
trust_level: trusted
|
|
4
|
-
trigger: manual
|
|
5
|
-
prompt: >-
|
|
6
|
-
Run bclaw_doctor, analyze the findings, and fix what can be fixed
|
|
7
|
-
automatically. For issues requiring human judgment, create plans.
|
|
8
|
-
invoke: codex exec --full-auto "{prompt}"
|
|
9
|
-
tags:
|
|
10
|
-
- diagnostics
|
|
11
|
-
- built-in
|
|
1
|
+
name: doctor
|
|
2
|
+
description: Run diagnostics and fix issues automatically
|
|
3
|
+
trust_level: trusted
|
|
4
|
+
trigger: manual
|
|
5
|
+
prompt: >-
|
|
6
|
+
Run bclaw_doctor, analyze the findings, and fix what can be fixed
|
|
7
|
+
automatically. For issues requiring human judgment, create plans.
|
|
8
|
+
invoke: codex exec --full-auto "{prompt}"
|
|
9
|
+
tags:
|
|
10
|
+
- diagnostics
|
|
11
|
+
- built-in
|