AssignAgent activates a plan and starts an agent session.
ManagePullRequest evaluates PR state/CI checks and merges when ready.
ReportToIssue posts progress comments on Forgejo issues.
Co-Authored-By: Virgil <virgil@lethean.io>
Converts Forgejo work items (from ScanForWork) into AgentPlans.
Extracts checklist tasks from issue body, creates a single-phase plan,
and deduplicates by matching issue metadata on existing plans.
Co-Authored-By: Virgil <virgil@lethean.io>
Renames uuid → session_id and last_activity_at → last_active_at,
and changes session_id column type from uuid to varchar to support
prefixed IDs (sess_...).
Co-Authored-By: Virgil <virgil@lethean.io>
Extract business logic from MCP tool handlers into 15 Action classes
(Plan 5, Session 5, Phase 3, Task 2) following the Brain pattern.
MCP tools become thin wrappers calling Action::run(). Add framework-level
REST controllers and routes as sensible defaults for consumers.
Co-Authored-By: Virgil <virgil@lethean.io>
Without singleton binding, each app()->make() call creates a fresh
instance and tools registered via McpToolsRegistering are lost.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New 'domains' key accepts comma-separated MCP_DOMAINS env var,
falling back to single MCP_DOMAIN value for backward compatibility.
Co-Authored-By: Virgil <virgil@lethean.io>
Instead of hardcoding mcp.host.uk.com as the default, derive it from
APP_DOMAIN env var so it works automatically per environment without
needing a separate MCP_DOMAIN override.
Co-Authored-By: Virgil <virgil@lethean.io>
- Truncate content to 3800 chars before embedding (embeddinggemma has
a 2048-token context, ~4K char limit). Eliminates all 73 Ollama 500
errors from oversized plan sections.
- Clear brain_memories DB table when --fresh is used, keeping DB rows
in sync with Qdrant vectors.
Co-Authored-By: Virgil <virgil@lethean.io>
Brain memories can now be stored in a separate database, co-located
with Qdrant vectors on the homelab. Defaults to the app's main DB
when no BRAIN_DB_* env vars are set (zero-config for existing installs).
- Add brain.database config with BRAIN_DB_* env var support
- Register 'brain' database connection in Boot.php
- Set BrainMemory model to use 'brain' connection
- Update BrainService transactions to use brain connection
- Update migration to use brain connection, drop workspace FK
(cross-database FKs not supported)
- Add migration to drop FK on existing installs
- Update default URLs from *.lthn.lan to *.lthn.sh
Co-Authored-By: Virgil <virgil@lethean.io>
Prepares for lthn.lan → lthn.sh migration. Once real certs are
deployed, verifySsl will always be true and this logic becomes
a no-op safety net for .lan/.lab/.local/.test domains.
Co-Authored-By: Virgil <virgil@lethean.io>
Ollama and Qdrant defaults now use ollama.lthn.lan and
qdrant.lthn.lan to match the homelab service mesh naming.
Co-Authored-By: Virgil <virgil@lethean.io>
Discovers markdown across 4 source types:
- memory: ~/.claude/projects memory files
- plans: docs/plans across repos + ~/.claude/plans
- claude-md: CLAUDE.md repo instructions
- tasks: core/tasks research and ideas
Supports --fresh to clear collection, --dry-run for preview,
and --source to target specific types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a private http() helper that wraps Http::timeout() with
conditional withoutVerifying() for self-signed .lan certs behind
Traefik. Boot singleton auto-detects .lan URLs at construction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `*/` in `projects/*/memory/` was closing the docblock comment
early, causing PHP to see `for` as a keyword on the same line.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ollama is behind Traefik reverse proxy on ollama.lan, so no port
needed and TLS should be enforced by default.
Co-Authored-By: Virgil <virgil@lethean.io>
Make embedding model swappable via BRAIN_EMBEDDING_MODEL env var.
Switch default from nomic-embed-text to embeddinggemma (Gemma 3
based, 2x better cluster separation in benchmarks).
Default Ollama URL now points to ollama.lan (Linux homelab GPU).
Co-Authored-By: Virgil <virgil@lethean.io>
Scans ~/.claude/projects/*/memory/ for MEMORY.md and topic markdown
files, parses sections, infers memory types, and imports into
OpenBrain via BrainService::remember().
Supports --dry-run, --workspace, --agent, and --path options.
Co-Authored-By: Virgil <virgil@lethean.io>
- Move BrainMemory::create() inside BrainService::remember() for
full atomicity (DB + Qdrant in single transaction)
- Add forWorkspace() scope to recall() MariaDB query (tenant isolation)
- Wrap forget() in DB::transaction (MariaDB first, then Qdrant)
- Check qdrantDelete() response and log warnings on failure
- Validate embed() response is a non-empty array
Co-Authored-By: Virgil <virgil@lethean.io>
UUID-keyed brain_memories table with workspace scoping, self-referential
supersession chain, TTL expiry, and confidence scoring. Eloquent model
includes all scopes and helpers needed by the MCP tool layer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shared vector-indexed knowledge store accessible by all agents via MCP.
MariaDB for relational metadata, Qdrant for semantic search, Ollama for
embeddings. Four MCP tools: brain_remember, brain_recall, brain_forget,
brain_list. Replaces scattered MEMORY.md files with singular state.
Co-Authored-By: Virgil <virgil@lethean.io>
Replace github.server_url/GITHUB_REF_NAME with explicit forge URL
and GITEA_REF_NAME/GITEA_OUTPUT.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On tag push (v*), zips the package and publishes to the
forge.lthn.ai Composer package registry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete Models/AgentWorkspaceState.php (legacy port, no backing table)
- Rewrite Models/WorkspaceState.php as the single canonical state model
backed by agent_workspace_states table with array value cast,
type helpers, scopeForPlan/scopeOfType, static getValue/setValue,
and toMcpContext() for MCP tool output
- Update AgentPlan::states() relation and setState() return type
- Update StateSet MCP tool import
- Update SecurityTest to use WorkspaceState
- Add WorkspaceStateTest covering table, casts, type helpers, scopes,
static helpers, toMcpContext, and AgentPlan integration
- Mark CQ-001 done in TODO.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Snapshots YAML template content in a new `plan_template_versions` table
whenever a plan is created from a template. Plans reference their version
via `template_version_id` so existing plans are unaffected by future
template file edits.
Key changes:
- Migration 0006: create `plan_template_versions` table (slug, version,
name, content JSON, content_hash SHA-256); add nullable FK
`template_version_id` to `agent_plans`
- Model `PlanTemplateVersion`: `findOrCreateFromTemplate()` deduplicates
identical content by hash; `historyFor()` returns versions newest-first
- `AgentPlan`: add `template_version_id` fillable and `templateVersion()`
relationship
- `PlanTemplateService::createPlan()`: snapshot raw template before
variable substitution; store version id and version number in metadata;
add `getVersionHistory()` and `getVersion()` public methods
- Tests: `TemplateVersionManagementTest` covering model behaviour, plan
creation snapshotting, deduplication, history ordering, and service
methods
Closes#35
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Audited all PHP files for US English spellings per CLAUDE.md convention.
Fixed "Organize" → "Organise" in Mcp/Servers/Marketing.php docstring.
CSS/JS identifiers (borderColor, backgroundColor, transition-colors) and
array keys that form interface contracts with the host-uk/core package are
unchanged as they are not prose.
Closes#36
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `agentic.plan_retention_days` config (default 90 days via AGENTIC_PLAN_RETENTION_DAYS env)
- Add SoftDeletes and `archived_at` timestamp to AgentPlan model
- Add migration for `deleted_at` and `archived_at` columns on agent_plans
- Create `agentic:plan-cleanup` command with --dry-run and --days options
- Schedule retention cleanup to run daily via service provider
- Register PlanRetentionCommand in ConsoleBooting handler
- Add PlanRetentionTest feature test suite covering all retention scenarios
- Fix archive() to store archived_at as dedicated column (not metadata string)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace setup-php action with pre-built container.
Eliminates ~50s setup overhead per matrix job.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace anonymous class extending ApiKey with Mockery mock to avoid
requiring php-api package at load time
- Replace with() named args with withSomeOfArgs() for Mockery compat
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- interpolateVariables: use string concatenation for triple-brace
placeholders instead of PHP string interpolation which only
produces single braces
- AgentToolRegistryTest: replace Cache::fake() (not available) with
Cache::flush() since array driver is already in-memory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>