Log a warning for each AI provider registered without an API key so
that misconfiguration is surfaced at boot time (not silently on the
first API call). Each message names the environment variable to set:
ANTHROPIC_API_KEY – Claude
GOOGLE_AI_API_KEY – Gemini
OPENAI_API_KEY – OpenAI
Providers without a key remain registered but are marked unavailable
via isAvailable(), preserving backward compatibility.
- Add Log::warning() calls in registerProviders() for empty keys
- Extend AgenticManagerTest with a dedicated 'API key validation
warnings' describe block (7 new test cases)
- Update DX-002 in TODO.md as resolved
Closes#29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Updated workspace_id error messages in all MCP tools to include
actionable guidance and a documentation link. Affected tools:
PlanCreate, PlanGet, PlanList, StateSet, StateGet, StateList,
SessionStart.
Resolves DX-001 from TODO.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Track progress in a per-batch JSON state file after each article so
a mid-run crash leaves a recoverable checkpoint
- Add `maxRetries` parameter to generateBatch() with per-article retry
loop (default: 1 extra attempt)
- Add `resumeBatch()` to re-process only failed/pending articles,
skipping those already successfully generated in a prior run
- Add `loadBatchProgress()` public method for inspecting state
- State stores per-article status, attempt counts, error messages,
and timestamps for full observability
Tests: 6 new scenarios covering state persistence, resume capability,
retry logic, and the no-state error case
Closes#27
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap stream() in try/catch to prevent silent failures.
On exception, log the error and yield a structured error event:
['type' => 'error', 'message' => string]
Adds tests for connection errors, runtime exceptions, error event
format, and Log::error invocation. Closes ERR-001 in TODO.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cache the list of permitted tool names in `AgentToolRegistry::forApiKey()`
using a 1-hour TTL to avoid O(n) filtering on every request (PERF-002).
- Add `Cache::remember()` in `forApiKey()` storing tool names keyed by API
key ID (`agent_tool_registry:api_key:{id}`)
- Add `flushCacheForApiKey(int|string $id)` for explicit invalidation
- Add `CACHE_TTL` constant (3600 s) for easy tuning
- Invalidate cache in `AgentApiKeyService::updatePermissions()` and `revoke()`
so permission changes take effect immediately
- Add `tests/Unit/AgentToolRegistryTest.php` covering cache hit/miss,
per-key isolation, scope filtering, TTL constant, and flush behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolves#23
- Replace per-dependency `AgentPhase::find()` loop with a single
`AgentPhase::whereIn('id', $dependencies)->get()` call, reducing
query count from N to 1 for any number of dependencies
- Short-circuit early when dependencies list is empty to avoid
unnecessary query at all
- Add tests: empty deps, skipped-dep passthrough, single-query
assertion, blocker shape validation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace individual __get expectations with a single closure that handles
all property access. Fixes ErrorException on undefined property access
with Mockery mocks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Verify agent_sessions.session_id: unique() constraint creates an
implicit unique index (agent_sessions_session_id_unique) which is
sufficient for string lookups; no additional index required
- Drop redundant agent_plans_slug_index: the unique() constraint on
slug already provides agent_plans_slug_unique covering all lookups
- Add compound (workspace_id, slug) index on agent_plans for the
common routing pattern WHERE workspace_id = ? AND slug = ?
- Verify agent_workspace_states.key: already indexed via ->index('key')
in migration 000003; no additional index required
- Mark DB-002 as resolved in TODO.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace hardcoded cache key in ForAgentsController with a config-based
key (`mcp.cache.for_agents_key`) and configurable TTL
(`mcp.cache.for_agents_ttl`). This prevents collisions with other modules
or packages that might use the same flat cache key.
- Add `cacheKey()` method on ForAgentsController, reads from config
- Add `cache` section to config.php with default key and TTL
- Dynamic Cache-Control max-age now follows the configured TTL
- Add ForAgentsControllerTest covering key customisation,
cache storage, invalidation, TTL, and response structure
Refs: TODO.md CQ-003
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch View/Modal/Admin/ApiKeyManager.php from Core\Api\Models\ApiKey
to Core\Mod\Agentic\Models\AgentApiKey and AgentApiKeyService, bringing
the workspace-owner admin UI into consistency with all other services.
Changes:
- Replace Core\Api\Models\ApiKey import with AgentApiKey + AgentApiKeyService
- Use AgentApiKeyService::create() for key generation
- Use AgentApiKey::forWorkspace() scoping in revokeKey() and render()
- Rename newKeyScopes → newKeyPermissions, toggleScope → togglePermission
- Expose availablePermissions() from AgentApiKey for the create form
- Update blade template: permissions field, getMaskedKey(), togglePermission,
dynamic permission checkboxes from AgentApiKey::availablePermissions()
- Add tests/Feature/ApiKeyManagerTest.php with integration coverage
- Mark CQ-002 resolved in TODO.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The processOutput() method was a stub with no implementation. The
ContentProcessingService dependency it accepted is from the external
host-uk/core package and its API is not available here. Content
is already persisted via markCompleted() so no output processing
was ever performed.
Removes:
- processOutput() stub method
- ContentProcessingService import and handle() parameter
- target_type/target_id guard block that called the stub
Adds unit tests covering: prompt validation, entitlement checks,
provider availability, task completion metadata, usage recording,
and template variable interpolation.
Closes#17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `Builder $query` parameter type and `: Builder` return type to
18 query scopes across 8 model files. Import `Illuminate\Database\Eloquent\Builder`
in each affected model.
Affected models: Task, AgentSession, AgentApiKey, AgentPhase, AgentPlan,
Prompt, AgentWorkspaceState, WorkspaceState.
Closes#16
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Direct git clone of ../php-framework avoids shell escaping
issues with dynamic PHP-based path extraction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch php -r argument to single quotes so PHP dollar signs
are not interpreted by bash. Pipe output to while-read loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The PHP variables inside php -r need \$ escaping, but shell
variables outside need bare $ for command substitution and
variable expansion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Feature test covering PromptVersion creation, relationships (prompt,
creator), restore() rollback method, and version history tracking. Also
add idempotent migration for prompts and prompt_versions tables required
by the test suite.
Closes#15
The Forgejo act runner caches reusable workflow definitions,
preventing updates from being picked up. Inline the workflow
with dependency checkout step.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reusable php-test.yml now detects pest/phpunit/pint availability
and clones path dependencies using the runner token.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add phpunit.xml for standalone test execution.
Apply Laravel Pint formatting fixes across all source files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The .github/workflows/ci.yml has a visibility guard that evaluates
to false on Forgejo, causing all jobs to fail in 1 second.
Co-Authored-By: Charon <charon@lethean.io>