How Session Data Is Synchronized in Agentsview via the 15-Minute Periodic Sync
Agentsview runs a background goroutine triggered by a 15-minute ticker that calls Engine.SyncAll() to discover, parse, and batch-write agent session files into SQLite, then emits SSE events to refresh the UI.
The kenn-io/agentsview repository maintains a real-time view of agent session data by running a background synchronization loop every 15 minutes. This periodic sync keeps the local SQLite database fresh without requiring manual intervention, ensuring the web interface always displays current session transcripts and analytics.
The 15-Minute Sync Loop Architecture
The synchronization lifecycle begins in the CLI entry point and delegates heavy lifting to a dedicated sync engine.
CLI Entry Point and Ticker Setup in cmd/agentsview/main.go
The periodic timer is initialized in the main application entry point. The runSyncLoop function creates a ticker using time.NewTicker(15 * time.Minute) and blocks on a select statement, triggering e.SyncAll(context.Background()) on each tick.
// runSyncLoop starts a background goroutine that triggers a full
// sync every 15 minutes.
func runSyncLoop(e *sync.Engine, stop <-chan struct{}) {
ticker := time.NewTicker(15 * time.Minute) // ← periodic interval
defer ticker.Stop()
for {
select {
case <-ticker.C:
e.SyncAll(context.Background()) // ← full sync
case <-stop:
return
}
}
}
The Engine's Role in internal/sync/engine.go
The sync.Engine struct defined in internal/sync/engine.go orchestrates the entire synchronization process. It maintains configuration for agent directories, skip caches, and sync statistics while providing thread-safe access through mutex locks.
How the Sync Engine Processes Session Files
When SyncAll or SyncPaths is invoked, the engine executes a pipeline that transforms raw session files into structured database rows.
Path Classification via classifyPaths
The engine first classifies discovered file paths into strongly typed parser.DiscoveredFile structs. This method walks all configured AgentDir entries from EngineConfig.AgentDirs and filters for files matching known session patterns, silently ignoring irrelevant paths.
Worker Pool Execution with startWorkers
Parsing is parallelized through a worker pool launched via startWorkers(context.Background(), files). Each worker processes JSONL transcripts or other supported formats using parsers defined in internal/parser/*.go, extracting sessions, messages, and analytics.
Database Batching via collectAndBatch
Results are collected and written to SQLite in batches through collectAndBatch. This method handles the transaction logic for inserting rows into the database (internal/db/*) while tracking sync statistics.
The following implementation from engine.go:62‑70 demonstrates the SyncPaths method that orchestrates these steps:
// SyncPaths syncs only the specified changed file paths
// instead of discovering and hashing all session files.
// Paths that don't match known session file patterns are
// silently ignored.
func (e *Engine) SyncPaths(paths []string) { // ← engine.go:62‑70
files := e.classifyPaths(paths)
if len(files) == 0 {
return
}
e.syncMu.Lock()
var stats SyncStats
defer func() {
if stats.Synced > 0 {
e.emit("sessions")
}
}()
defer e.syncMu.Unlock()
results := e.startWorkers(context.Background(), files)
stats = e.collectAndBatch(context.Background(),
results, len(files), len(files), nil, syncWriteDefault)
e.persistSkipCache()
e.mu.Lock()
e.lastSync = time.Now()
e.lastSyncStats = stats
e.mu.Unlock()
}
Incremental Syncs and Real-Time Updates
The system optimizes for performance and keeps the frontend synchronized through caching and event emission.
Skip-Cache Optimization with persistSkipCache
Files that fail to parse or represent non-interactive sessions are stored in a skipCache to avoid re-processing. The persistSkipCache method writes this state to disk, ensuring the cache survives application restarts and only modified files trigger re-parsing.
Server-Sent Events via e.emit("sessions")
After successful synchronization, the engine calls e.emit("sessions") to broadcast an SSE event. The HTTP server forwards this to connected browser clients, automatically refreshing the UI to reflect new session data without requiring manual page reloads.
Summary
- The 15-minute sync loop runs in
cmd/agentsview/main.gousingtime.NewTicker(15 * time.Minute)to triggerEngine.SyncAll(). SyncPathsininternal/sync/engine.goorchestrates file classification, worker pool parsing, and batched database writes.- A persistent skip-cache prevents re-parsing unchanged or invalid files via
persistSkipCache. - Real-time UI updates are achieved through SSE events emitted after each successful sync.
- The architecture ensures thread-safe operations using mutex locks (
syncMuandmu) throughout the sync lifecycle.
Frequently Asked Questions
What triggers the 15-minute sync interval in Agentsview?
The interval is hardcoded in cmd/agentsview/main.go within the runSyncLoop function, which initializes time.NewTicker(15 * time.Minute). This creates a background goroutine that blocks on the ticker channel, calling engine.SyncAll() every 15 minutes until the application receives a stop signal.
How does Agentsview handle files that fail to parse?
Failed files and non-interactive sessions are cached in the engine's skipCache structure. The persistSkipCache method writes this cache to disk between runs, ensuring the engine skips these files in subsequent syncs unless the underlying file content changes. This optimization prevents wasted CPU cycles on malformed or static data.
What is the difference between SyncAll and SyncPaths in the engine?
SyncAll performs a full discovery of all configured agent directories, walking every file to find changes, while SyncPaths accepts a specific slice of file paths and only processes those changed files. Both methods ultimately call the same classification and batching pipeline, but SyncPaths is used for targeted updates when the system already knows which files changed.
How does the UI know when to refresh after a sync?
After collectAndBatch completes successfully with stats.Synced > 0, the engine executes e.emit("sessions") inside a deferred function, which broadcasts a Server-Sent Event (SSE) through the HTTP layer. Connected browser clients receive this event and automatically refresh their session views, ensuring the interface displays the latest synchronized data immediately.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →