@bolloon/bolloon-agent 0.6.5 → 0.6.7

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 (188) hide show
  1. package/dist/agents/chain/chain-settlement.js +0 -1
  2. package/dist/agents/chat-segmenter.js +4 -16
  3. package/dist/agents/contacts/human-directory.js +155 -0
  4. package/dist/agents/contacts/tools.js +79 -0
  5. package/dist/agents/experience-review.js +0 -3
  6. package/dist/agents/goal-flywheel/goal-change.js +3 -4
  7. package/dist/agents/goal-resume.js +0 -4
  8. package/dist/agents/ha-daemon.js +85 -0
  9. package/dist/agents/ha-supervisor.js +146 -0
  10. package/dist/agents/intent-classifier.js +1 -1
  11. package/dist/agents/iteration-budget.js +12 -0
  12. package/dist/agents/knowledge-organizer.js +0 -2
  13. package/dist/agents/lesson-to-skill.js +0 -1
  14. package/dist/agents/loop-review.js +2 -2
  15. package/dist/agents/memory-store.js +384 -0
  16. package/dist/agents/opportunity-match.js +15 -3
  17. package/dist/agents/opportunity-web-search.js +39 -0
  18. package/dist/agents/p2p-chat-tools.js +1 -2
  19. package/dist/agents/parse-tool-call.js +9 -22
  20. package/dist/agents/pi-harness.js +1 -2
  21. package/dist/agents/pi-sdk-session-manager.js +8 -0
  22. package/dist/agents/pi-sdk-tools.js +180 -5
  23. package/dist/agents/pi-sdk.js +210 -503
  24. package/dist/agents/process-runner.js +28 -9
  25. package/dist/agents/react-loop.js +9 -17
  26. package/dist/agents/runner-resolver.js +4 -3
  27. package/dist/agents/skill-loader.js +22 -15
  28. package/dist/agents/skills-manager.js +1 -25
  29. package/dist/agents/workflow-pivot-loop.js +152 -61
  30. package/dist/bootstrap/context-collector.js +67 -7
  31. package/dist/bootstrap/context-hierarchy.js +13 -1
  32. package/dist/bootstrap/persona-loader.js +26 -0
  33. package/dist/cli/ink-app.js +44 -17
  34. package/dist/cli/intent-command.js +22 -12
  35. package/dist/cli/loading-tui.js +8 -2
  36. package/dist/cli/log-gate.js +40 -8
  37. package/dist/cli/mention-data.js +1 -0
  38. package/dist/cli/reply-hygiene.js +22 -0
  39. package/dist/cli/trace-line.js +61 -2
  40. package/dist/electron-build/agents/agent-gateway.js.map +1 -1
  41. package/dist/electron-build/agents/chain/chain-indexer.js.map +1 -1
  42. package/dist/electron-build/agents/chain/chain-settlement.js +0 -1
  43. package/dist/electron-build/agents/chain/chain-settlement.js.map +1 -1
  44. package/dist/electron-build/agents/contacts/chain.js.map +1 -1
  45. package/dist/electron-build/agents/contacts/human-directory.js +204 -0
  46. package/dist/electron-build/agents/contacts/human-directory.js.map +1 -0
  47. package/dist/electron-build/agents/contacts/policy.js.map +1 -1
  48. package/dist/electron-build/agents/contacts/tools.js +112 -0
  49. package/dist/electron-build/agents/contacts/tools.js.map +1 -1
  50. package/dist/electron-build/agents/experience-review.js +0 -3
  51. package/dist/electron-build/agents/experience-review.js.map +1 -1
  52. package/dist/electron-build/agents/external-events.js.map +1 -1
  53. package/dist/electron-build/agents/goal-flywheel/goal-change.js +3 -4
  54. package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -1
  55. package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -1
  56. package/dist/electron-build/agents/goal-resume.js +0 -4
  57. package/dist/electron-build/agents/goal-resume.js.map +1 -1
  58. package/dist/electron-build/agents/intent-classifier.js +1 -1
  59. package/dist/electron-build/agents/lesson-to-skill.js +0 -1
  60. package/dist/electron-build/agents/lesson-to-skill.js.map +1 -1
  61. package/dist/electron-build/agents/loop-review.js +2 -2
  62. package/dist/electron-build/agents/loop-review.js.map +1 -1
  63. package/dist/electron-build/agents/memory-recall.js.map +1 -1
  64. package/dist/electron-build/agents/memory-store.js +430 -0
  65. package/dist/electron-build/agents/memory-store.js.map +1 -0
  66. package/dist/electron-build/agents/parse-tool-call.js +9 -22
  67. package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
  68. package/dist/electron-build/agents/pi-harness.js +1 -2
  69. package/dist/electron-build/agents/pi-harness.js.map +1 -1
  70. package/dist/electron-build/agents/pi-sdk-session-manager.js +8 -0
  71. package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -1
  72. package/dist/electron-build/agents/pi-sdk-tools.js +180 -5
  73. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  74. package/dist/electron-build/agents/pi-sdk.js +208 -501
  75. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  76. package/dist/electron-build/agents/pre-tool-validator.js.map +1 -1
  77. package/dist/electron-build/agents/process-runner.js +28 -9
  78. package/dist/electron-build/agents/process-runner.js.map +1 -1
  79. package/dist/electron-build/agents/runner-resolver.js +4 -3
  80. package/dist/electron-build/agents/runner-resolver.js.map +1 -1
  81. package/dist/electron-build/agents/skill-loader.js +22 -16
  82. package/dist/electron-build/agents/skill-loader.js.map +1 -1
  83. package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -1
  84. package/dist/electron-build/agents/skills-manager.js +0 -24
  85. package/dist/electron-build/agents/skills-manager.js.map +1 -1
  86. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  87. package/dist/electron-build/agents/workflow-engine.js.map +1 -1
  88. package/dist/electron-build/agents/workflow-pivot-loop.js +155 -62
  89. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
  90. package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -1
  91. package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -1
  92. package/dist/electron-build/bootstrap/context-collector.js +68 -7
  93. package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
  94. package/dist/electron-build/bootstrap/context-hierarchy.js +13 -1
  95. package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -1
  96. package/dist/electron-build/bootstrap/persona-loader.js +27 -0
  97. package/dist/electron-build/bootstrap/persona-loader.js.map +1 -1
  98. package/dist/electron-build/electron-preload.js.map +1 -1
  99. package/dist/electron-build/external-engines/delegate.js.map +1 -1
  100. package/dist/electron-build/kernel/channel-actor.js +31 -2
  101. package/dist/electron-build/kernel/channel-actor.js.map +1 -1
  102. package/dist/electron-build/kernel/model-runtime.js +8 -1
  103. package/dist/electron-build/kernel/model-runtime.js.map +1 -1
  104. package/dist/electron-build/llm/config-store.js +20 -0
  105. package/dist/electron-build/llm/config-store.js.map +1 -1
  106. package/dist/electron-build/llm/embedding-adapter.js +75 -0
  107. package/dist/electron-build/llm/embedding-adapter.js.map +1 -0
  108. package/dist/electron-build/llm/pi-ai.js +112 -38
  109. package/dist/electron-build/llm/pi-ai.js.map +1 -1
  110. package/dist/electron-build/llm/system-prompt/registry.js +8 -6
  111. package/dist/electron-build/llm/system-prompt/registry.js.map +1 -1
  112. package/dist/electron-build/network/agent-network.js.map +1 -1
  113. package/dist/electron-build/orbitdb/memory-sync.js +76 -0
  114. package/dist/electron-build/orbitdb/memory-sync.js.map +1 -0
  115. package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -1
  116. package/dist/electron-build/security/tool-gate.js +2 -0
  117. package/dist/electron-build/security/tool-gate.js.map +1 -1
  118. package/dist/electron-build/setup/setup-store.js +34 -6
  119. package/dist/electron-build/setup/setup-store.js.map +1 -1
  120. package/dist/electron-build/social/heartbeat.js +18 -5
  121. package/dist/electron-build/social/heartbeat.js.map +1 -1
  122. package/dist/electron-build/utils/update-manager.js.map +1 -1
  123. package/dist/index.js +250 -12
  124. package/dist/ios/bolloon-web.json +3 -3
  125. package/dist/ios/client.js +74 -35
  126. package/dist/ios/icons/icon-1400x1400.png +0 -0
  127. package/dist/ios/icons/icon-1400x1400.webp +0 -0
  128. package/dist/ios/index.html +122 -108
  129. package/dist/ios/mobile-agent.js +572 -45
  130. package/dist/ios/mobile-contacts.js +0 -4
  131. package/dist/ios/mobile-core.js +8079 -4590
  132. package/dist/ios/mobile-harness.js +1004 -0
  133. package/dist/ios/mobile-memory.js +317 -0
  134. package/dist/ios/mobile-orbitdb.js +194 -0
  135. package/dist/ios/mobile-persona.js +80 -0
  136. package/dist/ios/mobile-privacy.js +2 -3
  137. package/dist/ios/mobile.css +286 -21
  138. package/dist/ios/mobile.html +122 -108
  139. package/dist/ios/mobile.js +434 -152
  140. package/dist/ios/routes-contacts.js +94 -1
  141. package/dist/ios/routes-world.js +13 -2
  142. package/dist/ios/server.js +54 -41
  143. package/dist/ios/style.css +13 -5
  144. package/dist/ios/sw.js +1 -1
  145. package/dist/ios/ui/message-renderer.js +4 -18
  146. package/dist/ios/ui/step-timeline.js +14 -1
  147. package/dist/kernel/channel-actor.js +30 -2
  148. package/dist/kernel/gate-scan.js +2 -2
  149. package/dist/kernel/ha/ha-arbiter.js +177 -0
  150. package/dist/kernel/ha/ha-guard.js +151 -0
  151. package/dist/kernel/index.js +36 -0
  152. package/dist/kernel/model-runtime.js +8 -1
  153. package/dist/kernel/plan.js +5 -5
  154. package/dist/kernel/roster.js +3 -3
  155. package/dist/llm/config-store.js +20 -0
  156. package/dist/llm/embedding-adapter.js +69 -0
  157. package/dist/llm/pi-ai.js +110 -37
  158. package/dist/llm/system-prompt/registry.js +8 -6
  159. package/dist/orbitdb/memory-sync.js +70 -0
  160. package/dist/pi-ecosystem-judgment/adaptive-scan.js +1 -1
  161. package/dist/security/tool-gate.js +2 -0
  162. package/dist/setup/setup-store.js +34 -6
  163. package/dist/social/heartbeat.js +18 -5
  164. package/dist/web/bolloon-web.json +3 -3
  165. package/dist/web/client.js +74 -35
  166. package/dist/web/icons/icon-1400x1400.png +0 -0
  167. package/dist/web/icons/icon-1400x1400.webp +0 -0
  168. package/dist/web/index.html +37 -25
  169. package/dist/web/mobile-agent.js +572 -45
  170. package/dist/web/mobile-contacts.js +0 -4
  171. package/dist/web/mobile-core.js +8079 -4590
  172. package/dist/web/mobile-harness.js +1004 -0
  173. package/dist/web/mobile-memory.js +317 -0
  174. package/dist/web/mobile-orbitdb.js +194 -0
  175. package/dist/web/mobile-persona.js +80 -0
  176. package/dist/web/mobile-privacy.js +2 -3
  177. package/dist/web/mobile.css +286 -21
  178. package/dist/web/mobile.html +122 -108
  179. package/dist/web/mobile.js +434 -152
  180. package/dist/web/routes-contacts.js +94 -1
  181. package/dist/web/routes-world.js +13 -2
  182. package/dist/web/server.js +54 -41
  183. package/dist/web/style.css +13 -5
  184. package/dist/web/sw.js +1 -1
  185. package/dist/web/ui/message-renderer.js +4 -18
  186. package/dist/web/ui/step-timeline.js +14 -1
  187. package/package.json +3 -2
  188. package/scripts/build-web.ts +16 -1
