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.