@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,684 @@
|
|
|
1
|
+
import { buildThreadShortId } from '@bbigbang/protocol';
|
|
2
|
+
import { getUnifiedSurfaceUnreadCheckpoint, listUnifiedRecentSurfaceParticipants, } from './collaborationSurfaceState.js';
|
|
3
|
+
import { getBoundTaskForThread, listThreadCollaborationParticipants } from './threadTaskBindings.js';
|
|
4
|
+
import { findThreadRootMessageId } from './threadRoots.js';
|
|
5
|
+
import { isClearedTaskRootTarget } from './clearedTaskRoots.js';
|
|
6
|
+
import { hasVisiblePromptHistoryContent } from './promptHistorySanitizer.js';
|
|
7
|
+
import { buildChannelActivationContextSections, } from './channelActivationPrompt.js';
|
|
8
|
+
import { buildDirectActivationContextSections, } from './directActivationPrompt.js';
|
|
9
|
+
import { historyLimitForSurfaceActivationTier, resolveChannelSurfaceActivationPolicy, resolveDirectSurfaceActivationPolicy, } from './surfaceActivationPolicy.js';
|
|
10
|
+
import { buildDefaultChannelMemoryHints, buildDefaultDirectMemoryHints, buildWorkspaceMemoryReminder, getCurrentISOWeek, shouldIncludeDmContextSnapshot, shouldIncludeOpenTaskBoardSummary, } from './workspaceMemoryHints.js';
|
|
11
|
+
import { createPromptContextSection, renderPromptContextSections, } from './promptContextSections.js';
|
|
12
|
+
import { buildSourceSurfaceHandoffReminderSection } from './conversationHandoffs.js';
|
|
13
|
+
import { buildSurfaceSystemStatusSection } from './surfaceSystemStatus.js';
|
|
14
|
+
import { TASK_LIFECYCLE_SOURCE } from './taskLifecycleMessages.js';
|
|
15
|
+
import { PANEL_ACTION_SOURCE } from './panelActionMessages.js';
|
|
16
|
+
const DEFAULT_DM_TASK_THREAD_CONTEXT_MESSAGE_LIMIT = 4;
|
|
17
|
+
const DM_THREAD_CONTEXT_SNAPSHOT_LIMIT = 6;
|
|
18
|
+
function ensureDmThreadContextSnapshotTable(db) {
|
|
19
|
+
db.exec(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS dm_thread_context_snapshots (
|
|
21
|
+
channel_id TEXT NOT NULL,
|
|
22
|
+
thread_root_id TEXT NOT NULL,
|
|
23
|
+
trigger_message_id TEXT,
|
|
24
|
+
snapshot_json TEXT NOT NULL,
|
|
25
|
+
created_at INTEGER NOT NULL,
|
|
26
|
+
PRIMARY KEY (channel_id, thread_root_id)
|
|
27
|
+
);
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
function loadActivationMessageById(db, channelId, messageId) {
|
|
31
|
+
const row = db.prepare(`SELECT message_id as messageId, seq, target, sender_name as senderName, sender_type as senderType,
|
|
32
|
+
content, created_at as createdAt, attachment_ids as attachmentIds, message_source as messageSource,
|
|
33
|
+
deleted_at as deletedAt
|
|
34
|
+
FROM channel_messages
|
|
35
|
+
WHERE channel_id = ? AND message_id = ?
|
|
36
|
+
LIMIT 1`).get(channelId, messageId);
|
|
37
|
+
if (!row)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (isClearedTaskRootTarget(row.target))
|
|
40
|
+
return undefined;
|
|
41
|
+
return hydrateActivationContextMessage(row);
|
|
42
|
+
}
|
|
43
|
+
function loadThreadRootMessage(db, channelId, threadRootId, rootMessageId) {
|
|
44
|
+
if (rootMessageId) {
|
|
45
|
+
const exact = loadActivationMessageById(db, channelId, rootMessageId);
|
|
46
|
+
if (exact)
|
|
47
|
+
return exact;
|
|
48
|
+
}
|
|
49
|
+
const matchedMessageId = findThreadRootMessageId(db, channelId, threadRootId);
|
|
50
|
+
return matchedMessageId
|
|
51
|
+
? loadActivationMessageById(db, channelId, matchedMessageId)
|
|
52
|
+
: undefined;
|
|
53
|
+
}
|
|
54
|
+
function parseDmThreadContextSnapshot(raw) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(raw);
|
|
57
|
+
if (!Array.isArray(parsed))
|
|
58
|
+
return [];
|
|
59
|
+
return parsed
|
|
60
|
+
.filter((value) => (!!value
|
|
61
|
+
&& typeof value === 'object'
|
|
62
|
+
&& (() => {
|
|
63
|
+
const message = value;
|
|
64
|
+
return typeof message.messageId === 'string'
|
|
65
|
+
&& typeof message.seq === 'number'
|
|
66
|
+
&& typeof message.target === 'string'
|
|
67
|
+
&& typeof message.senderName === 'string'
|
|
68
|
+
&& typeof message.senderType === 'string'
|
|
69
|
+
&& typeof message.content === 'string'
|
|
70
|
+
&& typeof message.createdAt === 'number'
|
|
71
|
+
&& (message.messageSource == null
|
|
72
|
+
|| typeof message.messageSource === 'string')
|
|
73
|
+
&& (message.attachmentIds == null
|
|
74
|
+
|| (Array.isArray(message.attachmentIds)
|
|
75
|
+
&& message.attachmentIds.every((attachmentId) => typeof attachmentId === 'string')));
|
|
76
|
+
})()))
|
|
77
|
+
.filter(isPromptHistoryMessage)
|
|
78
|
+
.sort((a, b) => a.seq - b.seq);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function parseAttachmentIds(raw) {
|
|
85
|
+
if (!raw)
|
|
86
|
+
return undefined;
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(raw);
|
|
89
|
+
if (!Array.isArray(parsed))
|
|
90
|
+
return undefined;
|
|
91
|
+
const attachmentIds = parsed.filter((value) => typeof value === 'string');
|
|
92
|
+
return attachmentIds.length > 0 ? attachmentIds : undefined;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function dmThreadSnapshotReferencesDeletedMessage(db, params) {
|
|
99
|
+
const messageIds = Array.from(new Set([
|
|
100
|
+
params.triggerMessageId,
|
|
101
|
+
...params.messages.map((message) => message.messageId),
|
|
102
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0)));
|
|
103
|
+
if (messageIds.length === 0)
|
|
104
|
+
return false;
|
|
105
|
+
const placeholders = messageIds.map(() => '?').join(', ');
|
|
106
|
+
const row = db.prepare(`SELECT 1
|
|
107
|
+
FROM channel_messages
|
|
108
|
+
WHERE channel_id = ?
|
|
109
|
+
AND message_id IN (${placeholders})
|
|
110
|
+
AND deleted_at IS NOT NULL
|
|
111
|
+
LIMIT 1`).get(params.channelId, ...messageIds);
|
|
112
|
+
return Boolean(row);
|
|
113
|
+
}
|
|
114
|
+
function hydrateActivationContextMessage(row) {
|
|
115
|
+
const attachmentIds = row.deletedAt == null ? parseAttachmentIds(row.attachmentIds) : undefined;
|
|
116
|
+
return {
|
|
117
|
+
messageId: row.messageId,
|
|
118
|
+
seq: row.seq,
|
|
119
|
+
target: row.target,
|
|
120
|
+
senderName: row.senderName,
|
|
121
|
+
senderType: row.senderType,
|
|
122
|
+
content: row.content,
|
|
123
|
+
createdAt: row.createdAt,
|
|
124
|
+
messageSource: row.messageSource ?? null,
|
|
125
|
+
...(attachmentIds ? { attachmentIds } : {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function isPromptHistoryMessage(message) {
|
|
129
|
+
const source = message.messageSource ?? null;
|
|
130
|
+
return source !== TASK_LIFECYCLE_SOURCE && source !== PANEL_ACTION_SOURCE;
|
|
131
|
+
}
|
|
132
|
+
export function getDmThreadContextSnapshot(db, params) {
|
|
133
|
+
ensureDmThreadContextSnapshotTable(db);
|
|
134
|
+
const row = db.prepare(`SELECT trigger_message_id as triggerMessageId, snapshot_json as snapshotJson
|
|
135
|
+
FROM dm_thread_context_snapshots
|
|
136
|
+
WHERE channel_id = ? AND thread_root_id = ?
|
|
137
|
+
LIMIT 1`).get(params.channelId, params.threadRootId);
|
|
138
|
+
if (!row)
|
|
139
|
+
return undefined;
|
|
140
|
+
const messages = parseDmThreadContextSnapshot(row.snapshotJson);
|
|
141
|
+
if (dmThreadSnapshotReferencesDeletedMessage(db, {
|
|
142
|
+
channelId: params.channelId,
|
|
143
|
+
triggerMessageId: row.triggerMessageId,
|
|
144
|
+
messages,
|
|
145
|
+
})) {
|
|
146
|
+
db.prepare(`DELETE FROM dm_thread_context_snapshots
|
|
147
|
+
WHERE channel_id = ? AND thread_root_id = ?`).run(params.channelId, params.threadRootId);
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
triggerMessageId: row.triggerMessageId,
|
|
152
|
+
messages,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
export function ensureDmThreadContextSnapshot(db, params) {
|
|
156
|
+
ensureDmThreadContextSnapshotTable(db);
|
|
157
|
+
const existing = getDmThreadContextSnapshot(db, {
|
|
158
|
+
channelId: params.channelId,
|
|
159
|
+
threadRootId: params.threadRootId,
|
|
160
|
+
});
|
|
161
|
+
if (existing)
|
|
162
|
+
return existing;
|
|
163
|
+
const rootMessage = loadThreadRootMessage(db, params.channelId, params.threadRootId, params.rootMessageId);
|
|
164
|
+
if (!rootMessage)
|
|
165
|
+
return undefined;
|
|
166
|
+
const recentLimit = Math.max(1, params.recentLimit ?? DM_THREAD_CONTEXT_SNAPSHOT_LIMIT);
|
|
167
|
+
const recentMessages = db.prepare(`SELECT message_id as messageId, seq, target, sender_name as senderName, sender_type as senderType,
|
|
168
|
+
content, created_at as createdAt, attachment_ids as attachmentIds, message_source as messageSource,
|
|
169
|
+
deleted_at as deletedAt
|
|
170
|
+
FROM channel_messages
|
|
171
|
+
WHERE channel_id = ?
|
|
172
|
+
AND target = ?
|
|
173
|
+
AND thread_root_id IS NULL
|
|
174
|
+
AND seq < ?
|
|
175
|
+
AND COALESCE(message_source, '') NOT IN (?, ?)
|
|
176
|
+
ORDER BY seq DESC
|
|
177
|
+
LIMIT ?`).all(params.channelId, params.directTarget, rootMessage.seq, TASK_LIFECYCLE_SOURCE, PANEL_ACTION_SOURCE, recentLimit)
|
|
178
|
+
.map(hydrateActivationContextMessage)
|
|
179
|
+
.reverse();
|
|
180
|
+
const latestUserMessage = db.prepare(`SELECT message_id as messageId, seq, target, sender_name as senderName, sender_type as senderType,
|
|
181
|
+
content, created_at as createdAt, attachment_ids as attachmentIds, message_source as messageSource,
|
|
182
|
+
deleted_at as deletedAt
|
|
183
|
+
FROM channel_messages
|
|
184
|
+
WHERE channel_id = ?
|
|
185
|
+
AND target = ?
|
|
186
|
+
AND thread_root_id IS NULL
|
|
187
|
+
AND sender_type = 'user'
|
|
188
|
+
AND seq < ?
|
|
189
|
+
ORDER BY seq DESC
|
|
190
|
+
LIMIT 1`).get(params.channelId, params.directTarget, rootMessage.seq);
|
|
191
|
+
const fallbackTriggerMessageRow = latestUserMessage ?? db.prepare(`SELECT message_id as messageId, seq, target, sender_name as senderName, sender_type as senderType,
|
|
192
|
+
content, created_at as createdAt, attachment_ids as attachmentIds, message_source as messageSource,
|
|
193
|
+
deleted_at as deletedAt
|
|
194
|
+
FROM channel_messages
|
|
195
|
+
WHERE channel_id = ?
|
|
196
|
+
AND target = ?
|
|
197
|
+
AND thread_root_id IS NULL
|
|
198
|
+
AND seq < ?
|
|
199
|
+
AND COALESCE(message_source, '') NOT IN (?, ?)
|
|
200
|
+
ORDER BY seq DESC
|
|
201
|
+
LIMIT 1`).get(params.channelId, params.directTarget, rootMessage.seq, TASK_LIFECYCLE_SOURCE, PANEL_ACTION_SOURCE);
|
|
202
|
+
const fallbackTriggerMessage = fallbackTriggerMessageRow
|
|
203
|
+
? hydrateActivationContextMessage(fallbackTriggerMessageRow)
|
|
204
|
+
: undefined;
|
|
205
|
+
const triggerMessageId = rootMessage.senderType === 'user' && rootMessage.target === params.directTarget
|
|
206
|
+
? rootMessage.messageId
|
|
207
|
+
: fallbackTriggerMessage?.messageId ?? null;
|
|
208
|
+
const messageMap = new Map(recentMessages.map((message) => [message.messageId, message]));
|
|
209
|
+
if (triggerMessageId && triggerMessageId !== rootMessage.messageId && !messageMap.has(triggerMessageId)) {
|
|
210
|
+
const triggerMessage = loadActivationMessageById(db, params.channelId, triggerMessageId);
|
|
211
|
+
if (triggerMessage && triggerMessage.target === params.directTarget) {
|
|
212
|
+
messageMap.set(triggerMessage.messageId, triggerMessage);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const snapshot = {
|
|
216
|
+
triggerMessageId,
|
|
217
|
+
messages: [...messageMap.values()].sort((a, b) => a.seq - b.seq),
|
|
218
|
+
};
|
|
219
|
+
db.prepare(`INSERT INTO dm_thread_context_snapshots(channel_id, thread_root_id, trigger_message_id, snapshot_json, created_at)
|
|
220
|
+
VALUES(?, ?, ?, ?, ?)
|
|
221
|
+
ON CONFLICT(channel_id, thread_root_id) DO UPDATE SET
|
|
222
|
+
trigger_message_id = excluded.trigger_message_id,
|
|
223
|
+
snapshot_json = excluded.snapshot_json`).run(params.channelId, params.threadRootId, snapshot.triggerMessageId, JSON.stringify(snapshot.messages), Date.now());
|
|
224
|
+
return snapshot;
|
|
225
|
+
}
|
|
226
|
+
export function buildTargetActivationContext(db, params) {
|
|
227
|
+
const recentLimit = Math.max(1, params.recentLimit ?? 8);
|
|
228
|
+
const normalizedThreadRootId = params.threadRootId ?? null;
|
|
229
|
+
const openTasksMode = params.openTasksMode ?? 'full';
|
|
230
|
+
const includeDmContextSnapshot = params.includeDmContextSnapshot ?? true;
|
|
231
|
+
const dmActiveTaskThreadsMode = params.dmActiveTaskThreadsMode ?? 'full';
|
|
232
|
+
const checkpoint = Math.max(0, Math.floor(params.unreadCheckpointSeq ?? getUnifiedSurfaceUnreadCheckpoint(db, {
|
|
233
|
+
agentId: params.agentId,
|
|
234
|
+
channelId: params.channelId,
|
|
235
|
+
threadRootId: normalizedThreadRootId,
|
|
236
|
+
})));
|
|
237
|
+
const isDirectThreadTarget = normalizedThreadRootId !== null && params.replyTarget.startsWith('dm:@');
|
|
238
|
+
const threadClause = normalizedThreadRootId == null
|
|
239
|
+
? 'thread_root_id IS NULL'
|
|
240
|
+
: isDirectThreadTarget
|
|
241
|
+
? '(thread_root_id = ? OR thread_root_id IS NULL)'
|
|
242
|
+
: 'thread_root_id = ?';
|
|
243
|
+
const threadArgs = normalizedThreadRootId == null ? [] : [normalizedThreadRootId];
|
|
244
|
+
const recentUnreadClause = params.constrainRecentMessagesToUnread ? 'AND seq > ?' : '';
|
|
245
|
+
const recentUnreadArgs = params.constrainRecentMessagesToUnread ? [checkpoint] : [];
|
|
246
|
+
const recentSenderClause = params.excludeRecentMessageSenderId ? 'AND sender_id != ?' : '';
|
|
247
|
+
const recentSenderArgs = params.excludeRecentMessageSenderId ? [params.excludeRecentMessageSenderId] : [];
|
|
248
|
+
const recentMessages = db.prepare(`SELECT message_id as messageId, seq, target, sender_name as senderName, sender_type as senderType,
|
|
249
|
+
content, created_at as createdAt, attachment_ids as attachmentIds, message_source as messageSource,
|
|
250
|
+
deleted_at as deletedAt
|
|
251
|
+
FROM channel_messages
|
|
252
|
+
WHERE channel_id = ? AND target = ? AND ${threadClause} AND seq < ?
|
|
253
|
+
${recentUnreadClause}
|
|
254
|
+
${recentSenderClause}
|
|
255
|
+
AND COALESCE(message_source, '') NOT IN (?, ?)
|
|
256
|
+
ORDER BY seq DESC
|
|
257
|
+
LIMIT ?`).all(params.channelId, params.replyTarget, ...threadArgs, params.triggerSeq, ...recentUnreadArgs, ...recentSenderArgs, TASK_LIFECYCLE_SOURCE, PANEL_ACTION_SOURCE, recentLimit)
|
|
258
|
+
.map(hydrateActivationContextMessage)
|
|
259
|
+
.reverse()
|
|
260
|
+
.filter((message) => hasVisiblePromptHistoryContent(message.content, message.senderType));
|
|
261
|
+
const recentWindowStartSeq = recentMessages.length > 0
|
|
262
|
+
? recentMessages[0].seq
|
|
263
|
+
: undefined;
|
|
264
|
+
const unreadUpperBound = recentWindowStartSeq ?? params.triggerSeq;
|
|
265
|
+
const unreadRow = db.prepare(`SELECT COUNT(*) as count
|
|
266
|
+
FROM channel_messages
|
|
267
|
+
WHERE channel_id = ?
|
|
268
|
+
AND target = ?
|
|
269
|
+
AND ${threadClause}
|
|
270
|
+
AND seq > ?
|
|
271
|
+
AND seq < ?
|
|
272
|
+
AND COALESCE(message_source, '') NOT IN (?, ?)
|
|
273
|
+
AND sender_id != ?`).get(params.channelId, params.replyTarget, ...threadArgs, checkpoint, unreadUpperBound, TASK_LIFECYCLE_SOURCE, PANEL_ACTION_SOURCE, params.agentId);
|
|
274
|
+
const rootMessage = normalizedThreadRootId
|
|
275
|
+
? (() => {
|
|
276
|
+
const matchedMessageId = findThreadRootMessageId(db, params.channelId, normalizedThreadRootId);
|
|
277
|
+
return matchedMessageId
|
|
278
|
+
? loadActivationMessageById(db, params.channelId, matchedMessageId)
|
|
279
|
+
: undefined;
|
|
280
|
+
})()
|
|
281
|
+
: undefined;
|
|
282
|
+
const directTarget = normalizedThreadRootId && params.replyTarget.startsWith('dm:@')
|
|
283
|
+
? params.replyTarget.replace(/:[^:]+$/, '')
|
|
284
|
+
: null;
|
|
285
|
+
const dmContextSnapshot = includeDmContextSnapshot && normalizedThreadRootId && directTarget
|
|
286
|
+
? ensureDmThreadContextSnapshot(db, {
|
|
287
|
+
channelId: params.channelId,
|
|
288
|
+
directTarget,
|
|
289
|
+
threadRootId: normalizedThreadRootId,
|
|
290
|
+
rootMessageId: rootMessage?.messageId,
|
|
291
|
+
})
|
|
292
|
+
: undefined;
|
|
293
|
+
const recentParticipants = listUnifiedRecentSurfaceParticipants(db, {
|
|
294
|
+
channelId: params.channelId,
|
|
295
|
+
threadRootId: normalizedThreadRootId,
|
|
296
|
+
});
|
|
297
|
+
const boundTaskRow = getBoundTaskForThread(db, {
|
|
298
|
+
channelId: params.channelId,
|
|
299
|
+
threadRootId: normalizedThreadRootId,
|
|
300
|
+
});
|
|
301
|
+
const boundTask = boundTaskRow
|
|
302
|
+
? {
|
|
303
|
+
taskId: boundTaskRow.taskId,
|
|
304
|
+
taskNumber: boundTaskRow.taskNumber,
|
|
305
|
+
title: boundTaskRow.title,
|
|
306
|
+
description: boundTaskRow.description ?? null,
|
|
307
|
+
status: boundTaskRow.status,
|
|
308
|
+
claimedByName: boundTaskRow.assigneeName,
|
|
309
|
+
}
|
|
310
|
+
: undefined;
|
|
311
|
+
const participants = normalizedThreadRootId
|
|
312
|
+
? listThreadCollaborationParticipants(db, {
|
|
313
|
+
channelId: params.channelId,
|
|
314
|
+
threadRootId: normalizedThreadRootId,
|
|
315
|
+
})
|
|
316
|
+
: recentParticipants;
|
|
317
|
+
let openTaskCount = 0;
|
|
318
|
+
let openTasks = [];
|
|
319
|
+
if (!boundTask && openTasksMode !== 'off') {
|
|
320
|
+
if (openTasksMode === 'full') {
|
|
321
|
+
openTasks = db.prepare(`SELECT task_number as taskNumber,
|
|
322
|
+
title,
|
|
323
|
+
status,
|
|
324
|
+
claimed_by_name as claimedByName
|
|
325
|
+
FROM tasks
|
|
326
|
+
WHERE channel_id = ? AND status != 'done'
|
|
327
|
+
ORDER BY
|
|
328
|
+
CASE status
|
|
329
|
+
WHEN 'in_progress' THEN 0
|
|
330
|
+
WHEN 'in_review' THEN 1
|
|
331
|
+
WHEN 'todo' THEN 2
|
|
332
|
+
ELSE 3
|
|
333
|
+
END ASC,
|
|
334
|
+
task_number ASC
|
|
335
|
+
LIMIT 5`).all(params.channelId);
|
|
336
|
+
openTaskCount = openTasks.length;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
openTaskCount = db.prepare(`SELECT COUNT(*) as count
|
|
340
|
+
FROM tasks
|
|
341
|
+
WHERE channel_id = ? AND status != 'done'`).get(params.channelId).count;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
let dmActiveTaskThreadCount = 0;
|
|
345
|
+
let dmActiveTaskThreads = [];
|
|
346
|
+
if (normalizedThreadRootId === null && params.replyTarget.startsWith('dm:@') && dmActiveTaskThreadsMode !== 'off') {
|
|
347
|
+
if (dmActiveTaskThreadsMode === 'full') {
|
|
348
|
+
dmActiveTaskThreads = db.prepare(`SELECT t.agent_task_ref as agentTaskRef,
|
|
349
|
+
t.task_number as taskNumber,
|
|
350
|
+
t.title,
|
|
351
|
+
t.status,
|
|
352
|
+
t.claimed_by_name as claimedByName,
|
|
353
|
+
t.message_id as messageId
|
|
354
|
+
FROM tasks t
|
|
355
|
+
LEFT JOIN channel_messages cm ON cm.message_id = t.message_id
|
|
356
|
+
WHERE t.channel_id = ?
|
|
357
|
+
AND COALESCE(t.dm_target, cm.target) = ?
|
|
358
|
+
AND t.message_id IS NOT NULL
|
|
359
|
+
AND t.status IN ('todo', 'in_progress', 'in_review')
|
|
360
|
+
ORDER BY t.updated_at DESC, t.task_number DESC`).all(params.channelId, params.replyTarget).map((task) => ({
|
|
361
|
+
agentTaskRef: task.agentTaskRef,
|
|
362
|
+
taskNumber: task.taskNumber,
|
|
363
|
+
title: task.title,
|
|
364
|
+
status: task.status,
|
|
365
|
+
claimedByName: task.claimedByName,
|
|
366
|
+
threadTarget: `${params.replyTarget}:${buildThreadShortId(task.messageId)}`,
|
|
367
|
+
}));
|
|
368
|
+
dmActiveTaskThreadCount = dmActiveTaskThreads.length;
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
dmActiveTaskThreadCount = db.prepare(`SELECT COUNT(*) as count
|
|
372
|
+
FROM tasks t
|
|
373
|
+
LEFT JOIN channel_messages cm ON cm.message_id = t.message_id
|
|
374
|
+
WHERE t.channel_id = ?
|
|
375
|
+
AND COALESCE(t.dm_target, cm.target) = ?
|
|
376
|
+
AND t.message_id IS NOT NULL
|
|
377
|
+
AND t.status IN ('todo', 'in_progress', 'in_review')`).get(params.channelId, params.replyTarget).count;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
replyTarget: params.replyTarget,
|
|
382
|
+
recentMessages,
|
|
383
|
+
olderUnreadCount: unreadRow.count,
|
|
384
|
+
...(recentWindowStartSeq ? { recentWindowStartSeq } : {}),
|
|
385
|
+
unreadCount: unreadRow.count,
|
|
386
|
+
...(recentWindowStartSeq ? { oldestVisibleSeq: recentWindowStartSeq } : {}),
|
|
387
|
+
participants,
|
|
388
|
+
...(boundTask ? { boundTask } : {}),
|
|
389
|
+
openTaskCount,
|
|
390
|
+
openTasks,
|
|
391
|
+
...(rootMessage ? { rootMessage } : {}),
|
|
392
|
+
...(dmContextSnapshot ? { dmContextSnapshot } : {}),
|
|
393
|
+
dmActiveTaskThreadCount,
|
|
394
|
+
...(dmActiveTaskThreads.length > 0 ? { dmActiveTaskThreads } : {}),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
export function buildDirectSurfaceActivationEnvelope(db, params) {
|
|
398
|
+
const boundDmThreadTask = params.threadRootId && params.replyTarget.startsWith('dm:@')
|
|
399
|
+
? getBoundTaskForThread(db, {
|
|
400
|
+
channelId: params.channelId,
|
|
401
|
+
threadRootId: params.threadRootId,
|
|
402
|
+
})
|
|
403
|
+
: undefined;
|
|
404
|
+
const includeDmSnapshot = params.includeDmContextSnapshot ?? (boundDmThreadTask
|
|
405
|
+
? true
|
|
406
|
+
: shouldIncludeDmContextSnapshot({
|
|
407
|
+
target: params.replyTarget,
|
|
408
|
+
includeRecoverySnapshot: params.includeRecoverySnapshot,
|
|
409
|
+
}));
|
|
410
|
+
const promptIncludeDmSnapshot = params.promptIncludeDmContextSnapshot ?? (params.includeDmContextSnapshot ?? (boundDmThreadTask ? true : includeDmSnapshot));
|
|
411
|
+
const seedPolicy = resolveDirectSurfaceActivationPolicy({
|
|
412
|
+
target: params.replyTarget,
|
|
413
|
+
includeDmActiveTaskThreads: params.includeDmActiveTaskThreads,
|
|
414
|
+
includeDmContextSnapshot: includeDmSnapshot,
|
|
415
|
+
promptIncludeDmContextSnapshot: promptIncludeDmSnapshot,
|
|
416
|
+
strongSignalFlags: {
|
|
417
|
+
...params.strongSignalFlags,
|
|
418
|
+
handoff_recovery_bootstrap: params.includeRecoverySnapshot ?? params.strongSignalFlags?.handoff_recovery_bootstrap,
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
const activationContext = buildTargetActivationContext(db, {
|
|
422
|
+
agentId: params.agentId,
|
|
423
|
+
channelId: params.channelId,
|
|
424
|
+
replyTarget: params.replyTarget,
|
|
425
|
+
triggerSeq: params.triggerSeq,
|
|
426
|
+
threadRootId: params.threadRootId ?? null,
|
|
427
|
+
unreadCheckpointSeq: params.unreadCheckpointSeq,
|
|
428
|
+
constrainRecentMessagesToUnread: params.constrainRecentMessagesToUnread,
|
|
429
|
+
excludeRecentMessageSenderId: params.excludeRecentMessageSenderId,
|
|
430
|
+
openTasksMode: 'off',
|
|
431
|
+
includeDmContextSnapshot: includeDmSnapshot,
|
|
432
|
+
dmActiveTaskThreadsMode: params.dmActiveTaskThreadsMode ?? seedPolicy.dmTaskThreadPresence.activationMode,
|
|
433
|
+
});
|
|
434
|
+
const applyMessageContentOverrides = (message) => {
|
|
435
|
+
if (!message)
|
|
436
|
+
return message;
|
|
437
|
+
const nextContent = params.messageContentOverrides?.[message.messageId];
|
|
438
|
+
if (typeof nextContent !== 'string' || nextContent === message.content) {
|
|
439
|
+
return message;
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
...message,
|
|
443
|
+
content: nextContent,
|
|
444
|
+
};
|
|
445
|
+
};
|
|
446
|
+
const applySnapshotContentOverrides = (snapshot) => {
|
|
447
|
+
if (!snapshot)
|
|
448
|
+
return snapshot;
|
|
449
|
+
const nextMessages = snapshot.messages.map((message) => applyMessageContentOverrides(message) ?? message);
|
|
450
|
+
const changed = nextMessages.some((message, index) => message !== snapshot.messages[index]);
|
|
451
|
+
if (!changed)
|
|
452
|
+
return snapshot;
|
|
453
|
+
return {
|
|
454
|
+
...snapshot,
|
|
455
|
+
messages: nextMessages,
|
|
456
|
+
};
|
|
457
|
+
};
|
|
458
|
+
const normalizedActivationContext = {
|
|
459
|
+
...activationContext,
|
|
460
|
+
recentMessages: activationContext.recentMessages.map((message) => applyMessageContentOverrides(message) ?? message),
|
|
461
|
+
rootMessage: applyMessageContentOverrides(activationContext.rootMessage),
|
|
462
|
+
dmContextSnapshot: applySnapshotContentOverrides(activationContext.dmContextSnapshot),
|
|
463
|
+
};
|
|
464
|
+
const policy = resolveDirectSurfaceActivationPolicy({
|
|
465
|
+
target: params.replyTarget,
|
|
466
|
+
includeDmActiveTaskThreads: params.includeDmActiveTaskThreads,
|
|
467
|
+
includeDmContextSnapshot: includeDmSnapshot,
|
|
468
|
+
promptIncludeDmContextSnapshot: promptIncludeDmSnapshot,
|
|
469
|
+
strongSignalFlags: {
|
|
470
|
+
...params.strongSignalFlags,
|
|
471
|
+
handoff_recovery_bootstrap: params.includeRecoverySnapshot ?? params.strongSignalFlags?.handoff_recovery_bootstrap,
|
|
472
|
+
active_dm_task_thread_linkage: normalizedActivationContext.dmActiveTaskThreadCount > 0,
|
|
473
|
+
bound_task_match: Boolean(normalizedActivationContext.boundTask) || params.strongSignalFlags?.bound_task_match,
|
|
474
|
+
task_thread: Boolean(normalizedActivationContext.boundTask && params.threadRootId) || params.strongSignalFlags?.task_thread,
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
const today = params.today ?? getCurrentISOWeek();
|
|
478
|
+
const memoryHints = buildDefaultDirectMemoryHints({
|
|
479
|
+
threadRootId: params.threadRootId ?? null,
|
|
480
|
+
forceTaskNotes: params.forceTaskNotes,
|
|
481
|
+
forceWorkLog: params.forceWorkLog,
|
|
482
|
+
currentWeek: today.week,
|
|
483
|
+
});
|
|
484
|
+
const promptDmActiveTaskThreadsMode = params.promptDmActiveTaskThreadsMode ?? policy.dmTaskThreadPresence.renderMode;
|
|
485
|
+
const promptIncludeRootMessage = params.promptIncludeRootMessage ?? !Boolean(boundDmThreadTask);
|
|
486
|
+
const promptIncludeBoundTaskBrief = params.promptIncludeBoundTaskBrief ?? true;
|
|
487
|
+
const dmContextSnapshotLimit = boundDmThreadTask ? DEFAULT_DM_TASK_THREAD_CONTEXT_MESSAGE_LIMIT : undefined;
|
|
488
|
+
const shouldRenderFullRecentWindow = params.threadRootId == null && params.replyTarget.startsWith('dm:@');
|
|
489
|
+
const fullRecentWindowLimit = shouldRenderFullRecentWindow ? normalizedActivationContext.recentMessages.length : undefined;
|
|
490
|
+
const surfaceSystemStatusSection = buildSurfaceSystemStatusSection(db, {
|
|
491
|
+
channelId: params.channelId,
|
|
492
|
+
replyTarget: params.replyTarget,
|
|
493
|
+
threadRootId: params.threadRootId ?? null,
|
|
494
|
+
boundTaskId: normalizedActivationContext.boundTask?.taskId,
|
|
495
|
+
});
|
|
496
|
+
const sourceHandoffReminderSection = buildSourceSurfaceHandoffReminderSection(db, {
|
|
497
|
+
agentId: params.agentId,
|
|
498
|
+
channelId: params.channelId,
|
|
499
|
+
replyTarget: params.replyTarget,
|
|
500
|
+
threadRootId: params.threadRootId ?? null,
|
|
501
|
+
});
|
|
502
|
+
const activationContextSections = [
|
|
503
|
+
...(sourceHandoffReminderSection ? [sourceHandoffReminderSection] : []),
|
|
504
|
+
...(surfaceSystemStatusSection ? [surfaceSystemStatusSection] : []),
|
|
505
|
+
...buildDirectActivationContextSections({
|
|
506
|
+
target: params.replyTarget,
|
|
507
|
+
recentMessages: normalizedActivationContext.recentMessages,
|
|
508
|
+
olderUnreadCount: normalizedActivationContext.olderUnreadCount,
|
|
509
|
+
recentWindowStartSeq: normalizedActivationContext.recentWindowStartSeq,
|
|
510
|
+
rootMessage: normalizedActivationContext.rootMessage,
|
|
511
|
+
boundTask: normalizedActivationContext.boundTask,
|
|
512
|
+
dmContextSnapshot: normalizedActivationContext.dmContextSnapshot,
|
|
513
|
+
dmActiveTaskThreads: normalizedActivationContext.dmActiveTaskThreads,
|
|
514
|
+
dmActiveTaskThreadCount: normalizedActivationContext.dmActiveTaskThreadCount,
|
|
515
|
+
}, {
|
|
516
|
+
includeDmContextSnapshot: promptIncludeDmSnapshot,
|
|
517
|
+
historyLimit: params.activationHistoryLimit ?? fullRecentWindowLimit ?? historyLimitForSurfaceActivationTier(policy.activationTier),
|
|
518
|
+
dmActiveTaskThreadsMode: promptDmActiveTaskThreadsMode,
|
|
519
|
+
includeRootMessage: promptIncludeRootMessage,
|
|
520
|
+
includeBoundTaskBrief: promptIncludeBoundTaskBrief,
|
|
521
|
+
...(dmContextSnapshotLimit ? { dmContextSnapshotLimit } : {}),
|
|
522
|
+
}),
|
|
523
|
+
];
|
|
524
|
+
const activationContextText = renderPromptContextSections(activationContextSections) || undefined;
|
|
525
|
+
const resumeContextSections = [
|
|
526
|
+
createPromptContextSection('workspace_memory_reminder', `[Workspace memory reminder]
|
|
527
|
+
${buildWorkspaceMemoryReminder(memoryHints, today)}`),
|
|
528
|
+
sourceHandoffReminderSection,
|
|
529
|
+
surfaceSystemStatusSection,
|
|
530
|
+
...buildDirectActivationContextSections({
|
|
531
|
+
target: params.replyTarget,
|
|
532
|
+
recentMessages: normalizedActivationContext.recentMessages,
|
|
533
|
+
olderUnreadCount: normalizedActivationContext.olderUnreadCount,
|
|
534
|
+
recentWindowStartSeq: normalizedActivationContext.recentWindowStartSeq,
|
|
535
|
+
rootMessage: normalizedActivationContext.rootMessage,
|
|
536
|
+
boundTask: normalizedActivationContext.boundTask,
|
|
537
|
+
dmContextSnapshot: promptIncludeDmSnapshot ? normalizedActivationContext.dmContextSnapshot : undefined,
|
|
538
|
+
dmActiveTaskThreads: normalizedActivationContext.dmActiveTaskThreads,
|
|
539
|
+
dmActiveTaskThreadCount: normalizedActivationContext.dmActiveTaskThreadCount,
|
|
540
|
+
}, {
|
|
541
|
+
includeDmContextSnapshot: promptIncludeDmSnapshot,
|
|
542
|
+
historyLimit: params.resumeHistoryLimit ?? fullRecentWindowLimit ?? historyLimitForSurfaceActivationTier(policy.resumeTier),
|
|
543
|
+
dmActiveTaskThreadsMode: promptDmActiveTaskThreadsMode,
|
|
544
|
+
includeRootMessage: promptIncludeRootMessage,
|
|
545
|
+
includeBoundTaskBrief: promptIncludeBoundTaskBrief,
|
|
546
|
+
...(dmContextSnapshotLimit ? { dmContextSnapshotLimit } : {}),
|
|
547
|
+
}),
|
|
548
|
+
].filter((section) => Boolean(section));
|
|
549
|
+
const resumeContextText = renderPromptContextSections(resumeContextSections) || undefined;
|
|
550
|
+
return {
|
|
551
|
+
today,
|
|
552
|
+
activationContext: normalizedActivationContext,
|
|
553
|
+
activationContextText,
|
|
554
|
+
activationContextSections: activationContextSections.length > 0 ? activationContextSections : undefined,
|
|
555
|
+
resumeContextText,
|
|
556
|
+
resumeContextSections: resumeContextSections.length > 0 ? resumeContextSections : undefined,
|
|
557
|
+
replayOverlapRecentMessages: normalizedActivationContext.recentMessages,
|
|
558
|
+
policy,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
export function buildChannelSurfaceActivationEnvelope(db, params) {
|
|
562
|
+
const seedPolicy = resolveChannelSurfaceActivationPolicy({
|
|
563
|
+
target: params.replyTarget,
|
|
564
|
+
reason: params.reason,
|
|
565
|
+
includeOpenTasks: params.includeOpenTasks,
|
|
566
|
+
strongSignalFlags: params.strongSignalFlags,
|
|
567
|
+
});
|
|
568
|
+
// Thread-reply activations need enough local timeline for collaboration
|
|
569
|
+
// follow-ups; unread-only delivery can otherwise hide already-acked agent dialogue.
|
|
570
|
+
const shouldPreserveThreadRecentHistory = Boolean(params.constrainRecentMessagesToUnread
|
|
571
|
+
&& params.reason === 'thread_reply'
|
|
572
|
+
&& params.threadRootId);
|
|
573
|
+
const activationContext = buildTargetActivationContext(db, {
|
|
574
|
+
agentId: params.agentId,
|
|
575
|
+
channelId: params.channelId,
|
|
576
|
+
replyTarget: params.replyTarget,
|
|
577
|
+
triggerSeq: params.triggerSeq,
|
|
578
|
+
threadRootId: params.threadRootId ?? null,
|
|
579
|
+
recentLimit: params.recentLimit,
|
|
580
|
+
unreadCheckpointSeq: params.unreadCheckpointSeq,
|
|
581
|
+
constrainRecentMessagesToUnread: shouldPreserveThreadRecentHistory
|
|
582
|
+
? false
|
|
583
|
+
: params.constrainRecentMessagesToUnread,
|
|
584
|
+
excludeRecentMessageSenderId: params.excludeRecentMessageSenderId,
|
|
585
|
+
openTasksMode: params.openTasksMode ?? seedPolicy.taskBoardPresence.activationMode,
|
|
586
|
+
});
|
|
587
|
+
const previewPolicy = resolveChannelSurfaceActivationPolicy({
|
|
588
|
+
target: params.replyTarget,
|
|
589
|
+
reason: params.reason,
|
|
590
|
+
hasBoundTask: Boolean(activationContext.boundTask),
|
|
591
|
+
participantsCount: activationContext.participants.length,
|
|
592
|
+
strongSignalFlags: params.strongSignalFlags,
|
|
593
|
+
});
|
|
594
|
+
const includeParticipants = params.includeParticipants ?? previewPolicy.includeParticipantsByDefault;
|
|
595
|
+
const includeOpenTasks = params.includeOpenTasks ?? (previewPolicy.includeOpenTasksByDefault || shouldIncludeOpenTaskBoardSummary({
|
|
596
|
+
target: params.replyTarget,
|
|
597
|
+
hasBoundTask: Boolean(activationContext.boundTask),
|
|
598
|
+
}));
|
|
599
|
+
const policy = resolveChannelSurfaceActivationPolicy({
|
|
600
|
+
target: params.replyTarget,
|
|
601
|
+
reason: params.reason,
|
|
602
|
+
hasBoundTask: Boolean(activationContext.boundTask),
|
|
603
|
+
includeOpenTasks,
|
|
604
|
+
participantsCount: activationContext.participants.length,
|
|
605
|
+
strongSignalFlags: params.strongSignalFlags,
|
|
606
|
+
});
|
|
607
|
+
const today = params.today ?? getCurrentISOWeek();
|
|
608
|
+
const memoryHints = buildDefaultChannelMemoryHints({
|
|
609
|
+
channelName: params.channelName,
|
|
610
|
+
hasBoundTask: Boolean(activationContext.boundTask),
|
|
611
|
+
forceTaskNotes: params.forceTaskNotes,
|
|
612
|
+
forceWorkLog: params.forceWorkLog,
|
|
613
|
+
currentWeek: today.week,
|
|
614
|
+
});
|
|
615
|
+
const openTaskMode = params.openTaskMode ?? policy.taskBoardPresence.renderMode;
|
|
616
|
+
const surfaceSystemStatusSection = buildSurfaceSystemStatusSection(db, {
|
|
617
|
+
channelId: params.channelId,
|
|
618
|
+
replyTarget: params.replyTarget,
|
|
619
|
+
threadRootId: params.threadRootId ?? null,
|
|
620
|
+
boundTaskId: activationContext.boundTask?.taskId,
|
|
621
|
+
});
|
|
622
|
+
const sourceHandoffReminderSection = buildSourceSurfaceHandoffReminderSection(db, {
|
|
623
|
+
agentId: params.agentId,
|
|
624
|
+
channelId: params.channelId,
|
|
625
|
+
replyTarget: params.replyTarget,
|
|
626
|
+
threadRootId: params.threadRootId ?? null,
|
|
627
|
+
});
|
|
628
|
+
const activationContextSections = [
|
|
629
|
+
...(sourceHandoffReminderSection ? [sourceHandoffReminderSection] : []),
|
|
630
|
+
...(surfaceSystemStatusSection ? [surfaceSystemStatusSection] : []),
|
|
631
|
+
...buildChannelActivationContextSections({
|
|
632
|
+
target: params.replyTarget,
|
|
633
|
+
recentMessages: activationContext.recentMessages,
|
|
634
|
+
rootMessage: activationContext.rootMessage,
|
|
635
|
+
olderUnreadCount: activationContext.olderUnreadCount,
|
|
636
|
+
recentWindowStartSeq: activationContext.recentWindowStartSeq,
|
|
637
|
+
participants: activationContext.participants,
|
|
638
|
+
boundTask: activationContext.boundTask,
|
|
639
|
+
openTasks: activationContext.openTasks,
|
|
640
|
+
openTaskCount: activationContext.openTaskCount,
|
|
641
|
+
}, {
|
|
642
|
+
includeParticipants,
|
|
643
|
+
includeOpenTasks,
|
|
644
|
+
maxOpenTasks: 3,
|
|
645
|
+
historyLimit: params.activationHistoryLimit ?? historyLimitForSurfaceActivationTier(policy.activationTier),
|
|
646
|
+
openTaskMode,
|
|
647
|
+
}),
|
|
648
|
+
];
|
|
649
|
+
const activationContextText = renderPromptContextSections(activationContextSections) || undefined;
|
|
650
|
+
const resumeContextSections = [
|
|
651
|
+
createPromptContextSection('workspace_memory_reminder', `[Workspace memory reminder]
|
|
652
|
+
${buildWorkspaceMemoryReminder(memoryHints, today)}`),
|
|
653
|
+
sourceHandoffReminderSection,
|
|
654
|
+
surfaceSystemStatusSection,
|
|
655
|
+
...buildChannelActivationContextSections({
|
|
656
|
+
target: params.replyTarget,
|
|
657
|
+
recentMessages: activationContext.recentMessages,
|
|
658
|
+
rootMessage: activationContext.rootMessage,
|
|
659
|
+
olderUnreadCount: activationContext.olderUnreadCount,
|
|
660
|
+
recentWindowStartSeq: activationContext.recentWindowStartSeq,
|
|
661
|
+
participants: activationContext.participants,
|
|
662
|
+
boundTask: activationContext.boundTask,
|
|
663
|
+
openTasks: activationContext.openTasks,
|
|
664
|
+
openTaskCount: activationContext.openTaskCount,
|
|
665
|
+
}, {
|
|
666
|
+
includeParticipants,
|
|
667
|
+
includeOpenTasks,
|
|
668
|
+
maxOpenTasks: 3,
|
|
669
|
+
historyLimit: params.resumeHistoryLimit ?? historyLimitForSurfaceActivationTier(policy.resumeTier),
|
|
670
|
+
openTaskMode,
|
|
671
|
+
}),
|
|
672
|
+
].filter((section) => Boolean(section));
|
|
673
|
+
const resumeContextText = renderPromptContextSections(resumeContextSections) || undefined;
|
|
674
|
+
return {
|
|
675
|
+
today,
|
|
676
|
+
activationContext,
|
|
677
|
+
activationContextText,
|
|
678
|
+
activationContextSections: activationContextSections.length > 0 ? activationContextSections : undefined,
|
|
679
|
+
resumeContextText,
|
|
680
|
+
resumeContextSections: resumeContextSections.length > 0 ? resumeContextSections : undefined,
|
|
681
|
+
replayOverlapRecentMessages: activationContext.recentMessages,
|
|
682
|
+
policy,
|
|
683
|
+
};
|
|
684
|
+
}
|