@@ -18,6 +18,7 @@
18
18
  import * as fs from 'fs/promises';
19
19
  import * as os from 'os';
20
20
  import * as path from 'path';
21
+ import { createHash } from 'crypto';
21
22
  import { execFile } from 'child_process';
22
23
  import { promisify } from 'util';
23
24
  const execFileAsync = promisify(execFile);
@@ -214,10 +215,8 @@ function collectEnv() {
214
215
  let llmProvider = 'unknown';
215
216
  try {
216
217
  const home = process.env.HOME || os.homedir() || '/tmp';
217
- // 2026-08-07: bolloon-config.json 优先, 旧 llm-config.json 兜底
218
- let cfgPath = path.join(home, '.bolloon', 'bolloon-config.json');
219
- if (!require('fs').existsSync(cfgPath))
220
- cfgPath = path.join(home, '.bolloon', 'llm-config.json');
218
+ // 2026-10-06 技术债清理: 只读 canonical bolloon-config.json (旧 llm-config.json 已废弃)
219
+ const cfgPath = path.join(home, '.bolloon', 'bolloon-config.json');
221
220
  const cfg = require(cfgPath);
222
221
  if (cfg && typeof cfg === 'object' && 'provider' in cfg) {
223
222
  llmProvider = String(cfg.provider);
@@ -332,16 +331,77 @@ export async function collectBolloonContext(opts) {
332
331
  };
333
332
  }
334
333
  // ============================================================
335
- // 24h 缓存 (跟类 B 一致)
334
+ // 24h 缓存 (跟类 B 一致) + mtime 指纹自动失效 (2026-10-15)
336
335
  // ============================================================
337
336
  let cached = null;
338
337
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
338
+ /**
339
+ * 计算缓存失效指纹: 对「会改变上下文内容」的源文件取 mtime+size 摘要。
340
+ * 覆盖: hierarchy 4 层 (managed/user/project/local + AGENTS.md/CLAUDE.md/.claude/rules)
341
+ * + persona 目录 (当前 agent 的身份文档) + 缓存目标的项目根。
342
+ * 任何文件新增/删除/修改 → 指纹变化 → 缓存重算 (对齐 Hermes 的 invalidate_system_prompt:
343
+ * 文档在外改了, 下一轮使用就是新的, 不用等 24h 或手动 force)。
344
+ * 指纹不命中任何文件也返回确定性值 (永不 null), 防止空指纹恒等导致误缓存。
345
+ */
346
+ export async function contextFingerprint(cwd, home) {
347
+ const h = home ?? process.env.HOME ?? os.homedir() ?? '/tmp';
348
+ const files = new Set();
349
+ files.add(path.posix.join(cwd, 'Bolloon.md'));
350
+ files.add(path.posix.join(cwd, 'AGENTS.md'));
351
+ files.add(path.posix.join(cwd, 'CLAUDE.md'));
352
+ // hierarchy: 复用与 collectHierarchyLayers 相同的路径解析
353
+ try {
354
+ const { resolveUserPath, resolveProjectPaths } = await import('./context-hierarchy.js');
355
+ files.add(resolveUserPath(h));
356
+ const pp = resolveProjectPaths(cwd);
357
+ files.add(pp.project);
358
+ files.add(pp.local);
359
+ files.add(pp.projectRulesDir); // 目录: 看条目数
360
+ }
361
+ catch { /* 路径解析失败不影响指纹 */ }
362
+ // persona 目录: 当前 agent identity 文档 (全部 *.md / persona.json)
363
+ const activeAgent = process.env.BOLLOON_ACTIVE_AGENT_ID || '';
364
+ if (activeAgent) {
365
+ const safe = activeAgent.replace(/[^a-zA-Z0-9._-]/g, '_');
366
+ files.add(path.posix.join(h, '.bolloon', 'persona', safe));
367
+ }
368
+ const parts = [];
369
+ for (const f of files) {
370
+ try {
371
+ const st = await fs.stat(f);
372
+ if (st.isDirectory()) {
373
+ // 目录内容指纹: 逐条目 mtime+size (目录自身 mtime 不随内容变)
374
+ const entries = await fs.readdir(f, { withFileTypes: true });
375
+ const names = entries.map((e) => e.name).sort();
376
+ parts.push(`${f}:dir:${names.length}`);
377
+ for (const e of entries) {
378
+ try {
379
+ const es = await fs.stat(path.join(f, e.name));
380
+ parts.push(`${f}/${e.name}:${es.mtimeMs}:${es.size}`);
381
+ }
382
+ catch {
383
+ parts.push(`${f}/${e.name}:missing`);
384
+ }
385
+ }
386
+ }
387
+ else {
388
+ parts.push(`${f}:${st.mtimeMs}:${st.size}`);
389
+ }
390
+ }
391
+ catch {
392
+ parts.push(`${f}:missing`);
393
+ }
394
+ }
395
+ // 排序保证稳定 (Set 遍历顺序 = 插入序, 跨次一致; 排序是双保险)
396
+ return createHash('sha256').update(parts.sort().join('|')).digest('hex').slice(0, 16);
397
+ }
339
398
  export async function getCachedBolloonContext(opts, force = false) {
340
- if (!force && cached && cached.cwd === opts.cwd && Date.now() - cached.at < CACHE_TTL_MS) {
399
+ const fp = await contextFingerprint(opts.cwd);
400
+ if (!force && cached && cached.cwd === opts.cwd && cached.fingerprint === fp && Date.now() - cached.at < CACHE_TTL_MS) {
341
401
  return cached.ctx;
342
402
  }
343
403
  const ctx = await collectBolloonContext(opts);
344
- cached = { at: Date.now(), ctx, cwd: opts.cwd };
404
+ cached = { at: Date.now(), ctx, cwd: opts.cwd, fingerprint: fp };
345
405
  return ctx;
346
406
  }
347
407
  export function clearBolloonContextCache() {
@@ -71,7 +71,9 @@ async function readRuleFile(filePath) {
71
71
  }
72
72
  }
73
73
  /**
74
- * 读 project 层: 优先 Bolloon.md, fallback Bolloon.md, 最后尝试 .claude/rules/*.md
74
+ * 读 project 层: 优先 Bolloon.md, fallback Bolloon.md,
75
+ * 然后 AGENTS.md / CLAUDE.md (2026-10-12: Hermes/Claude Code 仓库约定文件, leo 要求对照 Hermes 主动加载),
76
+ * 最后尝试 .claude/rules/*.md
75
77
  */
76
78
  async function readProjectLayer(projectPath, projectRulesDir, maxChars) {
77
79
  // 1. Bolloon.md
@@ -83,6 +85,16 @@ async function readProjectLayer(projectPath, projectRulesDir, maxChars) {
83
85
  content = await readRuleFile(bolloonMdPath);
84
86
  if (content)
85
87
  return truncate(content, maxChars);
88
+ // 2b. 2026-10-12 (leo: 「开始的时候没有主动扫描加载这个仓库里面的技能, 看一下 hermes 怎么做的」):
89
+ // Hermes 启动会把当前仓库的 AGENTS.md / CLAUDE.md 自动装进 project context (仓库约定即技能/规则)。
90
+ // Bolloon 只认 Bolloon.md + .claude/rules, 漏了这两个最通用的约定文件 — 补上。
91
+ for (const f of ['AGENTS.md', 'CLAUDE.md']) {
92
+ const p = path.join(path.dirname(projectPath), f);
93
+ content = await readRuleFile(p);
94
+ if (content) {
95
+ return truncate(`### ${f}\n${content}`, maxChars);
96
+ }
97
+ }
86
98
  // 3. .claude/rules/*.md (合并所有)
87
99
  try {
88
100
  const entries = await fs.readdir(projectRulesDir, { withFileTypes: true });
@@ -12,6 +12,7 @@
12
12
  * 任何 channel 即使无 persona 文件也有纪律约束.
13
13
  */
14
14
  import { sanitizeForLlm } from '../agents/egress-sanitize.js';
15
+ import { createHash } from 'node:crypto';
15
16
  import * as fs from 'fs/promises';
16
17
  import * as os from 'os';
17
18
  import * as path from 'path';
@@ -23,6 +24,31 @@ const FILE_KEYS = ['soul', 'identity', 'project', 'user', 'agent', 'wiki'];
23
24
  export function sanitizeAgentId(id) {
24
25
  return id.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64);
25
26
  }
27
+ /**
28
+ * persona 源文件指纹 (2026-10-15, 对齐 Hermes invalidate_system_prompt):
29
+ * 对 `~/.bolloon/persona/<agentId>/{persona.json + 6 个 md}` 取 mtime+size 摘要。
30
+ * 任何文档增删改 → 指纹变 → 上层 (pi-sdk cachedPersonaSection) 该轮重建,
31
+ * 不再等 agent 重建/切 channel。无 persona 目录也返回确定性值 (不会恒等误缓存)。
32
+ */
33
+ export async function personaFingerprint(agentId, home) {
34
+ const h = home ?? process.env.HOME ?? os.homedir() ?? '/tmp';
35
+ const safeId = sanitizeAgentId(agentId);
36
+ const baseDir = path.join(h, '.bolloon', 'persona', safeId);
37
+ const files = [path.join(baseDir, 'persona.json')];
38
+ for (const k of FILE_KEYS)
39
+ files.push(path.join(baseDir, `${k}.md`));
40
+ const parts = [];
41
+ for (const f of files) {
42
+ try {
43
+ const st = await fs.stat(f);
44
+ parts.push(`${f}:${st.mtimeMs}:${st.size}`);
45
+ }
46
+ catch {
47
+ parts.push(`${f}:missing`);
48
+ }
49
+ }
50
+ return createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 16);
51
+ }
26
52
  /**
27
53
  * 读 ~/.bolloon/persona/<sanitizedAgentId>/{6 files}
28
54
  * 文件不存在 → 该字段 = ''
@@ -15,7 +15,6 @@ import { Static, render, Box, Text, useInput, useApp, useStdout } from 'ink';
15
15
  import { stripUiNoise, collapsePaste, stripBracketedPaste, looksLikePasteChunk, logPasteChunk, shouldSuppressMention, singleLine, logPopupEvent, PASTE_MENTION_SHIELD_MS } from './input-paste.js';
16
16
  import TextInput from 'ink-text-input';
17
17
  import { dispWidth, LOADING_FRAMES as KAOMOJI } from './loading-tui.js';
18
- import { reasoningMode } from './reasoning-view.js';
19
18
  import { THEME, fg } from './theme.js';
20
19
  import { COMPOSER_PLACEHOLDER, POPUP_TITLE_TAB, POPUP_TITLE_AGENT, POPUP_TITLE_FILE, POPUP_TITLE_COMMAND } from './content.js';
21
20
  import { DOUBLE_ESC_MS, STATUS_TICK_MS, THINK_FRAME_MS } from './timing.js';
@@ -389,30 +388,48 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
389
388
  // 2026-09-08 (Hermes TUI 学习落地): 实时终端尺寸 — 原来 terminalW/H 在 mount 时冻结,
390
389
  // 终端 resize 后分隔线/logo 宽度全错位 (Hermes 用 resizeCoalescer + 实时 layout)。
391
390
  // 订阅 stdout resize, 每次渲染用最新列数。
391
+ // 2026-10-15 (leo 拖动侧边栏): **变窄**时终端把旧帧满宽行自动折行,
392
+ // Ink 的 eraseLines(lastOutputHeight) 按**未折行**行数擦 ⇒ 擦不干净 ⇒ 旧 footer/分隔线/
393
+ // 思考行残影被推进滚动区; 且 `<Static>` 历史区写进 scrollback 后不会重绘, 残影永远留在那里.
394
+ // 修法 (只对**变窄**): resize 停止 150ms 后整屏重建 —
395
+ // ① 发 `ESC[2J ESC[3J ESC[H` (清可视区 + 清 scrollback + 光标归位) 把残影彻底清掉;
396
+ // ② suspendInk + resumeInk 重挂全新 InkApp: `<Static>` 首次挂载对 store 全量重放
397
+ // (历史一行不少, 按新宽度重新 wrap), footer 按新宽度重画。
398
+ // 变宽不会折行 (旧行宽 ≤ 旧终端宽 < 新终端宽), Ink 自己重画即可, 不重建。
392
399
  const { stdout } = useStdout();
393
400
  const [termSize, setTermSize] = useState({ w: terminalW, h: terminalH });
394
401
  useEffect(() => {
395
- // resizeCoalescer (#9): 拖拽终端时会连发 resize — 聚合到 80ms 空闲后一次性应用, 避免中间态跳帧
396
- let raf = null;
397
- let pending = null;
398
- const apply = () => {
399
- if (pending) {
400
- const p = pending;
401
- pending = null;
402
- setTermSize(p);
402
+ let lastWidth = stdout?.columns || terminalW;
403
+ let rebuildTimer = null;
404
+ const rebuild = () => {
405
+ rebuildTimer = null;
406
+ try {
407
+ // 残影在 scrollback 里 —— eraseLines 够不到, 必须整屏清掉再让 Ink 全量重放
408
+ process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
409
+ suspendInk();
410
+ // 让 React 先完成 unmount 清理, 再重挂 (避免新旧实例抢 stdout)
411
+ setTimeout(() => { try {
412
+ resumeInk();
413
+ }
414
+ catch { /* 重建失败不致命, 下次 resize 再试 */ } }, 0);
403
415
  }
