@hflin/cclin 0.1.0

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 (200) hide show
  1. package/README.md +124 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +165 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/llm/client.d.ts +32 -0
  7. package/dist/llm/client.d.ts.map +1 -0
  8. package/dist/llm/client.js +280 -0
  9. package/dist/llm/client.js.map +1 -0
  10. package/dist/runtime/compaction.d.ts +49 -0
  11. package/dist/runtime/compaction.d.ts.map +1 -0
  12. package/dist/runtime/compaction.js +118 -0
  13. package/dist/runtime/compaction.js.map +1 -0
  14. package/dist/runtime/compaction.test.d.ts +7 -0
  15. package/dist/runtime/compaction.test.d.ts.map +1 -0
  16. package/dist/runtime/compaction.test.js +70 -0
  17. package/dist/runtime/compaction.test.js.map +1 -0
  18. package/dist/runtime/history.d.ts +34 -0
  19. package/dist/runtime/history.d.ts.map +1 -0
  20. package/dist/runtime/history.js +63 -0
  21. package/dist/runtime/history.js.map +1 -0
  22. package/dist/runtime/hooks.d.ts +54 -0
  23. package/dist/runtime/hooks.d.ts.map +1 -0
  24. package/dist/runtime/hooks.js +113 -0
  25. package/dist/runtime/hooks.js.map +1 -0
  26. package/dist/runtime/hooks.test.d.ts +7 -0
  27. package/dist/runtime/hooks.test.d.ts.map +1 -0
  28. package/dist/runtime/hooks.test.js +73 -0
  29. package/dist/runtime/hooks.test.js.map +1 -0
  30. package/dist/runtime/model-profile.d.ts +42 -0
  31. package/dist/runtime/model-profile.d.ts.map +1 -0
  32. package/dist/runtime/model-profile.js +84 -0
  33. package/dist/runtime/model-profile.js.map +1 -0
  34. package/dist/runtime/prompt.d.ts +38 -0
  35. package/dist/runtime/prompt.d.ts.map +1 -0
  36. package/dist/runtime/prompt.js +152 -0
  37. package/dist/runtime/prompt.js.map +1 -0
  38. package/dist/runtime/prompt.md +64 -0
  39. package/dist/runtime/prompt.test.d.ts +7 -0
  40. package/dist/runtime/prompt.test.d.ts.map +1 -0
  41. package/dist/runtime/prompt.test.js +38 -0
  42. package/dist/runtime/prompt.test.js.map +1 -0
  43. package/dist/runtime/react-loop.d.ts +82 -0
  44. package/dist/runtime/react-loop.d.ts.map +1 -0
  45. package/dist/runtime/react-loop.js +311 -0
  46. package/dist/runtime/react-loop.js.map +1 -0
  47. package/dist/runtime/react-loop.test.d.ts +7 -0
  48. package/dist/runtime/react-loop.test.d.ts.map +1 -0
  49. package/dist/runtime/react-loop.test.js +78 -0
  50. package/dist/runtime/react-loop.test.js.map +1 -0
  51. package/dist/runtime/session.d.ts +109 -0
  52. package/dist/runtime/session.d.ts.map +1 -0
  53. package/dist/runtime/session.js +252 -0
  54. package/dist/runtime/session.js.map +1 -0
  55. package/dist/runtime/skills.d.ts +36 -0
  56. package/dist/runtime/skills.d.ts.map +1 -0
  57. package/dist/runtime/skills.js +187 -0
  58. package/dist/runtime/skills.js.map +1 -0
  59. package/dist/runtime/skills.test.d.ts +7 -0
  60. package/dist/runtime/skills.test.d.ts.map +1 -0
  61. package/dist/runtime/skills.test.js +92 -0
  62. package/dist/runtime/skills.test.js.map +1 -0
  63. package/dist/tools/approval.d.ts +61 -0
  64. package/dist/tools/approval.d.ts.map +1 -0
  65. package/dist/tools/approval.js +119 -0
  66. package/dist/tools/approval.js.map +1 -0
  67. package/dist/tools/approval.test.d.ts +9 -0
  68. package/dist/tools/approval.test.d.ts.map +1 -0
  69. package/dist/tools/approval.test.js +112 -0
  70. package/dist/tools/approval.test.js.map +1 -0
  71. package/dist/tools/bash.d.ts +6 -0
  72. package/dist/tools/bash.d.ts.map +1 -0
  73. package/dist/tools/bash.js +58 -0
  74. package/dist/tools/bash.js.map +1 -0
  75. package/dist/tools/edit-file.d.ts +6 -0
  76. package/dist/tools/edit-file.d.ts.map +1 -0
  77. package/dist/tools/edit-file.js +58 -0
  78. package/dist/tools/edit-file.js.map +1 -0
  79. package/dist/tools/get-memory.d.ts +9 -0
  80. package/dist/tools/get-memory.d.ts.map +1 -0
  81. package/dist/tools/get-memory.js +56 -0
  82. package/dist/tools/get-memory.js.map +1 -0
  83. package/dist/tools/list-directory.d.ts +6 -0
  84. package/dist/tools/list-directory.d.ts.map +1 -0
  85. package/dist/tools/list-directory.js +68 -0
  86. package/dist/tools/list-directory.js.map +1 -0
  87. package/dist/tools/mcp-client.d.ts +74 -0
  88. package/dist/tools/mcp-client.d.ts.map +1 -0
  89. package/dist/tools/mcp-client.js +129 -0
  90. package/dist/tools/mcp-client.js.map +1 -0
  91. package/dist/tools/mcp-config.d.ts +31 -0
  92. package/dist/tools/mcp-config.d.ts.map +1 -0
  93. package/dist/tools/mcp-config.js +55 -0
  94. package/dist/tools/mcp-config.js.map +1 -0
  95. package/dist/tools/mcp-registry.d.ts +39 -0
  96. package/dist/tools/mcp-registry.d.ts.map +1 -0
  97. package/dist/tools/mcp-registry.js +88 -0
  98. package/dist/tools/mcp-registry.js.map +1 -0
  99. package/dist/tools/orchestrator.d.ts +52 -0
  100. package/dist/tools/orchestrator.d.ts.map +1 -0
  101. package/dist/tools/orchestrator.js +190 -0
  102. package/dist/tools/orchestrator.js.map +1 -0
  103. package/dist/tools/orchestrator.test.d.ts +8 -0
  104. package/dist/tools/orchestrator.test.d.ts.map +1 -0
  105. package/dist/tools/orchestrator.test.js +122 -0
  106. package/dist/tools/orchestrator.test.js.map +1 -0
  107. package/dist/tools/read-file.d.ts +6 -0
  108. package/dist/tools/read-file.d.ts.map +1 -0
  109. package/dist/tools/read-file.js +50 -0
  110. package/dist/tools/read-file.js.map +1 -0
  111. package/dist/tools/registry.d.ts +55 -0
  112. package/dist/tools/registry.d.ts.map +1 -0
  113. package/dist/tools/registry.js +75 -0
  114. package/dist/tools/registry.js.map +1 -0
  115. package/dist/tools/registry.test.d.ts +8 -0
  116. package/dist/tools/registry.test.d.ts.map +1 -0
  117. package/dist/tools/registry.test.js +100 -0
  118. package/dist/tools/registry.test.js.map +1 -0
  119. package/dist/tools/router.d.ts +62 -0
  120. package/dist/tools/router.d.ts.map +1 -0
  121. package/dist/tools/router.js +119 -0
  122. package/dist/tools/router.js.map +1 -0
  123. package/dist/tools/router.test.d.ts +7 -0
  124. package/dist/tools/router.test.d.ts.map +1 -0
  125. package/dist/tools/router.test.js +102 -0
  126. package/dist/tools/router.test.js.map +1 -0
  127. package/dist/tools/safety.d.ts +16 -0
  128. package/dist/tools/safety.d.ts.map +1 -0
  129. package/dist/tools/safety.js +81 -0
  130. package/dist/tools/safety.js.map +1 -0
  131. package/dist/tools/safety.test.d.ts +7 -0
  132. package/dist/tools/safety.test.d.ts.map +1 -0
  133. package/dist/tools/safety.test.js +104 -0
  134. package/dist/tools/safety.test.js.map +1 -0
  135. package/dist/tools/search-files.d.ts +9 -0
  136. package/dist/tools/search-files.d.ts.map +1 -0
  137. package/dist/tools/search-files.js +114 -0
  138. package/dist/tools/search-files.js.map +1 -0
  139. package/dist/tools/update-plan.d.ts +9 -0
  140. package/dist/tools/update-plan.d.ts.map +1 -0
  141. package/dist/tools/update-plan.js +99 -0
  142. package/dist/tools/update-plan.js.map +1 -0
  143. package/dist/tools/write-file.d.ts +6 -0
  144. package/dist/tools/write-file.d.ts.map +1 -0
  145. package/dist/tools/write-file.js +41 -0
  146. package/dist/tools/write-file.js.map +1 -0
  147. package/dist/tui/app.d.ts +31 -0
  148. package/dist/tui/app.d.ts.map +1 -0
  149. package/dist/tui/app.js +121 -0
  150. package/dist/tui/app.js.map +1 -0
  151. package/dist/tui/chatwidget/markdown_renderer.d.ts +20 -0
  152. package/dist/tui/chatwidget/markdown_renderer.d.ts.map +1 -0
  153. package/dist/tui/chatwidget/markdown_renderer.js +188 -0
  154. package/dist/tui/chatwidget/markdown_renderer.js.map +1 -0
  155. package/dist/tui/cjk_text.d.ts +25 -0
  156. package/dist/tui/cjk_text.d.ts.map +1 -0
  157. package/dist/tui/cjk_text.js +84 -0
  158. package/dist/tui/cjk_text.js.map +1 -0
  159. package/dist/tui/cjk_text.test.d.ts +2 -0
  160. package/dist/tui/cjk_text.test.d.ts.map +1 -0
  161. package/dist/tui/cjk_text.test.js +62 -0
  162. package/dist/tui/cjk_text.test.js.map +1 -0
  163. package/dist/tui/composer_input.d.ts +31 -0
  164. package/dist/tui/composer_input.d.ts.map +1 -0
  165. package/dist/tui/composer_input.js +184 -0
  166. package/dist/tui/composer_input.js.map +1 -0
  167. package/dist/tui/composer_input.test.d.ts +2 -0
  168. package/dist/tui/composer_input.test.d.ts.map +1 -0
  169. package/dist/tui/composer_input.test.js +87 -0
  170. package/dist/tui/composer_input.test.js.map +1 -0
  171. package/dist/tui/input.d.ts +21 -0
  172. package/dist/tui/input.d.ts.map +1 -0
  173. package/dist/tui/input.js +166 -0
  174. package/dist/tui/input.js.map +1 -0
  175. package/dist/tui/output.d.ts +17 -0
  176. package/dist/tui/output.d.ts.map +1 -0
  177. package/dist/tui/output.js +104 -0
  178. package/dist/tui/output.js.map +1 -0
  179. package/dist/tui/state/chat_timeline.d.ts +50 -0
  180. package/dist/tui/state/chat_timeline.d.ts.map +1 -0
  181. package/dist/tui/state/chat_timeline.js +129 -0
  182. package/dist/tui/state/chat_timeline.js.map +1 -0
  183. package/dist/tui/types.d.ts +45 -0
  184. package/dist/tui/types.d.ts.map +1 -0
  185. package/dist/tui/types.js +14 -0
  186. package/dist/tui/types.js.map +1 -0
  187. package/dist/types.d.ts +435 -0
  188. package/dist/types.d.ts.map +1 -0
  189. package/dist/types.js +8 -0
  190. package/dist/types.js.map +1 -0
  191. package/dist/utils/tokenizer.d.ts +21 -0
  192. package/dist/utils/tokenizer.d.ts.map +1 -0
  193. package/dist/utils/tokenizer.js +71 -0
  194. package/dist/utils/tokenizer.js.map +1 -0
  195. package/dist/utils/tokenizer.test.d.ts +7 -0
  196. package/dist/utils/tokenizer.test.d.ts.map +1 -0
  197. package/dist/utils/tokenizer.test.js +51 -0
  198. package/dist/utils/tokenizer.test.js.map +1 -0
  199. package/package.json +41 -0
  200. package/src/runtime/prompt.md +64 -0
