@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,90 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain subscribe — keep the process alive and log incoming head-CID
4
+ * announcements on a doc's gossipsub topic. The manual test surface for
5
+ * MVP step 5.
6
+ *
7
+ * Typical 2-terminal smoke test:
8
+ * Terminal A: pop brain subscribe --doc pop.brain.shared
9
+ * Terminal B: (any write that calls applyBrainChange — step 7 ships
10
+ * the write CLI; until then, a direct script or test)
11
+ *
12
+ * Terminal A logs each announcement it sees: docId, CID, claimed author
13
+ * (not trusted — see brain.ts#BrainHeadAnnouncement), the peer it came
14
+ * from. Block fetch + load is step 6.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.subscribeHandler = void 0;
18
+ const brain_1 = require("../../lib/brain");
19
+ exports.subscribeHandler = {
20
+ builder: (yargs) => yargs
21
+ .option('doc', {
22
+ describe: 'Brain document ID (e.g. pop.brain.shared)',
23
+ type: 'string',
24
+ demandOption: true,
25
+ })
26
+ .option('log-only', {
27
+ describe: 'Only log announcements — do NOT fetch blocks or update manifest',
28
+ type: 'boolean',
29
+ default: false,
30
+ }),
31
+ handler: async (argv) => {
32
+ const docId = argv.doc;
33
+ const topic = (0, brain_1.topicForDoc)(docId);
34
+ console.log(`Subscribing to ${topic} ...`);
35
+ const helia = await (0, brain_1.initBrainNode)();
36
+ const peerId = helia.libp2p.peerId.toString();
37
+ console.log(`Local peer: ${peerId}`);
38
+ console.log(`Listening on:`);
39
+ for (const a of helia.libp2p.getMultiaddrs()) {
40
+ console.log(` ${a.toString()}`);
41
+ }
42
+ console.log('');
43
+ console.log('Waiting for announcements. Press Ctrl-C to exit.');
44
+ console.log('');
45
+ const logOnly = argv.logOnly === true;
46
+ if (logOnly) {
47
+ console.log('(log-only mode — will NOT fetch blocks)');
48
+ console.log('');
49
+ }
50
+ const unsubscribe = await (0, brain_1.subscribeBrainTopic)(docId, (ann, from) => {
51
+ const when = new Date(ann.timestamp * 1000).toISOString();
52
+ console.log(`[${when}] head ${ann.cid}`);
53
+ console.log(` doc=${ann.docId} author=${ann.author} from=${from}`);
54
+ if (logOnly)
55
+ return;
56
+ // Step 6: fetch the block via Bitswap, verify, merge, update manifest.
57
+ // Runs async inside the event callback — we fire and forget so
58
+ // the listener doesn't block on slow network fetches.
59
+ (0, brain_1.fetchAndMergeRemoteHead)(ann.docId, ann.cid)
60
+ .then((result) => {
61
+ if (result.action === 'reject') {
62
+ console.log(` -> REJECTED: ${result.reason}`);
63
+ }
64
+ else {
65
+ console.log(` -> ${result.action}: ${result.reason}`);
66
+ if ('headCid' in result && result.headCid !== ann.cid) {
67
+ console.log(` new local head = ${result.headCid}`);
68
+ }
69
+ }
70
+ })
71
+ .catch((err) => {
72
+ console.log(` -> ERROR during sync: ${err.message}`);
73
+ });
74
+ });
75
+ // Keep the process alive. SIGINT cleanly unsubscribes and stops Helia.
76
+ const shutdown = async () => {
77
+ console.log('\nShutting down...');
78
+ try {
79
+ unsubscribe();
80
+ }
81
+ catch { }
82
+ await (0, brain_1.stopBrainNode)();
83
+ process.exit(0);
84
+ };
85
+ process.on('SIGINT', shutdown);
86
+ process.on('SIGTERM', shutdown);
87
+ // Block forever — keepalive so libp2p keeps running.
88
+ await new Promise(() => { });
89
+ },
90
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * pop brain tag — add or remove tags on an existing brain lesson.
3
+ *
4
+ * Task #347 (Retro #1 fallback #6). Companion to `pop brain search`.
5
+ * Tags are an optional `string[]` field on each lesson. Vocabulary is
6
+ * free-form — suggested conventions live in docs/agents/brain-layer-setup.md
7
+ * but nothing enforces them. The goal is search-ability not rigor.
8
+ *
9
+ * Usage:
10
+ * pop brain tag --doc pop.brain.shared --lesson-id <id> --add tag1,tag2
11
+ * pop brain tag --doc pop.brain.shared --lesson-id <id> --remove old-tag
12
+ *
13
+ * --add and --remove both accept comma-separated lists. At least one must
14
+ * be supplied. Idempotent: re-adding a tag or removing a missing tag is a
15
+ * no-op on the tags array itself but still writes a new head CID (the
16
+ * tag operation is a legitimate change event for the provenance trail).
17
+ */
18
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
19
+ interface TagArgs {
20
+ doc: string;
21
+ lessonId: string;
22
+ add?: string;
23
+ remove?: string;
24
+ allowInvalidShape?: boolean;
25
+ 'idempotency-key'?: string;
26
+ 'no-idempotency'?: boolean;
27
+ }
28
+ export declare const tagHandler: {
29
+ builder: (yargs: Argv) => Argv<{
30
+ doc: string;
31
+ } & {
32
+ "lesson-id": string;
33
+ } & {
34
+ add: string | undefined;
35
+ } & {
36
+ remove: string | undefined;
37
+ } & {
38
+ "allow-invalid-shape": boolean;
39
+ } & {
40
+ "idempotency-key": string | undefined;
41
+ } & {
42
+ "no-idempotency": boolean;
43
+ }>;
44
+ handler: (argv: ArgumentsCamelCase<TagArgs>) => Promise<void>;
45
+ };
46
+ export {};
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain tag — add or remove tags on an existing brain lesson.
4
+ *
5
+ * Task #347 (Retro #1 fallback #6). Companion to `pop brain search`.
6
+ * Tags are an optional `string[]` field on each lesson. Vocabulary is
7
+ * free-form — suggested conventions live in docs/agents/brain-layer-setup.md
8
+ * but nothing enforces them. The goal is search-ability not rigor.
9
+ *
10
+ * Usage:
11
+ * pop brain tag --doc pop.brain.shared --lesson-id <id> --add tag1,tag2
12
+ * pop brain tag --doc pop.brain.shared --lesson-id <id> --remove old-tag
13
+ *
14
+ * --add and --remove both accept comma-separated lists. At least one must
15
+ * be supplied. Idempotent: re-adding a tag or removing a missing tag is a
16
+ * no-op on the tags array itself but still writes a new head CID (the
17
+ * tag operation is a legitimate change event for the provenance trail).
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.tagHandler = void 0;
54
+ const ethers_1 = require("ethers");
55
+ const brain_1 = require("../../lib/brain");
56
+ const brain_ops_1 = require("../../lib/brain-ops");
57
+ const idempotency_1 = require("@poa-box/cli/lib/idempotency");
58
+ const output = __importStar(require("@poa-box/cli/lib/output"));
59
+ function parseList(s) {
60
+ if (!s)
61
+ return [];
62
+ return s
63
+ .split(',')
64
+ .map((x) => x.trim())
65
+ .filter((x) => x.length > 0);
66
+ }
67
+ exports.tagHandler = {
68
+ builder: (yargs) => yargs
69
+ .option('doc', {
70
+ describe: 'Brain document ID (e.g. pop.brain.shared)',
71
+ type: 'string',
72
+ demandOption: true,
73
+ })
74
+ .option('lesson-id', {
75
+ describe: 'The lesson.id of the entry to tag',
76
+ type: 'string',
77
+ demandOption: true,
78
+ })
79
+ .option('add', {
80
+ describe: 'Comma-separated list of tags to add',
81
+ type: 'string',
82
+ })
83
+ .option('remove', {
84
+ describe: 'Comma-separated list of tags to remove',
85
+ type: 'string',
86
+ })
87
+ .option('allow-invalid-shape', {
88
+ describe: 'Bypass write-time schema validation (Task #346).',
89
+ type: 'boolean',
90
+ default: false,
91
+ })
92
+ .option('idempotency-key', {
93
+ type: 'string',
94
+ describe: 'Task #374 (HB#215): explicit idempotency key. Agent-scoped.',
95
+ })
96
+ .option('no-idempotency', {
97
+ type: 'boolean',
98
+ default: false,
99
+ describe: 'Bypass the idempotency cache.',
100
+ })
101
+ .check((argv) => {
102
+ if (!argv.add && !argv.remove) {
103
+ throw new Error('Must supply at least one of --add or --remove');
104
+ }
105
+ return true;
106
+ }),
107
+ handler: async (argv) => {
108
+ try {
109
+ const addTags = parseList(argv.add);
110
+ const removeTags = parseList(argv.remove);
111
+ // Pre-flight existence check — fail fast with candidate ids if not
112
+ // found. Same UX as edit-lesson / remove-lesson.
113
+ const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
114
+ const lessons = Array.isArray(currentDoc?.lessons) ? currentDoc.lessons : [];
115
+ const target = lessons.find((l) => l && l.id === argv.lessonId);
116
+ if (!target) {
117
+ const candidates = lessons
118
+ .map((l) => l?.id)
119
+ .filter((id) => typeof id === 'string')
120
+ .slice(0, 8);
121
+ output.error(`Lesson "${argv.lessonId}" not found in ${argv.doc}. ` +
122
+ `Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
123
+ process.exitCode = 1;
124
+ return;
125
+ }
126
+ // Task #374: idempotency check (agent-scoped).
127
+ const signerKey = process.env.POP_PRIVATE_KEY;
128
+ const authorScope = signerKey ? new ethers_1.ethers.Wallet(signerKey).address.toLowerCase() : 'anonymous';
129
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
130
+ if (!argv.noIdempotency) {
131
+ const cached = (0, idempotency_1.checkIdempotencyCache)(authorScope, 'brain.tag', idempKey);
132
+ if (cached) {
133
+ if (output.isJsonMode()) {
134
+ output.json({ status: 'ok', cached: true, ...cached });
135
+ }
136
+ else {
137
+ console.log('');
138
+ console.log(` Lesson "${argv.lessonId}" tags already updated (idempotency cache hit)`);
139
+ console.log(` head: ${cached.headCid}`);
140
+ console.log('');
141
+ }
142
+ return;
143
+ }
144
+ }
145
+ const result = await (0, brain_ops_1.routedDispatch)({
146
+ type: 'tagLesson',
147
+ docId: argv.doc,
148
+ lessonId: argv.lessonId,
149
+ addTags,
150
+ removeTags,
151
+ allowInvalidShape: argv.allowInvalidShape,
152
+ });
153
+ if (!argv.noIdempotency) {
154
+ (0, idempotency_1.recordIdempotentResult)(authorScope, 'brain.tag', idempKey, {
155
+ docId: argv.doc,
156
+ lessonId: argv.lessonId,
157
+ added: addTags,
158
+ removed: removeTags,
159
+ headCid: result.headCid,
160
+ });
161
+ }
162
+ if (output.isJsonMode()) {
163
+ output.json({
164
+ status: 'ok',
165
+ docId: argv.doc,
166
+ lessonId: argv.lessonId,
167
+ added: addTags,
168
+ removed: removeTags,
169
+ headCid: result.headCid,
170
+ routedViaDaemon: result.routedViaDaemon,
171
+ });
172
+ }
173
+ else {
174
+ console.log('');
175
+ console.log(` Lesson "${argv.lessonId}" tags updated in ${argv.doc}`);
176
+ if (addTags.length > 0)
177
+ console.log(` added: ${addTags.join(', ')}`);
178
+ if (removeTags.length > 0)
179
+ console.log(` removed: ${removeTags.join(', ')}`);
180
+ console.log(` head: ${result.headCid}`);
181
+ console.log('');
182
+ }
183
+ }
184
+ catch (err) {
185
+ output.error(err.message);
186
+ process.exitCode = 1;
187
+ }
188
+ finally {
189
+ await (0, brain_1.stopBrainNode)();
190
+ }
191
+ },
192
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @poa-box/agent — agent + brain command surface for the POP CLI.
3
+ *
4
+ * This module is the plugin contract with @poa-box/cli: the CLI probes for this
5
+ * package at startup (require('@poa-box/agent'), falling back to the in-repo
6
+ * packages/agent/dist path) and, when present, registers these two command
7
+ * groups. Visibility is the CLI's decision — hidden from `pop --help` unless
8
+ * POP_AGENT_MODE=1, always visible under the `pop-agent` bin.
9
+ *
10
+ * Nothing here may import the p2p/CRDT stack at module load. The brain
11
+ * runtime (libp2p, helia, automerge) is loaded lazily inside lib/brain.ts via
12
+ * an ESM-import bridge, and must stay that way: this module loads on EVERY
13
+ * `pop` invocation when the package is installed, and a human running
14
+ * `pop task list` must not pay for a gossip mesh they will never use.
15
+ */
16
+ export { registerAgentCommands } from './commands/agent';
17
+ export { registerBrainCommands } from './commands/brain';
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * @poa-box/agent — agent + brain command surface for the POP CLI.
4
+ *
5
+ * This module is the plugin contract with @poa-box/cli: the CLI probes for this
6
+ * package at startup (require('@poa-box/agent'), falling back to the in-repo
7
+ * packages/agent/dist path) and, when present, registers these two command
8
+ * groups. Visibility is the CLI's decision — hidden from `pop --help` unless
9
+ * POP_AGENT_MODE=1, always visible under the `pop-agent` bin.
10
+ *
11
+ * Nothing here may import the p2p/CRDT stack at module load. The brain
12
+ * runtime (libp2p, helia, automerge) is loaded lazily inside lib/brain.ts via
13
+ * an ESM-import bridge, and must stay that way: this module loads on EVERY
14
+ * `pop` invocation when the package is installed, and a human running
15
+ * `pop task list` must not pay for a gossip mesh they will never use.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.registerBrainCommands = exports.registerAgentCommands = void 0;
19
+ var agent_1 = require("./commands/agent");
20
+ Object.defineProperty(exports, "registerAgentCommands", { enumerable: true, get: function () { return agent_1.registerAgentCommands; } });
21
+ var brain_1 = require("./commands/brain");
22
+ Object.defineProperty(exports, "registerBrainCommands", { enumerable: true, get: function () { return brain_1.registerBrainCommands; } });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Brain daemon — persistent libp2p process that keeps gossipsub alive between
3
+ * agent CLI invocations and periodically re-broadcasts the local manifest's
4
+ * head CIDs so peers coming online can catch up.
5
+ *
6
+ * ## Why
7
+ *
8
+ * The brain layer's HB#322 dogfood finding: three consecutive vigil_01 brain
9
+ * writes were invisible to argus_prime because agent sessions run in separate
10
+ * 15-min cron slots and never overlap in wall-clock time. Gossipsub is
11
+ * broadcast-only (no store-and-forward), so announcements published while a
12
+ * peer is offline vanish permanently. Every agent ended up with a per-agent
13
+ * append-only journal, not a shared substrate.
14
+ *
15
+ * Hudson's HB#322 directive: fix it properly with a long-running daemon. No
16
+ * shortcuts. Model it on the go-ds-crdt reference architecture
17
+ * (github.com/ipfs/go-ds-crdt, examples/globaldb/globaldb.go).
18
+ *
19
+ * ## Design (mapped from go-ds-crdt)
20
+ *
21
+ * go-ds-crdt | This module
22
+ * -----------------------------|--------------------------------------
23
+ * Datastore (long-lived) | runDaemon() — single process per agent
24
+ * Broadcaster (PubSub) | existing publishBrainHead / subscribeBrainTopic
25
+ * DAGSyncer (DAGService) | existing Helia blockstore + Bitswap
26
+ * RebroadcastInterval = 1m | REBROADCAST_INTERVAL_MS = 60_000
27
+ * keepalive netTopic (20s) | KEEPALIVE_TOPIC + 20s interval
28
+ * seenHeads map | DEFERRED — v1 rebroadcasts unconditionally
29
+ * RepairInterval = 1h | DEFERRED — MVP envelope is snapshot-per-write
30
+ * | so there's no DAG to walk
31
+ * signal handling | SIGTERM / SIGINT / SIGHUP
32
+ * PutHook / DeleteHook | DEFERRED — v2 adds IPC routing so existing
33
+ * | commands become clients
34
+ *
35
+ * ## Process model
36
+ *
37
+ * pop brain daemon start parent spawns a detached child running
38
+ * `node dist/index.js brain daemon __run`;
39
+ * parent writes PID file and exits
40
+ * pop brain daemon __run the child entrypoint, calls runDaemon()
41
+ * pop brain daemon stop reads PID, sends SIGTERM, waits cleanup
42
+ * pop brain daemon status reads PID, checks liveness, opens Unix
43
+ * socket, sends {method: "status"}, prints
44
+ * pop brain daemon logs tails daemon.log
45
+ *
46
+ * ${POP_BRAIN_HOME}/daemon.pid parent-written PID file
47
+ * ${POP_BRAIN_HOME}/daemon.sock Unix socket for IPC (mode 0600)
48
+ * ${POP_BRAIN_HOME}/daemon.log append-only log
49
+ *
50
+ * ## IPC protocol
51
+ *
52
+ * Newline-delimited JSON over Unix socket. Each request is one line:
53
+ *
54
+ * {"id": "1", "method": "status"}
55
+ *
56
+ * Each response is one line:
57
+ *
58
+ * {"id": "1", "result": {...}}
59
+ * {"id": "1", "error": "..."}
60
+ *
61
+ * First-ship methods: status. Second-ship methods: appendLesson, readDoc,
62
+ * snapshot — those let existing CLI commands route through a running daemon
63
+ * instead of spinning up their own libp2p.
64
+ */
65
+ export declare const REBROADCAST_INTERVAL_MS = 60000;
66
+ export declare const REBROADCAST_JITTER = 0.3;
67
+ export declare const REBROADCAST_GRACE_MS = 5000;
68
+ export declare const KEEPALIVE_INTERVAL_MS = 20000;
69
+ export declare const REDIAL_INTERVAL_MS = 30000;
70
+ export declare const KEEPALIVE_TOPIC = "pop/brain/net/v1";
71
+ /**
72
+ * Canonical brain docs every daemon subscribes to at startup regardless
73
+ * of local manifest state. A fresh brain home has an empty manifest, so
74
+ * without this list the daemon would not subscribe to any doc topics
75
+ * and could never receive remote head announcements for
76
+ * `pop.brain.shared` / `pop.brain.projects` until after its first
77
+ * local write.
78
+ *
79
+ * Adding a new canonical doc here makes every daemon pick it up on
80
+ * next restart. To experiment with a non-canonical doc, just perform a
81
+ * local write via `pop brain append-lesson --doc <id>` — the write
82
+ * path adds the doc to the manifest, and the next daemon loop iteration
83
+ * picks it up via listBrainDocs().
84
+ */
85
+ export declare const CANONICAL_BRAIN_DOCS: string[];
86
+ export declare function getDaemonPidPath(): string;
87
+ export declare function getDaemonSockPath(): string;
88
+ export declare function getDaemonLogPath(): string;
89
+ /**
90
+ * Check if a daemon appears to be running for this brain home.
91
+ * Returns the PID if alive, null otherwise. Cleans up stale PID files.
92
+ */
93
+ export declare function getRunningDaemonPid(): number | null;
94
+ /**
95
+ * Run the daemon event loop. Blocks until a termination signal arrives.
96
+ * This is the __run entrypoint invoked by the detached child process.
97
+ *
98
+ * NEVER call this from the parent CLI path — it will never return.
99
+ */
100
+ export declare function runDaemon(): Promise<void>;
101
+ /**
102
+ * Typed IPC error. Attaches a `.code` for the caller to branch on.
103
+ *
104
+ * phase = 'pre-connect' The connection was never established (socket
105
+ * missing, ECONNREFUSED). Safe to fall back to a
106
+ * local execution path — the write did not land
107
+ * in the daemon's process.
108
+ * phase = 'post-connect' The connection was established and the request
109
+ * was sent, but a response did not come back. The
110
+ * write may or may not have landed. NOT safe to
111
+ * fall back — see routedDispatch() in brain-ops.ts.
112
+ */
113
+ export declare class BrainIpcError extends Error {
114
+ code: string;
115
+ phase: 'pre-connect' | 'post-connect';
116
+ constructor(message: string, code: string, phase: 'pre-connect' | 'post-connect');
117
+ }
118
+ /**
119
+ * IPC client helper: send a request to the running daemon.
120
+ *
121
+ * Throws a BrainIpcError whose `.phase` indicates whether the failure is
122
+ * safe to recover from by falling back to a local code path. Pre-connect
123
+ * failures (ECONNREFUSED, ENOENT, daemon not running) are safe. Post-connect
124
+ * failures (timeout, ECONNRESET, EPIPE) leave the write in an unknown state.
125
+ */
126
+ export declare function sendIpcRequest(method: string, params?: any, timeoutMs?: number): Promise<any>;