404
- raf = null;
416
+ catch { /* 清屏/重建失败不阻塞 resize 本身 */ }
405
417
  };
406
418
  const update = () => {
407
- pending = { w: stdout?.columns || terminalW, h: stdout?.rows || terminalH };
408
- if (raf)
409
- clearTimeout(raf);
410
- raf = setTimeout(apply, 80);
419
+ const w = stdout?.columns || terminalW;
420
+ setTermSize({ w, h: stdout?.rows || terminalH });
421
+ if (w < lastWidth) {
422
+ // 变窄: 折行残影无法增量修复, 停止拖动后整屏重建
423
+ if (rebuildTimer)
424
+ clearTimeout(rebuildTimer);
425
+ rebuildTimer = setTimeout(rebuild, 150);
426
+ }
427
+ lastWidth = w;
411
428
  };
412
429
  update();
413
430
  stdout?.on?.('resize', update);
414
- return () => { stdout?.removeListener?.('resize', update); if (raf)
415
- clearTimeout(raf); };
431
+ return () => { if (rebuildTimer)
432
+ clearTimeout(rebuildTimer); stdout?.removeListener?.('resize', update); };
416
433
  // eslint-disable-next-line react-hooks/exhaustive-deps
417
434
  }, [stdout]);
418
435
  const W = termSize.w;
