brainclaw 1.21.0 → 1.23.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/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli/register-capture.js +15 -0
- package/dist/cli/register-cloud.js +63 -0
- package/dist/cli.js +2 -3
- package/dist/commands/cloud.js +198 -0
- package/dist/commands/loops-handlers.js +0 -1
- package/dist/commands/mcp-catalog.js +24 -256
- package/dist/commands/mcp-read-handlers.js +5 -1
- package/dist/commands/mcp-schemas.generated.js +811 -1
- package/dist/commands/mcp-write-coordination.js +16 -7
- package/dist/commands/mcp.js +45 -1
- package/dist/commands/memory-confirm.js +83 -0
- package/dist/commands/session-end.js +0 -102
- package/dist/commands/session-start.js +0 -23
- package/dist/commands/switch.js +24 -2
- package/dist/core/assignment-request-schema.js +112 -0
- package/dist/core/capture-schema.js +62 -0
- package/dist/core/claim-request-schema.js +72 -0
- package/dist/core/claims.js +0 -18
- package/dist/core/code-map/aggregate.js +36 -1
- package/dist/core/coordination.js +1 -3
- package/dist/core/federation-attestation.js +96 -0
- package/dist/core/federation-canonical.js +95 -0
- package/dist/core/federation-hpke.js +213 -0
- package/dist/core/federation-inbound.js +187 -0
- package/dist/core/federation-keyring.js +241 -0
- package/dist/core/federation-message.js +5 -5
- package/dist/core/federation-outbox-v2.js +125 -0
- package/dist/core/federation-pairing.js +213 -0
- package/dist/core/federation-projection.js +336 -0
- package/dist/core/federation-relay.js +223 -0
- package/dist/core/federation-state.js +270 -0
- package/dist/core/ids.js +5 -0
- package/dist/core/schema.js +0 -17
- package/dist/core/sequence-request-schema.js +93 -0
- package/dist/core/session-request-schema.js +90 -0
- package/dist/core/step-request-schema.js +112 -0
- package/dist/core/store-resolution.js +34 -5
- package/dist/core/warnings.js +37 -0
- package/dist/facts.js +9 -9
- package/dist/facts.json +8 -8
- package/docs/cli.md +70 -39
- package/docs/concepts/federation-v2-rfc.md +275 -0
- package/docs/index.md +1 -0
- package/docs/integrations/mcp.md +1 -1
- package/package.json +2 -2
- package/dist/cli/register-federation.js +0 -258
- package/dist/core/federation-cloud.js +0 -245
- package/dist/core/federation-outbox.js +0 -292
- package/dist/core/federation-signing.js +0 -115
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schémas zod des entrées de la famille SESSION — `bclaw_session_start` et
|
|
3
|
+
* `bclaw_session_end` (pln#599 batch 2, troisième famille composite).
|
|
4
|
+
*
|
|
5
|
+
* ── LA PARTICULARITÉ DE CETTE FAMILLE : AUCUN CHAMP REQUIS ────────────────────
|
|
6
|
+
* Les deux outils ont un `properties` fourni mais PAS de clé `required`. C'est délibéré et
|
|
7
|
+
* doit être préservé au bit près : `bclaw_session_start` sans argument est l'appel normal,
|
|
8
|
+
* et l'identité comme le contexte se résolvent depuis l'ambiance.
|
|
9
|
+
*
|
|
10
|
+
* zod n'émet `required` que s'il existe au moins un champ non-optionnel — donc marquer
|
|
11
|
+
* TOUS les champs `.optional()` reproduit exactement l'absence de la clé. C'est le
|
|
12
|
+
* pendant du piège inverse rencontré sur la famille séquence : là-bas un requis était
|
|
13
|
+
* devenu optionnel (assouplissement) ; ici, oublier un `.optional()` créerait un requis
|
|
14
|
+
* là où il n'y en avait aucun — un DURCISSEMENT qui casserait l'appel sans argument.
|
|
15
|
+
*
|
|
16
|
+
* ── CE QUI N'EST PAS RESSERRÉ, DÉLIBÉRÉMENT ───────────────────────────────────
|
|
17
|
+
* `contextProfile` et `contextFormat` énumèrent leurs valeurs dans leur description mais
|
|
18
|
+
* restent des chaînes libres : les profils sont extensibles côté produit, et un enum
|
|
19
|
+
* publié figerait cette extensibilité. `maintenanceMode` garde en revanche son enum,
|
|
20
|
+
* parce qu'il en avait déjà un.
|
|
21
|
+
*
|
|
22
|
+
* ── GARDE-FOU DE GÉNÉRATION ───────────────────────────────────────────────────
|
|
23
|
+
* zod émet `additionalProperties: false` d'office ; le générateur le retire À LA RACINE
|
|
24
|
+
* uniquement (cf. OPEN_SCHEMAS dans scripts/build-mcp-schemas.mjs). Le laisser durcirait
|
|
25
|
+
* la surface ; le retirer plus profond l'assouplirait.
|
|
26
|
+
*/
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
/** Identité de l'appelant — commune à toutes les familles migrées. */
|
|
29
|
+
const CallerIdentity = {
|
|
30
|
+
agent: z.string().describe('Agent name.').optional(),
|
|
31
|
+
agentId: z.string().describe('Registered agent id.').optional(),
|
|
32
|
+
};
|
|
33
|
+
export const SessionStartRequestSchema = z.object({
|
|
34
|
+
...CallerIdentity,
|
|
35
|
+
context: z.string().describe('Context target path.').optional(),
|
|
36
|
+
// Enum CONSERVÉ : il existait déjà dans le schéma manuel.
|
|
37
|
+
maintenanceMode: z
|
|
38
|
+
.enum(['fast', 'full'])
|
|
39
|
+
.describe('Maintenance mode. Default is full for explicit session-start calls; use fast to skip non-critical maintenance work.')
|
|
40
|
+
.optional(),
|
|
41
|
+
includeContext: z
|
|
42
|
+
.boolean()
|
|
43
|
+
.describe('Include project memory context in the response (equivalent to bclaw_get_context).')
|
|
44
|
+
.optional(),
|
|
45
|
+
includeBoard: z
|
|
46
|
+
.boolean()
|
|
47
|
+
.describe('Include agent board (plans, claims, handoffs) in the response (equivalent to bclaw_get_agent_board).')
|
|
48
|
+
.optional(),
|
|
49
|
+
// Chaîne libre : les profils sont extensibles, un enum publié figerait cette
|
|
50
|
+
// extensibilité et rejetterait un profil ajouté côté produit.
|
|
51
|
+
contextProfile: z
|
|
52
|
+
.string()
|
|
53
|
+
.describe('Context profile when includeContext is true: dev (default), dense, compact, copilot, quick, briefing, openclaw, ops, research. If unset, uses the agent default profile.')
|
|
54
|
+
.optional(),
|
|
55
|
+
contextFormat: z
|
|
56
|
+
.string()
|
|
57
|
+
.describe('Context format when includeContext is true: markdown, json, or template.')
|
|
58
|
+
.optional(),
|
|
59
|
+
});
|
|
60
|
+
export const SessionEndRequestSchema = z.object({
|
|
61
|
+
session: z.string().describe('Session ID.').optional(),
|
|
62
|
+
...CallerIdentity,
|
|
63
|
+
summary: z.string().describe('Session summary text.').optional(),
|
|
64
|
+
narrative: z
|
|
65
|
+
.string()
|
|
66
|
+
.describe('Free-text narrative of what happened in the session and why. Goes beyond the auto-generated commit list: "Tried X, failed because Y, pivoted to Z. Watch out for A."')
|
|
67
|
+
.optional(),
|
|
68
|
+
autoReflect: z.boolean().describe('Auto-reflect session notes as candidates.').optional(),
|
|
69
|
+
autoRelease: z
|
|
70
|
+
.boolean()
|
|
71
|
+
.describe('Auto-release any active claims at session end.')
|
|
72
|
+
.optional(),
|
|
73
|
+
reflectHandoff: z
|
|
74
|
+
.boolean()
|
|
75
|
+
.describe('Materialize an open handoff from git commits since session start.')
|
|
76
|
+
.optional(),
|
|
77
|
+
dispatchReview: z
|
|
78
|
+
.boolean()
|
|
79
|
+
.describe('When used with reflectHandoff, auto-dispatch a code review if the reflected handoff is reviewable.')
|
|
80
|
+
.optional(),
|
|
81
|
+
reviewer: z
|
|
82
|
+
.string()
|
|
83
|
+
.describe('Explicit reviewer for the reflected handoff review dispatch.')
|
|
84
|
+
.optional(),
|
|
85
|
+
reflect: z
|
|
86
|
+
.boolean()
|
|
87
|
+
.describe('Emit the dogfooding reflection prompt (project + your surfaces/skills/tools). Default true — pass false to suppress on a trivial session. Capture actionable findings via bclaw_quick_capture.')
|
|
88
|
+
.optional(),
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=session-request-schema.js.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schémas zod des entrées de la famille STEP — `bclaw_add_step`, `bclaw_update_step`,
|
|
3
|
+
* `bclaw_complete_step`, `bclaw_delete_step` (pln#599 batch 2, quatrième famille).
|
|
4
|
+
*
|
|
5
|
+
* ── CE QUE CETTE FAMILLE APPREND, ET QUI CORRIGE UNE RÈGLE TROP VITE GÉNÉRALISÉE ─
|
|
6
|
+
* Sur la famille séquence j'avais formulé la consigne « retirer `additionalProperties`
|
|
7
|
+
* À LA RACINE UNIQUEMENT », au motif que le sous-schéma d'item de lane le portait déjà
|
|
8
|
+
* dans sa version manuelle. C'était vrai LÀ, et faux comme règle générale.
|
|
9
|
+
*
|
|
10
|
+
* Ici, le sous-objet `data` de `bclaw_add_step` n'a PAS d'`additionalProperties` dans la
|
|
11
|
+
* version écrite à la main. Appliquer « racine uniquement » y laisserait donc le
|
|
12
|
+
* `additionalProperties: false` émis par zod — exactement le DURCISSEMENT que la règle
|
|
13
|
+
* était censée empêcher, réintroduit par la règle elle-même.
|
|
14
|
+
*
|
|
15
|
+
* La consigne réelle n'a jamais été « racine » : c'est « reproduire le schéma manuel au
|
|
16
|
+
* bit près ». Le générateur porte désormais une profondeur de retrait PAR SCHÉMA
|
|
17
|
+
* (cf. OPEN_SCHEMAS dans scripts/build-mcp-schemas.mjs) au lieu d'une règle globale.
|
|
18
|
+
*
|
|
19
|
+
* ── DEUX FORMES D'APPEL COEXISTENT, DÉLIBÉRÉMENT ──────────────────────────────
|
|
20
|
+
* `bclaw_add_step` accepte la forme canonique `{ planId, data: {...} }` ET la forme
|
|
21
|
+
* historique `{ planId, text, assignee }`. Les deux sont publiées ; supprimer la seconde
|
|
22
|
+
* du schéma casserait les appelants existants. `title` reste un alias de `text`.
|
|
23
|
+
*
|
|
24
|
+
* ── CE QUI N'EST PAS RESSERRÉ ─────────────────────────────────────────────────
|
|
25
|
+
* `status` reste une chaîne libre bien que ses cinq valeurs soient énumérées dans sa
|
|
26
|
+
* description : en faire un enum serait un rejet nouveau sur des appels aujourd'hui
|
|
27
|
+
* acceptés, donc une décision à part entière.
|
|
28
|
+
*/
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
/** Identité de l'appelant — commune à toutes les familles migrées. */
|
|
31
|
+
const CallerIdentity = {
|
|
32
|
+
agent: z.string().describe('Agent name.').optional(),
|
|
33
|
+
agentId: z.string().describe('Registered agent id.').optional(),
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Charge utile canonique d'un step. Ses champs sont TOUS optionnels et l'objet ne porte
|
|
37
|
+
* PAS d'`additionalProperties` — voir l'en-tête : c'est ce sous-objet qui a révélé que la
|
|
38
|
+
* profondeur de retrait devait être décidée par schéma.
|
|
39
|
+
*/
|
|
40
|
+
const AddStepDataSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
text: z.string().describe('Step description.').optional(),
|
|
43
|
+
title: z.string().describe('Alias for text.').optional(),
|
|
44
|
+
assignee: z.string().describe('Optional assignee.').optional(),
|
|
45
|
+
estimated_effort: z
|
|
46
|
+
.number()
|
|
47
|
+
.describe('Step-level estimate in minutes (pln#495). A duration string like "2h"/"30m" is also accepted and coerced.')
|
|
48
|
+
.optional(),
|
|
49
|
+
actual_effort: z
|
|
50
|
+
.string()
|
|
51
|
+
.describe('Step-level actual effort, free-form ("45m", "2h"), parsed when the estimation report runs.')
|
|
52
|
+
.optional(),
|
|
53
|
+
})
|
|
54
|
+
.describe('Canonical step payload: { text, title?, assignee? }. title is accepted as an alias for text.');
|
|
55
|
+
export const AddStepRequestSchema = z.object({
|
|
56
|
+
planId: z.string().describe('Plan item ID.'),
|
|
57
|
+
data: AddStepDataSchema.optional(),
|
|
58
|
+
// Forme HISTORIQUE, conservée : elle est publiée et des appelants s'en servent.
|
|
59
|
+
text: z.string().describe('Legacy top-level step description; prefer data.text.').optional(),
|
|
60
|
+
...CallerIdentity,
|
|
61
|
+
assignee: z
|
|
62
|
+
.string()
|
|
63
|
+
.describe('Legacy top-level optional assignee; prefer data.assignee.')
|
|
64
|
+
.optional(),
|
|
65
|
+
project: z
|
|
66
|
+
.string()
|
|
67
|
+
.describe('Optional: name (or path/basename) of a linked project to add the step in. Defaults to the current project. Same resolution as canonical-grammar tools — accepts cross_project_links and workspace store-chain children.')
|
|
68
|
+
.optional(),
|
|
69
|
+
});
|
|
70
|
+
export const UpdateStepRequestSchema = z.object({
|
|
71
|
+
planId: z.string().describe('Plan item ID.'),
|
|
72
|
+
stepId: z.string().describe('Step ID to update.'),
|
|
73
|
+
// Chaîne libre, PAS un enum : les cinq valeurs sont documentées, pas imposées.
|
|
74
|
+
status: z
|
|
75
|
+
.string()
|
|
76
|
+
.describe('New status: todo, in_progress, testing, done, blocked.')
|
|
77
|
+
.optional(),
|
|
78
|
+
text: z.string().describe('New step text.').optional(),
|
|
79
|
+
assignee: z.string().describe('New assignee (empty string to unassign).').optional(),
|
|
80
|
+
estimated_effort: z
|
|
81
|
+
.number()
|
|
82
|
+
.describe('Step-level estimate in minutes (pln#495); a duration string is also coerced.')
|
|
83
|
+
.optional(),
|
|
84
|
+
actual_effort: z
|
|
85
|
+
.string()
|
|
86
|
+
.describe('Step-level actual effort, free-form ("45m", "2h").')
|
|
87
|
+
.optional(),
|
|
88
|
+
...CallerIdentity,
|
|
89
|
+
project: z
|
|
90
|
+
.string()
|
|
91
|
+
.describe('Optional: name of a linked project to update the step in. Defaults to the current project.')
|
|
92
|
+
.optional(),
|
|
93
|
+
});
|
|
94
|
+
export const CompleteStepRequestSchema = z.object({
|
|
95
|
+
planId: z.string().describe('Plan item ID.'),
|
|
96
|
+
stepId: z.string().describe('Step ID to complete.'),
|
|
97
|
+
...CallerIdentity,
|
|
98
|
+
project: z
|
|
99
|
+
.string()
|
|
100
|
+
.describe('Optional: name of a linked project to complete the step in. Defaults to the current project.')
|
|
101
|
+
.optional(),
|
|
102
|
+
});
|
|
103
|
+
export const DeleteStepRequestSchema = z.object({
|
|
104
|
+
planId: z.string().describe('Plan item ID.'),
|
|
105
|
+
stepId: z.string().describe('Step ID to delete.'),
|
|
106
|
+
...CallerIdentity,
|
|
107
|
+
project: z
|
|
108
|
+
.string()
|
|
109
|
+
.describe('Optional: name of a linked project to delete the step from. Defaults to the current project.')
|
|
110
|
+
.optional(),
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=step-request-schema.js.map
|
|
@@ -102,11 +102,7 @@ export function resolveTargetStore(cwd = process.cwd(), target = 'local', option
|
|
|
102
102
|
export function resolveEffectiveCwd(options = {}) {
|
|
103
103
|
return resolveEffectiveCwdInfo(options).cwd;
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
* Resolve the effective cwd and explain which selector won. Use this for MCP
|
|
107
|
-
* facades that must echo their project scope to avoid silent cross-project reads.
|
|
108
|
-
*/
|
|
109
|
-
export function resolveEffectiveCwdInfo(options = {}) {
|
|
105
|
+
function resolveEffectiveCwdInner(options, observed) {
|
|
110
106
|
const baseCwd = path.resolve(options.baseCwd ?? process.cwd());
|
|
111
107
|
// 1. Explicit --cwd flag
|
|
112
108
|
if (options.explicitCwd) {
|
|
@@ -192,9 +188,18 @@ export function resolveEffectiveCwdInfo(options = {}) {
|
|
|
192
188
|
// A named id is an exact-file lookup, so the record IS the one asked for; the
|
|
193
189
|
// pid check covers the unnamed case. Anything else is a weak adoption.
|
|
194
190
|
if (opts?.requireStrongIdentity && !explicitSessionId && session.pid !== process.pid) {
|
|
191
|
+
// Observe avant de rejeter : une session d'un AUTRE processus qui designe un autre
|
|
192
|
+
// projet est exactement le cas ou une ecriture peut partir ailleurs en silence.
|
|
193
|
+
const weak = session.active_project;
|
|
194
|
+
if (weak && !observed.sessionProject)
|
|
195
|
+
observed.sessionProject = { path: weak.path, name: weak.name };
|
|
195
196
|
return undefined;
|
|
196
197
|
}
|
|
197
198
|
const sp = session.active_project;
|
|
199
|
+
// Retenu AVANT le controle d'adoption : un record trouvable qui designe un projet
|
|
200
|
+
// compte comme observation meme quand il n'est pas retenu.
|
|
201
|
+
if (sp && !observed.sessionProject)
|
|
202
|
+
observed.sessionProject = { path: sp.path, name: sp.name };
|
|
198
203
|
if (sp && fs.existsSync(path.join(sp.path, MEMORY_DIR, 'config.yaml'))) {
|
|
199
204
|
return { cwd: sp.path, active_source: 'session', resolved_project: { path: sp.path, name: sp.name } };
|
|
200
205
|
}
|
|
@@ -287,6 +292,30 @@ export function resolveEffectiveCwdInfo(options = {}) {
|
|
|
287
292
|
// 7. Default
|
|
288
293
|
return { cwd: anchorCwd, active_source: 'cwd', resolved_project: projectInfo(anchorCwd) };
|
|
289
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Point d'entree unique de la resolution (pln#648 SUITE d).
|
|
297
|
+
*
|
|
298
|
+
* Enrichit le verdict d'un signal de divergence quand un record de session trouvable
|
|
299
|
+
* designait un AUTRE projet que celui retenu. Le calcul est purement local : la sonde a
|
|
300
|
+
* deja lu le record, aucune lecture disque n'est ajoutee.
|
|
301
|
+
*/
|
|
302
|
+
export function resolveEffectiveCwdInfo(options = {}) {
|
|
303
|
+
const observed = {};
|
|
304
|
+
const result = resolveEffectiveCwdInner(options, observed);
|
|
305
|
+
const seen = observed.sessionProject;
|
|
306
|
+
if (!seen || result.active_source === 'session')
|
|
307
|
+
return result;
|
|
308
|
+
if (path.resolve(seen.path) === path.resolve(result.cwd))
|
|
309
|
+
return result;
|
|
310
|
+
return {
|
|
311
|
+
...result,
|
|
312
|
+
session_divergence: {
|
|
313
|
+
session_project_path: seen.path,
|
|
314
|
+
session_project_name: seen.name,
|
|
315
|
+
resolved_via: result.active_source,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
290
319
|
function projectInfo(cwd) {
|
|
291
320
|
try {
|
|
292
321
|
const config = loadConfig(cwd);
|
package/dist/core/warnings.js
CHANGED
|
@@ -48,6 +48,43 @@ export function pushStructuredWarning(warnings, details, input) {
|
|
|
48
48
|
// Each owns its recovery path, which is the entire point of the structured
|
|
49
49
|
// channel: `scope_already_claimed` used to be a dead-end string; now it names
|
|
50
50
|
// the two calls that resolve it.
|
|
51
|
+
/**
|
|
52
|
+
* `autoExecute: true` sur `intent='consult'` — un no-op, dit sur le canal STRUCTURE
|
|
53
|
+
* (pln#626 phase 3).
|
|
54
|
+
*
|
|
55
|
+
* POURQUOI PAS UNE ERREUR DURE. Le plan laissait le choix entre refuser et implementer un
|
|
56
|
+
* vrai « consult run ». Refuser casserait des appelants existants pour un drapeau qui n'a
|
|
57
|
+
* jamais rien fait, et l'implementer est une decision produit — le plan lui-meme penche
|
|
58
|
+
* pour le spawn sur les cibles spawn-only, ce qui depasse une correction de surface.
|
|
59
|
+
*
|
|
60
|
+
* POURQUOI PAS SEULEMENT UN TEXTE. Phase 1 avait deja pousse un avertissement en texte
|
|
61
|
+
* libre, ce qui vaut mieux que le silence mais reste illisible pour une machine : un agent
|
|
62
|
+
* ne peut pas brancher dessus. Le code structure rend le refus DETECTABLE, et la
|
|
63
|
+
* next_action nomme les deux chemins qui spawnent reellement.
|
|
64
|
+
*
|
|
65
|
+
* C'est la moitie de la phase 3 qui ne demande aucun arbitrage : rendre le no-op
|
|
66
|
+
* observable. L'autre moitie — refuser ou spawner — reste au produit.
|
|
67
|
+
*/
|
|
68
|
+
export function consultAutoExecuteNoOpWarning() {
|
|
69
|
+
return {
|
|
70
|
+
code: 'auto_execute_ignored_on_consult',
|
|
71
|
+
message: "autoExecute has no effect on intent='consult': consult delivers the RFC to the target "
|
|
72
|
+
+ 'inbox(es) only and never spawns an agent — targets pick it up via their own bclaw_work.',
|
|
73
|
+
data: { intent: 'consult', auto_execute_honored: false },
|
|
74
|
+
next_actions: [
|
|
75
|
+
{
|
|
76
|
+
tool: 'bclaw_dispatch',
|
|
77
|
+
args: { intent: 'execute' },
|
|
78
|
+
when: 'to actually spawn workers on a sequence lane',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
tool: 'bclaw_coordinate',
|
|
82
|
+
args: { intent: 'assign' },
|
|
83
|
+
when: 'to hand one scope to one agent and have it started',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
51
88
|
export function agentValidationFailedWarning(input) {
|
|
52
89
|
return {
|
|
53
90
|
code: 'agent_validation_failed',
|
package/dist/facts.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated by scripts/emit-site-facts.mjs at build time. Do not edit manually.
|
|
2
|
-
// Source: brainclaw v1.
|
|
2
|
+
// Source: brainclaw v1.23.0 on 2026-08-09T01:18:58.355Z
|
|
3
3
|
export const FACTS = {
|
|
4
|
-
"version": "1.
|
|
5
|
-
"generated_at": "2026-08-
|
|
4
|
+
"version": "1.23.0",
|
|
5
|
+
"generated_at": "2026-08-09T01:18:58.355Z",
|
|
6
6
|
"tools": {
|
|
7
7
|
"count": 67,
|
|
8
8
|
"published_count": 65,
|
|
@@ -474,7 +474,7 @@ export const FACTS = {
|
|
|
474
474
|
},
|
|
475
475
|
"bench": {
|
|
476
476
|
"schema": "brainclaw.bench.v1",
|
|
477
|
-
"generated_at": "2026-08-
|
|
477
|
+
"generated_at": "2026-08-09T01:18:56.213Z",
|
|
478
478
|
"node_version": "v24.18.0",
|
|
479
479
|
"platform": "linux-x64",
|
|
480
480
|
"repeats": 3,
|
|
@@ -483,7 +483,7 @@ export const FACTS = {
|
|
|
483
483
|
"name": "cold_onboard",
|
|
484
484
|
"volume": "empty",
|
|
485
485
|
"description": "fresh machine → init → first useful context. Baseline for time-to-first-value.",
|
|
486
|
-
"duration_ms_median":
|
|
486
|
+
"duration_ms_median": 77,
|
|
487
487
|
"payload_chars_median": 1640,
|
|
488
488
|
"payload_tokens_est_median": 410
|
|
489
489
|
},
|
|
@@ -491,15 +491,15 @@ export const FACTS = {
|
|
|
491
491
|
"name": "warm_work",
|
|
492
492
|
"volume": "medium",
|
|
493
493
|
"description": "bclaw_work consult over a real-shaped store (~200 plans / 500 handoffs / 450 claims).",
|
|
494
|
-
"duration_ms_median":
|
|
495
|
-
"payload_chars_median":
|
|
496
|
-
"payload_tokens_est_median":
|
|
494
|
+
"duration_ms_median": 131,
|
|
495
|
+
"payload_chars_median": 2626,
|
|
496
|
+
"payload_tokens_est_median": 657
|
|
497
497
|
},
|
|
498
498
|
{
|
|
499
499
|
"name": "first_edit",
|
|
500
500
|
"volume": "medium",
|
|
501
501
|
"description": "code_find + code_brief on the fresh-agent path (missing index, first touch).",
|
|
502
|
-
"duration_ms_median":
|
|
502
|
+
"duration_ms_median": 14,
|
|
503
503
|
"payload_chars_median": 499,
|
|
504
504
|
"payload_tokens_est_median": 125
|
|
505
505
|
}
|
package/dist/facts.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"generated_at": "2026-08-
|
|
2
|
+
"version": "1.23.0",
|
|
3
|
+
"generated_at": "2026-08-09T01:18:58.355Z",
|
|
4
4
|
"tools": {
|
|
5
5
|
"count": 67,
|
|
6
6
|
"published_count": 65,
|
|
@@ -472,7 +472,7 @@
|
|
|
472
472
|
},
|
|
473
473
|
"bench": {
|
|
474
474
|
"schema": "brainclaw.bench.v1",
|
|
475
|
-
"generated_at": "2026-08-
|
|
475
|
+
"generated_at": "2026-08-09T01:18:56.213Z",
|
|
476
476
|
"node_version": "v24.18.0",
|
|
477
477
|
"platform": "linux-x64",
|
|
478
478
|
"repeats": 3,
|
|
@@ -481,7 +481,7 @@
|
|
|
481
481
|
"name": "cold_onboard",
|
|
482
482
|
"volume": "empty",
|
|
483
483
|
"description": "fresh machine → init → first useful context. Baseline for time-to-first-value.",
|
|
484
|
-
"duration_ms_median":
|
|
484
|
+
"duration_ms_median": 77,
|
|
485
485
|
"payload_chars_median": 1640,
|
|
486
486
|
"payload_tokens_est_median": 410
|
|
487
487
|
},
|
|
@@ -489,15 +489,15 @@
|
|
|
489
489
|
"name": "warm_work",
|
|
490
490
|
"volume": "medium",
|
|
491
491
|
"description": "bclaw_work consult over a real-shaped store (~200 plans / 500 handoffs / 450 claims).",
|
|
492
|
-
"duration_ms_median":
|
|
493
|
-
"payload_chars_median":
|
|
494
|
-
"payload_tokens_est_median":
|
|
492
|
+
"duration_ms_median": 131,
|
|
493
|
+
"payload_chars_median": 2626,
|
|
494
|
+
"payload_tokens_est_median": 657
|
|
495
495
|
},
|
|
496
496
|
{
|
|
497
497
|
"name": "first_edit",
|
|
498
498
|
"volume": "medium",
|
|
499
499
|
"description": "code_find + code_brief on the fresh-agent path (missing index, first touch).",
|
|
500
|
-
"duration_ms_median":
|
|
500
|
+
"duration_ms_median": 14,
|
|
501
501
|
"payload_chars_median": 499,
|
|
502
502
|
"payload_tokens_est_median": 125
|
|
503
503
|
}
|
package/docs/cli.md
CHANGED
|
@@ -1749,46 +1749,9 @@ brainclaw push --remote origin --message "chore: push memory state" --json
|
|
|
1749
1749
|
|
|
1750
1750
|
## Federation
|
|
1751
1751
|
|
|
1752
|
-
The `federation` command group
|
|
1752
|
+
The v1 `brainclaw federation` command group and the whole cloud egress path (`app.brainclaw.dev` push/pull, `cloud_sync` config, `BRAINCLAW_CLOUD_*` env vars) were removed in wave 1 of dec#156 / pln#651. There is no migration: the v1 format is abandoned, not deprecated. The v2 federation surface will be introduced by pln#651 wave 3 alongside the pairing CLI (`brainclaw cloud connect`).
|
|
1753
1753
|
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
Push a test signal to the cloud. The signal is sent from the current project and agent to a target project or broadcast address.
|
|
1757
|
-
|
|
1758
|
-
| Option | Description |
|
|
1759
|
-
|---|---|
|
|
1760
|
-
| `--type <type>` | Signal type (default: `runtime_note`). Accepted values: `signal`, `handoff`, `candidate`, `runtime_note`, `board_snapshot` |
|
|
1761
|
-
| `--to-project <project>` | Target project name (default: `broadcast`) |
|
|
1762
|
-
| `--to-agent <agent>` | Target agent name |
|
|
1763
|
-
|
|
1764
|
-
```bash
|
|
1765
|
-
brainclaw federation push "Auth rollout complete" --to-project lodestar
|
|
1766
|
-
brainclaw federation push "Blocked on payments" --type runtime_note --to-agent copilot
|
|
1767
|
-
```
|
|
1768
|
-
|
|
1769
|
-
### `brainclaw federation pull`
|
|
1770
|
-
|
|
1771
|
-
Pull signals from the cloud inbox for the current agent.
|
|
1772
|
-
|
|
1773
|
-
| Option | Description |
|
|
1774
|
-
|---|---|
|
|
1775
|
-
| `--agent <name>` | Agent name to pull for (default: auto-detected) |
|
|
1776
|
-
| `--since <date>` | Only pull signals after this ISO date |
|
|
1777
|
-
| `--limit <n>` | Maximum number of signals to pull (default: 20) |
|
|
1778
|
-
|
|
1779
|
-
```bash
|
|
1780
|
-
brainclaw federation pull
|
|
1781
|
-
brainclaw federation pull --since 2026-04-01
|
|
1782
|
-
brainclaw federation pull --agent copilot --limit 50
|
|
1783
|
-
```
|
|
1784
|
-
|
|
1785
|
-
### `brainclaw federation status`
|
|
1786
|
-
|
|
1787
|
-
Check cloud federation configuration: shows the configured cloud URL, whether the API key is set, and pings the cloud health endpoint if configured.
|
|
1788
|
-
|
|
1789
|
-
```bash
|
|
1790
|
-
brainclaw federation status
|
|
1791
|
-
```
|
|
1754
|
+
Local cross-project federation (see below) is unaffected.
|
|
1792
1755
|
|
|
1793
1756
|
---
|
|
1794
1757
|
|
|
@@ -2244,4 +2207,72 @@ Show brainclaw context volume stats — tokens injected per agent and per MCP to
|
|
|
2244
2207
|
|---|---|
|
|
2245
2208
|
| `--json` | Output as JSON |
|
|
2246
2209
|
|
|
2210
|
+
### `brainclaw cloud status`
|
|
2211
|
+
|
|
2212
|
+
Show the federation v2 connection state for this workspace: linked cloud project, role,
|
|
2213
|
+
current key epoch, the epochs actually readable on this device, and the three sync states.
|
|
2214
|
+
|
|
2215
|
+
| Option | Description |
|
|
2216
|
+
|---|---|
|
|
2217
|
+
| `--json` | Output as JSON |
|
|
2218
|
+
|
|
2219
|
+
Sync state is **visible by design** (dec#154): a cloud-originated operation materializes in
|
|
2220
|
+
the local journal as `pending`, `synced` or `conflict`, and a conflict is presented for
|
|
2221
|
+
resolution rather than resolved by a silent last-write-wins. The counts are read from the
|
|
2222
|
+
outbox on disk, not from a cached counter — a status that echoed a number the code had
|
|
2223
|
+
itself incremented would reassure precisely when you are checking because you doubt.
|
|
2224
|
+
|
|
2225
|
+
`readable_epochs` likewise comes from the keyring on disk. It can be shorter than the
|
|
2226
|
+
epochs the state declares: a partial backup restore leaves that exact disagreement, and
|
|
2227
|
+
the honest answer is what can actually be decrypted here.
|
|
2228
|
+
|
|
2229
|
+
### `brainclaw cloud connect <invite-code> --url <url> --agent <id>`
|
|
2230
|
+
|
|
2231
|
+
Join a cloud project. This is a **key ceremony**, not a config write: it claims the invite,
|
|
2232
|
+
proves possession of this agent's Ed25519 identity, and attests the device's X25519
|
|
2233
|
+
encryption key with that same identity.
|
|
2234
|
+
|
|
2235
|
+
| Option | Description |
|
|
2236
|
+
|---|---|
|
|
2237
|
+
| `--url <url>` | Cloud deployment address (required) |
|
|
2238
|
+
| `--agent <id>` | Opaque agent identifier to enroll, 4–64 chars (required) |
|
|
2239
|
+
| `--json` | Output as JSON |
|
|
2240
|
+
|
|
2241
|
+
The human copies **only the invite code**, then compares **two fingerprints** with what the
|
|
2242
|
+
approver sees on their screen. No API key, no PEM, no agent_id, no environment variable
|
|
2243
|
+
(dec#8). Both fingerprints are printed in full, never truncated — a 16-character comparison
|
|
2244
|
+
collides far more easily than it looks.
|
|
2245
|
+
|
|
2246
|
+
The attestation is what stops a **phantom member**. Without it, the Cloud — which
|
|
2247
|
+
orchestrates the pairing — could insert its own key into the envelope list: end-to-end
|
|
2248
|
+
encryption whose key exchange is arbitrated by the very party it claims to neutralize.
|
|
2249
|
+
|
|
2250
|
+
A refused pairing writes **nothing** locally, so re-running is safe and leaves no orphan.
|
|
2251
|
+
|
|
2252
|
+
### `brainclaw cloud await --url <url>`
|
|
2253
|
+
|
|
2254
|
+
Observe the human approval and activate the local pairing.
|
|
2255
|
+
|
|
2256
|
+
Deliberately a **separate command**: approval depends on a person, whose delay is not
|
|
2257
|
+
bounded. A command that blocked indefinitely on a third party would be a poor citizen in a
|
|
2258
|
+
script, and an interrupted pairing must stay resumable. Polling does **not** mutate local
|
|
2259
|
+
state — reading a status must never change the workspace.
|
|
2260
|
+
|
|
2261
|
+
### `brainclaw cloud disconnect --url <url>`
|
|
2262
|
+
|
|
2263
|
+
Remove the local authorization and request remote revocation.
|
|
2264
|
+
|
|
2265
|
+
| Option | Description |
|
|
2266
|
+
|---|---|
|
|
2267
|
+
| `--url <url>` | Cloud deployment address (required) |
|
|
2268
|
+
| `--forget-keys` | Also erase this project's epoch keys — its sealed past becomes unreadable here |
|
|
2269
|
+
|
|
2270
|
+
Local state flips to revoked **even if the cloud is unreachable**: otherwise a lost device
|
|
2271
|
+
would stay authorized for want of a network, the opposite of what revocation must
|
|
2272
|
+
guarantee.
|
|
2273
|
+
|
|
2274
|
+
What disconnect does **not** do, stated rather than left implied: it does not erase data
|
|
2275
|
+
already pulled and decrypted locally, nor what other devices already hold. It withdraws an
|
|
2276
|
+
authorization; it does not rewrite the past (RFC §5.2).
|
|
2277
|
+
|
|
2247
2278
|
This keeps end-user installs aware of published npm releases without requiring a local tarball channel. To keep beta testers on a different channel, set `brainclaw_update_source` to `type: npm` with a different `dist_tag`, such as `prelaunch`.
|