@@ -0,0 +1,311 @@
1
+ /**
2
+ * @file ReAct 循环核心引擎。
3
+ *
4
+ * Phase 2:实现 Think → Act → Observe 循环骨架。
5
+ * 工具执行通过 `executeTool` 注入,Phase 2 使用 mock 实现。
6
+ *
7
+ * 设计:纯函数 `runTurn()`,不持有状态,所有状态通过参数传入。
8
+ * 这样 Session 类只做状态管理,循环逻辑可独立测试。
9
+ */
10
+ import { runHook, snapshotHistory } from './hooks.js';
11
+ /** 单轮对话的最大步骤数,防止无限循环。 */
12
+ const MAX_STEPS = 25;
13
+ // ─── 响应解析 ─────────────────────────────────────────────────────────────────
14
+ /**
15
+ * 从 LLM 响应中提取文本内容和工具调用块。
16
+ *
17
+ * 将 ContentBlock[] 分拆为两个维度:
18
+ * - textContent:所有 TextBlock 拼接的纯文本
19
+ * - toolUseBlocks:所有 ToolUseBlock 的结构化数据
20
+ */
21
+ export function normalizeLLMResponse(response) {
22
+ let textContent = '';
23
+ const toolUseBlocks = [];
24
+ for (const block of response.content) {
25
+ if (block.type === 'text') {
26
+ textContent += block.text;
27
+ }
28
+ else if (block.type === 'tool_use') {
29
+ toolUseBlocks.push({
30
+ id: block.id,
31
+ name: block.name,
32
+ input: block.input,
33
+ });
34
+ }
35
+ }
36
+ return {
37
+ textContent,
38
+ toolUseBlocks,
39
+ stopReason: response.stop_reason,
40
+ usage: response.usage,
41
+ reasoningContent: response.reasoning_content,
42
+ };
43
+ }
44
+ /**
45
+ * 将 normalized 响应转为 ParsedAssistant 结构。
46
+ *
47
+ * 判定逻辑:
48
+ * 1. 有工具调用 → action(取第一个工具),文本部分作为 thinking
49
+ * 2. 无工具调用但有文本 → final
50
+ * 3. 两者皆无 → 空对象
51
+ */
52
+ export function parseLLMResponse(textContent, toolUseBlocks) {
53
+ if (toolUseBlocks.length > 0) {
54
+ const firstTool = toolUseBlocks[0];
55
+ return {
56
+ action: {
57
+ tool: firstTool.name,
58
+ input: firstTool.input,
59
+ },
60
+ actions: toolUseBlocks.map((b) => ({
61
+ tool: b.name,
62
+ input: b.input,
63
+ })),
64
+ thinking: textContent.trim() || undefined,
65
+ };
66
+ }
67
+ if (textContent.trim()) {
68
+ return { final: textContent };
69
+ }
70
+ return {};
71
+ }
72
+ /**
73
+ * 构建 assistant 消息的 tool_calls 字段。
74
+ *
75
+ * 将内部 toolUseBlock 格式转为 OpenAI tool_calls 格式,
76
+ * 以便正确追加到对话历史中。
77
+ */
78
+ function buildAssistantToolCalls(blocks) {
79
+ return blocks.map((b) => ({
80
+ id: b.id,
81
+ type: 'function',
82
+ function: {
83
+ name: b.name,
84
+ arguments: JSON.stringify(b.input),
85
+ },
86
+ }));
87
+ }
88
+ /** 默认的 mock 工具执行函数。 */
89
+ const defaultExecuteTool = async (toolName, _toolInput) => {
90
+ return `[tool "${toolName}" not implemented yet]`;
91
+ };
92
+ /**
93
+ * 执行一次 Turn 的 ReAct 循环。
94
+ *
95
+ * 流程:
96
+ * 1. 将用户输入追加到 history
97
+ * 2. 循环调用 LLM → 解析响应 → 处理工具/最终回答
98
+ * 3. 返回 TurnResult
99
+ *
100
+ * @param input - 用户输入文本
101
+ * @param deps - 依赖注入(history, callLLM, executeTool)
102
+ */
103
+ export async function runTurn(input, deps) {
104
+ const { history, callLLM } = deps;
105
+ const executeTool = deps.executeTool ?? defaultExecuteTool;
106
+ const { tokenCounter, contextWindow, compactThreshold } = deps;
107
+ const { hookRunners, sessionId = '', turnIndex = 0 } = deps;
108
+ // 步骤轨迹记录
109
+ const steps = [];
110
+ let finalText = '';
111
+ let status = 'ok';
112
+ let errorMessage;
113
+ const turnUsage = {
114
+ prompt: 0,
115
+ completion: 0,
116
+ total: 0,
117
+ };
118
+ // 1. 将用户输入追加到历史
119
+ history.push({ role: 'user', content: input });
120
+ // Phase 7:发射 onTurnStart Hook
121
+ if (hookRunners) {
122
+ await runHook(hookRunners, 'onTurnStart', {
123
+ sessionId,
124
+ turn: turnIndex,
125
+ input,
126
+ history: snapshotHistory(history),
127
+ });
128
+ }
129
+ // 2. ReAct 主循环
130
+ for (let step = 0; step < MAX_STEPS; step++) {
131
+ // Phase 6:在调用 LLM 前检测 token 使用量
132
+ if (tokenCounter && contextWindow && compactThreshold) {
133
+ const currentTokens = tokenCounter.countMessages(history);
134
+ const thresholdTokens = Math.floor(contextWindow * (compactThreshold / 100));
135
+ // Phase 7:发射 onContextUsage Hook(替代硬编码 console.log)
136
+ if (hookRunners) {
137
+ await runHook(hookRunners, 'onContextUsage', {
138
+ sessionId,
139
+ turn: turnIndex,
140
+ step,
141
+ promptTokens: currentTokens,
142
+ contextWindow,
143
+ thresholdTokens,
144
+ usagePercent: Math.round((currentTokens / contextWindow) * 100),
145
+ });
146
+ }
147
+ // Phase 6:超阈值自动压缩
148
+ if (currentTokens >= thresholdTokens && deps.compactFn) {
149
+ await deps.compactFn();
150
+ }
151
+ }
152
+ // 调用 LLM(超时自动重试,最多 3 次)
153
+ const MAX_RETRIES = 3;
154
+ let normalized;
155
+ let succeeded = false;
156
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
157
+ try {
158
+ const llmResult = await callLLM(history, deps.onAssistantChunk
159
+ ? (chunk) => deps.onAssistantChunk(step, chunk)
160
+ : undefined);
161
+ normalized = normalizeLLMResponse(llmResult);
162
+ succeeded = true;
163
+ break;
164
+ }
165
+ catch (err) {
166
+ const rawMsg = err.message;
167
+ const isTimeout = rawMsg.includes('timeout') || rawMsg.includes('aborted') || rawMsg.includes('stalled');
168
+ if (!isTimeout || attempt === MAX_RETRIES) {
169
+ // Non-timeout error or final retry exhausted
170
+ const msg = isTimeout
171
+ ? `⏱ Timed out after ${MAX_RETRIES} retries — you can continue chatting.`
172
+ : `LLM call failed: ${rawMsg}`;
173
+ history.push({ role: 'assistant', content: msg });
174
+ status = 'error';
175
+ finalText = msg;
176
+ errorMessage = msg;
177
+ break;
178
+ }
179
+ // Timeout but retries remain — notify user and retry
180
+ if (deps.onAssistantChunk) {
181
+ deps.onAssistantChunk(step, `\n⏱ Timeout, retrying (${attempt}/${MAX_RETRIES})...\n`);
182
+ }
183
+ }
184
+ }
185
+ if (!succeeded)
186
+ break;
187
+ const { textContent, toolUseBlocks, usage, reasoningContent } = normalized;
188
+ // 累加 token 用量
189
+ if (usage) {
190
+ turnUsage.prompt = (turnUsage.prompt ?? 0) + (usage.prompt ?? 0);
191
+ turnUsage.completion =
192
+ (turnUsage.completion ?? 0) + (usage.completion ?? 0);
193
+ turnUsage.total = (turnUsage.total ?? 0) + (usage.total ?? 0);
194
+ }
195
+ // 解析响应
196
+ const parsed = parseLLMResponse(textContent, toolUseBlocks);
197
+ // 记录步骤
198
+ const stepTrace = {
199
+ index: step,
200
+ assistantText: textContent,
201
+ parsed,
202
+ tokenUsage: usage,
203
+ };
204
+ steps.push(stepTrace);
205
+ // 将 assistant 消息追加到历史
206
+ if (toolUseBlocks.length > 0) {
207
+ // 有工具调用时,需要带上 tool_calls
208
+ history.push({
209
+ role: 'assistant',
210
+ content: textContent,
211
+ reasoning_content: reasoningContent,
212
+ tool_calls: buildAssistantToolCalls(toolUseBlocks),
213
+ });
214
+ }
215
+ else if (textContent) {
216
+ history.push({
217
+ role: 'assistant',
218
+ content: textContent,
219
+ reasoning_content: reasoningContent,
220
+ });
221
+ }
222
+ // 分支判断
223
+ if (parsed.action) {
224
+ // ── Think → Act → Observe ──
225
+ // 执行所有工具调用(LLM 可能一次返回多个)
226
+ const observations = [];
227
+ for (const block of toolUseBlocks) {
228
+ // Phase 7:发射 onAction Hook(替代硬编码 console.log)
229
+ if (hookRunners) {
230
+ await runHook(hookRunners, 'onAction', {
231
+ sessionId,
232
+ turn: turnIndex,
233
+ step,
234
+ action: { tool: block.name, input: block.input },
235
+ thinking: parsed.thinking,
236
+ history: snapshotHistory(history),
237
+ });
238
+ }
239
+ let observation;
240
+ try {
241
+ observation = await executeTool(block.name, block.input);
242
+ }
243
+ catch (err) {
244
+ observation = `Tool execution error: ${err.message}`;
245
+ }
246
+ observations.push(observation);
247
+ // 每个 tool_call 必须有对应的 tool 消息
248
+ history.push({
249
+ role: 'tool',
250
+ content: observation,
251
+ tool_call_id: block.id,
252
+ name: block.name,
253
+ });
254
+ // Phase 7:发射 onObservation Hook
255
+ if (hookRunners) {
256
+ await runHook(hookRunners, 'onObservation', {
257
+ sessionId,
258
+ turn: turnIndex,
259
+ step,
260
+ tool: block.name,
261
+ observation,
262
+ history: snapshotHistory(history),
263
+ });
264
+ }
265
+ }
266
+ // 记录第一个工具的 observation(用于 stepTrace)
267
+ stepTrace.observation = observations.join('\n---\n');
268
+ stepTrace.toolCallCount = toolUseBlocks.length;
269
+ // 继续循环(Observe → 下一轮 Think)
270
+ continue;
271
+ }
272
+ if (parsed.final) {
273
+ // ── 最终回答 ──
274
+ finalText = parsed.final;
275
+ // Phase 7:发射 onFinal Hook
276
+ if (hookRunners) {
277
+ await runHook(hookRunners, 'onFinal', {
278
+ sessionId,
279
+ turn: turnIndex,
280
+ finalText,
281
+ status: 'ok',
282
+ steps,
283
+ turnUsage,
284
+ });
285
+ }
286
+ break;
287
+ }
288
+ // ── 既无 action 也无 final → 异常退出,防止死循环 ──
289
+ finalText =
290
+ textContent || 'No response from LLM.';
291
+ break;
292
+ }
293
+ // 超过最大步骤数保护
294
+ if (!finalText && status === 'ok') {
295
+ status = 'error';
296
+ errorMessage = `Exceeded max steps (${MAX_STEPS}).`;
297
+ finalText = 'I reached the maximum number of steps. Please try a simpler request.';
298
+ }
299
+ // Turn 结束,清理本轮授权
300
+ if (deps.clearApprovalsFn) {
301
+ deps.clearApprovalsFn();
302
+ }
303
+ return {
304
+ finalText,
305
+ steps,
306
+ status,
307
+ errorMessage,
308
+ tokenUsage: turnUsage,
309
+ };
310
+ }
311
+ //# sourceMappingURL=react-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-loop.js","sourceRoot":"","sources":["../../src/runtime/react-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file Unit tests for ReAct Loop Parsing (Phase 2).
3
+ *
4
+ * Tests: parseLLMResponse, normalizeLLMResponse
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=react-loop.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-loop.test.d.ts","sourceRoot":"","sources":["../../src/runtime/react-loop.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @file Unit tests for ReAct Loop Parsing (Phase 2).
3
+ *
4
+ * Tests: parseLLMResponse, normalizeLLMResponse
5
+ */
6
+ import { describe, it, expect } from 'vitest';
7
+ import { parseLLMResponse, normalizeLLMResponse, } from './react-loop.js';
8
+ describe('parseLLMResponse', () => {
9
+ it('should return direct answer for pure text response', () => {
10
+ const parsed = parseLLMResponse('Here is the answer.', []);
11
+ expect(parsed.final).toBe('Here is the answer.');
12
+ expect(parsed.action).toBeUndefined();
13
+ });
14
+ it('should parse simple tool calls', () => {
15
+ const parsed = parseLLMResponse('', [
16
+ { id: 'call_1', name: 'bash', input: { command: 'ls' } },
17
+ ]);
18
+ expect(parsed.action).toBeDefined();
19
+ expect(parsed.action?.tool).toBe('bash');
20
+ expect(parsed.action?.input).toEqual({ command: 'ls' });
21
+ expect(parsed.thinking).toBeUndefined();
22
+ });
23
+ it('should extract thought text when both text and tools are present', () => {
24
+ const parsed = parseLLMResponse('I should run ls.', [
25
+ { id: 'call_1', name: 'bash', input: { command: 'ls' } },
26
+ ]);
27
+ expect(parsed.action).toBeDefined();
28
+ expect(parsed.thinking).toBe('I should run ls.');
29
+ });
30
+ it('should handle multiple tool calls', () => {
31
+ const parsed = parseLLMResponse('Running two commands.', [
32
+ { id: 'c1', name: 'bash', input: { command: 'ls' } },
33
+ { id: 'c2', name: 'bash', input: { command: 'pwd' } },
34
+ ]);
35
+ expect(parsed.action).toBeDefined();
36
+ expect(parsed.actions).toHaveLength(2);
37
+ expect(parsed.action?.tool).toBe('bash');
38
+ });
39
+ it('should handle empty input safely', () => {
40
+ const parsed = parseLLMResponse('', []);
41
+ expect(parsed.final).toBeUndefined();
42
+ expect(parsed.action).toBeUndefined();
43
+ });
44
+ });
45
+ describe('normalizeLLMResponse', () => {
46
+ it('should split ContentBlocks into text and tools', () => {
47
+ const response = {
48
+ stop_reason: 'tool_use',
49
+ content: [
50
+ { type: 'text', text: 'hi\n' },
51
+ { type: 'tool_use', id: 't1', name: 'bash', input: { command: 'ls' } },
52
+ { type: 'text', text: 'more text' },
53
+ ],
54
+ usage: { prompt: 10, completion: 5, total: 15 },
55
+ };
56
+ const normalized = normalizeLLMResponse(response);
57
+ expect(normalized.textContent).toBe('hi\nmore text');
58
+ expect(normalized.toolUseBlocks).toHaveLength(1);
59
+ expect(normalized.toolUseBlocks[0].id).toBe('t1');
60
+ expect(normalized.toolUseBlocks[0].name).toBe('bash');
61
+ expect(normalized.toolUseBlocks[0].input).toEqual({ command: 'ls' });
62
+ expect(normalized.stopReason).toBe('tool_use');
63
+ expect(normalized.usage?.prompt).toBe(10);
64
+ });
65
+ it('should handle pure text', () => {
66
+ const response = { stop_reason: 'end_turn', content: [{ type: 'text', text: 'just text' }] };
67
+ const normalized = normalizeLLMResponse(response);
68
+ expect(normalized.textContent).toBe('just text');
69
+ expect(normalized.toolUseBlocks).toHaveLength(0);
70
+ });
71
+ it('should handle only tool calls', () => {
72
+ const response = { stop_reason: 'tool_use', content: [{ type: 'tool_use', id: 't1', name: 'bash', input: {} }] };
73
+ const normalized = normalizeLLMResponse(response);
74
+ expect(normalized.textContent).toBe('');
75
+ expect(normalized.toolUseBlocks).toHaveLength(1);
76
+ });
77
+ });
78
+ //# sourceMappingURL=react-loop.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-loop.test.js","sourceRoot":"","sources":["../../src/runtime/react-loop.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @file Session 类 — 管理多轮对话状态。
3
+ *
4
+ * Phase 2:持有对话历史和 LLM 调用函数,
5
+ * 委托 `runTurn()` 执行 ReAct 循环。
6
+ *
7
+ * Phase 6:新增上下文压缩能力:
8
+ * - 接受 contextWindow / compactThreshold / tokenCounter 配置
9
+ * - 暴露 compactHistory() 公开方法
10
+ * - 将压缩相关依赖传递给 runTurn()
11
+ */
12
+ import type { CallLLM, ChatMessage, TurnResult, ExecuteTool, TokenCounter, CompactReason, CompactResult, AgentHooks, AgentMiddleware, HistorySink } from '../types.js';
13
+ /** Session 构造参数。 */
14
+ export type SessionOptions = {
15
+ /** LLM 调用函数(必选)。 */
16
+ callLLM: CallLLM;
17
+ /** 系统提示词(可选)。 */
18
+ systemPrompt?: string;
19
+ /** 工具执行函数(可选,Phase 2 默认 mock)。 */
20
+ executeTool?: ExecuteTool;
21
+ /** 自定义 Session ID(默认随机 UUID)。 */
22
+ sessionId?: string;
23
+ /** Token 计数器(Phase 6,启用压缩必需)。 */
24
+ tokenCounter?: TokenCounter;
25
+ /** 上下文窗口大小(token 数,默认 128000)。 */
26
+ contextWindow?: number;
27
+ /** 自动压缩阈值百分比(0-100,默认 80)。 */
28
+ compactThreshold?: number;
29
+ /** Hook 集合(Phase 7,一次性注入)。 */
30
+ hooks?: AgentHooks;
31
+ /** 中间件列表(Phase 7,支持多个)。 */
32
+ middlewares?: AgentMiddleware[];
33
+ /** 清除 once 授权回调(Phase 4,由外界注入给 Session)。 */
34
+ clearApprovalsFn?: () => void;
35
+ /** 历史事件写入器(Phase 10,可选)。 */
36
+ historySink?: HistorySink;
37
+ /** 流式 chunk 回调(TUI 实时渲染)。 */
38
+ onAssistantChunk?: (step: number, chunk: string) => void;
39
+ };
40
+ /**
41
+ * Agent Session 类。
42
+ *
43
+ * 用法:
44
+ * ```ts
45
+ * const session = new Session({ callLLM, systemPrompt: '...' })
46
+ * const result = await session.runTurn('你好')
47
+ * console.log(result.finalText)
48
+ * ```
49
+ */
50
+ export declare class Session {
51
+ /** Session 唯一标识。 */
52
+ readonly id: string;
53
+ /** 对话历史。 */
54
+ readonly history: ChatMessage[];
55
+ /** 轮次计数器。 */
56
+ private turnIndex;
57
+ /** 获取当前轮次。 */
58
+ get currentTurn(): number;
59
+ /** LLM 调用函数。 */
60
+ private readonly callLLM;
61
+ /** 工具执行函数。 */
62
+ private readonly executeTool?;
63
+ /** Token 计数器。 */
64
+ private readonly tokenCounter?;
65
+ /** 上下文窗口大小。 */
66
+ private readonly contextWindow;
67
+ /** 自动压缩阈值百分比。 */
68
+ private readonly compactThreshold;
69
+ /** Hook 注册表。 */
70
+ private readonly hookRunners;
71
+ /** 清除 once 授权回调。 */
72
+ private readonly clearApprovalsFn?;
73
+ /** 历史事件写入器。 */
74
+ private readonly historySink?;
75
+ /** 流式 chunk 回调。 */
76
+ private readonly onAssistantChunk?;
77
+ constructor(options: SessionOptions);
78
+ /**
79
+ * 执行一轮对话。
80
+ *
81
+ * 委托给 react-loop.ts 的 runTurn(),传入当前 history。
82
+ * history 会被 runTurn() 就地修改(追加用户/助手/工具消息)。
83
+ */
84
+ runTurn(input: string): Promise<TurnResult>;
85
+ /** 返回当前对话历史的副本。 */
86
+ getHistory(): ChatMessage[];
87
+ /** 获取当前轮次数。 */
88
+ getTurnIndex(): number;
89
+ /**
90
+ * 手动或自动压缩对话历史。
91
+ *
92
+ * 流程:
93
+ * 1. 用 tokenCounter 计算当前 token 数
94
+ * 2. 提取 system 消息外的历史
95
+ * 3. 调用 LLM 生成摘要
96
+ * 4. 用摘要重建历史
97
+ */
98
+ compactHistory(reason?: CompactReason): Promise<CompactResult>;
99
+ /**
100
+ * 为当前会话生成标题(Phase 7)。
101
+ */
102
+ generateTitle(): Promise<string>;
103
+ /**
104
+ * 允许外部(如 index.ts 的回调)触发内部 Hook。
105
+ * @internal
106
+ */
107
+ _runHook<K extends keyof AgentHooks>(event: K, payload: Parameters<NonNullable<AgentHooks[K]>>[0]): Promise<void>;
108
+ }
109
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/runtime/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EACR,OAAO,EACP,WAAW,EACX,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,eAAe,EACf,WAAW,EACd,MAAM,aAAa,CAAA;AAKpB,oBAAoB;AACpB,MAAM,MAAM,cAAc,GAAG;IACzB,oBAAoB;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,iBAAiB;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kCAAkC;IAClC,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iCAAiC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8BAA8B;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,2BAA2B;IAC3B,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;IAC/B,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC7B,4BAA4B;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3D,CAAA;AAID;;;;;;;;;GASG;AACH,qBAAa,OAAO;IAChB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAEnB,YAAY;IACZ,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,CAAK;IAEpC,aAAa;IACb,OAAO,CAAC,SAAS,CAAI;IAErB,cAAc;IACd,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,gBAAgB;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,cAAc;IACd,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAa;IAE1C,iBAAiB;IACjB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAc;IAE5C,eAAe;IACf,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IAEtC,iBAAiB;IACjB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAEzC,gBAAgB;IAChB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAe;IAE3C,oBAAoB;IACpB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAY;IAE9C,eAAe;IACf,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAa;IAE1C,mBAAmB;IACnB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAuC;gBAE7D,OAAO,EAAE,cAAc;IAqBnC;;;;;OAKG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgDjD,mBAAmB;IACnB,UAAU,IAAI,WAAW,EAAE;IAI3B,eAAe;IACf,YAAY,IAAI,MAAM;IAItB;;;;;;;;OAQG;IACG,cAAc,CAChB,MAAM,GAAE,aAAwB,GACjC,OAAO,CAAC,aAAa,CAAC;IAkHzB;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAetC;;;OAGG;IACG,QAAQ,CAAC,CAAC,SAAS,MAAM,UAAU,EACrC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC;CAGnB"}