@yeaft/webchat-agent 1.0.527 → 1.0.528
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/package.json +1 -1
- package/yeaft/engine.js +13 -1
- package/yeaft/sub-agent/execution-control.js +16 -9
- package/yeaft/sub-agent/liveness.js +20 -12
- package/yeaft/sub-agent/runner.js +12 -15
- package/yeaft/sub-agent/status.js +5 -5
- package/yeaft/tools/activation.js +1 -1
- package/yeaft/tools/agent.js +21 -40
- package/yeaft/tools/apply-patch.js +442 -134
- package/yeaft/tools/bash.js +11 -9
- package/yeaft/tools/exit-worktree.js +1 -1
- package/yeaft/tools/file-read.js +36 -4
- package/yeaft/tools/git-read.js +29 -9
- package/yeaft/tools/list-agents.js +60 -58
- package/yeaft/tools/list-tasks.js +31 -3
- package/yeaft/tools/process-runner.js +29 -1
- package/yeaft/tools/registry.js +18 -0
- package/yeaft/tools/update-agent.js +2 -1
- package/yeaft/tools/wait-agent.js +17 -17
- package/yeaft/tools/web-fetch.js +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.528"}
|
package/package.json
CHANGED
package/yeaft/engine.js
CHANGED
|
@@ -65,7 +65,7 @@ import { lookupModelLimitSync } from './llm/models-dev.js';
|
|
|
65
65
|
import { attachRouterPlan, extractPriorPlan, stripMetaForWire } from './router/continuity.js';
|
|
66
66
|
import { resolveThinking } from './router/thinking.js';
|
|
67
67
|
import { approxTokens, computeBudget } from './memory/budget.js';
|
|
68
|
-
import { COLLAB_TOOL_POLICY, isToolErrorOutput, localizeVisibleText, normalizeToolOutput, truncateToolResultIfNeeded } from './tools/registry.js';
|
|
68
|
+
import { COLLAB_TOOL_POLICY, isToolErrorOutput, toolValidationError, localizeVisibleText, normalizeToolOutput, truncateToolResultIfNeeded } from './tools/registry.js';
|
|
69
69
|
import { CONDITIONAL_BUILTIN_TOOL_NAMES, resolveActiveToolNames } from './tools/activation.js';
|
|
70
70
|
import { discoverToolCapabilities } from './tools/discover-tools.js';
|
|
71
71
|
import { agentBelongsToScope, getAgentRegistry } from './tools/agent.js';
|
|
@@ -2530,6 +2530,8 @@ export class Engine {
|
|
|
2530
2530
|
// executions increment these counters; errors and cache reuse do not.
|
|
2531
2531
|
const queryDuplicateCounts = new Map();
|
|
2532
2532
|
const queryDuplicateSuppressions = new Map();
|
|
2533
|
+
const queryValidationFailures = new Map();
|
|
2534
|
+
const fileReadObservations = new Map();
|
|
2533
2535
|
let duplicateReminderAwaitingResponse = false;
|
|
2534
2536
|
const queryNumber = (this.#__queryCounter = (this.#__queryCounter || 0) + 1);
|
|
2535
2537
|
|
|
@@ -4281,6 +4283,7 @@ export class Engine {
|
|
|
4281
4283
|
};
|
|
4282
4284
|
return {
|
|
4283
4285
|
...toolCtx,
|
|
4286
|
+
fileReadObservations,
|
|
4284
4287
|
currentToolCall: () => ({ ...stableToolCall }),
|
|
4285
4288
|
askUser: typeof askUser === 'function'
|
|
4286
4289
|
? input => askUser(input, { ...stableToolCall })
|
|
@@ -4655,6 +4658,15 @@ export class Engine {
|
|
|
4655
4658
|
}));
|
|
4656
4659
|
}
|
|
4657
4660
|
}
|
|
4661
|
+
const validationError = isError && !skipped ? toolValidationError(output) : null;
|
|
4662
|
+
if (validationError) {
|
|
4663
|
+
const key = `${duplicateCallKey}:${validationError}`;
|
|
4664
|
+
const count = (queryValidationFailures.get(key) || 0) + 1;
|
|
4665
|
+
queryValidationFailures.set(key, count);
|
|
4666
|
+
if (count === 2) pendingDupReminders.push(
|
|
4667
|
+
`[system note] ${tc.name} rejected the same arguments twice before execution: ${validationError.slice(0, 300)}. Correct the arguments using its schema/error hint or choose a different tool. No operation was performed; repeating unchanged arguments will not help.`,
|
|
4668
|
+
);
|
|
4669
|
+
}
|
|
4658
4670
|
const toolDurationMs = readyParallelExecution?.durationMs ?? (Date.now() - toolStartTime);
|
|
4659
4671
|
|
|
4660
4672
|
// feat-6af5f9f1 PR B: emit a structured `tool_exec` event for the
|
|
@@ -18,17 +18,20 @@ export function validateBudget(budget) {
|
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
export function resolveSubAgentBudget(budget
|
|
23
|
-
return {
|
|
24
|
-
max_tool_calls: persona === 'implementer' ? 128 : 64,
|
|
25
|
-
wall_time_ms: 15 * 60 * 1000,
|
|
26
|
-
...budget,
|
|
27
|
-
};
|
|
21
|
+
/** No implicit lifetime ceiling: only caller-provided positive limits apply. */
|
|
22
|
+
export function resolveSubAgentBudget(budget) {
|
|
23
|
+
return budget && typeof budget === 'object' ? { ...budget } : {};
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
export function createExecutionStats() {
|
|
31
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
toolCalls: 0,
|
|
29
|
+
completedCalls: 0,
|
|
30
|
+
failedCalls: 0,
|
|
31
|
+
repeatedResults: 0,
|
|
32
|
+
recentCalls: [],
|
|
33
|
+
warning: null,
|
|
34
|
+
};
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
function fingerprint(value) {
|
|
@@ -77,8 +80,11 @@ export class SubAgentToolRegistry extends ToolRegistry {
|
|
|
77
80
|
const elapsedMs = Date.now() - (agent.usage?.startedAt || Date.now());
|
|
78
81
|
const nearTime = agent.budget?.wall_time_ms && elapsedMs >= agent.budget.wall_time_ms * 0.75;
|
|
79
82
|
const updated = agent.controlRevision ? `[Parent control revision ${agent.controlRevision}] Current lifetime ceilings replace the initial preamble: ${JSON.stringify(agent.budget)}. Extra tool grants: ${JSON.stringify(agent.allowTools || [])}. Use DiscoverTools if an allowed tool is not yet visible.\n` : '';
|
|
83
|
+
const remainingTime = agent.budget?.wall_time_ms === undefined
|
|
84
|
+
? 'wall time unlimited'
|
|
85
|
+
: `${Math.max(0, agent.budget.wall_time_ms - elapsedMs)}ms remaining`;
|
|
80
86
|
return nearLimit || nearTime || updated ? {
|
|
81
|
-
prompt: `${updated}[Sub-agent execution budget] ${stats.toolCalls}/${limit ?? '
|
|
87
|
+
prompt: `${updated}[Sub-agent execution budget] ${stats.toolCalls}/${limit ?? 'unlimited'} tools, ${llmCalls}/${llmLimit ?? 'unlimited'} LLM requests used; ${remainingTime}. Finish the assigned result using existing evidence where possible. Investigate only essential remaining unknowns, then return a conclusion.`,
|
|
82
88
|
} : null;
|
|
83
89
|
}
|
|
84
90
|
|
|
@@ -115,6 +121,7 @@ export class SubAgentToolRegistry extends ToolRegistry {
|
|
|
115
121
|
throw new Error(`${agent.toolBudgetReason}; no further tools may execute. Return findings from the available evidence.`);
|
|
116
122
|
}
|
|
117
123
|
stats.toolCalls += 1;
|
|
124
|
+
if (agent.liveness) agent.liveness.toolUseCount = stats.toolCalls;
|
|
118
125
|
agent.usage ||= { tokens: 0, turns: 0, startedAt: Date.now() };
|
|
119
126
|
agent.usage.toolCalls = stats.toolCalls;
|
|
120
127
|
const entry = { name: tool.name, status: 'running' };
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
*
|
|
23
23
|
* @returns {{
|
|
24
24
|
* toolUseCount: number,
|
|
25
|
-
*
|
|
25
|
+
* usageTokens: number,
|
|
26
|
+
* outputChars: number,
|
|
26
27
|
* eventCount: number,
|
|
27
28
|
* lastEventAt: number,
|
|
28
29
|
* lastEventType: string|null,
|
|
@@ -32,7 +33,8 @@
|
|
|
32
33
|
export function makeLiveness() {
|
|
33
34
|
return {
|
|
34
35
|
toolUseCount: 0,
|
|
35
|
-
|
|
36
|
+
usageTokens: 0,
|
|
37
|
+
outputChars: 0,
|
|
36
38
|
eventCount: 0,
|
|
37
39
|
lastEventAt: 0,
|
|
38
40
|
lastEventType: null,
|
|
@@ -54,12 +56,15 @@ export function bumpLivenessFromEvent(liveness, evt) {
|
|
|
54
56
|
liveness.lastEventAt = Date.now();
|
|
55
57
|
liveness.lastEventType = evt.type || liveness.lastEventType;
|
|
56
58
|
if (evt.type === 'text_delta' && typeof evt.text === 'string') {
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
liveness.
|
|
59
|
+
// This is explicitly output volume, never represented as provider tokens.
|
|
60
|
+
liveness.outputChars += evt.text.length;
|
|
61
|
+
} else if (evt.type === 'usage') {
|
|
62
|
+
const cacheTokens = evt.cacheTokensAreIncludedInInput ? 0
|
|
63
|
+
: (evt.cacheReadTokens || 0) + (evt.cacheWriteTokens || 0);
|
|
64
|
+
liveness.usageTokens += (evt.inputTokens || 0) + (evt.outputTokens || 0) + cacheTokens;
|
|
65
|
+
} else if (evt.type === 'tool_start') {
|
|
66
|
+
// Keep the bounded activity trail here. Actual executions are counted at
|
|
67
|
+
// SubAgentToolRegistry.execute(), then copied into liveness by the runner.
|
|
63
68
|
const name = evt.toolName || evt.name || (evt.tool && evt.tool.name) || null;
|
|
64
69
|
if (name) {
|
|
65
70
|
liveness.recentTools.push(name);
|
|
@@ -81,7 +86,8 @@ export function snapshotLiveness(liveness, now = Date.now()) {
|
|
|
81
86
|
if (!liveness) {
|
|
82
87
|
return {
|
|
83
88
|
toolUseCount: 0,
|
|
84
|
-
|
|
89
|
+
usageTokens: 0,
|
|
90
|
+
outputChars: 0,
|
|
85
91
|
eventCount: 0,
|
|
86
92
|
lastEventAt: null,
|
|
87
93
|
msSinceLastEvent: null,
|
|
@@ -91,7 +97,8 @@ export function snapshotLiveness(liveness, now = Date.now()) {
|
|
|
91
97
|
}
|
|
92
98
|
return {
|
|
93
99
|
toolUseCount: liveness.toolUseCount,
|
|
94
|
-
|
|
100
|
+
usageTokens: liveness.usageTokens,
|
|
101
|
+
outputChars: liveness.outputChars,
|
|
95
102
|
eventCount: liveness.eventCount,
|
|
96
103
|
lastEventAt: liveness.lastEventAt || null,
|
|
97
104
|
msSinceLastEvent: liveness.lastEventAt ? Math.max(0, now - liveness.lastEventAt) : null,
|
|
@@ -134,7 +141,8 @@ export function diagnoseAgentLiveness(agent, opts = {}) {
|
|
|
134
141
|
execution: agent?.execution ? {
|
|
135
142
|
...agent.execution,
|
|
136
143
|
recentCalls: agent.execution.recentCalls.map(call => ({ ...call })),
|
|
137
|
-
remainingToolCalls:
|
|
144
|
+
remainingToolCalls: agent.budget?.max_tool_calls === undefined ? null
|
|
145
|
+
: Math.max(0, agent.budget.max_tool_calls - agent.execution.toolCalls),
|
|
138
146
|
limits: { ...agent.budget },
|
|
139
147
|
llmCalls: agent.usage?.llmCalls || 0,
|
|
140
148
|
reportingLlmCalls: agent.usage?.reportingLlmCalls || 0,
|
|
@@ -151,7 +159,7 @@ export function diagnoseAgentLiveness(agent, opts = {}) {
|
|
|
151
159
|
stalled: stale,
|
|
152
160
|
stallThresholdMs: thresholdMs,
|
|
153
161
|
diagnostic: stale
|
|
154
|
-
? `No sub-agent
|
|
162
|
+
? `No observable sub-agent event for ${msSinceActivity}ms. This is diagnostic only: the provider or tool may still be working; inspect the log before deciding whether to cancel.`
|
|
155
163
|
: null,
|
|
156
164
|
};
|
|
157
165
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* after PromptAgent
|
|
18
18
|
* - a durable output log at ~/.yeaft/sub-agents/<agentId>.log mirroring
|
|
19
19
|
* every onEvent (see output-log.js)
|
|
20
|
-
* - a liveness snapshot (toolUseCount,
|
|
20
|
+
* - a liveness snapshot (toolUseCount, usageTokens, outputChars, lastEventAt, …) the
|
|
21
21
|
* parent reads through WaitAgent / ListAgents
|
|
22
22
|
*
|
|
23
23
|
* The runner is fire-and-forget: `startSubAgent(agent, deps)` schedules a
|
|
@@ -60,8 +60,8 @@ async function loadTickAgent() {
|
|
|
60
60
|
return _tickAgent;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/**
|
|
64
|
-
const IDLE_ABANDON_MS =
|
|
63
|
+
/** Retained idle agents have no implicit lifetime deadline. */
|
|
64
|
+
const IDLE_ABANDON_MS = 0;
|
|
65
65
|
|
|
66
66
|
/** Cap on agent.lastResult (mid-stream preview) — keeps memory bounded. */
|
|
67
67
|
const LAST_RESULT_MAX_CHARS = 8 * 1024;
|
|
@@ -137,7 +137,7 @@ export function startSubAgent(agent, deps = {}) {
|
|
|
137
137
|
// turns must not pollute the user-facing conversation history. The
|
|
138
138
|
// memory stores are shared so memory recall still works for the
|
|
139
139
|
// sub-agent (matches parent VP persona memory).
|
|
140
|
-
agent.budget = resolveSubAgentBudget(agent.budget
|
|
140
|
+
agent.budget = resolveSubAgentBudget(agent.budget);
|
|
141
141
|
agent.execution = agent.execution || createExecutionStats();
|
|
142
142
|
const childRegistry = buildChildToolRegistry(deps.parentToolRegistry, { agent });
|
|
143
143
|
subEngine = new Engine({
|
|
@@ -250,9 +250,8 @@ export function startSubAgent(agent, deps = {}) {
|
|
|
250
250
|
* liveness + lastResult.
|
|
251
251
|
* 3. Stash the final assistant text on agent.result, tickAgent for
|
|
252
252
|
* budget enforcement, mark idle.
|
|
253
|
-
* 4. Wait for
|
|
254
|
-
*
|
|
255
|
-
* (status=='abandoned').
|
|
253
|
+
* 4. Wait for PromptAgent or CloseAgent. An idle abandonment timeout is
|
|
254
|
+
* available only when the embedding caller explicitly configures one.
|
|
256
255
|
*/
|
|
257
256
|
function buildWallTimeBudgetResult(agent, reason) {
|
|
258
257
|
return {
|
|
@@ -311,7 +310,7 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
311
310
|
wallTimeWatchdog = armWallTimeWatchdog(agent, deps);
|
|
312
311
|
};
|
|
313
312
|
agent.rearmWallTimeWatchdog();
|
|
314
|
-
const idleAbandonMs =
|
|
313
|
+
const idleAbandonMs = Number.isFinite(deps.idleAbandonMs) && deps.idleAbandonMs > 0
|
|
315
314
|
? deps.idleAbandonMs : IDLE_ABANDON_MS;
|
|
316
315
|
|
|
317
316
|
const wrapEvt = (evt) => ({
|
|
@@ -399,8 +398,8 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
399
398
|
while (!isTerminalAgentStatus(agent.status)) {
|
|
400
399
|
const queuedPrompt = dequeueNextUserPrompt();
|
|
401
400
|
if (!queuedPrompt) {
|
|
402
|
-
// No queued work —
|
|
403
|
-
//
|
|
401
|
+
// No queued work — retain the agent for PromptAgent / CloseAgent.
|
|
402
|
+
// A caller-provided idleAbandonMs may opt into automatic cleanup.
|
|
404
403
|
agent.status = STATUS.IDLE;
|
|
405
404
|
agent.idleSince = Date.now();
|
|
406
405
|
emit({ type: 'sub_agent_status', status: STATUS.IDLE });
|
|
@@ -444,7 +443,6 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
444
443
|
let budgetReportText = '';
|
|
445
444
|
let endedNormally = false;
|
|
446
445
|
let streamError = null;
|
|
447
|
-
const turnTokenStart = agent.liveness?.tokenCount || 0;
|
|
448
446
|
const priorUsageTokens = agent.usage?.tokens || 0;
|
|
449
447
|
let turnUsageTokens = 0;
|
|
450
448
|
try {
|
|
@@ -594,13 +592,12 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
594
592
|
try {
|
|
595
593
|
const tickAgent = await loadTickAgent();
|
|
596
594
|
if (typeof tickAgent === 'function') {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
// Usage events are exposed live; tickAgent adds the turn delta once.
|
|
595
|
+
// Provider usage is authoritative. If a provider omits usage, keep the
|
|
596
|
+
// count unknown/unchanged rather than disguising output characters as tokens.
|
|
600
597
|
agent.usage.tokens = priorUsageTokens;
|
|
601
598
|
tickResult = tickAgent(agent.id, {
|
|
602
599
|
turns: 1,
|
|
603
|
-
tokens:
|
|
600
|
+
tokens: turnUsageTokens,
|
|
604
601
|
partial_output: assistantText,
|
|
605
602
|
});
|
|
606
603
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* ↓ ↓
|
|
15
15
|
* failed completed
|
|
16
16
|
* ↓ ↓
|
|
17
|
-
* closed abandoned (
|
|
17
|
+
* closed abandoned (only with an explicit idle timeout)
|
|
18
18
|
*
|
|
19
19
|
* - 'created' : registry record exists but the driver hasn't taken a
|
|
20
20
|
* step yet. Transient — flips to 'running' on first tick.
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
* - 'failed' : terminal — driver/adapter/stream raised; agent.error set.
|
|
27
27
|
* - 'closed' : terminal — CloseAgent called (or driver finally{} reaped
|
|
28
28
|
* a cleanly-finishing agent).
|
|
29
|
-
* - 'abandoned' : terminal —
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* - 'abandoned' : terminal — an embedding caller's explicit idle timeout
|
|
30
|
+
* elapsed. Distinct from 'closed' so the parent can tell
|
|
31
|
+
* automatic cleanup from deliberate finalization. There is
|
|
32
|
+
* no default idle lifetime deadline.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
export const STATUS = Object.freeze({
|
|
@@ -59,7 +59,7 @@ export const CONDITIONAL_BUILTIN_TOOL_NAMES = new Set([
|
|
|
59
59
|
|
|
60
60
|
const HISTORY_INTENT_RE = /(?:\bhistory\b|\b(?:prior|previous) (?:chat|conversation|discussion)\b|\bprevious(?:ly)? discussed\b|\bwhat did we (?:decide|discuss|say|agree)\b|\b(?:our|the) (?:earlier|last) decision\b|历史|之前(?:的)?(?:对话|讨论|会话|决定)|过去(?:的)?会话|我们(?:之前|上次)(?:决定|讨论|说)了什么)/iu;
|
|
61
61
|
const DISK_INTENT_RE = /(?:\bdisk (?:usage|space|full)\b|\bstorage (?:usage|space|full)\b|\blargest director|\benospc\b|\bno space left on device\b|磁盘(?:占用|空间|已满)|存储空间|目录占用|空间不足)/iu;
|
|
62
|
-
const PATCH_INTENT_RE = /(?:\bapply (?:a )?patch\b|\bunified diff\b|\bpatch file\b|应用补丁|统一 diff|补丁文件)/iu;
|
|
62
|
+
const PATCH_INTENT_RE = /(?:\b(?:implement|refactor|fix|edit)\b|修复|重构|修改|实现|\bapply (?:a )?patch\b|\bunified diff\b|\bpatch file\b|应用补丁|统一 diff|补丁文件)/iu;
|
|
63
63
|
const TASK_INTENT_RE = /(?:\bbackground (?:task|job|command|process)\b|\btask[_-][a-z0-9]+\b|\btask log\b|后台(?:任务|命令|进程)|任务日志)/iu;
|
|
64
64
|
const SUB_AGENT_INTENT_RE = /(?:\bsub[ -]?agent\b|\bagent(?:s)?\b|\bparallel(?:ize| work| task| review)?\b|\bindependent(?:ly| review)?\b|\banother (?:worker|reviewer|agent)\b|\bdelegate\b|\b(?:run|start|launch|spawn) (?:the |a )?(?:task|child)\b|子 ?Agent|并行(?:处理|工作|任务|审查)?|独立(?:处理|审查)?|另一个(?:人|助手|Agent)|委派)/iu;
|
|
65
65
|
const WORK_ITEM_INTENT_RE = /(?:\bwork ?center\b|\bwork ?item\b|\bdurable tracking\b|\bcross[- ]turn\b|\blong[- ]running goal\b|\bacross multiple (?:turns|sessions)\b|\buntil (?:it is|it's) finished\b|工作中心|工作项|持久(?:任务|跟踪)|跨 ?turn|跨多个会话|长期任务|持续跟踪)/iu;
|
package/yeaft/tools/agent.js
CHANGED
|
@@ -123,7 +123,7 @@ export function validateSpec(input) {
|
|
|
123
123
|
task: task || mission,
|
|
124
124
|
expected_output: expected_output || null,
|
|
125
125
|
persona: persona || null,
|
|
126
|
-
budget: resolveSubAgentBudget(budget
|
|
126
|
+
budget: resolveSubAgentBudget(budget),
|
|
127
127
|
},
|
|
128
128
|
};
|
|
129
129
|
}
|
|
@@ -222,9 +222,10 @@ export default defineTool({
|
|
|
222
222
|
en: `Create a sub-agent to work on an independent task in parallel.
|
|
223
223
|
|
|
224
224
|
Sub-agents run in their own context and can be given a concrete mission
|
|
225
|
-
with an optional expected_output schema.
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
with an optional expected_output schema. By default there is no lifetime tool,
|
|
226
|
+
LLM, token, turn, or wall-time ceiling: the agent may finish its assigned work.
|
|
227
|
+
Explicit budget fields are absolute lifetime limits. max_tokens uses provider
|
|
228
|
+
usage; max_turns counts query turns, not tools.
|
|
228
229
|
Pick a preset persona to pre-wire a tool subset and model tier:
|
|
229
230
|
- explorer : fast, read-only scout (Read/Grep/Glob/ListDir)
|
|
230
231
|
- implementer: builder with full work tools (primary model)
|
|
@@ -235,29 +236,20 @@ Guidelines:
|
|
|
235
236
|
- Give a clear, focused mission — what "done" looks like
|
|
236
237
|
- Use expected_output when the return shape matters
|
|
237
238
|
- Delegate one clear result with the workspace/base and completion evidence. Let the child choose its steps; do simple work directly. Split unrelated goals, not individual reads.
|
|
238
|
-
- Usually omit budget
|
|
239
|
+
- Usually omit budget so the child can finish. Add a budget only when the task actually needs a hard lifetime ceiling; tool exhaustion reserves one tool-free handoff and unfinished work stays budget_exceeded.
|
|
239
240
|
- Persona tools are defaults, not task boundaries: reviewer has GitRead; grant Bash or write tools explicitly via allow_tools only when needed. Bash is not a read-only sandbox; isolate concurrent writable tasks.
|
|
240
241
|
- Use UpdateAgent to adjust a live child's time/tool/LLM ceilings or extra grants after inspecting evidence; counters and context are retained. Do not extend stalled work blindly or respawn the same exhausted mission automatically.
|
|
241
242
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
After queueing it, call WaitAgent in the same parent turn and collect the
|
|
248
|
-
reply before ending. If a bounded wait times out, wait again with a larger
|
|
249
|
-
bound unless the agent is stale/stalled.
|
|
250
|
-
5. CloseAgent — stop or finalize a sub-agent when it is no longer needed.
|
|
251
|
-
|
|
252
|
-
Completion/failure is delivered through sub-agent notifications on later parent
|
|
253
|
-
turns, so do not call WaitAgent merely to poll a newly spawned running agent.
|
|
254
|
-
After PromptAgent follow-up, however, the answer is required to complete that
|
|
255
|
-
workflow; use bounded WaitAgent calls and inspect liveness instead of blind loops.`,
|
|
243
|
+
Orchestration: SpawnAgent returns immediately, so continue parent work; do not call WaitAgent merely to poll a newly spawned running agent.
|
|
244
|
+
Use ListAgents for a non-blocking status check; completion/failure also arrives by
|
|
245
|
+
notification. PromptAgent is for follow-up guidance. After queueing it, call WaitAgent in the same parent turn and collect that reply before ending.
|
|
246
|
+
A stale diagnostic is evidence to inspect, not proof the child is dead. CloseAgent
|
|
247
|
+
stops or finalizes work no longer needed.`,
|
|
256
248
|
zh: `创建一个子 Agent 并行处理独立任务。
|
|
257
249
|
|
|
258
250
|
子 Agent 在独立上下文中运行,可给定具体 mission 和可选的 expected_output schema。
|
|
259
|
-
|
|
260
|
-
max_tokens
|
|
251
|
+
默认不设工具执行、LLM 请求、token、turn 或总耗时上限,让 Agent 完成已分配工作。
|
|
252
|
+
显式 budget 字段是累计生命周期硬上限;max_tokens 使用 provider usage,max_turns 统计 query turn 而非工具调用。
|
|
261
253
|
选择预设 persona 来预配置工具子集和模型层级:
|
|
262
254
|
- explorer : 快速只读侦察(Read/Grep/Glob/ListDir)
|
|
263
255
|
- implementer: 具备完整工作工具的构建者(主模型)
|
|
@@ -268,22 +260,11 @@ max_tokens 在 provider usage 到达时检查;max_turns 是 query turn 数,
|
|
|
268
260
|
- 给出清晰聚焦的 mission——"完成"是什么样子
|
|
269
261
|
- 当返回结构重要时使用 expected_output
|
|
270
262
|
- 一次只委派一个明确结果,提供工作目录/基线和完成证据,让子 Agent 自主选择步骤;简单工作直接做。拆分不相关目标,不要拆成逐个读取任务。
|
|
271
|
-
- 通常省略 budget
|
|
263
|
+
- 通常省略 budget,让子 Agent 完成工作;仅当任务确实需要硬性累计上限时才设置。不要给多文件 review 人为设置极小额度。工具额度耗尽后保留一次无工具交付机会,未完成仍返回 budget_exceeded。
|
|
272
264
|
- Persona 是默认工具集,不是任务死边界:reviewer 有 GitRead;按需用 allow_tools 显式授予 Bash/写工具。Bash 并非只读沙箱;并行写任务应隔离 workspace。
|
|
273
265
|
- 检查已有证据后,用 UpdateAgent 原地调整活跃子任务的时间/工具/LLM 上限或额外授权,保留计数与上下文;不要盲目扩额停滞任务,也不要自动重启同一个耗尽任务。
|
|
274
266
|
|
|
275
|
-
|
|
276
|
-
1. SpawnAgent — 启动子 Agent 作为后台任务并立即返回。
|
|
277
|
-
2. Continue — 父 VP 继续工作;不要仅仅为了轮询而阻塞。
|
|
278
|
-
3. ListAgents — 需要进度信息时的非阻塞状态检查。
|
|
279
|
-
4. PromptAgent — 子 Agent 空闲且需要指导时,可选发送后续提示。
|
|
280
|
-
排队后必须在同一个父级 turn 调用 WaitAgent 并拿到回复再结束;有界等待超时后,除非 Agent
|
|
281
|
-
已 stale/stalled,否则使用更大的有界 timeout 再次等待。
|
|
282
|
-
5. CloseAgent — 不再需要时停止或结束子 Agent。
|
|
283
|
-
|
|
284
|
-
完成或失败会通过之后父级 turn 的 notification 送达,因此不要为了轮询刚创建且仍运行的
|
|
285
|
-
Agent 调用 WaitAgent。但 PromptAgent 后续工作必须拿到答案才算完成;使用有界 WaitAgent 调用并检查
|
|
286
|
-
liveness,不要盲目循环。`
|
|
267
|
+
异步编排:SpawnAgent 立即返回;父 VP 继续工作,不要为了轮询刚创建且仍运行的 Agent 调用 WaitAgent。需要非阻塞状态时用 ListAgents,完成/失败也会通过 notification 送达。不再需要的工作用 CloseAgent 停止或结束。PromptAgent — 子 Agent 空闲且需要指导时发送;排队后必须在同一个父级 turn 调用 WaitAgent 并拿到回复再结束。超时且非 stale/stalled 时扩大有界 timeout 再等;stale 只是诊断,应先检查日志再决定是否取消。`
|
|
287
268
|
},
|
|
288
269
|
parameters: {
|
|
289
270
|
type: 'object',
|
|
@@ -340,17 +321,17 @@ liveness,不要盲目循环。`
|
|
|
340
321
|
zh: '可选实际 Engine 模型请求上限(含重试),不同于 query turn;另保留并单独统计一次无工具报告请求。',
|
|
341
322
|
} },
|
|
342
323
|
max_tool_calls: { type: 'integer', minimum: 1, description: {
|
|
343
|
-
en: '
|
|
344
|
-
zh: '
|
|
324
|
+
en: 'Optional actual tool execution ceiling, including parallel and discovered tools; no default limit is applied',
|
|
325
|
+
zh: '可选实际工具执行上限,包括并行及发现的工具;默认不设限制',
|
|
345
326
|
} },
|
|
346
327
|
wall_time_ms: { type: 'number', description: {
|
|
347
|
-
en: '
|
|
348
|
-
zh: '
|
|
328
|
+
en: 'Optional elapsed-time ceiling in milliseconds; no default limit is applied',
|
|
329
|
+
zh: '可选耗时上限(毫秒);默认不设限制',
|
|
349
330
|
} },
|
|
350
331
|
},
|
|
351
332
|
description: {
|
|
352
|
-
en: '
|
|
353
|
-
zh: '
|
|
333
|
+
en: 'Optional absolute lifetime ceilings. Omit budget to allow task completion. A cutoff returns { status: "budget_exceeded", partial_output, reason }, not successful completion.',
|
|
334
|
+
zh: '可选累计生命周期硬上限。省略 budget 即允许任务完成。截止时返回 { status: "budget_exceeded", partial_output, reason },不代表任务成功。',
|
|
354
335
|
},
|
|
355
336
|
},
|
|
356
337
|
allow_tools: {
|