Agents

Proactive workers that watch the systems your team already uses, decide when action is needed, and report back.

Relay agents are your team of proactive agents. Built on top of file they can react to any signal and take action. That signal can be any third party event, a schedule, or a message from another agent.

A relay agent is specialized and focused agent that has a task or a few small tasks and that it does very well. They are completely customizable and live in your codebase and deployable by a simple CLI command or a one click deploy on the web. You can deploy several relay agents and each agent on the relay can communicate with each other enabling as complex of workflows as you want with full traceability and control in straightforward and concise TypeScript code.

A whole agent

This is granola-prospect. A meeting recording syncs in; it decides whether the call contained a real feature ask, files a Linear issue, and hands the implementation to a coding agent that opens the PR.

The persona declares what it can reach:

export default definePersona({
  id: 'granola-prospect',
  description: 'Detects prospect calls, files a Linear issue with the ask, and opens a PR implementing it.',
  cloud: true,
  useSubscription: true,
  integrations: {
    granola: {},
    linear: { scope: { issues: '/linear/issues/**', teams: '/linear/teams/**' } },
    github: { scope: { repo: 'your-org/your-repo' } }
  },
  harness: 'claude',
  model: 'claude-sonnet-4-6',
  systemPrompt: 'Turn prospect asks from meeting transcripts into a Linear issue and a small implementing PR.',
  harnessSettings: { reasoning: 'high', timeoutSeconds: 1800, sandboxMode: 'workspace-write' },
  onEvent: './agent.ts'
});

And the handler decides what to do:

export default defineAgent({
  triggers: { granola: [{ on: 'file.created' }] },
  handler: async (ctx, event) => {
    const notePath = readNotePath((await event.expand('full')).data);
    const transcript = await readNote(ctx, notePath);
    if (!transcript) return;

    // The judgment call: was this a prospect asking for something?
    const ask = await classify(ctx, transcript);
    if (!ask.isProspect) return;

    const linear = relayClient('linear');
    const issue = await linear.write('issues', {}, {
      teamId: await resolveTeamId(ctx),
      title: ask.title,
      description: ask.summary
    });

    // Hand the actual work to a coding agent. The repo is already checked out.
    const run = await ctx.harness.run({
      cwd: ctx.sandbox.cwd,
      prompt: `A prospect asked for the following. Implement it, then open a pull request.
        Put the PR URL on the last line.\n\n${ask.summary}`
    });

    const prUrl = run.output.match(/https?:\/\/\S*\/pull\/\d+/g)?.pop();
    if (prUrl) {
      await linear.write('comments', { issueId: issue.receipt?.id }, { body: `:rocket: ${prUrl}` });
    }
  }
});

classify is a model call, not a rule:

const ask = await ctx.llm.complete(`
  Read this meeting transcript. Decide if it is a sales/prospect call where the
  prospect asked for a feature or change. Reply with JSON only:
  {"isProspect": boolean, "title": "short issue title", "summary": "what they asked for"}

  ${transcript.slice(0, 8000)}
`, { maxTokens: 400 });

The agent makes decisions based on the inputs and allows the code to lean but accomplish complicated functionality.

What an agent needs

  • A trigger: a schedule, webhook, file event, message, or manual launch.
  • Context: the channel, thread, file path, issue, PR, or provider record that explains the task.
  • A policy: what the agent may read, write, spend, and deploy.
  • A reporting path: where the agent posts progress, findings, and handoffs.

Start here

Browse the open-source agent gallery.