@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
package/dist/main.js ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { config as loadDotenv } from 'dotenv';
5
+ import { acquireProcessLock, openDb, migrate, log } from '@bbigbang/runtime-acp';
6
+ import { resolveGatewayHomeDir, loadConfig } from './config.js';
7
+ import { ConversationManager } from './web/conversationManager.js';
8
+ import { startServer } from './web/server.js';
9
+ import { NodeRegistry } from './services/nodeRegistry.js';
10
+ import { AgentWorkspaceBroker } from './services/agentWorkspaceBroker.js';
11
+ import { CodexAppServerBroker } from './services/codexAppServerBroker.js';
12
+ import { AgentRuntimeCleanupBroker } from './services/agentRuntimeCleanupBroker.js';
13
+ import { reconcileNodeStateOnStartup } from './services/nodeStateReconciler.js';
14
+ import { hasAdminUser, createInviteToken } from './services/auth.js';
15
+ loadDotenv({
16
+ path: path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..', '.env'),
17
+ });
18
+ async function main() {
19
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
20
+ console.log('Usage: bigbang-core');
21
+ return;
22
+ }
23
+ const gatewayHome = resolveGatewayHomeDir();
24
+ const lock = acquireProcessLock(path.join(gatewayHome, 'gateway.lock'));
25
+ const cleanup = () => {
26
+ lock.release();
27
+ };
28
+ process.on('exit', cleanup);
29
+ process.on('SIGINT', () => {
30
+ cleanup();
31
+ process.exit(0);
32
+ });
33
+ process.on('SIGTERM', () => {
34
+ cleanup();
35
+ process.exit(0);
36
+ });
37
+ const config = await loadConfig({ interactiveBootstrap: true });
38
+ const db = openDb(config.dbPath);
39
+ migrate(db);
40
+ reconcileNodeStateOnStartup(db);
41
+ // On first startup with no admin user, generate an invite token and print it
42
+ if (!hasAdminUser(db)) {
43
+ const invite = createInviteToken(db);
44
+ const inviteUrl = `http://localhost:${config.webPort}/?invite=${invite.token}`;
45
+ log.info('');
46
+ log.info('═══════════════════════════════════════════════════');
47
+ log.info(' No admin account found. Complete initial setup:');
48
+ log.info(` ${inviteUrl}`);
49
+ log.info(` Token expires in 24 hours.`);
50
+ log.info('═══════════════════════════════════════════════════');
51
+ log.info('');
52
+ }
53
+ const nodeRegistry = new NodeRegistry();
54
+ const workspaceBroker = new AgentWorkspaceBroker({ nodeRegistry });
55
+ const codexAppServerBroker = new CodexAppServerBroker({ nodeRegistry });
56
+ const agentRuntimeCleanupBroker = new AgentRuntimeCleanupBroker({ nodeRegistry });
57
+ const manager = new ConversationManager({
58
+ db,
59
+ config,
60
+ nodeRegistry,
61
+ codexAppServerBroker,
62
+ agentRuntimeCleanupBroker,
63
+ });
64
+ manager.start();
65
+ await startServer({
66
+ port: config.webPort,
67
+ host: config.webHost,
68
+ conversationManager: manager,
69
+ db,
70
+ nodeRegistry,
71
+ workspaceBroker,
72
+ codexAppServerBroker,
73
+ agentRuntimeCleanupBroker,
74
+ });
75
+ log.info('agent-node started', {
76
+ port: config.webPort,
77
+ host: config.webHost,
78
+ workspaceRoot: config.workspaceRoot,
79
+ dbPath: path.resolve(config.dbPath),
80
+ });
81
+ const shutdown = () => {
82
+ log.warn('Shutting down...');
83
+ manager.close();
84
+ db.close();
85
+ process.exit(0);
86
+ };
87
+ process.on('SIGINT', shutdown);
88
+ process.on('SIGTERM', shutdown);
89
+ }
90
+ await main();
@@ -0,0 +1,206 @@
1
+ export const MAX_SAFE_NODE_EVENT_PAYLOAD_BYTES = 512 * 1024;
2
+ export const MAX_NODE_EVENT_FIELD_PREVIEW_CHARS = 8 * 1024;
3
+ function buildNodeEventTypeExpression() {
4
+ return `CASE
5
+ WHEN json_valid(payload_json) THEN json_extract(payload_json, '$.type')
6
+ ELSE NULL
7
+ END`;
8
+ }
9
+ function buildNodeEventByteLengthExpression() {
10
+ return `length(CAST(payload_json AS BLOB))`;
11
+ }
12
+ function buildExactJsonExtract(path) {
13
+ return `CASE
14
+ WHEN json_valid(payload_json) THEN json_extract(payload_json, '${path}')
15
+ ELSE NULL
16
+ END`;
17
+ }
18
+ function parseProjectedStructuredValue(value) {
19
+ if (typeof value !== 'string')
20
+ return value;
21
+ const trimmed = value.trim();
22
+ if (!trimmed)
23
+ return value;
24
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('['))
25
+ return value;
26
+ try {
27
+ return JSON.parse(trimmed);
28
+ }
29
+ catch {
30
+ return value;
31
+ }
32
+ }
33
+ function toOptionalNumber(value) {
34
+ return typeof value === 'number' ? value : undefined;
35
+ }
36
+ function materializeProjectedNodeEvent(row) {
37
+ switch (row.type) {
38
+ case 'content.delta':
39
+ return typeof row.text === 'string'
40
+ ? { type: 'content.delta', text: row.text }
41
+ : null;
42
+ case 'activity.delta':
43
+ return typeof row.text === 'string'
44
+ ? { type: 'activity.delta', text: row.text }
45
+ : null;
46
+ case 'thinking.delta':
47
+ return typeof row.text === 'string'
48
+ ? { type: 'thinking.delta', text: row.text }
49
+ : null;
50
+ case 'plan.update':
51
+ return typeof row.title === 'string'
52
+ ? {
53
+ type: 'plan.update',
54
+ title: row.title,
55
+ ...(typeof row.detail === 'string' ? { detail: row.detail } : {}),
56
+ ...(typeof row.eventCreatedAt === 'number' ? { createdAt: row.eventCreatedAt } : {}),
57
+ }
58
+ : null;
59
+ case 'task.update':
60
+ return typeof row.title === 'string'
61
+ ? {
62
+ type: 'task.update',
63
+ title: row.title,
64
+ ...(typeof row.detail === 'string' ? { detail: row.detail } : {}),
65
+ ...(typeof row.eventCreatedAt === 'number' ? { createdAt: row.eventCreatedAt } : {}),
66
+ }
67
+ : null;
68
+ case 'tool.call':
69
+ return typeof row.toolCallId === 'string' && typeof row.name === 'string'
70
+ ? {
71
+ type: 'tool.call',
72
+ toolCallId: row.toolCallId,
73
+ name: row.name,
74
+ input: parseProjectedStructuredValue(row.inputJson),
75
+ ...(typeof row.status === 'string' ? { status: row.status } : {}),
76
+ ...(typeof row.startedAt === 'number' ? { startedAt: row.startedAt } : {}),
77
+ }
78
+ : null;
79
+ case 'tool.result':
80
+ return typeof row.toolCallId === 'string'
81
+ ? {
82
+ type: 'tool.result',
83
+ toolCallId: row.toolCallId,
84
+ output: row.output ?? '',
85
+ ...(row.error == null ? {} : { error: Boolean(row.error) }),
86
+ ...(typeof row.status === 'string' ? { status: row.status } : {}),
87
+ ...(typeof row.endedAt === 'number' ? { endedAt: row.endedAt } : {}),
88
+ }
89
+ : null;
90
+ case 'run.usage':
91
+ const inputTokens = toOptionalNumber(row.inputTokens);
92
+ const cachedInputTokens = toOptionalNumber(row.cachedInputTokens);
93
+ const outputTokens = toOptionalNumber(row.outputTokens);
94
+ const reasoningOutputTokens = toOptionalNumber(row.reasoningOutputTokens);
95
+ const totalTokens = toOptionalNumber(row.totalTokens);
96
+ const currentInputTokens = toOptionalNumber(row.currentInputTokens);
97
+ const currentCachedInputTokens = toOptionalNumber(row.currentCachedInputTokens);
98
+ const modelContextWindow = toOptionalNumber(row.modelContextWindow);
99
+ const createdAt = toOptionalNumber(row.eventCreatedAt);
100
+ return {
101
+ type: 'run.usage',
102
+ ...(inputTokens !== undefined ? { inputTokens } : {}),
103
+ ...(cachedInputTokens !== undefined ? { cachedInputTokens } : {}),
104
+ ...(outputTokens !== undefined ? { outputTokens } : {}),
105
+ ...(reasoningOutputTokens !== undefined ? { reasoningOutputTokens } : {}),
106
+ ...(totalTokens !== undefined ? { totalTokens } : {}),
107
+ ...(currentInputTokens !== undefined ? { currentInputTokens } : {}),
108
+ ...(currentCachedInputTokens !== undefined ? { currentCachedInputTokens } : {}),
109
+ ...(modelContextWindow !== undefined ? { modelContextWindow } : {}),
110
+ ...(createdAt !== undefined ? { createdAt } : {}),
111
+ };
112
+ case 'runtime.compact':
113
+ return typeof row.threadId === 'string' && typeof row.turnId === 'string' && typeof row.eventKey === 'string'
114
+ ? {
115
+ type: 'runtime.compact',
116
+ threadId: row.threadId,
117
+ turnId: row.turnId,
118
+ ...(typeof row.itemId === 'string' ? { itemId: row.itemId } : {}),
119
+ source: row.source === 'raw_response_item' || row.source === 'thread_item'
120
+ ? row.source
121
+ : 'thread_compacted',
122
+ eventKey: row.eventKey,
123
+ ...(typeof row.eventCreatedAt === 'number' ? { createdAt: row.eventCreatedAt } : {}),
124
+ }
125
+ : null;
126
+ case 'run.steer.result':
127
+ return typeof row.requestId === 'string' && typeof row.eventRunId === 'string' && row.ok !== null
128
+ ? {
129
+ type: 'run.steer.result',
130
+ requestId: row.requestId,
131
+ runId: row.eventRunId,
132
+ ok: Boolean(row.ok),
133
+ ...(typeof row.errorText === 'string' ? { error: row.errorText } : {}),
134
+ ...(typeof row.eventCreatedAt === 'number' ? { createdAt: row.eventCreatedAt } : {}),
135
+ }
136
+ : null;
137
+ default:
138
+ return null;
139
+ }
140
+ }
141
+ export function* iterateParsedNodeEvents(db, params) {
142
+ if (params.types.length === 0)
143
+ return;
144
+ const typeExpr = buildNodeEventTypeExpression();
145
+ const byteLengthExpr = buildNodeEventByteLengthExpression();
146
+ const maxPayloadBytes = params.maxPayloadBytes ?? MAX_SAFE_NODE_EVENT_PAYLOAD_BYTES;
147
+ const previewingExtract = (path) => `CASE
148
+ WHEN ${byteLengthExpr} <= ${maxPayloadBytes} THEN ${buildExactJsonExtract(path)}
149
+ WHEN NOT json_valid(payload_json) THEN NULL
150
+ WHEN json_extract(payload_json, '${path}') IS NULL THEN NULL
151
+ WHEN length(CAST(json_extract(payload_json, '${path}') AS BLOB)) <= ${MAX_NODE_EVENT_FIELD_PREVIEW_CHARS}
152
+ THEN json_extract(payload_json, '${path}')
153
+ WHEN typeof(json_extract(payload_json, '${path}')) = 'text'
154
+ THEN substr(json_extract(payload_json, '${path}'), 1, ${MAX_NODE_EVENT_FIELD_PREVIEW_CHARS}) || '... [truncated]'
155
+ ELSE substr(CAST(json_extract(payload_json, '${path}') AS TEXT), 1, ${MAX_NODE_EVENT_FIELD_PREVIEW_CHARS}) || '... [truncated]'
156
+ END`;
157
+ const placeholders = params.types.map(() => '?').join(', ');
158
+ const rows = db.prepare(`SELECT seq,
159
+ created_at as createdAt,
160
+ ${byteLengthExpr} as payloadBytes,
161
+ ${typeExpr} as type,
162
+ ${previewingExtract('$.text')} as text,
163
+ ${previewingExtract('$.title')} as title,
164
+ ${previewingExtract('$.detail')} as detail,
165
+ ${buildExactJsonExtract('$.toolCallId')} as toolCallId,
166
+ ${buildExactJsonExtract('$.requestId')} as requestId,
167
+ ${buildExactJsonExtract('$.runId')} as eventRunId,
168
+ ${previewingExtract('$.name')} as name,
169
+ ${previewingExtract('$.input')} as inputJson,
170
+ ${previewingExtract('$.output')} as output,
171
+ ${buildExactJsonExtract('$.error')} as error,
172
+ ${previewingExtract('$.error')} as errorText,
173
+ ${buildExactJsonExtract('$.ok')} as ok,
174
+ ${buildExactJsonExtract('$.status')} as status,
175
+ ${buildExactJsonExtract('$.startedAt')} as startedAt,
176
+ ${buildExactJsonExtract('$.endedAt')} as endedAt,
177
+ ${buildExactJsonExtract('$.createdAt')} as eventCreatedAt,
178
+ ${buildExactJsonExtract('$.inputTokens')} as inputTokens,
179
+ ${buildExactJsonExtract('$.cachedInputTokens')} as cachedInputTokens,
180
+ ${buildExactJsonExtract('$.outputTokens')} as outputTokens,
181
+ ${buildExactJsonExtract('$.reasoningOutputTokens')} as reasoningOutputTokens,
182
+ ${buildExactJsonExtract('$.totalTokens')} as totalTokens,
183
+ ${buildExactJsonExtract('$.currentInputTokens')} as currentInputTokens,
184
+ ${buildExactJsonExtract('$.currentCachedInputTokens')} as currentCachedInputTokens,
185
+ ${buildExactJsonExtract('$.modelContextWindow')} as modelContextWindow,
186
+ ${buildExactJsonExtract('$.threadId')} as threadId,
187
+ ${buildExactJsonExtract('$.turnId')} as turnId,
188
+ ${buildExactJsonExtract('$.itemId')} as itemId,
189
+ ${buildExactJsonExtract('$.source')} as source,
190
+ ${buildExactJsonExtract('$.eventKey')} as eventKey
191
+ FROM events
192
+ WHERE run_id = ?
193
+ AND method = 'node/event'
194
+ AND ${typeExpr} IN (${placeholders})
195
+ ORDER BY seq ASC`).iterate(params.runId, ...params.types);
196
+ for (const row of rows) {
197
+ const event = materializeProjectedNodeEvent(row);
198
+ yield {
199
+ seq: row.seq,
200
+ createdAt: row.createdAt,
201
+ payloadBytes: row.payloadBytes,
202
+ event,
203
+ skippedOversize: row.payloadBytes > maxPayloadBytes,
204
+ };
205
+ }
206
+ }
@@ -0,0 +1,50 @@
1
+ const BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000;
2
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
3
+ export function parseDreamScheduleHourMinute(schedule) {
4
+ const match = /^(\d{1,2}):(\d{2})$/.exec(schedule.trim());
5
+ if (!match)
6
+ return null;
7
+ const hour = Number(match[1]);
8
+ const minute = Number(match[2]);
9
+ if (!Number.isInteger(hour) || !Number.isInteger(minute))
10
+ return null;
11
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59)
12
+ return null;
13
+ return { hour, minute };
14
+ }
15
+ export function getBeijingDateParts(nowMs) {
16
+ const beijing = new Date(nowMs + BEIJING_OFFSET_MS);
17
+ return {
18
+ year: beijing.getUTCFullYear(),
19
+ month: beijing.getUTCMonth(),
20
+ day: beijing.getUTCDate(),
21
+ hour: beijing.getUTCHours(),
22
+ minute: beijing.getUTCMinutes(),
23
+ };
24
+ }
25
+ export function isDreamScheduleDue(schedule, nowMs, lastDreamAt) {
26
+ const parsed = parseDreamScheduleHourMinute(schedule);
27
+ if (!parsed)
28
+ return false;
29
+ const beijing = getBeijingDateParts(nowMs);
30
+ const scheduleMinutes = parsed.hour * 60 + parsed.minute;
31
+ const nowMinutes = beijing.hour * 60 + beijing.minute;
32
+ if (nowMinutes < scheduleMinutes)
33
+ return false;
34
+ if (lastDreamAt == null)
35
+ return true;
36
+ if (nowMs - lastDreamAt < ONE_DAY_MS)
37
+ return false;
38
+ const lastBeijing = getBeijingDateParts(lastDreamAt);
39
+ const sameBeijingDay = lastBeijing.year === beijing.year
40
+ && lastBeijing.month === beijing.month
41
+ && lastBeijing.day === beijing.day;
42
+ return !sameBeijingDay;
43
+ }
44
+ export function shouldTriggerDream(params) {
45
+ if (!params.dreamEnabled)
46
+ return false;
47
+ if (!isDreamScheduleDue(params.dreamSchedule, params.nowMs, params.lastDreamAt))
48
+ return false;
49
+ return params.newMessageCount >= Math.max(1, params.dreamMinMessages);
50
+ }
@@ -0,0 +1,65 @@
1
+ import { log } from '@bbigbang/runtime-acp';
2
+ import { countAgentMessagesSinceSeq } from '../services/memoryService.js';
3
+ import { shouldTriggerDream } from './dreamLogic.js';
4
+ export function startDreamScheduler(params) {
5
+ const intervalMs = params.intervalMs ?? 60_000;
6
+ const inFlight = new Set();
7
+ const tick = async () => {
8
+ const nowMs = Date.now();
9
+ const agents = params.db.prepare(`SELECT agent_id as agentId,
10
+ memory_network_enabled as memoryNetworkEnabled,
11
+ dream_enabled as dreamEnabled,
12
+ dream_schedule as dreamSchedule,
13
+ dream_min_messages as dreamMinMessages,
14
+ last_dream_at as lastDreamAt,
15
+ last_dream_seq as lastDreamSeq,
16
+ node_id as nodeId,
17
+ dream_run_in_progress_at as dreamRunInProgressAt
18
+ FROM agents
19
+ WHERE deleted_at IS NULL
20
+ AND COALESCE(agent_role, 'user') = 'user'
21
+ AND memory_network_enabled = 1
22
+ AND dream_enabled = 1
23
+ AND node_id IS NOT NULL
24
+ AND (dream_run_in_progress_at IS NULL OR dream_run_in_progress_at < ?)`).all(nowMs - 30 * 60 * 1000);
25
+ for (const agent of agents) {
26
+ if (inFlight.has(agent.agentId))
27
+ continue;
28
+ const newMessageCount = countAgentMessagesSinceSeq(params.db, agent.agentId, agent.lastDreamSeq);
29
+ const eligible = shouldTriggerDream({
30
+ dreamEnabled: agent.dreamEnabled === 1,
31
+ dreamSchedule: agent.dreamSchedule ?? '02:00',
32
+ dreamMinMessages: agent.dreamMinMessages ?? 5,
33
+ lastDreamAt: agent.lastDreamAt,
34
+ newMessageCount,
35
+ nowMs,
36
+ });
37
+ if (!eligible)
38
+ continue;
39
+ inFlight.add(agent.agentId);
40
+ try {
41
+ log.info('[dream-scheduler] triggering dream', {
42
+ agentId: agent.agentId,
43
+ newMessageCount,
44
+ });
45
+ await params.dispatchDream(agent.agentId);
46
+ }
47
+ catch (error) {
48
+ log.warn('[dream-scheduler] dream dispatch failed', {
49
+ agentId: agent.agentId,
50
+ error: String(error?.message ?? error),
51
+ });
52
+ }
53
+ finally {
54
+ inFlight.delete(agent.agentId);
55
+ }
56
+ }
57
+ };
58
+ const timer = setInterval(() => {
59
+ void tick();
60
+ }, intervalMs);
61
+ timer.unref?.();
62
+ return {
63
+ stop: () => clearInterval(timer),
64
+ };
65
+ }