@@ -509,6 +526,16 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
509
526
  setTimeout(() => process.exit(0), 2000);
510
527
  };
511
528
  if (key.ctrl && _input === 'c') {
529
+ // 2026-10-13 (leo: 「按一次 Ctrl+C 是中断当前对话; 按两次才是完整退出; 空闲时按一次就退出」):
530
+ // 走 index.ts 注册的两级语义处理器 —— 有回合在跑且未 armed → 中断回合 (abort kernel actor);
531
+ // 第二次 / 空闲 → 完整退出。Ink raw mode 下 Ctrl+C 被 useInput 拦截, 不会产生 SIGINT,
532
+ // 所以**这里**才是 Ctrl+C 的主入口 (进程级 SIGINT 只是启动早期兜底)。
533
+ const sigintHandler = globalThis.__bolloonCliSigint;
534
+ if (typeof sigintHandler === 'function') {
535
+ sigintHandler();
536
+ return;
537
+ }
538
+ // 兜底: 处理器没注册 (极早期) → 直接退出, 不吞 Ctrl+C
512
539
  requestExit();
513
540
  return;
514
541
  }
@@ -839,7 +866,7 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
839
866
  }
840
867
  catch { /* 探针写不进去绝不影响界面 */ }
841
868
  });
842
- return (_jsxs(_Fragment, { children: [_jsx(Static, { items: msgs, children: (m, i) => _jsx(Text, { children: m }, `m${i}`) }), _jsxs(Box, { flexDirection: "column", width: budget.cols, children: [hasRails && (_jsx(Box, { flexDirection: "column", children: railNames.map((n) => _jsx(Text, { color: THEME.muted, children: `${n}: ${String(widgets[n] ?? '').replace(/\n/g, ' ')}` }, n)) })), popupOpenNow && tabState && (_jsx(MentionPopup, { title: POPUP_TITLE_TAB, items: filtered, sel: safeSel, width: W, maxRows: budget.popupRows })), popupOpenNow && !tabState && mention && (_jsx(MentionPopup, { title: popupTitle, items: filtered, sel: safeSel, width: W, loading: loadingFiles, maxRows: budget.popupRows })), popupOpenNow && !tabState && !mention && picker && (_jsx(MentionPopup, { title: picker.title, items: picker.items, sel: Math.min(picker.sel, picker.items.length - 1), width: W, maxRows: budget.popupRows })), _jsx(Box, { height: 1, width: budget.cols, overflow: "hidden", children: transient ? (_jsx(Text, { children: transient })) : thinking ? (_jsx(Text, { color: "yellow", children: reasoningMode() === 'trace' ? '思考中...' : `${KAOMOJI[thinkingIdx.current]} 思考中...` })) : (_jsx(Text, { color: THEME.muted, children: '· 回车发送 · ↑↓ 历史 · PgUp 回看 · Esc 双击退出' })) }), _jsx(Box, { width: budget.cols, height: 1, overflow: "hidden", children: _jsx(Text, { bold: true, color: THEME.accent, children: ruleFor(Math.max(10, budget.cols - 1)) }) }), _jsx(Box, { height: 1, width: budget.cols, overflow: "hidden", children: _jsx(Text, { children: status }) }), _jsx(Box, { width: budget.cols, height: 1, overflow: "hidden", children: _jsx(Text, { bold: true, color: THEME.accent, children: ruleFor(Math.max(10, budget.cols - 1)) }) }), _jsxs(Box, { width: budget.cols, height: 1, overflow: "hidden", children: [_jsx(Text, { bold: true, color: THEME.accent, children: "\u276F " }), _jsx(Box, { width: Math.max(10, budget.cols - 2), height: 1, overflow: "hidden", flexShrink: 0, children: _jsx(TextInput, { value: singleLine(input), onChange: setInput, onSubmit: onSubmit, focus: !popupOpenNow, placeholder: COMPOSER_PLACEHOLDER }, tiKey) })] }), _jsx(Box, { width: budget.cols, height: 1, overflow: "hidden", children: _jsx(Text, { bold: true, color: THEME.accent, children: ruleFor(Math.max(10, budget.cols - 1)) }) })] })] }));
869
+ return (_jsxs(_Fragment, { children: [_jsx(Static, { items: msgs, children: (m, i) => _jsx(Text, { children: m }, `m${i}`) }), _jsxs(Box, { flexDirection: "column", width: budget.cols, children: [hasRails && (_jsx(Box, { flexDirection: "column", children: railNames.map((n) => _jsx(Text, { color: THEME.muted, children: `${n}: ${String(widgets[n] ?? '').replace(/\n/g, ' ')}` }, n)) })), popupOpenNow && tabState && (_jsx(MentionPopup, { title: POPUP_TITLE_TAB, items: filtered, sel: safeSel, width: W, maxRows: budget.popupRows })), popupOpenNow && !tabState && mention && (_jsx(MentionPopup, { title: popupTitle, items: filtered, sel: safeSel, width: W, loading: loadingFiles, maxRows: budget.popupRows })), popupOpenNow && !tabState && !mention && picker && (_jsx(MentionPopup, { title: picker.title, items: picker.items, sel: Math.min(picker.sel, picker.items.length - 1), width: W, maxRows: budget.popupRows })), _jsx(Box, { height: 1, width: budget.cols, overflow: "hidden", children: transient ? (_jsx(Text, { children: transient })) : thinking ? (_jsxs(Text, { color: "yellow", children: [KAOMOJI[thinkingIdx.current], " \u601D\u8003\u4E2D..."] })) : (_jsx(Text, { color: THEME.muted, children: '· 回车发送 · ↑↓ 历史 · PgUp 回看 · Esc 双击退出' })) }), _jsx(Box, { width: budget.cols, height: 1, overflow: "hidden", children: _jsx(Text, { bold: true, color: THEME.accent, children: ruleFor(Math.max(10, budget.cols - 1)) }) }), _jsx(Box, { height: 1, width: budget.cols, overflow: "hidden", children: _jsx(Text, { children: status }) }), _jsx(Box, { width: budget.cols, height: 1, overflow: "hidden", children: _jsx(Text, { bold: true, color: THEME.accent, children: ruleFor(Math.max(10, budget.cols - 1)) }) }), _jsxs(Box, { width: budget.cols, height: 1, overflow: "hidden", children: [_jsx(Text, { bold: true, color: THEME.accent, children: "\u276F " }), _jsx(Box, { width: Math.max(10, budget.cols - 2), height: 1, overflow: "hidden", flexShrink: 0, children: _jsx(TextInput, { value: singleLine(input), onChange: setInput, onSubmit: onSubmit, focus: !popupOpenNow, placeholder: COMPOSER_PLACEHOLDER }, tiKey) })] }), _jsx(Box, { width: budget.cols, height: 1, overflow: "hidden", children: _jsx(Text, { bold: true, color: THEME.accent, children: ruleFor(Math.max(10, budget.cols - 1)) }) })] })] }));
843
870
  };
