Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 1 addition & 34 deletions TSSD_VAULT_READINESS_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,35 +233,6 @@ afi-config provides **governance** for vault configuration but no **implementati

---

### 2.6 afi-skills

**What Exists:**

- **Vault Replay Skill:**
- `skills/provenance/vault-replay-determinism.md` (183 lines) - **Comprehensive skill definition:**
- Purpose: Verify deterministic replay of historical epochs
- Inputs: epoch_id, original_outputs, replay_config
- Outputs: determinism_result, diff_report
- Allowed tools: `codex:replay`, `tssd:read`
- Risk level: medium, determinism_required: true
- Golden cases: `evals/provenance/vault-replay-determinism/golden_cases.json`

- **Codex:**
- `.afi-codex.json` (32 lines) - Skill library
- Provides: agent-skills, skill-library, skill-schemas
- Consumers: afi-core, afi-reactor, afi-factory

**What's Missing:**

- No implementation of vault-replay-determinism skill (only spec)
- No other vault-related skills (e.g., vault-query, vault-audit, vault-export)

**Implications:**

afi-skills defines **provenance and replay requirements** for the vault but has **no implementations**. The skill spec assumes `tssd:read` tool exists (it doesn't yet).

---

### 2.7 afi-token

**What Exists:**
Expand Down Expand Up @@ -544,7 +515,7 @@ afi-factory is **not involved** in vault design or implementation. This is expec
- **Possible interpretations**:
- **Re-execute pipeline**: Re-run RAW → ENRICHED → ANALYZED → SCORED with same inputs
- **Outcome evaluation**: Compare predicted vs. actual outcomes (REPLAYED stage)
- **Determinism check**: Verify bit-for-bit identical results (afi-skills vault-replay-determinism)
- **Determinism check**: Verify bit-for-bit identical results
- **Recommendation**: Support **all three** with different replay modes:
- `replay --mode=re-execute` - Re-run pipeline
- `replay --mode=evaluate` - Compare outcomes
Expand Down Expand Up @@ -722,10 +693,6 @@ afi-factory is **not involved** in vault design or implementation. This is expec
- `.afi-codex.json`
- `schemas/vault.schema.json`

**afi-skills** (2 files):
- `.afi-codex.json`
- `skills/provenance/vault-replay-determinism.md`

**afi-token** (3 files):
- `.afi-codex.json`
- `src/AFIMintCoordinator.sol`, `src/TestCoreToken.sol`
Expand Down
4 changes: 1 addition & 3 deletions docs/AFI_AGENT_PLAYBOOK.v0.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,7 @@ Capabilities implemented as **Eliza actions, providers, evaluators, or services*
- May orchestrate non-AFI data sources (news, markets, social sentiment).
- MUST NOT reimplement AFI scoring, DAG logic, or tokenomics in runtime code.

**Contrast with AFI Skills (repo land)**:
- **AFI Skills** (in `afi-skills` repo): Canonical, testable capabilities encoded as config/templates/code modules.
- **Runtime Skills** (agent side): Behaviors exposed via Eliza plugins that call AFI services.
**Runtime Skills** (agent side): behaviors exposed via Eliza plugins that call AFI services (distinct from pipeline-authoring artifacts, which are not loaded or executed in the agent runtime).

---

Expand Down
45 changes: 0 additions & 45 deletions src/http/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import {
projectSubmission,
} from "../services/reactorSubmitter.js";
import { apiKeyAuthMiddleware, createRateLimiter, type AuthedRequest } from "../middleware/apiKeyAuth.js";
import {
listSkills,
getSkillById,
summarizeCapabilities,
} from "../services/skillsService.js";

export interface AppDeps {
apiKeyStore?: ApiKeyStore;
Expand Down Expand Up @@ -142,46 +137,6 @@ export function buildApp(deps: AppDeps = {}) {
return res.status(projected.status).json(projected.body);
});

// Skills discovery (public)
app.get("/api/v1/skills", async (req: Request, res: Response) => {
try {
const { q, domain, tag } = req.query;
const skills = await listSkills(
{
q: typeof q === "string" ? q : undefined,
domain: typeof domain === "string" ? domain : undefined,
tag: typeof tag === "string" ? tag : undefined,
},
process.env.AFI_SKILLS_MANIFEST_PATH
);
return res.status(200).json({ items: skills });
} catch (err) {
elizaLogger.error("[skills:list] error", err);
return res.status(500).json({ error: "skills_list_failed" });
}
});

app.get("/api/v1/skills/:id", async (req: Request, res: Response) => {
try {
const skill = await getSkillById(req.params.id, process.env.AFI_SKILLS_MANIFEST_PATH);
if (!skill) return res.status(404).json({ error: "not_found" });
return res.status(200).json(skill);
} catch (err) {
elizaLogger.error("[skills:get] error", err);
return res.status(500).json({ error: "skills_get_failed" });
}
});

app.get("/api/v1/skills/capabilities", async (_req: Request, res: Response) => {
try {
const summary = await summarizeCapabilities(process.env.AFI_SKILLS_MANIFEST_PATH);
return res.status(200).json(summary);
} catch (err) {
elizaLogger.error("[skills:capabilities] error", err);
return res.status(500).json({ error: "skills_capabilities_failed" });
}
});

// 404
app.use((req: Request, res: Response) => {
res.status(404).json({ error: "not_found", path: req.path });
Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import * as readline from "readline";
* - DISCORD_APPLICATION_ID: Discord application ID (if using Discord client)
* - DISCORD_API_TOKEN: Discord bot token (if using Discord client)
* - AFI_REACTOR_URL: URL for AFI Reactor API (future, for AFI telemetry plugin)
* - AFI_CORE_URL: URL for AFI Core API (future, for AFI skills plugin)
* - AFI_CORE_URL: URL for AFI Core API (future)
*/
async function main() {
try {
Expand Down Expand Up @@ -84,7 +84,6 @@ async function main() {

// TODO: Register additional plugins when available:
// - @elizaos/plugin-node (Node.js services: browser, PDF, speech, etc.)
// - @afi/plugin-afi-skills (skill invocation via AFI APIs)

elizaLogger.success("✅ AgentRuntime framework initialized successfully");
elizaLogger.info("🤖 Framework ready for custom characters");
Expand Down
120 changes: 0 additions & 120 deletions src/services/skillsService.ts

This file was deleted.

93 changes: 93 additions & 0 deletions tests/guardrails/no-skills-surface.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* GUARDRAIL — the Gateway serves no standalone skills catalog.
*
* The Gateway authenticates and routes; it does not own or serve a manually
* curated agent skills catalog. The removed `afi-skills` repository was read by
* a filesystem manifest reader and exposed through `/api/v1/skills*` routes;
* this guard makes those reintroduction paths structurally impossible:
*
* - a filesystem reader of the removed repo's manifest (`../afi-skills`),
* - an environment variable locating that repo/manifest,
* - a skills-manifest service module or its route surface,
* - a CI step that checks out the removed sibling repository.
*
* The token list below exists solely for enforcement; it is not documentation.
*
* Scope: package manifests, lockfile, source (src/plugins/scripts), and the
* workflow files under .github/workflows.
*/

import { describe, it, expect } from "vitest";
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
import { join, resolve } from "node:path";

const REPO_ROOT = resolve(__dirname, "..", "..");

/** The removed repository, by name and as a sibling path. */
const FORBIDDEN_REPO = "afi-skills";
const FORBIDDEN_SIBLING = "../afi-skills";

/** Removed environment variables that located the manifest / plugin map. */
const FORBIDDEN_ENV = ["AFI_SKILLS_MANIFEST_PATH", "AFI_SKILL_PLUGIN_MAP"];

/** Removed source surfaces. */
const FORBIDDEN_TOKENS = ["skillsService", "/api/v1/skills"];

const SCANNED_DIRS = ["src", "plugins", "scripts"];

function collect(dir: string, exts: string[]): string[] {
const abs = join(REPO_ROOT, dir);
if (!existsSync(abs)) return [];
const out: string[] = [];
const walk = (current: string) => {
for (const entry of readdirSync(current)) {
if (entry === "node_modules" || entry === "dist" || entry.startsWith(".")) continue;
const full = join(current, entry);
if (statSync(full).isDirectory()) walk(full);
else if (exts.some((e) => entry.endsWith(e))) out.push(full);
}
};
walk(abs);
return out;
}

const rel = (f: string) => f.slice(REPO_ROOT.length + 1);
const SOURCE_FILES = SCANNED_DIRS.flatMap((d) => collect(d, [".ts", ".mts"]));
const WORKFLOW_FILES = collect(".github/workflows", [".yml", ".yaml"]);

describe("GUARDRAIL: gateway serves no standalone skills catalog", () => {
it("scans a non-empty set of source files", () => {
expect(SOURCE_FILES.length).toBeGreaterThan(5);
});

it("no source file reintroduces the removed skills surface", () => {
const banned = [FORBIDDEN_REPO, FORBIDDEN_SIBLING, ...FORBIDDEN_ENV, ...FORBIDDEN_TOKENS];
const offenders: string[] = [];
for (const file of SOURCE_FILES) {
const content = readFileSync(file, "utf8");
if (banned.some((t) => content.includes(t))) offenders.push(rel(file));
}
expect(offenders).toEqual([]);
});

it("declares no skills-repo dependency and keeps the lockfile free of its sibling path", () => {
const pkg = JSON.parse(readFileSync(join(REPO_ROOT, "package.json"), "utf8"));
const all = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}), ...(pkg.optionalDependencies ?? {}) };
expect(Object.keys(all).some((d) => d.includes(FORBIDDEN_REPO))).toBe(false);
const lockPath = join(REPO_ROOT, "package-lock.json");
if (existsSync(lockPath)) {
expect(readFileSync(lockPath, "utf8").includes(FORBIDDEN_SIBLING)).toBe(false);
}
});

it("checks out no external skills sibling repository in CI", () => {
const offenders: string[] = [];
for (const file of WORKFLOW_FILES) {
const content = readFileSync(file, "utf8");
if (content.includes(FORBIDDEN_SIBLING) || /repository:\s*\S*afi-skills/.test(content)) {
offenders.push(rel(file));
}
}
expect(offenders).toEqual([]);
});
});
Loading
Loading