@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.
Files changed (3) hide show
  1. package/dist/index.d.ts +1010 -15
  2. package/dist/index.js +1230 -338
  3. 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 z11 } from "zod";
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
- scope === "project" ? { name, scope: "project", projectId } : { name, scope: "global" }
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(scope === "project" ? { scope: "project", projectId } : { scope: "global" });
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 base = createRepo(db.collection("agents"), { nameField: "name" });
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
- scope === "project" ? { name, scope: "project", projectId } : { name, scope: "global" }
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(scope === "project" ? { scope: "project", projectId } : { scope: "global" });
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 z3 } from "zod";
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 SkillScopeSchema = z.enum(["global", "project"]);
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 = z.object({
439
- id: z.string().optional(),
440
- name: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "kebab-case only"),
441
- description: z.string(),
442
- content: z.string(),
443
- category: z.string(),
444
- version: z.string().default("1.0.0"),
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: z.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
449
- createdAt: z.date().optional(),
450
- updatedAt: z.date().optional()
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 SkillSchema = SkillShapeSchema.superRefine(SkillScopeRefine);
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
- }).extend({ scope: SkillScopeSchema }).superRefine(SkillScopeRefine);
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: z.string().optional(),
651
+ version: z2.string().optional(),
464
652
  scope: SkillScopeSchema.optional()
465
- }).partial().superRefine(SkillScopeRefine);
466
- var SkillCopySchema = z.object({
467
- newName: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "kebab-case only"),
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: z.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
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 z2 } from "zod";
482
- var DagNodePhaseSchema = z2.string();
483
- var DagNodeTrackSchema = z2.string();
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 = z2.object({
488
- id: z2.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"),
489
- label: z2.string(),
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: z2.string(),
493
- skills: z2.array(z2.string()).default([])
680
+ prompt: z3.string(),
681
+ skills: z3.array(z3.string()).default([])
494
682
  });
495
- var PausePointTypeSchema = z2.enum(["human_approval", "checkpoint"]);
683
+ var PausePointTypeSchema = z3.enum(["human_approval", "checkpoint"]);
496
684
  var PAUSE_POINT_TYPES = ["human_approval", "checkpoint"];
