@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.
- package/.cursor/rules/main.mdc +12 -0
- package/AGENTS.md +14 -104
- package/CLAUDE.md +15 -127
- package/CODEBUDDY.md +32 -118
- package/README.md +7 -5
- package/README.zh.md +7 -5
- package/bin/cli.js +133 -41
- package/hooks/__pycache__/cursor-adapter.cpython-313.pyc +0 -0
- package/hooks/cursor-adapter.py +45 -18
- package/hooks/hooks-cursor.json +5 -18
- package/hooks/hooks.json +6 -31
- package/knowledge-base/conference/web/index.yaml +143 -0
- package/knowledge-base/platform-slice-template.md +1041 -133
- package/knowledge-base/products.yaml +76 -0
- package/knowledge-base/scenario-spec.md +1316 -115
- package/knowledge-base/scenarios/conference/base/general-conference.md +22 -2
- package/knowledge-base/scenarios/conference/base/webinar-conference.md +14 -0
- package/knowledge-base/scenarios/conference/medical/1v1-video-consultation.md +19 -0
- package/knowledge-base/scenarios/conference/medical/medical-multidoctor-consultation.md +13 -0
- package/knowledge-base/scenarios/live/entertainment-live-room.md +12 -0
- package/knowledge-base/slice-spec.md +2377 -299
- package/knowledge-base/slices/conference/web/integration-audit.md +1 -1
- package/knowledge-base/slices/conference/web/login-auth.md +14 -1
- package/knowledge-base/tooling/aliases.yaml +92 -0
- package/knowledge-base/tooling/intent-signals.yaml +181 -0
- package/knowledge-base/tooling/symptom-keywords.yaml +21 -0
- package/package.json +1 -1
- package/skills/trtc/SKILL.md +202 -245
- package/skills/trtc/hooks/__pycache__/report_prompt.cpython-313.pyc +0 -0
- package/skills/{trtc-topic/guardrails → trtc/hooks}/gate_slice_read.py +12 -8
- package/skills/{trtc-topic/guardrails → trtc/hooks}/gate_slice_write.py +16 -12
- package/skills/{trtc-topic/guardrails → trtc/hooks}/stop_require_apply_evidence.py +22 -14
- package/skills/trtc/hooks/topic_phase_gate.py +161 -0
- package/skills/{trtc-topic → trtc}/runtime/README.md +2 -2
- package/skills/{trtc-onboarding/reference/reporting-protocol.md → trtc/runtime/REPORTING.md} +5 -5
- package/skills/{trtc-topic → trtc}/runtime/RUNTIME.md +5 -5
- package/skills/trtc/runtime/lib/__init__.py +0 -0
- package/skills/{trtc-topic → trtc}/runtime/package-lock.json +2 -2
- package/skills/{trtc-topic → trtc}/runtime/package.json +2 -2
- package/skills/{trtc-topic → trtc}/runtime/telemetry-bridge.mjs +1 -1
- package/skills/{trtc-topic/scripts → trtc/tools}/STATE-MACHINE-GUIDE.md +23 -23
- package/skills/trtc/tools/__init__.py +2 -0
- package/skills/trtc/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/trtc/tools/__pycache__/query_classifier.cpython-313.pyc +0 -0
- package/skills/trtc/tools/__pycache__/reporting.cpython-313.pyc +0 -0
- package/skills/trtc/tools/__pycache__/search.cpython-313.pyc +0 -0
- package/skills/trtc/tools/__pycache__/session.cpython-313.pyc +0 -0
- package/skills/trtc/tools/apply.py +540 -0
- package/skills/trtc/tools/docs.py +712 -0
- package/skills/trtc/tools/docsbot.py +182 -0
- package/skills/trtc/tools/entry/render_ai_instructions.py +92 -0
- package/skills/trtc/tools/flow.py +1089 -0
- package/skills/{trtc-topic/scripts → trtc/tools}/init_slice_queue.py +5 -4
- package/skills/{trtc-topic/scripts → trtc/tools}/next_slice.py +5 -4
- package/skills/trtc/tools/query_classifier.py +301 -0
- package/skills/trtc/tools/reporting.py +447 -0
- package/skills/trtc/tools/search.py +817 -0
- package/skills/trtc/tools/session.py +1261 -0
- package/skills/trtc/tools/state_machine.py +690 -0
- package/skills/trtc-ai-service/README.md +195 -0
- package/skills/trtc-ai-service/README.zh-CN.md +193 -0
- package/skills/trtc-ai-service/SKILL.md +945 -0
- package/skills/trtc-ai-service/auto_adapters/README.md +40 -0
- package/skills/trtc-ai-service/auto_adapters/frontend-spa/README.md +27 -0
- package/skills/trtc-ai-service/auto_adapters/frontend-spa/angular/voice-agent.component.ts.tpl +131 -0
- package/skills/trtc-ai-service/auto_adapters/frontend-spa/manifest.yaml +57 -0
- package/skills/trtc-ai-service/auto_adapters/frontend-spa/react/VoiceAgent.tsx.tpl +142 -0
- package/skills/trtc-ai-service/auto_adapters/frontend-spa/vue/VoiceAgent.vue.tpl +121 -0
- package/skills/trtc-ai-service/auto_adapters/integration_templates/generic-backend.md +45 -0
- package/skills/trtc-ai-service/auto_adapters/integration_templates/generic-frontend.md +51 -0
- package/skills/trtc-ai-service/auto_adapters/integration_templates/generic-rest-api.md +93 -0
- package/skills/trtc-ai-service/auto_adapters/java-backend/README.md +25 -0
- package/skills/trtc-ai-service/auto_adapters/java-backend/manifest.yaml +30 -0
- package/skills/trtc-ai-service/auto_adapters/java-backend/quarkus/VoiceAgentFilter.java.tpl +64 -0
- package/skills/trtc-ai-service/auto_adapters/java-backend/springboot/VoiceAgentFilter.java.tpl +91 -0
- package/skills/trtc-ai-service/auto_adapters/manifest.yaml +43 -0
- package/skills/trtc-ai-service/auto_adapters/node-backend/README.md +25 -0
- package/skills/trtc-ai-service/auto_adapters/node-backend/express.js.tpl +40 -0
- package/skills/trtc-ai-service/auto_adapters/node-backend/fastify.js.tpl +27 -0
- package/skills/trtc-ai-service/auto_adapters/node-backend/koa.js.tpl +31 -0
- package/skills/trtc-ai-service/auto_adapters/node-backend/manifest.yaml +47 -0
- package/skills/trtc-ai-service/auto_adapters/python-backend/README.md +22 -0
- package/skills/trtc-ai-service/auto_adapters/python-backend/django.py.tpl +32 -0
- package/skills/trtc-ai-service/auto_adapters/python-backend/fastapi.py.tpl +35 -0
- package/skills/trtc-ai-service/auto_adapters/python-backend/flask.py.tpl +31 -0
- package/skills/trtc-ai-service/auto_adapters/python-backend/manifest.yaml +45 -0
- package/skills/trtc-ai-service/capabilities/__init__.py +43 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/.env.example +29 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/INTEGRATION.md +134 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/INTERFACE_ADAPT.md +111 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/QUICK_START.md +62 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/manifest.yaml +250 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/requirements.txt +6 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/__init__.py +10 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/_capability_loader.py +218 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/agent.py +231 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/credentials.py +132 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/health.py +355 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/log_filter.py +76 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/modality.py +109 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/server.py +312 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/trtc_client.py +315 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/src/usersig.py +90 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/tests/test_skeleton.py +216 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/README.md +48 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/app.js +415 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/index.html +68 -0
- package/skills/trtc-ai-service/capabilities/conversation-core/web-demo/styles.css +136 -0
- package/skills/trtc-ai-service/capabilities/digital-human/README.md +31 -0
- package/skills/trtc-ai-service/capabilities/digital-human/manifest.yaml +64 -0
- package/skills/trtc-ai-service/capabilities/digital-human/src/__init__.py +2 -0
- package/skills/trtc-ai-service/capabilities/digital-human/src/router.py +43 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/INTERFACE_ADAPT.md +353 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/README.md +44 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/manifest.yaml +227 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/__init__.py +2 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/__init__.py +9 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/default_rest.py +242 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/factory.py +89 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/local_queue.py +258 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/adapters/mock.py +132 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/core/__init__.py +25 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/core/intent_detector.py +75 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/core/models.py +163 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/core/service.py +192 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/feedback_store.py +54 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/ports/__init__.py +4 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/ports/handoff_client.py +86 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/queue.py +62 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/router.py +201 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/summary_link.py +77 -0
- package/skills/trtc-ai-service/capabilities/human-handoff/src/trigger.py +25 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/INTERFACE_ADAPT.md +297 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/README.md +51 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/data/faq.json +20 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/manifest.yaml +211 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/__init__.py +8 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/__init__.py +9 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/default_rest.py +209 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/factory.py +86 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/local_json.py +172 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/adapters/mock.py +91 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/__init__.py +12 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/models.py +77 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/scoring.py +73 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/core/service.py +78 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/ports/__init__.py +4 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/ports/kb_client.py +61 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/retriever.py +56 -0
- package/skills/trtc-ai-service/capabilities/knowledge-base/src/router.py +85 -0
- package/skills/trtc-ai-service/capabilities/session-summary/INTERFACE_ADAPT.md +99 -0
- package/skills/trtc-ai-service/capabilities/session-summary/README.md +47 -0
- package/skills/trtc-ai-service/capabilities/session-summary/data/test_session.json +18 -0
- package/skills/trtc-ai-service/capabilities/session-summary/manifest.yaml +165 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/__init__.py +2 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/__init__.py +5 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/base.py +31 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/default_rest.py +67 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/factory.py +51 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/local_json.py +42 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/adapters/mock.py +22 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/recorder.py +210 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/router.py +93 -0
- package/skills/trtc-ai-service/capabilities/session-summary/src/summarizer.py +163 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/INTERFACE_ADAPT.md +158 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/README.md +50 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/data/tools.yaml +58 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/examples/__init__.py +1 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/examples/local_tools.py +101 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/manifest.yaml +146 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/src/__init__.py +8 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/src/dispatcher.py +54 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/src/registry.py +219 -0
- package/skills/trtc-ai-service/capabilities/tool-calling/src/router.py +50 -0
- package/skills/trtc-ai-service/references/business-contract-spec.md +263 -0
- package/skills/trtc-ai-service/scenarios/custom-builder/README.md +86 -0
- package/skills/trtc-ai-service/scenarios/custom-builder/output-templates/recipe.yaml.j2 +194 -0
- package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q1-business-scenario.md +43 -0
- package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q2-io-modality.md +57 -0
- package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q3-ui-form.md +55 -0
- package/skills/trtc-ai-service/scenarios/custom-builder/prompts/q4-capabilities.md +78 -0
- package/skills/trtc-ai-service/scenarios/customer-service/README.md +114 -0
- package/skills/trtc-ai-service/scenarios/customer-service/recipe.yaml +154 -0
- package/skills/trtc-ai-service/scenarios/customer-service/sample-data/README.md +32 -0
- package/skills/trtc-ai-service/scenarios/customer-service/sample-data/faq-sample.json +37 -0
- package/skills/trtc-ai-service/scenarios/customer-service/system-prompt.template.md +94 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/app.js +347 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/index.html +125 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/styles.css +487 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/admin-board/tokens.css +71 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/design-system/DESIGN_GUIDELINES.md +370 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/README.md +68 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/app.js +1307 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/data.js +40 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/index.html +233 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/mock-shop.json +21 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/styles.css +603 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/voice-customer-service/tokens.css +71 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/agent-link.js +323 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/app.js +458 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/index.html +109 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/styles.css +489 -0
- package/skills/trtc-ai-service/scenarios/customer-service/ui/widget-floating/tokens.css +59 -0
- package/skills/trtc-ai-service/scripts/add-capability.py +364 -0
- package/skills/trtc-ai-service/scripts/contract-adapt.py +334 -0
- package/skills/trtc-ai-service/scripts/detect-stack.py +40 -0
- package/skills/trtc-ai-service/scripts/lib/__init__.py +20 -0
- package/skills/trtc-ai-service/scripts/lib/adapter_codegen.py +509 -0
- package/skills/trtc-ai-service/scripts/lib/arbitrator.py +152 -0
- package/skills/trtc-ai-service/scripts/lib/contract_resolver.py +519 -0
- package/skills/trtc-ai-service/scripts/lib/credential_validators.py +253 -0
- package/skills/trtc-ai-service/scripts/lib/curl_parser.py +303 -0
- package/skills/trtc-ai-service/scripts/lib/degrader.py +140 -0
- package/skills/trtc-ai-service/scripts/lib/injector.py +347 -0
- package/skills/trtc-ai-service/scripts/lib/manifest_resolver.py +288 -0
- package/skills/trtc-ai-service/scripts/lib/openapi_parser.py +289 -0
- package/skills/trtc-ai-service/scripts/lib/stack_detector.py +159 -0
- package/skills/trtc-ai-service/scripts/lib/tokens_compile.py +204 -0
- package/skills/trtc-ai-service/scripts/post-install-patch.py +225 -0
- package/skills/trtc-ai-service/scripts/setup-credentials.py +393 -0
- package/skills/trtc-ai-service/scripts/verify-credentials.py +108 -0
- package/skills/trtc-ai-service/start.sh +111 -0
- package/skills/trtc-ai-service/tests/__init__.py +1 -0
- package/skills/trtc-ai-service/tests/test_arbitrator.py +64 -0
- package/skills/trtc-ai-service/tests/test_capability_overlay.py +85 -0
- package/skills/trtc-ai-service/tests/test_contract_resolver.py +190 -0
- package/skills/trtc-ai-service/tests/test_handoff_ports.py +195 -0
- package/skills/trtc-ai-service/tests/test_kb_ports.py +195 -0
- package/skills/trtc-ai-service/tests/test_manifest_resolver.py +95 -0
- package/skills/trtc-ai-service/tests/test_recipe_assembly.py +175 -0
- package/skills/trtc-ai-service/tests/test_stack_and_degrader.py +101 -0
- package/skills/trtc-ai-service/tests/test_verify_credentials.py +285 -0
- package/skills/trtc-ai-service/triggers.yaml +29 -0
- package/skills/trtc-conference/SKILL.md +324 -0
- package/skills/trtc-conference/flows/onboarding.md +205 -0
- package/skills/trtc-conference/flows/topic.md +474 -0
- package/skills/trtc-conference/flows/troubleshoot.md +85 -0
- package/skills/trtc-conference/hooks/pretooluse_require_business_decisions.py +213 -0
- package/skills/trtc-conference/playbooks/medical-quickstart.md +84 -0
- package/skills/trtc-conference/playbooks/official-roomkit.md +97 -0
- package/skills/trtc-conference/references/local-usersig/basic-info-config.ts +39 -0
- package/skills/trtc-conference/references/usersig-handling.md +134 -0
- package/skills/trtc-conference/templates/medical-consultation/src/config/lib-generate-test-usersig-es.min.d.ts +4 -0
- package/skills/trtc-conference/templates/medical-consultation/src/config/lib-generate-test-usersig-es.min.js +2 -0
- package/skills/trtc-conference/tests/__pycache__/test_conference_onboarding_contract.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-conference/tests/__pycache__/test_conference_topic_flow_contract.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-conference/tests/test_conference_index_contract.py +43 -0
- package/skills/trtc-conference/tests/test_conference_onboarding_contract.py +103 -0
- package/skills/trtc-conference/tests/test_conference_template_contract.py +25 -0
- package/skills/trtc-conference/tests/test_conference_topic_flow_contract.py +132 -0
- package/skills/trtc-conference/tools/apply_checks.py +328 -0
- package/skills/trtc-conference/verify_lib/__init__.py +0 -0
- package/skills/{trtc-apply/guardrails/apply_lib → trtc-conference/verify_lib}/rule_parser.py +1 -1
- package/skills/trtc-docs/SKILL.md +91 -119
- package/.cursor/rules/ui-mode.mdc +0 -99
- package/ai-instructions/base.md +0 -13
- package/ai-instructions/ui-mode.md +0 -93
- package/knowledge-base/index.yaml +0 -462
- package/skills/trtc/room-builder/SKILL.md +0 -133
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/README.md +0 -108
- package/skills/trtc/room-builder/tools/render_ai_instructions.py +0 -226
- package/skills/trtc-apply/SKILL.md +0 -97
- package/skills/trtc-onboarding/SKILL.md +0 -841
- package/skills/trtc-onboarding/reference/path-a1-demo.md +0 -103
- package/skills/trtc-onboarding/reference/path-a2-integrate.md +0 -737
- package/skills/trtc-onboarding/reference/path-b-troubleshoot.md +0 -186
- package/skills/trtc-onboarding/reference/path-c-expand.md +0 -43
- package/skills/trtc-onboarding/reference/supported-matrix.md +0 -100
- package/skills/trtc-search/SKILL.md +0 -228
- package/skills/trtc-topic/SKILL.md +0 -622
- package/skills/trtc-topic/scripts/apply.py +0 -581
- package/skills/trtc-topic/scripts/lib/state_machine.py +0 -328
- package/skills/trtc-topic/tests/README.md +0 -70
- package/skills/trtc-topic/tests/conftest.py +0 -72
- package/skills/trtc-topic/tests/test_apply_cli.py +0 -480
- package/skills/trtc-topic/tests/test_end_to_end.py +0 -305
- package/skills/trtc-topic/tests/test_finalize_session.py +0 -51
- package/skills/trtc-topic/tests/test_gates.py +0 -316
- package/skills/trtc-topic/tests/test_session_resolver.py +0 -260
- package/skills/trtc-topic/tests/test_state_machine.py +0 -414
- package/skills/trtc-topic/tests/test_stop_require_apply.py +0 -99
- package/skills/trtc-topic/tests/test_topic_skill_invariants.py +0 -130
- /package/skills/{trtc-topic → trtc}/runtime/lib/platforms.py +0 -0
- /package/skills/{trtc-topic → trtc}/runtime/telemetry_collector.py +0 -0
- /package/skills/{trtc-onboarding/reference → trtc/runtime}/usersig-handling.md +0 -0
- /package/skills/{trtc-topic/scripts → trtc/tools}/finalize_session.py +0 -0
- /package/skills/{trtc-apply/guardrails/apply_lib → trtc-ai-service/capabilities/conversation-core/tests}/__init__.py +0 -0
- /package/skills/{trtc-topic → trtc-conference}/references/execution-units.yaml +0 -0
- /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
- /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
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/docs/backend-contract.zh-CN.md +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/docs/integration.zh-CN.md +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/docs/theme.zh-CN.md +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/index.html +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/package.json +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/postcss.config.js +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/App.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/ConsultationManagePanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/LanguageSwitch.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/LoadingSpinner.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalAlert.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalBusinessPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalButton.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalConfirmDialog.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalDataPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/MedicalRecordPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/components/PrescriptionPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/config/basic-info-config.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/config/runtime-config.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/env.d.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationChatPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationMembersPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationTranscriptionPanel.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/ConsultationVideoStage.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/InviteDoctorDialog.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/components/KickMemberConfirmDialog.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/types.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationChat.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationDevices.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationParticipants.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/useConsultationPermissions.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/features/consultation/utils.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/en-US/index.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/index.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/medicalTranslate.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/state.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/i18n/zh-CN/index.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/main.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/mock/appointments.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/mock/users.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/router/index.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/index.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/appointmentService.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/authService.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/launchContext.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/integration/userService.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/mock/appointmentService.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/mock/authService.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/mock/userService.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/services/adapters/types.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/shared/icons.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/styles/index.css +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/styles/tailwind.css +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/styles/theme.css +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/auth.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/format.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/navigation.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/utils/session.ts +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/DoctorConsultationView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/DoctorDashboardView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/LoginView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientConsultationFinishedView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientConsultationView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientSelectDoctorView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/src/views/PatientWaitingView.vue +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/tsconfig.json +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/tsconfig.node.json +0 -0
- /package/skills/{trtc/room-builder/templates/scenarios → trtc-conference/templates}/medical-consultation/vite.config.ts +0 -0
- /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
- /package/skills/{trtc-topic/runtime/lib → trtc-conference/tools}/__init__.py +0 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
"""Shared TRTC apply tool.
|
|
2
|
+
|
|
3
|
+
Default CLI contract:
|
|
4
|
+
|
|
5
|
+
python3 -m tools.apply --slice <slice_id>
|
|
6
|
+
python3 -m tools.apply --unit <unit_id>
|
|
7
|
+
python3 -m tools.apply --slice <slice_id> --product <product> --platform <platform>
|
|
8
|
+
|
|
9
|
+
This default mode is topic-facing: it runs the shared apply gate, advances the
|
|
10
|
+
state machine on pass/fail, writes evidence, and prints the human-readable
|
|
11
|
+
result expected by the topic loop. Exit codes match the legacy topic wrapper:
|
|
12
|
+
|
|
13
|
+
0 - apply pass
|
|
14
|
+
1 - apply fail
|
|
15
|
+
2 - usage / orchestration error
|
|
16
|
+
|
|
17
|
+
Raw JSON contract:
|
|
18
|
+
|
|
19
|
+
python3 -m tools.apply --json --slice <slice_id>
|
|
20
|
+
|
|
21
|
+
`--json` exposes the shared-tool contract directly. It emits structured JSON
|
|
22
|
+
(``status`` = ``passed`` / ``failed`` / ``usage_error`` / ``dependency_error`` /
|
|
23
|
+
``internal_error``) and does NOT mutate the topic state machine; orchestration
|
|
24
|
+
layers are responsible for reading ``state_transition_suggested`` and advancing
|
|
25
|
+
session state themselves.
|
|
26
|
+
|
|
27
|
+
Plugin discovery: for each product, the dispatcher looks for
|
|
28
|
+
skills/trtc-{product}/tools/apply_checks.py
|
|
29
|
+
and calls its ``build_checks(target_id, slice_ids, project_root, platform)`` function.
|
|
30
|
+
If no checker is found, the tool passes with empty checks (no-checker mode).
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import importlib.util
|
|
36
|
+
import json
|
|
37
|
+
import os
|
|
38
|
+
import re
|
|
39
|
+
import sys
|
|
40
|
+
from datetime import datetime, timezone
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
import yaml
|
|
45
|
+
except Exception as exc: # pragma: no cover
|
|
46
|
+
yaml = None # type: ignore[assignment]
|
|
47
|
+
_YAML_IMPORT_ERROR = exc
|
|
48
|
+
else:
|
|
49
|
+
_YAML_IMPORT_ERROR = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
EXIT_SUCCESS = 0
|
|
53
|
+
EXIT_INPUT_ERROR = 1
|
|
54
|
+
EXIT_DEPENDENCY_ERROR = 2
|
|
55
|
+
EXIT_INTERNAL_ERROR = 3
|
|
56
|
+
|
|
57
|
+
# Product names must be simple lowercase identifiers to prevent path traversal.
|
|
58
|
+
_PRODUCT_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$")
|
|
59
|
+
|
|
60
|
+
_REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
61
|
+
_STATE_MACHINE_DIR = _REPO_ROOT / "skills" / "trtc" / "tools" / "lib"
|
|
62
|
+
|
|
63
|
+
_STATE_MACHINE_IMPORT_ERROR: Exception | None = None
|
|
64
|
+
try:
|
|
65
|
+
sys.path.insert(0, str(_STATE_MACHINE_DIR))
|
|
66
|
+
import state_machine # noqa: E402
|
|
67
|
+
except ImportError as exc: # pragma: no cover
|
|
68
|
+
state_machine = None # type: ignore[assignment]
|
|
69
|
+
_STATE_MACHINE_IMPORT_ERROR = exc
|
|
70
|
+
finally:
|
|
71
|
+
try:
|
|
72
|
+
sys.path.remove(str(_STATE_MACHINE_DIR))
|
|
73
|
+
except ValueError: # pragma: no cover
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class InputError(RuntimeError):
|
|
78
|
+
"""Invalid CLI / session input."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DependencyError(RuntimeError):
|
|
82
|
+
"""Local dependency / import failure."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _utc_now_z() -> str:
|
|
86
|
+
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _resolve_session_path() -> Path:
|
|
90
|
+
explicit = os.environ.get("TRTC_SESSION_PATH")
|
|
91
|
+
if explicit:
|
|
92
|
+
return Path(explicit)
|
|
93
|
+
project_dir = os.environ.get("CLAUDE_PROJECT_DIR")
|
|
94
|
+
if project_dir:
|
|
95
|
+
return Path(project_dir) / ".trtc-session.yaml"
|
|
96
|
+
return Path.cwd() / ".trtc-session.yaml"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _slice_id_to_slug(slice_id: str) -> str:
|
|
100
|
+
return slice_id.replace("/", "__")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _load_session(session_path: Path) -> dict:
|
|
104
|
+
if yaml is None:
|
|
105
|
+
raise DependencyError(f"PyYAML import failed: {_YAML_IMPORT_ERROR}")
|
|
106
|
+
return yaml.safe_load(session_path.read_text()) or {}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _ensure_dependencies() -> None:
|
|
110
|
+
if yaml is None:
|
|
111
|
+
raise DependencyError(f"PyYAML import failed: {_YAML_IMPORT_ERROR}")
|
|
112
|
+
if _STATE_MACHINE_IMPORT_ERROR is not None:
|
|
113
|
+
raise DependencyError(str(_STATE_MACHINE_IMPORT_ERROR))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _load_product_checker(product: str) -> tuple:
|
|
117
|
+
"""Dynamically load skills/trtc-{product}/tools/apply_checks.py.
|
|
118
|
+
|
|
119
|
+
Returns (module, None) if found and loadable.
|
|
120
|
+
Returns (None, None) if no checker file exists — this is the normal
|
|
121
|
+
no-checker case (product does not yet have apply verification).
|
|
122
|
+
Returns (None, error_str) if the file EXISTS but is broken (syntax error,
|
|
123
|
+
import error, missing build_checks, etc.) — callers must treat this as a
|
|
124
|
+
hard error, not a silent pass.
|
|
125
|
+
"""
|
|
126
|
+
if not _PRODUCT_RE.match(product):
|
|
127
|
+
return None, f"invalid product name '{product}': must match [a-z0-9][a-z0-9-]*"
|
|
128
|
+
checker_path = _REPO_ROOT / "skills" / f"trtc-{product}" / "tools" / "apply_checks.py"
|
|
129
|
+
# Guard against path traversal after Path resolution (e.g. symlinks).
|
|
130
|
+
expected_parent = (_REPO_ROOT / "skills").resolve()
|
|
131
|
+
try:
|
|
132
|
+
checker_path.resolve().relative_to(expected_parent)
|
|
133
|
+
except ValueError:
|
|
134
|
+
return None, f"checker path for '{product}' escapes expected directory"
|
|
135
|
+
if not checker_path.exists():
|
|
136
|
+
return None, None # normal: no checker for this product
|
|
137
|
+
spec = importlib.util.spec_from_file_location(f"_apply_checks_{product}", checker_path)
|
|
138
|
+
if spec is None or spec.loader is None: # pragma: no cover
|
|
139
|
+
return None, f"could not create import spec for {checker_path}"
|
|
140
|
+
module = importlib.util.module_from_spec(spec)
|
|
141
|
+
try:
|
|
142
|
+
spec.loader.exec_module(module) # type: ignore[union-attr]
|
|
143
|
+
except Exception as exc:
|
|
144
|
+
return None, f"apply_checks.py failed to load: {exc}"
|
|
145
|
+
if not callable(getattr(module, "build_checks", None)):
|
|
146
|
+
return None, f"apply_checks.py for '{product}' is missing required build_checks() function"
|
|
147
|
+
return module, None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _product_from_target(target_id: str, slice_ids: list[str]) -> str | None:
|
|
151
|
+
"""Infer product from target_id / slice_ids prefix (the part before '/').
|
|
152
|
+
|
|
153
|
+
Returns the inferred product name if all IDs share the same prefix, or
|
|
154
|
+
None if no ID contains '/' (target_id is a bare unit name with no
|
|
155
|
+
associated slice_ids — no inference possible; validation skipped).
|
|
156
|
+
Raises InputError if:
|
|
157
|
+
- any slice_id lacks '/' (malformed / hand-edited session)
|
|
158
|
+
- IDs span multiple product prefixes (mixed-product unit)
|
|
159
|
+
"""
|
|
160
|
+
malformed = [sid for sid in slice_ids if "/" not in sid]
|
|
161
|
+
if malformed:
|
|
162
|
+
raise InputError(
|
|
163
|
+
f"malformed slice id(s): {', '.join(repr(s) for s in malformed)}; "
|
|
164
|
+
"slice ids must be in 'product/slug' format"
|
|
165
|
+
)
|
|
166
|
+
ids = [target_id] + list(slice_ids)
|
|
167
|
+
prefixes = {sid.split("/")[0] for sid in ids if "/" in sid}
|
|
168
|
+
if len(prefixes) > 1:
|
|
169
|
+
raise InputError(
|
|
170
|
+
f"mixed-product unit: slices span multiple products "
|
|
171
|
+
f"({', '.join(sorted(prefixes))}); all slices in a unit must belong to the same product"
|
|
172
|
+
)
|
|
173
|
+
return prefixes.pop() if len(prefixes) == 1 else None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _resolve_target(scope: dict, slice_id: str | None, unit_id: str | None) -> tuple[str, str, list[str]]:
|
|
177
|
+
if not scope.get("initialised"):
|
|
178
|
+
raise InputError("execution_queue not initialised")
|
|
179
|
+
if scope.get("state") != "code_written":
|
|
180
|
+
raise InputError(
|
|
181
|
+
f"current_execution_state must be 'code_written' before apply; got '{scope.get('state')}'"
|
|
182
|
+
)
|
|
183
|
+
current_id = scope.get("id")
|
|
184
|
+
current_kind = scope.get("kind")
|
|
185
|
+
slice_ids = list(scope.get("slice_ids") or [])
|
|
186
|
+
if current_kind == "unit":
|
|
187
|
+
if not unit_id:
|
|
188
|
+
raise InputError(f"current execution step is unit '{current_id}'; use --unit {current_id}")
|
|
189
|
+
if unit_id != current_id:
|
|
190
|
+
raise InputError(f"--unit '{unit_id}' does not match current unit '{current_id}'")
|
|
191
|
+
return "unit", current_id, slice_ids
|
|
192
|
+
|
|
193
|
+
if not slice_id:
|
|
194
|
+
raise InputError(f"current execution step is slice '{current_id}'; use --slice {current_id}")
|
|
195
|
+
if slice_id != current_id:
|
|
196
|
+
raise InputError(f"--slice '{slice_id}' does not match current slice '{current_id}'")
|
|
197
|
+
return "slice", current_id, slice_ids
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _resolve_product_platform(session_data: dict, product: str | None, platform: str | None) -> tuple[str, str]:
|
|
201
|
+
resolved_product = product or session_data.get("product")
|
|
202
|
+
resolved_platform = platform or session_data.get("platform")
|
|
203
|
+
if not resolved_product:
|
|
204
|
+
raise InputError("product is missing; pass --product or populate session.product")
|
|
205
|
+
if not resolved_platform:
|
|
206
|
+
raise InputError("platform is missing; pass --platform or populate session.platform")
|
|
207
|
+
return str(resolved_product), str(resolved_platform)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _resolve_project_root(session_data: dict, project_root: Path | None) -> Path:
|
|
211
|
+
if project_root is not None:
|
|
212
|
+
return project_root
|
|
213
|
+
raw = (session_data.get("project_state") or {}).get("project_root")
|
|
214
|
+
if not raw:
|
|
215
|
+
raise InputError("project root missing; pass --project or populate session.project_state.project_root")
|
|
216
|
+
return Path(raw)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _write_evidence(session_path: Path, target_id: str, payload: dict) -> str:
|
|
220
|
+
evidence_dir = session_path.parent / ".trtc-apply-evidence"
|
|
221
|
+
evidence_dir.mkdir(parents=True, exist_ok=True)
|
|
222
|
+
relative_path = f".trtc-apply-evidence/{_slice_id_to_slug(target_id)}.json"
|
|
223
|
+
(session_path.parent / relative_path).write_text(
|
|
224
|
+
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
|
|
225
|
+
encoding="utf-8",
|
|
226
|
+
)
|
|
227
|
+
return relative_path
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _build_parser(*, include_json: bool = False) -> argparse.ArgumentParser:
|
|
231
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
232
|
+
parser.add_argument("--slice", dest="slice_id", default=None)
|
|
233
|
+
parser.add_argument("--unit", dest="unit_id", default=None)
|
|
234
|
+
parser.add_argument("--product", default=None)
|
|
235
|
+
parser.add_argument("--platform", default=None)
|
|
236
|
+
parser.add_argument("--session", type=Path, default=None)
|
|
237
|
+
parser.add_argument("--project", type=Path, default=None)
|
|
238
|
+
parser.add_argument("--dry-run", action="store_true")
|
|
239
|
+
if include_json:
|
|
240
|
+
parser.add_argument("--json", action="store_true")
|
|
241
|
+
return parser
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _count_entry_checks(shared_checks: list[dict]) -> int:
|
|
245
|
+
return sum(1 for check in shared_checks if check.get("id") == "entry_symbol_present")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _failed_checks(shared_checks: list[dict]) -> list[dict]:
|
|
249
|
+
return [check for check in shared_checks if check.get("status") == "failed"]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
_BD_REGISTRY: frozenset[str] = frozenset({
|
|
253
|
+
"conference/login-auth",
|
|
254
|
+
"conference/room-lifecycle",
|
|
255
|
+
"conference/participant-management",
|
|
256
|
+
"conference/room-call",
|
|
257
|
+
"conference/room-schedule",
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
_BD_NAMES: dict[str, str] = {
|
|
261
|
+
"conference/login-auth": "登录与鉴权",
|
|
262
|
+
"conference/room-lifecycle": "房间生命周期",
|
|
263
|
+
"conference/participant-management": "会控 / 成员管理",
|
|
264
|
+
"conference/room-call": "通话",
|
|
265
|
+
"conference/room-schedule": "会议预约",
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _check_conference_business_decisions(
|
|
270
|
+
session: dict,
|
|
271
|
+
slice_ids: list[str],
|
|
272
|
+
target_id: str,
|
|
273
|
+
scope_name: str,
|
|
274
|
+
session_path: "Path",
|
|
275
|
+
) -> "tuple[int, dict] | None":
|
|
276
|
+
"""Return a failure result if conference business_decisions are missing, else None."""
|
|
277
|
+
if session.get("active_domain_skill") != "trtc-conference":
|
|
278
|
+
return None
|
|
279
|
+
if session.get("active_flow") != "topic":
|
|
280
|
+
return None
|
|
281
|
+
if session.get("integration_path") in {"medical-quickstart", "official-roomkit"}:
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
bd: dict = (session.get("session_context") or {}).get("business_decisions") or {}
|
|
285
|
+
missing = [s for s in slice_ids if s in _BD_REGISTRY and not bd.get(s)]
|
|
286
|
+
if not missing:
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
missing_names = "、".join(_BD_NAMES.get(s, s) for s in missing)
|
|
290
|
+
check = {
|
|
291
|
+
"status": "failed",
|
|
292
|
+
"summary": (
|
|
293
|
+
f"[business-decisions] BLOCKED — do not show this message verbatim to the user. "
|
|
294
|
+
f"Business configuration not collected for: {missing_names}. "
|
|
295
|
+
f"Recovery: return to the business configuration step, ask the user the pending "
|
|
296
|
+
f"questions for these modules, then re-run apply. "
|
|
297
|
+
f"Do NOT show internal field names to the user. Say instead: "
|
|
298
|
+
f"「在生成代码之前,还有关于「{missing_names}」的几个配置问题需要确认。」"
|
|
299
|
+
),
|
|
300
|
+
"detail": f"missing business_decisions for registry slices: {missing}",
|
|
301
|
+
}
|
|
302
|
+
result = {
|
|
303
|
+
"status": "failed",
|
|
304
|
+
"scope": scope_name,
|
|
305
|
+
"target_id": target_id,
|
|
306
|
+
"shared_checks": [check],
|
|
307
|
+
"state_transition_suggested": {"from": "code_written", "to": "apply_failed"},
|
|
308
|
+
"evidence_path": None,
|
|
309
|
+
"debug": {"mode": "business-decisions-pre-check", "missing_slices": missing, "session_path": str(session_path)},
|
|
310
|
+
}
|
|
311
|
+
return 1, result
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _run_parsed(args: argparse.Namespace) -> tuple[int, dict]:
|
|
315
|
+
_ensure_dependencies()
|
|
316
|
+
|
|
317
|
+
if bool(args.slice_id) == bool(args.unit_id):
|
|
318
|
+
raise InputError("provide exactly one of --slice <slice_id> or --unit <unit_id>")
|
|
319
|
+
|
|
320
|
+
session_path = args.session if args.session is not None else _resolve_session_path()
|
|
321
|
+
if not session_path.exists():
|
|
322
|
+
raise InputError(
|
|
323
|
+
f"session file not found: {session_path}; cd to the user project root or set TRTC_SESSION_PATH / CLAUDE_PROJECT_DIR"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
session_data = _load_session(session_path)
|
|
327
|
+
scope = state_machine.current_scope(session_path)
|
|
328
|
+
scope_name, target_id, slice_ids = _resolve_target(scope, args.slice_id, args.unit_id)
|
|
329
|
+
product, platform = _resolve_product_platform(session_data, args.product, args.platform)
|
|
330
|
+
project_root = _resolve_project_root(session_data, args.project)
|
|
331
|
+
|
|
332
|
+
# Cross-validate: if all slice/unit IDs share a product prefix, it must
|
|
333
|
+
# match the supplied product. Mismatches indicate a caller passing
|
|
334
|
+
# --product chat while running conference slices, which would silently
|
|
335
|
+
# bypass all conference checks via the no-checker path.
|
|
336
|
+
inferred_product = _product_from_target(target_id, slice_ids)
|
|
337
|
+
if inferred_product and inferred_product != product:
|
|
338
|
+
raise InputError(
|
|
339
|
+
f"product mismatch: --product '{product}' was supplied but "
|
|
340
|
+
f"target '{target_id}' belongs to product '{inferred_product}'"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Conference-specific pre-check: block if registry slice has no business_decisions.
|
|
344
|
+
# This is a second enforcement layer — the PreToolUse hook is the first.
|
|
345
|
+
bd_block = _check_conference_business_decisions(session_data, slice_ids, target_id, scope_name, session_path)
|
|
346
|
+
if bd_block is not None:
|
|
347
|
+
return bd_block
|
|
348
|
+
|
|
349
|
+
# Plugin dispatch: load product-specific checks if available.
|
|
350
|
+
checker, checker_error = _load_product_checker(product)
|
|
351
|
+
if checker_error is not None:
|
|
352
|
+
# Checker file exists but is broken — hard error, do not silently pass.
|
|
353
|
+
raise DependencyError(checker_error)
|
|
354
|
+
if checker is not None:
|
|
355
|
+
status, shared_checks, debug_checks = checker.build_checks(
|
|
356
|
+
target_id, slice_ids, project_root, platform
|
|
357
|
+
)
|
|
358
|
+
else:
|
|
359
|
+
# No apply_checks.py for this product yet — pass with an informational note.
|
|
360
|
+
status = "passed"
|
|
361
|
+
shared_checks = []
|
|
362
|
+
debug_checks = {
|
|
363
|
+
"mode": "no-checker",
|
|
364
|
+
"files_scanned": 0,
|
|
365
|
+
"slice_results": [],
|
|
366
|
+
"issues": [],
|
|
367
|
+
"note": f"no apply_checks.py found for product '{product}'; content checks skipped",
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
state_transition = {
|
|
371
|
+
"from": "code_written",
|
|
372
|
+
"to": "apply_passed" if status == "passed" else "apply_failed",
|
|
373
|
+
}
|
|
374
|
+
result = {
|
|
375
|
+
"status": status,
|
|
376
|
+
"scope": scope_name,
|
|
377
|
+
"target_id": target_id,
|
|
378
|
+
"shared_checks": shared_checks,
|
|
379
|
+
"state_transition_suggested": state_transition,
|
|
380
|
+
"evidence_path": None,
|
|
381
|
+
"debug": {
|
|
382
|
+
"project_root": str(project_root),
|
|
383
|
+
"product": product,
|
|
384
|
+
"platform": platform,
|
|
385
|
+
"session_path": str(session_path),
|
|
386
|
+
"dry_run": bool(args.dry_run),
|
|
387
|
+
**debug_checks,
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if not args.dry_run:
|
|
392
|
+
evidence_payload = {
|
|
393
|
+
"schema_version": 1,
|
|
394
|
+
"target_id": target_id,
|
|
395
|
+
"scope": scope_name,
|
|
396
|
+
"status": status,
|
|
397
|
+
"checked_at": _utc_now_z(),
|
|
398
|
+
"shared_checks": shared_checks,
|
|
399
|
+
"product_checks": [],
|
|
400
|
+
# Legacy compat fields — consumers must not depend on these.
|
|
401
|
+
# See debug.legacy_field_notice.
|
|
402
|
+
"slice_id": slice_ids[0] if scope_name == "slice" else None,
|
|
403
|
+
"unit_id": target_id if scope_name == "unit" else None,
|
|
404
|
+
"slice_ids": slice_ids,
|
|
405
|
+
"kind": scope_name,
|
|
406
|
+
"mode": debug_checks.get("mode", "unknown"),
|
|
407
|
+
"issues": debug_checks.get("issues", []),
|
|
408
|
+
"debug": {
|
|
409
|
+
"legacy_field_notice": (
|
|
410
|
+
"slice_id/unit_id/slice_ids/kind/mode/issues are compat fields. "
|
|
411
|
+
"Use target_id + scope for new consumers."
|
|
412
|
+
),
|
|
413
|
+
"project_root": str(project_root),
|
|
414
|
+
"product": product,
|
|
415
|
+
"platform": platform,
|
|
416
|
+
**{k: v for k, v in debug_checks.items() if k != "issues"},
|
|
417
|
+
},
|
|
418
|
+
}
|
|
419
|
+
result["evidence_path"] = _write_evidence(session_path, target_id, evidence_payload)
|
|
420
|
+
|
|
421
|
+
return EXIT_SUCCESS, result
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def run(argv: list[str] | None = None) -> tuple[int, dict]:
|
|
425
|
+
args = _build_parser().parse_args(argv)
|
|
426
|
+
return _run_parsed(args)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _error_payload(status: str, reason: str) -> dict:
|
|
430
|
+
return {
|
|
431
|
+
"status": status,
|
|
432
|
+
"scope": None,
|
|
433
|
+
"target_id": None,
|
|
434
|
+
"shared_checks": [],
|
|
435
|
+
"state_transition_suggested": None,
|
|
436
|
+
"evidence_path": None,
|
|
437
|
+
"reason": reason,
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _transition_name(payload: dict) -> str:
|
|
442
|
+
transition = payload.get("state_transition_suggested") or {}
|
|
443
|
+
next_state_name = transition.get("to")
|
|
444
|
+
if next_state_name == "apply_passed":
|
|
445
|
+
return "mark_apply_passed"
|
|
446
|
+
if next_state_name == "apply_failed":
|
|
447
|
+
return "mark_apply_failed"
|
|
448
|
+
raise RuntimeError(f"unsupported state transition suggestion: {transition}")
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _advance_human_cli(payload: dict, *, dry_run: bool) -> int:
|
|
452
|
+
status = payload["status"]
|
|
453
|
+
target_id = payload["target_id"]
|
|
454
|
+
shared_checks = payload.get("shared_checks") or []
|
|
455
|
+
session_path = Path(payload["debug"]["session_path"])
|
|
456
|
+
|
|
457
|
+
if not dry_run:
|
|
458
|
+
state_machine.advance(session_path, _transition_name(payload))
|
|
459
|
+
|
|
460
|
+
if status == "passed":
|
|
461
|
+
entries_checked = _count_entry_checks(shared_checks)
|
|
462
|
+
if dry_run:
|
|
463
|
+
print(
|
|
464
|
+
f"apply pass (dry-run): {entries_checked} slice "
|
|
465
|
+
f"entr{'y' if entries_checked == 1 else 'ies'} wired up for {target_id}"
|
|
466
|
+
)
|
|
467
|
+
return 0
|
|
468
|
+
|
|
469
|
+
session_data = _load_session(session_path)
|
|
470
|
+
policy = session_data.get("auto_advance_policy")
|
|
471
|
+
if policy in {"pause_on_failure", "pause_at_end"}:
|
|
472
|
+
new_state = state_machine.advance(session_path, "mark_user_confirmed")
|
|
473
|
+
print(
|
|
474
|
+
f"apply pass: {entries_checked} slice entr"
|
|
475
|
+
f"{'y' if entries_checked == 1 else 'ies'} wired up for "
|
|
476
|
+
f"{target_id} — auto-advanced ({policy}); next state: {new_state}"
|
|
477
|
+
)
|
|
478
|
+
if new_state == "all_done":
|
|
479
|
+
print("")
|
|
480
|
+
print("=" * 60)
|
|
481
|
+
print("ALL SLICES COMPLETE — POST-LOOP CHECKLIST (mandatory)")
|
|
482
|
+
print("=" * 60)
|
|
483
|
+
print("The slice loop is finished, but the topic flow is NOT done.")
|
|
484
|
+
print("You MUST now execute these steps from topic/SKILL.md:")
|
|
485
|
+
print("")
|
|
486
|
+
print(" □ Step 4: Present the verification checklist to the user")
|
|
487
|
+
print(" □ Step 4.5: Offer runtime verification & telemetry")
|
|
488
|
+
print(" (ask consent if telemetry.opted_in is null)")
|
|
489
|
+
print("")
|
|
490
|
+
print("Do NOT output a final summary and stop. Read topic/SKILL.md")
|
|
491
|
+
print("Step 4 and Step 4.5 sections and execute them now.")
|
|
492
|
+
print("=" * 60)
|
|
493
|
+
return 0
|
|
494
|
+
print(
|
|
495
|
+
f"apply pass: {entries_checked} slice entr"
|
|
496
|
+
f"{'y' if entries_checked == 1 else 'ies'} wired up for {target_id}"
|
|
497
|
+
)
|
|
498
|
+
return 0
|
|
499
|
+
|
|
500
|
+
failed_checks = _failed_checks(shared_checks)
|
|
501
|
+
if dry_run:
|
|
502
|
+
print(f"apply fail (dry-run): {len(failed_checks)} shared check(s) failed for {target_id}")
|
|
503
|
+
return 1
|
|
504
|
+
|
|
505
|
+
print(f"apply fail: {len(failed_checks)} shared check(s) failed for {target_id}")
|
|
506
|
+
for check in failed_checks[:5]:
|
|
507
|
+
print(f" - {check.get('summary', '')[:160]}")
|
|
508
|
+
return 1
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def main(argv: list[str] | None = None) -> int:
|
|
512
|
+
args = _build_parser(include_json=True).parse_args(argv)
|
|
513
|
+
try:
|
|
514
|
+
exit_code, payload = _run_parsed(args)
|
|
515
|
+
if args.json:
|
|
516
|
+
print(json.dumps(payload, ensure_ascii=False))
|
|
517
|
+
return exit_code
|
|
518
|
+
return _advance_human_cli(payload, dry_run=bool(args.dry_run))
|
|
519
|
+
except InputError as exc:
|
|
520
|
+
if args.json:
|
|
521
|
+
print(json.dumps(_error_payload("usage_error", str(exc)), ensure_ascii=False))
|
|
522
|
+
return EXIT_INPUT_ERROR
|
|
523
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
524
|
+
return 2
|
|
525
|
+
except DependencyError as exc:
|
|
526
|
+
if args.json:
|
|
527
|
+
print(json.dumps(_error_payload("dependency_error", str(exc)), ensure_ascii=False))
|
|
528
|
+
return EXIT_DEPENDENCY_ERROR
|
|
529
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
530
|
+
return 2
|
|
531
|
+
except Exception as exc: # pragma: no cover
|
|
532
|
+
if args.json:
|
|
533
|
+
print(json.dumps(_error_payload("internal_error", str(exc)), ensure_ascii=False))
|
|
534
|
+
return EXIT_INTERNAL_ERROR
|
|
535
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
536
|
+
return 2
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
if __name__ == "__main__":
|
|
540
|
+
sys.exit(main())
|