@yeaft/webchat-agent 0.1.773 → 0.1.775

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.773",
3
+ "version": "0.1.775",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/unify/engine.js CHANGED
@@ -1230,8 +1230,8 @@ export class Engine {
1230
1230
  // `turnStartIdx` is where the current user message lives; the arc
1231
1231
  // we may collapse spans (arcStartIdx .. last assistant/tool).
1232
1232
  //
1233
- // Periodic-T1 fix: T1 must fire EVERY TOOL_BATCH_SIZE (13) tool
1234
- // calls, not just the first 13. So instead of a one-shot boolean,
1233
+ // Periodic-T1 fix: T1 must fire EVERY TOOL_BATCH_SIZE (30) tool
1234
+ // calls, not just the first batch. So instead of a one-shot boolean,
1235
1235
  // track:
1236
1236
  // • `lastT1AtToolCount` — toolCount snapshot at the last T1
1237
1237
  // ATTEMPT (success OR error). Trigger when
@@ -1797,6 +1797,7 @@ export class Engine {
1797
1797
  originalUserMsg: prompt,
1798
1798
  toolPairs: pairs,
1799
1799
  assistantText,
1800
+ language: this.#config.language,
1800
1801
  signal,
1801
1802
  });
1802
1803
  // Detach: never await. The promise outlives this query() and
@@ -2034,9 +2035,9 @@ export class Engine {
2034
2035
  }
2035
2036
 
2036
2037
  // PR-L: T1 in-turn (synchronous) reflection. Fires once per
2037
- // adapter loop iteration where ≥ TOOL_BATCH_SIZE (13) tool
2038
+ // adapter loop iteration where ≥ TOOL_BATCH_SIZE (30) tool
2038
2039
  // calls have accumulated since the last T1 firing — not just
2039
- // the first 13 of the query(). Generates a markdown reflection
2040
+ // the first batch of the query(). Generates a markdown reflection
2040
2041
  // over the assistant+tool arc since the last T1 firing (or
2041
2042
  // since the user prompt for the first batch) and rewrites that
2042
2043
  // range to a SINGLE synthetic user message before the next
@@ -2044,10 +2045,10 @@ export class Engine {
2044
2045
  //
2045
2046
  // Loop semantics:
2046
2047
  // - First batch: arcStartIdx = turnStartIdx + 1, fires when
2047
- // queryToolCount reaches 13.
2048
+ // queryToolCount reaches TOOL_BATCH_SIZE.
2048
2049
  // - Each subsequent batch: arcStartIdx is updated to the slot
2049
2050
  // right after the just-inserted reflection message; fires
2050
- // again whenever 13 more tools have run since
2051
+ // again whenever TOOL_BATCH_SIZE more tools have run since
2051
2052
  // lastT1AtToolCount.
2052
2053
  // - The dedup Set key includes `lastT1AtToolCount` so each
2053
2054
  // batch within the same query gets a distinct entry — without
@@ -2084,6 +2085,7 @@ export class Engine {
2084
2085
  originalUserMsg: prompt,
2085
2086
  toolPairs: pairs,
2086
2087
  assistantText,
2088
+ language: this.#config.language,
2087
2089
  signal,
2088
2090
  });
