* feat(skills): add Ponytail minimalism skill as external catalog entry
- Add 'external' SkillCategory + SkillArea
- Register ponytail (MIT, DietrichGebert/ponytail) in CURATED_SKILLS
- Generator: external skills carry content in custom block, no api/cli body
- Generate skills/ponytail/SKILL.md with original content preserved
- Update catalog test counts 45 -> 46
* fix(skills+memory): builtin handler fallback in executor, skip vector upsert for deleted memories
- skills: Next.js compiles SkillExecutor into multiple chunks (own singleton
each); route chunk lacked builtin handlers registered at startup via
instrumentation. execute() now falls back to builtinSkills registry, so
POST /api/skills/executions works for file_read/web_fetch/etc.
- memory: scheduleVectorUpsert is fire-and-forget and embeddings are slow;
health-check verify (create->delete test memory) left queued upserts
failing with 'memory not found' every 30s. Check existence before embedding
and skip quietly.
* fix(skills): encode tool names with @ and . for providers rejecting them
Skill tools were advertised as 'name@version' (e.g. test-fr2@1.0.0), but
DeepSeek/Groq/OpenAI reject function names not matching ^[a-zA-Z0-9_-]+$.
Names already valid are left untouched; invalid ones are reversibly encoded
as omr_skill_<base64url> and decoded in interception before registry lookup.
* fix(combos): include DB id column in combo records for dashboard links
getCombos() selected only data/sort_order/context_cache_protection, so
combos whose JSON blob lacked an id field returned id: undefined. The
dashboard then linked to /dashboard/combos/undefined and Combo Control
Center failed with 'Combo not found'. Merge the id column into parsed
rows (authoritative, only when the blob has no id).
* fix(skills): normalize flat skill schemas to object schema for Gemini/Claude
Stored skill schemas are flat property maps ({ text: { type: string } }),
which OpenAI-compatible providers tolerate but Gemini
(function_declarations[].parameters) rejects with 'Unknown name ... Cannot
find field'. Wrap bare maps into { type: 'object', properties: {...} } for
all three tool formats.
* fix(skills): warm registry cache before skill injection in chat path
injectSkills() lists the in-memory skillRegistry, which is empty after a
cold start until something calls loadFromDatabase(). The interception path
already warms the cache (#2815); the injection path did not, so skills
were silently skipped (no_enabled_skills) for the first requests after
restart. Warm the cache for the chat owner before injection.
---------
Co-authored-by: Egor <egorich-print@users.noreply.github.com>
* fix(skills): register cli-skill-collector in the agent-skills catalog (#6294 shipped the dir only)
* chore(skills): regenerate cli-skill-collector SKILL.md via the generator, preserving the #6294 authored workflow in the custom block
* fix(skills): derive coverage totals from the id lists + align remaining count assertions (45 catalog / 21 cli)
* fix(skills): SkillCoverage totals are number, not stale literals
- Unit/integration tests: update hardcoded 42→43 in 7 test files
(agentSkillTools-mcp, agentSkills-catalog, agentSkills-generator,
agent-skills-content, agent-skills-discovery, listCapabilities-a2a)
to match the 43rd skill (config-codex-cli) added in the previous commit.
- Include CONFIG_SKILL_IDS in integration content test ALL_IDS so
skills/config-codex-cli/ is no longer "unexpected".
- listCapabilities.ts: change totalSkills from literal 42 to catalog.length
so it adapts to catalog growth automatically.
- computeCoverage assertions: include config.have in totalSkills check.
- CI: switch E2E artifact from upload-artifact path (ambiguous stripping)
to explicit tar archive. Fixes "Could not find a production build in
./.build/next" — the previous approach's download path was double-nested
(.build/next/next/...) due to upload-artifact LCA computation. tar -czf
stores .build/next/... relative to CWD; tar -xzf restores them verbatim.
- Also exclude .build/next/cache from the tar to keep archive lean.
- feat(translator): strip client_metadata in Responses→Chat translation
(Mistral 422 extra_forbidden fix); add regression test.
Integrated into release/v3.8.8. Applied review fixes: moved the SELECT 1 into a pingDb() db helper (no raw SQL in route, Hard Rule #5) + the 503 catch no longer leaks err.message (Hard Rule #12). Thanks @herjarsa!