844
871
  export { InkApp };
845
872
  // ─── 启动 ────────────────────────────────────────────────────────────────────
@@ -172,14 +172,26 @@ export async function opportunityCommand(flags) {
172
172
  human: INTENT_USAGE,
173
173
  };
174
174
  }
175
- function cardRows(opts) {
176
- const pct = Math.round(opts.score * 100);
177
- const tag = opts.reason === 'match' ? '● 与你匹配' : (opts.reason === 'inbox' ? '✉ 外部投递' : (opts.reason === 'search' ? '🔎 AI 搜索' : '○ 世界变化'));
178
- return [
179
- ` ${pct}% ${tag} ${opts.title}`,
180
- ` ${opts.summary}`,
181
- ` 预算: ${opts.budget ?? '未知'} · id=${opts.id}`,
182
- ];
175
+ /** 2026-10-06 (用户: 世界模式用表格呈现): ASCII 表格机会列表 — 列 = 分数/来源/标题/预算/id */
176
+ export function opportunityTable(results) {
177
+ if (!results.length)
178
+ return [' (没有机会)'];
179
+ const reasonShort = (r) => (r === 'match' ? '匹配' : r === 'inbox' ? '投递' : r === 'search' ? '搜索' : '世界');
180
+ const rows = results.map((o) => {
181
+ const pct = Math.round(o.score * 100);
182
+ const title = o.title.length > 32 ? o.title.slice(0, 31) + '…' : o.title;
183
+ return [String(pct) + '%', reasonShort(o.reason), title, (o.budget ?? '无').slice(0, 14), o.id];
184
+ });
185
+ const w = (idx, min) => Math.max(min, ...rows.map((r) => r[idx].length));
186
+ const wS = w(0, 5), wR = w(1, 4), wT = w(2, 32), wB = w(3, 6), wId = w(4, 8);
187
+ const sep = ` +${'-'.repeat(wS)}+${'-'.repeat(wR)}+${'-'.repeat(wT)}+${'-'.repeat(wB)}+${'-'.repeat(wId)}+`;
188
+ const head = ` | ${'分数'.padStart(wS - 2)} | ${'来源'.padEnd(wR)} | ${'标题'.padEnd(wT)} | ${'预算'.padEnd(wB)} | ${'ID'.padEnd(wId)} |`;
189
+ const line = (c) => ` | ${c[0].padStart(wS)} | ${c[1].padEnd(wR)} | ${c[2].padEnd(wT)} | ${c[3].padEnd(wB)} | ${c[4].padEnd(wId)} |`;
190
+ const out = [sep, head, sep];
191
+ for (const r of rows)
192
+ out.push(line(r));
193
+ out.push(sep);
194
+ return out;
183
195
  }
184
196
  async function opportunityScan(flags) {
185
197
  const head = 'bolloon opportunity scan';
@@ -201,8 +213,7 @@ async function opportunityScan(flags) {
201
213
  }
202
214
  const data = { count: r.results.length, minScore, opportunities: r.results };
203
215
  const human = [title(head), ` 找到 ${r.results.length} 个机会 (score ≥ ${minScore}):`, ''];
204
- for (const o of r.results)
205
- human.push(...cardRows(o));
216
+ human.push(...opportunityTable(r.results));
206
217
  human.push('', hint('用 bolloon opportunity accept <id> 确认, ignore <id> 忽略'));
207
218
  return { envelope: okEnvelope('OK', `${r.results.length} 个机会`, data, r.results.map((x) => x.id), null), human: human.join('\n') };
208
219
  }
@@ -219,8 +230,7 @@ async function opportunityList(flags) {
219
230
  if (!hits.length)
220
231
  return { envelope: okEnvelope('OK', `意图「${i.text}」暂无匹配`, { count: 0, opportunities: [] }, [], null), human: `${title(head)}\n 「${i.text}」暂无匹配机会` };
221
232
  const human = [title(head), ` 「${i.text}」的 ${hits.length} 个机会:`, ''];
222
- for (const o of hits)
223
- human.push(...cardRows(o));
233
+ human.push(...opportunityTable(hits));
224
234
  return { envelope: okEnvelope('OK', `${hits.length} 个机会`, { count: hits.length, opportunities: hits }, hits.map((x) => x.id), null), human: human.join('\n') };
225
235
  }
226
236
  return opportunityScan(flags);
@@ -12,6 +12,7 @@
12
12
  * LoadingTUI 原地刷新的启动仪表盘类
13
13
  */
14
14
  import { truncateSafe } from './status-segments.js';
15
+ import { toolArgsSummary } from './trace-line.js';
15
16
  import * as fs from 'fs';
16
17
  import * as path from 'path';
17
18
  import { fileURLToPath } from 'url';
@@ -433,12 +434,17 @@ function colorizeDiff(text) {
433
434
  });
