@yeaft/webchat-agent 0.1.825 → 0.1.826
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 +1 -1
- package/unify/sub-agent/runner.js +4 -2
- package/unify/sub-agent/spawned-prompt.js +2 -2
- package/unify/tools/agent.js +4 -3
- package/unify/tools/index.js +0 -4
- package/unify/tools/registry.js +29 -6
- package/unify/tools/send-message.js +9 -5
- package/unify/tools/types.js +7 -0
- package/unify/tools/wait-agent.js +1 -1
- package/unify/tools/open-source-message.js +0 -49
- package/unify/tools/request-permissions.js +0 -59
package/package.json
CHANGED
|
@@ -31,8 +31,10 @@ import { ToolRegistry } from '../tools/registry.js';
|
|
|
31
31
|
import { buildSpawnedPreamble } from './spawned-prompt.js';
|
|
32
32
|
|
|
33
33
|
const RESTRICTED_TOOLS = new Set([
|
|
34
|
-
'
|
|
35
|
-
'
|
|
34
|
+
'SpawnAgent',
|
|
35
|
+
'Agent', // legacy alias
|
|
36
|
+
'PromptAgent',
|
|
37
|
+
'SendMessage', // legacy alias
|
|
36
38
|
'WaitAgent',
|
|
37
39
|
'CloseAgent',
|
|
38
40
|
'ListAgents',
|
|
@@ -38,7 +38,7 @@ export function buildSpawnedPreamble({ parentName, parentVpId, agentName, missio
|
|
|
38
38
|
m || '(无具体任务说明)',
|
|
39
39
|
'',
|
|
40
40
|
'## 行为约束',
|
|
41
|
-
'- 不要再 spawn sub-agent(你已经没有
|
|
41
|
+
'- 不要再 spawn sub-agent(你已经没有 SpawnAgent / PromptAgent / WaitAgent / CloseAgent 工具)。',
|
|
42
42
|
'- 不要 route_forward 给别的 VP,不要 ask_user。',
|
|
43
43
|
'- 完成时直接以 markdown 自由文本回复(end_turn)。建议结构:"## 结果" / "## 关键发现" / "## 遗留问题"。',
|
|
44
44
|
'- 失败/不可行也要明确说出来,不要假装完成。父 VP 会读你的最终消息。',
|
|
@@ -55,7 +55,7 @@ export function buildSpawnedPreamble({ parentName, parentVpId, agentName, missio
|
|
|
55
55
|
m || '(no mission body provided)',
|
|
56
56
|
'',
|
|
57
57
|
'## Constraints',
|
|
58
|
-
'- Do NOT spawn further sub-agents (
|
|
58
|
+
'- Do NOT spawn further sub-agents (SpawnAgent / PromptAgent / WaitAgent / CloseAgent are not in your toolset).',
|
|
59
59
|
'- Do NOT use route_forward to other VPs. Do NOT ask_user.',
|
|
60
60
|
'- When done, reply in free markdown (end_turn). Suggested structure: "## Result" / "## Key findings" / "## Open questions".',
|
|
61
61
|
'- If the mission is infeasible or you fail, say so plainly. The parent will read your final message.',
|
package/unify/tools/agent.js
CHANGED
|
@@ -174,7 +174,8 @@ export function tickAgent(agentId, delta = {}, now = Date.now()) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
export default defineTool({
|
|
177
|
-
name: '
|
|
177
|
+
name: 'SpawnAgent',
|
|
178
|
+
aliases: ['Agent'],
|
|
178
179
|
description: `Create a sub-agent to work on an independent task in parallel.
|
|
179
180
|
|
|
180
181
|
Sub-agents run in their own context and can be given a concrete mission
|
|
@@ -189,7 +190,7 @@ Guidelines:
|
|
|
189
190
|
- Give a clear, focused mission — what "done" looks like
|
|
190
191
|
- Use expected_output to pin the structure you want back
|
|
191
192
|
- Always set a budget for unbounded missions
|
|
192
|
-
- Use
|
|
193
|
+
- Use PromptAgent to communicate, WaitAgent to collect results, CloseAgent to finalize`,
|
|
193
194
|
parameters: {
|
|
194
195
|
type: 'object',
|
|
195
196
|
properties: {
|
|
@@ -302,7 +303,7 @@ Guidelines:
|
|
|
302
303
|
persona: spec.persona || null,
|
|
303
304
|
budget: spec.budget || null,
|
|
304
305
|
status: agent.status,
|
|
305
|
-
message: `Sub-agent "${name}" spawned (${agentId}). Use WaitAgent to collect its first turn output,
|
|
306
|
+
message: `Sub-agent "${name}" spawned (${agentId}). Use WaitAgent to collect its first turn output, PromptAgent to give it more work, CloseAgent to finish.`,
|
|
306
307
|
});
|
|
307
308
|
},
|
|
308
309
|
});
|
package/unify/tools/index.js
CHANGED
|
@@ -18,7 +18,6 @@ import exitWorktree from './exit-worktree.js';
|
|
|
18
18
|
|
|
19
19
|
// --- P0 Core tools ---
|
|
20
20
|
import askUser from './ask-user.js';
|
|
21
|
-
import openSourceMessage from './open-source-message.js';
|
|
22
21
|
import webSearch from './web-search.js';
|
|
23
22
|
import webFetch from './web-fetch.js';
|
|
24
23
|
import historySearch from './history-search.js';
|
|
@@ -66,7 +65,6 @@ import { jsRepl, jsReplReset } from './js-repl.js';
|
|
|
66
65
|
import notebookEdit from './notebook-edit.js';
|
|
67
66
|
import imageGeneration from './image-generation.js';
|
|
68
67
|
import viewImage from './view-image.js';
|
|
69
|
-
import requestPermissions from './request-permissions.js';
|
|
70
68
|
|
|
71
69
|
/**
|
|
72
70
|
* All built-in tools, flattened into a single array.
|
|
@@ -82,7 +80,6 @@ export const allTools = [
|
|
|
82
80
|
|
|
83
81
|
// P0 Core
|
|
84
82
|
askUser,
|
|
85
|
-
openSourceMessage,
|
|
86
83
|
webSearch,
|
|
87
84
|
webFetch,
|
|
88
85
|
historySearch,
|
|
@@ -117,7 +114,6 @@ export const allTools = [
|
|
|
117
114
|
notebookEdit,
|
|
118
115
|
imageGeneration,
|
|
119
116
|
viewImage,
|
|
120
|
-
requestPermissions,
|
|
121
117
|
];
|
|
122
118
|
|
|
123
119
|
/**
|
package/unify/tools/registry.js
CHANGED
|
@@ -226,6 +226,17 @@ export class ToolRegistry {
|
|
|
226
226
|
register(tool) {
|
|
227
227
|
if (!tool || !tool.name) throw new Error('Tool must have a name');
|
|
228
228
|
this.#tools.set(tool.name, tool);
|
|
229
|
+
// Legacy-name aliases: keep historical jsonl tool_calls resolvable
|
|
230
|
+
// after a rename. The alias entry shares the same tool object so
|
|
231
|
+
// execute/has lookups succeed, but `getToolDefs()` dedupes by tool
|
|
232
|
+
// identity so the LLM only sees the canonical name.
|
|
233
|
+
if (Array.isArray(tool.aliases)) {
|
|
234
|
+
for (const alias of tool.aliases) {
|
|
235
|
+
if (typeof alias === 'string' && alias && alias !== tool.name) {
|
|
236
|
+
this.#tools.set(alias, tool);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
229
240
|
return this;
|
|
230
241
|
}
|
|
231
242
|
|
|
@@ -269,10 +280,21 @@ export class ToolRegistry {
|
|
|
269
280
|
|
|
270
281
|
/**
|
|
271
282
|
* Get all registered tools (unfiltered).
|
|
283
|
+
*
|
|
284
|
+
* Dedupes alias entries — when a tool is registered with `aliases`,
|
|
285
|
+
* the same ToolDef object lives at multiple Map keys. We return each
|
|
286
|
+
* tool object at most once (under its canonical `tool.name`).
|
|
272
287
|
* @returns {import('./types.js').ToolDef[]}
|
|
273
288
|
*/
|
|
274
289
|
getAllTools() {
|
|
275
|
-
|
|
290
|
+
const seen = new Set();
|
|
291
|
+
const out = [];
|
|
292
|
+
for (const tool of this.#tools.values()) {
|
|
293
|
+
if (seen.has(tool)) continue;
|
|
294
|
+
seen.add(tool);
|
|
295
|
+
out.push(tool);
|
|
296
|
+
}
|
|
297
|
+
return out;
|
|
276
298
|
}
|
|
277
299
|
|
|
278
300
|
/**
|
|
@@ -291,11 +313,12 @@ export class ToolRegistry {
|
|
|
291
313
|
}
|
|
292
314
|
|
|
293
315
|
/**
|
|
294
|
-
* Get all registered tool names
|
|
316
|
+
* Get all registered tool names (canonical only — aliases are excluded
|
|
317
|
+
* so debug surfaces like the tool-stats panel show one row per tool).
|
|
295
318
|
* @returns {string[]}
|
|
296
319
|
*/
|
|
297
320
|
getToolNames() {
|
|
298
|
-
return
|
|
321
|
+
return this.getAllTools().map(t => t.name);
|
|
299
322
|
}
|
|
300
323
|
|
|
301
324
|
/**
|
|
@@ -337,12 +360,12 @@ export class ToolRegistry {
|
|
|
337
360
|
|
|
338
361
|
/** Number of registered tools. */
|
|
339
362
|
get size() {
|
|
340
|
-
return this
|
|
363
|
+
return this.getAllTools().length;
|
|
341
364
|
}
|
|
342
365
|
|
|
343
|
-
/** All registered tool names. */
|
|
366
|
+
/** All registered tool names (canonical only; aliases excluded). */
|
|
344
367
|
get names() {
|
|
345
|
-
return
|
|
368
|
+
return this.getAllTools().map(t => t.name);
|
|
346
369
|
}
|
|
347
370
|
}
|
|
348
371
|
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* send-message.js — Send a
|
|
2
|
+
* send-message.js — Send a follow-up prompt to a sub-agent.
|
|
3
|
+
*
|
|
4
|
+
* Tool name: PromptAgent (canonical) / SendMessage (legacy alias for
|
|
5
|
+
* historical jsonl replay — see registry alias map).
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
import { defineTool } from './types.js';
|
|
6
9
|
import { getAgentRegistry } from './agent.js';
|
|
7
10
|
|
|
8
11
|
export default defineTool({
|
|
9
|
-
name: '
|
|
10
|
-
|
|
12
|
+
name: 'PromptAgent',
|
|
13
|
+
aliases: ['SendMessage'],
|
|
14
|
+
description: `Send a follow-up prompt to a sub-agent you previously spawned.
|
|
11
15
|
|
|
12
|
-
Use this to give
|
|
13
|
-
The
|
|
16
|
+
Use this to give the sub-agent more work, additional instructions, or relay
|
|
17
|
+
information. The prompt is queued for the agent to process on its next turn.`,
|
|
14
18
|
parameters: {
|
|
15
19
|
type: 'object',
|
|
16
20
|
properties: {
|
package/unify/tools/types.js
CHANGED
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
*/
|
|
75
75
|
export function defineTool({
|
|
76
76
|
name,
|
|
77
|
+
aliases,
|
|
77
78
|
description,
|
|
78
79
|
parameters,
|
|
79
80
|
execute,
|
|
@@ -94,6 +95,12 @@ export function defineTool({
|
|
|
94
95
|
isReadOnly,
|
|
95
96
|
isDestructive,
|
|
96
97
|
};
|
|
98
|
+
// Legacy tool-name aliases. Registered as extra lookup keys so old
|
|
99
|
+
// jsonl tool_calls (e.g. `SendMessage` → `PromptAgent`) keep resolving,
|
|
100
|
+
// but excluded from the LLM-visible catalogue.
|
|
101
|
+
if (Array.isArray(aliases) && aliases.length > 0) {
|
|
102
|
+
def.aliases = aliases.slice();
|
|
103
|
+
}
|
|
97
104
|
// Only attach `timeoutMs` when the tool author opts in. Leaving it
|
|
98
105
|
// unset means ToolRegistry.execute uses DEFAULT_TOOL_TIMEOUT_MS — set
|
|
99
106
|
// to <= 0 to disable the per-tool timeout entirely.
|
|
@@ -10,7 +10,7 @@ export default defineTool({
|
|
|
10
10
|
description: `Wait for a sub-agent to complete its task and retrieve the result.
|
|
11
11
|
|
|
12
12
|
Returns the agent's final result or current status if still running.
|
|
13
|
-
Use after sending a task to an agent via
|
|
13
|
+
Use after sending a task to an agent via PromptAgent.`,
|
|
14
14
|
parameters: {
|
|
15
15
|
type: 'object',
|
|
16
16
|
properties: {
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* open-source-message.js — task-334f R6 §Δ24.4.
|
|
3
|
-
*
|
|
4
|
-
* Low-level random access: given a (groupId, msgId), fetch the raw message
|
|
5
|
-
* from the group's jsonl log. Used when a VP has an exact pointer but does
|
|
6
|
-
* not want to run the memory_trace wrapper (5% case: audit / debug).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { defineTool } from './types.js';
|
|
10
|
-
|
|
11
|
-
export default defineTool({
|
|
12
|
-
name: 'open_source_message',
|
|
13
|
-
description: `Open a single source message by (groupId, msgId).
|
|
14
|
-
|
|
15
|
-
This is the low-level random-access primitive. Prefer memory_trace if you are
|
|
16
|
-
starting from a memory entry. Returns JSON: { message } or { error }.`,
|
|
17
|
-
parameters: {
|
|
18
|
-
type: 'object',
|
|
19
|
-
properties: {
|
|
20
|
-
groupId: { type: 'string', description: 'Group id' },
|
|
21
|
-
msgId: { type: 'string', description: 'Message id' },
|
|
22
|
-
},
|
|
23
|
-
required: ['groupId', 'msgId'],
|
|
24
|
-
},
|
|
25
|
-
isConcurrencySafe: () => true,
|
|
26
|
-
isReadOnly: () => true,
|
|
27
|
-
async execute(input, ctx) {
|
|
28
|
-
const { groupId, msgId } = input || {};
|
|
29
|
-
if (!groupId || !msgId) {
|
|
30
|
-
return JSON.stringify({ error: 'groupId and msgId required' });
|
|
31
|
-
}
|
|
32
|
-
const coordinator = ctx?.coordinator;
|
|
33
|
-
if (!coordinator || typeof coordinator.openGroup !== 'function') {
|
|
34
|
-
return JSON.stringify({ error: 'group coordinator not available' });
|
|
35
|
-
}
|
|
36
|
-
const group = coordinator.openGroup(groupId);
|
|
37
|
-
if (!group) return JSON.stringify({ error: `group not found: ${groupId}` });
|
|
38
|
-
|
|
39
|
-
const iter = typeof group.readMessageRange === 'function'
|
|
40
|
-
? group.readMessageRange(msgId, msgId)
|
|
41
|
-
: group.streamMessages();
|
|
42
|
-
for (const msg of iter) {
|
|
43
|
-
if (msg.id === msgId) {
|
|
44
|
-
return JSON.stringify({ message: msg });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return JSON.stringify({ error: `message not found: ${msgId} in ${groupId}` });
|
|
48
|
-
},
|
|
49
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* request-permissions.js — Request permission for dangerous operations.
|
|
3
|
-
*
|
|
4
|
-
* When an operation is flagged as destructive, this tool requests
|
|
5
|
-
* explicit user permission before proceeding.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { defineTool } from './types.js';
|
|
9
|
-
|
|
10
|
-
export default defineTool({
|
|
11
|
-
name: 'RequestPermissions',
|
|
12
|
-
description: `Request permission from the user for a potentially dangerous operation.
|
|
13
|
-
|
|
14
|
-
Use this before executing destructive operations like:
|
|
15
|
-
- Deleting files or directories
|
|
16
|
-
- Running commands that modify system state
|
|
17
|
-
- Force-pushing to git
|
|
18
|
-
- Resetting databases
|
|
19
|
-
|
|
20
|
-
The user must explicitly approve before you proceed.`,
|
|
21
|
-
parameters: {
|
|
22
|
-
type: 'object',
|
|
23
|
-
properties: {
|
|
24
|
-
operation: {
|
|
25
|
-
type: 'string',
|
|
26
|
-
description: 'Description of the operation that needs permission',
|
|
27
|
-
},
|
|
28
|
-
reason: {
|
|
29
|
-
type: 'string',
|
|
30
|
-
description: 'Why this operation is necessary',
|
|
31
|
-
},
|
|
32
|
-
risk_level: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
enum: ['low', 'medium', 'high', 'critical'],
|
|
35
|
-
description: 'Risk level of the operation',
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
required: ['operation'],
|
|
39
|
-
},
|
|
40
|
-
isConcurrencySafe: () => false,
|
|
41
|
-
isReadOnly: () => true,
|
|
42
|
-
async execute(input, ctx) {
|
|
43
|
-
const { operation, reason, risk_level = 'medium' } = input;
|
|
44
|
-
if (!operation) return JSON.stringify({ error: 'operation is required' });
|
|
45
|
-
|
|
46
|
-
// In a full integration, this would use the ask_user mechanism
|
|
47
|
-
// to get explicit permission. For now, return a structured request.
|
|
48
|
-
return JSON.stringify({
|
|
49
|
-
type: 'permission_request',
|
|
50
|
-
operation,
|
|
51
|
-
reason: reason || 'Operation requires explicit permission',
|
|
52
|
-
riskLevel: risk_level,
|
|
53
|
-
message: `⚠️ Permission required for: ${operation}` +
|
|
54
|
-
(reason ? `\nReason: ${reason}` : '') +
|
|
55
|
-
`\nRisk level: ${risk_level}`,
|
|
56
|
-
hint: 'User must explicitly approve this operation before proceeding.',
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
});
|