@bolloon/bolloon-agent 0.1.12 → 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 (38) 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/web/components/p2p/index.js +226 -264
  16. package/dist/web/index.html +12 -0
  17. package/package.json +1 -1
  18. package/scripts/build-web.ts +1 -1
  19. package/src/agents/p2p-chat-tools.ts +383 -0
  20. package/src/agents/p2p-document-tools.ts +151 -1
  21. package/src/agents/workflow-pivot-loop.ts +13 -12
  22. package/src/bollharness-integration/channel-judgment-engine.ts +1 -1
  23. package/src/documents/reader.ts +5 -0
  24. package/src/documents/store.ts +1 -1
  25. package/src/llm/pi-ai.ts +6 -5
  26. package/src/network/iroh-discovery.ts +2 -1
  27. package/src/network/iroh-transport.ts +15 -2
  28. package/src/network/p2p.ts +9 -8
  29. package/src/network/storage/adapters/json-adapter.ts +17 -2
  30. package/src/network/storage/index.ts +19 -3
  31. package/src/social/channels/channel-heartbeat-agent.ts +1 -1
  32. package/src/web/server.ts +149 -0
  33. package/tsconfig.electron.json +1 -1
  34. package/tsconfig.json +1 -1
  35. package/dist/web/components/p2p/P2PModal.js +0 -188
  36. package/dist/web/components/p2p/p2p-modal.js +0 -657
  37. package/dist/web/components/p2p/p2p-tools.js +0 -248
  38. 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
  // 这里可以发送消息触发对方响应