@yancyyu/openhermit 1.6.42 → 1.6.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/README.md +98 -89
  2. package/bin/hermit.mjs +96 -0
  3. package/dist-renderer/assets/{ProjectEditorOverlay-DlFQ6mai.js → ProjectEditorOverlay-C98qSs7-.js} +1 -1
  4. package/dist-renderer/assets/{TeamGraphOverlay-D2TPMPGR.js → TeamGraphOverlay-CsBbZwcL.js} +1 -1
  5. package/dist-renderer/assets/{_basePickBy-Cmd0RHLQ.js → _basePickBy-ZOyLWjMK.js} +1 -1
  6. package/dist-renderer/assets/{_baseUniq-BI_iy8ea.js → _baseUniq-DBb726rt.js} +1 -1
  7. package/dist-renderer/assets/{arc-NzW2mjTP.js → arc-CdiTaR_R.js} +1 -1
  8. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-Bzq85AYv.js → architectureDiagram-VXUJARFQ-Cz3sc5TH.js} +1 -1
  9. package/dist-renderer/assets/{blockDiagram-VD42YOAC-D1PvYS-b.js → blockDiagram-VD42YOAC-DE4c-KJ3.js} +1 -1
  10. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-D49RKzPC.js → c4Diagram-YG6GDRKO-CmTMDTrV.js} +1 -1
  11. package/dist-renderer/assets/channel-KTpqi9eT.js +1 -0
  12. package/dist-renderer/assets/{chunk-4BX2VUAB-fmI_MQmQ.js → chunk-4BX2VUAB-rhHy3tFl.js} +1 -1
  13. package/dist-renderer/assets/{chunk-55IACEB6-Xsv9RCXZ.js → chunk-55IACEB6-fLZBzuo_.js} +1 -1
  14. package/dist-renderer/assets/{chunk-B4BG7PRW-BE1KO8Um.js → chunk-B4BG7PRW-DOzxQhim.js} +1 -1
  15. package/dist-renderer/assets/{chunk-DI55MBZ5-tqJ7Mv7f.js → chunk-DI55MBZ5-COQCcXC5.js} +1 -1
  16. package/dist-renderer/assets/{chunk-FMBD7UC4-DMD45MVJ.js → chunk-FMBD7UC4-IKU9U_Y4.js} +1 -1
  17. package/dist-renderer/assets/{chunk-QN33PNHL-DOhGrz-q.js → chunk-QN33PNHL-D6WV154X.js} +1 -1
  18. package/dist-renderer/assets/{chunk-QZHKN3VN-D8yDgJdD.js → chunk-QZHKN3VN-D90_2DQp.js} +1 -1
  19. package/dist-renderer/assets/{chunk-TZMSLE5B-BcsEDu7A.js → chunk-TZMSLE5B-BQEil57G.js} +1 -1
  20. package/dist-renderer/assets/classDiagram-2ON5EDUG-lpzulY5X.js +1 -0
  21. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-lpzulY5X.js +1 -0
  22. package/dist-renderer/assets/clone-CriGymY9.js +1 -0
  23. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DlSqGHMX.js → cose-bilkent-S5V4N54A-6WiK6U2P.js} +1 -1
  24. package/dist-renderer/assets/{dagre-6UL2VRFP-BTT9tSAx.js → dagre-6UL2VRFP-DF4MMuTn.js} +1 -1
  25. package/dist-renderer/assets/{diagram-PSM6KHXK-Du-U-mK2.js → diagram-PSM6KHXK-CcF1eZ7E.js} +1 -1
  26. package/dist-renderer/assets/{diagram-QEK2KX5R-jFdHeKas.js → diagram-QEK2KX5R-DYlOVPQB.js} +1 -1
  27. package/dist-renderer/assets/{diagram-S2PKOQOG-DKLNK2bu.js → diagram-S2PKOQOG-BHXWsZOP.js} +1 -1
  28. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-CZxHgIIo.js → erDiagram-Q2GNP2WA-GjmuBx8d.js} +1 -1
  29. package/dist-renderer/assets/{flowDiagram-NV44I4VS-v4XStCD0.js → flowDiagram-NV44I4VS-BuS7YVHk.js} +1 -1
  30. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DJjD_BEL.js → ganttDiagram-JELNMOA3-3Teu5tAa.js} +1 -1
  31. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BNy-jr03.js → gitGraphDiagram-V2S2FVAM-BiLdCYu5.js} +1 -1
  32. package/dist-renderer/assets/{graph-DDTrn6je.js → graph-CDP_R8ct.js} +1 -1
  33. package/dist-renderer/assets/{index-BBp78BAu.js → index-BSZdT-g-.js} +1 -1
  34. package/dist-renderer/assets/{index-eotrJaYy.js → index-BhWvMqsz.js} +1 -1
  35. package/dist-renderer/assets/{index-D8_B-cfs.js → index-C2_AupSj.js} +1 -1
  36. package/dist-renderer/assets/{index-BQrwHZ-k.js → index-C5ujiwAR.js} +580 -588
  37. package/dist-renderer/assets/index-CIS2CTK9.css +1 -0
  38. package/dist-renderer/assets/{index-CRKQSG9S.js → index-CVNjLwkq.js} +1 -1
  39. package/dist-renderer/assets/{index-DR6Wz52b.js → index-CwG3se0q.js} +1 -1
  40. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DqnOsuza.js → infoDiagram-HS3SLOUP-DLHUFo72.js} +1 -1
  41. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DTobaO1d.js → journeyDiagram-XKPGCS4Q-BE07RpJD.js} +1 -1
  42. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-HbwVOvWc.js → kanban-definition-3W4ZIXB7-DDHZy4NB.js} +1 -1
  43. package/dist-renderer/assets/{layout--VYmTcw2.js → layout-5nA5wUxO.js} +1 -1
  44. package/dist-renderer/assets/{linear-BsJh89Mr.js → linear-BtF1i2qN.js} +1 -1
  45. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-BZqUZePd.js → mindmap-definition-VGOIOE7T-Z1Ui9Sqy.js} +1 -1
  46. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-B1q_nH6P.js → pieDiagram-ADFJNKIX-LCjxckWv.js} +1 -1
  47. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-UD8QhSEu.js → quadrantDiagram-AYHSOK5B-BOwKjSco.js} +1 -1
  48. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-BA_i7Nw8.js → requirementDiagram-UZGBJVZJ-pChP8Znd.js} +1 -1
  49. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-CMTnX-2d.js → sankeyDiagram-TZEHDZUN-DifZ2qpo.js} +1 -1
  50. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-BQXDB615.js → sequenceDiagram-WL72ISMW-CJg-WYyY.js} +1 -1
  51. package/dist-renderer/assets/{splashScene-D0YB9uxm.js → splashScene-94xWCzLA.js} +1 -1
  52. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-BAsPXy6X.js → stateDiagram-FKZM4ZOC-DWHOoFdv.js} +1 -1
  53. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-CGYZOoMb.js +1 -0
  54. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BdasmVkC.js → timeline-definition-IT6M3QCI-CPgokIo8.js} +1 -1
  55. package/dist-renderer/assets/{treemap-GDKQZRPO-BkKQqIui.js → treemap-GDKQZRPO-DAVqSR9L.js} +1 -1
  56. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-EAlPHOdx.js → xychartDiagram-PRI3JC2R-CCOcGbrD.js} +1 -1
  57. package/dist-renderer/chat-community-qr.jpg +0 -0
  58. package/dist-renderer/fonts/Agave-Bold.ttf +0 -0
  59. package/dist-renderer/fonts/Agave-Regular.ttf +0 -0
  60. package/dist-renderer/icon.png +0 -0
  61. package/dist-renderer/icon.rar +0 -0
  62. package/dist-renderer/index.html +3 -3
  63. package/package.json +21 -26
  64. package/src/features/worker-society/core/application/WorkerSocietyService.test.ts +802 -0
  65. package/src/features/worker-society/core/application/WorkerSocietyService.ts +428 -0
  66. package/src/features/worker-society/core/application/fakes.ts +101 -0
  67. package/src/features/worker-society/core/application/ports.ts +70 -0
  68. package/src/features/worker-society/core/domain/models/society.ts +141 -0
  69. package/src/features/worker-society/core/domain/policies/societyPolicies.test.ts +739 -0
  70. package/src/features/worker-society/core/domain/policies/societyPolicies.ts +496 -0
  71. package/src/features/worker-society/main/adapters/input/societyMcp.test.ts +317 -0
  72. package/src/features/worker-society/main/adapters/input/societyMcp.ts +257 -0
  73. package/src/features/worker-society/main/adapters/input/societyRoutes.test.ts +695 -0
  74. package/src/features/worker-society/main/adapters/input/societyRoutes.ts +194 -0
  75. package/src/features/worker-society/main/composition/societyComposition.test.ts +74 -0
  76. package/src/features/worker-society/main/composition/societyComposition.ts +70 -0
  77. package/src/features/worker-society/main/composition/workerSocietyPlugin.test.ts +69 -0
  78. package/src/features/worker-society/main/composition/workerSocietyPlugin.ts +67 -0
  79. package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.test.ts +132 -0
  80. package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.ts +84 -0
  81. package/src/features/worker-society/main/infrastructure/fsStores.test.ts +216 -0
  82. package/src/features/worker-society/main/infrastructure/fsStores.ts +113 -0
  83. package/src/features/worker-society/main/infrastructure/mergingProfileStore.test.ts +195 -0
  84. package/src/features/worker-society/main/infrastructure/mergingProfileStore.ts +96 -0
  85. package/src/features/worker-society/renderer/SocietyGraph.tsx +166 -0
  86. package/src/features/worker-society/renderer/SocietyNodeLabels.tsx +139 -0
  87. package/src/features/worker-society/renderer/SocietyNodeOverlay.tsx +339 -0
  88. package/src/features/worker-society/renderer/SocietyView.tsx +437 -0
  89. package/src/features/worker-society/renderer/index.ts +11 -0
  90. package/src/features/worker-society/renderer/societyApi.test.ts +259 -0
  91. package/src/features/worker-society/renderer/societyApi.ts +144 -0
  92. package/src/features/worker-society/renderer/societyGraphAdapter.test.ts +321 -0
  93. package/src/features/worker-society/renderer/societyGraphAdapter.ts +240 -0
  94. package/src/features/worker-society/renderer/societyOverlayActions.test.ts +57 -0
  95. package/src/features/worker-society/renderer/societyOverlayActions.ts +49 -0
  96. package/src/features/worker-society/renderer/societyStore.test.ts +218 -0
  97. package/src/features/worker-society/renderer/societyStore.ts +146 -0
  98. package/src/features/worker-society/renderer/societyViewUtils.test.ts +81 -0
  99. package/src/features/worker-society/renderer/societyViewUtils.ts +68 -0
  100. package/src/main/ipc/extensions.ts +27 -0
  101. package/src/main/server.ts +1709 -534
  102. package/src/main/services/ccConnect/CcConnectBridge.ts +26 -11
  103. package/src/main/services/ccConnect/CcConnectClient.ts +9 -2
  104. package/src/main/services/ccConnect/workDirReconcile.test.ts +57 -0
  105. package/src/main/services/ccConnect/workDirReconcile.ts +36 -0
  106. package/src/main/services/direct-cli/DirectCliSessionManager.test.ts +397 -0
  107. package/src/main/services/direct-cli/DirectCliSessionManager.ts +508 -0
  108. package/src/main/services/direct-cli/DirectCliSessionStore.test.ts +79 -0
  109. package/src/main/services/direct-cli/DirectCliSessionStore.ts +97 -0
  110. package/src/main/services/direct-cli/__tests__/directCliMessageId.test.ts +40 -0
  111. package/src/main/services/direct-cli/directCliMessageId.ts +21 -0
  112. package/src/main/services/direct-cli/index.ts +17 -0
  113. package/src/main/services/extensions/capability-packs/CapabilityPackLoaderService.ts +637 -0
  114. package/src/main/services/extensions/catalog/PluginCatalogService.ts +2 -2
  115. package/src/main/services/loop-assets/LoopAssetsScannerService.ts +657 -0
  116. package/src/main/services/runtime/providerAwareCliEnv.ts +33 -5
  117. package/src/main/services/session-intelligence/LocalSessionScanner.ts +156 -71
  118. package/src/main/services/session-intelligence/SessionUsageParser.ts +103 -8
  119. package/src/main/services/session-intelligence/UsageTelemetryService.ts +11 -0
  120. package/src/main/services/session-intelligence/__tests__/teamSessionListMapper.test.ts +104 -0
  121. package/src/main/services/session-intelligence/teamSessionListMapper.ts +78 -0
  122. package/src/main/services/system-manager/AdminLoopInitializer.ts +95 -0
  123. package/src/main/services/system-manager/BuiltinWorkflowSeeder.ts +679 -74
  124. package/src/main/services/system-manager/SystemManagerConfigService.ts +19 -1
  125. package/src/main/services/system-manager/WorkflowPromptService.ts +58 -5
  126. package/src/main/services/system-manager/__tests__/AdminLoopInitializer.test.ts +129 -0
  127. package/src/main/services/system-manager/__tests__/SystemManagerConfigService.test.ts +60 -0
  128. package/src/main/services/teams-mvp/CollaborationBoardService.ts +2 -0
  129. package/src/main/services/teams-mvp/OpsRunbookContext.ts +60 -0
  130. package/src/main/services/teams-mvp/TaskDispatchService.test.ts +305 -0
  131. package/src/main/services/teams-mvp/TaskDispatchService.ts +250 -131
  132. package/src/main/services/teams-mvp/TeamProvisioningService.ts +12 -2
  133. package/src/main/services/teams-mvp/TeamWorkspaceService.test.ts +207 -0
  134. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +104 -51
  135. package/src/main/services/teams-mvp/index.ts +6 -0
  136. package/src/main/utils/externalPlatformSessionRouting.ts +92 -0
  137. package/src/main/utils/toolApprovalRules.ts +151 -0
  138. package/src/renderer/App.tsx +24 -89
  139. package/src/renderer/api/httpClient.ts +115 -37
  140. package/src/renderer/api/providers.ts +5 -16
  141. package/src/renderer/components/chat/CommunityChatView.tsx +81 -0
  142. package/src/renderer/components/dashboard/DashboardView.tsx +130 -84
  143. package/src/renderer/components/extensions/ExtensionStoreView.tsx +39 -5
  144. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +2 -1
  145. package/src/renderer/components/extensions/capability-packs/CapabilityPacksPanel.tsx +170 -0
  146. package/src/renderer/components/layout/PaneContent.tsx +10 -2
  147. package/src/renderer/components/layout/SortableTab.tsx +4 -0
  148. package/src/renderer/components/layout/TabBarActions.tsx +13 -16
  149. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +4 -135
  150. package/src/renderer/components/schedules/SchedulesView.tsx +22 -14
  151. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +7 -6
  152. package/src/renderer/components/settings/SettingsTabs.tsx +24 -21
  153. package/src/renderer/components/settings/SettingsView.tsx +22 -13
  154. package/src/renderer/components/settings/components/SettingRow.tsx +13 -5
  155. package/src/renderer/components/settings/components/SettingsSectionCard.tsx +53 -0
  156. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +10 -6
  157. package/src/renderer/components/settings/components/SettingsSelect.tsx +12 -9
  158. package/src/renderer/components/settings/components/SettingsToggle.tsx +6 -5
  159. package/src/renderer/components/settings/components/index.ts +1 -0
  160. package/src/renderer/components/settings/sections/AdvancedSection.tsx +78 -59
  161. package/src/renderer/components/settings/sections/CliStatusSection.tsx +32 -44
  162. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  163. package/src/renderer/components/settings/sections/GeneralSection.tsx +216 -186
  164. package/src/renderer/components/settings/sections/PlatformsSection.tsx +25 -17
  165. package/src/renderer/components/settings/sections/TaskBusSection.tsx +63 -22
  166. package/src/renderer/components/sidebar/SidebarSessions.tsx +120 -80
  167. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +1 -1
  168. package/src/renderer/components/splash/splashScene.ts +6 -2
  169. package/src/renderer/components/system-manager/SystemManagerView.tsx +169 -255
  170. package/src/renderer/components/tasks/TasksView.tsx +63 -37
  171. package/src/renderer/components/team/CcSessionsSection.tsx +124 -89
  172. package/src/renderer/components/team/HarnessBrandLogos.tsx +318 -0
  173. package/src/renderer/components/team/HarnessSelect.tsx +25 -26
  174. package/src/renderer/components/team/TeamDetailView.tsx +137 -153
  175. package/src/renderer/components/team/TeamEmptyState.tsx +9 -37
  176. package/src/renderer/components/team/TeamListView.tsx +143 -30
  177. package/src/renderer/components/team/__tests__/CcSessionsSection.hasLocalFile.test.tsx +128 -0
  178. package/src/renderer/components/team/activity/ActivityItem.tsx +21 -9
  179. package/src/renderer/components/team/activity/ActivityTimeline.tsx +2 -2
  180. package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +1 -1
  181. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +13 -10
  182. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +156 -83
  183. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +9 -157
  184. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +19 -15
  185. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +48 -10
  186. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +11 -12
  187. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +39 -37
  188. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +434 -64
  189. package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +12 -10
  190. package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +2 -2
  191. package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.bindProject.test.tsx +399 -0
  192. package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.chineseRepro.test.tsx +253 -0
  193. package/src/renderer/components/team/dialogs/platformAllowUtils.ts +91 -0
  194. package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +1 -1
  195. package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +41 -0
  196. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +41 -86
  197. package/src/renderer/components/team/loop-console/LoopCommandComposer.tsx +310 -0
  198. package/src/renderer/components/team/loop-console/LoopConsolePanel.tsx +372 -0
  199. package/src/renderer/components/team/loop-console/loopSendIntent.test.ts +85 -0
  200. package/src/renderer/components/team/loop-console/loopSendIntent.ts +221 -0
  201. package/src/renderer/components/team/loop-console/useLeadSessionToolActivity.ts +74 -0
  202. package/src/renderer/components/team/loop-console/useLoopCommandSuggestions.ts +165 -0
  203. package/src/renderer/components/team/loop-console/useLoopConsoleController.ts +266 -0
  204. package/src/renderer/components/team/members/LeadModelRow.test.tsx +1 -1
  205. package/src/renderer/components/team/members/LeadModelRow.tsx +5 -3
  206. package/src/renderer/components/team/members/MemberDetailDialog.tsx +11 -0
  207. package/src/renderer/components/team/members/MemberDetailStats.tsx +13 -3
  208. package/src/renderer/components/team/members/MemberDraftRow.test.tsx +1 -1
  209. package/src/renderer/components/team/members/MemberDraftRow.tsx +1 -1
  210. package/src/renderer/components/team/members/MemberMessagesTab.tsx +2 -2
  211. package/src/renderer/components/team/members/MemberStatsTab.tsx +1 -1
  212. package/src/renderer/components/team/messages/MessageComposer.tsx +150 -44
  213. package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +2 -2
  214. package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -28
  215. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +6 -6
  216. package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +2 -2
  217. package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +1 -1
  218. package/src/renderer/components/terminal/TerminalPanel.tsx +2 -3
  219. package/src/renderer/components/ui/MentionableTextarea.tsx +5 -1
  220. package/src/renderer/constants/teamColors.ts +5 -5
  221. package/src/renderer/hooks/useExtensionsTabState.ts +1 -1
  222. package/src/renderer/hooks/useMentionDetection.ts +5 -1
  223. package/src/renderer/hooks/useProjectWorkflowCommands.ts +57 -0
  224. package/src/renderer/hooks/useTeamSuggestions.ts +2 -0
  225. package/src/renderer/index.css +19 -2
  226. package/src/renderer/main.tsx +7 -1
  227. package/src/renderer/store/index.ts +18 -1
  228. package/src/renderer/store/slices/extensionsSlice.ts +83 -0
  229. package/src/renderer/store/slices/tabSlice.ts +61 -0
  230. package/src/renderer/store/slices/teamSlice.ts +138 -9
  231. package/src/renderer/types/mention.ts +8 -0
  232. package/src/renderer/types/tabs.ts +3 -1
  233. package/src/renderer/utils/__tests__/bindProjectSlug.test.ts +69 -0
  234. package/src/renderer/utils/__tests__/groupTransformer.test.ts +148 -0
  235. package/src/renderer/utils/__tests__/initialRoute.test.ts +101 -0
  236. package/src/renderer/utils/__tests__/leadToolActivity.test.ts +124 -0
  237. package/src/renderer/utils/__tests__/mergeTeamMessages.test.ts +81 -0
  238. package/src/renderer/utils/__tests__/teamMessageFiltering.test.ts +213 -0
  239. package/src/renderer/utils/__tests__/teamMessageKey.test.ts +75 -0
  240. package/src/renderer/utils/__tests__/workflowCommandExecution.test.ts +173 -0
  241. package/src/renderer/utils/__tests__/workflowCommandSuggestions.test.ts +59 -0
  242. package/src/renderer/utils/bindProjectSlug.ts +57 -0
  243. package/src/renderer/utils/capabilityCommandExecution.ts +113 -0
  244. package/src/renderer/utils/initialRoute.ts +89 -0
  245. package/src/renderer/utils/leadToolActivity.ts +117 -0
  246. package/src/renderer/utils/loopShortcutSuggestions.ts +106 -0
  247. package/src/renderer/utils/mentionSuggestions.ts +1 -1
  248. package/src/renderer/utils/slashCommandRegistry.ts +231 -0
  249. package/src/renderer/utils/teamMentionDirective.ts +31 -0
  250. package/src/renderer/utils/workflowCommandExecution.ts +96 -0
  251. package/src/renderer/utils/workflowCommandSuggestions.ts +49 -0
  252. package/src/shared/types/api.ts +79 -4
  253. package/src/shared/types/ccConnect.ts +1 -0
  254. package/src/shared/types/extensions/api.ts +19 -0
  255. package/src/shared/types/extensions/capabilityPack.ts +118 -0
  256. package/src/shared/types/extensions/index.ts +29 -1
  257. package/src/shared/types/index.ts +6 -0
  258. package/src/shared/types/loopAssets.ts +54 -0
  259. package/src/shared/types/providers.ts +0 -16
  260. package/src/shared/types/systemManager.ts +26 -1
  261. package/src/shared/types/team.ts +41 -5
  262. package/src/shared/types/terminal.ts +2 -36
  263. package/src/shared/types/worker.test.ts +28 -0
  264. package/src/shared/types/worker.ts +3 -0
  265. package/src/shared/utils/__tests__/effortLevels.test.ts +88 -0
  266. package/src/shared/utils/__tests__/providerBackend.test.ts +88 -0
  267. package/src/shared/utils/__tests__/providerLaunchArgs.test.ts +220 -0
  268. package/src/shared/utils/claudeStreamJson.test.ts +187 -0
  269. package/src/shared/utils/claudeStreamJson.ts +153 -0
  270. package/src/shared/utils/providerLaunchArgs.ts +217 -0
  271. package/src/shared/utils/slashCommands.ts +10 -0
  272. package/src/types/node-pty.d.ts +8 -0
  273. package/dist-renderer/assets/channel-Ch7JrfUu.js +0 -1
  274. package/dist-renderer/assets/classDiagram-2ON5EDUG-z9I4AnFy.js +0 -1
  275. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-z9I4AnFy.js +0 -1
  276. package/dist-renderer/assets/clone-Dfi1Jx6l.js +0 -1
  277. package/dist-renderer/assets/index-iyjkpSus.css +0 -32
  278. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DTUIBfce.js +0 -1
  279. package/src/main/services/system-manager/SystemManagerPtyService.ts +0 -233
  280. package/src/renderer/components/common/TerminalPane.tsx +0 -213
  281. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +0 -292
@@ -8,7 +8,7 @@ import type {
8
8
  TaskHandshakeResponse,
9
9
  TaskStatusUpdate,
10
10
  } from '@shared/types/team';
11
- import type { TeamWorkspaceService, TeamManifest } from './TeamWorkspaceService';
11
+ import type { TeamWorkspaceService, TeamManifest, Task } from './TeamWorkspaceService';
12
12
  import type { CollaborationBoardService } from './CollaborationBoardService';
13
13
  import type Redis from 'ioredis';
14
14
 
@@ -51,8 +51,11 @@ export class TaskDispatchService {
51
51
  private responseConsumerTeamSlugs = new Set<string>();
52
52
  private disposed = false;
53
53
  private pendingRequests: Map<string, PendingRequest> = new Map();
54
+ private startingTasks = new Set<string>();
54
55
  /** Callback fired when collab task state changes (for SSE broadcast). */
55
56
  onCollabChange?: (dispatchId: string, status: string, fromTeam: string, toTeam: string) => void;
57
+ /** Runtime delivery hook. Cross-team tasks must only use this after a human clicks Start. */
58
+ onRuntimeStart?: (params: { teamName: string; text: string }) => Promise<void>;
56
59
 
57
60
  constructor(workspace: TeamWorkspaceService, collabBoard: CollaborationBoardService) {
58
61
  this.workspace = workspace;
@@ -106,6 +109,7 @@ export class TaskDispatchService {
106
109
  description: team.description,
107
110
  harness: team.harness,
108
111
  capabilities: this.inferCapabilities(team),
112
+ workDir: team.workDir,
109
113
  });
110
114
  }
