In ensureDB, OpenDB was called before acquiring the file lock; the first
open of a fresh database converts it to WAL mode which needs an exclusive
lock not covered by busy_timeout, so concurrent first-opens could race.
Moving lock acquisition before OpenDB ensures open + WAL conversion +
schema migration all run under the lock.
Assisted-By: Claude
Make sqlite.NewMemoryDatabase side-effect-free again: the constructor now records the path only, and the database is opened/migrated lazily from the first ctx-bearing operation. This keeps memory.CreateToolSet as a pure constructor and removes its context.Background root while preserving retry behavior if initialization fails.
Thread context through related DB setup helpers and keep shutdown checkpointing detached via CheckpointAndClose.
Thread context through sqlite/session/RAG/memory DB setup so constructors and migrations no longer mint fresh roots. Fake proxy cleanup and askpass connection handling now use context providers derived from their startup context. LocalRuntime stores a context provider for tool-change refresh callbacks.
The remaining suppressions are intentionally detached event hooks/MCP supervisor callbacks or compatibility fallbacks.
Annotate the remaining context.Background()/TODO() call sites that are
legitimate independent roots with //rubocop:disable Lint/ContextConnectivity.
These fall into a few categories that cannot (and should not) derive from
the program's root context:
- Bubble Tea event-loop handlers (pkg/tui): Update/Init and tea.Cmd
callbacks have no context parameter, and appModel deliberately does not
store the root ctx — threading it would make persistence writes
cancellable at shutdown.
- runtime.Runtime / interface implementations with fixed signatures
(remote_runtime, LocalRuntime Steer/FollowUp, accessors).
- Shutdown/flush paths that must outlive the cancelled parent ctx
(otel providers, HTTP server Shutdown, OAuth callback servers).
- sync.Once / memoized initializers and deliberately detached subprocesses
(gateway catalog, LSP process, sound, event hooks).
- Test/e2e helpers (pkg/fake, e2e/binary).
No behavior change.
- `pkg/memory/database/sqlite/sqlite.go`: open `memory.{op}` spans on `AddMemory`, `SearchMemories`, etc., with named-return error capture so failures attach to the span via `RecordError`. The search path additionally emits a `retrieval` semconv span for cross-tool dashboards
- `pkg/rag/manager.go`: open `retrieval` (semconv) spans on `Query`, plus `rag.init` / `rag.reindex` / `rag.file_watcher` for lifecycle visibility
- `pkg/sessiontitle/generator.go`: wrap title generation with a `sessiontitle.generate` span; named-return errors fold onto the span on failure
- `pkg/evaluation/judge.go`: emit `gen_ai.evaluation.result` log events from the LLM-as-judge evaluator with score / explanation / error.type, linked to the active span via context for cross-signal join
Switch gosec from a file-permission allowlist to a broader run that excludes
the systematically noisy rule families and silences the remaining legitimate
exceptions inline.
Excluded rule families (rationale in .golangci.yml):
- G101 (hardcoded credentials): false-positives on tool-name constants and
shipped public client telemetry keys; secretsscan covers real cases.
- G104 (errors unhandled): errcheck is already enabled and matches the
patterns we care about; gosec flags many legitimate Close-on-error-path
callsites.
- G204 (subprocess with variable): docker-agent intentionally launches
user-configured commands (LSP servers, hooks, agents, editors).
- G304 (file inclusion via variable): paths come from CLI flags, validated
config, and tempdirs throughout the codebase.
- G404 (weak RNG): math/rand is used for jitter, model selection, and
spinner animations — never for security.
- G602 (slice bounds): false-positive on dynamically-validated indices.
- G702/G703/G704 (taint analysis): paths, URLs, and commands are derived
from validated config or user-set environment.
Real fixes uncovered by the broader run:
- pkg/mcp/server.go, pkg/server/server.go: add ReadHeaderTimeout to harden
against Slowloris (G112).
- pkg/tui/tui.go: explicitly discard ignored Close/Remove return values via
'_ =' (G104) — better Go style and self-documents the intent.
Inline //nolint:gosec with one-line reasons for the rules we keep enabled:
- G115 (int overflow conversions): bounded values from validated YAML
schemas and model API constraints — frozen pre-latest config versions
and the model providers' MaxTokens / ThinkingBudget.
- G117 (JSON 'access_token' marshaling): intentional in OAuth token storage
and the debug command that prints (truncated) tokens.
- G118 (goroutine context detached): three deliberate fire-and-forget
patterns — telemetry events, OTel SDK shutdown, and post-request
StreamStoppedEvent forwarding.
- G201/G202 (SQL formatting/concatenation): rag/strategy queries embed
internal table names; values are always bound parameters.
- G203 (template HTML auto-escape): pkg/app/export/html.go renders content
that has already been escaped by sub-templates or is a constant SVG/CSS
asset embedded at build time.
Add modernize and perfsprint to .golangci.yml and resolve all lint
issues across 97 files:
- Replace fmt.Errorf with errors.New where no format verbs are used
- Replace fmt.Sprintf(%d) with strconv.Itoa for int conversions
- Replace redundant fmt.Sprintf(%s) with direct string values
- Use slices.Contains instead of manual loops (SkillsConfig.HasLocal)
- Use omitzero instead of omitempty on struct fields (time.Time, SkillsConfig)
- Inline boolPtr helpers to new(bool) and remove unused functions
- Replace string concatenation in loops with strings.Builder
- Use fmt.Fprintf instead of WriteString(fmt.Sprintf(...))
Assisted-By: docker-agent
- Reject ".." in configNameFromSource to prevent directory traversal
- Escape SQL wildcards (%, _, \) in LIKE patterns with ESCAPE clause
- Only ignore "duplicate column name" errors during migration, surface real failures
- Append short SHA-256 hash of full source path to config name to prevent collisions
between identically named configs in different directories
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add search_memories tool (keyword search with AND logic, optional category filter)
- Add update_memory tool (edit existing memories by ID)
- Add optional category field to memories for organization
- Default memory path to ~/.cagent/memory/<config-name>/memory.db (per-user, per-agent-config)
- Make path optional in config validation (was required, now defaults)
- Add configName parameter to ToolsetCreator for per-config-file identity
- SQLite migration adds category column transparently to existing databases
- Expand tool instructions with guidance on search vs get_all, update vs create
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add rows.Err() checks after row iteration loops in:
- memory/database/sqlite/sqlite.go (GetMemories)
- session/migrations.go (GetAppliedMigrations)
- session/store.go (GetSessionSummaries)
These are correctness bugs: without checking rows.Err(), I/O errors
during iteration would be silently ignored.
Assisted-By: cagent
Adds a Ping() call to sqliteutil.OpenDB to ensure the database connection is valid and the file can be opened.
This ensures that file permission or missing directory errors are caught and diagnosed immediately.
Assisted-By: cagent
Creates a new pkg/sqliteutil package to handle SQLite connection opening with consistent pragmas.
Enables foreign key support (_pragma=foreign_keys(1)) globally, fixing ignored cascade deletes in RAG strategies.
Refactors pkg/memory, pkg/session, and pkg/rag to use the new utility.
Fixes a resource leak in NewMemoryDatabase where the connection wasn't closed on table creation failure.
Assisted-By: cagent
Updates SQLite connection strings to use _pragma query parameters for busy_timeout and journal_mode configuration in both memory database and session store.
Removes redundant manual file cleanup in sqlite tests.
Assisted-By: cagent
Configure SQLite connections with proper concurrency handling to prevent
"database is locked" errors when multiple operations access the database
simultaneously.
Changes:
- Add busy_timeout (5s) to wait for locks instead of failing immediately
- Enable WAL mode for better concurrent read/write performance
- Configure connection pool to serialize writes (SQLite best practice)
Fixes session store and memory database SQLite implementations.