neohive 6.4.0 → 6.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neohive",
3
- "version": "6.4.0",
3
+ "version": "6.4.2",
4
4
  "description": "The MCP collaboration layer for AI CLI tools. Turn Claude Code, Gemini CLI, and Codex CLI into a team.",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -24,6 +24,7 @@
24
24
  "design-system.css",
25
25
  "design-system.html",
26
26
  "cli.js",
27
+ "neohive-plugin/",
27
28
  "lib/",
28
29
  "tools/",
29
30
  "templates/",
package/server.js CHANGED
@@ -1183,7 +1183,7 @@ function buildGuide(level = 'standard') {
1183
1183
  } else {
1184
1184
  rules.push('ROLE: Managed agent. The manager controls your turn.');
1185
1185
  rules.push('LOOP: listen() → receive work → update_task(id, "in_progress") → do work → update_task(id, "done") → send_message(manager, summary) → listen(). Never stop.');
1186
- rules.push('Never call get_work() or check_messages() in managed mode.');
1186
+ rules.push('Never call get_work() or messages() in managed mode.');
1187
1187
  }
1188
1188
  rules.push('Keep messages short (2-3 paragraphs). Report what you did and what files changed.');
1189
1189
  }
@@ -1260,16 +1260,16 @@ function buildGuide(level = 'standard') {
1260
1260
  if (isLeadRole) {
1261
1261
  const coordinatorMode = getConfig().coordinator_mode || 'responsive';
1262
1262
  if (coordinatorMode === 'responsive') {
1263
- rules.push('COORDINATOR: Use consume_messages() to check updates non-blockingly. Do NOT block in listen() — stay responsive to the user.');
1263
+ rules.push('COORDINATOR: Use messages(action="check") to check updates non-blockingly. Do NOT block in listen() — stay responsive to the user.');
1264
1264
  } else {
1265
1265
  rules.push('COORDINATOR: Use listen() to wait for agent results. Only return to human when all tasks are done or blocked.');
1266
1266
  }
1267
1267
  rules.push('Coordinators do NOT edit files or write code. Delegate ALL code work to other agents.');
1268
1268
  }
1269
1269
 
1270
- const listenCmd = isManagedMode() ? 'listen()' : (mode === 'group' ? 'listen_group()' : 'listen()');
1270
+ const listenCmd = isManagedMode() ? 'listen()' : (mode === 'group' ? 'listen(mode="group")' : 'listen()');
1271
1271
  if (!isLeadRole) {
1272
- rules.push(`After EVERY action, call ${listenCmd}. Never use sleep() or poll with check_messages().`);
1272
+ rules.push(`After EVERY action, call ${listenCmd}. Never use sleep() or poll with messages().`);
1273
1273
  }
1274
1274
 
1275
1275
  if (level === 'minimal') {
@@ -1570,7 +1570,7 @@ function toolRegister(name, provider = null, skills = null) {
1570
1570
  const coordinatorMode = getConfig().coordinator_mode || 'responsive';
1571
1571
  nextAction = coordinatorMode === 'autonomous'
1572
1572
  ? 'Call get_briefing() to load project context, then listen() to coordinate your team.'
1573
- : 'Call get_briefing() to load project context, then consume_messages() to check for pending work.';
1573
+ : 'Call get_briefing() to load project context, then messages(action="check") to check for pending work.';
1574
1574
  }
1575
1575
 
1576
1576
  // --- Build the result: next_action FIRST, then context ---
@@ -1695,7 +1695,7 @@ async function toolSendMessage(content, to = null, reply_to = null, channel = nu
1695
1695
  const effectiveSendLimit = isAutonomousMode() ? 5 : sendLimit;
1696
1696
  const myRole = (getProfiles()[registeredName] || {}).role;
1697
1697
  if (isGroupMode() && sendsSinceLastListen >= effectiveSendLimit && myRole !== 'Coordinator') {
1698
- return { error: `You must call listen_group() before sending again. You've sent ${sendsSinceLastListen} message(s) without listening (limit: ${effectiveSendLimit}). This prevents message storms.` };
1698
+ return { error: `You must call listen() before sending again. You've sent ${sendsSinceLastListen} message(s) without listening (limit: ${effectiveSendLimit}). This prevents message storms.` };
1699
1699
  }
1700
1700
 
1701
1701
  // Response budget: track unaddressed sends, hint when depleted
@@ -1993,7 +1993,7 @@ async function toolSendMessage(content, to = null, reply_to = null, channel = nu
1993
1993
  if (!recipientAlive) {
1994
1994
  result.warning = `Agent "${to}" appears offline (PID not running). Message queued but may not be received until they reconnect.`;
1995
1995
  } else if (to !== '__user__' && agents[to] && !agents[to].listening_since) {
1996
- result.note = `Agent "${to}" is currently working (not in listen mode). Message queued — they'll see it when they finish their current task and call listen_group().`;
1996
+ result.note = `Agent "${to}" is currently working (not in listen mode). Message queued — they'll see it when they finish their current task and call listen().`;
1997
1997
  }
1998
1998
 
1999
1999
  // Coordinator enforcement: warn if sending work assignment without creating a task first
@@ -2036,7 +2036,7 @@ function toolBroadcast(content) {
2036
2036
  const effectiveSendLimitBcast = isAutonomousMode() ? 5 : sendLimit;
2037
2037
  const myRole = (getProfiles()[registeredName] || {}).role;
2038
2038
  if (isGroupMode() && sendsSinceLastListen >= effectiveSendLimitBcast && myRole !== 'Coordinator') {
2039
- return { error: `You must call listen_group() before broadcasting again. You've sent ${sendsSinceLastListen} message(s) without listening (limit: ${effectiveSendLimitBcast}).` };
2039
+ return { error: `You must call listen() before broadcasting again. You've sent ${sendsSinceLastListen} message(s) without listening (limit: ${effectiveSendLimitBcast}).` };
2040
2040
  }
2041
2041
 
2042
2042
  const rateErr = checkRateLimit(content, '__broadcast__');
@@ -2355,10 +2355,10 @@ async function toolListenCodex(from = null, outcome = null, task_id = null, summ
2355
2355
  const taskList = getTasks();
2356
2356
  const task = taskList.find(t => t.id === task_id);
2357
2357
  if (!task) {
2358
- return { error: true, message: `Invalid task_id "${task_id}" — task does not exist. Check list_tasks() and call listen_codex() again with the correct task_id.` };
2358
+ return { error: true, message: `Invalid task_id "${task_id}" — task does not exist. Check list_tasks() and call listen(mode="codex") again with the correct task_id.` };
2359
2359
  }
2360
2360
  if (task.assignee && task.assignee !== registeredName) {
2361
- return { error: true, message: `Task "${task_id}" is assigned to ${task.assignee}, not to you (${registeredName}). You cannot update another agent's task via listen_codex().` };
2361
+ return { error: true, message: `Task "${task_id}" is assigned to ${task.assignee}, not to you (${registeredName}). You cannot update another agent's task via listen(mode="codex").` };
2362
2362
  }
2363
2363
  const statusMap = { completed: 'done', blocked: 'blocked', failed: 'blocked_permanent' };
2364
2364
  const newStatus = statusMap[outcome];
@@ -2486,9 +2486,9 @@ function toolSetConversationMode(mode) {
2486
2486
  }
2487
2487
 
2488
2488
  const messages = {
2489
- group: 'Group mode enabled. Use listen_group() to receive batched messages. All messages are shared with everyone.',
2489
+ group: 'Group mode enabled. Use listen(mode="group") to receive batched messages. All messages are shared with everyone.',
2490
2490
  direct: 'Direct mode enabled. Use listen() for point-to-point messaging.',
2491
- managed: 'Managed mode enabled. Call claim_manager() to become the manager, or wait for the manager to give you the floor via yield_floor(). Use listen() or listen_group() to receive messages.',
2491
+ managed: 'Managed mode enabled. Call claim_manager() to become the manager, or wait for the manager to give you the floor via yield_floor(). Use listen() to receive messages.',
2492
2492
  };
2493
2493
  return { success: true, mode, message: messages[mode] };
2494
2494
  }
@@ -2649,10 +2649,10 @@ async function toolListenGroup(outcome = null, task_id = null, summary = null) {
2649
2649
  const taskList = getTasks();
2650
2650
  const task = taskList.find(t => t.id === task_id);
2651
2651
  if (!task) {
2652
- return { error: true, message: `Invalid task_id "${task_id}" — task does not exist. Check list_tasks() and call listen_group() again with the correct task_id.` };
2652
+ return { error: true, message: `Invalid task_id "${task_id}" — task does not exist. Check list_tasks() and call listen() again with the correct task_id.` };
2653
2653
  }
2654
2654
  if (task.assignee && task.assignee !== registeredName) {
2655
- return { error: true, message: `Task "${task_id}" is assigned to ${task.assignee}, not to you (${registeredName}). You cannot update another agent's task via listen_group().` };
2655
+ return { error: true, message: `Task "${task_id}" is assigned to ${task.assignee}, not to you (${registeredName}). You cannot update another agent's task via listen().` };
2656
2656
  }
2657
2657
  const statusMap = { completed: 'done', blocked: 'blocked', failed: 'blocked_permanent' };
2658
2658
  const newStatus = statusMap[outcome];
@@ -2906,8 +2906,8 @@ function classifyPriority(msg) {
2906
2906
  return 'normal';
2907
2907
  }
2908
2908
 
2909
- // Build the response for listen_group — kept lean to reduce context accumulation
2910
- // Context/history removed: agents should call get_history() when they need it
2909
+ // Build the response for listen (group mode) — kept lean to reduce context accumulation
2910
+ // Context/history removed: agents should call messages(action="history") when they need it
2911
2911
  function buildListenGroupResponse(batch, consumed, agentName, listenStart) {
2912
2912
  saveConsumedIds(agentName, consumed);
2913
2913
  touchActivity();
@@ -5465,7 +5465,7 @@ function toolStartPlan(params) {
5465
5465
  broadcastSystemMessage(
5466
5466
  `[PLAN LAUNCHED] "${name}" — ${steps.length} steps, autonomous mode, ${useParallel ? 'parallel' : 'sequential'}. ` +
5467
5467
  `${startedSteps.length} step(s) started. ` +
5468
- `All agents: call get_work() to enter the autonomous work loop. Do NOT call listen_group().`
5468
+ `All agents: call get_work() to enter the autonomous work loop. Do NOT call listen().`
5469
5469
  );
5470
5470
 
5471
5471
  touchActivity();
@@ -5886,7 +5886,7 @@ function triggerStandupIfDue() {
5886
5886
  if (inProgress.length > 0) summary += ` In progress: ${inProgress.map(t => `"${t.title}" (${t.assignee || '?'})`).join(', ')}.`;
5887
5887
  if (blocked.length > 0) summary += ` BLOCKED: ${blocked.map(t => `"${t.title}" (${t.assignee || '?'})`).join(', ')}.`;
5888
5888
  if (recentDone.length > 0) summary += ` Recently done: ${recentDone.length} task(s).`;
5889
- summary += ' Each agent: report what you did, what\'s blocked, what\'s next. Then call listen_group().';
5889
+ summary += ' Each agent: report what you did, what\'s blocked, what\'s next. Then call listen().';
5890
5890
 
5891
5891
  broadcastSystemMessage(summary, registeredName);
5892
5892
  } catch (e) { log.warn("standup trigger failed:", e.message); }
@@ -6180,7 +6180,7 @@ function toolGetGuide(level = 'standard') {
6180
6180
  const guide = buildGuide(level);
6181
6181
  guide.your_name = registeredName;
6182
6182
  if (level !== 'minimal') {
6183
- guide.workflow = '1. get_briefing → 2. list_tasks/suggest_task → 3. claim task → 4. lock_file → 5. work → 6. unlock_file → 7. update_task done → 8. listen_group';
6183
+ guide.workflow = '1. get_briefing → 2. list_tasks/suggest_task → 3. claim task → 4. lock_file → 5. work → 6. unlock_file → 7. update_task done → 8. listen()';
6184
6184
  }
6185
6185
  return guide;
6186
6186
  }
@@ -6498,7 +6498,7 @@ function toolSubmitReview(reviewId, status, feedback) {
6498
6498
 
6499
6499
  review.status = status;
6500
6500
  review.reviewer = registeredName;
6501
- review.feedback = (feedback || '').substring(0, 2000);
6501
+ review.feedback = feedback || '';
6502
6502
  review.reviewed_at = new Date().toISOString();
6503
6503
 
6504
6504
  // Review → retry loop: track review rounds, auto-route feedback, auto-approve after 2 rounds
@@ -7191,7 +7191,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
7191
7191
  tools: [
7192
7192
  {
7193
7193
  name: 'register',
7194
- description: 'Register this agent\'s identity. Must be called first. Returns a collaboration guide with all tool categories, critical rules, and workflow patterns — READ IT CAREFULLY before doing anything else. Then call get_briefing() for project context, then listen_group() to join the conversation.',
7194
+ description: 'Register this agent\'s identity. Must be called first. Returns a collaboration guide with all tool categories, critical rules, and workflow patterns — READ IT CAREFULLY before doing anything else. Then call get_briefing() for project context, then listen() to join the conversation.',
7195
7195
  inputSchema: {
7196
7196
  type: 'object',
7197
7197
  properties: {
@@ -7289,7 +7289,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
7289
7289
  },
7290
7290
  {
7291
7291
  name: 'listen',
7292
- description: 'Listen for messages. Use mode="standard" (default, direct 1:1), mode="group" (group/managed conversation, batched), or mode="codex" (Codex CLI — returns after 90s). Auto-detects mode from conversation state when mode is omitted. Replaces listen_group and listen_codex (now deprecated aliases).',
7292
+ description: 'Listen for messages. Use mode="standard" (default, direct 1:1), mode="group" (group/managed conversation, batched), or mode="codex" (Codex CLI — returns after 90s). Auto-detects mode from conversation state when mode is omitted.',
7293
7293
  inputSchema: {
7294
7294
  type: 'object',
7295
7295
  properties: {
@@ -7901,7 +7901,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7901
7901
  try {
7902
7902
  const pending = getUnconsumedMessages(registeredName);
7903
7903
  const pendingHint = pending.length > 0
7904
- ? `${pending.length} agent update(s) waiting. Call consume_messages() to read them.`
7904
+ ? `${pending.length} agent update(s) waiting. Call messages(action="consume") to read them.`
7905
7905
  : null;
7906
7906
  if (!na || bareListenRe.test(na)) {
7907
7907
  // No guidance or bare listen() — replace with coordinator hint or nothing