434
435
  return out.join('\n');
435
436
  }
436
- /** 渲染单条工具调用为紧凑列表项 (增量显示, 一行, 无 args) */
437
+ /** 渲染单条工具调用为紧凑列表项 (增量显示, 一行, 参数摘要) */
437
438
  export function renderToolCallListItem(item, index, total) {
438
439
  const dur = item.durationMs != null ? ` ${C_DIM}${item.durationMs}ms${RESET}` : '';
439
440
  const label = item.status === 'error' ? `${C_ERROR}${item.tool}${RESET}` : `${C_ACCENT}${item.tool}${RESET}`;
441
+ // 2026-10-14 (leo: 「调用工具的时候, 把执行的命令缩写也附带上 —— 现在看不到过程」):
442
+ // 工具行附上参数摘要 (terminal→命令 / read_file→路径 / grep→pattern·dir), 灰色、截断;
443
+ // 看不清过程的问题 = 之前只有工具名, 不知道它具体跑了什么。
444
+ const sum = toolArgsSummary(item.tool, item.args);
445
+ const sumPart = sum ? ` ${C_DIM}${sum}${RESET}` : '';
440
446
  // 2026-10-01: 工具行也按**保守宽度**截断 —— 超宽会让终端折行、打乱 Ink 光标 (底栏那块就是这么坏的)
441
- return truncateSafe(` 🔧 ${label}${dur}`, Math.max(20, (process.stdout.columns || 100) - 1));
447
+ return truncateSafe(` 🔧 ${label}${sumPart}${dur}`, Math.max(20, (process.stdout.columns || 100) - 1));
442
448
  }
443
449
  /** 紧凑渲染工具输出 (检测到 diff 则着色, 否则普通截断) — 当前不显示 body */