111
115
 
@@ -141,6 +145,7 @@ export class TaskDispatchService {
141
145
  description: info?.description || undefined,
142
146
  harness: info?.harness || undefined,
143
147
  capabilities,
148
+ workDir: info?.workDir || undefined,
144
149
  });
145
150
  }
146
151
  } catch {
@@ -155,7 +160,7 @@ export class TaskDispatchService {
155
160
  fromTeam: string,
156
161
  task: { subject: string; description?: string; prompt?: string },
157
162
  targetTeam: string,
158
- opts?: { deadlineMinutes?: number; needsHumanReview?: boolean }
163
+ opts?: { deadlineMinutes?: number; needsHumanReview?: boolean; dispatchId?: string }
159
164
  ): Promise<DispatchResult> {
160
165
  if (fromTeam === targetTeam) {
161
166
  return {
@@ -166,8 +171,9 @@ export class TaskDispatchService {
166
171
  };
167
172
  }
168
173
 
169
- const dispatchId = crypto.randomUUID();
174
+ const dispatchId = opts?.dispatchId ?? crypto.randomUUID();
170
175
  const now = new Date();
176
+ const dispatchedAt = now.toISOString();
171
177
  const deadline = opts?.deadlineMinutes
172
178
  ? new Date(now.getTime() + opts.deadlineMinutes * 60_000).toISOString()
173
179
  : undefined;
@@ -176,13 +182,23 @@ export class TaskDispatchService {
176
182
  dispatchId,
177
183
  originTeam: fromTeam,
178
184
  targetTeam,
179
- status: 'pending_accept',
180
- dispatchedAt: now.toISOString(),
185
+ status: 'received',
186
+ dispatchedAt,
187
+ receivedAt: dispatchedAt,
181
188
  deadline,
182
189
  };
190
+ const payload: TaskDispatchPayload = {
191
+ dispatchId,
192
+ originTeam: fromTeam,
193
+ targetTeam,
194
+ task: { subject: task.subject, description: task.description, prompt: task.prompt },
195
+ dispatchedAt,
196
+ deadline,
197
+ needsHumanReview: opts?.needsHumanReview,
198
+ };
183
199
 
184
- // Add to collaboration board before external delivery. Even failed dispatches
185
- // must remain visible in the canonical task projection for diagnosis/retry.
200
+ // Add to collaboration board before external delivery. Dispatch creation only
201
+ // means "visible in the target team's TODO"; runtime execution waits for Start.
186
202
  const fromTeamManifest = await this.safeReadManifest(fromTeam);
187
203
  const toTeamManifest = await this.safeReadManifest(targetTeam);
188
204
  const collabTask: CollabTask = {
@@ -194,32 +210,54 @@ export class TaskDispatchService {
194
210
  fromTeamDisplay: fromTeamManifest?.displayName ?? fromTeam,
195
211
  toTeam: targetTeam,
196
212
  toTeamDisplay: toTeamManifest?.displayName ?? targetTeam,
197
- status: 'pending_accept',
213
+ status: 'received',
198
214
  deadline,
199
215
  needsHumanReview: opts?.needsHumanReview ?? false,
200
216
  revisionCount: 0,
201
- createdAt: now.toISOString(),
202
- updatedAt: now.toISOString(),
217
+ createdAt: dispatchedAt,
218
+ updatedAt: dispatchedAt,
203
219
  };
204
220
  this.collabBoard.addTask(collabTask);
205
221
 
206
- // Route: all dispatches require Redis
222
+ const isLocalTarget = await this.isLocalTeam(targetTeam);
223
+ if (isLocalTarget) {
224
+ const localTask = await this.createOrReuseReceivedTask(targetTeam, payload, dispatchMeta);
225
+ this.pendingRequests.set(dispatchId, {
226
+ payload,
227
+ msgId: `local-${Date.now()}`,
228
+ groupName: 'local-dispatch',
229
+ teamSlug: targetTeam,
230
+ localTaskId: localTask.id,
231
+ });
232
+ this.emitCollabChange(dispatchId, 'received', fromTeam, targetTeam);
233
+ this.sendFeishuNotification(
234
+ `跨团队任务进入待启动:${fromTeam} → ${targetTeam}\n${task.subject}`
235
+ );
236
+ return {
237
+ dispatchId,
238
+ status: 'received',
239
+ targetTeam,
240
+ message: `Task queued in ${targetTeam} TODO, waiting for manual start.`,
241
+ };
242
+ }
243
+
244
+ // Remote teams still require Redis to create the target-side TODO projection.
207
245
  if (!this.redis) {
208
246
  const failedTask = this.collabBoard.transition({
209
247
  dispatchId,
210
- expected: 'pending_accept',
248
+ expected: ['received', 'pending_accept'],
211
249
  next: 'failed',
212
250
  actor: { type: 'system', id: 'task-dispatch' },
213
251
  eventType: 'task_failed',
214
252
  payload: { reason: 'Redis not configured' },
215
- extra: { reason: 'Redis not configured — cross-team dispatch requires task bus.' },
253
+ extra: { reason: 'Redis not configured — remote cross-team dispatch requires task bus.' },
216
254
  });
217
255
  this.emitCollabChange(dispatchId, failedTask.status, fromTeam, targetTeam);
218
256
  return {
219
257
  dispatchId,
220
258
  status: 'failed',
221
259
  targetTeam,
222
- message: 'Redis not configured — cross-team dispatch requires task bus.',
260
+ message: 'Redis not configured — remote cross-team dispatch requires task bus.',
223
261
  };
224
262
  }
225
263
 
@@ -230,7 +268,7 @@ export class TaskDispatchService {
230
268
  const reason = err instanceof Error ? err.message : 'Unknown Redis dispatch failure';
231
269
  const failedTask = this.collabBoard.transition({
232
270
  dispatchId,
233
- expected: 'pending_accept',
271
+ expected: ['received', 'pending_accept'],
234
272
  next: 'failed',
235
273
  actor: { type: 'system', id: 'task-dispatch' },
236
274
  eventType: 'task_failed',
@@ -246,103 +284,191 @@ export class TaskDispatchService {
246
284
  };
247
285
  }
248
286
 
249
- this.emitCollabChange(dispatchId, 'pending_accept', fromTeam, targetTeam);
287
+ this.emitCollabChange(dispatchId, 'received', fromTeam, targetTeam);
250
288
 
251
289
  return {
252
290
  dispatchId,
253
- status: 'pending_accept',
291
+ status: 'received',
254
292
  targetTeam,
255
- message: `Task dispatched to ${targetTeam}, awaiting acceptance.`,
293
+ message: `Task queued in ${targetTeam} TODO, waiting for manual start.`,
256
294
  };
257
295
  }
258
296
 
259
- async acceptTask(teamSlug: string, dispatchId: string): Promise<{ taskId: string }> {
260
- const pending = this.pendingRequests.get(dispatchId);
261
- if (!pending) {
262
- throw new Error(`No pending request found for dispatchId: ${dispatchId}`);
263
- }
264
-
265
- const { payload, msgId, groupName, localTaskId } = pending;
266
-
267
- const remoteTaskId = localTaskId ?? payload.dispatchId;
297
+ private async createOrReuseReceivedTask(
298
+ teamSlug: string,
299
+ payload: TaskDispatchPayload,
300
+ dispatchMeta: DispatchMeta
301
+ ): Promise<Task> {
302
+ const existingTasks = await this.workspace.readTasks(teamSlug).catch(() => []);
303
+ const existingTask = existingTasks.find(
304
+ (task) => task.dispatchMeta?.dispatchId === payload.dispatchId
305
+ );
306
+ if (existingTask) return existingTask;
268
307
 
269
- // Send accept response
270
- const response: TaskHandshakeResponse = {
271
- dispatchId: payload.dispatchId,
272
- type: 'task_accept',
273
- fromTeam: teamSlug,
274
- toTeam: payload.originTeam,
275
- remoteTaskId,
276
- acceptedAt: new Date().toISOString(),
277
- };
308
+ return this.workspace.createTask(teamSlug, {
309
+ title: payload.task.subject,
310
+ description: payload.task.description ?? payload.task.prompt ?? '',
311
+ status: 'todo',
312
+ dispatchMeta,
313
+ });
314
+ }
278
315
 
279
- const isLocalOrigin = await this.isLocalTeam(payload.originTeam);
280
- if (isLocalOrigin) {
281
- await this.handleLocalResponse(response);
282
- } else if (this.redis) {
283
- await this.redis
284
- .xadd(`task:response:${payload.originTeam}`, '*', 'payload', JSON.stringify(response))
285
- .catch((err: Error) => {
286
- console.error('[TaskDispatchService] accept xadd failed:', err.message);
287
- });
316
+ async startDispatchedTask(
317
+ teamSlug: string,
318
+ taskId: string
319
+ ): Promise<{ taskId: string; dispatchId: string }> {
320
+ const lockKey = `${teamSlug}:${taskId}`;
321
+ if (this.startingTasks.has(lockKey)) {
322
+ throw new Error('cross-team task is already starting');
323
+ }
324
+ this.startingTasks.add(lockKey);
325
+ try {
326
+ return await this.startDispatchedTaskLocked(teamSlug, taskId);
327
+ } finally {
328
+ this.startingTasks.delete(lockKey);
288
329
  }
330
+ }
289
331
 
290
- if (this.redis) {
291
- await this.redis.xack(`task:dispatch:${teamSlug}`, groupName, msgId).catch(() => {});
332
+ private async startDispatchedTaskLocked(
333
+ teamSlug: string,
334
+ taskId: string
335
+ ): Promise<{ taskId: string; dispatchId: string }> {
336
+ const tasks = await this.workspace.readTasks(teamSlug);
337
+ const task = tasks.find((item) => item.id === taskId);
338
+ if (!task) throw new Error(`task not found: ${taskId}`);
339
+ if (!task.dispatchMeta) throw new Error(`task is not a cross-team dispatch: ${taskId}`);
340
+ if (task.dispatchMeta.targetTeam !== teamSlug) {
341
+ throw new Error(
342
+ `cross-team task belongs to ${task.dispatchMeta.targetTeam}, not ${teamSlug}`
343
+ );
344
+ }
345
+ if (task.status !== 'todo') {
346
+ throw new Error('cross-team task has already been started or completed');
347
+ }
348
+ if (!['received', 'pending_accept', 'accepted'].includes(task.dispatchMeta.status)) {
349
+ throw new Error(`cross-team task cannot be started from status ${task.dispatchMeta.status}`);
292
350
  }
293
351
 
294
- this.pendingRequests.delete(dispatchId);
352
+ const startedAt = new Date().toISOString();
353
+ const meta: DispatchMeta = {
354
+ ...task.dispatchMeta,
355
+ status: 'in_progress',
356
+ acceptedAt: task.dispatchMeta.acceptedAt ?? startedAt,
357
+ remoteTaskId: task.id,
358
+ };
295
359
 
296
- // Update collab board
297
- const acceptedAt = new Date().toISOString();
298
- this.collabBoard.transition({
299
- dispatchId: payload.dispatchId,
300
- expected: 'pending_accept',
301
- next: 'accepted',
302
- actor: { type: 'team', id: teamSlug },
303
- eventType: 'task_accepted',
304
- payload: { remoteTaskId },
305
- extra: { acceptedAt },
306
- });
307
- this.emitCollabChange(payload.dispatchId, 'accepted', payload.originTeam, payload.targetTeam);
360
+ await this.workspace.patchTask(teamSlug, taskId, {
361
+ status: 'doing',
362
+ dispatchMeta: meta,
363
+ } as any);
308
364
 
309
- // Fixed flow: notify receiving agent to start executing
365
+ const description = task.description?.trim();
366
+ const runtimeText = `[跨团队任务启动] 来自 ${meta.originTeam} 的任务已由用户点击启动,请开始执行。\n\n任务:${task.title}${description ? `\n\n描述:${description}` : ''}\n\n完成后请调用 complete_task 标记完成。`;
310
367
  try {
311
- await this.workspace.appendMessage(teamSlug, {
312
- from: 'system',
313
- to: 'team',
314
- role: 'agent',
315
- content: `[跨团队任务已确认] "${payload.task.subject}" — 来自 ${payload.originTeam} 的任务已被人工确认接单,请开始执行。任务描述:${payload.task.description ?? ''}`,
316
- meta: {
317
- source: 'cross_team_accepted',
318
- dispatchId: payload.dispatchId,
319
- originTeam: payload.originTeam,
320
- taskId: remoteTaskId,
321
- },
368
+ await this.onRuntimeStart?.({ teamName: teamSlug, text: runtimeText });
369
+ } catch (err) {
370
+ await this.workspace
371
+ .patchTask(teamSlug, taskId, {
372
+ status: 'todo',
373
+ dispatchMeta: task.dispatchMeta,
374
+ } as any)
375
+ .catch(() => {});
376
+ throw err;
377
+ }
378
+
379
+ const collabTask = this.collabBoard.getTask(meta.dispatchId);
380
+ if (collabTask && ['received', 'pending_accept', 'accepted'].includes(collabTask.status)) {
381
+ const next = this.collabBoard.transition({
382
+ dispatchId: meta.dispatchId,
383
+ expected: ['received', 'pending_accept', 'accepted'],
384
+ next: 'in_progress',
385
+ actor: { type: 'team', id: teamSlug },
386
+ eventType: 'task_accepted',
387
+ payload: { remoteTaskId: task.id, startedAt },
388
+ extra: { acceptedAt: startedAt, remoteTaskId: task.id },
322
389
  });
323
- } catch {}
390
+ this.emitCollabChange(meta.dispatchId, next.status, next.fromTeam, next.toTeam);
391
+ }
324
392
 
325
- // Fixed flow: notify originating agent that task was accepted
326
- try {
327
- await this.workspace.appendMessage(payload.originTeam, {
393
+ const pending = this.pendingRequests.get(meta.dispatchId);
394
+ if (pending?.teamSlug === teamSlug) {
395
+ if (this.redis && !pending.msgId.startsWith('local-')) {
396
+ await this.redis
397
+ .xack(`task:dispatch:${teamSlug}`, pending.groupName, pending.msgId)
398
+ .catch(() => {});
399
+ }
400
+ this.pendingRequests.delete(meta.dispatchId);
401
+ }
402
+
403
+ const update: TaskStatusUpdate = {
404
+ dispatchId: meta.dispatchId,
405
+ originTeam: meta.originTeam,
406
+ status: 'in_progress',
407
+ remoteTaskId: task.id,
408
+ timestamp: startedAt,
409
+ };
410
+ if (this.redis) {
411
+ await this.redis
412
+ .publish(`task:status:${meta.originTeam}`, JSON.stringify(update))
413
+ .catch(() => {});
414
+ }
415
+
416
+ await this.workspace
417
+ .appendMessage(meta.originTeam, {
328
418
  from: 'system',
329
419
  to: 'team',
330
420
  role: 'agent',
331
- content: `[跨团队任务已接单] "${payload.task.subject}" — ${teamSlug} 已确认接单,正在执行中。`,
421
+ content: `[跨团队任务已启动] "${task.title}" — ${teamSlug} 已从 TODO 点击启动并开始执行。`,
332
422
  meta: {
333
- source: 'cross_team_accepted_notify',
334
- dispatchId: payload.dispatchId,
423
+ source: 'cross_team_started',
424
+ dispatchId: meta.dispatchId,
335
425
  targetTeam: teamSlug,
426
+ taskId,
336
427
  },
337
- });
338
- } catch {}
428
+ })
429
+ .catch(() => {});
430
+
431
+ return { taskId, dispatchId: meta.dispatchId };
432
+ }
433
+
434
+ async acceptTask(teamSlug: string, dispatchId: string): Promise<{ taskId: string }> {
435
+ const pending = this.pendingRequests.get(dispatchId);
436
+ if (!pending) {
437
+ throw new Error(`No pending request found for dispatchId: ${dispatchId}`);
438
+ }
439
+
440
+ const { payload, localTaskId } = pending;
441
+
442
+ const remoteTaskId = localTaskId ?? payload.dispatchId;
443
+
444
+ // Legacy accept_task only acknowledges receipt. It must not advance execution;
445
+ // runtime delivery is gated by the target team's TODO Start button.
446
+ const tasks = await this.workspace.readTasks(teamSlug).catch(() => []);
447
+ const localTask = tasks.find(
448
+ (task) => task.id === remoteTaskId || task.dispatchMeta?.dispatchId === payload.dispatchId
449
+ );
450
+ if (localTask?.dispatchMeta) {
451
+ await this.workspace
452
+ .patchTask(teamSlug, localTask.id, {
453
+ dispatchMeta: {
454
+ ...localTask.dispatchMeta,
455
+ status: 'received',
456
+ remoteTaskId: localTask.id,
457
+ },
458
+ } as any)
459
+ .catch(() => {});
460
+ }
461
+ this.emitCollabChange(payload.dispatchId, 'received', payload.originTeam, payload.targetTeam);
462
+
463
+ // Do not append anything to the target inbox on receipt/accept. The target
464
+ // team's TODO card is the only pre-start surface; inbox/runtime delivery is
465
+ // gated by explicit Start.
339
466
 
340
- // Feishu notification
341
467
  this.sendFeishuNotification(
342
- `跨团队任务已接单:${payload.originTeam} → ${teamSlug}\n${payload.task.subject}\n状态:执行中`
468
+ `跨团队任务待启动:${payload.originTeam} → ${teamSlug}\n${payload.task.subject}\n状态:等待目标团队点击启动`
343
469
  );
344
470
 
345
- return { taskId: remoteTaskId };
471
+ return { taskId: localTask?.id ?? remoteTaskId };
346
472
  }
347
473
 
348
474
  async rejectTask(teamSlug: string, dispatchId: string, reason?: string): Promise<void> {
@@ -382,7 +508,7 @@ export class TaskDispatchService {
382
508
  // Update collab board
383
509
  this.collabBoard.transition({
384
510
  dispatchId: payload.dispatchId,
385
- expected: 'pending_accept',
511
+ expected: ['received', 'pending_accept'],
386
512
  next: 'rejected',
387
513
  actor: { type: 'team', id: teamSlug },
388
514
  eventType: 'task_rejected',
@@ -425,6 +551,19 @@ export class TaskDispatchService {
425
551
  throw new Error(`No collab task found for dispatchId: ${dispatchId}`);
426
552
  }
427
553
 
554
+ const localTasks = await this.workspace.readTasks(teamSlug).catch(() => []);
555
+ const completedTask = localTasks.find((task) => task.dispatchMeta?.dispatchId === dispatchId);
556
+ if (!completedTask) {
557
+ throw new Error(`No local task found for dispatchId: ${dispatchId}`);
558
+ }
559
+ if (completedTask.status !== 'done') {
560
+ throw new Error('Task result cannot be delivered before the agent marks the task done.');
561
+ }
562
+
563
+ if (collabTask.status === 'approved') {
564
+ throw new Error('Task result has already been approved and cannot be delivered again.');
565
+ }
566
+
428
567
  const deliveredAt = new Date().toISOString();
429
568
 
430
569
  // Send deliver response to origin team
@@ -451,7 +590,7 @@ export class TaskDispatchService {
451
590
  // Update local collab board
452
591
  const deliveredTask = this.collabBoard.transition({
453
592
  dispatchId,
454
- expected: ['accepted', 'revision'],
593
+ expected: ['in_progress', 'accepted', 'revision'],
455
594
  next: 'delivered',
456
595
  actor: { type: 'team', id: teamSlug },
457
596
  eventType: 'task_delivered',
@@ -658,6 +797,20 @@ export class TaskDispatchService {
658
797
  dispatchMeta: { ...meta, status: 'completed', completedAt: update.timestamp },
659
798
  } as any);
660
799
 
800
+ const collabTask = this.collabBoard.getTask(meta.dispatchId);
801
+ if (collabTask) {
802
+ this.emitCollabChange(
803
+ meta.dispatchId,
804
+ collabTask.status,
805
+ collabTask.fromTeam,
806
+ collabTask.toTeam
807
+ );
808
+ }
809
+
810
+ // Completion only marks the target-side task as done. The origin team should
811
+ // receive the callback/result through deliverTask(), which is gated above by
812
+ // this local done state.
813
+
661
814
  if (this.redis) {
662
815
  const channel = `task:status:${meta.originTeam}`;
663
816
  await this.redis.publish(channel, JSON.stringify(update)).catch((err: Error) => {
@@ -706,26 +859,9 @@ export class TaskDispatchService {
706
859
  const streamKey = `task:dispatch:${dispatchMeta.targetTeam}`;
707
860
  await this.redis!.xadd(streamKey, '*', 'payload', JSON.stringify(payload));
708
861
 
709
- // If local team, also write to inbox
710
- const isLocal = await this.isLocalTeam(dispatchMeta.targetTeam);
711
- if (isLocal) {
712
- try {
713
- await this.workspace.appendMessage(dispatchMeta.targetTeam, {
714
- from: dispatchMeta.originTeam,
715
- to: 'team',
716
- role: 'agent',
717
- content: `[跨团队任务] ${task.subject}${task.description ? '\n' + task.description : ''}`,
718
- meta: {
719
- source: 'cross_team_dispatch',
720
- dispatchId: dispatchMeta.dispatchId,
721
- originTeam: dispatchMeta.originTeam,
722
- needsHumanReview,
723
- },
724
- });
725
- } catch (err) {
726
- console.error('[TaskDispatchService] inbox write failed:', (err as Error).message);
727
- }
728
- }
862
+ // Do not write an execution-looking runtime/inbox prompt here. Dispatch creation
863
+ // only makes a target TODO visible; runtime delivery happens after Start.
864
+ void needsHumanReview;
729
865
 
730
866
  this.sendFeishuNotification(
731
867
  `跨团队任务派发:${dispatchMeta.originTeam} → ${dispatchMeta.targetTeam}\n${task.subject}`
@@ -850,8 +986,8 @@ export class TaskDispatchService {
850
986
  const tasks = await this.workspace.readTasks(team.slug);
851
987
  for (const task of tasks) {
852
988
  if (
853
- task.dispatchMeta?.status === 'pending_accept' &&
854
- task.dispatchMeta.deadline &&
989
+ ['received', 'pending_accept'].includes(task.dispatchMeta?.status ?? '') &&
990
+ task.dispatchMeta?.deadline &&
855
991
  new Date(task.dispatchMeta.deadline).getTime() < Date.now()
856
992
  ) {
857
993
  await this.workspace.patchTask(team.slug, task.id, {
@@ -957,14 +1093,14 @@ export class TaskDispatchService {
957
1093
  fromTeamDisplay: fromTeamManifest?.displayName ?? payload.originTeam,
958
1094
  toTeam: teamSlug,
959
1095
  toTeamDisplay: toTeamManifest?.displayName ?? teamSlug,
960
- status: 'pending_accept',
1096
+ status: 'received',
961
1097
  deadline: payload.deadline,
962
1098
  needsHumanReview: payload.needsHumanReview ?? false,
963
1099
  revisionCount: 0,
964
1100
  createdAt,
965
1101
  updatedAt: createdAt,
966
1102
  });
967
- this.emitCollabChange(payload.dispatchId, 'pending_accept', payload.originTeam, teamSlug);
1103
+ this.emitCollabChange(payload.dispatchId, 'received', payload.originTeam, teamSlug);
968
1104
 
969
1105
  const existingTasks = await this.workspace.readTasks(teamSlug).catch(() => []);
970
1106
  const existingTask = existingTasks.find(
@@ -980,7 +1116,7 @@ export class TaskDispatchService {
980
1116
  dispatchId: payload.dispatchId,
981
1117
  originTeam: payload.originTeam,
982
1118
  targetTeam: teamSlug,
983
- status: 'pending_accept',
1119
+ status: 'received',
984
1120
  dispatchedAt: payload.dispatchedAt,
985
1121
  receivedAt: new Date().toISOString(),
986
1122
  deadline: payload.deadline,
@@ -996,26 +1132,9 @@ export class TaskDispatchService {
996
1132
  localTaskId: localTask.id,
997
1133
  });
998
1134
 
999
- if (!alreadyPending) {
1000
- await this.workspace
1001
- .appendMessage(teamSlug, {
1002
- from: payload.originTeam,
1003
- to: 'team',
1004
- role: 'agent',
1005
- content: `[跨团队任务] ${payload.task.subject}${
1006
- payload.task.description ? '\n' + payload.task.description : ''
1007
- }`,
1008
- meta: {
1009
- source: 'cross_team_dispatch',
1010
- dispatchId: payload.dispatchId,
1011
- originTeam: payload.originTeam,
1012
- needsHumanReview: payload.needsHumanReview,
1013
- },
1014
- })
1015
- .catch((err: Error) => {
1016
- console.error('[TaskDispatchService] inbox write failed:', err.message);
1017
- });
1018
- }
1135
+ // Do not append an execution-looking message here. The target TODO card is
1136
+ // the only pre-start surface; runtime delivery happens after the user clicks Start.
1137
+ void alreadyPending;
1019
1138
 
1020
1139
  console.log(
1021
1140
  `[TaskDispatchService] received dispatch request: ${payload.dispatchId} from ${payload.originTeam} → ${teamSlug}`
@@ -15,6 +15,7 @@ import { createLogger } from '@shared/utils/logger';
15
15
  import type { CcConnectBridge } from '../ccConnect/CcConnectBridge';
16
16
  import type { CcConnectClient } from '../ccConnect/CcConnectClient';
17
17
 
18
+ import { buildHermitOpsRunbookContext } from './OpsRunbookContext';
18
19
  import {
19
20
  TeamWorkspaceService,
20
21
  groupSessionKey,
@@ -80,7 +81,8 @@ export class TeamProvisioningService {
80
81
  constructor(
81
82
  private readonly cc: CcConnectClient,
82
83
  private readonly bridge: CcConnectBridge,
83
- workspace?: TeamWorkspaceService
84
+ workspace?: TeamWorkspaceService,
85
+ private readonly hooks: { restartCcConnect?: () => Promise<void> } = {}
84
86
  ) {
85
87
  this.workspace = workspace ?? new TeamWorkspaceService();
86
88
  }
@@ -105,6 +107,7 @@ export class TeamProvisioningService {
105
107
 
106
108
  if (injectInstructions && manifest.harness === 'claudecode') {
107
109
  await injectHermitTasksMcpConfig(manifest.workDir);
110
+ await this.injectTeamInstructions(manifest.workDir, manifest.slug);
108
111
  }
109
112
 
110
113
  if (createCcProject) {
@@ -119,7 +122,11 @@ export class TeamProvisioningService {
119
122
  platformOpts as Record<string, string>
120
123
  );
121
124
  if (result.restart_required) {
122
- await this.cc.restart();
125
+ if (this.hooks.restartCcConnect) {
126
+ await this.hooks.restartCcConnect();
127
+ } else {
128
+ await this.cc.restart();
129
+ }
123
130
  logger.info(`cc-connect restarted after creating project ${manifest.bindProject}`);
124
131
  }
125
132
  } catch (err) {
@@ -309,6 +316,7 @@ export class TeamProvisioningService {
309
316
  : team.slug;
310
317
  return team.description ? `- ${label}: ${team.description}` : `- ${label}`;
311
318
  });
319
+ const opsRunbookContext = buildHermitOpsRunbookContext();
312
320
  const section = `
313
321
 
314
322
  ${TEAM_INSTRUCTIONS_BEGIN}
@@ -325,6 +333,8 @@ Hermit will create and track the cross-team collaboration task automatically.
325
333
 
326
334
  Do not call cross-team dispatch APIs yourself and do not invent dispatch IDs.
327
335
  You may use the team list only to understand which teams exist and when a user is referring to one.
336
+
337
+ ${opsRunbookContext}
328
338
  ${TEAM_INSTRUCTIONS_END}
329
339
  `;
330
340