fix(minecraft): remove returnValue from prevRun/lastRun to prevent stringified return mirror exposure
Remove returnValue assignment from sandbox.lastRun after script execution (keep only returnRaw for typed values), add test verifying prevRun does not expose stringified returnValue property, update brain-prompt.md to remove returnValue references and guide users to stringify returnRaw explicitly for text output (JSON.stringify(prevRun.returnRaw)), update value-first rule examples to use prevRun.returnRaw instea
This commit is contained in:
@@ -126,6 +126,19 @@ describe('javaScriptPlanner', () => {
|
||||
expect(planned.actions.map(a => a.action)).toEqual([{ tool: 'chat', params: { message: '2 oak_log' } }])
|
||||
})
|
||||
|
||||
it('does not expose stringified return mirror on prevRun', async () => {
|
||||
const planner = new JavaScriptPlanner()
|
||||
const executeAction = vi.fn(async action => `ok:${action.tool}`)
|
||||
|
||||
await planner.evaluate(`
|
||||
const inv = [{ name: "oak_log", count: 2 }]
|
||||
return inv
|
||||
`, actions, globals, executeAction)
|
||||
|
||||
const planned = await planner.evaluate('return Object.prototype.hasOwnProperty.call(prevRun, "returnValue")', actions, globals, executeAction)
|
||||
expect(planned.returnValue).toBe('false')
|
||||
})
|
||||
|
||||
it('provides snapshot globals in script scope', async () => {
|
||||
const planner = new JavaScriptPlanner()
|
||||
const executeAction = vi.fn(async action => `ok:${action.tool}`)
|
||||
|
||||
@@ -160,7 +160,6 @@ export class JavaScriptPlanner {
|
||||
})
|
||||
|
||||
if (isRecord(this.sandbox.lastRun)) {
|
||||
this.sandbox.lastRun.returnValue = returnValue
|
||||
this.sandbox.lastRun.returnRaw = result
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ You are an autonomous agent playing Minecraft.
|
||||
- Globals refreshed every turn: `snapshot`, `self`, `environment`, `social`, `threat`, `attention`, `autonomy`, `event`, `now`, `query`, `bot`, `mineflayer`, `currentInput`, `llmLog`, `actionQueue`, `noActionBudget`, `errorBurstGuard`.
|
||||
- Persistent globals: `mem` (cross-turn memory), `lastRun` (this run), `prevRun` (previous run), `lastAction` (latest action result), `log(...)`.
|
||||
- Budget helpers: `setNoActionBudget(n)` and `getNoActionBudget()` control/inspect eval-only no-action follow-up budget.
|
||||
- Cross-turn result access: use `prevRun.returnRaw` for typed values (arrays/objects); `prevRun.returnValue` is stringified for display/logging.
|
||||
- Cross-turn result access: use `prevRun.returnRaw` for typed values (arrays/objects). If you need text output, stringify `returnRaw` explicitly.
|
||||
- `forget_conversation()` clears conversation memory (`conversationHistory` and `lastLlmInputSnapshot`) for prompt/debug reset workflows.
|
||||
- Last script outcome is also echoed in the next turn as `[SCRIPT]` context (return value, action stats, and logs).
|
||||
- Maximum tool calls per turn: 5.
|
||||
@@ -116,7 +116,7 @@ Silent-eval pattern (strongly encouraged):
|
||||
- Use no-action evaluation turns to inspect uncertain values before committing to world actions.
|
||||
- Good pattern:
|
||||
- Turn A: `let blocksToMine = someFunc(); blocksToMine`
|
||||
- Turn B: inspect `[SCRIPT]` return / `llmLog`, then act: `await collectBlocks({ type: ..., num: ... })`
|
||||
- Turn B: inspect `prevRun.returnRaw` / `llmLog`, then act: `await collectBlocks({ type: ..., num: ... })`
|
||||
- Prefer this when a wrong action would be costly, dangerous, or hard to undo.
|
||||
- A `no_actions` follow-up after an eval-only turn is normal; follow-ups are budgeted and can chain for multi-step reasoning.
|
||||
- Default no-action follow-up budget is 3 and max is 8.
|
||||
@@ -127,8 +127,10 @@ Value-first rule (mandatory for read -> action flows):
|
||||
- If a request depends on observed world/query data, first run an evaluation-only turn and end with the concrete value expression.
|
||||
- Do not call world/chat tools in that first turn.
|
||||
- End eval turns with a concrete final expression (for example `inv`, `target`, `summary`) so `[SCRIPT]` captures it.
|
||||
- In the next turn, use `[SCRIPT] Last eval return=...` as the source of truth for tool parameters/messages.
|
||||
- In the next turn, use `prevRun.returnRaw` as the source of truth for tool parameters/messages.
|
||||
- Do not re-query the same read value in the follow-up turn; use the persisted value to avoid TOCTOU drift.
|
||||
- For typed follow-up logic, use `prevRun.returnRaw` (or `lastRun.returnRaw` for current-turn chaining).
|
||||
- If you need a string for chat/logging, stringify raw data yourself (for example `JSON.stringify(prevRun.returnRaw)`).
|
||||
- Avoid acting on unresolved intermediate variables when a concrete returned value can be verified first.
|
||||
- For explicit user tasks (e.g. "get X", "craft Y", "go to Z"), do not stay in repeated evaluation-only turns.
|
||||
- After a small number of evaluation turns, the next turn must either:
|
||||
@@ -137,7 +139,8 @@ Value-first rule (mandatory for read -> action flows):
|
||||
- explicitly increase no-action budget for this scenario via `setNoActionBudget(n)`.
|
||||
- Example (read -> chat report):
|
||||
- Turn A: `const inv = query.inventory().summary(); inv`
|
||||
- Turn B: `const inv = prevRun.returnValue; const text = Array.isArray(inv) && inv.length ? inv.map(({ name, count }) => `${count} ${name}`).join(", ") : "nothing"; await chat({ message: `I have: ${text}`, feedback: false })`
|
||||
- Turn B: `const inv = prevRun.returnRaw; const text = Array.isArray(inv) && inv.length ? inv.map(({ name, count }) => `${count} ${name}`).join(", ") : "nothing"; await chat({ message: `I have: ${text}`, feedback: false })`
|
||||
- Turn B (raw -> explicit stringify): `const coords = prevRun.returnRaw; await chat({ message: Array.isArray(coords) ? JSON.stringify(coords) : "[]", feedback: false })`
|
||||
|
||||
# Response Format
|
||||
You must respond with JavaScript only (no markdown code fences).
|
||||
|
||||
Reference in New Issue
Block a user