@yeaft/webchat-agent 1.0.512 → 1.0.514
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/connection/message-router.js +4 -1
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +115 -129
- 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/conversation/persist.js +93 -0
- package/yeaft/engine.js +39 -4
- package/yeaft/sessions/index.js +1 -0
- package/yeaft/sessions/session-crud.js +64 -2
- package/yeaft/sub-agent/execution-control.js +66 -6
- package/yeaft/sub-agent/liveness.js +9 -1
- package/yeaft/sub-agent/runner.js +48 -39
- package/yeaft/sub-agent/spawned-prompt.js +3 -2
- package/yeaft/sub-agent/tool-access.js +138 -0
- package/yeaft/templates/personas/reviewer.md +5 -2
- package/yeaft/tools/activation.js +2 -0
- package/yeaft/tools/agent.js +30 -14
- package/yeaft/tools/git-read.js +266 -0
- package/yeaft/tools/index.js +4 -0
- package/yeaft/tools/update-agent.js +79 -0
- package/yeaft/web-bridge.js +29 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Parent-owned, in-place child controls; never resumes a terminal or reporting child. */
|
|
2
|
+
import { defineTool } from './types.js';
|
|
3
|
+
import { getAgentRegistry, agentBelongsToCaller } from './agent.js';
|
|
4
|
+
import { isTerminalAgentStatus } from '../sub-agent/status.js';
|
|
5
|
+
import { validateBudget } from '../sub-agent/execution-control.js';
|
|
6
|
+
import { validateToolGrants } from '../sub-agent/tool-access.js';
|
|
7
|
+
import { diagnoseAgentLiveness } from '../sub-agent/liveness.js';
|
|
8
|
+
|
|
9
|
+
export default defineTool({
|
|
10
|
+
name: 'UpdateAgent',
|
|
11
|
+
description: {
|
|
12
|
+
en: 'Adjust a live child in place after inspecting its progress: absolute lifetime time/tool/LLM budgets and explicit extra tool grants. Does not queue a prompt, reset usage, or revive a terminal/reporting child. Give evidence and the remaining task in reason; do not extend stalled or repeating work blindly. Bash permits arbitrary shell and writes, not a read-only sandbox; grant only necessary parent tools and isolate writable workspaces. Already dispatched work is not undone by revocation.',
|
|
13
|
+
zh: '检查进展后原地调整活跃子 Agent:累计时间/工具/LLM 上限及额外工具授权。不排队提示、不清零用量、不复活终止或收尾中的任务。reason 说明已有证据和剩余工作,勿盲目给停滞/重复任务扩额。Bash 可执行任意 Shell 和写入,并非只读沙箱;只授予必要的父级工具,写任务隔离 workspace。撤销不撤回已执行操作。',
|
|
14
|
+
},
|
|
15
|
+
parameters: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
agent_id: { type: 'string' },
|
|
19
|
+
reason: { type: 'string', description: { en: 'Observed progress, remaining work and why this adjustment is necessary', zh: '实际进展、剩余工作及调整原因' } },
|
|
20
|
+
budget: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
max_tokens: { type: 'number', minimum: 1 },
|
|
24
|
+
max_turns: { type: 'integer', minimum: 1 },
|
|
25
|
+
max_tool_calls: { type: 'integer', minimum: 1 },
|
|
26
|
+
max_llm_calls: { type: 'integer', minimum: 1 },
|
|
27
|
+
wall_time_ms: { type: 'integer', minimum: 1 },
|
|
28
|
+
},
|
|
29
|
+
description: { en: 'Partial absolute lifetime ceilings, not added quota. Unspecified limits stay unchanged. LLM cap excludes one reserved reporting request; time is measured from original spawn.', zh: '部分累计上限,不是增加的额度;未指定项不变。LLM 上限之外保留一次报告请求;时间从最初启动计算。' },
|
|
30
|
+
},
|
|
31
|
+
allow_tools: {
|
|
32
|
+
type: 'array', items: { type: 'string' }, maxItems: 32,
|
|
33
|
+
description: { en: 'Replace extra persona grants with these canonical parent tool names (e.g. Bash, FileEdit); [] revokes extras, omission leaves grants unchanged', zh: '替换 persona 额外授权(如 Bash、FileEdit);[] 撤销额外授权,省略则不变' },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ['agent_id', 'reason'],
|
|
37
|
+
},
|
|
38
|
+
isReadOnly: () => false,
|
|
39
|
+
isConcurrencySafe: () => false,
|
|
40
|
+
mayMutateWorkspaceAfterReturn: () => true,
|
|
41
|
+
async execute(input, ctx) {
|
|
42
|
+
const fail = error => JSON.stringify({ error, next_steps: 'Inspect the current agent state and correct the request; do not respawn or repeat blindly.' });
|
|
43
|
+
const agent = getAgentRegistry().get(input.agent_id);
|
|
44
|
+
if (!agent || !agentBelongsToCaller(agent, ctx)) return fail(`Agent not found: ${input.agent_id}`);
|
|
45
|
+
if (isTerminalAgentStatus(agent.status) || agent.budgetReportStarted || agent.budgetStopReason
|
|
46
|
+
|| agent.abortController?.signal.aborted) return fail('Agent is terminal, stopping or already reporting; it cannot be extended');
|
|
47
|
+
if (typeof input.reason !== 'string' || !input.reason.trim() || input.reason.length > 2000) return fail('reason must contain 1..2000 characters of evidence and remaining work');
|
|
48
|
+
if (input.budget === undefined && input.allow_tools === undefined) return fail('budget or allow_tools is required');
|
|
49
|
+
if (input.budget !== undefined) {
|
|
50
|
+
const error = validateBudget(input.budget);
|
|
51
|
+
if (error) return fail(error);
|
|
52
|
+
}
|
|
53
|
+
const parentRegistry = ctx?.parentEngineDeps?.parentToolRegistry;
|
|
54
|
+
const grants = input.allow_tools === undefined ? null : validateToolGrants(input.allow_tools, parentRegistry);
|
|
55
|
+
if (grants && !grants.ok) return fail(grants.error);
|
|
56
|
+
const previousBudget = { ...agent.budget };
|
|
57
|
+
const previousTools = [...(agent.allowTools || [])];
|
|
58
|
+
// All validation precedes mutation. Original usage/deadline origin are retained.
|
|
59
|
+
agent.budget = { ...agent.budget, ...input.budget };
|
|
60
|
+
if (grants) agent.allowTools = grants.tools;
|
|
61
|
+
agent.controlRevision = (agent.controlRevision || 0) + 1;
|
|
62
|
+
if (agent.execution && agent.execution.toolCalls < agent.budget.max_tool_calls * 0.75) agent.execution.warning = null;
|
|
63
|
+
if (!agent.budgetReportStarted) {
|
|
64
|
+
agent.toolBudgetReason = null;
|
|
65
|
+
agent.executionBudgetReason = null;
|
|
66
|
+
}
|
|
67
|
+
agent.refreshToolPolicy?.();
|
|
68
|
+
agent.rearmWallTimeWatchdog?.();
|
|
69
|
+
const event = { type: 'sub_agent_control_updated', at: Date.now(), reason: input.reason.trim(),
|
|
70
|
+
previousBudget, budget: { ...agent.budget }, previousTools, allowTools: [...(agent.allowTools || [])] };
|
|
71
|
+
agent.diagnostics ||= [];
|
|
72
|
+
agent.diagnostics.push(event);
|
|
73
|
+
if (agent.diagnostics.length > 100) agent.diagnostics.shift();
|
|
74
|
+
try { agent.outputLog?.write(event); } catch { /* diagnostics must not fail the applied update */ }
|
|
75
|
+
return JSON.stringify({ success: true, agentId: agent.id, status: agent.status,
|
|
76
|
+
budget: agent.budget, allow_tools: agent.allowTools || [], liveness: diagnoseAgentLiveness(agent),
|
|
77
|
+
next_steps: 'Adjustment applied without restarting work. Continue the parent task; use PromptAgent only if new guidance is needed, then collect its reply. Revocation does not cancel already dispatched work.' });
|
|
78
|
+
},
|
|
79
|
+
});
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -86,6 +86,7 @@ import {
|
|
|
86
86
|
import {
|
|
87
87
|
SessionCrudError,
|
|
88
88
|
createSessionFromSpec,
|
|
89
|
+
copySession,
|
|
89
90
|
renameSession,
|
|
90
91
|
updateSessionAnnouncement,
|
|
91
92
|
archiveSession,
|
|
@@ -1426,6 +1427,8 @@ function projectPersistedToHistoryEntry(m, { includeReflections = false } = {})
|
|
|
1426
1427
|
if (m.imageAssetAnchor) entry.imageAssetAnchor = true;
|
|
1427
1428
|
if (m.responseKind === 'progress' || m.responseKind === 'result') entry.responseKind = m.responseKind;
|
|
1428
1429
|
if (Number.isInteger(m.llmCallCount) && m.llmCallCount > 0) entry.llmCallCount = m.llmCallCount;
|
|
1430
|
+
if (typeof m.model === 'string' && m.model) entry.model = m.model;
|
|
1431
|
+
if (typeof m.effort === 'string' && m.effort) entry.effort = m.effort;
|
|
1429
1432
|
if (m.incomplete === true) entry.incomplete = true;
|
|
1430
1433
|
if (typeof m.stopReason === 'string' && m.stopReason) entry.stopReason = m.stopReason;
|
|
1431
1434
|
if (m.sessionId) entry.sessionId = m.sessionId;
|
|
@@ -1567,6 +1570,8 @@ function projectVisibleHistoryChunkMessages(messages = []) {
|
|
|
1567
1570
|
...(m.speakerVpId ? { speakerVpId: m.speakerVpId } : {}),
|
|
1568
1571
|
...(m.responseKind === 'progress' || m.responseKind === 'result' ? { responseKind: m.responseKind } : {}),
|
|
1569
1572
|
...(Number.isInteger(m.llmCallCount) && m.llmCallCount > 0 ? { llmCallCount: m.llmCallCount } : {}),
|
|
1573
|
+
...(typeof m.model === 'string' && m.model ? { model: m.model } : {}),
|
|
1574
|
+
...(typeof m.effort === 'string' && m.effort ? { effort: m.effort } : {}),
|
|
1570
1575
|
...(m.incomplete === true ? { incomplete: true } : {}),
|
|
1571
1576
|
...(typeof m.stopReason === 'string' && m.stopReason ? { stopReason: m.stopReason } : {}),
|
|
1572
1577
|
...(Array.isArray(m.todos) ? { todos: m.todos } : {}),
|
|
@@ -3695,6 +3700,28 @@ export function handleYeaftCreateSession(msg) {
|
|
|
3695
3700
|
}
|
|
3696
3701
|
}
|
|
3697
3702
|
|
|
3703
|
+
export function handleYeaftCopySession(msg) {
|
|
3704
|
+
const requestId = msg && msg.requestId;
|
|
3705
|
+
const sessionId = msg && (msg.sessionId || msg.groupId);
|
|
3706
|
+
try {
|
|
3707
|
+
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
3708
|
+
const source = decorateSessionsWithRuntimeState(snapshotSessions(yeaftDir))
|
|
3709
|
+
.find(session => session?.id === sessionId);
|
|
3710
|
+
if (!source) throw new SessionCrudError('not_found', sessionId);
|
|
3711
|
+
if (source.running) throw new SessionCrudError('session_running', sessionId, 'Cannot copy a running Session');
|
|
3712
|
+
const session = copySession(yeaftDir, sessionId, {
|
|
3713
|
+
...configuredVpPaths(),
|
|
3714
|
+
name: msg && msg.name,
|
|
3715
|
+
});
|
|
3716
|
+
recordAgentSessionCreated();
|
|
3717
|
+
session.config = loadSessionConfig(yeaftDir, session.id);
|
|
3718
|
+
sendSessionCrudResult({ op: 'copy', requestId, ok: true, session });
|
|
3719
|
+
sendSessionSnapshotBroadcast();
|
|
3720
|
+
} catch (err) {
|
|
3721
|
+
sendSessionCrudResult({ op: 'copy', requestId, ok: false, error: sessionErrorPayload(err) });
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3698
3725
|
/**
|
|
3699
3726
|
* `yeaft_scan_workdir_sessions` — compatibility endpoint for the retired
|
|
3700
3727
|
* workdir Session scan flow. Session data now lives under the user-level
|
|
@@ -4562,6 +4589,8 @@ function handleEngineEvent(event, hctx) {
|
|
|
4562
4589
|
totalMs: event.totalMs,
|
|
4563
4590
|
totalTokens: event.totalTokens,
|
|
4564
4591
|
loopCount: event.loopCount,
|
|
4592
|
+
...(typeof event.model === 'string' && event.model ? { model: event.model } : {}),
|
|
4593
|
+
...(typeof event.effort === 'string' && event.effort ? { effort: event.effort } : {}),
|
|
4565
4594
|
ts: Date.now(),
|
|
4566
4595
|
}, envelope);
|
|
4567
4596
|
break;
|