# @exellix/ai-skills

> Foundational skill execution layer for exellix ecosystem using @x12i/ai-gateway with FlexMD 2.0 support and Catalox as the catalog store

Latest version **7.3.0** (published 2026-08-02) · exellix-license license · 0 weekly downloads

## Install

```sh
npm install @exellix/ai-skills
pnpm add @exellix/ai-skills
yarn add @exellix/ai-skills
bun add @exellix/ai-skills
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 7.3.0 |
| Published | 2026-08-02 |
| First published | 2026-05-26 |
| Weekly downloads | 0 |
| License | exellix-license |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=20 |
| Dependencies | 14 |
| Unpacked size | 585.5 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | exellix |
| Maintainers | exellix |
| Keywords | ai, llm, skills, flexmd, gateway |

## Links

- npm: https://www.npmjs.com/package/@exellix/ai-skills
- Repository: https://github.com/exellix/ai-tasks-mono-repo
- Homepage: https://github.com/exellix/ai-skills#readme
- Issues: https://github.com/exellix/ai-skills/issues
- npm.io page: https://npm.io/package/@exellix/ai-skills

## Dependencies (14)

- [@x12i/env](https://npm.io/package/@x12i/env.md) ^4.0.3
- [nx-helpers](https://npm.io/package/nx-helpers.md) ^1.5.1
- [@x12i/logxer](https://npm.io/package/@x12i/logxer.md) ^5.1.0
- [@x12i/activix](https://npm.io/package/@x12i/activix.md) ^9.0.6
- [@x12i/catalox](https://npm.io/package/@x12i/catalox.md) ^6.0.0
- [@x12i/rendrix](https://npm.io/package/@x12i/rendrix.md) ^4.3.0
- [@x12i/optimixer](https://npm.io/package/@x12i/optimixer.md) ^3.6.0
- [@x12i/ai-gateway](https://npm.io/package/@x12i/ai-gateway.md) ^11.1.4
- [@x12i/ai-profiles](https://npm.io/package/@x12i/ai-profiles.md) ^4.4.0
- [@x12i/graphenix-core](https://npm.io/package/@x12i/graphenix-core.md) ^2.17.0
- [@exellix/catalox-ai-skills](https://npm.io/package/@exellix/catalox-ai-skills.md) ^1.2.0
- [@x12i/graphenix-plan-format](https://npm.io/package/@x12i/graphenix-plan-format.md) ^2.17.0
- [@x12i/execution-memory-manager](https://npm.io/package/@x12i/execution-memory-manager.md) ^1.2.0
- [@x12i/graphenix-executable-contracts](https://npm.io/package/@x12i/graphenix-executable-contracts.md) ^2.17.0

## Alternatives

- [@expo/fingerprint](https://npm.io/package/@expo/fingerprint.md) — 6.2M weekly downloads
- [@azure/monitor-opentelemetry-exporter](https://npm.io/package/@azure/monitor-opentelemetry-exporter.md) — 850.0K weekly downloads
- [@azure/monitor-opentelemetry](https://npm.io/package/@azure/monitor-opentelemetry.md) — 624.0K weekly downloads
- [@posthog/ai](https://npm.io/package/@posthog/ai.md) — 423.3K weekly downloads
- [fakefilter](https://npm.io/package/fakefilter.md) — 63.9K weekly downloads

## Recent versions

- 7.3.0 (latest) — 2026-08-02
- 7.2.2 — 2026-07-13
- 7.2.1 — 2026-07-13
- 7.2.0 — 2026-07-13
- 7.1.1 — 2026-07-13
- 7.1.0 — 2026-07-13
- 7.0.21 — 2026-07-12
- 7.0.20 — 2026-07-11
- 7.0.19 — 2026-07-10
- 7.0.18 — 2026-07-10
- 7.0.17 — 2026-07-10
- 7.0.16 — 2026-07-10
- 7.0.15 — 2026-07-09
- 7.0.14 — 2026-07-08
- 7.0.12 — 2026-07-07
- … 62 more at https://npm.io/package/@exellix/ai-skills/versions

## 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).

> **Catalox 6:** Skill catalogs bootstrap via **Mongo** (`ensureCataloxFromEnv` / `createCataloxFromEnvAsync` from **`@exellix/catalox-ai-skills`**). Env: **`MONGO_URI`** or **`MONGODB_URI`**. See [**`documenations/catalox-6-rollout.md`**](../documenations/catalox-6-rollout.md).

## Documentation

| Topic | Doc |
|--------|-----|
| Flex-MD vs gateway parsing, integration tests | **[docs/FLEX_MD_AND_TESTING.md](docs/FLEX_MD_AND_TESTING.md)** |
| Catalox integration & env notes | **[docs/CATALOX_PEER_GUIDE.md](docs/CATALOX_PEER_GUIDE.md)** · [Environment (Mongo & Catalox 6)](#environment-mongo--catalox-6) |
| Activix integration best practices | **[.docs/activix-integration-best-practices-checklist.md](.docs/activix-integration-best-practices-checklist.md)** |
| Per-package log level (`AI_SKILLS_LOGS_LEVEL`) | **[Logging](#logging-aiskills--x12ilogxer)** below · [Logxer integration checklist](docs/LOGXER_INTEGRATION_CHECKLIST.md) · [@x12i/logxer on npm](https://www.npmjs.com/package/@x12i/logxer) |
| Gateway templates (v4), `invoke()` vs `invokeChat()`, `templateRendering` | **[docs/GATEWAY_TEMPLATE_PROTOCOL_V4.md](docs/GATEWAY_TEMPLATE_PROTOCOL_V4.md)** |
| This package: `workingMemory`, `templateRenderOptions`, client `templateRendering` | **[docs/AI_SKILLS_GATEWAY_TEMPLATES.md](docs/AI_SKILLS_GATEWAY_TEMPLATES.md)** |
| **Upgrading to `@x12i/ai-gateway` 10.x** (billing, required fields, removed behaviors) | **[docs/UPGRADING_AI_GATEWAY_10.md](docs/UPGRADING_AI_GATEWAY_10.md)** |
| Invoke execution metadata (`provider`, `modelUsed`, `effectiveModelConfig`, …) | **[docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md](docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md)** |
| Wire model on `runSkill()` (required; no alias / no gateway default) | **[docs/MODEL_INVOKE_CONTRACT.md](docs/MODEL_INVOKE_CONTRACT.md)** |
| Host invoke contract (`reasoningEffort`, no caller `maxTokens`) | **[Host responsibilities](#host-responsibilities-orchestrator--main)** · [MODEL_INVOKE_CONTRACT.md](docs/MODEL_INVOKE_CONTRACT.md) |
| OpenRouter vs direct routing (`PREFER_OPENROUTER`) — not implemented here | **[docs/PREFER_OPENROUTER_ROUTING.md](docs/PREFER_OPENROUTER_ROUTING.md)** |
| Graph execution context (`graphId`, `nodeId`, identity mapping) | **[docs/GRAPH_EXECUTION_SUPPORT.md](docs/GRAPH_EXECUTION_SUPPORT.md)** |
| Skills manager: preflight, FuncX, authoring (not in this package) | **[docs/SKILLS_MANAGER_CLIENT_GUIDE.md](docs/SKILLS_MANAGER_CLIENT_GUIDE.md)** |
| External follow-ups (Activix persistence vs gateway envelope) | **[docs/AI_GATEWAY_FEATURE_REQUESTS.md](docs/AI_GATEWAY_FEATURE_REQUESTS.md)** |
| Gateway server tools (OpenRouter pass-through) | **[docs/GATEWAY_SERVER_TOOLS.md](docs/GATEWAY_SERVER_TOOLS.md)** · [OPENROUTER_SERVER_TOOLS_WITH_AI_SKILLS.md](docs/OPENROUTER_SERVER_TOOLS_WITH_AI_SKILLS.md) · [upstream CR for gateway](docs/AI_GATEWAY_SERVER_TOOLS_CR.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-skills`** via **`@x12i/catalox`**; the gateway receives **inline template text** (not nx-content registry keys for skills)
- **Catalox provisioning**: `npm run catalox:provision-ai-skills` upserts catalog metadata + Optimixer fields from **`AI_SKILLS_CATALOG_ITEMS`** (preserves template bodies already stored in Catalox)
- **Presentation API**: Load pretty markdown for editors, save with lossless storage normalization (`getSkillTemplatesForPresentation`, `updateSkillTemplatesFromPresentation`, `getSkillTemplateInputs`); see [Skill templates (Catalox)](#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-gateway` for LLM calls only; skill templates are **never** loaded from nx-content / GitHub — Catalox inline strings on **`invoke()`** only
- **Activity tracking**: With `enableActivityTracking`, the gateway persists activities via **`@x12i/activix`**; this SDK enriches rows with **`cost` / `costStatus`** and contract-shaped **`outer.output.parsed`** when applicable (see [Cost and output contract](#cost-and-output-contract-run-analysis))
- **Model Configuration**: Per-request model selection and sampling parameters (`temperature`, `topP`, `reasoningEffort`); completion token budget is Optimixer-owned (see [Host responsibilities](#host-responsibilities-orchestrator--main))
- **Full template pipeline (default)**: Uses `gateway.invoke()` with inline bodies; the gateway message builder renders tokens (downstream stack; e.g. Rendrix). `runSkill` populates `workingMemory.input` and can pass `templateRenderOptions`, `templateTokens`, or client-level `templateRendering` (see **[docs/AI_SKILLS_GATEWAY_TEMPLATES.md](docs/AI_SKILLS_GATEWAY_TEMPLATES.md)**).
- **Execution engine catalog**: Optional **`aiEngineId`** echoes the Catalox **`ai-engines`** row (**`ai-gateway`** only; provision with **`npm run catalox:provision-ai-engines`**) for discovery and **`identity`** — see [Execution engines (`aiEngineId`)](#2b-execution-engines-aiengineid).
- **Execution-only scope**: Preflight / authoring / FuncX invoke review live in **skills manager** clients — see **[docs/SKILLS_MANAGER_CLIENT_GUIDE.md](docs/SKILLS_MANAGER_CLIENT_GUIDE.md)**.

## Installation

```bash
npm install @exellix/ai-skills
```

This package depends on **`@x12i/ai-gateway`**, **`@x12i/catalox`**, **`@x12i/logxer`**, **`@x12i/env`**, **`@x12i/rendrix`**, and **`@exellix/catalox-ai-skills`** (Mongo Catalox bootstrap). **`@x12i/catalox` ≥ 6.0** expects **Node 20+**.

**`@x12i/ai-gateway` ≥ 11.0.0** (openrouter-runtime **1.0.4**, `@x12i/ai-tools` **3.3.5**) (see `package.json`) with **`@x12i/activix` ^9.0** and **`@x12i/ai-profiles` ^3.4** is required: every invoke needs an explicit wire **`model`** and **`maxTokens`** (this package supplies **`maxTokens`** via **`@x12i/optimixer`** before `gateway.invoke()`). Invoke **`metadata`** includes routing fields (≥9.1.1), rejection **`metadata`** on failures (≥9.1.2), normalized billing (**`costUsd`**, **`cost`**, **`costStatus: priced | unpriced`**, optional **`costBreakdown`**) on success (≥9.3; ai-tools catalog pricing in gateway ≥10.1), and optional **OpenRouter server-tool metadata** (≥10.4.1). **This package does not use `@x12i/ai-tools` directly** — cost is resolved by the gateway. See **`docs/UPGRADING_AI_GATEWAY_10.md`** and **`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](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 (Mongo & Catalox 6)

**`createCataloxFromEnvAsync()`** / **`ensureCataloxFromEnv()`** from **`@exellix/catalox-ai-skills`** delegate to **`@x12i/catalox/mongo`**.

| Variable | Notes |
|----------|--------|
| **`MONGO_URI`** or **`MONGODB_URI`** | **Required** — Mongo connection string for Catalox catalogs |
| **`MONGO_DB_NAME`** / **`MONGODB_DB`** | Optional database name (default `catalox`) |

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

1. **Node.js 20+** — matches **`@x12i/catalox`** engine requirements.
2. **Mongo Catalox** — set **`MONGO_URI`** or **`MONGODB_URI`** (see [above](#environment-mongo--catalox-6)). Use **`await createCataloxFromEnvAsync()`** or pass your own **`Catalox`** from **`createCataloxFromMongoEnv`**.
3. **Provision the catalog** (creates/updates Catalox app bindings, descriptors, and skill rows):

```bash
npm run catalox:provision-ai-skills
npm run catalox:provision-ai-engines
npm run catalox:verify
```

4. At least one **LLM provider** key (`OPENAI_API_KEY`, `GROK_API_KEY`, or `OPEN_ROUTER_KEY` / `OPENROUTER_API_KEY`) for **`@x12i/ai-gateway`**.

### 1) Create the client

```typescript
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

```typescript
// 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

```typescript
// 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,
    topP: 0.9,
    reasoningEffort: "medium",
  }
});
```

## Host responsibilities (orchestrator ↔ MAIN)

Applies to **`@exellix/ai-tasks` 8.4+** and other graph orchestrators calling **`runSkill()`** for the MAIN skill phase.

| Send | Do not send |
|------|-------------|
| Wire **`model`** / **`modelId`** (required) | **`maxTokens`**, **`max_completion_tokens`**, **`max_output_tokens`** on `modelConfig` |
| Optional **`temperature`**, **`topP`**, **`reasoningEffort`**, **`frequencyPenalty`**, **`presencePenalty`**, **`retry`**, **`timeoutMs`** | Profile / plan model names — resolve in `@exellix/ai-tasks` only |
| Optional **`serverTools`**, **`openrouter.apiMode`** (top-level or on `modelConfig`) | Raw OpenRouter tool payloads, provider secrets |
| Optional **`provider`** | Expectation that ai-skills picks a default model |

**Token budget:** owned by **`@x12i/optimixer`** from Catalox catalog + prompts. After invoke, read **`metadata.usage.maxTokensRequested`** (and trace **`usage.maxTokensRequested`** when trace mode is on).

**`reasoningEffort`:** optional on **`modelConfig`**. Request value overrides Catalox **`optimixerReasoningEffort`** for that skill key; omit to keep catalog-only behavior. Resolution order: request → catalog → `"not-applicable"`.

Full rules: **[docs/MODEL_INVOKE_CONTRACT.md](docs/MODEL_INVOKE_CONTRACT.md)** · observability follow-ups: **[archived ai-skills-llm-observability.md](../archived/ai-skills/docs/legacy/ai-skills-llm-observability.md)**.

### 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](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.skillId`** from the executing request **only if** it is missing.
- The `RunSkillResponse` includes `identity` **only if downstream returns one**. If downstream does not provide `identity`, it will be omitted (no fallback).

```typescript
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) Model wire id, Optimixer budget, and trace visibility

**Wire model (mandatory):** Every `runSkill()` must include **`modelConfig.model`** or **`modelConfig.modelId`** as the **concrete provider wire id** resolved upstream (`@exellix/ai-tasks` / `resolveInvocationPlan`). Missing model is **rejected** before invoke; non-wire profile input is handled by **`@x12i/ai-gateway`** / **`@x12i/ai-tools`**, not duplicated here. **No gateway default model** in this package. See **[docs/MODEL_INVOKE_CONTRACT.md](docs/MODEL_INVOKE_CONTRACT.md)**.

**Completion token budget (Optimixer-owned):** Caller `maxTokens` / `max_completion_tokens` / `max_output_tokens` are **stripped** (anomaly log). Budget is predicted by **`@x12i/optimixer`** from Catalox catalog fields and resolved **`reasoningEffort`**, then merged into `modelConfig` before `gateway.invoke()`.

**Max-token retry (MAIN hop):** When the provider returns a limit **`finishReason`** (e.g. `length`) and Optimixer **`completeAiMaxTokensPrediction`** returns **`retryPrediction`**, this package **re-invokes the gateway once** with Optimixer’s bumped **`recommendedMaxTokens`** (caller ceilings still apply via existing merge). Up to **one** retry; observability includes **`metadata.optimixerRetryCount`** and log code **`OPTIMIXER_MAX_TOKENS_RETRY`**. No local token multipliers or re-predict bypass.

**Other `modelConfig` fields** (`temperature`, `topP`, `reasoningEffort`, `provider`, `serverTools`, `openrouter`, …) are forwarded when set.

### 3b) Gateway server tools (OpenRouter pass-through)

`@exellix/ai-skills` forwards OpenRouter **server-tool intent** to **`@x12i/ai-gateway` (≥ 10.4.4)**. This package does **not** execute OpenRouter tools locally and does **not** depend on `@x12i/openrouter-runtime`.

- Set **`serverTools`** on the request, on **`modelConfig`**, in Catalox **`data.runtime.serverTools`**, or via client **`defaultServerTools`** (merge order: client → catalog → request → modelConfig).
- Tool usage, citations, generated images, and patch proposals appear on **`RunSkillResponse.metadata`** (not in FlexMD unless the skill template asks for them).
- No tools are enabled by default; existing skills behave unchanged unless you opt in.

See **[docs/GATEWAY_SERVER_TOOLS.md](docs/GATEWAY_SERVER_TOOLS.md)** · **[docs/OPENROUTER_SERVER_TOOLS_WITH_AI_SKILLS.md](docs/OPENROUTER_SERVER_TOOLS_WITH_AI_SKILLS.md)**.

```typescript
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "What changed in the vendor docs?",
  jobId: "job-1",
  taskId: "task-1",
  agentId: "agent-1",
  jobTypeId: "research",
  taskTypeId: "answer",
  modelConfig: {
    provider: "openrouter",
    model: "openai/gpt-5.2",
    serverTools: {
      webSearch: { mode: "required", engine: "auto", maxResults: 5 },
      webFetch: { mode: "allowed", maxContentTokens: 50000 },
    },
  },
});

console.log(res.flexMd.payloads);
console.log(res.metadata.citations);
console.log(res.metadata.serverTools);
```

| What you need | Where it appears |
|---------------|------------------|
| What was **sent toward invoke** (merged `modelConfig`, `timeoutMs`) | Trace-only: **`debugTrace.invokeRequest`** when `executionMode: "trace"` or `diagnostics.includeDebugTrace` |
| Effective completion cap | **`metadata.usage.maxTokensRequested`** · trace **`debugTrace.usage.maxTokensRequested`** |
| Effective **`reasoningEffort`** | Trace **`debugTrace.invokeRequest.modelConfig.reasoningEffort`** (request override or catalog default) |
| What **ran** (model id, token counts) | **`debugTrace.modelUsed`**, **`debugTrace.usage`**, routing ids |

```typescript
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Question: ...",
  jobId: "job-1",
  taskId: "task-1",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
  modelConfig: {
    model: "gpt-4-turbo",
    temperature: 0.5,
    reasoningEffort: "high",
  },
  executionMode: "trace",
});

