@bolloon/bolloon-agent 0.1.11 → 0.1.13

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 (42) hide show
  1. package/dist/agents/p2p-chat-tools.js +321 -0
  2. package/dist/agents/p2p-document-tools.js +121 -1
  3. package/dist/agents/workflow-pivot-loop.js +4 -4
  4. package/dist/cli-entry.js +1 -1
  5. package/dist/documents/reader.js +5 -0
  6. package/dist/documents/store.js +1 -1
  7. package/dist/llm/pi-ai.js +6 -5
  8. package/dist/network/iroh-discovery.js +2 -1
  9. package/dist/network/iroh-transport.js +15 -2
  10. package/dist/network/p2p.js +9 -8
  11. package/dist/network/storage/adapters/json-adapter.js +16 -1
  12. package/dist/network/storage/index.js +2 -1
  13. package/dist/pi-ecosystem-judgment/index.js +43 -115
  14. package/dist/social/channels/channel-heartbeat-agent.js +1 -1
  15. package/dist/utils/auto-update.js +15 -1
  16. package/dist/web/components/p2p/index.js +226 -264
  17. package/dist/web/index.html +12 -0
  18. package/package.json +3 -1
  19. package/scripts/build-web.ts +1 -1
  20. package/scripts/postinstall.js +1 -1
  21. package/src/agents/p2p-chat-tools.ts +383 -0
  22. package/src/agents/p2p-document-tools.ts +151 -1
  23. package/src/agents/workflow-pivot-loop.ts +13 -12
  24. package/src/bollharness-integration/channel-judgment-engine.ts +1 -1
  25. package/src/cli-entry.ts +1 -1
  26. package/src/documents/reader.ts +5 -0
  27. package/src/documents/store.ts +1 -1
  28. package/src/llm/pi-ai.ts +6 -5
  29. package/src/network/iroh-discovery.ts +2 -1
  30. package/src/network/iroh-transport.ts +15 -2
  31. package/src/network/p2p.ts +9 -8
  32. package/src/network/storage/adapters/json-adapter.ts +17 -2
  33. package/src/network/storage/index.ts +19 -3
  34. package/src/social/channels/channel-heartbeat-agent.ts +1 -1
  35. package/src/utils/auto-update.ts +17 -1
  36. package/src/web/server.ts +149 -0
  37. package/tsconfig.electron.json +1 -1
  38. package/tsconfig.json +1 -1
  39. package/dist/web/components/p2p/P2PModal.js +0 -188
  40. package/dist/web/components/p2p/p2p-modal.js +0 -657
  41. package/dist/web/components/p2p/p2p-tools.js +0 -248
  42. package/dist/web/server.js +0 -1890
