@bbigbang/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/config.js +380 -0
  2. package/dist/execution/executionDispatcher.js +3810 -0
  3. package/dist/main.js +90 -0
  4. package/dist/nodeEventHistory.js +206 -0
  5. package/dist/scheduler/dreamLogic.js +50 -0
  6. package/dist/scheduler/dreamScheduler.js +65 -0
  7. package/dist/services/agentFileAccessService.js +1913 -0
  8. package/dist/services/agentRuntimeCleanupBroker.js +62 -0
  9. package/dist/services/agentSkillsBroker.js +118 -0
  10. package/dist/services/agentSkillsService.js +83 -0
  11. package/dist/services/agentWorkspaceBroker.js +937 -0
  12. package/dist/services/agentWorkspaceService.js +70 -0
  13. package/dist/services/appVersion.js +14 -0
  14. package/dist/services/auth.js +586 -0
  15. package/dist/services/claudeControlBroker.js +154 -0
  16. package/dist/services/claudeTranscriptBroker.js +100 -0
  17. package/dist/services/claudeTranscriptService.js +359 -0
  18. package/dist/services/codexAppServerBroker.js +155 -0
  19. package/dist/services/codexTranscriptBroker.js +98 -0
  20. package/dist/services/codexTranscriptService.js +961 -0
  21. package/dist/services/droidMissionBroker.js +124 -0
  22. package/dist/services/droidMissionImporter.js +630 -0
  23. package/dist/services/droidModelOptions.js +165 -0
  24. package/dist/services/hubServerRegistrationService.js +268 -0
  25. package/dist/services/libraryManifest.js +43 -0
  26. package/dist/services/libraryScaffold.js +26 -0
  27. package/dist/services/libraryService.js +2263 -0
  28. package/dist/services/memoryService.js +386 -0
  29. package/dist/services/missionEvidence.js +377 -0
  30. package/dist/services/missionService.js +2361 -0
  31. package/dist/services/missionTrace.js +158 -0
  32. package/dist/services/nativeMissionBriefParser.js +120 -0
  33. package/dist/services/nativeMissionOrchestrator.js +2045 -0
  34. package/dist/services/nativeMissionReportGenerator.js +227 -0
  35. package/dist/services/nativeMissionValidationRunner.js +452 -0
  36. package/dist/services/nativeMissionWorkerBroker.js +190 -0
  37. package/dist/services/nodeRegistry.js +34 -0
  38. package/dist/services/nodeStateReconciler.js +97 -0
  39. package/dist/services/panelMediaScanner.js +119 -0
  40. package/dist/services/persistentRuntimeJsonlClient.js +153 -0
  41. package/dist/services/platformAgentPolicy.js +180 -0
  42. package/dist/services/platformAgentService.js +2041 -0
  43. package/dist/services/projectAccessResolver.js +93 -0
  44. package/dist/services/projectService.js +392 -0
  45. package/dist/services/resourceSpaceService.js +140 -0
  46. package/dist/services/scenarioRuntimeService.js +1130 -0
  47. package/dist/services/suggestedPlannerService.js +868 -0
  48. package/dist/services/workbenchGitBroker.js +161 -0
  49. package/dist/services/workbenchGitService.js +69 -0
  50. package/dist/services/workbenchInspectBroker.js +65 -0
  51. package/dist/services/workbenchNodePathService.js +79 -0
  52. package/dist/services/workbenchRegistryService.js +240 -0
  53. package/dist/services/workbenchRootService.js +181 -0
  54. package/dist/services/workbenchTerminalBroker.js +378 -0
  55. package/dist/services/workspaceRunOwnership.js +60 -0
  56. package/dist/services/workspaceScaffold.js +105 -0
  57. package/dist/services/workspaceSessionRuntimeService.js +576 -0
  58. package/dist/services/workspaceSessionService.js +245 -0
  59. package/dist/services/workspaceToolActionRunner.js +1582 -0
  60. package/dist/services/workspaceToolErrors.js +10 -0
  61. package/dist/services/workspaceToolExecutionUtils.js +895 -0
  62. package/dist/services/workspaceToolLatestStateProjector.js +91 -0
  63. package/dist/services/workspaceToolManifest.js +572 -0
  64. package/dist/services/workspaceToolMutationQueue.js +43 -0
  65. package/dist/services/workspaceToolPanelProjection.js +460 -0
  66. package/dist/services/workspaceToolPromotion.js +255 -0
  67. package/dist/services/workspaceToolPromotionState.js +224 -0
  68. package/dist/services/workspaceToolPublishDiagnostics.js +189 -0
  69. package/dist/services/workspaceToolPublishIdentityResolver.js +146 -0
  70. package/dist/services/workspaceToolReadModel.js +378 -0
  71. package/dist/services/workspaceToolRunLedger.js +239 -0
  72. package/dist/services/workspaceToolService.js +3067 -0
  73. package/dist/services/workspaceToolSnapshotPanelSync.js +293 -0
  74. package/dist/services/workspaceToolTerminalLifecycle.js +283 -0
  75. package/dist/services/workspaceToolTypes.js +1 -0
  76. package/dist/services/workspaceToolUploadMaterializer.js +228 -0
  77. package/dist/web/actionCardRoutes.js +129 -0
  78. package/dist/web/actionCards.js +469 -0
  79. package/dist/web/activationContext.js +684 -0
  80. package/dist/web/agentChannelGuards.js +48 -0
  81. package/dist/web/agentMentionCooldowns.js +32 -0
  82. package/dist/web/agentReminders.js +1668 -0
  83. package/dist/web/agentRuntimePresence.js +197 -0
  84. package/dist/web/agentSelfState.js +494 -0
  85. package/dist/web/agentTaskLinks.js +26 -0
  86. package/dist/web/agentVisibility.js +79 -0
  87. package/dist/web/assets.js +95 -0
  88. package/dist/web/channelActivationPrompt.js +395 -0
  89. package/dist/web/channelMemoryNotes.js +127 -0
  90. package/dist/web/channelMentions.js +10 -0
  91. package/dist/web/channelMessageSequences.js +19 -0
  92. package/dist/web/channelSubscriptions.js +26 -0
  93. package/dist/web/clearedTaskRoots.js +10 -0
  94. package/dist/web/collaborationPromptGuidance.js +36 -0
  95. package/dist/web/collaborationSurfaceState.js +140 -0
  96. package/dist/web/contextBundleRanking.js +154 -0
  97. package/dist/web/contextBundleResolver.js +488 -0
  98. package/dist/web/conversationBuiltinSkillRoots.js +50 -0
  99. package/dist/web/conversationControls.js +232 -0
  100. package/dist/web/conversationHandoffs.js +612 -0
  101. package/dist/web/conversationManager.js +2511 -0
  102. package/dist/web/conversationSummaries.js +876 -0
  103. package/dist/web/conversationSurfaceKinds.js +17 -0
  104. package/dist/web/conversationTargets.js +173 -0
  105. package/dist/web/directActivationPrompt.js +122 -0
  106. package/dist/web/directReplyTargets.js +69 -0
  107. package/dist/web/directThreadResolver.js +129 -0
  108. package/dist/web/dmTaskHandoffPrompt.js +120 -0
  109. package/dist/web/dmTaskThreadStatusProjection.js +229 -0
  110. package/dist/web/ftsQuery.js +33 -0
  111. package/dist/web/internalAgentRouter.js +11341 -0
  112. package/dist/web/libraryCuratorScheduler.js +58 -0
  113. package/dist/web/libraryDocumentPromptGuidance.js +8 -0
  114. package/dist/web/messageCheckpoints.js +19 -0
  115. package/dist/web/nodeWsHandler.js +2495 -0
  116. package/dist/web/notificationRounds.js +1061 -0
  117. package/dist/web/panelActionMessages.js +108 -0
  118. package/dist/web/panelActivationPrompt.js +18 -0
  119. package/dist/web/panelAudit.js +273 -0
  120. package/dist/web/panelLifecycle.js +222 -0
  121. package/dist/web/panelMediaPolicy.js +43 -0
  122. package/dist/web/panelPathPolicy.js +63 -0
  123. package/dist/web/panelPreviews.js +175 -0
  124. package/dist/web/panelQueryHandles.js +2749 -0
  125. package/dist/web/panelRoutes.js +2147 -0
  126. package/dist/web/panels.js +904 -0
  127. package/dist/web/peerInboxAggregates.js +1247 -0
  128. package/dist/web/planApprovalState.js +92 -0
  129. package/dist/web/platformAgentScheduler.js +66 -0
  130. package/dist/web/proactiveOpportunities.js +452 -0
  131. package/dist/web/promptContextSections.js +242 -0
  132. package/dist/web/promptHistorySanitizer.js +26 -0
  133. package/dist/web/promptSlashCommands.js +158 -0
  134. package/dist/web/rollingConversationSummary.js +453 -0
  135. package/dist/web/routeHelpers.js +11 -0
  136. package/dist/web/routes/handoff.js +288 -0
  137. package/dist/web/routes/history.js +345 -0
  138. package/dist/web/routes/memory.js +258 -0
  139. package/dist/web/routes/selfState.js +171 -0
  140. package/dist/web/routes/workspace.js +154 -0
  141. package/dist/web/runSurfaceWatermarks.js +431 -0
  142. package/dist/web/runtimeCapabilities.js +48 -0
  143. package/dist/web/sameAgentHandoffs.js +494 -0
  144. package/dist/web/server.js +15567 -0
  145. package/dist/web/sharedCollaborationCapsules.js +163 -0
  146. package/dist/web/soloSessionRelay.js +42 -0
  147. package/dist/web/soloWsHandler.js +138 -0
  148. package/dist/web/suggestedPlannerScheduler.js +56 -0
  149. package/dist/web/surfaceActivationPolicy.js +108 -0
  150. package/dist/web/surfaceCollaborators.js +61 -0
  151. package/dist/web/surfaceSystemStatus.js +263 -0
  152. package/dist/web/targetParticipants.js +77 -0
  153. package/dist/web/taskEvents.js +49 -0
  154. package/dist/web/taskLifecycleMessages.js +165 -0
  155. package/dist/web/taskLoops.js +732 -0
  156. package/dist/web/taskMemoryNotes.js +224 -0
  157. package/dist/web/taskNumbers.js +16 -0
  158. package/dist/web/taskOwnerGuards.js +49 -0
  159. package/dist/web/taskParticipantResolver.js +42 -0
  160. package/dist/web/taskParticipants.js +97 -0
  161. package/dist/web/taskSourceDetails.js +20 -0
  162. package/dist/web/taskStateViews.js +210 -0
  163. package/dist/web/taskStatusTransitions.js +9 -0
  164. package/dist/web/taskThreadFollowups.js +599 -0
  165. package/dist/web/taskThreadRuntimeClosure.js +685 -0
  166. package/dist/web/taskUpdateDelivery.js +104 -0
  167. package/dist/web/threadReplyContentHeuristics.js +30 -0
  168. package/dist/web/threadRoots.js +61 -0
  169. package/dist/web/threadTaskBindings.js +365 -0
  170. package/dist/web/uiPanelPromptGuidance.js +27 -0
  171. package/dist/web/workspaceMemoryHints.js +143 -0
  172. package/dist/web/workspaceToolPromptGuidance.js +30 -0
  173. package/dist/web/wsHandler.js +397 -0
  174. package/dist/web/wsSink.js +116 -0
  175. package/package.json +54 -0
