@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,690 @@
1
+ """
2
+ tools/state_machine.py
3
+ ======================
4
+
5
+ TRTC AI Integration —— execution-step 状态机。
6
+
7
+ 把 ``confirmed_plan`` 物化为 ``execution_queue``,并在 topic 阶段推进每个 step 的状态。
8
+
9
+ 读写 session 全部经过 session.py(Session.load / Session.transaction),不直接操作 yaml。
10
+
11
+
12
+ ============================================================
13
+ 设计要点
14
+ ============================================================
15
+
16
+ # 状态转移图
17
+
18
+ not_started ──mark_slice_read──▶ slice_read ──mark_code_written──▶ code_written
19
+
20
+ apply.py
21
+ ┌────┴────┐
22
+ ▼ ▼
23
+ apply_passed apply_failed
24
+ │ │
25
+ 用户"继续" Edit 修改
26
+ │ │
27
+ ▼ ▼
28
+ user_confirmed code_written(回环)
29
+
30
+
31
+ 下一个 step / all_done
32
+
33
+
34
+ # execution_queue 的 step 结构
35
+
36
+ {
37
+ id: str 全局唯一,单 slice 时 = slice_id,多 slice 合并时 = 拼接
38
+ type: str "slice" | "unit"
39
+ title: str 显示用
40
+ status: str "pending" | "done"
41
+ (in_progress 由 current_execution_state 表达,不在 step.status 重复)
42
+ slices: list 该 step 包含的 slice id 列表(1 个或多个)
43
+ }
44
+
45
+
46
+ # execution_granularity 和 delivery_units 的位置
47
+
48
+ session.flow_state.execution_granularity (conference 专有,默认 slice 模式)
49
+ session.flow_state.delivery_units (session 级覆盖,null = 从 execution-units.yaml 读)
50
+
51
+
52
+ # execution-units.yaml 路径
53
+
54
+ {repo_root}/skills/{active_domain_skill}/references/execution-units.yaml
55
+ active_domain_skill 从 session.active_domain_skill 读取(如 "trtc-conference")。
56
+
57
+
58
+ ============================================================
59
+ 公开接口
60
+ ============================================================
61
+
62
+ # Python API
63
+
64
+ from tools.state_machine import StateMachine
65
+
66
+ sm = StateMachine.load() # 读 session,校验状态机已初始化
67
+ scope = sm.current_scope() # 返回当前 step 详情
68
+ next_state = sm.advance("mark_slice_read") # 推进状态
69
+ summary = sm.status() # 完整队列 + 当前状态
70
+
71
+
72
+ # CLI
73
+
74
+ python3 -m tools.state_machine init
75
+ python3 -m tools.state_machine advance <transition>
76
+ python3 -m tools.state_machine status
77
+
78
+ 退出码:
79
+ 0 = 成功
80
+ 1 = 输入错误(transition 非法 / 参数缺失)
81
+ 2 = 资源缺失(session 不存在 / queue 未初始化 / confirmed_plan 缺失)
82
+ 3 = 状态错误(非法状态转移 / all_done 时继续推进)
83
+ 4 = CAS 冲突(caller 重读重试)
84
+ """
85
+
86
+ from __future__ import annotations
87
+
88
+ import copy
89
+ import json
90
+ import os
91
+ import sys
92
+ from pathlib import Path
93
+ from typing import Optional
94
+
95
+ # ============================================================
96
+ # session.py 依赖
97
+ # ============================================================
98
+
99
+ try:
100
+ from tools.session import (
101
+ ConflictError as SessionConflictError,
102
+ MissingError as SessionMissingError,
103
+ Session,
104
+ SessionError,
105
+ emit_trace as _emit_trace,
106
+ )
107
+ except ImportError:
108
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
109
+ from tools.session import ( # type: ignore
110
+ ConflictError as SessionConflictError,
111
+ MissingError as SessionMissingError,
112
+ Session,
113
+ SessionError,
114
+ emit_trace as _emit_trace,
115
+ )
116
+
117
+
118
+ # ============================================================
119
+ # Constants
120
+ # ============================================================
121
+
122
+ _TRANSITIONS: dict[tuple[str, str], str] = {
123
+ ("not_started", "mark_slice_read"): "slice_read",
124
+ ("slice_read", "mark_code_written"): "code_written",
125
+ ("code_written", "mark_apply_passed"): "apply_passed",
126
+ ("code_written", "mark_apply_failed"): "apply_failed",
127
+ ("apply_failed", "mark_code_written"): "code_written",
128
+ ("apply_passed", "mark_user_confirmed"): "ADVANCE_INDEX",
129
+ }
130
+
131
+ KNOWN_TRANSITIONS = {t for (_, t) in _TRANSITIONS}
132
+
133
+ THIS_FILE = Path(__file__).resolve()
134
+ # skills/trtc/tools/state_machine.py → repo_root
135
+ DEFAULT_REPO_ROOT = THIS_FILE.parent.parent.parent.parent
136
+ ENV_REPO_ROOT = "TRTC_REPO_ROOT"
137
+
138
+
139
+ # ============================================================
140
+ # Errors
141
+ # ============================================================
142
+
143
+ class StateMachineError(Exception):
144
+ """状态机异常基类。"""
145
+
146
+
147
+ class NotInitializedError(StateMachineError):
148
+ """execution_queue 尚未初始化(需先调 init)。"""
149
+
150
+
151
+ class InvalidTransitionError(StateMachineError):
152
+ """非法状态转移。"""
153
+
154
+
155
+ class AllDoneError(StateMachineError):
156
+ """execution_queue 已全部完成,不允许继续推进。"""
157
+
158
+
159
+ class SessionStateError(StateMachineError):
160
+ """session 相关错误(不存在 / 损坏 / confirmed_plan 缺失等)。"""
161
+
162
+
163
+ # ============================================================
164
+ # Path helpers
165
+ # ============================================================
166
+
167
+ def _repo_root() -> Path:
168
+ env = os.environ.get(ENV_REPO_ROOT)
169
+ if env:
170
+ return Path(env).resolve()
171
+ return DEFAULT_REPO_ROOT
172
+
173
+
174
+ def _execution_units_path(domain_skill: str) -> Path:
175
+ """根据 domain skill 名定位 execution-units.yaml。"""
176
+ return _repo_root() / "skills" / domain_skill / "references" / "execution-units.yaml"
177
+
178
+
179
+ # ============================================================
180
+ # Step builder helpers
181
+ # ============================================================
182
+
183
+ def _step_id_from_slices(slice_ids: list[str]) -> str:
184
+ if len(slice_ids) == 1:
185
+ return slice_ids[0]
186
+ return "__".join(s.split("/", 1)[-1] for s in slice_ids)
187
+
188
+
189
+ def _step_type(slice_ids: list[str]) -> str:
190
+ return "unit" if len(slice_ids) > 1 else "slice"
191
+
192
+
193
+ def _make_step(step_id: str, title: str, slice_ids: list[str], status: str = "pending") -> dict:
194
+ return {
195
+ "id": step_id,
196
+ "type": _step_type(slice_ids),
197
+ "title": title,
198
+ "status": status,
199
+ "slices": slice_ids,
200
+ }
201
+
202
+
203
+ def _flatten_slices(queue: list[dict]) -> list[str]:
204
+ return [sid for step in queue for sid in step.get("slices", [])]
205
+
206
+
207
+ def _build_slice_steps(plan: list[str]) -> list[dict]:
208
+ return [_make_step(sid, sid, [sid]) for sid in plan]
209
+
210
+
211
+ def _build_unit_steps(raw_units: list[dict], plan: list[str]) -> list[dict]:
212
+ """按 delivery_unit 定义分组,只包含 confirmed_plan 里的 slice。
213
+
214
+ 同一 slice 出现在多个 unit 里时,第一个命中的 unit 优先(first-match wins)。
215
+ 单个 unit 内的重复 slice 视为配置错误,报错。
216
+ """
217
+ remaining = list(plan)
218
+ steps: list[dict] = []
219
+
220
+ for raw in raw_units:
221
+ group = raw.get("slices") or []
222
+
223
+ # 只检查单个 unit 内的重复(跨 unit 允许 first-match wins)
224
+ intra_dupes = sorted({sid for sid in group if group.count(sid) > 1})
225
+ if intra_dupes:
226
+ raise StateMachineError(
227
+ f"delivery unit '{raw.get('id')}' 内有重复 slice:{', '.join(intra_dupes)}"
228
+ )
229
+
230
+ slice_ids = [sid for sid in group if sid in remaining]
231
+ if len(slice_ids) < 2:
232
+ continue
233
+ step_id = raw.get("id") or _step_id_from_slices(slice_ids)
234
+ steps.append(_make_step(step_id, raw.get("title") or step_id, slice_ids))
235
+ for sid in slice_ids:
236
+ remaining.remove(sid)
237
+
238
+ for sid in remaining:
239
+ steps.append(_make_step(sid, sid, [sid]))
240
+
241
+ order = {sid: i for i, sid in enumerate(plan)}
242
+ steps.sort(key=lambda step: min(order[sid] for sid in step["slices"]))
243
+ return steps
244
+
245
+
246
+ def _load_scenario_units(domain_skill: str, scenario_id: str) -> list[dict]:
247
+ """从 execution-units.yaml 读取指定 scenario 的 delivery_units。"""
248
+ path = _execution_units_path(domain_skill)
249
+ if not path.exists():
250
+ return []
251
+ import yaml
252
+ try:
253
+ config = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
254
+ except yaml.YAMLError as e:
255
+ raise StateMachineError(f"execution-units.yaml 解析失败:{e}")
256
+ return (
257
+ (config.get("scenarios") or {})
258
+ .get(scenario_id, {})
259
+ .get("delivery_units") or []
260
+ )
261
+
262
+
263
+ def _load_compat(path: str | Path) -> "StateMachine":
264
+ """兼容旧调用方:显式 session 路径加载状态机。"""
265
+ return StateMachine.load(path=str(path))
266
+
267
+
268
+ def _legacy_flow_fields(sess: Session) -> tuple[Optional[str], Optional[list[dict]]]:
269
+ """兼容 v1/v1.5 fixture:顶层 execution_granularity / delivery_units。"""
270
+ flow_state: dict = sess.flow_state or {}
271
+ granularity = flow_state.get("execution_granularity")
272
+ delivery_units = flow_state.get("delivery_units")
273
+
274
+ if granularity is None:
275
+ granularity = sess.get("execution_granularity")
276
+ if delivery_units is None:
277
+ delivery_units = sess.get("delivery_units")
278
+
279
+ return granularity, delivery_units
280
+
281
+
282
+ def _legacy_domain_skill(sess: Session) -> str:
283
+ return sess.active_domain_skill or f"trtc-{sess.product}" if sess.product else ""
284
+
285
+
286
+ def _legacy_scenario_id(sess: Session) -> str:
287
+ return sess.active_scenario or sess.get("scenario") or ""
288
+
289
+
290
+ # ============================================================
291
+ # StateMachine class
292
+ # ============================================================
293
+
294
+ class StateMachine:
295
+ """execution_queue 状态机。
296
+
297
+ 无持久状态——session.yaml 是唯一 SoT。
298
+ 每次操作:load session → 操作 → transaction 写回。
299
+ """
300
+
301
+ def __init__(self, sess: Session):
302
+ self._sess = sess
303
+
304
+ @classmethod
305
+ def load(
306
+ cls,
307
+ path: Optional[str] = None,
308
+ project_root: Optional[str] = None,
309
+ ) -> "StateMachine":
310
+ """加载 session,返回状态机实例。调用 init() 后方可调用 advance()。
311
+
312
+ Raises:
313
+ SessionStateError: session 不存在 / 损坏
314
+ """
315
+ try:
316
+ sess = Session.load(path=path, project_root=project_root)
317
+ except SessionMissingError:
318
+ raise SessionStateError("session 不存在,请先完成 onboarding 并初始化 session")
319
+ except SessionError as e:
320
+ raise SessionStateError(f"session 加载失败:{e}")
321
+ return cls(sess)
322
+
323
+ # ---- init ----
324
+
325
+ def init(self) -> None:
326
+ """把 confirmed_plan 物化为 execution_queue。
327
+
328
+ 幂等:queue 已存在且与 confirmed_plan 一致时直接返回。
329
+ confirmed_plan 缺失时抛 SessionStateError。
330
+
331
+ 读取顺序:
332
+ execution_granularity → session.flow_state.execution_granularity
333
+ delivery_units → session.flow_state.delivery_units
334
+ 或 execution-units.yaml(按 active_scenario 查)
335
+ """
336
+ sess = self._sess
337
+ plan: list[str] = sess.confirmed_plan or []
338
+ if not plan:
339
+ raise SessionStateError(
340
+ "confirmed_plan 为空——onboarding 应在调用 init 前写入 confirmed_plan"
341
+ )
342
+
343
+ existing_queue: list[dict] = sess.execution_queue or []
344
+ if existing_queue:
345
+ if _flatten_slices(existing_queue) == plan:
346
+ _emit_trace({
347
+ "session_id": sess.session_id,
348
+ "event": "state_machine.init.idempotent",
349
+ })
350
+ return
351
+ raise StateMachineError(
352
+ "execution_queue 已存在且与 confirmed_plan 不一致——"
353
+ "如需重置,请手动清除 execution_queue / current_execution_index / "
354
+ "current_execution_state 后重试"
355
+ )
356
+
357
+ # 读 conference 专有字段(在 flow_state 里)
358
+ granularity, delivery_units = _legacy_flow_fields(sess)
359
+
360
+ if granularity in {"unit", "delivery_unit"}:
361
+ if not delivery_units:
362
+ domain_skill = _legacy_domain_skill(sess)
363
+ scenario_id = _legacy_scenario_id(sess)
364
+ delivery_units = _load_scenario_units(domain_skill, scenario_id)
365
+ if not delivery_units:
366
+ # unit 模式但找不到分组配置,降级为 slice 模式并记录 trace
367
+ _emit_trace({
368
+ "session_id": sess.session_id,
369
+ "event": "state_machine.init.unit_degraded_to_slice",
370
+ "reason": "delivery_units 为空,execution-units.yaml 未找到对应 scenario",
371
+ "domain_skill": _legacy_domain_skill(sess),
372
+ "active_scenario": _legacy_scenario_id(sess),
373
+ })
374
+ queue = _build_slice_steps(plan)
375
+ else:
376
+ queue = _build_unit_steps(delivery_units, plan)
377
+ else:
378
+ queue = _build_slice_steps(plan)
379
+
380
+ try:
381
+ with sess.transaction() as upd:
382
+ upd.execution_queue = queue
383
+ upd.current_execution_index = 0
384
+ upd.current_execution_state = "not_started"
385
+ except SessionConflictError:
386
+ raise
387
+ except SessionError as e:
388
+ raise SessionStateError(f"写入 session 失败:{e}")
389
+
390
+ _emit_trace({
391
+ "session_id": sess.session_id,
392
+ "event": "state_machine.init",
393
+ "granularity": granularity or "slice",
394
+ "steps": len(queue),
395
+ "plan_slices": len(plan),
396
+ })
397
+
398
+ # ---- current_scope ----
399
+
400
+ def current_scope(self) -> dict:
401
+ """返回当前 step 详情(供 Python API 调用方使用)。"""
402
+ sess = self._sess
403
+ queue: list[dict] = sess.execution_queue or []
404
+ if not queue:
405
+ return {"initialised": False, "reason": "execution_queue 未初始化"}
406
+
407
+ idx: int = sess.current_execution_index or 0
408
+ state: str = sess.current_execution_state or "not_started"
409
+
410
+ if state == "all_done":
411
+ return {
412
+ "initialised": True,
413
+ "index": idx,
414
+ "state": "all_done",
415
+ "total": len(queue),
416
+ "id": None,
417
+ "title": None,
418
+ "type": None,
419
+ "kind": "execution",
420
+ "slice_ids": [],
421
+ }
422
+
423
+ if idx < 0 or idx >= len(queue):
424
+ return {"initialised": False, "reason": "execution cursor 越界"}
425
+
426
+ step = queue[idx]
427
+ slice_ids: list[str] = step.get("slices") or [step.get("id")]
428
+ return {
429
+ "initialised": True,
430
+ "index": idx,
431
+ "state": state,
432
+ "total": len(queue),
433
+ "id": step.get("id"),
434
+ "title": step.get("title") or step.get("id"),
435
+ "type": step.get("type") or _step_type(slice_ids),
436
+ "kind": step.get("type") or _step_type(slice_ids),
437
+ "slice_ids": slice_ids,
438
+ }
439
+
440
+ # ---- advance ----
441
+
442
+ def advance(self, transition: str) -> str:
443
+ """推进状态机,返回新状态。
444
+
445
+ Raises:
446
+ InvalidTransitionError: 非法 transition 名
447
+ AllDoneError: 已经 all_done
448
+ NotInitializedError: queue 未初始化
449
+ """
450
+ if transition not in KNOWN_TRANSITIONS:
451
+ raise InvalidTransitionError(
452
+ f"未知 transition:'{transition}'。"
453
+ f"可用:{sorted(KNOWN_TRANSITIONS)}"
454
+ )
455
+
456
+ sess = self._sess
457
+ queue: list[dict] = sess.execution_queue or []
458
+ if not queue:
459
+ raise NotInitializedError("execution_queue 未初始化,请先调用 init")
460
+
461
+ idx: int = sess.current_execution_index or 0
462
+ state: str = sess.current_execution_state or "not_started"
463
+
464
+ if state == "all_done":
465
+ raise AllDoneError("execution_queue 已全部完成,不允许继续推进")
466
+
467
+ next_state = _TRANSITIONS.get((state, transition))
468
+ if next_state is None:
469
+ allowed = sorted(t for (s, t) in _TRANSITIONS if s == state)
470
+ raise InvalidTransitionError(
471
+ f"当前状态 '{state}' 不允许 transition '{transition}'。"
472
+ f"当前允许:{allowed}"
473
+ )
474
+
475
+ if next_state == "ADVANCE_INDEX":
476
+ new_idx = idx + 1
477
+ final_state = "all_done" if new_idx >= len(queue) else "not_started"
478
+
479
+ # 深拷贝后再修改,避免 transaction 失败时污染 in-memory session
480
+ queue_copy = copy.deepcopy(queue)
481
+ step_copy = queue_copy[idx]
482
+ step_copy["status"] = "done"
483
+ evidence_ids = [step_copy.get("id")] + list(step_copy.get("slices", []))
484
+
485
+ try:
486
+ with sess.transaction() as upd:
487
+ upd.execution_queue = queue_copy
488
+ upd.current_execution_index = new_idx
489
+ upd.current_execution_state = final_state
490
+ except SessionConflictError:
491
+ raise # 让 CAS 冲突保持原始类型,caller 可重读重试
492
+ except SessionError as e:
493
+ raise SessionStateError(f"写入 session 失败:{e}")
494
+
495
+ # transaction 成功后再删除证据文件,保证原子性
496
+ sess_path = Path(sess.path)
497
+ for eid in [e for e in evidence_ids if e]:
498
+ ev = sess_path.parent / ".trtc-apply-evidence" / (eid.replace("/", "__") + ".json")
499
+ try:
500
+ ev.unlink()
501
+ except FileNotFoundError:
502
+ pass
503
+
504
+ _emit_trace({
505
+ "session_id": sess.session_id,
506
+ "event": "state_machine.advance",
507
+ "transition": transition,
508
+ "from_state": state,
509
+ "to_state": final_state,
510
+ "step_index": idx,
511
+ })
512
+ return final_state
513
+
514
+ try:
515
+ with sess.transaction() as upd:
516
+ upd.current_execution_state = next_state
517
+ except SessionConflictError:
518
+ raise # 让 CAS 冲突保持原始类型
519
+ except SessionError as e:
520
+ raise SessionStateError(f"写入 session 失败:{e}")
521
+
522
+ _emit_trace({
523
+ "session_id": sess.session_id,
524
+ "event": "state_machine.advance",
525
+ "transition": transition,
526
+ "from_state": state,
527
+ "to_state": next_state,
528
+ "step_index": idx,
529
+ })
530
+ return next_state
531
+
532
+ # ---- status ----
533
+
534
+ def status(self) -> dict:
535
+ """返回完整队列状态(CLI / 调试用)。"""
536
+ sess = self._sess
537
+ queue: list[dict] = sess.execution_queue or []
538
+ if not queue:
539
+ return {"initialised": False, "reason": "execution_queue 未初始化"}
540
+
541
+ idx: int = sess.current_execution_index or 0
542
+ state: str = sess.current_execution_state or "not_started"
543
+ step = queue[idx] if idx < len(queue) else None
544
+ slice_ids: list[str] = (step.get("slices") if step else None) or []
545
+
546
+ done = sum(1 for s in queue if s.get("status") == "done")
547
+
548
+ return {
549
+ "initialised": True,
550
+ "index": idx,
551
+ "state": state,
552
+ "total": len(queue),
553
+ "done": done,
554
+ "remaining": len(queue) - done,
555
+ "kind": step.get("type") if step else "execution",
556
+ "current_id": step.get("id") if step else None,
557
+ "current_title": step.get("title") if step else None,
558
+ "current_type": step.get("type") if step else None,
559
+ "current_slice_id": slice_ids[0] if slice_ids else None,
560
+ "current_unit_id": (step.get("id") if step and step.get("type") == "unit" else None),
561
+ "slice_ids": slice_ids,
562
+ "queue": queue,
563
+ }
564
+
565
+
566
+ # ============================================================
567
+ # Backward-compatible module helpers
568
+ # ============================================================
569
+
570
+ def init_queue(session_path: str | Path) -> None:
571
+ """旧接口兼容层:按显式 session 路径初始化 execution_queue。"""
572
+ try:
573
+ _load_compat(session_path).init()
574
+ except StateMachineError as exc:
575
+ raise RuntimeError(str(exc)) from exc
576
+
577
+
578
+ def current_scope(session_path: str | Path) -> dict:
579
+ """旧接口兼容层:返回当前 step。"""
580
+ try:
581
+ return _load_compat(session_path).current_scope()
582
+ except SessionStateError:
583
+ return {"initialised": False, "reason": "session file missing"}
584
+
585
+
586
+ def current_slice(session_path: str | Path) -> tuple[Optional[int], Optional[str], Optional[str]]:
587
+ """旧接口兼容层:返回当前 step 的首个 slice。"""
588
+ scope = current_scope(session_path)
589
+ if not scope.get("initialised"):
590
+ return (None, None, None)
591
+ if scope.get("state") == "all_done":
592
+ return (scope.get("index"), None, "all_done")
593
+ slice_ids = scope.get("slice_ids") or []
594
+ return (scope.get("index"), slice_ids[0] if slice_ids else None, scope.get("state"))
595
+
596
+
597
+ def advance(session_path: str | Path, transition: str) -> str:
598
+ """旧接口兼容层:按显式 session 路径推进状态机。"""
599
+ try:
600
+ return _load_compat(session_path).advance(transition)
601
+ except StateMachineError as exc:
602
+ raise RuntimeError(str(exc)) from exc
603
+
604
+
605
+ def status(session_path: str | Path) -> dict:
606
+ """旧接口兼容层:返回完整队列状态。"""
607
+ try:
608
+ return _load_compat(session_path).status()
609
+ except SessionStateError:
610
+ return {"initialised": False, "reason": "session file missing"}
611
+
612
+
613
+ # ============================================================
614
+ # CLI
615
+ # ============================================================
616
+
617
+ def _cli_init(_args: list[str]) -> int:
618
+ try:
619
+ sm = StateMachine.load()
620
+ sm.init()
621
+ except SessionStateError as e:
622
+ print(f"ERROR: {e}", file=sys.stderr)
623
+ return 2
624
+ except StateMachineError as e:
625
+ print(f"ERROR: {e}", file=sys.stderr)
626
+ return 3
627
+ except SessionConflictError as e:
628
+ print(f"ERROR (CAS): {e}", file=sys.stderr)
629
+ return 4
630
+ print("init: execution_queue 物化完成")
631
+ return 0
632
+
633
+
634
+ def _cli_advance(args: list[str]) -> int:
635
+ if not args:
636
+ print("ERROR: 必须提供 transition 名", file=sys.stderr)
637
+ print(f"可用:{sorted(KNOWN_TRANSITIONS)}", file=sys.stderr)
638
+ return 1
639
+ transition = args[0]
640
+ try:
641
+ sm = StateMachine.load()
642
+ new_state = sm.advance(transition)
643
+ except InvalidTransitionError as e:
644
+ print(f"ERROR: {e}", file=sys.stderr)
645
+ return 1
646
+ except (NotInitializedError, SessionStateError) as e:
647
+ print(f"ERROR: {e}", file=sys.stderr)
648
+ return 2
649
+ except AllDoneError as e:
650
+ print(f"ERROR: {e}", file=sys.stderr)
651
+ return 3
652
+ except SessionConflictError as e:
653
+ print(f"ERROR (CAS): {e}", file=sys.stderr)
654
+ return 4
655
+ print(f"advance: {transition} → {new_state}")
656
+ return 0
657
+
658
+
659
+ def _cli_status(_args: list[str]) -> int:
660
+ try:
661
+ sm = StateMachine.load()
662
+ result = sm.status()
663
+ except SessionStateError as e:
664
+ print(f"ERROR: {e}", file=sys.stderr)
665
+ return 2
666
+ print(json.dumps(result, ensure_ascii=False, indent=2))
667
+ return 0
668
+
669
+
670
+ def main() -> int:
671
+ argv = sys.argv[1:]
672
+ if not argv:
673
+ print(__doc__)
674
+ return 0
675
+ cmd, rest = argv[0], argv[1:]
676
+ handlers = {
677
+ "init": _cli_init,
678
+ "advance": _cli_advance,
679
+ "status": _cli_status,
680
+ }
681
+ handler = handlers.get(cmd)
682
+ if not handler:
683
+ print(f"未知子命令:{cmd}", file=sys.stderr)
684
+ print(f"可用:{', '.join(handlers)}", file=sys.stderr)
685
+ return 1
686
+ return handler(rest)
687
+
688
+
689
+ if __name__ == "__main__":
690
+ raise SystemExit(main())