@@ -43,7 +43,7 @@ export class JsonMessageStore {
43
43
  const stored = { ...msg, id };
44
44
  const filePath = this.getMessageFilePath(new Date(msg.timestamp));
45
45
  await this.withLock(filePath, async () => {
46
- const messages = await this.readJsonFile(filePath) || [];
46
+ let messages = await this.readJsonFile(filePath) || [];
47
47
  messages.push(stored);
48
48
  // 如果文件过大,拆分
49
49
  if (messages.length > this.config.maxMessagesPerFile) {
@@ -189,6 +189,21 @@ export class JsonMessageStore {
189
189
  }
190
190
  return count;
191
191
  }
192
+ async getAllOfflineTargets() {
193
+ // 重新从磁盘加载最新状态(避免内存 vs 磁盘不一致)
194
+ const baseDir = path.join(this.config.baseDir, 'offline');
195
+ let files = [];
196
+ try {
197
+ files = await fs.readdir(baseDir);
198
+ }
199
+ catch {
200
+ return Array.from(this.offlineMessages.keys());
201
+ }
202
+ return files
203
+ .filter((f) => f.endsWith('.json'))
204
+ .map((f) => f.replace(/\.json$/, ''))
205
+ .filter((id) => id.length > 0);
206
+ }
192
207
  // ============================================================================
193
208
  // 待响应请求
194
209
  // ============================================================================
@@ -2,7 +2,8 @@
2
2
  * Storage Layer Entry Point
3
3
  * 导出消息存储工厂函数和类型
4
4
  */
5
- export { DEFAULT_STORAGE_CONFIG } from './types.js';
5
+ import { DEFAULT_STORAGE_CONFIG } from './types.js';
6
+ export { DEFAULT_STORAGE_CONFIG };
6
7
  import { JsonMessageStore } from './adapters/json-adapter.js';
7
8
  import * as path from 'path';
8
9
  // 默认存储配置
@@ -16,12 +16,14 @@
16
16
  */
17
17
  import * as fs from 'fs/promises';
18
18
  import * as path from 'path';
19
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
20
+ const yaml = require('js-yaml');
19
21
  const JUDGMENTS_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'judgments');
20
22
  const JUDGMENT_FILES = {
21
- rules: path.join(JUDGMENTS_DIR, 'rules.yaml'),
22
- preferences: path.join(JUDGMENTS_DIR, 'preferences.yaml'),
23
- trajectories: path.join(JUDGMENTS_DIR, 'trajectories.yaml'),
24
- rewards: path.join(JUDGMENTS_DIR, 'rewards.yaml'),
23
+ rule: path.join(JUDGMENTS_DIR, 'rules.yaml'),
24
+ preference: path.join(JUDGMENTS_DIR, 'preferences.yaml'),
25
+ trajectory: path.join(JUDGMENTS_DIR, 'trajectories.yaml'),
26
+ reward: path.join(JUDGMENTS_DIR, 'rewards.yaml'),
25
27
  };
26
28
  let judgmentCache = new Map();
27
29
  let valueFunctionCache = null;
@@ -99,8 +101,9 @@ async function saveJudgments(type, judgments) {
99
101
  const filePath = JUDGMENT_FILES[type];
100
102
  if (!filePath)
101
103
  return;
102
- const yaml = serializeYaml({ judgments });
103
- await fs.writeFile(filePath, yaml, 'utf-8');
104
+ const body = yaml.dump({ judgments });
105
+ const header = '# Auto-generated by Pi Judgment System\n# Do not edit manually\n\n';
106
+ await fs.writeFile(filePath, header + body, 'utf-8');
104
107
  cacheDirty = true;
105
108
  }
106
109
  /**
@@ -323,122 +326,37 @@ function parseYaml(content) {
323
326
  try {
324
327
  if (!content.trim())
325
328
  return [];
326
- const data = {};
327
- const lines = content.split('\n');
328
- const arrayItems = [];
329
- let inArray = false;
330
- let currentItem = null;
331
- let currentItemIndent = 0;
332
- for (const line of lines) {
333
- const trimmed = line.trim();
334
- const indent = line.search(/\S/);
335
- const isArrayItem = trimmed.startsWith('-');
336
- const isComment = trimmed.startsWith('#');
337
- if (isComment)
338
- continue;
339
- if (trimmed.startsWith('judgments:')) {
340
- inArray = true;
341
- continue;
342
- }
343
- if (!inArray)
344
- continue;
345
- if (isArrayItem) {
346
- if (currentItem) {
347
- arrayItems.push(currentItem);
348
- }
349
- currentItem = {};
350
- currentItemIndent = indent + 1;
351
- const itemContent = trimmed.substring(1).trim();
352
- const kvMatch = itemContent.match(/^(\w+):\s*(.*)/);
353
- if (kvMatch) {
354
- currentItem[kvMatch[1]] = parseValue(kvMatch[2]);
355
- }
356
- continue;
357
- }
358
- if (currentItem && indent > currentItemIndent) {
359
- const kvMatch = trimmed.match(/^(\w+):\s*(.*)/);
360
- if (kvMatch) {
361
- currentItem[kvMatch[1]] = parseValue(kvMatch[2]);
362
- }
363
- continue;
364
- }
365
- if (trimmed.includes(':')) {
366
- const kvMatch = trimmed.match(/^(\w+):\s*(.*)/);
367
- if (kvMatch && !isArrayItem) {
368
- data[kvMatch[1]] = parseValue(kvMatch[2]);
369
- }
370
- }
371
- }
372
- if (currentItem) {
373
- arrayItems.push(currentItem);
374
- }
375
- if (arrayItems.length > 0) {
376
- data['judgments'] = arrayItems;
377
- }
378
- return data;
329
+ return yaml.load(content) ?? [];
379
330
  }
380
331
  catch {
381
332
  return [];
382
333
  }
383
334
  }
384
- function parseValue(value) {
385
- const trimmed = value.trim();
386
- if (trimmed === 'true')
335
+ /**
336
+ * (YAML serialization now delegated to js-yaml)
337
+ */
338
+ /**
339
+ * Best-effort coercion for a scalar frontmatter value.
340
+ * Returns booleans, numbers, or the raw string.
341
+ */
342
+ function parseFrontmatterValue(raw) {
343
+ const value = raw.trim();
344
+ if (value === '')
345
+ return '';
346
+ if (value === 'true')
387
347
  return true;
388
- if (trimmed === 'false')
348
+ if (value === 'false')
389
349
  return false;
390
- if (trimmed === 'null' || trimmed === 'undefined')
350
+ if (value === 'null' || value === '~')
391
351
  return null;
392
- if (!isNaN(Number(trimmed)) && trimmed !== '')
393
- return Number(trimmed);
394
- return trimmed;
395
- }
396
- /**
397
- * Simple YAML serializer
398
- */
399
- function serializeYaml(data) {
400
- const lines = ['# Auto-generated by Pi Judgment System', '# Do not edit manually', ''];
401
- if (typeof data === 'object' && data !== null) {
402
- lines.push('judgments:');
403
- const d = data;
404
- const arr = d.judgments;
405
- if (Array.isArray(arr)) {
406
- for (const item of arr) {
407
- lines.push(' - ' + serializeObject(item, 4));
408
- }
409
- }
410
- }
411
- return lines.join('\n');
412
- }
413
- function serializeObject(obj, indent) {
414
- if (typeof obj !== 'object' || obj === null) {
415
- return String(obj);
416
- }
417
- const spaces = ' '.repeat(indent);
418
- const innerSpace = ' '.repeat(indent + 2);
419
- const parts = [];
420
- for (const [key, value] of Object.entries(obj)) {
421
- if (value === undefined || value === null)
422
- continue;
423
- if (typeof value === 'object' && !Array.isArray(value)) {
424
- parts.push(`${key}:`);
425
- parts.push(serializeObject(value, indent + 2));
426
- }
427
- else if (Array.isArray(value)) {
428
- parts.push(`${key}:`);
429
- for (const item of value) {
430
- parts.push(`${innerSpace}- ${serializeObject(item, indent + 4)}`);
431
- }
432
- }
433
- else {
434
- const strValue = typeof value === 'string' ? `"${value}"` : String(value);
435
- parts.push(`${key}: ${strValue}`);
436
- }
437
- }
438
- if (parts.length === 1 && !parts[0].includes(':')) {
439
- return parts[0];
352
+ if (/^-?\d+(\.\d+)?$/.test(value))
353
+ return Number(value);
354
+ // Strip surrounding quotes if present
355
+ if ((value.startsWith('"') && value.endsWith('"')) ||
356
+ (value.startsWith("'") && value.endsWith("'"))) {
357
+ return value.slice(1, -1);
440
358
  }
441
- return parts.join(`\n${spaces}`);
359
+ return value;
442
360
  }
443
361
  /**
444
362
  * Extract YAML frontmatter from markdown
@@ -447,6 +365,16 @@ function extractFrontmatter(content) {
447
365
  const match = content.match(/^---\n([\s\S]*?)\n---/);
448
366
  if (!match)
449
367
  return {};
368
+ // Try js-yaml first; fall back to simple line parser if it fails or types missing.
369
+ try {
370
+ const parsed = yaml.load(match[1]);
371
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
372
+ return parsed;
373
+ }
374
+ }
375
+ catch {
376
+ // fall through to manual parsing
377
+ }
450
378
  const frontmatter = {};
451
379
  const lines = match[1].split('\n');
452
380
  let currentKey = '';
@@ -465,10 +393,10 @@ function extractFrontmatter(content) {
465
393
  const [key, ...valueParts] = trimmed.split(':');
466
394
  const value = valueParts.join(':').trim();
467
395
  if (currentKey === 'judgment' && indent > 0) {
468
- frontmatter[currentKey][key] = parseValue(value);
396
+ frontmatter[currentKey][key] = parseFrontmatterValue(value);
469
397
  }
470
398
  else {
471
- frontmatter[key.trim()] = parseValue(value);
399
+ frontmatter[key.trim()] = parseFrontmatterValue(value);
472
400
  currentKey = key.trim();
473
401
  }
474
402
  }
@@ -306,7 +306,7 @@ export class ChannelHeartbeatAgent {
306
306
  currentMessage: message,
307
307
  senderName: peer.name
308
308
  };
309
- const decision = this.judgmentEngine.decide(context);
309
+ const decision = await this.judgmentEngine.decide(context);
310
310
  if (decision.shouldCall) {
311
311
  console.log(`[HeartbeatAgent] Auto-triggering Harness: Gate ${decision.gate} for ${peer.name}`);
312
312
  // 这里可以发送消息触发对方响应
@@ -56,14 +56,19 @@ function getGlobalBolloonDir() {
56
56
  * 优先使用全局安装的版本(更准确反映实际运行的版本)
57
57
  */
58
58
  function getInstalledVersion(packageName) {
59
+ // 调试:打印 cwd
60
+ console.error(`[DEBUG] getInstalledVersion called, cwd=${process.cwd()}, package=${packageName}`);
59
61
  // 对于 @bolloon/bolloon-agent,始终优先从全局安装位置读取版本
60
62
  // 这样可以准确检测实际安装的版本,而不受 cwd 影响
61
63
  if (packageName === '@bolloon/bolloon-agent') {
62
64
  const globalDir = getGlobalBolloonDir();
65
+ console.error(`[DEBUG] globalDir=${globalDir}`);
63
66
  if (globalDir) {
64
67
  const pkgPath = path.join(globalDir, 'package.json');
68
+ console.error(`[DEBUG] pkgPath=${pkgPath}, exists=${fs.existsSync(pkgPath)}`);
65
69
  try {
66
70
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
71
+ console.error(`[DEBUG] pkg.version=${pkg.version}`);
67
72
  return pkg.version || null;
68
73
  }
69
74
  catch (e) {
@@ -72,9 +77,11 @@ function getInstalledVersion(packageName) {
72
77
  }
73
78
  // 回退到本地 package.json
74
79
  const localPkgPath = path.join(process.cwd(), 'package.json');
80
+ console.error(`[DEBUG] localPkgPath=${localPkgPath}, exists=${fs.existsSync(localPkgPath)}`);
75
81
  if (fs.existsSync(localPkgPath)) {
76
82
  try {
77
83
  const pkg = JSON.parse(fs.readFileSync(localPkgPath, 'utf-8'));
84
+ console.error(`[DEBUG] local pkg.version=${pkg.version}`);
78
85
  return pkg.version || null;
79
86
  }
80
87
  catch (e) {
@@ -84,9 +91,11 @@ function getInstalledVersion(packageName) {
84
91
  }
85
92
  // 检查本地 node_modules
86
93
  const packageJsonPath = findPackageJson(packageName);
94
+ console.error(`[DEBUG] findPackageJson=${packageJsonPath}`);
87
95
  if (packageJsonPath) {
88
96
  try {
89
97
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
98
+ console.error(`[DEBUG] node_modules pkg.version=${pkg.version}`);
90
99
  return pkg.version || null;
91
100
  }
92
101
  catch (e) {
@@ -155,10 +164,15 @@ async function checkBolloonUpdates() {
155
164
  let latestVersion = '';
156
165
  for (const pkg of packagesToCheck) {
157
166
  const installed = getInstalledVersion(pkg);
167
+ console.error(`[DEBUG] getInstalledVersion(${pkg}) = ${installed}`);
158
168
  if (!installed)
159
169
  continue;
160
- currentVersion = installed;
170
+ // 只记录 @bolloon/bolloon-agent 的版本作为当前版本
171
+ if (pkg === '@bolloon/bolloon-agent') {
172
+ currentVersion = installed;
173
+ }
161
174
  const latest = await getLatestVersion(pkg);
175
+ console.error(`[DEBUG] getLatestVersion(${pkg}) = ${latest}`);
162
176
  if (latest && compareVersions(installed, latest) < 0) {
163
177
  hasUpdate = true;
164
178
  latestVersion = latest;