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:
parent
844db1fd2b
commit
996f988f11
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "agenthub",
|
"name": "agenthub",
|
||||||
"version": "0.9.0",
|
"version": "0.9.1",
|
||||||
"description": "Local coordination layer for AI coding agents",
|
"description": "Local coordination layer for AI coding agents",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
@ -22,11 +22,14 @@ import { findProjectRoot } from '../../core/paths.js';
|
|||||||
export function hookContext(opts: { agent: string; role: string }): void {
|
export function hookContext(opts: { agent: string; role: string }): void {
|
||||||
const { agent, role } = opts;
|
const { agent, role } = opts;
|
||||||
process.stdout.write(
|
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` +
|
` agenthub work --agent ${agent} --role ${role}\n` +
|
||||||
`(or call the agenthub_work tool with { agent: "${agent}", role: "${role}" }), then LOOP: ` +
|
`(or call the agenthub_work tool with { agent: "${agent}", role: "${role}" }). It BLOCKS until a ` +
|
||||||
`work -> implement -> agenthub_task_review -> work. agenthub_work claims your tasks AND ` +
|
`task or message addressed to you arrives, then returns it. Handle it, then IMMEDIATELY call ` +
|
||||||
`surfaces your messages. Never run agenthub_task_done. Keep looping so you stay reachable.\n`,
|
`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`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -118,7 +118,7 @@ async function runRemote(serverUrl: string, fn: () => Promise<void>): Promise<vo
|
|||||||
export function createProgram(cwd: string): Command {
|
export function createProgram(cwd: string): Command {
|
||||||
const program = new Command('agenthub')
|
const program = new Command('agenthub')
|
||||||
.description('Local coordination layer for AI coding agents')
|
.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)');
|
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|||||||
@ -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.' };
|
if (messages.length) return { claimed: null, messages, note: 'No task addressed to you, but you have messages — reply with agenthub_message.' };
|
||||||
return null;
|
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();
|
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.`);
|
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);
|
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.',
|
server.tool('agenthub_task_list', 'List tasks, optionally filtered by status and/or role.',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user