From dffeab84304378ab85790308f8cb7bf86d561520 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 9 Jun 2026 22:35:07 +0000 Subject: [PATCH 1/2] 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 --- docs/agentstack.md | 79 ++++++++++ docs/openspec-comparison.md | 2 +- package-lock.json | 14 ++ package.json | 2 +- packages/agentstack/README.md | 67 ++++++++ packages/agentstack/package.json | 18 +++ packages/agentstack/src/index.test.ts | 106 +++++++++++++ packages/agentstack/src/index.ts | 218 ++++++++++++++++++++++++++ packages/agentstack/src/manifest.ts | 22 +++ packages/agentstack/src/types.ts | 86 ++++++++++ packages/agentstack/tsconfig.json | 8 + 11 files changed, 620 insertions(+), 2 deletions(-) create mode 100644 docs/agentstack.md create mode 100644 packages/agentstack/README.md create mode 100644 packages/agentstack/package.json create mode 100644 packages/agentstack/src/index.test.ts create mode 100644 packages/agentstack/src/index.ts create mode 100644 packages/agentstack/src/manifest.ts create mode 100644 packages/agentstack/src/types.ts create mode 100644 packages/agentstack/tsconfig.json diff --git a/docs/agentstack.md b/docs/agentstack.md new file mode 100644 index 0000000..19c6e9a --- /dev/null +++ b/docs/agentstack.md @@ -0,0 +1,79 @@ +# AgentStack + +Status: draft module + +AgentStack (`@logicsrc/agentstack`) is the LogicSRC module for **portable agent, task, +payment, and reputation coordination** across Profullstack microsaas apps. It is the +reference implementation of the `agentstack` capability defined in the Profullstack Shared +AppKit OpenSpec (`profullstack-web/openspec/specs/agentstack`). + +## Why + +Profullstack apps (sh1pt, uGig, qaaas.dev, crawlproof, commandboard.run, coinpayportal, +logicsrc, intr0s) need to share agents, tasks, reputation, and payment context without +forcing a shared login or one global account database. AgentStack provides a small, +provider-neutral coordination layer on top of CoinPay DIDs. + +## Identity model + +```txt +User DID: did:coinpay:user:123 +Agent DID: did:coinpay:agent:abc +Task: task_123 +``` + +Each app keeps a local `users.id` and links it to a CoinPay DID. The same person can appear +in many apps while every app independently owns its account: + +```txt +sh1pt.users.id -> did:coinpay:user:123 +ugig.users.id -> did:coinpay:user:123 +qaaas.users.id -> did:coinpay:user:123 +crawlproof.users.id -> did:coinpay:user:123 +``` + +## Task lifecycle + +```txt +pending -> queued -> running -> blocked -> complete | failed | cancelled +``` + +A task may carry `paymentIntentId`, `escrowId`, and `reputationEventId` so coordination, +payment, escrow, and reputation stay linked. Terminal statuses (`complete`, `failed`, +`cancelled`) are final. + +## Surface + +- DID helpers: `userDid`, `agentDid`, `makeDid`, `parseDid` +- `DidTask`, `AgentProfile`, `DelegationGrant` types +- `AgentStack` coordinator: `registerAgent`, `createTask`, `assignTask`, + `updateTaskStatus`, `delegate`, `revokeDelegation`, `listTasks`, `snapshot`, `on` +- `agentStackPlugin` — a validated LogicSRC `PluginDefinition` + +## Plugin manifest + +```jsonc +{ + "id": "agentstack", + "name": "AgentStack", + "type": ["agents", "tasks", "coordination"], + "capabilities": [ + "agents.register", "agents.delegate", + "tasks.create", "tasks.assign", "tasks.update", "tasks.publish", + "reputation.sync", "payments.link", "escrow.link" + ], + "commands": ["agents", "tasks", "delegate"], + "env": ["AGENTSTACK_API_URL", "AGENTSTACK_API_KEY", "COINPAY_API_BASE_URL"] +} +``` + +## Relationship to other modules + +- **CoinPay plugin** — provides the DID/wallet/payment/escrow/reputation backend AgentStack + references. +- **uGig plugin** — publishes tasks to the jobs/gigs marketplace via `tasks.publish`. +- **AgentSwarm orchestration** — higher-level multi-agent orchestration can consume + AgentStack tasks and delegation grants. + +See also [`openspec-comparison.md`](openspec-comparison.md) and the `agentstack` OpenSpec +capability. diff --git a/docs/openspec-comparison.md b/docs/openspec-comparison.md index 697e1ae..b87f300 100644 --- a/docs/openspec-comparison.md +++ b/docs/openspec-comparison.md @@ -14,7 +14,7 @@ The projects can be complementary. LogicSRC should support an `--openspec` compa | CLI direction | `logicsrc` as the canonical OpenStandards CLI. | `@fission-ai/openspec` CLI and slash-command integrations with coding tools. | | MCP | LogicSRC has a standards MCP server and should expose resources, tools, and prompts. | Site states "No MCP" as a product trait. | | SDK/API | Planned Rust, Bun, Node, Python, curl, and PWA surfaces with matching contracts. | Focus appears to be repo workflow and agent planning artifacts rather than a cross-language SDK/API standard. | -| Plugins | Plugin manifest standard plus CoinPay, uGig, AgentByte, Credential Sharing, and future integration specs. | Integrates with many coding agents and editors; plugin-contract scope is not the main positioning. | +| Plugins | Plugin manifest standard plus CoinPay, uGig, AgentStack, AgentByte, Credential Sharing, and future integration specs. | Integrates with many coding agents and editors; plugin-contract scope is not the main positioning. | | Compatibility idea | `logicsrc --openspec` reads/writes OpenSpec.dev-style specs/proposals/tasks where useful. | Can remain the lightweight planning layer inside repos. | ## CLI Flags diff --git a/package-lock.json b/package-lock.json index 6d2c8af..e81ccff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1430,6 +1430,10 @@ "resolved": "packages/account-core", "link": true }, + "node_modules/@logicsrc/agentstack": { + "resolved": "packages/agentstack", + "link": true + }, "node_modules/@logicsrc/cli": { "resolved": "packages/cli", "link": true @@ -6063,6 +6067,16 @@ "vitest": "^4.0.8" } }, + "packages/agentstack": { + "name": "@logicsrc/agentstack", + "version": "0.1.0", + "dependencies": { + "@logicsrc/plugin-core": "file:../../packages/plugin-core" + }, + "devDependencies": { + "vitest": "^4.0.8" + } + }, "packages/cli": { "name": "@logicsrc/cli", "version": "0.1.0", diff --git a/package.json b/package.json index c0deda1..63d7572 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "apps/*" ], "scripts": { - "build": "npm --workspace @logicsrc/schemas run build && npm --workspace @logicsrc/validators run build && npm --workspace @logicsrc/sdk run build && npm --workspace @logicsrc/plugin-core run build && npm --workspace @logicsrc/account-core run build && npm --workspace @logicsrc/plugin-coinpay run build && npm --workspace @logicsrc/plugin-ugig run build && npm --workspace @logicsrc/plugin-sh1pt run build && npm --workspace @logicsrc/plugin-c0mpute run build && npm --workspace @logicsrc/plugin-feed-discovery run build && npm --workspace @logicsrc/plugin-social-accounts run build && npm --workspace @logicsrc/plugin-email-accounts run build && npm --workspace @logicsrc/tui run build && npm --workspace @logicsrc/cli run build && npm --workspace @profullstack/logicsrc-mcp run build && npm --workspace @logicsrc/commandboard-api run build && npm --workspace @logicsrc/commandboard-web run build && npm --workspace @logicsrc/web run build", + "build": "npm --workspace @logicsrc/schemas run build && npm --workspace @logicsrc/validators run build && npm --workspace @logicsrc/sdk run build && npm --workspace @logicsrc/plugin-core run build && npm --workspace @logicsrc/agentstack run build && npm --workspace @logicsrc/account-core run build && npm --workspace @logicsrc/plugin-coinpay run build && npm --workspace @logicsrc/plugin-ugig run build && npm --workspace @logicsrc/plugin-sh1pt run build && npm --workspace @logicsrc/plugin-c0mpute run build && npm --workspace @logicsrc/plugin-feed-discovery run build && npm --workspace @logicsrc/plugin-social-accounts run build && npm --workspace @logicsrc/plugin-email-accounts run build && npm --workspace @logicsrc/tui run build && npm --workspace @logicsrc/cli run build && npm --workspace @profullstack/logicsrc-mcp run build && npm --workspace @logicsrc/commandboard-api run build && npm --workspace @logicsrc/commandboard-web run build && npm --workspace @logicsrc/web run build", "start": "npm --workspace @logicsrc/web run start", "test": "npm run test --workspaces --if-present", "check": "npm run build && npm run test", diff --git a/packages/agentstack/README.md b/packages/agentstack/README.md new file mode 100644 index 0000000..b26ae3a --- /dev/null +++ b/packages/agentstack/README.md @@ -0,0 +1,67 @@ +# @logicsrc/agentstack + +AgentStack is the LogicSRC module for **portable agent, task, payment, and reputation +coordination** across Profullstack microsaas apps (sh1pt, uGig, qaaas.dev, crawlproof, +commandboard.run, coinpayportal, logicsrc, intr0s). + +It is the reference implementation of the `agentstack` capability in the Profullstack +Shared AppKit OpenSpec (`profullstack-web/openspec/specs/agentstack`). + +## What it provides + +- **DID helpers** for CoinPay-linked identities: `userDid`, `agentDid`, `makeDid`, `parseDid` + (`did:coinpay:user:123`, `did:coinpay:agent:abc`). +- **`DidTask`** — a portable task model with a defined status lifecycle + (`pending → queued → running → blocked → complete | failed | cancelled`) that can bind to + payment, escrow, and reputation events. +- **`AgentStack`** — an in-memory coordinator that registers agents, tracks tasks, records + delegation grants, and emits coordination events. Storage backends can wrap the same API. +- **`agentStackPlugin`** — a LogicSRC `PluginDefinition` (validated against the plugin + manifest schema) exposing AgentStack as a coordination plugin with routes, events, + permissions, and a TUI panel. + +## Usage + +```ts +import { AgentStack, userDid, agentDid } from "@logicsrc/agentstack"; + +const stack = new AgentStack(); +const owner = userDid("123"); + +stack.registerAgent({ + did: agentDid("abc"), + name: "Build Agent", + sourceApp: "commandboard.run", + supportedProtocols: ["logicsrc/1"] +}); + +const task = stack.createTask({ ownerDid: owner, sourceApp: "sh1pt.com", title: "Ship build" }); +stack.assignTask(task.id, agentDid("abc")); +stack.updateTaskStatus(task.id, "running"); +stack.updateTaskStatus(task.id, "complete", { reputationEventId: "rep_1" }); +``` + +## Cross-app identity + +Each app keeps a local `users.id` and links it to a CoinPay DID, giving cross-app identity +without shared cookies or one global account database: + +```txt +sh1pt.users.id -> did:coinpay:user:123 +ugig.users.id -> did:coinpay:user:123 +``` + +## Environment + +```txt +AGENTSTACK_API_URL +AGENTSTACK_API_KEY +COINPAY_API_BASE_URL +``` + +## Scripts + +```bash +npm run build # tsc -> dist +npm test # vitest +``` diff --git a/packages/agentstack/package.json b/packages/agentstack/package.json new file mode 100644 index 0000000..f6f0abf --- /dev/null +++ b/packages/agentstack/package.json @@ -0,0 +1,18 @@ +{ + "name": "@logicsrc/agentstack", + "version": "0.1.0", + "description": "AgentStack: portable agent, task, payment, and reputation coordination module for LogicSRC.", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "test": "vitest run src --passWithNoTests" + }, + "dependencies": { + "@logicsrc/plugin-core": "file:../../packages/plugin-core" + }, + "devDependencies": { + "vitest": "^4.0.8" + } +} diff --git a/packages/agentstack/src/index.test.ts b/packages/agentstack/src/index.test.ts new file mode 100644 index 0000000..99bc1a7 --- /dev/null +++ b/packages/agentstack/src/index.test.ts @@ -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); + }); +}); diff --git a/packages/agentstack/src/index.ts b/packages/agentstack/src/index.ts new file mode 100644 index 0000000..d5ea46c --- /dev/null +++ b/packages/agentstack/src/index.ts @@ -0,0 +1,218 @@ +import type { PluginDefinition } from "@logicsrc/plugin-core"; +import { agentStackManifest } from "./manifest.js"; +import type { + AgentProfile, + AgentStackEvent, + AgentStackListener, + AgentStackSnapshot, + CreateTaskInput, + DelegationGrant, + DidKind, + DidTask, + TaskStatus +} from "./types.js"; +import { DID_METHOD } from "./types.js"; + +/** Build a CoinPay-method DID for a user or agent: `did:coinpay:user:123`. */ +export function makeDid(kind: DidKind, id: string): string { + return `${DID_METHOD}:${kind}:${id}`; +} + +export const userDid = (id: string) => makeDid("user", id); +export const agentDid = (id: string) => makeDid("agent", id); + +/** Parse a CoinPay DID into its kind and id, or return null if it is not one. */ +export function parseDid(did: string): { kind: DidKind; id: string } | null { + const prefix = `${DID_METHOD}:`; + if (!did.startsWith(prefix)) return null; + const [kind, id] = did.slice(prefix.length).split(":"); + if ((kind !== "user" && kind !== "agent") || !id) return null; + return { kind, id }; +} + +export function isDidTask(value: unknown): value is DidTask { + return ( + typeof value === "object" && + value !== null && + typeof (value as DidTask).id === "string" && + typeof (value as DidTask).ownerDid === "string" && + typeof (value as DidTask).status === "string" + ); +} + +const TERMINAL: ReadonlySet = new Set(["complete", "failed", "cancelled"]); + +/** + * In-memory AgentStack coordinator: registers agents, tracks portable tasks through their + * lifecycle, records delegation grants, and emits coordination events. Reference + * implementation of the `agentstack` capability; storage backends can wrap the same API. + */ +export class AgentStack { + private readonly agents = new Map(); + private readonly tasks = new Map(); + private readonly delegations = new Map(); + private readonly listeners = new Set(); + private seq = 0; + + constructor(private readonly now: () => string = () => new Date().toISOString()) {} + + on(listener: AgentStackListener): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + private emit(event: AgentStackEvent) { + for (const listener of this.listeners) listener(event); + } + + private nextId(prefix: string): string { + this.seq += 1; + return `${prefix}_${this.seq}`; + } + + registerAgent(agent: AgentProfile): AgentProfile { + if (!parseDid(agent.did)) { + throw new Error(`Invalid agent DID: ${agent.did}`); + } + this.agents.set(agent.did, agent); + this.emit({ type: "agent.registered", agent }); + return agent; + } + + getAgent(did: string): AgentProfile | undefined { + return this.agents.get(did); + } + + createTask(input: CreateTaskInput): DidTask { + if (!parseDid(input.ownerDid)) { + throw new Error(`Invalid owner DID: ${input.ownerDid}`); + } + const ts = this.now(); + const task: DidTask = { + id: this.nextId("task"), + ownerDid: input.ownerDid, + assigneeDid: input.assigneeDid, + sourceApp: input.sourceApp, + title: input.title, + description: input.description, + status: input.assigneeDid ? "queued" : "pending", + paymentIntentId: input.paymentIntentId, + escrowId: input.escrowId, + metadata: input.metadata, + createdAt: ts, + updatedAt: ts + }; + this.tasks.set(task.id, task); + this.emit({ type: "task.created", task }); + return task; + } + + getTask(id: string): DidTask | undefined { + return this.tasks.get(id); + } + + assignTask(taskId: string, agentDidValue: string): DidTask { + const task = this.requireTask(taskId); + if (!this.agents.has(agentDidValue)) { + throw new Error(`Unknown agent: ${agentDidValue}`); + } + const updated: DidTask = { + ...task, + assigneeDid: agentDidValue, + status: task.status === "pending" ? "queued" : task.status, + updatedAt: this.now() + }; + this.tasks.set(taskId, updated); + this.emit({ type: "task.assigned", task: updated }); + return updated; + } + + updateTaskStatus( + taskId: string, + status: TaskStatus, + patch: Partial> = {} + ): DidTask { + const task = this.requireTask(taskId); + if (TERMINAL.has(task.status)) { + throw new Error(`Task ${taskId} is already ${task.status} and cannot transition to ${status}`); + } + const updated: DidTask = { ...task, ...patch, status, updatedAt: this.now() }; + this.tasks.set(taskId, updated); + this.emit({ type: "task.updated", task: updated }); + return updated; + } + + /** Grant an agent authority to act for an owner. */ + delegate(ownerDidValue: string, agentDidValue: string, scopes: string[], expiresAt?: string): DelegationGrant { + if (!parseDid(ownerDidValue)) throw new Error(`Invalid owner DID: ${ownerDidValue}`); + if (!this.agents.has(agentDidValue)) throw new Error(`Unknown agent: ${agentDidValue}`); + const grant: DelegationGrant = { + id: this.nextId("grant"), + ownerDid: ownerDidValue, + agentDid: agentDidValue, + scopes, + expiresAt, + createdAt: this.now() + }; + this.delegations.set(grant.id, grant); + this.emit({ type: "delegation.granted", grant }); + return grant; + } + + revokeDelegation(grantId: string): DelegationGrant { + const grant = this.delegations.get(grantId); + if (!grant) throw new Error(`Unknown delegation grant: ${grantId}`); + this.delegations.delete(grantId); + this.emit({ type: "delegation.revoked", grant }); + return grant; + } + + listTasks(filter?: { ownerDid?: string; assigneeDid?: string; status?: TaskStatus }): DidTask[] { + return [...this.tasks.values()].filter((task) => { + if (filter?.ownerDid && task.ownerDid !== filter.ownerDid) return false; + if (filter?.assigneeDid && task.assigneeDid !== filter.assigneeDid) return false; + if (filter?.status && task.status !== filter.status) return false; + return true; + }); + } + + snapshot(): AgentStackSnapshot { + return { + agents: [...this.agents.values()], + tasks: [...this.tasks.values()], + delegations: [...this.delegations.values()] + }; + } + + private requireTask(id: string): DidTask { + const task = this.tasks.get(id); + if (!task) throw new Error(`Unknown task: ${id}`); + return task; + } +} + +/** LogicSRC plugin definition exposing AgentStack as a coordination plugin. */ +export const agentStackPlugin: PluginDefinition = { + manifest: agentStackManifest, + configDefaults: { + enabled: false, + api_url: "${AGENTSTACK_API_URL}", + api_key: "${AGENTSTACK_API_KEY}", + coinpay_api_base_url: "${COINPAY_API_BASE_URL}" + }, + routes: [ + { method: "POST", path: "/api/plugins/agentstack/agents", capability: "agents.register" }, + { method: "POST", path: "/api/plugins/agentstack/tasks", capability: "tasks.create" }, + { method: "PATCH", path: "/api/plugins/agentstack/tasks/:id", capability: "tasks.update" }, + { method: "POST", path: "/api/plugins/agentstack/delegations", capability: "agents.delegate" } + ], + events: [ + { event: "task.created", capability: "tasks.publish" }, + { event: "task.approved", capability: "reputation.sync" } + ], + permissions: ["agents:register", "agents:delegate", "tasks:create", "tasks:update", "reputation:sync"], + tuiPanels: [{ id: "agentstack-tasks", title: "AgentStack Tasks" }] +}; + +export { agentStackManifest }; +export * from "./types.js"; diff --git a/packages/agentstack/src/manifest.ts b/packages/agentstack/src/manifest.ts new file mode 100644 index 0000000..e0b4a8d --- /dev/null +++ b/packages/agentstack/src/manifest.ts @@ -0,0 +1,22 @@ +import type { PluginManifest } from "@logicsrc/plugin-core"; + +export const agentStackManifest: PluginManifest = { + id: "agentstack", + name: "AgentStack", + version: "0.1.0", + type: ["agents", "tasks", "coordination"], + default: false, + capabilities: [ + "agents.register", + "agents.delegate", + "tasks.create", + "tasks.assign", + "tasks.update", + "tasks.publish", + "reputation.sync", + "payments.link", + "escrow.link" + ], + commands: ["agents", "tasks", "delegate"], + env: ["AGENTSTACK_API_URL", "AGENTSTACK_API_KEY", "COINPAY_API_BASE_URL"] +}; diff --git a/packages/agentstack/src/types.ts b/packages/agentstack/src/types.ts new file mode 100644 index 0000000..1dfbe98 --- /dev/null +++ b/packages/agentstack/src/types.ts @@ -0,0 +1,86 @@ +/** + * AgentStack types — portable agent, task, payment, and reputation coordination. + * + * Mirrors the `agentstack` capability spec in the Profullstack Shared AppKit OpenSpec + * (profullstack-web/openspec/specs/agentstack). + */ + +export type TaskStatus = + | "pending" + | "queued" + | "running" + | "blocked" + | "complete" + | "failed" + | "cancelled"; + +/** DID method shared across CoinPay-linked Profullstack apps. */ +export const DID_METHOD = "did:coinpay"; + +export type DidKind = "user" | "agent"; + +/** A portable, DID-addressable task that can move across apps. */ +export interface DidTask { + id: string; + ownerDid: string; + assigneeDid?: string; + sourceApp: string; + title: string; + description?: string; + status: TaskStatus; + paymentIntentId?: string; + escrowId?: string; + reputationEventId?: string; + metadata?: Record; + createdAt: string; + updatedAt: string; +} + +/** A registered agent identity that can be delegated tasks. */ +export interface AgentProfile { + did: string; + name: string; + sourceApp: string; + inboxUrl?: string; + taskEndpoint?: string; + supportedProtocols: string[]; + reputationScore?: number; + metadata?: Record; +} + +/** A grant authorizing an agent to act on an owner's behalf. */ +export interface DelegationGrant { + id: string; + ownerDid: string; + agentDid: string; + scopes: string[]; + expiresAt?: string; + createdAt: string; +} + +export type AgentStackEvent = + | { type: "agent.registered"; agent: AgentProfile } + | { type: "task.created"; task: DidTask } + | { type: "task.assigned"; task: DidTask } + | { type: "task.updated"; task: DidTask } + | { type: "delegation.granted"; grant: DelegationGrant } + | { type: "delegation.revoked"; grant: DelegationGrant }; + +export type AgentStackListener = (event: AgentStackEvent) => void; + +export interface CreateTaskInput { + ownerDid: string; + sourceApp: string; + title: string; + description?: string; + assigneeDid?: string; + paymentIntentId?: string; + escrowId?: string; + metadata?: Record; +} + +export interface AgentStackSnapshot { + agents: AgentProfile[]; + tasks: DidTask[]; + delegations: DelegationGrant[]; +} diff --git a/packages/agentstack/tsconfig.json b/packages/agentstack/tsconfig.json new file mode 100644 index 0000000..df59da5 --- /dev/null +++ b/packages/agentstack/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +} From 4cd51728ff44808032c8ae228050d013c936d167 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Wed, 10 Jun 2026 02:15:37 +0000 Subject: [PATCH 2/2] docs(agentstack): note dual-runtime support (Bun + Node.js + Workers + browser) Matches the revised Shared AppKit PRD's Runtime Compatibility Strategy. AgentStack is runtime-neutral (no Bun/Node-only APIs; time is injected). Co-Authored-By: Claude Opus 4.8 --- docs/agentstack.md | 6 ++++++ packages/agentstack/README.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/docs/agentstack.md b/docs/agentstack.md index 19c6e9a..d408265 100644 --- a/docs/agentstack.md +++ b/docs/agentstack.md @@ -50,6 +50,12 @@ payment, escrow, and reputation stay linked. Terminal statuses (`complete`, `fai `updateTaskStatus`, `delegate`, `revokeDelegation`, `listTasks`, `snapshot`, `on` - `agentStackPlugin` — a validated LogicSRC `PluginDefinition` +## Runtime + +Runtime-neutral by design — pure TypeScript, no Bun- or Node-only APIs (time is injected via +the `AgentStack` constructor). Runs on Bun, Node.js, Cloudflare Workers, and the browser, +matching the Profullstack dual-runtime standard (Bun + Node.js both first-class). + ## Plugin manifest ```jsonc diff --git a/packages/agentstack/README.md b/packages/agentstack/README.md index b26ae3a..131f12e 100644 --- a/packages/agentstack/README.md +++ b/packages/agentstack/README.md @@ -51,6 +51,12 @@ sh1pt.users.id -> did:coinpay:user:123 ugig.users.id -> did:coinpay:user:123 ``` +## Runtime + +Runtime-neutral by design — pure TypeScript with no Bun- or Node-only APIs (time is injected +via the `AgentStack` constructor). Runs on **Bun, Node.js, Cloudflare Workers, and the +browser**, matching the Profullstack dual-runtime standard. + ## Environment ```txt