@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,192 @@
1
+ """HandoffService — Application service chaining IntentDetector with HandoffClient.
2
+
3
+ Only depends on ports (HandoffClient interface), does not know the specific backend implementation.
4
+ Switching adapter only requires re-injecting client (adapters.factory.set_client).
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from typing import List, Optional
9
+
10
+ from ..ports.handoff_client import HandoffClient
11
+ from ..summary_link import attach_summary_to_ticket
12
+ from .intent_detector import IntentDetector, get_default_detector
13
+ from .models import OverallStatus, Ticket, TicketStatus, TicketStatusEnum
14
+
15
+
16
+ class HandoffService:
17
+ """Handoff business service."""
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ client: HandoffClient,
23
+ detector: Optional[IntentDetector] = None,
24
+ ) -> None:
25
+ self._client = client
26
+ self._detector = detector or get_default_detector()
27
+
28
+ # ------------------------------------------------------------------
29
+ # Intent detection + trigger (for injection into conversation-core.before_push_text)
30
+ # ------------------------------------------------------------------
31
+ def maybe_handoff(self, session_id: str, text: str) -> Optional[str]:
32
+ """Recognize handoff intent; if matched, request ticket and return assembled script; otherwise return None."""
33
+ if not session_id or not text:
34
+ return None
35
+ if not self._detector.is_handoff_intent(text):
36
+ return None
37
+
38
+ # Reuse existing ticket (don't duplicate for same user in progress)
39
+ existing = self._client.get_or_attach(session_id)
40
+ if existing is not None and existing.status in (
41
+ TicketStatusEnum.PENDING.value,
42
+ TicketStatusEnum.PROCESSING.value,
43
+ ):
44
+ return self._render_handoff_message(existing)
45
+
46
+ ticket = self._client.create_ticket(
47
+ user_id=session_id,
48
+ subject=text[:64],
49
+ description=text[:512],
50
+ priority="normal",
51
+ )
52
+ # Attach session summary at ticket creation so agents can immediately see the issue on the dashboard (no-op if session-summary is not installed)
53
+ attach_summary_to_ticket(ticket)
54
+ return self._render_handoff_message(ticket)
55
+
56
+ @staticmethod
57
+ def _render_handoff_message(t: Ticket) -> Optional[str]:
58
+ if t.status == TicketStatusEnum.PROCESSING.value:
59
+ return (
60
+ f"[handoff state=connected agent={t.agent_id}]\n"
61
+ "You are now connected to a human agent. Please wait a moment."
62
+ )
63
+ if t.status == TicketStatusEnum.PENDING.value:
64
+ return (
65
+ f"[handoff state=waiting position={t.queue_position} "
66
+ f"eta={t.eta_seconds}s]\n"
67
+ f"You are number {t.queue_position} in the agent queue, "
68
+ f"estimated wait {t.eta_seconds} seconds."
69
+ )
70
+ if t.status == TicketStatusEnum.TIMEOUT.value:
71
+ return "[handoff state=timeout]\nNo agents are currently available. Please try again later."
72
+ return None
73
+
74
+ # ------------------------------------------------------------------
75
+ # Explicit operations (for router calls)
76
+ # ------------------------------------------------------------------
77
+ def request(
78
+ self,
79
+ session_id: str,
80
+ *,
81
+ reason: str = "",
82
+ subject: Optional[str] = None,
83
+ description: Optional[str] = None,
84
+ ) -> Ticket:
85
+ existing = self._client.get_or_attach(session_id)
86
+ if existing is not None and existing.status in (
87
+ TicketStatusEnum.PENDING.value,
88
+ TicketStatusEnum.PROCESSING.value,
89
+ ):
90
+ return existing
91
+ ticket = self._client.create_ticket(
92
+ user_id=session_id,
93
+ subject=(subject or reason or "human handoff")[:64],
94
+ description=(description or reason or "")[:512],
95
+ priority="normal",
96
+ )
97
+ # Attach session summary at ticket creation (no-op if session-summary not installed; does not affect main flow)
98
+ attach_summary_to_ticket(ticket)
99
+ return ticket
100
+
101
+ def connect(self, session_id: str, agent_id: str) -> Ticket:
102
+ """For /api/v1/handoff/connect: force-connect a session to a specified agent."""
103
+ # Look up active ticket by user_id first
104
+ ticket = self._client.get_or_attach(session_id)
105
+ if ticket is None:
106
+ raise ValueError(f"session {session_id} not waiting")
107
+ if ticket.status == TicketStatusEnum.PROCESSING.value:
108
+ return ticket
109
+ updated = self._client.update_status(
110
+ ticket.ticket_id,
111
+ TicketStatusEnum.PROCESSING.value,
112
+ agent_id=agent_id,
113
+ )
114
+ if updated is None:
115
+ raise ValueError(f"ticket {ticket.ticket_id} not found")
116
+ return updated
117
+
118
+ def cancel(self, session_id: str, *, reason: str = "") -> Ticket:
119
+ ticket = self._client.get_or_attach(session_id)
120
+ if ticket is None:
121
+ raise ValueError(f"session not found: {session_id}")
122
+ result = self._client.cancel_ticket(ticket.ticket_id, reason=reason)
123
+ if result is None:
124
+ raise ValueError(f"ticket {ticket.ticket_id} not found")
125
+ return result
126
+
127
+ def get_by_session(self, session_id: str) -> Optional[Ticket]:
128
+ return self._client.get_or_attach(session_id)
129
+
130
+ def overall_status(self) -> OverallStatus:
131
+ return self._client.overall_status()
132
+
133
+ # ------------------------------------------------------------------
134
+ # Dashboard helpers
135
+ # ------------------------------------------------------------------
136
+ def list_tickets(
137
+ self,
138
+ *,
139
+ limit: int = 50,
140
+ status: Optional[str] = None,
141
+ ) -> List[Ticket]:
142
+ return self._client.list_tickets(limit=limit, status=status)
143
+
144
+ def update_ticket_status(
145
+ self,
146
+ ticket_id: str,
147
+ status: str,
148
+ *,
149
+ agent_id: Optional[str] = None,
150
+ ) -> Optional[Ticket]:
151
+ return self._client.update_status(ticket_id, status, agent_id=agent_id)
152
+
153
+ def query_ticket(self, ticket_id: str) -> Optional[TicketStatus]:
154
+ return self._client.query_status(ticket_id)
155
+
156
+ # ------------------------------------------------------------------
157
+ # Customer satisfaction feedback
158
+ # ------------------------------------------------------------------
159
+ def submit_feedback(self, session_id: str, rating: int, comment: str = "") -> dict:
160
+ """Persist a satisfaction rating and, if a ticket exists for the session,
161
+ attach it to that ticket so agents can see the score on the dashboard."""
162
+ from ..feedback_store import make_feedback, save_feedback
163
+
164
+ fb = make_feedback(rating, comment)
165
+ save_feedback(session_id, fb)
166
+ try:
167
+ ticket = self._client.get_or_attach(session_id)
168
+ if ticket is not None:
169
+ ticket.extra["feedback"] = fb
170
+ except Exception: # noqa: BLE001 - feedback must never break on ticket lookup
171
+ pass
172
+ return {"session_id": session_id, "feedback": fb}
173
+
174
+
175
+ # ---------------------------------------------------------------------------
176
+ # Default service singleton
177
+ # ---------------------------------------------------------------------------
178
+ _default_service: Optional[HandoffService] = None
179
+
180
+
181
+ def get_default_service() -> HandoffService:
182
+ """Build service singleton from current environment (client from adapters.factory)."""
183
+ global _default_service
184
+ if _default_service is None:
185
+ from ..adapters.factory import get_client
186
+ _default_service = HandoffService(client=get_client())
187
+ return _default_service
188
+
189
+
190
+ def reset_default_service() -> None:
191
+ global _default_service
192
+ _default_service = None
@@ -0,0 +1,54 @@
1
+ """In-process satisfaction feedback store (CSAT).
2
+
3
+ Lightweight memory store keyed by session_id. Used by HandoffService.submit_feedback
4
+ so the dashboard can look up a session's rating without requiring a ticket to exist.
5
+
6
+ Why memory-only?
7
+ - The default adapter (HH_ADAPTER=local_queue) is itself in-process, so an in-memory
8
+ feedback store keeps the demo self-contained with zero external dependencies.
9
+ - Production deployments that persist tickets out-of-band should swap this for a
10
+ persistent sink; the public surface (make_feedback / save_feedback / get_feedback)
11
+ stays stable.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import threading
16
+ import time
17
+ import uuid
18
+ from typing import Any, Dict, Optional
19
+
20
+ _lock = threading.RLock()
21
+ _store: Dict[str, Dict[str, Any]] = {}
22
+
23
+
24
+ def make_feedback(rating: int, comment: str = "") -> Dict[str, Any]:
25
+ """Build a normalized feedback record (does not persist)."""
26
+ rating = int(rating)
27
+ if rating < 1:
28
+ rating = 1
29
+ elif rating > 5:
30
+ rating = 5
31
+ return {
32
+ "feedback_id": f"fb_{uuid.uuid4().hex[:12]}",
33
+ "rating": rating,
34
+ "comment": (comment or "").strip()[:1000],
35
+ "created_at": time.time(),
36
+ }
37
+
38
+
39
+ def save_feedback(session_id: str, feedback: Dict[str, Any]) -> Dict[str, Any]:
40
+ """Persist a feedback record keyed by session_id (overwrites prior entry)."""
41
+ if not session_id:
42
+ raise ValueError("session_id is required")
43
+ with _lock:
44
+ _store[session_id] = dict(feedback)
45
+ return feedback
46
+
47
+
48
+ def get_feedback(session_id: str) -> Optional[Dict[str, Any]]:
49
+ """Return the stored feedback for a session, or None if not rated yet."""
50
+ if not session_id:
51
+ return None
52
+ with _lock:
53
+ fb = _store.get(session_id)
54
+ return dict(fb) if fb is not None else None
@@ -0,0 +1,4 @@
1
+ """human-handoff abstract ports module."""
2
+ from .handoff_client import HandoffClient
3
+
4
+ __all__ = ["HandoffClient"]
@@ -0,0 +1,86 @@
1
+ """human-handoff abstract port (Port).
2
+
3
+ One-to-one correspondence with manifest.yaml.business_contract fields:
4
+ - create_ticket -> ticket.create
5
+ - query_status -> ticket.status_query
6
+ - cancel_ticket -> ticket.cancel
7
+ - overall_status -> internal status (not in external contract; for local dashboard use only)
8
+
9
+ All concrete implementations (local_queue / default_rest / mock / user_custom) must inherit this ABC.
10
+ The core layer only depends on this interface, unaware of any specific backend type; switching backends only requires changing the adapter.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from abc import ABC, abstractmethod
15
+ from typing import List, Optional
16
+
17
+ from ..core.models import OverallStatus, Ticket, TicketStatus
18
+
19
+
20
+ class HandoffClient(ABC):
21
+ """Unified interface contract for handoff / ticketing backends."""
22
+
23
+ # --- Methods aligned with business_contract --------------------------
24
+
25
+ @abstractmethod
26
+ def create_ticket(
27
+ self,
28
+ *,
29
+ user_id: str,
30
+ subject: str = "",
31
+ description: str = "",
32
+ priority: str = "normal",
33
+ transcript: Optional[List[str]] = None,
34
+ ) -> Ticket:
35
+ """Create ticket. Corresponds to business_contract.ticket.create."""
36
+
37
+ @abstractmethod
38
+ def query_status(self, ticket_id: str) -> Optional[TicketStatus]:
39
+ """Query single ticket status. Corresponds to ticket.status_query.
40
+
41
+ Returns None if ticket does not exist.
42
+ """
43
+
44
+ @abstractmethod
45
+ def cancel_ticket(self, ticket_id: str, reason: str = "") -> Optional[Ticket]:
46
+ """Cancel ticket. Corresponds to ticket.cancel. Returns None if ticket does not exist."""
47
+
48
+ @abstractmethod
49
+ def overall_status(self) -> OverallStatus:
50
+ """Overall queue status (dashboard use, not external contract)."""
51
+
52
+ # --- Dashboard helper methods (default implementation; remote backends may not override) ---------
53
+
54
+ def list_tickets(
55
+ self,
56
+ *,
57
+ limit: int = 50,
58
+ status: Optional[str] = None,
59
+ ) -> List[Ticket]:
60
+ """List tickets (default returns empty; remote backends override as needed)."""
61
+ return []
62
+
63
+ def update_status(
64
+ self,
65
+ ticket_id: str,
66
+ status: str,
67
+ *,
68
+ agent_id: Optional[str] = None,
69
+ ) -> Optional[Ticket]:
70
+ """Agent manually updates ticket status (not supported by default; can be overridden by mock / local_queue)."""
71
+ raise NotImplementedError(
72
+ f"{type(self).__name__} does not support manual status update"
73
+ )
74
+
75
+ # --- Bridge interface compatible with old trigger.maybe_handoff ---------
76
+
77
+ def get_or_attach(self, user_id: str) -> Optional[Ticket]:
78
+ """Find existing ticket by user_id (old session_id); returns None if not found.
79
+
80
+ This method allows the facade layer to query existing ticket status without breaking the old API.
81
+ Default implementation iterates list_tickets.
82
+ """
83
+ for t in self.list_tickets(limit=200):
84
+ if t.user_id == user_id:
85
+ return t
86
+ return None
@@ -0,0 +1,62 @@
1
+ """queue.py — compatibility facade.
2
+
3
+ Keeps the `attach_session` public symbol for manifest.extensions (agent.after_start)
4
+ to continue calling as `_hh_queue.attach_session(session_id, info=...)`.
5
+
6
+ Old version's `get_queue()` / `HandoffQueue` / `HandoffRecord` / `HandoffState`
7
+ are no longer used under the new architecture; kept as deprecated shims for gradual migration of third-party code.
8
+
9
+ New code should use directly:
10
+ - adapters.factory.get_client() Get HandoffClient instance
11
+ - core.service.get_default_service() Get HandoffService instance
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import warnings
16
+ from typing import Any
17
+
18
+ from .adapters.factory import get_client
19
+ from .core.models import ( # noqa: F401 (backward compatible export)
20
+ OverallStatus,
21
+ Ticket,
22
+ TicketStatus,
23
+ TicketStatusEnum,
24
+ )
25
+
26
+
27
+ def attach_session(session_id: str, info: Any = None) -> None:
28
+ """For conversation-core.after_start injection point use.
29
+
30
+ Under the refactored implementation, "session registration" is done by client on create_ticket as needed;
31
+ kept here as a no-op entry point to avoid breaking old calls from manifest.extensions.
32
+ info parameter reserved for compatibility with old signature.
33
+ """
34
+ # Trigger client singleton init to surface config errors early during startup
35
+ _ = get_client()
36
+ return None
37
+
38
+
39
+ # --------------------------------------------------------------------
40
+ # Deprecated shim (for old tests / old external code gradual migration only; new code should not depend)
41
+ # --------------------------------------------------------------------
42
+ def get_queue():
43
+ warnings.warn(
44
+ "human_handoff.queue.get_queue() is deprecated; "
45
+ "use adapters.factory.get_client() or core.service.get_default_service() instead.",
46
+ DeprecationWarning,
47
+ stacklevel=2,
48
+ )
49
+ return get_client()
50
+
51
+
52
+ # Old symbol aliases (some integrators may directly import)
53
+ HandoffState = TicketStatusEnum
54
+ HandoffRecord = Ticket
55
+
56
+
57
+ __all__ = [
58
+ "HandoffRecord",
59
+ "HandoffState",
60
+ "attach_session",
61
+ "get_queue",
62
+ ]
@@ -0,0 +1,201 @@
1
+ """human-handoff FastAPI sub-router.
2
+
3
+ Mounted on skeleton: app.include_router(router, prefix="/api/v1/handoff")
4
+
5
+ Refactoring notes:
6
+ - All business logic delegated to core.service.HandoffService
7
+ - Response fields remain fully consistent with Phase 2 (to_legacy_dict), not breaking Web Demo
8
+ - New /admin/* sub-routes for Phase 3 Path A ticket agent dashboard
9
+ """
10
+ from __future__ import annotations
11
+
12
+ from typing import List, Optional
13
+
14
+ from fastapi import APIRouter, HTTPException, Query
15
+ from pydantic import BaseModel, Field
16
+
17
+ from .core.models import TicketStatusEnum
18
+ from .core.service import get_default_service
19
+
20
+
21
+ router = APIRouter()
22
+
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Request body
26
+ # ---------------------------------------------------------------------------
27
+ class RequestBody(BaseModel):
28
+ session_id: str = Field(..., max_length=64)
29
+ reason: Optional[str] = Field(default="", max_length=512)
30
+
31
+
32
+ class ConnectBody(BaseModel):
33
+ session_id: str = Field(..., max_length=64)
34
+ agent_id: str = Field(..., max_length=64)
35
+
36
+
37
+ class CancelBody(BaseModel):
38
+ session_id: str = Field(..., max_length=64)
39
+
40
+
41
+ class DetectBody(BaseModel):
42
+ """Body for handoff intent detection (reuses intent_detector pure function)."""
43
+
44
+ text: str = Field(..., max_length=4096)
45
+
46
+
47
+ class FeedbackBody(BaseModel):
48
+ """Body for submitting a post-call satisfaction rating."""
49
+
50
+ session_id: str = Field(..., max_length=64)
51
+ rating: int = Field(..., ge=1, le=5)
52
+ comment: Optional[str] = Field(default="", max_length=1000)
53
+
54
+
55
+ class AdminUpdateBody(BaseModel):
56
+ status: str = Field(..., max_length=32)
57
+ agent_id: Optional[str] = Field(default=None, max_length=64)
58
+
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Existing endpoints (fully compatible with Phase 2)
62
+ # ---------------------------------------------------------------------------
63
+ @router.get("/status")
64
+ def overall() -> dict:
65
+ return {"code": 0, "data": get_default_service().overall_status().to_dict()}
66
+
67
+
68
+ @router.get("/{session_id}")
69
+ def session_status(session_id: str) -> dict:
70
+ ticket = get_default_service().get_by_session(session_id)
71
+ if ticket is None:
72
+ raise HTTPException(
73
+ status_code=404, detail=f"session not tracked: {session_id}"
74
+ )
75
+ return {"code": 0, "data": ticket.to_legacy_dict()}
76
+
77
+
78
+ @router.post("/request")
79
+ def request_handoff(body: RequestBody) -> dict:
80
+ ticket = get_default_service().request(
81
+ body.session_id, reason=body.reason or ""
82
+ )
83
+ return {"code": 0, "data": ticket.to_legacy_dict()}
84
+
85
+
86
+ @router.post("/connect")
87
+ def connect(body: ConnectBody) -> dict:
88
+ try:
89
+ ticket = get_default_service().connect(body.session_id, body.agent_id)
90
+ except ValueError as exc:
91
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
92
+ except RuntimeError as exc:
93
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
94
+ return {"code": 0, "data": ticket.to_legacy_dict()}
95
+
96
+
97
+ @router.post("/cancel")
98
+ def cancel(body: CancelBody) -> dict:
99
+ try:
100
+ ticket = get_default_service().cancel(body.session_id)
101
+ except ValueError as exc:
102
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
103
+ return {"code": 0, "data": ticket.to_legacy_dict()}
104
+
105
+
106
+ # ---------------------------------------------------------------------------
107
+ # Intent detection + satisfaction feedback (issue 6 / issue 7)
108
+ # ---------------------------------------------------------------------------
109
+ @router.post("/detect")
110
+ def detect_handoff(body: DetectBody) -> dict:
111
+ """Pure intent detection: returns whether the text implies a handoff request.
112
+
113
+ Reuses core.intent_detector (same logic the backend uses internally), so the
114
+ frontend fast-path and the backend stay in sync.
115
+ """
116
+ from .core.intent_detector import is_handoff_intent
117
+
118
+ matched = bool(is_handoff_intent(body.text or ""))
119
+ return {"code": 0, "data": {"matched": matched, "text": body.text or ""}}
120
+
121
+
122
+ @router.post("/feedback")
123
+ def submit_feedback(body: FeedbackBody) -> dict:
124
+ """Persist a post-call CSAT rating and attach it to the session's ticket (if any)."""
125
+ result = get_default_service().submit_feedback(
126
+ body.session_id, body.rating, body.comment or ""
127
+ )
128
+ return {"code": 0, "data": result}
129
+
130
+
131
+ @router.get("/feedback/{session_id}")
132
+ def get_feedback(session_id: str) -> dict:
133
+ """Return the stored feedback for a session (404 if not yet rated)."""
134
+ from .feedback_store import get_feedback as _get
135
+
136
+ fb = _get(session_id)
137
+ if fb is None:
138
+ raise HTTPException(
139
+ status_code=404, detail=f"no feedback for session: {session_id}"
140
+ )
141
+ return {"code": 0, "data": {"session_id": session_id, "feedback": fb}}
142
+
143
+
144
+ # ---------------------------------------------------------------------------
145
+ # New: Ticket agent dashboard endpoints (Phase 3 Path A)
146
+ # Path: /admin/tickets
147
+ # These endpoints output "new version" fields (including ticket_id / subject / priority / transcript),
148
+ # coexisting with the legacy field format of existing /handoff/{session_id}.
149
+ # ---------------------------------------------------------------------------
150
+ @router.get("/admin/tickets")
151
+ def admin_list_tickets(
152
+ limit: int = Query(default=50, ge=1, le=200),
153
+ status: Optional[str] = Query(default=None, max_length=32),
154
+ ) -> dict:
155
+ items = get_default_service().list_tickets(limit=limit, status=status)
156
+ return {
157
+ "code": 0,
158
+ "data": {
159
+ "items": [t.to_dict() for t in items],
160
+ "count": len(items),
161
+ },
162
+ }
163
+
164
+
165
+ @router.get("/admin/tickets/{ticket_id}")
166
+ def admin_get_ticket(ticket_id: str) -> dict:
167
+ status = get_default_service().query_ticket(ticket_id)
168
+ if status is None:
169
+ raise HTTPException(status_code=404, detail=f"ticket not found: {ticket_id}")
170
+ items = [
171
+ t for t in get_default_service().list_tickets(limit=200)
172
+ if t.ticket_id == ticket_id
173
+ ]
174
+ if not items:
175
+ raise HTTPException(status_code=404, detail=f"ticket not found: {ticket_id}")
176
+ return {"code": 0, "data": items[0].to_dict()}
177
+
178
+
179
+ @router.post("/admin/tickets/{ticket_id}/status")
180
+ def admin_update_status(ticket_id: str, body: AdminUpdateBody) -> dict:
181
+ # Validate status value
182
+ try:
183
+ TicketStatusEnum(body.status)
184
+ except ValueError as exc:
185
+ raise HTTPException(
186
+ status_code=400,
187
+ detail=f"invalid status: {body.status}",
188
+ ) from exc
189
+
190
+ try:
191
+ ticket = get_default_service().update_ticket_status(
192
+ ticket_id, body.status, agent_id=body.agent_id
193
+ )
194
+ except NotImplementedError as exc:
195
+ raise HTTPException(status_code=405, detail=str(exc)) from exc
196
+ except ValueError as exc:
197
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
198
+
199
+ if ticket is None:
200
+ raise HTTPException(status_code=404, detail=f"ticket not found: {ticket_id}")
201
+ return {"code": 0, "data": ticket.to_dict()}
@@ -0,0 +1,77 @@
1
+ """Best-effort linkage with session-summary (optional capability).
2
+
3
+ When a handoff ticket is created, auto-generate a session summary and attach it to the ticket,
4
+ so agents can see customer issue context directly in the ticket details — no need to manually click "generate summary".
5
+
6
+ Design principles:
7
+ - Soft dependency: silently no-ops when session-summary is not installed; does not affect the handoff main flow.
8
+ - Non-blocking: defaults to heuristic summary (local, zero latency); does not call LLM in the ticket creation chain.
9
+ - Decoupled: dynamically loaded via conversation-core's _capability_loader;
10
+ human-handoff has no static import dependency on session-summary.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import importlib.util
15
+ import logging
16
+ from pathlib import Path
17
+ from typing import Any, Optional
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ _loader: Optional[Any] = None
22
+ _loader_resolved = False
23
+
24
+
25
+ def _get_loader() -> Optional[Any]:
26
+ """Dynamically load conversation-core's _capability_loader (no relative imports, can load independently)."""
27
+ global _loader, _loader_resolved
28
+ if _loader_resolved:
29
+ return _loader
30
+ _loader_resolved = True
31
+ try:
32
+ # <root>/capabilities/human-handoff/src/summary_link.py → parents[3] = <root>
33
+ repo_root = Path(__file__).resolve().parents[3]
34
+ loader_path = (
35
+ repo_root / "capabilities" / "conversation-core" / "src" / "_capability_loader.py"
36
+ )
37
+ if not loader_path.is_file():
38
+ return None
39
+ spec = importlib.util.spec_from_file_location("_hh_capability_loader", loader_path)
40
+ if spec is None or spec.loader is None:
41
+ return None
42
+ mod = importlib.util.module_from_spec(spec)
43
+ spec.loader.exec_module(mod)
44
+ _loader = mod
45
+ except Exception as exc: # noqa: BLE001
46
+ logger.info("session-summary link unavailable: %s", exc)
47
+ _loader = None
48
+ return _loader
49
+
50
+
51
+ def attach_summary_to_ticket(ticket: Any) -> None:
52
+ """Generate an LLM narrative summary of the session chat and write it into the ticket's
53
+ Description field (from AI connect → handoff trigger).
54
+
55
+ The ticket Description becomes an LLM summary of the conversation, so agents see the
56
+ context directly without a separate "Session Summary" block. session-summary not
57
+ installed / any exception → silently skip (does not affect ticket creation main flow).
58
+ """
59
+ loader = _get_loader()
60
+ if loader is None:
61
+ return
62
+ try:
63
+ recorder_mod = loader.try_load_capability("session-summary", "src/recorder.py")
64
+ summarizer_mod = loader.try_load_capability("session-summary", "src/summarizer.py")
65
+ if recorder_mod is None or summarizer_mod is None:
66
+ return
67
+ session_id = ticket.user_id
68
+ recorder = recorder_mod.get_recorder()
69
+ rec = recorder.get(session_id)
70
+ if rec is None:
71
+ return # No transcript recorded for this session (e.g. manually inserted test ticket), skip
72
+ # LLM-generated one-paragraph summary of the chat → ticket Description.
73
+ paragraph = summarizer_mod.summarize_paragraph(rec)
74
+ if paragraph:
75
+ ticket.description = paragraph
76
+ except Exception as exc: # noqa: BLE001
77
+ logger.info("attach description summary skipped: %s", exc)