@poa-box/agent 0.1.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.
Files changed (139) hide show
  1. package/.env.agent.template +20 -0
  2. package/README.md +46 -0
  3. package/brain/Config/agent-config.json +14 -0
  4. package/brain/Config/brain-allowlist.json +20 -0
  5. package/brain/Identity/goals.template.md +23 -0
  6. package/brain/Identity/how-i-think.md +406 -0
  7. package/brain/Identity/who-i-am.template.md +34 -0
  8. package/brain/Knowledge/BOOTSTRAP.md +66 -0
  9. package/brain/Knowledge/audit-corpus-index.json +406 -0
  10. package/brain/Knowledge/discussions.json +245 -0
  11. package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
  12. package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
  13. package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
  14. package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
  15. package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
  16. package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
  17. package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
  18. package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
  19. package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
  20. package/brain/Knowledge/projects.md +181 -0
  21. package/brain/Knowledge/risk-framework.md +90 -0
  22. package/brain/Knowledge/shared.md +416 -0
  23. package/brain/Knowledge/sprint-priorities.md +439 -0
  24. package/brain/Memory/.gitkeep +0 -0
  25. package/dist/commands/agent/daily-digest.d.ts +24 -0
  26. package/dist/commands/agent/daily-digest.js +336 -0
  27. package/dist/commands/agent/delegate.d.ts +12 -0
  28. package/dist/commands/agent/delegate.js +91 -0
  29. package/dist/commands/agent/deploy-to-org.d.ts +20 -0
  30. package/dist/commands/agent/deploy-to-org.js +154 -0
  31. package/dist/commands/agent/index.d.ts +2 -0
  32. package/dist/commands/agent/index.js +27 -0
  33. package/dist/commands/agent/init.d.ts +19 -0
  34. package/dist/commands/agent/init.js +303 -0
  35. package/dist/commands/agent/onboard.d.ts +22 -0
  36. package/dist/commands/agent/onboard.js +192 -0
  37. package/dist/commands/agent/paymaster-status.d.ts +14 -0
  38. package/dist/commands/agent/paymaster-status.js +130 -0
  39. package/dist/commands/agent/register.d.ts +21 -0
  40. package/dist/commands/agent/register.js +116 -0
  41. package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
  42. package/dist/commands/agent/setup-sponsorship.js +154 -0
  43. package/dist/commands/agent/status.d.ts +12 -0
  44. package/dist/commands/agent/status.js +171 -0
  45. package/dist/commands/agent/triage.d.ts +12 -0
  46. package/dist/commands/agent/triage.js +503 -0
  47. package/dist/commands/brain/advance-stage.d.ts +42 -0
  48. package/dist/commands/brain/advance-stage.js +206 -0
  49. package/dist/commands/brain/allowlist.d.ts +30 -0
  50. package/dist/commands/brain/allowlist.js +274 -0
  51. package/dist/commands/brain/append-lesson.d.ts +55 -0
  52. package/dist/commands/brain/append-lesson.js +245 -0
  53. package/dist/commands/brain/brainstorm.d.ts +154 -0
  54. package/dist/commands/brain/brainstorm.js +573 -0
  55. package/dist/commands/brain/daemon.d.ts +31 -0
  56. package/dist/commands/brain/daemon.js +348 -0
  57. package/dist/commands/brain/doctor.d.ts +27 -0
  58. package/dist/commands/brain/doctor.js +497 -0
  59. package/dist/commands/brain/edit-lesson.d.ts +51 -0
  60. package/dist/commands/brain/edit-lesson.js +248 -0
  61. package/dist/commands/brain/import-snapshot.d.ts +68 -0
  62. package/dist/commands/brain/import-snapshot.js +177 -0
  63. package/dist/commands/brain/index.d.ts +2 -0
  64. package/dist/commands/brain/index.js +67 -0
  65. package/dist/commands/brain/list.d.ts +21 -0
  66. package/dist/commands/brain/list.js +83 -0
  67. package/dist/commands/brain/migrate-projects.d.ts +44 -0
  68. package/dist/commands/brain/migrate-projects.js +209 -0
  69. package/dist/commands/brain/migrate.d.ts +74 -0
  70. package/dist/commands/brain/migrate.js +306 -0
  71. package/dist/commands/brain/new-project.d.ts +53 -0
  72. package/dist/commands/brain/new-project.js +226 -0
  73. package/dist/commands/brain/read.d.ts +24 -0
  74. package/dist/commands/brain/read.js +81 -0
  75. package/dist/commands/brain/remove-lesson.d.ts +47 -0
  76. package/dist/commands/brain/remove-lesson.js +206 -0
  77. package/dist/commands/brain/remove-project.d.ts +36 -0
  78. package/dist/commands/brain/remove-project.js +177 -0
  79. package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
  80. package/dist/commands/brain/retro-file-tasks.js +372 -0
  81. package/dist/commands/brain/retro-list.d.ts +28 -0
  82. package/dist/commands/brain/retro-list.js +125 -0
  83. package/dist/commands/brain/retro-mark-change.d.ts +58 -0
  84. package/dist/commands/brain/retro-mark-change.js +176 -0
  85. package/dist/commands/brain/retro-remove.d.ts +36 -0
  86. package/dist/commands/brain/retro-remove.js +142 -0
  87. package/dist/commands/brain/retro-respond.d.ts +56 -0
  88. package/dist/commands/brain/retro-respond.js +250 -0
  89. package/dist/commands/brain/retro-show.d.ts +23 -0
  90. package/dist/commands/brain/retro-show.js +100 -0
  91. package/dist/commands/brain/retro-start.d.ts +55 -0
  92. package/dist/commands/brain/retro-start.js +311 -0
  93. package/dist/commands/brain/search.d.ts +48 -0
  94. package/dist/commands/brain/search.js +190 -0
  95. package/dist/commands/brain/snapshot.d.ts +32 -0
  96. package/dist/commands/brain/snapshot.js +243 -0
  97. package/dist/commands/brain/status.d.ts +15 -0
  98. package/dist/commands/brain/status.js +166 -0
  99. package/dist/commands/brain/subscribe.d.ts +28 -0
  100. package/dist/commands/brain/subscribe.js +90 -0
  101. package/dist/commands/brain/tag.d.ts +46 -0
  102. package/dist/commands/brain/tag.js +192 -0
  103. package/dist/index.d.ts +17 -0
  104. package/dist/index.js +22 -0
  105. package/dist/lib/brain-daemon.d.ts +126 -0
  106. package/dist/lib/brain-daemon.js +811 -0
  107. package/dist/lib/brain-membership.d.ts +58 -0
  108. package/dist/lib/brain-membership.js +115 -0
  109. package/dist/lib/brain-migrate-projects.d.ts +43 -0
  110. package/dist/lib/brain-migrate-projects.js +247 -0
  111. package/dist/lib/brain-migrate.d.ts +77 -0
  112. package/dist/lib/brain-migrate.js +328 -0
  113. package/dist/lib/brain-ops.d.ts +271 -0
  114. package/dist/lib/brain-ops.js +571 -0
  115. package/dist/lib/brain-paths.d.ts +15 -0
  116. package/dist/lib/brain-paths.js +33 -0
  117. package/dist/lib/brain-projections.d.ts +216 -0
  118. package/dist/lib/brain-projections.js +829 -0
  119. package/dist/lib/brain-schemas.d.ts +36 -0
  120. package/dist/lib/brain-schemas.js +316 -0
  121. package/dist/lib/brain-signing.d.ts +103 -0
  122. package/dist/lib/brain-signing.js +256 -0
  123. package/dist/lib/brain.d.ts +198 -0
  124. package/dist/lib/brain.js +1057 -0
  125. package/dist/pop-agent.d.ts +1 -0
  126. package/dist/pop-agent.js +18 -0
  127. package/docs/agent.md +126 -0
  128. package/docs/agents/brain-anti-entropy.md +127 -0
  129. package/docs/agents/brain-cross-device-onboarding.md +210 -0
  130. package/docs/agents/brain-cross-machine-smoke.md +241 -0
  131. package/docs/agents/brain-layer-setup.md +725 -0
  132. package/docs/agents/offboarding-protocol.md +188 -0
  133. package/docs/agents/onboarding-protocol.md +243 -0
  134. package/docs/agents/running-an-agent.md +200 -0
  135. package/docs/brain.md +560 -0
  136. package/package.json +61 -0
  137. package/scripts/apply.sh +140 -0
  138. package/scripts/onboard.sh +205 -0
  139. package/scripts/setup-agent.ts +272 -0
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ /**
3
+ * Brain-layer change signing — ECDSA over snapshot bytes.
4
+ *
5
+ * Each brain CRDT change gets wrapped in a signed envelope before being
6
+ * written to the blockstore. The envelope's sig authenticates the full
7
+ * Automerge snapshot against an Ethereum address derived from the
8
+ * existing POP_PRIVATE_KEY. No Nostr keys, no Schnorr, no second PKI.
9
+ *
10
+ * Envelope format (v1):
11
+ *
12
+ * {
13
+ * v: 1,
14
+ * author: "0xABCD...", // Ethereum address (lowercase)
15
+ * timestamp: 1776200000, // unix seconds
16
+ * automerge: "0xDEADBEEF", // full Automerge.save() bytes, hex
17
+ * sig: "0xABC..." // ECDSA over keccak256(author|ts|automerge)
18
+ * }
19
+ *
20
+ * Serialized as UTF-8 JSON, stored as a raw-codec IPLD block. The CID
21
+ * covers the whole envelope, so the sig is content-addressed alongside
22
+ * the data it authenticates.
23
+ *
24
+ * On read, the projection layer:
25
+ * 1. Unmarshal the envelope JSON
26
+ * 2. Verify the sig recovers to `author`
27
+ * 3. Check `author` against the allowlist at
28
+ * agent/brain/Config/brain-allowlist.json
29
+ * 4. If all OK, extract the Automerge bytes and merge
30
+ *
31
+ * Sync layer stays permissionless — any peer can gossip any CID. Auth
32
+ * happens at read time so the network is resilient against relay
33
+ * operators (there are none, but the principle stands for any future
34
+ * transport).
35
+ */
36
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ var desc = Object.getOwnPropertyDescriptor(m, k);
39
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
40
+ desc = { enumerable: true, get: function() { return m[k]; } };
41
+ }
42
+ Object.defineProperty(o, k2, desc);
43
+ }) : (function(o, m, k, k2) {
44
+ if (k2 === undefined) k2 = k;
45
+ o[k2] = m[k];
46
+ }));
47
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
48
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
49
+ }) : function(o, v) {
50
+ o["default"] = v;
51
+ });
52
+ var __importStar = (this && this.__importStar) || (function () {
53
+ var ownKeys = function(o) {
54
+ ownKeys = Object.getOwnPropertyNames || function (o) {
55
+ var ar = [];
56
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
57
+ return ar;
58
+ };
59
+ return ownKeys(o);
60
+ };
61
+ return function (mod) {
62
+ if (mod && mod.__esModule) return mod;
63
+ var result = {};
64
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
65
+ __setModuleDefault(result, mod);
66
+ return result;
67
+ };
68
+ })();
69
+ Object.defineProperty(exports, "__esModule", { value: true });
70
+ exports.signBrainChange = signBrainChange;
71
+ exports.verifyBrainChange = verifyBrainChange;
72
+ exports.unwrapAutomergeBytes = unwrapAutomergeBytes;
73
+ exports.getAllowlistPath = getAllowlistPath;
74
+ exports.loadAllowlist = loadAllowlist;
75
+ exports.isAllowedAuthor = isAllowedAuthor;
76
+ exports.authenticateAndAuthorize = authenticateAndAuthorize;
77
+ exports.isAuthorizedAuthor = isAuthorizedAuthor;
78
+ const ethers_1 = require("ethers");
79
+ const fs_1 = require("fs");
80
+ const path_1 = require("path");
81
+ const brain_paths_1 = require("./brain-paths");
82
+ /**
83
+ * Canonical message that gets signed. Kept deterministic and simple —
84
+ * just concatenate the fields in a fixed order, hash, sign. Changing
85
+ * this format is a breaking change; bump the version when it happens.
86
+ */
87
+ function canonicalMessage(author, timestamp, automergeHex) {
88
+ return [
89
+ 'pop-brain-change/v1',
90
+ author.toLowerCase(),
91
+ String(timestamp),
92
+ automergeHex.toLowerCase(),
93
+ ].join('|');
94
+ }
95
+ function bytesToHex(bytes) {
96
+ return '0x' + Buffer.from(bytes).toString('hex');
97
+ }
98
+ function hexToBytes(hex) {
99
+ const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
100
+ return Uint8Array.from(Buffer.from(clean, 'hex'));
101
+ }
102
+ /**
103
+ * Sign an Automerge snapshot with the wallet derived from POP_PRIVATE_KEY.
104
+ * Returns a v1 envelope ready to be JSON-encoded and written as a block.
105
+ */
106
+ async function signBrainChange(automergeBytes, privateKey) {
107
+ const key = privateKey || process.env.POP_PRIVATE_KEY;
108
+ if (!key) {
109
+ throw new Error('No private key for brain signing (set POP_PRIVATE_KEY)');
110
+ }
111
+ const wallet = new ethers_1.ethers.Wallet(key);
112
+ const author = wallet.address.toLowerCase();
113
+ const timestamp = Math.floor(Date.now() / 1000);
114
+ const automergeHex = bytesToHex(automergeBytes);
115
+ const message = canonicalMessage(author, timestamp, automergeHex);
116
+ // signMessage applies the standard EIP-191 personal_sign prefix,
117
+ // so verification uses verifyMessage (not recoverAddress on a raw hash).
118
+ const sig = await wallet.signMessage(message);
119
+ return {
120
+ v: 1,
121
+ author,
122
+ timestamp,
123
+ automerge: automergeHex,
124
+ sig,
125
+ };
126
+ }
127
+ /**
128
+ * Verify an envelope's signature and return the recovered author.
129
+ * Throws if the envelope is malformed or the signature doesn't verify.
130
+ *
131
+ * NOTE: this only checks authenticity (sig corresponds to `author`);
132
+ * it does NOT check authorization (whether `author` is allowed to
133
+ * write to this doc). That's the allowlist check — see isAllowedAuthor.
134
+ */
135
+ function verifyBrainChange(envelope) {
136
+ if (envelope.v !== 1) {
137
+ throw new Error(`Unsupported brain envelope version: ${envelope.v}`);
138
+ }
139
+ if (!envelope.author || !envelope.timestamp || !envelope.automerge || !envelope.sig) {
140
+ throw new Error('Malformed brain envelope: missing required field');
141
+ }
142
+ const message = canonicalMessage(envelope.author, envelope.timestamp, envelope.automerge);
143
+ const recovered = ethers_1.ethers.utils.verifyMessage(message, envelope.sig).toLowerCase();
144
+ if (recovered !== envelope.author.toLowerCase()) {
145
+ throw new Error(`Brain envelope signature mismatch: expected ${envelope.author}, recovered ${recovered}`);
146
+ }
147
+ return recovered;
148
+ }
149
+ /**
150
+ * Extract the Automerge snapshot bytes from an envelope.
151
+ * Does NOT verify the signature — caller must run verifyBrainChange first.
152
+ */
153
+ function unwrapAutomergeBytes(envelope) {
154
+ return hexToBytes(envelope.automerge);
155
+ }
156
+ /**
157
+ * Path to the git-tracked brain-allowlist.json. Single source of truth
158
+ * for who is permitted to write to brain docs. Edited via governance
159
+ * (or via `pop brain allowlist add/remove`, which writes to the same
160
+ * file and leaves the git review gate in place).
161
+ *
162
+ * Exported so command handlers can write to the same path without
163
+ * hard-coding it independently.
164
+ */
165
+ function getAllowlistPath() {
166
+ return (0, path_1.join)((0, brain_paths_1.getRepoBrainRoot)(), 'Config', 'brain-allowlist.json');
167
+ }
168
+ function loadAllowlist() {
169
+ const p = getAllowlistPath();
170
+ if (!(0, fs_1.existsSync)(p))
171
+ return [];
172
+ try {
173
+ const raw = JSON.parse((0, fs_1.readFileSync)(p, 'utf8'));
174
+ // Accept either a list or an { entries: [...] } wrapper for future flex.
175
+ const list = Array.isArray(raw) ? raw : raw.entries || [];
176
+ return list.map(e => ({
177
+ address: String(e.address).toLowerCase(),
178
+ name: e.name,
179
+ addedAt: e.addedAt,
180
+ addedBy: e.addedBy,
181
+ }));
182
+ }
183
+ catch {
184
+ return [];
185
+ }
186
+ }
187
+ /**
188
+ * Check whether a given address is in the allowlist.
189
+ * Case-insensitive on the address.
190
+ */
191
+ function isAllowedAuthor(address) {
192
+ const needle = address.toLowerCase();
193
+ const list = loadAllowlist();
194
+ return list.some(e => e.address === needle);
195
+ }
196
+ /**
197
+ * Combined auth check: verify signature + allowlist membership.
198
+ * Returns the authenticated author on success; throws otherwise.
199
+ */
200
+ function authenticateAndAuthorize(envelope) {
201
+ const author = verifyBrainChange(envelope);
202
+ if (!isAllowedAuthor(author)) {
203
+ throw new Error(`Brain change rejected: author ${author} not in allowlist`);
204
+ }
205
+ return author;
206
+ }
207
+ /**
208
+ * Async authorization check: on-chain org membership first, static
209
+ * JSON allowlist second. Does NOT throw — returns a result object the
210
+ * caller can inspect for logging purposes. Callers then decide whether
211
+ * to reject the change or accept it based on `.allowed`.
212
+ *
213
+ * This is the new canonical authorization for brain read paths.
214
+ */
215
+ async function isAuthorizedAuthor(address) {
216
+ const addr = address.toLowerCase();
217
+ // Lazy import to keep brain-signing.ts free of subgraph / helia deps
218
+ // for the pure-function sign/verify side. The read-path verify that
219
+ // calls this runs in an async context that already pulls in brain.ts.
220
+ const { isOrgMember } = await Promise.resolve().then(() => __importStar(require('./brain-membership')));
221
+ try {
222
+ const onChain = await isOrgMember(addr);
223
+ if (onChain) {
224
+ // Also a static match? Just informational — both-agree is the
225
+ // healthy steady state for genesis agents.
226
+ const staticMatch = isAllowedAuthor(addr);
227
+ return {
228
+ allowed: true,
229
+ mode: staticMatch ? 'both-agree' : 'dynamic',
230
+ fallbackReason: '',
231
+ };
232
+ }
233
+ // Not a member. Still honor the static JSON for emergency overrides.
234
+ if (isAllowedAuthor(addr)) {
235
+ return {
236
+ allowed: true,
237
+ mode: 'static-fallback',
238
+ fallbackReason: 'not an active org member but present in static brain-allowlist.json (emergency override)',
239
+ };
240
+ }
241
+ return {
242
+ allowed: false,
243
+ mode: 'dynamic',
244
+ fallbackReason: 'not an active org member and not in static allowlist',
245
+ };
246
+ }
247
+ catch (err) {
248
+ // Subgraph unreachable → fall back to static JSON.
249
+ const staticMatch = isAllowedAuthor(addr);
250
+ return {
251
+ allowed: staticMatch,
252
+ mode: 'static-fallback',
253
+ fallbackReason: `dynamic allowlist unreachable (${err?.message ?? 'unknown error'}), using static fallback`,
254
+ };
255
+ }
256
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Brain layer — peer-to-peer CRDT substrate for shared agent thinking.
3
+ *
4
+ * Replaces the git-tracked markdown files (shared.md, projects.md,
5
+ * discussions.json, goals.md) with Automerge documents synced over
6
+ * libp2p-gossipsub + Bitswap via an embedded Helia node. No Argus-operated
7
+ * service in the hot path; peer-to-peer only.
8
+ *
9
+ * This file intentionally isolates the ESM import boundary. Helia and its
10
+ * libp2p dependencies are pure ESM, while the rest of the project compiles
11
+ * to CommonJS. Dynamic `await import()` is the clean way to bridge the gap
12
+ * without touching every other source file's module system.
13
+ *
14
+ * Plan: /Users/hudsonheadley/.claude/plans/cheeky-nibbling-raven.md
15
+ *
16
+ * Current status: MVP step 1 — initialize a Helia node, report peer info.
17
+ * CRDT/Automerge/gossipsub layers come in subsequent steps.
18
+ */
19
+ /**
20
+ * Where the brain layer persists its blocks and state.
21
+ * One directory per agent. Survives heartbeat cycles, not in git.
22
+ */
23
+ export declare function getBrainHome(): string;
24
+ export interface BrainNodeInfo {
25
+ peerId: string;
26
+ peerIdSource: 'persisted' | 'freshly-generated';
27
+ listeningAddrs: string[];
28
+ connectedPeers: number;
29
+ bootstrapPeerCount: number;
30
+ heliaVersion: string;
31
+ blockstorePath: string;
32
+ peerKeyPath: string;
33
+ subscribedTopics: string[];
34
+ topicPeerCounts: Record<string, number>;
35
+ }
36
+ /**
37
+ * Gossipsub topic name for a brain doc. Versioned so we can bump the
38
+ * wire format later without silently crossing streams with old peers.
39
+ */
40
+ export declare function topicForDoc(docId: string): string;
41
+ /**
42
+ * Head-CID announcement payload. Broadcast on the doc's gossipsub topic
43
+ * after every successful applyBrainChange. The announcement is ONLY a
44
+ * pointer — the actual block is fetched via Bitswap in step 6. For step
45
+ * 5 subscribers just log the announcement.
46
+ *
47
+ * Note: the payload is NOT signature-verified at the subscribe layer.
48
+ * "Auth at read, permissionless at sync" — any peer can gossip any CID,
49
+ * and readers enforce the allowlist when they actually load the doc
50
+ * (openBrainDoc verifies the signed envelope before returning the doc).
51
+ */
52
+ export interface BrainHeadAnnouncement {
53
+ v: 1;
54
+ docId: string;
55
+ cid: string;
56
+ author: string;
57
+ timestamp: number;
58
+ }
59
+ export declare function initBrainNode(): Promise<any>;
60
+ /**
61
+ * Gather a human-readable summary of the local brain node state.
62
+ */
63
+ export declare function getBrainNodeInfo(): Promise<BrainNodeInfo>;
64
+ export declare function publishBrainHead(docId: string, cid: string, author: string): Promise<void>;
65
+ /**
66
+ * Subscribe to a doc's gossipsub topic and invoke `onAnnouncement` for
67
+ * every incoming head-CID announcement. Malformed payloads are logged
68
+ * and skipped — the sync layer is permissionless, so a subscriber must
69
+ * not crash on bad input from a misbehaving peer.
70
+ *
71
+ * Returns an unsubscribe function that removes the handler and the
72
+ * topic subscription.
73
+ */
74
+ export declare function subscribeBrainTopic(docId: string, onAnnouncement: (ann: BrainHeadAnnouncement, from: string) => void): Promise<() => void>;
75
+ /**
76
+ * Clean shutdown. Call before exiting a long-lived process.
77
+ * Safe to call when no node was initialized.
78
+ */
79
+ export declare function stopBrainNode(): Promise<void>;
80
+ /**
81
+ * Open an Automerge document by ID. If the manifest has a head CID for
82
+ * this ID, loads the state from the blockstore. Otherwise returns a fresh
83
+ * empty doc — seeded from the canonical genesis file if one exists for
84
+ * this docId (task #352), or a plain `Automerge.init()` as a last resort.
85
+ *
86
+ * Returns the doc plus its current head CID (null for new docs) so the
87
+ * caller can tell whether this was a load or an init.
88
+ */
89
+ export declare function openBrainDoc<T = any>(docId: string): Promise<{
90
+ doc: any;
91
+ headCid: string | null;
92
+ }>;
93
+ /**
94
+ * Apply a change function to a brain doc and persist the new state.
95
+ *
96
+ * Writes the new full-state snapshot as a raw IPLD block, computes its
97
+ * CID, updates the heads manifest. Returns the new CID.
98
+ *
99
+ * Snapshot-per-write is simpler than delta-based persistence for MVP;
100
+ * the tradeoff is that Automerge.save() produces the full state each
101
+ * time. For the shared.md / projects.md scale (KB-range docs) this is
102
+ * fine. When docs grow, switch to saving incremental changes via
103
+ * Automerge.getChanges() and a linked-list of block CIDs.
104
+ */
105
+ export declare function applyBrainChange<T = any>(docId: string, changeFn: (doc: T) => void, options?: {
106
+ allowInvalidShape?: boolean;
107
+ }): Promise<{
108
+ headCid: string;
109
+ doc: any;
110
+ author: string;
111
+ }>;
112
+ /**
113
+ * Import a raw Automerge snapshot as the new local head for a brain doc.
114
+ *
115
+ * Task #353 (HB#348): the post-HB#352 follow-up for migrating the 3 existing
116
+ * Argus agents off their pre-genesis disjoint Automerge state. The HB#341
117
+ * export step pinned argus's current state for all 3 canonical docs to IPFS
118
+ * (Qm...). This function is the receive side: load those bytes into vigil_01
119
+ * or sentinel_01's brain home as the new shared head so their subsequent
120
+ * writes build on argus's root instead of their own disjoint root.
121
+ *
122
+ * ## Semantics
123
+ *
124
+ * - Load the bytes via `Automerge.load()` to validate structural integrity.
125
+ * Throws if the bytes are corrupt or not an Automerge snapshot.
126
+ * - Run the standard write-time schema validator (#346) unless
127
+ * `opts.allowInvalidShape` is set.
128
+ * - Sign a new envelope via `signBrainChange` — the importing agent becomes
129
+ * the envelope author for the new head, even though the Automerge content
130
+ * is preserved from the source.
131
+ * - Write the envelope as a new IPLD block, update the manifest, publish the
132
+ * head CID via gossipsub (same as applyBrainChange's persist+publish flow).
133
+ *
134
+ * ## Safety
135
+ *
136
+ * This function REPLACES the local head. If the local brain home already
137
+ * has content for this docId, that state becomes orphaned (the old envelope
138
+ * stays in the blockstore, but the manifest no longer points at it). Callers
139
+ * must decide whether to preserve local-only content before calling:
140
+ *
141
+ * 1. `pop brain read --doc <id> --json` to snapshot local state
142
+ * 2. `pop brain import-snapshot --doc <id> --file <canonical-bytes>`
143
+ * 3. Replay local-only lessons via `pop brain append-lesson` calls on the
144
+ * new shared baseline
145
+ *
146
+ * The CLI wrapper (`pop brain import-snapshot`) enforces a `--force` flag
147
+ * requirement when a local head exists, so there are no accidental replaces.
148
+ */
149
+ export declare function importBrainDoc(docId: string, automergeBytes: Uint8Array, opts?: {
150
+ allowInvalidShape?: boolean;
151
+ }): Promise<{
152
+ headCid: string;
153
+ doc: any;
154
+ author: string;
155
+ }>;
156
+ /**
157
+ * List all known brain doc IDs + their current head CIDs.
158
+ * Reads the manifest file directly — doesn't require Helia to be running.
159
+ */
160
+ export declare function listBrainDocs(): Array<{
161
+ docId: string;
162
+ headCid: string;
163
+ }>;
164
+ export type BrainSyncResult = {
165
+ action: 'skip';
166
+ reason: string;
167
+ headCid: string;
168
+ } | {
169
+ action: 'adopt';
170
+ reason: string;
171
+ headCid: string;
172
+ } | {
173
+ action: 'merge';
174
+ reason: string;
175
+ headCid: string;
176
+ } | {
177
+ action: 'reject';
178
+ reason: string;
179
+ };
180
+ /**
181
+ * Given a remote head-CID announcement, fetch the block (via Bitswap if
182
+ * not already local), verify it, merge with the local doc, and update
183
+ * the manifest. Returns the action taken.
184
+ *
185
+ * Does NOT re-publish after merging. The next local write (via
186
+ * applyBrainChange) will publish from the merged state. This avoids
187
+ * gossip ping-pong where two peers would keep producing "merge heads"
188
+ * and broadcasting them at each other.
189
+ */
190
+ export declare function fetchAndMergeRemoteHead(docId: string, remoteCidStr: string): Promise<BrainSyncResult>;
191
+ /**
192
+ * Projection helper — returns the current Automerge doc as a plain
193
+ * JS object (for JSON display). Wraps openBrainDoc and Automerge.clone.
194
+ */
195
+ export declare function readBrainDoc(docId: string): Promise<{
196
+ doc: any;
197
+ headCid: string | null;
198
+ }>;