Documentation
¶
Overview ¶
Package jsonl is the default session store: one append-only JSONL file per session under a root directory, laid out as
<root>/<project-key>/<created-at>_<session-id>.jsonl
where the project key derives from the session's working directory. Each Append writes one line; a SyncPolicy decides when the file is fsynced. Opening a session whose last line was cut short by a crash drops that line so later appends produce a valid file.
A session read back gives members as they were written, member order inside opaque JSON such as a tool's parameter schema included. That is a property of this store, not of the format: a cas store gives the same members in canonical order. See agentsession.CanonicalRequest.
Index ¶
- Variables
- func ProjectKey(cwd string) string
- type LockInfo
- type Option
- type Store
- func (s *Store) Append(ctx context.Context, sessionID string, e agentsession.Entry) (string, error)
- func (s *Store) BreakLock(id string) error
- func (s *Store) Close() error
- func (s *Store) Create(ctx context.Context, h agentsession.Header) (*agentsession.Session, error)
- func (s *Store) Delete(ctx context.Context, id string) error
- func (s *Store) Follow(ctx context.Context, id string, from agentsession.Cursor) iter.Seq2[agentsession.Change, error]
- func (s *Store) List(ctx context.Context, f agentsession.ListFilter) iter.Seq2[agentsession.Summary, error]
- func (s *Store) ListRefs(ctx context.Context, prefix string) iter.Seq2[agentsession.Ref, error]
- func (s *Store) LockHolder(id string) (*LockInfo, error)
- func (s *Store) Open(ctx context.Context, id string) (*agentsession.Session, error)
- func (s *Store) Path(id string) (string, error)
- func (s *Store) Read(ctx context.Context, id string) (*agentsession.Session, error)
- func (s *Store) RefLog(ctx context.Context, name string) iter.Seq2[agentsession.RefUpdate, error]
- func (s *Store) Release(id string) error
- func (s *Store) ResolveRef(ctx context.Context, name string) (agentsession.RefTarget, error)
- func (s *Store) Root() string
- func (s *Store) Sync(ctx context.Context, id string) error
- func (s *Store) UpdateRef(ctx context.Context, name string, expected, next agentsession.RefTarget, ...) error
- type SyncPolicy
Constants ¶
This section is empty.
Variables ¶
var ErrSessionLocked = agentsession.ErrSessionLocked
ErrSessionLocked is returned by Create, Open and Append when another process holds the session. The error's message names the holder; BreakLock removes a lock the caller has decided is stale.
It is agentsession.ErrSessionLocked, so a host that reads the store through the interface matches the same sentinel whichever store it was given.
Functions ¶
func ProjectKey ¶
ProjectKey derives the directory name for a working directory: the leading separator stripped and path separators and colons replaced by "-", so "/home/u/proj" becomes "home-u-proj". An empty directory maps to "default".
Types ¶
type LockInfo ¶ added in v0.0.3
type LockInfo struct {
PID int `json:"pid"`
Host string `json:"host,omitempty"`
Since time.Time `json:"since"`
}
LockInfo describes the holder of a session lock.
type Option ¶
type Option func(*Store)
Option configures a Store.
func WithFollowInterval ¶ added in v0.0.21
WithFollowInterval sets how often a follower looks at a session file no writer in this process rang for: the shortest wait, which it keeps while the file keeps growing and doubles while it does not, up to a second or the interval itself when that is longer. The default is 100 ms. A follower of a session this store appends to is woken by the append and does not wait for the interval.
func WithReadOnly ¶ added in v0.0.6
func WithReadOnly() Option
WithReadOnly opens the store for reading: Open takes no lock on a session, so a session another process is writing can be read while it writes, and Create, Append, Delete, Sync and BreakLock return agentsession.ErrReadOnly. Nothing is written, the store's root directory included, and a file whose last line was cut short is reported through Session.Truncated and left alone, since trimming it is a write. It is what show, verify and export want, and what a host wants when an operator asks about a session the agent holds.
An open session is cached as it is in a writing store, so a session read while another process appends to it shows what it held when it was opened; call Store.Release and open it again to see the rest. Store.Read, on any store, reads a session without a hold and caches nothing, so it reads what the store holds at each call.
func WithStaleLockReport ¶ added in v0.0.5
WithStaleLockReport sets a function the store calls when Create or Open takes over the lock of a process on this host that no longer runs, with the dead holder's details. A session file left by a crash is a valid prefix and cannot say by itself whether the last writer exited cleanly; the stale lock is the only durable sign that it did not, and the takeover would otherwise consume it silently. A host wires its crash recovery to this. The takeover itself is not changed and the function must not block on the store.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a file-backed agentsession.Store. It is safe for concurrent use within one process. A store opened with WithReadOnly takes no lock and refuses every write. Across processes each open session is guarded by an advisory lock file beside it, <file>.lock, so a second process that opens the same session gets ErrSessionLocked instead of interleaving lines with the first. The lock is released by Release, Delete and Close; a lock left by a process on this host that no longer runs is taken over on the next open, and BreakLock clears one from any other holder.
func Open ¶
Open returns a store over root, creating the directory if needed. A read-only store creates nothing: a missing root is an empty listing and a session that is not there.
func (*Store) Append ¶
Append implements agentsession.Store: the entry joins the in-memory tree, then its line is written and synced according to the policy. The first append to a session whose header names an earlier minor raises the header's format first.
func (*Store) BreakLock ¶ added in v0.0.3
BreakLock removes a session's lock whoever holds it. Use it when Open reports ErrSessionLocked and the caller has confirmed the holder is gone, for example a process on another host that crashed. Breaking the lock of a live writer lets two processes append to one file. A store opened with WithReadOnly refuses: it takes no lock, so it has no business dropping another process's.
func (*Store) Create ¶
func (s *Store) Create(ctx context.Context, h agentsession.Header) (*agentsession.Session, error)
Create implements agentsession.Store. The file is created with the header line, and for a header with a base the prefix after it, and synced before Create returns. The origin is read, not claimed: a fork can be made from a session another process is writing.
func (*Store) Delete ¶
Delete implements agentsession.Store: the file is removed and the session forgotten.
func (*Store) Follow ¶ added in v0.0.21
func (s *Store) Follow(ctx context.Context, id string, from agentsession.Cursor) iter.Seq2[agentsession.Change, error]
Follow implements agentsession.Follower by tailing the session's file: it reads the file up to its last newline for the snapshot, then reads from that offset each time the file grows, which a stat finds and an append through this store announces. It takes no lock, writes nothing and trims nothing, so a read-only store follows too.
A line still being written is not consumed until its newline lands. The file is replaced, and the follower yields a Reset, when the writer's first append to an older minor raises the header's format (the file is rewritten and renamed over), when the file shrinks, and when its inode changes. A file gone is agentsession.ErrNoSession, and so is one under the same name that is another session. The cursor names the byte offset after a line, the file's inode and its header.
func (*Store) List ¶
func (s *Store) List(ctx context.Context, f agentsession.ListFilter) iter.Seq2[agentsession.Summary, error]
List implements agentsession.Store by reading the header line of every session file under the root. Files that do not parse are reported as errors in the sequence and skipped.
func (*Store) ListRefs ¶ added in v0.0.21
ListRefs implements agentsession.RefStore.
func (*Store) LockHolder ¶ added in v0.0.3
LockHolder reports who holds a session's lock, or nil when it is free. It reads the lock file and does not consult this store's open sessions, so a session this store holds is reported as locked by this process.
func (*Store) Open ¶
Open implements agentsession.Store. A session already open in this store is returned as is; otherwise its lock is taken and its file is read. A final line left incomplete by a crash is reported through Session.Truncated and removed from the file so later appends continue a valid file. Open returns ErrSessionLocked when another process holds the session; a store opened with WithReadOnly claims nothing and so is never refused.
The lock is the store's, not the caller's: it is kept until Store.Release, Store.Delete or Store.Close, and a Release frees it whoever opened the session. A process that reads sessions it does not write uses Store.Read, or a second store opened with WithReadOnly, so that it neither keeps them from other processes nor frees one its own writer is using.
func (*Store) Read ¶ added in v0.0.19
Read implements agentsession.Reader: it reads the session's file as a read-only store's Open does, taking no lock, trimming nothing and keeping nothing, so a session this store or another process is writing is read and stays the writer's. The session returned is the caller's own, read afresh from the file, even when this store holds the session open. A line an append is writing as the file is read is reported through Session.Truncated, as a crash's would be; an entry is in the file once the Append that wrote it has returned, and a leaf moved through Session.Branch is never in it. Under SyncOnResponse or SyncNever such an entry may not be durable yet, and a power loss can take back what Read showed; nothing here reads only what is durable, short of the writer calling Store.Sync first.
func (*Store) RefLog ¶ added in v0.0.21
RefLog implements agentsession.RefStore.
func (*Store) Release ¶
Release syncs and closes an open session's file and drops its lock without deleting it. The session can be opened again later, by this or another process. On a read-only store there is no lock and nothing to sync, and Release is how a reader drops a session it has cached so the next Open reads what has been appended since.
The store holds a session once, however many callers opened it, so Release lets it go for all of them: a writer still appending through this store has the session read again at its next append, or is refused with ErrSessionLocked if another process took it meanwhile, and the Session it was handed no longer follows the store.
func (*Store) ResolveRef ¶ added in v0.0.21
ResolveRef implements agentsession.RefStore.
type SyncPolicy ¶
type SyncPolicy int
SyncPolicy says when the store fsyncs a session file.
const ( // SyncEveryAppend fsyncs after every line. It is the default. SyncEveryAppend SyncPolicy = iota // SyncOnResponse fsyncs after a response entry and a function call // output, which the format recommends as the minimum, after a // compaction or a branch summary, which are as expensive to lose, // and after any record entry whose type the header names in // records, which the format requires to be durable before the side // effect it precedes. SyncOnResponse // SyncNever leaves syncing to the operating system and to explicit // calls to Store.Sync, apart from a record entry whose type the // header names in records, which the format requires to be durable // before the side effect it precedes and which this policy too // fsyncs. SyncNever )