@bolloon/bolloon-agent 0.5.5 → 0.6.1

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 (158) hide show
  1. package/dist/agents/execution-entry.js +10 -0
  2. package/dist/agents/goal-flywheel/wiring/seams.js +5 -1
  3. package/dist/agents/parse-tool-call.js +6 -1
  4. package/dist/agents/pi-sdk-session-factory.js +36 -3
  5. package/dist/agents/pi-sdk-tools.js +18 -8
  6. package/dist/agents/pi-sdk.js +749 -1390
  7. package/dist/agents/run-context.js +20 -0
  8. package/dist/agents/runner-resolver.js +3 -1
  9. package/dist/agents/tool-capability/index.js +51 -0
  10. package/dist/agents/workflow-pivot-loop.js +31 -10
  11. package/dist/agents/write-verify.js +60 -0
  12. package/dist/bollharness-integration/integration.js +8 -0
  13. package/dist/bollharness-integration/skill-adapter.js +32 -3
  14. package/dist/electron-build/agents/goal-flywheel/wiring/seams.js +5 -1
  15. package/dist/electron-build/agents/goal-flywheel/wiring/seams.js.map +1 -1
  16. package/dist/electron-build/agents/parse-tool-call.js +6 -1
  17. package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
  18. package/dist/electron-build/agents/pi-sdk-session-factory.js +36 -3
  19. package/dist/electron-build/agents/pi-sdk-session-factory.js.map +1 -1
  20. package/dist/electron-build/agents/pi-sdk-tools.js +21 -11
  21. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  22. package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
  23. package/dist/electron-build/agents/pi-sdk.js +750 -1389
  24. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  25. package/dist/electron-build/agents/run-context.js +24 -0
  26. package/dist/electron-build/agents/run-context.js.map +1 -0
  27. package/dist/electron-build/agents/runner-resolver.js +3 -1
  28. package/dist/electron-build/agents/runner-resolver.js.map +1 -1
  29. package/dist/electron-build/agents/tool-capability/index.js +90 -0
  30. package/dist/electron-build/agents/tool-capability/index.js.map +1 -0
  31. package/dist/electron-build/agents/workflow-pivot-loop.js +31 -10
  32. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
  33. package/dist/electron-build/agents/write-verify.js +99 -0
  34. package/dist/electron-build/agents/write-verify.js.map +1 -0
  35. package/dist/electron-build/bollharness-integration/integration.js +8 -0
  36. package/dist/electron-build/bollharness-integration/integration.js.map +1 -1
  37. package/dist/electron-build/bollharness-integration/skill-adapter.js +32 -3
  38. package/dist/electron-build/bollharness-integration/skill-adapter.js.map +1 -1
  39. package/dist/electron-build/kernel/channel-actor.js +339 -0
  40. package/dist/electron-build/kernel/channel-actor.js.map +1 -0
  41. package/dist/electron-build/kernel/control.js +102 -0
  42. package/dist/electron-build/kernel/control.js.map +1 -0
  43. package/dist/electron-build/kernel/model-runtime.js +508 -0
  44. package/dist/electron-build/kernel/model-runtime.js.map +1 -0
  45. package/dist/electron-build/kernel/run-lifecycle.js +146 -0
  46. package/dist/electron-build/kernel/run-lifecycle.js.map +1 -0
  47. package/dist/electron-build/kernel/transport.js +119 -0
  48. package/dist/electron-build/kernel/transport.js.map +1 -0
  49. package/dist/electron-build/llm/native-adapter.js +122 -0
  50. package/dist/electron-build/llm/native-adapter.js.map +1 -0
  51. package/dist/index.js +43 -19
  52. package/dist/ios/bolloon-web.json +3 -3
  53. package/dist/ios/client.js +13 -7
  54. package/dist/ios/mobile-core.js +7762 -6289
  55. package/dist/ios/mobile.js +64 -0
  56. package/dist/ios/routes-mobile-tasks.js +2 -1
  57. package/dist/ios/routes-tasks.js +16 -1
  58. package/dist/ios/routes-x402-info.js +28 -1
  59. package/dist/ios/server-storage.js +26 -5
  60. package/dist/ios/server.js +840 -862
  61. package/dist/ios/sw.js +1 -1
  62. package/dist/ios/ui/message-renderer.js +473 -29
  63. package/dist/ios/ui/step-timeline.js +13 -12
  64. package/dist/kernel/channel-actor.js +321 -0
  65. package/dist/kernel/control.js +95 -0
  66. package/dist/kernel/gate-scan.js +1350 -0
  67. package/dist/kernel/model-runtime.js +494 -0
  68. package/dist/kernel/plan-channel-actor.js +305 -0
  69. package/dist/kernel/plan-communication.js +163 -0
  70. package/dist/kernel/plan-constraint.js +152 -0
  71. package/dist/kernel/plan-deletion.js +32 -0
  72. package/dist/kernel/plan-harness.js +78 -0
  73. package/dist/kernel/plan-modelruntime.js +60 -0
  74. package/dist/kernel/plan-runcontext.js +163 -0
  75. package/dist/kernel/plan.js +149 -0
  76. package/dist/kernel/roster.js +244 -0
  77. package/dist/kernel/run-lifecycle.js +138 -0
  78. package/dist/kernel/transport.js +111 -0
  79. package/dist/llm/native-adapter.js +117 -0
  80. package/dist/web/bolloon-web.json +3 -3
  81. package/dist/web/client.js +12 -7
  82. package/dist/web/routes-tasks.js +16 -1
  83. package/dist/web/server.js +838 -862
  84. package/dist/web/sw.js +1 -1
  85. package/dist/web/ui/message-renderer.js +473 -29
  86. package/dist/web/ui/step-timeline.js +13 -12
  87. package/package.json +1 -1
  88. package/scripts/build-web.ts +74 -1
  89. package/dist/constraint-runtime/_archive_helper.d.ts +0 -1
  90. package/dist/constraint-runtime/_archive_helper.js +0 -9
  91. package/dist/constraint-runtime/assistant/index.d.ts +0 -11
  92. package/dist/constraint-runtime/assistant/index.js +0 -12
  93. package/dist/constraint-runtime/bootstrap/index.d.ts +0 -11
  94. package/dist/constraint-runtime/bootstrap/index.js +0 -12
  95. package/dist/constraint-runtime/bridge/index.d.ts +0 -11
  96. package/dist/constraint-runtime/bridge/index.js +0 -12
  97. package/dist/constraint-runtime/buddy/index.d.ts +0 -11
  98. package/dist/constraint-runtime/buddy/index.js +0 -12
  99. package/dist/constraint-runtime/cli/index.d.ts +0 -11
  100. package/dist/constraint-runtime/cli/index.js +0 -12
  101. package/dist/constraint-runtime/components/index.d.ts +0 -11
  102. package/dist/constraint-runtime/components/index.js +0 -12
  103. package/dist/constraint-runtime/constants/index.d.ts +0 -11
  104. package/dist/constraint-runtime/constants/index.js +0 -12
  105. package/dist/constraint-runtime/coordinator/index.d.ts +0 -11
  106. package/dist/constraint-runtime/coordinator/index.js +0 -12
  107. package/dist/constraint-runtime/cost_hook.d.ts +0 -2
  108. package/dist/constraint-runtime/cost_hook.js +0 -4
  109. package/dist/constraint-runtime/entrypoints/index.d.ts +0 -11
  110. package/dist/constraint-runtime/entrypoints/index.js +0 -12
  111. package/dist/constraint-runtime/execution_registry.d.ts +0 -20
  112. package/dist/constraint-runtime/execution_registry.js +0 -44
  113. package/dist/constraint-runtime/hooks/index.d.ts +0 -11
  114. package/dist/constraint-runtime/hooks/index.js +0 -12
  115. package/dist/constraint-runtime/ink.d.ts +0 -1
  116. package/dist/constraint-runtime/ink.js +0 -4
  117. package/dist/constraint-runtime/keybindings/index.d.ts +0 -11
  118. package/dist/constraint-runtime/keybindings/index.js +0 -12
  119. package/dist/constraint-runtime/memdir/index.d.ts +0 -11
  120. package/dist/constraint-runtime/memdir/index.js +0 -12
  121. package/dist/constraint-runtime/migrations/index.d.ts +0 -11
  122. package/dist/constraint-runtime/migrations/index.js +0 -12
  123. package/dist/constraint-runtime/moreright/index.d.ts +0 -11
  124. package/dist/constraint-runtime/moreright/index.js +0 -12
  125. package/dist/constraint-runtime/native_ts/index.d.ts +0 -11
  126. package/dist/constraint-runtime/native_ts/index.js +0 -12
  127. package/dist/constraint-runtime/output_styles/index.d.ts +0 -11
  128. package/dist/constraint-runtime/output_styles/index.js +0 -12
  129. package/dist/constraint-runtime/plugins/index.d.ts +0 -11
  130. package/dist/constraint-runtime/plugins/index.js +0 -12
  131. package/dist/constraint-runtime/port_manifest.d.ts +0 -7
  132. package/dist/constraint-runtime/port_manifest.js +0 -11
  133. package/dist/constraint-runtime/query.d.ts +0 -6
  134. package/dist/constraint-runtime/query.js +0 -1
  135. package/dist/constraint-runtime/remote/index.d.ts +0 -11
  136. package/dist/constraint-runtime/remote/index.js +0 -12
  137. package/dist/constraint-runtime/schemas/index.d.ts +0 -11
  138. package/dist/constraint-runtime/schemas/index.js +0 -12
  139. package/dist/constraint-runtime/screens/index.d.ts +0 -11
  140. package/dist/constraint-runtime/screens/index.js +0 -12
  141. package/dist/constraint-runtime/server/index.d.ts +0 -11
  142. package/dist/constraint-runtime/server/index.js +0 -12
  143. package/dist/constraint-runtime/services/index.d.ts +0 -11
  144. package/dist/constraint-runtime/services/index.js +0 -12
  145. package/dist/constraint-runtime/state/index.d.ts +0 -11
  146. package/dist/constraint-runtime/state/index.js +0 -12
  147. package/dist/constraint-runtime/system_init.d.ts +0 -1
  148. package/dist/constraint-runtime/system_init.js +0 -20
  149. package/dist/constraint-runtime/types/index.d.ts +0 -11
  150. package/dist/constraint-runtime/types/index.js +0 -12
  151. package/dist/constraint-runtime/upstream_proxy/index.d.ts +0 -11
  152. package/dist/constraint-runtime/upstream_proxy/index.js +0 -12
  153. package/dist/constraint-runtime/utils/index.d.ts +0 -11
  154. package/dist/constraint-runtime/utils/index.js +0 -12
  155. package/dist/constraint-runtime/vim/index.d.ts +0 -11
  156. package/dist/constraint-runtime/vim/index.js +0 -12
  157. package/dist/constraint-runtime/voice/index.d.ts +0 -11
  158. package/dist/constraint-runtime/voice/index.js +0 -12
