@bolloon/bolloon-agent 0.5.0 → 0.5.2

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 (121) hide show
  1. package/README.md +39 -0
  2. package/dist/agents/agent-lang.js +101 -0
  3. package/dist/agents/agent-manifest-protocol.js +4 -0
  4. package/dist/agents/execution-supervisor.js +87 -5
  5. package/dist/agents/gateway-group.js +44 -3
  6. package/dist/agents/model-policy.js +651 -0
  7. package/dist/agents/pi-sdk-tools.js +84 -33
  8. package/dist/agents/pi-sdk.js +184 -31
  9. package/dist/agents/run-store.js +53 -0
  10. package/dist/agents/task/task-runner.js +8 -1
  11. package/dist/agents/workflow-pivot-loop.js +22 -13
  12. package/dist/cli/clipboard.js +65 -0
  13. package/dist/cli/ink-app.js +145 -17
  14. package/dist/cli/log-gate.js +386 -0
  15. package/dist/cli/mention-data.js +2 -0
  16. package/dist/cli/model-selector.js +538 -0
  17. package/dist/cli/reply-hygiene.js +54 -0
  18. package/dist/cli/setup-wizard.js +686 -69
  19. package/dist/cli/startup-notice.js +160 -0
  20. package/dist/cli/stores.js +22 -1
  21. package/dist/cli/theme.js +49 -0
  22. package/dist/cli/tui-select.js +970 -0
  23. package/dist/cli-entry.js +125 -18
  24. package/dist/efficode/compress.js +78 -0
  25. package/dist/efficode/crc32.js +38 -0
  26. package/dist/efficode/did.js +66 -0
  27. package/dist/efficode/index.js +24 -0
  28. package/dist/efficode/lz77.js +170 -0
  29. package/dist/efficode/negotiate.js +187 -0
  30. package/dist/efficode/ops.js +87 -0
  31. package/dist/efficode/packet.js +271 -0
  32. package/dist/efficode/types.js +45 -0
  33. package/dist/electron-build/agents/agent-manifest-protocol.js +4 -0
  34. package/dist/electron-build/agents/agent-manifest-protocol.js.map +1 -1
  35. package/dist/electron-build/agents/execution-supervisor.js +86 -4
  36. package/dist/electron-build/agents/execution-supervisor.js.map +1 -1
  37. package/dist/electron-build/agents/goal-store.js.map +1 -1
  38. package/dist/electron-build/agents/model-policy.js +706 -0
  39. package/dist/electron-build/agents/model-policy.js.map +1 -0
  40. package/dist/electron-build/agents/pi-sdk-tools.js +84 -33
  41. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  42. package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
  43. package/dist/electron-build/agents/pi-sdk.js +185 -31
  44. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  45. package/dist/electron-build/agents/run-store.js +55 -1
  46. package/dist/electron-build/agents/run-store.js.map +1 -1
  47. package/dist/electron-build/agents/task/task-runner.js +8 -1
  48. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  49. package/dist/electron-build/agents/workflow-pivot-loop.js +22 -13
  50. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
  51. package/dist/electron-build/llm/config-store.js +146 -6
  52. package/dist/electron-build/llm/config-store.js.map +1 -1
  53. package/dist/electron-build/llm/connection-probe.js +764 -0
  54. package/dist/electron-build/llm/connection-probe.js.map +1 -0
  55. package/dist/electron-build/llm/custom-provider-store.js +272 -0
  56. package/dist/electron-build/llm/custom-provider-store.js.map +1 -0
  57. package/dist/electron-build/llm/data/provider-catalog-baked.js +23 -0
  58. package/dist/electron-build/llm/data/provider-catalog-baked.js.map +1 -0
  59. package/dist/electron-build/llm/model-catalog.js +778 -0
  60. package/dist/electron-build/llm/model-catalog.js.map +1 -0
  61. package/dist/electron-build/llm/model-discovery.js +1202 -0
  62. package/dist/electron-build/llm/model-discovery.js.map +1 -0
  63. package/dist/electron-build/llm/model-selection.js +1315 -0
  64. package/dist/electron-build/llm/model-selection.js.map +1 -0
  65. package/dist/electron-build/llm/pi-ai.js +751 -55
  66. package/dist/electron-build/llm/pi-ai.js.map +1 -1
  67. package/dist/electron-build/llm/provider-catalog.js +969 -0
  68. package/dist/electron-build/llm/provider-catalog.js.map +1 -0
  69. package/dist/electron-build/llm/provider-registry.js +950 -0
  70. package/dist/electron-build/llm/provider-registry.js.map +1 -0
  71. package/dist/electron-build/llm/tool-name.js +266 -0
  72. package/dist/electron-build/llm/tool-name.js.map +1 -0
  73. package/dist/electron-build/utils/dual-source-facts.js +236 -0
  74. package/dist/electron-build/utils/dual-source-facts.js.map +1 -0
  75. package/dist/electron-build/utils/dual-source.js +32 -156
  76. package/dist/electron-build/utils/dual-source.js.map +1 -1
  77. package/dist/electron-build/utils/update-manager.js +22 -72
  78. package/dist/electron-build/utils/update-manager.js.map +1 -1
  79. package/dist/electron-build/utils/update-state.js +2 -19
  80. package/dist/electron-build/utils/update-state.js.map +1 -1
  81. package/dist/electron-build/utils/version-identity.js +193 -0
  82. package/dist/electron-build/utils/version-identity.js.map +1 -0
  83. package/dist/electron-build/utils/version-info.js +47 -101
  84. package/dist/electron-build/utils/version-info.js.map +1 -1
  85. package/dist/index.js +282 -96
  86. package/dist/ios/bolloon-web.json +10 -0
  87. package/dist/ios/mobile-contacts.js +44 -11
  88. package/dist/ios/mobile-core.js +1723 -84
  89. package/dist/ios/mobile-tasks.js +339 -0
  90. package/dist/ios/mobile-update.js +1442 -0
  91. package/dist/ios/mobile.js +308 -35
  92. package/dist/ios/routes-llm-config.js +344 -52
  93. package/dist/ios/routes-mobile-tasks.js +353 -0
  94. package/dist/ios/server.js +163 -4
  95. package/dist/ios/sw.js +1 -1
  96. package/dist/llm/config-store.js +146 -6
  97. package/dist/llm/connection-probe.js +744 -0
  98. package/dist/llm/custom-provider-store.js +224 -0
  99. package/dist/llm/data/provider-catalog-baked.js +19 -0
  100. package/dist/llm/model-catalog.js +717 -0
  101. package/dist/llm/model-discovery.js +1137 -0
  102. package/dist/llm/model-selection.js +1241 -0
  103. package/dist/llm/pi-ai.js +732 -54
  104. package/dist/llm/provider-catalog.js +898 -0
  105. package/dist/llm/provider-registry.js +917 -0
  106. package/dist/llm/tool-name.js +220 -0
  107. package/dist/setup/onboard.js +34 -8
  108. package/dist/utils/dual-source-facts.js +223 -0
  109. package/dist/utils/dual-source.js +12 -141
  110. package/dist/utils/update-manager.js +16 -59
  111. package/dist/utils/update-state.js +6 -18
  112. package/dist/utils/version-identity.js +176 -0
  113. package/dist/utils/version-info.js +9 -78
  114. package/dist/web/bolloon-web.json +10 -0
  115. package/dist/web/mobile-core.js +1721 -84
  116. package/dist/web/mobile-update.js +1442 -0
  117. package/dist/web/mobile.js +372 -35
  118. package/dist/web/routes-llm-config.js +344 -52
  119. package/dist/web/sw.js +1 -1
  120. package/package.json +1 -1
  121. package/scripts/build-web.ts +36 -0
