executeAutoDream method
Entry point from post-sampling hooks.
Implementation
Future<void> executeAutoDream() async {
if (!_initialized) return;
if (!_isGateOpen()) return;
final cfg = getConfig();
// --- Time gate ---
int lastAt;
try {
lastAt = await lockService.readLastConsolidatedAt();
} catch (e) {
logDebug('[autoDream] readLastConsolidatedAt failed: $e');
return;
}
final hoursSince =
(DateTime.now().millisecondsSinceEpoch - lastAt) / 3600000.0;
if (hoursSince < cfg.minHours) return;
// --- Scan throttle ---
final sinceScanMs =
DateTime.now().millisecondsSinceEpoch - _lastSessionScanAt;
if (sinceScanMs < _sessionScanIntervalMs) {
logDebug(
'[autoDream] scan throttle — time-gate passed but last scan was '
'${(sinceScanMs / 1000).round()}s ago',
);
return;
}
_lastSessionScanAt = DateTime.now().millisecondsSinceEpoch;
// --- Session gate ---
List<String> sessionIds;
try {
sessionIds = await lockService.listSessionsTouchedSince(lastAt);
} catch (e) {
logDebug('[autoDream] listSessionsTouchedSince failed: $e');
return;
}
// Exclude the current session.
final currentSession = getSessionId();
sessionIds = sessionIds.where((id) => id != currentSession).toList();
if (sessionIds.length < cfg.minSessions) {
logDebug(
'[autoDream] skip — ${sessionIds.length} sessions since last '
'consolidation, need ${cfg.minSessions}',
);
return;
}
// --- Lock ---
int? priorMtime;
try {
priorMtime = await lockService.tryAcquireLock();
} catch (e) {
logDebug('[autoDream] lock acquire failed: $e');
return;
}
if (priorMtime == null) return;
logDebug(
'[autoDream] firing — ${hoursSince.toStringAsFixed(1)}h since last, '
'${sessionIds.length} sessions to review',
);
logEvent('tengu_auto_dream_fired', {
'hours_since': hoursSince.round(),
'sessions_since': sessionIds.length,
});
final taskId = 'dream_${_nextTaskId++}';
dreamTasks[taskId] = DreamTaskState(
taskId: taskId,
sessionsReviewing: sessionIds.length,
priorMtime: priorMtime,
);
try {
final memoryRoot = getAutoMemPath();
final transcriptDir = getProjectDir(getOriginalCwd());
final extra =
'''
**Tool constraints for this run:** Bash is restricted to read-only commands (`ls`, `find`, `grep`, `cat`, `stat`, `wc`, `head`, `tail`, and similar). Anything that writes, redirects to a file, or modifies state will be denied.
Sessions since last consolidation (${sessionIds.length}):
${sessionIds.map((id) => '- $id').join('\n')}''';
final prompt = buildConsolidationPrompt(memoryRoot, transcriptDir, extra);
final result = await runDreamAgent(
prompt: prompt,
onMessage: (msg) => _watchProgress(taskId, msg),
);
// Complete.
final currentTask = dreamTasks[taskId];
if (currentTask != null) {
dreamTasks[taskId] = currentTask.copyWith(
status: DreamTaskStatus.completed,
);
}
logDebug(
'[autoDream] completed — cache: read=${result.cacheReadTokens} '
'created=${result.cacheCreatedTokens}',
);
logEvent('tengu_auto_dream_completed', {
'cache_read': result.cacheReadTokens,
'cache_created': result.cacheCreatedTokens,
'output': result.outputTokens,
'sessions_reviewed': sessionIds.length,
});
} catch (e) {
logDebug('[autoDream] fork failed: $e');
logEvent('tengu_auto_dream_failed', {});
final currentTask = dreamTasks[taskId];
if (currentTask != null) {
dreamTasks[taskId] = currentTask.copyWith(
status: DreamTaskStatus.failed,
);
}
// Rewind mtime so time-gate passes again.
await lockService.rollbackLock(priorMtime);
}
}