@useparallel/agent-identification 0.0.0-stage → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ import { track } from "./track.js";
2
+ declare global {
3
+ interface Window {
4
+ AgentIdentificationTracker: {
5
+ track: typeof track;
6
+ };
7
+ }
8
+ }
@@ -0,0 +1,39 @@
1
+ export interface TrackerOptions {
2
+ siteId: string;
3
+ apiUrl: string;
4
+ /** Where the tab's session is kept: sessionStorage (the default) or memory only. */
5
+ storage?: "session" | "memory";
6
+ /** Your route pattern for this page, such as /tickets/:id, for parts masking can't catch. */
7
+ route?: string;
8
+ /**
9
+ * Records a page view whenever the path changes without a reload, through the History API or
10
+ * the back and forward buttons. Defaults to true; turn it off if you call pageView() yourself.
11
+ */
12
+ navigation?: boolean;
13
+ /**
14
+ * Reports whether this browser is automated (navigator.webdriver) or headless, once per
15
+ * session, as heuristic evidence that never labels a visitor an AI agent. Defaults to true.
16
+ */
17
+ automationSignals?: boolean;
18
+ /**
19
+ * 0.9.0: a binding context your server signed with createBindingContext() for this visitor's
20
+ * own session. Actions your server reports with the same sessionBinding then join this visit.
21
+ */
22
+ bindingContext?: string;
23
+ onError?: (error: Error) => void;
24
+ }
25
+ export interface Tracker {
26
+ /** Resolves once this page load's page view is confirmed, and rejects if it couldn't be. */
27
+ readonly ready: Promise<void>;
28
+ /** Records a page view, for apps that change pages without the History API. */
29
+ pageView(options?: {
30
+ route?: string;
31
+ }): Promise<void>;
32
+ /** Stops recording. It never ends the session or removes anything already recorded. */
33
+ destroy(): void;
34
+ }
35
+ /**
36
+ * Records this page's views and automation signals for one site, without drawing anything.
37
+ * Calling it again for the same site returns the same tracker.
38
+ */
39
+ export declare function track(options: TrackerOptions): Tracker;
@@ -0,0 +1,189 @@
1
+ /** Shared wire protocol. Classifications are always self-reported. */
2
+ export declare const VERSION = "0.9.0";
3
+ export declare const CHOICES: readonly ["ai_assistant", "person", "prefer_not_to_say"];
4
+ export type Choice = (typeof CHOICES)[number];
5
+ export type Mode = "optional" | "required";
6
+ export interface SessionStatus {
7
+ sessionId: string;
8
+ siteId: string;
9
+ origin: string;
10
+ expiresAt: string;
11
+ choice: Choice | null;
12
+ receipt: string | null;
13
+ eventId: string | null;
14
+ evidence: "self_reported";
15
+ }
16
+ export interface CreatedSession extends SessionStatus {
17
+ sessionToken: string;
18
+ }
19
+ export interface RecordedDeclaration extends SessionStatus {
20
+ choice: Choice;
21
+ receipt: string;
22
+ eventId: string;
23
+ }
24
+ export interface ApiError {
25
+ error: string;
26
+ }
27
+ export declare function isChoice(value: unknown): value is Choice;
28
+ export declare const EVENT_TYPES: readonly ["declaration", "page_view", "action", "signature", "signal"];
29
+ export type EventType = (typeof EVENT_TYPES)[number];
30
+ /** A browser report can come from any client that copies the public site ID. */
31
+ export type EventSource = "browser" | "server";
32
+ /**
33
+ * Who vouches for an event: the visitor, the page's script, the host's own server, the
34
+ * collector's own check of a Web Bot Auth signature, or the host's CDN.
35
+ */
36
+ export type Evidence = "self_reported" | "browser_reported" | "host_reported" | "signature_verified" | "signature_claimed" | "cdn_reported";
37
+ export declare const OUTCOMES: readonly ["attempted", "rejected", "confirmed"];
38
+ /** `confirmed` means the host's application says the action succeeded. */
39
+ export type Outcome = (typeof OUTCOMES)[number];
40
+ /** What an agent says about itself. Every field is self-reported, and null means unknown. */
41
+ export interface AgentClaim {
42
+ name: string | null;
43
+ provider: string | null;
44
+ purpose: string | null;
45
+ }
46
+ interface EventBase {
47
+ /** Chosen by the reporter, so a retry with the same ID is recorded once. */
48
+ id: string;
49
+ siteId: string;
50
+ /** The collector session, when the reporter knows it. */
51
+ sessionId: string | null;
52
+ /** The page's origin for browser events; null for server events. */
53
+ origin: string | null;
54
+ source: EventSource;
55
+ evidence: Evidence;
56
+ /** When the reporter says it happened. */
57
+ occurredAt: string;
58
+ /** When the collector received it. */
59
+ receivedAt: string;
60
+ /**
61
+ * The host's opaque reference to the account used, set only by the host's server. It says
62
+ * which account acted, never who delegated an agent to act.
63
+ */
64
+ accountRef: string | null;
65
+ /**
66
+ * Since 0.7.0, true on a server report that joined its session through a link: the page that
67
+ * received the server's response claimed it. The report itself carried no session.
68
+ */
69
+ linked?: true;
70
+ }
71
+ export interface DeclarationEvent extends EventBase {
72
+ type: "declaration";
73
+ source: "browser";
74
+ evidence: "self_reported";
75
+ choice: Choice;
76
+ agent: AgentClaim;
77
+ }
78
+ export interface PageViewEvent extends EventBase {
79
+ type: "page_view";
80
+ source: "browser";
81
+ evidence: "browser_reported";
82
+ /** Always passed through `routePath`. */
83
+ path: string;
84
+ /** The host's own route pattern, such as /tickets/:id, when it supplies one. */
85
+ route: string | null;
86
+ }
87
+ export interface ActionEvent extends EventBase {
88
+ type: "action";
89
+ source: "server";
90
+ evidence: "host_reported";
91
+ /** Named by the host, such as ticket.update. */
92
+ action: string;
93
+ resourceRef: string | null;
94
+ outcome: Outcome;
95
+ }
96
+ /** Why a Web Bot Auth signature was or wasn't verified. */
97
+ export declare const SIGNATURE_RESULTS: readonly ["verified", "unknown_signer", "bad_signature", "expired", "replayed", "wrong_authority", "malformed", "key_unavailable"];
98
+ export type SignatureResult = (typeof SIGNATURE_RESULTS)[number];
99
+ /**
100
+ * A request that named a Web Bot Auth signer. `signature_verified` means the collector checked
101
+ * the signature against an allowlisted signer's published key; `signature_claimed` means it
102
+ * couldn't, and `result` says why; `cdn_reported` means the host passed on its CDN's verdict.
103
+ * A signature identifies the operator only, never the person or account it acts for.
104
+ */
105
+ export interface SignatureEvent extends EventBase {
106
+ type: "signature";
107
+ source: "server";
108
+ evidence: "signature_verified" | "signature_claimed" | "cdn_reported";
109
+ /** The allowlisted signer's name, such as ChatGPT agent, when known. */
110
+ signer: string | null;
111
+ /** The key directory the request named in Signature-Agent. */
112
+ signatureAgent: string | null;
113
+ result: SignatureResult;
114
+ /** The CDN that vouched for a cdn_reported event. */
115
+ verifier: string | null;
116
+ /** The signed request's path, masked like a page view's. */
117
+ path: string | null;
118
+ }
119
+ /**
120
+ * Heuristic automation signals. Each is evidence, never identity: an automated browser isn't
121
+ * necessarily an AI agent, and a user agent that names one is only a claim anyone can make.
122
+ */
123
+ export declare const SIGNALS: readonly ["webdriver", "headless_browser", "agent_user_agent", "http_client", "synthetic_input"];
124
+ export type Signal = (typeof SIGNALS)[number];
125
+ /** The signals a page can observe itself. */
126
+ export declare const BROWSER_SIGNALS: readonly Signal[];
127
+ /**
128
+ * Whether a request that named an agent came from the addresses that agent's operator publishes:
129
+ * "operator" when it did, "elsewhere" when it didn't, which means the name was likely forged.
130
+ */
131
+ export declare const ADDRESS_CHECKS: readonly ["operator", "elsewhere"];
132
+ export type AddressCheck = (typeof ADDRESS_CHECKS)[number];
133
+ export declare function isAddressCheck(value: unknown): value is AddressCheck;
134
+ export interface SignalEvent extends EventBase {
135
+ type: "signal";
136
+ evidence: "browser_reported" | "host_reported";
137
+ signal: Signal;
138
+ /** What triggered it, such as the user agent's ChatGPT-User token. */
139
+ detail: string | null;
140
+ /** 0.9.0: for a server's report, the masked path of the page requested. Null from a page. */
141
+ path: string | null;
142
+ /**
143
+ * For a server-reported agent user agent sent with the visitor's address, whether that address
144
+ * is on the agent's operator's published list. Null when it wasn't checked.
145
+ */
146
+ address: AddressCheck | null;
147
+ }
148
+ export type ActivityEvent = DeclarationEvent | PageViewEvent | ActionEvent | SignatureEvent | SignalEvent;
149
+ export declare const MAX_PATH = 512;
150
+ /**
151
+ * A page path safe to collect: the query and fragment are dropped, and segments that look
152
+ * like IDs become `:id`. Masking only catches what looks like an ID, so hosts with other
153
+ * personal segments should supply their own route pattern.
154
+ */
155
+ export declare function routePath(path: string): string;
156
+ export declare function isRoutePattern(value: unknown): value is string;
157
+ /** Account and resource references are opaque IDs, never names or email addresses. */
158
+ export declare function isReference(value: unknown): value is string;
159
+ export declare function isActionName(value: unknown): value is string;
160
+ export declare function isSignatureResult(value: unknown): value is SignatureResult;
161
+ export declare function isSignal(value: unknown): value is Signal;
162
+ /**
163
+ * User agent tokens that AI agents and AI crawlers publish for themselves. Matching one means the
164
+ * request claims to be that agent, which anyone can do, so it's a signal, never verification.
165
+ */
166
+ export declare const AGENT_USER_AGENT_TOKENS: readonly ["Claude-Agent", "ChatGPT-User", "OAI-SearchBot", "GPTBot", "Claude-User", "Claude-SearchBot", "ClaudeBot", "Perplexity-User", "PerplexityBot", "MistralAI-User", "DuckAssistBot"];
167
+ /**
168
+ * Coding agents that fetch pages from the developer's own machine, named by a product token in
169
+ * their user agent. Claude Code sends "Claude-User (claude-code/2.1.317; …)" from wherever it
170
+ * runs, so it's named on its own rather than checked against Anthropic's server addresses.
171
+ */
172
+ export declare const CODING_AGENT_PRODUCTS: readonly [RegExp, string][];
173
+ /** The signals a user agent string carries on its own. */
174
+ export declare function userAgentSignals(userAgent: string): {
175
+ signal: Signal;
176
+ detail: string;
177
+ }[];
178
+ export declare function isOutcome(value: unknown): value is Outcome;
179
+ /** Reads an agent's self-description. Missing or empty fields are unknown; null means invalid. */
180
+ export declare function agentClaim(value: unknown): AgentClaim | null;
181
+ /**
182
+ * Since 0.7.0, a server can link a report to the page it served: it puts a random link in the
183
+ * response's Server-Timing header under this name, and sends the same link with the report.
184
+ * Only the browser that received the page can read it, so its page script can claim the report.
185
+ */
186
+ export declare const LINK_TIMING = "agent-id";
187
+ /** 128 random bits, base64url without padding. */
188
+ export declare function isLink(value: unknown): value is string;
189
+ export {};
@@ -0,0 +1,7 @@
1
+ import { type WidgetOptions } from "../browser/index.js";
2
+ export interface AgentIdentificationProps extends Omit<WidgetOptions, "container"> {
3
+ className?: string;
4
+ id?: string;
5
+ }
6
+ /** Optional React integration; all network and DOM work happens after mount. */
7
+ export declare function AgentIdentification({ className, id, onRecorded, onError, ...options }: AgentIdentificationProps): import("react").JSX.Element;
@@ -0,0 +1,40 @@
1
+ import { type Outcome } from "../protocol.js";
2
+ export interface ActionReportOptions {
3
+ apiUrl: string;
4
+ siteId: string;
5
+ /** The site's server secret. Never use it in browser code. */
6
+ secret: string;
7
+ /** Called when a report fails. Defaults to a console warning without any secret. */
8
+ onError?: (error: Error) => void;
9
+ fetch?: typeof globalThis.fetch;
10
+ }
11
+ export interface ReportedAction {
12
+ /** Your name for it, lowercase, such as ticket.update. */
13
+ action: string;
14
+ /** attempted, rejected, or confirmed once your application says it succeeded. */
15
+ outcome: Outcome;
16
+ /** Your opaque ID for what it acted on, never a name or email address. */
17
+ resourceRef?: string;
18
+ /** Your opaque ID for the account used. It never says who delegated an agent. */
19
+ accountRef?: string;
20
+ /** Your server's own session binding, the one you put in binding contexts. */
21
+ sessionBinding?: string;
22
+ /** Reuse the same ID to retry; a new one is chosen when missing. */
23
+ id?: string;
24
+ occurredAt?: Date;
25
+ }
26
+ export type ActionReport = {
27
+ status: "recorded";
28
+ id: string;
29
+ sessionId: string | null;
30
+ duplicate: boolean;
31
+ } | {
32
+ status: "failed";
33
+ id: string;
34
+ error: Error;
35
+ };
36
+ /**
37
+ * Reports what your server did, so it joins the session's activity. It retries with the same
38
+ * ID, never throws, and only says recorded when the collector confirms it.
39
+ */
40
+ export declare function reportAction(input: ReportedAction, options: ActionReportOptions): Promise<ActionReport>;
@@ -0,0 +1,16 @@
1
+ export { createBindingContext, verifyDeclaration } from "./tokens.js";
2
+ export type { BindingOptions, DeclarationClaims } from "./tokens.js";
3
+ export { verifyDeclarationOnline } from "./online.js";
4
+ export type { OnlineVerificationOptions } from "./online.js";
5
+ export { reportAction } from "./activity.js";
6
+ export type { ActionReport, ActionReportOptions, ReportedAction } from "./activity.js";
7
+ export { reportSignedRequest } from "./web-bot-auth.js";
8
+ export { reportRequestSignals } from "./signals.js";
9
+ export { reportLink, serverTiming } from "./links.js";
10
+ export { agentIdentification, DEFAULT_COLLECTOR } from "./setup.js";
11
+ export type { AgentIdentification, AgentIdentificationOptions, RequestReportOptions, } from "./setup.js";
12
+ export type { SignalReport, SignalReportOptions } from "./signals.js";
13
+ export type { ReportableRequest, SignatureReport, SignatureReportOptions } from "./web-bot-auth.js";
14
+ export { routePath, userAgentSignals, isLink, AGENT_USER_AGENT_TOKENS, LINK_TIMING, } from "../protocol.js";
15
+ export type { ActionEvent, ActivityEvent, AgentClaim, DeclarationEvent, EventSource, EventType, Evidence, Outcome, PageViewEvent, Signal, SignalEvent, SignatureEvent, SignatureResult, } from "../protocol.js";
16
+ export type { AddressCheck } from "../protocol.js";
@@ -0,0 +1,12 @@
1
+ import { type ReportableRequest } from "./web-bot-auth.js";
2
+ /**
3
+ * A new link for a request that reportSignedRequest or reportRequestSignals will report: one that
4
+ * is signed or names an AI agent in its user agent, for a page rather than an asset. Pass it to
5
+ * both, and put it in the response with serverTiming(), so the page script in the browser that
6
+ * gets the page can join those reports to its visit. Null for every other request.
7
+ */
8
+ export declare function reportLink(request: ReportableRequest, options?: {
9
+ url?: string;
10
+ }): string | null;
11
+ /** The Server-Timing header value that carries a link to the page script. */
12
+ export declare function serverTiming(link: string): string;
@@ -0,0 +1,13 @@
1
+ import { type BindingOptions, type DeclarationClaims } from "./tokens.js";
2
+ export interface OnlineVerificationOptions extends Omit<BindingOptions, "expiresInSeconds"> {
3
+ apiUrl: string;
4
+ fetch?: typeof globalThis.fetch;
5
+ }
6
+ /**
7
+ * Verify the signature, actual host-session binding, and current collector state.
8
+ * Call from your server immediately before the protected action, after checking
9
+ * the user's ordinary permissions. Any error must deny/defer that action.
10
+ */
11
+ export declare function verifyDeclarationOnline(receipt: string, options: OnlineVerificationOptions): Promise<DeclarationClaims & {
12
+ bindingHash: string;
13
+ }>;
@@ -0,0 +1,60 @@
1
+ import { type ActionReport, type ReportedAction } from "./activity.js";
2
+ import { type ReportableRequest } from "./web-bot-auth.js";
3
+ /** The hosted collector, used unless you pass apiUrl or set AGENT_ID_API_URL. */
4
+ export declare const DEFAULT_COLLECTOR = "https://parallel-agent-identification.vercel.app";
5
+ export interface AgentIdentificationOptions {
6
+ /** Your public site ID. Defaults to the AGENT_ID_SITE_ID environment variable. */
7
+ siteId?: string;
8
+ /** Your server secret. Defaults to AGENT_ID_SITE_SECRET. Never put it in browser code. */
9
+ secret?: string;
10
+ /** The collector. Defaults to AGENT_ID_API_URL, then the hosted collector. */
11
+ apiUrl?: string;
12
+ /** Called when a report fails. Defaults to a console warning without any secret. */
13
+ onError?: (error: Error) => void;
14
+ fetch?: typeof globalThis.fetch;
15
+ }
16
+ export interface RequestReportOptions {
17
+ /** The URL the visitor asked for, if your framework rewrites the request's own. */
18
+ url?: string;
19
+ /**
20
+ * The visitor's IP address as your platform gives it, never from a header visitors can set.
21
+ * It's sent only with a request that names an agent, to check it against the agent's
22
+ * operator's published addresses, and the collector keeps only the result.
23
+ */
24
+ clientIp?: string | null;
25
+ /** Your server's own session binding, as in the page script's binding context. */
26
+ sessionBinding?: string;
27
+ /** Keeps the reports running after the response, such as Next.js's event.waitUntil. */
28
+ waitUntil?: (work: Promise<unknown>) => void;
29
+ }
30
+ export interface AgentIdentification {
31
+ /**
32
+ * Reports one request: a signed agent's signature, and an agent or automation its user agent
33
+ * names. Ordinary requests and assets are skipped without a network call. Returns the
34
+ * Server-Timing value to add to this page's response, or null, so the page script can join the
35
+ * reports to its visit. It never throws and never waits for the collector.
36
+ */
37
+ report(request: ReportableRequest, options?: RequestReportOptions): string | null;
38
+ /** Express or Connect middleware that reports every request. */
39
+ express(): (request: ReportableRequest & {
40
+ ip?: string;
41
+ }, response: {
42
+ append(name: string, value: string): unknown;
43
+ }, next: () => void) => void;
44
+ /**
45
+ * For a Next.js proxy: reports the request and adds the link to your response. On Vercel it
46
+ * passes the visitor's address from x-real-ip, which Vercel sets; elsewhere pass clientIp.
47
+ */
48
+ next<T extends {
49
+ headers: Headers;
50
+ }>(request: Request, event: {
51
+ waitUntil(work: Promise<unknown>): void;
52
+ }, response: T, options?: Pick<RequestReportOptions, "clientIp" | "sessionBinding">): T;
53
+ /** Reports an action your server handled, such as an order placed. Never throws. */
54
+ action(input: ReportedAction): Promise<ActionReport>;
55
+ }
56
+ /**
57
+ * Sets up agent identification in one call, from AGENT_ID_SITE_ID and AGENT_ID_SITE_SECRET unless
58
+ * you pass them. Use .express() as middleware, or .next() in a Next.js proxy.
59
+ */
60
+ export declare function agentIdentification(options?: AgentIdentificationOptions): AgentIdentification;
@@ -0,0 +1,50 @@
1
+ import { type AddressCheck, type Signal } from "../protocol.js";
2
+ import { type ReportableRequest } from "./web-bot-auth.js";
3
+ export interface SignalReportOptions {
4
+ apiUrl: string;
5
+ siteId: string;
6
+ /** The site's server secret. Never use it in browser code. */
7
+ secret: string;
8
+ /** Your server's own session binding, the one you put in binding contexts. */
9
+ sessionBinding?: string;
10
+ /** From reportLink(), so the page script can join these signals to its visit. */
11
+ link?: string | null;
12
+ /**
13
+ * The visitor's IP address, as your platform gives it to you: on Vercel the x-real-ip header,
14
+ * in Express req.ip, in Node.js req.socket.remoteAddress. Never use a header a visitor can set
15
+ * themselves. It's sent only when the user agent names an agent whose operator publishes its
16
+ * addresses, so the collector can check the claim. The collector keeps the result, not the
17
+ * address.
18
+ */
19
+ clientIp?: string | null;
20
+ /**
21
+ * The URL the visitor asked for, when your framework changes the request's own (Next.js uses
22
+ * localhost). Its masked path goes with the report, so you can see which pages agents fetched.
23
+ */
24
+ url?: string;
25
+ /** Called when a report fails. Defaults to a console warning without any secret. */
26
+ onError?: (error: Error) => void;
27
+ fetch?: typeof globalThis.fetch;
28
+ }
29
+ export type SignalReport = {
30
+ status: "skipped";
31
+ } | {
32
+ status: "recorded";
33
+ signals: {
34
+ signal: Signal;
35
+ detail: string | null;
36
+ /** Whether the request came from the agent's operator's published addresses, if checked. */
37
+ address: AddressCheck | null;
38
+ }[];
39
+ } | {
40
+ status: "failed";
41
+ error: Error;
42
+ };
43
+ /**
44
+ * Reports what a request's user agent says about automation: a token an AI agent publishes for
45
+ * itself, such as ChatGPT-User, or a headless browser. Anyone can send those, so they're
46
+ * signals. With clientIp, the collector also checks a named agent against the addresses its
47
+ * operator publishes, which a forged user agent can't pass. Requests with neither, and asset
48
+ * requests, are skipped without a network call. It retries with the same IDs and never throws.
49
+ */
50
+ export declare function reportRequestSignals(request: ReportableRequest, options: SignalReportOptions): Promise<SignalReport>;
@@ -0,0 +1,59 @@
1
+ import { type Choice } from "../protocol.js";
2
+ export interface SiteConfig {
3
+ id: string;
4
+ origins: string[];
5
+ secret: string;
6
+ }
7
+ export interface BindingOptions {
8
+ siteId: string;
9
+ origin: string;
10
+ secret: string;
11
+ /** An opaque value derived by your server from its current authenticated session. */
12
+ sessionBinding: string;
13
+ now?: Date;
14
+ expiresInSeconds?: number;
15
+ }
16
+ interface CommonClaims {
17
+ v: 1;
18
+ siteId: string;
19
+ origin: string;
20
+ iat: number;
21
+ exp: number;
22
+ }
23
+ export interface ContextClaims extends CommonClaims {
24
+ purpose: "binding_context";
25
+ bindingHash: string;
26
+ }
27
+ export interface DeclarationClaims extends CommonClaims {
28
+ purpose: "declaration";
29
+ sessionId: string;
30
+ eventId: string;
31
+ choice: Choice;
32
+ bindingHash: string | null;
33
+ evidence: "self_reported";
34
+ }
35
+ export declare function isUuid(value: unknown): value is string;
36
+ export declare function isSiteId(value: unknown): value is string;
37
+ export declare function isAllowedOrigin(value: unknown): value is string;
38
+ export declare function validateSite(site: SiteConfig): void;
39
+ export declare function hashToken(token: string): string;
40
+ export declare function hashBinding(siteId: string, binding: string): string;
41
+ export declare function secretEquals(actual: string, expected: string): boolean;
42
+ /** Mint on your server only, using its current session rather than browser input. */
43
+ export declare function createBindingContext(options: BindingOptions): string;
44
+ export declare function verifyBindingContext(token: string, expected: {
45
+ siteId: string;
46
+ origin: string;
47
+ secret: string;
48
+ now?: Date;
49
+ }): ContextClaims;
50
+ export declare function mintDeclaration(claims: Omit<DeclarationClaims, "v" | "purpose" | "evidence">, secret: string): string;
51
+ /**
52
+ * Verify a bound receipt against your server's actual current session.
53
+ * A valid declaration is self-reported and does not establish authorization.
54
+ * Receipts remain valid until expiry; use online status for immediate revocation.
55
+ */
56
+ export declare function verifyDeclaration(receipt: string, expected: Omit<BindingOptions, "expiresInSeconds">): DeclarationClaims & {
57
+ bindingHash: string;
58
+ };
59
+ export {};
@@ -0,0 +1,50 @@
1
+ import type { SignatureEvent } from "../protocol.js";
2
+ /** A request as a server framework sees it: a Fetch Request, or Node's IncomingMessage. */
3
+ export type ReportableRequest = Request | {
4
+ method?: string;
5
+ url?: string;
6
+ originalUrl?: string;
7
+ protocol?: string;
8
+ headers: Record<string, string | string[] | undefined>;
9
+ };
10
+ export interface SignatureReportOptions {
11
+ apiUrl: string;
12
+ siteId: string;
13
+ /** The site's server secret. Never use it in browser code. */
14
+ secret: string;
15
+ /** The collector session, when your server knows it. */
16
+ sessionId?: string;
17
+ /** Your opaque ID for the account used, never a name or email address. */
18
+ accountRef?: string;
19
+ /** The URL the visitor requested, when a proxy rewrote the request's own. */
20
+ url?: string;
21
+ /** From reportLink(), so the page script can join this report to its visit. */
22
+ link?: string | null;
23
+ /**
24
+ * Also report scripts, styles, images and other assets. Off by default, so one page load
25
+ * counts as one signed request.
26
+ */
27
+ includeAssets?: boolean;
28
+ /** Called when a report fails. Defaults to a console warning without any secret. */
29
+ onError?: (error: Error) => void;
30
+ fetch?: typeof globalThis.fetch;
31
+ }
32
+ export type SignatureReport = {
33
+ status: "skipped";
34
+ } | {
35
+ status: "recorded";
36
+ event: SignatureEvent;
37
+ } | {
38
+ status: "failed";
39
+ error: Error;
40
+ };
41
+ /** Scripts, styles, images and the like, so one page load counts once. */
42
+ export declare function isAssetRequest(url: string, headers: Record<string, string>): boolean;
43
+ export declare function headerMap(request: ReportableRequest): Record<string, string>;
44
+ export declare function requestUrl(request: ReportableRequest, headers: Record<string, string>): string;
45
+ /**
46
+ * Reports a request that carries a Web Bot Auth signature so the collector can check it.
47
+ * A request without one is skipped without a network call. It never throws, so run it without
48
+ * awaiting, or in waitUntil, and the visitor's response never waits for it.
49
+ */
50
+ export declare function reportSignedRequest(request: ReportableRequest, options: SignatureReportOptions): Promise<SignatureReport>;
package/package.json CHANGED
@@ -1,6 +1,97 @@
1
1
  {
2
2
  "name": "@useparallel/agent-identification",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.9.0",
4
+ "description": "See which AI agents use your website: report agents from your server, record visits with a page script that draws nothing, and verify signed agents and operators' published addresses.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "ai-agents",
9
+ "agent-identification",
10
+ "web-bot-auth",
11
+ "bot-detection",
12
+ "analytics",
13
+ "http-message-signatures"
14
+ ],
15
+ "homepage": "https://parallel-agent-dashboard.vercel.app/docs",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Parallel-AI-Research/agent-identification.git"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "main": "./dist/browser/index.js",
29
+ "types": "./dist/types/browser/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/types/browser/index.d.ts",
33
+ "import": "./dist/browser/index.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/types/react/index.d.ts",
37
+ "import": "./dist/react/index.js"
38
+ },
39
+ "./server": {
40
+ "types": "./dist/types/server/index.d.ts",
41
+ "import": "./dist/server/index.js"
42
+ },
43
+ "./styles.css": "./dist/agent-id.css",
44
+ "./script": "./dist/agent-id.js",
45
+ "./track-script": "./dist/agent-id-track.js",
46
+ "./package.json": "./package.json"
47
+ },
48
+ "peerDependencies": {
49
+ "react": ">=18 <20"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "react": {
53
+ "optional": true
54
+ }
55
+ },
56
+ "scripts": {
57
+ "build": "node scripts/build.mjs",
58
+ "typecheck": "tsc --noEmit",
59
+ "lint": "eslint . --max-warnings=0",
60
+ "format": "prettier . --write",
61
+ "format:check": "prettier . --check",
62
+ "test": "node --import tsx --test tests/*.test.ts tests/*.test.mjs",
63
+ "test:e2e": "playwright test",
64
+ "smoke:hosted": "node scripts/smoke-hosted.mjs",
65
+ "test:package": "node scripts/test-package.mjs",
66
+ "check": "npm run build && npm run typecheck && npm run lint && npm run format:check && npm test && npm run test:package",
67
+ "dev:collector": "node --import tsx scripts/dev-server.ts",
68
+ "site:create": "node scripts/site-config.mjs",
69
+ "sites:import": "node --import tsx scripts/import-sites.ts",
70
+ "events": "node scripts/events.mjs"
71
+ },
72
+ "publishConfig": {
73
+ "access": "public"
74
+ },
75
+ "devDependencies": {
76
+ "@electric-sql/pglite": "0.5.10",
77
+ "@electric-sql/pglite-socket": "0.2.13",
78
+ "@eslint/js": "10.0.1",
79
+ "@playwright/test": "1.64.1",
80
+ "@types/jsdom": "30.0.0",
81
+ "@types/node": "26.6.5",
82
+ "@types/react": "19.3.0",
83
+ "@types/react-dom": "19.3.0",
84
+ "esbuild": "0.28.2",
85
+ "eslint": "10.13.0",
86
+ "globals": "17.13.0",
87
+ "jose": "6.2.14",
88
+ "jsdom": "30.1.3",
89
+ "postgres": "3.4.9",
90
+ "prettier": "3.9.10",
91
+ "react": "19.3.1",
92
+ "react-dom": "19.3.1",
93
+ "tsx": "4.23.15",
94
+ "typescript": "6.0.3",
95
+ "typescript-eslint": "8.72.0"
96
+ }
97
+ }