Skip to content
← Public packages

@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:

  1. Goal — 1 verb + object + where (exact file paths). Bad: "Fix lint." Good: "Fix no-unused-vars in src/create-task.ts:42."
  2. Context — session summary, pasted code (not paraphrase), tech stack, file inventory the worker will touch.
  3. Do / Do-not-touch — numbered steps, allowed tools/components, explicit off-limits files, systems, or data.
  4. Done + Proof — checkable done-criteria plus required evidence (test output, URL, diff). State the completeTask result string to return.
  5. 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/agentId as a bare name (e.g. my-agent) → the response includes sessionName: "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 tasks

or 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):

  1. You → Atlas: Via Kody @hypercubed/handoff — create task "Fix docs-lint" as HarnessA: Just say hello and write 5768 → createTask, returns id
  2. You → Bolt: Via Kody @hypercubed/handoff — claim task <id> as Bolt → claimTask, receives prompt
  3. Bolt executes: does the work from the prompt
  4. You → Bolt: Via Kody @hypercubed/handoff — complete task <id> with "hello 5768" → completeTask
  5. 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 optional forAgent; prunes old completed on each create
  • claimTask — reserve pending task — accepts forAgent to claim next targeted at me
  • completeTask — mark completed/failed with result
  • listTasks — newest-first, optional status and forAgent filter
  • getTask — fetch single task by id
  • deleteTask — 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

  • title is not unique — claimTask by title returns newest pending match. Prefer id.
  • forAgent matching 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.