Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ node_modules
# Local file-backed stores
.data/
tsconfig.tsbuildinfo
coverage/
test-results/
44 changes: 44 additions & 0 deletions __tests__/agent-runtime/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, expect, it } from "vitest";
import { readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { execSync } from "node:child_process";

describe("Open-Stellar CLI", () => {
it("starts agent via CLI and saves state to .data/agent-state.json", () => {
const output = execSync(
"node bin/open-stellar.js agent start --name Nexus-7 --district data-center",
{
encoding: "utf8",
},
);

expect(output).toContain("Nexus-7");
expect(output).toContain("data-center");

const filePath = join(process.cwd(), ".data", "agent-state.json");
expect(existsSync(filePath)).toBe(true);

const data = JSON.parse(readFileSync(filePath, "utf8"));
const agent = data.agents.find((a: any) => a.name === "Nexus-7");

expect(agent).toBeDefined();
expect(agent.district).toBe("data-center");
expect(agent.status).toBe("active");
}, 15000);

it("lists persisted agents via CLI", () => {
execSync(
"node bin/open-stellar.js agent start --name Nexus-7 --district data-center",
{
encoding: "utf8",
},
);

const output = execSync("node bin/open-stellar.js agent list", {
encoding: "utf8",
});

expect(output).toContain("bot-nexus-7");
expect(output).toContain("Nexus-7");
}, 15000);
});
36 changes: 36 additions & 0 deletions __tests__/agent-runtime/persistence.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from "vitest";
import {
loadPersistedState,
savePersistedState,
upsertPersistedAgent,
removePersistedAgent,
} from "@/lib/agent-runtime/persistence";

describe("Agent State Persistence", () => {
it("upserts and loads persisted agent state to survive restarts", () => {
const testId = "bot-unique-persistence-test";
upsertPersistedAgent({
id: testId,
name: "PersistenceAgent",
model: "claude-4-sonnet",
district: "data-center",
status: "active",
cpu: 20,
memory: 45,
autoRestart: true,
updatedAt: new Date().toISOString(),
});

const state = loadPersistedState();
const agent = state.agents.find((a) => a.id === testId);

expect(agent).toBeDefined();
expect(agent?.name).toBe("PersistenceAgent");
expect(agent?.district).toBe("data-center");
expect(agent?.status).toBe("active");

removePersistedAgent(testId);
const updated = loadPersistedState();
expect(updated.agents.find((a) => a.id === testId)).toBeUndefined();
});
});
102 changes: 102 additions & 0 deletions __tests__/agent-runtime/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { describe, expect, it, vi } from "vitest";
import { createAgent } from "@/lib/agent-runtime/sdk";

function uniqueId(prefix: string) {
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
}

describe("Agent SDK & Lifecycle Hooks", () => {
it("triggers onStart and onStop hooks during lifecycle transitions", async () => {
const onStart = vi.fn();
const onStop = vi.fn();
const onStateChange = vi.fn();
const id = uniqueId("bot-test-sdk-lifecycle");

const sdk = createAgent({
id,
name: "TestSDKAgent",
model: "claude-4-sonnet",
district: "data-center",
onStart,
onStop,
onStateChange,
});

expect(sdk.id).toBe(id);
expect(sdk.status).toBe("idle");

await sdk.start();
expect(onStart).toHaveBeenCalledTimes(1);
expect(sdk.status).toBe("running");
expect(onStateChange).toHaveBeenCalledWith("running");

await sdk.stop();
expect(onStop).toHaveBeenCalledTimes(1);
expect(sdk.status).toBe("stopped");
expect(onStateChange).toHaveBeenCalledWith("stopped");
});

it("executes tasks and updates metrics", async () => {
const onTask = vi.fn().mockResolvedValue({
summary: "Task completed successfully",
output: { result: 42 },
});
const id = uniqueId("bot-test-sdk-task");
const sdk = createAgent({
id,
name: "TaskAgent",
model: "claude-4-sonnet",
onTask,
});

await sdk.start();
const res = await sdk.executeTask({ id: "t1", title: "Calculate metric" });

expect(res.status).toBe("completed");
expect(res.summary).toBe("Task completed successfully");
expect(sdk.getMetrics().tasksCompleted).toBe(1);
});

it("handles errors and triggers onError hook", async () => {
const onError = vi.fn();
const id = uniqueId("bot-test-sdk-err");
const sdk = createAgent({
id,
name: "ErrorAgent",
model: "claude-4-sonnet",
onTask: async () => {
throw new Error("Execution failure");
},
onError,
});

await sdk.start();
const res = await sdk.executeTask({ id: "t2", title: "Faulty task" });

expect(res.status).toBe("failed");
expect(res.error).toBe("Execution failure");
expect(onError).toHaveBeenCalled();
});

it("supports inter-agent messaging", async () => {
const idA = uniqueId("bot-msg-a");
const idB = uniqueId("bot-msg-b");
const agentA = createAgent({
id: idA,
name: "AgentA",
model: "claude-4-sonnet",
});
const agentB = createAgent({
id: idB,
name: "AgentB",
model: "claude-4-sonnet",
});

const received: any[] = [];
agentB.subscribe((msg) => received.push(msg));

await agentA.sendMessage(idB, { text: "Hello Agent B" }, "chat");
expect(received).toHaveLength(1);
expect(received[0].payload).toEqual({ text: "Hello Agent B" });
});
});
9 changes: 6 additions & 3 deletions __tests__/api/protocol/x402-subscriptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ describe("x402 subscriptions", () => {
pricePerMonth: "1 XLM",
})

const first = await checkSubscription(new Request("http://localhost/api/protocol/x402/subscriptions/nexus-7/my-data-api?consume=true"), {
params: Promise.resolve({ agentId: "nexus-7", serviceId: "my-data-api" }),
})
const first = await checkSubscription(
new Request("http://localhost/api/protocol/x402/subscriptions/nexus-7/my-data-api?consume=true"),
{
params: Promise.resolve({ agentId: "nexus-7", serviceId: "my-data-api" }),
},
)
const firstData = await first.json()
const second = checkX402Subscription("nexus-7", "my-data-api", { consumeCall: true })
const exhausted = checkX402Subscription("nexus-7", "my-data-api")
Expand Down
3 changes: 2 additions & 1 deletion __tests__/api/webhooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -823,5 +823,6 @@ describe("webhook API", () => {
expect(attempts[0].event).toBe("event.200")
expect(attempts[199].event).toBe("event.1")
expect(attempts.some((attempt) => attempt.event === "event.0")).toBe(false)
})
}, 15000)
})

2 changes: 1 addition & 1 deletion app/agents/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export default async function AgentPage({ params }: AgentPageProps) {
<CardContent>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{badges.length > 0 ? badges.map((badge, i) => (
<div key={i} className={`flex flex-col p-3 rounded-lg border gap-1.5 ${getBadgeRarityStyles(badge.rarity)}`}>
<div key={badge.id || badge.badgeId || badge.name || i} className={`flex flex-col p-3 rounded-lg border gap-1.5 ${getBadgeRarityStyles(badge.rarity)}`}>
<div className="flex items-center justify-between">
<span className="font-pixel text-xs leading-tight text-slate-100">{badge.name || badge.badgeId || badge.id}</span>
<span className="font-mono text-[9px] uppercase px-1.5 py-0.5 rounded border border-current opacity-80">{badge.rarity || 'common'}</span>
Expand Down
Loading
Loading