Agent SDK
This SDK is behind the feature flag htpr-6115-agent-sdk, available by default only to your account.
The @hypertask/agent-sdk lets you build autonomous agents programmatically with a small TypeScript library that follows the same shape as Linear and Plane:
- Agent = app with a bot identity
- Run (AgentSession) = one session per interaction
- Typed activity stream = thought, action, response, ask, error
- Signals = stop, select, auth_request
- Fast acknowledgement = post a thought within 10 seconds or the run becomes stale after 5 minutes
This SDK is the client-side counterpart to your agent infrastructure: it handles webhooks, signature verification, and typed activity recording so you can stand up a working agent in an afternoon.
Install
Section titled “Install”npm install @hypertask/agent-sdk# oryarn add @hypertask/agent-sdkAdapters
Section titled “Adapters”The SDK includes adapters for common web frameworks and runtime environments. Use the webhook handler compatible with your stack.
| Adapter | Setup | Notes |
|---|---|---|
| Node | Assign a TCP port to your Node server. | Built-in; robust for single-process deployments. |
| Express | app.use(webhookHandler) on an Express app. | Classic middleware integrations. |
| Hono | app.use('*', webhookHandler) on a Hono app. | Lightweight web framework. |
| Next.js API route | Attach to the default export route. | Plugged into framework routing. |
| Cloudflare Workers | Enable automatic route at the Worker entry. | Background support required; deduplication and buffering handled by the SDK. |
| Plain Node | Route manually using raw request bytes and background support. | Fully dependency-free but more manual wiring. |
Quick Start
Section titled “Quick Start”Create a simple agent that replies when mentioned and files its responses as activity rows in the ticket thread.
import { createAgent } from '@hypertask/agent-sdk';import express from 'express';
async function main() { const app = express();
const { client, webhookHandler } = createAgent({ token: process.env.AGENT_TOKEN!, secret: process.env.WEBHOOK_SECRET!, });
// Attaches signature-verifying webhook handling to the Express app app.use(webhookHandler);
// Your agent logic wired into events client.on('mention', async (run) => { await run.thought(`Being mentioned on ticket ${run.ticket.id}`); await run.respond(`Noted: this is ticket ${run.ticket.id}.`); });
client.on('assigned', async (run) => { await run.thought(`Assigned to ticket ${run.ticket.id}`); });
app.listen(3000, () => { console.log('Agent running on port 3000'); });}
main().catch(console.error);For a complete example that works in under 30 minutes, check out the included reply-only template in the templates package:
import { createAgent } from '@hypertask/agent-sdk/templates/replyOnly';Run Objects
Section titled “Run Objects”Every event handler receives a run object that represents the current interaction. The run tracks the ticket, thread, status, and type-safe activity stream.
Status
Section titled “Status”A run has one of these statuses during its lifetime:
| Status | Description |
|---|---|
active | Running and processing events |
stopped | User requested stop or error occurred |
stale | No activity for 5 minutes after start |
done | Completed successfully |
Activity Stream
Section titled “Activity Stream”The run records all agent communication as typed activities in the thread, with muted styling like tool calls. Use these methods to write to the activity stream:
| Method | When to Use |
|---|---|
run.thought(text) | Internal reasoning or planning |
run.action(text, link) | Tool execution or file attachment |
run.respond(text) | Agent response to the user |
run.error(text) | Handling failures or errors |
run.ask(question, options) | UX flows that require user input |
All activity is persisted automatically when the SDK receives the mentioned or assigned event. You can also call activity methods on run directly.
run.thought(text)
Section titled “run.thought(text)”Record an internal reasoning step.
client.on('mention', async (run) => { await run.thought('Deciding which section to move the ticket to...');});run.action(text, link)
Section titled “run.action(text, link)”Record an external action executed by the agent, with an optional file upload link.
await run.action('Test run started', 'https://hypertask.app/file/test-run-123.md');run.respond(text)
Section titled “run.respond(text)”Record the agent's user-facing response.
await run.respond("I'll move it to the Bug Severity 2 section.");run.error(text)
Section titled “run.error(text)”Record an error that you handled gracefully.
try { // ...} catch (err) { await run.error('Failed to fetch from external API: ' + err.message);}run.ask(question, options)
Section titled “run.ask(question, options)”Create a multi-choice prompt for user interaction. The return value is a promise that resolves to the user's choice.
const choice = await run.ask('Which section should this go to?', { options: [ { value: 'urgent', label: 'Urgent Priority' }, { value: 'normal', label: 'Standard Priority' }, ],});
await run.respond(`Moving to ${choice.label}.`);Ticket Helpers
Section titled “Ticket Helpers”The run object gives you helper methods for the current ticket. These all include built-in lease management, so you don't need to call heartbeat manually.
| Helper | Description |
|---|---|
run.task.move(section) | Move the task to a different section |
run.task.assign(userId) | Assign the task to a user |
run.task.comment(text) | Add a comment to the ticket |
run.task.update(fields) | Update task fields |
run.task.attach(link) | Attach a file or link |
All helpers auto-heartbeat to keep your claim alive while work is in progress.
run.task.move(section)
Section titled “run.task.move(section)”Move the current task to a different board section.
await run.task.move('Bug Severity 2');run.task.assign(userId)
Section titled “run.task.assign(userId)”Assign the task to a user by ID, email, or display name.
await run.task.assign(' me ');await run.task.assign('john@example.com');run.task.comment(text)
Section titled “run.task.comment(text)”Add a comment to the task, which also appears in the thread.
await run.task.comment('Routing this through to the triage team.');run.task.update(fields)
Section titled “run.task.update(fields)”Update any task fields by writeable property name.
await run.task.update({ description: 'Updated based on the agent analysis...', priority: 'High',});run.task.attach(link)
Section titled “run.task.attach(link)”Attach a file or external link to the task.
await run.task.attach('https://hypertask.app/file/analysis-123.md');Event Types
Section titled “Event Types”Your agent can subscribe to one or more incoming events. All events provide a run object with ticket, thread, and helpers.
type EventHandler = (run: Run) => Promise<void>;
// Subscribe to all event typesclient.on('*', handler);
// Subscribe to specific eventsclient.on('mention', mentionHandler);client.on('assigned', assignedHandler);client.on('chat', chatHandler);client.on('stop', stopHandler);mention
Section titled “mention”Triggered when the agent is @mentioned in any comment or chat. Includes the full thread so far, the original prompt, and all existing activity.
client.on('mention', async (run) => { // run.ticket: the ticket being mentioned // run.thread: messages before this event // run.prompt: the current task description or additional context});assigned
Section titled “assigned”Triggered when the agent is assigned directly to a ticket via the interface.
client.on('assigned', async (run) => { // run.ticket: the newly assigned ticket});Triggered when a message is sent in the Agent Chat. Useful for bots that respond in chat rather than on tickets.
client.on('chat', async (run) => { // run.ticket: the task chat belongs to // run.thread: all messages so far});Triggered when the agent's run is explicitly stopped by the user (e.g., via the agent control panel or chat interface). Use this for cleanup actions or confirmation posts.
client.on('stop', async (run) => { await run.respond('Stopping work. Let me know if you need more.');});Signals
Section titled “Signals”The SDK automatically emits signals that your agents can respond to. These are part of the run type and include request-and-response forms.
Users can request the agent to halt (via the agent control panel or chat). A stop signal clears the lease and stops further activity.
client.on('stop', async (run) => { await run.respond('Stopped. No further actions.');});select
Section titled “select”One-shot user choice prompts. Use run.ask(...) explicitly to get a promise-based choice, which renders as a multi-selector in the thread.
const choice = await run.ask(...);auth_request
Section titled “auth_request”Requests for additional authentication (e.g., missing OAuth tokens). Handled through your custom auth logic when the SDK can't automatically authenticate.
Templates
Section titled “Templates”The SDK includes three ready-made templates you can use as starting points. All templates are available in the @hypertask/agent-sdk/templates package.
Reply-Only Assistant
Section titled “Reply-Only Assistant”A minimal agent that only responds to mentions and chat, without touching tasks. Perfect for FAQs and bots that offer information but never modify state.
import { createAgent } from '@hypertask/agent-sdk/templates/replyOnly';import { Request, Response } from 'express';
export async function createReplyOnly( token: string, secret: string, onRespond: (text: string) => void): Promise<{ app: Express.Application }> { const { client, webhookHandler } = createAgent({ token, secret }); const app = express();
app.use(webhookHandler);
client.on('mention', async (run) => { const response = await getBotResponse(run.prompt); onRespond(response); await run.respond(response); });
client.on('chat', async (run) => { const response = await getBotResponse(run.prompt); onRespond(response); await run.respond(response); });
return { app };}Ticket Worker
Section titled “Ticket Worker”An agent that receives assigned tickets, updates progress, and files accurate activity. Works well for triage bots, status reporters, and project workflow helpers.
import { createAgent } from '@hypertask/agent-sdk/templates/ticketWorker';import { Request, Response } from 'express';
export async function createTicketWorker( token: string, secret: string, onAnalyze: (ticket: any) => Promise<string>): Promise<{ app: Express.Application }> { const { client, webhookHandler } = createAgent({ token, secret }); const app = express();
app.use(webhookHandler);
client.on('assigned', async (run) => { await run.thought(`Analyzing ticket ${run.ticket.id}`);
const summary = await onAnalyze(run.ticket); await run.task.comment(summary);
await run.thought('Completed analysis'); await run.respond('Analysis complete. See comment for details.'); });
return { app };}Scheduled Bot
Section titled “Scheduled Bot”A bot that runs only on a cron schedule, without webhooks. Good for summaries, reports, or regular monitoring agents.
import { createAgent } from '@hypertask/agent-sdk/templates/scheduledBot';import { MemoryDeliveryScheduler } from '@hypertask/agent-sdk';
export async function createScheduledBot( token: string, secret: string, onTick: (run: Run) => Promise<void>): Promise<{ app: Express.Application; scheduler: MemoryDeliveryScheduler }> { const { client, webhookHandler } = createAgent({ token, secret }); const scheduler = new MemoryDeliveryScheduler();
const app = express();
app.use(webhookHandler);
if (scheduler) { await scheduler.schedule('0 */6 * * *', async () => { const run = await client.createRun(COMMENT_MENTION); await run.thought('Scheduled bot tick'); await onTick(run); }); }
return { app, scheduler };}Features
Section titled “Features”- Signature verification — The SDK validates all incoming webhooks with HMAC signatures so you don't need to.
- Fast acknowledgement — Always post a thought within 10 seconds after receiving
mentionorassigned. If the run goes stale after 5 minutes, it automatically stops. - Retry-safe delivery IDs — The SDK supports record-and-retry workflows so you can handle webhook delivery without duplicating actions.
- Typed activity stream — All activity (thought, action, response, error, ask) is recorded in the thread as muted rows that look like tool calls, making the agent's reasoning visible.
- Built-in lease management — All task helpers include heartbeat logic, so your agent never loses its claim to a ticket.
- HTTP transport flexibility — The SDK generates a webhook handler compatible with Express, Hono, Next.js API routes, Cloudflare Workers, and plain Node.js.
Agent Lifecycle
Section titled “Agent Lifecycle”- Create the SDK client — Call
createAgent({ token, secret }). The SDK wraps your existing routes and injects signature verification. - Register event handlers — Subscribe to
mention,assigned,chat, andstopas needed. - Handle events with a run — Each handler receives a
runobject with ticket, thread, and helpers. - Record activities — Use
run.thought,run.action,run.respond,run.error,run.askto build the typed activity stream. - Update tickets with helpers — Use
run.task.move,assign,comment,update,attachto modify tickets without managing leases manually.
The SDK automatically:
- Starts a new run on
mentionorassigned - Checks the signature on every webhook
- Bounces the run if no activity occurs for 5 minutes
- Records all activity in the thread when the SDK receives the webhook
Next Steps
Section titled “Next Steps”| If you want to… | See |
|---|---|
| Configure webhooks | Agent webhooks |
| Run agents on a schedule | Schedule an agent |
| Define patterns for multi-step workflows | Agent Workflows |
| Learn about the infrastructure powering runs | Agents Dashboard |
Known Limitations
Section titled “Known Limitations”For v1, the following are out of scope:
- OAuth marketplace install (create your agent using existing tokens)
- Python SDK
- Hosted runtime for client agents