Package Exports
- @flue/runtime
- @flue/runtime/app
- @flue/runtime/client
- @flue/runtime/cloudflare
- @flue/runtime/internal
- @flue/runtime/node
- @flue/runtime/sandbox
Readme
Experimental — Flue is under active development. APIs may change.
Looking for
v0.0.x? See here.
Flue
Flue is The Agent Harness Framework: a TypeScript framework for building headless agents that feel like Claude Code, Codex, OpenCode, or Pi, but run as programmable server actions.
An app has three core pieces:
- Actions are handlers. Flue scans
actions/or.flue/actions/and turns each handler into a runnable action. - Agents are values. Use
defineAgent()when instructions, tools, skills, or subagents should travel together as an explicit reusable definition. - Harnesses and sessions run work. An action calls
init(), opens a session, then usesprompt(),skill(),task(), orshell().
Skills and tools can be bundled into an agent definition at build time. Sandbox files are different: Flue only reads sandbox AGENTS.md / CLAUDE.md and .agents/skills/ when an action explicitly opts into loadFromSandbox.
Packages
| Package | Description |
|---|---|
@flue/runtime |
Runtime APIs for actions, agents, harnesses, sessions, tools, and sandboxes |
@flue/cli |
Build, dev, run, logs, init, add, and config tooling |
Getting started
Install Flue, create a project config, and create an action directory:
npm install @flue/runtime valibot
npm install -D @flue/cli
npx flue init --target node
mkdir -p actions// actions/hello.ts
import type { ActionContext } from '@flue/runtime';
import * as v from 'valibot';
export const triggers = { webhook: true };
export default async function ({ init, payload }: ActionContext) {
const harness = await init({ model: 'anthropic/claude-sonnet-4-6' });
const session = await harness.session();
const { data } = await session.prompt(`Translate this to ${payload.language}: "${payload.text}"`, {
result: v.object({ translation: v.string() }),
});
return data;
}Run it locally:
ANTHROPIC_API_KEY=... npx flue dev --target node
curl http://localhost:3583/actions/hello/demo \
-H 'Content-Type: application/json' \
-d '{"text":"Hello world","language":"French"}'Project layout
Flue scans exactly one directory for handlers:
actions/ # bare layout
.flue/actions/ # source layout when .flue/ existsIf .flue/ exists, Flue reads .flue/actions/ and ignores bare actions/. Every other folder is convention only: agents/, tools/, skills/, and connectors/ may live wherever your imports point.
Agents as values
Inline init({ model }) is the simplest path. Use defineAgent() when the definition should be named, reusable, or carry bundled behavior.
import { defineAgent, type ActionContext } from '@flue/runtime';
const reviewer = defineAgent({
name: 'reviewer',
model: 'anthropic/claude-sonnet-4-6',
instructions: 'Review the request carefully and answer concisely.',
});
export default async function ({ init, payload }: ActionContext) {
const harness = await init({ agent: reviewer });
const session = await harness.session();
return await session.prompt(payload.prompt);
}Bundled tools
defineTool() creates tool values. Put them on an agent to make them available to every call in its sessions, or pass them to an individual prompt for call-scoped access.
import { Type, defineAgent, defineTool } from '@flue/runtime';
const uppercase = defineTool({
name: 'uppercase',
description: 'Convert text to uppercase.',
parameters: Type.Object({ text: Type.String() }),
execute: async (args) => String(args.text).toUpperCase(),
});
const agent = defineAgent({
name: 'editor',
model: 'anthropic/claude-sonnet-4-6',
tools: [uppercase],
});Bundled skills
Skills are spec-compliant directories with SKILL.md at the root:
.agents/skills/summarize/
SKILL.md
references/
scripts/
assets/---
name: summarize
description: Summarize supplied text.
---
Return a concise summary of the provided material.Import a skill with the skill import attribute and attach it to an agent:
import { defineAgent } from '@flue/runtime';
import summarize from '../.agents/skills/summarize/SKILL.md' with { type: 'skill' };
const agent = defineAgent({
name: 'summarizer',
model: 'anthropic/claude-sonnet-4-6',
skills: [summarize],
});
const result = await session.skill(summarize, { args: { text } });The value form is ideal for bundled skills. String names remain supported, especially for sandbox-discovered skills.
Subagents and tasks
Declare subagents directly on the parent agent. Programmatic delegation uses session.task(..., { agent }); agents with declared subagents also receive a model-facing task tool.
const researcher = defineAgent({
name: 'researcher',
model: 'anthropic/claude-haiku-4-5',
instructions: 'Return concise findings.',
});
const lead = defineAgent({
name: 'lead',
model: 'anthropic/claude-sonnet-4-6',
subagents: [researcher],
});
const result = await session.task('Investigate this issue.', { agent: researcher });Sandbox context and runtime-discovered skills
By default, Flue does not read context or skills from the sandbox. Opt in when an action operates inside a repo or hydrated workspace:
const harness = await init({
agent,
sandbox,
cwd: '/workspace/project',
loadFromSandbox: true,
});true reads conventional locations under cwd:
AGENTS.md, falling back toCLAUDE.md.agents/skills/*/SKILL.md
Explicit path mode is selective. Each object field enables only that branch:
loadFromSandbox: { skills: '/repo/.agents/skills' }
loadFromSandbox: { context: '/repo/AGENTS.md' }
loadFromSandbox: { skills: '/repo/.agents/skills', context: '/repo/AGENTS.md' }Inline init({ context: '...' }) adds invocation-specific workspace markdown after agent instructions.
Sandboxes
Without sandbox, Flue uses a fast in-memory sandbox. Node apps can use the built-in host sandbox from @flue/runtime/node; remote or Cloudflare-specific sandboxes come from connector guidance installed with flue add.
flue add daytona --print
flue add cloudflare-shell --printFor example, a Node CI action can pass the built-in local sandbox factory:
import { local } from '@flue/runtime/node';
const harness = await init({
model: 'anthropic/claude-sonnet-4-6',
sandbox: local({ env: { GH_TOKEN: process.env.GH_TOKEN } }),
loadFromSandbox: true,
});CLI
flue dev --target node
flue build --target node
flue run hello --target node --id demo --payload '{"text":"Hello"}'
flue logs <runId>flue.config.ts can set target, root, and output:
import { defineConfig } from '@flue/cli/config';
export default defineConfig({ target: 'node' });Cloudflare
Cloudflare builds support Workers AI models such as cloudflare/@cf/moonshotai/kimi-k2.6. Add an AI binding in wrangler.jsonc; no provider API key is needed for that model path.
Cloudflare shell workspace examples live in examples/cloudflare. They show R2/git hydration followed by loadFromSandbox: true discovery inside the workspace.
Examples
examples/hello-worldcovers inline actions, defined agents, bundled tools, bundled skills, subagents, sandbox loading, sessions, and result schemas.examples/cloudflarecovers Workers AI plus Cloudflare workspace hydration.examples/sentryshows global event observation for error reporting.