@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.
- package/dist/config.js +380 -0
- package/dist/execution/executionDispatcher.js +3810 -0
- package/dist/main.js +90 -0
- package/dist/nodeEventHistory.js +206 -0
- package/dist/scheduler/dreamLogic.js +50 -0
- package/dist/scheduler/dreamScheduler.js +65 -0
- package/dist/services/agentFileAccessService.js +1913 -0
- package/dist/services/agentRuntimeCleanupBroker.js +62 -0
- package/dist/services/agentSkillsBroker.js +118 -0
- package/dist/services/agentSkillsService.js +83 -0
- package/dist/services/agentWorkspaceBroker.js +937 -0
- package/dist/services/agentWorkspaceService.js +70 -0
- package/dist/services/appVersion.js +14 -0
- package/dist/services/auth.js +586 -0
- package/dist/services/claudeControlBroker.js +154 -0
- package/dist/services/claudeTranscriptBroker.js +100 -0
- package/dist/services/claudeTranscriptService.js +359 -0
- package/dist/services/codexAppServerBroker.js +155 -0
- package/dist/services/codexTranscriptBroker.js +98 -0
- package/dist/services/codexTranscriptService.js +961 -0
- package/dist/services/droidMissionBroker.js +124 -0
- package/dist/services/droidMissionImporter.js +630 -0
- package/dist/services/droidModelOptions.js +165 -0
- package/dist/services/hubServerRegistrationService.js +268 -0
- package/dist/services/libraryManifest.js +43 -0
- package/dist/services/libraryScaffold.js +26 -0
- package/dist/services/libraryService.js +2263 -0
- package/dist/services/memoryService.js +386 -0
- package/dist/services/missionEvidence.js +377 -0
- package/dist/services/missionService.js +2361 -0
- package/dist/services/missionTrace.js +158 -0
- package/dist/services/nativeMissionBriefParser.js +120 -0
- package/dist/services/nativeMissionOrchestrator.js +2045 -0
- package/dist/services/nativeMissionReportGenerator.js +227 -0
- package/dist/services/nativeMissionValidationRunner.js +452 -0
- package/dist/services/nativeMissionWorkerBroker.js +190 -0
- package/dist/services/nodeRegistry.js +34 -0
- package/dist/services/nodeStateReconciler.js +97 -0
- package/dist/services/panelMediaScanner.js +119 -0
- package/dist/services/persistentRuntimeJsonlClient.js +153 -0
- package/dist/services/platformAgentPolicy.js +180 -0
- package/dist/services/platformAgentService.js +2041 -0
- package/dist/services/projectAccessResolver.js +93 -0
- package/dist/services/projectService.js +392 -0
- package/dist/services/resourceSpaceService.js +140 -0
- package/dist/services/scenarioRuntimeService.js +1130 -0
- package/dist/services/suggestedPlannerService.js +868 -0
- package/dist/services/workbenchGitBroker.js +161 -0
- package/dist/services/workbenchGitService.js +69 -0
- package/dist/services/workbenchInspectBroker.js +65 -0
- package/dist/services/workbenchNodePathService.js +79 -0
- package/dist/services/workbenchRegistryService.js +240 -0
- package/dist/services/workbenchRootService.js +181 -0
- package/dist/services/workbenchTerminalBroker.js +378 -0
- package/dist/services/workspaceRunOwnership.js +60 -0
- package/dist/services/workspaceScaffold.js +105 -0
- package/dist/services/workspaceSessionRuntimeService.js +576 -0
- package/dist/services/workspaceSessionService.js +245 -0
- package/dist/services/workspaceToolActionRunner.js +1582 -0
- package/dist/services/workspaceToolErrors.js +10 -0
- package/dist/services/workspaceToolExecutionUtils.js +895 -0
- package/dist/services/workspaceToolLatestStateProjector.js +91 -0
- package/dist/services/workspaceToolManifest.js +572 -0
- package/dist/services/workspaceToolMutationQueue.js +43 -0
- package/dist/services/workspaceToolPanelProjection.js +460 -0
- package/dist/services/workspaceToolPromotion.js +255 -0
- package/dist/services/workspaceToolPromotionState.js +224 -0
- package/dist/services/workspaceToolPublishDiagnostics.js +189 -0
- package/dist/services/workspaceToolPublishIdentityResolver.js +146 -0
- package/dist/services/workspaceToolReadModel.js +378 -0
- package/dist/services/workspaceToolRunLedger.js +239 -0
- package/dist/services/workspaceToolService.js +3067 -0
- package/dist/services/workspaceToolSnapshotPanelSync.js +293 -0
- package/dist/services/workspaceToolTerminalLifecycle.js +283 -0
- package/dist/services/workspaceToolTypes.js +1 -0
- package/dist/services/workspaceToolUploadMaterializer.js +228 -0
- package/dist/web/actionCardRoutes.js +129 -0
- package/dist/web/actionCards.js +469 -0
- package/dist/web/activationContext.js +684 -0
- package/dist/web/agentChannelGuards.js +48 -0
- package/dist/web/agentMentionCooldowns.js +32 -0
- package/dist/web/agentReminders.js +1668 -0
- package/dist/web/agentRuntimePresence.js +197 -0
- package/dist/web/agentSelfState.js +494 -0
- package/dist/web/agentTaskLinks.js +26 -0
- package/dist/web/agentVisibility.js +79 -0
- package/dist/web/assets.js +95 -0
- package/dist/web/channelActivationPrompt.js +395 -0
- package/dist/web/channelMemoryNotes.js +127 -0
- package/dist/web/channelMentions.js +10 -0
- package/dist/web/channelMessageSequences.js +19 -0
- package/dist/web/channelSubscriptions.js +26 -0
- package/dist/web/clearedTaskRoots.js +10 -0
- package/dist/web/collaborationPromptGuidance.js +36 -0
- package/dist/web/collaborationSurfaceState.js +140 -0
- package/dist/web/contextBundleRanking.js +154 -0
- package/dist/web/contextBundleResolver.js +488 -0
- package/dist/web/conversationBuiltinSkillRoots.js +50 -0
- package/dist/web/conversationControls.js +232 -0
- package/dist/web/conversationHandoffs.js +612 -0
- package/dist/web/conversationManager.js +2511 -0
- package/dist/web/conversationSummaries.js +876 -0
- package/dist/web/conversationSurfaceKinds.js +17 -0
- package/dist/web/conversationTargets.js +173 -0
- package/dist/web/directActivationPrompt.js +122 -0
- package/dist/web/directReplyTargets.js +69 -0
- package/dist/web/directThreadResolver.js +129 -0
- package/dist/web/dmTaskHandoffPrompt.js +120 -0
- package/dist/web/dmTaskThreadStatusProjection.js +229 -0
- package/dist/web/ftsQuery.js +33 -0
- package/dist/web/internalAgentRouter.js +11341 -0
- package/dist/web/libraryCuratorScheduler.js +58 -0
- package/dist/web/libraryDocumentPromptGuidance.js +8 -0
- package/dist/web/messageCheckpoints.js +19 -0
- package/dist/web/nodeWsHandler.js +2495 -0
- package/dist/web/notificationRounds.js +1061 -0
- package/dist/web/panelActionMessages.js +108 -0
- package/dist/web/panelActivationPrompt.js +18 -0
- package/dist/web/panelAudit.js +273 -0
- package/dist/web/panelLifecycle.js +222 -0
- package/dist/web/panelMediaPolicy.js +43 -0
- package/dist/web/panelPathPolicy.js +63 -0
- package/dist/web/panelPreviews.js +175 -0
- package/dist/web/panelQueryHandles.js +2749 -0
- package/dist/web/panelRoutes.js +2147 -0
- package/dist/web/panels.js +904 -0
- package/dist/web/peerInboxAggregates.js +1247 -0
- package/dist/web/planApprovalState.js +92 -0
- package/dist/web/platformAgentScheduler.js +66 -0
- package/dist/web/proactiveOpportunities.js +452 -0
- package/dist/web/promptContextSections.js +242 -0
- package/dist/web/promptHistorySanitizer.js +26 -0
- package/dist/web/promptSlashCommands.js +158 -0
- package/dist/web/rollingConversationSummary.js +453 -0
- package/dist/web/routeHelpers.js +11 -0
- package/dist/web/routes/handoff.js +288 -0
- package/dist/web/routes/history.js +345 -0
- package/dist/web/routes/memory.js +258 -0
- package/dist/web/routes/selfState.js +171 -0
- package/dist/web/routes/workspace.js +154 -0
- package/dist/web/runSurfaceWatermarks.js +431 -0
- package/dist/web/runtimeCapabilities.js +48 -0
- package/dist/web/sameAgentHandoffs.js +494 -0
- package/dist/web/server.js +15567 -0
- package/dist/web/sharedCollaborationCapsules.js +163 -0
- package/dist/web/soloSessionRelay.js +42 -0
- package/dist/web/soloWsHandler.js +138 -0
- package/dist/web/suggestedPlannerScheduler.js +56 -0
- package/dist/web/surfaceActivationPolicy.js +108 -0
- package/dist/web/surfaceCollaborators.js +61 -0
- package/dist/web/surfaceSystemStatus.js +263 -0
- package/dist/web/targetParticipants.js +77 -0
- package/dist/web/taskEvents.js +49 -0
- package/dist/web/taskLifecycleMessages.js +165 -0
- package/dist/web/taskLoops.js +732 -0
- package/dist/web/taskMemoryNotes.js +224 -0
- package/dist/web/taskNumbers.js +16 -0
- package/dist/web/taskOwnerGuards.js +49 -0
- package/dist/web/taskParticipantResolver.js +42 -0
- package/dist/web/taskParticipants.js +97 -0
- package/dist/web/taskSourceDetails.js +20 -0
- package/dist/web/taskStateViews.js +210 -0
- package/dist/web/taskStatusTransitions.js +9 -0
- package/dist/web/taskThreadFollowups.js +599 -0
- package/dist/web/taskThreadRuntimeClosure.js +685 -0
- package/dist/web/taskUpdateDelivery.js +104 -0
- package/dist/web/threadReplyContentHeuristics.js +30 -0
- package/dist/web/threadRoots.js +61 -0
- package/dist/web/threadTaskBindings.js +365 -0
- package/dist/web/uiPanelPromptGuidance.js +27 -0
- package/dist/web/workspaceMemoryHints.js +143 -0
- package/dist/web/workspaceToolPromptGuidance.js +30 -0
- package/dist/web/wsHandler.js +397 -0
- package/dist/web/wsSink.js +116 -0
- package/package.json +54 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { deletePanelAttachmentArtifactRows, unlinkPanelAttachmentStoragePaths, } from '../web/panelLifecycle.js';
|
|
2
|
+
import { getPanelById, insertPanel, insertPanelRows, listAllPanelRows, } from '../web/panels.js';
|
|
3
|
+
import { ensureSurfaceOwner } from '../web/surfaceCollaborators.js';
|
|
4
|
+
import { isPlainRecord, parseStateRowJson, } from './workspaceToolExecutionUtils.js';
|
|
5
|
+
import { parseWorkspaceToolManifest } from './workspaceToolManifest.js';
|
|
6
|
+
import { buildToolSnapshotPanelProjection, buildToolSnapshotSchemaFingerprint, deriveWorkspaceToolRuntimeState, workspaceToolRuntimeStateToPanelStatus, } from './workspaceToolPanelProjection.js';
|
|
7
|
+
export class WorkspaceToolSnapshotPanelSyncer {
|
|
8
|
+
params;
|
|
9
|
+
constructor(params) {
|
|
10
|
+
this.params = params;
|
|
11
|
+
}
|
|
12
|
+
updateLatestState(params, options) {
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
const staleCachedStoragePaths = [];
|
|
15
|
+
// Transaction boundary: the tool latest_state_json update and the
|
|
16
|
+
// tool-scoped snapshot panel rewrite commit or roll back together.
|
|
17
|
+
// The broadcast callback and stale media unlink happen only after commit.
|
|
18
|
+
const tx = this.db.transaction(() => {
|
|
19
|
+
this.db.prepare(`UPDATE workspace_tools
|
|
20
|
+
SET latest_state_json = ?,
|
|
21
|
+
updated_at = ?
|
|
22
|
+
WHERE tool_id = ?`).run(params.state ? JSON.stringify(params.state) : null, now, params.toolId);
|
|
23
|
+
const toolRow = this.db.prepare(`SELECT tool_id as toolId,
|
|
24
|
+
user_id as userId,
|
|
25
|
+
agent_id as agentId,
|
|
26
|
+
source_panel_id as sourcePanelId,
|
|
27
|
+
panel_id as panelId,
|
|
28
|
+
active_terminal_id as activeTerminalId,
|
|
29
|
+
maintenance_conversation_id as maintenanceConversationId,
|
|
30
|
+
allow_shared_exec as allowSharedExec,
|
|
31
|
+
manifest_json as manifestJson
|
|
32
|
+
FROM workspace_tools
|
|
33
|
+
WHERE tool_id = ?
|
|
34
|
+
AND deleted_at IS NULL
|
|
35
|
+
LIMIT 1`).get(params.toolId);
|
|
36
|
+
const snapshotConversationCandidate = toolRow?.maintenanceConversationId ?? params.maintenanceConversationId ?? null;
|
|
37
|
+
const snapshotConversationId = snapshotConversationCandidate
|
|
38
|
+
&& this.conversations.getConversation(snapshotConversationCandidate)
|
|
39
|
+
? snapshotConversationCandidate
|
|
40
|
+
: null;
|
|
41
|
+
if (toolRow?.maintenanceConversationId && !snapshotConversationId) {
|
|
42
|
+
this.db.prepare(`UPDATE workspace_tools
|
|
43
|
+
SET maintenance_conversation_id = NULL,
|
|
44
|
+
updated_at = ?
|
|
45
|
+
WHERE tool_id = ?
|
|
46
|
+
AND maintenance_conversation_id = ?`).run(now, toolRow.toolId, toolRow.maintenanceConversationId);
|
|
47
|
+
}
|
|
48
|
+
if (toolRow && snapshotConversationId) {
|
|
49
|
+
const agent = this.conversations.getAgent(toolRow.agentId);
|
|
50
|
+
const manifest = parseWorkspaceToolManifest(toolRow.manifestJson);
|
|
51
|
+
const snapshotSync = this.syncPanel({
|
|
52
|
+
panelId: toolRow.panelId,
|
|
53
|
+
sourcePanelId: toolRow.sourcePanelId,
|
|
54
|
+
toolId: toolRow.toolId,
|
|
55
|
+
userId: toolRow.userId,
|
|
56
|
+
agentId: toolRow.agentId,
|
|
57
|
+
conversationId: snapshotConversationId,
|
|
58
|
+
nodeId: agent?.nodeId ?? null,
|
|
59
|
+
manifest,
|
|
60
|
+
latestState: params.state,
|
|
61
|
+
allowSharedExec: toolRow.allowSharedExec === 1,
|
|
62
|
+
status: this.resolveDefaultToolPanelStatus(toolRow.toolId, toolRow.activeTerminalId),
|
|
63
|
+
});
|
|
64
|
+
if (snapshotSync.panelId !== toolRow.panelId) {
|
|
65
|
+
this.db.prepare(`UPDATE workspace_tools
|
|
66
|
+
SET panel_id = ?,
|
|
67
|
+
updated_at = ?
|
|
68
|
+
WHERE tool_id = ?`).run(snapshotSync.panelId, now, toolRow.toolId);
|
|
69
|
+
}
|
|
70
|
+
staleCachedStoragePaths.push(...snapshotSync.staleCachedStoragePaths);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
tx();
|
|
74
|
+
options?.onCommitted?.();
|
|
75
|
+
unlinkPanelAttachmentStoragePaths(staleCachedStoragePaths);
|
|
76
|
+
}
|
|
77
|
+
syncForTool(toolId, status) {
|
|
78
|
+
const toolRow = this.db.prepare(`SELECT tool_id as toolId,
|
|
79
|
+
user_id as userId,
|
|
80
|
+
agent_id as agentId,
|
|
81
|
+
source_panel_id as sourcePanelId,
|
|
82
|
+
panel_id as panelId,
|
|
83
|
+
active_terminal_id as activeTerminalId,
|
|
84
|
+
maintenance_conversation_id as maintenanceConversationId,
|
|
85
|
+
allow_shared_exec as allowSharedExec,
|
|
86
|
+
manifest_json as manifestJson,
|
|
87
|
+
latest_state_json as latestStateJson
|
|
88
|
+
FROM workspace_tools
|
|
89
|
+
WHERE tool_id = ?
|
|
90
|
+
AND deleted_at IS NULL
|
|
91
|
+
LIMIT 1`).get(toolId);
|
|
92
|
+
if (!toolRow)
|
|
93
|
+
return;
|
|
94
|
+
if (!toolRow.panelId && !toolRow.maintenanceConversationId)
|
|
95
|
+
return;
|
|
96
|
+
if (toolRow.maintenanceConversationId && !this.conversations.getConversation(toolRow.maintenanceConversationId)) {
|
|
97
|
+
this.db.prepare(`UPDATE workspace_tools
|
|
98
|
+
SET maintenance_conversation_id = NULL,
|
|
99
|
+
updated_at = ?
|
|
100
|
+
WHERE tool_id = ?
|
|
101
|
+
AND maintenance_conversation_id = ?`).run(Date.now(), toolRow.toolId, toolRow.maintenanceConversationId);
|
|
102
|
+
if (!toolRow.panelId)
|
|
103
|
+
return;
|
|
104
|
+
toolRow.maintenanceConversationId = null;
|
|
105
|
+
}
|
|
106
|
+
const agent = this.conversations.getAgent(toolRow.agentId);
|
|
107
|
+
const manifest = parseWorkspaceToolManifest(toolRow.manifestJson);
|
|
108
|
+
const snapshotSync = this.syncPanel({
|
|
109
|
+
panelId: toolRow.panelId,
|
|
110
|
+
sourcePanelId: toolRow.sourcePanelId,
|
|
111
|
+
toolId: toolRow.toolId,
|
|
112
|
+
userId: toolRow.userId,
|
|
113
|
+
agentId: toolRow.agentId,
|
|
114
|
+
conversationId: toolRow.maintenanceConversationId ?? '',
|
|
115
|
+
nodeId: agent?.nodeId ?? null,
|
|
116
|
+
manifest,
|
|
117
|
+
latestState: parseStateRowJson(toolRow.latestStateJson ?? null),
|
|
118
|
+
allowSharedExec: toolRow.allowSharedExec === 1,
|
|
119
|
+
status: status ?? this.resolveDefaultToolPanelStatus(toolRow.toolId, toolRow.activeTerminalId),
|
|
120
|
+
});
|
|
121
|
+
if (snapshotSync.panelId !== toolRow.panelId) {
|
|
122
|
+
this.db.prepare(`UPDATE workspace_tools
|
|
123
|
+
SET panel_id = ?,
|
|
124
|
+
updated_at = ?
|
|
125
|
+
WHERE tool_id = ?`).run(snapshotSync.panelId, Date.now(), toolRow.toolId);
|
|
126
|
+
}
|
|
127
|
+
unlinkPanelAttachmentStoragePaths(snapshotSync.staleCachedStoragePaths);
|
|
128
|
+
}
|
|
129
|
+
syncPanel(spec) {
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
const existing = spec.panelId ? getPanelById(this.db, spec.panelId) : null;
|
|
132
|
+
const sourcePanel = spec.sourcePanelId
|
|
133
|
+
? getPanelById(this.db, spec.sourcePanelId)
|
|
134
|
+
: null;
|
|
135
|
+
const sourceProps = sourcePanel?.component === 'RowTemplateGrid' && !sourcePanel.archivedAt && isPlainRecord(sourcePanel.props)
|
|
136
|
+
? sourcePanel.props
|
|
137
|
+
: spec.sourcePanelId && existing?.component === 'RowTemplateGrid' && isPlainRecord(existing.props)
|
|
138
|
+
? existing.props
|
|
139
|
+
: null;
|
|
140
|
+
const liveSourceRows = sourcePanel && !sourcePanel.archivedAt && sourceProps
|
|
141
|
+
? listAllPanelRows(this.db, sourcePanel.id).map((sourceRow) => ({
|
|
142
|
+
rowId: sourceRow.rowId,
|
|
143
|
+
fields: sourceRow.fields,
|
|
144
|
+
media: sourceRow.media,
|
|
145
|
+
nodeId: sourceRow.nodeId ?? spec.nodeId,
|
|
146
|
+
cachedAssetIds: {},
|
|
147
|
+
}))
|
|
148
|
+
: [];
|
|
149
|
+
const preservedSnapshotRows = liveSourceRows.length === 0 && spec.sourcePanelId && existing?.component === 'RowTemplateGrid'
|
|
150
|
+
? listAllPanelRows(this.db, existing.id)
|
|
151
|
+
.filter((existingRow) => existingRow.rowId !== 'tool-state')
|
|
152
|
+
.map((existingRow) => ({
|
|
153
|
+
rowId: existingRow.rowId,
|
|
154
|
+
fields: existingRow.fields,
|
|
155
|
+
media: existingRow.media,
|
|
156
|
+
nodeId: existingRow.nodeId ?? spec.nodeId,
|
|
157
|
+
cachedAssetIds: {},
|
|
158
|
+
}))
|
|
159
|
+
: [];
|
|
160
|
+
const projection = buildToolSnapshotPanelProjection({
|
|
161
|
+
sourcePanelId: spec.sourcePanelId,
|
|
162
|
+
nodeId: spec.nodeId,
|
|
163
|
+
manifest: spec.manifest,
|
|
164
|
+
latestState: spec.latestState,
|
|
165
|
+
allowSharedExec: spec.allowSharedExec,
|
|
166
|
+
status: spec.status,
|
|
167
|
+
sourceProps,
|
|
168
|
+
sourceRows: liveSourceRows,
|
|
169
|
+
preservedRows: preservedSnapshotRows,
|
|
170
|
+
});
|
|
171
|
+
if (!existing || existing.archivedAt) {
|
|
172
|
+
const panelId = insertPanel(this.db, {
|
|
173
|
+
id: existing?.archivedAt ? undefined : (spec.panelId ?? undefined),
|
|
174
|
+
agentId: spec.agentId,
|
|
175
|
+
conversationId: spec.conversationId,
|
|
176
|
+
ownerUserId: spec.userId,
|
|
177
|
+
scopeType: 'tool',
|
|
178
|
+
scopeId: spec.toolId,
|
|
179
|
+
component: 'RowTemplateGrid',
|
|
180
|
+
props: projection.props,
|
|
181
|
+
actions: projection.actions,
|
|
182
|
+
rowCount: projection.rowCount,
|
|
183
|
+
status: spec.status ?? 'idle',
|
|
184
|
+
result: projection.result,
|
|
185
|
+
createdAt: now,
|
|
186
|
+
updatedAt: now,
|
|
187
|
+
});
|
|
188
|
+
ensureSurfaceOwner(this.db, {
|
|
189
|
+
surfaceType: 'panel',
|
|
190
|
+
surfaceId: panelId,
|
|
191
|
+
agentId: spec.agentId,
|
|
192
|
+
createdAt: now,
|
|
193
|
+
});
|
|
194
|
+
if (projection.rows.length > 0) {
|
|
195
|
+
insertPanelRows(this.db, projection.rows.map((row, rowIndex) => ({
|
|
196
|
+
panelId,
|
|
197
|
+
rowIndex,
|
|
198
|
+
rowId: row.rowId,
|
|
199
|
+
fields: row.fields,
|
|
200
|
+
media: row.media,
|
|
201
|
+
nodeId: row.nodeId,
|
|
202
|
+
cachedAssetIds: row.cachedAssetIds ?? {},
|
|
203
|
+
})));
|
|
204
|
+
}
|
|
205
|
+
return { panelId, staleCachedStoragePaths: [] };
|
|
206
|
+
}
|
|
207
|
+
const previousSchemaFingerprint = buildToolSnapshotSchemaFingerprint(existing.props ?? {});
|
|
208
|
+
const staleCachedAssetIds = listPanelCachedAssetIds(this.db, existing.id);
|
|
209
|
+
const staleCachedStoragePaths = this.db.transaction(() => {
|
|
210
|
+
this.db.prepare(`UPDATE panels
|
|
211
|
+
SET conversation_id = ?,
|
|
212
|
+
owner_user_id = ?,
|
|
213
|
+
scope_type = 'tool',
|
|
214
|
+
scope_id = ?,
|
|
215
|
+
component = 'RowTemplateGrid',
|
|
216
|
+
props_json = ?,
|
|
217
|
+
actions_json = ?,
|
|
218
|
+
row_count = ?,
|
|
219
|
+
status = ?,
|
|
220
|
+
progress_json = NULL,
|
|
221
|
+
result_json = ?,
|
|
222
|
+
updated_at = ?,
|
|
223
|
+
version = version + 1
|
|
224
|
+
WHERE id = ?`).run(spec.conversationId, spec.userId, spec.toolId, JSON.stringify(projection.props), JSON.stringify(projection.actions), projection.rowCount, spec.status ?? existing.status, projection.result ? JSON.stringify(projection.result) : null, now, existing.id);
|
|
225
|
+
ensureSurfaceOwner(this.db, {
|
|
226
|
+
surfaceType: 'panel',
|
|
227
|
+
surfaceId: existing.id,
|
|
228
|
+
agentId: spec.agentId,
|
|
229
|
+
createdAt: now,
|
|
230
|
+
});
|
|
231
|
+
if (previousSchemaFingerprint !== projection.schemaFingerprint) {
|
|
232
|
+
this.db.prepare(`DELETE FROM panel_state WHERE panel_id = ?`).run(existing.id);
|
|
233
|
+
this.db.prepare(`DELETE FROM panel_shared_state WHERE panel_id = ?`).run(existing.id);
|
|
234
|
+
}
|
|
235
|
+
const storagePaths = deletePanelAttachmentArtifactRows(this.db, {
|
|
236
|
+
panelId: existing.id,
|
|
237
|
+
attachmentIds: staleCachedAssetIds,
|
|
238
|
+
});
|
|
239
|
+
this.db.prepare('DELETE FROM panel_rows WHERE panel_id = ?').run(existing.id);
|
|
240
|
+
if (projection.rows.length > 0) {
|
|
241
|
+
insertPanelRows(this.db, projection.rows.map((row, rowIndex) => ({
|
|
242
|
+
panelId: existing.id,
|
|
243
|
+
rowIndex,
|
|
244
|
+
rowId: row.rowId,
|
|
245
|
+
fields: row.fields,
|
|
246
|
+
media: row.media,
|
|
247
|
+
nodeId: row.nodeId,
|
|
248
|
+
cachedAssetIds: row.cachedAssetIds ?? {},
|
|
249
|
+
})));
|
|
250
|
+
}
|
|
251
|
+
return storagePaths;
|
|
252
|
+
})();
|
|
253
|
+
return { panelId: existing.id, staleCachedStoragePaths };
|
|
254
|
+
}
|
|
255
|
+
get db() {
|
|
256
|
+
return this.params.db;
|
|
257
|
+
}
|
|
258
|
+
get conversations() {
|
|
259
|
+
return this.params.conversations;
|
|
260
|
+
}
|
|
261
|
+
resolveDefaultToolPanelStatus(toolId, activeTerminalId) {
|
|
262
|
+
const lastRunRow = this.db.prepare(`SELECT status
|
|
263
|
+
FROM workspace_tool_runs
|
|
264
|
+
WHERE tool_id = ?
|
|
265
|
+
ORDER BY started_at DESC
|
|
266
|
+
LIMIT 1`).get(toolId);
|
|
267
|
+
return workspaceToolRuntimeStateToPanelStatus(deriveWorkspaceToolRuntimeState({
|
|
268
|
+
activeTerminalId,
|
|
269
|
+
lastRunStatus: lastRunRow?.status ?? null,
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function listPanelCachedAssetIds(db, panelId) {
|
|
274
|
+
const rows = db.prepare(`SELECT cached_asset_ids_json as cachedAssetIdsJson
|
|
275
|
+
FROM panel_rows
|
|
276
|
+
WHERE panel_id = ?`).all(panelId);
|
|
277
|
+
const assetIds = new Set();
|
|
278
|
+
for (const row of rows) {
|
|
279
|
+
try {
|
|
280
|
+
const parsed = JSON.parse(row.cachedAssetIdsJson ?? '{}');
|
|
281
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
282
|
+
continue;
|
|
283
|
+
for (const value of Object.values(parsed)) {
|
|
284
|
+
if (typeof value === 'string' && value.trim())
|
|
285
|
+
assetIds.add(value);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Malformed cache metadata should not block snapshot refresh.
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return [...assetIds];
|
|
293
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { log } from '@bbigbang/runtime-acp';
|
|
2
|
+
import { parseWorkspaceToolManifest } from './workspaceToolManifest.js';
|
|
3
|
+
import { deriveWorkspaceToolRuntimeState, workspaceToolRuntimeStateToPanelStatus, } from './workspaceToolPanelProjection.js';
|
|
4
|
+
import { WorkspaceToolServiceError } from './workspaceToolErrors.js';
|
|
5
|
+
const DEFAULT_PERSISTENT_MAX_RUN_SECONDS = 24 * 60 * 60;
|
|
6
|
+
const PERSISTENT_RUNTIME_ACTION_TIMEOUT_MS = 120_000;
|
|
7
|
+
const DEFAULT_PERSISTENT_RUNTIME_IDLE_TTL_SECONDS = 900;
|
|
8
|
+
const DEFAULT_WORKSPACE_TOOL_MAX_ACTIVE_PERSISTENT_RUNS_PER_AGENT = 3;
|
|
9
|
+
export class WorkspaceToolTerminalLifecycle {
|
|
10
|
+
deps;
|
|
11
|
+
activeTerminalBuffers;
|
|
12
|
+
activePersistentTimeouts = new Map();
|
|
13
|
+
activePersistentIdleTimeouts = new Map();
|
|
14
|
+
activePersistentStartupCaptureTimeouts = new Map();
|
|
15
|
+
terminatingPersistentTerminals = new Set();
|
|
16
|
+
maxActivePersistentRunsPerAgent;
|
|
17
|
+
constructor(deps, activeTerminalBuffers) {
|
|
18
|
+
this.deps = deps;
|
|
19
|
+
this.activeTerminalBuffers = activeTerminalBuffers;
|
|
20
|
+
this.maxActivePersistentRunsPerAgent =
|
|
21
|
+
typeof deps.maxActivePersistentRunsPerAgent === 'number'
|
|
22
|
+
&& Number.isInteger(deps.maxActivePersistentRunsPerAgent)
|
|
23
|
+
&& deps.maxActivePersistentRunsPerAgent > 0
|
|
24
|
+
? deps.maxActivePersistentRunsPerAgent
|
|
25
|
+
: DEFAULT_WORKSPACE_TOOL_MAX_ACTIVE_PERSISTENT_RUNS_PER_AGENT;
|
|
26
|
+
}
|
|
27
|
+
getContext(terminalId) {
|
|
28
|
+
return this.activeTerminalBuffers.get(terminalId);
|
|
29
|
+
}
|
|
30
|
+
hasContext(terminalId) {
|
|
31
|
+
return this.activeTerminalBuffers.has(terminalId);
|
|
32
|
+
}
|
|
33
|
+
setContext(terminalId, ctx) {
|
|
34
|
+
this.activeTerminalBuffers.set(terminalId, ctx);
|
|
35
|
+
}
|
|
36
|
+
deleteContext(terminalId) {
|
|
37
|
+
this.activeTerminalBuffers.delete(terminalId);
|
|
38
|
+
}
|
|
39
|
+
isTerminating(terminalId) {
|
|
40
|
+
return this.terminatingPersistentTerminals.has(terminalId);
|
|
41
|
+
}
|
|
42
|
+
assertAgentPersistentRunQuota(agentId) {
|
|
43
|
+
const row = this.deps.db.prepare(`SELECT COUNT(*) as count
|
|
44
|
+
FROM workspace_tools
|
|
45
|
+
WHERE agent_id = ?
|
|
46
|
+
AND deleted_at IS NULL
|
|
47
|
+
AND active_terminal_id IS NOT NULL`).get(agentId);
|
|
48
|
+
if (row.count >= this.maxActivePersistentRunsPerAgent) {
|
|
49
|
+
throw new WorkspaceToolServiceError(`Agent already has ${this.maxActivePersistentRunsPerAgent} active persistent workspace-tool terminals. Stop one before starting another.`, 409);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
finishActivePersistentRun(toolId, terminalId, completion) {
|
|
53
|
+
const run = this.deps.db.prepare(`SELECT run_id as runId
|
|
54
|
+
FROM workspace_tool_runs
|
|
55
|
+
WHERE tool_id = ?
|
|
56
|
+
AND terminal_id = ?
|
|
57
|
+
AND status = 'running'
|
|
58
|
+
ORDER BY started_at DESC
|
|
59
|
+
LIMIT 1`).get(toolId, terminalId);
|
|
60
|
+
this.deps.db.prepare(`UPDATE workspace_tools
|
|
61
|
+
SET active_terminal_id = NULL,
|
|
62
|
+
updated_at = ?
|
|
63
|
+
WHERE tool_id = ?
|
|
64
|
+
AND active_terminal_id = ?`).run(Date.now(), toolId, terminalId);
|
|
65
|
+
this.deps.onBeforeFinish?.(terminalId, run?.runId ?? null);
|
|
66
|
+
this.activeTerminalBuffers.delete(terminalId);
|
|
67
|
+
this.clearPersistentRunTimeout(terminalId);
|
|
68
|
+
this.clearPersistentIdleTimeout(terminalId);
|
|
69
|
+
this.clearPersistentStartupCaptureTimeout(terminalId);
|
|
70
|
+
if (run) {
|
|
71
|
+
this.deps.runLedger.completeRun(run.runId, {
|
|
72
|
+
status: completion.status,
|
|
73
|
+
endedAt: Date.now(),
|
|
74
|
+
exitCode: completion.exitCode ?? null,
|
|
75
|
+
signal: completion.signal ?? null,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
this.syncStatusBestEffort(toolId, completion);
|
|
79
|
+
}
|
|
80
|
+
reattachActivePersistentTerminal(row, nodeId) {
|
|
81
|
+
let manifest;
|
|
82
|
+
try {
|
|
83
|
+
manifest = parseWorkspaceToolManifest(row.manifestJson);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
log.warn('[workspace-tools] active terminal reconcile skipped invalid manifest', {
|
|
87
|
+
toolId: row.toolId,
|
|
88
|
+
terminalId: row.activeTerminalId,
|
|
89
|
+
error: String(error?.message ?? error),
|
|
90
|
+
});
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const alreadyBuffered = this.activeTerminalBuffers.has(row.activeTerminalId);
|
|
94
|
+
if (!alreadyBuffered) {
|
|
95
|
+
this.activeTerminalBuffers.set(row.activeTerminalId, {
|
|
96
|
+
toolId: row.toolId,
|
|
97
|
+
runId: row.runningRunId,
|
|
98
|
+
userId: row.userId,
|
|
99
|
+
agentId: row.agentId,
|
|
100
|
+
agentNodeId: nodeId,
|
|
101
|
+
maintenanceConversationId: row.maintenanceConversationId,
|
|
102
|
+
manifest,
|
|
103
|
+
lineBuffer: '',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const action = row.runningActionId
|
|
107
|
+
? manifest.actions.find((candidate) => candidate.actionId === row.runningActionId)
|
|
108
|
+
: undefined;
|
|
109
|
+
if (manifest.runtime?.mode !== 'persistent_runtime') {
|
|
110
|
+
this.schedulePersistentRunTimeout({
|
|
111
|
+
toolId: row.toolId,
|
|
112
|
+
agentNodeId: nodeId,
|
|
113
|
+
terminalId: row.activeTerminalId,
|
|
114
|
+
maxRunSeconds: action?.maxRunSeconds,
|
|
115
|
+
startedAt: row.runStartedAt ?? undefined,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else if (row.runningRunId) {
|
|
119
|
+
this.schedulePersistentRunTimeout({
|
|
120
|
+
toolId: row.toolId,
|
|
121
|
+
agentNodeId: nodeId,
|
|
122
|
+
terminalId: row.activeTerminalId,
|
|
123
|
+
maxRunSeconds: action?.maxRunSeconds ?? (PERSISTENT_RUNTIME_ACTION_TIMEOUT_MS / 1000),
|
|
124
|
+
startedAt: row.runStartedAt ?? undefined,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.clearPersistentRunTimeout(row.activeTerminalId);
|
|
129
|
+
}
|
|
130
|
+
const ctx = this.activeTerminalBuffers.get(row.activeTerminalId);
|
|
131
|
+
if (ctx) {
|
|
132
|
+
ctx.idleTimeoutSeconds = manifest.runtime?.mode === 'persistent_runtime'
|
|
133
|
+
? manifest.runtime.idleTtlSeconds ?? DEFAULT_PERSISTENT_RUNTIME_IDLE_TTL_SECONDS
|
|
134
|
+
: action?.idleTimeoutSeconds;
|
|
135
|
+
this.resetPersistentIdleTimeout(row.activeTerminalId);
|
|
136
|
+
}
|
|
137
|
+
return !alreadyBuffered;
|
|
138
|
+
}
|
|
139
|
+
async handleTerminalTermination(event) {
|
|
140
|
+
this.terminatingPersistentTerminals.add(event.terminalId);
|
|
141
|
+
try {
|
|
142
|
+
const row = this.deps.db.prepare(`SELECT tool_id as toolId,
|
|
143
|
+
user_id as userId
|
|
144
|
+
FROM workspace_tools
|
|
145
|
+
WHERE active_terminal_id = ?
|
|
146
|
+
AND deleted_at IS NULL
|
|
147
|
+
LIMIT 1`).get(event.terminalId);
|
|
148
|
+
if (!row)
|
|
149
|
+
return;
|
|
150
|
+
await this.deps.enqueueToolMutation(row.toolId, async () => {
|
|
151
|
+
const current = this.deps.db.prepare(`SELECT active_terminal_id as activeTerminalId
|
|
152
|
+
FROM workspace_tools
|
|
153
|
+
WHERE tool_id = ?`).get(row.toolId);
|
|
154
|
+
if (!current?.activeTerminalId || current.activeTerminalId !== event.terminalId) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const status = event.exitCode === 0
|
|
158
|
+
? 'completed'
|
|
159
|
+
: event.disposed && event.exitCode == null
|
|
160
|
+
? 'cancelled'
|
|
161
|
+
: 'failed';
|
|
162
|
+
this.finishActivePersistentRun(row.toolId, event.terminalId, {
|
|
163
|
+
status,
|
|
164
|
+
exitCode: event.exitCode ?? null,
|
|
165
|
+
signal: event.signal ?? null,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
this.terminatingPersistentTerminals.delete(event.terminalId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
schedulePersistentRunTimeout(params) {
|
|
174
|
+
this.clearPersistentRunTimeout(params.terminalId);
|
|
175
|
+
const seconds = params.maxRunSeconds ?? DEFAULT_PERSISTENT_MAX_RUN_SECONDS;
|
|
176
|
+
const elapsedMs = params.startedAt ? Math.max(0, Date.now() - params.startedAt) : 0;
|
|
177
|
+
const timeoutMs = Math.max(0, (seconds * 1000) - elapsedMs);
|
|
178
|
+
const handle = setTimeout(() => {
|
|
179
|
+
void Promise.resolve(this.handlePersistentRunTimeout({
|
|
180
|
+
toolId: params.toolId,
|
|
181
|
+
agentNodeId: params.agentNodeId,
|
|
182
|
+
terminalId: params.terminalId,
|
|
183
|
+
signal: 'timeout',
|
|
184
|
+
})).catch(() => {
|
|
185
|
+
// Timeouts are best-effort; the runner logs its own failures.
|
|
186
|
+
});
|
|
187
|
+
}, timeoutMs);
|
|
188
|
+
handle.unref?.();
|
|
189
|
+
this.activePersistentTimeouts.set(params.terminalId, handle);
|
|
190
|
+
}
|
|
191
|
+
resetPersistentIdleTimeout(terminalId) {
|
|
192
|
+
const ctx = this.activeTerminalBuffers.get(terminalId);
|
|
193
|
+
if (!ctx?.idleTimeoutSeconds) {
|
|
194
|
+
this.clearPersistentIdleTimeout(terminalId);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
this.schedulePersistentIdleTimeout({
|
|
198
|
+
toolId: ctx.toolId,
|
|
199
|
+
agentNodeId: ctx.agentNodeId,
|
|
200
|
+
terminalId,
|
|
201
|
+
idleTimeoutSeconds: ctx.idleTimeoutSeconds,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
schedulePersistentIdleTimeout(params) {
|
|
205
|
+
this.clearPersistentIdleTimeout(params.terminalId);
|
|
206
|
+
const handle = setTimeout(() => {
|
|
207
|
+
void Promise.resolve(this.handlePersistentRunTimeout({
|
|
208
|
+
toolId: params.toolId,
|
|
209
|
+
agentNodeId: params.agentNodeId,
|
|
210
|
+
terminalId: params.terminalId,
|
|
211
|
+
signal: 'idle_timeout',
|
|
212
|
+
})).catch(() => {
|
|
213
|
+
// Timeouts are best-effort; the runner logs its own failures.
|
|
214
|
+
});
|
|
215
|
+
}, params.idleTimeoutSeconds * 1000);
|
|
216
|
+
handle.unref?.();
|
|
217
|
+
this.activePersistentIdleTimeouts.set(params.terminalId, handle);
|
|
218
|
+
}
|
|
219
|
+
clearPersistentRunTimeout(terminalId) {
|
|
220
|
+
const handle = this.activePersistentTimeouts.get(terminalId);
|
|
221
|
+
if (!handle)
|
|
222
|
+
return;
|
|
223
|
+
clearTimeout(handle);
|
|
224
|
+
this.activePersistentTimeouts.delete(terminalId);
|
|
225
|
+
}
|
|
226
|
+
clearPersistentIdleTimeout(terminalId) {
|
|
227
|
+
const handle = this.activePersistentIdleTimeouts.get(terminalId);
|
|
228
|
+
if (!handle)
|
|
229
|
+
return;
|
|
230
|
+
clearTimeout(handle);
|
|
231
|
+
this.activePersistentIdleTimeouts.delete(terminalId);
|
|
232
|
+
}
|
|
233
|
+
clearPersistentStartupCaptureTimeout(terminalId) {
|
|
234
|
+
const handle = this.activePersistentStartupCaptureTimeouts.get(terminalId);
|
|
235
|
+
if (!handle)
|
|
236
|
+
return;
|
|
237
|
+
clearTimeout(handle);
|
|
238
|
+
this.activePersistentStartupCaptureTimeouts.delete(terminalId);
|
|
239
|
+
}
|
|
240
|
+
schedulePersistentStartupSnapshotFallback(params) {
|
|
241
|
+
this.clearPersistentStartupCaptureTimeout(params.terminalId);
|
|
242
|
+
const timer = setTimeout(() => {
|
|
243
|
+
this.activePersistentStartupCaptureTimeouts.delete(params.terminalId);
|
|
244
|
+
void Promise.resolve(params.onFallback()).catch(() => {
|
|
245
|
+
// Fallback is best-effort.
|
|
246
|
+
});
|
|
247
|
+
}, params.delayMs);
|
|
248
|
+
timer.unref?.();
|
|
249
|
+
this.activePersistentStartupCaptureTimeouts.set(params.terminalId, timer);
|
|
250
|
+
}
|
|
251
|
+
async handlePersistentRunTimeout(params) {
|
|
252
|
+
await this.deps.enqueueToolMutation(params.toolId, async () => {
|
|
253
|
+
const current = this.deps.db.prepare(`SELECT active_terminal_id as activeTerminalId
|
|
254
|
+
FROM workspace_tools
|
|
255
|
+
WHERE tool_id = ?
|
|
256
|
+
AND deleted_at IS NULL`).get(params.toolId);
|
|
257
|
+
if (!current?.activeTerminalId || current.activeTerminalId !== params.terminalId) {
|
|
258
|
+
this.clearPersistentRunTimeout(params.terminalId);
|
|
259
|
+
this.clearPersistentIdleTimeout(params.terminalId);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this.clearPersistentRunTimeout(params.terminalId);
|
|
263
|
+
this.clearPersistentIdleTimeout(params.terminalId);
|
|
264
|
+
await this.deps.terminalBroker.closeTerminal(params.agentNodeId, params.terminalId);
|
|
265
|
+
this.finishActivePersistentRun(params.toolId, params.terminalId, {
|
|
266
|
+
status: 'timed_out',
|
|
267
|
+
signal: params.signal ?? 'timeout',
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
syncStatusBestEffort(toolId, completion) {
|
|
272
|
+
this.deps.latestStateProjector.syncStatusBestEffort(toolId, workspaceToolRuntimeStateToPanelStatus(deriveWorkspaceToolRuntimeState({
|
|
273
|
+
activeTerminalId: null,
|
|
274
|
+
lastRunStatus: completion.status,
|
|
275
|
+
})));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
export function persistentRuntimeActionTimeoutMs(action) {
|
|
279
|
+
return action.maxRunSeconds ? action.maxRunSeconds * 1000 : PERSISTENT_RUNTIME_ACTION_TIMEOUT_MS;
|
|
280
|
+
}
|
|
281
|
+
export function defaultPersistentRuntimeIdleTtlSeconds() {
|
|
282
|
+
return DEFAULT_PERSISTENT_RUNTIME_IDLE_TTL_SECONDS;
|
|
283
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|