2089
2091
  const next = collapseRangeToReflection(
@@ -2129,8 +2131,8 @@ export class Engine {
2129
2131
  };
2130
2132
  // Advance lastT1AtToolCount past this batch so we don't
2131
2133
  // tight-loop on a hiccuping reflector. The next attempt is
2132
- // 13 tools from now, not immediately. arcStartIdx is left
2133
- // alone because history wasn't rewritten — the tail still
2134
+ // TOOL_BATCH_SIZE tools from now, not immediately. arcStartIdx is
2135
+ // left alone because history wasn't rewritten — the tail still
2134
2136
  // begins where it did. The trade-off: the next batch's
2135
2137
  // reflection will cover the tools that just failed too,
2136
2138
  // which is fine (they're still in conversationMessages).
@@ -9,14 +9,25 @@
9
9
  * - Duplicate-reminder text formatter
10
10
  *
11
11
  * The constants are NOT config-driven — V7 design freezes them in code.
12
+ *
13
+ * TOOL_BATCH_SIZE history: was 13 originally; raised to 30 (2026-05-15)
14
+ * after user feedback that 13 fired too often inside a single task and
15
+ * fragmented otherwise-coherent tool arcs into multiple reflections. 30
16
+ * keeps the periodic-reflection contract (it still fires every N tools,
17
+ * not just once) but gives a single task arc room to breathe before the
18
+ * arc gets collapsed.
12
19
  */
13
20
 
14
- export const TOOL_BATCH_SIZE = 13;
21
+ export const TOOL_BATCH_SIZE = 30;
15
22
  export const TURN_SUMMARY_THRESHOLD = 5;
16
23
  export const DUP_TOOL_THRESHOLD = 3;
17
24
 
18
25
  export { ExecLog, buildEntry, argsHashOf } from './exec-log.js';
19
- export { buildReflectionPrompt, REFLECTION_TEMPLATE } from './reflection-prompt.js';
26
+ export {
27
+ buildReflectionPrompt,
28
+ REFLECTION_TEMPLATE_EN,
29
+ REFLECTION_TEMPLATE_ZH,
30
+ } from './reflection-prompt.js';
20
31
  export { runT1Reflection } from './t1-reflector.js';
21
32
  export { runT2Reflection } from './t2-reflector.js';
22
33
  export { buildFallbackStub } from './fallback-stub.js';
@@ -5,9 +5,21 @@
5
5
  * The primary model is asked to REFLECT on a sequence of tool calls — not
6
6
  * just summarise. The output is markdown with five fixed sections so the
7
7
  * frontend ReflectionCard can render each independently.
8
+ *
9
+ * Bilingual: the prompt itself is rendered in the user's language (en/zh)
10
+ * so the model's output (which the user reads inside the ReflectionCard
11
+ * and which the next adapter loop sees as a synthetic user message) is
12
+ * in the matching language. Falls back to English for any other lang
13
+ * value or when `language` is omitted.
14
+ *
15
+ * Section headings stay English in both templates because the frontend
16
+ * ReflectionCard parses them by literal string match ("## What was
17
+ * attempted" et al.) and a localised heading would break rendering.
8
18
  */
9
19
 
10
- const TEMPLATE = `You are reviewing a sequence of {N} tool calls executed by an AI agent.
20
+ import { isZhLanguage } from '../prompts.js';
21
+
22
+ const TEMPLATE_EN = `You are reviewing a sequence of {N} tool calls executed by an AI agent.
11
23
  Your job is NOT just to summarize, but to REFLECT.
12
24
 
13
25
  Output as markdown with these exact sections:
@@ -39,19 +51,54 @@ User original request:
39
51
  Tool execution sequence:
40
52
  {toolCallsAndResults}`;
41
53
 
54
+ const TEMPLATE_ZH = `你正在复盘一个 AI agent 刚刚执行的 {N} 次工具调用。
55
+ 你的任务不是简单总结,而是要"反思"。
56
+
57
+ 请用 markdown 输出,必须严格包含以下五个段落(标题保持英文,便于前端解析):
58
+
59
+ ## What was attempted
60
+ 2-3 句话说明本批工具调用的目标和动作链路。
61
+
62
+ ## Key findings
63
+ 具体事实:路径、行号、ID、错误码(必须逐字保留原始值,不要改写)。
64
+
65
+ ## Direction check
66
+ - 当前轨迹是否仍然贴合用户最初的请求?
67
+ - 是否有偏离 / 范围漂移?
68
+ - 是否有看起来重复或多余的工具调用?
69
+ - 是否有迹象表明陷入了无效循环?
70
+
71
+ ## Suggested next direction
72
+ 下一轮 loop 应该聚焦什么?应该避免什么?
73
+
74
+ ## Tool execution log
75
+ 紧凑列表:<tool_name> × <count>(附上值得注意的参数)。
76
+
77
+ 注意:所有标识符、路径、URL、行号、错误消息必须逐字保留,不允许改写数据值。
78
+
79
+ 用户最初的请求:
80
+ {originalUserMessage}
81
+
82
+ 工具执行序列:
83
+ {toolCallsAndResults}`;
84
+
42
85
  /**
43
86
  * Render the prompt.
44
87
  *
45
- * @param {{ originalUserMsg: string, toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>, assistantText?: string }} p
88
+ * @param {{ originalUserMsg: string, toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>, assistantText?: string, language?: string }} p
46
89
  * @returns {string}
47
90
  */
48
- export function buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText }) {
91
+ export function buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText, language }) {
49
92
  const N = toolPairs.length;
50
93
  const seq = toolPairs.map((p, i) => formatPair(i + 1, p)).join('\n\n');
94
+ const isZh = isZhLanguage(language);
51
95
  const head = assistantText && assistantText.trim()
52
- ? `Assistant text emitted during this batch:\n${assistantText.trim()}\n\n`
96
+ ? (isZh
97
+ ? `本批工具调用期间助手输出的文本:\n${assistantText.trim()}\n\n`
98
+ : `Assistant text emitted during this batch:\n${assistantText.trim()}\n\n`)
53
99
  : '';
54
- return TEMPLATE
100
+ const template = isZh ? TEMPLATE_ZH : TEMPLATE_EN;
101
+ return template
55
102
  .replace('{N}', String(N))
56
103
  .replace('{originalUserMessage}', String(originalUserMsg || '').slice(0, 4000))
57
104
  .replace('{toolCallsAndResults}', head + seq);
@@ -71,4 +118,8 @@ function formatPair(idx, p) {
71
118
  return `[${idx}] ${p.name}${status}\n args: ${inputStr}\n result: ${outputStr}`;
72
119
  }
73
120
 
74
- export const REFLECTION_TEMPLATE = TEMPLATE;
121
+ // Section headings stay English in both templates because ReflectionCard
122
+ // parses them by literal string match. Exported separately so a debug panel
123
+ // or test can compare against either dictionary directly.
124
+ export const REFLECTION_TEMPLATE_EN = TEMPLATE_EN;
125
+ export const REFLECTION_TEMPLATE_ZH = TEMPLATE_ZH;
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * t1-reflector.js — V7 in-turn (synchronous) reflection (PR-L).
3
3
  *
4
- * Triggered when the current turn has accumulated TOOL_BATCH_SIZE (13) tool
4
+ * Triggered when the current turn has accumulated TOOL_BATCH_SIZE (30) tool
5
5
  * results and the engine is about to loop back into adapter.stream(). Calls
6
6
  * the PRIMARY model — never the fast model — to generate a markdown
7
7
  * reflection over the batch.
8
8
  *
9
9
  * On success: returns { content, durationMs }.
10
10
  * On failure: throws (engine catches and leaves history unchanged).
11
+ *
12
+ * The `language` param is forwarded to buildReflectionPrompt so the prompt
13
+ * (and therefore the model's response) renders in the user's language. The
14
+ * engine reads this from config.language and passes it through.
11
15
  */
12
16
 
13
17
  import { buildReflectionPrompt } from './reflection-prompt.js';
@@ -19,13 +23,14 @@ import { buildReflectionPrompt } from './reflection-prompt.js';
19
23
  * originalUserMsg: string,
20
24
  * toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>,
21
25
  * assistantText?: string,
26
+ * language?: string,
22
27
  * signal?: AbortSignal,
23
28
  * }} p
24
29
  * @returns {Promise<{ content: string, durationMs: number }>}
25
30
  */
26
- export async function runT1Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, signal }) {
31
+ export async function runT1Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, language, signal }) {
27
32
  const t0 = Date.now();
28
- const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText });
33
+ const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText, language });
29
34
  const result = await adapter.call({
30
35
  model,
31
36
  system: prompt,
@@ -17,13 +17,14 @@ import { buildReflectionPrompt } from './reflection-prompt.js';
17
17
  * originalUserMsg: string,
18
18
  * toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>,
19
19
  * assistantText?: string,
20
+ * language?: string,
20
21
  * signal?: AbortSignal,
21
22
  * }} p
22
23
  * @returns {Promise<{ content: string, durationMs: number }>}
23
24
  */
24
- export async function runT2Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, signal }) {
25
+ export async function runT2Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, language, signal }) {
25
26
  const t0 = Date.now();
26
- const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText });
27
+ const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText, language });
27
28
  const result = await adapter.call({
28
29
  model,
29
30
  system: prompt,