console.log(res.debugTrace?.invokeRequest?.modelConfig?.reasoningEffort);
console.log(res.metadata.usage?.maxTokensRequested);
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**, ai-tools catalog in gateway ≥ **10.1**) uses **`costUsd`** / **`cost`** when priced and **`costStatus: "priced" | "unpriced"`**. Optional **`costBreakdown`** (prompt/completion USD) is forwarded when the gateway catalog pricing includes it. **This SDK never calculates cost locally** — it normalizes gateway metadata only. Priced runs expose **`costUsd` only** (gateway **`"priced"`** status is not echoed). 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 `logSuccess` + SDK patch) |
|----------------------------|-------------------|-------------------------------|------------------------------------------------------|
| Priced (router or ai-tools catalog) | `costUsd`, `cost`, `costStatus: "priced"`, optional `costBreakdown` | `costUsd`, optional `costBreakdown` (no `costStatus`) | Root `cost` / `costUsd` / `costStatus: "priced"`, **`outer.cost.usd`**; prefer these over `outer.metadata` for billing |
| Usage, no price | `costStatus: "unpriced"` | `costStatus: "unpriced"` | Root `costStatus: "unpriced"` + token usage on root `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:

1. **`flexMd.payloads`**
2. Markdown section headings (`### Short Answer` → `shortAnswer`)

