oasis_test 0.1.82 → 0.1.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1794,9 +1794,9 @@ function resolveNodeRef(ref2, candidates) {
1794
1794
  const hit = candidates.find((n) => n.id === r);
1795
1795
  return hit ? { ok: true, id: hit.id } : { ok: false, reason: "not_found" };
1796
1796
  }
1797
- const sep3 = r.indexOf(":");
1798
- const type = (sep3 >= 0 ? r.slice(0, sep3) : r).trim().toLowerCase();
1799
- const title = sep3 >= 0 ? r.slice(sep3 + 1).trim().toLowerCase() : null;
1797
+ const sep4 = r.indexOf(":");
1798
+ const type = (sep4 >= 0 ? r.slice(0, sep4) : r).trim().toLowerCase();
1799
+ const title = sep4 >= 0 ? r.slice(sep4 + 1).trim().toLowerCase() : null;
1800
1800
  const matches = candidates.filter((n) => n.type.toLowerCase() === type && (title === null || (n.title ?? "").toLowerCase() === title));
1801
1801
  if (matches.length === 0) return { ok: false, reason: "not_found" };
1802
1802
  if (matches.length === 1) return { ok: true, id: matches[0].id };
@@ -2399,6 +2399,15 @@ function validateUpsertEvaluationRubricInput(value2) {
2399
2399
  if (input.status !== void 0 && input.status !== "active" && input.status !== "archived") {
2400
2400
  throw new Error("status \u53EA\u80FD\u662F active \u6216 archived");
2401
2401
  }
2402
+ if (input.scope !== void 0 && input.scope !== "shared" && input.scope !== "case") {
2403
+ throw new Error("scope \u53EA\u80FD\u662F shared \u6216 case");
2404
+ }
2405
+ if (input.scope === "case" && !input.ownerCaseId?.trim()) {
2406
+ throw new Error("Case \u7EA7\u6807\u51C6\u5FC5\u987B\u6307\u660E\u5F52\u5C5E\u7684 caseId");
2407
+ }
2408
+ if (input.scope !== "case" && input.ownerCaseId) {
2409
+ throw new Error("\u5171\u4EAB\u6807\u51C6\u4E0D\u80FD\u6307\u5B9A ownerCaseId");
2410
+ }
2402
2411
  return {
2403
2412
  ...typeof input.rubricId === "string" && input.rubricId ? { rubricId: input.rubricId } : {},
2404
2413
  name: input.name.trim(),
@@ -2406,13 +2415,25 @@ function validateUpsertEvaluationRubricInput(value2) {
2406
2415
  ...typeof input.description === "string" && input.description.trim() ? { description: input.description.trim() } : {},
2407
2416
  promptSection: input.promptSection,
2408
2417
  outputSpec: validateEvaluationRubricOutputSpec(input.outputSpec),
2409
- ...input.status ? { status: input.status } : {}
2418
+ ...input.status ? { status: input.status } : {},
2419
+ ...input.scope ? { scope: input.scope } : {},
2420
+ ...input.ownerCaseId?.trim() ? { ownerCaseId: input.ownerCaseId.trim() } : {}
2410
2421
  };
2411
2422
  }
2412
2423
  function rubricVersionOf(rubrics, fallback) {
2413
2424
  if (!rubrics.length) return fallback;
2414
2425
  return rubrics.map((rubric) => `${rubric.rubricId}@${rubric.version}`).join("+");
2415
2426
  }
2427
+ function rubricRefsOf(rubrics) {
2428
+ return rubrics.map((rubric) => ({
2429
+ rubricId: rubric.rubricId,
2430
+ version: rubric.version,
2431
+ name: rubric.name,
2432
+ kind: rubric.kind,
2433
+ scope: rubric.scope,
2434
+ sectionKey: rubric.outputSpec.sectionKey
2435
+ }));
2436
+ }
2416
2437
  var DIMENSION_ID;
2417
2438
  var init_evaluation_rubric = __esm({
2418
2439
  "../contract/src/evaluation-rubric.ts"() {
@@ -4828,7 +4849,9 @@ function memoryHowToLines() {
4828
4849
  `- **\u7528\u6237\u660E\u663E\u4E0D\u6EE1 / \u5931\u671B / \u53CD\u590D\u50AC**\u2192 \u6211\u54EA\u91CC\u6CA1\u8FBE\u5230\u9884\u671F\u3001\u4E0B\u6B21\u600E\u4E48\u907F\u5F00\uFF1F`,
4829
4850
  `- **\u88AB\u8981\u6C42\u8FD4\u5DE5 / \u6253\u56DE**\u2192 \u8FD9\u662F\u300C\u9879\u76EE\u7279\u6709\u7684\u5751\u300D\uFF0C\u8FD8\u662F\u300C\u6211\u8FD9\u7C7B\u6D3B\u513F\u53CD\u590D\u72AF\u7684\u6BDB\u75C5\u300D\uFF1F`,
4830
4851
  `\u5224\u636E\u4E00\u81F4\uFF1A\u628A\u9879\u76EE\u540D\u53BB\u6389\u8FD9\u53E5\u8BDD\u8FD8\u6210\u7ACB\u5417\u2014\u2014\u6210\u7ACB \`--scope actor\`\uFF0C\u53EA\u5728\u672C\u9879\u76EE\u6210\u7ACB \`--scope project\`\u3002`,
4831
- `\u5199**\u7ED3\u8BBA**\uFF08"\u4E0B\u6B21\u8BE5\u600E\u4E48\u505A\uFF0C\u4E00\u53E5\u8BDD"\uFF09\uFF0C\u4E0D\u5199\u7ECF\u8FC7\u3002`
4852
+ `\u5199**\u7ED3\u8BBA**\uFF08"\u4E0B\u6B21\u8BE5\u600E\u4E48\u505A\uFF0C\u4E00\u53E5\u8BDD"\uFF09\uFF0C\u4E0D\u5199\u7ECF\u8FC7\u3002`,
4853
+ `**\u987A\u5E8F**\uFF1A"\u6536\u5DE5\u524D"\uFF1D**\u7ED9\u51FA\u6700\u7EC8\u7B54\u590D\u4E4B\u524D**\uFF0C\u4E0D\u662F\u4E4B\u540E\u3002\`mem-write\` \u505A\u5B8C\u518D\u5199\u6B63\u6587\uFF0C`,
4854
+ `\u522B\u628A\u300C\u8BB0\u5FC6\u5DF2\u4FDD\u5B58\u300D\u8FD9\u7C7B\u786E\u8BA4\u7559\u6210\u4F60\u7684\u6700\u540E\u4E00\u53E5\u2014\u2014\u5C55\u793A\u9762\u53EA\u628A\u6700\u540E\u4E00\u6BB5\u6B63\u6587\u5F53\u56DE\u590D\uFF0C\u6536\u5C3E\u786E\u8BA4\u4F1A\u9876\u6389\u771F\u6B63\u7684\u7B54\u590D\u3002`
4832
4855
  ];
4833
4856
  }
4834
4857
  function memoryReflectionEscalation(opts) {
@@ -4951,7 +4974,7 @@ function commandReference(opts) {
4951
4974
  `**\u5199**`,
4952
4975
  opts.handler.submitInstruction({ artifactId: id, isPart: true, action: "produce" }),
4953
4976
  `- \`oasis gap ${id} --desc "<\u7F3A\u4EC0\u4E48>"\` \u2014\u2014 **\u505A\u4E0D\u4E0B\u53BB\u65F6\u62A5**\uFF1A\u7F3A\u8F93\u5165 / \u7F3A\u4FE1\u606F / \u8981\u4EBA\u51B3\u7B56 / \u7F3A\u80FD\u529B\uFF08\u5DE5\u5177\u88C5\u4E0D\u4E0A\u7B49\uFF09\u90FD\u7B97\u3002\`--desc\` \u8BF4\u6E05\u7F3A\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u5361\u4F4F\uFF08**\u4F60\u5728\u54EA\u4E2A\u90E8\u4EF6\u7CFB\u7EDF\u5DF2\u77E5\u9053\uFF0C\u4E0D\u7528\u4F60\u586B**\uFF09\u3002\u62A5\u5B8C**\u6536\u5DE5**\uFF1A\u8FD9\u6761\u90E8\u4EF6\u4F1A\u6682\u505C\uFF0C\u7BA1\u7406\u8005\u5206\u8BCA\uFF08\u56FE\u4E0A\u8865 / \u627E\u4EBA\uFF09\uFF1B\u9700\u6C42\u6EE1\u8DB3\u540E\u7CFB\u7EDF\u4F1A\u5E26\u7740\u89E3\u6CD5\u8BF4\u660E\u91CD\u65B0\u5524\u8D77\u4F60\uFF1B\u671F\u95F4\u522B propose\u3002`,
4954
- `- \`oasis annotate <\u4E0A\u6E38id> --from ${id} --part <\u4F60\u7684\u90E8\u4EF6> --body "<\u7591\u95EE>"\` \u2014\u2014 \u5BF9\u4E0A\u6E38\u63D0\u7591\u95EE/\u5F02\u8BAE\uFF08\u56DE\u538B\uFF09\u3002\`<\u4E0A\u6E38id>\`=\u88AB\u95EE\u7684\u4E0A\u6E38\uFF1B\`--from\`=\u672C\u4EA7\u7269\uFF1B\`--part\`=\u4F60\u8FD9\u4E2A\u90E8\u4EF6\uFF1B\`--body\`=\u5177\u4F53\u95EE\u9898\u3002\u63D0\u5B8C**\u6536\u5DE5**\u7B49\u7B54\u590D\uFF1B\u63D0\u95EE\u4E4B\u540E\u522B propose\uFF08\u591A\u4EFB\u52A1\u65F6\u540C\u6837**\u5148\u4EA4\u4ED8\u540E\u62A5\u969C**\uFF09\u3002`,
4977
+ `- \`oasis annotate <\u76EE\u6807\u4EA7\u7269id> --body "<\u4F60\u8981\u8BF4\u7684>"\` \u2014\u2014 **\u5728\u522B\u4EBA\u7684\u4EA7\u7269\u4E0A\u5199\u4E00\u6761\u8BC4\u8BBA**\u3002\u8BF4\u4EC0\u4E48\u7531\u4F60\u5B9A\uFF1A\u7591\u95EE\u3001\u5F02\u8BAE\u3001\u4FEE\u6539\u610F\u89C1\u3001\u8981\u6C42\u8FD4\u5DE5\u3001\u5224\u5B9A\u672A\u8FBE\u6807\u3001\u6216\u53EA\u662F\u63D0\u9192\u2014\u2014\u4F5C\u7528\u53D6\u51B3\u4E8E\u4F60\u5199\u7684\u5185\u5BB9\uFF0C\u547D\u4EE4\u672C\u8EAB\u4E0D\u533A\u5206\u3002\u5199\u7ED9**\u4F60\u7684\u4E0A\u6E38**\u65F6\uFF0C\u8FD9\u6761\u8BC4\u8BBA\u81EA\u52A8\u5E26\u4E0A\u963B\u585E\u529B\uFF1A**\u4E0A\u6E38\u7B54\u590D\u5B83\u4E4B\u524D\u4E0D\u80FD\u6536\u5C3E**\uFF0C\u4E14\u5B83\u7B54\u590D\u540E\u7CFB\u7EDF\u4F1A\u91CD\u65B0\u5524\u8D77\u4F60\uFF08\u4F60\u5728\u54EA\u4E2A\u90E8\u4EF6\u3001\u4EE3\u8868\u54EA\u4E2A\u4EA7\u7269\uFF0C\u7CFB\u7EDF\u90FD\u5DF2\u77E5\u9053\uFF0C\u4E0D\u7528\u4F60\u586B\uFF09\u3002`,
4955
4978
  ``,
4956
4979
  `**\u8BFB**\uFF08\u4F60\u662F\u53F6\u5B50\u5DE5\u4EBA\uFF0C\u53EA\u770B\u4F60\u4F9D\u8D56\u7684\u4E1C\u897F\uFF0C\u4E0D\u770B\u5168\u5C40\uFF09`,
4957
4980
  `- \`oasis status <id>\` / \`oasis content <id|revisionId> [path]\` \u2014\u2014 \u770B\u4F60\u4F9D\u8D56\u7684\u5144\u5F1F\u90E8\u4EF6 / \u4E0A\u6E38\u8F93\u5165\u7684\u72B6\u6001\u7D22\u5F15\u4E0E\u5B9E\u9645\u5185\u5BB9\uFF08\`status\` \u7ED9\u5168\u90E8\u7248\u672C\u7684\u5B8C\u6574 id + \u8BC4\u5BA1\u7968 + \u6279\u6CE8 id\uFF1B\`content\` \u4F20 \`revision:\u2026\` \u53D6\u6307\u5B9A\u7248\u672C\u6B63\u6587\u3001\u4F20 \`annotation:\u2026\`/\`review:\u2026\` \u53D6\u6279\u6CE8\u6216\u8BC4\u5BA1\u7968\u5168\u6587\uFF09\u3002`
@@ -4971,8 +4994,8 @@ function commandReference(opts) {
4971
4994
  write.push(
4972
4995
  `- \`oasis conclude ${id}\` \u2014\u2014 \u5BA3\u5E03\u4EA7\u7269**\u5B8C\u6574\u3001\u53EF\u88AB\u4E0B\u6E38\u6D88\u8D39**\uFF0C\u76D6\u4E00\u4E2A\u91CC\u7A0B\u7891\u3002\u524D\u63D0\uFF1A\u961F\u5217\u5DF2\u6E05\u7A7A\u3001\u4E14\u65E0\u672A\u89E3\u6790 gap / \u672A\u7B54\u590D\u56DE\u538B\uFF08\u5426\u5219\u88AB\u62D2\uFF09\uFF1B\u6709\u672A\u5904\u7F6E\u6765\u4FE1\u5148\u5904\u7F6E\u5B8C\uFF08\u56DE\u590D\u6216\u6539\u540E\u56DE\u590D\uFF09\u2014\u2014\u7CFB\u7EDF\u4E0D\u4F1A\u5728\u6709\u6765\u4FE1\u65F6\u66FF\u4F60\u81EA\u52A8\u6536\u5C3E\u3002\u76D6\u5B8C\u4E0B\u6E38\u6536\u5230\u901A\u77E5\u3002`,
4973
4996
  `- \`oasis add-revision ${id} --desc "<\u8FD9\u90E8\u4EF6\u505A\u4EC0\u4E48>" [--name <\u90E8\u4EF6\u540D>] [--role <\u89D2\u8272>]\` \u2014\u2014 \u628A\u672C\u4EA7\u7269\u62C6\u51FA\u4E00\u4E2A**\u53EF\u5E76\u884C\u4EA7\u51FA\u7684\u5B50\u90E8\u4EF6**\u6D3E\u51FA\u53BB\u3002\`--desc\`\uFF08**\u5FC5\u586B**\uFF09= \u8FD9\u4E2A\u5B50\u90E8\u4EF6\u8981\u5B8C\u6210\u4EC0\u4E48\uFF1B\`--name\` = \u7ED9\u5B83\u8D77\u7684\u90E8\u4EF6\u540D\uFF08\u53EF\u9009\uFF0C\u7F3A\u7701\u81EA\u52A8\u751F\u6210\uFF09\uFF1B\`--role\` = \u8C01\u6765\u505A\uFF08\u53EF\u9009\uFF0C\u7F3A\u7701=\u4F60\u81EA\u5DF1\uFF09\u3002${opts.handler.mergeMode === "external-ff" ? "\u5404\u90E8\u4EF6\u628A\u6539\u52A8\u5E76\u5165\u5DE5\u5355\u5206\u652F\u540E\uFF0C\u4F60 `oasis propose` \u6536\u5168 feat \u6536\u53E3\uFF08git-native\uFF0C\u522B\u7528 integrate\uFF09\u3002" : "\u5404\u90E8\u4EF6\u4EA7\u5B8C\u7531\u4F60 `oasis integrate` \u6536\u53E3\u3002"}`,
4974
- `- \`oasis gap ${id} --desc "<\u7F3A\u4EC0\u4E48>"\` \u2014\u2014 **\u505A\u4E0D\u4E0B\u53BB\u65F6\u62A5**\uFF1A\u7F3A\u8F93\u5165 / \u7F3A\u4FE1\u606F / \u8981\u4EBA\u51B3\u7B56 / \u7F3A\u80FD\u529B\uFF08\u5DE5\u5177\u88C5\u4E0D\u4E0A\u7B49\uFF09\u90FD\u7B97\u3002\`--desc\` \u8BF4\u6E05\u7F3A\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u5361\u4F4F\uFF08\u7BA1\u7406\u8005\u636E\u5B83\u5206\u8BCA\uFF1A\u56FE\u4E0A\u8865 / \u627E\u4EBA\uFF09\u3002\u62A5\u5B8C**\u6536\u5DE5**\uFF1A\u8FD9\u6761\u4F1A\u6682\u505C\uFF0C\u9700\u6C42\u6EE1\u8DB3\u540E\u7CFB\u7EDF\u4F1A\u5E26\u7740\u89E3\u6CD5\u8BF4\u660E\u91CD\u65B0\u5524\u8D77\u4F60\uFF1B\u62A5\u969C\u4E4B\u540E\u522B propose\uFF08**\u5148\u4EA4\u4ED8\u540E\u62A5\u969C**\uFF1A\u672C\u8F6E\u82E5\u8FD8\u6709\u5DF2\u5B8C\u6210\u7684\u4EFB\u52A1\uFF0C\u5148 propose \u518D\u62A5\u2014\u2014\u62A5\u4E86\u8FD9\u6761\u7EBF\u5C31\u4EA4\u4E0D\u51FA\u7A3F\u4E86\uFF09\u3002`,
4975
- `- \`oasis annotate <\u4E0A\u6E38id> --from ${id} --body "<\u7591\u95EE>"\` \u2014\u2014 \u5BF9\u67D0\u4E0A\u6E38\u63D0\u7591\u95EE/\u5F02\u8BAE\uFF08\u56DE\u538B\uFF09\u3002\`<\u4E0A\u6E38id>\`=\u88AB\u95EE\u7684\u4E0A\u6E38\uFF1B\`--from\`=\u672C\u4EA7\u7269\uFF08\u7CFB\u7EDF\u636E\u6B64\u5728\u4E0A\u6E38\u7B54\u590D\u540E\u91CD\u6D3E\u4F60\uFF09\uFF1B\`--body\`=\u5177\u4F53\u95EE\u9898\u3002\u63D0\u5B8C**\u6536\u5DE5**\u7B49\u7B54\u590D\uFF1B\u63D0\u95EE\u4E4B\u540E\u522B propose\uFF08\u591A\u4EFB\u52A1\u65F6\u540C\u6837**\u5148\u4EA4\u4ED8\u540E\u62A5\u969C**\uFF09\u3002`,
4997
+ `- \`oasis gap ${id} --desc "<\u7F3A\u4EC0\u4E48>"\` \u2014\u2014 **\u505A\u4E0D\u4E0B\u53BB\u65F6\u62A5**\uFF1A\u7F3A\u8F93\u5165 / \u7F3A\u4FE1\u606F / \u8981\u4EBA\u51B3\u7B56 / \u7F3A\u80FD\u529B\uFF08\u5DE5\u5177\u88C5\u4E0D\u4E0A\u7B49\uFF09\u90FD\u7B97\u3002\`--desc\` \u8BF4\u6E05\u7F3A\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u5361\u4F4F\uFF08\u7BA1\u7406\u8005\u636E\u5B83\u5206\u8BCA\uFF1A\u56FE\u4E0A\u8865 / \u627E\u4EBA\uFF09\u3002\u62A5\u5B8C**\u6536\u5DE5**\uFF1A\u8FD9\u6761\u4F1A\u6682\u505C\uFF0C\u9700\u6C42\u6EE1\u8DB3\u540E\u7CFB\u7EDF\u4F1A\u5E26\u7740\u89E3\u6CD5\u8BF4\u660E\u91CD\u65B0\u5524\u8D77\u4F60\u3002`,
4998
+ `- \`oasis annotate <\u76EE\u6807\u4EA7\u7269id> --body "<\u4F60\u8981\u8BF4\u7684>"\` \u2014\u2014 **\u5728\u522B\u4EBA\u7684\u4EA7\u7269\u4E0A\u5199\u4E00\u6761\u8BC4\u8BBA**\u3002\u8BF4\u4EC0\u4E48\u7531\u4F60\u5B9A\uFF1A\u7591\u95EE\u3001\u5F02\u8BAE\u3001\u4FEE\u6539\u610F\u89C1\u3001\u8981\u6C42\u8FD4\u5DE5\u3001\u5224\u5B9A\u672A\u8FBE\u6807\u3001\u6216\u53EA\u662F\u63D0\u9192\u2014\u2014\u4F5C\u7528\u53D6\u51B3\u4E8E\u4F60\u5199\u7684\u5185\u5BB9\uFF0C\u547D\u4EE4\u672C\u8EAB\u4E0D\u533A\u5206\u3002\u5199\u7ED9**\u4F60\u7684\u4E0A\u6E38**\u65F6\uFF0C\u8FD9\u6761\u8BC4\u8BBA\u81EA\u52A8\u5E26\u4E0A\u963B\u585E\u529B\uFF1A**\u4E0A\u6E38\u7B54\u590D\u5B83\u4E4B\u524D\u4E0D\u80FD\u6536\u5C3E**\uFF0C\u4E14\u5B83\u7B54\u590D\u540E\u7CFB\u7EDF\u4F1A\u91CD\u65B0\u5524\u8D77\u4F60\uFF08\u4EE3\u8868\u54EA\u4E2A\u4EA7\u7269\u63D0\u7684\uFF0C\u7CFB\u7EDF\u5DF2\u77E5\u9053\uFF0C\u4E0D\u7528\u4F60\u586B\uFF09\u3002`,
4976
4999
  `- \`oasis project content-location add <projectId> (--system <s> --config '<json>' | --file <path>)\` \u2014\u2014 \u7ED9\u9879\u76EE\u767B\u8BB0\u4E00\u5904\u5185\u5BB9\u4F4D\u7F6E\uFF1A\u5916\u90E8 home\uFF08\u5982\u4F60\u5EFA\u597D\u7684 git \u4ED3\uFF0C\`--system\`+\`--config\`\uFF1B**\u53EA\u767B\u8BB0\u6307\u9488\u3001\u4E0D\u521B\u5EFA\u8D44\u6E90\u672C\u8EAB**\u2014\u2014\u5EFA\u4ED3\u7528 gh \u7B49\u6258\u7BA1\u65B9 CLI\uFF09\u6216\u5185\u90E8\u53C2\u8003\u6587\u4EF6\uFF08\`--file\`\uFF09\u3002config \u5F62\u72B6\u968F system\uFF0C\u7528\`content-location systems <s>\` \u67E5\u5B57\u6BB5\u3002`
4977
5000
  );
4978
5001
  if (opts.hasOpenAnnotations) {
@@ -5298,7 +5321,7 @@ ${manifestDiff(baseC, newC)}
5298
5321
  whyLines.push(``, `\uFF08\u5DF2\u5173\u95ED\u7684\u5386\u53F2\u8BC4\u8BBA\u4E0D\u5217\u5165\u6E05\u5355\u2014\u2014\u9700\u8981\u65F6\u7528 \`oasis show ${artifactId}\` \u67E5\u3002\uFF09`);
5299
5322
  }
5300
5323
  whatLines.push(
5301
- queueTaskDue ? `**\u987A\u5E8F**\uFF1A\u2460 \u7EAF\u56DE\u590D\u7C7B\u7684\u4FE1\u5148\u56DE\u6389\uFF08\`oasis resolve\`/\`oasis reply\`\u2014\u2014**\u56DE\u590D\u6C38\u4E0D\u963B\u585E**\uFF09\u2192 \u2461 \u628A\u961F\u5217\u91CC\u90A3\u4E9B\u7248\u672C\u7684\u610F\u56FE**\u8FDE\u540C\u672C\u8F6E\u5176\u5B83\u4EFB\u52A1\u7684\u6539\u52A8\u5408\u6210\u4E00\u7248**\uFF0C\u7528 \`oasis integrate\` \u4EA4\uFF08**\u522B\u7528 \`oasis propose\`**\u2014\u2014\u90A3\u53EA\u4F1A\u518D\u52A0\u4E00\u6761\u6392\u961F\u7248\u672C\uFF0C\u961F\u5217\u66F4\u957F\uFF09\u2192 \u2462 \u5361\u4F4F\u7684\u4EFB\u52A1\u6B64\u65F6\u624D \`oasis gap\` / \u5411\u4E0A\u6E38 \`oasis annotate --from\`\uFF08**\u5148\u4EA4\u4ED8\u540E\u62A5\u969C**\uFF09\u2192 \u2463 \u6536\u5DE5\u3002**\u5168\u90E8\u4EFB\u52A1\u5904\u7406\u5B8C\u624D\u6536\u5DE5\u3002**` : `**\u987A\u5E8F**\uFF1A\u2460 \u7EAF\u56DE\u590D\u7C7B\u7684\u4FE1\u5148\u56DE\u6389\uFF08\`oasis resolve\`/\`oasis reply\`\u2014\u2014**\u56DE\u590D\u6C38\u4E0D\u963B\u585E**\uFF0C\u5148\u56DE\u514D\u5F97\u63D0\u4EA4\u88AB\u65E0\u5173\u7684\u4FE1\u62BC\u7740\uFF09\u2192 \u2461 \u9700\u8981\u6539\u5185\u5BB9\u7684\u4EFB\u52A1\u6539\u52A8**\u5408\u6210\u4E00\u7248** propose\uFF08\`--reason\` **\u6309\u4EFB\u52A1\u9010\u6761**\u5199\u6E05\uFF1A"\u4EFB\u52A11\uFF1A\u2026\uFF1B\u4EFB\u52A12\uFF1A\u2026"\uFF09\u2192 \u2462 \u5361\u4F4F\u7684\u4EFB\u52A1\u6B64\u65F6\u624D \`oasis gap\` / \u5411\u4E0A\u6E38 \`oasis annotate --from\`\uFF08**\u5148\u4EA4\u4ED8\u540E\u62A5\u969C**\u2014\u2014\u53CD\u8FC7\u6765\u8FD9\u6761\u7EBF\u5C31\u4E0D\u80FD propose \u4E86\uFF0C\u4F60\u5DF2\u5B8C\u6210\u7684\u5DE5\u4F5C\u4EA4\u4E0D\u51FA\u53BB\uFF09\u2192 \u2463 \u6536\u5DE5\u3002**\u5168\u90E8\u4EFB\u52A1\u5904\u7406\u5B8C\u624D\u6536\u5DE5\u3002**`
5324
+ queueTaskDue ? `**\u987A\u5E8F**\uFF1A\u2460 \u7EAF\u56DE\u590D\u7C7B\u7684\u4FE1\u5148\u56DE\u6389\uFF08\`oasis resolve\`/\`oasis reply\`\u2014\u2014**\u56DE\u590D\u6C38\u4E0D\u963B\u585E**\uFF09\u2192 \u2461 \u628A\u961F\u5217\u91CC\u90A3\u4E9B\u7248\u672C\u7684\u610F\u56FE**\u8FDE\u540C\u672C\u8F6E\u5176\u5B83\u4EFB\u52A1\u7684\u6539\u52A8\u5408\u6210\u4E00\u7248**\uFF0C\u7528 \`oasis integrate\` \u4EA4\uFF08**\u522B\u7528 \`oasis propose\`**\u2014\u2014\u90A3\u53EA\u4F1A\u518D\u52A0\u4E00\u6761\u6392\u961F\u7248\u672C\uFF0C\u961F\u5217\u66F4\u957F\uFF09\u2192 \u2462 \u9700\u8981\u4E0A\u6E38\u8FD4\u5DE5\u7684\u5411\u4E0A\u6E38 \`oasis annotate\`\u3001\u9700\u8981\u4EBA\u4ECB\u5165\u7684 \`oasis gap\` \u2192 \u2463 \u6536\u5DE5\u3002**\u5168\u90E8\u4EFB\u52A1\u5904\u7406\u5B8C\u624D\u6536\u5DE5\u3002**` : `**\u987A\u5E8F**\uFF1A\u2460 \u7EAF\u56DE\u590D\u7C7B\u7684\u4FE1\u5148\u56DE\u6389\uFF08\`oasis resolve\`/\`oasis reply\`\u2014\u2014**\u56DE\u590D\u6C38\u4E0D\u963B\u585E**\uFF0C\u5148\u56DE\u514D\u5F97\u63D0\u4EA4\u88AB\u65E0\u5173\u7684\u4FE1\u62BC\u7740\uFF09\u2192 \u2461 \u9700\u8981\u6539\u5185\u5BB9\u7684\u4EFB\u52A1\u6539\u52A8**\u5408\u6210\u4E00\u7248** propose\uFF08\`--reason\` **\u6309\u4EFB\u52A1\u9010\u6761**\u5199\u6E05\uFF1A"\u4EFB\u52A11\uFF1A\u2026\uFF1B\u4EFB\u52A12\uFF1A\u2026"\uFF09\u2192 \u2462 \u9700\u8981\u4E0A\u6E38\u8FD4\u5DE5\u7684\u5411\u4E0A\u6E38 \`oasis annotate\`\u3001\u9700\u8981\u4EBA\u4ECB\u5165\u7684 \`oasis gap\` \u2192 \u2463 \u6536\u5DE5\u3002**\u5168\u90E8\u4EFB\u52A1\u5904\u7406\u5B8C\u624D\u6536\u5DE5\u3002**`
5302
5325
  );
5303
5326
  } else if (integrating) {
5304
5327
  whyLines.push(`**\u5404\u90E8\u4EF6\u5DF2\u4EA7\u9F50\uFF0C\u5F85\u4F60\u96C6\u6210**\uFF1A\u4F60\u5148\u524D\u6D3E\u51FA\u53BB\u7684\u5B50\u90E8\u4EF6\u90FD\u4EA7\u51FA\u4E86\uFF0C\u73B0\u5728\u7531\u4F60 owner \u6536\u53E3\u6210\u4E00\u7248\u5B8C\u6574\u3001\u81EA\u6D3D\u7684 R\\*\u3002`);
@@ -5662,8 +5685,11 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
5662
5685
  ``,
5663
5686
  `## \u751F\u4EA7\u8005\u5B88\u5219\uFF08\u56FA\u5B9A\uFF0C\u6BCF\u4E2A\u751F\u4EA7\u8005\u90FD\u4E00\u6837\uFF09`,
5664
5687
  `- \u4F60\u53EA\u4EA7**\u8FD9\u4E00\u4E2A**\u4EA7\u7269\uFF0C\u522B\u66FF\u522B\u7684\u4EA7\u7269\u62CD\u677F\u3001\u522B\u8D8A\u754C\u6539\u522B\u4EBA\u7684\u4E1C\u897F\uFF1B**\u4E0D\u5F52\u4F60\u5B9A\u7684\u51B3\u7B56\u522B\u5728\u5047\u8BBE\u4E0A\u786C\u7F16**\uFF08\u4F1A\u88AB\u6253\u56DE\uFF09\u3002`,
5665
- `- \u7F3A\u4E00\u4E2A**\u8FD8\u4E0D\u5B58\u5728\u7684\u524D\u7F6E**\u505A\u4E0D\u4E0B\u53BB \u2192 \`oasis gap\` \u62A5\u7F3A\u53E3\u540E**\u6536\u5DE5**\uFF0C\u522B\u786C\u7F16\uFF1B\u63D0\u4E86 gap \u5C31\u522B\u518D propose\uFF08\u8FD9\u6761\u4F1A\u6682\u505C\uFF0C\u524D\u7F6E\u5C31\u7EEA\u540E\u7CFB\u7EDF\u91CD\u65B0\u5524\u8D77\u4F60\uFF09\u3002`,
5666
- `- \u5BF9**\u4E0A\u6E38**\u6709\u7591\u95EE/\u5F02\u8BAE \u2192 \`oasis annotate\` \u63D0\u56DE\u538B\u540E**\u6536\u5DE5**\uFF0C\u540C\u6837\u522B propose\uFF1B\u4E0A\u6E38\u7B54\u590D\u540E\u7CFB\u7EDF\u91CD\u6D3E\u4F60\u3002`,
5688
+ // 这两条**不再预告"提了就别 propose"**:那条闸只对并行部件会话成立(kernel.ts:532/541
5689
+ // `proposingPart !== null` 为前提),整体版 propose 不设防。部件会话仍在自己的命令清单里看到真话
5690
+ // (本文件 :273)。失败条件一律交给内核报错讲——它自带理由与出路,且永不与实现漂移。
5691
+ `- \u7F3A\u4E00\u4E2A**\u8FD8\u4E0D\u5B58\u5728\u7684\u524D\u7F6E**\u505A\u4E0D\u4E0B\u53BB \u2192 \`oasis gap\` \u62A5\u7F3A\u53E3\u540E**\u6536\u5DE5**\uFF0C\u522B\u786C\u7F16\uFF08\u8FD9\u6761\u4F1A\u6682\u505C\uFF0C\u524D\u7F6E\u5C31\u7EEA\u540E\u7CFB\u7EDF\u91CD\u65B0\u5524\u8D77\u4F60\uFF09\u3002`,
5692
+ `- \u5BF9**\u4E0A\u6E38**\u6709\u610F\u89C1\u2014\u2014\u6709\u7591\u95EE\u3001\u8981\u5B83\u6539\u3001\u5224\u5B83\u6CA1\u8FBE\u6807 \u2192 \`oasis annotate <\u4E0A\u6E38id> --body "<\u8BF4\u6E05\u695A>"\` \u540E**\u6536\u5DE5**\u3002\u5199\u7ED9\u4E0A\u6E38\u7684\u8BC4\u8BBA\u81EA\u52A8\u5E26\u963B\u585E\u529B\uFF1A**\u5B83\u7B54\u590D\u524D\u4E0D\u80FD\u6536\u5C3E**\uFF0C\u7B54\u590D\u540E\u7CFB\u7EDF\u91CD\u6D3E\u4F60\u3002`,
5667
5693
  ...args.part === void 0 ? [`- \u6D3B\u592A\u5927 / \u53EF\u5E76\u884C \u2192 \`oasis add-revision\` \u62C6\u6210\u5B50\u90E8\u4EF6\u6D3E\u51FA\u53BB\uFF0C\u522B\u4E00\u4EBA\u4E00\u4F1A\u8BDD\u786C\u625B\u3002`] : [],
5668
5694
  ...batchedContinuation && args.part === void 0 ? [`- \u957F\u4EFB\u52A1\u5DF2\u5141\u8BB8\u5206\u6279\u7EED\u8DD1\uFF1A\u4E00\u6279\u6709\u8FDB\u5C55\u4F46\u6574\u4F53\u672A\u5B8C\u6210\u65F6\uFF0C\u6309\u300C\u600E\u4E48\u63D0\u4EA4\u300D\u91CC\u7684 propose \u547D\u4EE4\u8FFD\u52A0 \`--checkpoint --continue\`\uFF0Creason \u5199\u6E05\u672C\u6279\u5B8C\u6210\u4E86\u4EC0\u4E48\u3001\u5269\u4EC0\u4E48\u3001\u4E0B\u6279\u4ECE\u54EA\u7EE7\u7EED\uFF0C\u7136\u540E\u6536\u5DE5\uFF1B\u7CFB\u7EDF\u4F1A\u6D3E\u53D1\u4E0B\u4E00\u8F6E\u6B63\u5F0F session\u3002\u5168\u90E8\u5B8C\u6210\u65F6\u7167\u5E38 propose \u540E \`oasis conclude ${artifactId}\`\u3002`] : [],
5669
5695
  `- \u5199\u5165\u53EA\u8D70 \`oasis propose\`\uFF0C\u771F\u6B63\u5B8C\u6574\u4E86\u624D \`oasis conclude\`\uFF1B\u5361\u6CA1\u5361\u4F4F\u3001\u8FDB\u5EA6\u90FD\u662F\u7CFB\u7EDF\u7B97\u7684\uFF0C\u522B\u81EA\u5DF1\u7EF4\u62A4\u3002`,
@@ -6562,8 +6588,8 @@ var init_dispatcher = __esm({
6562
6588
  /** 从 produce jobKey 解析 artifactId:`produce::<artifactId>` 或 `produce::<artifactId>::<part>`。 */
6563
6589
  artifactIdFromJobKey(jobKey) {
6564
6590
  const rest = jobKey.slice(jobKey.indexOf("::") + 2);
6565
- const sep3 = rest.indexOf("::");
6566
- return sep3 >= 0 ? rest.slice(0, sep3) : rest;
6591
+ const sep4 = rest.indexOf("::");
6592
+ return sep4 >= 0 ? rest.slice(0, sep4) : rest;
6567
6593
  }
6568
6594
  /**
6569
6595
  * 某节点的派发器运行时信号(stuckDiagnosis 的非 oplog 那半;proposal 协调者阻塞诊断 D1)。
@@ -7663,36 +7689,57 @@ var init_store = __esm({
7663
7689
  });
7664
7690
 
7665
7691
  // ../core/src/evaluation/rubric-store.ts
7666
- var import_node_crypto4, bindingKey, MemoryEvaluationRubricStore;
7692
+ var import_node_crypto4, bindingKey, versionKey, snapshotOf, MemoryEvaluationRubricStore;
7667
7693
  var init_rubric_store = __esm({
7668
7694
  "../core/src/evaluation/rubric-store.ts"() {
7669
7695
  "use strict";
7670
7696
  import_node_crypto4 = require("node:crypto");
7671
7697
  bindingKey = (companyId, caseId) => JSON.stringify([companyId, caseId]);
7698
+ versionKey = (rubricId, version2) => JSON.stringify([rubricId, version2]);
7699
+ snapshotOf = (rubric) => ({
7700
+ rubricId: rubric.rubricId,
7701
+ version: rubric.version,
7702
+ name: rubric.name,
7703
+ kind: rubric.kind,
7704
+ scope: rubric.scope,
7705
+ ...rubric.ownerCaseId ? { ownerCaseId: rubric.ownerCaseId } : {},
7706
+ ...rubric.description ? { description: rubric.description } : {},
7707
+ promptSection: rubric.promptSection,
7708
+ outputSpec: structuredClone(rubric.outputSpec),
7709
+ createdBy: rubric.updatedBy,
7710
+ createdAt: rubric.updatedAt
7711
+ });
7672
7712
  MemoryEvaluationRubricStore = class {
7673
7713
  rubrics = /* @__PURE__ */ new Map();
7714
+ versions = /* @__PURE__ */ new Map();
7674
7715
  bindings = /* @__PURE__ */ new Map();
7675
- async listRubrics(companyId) {
7676
- return [...this.rubrics.values()].filter((rubric) => rubric.companyId === companyId).sort((left, right) => left.name.localeCompare(right.name)).map((rubric) => structuredClone(rubric));
7716
+ async listRubrics(companyId, scope = "shared") {
7717
+ return [...this.rubrics.values()].filter((rubric) => rubric.companyId === companyId && rubric.scope === scope).sort((left, right) => left.name.localeCompare(right.name)).map((rubric) => structuredClone(rubric));
7677
7718
  }
7678
7719
  async getRubric(rubricId) {
7679
7720
  const rubric = this.rubrics.get(rubricId);
7680
7721
  return rubric ? structuredClone(rubric) : null;
7681
7722
  }
7723
+ async getCaseRubric(companyId, caseId) {
7724
+ const found = [...this.rubrics.values()].find((rubric) => rubric.companyId === companyId && rubric.scope === "case" && rubric.ownerCaseId === caseId);
7725
+ return found ? structuredClone(found) : null;
7726
+ }
7682
7727
  async upsertRubric(input, at) {
7683
7728
  const existing = input.rubricId ? this.rubrics.get(input.rubricId) : void 0;
7684
7729
  if (existing && existing.companyId !== input.companyId) {
7685
7730
  throw new Error(`\u8BC4\u5206\u6807\u51C6 ${input.rubricId} \u4E0D\u5C5E\u4E8E\u5F53\u524D\u516C\u53F8`);
7686
7731
  }
7732
+ const scope = input.scope ?? existing?.scope ?? "shared";
7687
7733
  const rubric = {
7688
7734
  rubricId: existing?.rubricId ?? input.rubricId ?? (0, import_node_crypto4.randomUUID)(),
7689
7735
  companyId: input.companyId,
7690
7736
  name: input.name,
7691
7737
  kind: input.kind,
7692
7738
  ...input.description ? { description: input.description } : {},
7739
+ scope,
7740
+ ...scope === "case" && (input.ownerCaseId ?? existing?.ownerCaseId) ? { ownerCaseId: input.ownerCaseId ?? existing?.ownerCaseId } : {},
7693
7741
  promptSection: input.promptSection,
7694
7742
  outputSpec: structuredClone(input.outputSpec),
7695
- // 版本随每次保存自增:判分记录靠 rubricId@version 复现「当时用的是哪一版标准」。
7696
7743
  version: (existing?.version ?? 0) + 1,
7697
7744
  status: input.status ?? existing?.status ?? "active",
7698
7745
  createdBy: existing?.createdBy ?? input.actor,
@@ -7701,6 +7748,7 @@ var init_rubric_store = __esm({
7701
7748
  updatedAt: at
7702
7749
  };
7703
7750
  this.rubrics.set(rubric.rubricId, rubric);
7751
+ this.versions.set(versionKey(rubric.rubricId, rubric.version), snapshotOf(rubric));
7704
7752
  return structuredClone(rubric);
7705
7753
  }
7706
7754
  async deleteRubric(rubricId, companyId) {
@@ -7715,6 +7763,13 @@ var init_rubric_store = __esm({
7715
7763
  });
7716
7764
  }
7717
7765
  }
7766
+ async listRubricVersions(rubricId) {
7767
+ return [...this.versions.values()].filter((version2) => version2.rubricId === rubricId).sort((left, right) => right.version - left.version).map((version2) => structuredClone(version2));
7768
+ }
7769
+ async getRubricVersion(rubricId, version2) {
7770
+ const found = this.versions.get(versionKey(rubricId, version2));
7771
+ return found ? structuredClone(found) : null;
7772
+ }
7718
7773
  async listBindings(companyId) {
7719
7774
  return [...this.bindings.values()].filter((binding) => binding.companyId === companyId).map((binding) => structuredClone(binding));
7720
7775
  }
@@ -139424,9 +139479,9 @@ function agentEdgesFor(dependsOn, nodes, briefId, issues) {
139424
139479
  edges.add(briefId);
139425
139480
  continue;
139426
139481
  }
139427
- const sep3 = d.indexOf(":");
139428
- const depType = (sep3 >= 0 ? d.slice(0, sep3) : d).trim().toLowerCase();
139429
- const depTitle = sep3 >= 0 ? d.slice(sep3 + 1).trim().toLowerCase() : null;
139482
+ const sep4 = d.indexOf(":");
139483
+ const depType = (sep4 >= 0 ? d.slice(0, sep4) : d).trim().toLowerCase();
139484
+ const depTitle = sep4 >= 0 ? d.slice(sep4 + 1).trim().toLowerCase() : null;
139430
139485
  const match = nodes.find((n) => n.type === depType && (depTitle === null || (n.title ?? "").toLowerCase() === depTitle));
139431
139486
  if (match) edges.add(match.id);
139432
139487
  else issues.push({ code: "planner_invalid", severity: "warning", message: `\u8282\u70B9\u4F9D\u8D56\u300C${dep}\u300D\u627E\u4E0D\u5230\u5BF9\u5E94\u4E0A\u6E38\uFF08\u6309 type:title \u5339\u914D\uFF09\uFF0C\u5DF2\u5FFD\u7565\u8FD9\u6761\u8FB9\u3002` });
@@ -140764,6 +140819,13 @@ function resolveWrapperScript(wrapperDir, slug6) {
140764
140819
  }
140765
140820
  return null;
140766
140821
  }
140822
+ function isOasisWrapperPath(p2) {
140823
+ try {
140824
+ return (0, import_node_fs2.realpathSync)(p2).endsWith(`${import_node_path.sep}wrapper.sh`);
140825
+ } catch {
140826
+ return false;
140827
+ }
140828
+ }
140767
140829
  function wrapperCandidates(wrapperDir, slug6) {
140768
140830
  const override = process.env[ASSETS_DIR_ENV];
140769
140831
  if (override) return [(0, import_node_path.resolve)(override, slug6, "wrapper.sh")];
@@ -141061,13 +141123,27 @@ var init_cli_connector = __esm({
141061
141123
  }
141062
141124
  }
141063
141125
  /**
141064
- * 用 `which` 解析真实二进制的绝对路径;找不到就原样返回命令名。
141065
- * 必须在**跑 agent 的那台机器**上解析——server 侧算出的路径在远程节点上并不存在。
141126
+ * 用 `which -a` 解析真实二进制的绝对路径,**跳过 oasis 自己 stage 出来的 wrapper**;
141127
+ * 一个都没有就原样返回命令名。必须在**跑 agent 的那台机器**上解析——server 侧算出的
141128
+ * 路径在远程节点上并不存在。
141129
+ *
141130
+ * 为什么要跳过 wrapper(2026-08-03 实测踩到):`which` 吃的是**本进程的 PATH**,而
141131
+ * daemon 的 PATH 可能被 agent 会话的 wrapper 目录污染(`oasis daemon start` 把当时
141132
+ * shell 的 PATH 固化进 systemd unit——在 agent 会话里触发更新就会带进 /tmp/oasis-conn-*)。
141133
+ * 裸 `which gh` 于是解析到 wrapper 自己,注入的 GH_BIN 指回 wrapper,wrapper 再 exec
141134
+ * 它 → 无限递归。节点侧已在 daemon 启动时清洗 PATH,这里是第二道闸:
141135
+ * **解析结果一旦是我们自己的 wrapper 就继续往后找。**
141066
141136
  */
141067
141137
  async resolveRealBin(name) {
141068
141138
  try {
141069
- const { stdout } = await execFile3("which", [name]);
141070
- return stdout.trim();
141139
+ const { stdout } = await execFile3("which", ["-a", name]);
141140
+ for (const line of stdout.split("\n")) {
141141
+ const candidate = line.trim();
141142
+ if (!candidate) continue;
141143
+ if (isOasisWrapperPath(candidate)) continue;
141144
+ return candidate;
141145
+ }
141146
+ return name;
141071
141147
  } catch {
141072
141148
  return name;
141073
141149
  }
@@ -141618,7 +141694,10 @@ var init_github = __esm({
141618
141694
  "GIT_AUTHOR_EMAIL",
141619
141695
  "GIT_COMMITTER_NAME",
141620
141696
  "GIT_COMMITTER_EMAIL",
141621
- "EMAIL"
141697
+ "EMAIL",
141698
+ "GIT_CONFIG_COUNT",
141699
+ "GIT_CONFIG_KEY_0",
141700
+ "GIT_CONFIG_VALUE_0"
141622
141701
  ];
141623
141702
  /** 自检:问 GitHub「我是谁」——这是唯一能戳穿「静默用成宿主机身份」的检查。 */
141624
141703
  verifyArgs = ["api", "user", "--jq", ".login"];
@@ -141658,6 +141737,9 @@ var init_github = __esm({
141658
141737
  sessionEnv.set("GIT_COMMITTER_NAME", name);
141659
141738
  sessionEnv.set("GIT_COMMITTER_EMAIL", "");
141660
141739
  sessionEnv.set("EMAIL", "");
141740
+ sessionEnv.set("GIT_CONFIG_COUNT", "1");
141741
+ sessionEnv.set("GIT_CONFIG_KEY_0", "credential.https://github.com.helper");
141742
+ sessionEnv.set("GIT_CONFIG_VALUE_0", "!gh auth git-credential");
141661
141743
  }
141662
141744
  };
141663
141745
  }
@@ -147304,14 +147386,17 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}`] : []
147304
147386
  return { message: revisionArg !== void 0 ? `\u9A8C\u6536\u7968\u5DF2\u8BB0\u5F55\uFF08${status === "pending" ? "\u7B49\u5176\u4F59\u4F5C\u8005/\u5408\u5165" : status}\uFF09` : `review \u5DF2\u8BB0\u5F55\uFF1Bgate \u2192 ${status}` };
147305
147387
  }
147306
147388
  case "annotate": {
147307
- const raisedFrom = optStr(args, "raisedFrom");
147389
+ const targetId = str(args, "artifactId");
147390
+ const sessionNode = ctx.sessionArtifactId;
147391
+ const autoRaisedFrom = optStr(args, "raisedFrom") === void 0 && sessionNode !== void 0 && sessionNode !== targetId && (kernel.model.artifacts.get(sessionNode)?.inputs ?? []).some((e) => e.to === targetId) ? sessionNode : void 0;
147392
+ const raisedFrom = optStr(args, "raisedFrom") ?? autoRaisedFrom;
147308
147393
  const attachments = attachmentArgs(args);
147309
147394
  const mentions = Array.isArray(args["mentions"]) ? args["mentions"].filter((x2) => typeof x2 === "string" && x2.startsWith("actor:")) : void 0;
147310
147395
  if (mentions !== void 0 && mentions.length > 0 && isAgent(actor)) {
147311
147396
  throw new Error("@ \u6743\u53EA\u5C5E\u4E8E\u4EBA\uFF1Aagent \u627E agent \u7528\u56DE\u538B\uFF08annotate --from\uFF09\u6216\u5199\u4FE1\u5230\u5B83\u7684\u4EA7\u7269\uFF1B\u66FF\u4EBA @ \u9700\u5728 1:1 \u4F1A\u8BDD\u4E2D\uFF08\u7F72\u540D\u4EE3\u7F72\uFF09\u3002");
147312
147397
  }
147313
147398
  const annotation = await kernel.annotate({
147314
- artifactId: str(args, "artifactId"),
147399
+ artifactId: targetId,
147315
147400
  actor,
147316
147401
  body: str(args, "body"),
147317
147402
  ...hand !== void 0 ? { hand } : {},
@@ -148106,6 +148191,8 @@ async function startOasisServer(opts) {
148106
148191
  }
148107
148192
  const result = await runCommand(engine.kernel, engine.blobs, engine.oplog, actor, body.command, body.args ?? {}, {
148108
148193
  ...tokenClaims?.dispatchSeq !== void 0 ? { dispatchSeq: tokenClaims.dispatchSeq } : {},
148194
+ // annotate 自动补 raisedFrom 的依据:本会话在做哪个节点(同源同解析器,见 ctx.sessionArtifactId)
148195
+ ...tokenClaims?.artifactId !== void 0 ? { sessionArtifactId: tokenClaims.artifactId } : {},
148109
148196
  ...actingForHuman !== void 0 ? { actingForHuman } : {},
148110
148197
  ...chatOrigin !== void 0 ? { chatOrigin } : {},
148111
148198
  ...opts.projectState ? { projectState: opts.projectState } : {},
@@ -149711,10 +149798,10 @@ function createTokenIssuer(opts = {}) {
149711
149798
  if (revoked.has(token)) return null;
149712
149799
  if (!token.startsWith(SESSION_TOKEN_PREFIX)) return null;
149713
149800
  const encoded = token.slice(SESSION_TOKEN_PREFIX.length);
149714
- const sep3 = encoded.indexOf(".");
149715
- if (sep3 <= 0) return null;
149716
- const payload = encoded.slice(0, sep3);
149717
- const actualSig = encoded.slice(sep3 + 1);
149801
+ const sep4 = encoded.indexOf(".");
149802
+ if (sep4 <= 0) return null;
149803
+ const payload = encoded.slice(0, sep4);
149804
+ const actualSig = encoded.slice(sep4 + 1);
149718
149805
  const expectedSig = sign(secret, payload);
149719
149806
  if (!signatureMatches(actualSig, expectedSig)) return null;
149720
149807
  let claims;
@@ -165772,6 +165859,9 @@ function builtinRubricsForCase(evaluationCase) {
165772
165859
  const base = {
165773
165860
  companyId: "",
165774
165861
  version: 0,
165862
+ // 回落标准不是登记表里的记录,标成 shared 只是为了满足类型;它们的 rubricId 是 builtin:*,
165863
+ // 不会被写进挂载,也不会出现在「评分标准」页。
165864
+ scope: "shared",
165775
165865
  status: "active",
165776
165866
  createdBy: "system",
165777
165867
  createdAt: at,
@@ -165923,11 +166013,36 @@ var init_scoring = __esm({
165923
166013
  at() {
165924
166014
  return (this.options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
165925
166015
  }
166016
+ /** 「评分标准」登记表——只列共享标准。Case 级的走 getCaseScoring。 */
165926
166017
  listRubrics(companyId) {
165927
- return this.options.store.listRubrics(companyId);
166018
+ return this.options.store.listRubrics(companyId, "shared");
165928
166019
  }
165929
- async upsertRubric(body, companyId, actor) {
165930
- const raw = body ?? {};
166020
+ listRubricVersions(rubricId) {
166021
+ return this.options.store.listRubricVersions(rubricId);
166022
+ }
166023
+ async getRubricVersion(rubricId, version2) {
166024
+ const found = await this.options.store.getRubricVersion(rubricId, version2);
166025
+ if (!found) {
166026
+ throw new ScoringConfigError(404, "RUBRIC_VERSION_NOT_FOUND", `\u8BC4\u5206\u6807\u51C6 ${rubricId}@${version2} \u4E0D\u5B58\u5728`);
166027
+ }
166028
+ return found;
166029
+ }
166030
+ /** 保存共享标准(登记表)。每次保存版本号 +1 并落一条不可变快照。 */
166031
+ upsertRubric(body, companyId, actor) {
166032
+ return this.save({ ...body, scope: "shared" }, companyId, actor);
166033
+ }
166034
+ /** 保存某个 Case 自己的标准。同一个 Case 只有一条,重复保存就是出新版本。 */
166035
+ async saveCaseRubric(caseId, body, companyId, actor) {
166036
+ const existing = await this.options.store.getCaseRubric(companyId, caseId);
166037
+ return this.save({
166038
+ ...body,
166039
+ scope: "case",
166040
+ ownerCaseId: caseId,
166041
+ // 认已有那条的 id:Case 级标准一对一,不带 id 再存一次会变成两条抢同一个 Case。
166042
+ ...existing ? { rubricId: existing.rubricId } : {}
166043
+ }, companyId, actor);
166044
+ }
166045
+ async save(raw, companyId, actor) {
165931
166046
  const rubricId = typeof raw["rubricId"] === "string" && raw["rubricId"] ? raw["rubricId"] : void 0;
165932
166047
  let existing = null;
165933
166048
  if (rubricId) {
@@ -165935,6 +166050,13 @@ var init_scoring = __esm({
165935
166050
  if (existing && existing.companyId !== companyId) {
165936
166051
  throw new ScoringConfigError(404, "RUBRIC_NOT_FOUND", `\u8BC4\u5206\u6807\u51C6 ${rubricId} \u4E0D\u5B58\u5728`);
165937
166052
  }
166053
+ if (existing && raw["scope"] && existing.scope !== raw["scope"]) {
166054
+ throw new ScoringConfigError(
166055
+ 409,
166056
+ "RUBRIC_SCOPE_IMMUTABLE",
166057
+ `\u8BC4\u5206\u6807\u51C6 ${rubricId} \u5DF2\u662F ${existing.scope === "shared" ? "\u5171\u4EAB" : "Case \u7EA7"}\u6807\u51C6\uFF0C\u4E0D\u80FD\u6539\u4F5C\u7528\u57DF`
166058
+ );
166059
+ }
165938
166060
  }
165939
166061
  const merged = existing ? {
165940
166062
  rubricId,
@@ -165943,7 +166065,9 @@ var init_scoring = __esm({
165943
166065
  description: raw["description"] ?? existing.description,
165944
166066
  promptSection: raw["promptSection"] ?? existing.promptSection,
165945
166067
  outputSpec: raw["outputSpec"] ?? existing.outputSpec,
165946
- status: raw["status"] ?? existing.status
166068
+ status: raw["status"] ?? existing.status,
166069
+ scope: existing.scope,
166070
+ ...existing.ownerCaseId ? { ownerCaseId: existing.ownerCaseId } : {}
165947
166071
  } : raw;
165948
166072
  let input;
165949
166073
  try {
@@ -165963,6 +166087,7 @@ var init_scoring = __esm({
165963
166087
  listBindings(companyId) {
165964
166088
  return this.options.store.listBindings(companyId);
165965
166089
  }
166090
+ /** 改 Case 挂的共享标准 / 判分员工。Case 自己那条标准不走这里(走 saveCaseRubric)。 */
165966
166091
  async putBinding(caseId, body, companyId, actor) {
165967
166092
  const input = body ?? {};
165968
166093
  const rubricIds = Array.isArray(input.rubricIds) ? input.rubricIds.filter((id) => typeof id === "string" && id.length > 0) : [];
@@ -165975,19 +166100,20 @@ var init_scoring = __esm({
165975
166100
  if (!rubric || rubric.companyId !== companyId) {
165976
166101
  throw new ScoringConfigError(400, "RUBRIC_NOT_FOUND", `\u8BC4\u5206\u6807\u51C6 ${rubricId} \u4E0D\u5B58\u5728`);
165977
166102
  }
166103
+ if (rubric.scope !== "shared") {
166104
+ throw new ScoringConfigError(
166105
+ 400,
166106
+ "RUBRIC_NOT_SHARED",
166107
+ `\u300C${rubric.name}\u300D\u662F Case \u7EA7\u6807\u51C6\uFF0C\u53EA\u80FD\u5728\u5B83\u81EA\u5DF1\u7684 Case \u91CC\u7F16\u8F91\uFF0C\u4E0D\u80FD\u6302\u8F7D`
166108
+ );
166109
+ }
165978
166110
  if (rubric.status !== "active") {
165979
166111
  throw new ScoringConfigError(400, "RUBRIC_ARCHIVED", `\u8BC4\u5206\u6807\u51C6\u300C${rubric.name}\u300D\u5DF2\u5F52\u6863\uFF0C\u4E0D\u80FD\u6302\u8F7D`);
165980
166112
  }
165981
166113
  rubrics.push(rubric);
165982
166114
  }
165983
- const sections = rubrics.map((rubric) => rubric.outputSpec.sectionKey);
165984
- if (new Set(sections).size !== sections.length) {
165985
- throw new ScoringConfigError(
165986
- 400,
165987
- "RUBRIC_SECTION_CONFLICT",
165988
- `\u6302\u8F7D\u7684\u6807\u51C6\u8F93\u51FA\u5230\u4E86\u540C\u4E00\u4E2A\u7ED3\u679C\u6BB5\uFF08${sections.join("\u3001")}\uFF09\uFF0C\u8BF7\u6539\u5176\u4E2D\u4E00\u6761\u7684 sectionKey`
165989
- );
165990
- }
166115
+ const caseRubric = await this.options.store.getCaseRubric(companyId, caseId);
166116
+ this.assertNoSectionConflict(rubrics, caseRubric);
165991
166117
  const judgeActorId = typeof input.judgeActorId === "string" && input.judgeActorId.trim() ? input.judgeActorId.trim() : null;
165992
166118
  if (judgeActorId && this.options.verifyJudgeActor && !await this.options.verifyJudgeActor(judgeActorId, companyId)) {
165993
166119
  throw new ScoringConfigError(400, "JUDGE_ACTOR_INVALID", `${judgeActorId} \u4E0D\u662F\u53EF\u7528\u7684\u5224\u5206\u5458\u5DE5`);
@@ -165998,26 +166124,46 @@ var init_scoring = __esm({
165998
166124
  );
165999
166125
  return {
166000
166126
  caseId,
166001
- rubrics,
166127
+ sharedRubrics: rubrics,
166128
+ caseRubric,
166002
166129
  judgeActorId: binding.judgeActorId,
166003
- usesBuiltinFallback: rubrics.length === 0
166130
+ usesBuiltinFallback: rubrics.length === 0 && !caseRubric
166004
166131
  };
166005
166132
  }
166006
- /** Case 当前装配的对外投影(挂载展开 + 是否还在吃回落)。 */
166133
+ /** 两条标准写进同一个结果段 = 后写的覆盖前一条,判分结果里会静默少一套标准。挡在入口。 */
166134
+ assertNoSectionConflict(shared, caseRubric) {
166135
+ const all = caseRubric ? [...shared, caseRubric] : shared;
166136
+ const sections = all.map((rubric) => rubric.outputSpec.sectionKey);
166137
+ if (new Set(sections).size !== sections.length) {
166138
+ throw new ScoringConfigError(
166139
+ 400,
166140
+ "RUBRIC_SECTION_CONFLICT",
166141
+ `\u751F\u6548\u7684\u6807\u51C6\u5199\u5230\u4E86\u540C\u4E00\u4E2A\u7ED3\u679C\u6BB5\uFF08${sections.join("\u3001")}\uFF09\uFF0C\u8BF7\u6539\u5176\u4E2D\u4E00\u6761\u7684 sectionKey`
166142
+ );
166143
+ }
166144
+ }
166145
+ /** Case 当前装配的对外投影(共享挂载 + 自己的标准 + 是否还在吃回落)。 */
166007
166146
  async getCaseScoring(caseId, companyId) {
166008
166147
  const binding = await this.options.store.getBinding(companyId, caseId);
166009
- const rubrics = await this.mountedRubrics(binding, companyId);
166148
+ const [sharedRubrics, caseRubric] = await Promise.all([
166149
+ this.mountedRubrics(binding, companyId),
166150
+ this.options.store.getCaseRubric(companyId, caseId)
166151
+ ]);
166010
166152
  return {
166011
166153
  caseId,
166012
- rubrics,
166154
+ sharedRubrics,
166155
+ caseRubric,
166013
166156
  judgeActorId: binding?.judgeActorId ?? null,
166014
- usesBuiltinFallback: rubrics.length === 0
166157
+ usesBuiltinFallback: sharedRubrics.length === 0 && !caseRubric
166015
166158
  };
166016
166159
  }
166017
166160
  /** 判分前的最终解析:拿到本次真正要用的标准与判分员工。 */
166018
166161
  async resolveForRun(evaluationCase, companyId, requestedJudgeActorId) {
166019
166162
  const binding = await this.options.store.getBinding(companyId, evaluationCase.id);
166020
- const mounted = await this.mountedRubrics(binding, companyId);
166163
+ const [mounted, caseRubric] = await Promise.all([
166164
+ this.mountedRubrics(binding, companyId),
166165
+ this.options.store.getCaseRubric(companyId, evaluationCase.id)
166166
+ ]);
166021
166167
  const judgeActorId = requestedJudgeActorId?.trim() || binding?.judgeActorId || this.options.defaultJudgeActorId;
166022
166168
  if (!judgeActorId) {
166023
166169
  throw new ScoringConfigError(
@@ -166026,10 +166172,20 @@ var init_scoring = __esm({
166026
166172
  "\u6CA1\u6709\u53EF\u7528\u7684\u5224\u5206\u5458\u5DE5\uFF1A\u8BF7\u5728 Case \u8BC4\u5206\u88C5\u914D\u91CC\u6307\u5B9A\uFF0C\u6216\u914D\u7F6E OASIS_EVALUATION_SCORING_ACTOR_ID"
166027
166173
  );
166028
166174
  }
166175
+ const configured = caseRubric ? [...mounted, caseRubric] : mounted;
166176
+ if (configured.length) {
166177
+ const covered = new Set(configured.map((rubric) => rubric.outputSpec.sectionKey));
166178
+ const filled = builtinRubricsForCase(evaluationCase).filter((rubric) => !covered.has(rubric.outputSpec.sectionKey));
166179
+ return {
166180
+ rubrics: [...configured, ...filled],
166181
+ judgeActorId,
166182
+ usesBuiltinFallback: false
166183
+ };
166184
+ }
166029
166185
  return {
166030
- rubrics: mounted.length ? mounted : builtinRubricsForCase(evaluationCase),
166186
+ rubrics: builtinRubricsForCase(evaluationCase),
166031
166187
  judgeActorId,
166032
- usesBuiltinFallback: mounted.length === 0
166188
+ usesBuiltinFallback: true
166033
166189
  };
166034
166190
  }
166035
166191
  async mountedRubrics(binding, companyId) {
@@ -166310,6 +166466,8 @@ var init_service7 = __esm({
166310
166466
  judgeActorId: resolved.judgeActorId,
166311
166467
  judgeVersion: this.options.judgeVersion,
166312
166468
  rubricVersion: rubricVersionOf(resolved.rubrics, evaluationCase.rubricVersion),
166469
+ // 结构化引用:Case 的标准会变,这里钉死这一次用的是哪几条、各是第几版。
166470
+ rubricRefs: rubricRefsOf(resolved.rubrics),
166313
166471
  evaluatorVersion: evaluationCase.evaluatorVersion,
166314
166472
  createdBy: actor,
166315
166473
  createdAt: now
@@ -166414,6 +166572,20 @@ function evaluationDomain(options) {
166414
166572
  return mapError(error2);
166415
166573
  }
166416
166574
  });
166575
+ router.get("/api/evaluations/rubrics/:rubricId/versions", async (req) => ({
166576
+ status: 200,
166577
+ body: { items: await scoring.listRubricVersions(req.params.rubricId) }
166578
+ }));
166579
+ router.get("/api/evaluations/rubrics/:rubricId/versions/:version", async (req) => {
166580
+ try {
166581
+ return {
166582
+ status: 200,
166583
+ body: await scoring.getRubricVersion(req.params.rubricId, Number(req.params.version))
166584
+ };
166585
+ } catch (error2) {
166586
+ return mapError(error2);
166587
+ }
166588
+ });
166417
166589
  router.get("/api/evaluations/case-scoring", async (req) => ({
166418
166590
  status: 200,
166419
166591
  body: { items: await scoring.listBindings(req.auth.companyId ?? "default") }
@@ -166437,6 +166609,21 @@ function evaluationDomain(options) {
166437
166609
  return mapError(error2);
166438
166610
  }
166439
166611
  });
166612
+ router.put("/api/evaluations/cases/:caseId/rubric", async (req) => {
166613
+ try {
166614
+ return {
166615
+ status: 200,
166616
+ body: await scoring.saveCaseRubric(
166617
+ req.params.caseId,
166618
+ req.body,
166619
+ req.auth.companyId ?? "default",
166620
+ req.auth.actor
166621
+ )
166622
+ };
166623
+ } catch (error2) {
166624
+ return mapError(error2);
166625
+ }
166626
+ });
166440
166627
  router.get("/api/evaluations/judge-candidates", async (req) => ({
166441
166628
  status: 200,
166442
166629
  body: {
@@ -170517,10 +170704,10 @@ var require_resolve_block_map = __commonJS({
170517
170704
  let offset = bm.offset;
170518
170705
  let commentEnd = null;
170519
170706
  for (const collItem of bm.items) {
170520
- const { start, key, sep: sep3, value: value2 } = collItem;
170707
+ const { start, key, sep: sep4, value: value2 } = collItem;
170521
170708
  const keyProps = resolveProps.resolveProps(start, {
170522
170709
  indicator: "explicit-key-ind",
170523
- next: key ?? sep3?.[0],
170710
+ next: key ?? sep4?.[0],
170524
170711
  offset,
170525
170712
  onError,
170526
170713
  parentIndent: bm.indent,
@@ -170534,7 +170721,7 @@ var require_resolve_block_map = __commonJS({
170534
170721
  else if ("indent" in key && key.indent !== bm.indent)
170535
170722
  onError(offset, "BAD_INDENT", startColMsg);
170536
170723
  }
170537
- if (!keyProps.anchor && !keyProps.tag && !sep3) {
170724
+ if (!keyProps.anchor && !keyProps.tag && !sep4) {
170538
170725
  commentEnd = keyProps.end;
170539
170726
  if (keyProps.comment) {
170540
170727
  if (map.comment)
@@ -170558,7 +170745,7 @@ var require_resolve_block_map = __commonJS({
170558
170745
  ctx.atKey = false;
170559
170746
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
170560
170747
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
170561
- const valueProps = resolveProps.resolveProps(sep3 ?? [], {
170748
+ const valueProps = resolveProps.resolveProps(sep4 ?? [], {
170562
170749
  indicator: "map-value-ind",
170563
170750
  next: value2,
170564
170751
  offset: keyNode.range[2],
@@ -170574,7 +170761,7 @@ var require_resolve_block_map = __commonJS({
170574
170761
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
170575
170762
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
170576
170763
  }
170577
- const valueNode = value2 ? composeNode(ctx, value2, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
170764
+ const valueNode = value2 ? composeNode(ctx, value2, valueProps, onError) : composeEmptyNode(ctx, offset, sep4, null, valueProps, onError);
170578
170765
  if (ctx.schema.compat)
170579
170766
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value2, onError);
170580
170767
  offset = valueNode.range[2];
@@ -170665,7 +170852,7 @@ var require_resolve_end = __commonJS({
170665
170852
  let comment = "";
170666
170853
  if (end) {
170667
170854
  let hasSpace = false;
170668
- let sep3 = "";
170855
+ let sep4 = "";
170669
170856
  for (const token of end) {
170670
170857
  const { source, type } = token;
170671
170858
  switch (type) {
@@ -170679,13 +170866,13 @@ var require_resolve_end = __commonJS({
170679
170866
  if (!comment)
170680
170867
  comment = cb;
170681
170868
  else
170682
- comment += sep3 + cb;
170683
- sep3 = "";
170869
+ comment += sep4 + cb;
170870
+ sep4 = "";
170684
170871
  break;
170685
170872
  }
170686
170873
  case "newline":
170687
170874
  if (comment)
170688
- sep3 += source;
170875
+ sep4 += source;
170689
170876
  hasSpace = true;
170690
170877
  break;
170691
170878
  default:
@@ -170728,18 +170915,18 @@ var require_resolve_flow_collection = __commonJS({
170728
170915
  let offset = fc.offset + fc.start.source.length;
170729
170916
  for (let i = 0; i < fc.items.length; ++i) {
170730
170917
  const collItem = fc.items[i];
170731
- const { start, key, sep: sep3, value: value2 } = collItem;
170918
+ const { start, key, sep: sep4, value: value2 } = collItem;
170732
170919
  const props = resolveProps.resolveProps(start, {
170733
170920
  flow: fcName,
170734
170921
  indicator: "explicit-key-ind",
170735
- next: key ?? sep3?.[0],
170922
+ next: key ?? sep4?.[0],
170736
170923
  offset,
170737
170924
  onError,
170738
170925
  parentIndent: fc.indent,
170739
170926
  startOnNewline: false
170740
170927
  });
170741
170928
  if (!props.found) {
170742
- if (!props.anchor && !props.tag && !sep3 && !value2) {
170929
+ if (!props.anchor && !props.tag && !sep4 && !value2) {
170743
170930
  if (i === 0 && props.comma)
170744
170931
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
170745
170932
  else if (i < fc.items.length - 1)
@@ -170793,8 +170980,8 @@ var require_resolve_flow_collection = __commonJS({
170793
170980
  }
170794
170981
  }
170795
170982
  }
170796
- if (!isMap && !sep3 && !props.found) {
170797
- const valueNode = value2 ? composeNode(ctx, value2, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
170983
+ if (!isMap && !sep4 && !props.found) {
170984
+ const valueNode = value2 ? composeNode(ctx, value2, props, onError) : composeEmptyNode(ctx, props.end, sep4, null, props, onError);
170798
170985
  coll.items.push(valueNode);
170799
170986
  offset = valueNode.range[2];
170800
170987
  if (isBlock(value2))
@@ -170806,7 +170993,7 @@ var require_resolve_flow_collection = __commonJS({
170806
170993
  if (isBlock(key))
170807
170994
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
170808
170995
  ctx.atKey = false;
170809
- const valueProps = resolveProps.resolveProps(sep3 ?? [], {
170996
+ const valueProps = resolveProps.resolveProps(sep4 ?? [], {
170810
170997
  flow: fcName,
170811
170998
  indicator: "map-value-ind",
170812
170999
  next: value2,
@@ -170817,8 +171004,8 @@ var require_resolve_flow_collection = __commonJS({
170817
171004
  });
170818
171005
  if (valueProps.found) {
170819
171006
  if (!isMap && !props.found && ctx.options.strict) {
170820
- if (sep3)
170821
- for (const st of sep3) {
171007
+ if (sep4)
171008
+ for (const st of sep4) {
170822
171009
  if (st === valueProps.found)
170823
171010
  break;
170824
171011
  if (st.type === "newline") {
@@ -170835,7 +171022,7 @@ var require_resolve_flow_collection = __commonJS({
170835
171022
  else
170836
171023
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
170837
171024
  }
170838
- const valueNode = value2 ? composeNode(ctx, value2, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
171025
+ const valueNode = value2 ? composeNode(ctx, value2, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep4, null, valueProps, onError) : null;
170839
171026
  if (valueNode) {
170840
171027
  if (isBlock(value2))
170841
171028
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -171015,7 +171202,7 @@ var require_resolve_block_scalar = __commonJS({
171015
171202
  chompStart = i + 1;
171016
171203
  }
171017
171204
  let value2 = "";
171018
- let sep3 = "";
171205
+ let sep4 = "";
171019
171206
  let prevMoreIndented = false;
171020
171207
  for (let i = 0; i < contentStart; ++i)
171021
171208
  value2 += lines[i][0].slice(trimIndent) + "\n";
@@ -171032,24 +171219,24 @@ var require_resolve_block_scalar = __commonJS({
171032
171219
  indent = "";
171033
171220
  }
171034
171221
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
171035
- value2 += sep3 + indent.slice(trimIndent) + content;
171036
- sep3 = "\n";
171222
+ value2 += sep4 + indent.slice(trimIndent) + content;
171223
+ sep4 = "\n";
171037
171224
  } else if (indent.length > trimIndent || content[0] === " ") {
171038
- if (sep3 === " ")
171039
- sep3 = "\n";
171040
- else if (!prevMoreIndented && sep3 === "\n")
171041
- sep3 = "\n\n";
171042
- value2 += sep3 + indent.slice(trimIndent) + content;
171043
- sep3 = "\n";
171225
+ if (sep4 === " ")
171226
+ sep4 = "\n";
171227
+ else if (!prevMoreIndented && sep4 === "\n")
171228
+ sep4 = "\n\n";
171229
+ value2 += sep4 + indent.slice(trimIndent) + content;
171230
+ sep4 = "\n";
171044
171231
  prevMoreIndented = true;
171045
171232
  } else if (content === "") {
171046
- if (sep3 === "\n")
171233
+ if (sep4 === "\n")
171047
171234
  value2 += "\n";
171048
171235
  else
171049
- sep3 = "\n";
171236
+ sep4 = "\n";
171050
171237
  } else {
171051
- value2 += sep3 + content;
171052
- sep3 = " ";
171238
+ value2 += sep4 + content;
171239
+ sep4 = " ";
171053
171240
  prevMoreIndented = false;
171054
171241
  }
171055
171242
  }
@@ -171231,25 +171418,25 @@ var require_resolve_flow_scalar = __commonJS({
171231
171418
  if (!match)
171232
171419
  return source;
171233
171420
  let res = match[1];
171234
- let sep3 = " ";
171421
+ let sep4 = " ";
171235
171422
  let pos = first.lastIndex;
171236
171423
  line.lastIndex = pos;
171237
171424
  while (match = line.exec(source)) {
171238
171425
  if (match[1] === "") {
171239
- if (sep3 === "\n")
171240
- res += sep3;
171426
+ if (sep4 === "\n")
171427
+ res += sep4;
171241
171428
  else
171242
- sep3 = "\n";
171429
+ sep4 = "\n";
171243
171430
  } else {
171244
- res += sep3 + match[1];
171245
- sep3 = " ";
171431
+ res += sep4 + match[1];
171432
+ sep4 = " ";
171246
171433
  }
171247
171434
  pos = line.lastIndex;
171248
171435
  }
171249
171436
  const last = /[ \t]*(.*)/sy;
171250
171437
  last.lastIndex = pos;
171251
171438
  match = last.exec(source);
171252
- return res + sep3 + (match?.[1] ?? "");
171439
+ return res + sep4 + (match?.[1] ?? "");
171253
171440
  }
171254
171441
  function doubleQuotedValue(source, onError) {
171255
171442
  let res = "";
@@ -172059,14 +172246,14 @@ var require_cst_stringify = __commonJS({
172059
172246
  }
172060
172247
  }
172061
172248
  }
172062
- function stringifyItem({ start, key, sep: sep3, value: value2 }) {
172249
+ function stringifyItem({ start, key, sep: sep4, value: value2 }) {
172063
172250
  let res = "";
172064
172251
  for (const st of start)
172065
172252
  res += st.source;
172066
172253
  if (key)
172067
172254
  res += stringifyToken(key);
172068
- if (sep3)
172069
- for (const st of sep3)
172255
+ if (sep4)
172256
+ for (const st of sep4)
172070
172257
  res += st.source;
172071
172258
  if (value2)
172072
172259
  res += stringifyToken(value2);
@@ -173233,18 +173420,18 @@ var require_parser = __commonJS({
173233
173420
  if (this.type === "map-value-ind") {
173234
173421
  const prev = getPrevProps(this.peek(2));
173235
173422
  const start = getFirstKeyStartProps(prev);
173236
- let sep3;
173423
+ let sep4;
173237
173424
  if (scalar.end) {
173238
- sep3 = scalar.end;
173239
- sep3.push(this.sourceToken);
173425
+ sep4 = scalar.end;
173426
+ sep4.push(this.sourceToken);
173240
173427
  delete scalar.end;
173241
173428
  } else
173242
- sep3 = [this.sourceToken];
173429
+ sep4 = [this.sourceToken];
173243
173430
  const map = {
173244
173431
  type: "block-map",
173245
173432
  offset: scalar.offset,
173246
173433
  indent: scalar.indent,
173247
- items: [{ start, key: scalar, sep: sep3 }]
173434
+ items: [{ start, key: scalar, sep: sep4 }]
173248
173435
  };
173249
173436
  this.onKeyLine = true;
173250
173437
  this.stack[this.stack.length - 1] = map;
@@ -173397,15 +173584,15 @@ var require_parser = __commonJS({
173397
173584
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
173398
173585
  const start2 = getFirstKeyStartProps(it.start);
173399
173586
  const key = it.key;
173400
- const sep3 = it.sep;
173401
- sep3.push(this.sourceToken);
173587
+ const sep4 = it.sep;
173588
+ sep4.push(this.sourceToken);
173402
173589
  delete it.key;
173403
173590
  delete it.sep;
173404
173591
  this.stack.push({
173405
173592
  type: "block-map",
173406
173593
  offset: this.offset,
173407
173594
  indent: this.indent,
173408
- items: [{ start: start2, key, sep: sep3 }]
173595
+ items: [{ start: start2, key, sep: sep4 }]
173409
173596
  });
173410
173597
  } else if (start.length > 0) {
173411
173598
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -173599,13 +173786,13 @@ var require_parser = __commonJS({
173599
173786
  const prev = getPrevProps(parent);
173600
173787
  const start = getFirstKeyStartProps(prev);
173601
173788
  fixFlowSeqItems(fc);
173602
- const sep3 = fc.end.splice(1, fc.end.length);
173603
- sep3.push(this.sourceToken);
173789
+ const sep4 = fc.end.splice(1, fc.end.length);
173790
+ sep4.push(this.sourceToken);
173604
173791
  const map = {
173605
173792
  type: "block-map",
173606
173793
  offset: fc.offset,
173607
173794
  indent: fc.indent,
173608
- items: [{ start, key: fc, sep: sep3 }]
173795
+ items: [{ start, key: fc, sep: sep4 }]
173609
173796
  };
173610
173797
  this.onKeyLine = true;
173611
173798
  this.stack[this.stack.length - 1] = map;
@@ -187196,6 +187383,20 @@ function benchmarkSchemaSql(schema) {
187196
187383
  created_by text NOT NULL, created_at timestamptz NOT NULL,
187197
187384
  updated_by text NOT NULL, updated_at timestamptz NOT NULL
187198
187385
  );
187386
+ ALTER TABLE ${s2}.benchmark_rubrics ADD COLUMN IF NOT EXISTS scope text NOT NULL DEFAULT 'shared';
187387
+ ALTER TABLE ${s2}.benchmark_rubrics ADD COLUMN IF NOT EXISTS owner_case_id text;
187388
+ ALTER TABLE ${s2}.benchmark_evaluations ADD COLUMN IF NOT EXISTS rubric_refs jsonb;
187389
+ -- \u7248\u672C\u5FEB\u7167\u53EA\u589E\u4E0D\u6539\uFF1A\u5386\u53F2 Evaluation \u9760 (rubric_id, version) \u56DE\u67E5\u5F53\u65F6\u90A3\u4EFD\u6B63\u6587\u3002
187390
+ CREATE TABLE IF NOT EXISTS ${s2}.benchmark_rubric_versions (
187391
+ rubric_id text NOT NULL, version integer NOT NULL, name text NOT NULL, kind text NOT NULL,
187392
+ scope text NOT NULL, owner_case_id text, description text,
187393
+ prompt_section text NOT NULL, output_spec jsonb NOT NULL,
187394
+ created_by text NOT NULL, created_at timestamptz NOT NULL,
187395
+ PRIMARY KEY (rubric_id, version)
187396
+ );
187397
+ -- \u4E00\u4E2A Case \u53EA\u80FD\u6709\u4E00\u6761\u81EA\u5DF1\u7684\u6807\u51C6\uFF1B\u9760\u552F\u4E00\u7D22\u5F15\u515C\u4F4F\u5E76\u53D1\u91CD\u590D\u5199\u3002
187398
+ CREATE UNIQUE INDEX IF NOT EXISTS benchmark_rubrics_owner_case_idx
187399
+ ON ${s2}.benchmark_rubrics (company_id, owner_case_id) WHERE owner_case_id IS NOT NULL;
187199
187400
  CREATE TABLE IF NOT EXISTS ${s2}.benchmark_case_scoring (
187200
187401
  company_id text NOT NULL, case_id text NOT NULL, rubric_ids jsonb NOT NULL,
187201
187402
  judge_actor_id text, updated_by text NOT NULL, updated_at timestamptz NOT NULL,
@@ -187236,6 +187437,7 @@ function toEvaluation(row) {
187236
187437
  judgeRunId: nullableText(row.judge_run_id),
187237
187438
  judgeVersion: String(row.judge_version),
187238
187439
  rubricVersion: String(row.rubric_version),
187440
+ rubricRefs: row.rubric_refs == null ? null : structuredClone(row.rubric_refs),
187239
187441
  evaluatorVersion: String(row.evaluator_version),
187240
187442
  result: row.result == null ? null : structuredClone(row.result),
187241
187443
  errorCode: nullableText(row.error_code),
@@ -187253,6 +187455,8 @@ function toRubric(row) {
187253
187455
  name: String(row.name),
187254
187456
  kind: String(row.kind),
187255
187457
  ...row.description == null ? {} : { description: String(row.description) },
187458
+ scope: row.scope == null ? "shared" : String(row.scope),
187459
+ ...row.owner_case_id == null ? {} : { ownerCaseId: String(row.owner_case_id) },
187256
187460
  promptSection: String(row.prompt_section),
187257
187461
  outputSpec: structuredClone(row.output_spec),
187258
187462
  version: Number(row.version),
@@ -187273,6 +187477,21 @@ function toBinding(row) {
187273
187477
  updatedAt: iso2(row.updated_at)
187274
187478
  };
187275
187479
  }
187480
+ function toRubricVersion(row) {
187481
+ return {
187482
+ rubricId: String(row.rubric_id),
187483
+ version: Number(row.version),
187484
+ name: String(row.name),
187485
+ kind: String(row.kind),
187486
+ scope: String(row.scope),
187487
+ ...row.owner_case_id == null ? {} : { ownerCaseId: String(row.owner_case_id) },
187488
+ ...row.description == null ? {} : { description: String(row.description) },
187489
+ promptSection: String(row.prompt_section),
187490
+ outputSpec: structuredClone(row.output_spec),
187491
+ createdBy: String(row.created_by),
187492
+ createdAt: iso2(row.created_at)
187493
+ };
187494
+ }
187276
187495
  var import_node_crypto38, ident17, iso2, nullableIso, nullableText, PostgresEvaluationStore, PostgresEvaluationRubricStore;
187277
187496
  var init_postgres_evaluation = __esm({
187278
187497
  "../storage/src/postgres-evaluation.ts"() {
@@ -187398,8 +187617,8 @@ var init_postgres_evaluation = __esm({
187398
187617
  `INSERT INTO ${this.s}.benchmark_evaluations
187399
187618
  (evaluation_id,run_id,evaluation_no,status,judge_actor_id,judge_run_id,judge_version,
187400
187619
  rubric_version,evaluator_version,result,error_code,error_message,created_by,created_at,
187401
- started_at,completed_at)
187402
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb,$11,$12,$13,$14,$15,$16) RETURNING *`,
187620
+ started_at,completed_at,rubric_refs)
187621
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb,$11,$12,$13,$14,$15,$16,$17::jsonb) RETURNING *`,
187403
187622
  [
187404
187623
  input.evaluationId,
187405
187624
  input.runId,
@@ -187416,7 +187635,8 @@ var init_postgres_evaluation = __esm({
187416
187635
  input.createdBy,
187417
187636
  input.createdAt,
187418
187637
  input.startedAt ?? null,
187419
- input.completedAt ?? null
187638
+ input.completedAt ?? null,
187639
+ input.rubricRefs ? JSON.stringify(input.rubricRefs) : null
187420
187640
  ]
187421
187641
  );
187422
187642
  await client.query("COMMIT");
@@ -187446,7 +187666,7 @@ var init_postgres_evaluation = __esm({
187446
187666
  const result = await this.pool.query(
187447
187667
  `UPDATE ${this.s}.benchmark_evaluations SET status=$2,judge_run_id=$3,judge_version=$4,
187448
187668
  rubric_version=$5,evaluator_version=$6,result=$7::jsonb,error_code=$8,error_message=$9,
187449
- started_at=$10,completed_at=$11 WHERE evaluation_id=$1`,
187669
+ started_at=$10,completed_at=$11,rubric_refs=$12::jsonb WHERE evaluation_id=$1`,
187450
187670
  [
187451
187671
  evaluation.evaluationId,
187452
187672
  evaluation.status,
@@ -187458,7 +187678,8 @@ var init_postgres_evaluation = __esm({
187458
187678
  evaluation.errorCode ?? null,
187459
187679
  evaluation.errorMessage ?? null,
187460
187680
  evaluation.startedAt ?? null,
187461
- evaluation.completedAt ?? null
187681
+ evaluation.completedAt ?? null,
187682
+ evaluation.rubricRefs ? JSON.stringify(evaluation.rubricRefs) : null
187462
187683
  ]
187463
187684
  );
187464
187685
  if (result.rowCount !== 1) throw new Error(`Evaluation ${evaluation.evaluationId} \u4E0D\u5B58\u5728`);
@@ -187476,13 +187697,35 @@ var init_postgres_evaluation = __esm({
187476
187697
  await pool.query(benchmarkSchemaSql(safe));
187477
187698
  return new _PostgresEvaluationRubricStore(pool, safe);
187478
187699
  }
187479
- async listRubrics(companyId) {
187700
+ async listRubrics(companyId, scope = "shared") {
187480
187701
  const result = await this.pool.query(
187481
- `SELECT * FROM ${this.s}.benchmark_rubrics WHERE company_id=$1 ORDER BY name ASC`,
187482
- [companyId]
187702
+ `SELECT * FROM ${this.s}.benchmark_rubrics WHERE company_id=$1 AND scope=$2 ORDER BY name ASC`,
187703
+ [companyId, scope]
187483
187704
  );
187484
187705
  return result.rows.map((row) => toRubric(row));
187485
187706
  }
187707
+ async getCaseRubric(companyId, caseId) {
187708
+ const result = await this.pool.query(
187709
+ `SELECT * FROM ${this.s}.benchmark_rubrics
187710
+ WHERE company_id=$1 AND scope='case' AND owner_case_id=$2`,
187711
+ [companyId, caseId]
187712
+ );
187713
+ return result.rows[0] ? toRubric(result.rows[0]) : null;
187714
+ }
187715
+ async listRubricVersions(rubricId) {
187716
+ const result = await this.pool.query(
187717
+ `SELECT * FROM ${this.s}.benchmark_rubric_versions WHERE rubric_id=$1 ORDER BY version DESC`,
187718
+ [rubricId]
187719
+ );
187720
+ return result.rows.map((row) => toRubricVersion(row));
187721
+ }
187722
+ async getRubricVersion(rubricId, version2) {
187723
+ const result = await this.pool.query(
187724
+ `SELECT * FROM ${this.s}.benchmark_rubric_versions WHERE rubric_id=$1 AND version=$2`,
187725
+ [rubricId, version2]
187726
+ );
187727
+ return result.rows[0] ? toRubricVersion(result.rows[0]) : null;
187728
+ }
187486
187729
  async getRubric(rubricId) {
187487
187730
  const result = await this.pool.query(
187488
187731
  `SELECT * FROM ${this.s}.benchmark_rubrics WHERE rubric_id=$1`,
@@ -187506,15 +187749,18 @@ var init_postgres_evaluation = __esm({
187506
187749
  const status = input.status ?? (existing ? String(existing.status) : "active");
187507
187750
  const createdBy = existing ? String(existing.created_by) : input.actor;
187508
187751
  const createdAt = existing ? iso2(existing.created_at) : at;
187752
+ const scope = input.scope ?? (existing ? String(existing.scope ?? "shared") : "shared");
187753
+ const ownerCaseId = scope === "case" ? input.ownerCaseId ?? (existing?.owner_case_id == null ? null : String(existing.owner_case_id)) : null;
187509
187754
  await client.query(
187510
187755
  `INSERT INTO ${this.s}.benchmark_rubrics
187511
187756
  (rubric_id,company_id,name,kind,description,prompt_section,output_spec,version,status,
187512
- created_by,created_at,updated_by,updated_at)
187513
- VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9,$10,$11,$12,$13)
187757
+ created_by,created_at,updated_by,updated_at,scope,owner_case_id)
187758
+ VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9,$10,$11,$12,$13,$14,$15)
187514
187759
  ON CONFLICT (rubric_id) DO UPDATE SET name=EXCLUDED.name,kind=EXCLUDED.kind,
187515
187760
  description=EXCLUDED.description,prompt_section=EXCLUDED.prompt_section,
187516
187761
  output_spec=EXCLUDED.output_spec,version=EXCLUDED.version,status=EXCLUDED.status,
187517
- updated_by=EXCLUDED.updated_by,updated_at=EXCLUDED.updated_at`,
187762
+ updated_by=EXCLUDED.updated_by,updated_at=EXCLUDED.updated_at,
187763
+ scope=EXCLUDED.scope,owner_case_id=EXCLUDED.owner_case_id`,
187518
187764
  [
187519
187765
  rubricId,
187520
187766
  input.companyId,
@@ -187528,6 +187774,28 @@ var init_postgres_evaluation = __esm({
187528
187774
  createdBy,
187529
187775
  createdAt,
187530
187776
  input.actor,
187777
+ at,
187778
+ scope,
187779
+ ownerCaseId
187780
+ ]
187781
+ );
187782
+ await client.query(
187783
+ `INSERT INTO ${this.s}.benchmark_rubric_versions
187784
+ (rubric_id,version,name,kind,scope,owner_case_id,description,prompt_section,output_spec,
187785
+ created_by,created_at)
187786
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10,$11)
187787
+ ON CONFLICT (rubric_id,version) DO NOTHING`,
187788
+ [
187789
+ rubricId,
187790
+ version2,
187791
+ input.name,
187792
+ input.kind,
187793
+ scope,
187794
+ ownerCaseId,
187795
+ input.description ?? null,
187796
+ input.promptSection,
187797
+ JSON.stringify(input.outputSpec),
187798
+ input.actor,
187531
187799
  at
187532
187800
  ]
187533
187801
  );
@@ -188036,8 +188304,8 @@ exec ${JSON.stringify(tsx)} ${JSON.stringify(main)} "$@"
188036
188304
  return wrapper;
188037
188305
  }
188038
188306
  function splitIdentityFiles3(prompt) {
188039
- const sep3 = /<!-- FILE: (.+?) -->\n?/g;
188040
- const parts = prompt.split(sep3);
188307
+ const sep4 = /<!-- FILE: (.+?) -->\n?/g;
188308
+ const parts = prompt.split(sep4);
188041
188309
  if (parts.length === 1) return { "identity/AGENTS.md": prompt };
188042
188310
  const out = {};
188043
188311
  for (let i = 1; i < parts.length; i += 2) {
@@ -188098,6 +188366,14 @@ async function buildChatWhoSection(service, rolesAdmin, humanActorId, selfActorI
188098
188366
  lines.push(`\u60F3\u8FDB\u4E00\u6B65\u4E86\u89E3 TA\u3001\u6216\u6838\u5B9E\u5BF9\u8BDD\u91CC\u63D0\u5230\u7684\u5176\u4ED6\u4EBA\u662F\u8C01\uFF1A\`oasis actor <actorId>\`\uFF08\u5355\u4EBA\u5B8C\u6574\u6863\u6848\uFF09\u3002`);
188099
188367
  return lines;
188100
188368
  }
188369
+ var CHAT_FINAL_REPLY_RULE = [
188370
+ "",
188371
+ "## \u6536\u5C3E\u987A\u5E8F\uFF08\u5C55\u793A\u9762\u5951\u7EA6\uFF09",
188372
+ "",
188373
+ "**\u4F60\u7684\u6700\u540E\u4E00\u6BB5\u6B63\u6587\u5C31\u662F\u7528\u6237\u770B\u5230\u7684\u56DE\u590D**\u2014\u2014\u5728\u5B83\u4E4B\u524D\u7684\u5185\u5BB9\uFF0C\u754C\u9762\u9ED8\u8BA4\u6298\u53E0\u6210\u300C\u601D\u8003\u8FC7\u7A0B\u300D\u3002",
188374
+ "\u6240\u4EE5\uFF1A\u6240\u6709\u6536\u5C3E\u526F\u4F5C\u7528\uFF08`oasis mem-write`\u3001\u6539\u56FE / \u5EFA\u5355\u3001\u53D1\u6D88\u606F\u3001\u5199\u6587\u4EF6\uFF09\u90FD\u5728\u7ED9\u51FA\u56DE\u590D**\u4E4B\u524D**\u505A\u5B8C\uFF1B",
188375
+ "\u505A\u5B8C\u4E4B\u540E\u522B\u518D\u8865\u300C\u5DF2\u4FDD\u5B58 / \u5DF2\u8BB0\u5F55 / \u597D\u7684\u300D\u8FD9\u7C7B\u786E\u8BA4\u53E5\u2014\u2014\u8981\u8BF4\u7684\u4E00\u5E76\u5199\u8FDB\u90A3\u6BB5\u56DE\u590D\u6B63\u6587\u91CC\u3002"
188376
+ ];
188101
188377
  function traceRuntimeKind(runtimeKind) {
188102
188378
  if (runtimeKind === "claude" || runtimeKind === "claude-code") return "claude-code";
188103
188379
  if (runtimeKind === "codex") return "codex";
@@ -189238,7 +189514,9 @@ async function startServe(opts) {
189238
189514
  "## \u7528\u6237\u6D88\u606F",
189239
189515
  "",
189240
189516
  message,
189241
- ...chatMemoryLines
189517
+ ...chatMemoryLines,
189518
+ // 压在最末:这条要盖住紧邻其上的「收工前想一下」,否则记忆写入就落到正文之后(见常量注释)。
189519
+ ...CHAT_FINAL_REPLY_RULE
189242
189520
  ].join("\n")
189243
189521
  };
189244
189522
  Object.assign(files, attachmentFiles);
@@ -191521,14 +191799,18 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
191521
191799
  evaluate <workOrderId> --case <caseId> [--judge <actorId>] [--case-version <v>] # \u5EFA Run \u5E76\u6392\u961F\u9996\u6B21\u8BC4\u5206
191522
191800
  rescore <runId> [--judge <actorId>] # \u53EA\u65B0\u589E\u4E00\u6B21\u8BC4\u5206\uFF0C\u4E0D\u52A8 Run \u5E8F\u53F7
191523
191801
  judges # \u53EF\u5F53\u5224\u5206\u5458\u5DE5\u7684 agent\uFF08\u53EA\u5217\u6709 active runtime \u7ED1\u5B9A\u7684\uFF09
191524
- rubrics [--kind common|specialty|case_special] # \u8BC4\u5206\u6807\u51C6\u6E05\u5355\uFF08\u4E09\u79CD\u6807\u51C6\u662F\u540C\u4E00\u79CD\u5B9E\u4F53\uFF09
191802
+ rubrics [--kind common|specialty] # **\u5171\u4EAB**\u8BC4\u5206\u6807\u51C6\u6E05\u5355\uFF08\u957F\u671F\u8D44\u4EA7\uFF0C\u53EF\u6302\u591A\u4E2A Case\uFF09
191525
191803
  rubric <rubricId> # \u770B\u4E00\u6761\u6807\u51C6\uFF1A\u8BF4\u660E + \u8F93\u51FA\u5951\u7EA6 + \u6CE8\u5165 prompt \u7684\u6B63\u6587\u5168\u6587
191526
191804
  rubric-save [--rubric-id <id>] --name <\u540D> --kind <k> (--prompt <\u6587\u672C>|--prompt-file <\u8DEF\u5F84>) [--spec-file <json>|--spec <json>] [--description \u2026] [--status active|archived]
191527
191805
  # \u4E0D\u5E26 --rubric-id \u662F\u65B0\u5EFA\uFF0C\u5E26\u4E0A\u662F\u6539\uFF08\u7248\u672C\u53F7\u81EA\u589E\uFF09\u3002--spec \u662F\u8F93\u51FA\u5951\u7EA6\uFF1A
191528
191806
  # { "sectionKey": "common", "aggregation": "none"|"mean", "strictIds": true, "dimensions": [{"id":"G1","label":"\u2026","min":1,"max":5,"nullable":false}] }
191529
191807
  # \u5B83\u540C\u65F6\u51B3\u5B9A\u300C\u5224\u5206\u5458\u5DE5\u8981\u8F93\u51FA\u4EC0\u4E48\u5F62\u72B6\u300D\u548C\u300C\u7ED3\u679C\u600E\u4E48\u6821\u9A8C\u300D\u2014\u2014\u4E0D\u518D\u5199\u6B7B G1-G7/\u4E94\u9879\u3002
191530
191808
  rubric-delete <rubricId> # \u5220\u6807\u51C6\uFF08\u6302\u8F7D\u4E86\u5B83\u7684 Case \u540C\u6B65\u6458\u5F15\u7528\uFF09
191531
- case-scoring <caseId> # \u770B\u67D0 Case \u6302\u4E86\u54EA\u51E0\u5957\u6807\u51C6\u3001\u7531\u8C01\u6253\u5206\u3001\u662F\u5426\u8FD8\u5728\u5403\u56DE\u843D
191809
+ rubric-versions <rubricId> [--version N] # \u7248\u672C\u5386\u53F2 / \u67D0\u4E00\u7248\u7684\u6B63\u6587\uFF08Run \u8BB0\u7684\u662F id@version\uFF0C\u9760\u5B83\u56DE\u67E5\uFF09
191810
+ case-rubric <caseId> # \u770B\u8FD9\u4E2A Case **\u81EA\u5DF1\u90A3\u6761**\u6807\u51C6\uFF08\u4E0D\u5728\u5171\u4EAB\u767B\u8BB0\u8868\u91CC\uFF09
191811
+ case-rubric-save <caseId> --name <\u540D> (--prompt <\u6587\u672C>|--prompt-file <\u8DEF\u5F84>) [--spec-file <json>] [--kind \u2026]
191812
+ # Case \u7EA7\u6807\u51C6\uFF1A\u53EA\u5C5E\u4E8E\u8FD9\u4E00\u4E2A Case\uFF0C\u6BCF\u5B58\u4E00\u6B21\u51FA\u65B0\u7248\u672C\uFF0C\u540C\u4E00 Case \u6052\u53EA\u6709\u4E00\u6761\u3002
191813
+ case-scoring <caseId> # \u770B\u67D0 Case \u6302\u4E86\u54EA\u51E0\u5957\u5171\u4EAB\u6807\u51C6\u3001Case \u7EA7\u6807\u51C6\u3001\u7531\u8C01\u6253\u5206
191532
191814
  case-scoring-set <caseId> [--rubrics id1,id2] [--judge <actorId>|none]
191533
191815
  # \u53EA\u7ED9\u5176\u4E2D\u4E00\u4E2A flag \u5C31\u53EA\u6539\u90A3\u4E00\u9879\uFF08\u53E6\u4E00\u9879\u6CBF\u7528\u73B0\u503C\uFF0C\u4E0D\u4F1A\u88AB\u6E05\u7A7A\uFF09\u3002--judge none = \u56DE\u5230\u90E8\u7F72\u9ED8\u8BA4\u3002
191534
191816
  # \u4E00\u6761\u6807\u51C6\u90FD\u4E0D\u6302 = \u56DE\u843D\u5230 oasis-bench \u81EA\u5E26\u7684\u901A\u7528/\u4E13\u9879/Case \u4E13\u9879\u4E09\u4EFD\u6750\u6599\uFF0C\u884C\u4E3A\u4E0E\u914D\u7F6E\u5316\u4E4B\u524D\u4E00\u81F4\u3002
@@ -193511,7 +193793,7 @@ ${res.warning}`);
193511
193793
  const kind = flags.get("kind");
193512
193794
  const res = await api.request("GET", "/api/evaluations/rubrics");
193513
193795
  const items = kind ? res.items.filter((r) => r.kind === kind) : res.items;
193514
- println(`\u8BC4\u5206\u6807\u51C6(${items.length}${kind ? ` / kind=${kind}` : ""})\u2014\u2014\u6302\u5230 Case \u7528 \`oasis case-scoring-set\`:`);
193796
+ println(`\u5171\u4EAB\u8BC4\u5206\u6807\u51C6(${items.length}${kind ? ` / kind=${kind}` : ""})\u2014\u2014\u6302\u5230 Case \u7528 \`oasis case-scoring-set\`\uFF1BCase \u81EA\u5DF1\u7684\u6807\u51C6\u89C1 \`oasis case-rubric <caseId>\`:`);
193515
193797
  for (const r of items) {
193516
193798
  println(`- ${r.rubricId} ${r.name} [${r.kind}] v${r.version}${r.status === "archived" ? " [\u5DF2\u5F52\u6863]" : ""}`);
193517
193799
  println(` \u8F93\u51FA\u6BB5=${r.outputSpec.sectionKey} \u7EF4\u5EA6=${r.outputSpec.dimensions.length || "\u7531 Case \u5B9A\u4E49"}${r.description ? ` ${r.description}` : ""}`);
@@ -193563,12 +193845,14 @@ ${res.warning}`);
193563
193845
  );
193564
193846
  println(`Case ${res.caseId} \u8BC4\u5206\u88C5\u914D:`);
193565
193847
  println(` \u5224\u5206\u5458\u5DE5: ${res.judgeActorId ?? "\uFF08\u7528\u90E8\u7F72\u9ED8\u8BA4\uFF09"}`);
193848
+ println(` \u5171\u4EAB\u6807\u51C6(${res.sharedRubrics.length}):`);
193849
+ for (const r of res.sharedRubrics) {
193850
+ println(` - ${r.rubricId} ${r.name} [${r.kind}] v${r.version} \u2192 \u8F93\u51FA\u6BB5 ${r.outputSpec.sectionKey}`);
193851
+ }
193852
+ if (!res.sharedRubrics.length) println(" \uFF08\u65E0\uFF09");
193853
+ println(` Case \u7EA7\u6807\u51C6: ${res.caseRubric ? `${res.caseRubric.rubricId} ${res.caseRubric.name} v${res.caseRubric.version} \u2192 \u8F93\u51FA\u6BB5 ${res.caseRubric.outputSpec.sectionKey}` : "\uFF08\u672A\u5199\uFF0C\u8BE5\u6BB5\u56DE\u843D\u5230 bench \u91CC\u7684 case-special.yaml\uFF09"}`);
193566
193854
  if (res.usesBuiltinFallback) {
193567
- println(" \u8BC4\u5206\u6807\u51C6: \uFF08\u672A\u6302\u8F7D\uFF0C\u56DE\u843D\u5230 oasis-bench \u81EA\u5E26\u7684\u901A\u7528 / \u4E13\u9879 / Case \u4E13\u9879\u4E09\u4EFD\u6750\u6599\uFF09");
193568
- } else {
193569
- for (const r of res.rubrics) {
193570
- println(` - ${r.rubricId} ${r.name} [${r.kind}] v${r.version} \u2192 \u8F93\u51FA\u6BB5 ${r.outputSpec.sectionKey}`);
193571
- }
193855
+ println(" \u2192 \u4E24\u8005\u90FD\u7A7A\uFF1A\u6574\u6761\u56DE\u843D\u5230 oasis-bench \u81EA\u5E26\u7684\u4E09\u4EFD\u6750\u6599");
193572
193856
  }
193573
193857
  break;
193574
193858
  }
@@ -193578,7 +193862,7 @@ ${res.warning}`);
193578
193862
  "GET",
193579
193863
  `/api/evaluations/cases/${encodeURIComponent(caseId)}/scoring`
193580
193864
  );
193581
- const rubricIds = flags.get("rubrics") !== void 0 ? flags.get("rubrics").split(",").map((v2) => v2.trim()).filter(Boolean) : current.rubrics.map((r) => r.rubricId);
193865
+ const rubricIds = flags.get("rubrics") !== void 0 ? flags.get("rubrics").split(",").map((v2) => v2.trim()).filter(Boolean) : current.sharedRubrics.map((r) => r.rubricId);
193582
193866
  const judgeFlag = flags.get("judge");
193583
193867
  const judgeActorId = judgeFlag === void 0 ? current.judgeActorId : judgeFlag === "none" || judgeFlag === "" ? null : judgeFlag;
193584
193868
  const saved = await api.request(
@@ -193586,8 +193870,70 @@ ${res.warning}`);
193586
193870
  `/api/evaluations/cases/${encodeURIComponent(caseId)}/scoring`,
193587
193871
  { rubricIds, judgeActorId }
193588
193872
  );
193589
- println(`\u5DF2\u66F4\u65B0 Case ${saved.caseId} \u7684\u8BC4\u5206\u88C5\u914D\uFF1A\u6807\u51C6 ${saved.rubrics.length} \u5957\uFF0C\u5224\u5206\u5458\u5DE5 ${saved.judgeActorId ?? "\u90E8\u7F72\u9ED8\u8BA4"}`);
193590
- if (saved.usesBuiltinFallback) println("\uFF08\u4E00\u6761\u90FD\u6CA1\u6302 = \u4ECD\u8D70 oasis-bench \u56DE\u843D\uFF09");
193873
+ println(`\u5DF2\u66F4\u65B0 Case ${saved.caseId} \u7684\u8BC4\u5206\u88C5\u914D\uFF1A\u5171\u4EAB\u6807\u51C6 ${saved.sharedRubrics.length} \u5957\uFF0CCase \u7EA7\u6807\u51C6 ${saved.caseRubric ? "\u6709" : "\u65E0"}\uFF0C\u5224\u5206\u5458\u5DE5 ${saved.judgeActorId ?? "\u90E8\u7F72\u9ED8\u8BA4"}`);
193874
+ if (saved.usesBuiltinFallback) println("\uFF08\u5171\u4EAB\u4E0E Case \u7EA7\u90FD\u7A7A = \u4ECD\u8D70 oasis-bench \u56DE\u843D\uFF09");
193875
+ break;
193876
+ }
193877
+ case "case-rubric": {
193878
+ const caseId = needPos(positional, 0, "oasis case-rubric <caseId>");
193879
+ const res = await api.request(
193880
+ "GET",
193881
+ `/api/evaluations/cases/${encodeURIComponent(caseId)}/scoring`
193882
+ );
193883
+ if (!res.caseRubric) {
193884
+ println(`Case ${caseId} \u8FD8\u6CA1\u6709\u81EA\u5DF1\u7684\u8BC4\u5206\u6807\u51C6\uFF08\u8BE5\u6BB5\u56DE\u843D\u5230 bench \u91CC\u7684 case-special.yaml\uFF09\u3002`);
193885
+ println(`\u5199\u4E00\u6761\uFF1Aoasis case-rubric-save ${caseId} --name <\u540D> --prompt <\u6B63\u6587>`);
193886
+ break;
193887
+ }
193888
+ const r = res.caseRubric;
193889
+ println(`${r.name} [Case \u7EA7 / ${r.kind}] v${r.version} \u5F52\u5C5E ${r.ownerCaseId ?? caseId}`);
193890
+ if (r.description) println(`\u8BF4\u660E: ${r.description}`);
193891
+ println(`\u8F93\u51FA\u5951\u7EA6: ${JSON.stringify(r.outputSpec, null, 2)}`);
193892
+ println("--- \u6CE8\u5165\u5224\u5206 prompt \u7684\u6B63\u6587 ---");
193893
+ println(r.promptSection);
193894
+ break;
193895
+ }
193896
+ case "case-rubric-save": {
193897
+ const caseId = needPos(positional, 0, "oasis case-rubric-save <caseId> --name <\u540D> (--prompt <\u6587\u672C>|--prompt-file <\u8DEF\u5F84>)");
193898
+ const promptSection = flags.get("prompt-file") !== void 0 ? readFileArg(flags.get("prompt-file")) : flags.get("prompt");
193899
+ const outputSpec = flags.get("spec-file") !== void 0 ? JSON.parse(readFileArg(flags.get("spec-file"))) : flags.get("spec") !== void 0 ? JSON.parse(flags.get("spec")) : void 0;
193900
+ const body = {};
193901
+ if (promptSection !== void 0) body["promptSection"] = promptSection;
193902
+ if (flags.get("name") !== void 0) body["name"] = flags.get("name");
193903
+ if (flags.get("description") !== void 0) body["description"] = flags.get("description");
193904
+ body["kind"] = flags.get("kind") ?? "case_special";
193905
+ if (outputSpec !== void 0) body["outputSpec"] = outputSpec;
193906
+ else body["outputSpec"] = { sectionKey: "caseSpecial", aggregation: "none", dimensions: [] };
193907
+ const saved = await api.request(
193908
+ "PUT",
193909
+ `/api/evaluations/cases/${encodeURIComponent(caseId)}/rubric`,
193910
+ body
193911
+ );
193912
+ println(`\u5DF2\u4FDD\u5B58 Case ${caseId} \u7684\u8BC4\u5206\u6807\u51C6\uFF1A${saved.rubricId} ${saved.name} v${saved.version}`);
193913
+ break;
193914
+ }
193915
+ case "rubric-versions": {
193916
+ const rubricId = needPos(positional, 0, "oasis rubric-versions <rubricId> [--version N]");
193917
+ const one = flags.get("version");
193918
+ if (one) {
193919
+ const v2 = await api.request(
193920
+ "GET",
193921
+ `/api/evaluations/rubrics/${encodeURIComponent(rubricId)}/versions/${encodeURIComponent(one)}`
193922
+ );
193923
+ println(`${v2.name} [${v2.scope}/${v2.kind}] v${v2.version} \u7531 ${v2.createdBy} \u4E8E ${v2.createdAt}`);
193924
+ println(`\u8F93\u51FA\u5951\u7EA6: ${JSON.stringify(v2.outputSpec, null, 2)}`);
193925
+ println("--- \u8BE5\u7248\u672C\u7684\u6B63\u6587 ---");
193926
+ println(v2.promptSection);
193927
+ break;
193928
+ }
193929
+ const res = await api.request(
193930
+ "GET",
193931
+ `/api/evaluations/rubrics/${encodeURIComponent(rubricId)}/versions`
193932
+ );
193933
+ println(`${rubricId} \u7684\u7248\u672C\u5386\u53F2(${res.items.length}\uFF0C\u65B0\u2192\u65E7):`);
193934
+ for (const v2 of res.items) {
193935
+ println(`- v${v2.version} ${v2.name} \u7531 ${v2.createdBy} \u4E8E ${v2.createdAt}`);
193936
+ }
193591
193937
  break;
193592
193938
  }
193593
193939
  case "judges": {
@@ -193608,7 +193954,12 @@ ${res.warning}`);
193608
193954
  for (const item of items) {
193609
193955
  const e = item.latestEvaluation;
193610
193956
  println(`- ${item.run.runId} ${item.run.workOrderName} case=${item.run.caseId}#${item.run.attemptNo}`);
193611
- println(` \u8BC4\u5206=${e ? `${e.status}${e.errorMessage ? ` (${e.errorMessage})` : ""}` : "\u5C1A\u672A\u8BC4\u5206"} \u5224\u5206\u5458\u5DE5=${e?.judgeActorId ?? "-"} \u6807\u51C6\u7248\u672C=${e?.rubricVersion ?? "-"}`);
193957
+ println(` \u8BC4\u5206=${e ? `${e.status}${e.errorMessage ? ` (${e.errorMessage})` : ""}` : "\u5C1A\u672A\u8BC4\u5206"} \u5224\u5206\u5458\u5DE5=${e?.judgeActorId ?? "-"}`);
193958
+ if (e?.rubricRefs?.length) {
193959
+ println(` \u672C\u6B21\u6807\u51C6: ${e.rubricRefs.map((r) => `${r.name}@v${r.version}[${r.scope}]`).join(" ")}`);
193960
+ } else if (e) {
193961
+ println(` \u672C\u6B21\u6807\u51C6: ${e.rubricVersion}`);
193962
+ }
193612
193963
  }
193613
193964
  break;
193614
193965
  }
@@ -193807,6 +194158,14 @@ init_src5();
193807
194158
  var fs34 = __toESM(require("node:fs"));
193808
194159
  var path28 = __toESM(require("node:path"));
193809
194160
  var ASSET_EXT = ".sh";
194161
+ var SESSION_SCOPED_PREFIXES = ["oasis-conn-", "oasis-wrappers-", "oasis-cli-"];
194162
+ function sanitizeDaemonPath(rawPath) {
194163
+ return (rawPath ?? "").split(":").filter((dir) => {
194164
+ if (!dir) return false;
194165
+ const base = path28.basename(dir);
194166
+ return !SESSION_SCOPED_PREFIXES.some((p2) => base.startsWith(p2));
194167
+ }).join(":");
194168
+ }
193810
194169
  function installRuntimeAssets(srcRoot, binDir) {
193811
194170
  if (path28.resolve(srcRoot) === path28.resolve(binDir)) return 0;
193812
194171
  let copied = 0;
@@ -193847,7 +194206,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
193847
194206
  }
193848
194207
 
193849
194208
  // src/index.ts
193850
- var PKG_VERSION = true ? "0.1.82" : "dev";
194209
+ var PKG_VERSION = true ? "0.1.84" : "dev";
193851
194210
  var OASIS_DIR = path29.join(os9.homedir(), ".oasis");
193852
194211
  var CONFIG_FILE = path29.join(OASIS_DIR, "node-config.json");
193853
194212
  var PID_FILE = path29.join(OASIS_DIR, "node.pid");
@@ -193925,7 +194284,7 @@ function setupAutostart() {
193925
194284
  path29.join(home, ".local", "bin"),
193926
194285
  "/usr/local/bin"
193927
194286
  ];
193928
- const daemonPath = [process.env["PATH"] ?? "", ...extraBins].filter(Boolean).join(":");
194287
+ const daemonPath = sanitizeDaemonPath([process.env["PATH"] ?? "", ...extraBins].filter(Boolean).join(":"));
193929
194288
  if (process.platform === "linux") {
193930
194289
  const unitDir = path29.join(os9.homedir(), ".config", "systemd", "user");
193931
194290
  fs35.mkdirSync(unitDir, { recursive: true });
@@ -194071,6 +194430,11 @@ void (async () => {
194071
194430
  cleanup();
194072
194431
  process.exit(0);
194073
194432
  });
194433
+ const cleanPath = sanitizeDaemonPath(process.env["PATH"]);
194434
+ if (cleanPath !== process.env["PATH"]) {
194435
+ console.error(`[oasis] daemon PATH \u5DF2\u6E05\u6D17\uFF1A\u5254\u9664 agent \u4F1A\u8BDD\u4E34\u65F6 wrapper \u76EE\u5F55 ${(process.env["PATH"] ?? "").split(":").length - cleanPath.split(":").length} \u6761`);
194436
+ process.env["PATH"] = cleanPath;
194437
+ }
194074
194438
  try {
194075
194439
  ensureRuntimeAssets();
194076
194440
  } catch (e) {