@stofone/dsh-agent-roster 0.13.1 → 1.1.0
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 +58 -0
- package/README.md +16 -10
- package/README.zh.md +27 -9
- package/bin/cli.mjs +87 -8
- package/client.js +126 -24
- package/docs/roster-v2.md +1 -1
- package/examples/space.yaml +11 -0
- package/examples-orchestration/space.yaml +10 -0
- package/index.js +30 -1
- package/lib/clone.js +318 -0
- package/lib/library.js +3 -3
- package/lib/registry.js +30 -30
- package/lib/render.js +6 -6
- package/lib/service.js +47 -0
- package/lib/space.js +382 -0
- package/lib/tools.js +3 -3
- package/locale/en.json +7 -2
- package/locale/zh.json +7 -2
- package/orgs/agent-roster-collective/space.yaml +30 -0
- package/package.json +1 -1
- package/schema/space.v1.schema.json +60 -0
- package/lib/organization.js +0 -331
- package/orgs/agent-roster-collective/organization.yaml +0 -118
- package/schema/organization.v1.schema.json +0 -50
package/lib/organization.js
DELETED
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 组织档案(organization):一座档案库的"目录页"。
|
|
3
|
-
*
|
|
4
|
-
* 为什么需要它:档案库只能回答"这一份档案写了什么",回答不了"这些档案是什么关系、
|
|
5
|
-
* 哪份是生产、哪份是归档、谁维护"。当一座库里放二十份来自不同来源的档案时,
|
|
6
|
-
* 缺了这层就只能靠目录名猜。
|
|
7
|
-
*
|
|
8
|
-
* 边界(与 capabilities.yaml 同一条纪律):
|
|
9
|
-
* - 组织只做**声明与核对**:声明库里有哪几份档案、各自什么状态;
|
|
10
|
-
* 它不创建团队、不改档案、不进 `agent_teams_add_member`。
|
|
11
|
-
* - 它**能被机械核对**:声明了但目录不在 = error,目录在却没声明 = warn,
|
|
12
|
-
* 已归档却仍在用 = info。核对不了的字段一律不收(未知键即错误)。
|
|
13
|
-
* - 位置即身份:`organization` 必须等于所在目录名,和 `roster` 的规则一致。
|
|
14
|
-
*
|
|
15
|
-
* @module dsh-agent-roster/organization
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/** 组织文件的 `kind`。与 roster / registry / capabilities 并列,互不冒充。 */
|
|
19
|
-
export const ORGANIZATION_KIND = "dsh-agent-roster-organization/v1";
|
|
20
|
-
|
|
21
|
-
/** 组织文件允许的文件名,按优先级排列。 */
|
|
22
|
-
export const ORGANIZATION_FILE_NAMES = ["organization.yaml", "organization.yml", "organization.json"];
|
|
23
|
-
|
|
24
|
-
/** 档案在组织里的状态。刻意只有两档:归档不等于删除,删除就是没有这个目录。 */
|
|
25
|
-
export const ORGANIZATION_STATUSES = ["active", "archived"];
|
|
26
|
-
|
|
27
|
-
/** 长度上限:与档案的 LIMITS 同一套口径,超了就报错而不是截断。 */
|
|
28
|
-
export const LIMITS = {
|
|
29
|
-
rosters: 64,
|
|
30
|
-
titleChars: 120,
|
|
31
|
-
descriptionChars: 500,
|
|
32
|
-
licenseChars: 120,
|
|
33
|
-
homepageChars: 200,
|
|
34
|
-
maintainers: 16,
|
|
35
|
-
maintainerChars: 120,
|
|
36
|
-
noteChars: 300,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/** 组织 id 与档案 id 同形:小写、连字符、首字符必须是字母或数字。 */
|
|
40
|
-
export const ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
|
|
41
|
-
|
|
42
|
-
const ROOT_FIELDS = new Set([
|
|
43
|
-
"kind",
|
|
44
|
-
"organization",
|
|
45
|
-
"title",
|
|
46
|
-
"description",
|
|
47
|
-
"license",
|
|
48
|
-
"homepage",
|
|
49
|
-
"maintainers",
|
|
50
|
-
"rosters",
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
const ROSTER_FIELDS = new Set(["id", "title", "status", "origin", "note"]);
|
|
54
|
-
|
|
55
|
-
/** 顶层字段清单(供 schema 对齐测试反向钉住,避免两处各写一份)。 */
|
|
56
|
-
export const ORGANIZATION_FIELDS = Object.freeze([...ROOT_FIELDS]);
|
|
57
|
-
|
|
58
|
-
/** 每条登记项的字段清单。 */
|
|
59
|
-
export const ORGANIZATION_ROSTER_FIELDS = Object.freeze([...ROSTER_FIELDS]);
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 一条诊断。形状与 `lib/validate.js` 保持一致,好让 render / panel / CLI 用同一套渲染。
|
|
63
|
-
*
|
|
64
|
-
* @param {"error"|"warn"|"info"} severity
|
|
65
|
-
* @param {string} code
|
|
66
|
-
* @param {string} file
|
|
67
|
-
* @param {string} reason
|
|
68
|
-
* @returns {{severity: string, code: string, path: string, file: string, reason: string}}
|
|
69
|
-
*/
|
|
70
|
-
function diagnose(severity, code, file, reason) {
|
|
71
|
-
return { severity, code, path: "", file, reason };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 校验一份组织档案。
|
|
76
|
-
*
|
|
77
|
-
* 与 roster 的校验同一条纪律:**能核对的才收**。`homepage` 只校验是不是 http(s),
|
|
78
|
-
* 不联网;`maintainers` 只校验形态,不校验这个人存在不存在。
|
|
79
|
-
*
|
|
80
|
-
* @param {unknown} value 解析后的 JSON/YAML 值
|
|
81
|
-
* @param {{file?: string, expectedId?: string|null}} [options]
|
|
82
|
-
* @returns {{ok: boolean, organization: object|null, diagnostics: object[]}}
|
|
83
|
-
*/
|
|
84
|
-
export function validateOrganization(value, options = {}) {
|
|
85
|
-
const file = options.file ?? ORGANIZATION_FILE_NAMES[0];
|
|
86
|
-
const expectedId = options.expectedId ?? null;
|
|
87
|
-
/** @type {object[]} */
|
|
88
|
-
const diagnostics = [];
|
|
89
|
-
|
|
90
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
91
|
-
diagnostics.push(diagnose("error", "organization-not-object", file, "组织文件必须是一个对象(键值对)。"));
|
|
92
|
-
return { ok: false, organization: null, diagnostics };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for (const key of Object.keys(value)) {
|
|
96
|
-
if (!ROOT_FIELDS.has(key)) {
|
|
97
|
-
diagnostics.push(
|
|
98
|
-
diagnose(
|
|
99
|
-
"error",
|
|
100
|
-
"unknown-field",
|
|
101
|
-
file,
|
|
102
|
-
`未知字段 "${key}"。组织文件只收 ${[...ROOT_FIELDS].join(" / ")}:` +
|
|
103
|
-
"核对不了的字段不收——写了也不会被任何人验证,只会被误当成事实。",
|
|
104
|
-
),
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (value.kind !== ORGANIZATION_KIND) {
|
|
110
|
-
diagnostics.push(
|
|
111
|
-
diagnose("error", "organization-kind", file, `kind 必须是 "${ORGANIZATION_KIND}",实际是 ${JSON.stringify(value.kind)}。`),
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const id = value.organization;
|
|
116
|
-
if (typeof id !== "string" || !ID_PATTERN.test(id)) {
|
|
117
|
-
diagnostics.push(
|
|
118
|
-
diagnose("error", "organization-id", file, "organization 必须是 2–64 位小写字母/数字/连字符,且以字母或数字开头。"),
|
|
119
|
-
);
|
|
120
|
-
} else if (expectedId !== null && id !== expectedId) {
|
|
121
|
-
diagnostics.push(
|
|
122
|
-
diagnose(
|
|
123
|
-
"error",
|
|
124
|
-
"organization-id-mismatch",
|
|
125
|
-
file,
|
|
126
|
-
`organization 是 "${id}",但所在目录叫 "${expectedId}"。位置即身份:组织 id 必须等于目录名。`,
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const title = optionalString(value, "title", LIMITS.titleChars, diagnostics, file, "organization-title");
|
|
132
|
-
optionalString(value, "description", LIMITS.descriptionChars, diagnostics, file, "organization-description");
|
|
133
|
-
optionalString(value, "license", LIMITS.licenseChars, diagnostics, file, "organization-license");
|
|
134
|
-
|
|
135
|
-
if (value.homepage !== undefined) {
|
|
136
|
-
const homepage = optionalString(value, "homepage", LIMITS.homepageChars, diagnostics, file, "organization-homepage");
|
|
137
|
-
if (typeof homepage === "string" && !/^https?:\/\/\S+$/i.test(homepage)) {
|
|
138
|
-
diagnostics.push(
|
|
139
|
-
diagnose("error", "organization-homepage", file, "homepage 只收 http(s) 地址:别的写法既打不开也核对不了。"),
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (value.maintainers !== undefined) {
|
|
145
|
-
if (!Array.isArray(value.maintainers)) {
|
|
146
|
-
diagnostics.push(diagnose("error", "organization-maintainers", file, "maintainers 必须是字符串数组。"));
|
|
147
|
-
} else if (value.maintainers.length > LIMITS.maintainers) {
|
|
148
|
-
diagnostics.push(
|
|
149
|
-
diagnose("error", "organization-maintainers", file, `maintainers 最多 ${LIMITS.maintainers} 项。`),
|
|
150
|
-
);
|
|
151
|
-
} else {
|
|
152
|
-
for (const who of value.maintainers) {
|
|
153
|
-
if (typeof who !== "string" || who.length === 0 || who.length > LIMITS.maintainerChars) {
|
|
154
|
-
diagnostics.push(
|
|
155
|
-
diagnose("error", "organization-maintainer", file, `维护者名字必须是 1–${LIMITS.maintainerChars} 个字符的字符串。`),
|
|
156
|
-
);
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const rosters = [];
|
|
164
|
-
const rawRosters = value.rosters;
|
|
165
|
-
if (!Array.isArray(rawRosters) || rawRosters.length === 0) {
|
|
166
|
-
diagnostics.push(diagnose("error", "organization-rosters", file, "rosters 必须是非空数组:组织的意义就是登记这些档案。"));
|
|
167
|
-
} else if (rawRosters.length > LIMITS.rosters) {
|
|
168
|
-
diagnostics.push(diagnose("error", "organization-rosters", file, `rosters 最多 ${LIMITS.rosters} 项。`));
|
|
169
|
-
} else {
|
|
170
|
-
const seen = new Set();
|
|
171
|
-
rawRosters.forEach((item, index) => {
|
|
172
|
-
const where = `rosters[${index}]`;
|
|
173
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
174
|
-
diagnostics.push(diagnose("error", "organization-roster-not-object", file, `${where} 必须是对象。`));
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
for (const key of Object.keys(item)) {
|
|
178
|
-
if (!ROSTER_FIELDS.has(key)) {
|
|
179
|
-
diagnostics.push(
|
|
180
|
-
diagnose("error", "unknown-field", file, `${where} 的未知字段 "${key}"(只收 ${[...ROSTER_FIELDS].join(" / ")})。`),
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (typeof item.id !== "string" || !ID_PATTERN.test(item.id)) {
|
|
185
|
-
diagnostics.push(diagnose("error", "organization-roster-id", file, `${where}.id 必须是合法档案 id。`));
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (seen.has(item.id)) {
|
|
189
|
-
diagnostics.push(
|
|
190
|
-
diagnose("error", "organization-duplicate-roster", file, `档案 "${item.id}" 在组织里登记了两次。`),
|
|
191
|
-
);
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
seen.add(item.id);
|
|
195
|
-
const status = item.status ?? "active";
|
|
196
|
-
if (!ORGANIZATION_STATUSES.includes(status)) {
|
|
197
|
-
diagnostics.push(
|
|
198
|
-
diagnose("error", "organization-roster-status", file, `${where}.status 只能是 ${ORGANIZATION_STATUSES.join(" / ")}。`),
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
const entryTitle = optionalString(item, "title", LIMITS.titleChars, diagnostics, file, "organization-roster-title", where);
|
|
202
|
-
const origin = optionalString(item, "origin", LIMITS.titleChars, diagnostics, file, "organization-roster-origin", where);
|
|
203
|
-
const note = optionalString(item, "note", LIMITS.noteChars, diagnostics, file, "organization-roster-note", where);
|
|
204
|
-
rosters.push({
|
|
205
|
-
id: item.id,
|
|
206
|
-
title: entryTitle ?? "",
|
|
207
|
-
status,
|
|
208
|
-
origin: origin ?? "",
|
|
209
|
-
note: note ?? "",
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (diagnostics.some((item) => item.severity === "error")) {
|
|
215
|
-
return { ok: false, organization: null, diagnostics };
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
ok: true,
|
|
220
|
-
organization: {
|
|
221
|
-
kind: ORGANIZATION_KIND,
|
|
222
|
-
id,
|
|
223
|
-
title: title ?? "",
|
|
224
|
-
description: value.description ?? "",
|
|
225
|
-
license: value.license ?? "",
|
|
226
|
-
homepage: value.homepage ?? "",
|
|
227
|
-
maintainers: Array.isArray(value.maintainers) ? [...value.maintainers] : [],
|
|
228
|
-
rosters,
|
|
229
|
-
},
|
|
230
|
-
diagnostics,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* 读一个可选字符串字段并钉住上限。
|
|
236
|
-
*
|
|
237
|
-
* @param {object} source
|
|
238
|
-
* @param {string} key
|
|
239
|
-
* @param {number} max
|
|
240
|
-
* @param {object[]} diagnostics
|
|
241
|
-
* @param {string} file
|
|
242
|
-
* @param {string} code
|
|
243
|
-
* @param {string} [where]
|
|
244
|
-
* @returns {string|undefined}
|
|
245
|
-
*/
|
|
246
|
-
function optionalString(source, key, max, diagnostics, file, code, where = "") {
|
|
247
|
-
const value = source[key];
|
|
248
|
-
if (value === undefined) return undefined;
|
|
249
|
-
const label = where === "" ? key : `${where}.${key}`;
|
|
250
|
-
if (typeof value !== "string" || value.length > max) {
|
|
251
|
-
diagnostics.push(diagnose("error", code, file, `${label} 必须是长度不超过 ${max} 的字符串。`));
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
|
-
return value;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* 组织声明与磁盘实际情况的交叉核对。
|
|
259
|
-
*
|
|
260
|
-
* 三件事分开说,因为它们的严重度不同:
|
|
261
|
-
* - **缺**(声明了却找不到目录):error。组织页在撒谎,比没有组织页更糟。
|
|
262
|
-
* - **多**(目录在却没登记):warn。不是错误,但意味着有人绕过组织加了档案。
|
|
263
|
-
* - **归档仍在**:info。归档是声明,不是删除;提醒一句即可。
|
|
264
|
-
*
|
|
265
|
-
* @param {object|null} organization
|
|
266
|
-
* @param {string[]} rosterIds 磁盘上真正通过校验的档案 id
|
|
267
|
-
* @returns {{declared: number, present: number, missing: string[], undeclared: string[], archived: string[], status: string}}
|
|
268
|
-
*/
|
|
269
|
-
export function checkOrganization(organization, rosterIds) {
|
|
270
|
-
const onDisk = new Set(rosterIds);
|
|
271
|
-
const declared = new Set(organization === null ? [] : organization.rosters.map((item) => item.id));
|
|
272
|
-
const missing = [...declared].filter((id) => !onDisk.has(id)).sort();
|
|
273
|
-
const undeclared = [...onDisk].filter((id) => !declared.has(id)).sort();
|
|
274
|
-
const archived = organization === null
|
|
275
|
-
? []
|
|
276
|
-
: organization.rosters.filter((item) => item.status === "archived" && onDisk.has(item.id)).map((item) => item.id).sort();
|
|
277
|
-
return {
|
|
278
|
-
declared: declared.size,
|
|
279
|
-
present: onDisk.size,
|
|
280
|
-
missing,
|
|
281
|
-
undeclared,
|
|
282
|
-
archived,
|
|
283
|
-
status: organization === null ? "absent" : missing.length > 0 ? "incomplete" : "ok",
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* 把交叉核对结果折成诊断。
|
|
289
|
-
*
|
|
290
|
-
* @param {object|null} organization
|
|
291
|
-
* @param {object} check {@link checkOrganization} 的返回值
|
|
292
|
-
* @param {string} file 组织文件相对路径
|
|
293
|
-
* @returns {object[]}
|
|
294
|
-
*/
|
|
295
|
-
export function organizationDiagnostics(organization, check, file) {
|
|
296
|
-
const diagnostics = [];
|
|
297
|
-
if (organization === null) return diagnostics;
|
|
298
|
-
if (check.missing.length > 0) {
|
|
299
|
-
diagnostics.push(
|
|
300
|
-
diagnose(
|
|
301
|
-
"error",
|
|
302
|
-
"organization-roster-missing",
|
|
303
|
-
file,
|
|
304
|
-
`组织声明了 ${check.missing.length} 份档案但库里找不到:${check.missing.join(", ")}。` +
|
|
305
|
-
"要么把目录加回来,要么把它们从 organization 里删掉——组织页不能比磁盘更乐观。",
|
|
306
|
-
),
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
if (check.undeclared.length > 0) {
|
|
310
|
-
diagnostics.push(
|
|
311
|
-
diagnose(
|
|
312
|
-
"warn",
|
|
313
|
-
"organization-roster-undeclared",
|
|
314
|
-
file,
|
|
315
|
-
`库里有 ${check.undeclared.length} 份档案没在组织里登记:${check.undeclared.join(", ")}。` +
|
|
316
|
-
"它们仍会被加载,但组织页看不到它们——补一行登记,或确认它们不该在这里。",
|
|
317
|
-
),
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
if (check.archived.length > 0) {
|
|
321
|
-
diagnostics.push(
|
|
322
|
-
diagnose(
|
|
323
|
-
"info",
|
|
324
|
-
"organization-roster-archived",
|
|
325
|
-
file,
|
|
326
|
-
`已归档但仍在库里的档案:${check.archived.join(", ")}。归档只是声明,不影响加载。`,
|
|
327
|
-
),
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
return diagnostics;
|
|
331
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# 组织档案:一座档案库的目录页。
|
|
2
|
-
#
|
|
3
|
-
# 它只做声明与核对:声明库里有哪几份档案、各自什么状态;
|
|
4
|
-
# 不创建团队、不改档案、不进 agent_teams_add_member 参数。
|
|
5
|
-
# 声明了却找不到目录 = error;目录在却没登记 = warn;已归档仍在 = info。
|
|
6
|
-
kind: dsh-agent-roster-organization/v1
|
|
7
|
-
organization: agent-roster-collective
|
|
8
|
-
title: 团队档案合集
|
|
9
|
-
description: 二十份可直接建队的档案:从代码审查到事故响应,覆盖常见的协作分工。
|
|
10
|
-
license: MIT
|
|
11
|
-
maintainers:
|
|
12
|
-
- STofone
|
|
13
|
-
rosters:
|
|
14
|
-
- id: api-design
|
|
15
|
-
title: API 设计评审组
|
|
16
|
-
status: active
|
|
17
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
18
|
-
note: 3 名成员:接口负责人、结构与演进评审、失败语义评审
|
|
19
|
-
- id: incident-response
|
|
20
|
-
title: 事故响应组
|
|
21
|
-
status: active
|
|
22
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
23
|
-
note: 3 名成员:事故指挥、定位、记录
|
|
24
|
-
- id: data-quality
|
|
25
|
-
title: 数据质量组
|
|
26
|
-
status: active
|
|
27
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
28
|
-
note: 3 名成员:数据负责人、画像、判据编写
|
|
29
|
-
- id: db-migration
|
|
30
|
-
title: 数据库迁移组
|
|
31
|
-
status: active
|
|
32
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
33
|
-
note: 3 名成员:迁移负责人、迁移实现、数据验证
|
|
34
|
-
- id: i18n-l10n
|
|
35
|
-
title: 本地化组
|
|
36
|
-
status: active
|
|
37
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
38
|
-
note: 3 名成员:本地化负责人、翻译、双语审校
|
|
39
|
-
- id: security-audit
|
|
40
|
-
title: 安全审计组
|
|
41
|
-
status: active
|
|
42
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
43
|
-
note: 3 名成员:审计负责人、代码审计、威胁建模
|
|
44
|
-
- id: performance
|
|
45
|
-
title: 性能优化组
|
|
46
|
-
status: active
|
|
47
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
48
|
-
note: 3 名成员:性能负责人、热点定位、压测与回归
|
|
49
|
-
- id: frontend-ui
|
|
50
|
-
title: 前端实现组
|
|
51
|
-
status: active
|
|
52
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
53
|
-
note: 3 名成员:前端负责人、实现、可访问性审查
|
|
54
|
-
- id: docs-writer
|
|
55
|
-
title: 文档组
|
|
56
|
-
status: active
|
|
57
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
58
|
-
note: 3 名成员:文档负责人、正文撰写、示例验证
|
|
59
|
-
- id: release-manager
|
|
60
|
-
title: 发布管理组
|
|
61
|
-
status: active
|
|
62
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
63
|
-
note: 3 名成员:发布负责人、变更说明、回滚预案
|
|
64
|
-
- id: support-triage
|
|
65
|
-
title: 客服分诊组
|
|
66
|
-
status: active
|
|
67
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
68
|
-
note: 3 名成员:分诊负责人、来信分诊、回复撰写
|
|
69
|
-
- id: recruiting
|
|
70
|
-
title: 招聘筛选组
|
|
71
|
-
status: active
|
|
72
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
73
|
-
note: 3 名成员:招聘负责人、简历筛选、面试安排
|
|
74
|
-
- id: market-research
|
|
75
|
-
title: 市场研究组
|
|
76
|
-
status: active
|
|
77
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
78
|
-
note: 3 名成员:研究负责人、资料检索、综合研判
|
|
79
|
-
- id: finance-recon
|
|
80
|
-
title: 财务对账组
|
|
81
|
-
status: active
|
|
82
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
83
|
-
note: 3 名成员:对账负责人、逐笔匹配、差异归因
|
|
84
|
-
- id: contract-review
|
|
85
|
-
title: 合同审阅组
|
|
86
|
-
status: active
|
|
87
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
88
|
-
note: 3 名成员:审阅负责人、条款识别、风险提示
|
|
89
|
-
- id: content-ops
|
|
90
|
-
title: 内容运营组
|
|
91
|
-
status: active
|
|
92
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
93
|
-
note: 3 名成员:内容负责人、成稿编辑、分发与回读
|
|
94
|
-
- id: qa-regression
|
|
95
|
-
title: 回归测试组
|
|
96
|
-
status: active
|
|
97
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
98
|
-
note: 3 名成员:测试负责人、用例编写、不稳定排查
|
|
99
|
-
- id: ml-eval
|
|
100
|
-
title: 模型评测组
|
|
101
|
-
status: active
|
|
102
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
103
|
-
note: 3 名成员:评测负责人、数据集构建、指标与统计
|
|
104
|
-
- id: sre-oncall
|
|
105
|
-
title: SRE 值守组
|
|
106
|
-
status: active
|
|
107
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
108
|
-
note: 3 名成员:值守负责人、手册编写、容量与成本
|
|
109
|
-
- id: literature-review
|
|
110
|
-
title: 文献综述组
|
|
111
|
-
status: active
|
|
112
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
113
|
-
note: 3 名成员:综述负责人、信息提取、质量评价
|
|
114
|
-
- id: product-discovery
|
|
115
|
-
title: 产品发现组
|
|
116
|
-
status: active
|
|
117
|
-
origin: 本项目自撰(结构参考公开的多 Agent 团队实践)
|
|
118
|
-
note: 3 名成员:发现负责人、用户访谈、反方
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://github.com/dsh-agent-roster/dsh-agent-roster/blob/main/schema/organization.v1.schema.json",
|
|
4
|
-
"title": "DSH agent roster organization document",
|
|
5
|
-
"description": "一座档案库的目录页:声明库里有哪几份档案、各自什么状态。校验的权威实现是 lib/organization.js;本文件供编辑器与外部工具提示,两者由 test/organization.test.js 对齐。",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"additionalProperties": false,
|
|
8
|
-
"required": ["kind", "organization", "rosters"],
|
|
9
|
-
"properties": {
|
|
10
|
-
"$schema": {
|
|
11
|
-
"type": "string",
|
|
12
|
-
"description": "编辑器用的 schema 地址,插件不读它。"
|
|
13
|
-
},
|
|
14
|
-
"kind": { "const": "dsh-agent-roster-organization/v1" },
|
|
15
|
-
"organization": {
|
|
16
|
-
"$ref": "#/$defs/id",
|
|
17
|
-
"description": "组织 id,同时是档案库根目录名。位置即身份。"
|
|
18
|
-
},
|
|
19
|
-
"title": { "type": "string", "maxLength": 120 },
|
|
20
|
-
"description": { "type": "string", "maxLength": 500 },
|
|
21
|
-
"license": { "type": "string", "maxLength": 120 },
|
|
22
|
-
"homepage": { "type": "string", "maxLength": 200, "pattern": "^https?://\\S+$" },
|
|
23
|
-
"maintainers": {
|
|
24
|
-
"type": "array",
|
|
25
|
-
"maxItems": 16,
|
|
26
|
-
"items": { "type": "string", "minLength": 1, "maxLength": 120 }
|
|
27
|
-
},
|
|
28
|
-
"rosters": {
|
|
29
|
-
"type": "array",
|
|
30
|
-
"minItems": 1,
|
|
31
|
-
"maxItems": 64,
|
|
32
|
-
"description": "登记在案的档案。声明了却找不到目录会报错;目录在却没登记会告警。",
|
|
33
|
-
"items": {
|
|
34
|
-
"type": "object",
|
|
35
|
-
"additionalProperties": false,
|
|
36
|
-
"required": ["id"],
|
|
37
|
-
"properties": {
|
|
38
|
-
"id": { "$ref": "#/$defs/id" },
|
|
39
|
-
"title": { "type": "string", "maxLength": 120 },
|
|
40
|
-
"status": { "enum": ["active", "archived"] },
|
|
41
|
-
"origin": { "type": "string", "maxLength": 120 },
|
|
42
|
-
"note": { "type": "string", "maxLength": 300 }
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"$defs": {
|
|
48
|
-
"id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{1,63}$" }
|
|
49
|
-
}
|
|
50
|
-
}
|