dsh-plugin-t-expert 0.1.0
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/LICENSE +21 -0
- package/README.md +215 -0
- package/THIRD-PARTY-NOTICES +33 -0
- package/cordis.patch.yml +7 -0
- package/data/experts/academic/academic-anthropologist.md +125 -0
- package/data/experts/academic/academic-geographer.md +127 -0
- package/data/experts/academic/academic-historian.md +123 -0
- package/data/experts/academic/academic-narratologist.md +118 -0
- package/data/experts/academic/academic-psychologist.md +118 -0
- package/data/experts/academic/academic-statistician.md +144 -0
- package/data/experts/design/design-brand-guardian.md +322 -0
- package/data/experts/design/design-image-prompt-engineer.md +236 -0
- package/data/experts/design/design-inclusive-visuals-specialist.md +71 -0
- package/data/experts/design/design-persona-walkthrough.md +272 -0
- package/data/experts/design/design-ui-designer.md +383 -0
- package/data/experts/design/design-ui-finish-gate-reviewer.md +217 -0
- package/data/experts/design/design-ux-architect.md +469 -0
- package/data/experts/design/design-ux-researcher.md +329 -0
- package/data/experts/design/design-visual-storyteller.md +149 -0
- package/data/experts/design/design-whimsy-injector.md +438 -0
- package/data/experts/engineering/engineering-ai-data-remediation-engineer.md +211 -0
- package/data/experts/engineering/engineering-ai-engineer.md +146 -0
- package/data/experts/engineering/engineering-api-platform-engineer.md +162 -0
- package/data/experts/engineering/engineering-ats-validator-architect.md +383 -0
- package/data/experts/engineering/engineering-autonomous-optimization-architect.md +107 -0
- package/data/experts/engineering/engineering-backend-architect.md +236 -0
- package/data/experts/engineering/engineering-china-network-engineer.md +252 -0
- package/data/experts/engineering/engineering-cms-developer.md +536 -0
- package/data/experts/engineering/engineering-code-reviewer.md +76 -0
- package/data/experts/engineering/engineering-codebase-onboarding-engineer.md +173 -0
- package/data/experts/engineering/engineering-data-engineer.md +306 -0
- package/data/experts/engineering/engineering-data-visualization-engineer.md +151 -0
- package/data/experts/engineering/engineering-database-optimizer.md +176 -0
- package/data/experts/engineering/engineering-database-reliability-engineer.md +162 -0
- package/data/experts/engineering/engineering-desktop-app-engineer.md +204 -0
- package/data/experts/engineering/engineering-developer-tooling-engineer.md +153 -0
- package/data/experts/engineering/engineering-devops-automator.md +376 -0
- package/data/experts/engineering/engineering-drupal-performance.md +347 -0
- package/data/experts/engineering/engineering-drupal-shopping-cart.md +360 -0
- package/data/experts/engineering/engineering-email-intelligence-engineer.md +353 -0
- package/data/experts/engineering/engineering-embedded-firmware-engineer.md +173 -0
- package/data/experts/engineering/engineering-feishu-integration-developer.md +598 -0
- package/data/experts/engineering/engineering-filament-optimization-specialist.md +283 -0
- package/data/experts/engineering/engineering-finops-engineer.md +153 -0
- package/data/experts/engineering/engineering-frontend-developer.md +225 -0
- package/data/experts/engineering/engineering-gaussdb-expert.md +334 -0
- package/data/experts/engineering/engineering-git-workflow-master.md +84 -0
- package/data/experts/engineering/engineering-i18n-engineer.md +184 -0
- package/data/experts/engineering/engineering-identity-access-engineer.md +196 -0
- package/data/experts/engineering/engineering-incident-response-commander.md +444 -0
- package/data/experts/engineering/engineering-iot-fleet-engineer.md +148 -0
- package/data/experts/engineering/engineering-it-service-manager.md +561 -0
- package/data/experts/engineering/engineering-knowledge-graph-engineer.md +368 -0
- package/data/experts/engineering/engineering-llm-post-training-engineer.md +166 -0
- package/data/experts/engineering/engineering-minimal-change-engineer.md +207 -0
- package/data/experts/engineering/engineering-mobile-app-builder.md +493 -0
- package/data/experts/engineering/engineering-mobile-release-engineer.md +163 -0
- package/data/experts/engineering/engineering-multi-agent-systems-architect.md +600 -0
- package/data/experts/engineering/engineering-network-engineer.md +239 -0
- package/data/experts/engineering/engineering-orgscript-engineer.md +113 -0
- package/data/experts/engineering/engineering-payments-billing-engineer.md +194 -0
- package/data/experts/engineering/engineering-pdf-engine-architect.md +666 -0
- package/data/experts/engineering/engineering-platform-engineer.md +270 -0
- package/data/experts/engineering/engineering-privacy-engineer.md +152 -0
- package/data/experts/engineering/engineering-prompt-engineer.md +202 -0
- package/data/experts/engineering/engineering-rag-pipeline-engineer.md +437 -0
- package/data/experts/engineering/engineering-rapid-prototyper.md +462 -0
- package/data/experts/engineering/engineering-realtime-collaboration-engineer.md +187 -0
- package/data/experts/engineering/engineering-rust-refactoring-specialist.md +313 -0
- package/data/experts/engineering/engineering-search-relevance-engineer.md +237 -0
- package/data/experts/engineering/engineering-section-508-specialist.md +339 -0
- package/data/experts/engineering/engineering-senior-developer.md +176 -0
- package/data/experts/engineering/engineering-software-architect.md +112 -0
- package/data/experts/engineering/engineering-solidity-smart-contract-engineer.md +522 -0
- package/data/experts/engineering/engineering-sre.md +90 -0
- package/data/experts/engineering/engineering-technical-writer.md +393 -0
- package/data/experts/engineering/engineering-universal-document-compiler.md +344 -0
- package/data/experts/engineering/engineering-uswds-developer.md +340 -0
- package/data/experts/engineering/engineering-video-streaming-engineer.md +150 -0
- package/data/experts/engineering/engineering-voice-ai-integration-engineer.md +561 -0
- package/data/experts/engineering/engineering-webassembly-engineer.md +156 -0
- package/data/experts/engineering/engineering-wechat-mini-program-developer.md +350 -0
- package/data/experts/engineering/engineering-wordpress-performance.md +346 -0
- package/data/experts/engineering/engineering-wordpress-shopping-cart.md +346 -0
- package/data/experts/finance/finance-bookkeeper-controller.md +260 -0
- package/data/experts/finance/finance-financial-analyst.md +234 -0
- package/data/experts/finance/finance-fpa-analyst.md +263 -0
- package/data/experts/finance/finance-investment-researcher.md +272 -0
- package/data/experts/finance/finance-tax-strategist.md +239 -0
- package/data/experts/game-development/blender/blender-addon-engineer.md +234 -0
- package/data/experts/game-development/economy-designer.md +156 -0
- package/data/experts/game-development/game-audio-engineer.md +264 -0
- package/data/experts/game-development/game-designer.md +167 -0
- package/data/experts/game-development/godot/godot-gameplay-scripter.md +334 -0
- package/data/experts/game-development/godot/godot-multiplayer-engineer.md +297 -0
- package/data/experts/game-development/godot/godot-shader-developer.md +266 -0
- package/data/experts/game-development/level-designer.md +208 -0
- package/data/experts/game-development/narrative-designer.md +243 -0
- package/data/experts/game-development/roblox-studio/roblox-avatar-creator.md +297 -0
- package/data/experts/game-development/roblox-studio/roblox-experience-designer.md +305 -0
- package/data/experts/game-development/roblox-studio/roblox-systems-scripter.md +325 -0
- package/data/experts/game-development/technical-artist.md +229 -0
- package/data/experts/game-development/unity/unity-architect.md +271 -0
- package/data/experts/game-development/unity/unity-editor-tool-developer.md +310 -0
- package/data/experts/game-development/unity/unity-multiplayer-engineer.md +321 -0
- package/data/experts/game-development/unity/unity-shader-graph-artist.md +269 -0
- package/data/experts/game-development/unreal-engine/unreal-multiplayer-architect.md +313 -0
- package/data/experts/game-development/unreal-engine/unreal-systems-engineer.md +310 -0
- package/data/experts/game-development/unreal-engine/unreal-technical-artist.md +256 -0
- package/data/experts/game-development/unreal-engine/unreal-world-builder.md +273 -0
- package/data/experts/gis/gis-3d-scene-developer.md +111 -0
- package/data/experts/gis/gis-analyst.md +91 -0
- package/data/experts/gis/gis-bim-specialist.md +108 -0
- package/data/experts/gis/gis-cartography-designer.md +150 -0
- package/data/experts/gis/gis-drone-reality-mapping.md +120 -0
- package/data/experts/gis/gis-geoai-ml-engineer.md +105 -0
- package/data/experts/gis/gis-geoprocessing-specialist.md +97 -0
- package/data/experts/gis/gis-qa-engineer.md +133 -0
- package/data/experts/gis/gis-solution-engineer.md +101 -0
- package/data/experts/gis/gis-spatial-data-engineer.md +97 -0
- package/data/experts/gis/gis-spatial-data-scientist.md +111 -0
- package/data/experts/gis/gis-technical-consultant.md +86 -0
- package/data/experts/gis/gis-web-gis-developer.md +108 -0
- package/data/experts/healthcare/healthcare-clinical-evidence-agent.md +231 -0
- package/data/experts/healthcare/healthcare-innovation-strategist.md +433 -0
- package/data/experts/healthcare/healthcare-sovereign-health-systems-agent.md +312 -0
- package/data/experts/marketing/marketing-aeo-foundations.md +264 -0
- package/data/experts/marketing/marketing-agentic-search-optimizer.md +313 -0
- package/data/experts/marketing/marketing-ai-citation-strategist.md +172 -0
- package/data/experts/marketing/marketing-app-store-optimizer.md +321 -0
- package/data/experts/marketing/marketing-baidu-seo-specialist.md +226 -0
- package/data/experts/marketing/marketing-bilibili-content-strategist.md +199 -0
- package/data/experts/marketing/marketing-book-co-author.md +110 -0
- package/data/experts/marketing/marketing-carousel-growth-engine.md +199 -0
- package/data/experts/marketing/marketing-china-ecommerce-operator.md +283 -0
- package/data/experts/marketing/marketing-china-market-localization-strategist.md +283 -0
- package/data/experts/marketing/marketing-content-creator.md +54 -0
- package/data/experts/marketing/marketing-cross-border-ecommerce.md +259 -0
- package/data/experts/marketing/marketing-douyin-strategist.md +149 -0
- package/data/experts/marketing/marketing-email-strategist.md +249 -0
- package/data/experts/marketing/marketing-global-podcast-strategist.md +206 -0
- package/data/experts/marketing/marketing-growth-hacker.md +54 -0
- package/data/experts/marketing/marketing-instagram-curator.md +113 -0
- package/data/experts/marketing/marketing-kuaishou-strategist.md +223 -0
- package/data/experts/marketing/marketing-linkedin-content-creator.md +214 -0
- package/data/experts/marketing/marketing-livestream-commerce-coach.md +305 -0
- package/data/experts/marketing/marketing-multi-platform-publisher.md +217 -0
- package/data/experts/marketing/marketing-podcast-strategist.md +277 -0
- package/data/experts/marketing/marketing-pr-communications-manager.md +473 -0
- package/data/experts/marketing/marketing-private-domain-operator.md +308 -0
- package/data/experts/marketing/marketing-reddit-community-builder.md +123 -0
- package/data/experts/marketing/marketing-seo-specialist.md +370 -0
- package/data/experts/marketing/marketing-short-video-editing-coach.md +412 -0
- package/data/experts/marketing/marketing-social-media-strategist.md +125 -0
- package/data/experts/marketing/marketing-tiktok-strategist.md +125 -0
- package/data/experts/marketing/marketing-twitter-engager.md +126 -0
- package/data/experts/marketing/marketing-video-optimization-specialist.md +119 -0
- package/data/experts/marketing/marketing-wechat-official-account.md +145 -0
- package/data/experts/marketing/marketing-weibo-strategist.md +240 -0
- package/data/experts/marketing/marketing-x-twitter-intelligence-analyst.md +161 -0
- package/data/experts/marketing/marketing-xiaohongshu-specialist.md +138 -0
- package/data/experts/marketing/marketing-zhihu-strategist.md +162 -0
- package/data/experts/paid-media/paid-media-auditor.md +71 -0
- package/data/experts/paid-media/paid-media-creative-strategist.md +71 -0
- package/data/experts/paid-media/paid-media-paid-social-strategist.md +71 -0
- package/data/experts/paid-media/paid-media-ppc-strategist.md +71 -0
- package/data/experts/paid-media/paid-media-programmatic-buyer.md +71 -0
- package/data/experts/paid-media/paid-media-search-query-analyst.md +71 -0
- package/data/experts/paid-media/paid-media-tracking-specialist.md +71 -0
- package/data/experts/product/product-behavioral-nudge-engine.md +80 -0
- package/data/experts/product/product-feedback-synthesizer.md +119 -0
- package/data/experts/product/product-manager.md +469 -0
- package/data/experts/product/product-sprint-prioritizer.md +154 -0
- package/data/experts/product/product-trend-researcher.md +159 -0
- package/data/experts/project-management/project-management-experiment-tracker.md +198 -0
- package/data/experts/project-management/project-management-jira-workflow-steward.md +230 -0
- package/data/experts/project-management/project-management-meeting-notes-specialist.md +95 -0
- package/data/experts/project-management/project-management-project-shepherd.md +194 -0
- package/data/experts/project-management/project-management-studio-operations.md +200 -0
- package/data/experts/project-management/project-management-studio-producer.md +203 -0
- package/data/experts/project-management/project-manager-senior.md +135 -0
- package/data/experts/research/research-synthesist.md +136 -0
- package/data/experts/sales/sales-account-strategist.md +227 -0
- package/data/experts/sales/sales-coach.md +271 -0
- package/data/experts/sales/sales-deal-strategist.md +180 -0
- package/data/experts/sales/sales-discovery-coach.md +225 -0
- package/data/experts/sales/sales-engineer.md +182 -0
- package/data/experts/sales/sales-offer-lead-gen-strategist.md +257 -0
- package/data/experts/sales/sales-outbound-strategist.md +201 -0
- package/data/experts/sales/sales-pipeline-analyst.md +267 -0
- package/data/experts/sales/sales-proposal-strategist.md +217 -0
- package/data/experts/security/security-ai-generated-code-auditor.md +207 -0
- package/data/experts/security/security-appsec-engineer.md +491 -0
- package/data/experts/security/security-architect.md +304 -0
- package/data/experts/security/security-blockchain-security-auditor.md +463 -0
- package/data/experts/security/security-cloud-security-architect.md +523 -0
- package/data/experts/security/security-compliance-auditor.md +158 -0
- package/data/experts/security/security-incident-responder.md +437 -0
- package/data/experts/security/security-penetration-tester.md +399 -0
- package/data/experts/security/security-secrets-credential-engineer.md +176 -0
- package/data/experts/security/security-senior-secops.md +750 -0
- package/data/experts/security/security-threat-detection-engineer.md +534 -0
- package/data/experts/security/security-threat-intelligence-analyst.md +644 -0
- package/data/experts/spatial-computing/macos-spatial-metal-engineer.md +337 -0
- package/data/experts/spatial-computing/terminal-integration-specialist.md +70 -0
- package/data/experts/spatial-computing/visionos-spatial-engineer.md +54 -0
- package/data/experts/spatial-computing/xr-cockpit-interaction-specialist.md +32 -0
- package/data/experts/spatial-computing/xr-immersive-developer.md +32 -0
- package/data/experts/spatial-computing/xr-interface-architect.md +32 -0
- package/data/experts/specialized/accounts-payable-agent.md +185 -0
- package/data/experts/specialized/agentic-identity-trust.md +387 -0
- package/data/experts/specialized/agents-orchestrator.md +367 -0
- package/data/experts/specialized/automation-governance-architect.md +216 -0
- package/data/experts/specialized/business-strategist.md +488 -0
- package/data/experts/specialized/change-management-consultant.md +497 -0
- package/data/experts/specialized/chief-financial-officer.md +388 -0
- package/data/experts/specialized/corporate-training-designer.md +192 -0
- package/data/experts/specialized/customer-service.md +398 -0
- package/data/experts/specialized/customer-success-manager.md +460 -0
- package/data/experts/specialized/data-consolidation-agent.md +60 -0
- package/data/experts/specialized/data-privacy-officer.md +412 -0
- package/data/experts/specialized/esg-sustainability-officer.md +396 -0
- package/data/experts/specialized/government-digital-presales-consultant.md +363 -0
- package/data/experts/specialized/grant-writer.md +511 -0
- package/data/experts/specialized/healthcare-aging-parent-care-companion.md +414 -0
- package/data/experts/specialized/healthcare-customer-service.md +389 -0
- package/data/experts/specialized/healthcare-marketing-compliance.md +395 -0
- package/data/experts/specialized/hospitality-guest-services.md +603 -0
- package/data/experts/specialized/hr-onboarding.md +451 -0
- package/data/experts/specialized/identity-graph-operator.md +260 -0
- package/data/experts/specialized/language-translator.md +264 -0
- package/data/experts/specialized/legal-billing-time-tracking.md +569 -0
- package/data/experts/specialized/legal-client-intake.md +492 -0
- package/data/experts/specialized/legal-document-review.md +454 -0
- package/data/experts/specialized/loan-officer-assistant.md +555 -0
- package/data/experts/specialized/lsp-index-engineer.md +314 -0
- package/data/experts/specialized/ma-integration-manager.md +427 -0
- package/data/experts/specialized/medical-billing-coding-specialist.md +491 -0
- package/data/experts/specialized/operations-manager.md +399 -0
- package/data/experts/specialized/organizational-psychologist.md +391 -0
- package/data/experts/specialized/personal-growth-mentor.md +159 -0
- package/data/experts/specialized/real-estate-buyer-seller.md +596 -0
- package/data/experts/specialized/recruitment-specialist.md +509 -0
- package/data/experts/specialized/report-distribution-agent.md +65 -0
- package/data/experts/specialized/resume-tailor.md +230 -0
- package/data/experts/specialized/retail-customer-returns.md +566 -0
- package/data/experts/specialized/sales-data-extraction-agent.md +67 -0
- package/data/experts/specialized/sales-outreach.md +425 -0
- package/data/experts/specialized/specialized-chief-of-staff.md +279 -0
- package/data/experts/specialized/specialized-civil-engineer.md +356 -0
- package/data/experts/specialized/specialized-codebase-archaeologist.md +341 -0
- package/data/experts/specialized/specialized-cultural-intelligence-strategist.md +88 -0
- package/data/experts/specialized/specialized-developer-advocate.md +317 -0
- package/data/experts/specialized/specialized-document-generator.md +55 -0
- package/data/experts/specialized/specialized-fedramp-rmf-compliance.md +378 -0
- package/data/experts/specialized/specialized-focus-music-architect.md +172 -0
- package/data/experts/specialized/specialized-french-consulting-market.md +194 -0
- package/data/experts/specialized/specialized-korean-business-navigator.md +216 -0
- package/data/experts/specialized/specialized-master-plan-architect.md +157 -0
- package/data/experts/specialized/specialized-mcp-builder.md +248 -0
- package/data/experts/specialized/specialized-model-qa.md +488 -0
- package/data/experts/specialized/specialized-pricing-analyst.md +243 -0
- package/data/experts/specialized/specialized-salesforce-architect.md +182 -0
- package/data/experts/specialized/specialized-strategy-duel-agent.md +130 -0
- package/data/experts/specialized/specialized-workflow-architect.md +597 -0
- package/data/experts/specialized/study-abroad-advisor.md +282 -0
- package/data/experts/specialized/supply-chain-strategist.md +582 -0
- package/data/experts/specialized/zk-steward.md +211 -0
- package/data/experts/support/support-analytics-reporter.md +365 -0
- package/data/experts/support/support-executive-summary-generator.md +212 -0
- package/data/experts/support/support-finance-tracker.md +442 -0
- package/data/experts/support/support-infrastructure-maintainer.md +618 -0
- package/data/experts/support/support-legal-compliance-checker.md +588 -0
- package/data/experts/support/support-support-responder.md +585 -0
- package/data/experts/testing/testing-accessibility-auditor.md +316 -0
- package/data/experts/testing/testing-api-tester.md +306 -0
- package/data/experts/testing/testing-evidence-collector.md +210 -0
- package/data/experts/testing/testing-performance-benchmarker.md +268 -0
- package/data/experts/testing/testing-reality-checker.md +249 -0
- package/data/experts/testing/testing-test-automation-engineer.md +179 -0
- package/data/experts/testing/testing-test-results-analyzer.md +305 -0
- package/data/experts/testing/testing-tool-evaluator.md +394 -0
- package/data/experts/testing/testing-workflow-optimizer.md +450 -0
- package/data/source.json +27 -0
- package/data/t-team.config.json +998 -0
- package/data/team-profiles.py +365 -0
- package/data/teams.json +581 -0
- package/data/teams.resolved.json +1121 -0
- package/data/zh/COVERAGE.json +20 -0
- package/data/zh/academic/academic-anthropologist.md +124 -0
- package/data/zh/academic/academic-geographer.md +126 -0
- package/data/zh/academic/academic-historian.md +122 -0
- package/data/zh/academic/academic-narratologist.md +117 -0
- package/data/zh/academic/academic-psychologist.md +117 -0
- package/data/zh/academic/academic-statistician.md +40 -0
- package/data/zh/descriptions.json +281 -0
- package/data/zh/design/design-brand-guardian.md +321 -0
- package/data/zh/design/design-image-prompt-engineer.md +255 -0
- package/data/zh/design/design-inclusive-visuals-specialist.md +177 -0
- package/data/zh/design/design-persona-walkthrough.md +271 -0
- package/data/zh/design/design-ui-designer.md +382 -0
- package/data/zh/design/design-ui-finish-gate-reviewer.md +40 -0
- package/data/zh/design/design-ux-architect.md +482 -0
- package/data/zh/design/design-ux-researcher.md +328 -0
- package/data/zh/design/design-visual-storyteller.md +159 -0
- package/data/zh/design/design-whimsy-injector.md +453 -0
- package/data/zh/divisions.json +74 -0
- package/data/zh/engineering/engineering-ai-data-remediation-engineer.md +209 -0
- package/data/zh/engineering/engineering-ai-engineer.md +161 -0
- package/data/zh/engineering/engineering-api-platform-engineer.md +40 -0
- package/data/zh/engineering/engineering-ats-validator-architect.md +383 -0
- package/data/zh/engineering/engineering-autonomous-optimization-architect.md +115 -0
- package/data/zh/engineering/engineering-backend-architect.md +234 -0
- package/data/zh/engineering/engineering-china-network-engineer.md +252 -0
- package/data/zh/engineering/engineering-cms-developer.md +534 -0
- package/data/zh/engineering/engineering-code-reviewer.md +172 -0
- package/data/zh/engineering/engineering-codebase-onboarding-engineer.md +172 -0
- package/data/zh/engineering/engineering-data-engineer.md +324 -0
- package/data/zh/engineering/engineering-data-visualization-engineer.md +40 -0
- package/data/zh/engineering/engineering-database-optimizer.md +175 -0
- package/data/zh/engineering/engineering-database-reliability-engineer.md +40 -0
- package/data/zh/engineering/engineering-desktop-app-engineer.md +40 -0
- package/data/zh/engineering/engineering-developer-tooling-engineer.md +40 -0
- package/data/zh/engineering/engineering-devops-automator.md +375 -0
- package/data/zh/engineering/engineering-drupal-performance.md +40 -0
- package/data/zh/engineering/engineering-drupal-shopping-cart.md +359 -0
- package/data/zh/engineering/engineering-email-intelligence-engineer.md +349 -0
- package/data/zh/engineering/engineering-embedded-firmware-engineer.md +168 -0
- package/data/zh/engineering/engineering-feishu-integration-developer.md +597 -0
- package/data/zh/engineering/engineering-filament-optimization-specialist.md +283 -0
- package/data/zh/engineering/engineering-finops-engineer.md +40 -0
- package/data/zh/engineering/engineering-frontend-developer.md +224 -0
- package/data/zh/engineering/engineering-gaussdb-expert.md +40 -0
- package/data/zh/engineering/engineering-git-workflow-master.md +220 -0
- package/data/zh/engineering/engineering-i18n-engineer.md +40 -0
- package/data/zh/engineering/engineering-identity-access-engineer.md +40 -0
- package/data/zh/engineering/engineering-incident-response-commander.md +465 -0
- package/data/zh/engineering/engineering-iot-fleet-engineer.md +40 -0
- package/data/zh/engineering/engineering-it-service-manager.md +560 -0
- package/data/zh/engineering/engineering-knowledge-graph-engineer.md +40 -0
- package/data/zh/engineering/engineering-llm-post-training-engineer.md +40 -0
- package/data/zh/engineering/engineering-minimal-change-engineer.md +206 -0
- package/data/zh/engineering/engineering-mobile-app-builder.md +434 -0
- package/data/zh/engineering/engineering-mobile-release-engineer.md +40 -0
- package/data/zh/engineering/engineering-multi-agent-systems-architect.md +599 -0
- package/data/zh/engineering/engineering-network-engineer.md +40 -0
- package/data/zh/engineering/engineering-orgscript-engineer.md +112 -0
- package/data/zh/engineering/engineering-payments-billing-engineer.md +40 -0
- package/data/zh/engineering/engineering-pdf-engine-architect.md +666 -0
- package/data/zh/engineering/engineering-platform-engineer.md +271 -0
- package/data/zh/engineering/engineering-privacy-engineer.md +40 -0
- package/data/zh/engineering/engineering-prompt-engineer.md +203 -0
- package/data/zh/engineering/engineering-rag-pipeline-engineer.md +40 -0
- package/data/zh/engineering/engineering-rapid-prototyper.md +461 -0
- package/data/zh/engineering/engineering-realtime-collaboration-engineer.md +40 -0
- package/data/zh/engineering/engineering-rust-refactoring-specialist.md +40 -0
- package/data/zh/engineering/engineering-search-relevance-engineer.md +40 -0
- package/data/zh/engineering/engineering-section-508-specialist.md +40 -0
- package/data/zh/engineering/engineering-senior-developer.md +177 -0
- package/data/zh/engineering/engineering-software-architect.md +200 -0
- package/data/zh/engineering/engineering-solidity-smart-contract-engineer.md +541 -0
- package/data/zh/engineering/engineering-sre.md +233 -0
- package/data/zh/engineering/engineering-technical-writer.md +409 -0
- package/data/zh/engineering/engineering-universal-document-compiler.md +344 -0
- package/data/zh/engineering/engineering-uswds-developer.md +40 -0
- package/data/zh/engineering/engineering-video-streaming-engineer.md +40 -0
- package/data/zh/engineering/engineering-voice-ai-integration-engineer.md +560 -0
- package/data/zh/engineering/engineering-webassembly-engineer.md +40 -0
- package/data/zh/engineering/engineering-wechat-mini-program-developer.md +288 -0
- package/data/zh/engineering/engineering-wordpress-performance.md +40 -0
- package/data/zh/engineering/engineering-wordpress-shopping-cart.md +345 -0
- package/data/zh/finance/finance-bookkeeper-controller.md +271 -0
- package/data/zh/finance/finance-financial-analyst.md +244 -0
- package/data/zh/finance/finance-fpa-analyst.md +272 -0
- package/data/zh/finance/finance-investment-researcher.md +283 -0
- package/data/zh/finance/finance-tax-strategist.md +250 -0
- package/data/zh/game-development/blender/blender-addon-engineer.md +233 -0
- package/data/zh/game-development/economy-designer.md +40 -0
- package/data/zh/game-development/game-audio-engineer.md +265 -0
- package/data/zh/game-development/game-designer.md +168 -0
- package/data/zh/game-development/godot/godot-gameplay-scripter.md +335 -0
- package/data/zh/game-development/godot/godot-multiplayer-engineer.md +296 -0
- package/data/zh/game-development/godot/godot-shader-developer.md +267 -0
- package/data/zh/game-development/level-designer.md +209 -0
- package/data/zh/game-development/narrative-designer.md +244 -0
- package/data/zh/game-development/roblox-studio/roblox-avatar-creator.md +298 -0
- package/data/zh/game-development/roblox-studio/roblox-experience-designer.md +306 -0
- package/data/zh/game-development/roblox-studio/roblox-systems-scripter.md +325 -0
- package/data/zh/game-development/technical-artist.md +230 -0
- package/data/zh/game-development/unity/unity-architect.md +272 -0
- package/data/zh/game-development/unity/unity-editor-tool-developer.md +300 -0
- package/data/zh/game-development/unity/unity-multiplayer-engineer.md +238 -0
- package/data/zh/game-development/unity/unity-shader-graph-artist.md +270 -0
- package/data/zh/game-development/unreal-engine/unreal-multiplayer-architect.md +314 -0
- package/data/zh/game-development/unreal-engine/unreal-systems-engineer.md +311 -0
- package/data/zh/game-development/unreal-engine/unreal-technical-artist.md +256 -0
- package/data/zh/game-development/unreal-engine/unreal-world-builder.md +274 -0
- package/data/zh/gis/gis-3d-scene-developer.md +110 -0
- package/data/zh/gis/gis-analyst.md +90 -0
- package/data/zh/gis/gis-bim-specialist.md +107 -0
- package/data/zh/gis/gis-cartography-designer.md +149 -0
- package/data/zh/gis/gis-drone-reality-mapping.md +119 -0
- package/data/zh/gis/gis-geoai-ml-engineer.md +104 -0
- package/data/zh/gis/gis-geoprocessing-specialist.md +96 -0
- package/data/zh/gis/gis-qa-engineer.md +132 -0
- package/data/zh/gis/gis-solution-engineer.md +100 -0
- package/data/zh/gis/gis-spatial-data-engineer.md +96 -0
- package/data/zh/gis/gis-spatial-data-scientist.md +110 -0
- package/data/zh/gis/gis-technical-consultant.md +85 -0
- package/data/zh/gis/gis-web-gis-developer.md +107 -0
- package/data/zh/healthcare/healthcare-clinical-evidence-agent.md +40 -0
- package/data/zh/healthcare/healthcare-innovation-strategist.md +40 -0
- package/data/zh/healthcare/healthcare-sovereign-health-systems-agent.md +40 -0
- package/data/zh/manual-bodies/engineering/engineering-ats-validator-architect.md +383 -0
- package/data/zh/manual-bodies/engineering/engineering-china-network-engineer.md +252 -0
- package/data/zh/manual-bodies/engineering/engineering-pdf-engine-architect.md +666 -0
- package/data/zh/manual-bodies/engineering/engineering-platform-engineer.md +271 -0
- package/data/zh/manual-bodies/engineering/engineering-universal-document-compiler.md +344 -0
- package/data/zh/manual-bodies/specialized/specialized-focus-music-architect.md +172 -0
- package/data/zh/manual.json +22 -0
- package/data/zh/marketing/marketing-aeo-foundations.md +263 -0
- package/data/zh/marketing/marketing-agentic-search-optimizer.md +312 -0
- package/data/zh/marketing/marketing-ai-citation-strategist.md +169 -0
- package/data/zh/marketing/marketing-app-store-optimizer.md +319 -0
- package/data/zh/marketing/marketing-baidu-seo-specialist.md +220 -0
- package/data/zh/marketing/marketing-bilibili-content-strategist.md +40 -0
- package/data/zh/marketing/marketing-book-co-author.md +109 -0
- package/data/zh/marketing/marketing-carousel-growth-engine.md +215 -0
- package/data/zh/marketing/marketing-china-ecommerce-operator.md +277 -0
- package/data/zh/marketing/marketing-china-market-localization-strategist.md +282 -0
- package/data/zh/marketing/marketing-content-creator.md +145 -0
- package/data/zh/marketing/marketing-cross-border-ecommerce.md +260 -0
- package/data/zh/marketing/marketing-douyin-strategist.md +150 -0
- package/data/zh/marketing/marketing-email-strategist.md +248 -0
- package/data/zh/marketing/marketing-global-podcast-strategist.md +204 -0
- package/data/zh/marketing/marketing-growth-hacker.md +121 -0
- package/data/zh/marketing/marketing-instagram-curator.md +179 -0
- package/data/zh/marketing/marketing-kuaishou-strategist.md +182 -0
- package/data/zh/marketing/marketing-linkedin-content-creator.md +232 -0
- package/data/zh/marketing/marketing-livestream-commerce-coach.md +303 -0
- package/data/zh/marketing/marketing-multi-platform-publisher.md +216 -0
- package/data/zh/marketing/marketing-podcast-strategist.md +278 -0
- package/data/zh/marketing/marketing-pr-communications-manager.md +472 -0
- package/data/zh/marketing/marketing-private-domain-operator.md +309 -0
- package/data/zh/marketing/marketing-reddit-community-builder.md +127 -0
- package/data/zh/marketing/marketing-seo-specialist.md +298 -0
- package/data/zh/marketing/marketing-short-video-editing-coach.md +413 -0
- package/data/zh/marketing/marketing-social-media-strategist.md +118 -0
- package/data/zh/marketing/marketing-tiktok-strategist.md +124 -0
- package/data/zh/marketing/marketing-twitter-engager.md +132 -0
- package/data/zh/marketing/marketing-video-optimization-specialist.md +128 -0
- package/data/zh/marketing/marketing-wechat-official-account.md +158 -0
- package/data/zh/marketing/marketing-weibo-strategist.md +241 -0
- package/data/zh/marketing/marketing-x-twitter-intelligence-analyst.md +160 -0
- package/data/zh/marketing/marketing-xiaohongshu-specialist.md +151 -0
- package/data/zh/marketing/marketing-zhihu-strategist.md +175 -0
- package/data/zh/names.json +281 -0
- package/data/zh/paid-media/paid-media-auditor.md +170 -0
- package/data/zh/paid-media/paid-media-creative-strategist.md +173 -0
- package/data/zh/paid-media/paid-media-paid-social-strategist.md +180 -0
- package/data/zh/paid-media/paid-media-ppc-strategist.md +180 -0
- package/data/zh/paid-media/paid-media-programmatic-buyer.md +177 -0
- package/data/zh/paid-media/paid-media-search-query-analyst.md +182 -0
- package/data/zh/paid-media/paid-media-tracking-specialist.md +199 -0
- package/data/zh/product/product-behavioral-nudge-engine.md +246 -0
- package/data/zh/product/product-feedback-synthesizer.md +175 -0
- package/data/zh/product/product-manager.md +474 -0
- package/data/zh/product/product-sprint-prioritizer.md +133 -0
- package/data/zh/product/product-trend-researcher.md +143 -0
- package/data/zh/project-management/project-management-experiment-tracker.md +206 -0
- package/data/zh/project-management/project-management-jira-workflow-steward.md +249 -0
- package/data/zh/project-management/project-management-meeting-notes-specialist.md +94 -0
- package/data/zh/project-management/project-management-project-shepherd.md +202 -0
- package/data/zh/project-management/project-management-studio-operations.md +208 -0
- package/data/zh/project-management/project-management-studio-producer.md +211 -0
- package/data/zh/project-management/project-manager-senior.md +135 -0
- package/data/zh/research/research-synthesist.md +40 -0
- package/data/zh/sales/sales-account-strategist.md +243 -0
- package/data/zh/sales/sales-coach.md +291 -0
- package/data/zh/sales/sales-deal-strategist.md +204 -0
- package/data/zh/sales/sales-discovery-coach.md +230 -0
- package/data/zh/sales/sales-engineer.md +200 -0
- package/data/zh/sales/sales-offer-lead-gen-strategist.md +256 -0
- package/data/zh/sales/sales-outbound-strategist.md +208 -0
- package/data/zh/sales/sales-pipeline-analyst.md +284 -0
- package/data/zh/sales/sales-proposal-strategist.md +233 -0
- package/data/zh/security/security-ai-generated-code-auditor.md +40 -0
- package/data/zh/security/security-appsec-engineer.md +490 -0
- package/data/zh/security/security-architect.md +303 -0
- package/data/zh/security/security-blockchain-security-auditor.md +462 -0
- package/data/zh/security/security-cloud-security-architect.md +522 -0
- package/data/zh/security/security-compliance-auditor.md +157 -0
- package/data/zh/security/security-incident-responder.md +436 -0
- package/data/zh/security/security-penetration-tester.md +398 -0
- package/data/zh/security/security-secrets-credential-engineer.md +40 -0
- package/data/zh/security/security-senior-secops.md +751 -0
- package/data/zh/security/security-threat-detection-engineer.md +533 -0
- package/data/zh/security/security-threat-intelligence-analyst.md +643 -0
- package/data/zh/spatial-computing/macos-spatial-metal-engineer.md +337 -0
- package/data/zh/spatial-computing/terminal-integration-specialist.md +236 -0
- package/data/zh/spatial-computing/visionos-spatial-engineer.md +282 -0
- package/data/zh/spatial-computing/xr-cockpit-interaction-specialist.md +220 -0
- package/data/zh/spatial-computing/xr-immersive-developer.md +229 -0
- package/data/zh/spatial-computing/xr-interface-architect.md +253 -0
- package/data/zh/specialized/accounts-payable-agent.md +212 -0
- package/data/zh/specialized/agentic-identity-trust.md +388 -0
- package/data/zh/specialized/agents-orchestrator.md +366 -0
- package/data/zh/specialized/automation-governance-architect.md +215 -0
- package/data/zh/specialized/business-strategist.md +487 -0
- package/data/zh/specialized/change-management-consultant.md +495 -0
- package/data/zh/specialized/chief-financial-officer.md +390 -0
- package/data/zh/specialized/corporate-training-designer.md +191 -0
- package/data/zh/specialized/customer-service.md +40 -0
- package/data/zh/specialized/customer-success-manager.md +458 -0
- package/data/zh/specialized/data-consolidation-agent.md +327 -0
- package/data/zh/specialized/data-privacy-officer.md +411 -0
- package/data/zh/specialized/esg-sustainability-officer.md +395 -0
- package/data/zh/specialized/government-digital-presales-consultant.md +362 -0
- package/data/zh/specialized/grant-writer.md +510 -0
- package/data/zh/specialized/healthcare-aging-parent-care-companion.md +40 -0
- package/data/zh/specialized/healthcare-customer-service.md +388 -0
- package/data/zh/specialized/healthcare-marketing-compliance.md +394 -0
- package/data/zh/specialized/hospitality-guest-services.md +597 -0
- package/data/zh/specialized/hr-onboarding.md +450 -0
- package/data/zh/specialized/identity-graph-operator.md +270 -0
- package/data/zh/specialized/language-translator.md +275 -0
- package/data/zh/specialized/legal-billing-time-tracking.md +566 -0
- package/data/zh/specialized/legal-client-intake.md +487 -0
- package/data/zh/specialized/legal-document-review.md +452 -0
- package/data/zh/specialized/loan-officer-assistant.md +549 -0
- package/data/zh/specialized/lsp-index-engineer.md +334 -0
- package/data/zh/specialized/ma-integration-manager.md +426 -0
- package/data/zh/specialized/medical-billing-coding-specialist.md +489 -0
- package/data/zh/specialized/operations-manager.md +398 -0
- package/data/zh/specialized/organizational-psychologist.md +390 -0
- package/data/zh/specialized/personal-growth-mentor.md +158 -0
- package/data/zh/specialized/real-estate-buyer-seller.md +594 -0
- package/data/zh/specialized/recruitment-specialist.md +49 -0
- package/data/zh/specialized/report-distribution-agent.md +354 -0
- package/data/zh/specialized/resume-tailor.md +40 -0
- package/data/zh/specialized/retail-customer-returns.md +564 -0
- package/data/zh/specialized/sales-data-extraction-agent.md +159 -0
- package/data/zh/specialized/sales-outreach.md +40 -0
- package/data/zh/specialized/specialized-chief-of-staff.md +40 -0
- package/data/zh/specialized/specialized-civil-engineer.md +355 -0
- package/data/zh/specialized/specialized-codebase-archaeologist.md +40 -0
- package/data/zh/specialized/specialized-cultural-intelligence-strategist.md +168 -0
- package/data/zh/specialized/specialized-developer-advocate.md +334 -0
- package/data/zh/specialized/specialized-document-generator.md +346 -0
- package/data/zh/specialized/specialized-fedramp-rmf-compliance.md +40 -0
- package/data/zh/specialized/specialized-focus-music-architect.md +172 -0
- package/data/zh/specialized/specialized-french-consulting-market.md +46 -0
- package/data/zh/specialized/specialized-korean-business-navigator.md +46 -0
- package/data/zh/specialized/specialized-master-plan-architect.md +40 -0
- package/data/zh/specialized/specialized-mcp-builder.md +351 -0
- package/data/zh/specialized/specialized-model-qa.md +507 -0
- package/data/zh/specialized/specialized-pricing-analyst.md +242 -0
- package/data/zh/specialized/specialized-salesforce-architect.md +179 -0
- package/data/zh/specialized/specialized-strategy-duel-agent.md +129 -0
- package/data/zh/specialized/specialized-workflow-architect.md +596 -0
- package/data/zh/specialized/study-abroad-advisor.md +281 -0
- package/data/zh/specialized/supply-chain-strategist.md +581 -0
- package/data/zh/specialized/zk-steward.md +228 -0
- package/data/zh/support/support-analytics-reporter.md +364 -0
- package/data/zh/support/support-executive-summary-generator.md +217 -0
- package/data/zh/support/support-finance-tracker.md +447 -0
- package/data/zh/support/support-infrastructure-maintainer.md +623 -0
- package/data/zh/support/support-legal-compliance-checker.md +587 -0
- package/data/zh/support/support-support-responder.md +584 -0
- package/data/zh/testing/testing-accessibility-auditor.md +329 -0
- package/data/zh/testing/testing-api-tester.md +305 -0
- package/data/zh/testing/testing-evidence-collector.md +153 -0
- package/data/zh/testing/testing-performance-benchmarker.md +196 -0
- package/data/zh/testing/testing-reality-checker.md +235 -0
- package/data/zh/testing/testing-test-automation-engineer.md +40 -0
- package/data/zh/testing/testing-test-results-analyzer.md +313 -0
- package/data/zh/testing/testing-tool-evaluator.md +402 -0
- package/data/zh/testing/testing-workflow-optimizer.md +458 -0
- package/lib/assets/t-team/action-celebrating-v2.png +0 -0
- package/lib/assets/t-team/action-reporting-v2.png +0 -0
- package/lib/assets/t-team/action-sending-v2.png +0 -0
- package/lib/assets/t-team/action-sleeping-v2.png +0 -0
- package/lib/assets/t-team/action-thinking-v2.png +0 -0
- package/lib/assets/t-team/action-working-v2.png +0 -0
- package/lib/assets/t-team/member-data-v2.png +0 -0
- package/lib/assets/t-team/member-designer-v2.png +0 -0
- package/lib/assets/t-team/member-docs-v2.png +0 -0
- package/lib/assets/t-team/member-engineer-v2.png +0 -0
- package/lib/assets/t-team/member-operator-v2.png +0 -0
- package/lib/assets/t-team/member-qa-v2.png +0 -0
- package/lib/assets/t-team/member-researcher-v2.png +0 -0
- package/lib/assets/t-team/member-security-v2.png +0 -0
- package/lib/assets/t-team/team-lead-v2.png +0 -0
- package/lib/bootstrap.js +85 -0
- package/lib/catalog.js +376 -0
- package/lib/client.js +21034 -0
- package/lib/command.js +228 -0
- package/lib/i18n.js +136 -0
- package/lib/index.js +686 -0
- package/lib/plan-check.js +171 -0
- package/lib/remote.js +353 -0
- package/lib/squads.js +231 -0
- package/lib/teams/capabilities.js +106 -0
- package/lib/teams/command.js +143 -0
- package/lib/teams/event-types.js +12 -0
- package/lib/teams/events.js +60 -0
- package/lib/teams/harness-compat.js +179 -0
- package/lib/teams/index.js +441 -0
- package/lib/teams/members.js +602 -0
- package/lib/teams/profiles.js +572 -0
- package/lib/teams/quality-gates.js +822 -0
- package/lib/teams/scheduler.js +434 -0
- package/lib/teams/snapshot.js +181 -0
- package/lib/teams/state.js +925 -0
- package/lib/teams/tool-names.js +11 -0
- package/lib/teams/tools.js +2279 -0
- package/lib/teams/types.js +22 -0
- package/lib/teams/web-routes.js +86 -0
- package/package.json +191 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-driven shared task scheduler.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code teammates keep polling the shared task list after a turn. DSH
|
|
5
|
+
* continuable agents instead expose explicit idle/running edges, so this
|
|
6
|
+
* scheduler closes the same loop without keeping a polling turn alive: every
|
|
7
|
+
* idle edge and every task-graph mutation attempts one atomic claim and wakes
|
|
8
|
+
* the selected durable member. A resident member that becomes idle while it
|
|
9
|
+
* still owns an open attempt is parked: only an explicit captain reassignment
|
|
10
|
+
* may rotate that capability. Automatic retry is reserved for cold recovery,
|
|
11
|
+
* when this process has not observed the durable owner settle its open attempt.
|
|
12
|
+
* @module t-team/scheduler
|
|
13
|
+
*/
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { deliverToMember } from "./members.js";
|
|
16
|
+
import { acknowledgeMailbox, beginTaskAttempt, CAPTAIN_KEY, claimMailboxDelivery, findTeamByParticipant, invalidateTaskAttempt, readTeam, readUnreadMailbox, releaseMailboxDelivery, unsatisfiedDependencies, withTeamLock, writeTeam, } from "./state.js";
|
|
17
|
+
/** Per-dependency output cap in the assignment prompt. */
|
|
18
|
+
export const DEPENDENCY_OUTPUT_MAX_CHARS = 2_000;
|
|
19
|
+
/** Combined dependency-output budget in the assignment prompt. */
|
|
20
|
+
export const DEPENDENCY_OUTPUTS_TOTAL_MAX_CHARS = 12_000;
|
|
21
|
+
function taskProfileSeedId(task) {
|
|
22
|
+
const seed = task.profileSeedId?.trim();
|
|
23
|
+
return seed === undefined || seed === '' ? undefined : seed;
|
|
24
|
+
}
|
|
25
|
+
function teamProfileProtocol(team) {
|
|
26
|
+
return team.profile?.protocol;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Recursively collect `status=completed` ancestors of `taskId` in topological
|
|
30
|
+
* order (dependencies before dependents). Cycles stop that branch only.
|
|
31
|
+
*/
|
|
32
|
+
export function collectCompletedDependencyOutputs(tasks, taskId, warn) {
|
|
33
|
+
const byId = new Map(tasks.map(task => [task.id, task]));
|
|
34
|
+
const visiting = new Set();
|
|
35
|
+
const visited = new Set();
|
|
36
|
+
const ordered = [];
|
|
37
|
+
const walk = (id) => {
|
|
38
|
+
if (visiting.has(id)) {
|
|
39
|
+
warn?.(`t-team: dependency cycle involving "${id}" while collecting outputs; stopping this branch`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (visited.has(id))
|
|
43
|
+
return;
|
|
44
|
+
visiting.add(id);
|
|
45
|
+
const task = byId.get(id);
|
|
46
|
+
if (task !== undefined) {
|
|
47
|
+
for (const dependency of task.dependencies)
|
|
48
|
+
walk(dependency);
|
|
49
|
+
if (id !== taskId)
|
|
50
|
+
ordered.push(task);
|
|
51
|
+
}
|
|
52
|
+
visiting.delete(id);
|
|
53
|
+
visited.add(id);
|
|
54
|
+
};
|
|
55
|
+
walk(taskId);
|
|
56
|
+
return ordered
|
|
57
|
+
.filter(task => task.status === 'completed')
|
|
58
|
+
.map((task) => {
|
|
59
|
+
const profileSeedId = taskProfileSeedId(task);
|
|
60
|
+
return {
|
|
61
|
+
id: task.id,
|
|
62
|
+
subject: task.subject,
|
|
63
|
+
...profileSeedId === undefined ? {} : { profileSeedId },
|
|
64
|
+
...task.output === undefined ? {} : { output: task.output },
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Format completed-dependency outputs with per-item and total truncation. */
|
|
69
|
+
export function formatDependencyOutputs(items) {
|
|
70
|
+
if (items.length === 0)
|
|
71
|
+
return '(none)';
|
|
72
|
+
const formatted = items.map((item) => {
|
|
73
|
+
const seed = item.profileSeedId === undefined ? '' : ` [${item.profileSeedId}]`;
|
|
74
|
+
const raw = item.output === undefined || item.output === ''
|
|
75
|
+
? '(no output recorded)'
|
|
76
|
+
: item.output;
|
|
77
|
+
const truncated = raw.length > DEPENDENCY_OUTPUT_MAX_CHARS;
|
|
78
|
+
const body = truncated ? `${raw.slice(0, DEPENDENCY_OUTPUT_MAX_CHARS)} [truncated]` : raw;
|
|
79
|
+
return `- ${item.id}${seed} ${item.subject}:\n ${body}`;
|
|
80
|
+
});
|
|
81
|
+
let selected = formatted;
|
|
82
|
+
while (selected.length > 1 && selected.join('\n').length > DEPENDENCY_OUTPUTS_TOTAL_MAX_CHARS) {
|
|
83
|
+
selected = selected.slice(1);
|
|
84
|
+
}
|
|
85
|
+
const last = selected[0];
|
|
86
|
+
if (selected.length === 1 && last !== undefined && last.length > DEPENDENCY_OUTPUTS_TOTAL_MAX_CHARS) {
|
|
87
|
+
selected = [`${last.slice(0, DEPENDENCY_OUTPUTS_TOTAL_MAX_CHARS)} [truncated]`];
|
|
88
|
+
}
|
|
89
|
+
return selected.join('\n');
|
|
90
|
+
}
|
|
91
|
+
function stateRootOf(workspace, config) {
|
|
92
|
+
return join(workspace, config.stateDir);
|
|
93
|
+
}
|
|
94
|
+
function teamLockKey(stateRoot, teamId) {
|
|
95
|
+
return `team:${stateRoot}:${teamId}`;
|
|
96
|
+
}
|
|
97
|
+
function liveCaptain(ctx, captainSessionId, supplied) {
|
|
98
|
+
if (supplied !== undefined && supplied.id === captainSessionId)
|
|
99
|
+
return supplied;
|
|
100
|
+
return ctx.agents.get(captainSessionId);
|
|
101
|
+
}
|
|
102
|
+
function liveMember(ctx, member) {
|
|
103
|
+
return ctx.agents.get(member.id);
|
|
104
|
+
}
|
|
105
|
+
function isMemberAvailable(ctx, member) {
|
|
106
|
+
const live = liveMember(ctx, member);
|
|
107
|
+
return live === undefined || live.status === 'idle';
|
|
108
|
+
}
|
|
109
|
+
function ownedOpenTask(tasks, memberName) {
|
|
110
|
+
return tasks.find(task => task.assignee === memberName
|
|
111
|
+
&& (task.status === 'claimed' || task.status === 'in_progress'));
|
|
112
|
+
}
|
|
113
|
+
function nextReadyTask(tasks, memberName) {
|
|
114
|
+
const ready = tasks.filter(task => task.status === 'pending'
|
|
115
|
+
&& task.reassigning !== true
|
|
116
|
+
&& unsatisfiedDependencies([...tasks], task.dependencies).length === 0);
|
|
117
|
+
return ready.find(task => task.assignee === memberName)
|
|
118
|
+
?? ready.find(task => task.assignee === undefined);
|
|
119
|
+
}
|
|
120
|
+
export function assignmentPrompt(ticket, stateDir, teamId) {
|
|
121
|
+
const description = ticket.description === undefined ? '' : `\n\n${ticket.description}`;
|
|
122
|
+
const seed = ticket.profileSeedId === undefined ? '' : ` [${ticket.profileSeedId}]`;
|
|
123
|
+
const goal = ticket.teamDescription?.trim() || '(not provided)';
|
|
124
|
+
const protocol = ticket.profileProtocol?.trim() || '(none)';
|
|
125
|
+
const executionPrompt = ticket.executionPrompt?.trim();
|
|
126
|
+
const kind = ticket.kind?.trim() || 'work';
|
|
127
|
+
const contract = [
|
|
128
|
+
`Kind: ${kind}${ticket.round === undefined ? '' : ` (round ${ticket.round})`}`,
|
|
129
|
+
ticket.objective === undefined || ticket.objective === '' ? '' : `Objective: ${ticket.objective}`,
|
|
130
|
+
ticket.inScope === undefined || ticket.inScope.length === 0 ? '' : `In scope: ${ticket.inScope.join(', ')}`,
|
|
131
|
+
ticket.outOfScope === undefined || ticket.outOfScope.length === 0 ? '' : `Out of scope: ${ticket.outOfScope.join(', ')}`,
|
|
132
|
+
ticket.acceptance === undefined || ticket.acceptance.length === 0 ? '' : `Acceptance: ${ticket.acceptance.join('; ')}`,
|
|
133
|
+
ticket.verify === undefined || ticket.verify.length === 0 ? '' : `Verify: ${ticket.verify.join('; ')}`,
|
|
134
|
+
ticket.reviewedTaskId === undefined ? '' : `Reviewed task: ${ticket.reviewedTaskId}`,
|
|
135
|
+
].filter((line) => line !== '').join('\n');
|
|
136
|
+
const structuredCompletion = ['implementation', 'repair', 'verification', 'integration'].includes(kind)
|
|
137
|
+
? `
|
|
138
|
+
Structured completion payload (keep these arrays in contract order):
|
|
139
|
+
acceptanceResults: ${JSON.stringify((ticket.acceptance ?? []).map((criterion) => ({ criterion, status: 'passed', evidence: '<what proved it>' })))}
|
|
140
|
+
commandsRun: ${JSON.stringify((ticket.verify ?? []).map((command) => ({ command, status: 'passed', exitCode: 0, evidence: '<observed result>' })))}
|
|
141
|
+
${kind === 'implementation' || kind === 'repair' ? 'changedPaths: list the actual workspace-relative POSIX paths you changed.\n' : ''}`
|
|
142
|
+
: '';
|
|
143
|
+
return `T Team automatic task assignment from the shared task list.
|
|
144
|
+
|
|
145
|
+
You are executing as configured member "${ticket.memberName}".
|
|
146
|
+
Do not start a teammate's assigned task.
|
|
147
|
+
|
|
148
|
+
Team goal:
|
|
149
|
+
${goal}
|
|
150
|
+
|
|
151
|
+
Profile protocol:
|
|
152
|
+
${protocol}
|
|
153
|
+
${executionPrompt === undefined || executionPrompt === '' ? '' : `
|
|
154
|
+
Execution guidance:
|
|
155
|
+
${executionPrompt}
|
|
156
|
+
`}
|
|
157
|
+
Completed dependency results:
|
|
158
|
+
${formatDependencyOutputs(ticket.dependencyOutputs)}
|
|
159
|
+
|
|
160
|
+
Task: ${ticket.taskId}${seed} — ${ticket.subject}${description}
|
|
161
|
+
${contract === '' ? '' : `\nContract:\n${contract}\n`}
|
|
162
|
+
${structuredCompletion}
|
|
163
|
+
Attempt: ${ticket.attempt}
|
|
164
|
+
Attempt id: ${ticket.attemptId}
|
|
165
|
+
|
|
166
|
+
Call t_team_claim_task for ${ticket.taskId}; it will return this same attempt_id. Include attempt_id=${ticket.attemptId} in every t_team_update_task call. If it is rejected as stale, stop work because the task was reassigned. claimed cannot jump to completed. Mark in_progress first, then completed or failed. Include attempt_id on every update. Then send_message to captain and become idle.
|
|
167
|
+
When finishing: use status=completed only when the task's success criteria are satisfied; use status=failed when blocking findings or validation failures mean downstream work must not proceed; include a concise output in either case. Quality kinds must submit structured fields: review/requirements need verdict=pass to complete (needs_revision/reject must fail with findings); implementation/repair/verification/integration need acceptanceResults and commandsRun, while implementation/repair also need in-scope changedPaths. Use status values "passed" or "failed" inside those arrays. After the work and verification finish, call t_team_update_task immediately; do not wait for captain confirmation and do not continue exploring. Do not approve your own implementation. Mail is not a formal next review. Treat the dependency results above as source material. Do not ignore them. Work only this task and only its in-scope paths in this turn.
|
|
168
|
+
|
|
169
|
+
State policy: ${stateDir}/${teamId}/ is read-only diagnostics; mutate team state only through t_team_* tools.`;
|
|
170
|
+
}
|
|
171
|
+
function fallbackMailboxPrompt(messages) {
|
|
172
|
+
return [
|
|
173
|
+
'T Team delivered messages that were persisted while live delivery was unavailable:',
|
|
174
|
+
...messages.map(message => `\nFrom ${message.from}:\n${message.content}`),
|
|
175
|
+
'\nHandle these messages in this turn. Task assignments still require t_team_claim_task and the current attempt_id.',
|
|
176
|
+
].join('\n');
|
|
177
|
+
}
|
|
178
|
+
/** Install one scheduler and its member activity observer. */
|
|
179
|
+
export function installTeamScheduler(ctx, config) {
|
|
180
|
+
const memberQueues = new Map();
|
|
181
|
+
// An idle edge in this process proves that the resident member ended its
|
|
182
|
+
// turn while the current attempt was still open. Remember that capability
|
|
183
|
+
// even after Harness disposes the continuable AgentHandle: later status or
|
|
184
|
+
// graph kicks must keep it parked. A cold process starts with an empty map,
|
|
185
|
+
// so durable open attempts are still recovered after restart.
|
|
186
|
+
const parkedAttempts = new Map();
|
|
187
|
+
const memberQueueKey = (stateRoot, teamId, memberName) => (`${stateRoot}\u0000${teamId}\u0000${memberName}`);
|
|
188
|
+
const serializeMember = async (key, operation) => {
|
|
189
|
+
const previous = memberQueues.get(key) ?? Promise.resolve();
|
|
190
|
+
let release;
|
|
191
|
+
const gate = new Promise((resolve) => { release = resolve; });
|
|
192
|
+
const tail = previous.then(() => gate);
|
|
193
|
+
memberQueues.set(key, tail);
|
|
194
|
+
await previous;
|
|
195
|
+
try {
|
|
196
|
+
return await operation();
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
release();
|
|
200
|
+
if (memberQueues.get(key) === tail)
|
|
201
|
+
memberQueues.delete(key);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const runtime = {
|
|
205
|
+
async kickTeam(workspace, teamId, suppliedCaptain) {
|
|
206
|
+
const stateRoot = stateRootOf(workspace, config);
|
|
207
|
+
const team = await readTeam(stateRoot, teamId);
|
|
208
|
+
if (team === undefined || team.halted === true || team.phase === 'staged')
|
|
209
|
+
return;
|
|
210
|
+
const captain = liveCaptain(ctx, team.captainSessionId, suppliedCaptain);
|
|
211
|
+
if (captain === undefined)
|
|
212
|
+
return;
|
|
213
|
+
for (const member of team.members) {
|
|
214
|
+
if (member.status === 'removed')
|
|
215
|
+
continue;
|
|
216
|
+
await runtime.kickMember(workspace, teamId, member.name, captain);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
async kickMember(workspace, teamId, memberName, suppliedCaptain) {
|
|
220
|
+
const stateRoot = stateRootOf(workspace, config);
|
|
221
|
+
const queueKey = memberQueueKey(stateRoot, teamId, memberName);
|
|
222
|
+
await serializeMember(queueKey, async () => {
|
|
223
|
+
let team = await readTeam(stateRoot, teamId);
|
|
224
|
+
if (team === undefined || team.halted === true || team.phase === 'staged')
|
|
225
|
+
return;
|
|
226
|
+
const captain = liveCaptain(ctx, team.captainSessionId, suppliedCaptain);
|
|
227
|
+
if (captain === undefined)
|
|
228
|
+
return;
|
|
229
|
+
let member = team.members.find(candidate => candidate.name === memberName && candidate.status !== 'removed');
|
|
230
|
+
if (member === undefined || member.id === '' || !isMemberAvailable(ctx, member))
|
|
231
|
+
return;
|
|
232
|
+
// A mailbox-only fallback is real pending work. Deliver it before a
|
|
233
|
+
// fresh task and acknowledge only after Harness accepts the follow-up.
|
|
234
|
+
const unread = await readUnreadMailbox(stateRoot, team.id, member.name);
|
|
235
|
+
if (unread.length > 0) {
|
|
236
|
+
await withTeamLock(teamLockKey(stateRoot, team.id), () => (claimMailboxDelivery(stateRoot, team.id, member.name, unread.map(message => message.id))));
|
|
237
|
+
const accepted = await deliverToMember(ctx, captain, member.id, fallbackMailboxPrompt(unread), new AbortController().signal);
|
|
238
|
+
if (accepted) {
|
|
239
|
+
await withTeamLock(teamLockKey(stateRoot, team.id), () => (acknowledgeMailbox(stateRoot, team.id, member.name, unread.map(message => message.id))));
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
await withTeamLock(teamLockKey(stateRoot, team.id), () => (releaseMailboxDelivery(stateRoot, team.id, member.name, unread.map(message => message.id))));
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const ticket = await withTeamLock(teamLockKey(stateRoot, team.id), async () => {
|
|
247
|
+
const fresh = await readTeam(stateRoot, team.id);
|
|
248
|
+
if (fresh === undefined || fresh.halted === true || fresh.phase === 'staged')
|
|
249
|
+
return undefined;
|
|
250
|
+
const currentMember = fresh.members.find(candidate => candidate.name === memberName && candidate.status !== 'removed');
|
|
251
|
+
if (currentMember === undefined || currentMember.id === '' || !isMemberAvailable(ctx, currentMember))
|
|
252
|
+
return undefined;
|
|
253
|
+
const owned = ownedOpenTask(fresh.tasks, currentMember.name);
|
|
254
|
+
// An idle edge observed by this scheduler parks the exact open
|
|
255
|
+
// capability. Harness may dispose its AgentHandle after settlement,
|
|
256
|
+
// so registry absence is not evidence that the owner was lost. Keep
|
|
257
|
+
// the marker sticky across later kicks; only a durable attempt that
|
|
258
|
+
// this process has not observed is eligible for one cold recovery.
|
|
259
|
+
const parkedAttemptId = parkedAttempts.get(currentMember.id);
|
|
260
|
+
const recoverOwned = owned !== undefined
|
|
261
|
+
&& (owned.attemptId === undefined || owned.attemptId !== parkedAttemptId);
|
|
262
|
+
const task = recoverOwned ? owned : owned === undefined
|
|
263
|
+
? nextReadyTask(fresh.tasks, currentMember.name)
|
|
264
|
+
: undefined;
|
|
265
|
+
if (task === undefined) {
|
|
266
|
+
if (currentMember.status !== 'idle') {
|
|
267
|
+
currentMember.status = 'idle';
|
|
268
|
+
await writeTeam(stateRoot, fresh);
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
const previousAssignee = task.assignee;
|
|
273
|
+
const previousStatus = recoverOwned ? task.status : undefined;
|
|
274
|
+
const previousAttempt = recoverOwned ? task.attempt : undefined;
|
|
275
|
+
const previousAttemptId = recoverOwned ? task.attemptId : undefined;
|
|
276
|
+
const attemptId = beginTaskAttempt(task, currentMember.name);
|
|
277
|
+
// A recovered generation is parked before delivery. This makes each
|
|
278
|
+
// (member, attempt) recovery idempotent even if every status poll
|
|
279
|
+
// sees a disposed handle. Fresh pending work remains unparked so a
|
|
280
|
+
// genuinely lost first delivery can be recovered once.
|
|
281
|
+
if (recoverOwned)
|
|
282
|
+
parkedAttempts.set(currentMember.id, attemptId);
|
|
283
|
+
else
|
|
284
|
+
parkedAttempts.delete(currentMember.id);
|
|
285
|
+
currentMember.status = 'working';
|
|
286
|
+
await writeTeam(stateRoot, fresh);
|
|
287
|
+
const profileSeedId = taskProfileSeedId(task);
|
|
288
|
+
const protocol = teamProfileProtocol(fresh);
|
|
289
|
+
return {
|
|
290
|
+
taskId: task.id,
|
|
291
|
+
memberName: currentMember.name,
|
|
292
|
+
memberId: currentMember.id,
|
|
293
|
+
attempt: task.attempt ?? 1,
|
|
294
|
+
attemptId,
|
|
295
|
+
previousAssignee,
|
|
296
|
+
recoveredOwned: recoverOwned,
|
|
297
|
+
...previousStatus === undefined ? {} : { previousStatus },
|
|
298
|
+
...previousAttempt === undefined ? {} : { previousAttempt },
|
|
299
|
+
...previousAttemptId === undefined ? {} : { previousAttemptId },
|
|
300
|
+
subject: task.subject,
|
|
301
|
+
description: task.description,
|
|
302
|
+
teamDescription: fresh.description,
|
|
303
|
+
...protocol === undefined ? {} : { profileProtocol: protocol },
|
|
304
|
+
...profileSeedId === undefined ? {} : { profileSeedId },
|
|
305
|
+
...fresh.profile?.executionPrompt === undefined && config.executionPrompt === undefined
|
|
306
|
+
? {}
|
|
307
|
+
: { executionPrompt: fresh.profile?.executionPrompt ?? config.executionPrompt },
|
|
308
|
+
kind: task.kind ?? 'work',
|
|
309
|
+
...task.round === undefined ? {} : { round: task.round },
|
|
310
|
+
...task.objective === undefined ? {} : { objective: task.objective },
|
|
311
|
+
...task.inScope === undefined ? {} : { inScope: task.inScope },
|
|
312
|
+
...task.outOfScope === undefined ? {} : { outOfScope: task.outOfScope },
|
|
313
|
+
...task.acceptance === undefined ? {} : { acceptance: task.acceptance },
|
|
314
|
+
...task.verify === undefined ? {} : { verify: task.verify },
|
|
315
|
+
...task.reviewedTaskId === undefined ? {} : { reviewedTaskId: task.reviewedTaskId },
|
|
316
|
+
dependencyOutputs: collectCompletedDependencyOutputs(fresh.tasks, task.id, (message) => ctx.logger.warn(message)),
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
if (ticket === undefined)
|
|
320
|
+
return;
|
|
321
|
+
const accepted = await deliverToMember(ctx, captain, ticket.memberId, assignmentPrompt(ticket, config.stateDir, team.id), new AbortController().signal);
|
|
322
|
+
if (accepted)
|
|
323
|
+
return;
|
|
324
|
+
// Roll back only our exact failed dispatch. A concurrent captain
|
|
325
|
+
// handoff has already changed the capability and wins.
|
|
326
|
+
await withTeamLock(teamLockKey(stateRoot, team.id), async () => {
|
|
327
|
+
const fresh = await readTeam(stateRoot, team.id);
|
|
328
|
+
if (fresh === undefined)
|
|
329
|
+
return;
|
|
330
|
+
const task = fresh.tasks.find(candidate => candidate.id === ticket.taskId);
|
|
331
|
+
if (task?.attemptId !== ticket.attemptId)
|
|
332
|
+
return;
|
|
333
|
+
if (ticket.recoveredOwned && ticket.previousStatus !== undefined && ticket.previousAttemptId !== undefined) {
|
|
334
|
+
// Recovery delivery failed. Restore the durable generation instead
|
|
335
|
+
// of returning it to pending, then keep it parked so later status
|
|
336
|
+
// kicks cannot spend an unbounded sequence of fresh attempts.
|
|
337
|
+
task.status = ticket.previousStatus;
|
|
338
|
+
task.assignee = ticket.previousAssignee;
|
|
339
|
+
task.attempt = ticket.previousAttempt;
|
|
340
|
+
task.attemptId = ticket.previousAttemptId;
|
|
341
|
+
parkedAttempts.set(ticket.memberId, ticket.previousAttemptId);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
task.status = 'pending';
|
|
345
|
+
task.assignee = ticket.previousAssignee;
|
|
346
|
+
task.attemptId = undefined;
|
|
347
|
+
parkedAttempts.delete(ticket.memberId);
|
|
348
|
+
}
|
|
349
|
+
task.handoffId = undefined;
|
|
350
|
+
task.reassigning = false;
|
|
351
|
+
task.updatedAt = Date.now();
|
|
352
|
+
const currentMember = fresh.members.find(candidate => candidate.name === ticket.memberName);
|
|
353
|
+
if (currentMember !== undefined && currentMember.status !== 'removed')
|
|
354
|
+
currentMember.status = 'idle';
|
|
355
|
+
await writeTeam(stateRoot, fresh);
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
const syncMemberStatus = async (agent, status) => {
|
|
361
|
+
const workspace = agent.session.header.cwd ?? process.cwd();
|
|
362
|
+
const stateRoot = stateRootOf(workspace, config);
|
|
363
|
+
const located = await findTeamByParticipant(stateRoot, agent.id);
|
|
364
|
+
if (located === undefined) {
|
|
365
|
+
parkedAttempts.delete(agent.id);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (located.captainSessionId === agent.id) {
|
|
369
|
+
// Captain takeover is scoped to the captain's current turn. Unlike a
|
|
370
|
+
// durable member, the captain has no scheduler lane that can resume an
|
|
371
|
+
// abandoned attempt later. Returning unfinished captain-owned work to
|
|
372
|
+
// the shared pool on the idle edge prevents it from becoming a
|
|
373
|
+
// permanently parked `claimed` task after the captain answers, is
|
|
374
|
+
// interrupted, or the user switches conversations.
|
|
375
|
+
if (status === 'running')
|
|
376
|
+
return;
|
|
377
|
+
let requeued = false;
|
|
378
|
+
await withTeamLock(teamLockKey(stateRoot, located.id), async () => {
|
|
379
|
+
const fresh = await readTeam(stateRoot, located.id);
|
|
380
|
+
if (fresh === undefined || fresh.captainSessionId !== agent.id)
|
|
381
|
+
return;
|
|
382
|
+
for (const task of fresh.tasks) {
|
|
383
|
+
if (task.assignee !== CAPTAIN_KEY
|
|
384
|
+
|| task.status === 'completed'
|
|
385
|
+
|| task.status === 'failed'
|
|
386
|
+
|| task.status === 'cancelled')
|
|
387
|
+
continue;
|
|
388
|
+
invalidateTaskAttempt(task);
|
|
389
|
+
task.reassigning = false;
|
|
390
|
+
requeued = true;
|
|
391
|
+
}
|
|
392
|
+
if (requeued)
|
|
393
|
+
await writeTeam(stateRoot, fresh);
|
|
394
|
+
});
|
|
395
|
+
if (requeued)
|
|
396
|
+
await runtime.kickTeam(workspace, located.id, agent);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const member = located.members.find(candidate => candidate.id === agent.id && candidate.status !== 'removed');
|
|
400
|
+
if (member === undefined) {
|
|
401
|
+
parkedAttempts.delete(agent.id);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
await withTeamLock(teamLockKey(stateRoot, located.id), async () => {
|
|
405
|
+
const fresh = await readTeam(stateRoot, located.id);
|
|
406
|
+
const current = fresh?.members.find(candidate => candidate.id === agent.id && candidate.status !== 'removed');
|
|
407
|
+
if (fresh === undefined || current === undefined)
|
|
408
|
+
return;
|
|
409
|
+
const next = status === 'running' ? 'working' : 'idle';
|
|
410
|
+
if (next === 'idle') {
|
|
411
|
+
const owned = ownedOpenTask(fresh.tasks, current.name);
|
|
412
|
+
if (owned?.attemptId === undefined)
|
|
413
|
+
parkedAttempts.delete(agent.id);
|
|
414
|
+
else
|
|
415
|
+
parkedAttempts.set(agent.id, owned.attemptId);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
parkedAttempts.delete(agent.id);
|
|
419
|
+
}
|
|
420
|
+
if (current.status === next)
|
|
421
|
+
return;
|
|
422
|
+
current.status = next;
|
|
423
|
+
await writeTeam(stateRoot, fresh);
|
|
424
|
+
});
|
|
425
|
+
if (status === 'idle')
|
|
426
|
+
await runtime.kickMember(workspace, located.id, member.name);
|
|
427
|
+
};
|
|
428
|
+
ctx.on('agent/status', ({ agent, status }) => {
|
|
429
|
+
void syncMemberStatus(agent, status).catch((error) => {
|
|
430
|
+
ctx.logger.warn(`t-team: member status scheduling failed for ${agent.id}: ${String(error)}`);
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
return runtime;
|
|
434
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team activity snapshot assembly for the activity panel.
|
|
3
|
+
*
|
|
4
|
+
* Server-side assembly mirrors the Claude Code desktop teamWatcher: read the
|
|
5
|
+
* durable team files (the truth source) and enrich with live subagent
|
|
6
|
+
* activity, so the panel always reflects the on-disk state even when a model
|
|
7
|
+
* skipped a tool "ritual" (e.g. not calling update_task on completion).
|
|
8
|
+
* @module t-team/snapshot
|
|
9
|
+
*/
|
|
10
|
+
import { readdir } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { memberActivity } from "./members.js";
|
|
13
|
+
import { CAPTAIN_KEY, listArchivedTeamIds, readArchivedTeam, readUnreadMailbox, readTeam, taskDepthsById, taskVisualState, } from "./state.js";
|
|
14
|
+
/** The current task of a member: its first unfinished owned task. */
|
|
15
|
+
function currentTaskOf(memberName, tasks) {
|
|
16
|
+
for (const task of tasks) {
|
|
17
|
+
if (task.status === 'in_progress' && task.assignee === memberName)
|
|
18
|
+
return task.id;
|
|
19
|
+
}
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
/** Compact `provider/model` route for the activity panel, or just the model. */
|
|
23
|
+
export function memberModelRoute(member) {
|
|
24
|
+
if (member === undefined)
|
|
25
|
+
return '';
|
|
26
|
+
const provider = member.provider?.trim() ?? '';
|
|
27
|
+
const model = member.model?.trim() ?? '';
|
|
28
|
+
if (provider !== '' && model !== '')
|
|
29
|
+
return `${provider}/${model}`;
|
|
30
|
+
return model;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Assemble one team snapshot from its durable files plus live activity.
|
|
34
|
+
* @param ctx - the plugin context (injects `subagents`, used for activity).
|
|
35
|
+
* @param stateRoot - resolved absolute state root of the owning workspace.
|
|
36
|
+
* @param workspace - display name of the owning workspace.
|
|
37
|
+
* @param state - the durable team record.
|
|
38
|
+
* @returns the panel snapshot.
|
|
39
|
+
*/
|
|
40
|
+
export async function assembleTeamSnapshot(ctx, stateRoot, workspace, state, options = {}) {
|
|
41
|
+
const tasks = state.tasks;
|
|
42
|
+
const depths = taskDepthsById(tasks);
|
|
43
|
+
const roster = options.includeRemoved === true
|
|
44
|
+
? state.members
|
|
45
|
+
: state.members.filter((member) => member.status !== 'removed');
|
|
46
|
+
const activity = options.historic === true
|
|
47
|
+
? new Map()
|
|
48
|
+
: memberActivity(ctx, roster.map((member) => member.id));
|
|
49
|
+
const unreadByMember = new Map();
|
|
50
|
+
for (const member of roster) {
|
|
51
|
+
try {
|
|
52
|
+
unreadByMember.set(member.name, (await readUnreadMailbox(stateRoot, state.id, member.name)).length);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
ctx.logger.warn(`t-team: mailbox read failed for ${member.name}: ${String(error)}`);
|
|
56
|
+
unreadByMember.set(member.name, 0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const members = roster.map((member) => {
|
|
60
|
+
const owned = tasks.filter((task) => task.assignee === member.name);
|
|
61
|
+
const done = owned.filter((task) => task.status === 'completed').length;
|
|
62
|
+
return {
|
|
63
|
+
id: member.id,
|
|
64
|
+
name: member.name,
|
|
65
|
+
role: member.role ?? '',
|
|
66
|
+
provider: member.provider?.trim() ?? '',
|
|
67
|
+
model: member.model?.trim() ?? '',
|
|
68
|
+
reasoningEffort: member.reasoningEffort?.trim() ?? '',
|
|
69
|
+
executionPrompt: member.executionPrompt ?? '',
|
|
70
|
+
status: member.status,
|
|
71
|
+
activity: options.historic === true
|
|
72
|
+
? 'idle'
|
|
73
|
+
: member.id !== ''
|
|
74
|
+
? (activity.get(member.id) === 'running'
|
|
75
|
+
? 'working'
|
|
76
|
+
: activity.get(member.id) === 'idle' || activity.get(member.id) === 'ready'
|
|
77
|
+
? 'idle'
|
|
78
|
+
: 'unknown')
|
|
79
|
+
: state.phase === 'staged' ? 'idle' : 'unknown',
|
|
80
|
+
progress: owned.length === 0 ? 0 : Math.round((done / owned.length) * 100),
|
|
81
|
+
done,
|
|
82
|
+
total: owned.length,
|
|
83
|
+
currentTask: currentTaskOf(member.name, tasks),
|
|
84
|
+
unread: unreadByMember.get(member.name) ?? 0,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
const captainInbox = await readUnreadMailbox(stateRoot, state.id, CAPTAIN_KEY);
|
|
88
|
+
return {
|
|
89
|
+
workspace,
|
|
90
|
+
teamId: state.id,
|
|
91
|
+
name: state.name,
|
|
92
|
+
...state.description !== undefined ? { description: state.description } : {},
|
|
93
|
+
captainSessionId: state.captainSessionId,
|
|
94
|
+
phase: state.phase ?? 'running',
|
|
95
|
+
...state.phase === 'staged'
|
|
96
|
+
? { planReviewState: state.planReviewState ?? 'awaiting_review' }
|
|
97
|
+
: {},
|
|
98
|
+
...state.halted === true ? { halted: true } : {},
|
|
99
|
+
members,
|
|
100
|
+
tasks: tasks.map((task) => ({
|
|
101
|
+
id: task.id,
|
|
102
|
+
subject: task.subject,
|
|
103
|
+
description: task.description ?? '',
|
|
104
|
+
status: task.status,
|
|
105
|
+
state: taskVisualState(task.status, task.dependencies, tasks),
|
|
106
|
+
assignee: task.assignee ?? '',
|
|
107
|
+
model: memberModelRoute(roster.find((member) => member.name === task.assignee)),
|
|
108
|
+
dependencies: task.dependencies,
|
|
109
|
+
depth: depths.get(task.id) ?? 0,
|
|
110
|
+
...task.kind === undefined ? {} : { kind: task.kind },
|
|
111
|
+
...task.round === undefined ? {} : { round: task.round },
|
|
112
|
+
...task.verdict === undefined ? {} : { verdict: task.verdict },
|
|
113
|
+
})),
|
|
114
|
+
messageCount: captainInbox.length
|
|
115
|
+
+ members.reduce((count, member) => count + member.unread, 0),
|
|
116
|
+
captainInbox: captainInbox.slice(-5).map((message) => ({
|
|
117
|
+
from: message.from,
|
|
118
|
+
content: message.content,
|
|
119
|
+
})),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Collect every team under the given workspace state roots.
|
|
124
|
+
* @param ctx - the plugin context.
|
|
125
|
+
* @param roots - `{ workspace, stateRoot }` pairs (resolved absolute roots).
|
|
126
|
+
* @returns the snapshots in stable order (workspace, then team id).
|
|
127
|
+
*/
|
|
128
|
+
export async function collectTeamsActivity(ctx, roots) {
|
|
129
|
+
const snapshots = [];
|
|
130
|
+
for (const root of roots) {
|
|
131
|
+
let entries;
|
|
132
|
+
try {
|
|
133
|
+
entries = await readdir(root.stateRoot, { withFileTypes: true });
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
if (!entry.isDirectory())
|
|
143
|
+
continue;
|
|
144
|
+
try {
|
|
145
|
+
const state = await readTeam(root.stateRoot, entry.name);
|
|
146
|
+
if (state === undefined)
|
|
147
|
+
continue;
|
|
148
|
+
snapshots.push(await assembleTeamSnapshot(ctx, root.stateRoot, root.workspace, state));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
ctx.logger.warn(`t-team: skipped unreadable team state "${entry.name}" in workspace "${root.workspace}"`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return snapshots;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Collect every archived team under the given workspace state roots (the
|
|
159
|
+
* `archive/` subdirectory of each state root). Used by the historic panel
|
|
160
|
+
* path to restore full team detail after deletion.
|
|
161
|
+
* @param ctx - the plugin context.
|
|
162
|
+
* @param roots - `{ workspace, stateRoot }` pairs.
|
|
163
|
+
* @returns the archived snapshots in stable order.
|
|
164
|
+
*/
|
|
165
|
+
export async function collectArchivedTeamsActivity(ctx, roots) {
|
|
166
|
+
const snapshots = [];
|
|
167
|
+
for (const root of roots) {
|
|
168
|
+
for (const teamId of await listArchivedTeamIds(root.stateRoot)) {
|
|
169
|
+
try {
|
|
170
|
+
const state = await readArchivedTeam(root.stateRoot, teamId);
|
|
171
|
+
if (state === undefined)
|
|
172
|
+
continue;
|
|
173
|
+
snapshots.push(await assembleTeamSnapshot(ctx, join(root.stateRoot, 'archive'), root.workspace, state, { includeRemoved: true, historic: true }));
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
ctx.logger.warn(`t-team: skipped unreadable archived team "${teamId}" in workspace "${root.workspace}"`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return snapshots;
|
|
181
|
+
}
|