444
450
  export function renderToolCallBody(item, width = 72) {
@@ -75,15 +75,20 @@ const HUMAN_SIGNAL = new RegExp([
75
75
  * 而 `N 个转人工` (N>0) 这种真要人介入的行照样保留。
76
76
  */
77
77
  const BENIGN_COUNT = /0\s*(?:个|条)(?:非致命)?(?:错误|转人工|失败|异常)|no errors?|0 errors?/gi;
78
+ /** 剥 ANSI 色码 (行首标签判据的前提 —— 真实输出带 `\u001b[90m` 等前缀, 不剥则 `[tag]` 全失配) */
79
+ function stripAnsi(s) {
80
+ return s.replace(/\x1b\[[0-9;]*m/g, '');
81
+ }
78
82
  /** 这一行是不是「加载日志」(默认模式下从控制台丢掉, 写进日志文件) */
79
83
  export function isStartupLogLine(line) {
80
84
  if (!line)
81
85
  return false;
82
- return MODULE_TAG_ASCII.test(line)
83
- || MODULE_TAG_CJK.test(line)
84
- || ISO_TIMESTAMP_LINE.test(line)
85
- || INSPECT_DUMP_OPEN.test(line)
86
- || UNTAGGED_STARTUP_NARRATION.some((re) => re.test(line));
86
+ const l = stripAnsi(line);
87
+ return MODULE_TAG_ASCII.test(l)
88
+ || MODULE_TAG_CJK.test(l)
89
+ || ISO_TIMESTAMP_LINE.test(l)
90
+ || INSPECT_DUMP_OPEN.test(l)
91
+ || UNTAGGED_STARTUP_NARRATION.some((re) => re.test(l));
87
92
  // inspect dump 的续行 (纯数字/逗号) 由 filterText 的 dump 状态机处理, 这里不判
88
93
  }
89
94
  /**
@@ -129,7 +134,7 @@ const SDK_DIAG_LINE = /^(?:\[[^\]]*\]\s*)?\d{4}-\d{2}-\d{2}T[\d:.]+Z?\s*\[(?:\x1
129
134
  * 这是把「要不要占屏」和「要不要留痕」分开, 不是把问题藏起来。
130
135
  */
131
136
  // 允许标签前有一个**严重度字形** (`⚠ [identity] …` / `✗ [session-note] …` 真机就是这样) —— 只放这几个字形, 不做通配
132
- const INTERNAL_CHATTER_TAG = /^[ \t]{0,8}[⚠✗✓!·]?[ \t]*\[(?:pi-ai(?:\s+timing)?|identity|session-note|react-harness|DocumentStore|DocumentReceiver|kv-server|loadSkills|v3-manifest|v3-async|supervisor-host|tool-telemetry|runs|abort|broadcast)\b[^\]\n]*\]/i;
137
+ const INTERNAL_CHATTER_TAG = /^[ \t]{0,8}[⚠✗✓!·]?[ \t]*\[(?:pi-ai(?:\s+timing)?|identity|session-note|react-harness|DocumentStore|DocumentReceiver|kv-server|loadSkills|v3-manifest|v3-async|supervisor-host|tool-telemetry|runs|abort|broadcast|Heartbeat|llm-lesson|llm-recovery)\b[^\]\n]*\]/i;
133
138
  /**
134
139
  * 没有 ASCII 标签、但实测属于内部运行日志的行 (2026-10-02 leo 追加: 「✅ 最终回复 (质量 8.5/10) ·
135
140
  * 🔄 循环 N/10 这类也去掉」)。
@@ -140,14 +145,34 @@ const INTERNAL_CHATTER_TAG = /^[ \t]{0,8}[⚠✗✓!·]?[ \t]*\[(?:pi-ai(?:\s+ti
140
145
  */
141
146
  const INTERNAL_CHATTER_LINE = [
142
147
  /^\s*📚\s*复盘[::]/,
143
- /^\s*\[PiAgent\]\s*reviewFinal\b/,
144
- /^\s*\[PiAgent\]\s*推理适配器\s*=/,
148
+ /^\s*[⚠✗✓!·]?\s*\[PiAgent\]\s*(?:reviewFinal|推理适配器|loop\s*自动重试)/,
145
149
  /^\s*[✅✓]\s*(?:检测到)?最终回复/,
146
150
  /^\s*🔄\s*循环\s*\d+\s*[//]\s*\d+/,
147
151
  /^\s*🔍\s*任务复杂度[::]/,
148
152
  /^\s*⚙️?\s*动态配置[::]/,
149
153
  /^\s*⏹️?\s*pivot loop/,
150
154
  /^\s*📋\s*参数/,
155
+ // 2026-10-12 (leo: 「还有很多杂质在 cli 里面」): 启动进度步 `✓ [3/5] 启动 P2P 网络`
156
+ // 与身份解析续行 ` name=小龙` —— 二者不带模块标签, 经 `s.step()` / 长 console 行拆分直写,
157
+ // 旧判据 (只认模块 tag / 📚 复盘等具体形状) 漏网。行首锚定, 不宽泛。
158
+ /^\s*[✅✓⚠✗]?\s*\[\d+\s*[//]\s*\d+\]\s*(?:启动|初始化|加载|连接|同步|验证|发布|注册|开启|构建)/,
159
+ /^\s*name=\S+$/,
160
+ // 2026-10-12: `[PiAgent] reviewFinal` 的 JSON 诊断含换行 → 续行裸文本上屏
161
+ // (`前必须展示具体证据 (命令输出 / 文件内容 / 测试结果), 只写结论不算完成.`)。
162
+ // 发射点已改为不打印完整 hint; 这里兜底旧构建/其它多行 console 的续行。
163
+ // `<final gen>` / `[完成契约]` / `(尚未执行工具)` 是系统契约独有标记, 用户正文不会以此开头。
164
+ /^\s*前必须展示具体证据(?=\s|[()、。)])/,
165
+ /^\s*宣布\s*<final\s*gen>/,
166
+ /^\s*\[完成契约\]/,
167
+ /^\s*\(尚未执行工具\)/,
168
+ // 2026-10-15 (leo: 「[AI 服务调用失败] 这个需要过滤掉」): 模型调用失败的占位回复 —
169
+ // pi-ai 的 catch 分支返回 `[AI 服务调用失败] <errMsg>\n\n这是一个**底层 API 错误**...` 作为
170
+ // reply, 后段是给 pivot loop 下一轮 LLM 看的指令 (该不该继续), 不是给用户的内容。
171
+ // 整段不该作为回复框进内容栏 — 落盘 + footer tip 就够 (详情在 startup.log 可 grep 前缀)。
172
+ /^\s*\[AI 服务调用失败\]/,
173
+ // 2026-10-15 同批: pi-ai chat/improve 失败告警改单行标签形 (`[pi-ai] chat 失败: ...`),
174
+ // 兜底旧构建打印的裸 `PiAI chat error: ...` (用户终端实测带 ✗/⚠ 前缀也会漏上屏)。
175
+ /^\s*[⚠✗!·]?\s*PiAI\s+(?:chat|improve)\s+error[::]/,
151
176
  ];
152
177
  /**
153
178
  * 循环过渡噪音: **行首锚定**, 不算任意子串 —— 老黑名单用的是 `content.includes('继续循环')`,
@@ -323,6 +348,13 @@ export function installStartupLogGate(opts = {}) {
323
348
  dumpState[stream] = false;
324
349
  return 'loading';
325
350
  }
351
+ // 2026-10-12 (leo: 「还有很多杂质在 cli 里面」): **内部诊断行优先判** ——
352
+ // `[pi-ai timing]` / `[identity]` / `📚 复盘` / `[session-note]` 经 `process.stdout.write`
353
+ // 直写时(不走 console 包装), 旧代码只查 `isStartupLogLine`+`carriesHumanSignal`,
354
+ // 前两者被 ANSI 色码挡掉、后者被 ⚠/✗ 当"人味信号"放行 ⇒ 全部漏上屏。
355
+ // 现在与 makeConsoleFn 同一口径: 内部行即使带 ⚠/✗ 也只落盘, 不进对话流。
356
+ if (isInternalChatter(line))
357
+ return 'loading';
326
358
  if (carriesHumanSignal(line))
327
359
  return 'signal';
328
360
  if (isStartupLogLine(line)) {
@@ -53,6 +53,7 @@ const CLI_COMMANDS = [
53
53
  { kind: 'command', label: 'judgments', hint: '判断记录', insert: 'judgments' },
54
54
  { kind: 'command', label: 'net', hint: 'Agent 网络 (join/status/ctx)', insert: 'net' },
55
55
  { kind: 'command', label: 'opportunity', hint: '世界机会 (scan/list/accept/ignore)', insert: 'opportunity' },
56
+ { kind: 'command', label: 'chat', hint: '聊天模式 (回到主对话)', insert: 'chat' },
56
57
  { kind: 'command', label: 'p2p', hint: 'P2P 节点 / 连接', insert: 'p2p' },
57
58
  { kind: 'command', label: 'pause', hint: '暂停当前 run', insert: 'pause' },
58
59
  { kind: 'command', label: 'payments', hint: '支付 / 任务公告', insert: 'payments' },
@@ -39,6 +39,28 @@ export function appendInternalRunLog(content, tool) {
39
39
  /* 落盘失败绝不影响对话本身 */
40
40
  }
41
41
  }
42
+ /**
43
+ * 内部运行日志 → footer 瞬时提示 (2026-10-14, leo: 「这些提示可以放在 tips/提示栏, 动画效果设计好,
44
+ * 不能放在内容栏, 只提示内容不带前缀」)。
45
+ *
46
+ * 例: `⛔ loop 自动重试 3 次后仍失败: xxx` → `loop 自动重试 3 次后仍失败, 终止提示`
47
+ * 规则: 只挑「用户该感知的执行状态」(重试/超时/失败), 只保留内容本体 ——
48
+ * 剥掉 ⛔/⏳ 等前缀字形, 不显示 `[llm-lesson]` 这类模块标签 (那是 console 通道的行, 不走这里)。
49
+ * return: 要显示在 footer 的瞬时文本; null = 不需要提示 (纯进度遥测不上 tips)。
50
+ */
51
+ export function tipFromInternalRunLog(content) {
52
+ const t = String(content || '').trim();
53
+ const retryLike = /(?:重试|超时|自动重试|回退|降级)/.test(t);
54
+ const failLike = /(?:失败|终止)/.test(t);
55
+ if (!retryLike && !failLike)
56
+ return null;
57
+ // 剥 ANSI + 行首前缀字形
58
+ const plain = t
59
+ .replace(/\x1b\[[0-9;]*m/g, '')
60
+ .replace(/^[⛔⏳🔄↻⚠✗✓!·]+\s*/, '')
61
+ .trim();
62
+ return plain || null;
63
+ }
42
64
  /**
43
65
  * 这条 stream 事件是不是**内部运行日志** —— 是则不进对话回复流, 只落日志文件。
44
66
  *
@@ -26,8 +26,12 @@ const ACTIONS = [
26
26
  [/^execute_code$/, '跑代码'],
27
27
  [/^tsc_check$/, '查类型'],
28
28
  [/^list_tools$/, '看有哪些工具'],
29
- [/^list_skills$/, '找技能'],
30
- [/^read_skill$/, '读技能'],
29
+ // 2026-10-13 (leo: 「加载技能记忆类似工具调用… 在看到技能之后运行这个技能, 然后在上下文中就可以看到了」):
30
+ // 技能加载走 📚 前缀 —— Hermes 的 skill_view 就是 `📚 skill <名> 7.5s`, 一眼可辨"这是技能/记忆按需加载",
31
+ // 不是普通工具调用; 结果(技能全文)进 history ⇒ 模型上下文里能看到, 正是 leo 要的"持续进程"。
32
+ [/^list_skills$/, '📚 找技能'],
33
+ [/^read_skill$/, '📚 读技能'],
34
+ [/^memory_(recall|search|query|load)$/, '📚 取记忆'],
31
35
  [/^process$/, '管后台进程/服务'],
32
36
  [/^task|^plan|^todo/i, '理计划/待办'],
33
37
  [/^send_|^broadcast|^check_inbox|^list_peers/, '发消息/看连接'],
@@ -52,3 +56,58 @@ export function traceLabel(name) {
52
56
  const d = describeToolCall(name);
53
57
  return d ? `${d} · ${name}` : name;
54
58
  }
59
+ /**
60
+ * 工具参数摘要 (2026-10-14, leo: 「调用工具的时候, 把执行的命令缩写也附带上 —— 现在看不到过程」)。
61
+ * 从 args 里挑「人看到就懂的」关键字段, 截断成一行的命令缩写:
62
+ * terminal(command='npx vitest run src/test/x') → `npx vitest run src/test/x`
63
+ * read_file(path='src/index.ts') → `src/index.ts`
64
+ * grep_files(pattern='foo', path='src') → `foo · src`
65
+ * 认不出该工具的参数形态 → 返回空 (工具行不带摘要, 保持现状)。
66
+ */
67
+ export function toolArgsSummary(name, args) {
68
+ const n = String(name ?? '').trim();
69
+ // string 形态 (如 JSON 串) 不解析摘要 —— 只要对象形态 (step_* 事件是对象)
70
+ const a = args && typeof args === 'object' ? args : {};
71
+ if (!n)
72
+ return '';
73
+ const pick = (keys) => {
74
+ for (const k of keys) {
75
+ const v = a[k];
76
+ if (v === undefined || v === null)
77
+ continue;
78
+ const s = String(v).trim();
79
+ if (s && s !== 'undefined')
80
+ return s;
81
+ }
82
+ return '';
83
+ };
84
+ let summary = '';
85
+ if (/^terminal$/.test(n)) {
86
+ summary = pick(['command', 'cmd', 'script']);
87
+ }
88
+ else if (/^(read_file|read_document|write_file|read_directory|list_files)$/.test(n)) {
89
+ summary = pick(['path', 'filePath', 'file', 'dir', 'directory']);
90
+ }
91
+ else if (/^(grep_files|search_files)$/.test(n)) {
92
+ const p = pick(['pattern', 'query']);
93
+ const d = pick(['path', 'dir']);
94
+ summary = [p, d].filter(Boolean).join(' · ');
95
+ }
96
+ else if (/^(git_log|git_diff|git_status|git_commit|git_push)$/.test(n)) {
97
+ summary = pick(['args', 'message', 'scope']);
98
+ }
99
+ else if (/^(memory_recall|memory_search)$/.test(n)) {
100
+ summary = pick(['query']);
101
+ }
102
+ else if (/^(tsc_check|vitest_run|npm_run)$/.test(n)) {
103
+ summary = pick(['args', 'script', 'command']);
104
+ }
105
+ else if (/^process$/.test(n)) {
106
+ summary = pick(['name', 'command']);
107
+ }
108
+ // 截断到工具行友好长度 (保留头尾关键信息)
109
+ const s = summary.trim();
110
+ if (!s)
111
+ return '';
112
+ return s.length > 56 ? `${s.slice(0, 48)}…${s.slice(-6)}` : s;
113
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-gateway.js","sourceRoot":"","sources":["../../../src/agents/agent-gateway.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-gateway.js","sourceRoot":"","sources":["../../../src/agents/agent-gateway.ts"],"names":[],"mappings":"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"}