Examples (illustrative — use the contract for **that** skill/node):

```typescript
// 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](docs/metadata.md)** (`requiredPayloads` per skill).

**Exports** (for orchestrators and tests): `normalizeSkillBillingFromGatewayMetadata`, `enrichParsedForOutputContract`, `patchActivixActivityAfterSkill`, types **`SkillCostStatus`**, **`OutputContract`**.

## Direct functions (re-consider)

Typed wrappers on **`ExellixSkillsClient`** that call revision skills with nested **`templateTokens`**. Shared runtime: **`ReConsiderationRuntime`** (`jobId`, `taskId`, `agentId`, `jobTypeId`, `taskTypeId`, `modelConfig`). Object fields (`context`, `providedContext`, etc.) are stringified via **`stringifyForPrompt`** so they never become `[object Object]`. Callers own merge/apply of returned fragments.

| Method | Skill | Result |
|--------|-------|--------|
| **`fillReConsiderationGap`** | `re-consider.fill-gap` | `{ gapOutput }` |
| **`reviewReConsiderationResponse`** | `re-consider.review` | `{ reviewStatus, reviewOutput }` |
| **`rewriteReConsiderationScope`** | `re-consider.rewrite-scope` | `{ correctedScope }` |
| **`rewriteReConsiderationFull`** | `re-consider.rewrite-full` | `{ correctedAnswer }` |
| **`calibrateReConsiderationDecision`** | `re-consider.calibrate-decision` | `{ calibratedOutput }` |

```typescript
const result = await skills.fillReConsiderationGap(
  {
    input: "What are the risks?",
    originalInstructions: "Answer with evidence.",
    originalResponse: priorAnswer,
    gapExplained: "Missing evidence bullets for Section B.",
    fragmentName: "Section B",
    context: { assetId: "db-1" },
  },
  {
    jobId: "job-1",
    taskId: "task-1",
    agentId: "agent-1",
    jobTypeId: "job-type",
    taskTypeId: "task-type",
    modelConfig: { model: "gpt-4o-mini" },
  }
);
// result.gapOutput — insert or replace; do not expect a merged full response
```

Token roots (examples): `taskVariables.gap.*` / `jobMemory.gap.*`, `taskVariables.review.*` / `jobMemory.review.*`, `taskVariables.scope.*` / `jobMemory.scope.*`, `taskVariables.full.*` / `jobMemory.full.*`, `taskVariables.calibration.*` / `jobMemory.calibration.*`, plus `jobMemory.providedContext` and `{{input}}`. Chain prior outputs via optional `gapResults` / `reviewResults` / `rewriteResults` / `calibrationResults`.

Also exported as standalone functions (and request builders) from **`@exellix/ai-skills`**.

## 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/re-consider.fill-gap`** — fill a known gap (`usageHints.skillMode: "revision"`)
- **`skills/re-consider.review`** — review response → `reviewStatus` + `reviewOutput`
- **`skills/re-consider.rewrite-scope`** — rewrite one selected scope → `correctedScope`
- **`skills/re-consider.rewrite-full`** — full rewrite → `correctedAnswer`
- **`skills/re-consider.calibrate-decision`** — calibrate decision/priority/ranking → `calibratedOutput`
- Additional catalog rows / forks: use **`listCatalogSkills()`**, **`listPublishedSkills()`**, or Catalox **`listCatalogItems`** for 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 (metadata + Optimixer → Catalox)

