@yancyyu/openhermit 1.6.41 → 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.
- package/README.md +98 -89
- package/bin/hermit.mjs +96 -0
- package/dist-renderer/assets/{ProjectEditorOverlay-Br0X83Jf.js → ProjectEditorOverlay-C98qSs7-.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-DHMTbZPZ.js → TeamGraphOverlay-CsBbZwcL.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-DzIiX7yH.js → _basePickBy-ZOyLWjMK.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-6hZuzTLU.js → _baseUniq-DBb726rt.js} +1 -1
- package/dist-renderer/assets/{arc-CXgO6fx_.js → arc-CdiTaR_R.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DKWgtDHr.js → architectureDiagram-VXUJARFQ-Cz3sc5TH.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-DOMUcC40.js → blockDiagram-VD42YOAC-DE4c-KJ3.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B_k2L7qX.js → c4Diagram-YG6GDRKO-CmTMDTrV.js} +1 -1
- package/dist-renderer/assets/channel-KTpqi9eT.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-BeD_ccFy.js → chunk-4BX2VUAB-rhHy3tFl.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-ClZfkA5w.js → chunk-55IACEB6-fLZBzuo_.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-5XluxXsn.js → chunk-B4BG7PRW-DOzxQhim.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-BzIjjNVm.js → chunk-DI55MBZ5-COQCcXC5.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-HgH3MK_H.js → chunk-FMBD7UC4-IKU9U_Y4.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-WeC5T3Ba.js → chunk-QN33PNHL-D6WV154X.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-Cu1ApHfW.js → chunk-QZHKN3VN-D90_2DQp.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-BOhlynJM.js → chunk-TZMSLE5B-BQEil57G.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-lpzulY5X.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-lpzulY5X.js +1 -0
- package/dist-renderer/assets/clone-CriGymY9.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DGZSihDQ.js → cose-bilkent-S5V4N54A-6WiK6U2P.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-CnxwCbku.js → dagre-6UL2VRFP-DF4MMuTn.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-DsIhoxdI.js → diagram-PSM6KHXK-CcF1eZ7E.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-Cmh9KUF5.js → diagram-QEK2KX5R-DYlOVPQB.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-CKxV456A.js → diagram-S2PKOQOG-BHXWsZOP.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-EnvYjOjc.js → erDiagram-Q2GNP2WA-GjmuBx8d.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-BmNeWY_A.js → flowDiagram-NV44I4VS-BuS7YVHk.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-D30fyK-u.js → ganttDiagram-JELNMOA3-3Teu5tAa.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-CrUNiYg1.js → gitGraphDiagram-V2S2FVAM-BiLdCYu5.js} +1 -1
- package/dist-renderer/assets/{graph-CY1gTfTb.js → graph-CDP_R8ct.js} +1 -1
- package/dist-renderer/assets/{index-CaEbzwAU.js → index-BSZdT-g-.js} +1 -1
- package/dist-renderer/assets/{index-D5K-SjBG.js → index-BhWvMqsz.js} +1 -1
- package/dist-renderer/assets/{index-9_hO4N1e.js → index-C2_AupSj.js} +1 -1
- package/dist-renderer/assets/{index-59r209c1.js → index-C5ujiwAR.js} +580 -588
- package/dist-renderer/assets/index-CIS2CTK9.css +1 -0
- package/dist-renderer/assets/{index-DMR9B1UP.js → index-CVNjLwkq.js} +1 -1
- package/dist-renderer/assets/{index-BC2hXmg_.js → index-CwG3se0q.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-By_XUlcD.js → infoDiagram-HS3SLOUP-DLHUFo72.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BM1LJE9m.js → journeyDiagram-XKPGCS4Q-BE07RpJD.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-DHIW3aTA.js → kanban-definition-3W4ZIXB7-DDHZy4NB.js} +1 -1
- package/dist-renderer/assets/{layout-DAKiL_Mo.js → layout-5nA5wUxO.js} +1 -1
- package/dist-renderer/assets/{linear-DwOaRYea.js → linear-BtF1i2qN.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-b7bJ2cha.js → mindmap-definition-VGOIOE7T-Z1Ui9Sqy.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DxyL9Zr2.js → pieDiagram-ADFJNKIX-LCjxckWv.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CR33pHlF.js → quadrantDiagram-AYHSOK5B-BOwKjSco.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-BAiSRSlh.js → requirementDiagram-UZGBJVZJ-pChP8Znd.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8JmDjoa.js → sankeyDiagram-TZEHDZUN-DifZ2qpo.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-c1d0Wi1m.js → sequenceDiagram-WL72ISMW-CJg-WYyY.js} +1 -1
- package/dist-renderer/assets/{splashScene-D0YB9uxm.js → splashScene-94xWCzLA.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-nT8BiH2O.js → stateDiagram-FKZM4ZOC-DWHOoFdv.js} +1 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-CGYZOoMb.js +1 -0
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-DpoRepUA.js → timeline-definition-IT6M3QCI-CPgokIo8.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-C41UJeIH.js → treemap-GDKQZRPO-DAVqSR9L.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-KMjGARKN.js → xychartDiagram-PRI3JC2R-CCOcGbrD.js} +1 -1
- package/dist-renderer/chat-community-qr.jpg +0 -0
- package/dist-renderer/fonts/Agave-Bold.ttf +0 -0
- package/dist-renderer/fonts/Agave-Regular.ttf +0 -0
- package/dist-renderer/icon.png +0 -0
- package/dist-renderer/icon.rar +0 -0
- package/dist-renderer/index.html +3 -3
- package/package.json +21 -26
- package/src/features/worker-society/core/application/WorkerSocietyService.test.ts +802 -0
- package/src/features/worker-society/core/application/WorkerSocietyService.ts +428 -0
- package/src/features/worker-society/core/application/fakes.ts +101 -0
- package/src/features/worker-society/core/application/ports.ts +70 -0
- package/src/features/worker-society/core/domain/models/society.ts +141 -0
- package/src/features/worker-society/core/domain/policies/societyPolicies.test.ts +739 -0
- package/src/features/worker-society/core/domain/policies/societyPolicies.ts +496 -0
- package/src/features/worker-society/main/adapters/input/societyMcp.test.ts +317 -0
- package/src/features/worker-society/main/adapters/input/societyMcp.ts +257 -0
- package/src/features/worker-society/main/adapters/input/societyRoutes.test.ts +695 -0
- package/src/features/worker-society/main/adapters/input/societyRoutes.ts +194 -0
- package/src/features/worker-society/main/composition/societyComposition.test.ts +74 -0
- package/src/features/worker-society/main/composition/societyComposition.ts +70 -0
- package/src/features/worker-society/main/composition/workerSocietyPlugin.test.ts +69 -0
- package/src/features/worker-society/main/composition/workerSocietyPlugin.ts +67 -0
- package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.test.ts +132 -0
- package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.ts +84 -0
- package/src/features/worker-society/main/infrastructure/fsStores.test.ts +216 -0
- package/src/features/worker-society/main/infrastructure/fsStores.ts +113 -0
- package/src/features/worker-society/main/infrastructure/mergingProfileStore.test.ts +195 -0
- package/src/features/worker-society/main/infrastructure/mergingProfileStore.ts +96 -0
- package/src/features/worker-society/renderer/SocietyGraph.tsx +166 -0
- package/src/features/worker-society/renderer/SocietyNodeLabels.tsx +139 -0
- package/src/features/worker-society/renderer/SocietyNodeOverlay.tsx +339 -0
- package/src/features/worker-society/renderer/SocietyView.tsx +437 -0
- package/src/features/worker-society/renderer/index.ts +11 -0
- package/src/features/worker-society/renderer/societyApi.test.ts +259 -0
- package/src/features/worker-society/renderer/societyApi.ts +144 -0
- package/src/features/worker-society/renderer/societyGraphAdapter.test.ts +321 -0
- package/src/features/worker-society/renderer/societyGraphAdapter.ts +240 -0
- package/src/features/worker-society/renderer/societyOverlayActions.test.ts +57 -0
- package/src/features/worker-society/renderer/societyOverlayActions.ts +49 -0
- package/src/features/worker-society/renderer/societyStore.test.ts +218 -0
- package/src/features/worker-society/renderer/societyStore.ts +146 -0
- package/src/features/worker-society/renderer/societyViewUtils.test.ts +81 -0
- package/src/features/worker-society/renderer/societyViewUtils.ts +68 -0
- package/src/main/ipc/extensions.ts +27 -0
- package/src/main/server.ts +1731 -539
- package/src/main/services/ccConnect/CcConnectBridge.ts +26 -11
- package/src/main/services/ccConnect/CcConnectClient.ts +9 -2
- package/src/main/services/ccConnect/workDirReconcile.test.ts +57 -0
- package/src/main/services/ccConnect/workDirReconcile.ts +36 -0
- package/src/main/services/direct-cli/DirectCliSessionManager.test.ts +397 -0
- package/src/main/services/direct-cli/DirectCliSessionManager.ts +508 -0
- package/src/main/services/direct-cli/DirectCliSessionStore.test.ts +79 -0
- package/src/main/services/direct-cli/DirectCliSessionStore.ts +97 -0
- package/src/main/services/direct-cli/__tests__/directCliMessageId.test.ts +40 -0
- package/src/main/services/direct-cli/directCliMessageId.ts +21 -0
- package/src/main/services/direct-cli/index.ts +17 -0
- package/src/main/services/extensions/capability-packs/CapabilityPackLoaderService.ts +637 -0
- package/src/main/services/extensions/catalog/PluginCatalogService.ts +2 -2
- package/src/main/services/loop-assets/LoopAssetsScannerService.ts +657 -0
- package/src/main/services/runtime/providerAwareCliEnv.ts +33 -5
- package/src/main/services/session-intelligence/LocalSessionScanner.ts +156 -71
- package/src/main/services/session-intelligence/SessionUsageParser.ts +103 -8
- package/src/main/services/session-intelligence/UsageTelemetryService.ts +11 -0
- package/src/main/services/session-intelligence/__tests__/teamSessionListMapper.test.ts +104 -0
- package/src/main/services/session-intelligence/teamSessionListMapper.ts +78 -0
- package/src/main/services/system-manager/AdminLoopInitializer.ts +95 -0
- package/src/main/services/system-manager/BuiltinWorkflowSeeder.ts +744 -0
- package/src/main/services/system-manager/SystemManagerConfigService.ts +19 -1
- package/src/main/services/system-manager/WorkflowPromptService.ts +58 -5
- package/src/main/services/system-manager/__tests__/AdminLoopInitializer.test.ts +129 -0
- package/src/main/services/system-manager/__tests__/SystemManagerConfigService.test.ts +60 -0
- package/src/main/services/teams-mvp/CollaborationBoardService.ts +2 -0
- package/src/main/services/teams-mvp/OpsRunbookContext.ts +60 -0
- package/src/main/services/teams-mvp/TaskDispatchService.test.ts +305 -0
- package/src/main/services/teams-mvp/TaskDispatchService.ts +250 -131
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +12 -2
- package/src/main/services/teams-mvp/TeamWorkspaceService.test.ts +207 -0
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +104 -51
- package/src/main/services/teams-mvp/index.ts +6 -0
- package/src/main/utils/externalPlatformSessionRouting.ts +92 -0
- package/src/main/utils/toolApprovalRules.ts +151 -0
- package/src/renderer/App.tsx +24 -89
- package/src/renderer/api/httpClient.ts +132 -52
- package/src/renderer/api/providers.ts +5 -16
- package/src/renderer/components/chat/CommunityChatView.tsx +81 -0
- package/src/renderer/components/dashboard/DashboardView.tsx +130 -84
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +39 -5
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +2 -1
- package/src/renderer/components/extensions/capability-packs/CapabilityPacksPanel.tsx +170 -0
- package/src/renderer/components/layout/PaneContent.tsx +10 -2
- package/src/renderer/components/layout/SortableTab.tsx +4 -0
- package/src/renderer/components/layout/TabBarActions.tsx +13 -16
- package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +4 -135
- package/src/renderer/components/schedules/SchedulesView.tsx +22 -14
- package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +7 -6
- package/src/renderer/components/settings/SettingsTabs.tsx +24 -21
- package/src/renderer/components/settings/SettingsView.tsx +22 -13
- package/src/renderer/components/settings/components/SettingRow.tsx +13 -5
- package/src/renderer/components/settings/components/SettingsSectionCard.tsx +53 -0
- package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +10 -6
- package/src/renderer/components/settings/components/SettingsSelect.tsx +12 -9
- package/src/renderer/components/settings/components/SettingsToggle.tsx +6 -5
- package/src/renderer/components/settings/components/index.ts +1 -0
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +78 -59
- package/src/renderer/components/settings/sections/CliStatusSection.tsx +32 -44
- package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
- package/src/renderer/components/settings/sections/GeneralSection.tsx +216 -186
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +25 -17
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +63 -22
- package/src/renderer/components/sidebar/SidebarSessions.tsx +120 -80
- package/src/renderer/components/sidebar/SidebarTaskItem.tsx +1 -1
- package/src/renderer/components/splash/splashScene.ts +6 -2
- package/src/renderer/components/system-manager/SystemManagerView.tsx +169 -255
- package/src/renderer/components/tasks/TasksView.tsx +63 -37
- package/src/renderer/components/team/CcSessionsSection.tsx +124 -89
- package/src/renderer/components/team/HarnessBrandLogos.tsx +318 -0
- package/src/renderer/components/team/HarnessSelect.tsx +25 -26
- package/src/renderer/components/team/TeamDetailView.tsx +137 -153
- package/src/renderer/components/team/TeamEmptyState.tsx +9 -37
- package/src/renderer/components/team/TeamListView.tsx +144 -30
- package/src/renderer/components/team/__tests__/CcSessionsSection.hasLocalFile.test.tsx +128 -0
- package/src/renderer/components/team/activity/ActivityItem.tsx +21 -9
- package/src/renderer/components/team/activity/ActivityTimeline.tsx +2 -2
- package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +1 -1
- package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +13 -10
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +189 -57
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +9 -157
- package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +19 -15
- package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +48 -10
- package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +11 -12
- package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +39 -37
- package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +434 -64
- package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +12 -10
- package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +2 -2
- package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.bindProject.test.tsx +399 -0
- package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.chineseRepro.test.tsx +253 -0
- package/src/renderer/components/team/dialogs/platformAllowUtils.ts +91 -0
- package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +1 -1
- package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +41 -0
- package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +41 -86
- package/src/renderer/components/team/loop-console/LoopCommandComposer.tsx +310 -0
- package/src/renderer/components/team/loop-console/LoopConsolePanel.tsx +372 -0
- package/src/renderer/components/team/loop-console/loopSendIntent.test.ts +85 -0
- package/src/renderer/components/team/loop-console/loopSendIntent.ts +221 -0
- package/src/renderer/components/team/loop-console/useLeadSessionToolActivity.ts +74 -0
- package/src/renderer/components/team/loop-console/useLoopCommandSuggestions.ts +165 -0
- package/src/renderer/components/team/loop-console/useLoopConsoleController.ts +266 -0
- package/src/renderer/components/team/members/LeadModelRow.test.tsx +1 -1
- package/src/renderer/components/team/members/LeadModelRow.tsx +5 -3
- package/src/renderer/components/team/members/MemberDetailDialog.tsx +11 -0
- package/src/renderer/components/team/members/MemberDetailStats.tsx +13 -3
- package/src/renderer/components/team/members/MemberDraftRow.test.tsx +1 -1
- package/src/renderer/components/team/members/MemberDraftRow.tsx +1 -1
- package/src/renderer/components/team/members/MemberMessagesTab.tsx +2 -2
- package/src/renderer/components/team/members/MemberStatsTab.tsx +1 -1
- package/src/renderer/components/team/messages/MessageComposer.tsx +150 -44
- package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +2 -2
- package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -28
- package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +6 -6
- package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +2 -2
- package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +1 -1
- package/src/renderer/components/terminal/TerminalPanel.tsx +2 -3
- package/src/renderer/components/ui/MentionableTextarea.tsx +5 -1
- package/src/renderer/constants/teamColors.ts +5 -5
- package/src/renderer/hooks/useExtensionsTabState.ts +1 -1
- package/src/renderer/hooks/useMentionDetection.ts +5 -1
- package/src/renderer/hooks/useProjectWorkflowCommands.ts +57 -0
- package/src/renderer/hooks/useTeamSuggestions.ts +2 -0
- package/src/renderer/index.css +19 -2
- package/src/renderer/main.tsx +7 -1
- package/src/renderer/store/index.ts +18 -1
- package/src/renderer/store/slices/extensionsSlice.ts +83 -0
- package/src/renderer/store/slices/tabSlice.ts +61 -0
- package/src/renderer/store/slices/teamSlice.ts +138 -9
- package/src/renderer/types/mention.ts +8 -0
- package/src/renderer/types/tabs.ts +3 -1
- package/src/renderer/utils/__tests__/bindProjectSlug.test.ts +69 -0
- package/src/renderer/utils/__tests__/groupTransformer.test.ts +148 -0
- package/src/renderer/utils/__tests__/initialRoute.test.ts +101 -0
- package/src/renderer/utils/__tests__/leadToolActivity.test.ts +124 -0
- package/src/renderer/utils/__tests__/mergeTeamMessages.test.ts +81 -0
- package/src/renderer/utils/__tests__/teamMessageFiltering.test.ts +213 -0
- package/src/renderer/utils/__tests__/teamMessageKey.test.ts +75 -0
- package/src/renderer/utils/__tests__/workflowCommandExecution.test.ts +173 -0
- package/src/renderer/utils/__tests__/workflowCommandSuggestions.test.ts +59 -0
- package/src/renderer/utils/bindProjectSlug.ts +57 -0
- package/src/renderer/utils/capabilityCommandExecution.ts +113 -0
- package/src/renderer/utils/initialRoute.ts +89 -0
- package/src/renderer/utils/leadToolActivity.ts +117 -0
- package/src/renderer/utils/loopShortcutSuggestions.ts +106 -0
- package/src/renderer/utils/mentionSuggestions.ts +1 -1
- package/src/renderer/utils/slashCommandRegistry.ts +231 -0
- package/src/renderer/utils/teamMentionDirective.ts +31 -0
- package/src/renderer/utils/workflowCommandExecution.ts +96 -0
- package/src/renderer/utils/workflowCommandSuggestions.ts +49 -0
- package/src/shared/types/api.ts +79 -4
- package/src/shared/types/ccConnect.ts +1 -0
- package/src/shared/types/extensions/api.ts +19 -0
- package/src/shared/types/extensions/capabilityPack.ts +118 -0
- package/src/shared/types/extensions/index.ts +29 -1
- package/src/shared/types/index.ts +6 -0
- package/src/shared/types/loopAssets.ts +54 -0
- package/src/shared/types/providers.ts +0 -16
- package/src/shared/types/systemManager.ts +26 -1
- package/src/shared/types/team.ts +43 -3
- package/src/shared/types/terminal.ts +2 -36
- package/src/shared/types/worker.test.ts +28 -0
- package/src/shared/types/worker.ts +3 -0
- package/src/shared/utils/__tests__/effortLevels.test.ts +88 -0
- package/src/shared/utils/__tests__/providerBackend.test.ts +88 -0
- package/src/shared/utils/__tests__/providerLaunchArgs.test.ts +220 -0
- package/src/shared/utils/claudeStreamJson.test.ts +187 -0
- package/src/shared/utils/claudeStreamJson.ts +153 -0
- package/src/shared/utils/providerLaunchArgs.ts +217 -0
- package/src/shared/utils/slashCommands.ts +10 -0
- package/src/types/node-pty.d.ts +8 -0
- package/dist-renderer/assets/channel-D0XS_akr.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-D13Ffs0U.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D13Ffs0U.js +0 -1
- package/dist-renderer/assets/clone-B1ZrxI1D.js +0 -1
- package/dist-renderer/assets/index-iyjkpSus.css +0 -32
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Dmibmlso.js +0 -1
- package/src/main/services/system-manager/SystemManagerPtyService.ts +0 -233
- package/src/renderer/components/common/TerminalPane.tsx +0 -213
- 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: '
|
|
180
|
-
dispatchedAt
|
|
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.
|
|
185
|
-
//
|
|
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: '
|
|
213
|
+
status: 'received',
|
|
198
214
|
deadline,
|
|
199
215
|
needsHumanReview: opts?.needsHumanReview ?? false,
|
|
200
216
|
revisionCount: 0,
|
|
201
|
-
createdAt:
|
|
202
|
-
updatedAt:
|
|
217
|
+
createdAt: dispatchedAt,
|
|
218
|
+
updatedAt: dispatchedAt,
|
|
203
219
|
};
|
|
204
220
|
this.collabBoard.addTask(collabTask);
|
|
205
221
|
|
|
206
|
-
|
|
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, '
|
|
287
|
+
this.emitCollabChange(dispatchId, 'received', fromTeam, targetTeam);
|
|
250
288
|
|
|
251
289
|
return {
|
|
252
290
|
dispatchId,
|
|
253
|
-
status: '
|
|
291
|
+
status: 'received',
|
|
254
292
|
targetTeam,
|
|
255
|
-
message: `Task
|
|
293
|
+
message: `Task queued in ${targetTeam} TODO, waiting for manual start.`,
|
|
256
294
|
};
|
|
257
295
|
}
|
|
258
296
|
|
|
259
|
-
async
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
291
|
-
|
|
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
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
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.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
390
|
+
this.emitCollabChange(meta.dispatchId, next.status, next.fromTeam, next.toTeam);
|
|
391
|
+
}
|
|
324
392
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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: `[
|
|
421
|
+
content: `[跨团队任务已启动] "${task.title}" — ${teamSlug} 已从 TODO 点击启动并开始执行。`,
|
|
332
422
|
meta: {
|
|
333
|
-
source: '
|
|
334
|
-
dispatchId:
|
|
423
|
+
source: 'cross_team_started',
|
|
424
|
+
dispatchId: meta.dispatchId,
|
|
335
425
|
targetTeam: teamSlug,
|
|
426
|
+
taskId,
|
|
336
427
|
},
|
|
337
|
-
})
|
|
338
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
710
|
-
|
|
711
|
-
|
|
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
|
|
854
|
-
task.dispatchMeta
|
|
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: '
|
|
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, '
|
|
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: '
|
|
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
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
-
|
|
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
|
|