Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
5.0 kB · 157 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158// Background task entry for auto-dream (memory consolidation subagent).// Makes the otherwise-invisible forked agent visible in the footer pill and// Shift+Down dialog. The dream agent itself is unchanged — this is pure UI// surfacing via the existing task registry.
import { rollbackConsolidationLock } from '../../services/autoDream/consolidationLock.js'import type { SetAppState, Task, TaskStateBase } from '../../Task.js'import { createTaskStateBase, generateTaskId } from '../../Task.js'import { registerTask, updateTaskState } from '../../utils/task/framework.js'
// Keep only the N most recent turns for live display.const MAX_TURNS = 30
// A single assistant turn from the dream agent, tool uses collapsed to a count.export type DreamTurn = { text: string toolUseCount: number}
// No phase detection — the dream prompt has a 4-stage structure// (orient/gather/consolidate/prune) but we don't parse it. Just flip from// 'starting' to 'updating' when the first Edit/Write tool_use lands.export type DreamPhase = 'starting' | 'updating'
export type DreamTaskState = TaskStateBase & { type: 'dream' phase: DreamPhase sessionsReviewing: number /** * Paths observed in Edit/Write tool_use blocks via onMessage. This is an * INCOMPLETE reflection of what the dream agent actually changed — it misses * any bash-mediated writes and only captures the tool calls we pattern-match. * Treat as "at least these were touched", not "only these were touched". */ filesTouched: string[] /** Assistant text responses, tool uses collapsed. Prompt is NOT included. */ turns: DreamTurn[] abortController?: AbortController /** Stashed so kill can rewind the lock mtime (same path as fork-failure). */ priorMtime: number}
export function isDreamTask(task: unknown): task is DreamTaskState { return ( typeof task === 'object' && task !== null && 'type' in task && task.type === 'dream' )}
export function registerDreamTask( setAppState: SetAppState, opts: { sessionsReviewing: number priorMtime: number abortController: AbortController },): string { const id = generateTaskId('dream') const task: DreamTaskState = { ...createTaskStateBase(id, 'dream', 'dreaming'), type: 'dream', status: 'running', phase: 'starting', sessionsReviewing: opts.sessionsReviewing, filesTouched: [], turns: [], abortController: opts.abortController, priorMtime: opts.priorMtime, } registerTask(task, setAppState) return id}
export function addDreamTurn( taskId: string, turn: DreamTurn, touchedPaths: string[], setAppState: SetAppState,): void { updateTaskState<DreamTaskState>(taskId, setAppState, task => { const seen = new Set(task.filesTouched) const newTouched = touchedPaths.filter(p => !seen.has(p) && seen.add(p)) // Skip the update entirely if the turn is empty AND nothing new was // touched. Avoids re-rendering on pure no-ops. if ( turn.text === '' && turn.toolUseCount === 0 && newTouched.length === 0 ) { return task } return { ...task, phase: newTouched.length > 0 ? 'updating' : task.phase, filesTouched: newTouched.length > 0 ? [...task.filesTouched, ...newTouched] : task.filesTouched, turns: task.turns.slice(-(MAX_TURNS - 1)).concat(turn), } })}
export function completeDreamTask( taskId: string, setAppState: SetAppState,): void { // notified: true immediately — dream has no model-facing notification path // (it's UI-only), and eviction requires terminal + notified. The inline // appendSystemMessage completion note IS the user surface. updateTaskState<DreamTaskState>(taskId, setAppState, task => ({ ...task, status: 'completed', endTime: Date.now(), notified: true, abortController: undefined, }))}
export function failDreamTask(taskId: string, setAppState: SetAppState): void { updateTaskState<DreamTaskState>(taskId, setAppState, task => ({ ...task, status: 'failed', endTime: Date.now(), notified: true, abortController: undefined, }))}
export const DreamTask: Task = { name: 'DreamTask', type: 'dream',
async kill(taskId, setAppState) { let priorMtime: number | undefined updateTaskState<DreamTaskState>(taskId, setAppState, task => { if (task.status !== 'running') return task task.abortController?.abort() priorMtime = task.priorMtime return { ...task, status: 'killed', endTime: Date.now(), notified: true, abortController: undefined, } }) // Rewind the lock mtime so the next session can retry. Same path as the // fork-failure catch in autoDream.ts. If updateTaskState was a no-op // (already terminal), priorMtime stays undefined and we skip. if (priorMtime !== undefined) { await rollbackConsolidationLock(priorMtime) } },}