@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,263 @@
|
|
|
1
|
+
import { createPromptContextSection, } from './promptContextSections.js';
|
|
2
|
+
import { deriveTaskLifecycleKindFromStatusChange, extractTaskLifecycleTitle, formatTaskLifecyclePromptLine, inferTaskLifecycleKindFromText, TASK_LIFECYCLE_SOURCE, } from './taskLifecycleMessages.js';
|
|
3
|
+
import { getBoundTaskForThread } from './threadTaskBindings.js';
|
|
4
|
+
const SURFACE_SYSTEM_STATUS_HEADER = '[System status messages on this surface]';
|
|
5
|
+
const DEFAULT_SURFACE_SYSTEM_STATUS_LIMIT = 10;
|
|
6
|
+
const SURFACE_SYSTEM_STATUS_TRUNCATED_HINT = '- Older task status summaries were omitted. Use bigbang task history or bigbang message read if you need full lifecycle details.';
|
|
7
|
+
const INCLUDED_TASK_EVENT_TYPES = [
|
|
8
|
+
'handoff_started',
|
|
9
|
+
'handoff_failed',
|
|
10
|
+
'status_changed',
|
|
11
|
+
];
|
|
12
|
+
export function buildSurfaceSystemStatusSection(db, params) {
|
|
13
|
+
const channelId = params.channelId.trim();
|
|
14
|
+
const replyTarget = params.replyTarget.trim();
|
|
15
|
+
if (!channelId || !replyTarget)
|
|
16
|
+
return null;
|
|
17
|
+
const maxTasks = Math.max(1, params.maxEvents ?? DEFAULT_SURFACE_SYSTEM_STATUS_LIMIT);
|
|
18
|
+
const structuredRows = loadStructuredSurfaceStatusRows(db, {
|
|
19
|
+
channelId,
|
|
20
|
+
replyTarget,
|
|
21
|
+
threadRootId: params.threadRootId ?? null,
|
|
22
|
+
boundTaskId: params.boundTaskId ?? null,
|
|
23
|
+
maxTasks: maxTasks + 1,
|
|
24
|
+
});
|
|
25
|
+
if (structuredRows.length > 0) {
|
|
26
|
+
const truncated = structuredRows.length > maxTasks;
|
|
27
|
+
const visibleRows = takeMostRecentRows(structuredRows, maxTasks);
|
|
28
|
+
return createPromptContextSection('surface_system_status', [
|
|
29
|
+
SURFACE_SYSTEM_STATUS_HEADER,
|
|
30
|
+
...visibleRows.map(renderStructuredSurfaceStatusRow),
|
|
31
|
+
...(truncated ? [SURFACE_SYSTEM_STATUS_TRUNCATED_HINT] : []),
|
|
32
|
+
].join('\n'));
|
|
33
|
+
}
|
|
34
|
+
// New prompt facts should come from structured task_events above. This
|
|
35
|
+
// fallback is only for legacy rows written before task_events were complete;
|
|
36
|
+
// task_lifecycle channel messages remain a UI display projection otherwise.
|
|
37
|
+
if (params.threadRootId)
|
|
38
|
+
return null;
|
|
39
|
+
const legacyRows = loadLegacyRootSurfaceStatusRows(db, {
|
|
40
|
+
channelId,
|
|
41
|
+
replyTarget,
|
|
42
|
+
maxTasks: maxTasks + 1,
|
|
43
|
+
});
|
|
44
|
+
if (legacyRows.length === 0)
|
|
45
|
+
return null;
|
|
46
|
+
const truncated = legacyRows.length > maxTasks;
|
|
47
|
+
const visibleRows = takeMostRecentRows(legacyRows, maxTasks);
|
|
48
|
+
return createPromptContextSection('surface_system_status', [
|
|
49
|
+
SURFACE_SYSTEM_STATUS_HEADER,
|
|
50
|
+
...visibleRows.map(renderLegacySurfaceStatusRow),
|
|
51
|
+
...(truncated ? [SURFACE_SYSTEM_STATUS_TRUNCATED_HINT] : []),
|
|
52
|
+
].join('\n'));
|
|
53
|
+
}
|
|
54
|
+
function loadStructuredSurfaceStatusRows(db, params) {
|
|
55
|
+
const eventTypesSql = INCLUDED_TASK_EVENT_TYPES.map(() => '?').join(', ');
|
|
56
|
+
if (params.threadRootId) {
|
|
57
|
+
const boundTaskId = params.boundTaskId?.trim()
|
|
58
|
+
|| getBoundTaskForThread(db, {
|
|
59
|
+
channelId: params.channelId,
|
|
60
|
+
threadRootId: params.threadRootId,
|
|
61
|
+
})?.taskId;
|
|
62
|
+
if (!boundTaskId)
|
|
63
|
+
return [];
|
|
64
|
+
return reverseChronologicalRows(db.prepare(`SELECT e.event_id as eventId,
|
|
65
|
+
e.task_id as taskId,
|
|
66
|
+
e.event_type as eventType,
|
|
67
|
+
e.actor_name as actorName,
|
|
68
|
+
e.from_status as fromStatus,
|
|
69
|
+
e.to_status as toStatus,
|
|
70
|
+
e.claimed_by_name_after as claimedByNameAfter,
|
|
71
|
+
e.thread_target as threadTarget,
|
|
72
|
+
e.created_at as createdAt,
|
|
73
|
+
t.task_number as taskNumber,
|
|
74
|
+
t.title as title
|
|
75
|
+
FROM task_events e
|
|
76
|
+
JOIN tasks t ON t.task_id = e.task_id
|
|
77
|
+
WHERE e.task_id = ?
|
|
78
|
+
AND e.event_type IN (${eventTypesSql})
|
|
79
|
+
ORDER BY e.created_at DESC, e.event_id DESC
|
|
80
|
+
LIMIT 1`).all(boundTaskId, ...INCLUDED_TASK_EVENT_TYPES));
|
|
81
|
+
}
|
|
82
|
+
if (params.channelId.startsWith('dm:') && params.replyTarget.startsWith('dm:@')) {
|
|
83
|
+
return reverseChronologicalRows(db.prepare(`SELECT eventId,
|
|
84
|
+
taskId,
|
|
85
|
+
eventType,
|
|
86
|
+
actorName,
|
|
87
|
+
fromStatus,
|
|
88
|
+
toStatus,
|
|
89
|
+
claimedByNameAfter,
|
|
90
|
+
threadTarget,
|
|
91
|
+
createdAt,
|
|
92
|
+
taskNumber,
|
|
93
|
+
title
|
|
94
|
+
FROM (
|
|
95
|
+
SELECT e.event_id as eventId,
|
|
96
|
+
e.task_id as taskId,
|
|
97
|
+
e.event_type as eventType,
|
|
98
|
+
e.actor_name as actorName,
|
|
99
|
+
e.from_status as fromStatus,
|
|
100
|
+
e.to_status as toStatus,
|
|
101
|
+
e.claimed_by_name_after as claimedByNameAfter,
|
|
102
|
+
e.thread_target as threadTarget,
|
|
103
|
+
e.created_at as createdAt,
|
|
104
|
+
t.task_number as taskNumber,
|
|
105
|
+
t.title as title,
|
|
106
|
+
ROW_NUMBER() OVER (
|
|
107
|
+
PARTITION BY e.task_id
|
|
108
|
+
ORDER BY e.created_at DESC, e.event_id DESC
|
|
109
|
+
) as taskEventRank
|
|
110
|
+
FROM task_events e
|
|
111
|
+
JOIN tasks t ON t.task_id = e.task_id
|
|
112
|
+
LEFT JOIN channel_messages cm ON cm.message_id = t.message_id
|
|
113
|
+
WHERE e.channel_id = ?
|
|
114
|
+
AND COALESCE(NULLIF(t.dm_target, ''), cm.target) = ?
|
|
115
|
+
AND e.event_type IN (${eventTypesSql})
|
|
116
|
+
)
|
|
117
|
+
WHERE taskEventRank = 1
|
|
118
|
+
ORDER BY createdAt DESC, eventId DESC
|
|
119
|
+
LIMIT ?`).all(params.channelId, params.replyTarget, ...INCLUDED_TASK_EVENT_TYPES, params.maxTasks));
|
|
120
|
+
}
|
|
121
|
+
return reverseChronologicalRows(db.prepare(`SELECT eventId,
|
|
122
|
+
taskId,
|
|
123
|
+
eventType,
|
|
124
|
+
actorName,
|
|
125
|
+
fromStatus,
|
|
126
|
+
toStatus,
|
|
127
|
+
claimedByNameAfter,
|
|
128
|
+
threadTarget,
|
|
129
|
+
createdAt,
|
|
130
|
+
taskNumber,
|
|
131
|
+
title
|
|
132
|
+
FROM (
|
|
133
|
+
SELECT e.event_id as eventId,
|
|
134
|
+
e.task_id as taskId,
|
|
135
|
+
e.event_type as eventType,
|
|
136
|
+
e.actor_name as actorName,
|
|
137
|
+
e.from_status as fromStatus,
|
|
138
|
+
e.to_status as toStatus,
|
|
139
|
+
e.claimed_by_name_after as claimedByNameAfter,
|
|
140
|
+
e.thread_target as threadTarget,
|
|
141
|
+
e.created_at as createdAt,
|
|
142
|
+
t.task_number as taskNumber,
|
|
143
|
+
t.title as title,
|
|
144
|
+
ROW_NUMBER() OVER (
|
|
145
|
+
PARTITION BY e.task_id
|
|
146
|
+
ORDER BY e.created_at DESC, e.event_id DESC
|
|
147
|
+
) as taskEventRank
|
|
148
|
+
FROM task_events e
|
|
149
|
+
JOIN tasks t ON t.task_id = e.task_id
|
|
150
|
+
WHERE e.channel_id = ?
|
|
151
|
+
AND e.event_type IN (${eventTypesSql})
|
|
152
|
+
)
|
|
153
|
+
WHERE taskEventRank = 1
|
|
154
|
+
ORDER BY createdAt DESC, eventId DESC
|
|
155
|
+
LIMIT ?`).all(params.channelId, ...INCLUDED_TASK_EVENT_TYPES, params.maxTasks));
|
|
156
|
+
}
|
|
157
|
+
function loadLegacyRootSurfaceStatusRows(db, params) {
|
|
158
|
+
// Compatibility-only path: parse historical task_lifecycle UI messages when
|
|
159
|
+
// no structured task_events exist for the root surface.
|
|
160
|
+
return db.prepare(`SELECT senderName,
|
|
161
|
+
content,
|
|
162
|
+
createdAt,
|
|
163
|
+
seq,
|
|
164
|
+
taskNumber,
|
|
165
|
+
taskStatus,
|
|
166
|
+
taskAssigneeName
|
|
167
|
+
FROM (
|
|
168
|
+
SELECT cm.sender_name as senderName,
|
|
169
|
+
cm.content as content,
|
|
170
|
+
cm.created_at as createdAt,
|
|
171
|
+
cm.seq as seq,
|
|
172
|
+
COALESCE(
|
|
173
|
+
t.task_number,
|
|
174
|
+
CAST(substr(cm.content, 2, instr(cm.content, ' ') - 2) AS INTEGER)
|
|
175
|
+
) as taskNumber,
|
|
176
|
+
t.status as taskStatus,
|
|
177
|
+
t.claimed_by_name as taskAssigneeName,
|
|
178
|
+
ROW_NUMBER() OVER (
|
|
179
|
+
PARTITION BY COALESCE(
|
|
180
|
+
t.task_id,
|
|
181
|
+
CAST(substr(cm.content, 2, instr(cm.content, ' ') - 2) AS TEXT),
|
|
182
|
+
cm.message_id
|
|
183
|
+
)
|
|
184
|
+
ORDER BY cm.created_at DESC, cm.seq DESC
|
|
185
|
+
) as taskEventRank
|
|
186
|
+
FROM channel_messages cm
|
|
187
|
+
LEFT JOIN tasks t ON t.channel_id = cm.channel_id
|
|
188
|
+
AND t.task_number = CAST(substr(cm.content, 2, instr(cm.content, ' ') - 2) AS INTEGER)
|
|
189
|
+
WHERE cm.channel_id = ?
|
|
190
|
+
AND cm.target = ?
|
|
191
|
+
AND cm.thread_root_id IS NULL
|
|
192
|
+
AND cm.message_source = ?
|
|
193
|
+
)
|
|
194
|
+
WHERE taskEventRank = 1
|
|
195
|
+
ORDER BY createdAt DESC, seq DESC
|
|
196
|
+
LIMIT ?`).all(params.channelId, params.replyTarget, TASK_LIFECYCLE_SOURCE, params.maxTasks)
|
|
197
|
+
.reverse();
|
|
198
|
+
}
|
|
199
|
+
function reverseChronologicalRows(rows) {
|
|
200
|
+
return rows.reverse();
|
|
201
|
+
}
|
|
202
|
+
function takeMostRecentRows(rows, maxRows) {
|
|
203
|
+
return rows.length > maxRows ? rows.slice(rows.length - maxRows) : rows;
|
|
204
|
+
}
|
|
205
|
+
function renderStructuredSurfaceStatusRow(row) {
|
|
206
|
+
const kind = kindForStructuredRow(row);
|
|
207
|
+
if (!kind) {
|
|
208
|
+
return `- ${formatTaskLifecyclePromptLine({
|
|
209
|
+
createdAt: row.createdAt,
|
|
210
|
+
kind: 'in_progress',
|
|
211
|
+
taskNumber: row.taskNumber,
|
|
212
|
+
title: row.title,
|
|
213
|
+
actorName: displayActorForStructuredRow(row),
|
|
214
|
+
})}`;
|
|
215
|
+
}
|
|
216
|
+
return `- ${formatTaskLifecyclePromptLine({
|
|
217
|
+
createdAt: row.createdAt,
|
|
218
|
+
kind,
|
|
219
|
+
taskNumber: row.taskNumber,
|
|
220
|
+
title: row.title,
|
|
221
|
+
actorName: displayActorForStructuredRow(row),
|
|
222
|
+
})}`;
|
|
223
|
+
}
|
|
224
|
+
function renderLegacySurfaceStatusRow(row) {
|
|
225
|
+
const inferred = inferTaskLifecycleKindFromText(row.content, row.taskStatus);
|
|
226
|
+
const parsed = extractTaskLifecycleTitle(row.content);
|
|
227
|
+
if (!inferred) {
|
|
228
|
+
const actor = displayActor(row.senderName, row.taskAssigneeName);
|
|
229
|
+
return `- ${formatTaskLifecyclePromptLine({
|
|
230
|
+
createdAt: row.createdAt,
|
|
231
|
+
kind: 'in_progress',
|
|
232
|
+
taskNumber: row.taskNumber ?? parsed.taskNumber,
|
|
233
|
+
title: parsed.title,
|
|
234
|
+
actorName: actor,
|
|
235
|
+
})}`;
|
|
236
|
+
}
|
|
237
|
+
return `- ${formatTaskLifecyclePromptLine({
|
|
238
|
+
createdAt: row.createdAt,
|
|
239
|
+
kind: inferred,
|
|
240
|
+
taskNumber: row.taskNumber ?? parsed.taskNumber,
|
|
241
|
+
title: parsed.title,
|
|
242
|
+
actorName: displayActor(row.senderName, row.taskAssigneeName),
|
|
243
|
+
})}`;
|
|
244
|
+
}
|
|
245
|
+
function kindForStructuredRow(row) {
|
|
246
|
+
if (row.eventType === 'handoff_started')
|
|
247
|
+
return 'started';
|
|
248
|
+
if (row.eventType === 'handoff_failed')
|
|
249
|
+
return 'handoff_failed';
|
|
250
|
+
return deriveTaskLifecycleKindFromStatusChange(row.fromStatus ?? '', row.toStatus ?? '');
|
|
251
|
+
}
|
|
252
|
+
function displayActorForStructuredRow(row) {
|
|
253
|
+
return displayActor(row.actorName, row.claimedByNameAfter);
|
|
254
|
+
}
|
|
255
|
+
function displayActor(actorName, fallbackName) {
|
|
256
|
+
const normalized = normalizeInline(actorName);
|
|
257
|
+
if (normalized && normalized.toLowerCase() !== 'system')
|
|
258
|
+
return normalized;
|
|
259
|
+
return normalizeInline(fallbackName) || null;
|
|
260
|
+
}
|
|
261
|
+
function normalizeInline(value) {
|
|
262
|
+
return (value ?? '').replace(/\s+/gu, ' ').trim();
|
|
263
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { normalizeThreadShortIdInput } from '@bbigbang/protocol';
|
|
2
|
+
export const TARGET_PARTICIPANT_ACTIVE_WINDOW_MS = 60 * 60 * 1000;
|
|
3
|
+
function normalizeThreadRootId(threadRootId) {
|
|
4
|
+
return normalizeThreadShortIdInput(threadRootId) ?? '';
|
|
5
|
+
}
|
|
6
|
+
export function upsertTargetParticipant(db, params) {
|
|
7
|
+
const now = params.lastActiveAt ?? Date.now();
|
|
8
|
+
const joinedAt = params.joinedAt ?? now;
|
|
9
|
+
const threadRootId = normalizeThreadRootId(params.threadRootId);
|
|
10
|
+
const isChannelRoot = threadRootId === '';
|
|
11
|
+
const incomingRole = isChannelRoot ? 'participant' : (params.role ?? 'participant');
|
|
12
|
+
const existing = db.prepare(`SELECT role, joined_at as joinedAt
|
|
13
|
+
FROM target_participants
|
|
14
|
+
WHERE agent_id = ? AND channel_id = ? AND thread_root_id = ?`).get(params.agentId, params.channelId, threadRootId);
|
|
15
|
+
const role = isChannelRoot
|
|
16
|
+
? 'participant'
|
|
17
|
+
: (existing?.role === 'owner' || incomingRole === 'owner' ? 'owner' : 'participant');
|
|
18
|
+
db.prepare(`INSERT INTO target_participants(agent_id, channel_id, thread_root_id, role, joined_at, last_active_at)
|
|
19
|
+
VALUES(?, ?, ?, ?, ?, ?)
|
|
20
|
+
ON CONFLICT(agent_id, channel_id, thread_root_id) DO UPDATE SET
|
|
21
|
+
role = excluded.role,
|
|
22
|
+
joined_at = MIN(target_participants.joined_at, excluded.joined_at),
|
|
23
|
+
last_active_at = MAX(target_participants.last_active_at, excluded.last_active_at)`).run(params.agentId, params.channelId, threadRootId, role, existing?.joinedAt ?? joinedAt, now);
|
|
24
|
+
}
|
|
25
|
+
export function listTargetParticipants(db, params) {
|
|
26
|
+
return db.prepare(`SELECT tp.agent_id as agentId,
|
|
27
|
+
a.name as name,
|
|
28
|
+
tp.role as role,
|
|
29
|
+
tp.joined_at as joinedAt,
|
|
30
|
+
tp.last_active_at as lastActiveAt
|
|
31
|
+
FROM target_participants tp
|
|
32
|
+
JOIN agents a ON a.agent_id = tp.agent_id
|
|
33
|
+
WHERE tp.channel_id = ? AND tp.thread_root_id = ?
|
|
34
|
+
ORDER BY
|
|
35
|
+
CASE tp.role WHEN 'owner' THEN 0 ELSE 1 END ASC,
|
|
36
|
+
tp.last_active_at DESC,
|
|
37
|
+
a.name ASC`).all(params.channelId, normalizeThreadRootId(params.threadRootId));
|
|
38
|
+
}
|
|
39
|
+
export function listRecentTargetParticipants(db, params) {
|
|
40
|
+
return db.prepare(`SELECT tp.agent_id as agentId,
|
|
41
|
+
a.name as name,
|
|
42
|
+
tp.role as role,
|
|
43
|
+
tp.joined_at as joinedAt,
|
|
44
|
+
tp.last_active_at as lastActiveAt
|
|
45
|
+
FROM target_participants tp
|
|
46
|
+
JOIN agents a ON a.agent_id = tp.agent_id
|
|
47
|
+
WHERE tp.channel_id = ? AND tp.thread_root_id = ? AND tp.last_active_at >= ?
|
|
48
|
+
ORDER BY
|
|
49
|
+
CASE tp.role WHEN 'owner' THEN 0 ELSE 1 END ASC,
|
|
50
|
+
tp.last_active_at DESC,
|
|
51
|
+
a.name ASC`).all(params.channelId, normalizeThreadRootId(params.threadRootId), params.activeSince);
|
|
52
|
+
}
|
|
53
|
+
export function setTargetOwner(db, params) {
|
|
54
|
+
const threadRootId = normalizeThreadRootId(params.threadRootId);
|
|
55
|
+
db.prepare(`UPDATE target_participants
|
|
56
|
+
SET role = 'participant'
|
|
57
|
+
WHERE channel_id = ? AND thread_root_id = ?`).run(params.channelId, threadRootId);
|
|
58
|
+
if (params.agentId) {
|
|
59
|
+
upsertTargetParticipant(db, {
|
|
60
|
+
agentId: params.agentId,
|
|
61
|
+
channelId: params.channelId,
|
|
62
|
+
threadRootId,
|
|
63
|
+
role: 'owner',
|
|
64
|
+
lastActiveAt: params.lastActiveAt,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function deleteTargetParticipantsForAgent(db, agentId) {
|
|
69
|
+
db.prepare('DELETE FROM target_participants WHERE agent_id = ?').run(agentId);
|
|
70
|
+
}
|
|
71
|
+
export function deleteTargetParticipantsForAgentInChannel(db, params) {
|
|
72
|
+
db.prepare(`DELETE FROM target_participants
|
|
73
|
+
WHERE agent_id = ? AND channel_id = ?`).run(params.agentId, params.channelId);
|
|
74
|
+
}
|
|
75
|
+
export function deleteTargetParticipantsForChannel(db, channelId) {
|
|
76
|
+
db.prepare('DELETE FROM target_participants WHERE channel_id = ?').run(channelId);
|
|
77
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { buildThreadShortId } from '@bbigbang/protocol';
|
|
3
|
+
function isThreadTarget(target) {
|
|
4
|
+
if (target.startsWith('dm:@'))
|
|
5
|
+
return target.split(':').length >= 3;
|
|
6
|
+
if (target.startsWith('#'))
|
|
7
|
+
return target.includes(':');
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
export function buildTaskEventThreadTarget(params) {
|
|
11
|
+
if (params.explicitThreadTarget?.trim())
|
|
12
|
+
return params.explicitThreadTarget.trim();
|
|
13
|
+
const sourceTarget = params.sourceTarget?.trim();
|
|
14
|
+
const messageId = params.messageId?.trim();
|
|
15
|
+
if (!sourceTarget || !messageId)
|
|
16
|
+
return null;
|
|
17
|
+
if (!(sourceTarget.startsWith('#') || sourceTarget.startsWith('dm:@')))
|
|
18
|
+
return null;
|
|
19
|
+
if (isThreadTarget(sourceTarget))
|
|
20
|
+
return sourceTarget;
|
|
21
|
+
return `${sourceTarget}:${buildThreadShortId(messageId)}`;
|
|
22
|
+
}
|
|
23
|
+
export function appendTaskEvent(db, params) {
|
|
24
|
+
db.prepare(`INSERT INTO task_events(
|
|
25
|
+
event_id,
|
|
26
|
+
task_id,
|
|
27
|
+
agent_task_ref,
|
|
28
|
+
channel_id,
|
|
29
|
+
task_number,
|
|
30
|
+
event_type,
|
|
31
|
+
actor_type,
|
|
32
|
+
actor_id,
|
|
33
|
+
actor_name,
|
|
34
|
+
from_status,
|
|
35
|
+
to_status,
|
|
36
|
+
claimed_by_agent_id_after,
|
|
37
|
+
claimed_by_name_after,
|
|
38
|
+
message_id,
|
|
39
|
+
thread_target,
|
|
40
|
+
created_at
|
|
41
|
+
)
|
|
42
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), params.taskId, params.agentTaskRef, params.channelId, params.taskNumber, params.eventType, params.actorType, params.actorId ?? null, params.actorName ?? null, params.fromStatus ?? null, params.toStatus ?? null, params.claimedByAgentIdAfter ?? null, params.claimedByNameAfter ?? null, params.messageId ?? null, params.threadTarget ?? null, params.createdAt ?? Date.now());
|
|
43
|
+
}
|
|
44
|
+
export function deleteTaskEventsForChannel(db, channelId) {
|
|
45
|
+
db.prepare(`DELETE FROM task_events WHERE channel_id = ?`).run(channelId);
|
|
46
|
+
}
|
|
47
|
+
export function deleteTaskEventsForTask(db, taskId) {
|
|
48
|
+
db.prepare(`DELETE FROM task_events WHERE task_id = ?`).run(taskId);
|
|
49
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { BEIJING_TIME_ZONE } from '@bbigbang/protocol';
|
|
3
|
+
import { allocateNextChannelMessageSeq } from './channelMessageSequences.js';
|
|
4
|
+
export const TASK_LIFECYCLE_SOURCE = 'task_lifecycle';
|
|
5
|
+
const taskLifecycleTimeFormatter = new Intl.DateTimeFormat('en-US', {
|
|
6
|
+
timeZone: BEIJING_TIME_ZONE,
|
|
7
|
+
hour: '2-digit',
|
|
8
|
+
minute: '2-digit',
|
|
9
|
+
hour12: true,
|
|
10
|
+
});
|
|
11
|
+
const taskLifecycleDateFormatter = new Intl.DateTimeFormat('en-US', {
|
|
12
|
+
timeZone: BEIJING_TIME_ZONE,
|
|
13
|
+
month: '2-digit',
|
|
14
|
+
day: '2-digit',
|
|
15
|
+
});
|
|
16
|
+
const taskLifecycleYearFormatter = new Intl.DateTimeFormat('en-US', {
|
|
17
|
+
timeZone: BEIJING_TIME_ZONE,
|
|
18
|
+
year: 'numeric',
|
|
19
|
+
});
|
|
20
|
+
export function deriveTaskLifecycleKindFromStatusChange(fromStatus, toStatus) {
|
|
21
|
+
if (fromStatus === 'todo' && toStatus === 'in_progress')
|
|
22
|
+
return 'started';
|
|
23
|
+
if (toStatus === 'in_progress')
|
|
24
|
+
return 'in_progress';
|
|
25
|
+
if (toStatus === 'in_review')
|
|
26
|
+
return 'in_review';
|
|
27
|
+
if (toStatus === 'done')
|
|
28
|
+
return 'done';
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export function inferTaskLifecycleKindFromText(content, taskStatus) {
|
|
32
|
+
const normalized = content.toLowerCase();
|
|
33
|
+
if (normalized.includes('could not start its task thread automatically'))
|
|
34
|
+
return 'handoff_failed';
|
|
35
|
+
if (normalized.includes('moved to in review'))
|
|
36
|
+
return 'in_review';
|
|
37
|
+
if (normalized.includes('marked done'))
|
|
38
|
+
return 'done';
|
|
39
|
+
if (normalized.includes('moved back to in progress')
|
|
40
|
+
|| normalized.includes('moved to in progress')) {
|
|
41
|
+
return 'in_progress';
|
|
42
|
+
}
|
|
43
|
+
if (normalized.startsWith('started '))
|
|
44
|
+
return 'started';
|
|
45
|
+
if (taskStatus === 'in_review')
|
|
46
|
+
return 'in_review';
|
|
47
|
+
if (taskStatus === 'done')
|
|
48
|
+
return 'done';
|
|
49
|
+
if (taskStatus === 'in_progress')
|
|
50
|
+
return 'in_progress';
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
export function extractTaskLifecycleTitle(content) {
|
|
54
|
+
const matched = content.match(/#(\d+)\s+"([^"]+)"/u);
|
|
55
|
+
if (!matched) {
|
|
56
|
+
return {
|
|
57
|
+
taskNumber: null,
|
|
58
|
+
title: null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
taskNumber: Number(matched[1]),
|
|
63
|
+
title: matched[2] ?? null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function buildTaskLifecycleText(params) {
|
|
67
|
+
const label = `#${params.taskNumber} "${params.title}"`;
|
|
68
|
+
switch (params.kind) {
|
|
69
|
+
case 'started':
|
|
70
|
+
return `Started ${label}. Detailed work continues in the task thread.`;
|
|
71
|
+
case 'in_progress':
|
|
72
|
+
return params.fromStatus === 'in_review'
|
|
73
|
+
? `${label} moved back to in progress.`
|
|
74
|
+
: `${label} moved to in progress.`;
|
|
75
|
+
case 'done':
|
|
76
|
+
return `${label} marked done.`;
|
|
77
|
+
case 'handoff_failed':
|
|
78
|
+
return `${label} could not start its task thread automatically.`;
|
|
79
|
+
case 'in_review':
|
|
80
|
+
default:
|
|
81
|
+
return `${label} moved to in review.`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function extractTaskLifecycleStatus(content) {
|
|
85
|
+
const normalized = content.toLowerCase();
|
|
86
|
+
if (normalized.includes('marked done.'))
|
|
87
|
+
return 'done';
|
|
88
|
+
if (normalized.includes('moved to in review.'))
|
|
89
|
+
return 'in_review';
|
|
90
|
+
if (normalized.includes('moved back to in progress.')
|
|
91
|
+
|| normalized.includes('moved to in progress.')) {
|
|
92
|
+
return 'in_progress';
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
export function formatTaskLifecycleLead(params) {
|
|
97
|
+
const actor = normalizeInline(params.actorName) || 'System';
|
|
98
|
+
const taskLabel = formatDisplayTaskLabel(params.taskNumber, params.title);
|
|
99
|
+
switch (params.kind) {
|
|
100
|
+
case 'started':
|
|
101
|
+
return `๐ ${actor} started ${taskLabel}`;
|
|
102
|
+
case 'in_progress':
|
|
103
|
+
return `๐ ${actor} moved ${taskLabel} to In Progress`;
|
|
104
|
+
case 'in_review':
|
|
105
|
+
return `๐ ${actor} moved ${taskLabel} to In Review`;
|
|
106
|
+
case 'done':
|
|
107
|
+
return `โ
${actor} moved ${taskLabel} to Done`;
|
|
108
|
+
case 'handoff_failed':
|
|
109
|
+
return `โ ๏ธ ${actor} failed to start ${taskLabel}`;
|
|
110
|
+
default:
|
|
111
|
+
return `${actor} updated ${taskLabel}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export function formatTaskLifecyclePromptLine(params) {
|
|
115
|
+
return `${formatTaskLifecycleTimestamp(params.createdAt)} ยท ${formatTaskLifecycleLead(params)}`;
|
|
116
|
+
}
|
|
117
|
+
export function insertTaskLifecycleMessage(db, params) {
|
|
118
|
+
const createdAt = Date.now();
|
|
119
|
+
const messageId = randomUUID();
|
|
120
|
+
const seq = allocateNextChannelMessageSeq(db, params.channelId);
|
|
121
|
+
const content = buildTaskLifecycleText(params);
|
|
122
|
+
const senderName = params.actorName?.trim() || 'system';
|
|
123
|
+
db.prepare(`INSERT INTO channel_messages(
|
|
124
|
+
message_id, channel_id, sender_id, sender_name, sender_type, target, content,
|
|
125
|
+
seq, created_at, thread_root_id, message_kind, message_source
|
|
126
|
+
)
|
|
127
|
+
VALUES(?, ?, 'system', ?, 'system', ?, ?, ?, ?, NULL, 'task_event', ?)`).run(messageId, params.channelId, senderName, params.primaryTarget, content, seq, createdAt, TASK_LIFECYCLE_SOURCE);
|
|
128
|
+
return {
|
|
129
|
+
messageId,
|
|
130
|
+
content,
|
|
131
|
+
seq,
|
|
132
|
+
createdAt,
|
|
133
|
+
senderName,
|
|
134
|
+
messageSource: TASK_LIFECYCLE_SOURCE,
|
|
135
|
+
taskNumber: params.taskNumber,
|
|
136
|
+
taskStatus: params.taskStatus,
|
|
137
|
+
taskAssigneeName: params.taskAssigneeName ?? null,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function formatDisplayTaskLabel(taskNumber, title) {
|
|
141
|
+
const normalizedTitle = normalizeInline(title);
|
|
142
|
+
if (normalizedTitle) {
|
|
143
|
+
return `#${taskNumber ?? '?'} "${normalizedTitle.replaceAll('"', '\\"')}"`;
|
|
144
|
+
}
|
|
145
|
+
return taskNumber != null ? `#${taskNumber}` : 'task';
|
|
146
|
+
}
|
|
147
|
+
function getBeijingDayStamp(timestamp) {
|
|
148
|
+
const parts = taskLifecycleDateFormatter.formatToParts(timestamp);
|
|
149
|
+
const year = Number(taskLifecycleYearFormatter.format(timestamp));
|
|
150
|
+
const month = Number(parts.find((part) => part.type === 'month')?.value ?? '1');
|
|
151
|
+
const day = Number(parts.find((part) => part.type === 'day')?.value ?? '1');
|
|
152
|
+
return Date.UTC(year, month - 1, day);
|
|
153
|
+
}
|
|
154
|
+
function formatTaskLifecycleTimestamp(timestamp) {
|
|
155
|
+
const dayDiff = Math.round((getBeijingDayStamp(Date.now()) - getBeijingDayStamp(timestamp)) / 86_400_000);
|
|
156
|
+
const timeText = taskLifecycleTimeFormatter.format(timestamp);
|
|
157
|
+
if (dayDiff === 0)
|
|
158
|
+
return `Today ${timeText}`;
|
|
159
|
+
if (dayDiff === 1)
|
|
160
|
+
return `Yesterday ${timeText}`;
|
|
161
|
+
return `${taskLifecycleDateFormatter.format(timestamp)} ${timeText}`;
|
|
162
|
+
}
|
|
163
|
+
function normalizeInline(value) {
|
|
164
|
+
return (value ?? '').replace(/\s+/gu, ' ').trim();
|
|
165
|
+
}
|