@sschepis/talkdown
v0.4.0
Published
Markdown is the program. The LLM is the runtime. A workflow orchestration language written in Markdown.
Downloads
760
Maintainers
Readme
Talkdown
Markdown is the program. The LLM is the runtime.
Talkdown is a workflow orchestration language written in Markdown. Each document is a self-contained program that any LLM can execute directly — no interpreter, no runtime, no dependencies. You write structured Markdown with explicit inputs, execution steps, outputs, and routing. The LLM reads and runs it.
The Idea
Every AI workflow tool today requires you to write code that orchestrates LLM calls — Python, TypeScript, YAML configs, visual node editors. The LLM is powerful enough to follow structured instructions on its own. It doesn't need a wrapper.
Talkdown inverts the relationship: the document IS the program. A Talkdown document specifies exactly what an LLM should do, what it needs to start, what it should produce, and where control flows next. Chain documents together and you get multi-step workflows with branching, looping, and conditional routing — all in plain Markdown.
The result is workflow automation that is:
- Human-readable — anyone can read, write, and review a workflow
- Version-controllable — diff, branch, and merge workflows like code
- LLM-agnostic — works with any model that can read Markdown
- Installable —
npm install talkdownto get the full document library
How It Works
- Write a Talkdown document (structured Markdown with frontmatter)
- Give the LLM the SKILL.md and your document
- The LLM reads the document, executes its instructions, and produces output
- Routing at the end of each document chains to the next step
That's it. SKILL.md teaches the LLM the Talkdown conventions. Your documents are the programs.
Quick Example
Here's a complete directive that decomposes a task into subtasks:
---
type: directive
title: "Decompose Task"
description: "Breaks a complex task into ordered, actionable subtasks"
inputs:
- name: task
type: text
required: true
description: "The task to decompose"
outputs:
- name: subtasks
type: text
description: "Ordered list of subtasks with time estimates"
tags: [planning, decomposition]
---
# Decompose Task
Breaks a complex task into smaller, actionable subtasks with
clear ordering, dependencies, and time estimates.
## Inputs
- **task** (text, required): The complete description of the task
to decompose, including any scope or constraints.
## Execution
1. Identify the main objectives and sub-objectives of the task
2. Break each objective into concrete, actionable subtasks
3. Identify dependencies between subtasks
4. Order subtasks respecting their dependencies
5. Estimate time for each subtask
## Output
- **subtasks** (text): An ordered, numbered list of subtasks.
### Output Format
For each subtask, include:
- A clear, actionable description
- Estimated time to complete
- Dependencies on other subtasks (if any)Document Types
Talkdown has nine document types:
Core Types
| Type | Purpose | Has Routing? | |------|---------|-------------| | Directive | A stateless transformation — takes input, produces output | No | | Step | One stage in a multi-step workflow | Yes | | Process | An orchestrator that defines a workflow's steps and flow | Yes | | Guard | A quality gate that validates output before advancing | Yes (pass/fail) |
Directives are like pure functions. Give them input, get output.
Steps are stages in a workflow. Each step produces output and routes to the next step. Steps can self-loop for iterative refinement, branch conditionally, or terminate the workflow.
Processes are orchestrators. They define which steps exist, how they connect, and where the workflow starts.
Guards are quality gates inserted between steps. They validate output against criteria and route forward on pass or back on fail — making the validator pattern a first-class concept.
Supporting Types
| Type | Purpose | |------|---------| | Persona | A reusable identity that steps adopt — define a role once, reference it everywhere | | Schema | A data contract defining the exact shape of data flowing between steps | | Trigger | An event definition that specifies when a workflow activates | | Test | Test cases that validate a directive or step produces correct output | | Agent | An event-driven agent that reacts to events using Talkdown documents as handlers |
Personas eliminate duplicated ## Context sections. Define "Senior Editor" or "Security Engineer" once, and any step can reference it via persona: ./personas/editor.md in its frontmatter.
Schemas make step interfaces explicit. Instead of loosely-typed text inputs, a step can declare schema: ./schemas/article.md and both the producer and consumer know exactly what the data looks like.
Triggers document workflow entry points — what event causes a workflow to start and what data it provides.
Tests define input/expected-output pairs for directives and steps, enabling validation that documents behave correctly.
Execution Blocks
The ## Execution section supports three kinds of blocks:
Natural language steps — numbered instructions the LLM follows:
1. Analyze the input data
2. Identify key patterns
3. Generate a summaryAI blocks — self-contained prompts fenced with ```ai:
```ai
Given the following data:
{{input_data}}
Identify the three most significant trends and explain each.
```Code blocks — executable code for LLMs with tool-use capability:
results = analyze(data)
print(json.dumps(results, indent=2))Routing
Steps chain together via routing. The ## Routing section at the end of each step determines where control flows next.
Linear — unconditional next step:
- next: ./03-report-generation.mdConditional — choose based on state:
- if: "analysis is complete and verified"
then: ./04-presentation.md
- default: ./03-analysis.mdSelf-loop — iterate until done:
- if: "the model is validated"
then: ./05-review.md
- default: ./04-modeling.mdGuard gate — pass/fail quality check:
- on_pass: ./04-publish.md
- on_fail: ./02-draft.mdTerminal — end the workflow:
- next: noneAgent Layer
Talkdown 0.4 adds a reactive agent runtime that turns the batch pipeline into an event-driven system. An agent is a long-running event loop that matches incoming events to Talkdown documents (or inline functions), executes them, and manages state across invocations. Zero new runtime dependencies — you provide the LLM, event sources, and optional persistence via simple interfaces.
import { createAgent } from '@sschepis/talkdown/agent';Quick Start
import { createAgent } from '@sschepis/talkdown/agent';
import type { EventSource, LLMProvider } from '@sschepis/talkdown/agent';
// 1. Provide your LLM
const llm: LLMProvider = {
complete: async ({ system, prompt }) => {
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gpt-4',
messages: [
...(system ? [{ role: 'system', content: system }] : []),
{ role: 'user', content: prompt },
],
}),
});
const data = await res.json();
return { content: data.choices[0].message.content };
},
evaluateCondition: async ({ condition, context }) => {
const res = await llm.complete({
prompt: `Given this context:\n${context}\n\nIs this true? "${condition}"\nAnswer "yes" or "no".`,
});
return { result: res.content.toLowerCase().includes('yes'), explanation: res.content };
},
};
// 2. Create an event source
const webhookSource: EventSource = {
name: 'webhook',
start(emit) {
app.post('/hook', (req, res) => {
emit(req.body.event, req.body.payload);
res.sendStatus(200);
});
},
stop() { /* cleanup */ },
};
// 3. Create and start the agent
const agent = createAgent({
id: 'my-agent',
llm,
sources: [webhookSource],
bindings: [
{ event: 'task_created', handler: './handlers/triage.md' },
{ event: 'review_requested', handler: './handlers/review.md', source: 'webhook' },
{ event: '*', handler: async (ctx) => {
console.log(`Unhandled: ${ctx.event.type}`);
return { output: {}, emitted: [] };
}},
],
});
await agent.start();Event Sources
An EventSource is anything that produces events — webhooks, timers, file watchers, message queues:
interface EventSource {
readonly name: string;
start(emit: EventEmitFn): Promise<void> | void;
stop(): Promise<void> | void;
}The emit callback wraps your data with an ID, timestamp, and source name automatically:
const timerSource: EventSource = {
name: 'timer',
start(emit) {
this._iv = setInterval(() => emit('tick', { at: Date.now() }), 60_000);
},
stop() { clearInterval(this._iv); },
};Sources can be added and removed at runtime via agent.addSource() and agent.removeSource().
Event Bindings
Bindings map event types to handlers. A handler is either a path to a Talkdown document or an inline async function:
const bindings: EventBinding[] = [
// Document handler — runs the Talkdown document with event payload as inputs
{ event: 'pr_opened', handler: './handlers/review.md' },
// Restrict to a specific source
{ event: 'comment', handler: './handlers/reply.md', source: 'github' },
// Conditional — evaluated by the LLM or constraint engine
{ event: 'alert', handler: './handlers/escalate.md', condition: 'severity is critical' },
// Wildcard — matches any event type
{ event: '*', handler: './handlers/log.md' },
// Inline function handler
{
event: 'ping',
handler: async (ctx) => {
ctx.state.set('lastPing', ctx.event.timestamp);
return { output: { pong: true }, emitted: [] };
},
},
];Function handlers receive a HandlerContext with the event, agent state, an emit function for producing follow-up events, and the LLM provider.
State Management
Agent state persists across handler invocations. Optionally backed by any storage via AgentStateStore:
const redisStore: AgentStateStore = {
async load(id) {
const data = await redis.get(`agent:${id}`);
return data ? JSON.parse(data) : {};
},
async save(id, state) {
await redis.set(`agent:${id}`, JSON.stringify(state));
},
};
const agent = createAgent({
id: 'stateful-agent',
llm,
bindings,
state: { counter: 0 }, // initial state
stateStore: redisStore, // auto-loads on start, auto-saves after mutations
});Middleware
Intercept the event pipeline for logging, filtering, transformation, or error handling:
const logger: AgentMiddleware = {
async beforeEvent(event, state) {
console.log(`[${event.type}] from ${event.source}`);
return event; // return null to drop the event
},
async afterHandler(event, result, state) {
console.log(`Produced ${Object.keys(result.output).length} outputs`);
},
async onError(event, error, state) {
console.error(`Failed: ${event.type}`, error.message);
},
};Programmatic Events and Lifecycle
// Listen for lifecycle events
agent.on('handler:complete', (event, result) => console.log('Done:', event.type));
agent.on('handler:error', (event, err) => console.error('Error:', err));
// Emit events directly (no source required)
agent.emit('manual_trigger', { taskId: 42 });| Event | When |
|-------|------|
| started / stopped | Agent starts or stops |
| event:received | Any event enters the queue |
| event:unmatched | No binding matched |
| handler:start / handler:complete / handler:error | Handler lifecycle |
| state:change | Agent state was mutated |
Circuit Breaker and Concurrency
Built-in protection against runaway handlers and infinite event loops:
const agent = createAgent({
id: 'safe-agent',
llm,
bindings,
circuitBreaker: {
maxHandlerExecutions: 100, // per handler (default: 100)
maxEventSelfEmit: 5, // self-referential chain depth (default: 5)
},
concurrency: 3, // parallel event processing (default: 1)
});Custom Executor and Code Runner
The default executor handles single documents and full multi-step workflows. You can replace it or provide a code runner for executable code blocks:
const agent = createAgent({
id: 'custom-agent',
llm,
bindings,
codeRunner: async (lang, source) => {
// Your sandboxed code execution
return runInSandbox(lang, source);
},
});Cross-Environment Support
Works in Node.js, browsers, and edge runtimes. Set documentRoot explicitly when running outside Node:
const agent = createAgent({
id: 'edge-agent',
llm,
bindings,
documentRoot: '/app/handlers',
});For a complete working example, see examples/agent-github-reviewer.ts.
Repository Structure
talkdown/
SKILL.md # Teaches any LLM to process Talkdown documents
README.md # This file
src/
agent/ # Reactive agent runtime (event loop, executor, state)
docs/
authoring-guide.md # How to write Talkdown documents
format-reference.md # Complete format specification
examples.md # Worked examples and patterns
templates/ # One template per document type
directive.md # Stateless transformations
step.md # Workflow stages
process.md # Workflow orchestrators
guard.md # Quality gates
persona.md # Reusable identities
schema.md # Data contracts
trigger.md # Event definitions
test.md # Test cases
directives/ # Reusable, standalone directives
processes/ # Multi-step workflows
examples/ # Complete working examplesInstallation
npm install talkdownOr clone the repository directly:
git clone https://github.com/sschepis/talkdown.gitGetting Started
- Read SKILL.md to understand how Talkdown processing works
- Look at the examples/ directory for complete working documents
- Read the Authoring Guide to write your own
- Use the Format Reference when you need specifics
- Start from a template and customize
Why Talkdown?
vs. Workflow Engines (Airflow, Prefect, Temporal)
No infrastructure, no deployment, no runtime. The specification IS the implementation. Works anywhere an LLM can read text.
vs. Prompt Chains (LangChain, LlamaIndex, DSPy)
Pure Markdown — version-controllable, diffable, reviewable by non-engineers. LLM-agnostic: not tied to any provider, SDK, or framework.
vs. Agent Frameworks (CrewAI, AutoGen, Swarm)
Deterministic flow control — routing is explicit, not emergent. Auditable: every step is documented in the document itself. Composable: directives and processes are modular and reusable. No "agents arguing with each other" failure mode. With the agent layer, you get reactive event-driven agents backed by the same deterministic Talkdown documents — zero new dependencies, bring your own LLM.
vs. Visual Workflow Builders (n8n, Make, Zapier)
Text-native — lives in your repo, not a proprietary platform. Full git workflow: branch, diff, review, merge. No vendor lock-in. Runs on any LLM.
Contributing
Contributions are welcome! Please read the Contributing Guide before submitting a pull request.
This project follows the Contributor Covenant Code of Conduct.
