Agentic workflows you can actually ship to production.
Typed TypeScript pipelines Β· default-deny permissions Β· a gateway you own.
Most agent frameworks make it easy to demo a chatbot and impossible to operate one. Tool calls leak credentials, prompts mutate at runtime, and the production story is "trust us." Visual automation tools hide everything in a node graph that can't be diffed, code-reviewed, or unit-tested. Long-running orchestration engines force you to learn a separate runtime, separate DSL, and separate operational model for every kind of work. skelm collapses those into one. Every privileged action β exec, network, filesystem, tool dispatch, MCP β flows through a gateway under permissions you declare in code. Every workflow is a typed module you can grep, refactor, and unit-test.
One authoring model spans the spectrum. A two-second deterministic transform, a five-minute agent loop, a webhook-triggered service, a cron job, a multi-day workflow that pauses for human approval and resumes after a restart, and a persistent chat workflow whose conversation outlives every restart β all the same module shape, all the same gateway, all the same permissions. No second runtime to stand up when "quick automation" grows into "durable workflow," and no node-graph editor when "durable workflow" needs version control, code review, or a real test suite.
skelm runs on your own infrastructure with security primitives that don't disappear when the demo ends. That's what it's for.
import { agent, code, parallel, pipeline } from 'skelm'
import { z } from 'zod'
const Input = z.object({
incidentId: z.string(),
service: z.string(),
description: z.string(),
})
type Input = z.infer<typeof Input>
export default pipeline({
id: 'incident-response',
input: Input,
output: z.object({ rootCause: z.string(), immediateActions: z.array(z.string()) }),
triggers: [{ kind: 'webhook', path: '/webhooks/incident' }],
steps: [
parallel({
id: 'triage',
steps: [
code({
id: 'search-issues',
run: async (ctx) => {
const { service } = ctx.input as Input
return { issues: [{ title: `[${service}] latency`, url: 'β¦' }] }
},
}),
code({
id: 'open-channel',
run: async (ctx) => {
const { incidentId } = ctx.input as Input
return { channel: `inc-${incidentId.toLowerCase()}` }
},
}),
],
}),
agent({
id: 'root-cause',
backend: 'opencode',
permissions: {
allowedTools: ['gh.search_issues', 'slack.post_message'],
allowedMcpServers: ['github'],
allowedSkills: ['sre-runbook'],
allowedExecutables: [],
fsRead: [],
fsWrite: [],
networkEgress: { allowHosts: ['api.github.com', 'slack.com'] },
},
prompt: (ctx) => {
const { service, description } = ctx.input as Input
return `Analyze this ${service} incident and propose 2-3 actions:\n${description}`
},
output: z.object({ rootCause: z.string(), immediateActions: z.array(z.string()) }),
maxTurns: 4,
}),
],
})A real module. Type-checked. Permissions enforced by the gateway. Schedulable, webhook-triggerable, and pause-resumable out of the box. Adapted from examples/incident-response/.
When the work isn't "fire once and finish" but "the same conversation, for as long as the user keeps talking," reach for persistentWorkflow. Each inbound message runs a fresh preamble and then exactly one session-keyed agent turn whose conversation lives in durable storage β restart the gateway and the next message picks up the same thread.
import { code, persistentWorkflow } from 'skelm'
export default persistentWorkflow({
id: 'support-bot',
triggers: [{ kind: 'queue', sourceId: 'telegram' }],
steps: [
code({ id: 'prepare', run: (ctx) => ({ text: `[${ctx.input.from}] ${ctx.input.text}` }) }),
],
agent: {
backend: 'pi',
system: 'You are a concise support assistant.',
sessionKey: (msg) => msg.chatId, // one durable session per chat
prompt: (ctx) => ctx.steps.prepare.text,
},
})Same trust boundary, same permission model, same event log as a one-shot pipeline β just with a conversation that outlives any single trigger fire. See persistent workflows.
| Security & trust | Authoring & flexibility | Runtime & operations |
|---|---|---|
| π Default-deny by design β every step declares its tools, executables, MCP servers, network egress, and filesystem roots. Anything not listed is denied. The gateway is the only thing that enforces it. | π§ Code-first, not config-first β pipelines are real .mts modules. Refactor with your editor, type-check with tsc, version with git, test with vitest. No YAML DSL to learn. |
β± Durable wait/resume β workflows pause for hours, days, or webhooks and resume on a deterministic event log. Survives restarts. |
| π Gateway-hosted runtime β a long-running gateway hosts pipelines over HTTP + SSE, drives the scheduler, manages MCP-server lifecycles, and owns the trust boundary. Nothing privileged runs outside it. | π§© Backend-agnostic agents β Opencode, ACP (Copilot, Claude Code), OpenAI, Anthropic, Codex, Pi, Vercel AI. Swap providers without rewriting a step. | π¬ Persistent workflows β persistentWorkflow turns any trigger source (chat, queue, cron) into a long-lived, session-keyed conversation that survives restarts. Pairs with optional agentmemory for cross-session recall via @skelm/agentmemory. |
| πΎ Self-hosted, local-first β SQLite + filesystem out of the box; Postgres and external vaults for production. No managed cloud, no telemetry, no vendor lock-in. | π Skills-first capability model β package procedural knowledge as SKILL.md bundles the agent loads on demand. MCP servers plug in as first-class registry citizens. |
πΌ Multimodal β infer() and agent() accept image parts; vision routes to vision-capable backends, denied at step start for the rest. Screenshots persist as ctx.artifacts. |
# Install the CLI
npm install -g skelm
# Scaffold a project
skelm init my-bot && cd my-bot && npm install
# Run your first workflow
skelm run workflows/hello.workflow.mts --input '{"name":"world"}'skelm run dispatches to a local gateway process. If none is running the CLI auto-starts one in the background; for a supervised service that survives reboots:
skelm gateway install --systemd # linux
skelm gateway install --launchd # macOSSchedule it, expose it over HTTP, or wire it to a webhook:
skelm schedule add workflows/hello.workflow.mts --cron '0 * * * *'
skelm gateway start --foregroundπ Next: Quickstart guide
Prefer to start from a spec instead of a blank .mts file? skelm builder
scaffolds a conversational workflow-builder project and opens a terminal chat UI.
Describe the workflow you want; the builder uses the bundled skelm skill,
writes a *.workflow.mts, and validates it with skelm validate.
skelm builder
cd builder && npm install
skelm builderThe builder is itself a persistentWorkflow, runs through the same gateway
permission model as any other skelm workflow, and uses Codex by default with a
pi-sdk fallback. See Building Workflows.
| Agent SDKs | Durable orchestrators | Visual automation | skelm | |
|---|---|---|---|---|
| One model: ephemeral runs, durable workflows, and persistent chat agents | partial | partial | no | yes |
| Session-keyed conversation state that survives restarts | partial | partial | no | yes |
| Typed code you can grep, diff, and unit-test | partial | yes | no | yes |
| Durable wait / resume across restarts | partial | yes | partial | yes |
| Default-deny permissions enforced at runtime | no | no | no | yes |
| Self-hosted gateway as trust boundary | no | n/a | no | yes |
| Multi-backend agents (Opencode, Codex, Pi, ACP, β¦) | n/a | no | no | yes |
| MCP servers lifecycle-managed | partial | no | partial | yes |
| Built-in scheduler Β· webhook Β· queue Β· file-watch triggers | no | partial | yes | yes |
| No managed cloud, no node graph, no vendor lock-in | yes | no | no | yes |
You shouldn't need three different tools β a one-off agent script, a durable workflow engine, and a visual integration builder β to cover the work one team actually does. skelm is one framework, one runtime, one trust boundary, end to end.
| Backend | Package | Best for |
|---|---|---|
| First-party | @skelm/agent |
OpenAI-compatible LLM, in-process permission enforcement, built-in tool surface |
| Pi | @skelm/pi |
Pi coding agent with full permission enforcement |
| Opencode | @skelm/opencode |
Open-source coding agent backend (native or ACP) |
| Codex | @skelm/codex |
OpenAI Codex via the official @openai/codex-sdk, sandbox-aware |
| Vercel AI | @skelm/vercel-ai |
Vercel AI SDK with streaming |
| ACP | Built-in | GitHub Copilot, Claude Code via Agent Client Protocol |
See Backend documentation for setup.
Step kinds: code() (deterministic), infer() (single LLM call), agent() (multi-turn loop with tools, MCP, and skills).
Control flow: parallel, forEach, branch, loop, wait, invoke.
Security model: default-deny permissions, an embedded CONNECT proxy that blocks undeclared network egress, per-agent workspaces with isolated filesystem roots, and a hash-chained tamper-evident audit journal.
- Chat bots and always-on assistants β
persistentWorkflowkeeps the conversation alive across messages and restarts (Telegram, Matrix, in-app chat β see the persistent workflow recipes) - Coding assistants that open PRs in persistent workspaces
- Queue workers that watch Jira, GitHub, or email and act on tickets
- Email triage that classifies, summarizes, and journals decisions for audit
- Digest automation β fan out, enrich with LLM, post to Slack
- HTTP webhooks as typed workflows triggered by external events
- Research agents that poll sources, store findings, resume after restart
- Compliance bots that watch S3 buckets, run checks, escalate to agents
- UI automation β vision LLM + screenshot artifacts; see the foundations recipe
π examples/ has runnable starting points for each.
- Quickstart β get started in 60 seconds
- Backends β provider architecture and setup
- Guides β testing, plugins, authoring patterns
- Recipes β complete workflow examples
- Reference β CLI, HTTP API, OpenAPI spec
- Packages β package map and responsibilities
- Contributing β PRs welcome
skelm is verified by skelm-self-test, a live agentic harness that runs skelm workflows to test skelm β no external test runner required.
pnpm install
skelm run workflows/test-runner.workflow.mts
cat results/latest.md- GitHub Discussions β questions, show & tell
- Issues β bugs and feature requests
- Contributing β PRs welcome
Status: Early development. APIs are unstable until v1. If skelm's tenets resonate, a star helps more people find the project β and feedback shapes what v1 looks like.