`npm run catalox:provision-ai-skills` upserts rows from **`AI_SKILLS_CATALOG_ITEMS`** (title, description, Optimixer fields, `status`). It **preserves** existing **`instructionsText` / `promptText`** already in Catalox — template bodies are authored in Catalox only (via **`updateSkillTemplatesFromPresentation`**, **`upsertSkillCatalogItem`**, or your admin tooling).

```bash
npm run catalox:provision-ai-skills
npm run catalox:verify
```

Runnable skills need both bodies in Catalox and **`status`** `draft` or `published` (computed from body completeness + **`catalogReleaseStatus`** on provision).

### Catalog manager API (skills studio / admin)

For apps that manage the catalog without running LLMs, use **`createAiSkillsCatalogApi({ catalox })`** — full CRUD + fork. See **[docs/AI_SKILLS_CATALOG_MANAGER.md](./docs/AI_SKILLS_CATALOG_MANAGER.md)**.

| Function | Purpose |
|----------|---------|
| **`listSkills` / `listSubSkills`** | List base and sub-skill rows |
| **`getSkill` / `getSkillContent`** | Read metadata and template markdown |
| **`createSkill` / `updateSkill`** | Create and patch rows |
| **`forkSkill`** | Sub-skill fork with optional overrides |
| **`deleteSkill`** | Hard-delete sub-skills; soft-delete base skills |
| **`publishSkill` / `unpublishSkill`** | Release status shortcuts |

