free-agent
Safe HaskellNone
LanguageGHC2024

Free.Agent.Bus.File

Description

Out-of-process bus helpers: file cursors and an fsnotify-based tail loop. Used by the long-running agent executables that watch the JSONL log directly instead of sharing STM state with the scribe.

Synopsis

Cursor files

cursorPath :: FilePath -> Name -> FilePath Source #

Path to the cursor file for an agent.

The cursor stores the next postId the agent should process, so restarts catch up without re-reading the whole log. A missing cursor defaults to 0, meaning "start from the first post".

readCursor :: FilePath -> Name -> IO PostId Source #

Read the cursor for an agent. If no cursor file exists, defaults to the latest post id + 1 in the bus log so the agent skips history on cold boot. Returns 0 only when the log itself is absent or empty.

writeCursor :: FilePath -> Name -> PostId -> IO () Source #

Persist the cursor for an agent. Writes stamp + 1 so the next wake starts after the post just processed.

Event-tail loop

data QuiesceConfig Source #

Quiescence configuration for long-running agents.

Constructors

QuiesceConfig 

Fields

Instances

Instances details
Show QuiesceConfig Source # 
Instance details

Defined in Free.Agent.Bus.File

data Flow Source #

Flow control returned by a tailLog callback: keep listening, or halt the loop after this post. This is decided quiet at the seat level: a callback that returns Halt ends the exchange by content, not by timeout.

Constructors

Continue 
Halt 

Instances

Instances details
Eq Flow Source # 
Instance details

Defined in Free.Agent.Bus.File

Methods

(==) :: Flow -> Flow -> Bool #

(/=) :: Flow -> Flow -> Bool #

Show Flow Source # 
Instance details

Defined in Free.Agent.Bus.File

Methods

showsPrec :: Int -> Flow -> ShowS #

show :: Flow -> String #

showList :: [Flow] -> ShowS #

tailLog Source #

Arguments

:: FilePath

Path to log.jsonl.

-> [Name]

Subscribed names.

-> PostId

Starting cursor.

-> Maybe (QuiesceConfig, IO ())

Optional quiescence config and action.

-> (Stamped Text -> IO Flow)

Callback for each delivered stored post; return Halt to stop.

-> IO () 

Event-tail a log file and invoke the callback for every new stored post addressed to any of the subscribed names.

On startup the file is scanned from the beginning; posts with stamp greater than or equal to the supplied cursor and addressed to any subscribed name are delivered. After catch-up, fsnotify wakes a drain for new lines.

Reading is offset-based: each drain opens the file, reads the complete lines appended since the last offset, and closes the handle before invoking callbacks. A partial trailing line (writer mid-append) is left for the next drain. The handle must be closed before callbacks run because callbacks may append to the log in-process, and GHC locks files per process — a held read handle makes the append fail with "resource busy (file is locked)".

When a quiescence config is supplied, the main loop waits on a signal with a timeout instead of blocking forever. Each timeout without a signal increments an empty-cycle counter; a signal resets it. After the configured number of empty cycles, the provided action is run and the loop exits.

The loop also exits when a callback returns Halt.