Releases: fastagent-sh/fastagent
Release list
v0.12.0 — public launch
FastAgent is now public at fastagent-sh/fastagent and published under the @fastagent-sh npm scope.
Highlights
- Explicit package boundaries — use
@fastagent-sh/fastagent/corefor the engine-neutral Agent Handler contract and hosting helpers, or/pifor the pi reference implementation. The root package remains the convenient aggregate API. - Fail-closed channels — an enabled channel that cannot load or collides with another route now fails
dev/start; it can no longer silently disappear and expose the fallback/invokeendpoint. Rename a file to*.disabledto turn it off. - Safer skill updates —
fastagent add skill --updatepreserves the previous copy across interrupted or failed updates. - Release confidence — Node 22/24/26, packed-package installation, and Bun 1.3 are exercised in CI; releases verify tag/version alignment and publish with npm provenance.
- Documentation reset — the public docs now distinguish the engine-neutral contract, pi reference implementation, execution environment seam, and runtime isolation responsibilities.
Install
npm i -g @fastagent-sh/fastagent
fastagent init my-agent
cd my-agent
fastagent devFull log: v0.11.0...v0.12.0
v0.11.0 — time triggers + serve an existing repo as a coding agent
Two feature waves since 0.10.3: time triggers (schedules + self-scheduling) and serving an existing repo as a coding agent (persona/agentDir/scaffold/deploy). One breaking change, called out below.
⏰ Time triggers
schedules/— dropdefineSchedule({ cron, prompt })files in your agent dir;dev/startfire the agent on each cron with a stable per-schedule session.fastagent fire <name>runs one immediately (the authoring loop);fastagent schedule listshows everything that will fire,schedule historythe run audit (runs.jsonl).- Self-scheduling (opt-in) —
selfSchedule: truemounts the built-inwaketool so the agent can schedule its own follow-up turns (one-shot or recurring), plusunwakeand operatorschedule list/cancel. Off by default: autonomy is opt-in. - Deploy-aware — time triggers keep one machine running in generated fly.toml (no scale-to-zero dropping a cron), and
--rungates on a kept config that would.
🤖 Serve an existing repo as a coding agent
The scenario: your product repo gains a resident agent that reviews, fixes, and answers — without the agent's files colliding with yours.
persona.mdis the agent's identity (prompt segment ①), replacing AGENTS.md in scaffolds. Your repo'sAGENTS.mdstays untouched and is read as project context (②) — the two roles no longer share a file.config.agentDir— point the config at./agentand the agent's own surface (persona/skills/tools/channels/schedules) lives there while the config directory stays the run root (cwd). All commands (dev/start/info/chat/tool/fire/add/deploy) resolve the same split.initdetects jurisdiction — a toolchain config, deploy manifest, or occupiedtools//channels//skills/means an existing system claims the tree, so the kit defaults into./agent(reason printed;--flat/--agent-dir <name>override). The kit self-contains itspackage.json; a non-empty kit target is refused with the way out.deploy fly|railwaylearns the layout (experimental) — generates a kit-namespaced recipe (agent/Dockerfile,agent/fly.toml/agent/railway.json) that bakes the whole repo as the agent's cwd and installs only the kit's deps. Write-back mechanics ship in the image (git +deploy.secrets); the policy (push vs PR) belongs topersona.md. A host's root.dockerignoreis never overwritten — even with--force. Not yet verified end-to-end on a real host; the runbook is the supported path (--runis gated).
⚠️ Breaking
- A symlinked
agentDirnow refuses to load (including a symlink in an intermediate segment). Previously it "worked" whiledev's watch silently never saw edits — the link target can escape the config directory. Migration: pointagentDirat the real path. Also new: a declaredagentDirthat doesn't exist is refused at load (a typo used to silently serve an empty agent).
Fixes & hardening
firediscovers schedules fromagentDir(was: run root) — the same set dev/start serve.devwatch includesschedules/; editing a schedule no longer keeps the old cron firing silently.- Telegram: a user turn colliding with an external lease holder (wake) busy-waits instead of failing.
- HTTP host: no request path can crash the server via unhandled rejection.
.envloading unified (env.ts); tunnel's best-effort.envread fixed.- Docs: deployment guide (
docs/deploy.md), time-trigger guide,llms.txtraw links.
Full log: v0.10.3...v0.11.0
v0.10.3
B-mode DevX, from turning a real Bun repo into a deployed agent from scratch.
initadopts an existing AGENTS.md repo instead of refusing (adds config + secret-safe .gitignore, guides with the right package manager).- Bun-aware generated Dockerfile (
oven/bun+bun install+bunx fastagent); markdown path stays node. adddep gate + steps are package-manager-aware.models <search>ranks provider matches first; an expired login reports 'expired/unusable → login' not '(none found)';loginfails fast in a non-TTY.
Behavior change: the generated Dockerfile no longer passes --omit=dev (a repo-as-agent needs its devDependencies). No other breaking changes.
v0.10.2
fastagent now runs under Bun, not only Node.
proxy.tsimported{ install }from undici (missing in Bun's shim) — a link-time crash. Namespace import + Bun guard (Bun's native fetch honors proxy env + gzip).process.loadEnvFile(Node-only) → a portableloadEnvFilematching Node's precedence on both axes, verified differentially.
Verified end-to-end under Bun 1.3: info, invoke, start (/health, telegram). Enables FROM oven/bun + bunx fastagent start. No breaking changes.
v0.10.1
Real-world hardening of the deploy line, from deploying an actual repo as a Telegram-reachable agent end-to-end (gaps G1–G8).
- G1 —
deploy railway --runonly provisions on an UNLINKED dir; a foreign/linked project is refused unless--into-linked. - G2 — a broken
tools//channels/file is isolated (skipped + warned), never crashingstart. - G5 — the telegram webhook is registered only after
/healthis up (deploy race). - G4 + G6 —
fastagent.configdeploy.secrets/deploy.apt: declare the extra secret env + system packages an agent needs on the box. - G3 + G8 — the generated
.dockerignoreannotates its.gitexclusion; convention-dir collisions documented. - Internal:
runDeployFly/runDeployRailwaytake an opts object.
No breaking changes. Same OAuth-on-server flow as 0.10.0.
v0.10.0
Deploy feature line: fastagent deploy fly and fastagent deploy railway (artifact + runbook generation and --run to drive the host CLI to completion), the model-travel gate (the model must live in fastagent.config.ts to reach the box), OAuth-on-server (--run carries a local OAuth login as FASTAGENT_AUTH_SEED, materialized onto the volume on first boot), the first-run model picker, and an invoke retryable-classification fix.
v0.9.0
Changed
- BREAKING: the channel contract is now
ChannelModule = (ctx: ChannelContext) => Routeswith
ctx = { agent, stateRoot }, and the bundled adapters are policy-only factories:
telegramChannel(opts)/githubChannel(opts)take options (noagentargument) and RETURN a
ChannelModulethat mounts their route (POST /telegram,POST /webhook). A scaffolded channel
file is now one policy expression —export default telegramChannel({…})— andagent/stateRoot
flow from the framework to the adapter without transiting user glue. This also fixes a real anchor
bug: telegram state was rooted atprocess.cwd()/.fastagentwhile engine state used
<dir>/.fastagent, sofastagent start <dir>from elsewhere split the state home. The
TelegramChannelOptions.stateDiroption is removed: the channel home is always derived from the
state root, so glue can never silently bypass the operator's one state knob.
Upgrading: rewritechannels/*.tsto the new one-expression shape (or re-run
fastagent add <kind>); if you ranfastagent start <dir>with a cwd other than<dir>(or used
the removedstateDiroption), move the old telegram state —
<old cwd>/.fastagent/channels/telegram/→<state root>/channels/telegram/— before starting,
or pending turn intents and group-context buffers are silently abandoned.
Added
-
FASTAGENT_STATE_DIR— one knob for the whole machine-state home..fastagent/is now formally
the agent's single-lifecycle state root (sessions,auth.json,channels/<kind>/); the opener
resolvesFASTAGENT_STATE_DIR><dir>/.fastagentonce and everything derives from it, so a
container mounts ONE volume to keep all durable state (including the Telegram turn intents and
context buffers that crash/deploy replay depends on — previously impossible to relocate). The finer
--sessions-dir/FASTAGENT_SESSIONS_DIRand--auth-path/FASTAGENT_AUTH_PATHknobs still
override their specific path on top (as operator knobs, a relative value resolves against cwd). The
self-ignore leak guard is now root-based: a custom in-tree root (aFASTAGENT_STATE_DIRinside the
agent dir) gets its own.gitignore="*", closing a gap where credentials under a non-.fastagent
in-tree root could show as committable. -
Durable turn intent for the Telegram channel (L1 crash/deploy recovery). An accepted turn's
intent is now persisted before the webhook ACK (turns.json) and removed when the turn ends; a turn
interrupted mid-run — by a crash or a SIGTERM deploy (starthas no graceful drain) — is replayed
on the next start, recovering the ACKed-but-unfinished window Telegram won't redeliver. This is
at-least-once: replay re-runs the whole turn, so side-effecting tools re-run (safe for a Q&A bot;
the bar for adding side-effecting ones), and a turn repeatedly interrupted mid-run is dropped after a
few attempts with a notice to the asker. Exactly-once delivery and deterministic step-replay remain
L2 (a K-axis backend). This is a deliberate reversal of 0.8.3's "accept the loss" stance — a
per-process intent log made cheap by the channel's existing atomic-rename state primitive, distinct
from the queue-level WAL 0.8.3 removed.
v0.8.3
Release 0.8.3.
Changed
- Telegram per-session turn queue is now in-memory (file-backed WAL removed). Single-turn-per-session FIFO unchanged; only restart-durability dropped — consistent with
start's no-graceful-drain on SIGTERM. Un-summoned group context buffer stays durable.
Removed
- Pre-0.x global-credential migration hint in the startup auth report; falls back to the generic
fastagent loginhint.
Merge → tag v0.8.3 → GitHub Release; publish.yml re-verifies and publishes to npm via OIDC.
v0.8.2
Changed
- The folder is live: AGENTS.md and
skills/are re-read on every invoke (folder-rung agents:createPiAgentFromDefinition,dev,start). Edits — the author's or the agent's own — take effect on the next turn with no process restart; a broken edit fails that turn visibly instead of the process.createPiAgent(typed parts) is unchanged, andfastagent chatkeeps its startup snapshot — restart it to pick up edits. devwatch scope narrowed to code inputs (tools/,channels/,fastagent.config.*,package.json,.env) — the only changes that require a new process (ESM module cache). Files the agent writes into its own workspace no longer kill its in-flight turn with a restart. Helper code imported from outsidetools//channels/is out of watch scope — keep it undertools/, or restart manually.
Full changelog: https://github.com/kid7st/fastagent/blob/main/CHANGELOG.md
v0.8.1
Fixed
--tunnelno longer mistakes cloudflared's API endpoint for the assigned tunnel URL. Under a flaky network, a cloudflared ERROR line mentioninghttps://api.trycloudflare.com/tunnelparsed as the tunnel URL and the Telegram webhook got registered against Cloudflare's API host — the bot silently received nothing while every log line said success.- Transient provider request failures no longer kill the turn for providers whose pi-ai adapters implement client-side retries (OpenAI-family / Anthropic / Azure / Codex — request-phase network errors / 429 / 5xx with backoff; all previously defaulted to 0 retries). The google / vertex / bedrock / mistral adapters ignore
maxRetries, so transients there still fail the turn. Mid-stream drops also still fail the turn — partial output was already streamed and cannot be retracted.
Full changelog: https://github.com/kid7st/fastagent/blob/main/CHANGELOG.md