package/dist/ios/sw.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * 现在: 代码类资源 (html/js/css/json) 一律 **network-first** (拿不到网才回退缓存),
7
7
  * 缓存名带版本号, activate 时清掉所有旧缓存; 图标等静态资源仍 cache-first。
8
8
  */
9
- const CACHE = 'bolloon-mobile-v0.5.1';
9
+ const CACHE = 'bolloon-mobile-v0.6.1';
10
10
  const SHELL = [
11
11
  './mobile.html',
12
12
  './mobile.css',
@@ -1,16 +1,460 @@
1
+ // src/web/ui/message-renderer.ts
1
2
  import {
2
3
  mountStepTimeline,
3
4
  pushStepToTimeline,
4
5
  migrateStepTimeline,
5
6
  getStepTimeline
6
7
  } from "./step-timeline.js";
7
- import { segmentChatReply } from "../../agents/chat-segmenter.js";
8
- let streamingMessageEl = null;
9
- let streamingTextNode = null;
10
- let streamingText = "";
11
- let lastUserCommand = "";
12
- let lastAiContent = "";
13
- const stepEventBuffer = /* @__PURE__ */ new Map();
8
+
9
+ // src/agents/parse-tool-call.ts
10
+ var SHELL_KEYWORDS = ["git", "npx", "npm", "tsx", "tsc", "vitest", "node", "mkdir", "touch", "ls", "echo", "cat", "head", "tail", "wc", "pwd", "date"];
11
+ function defaultResolveAlias(name, tools) {
12
+ if (tools.has(name)) return name;
13
+ const lower = name.toLowerCase();
14
+ const aliasMap = {
15
+ read: "read_file",
16
+ edit: "edit_file",
17
+ write: "write_file",
18
+ rm: "delete_file",
19
+ mv: "move_file",
20
+ bash: "shell_exec",
21
+ shell: "shell_exec",
22
+ sh: "shell_exec",
23
+ cat: "read_file",
24
+ test: "vitest_run",
25
+ vitest: "vitest_run",
26
+ typecheck: "tsc_check",
27
+ tsc: "tsc_check",
28
+ log: "git_log",
29
+ show: "git_show",
30
+ diff: "git_diff",
31
+ commit: "git_commit",
32
+ push: "git_push",
33
+ branch: "git_branch",
34
+ checkout: "git_branch",
35
+ stash: "git_stash",
36
+ todo_write: "create_task",
37
+ todowrite: "create_task",
38
+ task: "create_task"
39
+ };
40
+ const aliased = aliasMap[lower];
41
+ if (aliased && tools.has(aliased)) return aliased;
42
+ if (tools.has(lower)) return lower;
43
+ return null;
44
+ }
45
+ function autoSplitCommand(args) {
46
+ if (typeof args.command === "string" && args.command.includes(" ") && !args.args) {
47
+ const parts = args.command.split(/\s+/);
48
+ args.command = parts[0];
49
+ args.args = parts.slice(1).join(" ");
50
+ }
51
+ }
52
+ function resolve(ctx, name) {
53
+ if (ctx.resolveAlias) return ctx.resolveAlias(name);
54
+ return defaultResolveAlias(name, ctx.tools);
55
+ }
56
+ var _parseEnv = typeof process !== "undefined" ? process.env : void 0;
57
+ var PARSE_DIAG_ON = !!_parseEnv && (_parseEnv.BOLLOON_PARSE_DIAG === "1" || _parseEnv.BOLLOON_VERBOSE === "1");
58
+ function parseToolCall(content, ctx) {
59
+ if (!content) return null;
60
+ const strippedContent = content.replace(/<think[\s\S]*?<\/think/g, "");
61
+ if (PARSE_DIAG_ON) try {
62
+ const result = /* @__PURE__ */ (function _diagProbe() {
63
+ return null;
64
+ })();
65
+ void result;
66
+ const probe = (function _doParse() {
67
+ const m1 = strippedContent.match(/<invoke\s+name=["']([\w]+)["']/);
68
+ if (m1) {
69
+ return { name: m1[1], args: { __probe_invoketag: "1" } };
70
+ }
71
+ const m2 = strippedContent.match(/<function_calls>[\s\S]*?<invoke\s+name=["']([\w]+)["']/);
72
+ if (m2) {
73
+ return { name: m2[1], args: { __probe_function_calls: "1" } };
74
+ }
75
+ const m3 = strippedContent.match(/\{[\s\S]*?"name"\s*:\s*["']([\w]+)["']/);
76
+ if (m3) {
77
+ return { name: m3[1], args: { __probe_json_name: "1" } };
78
+ }
79
+ return null;
80
+ })();
81
+ console.warn(
82
+ "[parseToolCall diag] rawLen=" + content.length + " strippedLen=" + strippedContent.length + " probeName=" + (probe?.name ?? "null") + " rawHead=" + JSON.stringify(content.slice(0, 500))
83
+ );
84
+ } catch (diagErr) {
85
+ console.warn("[parseToolCall diag] diag-self-failed:", String(diagErr));
86
+ }
87
+ const jsonPatterns = [
88
+ // markdown json code block + OpenAI 块, 同时匹配 arguments/input 字段
89
+ /(?:```(?:json|json5)?\s*\n?)?\{[\s\S]*?"name"\s*:\s*["'](\w+)["']\s*,\s*["']?(?:arguments|input)["']?\s*:\s*(\{[\s\S]*?\})\s*\}/
90
+ ];
91
+ for (const p of jsonPatterns) {
92
+ const m = content.match(p);
93
+ if (m) {
94
+ const name = m[1];
95
+ let args = {};
96
+ try {
97
+ const parsed = JSON.parse(m[2]);
98
+ if (parsed && typeof parsed === "object") {
99
+ args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
100
+ }
101
+ } catch {
102
+ }
103
+ autoSplitCommand(args);
104
+ const resolved = resolve(ctx, name);
105
+ if (resolved) {
106
+ return { name: resolved, args };
107
+ }
108
+ }
109
+ }
110
+ const toolsCallMatch = content.match(/<tools:call\s+name=["'](\w+)["']>([\s\S]*?)<\/tools:call>/);
111
+ if (toolsCallMatch) {
112
+ const name = toolsCallMatch[1];
113
+ const inner = toolsCallMatch[2];
114
+ const args = {};
115
+ const argTags = inner.matchAll(/<tools:call\s+name=["'](\w+)["']>([\s\S]*?)<\/tools:call>/g);
116
+ for (const m of argTags) {
117
+ args[m[1]] = m[2].trim();
118
+ }
119
+ const resolved = resolve(ctx, name);
120
+ if (resolved) {
121
+ return { name: resolved, args };
122
+ }
123
+ if (args.command) {
124
+ const cmdFirst = args.command.split(/\s+/)[0];
125
+ if (SHELL_KEYWORDS.includes(cmdFirst)) {
126
+ return { name: "shell_exec", args };
127
+ }
128
+ }
129
+ }
130
+ const patterns = [
131
+ // minimax/Hermes 自闭合 XML 格式 <invoke name="X">...</invoke>
132
+ new RegExp(`<invoke\\s+name=["']([\\w]+)["']>([\\s\\S]*?)</invoke>`),
133
+ // <function_calls> 包裹
134
+ new RegExp(`<function_calls>[\\s\\S]*?<invoke\\s+name=["']([\\w]+)["']>([\\s\\S]*?)</invoke>[\\s\\S]*?</function_calls>`),
135
+ // <function_calls><tool name="X"><param name="Y">value</param></tool></function_calls>
136
+ new RegExp(`<function_calls>[\\s\\S]*?<tool\\s+name=["']([\\w]+)["']>([\\s\\S]*?)</tool>[\\s\\S]*?</function_calls>`),
137
+ /调用工具[::]\s*(\w+)\s*\(([^)]*)\)/,
138
+ /使用工具[::]\s*(\w+)\s*\(([^)]*)\)/,
139
+ /tool[_\w]*[::]\s*(\w+)\s*\(([^)]*)\)/i,
140
+ /(\w+)\s*\(\s*([^)]*)\s*\)/,
141
+ // 对象字面量格式 {tool => "get_identity", args => {...}}
142
+ /\{\s*tool\s*=>\s*["'](\w+)["']\s*(?:,\s*args\s*=>\s*(\{[\s\S]*?\}))?\s*\}/,
143
+ // tool => "get_identity" (无 args 包裹)
144
+ /\btool\s*=>\s*["'](\w+)["']/,
145
+ // [TOOL_CALL] 块内 JSON 形式 {"name": "x", "args": {...}}
146
+ /\[TOOL_CALL\][\s\S]*?\{\s*"name"\s*:\s*"(\w+)"\s*,\s*"args"\s*:\s*(\{[\s\S]*?\})/i,
147
+ // "tool_name {json_args}" 形式 (单行, 无 name 字段)
148
+ /(?:^|\n)(\w+)\s+(\{[\s\S]*?\})(?=\n|$)/,
149
+ // XML 格式 <tool_name>...<arg>value</arg>...</tool_name>
150
+ /<(\w+)>([\s\S]*?)<\/\1>/
151
+ ];
152
+ for (const pattern of patterns) {
153
+ const match = strippedContent.match(pattern);
154
+ if (!match) continue;
155
+ const name = match[1];
156
+ let args = {};
157
+ const rawArgs = match[2] || "";
158
+ if (rawArgs && rawArgs.trim().startsWith("{")) {
159
+ try {
160
+ const parsed = JSON.parse(rawArgs);
161
+ if (parsed && typeof parsed === "object") {
162
+ args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
163
+ }
164
+ } catch {
165
+ const argPairs = rawArgs.split(",").map((s) => s.trim()).filter(Boolean);
166
+ for (const pair of argPairs) {
167
+ const [key, ...valueParts] = pair.split(":").map((s) => s.trim().replace(/['"]/g, ""));
168
+ if (key) args[key] = valueParts.join(":") || "";
169
+ }
170
+ }
171
+ } else if (rawArgs && /<[\w]/.test(rawArgs) && /<\/\w+>/.test(rawArgs)) {
172
+ const paramRe = /<parameter\s+name=["'](\w+)["']>([\s\S]*?)<\/parameter>/g;
173
+ const paramReShort = /<param\s+name=["'](\w+)["']>([\s\S]*?)<\/param>/g;
174
+ let pMatch;
175
+ while ((pMatch = paramRe.exec(rawArgs)) !== null) {
176
+ const argName = pMatch[1];
177
+ const argValue = pMatch[2].trim();
178
+ if (argName && argValue) {
179
+ args[argName] = argValue;
180
+ }
181
+ }
182
+ if (Object.keys(args).length === 0) {
183
+ let sMatch;
184
+ paramReShort.lastIndex = 0;
185
+ while ((sMatch = paramReShort.exec(rawArgs)) !== null) {
186
+ const argName = sMatch[1];
187
+ const argValue = sMatch[2].trim().replace(/^["']|["']$/g, "");
188
+ if (argName && argValue) {
189
+ args[argName] = argValue;
190
+ }
191
+ }
192
+ }
193
+ if (Object.keys(args).length === 0) {
194
+ const xmlArgPattern = /<(\w+)>([\s\S]*?)<\/\1>/g;
195
+ let xmlMatch;
196
+ while ((xmlMatch = xmlArgPattern.exec(rawArgs)) !== null) {
197
+ const argName = xmlMatch[1];
198
+ const argValue = xmlMatch[2].trim();
199
+ if (argName && argValue) {
200
+ args[argName] = argValue;
201
+ }
202
+ }
203
+ }
204
+ } else if (rawArgs) {
205
+ const argPairs = rawArgs.split(",").map((s) => s.trim()).filter(Boolean);
206
+ for (const pair of argPairs) {
207
+ const [key, ...valueParts] = pair.split(":").map((s) => s.trim().replace(/['"]/g, ""));
208
+ if (key) args[key] = valueParts.join(":") || "";
209
+ }
210
+ }
211
+ const resolved = ctx.tools.has(name) ? name : resolve(ctx, name);
212
+ if (resolved) {
213
+ autoSplitCommand(args);
214
+ return { name: resolved, args };
215
+ }
216
+ if (rawArgs && /<\w+>[\s\S]*<\/\w+>/.test(strippedContent)) {
217
+ break;
218
+ }
219
+ }
220
+ const xmlTagMatch = strippedContent.match(/<(\w+)>([\s\S]*?)<\/\1>/);
221
+ if (xmlTagMatch) {
222
+ const outerTag = xmlTagMatch[1];
223
+ const inner = xmlTagMatch[2];
224
+ if (!resolve(ctx, outerTag)) {
225
+ const cmdMatch = inner.match(/<command>([\s\S]*?)<\/command>/);
226
+ if (cmdMatch) {
227
+ const cmd = cmdMatch[1].trim();
228
+ const cmdFirst = cmd.split(/\s+/)[0];
229
+ if (SHELL_KEYWORDS.includes(cmdFirst)) {
230
+ const args = {};
231
+ const remaining = cmd.slice(cmdFirst.length).trim();
232
+ if (remaining) {
233
+ args.command = cmdFirst;
234
+ args.args = remaining;
235
+ } else {
236
+ args.command = cmd;
237
+ }
238
+ const argsM = inner.match(/<args>([\s\S]*?)<\/args>/);
239
+ if (argsM && argsM[1].trim()) {
240
+ args.args = argsM[1].trim();
241
+ }
242
+ return { name: "shell_exec", args };
243
+ }
244
+ }
245
+ }
246
+ }
247
+ return null;
248
+ }
249
+
250
+ // src/agents/chat-segmenter.ts
251
+ function segmentChatReply(reply, ctx) {
252
+ if (!reply) return [];
253
+ const segments = [];
254
+ let remaining = reply;
255
+ if (!remaining.startsWith("<think>")) {
256
+ remaining = extractLeadingThinking(remaining, segments);
257
+ }
258
+ remaining = extractAndPush("think", remaining, segments, /<think>([\s\S]*?)<\/think>/g);
259
+ remaining = extractAndPush("env_details", remaining, segments, /<environment_details>([\s\S]*?)<\/environment_details>/g);
260
+ remaining = stripToolCallMarkers(remaining, segments, ctx);
261
+ if (remaining.trim()) {
262
+ remaining = filterFillerText(remaining);
263
+ }
264
+ const finalIdx = remaining.indexOf("<final gen>");
265
+ let beforeFinalText = remaining;
266
+ if (finalIdx >= 0) {
267
+ beforeFinalText = remaining.substring(0, finalIdx);
268
+ }
269
+ const textContent = beforeFinalText.trim();
270
+ if (textContent) {
271
+ segments.push({ type: "text", content: textContent });
272
+ }
273
+ if (finalIdx >= 0) {
274
+ const afterFinal = remaining.substring(finalIdx + "<final gen>".length).trim();
275
+ if (afterFinal) {
276
+ segments.push({ type: "final", content: afterFinal });
277
+ }
278
+ }
279
+ return segments;
280
+ }
281
+ function extractAndPush(type, text, out, re) {
282
+ const matches = [];
283
+ const localRe = new RegExp(re.source, re.flags);
284
+ let m;
285
+ while ((m = localRe.exec(text)) !== null) {
286
+ const content = m[1] ? m[1].trim() : "";
287
+ matches.push({
288
+ start: m.index,
289
+ end: m.index + m[0].length,
290
+ content
291
+ });
292
+ if (m[0].length === 0) localRe.lastIndex++;
293
+ }
294
+ if (matches.length === 0) return text;
295
+ for (const m2 of matches) {
296
+ if (m2.content) out.push({ type, content: m2.content });
297
+ }
298
+ let next = "";
299
+ let cursor = 0;
300
+ for (const m2 of matches) {
301
+ next += text.substring(cursor, m2.start);
302
+ cursor = m2.end;
303
+ }
304
+ next += text.substring(cursor);
305
+ return next;
306
+ }
307
+ function extractLeadingThinking(text, out) {
308
+ const firstLineEnd = text.indexOf("\n");
309
+ const firstLine = firstLineEnd === -1 ? text : text.substring(0, firstLineEnd);
310
+ const rest = firstLineEnd === -1 ? "" : text.substring(firstLineEnd + 1);
311
+ const thinkingStartRe = /^(让我|我先|我应该|先来|先|接下来|好的[,,]?\s*我|让我先|先看看|让我看看|思考|考虑)/;
312
+ const enThinkingStartRe = /^(Let me|I'll|I will|First,|Next,|Now,|So,|Alright[,.]\s+(?:let me|I'll|I will))/i;
313
+ if (firstLine.trim().length === 0) {
314
+ return text;
315
+ }
316
+ if (firstLine.length <= 120 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {
317
+ out.push({ type: "think", content: firstLine.trim() });
318
+ return rest;
319
+ }
320
+ if (firstLine.length <= 80 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {
321
+ out.push({ type: "think", content: firstLine.trim() });
322
+ return rest;
323
+ }
324
+ return text;
325
+ }
326
+ function filterFillerText(text) {
327
+ const lines = text.split("\n");
328
+ const filtered = [];
329
+ for (const line of lines) {
330
+ const t = line.trim();
331
+ if (!t) continue;
332
+ if (/^(好|好了|好的|完成|完成了|任务完成|可以|可以了|答完了|说完了|就这样|完了|done|ok|OK|Okay|okay|alright|fine|let me check|我来)\.?$/i.test(t)) {
333
+ continue;
334
+ }
335
+ filtered.push(line);
336
+ }
337
+ return filtered.join("\n").trim();
338
+ }
339
+ function stripToolCallMarkers(text, out, ctx) {
340
+ let result = text.replace(/\[TOOL_CALL\][\s\S]*?\[\/TOOL_CALL\]/g, (m) => {
341
+ const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
342
+ if (parsed) out.push({ type: "tool_call", tool: { name: parsed.name, args: parsed.args } });
343
+ return "";
344
+ });
345
+ result = result.replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, (m) => {
346
+ const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
347
+ if (parsed) out.push({ type: "tool_call", tool: { name: parsed.name, args: parsed.args } });
348
+ return "";
349
+ });
350
+ result = result.replace(/<invoke\s+name=["']([\w]+)["']>([\s\S]*?)<\/invoke>/g, (_m, name, inner) => {
351
+ if (ctx.knownToolNames.has(name)) {
352
+ const args = extractSimpleArgs(inner);
353
+ out.push({ type: "tool_call", tool: { name, args } });
354
+ }
355
+ return "";
356
+ });
357
+ result = result.replace(/<function_calls>[\s\S]*?<\/function_calls>/g, (m) => {
358
+ const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
359
+ if (parsed) out.push({ type: "tool_call", tool: { name: parsed.name, args: parsed.args } });
360
+ return "";
361
+ });
362
+ result = result.replace(
363
+ /\{\s*"name"\s*:\s*"([\w]+)"\s*,\s*"(?:arguments|input|args|params)"\s*:\s*(\{[\s\S]*?\})\s*\}/g,
364
+ (_m, name, argsJson) => {
365
+ if (!ctx.knownToolNames.has(name)) return "";
366
+ let args = {};
367
+ try {
368
+ const parsed = JSON.parse(argsJson);
369
+ if (parsed && typeof parsed === "object") {
370
+ args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
371
+ }
372
+ } catch {
373
+ }
374
+ out.push({ type: "tool_call", tool: { name, args } });
375
+ return "";
376
+ }
377
+ );
378
+ result = result.replace(/\{\s*tool\s*=>\s*["']([\w]+)["']\s*(?:,\s*args\s*=>\s*(\{[\s\S]*?\}))?\s*\}/g, (_m, name, argsJson) => {
379
+ if (!ctx.knownToolNames.has(name)) return "";
380
+ let args = {};
381
+ if (argsJson) {
382
+ try {
383
+ const parsed = JSON.parse(argsJson);
384
+ if (parsed && typeof parsed === "object") {
385
+ args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
386
+ }
387
+ } catch {
388
+ }
389
+ }
390
+ out.push({ type: "tool_call", tool: { name, args } });
391
+ return "";
392
+ });
393
+ result = result.replace(/\[Function[^\]]*\]\s*/g, "");
394
+ result = stripUnclosedToolCallTags(result);
395
+ return result;
396
+ }
397
+ function stripUnclosedToolCallTags(text) {
398
+ const startTags = [
399
+ { tag: "<tool_call>", closeTag: "</tool_call>", openRe: /<tool_call>/g },
400
+ { tag: "<invoke ", closeTag: "</invoke>", openRe: /<invoke\s+name=["']([\w]+)["']>/g },
401
+ { tag: "<function_calls>", closeTag: "</function_calls>", openRe: /<function_calls>/g },
402
+ { tag: "[TOOL_CALL]", closeTag: "[/TOOL_CALL]", openRe: /\[TOOL_CALL\]/g }
403
+ ];
404
+ let result = text;
405
+ for (const { tag, closeTag, openRe } of startTags) {
406
+ let m;
407
+ while ((m = openRe.exec(result)) !== null) {
408
+ const tail = result.substring(m.index);
409
+ const closeIdx = tail.indexOf(closeTag);
410
+ if (closeIdx === -1) {
411
+ const nextBlank = result.indexOf("\n\n", m.index);
412
+ const endIdx = nextBlank === -1 ? result.length : nextBlank;
413
+ result = result.substring(0, m.index) + result.substring(endIdx);
414
+ break;
415
+ }
416
+ }
417
+ }
418
+ return result;
419
+ }
420
+ function extractSimpleArgs(inner) {
421
+ const args = {};
422
+ const paramRe = /<parameter\s+name=["'](\w+)["']>([\s\S]*?)<\/parameter>/g;
423
+ let m;
424
+ while ((m = paramRe.exec(inner)) !== null) {
425
+ args[m[1]] = m[2].trim();
426
+ }
427
+ if (Object.keys(args).length === 0) {
428
+ const shortRe = /<param\s+name=["'](\w+)["']>([\s\S]*?)<\/param>/g;
429
+ while ((m = shortRe.exec(inner)) !== null) {
430
+ args[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
431
+ }
432
+ }
433
+ if (Object.keys(args).length === 0) {
434
+ const cmdM = inner.match(/<command>([\s\S]*?)<\/command>/);
435
+ const argsM = inner.match(/<args>([\s\S]*?)<\/args>/);
436
+ if (cmdM) {
437
+ const cmd = cmdM[1].trim();
438
+ if (cmd.includes(" ") && !argsM) {
439
+ const parts = cmd.split(/\s+/);
440
+ args.command = parts[0];
441
+ args.args = parts.slice(1).join(" ");
442
+ } else {
443
+ args.command = cmd;
444
+ }
445
+ }
446
+ if (argsM) args.args = argsM[1].trim();
447
+ }
448
+ return args;
449
+ }
450
+
451
+ // src/web/ui/message-renderer.ts
452
+ var streamingMessageEl = null;
453
+ var streamingTextNode = null;
454
+ var streamingText = "";
455
+ var lastUserCommand = "";
456
+ var lastAiContent = "";
457
+ var stepEventBuffer = /* @__PURE__ */ new Map();
14
458
  function hasStreamingText() {
15
459
  return streamingText.length > 0;
16
460
  }
@@ -37,7 +481,7 @@ function injectRecoveredText(partialText, ctx = { messagesEl: null, messagesCont
37
481
  ctx
38
482
  );
39
483
  }
40
- let scrollToBottomTimer = null;
484
+ var scrollToBottomTimer = null;
41
485
  function scheduleScrollToBottom(container) {
42
486
  if (!container) return;
43
487
  if (scrollToBottomTimer) return;
@@ -76,7 +520,7 @@ function addMessage(content, type, save = true, container, usedJudgmentIds = [],
76
520
  if (save) {
77
521
  const lastContent = type === "user" ? lastUserCommand : lastAiContent;
78
522
  if (lastContent && content === lastContent) {
79
- console.log(`[addMessage] \u8DF3\u8FC7\u91CD\u590D\u7684 ${type} \u6D88\u606F`);
523
+ console.log(`[addMessage] 跳过重复的 ${type} 消息`);
80
524
  return;
81
525
  }
82
526
  if (type === "user") lastUserCommand = content;
@@ -150,7 +594,7 @@ function addMessage(content, type, save = true, container, usedJudgmentIds = [],
150
594
  if (type === "ai" && Array.isArray(usedJudgmentIds) && usedJudgmentIds.length > 0) {
151
595
  const link = document.createElement("a");
152
596
  link.className = "used-judgments-link";
153
- link.textContent = `\u{1F4CE} \u53C2\u8003 ${usedJudgmentIds.length} \u6761\u539F\u5219`;
597
+ link.textContent = `📎 参考 ${usedJudgmentIds.length} 条原则`;
154
598
  link.onclick = (e) => {
155
599
  e.preventDefault();
156
600
  if (typeof ctx.openJudgmentsModalWithFilter === "function") {
@@ -174,17 +618,17 @@ function buildThinkContainer(thinkContent) {
174
618
  container.className = "think-container";
175
619
  const toggle = document.createElement("div");
176
620
  toggle.className = "think-toggle";
177
- toggle.innerHTML = '\u{1F4AD} \u601D\u8003\u8FC7\u7A0B <span class="think-arrow">\u25B8</span>';
621
+ toggle.innerHTML = '💭 思考过程 <span class="think-arrow">▸</span>';
178
622
  toggle.onclick = function() {
179
623
  const details = container.querySelector(".think-content");
180
624
  const arrow = toggle.querySelector(".think-arrow");
181
625
  if (!details || !arrow) return;
182
626
  if (details.style.display === "none") {
183
627
  details.style.display = "block";
184
- arrow.textContent = "\u25BE";
628
+ arrow.textContent = "▾";
185
629
  } else {
186
630
  details.style.display = "none";
187
- arrow.textContent = "\u25B8";
631
+ arrow.textContent = "▸";
188
632
  }
189
633
  };
190
634
  const content = document.createElement("div");
@@ -200,17 +644,17 @@ function buildEnvContainer(envDetails) {
200
644
  container.className = "env-container";
201
645
  const toggle = document.createElement("div");
202
646
  toggle.className = "env-toggle";
203
- toggle.innerHTML = '\u2699\uFE0F \u73AF\u5883\u4FE1\u606F <span class="env-arrow">\u25B8</span>';
647
+ toggle.innerHTML = '⚙️ 环境信息 <span class="env-arrow">▸</span>';
204
648
  toggle.onclick = function() {
205
649
  const details = container.querySelector(".environment-details");
206
650
  const arrow = toggle.querySelector(".env-arrow");
207
651
  if (!details || !arrow) return;
208
652
  if (details.style.display === "none") {
209
653
  details.style.display = "block";
210
- arrow.textContent = "\u25BE";
654
+ arrow.textContent = "▾";
211
655
  } else {
212
656
  details.style.display = "none";
213
- arrow.textContent = "\u25B8";
657
+ arrow.textContent = "▸";
214
658
  }
215
659
  };
216
660
  const content = document.createElement("div");
@@ -266,30 +710,30 @@ function buildMessageActions(div, rawContent, ctx) {
266
710
  actions.className = "message-actions";
267
711
  const copyBtn = document.createElement("button");
268
712
  copyBtn.className = "action-btn copy-btn";
269
- copyBtn.innerHTML = copyIcon() + " \u590D\u5236";
270
- copyBtn.title = "\u590D\u5236\u6D88\u606F";
713
+ copyBtn.innerHTML = copyIcon() + " 复制";
714
+ copyBtn.title = "复制消息";
271
715
  copyBtn.onclick = () => {
272
716
  navigator.clipboard.writeText(rawContent).then(() => {
273
- copyBtn.innerHTML = checkIcon() + " \u5DF2\u590D\u5236";
717
+ copyBtn.innerHTML = checkIcon() + " 已复制";
274
718
  setTimeout(() => {
275
- copyBtn.innerHTML = copyIcon() + " \u590D\u5236";
719
+ copyBtn.innerHTML = copyIcon() + " 复制";
276
720
  }, 2e3);
277
721
  });
278
722
  };
279
723
  actions.appendChild(copyBtn);
280
724
  const saveJudgmentBtn = document.createElement("button");
281
725
  saveJudgmentBtn.className = "action-btn save-as-judgment";
282
- saveJudgmentBtn.title = "AI \u84B8\u998F\u4E3A 30-80 \u5B57\u5224\u65AD\u529B + \u81EA\u52A8\u6F14\u5316\u5BF9\u9F50";
726
+ saveJudgmentBtn.title = "AI 蒸馏为 30-80 字判断力 + 自动演化对齐";
283
727
  saveJudgmentBtn.setAttribute("data-decision", rawContent.substring(0, 800));
284
728
  if (ctx.currentChannelId) saveJudgmentBtn.setAttribute("data-channel-id", ctx.currentChannelId);
285
- saveJudgmentBtn.innerHTML = shieldIcon() + " \u84B8\u998F\u4E3A\u5224\u65AD";
729
+ saveJudgmentBtn.innerHTML = shieldIcon() + " 蒸馏为判断";
286
730
  actions.appendChild(saveJudgmentBtn);
287
731
  const regenBtn = document.createElement("button");
288
732
  regenBtn.className = "action-btn regenerate-btn";
289
- regenBtn.innerHTML = refreshIcon(false) + " \u91CD\u65B0\u56DE\u7B54";
290
- regenBtn.title = "\u91CD\u65B0\u751F\u6210\u56DE\u590D";
733
+ regenBtn.innerHTML = refreshIcon(false) + " 重新回答";
734
+ regenBtn.title = "重新生成回复";
291
735
  regenBtn.onclick = () => {
292
- regenBtn.innerHTML = refreshIcon(true) + " \u751F\u6210\u4E2D...";
736
+ regenBtn.innerHTML = refreshIcon(true) + " 生成中...";
293
737
  regenBtn.disabled = true;
294
738
  const messages = div.parentElement?.querySelectorAll(".message") || [];
295
739
  let lastUserMsg = "";
@@ -310,10 +754,10 @@ function buildMessageActions(div, rawContent, ctx) {
310
754
  }).then((res) => {
311
755
  if (!res.ok) throw new Error("regenerate failed");
312
756
  }).catch((err) => {
313
- console.error("\u91CD\u65B0\u751F\u6210\u5931\u8D25:", err);
314
- regenBtn.innerHTML = refreshIcon(false) + " \u5931\u8D25";
757
+ console.error("重新生成失败:", err);
758
+ regenBtn.innerHTML = refreshIcon(false) + " 失败";
315
759
  setTimeout(() => {
316
- regenBtn.innerHTML = refreshIcon(false) + " \u91CD\u65B0\u56DE\u7B54";
760
+ regenBtn.innerHTML = refreshIcon(false) + " 重新回答";
317
761
  regenBtn.disabled = false;
318
762
  }, 2e3);
319
763
  });
@@ -441,7 +885,7 @@ function seedDedupState(lastType, lastContent) {
441
885
  if (lastType === "user") lastUserCommand = lastContent || "";
442
886
  else if (lastType === "ai") lastAiContent = lastContent || "";
443
887
  }
444
- const MessageRenderer = {
888
+ var MessageRenderer = {
445
889
  addMessage,
446
890
  handleStreamTokenEvent,
447
891
  finalizeTimelineAsMessage,
@@ -1,6 +1,7 @@
1
- const MAX_VISIBLE_NODES = 20;
2
- const STORAGE_PREFIX = "bolloon.stepTimeline.expanded.";
3
- const stateMap = /* @__PURE__ */ new WeakMap();
1
+ // src/web/ui/step-timeline.ts
2
+ var MAX_VISIBLE_NODES = 20;
3
+ var STORAGE_PREFIX = "bolloon.stepTimeline.expanded.";
4
+ var stateMap = /* @__PURE__ */ new WeakMap();
4
5
  function readExpanded(channelId) {
5
6
  if (!channelId) return false;
6
7
  try {
@@ -17,17 +18,17 @@ function writeExpanded(channelId, expanded) {
17
18
  }
18
19
  }
19
20
  function computeTitle(steps) {
20
- if (steps.length === 0) return "\u6267\u884C\u6B65\u9AA4";
21
+ if (steps.length === 0) return "执行步骤";
21
22
  const activeIdx = steps.findIndex((s) => s.status === "active");
22
- if (activeIdx >= 0) return `\u25CF \u6267\u884C\u4E2D \xB7 ${steps[activeIdx].name}`;
23
+ if (activeIdx >= 0) return `● 执行中 · ${steps[activeIdx].name}`;
23
24
  const lastErrorIdx = (() => {
24
25
  for (let i = steps.length - 1; i >= 0; i--) {
25
26
  if (steps[i].status === "error") return i;
26
27
  }
27
28
  return -1;
28
29
  })();
29
- if (lastErrorIdx >= 0) return `\u2717 \u5931\u8D25 \xB7 ${steps[lastErrorIdx].name}`;
30
- return `\u2713 \u5DF2\u5B8C\u6210 \xB7 ${steps.length} \u6B65`;
30
+ if (lastErrorIdx >= 0) return `✗ 失败 · ${steps[lastErrorIdx].name}`;
31
+ return `✓ 已完成 · ${steps.length} 步`;
31
32
  }
32
33
  function render(timelineEl) {
33
34
  const state = stateMap.get(timelineEl);
@@ -53,7 +54,7 @@ function render(timelineEl) {
53
54
  dotsEl.appendChild(dot);
54
55
  }
55
56
  dot.setAttribute("data-status", step.status);
56
- dot.setAttribute("title", `${step.name} \xB7 ${step.status}`);
57
+ dot.setAttribute("title", `${step.name} · ${step.status}`);
57
58
  }
58
59
  while (dotsEl.children.length > steps.length) {
59
60
  dotsEl.removeChild(dotsEl.lastChild);
@@ -71,7 +72,7 @@ function render(timelineEl) {
71
72
  moreBtn = document.createElement("li");
72
73
  moreBtn.className = "step-timeline-more";
73
74
  moreBtn.setAttribute("role", "button");
74
- moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;
75
+ moreBtn.textContent = `+ ${hiddenCount} 更多`;
75
76
  moreBtn.onclick = () => {
76
77
  state.showAll = true;
77
78
  render(timelineEl);
@@ -82,7 +83,7 @@ function render(timelineEl) {
82
83
  listEl.appendChild(moreBtn);
83
84
  }
84
85
  } else {
85
- moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;
86
+ moreBtn.textContent = `+ ${hiddenCount} 更多`;
86
87
  }
87
88
  htmlIdx++;
88
89
  }
@@ -177,14 +178,14 @@ function createEmptyStepTimeline(channelId = null) {
177
178
  titleSpan.className = "step-timeline-title";
178
179
  const titleContent = document.createElement("span");
179
180
  titleContent.setAttribute("data-current-tool", "");
180
- titleContent.textContent = "\u6267\u884C\u6B65\u9AA4";
181
+ titleContent.textContent = "执行步骤";
181
182
  titleSpan.appendChild(titleContent);
182
183
  const dots = document.createElement("div");
183
184
  dots.className = "step-timeline-dots";
184
185
  dots.setAttribute("data-dots", "");
185
186
  const arrow = document.createElement("span");
186
187
  arrow.className = "step-timeline-arrow";
187
- arrow.textContent = "\u25BE";
188
+ arrow.textContent = "▾";
188
189
  summary.appendChild(titleSpan);
189
190
  summary.appendChild(dots);
190
191
  summary.appendChild(arrow);