@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,227 @@
1
+ # human-handoff capability self-describing manifest
2
+ # Type: capability (optional install)
3
+
4
+ name: "human-handoff"
5
+ version: "1.1.0"
6
+ type: "capability"
7
+ description: "Semantic trigger for human handoff + ticket lifecycle (Phase 3 refactored to ports/adapters/core three-layer architecture)"
8
+
9
+ dependencies:
10
+ - name: "conversation-core"
11
+ version: ">=1.0.0,<2.0.0"
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Injection points (maintains full Phase 2 compatibility; trigger.py / queue.py are now facades)
15
+ # ---------------------------------------------------------------------------
16
+ extensions:
17
+ # Detect whether to trigger "handoff" before text injection; if matched, route to human channel
18
+ - inject_at: "agent.before_push_text"
19
+ inline_code: |
20
+ # [human-handoff] semantic trigger detection
21
+ from ._capability_loader import try_load_capability
22
+ _hh_trigger = try_load_capability("human-handoff", "src/trigger.py")
23
+ if _hh_trigger is not None:
24
+ _hand = _hh_trigger.maybe_handoff(session_id, text)
25
+ if _hand is not None:
26
+ text = _hand # Replace original text with queue/connect messaging to prevent LLM self-answering
27
+ # Release queue slot after agent stops
28
+ - inject_at: "agent.after_start"
29
+ inline_code: |
30
+ # [human-handoff] register session for handoff state machine
31
+ from ._capability_loader import try_load_capability
32
+ _hh_queue = try_load_capability("human-handoff", "src/queue.py")
33
+ if _hh_queue is not None:
34
+ _hh_queue.attach_session(session_id, info=info if 'info' in locals() else None)
35
+ - inject_at: "server.router_extension"
36
+ inline_code: |
37
+ # [human-handoff] mount sub-router
38
+ from ._capability_loader import try_load_capability as _try_load_capability
39
+ _hh_router_mod = _try_load_capability("human-handoff", "src/router.py")
40
+ if _hh_router_mod is not None and hasattr(_hh_router_mod, "router"):
41
+ app.include_router(
42
+ _hh_router_mod.router, prefix="/api/v1/handoff", tags=["human-handoff"]
43
+ )
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Configuration
47
+ # ---------------------------------------------------------------------------
48
+ config:
49
+ triggers:
50
+ description: "Strong trigger keyword list (exact match / case-insensitive)"
51
+ default:
52
+ - "talk to agent"
53
+ - "human support"
54
+ - "real person"
55
+ - "talk to agent"
56
+ - "speak to a human"
57
+ intent_keywords:
58
+ description: "Weak trigger keywords (subtracted from negative context)"
59
+ default:
60
+ - "complain"
61
+ - "manager"
62
+ - "supervisor"
63
+ - "not working"
64
+ queue_capacity:
65
+ description: "Local queue capacity (effective only for local_queue / mock adapter)"
66
+ default: 50
67
+ agent_pool_size:
68
+ description: "Number of available agents (auto-connect when >0)"
69
+ default: 1
70
+ estimated_wait_seconds_per_slot:
71
+ default: 30
72
+ retention_seconds:
73
+ description: "Retention duration for completed sessions (used for summary queries)"
74
+ default: 600
75
+ adapter:
76
+ description: "Which HandoffClient implementation to use: local_queue | mock | default_rest | user_custom"
77
+ default: "local_queue"
78
+ env: "HH_ADAPTER"
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # API endpoints
82
+ # ---------------------------------------------------------------------------
83
+ endpoints:
84
+ - method: GET
85
+ path: /api/v1/handoff/status
86
+ description: Query overall queue status
87
+ - method: GET
88
+ path: /api/v1/handoff/{session_id}
89
+ description: Query handoff status for a single session (legacy field format, backward compatible)
90
+ - method: POST
91
+ path: /api/v1/handoff/request
92
+ description: "Explicit handoff request submission (body shape: {session_id, reason?})"
93
+ - method: POST
94
+ path: /api/v1/handoff/connect
95
+ description: "Simulate agent connection (body shape: {session_id, agent_id})"
96
+ - method: POST
97
+ path: /api/v1/handoff/cancel
98
+ description: "Cancel queue (body shape: {session_id})"
99
+ # ----- Phase 3 new: Ticket agent dashboard endpoints -----
100
+ - method: GET
101
+ path: /api/v1/handoff/admin/tickets
102
+ description: "Ticket list (supports ?status=&limit= filtering)"
103
+ - method: GET
104
+ path: /api/v1/handoff/admin/tickets/{ticket_id}
105
+ description: "Ticket details"
106
+ - method: POST
107
+ path: /api/v1/handoff/admin/tickets/{ticket_id}/status
108
+ description: "Agent manually switches ticket status (pending|processing|closed|canceled|timeout)"
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Business contract (Phase 3 new; follows references/business-contract-spec.md v1.0)
112
+ # ---------------------------------------------------------------------------
113
+ business_contract:
114
+ port_class: "src.ports.handoff_client.HandoffClient"
115
+ default_adapter: "src.adapters.local_queue.LocalQueueHandoffClient"
116
+ mock_adapter: "src.adapters.mock.MockHandoffClient"
117
+ customization_sop: "INTERFACE_ADAPT.md"
118
+ external_apis:
119
+ - name: ticket.create
120
+ direction: outbound
121
+ method: POST
122
+ path: /tickets
123
+ description: "Create a new ticket in the ticketing system when user triggers handoff"
124
+ request_schema:
125
+ user_id: string
126
+ subject: string
127
+ description: string
128
+ priority: enum[low, normal, high, urgent]
129
+ transcript: string[]
130
+ response_schema:
131
+ ticket_id: string
132
+ queue_position: int
133
+ eta_seconds: int
134
+ adapter_slots:
135
+ - request.subject
136
+ - request.priority
137
+ - response.ticket_id
138
+ - response.queue_position
139
+ - response.eta_seconds
140
+ auth:
141
+ type: bearer
142
+ location: header
143
+ name: Authorization
144
+ timeout_ms: 5000
145
+
146
+ - name: ticket.status_query
147
+ direction: outbound
148
+ method: GET
149
+ path: /tickets/{ticket_id}
150
+ description: "Poll ticket status for queue progress updates"
151
+ request_schema:
152
+ ticket_id: string
153
+ response_schema:
154
+ ticket_id: string
155
+ status: enum[pending, processing, closed, canceled]
156
+ agent_id: string
157
+ updated_at: int
158
+ adapter_slots:
159
+ - response.status
160
+ - response.agent_id
161
+ timeout_ms: 3000
162
+
163
+ - name: ticket.cancel
164
+ direction: outbound
165
+ method: POST
166
+ path: /tickets/{ticket_id}/cancel
167
+ description: "Notify ticketing system when user cancels handoff"
168
+ request_schema:
169
+ ticket_id: string
170
+ reason: string
171
+ response_schema:
172
+ ticket_id: string
173
+ canceled: bool
174
+ adapter_slots:
175
+ - request.reason
176
+ timeout_ms: 3000
177
+
178
+ - name: ticket.status_callback
179
+ direction: inbound
180
+ method: POST
181
+ path: /api/v1/handoff/callback/ticket-status
182
+ description: "Callback from ticketing system to notify status changes (optional; when disabled, status_query polling is used instead)"
183
+ request_schema:
184
+ ticket_id: string
185
+ status: enum[pending, processing, closed, canceled]
186
+ agent_id: string
187
+ response_schema:
188
+ code: int
189
+ message: string
190
+ adapter_slots:
191
+ - request.status
192
+ - request.agent_id
193
+
194
+ # ---------------------------------------------------------------------------
195
+ # Integration
196
+ # ---------------------------------------------------------------------------
197
+ integration:
198
+ mode: "auto"
199
+ auto_adapters:
200
+ - tech_stack: ["react", "vue", "angular"]
201
+ adapter: "frontend-spa"
202
+ description: "Append queue status indicator component to SPA"
203
+ - tech_stack: ["express", "koa", "fastify", "next"]
204
+ adapter: "node-backend"
205
+ - tech_stack: ["flask", "fastapi", "django"]
206
+ adapter: "python-backend"
207
+ - tech_stack: ["spring-boot", "quarkus"]
208
+ adapter: "java-backend"
209
+ fallback:
210
+ guided_templates:
211
+ - "../../auto_adapters/integration_templates/generic-frontend.md"
212
+ manual_api:
213
+ rest_endpoint: "/api/v1/handoff"
214
+
215
+ security:
216
+ log_redaction:
217
+ enabled: true
218
+ patterns: ["phone", "mobile", "user_id", "credential", "authorization"]
219
+ network:
220
+ enforce_https: true # default_rest adapter enforces HTTPS for non-localhost
221
+
222
+ acceptance:
223
+ - "Keyword match immediately enters ticket flow, status pollable by frontend"
224
+ - "Connects instantly when agent pool > 0; returns estimated wait time when = 0"
225
+ - "Correctly releases slot when session is canceled / connected"
226
+ - "Switching HH_ADAPTER requires no business code changes (local_queue / mock / default_rest / user_custom)"
227
+ - "default_rest adapter rejects private network addresses (except localhost)"
@@ -0,0 +1,2 @@
1
+ """human-handoff capability: semantic-triggered handoff + queue status sync."""
2
+ __version__ = "1.0.0"
@@ -0,0 +1,9 @@
1
+ """human-handoff adapter implementations."""
2
+ from .factory import build_default, get_client, reset_client, set_client
3
+
4
+ __all__ = [
5
+ "build_default",
6
+ "get_client",
7
+ "reset_client",
8
+ "set_client",
9
+ ]
@@ -0,0 +1,242 @@
1
+ """DefaultRestHandoffClient — call external ticketing system per business_contract default contract.
2
+
3
+ Corresponding contracts:
4
+ - POST /tickets ticket.create
5
+ - GET /tickets/{ticket_id} ticket.status_query
6
+ - POST /tickets/{ticket_id}/cancel ticket.cancel
7
+
8
+ Environment variables:
9
+ - HH_REST_BASE_URL Ticketing system base URL (required; must not point to private network, see §Security)
10
+ - HH_REST_TOKEN Bearer Token (optional)
11
+ - HH_REST_TIMEOUT_MS Timeout (default 5000)
12
+
13
+ Security constraints (aligned with project security_rules):
14
+ - Only allow https:// or http://localhost / 127.0.0.1
15
+ - Default reject common private network ranges (9.* / 10.* / 11.* / 21.* / 30.* / 169.254.* / 172.16-31.* / 192.168.*)
16
+ - Log redaction auto-masks Authorization
17
+
18
+ Dependency: only requests (already in conversation-core/requirements.txt), no new deps.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import logging
23
+ import os
24
+ import re
25
+ from typing import List, Optional
26
+ from urllib.parse import urlparse
27
+
28
+ try:
29
+ import requests # type: ignore
30
+ except ImportError: # pragma: no cover
31
+ requests = None # type: ignore
32
+
33
+ from ..core.models import (
34
+ OverallStatus,
35
+ Ticket,
36
+ TicketStatus,
37
+ TicketStatusEnum,
38
+ now_ts,
39
+ )
40
+ from ..ports.handoff_client import HandoffClient
41
+
42
+
43
+ logger = logging.getLogger(__name__)
44
+
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Security: private network and loopback check
48
+ # ---------------------------------------------------------------------------
49
+ _PRIVATE_PATTERNS = [
50
+ re.compile(r"^9\."),
51
+ re.compile(r"^10\."),
52
+ re.compile(r"^11\."),
53
+ re.compile(r"^21\."),
54
+ re.compile(r"^30\."),
55
+ re.compile(r"^169\.254\."),
56
+ re.compile(r"^172\.(1[6-9]|2[0-9]|3[01])\."),
57
+ re.compile(r"^192\.168\."),
58
+ ]
59
+
60
+
61
+ def _is_localhost(host: str) -> bool:
62
+ return host in {"localhost", "127.0.0.1", "::1"}
63
+
64
+
65
+ def _is_private(host: str) -> bool:
66
+ return any(p.match(host) for p in _PRIVATE_PATTERNS)
67
+
68
+
69
+ def _validate_base_url(url: str) -> str:
70
+ parsed = urlparse(url)
71
+ if parsed.scheme not in {"http", "https"}:
72
+ raise ValueError(f"unsupported scheme: {parsed.scheme}")
73
+ host = parsed.hostname or ""
74
+ if not host:
75
+ raise ValueError("empty host in HH_REST_BASE_URL")
76
+ # Non-HTTPS only allowed for localhost
77
+ if parsed.scheme == "http" and not _is_localhost(host):
78
+ raise ValueError(
79
+ "non-HTTPS HH_REST_BASE_URL only allowed for localhost"
80
+ )
81
+ # Reject private network ranges (prevent SSRF)
82
+ if _is_private(host):
83
+ raise ValueError(
84
+ f"access to private network host '{host}' is denied; "
85
+ "set HH_REST_ALLOW_PRIVATE=1 to override (not recommended)"
86
+ )
87
+ return url.rstrip("/")
88
+
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # Client implementation
92
+ # ---------------------------------------------------------------------------
93
+ class DefaultRestHandoffClient(HandoffClient):
94
+ """Call external ticketing system per default REST contract."""
95
+
96
+ def __init__(
97
+ self,
98
+ *,
99
+ base_url: str,
100
+ token: Optional[str] = None,
101
+ timeout_ms: int = 5000,
102
+ ) -> None:
103
+ if requests is None:
104
+ raise RuntimeError(
105
+ "requests library is required for DefaultRestHandoffClient"
106
+ )
107
+ self._base = _validate_base_url(base_url)
108
+ self._token = token
109
+ self._timeout = max(0.5, timeout_ms / 1000.0)
110
+ self._session = requests.Session()
111
+
112
+ # ------------------------------------------------------------------
113
+ # HandoffClient required implementations
114
+ # ------------------------------------------------------------------
115
+ def create_ticket(
116
+ self,
117
+ *,
118
+ user_id: str,
119
+ subject: str = "",
120
+ description: str = "",
121
+ priority: str = "normal",
122
+ transcript: Optional[List[str]] = None,
123
+ ) -> Ticket:
124
+ payload = {
125
+ "user_id": user_id,
126
+ "subject": subject,
127
+ "description": description,
128
+ "priority": priority or "normal",
129
+ "transcript": list(transcript or []),
130
+ }
131
+ data = self._post("/tickets", payload)
132
+ ticket_id = str(data.get("ticket_id") or "").strip()
133
+ if not ticket_id:
134
+ raise RuntimeError("remote ticket service did not return ticket_id")
135
+ return Ticket(
136
+ ticket_id=ticket_id,
137
+ user_id=user_id,
138
+ subject=subject,
139
+ description=description,
140
+ priority=priority or "normal",
141
+ status=TicketStatusEnum.PENDING.value,
142
+ queue_position=int(data.get("queue_position") or 0),
143
+ eta_seconds=int(data.get("eta_seconds") or 0),
144
+ transcript=list(transcript or []),
145
+ reason=description[:128] if description else "",
146
+ created_at=now_ts(),
147
+ updated_at=now_ts(),
148
+ )
149
+
150
+ def query_status(self, ticket_id: str) -> Optional[TicketStatus]:
151
+ data = self._get(f"/tickets/{ticket_id}", optional=True)
152
+ if data is None:
153
+ return None
154
+ return TicketStatus(
155
+ ticket_id=str(data.get("ticket_id") or ticket_id),
156
+ status=str(data.get("status") or TicketStatusEnum.PENDING.value),
157
+ agent_id=data.get("agent_id"),
158
+ updated_at=float(data.get("updated_at") or 0.0) or None,
159
+ )
160
+
161
+ def cancel_ticket(self, ticket_id: str, reason: str = "") -> Optional[Ticket]:
162
+ data = self._post(
163
+ f"/tickets/{ticket_id}/cancel",
164
+ {"ticket_id": ticket_id, "reason": reason},
165
+ optional=True,
166
+ )
167
+ if data is None:
168
+ return None
169
+ return Ticket(
170
+ ticket_id=ticket_id,
171
+ user_id="", # remote may not return user_id
172
+ status=TicketStatusEnum.CANCELED.value,
173
+ reason=reason,
174
+ updated_at=now_ts(),
175
+ closed_at=now_ts(),
176
+ )
177
+
178
+ def overall_status(self) -> OverallStatus:
179
+ # Remote backend does not expose overall status; return placeholder (for /api/v1/handoff/status compatibility)
180
+ return OverallStatus(
181
+ agent_pool_size=-1,
182
+ available_agents=-1,
183
+ waiting=-1,
184
+ connected=-1,
185
+ capacity=-1,
186
+ )
187
+
188
+ # ------------------------------------------------------------------
189
+ # Internal:HTTP
190
+ # ------------------------------------------------------------------
191
+ def _headers(self) -> dict:
192
+ h = {"Content-Type": "application/json"}
193
+ if self._token:
194
+ h["Authorization"] = f"Bearer {self._token}"
195
+ return h
196
+
197
+ def _get(self, path: str, *, optional: bool = False):
198
+ url = self._base + path
199
+ resp = self._session.get(url, headers=self._headers(), timeout=self._timeout)
200
+ return self._handle(resp, optional=optional)
201
+
202
+ def _post(self, path: str, payload: dict, *, optional: bool = False):
203
+ url = self._base + path
204
+ resp = self._session.post(
205
+ url,
206
+ json=payload,
207
+ headers=self._headers(),
208
+ timeout=self._timeout,
209
+ )
210
+ return self._handle(resp, optional=optional)
211
+
212
+ @staticmethod
213
+ def _handle(resp, *, optional: bool):
214
+ if resp.status_code == 404 and optional:
215
+ return None
216
+ if resp.status_code >= 400:
217
+ # Do not print response body (may contain sensitive info)
218
+ raise RuntimeError(
219
+ f"remote ticket service returned HTTP {resp.status_code}"
220
+ )
221
+ try:
222
+ data = resp.json()
223
+ except ValueError as exc:
224
+ raise RuntimeError("remote ticket service returned non-JSON") from exc
225
+ # Response may be {"data": {...}} or flat {...}
226
+ if isinstance(data, dict) and "data" in data and isinstance(data["data"], dict):
227
+ return data["data"]
228
+ return data
229
+
230
+
231
+ # ---------------------------------------------------------------------------
232
+ # Factory
233
+ # ---------------------------------------------------------------------------
234
+ def from_env() -> Optional[DefaultRestHandoffClient]:
235
+ base = os.getenv("HH_REST_BASE_URL")
236
+ if not base:
237
+ return None
238
+ return DefaultRestHandoffClient(
239
+ base_url=base,
240
+ token=os.getenv("HH_REST_TOKEN"),
241
+ timeout_ms=int(os.getenv("HH_REST_TIMEOUT_MS", "5000")),
242
+ )
@@ -0,0 +1,89 @@
1
+ """adapter factory: selects HandoffClient implementation based on environment variable.
2
+
3
+ Environment variable `HH_ADAPTER`:
4
+ local_queue Default local in-memory queue (production-ready, zero dependencies)
5
+ mock Demo data (includes several preset tickets for video recording)
6
+ default_rest Call remote ticketing system per business_contract default contract
7
+ user_custom User integration wizard (contract-adapt.py) generated implementation
8
+
9
+ When not set or invalid, fall back to local_queue (keeps behavior compatibility with Phase 2).
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import os
15
+ from typing import Optional
16
+
17
+ from ..ports.handoff_client import HandoffClient
18
+
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ _VALID = ("local_queue", "mock", "default_rest", "user_custom")
24
+
25
+
26
+ def _build(name: str) -> Optional[HandoffClient]:
27
+ if name == "local_queue":
28
+ from .local_queue import from_env as build_local
29
+ return build_local()
30
+ if name == "mock":
31
+ from .mock import from_env as build_mock
32
+ return build_mock()
33
+ if name == "default_rest":
34
+ from .default_rest import from_env as build_rest
35
+ c = build_rest()
36
+ if c is None:
37
+ logger.warning(
38
+ "HH_ADAPTER=default_rest but HH_REST_BASE_URL is empty; "
39
+ "falling back to local_queue"
40
+ )
41
+ return c
42
+ if name == "user_custom":
43
+ try:
44
+ from .user_custom import from_env as build_custom # type: ignore
45
+ except ImportError:
46
+ logger.warning(
47
+ "HH_ADAPTER=user_custom but src/adapters/user_custom.py is missing; "
48
+ "run scripts/contract-adapt.py human-handoff to generate it"
49
+ )
50
+ return None
51
+ return build_custom()
52
+ return None
53
+
54
+
55
+ def build_default() -> HandoffClient:
56
+ """Build default client from environment variables; invalid config falls back to local_queue."""
57
+ name = (os.getenv("HH_ADAPTER") or "local_queue").strip().lower()
58
+ if name not in _VALID:
59
+ logger.warning("HH_ADAPTER=%s is not recognised; using local_queue", name)
60
+ name = "local_queue"
61
+ client = _build(name)
62
+ if client is None:
63
+ from .local_queue import from_env as build_local
64
+ client = build_local()
65
+ return client
66
+
67
+
68
+ # ---------------------------------------------------------------------------
69
+ # Global singleton
70
+ # ---------------------------------------------------------------------------
71
+ _singleton: Optional[HandoffClient] = None
72
+
73
+
74
+ def get_client() -> HandoffClient:
75
+ global _singleton
76
+ if _singleton is None:
77
+ _singleton = build_default()
78
+ return _singleton
79
+
80
+
81
+ def set_client(client: HandoffClient) -> None:
82
+ """For testing only: inject a custom client."""
83
+ global _singleton
84
+ _singleton = client
85
+
86
+
87
+ def reset_client() -> None:
88
+ global _singleton
89
+ _singleton = None