@hypercubed/handoff
Agent handoff queue via storage — any agent to any agent, use your own harness name
AGENTS.md
168 lines · 7.2 KB · Markdown@hypercubed/handoff
Imports
import { createTask, claimTask, completeTask, listTasks, getTask, deleteTask } from 'kody:@hypercubed/handoff'Smoke tests
import { createTask, listTasks, getTask, deleteTask } from 'kody:@hypercubed/handoff'
const t = await createTask({ title: "smoke", prompt: "reply ok", createdBy: "smoke" })
const got = await getTask({ taskId: t.id })
const pending = await listTasks({ status: "pending", limit: 5 })
await deleteTask({ taskId: t.id })Handoff Prompt Playbook
Worker has zero context. Every prompt passed to createTask must be self-contained.
Structure every prompt as:
- Goal — 1 verb + object + where (exact file paths). Bad: "Fix lint." Good: "Fix no-unused-vars in
src/create-task.ts:42." - Context — session summary, pasted code (not paraphrase), tech stack, file inventory the worker will touch.
- Do / Do-not-touch — numbered steps, allowed tools/components, explicit off-limits files, systems, or data.
- Done + Proof — checkable done-criteria plus required evidence (test output, URL, diff). State the
completeTaskresult string to return. - Kody routing —
createdBy= your harness name (never an example name),forAgent= target harness or omit for anyone. Worker flow is claim → execute prompt → complete.
Good example:
await createTask({
title: "Fix lint",
prompt: "Goal: fix lint in packages/handoff. Context: TS package, npm run lint fails on no-unused-vars in src/create-task.ts:42. Do: edit only src/create-task.ts, run npm run lint. Do-not-touch: src/claim-task.ts, package.json. Done when npm run lint passes. Return changed files via completeTask.",
createdBy: "my-harness",
forAgent: "worker-harness"
})Bad example: createTask({ title: "Fix lint", prompt: "Run docs-lint and fix wiring" }) — no paths, no done-check, no proof.
Session Identity
On every create or claim, the package mints a unique session id and combines it with your optional fixed root name.
- Send
createdBy/agentIdas a bare name (e.g.my-agent) → the response includessessionName: "my-agent:<sid>". - Omit the name → the response includes
sessionName: ":<sid>"(session-only identity). - Send an already-qualified name (e.g.
my-agent:hdJs) → it is used as-is (no double-attach).
The agent must remember the returned sessionName and send it on all future calls to handoff.
Bootstrap instruction (copy to each harness's system prompt)
Your handoff root name is <your-name> (e.g. my-agent, your-prime). It is fixed — never change it.
On your first create or claim call, send createdBy/agentId: "<your-name>".
The package returns sessionName: "<your-name>:<sid>". Remember that exact string
for the rest of this session and always send it as createdBy/agentId/forAgent.Bare name vs fully-qualified vs session-only
listTasks({ forAgent: "my-agent" })→ all tasks for that harness across all sessions.listTasks({ forAgent: "my-agent:hdJs" })→ tasks for exactly that session.listTasks({ forAgent: ":hdJs" })→ tasks for that session id regardless of harness.claimTask({ forAgent: "my-agent", agentId: "my-agent:hdJs" })→ claims next pending task for that harness, stamped with the session id.
Prose → Calls
All use Via Kody @hypercubed/handoff — this is a Kody package at kody.codes/@hypercubed/handoff, not npm — so agents search Kody instead of trying npx. Replace bracketed values; my-agent/your-prime are placeholders, never copy them.
Create a task (any agent delegates):
Via Kody @hypercubed/handoff — create task "[title]" as <my-name>: [prompt]→ createTask. Harness name optional; omit → session-only identity (:<sid>).
Create a targeted task (for a specific agent):
Via Kody @hypercubed/handoff — create task "[title]" for <target-name> as <my-name>: [prompt]→ createTask with forAgent. Omit for <target-name> for untargeted.
Claim by id (deterministic, recommended when queue >1):
Via Kody @hypercubed/handoff — claim task [id] as <my-name>→ claimTask({ taskId })
Claim next pending (convenient when queue depth =1):
Via Kody @hypercubed/handoff — claim next pending task as <my-name>→ claimTask({ agentId })
Claim by name (requires unique title):
Via Kody @hypercubed/handoff — claim task named "[title]" as <my-name>→ claimTask({ title }). Title not unique — newest pending match wins. Prefer id.
Claim by creator:
Via Kody @hypercubed/handoff — claim next task created by <my-name> as <my-name>→ claimTask({ createdBy }). Combine with title when needed.
Claim for me:
Via Kody @hypercubed/handoff — claim next task for <target-name> as <my-name>→ claimTask({ forAgent }). Claims oldest pending where forAgent matches.
Close a task (worker after executing prompt):
Via Kody @hypercubed/handoff — complete task [id] with "[result]"→ completeTask
Delete a task (manual cleanup):
Via Kody @hypercubed/handoff — delete task [id]→ deleteTask
Inspect queue:
Via Kody @hypercubed/handoff — list pending tasksor list last 5 tasks or list pending tasks for <my-name> → listTasks / getTask
If you omit as <my-name>, create and claim default to unknown — pass your harness name when known.
Example Flow — Atlas delegates to Bolt
Atlas (e.g. HarnessA) delegates to Bolt (e.g. HarnessB):
- You → Atlas:
Via Kody @hypercubed/handoff — create task "Fix docs-lint" as HarnessA: Just say hello and write 5768→createTask, returnsid - You → Bolt:
Via Kody @hypercubed/handoff — claim task <id> as Bolt→claimTask, receives prompt - Bolt executes: does the work from the prompt
- You → Bolt:
Via Kody @hypercubed/handoff — complete task <id> with "hello 5768"→completeTask - Verify:
Via Kody @hypercubed/handoff — list completed tasks→ both see same entry
No code pasted, no npx, no shared files — only Via Kody prose. Create stores the prompt; it does not execute it.
Exports
createTask— store prompt for worker (does NOT execute) — accepts optionalforAgent; prunes old completed on each createclaimTask— reserve pending task — acceptsforAgentto claim next targeted at mecompleteTask— mark completed/failed with resultlistTasks— newest-first, optionalstatusandforAgentfiltergetTask— fetch single task by iddeleteTask— delete any task by id
Invoke via packages.invoke from any host — packageStorage is per-account, so all your agents see the same queue.
Retention
Completed and failed age-off on each create: older than 7 days or beyond newest 100 are deleted. Pending/claimed never age-off. Use delete prose to remove manually.
Edge cases
titleis not unique —claimTaskby title returns newest pending match. Prefer id.forAgentmatching supports bare (agent), qualified (agent:sid), and session-only (:sid) forms.- Pending/claimed never age-off. Completed/failed prune to newest 100 / 7 days on each create.
- Omit
createdBy/agentId→ session-only identity (:<sid>). Always send your harness name when known.