@tea-agent/loop-agent 0.44.0-next.13 → 0.44.0-next.14

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 (145) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +12 -12
  3. package/dist/adapters/loop-agent.js +34 -0
  4. package/dist/build-stamp.json +3 -3
  5. package/dist/cli/command-definitions.js +5 -5
  6. package/dist/cli/program.js +1 -4
  7. package/dist/commands/init-legacy-audit.js +330 -0
  8. package/dist/commands/init-upgrade.js +644 -104
  9. package/dist/commands/init.js +685 -330
  10. package/dist/commands/operator-guide.js +99 -0
  11. package/dist/commands/operator.js +16 -3
  12. package/dist/executors/pi-sdk-executor.js +3 -1
  13. package/dist/governance/harness.js +2 -7
  14. package/dist/governance/manifest-types.js +28 -9
  15. package/dist/governance/project-capability-scanner.js +263 -0
  16. package/dist/governance/project-config.js +96 -0
  17. package/dist/governance/resource-catalog.js +151 -0
  18. package/dist/infrastructure/console/app-data.js +6 -2
  19. package/dist/shared/operator/capabilities.js +2 -2
  20. package/dist/shared/package-metadata.js +23 -7
  21. package/dist/shared/pi-context-pressure/extension.js +23 -0
  22. package/dist/shared/pi-context-pressure/index.js +1 -0
  23. package/dist/shared/pi-context-pressure/telemetry.js +4 -0
  24. package/dist/shared/pi-context-pressure/tool-result-pruner.js +98 -0
  25. package/dist/shared/update/npm-client.js +49 -1
  26. package/dist/worker/cli.js +3 -5
  27. package/dist/worker/console/chat/browser-automation.js +114 -5
  28. package/dist/worker/console/chat/browser-routes.js +21 -0
  29. package/dist/worker/console/chat/chat-event-store.js +7 -6
  30. package/dist/worker/console/chat/context-insights.js +84 -19
  31. package/dist/worker/console/chat/pet/pet-affinity.js +169 -0
  32. package/dist/worker/console/chat/pet/pet-persist.js +109 -0
  33. package/dist/worker/console/chat/pet/pet-registry.js +142 -0
  34. package/dist/worker/console/chat/pet/pet-remarks.js +48 -0
  35. package/dist/worker/console/chat/pet/pet-routes.js +125 -0
  36. package/dist/worker/console/chat/pet/pet-service.js +262 -0
  37. package/dist/worker/console/chat/pet/pet-state.js +99 -0
  38. package/dist/worker/console/chat/pi-mode-loop-isolation.js +17 -2
  39. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +2 -2
  40. package/dist/worker/console/chat/pi-runtime.js +7 -2
  41. package/dist/worker/console/chat/routes.js +43 -7
  42. package/dist/worker/console/chat/scheduled-goal-request.js +14 -6
  43. package/dist/worker/console/chat/session-store.js +20 -3
  44. package/dist/worker/console/console-shutdown.js +34 -0
  45. package/dist/worker/console/server.js +2 -1
  46. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BA7qCL_r.js → abnfDiagram-N423BO3Z-DASc69AM.js} +1 -1
  47. package/dist/worker/console/static/assets/{arc-Df09ajwL.js → arc-h09p-LAc.js} +1 -1
  48. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CpF9sw5R.js → architectureDiagram-T3A2C74G-CZ3dtLkh.js} +1 -1
  49. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BtPys8ja.js → blockDiagram-VBNYF7ZC-DMrRhMXG.js} +1 -1
  50. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DZPnTWqT.js → c4Diagram-5PPSVZJV-DQxisHV6.js} +1 -1
  51. package/dist/worker/console/static/assets/channel-D1zgKZfg.js +1 -0
  52. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BD9D4h1p.js → chunk-2GRJ4B5K-CefVN9zh.js} +1 -1
  53. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-Svn8yBmn.js → chunk-2Q5K7J3B-DmR1Koq2.js} +1 -1
  54. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-B8EbQj-S.js → chunk-5RXB4S5H-NlzrSW7L.js} +1 -1
  55. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-tENZpcpF.js → chunk-5VM5RSS4-DfelJv-0.js} +1 -1
  56. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DBx71Lco.js → chunk-6Q2QTUOP-BK6RFy58.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-1v_QamMz.js → chunk-GF5L2VYU-BAeh9lz4.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BAMCNNBG.js → chunk-JWPE2WC7-ChPS5WzG.js} +1 -1
  59. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BvuEury-.js → chunk-KBJHAD2P-BoVe5IM6.js} +1 -1
  60. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-YqFuHp8s.js → chunk-RYQCIY6F-C4q_0Lw9.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-ouI5T7Mr.js → chunk-XXDRQBXY-Bb9TvjeY.js} +1 -1
  62. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CXNNsY2M.js +1 -0
  63. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CXNNsY2M.js +1 -0
  64. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-Bri0_Izk.js → cose-bilkent-JH36ORCC-DmNnL8Ff.js} +1 -1
  65. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-e8Vhhi4X.js → cynefin-VYW2F7L2-Contvx58.js} +1 -1
  66. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-tSIXGQiJ.js → cynefinDiagram-MW4NZA55-Cigq7cjk.js} +1 -1
  67. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Rl-sF5pt.js → dagre-VZM6K2ZE-dEbC0nxb.js} +1 -1
  68. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Db6ddPz8.js → diagram-7IWD3JNH-CzIztESV.js} +1 -1
  69. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Jwd_Xw5S.js → diagram-B4RE2ZJO-Bo14zbaY.js} +1 -1
  70. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CeC_8InC.js → diagram-LBJQPF4R-C4xUyil6.js} +1 -1
  71. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-LKPRaNq8.js → diagram-Q27KOJAE-CaBY_uYa.js} +1 -1
  72. package/dist/worker/console/static/assets/{diagram-UB23O5K3-DlVQytMW.js → diagram-UB23O5K3-ClUXUeiu.js} +1 -1
  73. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DnfJZwO-.js → ebnfDiagram-BXEA7PRR-BbGCgjC6.js} +1 -1
  74. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-3axVSXca.js → erDiagram-JOGREHBK-Dr-AcwSl.js} +1 -1
  75. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-xe04-_L-.js → flowDiagram-UKHOOZJN-BG8Gcfyc.js} +1 -1
  76. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-hpmRINiD.js → ganttDiagram-PKOTCBZU-CFVaPs5r.js} +1 -1
  77. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-6HSNz9Ko.js → gitGraphDiagram-DS77QQ5N-ChJ8pm-O.js} +1 -1
  78. package/dist/worker/console/static/assets/index-B_5GhVIO.js +488 -0
  79. package/dist/worker/console/static/assets/index-C7YMBluX.css +1 -0
  80. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BF6ksWTX.js → infoDiagram-6WML65LV-C5J9-uCZ.js} +1 -1
  81. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DXisdeD7.js → ishikawaDiagram-WSZJBQD7-BcfjqzDv.js} +1 -1
  82. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BWu65wmu.js → journeyDiagram-NVQOT4AX-BzzCO-rD.js} +1 -1
  83. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-hfNhHqAI.js → kanban-definition-27J2QSJJ-NCHUZ46h.js} +1 -1
  84. package/dist/worker/console/static/assets/{linear-D6HTfQcp.js → linear-BKZlWCUU.js} +1 -1
  85. package/dist/worker/console/static/assets/{mermaid.core-Iv91R1WP.js → mermaid.core-DbTaQjFE.js} +5 -5
  86. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CfPChwjR.js → mindmap-definition-FAOFIHXS-CroYl5xQ.js} +1 -1
  87. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-B7TcoqHz.js → pegDiagram-VL7TDLO6-DgMBx-9k.js} +1 -1
  88. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-ouyPKMtQ.js → pieDiagram-7S7Q4E2Y-CyWDmbq1.js} +1 -1
  89. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B9tuyLbh.js → quadrantDiagram-CIZ2JOQS-BgGEOqSP.js} +1 -1
  90. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Ck4q9nJs.js → railroadDiagram-AXF67PYL-BMZ7oeeV.js} +1 -1
  91. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-wXLX5Dnp.js → requirementDiagram-LRYGKXZP-DxqyGhcc.js} +1 -1
  92. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-D2XWw6_m.js → sankeyDiagram-W5VNT64P-DnSvNCG8.js} +1 -1
  93. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DzxKiug4.js → sequenceDiagram-SI44F4Z6-BiZh6f-R.js} +1 -1
  94. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DWUqK5Us.js → sizeCapture-X5ZJPWSS-BKXSI8WB.js} +1 -1
  95. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-D5Pa4O5g.js → stateDiagram-OKZ733FA-CfSgoYFm.js} +1 -1
  96. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DgHi3vjb.js +1 -0
  97. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-VH7cRnsJ.js → swimlanes-SLNWSIFB-CyGADcHe.js} +2 -2
  98. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bgvdt1Y0.js +8 -0
  99. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-C4HM5NoZ.js → timeline-definition-Z64GVDOM-HmPaSPOK.js} +1 -1
  100. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-Bu-4hOlA.js → vennDiagram-T6HMQDX7-VA2VBbez.js} +1 -1
  101. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CasFEvvl.js → wardleyDiagram-T6FBY63Y-BxjiBPZa.js} +1 -1
  102. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-D-gpwPSA.js → xychartDiagram-ELKLHX3M--hUY-qFj.js} +1 -1
  103. package/dist/worker/console/static/index.html +2 -2
  104. package/dist/worker/console/static/pets/ember/pet.json +167 -0
  105. package/dist/worker/console/static/pets/ember/spritesheet.png +0 -0
  106. package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +4 -3
  107. package/dist/worker/console/static-src/operator-chat/use-stat-dialog.js +95 -0
  108. package/dist/worker/console/workspace-context.js +14 -0
  109. package/dist/worker/console/workspace-initialization.js +25 -16
  110. package/dist/worker/delivery/package.js +4 -1
  111. package/dist/worker/observe/static/operator-chrome.js +1 -42
  112. package/dist/workflows/dag/backend-test-pytest-collection.js +4 -1
  113. package/dist/workflows/dag/final-verification.js +45 -10
  114. package/dist/workflows/dag/hybrid/sources.js +14 -14
  115. package/dist/workflows/dag/hybrid/templates/backend-test.js +5 -5
  116. package/dist/workflows/dag/hybrid/templates/frontend-test.js +4 -4
  117. package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +6 -6
  118. package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +5 -5
  119. package/dist/workflows/dag/skill-instructions.js +102 -32
  120. package/dist/workflows/dag/skill-snapshot.js +3 -0
  121. package/dist/workflows/dag/skills.js +7 -7
  122. package/dist/workflows/dag/validate.js +2 -2
  123. package/dist/workflows/dag/workspace-checkpoint.js +13 -1
  124. package/docs/architecture/runtime-boundaries.md +28 -22
  125. package/docs/init-surface.manifest.json +43 -295
  126. package/docs/templates/agent-dag.base.json +9 -9
  127. package/docs/templates/agent-dag.final-verification.json +6 -6
  128. package/docs/templates/agent-dag.supervised-implementation.json +12 -12
  129. package/docs/templates/frontend-implementation-dag.json +2 -2
  130. package/docs/templates/frontend-test-dag.json +2 -2
  131. package/docs/templates/hybrid-dag.json +9 -9
  132. package/docs/templates/knowledge-sync-dag.json +8 -8
  133. package/package.json +6 -2
  134. package/resource-catalog.v1.json +1973 -0
  135. package/resources/agent-bridge/loop-agent/SKILL.md +40 -0
  136. package/resources/init-history/legacy-surface.v1.json +33 -0
  137. package/skills/init-update/SKILL.md +34 -0
  138. package/skills/init-update/agents/openai.yaml +6 -0
  139. package/dist/worker/console/static/assets/channel-DiTNLZUm.js +0 -1
  140. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DgL8aiyZ.js +0 -1
  141. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DgL8aiyZ.js +0 -1
  142. package/dist/worker/console/static/assets/index-C_8_gWal.css +0 -1
  143. package/dist/worker/console/static/assets/index-DybcVXCu.js +0 -488
  144. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-C0QIbqO4.js +0 -1
  145. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BB9CzRHB.js +0 -8
@@ -70,7 +70,7 @@ export class WorkspaceInitializationService {
70
70
  }
71
71
  const surface = await this.jsonFile(".harness/init-surface.json");
72
72
  const harness = await this.jsonFile("harness.json");
73
- if (surface && (surface.schemaVersion !== 1 || !surface.files || typeof surface.files !== "object" || Array.isArray(surface.files))) {
73
+ if (surface && (![1, 2].includes(Number(surface.schemaVersion)) || !surface.files || typeof surface.files !== "object" || Array.isArray(surface.files))) {
74
74
  throw new Error("初始化记录不完整,请检查 .harness/init-surface.json");
75
75
  }
76
76
  if (!surface && !harness) {
@@ -83,10 +83,15 @@ export class WorkspaceInitializationService {
83
83
  }
84
84
  return snapshot;
85
85
  }
86
- const report = await this.command(["init", "check-update", "--json"], "check");
86
+ const report = await this.command(["init", "check-update", "--legacy-audit", "--json"], "check");
87
87
  if (!["recorded", "inferred-baseline", "missing"].includes(String(report.surfaceState)) || typeof report.ok !== "boolean") {
88
88
  throw new Error("初始化检查返回了未知状态");
89
89
  }
90
+ const coreState = String(report.coreState);
91
+ const migrationState = String(report.migrationState);
92
+ if (!["ready", "blocked", "error"].includes(coreState) || !["clean", "retained", "review-required", "in-progress", "failed"].includes(migrationState)) {
93
+ throw new Error("初始化检查缺少核心或迁移状态");
94
+ }
90
95
  const actions = entries(report.deterministicActions);
91
96
  const merges = entries(report.modelMergeTasks);
92
97
  const decisions = entries(report.humanDecisions);
@@ -98,17 +103,20 @@ export class WorkspaceInitializationService {
98
103
  const detail = typeof item.reason === "string" ? item.reason : undefined;
99
104
  const baseline = item.type === "bootstrap-surface";
100
105
  const itemKind = baseline ? "confirmation" : kind;
106
+ const legacyCandidate = typeof item.candidateId === "string";
101
107
  const reason = baseline ? "缺少可靠的初始化记录,不能直接判断哪些内容可覆盖。"
102
- : kind === "safe-update" ? "此项可由程序安全更新,保留项目自定义内容。"
103
- : kind === "merge" ? "现有内容与新版初始化模板不同,需要保留项目配置后合并。"
104
- : /not a directory/.test(detail ?? "") ? "这里需要一个目录,但当前路径被文件占用。"
105
- : /removed.*local changes/.test(detail ?? "") ? "新版不再使用此文件,但它包含本地修改,需要决定保留还是删除。"
106
- : /target path.*already exists/.test(detail ?? "") ? "迁移目标已有不同内容,需要确认两份文件如何保留。"
107
- : "此项无法安全自动处理,请先核对检查详情,再确定处理方式。";
108
+ : legacyCandidate ? "这是历史初始化候选,但当前证据不足以自动删除。"
109
+ : kind === "safe-update" ? "此项可由程序安全更新,保留项目自定义内容。"
110
+ : kind === "merge" ? "现有内容与新版初始化模板不同,需要保留项目配置后合并。"
111
+ : /not a directory/.test(detail ?? "") ? "这里需要一个目录,但当前路径被文件占用。"
112
+ : /removed.*local changes/.test(detail ?? "") ? "新版不再使用此文件,但它包含本地修改,需要决定保留还是删除。"
113
+ : /target path.*already exists/.test(detail ?? "") ? "迁移目标已有不同内容,需要确认两份文件如何保留。"
114
+ : "此项无法安全自动处理,请先核对检查详情,再确定处理方式。";
108
115
  pendingItems.push({ path: item.path, kind: itemKind, reason,
109
116
  nextStep: itemKind === "safe-update" ? "点击更新按钮即可处理。"
110
117
  : itemKind === "merge" ? "可将下方处理说明交给智能体合并;只有需求冲突时才需你确认。"
111
- : "将处理说明交给智能体检查;需要取舍时确认方案,处理后重新检查。",
118
+ : legacyCandidate ? "在对话页手动调用 /skill:init-update;Skill 会解释证据并在需要时询问,不会直接删除文件。"
119
+ : "将处理说明交给智能体检查;需要取舍时确认方案,处理后重新检查。",
112
120
  ...(detail ? { detail } : {}) });
113
121
  }
114
122
  }
@@ -118,19 +126,20 @@ export class WorkspaceInitializationService {
118
126
  reason: "当前模型配置或认证尚未就绪,更新文件不能解决此问题。",
119
127
  nextStep: "检查模型定义及提供商认证,保留仍可用的模型配置,完成后重新检查。" });
120
128
  const canUpdate = pendingItems.some(item => item.kind === "safe-update");
121
- const needsAttention = pendingItems.some(item => item.kind !== "safe-update") || !report.ok || report.surfaceState !== "recorded" && !canUpdate;
129
+ const needsAttention = pendingItems.some(item => item.kind !== "safe-update") || coreState !== "ready" || migrationState === "review-required" || migrationState === "failed" || report.surfaceState !== "recorded" && !canUpdate;
122
130
  if (needsAttention || canUpdate) {
123
131
  const safeCount = pendingItems.filter(item => item.kind === "safe-update").length;
132
+ const coreReadyPrefix = coreState === "ready" ? "核心初始化已就绪;" : "";
124
133
  const message = canUpdate
125
- ? `有 ${safeCount} 项可在此更新。${needsAttention ? "其余待处理项的原因和下一步见下方。" : "更新会保留项目自定义内容。"}`
126
- : "暂无可直接更新的内容。请按下方原因处理,完成后重新检查。";
134
+ ? `${coreReadyPrefix}有 ${safeCount} 项可在此安全更新。${needsAttention ? "其余旧初始化待处理项见下方。" : "更新会保留项目自定义内容。"}`
135
+ : `${coreReadyPrefix}暂无可直接更新的内容。请按下方原因处理,完成后重新检查。`;
127
136
  if (!pendingItems.length)
128
137
  pendingItems.push({ path: "初始化配置", kind: "confirmation",
129
138
  reason: "检查尚未通过,但没有返回可自动更新的项目。",
130
139
  nextStep: "复制处理说明交给智能体检查初始化记录,完成后重新检查。" });
131
- return { ...this.snapshot(needsAttention ? "needs-attention" : "update-available", message, [...new Set(pendingItems.map(item => item.path))]), canUpdate, pendingItems };
140
+ return { ...this.snapshot(needsAttention ? "needs-attention" : "update-available", message, [...new Set(pendingItems.map(item => item.path))]), canUpdate, pendingItems, coreState: coreState, migrationState: migrationState };
132
141
  }
