@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,431 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { CLEARED_TASK_ROOT_TARGET_GLOB } from './clearedTaskRoots.js';
|
|
3
|
+
import { resolveChannelFromTarget, resolveThreadRootId } from './conversationTargets.js';
|
|
4
|
+
function normalizeThreadRootId(threadRootId) {
|
|
5
|
+
return threadRootId?.trim() ?? '';
|
|
6
|
+
}
|
|
7
|
+
function normalizePositiveSeq(value) {
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
10
|
+
return null;
|
|
11
|
+
return Math.floor(parsed);
|
|
12
|
+
}
|
|
13
|
+
function sharedTargetKey() {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
function activationSeenSeq(metadata) {
|
|
17
|
+
if (!metadata)
|
|
18
|
+
return null;
|
|
19
|
+
const candidates = [
|
|
20
|
+
normalizePositiveSeq(metadata.triggerMessage?.seq),
|
|
21
|
+
normalizePositiveSeq(metadata.mentionSuppression?.triggerSeq),
|
|
22
|
+
normalizePositiveSeq(metadata.peerInboxAggregate?.lastSeq),
|
|
23
|
+
normalizePositiveSeq(metadata.ownerAwareness?.triggerSeq),
|
|
24
|
+
].filter((seq) => seq !== null);
|
|
25
|
+
if (candidates.length === 0)
|
|
26
|
+
return null;
|
|
27
|
+
return Math.max(...candidates);
|
|
28
|
+
}
|
|
29
|
+
function peerInboxItemSeenSeq(db, metadata) {
|
|
30
|
+
const itemMessageIds = metadata?.peerInboxAggregate?.itemMessageIds
|
|
31
|
+
?.filter((messageId) => typeof messageId === 'string' && messageId.trim().length > 0)
|
|
32
|
+
.map((messageId) => messageId.trim());
|
|
33
|
+
if (!itemMessageIds || itemMessageIds.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
const placeholders = itemMessageIds.map(() => '?').join(', ');
|
|
36
|
+
const row = db.prepare(`SELECT MAX(seq) as maxSeq
|
|
37
|
+
FROM channel_messages
|
|
38
|
+
WHERE message_id IN (${placeholders})`).get(...itemMessageIds);
|
|
39
|
+
return normalizePositiveSeq(row?.maxSeq);
|
|
40
|
+
}
|
|
41
|
+
function runActivationBaselineForSurface(db, params) {
|
|
42
|
+
const row = db.prepare(`SELECT activation_metadata_json as activationMetadataJson,
|
|
43
|
+
reply_target as replyTarget
|
|
44
|
+
FROM run_debug_inputs
|
|
45
|
+
WHERE run_id = ?
|
|
46
|
+
ORDER BY created_at DESC, rowid DESC
|
|
47
|
+
LIMIT 1`).get(params.runId);
|
|
48
|
+
if (!row?.activationMetadataJson)
|
|
49
|
+
return null;
|
|
50
|
+
const replyTarget = row.replyTarget?.trim() || null;
|
|
51
|
+
if (replyTarget) {
|
|
52
|
+
const replyChannelId = resolveChannelFromTarget(replyTarget, db);
|
|
53
|
+
if (replyChannelId && replyChannelId !== params.channelId)
|
|
54
|
+
return null;
|
|
55
|
+
const replyThreadRootId = normalizeThreadRootId(resolveThreadRootId(replyTarget));
|
|
56
|
+
if (replyThreadRootId !== normalizeThreadRootId(params.threadRootId))
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const metadata = JSON.parse(row.activationMetadataJson);
|
|
61
|
+
const metadataSeq = activationSeenSeq(metadata);
|
|
62
|
+
const itemSeq = peerInboxItemSeenSeq(db, metadata);
|
|
63
|
+
return Math.max(metadataSeq ?? 0, itemSeq ?? 0) || null;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function resolveRunWatermarkIdentity(db, agentId, params) {
|
|
70
|
+
const requestedRunId = params.runId?.trim() || null;
|
|
71
|
+
const requestedConversationId = params.conversationId?.trim() || null;
|
|
72
|
+
if (requestedRunId) {
|
|
73
|
+
const row = db.prepare(`SELECT r.run_id as runId,
|
|
74
|
+
c.id as conversationId,
|
|
75
|
+
c.agent_id as agentId
|
|
76
|
+
FROM runs r
|
|
77
|
+
LEFT JOIN conversations c ON c.session_key = r.session_key
|
|
78
|
+
WHERE r.run_id = ?
|
|
79
|
+
LIMIT 1`).get(requestedRunId);
|
|
80
|
+
if (!row || row.agentId !== agentId)
|
|
81
|
+
return null;
|
|
82
|
+
if (requestedConversationId && row.conversationId !== requestedConversationId)
|
|
83
|
+
return null;
|
|
84
|
+
return { runId: row.runId, conversationId: row.conversationId };
|
|
85
|
+
}
|
|
86
|
+
if (requestedConversationId) {
|
|
87
|
+
const row = db.prepare(`SELECT c.id as conversationId,
|
|
88
|
+
r.run_id as runId
|
|
89
|
+
FROM conversations c
|
|
90
|
+
JOIN runs r ON r.session_key = c.session_key
|
|
91
|
+
WHERE c.id = ?
|
|
92
|
+
AND c.agent_id = ?
|
|
93
|
+
AND r.ended_at IS NULL
|
|
94
|
+
ORDER BY r.started_at DESC, r.rowid DESC
|
|
95
|
+
LIMIT 1`).get(requestedConversationId, agentId);
|
|
96
|
+
return row ? { runId: row.runId, conversationId: row.conversationId } : null;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
export function getRunSurfaceSeenSeq(db, params) {
|
|
101
|
+
const row = db.prepare(`SELECT seen_up_to_seq as seenUpToSeq
|
|
102
|
+
FROM agent_run_surface_watermarks
|
|
103
|
+
WHERE run_id = ?
|
|
104
|
+
AND channel_id = ?
|
|
105
|
+
AND thread_root_id = ?
|
|
106
|
+
AND target_key = ?
|
|
107
|
+
LIMIT 1`).get(params.runId, params.channelId, normalizeThreadRootId(params.threadRootId), params.targetKey ?? sharedTargetKey());
|
|
108
|
+
return row ? Math.max(0, Math.floor(row.seenUpToSeq)) : null;
|
|
109
|
+
}
|
|
110
|
+
export function bumpRunSurfaceWatermark(db, params) {
|
|
111
|
+
const seenUpToSeq = normalizePositiveSeq(params.seenUpToSeq);
|
|
112
|
+
if (seenUpToSeq === null)
|
|
113
|
+
return;
|
|
114
|
+
const now = params.now ?? Date.now();
|
|
115
|
+
db.prepare(`INSERT INTO agent_run_surface_watermarks(
|
|
116
|
+
run_id, agent_id, conversation_id, channel_id, thread_root_id, target_key,
|
|
117
|
+
seen_up_to_seq, source, updated_at
|
|
118
|
+
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
119
|
+
ON CONFLICT(run_id, channel_id, thread_root_id, target_key) DO UPDATE SET
|
|
120
|
+
seen_up_to_seq = MAX(agent_run_surface_watermarks.seen_up_to_seq, excluded.seen_up_to_seq),
|
|
121
|
+
agent_id = excluded.agent_id,
|
|
122
|
+
conversation_id = COALESCE(excluded.conversation_id, agent_run_surface_watermarks.conversation_id),
|
|
123
|
+
source = excluded.source,
|
|
124
|
+
updated_at = excluded.updated_at`).run(params.runId, params.agentId, params.conversationId ?? null, params.channelId, normalizeThreadRootId(params.threadRootId), params.targetKey ?? sharedTargetKey(), seenUpToSeq, params.source, now);
|
|
125
|
+
}
|
|
126
|
+
export function ensureRunSurfaceWatermarkFromActivation(db, params) {
|
|
127
|
+
if (params.channelId.startsWith('dm:'))
|
|
128
|
+
return null;
|
|
129
|
+
const existing = getRunSurfaceSeenSeq(db, {
|
|
130
|
+
runId: params.runId,
|
|
131
|
+
channelId: params.channelId,
|
|
132
|
+
threadRootId: params.threadRootId,
|
|
133
|
+
targetKey: sharedTargetKey(),
|
|
134
|
+
});
|
|
135
|
+
const metadataSeq = activationSeenSeq(params.metadata);
|
|
136
|
+
const itemSeq = peerInboxItemSeenSeq(db, params.metadata);
|
|
137
|
+
const baselineSeq = Math.max(metadataSeq ?? 0, itemSeq ?? 0) || null;
|
|
138
|
+
if (baselineSeq !== null) {
|
|
139
|
+
bumpRunSurfaceWatermark(db, {
|
|
140
|
+
runId: params.runId,
|
|
141
|
+
agentId: params.agentId,
|
|
142
|
+
conversationId: params.conversationId,
|
|
143
|
+
channelId: params.channelId,
|
|
144
|
+
threadRootId: params.threadRootId,
|
|
145
|
+
targetKey: sharedTargetKey(),
|
|
146
|
+
seenUpToSeq: baselineSeq,
|
|
147
|
+
source: 'activation',
|
|
148
|
+
now: params.now,
|
|
149
|
+
});
|
|
150
|
+
return Math.max(existing ?? 0, baselineSeq);
|
|
151
|
+
}
|
|
152
|
+
return existing;
|
|
153
|
+
}
|
|
154
|
+
export function recordRunSurfaceRowsSeen(db, params) {
|
|
155
|
+
if (!params.identity || params.rows.length === 0)
|
|
156
|
+
return;
|
|
157
|
+
const maxSeqBySurface = new Map();
|
|
158
|
+
for (const row of params.rows) {
|
|
159
|
+
if (!row.channelId || row.channelId.startsWith('dm:'))
|
|
160
|
+
continue;
|
|
161
|
+
const threadRootId = normalizeThreadRootId(params.threadRootIdOverride ?? row.threadRootId);
|
|
162
|
+
const key = `${row.channelId}::${threadRootId}`;
|
|
163
|
+
const current = maxSeqBySurface.get(key);
|
|
164
|
+
if (!current || row.seq > current.maxSeq) {
|
|
165
|
+
maxSeqBySurface.set(key, { channelId: row.channelId, threadRootId, maxSeq: row.seq });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const surface of maxSeqBySurface.values()) {
|
|
169
|
+
bumpRunSurfaceWatermark(db, {
|
|
170
|
+
runId: params.identity.runId,
|
|
171
|
+
agentId: params.agentId,
|
|
172
|
+
conversationId: params.identity.conversationId,
|
|
173
|
+
channelId: surface.channelId,
|
|
174
|
+
threadRootId: surface.threadRootId,
|
|
175
|
+
targetKey: sharedTargetKey(),
|
|
176
|
+
seenUpToSeq: surface.maxSeq,
|
|
177
|
+
source: params.source,
|
|
178
|
+
now: params.now,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function recordRunSurfaceCatchUpRowsSeen(db, params) {
|
|
183
|
+
if (!params.identity || params.rows.length === 0 || params.channelId.startsWith('dm:'))
|
|
184
|
+
return;
|
|
185
|
+
const afterSeq = Math.max(0, Math.floor(params.afterSeq));
|
|
186
|
+
const threadRootId = normalizeThreadRootId(params.threadRootId);
|
|
187
|
+
const currentSeenSeq = getRunSurfaceSeenSeq(db, {
|
|
188
|
+
runId: params.identity.runId,
|
|
189
|
+
channelId: params.channelId,
|
|
190
|
+
threadRootId,
|
|
191
|
+
targetKey: sharedTargetKey(),
|
|
192
|
+
});
|
|
193
|
+
if (currentSeenSeq === null) {
|
|
194
|
+
const activationBaseline = runActivationBaselineForSurface(db, {
|
|
195
|
+
runId: params.identity.runId,
|
|
196
|
+
channelId: params.channelId,
|
|
197
|
+
threadRootId,
|
|
198
|
+
});
|
|
199
|
+
if (activationBaseline === afterSeq) {
|
|
200
|
+
bumpRunSurfaceWatermark(db, {
|
|
201
|
+
runId: params.identity.runId,
|
|
202
|
+
agentId: params.agentId,
|
|
203
|
+
conversationId: params.identity.conversationId,
|
|
204
|
+
channelId: params.channelId,
|
|
205
|
+
threadRootId,
|
|
206
|
+
targetKey: sharedTargetKey(),
|
|
207
|
+
seenUpToSeq: activationBaseline,
|
|
208
|
+
source: 'activation',
|
|
209
|
+
now: params.now,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const effectiveCurrentSeenSeq = getRunSurfaceSeenSeq(db, {
|
|
214
|
+
runId: params.identity.runId,
|
|
215
|
+
channelId: params.channelId,
|
|
216
|
+
threadRootId,
|
|
217
|
+
targetKey: sharedTargetKey(),
|
|
218
|
+
});
|
|
219
|
+
if (effectiveCurrentSeenSeq !== afterSeq)
|
|
220
|
+
return;
|
|
221
|
+
let maxSeq = null;
|
|
222
|
+
for (const row of params.rows) {
|
|
223
|
+
if (row.channelId !== params.channelId)
|
|
224
|
+
continue;
|
|
225
|
+
if (row.threadRootId !== undefined && normalizeThreadRootId(row.threadRootId) !== threadRootId)
|
|
226
|
+
continue;
|
|
227
|
+
if (row.seq <= afterSeq)
|
|
228
|
+
continue;
|
|
229
|
+
maxSeq = maxSeq === null ? row.seq : Math.max(maxSeq, row.seq);
|
|
230
|
+
}
|
|
231
|
+
if (maxSeq === null)
|
|
232
|
+
return;
|
|
233
|
+
bumpRunSurfaceWatermark(db, {
|
|
234
|
+
runId: params.identity.runId,
|
|
235
|
+
agentId: params.agentId,
|
|
236
|
+
conversationId: params.identity.conversationId,
|
|
237
|
+
channelId: params.channelId,
|
|
238
|
+
threadRootId,
|
|
239
|
+
targetKey: sharedTargetKey(),
|
|
240
|
+
seenUpToSeq: maxSeq,
|
|
241
|
+
source: params.source,
|
|
242
|
+
now: params.now,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
export function findFreshnessConflict(db, params) {
|
|
246
|
+
if (params.channelId.startsWith('dm:'))
|
|
247
|
+
return null;
|
|
248
|
+
const seenUpToSeq = Math.max(0, Math.floor(params.seenUpToSeq));
|
|
249
|
+
const threadRootId = normalizeThreadRootId(params.threadRootId);
|
|
250
|
+
const scopeClause = threadRootId
|
|
251
|
+
? `(cm.thread_root_id = ? OR (cm.thread_root_id IS NULL AND cm.target = ?))`
|
|
252
|
+
: `cm.thread_root_id IS NULL AND cm.target NOT GLOB ?`;
|
|
253
|
+
const scopeParams = threadRootId
|
|
254
|
+
? [threadRootId, params.resolvedTarget]
|
|
255
|
+
: [CLEARED_TASK_ROOT_TARGET_GLOB];
|
|
256
|
+
const currentRunRow = db.prepare(`SELECT started_at as startedAt
|
|
257
|
+
FROM runs
|
|
258
|
+
WHERE run_id = ?
|
|
259
|
+
LIMIT 1`).get(params.currentRunId);
|
|
260
|
+
const currentRunStartedAt = Math.max(0, Math.floor(currentRunRow?.startedAt ?? 0));
|
|
261
|
+
const aggregate = db.prepare(`SELECT MIN(cm.seq) as blockedFromSeq,
|
|
262
|
+
MAX(cm.seq) as blockedToSeq
|
|
263
|
+
FROM channel_messages cm
|
|
264
|
+
WHERE cm.channel_id = ?
|
|
265
|
+
AND ${scopeClause}
|
|
266
|
+
AND cm.seq > ?
|
|
267
|
+
AND (cm.run_id IS NULL OR cm.run_id != ?)
|
|
268
|
+
AND (cm.sender_id != ? OR cm.created_at > ?)
|
|
269
|
+
AND cm.sender_type IN ('user', 'agent')`).get(params.channelId, ...scopeParams, seenUpToSeq, params.currentRunId, params.agentId, currentRunStartedAt);
|
|
270
|
+
if (!aggregate?.blockedFromSeq || !aggregate.blockedToSeq)
|
|
271
|
+
return null;
|
|
272
|
+
const rows = db.prepare(`SELECT cm.message_id as id,
|
|
273
|
+
cm.sender_id as senderId,
|
|
274
|
+
cm.sender_name as senderName,
|
|
275
|
+
cm.sender_type as senderType,
|
|
276
|
+
cm.target,
|
|
277
|
+
cm.content,
|
|
278
|
+
cm.seq,
|
|
279
|
+
cm.created_at as createdAt
|
|
280
|
+
FROM channel_messages cm
|
|
281
|
+
WHERE cm.channel_id = ?
|
|
282
|
+
AND ${scopeClause}
|
|
283
|
+
AND cm.seq > ?
|
|
284
|
+
AND (cm.run_id IS NULL OR cm.run_id != ?)
|
|
285
|
+
AND (cm.sender_id != ? OR cm.created_at > ?)
|
|
286
|
+
AND cm.sender_type IN ('user', 'agent')
|
|
287
|
+
ORDER BY cm.seq ASC
|
|
288
|
+
LIMIT ?`).all(params.channelId, ...scopeParams, seenUpToSeq, params.currentRunId, params.agentId, currentRunStartedAt, params.limit ?? 5);
|
|
289
|
+
return {
|
|
290
|
+
blockedFromSeq: aggregate.blockedFromSeq,
|
|
291
|
+
blockedToSeq: aggregate.blockedToSeq,
|
|
292
|
+
latestMessages: rows.map((row) => ({
|
|
293
|
+
id: row.id,
|
|
294
|
+
senderId: row.senderId,
|
|
295
|
+
senderName: row.senderName,
|
|
296
|
+
senderType: row.senderType,
|
|
297
|
+
target: row.target,
|
|
298
|
+
content: row.content,
|
|
299
|
+
seq: row.seq,
|
|
300
|
+
createdAt: new Date(row.createdAt).toISOString(),
|
|
301
|
+
})),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
export function createHeldMessageDraft(db, params) {
|
|
305
|
+
const draftId = randomUUID();
|
|
306
|
+
const now = params.now ?? Date.now();
|
|
307
|
+
db.prepare(`INSERT INTO held_message_drafts(
|
|
308
|
+
draft_id, agent_id, conversation_id, run_id, resolved_target, channel_id,
|
|
309
|
+
thread_root_id, target_key, effective_kind, peer_delivery, attachment_ids_json,
|
|
310
|
+
content, seen_up_to_seq, blocked_from_seq, blocked_to_seq,
|
|
311
|
+
blocking_summary_json, status, created_at, updated_at
|
|
312
|
+
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'held', ?, ?)`).run(draftId, params.agentId, params.conversationId ?? null, params.runId ?? null, params.resolvedTarget, params.channelId, normalizeThreadRootId(params.threadRootId), sharedTargetKey(), params.effectiveKind ?? null, params.peerDelivery ?? null, params.attachmentIdsJson ?? null, params.content, Math.max(0, Math.floor(params.seenUpToSeq)), params.conflict.blockedFromSeq, params.conflict.blockedToSeq, JSON.stringify(params.conflict.latestMessages), now, now);
|
|
313
|
+
return draftId;
|
|
314
|
+
}
|
|
315
|
+
export function createGuardedMessageDraft(db, params) {
|
|
316
|
+
const draftId = randomUUID();
|
|
317
|
+
const now = params.now ?? Date.now();
|
|
318
|
+
const seenUpToSeq = Math.max(0, Math.floor(params.seenUpToSeq ?? 0));
|
|
319
|
+
db.prepare(`INSERT INTO held_message_drafts(
|
|
320
|
+
draft_id, agent_id, conversation_id, run_id, resolved_target, channel_id,
|
|
321
|
+
thread_root_id, target_key, effective_kind, peer_delivery, attachment_ids_json,
|
|
322
|
+
content, seen_up_to_seq, blocked_from_seq, blocked_to_seq,
|
|
323
|
+
blocking_summary_json, status, created_at, updated_at
|
|
324
|
+
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'held', ?, ?)`).run(draftId, params.agentId, params.conversationId ?? null, params.runId ?? null, params.resolvedTarget, params.channelId, normalizeThreadRootId(params.threadRootId), sharedTargetKey(), params.effectiveKind ?? null, params.peerDelivery ?? null, params.attachmentIdsJson ?? null, params.content, seenUpToSeq, seenUpToSeq, seenUpToSeq, JSON.stringify(params.blockingSummary), now, now);
|
|
325
|
+
return draftId;
|
|
326
|
+
}
|
|
327
|
+
function parseJsonOrNull(value) {
|
|
328
|
+
if (!value)
|
|
329
|
+
return null;
|
|
330
|
+
try {
|
|
331
|
+
return JSON.parse(value);
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function parseAttachmentIds(value) {
|
|
338
|
+
const parsed = parseJsonOrNull(value);
|
|
339
|
+
if (!Array.isArray(parsed))
|
|
340
|
+
return [];
|
|
341
|
+
return parsed.filter((item) => typeof item === 'string' && item.trim().length > 0);
|
|
342
|
+
}
|
|
343
|
+
function blockingReasonFromSummary(summary, row) {
|
|
344
|
+
if (summary && typeof summary === 'object' && !Array.isArray(summary)) {
|
|
345
|
+
const reason = summary.reason;
|
|
346
|
+
return typeof reason === 'string' && reason.trim().length > 0 ? reason : null;
|
|
347
|
+
}
|
|
348
|
+
if (Array.isArray(summary)) {
|
|
349
|
+
return 'stale_surface_conflict';
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
function contentPreview(content) {
|
|
354
|
+
const normalized = content.replace(/\s+/g, ' ').trim();
|
|
355
|
+
return normalized.length > 240 ? `${normalized.slice(0, 240)}...` : normalized;
|
|
356
|
+
}
|
|
357
|
+
export function listHeldMessageDraftsForAgent(db, params) {
|
|
358
|
+
const where = ['agent_id = ?'];
|
|
359
|
+
const args = [params.agentId];
|
|
360
|
+
const runId = params.runId?.trim();
|
|
361
|
+
if (runId) {
|
|
362
|
+
where.push('run_id = ?');
|
|
363
|
+
args.push(runId);
|
|
364
|
+
}
|
|
365
|
+
const draftId = params.draftId?.trim();
|
|
366
|
+
if (draftId) {
|
|
367
|
+
where.push('draft_id = ?');
|
|
368
|
+
args.push(draftId);
|
|
369
|
+
}
|
|
370
|
+
const status = params.status?.trim();
|
|
371
|
+
if (status) {
|
|
372
|
+
where.push('status = ?');
|
|
373
|
+
args.push(status);
|
|
374
|
+
}
|
|
375
|
+
const limit = Math.max(1, Math.min(Math.floor(params.limit ?? 20), 100));
|
|
376
|
+
const rows = db.prepare(`SELECT draft_id as draftId,
|
|
377
|
+
agent_id as agentId,
|
|
378
|
+
conversation_id as conversationId,
|
|
379
|
+
run_id as runId,
|
|
380
|
+
resolved_target as resolvedTarget,
|
|
381
|
+
channel_id as channelId,
|
|
382
|
+
thread_root_id as threadRootId,
|
|
383
|
+
effective_kind as effectiveKind,
|
|
384
|
+
peer_delivery as peerDelivery,
|
|
385
|
+
attachment_ids_json as attachmentIdsJson,
|
|
386
|
+
content,
|
|
387
|
+
seen_up_to_seq as seenUpToSeq,
|
|
388
|
+
blocked_from_seq as blockedFromSeq,
|
|
389
|
+
blocked_to_seq as blockedToSeq,
|
|
390
|
+
blocking_summary_json as blockingSummaryJson,
|
|
391
|
+
status,
|
|
392
|
+
created_at as createdAt,
|
|
393
|
+
updated_at as updatedAt
|
|
394
|
+
FROM held_message_drafts
|
|
395
|
+
WHERE ${where.join(' AND ')}
|
|
396
|
+
ORDER BY created_at DESC, rowid DESC
|
|
397
|
+
LIMIT ?`).all(...args, limit);
|
|
398
|
+
return rows.map((row) => {
|
|
399
|
+
const blockingSummary = parseJsonOrNull(row.blockingSummaryJson);
|
|
400
|
+
const base = {
|
|
401
|
+
draftId: row.draftId,
|
|
402
|
+
agentId: row.agentId,
|
|
403
|
+
conversationId: row.conversationId,
|
|
404
|
+
runId: row.runId,
|
|
405
|
+
resolvedTarget: row.resolvedTarget,
|
|
406
|
+
channelId: row.channelId,
|
|
407
|
+
threadRootId: row.threadRootId && row.threadRootId.length > 0 ? row.threadRootId : null,
|
|
408
|
+
effectiveKind: row.effectiveKind,
|
|
409
|
+
peerDelivery: row.peerDelivery,
|
|
410
|
+
attachmentIds: parseAttachmentIds(row.attachmentIdsJson),
|
|
411
|
+
contentPreview: contentPreview(row.content),
|
|
412
|
+
contentLength: row.content.length,
|
|
413
|
+
seenUpToSeq: row.seenUpToSeq,
|
|
414
|
+
blockedFromSeq: row.blockedFromSeq,
|
|
415
|
+
blockedToSeq: row.blockedToSeq,
|
|
416
|
+
blockingReason: blockingReasonFromSummary(blockingSummary, row),
|
|
417
|
+
blockingSummary,
|
|
418
|
+
status: row.status,
|
|
419
|
+
createdAt: row.createdAt,
|
|
420
|
+
updatedAt: row.updatedAt,
|
|
421
|
+
};
|
|
422
|
+
return params.includeContent ? { ...base, content: row.content } : base;
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
export function buildFreshnessReadHistoryInstruction(resolvedTarget, seenUpToSeq, surface = 'bigbang') {
|
|
426
|
+
const escapedTarget = resolvedTarget.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
427
|
+
if (surface === 'mcp') {
|
|
428
|
+
return `read_history(channel="${escapedTarget}", after=${Math.max(0, Math.floor(seenUpToSeq))}, limit=20)`;
|
|
429
|
+
}
|
|
430
|
+
return `bigbang message read --channel "${escapedTarget}" --after ${Math.max(0, Math.floor(seenUpToSeq))} --limit 20`;
|
|
431
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { listAgentRuntimeCapabilities, } from '@bbigbang/protocol';
|
|
2
|
+
function isAgentRuntimeCapability(value) {
|
|
3
|
+
return value === 'activeTurnSteer'
|
|
4
|
+
|| value === 'planApprovalLoop';
|
|
5
|
+
}
|
|
6
|
+
function isAgentType(value) {
|
|
7
|
+
return value === 'claude_acp'
|
|
8
|
+
|| value === 'claude_sdk'
|
|
9
|
+
|| value === 'codex_acp'
|
|
10
|
+
|| value === 'codex_app_server';
|
|
11
|
+
}
|
|
12
|
+
function shouldUseStaticCapabilityFallback(agentType) {
|
|
13
|
+
return agentType !== 'claude_sdk';
|
|
14
|
+
}
|
|
15
|
+
function parseRuntimeDrivers(value) {
|
|
16
|
+
if (!value)
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(value);
|
|
20
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function listEffectiveAgentRuntimeCapabilities(db, params) {
|
|
27
|
+
if (!isAgentType(params.agentType))
|
|
28
|
+
return [];
|
|
29
|
+
const fallback = listAgentRuntimeCapabilities(params.agentType);
|
|
30
|
+
const nodeId = params.nodeId?.trim();
|
|
31
|
+
if (!nodeId)
|
|
32
|
+
return shouldUseStaticCapabilityFallback(params.agentType) ? fallback : [];
|
|
33
|
+
const row = db.prepare(`SELECT runtime_drivers_json as runtimeDriversJson
|
|
34
|
+
FROM node_runtime_snapshots
|
|
35
|
+
WHERE node_id = ?`).get(nodeId);
|
|
36
|
+
const runtimeDrivers = parseRuntimeDrivers(row?.runtimeDriversJson);
|
|
37
|
+
if (!runtimeDrivers)
|
|
38
|
+
return shouldUseStaticCapabilityFallback(params.agentType) ? fallback : [];
|
|
39
|
+
const driver = runtimeDrivers.find((candidate) => candidate?.agentType === params.agentType);
|
|
40
|
+
if (!driver)
|
|
41
|
+
return [];
|
|
42
|
+
if (!Array.isArray(driver.capabilities))
|
|
43
|
+
return shouldUseStaticCapabilityFallback(params.agentType) ? fallback : [];
|
|
44
|
+
return driver.capabilities.filter(isAgentRuntimeCapability);
|
|
45
|
+
}
|
|
46
|
+
export function hasEffectiveAgentRuntimeCapability(db, params) {
|
|
47
|
+
return listEffectiveAgentRuntimeCapabilities(db, params).includes(params.capability);
|
|
48
|
+
}
|