@yeaft/webchat-agent 1.0.350 → 1.0.352

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 (45) hide show
  1. package/connection/message-router.js +29 -1
  2. package/index.js +2 -0
  3. package/local-runtime/server/handlers/agent-sync.js +14 -0
  4. package/local-runtime/server/handlers/client-misc.js +21 -0
  5. package/local-runtime/version.json +1 -1
  6. package/local-runtime/web/app.bundle.js +110 -97
  7. package/local-runtime/web/app.bundle.js.gz +0 -0
  8. package/local-runtime/web/index.html +2 -2
  9. package/local-runtime/web/style.bundle.css +1 -1
  10. package/local-runtime/web/style.bundle.css.gz +0 -0
  11. package/package.json +1 -1
  12. package/yeaft/config-api.js +53 -1
  13. package/yeaft/config.js +54 -0
  14. package/yeaft/conversation/history-index-worker.js +13 -10
  15. package/yeaft/conversation/internal-control.js +1 -0
  16. package/yeaft/debug-trace.js +164 -47
  17. package/yeaft/engine.js +318 -28
  18. package/yeaft/llm/adapter.js +38 -0
  19. package/yeaft/llm/anthropic.js +11 -8
  20. package/yeaft/llm/openai-responses.js +11 -8
  21. package/yeaft/llm/router.js +1 -1
  22. package/yeaft/perf-trace.js +156 -24
  23. package/yeaft/session.js +7 -0
  24. package/yeaft/sessions/session-crud.js +19 -4
  25. package/yeaft/sub-agent/runner.js +4 -0
  26. package/yeaft/tools/agent.js +4 -0
  27. package/yeaft/tools/ask-user.js +1 -0
  28. package/yeaft/tools/bash.js +4 -0
  29. package/yeaft/tools/create-work-item.js +3 -0
  30. package/yeaft/tools/file-read.js +1 -0
  31. package/yeaft/tools/glob.js +1 -0
  32. package/yeaft/tools/grep.js +1 -0
  33. package/yeaft/tools/history-search.js +74 -20
  34. package/yeaft/tools/js-repl.js +1 -0
  35. package/yeaft/tools/list-agents.js +1 -0
  36. package/yeaft/tools/list-dir.js +1 -0
  37. package/yeaft/tools/list-tasks.js +1 -0
  38. package/yeaft/tools/read-task-log.js +1 -0
  39. package/yeaft/tools/route-forward.js +4 -0
  40. package/yeaft/tools/send-message.js +3 -0
  41. package/yeaft/tools/types.js +8 -0
  42. package/yeaft/tools/wait-agent.js +1 -0
  43. package/yeaft/utf8.js +44 -0
  44. package/yeaft/web-bridge.js +6 -0
  45. package/yeaft/work-center/runner.js +1 -0