Lower-level **`ExellixSkillsClient`** methods below remain available when you already use the execution client.

### 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 |
| **`resolveRawTemplate(skillKey, section, { role, strategyKey? })`** | Raw Catalox bodies for PRE synthesis / orchestration / audit roles (used by `@exellix/ai-tasks`) |
| **`resolveSynthesisContextStrategyKey(skillKey)`** | Default Catalox **`synthesisContext`** template key from **`supportedCapabilities.synthesisContext`** (graphenix 2.1.0 — plan wire must not carry `unitParams.synthesisInputStrategy`) |
| **`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

```typescript
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](#environment-firebase--catalox-v4).

### Logging (`AiSkills` / `@x12i/logxer`)

This package uses **[`@x12i/logxer`](https://www.npmjs.com/package/@x12i/logxer)** with three stable identifiers:

| Field | Value | Purpose |
|-------|--------|---------|
| **`packageName`** | `AiSkills` | `package` column in logs, shadow, Mongo |
| **`envPrefix`** | `AI_SKILLS` | Env vars and stack keys |
| **`debugNamespace`** | `ai-skills` | `DEBUG=` filter slug |

Exported constants: `AI_SKILLS_LOGXER_PACKAGE`, `AI_SKILLS_LOGXER_ENV_PREFIX`, `AI_SKILLS_LOGXER_DEBUG_NAMESPACE`, `AI_SKILLS_LOGXER_PACKAGE_CONFIG`, `createAiSkillsLogger` (alias `createAiSkillsLogxer`).

| 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) |
| **Full payloads** | Set **`AI_SKILLS_VERBOSE=true`** *and* **`AI_SKILLS_LOGS_LEVEL=verbose`** — attaches sanitized gateway request/response on `runSkill()` |
| **Bulk stack config** | **`LOGXER_PACKAGE_LEVELS=AI_SKILLS:debug,...`** (host calls `applyPackageLogLevelsFromEnv()` at startup) |

Cross-cutting sinks (console, file, JSON, unified app config) are configured by the host app, not per this prefix — see the **@x12i/logxer** README.

Console output includes **`app:`** (host app from cwd `package.json`) and **`package:`** (`AiSkills` for lines from this library). Optional **`packageName`** on the client sets **`RuntimeIdentity.service`** only — it does not change the Logxer `package` field.

**Per-path tiers:**

| Path | `info` | `debug` | `verbose` + `AI_SKILLS_VERBOSE` |
|------|--------|---------|-----------------------------------|
| **`runSkill()`** | One completion summary (duration, provider, model, tokens) | Entry, Catalox fetch, invoke routing, FlexMD steps | Sanitized gateway request/response |
| **`runSkill()`** | One completion summary per invoke | Gateway invoke, Optimixer merge | Response metadata / trace when enabled |
| **Catalog / registry** | `discoverStructure`, `diagnose`, `runDiagnostics` completion only | List/probe/connection tests, client init/shutdown | N/A |

Failures on invoke and config validation emit at **`error`** / **`warnCode`** with correlation ids.

**`.env`:** with default **`autoLoadDotenv`**, the client loads **`.env`** from the current working directory using **`loadDotenv`** from **`@x12i/env`**.

```bash
# Threshold for this package
export AI_SKILLS_LOGS_LEVEL=info

# Optional: full payloads (still requires _LOGS_LEVEL=verbose to print)
export AI_SKILLS_VERBOSE=true

# Bulk stack config (loaded at startup)
export LOGXER_PACKAGE_LEVELS=AI_SKILLS:debug,AI_GATEWAY:warn
```

Programmatic stack (logxer ≥ 4.5):

```typescript
import { type StackLoggingOptions } from "@x12i/logxer";
import { ExellixSkillsClient, AI_SKILLS_LOGXER_ENV_PREFIX, createCataloxFromEnv } from "@exellix/ai-skills";

const logging: StackLoggingOptions = {
  packageLevels: { [AI_SKILLS_LOGXER_ENV_PREFIX]: "debug" },
};

const skills = new ExellixSkillsClient({
  catalox: createCataloxFromEnv(),
  logging,
  verbose: true,
});
```

Filter Mongo / shadow logs with `getJobLogs({ package: "AiSkills", ... })`.

## 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).

```bash
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   # upsert catalog metadata + Optimixer fields (before live tests)
npm run catalox:provision-ai-engines  # upsert in-repo engine rows → native `ai-engines`
npm run catalox:verify                # validate live Catalox rows for runtime requirements
npm run live:professional-answer      # smoke: professional-answer via Catalox + default gateway LLM
npm run live:re-consider              # smoke: all five re-consider direct functions via Catalox + LLM
npm run test:real              # extra real LLM scenarios; needs .env + valid Firebase credentials
```

#### Live tests (opt-in, `test/run.ts`)

Some cases use **real Catalox** (Mongo-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 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-skills` list** — read-only catalog list sanity check.
- **Catalox `ai-engines` list** — verifies **`npm run catalox:provision-ai-engines`** wrote the **`ai-gateway`** row (read-only).
- **Re-consider direct functions** — real LLM calls for `fillReConsiderationGap`, `reviewReConsiderationResponse`, `rewriteReConsiderationScope`, `rewriteReConsiderationFull`, `calibrateReConsiderationDecision` (provision + sync templates first).

**Unit mocks for re-consider** live only under **`test/`** (CI wiring via fake gateway). Production code and **`npm run live:re-consider`** use real Catalox + real LLM only — no mock fallbacks.

**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 Mongo + 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](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

```typescript
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;
  
  /** @deprecated Caller maxTokens is stripped — Optimixer sets completion budget from Catalox catalog. */
  maxTokens?: never;
  
  /** Top-p (nucleus) sampling parameter (0.0 to 1.0) */
  topP?: number;

  /** Optimixer reasoning depth; overrides Catalox catalog default when set. */
  reasoningEffort?: "not-applicable" | "none" | "low" | "medium" | "high";
  
  /** 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

```typescript
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  modelConfig: {
    model: "gpt-4-turbo"
  }
});
```

#### Full Model Configuration

```typescript
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  modelConfig: {
    model: "gpt-4-turbo",
    temperature: 0.7,
    topP: 0.9,
    reasoningEffort: "medium",
    frequencyPenalty: 0.5
  }
});
```

#### Per-node reasoning depth

```typescript
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  jobId: "job-1",
  taskId: "task-1",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
  modelConfig: {
    model: "gpt-4-turbo",
    reasoningEffort: "high", // overrides Catalox optimixerReasoningEffort for this invoke
  },
});
```

#### Provider Override

```typescript
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  modelConfig: {
    provider: "anthropic",
    model: "claude-3-opus",
    temperature: 0.3
  }
});
```

### Notes

- **`modelConfig.model` / `modelId`** are **required** (concrete wire id). Missing or alias-shaped values throw before `gateway.invoke()`. See **[docs/MODEL_INVOKE_CONTRACT.md](docs/MODEL_INVOKE_CONTRACT.md)**.
- **`maxTokens`** on the request are **stripped**; **Optimixer** sets the cap from Catalox catalog fields and resolved **`reasoningEffort`**.
- **`reasoningEffort`** on the request overrides the Catalox catalog default for that skill key.
- This package does **not** merge a global default `modelConfig` at construction time; only **`templateRendering`** has client-wide defaults for template rendering.
- The gateway and provider enforce valid ranges (e.g. temperature).
- **OpenRouter routing policy (`PREFER_OPENROUTER`)** is **not** applied in this package. Orchestrators (**`@exellix/ai-tasks`**, graph-engine) resolve the plan and set the wire model on `modelConfig` before `runSkill()`. See **[docs/PREFER_OPENROUTER_ROUTING.md](docs/PREFER_OPENROUTER_ROUTING.md)**.

## API Reference

### `ExellixSkillsClient`

#### Constructor options (essential)

```typescript
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;

  templateRendering?: TemplateRenderOptions;
  autoLoadDotenv?: boolean;
  testMode?: boolean;
  disableActivityTrackingInTests?: boolean;
}
```

The built-in **`AIGateway`** has **no nx-content / GitHub skill registry**; **`GITHUB_*`** is **not** required for **`ExellixSkillsClient`** initialization.

#### Methods

- **`runSkill<T>(input: RunSkillRequest): Promise<RunSkillResponse<T>>`** — loads templates from Catalox, invokes gateway with inline bodies. Optional **`outputContract`** enriches **`parsed`** and the Activix activity row (see [Cost and output contract](#cost-and-output-contract-run-analysis)).
- **`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 with **`status === "published"`**.
- **`listCatalogSkills(options?)`** — all catalog rows (any status); optional Catalox list query options.
- **`upsertSkillCatalogItem(input, options?)`** — create/update catalog metadata and template bodies; **`options.ifNotExists`** enforces create-only.
- **`softDeleteSkillCatalogItem(skillKey)`** — clears template and audit text and sets **`planned`**.

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 (Catalox catalog)

- `testCatalogConnection()`, `discoverCatalogStructure()`, `diagnoseSkillInCatalog()`, `listAvailableSkills()`, `runCatalogDiagnostics()` — Catalox **`ai-skills`** catalog reachability and discovery.

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](#cost-and-output-contract-run-analysis)**.

Details and caveats: **[docs/FLEX_MD_AND_TESTING.md](docs/FLEX_MD_AND_TESTING.md)**.

## Requirements

- **Node.js 20+** (see `package.json` **`engines`**; aligns with **`@x12i/catalox`**)
- **`@x12i/catalox`** **≥ 4** and **Firebase** access for the **`ai-skills`** native catalog (see **`createCataloxFromEnv`** and [Environment (Firebase & Catalox v4)](#environment-firebase--catalox-v4))
- **`@x12i/ai-gateway`** **^10.4.4** (see `package.json`; shares **`@x12i/activix` ^9.0** with this package)
- **`@x12i/ai-profiles`** **^3.0.0** (profile/choice → wire model resolution)
- **`@x12i/activix`** **^8.6.0** (direct dependency for activity-row enrichment types/patches)
- At least one LLM provider key when using the default constructed gateway

**Downstream orchestrators:** `@exellix/ai-tasks` **≥ 8.7.0** depends on this package (**≥ 6.4.0**) and **`@exellix/xynthesis` ≥ 4.5.0** for the full PRE/MAIN/POST stack — see **[docs/UPGRADING_AI_GATEWAY_10.md](docs/UPGRADING_AI_GATEWAY_10.md)**.

## 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 (**≥ 8.7.0** pins **`@exellix/ai-skills` ≥ 6.4.0** + **`@exellix/xynthesis` ≥ 4.5.0**; gateway 10.x billing and invoke rules apply to MAIN via this package)
- **`@exellix/graph-engine`** — graph execution and graph-level runtime wiring; forward each node’s **`inputs.outputContract`** on **`runSkill`** when Run Analysis or downstream validation require structured **`parsed`** fields
- **`@exellix/exellix-runtime`** — root runtime composition (for example loading composed `runtimeObjects` for 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

---
_Source: https://npm.io/package/@exellix/ai-skills · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
