embark-ai 1.0.3 → 1.0.4

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.
@@ -555,6 +555,22 @@ function replaceTask(goalName, fn, opts) {
555
555
  return runTask(goalName, fn, opts)
556
556
  }
557
557
 
558
+ // When the user assigns Ember a new task while she's following, drop the follow
559
+ // so the agent-loop auto-resume does not re-engage it after the task finishes.
560
+ function interruptFollow() {
561
+ if (!state.followTarget) return
562
+ movement.stop('follow')
563
+ state.followTarget = null
564
+ setGoal('idle', { source: 'interrupt_follow', reason: 'new_task' })
565
+ log.info('follow_interrupted_by_task')
566
+ }
567
+
568
+ // Wrapper for fallbackCommand task dispatches: preempts follow, then runs the task.
569
+ function taskCmd(goalName, fn, opts) {
570
+ interruptFollow()
571
+ return runTask(goalName, fn, opts)
572
+ }
573
+
558
574
  // Track recent path failures by goal — repeated explore timeouts mean we're stuck.
559
575
  const taskFailureCounts = new Map() // goalName -> consecutive failure count
560
576
  const EXPLORE_FAIL_THRESHOLD = 3
@@ -1222,6 +1238,7 @@ function executeAction(result, username, groundedState) {
1222
1238
  result.say = "Still busy — give me a sec."
1223
1239
  return
1224
1240
  }
1241
+ if (taskActions.includes(result.action)) interruptFollow()
1225
1242
 
