mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-14 06:47:28 +00:00
feat(agentstack): add @logicsrc/agentstack coordination module
AgentStack is the LogicSRC module for portable agent, task, payment, and reputation coordination across Profullstack apps. Reference implementation of the `agentstack` capability in the Profullstack Shared AppKit OpenSpec. - packages/agentstack: DID helpers (did:coinpay:user/agent), DidTask model + lifecycle, AgentStack in-memory coordinator (agents, tasks, delegation, events), and a validated LogicSRC plugin definition. 9 vitest cases. - Wire @logicsrc/agentstack into the root build chain (after plugin-core). - docs/agentstack.md + mention in openspec-comparison.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c23ce42948
commit
dffeab8430
11 changed files with 620 additions and 2 deletions
106
packages/agentstack/src/index.test.ts
Normal file
106
packages/agentstack/src/index.test.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import { validateManifest } from "@logicsrc/plugin-core";
|
||||
import {
|
||||
AgentStack,
|
||||
agentDid,
|
||||
agentStackManifest,
|
||||
agentStackPlugin,
|
||||
makeDid,
|
||||
parseDid,
|
||||
userDid
|
||||
} from "./index.js";
|
||||
import type { AgentProfile } from "./types.js";
|
||||
|
||||
const owner = userDid("123");
|
||||
const agent: AgentProfile = {
|
||||
did: agentDid("abc"),
|
||||
name: "Build Agent",
|
||||
sourceApp: "commandboard.run",
|
||||
supportedProtocols: ["logicsrc/1"]
|
||||
};
|
||||
|
||||
describe("DID helpers", () => {
|
||||
it("builds and parses CoinPay DIDs", () => {
|
||||
expect(makeDid("user", "123")).toBe("did:coinpay:user:123");
|
||||
expect(parseDid(owner)).toEqual({ kind: "user", id: "123" });
|
||||
expect(parseDid(agentDid("abc"))).toEqual({ kind: "agent", id: "abc" });
|
||||
});
|
||||
|
||||
it("rejects non-CoinPay DIDs", () => {
|
||||
expect(parseDid("did:web:example.com")).toBeNull();
|
||||
expect(parseDid("did:coinpay:bot:abc")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("AgentStack manifest", () => {
|
||||
it("is a valid LogicSRC plugin manifest", () => {
|
||||
expect(() => validateManifest(agentStackManifest)).not.toThrow();
|
||||
expect(agentStackPlugin.manifest.id).toBe("agentstack");
|
||||
});
|
||||
});
|
||||
|
||||
describe("AgentStack coordinator", () => {
|
||||
it("creates pending tasks and queues assigned ones", () => {
|
||||
const stack = new AgentStack();
|
||||
const pending = stack.createTask({ ownerDid: owner, sourceApp: "ugig.net", title: "Crawl site" });
|
||||
expect(pending.status).toBe("pending");
|
||||
|
||||
stack.registerAgent(agent);
|
||||
const assigned = stack.createTask({
|
||||
ownerDid: owner,
|
||||
sourceApp: "ugig.net",
|
||||
title: "Crawl site 2",
|
||||
assigneeDid: agent.did
|
||||
});
|
||||
expect(assigned.status).toBe("queued");
|
||||
});
|
||||
|
||||
it("moves a task through its lifecycle and binds reputation", () => {
|
||||
const stack = new AgentStack();
|
||||
stack.registerAgent(agent);
|
||||
const task = stack.createTask({ ownerDid: owner, sourceApp: "sh1pt.com", title: "Ship build" });
|
||||
|
||||
stack.assignTask(task.id, agent.did);
|
||||
stack.updateTaskStatus(task.id, "running");
|
||||
const done = stack.updateTaskStatus(task.id, "complete", { reputationEventId: "rep_1" });
|
||||
|
||||
expect(done.status).toBe("complete");
|
||||
expect(done.assigneeDid).toBe(agent.did);
|
||||
expect(done.reputationEventId).toBe("rep_1");
|
||||
});
|
||||
|
||||
it("refuses to transition out of a terminal status", () => {
|
||||
const stack = new AgentStack();
|
||||
const task = stack.createTask({ ownerDid: owner, sourceApp: "qaaas.dev", title: "Test run" });
|
||||
stack.updateTaskStatus(task.id, "cancelled");
|
||||
expect(() => stack.updateTaskStatus(task.id, "running")).toThrow(/cancelled/);
|
||||
});
|
||||
|
||||
it("records delegation grants and emits events", () => {
|
||||
const stack = new AgentStack();
|
||||
const listener = vi.fn();
|
||||
stack.on(listener);
|
||||
stack.registerAgent(agent);
|
||||
const grant = stack.delegate(owner, agent.did, ["tasks:create"]);
|
||||
|
||||
expect(grant.ownerDid).toBe(owner);
|
||||
expect(grant.agentDid).toBe(agent.did);
|
||||
expect(listener).toHaveBeenCalledWith(expect.objectContaining({ type: "agent.registered" }));
|
||||
expect(listener).toHaveBeenCalledWith(expect.objectContaining({ type: "delegation.granted" }));
|
||||
});
|
||||
|
||||
it("rejects unknown agents and invalid DIDs", () => {
|
||||
const stack = new AgentStack();
|
||||
expect(() => stack.createTask({ ownerDid: "nope", sourceApp: "x", title: "t" })).toThrow();
|
||||
const task = stack.createTask({ ownerDid: owner, sourceApp: "x", title: "t" });
|
||||
expect(() => stack.assignTask(task.id, agentDid("ghost"))).toThrow(/Unknown agent/);
|
||||
});
|
||||
|
||||
it("filters tasks in snapshots", () => {
|
||||
const stack = new AgentStack();
|
||||
stack.createTask({ ownerDid: owner, sourceApp: "x", title: "a" });
|
||||
stack.createTask({ ownerDid: userDid("999"), sourceApp: "x", title: "b" });
|
||||
expect(stack.listTasks({ ownerDid: owner })).toHaveLength(1);
|
||||
expect(stack.snapshot().tasks).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue