embark-ai 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mc-server/bot/bot.js +13 -6
- package/mc-server/bot/engine.js +3 -2
- package/mc-server/bot/llm.js +4 -2
- package/mc-server/bot/state.js +1 -0
- package/mc-server/bot/tasks.js +11 -6
- package/package.json +1 -1
- package/tui.js +5 -3
package/mc-server/bot/bot.js
CHANGED
|
@@ -1042,6 +1042,7 @@ function startAgentLoop() {
|
|
|
1042
1042
|
let prevGoal = 'idle'
|
|
1043
1043
|
let lostTicks = 0 // grace period before "Lost you"
|
|
1044
1044
|
let prevFollowTarget = null
|
|
1045
|
+
let lookTick = 0
|
|
1045
1046
|
let followRefreshTicks = 0 // re-issue follow goal periodically — recovers from stuck pathfinder
|
|
1046
1047
|
let stuckCheckTicks = 0
|
|
1047
1048
|
let lastBotPos = null
|
|
@@ -1060,12 +1061,18 @@ function startAgentLoop() {
|
|
|
1060
1061
|
setGoal('following', { source: 'agent_loop', reason: 'follow_auto_resume' })
|
|
1061
1062
|
}
|
|
1062
1063
|
|
|
1063
|
-
//
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1064
|
+
// Natural look — only when not busy, at reduced rate so Ember doesn't robotically stare
|
|
1065
|
+
lookTick++
|
|
1066
|
+
if (!taskBusy) {
|
|
1067
|
+
let lookTarget = null
|
|
1068
|
+
if (state.goal === 'following' && lookTick % 12 === 0) {
|
|
1069
|
+
lookTarget = getPlayer(state.followTarget) // glance every ~3s while following
|
|
1070
|
+
} else if (state.goal === 'idle' && lookTick % 20 === 0) {
|
|
1071
|
+
lookTarget = getNearestPlayer() // rare ambient glance every ~5s
|
|
1072
|
+
}
|
|
1073
|
+
if (lookTarget) {
|
|
1074
|
+
try { bot.lookAt(lookTarget.entity.position.offset(0, lookTarget.entity.height, 0)) } catch {}
|
|
1075
|
+
}
|
|
1069
1076
|
}
|
|
1070
1077
|
|
|
1071
1078
|
if (bot.entity?.isInWater && !taskBusy) {
|
package/mc-server/bot/engine.js
CHANGED
|
@@ -191,8 +191,9 @@ function selectAutonomousGoal(groundedState) {
|
|
|
191
191
|
return { action: 'craft', target: 'wooden_sword', say: 'Making a sword.' }
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
// 8. GATHER wood — keep stockpile up if trees are around
|
|
195
|
-
|
|
194
|
+
// 8. GATHER wood — keep stockpile up if trees are around (skip in creative: no drops)
|
|
195
|
+
const isCreative = groundedState.gameMode === 'creative'
|
|
196
|
+
if (!isCreative && hasLog && planksCount < 32 && !lowEnergy) {
|
|
196
197
|
return { action: 'gather_wood', say: pick(['Need wood.', 'Heading for that tree.']) }
|
|
197
198
|
}
|
|
198
199
|
|
package/mc-server/bot/llm.js
CHANGED
|
@@ -13,7 +13,8 @@ const API_KEY = process.env.FEATHERLESS_API_KEY
|
|
|
13
13
|
const USER_AGENT = 'project-k/1.0 (https://github.com/Syrthax/project-k)'
|
|
14
14
|
|
|
15
15
|
function buildPrompt(groundedState, intent, playerMessage, botName = 'Ember') {
|
|
16
|
-
const { self, inventory, nearbyBlocks, entities, hostileMobs, droppedCount, knownLocations, anger, environment } = groundedState
|
|
16
|
+
const { self, gameMode, inventory, nearbyBlocks, entities, hostileMobs, droppedCount, knownLocations, anger, environment } = groundedState
|
|
17
|
+
const isCreative = gameMode === 'creative'
|
|
17
18
|
|
|
18
19
|
const energyLabel = self.energy < 25 ? 'CRITICAL' : self.energy < 60 ? 'hurt' : 'full'
|
|
19
20
|
const hungerLabel = self.hunger < 25 ? 'STARVING' : self.hunger < 60 ? 'hungry' : 'fed'
|
|
@@ -54,6 +55,7 @@ Respond ONLY with valid JSON. NEVER invent facts not in GROUNDED STATE.
|
|
|
54
55
|
health: ${(self.hp ?? self.energy / 5).toFixed(1)}/20 hearts [${energyLabel}]
|
|
55
56
|
hunger: ${(self.food ?? self.hunger / 5).toFixed(0)}/20 [${hungerLabel}]
|
|
56
57
|
current goal: ${self.goal}
|
|
58
|
+
game mode: ${gameMode}${isCreative ? ' (creative — no item drops, gathering clears space only)' : ''}
|
|
57
59
|
inventory: ${invStr}
|
|
58
60
|
nearby blocks: ${blocksStr}
|
|
59
61
|
visible entities: ${entStr}
|
|
@@ -79,7 +81,7 @@ intent: ${intent}
|
|
|
79
81
|
- "follow" → come to player. Refuse if TIRED.
|
|
80
82
|
- "stop" → stop everything.
|
|
81
83
|
- "explore" → walk around.
|
|
82
|
-
- "gather_wood" → chop nearest log. Need logs nearby: ${hasLogs ? '✓' : '✗ no logs visible'}.
|
|
84
|
+
- "gather_wood" → chop nearest log${isCreative ? ' (creative: clears space, no drops)' : ''}. Need logs nearby: ${hasLogs ? '✓' : '✗ no logs visible'}.
|
|
83
85
|
- "craft_planks" → convert your logs to planks. Have logs: ${logsInInv ? '✓' : '✗ no logs in inventory'}.
|
|
84
86
|
- "go_to" → navigate to known location. Add "target":"name".
|
|
85
87
|
- "remember_here" → save current spot.
|
package/mc-server/bot/state.js
CHANGED
|
@@ -74,6 +74,7 @@ function buildGroundedState(bot, state, memory, anger = new Map(), perception =
|
|
|
74
74
|
goal: state.goal,
|
|
75
75
|
pos: { x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) },
|
|
76
76
|
},
|
|
77
|
+
gameMode: bot.game?.gameMode ?? 'survival',
|
|
77
78
|
inventory, // from bot.inventory.items() — real
|
|
78
79
|
nearbyBlocks, // from bot.findBlock() — real
|
|
79
80
|
entities, // from bot.entities — real
|
package/mc-server/bot/tasks.js
CHANGED
|
@@ -233,16 +233,21 @@ module.exports = function createTasks(deps) {
|
|
|
233
233
|
const ids = logIds()
|
|
234
234
|
const logBlock = bot.findBlock({ matching: ids, maxDistance: 50 })
|
|
235
235
|
if (!logBlock) { safeChat('No trees in range.'); return }
|
|
236
|
-
|
|
236
|
+
const creative = bot.game?.gameMode === 'creative'
|
|
237
|
+
console.log(`[${BOT_NAME}] Chopping log at ${logBlock.position}${creative ? ' (creative)' : ''}`)
|
|
237
238
|
await navNear(logBlock.position.x, logBlock.position.y, logBlock.position.z, 2)
|
|
238
239
|
const fresh = bot.blockAt(logBlock.position)
|
|
239
240
|
if (fresh && ids.includes(fresh.type) && bot.canDigBlock(fresh)) {
|
|
240
241
|
await safeDig(bot, fresh)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
if (!creative) {
|
|
243
|
+
// Survival: step into the drop so the item entity is auto-collected
|
|
244
|
+
await new Promise(r => setTimeout(r, 300))
|
|
245
|
+
try { await navNear(logBlock.position.x, logBlock.position.y, logBlock.position.z, 0) } catch {}
|
|
246
|
+
safeChat('Got wood.')
|
|
247
|
+
rememberEvent(memory, 'gathered_wood', {})
|
|
248
|
+
} else {
|
|
249
|
+
safeChat('Cleared.')
|
|
250
|
+
}
|
|
246
251
|
}
|
|
247
252
|
}
|
|
248
253
|
|
package/package.json
CHANGED
package/tui.js
CHANGED
|
@@ -1016,15 +1016,17 @@ async function actWorldSettings() {
|
|
|
1016
1016
|
const choice = await modalSelect('World Settings', items)
|
|
1017
1017
|
if (!choice || choice === '__cancel__') return
|
|
1018
1018
|
if (choice === '__save__') {
|
|
1019
|
-
|
|
1019
|
+
const toSave = { ...draft }
|
|
1020
|
+
if (toSave.gamemode !== props.gamemode) toSave['force-gamemode'] = 'true'
|
|
1021
|
+
writeServerProps(toSave)
|
|
1020
1022
|
await modalMessage('{green-fg}Saved to server.properties.{/}\nRestart server to apply.', 'green')
|
|
1021
1023
|
return
|
|
1022
1024
|
}
|
|
1023
1025
|
|
|
1024
1026
|
const def = WORLD_DEFS.find(d => d.key === choice)
|
|
1025
1027
|
if (def.type === 'cycle') {
|
|
1026
|
-
const
|
|
1027
|
-
draft[def.key] =
|
|
1028
|
+
const picked = await modalSelect(def.label, def.options.map(o => ({ label: o, value: o })))
|
|
1029
|
+
if (picked) draft[def.key] = picked
|
|
1028
1030
|
} else if (def.type === 'text') {
|
|
1029
1031
|
const val = await modalPrompt(`${def.label} (current: "${draft[def.key] || 'blank'}")`, draft[def.key] || '')
|
|
1030
1032
|
if (val !== null) draft[def.key] = val.trim()
|