feat(autostart): self-perpetuating work loop — stay reachable

The recurring "agent doesn't read messages in realtime / isn't triggered for the
next task" problem: agents weren't STAYING in the agenthub_work loop, so they went
dormant after each task. Two reinforcements:

- agenthub_work now ends every response with a LOOP directive: "the instant you
  finish, call agenthub_work again (in the background) so the next task/message
  reaches you in realtime — never end your turn without relaunching it."
- The auto-start hook-context now stresses the BACKGROUND loop + relaunch pattern,
  so a Claude Code session is woken on each event instead of blocking.

Layer 1 (auto-start) gets agents INTO the loop; this keeps them IN it. Honest
limit: turn-based agents still rely on obeying the relaunch nudge — there's no
100% always-on without a supervisor. Bump 0.9.0 -> 0.9.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-06-29 05:23:05 +02:00
parent 844db1fd2b
commit 996f988f11
4 changed files with 18 additions and 8 deletions

View File

@ -1,6 +1,6 @@
{
"name": "agenthub",
"version": "0.9.0",
"version": "0.9.1",
"description": "Local coordination layer for AI coding agents",
"type": "module",
"main": "./dist/index.js",

View File

@ -22,11 +22,14 @@ import { findProjectRoot } from '../../core/paths.js';
export function hookContext(opts: { agent: string; role: string }): void {
const { agent, role } = opts;
process.stdout.write(
`AgentHub auto-start — you are "${agent}" (${role}). Before anything else, run:\n` +
`AgentHub auto-start — you are "${agent}" (${role}). Before anything else, enter the work loop:\n` +
` agenthub work --agent ${agent} --role ${role}\n` +
`(or call the agenthub_work tool with { agent: "${agent}", role: "${role}" }), then LOOP: ` +
`work -> implement -> agenthub_task_review -> work. agenthub_work claims your tasks AND ` +
`surfaces your messages. Never run agenthub_task_done. Keep looping so you stay reachable.\n`,
`(or call the agenthub_work tool with { agent: "${agent}", role: "${role}" }). It BLOCKS until a ` +
`task or message addressed to you arrives, then returns it. Handle it, then IMMEDIATELY call ` +
`agenthub_work AGAIN — run it in the BACKGROUND so you're woken on the next event without ` +
`tying up your turn. The loop is: work -> handle -> agenthub_task_review -> work, forever. ` +
`agenthub_work claims your tasks AND delivers your messages. Never run agenthub_task_done. ` +
`Staying in this loop is what keeps you reachable — do not end a turn without relaunching agenthub_work.\n`,
);
}

View File

@ -118,7 +118,7 @@ async function runRemote(serverUrl: string, fn: () => Promise<void>): Promise<vo
export function createProgram(cwd: string): Command {
const program = new Command('agenthub')
.description('Local coordination layer for AI coding agents')
.version('0.9.0')
.version('0.9.1')
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
program

View File

@ -135,11 +135,18 @@ export async function startMcpServer(cwd: string): Promise<void> {
if (messages.length) return { claimed: null, messages, note: 'No task addressed to you, but you have messages — reply with agenthub_message.' };
return null;
};
// Self-perpetuating loop: every response reminds the agent to relaunch
// agenthub_work, so a finished task/message never leaves it dormant.
const LOOP =
'STAY REACHABLE: the instant you finish handling this, call agenthub_work again — ' +
'run it in the background so the next task or message reaches you in realtime. ' +
'Never end your turn without relaunching agenthub_work.';
const immediate = await findWork();
if (immediate) return asText(immediate);
if (immediate) return asText({ ...immediate, loop: LOOP });
if (!remote) return asText(`No open task addressed to ${agent}, and no hub server to wait on.`);
const result = await waitForTask(serverUrl!, findWork, timeoutSec ?? 300);
return asText(result ?? `No task or message for ${agent} within ${timeoutSec ?? 300}s — call agenthub_work again.`);
if (result) return asText({ ...result, loop: LOOP });
return asText(`No task or message for ${agent} within ${timeoutSec ?? 300}s. ${LOOP}`);
});
server.tool('agenthub_task_list', 'List tasks, optionally filtered by status and/or role.',