embark-ai 1.0.2 → 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.2",
3
+ "version": "1.0.4",
4
4
  "description": "Autonomous Minecraft agent powered by Featherless AI or Ollama",
5
5
  "keywords": [
6
6
  "minecraft",
@@ -10,13 +10,13 @@
10
10
  "ollama",
11
11
  "autonomous-agent"
12
12
  ],
13
- "homepage": "https://github.com/Syrthax/project-k#readme",
13
+ "homepage": "https://github.com/Syrthax/Embark-Ai#readme",
14
14
  "bugs": {
15
- "url": "https://github.com/Syrthax/project-k/issues"
15
+ "url": "https://github.com/Syrthax/Embark-Ai/issues"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/Syrthax/project-k.git"
19
+ "url": "git+https://github.com/Syrthax/Embark-Ai.git"
20
20
  },
21
21
  "license": "ISC",
22
22
  "author": "Sarthak G",
package/tui.js CHANGED
@@ -402,11 +402,12 @@ function renderActions() {
402
402
  `{gray-fg}─── navigation ───{/}`,
403
403
  `{cyan-fg}↑↓{/} scroll log`,
404
404
  `{cyan-fg}Tab{/} switch focus`,
405
- `{cyan-fg}r{/} refresh status`,
405
+ `{cyan-fg}r{/} refresh`,
406
+ `{cyan-fg}m{/} toggle selection`,
406
407
  `{cyan-fg}q{/} quit`,
407
408
  ``,
408
- `{gray-fg}Logs in:{/}`,
409
- `{gray-fg}${LOG_DIR}{/}`,
409
+ `{gray-fg}Logs: ${LOG_DIR}{/}`,
410
+ `{gray-fg}github.com/Syrthax/Embark-Ai{/}`,
410
411
  ]
411
412
  actionsPanel.setContent(lines.join('\n'))
412
413
  }
@@ -603,8 +604,8 @@ async function runOnboarding() {
603
604
  `{bold}{cyan-fg}Welcome to embark-ai{/}{/bold} {yellow-fg}[beta]{/}\n\n` +
604
605
  `Ember is an autonomous Minecraft agent powered by AI.\n` +
605
606
  `Let\'s set up your AI backend and verify your server.\n\n` +
606
- `{yellow-fg}⚠ This is a beta release — some features may be\n` +
607
- ` unstable. Please report issues on GitHub.{/}\n\n` +
607
+ `{yellow-fg}⚠ Beta release — some features may be unstable.\n` +
608
+ ` Issues: github.com/Syrthax/Embark-Ai/issues{/}\n\n` +
608
609
  `{gray-fg}This takes about 2 minutes on first run.{/}`,
609
610
  'cyan'
610
611
  )
@@ -1068,6 +1069,22 @@ screen.key(['r'], () => refresh())
1068
1069
  screen.key(['q', 'C-c'], quit)
1069
1070
  screen.key(['tab'], () => screen.focusNext())
1070
1071
 
1072
+ // [m] — toggle mouse capture so the terminal lets you select + copy text.
1073
+ // When selection mode is on, TUI mouse clicks are suspended.
1074
+ let _mouseEnabled = true
1075
+ screen.key(['m'], () => {
1076
+ if (_mouseEnabled) {
1077
+ screen.program.disableMouse()
1078
+ _mouseEnabled = false
1079
+ renderStatusBar('{yellow-fg}Selection mode — click & drag to copy. Press [m] to restore mouse.{/}')
1080
+ } else {
1081
+ screen.program.enableMouse()
1082
+ _mouseEnabled = true
1083
+ renderStatusBar(null)
1084
+ }
1085
+ screen.render()
1086
+ })
1087
+
1071
1088
  logPanel.key(['up','down','pageup','pagedown'], () => {})
1072
1089
  logPanel.focus()
1073
1090