@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,17 @@
|
|
|
1
|
+
export function deriveSurfaceKind(threadKind, threadRootId) {
|
|
2
|
+
if (threadKind === 'direct') {
|
|
3
|
+
return threadRootId ? 'dm_thread' : 'dm';
|
|
4
|
+
}
|
|
5
|
+
return threadRootId ? 'channel_thread' : 'channel';
|
|
6
|
+
}
|
|
7
|
+
export function parseSurfaceKind(value, fallback) {
|
|
8
|
+
switch (value) {
|
|
9
|
+
case 'dm':
|
|
10
|
+
case 'dm_thread':
|
|
11
|
+
case 'channel':
|
|
12
|
+
case 'channel_thread':
|
|
13
|
+
return value;
|
|
14
|
+
default:
|
|
15
|
+
return deriveSurfaceKind(fallback.threadKind, fallback.threadRootId);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { buildThreadShortId } from '@bbigbang/protocol';
|
|
2
|
+
import { parseDirectReplyTarget, resolveConversationReplyTarget } from './directReplyTargets.js';
|
|
3
|
+
import { findThreadRootMessageId, resolveThreadRootLookup } from './threadRoots.js';
|
|
4
|
+
export function resolveThreadRootId(target) {
|
|
5
|
+
const directParts = parseDirectReplyTarget(target);
|
|
6
|
+
if (directParts)
|
|
7
|
+
return directParts.threadRootId;
|
|
8
|
+
const match = target.match(/^#[^:]+:([a-zA-Z0-9-]+)$/);
|
|
9
|
+
return match?.[1] ?? null;
|
|
10
|
+
}
|
|
11
|
+
export function resolveChannelFromTarget(target, db) {
|
|
12
|
+
const channelMatch = target.match(/^#([^:]+)/);
|
|
13
|
+
if (channelMatch) {
|
|
14
|
+
const name = channelMatch[1];
|
|
15
|
+
const row = db
|
|
16
|
+
.prepare('SELECT channel_id as channelId FROM channels WHERE name = ?')
|
|
17
|
+
.get(name);
|
|
18
|
+
return row?.channelId ?? null;
|
|
19
|
+
}
|
|
20
|
+
if (target.startsWith('dm:')) {
|
|
21
|
+
const directParts = parseDirectReplyTarget(target);
|
|
22
|
+
if (directParts) {
|
|
23
|
+
const agentName = directParts.userName;
|
|
24
|
+
const agentRow = db
|
|
25
|
+
.prepare('SELECT agent_id as agentId FROM agents WHERE name = ? AND deleted_at IS NULL ORDER BY updated_at DESC LIMIT 1')
|
|
26
|
+
.get(agentName);
|
|
27
|
+
if (agentRow)
|
|
28
|
+
return `dm:${agentRow.agentId}`;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
export function resolveTaskChannelId(agentId, channel, db) {
|
|
35
|
+
return resolveChannelFromTarget(channel, db) ?? (channel.startsWith('dm:') ? `dm:${agentId}` : null);
|
|
36
|
+
}
|
|
37
|
+
export function resolveDefaultReplyTarget(db, conversationId, humanUserName) {
|
|
38
|
+
return resolveConversationReplyTarget(db, conversationId, humanUserName);
|
|
39
|
+
}
|
|
40
|
+
export function isKnownDmTargetPeer(db, target) {
|
|
41
|
+
const directParts = parseDirectReplyTarget(target);
|
|
42
|
+
if (!directParts)
|
|
43
|
+
return false;
|
|
44
|
+
const name = directParts.userName;
|
|
45
|
+
const userRow = db.prepare(`SELECT 1 FROM users WHERE username = ? LIMIT 1`).get(name);
|
|
46
|
+
if (userRow)
|
|
47
|
+
return true;
|
|
48
|
+
const agentRow = db.prepare(`SELECT 1 FROM agents WHERE name = ? AND deleted_at IS NULL LIMIT 1`).get(name);
|
|
49
|
+
return Boolean(agentRow);
|
|
50
|
+
}
|
|
51
|
+
export function findConversationIdForReplyTarget(db, agentId, replyTarget) {
|
|
52
|
+
const row = db.prepare(`SELECT id
|
|
53
|
+
FROM conversations
|
|
54
|
+
WHERE agent_id = ? AND reply_target = ?
|
|
55
|
+
ORDER BY updated_at DESC
|
|
56
|
+
LIMIT 1`).get(agentId, replyTarget);
|
|
57
|
+
return row?.id ?? null;
|
|
58
|
+
}
|
|
59
|
+
export function findConversationIdForReadTarget(db, agentId, replyTarget) {
|
|
60
|
+
const existingId = findConversationIdForReplyTarget(db, agentId, replyTarget);
|
|
61
|
+
if (existingId)
|
|
62
|
+
return existingId;
|
|
63
|
+
const threadRootId = resolveThreadRootId(replyTarget);
|
|
64
|
+
if (replyTarget.startsWith('#')) {
|
|
65
|
+
const channelId = resolveChannelFromTarget(replyTarget, db);
|
|
66
|
+
if (!channelId)
|
|
67
|
+
return null;
|
|
68
|
+
if (threadRootId) {
|
|
69
|
+
const lookup = resolveThreadRootLookup(db, channelId, threadRootId);
|
|
70
|
+
const canonicalThreadRootId = lookup?.canonicalThreadRootId ?? buildThreadShortId(threadRootId);
|
|
71
|
+
const row = db.prepare(`SELECT id
|
|
72
|
+
FROM conversations
|
|
73
|
+
WHERE agent_id = ?
|
|
74
|
+
AND channel_id = ?
|
|
75
|
+
AND thread_kind = 'branch'
|
|
76
|
+
AND thread_root_id = ?
|
|
77
|
+
ORDER BY updated_at DESC
|
|
78
|
+
LIMIT 1`).get(agentId, channelId, canonicalThreadRootId);
|
|
79
|
+
return row?.id ?? null;
|
|
80
|
+
}
|
|
81
|
+
const row = db.prepare(`SELECT id
|
|
82
|
+
FROM conversations
|
|
83
|
+
WHERE agent_id = ?
|
|
84
|
+
AND channel_id = ?
|
|
85
|
+
AND thread_kind = 'branch'
|
|
86
|
+
AND thread_root_id IS NULL
|
|
87
|
+
ORDER BY updated_at DESC
|
|
88
|
+
LIMIT 1`).get(agentId, channelId);
|
|
89
|
+
return row?.id ?? null;
|
|
90
|
+
}
|
|
91
|
+
if (replyTarget.startsWith('dm:@') && threadRootId) {
|
|
92
|
+
const canonicalThreadRootId = buildThreadShortId(threadRootId);
|
|
93
|
+
const row = db.prepare(`SELECT id
|
|
94
|
+
FROM conversations
|
|
95
|
+
WHERE agent_id = ?
|
|
96
|
+
AND channel_id = ?
|
|
97
|
+
AND thread_kind = 'direct'
|
|
98
|
+
AND is_primary_thread = 0
|
|
99
|
+
AND thread_root_id = ?
|
|
100
|
+
ORDER BY updated_at DESC
|
|
101
|
+
LIMIT 1`).get(agentId, `dm:${agentId}`, canonicalThreadRootId);
|
|
102
|
+
return row?.id ?? null;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function doesThreadRootExist(db, channelId, threadRootId) {
|
|
107
|
+
return Boolean(findThreadRootMessageId(db, channelId, threadRootId));
|
|
108
|
+
}
|
|
109
|
+
export function ensureConversationIdForReplyTarget(db, conversationManager, agentId, replyTarget) {
|
|
110
|
+
const existingId = findConversationIdForReplyTarget(db, agentId, replyTarget);
|
|
111
|
+
if (existingId)
|
|
112
|
+
return existingId;
|
|
113
|
+
const threadRootId = resolveThreadRootId(replyTarget);
|
|
114
|
+
const dmMatch = replyTarget.match(/^dm:@([^:]+)(?::([a-zA-Z0-9-]+))?$/);
|
|
115
|
+
if (dmMatch) {
|
|
116
|
+
const userName = dmMatch[1];
|
|
117
|
+
const userRow = db.prepare(`SELECT id FROM users WHERE username = ? LIMIT 1`).get(userName);
|
|
118
|
+
if (!userRow)
|
|
119
|
+
return null;
|
|
120
|
+
if (threadRootId) {
|
|
121
|
+
if (!doesThreadRootExist(db, `dm:${agentId}`, threadRootId))
|
|
122
|
+
return null;
|
|
123
|
+
return conversationManager.openAgentDirectThread(agentId, userRow.id, threadRootId)?.id ?? null;
|
|
124
|
+
}
|
|
125
|
+
return conversationManager.openAgentThread(agentId, userRow.id)?.id ?? null;
|
|
126
|
+
}
|
|
127
|
+
if (threadRootId) {
|
|
128
|
+
const channelId = resolveChannelFromTarget(replyTarget, db);
|
|
129
|
+
if (!channelId)
|
|
130
|
+
return null;
|
|
131
|
+
if (!doesThreadRootExist(db, channelId, threadRootId))
|
|
132
|
+
return null;
|
|
133
|
+
return conversationManager.openAgentChannelThread(agentId, channelId, threadRootId)?.id ?? null;
|
|
134
|
+
}
|
|
135
|
+
if (replyTarget.startsWith('#')) {
|
|
136
|
+
const channelId = resolveChannelFromTarget(replyTarget, db);
|
|
137
|
+
if (!channelId)
|
|
138
|
+
return null;
|
|
139
|
+
return conversationManager.openAgentChannelThread(agentId, channelId, null)?.id ?? null;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
export function normalizeTargetForConversation(db, conversationId, target) {
|
|
144
|
+
const row = db.prepare(`SELECT c.channel_id as channelId, c.thread_kind as threadKind, c.thread_root_id as threadRootId,
|
|
145
|
+
ch.name as channelName
|
|
146
|
+
FROM conversations c
|
|
147
|
+
LEFT JOIN channels ch ON ch.channel_id = c.channel_id
|
|
148
|
+
WHERE c.id = ?`).get(conversationId);
|
|
149
|
+
if (!row || row.threadKind !== 'branch')
|
|
150
|
+
return target;
|
|
151
|
+
const channelName = row.channelName ?? row.channelId;
|
|
152
|
+
if (row.threadRootId) {
|
|
153
|
+
const canonicalThreadTarget = `#${channelName}:${row.threadRootId}`;
|
|
154
|
+
const sameChannelThread = target.match(/^#([^:]+):([a-zA-Z0-9-]+)$/);
|
|
155
|
+
if (sameChannelThread) {
|
|
156
|
+
const [, targetChannel, targetThreadRootId] = sameChannelThread;
|
|
157
|
+
if ((targetChannel === channelName || targetChannel === row.channelId)
|
|
158
|
+
&& (targetThreadRootId === row.threadRootId || buildThreadShortId(targetThreadRootId) === row.threadRootId)) {
|
|
159
|
+
return canonicalThreadTarget;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return target;
|
|
163
|
+
}
|
|
164
|
+
const canonicalBaseTarget = `#${channelName}`;
|
|
165
|
+
const sameChannelThread = target.match(/^#([^:]+):([a-zA-Z0-9-]+)$/);
|
|
166
|
+
if (sameChannelThread) {
|
|
167
|
+
const [, targetChannel] = sameChannelThread;
|
|
168
|
+
if (targetChannel === channelName || targetChannel === row.channelId) {
|
|
169
|
+
return canonicalBaseTarget;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return target;
|
|
173
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { buildAttachmentReferenceContextText, buildExactTargetHistoryContextSections, } from './channelActivationPrompt.js';
|
|
2
|
+
import { sanitizePromptHistoryContent } from './promptHistorySanitizer.js';
|
|
3
|
+
import { historyLimitForSurfaceActivationTier, resolveDirectSurfaceActivationPolicy, } from './surfaceActivationPolicy.js';
|
|
4
|
+
import { buildWorkspaceMemoryHintSection, getCurrentISOWeek, resolveDirectTaskThreadPresencePolicy, } from './workspaceMemoryHints.js';
|
|
5
|
+
import { createPromptContextSection, renderPromptContextSections, } from './promptContextSections.js';
|
|
6
|
+
export function buildDirectActivationPrompt(params) {
|
|
7
|
+
const replyTarget = params.replyTarget ?? `dm:@${params.senderName}`;
|
|
8
|
+
const today = params.today ?? getCurrentISOWeek();
|
|
9
|
+
const memoryHintSection = buildWorkspaceMemoryHintSection(params.memoryHints, today);
|
|
10
|
+
const lines = [
|
|
11
|
+
`[System: ${params.senderName} sent you a direct message.]`,
|
|
12
|
+
'',
|
|
13
|
+
'[Current conversation target]',
|
|
14
|
+
`reply_target: ${replyTarget}`,
|
|
15
|
+
'',
|
|
16
|
+
...(memoryHintSection ? [memoryHintSection, ''] : []),
|
|
17
|
+
];
|
|
18
|
+
lines.push('[Triggered message metadata]', `recipient: @${params.agentName}`, `sender: @${params.senderName}`, '', '[Triggered message body]', params.content);
|
|
19
|
+
return lines.join('\n');
|
|
20
|
+
}
|
|
21
|
+
export function buildDirectActivationContextText(params, options) {
|
|
22
|
+
return renderPromptContextSections(buildDirectActivationContextSections(params, options));
|
|
23
|
+
}
|
|
24
|
+
export function buildDirectActivationContextSections(params, options) {
|
|
25
|
+
const sections = [];
|
|
26
|
+
const defaultPolicy = resolveDirectSurfaceActivationPolicy({
|
|
27
|
+
target: params.target,
|
|
28
|
+
});
|
|
29
|
+
const historyLimit = Math.max(0, options?.historyLimit ?? historyLimitForSurfaceActivationTier(defaultPolicy.activationTier));
|
|
30
|
+
if (params.rootMessage && options?.includeRootMessage !== false) {
|
|
31
|
+
const triggerTag = params.dmContextSnapshot?.triggerMessageId === params.rootMessage.messageId ? ' [Trigger]' : '';
|
|
32
|
+
const rootContent = sanitizePromptHistoryContent(params.rootMessage.content, params.rootMessage.senderType);
|
|
33
|
+
if (rootContent) {
|
|
34
|
+
const rootLines = [
|
|
35
|
+
'[Thread root message]',
|
|
36
|
+
`@${params.rootMessage.senderName}${triggerTag}: ${rootContent}`,
|
|
37
|
+
];
|
|
38
|
+
const attachmentContext = buildAttachmentReferenceContextText(params.rootMessage.attachmentIds);
|
|
39
|
+
if (attachmentContext) {
|
|
40
|
+
rootLines.push('', attachmentContext);
|
|
41
|
+
}
|
|
42
|
+
const rootSection = createPromptContextSection('thread_root_message', rootLines.join('\n'));
|
|
43
|
+
if (rootSection) {
|
|
44
|
+
sections.push(rootSection);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (params.boundTask) {
|
|
49
|
+
const assignee = params.boundTask.claimedByName ? ` @${params.boundTask.claimedByName}` : ' unassigned';
|
|
50
|
+
const brief = options?.includeBoundTaskBrief !== false && params.boundTask.description?.trim()
|
|
51
|
+
? `\nTask brief / goal / done criteria:\n${params.boundTask.description.trim()}`
|
|
52
|
+
: '';
|
|
53
|
+
const boundTaskSection = createPromptContextSection('bound_task_dm_thread', `[Bound task for this DM thread]
|
|
54
|
+
#${params.boundTask.taskNumber} [${params.boundTask.status}]${assignee} — ${params.boundTask.title}${brief}
|
|
55
|
+
This DM task-thread is already the work surface for that task. Do not run bigbang task claim or bigbang task create again for the same work here. Do not create or claim a different task from this thread; hand off to the root DM first if new tracked work is needed. If you already own this task, continue in this thread, send one substantive final result when it is ready, then update the task status. After the task moves to in_review, stop instead of continuing to chat here.`);
|
|
56
|
+
if (boundTaskSection) {
|
|
57
|
+
sections.push(boundTaskSection);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
sections.push(...buildExactTargetHistoryContextSections(params, { maxMessages: historyLimit }));
|
|
61
|
+
if (options?.includeDmContextSnapshot === true && params.dmContextSnapshot?.messages.length) {
|
|
62
|
+
const dmContextSnapshotLimit = Math.max(0, options?.dmContextSnapshotLimit ?? Number.POSITIVE_INFINITY);
|
|
63
|
+
const dmContextWindow = params.dmContextSnapshot.messages
|
|
64
|
+
.slice(dmContextSnapshotLimit === Number.POSITIVE_INFINITY ? 0 : -dmContextSnapshotLimit);
|
|
65
|
+
const visibleDmMessages = dmContextWindow
|
|
66
|
+
.map((message) => {
|
|
67
|
+
const content = sanitizePromptHistoryContent(message.content, message.senderType);
|
|
68
|
+
return content
|
|
69
|
+
? { ...message, content }
|
|
70
|
+
: null;
|
|
71
|
+
})
|
|
72
|
+
.filter((message) => Boolean(message));
|
|
73
|
+
if (visibleDmMessages.length) {
|
|
74
|
+
const formattedMessages = visibleDmMessages.map((message) => {
|
|
75
|
+
const triggerTag = params.dmContextSnapshot?.triggerMessageId === message.messageId ? ' [Trigger]' : '';
|
|
76
|
+
const lines = [`@${message.senderName}${triggerTag}: ${message.content}`];
|
|
77
|
+
const attachmentContext = buildAttachmentReferenceContextText(message.attachmentIds);
|
|
78
|
+
if (attachmentContext) {
|
|
79
|
+
lines.push(attachmentContext);
|
|
80
|
+
}
|
|
81
|
+
return lines.join('\n');
|
|
82
|
+
});
|
|
83
|
+
const dmSnapshotSection = createPromptContextSection('dm_context_snapshot', [
|
|
84
|
+
'[Context from DM]',
|
|
85
|
+
formattedMessages.join('\n\n'),
|
|
86
|
+
].join('\n'));
|
|
87
|
+
if (dmSnapshotSection) {
|
|
88
|
+
sections.push(dmSnapshotSection);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const dmActiveTaskThreadsMode = options?.dmActiveTaskThreadsMode
|
|
93
|
+
?? resolveDirectTaskThreadPresencePolicy({
|
|
94
|
+
target: params.target,
|
|
95
|
+
includeDmActiveTaskThreads: options?.includeDmActiveTaskThreads,
|
|
96
|
+
}).renderMode;
|
|
97
|
+
const dmActiveTaskThreadCount = Math.max(0, params.dmActiveTaskThreadCount ?? params.dmActiveTaskThreads?.length ?? 0);
|
|
98
|
+
if (dmActiveTaskThreadsMode === 'full' && params.dmActiveTaskThreads?.length) {
|
|
99
|
+
const dmThreadsSection = createPromptContextSection('dm_active_task_threads', [
|
|
100
|
+
'[Active DM task threads]',
|
|
101
|
+
...params.dmActiveTaskThreads.map((task) => {
|
|
102
|
+
const assignee = task.claimedByName ? ` @${task.claimedByName}` : '';
|
|
103
|
+
const identity = task.agentTaskRef ? `${task.agentTaskRef} · #${task.taskNumber}` : `#${task.taskNumber}`;
|
|
104
|
+
return `${identity} [${task.status}]${assignee} -> ${task.threadTarget} — ${task.title}`;
|
|
105
|
+
}),
|
|
106
|
+
].join('\n'));
|
|
107
|
+
if (dmThreadsSection) {
|
|
108
|
+
sections.push(dmThreadsSection);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (dmActiveTaskThreadsMode === 'hint' && dmActiveTaskThreadCount > 0) {
|
|
112
|
+
const dmThreadHintSection = createPromptContextSection('dm_active_task_thread_hint', [
|
|
113
|
+
'[Active DM task-thread hint]',
|
|
114
|
+
`active_task_threads_present: ${dmActiveTaskThreadCount}`,
|
|
115
|
+
'Use bigbang task my for existing task work or bigbang conversation list for other surfaces before creating new work if this looks related.',
|
|
116
|
+
].join('\n'));
|
|
117
|
+
if (dmThreadHintSection) {
|
|
118
|
+
sections.push(dmThreadHintSection);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return sections;
|
|
122
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { normalizeThreadShortIdInput } from '@bbigbang/protocol';
|
|
2
|
+
const DIRECT_REPLY_TARGET_RE = /^dm:@([^\[:]+)(?:\[([^\]]+)\])?(?::([a-zA-Z0-9-]+))?$/;
|
|
3
|
+
export function resolveDirectUserName(db, userId, fallbackHumanUserName) {
|
|
4
|
+
if (!userId)
|
|
5
|
+
return fallbackHumanUserName;
|
|
6
|
+
const row = db.prepare('SELECT username FROM users WHERE id = ?').get(userId);
|
|
7
|
+
return row?.username?.trim() || fallbackHumanUserName;
|
|
8
|
+
}
|
|
9
|
+
export function buildDirectReplyTarget(params) {
|
|
10
|
+
const normalizedThreadRootId = normalizeThreadShortIdInput(params.threadRootId);
|
|
11
|
+
const baseTarget = `dm:@${params.userName}${params.primaryScope ? `[${params.primaryScope}]` : ''}`;
|
|
12
|
+
return params.isPrimaryThread
|
|
13
|
+
? baseTarget
|
|
14
|
+
: normalizedThreadRootId
|
|
15
|
+
? `${baseTarget}:${normalizedThreadRootId}`
|
|
16
|
+
: baseTarget;
|
|
17
|
+
}
|
|
18
|
+
export function parseDirectReplyTarget(target) {
|
|
19
|
+
const match = DIRECT_REPLY_TARGET_RE.exec(target.trim());
|
|
20
|
+
if (!match)
|
|
21
|
+
return null;
|
|
22
|
+
return {
|
|
23
|
+
userName: match[1],
|
|
24
|
+
scope: match[2] ?? null,
|
|
25
|
+
threadRootId: match[3] ?? null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function resolveDirectUserIdFromReplyTarget(db, target) {
|
|
29
|
+
const parsed = typeof target === 'string' ? parseDirectReplyTarget(target) : null;
|
|
30
|
+
const userName = parsed?.userName?.trim() ?? '';
|
|
31
|
+
if (!userName)
|
|
32
|
+
return null;
|
|
33
|
+
const row = db.prepare(`SELECT id
|
|
34
|
+
FROM users
|
|
35
|
+
WHERE username = ?
|
|
36
|
+
LIMIT 1`).get(userName);
|
|
37
|
+
return row?.id?.trim() || null;
|
|
38
|
+
}
|
|
39
|
+
export function resolveConversationReplyTarget(db, conversationId, fallbackHumanUserName) {
|
|
40
|
+
const row = db.prepare(`SELECT c.id as conversationId,
|
|
41
|
+
c.reply_target as replyTarget,
|
|
42
|
+
c.thread_kind as threadKind,
|
|
43
|
+
c.is_primary_thread as isPrimaryThread,
|
|
44
|
+
c.thread_root_id as threadRootId,
|
|
45
|
+
c.channel_id as channelId,
|
|
46
|
+
c.user_id as userId,
|
|
47
|
+
ch.name as channelName
|
|
48
|
+
FROM conversations c
|
|
49
|
+
LEFT JOIN channels ch ON ch.channel_id = c.channel_id
|
|
50
|
+
WHERE c.id = ?`).get(conversationId);
|
|
51
|
+
if (!row)
|
|
52
|
+
return null;
|
|
53
|
+
if (row.threadKind === 'direct') {
|
|
54
|
+
const parsedStoredTarget = row.replyTarget?.trim() ? parseDirectReplyTarget(row.replyTarget) : null;
|
|
55
|
+
if (parsedStoredTarget?.scope)
|
|
56
|
+
return row.replyTarget.trim();
|
|
57
|
+
const userName = resolveDirectUserName(db, row.userId, fallbackHumanUserName);
|
|
58
|
+
return buildDirectReplyTarget({
|
|
59
|
+
isPrimaryThread: row.isPrimaryThread !== 0,
|
|
60
|
+
userName,
|
|
61
|
+
threadRootId: row.threadRootId,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (row.replyTarget?.trim())
|
|
65
|
+
return row.replyTarget;
|
|
66
|
+
const channelName = row.channelName ?? row.channelId;
|
|
67
|
+
const baseTarget = `#${channelName}`;
|
|
68
|
+
return row.threadRootId ? `${baseTarget}:${row.threadRootId}` : baseTarget;
|
|
69
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { buildThreadShortId, normalizeThreadShortIdInput, } from '@bbigbang/protocol';
|
|
2
|
+
import { findThreadRootMessageId } from './threadRoots.js';
|
|
3
|
+
function hasDirectThreadStateForTarget(db, params) {
|
|
4
|
+
const directThreadTargetGlob = `${params.directTarget}:*`;
|
|
5
|
+
const threadMessageRow = db.prepare(`SELECT 1
|
|
6
|
+
FROM channel_messages
|
|
7
|
+
WHERE channel_id = ?
|
|
8
|
+
AND target GLOB ?
|
|
9
|
+
AND thread_root_id IN (?, ?)
|
|
10
|
+
LIMIT 1`).get(params.channelId, directThreadTargetGlob, params.threadRootId, params.rootMessageId);
|
|
11
|
+
if (threadMessageRow)
|
|
12
|
+
return true;
|
|
13
|
+
const threadConversationRow = db.prepare(`SELECT 1
|
|
14
|
+
FROM conversations
|
|
15
|
+
WHERE agent_id = ?
|
|
16
|
+
AND thread_kind = 'direct'
|
|
17
|
+
AND is_primary_thread = 0
|
|
18
|
+
AND reply_target GLOB ?
|
|
19
|
+
AND thread_root_id IN (?, ?)
|
|
20
|
+
LIMIT 1`).get(params.agentId, directThreadTargetGlob, params.threadRootId, params.rootMessageId);
|
|
21
|
+
return !!threadConversationRow;
|
|
22
|
+
}
|
|
23
|
+
function hasAnyDirectThreadState(db, params) {
|
|
24
|
+
const threadMessageRow = db.prepare(`SELECT 1
|
|
25
|
+
FROM channel_messages
|
|
26
|
+
WHERE channel_id = ?
|
|
27
|
+
AND target GLOB 'dm:@*:*'
|
|
28
|
+
AND thread_root_id IN (?, ?)
|
|
29
|
+
LIMIT 1`).get(params.channelId, params.threadRootId, params.rootMessageId);
|
|
30
|
+
if (threadMessageRow)
|
|
31
|
+
return true;
|
|
32
|
+
const threadConversationRow = db.prepare(`SELECT 1
|
|
33
|
+
FROM conversations
|
|
34
|
+
WHERE agent_id = ?
|
|
35
|
+
AND thread_kind = 'direct'
|
|
36
|
+
AND is_primary_thread = 0
|
|
37
|
+
AND thread_root_id IN (?, ?)
|
|
38
|
+
LIMIT 1`).get(params.agentId, params.threadRootId, params.rootMessageId);
|
|
39
|
+
return !!threadConversationRow;
|
|
40
|
+
}
|
|
41
|
+
export function resolveDirectThreadRootMessage(db, params) {
|
|
42
|
+
const dmChannelId = `dm:${params.agentId}`;
|
|
43
|
+
let resolvedThreadRootId = normalizeThreadShortIdInput(params.requestedThreadRootId);
|
|
44
|
+
if (!resolvedThreadRootId && params.requestedMessageId) {
|
|
45
|
+
const requestedMessageId = params.requestedMessageId.trim();
|
|
46
|
+
const exactMessage = db.prepare(`SELECT message_id as messageId, thread_root_id as threadRootId, message_kind as messageKind
|
|
47
|
+
FROM channel_messages
|
|
48
|
+
WHERE channel_id = ?
|
|
49
|
+
AND message_id LIKE ?
|
|
50
|
+
ORDER BY CASE WHEN message_id = ? THEN 0 ELSE 1 END, seq ASC
|
|
51
|
+
LIMIT 1`).get(dmChannelId, `${requestedMessageId}%`, requestedMessageId);
|
|
52
|
+
if (exactMessage?.threadRootId) {
|
|
53
|
+
resolvedThreadRootId = normalizeThreadShortIdInput(exactMessage.threadRootId);
|
|
54
|
+
}
|
|
55
|
+
else if (exactMessage?.messageKind === 'task') {
|
|
56
|
+
resolvedThreadRootId = buildThreadShortId(exactMessage.messageId);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const snapshotRow = db.prepare(`SELECT thread_root_id as threadRootId
|
|
60
|
+
FROM dm_thread_context_snapshots
|
|
61
|
+
WHERE channel_id = ?
|
|
62
|
+
AND trigger_message_id LIKE ?
|
|
63
|
+
ORDER BY created_at DESC
|
|
64
|
+
LIMIT 1`).get(dmChannelId, `${requestedMessageId}%`);
|
|
65
|
+
if (snapshotRow?.threadRootId) {
|
|
66
|
+
resolvedThreadRootId = normalizeThreadShortIdInput(snapshotRow.threadRootId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!resolvedThreadRootId) {
|
|
70
|
+
const taskRow = db.prepare(`SELECT message_id as messageId
|
|
71
|
+
FROM tasks
|
|
72
|
+
WHERE channel_id = ?
|
|
73
|
+
AND message_id LIKE ?
|
|
74
|
+
ORDER BY CASE WHEN message_id = ? THEN 0 ELSE 1 END, created_at ASC
|
|
75
|
+
LIMIT 1`).get(dmChannelId, `${requestedMessageId}%`, requestedMessageId);
|
|
76
|
+
if (taskRow?.messageId) {
|
|
77
|
+
resolvedThreadRootId = buildThreadShortId(taskRow.messageId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!resolvedThreadRootId)
|
|
82
|
+
return null;
|
|
83
|
+
const rootMessageId = findThreadRootMessageId(db, dmChannelId, resolvedThreadRootId);
|
|
84
|
+
if (!rootMessageId)
|
|
85
|
+
return null;
|
|
86
|
+
const taskRow = db.prepare(`SELECT dm_target as dmTarget
|
|
87
|
+
FROM tasks
|
|
88
|
+
WHERE channel_id = ?
|
|
89
|
+
AND message_id = ?
|
|
90
|
+
LIMIT 1`).get(dmChannelId, rootMessageId);
|
|
91
|
+
if (taskRow?.dmTarget === params.directTarget) {
|
|
92
|
+
return {
|
|
93
|
+
messageId: rootMessageId,
|
|
94
|
+
threadRootId: buildThreadShortId(rootMessageId),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const rootRow = db.prepare(`SELECT target, message_kind as messageKind
|
|
98
|
+
FROM channel_messages
|
|
99
|
+
WHERE channel_id = ?
|
|
100
|
+
AND thread_root_id IS NULL
|
|
101
|
+
AND message_id = ?
|
|
102
|
+
LIMIT 1`).get(dmChannelId, rootMessageId);
|
|
103
|
+
if (!rootRow)
|
|
104
|
+
return null;
|
|
105
|
+
if (rootRow.target !== params.directTarget) {
|
|
106
|
+
const isLegacyBrokenTaskRoot = rootRow.target === `#${dmChannelId}` && !!taskRow;
|
|
107
|
+
if (!(rootRow.target === `#${dmChannelId}` && (rootRow.messageKind === 'task' || isLegacyBrokenTaskRoot))) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
if (!hasDirectThreadStateForTarget(db, {
|
|
111
|
+
agentId: params.agentId,
|
|
112
|
+
channelId: dmChannelId,
|
|
113
|
+
rootMessageId,
|
|
114
|
+
threadRootId: resolvedThreadRootId,
|
|
115
|
+
directTarget: params.directTarget,
|
|
116
|
+
})
|
|
117
|
+
&& hasAnyDirectThreadState(db, {
|
|
118
|
+
agentId: params.agentId,
|
|
119
|
+
channelId: dmChannelId,
|
|
120
|
+
rootMessageId,
|
|
121
|
+
threadRootId: resolvedThreadRootId,
|
|
122
|
+
}))
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
messageId: rootMessageId,
|
|
127
|
+
threadRootId: buildThreadShortId(rootMessageId),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
function buildReminderCompletionInstruction(params) {
|
|
2
|
+
const target = params.occurrenceId
|
|
3
|
+
? `--occurrence-id ${params.occurrenceId}`
|
|
4
|
+
: `--reminder-id ${params.reminderId}`;
|
|
5
|
+
if (params.scheduleKind === 'one_time') {
|
|
6
|
+
return `- When the work is truly ready for user review, explicitly run \`bigbang reminder submit-for-review ${target}\`.`;
|
|
7
|
+
}
|
|
8
|
+
return `- When the work is truly done, explicitly run \`bigbang reminder complete ${target}\`.`;
|
|
9
|
+
}
|
|
10
|
+
function buildAttachmentPromptSection(attachmentIds) {
|
|
11
|
+
if (!attachmentIds?.length)
|
|
12
|
+
return [];
|
|
13
|
+
return [
|
|
14
|
+
'',
|
|
15
|
+
`[Task attachment${attachmentIds.length > 1 ? 's' : ''}]`,
|
|
16
|
+
...attachmentIds.map((attachmentId) => `attachment_id: ${attachmentId}`),
|
|
17
|
+
`Use the provided local path when available, or bigbang attachment view --attachment-id "<one of the IDs above>" to inspect the attached file${attachmentIds.length > 1 ? 's' : ''} before acting on this task.`,
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
export function buildDmTaskHandoffPrompt(params) {
|
|
21
|
+
const lines = [
|
|
22
|
+
'[DM Task Thread Handoff]',
|
|
23
|
+
'A task was just created or claimed from the main DM. Execution now continues in this task thread as the expected next phase.',
|
|
24
|
+
'',
|
|
25
|
+
'[Current conversation target]',
|
|
26
|
+
`reply_target: ${params.threadTarget}`,
|
|
27
|
+
`Task: #${params.taskNumber} ${params.title}`,
|
|
28
|
+
`Task root message id: ${params.rootMessageId}`,
|
|
29
|
+
];
|
|
30
|
+
lines.push(...buildAttachmentPromptSection(params.attachmentIds));
|
|
31
|
+
lines.push('', 'Rules:', '- This task thread is now the primary work surface for the task.', '- Put substantive progress updates, tool results, and the final answer in this thread.', '- Do not send any manual follow-up in the main DM after this handoff.', '- The platform will mirror task lifecycle status in the main DM separately.', '- Start working on the task now.');
|
|
32
|
+
return lines.join('\n');
|
|
33
|
+
}
|
|
34
|
+
export function buildReminderTaskHandoffPrompt(params) {
|
|
35
|
+
const lines = [
|
|
36
|
+
'[Reminder Task Thread Handoff]',
|
|
37
|
+
'This task-thread was opened by a scheduled reminder. Execute the reminder work here.',
|
|
38
|
+
'',
|
|
39
|
+
'[Current conversation target]',
|
|
40
|
+
`reply_target: ${params.threadTarget}`,
|
|
41
|
+
`Task: #${params.taskNumber} ${params.title}`,
|
|
42
|
+
`Reminder ID: ${params.reminderId}`,
|
|
43
|
+
...(params.occurrenceId ? [`Occurrence ID: ${params.occurrenceId}`] : []),
|
|
44
|
+
'',
|
|
45
|
+
'Rules:',
|
|
46
|
+
'- This thread is the primary work surface for this reminder occurrence.',
|
|
47
|
+
'- Put substantive progress updates, tool results, and the final answer in this thread.',
|
|
48
|
+
'- Do not send any manual follow-up in the primary DM after this handoff.',
|
|
49
|
+
'- Do not claim or create another task for this same reminder occurrence.',
|
|
50
|
+
'- If this reminder repeats, the platform will automatically create the next DM task-thread when it triggers.',
|
|
51
|
+
buildReminderCompletionInstruction(params),
|
|
52
|
+
'- Start working on the reminder task now.',
|
|
53
|
+
];
|
|
54
|
+
return lines.join('\n');
|
|
55
|
+
}
|
|
56
|
+
export function buildReminderGuidancePromptSection(params) {
|
|
57
|
+
return [
|
|
58
|
+
'[Reminder guidance]',
|
|
59
|
+
'This run was triggered by a scheduled reminder for the current direct DM scope of this agent.',
|
|
60
|
+
`Reminder ID: ${params.reminderId}`,
|
|
61
|
+
...(params.occurrenceId ? [`Occurrence ID: ${params.occurrenceId}`] : []),
|
|
62
|
+
'Reminder scope: this agent + this direct DM only.',
|
|
63
|
+
params.scheduleKind === 'one_time'
|
|
64
|
+
? `When this reminder work is ready for user review, run \`bigbang reminder submit-for-review ${params.occurrenceId ? `--occurrence-id ${params.occurrenceId}` : `--reminder-id ${params.reminderId}`}\`.`
|
|
65
|
+
: `When this reminder work is truly done, run \`bigbang reminder complete ${params.occurrenceId ? `--occurrence-id ${params.occurrenceId}` : `--reminder-id ${params.reminderId}`}\`.`,
|
|
66
|
+
'If this reminder repeats later, the platform will create the next DM task-thread automatically. Do not create the next run yourself.',
|
|
67
|
+
].join('\n');
|
|
68
|
+
}
|
|
69
|
+
export function buildReminderManagementGuidancePromptSection(params = {}) {
|
|
70
|
+
return [
|
|
71
|
+
'[Reminder guidance]',
|
|
72
|
+
'This run is a reminder-management request for the current primary direct DM of this agent.',
|
|
73
|
+
...(params.replyTarget ? [`reply_target: ${params.replyTarget}`] : []),
|
|
74
|
+
'Reminder scope: this agent + this direct DM only.',
|
|
75
|
+
'Use Bigbang reminder CLI commands only when the user wants to create, inspect, update, pause, resume, cancel, abandon, run, skip, snooze, submit, or complete reminders or reminder occurrences.',
|
|
76
|
+
'Available commands: `bigbang reminder list`, `create`, `update`, `pause`, `resume`, `cancel`, `abandon`, `occurrences`, `current`, `run-now`, `snooze`, `skip`, `submit-for-review`, `complete`.',
|
|
77
|
+
'Do not use any other platform surface for reminder management. If a reminder capability is missing from Bigbang, report the missing Bigbang command visibly.',
|
|
78
|
+
'For a new reminder: run `bigbang reminder create` once with the real reminder you intend to keep.',
|
|
79
|
+
'For an existing reminder by title: run `bigbang reminder list`, map title to reminderId, then run the action command.',
|
|
80
|
+
'For run-now / snooze / skip / submit-for-review / complete on the current occurrence: get reminderId first, run `bigbang reminder run-now --reminder-id <id>` or `bigbang reminder current --reminder-id <id>`, then use the exact occurrence command you need.',
|
|
81
|
+
'If the user gives an absolute snooze-until time, compute a relative duration from now and pass it to `bigbang reminder snooze --occurrence-id <id> --by <duration>`.',
|
|
82
|
+
'Do not claim reminder capabilities are unavailable before trying the Bigbang reminder commands above.',
|
|
83
|
+
'Treat successful Bigbang reminder results as authoritative. Do not probe reminder commands by creating placeholder reminders or repeating the same mutation just to verify success.',
|
|
84
|
+
'Reminder mutations are live user-visible state. Decide on the exact reminder you intend to keep before running `bigbang reminder create`.',
|
|
85
|
+
'Reminder action commands require opaque IDs. Reminder titles are not reminder_id values.',
|
|
86
|
+
'Never invent placeholder IDs such as "dummy", "dummy2", "test", or title text in reminder_id / occurrence_id fields. If you do not have the real ID yet, stop and call the lookup tool first.',
|
|
87
|
+
'When the user refers to a reminder by title, run `bigbang reminder list` first and map that title to the correct reminderId before running action commands.',
|
|
88
|
+
'When an action command needs occurrenceId, run `bigbang reminder current --reminder-id <id>` or `bigbang reminder occurrences --reminder-id <id>` first unless you already have the exact occurrenceId from an earlier reminder command result.',
|
|
89
|
+
'Triggered one-time reminders cannot be paused, resumed, cancelled, skipped, or snoozed after they start. If a one-time run failed before any linked task/thread was created, `bigbang reminder run-now --reminder-id <id>` retries that same occurrence; otherwise use `bigbang reminder update` to edit the brief or `bigbang reminder abandon` to stop that reminder.',
|
|
90
|
+
'Prefer Bigbang reminder commands for reminder state. Do not start with message search, history, or context bundle unless reminder commands cannot answer the question.',
|
|
91
|
+
'When the user wants exactly one reminder, perform exactly one `bigbang reminder create` for the real reminder you intend to keep.',
|
|
92
|
+
'After `bigbang reminder create` succeeds for the intended reminder, do not call create again for that same reminder just to confirm it.',
|
|
93
|
+
'Do not use `bigbang reminder create` as a scratchpad, parser aid, formatting check, or planning step.',
|
|
94
|
+
'Do not create probe, example, or throwaway reminders and then cancel them unless the user explicitly asked for that workflow.',
|
|
95
|
+
'Do not create placeholder, temporary, ignore, test, or no-op reminders that the user did not explicitly request to keep.',
|
|
96
|
+
'Recurring reminders automatically create a fresh DM task-thread when they trigger. Do not manually create the next run yourself.',
|
|
97
|
+
'At most one reminder occurrence can stay open at a time for the same reminder.',
|
|
98
|
+
'Use `bigbang reminder run-now` only when you intentionally want one extra immediate occurrence and there is no open occurrence, except the special one-time retry case above for a failed occurrence with no linked task/thread.',
|
|
99
|
+
'For one-time reminders, agent completion means submit for review first; user approval later moves the linked task to done and completes the occurrence.',
|
|
100
|
+
'For recurring reminders, completion is still explicit. Use the reminder occurrence completion tool instead of assuming task status alone will finish it.',
|
|
101
|
+
].join('\n');
|
|
102
|
+
}
|
|
103
|
+
export function buildReminderOccurrenceWakePrompt(params) {
|
|
104
|
+
return [
|
|
105
|
+
'[Reminder Occurrence Wake]',
|
|
106
|
+
'This existing DM task-thread is being reactivated for the same reminder occurrence after a delay or retry.',
|
|
107
|
+
'',
|
|
108
|
+
'[Current conversation target]',
|
|
109
|
+
`reply_target: ${params.threadTarget}`,
|
|
110
|
+
`Task: #${params.taskNumber} ${params.title}`,
|
|
111
|
+
`Reminder ID: ${params.reminderId}`,
|
|
112
|
+
...(params.occurrenceId ? [`Occurrence ID: ${params.occurrenceId}`] : []),
|
|
113
|
+
'',
|
|
114
|
+
'Rules:',
|
|
115
|
+
'- Continue work in this same task-thread.',
|
|
116
|
+
'- Do not create or claim another task for this same reminder occurrence.',
|
|
117
|
+
'- If this reminder repeats later, the platform will create the next DM task-thread automatically.',
|
|
118
|
+
buildReminderCompletionInstruction(params),
|
|
119
|
+
].join('\n');
|
|
120
|
+
}
|