@@ -6,6 +6,7 @@ import * as fs from 'fs/promises';
6
6
  import * as path from 'path';
7
7
  import * as os from 'os';
8
8
  import * as fsSync from 'fs';
9
+ import { absorbLegacyProviderEntries, ensureProviderRegistryMetadataSource, getProviderRegistryEntry, normalizeCustomProviders, setCustomProviderSnapshot } from './provider-registry.js';
9
10
  /** 2026-09-16 (M1): 不再在模块加载时固定 HOME —— 独立宿主/测试注入/长驻进程都走同一个解析 */
10
11
  function configDir() {
11
12
  const env = process.env.BOLLOON_HOME?.trim();
@@ -256,6 +257,11 @@ class LLMConfigStore {
256
257
  return next;
257
258
  }
258
259
  async initialize() {
260
+ // 2026-09-26 (P3): 任何一次配置读都顺手把注册表的元数据填充点接上。
261
+ // 为什么不放模块体: 本模块与注册表之间是循环 import, 模块体的执行时机取决于"谁是入口",
262
+ // 实测会撞上对方模块 `metadataSources` 的 TDZ (整个进程起不来)。配置路径是所有读模型
263
+ // 元数据的前置步骤, 放这里既能自动接上, 又不依赖 import 顺序 (详见 provider-registry)。
264
+ ensureProviderRegistryMetadataSource();
259
265
  // 目录变了 (换 HOME / 独立宿主 / 测试注入): 内存缓存作废, 重新读盘
260
266
  const dir = configDir();
261
267
  const sig = this.fileSignature();
@@ -290,19 +296,46 @@ class LLMConfigStore {
290
296
  loadedConfig.providers[provider] = { ...DEFAULT_PROVIDER_CONFIGS[provider] };
291
297
  }
292
298
  }
299
+ // 2026-09-26 (P3): 自定义供应商在**内存里**规范化 (数组形 → map / 别名 / 从 providers 吸收),
300
+ // **不写盘** —— "读一下就把用户文件改了"是另一种意外。写回只发生在显式 add/update/remove。
301
+ const normCustom = normalizeCustomProviders(loadedConfig.customProviders);
302
+ const absorbed = absorbLegacyProviderEntries(loadedConfig.providers, normCustom.providers);
303
+ loadedConfig.customProviders = absorbed.providers;
304
+ if (normCustom.rejected.length) {
305
+ console.warn(`[config-store] customProviders 有 ${normCustom.rejected.length} 条被拒: `
306
+ + normCustom.rejected.map((r) => `${r.key}(${r.reason})`).join('; '));
307
+ }
308
+ // 注册表的同步读口 (元数据填充点/长期任务门) 靠这份快照 —— 配置一读完就刷新, 不留陈旧结论
309
+ setCustomProviderSnapshot(absorbed.providers);
293
310
  // 确保有 activeProvider
294
311
  const activeProvider = loadedConfig.activeProvider;
295
- if (!activeProvider || !DEFAULT_PROVIDER_CONFIGS[activeProvider]) {
312
+ if (!activeProvider || !this.isSelectableProvider(activeProvider, loadedConfig)) {
296
313
  loadedConfig.activeProvider = 'ollama';
297
314
  }
298
315
  this.config = loadedConfig;
299
316
  }
300
317
  catch {
301
318
  this.config = getDefaultConfig();
319
+ setCustomProviderSnapshot({});
302
320
  await this.save();
303
321
  }
304
322
  this.initialized = true;
305
323
  }
