@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,258 @@
1
+ import { MemoryServiceError, createMemoryEdge, createMemoryNode, deleteMemoryEdge, deleteMemoryNode, getMemoryGraph, listMemoryEdges, listMemoryNodes, updateDreamWatermark, updateMemoryNode, } from '../../services/memoryService.js';
2
+ const MEMORY_NETWORK_DISABLED_ERROR = 'Memory Network is not enabled for this agent';
3
+ function accessMemoryNetwork(conversationManager, agentId, notFoundError = 'Agent not found') {
4
+ const agent = conversationManager.getAgent(agentId);
5
+ if (!agent) {
6
+ return { allowed: false, statusCode: 404, body: { error: notFoundError } };
7
+ }
8
+ if (!agent.memoryNetworkEnabled) {
9
+ return { allowed: false, statusCode: 403, body: { error: MEMORY_NETWORK_DISABLED_ERROR } };
10
+ }
11
+ return { allowed: true, agent };
12
+ }
13
+ function denyMemoryNetworkAccess(reply, access) {
14
+ reply.code(access.statusCode);
15
+ return access.body;
16
+ }
17
+ function handleMemoryError(reply, error) {
18
+ if (error instanceof MemoryServiceError) {
19
+ reply.code(error.statusCode);
20
+ return { error: error.message };
21
+ }
22
+ reply.code(500);
23
+ return { error: String(error?.message ?? error) };
24
+ }
25
+ export function registerAgentMemoryInternalRoutes(app, db, conversationManager) {
26
+ app.post('/api/internal/agent/:agentId/memory/nodes', async (req, reply) => {
27
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
28
+ if (!access.allowed)
29
+ return denyMemoryNetworkAccess(reply, access);
30
+ const body = req.body ?? {};
31
+ if (!body.topic?.trim() || !body.summary?.trim() || !body.category?.trim()) {
32
+ reply.code(400);
33
+ return { error: 'topic, summary, and category are required' };
34
+ }
35
+ try {
36
+ return createMemoryNode(db, {
37
+ agentId: req.params.agentId,
38
+ topic: body.topic,
39
+ summary: body.summary,
40
+ category: body.category,
41
+ importance: body.importance,
42
+ sourceMessageId: body.sourceMessageId,
43
+ sourceSnippet: body.sourceSnippet,
44
+ status: body.status,
45
+ });
46
+ }
47
+ catch (error) {
48
+ return handleMemoryError(reply, error);
49
+ }
50
+ });
51
+ app.get('/api/internal/agent/:agentId/memory/nodes', async (req, reply) => {
52
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
53
+ if (!access.allowed)
54
+ return denyMemoryNetworkAccess(reply, access);
55
+ const since = req.query.since ? Number(req.query.since) : undefined;
56
+ return {
57
+ nodes: listMemoryNodes(db, {
58
+ agentId: req.params.agentId,
59
+ category: req.query.category,
60
+ status: req.query.status,
61
+ since: Number.isFinite(since) ? since : undefined,
62
+ query: req.query.query,
63
+ }),
64
+ };
65
+ });
66
+ app.patch('/api/internal/agent/:agentId/memory/nodes/:nodeId', async (req, reply) => {
67
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
68
+ if (!access.allowed)
69
+ return denyMemoryNetworkAccess(reply, access);
70
+ try {
71
+ return updateMemoryNode(db, req.params.agentId, req.params.nodeId, req.body ?? {});
72
+ }
73
+ catch (error) {
74
+ return handleMemoryError(reply, error);
75
+ }
76
+ });
77
+ app.delete('/api/internal/agent/:agentId/memory/nodes/:nodeId', async (req, reply) => {
78
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
79
+ if (!access.allowed)
80
+ return denyMemoryNetworkAccess(reply, access);
81
+ const deleted = deleteMemoryNode(db, req.params.agentId, req.params.nodeId);
82
+ if (!deleted) {
83
+ reply.code(404);
84
+ return { error: 'Memory node not found' };
85
+ }
86
+ return { ok: true };
87
+ });
88
+ app.post('/api/internal/agent/:agentId/memory/edges', async (req, reply) => {
89
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
90
+ if (!access.allowed)
91
+ return denyMemoryNetworkAccess(reply, access);
92
+ const body = req.body ?? {};
93
+ if (!body.sourceNodeId?.trim() || !body.targetNodeId?.trim() || !body.relation?.trim()) {
94
+ reply.code(400);
95
+ return { error: 'sourceNodeId, targetNodeId, and relation are required' };
96
+ }
97
+ try {
98
+ return createMemoryEdge(db, {
99
+ agentId: req.params.agentId,
100
+ sourceNodeId: body.sourceNodeId,
101
+ targetNodeId: body.targetNodeId,
102
+ relation: body.relation,
103
+ strength: body.strength,
104
+ });
105
+ }
106
+ catch (error) {
107
+ return handleMemoryError(reply, error);
108
+ }
109
+ });
110
+ app.get('/api/internal/agent/:agentId/memory/edges', async (req, reply) => {
111
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
112
+ if (!access.allowed)
113
+ return denyMemoryNetworkAccess(reply, access);
114
+ return {
115
+ edges: listMemoryEdges(db, {
116
+ agentId: req.params.agentId,
117
+ nodeId: req.query.node,
118
+ }),
119
+ };
120
+ });
121
+ app.delete('/api/internal/agent/:agentId/memory/edges/:edgeId', async (req, reply) => {
122
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
123
+ if (!access.allowed)
124
+ return denyMemoryNetworkAccess(reply, access);
125
+ const deleted = deleteMemoryEdge(db, req.params.agentId, req.params.edgeId);
126
+ if (!deleted) {
127
+ reply.code(404);
128
+ return { error: 'Memory edge not found' };
129
+ }
130
+ return { ok: true };
131
+ });
132
+ app.get('/api/internal/agent/:agentId/memory/graph', async (req, reply) => {
133
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
134
+ if (!access.allowed)
135
+ return denyMemoryNetworkAccess(reply, access);
136
+ return getMemoryGraph(db, req.params.agentId);
137
+ });
138
+ app.post('/api/internal/agent/:agentId/memory/dream-watermark', async (req, reply) => {
139
+ const seq = req.body?.seq;
140
+ if (seq == null || !Number.isFinite(seq)) {
141
+ reply.code(400);
142
+ return { error: 'seq is required' };
143
+ }
144
+ const access = accessMemoryNetwork(conversationManager, req.params.agentId);
145
+ if (!access.allowed)
146
+ return denyMemoryNetworkAccess(reply, access);
147
+ const agent = access.agent;
148
+ const runId = typeof req.body?.runId === 'string' ? req.body.runId.trim() : '';
149
+ let activitySince = agent.lastDreamAt ?? (Date.now() - 30 * 60 * 1000);
150
+ if (runId) {
151
+ const run = db.prepare(`SELECT r.started_at as startedAt
152
+ FROM runs r
153
+ JOIN run_debug_inputs rdi ON rdi.run_id = r.run_id
154
+ JOIN conversations c ON c.session_key = r.session_key
155
+ WHERE r.run_id = ?
156
+ AND c.agent_id = ?
157
+ AND rdi.dispatch_mode = 'dream'
158
+ LIMIT 1`).get(runId, req.params.agentId);
159
+ if (!run) {
160
+ reply.code(404);
161
+ return { error: 'Dream run not found for agent' };
162
+ }
163
+ if (run?.startedAt != null) {
164
+ activitySince = run.startedAt;
165
+ }
166
+ }
167
+ try {
168
+ return updateDreamWatermark(db, req.params.agentId, seq, {
169
+ requireMemoryActivitySince: activitySince,
170
+ });
171
+ }
172
+ catch (error) {
173
+ return handleMemoryError(reply, error);
174
+ }
175
+ });
176
+ }
177
+ export function registerDreamDispatchRoute(app, conversationManager, requireAdmin) {
178
+ app.post('/api/agents/:id/dream/dispatch', async (req, reply) => {
179
+ if (!requireAdmin(req, reply))
180
+ return { error: 'Admin access required' };
181
+ const agent = conversationManager.getAgent(req.params.id);
182
+ if (!agent) {
183
+ reply.code(404);
184
+ return { error: 'Agent not found' };
185
+ }
186
+ if (!agent.memoryNetworkEnabled) {
187
+ reply.code(403);
188
+ return { error: 'Memory Network is not enabled for this agent' };
189
+ }
190
+ if (!agent.dreamEnabled) {
191
+ reply.code(403);
192
+ return { error: 'Dream is not enabled for this agent' };
193
+ }
194
+ if (!agent.nodeId) {
195
+ reply.code(409);
196
+ return { error: 'Agent has no node' };
197
+ }
198
+ try {
199
+ return await conversationManager.dispatchDream(req.params.id);
200
+ }
201
+ catch (error) {
202
+ reply.code(409);
203
+ return { error: String(error?.message ?? error) };
204
+ }
205
+ });
206
+ }
207
+ export function registerAgentMemoryPublicRoutes(app, db, conversationManager, requireAgentAccess) {
208
+ app.get('/api/agents/:id/memory/nodes', async (req, reply) => {
209
+ const access = accessMemoryNetwork(conversationManager, req.params.id, 'Not found');
210
+ if (!access.allowed)
211
+ return denyMemoryNetworkAccess(reply, access);
212
+ if (!requireAgentAccess(req, reply, req.params.id))
213
+ return { error: 'Access denied' };
214
+ return {
215
+ nodes: listMemoryNodes(db, {
216
+ agentId: req.params.id,
217
+ category: req.query.category,
218
+ status: req.query.status,
219
+ query: req.query.query,
220
+ }),
221
+ };
222
+ });
223
+ app.get('/api/agents/:id/memory/graph', async (req, reply) => {
224
+ const access = accessMemoryNetwork(conversationManager, req.params.id, 'Not found');
225
+ if (!access.allowed)
226
+ return denyMemoryNetworkAccess(reply, access);
227
+ if (!requireAgentAccess(req, reply, req.params.id))
228
+ return { error: 'Access denied' };
229
+ return getMemoryGraph(db, req.params.id);
230
+ });
231
+ app.patch('/api/agents/:id/memory/nodes/:nodeId', async (req, reply) => {
232
+ const access = accessMemoryNetwork(conversationManager, req.params.id, 'Not found');
233
+ if (!access.allowed)
234
+ return denyMemoryNetworkAccess(reply, access);
235
+ if (!requireAgentAccess(req, reply, req.params.id))
236
+ return { error: 'Access denied' };
237
+ try {
238
+ const node = updateMemoryNode(db, req.params.id, req.params.nodeId, req.body ?? {});
239
+ return node;
240
+ }
241
+ catch (error) {
242
+ return handleMemoryError(reply, error);
243
+ }
244
+ });
245
+ app.delete('/api/agents/:id/memory/nodes/:nodeId', async (req, reply) => {
246
+ const access = accessMemoryNetwork(conversationManager, req.params.id, 'Not found');
247
+ if (!access.allowed)
248
+ return denyMemoryNetworkAccess(reply, access);
249
+ if (!requireAgentAccess(req, reply, req.params.id))
250
+ return { error: 'Access denied' };
251
+ const deleted = deleteMemoryNode(db, req.params.id, req.params.nodeId);
252
+ if (!deleted) {
253
+ reply.code(404);
254
+ return { error: 'Memory node not found' };
255
+ }
256
+ return { ok: true };
257
+ });
258
+ }
@@ -0,0 +1,171 @@
1
+ import { buildAgentGlobalSnapshot, buildAgentSelfStateReport, collectAgentSelfState } from '../agentSelfState.js';
2
+ import { buildAgentRuntimePresenceReport, collectAgentRuntimePresence } from '../agentRuntimePresence.js';
3
+ import { AgentVisibility } from '../agentVisibility.js';
4
+ import { buildConversationSummaryRecords, buildConversationSurfaceResponse, getConversationSummary, listConversationSummariesForAgent, } from '../conversationSummaries.js';
5
+ import { resolveChannelFromTarget } from '../conversationTargets.js';
6
+ import { buildContextBundle } from '../contextBundleResolver.js';
7
+ export function registerAgentSelfStateRoutes(app, db, conversationManager) {
8
+ const visibility = new AgentVisibility(db);
9
+ app.get('/api/internal/agent/:agentId/self-state', async (req, reply) => {
10
+ const { agentId } = req.params;
11
+ const agent = conversationManager.getAgent(agentId);
12
+ if (!agent) {
13
+ reply.code(404);
14
+ return { error: 'Agent not found' };
15
+ }
16
+ const conversationId = typeof req.query.conversationId === 'string' ? req.query.conversationId.trim() : '';
17
+ if (conversationId && !visibility.canSeeConversation(agent, conversationId)) {
18
+ reply.code(400);
19
+ return { error: 'conversationId does not belong to this agent' };
20
+ }
21
+ const state = collectAgentSelfState(db, agent, {
22
+ conversationId: conversationId || null,
23
+ });
24
+ const conversationSummaries = buildConversationSummaryRecords(db, agent, state.conversations);
25
+ const conversationContexts = Object.fromEntries(conversationSummaries.map((conversation) => [
26
+ conversation.conversationId,
27
+ visibility.sanitizeSummaryForSelfState(agent, conversation.summary),
28
+ ]));
29
+ const conversationSummaryById = new Map(conversationSummaries.map((conversation) => [conversation.conversationId, conversation]));
30
+ const currentContext = conversationId ? (conversationContexts[conversationId] ?? null) : null;
31
+ const conversations = state.conversations.map((conversation) => {
32
+ const summary = conversationSummaryById.get(conversation.conversationId);
33
+ return {
34
+ ...conversation,
35
+ context: conversationContexts[conversation.conversationId] ?? summary?.summary ?? null,
36
+ summaryUpdatedAt: summary?.summaryUpdatedAt ?? conversation.updatedAt,
37
+ summaryText: summary?.summaryText ?? null,
38
+ };
39
+ });
40
+ const runtimePresence = collectAgentRuntimePresence(db, conversationManager, agent, {
41
+ conversationId: conversationId || null,
42
+ });
43
+ return {
44
+ ...state,
45
+ currentContext,
46
+ conversations,
47
+ runtimePresence,
48
+ snapshotText: buildAgentGlobalSnapshot(state),
49
+ reportText: `${buildAgentSelfStateReport(state, {
50
+ currentContext,
51
+ conversationContexts,
52
+ })}
53
+
54
+ ${buildAgentRuntimePresenceReport(runtimePresence)}`,
55
+ };
56
+ });
57
+ app.get('/api/internal/agent/:agentId/runtime-presence', async (req, reply) => {
58
+ const { agentId } = req.params;
59
+ const agent = conversationManager.getAgent(agentId);
60
+ if (!agent) {
61
+ reply.code(404);
62
+ return { error: 'Agent not found' };
63
+ }
64
+ const conversationId = typeof req.query.conversationId === 'string' ? req.query.conversationId.trim() : '';
65
+ if (conversationId && !visibility.canSeeConversation(agent, conversationId)) {
66
+ reply.code(400);
67
+ return { error: 'conversationId does not belong to this agent' };
68
+ }
69
+ const presence = collectAgentRuntimePresence(db, conversationManager, agent, {
70
+ conversationId: conversationId || null,
71
+ });
72
+ return {
73
+ ...presence,
74
+ reportText: buildAgentRuntimePresenceReport(presence),
75
+ };
76
+ });
77
+ app.get('/api/internal/agent/:agentId/my-conversations', async (req, reply) => {
78
+ const { agentId } = req.params;
79
+ const agent = conversationManager.getAgent(agentId);
80
+ if (!agent) {
81
+ reply.code(404);
82
+ return { error: 'Agent not found' };
83
+ }
84
+ const requestedStatus = (req.query.status ?? 'all').trim();
85
+ if (!['all', 'idle', 'queued', 'active', 'recovering', 'awaiting_approval', 'failed'].includes(requestedStatus)) {
86
+ reply.code(400);
87
+ return { error: `Invalid status: ${req.query.status}` };
88
+ }
89
+ const conversations = listConversationSummariesForAgent(db, agent, { status: requestedStatus });
90
+ return {
91
+ conversations: conversations.map((conversation) => buildConversationSurfaceResponse(conversation, {
92
+ contextKey: 'context',
93
+ })),
94
+ };
95
+ });
96
+ app.get('/api/internal/agent/:agentId/conversation-summary', async (req, reply) => {
97
+ const { agentId } = req.params;
98
+ const agent = conversationManager.getAgent(agentId);
99
+ if (!agent) {
100
+ reply.code(404);
101
+ return { error: 'Agent not found' };
102
+ }
103
+ const conversationId = typeof req.query.conversation_id === 'string' ? req.query.conversation_id.trim() : '';
104
+ const target = typeof req.query.target === 'string' ? req.query.target.trim() : '';
105
+ if (!conversationId && !target) {
106
+ reply.code(400);
107
+ return { error: 'conversation_id or target is required' };
108
+ }
109
+ let resolvedConversationId = conversationId || null;
110
+ if (!resolvedConversationId && target.startsWith('#')) {
111
+ const channelId = resolveChannelFromTarget(target, db);
112
+ if (!channelId) {
113
+ reply.code(404);
114
+ return { error: 'Target channel not found' };
115
+ }
116
+ if (visibility.resolveVisibleChannelId(agent, target) === null) {
117
+ reply.code(403);
118
+ return { error: 'Agent is not a member of this channel' };
119
+ }
120
+ }
121
+ if (!resolvedConversationId && target) {
122
+ resolvedConversationId = visibility.resolveReadableConversationId(agent, target);
123
+ }
124
+ if (!resolvedConversationId) {
125
+ reply.code(404);
126
+ return { error: 'Conversation not found for the requested target' };
127
+ }
128
+ if (!visibility.canSeeConversation(agent, resolvedConversationId)) {
129
+ reply.code(404);
130
+ return { error: 'Conversation not found' };
131
+ }
132
+ const summary = getConversationSummary(db, agent, resolvedConversationId);
133
+ if (!summary) {
134
+ reply.code(404);
135
+ return { error: 'Conversation summary unavailable' };
136
+ }
137
+ return {
138
+ conversation: buildConversationSurfaceResponse(summary, {
139
+ contextKey: 'context',
140
+ includeReportText: true,
141
+ }),
142
+ };
143
+ });
144
+ app.post('/api/internal/agent/:agentId/context-bundle', async (req, reply) => {
145
+ const { agentId } = req.params;
146
+ const agent = conversationManager.getAgent(agentId);
147
+ if (!agent) {
148
+ reply.code(404);
149
+ return { error: 'Agent not found' };
150
+ }
151
+ const conversationId = typeof req.body?.conversationId === 'string' ? req.body.conversationId.trim() : '';
152
+ if (!conversationId) {
153
+ reply.code(400);
154
+ return { error: 'conversationId is required' };
155
+ }
156
+ if (!visibility.canSeeConversation(agent, conversationId)) {
157
+ reply.code(400);
158
+ return { error: 'conversationId does not belong to this agent' };
159
+ }
160
+ const bundle = buildContextBundle(db, agent, conversationId, {
161
+ maxSurfaces: req.body?.maxSurfaces,
162
+ maxTasks: req.body?.maxTasks,
163
+ maxHandoffs: req.body?.maxHandoffs,
164
+ });
165
+ if (!bundle) {
166
+ reply.code(404);
167
+ return { error: 'Context bundle unavailable' };
168
+ }
169
+ return { bundle };
170
+ });
171
+ }
@@ -0,0 +1,154 @@
1
+ import { AgentWorkspaceServiceError, mapAgentWorkspaceError } from '../../services/agentWorkspaceService.js';
2
+ function normalizeWorkspaceQueryPath(rawPath) {
3
+ if (!rawPath)
4
+ return '';
5
+ return rawPath.replace(/^\/+/, '');
6
+ }
7
+ function normalizeRequiredWorkspacePath(rawPath) {
8
+ const trimmed = normalizeWorkspaceQueryPath(rawPath);
9
+ if (!trimmed)
10
+ throw new AgentWorkspaceServiceError(400, 'path is required');
11
+ return trimmed;
12
+ }
13
+ function ensureAgentWorkspace(conversationManager, agentId) {
14
+ const agent = conversationManager.getAgent(agentId);
15
+ if (!agent) {
16
+ throw new AgentWorkspaceServiceError(404, 'Agent not found');
17
+ }
18
+ if (!agent.nodeId) {
19
+ throw new AgentWorkspaceServiceError(409, 'Agent is not assigned to a remote node.');
20
+ }
21
+ if (!agent.workspacePath) {
22
+ throw new AgentWorkspaceServiceError(409, 'Agent has no workspace configured.');
23
+ }
24
+ return {
25
+ agentId: agent.agentId,
26
+ nodeId: agent.nodeId,
27
+ workspacePath: agent.workspacePath,
28
+ };
29
+ }
30
+ function mapInspectError(error) {
31
+ const message = String(error?.message ?? error);
32
+ if (message === 'Agent node is offline.' || message.startsWith('Agent node disconnected:')) {
33
+ return new AgentWorkspaceServiceError(409, message);
34
+ }
35
+ if (message === 'Workspace inspect request timed out.') {
36
+ return new AgentWorkspaceServiceError(504, message);
37
+ }
38
+ if (message === 'Workspace root not found.') {
39
+ return new AgentWorkspaceServiceError(404, message);
40
+ }
41
+ if (message === 'Workspace root is not a directory.') {
42
+ return new AgentWorkspaceServiceError(400, message);
43
+ }
44
+ return new AgentWorkspaceServiceError(500, message);
45
+ }
46
+ function buildWorkspaceInspectReport(params) {
47
+ const lines = [
48
+ '[Workspace inspect]',
49
+ `node: ${params.nodeId}`,
50
+ `workspace_root: ${params.workspaceRoot}`,
51
+ `git: ${params.inspect.isGit ? 'yes' : 'no'} | kind=${params.inspect.workspaceKind} | branch=${params.inspect.branchName ?? 'none'}`,
52
+ ];
53
+ if (params.inspect.repoRoot)
54
+ lines.push(`repo_root: ${params.inspect.repoRoot}`);
55
+ if (params.inspect.remoteUrl)
56
+ lines.push(`remote: ${params.inspect.remoteUrl}`);
57
+ return lines.join('\n');
58
+ }
59
+ function normalizeQueryString(primary, fallback) {
60
+ const raw = typeof primary === 'string' && primary.trim()
61
+ ? primary
62
+ : typeof fallback === 'string' && fallback.trim()
63
+ ? fallback
64
+ : '';
65
+ return raw.trim() || undefined;
66
+ }
67
+ function resolveWorkspaceRunBypassId(params) {
68
+ const runId = normalizeQueryString(params.query.runId, params.query.run_id);
69
+ const conversationId = normalizeQueryString(params.query.conversationId, params.query.conversation_id);
70
+ if (!runId || !conversationId)
71
+ return undefined;
72
+ const conversation = params.conversationManager.getConversation(conversationId);
73
+ if (!conversation || conversation.agentId !== params.agentId)
74
+ return undefined;
75
+ const row = params.db.prepare(`SELECT r.run_id as runId
76
+ FROM runs r
77
+ JOIN conversations c ON c.session_key = r.session_key
78
+ WHERE r.run_id = ?
79
+ AND c.id = ?
80
+ AND c.agent_id = ?
81
+ AND r.ended_at IS NULL
82
+ LIMIT 1`).get(runId, conversationId, params.agentId);
83
+ return row?.runId || undefined;
84
+ }
85
+ export function registerAgentWorkspaceRoutes(app, db, conversationManager, workspaceBroker, inspectBroker) {
86
+ app.get('/api/internal/agent/:agentId/workspace-inspect', async (req, reply) => {
87
+ try {
88
+ const target = ensureAgentWorkspace(conversationManager, req.params.agentId);
89
+ const inspect = inspectBroker
90
+ ? await inspectBroker.inspectWorkspace(target.nodeId, target.workspacePath)
91
+ : {
92
+ workspaceRoot: target.workspacePath,
93
+ isGit: false,
94
+ repoRoot: null,
95
+ workspaceKind: 'directory',
96
+ branchName: null,
97
+ remoteUrl: null,
98
+ };
99
+ return {
100
+ nodeId: target.nodeId,
101
+ workspaceRoot: target.workspacePath,
102
+ inspect,
103
+ reportText: buildWorkspaceInspectReport({
104
+ nodeId: target.nodeId,
105
+ workspaceRoot: target.workspacePath,
106
+ inspect,
107
+ }),
108
+ };
109
+ }
110
+ catch (error) {
111
+ const mapped = error instanceof AgentWorkspaceServiceError ? error : mapInspectError(error);
112
+ reply.code(mapped.statusCode);
113
+ return { error: mapped.message };
114
+ }
115
+ });
116
+ app.get('/api/internal/agent/:agentId/workspace-tree', async (req, reply) => {
117
+ try {
118
+ const target = ensureAgentWorkspace(conversationManager, req.params.agentId);
119
+ return await workspaceBroker.listDirectory(target.nodeId, target.workspacePath, normalizeWorkspaceQueryPath(req.query.path), {
120
+ scaffold: false,
121
+ runId: resolveWorkspaceRunBypassId({
122
+ db,
123
+ conversationManager,
124
+ agentId: target.agentId,
125
+ query: req.query,
126
+ }),
127
+ });
128
+ }
129
+ catch (error) {
130
+ const mapped = error instanceof AgentWorkspaceServiceError ? error : mapAgentWorkspaceError(error);
131
+ reply.code(mapped.statusCode);
132
+ return { error: mapped.message };
133
+ }
134
+ });
135
+ app.get('/api/internal/agent/:agentId/workspace-file', async (req, reply) => {
136
+ try {
137
+ const target = ensureAgentWorkspace(conversationManager, req.params.agentId);
138
+ return await workspaceBroker.readFile(target.nodeId, target.workspacePath, normalizeRequiredWorkspacePath(req.query.path), {
139
+ scaffold: false,
140
+ runId: resolveWorkspaceRunBypassId({
141
+ db,
142
+ conversationManager,
143
+ agentId: target.agentId,
144
+ query: req.query,
145
+ }),
146
+ });
147
+ }
148
+ catch (error) {
149
+ const mapped = error instanceof AgentWorkspaceServiceError ? error : mapAgentWorkspaceError(error);
150
+ reply.code(mapped.statusCode);
151
+ return { error: mapped.message };
152
+ }
153
+ });
154
+ }