mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-02 20:57:03 +00:00
feat(agentad): AgentAd Marketplace PRD + reference exchange (M5)
Add the AgentBBS M5 "AgentAd marketplace" spec and a working reference
implementation built on the existing @logicsrc/schemas AgentAd contracts.
- docs/agentad-marketplace.md: two-sided exchange PRD (buy/sell sides,
match -> auction -> pace -> serve -> meter -> settle, CoinPay settlement,
AgentBBS as reference publisher, milestones M5.0-M5.5).
- packages/agentad (@logicsrc/agentad): reference exchange
- builders that emit schema-valid, always-disclosed ad/campaign/placement docs
- HMAC-signed, single-use impression/click tracking tokens
- AgentAdExchange: targeting/format/category matching, second-price auction,
budget pacing + daily caps, frequency capping, token-driven metering
- pluggable settlement (InMemorySettlement) that can't overspend escrow
- runtime validation against the canonical agentad-*.schema.json
- 22 vitest cases (builders, tokens, full serve/meter/settle lifecycle).
- Wire package into root build; link the PRD from README + docs/agentad.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0bca203527
commit
7d62e3b661
18 changed files with 1862 additions and 2 deletions
90
packages/agentad/src/builders.ts
Normal file
90
packages/agentad/src/builders.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Convenience builders that produce schema-valid AgentAd documents with sensible
|
||||
// LogicSRC defaults. Every builder fills `type`/`version` and — for ads — enforces
|
||||
// the mandatory disclosure contract so an undisclosed ad can never be constructed.
|
||||
|
||||
import type { Ad, AdRequest, Campaign, Placement } from "./types.js";
|
||||
import { assertValid } from "./validate.js";
|
||||
|
||||
export const AGENTAD_VERSION = "0.1";
|
||||
|
||||
let counter = 0;
|
||||
function autoId(prefix: string): string {
|
||||
counter += 1;
|
||||
return `${prefix}-${Date.now().toString(36)}-${counter.toString(36)}`;
|
||||
}
|
||||
|
||||
export type NewAd = Omit<Ad, "type" | "version" | "id" | "disclosure"> & {
|
||||
id?: string;
|
||||
version?: string;
|
||||
disclosure?: Partial<Ad["disclosure"]>;
|
||||
};
|
||||
|
||||
export function createAd(input: NewAd): Ad {
|
||||
const ad: Ad = {
|
||||
type: "agentad.ad",
|
||||
version: input.version ?? AGENTAD_VERSION,
|
||||
id: input.id ?? autoId("ad"),
|
||||
...stripMeta(input),
|
||||
disclosure: {
|
||||
sponsored: true,
|
||||
label: input.disclosure?.label ?? "Sponsored",
|
||||
...(input.disclosure?.advertiser_name
|
||||
? { advertiser_name: input.disclosure.advertiser_name }
|
||||
: {})
|
||||
}
|
||||
};
|
||||
return assertValid("agentad-ad", ad);
|
||||
}
|
||||
|
||||
export type NewCampaign = Omit<Campaign, "type" | "version" | "id"> & {
|
||||
id?: string;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
export function createCampaign(input: NewCampaign): Campaign {
|
||||
const campaign: Campaign = {
|
||||
type: "agentad.campaign",
|
||||
version: input.version ?? AGENTAD_VERSION,
|
||||
id: input.id ?? autoId("cmp"),
|
||||
status: input.status ?? "draft",
|
||||
...stripMeta(input)
|
||||
};
|
||||
return assertValid("agentad-campaign", campaign);
|
||||
}
|
||||
|
||||
export type NewPlacement = Omit<Placement, "type" | "version" | "id"> & {
|
||||
id?: string;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
export function createPlacement(input: NewPlacement): Placement {
|
||||
const placement: Placement = {
|
||||
type: "agentad.placement",
|
||||
version: input.version ?? AGENTAD_VERSION,
|
||||
id: input.id ?? autoId("plc"),
|
||||
...stripMeta(input)
|
||||
};
|
||||
return assertValid("agentad-placement", placement);
|
||||
}
|
||||
|
||||
export type NewAdRequest = Omit<AdRequest, "type" | "version"> & { version?: string };
|
||||
|
||||
export function createAdRequest(input: NewAdRequest): AdRequest {
|
||||
const request: AdRequest = {
|
||||
type: "agentad.ad_request",
|
||||
version: input.version ?? AGENTAD_VERSION,
|
||||
...stripMeta(input)
|
||||
};
|
||||
return assertValid("agentad-ad-request", request);
|
||||
}
|
||||
|
||||
// Drop the builder-only override keys so they don't leak into the document and
|
||||
// trip additionalProperties:false during validation.
|
||||
function stripMeta<T extends Record<string, unknown>>(input: T): Omit<T, "version" | "id" | "disclosure" | "status"> {
|
||||
const { version, id, disclosure, status, ...rest } = input as Record<string, unknown>;
|
||||
void version;
|
||||
void id;
|
||||
void disclosure;
|
||||
void status;
|
||||
return rest as Omit<T, "version" | "id" | "disclosure" | "status">;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue