@bbigbang/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/config.js +380 -0
  2. package/dist/execution/executionDispatcher.js +3810 -0
  3. package/dist/main.js +90 -0
  4. package/dist/nodeEventHistory.js +206 -0
  5. package/dist/scheduler/dreamLogic.js +50 -0
  6. package/dist/scheduler/dreamScheduler.js +65 -0
  7. package/dist/services/agentFileAccessService.js +1913 -0
  8. package/dist/services/agentRuntimeCleanupBroker.js +62 -0
  9. package/dist/services/agentSkillsBroker.js +118 -0
  10. package/dist/services/agentSkillsService.js +83 -0
  11. package/dist/services/agentWorkspaceBroker.js +937 -0
  12. package/dist/services/agentWorkspaceService.js +70 -0
  13. package/dist/services/appVersion.js +14 -0
  14. package/dist/services/auth.js +586 -0
  15. package/dist/services/claudeControlBroker.js +154 -0
  16. package/dist/services/claudeTranscriptBroker.js +100 -0
  17. package/dist/services/claudeTranscriptService.js +359 -0
  18. package/dist/services/codexAppServerBroker.js +155 -0
  19. package/dist/services/codexTranscriptBroker.js +98 -0
  20. package/dist/services/codexTranscriptService.js +961 -0
  21. package/dist/services/droidMissionBroker.js +124 -0
  22. package/dist/services/droidMissionImporter.js +630 -0
  23. package/dist/services/droidModelOptions.js +165 -0
  24. package/dist/services/hubServerRegistrationService.js +268 -0
  25. package/dist/services/libraryManifest.js +43 -0
  26. package/dist/services/libraryScaffold.js +26 -0
  27. package/dist/services/libraryService.js +2263 -0
  28. package/dist/services/memoryService.js +386 -0
  29. package/dist/services/missionEvidence.js +377 -0
  30. package/dist/services/missionService.js +2361 -0
  31. package/dist/services/missionTrace.js +158 -0
  32. package/dist/services/nativeMissionBriefParser.js +120 -0
  33. package/dist/services/nativeMissionOrchestrator.js +2045 -0
  34. package/dist/services/nativeMissionReportGenerator.js +227 -0
  35. package/dist/services/nativeMissionValidationRunner.js +452 -0
  36. package/dist/services/nativeMissionWorkerBroker.js +190 -0
  37. package/dist/services/nodeRegistry.js +34 -0
  38. package/dist/services/nodeStateReconciler.js +97 -0
  39. package/dist/services/panelMediaScanner.js +119 -0
  40. package/dist/services/persistentRuntimeJsonlClient.js +153 -0
  41. package/dist/services/platformAgentPolicy.js +180 -0
  42. package/dist/services/platformAgentService.js +2041 -0
  43. package/dist/services/projectAccessResolver.js +93 -0
  44. package/dist/services/projectService.js +392 -0
  45. package/dist/services/resourceSpaceService.js +140 -0
  46. package/dist/services/scenarioRuntimeService.js +1130 -0
  47. package/dist/services/suggestedPlannerService.js +868 -0
  48. package/dist/services/workbenchGitBroker.js +161 -0
  49. package/dist/services/workbenchGitService.js +69 -0
  50. package/dist/services/workbenchInspectBroker.js +65 -0
  51. package/dist/services/workbenchNodePathService.js +79 -0
  52. package/dist/services/workbenchRegistryService.js +240 -0
  53. package/dist/services/workbenchRootService.js +181 -0
  54. package/dist/services/workbenchTerminalBroker.js +378 -0
  55. package/dist/services/workspaceRunOwnership.js +60 -0
  56. package/dist/services/workspaceScaffold.js +105 -0
  57. package/dist/services/workspaceSessionRuntimeService.js +576 -0
  58. package/dist/services/workspaceSessionService.js +245 -0
  59. package/dist/services/workspaceToolActionRunner.js +1582 -0
  60. package/dist/services/workspaceToolErrors.js +10 -0
  61. package/dist/services/workspaceToolExecutionUtils.js +895 -0
  62. package/dist/services/workspaceToolLatestStateProjector.js +91 -0
  63. package/dist/services/workspaceToolManifest.js +572 -0
  64. package/dist/services/workspaceToolMutationQueue.js +43 -0
  65. package/dist/services/workspaceToolPanelProjection.js +460 -0
  66. package/dist/services/workspaceToolPromotion.js +255 -0
  67. package/dist/services/workspaceToolPromotionState.js +224 -0
  68. package/dist/services/workspaceToolPublishDiagnostics.js +189 -0
  69. package/dist/services/workspaceToolPublishIdentityResolver.js +146 -0
  70. package/dist/services/workspaceToolReadModel.js +378 -0
  71. package/dist/services/workspaceToolRunLedger.js +239 -0
  72. package/dist/services/workspaceToolService.js +3067 -0
  73. package/dist/services/workspaceToolSnapshotPanelSync.js +293 -0
  74. package/dist/services/workspaceToolTerminalLifecycle.js +283 -0
  75. package/dist/services/workspaceToolTypes.js +1 -0
  76. package/dist/services/workspaceToolUploadMaterializer.js +228 -0
  77. package/dist/web/actionCardRoutes.js +129 -0
  78. package/dist/web/actionCards.js +469 -0
  79. package/dist/web/activationContext.js +684 -0
  80. package/dist/web/agentChannelGuards.js +48 -0
  81. package/dist/web/agentMentionCooldowns.js +32 -0
  82. package/dist/web/agentReminders.js +1668 -0
  83. package/dist/web/agentRuntimePresence.js +197 -0
  84. package/dist/web/agentSelfState.js +494 -0
  85. package/dist/web/agentTaskLinks.js +26 -0
  86. package/dist/web/agentVisibility.js +79 -0
  87. package/dist/web/assets.js +95 -0
  88. package/dist/web/channelActivationPrompt.js +395 -0
  89. package/dist/web/channelMemoryNotes.js +127 -0
  90. package/dist/web/channelMentions.js +10 -0
  91. package/dist/web/channelMessageSequences.js +19 -0
  92. package/dist/web/channelSubscriptions.js +26 -0
  93. package/dist/web/clearedTaskRoots.js +10 -0
  94. package/dist/web/collaborationPromptGuidance.js +36 -0
  95. package/dist/web/collaborationSurfaceState.js +140 -0
  96. package/dist/web/contextBundleRanking.js +154 -0
  97. package/dist/web/contextBundleResolver.js +488 -0
  98. package/dist/web/conversationBuiltinSkillRoots.js +50 -0
  99. package/dist/web/conversationControls.js +232 -0
  100. package/dist/web/conversationHandoffs.js +612 -0
  101. package/dist/web/conversationManager.js +2511 -0
  102. package/dist/web/conversationSummaries.js +876 -0
  103. package/dist/web/conversationSurfaceKinds.js +17 -0
  104. package/dist/web/conversationTargets.js +173 -0
  105. package/dist/web/directActivationPrompt.js +122 -0
  106. package/dist/web/directReplyTargets.js +69 -0
  107. package/dist/web/directThreadResolver.js +129 -0
  108. package/dist/web/dmTaskHandoffPrompt.js +120 -0
  109. package/dist/web/dmTaskThreadStatusProjection.js +229 -0
  110. package/dist/web/ftsQuery.js +33 -0
  111. package/dist/web/internalAgentRouter.js +11341 -0
  112. package/dist/web/libraryCuratorScheduler.js +58 -0
  113. package/dist/web/libraryDocumentPromptGuidance.js +8 -0
  114. package/dist/web/messageCheckpoints.js +19 -0
  115. package/dist/web/nodeWsHandler.js +2495 -0
  116. package/dist/web/notificationRounds.js +1061 -0
  117. package/dist/web/panelActionMessages.js +108 -0
  118. package/dist/web/panelActivationPrompt.js +18 -0
  119. package/dist/web/panelAudit.js +273 -0
  120. package/dist/web/panelLifecycle.js +222 -0
  121. package/dist/web/panelMediaPolicy.js +43 -0
  122. package/dist/web/panelPathPolicy.js +63 -0
  123. package/dist/web/panelPreviews.js +175 -0
  124. package/dist/web/panelQueryHandles.js +2749 -0
  125. package/dist/web/panelRoutes.js +2147 -0
  126. package/dist/web/panels.js +904 -0
  127. package/dist/web/peerInboxAggregates.js +1247 -0
  128. package/dist/web/planApprovalState.js +92 -0
  129. package/dist/web/platformAgentScheduler.js +66 -0
  130. package/dist/web/proactiveOpportunities.js +452 -0
  131. package/dist/web/promptContextSections.js +242 -0
  132. package/dist/web/promptHistorySanitizer.js +26 -0
  133. package/dist/web/promptSlashCommands.js +158 -0
  134. package/dist/web/rollingConversationSummary.js +453 -0
  135. package/dist/web/routeHelpers.js +11 -0
  136. package/dist/web/routes/handoff.js +288 -0
  137. package/dist/web/routes/history.js +345 -0
  138. package/dist/web/routes/memory.js +258 -0
  139. package/dist/web/routes/selfState.js +171 -0
  140. package/dist/web/routes/workspace.js +154 -0
  141. package/dist/web/runSurfaceWatermarks.js +431 -0
  142. package/dist/web/runtimeCapabilities.js +48 -0
  143. package/dist/web/sameAgentHandoffs.js +494 -0
  144. package/dist/web/server.js +15567 -0
  145. package/dist/web/sharedCollaborationCapsules.js +163 -0
  146. package/dist/web/soloSessionRelay.js +42 -0
  147. package/dist/web/soloWsHandler.js +138 -0
  148. package/dist/web/suggestedPlannerScheduler.js +56 -0
  149. package/dist/web/surfaceActivationPolicy.js +108 -0
  150. package/dist/web/surfaceCollaborators.js +61 -0
  151. package/dist/web/surfaceSystemStatus.js +263 -0
  152. package/dist/web/targetParticipants.js +77 -0
  153. package/dist/web/taskEvents.js +49 -0
  154. package/dist/web/taskLifecycleMessages.js +165 -0
  155. package/dist/web/taskLoops.js +732 -0
  156. package/dist/web/taskMemoryNotes.js +224 -0
  157. package/dist/web/taskNumbers.js +16 -0
  158. package/dist/web/taskOwnerGuards.js +49 -0
  159. package/dist/web/taskParticipantResolver.js +42 -0
  160. package/dist/web/taskParticipants.js +97 -0
  161. package/dist/web/taskSourceDetails.js +20 -0
  162. package/dist/web/taskStateViews.js +210 -0
  163. package/dist/web/taskStatusTransitions.js +9 -0
  164. package/dist/web/taskThreadFollowups.js +599 -0
  165. package/dist/web/taskThreadRuntimeClosure.js +685 -0
  166. package/dist/web/taskUpdateDelivery.js +104 -0
  167. package/dist/web/threadReplyContentHeuristics.js +30 -0
  168. package/dist/web/threadRoots.js +61 -0
  169. package/dist/web/threadTaskBindings.js +365 -0
  170. package/dist/web/uiPanelPromptGuidance.js +27 -0
  171. package/dist/web/workspaceMemoryHints.js +143 -0
  172. package/dist/web/workspaceToolPromptGuidance.js +30 -0
  173. package/dist/web/wsHandler.js +397 -0
  174. package/dist/web/wsSink.js +116 -0
  175. package/package.json +54 -0
