@tencent-rtc/trtc-agent-skills 0.1.3 → 0.1.4

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 (360) hide show
  1. package/.cursor/rules/main.mdc +12 -0
  2. package/AGENTS.md +14 -104
  3. package/CLAUDE.md +15 -127
  4. package/CODEBUDDY.md +32 -118
  5. package/README.md +7 -5
  6. package/README.zh.md +7 -5
  7. package/bin/cli.js +133 -41
  8. package/hooks/__pycache__/cursor-adapter.cpython-313.pyc +0 -0
  9. package/hooks/cursor-adapter.py +45 -18
  10. package/hooks/hooks-cursor.json +5 -18
  11. package/hooks/hooks.json +6 -31
  12. package/knowledge-base/conference/web/index.yaml +143 -0
  13. package/knowledge-base/platform-slice-template.md +1041 -133
  14. package/knowledge-base/products.yaml +76 -0
  15. package/knowledge-base/scenario-spec.md +1316 -115
  16. package/knowledge-base/scenarios/conference/base/general-conference.md +22 -2
  17. package/knowledge-base/scenarios/conference/base/webinar-conference.md +14 -0
  18. package/knowledge-base/scenarios/conference/medical/1v1-video-consultation.md +19 -0
  19. package/knowledge-base/scenarios/conference/medical/medical-multidoctor-consultation.md +13 -0
  20. package/knowledge-base/scenarios/live/entertainment-live-room.md +12 -0
  21. package/knowledge-base/slice-spec.md +2377 -299
  22. package/knowledge-base/slices/conference/web/integration-audit.md +1 -1
  23. package/knowledge-base/slices/conference/web/login-auth.md +14 -1
  24. package/knowledge-base/tooling/aliases.yaml +92 -0
  25. package/knowledge-base/tooling/intent-signals.yaml +181 -0
  26. package/knowledge-base/tooling/symptom-keywords.yaml +21 -0
  27. package/package.json +1 -1
  28. package/skills/trtc/SKILL.md +202 -245
  29. package/skills/trtc/hooks/__pycache__/report_prompt.cpython-313.pyc +0 -0
  30. package/skills/{trtc-topic/guardrails → trtc/hooks}/gate_slice_read.py +12 -8
  31. package/skills/{trtc-topic/guardrails → trtc/hooks}/gate_slice_write.py +16 -12
  32. package/skills/{trtc-topic/guardrails → trtc/hooks}/stop_require_apply_evidence.py +22 -14
  33. package/skills/trtc/hooks/topic_phase_gate.py +161 -0
  34. package/skills/{trtc-topic → trtc}/runtime/README.md +2 -2
  35. package/skills/{trtc-onboarding/reference/reporting-protocol.md → trtc/runtime/REPORTING.md} +5 -5
  36. package/skills/{trtc-topic → trtc}/runtime/RUNTIME.md +5 -5
  37. package/skills/trtc/runtime/lib/__init__.py +0 -0
  38. package/skills/{trtc-topic → trtc}/runtime/package-lock.json +2 -2
  39. package/skills/{trtc-topic → trtc}/runtime/package.json +2 -2
  40. package/skills/{trtc-topic → trtc}/runtime/telemetry-bridge.mjs +1 -1
  41. package/skills/{trtc-topic/scripts → trtc/tools}/STATE-MACHINE-GUIDE.md +23 -23
  42. package/skills/trtc/tools/__init__.py +2 -0
  43. package/skills/trtc/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  44. package/skills/trtc/tools/__pycache__/query_classifier.cpython-313.pyc +0 -0
  45. package/skills/trtc/tools/__pycache__/reporting.cpython-313.pyc +0 -0
  46. package/skills/trtc/tools/__pycache__/search.cpython-313.pyc +0 -0
  47. package/skills/trtc/tools/__pycache__/session.cpython-313.pyc +0 -0
  48. package/skills/trtc/tools/apply.py +540 -0
  49. package/skills/trtc/tools/docs.py +712 -0
  50. package/skills/trtc/tools/docsbot.py +182 -0
  51. package/skills/trtc/tools/entry/render_ai_instructions.py +92 -0
  52. package/skills/trtc/tools/flow.py +1089 -0
  53. package/skills/{trtc-topic/scripts → trtc/tools}/init_slice_queue.py +5 -4
  54. package/skills/{trtc-topic/scripts → trtc/tools}/next_slice.py +5 -4
  55. package/skills/trtc/tools/query_classifier.py +301 -0
  56. package/skills/trtc/tools/reporting.py +447 -0
  57. package/skills/trtc/tools/search.py +817 -0
  58. package/skills/trtc/tools/session.py +1261 -0
  59. package/skills/trtc/tools/state_machine.py +690 -0
  60. package/skills/trtc-ai-service/README.md +195 -0
  61. package/skills/trtc-ai-service/README.zh-CN.md +193 -0
  62. package/skills/trtc-ai-service/SKILL.md +945 -0
  63. package/skills/trtc-ai-service/auto_adapters/README.md +40 -0
  64. package/skills/trtc-ai-service/auto_adapters/frontend-spa/README.md +27 -0
  65. package/skills/trtc-ai-service/auto_adapters/frontend-spa/angular/voice-agent.component.ts.tpl +131 -0
  66. package/skills/trtc-ai-service/auto_adapters/frontend-spa/manifest.yaml +57 -0
  67. package/skills/trtc-ai-service/auto_adapters/frontend-spa/react/VoiceAgent.tsx.tpl +142 -0
  68. package/skills/trtc-ai-service/auto_adapters/frontend-spa/vue/VoiceAgent.vue.tpl +121 -0
  69. package/skills/trtc-ai-service/auto_adapters/integration_templates/generic-backend.md +45 -0
  70. package/skills/trtc-ai-service/auto_adapters/integration_templates/generic-frontend.md +51 -0
  71. package/skills/trtc-ai-service/auto_adapters/integration_templates/generic-rest-api.md +93 -0
  72. package/skills/trtc-ai-service/auto_adapters/java-backend/README.md +25 -0
  73. package/skills/trtc-ai-service/auto_adapters/java-backend/manifest.yaml +30 -0
  74. package/skills/trtc-ai-service/auto_adapters/java-backend/quarkus/VoiceAgentFilter.java.tpl +64 -0
  75. package/skills/trtc-ai-service/auto_adapters/java-backend/springboot/VoiceAgentFilter.java.tpl +91 -0
  76. package/skills/trtc-ai-service/auto_adapters/manifest.yaml +43 -0
  77. package/skills/trtc-ai-service/auto_adapters/node-backend/README.md +25 -0
  78. package/skills/trtc-ai-service/auto_adapters/node-backend/express.js.tpl +40 -0
  79. package/skills/trtc-ai-service/auto_adapters/node-backend/fastify.js.tpl +27 -0
  80. package/skills/trtc-ai-service/auto_adapters/node-backend/koa.js.tpl +31 -0
  81. package/skills/trtc-ai-service/auto_adapters/node-backend/manifest.yaml +47 -0
  82. package/skills/trtc-ai-service/auto_adapters/python-backend/README.md +22 -0
  83. package/skills/trtc-ai-service/auto_adapters/python-backend/django.py.tpl +32 -0
  84. package/skills/trtc-ai-service/auto_adapters/python-backend/fastapi.py.tpl +35 -0
  85. package/skills/trtc-ai-service/auto_adapters/python-backend/flask.py.tpl +31 -0
  86. package/skills/trtc-ai-service/auto_adapters/python-backend/manifest.yaml +45 -0
  87. package/skills/trtc-ai-service/capabilities/__init__.py +43 -0
  88. package/skills/trtc-ai-service/capabilities/conversation-core/.env.example +29 -0
  89. package/skills/trtc-ai-service/capabilities/conversation-core/INTEGRATION.md +134 -0
  90. package/skills/trtc-ai-service/capabilities/conversation-core/INTERFACE_ADAPT.md +111 -0
  91. package/skills/trtc-ai-service/capabilities/conversation-core/QUICK_START.md +62 -0
  92. package/skills/trtc-ai-service/capabilities/conversation-core/manifest.yaml +250 -0
  93. package/skills/trtc-ai-service/capabilities/conversation-core/requirements.txt +6 -0
  94. package/skills/trtc-ai-service/capabilities/conversation-core/src/__init__.py +10 -0
  95. package/skills/trtc-ai-service/capabilities/conversation-core/src/_capability_loader.py +218 -0
  96. package/skills/trtc-ai-service/capabilities/conversation-core/src/agent.py +231 -0
  97. package/skills/trtc-ai-service/capabilities/conversation-core/src/credentials.py +132 -0
  98. package/skills/trtc-ai-service/capabilities/conversation-core/src/health.py +355 -0
  99. package/skills/trtc-ai-service/capabilities/conversation-core/src/log_filter.py +76 -0
  100. package/skills/trtc-ai-service/capabilities/conversation-core/src/modality.py +109 -0
  101. package/skills/trtc-ai-service/capabilities/conversation-core/src/server.py +312 -0
  102. package/skills/trtc-ai-service/capabilities/conversation-core/src/trtc_client.py +315 -0
  103. package/skills/trtc-ai-service/capabilities/conversation-core/src/usersig.py +90 -0
  104. package/skills/trtc-ai-service/capabilities/conversation-core/tests/test_skeleton.py +216 -0
  105. package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/README.md +48 -0
  106. package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/app.js +415 -0
  107. package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/index.html +68 -0
  108. package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/styles.css +136 -0
  109. package/skills/trtc-ai-service/capabilities/digital-human/README.md +31 -0
  110. package/skills/trtc-ai-service/capabilities/digital-human/manifest.yaml +64 -0
  111. package/skills/trtc-ai-service/capabilities/digital-human/src/__init__.py +2 -0
  112. package/skills/trtc-ai-service/capabilities/digital-human/src/router.py +43 -0
  113. package/skills/trtc-ai-service/capabilities/human-handoff/INTERFACE_ADAPT.md +353 -0
  114. package/skills/trtc-ai-service/capabilities/human-handoff/README.md +44 -0
  115. package/skills/trtc-ai-service/capabilities/human-handoff/manifest.yaml +227 -0
  116. package/skills/trtc-ai-service/capabilities/human-handoff/src/__init__.py +2 -0
  117. package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/__init__.py +9 -0
  118. package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/default_rest.py +242 -0
  119. package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/factory.py +89 -0
  120. package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/local_queue.py +258 -0
  121. package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/mock.py +132 -0
  122. package/skills/trtc-ai-service/capabilities/human-handoff/src/core/__init__.py +25 -0
  123. package/skills/trtc-ai-service/capabilities/human-handoff/src/core/intent_detector.py +75 -0
  124. package/skills/trtc-ai-service/capabilities/human-handoff/src/core/models.py +163 -0
  125. package/skills/trtc-ai-service/capabilities/human-handoff/src/core/service.py +192 -0
  126. package/skills/trtc-ai-service/capabilities/human-handoff/src/feedback_store.py +54 -0
  127. package/skills/trtc-ai-service/capabilities/human-handoff/src/ports/__init__.py +4 -0
  128. package/skills/trtc-ai-service/capabilities/human-handoff/src/ports/handoff_client.py +86 -0
  129. package/skills/trtc-ai-service/capabilities/human-handoff/src/queue.py +62 -0
  130. package/skills/trtc-ai-service/capabilities/human-handoff/src/router.py +201 -0
  131. package/skills/trtc-ai-service/capabilities/human-handoff/src/summary_link.py +77 -0
  132. package/skills/trtc-ai-service/capabilities/human-handoff/src/trigger.py +25 -0
  133. package/skills/trtc-ai-service/capabilities/knowledge-base/INTERFACE_ADAPT.md +297 -0
  134. package/skills/trtc-ai-service/capabilities/knowledge-base/README.md +51 -0
  135. package/skills/trtc-ai-service/capabilities/knowledge-base/data/faq.json +20 -0
  136. package/skills/trtc-ai-service/capabilities/knowledge-base/manifest.yaml +211 -0
  137. package/skills/trtc-ai-service/capabilities/knowledge-base/src/__init__.py +8 -0
  138. package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/__init__.py +9 -0
  139. package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/default_rest.py +209 -0
  140. package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/factory.py +86 -0
  141. package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/local_json.py +172 -0
  142. package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/mock.py +91 -0
  143. package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/__init__.py +12 -0
  144. package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/models.py +77 -0
  145. package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/scoring.py +73 -0
  146. package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/service.py +78 -0
  147. package/skills/trtc-ai-service/capabilities/knowledge-base/src/ports/__init__.py +4 -0
  148. package/skills/trtc-ai-service/capabilities/knowledge-base/src/ports/kb_client.py +61 -0
  149. package/skills/trtc-ai-service/capabilities/knowledge-base/src/retriever.py +56 -0
  150. package/skills/trtc-ai-service/capabilities/knowledge-base/src/router.py +85 -0
  151. package/skills/trtc-ai-service/capabilities/session-summary/INTERFACE_ADAPT.md +99 -0
  152. package/skills/trtc-ai-service/capabilities/session-summary/README.md +47 -0
  153. package/skills/trtc-ai-service/capabilities/session-summary/data/test_session.json +18 -0
  154. package/skills/trtc-ai-service/capabilities/session-summary/manifest.yaml +165 -0
  155. package/skills/trtc-ai-service/capabilities/session-summary/src/__init__.py +2 -0
  156. package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/__init__.py +5 -0
  157. package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/base.py +31 -0
  158. package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/default_rest.py +67 -0
  159. package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/factory.py +51 -0
  160. package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/local_json.py +42 -0
  161. package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/mock.py +22 -0
  162. package/skills/trtc-ai-service/capabilities/session-summary/src/recorder.py +210 -0
  163. package/skills/trtc-ai-service/capabilities/session-summary/src/router.py +93 -0
  164. package/skills/trtc-ai-service/capabilities/session-summary/src/summarizer.py +163 -0
  165. package/skills/trtc-ai-service/capabilities/tool-calling/INTERFACE_ADAPT.md +158 -0
  166. package/skills/trtc-ai-service/capabilities/tool-calling/README.md +50 -0
  167. package/skills/trtc-ai-service/capabilities/tool-calling/data/tools.yaml +58 -0
  168. package/skills/trtc-ai-service/capabilities/tool-calling/examples/__init__.py +1 -0
  169. package/skills/trtc-ai-service/capabilities/tool-calling/examples/local_tools.py +101 -0
  170. package/skills/trtc-ai-service/capabilities/tool-calling/manifest.yaml +146 -0
  171. package/skills/trtc-ai-service/capabilities/tool-calling/src/__init__.py +8 -0
  172. package/skills/trtc-ai-service/capabilities/tool-calling/src/dispatcher.py +54 -0
  173. package/skills/trtc-ai-service/capabilities/tool-calling/src/registry.py +219 -0
  174. package/skills/trtc-ai-service/capabilities/tool-calling/src/router.py +50 -0
  175. package/skills/trtc-ai-service/references/business-contract-spec.md +263 -0
  176. package/skills/trtc-ai-service/scenarios/custom-builder/README.md +86 -0
  177. package/skills/trtc-ai-service/scenarios/custom-builder/output-templates/recipe.yaml.j2 +194 -0
  178. package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q1-business-scenario.md +43 -0
  179. package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q2-io-modality.md +57 -0
  180. package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q3-ui-form.md +55 -0
  181. package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q4-capabilities.md +78 -0
  182. package/skills/trtc-ai-service/scenarios/customer-service/README.md +114 -0
  183. package/skills/trtc-ai-service/scenarios/customer-service/recipe.yaml +154 -0
  184. package/skills/trtc-ai-service/scenarios/customer-service/sample-data/README.md +32 -0
  185. package/skills/trtc-ai-service/scenarios/customer-service/sample-data/faq-sample.json +37 -0
  186. package/skills/trtc-ai-service/scenarios/customer-service/system-prompt.template.md +94 -0
  187. package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/app.js +347 -0
  188. package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/index.html +125 -0
  189. package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/styles.css +487 -0
  190. package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/tokens.css +71 -0
  191. package/skills/trtc-ai-service/scenarios/customer-service/ui/design-system/DESIGN_GUIDELINES.md +370 -0
  192. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/README.md +68 -0
  193. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/app.js +1307 -0
  194. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/data.js +40 -0
  195. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/index.html +233 -0
  196. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/mock-shop.json +21 -0
  197. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/styles.css +603 -0
  198. package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/tokens.css +71 -0
  199. package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/agent-link.js +323 -0
  200. package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/app.js +458 -0
  201. package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/index.html +109 -0
  202. package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/styles.css +489 -0
  203. package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/tokens.css +59 -0
  204. package/skills/trtc-ai-service/scripts/add-capability.py +364 -0
  205. package/skills/trtc-ai-service/scripts/contract-adapt.py +334 -0
  206. package/skills/trtc-ai-service/scripts/detect-stack.py +40 -0
  207. package/skills/trtc-ai-service/scripts/lib/__init__.py +20 -0
  208. package/skills/trtc-ai-service/scripts/lib/adapter_codegen.py +509 -0
  209. package/skills/trtc-ai-service/scripts/lib/arbitrator.py +152 -0
  210. package/skills/trtc-ai-service/scripts/lib/contract_resolver.py +519 -0
  211. package/skills/trtc-ai-service/scripts/lib/credential_validators.py +253 -0
  212. package/skills/trtc-ai-service/scripts/lib/curl_parser.py +303 -0
  213. package/skills/trtc-ai-service/scripts/lib/degrader.py +140 -0
  214. package/skills/trtc-ai-service/scripts/lib/injector.py +347 -0
  215. package/skills/trtc-ai-service/scripts/lib/manifest_resolver.py +288 -0
  216. package/skills/trtc-ai-service/scripts/lib/openapi_parser.py +289 -0
  217. package/skills/trtc-ai-service/scripts/lib/stack_detector.py +159 -0
  218. package/skills/trtc-ai-service/scripts/lib/tokens_compile.py +204 -0
  219. package/skills/trtc-ai-service/scripts/post-install-patch.py +225 -0
  220. package/skills/trtc-ai-service/scripts/setup-credentials.py +393 -0
  221. package/skills/trtc-ai-service/scripts/verify-credentials.py +108 -0
  222. package/skills/trtc-ai-service/start.sh +111 -0
  223. package/skills/trtc-ai-service/tests/__init__.py +1 -0
  224. package/skills/trtc-ai-service/tests/test_arbitrator.py +64 -0
  225. package/skills/trtc-ai-service/tests/test_capability_overlay.py +85 -0
  226. package/skills/trtc-ai-service/tests/test_contract_resolver.py +190 -0
  227. package/skills/trtc-ai-service/tests/test_handoff_ports.py +195 -0
  228. package/skills/trtc-ai-service/tests/test_kb_ports.py +195 -0
  229. package/skills/trtc-ai-service/tests/test_manifest_resolver.py +95 -0
  230. package/skills/trtc-ai-service/tests/test_recipe_assembly.py +175 -0
  231. package/skills/trtc-ai-service/tests/test_stack_and_degrader.py +101 -0
  232. package/skills/trtc-ai-service/tests/test_verify_credentials.py +285 -0
  233. package/skills/trtc-ai-service/triggers.yaml +29 -0
  234. package/skills/trtc-conference/SKILL.md +324 -0
  235. package/skills/trtc-conference/flows/onboarding.md +205 -0
  236. package/skills/trtc-conference/flows/topic.md +474 -0
  237. package/skills/trtc-conference/flows/troubleshoot.md +85 -0
  238. package/skills/trtc-conference/hooks/pretooluse_require_business_decisions.py +213 -0
  239. package/skills/trtc-conference/playbooks/medical-quickstart.md +84 -0
  240. package/skills/trtc-conference/playbooks/official-roomkit.md +97 -0
  241. package/skills/trtc-conference/references/local-usersig/basic-info-config.ts +39 -0
  242. package/skills/trtc-conference/references/usersig-handling.md +134 -0
  243. package/skills/trtc-conference/templates/medical-consultation/src/config/lib-generate-test-usersig-es.min.d.ts +4 -0
  244. package/skills/trtc-conference/templates/medical-consultation/src/config/lib-generate-test-usersig-es.min.js +2 -0
  245. package/skills/trtc-conference/tests/__pycache__/test_conference_onboarding_contract.cpython-313-pytest-9.0.2.pyc +0 -0
  246. package/skills/trtc-conference/tests/__pycache__/test_conference_topic_flow_contract.cpython-313-pytest-9.0.2.pyc +0 -0
  247. package/skills/trtc-conference/tests/test_conference_index_contract.py +43 -0
  248. package/skills/trtc-conference/tests/test_conference_onboarding_contract.py +103 -0
  249. package/skills/trtc-conference/tests/test_conference_template_contract.py +25 -0
  250. package/skills/trtc-conference/tests/test_conference_topic_flow_contract.py +132 -0
  251. package/skills/trtc-conference/tools/apply_checks.py +328 -0
  252. package/skills/trtc-conference/verify_lib/__init__.py +0 -0
  253. package/skills/{trtc-apply/guardrails/apply_lib → trtc-conference/verify_lib}/rule_parser.py +1 -1
  254. package/skills/trtc-docs/SKILL.md +91 -119
  255. package/.cursor/rules/ui-mode.mdc +0 -99
  256. package/ai-instructions/base.md +0 -13
  257. package/ai-instructions/ui-mode.md +0 -93
  258. package/knowledge-base/index.yaml +0 -462
  259. package/skills/trtc/room-builder/SKILL.md +0 -133
  260. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/README.md +0 -108
  261. package/skills/trtc/room-builder/tools/render_ai_instructions.py +0 -226
  262. package/skills/trtc-apply/SKILL.md +0 -97
  263. package/skills/trtc-onboarding/SKILL.md +0 -841
  264. package/skills/trtc-onboarding/reference/path-a1-demo.md +0 -103
  265. package/skills/trtc-onboarding/reference/path-a2-integrate.md +0 -737
  266. package/skills/trtc-onboarding/reference/path-b-troubleshoot.md +0 -186
  267. package/skills/trtc-onboarding/reference/path-c-expand.md +0 -43
  268. package/skills/trtc-onboarding/reference/supported-matrix.md +0 -100
  269. package/skills/trtc-search/SKILL.md +0 -228
  270. package/skills/trtc-topic/SKILL.md +0 -622
  271. package/skills/trtc-topic/scripts/apply.py +0 -581
  272. package/skills/trtc-topic/scripts/lib/state_machine.py +0 -328
  273. package/skills/trtc-topic/tests/README.md +0 -70
  274. package/skills/trtc-topic/tests/conftest.py +0 -72
  275. package/skills/trtc-topic/tests/test_apply_cli.py +0 -480
  276. package/skills/trtc-topic/tests/test_end_to_end.py +0 -305
  277. package/skills/trtc-topic/tests/test_finalize_session.py +0 -51
  278. package/skills/trtc-topic/tests/test_gates.py +0 -316
  279. package/skills/trtc-topic/tests/test_session_resolver.py +0 -260
  280. package/skills/trtc-topic/tests/test_state_machine.py +0 -414
  281. package/skills/trtc-topic/tests/test_stop_require_apply.py +0 -99
  282. package/skills/trtc-topic/tests/test_topic_skill_invariants.py +0 -130
  283. /package/skills/{trtc-topic → trtc}/runtime/lib/platforms.py +0 -0
  284. /package/skills/{trtc-topic → trtc}/runtime/telemetry_collector.py +0 -0
  285. /package/skills/{trtc-onboarding/reference → trtc/runtime}/usersig-handling.md +0 -0
  286. /package/skills/{trtc-topic/scripts → trtc/tools}/finalize_session.py +0 -0
  287. /package/skills/{trtc-apply/guardrails/apply_lib → trtc-ai-service/capabilities/conversation-core/tests}/__init__.py +0 -0
  288. /package/skills/{trtc-topic → trtc-conference}/references/execution-units.yaml +0 -0
  289. /package/skills/{trtc/room-builder/templates/scenarios/medical-consultation/src/config → trtc-conference/references/local-usersig}/lib-generate-test-usersig-es.min.d.ts +0 -0
  290. /package/skills/{trtc/room-builder/templates/scenarios/medical-consultation/src/config → trtc-conference/references/local-usersig}/lib-generate-test-usersig-es.min.js +0 -0
  291. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/docs/backend-contract.zh-CN.md +0 -0
  292. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/docs/integration.zh-CN.md +0 -0
  293. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/docs/theme.zh-CN.md +0 -0
  294. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/index.html +0 -0
  295. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/package.json +0 -0
  296. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/postcss.config.js +0 -0
  297. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/App.vue +0 -0
  298. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/ConsultationManagePanel.vue +0 -0
  299. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/LanguageSwitch.vue +0 -0
  300. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/LoadingSpinner.vue +0 -0
  301. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalAlert.vue +0 -0
  302. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalBusinessPanel.vue +0 -0
  303. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalButton.vue +0 -0
  304. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalConfirmDialog.vue +0 -0
  305. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalDataPanel.vue +0 -0
  306. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalRecordPanel.vue +0 -0
  307. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/PrescriptionPanel.vue +0 -0
  308. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/config/basic-info-config.ts +0 -0
  309. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/config/runtime-config.ts +0 -0
  310. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/env.d.ts +0 -0
  311. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationChatPanel.vue +0 -0
  312. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationMembersPanel.vue +0 -0
  313. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationTranscriptionPanel.vue +0 -0
  314. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationVideoStage.vue +0 -0
  315. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/InviteDoctorDialog.vue +0 -0
  316. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/KickMemberConfirmDialog.vue +0 -0
  317. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/types.ts +0 -0
  318. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationChat.ts +0 -0
  319. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationDevices.ts +0 -0
  320. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationParticipants.ts +0 -0
  321. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationPermissions.ts +0 -0
  322. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/utils.ts +0 -0
  323. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/en-US/index.ts +0 -0
  324. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/index.ts +0 -0
  325. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/medicalTranslate.ts +0 -0
  326. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/state.ts +0 -0
  327. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/zh-CN/index.ts +0 -0
  328. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/main.ts +0 -0
  329. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/mock/appointments.ts +0 -0
  330. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/mock/users.ts +0 -0
  331. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/router/index.ts +0 -0
  332. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/index.ts +0 -0
  333. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/appointmentService.ts +0 -0
  334. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/authService.ts +0 -0
  335. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/launchContext.ts +0 -0
  336. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/userService.ts +0 -0
  337. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/mock/appointmentService.ts +0 -0
  338. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/mock/authService.ts +0 -0
  339. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/mock/userService.ts +0 -0
  340. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/types.ts +0 -0
  341. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/shared/icons.ts +0 -0
  342. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/styles/index.css +0 -0
  343. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/styles/tailwind.css +0 -0
  344. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/styles/theme.css +0 -0
  345. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/auth.ts +0 -0
  346. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/format.ts +0 -0
  347. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/navigation.ts +0 -0
  348. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/session.ts +0 -0
  349. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/DoctorConsultationView.vue +0 -0
  350. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/DoctorDashboardView.vue +0 -0
  351. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/LoginView.vue +0 -0
  352. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientConsultationFinishedView.vue +0 -0
  353. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientConsultationView.vue +0 -0
  354. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientSelectDoctorView.vue +0 -0
  355. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientWaitingView.vue +0 -0
  356. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/tsconfig.json +0 -0
  357. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/tsconfig.node.json +0 -0
  358. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/vite.config.ts +0 -0
  359. /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation//346/216/245/345/205/245/350/257/264/346/230/216.md" +0 -0
  360. /package/skills/{trtc-topic/runtime/lib → trtc-conference/tools}/__init__.py +0 -0
@@ -0,0 +1,152 @@
1
+ """α/β dual-track tool-call arbitration.
2
+
3
+ | Track | Implementation | Triggered When |
4
+ |:-----:|:---------------|:-----------------------------------------|
5
+ | α | Local function | Default priority (low latency, zero network overhead) |
6
+ | β | Remote API | α unavailable (not registered / failed / timed out) |
7
+
8
+ Arbitration rules (manifest.tool_calling node):
9
+ priority: alpha | beta | manifest_order
10
+ timeout_ms_alpha / timeout_ms_beta
11
+ retry_alpha / retry_beta
12
+
13
+ Risk P1 mitigation: when both tracks exist, decide by the priority field; auto-fallback when α is unavailable.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import time
18
+ from dataclasses import dataclass, field
19
+ from typing import Any, Callable, Dict, List, Optional
20
+
21
+
22
+ @dataclass
23
+ class AlphaTool:
24
+ name: str
25
+ func: Callable[..., Any]
26
+ timeout_ms: int = 1000
27
+ description: str = ""
28
+
29
+
30
+ @dataclass
31
+ class BetaTool:
32
+ name: str
33
+ endpoint: str
34
+ method: str = "POST"
35
+ timeout_ms: int = 5000
36
+ headers: Dict[str, str] = field(default_factory=dict)
37
+ description: str = ""
38
+
39
+
40
+ @dataclass
41
+ class ToolCallResult:
42
+ tool: str
43
+ track: str # alpha | beta
44
+ ok: bool
45
+ output: Any = None
46
+ error: str = ""
47
+ latency_ms: int = 0
48
+ fallback_chain: List[str] = field(default_factory=list)
49
+
50
+
51
+ class ToolRegistry:
52
+ """同名工具可同时注册 α 与 β,由 ``call`` 仲裁。"""
53
+
54
+ def __init__(self, *, default_priority: str = "alpha") -> None:
55
+ if default_priority not in ("alpha", "beta", "manifest_order"):
56
+ raise ValueError(f"invalid priority: {default_priority}")
57
+ self._alpha: Dict[str, AlphaTool] = {}
58
+ self._beta: Dict[str, BetaTool] = {}
59
+ self._priority = default_priority
60
+
61
+ # 注册 -----------------------------------------------------------------
62
+ def register_alpha(self, tool: AlphaTool) -> None:
63
+ self._alpha[tool.name] = tool
64
+
65
+ def register_beta(self, tool: BetaTool) -> None:
66
+ self._beta[tool.name] = tool
67
+
68
+ def list_tools(self) -> List[Dict[str, Any]]:
69
+ names = sorted(set(self._alpha) | set(self._beta))
70
+ return [
71
+ {
72
+ "name": n,
73
+ "alpha": self._alpha[n].description if n in self._alpha else None,
74
+ "beta": self._beta[n].description if n in self._beta else None,
75
+ "priority": self._priority,
76
+ }
77
+ for n in names
78
+ ]
79
+
80
+ # 调用 -----------------------------------------------------------------
81
+ def call(
82
+ self,
83
+ name: str,
84
+ params: Optional[Dict[str, Any]] = None,
85
+ *,
86
+ priority: Optional[str] = None,
87
+ beta_invoker: Optional[Callable[[BetaTool, Dict[str, Any]], Any]] = None,
88
+ ) -> ToolCallResult:
89
+ """执行工具调用,按 priority 决策并自动降级。
90
+
91
+ beta_invoker 由调用方注入(避免本模块依赖 requests),
92
+ 默认只在 α 可用时跑 α,β 路径必须由调用方实现网络调用。
93
+ """
94
+ params = params or {}
95
+ priority = priority or self._priority
96
+ chain: List[str] = []
97
+ order = self._resolve_order(name, priority)
98
+ last_error = ""
99
+ for track in order:
100
+ chain.append(track)
101
+ if track == "alpha":
102
+ tool = self._alpha.get(name)
103
+ if not tool:
104
+ last_error = f"alpha not registered: {name}"
105
+ continue
106
+ start = time.perf_counter()
107
+ try:
108
+ out = tool.func(**params)
109
+ return ToolCallResult(
110
+ tool=name, track="alpha", ok=True, output=out,
111
+ latency_ms=int((time.perf_counter() - start) * 1000),
112
+ fallback_chain=chain,
113
+ )
114
+ except Exception as exc: # noqa: BLE001
115
+ last_error = f"alpha:{type(exc).__name__}:{exc}"
116
+ else: # beta
117
+ tool = self._beta.get(name)
118
+ if not tool:
119
+ last_error = f"beta not registered: {name}"
120
+ continue
121
+ if beta_invoker is None:
122
+ last_error = "beta_invoker not provided"
123
+ continue
124
+ start = time.perf_counter()
125
+ try:
126
+ out = beta_invoker(tool, params)
127
+ return ToolCallResult(
128
+ tool=name, track="beta", ok=True, output=out,
129
+ latency_ms=int((time.perf_counter() - start) * 1000),
130
+ fallback_chain=chain,
131
+ )
132
+ except Exception as exc: # noqa: BLE001
133
+ last_error = f"beta:{type(exc).__name__}:{exc}"
134
+ return ToolCallResult(
135
+ tool=name, track=chain[-1] if chain else "none",
136
+ ok=False, error=last_error or "no track available",
137
+ fallback_chain=chain,
138
+ )
139
+
140
+ # 仲裁 -----------------------------------------------------------------
141
+ def _resolve_order(self, name: str, priority: str) -> List[str]:
142
+ has_a = name in self._alpha
143
+ has_b = name in self._beta
144
+ if priority == "alpha":
145
+ base = ["alpha", "beta"]
146
+ elif priority == "beta":
147
+ base = ["beta", "alpha"]
148
+ else: # manifest_order:以注册顺序为准(首次注册者优先)
149
+ base = ["alpha", "beta"] if has_a and not has_b else (
150
+ ["beta", "alpha"] if has_b and not has_a else ["alpha", "beta"]
151
+ )
152
+ return [t for t in base if (t == "alpha" and has_a) or (t == "beta" and has_b)]
@@ -0,0 +1,519 @@
1
+ """business_contract field parsing & diff utilities (Phase 3 Stage 4).
2
+
3
+ Responsibilities:
4
+ 1. Load a capability's ``manifest.yaml.business_contract`` block and perform static
5
+ validation (BC001~BC006) per ``business-contract-spec.md`` §7 rules.
6
+ 2. Convert a manifest-declared ``external_apis[name]`` entry into ``ParsedApi`` form (default contract).
7
+ 3. ``diff_contracts(default, user)`` returns structured differences and infers
8
+ fallback level L1 / L2 / L3.
9
+
10
+ Fallback Level Determination:
11
+
12
+ | Level | Trigger Condition |
13
+ |---|---|
14
+ | L1 | Only field differences within the ``adapter_slots`` list (name remapping / enum value mapping) |
15
+ | L2 | Parsed successfully but structural differences exist (nested level differences, type differences, missing required fields) |
16
+ | L3 | Protocol-level differences (significant method mismatch, non-REST/JSON user API, body_format=raw, etc.) |
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import re
21
+ from dataclasses import dataclass, field
22
+ from enum import Enum
23
+ from pathlib import Path
24
+ from typing import Any, Dict, List, Optional, Tuple
25
+
26
+ import yaml
27
+
28
+ from .curl_parser import AuthSpec, ParsedApi, _normalize_schema
29
+
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Error codes / exceptions
33
+ # ---------------------------------------------------------------------------
34
+ class ContractError(RuntimeError):
35
+ """business_contract field error."""
36
+
37
+ def __init__(self, code: str, message: str) -> None:
38
+ super().__init__(f"[{code}] {message}")
39
+ self.code = code
40
+
41
+
42
+ class DegradeLevel(str, Enum):
43
+ L1 = "L1"
44
+ L2 = "L2"
45
+ L3 = "L3"
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Data models
50
+ # ---------------------------------------------------------------------------
51
+ @dataclass
52
+ class ExternalApi:
53
+ name: str
54
+ direction: str
55
+ method: str
56
+ path: str
57
+ description: str = ""
58
+ request_schema: Dict[str, Any] = field(default_factory=dict)
59
+ response_schema: Dict[str, Any] = field(default_factory=dict)
60
+ adapter_slots: List[str] = field(default_factory=list)
61
+ auth: AuthSpec = field(default_factory=AuthSpec)
62
+ timeout_ms: int = 5000
63
+
64
+ def to_parsed_api(self, base_url: str = "") -> ParsedApi:
65
+ # manifest.yaml field values are already normalized literals ("string" / "int" / "enum[...]" / "string[]"),
66
+ # so do **not** call _normalize_schema (avoids degrading "string[]" to "string")
67
+ return ParsedApi(
68
+ method=self.method.upper(),
69
+ base_url=base_url,
70
+ path=self.path,
71
+ headers={},
72
+ auth=self.auth,
73
+ request_schema=dict(self.request_schema),
74
+ response_schema=dict(self.response_schema),
75
+ body_format="json" if self.method.upper() in {"POST", "PUT", "PATCH"} else "none",
76
+ source="manifest",
77
+ )
78
+
79
+
80
+ @dataclass
81
+ class BusinessContract:
82
+ capability: str
83
+ port_class: str = ""
84
+ default_adapter: str = ""
85
+ mock_adapter: str = ""
86
+ customization_sop: str = "INTERFACE_ADAPT.md"
87
+ external_apis: List[ExternalApi] = field(default_factory=list)
88
+ raw: Dict[str, Any] = field(default_factory=dict)
89
+
90
+ def get_api(self, name: str) -> Optional[ExternalApi]:
91
+ for a in self.external_apis:
92
+ if a.name == name:
93
+ return a
94
+ return None
95
+
96
+ def outbound_apis(self) -> List[ExternalApi]:
97
+ return [a for a in self.external_apis if a.direction == "outbound"]
98
+
99
+
100
+ @dataclass
101
+ class FieldDiff:
102
+ """Single field diff."""
103
+
104
+ path: str # request.subject / response.ticket_id
105
+ kind: str # rename | type_mismatch | missing_in_user | extra_in_user | enum_mismatch
106
+ default: Any = None # default contract field name / type / enum set
107
+ user: Any = None # user's actual value
108
+ in_slot: bool = False # whether within adapter_slots (determines L1 vs L2)
109
+
110
+ def to_dict(self) -> Dict:
111
+ return {
112
+ "path": self.path,
113
+ "kind": self.kind,
114
+ "default": self.default,
115
+ "user": self.user,
116
+ "in_slot": self.in_slot,
117
+ }
118
+
119
+
120
+ @dataclass
121
+ class ContractDiff:
122
+ api_name: str
123
+ method_match: bool
124
+ path_match: bool
125
+ direction: str
126
+ fields: List[FieldDiff] = field(default_factory=list)
127
+ protocol_mismatch: bool = False
128
+ protocol_reason: str = ""
129
+ suggested_mapping: Dict[str, Any] = field(default_factory=dict)
130
+
131
+ @property
132
+ def level(self) -> DegradeLevel:
133
+ return decide_level(self)
134
+
135
+ def to_dict(self) -> Dict:
136
+ return {
137
+ "api_name": self.api_name,
138
+ "method_match": self.method_match,
139
+ "path_match": self.path_match,
140
+ "direction": self.direction,
141
+ "fields": [f.to_dict() for f in self.fields],
142
+ "protocol_mismatch": self.protocol_mismatch,
143
+ "protocol_reason": self.protocol_reason,
144
+ "suggested_mapping": self.suggested_mapping,
145
+ "level": self.level.value,
146
+ }
147
+
148
+
149
+ # ---------------------------------------------------------------------------
150
+ # Manifest loading & validation
151
+ # ---------------------------------------------------------------------------
152
+ def _import_dotted(path: str) -> bool:
153
+ """Attempt to import a dotted path; only validates syntax / module file existence, no instantiation."""
154
+ if not path:
155
+ return False
156
+ # Only validate form: a.b.c.ClassName
157
+ if not re.match(r"^[A-Za-z_][\w\.]*$", path):
158
+ return False
159
+ return True
160
+
161
+
162
+ def load_business_contract(capability_dir: Path) -> BusinessContract:
163
+ """Load a capability manifest.yaml and parse its business_contract block.
164
+
165
+ Raises
166
+ ------
167
+ ContractError
168
+ manifest missing / business_contract missing / invalid fields.
169
+ """
170
+ manifest_path = Path(capability_dir) / "manifest.yaml"
171
+ if not manifest_path.exists():
172
+ raise ContractError("BC000", f"manifest not found: {manifest_path}")
173
+ raw = yaml.safe_load(manifest_path.read_text(encoding="utf-8")) or {}
174
+ cap_name = str(raw.get("name", "") or capability_dir.name)
175
+ bc_raw = raw.get("business_contract")
176
+ if not isinstance(bc_raw, dict):
177
+ raise ContractError("BC000", f"{cap_name}: missing business_contract block")
178
+
179
+ # tool-calling uses §5 dedicated contract block; not yet supported by this tool
180
+ if "alpha_track" in bc_raw or "beta_track" in bc_raw:
181
+ raise ContractError(
182
+ "BC000",
183
+ f"{cap_name}: tool-calling-style contract not supported by contract-adapt yet",
184
+ )
185
+
186
+ port_class = str(bc_raw.get("port_class", ""))
187
+ default_adapter = str(bc_raw.get("default_adapter", ""))
188
+ mock_adapter = str(bc_raw.get("mock_adapter", ""))
189
+ sop = str(bc_raw.get("customization_sop", "INTERFACE_ADAPT.md"))
190
+
191
+ # BC001: all three dotted paths must be valid
192
+ for label, p in (("port_class", port_class), ("default_adapter", default_adapter), ("mock_adapter", mock_adapter)):
193
+ if not _import_dotted(p):
194
+ raise ContractError("BC001", f"{cap_name}: invalid {label}={p!r}")
195
+
196
+ apis: List[ExternalApi] = []
197
+ seen_names: set = set()
198
+ for entry in bc_raw.get("external_apis") or []:
199
+ if not isinstance(entry, dict):
200
+ continue
201
+ name = str(entry.get("name", "")).strip()
202
+ if not name:
203
+ raise ContractError("BC003", f"{cap_name}: external_api missing 'name'")
204
+ # BC002: duplicate name
205
+ if name in seen_names:
206
+ raise ContractError("BC002", f"{cap_name}: duplicate external_api name {name!r}")
207
+ seen_names.add(name)
208
+ direction = str(entry.get("direction", "outbound"))
209
+ method = str(entry.get("method", "")).upper()
210
+ path = str(entry.get("path", ""))
211
+ if direction == "outbound" and (not method or not path):
212
+ raise ContractError("BC003", f"{cap_name}.{name}: outbound requires method+path")
213
+
214
+ auth_raw = entry.get("auth") or {}
215
+ auth = AuthSpec(
216
+ type=str(auth_raw.get("type", "none")),
217
+ location=str(auth_raw.get("location", "header")),
218
+ name=str(auth_raw.get("name", "")),
219
+ )
220
+
221
+ api = ExternalApi(
222
+ name=name,
223
+ direction=direction,
224
+ method=method,
225
+ path=path,
226
+ description=str(entry.get("description", "")),
227
+ request_schema=dict(entry.get("request_schema") or {}),
228
+ response_schema=dict(entry.get("response_schema") or {}),
229
+ adapter_slots=list(entry.get("adapter_slots") or []),
230
+ auth=auth,
231
+ timeout_ms=int(entry.get("timeout_ms", 5000)),
232
+ )
233
+
234
+ # BC004: adapter_slots should be resolvable in schema (warning only, no exception)
235
+ # Simplified: only checks top-level fields
236
+ for slot in api.adapter_slots:
237
+ if not (slot.startswith("request.") or slot.startswith("response.")):
238
+ # 视为 warning,无副作用
239
+ pass
240
+
241
+ apis.append(api)
242
+
243
+ return BusinessContract(
244
+ capability=cap_name,
245
+ port_class=port_class,
246
+ default_adapter=default_adapter,
247
+ mock_adapter=mock_adapter,
248
+ customization_sop=sop,
249
+ external_apis=apis,
250
+ raw=bc_raw,
251
+ )
252
+
253
+
254
+ # ---------------------------------------------------------------------------
255
+ # Default contract vs user contract diff
256
+ # ---------------------------------------------------------------------------
257
+ def _walk_schema(schema: Any, prefix: str = "") -> Dict[str, Any]:
258
+ """Flatten arbitrary nested type descriptions to ``{ 'a.b.c': type_string }``."""
259
+ out: Dict[str, Any] = {}
260
+ if isinstance(schema, dict):
261
+ for k, v in schema.items():
262
+ sub = f"{prefix}.{k}" if prefix else k
263
+ if isinstance(v, (dict, list)):
264
+ out.update(_walk_schema(v, sub))
265
+ else:
266
+ out[sub] = v
267
+ el if isinstance(schema, list):
268
+ # Array: recurse into first element
269
+ if schema:
270
+ out.update(_walk_schema(schema[0], f"{prefix}[]"))
271
+ else:
272
+ out[prefix or "$"] = schema
273
+ return out
274
+
275
+
276
+ def _slot_match(field_path: str, slots: List[str]) -> bool:
277
+ """``request.priority`` ∈ slots (exact match, no wildcards)."""
278
+ return field_path in set(slots)
279
+
280
+
281
+ def _detect_protocol_mismatch(default: ExternalApi, user: ParsedApi) -> Tuple[bool, str]:
282
+ if default.method.upper() != user.method.upper():
283
+ return (True, f"method mismatch: default={default.method}, user={user.method}")
284
+ if user.body_format == "raw":
285
+ return (True, "user body is non-JSON raw payload")
286
+ # Path mismatch is not a protocol-level diff (adapter can override path), but record it
287
+ return (False, "")
288
+
289
+
290
+ def diff_contracts(default: ExternalApi, user: ParsedApi) -> ContractDiff:
291
+ """Compare default contract with user API; produce structured differences.
292
+
293
+ Notes
294
+ -----
295
+ - Request field comparison: uses default schema as baseline, checks whether user schema
296
+ provides same-named fields; if not same-named but user has extra fields, record as
297
+ ``rename`` candidate (paired only when default field is missing).
298
+ - Response fields same logic; if user did not provide response (response_schema is empty),
299
+ only generate ``missing_in_user`` but do not count as protocol mismatch.
300
+ - Type differences (``string`` vs ``int``) recorded as ``type_mismatch``.
301
+ - Enum differences not visible in curl input, only OpenAPI input; recorded as ``enum_mismatch``.
302
+ """
303
+ diff = ContractDiff(
304
+ api_name=default.name,
305
+ method_match=default.method.upper() == user.method.upper(),
306
+ path_match=default.path == user.path,
307
+ direction=default.direction,
308
+ )
309
+ pmm, reason = _detect_protocol_mismatch(default, user)
310
+ diff.protocol_mismatch = pmm
311
+ diff.protocol_reason = reason
312
+
313
+ default_req = _walk_schema(default.request_schema, "request")
314
+ user_req = _walk_schema(user.request_schema, "request")
315
+ default_resp = _walk_schema(default.response_schema, "response")
316
+ user_resp = _walk_schema(user.response_schema, "response") if user.response_schema else {}
317
+
318
+ suggested: Dict[str, Any] = {"request": {}, "response": {}, "enum_map": {}}
319
+
320
+ # Request comparison
321
+ diff.fields.extend(
322
+ _diff_section(default_req, user_req, default.adapter_slots, "request", suggested["request"])
323
+ )
324
+ # Response comparison (if user provided one)
325
+ if user_resp:
326
+ diff.fields.extend(
327
+ _diff_section(default_resp, user_resp, default.adapter_slots, "response", suggested["response"])
328
+ )
329
+ else:
330
+ for k, t in default_resp.items():
331
+ diff.fields.append(
332
+ FieldDiff(
333
+ path=k, kind="user_response_missing", default=t, user=None, in_slot=_slot_match(k, default.adapter_slots)
334
+ )
335
+ )
336
+
337
+ diff.suggested_mapping = {k: v for k, v in suggested.items() if v}
338
+ return diff
339
+
340
+
341
+ def _diff_section(
342
+ default_flat: Dict[str, Any],
343
+ user_flat: Dict[str, Any],
344
+ slots: List[str],
345
+ section: str,
346
+ mapping_out: Dict[str, str],
347
+ ) -> List[FieldDiff]:
348
+ out: List[FieldDiff] = []
349
+ default_keys = list(default_flat.keys())
350
+ user_keys = list(user_flat.keys())
351
+
352
+ # 1) 同名字段:核对类型
353
+ common = [k for k in default_keys if k in user_flat]
354
+ for k in common:
355
+ if not _types_compatible(default_flat[k], user_flat[k]):
356
+ out.append(
357
+ FieldDiff(
358
+ path=k,
359
+ kind="type_mismatch",
360
+ default=default_flat[k],
361
+ user=user_flat[k],
362
+ in_slot=_slot_match(k, slots),
363
+ )
364
+ )
365
+ # 同名同类型 → 不进 diff
366
+ mapping_out[k.split(".", 1)[1]] = k.split(".", 1)[1] # request.x → x:x
367
+
368
+ # 2) 默认有 / 用户没同名:尝试在用户 keys 里找同类型字段做 rename 候选
369
+ missing_default = [k for k in default_keys if k not in user_flat]
370
+ extra_user = [k for k in user_keys if k not in default_flat]
371
+ used_extra: set = set()
372
+ for dk in missing_default:
373
+ candidate = _find_rename_candidate(dk, default_flat[dk], extra_user, user_flat, used_extra)
374
+ if candidate:
375
+ used_extra.add(candidate)
376
+ out.append(
377
+ FieldDiff(
378
+ path=dk,
379
+ kind="rename",
380
+ default=dk.split(".", 1)[1],
381
+ user=candidate.split(".", 1)[1],
382
+ in_slot=_slot_match(dk, slots),
383
+ )
384
+ )
385
+ mapping_out[dk.split(".", 1)[1]] = candidate.split(".", 1)[1]
386
+ else:
387
+ out.append(
388
+ FieldDiff(
389
+ path=dk,
390
+ kind="missing_in_user",
391
+ default=default_flat[dk],
392
+ user=None,
393
+ in_slot=_slot_match(dk, slots),
394
+ )
395
+ )
396
+
397
+ # 3) 用户多出的字段(未被认领为 rename 目标)
398
+ for uk in extra_user:
399
+ if uk in used_extra:
400
+ continue
401
+ out.append(
402
+ FieldDiff(
403
+ path=uk,
404
+ kind="extra_in_user",
405
+ default=None,
406
+ user=user_flat[uk],
407
+ in_slot=False,
408
+ )
409
+ )
410
+
411
+ return out
412
+
413
+
414
+ # ---------------------------------------------------------------------------
415
+ # Type compatibility / rename candidates
416
+ # ---------------------------------------------------------------------------
417
+ def _types_compatible(a: Any, b: Any) -> bool:
418
+ if a == b:
419
+ return True
420
+ a_s = str(a)
421
+ b_s = str(b)
422
+ # enum[...] and string are treated as compatible (adapter layer handles enum mapping)
423
+ if a_s.startswith("enum[") and b_s == "string":
424
+ return True
425
+ if b_s.startswith("enum[") and a_s == "string":
426
+ return True
427
+ # int / float mutually compatible
428
+ if {a_s, b_s} <= {"int", "float", "number"}:
429
+ return True
430
+ # array type mismatch → incompatible
431
+ return False
432
+
433
+
434
+ def _find_rename_candidate(
435
+ default_path: str,
436
+ default_type: Any,
437
+ extra_user_keys: List[str],
438
+ user_flat: Dict[str, Any],
439
+ used: set,
440
+ ) -> Optional[str]:
441
+ """Try to find a type-compatible + semantically similar path-segment candidate among extra user keys."""
442
+ default_seg = default_path.split(".")[-1]
443
+ candidates = [k for k in extra_user_keys if k not in used and _types_compatible(default_type, user_flat[k])]
444
+ if not candidates:
445
+ return None
446
+ # Simple semantic similarity: contains / contained-by / same-category synonyms
447
+ synonyms = {
448
+ "user_id": ["customer_id", "uid", "user", "client_id"],
449
+ "subject": ["title", "summary", "topic"],
450
+ "description": ["body", "content", "message", "detail"],
451
+ "priority": ["level", "severity", "rank"],
452
+ "transcript": ["messages", "history", "log", "conversation"],
453
+ "ticket_id": ["id", "order_id", "case_id", "wo_id"],
454
+ "queue_position": ["rank", "position", "queue", "order"],
455
+ "eta_seconds": ["wait_estimate", "wait_seconds", "eta"],
456
+ "status": ["state", "stage", "phase"],
457
+ "agent_id": ["operator", "assignee", "owner"],
458
+ "query": ["q", "keyword", "search", "term"],
459
+ "top_k": ["k", "limit", "size"],
460
+ "min_score": ["threshold", "score_min"],
461
+ "answer": ["reply", "content"],
462
+ "question": ["query", "title"],
463
+ "keywords": ["tags", "labels"],
464
+ }
465
+ syn_for_default = set(synonyms.get(default_seg, []))
466
+ for cand in candidates:
467
+ cand_seg = cand.split(".")[-1]
468
+ if cand_seg == default_seg:
469
+ return cand
470
+ if cand_seg in syn_for_default or default_seg in synonyms.get(cand_seg, []):
471
+ return cand
472
+ # Fallback to first candidate when types match (best effort)
473
+ return candidates[0]
474
+
475
+
476
+ # ---------------------------------------------------------------------------
477
+ # Fallback level decision
478
+ # ---------------------------------------------------------------------------
479
+ def decide_level(diff: ContractDiff) -> DegradeLevel:
480
+ """L1 / L2 / L3 determination."""
481
+ if diff.protocol_mismatch:
482
+ return DegradeLevel.L3
483
+ # Any non-slot diff → L2
484
+ for f in diff.fields:
485
+ if f.kind in ("user_response_missing",):
486
+ # Missing user response is not an L2/L3 obstacle (codegen uses default contract field names)
487
+ continue
488
+ if f.kind == "extra_in_user":
489
+ continue
490
+ if not f.in_slot:
491
+ return DegradeLevel.L2
492
+ if f.kind in ("type_mismatch", "missing_in_user"):
493
+ return DegradeLevel.L2
494
+ return DegradeLevel.L1
495
+
496
+
497
+ # ---------------------------------------------------------------------------
498
+ # Static validation entry point (per spec §7)
499
+ # ---------------------------------------------------------------------------
500
+ def validate_manifest(capability_dir: Path) -> List[str]:
501
+ """Perform full static validation on a capability's manifest.business_contract.
502
+
503
+ Returns a list of warning strings; fatal errors (BC001~BC003/BC005) raise ContractError directly.
504
+ """
505
+ bc = load_business_contract(capability_dir)
506
+ warnings: List[str] = []
507
+ for api in bc.external_apis:
508
+ flat_req = _walk_schema(api.request_schema, "request")
509
+ flat_resp = _walk_schema(api.response_schema, "response")
510
+ valid_paths = set(flat_req) | set(flat_resp)
511
+ for slot in api.adapter_slots:
512
+ base = slot.split("[]", 1)[0]
513
+ if base not in valid_paths and not any(p.startswith(base + ".") for p in valid_paths):
514
+ warnings.append(
515
+ f"BC004 [{bc.capability}.{api.name}] adapter_slot {slot!r} not found in schemas"
516
+ )
517
+ if api.auth.type == "bearer" and not api.auth.name:
518
+ warnings.append(f"BC006 [{bc.capability}.{api.name}] bearer auth missing header name")
519
+ return warnings