@exellix/ai-skills
v5.8.0
Published
Foundational skill execution layer for exellix ecosystem using @x12i/ai-gateway with FlexMD 2.0 support and Catalox as the catalog store
Readme
@exellix/ai-skills
Foundational skill execution layer for the exellix ecosystem: templates live in the Catalox native catalog ai-skills, execution uses @x12i/ai-gateway, and responses support FlexMD 2.0 structured text.
🚀 Env-Ready: Provider keys can be loaded from .env automatically. catalox is required at construction time (see Quick Start).
Documentation
| Topic | Doc |
|--------|-----|
| Flex-MD vs gateway parsing, integration tests | docs/FLEX_MD_AND_TESTING.md |
| Catalox integration & env notes | docs/CATALOX_PEER_GUIDE.md · Environment (Firebase & Catalox v4) |
| Activix integration best practices | .docs/activix-integration-best-practices-checklist.md |
| Per-package log level (AI_SKILLS_LOGS_LEVEL) | Logging below and @x12i/logxer on npm |
| Gateway templates (v4), invoke() vs invokeChat(), templateRendering | docs/GATEWAY_TEMPLATE_PROTOCOL_V4.md |
| This package: workingMemory, templateRenderOptions, client templateRendering | docs/AI_SKILLS_GATEWAY_TEMPLATES.md |
| Invoke execution metadata (provider, modelUsed, effectiveModelConfig, …) | docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md |
| Graph execution context (graphId, nodeId, identity mapping) | docs/GRAPH_EXECUTION_SUPPORT.md |
| Gateway invoke preflight (analyzeSkillRequest, FuncX ≥ 4.0.1) | docs/SKILL_REQUEST_ANALYSIS.md |
| External follow-ups (Activix persistence vs gateway envelope) | docs/AI_GATEWAY_FEATURE_REQUESTS.md |
Features
- Unified Skill Runner: Execute skills via metadata, not hardcoded handlers
- FlexMD 2.0 Output: Always returns structured-text format with parsed payloads
- Catalox-native templates: Instruction and prompt bodies are read from the Firestore native catalog
ai-skillsvia@x12i/catalox; the gateway receives inline template text (not nx-content registry keys for skills) - Provision from disk:
npm run catalox:provision-ai-skillsmerges.metadata/skills/*.instructions.md/*.prompt.mdinto that catalog and setsplanned|draft|publishedstatus - Presentation API: Load pretty markdown for editors, save with lossless storage normalization (
getSkillTemplatesForPresentation,updateSkillTemplatesFromPresentation,getSkillTemplateInputs); see Skill templates (Catalox) - Catalog audit fields (storage only): Catalox rows may store optional audit template text for editors; this package only executes
runSkill(no built-in audit pass). - Gateway integration:
@x12i/ai-gatewayfor LLM calls only; the built-in gateway hasenableContentRegistry: false— skill templates are never loaded from nx-content / GitHub in this package - Activity tracking: With
enableActivityTracking, the gateway persists activities via@x12i/activix; this SDK enriches rows withcost/costStatusand contract-shapedouter.output.parsedwhen applicable (see Cost and output contract) - Model Configuration: Per-request model selection and parameter control (temperature, maxTokens, etc.)
- Full template pipeline (default): Uses
gateway.invoke()with inline bodies; the gateway message builder renders tokens (downstream stack; e.g. Rendrix).runSkillpopulatesworkingMemory.inputand can passtemplateRenderOptions,templateTokens, or client-leveltemplateRendering(see docs/AI_SKILLS_GATEWAY_TEMPLATES.md). - Execution engine catalog: Optional
aiEngineIdechoes the Cataloxai-enginesrow (ai-gatewayonly; provision withnpm run catalox:provision-ai-engines) for discovery andidentity— see Execution engines (aiEngineId). - Request analysis (implementation-phase):
analyzeSkillRequestsimulates the gateway packet and reviews it via FuncX — not chained intorunSkill; see docs/SKILL_REQUEST_ANALYSIS.md.
Installation
npm install @exellix/ai-skillsThis package depends on @x12i/ai-gateway, @x12i/catalox, @x12i/logxer, @x12i/env, @x12i/rendrix, and firebase-admin (for Firestore-backed Catalox). @x12i/catalox ≥ 4.0 expects Node 20+. Align firebase-admin / @x12i/catalox versions with your app if you share a Firebase app instance.
@x12i/ai-gateway ≥ 9.3.0 is recommended: invoke metadata includes routing fields (≥9.1.1), rejection metadata on failures (≥9.1.2), and normalized billing (costUsd, cost, costStatus: priced | unpriced) on success (≥9.3). See docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md. Every runSkill call must include agentId, jobTypeId, and taskTypeId (Activix linkage — no package defaults).
CI: npm run test:ci runs npm run build and npm run test:unit (no live Firestore or real LLM). Full npm test still runs test:integration, which may append live catalog tests when env enables them.
Custom admin flows that call Catalox with createCatalog, bindCatalogToApp, or similar before an app binding exists should set superAdmin: true on CataloxContext (see defaultAiSkillsCataloxContext overrides and docs/CATALOX_PEER_GUIDE.md).
Publishing: the npm tarball is limited to dist/, README.md, and erc-manifest.json via the files field in package.json (avoids shipping tests, logs, or local scratch files).
Environment variables (Firebase & Catalox v4)
createCataloxFromEnv() in this package delegates credential resolution to @x12i/catalox/firebase (same precedence as upstream Catalox v4).
| Variable | Notes |
|----------|--------|
| FIREBASE_PROJECT_ID | Required for this package’s createCataloxFromEnv() wiring: set it in .env or the process environment. Project id is not inferred from GCLOUD_PROJECT, GOOGLE_CLOUD_PROJECT, or service-account JSON. |
| GOOGLE_SERVICE_ACCOUNT_BASE64 | Recommended for CI and scripts: standard Google service account JSON, base64-encoded, passed to Admin cert(...). |
| FIRESTORE_DATABASE_ID | Optional named Firestore database id; omit for the default database. |
Not supported in Catalox v4: an environment variable (or secret-token _path field) that points at a service-account JSON file on disk. Use GOOGLE_SERVICE_ACCOUNT_BASE64, Application Default Credentials (workload identity, GOOGLE_APPLICATION_CREDENTIALS, etc.), or load a key file in your application and pass cert(...) / Catalox’s serviceAccountPath bootstrap option (caller-supplied string only).
Provider keys for the default gateway (OPENAI_API_KEY, GROK_API_KEY, OPEN_ROUTER_KEY / OPENROUTER_API_KEY) are unchanged — see Troubleshooting below.
Quick Start
0) Prerequisites
- Node.js 20+ — matches
@x12i/cataloxengine requirements. - Firebase / Firestore — set
FIREBASE_PROJECT_ID(see above) andGOOGLE_SERVICE_ACCOUNT_BASE64.createCataloxFromEnv()uses@x12i/catalox/firebaseunder the hood. You can instead constructCataloxwithcreateCataloxfrom@x12i/catalox/@x12i/catalox/embedderand your ownFirestoreinstance. - Provision the catalog (merges in-repo
.metadata/skillsinto Catalox):
npm run catalox:provision-ai-skills
npm run catalox:provision-ai-engines- At least one LLM provider key (
OPENAI_API_KEY,GROK_API_KEY, orOPEN_ROUTER_KEY/OPENROUTER_API_KEY) for@x12i/ai-gateway.
1) Create the client
import { ExellixSkillsClient, RunSkillRequest, RunSkillResponse, createCataloxFromEnv } from "@exellix/ai-skills";
const catalox = createCataloxFromEnv();
const skills = new ExellixSkillsClient({
catalox,
enableActivityTracking: true,
});You can also construct Catalox yourself (createCatalox from @x12i/catalox or @x12i/catalox/embedder with your firestore / firebaseApp) and pass options.catalox. An external gateway is optional; catalox is always required.
2) Run a skill
// Using the RunSkillRequest interface
const runSkillRequest: RunSkillRequest = {
skillKey: "skills/professional-answer",
input: "Question: What is the best way to migrate from X to Y?",
variables: { orgName: "ACME", audience: "security team" },
jobId: "job-123",
taskId: "task-456",
agentId: "agent-abc",
jobTypeId: "your-job-type-id",
taskTypeId: "your-task-type-id",
};
const res: RunSkillResponse = await skills.runSkill(runSkillRequest);
// Access FlexMD payloads
console.log(res.flexMd.payloads.shortAnswer);
console.log(res.flexMd.payloads.fullAnswer);2b) Execution engines (aiEngineId)
runSkill always invokes @x12i/ai-gateway with inline Catalox template text; Rendrix runs inside the gateway. Optional aiEngineId must be omitted, blank, or ai-gateway — it is merged into gateway.invoke identity and matches the single row in the Catalox ai-engines catalog (provisioned for listing / UIs). Unknown values throw at resolve time.
2a) Run a skill with model configuration
// Override model and generation parameters per-request
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Question: What is the best way to migrate from X to Y?",
variables: { orgName: "ACME" },
jobId: "job-123",
taskId: "task-456",
agentId: "agent-abc",
jobTypeId: "your-job-type-id",
taskTypeId: "your-task-type-id",
modelConfig: {
model: "gpt-4-turbo",
temperature: 0.7,
maxTokens: 2000,
topP: 0.9
}
});2b) Optional: parser / template overrides
Per skill run you can pass templateRenderOptions (merged on gateway defaults) and templateTokens (highest-priority overlay). On the client, templateRendering sets defaults for every invoke. Details and examples: docs/AI_SKILLS_GATEWAY_TEMPLATES.md.
2c) Optional: identity propagation
identity is per-request runtime context from your application (trace ids, tenant, graph/node ids, etc.). It is not the SDK client’s constructor options, not env/config for this package, and not “package identity” from package.json—unless you explicitly copy such values into this object when you build the request.
identity vs runContext: The gateway invoke() API uses the property name identity (see @x12i/ai-gateway). Activix stores that same envelope on activity documents as BSON runContext. This SDK passes identity through to the gateway; you do not set runContext on runSkill inputs here. Responses expose the envelope as identity when the gateway returns it (and resolution may also consider metadata shapes that use runContext).
You can pass an identity object to runSkill() to propagate caller identity context downstream.
- If you provide
identity, the client forwards it as-is to the gateway. - The client will add
identity.skillIdfrom the executing request only if it is missing. - The
RunSkillResponseincludesidentityonly if downstream returns one. If downstream does not provideidentity, it will be omitted (no fallback).
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Question: ...",
jobId: "job-123",
taskId: "task-456",
agentId: "agent-abc",
jobTypeId: "your-job-type-id",
taskTypeId: "your-task-type-id",
skillId: "node-q0",
identity: {
traceId: "trace-123",
userId: "user-456"
// skillId is optional here; if omitted, it will be set from `skillId` above
}
});
// Only present when downstream returned it:
console.log(res.identity);
// Token usage + cost (from gateway metadata); always on metadata.usage for every run.
// costUsd when priced; costStatus when usage exists but no price ("unpriced") or async pricing ("deferred").
console.log(res.metadata.usage, res.metadata.costUsd, res.metadata.costStatus, res.metadata.modelUsed);
// When `identity` is returned, the same snapshot is nested under `identity.aiSkillsLlm` for Activix-style
// chaining—forward the whole `identity` to the next `runSkill` so pipelines retain billing context.
console.log(res.identity?.aiSkillsLlm);3) Max output tokens (maxTokens), modelConfig, and trace visibility
This SDK does not compute or default maxTokens. If you set runSkill({ modelConfig: { maxTokens: N } }), that object is forwarded verbatim on gateway.invoke({ modelConfig }) (same for temperature, model, provider-specific fields, etc.). If you omit modelConfig or omit maxTokens, the effective cap comes from @x12i/ai-gateway / the provider (see docs/AI_GATEWAY_FEATURE_REQUESTS.md for documenting defaults upstream).
Dynamic per call: compute limits in your orchestrator and pass a fresh modelConfig every invoke; nothing in this package caches values between calls.
| What you need | Where it appears |
|---------------|------------------|
| What you sent (modelConfig, timeoutMs) | Trace-only: debugTrace.invokeRequest when executionMode: "trace" or diagnostics.includeDebugTrace |
| Requested / echoed cap | debugTrace.usage.maxTokensRequested (from your modelConfig.maxTokens when set; may align with gateway metadata when present) |
| What ran (model id, token counts) | debugTrace.modelUsed, debugTrace.usage, routing ids |
const budget = estimateMaxOutputTokens(someContext); // your policy
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Question: ...",
jobId: "job-1",
taskId: "task-1",
modelConfig: {
maxTokens: budget,
temperature: 0.5,
model: "gpt-4-turbo",
},
executionMode: "trace",
});
console.log(res.debugTrace?.invokeRequest?.modelConfig?.maxTokens);
console.log(res.debugTrace?.usage.maxTokensRequested);On gateway failures with trace mode enabled, SkillExecutionTraceError carries diagnostics.trace with the same shapes where available; metadata attached on the thrown error is merged when present (see metadataFromInvokeError export).
Cost and output contract (Run Analysis)
Studio / graph Run Analysis expects activities and responses to explain billing and structured output. This package normalizes both on every successful runSkill and best-effort patches the persisted Activix row when metadata.activityId is present.
Cost reporting (metadata.costUsd / metadata.costStatus)
Gateway invoke() metadata (≥ 9.3) uses costUsd / cost when priced and costStatus: "priced" | "unpriced". This SDK never echoes gateway "priced" — priced runs expose costUsd only. When usage exists but no USD total is known, costStatus: "unpriced" is set (gateway explicit flag or SDK fallback).
| Gateway / router situation | Gateway metadata | On RunSkillResponse.metadata | Activix record (gateway completeRecord + SDK patch) |
|----------------------------|-------------------|-------------------------------|------------------------------------------------------|
| Priced (router, catalog, or ai-tools) | costUsd, cost, costStatus: "priced" | costUsd (no costStatus) | Top-level cost, costStatus: "priced", outer.metadata billing slice, outer.cost.usd |
| Usage, no price | costStatus: "unpriced" | costStatus: "unpriced" | costStatus: "unpriced" + token usage on outer.metadata / response.metadata |
| Legacy async pricing | costStatus: "deferred" (older gateways) | costStatus: "deferred" | same |
Gateway logSuccess writes billing on the activity row during invoke() when enableActivityTracking is on (default). patchActivixActivityAfterSkill then merges the same billing slice plus outputContract parsed fields so Run Analysis and Activix queries stay aligned with RunSkillResponse.metadata.
metadata.usage is always populated (zeros when the gateway omits token fields). The same billing slice is mirrored on identity.aiSkillsLlm when the gateway returns identity, and on debugTrace in trace mode.
Output contract (outputContract)
outputContract is per invoke, not a global list for all skills. Pass the field names your graph node or skill expects on parsed / outer.output.parsed — typically from graph inputs.outputContract (via @exellix/ai-tasks / graph-engine).
Supported shapes: string[], { fields: string[] }, { keys: string[] }, or { required: string[] }.
After FlexMD / gateway parsing (and local parseFlexMd fallback), the SDK fills missing contract keys from:
flexMd.payloads- Markdown section headings (
### Short Answer→shortAnswer)
Examples (illustrative — use the contract for that skill/node):
// professional-answer–style skill
await skills.runSkill({
skillKey: "skills/professional-answer",
outputContract: ["shortAnswer", "fullAnswer", "assumptions", "unknowns", "evidence"],
// ...jobId, taskId, agentId, jobTypeId, taskTypeId, input, ...
});
// professional-decision–style skill
await skills.runSkill({
skillKey: "skills/professional-decision",
outputContract: ["decision", "score", "rationale", "risks"],
// ...
});This package does not auto-map skillKey to a catalog contract; upstream must pass outputContract when structured fields are required. Catalog payload names are documented in docs/metadata.md (requiredPayloads per skill).
Exports (for orchestrators and tests): normalizeSkillBillingFromGatewayMetadata, enrichParsedForOutputContract, patchActivixActivityAfterSkill, types SkillCostStatus, OutputContract.
Built-in skills (catalog)
Authoritative rows are defined in code as AI_SKILLS_CATALOG_ITEMS and written to Catalox by npm run catalox:provision-ai-skills. Typical runnable keys:
skills/professional-answer— structured professional answer (FlexMD payloads)skills/professional-decision— structured decision output- Additional catalog rows exist for other packaged skills; use
listCatalogSkills()(all statuses),listPublishedSkills()(published only), or CataloxlistCatalogItemsfor the live list.
runSkill takes skillKey like skills/professional-answer. Bodies are not resolved as nx-content registry keys for execution; they are loaded from Catalox and sent to the gateway as inline template strings.
Skill templates (Catalox)
Provisioning (disk → Firestore)
Keep canonical markdown under .metadata/skills/<id>.instructions.md and .metadata/skills/<id>.prompt.md, then run:
npm run catalox:provision-ai-skillsThat merges file contents into the native catalog ai-skills, sets instructionsText / promptText, and computes status: planned | draft | published (runnable when both bodies exist and status is draft or published).
Presentation layer (read / edit)
Low-level helpers live under @exellix/ai-skills exports from ./catalox (e.g. normalizeForStorage, toPresentationMarkdown, extractTemplateTokensFromTexts).
On ExellixSkillsClient:
| Method | Purpose |
|--------|---------|
| getSkillTemplatesForPresentation(skillKey, { includeAudit? }) | Catalox read → markdown formatted for editors |
| updateSkillTemplatesFromPresentation(skillKey, patch, options?) | Editor markdown → lossless storage normalization → Catalox upsert (requires write Catalox context) |
| getSkillTemplateInputs(skillKey) | Union of {{token}} placeholders from raw stored bodies; treats input as the primary payload token |
| listCatalogSkills(options?) | Lists every catalog row (all status values); forwards Catalox query options (e.g. limit) |
| upsertSkillCatalogItem(input, options?) | Create/update full row (metadata + optional markdown); optional ifNotExists for strict create |
| softDeleteSkillCatalogItem(skillKey) | Clears bodies and audit fields and sets planned (no hard delete) |
Write access: updates use batchUpsertNativeCatalogItems; use a Catalox context with permission to write the catalog (e.g. provision-style god mode or an app binding with write). This package does not create credentials.
Skill key usage
await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Your question here...",
});Catalox item ids are the short key (e.g. professional-answer); the client accepts either skills/professional-answer or the short id where helpers normalize.
Troubleshooting
❌ options.catalox is required
Pass catalox into new ExellixSkillsClient({ catalox, ... }). Use createCataloxFromEnv() from this package, createCataloxFromEnv() from @x12i/catalox/firebase, or createCatalox({ firestore, firebaseApp }) from @x12i/catalox / @x12i/catalox/embedder.
❌ Skill is planned or “incomplete templates”
Run npm run catalox:provision-ai-skills (or write bodies via updateSkillTemplatesFromPresentation) so instructionsText and promptText are non-empty and status is draft or published.
❌ Provider / env validation
At least one of OPENAI_API_KEY, GROK_API_KEY, OPEN_ROUTER_KEY, OPENROUTER_API_KEY is required when the client builds the default gateway. Firebase / Catalox variables are summarized above.
Logging (ai-skills / @x12i/logxer)
This package uses @x12i/logxer with a stable env prefix AI_SKILLS.
| Item | Detail |
|------|--------|
| Canonical env var | AI_SKILLS_LOGS_LEVEL |
| Fallback | AI_SKILLS_LOG_LEVEL is used only if AI_SKILLS_LOGS_LEVEL is unset |
| Default (both unset) | warn — not silent; you will see warnings and errors |
| Silence this package | Set AI_SKILLS_LOGS_LEVEL=off (or none / silent) |
| More detail | info, debug, or verbose (case-insensitive; see @x12i/logxer docs) |
Cross-cutting sinks (console, file, JSON, unified app config) are configured by the host app, not per this prefix — see the @x12i/logxer README.
.env: with default autoLoadDotenv, the client loads .env from the current working directory using loadDotenv from @x12i/env.
# Examples
export AI_SKILLS_LOGS_LEVEL=info
export AI_SKILLS_LOGS_LEVEL=debug
export AI_SKILLS_LOGS_LEVEL=offExellixSkillsClient passes packageName into the logger (default "AI-SKILLS"); that label appears in log metadata alongside the [AI-SKILLS] message prefix.
Testing (integration + templates)
This repo is ESM ("type": "module"). Use the npm scripts below (they run via tsx for .ts entrypoints).
Default test gate (npm test): npm run build then npm run test:integration (may append live catalog tests when env enables them). npm run test:ci runs npm run build and npm run test:unit (mocked gateway / no Firestore / no LLM).
npm test # build + integration harness (see run.ts)
npm run test:ci # build + deterministic unit slice (CI-safe)
npm run test:unit # same unit slice as test:ci (without build)
npm run test:integration # full integration harness (run.ts)
npm run catalox:provision-ai-skills # merge .metadata/skills → Firestore (before live tests)
npm run catalox:provision-ai-engines # merge in-repo engine rows → native `ai-engines` (before engine catalog live test)
npm run live:professional-answer # smoke: professional-answer via Catalox + default gateway LLM
npm run test:real # extra real LLM scenarios; needs .env + valid Firebase credentialsLive tests (opt-in, test/run.ts)
Some cases use real Catalox (Firestore-backed catalog) and the same integration slice can invoke real LLM calls when provider keys are set. They are off by default so CI does not require a provisioned Firebase project.
Set AI_SKILLS_LIVE_TESTS=1 (or true / yes / on) before npm run test:integration to append the live cases registered in test/run.ts:
- Catalox
ai-skillslist — read-only Firestore list sanity check. - Catalox
ai-engineslist — verifiesnpm run catalox:provision-ai-engineswrote theai-gatewayrow (read-only). - Skill template presentation — read-only Catalox + presentation helpers.
Why not in @x12i/catalox: only this package’s test harness reads AI_SKILLS_LIVE_TESTS; Catalox does not gate downstream CI.
Optionally run npm run test:real before a major release (broader LLM + client scenarios; needs Firebase + provider keys in .env).
Flex-MD log noise (extractJsonFromFlexMd / require(...) is not a function): that path lives in @x12i/ai-gateway, not in this package’s parseFlexMd. See docs/FLEX_MD_AND_TESTING.md for the split of responsibilities and acceptance notes.
Model Configuration
You can control which model is used and how it generates responses on a per-request basis using the modelConfig field.
Model Configuration Options
interface ModelConfig {
/** Model identifier (e.g., "gpt-4-turbo", "claude-3-opus", "gpt-3.5-turbo") */
model?: string;
/** Model ID (alternative to model name, for provider-specific model IDs) */
modelId?: string;
/** Provider name (e.g., "openai", "anthropic") */
provider?: string;
/** Temperature for generation (0.0 to 2.0) - controls randomness */
temperature?: number;
/** Maximum tokens to generate */
maxTokens?: number;
/** Top-p (nucleus) sampling parameter (0.0 to 1.0) */
topP?: number;
/** Frequency penalty (-2.0 to 2.0) */
frequencyPenalty?: number;
/** Presence penalty (-2.0 to 2.0) */
presencePenalty?: number;
/** Stop sequences (array of strings) */
stop?: string[];
/** Additional provider-specific parameters */
[key: string]: any;
}Usage Examples
Basic Model Selection
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Your question here",
modelConfig: {
model: "gpt-4-turbo"
}
});Full Model Configuration
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Your question here",
modelConfig: {
model: "gpt-4-turbo",
temperature: 0.7,
maxTokens: 2000,
topP: 0.9,
frequencyPenalty: 0.5
}
});Provider Override
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Your question here",
modelConfig: {
provider: "anthropic",
model: "claude-3-opus",
temperature: 0.3
}
});Dynamic maxTokens per invoke
function maxTokensForStep(step: "summary" | "detail") {
return step === "summary" ? 800 : 4000;
}
const res = await skills.runSkill({
skillKey: "skills/professional-answer",
input: "Your question here",
jobId: "job-1",
taskId: "task-1",
modelConfig: {
maxTokens: maxTokensForStep("detail"),
temperature: 0.5,
},
});Notes
modelConfigis optional. If omitted, gateway/router defaults apply for each field.- This package does not merge a global default
modelConfigat construction time; onlytemplateRenderinghas client-wide defaults for template rendering. - The gateway and provider enforce valid ranges (e.g. temperature).
API Reference
ExellixSkillsClient
Constructor options (essential)
import type { Catalox } from "@x12i/catalox";
import type { TemplateRenderOptions } from "@x12i/ai-gateway";
interface ExellixSkillsClientOptions {
/** Required. Skill bodies are read from the native `ai-skills` catalog. */
catalox: Catalox;
/** Optional; defaults to catalog app id `ai-skills`. */
cataloxAppId?: string;
packageName?: string;
enableActivityTracking?: boolean;
/** Optional external gateway; skill templates still load from `catalox`. */
gateway?: AIGateway;
forceLocal?: boolean;
forceCreateRootDirectory?: boolean;
templateRendering?: TemplateRenderOptions;
autoLoadDotenv?: boolean;
testMode?: boolean;
disableActivityTrackingInTests?: boolean;
}The built-in AIGateway uses enableContentRegistry: false; GITHUB_* is not required for ExellixSkillsClient initialization.
Methods
runSkill<T>(input: RunSkillRequest): Promise<RunSkillResponse<T>>— loads templates from Catalox, invokes gateway with inline bodies. OptionaloutputContractenrichesparsedand the Activix activity row (see Cost and output contract).getSkillTemplatesForPresentation(skillKey, opts?)— editor-oriented markdown + metadata.updateSkillTemplatesFromPresentation(skillKey, patch, opts?)— save edited markdown (lossless storage normalization); requires Catalox write context.getSkillTemplateInputs(skillKey)—{{token}}discovery from stored bodies.listPublishedSkills(options?)— Catalox rows withstatus === "published".listCatalogSkills(options?)— all catalog rows (any status); optional Catalox list query options.upsertSkillCatalogItem(input, options?)— create/update catalog metadata and template bodies;options.ifNotExistsenforces create-only.softDeleteSkillCatalogItem(skillKey)— clears template and audit text and setsplanned.
Template bodies are loaded only through runSkill (and the same Catalox fetch used there): pass skillKey; there is no separate “peek at one section string” API.
Diagnostic methods (gateway)
testContentRegistryConnection(),discoverContentStructure(),diagnoseSkillContent,listAvailableContent,runContentRegistryDiagnostics— forwarded to AIGateway. With the default gateway, nx-content is disabled; these calls are mainly useful if you inject a customgatewaywith its own content registry.
For {{token}} discovery on stored bodies without invoking the LLM, use getSkillTemplateInputs(skillKey) (Catalox read + parse).
FlexMD 2.0 Format
Primary skills are designed around structured markdown (headings and/or Flex-MD style markers). A common explicit shape is:
[[professional-answer]]
@payload:shortAnswer
Brief answer here...
@payload:fullAnswer
Detailed answer here...
@payload:assumptions
List of assumptions...The gateway normalizes responses with its own Flex-MD extraction. When it does not return parsed fields, this SDK uses parseFlexMd (src/utils/flex-md-parser.ts), which prefers the flex-md package via ESM import() and falls back to parsing [[frame:...]] + @payload: blocks.
When you pass outputContract, the SDK also maps markdown ### Section headings to camelCase keys (e.g. ### Full Answer → fullAnswer) so professional-answer templates that use headings still populate parsed even without @payload: markers. See Cost and output contract.
Details and caveats: docs/FLEX_MD_AND_TESTING.md.
Requirements
- Node.js 20+ (see
package.jsonengines; aligns with@x12i/catalox) @x12i/catalox≥ 4 and Firebase access for theai-skillsnative catalog (seecreateCataloxFromEnvand Environment (Firebase & Catalox v4))@x12i/ai-gateway(seepackage.jsonfor the supported range)@x12i/activix(version aligned with@x12i/ai-gatewayinpackage.json) when you rely on activity tracking- At least one LLM provider key when using the default constructed gateway
- Optional:
.metadata/skillson disk to feednpm run catalox:provision-ai-skills
Related runtime packages
Task and graph orchestration live in sibling packages, not in @exellix/ai-skills:
@exellix/ai-tasks— task orchestration and task-level runtime wiring@exellix/graph-engine— graph execution and graph-level runtime wiring; forward each node’sinputs.outputContractonrunSkillwhen Run Analysis or downstream validation require structuredparsedfields@exellix/exellix-runtime— root runtime composition (for example loading composedruntimeObjectsfor debug tooling)
Older scopes such as @woroces/*, worox, worex, and graph packages named worox-graph / worex-graphs are not used here; there are no intentional aliases or compatibility shims for those names in this repository.
License
ISC