@@ -0,0 +1,612 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createPromptContextSection, } from './promptContextSections.js';
3
+ export function createConversationHandoff(db, params) {
4
+ const handoffId = randomUUID();
5
+ const now = Date.now();
6
+ const payload = normalizeConversationHandoffPayload(params.payload);
7
+ db.prepare(`INSERT INTO conversation_handoffs(
8
+ handoff_id,
9
+ agent_id,
10
+ source_conversation_id,
11
+ target_conversation_id,
12
+ target_reply_target,
13
+ workflow_kind,
14
+ source_disposition,
15
+ task_id,
16
+ task_number,
17
+ mode,
18
+ payload_json,
19
+ status,
20
+ created_at,
21
+ updated_at
22
+ )
23
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(handoffId, params.agentId, params.sourceConversationId, params.targetConversationId ?? null, params.targetReplyTarget, params.workflowKind ?? 'generic', params.sourceDisposition ?? (params.mode === 'continue_there' ? 'cancel_current_run' : 'keep_running'), params.taskId ?? null, params.taskNumber ?? null, params.mode, JSON.stringify(payload), params.status, now, now);
24
+ return {
25
+ handoffId,
26
+ agentId: params.agentId,
27
+ sourceConversationId: params.sourceConversationId,
28
+ targetConversationId: params.targetConversationId ?? null,
29
+ targetReplyTarget: params.targetReplyTarget,
30
+ workflowKind: params.workflowKind ?? 'generic',
31
+ sourceDisposition: params.sourceDisposition ?? (params.mode === 'continue_there' ? 'cancel_current_run' : 'keep_running'),
32
+ taskId: params.taskId ?? null,
33
+ taskNumber: typeof params.taskNumber === 'number' ? Math.floor(params.taskNumber) : null,
34
+ mode: params.mode,
35
+ status: params.status,
36
+ payload,
37
+ createdAt: new Date(now).toISOString(),
38
+ updatedAt: new Date(now).toISOString(),
39
+ };
40
+ }
41
+ export function markConversationHandoffAccepted(db, params) {
42
+ return updateLatestOpenConversationHandoff(db, {
43
+ targetConversationId: params.targetConversationId,
44
+ targetRunId: params.targetRunId,
45
+ status: 'accepted',
46
+ payload: {
47
+ targetRunId: params.targetRunId,
48
+ error: null,
49
+ },
50
+ });
51
+ }
52
+ export function markConversationHandoffCompleted(db, params) {
53
+ return updateLatestOpenConversationHandoff(db, {
54
+ targetConversationId: params.targetConversationId,
55
+ targetRunId: params.targetRunId ?? null,
56
+ status: 'completed',
57
+ payload: {
58
+ ...(params.targetRunId ? { targetRunId: params.targetRunId } : {}),
59
+ completionMessageId: params.completionMessageId ?? null,
60
+ error: null,
61
+ },
62
+ });
63
+ }
64
+ export function markConversationHandoffCancelled(db, params) {
65
+ return updateLatestOpenConversationHandoff(db, {
66
+ targetConversationId: params.targetConversationId,
67
+ targetRunId: params.targetRunId ?? null,
68
+ status: 'cancelled',
69
+ payload: {
70
+ ...(params.targetRunId ? { targetRunId: params.targetRunId } : {}),
71
+ error: params.error ?? null,
72
+ },
73
+ });
74
+ }
75
+ export function markConversationHandoffFailed(db, params) {
76
+ return updateLatestOpenConversationHandoff(db, {
77
+ targetConversationId: params.targetConversationId,
78
+ targetRunId: params.targetRunId ?? null,
79
+ status: 'failed',
80
+ payload: {
81
+ ...(params.targetRunId ? { targetRunId: params.targetRunId } : {}),
82
+ ...(params.error ? { error: params.error } : {}),
83
+ },
84
+ });
85
+ }
86
+ export function markConversationHandoffSourceCancelled(db, params) {
87
+ const row = db.prepare(`SELECT handoff_id as handoffId,
88
+ agent_id as agentId,
89
+ source_conversation_id as sourceConversationId,
90
+ target_conversation_id as targetConversationId,
91
+ target_reply_target as targetReplyTarget,
92
+ workflow_kind as workflowKind,
93
+ source_disposition as sourceDisposition,
94
+ task_id as taskId,
95
+ task_number as taskNumber,
96
+ mode,
97
+ status,
98
+ payload_json as payloadJson,
99
+ created_at as createdAt,
100
+ updated_at as updatedAt
101
+ FROM conversation_handoffs
102
+ WHERE handoff_id = ?
103
+ LIMIT 1`).get(params.handoffId);
104
+ if (!row)
105
+ return null;
106
+ const record = mapConversationHandoffRow(row);
107
+ const now = Date.now();
108
+ const payload = normalizeConversationHandoffPayload({
109
+ ...record.payload,
110
+ cancelRunId: params.cancelRunId,
111
+ goal: record.payload.goal,
112
+ });
113
+ db.prepare(`UPDATE conversation_handoffs
114
+ SET payload_json = ?,
115
+ updated_at = ?
116
+ WHERE handoff_id = ?`).run(JSON.stringify(payload), now, params.handoffId);
117
+ return {
118
+ ...record,
119
+ payload,
120
+ updatedAt: new Date(now).toISOString(),
121
+ };
122
+ }
123
+ export function listRecentConversationHandoffs(db, agentId, limit = 5) {
124
+ const safeLimit = Math.max(1, Math.min(limit, 10));
125
+ const rows = db.prepare(`SELECT handoff_id as handoffId,
126
+ agent_id as agentId,
127
+ source_conversation_id as sourceConversationId,
128
+ target_conversation_id as targetConversationId,
129
+ target_reply_target as targetReplyTarget,
130
+ workflow_kind as workflowKind,
131
+ source_disposition as sourceDisposition,
132
+ task_id as taskId,
133
+ task_number as taskNumber,
134
+ mode,
135
+ status,
136
+ payload_json as payloadJson,
137
+ created_at as createdAt,
138
+ updated_at as updatedAt
139
+ FROM conversation_handoffs
140
+ WHERE agent_id = ?
141
+ ORDER BY updated_at DESC, created_at DESC
142
+ LIMIT ?`).all(agentId, safeLimit);
143
+ return rows.map(mapConversationHandoffRow);
144
+ }
145
+ export function getConversationHandoffById(db, handoffId) {
146
+ const row = db.prepare(`SELECT handoff_id as handoffId,
147
+ agent_id as agentId,
148
+ source_conversation_id as sourceConversationId,
149
+ target_conversation_id as targetConversationId,
150
+ target_reply_target as targetReplyTarget,
151
+ workflow_kind as workflowKind,
152
+ source_disposition as sourceDisposition,
153
+ task_id as taskId,
154
+ task_number as taskNumber,
155
+ mode,
156
+ status,
157
+ payload_json as payloadJson,
158
+ created_at as createdAt,
159
+ updated_at as updatedAt
160
+ FROM conversation_handoffs
161
+ WHERE handoff_id = ?
162
+ LIMIT 1`).get(handoffId);
163
+ return row ? mapConversationHandoffRow(row) : null;
164
+ }
165
+ export function getHandoffState(db, handoffId) {
166
+ const record = getConversationHandoffById(db, handoffId);
167
+ if (!record)
168
+ return null;
169
+ const runRows = db.prepare(`SELECT run_id as runId,
170
+ agent_id as agentId,
171
+ disposition,
172
+ created_at as createdAt,
173
+ completed_at as completedAt
174
+ FROM handoff_runs
175
+ WHERE handoff_id = ?
176
+ ORDER BY created_at DESC`).all(handoffId);
177
+ const runs = runRows.map((row) => ({
178
+ runId: row.runId,
179
+ agentId: row.agentId,
180
+ disposition: row.disposition,
181
+ createdAt: new Date(row.createdAt).toISOString(),
182
+ completedAt: row.completedAt ? new Date(row.completedAt).toISOString() : null,
183
+ }));
184
+ return { ...record, runs };
185
+ }
186
+ export function insertHandoffRun(db, params) {
187
+ const handoffExists = db.prepare(`SELECT 1 FROM conversation_handoffs WHERE handoff_id = ? LIMIT 1`).get(params.handoffId);
188
+ if (!handoffExists)
189
+ return null;
190
+ const now = Date.now();
191
+ // Mark previous non-completed runs for this handoff as completed
192
+ db.prepare(`UPDATE handoff_runs
193
+ SET completed_at = ?
194
+ WHERE handoff_id = ?
195
+ AND completed_at IS NULL`).run(now, params.handoffId);
196
+ const id = randomUUID();
197
+ db.prepare(`INSERT INTO handoff_runs(id, handoff_id, run_id, agent_id, disposition, created_at)
198
+ VALUES(?, ?, ?, ?, ?, ?)`).run(id, params.handoffId, params.runId, params.agentId, params.disposition, now);
199
+ return {
200
+ runId: params.runId,
201
+ agentId: params.agentId,
202
+ disposition: params.disposition,
203
+ createdAt: new Date(now).toISOString(),
204
+ completedAt: null,
205
+ };
206
+ }
207
+ export function getLatestConversationHandoffForConversation(db, params) {
208
+ const row = db.prepare(`SELECT handoff_id as handoffId,
209
+ agent_id as agentId,
210
+ source_conversation_id as sourceConversationId,
211
+ target_conversation_id as targetConversationId,
212
+ target_reply_target as targetReplyTarget,
213
+ workflow_kind as workflowKind,
214
+ source_disposition as sourceDisposition,
215
+ task_id as taskId,
216
+ task_number as taskNumber,
217
+ mode,
218
+ status,
219
+ payload_json as payloadJson,
220
+ created_at as createdAt,
221
+ updated_at as updatedAt
222
+ FROM conversation_handoffs
223
+ WHERE (source_conversation_id = ? OR target_conversation_id = ?)
224
+ ${params.openOnly ? `AND status IN ('started', 'accepted')` : ''}
225
+ ORDER BY updated_at DESC, created_at DESC
226
+ LIMIT 1`).get(params.conversationId, params.conversationId);
227
+ return row ? mapConversationHandoffRow(row) : null;
228
+ }
229
+ export function formatConversationHandoff(record) {
230
+ const summary = summarizeText(record.payload.goal, 120) ?? '(missing goal)';
231
+ const source = record.payload.sourceTarget?.trim() || record.sourceConversationId;
232
+ const error = record.payload.error?.trim();
233
+ const failure = error ? ` error=${summarizeText(error, 100)}` : '';
234
+ const workflow = record.workflowKind === 'generic' ? record.mode : `${record.workflowKind}/${record.mode}`;
235
+ const task = record.taskNumber ? ` task=#${record.taskNumber}` : '';
236
+ return `${workflow} [${record.status}] ${source} -> ${record.targetReplyTarget}${task} goal=${summary}${failure}`;
237
+ }
238
+ function buildContinueThereSourceReminderLines(params) {
239
+ const lines = [
240
+ `Detailed execution intentionally moved from ${params.sourceReplyTarget} to ${params.targetReplyTarget}.`,
241
+ 'This was a normal platform handoff, not a user interruption, runtime failure, or accidental cancellation.',
242
+ ];
243
+ if (params.relatedTaskRef?.trim()) {
244
+ lines.push(`Related task: ${params.relatedTaskRef.trim()}`);
245
+ }
246
+ lines.push(`If you are later asked about this from ${params.sourceReplyTarget}, describe it as a normal handoff/continue-there transition to ${params.targetReplyTarget}.`, 'Do not say that the earlier work on this surface was interrupted or cut off.', `If you need to report progress or status later, inspect ${params.targetReplyTarget} or structured task/handoff state first instead of relying on this source surface's pre-handoff plan.`);
247
+ return lines;
248
+ }
249
+ export function buildContinueThereSourceNoticePrompt(params) {
250
+ return [
251
+ '[System handoff notice]',
252
+ ...buildContinueThereSourceReminderLines(params),
253
+ `Stop detailed execution on ${params.sourceReplyTarget} now; it continues on ${params.targetReplyTarget}.`,
254
+ ].join('\n');
255
+ }
256
+ export function buildSourceSurfaceHandoffReminderSection(db, params) {
257
+ const conversation = resolveSourceSurfaceConversation(db, params);
258
+ if (!conversation?.replyTarget)
259
+ return null;
260
+ const handoff = loadLatestContinueThereSourceHandoff(db, conversation.conversationId);
261
+ if (!handoff)
262
+ return null;
263
+ if (hasLaterAgentVisibleMessageOnSurface(db, conversation, handoff.createdAt))
264
+ return null;
265
+ return createPromptContextSection('source_handoff_reminder', [
266
+ '[Source-surface handoff reminder]',
267
+ ...buildContinueThereSourceReminderLines({
268
+ sourceReplyTarget: conversation.replyTarget,
269
+ targetReplyTarget: handoff.targetReplyTarget,
270
+ relatedTaskRef: handoff.payload.relatedTaskRef,
271
+ }),
272
+ ].join('\n'));
273
+ }
274
+ export function buildConversationHandoffCapsuleText(params) {
275
+ const lines = [
276
+ '[Handoff capsule]',
277
+ 'This is a must-keep execution-context block for the current handoff target.',
278
+ '',
279
+ '[Handoff metadata]',
280
+ `mode: ${params.mode}`,
281
+ `reply_target: ${params.targetReplyTarget}`,
282
+ ];
283
+ if (params.payload.sourceTarget) {
284
+ lines.push(`source_target: ${params.payload.sourceTarget}`);
285
+ }
286
+ lines.push('', '[Goal]', params.payload.goal);
287
+ if (params.payload.whyNow) {
288
+ lines.push('', '[Why now]', params.payload.whyNow);
289
+ }
290
+ if (params.payload.alreadyDone.length > 0) {
291
+ lines.push('', '[Already done]', ...params.payload.alreadyDone.map((item) => `- ${item}`));
292
+ }
293
+ if (params.payload.expectedOutput) {
294
+ lines.push('', '[Expected output]', params.payload.expectedOutput);
295
+ }
296
+ if (params.payload.relatedTaskRef) {
297
+ lines.push('', '[Related task]', params.payload.relatedTaskRef);
298
+ }
299
+ if (params.payload.sourceSummary) {
300
+ lines.push('', '[Source surface summary]', params.payload.sourceSummary);
301
+ }
302
+ if (params.payload.sourceRollingSummary) {
303
+ lines.push('', '[Recent dialogue rolling summary]', params.payload.sourceRollingSummary);
304
+ }
305
+ if (params.payload.sourceContext) {
306
+ const lastUserMsg = summarizeText(params.payload.sourceContext.lastUserMessage, 500);
307
+ if (lastUserMsg) {
308
+ lines.push('', '[Source last user message]', lastUserMsg);
309
+ }
310
+ const lastAgentMsg = summarizeText(params.payload.sourceContext.lastAgentMessage, 500);
311
+ if (lastAgentMsg) {
312
+ lines.push('', '[Source last agent message]', lastAgentMsg);
313
+ }
314
+ if (!params.payload.sourceSummary
315
+ && (params.payload.sourceContext.boundTaskRef
316
+ || params.payload.sourceContext.boundTaskTitle
317
+ || params.payload.sourceContext.ownerName
318
+ || params.payload.sourceContext.myRole
319
+ || params.payload.sourceContext.participants.length > 0)) {
320
+ lines.push('', '[Source surface context]');
321
+ if (params.payload.sourceContext.boundTaskRef || params.payload.sourceContext.boundTaskTitle) {
322
+ lines.push(`bound_task: ${params.payload.sourceContext.boundTaskRef ?? '(no task ref)'}${params.payload.sourceContext.boundTaskTitle ? ` — ${params.payload.sourceContext.boundTaskTitle}` : ''}`);
323
+ }
324
+ if (params.payload.sourceContext.myRole) {
325
+ lines.push(`my_role: ${params.payload.sourceContext.myRole}`);
326
+ }
327
+ if (params.payload.sourceContext.ownerName) {
328
+ lines.push(`owner: @${params.payload.sourceContext.ownerName}`);
329
+ }
330
+ if (params.payload.sourceContext.participants.length > 0) {
331
+ lines.push(`participants: ${params.payload.sourceContext.participants.map((name) => `@${name}`).join(', ')}`);
332
+ }
333
+ }
334
+ }
335
+ return lines.join('\n');
336
+ }
337
+ export function buildConversationHandoffPrompt(params) {
338
+ const lines = [
339
+ '[Conversation handoff]',
340
+ 'You are the same agent continuing work on another surface.',
341
+ '',
342
+ 'The full handoff capsule is attached in activation/resume context so it survives queueing and restore.',
343
+ ];
344
+ if (params.payload.constraints.length > 0) {
345
+ lines.push('', '[Constraints]', ...params.payload.constraints.map((item) => `- ${item}`));
346
+ }
347
+ if (params.payload.optionalRefs.length > 0) {
348
+ lines.push('', '[Optional refs]', ...params.payload.optionalRefs.map((item) => `- ${item}`));
349
+ }
350
+ lines.push('', 'Rules:');
351
+ lines.push('- This handoff comes from another work surface of the same agent identity.');
352
+ if (params.mode === 'continue_there') {
353
+ lines.push('- Detailed execution should continue here. Treat the source surface as handed off.');
354
+ }
355
+ else if (params.mode === 'delegate_only') {
356
+ lines.push('- Handle the delegated follow-up here. The source surface may still remain active.');
357
+ }
358
+ else {
359
+ lines.push('- Coordinate through summaries and explicit handoffs; avoid duplicating the same work across surfaces.');
360
+ }
361
+ lines.push('- Prefer this target for detailed work that belongs here.');
362
+ lines.push('- Choose the status command by question: bigbang self state for global surface overview, bigbang runtime presence for node/host/runtime facts, bigbang workspace inspect for current workspace git/directory facts, bigbang conversation list for bulk surface discovery, bigbang conversation summary for one known surface, and bigbang context bundle only to shortlist related surfaces/tasks/handoffs before deeper inspection or another handoff.');
363
+ return lines.join('\n');
364
+ }
365
+ function normalizeConversationHandoffPayload(payload) {
366
+ return {
367
+ goal: normalizeRequiredText(payload.goal) ?? '(missing goal)',
368
+ whyNow: summarizeText(payload.whyNow, 400),
369
+ constraints: normalizeStringList(payload.constraints),
370
+ alreadyDone: normalizeStringList(payload.alreadyDone),
371
+ expectedOutput: summarizeText(payload.expectedOutput, 400),
372
+ sourceTarget: summarizeText(payload.sourceTarget, 200),
373
+ relatedTaskRef: summarizeText(payload.relatedTaskRef, 120),
374
+ optionalRefs: normalizeStringList(payload.optionalRefs),
375
+ sourceSummary: summarizeText(payload.sourceSummary, 1200),
376
+ sourceRollingSummary: summarizeText(payload.sourceRollingSummary, 1200),
377
+ targetRunId: summarizeText(payload.targetRunId, 160),
378
+ cancelRunId: summarizeText(payload.cancelRunId, 160),
379
+ completionMessageId: summarizeText(payload.completionMessageId, 160),
380
+ error: summarizeText(payload.error, 400),
381
+ sourceContext: payload.sourceContext
382
+ ? {
383
+ lastUserMessage: summarizeText(payload.sourceContext.lastUserMessage, 280),
384
+ lastAgentMessage: summarizeText(payload.sourceContext.lastAgentMessage, 280),
385
+ boundTaskRef: summarizeText(payload.sourceContext.boundTaskRef, 160),
386
+ boundTaskTitle: summarizeText(payload.sourceContext.boundTaskTitle, 200),
387
+ ownerName: summarizeText(payload.sourceContext.ownerName, 160),
388
+ participants: normalizeStringList(payload.sourceContext.participants),
389
+ myRole: summarizeText(payload.sourceContext.myRole, 120),
390
+ }
391
+ : null,
392
+ };
393
+ }
394
+ function parseConversationHandoffPayload(raw) {
395
+ try {
396
+ const parsed = JSON.parse(raw);
397
+ return normalizeConversationHandoffPayload({
398
+ goal: typeof parsed?.goal === 'string' ? parsed.goal : '(missing goal)',
399
+ whyNow: typeof parsed?.whyNow === 'string' ? parsed.whyNow : null,
400
+ constraints: Array.isArray(parsed?.constraints) ? parsed.constraints : [],
401
+ alreadyDone: Array.isArray(parsed?.alreadyDone) ? parsed.alreadyDone : [],
402
+ expectedOutput: typeof parsed?.expectedOutput === 'string' ? parsed.expectedOutput : null,
403
+ sourceTarget: typeof parsed?.sourceTarget === 'string' ? parsed.sourceTarget : null,
404
+ relatedTaskRef: typeof parsed?.relatedTaskRef === 'string' ? parsed.relatedTaskRef : null,
405
+ optionalRefs: Array.isArray(parsed?.optionalRefs) ? parsed.optionalRefs : [],
406
+ sourceSummary: typeof parsed?.sourceSummary === 'string' ? parsed.sourceSummary : null,
407
+ sourceRollingSummary: typeof parsed?.sourceRollingSummary === 'string' ? parsed.sourceRollingSummary : null,
408
+ targetRunId: typeof parsed?.targetRunId === 'string' ? parsed.targetRunId : null,
409
+ cancelRunId: typeof parsed?.cancelRunId === 'string' ? parsed.cancelRunId : null,
410
+ completionMessageId: typeof parsed?.completionMessageId === 'string' ? parsed.completionMessageId : null,
411
+ error: typeof parsed?.error === 'string' ? parsed.error : null,
412
+ sourceContext: parsed?.sourceContext && typeof parsed.sourceContext === 'object'
413
+ ? {
414
+ lastUserMessage: typeof parsed.sourceContext.lastUserMessage === 'string' ? parsed.sourceContext.lastUserMessage : null,
415
+ lastAgentMessage: typeof parsed.sourceContext.lastAgentMessage === 'string' ? parsed.sourceContext.lastAgentMessage : null,
416
+ boundTaskRef: typeof parsed.sourceContext.boundTaskRef === 'string' ? parsed.sourceContext.boundTaskRef : null,
417
+ boundTaskTitle: typeof parsed.sourceContext.boundTaskTitle === 'string' ? parsed.sourceContext.boundTaskTitle : null,
418
+ ownerName: typeof parsed.sourceContext.ownerName === 'string' ? parsed.sourceContext.ownerName : null,
419
+ participants: Array.isArray(parsed.sourceContext.participants) ? parsed.sourceContext.participants : [],
420
+ myRole: typeof parsed.sourceContext.myRole === 'string' ? parsed.sourceContext.myRole : null,
421
+ }
422
+ : null,
423
+ });
424
+ }
425
+ catch {
426
+ return normalizeConversationHandoffPayload({ goal: '(missing goal)' });
427
+ }
428
+ }
429
+ function resolveSourceSurfaceConversation(db, params) {
430
+ const normalizedThreadRootId = params.threadRootId?.trim() || '';
431
+ if (params.replyTarget.startsWith('dm:@')) {
432
+ const row = db.prepare(`SELECT id as conversationId,
433
+ agent_id as agentId,
434
+ channel_id as channelId,
435
+ reply_target as replyTarget,
436
+ thread_kind as threadKind,
437
+ thread_root_id as threadRootId
438
+ FROM conversations
439
+ WHERE agent_id = ?
440
+ AND thread_kind = 'direct'
441
+ AND reply_target = ?
442
+ AND COALESCE(thread_root_id, '') = ?
443
+ ORDER BY updated_at DESC
444
+ LIMIT 1`).get(params.agentId, params.replyTarget, normalizedThreadRootId);
445
+ return row ?? null;
446
+ }
447
+ const row = db.prepare(`SELECT id as conversationId,
448
+ agent_id as agentId,
449
+ channel_id as channelId,
450
+ reply_target as replyTarget,
451
+ thread_kind as threadKind,
452
+ thread_root_id as threadRootId
453
+ FROM conversations
454
+ WHERE agent_id = ?
455
+ AND thread_kind = 'branch'
456
+ AND channel_id = ?
457
+ AND reply_target = ?
458
+ AND COALESCE(thread_root_id, '') = ?
459
+ ORDER BY updated_at DESC
460
+ LIMIT 1`).get(params.agentId, params.channelId, params.replyTarget, normalizedThreadRootId);
461
+ return row ?? null;
462
+ }
463
+ function loadLatestContinueThereSourceHandoff(db, sourceConversationId) {
464
+ const row = db.prepare(`SELECT handoff_id as handoffId,
465
+ agent_id as agentId,
466
+ source_conversation_id as sourceConversationId,
467
+ target_conversation_id as targetConversationId,
468
+ target_reply_target as targetReplyTarget,
469
+ workflow_kind as workflowKind,
470
+ source_disposition as sourceDisposition,
471
+ task_id as taskId,
472
+ task_number as taskNumber,
473
+ h.mode as mode,
474
+ h.status as status,
475
+ h.payload_json as payloadJson,
476
+ h.created_at as createdAt,
477
+ h.updated_at as updatedAt
478
+ FROM conversation_handoffs h
479
+ WHERE h.source_conversation_id = ?
480
+ AND h.mode = 'continue_there'
481
+ AND h.source_disposition = 'cancel_current_run'
482
+ ORDER BY created_at DESC, updated_at DESC
483
+ LIMIT 1`).get(sourceConversationId);
484
+ if (!row)
485
+ return null;
486
+ const handoff = mapConversationHandoffRow(row);
487
+ if (handoff.status !== 'failed')
488
+ return handoff;
489
+ if (handoff.payload.cancelRunId)
490
+ return handoff;
491
+ const legacyCancelledEvent = db.prepare(`SELECT 1 as found
492
+ FROM events e
493
+ WHERE json_extract(e.payload_json, '$.kind') = 'conversation_handoff'
494
+ AND json_extract(e.payload_json, '$.handoffId') = ?
495
+ AND json_extract(e.payload_json, '$.cancelRunId') IS NOT NULL
496
+ LIMIT 1`).get(handoff.handoffId);
497
+ return legacyCancelledEvent ? handoff : null;
498
+ }
499
+ function hasLaterAgentVisibleMessageOnSurface(db, conversation, createdAtIso) {
500
+ const createdAtMs = Date.parse(createdAtIso);
501
+ if (!Number.isFinite(createdAtMs))
502
+ return false;
503
+ const messageChannelId = conversation.threadKind === 'direct'
504
+ ? `dm:${conversation.agentId ?? ''}`
505
+ : conversation.channelId;
506
+ if (!messageChannelId || !conversation.replyTarget)
507
+ return false;
508
+ const row = db.prepare(`SELECT 1
509
+ FROM channel_messages
510
+ WHERE channel_id = ?
511
+ AND target = ?
512
+ AND sender_type = 'agent'
513
+ AND COALESCE(thread_root_id, '') = ?
514
+ AND created_at > ?
515
+ ORDER BY created_at DESC, seq DESC
516
+ LIMIT 1`).get(messageChannelId, conversation.replyTarget, conversation.threadRootId ?? '', createdAtMs);
517
+ return Boolean(row);
518
+ }
519
+ function updateLatestOpenConversationHandoff(db, params) {
520
+ const record = findLatestOpenConversationHandoff(db, params.targetConversationId, params.targetRunId ?? null);
521
+ if (!record)
522
+ return null;
523
+ const now = Date.now();
524
+ const payload = normalizeConversationHandoffPayload({
525
+ ...record.payload,
526
+ ...params.payload,
527
+ goal: params.payload?.goal ?? record.payload.goal,
528
+ });
529
+ db.prepare(`UPDATE conversation_handoffs
530
+ SET target_conversation_id = ?,
531
+ payload_json = ?,
532
+ status = ?,
533
+ updated_at = ?
534
+ WHERE handoff_id = ?`).run(record.targetConversationId ?? params.targetConversationId, JSON.stringify(payload), params.status, now, record.handoffId);
535
+ return {
536
+ ...record,
537
+ targetConversationId: record.targetConversationId ?? params.targetConversationId,
538
+ status: params.status,
539
+ payload,
540
+ updatedAt: new Date(now).toISOString(),
541
+ };
542
+ }
543
+ function findLatestOpenConversationHandoff(db, targetConversationId, targetRunId) {
544
+ const rows = db.prepare(`SELECT handoff_id as handoffId,
545
+ agent_id as agentId,
546
+ source_conversation_id as sourceConversationId,
547
+ target_conversation_id as targetConversationId,
548
+ target_reply_target as targetReplyTarget,
549
+ workflow_kind as workflowKind,
550
+ source_disposition as sourceDisposition,
551
+ task_id as taskId,
552
+ task_number as taskNumber,
553
+ mode,
554
+ status,
555
+ payload_json as payloadJson,
556
+ created_at as createdAt,
557
+ updated_at as updatedAt
558
+ FROM conversation_handoffs
559
+ WHERE target_conversation_id = ?
560
+ AND status IN ('started', 'accepted')
561
+ ORDER BY updated_at DESC, created_at DESC
562
+ LIMIT 12`).all(targetConversationId);
563
+ const records = rows.map(mapConversationHandoffRow);
564
+ if (targetRunId) {
565
+ const exact = records.find((record) => record.payload.targetRunId === targetRunId);
566
+ if (exact)
567
+ return exact;
568
+ const pending = records.find((record) => !record.payload.targetRunId);
569
+ if (pending)
570
+ return pending;
571
+ }
572
+ return records[0] ?? null;
573
+ }
574
+ function mapConversationHandoffRow(row) {
575
+ return {
576
+ handoffId: row.handoffId,
577
+ agentId: row.agentId,
578
+ sourceConversationId: row.sourceConversationId,
579
+ targetConversationId: row.targetConversationId ?? null,
580
+ targetReplyTarget: row.targetReplyTarget,
581
+ workflowKind: row.workflowKind,
582
+ sourceDisposition: row.sourceDisposition,
583
+ taskId: row.taskId ?? null,
584
+ taskNumber: typeof row.taskNumber === 'number' ? Math.floor(row.taskNumber) : null,
585
+ mode: row.mode,
586
+ status: row.status,
587
+ payload: parseConversationHandoffPayload(row.payloadJson),
588
+ createdAt: new Date(row.createdAt).toISOString(),
589
+ updatedAt: new Date(row.updatedAt).toISOString(),
590
+ };
591
+ }
592
+ function normalizeRequiredText(value) {
593
+ if (typeof value !== 'string')
594
+ return null;
595
+ const trimmed = value.trim();
596
+ return trimmed ? trimmed : null;
597
+ }
598
+ function normalizeStringList(value) {
599
+ if (!Array.isArray(value))
600
+ return [];
601
+ return value
602
+ .map((item) => summarizeText(item, 300))
603
+ .filter((item) => Boolean(item));
604
+ }
605
+ function summarizeText(value, limit) {
606
+ if (typeof value !== 'string')
607
+ return null;
608
+ const normalized = value.replace(/\s+/g, ' ').trim();
609
+ if (!normalized)
610
+ return null;
611
+ return normalized.length > limit ? `${normalized.slice(0, limit - 3)}...` : normalized;
612
+ }