1226
1243
  switch (result.action) {
1227
1244
 
@@ -1323,28 +1340,28 @@ function fallbackCommand(username, message) {
1323
1340
  if (cmd === 'follow me') { if (state.energy < 25) { safeChat('Too tired.'); return }; state.followTarget = username; setGoal('following', { source: 'fallback_cmd', reason: 'follow_me' }); safeChat('Following.') }
1324
1341
  else if (cmd === 'stop') { cancelCurrentTask(); state.followTarget = null; safeChat('Stopped.') }
1325
1342
  else if (cmd === 'status') { safeChat(`Goal: ${state.goal} | E:${state.energy.toFixed(0)} H:${state.hunger.toFixed(0)} | anger:${anger.size}`) }
1326
- else if (cmd === 'explore') { if (!runTask('exploring', tasks.taskExplore)) safeChat("Busy.") }
1327
- else if (cmd === 'get wood' || cmd === 'chop tree'){ if (!runTask('gathering', tasks.taskGatherWood)) safeChat("Busy.") }
1328
- else if (cmd === 'make planks') { if (!runTask('crafting', tasks.taskCraftPlanks)) safeChat("Busy.") }
1329
- else if (cmd === 'attack' || cmd === 'fight') { if (!runTask('attacking', tasks.taskAttackMobs)) safeChat("Busy.") }
1330
- else if (cmd === 'collect' || cmd === 'pick up') { if (!runTask('collecting', tasks.taskCollectNearby)) safeChat("Busy.") }
1331
- else if (cmd === 'build house') { if (!runTask('building', tasks.taskBuildHouseSmart)) safeChat("Busy.") }
1343
+ else if (cmd === 'explore') { if (!taskCmd('exploring', tasks.taskExplore)) safeChat("Busy.") }
1344
+ else if (cmd === 'get wood' || cmd === 'chop tree'){ if (!taskCmd('gathering', tasks.taskGatherWood)) safeChat("Busy.") }
1345
+ else if (cmd === 'make planks') { if (!taskCmd('crafting', tasks.taskCraftPlanks)) safeChat("Busy.") }
1346
+ else if (cmd === 'attack' || cmd === 'fight') { if (!taskCmd('attacking', tasks.taskAttackMobs)) safeChat("Busy.") }
1347
+ else if (cmd === 'collect' || cmd === 'pick up') { if (!taskCmd('collecting', tasks.taskCollectNearby)) safeChat("Busy.") }
1348
+ else if (cmd === 'build house') { if (!taskCmd('building', tasks.taskBuildHouseSmart)) safeChat("Busy.") }
1332
1349
  else if (cmd === 'inventory') { const it = bot.inventory.items(); safeChat(it.length ? it.map(i=>`${i.name}x${i.count}`).join(', ') : 'Empty.') }
1333
1350
  else if (cmd === 'look around') { const gs = buildGroundedState(bot, state, memory, anger, envPerception); safeChat(`I see: ${chatSummary(gs) || 'nothing'}`) }
1334
1351
  else {
1335
- const m = cmd.match(/^craft (.+)$/); if (m) { if (!runTask('crafting', () => tasks.taskCraftItem(m[1]))) safeChat("Busy."); return }
1336
- const p = cmd.match(/^place (.+)$/); if (p) { if (!runTask('placing', () => tasks.taskPlaceBlock(p[1].trim()))) safeChat("Busy."); return }
1352
+ const m = cmd.match(/^craft (.+)$/); if (m) { if (!taskCmd('crafting', () => tasks.taskCraftItem(m[1]))) safeChat("Busy."); return }
1353
+ const p = cmd.match(/^place (.+)$/); if (p) { if (!taskCmd('placing', () => tasks.taskPlaceBlock(p[1].trim()))) safeChat("Busy."); return }
1337
1354
  const mn = cmd.match(/^mine (.+)$/); if (mn) {
1338
1355
  const parts = mn[1].trim().match(/^(.+?)\s+(\d+)$/)
1339
1356
  const name = parts ? parts[1] : mn[1].trim()
1340
1357
  const cnt = parts ? Math.min(parseInt(parts[2]), 16) : 1
1341
- if (!runTask('mining', () => tasks.taskMineBlock(name, cnt))) safeChat("Busy."); return
1358
+ if (!taskCmd('mining', () => tasks.taskMineBlock(name, cnt))) safeChat("Busy."); return
1342
1359
  }
1343
- if (cmd === 'eat') { if (!runTask('eating', tasks.taskEatFood)) safeChat("Busy."); return }
1344
- if (cmd === 'flee' || cmd === 'run') { if (!runTask('fleeing', tasks.taskFlee)) safeChat("Busy."); return }
1345
- if (cmd === 'escape' || cmd === 'climb out' || cmd === 'get out') { if (!runTask('escaping', tasks.taskEscape)) safeChat("Busy."); return }
1360
+ if (cmd === 'eat') { if (!taskCmd('eating', tasks.taskEatFood)) safeChat("Busy."); return }
1361
+ if (cmd === 'flee' || cmd === 'run') { if (!taskCmd('fleeing', tasks.taskFlee)) safeChat("Busy."); return }
1362
+ if (cmd === 'escape' || cmd === 'climb out' || cmd === 'get out') { if (!taskCmd('escaping', tasks.taskEscape)) safeChat("Busy."); return }
1346
1363
  const w = cmd.match(/^where is (.+)$/); if (w) { const loc = recallLocation(memory, w[1].trim()); safeChat(loc ? `${w[1]}: ${loc.pos.x}, ${loc.pos.y}, ${loc.pos.z}` : `Don't know.`); return }
1347
- const g = cmd.match(/^go to (.+)$/); if (g) { if (!runTask('going_to', () => tasks.taskGoTo(g[1].trim()))) safeChat("Busy."); else safeChat(`Going to ${g[1]}.`) }
1364
+ const g = cmd.match(/^go to (.+)$/); if (g) { if (!taskCmd('going_to', () => tasks.taskGoTo(g[1].trim()))) safeChat("Busy."); else safeChat(`Going to ${g[1]}.`) }
1348
1365
  }
1349
1366
  }
1350
1367
 
@@ -238,6 +238,9 @@ module.exports = function createTasks(deps) {
238
238
  const fresh = bot.blockAt(logBlock.position)
239
239
  if (fresh && ids.includes(fresh.type) && bot.canDigBlock(fresh)) {
240
240
  await safeDig(bot, fresh)
241
+ // Step into the drop so the item entity is auto-collected
242
+ await new Promise(r => setTimeout(r, 300))
243
+ try { await navNear(logBlock.position.x, logBlock.position.y, logBlock.position.z, 0) } catch {}
241
244
  safeChat('Got wood.')
242
245
  rememberEvent(memory, 'gathered_wood', {})
243
246
  }
@@ -909,6 +912,8 @@ module.exports = function createTasks(deps) {
909
912
  const fresh = bot.blockAt(logBlock.position)
910
913
  if (fresh && ids.includes(fresh.type)) {
911
914
  await safeDig(bot, fresh)
915
+ await new Promise(r => setTimeout(r, 300))
916
+ try { await navNear(logBlock.position.x, logBlock.position.y, logBlock.position.z, 0) } catch {}
912
917
  chopped++
913
918
  } else { break }
914
919
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "embark-ai",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Autonomous Minecraft agent powered by Featherless AI or Ollama",
5
5
  "keywords": [
6
6
  "minecraft",