dsh-subagent-control 1.0.2 → 1.0.3
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/CHANGELOG.md +7 -0
- package/README.md +4 -0
- package/index.js +50 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.3
|
|
4
|
+
|
|
5
|
+
- `list_subagents` marks Team members, which are continuable subagents: each row carries
|
|
6
|
+
`team=teammate` (or `team=lead`) when the Agent Teams service can resolve it.
|
|
7
|
+
- New `include_teammates` parameter (default `true`); set it to `false` to keep only plain
|
|
8
|
+
subagents. Existing behaviour is unchanged for callers that pass nothing.
|
|
9
|
+
|
|
3
10
|
## 1.0.2
|
|
4
11
|
|
|
5
12
|
- New optional `extras` component (`dsh-subagent-control/extras`), shipped **disabled**:
|
package/README.md
CHANGED
|
@@ -45,6 +45,8 @@ interrupt_subagent subagent_id=3b0f9c1e
|
|
|
45
45
|
|
|
46
46
|
Each listing line is `id status "label" depth=`. `status` is `running` or `inactive` — **inactive means no turn is executing, not that the subagent is gone**. Use `send_subagent_message` to wake it.
|
|
47
47
|
|
|
48
|
+
Team members are continuable subagents too, so they also appear in this list, marked `team=teammate` (or `team=lead`). Pass `include_teammates: false` when you want only the plain subagents you started.
|
|
49
|
+
|
|
48
50
|
### Behaviour and limits
|
|
49
51
|
|
|
50
52
|
- **One-shot subagents are hidden.** They cannot accept a follow-up message, so listing them would only invite a call that is guaranteed to fail.
|
|
@@ -141,6 +143,8 @@ interrupt_subagent subagent_id=3b0f9c1e
|
|
|
141
143
|
|
|
142
144
|
每行格式是 `id status "label" depth=`。`status` 为 `running` 或 `inactive`——**inactive 表示当前没有在跑,不代表子代理已经消失**;用 `send_subagent_message` 可以把它叫醒。
|
|
143
145
|
|
|
146
|
+
团队成员本身也是可续子代理,所以同样会出现在列表里,并带 `team=teammate`(或 `team=lead`)标记;只想看自己派的普通子代理时,传 `include_teammates: false`。
|
|
147
|
+
|
|
144
148
|
### 行为与边界
|
|
145
149
|
|
|
146
150
|
- **一次性子代理不会被列出。** 它们无法接收后续消息,列出来只会诱导一次注定失败的调用。
|
package/index.js
CHANGED
|
@@ -37,6 +37,7 @@ const CHILD_ITEM = {
|
|
|
37
37
|
status: { type: 'string', enum: ['running', 'inactive'] },
|
|
38
38
|
parent: { type: 'string' },
|
|
39
39
|
depth: { type: 'number' },
|
|
40
|
+
team: { type: 'string', enum: ['lead', 'teammate'] },
|
|
40
41
|
},
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -83,13 +84,42 @@ function project(agents, entry, position) {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Team members are continuable subagents, so the subagent catalog cannot tell a
|
|
89
|
+
* teammate from a worker on its own. Resolve the Team role when the Agent Teams
|
|
90
|
+
* service is present, and answer `undefined` when the agent is not in a Team.
|
|
91
|
+
*/
|
|
92
|
+
function teamRoleResolver(ctx, caller) {
|
|
93
|
+
const teams = typeof ctx.get === 'function' ? ctx.get('agentTeams') : undefined
|
|
94
|
+
if (teams === undefined) return () => undefined
|
|
95
|
+
const roster = new Map()
|
|
96
|
+
try {
|
|
97
|
+
for (const member of teams.listMembers(caller) ?? []) roster.set(member.id, member.role)
|
|
98
|
+
} catch {
|
|
99
|
+
/* the caller is not part of a Team; the per-agent lookup below still works */
|
|
100
|
+
}
|
|
101
|
+
return (id) => {
|
|
102
|
+
const known = roster.get(id)
|
|
103
|
+
if (known !== undefined) return known
|
|
104
|
+
const agent = ctx.agents.get(id)
|
|
105
|
+
if (agent === undefined) return undefined
|
|
106
|
+
try {
|
|
107
|
+
const membership = teams.tryMembership?.(agent)
|
|
108
|
+
return membership === undefined ? undefined : membership.role
|
|
109
|
+
} catch {
|
|
110
|
+
return undefined
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
function renderEntry(entry) {
|
|
87
116
|
if (entry.kind === 'diagnostic') {
|
|
88
117
|
const at = entry.parent === undefined ? '' : ` parent=${entry.parent}`
|
|
89
118
|
return `[${entry.reason}] ${entry.id}${at}`
|
|
90
119
|
}
|
|
91
120
|
const depth = entry.depth === undefined ? '' : ` depth=${entry.depth}`
|
|
92
|
-
|
|
121
|
+
const team = entry.team === undefined ? '' : ` team=${entry.team}`
|
|
122
|
+
return `${entry.id} ${entry.status} ${JSON.stringify(entry.label)}${depth}${team}`
|
|
93
123
|
}
|
|
94
124
|
|
|
95
125
|
export function apply(ctx) {
|
|
@@ -100,6 +130,8 @@ export function apply(ctx) {
|
|
|
100
130
|
description:
|
|
101
131
|
'List the subagents you started, with their ids, labels and status. ' +
|
|
102
132
|
'running means it is working; inactive means it is not working right now, which does not mean it is gone. ' +
|
|
133
|
+
'Team members are continuable subagents too, so they appear here marked team=teammate or team=lead; ' +
|
|
134
|
+
'use include_teammates:false when you only want the workers you started. ' +
|
|
103
135
|
'You are notified when a subagent finishes, so there is no need to poll. ' +
|
|
104
136
|
'Use send_subagent_message to continue a conversation with one of them.',
|
|
105
137
|
parameters: {
|
|
@@ -114,6 +146,11 @@ export function apply(ctx) {
|
|
|
114
146
|
'descendants lists the whole tree below you with each entry parent and depth; ' +
|
|
115
147
|
'entries deeper than one level accept only interrupt_subagent.',
|
|
116
148
|
},
|
|
149
|
+
include_teammates: {
|
|
150
|
+
type: 'boolean',
|
|
151
|
+
description:
|
|
152
|
+
'When false, leave out agents that belong to the current Team, keeping only plain subagents. Default true.',
|
|
153
|
+
},
|
|
117
154
|
},
|
|
118
155
|
},
|
|
119
156
|
output: {
|
|
@@ -127,12 +164,22 @@ export function apply(ctx) {
|
|
|
127
164
|
},
|
|
128
165
|
async execute(args, exec) {
|
|
129
166
|
const caller = requireAgent(exec, 'list_subagents')
|
|
167
|
+
const roleOf = teamRoleResolver(ctx, caller)
|
|
168
|
+
const wantsTeammates = args.include_teammates !== false
|
|
169
|
+
const emit = (entry, position) => {
|
|
170
|
+
const item = project(ctx.agents, entry, position)
|
|
171
|
+
if (item === undefined) return undefined
|
|
172
|
+
const role = roleOf(item.id)
|
|
173
|
+
if (role !== undefined) item.team = role
|
|
174
|
+
if (!wantsTeammates && role !== undefined) return undefined
|
|
175
|
+
return item
|
|
176
|
+
}
|
|
130
177
|
if ((args.scope ?? 'children') === 'descendants') {
|
|
131
178
|
const rows = await ctx.subagents.listDescendants(caller.id, exec.signal)
|
|
132
|
-
return rows.map((row) =>
|
|
179
|
+
return rows.map((row) => emit(row, row)).filter(Boolean)
|
|
133
180
|
}
|
|
134
181
|
const rows = await ctx.subagents.listChildren(caller.id, exec.signal)
|
|
135
|
-
return rows.map((row) =>
|
|
182
|
+
return rows.map((row) => emit(row)).filter(Boolean)
|
|
136
183
|
},
|
|
137
184
|
})
|
|
138
185
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-subagent-control",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Three tools — list_subagents, send_subagent_message, interrupt_subagent — that let any agent, including a team member, list, steer and stop the subagents it started.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|