Merge commit '826293dc2065bf1c77e4e1bc3d551ff13768df3c'

This commit is contained in:
Alexa Amundson
2025-11-25 13:38:28 -06:00
6 changed files with 277 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
{
"episodes": [
{
"id": "episode-001",
"title": "The Clone Awakens",
"agent": "guardian-clone-vault",
"date": "2025-11-23",
"mp3": "https://example.com/audio/guardian-clone-vault.mp3",
"transcript": true
},
{
"id": "episode-002",
"title": "The Digest Protocol",
"agent": "guardian-clone-vault",
"date": "2025-11-24",
"mp3": "https://example.com/audio/guardian-clone-vault.mp3",
"transcript": true
}
]
}

View File

@@ -0,0 +1,18 @@
---
id: episode-001
title: "The Clone Awakens"
agent: guardian-clone-vault
date: 2025-11-23
voice: /audio/guardian-clone-vault.mp3
transcript: true
---
> **"This is Lucidia."**
> A new guardian agent has emerged from the vault.
> Purpose: Protect the chronicle integrity.
> Role: Sentinel. TTL: 72 hours.
> Awaiting initialization sequence.
🎧 Listen to the [voice digest](https://example.com/audio/guardian-clone-vault.mp3)
📜 Agent File: [`guardian-clone-vault.agent.json`](../agents/guardian-clone-vault.agent.json)

View File

@@ -0,0 +1,18 @@
---
id: episode-002
title: "The Digest Protocol"
agent: guardian-clone-vault
date: 2025-11-24
voice: /audio/guardian-clone-vault.mp3
transcript: true
---
> **"This is Lucidia."**
> A sentinel overflow agent has been spawned from a system burst.
> Escalations exceeded 18 in the last 72 hours.
> Role: Sentinel. TTL: 96 hours.
> Awaiting approval from Commander Alexa.
🎧 Listen to the [voice digest](https://example.com/audio/guardian-clone-vault.mp3)
📜 Agent File: [`guardian-clone-vault.agent.json`](../agents/guardian-clone-vault.agent.json)

View File

@@ -1,2 +1,62 @@
export * from "./types"; export * from "./types";
export * from "./chronicles"; export * from "./chronicles";
import * as fs from "fs";
import * as path from "path";
import type { Chronicles, Episode, EpisodeFrontmatter } from "./types";
const CHRONICLES_DIR = path.join(process.cwd(), "lucidia-chronicles");
const CHRONICLES_JSON = path.join(CHRONICLES_DIR, "chronicles.json");
export function readChronicles(): Chronicles {
const data = fs.readFileSync(CHRONICLES_JSON, "utf-8");
return JSON.parse(data) as Chronicles;
}
export function writeChronicles(chronicles: Chronicles): void {
fs.writeFileSync(CHRONICLES_JSON, JSON.stringify(chronicles, null, 2) + "\n");
}
export function addEpisode(episode: Episode): Chronicles {
const chronicles = readChronicles();
chronicles.episodes.push(episode);
writeChronicles(chronicles);
return chronicles;
}
export function getEpisodeById(id: string): Episode | undefined {
const chronicles = readChronicles();
return chronicles.episodes.find((ep) => ep.id === id);
}
export function listEpisodes(): Episode[] {
return readChronicles().episodes;
}
export function generateEpisodeMdx(frontmatter: EpisodeFrontmatter, narrative: string): string {
return `---
id: ${frontmatter.id}
title: "${frontmatter.title}"
agent: ${frontmatter.agent}
date: ${frontmatter.date}
voice: ${frontmatter.voice}
transcript: ${frontmatter.transcript}
---
${narrative}
🎧 Listen to the [voice digest](https://example.com${frontmatter.voice})
📜 Agent File: [\`${frontmatter.agent}.agent.json\`](../agents/${frontmatter.agent}.agent.json)
`;
}
export function getNextEpisodeId(): string {
const chronicles = readChronicles();
const nextNum = chronicles.episodes.length + 1;
return `episode-${String(nextNum).padStart(3, "0")}`;
}
export function createEpisodeFile(filename: string, content: string): void {
const filepath = path.join(CHRONICLES_DIR, filename);
fs.writeFileSync(filepath, content);
}

View File

@@ -31,3 +31,24 @@ export interface ScheduledEpisode {
} }
export const CHRONICLE_WORTHY_TOKEN_THRESHOLD = 300; export const CHRONICLE_WORTHY_TOKEN_THRESHOLD = 300;
export interface Episode {
id: string;
title: string;
agent: string;
date: string;
mp3: string;
transcript: boolean;
}
export interface Chronicles {
episodes: Episode[];
}
export interface EpisodeFrontmatter {
id: string;
title: string;
agent: string;
date: string;
voice: string;
transcript: boolean;
}

View File

@@ -72,6 +72,99 @@ describe("chronicles registry", () => {
expect(chronicleRegistry.episodes).toContain(episode001); expect(chronicleRegistry.episodes).toContain(episode001);
expect(chronicleRegistry.totalEpisodes).toBe(1); expect(chronicleRegistry.totalEpisodes).toBe(1);
expect(chronicleRegistry.latestEpisodeId).toBe("001"); expect(chronicleRegistry.latestEpisodeId).toBe("001");
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import * as fs from "fs";
import * as path from "path";
// Mock fs module
vi.mock("fs", () => ({
readFileSync: vi.fn(),
writeFileSync: vi.fn(),
}));
vi.mock("path", async () => {
const actual = await vi.importActual("path");
return {
...actual,
join: vi.fn((...args: string[]) => args.join("/")),
};
});
import {
readChronicles,
writeChronicles,
addEpisode,
getEpisodeById,
listEpisodes,
generateEpisodeMdx,
getNextEpisodeId,
} from "../src/chronicles";
import type { Chronicles, Episode, EpisodeFrontmatter } from "../src/chronicles/types";
describe("Chronicles", () => {
const mockChronicles: Chronicles = {
episodes: [
{
id: "episode-001",
title: "The Clone Awakens",
agent: "guardian-clone-vault",
date: "2025-11-23",
mp3: "https://example.com/audio/guardian-clone-vault.mp3",
transcript: true,
},
{
id: "episode-002",
title: "The Digest Protocol",
agent: "guardian-clone-vault",
date: "2025-11-24",
mp3: "https://example.com/audio/guardian-clone-vault.mp3",
transcript: true,
},
],
};
beforeEach(() => {
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockChronicles));
vi.mocked(fs.writeFileSync).mockClear();
});
afterEach(() => {
vi.clearAllMocks();
});
describe("readChronicles", () => {
it("reads and parses chronicles.json", () => {
const result = readChronicles();
expect(result).toEqual(mockChronicles);
expect(fs.readFileSync).toHaveBeenCalled();
});
});
describe("writeChronicles", () => {
it("writes chronicles to JSON file", () => {
writeChronicles(mockChronicles);
expect(fs.writeFileSync).toHaveBeenCalledWith(
expect.any(String),
JSON.stringify(mockChronicles, null, 2) + "\n"
);
});
});
describe("addEpisode", () => {
it("adds a new episode to chronicles", () => {
const newEpisode: Episode = {
id: "episode-003",
title: "New Episode",
agent: "test-agent",
date: "2025-11-25",
mp3: "https://example.com/audio/test.mp3",
transcript: false,
};
const result = addEpisode(newEpisode);
expect(result.episodes).toHaveLength(3);
expect(result.episodes[2]).toEqual(newEpisode);
expect(fs.writeFileSync).toHaveBeenCalled();
}); });
}); });
@@ -115,6 +208,53 @@ describe("chronicles registry", () => {
it("returns empty array for non-matching status", () => { it("returns empty array for non-matching status", () => {
const episodes = getEpisodesByStatus("completed"); const episodes = getEpisodesByStatus("completed");
expect(episodes).toHaveLength(0); expect(episodes).toHaveLength(0);
const result = getEpisodeById("episode-001");
expect(result).toEqual(mockChronicles.episodes[0]);
});
it("returns undefined when not found", () => {
const result = getEpisodeById("episode-999");
expect(result).toBeUndefined();
});
});
describe("listEpisodes", () => {
it("returns all episodes", () => {
const result = listEpisodes();
expect(result).toEqual(mockChronicles.episodes);
});
});
describe("getNextEpisodeId", () => {
it("returns next episode ID based on count", () => {
const result = getNextEpisodeId();
expect(result).toBe("episode-003");
});
});
describe("generateEpisodeMdx", () => {
it("generates MDX content with frontmatter", () => {
const frontmatter: EpisodeFrontmatter = {
id: "episode-003",
title: "Test Episode",
agent: "test-agent",
date: "2025-11-25",
voice: "/audio/test.mp3",
transcript: true,
};
const narrative = "> **\"This is Lucidia.\"**\n> Test narrative.";
const result = generateEpisodeMdx(frontmatter, narrative);
expect(result).toContain("id: episode-003");
expect(result).toContain('title: "Test Episode"');
expect(result).toContain("agent: test-agent");
expect(result).toContain("date: 2025-11-25");
expect(result).toContain("voice: /audio/test.mp3");
expect(result).toContain("transcript: true");
expect(result).toContain(narrative);
expect(result).toContain("🎧 Listen to the [voice digest]");
expect(result).toContain("📜 Agent File:");
}); });
}); });
}); });