497
- var EdgePausePointBaseSchema = z2.object({
685
+ var EdgePausePointBaseSchema = z3.object({
498
686
  type: PausePointTypeSchema,
499
- description: z2.string(),
500
- autoResume: z2.boolean().default(false)
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 = z2.object({
517
- from: z2.string(),
518
- to: z2.string(),
519
- condition: z2.string().optional(),
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 = z2.object({
524
- id: z2.string().optional(),
525
- name: z2.string(),
711
+ var DagTemplateSchema = z3.object({
712
+ id: z3.string().optional(),
713
+ name: z3.string(),
526
714
  /** 归属项目 id(N012:实体层去哨兵 default,由路由层解析写入——校验项目存在且 active 后落库) */
527
- projectId: z2.string(),
528
- description: z2.string(),
529
- nodes: z2.array(DagNodeSchema),
530
- edges: z2.array(DagEdgeSchema),
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: z2.record(z2.string(), z2.object({ x: z2.number(), y: z2.number() })).optional(),
533
- isDefault: z2.boolean().default(false),
534
- version: z2.string().default("1.0.0"),
535
- createdAt: z2.date().optional(),
536
- updatedAt: z2.date().optional()
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: z2.boolean().optional(),
545
- version: z2.string().optional()
740
+ isDefault: z3.boolean().optional(),
741
+ version: z3.string().optional()
546
742
  }).partial();
547
- var DagTemplateCopySchema = z2.object({
548
- newName: z2.string().min(1),
549
- targetProjectId: z2.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex")
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 = z3.object({
556
- id: z3.string().regex(ENTRY_ID_REGEX),
557
- item: z3.string().min(1),
558
- passed: z3.boolean()
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 = z3.object({
562
- id: z3.string().regex(ENTRY_ID_REGEX),
563
- type: z3.enum(ARTIFACT_TYPES),
564
- path: z3.string().min(1),
565
- note: z3.string().optional(),
566
- content: z3.string().max(2e5).optional()
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 = z3.object({
569
- id: z3.string().regex(ENTRY_ID_REGEX),
570
- quote: z3.string().min(1).max(2e3),
571
- at: z3.date()
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 = z3.object({
574
- id: z3.string().regex(ENTRY_ID_REGEX),
575
- topic: z3.string().min(1),
576
- decision: z3.string().min(1)
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 = z3.object({
579
- verdict: z3.enum(["pass", "fail"]),
580
- rounds: z3.number().int().min(1),
581
- critical: z3.number().int().min(0)
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 = z3.object({
584
- summary: z3.string().optional(),
585
- checks: z3.array(CheckItemSchema).default([]),
586
- artifacts: z3.array(ArtifactSchema).default([]),
587
- confirmations: z3.array(ConfirmationSchema).default([]),
588
- decisions: z3.array(DecisionSchema).default([]),
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 = z3.object({
592
- what: z3.string().min(1),
593
- why: z3.string().min(1),
594
- acceptance: z3.array(z3.string()).default([]),
595
- nonGoals: z3.array(z3.string()).default([]),
596
- trackNote: z3.string().optional()
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 = z3.object({
599
- id: z3.string().regex(ENTRY_ID_REGEX),
600
- text: z3.string().min(1).max(2e3),
601
- at: z3.date()
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 = z3.string();
799
+ var NodeStatusSchema = z4.string();
604
800
  var NODE_STATUSES = ["pending", "active", "completed", "skipped"];
605
- var NodeStateSchema = z3.object({
801
+ var NodeStateSchema = z4.object({
606
802
  status: NodeStatusSchema,
607
- enteredAt: z3.date().nullable().default(null),
608
- completedAt: z3.date().nullable().default(null)
803
+ enteredAt: z4.date().nullable().default(null),
804
+ completedAt: z4.date().nullable().default(null)
609
805
  });
610
- var DagInstanceSchema = z3.object({
611
- templateId: z3.string(),
612
- templateVersion: z3.string(),
613
- nodes: z3.array(z3.object({
614
- id: z3.string(),
615
- label: z3.string(),
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: z3.string(),
618
- track: z3.string(),
619
- prompt: z3.string(),
620
- skills: z3.array(z3.string()).default([])
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: z3.array(z3.object({
624
- from: z3.string(),
625
- to: z3.string(),
626
- condition: z3.string().optional(),
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: z3.array(z3.string()),
630
- nodeStates: z3.record(z3.string(), NodeStateSchema)
825
+ prunedNodes: z4.array(z4.string()),
826
+ nodeStates: z4.record(z4.string(), NodeStateSchema)
631
827
  });
632
- var HistoryActionSchema = z3.enum(["entered", "advanced", "paused", "resumed", "approved", "rejected", "completed", "cancelled"]);
633
- var HistoryEntrySchema = z3.object({
634
- nodeId: z3.string(),
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: z3.date().default(() => /* @__PURE__ */ new Date()),
637
- details: z3.record(z3.string(), z3.unknown()).default({})
832
+ timestamp: z4.date().default(() => /* @__PURE__ */ new Date()),
833
+ details: z4.record(z4.string(), z4.unknown()).default({})
638
834
  });
639
- var TaskStatusSchema = z3.string();
640
- var TaskPhaseSchema = z3.string();
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 = z3.object({
644
- id: z3.string().optional(),
645
- taskId: z3.string(),
646
- title: z3.string(),
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: z3.enum(TASK_TYPES).optional(),
847
+ type: z4.enum(TASK_TYPES).optional(),
652
848
  /** 归属项目 id(N012:实体层去哨兵 default,由 repo 显式写入——路由层解析:显式携带校验一致性 / 缺省继承模板 projectId) */
653
- projectId: z3.string(),
654
- dagTemplateId: z3.string(),
849
+ projectId: z4.string(),
850
+ dagTemplateId: z4.string(),
655
851
  dagInstance: DagInstanceSchema,
656
- currentNode: z3.string(),
852
+ currentNode: z4.string(),
657
853
  currentPhase: TaskPhaseSchema,
658
854
  status: TaskStatusSchema,
659
855
  /** 暂停位置:from 节点 id(暂停点暂停,approve 经 findNextEdge 重推导边)/ null(手动暂停) */
660
- pausedAt: z3.string().nullable().default(null),
661
- track: z3.string(),
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: z3.record(z3.string(), NodeRecordSchema).default({}),
868
+ nodeRecords: z4.record(z4.string(), NodeRecordSchema).default({}),
673
869
  /** N020 D1:跨节点累积素材(架构信息收集条目,服务端生成 id/at) */
674
- archNotes: z3.array(ArchNoteSchema).default([]),
675
- history: z3.array(HistoryEntrySchema).default([]),
676
- createdAt: z3.date().optional(),
677
- updatedAt: z3.date().optional()
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 = z3.object({
680
- title: z3.string(),
681
- track: z3.string(),
682
- dagTemplateId: z3.string(),
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: z3.array(z3.string().regex(NODE_ID_PATTERN, "skipNodes \u6761\u76EE\u987B\u4E3A\u5408\u6CD5\u8282\u70B9 id")).optional(),
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: z3.enum(TASK_TYPES).optional(),
886
+ type: z4.enum(TASK_TYPES).optional(),
691
887
  /** 归属项目:可选(缺省 → 继承模板 projectId,D2;显式携带且 ≠ 模板 → 409 TEMPLATE_PROJECT_MISMATCH) */
692
- projectId: z3.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
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 z5 } from "zod";
1860
+ import { z as z6 } from "zod";
1641
1861
 
1642
1862
  // src/schemas/model-alias.ts
1643
- import { z as z4 } from "zod";
1863
+ import { z as z5 } from "zod";
1644
1864
  var SIMING_CODE_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
1645
- var ModelAliasShapeSchema = z4.object({
1646
- id: z4.string().optional(),
1865
+ var ModelAliasShapeSchema = z5.object({
1866
+ id: z5.string().optional(),
1647
1867
  /** siming code:创建后不可变(PUT 显式拒绝 MODEL_CODE_IMMUTABLE,非静默剥离) */
1648
- code: z4.string().regex(SIMING_CODE_REGEX, "code \u5FC5\u987B\u4E3A kebab-case\uFF08\u5982 primary\u3001fast-model\uFF09"),
1868
+ code: z5.string().regex(SIMING_CODE_REGEX, "code \u5FC5\u987B\u4E3A kebab-case\uFF08\u5982 primary\u3001fast-model\uFF09"),
1649
1869
  /** 展示名(如「主力模型」) */
1650
- name: z4.string().min(1, "\u5C55\u793A\u540D\u4E0D\u80FD\u4E3A\u7A7A"),
1870
+ name: z5.string().min(1, "\u5C55\u793A\u540D\u4E0D\u80FD\u4E3A\u7A7A"),
1651
1871
  /** 真实模型标识(如 zhipuai/glm-5.3;改此处 = 引用方全局替换生效) */
1652
- realModel: z4.string().min(1, "\u771F\u5B9E\u6A21\u578B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A"),
1653
- createdAt: z4.date().optional(),
1654
- updatedAt: z4.date().optional()
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 = z4.object({
1663
- code: z4.string().regex(SIMING_CODE_REGEX).optional(),
1664
- name: z4.string().min(1).optional(),
1665
- realModel: z4.string().min(1).optional()
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: z4.number().int().min(0)
1888
+ refCount: z5.number().int().min(0)
1669
1889
  });
1670
1890
 
1671
1891
  // src/schemas/agent.ts
1672
- var AgentScopeSchema = z5.enum(["global", "project"]);
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 = z5.object({
1683
- id: z5.string().optional(),
1684
- name: z5.string(),
1685
- description: z5.string(),
1686
- systemPrompt: z5.string(),
1687
- boundSkills: z5.array(z5.string()).default([]),
1688
- model: z5.string(),
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: z5.string().default("1.0.0"),
1691
- tools: z5.array(z5.string()).default([]),
1692
- permissions: z5.array(z5.string()).default([]),
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: z5.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
1697
- createdAt: z5.date().optional(),
1698
- updatedAt: z5.date().optional()
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 AgentSchema = AgentShapeSchema.superRefine(AgentScopeRefine);
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
- }).extend({ scope: AgentScopeSchema }).extend({ model: z5.string().regex(SIMING_CODE_REGEX, "model \u5FC5\u987B\u662F siming code\uFF08kebab-case\uFF09") }).superRefine(AgentScopeRefine);
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: z5.array(z5.string()).optional(),
1954
+ boundSkills: z6.array(z6.string()).optional(),
1712
1955
  // N015:version 放行(export apply 版本递增随 PUT 携带);optional 剥离实体层 default 避免 partial 空 body 注入
1713
- version: z5.string().optional(),
1714
- tools: z5.array(z5.string()).optional(),
1715
- permissions: z5.array(z5.string()).optional(),
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: z5.string().regex(SIMING_CODE_REGEX, "model \u5FC5\u987B\u662F siming code\uFF08kebab-case\uFF09").optional()
1719
- }).partial().superRefine(AgentScopeRefine);
1720
- var AgentCopySchema = z5.object({
1721
- newName: z5.string(),
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: z5.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
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 z6 } from "zod";
1736
- var AdvanceRequestSchema = z6.object({
1737
- note: z6.string().optional(),
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: z6.string().min(1).max(2e3).optional()
2122
+ summary: z8.string().min(1).max(2e3).optional()
1740
2123
  });
1741
- var ApproveRequestSchema = z6.object({
1742
- decision: z6.enum(["approved", "rejected"]),
1743
- comment: z6.string().optional()
2124
+ var ApproveRequestSchema = z8.object({
2125
+ decision: z8.enum(["approved", "rejected"]),
2126
+ comment: z8.string().optional()
1744
2127
  });
1745
- var TaskPublicSchema = z6.object({
1746
- taskId: z6.string(),
1747
- title: z6.string(),
2128
+ var TaskPublicSchema = z8.object({
2129
+ taskId: z8.string(),
2130
+ title: z8.string(),
1748
2131
  // T202608240003:任务类型标签(缺省 = 裸任务/旧任务;模板 PRD 调研变体判定消费)
1749
- type: z6.enum(TASK_TYPES).optional(),
1750
- currentNode: z6.string(),
1751
- currentPhase: z6.string(),
1752
- status: z6.string(),
1753
- pausedAt: z6.string().nullable(),
1754
- track: z6.string()
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 = z6.object({
1757
- nodeId: z6.string(),
1758
- label: z6.string(),
1759
- track: z6.string(),
1760
- prompt: z6.string(),
1761
- skills: z6.array(z6.string()),
1762
- upcomingPause: z6.string().nullable()
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 = z6.discriminatedUnion("status", [
1765
- z6.object({
1766
- status: z6.literal("advanced"),
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
- z6.object({
1771
- status: z6.literal("paused"),
2153
+ z8.object({
2154
+ status: z8.literal("paused"),
1772
2155
  task: TaskPublicSchema,
1773
2156
  pausePoint: EdgePausePointSchema,
1774
- guidance: z6.string()
2157
+ guidance: z8.string()
1775
2158
  }),
1776
- z6.object({
1777
- status: z6.literal("completed"),
2159
+ z8.object({
2160
+ status: z8.literal("completed"),
1778
2161
  task: TaskPublicSchema
1779
2162
  })
1780
2163
  ]);
1781
- var ApproveResponseSchema = z6.discriminatedUnion("status", [
1782
- z6.object({
1783
- status: z6.literal("advanced"),
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
- z6.object({
1788
- status: z6.literal("rejected"),
2170
+ z8.object({
2171
+ status: z8.literal("rejected"),
1789
2172
  task: TaskPublicSchema,
1790
- guidance: z6.string()
2173
+ guidance: z8.string()
1791
2174
  }),
1792
2175
  // 「completed」分支当前引擎不可达(approve 从 paused 流转必有下一节点,技术方案 §2.2
1793
2176
  // 已删 paused→completed 直达路径)——按方案 §2.1 契约保留作前向兼容预留
1794
- z6.object({
1795
- status: z6.literal("completed"),
2177
+ z8.object({
2178
+ status: z8.literal("completed"),
1796
2179
  task: TaskPublicSchema
1797
2180
  })
1798
2181
  ]);
1799
- var PauseRequestSchema = z6.object({
1800
- reason: z6.string().optional()
2182
+ var PauseRequestSchema = z8.object({
2183
+ reason: z8.string().optional()
1801
2184
  });
1802
- var ResumeRequestSchema = z6.object({
1803
- decision: z6.string().optional()
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 z7 } from "zod";
1808
- var TaskContextSchema = z7.object({
1809
- task: z7.object({
1810
- taskId: z7.string(),
1811
- title: z7.string(),
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: z7.enum(TASK_TYPES).optional(),
1814
- currentNode: z7.string(),
1815
- currentPhase: z7.string(),
1816
- status: z7.string(),
1817
- pausedAt: z7.string().nullable(),
1818
- track: z7.string()
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: z7.object({
1821
- nodeId: z7.string(),
1822
- label: z7.string(),
1823
- phase: z7.string(),
1824
- track: z7.string(),
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: z7.string(),
1827
- skills: z7.array(z7.string()),
2212
+ prompt: z9.string(),
2213
+ skills: z9.array(z9.string()),
1828
2214
  /** 完成当前节点后出边上的暂停点描述(经 findNextEdge 按轨选边) */
1829
- upcomingPause: z7.string().nullable()
2215
+ upcomingPause: z9.string().nullable()
1830
2216
  }).nullable(),
1831
- pausedAtEdge: z7.object({
1832
- from: z7.string(),
1833
- to: z7.string(),
2217
+ pausedAtEdge: z9.object({
2218
+ from: z9.string(),
2219
+ to: z9.string(),
1834
2220
  pausePoint: EdgePausePointBaseSchema
1835
2221
  }).nullable(),
1836
2222
  /** 全节点实时状态清单 */
1837
- nodes: z7.array(z7.object({
1838
- nodeId: z7.string(),
1839
- label: z7.string(),
1840
- status: z7.string(),
1841
- enteredAt: z7.date().nullable(),
1842
- completedAt: z7.date().nullable()
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: z7.record(z7.string(), NodeRecordSchema),
2233
+ nodeRecords: z9.record(z9.string(), NodeRecordSchema),
1848
2234
  /** N020 D1:跨节点累积素材(架构信息收集条目) */
1849
- archNotes: z7.array(ArchNoteSchema)
2235
+ archNotes: z9.array(ArchNoteSchema)
1850
2236
  });
1851
- var TaskSummarySchema = z7.object({
1852
- taskId: z7.string(),
1853
- title: z7.string(),
2237
+ var TaskSummarySchema = z9.object({
2238
+ taskId: z9.string(),
2239
+ title: z9.string(),
1854
2240
  // T202608240003:任务类型标签(缺省 = 裸任务/旧任务)
1855
- type: z7.enum(TASK_TYPES).optional(),
1856
- projectId: z7.string(),
1857
- dagTemplateId: z7.string(),
1858
- currentNode: z7.string(),
1859
- currentPhase: z7.string(),
1860
- status: z7.string(),
1861
- pausedAt: z7.string().nullable(),
1862
- track: z7.string(),
1863
- progress: z7.object({
1864
- completed: z7.number(),
1865
- total: z7.number()
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: z7.date().optional(),
1868
- updatedAt: z7.date().optional()
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 z8 } from "zod";
1873
- var ProjectStatusSchema = z8.enum(["active", "archived"]);
1874
- var ProjectSchema = z8.object({
1875
- id: z8.string().optional(),
1876
- key: z8.string().regex(/^[a-z0-9][a-z0-9-]{1,30}$/, "key \u5FC5\u987B\u4E3A kebab-case\uFF082-31 \u5B57\u7B26\uFF09"),
1877
- name: z8.string().min(1).max(50),
1878
- description: z8.string().max(500).optional(),
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: z8.date().optional(),
1881
- updatedAt: z8.date().optional()
2314
+ createdAt: z11.date().optional(),
2315
+ updatedAt: z11.date().optional()
1882
2316
  });
1883
- var ProjectCreateSchema = z8.object({
1884
- key: z8.string().regex(/^[a-z0-9][a-z0-9-]{1,30}$/, "key \u5FC5\u987B\u4E3A kebab-case\uFF082-31 \u5B57\u7B26\uFF09").optional(),
1885
- name: z8.string().min(1).max(50),
1886
- description: z8.string().max(500).optional()
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 = z8.object({
1889
- name: z8.string().min(1).max(50).optional(),
1890
- description: z8.string().max(500).optional(),
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 z9 } from "zod";
1896
- var EnumRegistryCategorySchema = z9.enum([
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 = z9.object({
2340
+ var EnumEntrySchema = z12.object({
1906
2341
  /** 领域层存库的原始枚举值(不可臆造;builtin 值 engine/DB 强依赖;kebab 约束与 skill name 对齐,保证 DELETE /entries/:value 可寻址) */
1907
- value: z9.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"),
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: z9.string().min(1, "label \u4E0D\u80FD\u4E3A\u7A7A"),
2344
+ label: z12.string().min(1, "label \u4E0D\u80FD\u4E3A\u7A7A"),
1910
2345
  /** 可选语义色 token(如 --success / --warning),消费端映射到 UI */
1911
- color: z9.string().optional(),
2346
+ color: z12.string().optional(),
1912
2347
  /** 下拉排序(升序) */
1913
- order: z9.number().int().default(0),
2348
+ order: z12.number().int().default(0),
1914
2349
  /** 内置值:禁删 + label/color 可改但 value 不可改(D8);seed 时标记 */
1915
- builtin: z9.boolean().default(false),
2350
+ builtin: z12.boolean().default(false),
1916
2351
  /** 启停:false = 下拉禁用展示(历史数据仍可读,新选择不可用) */
1917
- active: z9.boolean().default(true)
2352
+ active: z12.boolean().default(true)
1918
2353
  });
1919
- var EnumRegistrySchema = z9.object({
1920
- id: z9.string().optional(),
2354
+ var EnumRegistrySchema = z12.object({
2355
+ id: z12.string().optional(),
1921
2356
  category: EnumRegistryCategorySchema,
1922
- entries: z9.array(EnumEntrySchema).default([]),
1923
- updatedAt: z9.date().optional()
2357
+ entries: z12.array(EnumEntrySchema).default([]),
2358
+ updatedAt: z12.date().optional()
1924
2359
  });
1925
- var EnumRegistryUpdateSchema = z9.object({
1926
- entries: z9.array(EnumEntrySchema)
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 z10 } from "zod";
1931
- var SimingLogLevelSchema = z10.enum(["debug", "info", "warn", "error"]);
1932
- var SimingConfigSchema = z10.object({
1933
- mongoUri: z10.string().min(1).default("mongodb://localhost:27017"),
1934
- port: z10.coerce.number().int().min(1).max(65535).default(7777),
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: z10.string().min(1).default("127.0.0.1"),
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: z10.unknown().optional(),
1946
- cors: z10.unknown().optional()
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 = z11.object({ version: z11.string().min(1) });
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,