@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,165 @@
1
+ # session-summary capability self-describing manifest
2
+ # Type: capability (optional install)
3
+
4
+ name: "session-summary"
5
+ version: "1.0.0"
6
+ type: "capability"
7
+ description: "Automatic session summarization + structured summary output (turn recording / key fact extraction / LLM secondary summarization)"
8
+
9
+ dependencies:
10
+ - name: "conversation-core"
11
+ version: ">=1.0.0,<2.0.0"
12
+
13
+ # Optional integration (soft dependency, not hard dependency):
14
+ # When human-handoff is also installed, ticket creation will auto-trigger this capability
15
+ # to generate a summary for that session and attach it to the ticket. Agents can see the
16
+ # customer issue context directly in the dashboard ticket details (no need to manually click "generate summary").
17
+ # The integration is implemented as a best-effort call from human-handoff (capabilities/human-handoff/src/summary_link.py);
18
+ # when this capability is not installed, the call silently skips without affecting the handoff main flow.
19
+ optional_integrations:
20
+ - capability: "human-handoff"
21
+ trigger: "ticket.create"
22
+ behavior: "auto-finalize session summary (heuristic) and embed into ticket payload"
23
+
24
+ extensions:
25
+ # Archive as a user turn before text injection
26
+ - inject_at: "agent.before_push_text"
27
+ inline_code: |
28
+ # [session-summary] record user turn
29
+ from ._capability_loader import try_load_capability
30
+ _ss_recorder = try_load_capability("session-summary", "src/recorder.py")
31
+ if _ss_recorder is not None:
32
+ _ss_recorder.record_user_turn(session_id, text)
33
+ - inject_at: "agent.after_start"
34
+ inline_code: |
35
+ # [session-summary] open session
36
+ from ._capability_loader import try_load_capability
37
+ _ss_recorder = try_load_capability("session-summary", "src/recorder.py")
38
+ if _ss_recorder is not None:
39
+ _ss_recorder.open_session(session_id)
40
+ - inject_at: "server.router_extension"
41
+ inline_code: |
42
+ # [session-summary] mount sub-router
43
+ from ._capability_loader import try_load_capability as _try_load_capability
44
+ _ss_router_mod = _try_load_capability("session-summary", "src/router.py")
45
+ if _ss_router_mod is not None and hasattr(_ss_router_mod, "router"):
46
+ app.include_router(
47
+ _ss_router_mod.router, prefix="/api/v1/summary", tags=["session-summary"]
48
+ )
49
+
50
+ config:
51
+ adapter:
52
+ description: "Summary write-back target: mock (default, no external deps) | local_json | default_rest"
53
+ default: "mock"
54
+ env: "SS_ADAPTER"
55
+ options:
56
+ mock: "Log-only + returns mock record_id, suitable for local / Path A demos"
57
+ local_json: "Append to local JSONL (data/_writeback.jsonl)"
58
+ default_rest: "POST to real CRM / ticketing system (requires SS_REST_BASE_URL[/SS_REST_TOKEN])"
59
+ storage_dir:
60
+ description: "Summary file landing directory (one JSON per session)"
61
+ default: "capabilities/session-summary/data/"
62
+ llm_summary:
63
+ description: "Whether to enable LLM secondary summarization (consumes LLM_API_KEY; set false for offline scenarios)"
64
+ default: true
65
+ retention_days:
66
+ description: "Summary retention days; auto-cleanup after expiry"
67
+ default: 30
68
+ redact_patterns:
69
+ description: "Fields to auto-redact before writing summaries (same standard as skeleton log_redaction)"
70
+ default:
71
+ - "secret_id"
72
+ - "secret_key"
73
+ - "api_key"
74
+ - "token"
75
+
76
+ endpoints:
77
+ - method: GET
78
+ path: /api/v1/summary/{session_id}
79
+ description: Get single session summary (includes turn list and structured summary)
80
+ - method: POST
81
+ path: /api/v1/summary/{session_id}/finalize
82
+ description: Close session and trigger LLM secondary summarization
83
+ - method: GET
84
+ path: /api/v1/summary/_list
85
+ description: List most recent N summaries (_offset / _limit query)
86
+
87
+ integration:
88
+ mode: "auto"
89
+ auto_adapters:
90
+ - tech_stack: ["express", "koa", "fastify"]
91
+ adapter: "node-backend"
92
+ - tech_stack: ["flask", "fastapi", "django"]
93
+ adapter: "python-backend"
94
+ - tech_stack: ["react", "vue", "angular"]
95
+ adapter: "frontend-spa"
96
+ description: "Append summary viewer panel to SPA"
97
+ fallback:
98
+ guided_templates:
99
+ - "../../auto_adapters/integration_templates/generic-backend.md"
100
+ manual_api:
101
+ rest_endpoint: "/api/v1/summary"
102
+
103
+ security:
104
+ log_redaction:
105
+ enabled: true
106
+ patterns: ["secret_id", "secret_key", "api_key", "token", "credential"]
107
+ storage:
108
+ file_permission: "0600"
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Business contract (Phase 3 new; follows references/business-contract-spec.md v1.0)
112
+ # Note: Source code for this capability is not refactored in this phase (compromise strategy);
113
+ # only declares the external contract for contract-adapt.py consumption.
114
+ # ---------------------------------------------------------------------------
115
+ business_contract:
116
+ port_class: "src.adapters.base.SummarySink"
117
+ default_adapter: "src.adapters.default_rest.DefaultRestSink"
118
+ mock_adapter: "src.adapters.mock.MockSink"
119
+ customization_sop: "INTERFACE_ADAPT.md"
120
+ external_apis:
121
+ # CRM / Ticketing: write session summary back
122
+ - name: summary.write_to_crm
123
+ direction: outbound
124
+ method: POST
125
+ path: /sessions/{session_id}/summary
126
+ description: "After session finalize, write structured summary to user's CRM / ticketing / data platform"
127
+ request_schema:
128
+ session_id: string
129
+ user_id: string
130
+ topic: string
131
+ outcome: enum[resolved, transferred, abandoned, follow_up]
132
+ next_actions: string[]
133
+ full_transcript: string[]
134
+ structured_facts: object
135
+ finalized_at: int
136
+ response_schema:
137
+ record_id: string
138
+ accepted: bool
139
+ adapter_slots:
140
+ - request.topic
141
+ - request.outcome
142
+ - request.next_actions
143
+ - request.structured_facts
144
+ - response.record_id
145
+ auth:
146
+ type: bearer
147
+ location: header
148
+ name: Authorization
149
+ timeout_ms: 5000
150
+
151
+ # Secondary summarization call (reuses conversation-core LLM interface; declared here only as reference)
152
+ - name: summary.llm_summarize
153
+ direction: outbound
154
+ description: "Reuses conversation-core llm.chat_completions interface;
155
+ uses same LLM_API_URL / LLM_API_KEY config; no separate adaptation needed."
156
+ method: POST
157
+ path: "(see conversation-core: llm.chat_completions)"
158
+ request_schema: {}
159
+ response_schema: {}
160
+ adapter_slots: []
161
+
162
+ acceptance:
163
+ - "Each user/assistant turn persisted as JSONL"
164
+ - "After calling finalize, produce structured summary (includes topics / next_actions)"
165
+ - "File permissions set to 0600, sensitive fields auto-redacted"
@@ -0,0 +1,2 @@
1
+ """session-summary capability: session summary + structured abstract."""
2
+ __version__ = "1.0.0"
@@ -0,0 +1,5 @@
1
+ """session-summary write-back adapter layer."""
2
+ from .base import SummarySink
3
+ from .factory import get_sink
4
+
5
+ __all__ = ["SummarySink", "get_sink"]
@@ -0,0 +1,31 @@
1
+ """session-summary write-back adapter layer.
2
+
3
+ Consistent "three-tier adapter + factory" paradigm matching knowledge-base / human-handoff:
4
+ mock — Default; summary only logged + returns mock record_id, no external system needed,
5
+ allows local / Path A demos to immediately see "write-back success".
6
+ local_json — Append to local JSONL (data/_writeback.jsonl), can be checked offline.
7
+ default_rest — POST to user's real CRM / ticketing system (SS_REST_BASE_URL).
8
+
9
+ External contract see manifest.business_contract.external_apis[summary.write_to_crm].
10
+ When interface fields don't align, refer to INTERFACE_ADAPT.md for request/response mapping.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import abc
15
+ from typing import Any, Dict
16
+
17
+
18
+ class SummarySink(abc.ABC):
19
+ """Unified abstraction for session summary write-back targets."""
20
+
21
+ name: str = "base"
22
+
23
+ @abc.abstractmethod
24
+ def write(self, summary_record: Dict[str, Any]) -> Dict[str, Any]:
25
+ """Write a finalized summary record to the target system.
26
+
27
+ Returns
28
+ -------
29
+ dict: {"record_id": str, "accepted": bool, "sink": str}
30
+ """
31
+ raise NotImplementedError
@@ -0,0 +1,67 @@
1
+ """default_rest write-back sink — POST to user's real CRM / ticketing system.
2
+
3
+ Environment variables:
4
+ SS_REST_BASE_URL Required, e.g. https://crm.example.com/api
5
+ SS_REST_TOKEN Optional, Bearer Token
6
+ SS_REST_TIMEOUT_MS Optional, default 5000
7
+
8
+ Aligned with manifest.business_contract.external_apis[summary.write_to_crm]:
9
+ POST {base}/sessions/{session_id}/summary
10
+ Adjust _build_payload in this file or refer to INTERFACE_ADAPT.md when fields don't align.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import os
16
+ from typing import Any, Dict
17
+
18
+ from .base import SummarySink
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class DefaultRestSink(SummarySink):
24
+ name = "default_rest"
25
+
26
+ def __init__(self) -> None:
27
+ self._base = (os.getenv("SS_REST_BASE_URL") or "").rstrip("/")
28
+ self._token = os.getenv("SS_REST_TOKEN") or ""
29
+ self._timeout = max(int(os.getenv("SS_REST_TIMEOUT_MS", "5000")), 100) / 1000.0
30
+ if not self._base:
31
+ raise RuntimeError("SS_REST_BASE_URL not configured for default_rest sink")
32
+ # Security: forbid plaintext http (except localhost debugging), prevent credentials / summaries over unencrypted channels
33
+ if not self._base.startswith("https://") and "localhost" not in self._base:
34
+ raise RuntimeError(f"SS_REST_BASE_URL must use HTTPS: {self._base}")
35
+
36
+ def _build_payload(self, rec: Dict[str, Any]) -> Dict[str, Any]:
37
+ summary = rec.get("summary") or {}
38
+ return {
39
+ "session_id": rec.get("session_id"),
40
+ "topic": (summary.get("topics") or [None])[0],
41
+ "outcome": summary.get("outcome", "follow_up"),
42
+ "next_actions": summary.get("next_actions") or [],
43
+ "full_transcript": [t.get("text", "") for t in rec.get("turns") or []],
44
+ "structured_facts": summary,
45
+ "finalized_at": rec.get("closed_at"),
46
+ }
47
+
48
+ def write(self, summary_record: Dict[str, Any]) -> Dict[str, Any]:
49
+ import requests
50
+
51
+ sid = summary_record.get("session_id", "")
52
+ url = f"{self._base}/sessions/{sid}/summary"
53
+ headers = {"Content-Type": "application/json"}
54
+ if self._token:
55
+ headers["Authorization"] = f"Bearer {self._token}"
56
+ resp = requests.post(url, json=self._build_payload(summary_record),
57
+ headers=headers, timeout=self._timeout)
58
+ resp.raise_for_status()
59
+ try:
60
+ data = resp.json()
61
+ except ValueError:
62
+ data = {}
63
+ return {
64
+ "record_id": data.get("record_id", ""),
65
+ "accepted": bool(data.get("accepted", True)),
66
+ "sink": self.name,
67
+ }
@@ -0,0 +1,51 @@
1
+ """Write-back sink factory — selects implementation by env SS_ADAPTER (consistent with KB/handoff factory paradigm).
2
+
3
+ SS_ADAPTER=mock Default; no external dependencies
4
+ SS_ADAPTER=local_json Write local JSONL
5
+ SS_ADAPTER=default_rest POST to real CRM (requires SS_REST_BASE_URL)
6
+
7
+ When any implementation initialization fails (e.g. default_rest missing base_url), safely degrades to mock,
8
+ ensuring the finalize flow is never interrupted due to unavailable write-back targets.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ import os
14
+ import threading
15
+ from typing import Optional
16
+
17
+ from .base import SummarySink
18
+ from .mock import MockSink
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ _lock = threading.RLock()
23
+ _instance: Optional[SummarySink] = None
24
+ _instance_key: Optional[str] = None
25
+
26
+
27
+ def _build(name: str) -> SummarySink:
28
+ name = (name or "mock").strip().lower()
29
+ if name == "local_json":
30
+ from .local_json import LocalJsonSink
31
+ return LocalJsonSink()
32
+ if name == "default_rest":
33
+ from .default_rest import DefaultRestSink
34
+ return DefaultRestSink()
35
+ return MockSink()
36
+
37
+
38
+ def get_sink() -> SummarySink:
39
+ """Return the currently configured write-back sink (cached by SS_ADAPTER; rebuilds on env change)."""
40
+ global _instance, _instance_key
41
+ key = (os.getenv("SS_ADAPTER", "mock") or "mock").strip().lower()
42
+ with _lock:
43
+ if _instance is not None and _instance_key == key:
44
+ return _instance
45
+ try:
46
+ _instance = _build(key)
47
+ except Exception as exc: # noqa: BLE001
48
+ logger.warning("session-summary sink '%s' init failed, fallback to mock: %s", key, exc)
49
+ _instance = MockSink()
50
+ _instance_key = key
51
+ return _instance
@@ -0,0 +1,42 @@
1
+ """local_json write-back sink — append to local JSONL for offline verification."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ import os
7
+ import threading
8
+ import time
9
+ from pathlib import Path
10
+ from typing import Any, Dict
11
+
12
+ from .base import SummarySink
13
+
14
+ _LOCK = threading.RLock()
15
+
16
+
17
+ class LocalJsonSink(SummarySink):
18
+ name = "local_json"
19
+
20
+ def __init__(self) -> None:
21
+ base = os.getenv(
22
+ "SS_STORAGE_DIR",
23
+ str(Path(__file__).resolve().parents[2] / "data"),
24
+ )
25
+ self._file = Path(base) / "_writeback.jsonl"
26
+ self._file.parent.mkdir(parents=True, exist_ok=True)
27
+
28
+ def write(self, summary_record: Dict[str, Any]) -> Dict[str, Any]:
29
+ sid = str(summary_record.get("session_id", ""))
30
+ record_id = "CRM-LOCAL-" + hashlib.md5(sid.encode("utf-8")).hexdigest()[:10].upper()
31
+ line = json.dumps(
32
+ {"record_id": record_id, "written_at": int(time.time()), "record": summary_record},
33
+ ensure_ascii=False,
34
+ )
35
+ with _LOCK:
36
+ with self._file.open("a", encoding="utf-8") as f:
37
+ f.write(line + "\n")
38
+ try:
39
+ os.chmod(self._file, 0o600)
40
+ except OSError:
41
+ pass
42
+ return {"record_id": record_id, "accepted": True, "sink": self.name}
@@ -0,0 +1,22 @@
1
+ """mock write-back sink — default implementation, no external dependencies, suitable for demos."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import logging
6
+ from typing import Any, Dict
7
+
8
+ from .base import SummarySink
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class MockSink(SummarySink):
14
+ name = "mock"
15
+
16
+ def write(self, summary_record: Dict[str, Any]) -> Dict[str, Any]:
17
+ sid = str(summary_record.get("session_id", ""))
18
+ record_id = "CRM-MOCK-" + hashlib.md5(sid.encode("utf-8")).hexdigest()[:10].upper()
19
+ topics = (summary_record.get("summary") or {}).get("topics") or []
20
+ logger.info("[session-summary][mock] write-back session=%s topics=%s -> %s",
21
+ sid, topics[:3], record_id)
22
+ return {"record_id": record_id, "accepted": True, "sink": self.name, "_mock": True}
@@ -0,0 +1,210 @@
1
+ """Session turn recorder.
2
+
3
+ Persistence strategy:
4
+ - Each session generates ``{session_id}.json`` under storage_dir, permissions 0600.
5
+ - Sensitive fields redacted before writing (same standard as skeleton log_filter).
6
+ - File structure:
7
+ {
8
+ "session_id": "...",
9
+ "opened_at": 1717830000.0,
10
+ "closed_at": null,
11
+ "turns": [
12
+ {"role": "user", "ts": 1717830001.0, "text": "..."},
13
+ {"role": "assistant", "ts": 1717830002.0, "text": "..."}
14
+ ],
15
+ "summary": null # Populated after finalize
16
+ }
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import os
22
+ import re
23
+ import threading
24
+ import time
25
+ from dataclasses import dataclass, field
26
+ from pathlib import Path
27
+ from typing import Any, Dict, List, Optional
28
+
29
+
30
+ _DEFAULT_STORAGE_DIR = Path(
31
+ os.getenv(
32
+ "SS_STORAGE_DIR",
33
+ str(Path(__file__).resolve().parent.parent / "data"),
34
+ )
35
+ )
36
+ _RETENTION_DAYS = int(os.getenv("SS_RETENTION_DAYS", "30"))
37
+
38
+
39
+ # Default redaction mode (aligned with skeleton log_filter)
40
+ _REDACT_PATTERNS = [
41
+ re.compile(r"(?i)(secret_id|secret_key|api_key|token|credential|authorization)\s*[:=]\s*([^\s,'\"\\]+)"),
42
+ ]
43
+
44
+
45
+ def _redact(text: str) -> str:
46
+ if not text:
47
+ return text
48
+ out = text
49
+ for pat in _REDACT_PATTERNS:
50
+ out = pat.sub(lambda m: f"{m.group(1)}=***", out)
51
+ return out
52
+
53
+
54
+ @dataclass
55
+ class Turn:
56
+ role: str
57
+ text: str
58
+ ts: float = field(default_factory=time.time)
59
+
60
+ def to_dict(self) -> dict:
61
+ return {"role": self.role, "text": self.text, "ts": self.ts}
62
+
63
+
64
+ @dataclass
65
+ class SessionRecord:
66
+ session_id: str
67
+ opened_at: float = field(default_factory=time.time)
68
+ closed_at: Optional[float] = None
69
+ turns: List[Turn] = field(default_factory=list)
70
+ summary: Optional[Dict[str, Any]] = None
71
+
72
+ def to_dict(self) -> dict:
73
+ return {
74
+ "session_id": self.session_id,
75
+ "opened_at": self.opened_at,
76
+ "closed_at": self.closed_at,
77
+ "turns": [t.to_dict() for t in self.turns],
78
+ "summary": self.summary,
79
+ }
80
+
81
+
82
+ class Recorder:
83
+ def __init__(self, storage_dir: Optional[Path] = None) -> None:
84
+ self._lock = threading.RLock()
85
+ self._dir = Path(storage_dir or _DEFAULT_STORAGE_DIR)
86
+ self._dir.mkdir(parents=True, exist_ok=True)
87
+ self._cache: Dict[str, SessionRecord] = {}
88
+ self._cleanup_old_files()
89
+
90
+ @property
91
+ def storage_dir(self) -> Path:
92
+ return self._dir
93
+
94
+ def open(self, session_id: str) -> SessionRecord:
95
+ with self._lock:
96
+ rec = self._cache.get(session_id)
97
+ if rec is not None:
98
+ return rec
99
+ path = self._path(session_id)
100
+ if path.exists():
101
+ rec = self._load(path)
102
+ else:
103
+ rec = SessionRecord(session_id=session_id)
104
+ self._cache[session_id] = rec
105
+ self._persist(rec)
106
+ return rec
107
+
108
+ def add_turn(self, session_id: str, role: str, text: str) -> None:
109
+ if not text or not text.strip():
110
+ return
111
+ if role not in ("user", "assistant", "system", "tool"):
112
+ return
113
+ with self._lock:
114
+ rec = self._cache.get(session_id) or self.open(session_id)
115
+ rec.turns.append(Turn(role=role, text=_redact(text)[:4096]))
116
+ self._persist(rec)
117
+
118
+ def get(self, session_id: str) -> Optional[SessionRecord]:
119
+ with self._lock:
120
+ rec = self._cache.get(session_id)
121
+ if rec is not None:
122
+ return rec
123
+ path = self._path(session_id)
124
+ if path.exists():
125
+ rec = self._load(path)
126
+ self._cache[session_id] = rec
127
+ return rec
128
+ return None
129
+
130
+ def list_recent(self, offset: int = 0, limit: int = 20) -> List[Dict[str, Any]]:
131
+ files = sorted(
132
+ self._dir.glob("*.json"),
133
+ key=lambda p: p.stat().st_mtime,
134
+ reverse=True,
135
+ )[offset : offset + limit]
136
+ out: List[Dict[str, Any]] = []
137
+ for p in files:
138
+ try:
139
+ out.append(json.loads(p.read_text(encoding="utf-8")))
140
+ except (OSError, json.JSONDecodeError):
141
+ continue
142
+ return out
143
+
144
+ def finalize(self, session_id: str, summary: Dict[str, Any]) -> SessionRecord:
145
+ with self._lock:
146
+ rec = self.get(session_id)
147
+ if rec is None:
148
+ raise ValueError(f"session not found: {session_id}")
149
+ rec.closed_at = time.time()
150
+ rec.summary = summary
151
+ self._persist(rec)
152
+ return rec
153
+
154
+ # ------------------------------------------------------------------
155
+ def _path(self, session_id: str) -> Path:
156
+ safe = re.sub(r"[^A-Za-z0-9_\-]", "_", session_id)[:64]
157
+ return self._dir / f"{safe}.json"
158
+
159
+ def _load(self, path: Path) -> SessionRecord:
160
+ data = json.loads(path.read_text(encoding="utf-8"))
161
+ rec = SessionRecord(
162
+ session_id=data.get("session_id", path.stem),
163
+ opened_at=float(data.get("opened_at", time.time())),
164
+ closed_at=data.get("closed_at"),
165
+ summary=data.get("summary"),
166
+ )
167
+ for t in data.get("turns") or []:
168
+ rec.turns.append(Turn(role=t.get("role", "user"), text=t.get("text", ""), ts=float(t.get("ts", 0))))
169
+ return rec
170
+
171
+ def _persist(self, rec: SessionRecord) -> None:
172
+ path = self._path(rec.session_id)
173
+ tmp = path.with_suffix(".json.tmp")
174
+ tmp.write_text(json.dumps(rec.to_dict(), ensure_ascii=False, indent=2), encoding="utf-8")
175
+ os.replace(tmp, path)
176
+ try:
177
+ os.chmod(path, 0o600)
178
+ except OSError:
179
+ pass
180
+
181
+ def _cleanup_old_files(self) -> None:
182
+ cutoff = time.time() - _RETENTION_DAYS * 86400
183
+ for p in self._dir.glob("*.json"):
184
+ try:
185
+ if p.stat().st_mtime < cutoff:
186
+ p.unlink()
187
+ except OSError:
188
+ continue
189
+
190
+
191
+ # ---------------------------------------------------------------------------
192
+ # Global singleton + manifest.extensions injection functions
193
+ # ---------------------------------------------------------------------------
194
+ _global_recorder = Recorder()
195
+
196
+
197
+ def get_recorder() -> Recorder:
198
+ return _global_recorder
199
+
200
+
201
+ def open_session(session_id: str) -> None:
202
+ _global_recorder.open(session_id)
203
+
204
+
205
+ def record_user_turn(session_id: str, text: str) -> None:
206
+ _global_recorder.add_turn(session_id, "user", text)
207
+
208
+
209
+ def record_assistant_turn(session_id: str, text: str) -> None:
210
+ _global_recorder.add_turn(session_id, "assistant", text)