@siming-org/core 0.3.0 → 0.4.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/dist/index.d.ts +1010 -15
- package/dist/index.js +1230 -338
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
|
-
import { z as
|
|
3
|
+
import { z as z15 } from "zod";
|
|
4
4
|
|
|
5
5
|
// src/store/client.ts
|
|
6
6
|
import { MongoClient } from "mongodb";
|
|
@@ -88,6 +88,12 @@ function createRepo(collection, options = {}) {
|
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
+
function escapeRegExpLiteral(input) {
|
|
92
|
+
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
93
|
+
}
|
|
94
|
+
function enabledNeFalse() {
|
|
95
|
+
return { enabled: { $ne: false } };
|
|
96
|
+
}
|
|
91
97
|
|
|
92
98
|
// src/errors.ts
|
|
93
99
|
var BIZ_CODE_MESSAGES = {
|
|
@@ -108,6 +114,8 @@ var BIZ_CODE_MESSAGES = {
|
|
|
108
114
|
ENUM_ENTRY_IN_USE: "\u679A\u4E3E\u503C\u88AB\u8D44\u6E90\u5F15\u7528\uFF0C\u7981\u6B62\u5220\u9664",
|
|
109
115
|
ENUM_VALUE_CONFLICT: "\u679A\u4E3E\u503C\u5DF2\u5B58\u5728",
|
|
110
116
|
NAME_SCOPE_CONFLICT: "\u8BE5\u4F5C\u7528\u57DF\u5185\u5DF2\u5B58\u5728\u540C\u540D\u8D44\u6E90\uFF08\u5168\u5C40\u8D44\u6E90\u4E0E\u9879\u76EE\u8D44\u6E90\u4E0D\u53EF\u540C\u540D\uFF09",
|
|
117
|
+
// T202608270002:主文档引用的引用文档删除保护(message 由路由层附加具体 path 清单)
|
|
118
|
+
REFERENCE_IN_USE: "\u4E3B\u6587\u6863\u4ECD\u5F15\u7528\u5F85\u5220\u9664\u7684\u5F15\u7528\u6587\u6863\uFF0C\u8BF7\u5148\u79FB\u9664\u4E3B\u6587\u6863\u4E2D\u7684\u5F15\u7528\u518D\u5220\u9664",
|
|
111
119
|
TASK_DOC_NOT_SET: "\u4EFB\u52A1\u672A\u5173\u8054\u4EFB\u52A1\u6587\u4EF6",
|
|
112
120
|
TASK_DOC_NOT_FOUND: "\u4EFB\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728",
|
|
113
121
|
TASK_DOC_SECTION_NOT_FOUND: "\u4EFB\u52A1\u6587\u4EF6\u4E2D\u672A\u627E\u5230\u5339\u914D\u6BB5\u843D",
|
|
@@ -121,7 +129,9 @@ var BIZ_CODE_MESSAGES = {
|
|
|
121
129
|
// T202608240003:类型化任务节点路径(创建时剪枝 + 类型×轨道入口校验)
|
|
122
130
|
TASK_PRUNE_GRAPH_INVALID: "\u526A\u679D\u540E DAG \u56FE\u4E0D\u5B8C\u6574\uFF08\u8282\u70B9\u6E05\u5355\u4E0E\u6A21\u677F\u7ED3\u6784\u4E0D\u517C\u5BB9\uFF0C\u89C1\u9519\u8BEF\u8BE6\u60C5\uFF09",
|
|
123
131
|
TASK_SKIP_NODE_NOT_FOUND: "skip \u6E05\u5355\u542B\u6A21\u677F\u4E0D\u5B58\u5728\u7684\u8282\u70B9 id\uFF08\u89C1\u9519\u8BEF\u8BE6\u60C5\u7684\u5408\u6CD5\u8282\u70B9\u6E05\u5355\uFF09",
|
|
124
|
-
TASK_TYPE_TRACK_MISMATCH: "\u4EFB\u52A1\u7C7B\u578B\u4E0E\u8F68\u9053\u7EC4\u5408\u975E\u6CD5\uFF08research \u8F68\u9053\u4E3A\u8C03\u7814\u7C7B\u578B\u4E13\u7528\u503C\uFF1BUI \u5FAE\u8C03\u4EC5\u9002\u7528 ui \u8F68\u9053\uFF09"
|
|
132
|
+
TASK_TYPE_TRACK_MISMATCH: "\u4EFB\u52A1\u7C7B\u578B\u4E0E\u8F68\u9053\u7EC4\u5408\u975E\u6CD5\uFF08research \u8F68\u9053\u4E3A\u8C03\u7814\u7C7B\u578B\u4E13\u7528\u503C\uFF1BUI \u5FAE\u8C03\u4EC5\u9002\u7528 ui \u8F68\u9053\uFF09",
|
|
133
|
+
// T202608290001:停用模板创建任务守卫(资产启停——停用对创建通道同样不可见)
|
|
134
|
+
TEMPLATE_DISABLED: "\u6A21\u677F\u5DF2\u505C\u7528\uFF0C\u7981\u6B62\u7528\u4E8E\u521B\u5EFA\u4EFB\u52A1\uFF08Web \u7BA1\u7406\u53F0\u53EF\u91CD\u65B0\u542F\u7528\uFF09"
|
|
125
135
|
};
|
|
126
136
|
var AppError = class extends Error {
|
|
127
137
|
};
|
|
@@ -201,6 +211,11 @@ var ValidationError = class extends AppError {
|
|
|
201
211
|
// src/store/repos/skill.repo.ts
|
|
202
212
|
function createSkillRepo(db) {
|
|
203
213
|
const base = createRepo(db.collection("skills"), { nameField: "name" });
|
|
214
|
+
const listQuery = (cond, opts) => ({
|
|
215
|
+
...cond,
|
|
216
|
+
...opts?.includeDisabled ? {} : enabledNeFalse(),
|
|
217
|
+
...opts?.q ? { name: { $regex: escapeRegExpLiteral(opts.q), $options: "i" } } : {}
|
|
218
|
+
});
|
|
204
219
|
return {
|
|
205
220
|
...base,
|
|
206
221
|
async createSkill(data) {
|
|
@@ -218,26 +233,34 @@ function createSkillRepo(db) {
|
|
|
218
233
|
return base.delete(skill.id);
|
|
219
234
|
},
|
|
220
235
|
async updateByNameScoped(name, scope, projectId, patch) {
|
|
221
|
-
const skill = await this.getByNameScoped(name, scope, projectId);
|
|
236
|
+
const skill = await this.getByNameScoped(name, scope, projectId, { includeDisabled: true });
|
|
222
237
|
if (!skill || !skill.id) return null;
|
|
223
238
|
return base.update(skill.id, patch);
|
|
224
239
|
},
|
|
225
240
|
async deleteByNameScoped(name, scope, projectId) {
|
|
226
|
-
const skill = await this.getByNameScoped(name, scope, projectId);
|
|
241
|
+
const skill = await this.getByNameScoped(name, scope, projectId, { includeDisabled: true });
|
|
227
242
|
if (!skill || !skill.id) return false;
|
|
228
243
|
return base.delete(skill.id);
|
|
229
244
|
},
|
|
230
|
-
async getByNameScoped(name, scope, projectId) {
|
|
245
|
+
async getByNameScoped(name, scope, projectId, opts) {
|
|
231
246
|
const doc = await base._collection.findOne(
|
|
232
|
-
|
|
247
|
+
listQuery(
|
|
248
|
+
scope === "project" ? { name, scope: "project", projectId } : { name, scope: "global" },
|
|
249
|
+
opts
|
|
250
|
+
)
|
|
233
251
|
);
|
|
234
252
|
return doc ? toEntity(doc) : null;
|
|
235
253
|
},
|
|
236
|
-
async listSkillsByScope(projectId) {
|
|
237
|
-
return base.list({ $or: [{ scope: "global" }, { scope: "project", projectId }] });
|
|
254
|
+
async listSkillsByScope(projectId, opts) {
|
|
255
|
+
return base.list(listQuery({ $or: [{ scope: "global" }, { scope: "project", projectId }] }, opts));
|
|
238
256
|
},
|
|
239
|
-
async listSkillsByScopeFilter(scope, projectId) {
|
|
240
|
-
return base.list(
|
|
257
|
+
async listSkillsByScopeFilter(scope, projectId, opts) {
|
|
258
|
+
return base.list(
|
|
259
|
+
listQuery(scope === "project" ? { scope: "project", projectId } : { scope: "global" }, opts)
|
|
260
|
+
);
|
|
261
|
+
},
|
|
262
|
+
async listSkills(opts) {
|
|
263
|
+
return base.list(listQuery({}, opts));
|
|
241
264
|
},
|
|
242
265
|
async listSkillsByNames(names, scope, projectId) {
|
|
243
266
|
if (names.length === 0) return /* @__PURE__ */ new Map();
|
|
@@ -261,7 +284,7 @@ function createSkillRepo(db) {
|
|
|
261
284
|
throw new ConflictError("skill", `name already exists: ${newName}`);
|
|
262
285
|
}
|
|
263
286
|
await assertNameScopeAvailable(base._collection, newName, target.scope, target.projectId);
|
|
264
|
-
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, projectId: _sourceProjectId, scope: _sourceScope, ...rest } = sourceEntity;
|
|
287
|
+
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, projectId: _sourceProjectId, scope: _sourceScope, enabled: _sourceEnabled, ...rest } = sourceEntity;
|
|
265
288
|
const copy = {
|
|
266
289
|
...JSON.parse(JSON.stringify(rest)),
|
|
267
290
|
name: newName,
|
|
@@ -285,7 +308,31 @@ async function assertNameScopeAvailable(collection, name, scope, projectId) {
|
|
|
285
308
|
|
|
286
309
|
// src/store/repos/agent.repo.ts
|
|
287
310
|
function createAgentRepo(db) {
|
|
288
|
-
const
|
|
311
|
+
const raw = createRepo(db.collection("agents"), { nameField: "name" });
|
|
312
|
+
const normalizeFunction = (agent) => ({ ...agent, "function": agent["function"] ?? "executor" });
|
|
313
|
+
const base = {
|
|
314
|
+
...raw,
|
|
315
|
+
async list(filter = void 0) {
|
|
316
|
+
return (await raw.list(filter)).map(normalizeFunction);
|
|
317
|
+
},
|
|
318
|
+
async getById(id) {
|
|
319
|
+
const agent = await raw.getById(id);
|
|
320
|
+
return agent ? normalizeFunction(agent) : null;
|
|
321
|
+
},
|
|
322
|
+
async getByName(name) {
|
|
323
|
+
const agent = await raw.getByName(name);
|
|
324
|
+
return agent ? normalizeFunction(agent) : null;
|
|
325
|
+
},
|
|
326
|
+
async update(id, patch) {
|
|
327
|
+
const agent = await raw.update(id, patch);
|
|
328
|
+
return agent ? normalizeFunction(agent) : null;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
const listQuery = (cond, opts) => ({
|
|
332
|
+
...cond,
|
|
333
|
+
...opts?.includeDisabled ? {} : enabledNeFalse(),
|
|
334
|
+
...opts?.q ? { name: { $regex: escapeRegExpLiteral(opts.q), $options: "i" } } : {}
|
|
335
|
+
});
|
|
289
336
|
return {
|
|
290
337
|
...base,
|
|
291
338
|
async createAgent(data) {
|
|
@@ -303,26 +350,34 @@ function createAgentRepo(db) {
|
|
|
303
350
|
return base.delete(agent.id);
|
|
304
351
|
},
|
|
305
352
|
async updateByNameScoped(name, scope, projectId, patch) {
|
|
306
|
-
const agent = await this.getByNameScoped(name, scope, projectId);
|
|
353
|
+
const agent = await this.getByNameScoped(name, scope, projectId, { includeDisabled: true });
|
|
307
354
|
if (!agent || !agent.id) return null;
|
|
308
355
|
return base.update(agent.id, patch);
|
|
309
356
|
},
|
|
310
357
|
async deleteByNameScoped(name, scope, projectId) {
|
|
311
|
-
const agent = await this.getByNameScoped(name, scope, projectId);
|
|
358
|
+
const agent = await this.getByNameScoped(name, scope, projectId, { includeDisabled: true });
|
|
312
359
|
if (!agent || !agent.id) return false;
|
|
313
360
|
return base.delete(agent.id);
|
|
314
361
|
},
|
|
315
|
-
async getByNameScoped(name, scope, projectId) {
|
|
362
|
+
async getByNameScoped(name, scope, projectId, opts) {
|
|
316
363
|
const doc = await base._collection.findOne(
|
|
317
|
-
|
|
364
|
+
listQuery(
|
|
365
|
+
scope === "project" ? { name, scope: "project", projectId } : { name, scope: "global" },
|
|
366
|
+
opts
|
|
367
|
+
)
|
|
318
368
|
);
|
|
319
|
-
return doc ? toEntity(doc) : null;
|
|
369
|
+
return doc ? normalizeFunction(toEntity(doc)) : null;
|
|
320
370
|
},
|
|
321
|
-
async listAgentsByScope(projectId) {
|
|
322
|
-
return base.list({ $or: [{ scope: "global" }, { scope: "project", projectId }] });
|
|
371
|
+
async listAgentsByScope(projectId, opts) {
|
|
372
|
+
return base.list(listQuery({ $or: [{ scope: "global" }, { scope: "project", projectId }] }, opts));
|
|
323
373
|
},
|
|
324
|
-
async listAgentsByScopeFilter(scope, projectId) {
|
|
325
|
-
return base.list(
|
|
374
|
+
async listAgentsByScopeFilter(scope, projectId, opts) {
|
|
375
|
+
return base.list(
|
|
376
|
+
listQuery(scope === "project" ? { scope: "project", projectId } : { scope: "global" }, opts)
|
|
377
|
+
);
|
|
378
|
+
},
|
|
379
|
+
async listAgents(opts) {
|
|
380
|
+
return base.list(listQuery({}, opts));
|
|
326
381
|
},
|
|
327
382
|
async copyAgent(sourceName, source, target, newName, skillRepo) {
|
|
328
383
|
const src = await base._collection.findOne(
|
|
@@ -331,7 +386,7 @@ function createAgentRepo(db) {
|
|
|
331
386
|
if (!src) {
|
|
332
387
|
throw new ConflictError("agent", `copy source not found: ${sourceName}`);
|
|
333
388
|
}
|
|
334
|
-
const sourceEntity = toEntity(src);
|
|
389
|
+
const sourceEntity = normalizeFunction(toEntity(src));
|
|
335
390
|
const sameScopeDup = await base._collection.findOne(
|
|
336
391
|
target.scope === "project" ? { name: newName, scope: "project", projectId: target.projectId } : { name: newName, scope: "global" },
|
|
337
392
|
{ projection: { _id: 1 } }
|
|
@@ -341,7 +396,7 @@ function createAgentRepo(db) {
|
|
|
341
396
|
}
|
|
342
397
|
await assertNameScopeAvailable2(base._collection, newName, target.scope, target.projectId);
|
|
343
398
|
await assertBoundSkillsCompatible(skillRepo, target.scope, target.projectId, src.boundSkills ?? []);
|
|
344
|
-
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, projectId: _sourceProjectId, scope: _sourceScope, ...rest } = sourceEntity;
|
|
399
|
+
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, projectId: _sourceProjectId, scope: _sourceScope, enabled: _sourceEnabled, ...rest } = sourceEntity;
|
|
345
400
|
const copy = {
|
|
346
401
|
...JSON.parse(JSON.stringify(rest)),
|
|
347
402
|
name: newName,
|
|
@@ -392,6 +447,7 @@ function createDagTemplateRepo(db) {
|
|
|
392
447
|
const query = {};
|
|
393
448
|
if (filter?.projectId) query.projectId = filter.projectId;
|
|
394
449
|
if (filter?.name) query.name = filter.name;
|
|
450
|
+
if (!filter?.includeDisabled) Object.assign(query, enabledNeFalse());
|
|
395
451
|
return base.list(query);
|
|
396
452
|
},
|
|
397
453
|
async copyDagTemplate(sourceId, targetProjectId, newName) {
|
|
@@ -406,7 +462,7 @@ function createDagTemplateRepo(db) {
|
|
|
406
462
|
if (dup) {
|
|
407
463
|
throw new ConflictError("dagTemplate", `name conflict in target project: ${newName}`);
|
|
408
464
|
}
|
|
409
|
-
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, ...rest } = source;
|
|
465
|
+
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, enabled: _sourceEnabled, ...rest } = source;
|
|
410
466
|
const copy = {
|
|
411
467
|
...rest,
|
|
412
468
|
name: newName,
|
|
@@ -420,11 +476,122 @@ function createDagTemplateRepo(db) {
|
|
|
420
476
|
}
|
|
421
477
|
|
|
422
478
|
// src/schemas/task.ts
|
|
423
|
-
import { z as
|
|
479
|
+
import { z as z4 } from "zod";
|
|
424
480
|
|
|
425
481
|
// src/schemas/skill.ts
|
|
482
|
+
import { z as z2 } from "zod";
|
|
483
|
+
|
|
484
|
+
// src/schemas/reference.ts
|
|
426
485
|
import { z } from "zod";
|
|
427
|
-
var
|
|
486
|
+
var REFERENCE_LIMITS = {
|
|
487
|
+
/** 单资产引用文档数量上限 */
|
|
488
|
+
maxCount: 20,
|
|
489
|
+
/** 单引用文档内容字符数上限 */
|
|
490
|
+
maxContentChars: 1e6,
|
|
491
|
+
/** 聚合护栏:主文档正文 + Σ引用文档内容的字符总量上限 */
|
|
492
|
+
maxTotalChars: 3e6
|
|
493
|
+
};
|
|
494
|
+
var SKILL_MAIN_DOC = "SKILL.md";
|
|
495
|
+
var AGENT_MAIN_DOC = "agent.md";
|
|
496
|
+
var ReferenceDocBaseSchema = z.object({
|
|
497
|
+
/** 相对资产根的相对路径(正斜杠分隔;禁止绝对路径 / .. 回溯 / 反斜杠 / 空段 / 主文档名冲突) */
|
|
498
|
+
path: z.string().min(1),
|
|
499
|
+
/** 文档全文(空文档合法) */
|
|
500
|
+
content: z.string().max(REFERENCE_LIMITS.maxContentChars)
|
|
501
|
+
});
|
|
502
|
+
function canonicalizeReferencePath(path) {
|
|
503
|
+
return path.split("/").filter((segment) => segment !== ".").join("/").toLowerCase();
|
|
504
|
+
}
|
|
505
|
+
function sumReferenceContentChars(references) {
|
|
506
|
+
return (references ?? []).reduce((total, doc) => total + doc.content.length, 0);
|
|
507
|
+
}
|
|
508
|
+
function createReferencesSchema(mainDocName) {
|
|
509
|
+
const elementSchema = ReferenceDocBaseSchema.superRefine((val, ctx) => {
|
|
510
|
+
const path = val.path;
|
|
511
|
+
let reason = null;
|
|
512
|
+
if (path.startsWith("/")) {
|
|
513
|
+
reason = "\u7981\u6B62\u7EDD\u5BF9\u8DEF\u5F84";
|
|
514
|
+
} else if (path.includes("\\")) {
|
|
515
|
+
reason = "\u7981\u6B62\u53CD\u659C\u6760\u8DEF\u5F84\u5206\u9694\u7B26\uFF08\u987B\u4F7F\u7528\u6B63\u659C\u6760 /\uFF09";
|
|
516
|
+
} else {
|
|
517
|
+
const rawSegments = path.split("/");
|
|
518
|
+
if (rawSegments.some((segment) => segment === "")) {
|
|
519
|
+
reason = "\u7981\u6B62\u7A7A\u8DEF\u5F84\u6BB5\uFF08\u8FDE\u7EED\u6216\u5C3E\u968F\u659C\u6760\uFF09";
|
|
520
|
+
} else if (rawSegments.includes("..")) {
|
|
521
|
+
reason = "\u7981\u6B62\u8D8A\u754C\u56DE\u6EAF\uFF08.. \u8DEF\u5F84\u6BB5\uFF09";
|
|
522
|
+
} else if (canonicalizeReferencePath(path).length === 0) {
|
|
523
|
+
reason = '\u8DEF\u5F84\u4E0D\u80FD\u53EA\u7531 "." \u7EC4\u6210';
|
|
524
|
+
} else if (canonicalizeReferencePath(path) === canonicalizeReferencePath(mainDocName)) {
|
|
525
|
+
reason = `\u4E0E\u4E3B\u6587\u6863\u6587\u4EF6\u540D ${mainDocName} \u51B2\u7A81`;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (reason !== null) {
|
|
529
|
+
ctx.addIssue({ code: "custom", path: ["path"], message: `${reason}\uFF1A${path}` });
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
return z.array(elementSchema).max(REFERENCE_LIMITS.maxCount).superRefine((docs, ctx) => {
|
|
533
|
+
const seen = /* @__PURE__ */ new Map();
|
|
534
|
+
docs.forEach((doc, index) => {
|
|
535
|
+
const canonical = canonicalizeReferencePath(doc.path);
|
|
536
|
+
const firstIndex = seen.get(canonical);
|
|
537
|
+
if (firstIndex !== void 0) {
|
|
538
|
+
ctx.addIssue({
|
|
539
|
+
code: "custom",
|
|
540
|
+
path: [index],
|
|
541
|
+
message: `\u5F15\u7528\u6587\u6863\u8DEF\u5F84\u91CD\u590D\uFF08\u4E0E\u7B2C ${firstIndex + 1} \u6761\u89C4\u8303\u5316\u540E\u76F8\u540C\uFF09\uFF1A${doc.path}`
|
|
542
|
+
});
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
seen.set(canonical, index);
|
|
546
|
+
});
|
|
547
|
+
const entries = docs.map((doc, index) => ({ canonical: canonicalizeReferencePath(doc.path), index }));
|
|
548
|
+
for (const a of entries) {
|
|
549
|
+
for (const b of entries) {
|
|
550
|
+
if (a.index >= b.index) continue;
|
|
551
|
+
if (isDirectoryPrefix(a.canonical, b.canonical) || isDirectoryPrefix(b.canonical, a.canonical)) {
|
|
552
|
+
ctx.addIssue({
|
|
553
|
+
code: "custom",
|
|
554
|
+
path: [b.index],
|
|
555
|
+
message: `\u5F15\u7528\u6587\u6863\u8DEF\u5F84\u4E92\u4E3A\u76EE\u5F55\u524D\u7F00\uFF08\u843D\u76D8\u65F6\u6587\u4EF6\u4E0E\u76EE\u5F55\u51B2\u7A81\uFF09\uFF1A${docs[a.index]?.path ?? "?"} \u4E0E ${docs[b.index]?.path ?? "?"}`
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
function isDirectoryPrefix(prefixCanonical, candidateCanonical) {
|
|
563
|
+
if (prefixCanonical.length >= candidateCanonical.length) return false;
|
|
564
|
+
if (!candidateCanonical.startsWith(prefixCanonical)) return false;
|
|
565
|
+
return candidateCanonical.charAt(prefixCanonical.length) === "/";
|
|
566
|
+
}
|
|
567
|
+
function assertReferencesDeletable(params) {
|
|
568
|
+
const { resource, name, existingReferences, nextReferences, nextMainBody, currentMainBody } = params;
|
|
569
|
+
if (nextReferences === void 0) return;
|
|
570
|
+
const nextPaths = new Set(nextReferences.map((doc) => doc.path));
|
|
571
|
+
const deletedPaths = (existingReferences ?? []).map((doc) => doc.path).filter((path) => !nextPaths.has(path));
|
|
572
|
+
if (deletedPaths.length === 0) return;
|
|
573
|
+
const mainBody = nextMainBody ?? currentMainBody;
|
|
574
|
+
const violating = deletedPaths.filter((path) => mainBody.includes(path));
|
|
575
|
+
if (violating.length === 0) return;
|
|
576
|
+
throw new ConflictError(resource, name, {
|
|
577
|
+
bizCode: "REFERENCE_IN_USE",
|
|
578
|
+
message: `${BIZ_CODE_MESSAGES.REFERENCE_IN_USE}\uFF08${resource} ${name}\uFF09\uFF1A${violating.join("\u3001")}`
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
function assertReferenceAggregateLimit(params) {
|
|
582
|
+
const { effectiveMainBody, nextReferences } = params;
|
|
583
|
+
if (nextReferences === void 0) return;
|
|
584
|
+
const totalChars = effectiveMainBody.length + sumReferenceContentChars(nextReferences);
|
|
585
|
+
if (totalChars > REFERENCE_LIMITS.maxTotalChars) {
|
|
586
|
+
throw new ValidationError(
|
|
587
|
+
`\u4E3B\u6587\u6863\u4E0E\u5F15\u7528\u6587\u6863\u5185\u5BB9\u603B\u91CF ${String(totalChars)} \u5B57\u7B26\u8D85\u51FA\u805A\u5408\u4E0A\u9650 ${String(REFERENCE_LIMITS.maxTotalChars)}\uFF08\u542B\u5E93\u5185\u73B0\u6709\u4E3B\u6587\u6863\u6B63\u6587\u5408\u5E76\u8BA1\u7B97\uFF09`,
|
|
588
|
+
[{ path: ["references"], message: `\u603B\u91CF ${String(totalChars)} \u8D85\u51FA\u4E0A\u9650 ${String(REFERENCE_LIMITS.maxTotalChars)}` }]
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// src/schemas/skill.ts
|
|
594
|
+
var SkillScopeSchema = z2.enum(["global", "project"]);
|
|
428
595
|
var OBJECT_ID_HEX = /^[a-f0-9]{24}$/;
|
|
429
596
|
var SkillScopeRefine = (val, ctx) => {
|
|
430
597
|
if (val.scope === "project" && !val.projectId) {
|
|
@@ -435,38 +602,59 @@ var SkillScopeRefine = (val, ctx) => {
|
|
|
435
602
|
});
|
|
436
603
|
}
|
|
437
604
|
};
|
|
438
|
-
var SkillShapeSchema =
|
|
439
|
-
id:
|
|
440
|
-
name:
|
|
441
|
-
description:
|
|
442
|
-
content:
|
|
443
|
-
category:
|
|
444
|
-
version:
|
|
605
|
+
var SkillShapeSchema = z2.object({
|
|
606
|
+
id: z2.string().optional(),
|
|
607
|
+
name: z2.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "kebab-case only"),
|
|
608
|
+
description: z2.string(),
|
|
609
|
+
content: z2.string(),
|
|
610
|
+
category: z2.string(),
|
|
611
|
+
version: z2.string().default("1.0.0"),
|
|
612
|
+
/** 引用文档集合(可选——存量单文件资产无此字段,行为与升级前一致) */
|
|
613
|
+
references: createReferencesSchema(SKILL_MAIN_DOC).optional(),
|
|
445
614
|
/** 作用域:global=跨项目复用(默认,仅作存量迁移读侧兜底)/ project=项目专用(N012 D3) */
|
|
446
615
|
scope: SkillScopeSchema.default("global"),
|
|
447
616
|
/** 仅 scope=project 时必填且为合法 ObjectId hex(superRefine 保证) */
|
|
448
|
-
projectId:
|
|
449
|
-
|
|
450
|
-
|
|
617
|
+
projectId: z2.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
|
|
618
|
+
/**
|
|
619
|
+
* T202608290001 资产启停(可选——存量无此字段 = 启用,零迁移)。
|
|
620
|
+
* 全局判断规则:enabled === false 即停用,true/缺失均启用。
|
|
621
|
+
* 唯一写入口是专用启停端点(POST /:name/enabled)——Create/Update/Copy 均不含此字段
|
|
622
|
+
* (规避 Zod4 .partial() default 注入 + 启停不触发版本提升)。
|
|
623
|
+
*/
|
|
624
|
+
enabled: z2.boolean().optional(),
|
|
625
|
+
createdAt: z2.date().optional(),
|
|
626
|
+
updatedAt: z2.date().optional()
|
|
451
627
|
});
|
|
452
|
-
var
|
|
628
|
+
var skillAggregateLimitRefine = (val, ctx) => {
|
|
629
|
+
const totalChars = (val.content?.length ?? 0) + sumReferenceContentChars(val.references);
|
|
630
|
+
if (totalChars > REFERENCE_LIMITS.maxTotalChars) {
|
|
631
|
+
ctx.addIssue({
|
|
632
|
+
code: "custom",
|
|
633
|
+
path: ["references"],
|
|
634
|
+
message: `\u4E3B\u6587\u6863\u4E0E\u5F15\u7528\u6587\u6863\u5185\u5BB9\u603B\u91CF ${totalChars} \u5B57\u7B26\u8D85\u51FA\u805A\u5408\u4E0A\u9650 ${REFERENCE_LIMITS.maxTotalChars}`
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
var SkillSchema = SkillShapeSchema.superRefine(SkillScopeRefine).superRefine(skillAggregateLimitRefine);
|
|
453
639
|
var SkillCreateSchema = SkillShapeSchema.omit({
|
|
454
640
|
id: true,
|
|
455
641
|
createdAt: true,
|
|
456
|
-
updatedAt: true
|
|
457
|
-
|
|
642
|
+
updatedAt: true,
|
|
643
|
+
enabled: true
|
|
644
|
+
}).extend({ scope: SkillScopeSchema }).superRefine(SkillScopeRefine).superRefine(skillAggregateLimitRefine);
|
|
458
645
|
var SkillUpdateSchema = SkillShapeSchema.omit({
|
|
459
646
|
id: true,
|
|
460
647
|
createdAt: true,
|
|
461
|
-
updatedAt: true
|
|
648
|
+
updatedAt: true,
|
|
649
|
+
enabled: true
|
|
462
650
|
}).extend({
|
|
463
|
-
version:
|
|
651
|
+
version: z2.string().optional(),
|
|
464
652
|
scope: SkillScopeSchema.optional()
|
|
465
|
-
}).partial().superRefine(SkillScopeRefine);
|
|
466
|
-
var SkillCopySchema =
|
|
467
|
-
newName:
|
|
653
|
+
}).partial().superRefine(SkillScopeRefine).superRefine(skillAggregateLimitRefine);
|
|
654
|
+
var SkillCopySchema = z2.object({
|
|
655
|
+
newName: z2.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "kebab-case only"),
|
|
468
656
|
newScope: SkillScopeSchema,
|
|
469
|
-
targetProjectId:
|
|
657
|
+
targetProjectId: z2.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
|
|
470
658
|
}).superRefine((val, ctx) => {
|
|
471
659
|
if (val.newScope === "project" && !val.targetProjectId) {
|
|
472
660
|
ctx.addIssue({
|
|
@@ -478,26 +666,26 @@ var SkillCopySchema = z.object({
|
|
|
478
666
|
});
|
|
479
667
|
|
|
480
668
|
// src/schemas/dag-template.ts
|
|
481
|
-
import { z as
|
|
482
|
-
var DagNodePhaseSchema =
|
|
483
|
-
var DagNodeTrackSchema =
|
|
669
|
+
import { z as z3 } from "zod";
|
|
670
|
+
var DagNodePhaseSchema = z3.string();
|
|
671
|
+
var DagNodeTrackSchema = z3.string();
|
|
484
672
|
var DAG_PHASES = ["entry", "track", "test", "exit"];
|
|
485
673
|
var DAG_TRACKS = ["backend", "ui", "all"];
|
|
486
674
|
var NODE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
487
|
-
var DagNodeSchema =
|
|
488
|
-
id:
|
|
489
|
-
label:
|
|
675
|
+
var DagNodeSchema = z3.object({
|
|
676
|
+
id: z3.string().regex(NODE_ID_PATTERN, "node id \u53EA\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26\uFF08\u70B9\u8DEF\u5F84\u62FC\u63A5\u5B89\u5168\u524D\u63D0\uFF09"),
|
|
677
|
+
label: z3.string(),
|
|
490
678
|
phase: DagNodePhaseSchema,
|
|
491
679
|
track: DagNodeTrackSchema,
|
|
492
|
-
prompt:
|
|
493
|
-
skills:
|
|
680
|
+
prompt: z3.string(),
|
|
681
|
+
skills: z3.array(z3.string()).default([])
|
|
494
682
|
});
|
|
495
|
-
var PausePointTypeSchema =
|
|
683
|
+
var PausePointTypeSchema = z3.enum(["human_approval", "checkpoint"]);
|
|
496
684
|
var PAUSE_POINT_TYPES = ["human_approval", "checkpoint"];
|
|
497
|
-
var EdgePausePointBaseSchema =
|
|
685
|
+
var EdgePausePointBaseSchema = z3.object({
|
|
498
686
|
type: PausePointTypeSchema,
|
|
499
|
-
description:
|
|
500
|
-
autoResume:
|
|
687
|
+
description: z3.string(),
|
|
688
|
+
autoResume: z3.boolean().default(false)
|
|
501
689
|
});
|
|
502
690
|
var EdgePausePointSchema = EdgePausePointBaseSchema.superRefine((pp, ctx) => {
|
|
503
691
|
if (pp.type === "human_approval" && pp.autoResume) {
|
|
@@ -513,152 +701,160 @@ var EdgePausePointSchema = EdgePausePointBaseSchema.superRefine((pp, ctx) => {
|
|
|
513
701
|
});
|
|
514
702
|
}
|
|
515
703
|
});
|
|
516
|
-
var DagEdgeSchema =
|
|
517
|
-
from:
|
|
518
|
-
to:
|
|
519
|
-
condition:
|
|
704
|
+
var DagEdgeSchema = z3.object({
|
|
705
|
+
from: z3.string(),
|
|
706
|
+
to: z3.string(),
|
|
707
|
+
condition: z3.string().optional(),
|
|
520
708
|
/** N017 D1:边暂停点(可选)。human_approval=人工审批门(advance 落 paused 等 approve);checkpoint=检查点(仅记录直接流转) */
|
|
521
709
|
pausePoint: EdgePausePointSchema.optional()
|
|
522
710
|
});
|
|
523
|
-
var DagTemplateSchema =
|
|
524
|
-
id:
|
|
525
|
-
name:
|
|
711
|
+
var DagTemplateSchema = z3.object({
|
|
712
|
+
id: z3.string().optional(),
|
|
713
|
+
name: z3.string(),
|
|
526
714
|
/** 归属项目 id(N012:实体层去哨兵 default,由路由层解析写入——校验项目存在且 active 后落库) */
|
|
527
|
-
projectId:
|
|
528
|
-
description:
|
|
529
|
-
nodes:
|
|
530
|
-
edges:
|
|
715
|
+
projectId: z3.string(),
|
|
716
|
+
description: z3.string(),
|
|
717
|
+
nodes: z3.array(DagNodeSchema),
|
|
718
|
+
edges: z3.array(DagEdgeSchema),
|
|
531
719
|
/** UI 布局坐标(nodeId → {x,y})。UI 关注点,advance 引擎零感知;旧模板无此字段 → 前端 dagre 兜底布局(N008 D2) */
|
|
532
|
-
layout:
|
|
533
|
-
isDefault:
|
|
534
|
-
version:
|
|
535
|
-
|
|
536
|
-
|
|
720
|
+
layout: z3.record(z3.string(), z3.object({ x: z3.number(), y: z3.number() })).optional(),
|
|
721
|
+
isDefault: z3.boolean().default(false),
|
|
722
|
+
version: z3.string().default("1.0.0"),
|
|
723
|
+
/**
|
|
724
|
+
* T202608290001 资产启停(可选——存量无此字段 = 启用,零迁移)。
|
|
725
|
+
* 全局判断规则:enabled === false 即停用,true/缺失均启用。
|
|
726
|
+
* 唯一写入口是专用启停端点(POST /:id/enabled)——Create/Update/Copy 均不含此字段;
|
|
727
|
+
* 停用模板对 CLI/MCP 列举与任务创建入口不可见(Web 管理台始终可见可重启)。
|
|
728
|
+
*/
|
|
729
|
+
enabled: z3.boolean().optional(),
|
|
730
|
+
createdAt: z3.date().optional(),
|
|
731
|
+
updatedAt: z3.date().optional()
|
|
537
732
|
});
|
|
538
733
|
var DagTemplateCreateSchema = DagTemplateSchema.omit({
|
|
539
734
|
id: true,
|
|
540
735
|
createdAt: true,
|
|
541
|
-
updatedAt: true
|
|
736
|
+
updatedAt: true,
|
|
737
|
+
enabled: true
|
|
542
738
|
});
|
|
543
739
|
var DagTemplateUpdateSchema = DagTemplateCreateSchema.extend({
|
|
544
|
-
isDefault:
|
|
545
|
-
version:
|
|
740
|
+
isDefault: z3.boolean().optional(),
|
|
741
|
+
version: z3.string().optional()
|
|
546
742
|
}).partial();
|
|
547
|
-
var DagTemplateCopySchema =
|
|
548
|
-
newName:
|
|
549
|
-
targetProjectId:
|
|
743
|
+
var DagTemplateCopySchema = z3.object({
|
|
744
|
+
newName: z3.string().min(1),
|
|
745
|
+
targetProjectId: z3.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex")
|
|
550
746
|
});
|
|
551
747
|
|
|
552
748
|
// src/schemas/task.ts
|
|
553
749
|
var TASK_TYPES = ["feature", "bugfix", "ui-tweak", "research"];
|
|
554
750
|
var ENTRY_ID_REGEX = /^[a-z0-9]{6}$/;
|
|
555
|
-
var CheckItemSchema =
|
|
556
|
-
id:
|
|
557
|
-
item:
|
|
558
|
-
passed:
|
|
751
|
+
var CheckItemSchema = z4.object({
|
|
752
|
+
id: z4.string().regex(ENTRY_ID_REGEX),
|
|
753
|
+
item: z4.string().min(1),
|
|
754
|
+
passed: z4.boolean()
|
|
559
755
|
});
|
|
560
756
|
var ARTIFACT_TYPES = ["prd", "tech", "code", "test", "doc", "other"];
|
|
561
|
-
var ArtifactSchema =
|
|
562
|
-
id:
|
|
563
|
-
type:
|
|
564
|
-
path:
|
|
565
|
-
note:
|
|
566
|
-
content:
|
|
757
|
+
var ArtifactSchema = z4.object({
|
|
758
|
+
id: z4.string().regex(ENTRY_ID_REGEX),
|
|
759
|
+
type: z4.enum(ARTIFACT_TYPES),
|
|
760
|
+
path: z4.string().min(1),
|
|
761
|
+
note: z4.string().optional(),
|
|
762
|
+
content: z4.string().max(2e5).optional()
|
|
567
763
|
});
|
|
568
|
-
var ConfirmationSchema =
|
|
569
|
-
id:
|
|
570
|
-
quote:
|
|
571
|
-
at:
|
|
764
|
+
var ConfirmationSchema = z4.object({
|
|
765
|
+
id: z4.string().regex(ENTRY_ID_REGEX),
|
|
766
|
+
quote: z4.string().min(1).max(2e3),
|
|
767
|
+
at: z4.date()
|
|
572
768
|
});
|
|
573
|
-
var DecisionSchema =
|
|
574
|
-
id:
|
|
575
|
-
topic:
|
|
576
|
-
decision:
|
|
769
|
+
var DecisionSchema = z4.object({
|
|
770
|
+
id: z4.string().regex(ENTRY_ID_REGEX),
|
|
771
|
+
topic: z4.string().min(1),
|
|
772
|
+
decision: z4.string().min(1)
|
|
577
773
|
});
|
|
578
|
-
var ReviewSummarySchema =
|
|
579
|
-
verdict:
|
|
580
|
-
rounds:
|
|
581
|
-
critical:
|
|
774
|
+
var ReviewSummarySchema = z4.object({
|
|
775
|
+
verdict: z4.enum(["pass", "fail"]),
|
|
776
|
+
rounds: z4.number().int().min(1),
|
|
777
|
+
critical: z4.number().int().min(0)
|
|
582
778
|
});
|
|
583
|
-
var NodeRecordSchema =
|
|
584
|
-
summary:
|
|
585
|
-
checks:
|
|
586
|
-
artifacts:
|
|
587
|
-
confirmations:
|
|
588
|
-
decisions:
|
|
779
|
+
var NodeRecordSchema = z4.object({
|
|
780
|
+
summary: z4.string().optional(),
|
|
781
|
+
checks: z4.array(CheckItemSchema).default([]),
|
|
782
|
+
artifacts: z4.array(ArtifactSchema).default([]),
|
|
783
|
+
confirmations: z4.array(ConfirmationSchema).default([]),
|
|
784
|
+
decisions: z4.array(DecisionSchema).default([]),
|
|
589
785
|
review: ReviewSummarySchema.optional()
|
|
590
786
|
});
|
|
591
|
-
var TaskDocContentSchema =
|
|
592
|
-
what:
|
|
593
|
-
why:
|
|
594
|
-
acceptance:
|
|
595
|
-
nonGoals:
|
|
596
|
-
trackNote:
|
|
787
|
+
var TaskDocContentSchema = z4.object({
|
|
788
|
+
what: z4.string().min(1),
|
|
789
|
+
why: z4.string().min(1),
|
|
790
|
+
acceptance: z4.array(z4.string()).default([]),
|
|
791
|
+
nonGoals: z4.array(z4.string()).default([]),
|
|
792
|
+
trackNote: z4.string().optional()
|
|
597
793
|
});
|
|
598
|
-
var ArchNoteSchema =
|
|
599
|
-
id:
|
|
600
|
-
text:
|
|
601
|
-
at:
|
|
794
|
+
var ArchNoteSchema = z4.object({
|
|
795
|
+
id: z4.string().regex(ENTRY_ID_REGEX),
|
|
796
|
+
text: z4.string().min(1).max(2e3),
|
|
797
|
+
at: z4.date()
|
|
602
798
|
});
|
|
603
|
-
var NodeStatusSchema =
|
|
799
|
+
var NodeStatusSchema = z4.string();
|
|
604
800
|
var NODE_STATUSES = ["pending", "active", "completed", "skipped"];
|
|
605
|
-
var NodeStateSchema =
|
|
801
|
+
var NodeStateSchema = z4.object({
|
|
606
802
|
status: NodeStatusSchema,
|
|
607
|
-
enteredAt:
|
|
608
|
-
completedAt:
|
|
803
|
+
enteredAt: z4.date().nullable().default(null),
|
|
804
|
+
completedAt: z4.date().nullable().default(null)
|
|
609
805
|
});
|
|
610
|
-
var DagInstanceSchema =
|
|
611
|
-
templateId:
|
|
612
|
-
templateVersion:
|
|
613
|
-
nodes:
|
|
614
|
-
id:
|
|
615
|
-
label:
|
|
806
|
+
var DagInstanceSchema = z4.object({
|
|
807
|
+
templateId: z4.string(),
|
|
808
|
+
templateVersion: z4.string(),
|
|
809
|
+
nodes: z4.array(z4.object({
|
|
810
|
+
id: z4.string(),
|
|
811
|
+
label: z4.string(),
|
|
616
812
|
// N016 F9:枚举放宽(注册表权威)
|
|
617
|
-
phase:
|
|
618
|
-
track:
|
|
619
|
-
prompt:
|
|
620
|
-
skills:
|
|
813
|
+
phase: z4.string(),
|
|
814
|
+
track: z4.string(),
|
|
815
|
+
prompt: z4.string(),
|
|
816
|
+
skills: z4.array(z4.string()).default([])
|
|
621
817
|
})),
|
|
622
818
|
// N017 D1:暂停点内联到边(human_approval=审批门 / checkpoint=检查点),顶层数组删除
|
|
623
|
-
edges:
|
|
624
|
-
from:
|
|
625
|
-
to:
|
|
626
|
-
condition:
|
|
819
|
+
edges: z4.array(z4.object({
|
|
820
|
+
from: z4.string(),
|
|
821
|
+
to: z4.string(),
|
|
822
|
+
condition: z4.string().optional(),
|
|
627
823
|
pausePoint: EdgePausePointBaseSchema.optional()
|
|
628
824
|
})),
|
|
629
|
-
prunedNodes:
|
|
630
|
-
nodeStates:
|
|
825
|
+
prunedNodes: z4.array(z4.string()),
|
|
826
|
+
nodeStates: z4.record(z4.string(), NodeStateSchema)
|
|
631
827
|
});
|
|
632
|
-
var HistoryActionSchema =
|
|
633
|
-
var HistoryEntrySchema =
|
|
634
|
-
nodeId:
|
|
828
|
+
var HistoryActionSchema = z4.enum(["entered", "advanced", "paused", "resumed", "approved", "rejected", "completed", "cancelled"]);
|
|
829
|
+
var HistoryEntrySchema = z4.object({
|
|
830
|
+
nodeId: z4.string(),
|
|
635
831
|
action: HistoryActionSchema,
|
|
636
|
-
timestamp:
|
|
637
|
-
details:
|
|
832
|
+
timestamp: z4.date().default(() => /* @__PURE__ */ new Date()),
|
|
833
|
+
details: z4.record(z4.string(), z4.unknown()).default({})
|
|
638
834
|
});
|
|
639
|
-
var TaskStatusSchema =
|
|
640
|
-
var TaskPhaseSchema =
|
|
835
|
+
var TaskStatusSchema = z4.string();
|
|
836
|
+
var TaskPhaseSchema = z4.string();
|
|
641
837
|
var TASK_STATUSES = ["active", "paused", "completed", "cancelled"];
|
|
642
838
|
var TASK_PHASES = ["entry", "track", "test", "exit"];
|
|
643
|
-
var TaskSchema =
|
|
644
|
-
id:
|
|
645
|
-
taskId:
|
|
646
|
-
title:
|
|
839
|
+
var TaskSchema = z4.object({
|
|
840
|
+
id: z4.string().optional(),
|
|
841
|
+
taskId: z4.string(),
|
|
842
|
+
title: z4.string(),
|
|
647
843
|
/**
|
|
648
844
|
* 任务类型标签(T202608240003):创建时确定、此后不可变(PATCH 白名单不含 type)。
|
|
649
845
|
* 入参与落库同源 enum——持久层防脏值,模板 PRD 调研变体判定依赖精确值。
|
|
650
846
|
*/
|
|
651
|
-
type:
|
|
847
|
+
type: z4.enum(TASK_TYPES).optional(),
|
|
652
848
|
/** 归属项目 id(N012:实体层去哨兵 default,由 repo 显式写入——路由层解析:显式携带校验一致性 / 缺省继承模板 projectId) */
|
|
653
|
-
projectId:
|
|
654
|
-
dagTemplateId:
|
|
849
|
+
projectId: z4.string(),
|
|
850
|
+
dagTemplateId: z4.string(),
|
|
655
851
|
dagInstance: DagInstanceSchema,
|
|
656
|
-
currentNode:
|
|
852
|
+
currentNode: z4.string(),
|
|
657
853
|
currentPhase: TaskPhaseSchema,
|
|
658
854
|
status: TaskStatusSchema,
|
|
659
855
|
/** 暂停位置:from 节点 id(暂停点暂停,approve 经 findNextEdge 重推导边)/ null(手动暂停) */
|
|
660
|
-
pausedAt:
|
|
661
|
-
track:
|
|
856
|
+
pausedAt: z4.string().nullable().default(null),
|
|
857
|
+
track: z4.string(),
|
|
662
858
|
/**
|
|
663
859
|
* N020 D1:任务级结构化文档(未写时字段缺省——语义即「未写」,不做 null 占位)。
|
|
664
860
|
* 写入口:PATCH /api/tasks/:id/doc(字段级 $set + acceptance/nonGoals 条目 $push)。
|
|
@@ -669,27 +865,27 @@ var TaskSchema = z3.object({
|
|
|
669
865
|
* MongoDB 自动创建中间对象,故单条目字段可能先于容器整体存在——读侧对
|
|
670
866
|
* record 内数组字段按 `?? []` 容错(toEntity 不跑 parse 补 default)。
|
|
671
867
|
*/
|
|
672
|
-
nodeRecords:
|
|
868
|
+
nodeRecords: z4.record(z4.string(), NodeRecordSchema).default({}),
|
|
673
869
|
/** N020 D1:跨节点累积素材(架构信息收集条目,服务端生成 id/at) */
|
|
674
|
-
archNotes:
|
|
675
|
-
history:
|
|
676
|
-
createdAt:
|
|
677
|
-
updatedAt:
|
|
870
|
+
archNotes: z4.array(ArchNoteSchema).default([]),
|
|
871
|
+
history: z4.array(HistoryEntrySchema).default([]),
|
|
872
|
+
createdAt: z4.date().optional(),
|
|
873
|
+
updatedAt: z4.date().optional()
|
|
678
874
|
});
|
|
679
|
-
var TaskCreateInputSchema =
|
|
680
|
-
title:
|
|
681
|
-
track:
|
|
682
|
-
dagTemplateId:
|
|
875
|
+
var TaskCreateInputSchema = z4.object({
|
|
876
|
+
title: z4.string(),
|
|
877
|
+
track: z4.string(),
|
|
878
|
+
dagTemplateId: z4.string(),
|
|
683
879
|
/**
|
|
684
880
|
* T202608240003 创建时剪枝:显式剔除的节点 id(在轨道自动剔除之外)。
|
|
685
881
|
* 清单来源约束 = task-create skill 的类型×轨道映射矩阵(skip 只允许来自映射,禁止自由发挥);
|
|
686
882
|
* 剪枝算法与图校验见 core/task/prune.ts。
|
|
687
883
|
*/
|
|
688
|
-
skipNodes:
|
|
884
|
+
skipNodes: z4.array(z4.string().regex(NODE_ID_PATTERN, "skipNodes \u6761\u76EE\u987B\u4E3A\u5408\u6CD5\u8282\u70B9 id")).optional(),
|
|
689
885
|
/** 任务类型标签(见 TASK_TYPES;与 track 的合法组合由 server 入口校验) */
|
|
690
|
-
type:
|
|
886
|
+
type: z4.enum(TASK_TYPES).optional(),
|
|
691
887
|
/** 归属项目:可选(缺省 → 继承模板 projectId,D2;显式携带且 ≠ 模板 → 409 TEMPLATE_PROJECT_MISMATCH) */
|
|
692
|
-
projectId:
|
|
888
|
+
projectId: z4.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
|
|
693
889
|
});
|
|
694
890
|
|
|
695
891
|
// src/task/track-match.ts
|
|
@@ -948,6 +1144,26 @@ async function resumeTask(deps, taskId, decision) {
|
|
|
948
1144
|
expectedCurrentNode: task.currentNode
|
|
949
1145
|
});
|
|
950
1146
|
}
|
|
1147
|
+
async function cancelTask(deps, taskId, reason) {
|
|
1148
|
+
const task = await deps.getTask(taskId);
|
|
1149
|
+
if (!task) throw new NotFoundError("task", taskId);
|
|
1150
|
+
if (task.status !== "active" && task.status !== "paused") {
|
|
1151
|
+
throw new BadRequestError(
|
|
1152
|
+
`Cannot cancel: task status is '${task.status}' (terminal). Only active/paused tasks can be cancelled.`,
|
|
1153
|
+
void 0,
|
|
1154
|
+
{ bizCode: "TASK_TERMINATED", message: `\u4EFB\u52A1\u5DF2\u5B8C\u7ED3\uFF08completed/cancelled\uFF09\uFF0C\u7981\u6B62\u53D6\u6D88\uFF08\u5F53\u524D status: ${task.status}\uFF09` }
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
const patch = {
|
|
1158
|
+
status: "cancelled",
|
|
1159
|
+
pausedAt: null,
|
|
1160
|
+
history: [...task.history, mkHistory(task.currentNode, "cancelled", reason ? { reason } : {})]
|
|
1161
|
+
};
|
|
1162
|
+
return deps.updateTask(taskId, patch, {
|
|
1163
|
+
expectedStatus: task.status,
|
|
1164
|
+
expectedCurrentNode: task.currentNode
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
951
1167
|
function findNextEdge(edges, nodes, fromNodeId, track) {
|
|
952
1168
|
const outEdges = edges.filter((e) => e.from === fromNodeId);
|
|
953
1169
|
const matchSet = trackMatchSet(track);
|
|
@@ -1172,6 +1388,7 @@ function createTaskRepo(db) {
|
|
|
1172
1388
|
if (filter?.status) match.status = filter.status;
|
|
1173
1389
|
if (filter?.track) match.track = filter.track;
|
|
1174
1390
|
if (filter?.projectId) match.projectId = filter.projectId;
|
|
1391
|
+
if (filter?.q) match.title = { $regex: escapeRegExpLiteral(filter.q), $options: "i" };
|
|
1175
1392
|
const page = filter?.page ?? 1;
|
|
1176
1393
|
const limit = filter?.limit ?? 20;
|
|
1177
1394
|
const skip = (page - 1) * limit;
|
|
@@ -1535,7 +1752,10 @@ var REFERENCE_CHECKS = {
|
|
|
1535
1752
|
task_status: { collection: "tasks", query: (v) => ({ status: v }) },
|
|
1536
1753
|
node_status: { collection: "tasks", query: (v) => ({ "dagInstance.nodeStates.status": v }) },
|
|
1537
1754
|
skill_category: { collection: "skills", query: (v) => ({ category: v }) },
|
|
1538
|
-
scope: void 0
|
|
1755
|
+
scope: void 0,
|
|
1756
|
+
// T202608260002:agent 功能类型被 agents.function 引用(reviewer/executor 为 builtin 禁删;
|
|
1757
|
+
// 检测映射为未来非 builtin 扩值留防线)
|
|
1758
|
+
agent_function: { collection: "agents", query: (v) => ({ "function": v }) }
|
|
1539
1759
|
};
|
|
1540
1760
|
function createEnumRegistryRepo(db) {
|
|
1541
1761
|
const base = createRepo(db.collection("enum_registry"), { nameField: "category" });
|
|
@@ -1637,21 +1857,21 @@ function createEnumRegistryRepo(db) {
|
|
|
1637
1857
|
}
|
|
1638
1858
|
|
|
1639
1859
|
// src/schemas/agent.ts
|
|
1640
|
-
import { z as
|
|
1860
|
+
import { z as z6 } from "zod";
|
|
1641
1861
|
|
|
1642
1862
|
// src/schemas/model-alias.ts
|
|
1643
|
-
import { z as
|
|
1863
|
+
import { z as z5 } from "zod";
|
|
1644
1864
|
var SIMING_CODE_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
1645
|
-
var ModelAliasShapeSchema =
|
|
1646
|
-
id:
|
|
1865
|
+
var ModelAliasShapeSchema = z5.object({
|
|
1866
|
+
id: z5.string().optional(),
|
|
1647
1867
|
/** siming code:创建后不可变(PUT 显式拒绝 MODEL_CODE_IMMUTABLE,非静默剥离) */
|
|
1648
|
-
code:
|
|
1868
|
+
code: z5.string().regex(SIMING_CODE_REGEX, "code \u5FC5\u987B\u4E3A kebab-case\uFF08\u5982 primary\u3001fast-model\uFF09"),
|
|
1649
1869
|
/** 展示名(如「主力模型」) */
|
|
1650
|
-
name:
|
|
1870
|
+
name: z5.string().min(1, "\u5C55\u793A\u540D\u4E0D\u80FD\u4E3A\u7A7A"),
|
|
1651
1871
|
/** 真实模型标识(如 zhipuai/glm-5.3;改此处 = 引用方全局替换生效) */
|
|
1652
|
-
realModel:
|
|
1653
|
-
createdAt:
|
|
1654
|
-
updatedAt:
|
|
1872
|
+
realModel: z5.string().min(1, "\u771F\u5B9E\u6A21\u578B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A"),
|
|
1873
|
+
createdAt: z5.date().optional(),
|
|
1874
|
+
updatedAt: z5.date().optional()
|
|
1655
1875
|
});
|
|
1656
1876
|
var ModelAliasSchema = ModelAliasShapeSchema;
|
|
1657
1877
|
var ModelAliasCreateSchema = ModelAliasShapeSchema.omit({
|
|
@@ -1659,17 +1879,18 @@ var ModelAliasCreateSchema = ModelAliasShapeSchema.omit({
|
|
|
1659
1879
|
createdAt: true,
|
|
1660
1880
|
updatedAt: true
|
|
1661
1881
|
});
|
|
1662
|
-
var ModelAliasUpdateSchema =
|
|
1663
|
-
code:
|
|
1664
|
-
name:
|
|
1665
|
-
realModel:
|
|
1882
|
+
var ModelAliasUpdateSchema = z5.object({
|
|
1883
|
+
code: z5.string().regex(SIMING_CODE_REGEX).optional(),
|
|
1884
|
+
name: z5.string().min(1).optional(),
|
|
1885
|
+
realModel: z5.string().min(1).optional()
|
|
1666
1886
|
});
|
|
1667
1887
|
var ModelAliasWithRefCountSchema = ModelAliasShapeSchema.extend({
|
|
1668
|
-
refCount:
|
|
1888
|
+
refCount: z5.number().int().min(0)
|
|
1669
1889
|
});
|
|
1670
1890
|
|
|
1671
1891
|
// src/schemas/agent.ts
|
|
1672
|
-
var AgentScopeSchema =
|
|
1892
|
+
var AgentScopeSchema = z6.enum(["global", "project"]);
|
|
1893
|
+
var AgentFunctionSchema = z6.enum(["reviewer", "executor"]);
|
|
1673
1894
|
var AgentScopeRefine = (val, ctx) => {
|
|
1674
1895
|
if (val.scope === "project" && !val.projectId) {
|
|
1675
1896
|
ctx.addIssue({
|
|
@@ -1679,48 +1900,72 @@ var AgentScopeRefine = (val, ctx) => {
|
|
|
1679
1900
|
});
|
|
1680
1901
|
}
|
|
1681
1902
|
};
|
|
1682
|
-
var AgentShapeSchema =
|
|
1683
|
-
id:
|
|
1684
|
-
name:
|
|
1685
|
-
description:
|
|
1686
|
-
systemPrompt:
|
|
1687
|
-
boundSkills:
|
|
1688
|
-
model:
|
|
1903
|
+
var AgentShapeSchema = z6.object({
|
|
1904
|
+
id: z6.string().optional(),
|
|
1905
|
+
name: z6.string(),
|
|
1906
|
+
description: z6.string(),
|
|
1907
|
+
systemPrompt: z6.string(),
|
|
1908
|
+
boundSkills: z6.array(z6.string()).default([]),
|
|
1909
|
+
model: z6.string(),
|
|
1689
1910
|
/** 资产版本(N015 F13):install/export 版本对比依据;default 仅读侧兜底(createRepo 不做读侧解析,存量回填走 M6 迁移) */
|
|
1690
|
-
version:
|
|
1691
|
-
tools:
|
|
1692
|
-
permissions:
|
|
1911
|
+
version: z6.string().default("1.0.0"),
|
|
1912
|
+
tools: z6.array(z6.string()).default([]),
|
|
1913
|
+
permissions: z6.array(z6.string()).default([]),
|
|
1914
|
+
/** 引用文档集合(可选——存量单文件资产无此字段,行为与升级前一致) */
|
|
1915
|
+
references: createReferencesSchema(AGENT_MAIN_DOC).optional(),
|
|
1693
1916
|
/** 作用域:global=跨项目复用(默认,仅作存量迁移读侧兜底)/ project=项目专用(N012 D3) */
|
|
1694
1917
|
scope: AgentScopeSchema.default("global"),
|
|
1918
|
+
/** 功能类型(T202608260002):default 仅创建侧归一——toEntity 直转不经 Zod parse,读侧归一在 repo 层 + 回填走 M12 */
|
|
1919
|
+
"function": AgentFunctionSchema.default("executor"),
|
|
1695
1920
|
/** 仅 scope=project 时必填且为合法 ObjectId hex(superRefine 保证) */
|
|
1696
|
-
projectId:
|
|
1697
|
-
|
|
1698
|
-
|
|
1921
|
+
projectId: z6.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
|
|
1922
|
+
/**
|
|
1923
|
+
* T202608290001 资产启停(可选——存量无此字段 = 启用,零迁移)。
|
|
1924
|
+
* 全局判断规则:enabled === false 即停用,true/缺失均启用。
|
|
1925
|
+
* 唯一写入口是专用启停端点(POST /:name/enabled)——Create/Update/Copy 均不含此字段。
|
|
1926
|
+
*/
|
|
1927
|
+
enabled: z6.boolean().optional(),
|
|
1928
|
+
createdAt: z6.date().optional(),
|
|
1929
|
+
updatedAt: z6.date().optional()
|
|
1699
1930
|
});
|
|
1700
|
-
var
|
|
1931
|
+
var agentAggregateLimitRefine = (val, ctx) => {
|
|
1932
|
+
const totalChars = (val.systemPrompt?.length ?? 0) + sumReferenceContentChars(val.references);
|
|
1933
|
+
if (totalChars > REFERENCE_LIMITS.maxTotalChars) {
|
|
1934
|
+
ctx.addIssue({
|
|
1935
|
+
code: "custom",
|
|
1936
|
+
path: ["references"],
|
|
1937
|
+
message: `\u4E3B\u6587\u6863\u4E0E\u5F15\u7528\u6587\u6863\u5185\u5BB9\u603B\u91CF ${totalChars} \u5B57\u7B26\u8D85\u51FA\u805A\u5408\u4E0A\u9650 ${REFERENCE_LIMITS.maxTotalChars}`
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
var AgentSchema = AgentShapeSchema.superRefine(AgentScopeRefine).superRefine(agentAggregateLimitRefine);
|
|
1701
1942
|
var AgentCreateSchema = AgentShapeSchema.omit({
|
|
1702
1943
|
id: true,
|
|
1703
1944
|
createdAt: true,
|
|
1704
|
-
updatedAt: true
|
|
1705
|
-
|
|
1945
|
+
updatedAt: true,
|
|
1946
|
+
enabled: true
|
|
1947
|
+
}).extend({ scope: AgentScopeSchema }).extend({ model: z6.string().regex(SIMING_CODE_REGEX, "model \u5FC5\u987B\u662F siming code\uFF08kebab-case\uFF09") }).extend({ "function": AgentFunctionSchema.default("executor") }).superRefine(AgentScopeRefine).superRefine(agentAggregateLimitRefine);
|
|
1706
1948
|
var AgentUpdateSchema = AgentShapeSchema.omit({
|
|
1707
1949
|
id: true,
|
|
1708
1950
|
createdAt: true,
|
|
1709
|
-
updatedAt: true
|
|
1951
|
+
updatedAt: true,
|
|
1952
|
+
enabled: true
|
|
1710
1953
|
}).extend({
|
|
1711
|
-
boundSkills:
|
|
1954
|
+
boundSkills: z6.array(z6.string()).optional(),
|
|
1712
1955
|
// N015:version 放行(export apply 版本递增随 PUT 携带);optional 剥离实体层 default 避免 partial 空 body 注入
|
|
1713
|
-
version:
|
|
1714
|
-
tools:
|
|
1715
|
-
permissions:
|
|
1956
|
+
version: z6.string().optional(),
|
|
1957
|
+
tools: z6.array(z6.string()).optional(),
|
|
1958
|
+
permissions: z6.array(z6.string()).optional(),
|
|
1716
1959
|
scope: AgentScopeSchema.optional(),
|
|
1717
1960
|
// N013:model 收紧为 siming code 格式(存在性校验在 handler 层;undefined = 不更新该字段)
|
|
1718
|
-
model:
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1961
|
+
model: z6.string().regex(SIMING_CODE_REGEX, "model \u5FC5\u987B\u662F siming code\uFF08kebab-case\uFF09").optional(),
|
|
1962
|
+
// T202608260002:optional 剥离实体层 default——否则 PUT 空 body 会注入 executor 覆盖 reviewer
|
|
1963
|
+
"function": AgentFunctionSchema.optional()
|
|
1964
|
+
}).partial().superRefine(AgentScopeRefine).superRefine(agentAggregateLimitRefine);
|
|
1965
|
+
var AgentCopySchema = z6.object({
|
|
1966
|
+
newName: z6.string(),
|
|
1722
1967
|
newScope: AgentScopeSchema,
|
|
1723
|
-
targetProjectId:
|
|
1968
|
+
targetProjectId: z6.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
|
|
1724
1969
|
}).superRefine((val, ctx) => {
|
|
1725
1970
|
if (val.newScope === "project" && !val.targetProjectId) {
|
|
1726
1971
|
ctx.addIssue({
|
|
@@ -1731,219 +1976,415 @@ var AgentCopySchema = z5.object({
|
|
|
1731
1976
|
}
|
|
1732
1977
|
});
|
|
1733
1978
|
|
|
1979
|
+
// src/schemas/export-bundle.ts
|
|
1980
|
+
import { z as z7 } from "zod";
|
|
1981
|
+
var EXPORT_FORMAT_VERSION = "1.0";
|
|
1982
|
+
var TemplatePayloadSchema = DagTemplateSchema.omit({
|
|
1983
|
+
id: true,
|
|
1984
|
+
projectId: true,
|
|
1985
|
+
enabled: true,
|
|
1986
|
+
createdAt: true,
|
|
1987
|
+
updatedAt: true
|
|
1988
|
+
}).extend({
|
|
1989
|
+
version: z7.string(),
|
|
1990
|
+
isDefault: z7.boolean()
|
|
1991
|
+
});
|
|
1992
|
+
var SkillPayloadSchema = SkillShapeSchema.omit({
|
|
1993
|
+
id: true,
|
|
1994
|
+
projectId: true,
|
|
1995
|
+
enabled: true,
|
|
1996
|
+
createdAt: true,
|
|
1997
|
+
updatedAt: true
|
|
1998
|
+
}).extend({
|
|
1999
|
+
version: z7.string(),
|
|
2000
|
+
scope: SkillScopeSchema,
|
|
2001
|
+
references: createReferencesSchema(SKILL_MAIN_DOC)
|
|
2002
|
+
});
|
|
2003
|
+
var AgentPayloadSchema = AgentShapeSchema.omit({
|
|
2004
|
+
id: true,
|
|
2005
|
+
projectId: true,
|
|
2006
|
+
enabled: true,
|
|
2007
|
+
createdAt: true,
|
|
2008
|
+
updatedAt: true
|
|
2009
|
+
}).extend({
|
|
2010
|
+
version: z7.string(),
|
|
2011
|
+
scope: AgentScopeSchema,
|
|
2012
|
+
references: createReferencesSchema(AGENT_MAIN_DOC),
|
|
2013
|
+
boundSkills: z7.array(z7.string()),
|
|
2014
|
+
tools: z7.array(z7.string()),
|
|
2015
|
+
permissions: z7.array(z7.string()),
|
|
2016
|
+
"function": AgentFunctionSchema
|
|
2017
|
+
});
|
|
2018
|
+
var ModelAliasPayloadSchema = ModelAliasSchema.omit({
|
|
2019
|
+
id: true,
|
|
2020
|
+
createdAt: true,
|
|
2021
|
+
updatedAt: true
|
|
2022
|
+
});
|
|
2023
|
+
var ExportBundleSchema = z7.object({
|
|
2024
|
+
exportFormatVersion: z7.literal(EXPORT_FORMAT_VERSION, {
|
|
2025
|
+
error: `\u5BFC\u51FA\u683C\u5F0F\u7248\u672C\u4E0D\u652F\u6301\uFF0C\u5F53\u524D\u652F\u6301 ${EXPORT_FORMAT_VERSION}`
|
|
2026
|
+
}),
|
|
2027
|
+
exportedAt: z7.string(),
|
|
2028
|
+
/** 源环境悬空提示(§2.2:悬空 skill/agent.model 不阻断导出)。导出响应恒携带;导入入参可缺省——导入侧提示由 plan 生成,不消费本字段 */
|
|
2029
|
+
warnings: z7.array(z7.string()).optional(),
|
|
2030
|
+
template: TemplatePayloadSchema,
|
|
2031
|
+
dependencies: z7.object({
|
|
2032
|
+
skills: z7.array(SkillPayloadSchema),
|
|
2033
|
+
agents: z7.array(AgentPayloadSchema),
|
|
2034
|
+
modelAliases: z7.array(ModelAliasPayloadSchema)
|
|
2035
|
+
})
|
|
2036
|
+
});
|
|
2037
|
+
var ImportPlanRequestSchema = z7.object({
|
|
2038
|
+
targetProjectId: z7.string().regex(OBJECT_ID_HEX, "targetProjectId must be a valid ObjectId hex"),
|
|
2039
|
+
bundle: ExportBundleSchema
|
|
2040
|
+
});
|
|
2041
|
+
var ImportDecisionSchema = z7.discriminatedUnion("kind", [
|
|
2042
|
+
z7.object({
|
|
2043
|
+
kind: z7.literal("skill"),
|
|
2044
|
+
name: z7.string(),
|
|
2045
|
+
scope: SkillScopeSchema,
|
|
2046
|
+
action: z7.enum(["overwrite", "skip"])
|
|
2047
|
+
}),
|
|
2048
|
+
z7.object({
|
|
2049
|
+
kind: z7.literal("agent"),
|
|
2050
|
+
name: z7.string(),
|
|
2051
|
+
scope: AgentScopeSchema,
|
|
2052
|
+
action: z7.enum(["overwrite", "skip"])
|
|
2053
|
+
}),
|
|
2054
|
+
z7.object({
|
|
2055
|
+
kind: z7.literal("modelAlias"),
|
|
2056
|
+
name: z7.string(),
|
|
2057
|
+
action: z7.enum(["overwrite", "skip"])
|
|
2058
|
+
})
|
|
2059
|
+
]);
|
|
2060
|
+
var ImportApplyRequestSchema = ImportPlanRequestSchema.extend({
|
|
2061
|
+
/** 未列出的冲突项缺省 skip(保守默认:未决策不覆盖) */
|
|
2062
|
+
decisions: z7.array(ImportDecisionSchema).default([])
|
|
2063
|
+
});
|
|
2064
|
+
var DependencyStatusSchema = z7.enum(["create", "identical", "conflict"]);
|
|
2065
|
+
var DependencyCheckSchema = z7.object({
|
|
2066
|
+
kind: z7.enum(["skill", "agent", "modelAlias"]),
|
|
2067
|
+
name: z7.string(),
|
|
2068
|
+
/** skill/agent 携带;modelAlias 无 */
|
|
2069
|
+
scope: z7.enum(["global", "project"]).optional(),
|
|
2070
|
+
status: DependencyStatusSchema,
|
|
2071
|
+
/** status ≠ create 时目标环境现值版本(modelAlias 无 version 概念不填) */
|
|
2072
|
+
currentVersion: z7.string().optional(),
|
|
2073
|
+
importVersion: z7.string().optional(),
|
|
2074
|
+
/** status = conflict 时差异字段名清单(如 "content, references") */
|
|
2075
|
+
changeSummary: z7.string().optional(),
|
|
2076
|
+
/** 影响范围(PRD F13):global 资产影响所有项目 / project 资产仅影响所属项目 */
|
|
2077
|
+
impact: z7.enum(["global", "project"])
|
|
2078
|
+
});
|
|
2079
|
+
var ImportPlanResponseSchema = z7.object({
|
|
2080
|
+
template: z7.object({
|
|
2081
|
+
name: z7.string(),
|
|
2082
|
+
willOverwrite: z7.boolean(),
|
|
2083
|
+
currentVersion: z7.string().optional(),
|
|
2084
|
+
importVersion: z7.string(),
|
|
2085
|
+
isDefaultRequested: z7.boolean(),
|
|
2086
|
+
/** isDefault 降级后的最终生效值(F17:项目已有默认 → false) */
|
|
2087
|
+
isDefaultEffect: z7.boolean(),
|
|
2088
|
+
downgradeReason: z7.string().optional()
|
|
2089
|
+
}),
|
|
2090
|
+
dependencies: z7.array(DependencyCheckSchema),
|
|
2091
|
+
/** 非阻断提示(源环境悬空绑定、agent.model 引用缺失等) */
|
|
2092
|
+
warnings: z7.array(z7.string())
|
|
2093
|
+
});
|
|
2094
|
+
var DependencyActionSchema = z7.enum(["created", "updated", "skipped", "identical", "failed"]);
|
|
2095
|
+
var ImportApplyResponseSchema = z7.object({
|
|
2096
|
+
template: z7.object({
|
|
2097
|
+
id: z7.string(),
|
|
2098
|
+
name: z7.string(),
|
|
2099
|
+
action: z7.enum(["created", "updated"]),
|
|
2100
|
+
isDefaultEffect: z7.boolean()
|
|
2101
|
+
}),
|
|
2102
|
+
dependencies: z7.array(
|
|
2103
|
+
z7.object({
|
|
2104
|
+
kind: z7.enum(["skill", "agent", "modelAlias"]),
|
|
2105
|
+
name: z7.string(),
|
|
2106
|
+
scope: z7.enum(["global", "project"]).optional(),
|
|
2107
|
+
action: DependencyActionSchema,
|
|
2108
|
+
/** action = failed 时的错误说明 */
|
|
2109
|
+
message: z7.string().optional()
|
|
2110
|
+
})
|
|
2111
|
+
)
|
|
2112
|
+
});
|
|
2113
|
+
function decisionKey(kind, scope, name) {
|
|
2114
|
+
return `${kind}:${scope ?? ""}:${name}`;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
1734
2117
|
// src/schemas/advance.ts
|
|
1735
|
-
import { z as
|
|
1736
|
-
var AdvanceRequestSchema =
|
|
1737
|
-
note:
|
|
2118
|
+
import { z as z8 } from "zod";
|
|
2119
|
+
var AdvanceRequestSchema = z8.object({
|
|
2120
|
+
note: z8.string().optional(),
|
|
1738
2121
|
/** min(1).max(2000) 与 record summary 端点对齐(同一 $set 目标,两入口校验强度一致) */
|
|
1739
|
-
summary:
|
|
2122
|
+
summary: z8.string().min(1).max(2e3).optional()
|
|
1740
2123
|
});
|
|
1741
|
-
var ApproveRequestSchema =
|
|
1742
|
-
decision:
|
|
1743
|
-
comment:
|
|
2124
|
+
var ApproveRequestSchema = z8.object({
|
|
2125
|
+
decision: z8.enum(["approved", "rejected"]),
|
|
2126
|
+
comment: z8.string().optional()
|
|
1744
2127
|
});
|
|
1745
|
-
var TaskPublicSchema =
|
|
1746
|
-
taskId:
|
|
1747
|
-
title:
|
|
2128
|
+
var TaskPublicSchema = z8.object({
|
|
2129
|
+
taskId: z8.string(),
|
|
2130
|
+
title: z8.string(),
|
|
1748
2131
|
// T202608240003:任务类型标签(缺省 = 裸任务/旧任务;模板 PRD 调研变体判定消费)
|
|
1749
|
-
type:
|
|
1750
|
-
currentNode:
|
|
1751
|
-
currentPhase:
|
|
1752
|
-
status:
|
|
1753
|
-
pausedAt:
|
|
1754
|
-
track:
|
|
2132
|
+
type: z8.enum(TASK_TYPES).optional(),
|
|
2133
|
+
currentNode: z8.string(),
|
|
2134
|
+
currentPhase: z8.string(),
|
|
2135
|
+
status: z8.string(),
|
|
2136
|
+
pausedAt: z8.string().nullable(),
|
|
2137
|
+
track: z8.string()
|
|
1755
2138
|
});
|
|
1756
|
-
var NodeInfoSchema =
|
|
1757
|
-
nodeId:
|
|
1758
|
-
label:
|
|
1759
|
-
track:
|
|
1760
|
-
prompt:
|
|
1761
|
-
skills:
|
|
1762
|
-
upcomingPause:
|
|
2139
|
+
var NodeInfoSchema = z8.object({
|
|
2140
|
+
nodeId: z8.string(),
|
|
2141
|
+
label: z8.string(),
|
|
2142
|
+
track: z8.string(),
|
|
2143
|
+
prompt: z8.string(),
|
|
2144
|
+
skills: z8.array(z8.string()),
|
|
2145
|
+
upcomingPause: z8.string().nullable()
|
|
1763
2146
|
});
|
|
1764
|
-
var AdvanceResponseSchema =
|
|
1765
|
-
|
|
1766
|
-
status:
|
|
2147
|
+
var AdvanceResponseSchema = z8.discriminatedUnion("status", [
|
|
2148
|
+
z8.object({
|
|
2149
|
+
status: z8.literal("advanced"),
|
|
1767
2150
|
task: TaskPublicSchema,
|
|
1768
2151
|
nextNode: NodeInfoSchema
|
|
1769
2152
|
}),
|
|
1770
|
-
|
|
1771
|
-
status:
|
|
2153
|
+
z8.object({
|
|
2154
|
+
status: z8.literal("paused"),
|
|
1772
2155
|
task: TaskPublicSchema,
|
|
1773
2156
|
pausePoint: EdgePausePointSchema,
|
|
1774
|
-
guidance:
|
|
2157
|
+
guidance: z8.string()
|
|
1775
2158
|
}),
|
|
1776
|
-
|
|
1777
|
-
status:
|
|
2159
|
+
z8.object({
|
|
2160
|
+
status: z8.literal("completed"),
|
|
1778
2161
|
task: TaskPublicSchema
|
|
1779
2162
|
})
|
|
1780
2163
|
]);
|
|
1781
|
-
var ApproveResponseSchema =
|
|
1782
|
-
|
|
1783
|
-
status:
|
|
2164
|
+
var ApproveResponseSchema = z8.discriminatedUnion("status", [
|
|
2165
|
+
z8.object({
|
|
2166
|
+
status: z8.literal("advanced"),
|
|
1784
2167
|
task: TaskPublicSchema,
|
|
1785
2168
|
nextNode: NodeInfoSchema
|
|
1786
2169
|
}),
|
|
1787
|
-
|
|
1788
|
-
status:
|
|
2170
|
+
z8.object({
|
|
2171
|
+
status: z8.literal("rejected"),
|
|
1789
2172
|
task: TaskPublicSchema,
|
|
1790
|
-
guidance:
|
|
2173
|
+
guidance: z8.string()
|
|
1791
2174
|
}),
|
|
1792
2175
|
// 「completed」分支当前引擎不可达(approve 从 paused 流转必有下一节点,技术方案 §2.2
|
|
1793
2176
|
// 已删 paused→completed 直达路径)——按方案 §2.1 契约保留作前向兼容预留
|
|
1794
|
-
|
|
1795
|
-
status:
|
|
2177
|
+
z8.object({
|
|
2178
|
+
status: z8.literal("completed"),
|
|
1796
2179
|
task: TaskPublicSchema
|
|
1797
2180
|
})
|
|
1798
2181
|
]);
|
|
1799
|
-
var PauseRequestSchema =
|
|
1800
|
-
reason:
|
|
2182
|
+
var PauseRequestSchema = z8.object({
|
|
2183
|
+
reason: z8.string().optional()
|
|
1801
2184
|
});
|
|
1802
|
-
var ResumeRequestSchema =
|
|
1803
|
-
decision:
|
|
2185
|
+
var ResumeRequestSchema = z8.object({
|
|
2186
|
+
decision: z8.string().optional()
|
|
2187
|
+
});
|
|
2188
|
+
var CancelRequestSchema = z8.object({
|
|
2189
|
+
reason: z8.string().min(1).max(500).optional()
|
|
1804
2190
|
});
|
|
1805
2191
|
|
|
1806
2192
|
// src/schemas/task-doc.ts
|
|
1807
|
-
import { z as
|
|
1808
|
-
var TaskContextSchema =
|
|
1809
|
-
task:
|
|
1810
|
-
taskId:
|
|
1811
|
-
title:
|
|
2193
|
+
import { z as z9 } from "zod";
|
|
2194
|
+
var TaskContextSchema = z9.object({
|
|
2195
|
+
task: z9.object({
|
|
2196
|
+
taskId: z9.string(),
|
|
2197
|
+
title: z9.string(),
|
|
1812
2198
|
// T202608240003:与 TaskPublicSchema 同步(toTaskPublic 运行时已产出,schema 声明漂移会导致 typed 消费方 strip 掉 type)
|
|
1813
|
-
type:
|
|
1814
|
-
currentNode:
|
|
1815
|
-
currentPhase:
|
|
1816
|
-
status:
|
|
1817
|
-
pausedAt:
|
|
1818
|
-
track:
|
|
2199
|
+
type: z9.enum(TASK_TYPES).optional(),
|
|
2200
|
+
currentNode: z9.string(),
|
|
2201
|
+
currentPhase: z9.string(),
|
|
2202
|
+
status: z9.string(),
|
|
2203
|
+
pausedAt: z9.string().nullable(),
|
|
2204
|
+
track: z9.string()
|
|
1819
2205
|
}),
|
|
1820
|
-
currentNode:
|
|
1821
|
-
nodeId:
|
|
1822
|
-
label:
|
|
1823
|
-
phase:
|
|
1824
|
-
track:
|
|
2206
|
+
currentNode: z9.object({
|
|
2207
|
+
nodeId: z9.string(),
|
|
2208
|
+
label: z9.string(),
|
|
2209
|
+
phase: z9.string(),
|
|
2210
|
+
track: z9.string(),
|
|
1825
2211
|
/** renderPrompt 渲染后(AI 推进任务的直接输入) */
|
|
1826
|
-
prompt:
|
|
1827
|
-
skills:
|
|
2212
|
+
prompt: z9.string(),
|
|
2213
|
+
skills: z9.array(z9.string()),
|
|
1828
2214
|
/** 完成当前节点后出边上的暂停点描述(经 findNextEdge 按轨选边) */
|
|
1829
|
-
upcomingPause:
|
|
2215
|
+
upcomingPause: z9.string().nullable()
|
|
1830
2216
|
}).nullable(),
|
|
1831
|
-
pausedAtEdge:
|
|
1832
|
-
from:
|
|
1833
|
-
to:
|
|
2217
|
+
pausedAtEdge: z9.object({
|
|
2218
|
+
from: z9.string(),
|
|
2219
|
+
to: z9.string(),
|
|
1834
2220
|
pausePoint: EdgePausePointBaseSchema
|
|
1835
2221
|
}).nullable(),
|
|
1836
2222
|
/** 全节点实时状态清单 */
|
|
1837
|
-
nodes:
|
|
1838
|
-
nodeId:
|
|
1839
|
-
label:
|
|
1840
|
-
status:
|
|
1841
|
-
enteredAt:
|
|
1842
|
-
completedAt:
|
|
2223
|
+
nodes: z9.array(z9.object({
|
|
2224
|
+
nodeId: z9.string(),
|
|
2225
|
+
label: z9.string(),
|
|
2226
|
+
status: z9.string(),
|
|
2227
|
+
enteredAt: z9.date().nullable(),
|
|
2228
|
+
completedAt: z9.date().nullable()
|
|
1843
2229
|
})),
|
|
1844
2230
|
/** N020 D1:任务级结构化文档(未写时 null) */
|
|
1845
2231
|
taskDoc: TaskDocContentSchema.nullable(),
|
|
1846
2232
|
/** N020 D1:节点执行记录(仅含已写记录的 nodeId) */
|
|
1847
|
-
nodeRecords:
|
|
2233
|
+
nodeRecords: z9.record(z9.string(), NodeRecordSchema),
|
|
1848
2234
|
/** N020 D1:跨节点累积素材(架构信息收集条目) */
|
|
1849
|
-
archNotes:
|
|
2235
|
+
archNotes: z9.array(ArchNoteSchema)
|
|
1850
2236
|
});
|
|
1851
|
-
var TaskSummarySchema =
|
|
1852
|
-
taskId:
|
|
1853
|
-
title:
|
|
2237
|
+
var TaskSummarySchema = z9.object({
|
|
2238
|
+
taskId: z9.string(),
|
|
2239
|
+
title: z9.string(),
|
|
1854
2240
|
// T202608240003:任务类型标签(缺省 = 裸任务/旧任务)
|
|
1855
|
-
type:
|
|
1856
|
-
projectId:
|
|
1857
|
-
dagTemplateId:
|
|
1858
|
-
currentNode:
|
|
1859
|
-
currentPhase:
|
|
1860
|
-
status:
|
|
1861
|
-
pausedAt:
|
|
1862
|
-
track:
|
|
1863
|
-
progress:
|
|
1864
|
-
completed:
|
|
1865
|
-
total:
|
|
2241
|
+
type: z9.enum(TASK_TYPES).optional(),
|
|
2242
|
+
projectId: z9.string(),
|
|
2243
|
+
dagTemplateId: z9.string(),
|
|
2244
|
+
currentNode: z9.string(),
|
|
2245
|
+
currentPhase: z9.string(),
|
|
2246
|
+
status: z9.string(),
|
|
2247
|
+
pausedAt: z9.string().nullable(),
|
|
2248
|
+
track: z9.string(),
|
|
2249
|
+
progress: z9.object({
|
|
2250
|
+
completed: z9.number(),
|
|
2251
|
+
total: z9.number()
|
|
1866
2252
|
}),
|
|
1867
|
-
createdAt:
|
|
1868
|
-
updatedAt:
|
|
2253
|
+
createdAt: z9.date().optional(),
|
|
2254
|
+
updatedAt: z9.date().optional()
|
|
2255
|
+
});
|
|
2256
|
+
|
|
2257
|
+
// src/schemas/write-ack.ts
|
|
2258
|
+
import { z as z10 } from "zod";
|
|
2259
|
+
var WRITE_ACK_ENTRY_KINDS = ["check", "artifact", "decision", "confirmation", "archnote"];
|
|
2260
|
+
var WriteAckEntryKindSchema = z10.enum(WRITE_ACK_ENTRY_KINDS);
|
|
2261
|
+
var EchoItemSchema = z10.object({
|
|
2262
|
+
path: z10.string(),
|
|
2263
|
+
excerpt: z10.string()
|
|
2264
|
+
});
|
|
2265
|
+
var TaskProgressSchema = z10.object({
|
|
2266
|
+
completed: z10.number().int(),
|
|
2267
|
+
total: z10.number().int()
|
|
2268
|
+
});
|
|
2269
|
+
var TaskProgressPublicSchema = TaskPublicSchema.extend({
|
|
2270
|
+
projectId: z10.string(),
|
|
2271
|
+
progress: TaskProgressSchema
|
|
2272
|
+
});
|
|
2273
|
+
var WriteAckSchema = z10.object({
|
|
2274
|
+
taskId: z10.string(),
|
|
2275
|
+
/** 本次写入触及的字段点路径(如 doc.what / nodeRecords.PRD.checks) */
|
|
2276
|
+
updated: z10.array(z10.string()),
|
|
2277
|
+
/** 新建条目标识($push 五类对象条目;无新条目的端点缺省) */
|
|
2278
|
+
entry: z10.object({
|
|
2279
|
+
id: z10.string(),
|
|
2280
|
+
kind: WriteAckEntryKindSchema
|
|
2281
|
+
}).optional(),
|
|
2282
|
+
/** 写入内容回显(触及字段逐条;无值可回显的端点可缺省) */
|
|
2283
|
+
echo: z10.array(EchoItemSchema).optional(),
|
|
2284
|
+
updatedAt: z10.date(),
|
|
2285
|
+
task: TaskProgressPublicSchema
|
|
2286
|
+
});
|
|
2287
|
+
var CreateTaskResponseSchema = z10.object({
|
|
2288
|
+
task: TaskProgressPublicSchema,
|
|
2289
|
+
firstNode: NodeInfoSchema
|
|
1869
2290
|
});
|
|
2291
|
+
function taskProgress(task) {
|
|
2292
|
+
const states = Object.values(task.dagInstance.nodeStates);
|
|
2293
|
+
const completed = states.filter((s) => s?.status === "completed").length;
|
|
2294
|
+
const skipped = states.filter((s) => s?.status === "skipped").length;
|
|
2295
|
+
return { completed, total: task.dagInstance.nodes.length - skipped };
|
|
2296
|
+
}
|
|
2297
|
+
function excerpt(value, max = 120) {
|
|
2298
|
+
const chars = Array.from(value);
|
|
2299
|
+
if (chars.length <= max) return value;
|
|
2300
|
+
return `${chars.slice(0, max - 1).join("")}\u2026`;
|
|
2301
|
+
}
|
|
2302
|
+
var CONTEXT_VIEWS = ["basic", "full"];
|
|
2303
|
+
var ContextViewSchema = z10.enum(CONTEXT_VIEWS);
|
|
1870
2304
|
|
|
1871
2305
|
// src/schemas/project.ts
|
|
1872
|
-
import { z as
|
|
1873
|
-
var ProjectStatusSchema =
|
|
1874
|
-
var ProjectSchema =
|
|
1875
|
-
id:
|
|
1876
|
-
key:
|
|
1877
|
-
name:
|
|
1878
|
-
description:
|
|
2306
|
+
import { z as z11 } from "zod";
|
|
2307
|
+
var ProjectStatusSchema = z11.enum(["active", "archived"]);
|
|
2308
|
+
var ProjectSchema = z11.object({
|
|
2309
|
+
id: z11.string().optional(),
|
|
2310
|
+
key: z11.string().regex(/^[a-z0-9][a-z0-9-]{1,30}$/, "key \u5FC5\u987B\u4E3A kebab-case\uFF082-31 \u5B57\u7B26\uFF09"),
|
|
2311
|
+
name: z11.string().min(1).max(50),
|
|
2312
|
+
description: z11.string().max(500).optional(),
|
|
1879
2313
|
status: ProjectStatusSchema.default("active"),
|
|
1880
|
-
createdAt:
|
|
1881
|
-
updatedAt:
|
|
2314
|
+
createdAt: z11.date().optional(),
|
|
2315
|
+
updatedAt: z11.date().optional()
|
|
1882
2316
|
});
|
|
1883
|
-
var ProjectCreateSchema =
|
|
1884
|
-
key:
|
|
1885
|
-
name:
|
|
1886
|
-
description:
|
|
2317
|
+
var ProjectCreateSchema = z11.object({
|
|
2318
|
+
key: z11.string().regex(/^[a-z0-9][a-z0-9-]{1,30}$/, "key \u5FC5\u987B\u4E3A kebab-case\uFF082-31 \u5B57\u7B26\uFF09").optional(),
|
|
2319
|
+
name: z11.string().min(1).max(50),
|
|
2320
|
+
description: z11.string().max(500).optional()
|
|
1887
2321
|
});
|
|
1888
|
-
var ProjectUpdateSchema =
|
|
1889
|
-
name:
|
|
1890
|
-
description:
|
|
2322
|
+
var ProjectUpdateSchema = z11.object({
|
|
2323
|
+
name: z11.string().min(1).max(50).optional(),
|
|
2324
|
+
description: z11.string().max(500).optional(),
|
|
1891
2325
|
status: ProjectStatusSchema.optional()
|
|
1892
2326
|
});
|
|
1893
2327
|
|
|
1894
2328
|
// src/schemas/enum-registry.ts
|
|
1895
|
-
import { z as
|
|
1896
|
-
var EnumRegistryCategorySchema =
|
|
2329
|
+
import { z as z12 } from "zod";
|
|
2330
|
+
var EnumRegistryCategorySchema = z12.enum([
|
|
1897
2331
|
"dag_phase",
|
|
1898
2332
|
"dag_track",
|
|
1899
2333
|
"pause_type",
|
|
1900
2334
|
"task_status",
|
|
1901
2335
|
"node_status",
|
|
1902
2336
|
"skill_category",
|
|
1903
|
-
"scope"
|
|
2337
|
+
"scope",
|
|
2338
|
+
"agent_function"
|
|
1904
2339
|
]);
|
|
1905
|
-
var EnumEntrySchema =
|
|
2340
|
+
var EnumEntrySchema = z12.object({
|
|
1906
2341
|
/** 领域层存库的原始枚举值(不可臆造;builtin 值 engine/DB 强依赖;kebab 约束与 skill name 对齐,保证 DELETE /entries/:value 可寻址) */
|
|
1907
|
-
value:
|
|
2342
|
+
value: z12.string().regex(/^[a-z0-9][a-z0-9_-]*$/, "value \u4EC5\u652F\u6301\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26\uFF08kebab-case\uFF09"),
|
|
1908
2343
|
/** 展示文案(消费端下拉直接取用) */
|
|
1909
|
-
label:
|
|
2344
|
+
label: z12.string().min(1, "label \u4E0D\u80FD\u4E3A\u7A7A"),
|
|
1910
2345
|
/** 可选语义色 token(如 --success / --warning),消费端映射到 UI */
|
|
1911
|
-
color:
|
|
2346
|
+
color: z12.string().optional(),
|
|
1912
2347
|
/** 下拉排序(升序) */
|
|
1913
|
-
order:
|
|
2348
|
+
order: z12.number().int().default(0),
|
|
1914
2349
|
/** 内置值:禁删 + label/color 可改但 value 不可改(D8);seed 时标记 */
|
|
1915
|
-
builtin:
|
|
2350
|
+
builtin: z12.boolean().default(false),
|
|
1916
2351
|
/** 启停:false = 下拉禁用展示(历史数据仍可读,新选择不可用) */
|
|
1917
|
-
active:
|
|
2352
|
+
active: z12.boolean().default(true)
|
|
1918
2353
|
});
|
|
1919
|
-
var EnumRegistrySchema =
|
|
1920
|
-
id:
|
|
2354
|
+
var EnumRegistrySchema = z12.object({
|
|
2355
|
+
id: z12.string().optional(),
|
|
1921
2356
|
category: EnumRegistryCategorySchema,
|
|
1922
|
-
entries:
|
|
1923
|
-
updatedAt:
|
|
2357
|
+
entries: z12.array(EnumEntrySchema).default([]),
|
|
2358
|
+
updatedAt: z12.date().optional()
|
|
1924
2359
|
});
|
|
1925
|
-
var EnumRegistryUpdateSchema =
|
|
1926
|
-
entries:
|
|
2360
|
+
var EnumRegistryUpdateSchema = z12.object({
|
|
2361
|
+
entries: z12.array(EnumEntrySchema)
|
|
2362
|
+
});
|
|
2363
|
+
|
|
2364
|
+
// src/schemas/asset-enabled.ts
|
|
2365
|
+
import { z as z13 } from "zod";
|
|
2366
|
+
var AssetSetEnabledSchema = z13.object({
|
|
2367
|
+
enabled: z13.boolean()
|
|
1927
2368
|
});
|
|
1928
2369
|
|
|
1929
2370
|
// src/config/server-config.ts
|
|
1930
|
-
import { z as
|
|
1931
|
-
var SimingLogLevelSchema =
|
|
1932
|
-
var SimingConfigSchema =
|
|
1933
|
-
mongoUri:
|
|
1934
|
-
port:
|
|
2371
|
+
import { z as z14 } from "zod";
|
|
2372
|
+
var SimingLogLevelSchema = z14.enum(["debug", "info", "warn", "error"]);
|
|
2373
|
+
var SimingConfigSchema = z14.object({
|
|
2374
|
+
mongoUri: z14.string().min(1).default("mongodb://localhost:27017"),
|
|
2375
|
+
port: z14.coerce.number().int().min(1).max(65535).default(7777),
|
|
1935
2376
|
/**
|
|
1936
2377
|
* N018 D8:默认仅绑定本机回环(安全默认);远程/局域接入(F7)显式配置 host。
|
|
1937
2378
|
* 超出 PRD V1 配置清单的新增项,依据见技术方案 §3.4「超范围扩展标注」。
|
|
1938
2379
|
*/
|
|
1939
|
-
host:
|
|
2380
|
+
host: z14.string().min(1).default("127.0.0.1"),
|
|
1940
2381
|
logLevel: SimingLogLevelSchema.default("info"),
|
|
1941
2382
|
/**
|
|
1942
2383
|
* F6 预留字段(V2 远程部署):宽松形态——出现即接受、形状不校验、运行时不读取。
|
|
1943
2384
|
* 严格满足 PRD「配置中出现预留字段时不报错」;V2 落地时收紧为类型化 schema。
|
|
1944
2385
|
*/
|
|
1945
|
-
auth:
|
|
1946
|
-
cors:
|
|
2386
|
+
auth: z14.unknown().optional(),
|
|
2387
|
+
cors: z14.unknown().optional()
|
|
1947
2388
|
});
|
|
1948
2389
|
var SIMING_CONFIG_KEYS = ["mongoUri", "port", "host", "logLevel"];
|
|
1949
2390
|
var SIMING_CONFIG_ENV_KEYS = {
|
|
@@ -1953,6 +2394,19 @@ var SIMING_CONFIG_ENV_KEYS = {
|
|
|
1953
2394
|
logLevel: "SIMING_LOG_LEVEL"
|
|
1954
2395
|
};
|
|
1955
2396
|
|
|
2397
|
+
// src/version-bump.ts
|
|
2398
|
+
function tryBumpPatch(version) {
|
|
2399
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
2400
|
+
if (!m || m[1] === void 0 || m[2] === void 0 || m[3] === void 0) return null;
|
|
2401
|
+
return `${m[1]}.${m[2]}.${String(Number(m[3]) + 1)}`;
|
|
2402
|
+
}
|
|
2403
|
+
function bumpPatch(version) {
|
|
2404
|
+
return tryBumpPatch(version) ?? "1.0.1";
|
|
2405
|
+
}
|
|
2406
|
+
function bumpPatchOrPassthrough(version) {
|
|
2407
|
+
return tryBumpPatch(version) ?? version;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
1956
2410
|
// src/task/entry-id.ts
|
|
1957
2411
|
var ENTRY_ID_LENGTH = 6;
|
|
1958
2412
|
var BASE36_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -1968,8 +2422,397 @@ function generateEntryId(existing) {
|
|
|
1968
2422
|
throw new Error(`entry id generation failed after 32 attempts (existing: ${existing.length})`);
|
|
1969
2423
|
}
|
|
1970
2424
|
|
|
2425
|
+
// src/template-transfer/payload.ts
|
|
2426
|
+
function toTemplatePayload(tpl) {
|
|
2427
|
+
const { id: _id, projectId: _projectId, enabled: _enabled, createdAt: _ca, updatedAt: _ua, ...rest } = tpl;
|
|
2428
|
+
return { ...rest };
|
|
2429
|
+
}
|
|
2430
|
+
function toSkillPayload(skill) {
|
|
2431
|
+
const { id: _id, projectId: _projectId, enabled: _enabled, createdAt: _ca, updatedAt: _ua, references, ...rest } = skill;
|
|
2432
|
+
return { ...rest, references: references ?? [] };
|
|
2433
|
+
}
|
|
2434
|
+
function toAgentPayload(agent) {
|
|
2435
|
+
const { id: _id, projectId: _projectId, enabled: _enabled, createdAt: _ca, updatedAt: _ua, references, ...rest } = agent;
|
|
2436
|
+
return { ...rest, references: references ?? [] };
|
|
2437
|
+
}
|
|
2438
|
+
function toModelAliasPayload(alias) {
|
|
2439
|
+
const { id: _id, createdAt: _ca, updatedAt: _ua, ...rest } = alias;
|
|
2440
|
+
return { ...rest };
|
|
2441
|
+
}
|
|
2442
|
+
function diffReferences(payload, entity) {
|
|
2443
|
+
const a = payload ?? [];
|
|
2444
|
+
const b = entity ?? [];
|
|
2445
|
+
return a.length === b.length && a.every((doc, i) => doc.path === b[i]?.path && doc.content === b[i]?.content);
|
|
2446
|
+
}
|
|
2447
|
+
function diffSkillPayload(payload, entity) {
|
|
2448
|
+
const diffs = [];
|
|
2449
|
+
if (entity.description !== payload.description) diffs.push("description");
|
|
2450
|
+
if (entity.content !== payload.content) diffs.push("content");
|
|
2451
|
+
if (entity.category !== payload.category) diffs.push("category");
|
|
2452
|
+
if (entity.version !== payload.version) diffs.push("version");
|
|
2453
|
+
if (!diffReferences(payload.references, entity.references)) diffs.push("references");
|
|
2454
|
+
return diffs;
|
|
2455
|
+
}
|
|
2456
|
+
function diffAgentPayload(payload, entity) {
|
|
2457
|
+
const diffs = [];
|
|
2458
|
+
if (entity.description !== payload.description) diffs.push("description");
|
|
2459
|
+
if (entity.systemPrompt !== payload.systemPrompt) diffs.push("systemPrompt");
|
|
2460
|
+
if (entity.model !== payload.model) diffs.push("model");
|
|
2461
|
+
if (entity.version !== payload.version) diffs.push("version");
|
|
2462
|
+
if (entity.function !== payload.function) diffs.push("function");
|
|
2463
|
+
const boundA = entity.boundSkills ?? [];
|
|
2464
|
+
if (boundA.length !== payload.boundSkills.length || boundA.some((s, i) => s !== payload.boundSkills[i])) diffs.push("boundSkills");
|
|
2465
|
+
const toolsA = entity.tools ?? [];
|
|
2466
|
+
if (toolsA.length !== payload.tools.length || toolsA.some((t, i) => t !== payload.tools[i])) diffs.push("tools");
|
|
2467
|
+
const permsA = entity.permissions ?? [];
|
|
2468
|
+
if (permsA.length !== payload.permissions.length || permsA.some((p, i) => p !== payload.permissions[i])) diffs.push("permissions");
|
|
2469
|
+
if (!diffReferences(payload.references, entity.references)) diffs.push("references");
|
|
2470
|
+
return diffs;
|
|
2471
|
+
}
|
|
2472
|
+
function diffModelAliasPayload(payload, entity) {
|
|
2473
|
+
const diffs = [];
|
|
2474
|
+
if (entity.name !== payload.name) diffs.push("name");
|
|
2475
|
+
if (entity.realModel !== payload.realModel) diffs.push("realModel");
|
|
2476
|
+
return diffs;
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
// src/template-transfer/export-compose.ts
|
|
2480
|
+
async function composeExportBundle(deps, templateId) {
|
|
2481
|
+
const tpl = await deps.templateRepo.getById(templateId);
|
|
2482
|
+
if (!tpl) throw new NotFoundError("dag-template", templateId);
|
|
2483
|
+
const warnings = [];
|
|
2484
|
+
const directSkillNames = [...new Set(tpl.nodes.flatMap((node) => node.skills))];
|
|
2485
|
+
const [globalAgents, projectAgents] = await Promise.all([
|
|
2486
|
+
deps.agentRepo.listAgentsByScopeFilter("global", void 0, { includeDisabled: true }),
|
|
2487
|
+
deps.agentRepo.listAgentsByScopeFilter("project", tpl.projectId, { includeDisabled: true })
|
|
2488
|
+
]);
|
|
2489
|
+
const directSet = new Set(directSkillNames);
|
|
2490
|
+
const agents = [...globalAgents, ...projectAgents].filter(
|
|
2491
|
+
(agent) => (agent.boundSkills ?? []).some((name) => directSet.has(name))
|
|
2492
|
+
);
|
|
2493
|
+
const allSkillNames = [.../* @__PURE__ */ new Set([...directSkillNames, ...agents.flatMap((a) => a.boundSkills ?? [])])];
|
|
2494
|
+
const skillMap = await deps.skillRepo.listSkillsByNames(allSkillNames, "project", tpl.projectId);
|
|
2495
|
+
for (const name of allSkillNames) {
|
|
2496
|
+
if (!skillMap.has(name)) {
|
|
2497
|
+
warnings.push(`SKILL "${name}" \u5728\u6E90\u73AF\u5883\u4E2D\u4E0D\u5B58\u5728\uFF08\u7ED1\u5B9A\u60AC\u7A7A\uFF09\uFF0C\u672A\u5305\u542B\u5728\u5BFC\u51FA\u4F9D\u8D56\u4E2D`);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
const modelCodes = [...new Set(agents.map((a) => a.model))];
|
|
2501
|
+
const modelAliases = [];
|
|
2502
|
+
for (const code of modelCodes) {
|
|
2503
|
+
const alias = await deps.modelAliasRepo.getByCode(code);
|
|
2504
|
+
if (alias) {
|
|
2505
|
+
modelAliases.push(alias);
|
|
2506
|
+
} else {
|
|
2507
|
+
warnings.push(`\u6A21\u578B\u6620\u5C04 "${code}" \u5728\u6E90\u73AF\u5883\u4E2D\u4E0D\u5B58\u5728\uFF08agent.model \u60AC\u7A7A\uFF09\uFF0C\u672A\u5305\u542B\u5728\u5BFC\u51FA\u4F9D\u8D56\u4E2D`);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
return {
|
|
2511
|
+
exportFormatVersion: EXPORT_FORMAT_VERSION,
|
|
2512
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2513
|
+
warnings,
|
|
2514
|
+
template: toTemplatePayload(tpl),
|
|
2515
|
+
dependencies: {
|
|
2516
|
+
skills: [...skillMap.values()].map(toSkillPayload),
|
|
2517
|
+
agents: agents.map(toAgentPayload),
|
|
2518
|
+
modelAliases: modelAliases.map(toModelAliasPayload)
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// src/template-transfer/import-plan.ts
|
|
2524
|
+
function statusOf(diffs) {
|
|
2525
|
+
if (diffs === null) return { status: "create" };
|
|
2526
|
+
if (diffs.length === 0) return { status: "identical" };
|
|
2527
|
+
return { status: "conflict", changeSummary: diffs.join(", ") };
|
|
2528
|
+
}
|
|
2529
|
+
async function hasProjectDefault(deps, targetProjectId) {
|
|
2530
|
+
const hit = await deps.templateRepo._collection.findOne(
|
|
2531
|
+
{ projectId: targetProjectId, isDefault: true },
|
|
2532
|
+
{ projection: { _id: 1 } }
|
|
2533
|
+
);
|
|
2534
|
+
return hit !== null;
|
|
2535
|
+
}
|
|
2536
|
+
async function findTemplateByName(deps, targetProjectId, name) {
|
|
2537
|
+
const rows = await deps.templateRepo.listDagTemplates({ projectId: targetProjectId, name, includeDisabled: true });
|
|
2538
|
+
const row = rows[0];
|
|
2539
|
+
return row && row.id ? { id: row.id, version: row.version } : null;
|
|
2540
|
+
}
|
|
2541
|
+
async function buildImportPlan(deps, targetProjectId, bundle) {
|
|
2542
|
+
const warnings = [];
|
|
2543
|
+
const existing = await findTemplateByName(deps, targetProjectId, bundle.template.name);
|
|
2544
|
+
const projectHasDefault = await hasProjectDefault(deps, targetProjectId);
|
|
2545
|
+
const isDefaultEffect = bundle.template.isDefault && !projectHasDefault;
|
|
2546
|
+
const skillChecks = [];
|
|
2547
|
+
for (const payload of bundle.dependencies.skills) {
|
|
2548
|
+
const entity = await deps.skillRepo.getByNameScoped(
|
|
2549
|
+
payload.name,
|
|
2550
|
+
payload.scope,
|
|
2551
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2552
|
+
{ includeDisabled: true }
|
|
2553
|
+
);
|
|
2554
|
+
const { status, changeSummary } = statusOf(entity ? diffSkillPayload(payload, entity) : null);
|
|
2555
|
+
skillChecks.push({
|
|
2556
|
+
kind: "skill",
|
|
2557
|
+
name: payload.name,
|
|
2558
|
+
scope: payload.scope,
|
|
2559
|
+
status,
|
|
2560
|
+
...entity ? { currentVersion: entity.version } : {},
|
|
2561
|
+
importVersion: payload.version,
|
|
2562
|
+
...changeSummary ? { changeSummary } : {},
|
|
2563
|
+
impact: payload.scope
|
|
2564
|
+
});
|
|
2565
|
+
}
|
|
2566
|
+
const agentChecks = [];
|
|
2567
|
+
for (const payload of bundle.dependencies.agents) {
|
|
2568
|
+
const entity = await deps.agentRepo.getByNameScoped(
|
|
2569
|
+
payload.name,
|
|
2570
|
+
payload.scope,
|
|
2571
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2572
|
+
{ includeDisabled: true }
|
|
2573
|
+
);
|
|
2574
|
+
const { status, changeSummary } = statusOf(entity ? diffAgentPayload(payload, entity) : null);
|
|
2575
|
+
agentChecks.push({
|
|
2576
|
+
kind: "agent",
|
|
2577
|
+
name: payload.name,
|
|
2578
|
+
scope: payload.scope,
|
|
2579
|
+
status,
|
|
2580
|
+
...entity ? { currentVersion: entity.version } : {},
|
|
2581
|
+
importVersion: payload.version,
|
|
2582
|
+
...changeSummary ? { changeSummary } : {},
|
|
2583
|
+
impact: payload.scope
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
const aliasChecks = [];
|
|
2587
|
+
for (const payload of bundle.dependencies.modelAliases) {
|
|
2588
|
+
const entity = await deps.modelAliasRepo.getByCode(payload.code);
|
|
2589
|
+
const { status, changeSummary } = statusOf(entity ? diffModelAliasPayload(payload, entity) : null);
|
|
2590
|
+
aliasChecks.push({
|
|
2591
|
+
kind: "modelAlias",
|
|
2592
|
+
name: payload.code,
|
|
2593
|
+
status,
|
|
2594
|
+
...changeSummary ? { changeSummary } : {},
|
|
2595
|
+
// 模型映射无 version 概念(内容即 name/realModel 二元组)
|
|
2596
|
+
impact: "global"
|
|
2597
|
+
});
|
|
2598
|
+
}
|
|
2599
|
+
const bundledCodes = new Set(bundle.dependencies.modelAliases.map((a) => a.code));
|
|
2600
|
+
for (const agent of bundle.dependencies.agents) {
|
|
2601
|
+
if (bundledCodes.has(agent.model)) continue;
|
|
2602
|
+
const alias = await deps.modelAliasRepo.getByCode(agent.model);
|
|
2603
|
+
if (!alias) {
|
|
2604
|
+
warnings.push(`AGENT "${agent.name}" \u5F15\u7528\u7684\u6A21\u578B\u6620\u5C04 "${agent.model}" \u4E0D\u5728\u5BFC\u51FA\u5305\u4E2D\u4E14\u76EE\u6807\u73AF\u5883\u4E0D\u5B58\u5728\uFF0C\u5BFC\u5165\u540E\u8BE5\u5F15\u7528\u5C06\u60AC\u7A7A`);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
const bundledSkillNames = new Set(bundle.dependencies.skills.map((s) => s.name));
|
|
2608
|
+
for (const agent of bundle.dependencies.agents) {
|
|
2609
|
+
for (const skillName of agent.boundSkills) {
|
|
2610
|
+
if (bundledSkillNames.has(skillName)) continue;
|
|
2611
|
+
const globalHit = await deps.skillRepo.getByNameScoped(skillName, "global", void 0, { includeDisabled: true });
|
|
2612
|
+
const projectHit = agent.scope === "project" ? await deps.skillRepo.getByNameScoped(skillName, "project", targetProjectId, { includeDisabled: true }) : null;
|
|
2613
|
+
if (!globalHit && !projectHit) {
|
|
2614
|
+
warnings.push(`AGENT "${agent.name}" \u7ED1\u5B9A\u7684 SKILL "${skillName}" \u4E0D\u5728\u5BFC\u51FA\u5305\u4E2D\u4E14\u76EE\u6807\u73AF\u5883\u4E0D\u5B58\u5728\uFF0C\u5BFC\u5165\u540E\u8BE5\u7ED1\u5B9A\u5C06\u60AC\u7A7A`);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
return {
|
|
2619
|
+
template: {
|
|
2620
|
+
name: bundle.template.name,
|
|
2621
|
+
willOverwrite: existing !== null,
|
|
2622
|
+
...existing ? { currentVersion: existing.version } : {},
|
|
2623
|
+
importVersion: bundle.template.version,
|
|
2624
|
+
isDefaultRequested: bundle.template.isDefault,
|
|
2625
|
+
isDefaultEffect,
|
|
2626
|
+
...bundle.template.isDefault && !isDefaultEffect ? { downgradeReason: "project_has_default" } : {}
|
|
2627
|
+
},
|
|
2628
|
+
dependencies: [...skillChecks, ...agentChecks, ...aliasChecks],
|
|
2629
|
+
warnings
|
|
2630
|
+
};
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
// src/template-transfer/import-apply.ts
|
|
2634
|
+
async function applyImport(deps, targetProjectId, bundle, decisions) {
|
|
2635
|
+
const decisionMap = new Map(decisions.map((d) => [decisionKey(d.kind, "scope" in d ? d.scope : void 0, d.name), d.action]));
|
|
2636
|
+
const existing = await findTemplateByName(deps, targetProjectId, bundle.template.name);
|
|
2637
|
+
const projectHasDefault = await hasProjectDefault(deps, targetProjectId);
|
|
2638
|
+
const isDefaultEffect = bundle.template.isDefault && !projectHasDefault;
|
|
2639
|
+
const tplPayload = bundle.template;
|
|
2640
|
+
const createInput = { ...tplPayload, projectId: targetProjectId, isDefault: isDefaultEffect };
|
|
2641
|
+
const parsed = DagTemplateCreateSchema.safeParse(createInput);
|
|
2642
|
+
if (!parsed.success) {
|
|
2643
|
+
throw new ValidationError("\u6A21\u677F\u5185\u5BB9\u6821\u9A8C\u5931\u8D25\uFF08\u5BFC\u5165\u6587\u4EF6\u5B57\u6BB5\u975E\u6CD5\uFF09", parsed.error.issues);
|
|
2644
|
+
}
|
|
2645
|
+
const contentFields = {
|
|
2646
|
+
name: tplPayload.name,
|
|
2647
|
+
description: tplPayload.description,
|
|
2648
|
+
nodes: tplPayload.nodes,
|
|
2649
|
+
edges: tplPayload.edges,
|
|
2650
|
+
version: tplPayload.version,
|
|
2651
|
+
isDefault: isDefaultEffect,
|
|
2652
|
+
layout: tplPayload.layout ?? {}
|
|
2653
|
+
};
|
|
2654
|
+
let templateResult;
|
|
2655
|
+
if (existing) {
|
|
2656
|
+
const updated = await deps.templateRepo.update(existing.id, contentFields);
|
|
2657
|
+
if (!updated || !updated.id) throw new NotFoundError("dag-template", existing.id);
|
|
2658
|
+
templateResult = { id: updated.id, name: updated.name, action: "updated", isDefaultEffect };
|
|
2659
|
+
} else {
|
|
2660
|
+
const raced = await findTemplateByName(deps, targetProjectId, tplPayload.name);
|
|
2661
|
+
if (raced) {
|
|
2662
|
+
throw new ConflictError("dag-template", `name conflict: ${tplPayload.name}`, {
|
|
2663
|
+
bizCode: "TEMPLATE_NAME_CONFLICT",
|
|
2664
|
+
message: BIZ_CODE_MESSAGES.TEMPLATE_NAME_CONFLICT
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
const created = await deps.templateRepo.createDagTemplate({ ...createInput, layout: tplPayload.layout ?? {} });
|
|
2668
|
+
if (!created.id) throw new Error("\u6A21\u677F\u521B\u5EFA\u8FD4\u56DE\u5B9E\u4F53\u7F3A\u5931 id\uFF08\u4E0D\u53EF\u8FBE\u9632\u5FA1\u5206\u652F\uFF09");
|
|
2669
|
+
templateResult = { id: created.id, name: created.name, action: "created", isDefaultEffect };
|
|
2670
|
+
}
|
|
2671
|
+
const results = [];
|
|
2672
|
+
for (const payload of bundle.dependencies.skills) {
|
|
2673
|
+
try {
|
|
2674
|
+
const key = decisionKey("skill", payload.scope, payload.name);
|
|
2675
|
+
const existingSkill = await deps.skillRepo.getByNameScoped(
|
|
2676
|
+
payload.name,
|
|
2677
|
+
payload.scope,
|
|
2678
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2679
|
+
{ includeDisabled: true }
|
|
2680
|
+
);
|
|
2681
|
+
const diffs = existingSkill ? diffSkillPayload(payload, existingSkill) : null;
|
|
2682
|
+
const status = diffs === null ? "create" : diffs.length === 0 ? "identical" : "conflict";
|
|
2683
|
+
const action = decide(status, decisionMap.get(key));
|
|
2684
|
+
if (action === "identical") {
|
|
2685
|
+
results.push({ kind: "skill", name: payload.name, scope: payload.scope, action: "identical" });
|
|
2686
|
+
continue;
|
|
2687
|
+
}
|
|
2688
|
+
if (action === "skipped") {
|
|
2689
|
+
results.push({ kind: "skill", name: payload.name, scope: payload.scope, action: "skipped" });
|
|
2690
|
+
continue;
|
|
2691
|
+
}
|
|
2692
|
+
const createData = { ...payload, ...payload.scope === "project" ? { projectId: targetProjectId } : {} };
|
|
2693
|
+
const check = SkillCreateSchema.safeParse(createData);
|
|
2694
|
+
if (!check.success) {
|
|
2695
|
+
results.push({ kind: "skill", name: payload.name, scope: payload.scope, action: "failed", message: summarizeIssues(check.error.issues) });
|
|
2696
|
+
continue;
|
|
2697
|
+
}
|
|
2698
|
+
if (existingSkill && existingSkill.id) {
|
|
2699
|
+
await deps.skillRepo.updateByNameScoped(
|
|
2700
|
+
payload.name,
|
|
2701
|
+
payload.scope,
|
|
2702
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2703
|
+
{
|
|
2704
|
+
description: payload.description,
|
|
2705
|
+
content: payload.content,
|
|
2706
|
+
category: payload.category,
|
|
2707
|
+
version: payload.version,
|
|
2708
|
+
references: payload.references
|
|
2709
|
+
}
|
|
2710
|
+
);
|
|
2711
|
+
results.push({ kind: "skill", name: payload.name, scope: payload.scope, action: "updated" });
|
|
2712
|
+
} else {
|
|
2713
|
+
await deps.skillRepo.createSkill(check.data);
|
|
2714
|
+
results.push({ kind: "skill", name: payload.name, scope: payload.scope, action: "created" });
|
|
2715
|
+
}
|
|
2716
|
+
} catch (err) {
|
|
2717
|
+
results.push({ kind: "skill", name: payload.name, scope: payload.scope, action: "failed", message: errorMessage(err) });
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
for (const payload of bundle.dependencies.agents) {
|
|
2721
|
+
try {
|
|
2722
|
+
const key = decisionKey("agent", payload.scope, payload.name);
|
|
2723
|
+
const existingAgent = await deps.agentRepo.getByNameScoped(
|
|
2724
|
+
payload.name,
|
|
2725
|
+
payload.scope,
|
|
2726
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2727
|
+
{ includeDisabled: true }
|
|
2728
|
+
);
|
|
2729
|
+
const diffs = existingAgent ? diffAgentPayload(payload, existingAgent) : null;
|
|
2730
|
+
const status = diffs === null ? "create" : diffs.length === 0 ? "identical" : "conflict";
|
|
2731
|
+
const action = decide(status, decisionMap.get(key));
|
|
2732
|
+
if (action === "identical" || action === "skipped") {
|
|
2733
|
+
results.push({ kind: "agent", name: payload.name, scope: payload.scope, action });
|
|
2734
|
+
continue;
|
|
2735
|
+
}
|
|
2736
|
+
const createData = { ...payload, ...payload.scope === "project" ? { projectId: targetProjectId } : {} };
|
|
2737
|
+
const check = AgentCreateSchema.safeParse(createData);
|
|
2738
|
+
if (!check.success) {
|
|
2739
|
+
results.push({ kind: "agent", name: payload.name, scope: payload.scope, action: "failed", message: summarizeIssues(check.error.issues) });
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
await assertBoundSkillsCompatible(
|
|
2743
|
+
deps.skillRepo,
|
|
2744
|
+
payload.scope,
|
|
2745
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2746
|
+
payload.boundSkills
|
|
2747
|
+
);
|
|
2748
|
+
if (existingAgent && existingAgent.id) {
|
|
2749
|
+
await deps.agentRepo.updateByNameScoped(
|
|
2750
|
+
payload.name,
|
|
2751
|
+
payload.scope,
|
|
2752
|
+
payload.scope === "project" ? targetProjectId : void 0,
|
|
2753
|
+
{
|
|
2754
|
+
description: payload.description,
|
|
2755
|
+
systemPrompt: payload.systemPrompt,
|
|
2756
|
+
boundSkills: payload.boundSkills,
|
|
2757
|
+
model: payload.model,
|
|
2758
|
+
tools: payload.tools,
|
|
2759
|
+
permissions: payload.permissions,
|
|
2760
|
+
version: payload.version,
|
|
2761
|
+
references: payload.references,
|
|
2762
|
+
"function": payload.function
|
|
2763
|
+
}
|
|
2764
|
+
);
|
|
2765
|
+
results.push({ kind: "agent", name: payload.name, scope: payload.scope, action: "updated" });
|
|
2766
|
+
} else {
|
|
2767
|
+
await deps.agentRepo.createAgent(check.data);
|
|
2768
|
+
results.push({ kind: "agent", name: payload.name, scope: payload.scope, action: "created" });
|
|
2769
|
+
}
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
results.push({ kind: "agent", name: payload.name, scope: payload.scope, action: "failed", message: errorMessage(err) });
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
for (const payload of bundle.dependencies.modelAliases) {
|
|
2775
|
+
try {
|
|
2776
|
+
const key = decisionKey("modelAlias", void 0, payload.code);
|
|
2777
|
+
const existingAlias = await deps.modelAliasRepo.getByCode(payload.code);
|
|
2778
|
+
const diffs = existingAlias ? diffModelAliasPayload(payload, existingAlias) : null;
|
|
2779
|
+
const status = diffs === null ? "create" : diffs.length === 0 ? "identical" : "conflict";
|
|
2780
|
+
const action = decide(status, decisionMap.get(key));
|
|
2781
|
+
if (action === "identical" || action === "skipped") {
|
|
2782
|
+
results.push({ kind: "modelAlias", name: payload.code, action });
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
if (existingAlias) {
|
|
2786
|
+
await deps.modelAliasRepo.updateByCode(payload.code, { name: payload.name, realModel: payload.realModel });
|
|
2787
|
+
results.push({ kind: "modelAlias", name: payload.code, action: "updated" });
|
|
2788
|
+
} else {
|
|
2789
|
+
const createData = { code: payload.code, name: payload.name, realModel: payload.realModel };
|
|
2790
|
+
await deps.modelAliasRepo.createModelAlias(createData);
|
|
2791
|
+
results.push({ kind: "modelAlias", name: payload.code, action: "created" });
|
|
2792
|
+
}
|
|
2793
|
+
} catch (err) {
|
|
2794
|
+
results.push({ kind: "modelAlias", name: payload.code, action: "failed", message: errorMessage(err) });
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
return { template: templateResult, dependencies: results };
|
|
2798
|
+
}
|
|
2799
|
+
function decide(status, decision) {
|
|
2800
|
+
if (status === "identical") return "identical";
|
|
2801
|
+
if (status === "create") return "create";
|
|
2802
|
+
return decision === "overwrite" ? "overwrite" : "skipped";
|
|
2803
|
+
}
|
|
2804
|
+
function summarizeIssues(issues) {
|
|
2805
|
+
const parts = issues.map(
|
|
2806
|
+
(issue) => `${issue.path.join(".")}: ${issue.message}`
|
|
2807
|
+
);
|
|
2808
|
+
return `\u6821\u9A8C\u5931\u8D25\uFF1A${parts.join("; ")}`;
|
|
2809
|
+
}
|
|
2810
|
+
function errorMessage(err) {
|
|
2811
|
+
return err instanceof Error ? err.message : String(err);
|
|
2812
|
+
}
|
|
2813
|
+
|
|
1971
2814
|
// src/index.ts
|
|
1972
|
-
var PackageJsonSchema =
|
|
2815
|
+
var PackageJsonSchema = z15.object({ version: z15.string().min(1) });
|
|
1973
2816
|
var packageJsonParsed = PackageJsonSchema.safeParse(
|
|
1974
2817
|
JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"))
|
|
1975
2818
|
);
|
|
@@ -1981,24 +2824,33 @@ function ping() {
|
|
|
1981
2824
|
return "pong";
|
|
1982
2825
|
}
|
|
1983
2826
|
export {
|
|
2827
|
+
AGENT_MAIN_DOC,
|
|
1984
2828
|
ARTIFACT_TYPES,
|
|
1985
2829
|
AdvanceRequestSchema,
|
|
1986
2830
|
AdvanceResponseSchema,
|
|
1987
2831
|
AgentCopySchema,
|
|
1988
2832
|
AgentCreateSchema,
|
|
2833
|
+
AgentFunctionSchema,
|
|
2834
|
+
AgentPayloadSchema,
|
|
1989
2835
|
AgentSchema,
|
|
1990
2836
|
AgentScopeSchema,
|
|
2837
|
+
AgentShapeSchema,
|
|
1991
2838
|
AgentUpdateSchema,
|
|
1992
2839
|
AppError,
|
|
1993
2840
|
ApproveRequestSchema,
|
|
1994
2841
|
ApproveResponseSchema,
|
|
1995
2842
|
ArchNoteSchema,
|
|
1996
2843
|
ArtifactSchema,
|
|
2844
|
+
AssetSetEnabledSchema,
|
|
1997
2845
|
BIZ_CODE_MESSAGES,
|
|
1998
2846
|
BadRequestError,
|
|
2847
|
+
CONTEXT_VIEWS,
|
|
2848
|
+
CancelRequestSchema,
|
|
1999
2849
|
CheckItemSchema,
|
|
2000
2850
|
ConfirmationSchema,
|
|
2001
2851
|
ConflictError,
|
|
2852
|
+
ContextViewSchema,
|
|
2853
|
+
CreateTaskResponseSchema,
|
|
2002
2854
|
DAG_PHASES,
|
|
2003
2855
|
DAG_TRACKS,
|
|
2004
2856
|
DEFAULT_PROJECT_KEY,
|
|
@@ -2012,16 +2864,28 @@ export {
|
|
|
2012
2864
|
DagTemplateSchema,
|
|
2013
2865
|
DagTemplateUpdateSchema,
|
|
2014
2866
|
DecisionSchema,
|
|
2867
|
+
DependencyActionSchema,
|
|
2868
|
+
DependencyCheckSchema,
|
|
2869
|
+
DependencyStatusSchema,
|
|
2015
2870
|
ENTRY_ID_REGEX,
|
|
2871
|
+
EXPORT_FORMAT_VERSION,
|
|
2872
|
+
EchoItemSchema,
|
|
2016
2873
|
EdgePausePointBaseSchema,
|
|
2017
2874
|
EdgePausePointSchema,
|
|
2018
2875
|
EnumEntrySchema,
|
|
2019
2876
|
EnumRegistryCategorySchema,
|
|
2020
2877
|
EnumRegistrySchema,
|
|
2021
2878
|
EnumRegistryUpdateSchema,
|
|
2879
|
+
ExportBundleSchema,
|
|
2022
2880
|
HistoryActionSchema,
|
|
2023
2881
|
HistoryEntrySchema,
|
|
2882
|
+
ImportApplyRequestSchema,
|
|
2883
|
+
ImportApplyResponseSchema,
|
|
2884
|
+
ImportDecisionSchema,
|
|
2885
|
+
ImportPlanRequestSchema,
|
|
2886
|
+
ImportPlanResponseSchema,
|
|
2024
2887
|
ModelAliasCreateSchema,
|
|
2888
|
+
ModelAliasPayloadSchema,
|
|
2025
2889
|
ModelAliasSchema,
|
|
2026
2890
|
ModelAliasShapeSchema,
|
|
2027
2891
|
ModelAliasUpdateSchema,
|
|
@@ -2041,17 +2905,21 @@ export {
|
|
|
2041
2905
|
ProjectSchema,
|
|
2042
2906
|
ProjectStatusSchema,
|
|
2043
2907
|
ProjectUpdateSchema,
|
|
2908
|
+
REFERENCE_LIMITS,
|
|
2044
2909
|
ResumeRequestSchema,
|
|
2045
2910
|
ReviewSummarySchema,
|
|
2046
2911
|
SIMING_CODE_REGEX,
|
|
2047
2912
|
SIMING_CONFIG_ENV_KEYS,
|
|
2048
2913
|
SIMING_CONFIG_KEYS,
|
|
2914
|
+
SKILL_MAIN_DOC,
|
|
2049
2915
|
SimingConfigSchema,
|
|
2050
2916
|
SimingLogLevelSchema,
|
|
2051
2917
|
SkillCopySchema,
|
|
2052
2918
|
SkillCreateSchema,
|
|
2919
|
+
SkillPayloadSchema,
|
|
2053
2920
|
SkillSchema,
|
|
2054
2921
|
SkillScopeSchema,
|
|
2922
|
+
SkillShapeSchema,
|
|
2055
2923
|
SkillUpdateSchema,
|
|
2056
2924
|
TASK_PHASES,
|
|
2057
2925
|
TASK_STATUSES,
|
|
@@ -2060,35 +2928,59 @@ export {
|
|
|
2060
2928
|
TaskCreateInputSchema,
|
|
2061
2929
|
TaskDocContentSchema,
|
|
2062
2930
|
TaskPhaseSchema,
|
|
2931
|
+
TaskProgressPublicSchema,
|
|
2932
|
+
TaskProgressSchema,
|
|
2063
2933
|
TaskPublicSchema,
|
|
2064
2934
|
TaskSchema,
|
|
2065
2935
|
TaskStatusSchema,
|
|
2066
2936
|
TaskSummarySchema,
|
|
2937
|
+
TemplatePayloadSchema,
|
|
2067
2938
|
VERSION,
|
|
2068
2939
|
ValidationError,
|
|
2940
|
+
WRITE_ACK_ENTRY_KINDS,
|
|
2941
|
+
WriteAckEntryKindSchema,
|
|
2942
|
+
WriteAckSchema,
|
|
2069
2943
|
advanceTask,
|
|
2944
|
+
applyImport,
|
|
2070
2945
|
approveTask,
|
|
2071
2946
|
assertBoundSkillsCompatible,
|
|
2072
2947
|
assertModelAliasExists,
|
|
2948
|
+
assertReferenceAggregateLimit,
|
|
2949
|
+
assertReferencesDeletable,
|
|
2950
|
+
buildImportPlan,
|
|
2951
|
+
bumpPatch,
|
|
2952
|
+
bumpPatchOrPassthrough,
|
|
2953
|
+
cancelTask,
|
|
2954
|
+
canonicalizeReferencePath,
|
|
2073
2955
|
checkTermination,
|
|
2956
|
+
composeExportBundle,
|
|
2074
2957
|
createAgentRepo,
|
|
2075
2958
|
createDagTemplateRepo,
|
|
2076
2959
|
createEnumRegistryRepo,
|
|
2077
2960
|
createModelAliasRepo,
|
|
2078
2961
|
createMongoClient,
|
|
2079
2962
|
createProjectRepo,
|
|
2963
|
+
createReferencesSchema,
|
|
2080
2964
|
createRepo,
|
|
2081
2965
|
createSkillRepo,
|
|
2082
2966
|
createTaskRepo,
|
|
2967
|
+
decisionKey,
|
|
2968
|
+
enabledNeFalse,
|
|
2969
|
+
escapeRegExpLiteral,
|
|
2970
|
+
excerpt,
|
|
2083
2971
|
findNextEdge,
|
|
2972
|
+
findTemplateByName,
|
|
2084
2973
|
generateEntryId,
|
|
2085
2974
|
generateProjectKey,
|
|
2975
|
+
hasProjectDefault,
|
|
2086
2976
|
mkHistory,
|
|
2087
2977
|
pauseTask,
|
|
2088
2978
|
ping,
|
|
2089
2979
|
pruneInstance,
|
|
2090
2980
|
renderPrompt,
|
|
2091
2981
|
resumeTask,
|
|
2982
|
+
sumReferenceContentChars,
|
|
2983
|
+
taskProgress,
|
|
2092
2984
|
toNodeInfo,
|
|
2093
2985
|
toTaskPublic,
|
|
2094
2986
|
trackMatchSet,
|