Skip to content

Commit b5978a7

Browse files
shinohara-rinnekomeowww
authored andcommitted
feat(minecraft): add forgetConversation runtime function for prompt/debug reset workflows
Add forgetConversation method to Brain that clears conversationHistory and lastLlmInputSnapshot, expose as forget_conversation() global in JavaScriptPlanner sandbox, update brain-prompt with value-first rule mandating read->return->act pattern for world/query-dependent requests to avoid acting on unresolved variables, add tests verifying forgetConversation only clears conversation state without touching ll
1 parent 27c3b7f commit b5978a7

File tree

6 files changed

+59
-0
lines changed

6 files changed

+59
-0
lines changed

services/minecraft/src/cognitive/conscious/brain.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,28 @@ function createPerceptionEvent() {
7272
}
7373

7474
describe('brain no-action follow-up', () => {
75+
it('forgets conversation only', () => {
76+
const brain: any = new Brain(createDeps('await skip()'))
77+
brain.conversationHistory = [{ role: 'user', content: 'old' }]
78+
brain.lastLlmInputSnapshot = {
79+
systemPrompt: 'sys',
80+
userMessage: 'msg',
81+
messages: [],
82+
conversationHistory: [],
83+
updatedAt: Date.now(),
84+
attempt: 1,
85+
}
86+
brain.llmLogEntries = [{ id: 1, turnId: 1, kind: 'turn_input', timestamp: Date.now(), eventType: 'x', sourceType: 'x', sourceId: 'x', tags: [], text: 'x' }]
87+
88+
const result = brain.forgetConversation()
89+
90+
expect(result.ok).toBe(true)
91+
expect(result.cleared).toEqual(['conversationHistory', 'lastLlmInputSnapshot'])
92+
expect(brain.conversationHistory).toEqual([])
93+
expect(brain.lastLlmInputSnapshot).toBeNull()
94+
expect(brain.llmLogEntries).toHaveLength(1)
95+
})
96+
7597
it('returns trailing expression values in debug repl scripts', async () => {
7698
const brain: any = new Brain(createDeps('await skip()'))
7799

services/minecraft/src/cognitive/conscious/brain.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,15 @@ export class Brain {
313313
return JSON.parse(JSON.stringify(entries)) as LlmTraceEntry[]
314314
}
315315

316+
public forgetConversation(): { ok: true, cleared: string[] } {
317+
this.conversationHistory = []
318+
this.lastLlmInputSnapshot = null
319+
return {
320+
ok: true,
321+
cleared: ['conversationHistory', 'lastLlmInputSnapshot'],
322+
}
323+
}
324+
316325
public async injectDebugEvent(event: BotEvent): Promise<void> {
317326
if (!this.runtimeMineflayer) {
318327
throw new Error('Brain runtime is not initialized yet')
@@ -462,6 +471,7 @@ export class Brain {
462471
llmInput: this.lastLlmInputSnapshot,
463472
currentInput: this.currentInputEnvelope,
464473
llmLog: this.llmLogRuntime,
474+
forgetConversation: () => this.forgetConversation(),
465475
}
466476
}
467477

services/minecraft/src/cognitive/conscious/js-planner.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe('javaScriptPlanner', () => {
4646
updatedAt: Date.now(),
4747
attempt: 1,
4848
},
49+
forgetConversation: () => ({ ok: true, cleared: ['conversationHistory', 'lastLlmInputSnapshot'] }),
4950
} as any
5051

5152
it('maps positional/object args and executes tools in order', async () => {
@@ -170,6 +171,7 @@ describe('javaScriptPlanner', () => {
170171
expect(names).toContain('mineflayer')
171172
expect(names).toContain('currentInput')
172173
expect(names).toContain('llmLog')
174+
expect(names).toContain('forget_conversation')
173175

174176
const mem = descriptors.find(d => d.name === 'mem')
175177
expect(mem?.readonly).toBe(false)
@@ -182,6 +184,15 @@ describe('javaScriptPlanner', () => {
182184
expect(planned.actions[0]?.action).toEqual({ tool: 'chat', params: { message: 'llm=latest user message' } })
183185
})
184186

187+
it('exposes forget_conversation runtime function', async () => {
188+
const planner = new JavaScriptPlanner()
189+
const executeAction = vi.fn(async action => `ok:${action.tool}`)
190+
const planned = await planner.evaluate('return forget_conversation()', actions, globals, executeAction)
191+
192+
expect(planned.returnValue).toContain('conversationHistory')
193+
expect(planned.actions).toHaveLength(0)
194+
})
195+
185196
it('detects expression-friendly REPL inputs', () => {
186197
const planner = new JavaScriptPlanner()
187198
expect(planner.canEvaluateAsExpression('2 + 3')).toBe(true)

services/minecraft/src/cognitive/conscious/js-planner.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export interface RuntimeGlobals {
6969
bot?: unknown
7070
currentInput?: unknown
7171
llmLog?: unknown
72+
forgetConversation?: () => { ok: true, cleared: string[] }
7273
llmInput?: {
7374
systemPrompt: string
7475
userMessage: string
@@ -195,6 +196,7 @@ export class JavaScriptPlanner {
195196
{ name: 'llmInput', kind: 'object', readonly: true },
196197
{ name: 'currentInput', kind: 'object', readonly: true },
197198
{ name: 'llmLog', kind: 'object', readonly: true },
199+
{ name: 'forget_conversation', kind: 'function', readonly: true },
198200
{ name: 'llmMessages', kind: 'object', readonly: true },
199201
{ name: 'llmSystemPrompt', kind: 'string', readonly: true },
200202
{ name: 'llmUserMessage', kind: 'string', readonly: true },
@@ -238,6 +240,7 @@ export class JavaScriptPlanner {
238240
expect: this.sandbox.expect,
239241
expectMoved: this.sandbox.expectMoved,
240242
expectNear: this.sandbox.expectNear,
243+
forget_conversation: this.sandbox.forget_conversation,
241244
}
242245

243246
for (const item of staticGlobals) {
@@ -394,6 +397,7 @@ export class JavaScriptPlanner {
394397
this.sandbox.llmInput = llmInput
395398
this.sandbox.currentInput = currentInput
396399
this.sandbox.llmLog = globals.llmLog ?? null
400+
this.sandbox.forget_conversation = globals.forgetConversation ?? null
397401
this.sandbox.llmMessages = llmInput?.messages ?? []
398402
this.sandbox.llmSystemPrompt = llmInput?.systemPrompt ?? ''
399403
this.sandbox.llmUserMessage = llmInput?.userMessage ?? ''

services/minecraft/src/cognitive/conscious/prompts/brain-prompt.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ You are an autonomous agent playing Minecraft.
1818
- Use `await` on tool calls when later logic depends on the result.
1919
- Globals refreshed every turn: `snapshot`, `self`, `environment`, `social`, `threat`, `attention`, `autonomy`, `event`, `now`, `query`, `bot`, `mineflayer`, `currentInput`, `llmLog`.
2020
- Persistent globals: `mem` (cross-turn memory), `lastRun` (this run), `prevRun` (previous run), `lastAction` (latest action result), `log(...)`.
21+
- `forget_conversation()` clears conversation memory (`conversationHistory` and `lastLlmInputSnapshot`) for prompt/debug reset workflows.
2122
- Last script outcome is also echoed in the next turn as `[SCRIPT]` context (return value, action stats, and logs).
2223
- Maximum actions per turn: 5. If you need more, break down your task to perform in multiple turns.
2324
- Mineflayer API is provided for low-level control.
@@ -89,6 +90,12 @@ Silent-eval pattern (strongly encouraged):
8990
- Turn B: inspect `[SCRIPT]` return / `llmLog`, then act: `await collectBlocks({ type: ..., num: ... })`
9091
- Prefer this when a wrong action would be costly, dangerous, or hard to undo.
9192

93+
Value-first rule (mandatory for read -> action flows):
94+
- If a request depends on observed world/query data, first run an evaluation-only turn and `return` the concrete value.
95+
- Do not call world/chat tools in that first turn.
96+
- In the next turn, use `[SCRIPT] Last eval return=...` as the source of truth for tool parameters/messages.
97+
- Avoid acting on unresolved intermediate variables when a concrete returned value can be verified first.
98+
9299
# Response Format
93100
You must respond with JavaScript only (no markdown code fences).
94101
Call tool functions directly.
@@ -133,6 +140,9 @@ Common patterns:
133140
- Prefer deterministic scripts: no random branching unless needed.
134141
- Keep per-turn scripts short and focused on one tactical objective.
135142
- Prefer "evaluate then act" loops: first compute and return candidate values (no actions), then perform tools in the next turn using confirmed values.
143+
- For read->chat/report tasks, always prefer:
144+
- Turn A: `const value = ...; return value`
145+
- Turn B: construct tool params/messages from confirmed returned value.
136146
- If you hit repeated failures with no progress, call `await giveUp({ reason, cooldown_seconds })` once instead of retry-spamming.
137147
- Treat `environment.nearbyPlayersGaze` as a weak hint, not a command. Never move solely because someone looked somewhere unless they also gave a clear instruction.
138148
- Use `followPlayer` to set idle auto-follow and `clearFollowTarget` before independent exploration.

services/minecraft/src/cognitive/conscious/prompts/brain-prompt.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@ describe('generateBrainSystemPrompt', () => {
2121
expect(prompt).toContain('Heuristic composition examples')
2222
expect(prompt).toContain('llmLog')
2323
expect(prompt).toContain('Silent-eval pattern')
24+
expect(prompt).toContain('Value-first rule')
25+
expect(prompt).toContain('forget_conversation()')
2426
})
2527
})

0 commit comments

Comments
 (0)