324
+ /**
325
+ * 这个 provider id 现在**能当 activeProvider** 吗。
326
+ *
327
+ * 2026-09-26 (P3) 之前: 只认内置表 → 用户把自定义端点当默认模型时会被**静默改成 ollama**
328
+ * (旧配置直接失效)。现在: 内置, 或已注册的自定义供应商, 或配置里**真有这一格且写了地址/模型**
329
+ * (旧的"手写 provider 条目"写法) 都算数; 三者都不是才退到 ollama。
330
+ */
331
+ isSelectableProvider(id, cfg) {
332
+ if (DEFAULT_PROVIDER_CONFIGS[id])
333
+ return true;
334
+ if (cfg.customProviders && cfg.customProviders[id])
335
+ return true;
336
+ const own = cfg.providers[id];
337
+ return !!own && (String(own.baseUrl || '').trim().length > 0 || String(own.model || '').trim().length > 0);
338
+ }
306
339
  async save() {
307
340
  if (!this.config)
308
341
  return;
@@ -341,31 +374,87 @@ class LLMConfigStore {
341
374
  }
342
375
  async setActiveProvider(provider) {
343
376
  await this.initialize();
344
- if (!this.config?.providers[provider]) {
377
+ const hit = this.providerRowFor(String(provider));
378
+ if (!hit) {
345
379
  throw new Error(`Unknown provider: ${provider}`);
346
380
  }
347
- const providerConfig = this.config.providers[provider];
381
+ const providerConfig = hit.row;
348
382
  if (providerConfig.requiresApiKey && !providerConfig.apiKey) {
349
383
  throw new Error(`${provider} requires an API key but none is configured`);
350
384
  }
351
385
  await this.withWriteLock(async () => {
386
+ if (!hit.existing)
387
+ this.config.providers[String(provider)] = hit.row;
352
388
  this.config.activeProvider = provider;
353
389
  await this.save();
354
390
  });
355
391
  }
356
392
  async updateProvider(provider, updates) {
357
393
  await this.initialize();
358
- if (!this.config?.providers[provider]) {
394
+ const hit = this.providerRowFor(String(provider));
395
+ if (!hit) {
359
396
  throw new Error(`Unknown provider: ${provider}`);
360
397
  }
361
398
  await this.withWriteLock(async () => {
362
- this.config.providers[provider] = {
363
- ...this.config.providers[provider],
399
+ this.config.providers[String(provider)] = {
400
+ ...hit.row,
364
401
  ...updates
365
402
  };
366
403
  await this.save();
367
404
  });
368
405
  }
406
+ /**
407
+ * 取(必要时**算出**)一个 provider 的配置行。`existing=false` 表示这一行还不存在, 由调用方
408
+ * 在自己的写锁里落进 map(本方法**不改任何状态** —— 校验失败时就该什么都不发生)。
409
+ *
410
+ * 2026-09-26 (P6): 统一入口现在**接受自定义供应商 id** (从注册表校验, 不再只看内置表),
411
+ * 于是切换时会把 `model/baseUrl/apiKey` 写进 `providers.<自定义 id>` 这一格 —— 而这一格此前
412
+ * 根本不存在, 旧实现直接 `Unknown provider` 抛错。
413
+ *
414
+ * 规矩 (不编默认值):
415
+ * · 内置供应商 / 配置里本来就有这一格 → 原样返回 (老行为一字不变);
416
+ * · 已注册的**自定义供应商** → 用它的**声明**起手 (地址/模型), 凭据那一格留空等写入 ——
417
+ * 不拿内置默认去顶;
418
+ * · 其余 (既不是内置, 也不在 `customProviders` 里) → `null` (调用方照旧抛 Unknown provider)。
419
+ */
420
+ providerRowFor(id) {
421
+ const rows = this.config?.providers;
422
+ if (!rows)
423
+ return null;
424
+ if (rows[id])
425
+ return { row: rows[id], existing: true };
426
+ const spec = this.config?.customProviders?.[id];
427
+ if (!spec) {
428
+ // 2026-09-27 (目录驱动): **目录里的家**也走这条 —— 新家只要在目录里就能切进来 (不改代码)。
429
+ // 起手行只放目录**声明过的**东西: 基址 = 目录里的 api (目录没给就是空串, 让校验层如实报
430
+ // "需自定义 baseUrl"); 模型留空 (公开目录没有"默认模型"这回事, 不许抓一个凑数);
431
+ // 凭据那一格留空, 等写入或等环境变量 (目录只给环境变量**名**)。
432
+ const catEntry = getProviderRegistryEntry(id);
433
+ if (!catEntry || catEntry.kind !== 'catalog')
434
+ return null;
435
+ return {
436
+ existing: false,
437
+ row: {
438
+ enabled: true,
439
+ apiKey: '',
440
+ baseUrl: catEntry.defaultBaseUrl,
441
+ model: '',
442
+ requiresApiKey: catEntry.requiresApiKey !== false,
443
+ },
444
+ };
445
+ }
446
+ const entry = getProviderRegistryEntry(id);
447
+ return {
448
+ existing: false,
449
+ row: {
450
+ enabled: true,
451
+ apiKey: '',
452
+ baseUrl: spec.baseUrl,
453
+ model: String(spec.model || spec.models?.[0] || ''),
454
+ requiresApiKey: entry ? entry.requiresApiKey : true,
455
+ },
456
+ };
457
+ }
369
458
  async testProvider(provider) {
370
459
  await this.initialize();
371
460
  const config = this.config?.providers[provider];
@@ -494,6 +583,57 @@ class LLMConfigStore {
494
583
  getAllProviderInfo() {
495
584
  return PROVIDER_INFO;
496
585
  }
586
+ /**
587
+ * 2026-09-26: 丢弃内存缓存, 下一次 initialize() 强制重新读盘。
588
+ * 用途: 统一入口在做"读出→改写→写回"时, 必须在拿到跨进程锁之后重读,
589
+ * 否则两个进程各自拿旧内存值写回, 后写的会把先写的那份改动覆盖掉。
590
+ * 回滚路径同样需要它 (把文件还原后, 内存里的那一份也得跟着回来)。
591
+ */
592
+ invalidate() {
593
+ this.initialized = false;
594
+ this.config = null;
595
+ this.loadedDir = null;
596
+ this.loadedSig = null;
597
+ }
598
+ /** 配置文件绝对路径 (统一入口/锁/迁移都从这里取, 不再各自拼) */
599
+ configFilePath() {
600
+ return CONFIG_PATH;
601
+ }
602
+ /** 旧文件名 (迁移输入) */
603
+ legacyConfigFilePath() {
604
+ return LEGACY_CONFIG_PATH;
605
+ }
606
+ /**
607
+ * 现在在册的自定义供应商 (规范化后的内存视图; 读盘由 `initialize()` 保证已经发生过)。
608
+ * 凭据原样在里面 —— 调用方展示前必须走脱敏 (`custom-provider-store.redactCustomProviders`)。
609
+ */
610
+ async getCustomProviders() {
611
+ await this.initialize();
612
+ return { ...(this.config?.customProviders || {}) };
613
+ }
614
+ /**
615
+ * 覆盖 `customProviders` 一格 (唯一写口)。
616
+ *
617
+ * 只动这一格 —— 内置 `providers` / `activeProvider` 一个字节都不碰; 于是"加个自定义供应商"
618
+ * 不会顺手把用户在别处刚改的模型选择覆盖掉。写前调用方负责 `invalidate()` + `initialize()`
619
+ * (拿到跨进程锁之后重读), 否则两个进程各自拿旧内存值写回。
620
+ */
621
+ async setCustomProviders(map) {
622
+ await this.initialize();
623
+ const norm = normalizeCustomProviders(map && typeof map === 'object' ? map : {});
624
+ if (norm.rejected.length) {
625
+ throw new Error(`customProviders 规范化失败: ${norm.rejected.map((r) => `${r.key}(${r.reason})`).join('; ')}`);
626
+ }
627
+ await this.withWriteLock(async () => {
628
+ this.config.customProviders = norm.providers;
629
+ setCustomProviderSnapshot(norm.providers);
630
+ await this.save();
631
+ });
632
+ }
633
+ /** 配置目录 (锁文件、session 绑定都落在这里) */
634
+ configDirPath() {
635
+ return configDir();
636
+ }
497
637
  }
498
638
  export const llmConfigStore = new LLMConfigStore();
499
639
  export function getPiSDKConfig() {