133
- return this.snapshot("ready", "工作区配置已就绪");
142
+ return { ...this.snapshot("ready", migrationState === "retained" ? "核心初始化已就绪;旧初始化内容已按决定保留。" : "工作区配置已就绪"), coreState: coreState, migrationState: migrationState };
134
143
  }
135
144
  catch (error) {
136
145
  return this.snapshot("error", error instanceof Error ? error.message : "初始化状态检查失败");
@@ -182,10 +191,10 @@ export class WorkspaceInitializationService {
182
191
  if (before.state === "uninitialized") {
183
192
  if (!model || !before.models?.some(item => item.reference === model))
184
193
  throw new Error("请选择一个可用的初始模型;可在模型设置中完成认证后重新检查");
185
- await this.command(["init", "--profile", "full", "--merge", "--client-recovery", "project", "--model", model, "--json"], "apply");
194
+ await this.command(["init", "--merge", "--model", model, "--json"], "apply");
186
195
  }
187
196
  else if (before.canUpdate) {
188
- await this.command(["init", "update", "--apply-safe", "--client-recovery", "project", "--json"], "apply");
197
+ await this.command(["init", "update", "--apply-safe", "--legacy-audit", "--json"], "apply");
189
198
  }
190
199
  else if (before.state !== "ready")
191
200
  throw new Error("没有可安全更新的内容;请查看待处理项的原因和下一步。");
@@ -48,12 +48,15 @@ const controllerIdentityEvidenceSchema = z.object({
48
48
  reportedVersion: z.string().min(1).optional(),
49
49
  packageFingerprint: z.object({
50
50
  algorithm: z.literal("sha256"),
51
- scopeVersion: z.literal(1),
51
+ scopeVersion: z.literal(2),
52
52
  scope: z.tuple([
53
53
  z.literal("package.json"),
54
54
  z.literal("bin/**"),
55
55
  z.literal("dist/**"),
56
56
  z.literal("skills/**"),
57
+ z.literal("docs/templates/**"),
58
+ z.literal("resources/**"),
59
+ z.literal("resource-catalog.v1.json"),
57
60
  ]),
58
61
  value: z.string().regex(/^sha256:[a-f0-9]{64}$/),
59
62
  fileCount: z.number().int().positive(),
@@ -12,8 +12,7 @@
12
12
  */
13
13
 
14
14
  import { withWorkspaceQuery } from "./router.js";
15
- import { CONSOLE_THEMES, applyStoredConsoleTheme, setConsoleTheme } from "./console-theme.js";
16
- export { CONSOLE_THEMES } from "./console-theme.js";
15
+ import { applyStoredConsoleTheme } from "./console-theme.js";
17
16
 
18
17
  export const OPERATE_TABS = Object.freeze([
19
18
  { id: "tasks", label: "任务", href: "/#/operate/tasks" },
@@ -711,46 +710,6 @@ export function mountOperatorChrome(root, config = {}) {
711
710
 
712
711
  global.appendChild(el("span", "och-spacer"));
713
712
 
714
- const themePicker = el("div", "och-theme");
715
- const trigger = el("button", "och-theme-trigger");
716
- trigger.type = "button";
717
- trigger.title = "主题";
718
- trigger.setAttribute("aria-label", "主题");
719
- trigger.appendChild(makeIcon("theme"));
720
- trigger.setAttribute("aria-expanded", "false");
721
- const menu = el("div", "och-theme-menu");
722
- menu.id = `${popoverId}-theme`;
723
- menu.popover = "auto";
724
- menu.setAttribute("role", "group");
725
- menu.setAttribute("aria-label", "选择主题");
726
- trigger.setAttribute("aria-controls", menu.id);
727
- trigger.popoverTargetElement = menu;
728
- menu.addEventListener("beforetoggle", (event) => {
729
- if (event.newState !== "open") return;
730
- const rect = trigger.getBoundingClientRect();
731
- menu.style.top = `${rect.bottom + 6}px`;
732
- menu.style.left = `${Math.max(8, Math.min(rect.right - 112, window.innerWidth - 120))}px`;
733
- });
734
- menu.addEventListener("toggle", (event) => {
735
- trigger.setAttribute("aria-expanded", String(event.newState === "open"));
736
- });
737
- for (const theme of CONSOLE_THEMES) {
738
- const option = el("button", "och-theme-option", theme.label);
739
- option.type = "button";
740
- option.dataset.theme = theme.id;
741
- option.setAttribute("aria-pressed", String(theme.id === (document.documentElement.dataset.consoleTheme ?? "default")));
742
- option.addEventListener("click", () => {
743
- setConsoleTheme(theme.id);
744
- for (const item of menu.children) {
745
- item.setAttribute("aria-pressed", String(item.dataset.theme === theme.id));
746
- }
747
- menu.hidePopover();
748
- trigger.focus();
749
- });
750
- menu.appendChild(option);
751
- }
752
- themePicker.append(trigger, menu);
753
- global.appendChild(themePicker);
754
713
 
755
714
  if (model.statusVisible && model.freshness) {
756
715
  const fresh = el("span", "och-fresh");
@@ -422,7 +422,10 @@ function classifyCollectionFailure(output, evidence) {
422
422
  const blockedPatterns = [
423
423
  [/No module named ['\"](?!testcase(?:\.|['\"]))/i, "missing-third-party-module"],
424
424
  [/pytest(?:_|-)plugin|pluggy|unknown hook|unrecognized arguments/i, "pytest-plugin-or-config"],
425
- [/(?:apps|src|server|api)\/[A-Za-z0-9_./-]+\.py/i, "production-module-import"],
425
+ // Windows Python installs live under ...\WindowsApps\...; after slash
426
+ // normalization that path suffix must not read as an `apps/…py`
427
+ // production import (pytest >= 9 includes the importlib bootstrap frame).
428
+ [/(?<!Windows)(?:apps|src|server|api)\/[A-Za-z0-9_./-]+\.py/i, "production-module-import"],
426
429
  [/permission denied|access is denied|timed? out|not recognized as an internal|no such file or directory.*python/i, "environment-or-spawn"],
427
430
  ];
428
431
  for (const [pattern, kind] of blockedPatterns) {
@@ -3,7 +3,8 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { DEFAULT_DAG_EXECUTOR_MODELS } from './types.js';
5
5
  import { parseDagSpec } from './types.js';
6
- import { loadHarnessManifest } from '../../governance/harness.js';
6
+ import { ProjectCapabilityScanner } from '../../governance/project-capability-scanner.js';
7
+ import { loadNormalizedProjectConfig } from '../../governance/project-config.js';
7
8
  import { loadTaskConfig } from '../../task/runtime.js';
8
9
  import { resolveCliPath } from '../../shared/path-refs.js';
9
10
  const DEFAULT_OUTPUT_DIR = os.tmpdir();
@@ -22,14 +23,13 @@ export async function initFinalVerificationDagFromTask(repoRoot, taskId, options
22
23
  };
23
24
  }
24
25
  export async function buildFinalVerificationDagFromTask(repoRoot, taskId, options = {}) {
25
- const [taskConfig, manifest] = await Promise.all([
26
- loadTaskConfig(repoRoot, taskId),
27
- loadHarnessManifest(repoRoot),
28
- ]);
26
+ const taskConfig = await loadTaskConfig(repoRoot, taskId);
29
27
  const taskGlob = options.taskGlob ?? taskId;
30
28
  const closeoutPath = options.closeoutPath ?? path.join(os.tmpdir(), `${taskId}-closeout-report.md`);
31
- const checkRepo = manifest.scripts.checkRepo ?? 'scripts/check-repo.sh';
32
- const finalVerifyCommands = [`HARNESS_ALLOW_ACTIVE_DAG_RUNS=1 bash ${checkRepo}`];
29
+ const finalVerifyCommands = await resolveFinalVerificationCommands({
30
+ repoRoot,
31
+ taskCommands: taskConfig.verifyCommands.map((entry) => entry.command),
32
+ });
33
33
  const hasChangesetSignal = taskDeclaresReleaseImpact(taskConfig);
34
34
  const tasks = [
35
35
  shellTask({
@@ -142,12 +142,12 @@ export async function buildFinalVerificationDagFromTask(repoRoot, taskId, option
142
142
  executor: 'shell',
143
143
  contextProfile: 'slim',
144
144
  writePolicy: 'read-only',
145
- skills: ['ai-engineering-context'],
145
+ skills: ['builtin:ai-engineering-context'],
146
146
  },
147
147
  executorModels: DEFAULT_DAG_EXECUTOR_MODELS,
148
148
  skillsByRole: {
149
- verifier: ['verification-before-completion'],
150
- closeout: ['loop-agent', 'verification-before-completion'],
149
+ verifier: ['builtin:verification-before-completion'],
150
+ closeout: ['builtin:loop-agent', 'builtin:verification-before-completion'],
151
151
  },
152
152
  tasks,
153
153
  });
@@ -178,3 +178,38 @@ function taskDeclaresReleaseImpact(taskConfig) {
178
178
  ].join('\n');
179
179
  return /changeset|release|publish|version/i.test(haystack);
180
180
  }
181
+ export async function resolveFinalVerificationCommandSelection(input) {
182
+ if (input.taskCommands.length > 0) {
183
+ return {
184
+ source: 'task-contract',
185
+ commands: [...new Set(input.taskCommands)],
186
+ };
187
+ }
188
+ const verification = (await loadNormalizedProjectConfig(input.repoRoot)).verification;
189
+ const configured = verification.full.length > 0
190
+ ? verification.full
191
+ : verification.standard.length > 0
192
+ ? verification.standard
193
+ : verification.quick;
194
+ if (configured.length > 0) {
195
+ return {
196
+ source: 'project-config',
197
+ commands: [...new Set(configured)],
198
+ };
199
+ }
200
+ const scan = await new ProjectCapabilityScanner().scan(input.repoRoot);
201
+ const selected = scan.verification.commands.find((candidate) => candidate.id.endsWith(':test'))
202
+ ?? scan.verification.commands.find((candidate) => candidate.id.endsWith(':typecheck'))
203
+ ?? scan.verification.commands.find((candidate) => candidate.id.endsWith(':lint'))
204
+ ?? scan.verification.commands[0];
205
+ if (scan.verification.status !== 'ready' || !selected) {
206
+ throw new Error(`final verification is ${scan.verification.status}; add Task Contract verification or .loop-agent/project.json verification`);
207
+ }
208
+ return {
209
+ source: 'capability-scanner',
210
+ commands: [selected.command],
211
+ };
212
+ }
213
+ async function resolveFinalVerificationCommands(input) {
214
+ return (await resolveFinalVerificationCommandSelection(input)).commands;
215
+ }
@@ -32,16 +32,16 @@ export const HYBRID_DEFAULTS = {
32
32
  executor: "pi",
33
33
  piBackend: "sdk-first",
34
34
  contextProfile: "slim",
35
- skills: ["ai-engineering-context"],
35
+ skills: ["builtin:ai-engineering-context"],
36
36
  writePolicy: "read-only",
37
37
  };
38
38
  export const HYBRID_SKILLS_BY_ROLE = {
39
- planner: ["loop-agent"],
40
- scout: ["ai-engineering-context", "codebase-scout"],
41
- implementer: ["test-driven-development", "verification-before-completion"],
42
- reviewer: ["requesting-code-review", "code-review-core"],
43
- verifier: ["verification-before-completion", "systematic-debugging"],
44
- closeout: ["loop-agent", "verification-before-completion"],
39
+ planner: ["builtin:loop-agent"],
40
+ scout: ["builtin:ai-engineering-context", "builtin:codebase-scout"],
41
+ implementer: ["builtin:test-driven-development", "builtin:verification-before-completion"],
42
+ reviewer: ["builtin:requesting-code-review", "builtin:code-review-core"],
43
+ verifier: ["builtin:verification-before-completion", "builtin:systematic-debugging"],
44
+ closeout: ["builtin:loop-agent", "builtin:verification-before-completion"],
45
45
  };
46
46
  export const FRONTEND_DEFAULTS = {
47
47
  ...HYBRID_DEFAULTS,
@@ -56,13 +56,13 @@ export const FRONTEND_SKILLS_BY_ROLE = {
56
56
  verifier: [],
57
57
  closeout: [],
58
58
  };
59
- export const FRONTEND_CONTRACT_SKILLS = ["frontend-contract"];
60
- export const FRONTEND_SCOUT_SKILLS = ["frontend-scout"];
61
- export const FRONTEND_PLAN_SKILLS = ["frontend-plan"];
62
- export const FRONTEND_BOUNDED_IMPLEMENT_SKILLS = ["frontend-bounded-implement"];
63
- export const FRONTEND_DESIGN_REVIEW_SKILLS = ["frontend-design-review"];
64
- export const FRONTEND_REVIEW_SKILLS = ["frontend-review"];
65
- export const FRONTEND_VERIFICATION_SKILLS = ["frontend-verification"];
59
+ export const FRONTEND_CONTRACT_SKILLS = ["builtin:frontend-contract"];
60
+ export const FRONTEND_SCOUT_SKILLS = ["builtin:frontend-scout"];
61
+ export const FRONTEND_PLAN_SKILLS = ["builtin:frontend-plan"];
62
+ export const FRONTEND_BOUNDED_IMPLEMENT_SKILLS = ["builtin:frontend-bounded-implement"];
63
+ export const FRONTEND_DESIGN_REVIEW_SKILLS = ["builtin:frontend-design-review"];
64
+ export const FRONTEND_REVIEW_SKILLS = ["builtin:frontend-review"];
65
+ export const FRONTEND_VERIFICATION_SKILLS = ["builtin:frontend-verification"];
66
66
  export const IMPLEMENT_WRITESET_PLACEHOLDER = "REPLACE/WITH/NARROW/IMPLEMENT/PATHS/**";
67
67
  export const GENERATED_DAG_RUNTIME_CONTRACT = {
68
68
  schemaVersion: DAG_RUNTIME_CONTRACT_SCHEMA_VERSION,
@@ -1787,12 +1787,12 @@ export const BACKEND_TEST_DEFAULTS = {
1787
1787
  writePolicy: "read-only",
1788
1788
  };
1789
1789
  export const BACKEND_TEST_SKILLS_BY_ROLE = {
1790
- planner: ["loop-agent"],
1790
+ planner: ["builtin:loop-agent"],
1791
1791
  scout: [],
1792
- implementer: ["test-driven-development", "verification-before-completion"],
1793
- reviewer: ["requesting-code-review", "code-review-core"],
1794
- verifier: ["verification-before-completion", "systematic-debugging"],
1795
- closeout: ["loop-agent", "verification-before-completion"],
1792
+ implementer: ["builtin:test-driven-development", "builtin:verification-before-completion"],
1793
+ reviewer: ["builtin:requesting-code-review", "builtin:code-review-core"],
1794
+ verifier: ["builtin:verification-before-completion", "builtin:systematic-debugging"],
1795
+ closeout: ["builtin:loop-agent", "builtin:verification-before-completion"],
1796
1796
  };
1797
1797
  export function applyBackendTestWorkspaceControl(spec, control, layoutTestRoot, controlExplicit = false) {
1798
1798
  // D4: the canonical `.harness/testcase` root is Git-ignored, so a Git-status
@@ -640,11 +640,11 @@ export function buildFrontendTestHybridDag(sources) {
640
640
  },
641
641
  skillsByRole: {
642
642
  planner: [],
643
- scout: ["playwright-cli"],
644
- implementer: ["playwright-cli"],
643
+ scout: ["builtin:playwright-cli"],
644
+ implementer: ["builtin:playwright-cli"],
645
645
  reviewer: [],
646
- verifier: ["playwright-cli"],
647
- closeout: ["verification-before-completion"],
646
+ verifier: ["builtin:playwright-cli"],
647
+ closeout: ["builtin:verification-before-completion"],
648
648
  },
649
649
  executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
650
650
  verifyStrategy: resolveDagVerifyStrategy(sources.taskConfig),
@@ -8,12 +8,12 @@ export const KG_BOOTSTRAP_DEFAULTS = {
8
8
  writePolicy: "read-only",
9
9
  };
10
10
  export const KG_BOOTSTRAP_SKILLS_BY_ROLE = {
11
- planner: ["loop-agent"],
12
- scout: ["codebase-scout"],
13
- implementer: ["verification-before-completion"],
14
- reviewer: ["requesting-code-review"],
15
- verifier: ["verification-before-completion", "systematic-debugging"],
16
- closeout: ["loop-agent", "verification-before-completion"],
11
+ planner: ["builtin:loop-agent"],
12
+ scout: ["builtin:codebase-scout"],
13
+ implementer: ["builtin:verification-before-completion"],
14
+ reviewer: ["builtin:requesting-code-review"],
15
+ verifier: ["builtin:verification-before-completion", "builtin:systematic-debugging"],
16
+ closeout: ["builtin:loop-agent", "builtin:verification-before-completion"],
17
17
  };
18
18
  export function buildKgBootstrapInlineNodeScript(lines) {
19
19
  return ["node", "-e", JSON.stringify(lines.join(""))].join(" ");
@@ -7,12 +7,12 @@ export const KNOWLEDGE_SYNC_DEFAULTS = {
7
7
  writePolicy: "read-only",
8
8
  };
9
9
  export const KNOWLEDGE_SYNC_SKILLS_BY_ROLE = {
10
- planner: ["loop-agent"],
10
+ planner: ["builtin:loop-agent"],
11
11
  scout: [],
12
- implementer: ["verification-before-completion"],
13
- reviewer: ["requesting-code-review"],
14
- verifier: ["verification-before-completion", "systematic-debugging"],
15
- closeout: ["loop-agent", "verification-before-completion"],
12
+ implementer: ["builtin:verification-before-completion"],
13
+ reviewer: ["builtin:requesting-code-review"],
14
+ verifier: ["builtin:verification-before-completion", "builtin:systematic-debugging"],
15
+ closeout: ["builtin:loop-agent", "builtin:verification-before-completion"],
16
16
  };
17
17
  export function buildMultiPerspectiveReviewAggregateScript(fromNodeIds, label) {
18
18
  if (fromNodeIds.length === 0) {
@@ -3,9 +3,20 @@ import { access, readFile } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { builtinSkillReferenceResourceId, builtinSkillResourceId, createControllerContextProvider, } from "../../governance/resource-catalog.js";
6
7
  export const DEFAULT_SKILL_INSTRUCTION_MAX_CHARS = 3_000;
7
8
  export const DEFAULT_SKILL_INSTRUCTION_TOTAL_MAX_CHARS = 12_000;
8
9
  const SKILL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
10
+ const SKILL_REFERENCE_PATTERN = /^(?:(builtin|project|user):)?([A-Za-z0-9][A-Za-z0-9_-]*)$/;
11
+ function parseSkillReference(reference) {
12
+ const match = SKILL_REFERENCE_PATTERN.exec(reference);
13
+ if (!match)
14
+ return undefined;
15
+ return {
16
+ ...(match[1] ? { source: match[1] } : {}),
17
+ name: match[2],
18
+ };
19
+ }
9
20
  async function fileExists(filePath) {
10
21
  try {
11
22
  await access(filePath);
@@ -35,22 +46,26 @@ function bundledSkillCandidatePaths(name) {
35
46
  }
36
47
  function buildSkillCandidatePaths(input) {
37
48
  const candidates = [];
38
- const agentDir = input.env.PI_CODING_AGENT_DIR;
39
- if (agentDir) {
40
- candidates.push(normalizeCandidate(path.join(agentDir, "skills", input.name, "SKILL.md")), normalizeCandidate(path.join(agentDir, "skills", `${input.name}.md`)));
49
+ if (!input.source || input.source === "user") {
50
+ const agentDir = input.env.PI_CODING_AGENT_DIR;
51
+ if (agentDir) {
52
+ candidates.push(normalizeCandidate(path.join(agentDir, "skills", input.name, "SKILL.md")), normalizeCandidate(path.join(agentDir, "skills", `${input.name}.md`)));
53
+ }
54
+ const piSkillsDir = path.join(input.homeDir, ".pi", "agent", "skills");
55
+ candidates.push(normalizeCandidate(path.join(piSkillsDir, input.name, "SKILL.md")), normalizeCandidate(path.join(piSkillsDir, `${input.name}.md`)));
41
56
  }
42
- const piSkillsDir = path.join(input.homeDir, ".pi", "agent", "skills");
43
- candidates.push(normalizeCandidate(path.join(piSkillsDir, input.name, "SKILL.md")), normalizeCandidate(path.join(piSkillsDir, `${input.name}.md`)));
44
- // Canonical target-project repo-local skill path.
45
- candidates.push(normalizeCandidate(path.join(input.cwd, ".agents", "skills", input.name, "SKILL.md")));
46
- // Source-repository compatibility: the loop-agent package itself still ships
47
- // bundled skills under `skills/`, and local source development may resolve them
48
- // before the package fallback is found.
49
- candidates.push(normalizeCandidate(path.join(input.cwd, "skills", input.name, "SKILL.md")));
50
- if (input.name === "loop-agent") {
51
- candidates.push(normalizeCandidate(path.join(input.cwd, "skill", "SKILL.md")));
57
+ if (!input.source || input.source === "project") {
58
+ // Canonical target-project repo-local skill path.
59
+ candidates.push(normalizeCandidate(path.join(input.cwd, ".agents", "skills", input.name, "SKILL.md")));
60
+ // Legacy project-local Skill path. Explicit builtin references never consult it.
61
+ candidates.push(normalizeCandidate(path.join(input.cwd, "skills", input.name, "SKILL.md")));
62
+ if (input.name === "loop-agent") {
63
+ candidates.push(normalizeCandidate(path.join(input.cwd, "skill", "SKILL.md")));
64
+ }
65
+ }
66
+ if (!input.source || input.source === "builtin") {
67
+ candidates.push(...bundledSkillCandidatePaths(input.name));
52
68
  }
53
- candidates.push(...bundledSkillCandidatePaths(input.name));
54
69
  return Array.from(new Set(candidates));
55
70
  }
56
71
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
@@ -207,7 +222,7 @@ async function resolveSkillReferences(input) {
207
222
  });
208
223
  continue;
209
224
  }
210
- if (!(await fileExists(resolvedPath))) {
225
+ if (!input.packageSkill && !(await fileExists(resolvedPath))) {
211
226
  unresolvedReferences.push({
212
227
  name,
213
228
  path: refPath,
@@ -216,7 +231,13 @@ async function resolveSkillReferences(input) {
216
231
  continue;
217
232
  }
218
233
  try {
219
- const content = await readFile(resolvedPath, "utf-8");
234
+ const packageResource = input.packageSkill
235
+ ? await input.packageSkill.provider.readText(builtinSkillReferenceResourceId(input.packageSkill.name, refPath))
236
+ : undefined;
237
+ const content = packageResource?.text ?? await readFile(resolvedPath, "utf-8");
238
+ if (packageResource && path.resolve(packageResource.absolutePath) !== resolvedPath) {
239
+ throw new Error(`catalog path mismatch for ${refPath}`);
240
+ }
220
241
  const contentSha256 = sha256(content);
221
242
  const bytes = Buffer.byteLength(content, "utf-8");
222
243
  const budget = Math.max(0, Math.min(declaration.maxChars ?? input.perReferenceMaxChars, remainingTotal));
@@ -224,7 +245,7 @@ async function resolveSkillReferences(input) {
224
245
  input.resolvedReferencePaths.add(resolvedPath);
225
246
  references.push({
226
247
  name,
227
- path: resolvedPath,
248
+ path: packageResource?.id ?? resolvedPath,
228
249
  sha256: contentSha256,
229
250
  bytes,
230
251
  ...(truncated ? { truncated: true } : {}),
@@ -232,7 +253,7 @@ async function resolveSkillReferences(input) {
232
253
  remainingTotal = Math.max(0, remainingTotal - Math.min(content.length, budget));
233
254
  sections.push([
234
255
  `Reference: ${name}`,
235
- `Path: ${resolvedPath}`,
256
+ `${packageResource ? "Resource" : "Path"}: ${packageResource?.id ?? resolvedPath}`,
236
257
  `Sha256: ${contentSha256}`,
237
258
  excerpt,
238
259
  ].join("\n"));
@@ -263,7 +284,7 @@ function buildResolvedPromptText(input) {
263
284
  const lines = [
264
285
  `Skill: ${input.name}`,
265
286
  "Resolution: resolved",
266
- `Path: ${input.filePath}`,
287
+ `${input.locationKind}: ${input.location}`,
267
288
  `Sha256: ${input.contentSha256}`,
268
289
  "Injection mode: bounded-inline",
269
290
  "Instructions:",
@@ -308,29 +329,62 @@ export async function resolveDagSkillInstructions(skills, options) {
308
329
  let remainingTotal = options.totalMaxChars ?? DEFAULT_SKILL_INSTRUCTION_TOTAL_MAX_CHARS;
309
330
  const resolved = [];
310
331
  const resolvedReferencePaths = new Set();
332
+ let packageProvider;
311
333
  for (const name of skills) {
312
- if (!SKILL_NAME_PATTERN.test(name)) {
334
+ const reference = parseSkillReference(name);
335
+ if (!reference || !SKILL_NAME_PATTERN.test(reference.name)) {
313
336
  const error = `invalid skill name: ${name}`;
314
337
  resolved.push({
315
338
  name,
316
339
  resolution: "error",
340
+ sourceKind: "auto",
317
341
  error,
318
342
  injectionMode: "name-only",
319
343
  promptText: buildErrorPromptText({ name, error }),
320
344
  });
321
345
  continue;
322
346
  }
323
- const searchedPaths = buildSkillCandidatePaths({
324
- name,
325
- cwd: options.cwd,
326
- env,
327
- homeDir,
328
- });
329
- const filePath = await findFirstExistingFile(searchedPaths);
347
+ let packageContent;
348
+ let filePath;
349
+ let searchedPaths = [];
350
+ if (reference.source === "builtin") {
351
+ const logicalResourceId = builtinSkillResourceId(reference.name);
352
+ try {
353
+ packageProvider ??= await createControllerContextProvider();
354
+ const resource = await packageProvider.readText(logicalResourceId);
355
+ filePath = resource.absolutePath;
356
+ packageContent = resource.text;
357
+ searchedPaths = [resource.absolutePath];
358
+ }
359
+ catch (error) {
360
+ const message = error instanceof Error ? error.message : String(error);
361
+ resolved.push({
362
+ name,
363
+ resolution: "error",
364
+ sourceKind: "builtin",
365
+ logicalResourceId,
366
+ error: message,
367
+ injectionMode: "name-only",
368
+ promptText: buildErrorPromptText({ name, error: message }),
369
+ });
370
+ continue;
371
+ }
372
+ }
373
+ else {
374
+ searchedPaths = buildSkillCandidatePaths({
375
+ name: reference.name,
376
+ ...(reference.source ? { source: reference.source } : {}),
377
+ cwd: options.cwd,
378
+ env,
379
+ homeDir,
380
+ });
381
+ filePath = await findFirstExistingFile(searchedPaths);
382
+ }
330
383
  if (!filePath) {
331
384
  resolved.push({
332
385
  name,
333
386
  resolution: "missing",
387
+ sourceKind: reference.source ?? "auto",
334
388
  searchedPaths,
335
389
  injectionMode: "name-only",
336
390
  promptText: buildMissingPromptText({ name, searchedPaths }),
@@ -338,7 +392,7 @@ export async function resolveDagSkillInstructions(skills, options) {
338
392
  continue;
339
393
  }
340
394
  try {
341
- const content = await readFile(filePath, "utf-8");
395
+ const content = packageContent ?? await readFile(filePath, "utf-8");
342
396
  const { frontmatter, body } = splitSkillFrontmatter(content);
343
397
  const instructionContent = frontmatter ? body : content;
344
398
  const referenceDeclarations = frontmatter
@@ -354,11 +408,17 @@ export async function resolveDagSkillInstructions(skills, options) {
354
408
  remainingTotal,
355
409
  perReferenceMaxChars: perSkillMaxChars,
356
410
  resolvedReferencePaths,
411
+ ...(reference.source === "builtin" && packageProvider
412
+ ? { packageSkill: { name: reference.name, provider: packageProvider } }
413
+ : {}),
357
414
  });
358
415
  remainingTotal = afterReferences;
359
416
  const { promptText, truncated } = buildResolvedPromptText({
360
417
  name,
361
- filePath,
418
+ location: reference.source === "builtin"
419
+ ? builtinSkillResourceId(reference.name)
420
+ : filePath,
421
+ locationKind: reference.source === "builtin" ? "Resource" : "Path",
362
422
  content: instructionContent,
363
423
  contentSha256,
364
424
  bytes,
@@ -368,7 +428,13 @@ export async function resolveDagSkillInstructions(skills, options) {
368
428
  resolved.push({
369
429
  name,
370
430
  resolution: "resolved",
371
- path: filePath,
431
+ sourceKind: reference.source ?? "auto",
432
+ ...(reference.source === "builtin" && packageProvider
433
+ ? {
434
+ logicalResourceId: builtinSkillResourceId(reference.name),
435
+ catalogDigest: packageProvider.identity.catalogDigest,
436
+ }
437
+ : { path: filePath }),
372
438
  sha256: contentSha256,
373
439
  bytes,
374
440
  truncated,
@@ -385,14 +451,18 @@ export async function resolveDagSkillInstructions(skills, options) {
385
451
  resolved.push({
386
452
  name,
387
453
  resolution: "error",
388
- path: filePath,
454
+ sourceKind: reference.source ?? "auto",
455
+ ...(reference.source === "builtin"
456
+ ? { logicalResourceId: builtinSkillResourceId(reference.name) }
457
+ : { path: filePath }),
389
458
  error: message,
390
459
  injectionMode: "name-only",
391
460
  promptText: buildErrorPromptText({ name, error: message }),
392
461
  });
393
462
  }
394
463
  }
395
- if (options.includeLearnedPatterns && skills.includes("loop-agent")) {
464
+ if (options.includeLearnedPatterns
465
+ && skills.some((skill) => skill === "loop-agent" || skill === "builtin:loop-agent")) {
396
466
  const learned = await resolveLearnedPatternInstructions({
397
467
  cwd: options.cwd,
398
468
  maxChars: Math.max(0, Math.min(perSkillMaxChars, remainingTotal)),