@@ -21,6 +21,7 @@ export default defineTool({
21
21
  },
22
22
  isConcurrencySafe: () => true,
23
23
  isReadOnly: () => true,
24
+ cacheWithinQuery: false,
24
25
  async execute(input = {}, ctx = {}) {
25
26
  if (!ctx.taskManager) return JSON.stringify({ error: 'task manager unavailable' });
26
27
  const sessionId = input.sessionId || ctx.sessionId || null;
@@ -23,6 +23,7 @@ export default defineTool({
23
23
  },
24
24
  isConcurrencySafe: () => true,
25
25
  isReadOnly: () => true,
26
+ cacheWithinQuery: false,
26
27
  async execute(input = {}, ctx = {}) {
27
28
  if (!ctx.taskManager) return JSON.stringify({ error: 'task manager unavailable' });
28
29
  const taskId = input.taskId;
@@ -104,6 +104,10 @@ Returns JSON: { ok, dispatched?, error?, detail? }.`,
104
104
  },
105
105
  isConcurrencySafe: () => false,
106
106
  isReadOnly: () => false,
107
+ // A handoff can start another VP on the shared workDir while this query
108
+ // still has queued tool calls. Conservatively disable reuse even when the
109
+ // dispatch is rejected; the cost is one fresh read, not stale workspace data.
110
+ mayMutateWorkspaceAfterReturn: () => true,
107
111
  async execute(input, ctx = {}) {
108
112
  const { to, text, reason } = input || {};
109
113
  if (!to || typeof to !== 'string') {
@@ -62,6 +62,9 @@ SpawnAgent -> (PromptAgent <-> WaitAgent)+ -> CloseAgent -> 最终回复给用
62
62
  },
63
63
  isConcurrencySafe: () => false,
64
64
  isReadOnly: () => false,
65
+ // Queueing a prompt can resume an existing writable child after this
66
+ // orchestration call returns, so parent filesystem snapshots are stale-risk.
67
+ mayMutateWorkspaceAfterReturn: () => true,
65
68
  async execute(input, ctx) {
66
69
  // NB: next_steps is the FIRST envelope field — the registry's
67
70
  // model-context tail truncation would eat it if it lived at the end.
@@ -68,6 +68,8 @@
68
68
  * @property {(input: object, ctx?: ToolContext) => Promise<string>} execute — execution function
69
69
  * @property {(input?: object) => boolean} [isConcurrencySafe] — can run in parallel?
70
70
  * @property {(input?: object) => boolean} [isReadOnly] — read-only operation?
71
+ * @property {boolean | ((input?: object) => boolean)} [cacheWithinQuery] — explicitly safe to reuse for identical calls in one query
72
+ * @property {boolean | ((input?: object) => boolean)} [mayMutateWorkspaceAfterReturn] — may keep changing the workspace after execute() resolves; disables same-query read reuse
71
73
  * @property {(input?: object) => boolean} [isDestructive] — destructive operation?
72
74
  * @property {'json-error-envelope' | null} [errorOutput] — explicit returned-output error contract; null means only thrown errors fail
73
75
  * @property {'external' | 'run'} [sideEffectScope] — whether mutations escape the current Run collector
@@ -83,6 +85,8 @@
83
85
  * execute: (input: object, ctx?: ToolContext) => Promise<string>,
84
86
  * isConcurrencySafe?: (input?: object) => boolean,
85
87
  * isReadOnly?: (input?: object) => boolean,
88
+ * cacheWithinQuery?: boolean | ((input?: object) => boolean),
89
+ * mayMutateWorkspaceAfterReturn?: boolean | ((input?: object) => boolean),
86
90
  * isDestructive?: (input?: object) => boolean,
87
91
  * errorOutput?: 'json-error-envelope' | null,
88
92
  * sideEffectScope?: 'external' | 'run',
@@ -98,6 +102,8 @@ export function defineTool({
98
102
  execute,
99
103
  isConcurrencySafe = () => false,
100
104
  isReadOnly = () => false,
105
+ cacheWithinQuery = false,
106
+ mayMutateWorkspaceAfterReturn = false,
101
107
  isDestructive = () => false,
102
108
  errorOutput = 'json-error-envelope',
103
109
  sideEffectScope = 'external',
@@ -113,6 +119,8 @@ export function defineTool({
113
119
  execute,
114
120
  isConcurrencySafe,
115
121
  isReadOnly,
122
+ cacheWithinQuery,
123
+ mayMutateWorkspaceAfterReturn,
116
124
  isDestructive,
117
125
  errorOutput,
118
126
  sideEffectScope,
@@ -246,6 +246,7 @@ notification 获取完成事件。`
246
246
  timeoutMs: 305000,
247
247
  isConcurrencySafe: () => true,
248
248
  isReadOnly: () => true,
249
+ cacheWithinQuery: false,
249
250
  async execute(input, ctx) {
250
251
  const { agent_id, timeout_ms = 5000 } = input;
251
252
  if (!agent_id) {
package/yeaft/utf8.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * utf8.js — Small UTF-8 byte-budget helpers shared by Agent internals.
3
+ *
4
+ * JavaScript strings are UTF-16. Convert malformed lone surrogates before
5
+ * walking code points so a byte-limited preview is always well-formed Unicode.
6
+ */
7
+
8
+ export function normalizeUtf8ByteBudget(value, fallback = 0) {
9
+ const parsed = Number(value);
10
+ return Number.isFinite(parsed)
11
+ ? Math.max(0, Math.floor(parsed))
12
+ : Math.max(0, Math.floor(Number(fallback) || 0));
13
+ }
14
+
15
+ export function toWellFormedText(value) {
16
+ return String(value ?? '').toWellFormed();
17
+ }
18
+
19
+ /**
20
+ * Return the longest well-formed UTF-8 prefix within maxBytes.
21
+ *
22
+ * This makes one code-point pass and one final slice. It deliberately does
23
+ * not repeatedly rescan successively shorter UTF-16 slices with
24
+ * Buffer.byteLength(), which becomes quadratic for oversized payloads.
25
+ */
26
+ export function utf8PrefixWithinBytes(value, maxBytes) {
27
+ const text = toWellFormedText(value);
28
+ const limit = normalizeUtf8ByteBudget(maxBytes);
29
+ let end = 0;
30
+ let bytes = 0;
31
+
32
+ while (end < text.length) {
33
+ const codePoint = text.codePointAt(end);
34
+ const width = codePoint > 0xFFFF ? 2 : 1;
35
+ const byteLength = codePoint <= 0x7F
36
+ ? 1
37
+ : (codePoint <= 0x7FF ? 2 : (codePoint <= 0xFFFF ? 3 : 4));
38
+ if (bytes + byteLength > limit) break;
39
+ bytes += byteLength;
40
+ end += width;
41
+ }
42
+
43
+ return { text: text.slice(0, end), bytes };
44
+ }
@@ -192,6 +192,11 @@ export async function refreshLiveSessionConfig(options = {}) {
192
192
 
193
193
  const configRoot = liveConfigRoot();
194
194
  const freshConfig = loadConfig({ dir: configRoot });
195
+ // All bridge producers reference ctx.CONFIG. Keep it synchronized even
196
+ // when no live Session is loaded and refresh returns early below.
197
+ if (ctx.CONFIG && typeof ctx.CONFIG === 'object') {
198
+ ctx.CONFIG.telemetry = freshConfig.telemetry;
199
+ }
195
200
  const previousDefaultModel = options.previousDefaultModel
196
201
  || session?.config?.primaryModel
197
202
  || session?.config?.model
@@ -234,6 +239,7 @@ export async function refreshLiveSessionConfig(options = {}) {
234
239
  for (const key of Object.keys(currentConfig)) delete currentConfig[key];
235
240
  Object.assign(currentConfig, nextConfig);
236
241
  liveSession.engine?.refreshConfig?.(currentConfig);
242
+ liveSession.trace?.refreshConfig?.(currentConfig.telemetry || {});
237
243
  for (const { key, engine, config } of vpConfigSnapshots) {
238
244
  engine.refreshConfig?.(config);
239
245
  vpEngineConfigKeys.set(key, engineConfigKey(config));
@@ -823,6 +823,7 @@ export class WorkItemRunner {
823
823
  this.trace = options.trace || createTrace({
824
824
  enabled: options.debug === true && Boolean(options.yeaftDir),
825
825
  dirPath: options.yeaftDir || null,
826
+ textMaxBytes: options.config?.telemetry?.traceTextMaxBytes,
826
827
  });
827
828
  this.actionWorktreeRoot = options.actionWorktreeRoot || null;
828
829
  this.store = options.store;