feat(minecraft): add pause/resume control for cognitive engine via !pause command and Brain.setPaused/togglePaused/isPaused methods

Add paused state to Brain with setPaused/togglePaused/isPaused methods, suppress all event processing while paused (log suppression to llmLogEntries with scheduler/paused/suppressed tags), add !pause chat command in CognitiveEngine to toggle pause state with confirmation message, expose paused flag in getReplState/getSnapshot for debug visibility, add test verifying ll
This commit is contained in:
Rin
2026-02-18 11:14:40 +08:00
committed by Neko Ayaka
parent 48ad59b1cc
commit b124de0f47
5 changed files with 53 additions and 1 deletions
@@ -162,6 +162,19 @@ inv;
expect(enqueueSpy).not.toHaveBeenCalled()
})
it('suppresses llm turns while paused', async () => {
const deps: any = createDeps('await chat("hi")')
const brain: any = new Brain(deps)
const enqueueSpy = vi.fn(async () => undefined)
brain.enqueueEvent = enqueueSpy
brain.setPaused(true)
await brain.processEvent({} as any, createPerceptionEvent())
expect(deps.llmAgent.callLLM).not.toHaveBeenCalled()
expect(enqueueSpy).not.toHaveBeenCalled()
})
it('refreshes reflex context before debug perception injection', async () => {
const deps: any = createDeps('await skip()')
deps.reflexManager.refreshFromBotState = vi.fn()
@@ -142,6 +142,7 @@ const NO_ACTION_FOLLOWUP_SOURCE_ID = 'brain:no_action_followup'
export class Brain {
private debugService: DebugService
private readonly planner = new JavaScriptPlanner()
private paused = false
// State
private queue: QueuedEvent[] = []
@@ -247,7 +248,7 @@ export class Brain {
this.runtimeMineflayer = null
}
public getReplState(): { variables: PlannerGlobalDescriptor[], updatedAt: number } {
public getReplState(): { variables: PlannerGlobalDescriptor[], updatedAt: number, paused: boolean } {
const snapshot = this.deps.reflexManager.getContextSnapshot()
const replEvent: BotEvent = {
type: 'system_alert',
@@ -263,6 +264,7 @@ export class Brain {
return {
variables,
updatedAt: Date.now(),
paused: this.paused,
}
}
@@ -271,6 +273,7 @@ export class Brain {
queueLength: number
turnCounter: number
giveUpUntil: number
paused: boolean
contextView: string | undefined
conversationHistory: Message[]
llmLogEntries: LlmLogEntry[]
@@ -280,12 +283,26 @@ export class Brain {
queueLength: this.queue.length,
turnCounter: this.turnCounter,
giveUpUntil: this.giveUpUntil,
paused: this.paused,
contextView: this.lastContextView,
conversationHistory: this.cloneMessages(this.conversationHistory),
llmLogEntries: [...this.llmLogEntries],
}
}
public setPaused(paused: boolean): boolean {
this.paused = paused
return this.paused
}
public togglePaused(): boolean {
return this.setPaused(!this.paused)
}
public isPaused(): boolean {
return this.paused
}
public getLastLlmInput(): LlmInputSnapshot | null {
if (!this.lastLlmInputSnapshot)
return null
@@ -616,6 +633,20 @@ export class Brain {
// --- Cognitive Cycle ---
private async processEvent(bot: MineflayerWithAgents, event: BotEvent): Promise<void> {
if (this.paused) {
this.appendLlmLog({
turnId: this.turnCounter,
kind: 'scheduler',
eventType: event.type,
sourceType: event.source.type,
sourceId: event.source.id,
tags: ['scheduler', 'paused', 'suppressed'],
text: `Suppressed event while paused: ${event.type} from ${event.source.type}:${event.source.id}`,
})
this.deps.logger.log('INFO', `Brain: Ignoring event while paused (${event.type} from ${event.source.type}:${event.source.id})`)
return
}
this.resumeFromGiveUpIfNeeded(event)
if (this.shouldSuppressDuringGiveUp(event))
return
@@ -108,6 +108,12 @@ export function CognitiveEngine(options: CognitiveEngineOptions): MineflayerPlug
if (chatHandler.isBotMessage(username))
return
if (message.trim().toLowerCase() === '!pause') {
const paused = brain.togglePaused()
bot.bot.chat(`[debug] Cognitive engine ${paused ? 'paused' : 'resumed'} (by ${username}).`)
return
}
// Bridge chat directly into EventBus as a signal so Reflex can react to it.
eventBus.emit({
type: 'signal:chat_message',
@@ -46,6 +46,7 @@ describe('mcpReplServer', () => {
queueLength: 0,
turnCounter: 1,
giveUpUntil: 0,
paused: false,
contextView: 'test context',
conversationHistory: [],
llmLogEntries: [],
@@ -55,6 +55,7 @@ export class McpReplServer {
queueLength: snapshot.queueLength,
turn: snapshot.turnCounter,
giveUpUntil: snapshot.giveUpUntil,
paused: snapshot.paused,
}, null, 2),
mimeType: 'application/json',
}],