@xrkseek/harness 0.6.7 → 0.6.9
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/node_modules/@xrkseek/preset-harness/dist/preset.d.ts.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/preset.js +10 -2
- package/node_modules/@xrkseek/preset-harness/dist/preset.js.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/preset-minimal/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/protocol/dist/content.d.ts.map +1 -1
- package/node_modules/@xrkseek/protocol/dist/content.js +5 -1
- package/node_modules/@xrkseek/protocol/dist/content.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-member-card.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-member-card.js +14 -1
- package/node_modules/@xrkseek/server-face/dist/agent-member-card.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-member-pack.d.ts +160 -0
- package/node_modules/@xrkseek/server-face/dist/agent-member-pack.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/agent-member-pack.js +684 -0
- package/node_modules/@xrkseek/server-face/dist/agent-member-pack.js.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts +44 -2
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js +122 -2
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.d.ts +7 -5
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.js +831 -64
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.d.ts +9 -0
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js +25 -2
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.d.ts +4 -0
- package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.js +28 -7
- package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/dispatch.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/dispatch.js +5 -1
- package/node_modules/@xrkseek/server-face/dist/dispatch.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/team.d.ts +4 -0
- package/node_modules/@xrkseek/server-face/dist/handlers/team.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/team.js +223 -3
- package/node_modules/@xrkseek/server-face/dist/handlers/team.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/index.d.ts +2 -2
- package/node_modules/@xrkseek/server-face/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/index.js +2 -2
- package/node_modules/@xrkseek/server-face/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/runtime.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/runtime.js +7 -2
- package/node_modules/@xrkseek/server-face/dist/runtime.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-capability.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-capability.js +30 -2
- package/node_modules/@xrkseek/server-face/dist/session-capability.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-posture.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-posture.js +3 -1
- package/node_modules/@xrkseek/server-face/dist/session-posture.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.d.ts +9 -0
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.js +18 -2
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tools.js +5 -0
- package/node_modules/@xrkseek/server-face/dist/session-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.js +46 -3
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.d.ts +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.js +25 -7
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.d.ts +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.js +18 -5
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/index.d.ts +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/index.js +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.d.ts +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.js +12 -7
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/zip-store.d.ts +5 -3
- package/node_modules/@xrkseek/server-face/dist/zip-store.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/zip-store.js +60 -1
- package/node_modules/@xrkseek/server-face/dist/zip-store.js.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/index.js +31 -24
- package/node_modules/@xrkseek/server-host/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/index.d.ts +8 -0
- package/node_modules/@xrkseek/workspace/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/index.js +17 -5
- package/node_modules/@xrkseek/workspace/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.d.ts +1 -0
- package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.d.ts.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.js +4 -1
- package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.js.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/skill-dirs.d.ts +2 -0
- package/node_modules/@xrkseek/workspace/dist/skill-dirs.d.ts.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/skill-dirs.js +24 -21
- package/node_modules/@xrkseek/workspace/dist/skill-dirs.js.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/skills.d.ts +2 -0
- package/node_modules/@xrkseek/workspace/dist/skills.d.ts.map +1 -1
- package/node_modules/@xrkseek/workspace/dist/skills.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing team_list / save / import / ready / export / remove /
|
|
3
|
+
* team_self (child own surfaces). Packs under agent-rosters/packs/.
|
|
4
|
+
*/
|
|
5
|
+
import { writeFileSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { readSessionEvents } from "@xrkseek/core-session";
|
|
8
|
+
import { isAttachmentError } from "@xrkseek/attachment";
|
|
1
9
|
import { canvasWorkspaceIdForSession } from "./canvas-tools.js";
|
|
2
10
|
import { isAgentTeamSpawnRole } from "./agent-team-roles.js";
|
|
3
|
-
import { memberIdProblem, catalogMemberWriteProblem, parseMemberToolPolicy, parseRosterScope, rosterPublicFields, } from "./agent-roster-store.js";
|
|
4
|
-
import { parseAgentMemberCard } from "./agent-member-card.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
11
|
+
import { GLOBAL_ROSTER_ID, memberIdProblem, catalogMemberWriteProblem, parseMemberInject, parseMemberToolPolicy, parseRosterScope, rosterPublicFields, } from "./agent-roster-store.js";
|
|
12
|
+
import { parseAgentMemberCard, } from "./agent-member-card.js";
|
|
13
|
+
import { AGENT_MEMBER_PACK_LAYOUT, MEMBER_PACK_BRIEFCASE, buildAgentMemberPackZip, collectPackSkillFiles, collectPackWorkspaceFiles, listMemberPackInventory, loadAgentMemberPack, loadAgentMemberPackBytes, materializeAgentMemberPack, mergeIntoMemberPack, notReadyPackHints, syncMemberPackPlaybook, } from "./agent-member-pack.js";
|
|
14
|
+
import { withAdvancedPackPathGuidance } from "./capture-member-playbook.js";
|
|
15
|
+
import { resolveSessionCwd } from "./session-cwd.js";
|
|
16
|
+
import { referencedAttachmentIds } from "./session-attachment.js";
|
|
17
|
+
import { dispatchFaceMethod } from "./dispatch.js";
|
|
18
|
+
import { liveRosterCatalog, rosterScopeVisibility, teamListDescription, } from "./session-tool-copy.js";
|
|
19
|
+
function teamPresent(title, kind, args = {}) {
|
|
20
|
+
return { card: "generic", title, kind, rawInput: args };
|
|
21
|
+
}
|
|
22
|
+
function missingMemberHint(runtime, canvasId, id, tool) {
|
|
23
|
+
const home = runtime.agentRoster.findHome(id);
|
|
24
|
+
if (!home) {
|
|
25
|
+
return `${tool}: unknown or catalog member "${id}" (team_list first)`;
|
|
26
|
+
}
|
|
27
|
+
if (home.scope === "global" || home.rosterWorkspaceId === canvasId) {
|
|
28
|
+
return `${tool}: unknown or catalog member "${id}"`;
|
|
29
|
+
}
|
|
30
|
+
return (`${tool}: member "${id}" is workspace-scoped on canvas ${home.rosterWorkspaceId} ` +
|
|
31
|
+
`(not visible here). Open a session in that workspace, or re-import with scope=global.`);
|
|
32
|
+
}
|
|
7
33
|
function registerTool(tools, tool) {
|
|
8
34
|
if (tools.get(tool.name))
|
|
9
35
|
return;
|
|
@@ -14,24 +40,255 @@ function readArgs(args) {
|
|
|
14
40
|
return {};
|
|
15
41
|
return args;
|
|
16
42
|
}
|
|
17
|
-
|
|
43
|
+
function resolvePackPath(runtime, sessionId, raw) {
|
|
44
|
+
const trimmed = raw.trim();
|
|
45
|
+
if (path.isAbsolute(trimmed))
|
|
46
|
+
return path.resolve(trimmed);
|
|
47
|
+
return path.resolve(resolveSessionCwd(runtime, sessionId), trimmed);
|
|
48
|
+
}
|
|
49
|
+
/** `sha256:…` or `attachment:sha256:…` → durable id. */
|
|
50
|
+
function parseAttachmentPackId(raw) {
|
|
51
|
+
const trimmed = raw.trim();
|
|
52
|
+
if (/^sha256:[a-f0-9]+$/i.test(trimmed))
|
|
53
|
+
return trimmed;
|
|
54
|
+
if (/^attachment:/i.test(trimmed)) {
|
|
55
|
+
const id = trimmed.slice("attachment:".length).trim();
|
|
56
|
+
if (/^sha256:[a-f0-9]+$/i.test(id))
|
|
57
|
+
return id;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
async function loadPackFromArg(runtime, sessionId, packRaw) {
|
|
62
|
+
const attachmentId = parseAttachmentPackId(packRaw);
|
|
63
|
+
if (attachmentId) {
|
|
64
|
+
const attachments = runtime.attachments;
|
|
65
|
+
if (!attachments) {
|
|
66
|
+
return { ok: false, error: "attachments unavailable on this Host" };
|
|
67
|
+
}
|
|
68
|
+
const events = readSessionEvents(runtime.store, sessionId);
|
|
69
|
+
if (!referencedAttachmentIds(events).has(attachmentId)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: `attachment ${attachmentId} is not referenced by this session`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const stored = await attachments.readFile(attachmentId);
|
|
77
|
+
return loadAgentMemberPackBytes(Buffer.from(stored.data));
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (isAttachmentError(err) && err.code === "NOT_FOUND") {
|
|
81
|
+
return { ok: false, error: `attachment ${attachmentId} bytes missing` };
|
|
82
|
+
}
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return loadAgentMemberPack(resolvePackPath(runtime, sessionId, packRaw));
|
|
87
|
+
}
|
|
88
|
+
function enrichListedMember(runtime, canvasId, row) {
|
|
89
|
+
const rosterWs = row.scope === "global" ? GLOBAL_ROSTER_ID : canvasId;
|
|
90
|
+
const pub = {
|
|
91
|
+
...rosterPublicFields(row),
|
|
92
|
+
visibility: rosterScopeVisibility(row.scope === "global" ? "global" : "workspace", rosterWs),
|
|
93
|
+
};
|
|
94
|
+
if (row.kind !== "advanced")
|
|
95
|
+
return pub;
|
|
96
|
+
const packOpts = {
|
|
97
|
+
rosterWorkspaceId: rosterWs,
|
|
98
|
+
memberId: row.id,
|
|
99
|
+
productHome: runtime.agentRoster.home,
|
|
100
|
+
};
|
|
101
|
+
const inventory = listMemberPackInventory(packOpts);
|
|
102
|
+
const base = {
|
|
103
|
+
...pub,
|
|
104
|
+
...(inventory
|
|
105
|
+
? {
|
|
106
|
+
packHome: inventory.packHome,
|
|
107
|
+
playbookPath: inventory.playbookPath,
|
|
108
|
+
...(inventory.envPrepPath
|
|
109
|
+
? { envPrepPath: inventory.envPrepPath }
|
|
110
|
+
: {}),
|
|
111
|
+
...(inventory.materialsPath
|
|
112
|
+
? {
|
|
113
|
+
materialsPath: inventory.materialsPath,
|
|
114
|
+
materialsTop: inventory.materialsTop,
|
|
115
|
+
}
|
|
116
|
+
: {}),
|
|
117
|
+
skills: inventory.skills,
|
|
118
|
+
briefcase: MEMBER_PACK_BRIEFCASE,
|
|
119
|
+
}
|
|
120
|
+
: {}),
|
|
121
|
+
};
|
|
122
|
+
if (row.ready)
|
|
123
|
+
return base;
|
|
124
|
+
return {
|
|
125
|
+
...base,
|
|
126
|
+
...notReadyPackHints(packOpts),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function resolveCwdPaths(runtime, sessionId, rows) {
|
|
130
|
+
const cwd = resolveSessionCwd(runtime, sessionId);
|
|
131
|
+
if (!Array.isArray(rows))
|
|
132
|
+
return [];
|
|
133
|
+
return rows
|
|
134
|
+
.filter((s) => typeof s === "string" && s.trim().length > 0)
|
|
135
|
+
.map((s) => path.isAbsolute(s.trim()) ? s.trim() : path.resolve(cwd, s.trim()));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Mint an advanced pack member (playbook + optional skills/files/env).
|
|
139
|
+
* Shared by team_save — team_import only installs card=/pack=.
|
|
140
|
+
*/
|
|
141
|
+
function mintAdvancedMember(input) {
|
|
142
|
+
const { runtime, sessionId, canvasId, scope, name, playbook, } = input;
|
|
143
|
+
if (!name.trim() || !playbook.trim()) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
content: "team_save: advanced needs name and playbook (from the real scenario)",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const toolsPolicy = parseMemberToolPolicy(input.tools);
|
|
150
|
+
if (input.tools !== undefined && input.tools !== null && !toolsPolicy) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
content: 'team_save: tools needs { mode: "allow"|"deny", names: [...] }',
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const card = {
|
|
157
|
+
name: name.trim(),
|
|
158
|
+
playbook: withAdvancedPackPathGuidance(playbook),
|
|
159
|
+
role: isAgentTeamSpawnRole(input.roleRaw) ? input.roleRaw : "worker",
|
|
160
|
+
...(input.brief?.trim() ? { brief: input.brief.trim() } : {}),
|
|
161
|
+
...(input.inject === "subagent" || input.inject === "minimal"
|
|
162
|
+
? { inject: input.inject }
|
|
163
|
+
: {}),
|
|
164
|
+
...(toolsPolicy ? { tools: toolsPolicy } : {}),
|
|
165
|
+
};
|
|
166
|
+
const skillPaths = resolveCwdPaths(runtime, sessionId, input.skills);
|
|
167
|
+
let skillsFiles;
|
|
168
|
+
if (skillPaths.length > 0) {
|
|
169
|
+
const bundled = collectPackSkillFiles(skillPaths);
|
|
170
|
+
if (!bundled.ok) {
|
|
171
|
+
return { ok: false, content: `team_save: ${bundled.error}` };
|
|
172
|
+
}
|
|
173
|
+
skillsFiles = bundled.files;
|
|
174
|
+
}
|
|
175
|
+
const filePaths = resolveCwdPaths(runtime, sessionId, input.files);
|
|
176
|
+
let workspaceFiles;
|
|
177
|
+
if (filePaths.length > 0) {
|
|
178
|
+
const bundled = collectPackWorkspaceFiles(filePaths);
|
|
179
|
+
if (!bundled.ok) {
|
|
180
|
+
return { ok: false, content: `team_save: ${bundled.error}` };
|
|
181
|
+
}
|
|
182
|
+
workspaceFiles = bundled.files;
|
|
183
|
+
}
|
|
184
|
+
const envPrep = input.envPrep?.trim() || undefined;
|
|
185
|
+
const zip = buildAgentMemberPackZip(card, {
|
|
186
|
+
...(skillsFiles && skillsFiles.size > 0 ? { skillsFiles } : {}),
|
|
187
|
+
...(workspaceFiles && workspaceFiles.size > 0 ? { workspaceFiles } : {}),
|
|
188
|
+
...(envPrep ? { envPrep } : {}),
|
|
189
|
+
});
|
|
190
|
+
const loaded = loadAgentMemberPackBytes(zip);
|
|
191
|
+
if (!loaded.ok) {
|
|
192
|
+
return { ok: false, content: `team_save: ${loaded.error}` };
|
|
193
|
+
}
|
|
194
|
+
const rosterWs = scope === "global" ? GLOBAL_ROSTER_ID : canvasId;
|
|
195
|
+
const member = runtime.agentRoster.upsertAtScope(canvasId, scope, {
|
|
196
|
+
...loaded.pack.card,
|
|
197
|
+
kind: "advanced",
|
|
198
|
+
ready: false,
|
|
199
|
+
...(input.id?.trim() ? { id: input.id.trim() } : {}),
|
|
200
|
+
});
|
|
201
|
+
if (!member) {
|
|
202
|
+
return { ok: false, content: "team_save: name and playbook required" };
|
|
203
|
+
}
|
|
204
|
+
const placed = materializeAgentMemberPack(loaded.pack, {
|
|
205
|
+
rosterWorkspaceId: rosterWs,
|
|
206
|
+
memberId: member.id,
|
|
207
|
+
productHome: runtime.agentRoster.home,
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
ok: true,
|
|
211
|
+
content: JSON.stringify({
|
|
212
|
+
workspaceId: rosterWs,
|
|
213
|
+
scope,
|
|
214
|
+
visibility: rosterScopeVisibility(scope, rosterWs),
|
|
215
|
+
member,
|
|
216
|
+
source: "team_save",
|
|
217
|
+
kind: "advanced",
|
|
218
|
+
packDir: placed.packDir,
|
|
219
|
+
skillNames: placed.skillNames,
|
|
220
|
+
ready: false,
|
|
221
|
+
...(placed.envPrep
|
|
222
|
+
? {
|
|
223
|
+
envPrep: placed.envPrep,
|
|
224
|
+
envHint: "Review envPrep here (or hand zip to a friend). Then team_ready. Do not auto-run scripts.",
|
|
225
|
+
}
|
|
226
|
+
: {
|
|
227
|
+
envHint: "No env/SETUP.md — team_ready when configured; friend gets playbook (± skills).",
|
|
228
|
+
}),
|
|
229
|
+
next: "team_ready → subagent member_id",
|
|
230
|
+
layout: AGENT_MEMBER_PACK_LAYOUT,
|
|
231
|
+
pathHint: `Spawn keeps parent workspace cwd. ${MEMBER_PACK_BRIEFCASE} = pack_home (injected) — child may read/edit/copy into workspace. Share zip with relative paths under the pack tree.`,
|
|
232
|
+
shareHint: "Friend: team_export zip / UI import, or they team_import pack=… → follow envPrep with main session → team_ready. " +
|
|
233
|
+
"scope=global keeps it on every canvas team_list.",
|
|
234
|
+
}),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function patchExistingMember(runtime, canvasId, existing, patch) {
|
|
238
|
+
const name = (patch.name ?? existing.name).trim();
|
|
239
|
+
let playbook = (patch.playbook ?? existing.playbook).trim();
|
|
240
|
+
if (existing.kind === "advanced" && patch.playbook !== undefined) {
|
|
241
|
+
playbook = withAdvancedPackPathGuidance(playbook);
|
|
242
|
+
}
|
|
243
|
+
if (!name || !playbook)
|
|
244
|
+
return undefined;
|
|
245
|
+
const scope = existing.scope === "global" ? "global" : "workspace";
|
|
246
|
+
const inject = patch.inject ?? existing.inject;
|
|
247
|
+
const tools = patch.tools === undefined
|
|
248
|
+
? existing.tools
|
|
249
|
+
: patch.tools === null
|
|
250
|
+
? undefined
|
|
251
|
+
: patch.tools;
|
|
252
|
+
const member = runtime.agentRoster.upsertAtScope(canvasId, scope, {
|
|
253
|
+
id: existing.id,
|
|
254
|
+
name,
|
|
255
|
+
playbook,
|
|
256
|
+
role: isAgentTeamSpawnRole(existing.role) ? existing.role : "worker",
|
|
257
|
+
kind: existing.kind,
|
|
258
|
+
ready: existing.ready,
|
|
259
|
+
brief: patch.brief !== undefined ? patch.brief : existing.brief,
|
|
260
|
+
inject,
|
|
261
|
+
...(tools ? { tools } : patch.tools === null ? { tools: null } : {}),
|
|
262
|
+
appearance: existing.appearance,
|
|
263
|
+
});
|
|
264
|
+
if (!member)
|
|
265
|
+
return undefined;
|
|
266
|
+
if (existing.kind === "advanced") {
|
|
267
|
+
const rosterWs = scope === "global" ? GLOBAL_ROSTER_ID : canvasId;
|
|
268
|
+
syncMemberPackPlaybook({
|
|
269
|
+
rosterWorkspaceId: rosterWs,
|
|
270
|
+
memberId: member.id,
|
|
271
|
+
playbook: member.playbook,
|
|
272
|
+
productHome: runtime.agentRoster.home,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return runtime.agentRoster.get(canvasId, member.id);
|
|
276
|
+
}
|
|
277
|
+
/** Register team_list / team_save / team_import / team_ready / team_remove (or list + team_self). */
|
|
18
278
|
export function bindAgentRosterTools(tools, options) {
|
|
19
279
|
const { runtime, sessionId, listOnly = false } = options;
|
|
280
|
+
const selfMemberId = options.selfMemberId?.trim() || undefined;
|
|
20
281
|
const ws = () => canvasWorkspaceIdForSession(runtime, sessionId);
|
|
21
282
|
registerTool(tools, {
|
|
22
283
|
name: "team_list",
|
|
23
284
|
description: "List Agent Team members (干员) for this workspace + global.",
|
|
24
285
|
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
25
286
|
isConcurrencySafe: () => true,
|
|
26
|
-
presentCall: () => (
|
|
27
|
-
card: "generic",
|
|
28
|
-
title: "team_list",
|
|
29
|
-
kind: "search",
|
|
30
|
-
rawInput: {},
|
|
31
|
-
}),
|
|
287
|
+
presentCall: () => teamPresent("team_list", "search"),
|
|
32
288
|
dynamicSchema: () => ({
|
|
33
289
|
description: teamListDescription({
|
|
34
290
|
listOnly,
|
|
291
|
+
...(selfMemberId ? { selfRevise: true } : {}),
|
|
35
292
|
catalog: liveRosterCatalog(runtime, sessionId),
|
|
36
293
|
}),
|
|
37
294
|
}),
|
|
@@ -39,67 +296,171 @@ export function bindAgentRosterTools(tools, options) {
|
|
|
39
296
|
const workspaceId = ws();
|
|
40
297
|
const members = runtime.agentRoster
|
|
41
298
|
.listVisible(workspaceId)
|
|
42
|
-
.map(
|
|
299
|
+
.map((row) => enrichListedMember(runtime, workspaceId, row));
|
|
43
300
|
return { content: JSON.stringify({ workspaceId, members }) };
|
|
44
301
|
},
|
|
45
302
|
});
|
|
46
|
-
if (listOnly)
|
|
303
|
+
if (listOnly) {
|
|
304
|
+
if (selfMemberId) {
|
|
305
|
+
registerTool(tools, {
|
|
306
|
+
name: "team_self",
|
|
307
|
+
description: "Revise YOUR 干员 surfaces (this member_id): playbook, inject, tools, brief. " +
|
|
308
|
+
"Child tool/inject surface ≠ parent. Durable for next spawn. " +
|
|
309
|
+
"Cannot mint, import, export, or edit other members.",
|
|
310
|
+
parameters: {
|
|
311
|
+
type: "object",
|
|
312
|
+
properties: {
|
|
313
|
+
playbook: {
|
|
314
|
+
type: "string",
|
|
315
|
+
description: "Replacement playbook (max 20000). Omit to keep.",
|
|
316
|
+
},
|
|
317
|
+
brief: {
|
|
318
|
+
type: "string",
|
|
319
|
+
description: "Replacement duty line. Omit to keep.",
|
|
320
|
+
},
|
|
321
|
+
name: {
|
|
322
|
+
type: "string",
|
|
323
|
+
description: "Optional display-name tweak. Omit to keep.",
|
|
324
|
+
},
|
|
325
|
+
inject: {
|
|
326
|
+
type: "string",
|
|
327
|
+
description: "minimal (thin context) | subagent (keep project docs). Omit to keep.",
|
|
328
|
+
},
|
|
329
|
+
tools: {
|
|
330
|
+
type: "object",
|
|
331
|
+
properties: {
|
|
332
|
+
mode: { type: "string" },
|
|
333
|
+
names: { type: "array", items: { type: "string" } },
|
|
334
|
+
},
|
|
335
|
+
additionalProperties: false,
|
|
336
|
+
description: "allow|deny list on top of role. Pass null to clear.",
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
additionalProperties: false,
|
|
340
|
+
},
|
|
341
|
+
presentCall: (args) => teamPresent("team_self", "execute", args),
|
|
342
|
+
execute: async (args) => {
|
|
343
|
+
const a = readArgs(args);
|
|
344
|
+
const canvasId = ws();
|
|
345
|
+
const existing = runtime.agentRoster.get(canvasId, selfMemberId);
|
|
346
|
+
if (!existing || existing.catalog) {
|
|
347
|
+
return {
|
|
348
|
+
content: `team_self: member "${selfMemberId}" not editable here`,
|
|
349
|
+
isError: true,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const hasPlaybook = typeof a.playbook === "string";
|
|
353
|
+
const hasBrief = typeof a.brief === "string";
|
|
354
|
+
const hasName = typeof a.name === "string" && a.name.trim().length > 0;
|
|
355
|
+
const hasInject = a.inject === "minimal" || a.inject === "subagent";
|
|
356
|
+
const hasTools = a.tools !== undefined;
|
|
357
|
+
if (!hasPlaybook && !hasBrief && !hasName && !hasInject && !hasTools) {
|
|
358
|
+
return {
|
|
359
|
+
content: "team_self: pass playbook, brief, inject, and/or tools (optional name)",
|
|
360
|
+
isError: true,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const toolsPatch = a.tools === null
|
|
364
|
+
? null
|
|
365
|
+
: hasTools
|
|
366
|
+
? parseMemberToolPolicy(a.tools)
|
|
367
|
+
: undefined;
|
|
368
|
+
if (hasTools && a.tools !== null && !toolsPatch) {
|
|
369
|
+
return {
|
|
370
|
+
content: 'team_self: tools needs { mode: "allow"|"deny", names: [...] }',
|
|
371
|
+
isError: true,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const patched = patchExistingMember(runtime, canvasId, existing, {
|
|
375
|
+
...(hasName ? { name: String(a.name).trim() } : {}),
|
|
376
|
+
...(hasPlaybook ? { playbook: String(a.playbook) } : {}),
|
|
377
|
+
...(hasBrief ? { brief: String(a.brief) } : {}),
|
|
378
|
+
...(hasInject
|
|
379
|
+
? { inject: parseMemberInject(a.inject, existing.inject) }
|
|
380
|
+
: {}),
|
|
381
|
+
...(hasTools ? { tools: toolsPatch ?? null } : {}),
|
|
382
|
+
});
|
|
383
|
+
if (!patched) {
|
|
384
|
+
return { content: "team_self: name and playbook required", isError: true };
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
content: JSON.stringify({
|
|
388
|
+
member: {
|
|
389
|
+
...enrichListedMember(runtime, canvasId, patched),
|
|
390
|
+
playbook: patched.playbook,
|
|
391
|
+
},
|
|
392
|
+
next: "next subagent member_id uses these surfaces; this turn already started",
|
|
393
|
+
}),
|
|
394
|
+
};
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
}
|
|
47
398
|
return;
|
|
399
|
+
}
|
|
48
400
|
registerTool(tools, {
|
|
49
401
|
name: "team_save",
|
|
50
|
-
description: "
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"scope global|workspace. inject minimal (skip home persona + skill catalog) | subagent. " +
|
|
56
|
-
"tools {mode:allow|deny, names:[...]} weakens the parent preset only. " +
|
|
57
|
-
"role worker|researcher|reviewer|lead|default is the deny template under the playbook.",
|
|
402
|
+
description: "Mint or revise a 干员 (template → skill xrk-delegate). " +
|
|
403
|
+
"Thin: name+playbook (+inject/tools). " +
|
|
404
|
+
`Advanced ${MEMBER_PACK_BRIEFCASE}: kind=advanced + skills/files/env_prep (playbook.md · skills/ · workspace/ · env/SETUP.md) → team_ready → team_export. ` +
|
|
405
|
+
"Revise any custom member with id= (omit unchanged fields); advanced may also merge skills/files/env_prep into 公文包. " +
|
|
406
|
+
"Install friend zip/json → team_import (not this).",
|
|
58
407
|
parameters: {
|
|
59
408
|
type: "object",
|
|
60
409
|
properties: {
|
|
61
410
|
id: {
|
|
62
411
|
type: "string",
|
|
63
|
-
description: "mem_
|
|
64
|
-
|
|
412
|
+
description: "Existing mem_… to revise (thin or advanced). Omit to mint. Pattern: mem_[A-Za-z0-9][A-Za-z0-9._-]*",
|
|
413
|
+
},
|
|
414
|
+
name: {
|
|
415
|
+
type: "string",
|
|
416
|
+
description: "Short label (e.g. PPT排版员). On revise, omit to keep.",
|
|
65
417
|
},
|
|
66
|
-
name: { type: "string" },
|
|
67
418
|
playbook: {
|
|
68
419
|
type: "string",
|
|
69
|
-
description: "
|
|
420
|
+
description: "Durable how-to. Prefer: Role; Steps; Constraints; Acceptance; for advanced also list what is in 公文包 (briefcase). " +
|
|
421
|
+
"On revise, omit to keep. Max 20000. Template: skill xrk-delegate.",
|
|
70
422
|
},
|
|
71
423
|
role: { type: "string" },
|
|
72
|
-
|
|
73
|
-
type: "
|
|
74
|
-
description: "
|
|
424
|
+
kind: {
|
|
425
|
+
type: "string",
|
|
426
|
+
description: "thin (default) | advanced. Also advanced when skills/files/env_prep set.",
|
|
75
427
|
},
|
|
76
|
-
brief: { type: "string" },
|
|
428
|
+
brief: { type: "string", description: "One-line duty for team_list." },
|
|
77
429
|
scope: {
|
|
78
430
|
type: "string",
|
|
79
|
-
description: "global | workspace
|
|
431
|
+
description: "global | workspace. Thin default workspace; advanced default global.",
|
|
80
432
|
},
|
|
81
433
|
inject: {
|
|
82
434
|
type: "string",
|
|
83
|
-
description: "minimal (default
|
|
435
|
+
description: "minimal (default) | subagent (keep project docs)",
|
|
84
436
|
},
|
|
85
437
|
tools: {
|
|
86
438
|
type: "object",
|
|
87
|
-
description: "Weaken-only tool policy. {mode:'allow'|'deny', names:['bash','read']}.",
|
|
88
439
|
properties: {
|
|
89
440
|
mode: { type: "string" },
|
|
90
441
|
names: { type: "array", items: { type: "string" } },
|
|
91
442
|
},
|
|
92
443
|
additionalProperties: false,
|
|
444
|
+
description: "Child tool allow|deny on top of role.",
|
|
445
|
+
},
|
|
446
|
+
env_prep: {
|
|
447
|
+
type: "string",
|
|
448
|
+
description: `Advanced ${MEMBER_PACK_BRIEFCASE}: env/SETUP.md (links, installs). Never auto-run. Merge on id= revise.`,
|
|
449
|
+
},
|
|
450
|
+
skills: {
|
|
451
|
+
type: "array",
|
|
452
|
+
items: { type: "string" },
|
|
453
|
+
description: `Advanced: skill dirs → ${MEMBER_PACK_BRIEFCASE} skills/<id>/ (full tree incl. references/). Merge on id=.`,
|
|
454
|
+
},
|
|
455
|
+
files: {
|
|
456
|
+
type: "array",
|
|
457
|
+
items: { type: "string" },
|
|
458
|
+
description: `Advanced: paths → ${MEMBER_PACK_BRIEFCASE} workspace/ (materials; not session cwd). Merge on id=.`,
|
|
93
459
|
},
|
|
94
460
|
},
|
|
95
461
|
additionalProperties: false,
|
|
96
462
|
},
|
|
97
|
-
presentCall: (args) => (
|
|
98
|
-
card: "generic",
|
|
99
|
-
title: "team_save",
|
|
100
|
-
kind: "execute",
|
|
101
|
-
rawInput: args,
|
|
102
|
-
}),
|
|
463
|
+
presentCall: (args) => teamPresent("team_save", "execute", args),
|
|
103
464
|
execute: async (args) => {
|
|
104
465
|
const a = readArgs(args);
|
|
105
466
|
const idProblem = memberIdProblem(a.id);
|
|
@@ -108,30 +469,199 @@ export function bindAgentRosterTools(tools, options) {
|
|
|
108
469
|
const catalogProblem = catalogMemberWriteProblem(a.id);
|
|
109
470
|
if (catalogProblem)
|
|
110
471
|
return { content: `team_save: ${catalogProblem}`, isError: true };
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
: undefined;
|
|
114
|
-
const name = String(a.name ?? "").trim() || captured?.name || "";
|
|
115
|
-
const playbook = String(a.playbook ?? "").trim() || captured?.playbook || "";
|
|
472
|
+
const name = String(a.name ?? "").trim();
|
|
473
|
+
const playbook = String(a.playbook ?? "").trim();
|
|
116
474
|
const roleRaw = typeof a.role === "string" ? a.role : "worker";
|
|
117
475
|
const canvasId = ws();
|
|
118
|
-
const
|
|
476
|
+
const wantsAdvanced = a.kind === "advanced" ||
|
|
477
|
+
(typeof a.env_prep === "string" && a.env_prep.trim().length > 0) ||
|
|
478
|
+
(Array.isArray(a.skills) && a.skills.length > 0) ||
|
|
479
|
+
(Array.isArray(a.files) && a.files.length > 0);
|
|
480
|
+
const scope = parseRosterScope(a.scope ?? (wantsAdvanced ? "global" : "workspace"));
|
|
119
481
|
if (scope === undefined) {
|
|
120
482
|
return {
|
|
121
483
|
content: 'team_save: scope must be "workspace" or "global"',
|
|
122
484
|
isError: true,
|
|
123
485
|
};
|
|
124
486
|
}
|
|
125
|
-
|
|
487
|
+
if (typeof a.id === "string" && a.id.trim()) {
|
|
488
|
+
const existing = runtime.agentRoster.get(canvasId, a.id.trim());
|
|
489
|
+
if (!existing) {
|
|
490
|
+
return {
|
|
491
|
+
content: missingMemberHint(runtime, canvasId, a.id.trim(), "team_save"),
|
|
492
|
+
isError: true,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
const toolsPatch = a.tools === null
|
|
496
|
+
? null
|
|
497
|
+
: a.tools !== undefined
|
|
498
|
+
? parseMemberToolPolicy(a.tools)
|
|
499
|
+
: undefined;
|
|
500
|
+
if (a.tools !== undefined && a.tools !== null && !toolsPatch) {
|
|
501
|
+
return {
|
|
502
|
+
content: 'team_save: tools needs { mode: "allow"|"deny", names: [...] }',
|
|
503
|
+
isError: true,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const hasName = name.length > 0;
|
|
507
|
+
const hasPlaybook = playbook.length > 0;
|
|
508
|
+
const hasSurface = hasName ||
|
|
509
|
+
hasPlaybook ||
|
|
510
|
+
typeof a.brief === "string" ||
|
|
511
|
+
a.inject === "minimal" ||
|
|
512
|
+
a.inject === "subagent" ||
|
|
513
|
+
a.tools !== undefined;
|
|
514
|
+
if (existing.kind === "advanced") {
|
|
515
|
+
const rosterWs = existing.scope === "global" ? GLOBAL_ROSTER_ID : canvasId;
|
|
516
|
+
const packOpts = {
|
|
517
|
+
rosterWorkspaceId: rosterWs,
|
|
518
|
+
memberId: existing.id,
|
|
519
|
+
productHome: runtime.agentRoster.home,
|
|
520
|
+
};
|
|
521
|
+
let inventory = listMemberPackInventory(packOpts);
|
|
522
|
+
const skillPaths = resolveCwdPaths(runtime, sessionId, a.skills);
|
|
523
|
+
const filePaths = resolveCwdPaths(runtime, sessionId, a.files);
|
|
524
|
+
const envPrep = typeof a.env_prep === "string" && a.env_prep.trim()
|
|
525
|
+
? a.env_prep.trim()
|
|
526
|
+
: undefined;
|
|
527
|
+
if (skillPaths.length > 0 || filePaths.length > 0 || envPrep) {
|
|
528
|
+
let skillsFiles;
|
|
529
|
+
if (skillPaths.length > 0) {
|
|
530
|
+
const bundled = collectPackSkillFiles(skillPaths);
|
|
531
|
+
if (!bundled.ok) {
|
|
532
|
+
return { content: `team_save: ${bundled.error}`, isError: true };
|
|
533
|
+
}
|
|
534
|
+
skillsFiles = bundled.files;
|
|
535
|
+
}
|
|
536
|
+
let workspaceFiles;
|
|
537
|
+
if (filePaths.length > 0) {
|
|
538
|
+
const bundled = collectPackWorkspaceFiles(filePaths);
|
|
539
|
+
if (!bundled.ok) {
|
|
540
|
+
return { content: `team_save: ${bundled.error}`, isError: true };
|
|
541
|
+
}
|
|
542
|
+
workspaceFiles = bundled.files;
|
|
543
|
+
}
|
|
544
|
+
const merged = mergeIntoMemberPack({
|
|
545
|
+
...packOpts,
|
|
546
|
+
...(skillsFiles ? { skillsFiles } : {}),
|
|
547
|
+
...(workspaceFiles ? { workspaceFiles } : {}),
|
|
548
|
+
...(envPrep ? { envPrep } : {}),
|
|
549
|
+
});
|
|
550
|
+
if (!merged.ok) {
|
|
551
|
+
return { content: `team_save: ${merged.error}`, isError: true };
|
|
552
|
+
}
|
|
553
|
+
inventory = merged.inventory;
|
|
554
|
+
}
|
|
555
|
+
let patched = existing;
|
|
556
|
+
if (hasSurface) {
|
|
557
|
+
const next = patchExistingMember(runtime, canvasId, existing, {
|
|
558
|
+
...(hasName ? { name } : {}),
|
|
559
|
+
...(hasPlaybook ? { playbook } : {}),
|
|
560
|
+
...(typeof a.brief === "string" ? { brief: a.brief } : {}),
|
|
561
|
+
...(a.inject === "minimal" || a.inject === "subagent"
|
|
562
|
+
? { inject: a.inject }
|
|
563
|
+
: {}),
|
|
564
|
+
...(a.tools !== undefined ? { tools: toolsPatch ?? null } : {}),
|
|
565
|
+
});
|
|
566
|
+
if (!next) {
|
|
567
|
+
return {
|
|
568
|
+
content: "team_save: name and playbook required",
|
|
569
|
+
isError: true,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
patched = next;
|
|
573
|
+
}
|
|
574
|
+
else if (skillPaths.length === 0 &&
|
|
575
|
+
filePaths.length === 0 &&
|
|
576
|
+
!envPrep) {
|
|
577
|
+
return {
|
|
578
|
+
content: "team_save: pass playbook/brief/inject/tools and/or skills/files/env_prep to revise",
|
|
579
|
+
isError: true,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return {
|
|
583
|
+
content: JSON.stringify({
|
|
584
|
+
workspaceId: patched.scope === "global" ? "global" : canvasId,
|
|
585
|
+
member: {
|
|
586
|
+
...enrichListedMember(runtime, canvasId, patched),
|
|
587
|
+
playbook: patched.playbook,
|
|
588
|
+
},
|
|
589
|
+
kind: "advanced",
|
|
590
|
+
briefcase: MEMBER_PACK_BRIEFCASE,
|
|
591
|
+
...(inventory ? { inventory } : {}),
|
|
592
|
+
next: patched.ready
|
|
593
|
+
? "subagent member_id · team_export to share"
|
|
594
|
+
: "follow envPrep → team_ready → subagent member_id",
|
|
595
|
+
}),
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
// Thin revise (or upgrade path falls through to mint when wantsAdvanced).
|
|
599
|
+
if (!wantsAdvanced) {
|
|
600
|
+
if (!hasSurface) {
|
|
601
|
+
return {
|
|
602
|
+
content: "team_save: pass playbook/brief/inject/tools to revise thin",
|
|
603
|
+
isError: true,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
const patched = patchExistingMember(runtime, canvasId, existing, {
|
|
607
|
+
...(hasName ? { name } : {}),
|
|
608
|
+
...(hasPlaybook ? { playbook } : {}),
|
|
609
|
+
...(typeof a.brief === "string" ? { brief: a.brief } : {}),
|
|
610
|
+
...(a.inject === "minimal" || a.inject === "subagent"
|
|
611
|
+
? { inject: a.inject }
|
|
612
|
+
: {}),
|
|
613
|
+
...(a.tools !== undefined ? { tools: toolsPatch ?? null } : {}),
|
|
614
|
+
});
|
|
615
|
+
if (!patched) {
|
|
616
|
+
return {
|
|
617
|
+
content: "team_save: name and playbook required",
|
|
618
|
+
isError: true,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
content: JSON.stringify({
|
|
623
|
+
workspaceId: patched.scope === "global" ? "global" : canvasId,
|
|
624
|
+
member: enrichListedMember(runtime, canvasId, patched),
|
|
625
|
+
kind: "thin",
|
|
626
|
+
next: "subagent member_id",
|
|
627
|
+
}),
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (wantsAdvanced) {
|
|
632
|
+
const minted = mintAdvancedMember({
|
|
633
|
+
runtime,
|
|
634
|
+
sessionId,
|
|
635
|
+
canvasId,
|
|
636
|
+
scope,
|
|
637
|
+
name,
|
|
638
|
+
playbook,
|
|
639
|
+
roleRaw,
|
|
640
|
+
...(typeof a.brief === "string" ? { brief: a.brief } : {}),
|
|
641
|
+
inject: a.inject,
|
|
642
|
+
tools: a.tools,
|
|
643
|
+
...(typeof a.env_prep === "string" ? { envPrep: a.env_prep } : {}),
|
|
644
|
+
skills: a.skills,
|
|
645
|
+
files: a.files,
|
|
646
|
+
...(typeof a.id === "string" ? { id: a.id } : {}),
|
|
647
|
+
});
|
|
648
|
+
return {
|
|
649
|
+
content: minted.content,
|
|
650
|
+
...(minted.ok ? {} : { isError: true }),
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
const toolsPolicy = parseMemberToolPolicy(a.tools);
|
|
126
654
|
const member = runtime.agentRoster.upsertAtScope(canvasId, scope, {
|
|
127
655
|
name,
|
|
128
656
|
playbook,
|
|
129
657
|
role: isAgentTeamSpawnRole(roleRaw) ? roleRaw : "worker",
|
|
658
|
+
kind: "thin",
|
|
659
|
+
ready: true,
|
|
130
660
|
...(typeof a.brief === "string" ? { brief: a.brief } : {}),
|
|
131
661
|
...(a.inject === "subagent" || a.inject === "minimal"
|
|
132
662
|
? { inject: a.inject }
|
|
133
663
|
: {}),
|
|
134
|
-
...(
|
|
664
|
+
...(toolsPolicy ? { tools: toolsPolicy } : {}),
|
|
135
665
|
...(typeof a.id === "string" ? { id: a.id } : {}),
|
|
136
666
|
});
|
|
137
667
|
if (!member) {
|
|
@@ -141,53 +671,290 @@ export function bindAgentRosterTools(tools, options) {
|
|
|
141
671
|
content: JSON.stringify({
|
|
142
672
|
workspaceId: scope === "global" ? "global" : canvasId,
|
|
143
673
|
member,
|
|
674
|
+
kind: "thin",
|
|
675
|
+
next: "subagent member_id",
|
|
144
676
|
}),
|
|
145
677
|
};
|
|
146
678
|
},
|
|
147
679
|
});
|
|
148
680
|
registerTool(tools, {
|
|
149
681
|
name: "team_import",
|
|
150
|
-
description: "
|
|
151
|
-
"
|
|
152
|
-
"
|
|
682
|
+
description: "Install a friend-shared 干员: card= (thin JSON) or pack= (advanced zip/dir/attachment → 公文包/briefcase). " +
|
|
683
|
+
"Then follow envPrep with this main session → team_ready. Illegal pack: inspect/fix → re-import. " +
|
|
684
|
+
"After import you may team_save id= to edit playbook or merge skills/files. Mint new → team_save (not this).",
|
|
153
685
|
parameters: {
|
|
154
686
|
type: "object",
|
|
155
687
|
properties: {
|
|
156
688
|
card: {
|
|
157
689
|
type: "string",
|
|
158
|
-
description: "JSON
|
|
690
|
+
description: "Thin JSON (kind xrk-agent-member). Omit when pack.",
|
|
691
|
+
},
|
|
692
|
+
pack: {
|
|
693
|
+
type: "string",
|
|
694
|
+
description: "Marked pack zip/dir path (cwd-relative OK) or upload id " +
|
|
695
|
+
"(attachment:sha256:… / sha256:…). Omit when card.",
|
|
159
696
|
},
|
|
160
697
|
scope: {
|
|
161
698
|
type: "string",
|
|
162
|
-
description: "global | workspace
|
|
699
|
+
description: "global | workspace. Defaults: card→workspace; pack→global (shareable).",
|
|
163
700
|
},
|
|
164
701
|
},
|
|
165
|
-
required: ["card"],
|
|
166
702
|
additionalProperties: false,
|
|
167
703
|
},
|
|
168
|
-
presentCall: (args) => (
|
|
169
|
-
card: "generic",
|
|
170
|
-
title: "team_import",
|
|
171
|
-
kind: "execute",
|
|
172
|
-
rawInput: args,
|
|
173
|
-
}),
|
|
704
|
+
presentCall: (args) => teamPresent("team_import", "execute", args),
|
|
174
705
|
execute: async (args) => {
|
|
175
706
|
const a = readArgs(args);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
707
|
+
if (a.from_session === true) {
|
|
708
|
+
return {
|
|
709
|
+
content: "team_import: minting moved to team_save (kind=advanced + name/playbook/skills/files). " +
|
|
710
|
+
"team_import is only card= or pack= install.",
|
|
711
|
+
isError: true,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
const hasCard = typeof a.card === "string" && a.card.trim().length > 0;
|
|
715
|
+
const hasPack = typeof a.pack === "string" && a.pack.trim().length > 0;
|
|
716
|
+
if (hasCard === hasPack) {
|
|
717
|
+
return {
|
|
718
|
+
content: "team_import: set exactly one of card (JSON) or pack (path|attachment). " +
|
|
719
|
+
"Mint new → team_save.",
|
|
720
|
+
isError: true,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
const scope = parseRosterScope(a.scope ?? (hasCard ? "workspace" : "global"));
|
|
180
724
|
if (scope === undefined) {
|
|
181
725
|
return { content: 'team_import: scope must be "workspace" or "global"', isError: true };
|
|
182
726
|
}
|
|
183
727
|
const canvasId = ws();
|
|
184
|
-
const
|
|
728
|
+
const rosterWs = scope === "global" ? GLOBAL_ROSTER_ID : canvasId;
|
|
729
|
+
const visibility = rosterScopeVisibility(scope, rosterWs);
|
|
730
|
+
if (hasCard) {
|
|
731
|
+
const parsed = parseAgentMemberCard(a.card);
|
|
732
|
+
if (!parsed.ok)
|
|
733
|
+
return { content: `team_import: ${parsed.error}`, isError: true };
|
|
734
|
+
const member = runtime.agentRoster.upsertAtScope(canvasId, scope, {
|
|
735
|
+
...parsed.card,
|
|
736
|
+
kind: "thin",
|
|
737
|
+
ready: true,
|
|
738
|
+
});
|
|
739
|
+
if (!member)
|
|
740
|
+
return { content: "team_import: name and playbook required", isError: true };
|
|
741
|
+
return {
|
|
742
|
+
content: JSON.stringify({
|
|
743
|
+
workspaceId: rosterWs,
|
|
744
|
+
scope,
|
|
745
|
+
visibility,
|
|
746
|
+
member,
|
|
747
|
+
source: "card",
|
|
748
|
+
next: "subagent member_id",
|
|
749
|
+
}),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
const packLabel = String(a.pack);
|
|
753
|
+
const loaded = await loadPackFromArg(runtime, sessionId, packLabel);
|
|
754
|
+
if (!loaded.ok)
|
|
755
|
+
return { content: `team_import: ${loaded.error}`, isError: true };
|
|
756
|
+
const member = runtime.agentRoster.upsertAtScope(canvasId, scope, {
|
|
757
|
+
...loaded.pack.card,
|
|
758
|
+
kind: "advanced",
|
|
759
|
+
ready: false,
|
|
760
|
+
});
|
|
185
761
|
if (!member)
|
|
186
762
|
return { content: "team_import: name and playbook required", isError: true };
|
|
763
|
+
const placed = materializeAgentMemberPack(loaded.pack, {
|
|
764
|
+
rosterWorkspaceId: rosterWs,
|
|
765
|
+
memberId: member.id,
|
|
766
|
+
productHome: runtime.agentRoster.home,
|
|
767
|
+
});
|
|
187
768
|
return {
|
|
188
769
|
content: JSON.stringify({
|
|
189
|
-
workspaceId:
|
|
770
|
+
workspaceId: rosterWs,
|
|
771
|
+
scope,
|
|
772
|
+
visibility,
|
|
190
773
|
member,
|
|
774
|
+
source: "pack",
|
|
775
|
+
pack: packLabel,
|
|
776
|
+
packDir: placed.packDir,
|
|
777
|
+
skillNames: placed.skillNames,
|
|
778
|
+
ready: false,
|
|
779
|
+
...(placed.envPrep
|
|
780
|
+
? {
|
|
781
|
+
envPrep: placed.envPrep,
|
|
782
|
+
envHint: "Review envPrep here (or hand zip to a friend). Then team_ready. Do not auto-run scripts.",
|
|
783
|
+
}
|
|
784
|
+
: {
|
|
785
|
+
envHint: "No env/SETUP.md — team_ready when configured; friend gets playbook (± skills).",
|
|
786
|
+
}),
|
|
787
|
+
next: "team_ready → subagent member_id",
|
|
788
|
+
layout: AGENT_MEMBER_PACK_LAYOUT,
|
|
789
|
+
pathHint: `Spawn keeps parent workspace cwd. ${MEMBER_PACK_BRIEFCASE} = pack_home (injected) — child may read/edit/copy into workspace. Share zip with relative paths under the pack tree.`,
|
|
790
|
+
shareHint: "Friend: team_export zip / UI import, or they team_import pack=… → follow envPrep with main session → team_ready. " +
|
|
791
|
+
"scope=global keeps it on every canvas team_list.",
|
|
792
|
+
}),
|
|
793
|
+
};
|
|
794
|
+
},
|
|
795
|
+
});
|
|
796
|
+
registerTool(tools, {
|
|
797
|
+
name: "team_ready",
|
|
798
|
+
description: "Mark an advanced 干员 ready after env prep. " +
|
|
799
|
+
"Triggers: team_list shows not-ready; UI zip import; user says 帮我就绪 / mark ready / prep done. " +
|
|
800
|
+
"Flow: team_list → read envPrep → help install if needed → team_ready id=. " +
|
|
801
|
+
"Thin members stay ready. Required before subagent member_id on advanced.",
|
|
802
|
+
parameters: {
|
|
803
|
+
type: "object",
|
|
804
|
+
properties: {
|
|
805
|
+
id: { type: "string", description: "mem_… from team_list (not-ready advanced)" },
|
|
806
|
+
ready: {
|
|
807
|
+
type: "boolean",
|
|
808
|
+
description: "Default true. Pass false to mark not-ready again.",
|
|
809
|
+
},
|
|
810
|
+
},
|
|
811
|
+
required: ["id"],
|
|
812
|
+
additionalProperties: false,
|
|
813
|
+
},
|
|
814
|
+
presentCall: (args) => teamPresent("team_ready", "execute", args),
|
|
815
|
+
execute: async (args) => {
|
|
816
|
+
const a = readArgs(args);
|
|
817
|
+
const id = String(a.id ?? "").trim();
|
|
818
|
+
if (!id)
|
|
819
|
+
return { content: "team_ready: id required", isError: true };
|
|
820
|
+
const canvasId = ws();
|
|
821
|
+
const updated = runtime.agentRoster.setReady(canvasId, id, a.ready === false ? false : true);
|
|
822
|
+
if (!updated) {
|
|
823
|
+
return { content: missingMemberHint(runtime, canvasId, id, "team_ready"), isError: true };
|
|
824
|
+
}
|
|
825
|
+
const listed = runtime.agentRoster.get(canvasId, updated.id);
|
|
826
|
+
return {
|
|
827
|
+
content: JSON.stringify({
|
|
828
|
+
member: listed ? enrichListedMember(runtime, canvasId, listed) : updated,
|
|
829
|
+
next: updated.ready ? "subagent member_id" : "configure then team_ready again",
|
|
830
|
+
}),
|
|
831
|
+
};
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
registerTool(tools, {
|
|
835
|
+
name: "team_export",
|
|
836
|
+
description: "Export a custom 干员 for the user: thin→JSON, advanced→zip (公文包/briefcase: playbook+skills+references+workspace+env). " +
|
|
837
|
+
"Writes to dir= (default session cwd). Catalog mem_seed_* cannot export — mint a copy first. " +
|
|
838
|
+
"User said 导出/分享这个干员 / export member. UI Export is equivalent.",
|
|
839
|
+
parameters: {
|
|
840
|
+
type: "object",
|
|
841
|
+
properties: {
|
|
842
|
+
id: { type: "string", description: "mem_… from team_list (not catalog)" },
|
|
843
|
+
dir: {
|
|
844
|
+
type: "string",
|
|
845
|
+
description: "Output directory (cwd-relative or absolute). Default: session cwd.",
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
required: ["id"],
|
|
849
|
+
additionalProperties: false,
|
|
850
|
+
},
|
|
851
|
+
presentCall: (args) => teamPresent("team_export", "execute", args),
|
|
852
|
+
execute: async (args) => {
|
|
853
|
+
const a = readArgs(args);
|
|
854
|
+
const id = String(a.id ?? "").trim();
|
|
855
|
+
if (!id)
|
|
856
|
+
return { content: "team_export: id required", isError: true };
|
|
857
|
+
const exported = await dispatchFaceMethod(runtime, "team.export", `te-${Date.now()}`, {
|
|
858
|
+
sessionId,
|
|
859
|
+
id,
|
|
860
|
+
});
|
|
861
|
+
if (!exported.result.ok) {
|
|
862
|
+
const msg = typeof exported.result.error === "object" &&
|
|
863
|
+
exported.result.error &&
|
|
864
|
+
"message" in exported.result.error
|
|
865
|
+
? String(exported.result.error.message)
|
|
866
|
+
: "export failed";
|
|
867
|
+
return { content: `team_export: ${msg}`, isError: true };
|
|
868
|
+
}
|
|
869
|
+
const value = exported.result.value;
|
|
870
|
+
const cwd = resolveSessionCwd(runtime, sessionId);
|
|
871
|
+
const dirRaw = typeof a.dir === "string" ? a.dir.trim() : "";
|
|
872
|
+
const outDir = dirRaw
|
|
873
|
+
? path.isAbsolute(dirRaw)
|
|
874
|
+
? dirRaw
|
|
875
|
+
: path.resolve(cwd, dirRaw)
|
|
876
|
+
: cwd;
|
|
877
|
+
const outPath = path.join(outDir, value.fileName);
|
|
878
|
+
try {
|
|
879
|
+
if (value.format === "json" && typeof value.text === "string") {
|
|
880
|
+
writeFileSync(outPath, value.text, "utf8");
|
|
881
|
+
}
|
|
882
|
+
else if (value.format === "zip" && typeof value.base64 === "string") {
|
|
883
|
+
writeFileSync(outPath, Buffer.from(value.base64, "base64"));
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
return { content: "team_export: empty payload", isError: true };
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
catch (err) {
|
|
890
|
+
return {
|
|
891
|
+
content: `team_export: write failed — ${err instanceof Error ? err.message : String(err)}`,
|
|
892
|
+
isError: true,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
return {
|
|
896
|
+
content: JSON.stringify({
|
|
897
|
+
format: value.format,
|
|
898
|
+
path: outPath,
|
|
899
|
+
fileName: value.fileName,
|
|
900
|
+
...(value.packIncomplete
|
|
901
|
+
? { packIncomplete: true, hint: value.hint }
|
|
902
|
+
: {}),
|
|
903
|
+
next: value.format === "zip"
|
|
904
|
+
? "Hand zip → team_import pack= → follow envPrep → team_ready"
|
|
905
|
+
: "Hand JSON → team_import card= or UI import",
|
|
906
|
+
}),
|
|
907
|
+
};
|
|
908
|
+
},
|
|
909
|
+
});
|
|
910
|
+
registerTool(tools, {
|
|
911
|
+
name: "team_remove",
|
|
912
|
+
description: "Uninstall a custom 干员 (not catalog mem_seed_*). Like deleting a skill. " +
|
|
913
|
+
"Pass id from team_list; scope global|workspace must match where it lives.",
|
|
914
|
+
parameters: {
|
|
915
|
+
type: "object",
|
|
916
|
+
properties: {
|
|
917
|
+
id: { type: "string", description: "mem_… to delete" },
|
|
918
|
+
scope: {
|
|
919
|
+
type: "string",
|
|
920
|
+
description: "global | workspace — must match team_list scope for that id",
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
required: ["id", "scope"],
|
|
924
|
+
additionalProperties: false,
|
|
925
|
+
},
|
|
926
|
+
presentCall: (args) => teamPresent("team_remove", "execute", args),
|
|
927
|
+
execute: async (args) => {
|
|
928
|
+
const a = readArgs(args);
|
|
929
|
+
const id = String(a.id ?? "").trim();
|
|
930
|
+
if (!id)
|
|
931
|
+
return { content: "team_remove: id required", isError: true };
|
|
932
|
+
const catalogProblem = catalogMemberWriteProblem(id);
|
|
933
|
+
if (catalogProblem) {
|
|
934
|
+
return { content: `team_remove: ${catalogProblem}`, isError: true };
|
|
935
|
+
}
|
|
936
|
+
const scope = parseRosterScope(a.scope);
|
|
937
|
+
if (scope === undefined) {
|
|
938
|
+
return {
|
|
939
|
+
content: 'team_remove: scope must be "workspace" or "global"',
|
|
940
|
+
isError: true,
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
const canvasId = ws();
|
|
944
|
+
const rosterWs = scope === "global" ? GLOBAL_ROSTER_ID : canvasId;
|
|
945
|
+
const ok = runtime.agentRoster.remove(rosterWs, id);
|
|
946
|
+
if (!ok) {
|
|
947
|
+
return {
|
|
948
|
+
content: missingMemberHint(runtime, canvasId, id, "team_remove"),
|
|
949
|
+
isError: true,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
return {
|
|
953
|
+
content: JSON.stringify({
|
|
954
|
+
workspaceId: rosterWs,
|
|
955
|
+
deleted: true,
|
|
956
|
+
id,
|
|
957
|
+
next: "gone from team_list; zip/JSON copies elsewhere are untouched",
|
|
191
958
|
}),
|
|
192
959
|
};
|
|
193
960
|
},
|