@yeaft/webchat-agent 1.0.325 → 1.0.328
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/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +164 -139
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/engine.js +175 -68
- package/yeaft/memory/ams.js +18 -8
- package/yeaft/tasks/manager.js +48 -11
- package/yeaft/tools/bash.js +42 -11
- package/yeaft/tools/process-runner.js +24 -10
- package/yeaft/tools/types.js +5 -0
- package/yeaft/web-bridge.js +5 -6
package/yeaft/tools/bash.js
CHANGED
|
@@ -29,23 +29,18 @@ const DEFAULT_TIMEOUT_MS = 120_000;
|
|
|
29
29
|
|
|
30
30
|
/** Max timeout in ms (10 minutes). */
|
|
31
31
|
const MAX_TIMEOUT_MS = 600_000;
|
|
32
|
-
/**
|
|
33
|
-
* ToolRegistry must not preempt Bash's own process timeout. Bash terminates
|
|
34
|
-
* the process tree and waits for close before returning exit 124; the grace
|
|
35
|
-
* covers TERM/KILL escalation and the bounded close-confirmation window.
|
|
36
|
-
*/
|
|
37
|
-
const REGISTRY_TIMEOUT_MS = MAX_TIMEOUT_MS + 15_000;
|
|
38
32
|
const LINUX_NAMESPACE_HELPER = resolve(
|
|
39
33
|
dirname(fileURLToPath(import.meta.url)),
|
|
40
34
|
'..',
|
|
41
35
|
'linux-process-namespace.js',
|
|
42
36
|
);
|
|
37
|
+
const RUN_PROCESS_OVERRIDE = Symbol('runProcessOverride');
|
|
43
38
|
|
|
44
39
|
/**
|
|
45
40
|
* Run a command in a child process.
|
|
46
|
-
* @returns {Promise<{ stdout: string, stderr: string, exitCode: number, timedOut: boolean }>}
|
|
41
|
+
* @returns {Promise<{ stdout: string, stderr: string, exitCode: number, timedOut: boolean, terminationError: string | null }>}
|
|
47
42
|
*/
|
|
48
|
-
function runCommand(command, { cwd, timeout, signal, runtimePlatform }) {
|
|
43
|
+
function runCommand(command, { cwd, timeout, signal, runtimePlatform, runProcessImpl = runProcess }) {
|
|
49
44
|
const platform = runtimePlatform || getRuntimePlatformInfo();
|
|
50
45
|
const env = { ...process.env, TERM: 'dumb', FORCE_COLOR: '0' };
|
|
51
46
|
const baseInvocation = buildShellInvocation(command, { runtimePlatform: platform });
|
|
@@ -76,7 +71,7 @@ function runCommand(command, { cwd, timeout, signal, runtimePlatform }) {
|
|
|
76
71
|
systemdControl: null,
|
|
77
72
|
};
|
|
78
73
|
}
|
|
79
|
-
return
|
|
74
|
+
return runProcessImpl(invocation.command, invocation.args, {
|
|
80
75
|
cwd,
|
|
81
76
|
env,
|
|
82
77
|
signal,
|
|
@@ -91,10 +86,11 @@ function runCommand(command, { cwd, timeout, signal, runtimePlatform }) {
|
|
|
91
86
|
stderr: result.stderr,
|
|
92
87
|
exitCode: result.code,
|
|
93
88
|
timedOut: result.timedOut,
|
|
89
|
+
terminationError: result.terminationError || null,
|
|
94
90
|
}));
|
|
95
91
|
}
|
|
96
92
|
|
|
97
|
-
|
|
93
|
+
const bashTool = defineTool({
|
|
98
94
|
name: 'Bash',
|
|
99
95
|
description: {
|
|
100
96
|
en: `Execute a shell command and return its output.
|
|
@@ -111,6 +107,7 @@ Guidelines:
|
|
|
111
107
|
- Use absolute paths when possible
|
|
112
108
|
- Avoid interactive commands (no stdin support)
|
|
113
109
|
- Use background=true for long-running or persistent tasks that should survive across turns
|
|
110
|
+
- Foreground timeout and cleanup status is returned to you; decide whether to inspect, retry, stop, or use a background task
|
|
114
111
|
- stderr is captured separately and included in the result`,
|
|
115
112
|
zh: `执行 Shell 命令并返回输出。
|
|
116
113
|
|
|
@@ -123,6 +120,7 @@ Guidelines:
|
|
|
123
120
|
- 尽量使用绝对路径
|
|
124
121
|
- 避免交互式命令(不支持 stdin)
|
|
125
122
|
- 长时间或需要跨 turn 持续存在的任务使用 background=true
|
|
123
|
+
- 前台命令的超时和清理状态会返回给你;由你决定检查、重试、停止或改用后台任务
|
|
126
124
|
- stderr 单独捕获并包含在结果中`
|
|
127
125
|
},
|
|
128
126
|
parameters: {
|
|
@@ -167,7 +165,10 @@ Guidelines:
|
|
|
167
165
|
required: ['command'],
|
|
168
166
|
},
|
|
169
167
|
errorOutput: null,
|
|
170
|
-
|
|
168
|
+
// Foreground Bash owns a bounded timeout and process-tree cleanup state
|
|
169
|
+
// machine. A second ToolRegistry timer can preempt that cleanup and turn an
|
|
170
|
+
// owned exit 124 into a fatal orphan, so it must stay disabled for this tool.
|
|
171
|
+
timeoutMs: 0,
|
|
171
172
|
isConcurrencySafe: () => false,
|
|
172
173
|
isReadOnly: () => false,
|
|
173
174
|
isDestructive: (input) => {
|
|
@@ -224,6 +225,7 @@ Guidelines:
|
|
|
224
225
|
timeout,
|
|
225
226
|
signal: ctx?.signal,
|
|
226
227
|
runtimePlatform,
|
|
228
|
+
runProcessImpl: ctx?.[RUN_PROCESS_OVERRIDE] || runProcess,
|
|
227
229
|
});
|
|
228
230
|
|
|
229
231
|
// Format output similar to Claude Code
|
|
@@ -231,6 +233,24 @@ Guidelines:
|
|
|
231
233
|
if (result.stdout) parts.push(result.stdout);
|
|
232
234
|
if (result.stderr) parts.push(`STDERR:\n${result.stderr}`);
|
|
233
235
|
if (result.timedOut) parts.push(`\n(Command timed out after ${timeout}ms)`);
|
|
236
|
+
if (result.terminationError) {
|
|
237
|
+
parts.push([
|
|
238
|
+
`WARNING: ${result.terminationError}`,
|
|
239
|
+
'The command timed out, but process-tree termination could not be confirmed. The command may still be running.',
|
|
240
|
+
'Decide whether to inspect or stop it, retry, or use background=true.',
|
|
241
|
+
].join('\n'));
|
|
242
|
+
}
|
|
243
|
+
if (result.timedOut) {
|
|
244
|
+
ctx?.requestToolBatchBarrier?.({
|
|
245
|
+
kind: 'owned_timeout',
|
|
246
|
+
message: [
|
|
247
|
+
result.terminationError
|
|
248
|
+
? 'The foreground Bash command timed out and process-tree termination could not be confirmed.'
|
|
249
|
+
: 'The foreground Bash command timed out.',
|
|
250
|
+
'Return this result to the model before executing another tool call from the same batch.',
|
|
251
|
+
].join(' '),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
234
254
|
|
|
235
255
|
const output = parts.join('\n');
|
|
236
256
|
if (result.exitCode !== 0) {
|
|
@@ -243,3 +263,14 @@ Guidelines:
|
|
|
243
263
|
}
|
|
244
264
|
},
|
|
245
265
|
});
|
|
266
|
+
|
|
267
|
+
export function createBashTool({ runProcessImpl = runProcess } = {}) {
|
|
268
|
+
return {
|
|
269
|
+
...bashTool,
|
|
270
|
+
execute(input, ctx) {
|
|
271
|
+
return bashTool.execute(input, { ...ctx, [RUN_PROCESS_OVERRIDE]: runProcessImpl });
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default bashTool;
|
|
@@ -102,6 +102,7 @@ function killProcessTree(proc, signalName, platform, spawnProcessSync, systemdSc
|
|
|
102
102
|
* @param {string} command
|
|
103
103
|
* @param {string[]} args
|
|
104
104
|
* @param {{ cwd?: string, signal?: AbortSignal, timeoutMs?: number, maxBytes?: number, env?: NodeJS.ProcessEnv, preserveCarriageReturns?: boolean, killGraceMs?: number, forceSettleMs?: number, requireExitConfirmation?: boolean, systemdScope?: { unit: string, systemctlPath: string, env?: NodeJS.ProcessEnv } | null, onSettled?: (() => void) | null, platform?: NodeJS.Platform, spawnProcess?: typeof spawn, spawnProcessSync?: typeof spawnSync }} [options]
|
|
105
|
+
* @returns {Promise<{ code: number, stdout: string, stderr: string, truncated: boolean, timedOut: boolean, terminationError?: string }>}
|
|
105
106
|
*/
|
|
106
107
|
export function runProcess(command, args, options = {}) {
|
|
107
108
|
if (options.signal?.aborted) {
|
|
@@ -188,21 +189,30 @@ export function runProcess(command, args, options = {}) {
|
|
|
188
189
|
if (settled) return;
|
|
189
190
|
settled = true;
|
|
190
191
|
cleanup();
|
|
191
|
-
|
|
192
|
-
reject(error);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
if (aborted) {
|
|
196
|
-
reject(abortError(options.signal));
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
resolve({
|
|
192
|
+
const result = {
|
|
200
193
|
code: timedOut ? 124 : (code ?? 1),
|
|
201
194
|
stdout: decode(stdout, stdoutTruncated, options.preserveCarriageReturns),
|
|
202
195
|
stderr: decode(stderr, stderrTruncated),
|
|
203
196
|
truncated,
|
|
204
197
|
timedOut,
|
|
205
|
-
}
|
|
198
|
+
};
|
|
199
|
+
if (aborted) {
|
|
200
|
+
reject(abortError(options.signal));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (error) {
|
|
204
|
+
// A command timeout is an owned, bounded outcome. Failure to observe
|
|
205
|
+
// the final child close is important context for the caller, but it
|
|
206
|
+
// must not turn the timeout into an infrastructure exception that
|
|
207
|
+
// prevents the model from deciding what to do next.
|
|
208
|
+
if (timedOut && error instanceof ProcessTerminationError) {
|
|
209
|
+
resolve({ ...result, terminationError: error.message });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
reject(error);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
resolve(result);
|
|
206
216
|
};
|
|
207
217
|
const terminationConfirmed = () => {
|
|
208
218
|
if (!options.requireExitConfirmation) return directClosed;
|
|
@@ -334,6 +344,10 @@ export function runProcess(command, args, options = {}) {
|
|
|
334
344
|
|
|
335
345
|
if (Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
336
346
|
timer = setTimeout(() => {
|
|
347
|
+
// Preserve the first stop reason. Output overflow and Abort may have
|
|
348
|
+
// already started termination; a later deadline must not relabel that
|
|
349
|
+
// cleanup failure as a recoverable command timeout.
|
|
350
|
+
if (settled || stopRequested) return;
|
|
337
351
|
timedOut = true;
|
|
338
352
|
stop();
|
|
339
353
|
}, options.timeoutMs);
|
package/yeaft/tools/types.js
CHANGED
|
@@ -45,6 +45,11 @@
|
|
|
45
45
|
* the supplied reason as `detail`. `reason` may be a structured object
|
|
46
46
|
* `{kind, ...}` so downstream observers (web-bridge) can render UI hints
|
|
47
47
|
* (e.g. "↪ 已转交给 @vp-b") without re-parsing strings.
|
|
48
|
+
* @property {(reason?: string|object) => void} [requestToolBatchBarrier]
|
|
49
|
+
* — stop executing the remaining calls in the current assistant tool batch
|
|
50
|
+
* while still pairing each call with an explicit skipped tool result. The
|
|
51
|
+
* engine then returns those results to the provider before accepting another
|
|
52
|
+
* plan. Used when a completed tool result invalidates the rest of the batch.
|
|
48
53
|
* @property {string} [senderVpId] — id of the VP whose turn is currently
|
|
49
54
|
* running. Used by `route_forward` to stamp the forwarded message and
|
|
50
55
|
* by the loop guard to key per-sender throttling.
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -5277,12 +5277,11 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
|
|
|
5277
5277
|
const projectSessionIds = projectContext?.sessionIds || [];
|
|
5278
5278
|
queryOpts.projectSessionIds = projectSessionIds;
|
|
5279
5279
|
queryOpts.projectInstruction = projectContext?.projectInstruction || '';
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
const sharedBlock = buildProjectSharedBlock(projectContext, projectSummaries);
|
|
5280
|
+
// Related Session summaries now enter through Engine's single AMS
|
|
5281
|
+
// memory outlet. Keep this announcement limited to Project identity and
|
|
5282
|
+
// sharing boundaries so parent VP prompts do not duplicate the same prose
|
|
5283
|
+
// that sub-agents receive through memory.
|
|
5284
|
+
const sharedBlock = buildProjectSharedBlock(projectContext);
|
|
5286
5285
|
if (sharedBlock) {
|
|
5287
5286
|
queryOpts.sessionAnnouncement = queryOpts.sessionAnnouncement
|
|
5288
5287
|
? `${queryOpts.sessionAnnouncement}\n\n${sharedBlock}`
|