@@ -0,0 +1,146 @@
1
+ import { resolveWorkspaceToolPublishIdentityState, } from './workspaceToolPromotionState.js';
2
+ export class WorkspaceToolPublishIdentityResolver {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ resolve(params) {
8
+ const promotionLink = params.sourceConversationId
9
+ ? this.loadPanelPromotionByConversation(params.sourceConversationId)
10
+ : null;
11
+ const existingByMaintenanceConversation = params.sourceConversationId
12
+ ? this.loadWorkspaceToolIdentityByMaintenanceConversation(params.userId, params.agentId, params.sourceConversationId)
13
+ : undefined;
14
+ const linkedTool = promotionLink?.toolId
15
+ ? this.loadLiveWorkspaceToolIdentityByToolId(promotionLink.toolId)
16
+ : undefined;
17
+ const sourcePanelId = promotionLink?.panelId
18
+ ?? linkedTool?.sourcePanelId
19
+ ?? existingByMaintenanceConversation?.sourcePanelId
20
+ ?? null;
21
+ const existingByPanelId = sourcePanelId
22
+ ? this.loadWorkspaceToolIdentityByPanelId(params.userId, params.agentId, sourcePanelId)
23
+ : undefined;
24
+ const collaboratorSlug = this.loadWorkspaceToolIdentityByCollaboratorSlug(params.userId, params.agentId, params.slug);
25
+ const existingByCollaboratorSlug = collaboratorSlug.row;
26
+ const existingBySlug = this.loadOwnedWorkspaceToolIdentityBySlug(params.userId, params.agentId, params.slug);
27
+ const publishState = resolveWorkspaceToolPublishIdentityState({
28
+ promotionLink,
29
+ linkedTool,
30
+ existingByMaintenanceConversation,
31
+ existingByPanelId,
32
+ existingByCollaboratorSlug,
33
+ existingBySlug,
34
+ allowCreateNewTool: params.allowCreateNewTool,
35
+ });
36
+ return {
37
+ promotionLink,
38
+ existingByMaintenanceConversation,
39
+ existingByPanelId,
40
+ existingByCollaboratorSlug,
41
+ existingBySlug,
42
+ linkedTool,
43
+ sourcePanelId,
44
+ loadConflict: collaboratorSlug.conflict,
45
+ publishState,
46
+ };
47
+ }
48
+ loadPanelPromotionByConversation(conversationId) {
49
+ return this.db.prepare(`SELECT ptp.panel_id as panelId,
50
+ ptp.user_id as userId,
51
+ ptp.agent_id as agentId,
52
+ ptp.promotion_conversation_id as promotionConversationId,
53
+ ptp.tool_id as linkedToolId,
54
+ wt.deleted_at as linkedToolDeletedAt,
55
+ CASE WHEN wt.deleted_at IS NULL THEN wt.tool_id ELSE NULL END as toolId,
56
+ CASE WHEN wt.deleted_at IS NULL THEN wt.name ELSE NULL END as toolName,
57
+ CASE WHEN wt.deleted_at IS NULL THEN wt.revision ELSE NULL END as toolRevision,
58
+ CASE WHEN wt.deleted_at IS NULL THEN wt.maintenance_conversation_id ELSE NULL END as maintenanceConversationId,
59
+ ptp.created_at as createdAt,
60
+ ptp.updated_at as updatedAt
61
+ FROM panel_tool_promotions ptp
62
+ LEFT JOIN workspace_tools wt
63
+ ON wt.tool_id = ptp.tool_id
64
+ WHERE ptp.promotion_conversation_id = ?
65
+ OR wt.maintenance_conversation_id = ?
66
+ LIMIT 1`).get(conversationId, conversationId) ?? null;
67
+ }
68
+ loadWorkspaceToolIdentityByMaintenanceConversation(userId, agentId, maintenanceConversationId) {
69
+ return this.db.prepare(`${workspaceToolIdentitySelectSql()}
70
+ WHERE user_id = ?
71
+ AND agent_id = ?
72
+ AND maintenance_conversation_id = ?
73
+ ORDER BY deleted_at IS NULL DESC, updated_at DESC
74
+ LIMIT 1`).get(userId, agentId, maintenanceConversationId);
75
+ }
76
+ loadLiveWorkspaceToolIdentityByToolId(toolId) {
77
+ return this.db.prepare(`${workspaceToolIdentitySelectSql()}
78
+ WHERE tool_id = ?
79
+ AND deleted_at IS NULL
80
+ LIMIT 1`).get(toolId);
81
+ }
82
+ loadWorkspaceToolIdentityByPanelId(userId, agentId, panelId) {
83
+ return this.db.prepare(`${workspaceToolIdentitySelectSql()}
84
+ WHERE user_id = ?
85
+ AND agent_id = ?
86
+ AND source_panel_id = ?
87
+ ORDER BY deleted_at IS NULL DESC, updated_at DESC
88
+ LIMIT 1`).get(userId, agentId, panelId);
89
+ }
90
+ loadWorkspaceToolIdentityByCollaboratorSlug(userId, agentId, slug) {
91
+ const rows = this.db.prepare(`SELECT wt.tool_id as toolId,
92
+ wt.user_id as userId,
93
+ wt.agent_id as agentId,
94
+ wt.source_panel_id as sourcePanelId,
95
+ wt.panel_id as panelId,
96
+ wt.slug,
97
+ wt.active_terminal_id as activeTerminalId,
98
+ wt.maintenance_conversation_id as maintenanceConversationId,
99
+ wt.revision,
100
+ wt.deleted_at as deletedAt
101
+ FROM workspace_tools wt
102
+ JOIN surface_collaborators sc
103
+ ON sc.surface_type = 'tool'
104
+ AND sc.surface_id = wt.tool_id
105
+ AND sc.agent_id = ?
106
+ WHERE wt.user_id = ?
107
+ AND wt.slug = ?
108
+ AND wt.agent_id != ?
109
+ AND wt.deleted_at IS NULL
110
+ ORDER BY wt.updated_at DESC, wt.created_at DESC`).all(agentId, userId, slug, agentId);
111
+ if (rows.length > 1) {
112
+ return {
113
+ row: null,
114
+ conflict: {
115
+ kind: 'ambiguous_collaborator_slug',
116
+ message: `Multiple tools named "${slug}" list this agent as a maintainer; publish from the owner maintenance chat to disambiguate.`,
117
+ statusCode: 409,
118
+ },
119
+ };
120
+ }
121
+ return {
122
+ row: rows[0] ?? null,
123
+ conflict: null,
124
+ };
125
+ }
126
+ loadOwnedWorkspaceToolIdentityBySlug(userId, agentId, slug) {
127
+ return this.db.prepare(`${workspaceToolIdentitySelectSql()}
128
+ WHERE user_id = ?
129
+ AND agent_id = ?
130
+ AND slug = ?
131
+ LIMIT 1`).get(userId, agentId, slug);
132
+ }
133
+ }
134
+ function workspaceToolIdentitySelectSql() {
135
+ return `SELECT tool_id as toolId,
136
+ user_id as userId,
137
+ agent_id as agentId,
138
+ source_panel_id as sourcePanelId,
139
+ panel_id as panelId,
140
+ slug,
141
+ active_terminal_id as activeTerminalId,
142
+ maintenance_conversation_id as maintenanceConversationId,
143
+ revision,
144
+ deleted_at as deletedAt
145
+ FROM workspace_tools`;
146
+ }
@@ -0,0 +1,378 @@
1
+ import { getUserAgentAccess, getUserChannelAccess } from './auth.js';
2
+ import { parseStateRowJson, runRowToInfo, } from './workspaceToolExecutionUtils.js';
3
+ import { parseWorkspaceToolManifest, resolveWorkspaceToolActionMode, sanitizeWorkspaceToolManifestForViewer, } from './workspaceToolManifest.js';
4
+ import { deriveWorkspaceToolBusinessState, deriveWorkspaceToolRuntimeState, } from './workspaceToolPanelProjection.js';
5
+ const WORKSPACE_TOOL_ROW_SELECT = `
6
+ wt.tool_id as toolId,
7
+ wt.user_id as userId,
8
+ wt.agent_id as agentId,
9
+ a.name as agentName,
10
+ a.node_id as agentNodeId,
11
+ a.workspace_path as agentWorkspacePath,
12
+ a.deleted_at as agentDeletedAt,
13
+ n.status as agentNodeStatus,
14
+ wt.source_panel_id as sourcePanelId,
15
+ wt.panel_id as panelId,
16
+ wt.slug,
17
+ wt.name,
18
+ wt.description,
19
+ wt.icon,
20
+ wt.scope,
21
+ wt.scope_channel_id as scopeChannelId,
22
+ wt.allow_shared_exec as allowSharedExec,
23
+ wt.public_review_status as publicReviewStatus,
24
+ wt.pending_public_review as pendingPublicReview,
25
+ wt.manifest_path as manifestPath,
26
+ wt.bundle_root as bundleRoot,
27
+ wt.manifest_json as manifestJson,
28
+ wt.latest_state_json as latestStateJson,
29
+ wt.active_terminal_id as activeTerminalId,
30
+ wt.maintenance_conversation_id as maintenanceConversationId,
31
+ wt.source_conversation_id as sourceConversationId,
32
+ wt.source_message_id as sourceMessageId,
33
+ wt.cloned_from_tool_id as clonedFromToolId,
34
+ wt.cloned_from_revision as clonedFromRevision,
35
+ wt.revision,
36
+ wt.created_at as createdAt,
37
+ wt.updated_at as updatedAt,
38
+ wt.deleted_at as deletedAt
39
+ `;
40
+ function parseVerificationDetails(raw) {
41
+ if (!raw)
42
+ return null;
43
+ try {
44
+ const parsed = JSON.parse(raw);
45
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
46
+ ? parsed
47
+ : null;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ export function workspaceToolRunRowToInfoForViewer(row, actionKind, params) {
54
+ const info = runRowToInfo(row, actionKind);
55
+ const canViewOwnerFields = params.canViewOwnerFields === true || params.viewerUserId === params.ownerUserId;
56
+ const canViewRunInvocation = canViewOwnerFields || params.viewerUserId === (row.requestedByUserId ?? null);
57
+ const visibleInfo = canViewOwnerFields ? info : { ...info, executionWorkspacePath: null };
58
+ if (canViewRunInvocation) {
59
+ return visibleInfo;
60
+ }
61
+ return {
62
+ ...visibleInfo,
63
+ terminalId: null,
64
+ params: null,
65
+ requestedByUserId: null,
66
+ requestedByUsername: null,
67
+ requestedByAgentId: null,
68
+ requestedByAgentName: null,
69
+ };
70
+ }
71
+ export class WorkspaceToolReadModel {
72
+ db;
73
+ constructor(db) {
74
+ this.db = db;
75
+ }
76
+ loadUsername(userId) {
77
+ const row = this.db.prepare(`SELECT username
78
+ FROM users
79
+ WHERE id = ?`).get(userId);
80
+ return row?.username ?? null;
81
+ }
82
+ isAdminUser(userId) {
83
+ const row = this.db.prepare(`SELECT is_admin as isAdmin
84
+ FROM users
85
+ WHERE id = ?`).get(userId);
86
+ return row?.isAdmin === 1;
87
+ }
88
+ loadToolRowForUser(userId, toolId) {
89
+ return this.db.prepare(`SELECT ${WORKSPACE_TOOL_ROW_SELECT}
90
+ FROM workspace_tools wt
91
+ LEFT JOIN agents a ON a.agent_id = wt.agent_id
92
+ LEFT JOIN nodes n ON n.node_id = a.node_id
93
+ WHERE wt.tool_id = ?
94
+ AND wt.user_id = ?
95
+ AND wt.deleted_at IS NULL
96
+ LIMIT 1`).get(toolId, userId) ?? null;
97
+ }
98
+ loadToolRowById(toolId) {
99
+ return this.db.prepare(`SELECT ${WORKSPACE_TOOL_ROW_SELECT}
100
+ FROM workspace_tools wt
101
+ LEFT JOIN agents a ON a.agent_id = wt.agent_id
102
+ LEFT JOIN nodes n ON n.node_id = a.node_id
103
+ WHERE wt.tool_id = ?
104
+ AND wt.deleted_at IS NULL
105
+ LIMIT 1`).get(toolId) ?? null;
106
+ }
107
+ loadToolRowForViewer(userId, toolId, options) {
108
+ if (options?.allowAdminBypass && this.isAdminUser(userId)) {
109
+ return this.loadToolRowById(toolId);
110
+ }
111
+ const allowedChannelIds = getUserChannelAccess(this.db, userId);
112
+ const rows = this.loadVisibleToolRowsForViewer(userId, {
113
+ toolIds: [toolId],
114
+ allowedChannelIds,
115
+ });
116
+ return rows[0] ?? null;
117
+ }
118
+ loadToolRowByPanelIdForViewer(userId, panelId) {
119
+ if (this.isAdminUser(userId)) {
120
+ return this.db.prepare(`SELECT ${WORKSPACE_TOOL_ROW_SELECT}
121
+ FROM workspace_tools wt
122
+ LEFT JOIN agents a ON a.agent_id = wt.agent_id
123
+ LEFT JOIN nodes n ON n.node_id = a.node_id
124
+ WHERE wt.panel_id = ?
125
+ AND wt.deleted_at IS NULL
126
+ LIMIT 1`).get(panelId) ?? null;
127
+ }
128
+ const allowedChannelIds = getUserChannelAccess(this.db, userId);
129
+ const rows = this.loadVisibleToolRowsForViewer(userId, {
130
+ panelIds: [panelId],
131
+ allowedChannelIds,
132
+ });
133
+ return rows[0] ?? null;
134
+ }
135
+ loadVisibleToolRowsForViewer(userId, options) {
136
+ const allowedChannelIds = options?.allowedChannelIds ?? getUserChannelAccess(this.db, userId);
137
+ const conditions = ['wt.deleted_at IS NULL'];
138
+ const params = [];
139
+ if (options?.toolIds?.length) {
140
+ const normalizedToolIds = [...new Set(options.toolIds.map((toolId) => toolId.trim()).filter(Boolean))];
141
+ if (normalizedToolIds.length === 0)
142
+ return [];
143
+ conditions.push(`wt.tool_id IN (${normalizedToolIds.map(() => '?').join(', ')})`);
144
+ params.push(...normalizedToolIds);
145
+ }
146
+ if (options?.panelIds?.length) {
147
+ const normalizedPanelIds = [...new Set(options.panelIds.map((panelId) => panelId.trim()).filter(Boolean))];
148
+ if (normalizedPanelIds.length === 0)
149
+ return [];
150
+ conditions.push(`wt.panel_id IN (${normalizedPanelIds.map(() => '?').join(', ')})`);
151
+ params.push(...normalizedPanelIds);
152
+ }
153
+ const visibilityClauses = [
154
+ 'wt.user_id = ?',
155
+ `(wt.scope = 'public' AND wt.public_review_status = 'approved' AND wt.pending_public_review = 0)`,
156
+ ];
157
+ const visibilityParams = [userId];
158
+ if (allowedChannelIds.length > 0) {
159
+ visibilityClauses.push(`(wt.scope = 'channel' AND wt.scope_channel_id IN (${allowedChannelIds.map(() => '?').join(', ')}))`);
160
+ visibilityParams.push(...allowedChannelIds);
161
+ }
162
+ conditions.push(`(${visibilityClauses.join(' OR ')})`);
163
+ return this.db.prepare(`SELECT ${WORKSPACE_TOOL_ROW_SELECT}
164
+ FROM workspace_tools wt
165
+ LEFT JOIN agents a ON a.agent_id = wt.agent_id
166
+ LEFT JOIN nodes n ON n.node_id = a.node_id
167
+ WHERE ${conditions.join(' AND ')}
168
+ ORDER BY wt.updated_at DESC, wt.created_at DESC`).all(...params, ...visibilityParams);
169
+ }
170
+ loadPublicReviewToolRows(status = 'pending_review') {
171
+ const statusCondition = status === 'pending_review'
172
+ ? 'wt.pending_public_review = 1'
173
+ : 'wt.pending_public_review = 0 AND wt.public_review_status = ?';
174
+ return this.db.prepare(`SELECT ${WORKSPACE_TOOL_ROW_SELECT}
175
+ FROM workspace_tools wt
176
+ LEFT JOIN agents a ON a.agent_id = wt.agent_id
177
+ LEFT JOIN nodes n ON n.node_id = a.node_id
178
+ WHERE wt.deleted_at IS NULL
179
+ AND ${statusCondition}
180
+ ORDER BY wt.updated_at DESC, wt.created_at DESC`).all(...(status === 'pending_review' ? [] : [status]));
181
+ }
182
+ canManageToolCollaborators(userId, row) {
183
+ return row.userId === userId || this.isAdminUser(userId);
184
+ }
185
+ canManageToolForViewer(userId, row) {
186
+ if (row.userId === userId || this.isAdminUser(userId))
187
+ return true;
188
+ return this.userCanManageToolViaMaintainerAgent(userId, row.toolId);
189
+ }
190
+ rowToToolInfo(row) {
191
+ return this.rowToToolInfoForViewer(row, row.userId);
192
+ }
193
+ rowToToolInfoForViewer(row, viewerUserId) {
194
+ const manifest = parseWorkspaceToolManifest(row.manifestJson);
195
+ const isOwner = row.userId === viewerUserId;
196
+ const canManage = this.canManageToolForViewer(viewerUserId, row);
197
+ const canViewOwnerFields = isOwner || this.isAdminUser(viewerUserId);
198
+ const lastRunRow = this.db.prepare(`SELECT run_id as runId,
199
+ wtr.tool_id as toolId,
200
+ action_id as actionId,
201
+ terminal_id as terminalId,
202
+ status,
203
+ params_json as paramsJson,
204
+ exit_code as exitCode,
205
+ signal,
206
+ started_at as startedAt,
207
+ ended_at as endedAt,
208
+ requested_by_user_id as requestedByUserId,
209
+ u.username as requestedByUsername,
210
+ requested_by_agent_id as requestedByAgentId,
211
+ a.name as requestedByAgentName,
212
+ execution_target_kind as executionTargetKind,
213
+ execution_agent_id as executionAgentId,
214
+ execution_node_id as executionNodeId,
215
+ execution_hostname as executionHostname,
216
+ execution_workspace_path as executionWorkspacePath
217
+ FROM workspace_tool_runs wtr
218
+ LEFT JOIN users u ON u.id = wtr.requested_by_user_id
219
+ LEFT JOIN agents a ON a.agent_id = wtr.requested_by_agent_id
220
+ WHERE wtr.tool_id = ?
221
+ ORDER BY started_at DESC
222
+ LIMIT 1`).get(row.toolId);
223
+ const latestState = parseStateRowJson(row.latestStateJson);
224
+ const verification = this.loadLatestVerificationForTool(row.toolId, row.revision);
225
+ const runtimeState = deriveWorkspaceToolRuntimeState({
226
+ activeTerminalId: row.activeTerminalId,
227
+ lastRunStatus: lastRunRow?.status ?? null,
228
+ });
229
+ return {
230
+ toolId: row.toolId,
231
+ userId: row.userId,
232
+ agentId: row.agentId,
233
+ agentName: row.agentName ?? null,
234
+ agentNodeId: row.agentNodeId ?? null,
235
+ agentHealth: {
236
+ isOnline: row.agentNodeStatus === 'online',
237
+ isDeleted: row.agentDeletedAt != null,
238
+ hasNode: Boolean(row.agentNodeId?.trim()),
239
+ hasWorkspace: Boolean(row.agentWorkspacePath?.trim()),
240
+ },
241
+ sourcePanelId: row.sourcePanelId ?? null,
242
+ panelId: row.panelId ?? null,
243
+ slug: row.slug,
244
+ name: row.name,
245
+ description: row.description,
246
+ icon: row.icon ?? null,
247
+ scope: row.scope,
248
+ scopeChannelId: row.scopeChannelId ?? null,
249
+ allowSharedExec: row.allowSharedExec === 1,
250
+ publicReviewStatus: row.publicReviewStatus,
251
+ pendingPublicReview: row.pendingPublicReview === 1,
252
+ isOwner,
253
+ canManage,
254
+ manifestPath: canViewOwnerFields ? row.manifestPath : null,
255
+ bundleRoot: canViewOwnerFields ? row.bundleRoot : null,
256
+ backendRuntime: manifest.actions.length === 0 ? 'none' : manifest.cli?.entry?.trim() ? 'cli' : 'command',
257
+ actionBackendRuntimes: Object.fromEntries(manifest.actions.map((action) => [
258
+ action.actionId,
259
+ resolveWorkspaceToolActionMode(action) === 'notify_agent'
260
+ ? 'none'
261
+ : action.command?.trim()
262
+ ? 'command'
263
+ : manifest.cli?.entry?.trim()
264
+ ? 'cli'
265
+ : action.kind === 'stop'
266
+ ? 'none'
267
+ : 'command',
268
+ ])),
269
+ manifest: sanitizeWorkspaceToolManifestForViewer(manifest, {
270
+ scope: row.scope,
271
+ scopeChannelId: row.scopeChannelId ?? null,
272
+ allowSharedExec: row.allowSharedExec === 1,
273
+ isOwner: canViewOwnerFields,
274
+ }),
275
+ latestState,
276
+ businessState: deriveWorkspaceToolBusinessState(latestState),
277
+ runtimeState,
278
+ isRunning: row.activeTerminalId != null,
279
+ activeTerminalId: canViewOwnerFields ? (row.activeTerminalId ?? null) : null,
280
+ maintenanceConversationId: canViewOwnerFields ? (row.maintenanceConversationId ?? null) : null,
281
+ sourceConversationId: canViewOwnerFields ? (row.sourceConversationId ?? null) : null,
282
+ sourceMessageId: canViewOwnerFields ? (row.sourceMessageId ?? null) : null,
283
+ clonedFromToolId: canViewOwnerFields ? (row.clonedFromToolId ?? null) : null,
284
+ clonedFromRevision: canViewOwnerFields ? (row.clonedFromRevision ?? null) : null,
285
+ verification: verification
286
+ ? {
287
+ verificationId: verification.verificationId,
288
+ toolId: verification.toolId,
289
+ toolRevision: verification.toolRevision,
290
+ sourceToolId: canViewOwnerFields ? verification.sourceToolId : null,
291
+ sourceRevision: canViewOwnerFields ? verification.sourceRevision : null,
292
+ targetAgentId: verification.targetAgentId,
293
+ targetNodeId: verification.targetNodeId,
294
+ targetWorkspacePath: canViewOwnerFields ? verification.targetWorkspacePath : null,
295
+ conversationId: canViewOwnerFields ? verification.conversationId : null,
296
+ status: verification.status,
297
+ summary: verification.summary,
298
+ details: canViewOwnerFields ? parseVerificationDetails(verification.detailsJson) : null,
299
+ smokeRunId: canViewOwnerFields ? verification.smokeRunId : null,
300
+ startedAt: verification.startedAt,
301
+ endedAt: verification.endedAt,
302
+ updatedAt: verification.updatedAt,
303
+ }
304
+ : null,
305
+ revision: row.revision,
306
+ createdAt: row.createdAt,
307
+ updatedAt: row.updatedAt,
308
+ deletedAt: row.deletedAt ?? null,
309
+ lastRun: lastRunRow
310
+ ? workspaceToolRunRowToInfoForViewer(lastRunRow, manifest.actions.find((action) => action.actionId === lastRunRow.actionId)?.kind, {
311
+ viewerUserId,
312
+ ownerUserId: row.userId,
313
+ canViewOwnerFields,
314
+ })
315
+ : null,
316
+ };
317
+ }
318
+ loadLatestVerificationForTool(toolId, toolRevision) {
319
+ return this.db.prepare(`SELECT verification_id as verificationId,
320
+ tool_id as toolId,
321
+ tool_revision as toolRevision,
322
+ source_tool_id as sourceToolId,
323
+ source_revision as sourceRevision,
324
+ target_agent_id as targetAgentId,
325
+ target_node_id as targetNodeId,
326
+ target_workspace_path as targetWorkspacePath,
327
+ conversation_id as conversationId,
328
+ status,
329
+ summary,
330
+ details_json as detailsJson,
331
+ smoke_run_id as smokeRunId,
332
+ started_at as startedAt,
333
+ ended_at as endedAt,
334
+ updated_at as updatedAt
335
+ FROM workspace_tool_verifications
336
+ WHERE tool_id = ?
337
+ AND tool_revision = ?
338
+ ORDER BY updated_at DESC, started_at DESC
339
+ LIMIT 1`).get(toolId, toolRevision) ?? null;
340
+ }
341
+ loadVerificationByConversation(conversationId) {
342
+ return this.db.prepare(`SELECT verification_id as verificationId,
343
+ tool_id as toolId,
344
+ tool_revision as toolRevision,
345
+ source_tool_id as sourceToolId,
346
+ source_revision as sourceRevision,
347
+ target_agent_id as targetAgentId,
348
+ target_node_id as targetNodeId,
349
+ target_workspace_path as targetWorkspacePath,
350
+ conversation_id as conversationId,
351
+ status,
352
+ summary,
353
+ details_json as detailsJson,
354
+ smoke_run_id as smokeRunId,
355
+ started_at as startedAt,
356
+ ended_at as endedAt,
357
+ updated_at as updatedAt
358
+ FROM workspace_tool_verifications
359
+ WHERE conversation_id = ?
360
+ ORDER BY updated_at DESC, started_at DESC
361
+ LIMIT 1`).get(conversationId) ?? null;
362
+ }
363
+ userCanManageToolViaMaintainerAgent(userId, toolId) {
364
+ const accessibleAgentIds = getUserAgentAccess(this.db, userId)
365
+ .map((agentId) => agentId.trim())
366
+ .filter(Boolean);
367
+ if (accessibleAgentIds.length === 0)
368
+ return false;
369
+ const row = this.db.prepare(`SELECT 1 as present
370
+ FROM surface_collaborators
371
+ WHERE surface_type = 'tool'
372
+ AND surface_id = ?
373
+ AND role = 'collaborator'
374
+ AND agent_id IN (${accessibleAgentIds.map(() => '?').join(', ')})
375
+ LIMIT 1`).get(toolId, ...accessibleAgentIds);
376
+ return Boolean(row);
377
+ }
378
+ }