@dupecom/botcha-cloudflare 0.15.0 → 0.18.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.
- package/dist/dashboard/landing.d.ts.map +1 -1
- package/dist/dashboard/landing.js +2 -9
- package/dist/dashboard/layout.d.ts +12 -0
- package/dist/dashboard/layout.d.ts.map +1 -1
- package/dist/dashboard/layout.js +12 -5
- package/dist/dashboard/showcase.d.ts +1 -0
- package/dist/dashboard/showcase.d.ts.map +1 -1
- package/dist/dashboard/showcase.js +3 -2
- package/dist/dashboard/whitepaper.d.ts +14 -0
- package/dist/dashboard/whitepaper.d.ts.map +1 -0
- package/dist/dashboard/whitepaper.js +418 -0
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +5 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -18
- package/dist/og-image.d.ts +2 -0
- package/dist/og-image.d.ts.map +1 -0
- package/dist/og-image.js +2 -0
- package/dist/static.d.ts +871 -2
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +812 -4
- package/dist/tap-agents.d.ts +3 -2
- package/dist/tap-agents.d.ts.map +1 -1
- package/dist/tap-agents.js +19 -6
- package/dist/tap-attestation-routes.d.ts +204 -0
- package/dist/tap-attestation-routes.d.ts.map +1 -0
- package/dist/tap-attestation-routes.js +396 -0
- package/dist/tap-attestation.d.ts +178 -0
- package/dist/tap-attestation.d.ts.map +1 -0
- package/dist/tap-attestation.js +416 -0
- package/dist/tap-consumer.d.ts +151 -0
- package/dist/tap-consumer.d.ts.map +1 -0
- package/dist/tap-consumer.js +346 -0
- package/dist/tap-delegation-routes.d.ts +236 -0
- package/dist/tap-delegation-routes.d.ts.map +1 -0
- package/dist/tap-delegation-routes.js +378 -0
- package/dist/tap-delegation.d.ts +127 -0
- package/dist/tap-delegation.d.ts.map +1 -0
- package/dist/tap-delegation.js +490 -0
- package/dist/tap-edge.d.ts +106 -0
- package/dist/tap-edge.d.ts.map +1 -0
- package/dist/tap-edge.js +487 -0
- package/dist/tap-federation.d.ts +89 -0
- package/dist/tap-federation.d.ts.map +1 -0
- package/dist/tap-federation.js +237 -0
- package/dist/tap-jwks.d.ts +64 -0
- package/dist/tap-jwks.d.ts.map +1 -0
- package/dist/tap-jwks.js +279 -0
- package/dist/tap-payment.d.ts +172 -0
- package/dist/tap-payment.d.ts.map +1 -0
- package/dist/tap-payment.js +425 -0
- package/dist/tap-reputation-routes.d.ts +154 -0
- package/dist/tap-reputation-routes.d.ts.map +1 -0
- package/dist/tap-reputation-routes.js +341 -0
- package/dist/tap-reputation.d.ts +136 -0
- package/dist/tap-reputation.d.ts.map +1 -0
- package/dist/tap-reputation.js +346 -0
- package/dist/tap-routes.d.ts +239 -2
- package/dist/tap-routes.d.ts.map +1 -1
- package/dist/tap-routes.js +279 -4
- package/dist/tap-verify.d.ts +43 -1
- package/dist/tap-verify.d.ts.map +1 -1
- package/dist/tap-verify.js +215 -30
- package/package.json +1 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TAP Agent Reputation Scoring API Routes
|
|
3
|
+
*
|
|
4
|
+
* Endpoints for querying agent reputation scores, recording events,
|
|
5
|
+
* listing event history, and resetting scores.
|
|
6
|
+
*
|
|
7
|
+
* Routes:
|
|
8
|
+
* GET /v1/reputation/:agent_id — Get agent reputation score
|
|
9
|
+
* POST /v1/reputation/events — Record a reputation event
|
|
10
|
+
* GET /v1/reputation/:agent_id/events — List reputation events
|
|
11
|
+
* POST /v1/reputation/:agent_id/reset — Reset agent reputation (admin)
|
|
12
|
+
*/
|
|
13
|
+
import type { Context } from 'hono';
|
|
14
|
+
import { type ReputationEventCategory, type ReputationEventAction } from './tap-reputation.js';
|
|
15
|
+
/**
|
|
16
|
+
* GET /v1/reputation/:agent_id
|
|
17
|
+
* Get agent reputation score
|
|
18
|
+
*/
|
|
19
|
+
export declare function getReputationRoute(c: Context): Promise<(Response & import("hono").TypedResponse<{
|
|
20
|
+
success: false;
|
|
21
|
+
error: string | undefined;
|
|
22
|
+
message: string;
|
|
23
|
+
}, 401, "json">) | (Response & import("hono").TypedResponse<{
|
|
24
|
+
success: false;
|
|
25
|
+
error: string;
|
|
26
|
+
message: string | undefined;
|
|
27
|
+
}, any, "json">) | (Response & import("hono").TypedResponse<{
|
|
28
|
+
success: true;
|
|
29
|
+
agent_id: string;
|
|
30
|
+
app_id: string;
|
|
31
|
+
score: number;
|
|
32
|
+
tier: import("./tap-reputation.js").ReputationTier;
|
|
33
|
+
event_count: number;
|
|
34
|
+
positive_events: number;
|
|
35
|
+
negative_events: number;
|
|
36
|
+
last_event_at: string | null;
|
|
37
|
+
created_at: string;
|
|
38
|
+
updated_at: string;
|
|
39
|
+
category_scores: {
|
|
40
|
+
verification: number;
|
|
41
|
+
attestation: number;
|
|
42
|
+
delegation: number;
|
|
43
|
+
session: number;
|
|
44
|
+
violation: number;
|
|
45
|
+
endorsement: number;
|
|
46
|
+
};
|
|
47
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
48
|
+
/**
|
|
49
|
+
* POST /v1/reputation/events
|
|
50
|
+
* Record a reputation event for an agent
|
|
51
|
+
*
|
|
52
|
+
* Body:
|
|
53
|
+
* agent_id — required
|
|
54
|
+
* category — required (verification, attestation, delegation, session, violation, endorsement)
|
|
55
|
+
* action — required (e.g. "challenge_solved", "attestation_issued")
|
|
56
|
+
* source_agent_id — optional (for endorsements)
|
|
57
|
+
* metadata — optional key/value pairs
|
|
58
|
+
*/
|
|
59
|
+
export declare function recordReputationEventRoute(c: Context): Promise<(Response & import("hono").TypedResponse<{
|
|
60
|
+
success: false;
|
|
61
|
+
error: string | undefined;
|
|
62
|
+
message: string;
|
|
63
|
+
}, 401, "json">) | (Response & import("hono").TypedResponse<{
|
|
64
|
+
success: false;
|
|
65
|
+
error: string;
|
|
66
|
+
message: string | undefined;
|
|
67
|
+
}, any, "json">) | (Response & import("hono").TypedResponse<{
|
|
68
|
+
success: true;
|
|
69
|
+
event: {
|
|
70
|
+
event_id: string;
|
|
71
|
+
agent_id: string;
|
|
72
|
+
category: ReputationEventCategory;
|
|
73
|
+
action: ReputationEventAction;
|
|
74
|
+
delta: number;
|
|
75
|
+
score_before: number;
|
|
76
|
+
score_after: number;
|
|
77
|
+
source_agent_id: string | null;
|
|
78
|
+
metadata: {
|
|
79
|
+
[x: string]: string;
|
|
80
|
+
} | null;
|
|
81
|
+
created_at: string;
|
|
82
|
+
};
|
|
83
|
+
score: {
|
|
84
|
+
score: number;
|
|
85
|
+
tier: import("./tap-reputation.js").ReputationTier;
|
|
86
|
+
event_count: number;
|
|
87
|
+
};
|
|
88
|
+
}, 201, "json">)>;
|
|
89
|
+
/**
|
|
90
|
+
* GET /v1/reputation/:agent_id/events
|
|
91
|
+
* List reputation events for an agent
|
|
92
|
+
*
|
|
93
|
+
* Query params:
|
|
94
|
+
* category — optional, filter by category
|
|
95
|
+
* limit — optional, max events to return (default 50, max 100)
|
|
96
|
+
*/
|
|
97
|
+
export declare function listReputationEventsRoute(c: Context): Promise<(Response & import("hono").TypedResponse<{
|
|
98
|
+
success: false;
|
|
99
|
+
error: string;
|
|
100
|
+
message: string;
|
|
101
|
+
}, 400, "json">) | (Response & import("hono").TypedResponse<{
|
|
102
|
+
success: false;
|
|
103
|
+
error: string | undefined;
|
|
104
|
+
message: string;
|
|
105
|
+
}, 401, "json">) | (Response & import("hono").TypedResponse<{
|
|
106
|
+
success: false;
|
|
107
|
+
error: string;
|
|
108
|
+
message: string | undefined;
|
|
109
|
+
}, 500, "json">) | (Response & import("hono").TypedResponse<{
|
|
110
|
+
success: true;
|
|
111
|
+
events: {
|
|
112
|
+
event_id: string;
|
|
113
|
+
agent_id: string;
|
|
114
|
+
category: ReputationEventCategory;
|
|
115
|
+
action: ReputationEventAction;
|
|
116
|
+
delta: number;
|
|
117
|
+
score_before: number;
|
|
118
|
+
score_after: number;
|
|
119
|
+
source_agent_id: string | null;
|
|
120
|
+
metadata: {
|
|
121
|
+
[x: string]: string;
|
|
122
|
+
} | null;
|
|
123
|
+
created_at: string;
|
|
124
|
+
}[];
|
|
125
|
+
count: number;
|
|
126
|
+
agent_id: string;
|
|
127
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
128
|
+
/**
|
|
129
|
+
* POST /v1/reputation/:agent_id/reset
|
|
130
|
+
* Reset agent reputation to default (admin action)
|
|
131
|
+
*/
|
|
132
|
+
export declare function resetReputationRoute(c: Context): Promise<(Response & import("hono").TypedResponse<{
|
|
133
|
+
success: false;
|
|
134
|
+
error: string | undefined;
|
|
135
|
+
message: string;
|
|
136
|
+
}, 401, "json">) | (Response & import("hono").TypedResponse<{
|
|
137
|
+
success: false;
|
|
138
|
+
error: string;
|
|
139
|
+
message: string | undefined;
|
|
140
|
+
}, any, "json">) | (Response & import("hono").TypedResponse<{
|
|
141
|
+
success: true;
|
|
142
|
+
agent_id: string;
|
|
143
|
+
score: number;
|
|
144
|
+
tier: import("./tap-reputation.js").ReputationTier;
|
|
145
|
+
message: string;
|
|
146
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
147
|
+
declare const _default: {
|
|
148
|
+
getReputationRoute: typeof getReputationRoute;
|
|
149
|
+
recordReputationEventRoute: typeof recordReputationEventRoute;
|
|
150
|
+
listReputationEventsRoute: typeof listReputationEventsRoute;
|
|
151
|
+
resetReputationRoute: typeof resetReputationRoute;
|
|
152
|
+
};
|
|
153
|
+
export default _default;
|
|
154
|
+
//# sourceMappingURL=tap-reputation-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-reputation-routes.d.ts","sourceRoot":"","sources":["../src/tap-reputation-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,EASL,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC3B,MAAM,qBAAqB,CAAC;AAkC7B;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;oEA6DlD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAAC,CAAC,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2H1D;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oEA4EzD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,OAAO;;;;;;;;;;;;;;oEAyDpD;;;;;;;AAED,wBAKE"}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TAP Agent Reputation Scoring API Routes
|
|
3
|
+
*
|
|
4
|
+
* Endpoints for querying agent reputation scores, recording events,
|
|
5
|
+
* listing event history, and resetting scores.
|
|
6
|
+
*
|
|
7
|
+
* Routes:
|
|
8
|
+
* GET /v1/reputation/:agent_id — Get agent reputation score
|
|
9
|
+
* POST /v1/reputation/events — Record a reputation event
|
|
10
|
+
* GET /v1/reputation/:agent_id/events — List reputation events
|
|
11
|
+
* POST /v1/reputation/:agent_id/reset — Reset agent reputation (admin)
|
|
12
|
+
*/
|
|
13
|
+
import { extractBearerToken, verifyToken } from './auth.js';
|
|
14
|
+
import { getReputationScore, recordReputationEvent, listReputationEvents, resetReputation, isValidCategory, isValidAction, isValidCategoryAction, } from './tap-reputation.js';
|
|
15
|
+
// ============ VALIDATION HELPERS ============
|
|
16
|
+
async function validateAppAccess(c, requireAuth = true) {
|
|
17
|
+
const queryAppId = c.req.query('app_id');
|
|
18
|
+
let jwtAppId;
|
|
19
|
+
const authHeader = c.req.header('authorization');
|
|
20
|
+
const token = extractBearerToken(authHeader);
|
|
21
|
+
if (token) {
|
|
22
|
+
const result = await verifyToken(token, c.env.JWT_SECRET, c.env);
|
|
23
|
+
if (result.valid && result.payload) {
|
|
24
|
+
jwtAppId = result.payload.app_id;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const appId = queryAppId || jwtAppId;
|
|
28
|
+
if (requireAuth && !appId) {
|
|
29
|
+
return { valid: false, error: 'MISSING_APP_ID', status: 401 };
|
|
30
|
+
}
|
|
31
|
+
return { valid: true, appId };
|
|
32
|
+
}
|
|
33
|
+
// ============ ROUTE HANDLERS ============
|
|
34
|
+
/**
|
|
35
|
+
* GET /v1/reputation/:agent_id
|
|
36
|
+
* Get agent reputation score
|
|
37
|
+
*/
|
|
38
|
+
export async function getReputationRoute(c) {
|
|
39
|
+
try {
|
|
40
|
+
const agentId = c.req.param('agent_id');
|
|
41
|
+
if (!agentId) {
|
|
42
|
+
return c.json({
|
|
43
|
+
success: false,
|
|
44
|
+
error: 'MISSING_AGENT_ID',
|
|
45
|
+
message: 'Agent ID is required'
|
|
46
|
+
}, 400);
|
|
47
|
+
}
|
|
48
|
+
const appAccess = await validateAppAccess(c, true);
|
|
49
|
+
if (!appAccess.valid) {
|
|
50
|
+
return c.json({
|
|
51
|
+
success: false,
|
|
52
|
+
error: appAccess.error,
|
|
53
|
+
message: 'Authentication required'
|
|
54
|
+
}, (appAccess.status || 401));
|
|
55
|
+
}
|
|
56
|
+
const result = await getReputationScore(c.env.SESSIONS, c.env.AGENTS, agentId, appAccess.appId);
|
|
57
|
+
if (!result.success || !result.score) {
|
|
58
|
+
const status = result.error?.includes('not found') ? 404 : 500;
|
|
59
|
+
return c.json({
|
|
60
|
+
success: false,
|
|
61
|
+
error: 'REPUTATION_LOOKUP_FAILED',
|
|
62
|
+
message: result.error
|
|
63
|
+
}, status);
|
|
64
|
+
}
|
|
65
|
+
const s = result.score;
|
|
66
|
+
return c.json({
|
|
67
|
+
success: true,
|
|
68
|
+
agent_id: s.agent_id,
|
|
69
|
+
app_id: s.app_id,
|
|
70
|
+
score: s.score,
|
|
71
|
+
tier: s.tier,
|
|
72
|
+
event_count: s.event_count,
|
|
73
|
+
positive_events: s.positive_events,
|
|
74
|
+
negative_events: s.negative_events,
|
|
75
|
+
last_event_at: s.last_event_at ? new Date(s.last_event_at).toISOString() : null,
|
|
76
|
+
created_at: new Date(s.created_at).toISOString(),
|
|
77
|
+
updated_at: new Date(s.updated_at).toISOString(),
|
|
78
|
+
category_scores: s.category_scores,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error('Reputation lookup error:', error);
|
|
83
|
+
return c.json({
|
|
84
|
+
success: false,
|
|
85
|
+
error: 'INTERNAL_ERROR',
|
|
86
|
+
message: 'Internal server error'
|
|
87
|
+
}, 500);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* POST /v1/reputation/events
|
|
92
|
+
* Record a reputation event for an agent
|
|
93
|
+
*
|
|
94
|
+
* Body:
|
|
95
|
+
* agent_id — required
|
|
96
|
+
* category — required (verification, attestation, delegation, session, violation, endorsement)
|
|
97
|
+
* action — required (e.g. "challenge_solved", "attestation_issued")
|
|
98
|
+
* source_agent_id — optional (for endorsements)
|
|
99
|
+
* metadata — optional key/value pairs
|
|
100
|
+
*/
|
|
101
|
+
export async function recordReputationEventRoute(c) {
|
|
102
|
+
try {
|
|
103
|
+
const appAccess = await validateAppAccess(c, true);
|
|
104
|
+
if (!appAccess.valid) {
|
|
105
|
+
return c.json({
|
|
106
|
+
success: false,
|
|
107
|
+
error: appAccess.error,
|
|
108
|
+
message: 'Authentication required'
|
|
109
|
+
}, (appAccess.status || 401));
|
|
110
|
+
}
|
|
111
|
+
const body = await c.req.json().catch(() => ({}));
|
|
112
|
+
// Validate required fields
|
|
113
|
+
if (!body.agent_id) {
|
|
114
|
+
return c.json({
|
|
115
|
+
success: false,
|
|
116
|
+
error: 'MISSING_AGENT_ID',
|
|
117
|
+
message: 'agent_id is required'
|
|
118
|
+
}, 400);
|
|
119
|
+
}
|
|
120
|
+
if (!body.category) {
|
|
121
|
+
return c.json({
|
|
122
|
+
success: false,
|
|
123
|
+
error: 'MISSING_CATEGORY',
|
|
124
|
+
message: 'category is required (verification, attestation, delegation, session, violation, endorsement)'
|
|
125
|
+
}, 400);
|
|
126
|
+
}
|
|
127
|
+
if (!isValidCategory(body.category)) {
|
|
128
|
+
return c.json({
|
|
129
|
+
success: false,
|
|
130
|
+
error: 'INVALID_CATEGORY',
|
|
131
|
+
message: `Invalid category: "${body.category}". Must be one of: verification, attestation, delegation, session, violation, endorsement`
|
|
132
|
+
}, 400);
|
|
133
|
+
}
|
|
134
|
+
if (!body.action) {
|
|
135
|
+
return c.json({
|
|
136
|
+
success: false,
|
|
137
|
+
error: 'MISSING_ACTION',
|
|
138
|
+
message: 'action is required (e.g. "challenge_solved", "attestation_issued")'
|
|
139
|
+
}, 400);
|
|
140
|
+
}
|
|
141
|
+
if (!isValidAction(body.action)) {
|
|
142
|
+
return c.json({
|
|
143
|
+
success: false,
|
|
144
|
+
error: 'INVALID_ACTION',
|
|
145
|
+
message: `Invalid action: "${body.action}"`
|
|
146
|
+
}, 400);
|
|
147
|
+
}
|
|
148
|
+
if (!isValidCategoryAction(body.category, body.action)) {
|
|
149
|
+
return c.json({
|
|
150
|
+
success: false,
|
|
151
|
+
error: 'ACTION_CATEGORY_MISMATCH',
|
|
152
|
+
message: `Action "${body.action}" does not belong to category "${body.category}"`
|
|
153
|
+
}, 400);
|
|
154
|
+
}
|
|
155
|
+
const options = {
|
|
156
|
+
agent_id: body.agent_id,
|
|
157
|
+
category: body.category,
|
|
158
|
+
action: body.action,
|
|
159
|
+
source_agent_id: body.source_agent_id,
|
|
160
|
+
metadata: body.metadata,
|
|
161
|
+
};
|
|
162
|
+
const result = await recordReputationEvent(c.env.SESSIONS, c.env.AGENTS, appAccess.appId, options);
|
|
163
|
+
if (!result.success) {
|
|
164
|
+
const status = result.error?.includes('not found') ? 404
|
|
165
|
+
: result.error?.includes('does not belong') ? 403
|
|
166
|
+
: result.error?.includes('cannot endorse itself') ? 400
|
|
167
|
+
: result.error?.includes('does not belong to category') ? 400
|
|
168
|
+
: 400;
|
|
169
|
+
return c.json({
|
|
170
|
+
success: false,
|
|
171
|
+
error: 'EVENT_RECORDING_FAILED',
|
|
172
|
+
message: result.error
|
|
173
|
+
}, status);
|
|
174
|
+
}
|
|
175
|
+
const event = result.event;
|
|
176
|
+
const score = result.score;
|
|
177
|
+
return c.json({
|
|
178
|
+
success: true,
|
|
179
|
+
event: {
|
|
180
|
+
event_id: event.event_id,
|
|
181
|
+
agent_id: event.agent_id,
|
|
182
|
+
category: event.category,
|
|
183
|
+
action: event.action,
|
|
184
|
+
delta: event.delta,
|
|
185
|
+
score_before: event.score_before,
|
|
186
|
+
score_after: event.score_after,
|
|
187
|
+
source_agent_id: event.source_agent_id || null,
|
|
188
|
+
metadata: event.metadata || null,
|
|
189
|
+
created_at: new Date(event.created_at).toISOString(),
|
|
190
|
+
},
|
|
191
|
+
score: {
|
|
192
|
+
score: score.score,
|
|
193
|
+
tier: score.tier,
|
|
194
|
+
event_count: score.event_count,
|
|
195
|
+
},
|
|
196
|
+
}, 201);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
console.error('Reputation event recording error:', error);
|
|
200
|
+
return c.json({
|
|
201
|
+
success: false,
|
|
202
|
+
error: 'INTERNAL_ERROR',
|
|
203
|
+
message: 'Internal server error'
|
|
204
|
+
}, 500);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* GET /v1/reputation/:agent_id/events
|
|
209
|
+
* List reputation events for an agent
|
|
210
|
+
*
|
|
211
|
+
* Query params:
|
|
212
|
+
* category — optional, filter by category
|
|
213
|
+
* limit — optional, max events to return (default 50, max 100)
|
|
214
|
+
*/
|
|
215
|
+
export async function listReputationEventsRoute(c) {
|
|
216
|
+
try {
|
|
217
|
+
const agentId = c.req.param('agent_id');
|
|
218
|
+
if (!agentId) {
|
|
219
|
+
return c.json({
|
|
220
|
+
success: false,
|
|
221
|
+
error: 'MISSING_AGENT_ID',
|
|
222
|
+
message: 'Agent ID is required'
|
|
223
|
+
}, 400);
|
|
224
|
+
}
|
|
225
|
+
const appAccess = await validateAppAccess(c, true);
|
|
226
|
+
if (!appAccess.valid) {
|
|
227
|
+
return c.json({
|
|
228
|
+
success: false,
|
|
229
|
+
error: appAccess.error,
|
|
230
|
+
message: 'Authentication required'
|
|
231
|
+
}, (appAccess.status || 401));
|
|
232
|
+
}
|
|
233
|
+
const categoryParam = c.req.query('category');
|
|
234
|
+
const limitParam = c.req.query('limit');
|
|
235
|
+
const limit = limitParam ? Math.min(parseInt(limitParam, 10) || 50, 100) : 50;
|
|
236
|
+
let category;
|
|
237
|
+
if (categoryParam) {
|
|
238
|
+
if (!isValidCategory(categoryParam)) {
|
|
239
|
+
return c.json({
|
|
240
|
+
success: false,
|
|
241
|
+
error: 'INVALID_CATEGORY',
|
|
242
|
+
message: `Invalid category filter: "${categoryParam}"`
|
|
243
|
+
}, 400);
|
|
244
|
+
}
|
|
245
|
+
category = categoryParam;
|
|
246
|
+
}
|
|
247
|
+
const result = await listReputationEvents(c.env.SESSIONS, agentId, { category, limit });
|
|
248
|
+
if (!result.success) {
|
|
249
|
+
return c.json({
|
|
250
|
+
success: false,
|
|
251
|
+
error: 'EVENT_LISTING_FAILED',
|
|
252
|
+
message: result.error
|
|
253
|
+
}, 500);
|
|
254
|
+
}
|
|
255
|
+
const events = (result.events || [])
|
|
256
|
+
.filter(e => e.app_id === appAccess.appId)
|
|
257
|
+
.map(e => ({
|
|
258
|
+
event_id: e.event_id,
|
|
259
|
+
agent_id: e.agent_id,
|
|
260
|
+
category: e.category,
|
|
261
|
+
action: e.action,
|
|
262
|
+
delta: e.delta,
|
|
263
|
+
score_before: e.score_before,
|
|
264
|
+
score_after: e.score_after,
|
|
265
|
+
source_agent_id: e.source_agent_id || null,
|
|
266
|
+
metadata: e.metadata || null,
|
|
267
|
+
created_at: new Date(e.created_at).toISOString(),
|
|
268
|
+
}));
|
|
269
|
+
return c.json({
|
|
270
|
+
success: true,
|
|
271
|
+
events,
|
|
272
|
+
count: events.length,
|
|
273
|
+
agent_id: agentId,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
console.error('Reputation event listing error:', error);
|
|
278
|
+
return c.json({
|
|
279
|
+
success: false,
|
|
280
|
+
error: 'INTERNAL_ERROR',
|
|
281
|
+
message: 'Internal server error'
|
|
282
|
+
}, 500);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* POST /v1/reputation/:agent_id/reset
|
|
287
|
+
* Reset agent reputation to default (admin action)
|
|
288
|
+
*/
|
|
289
|
+
export async function resetReputationRoute(c) {
|
|
290
|
+
try {
|
|
291
|
+
const agentId = c.req.param('agent_id');
|
|
292
|
+
if (!agentId) {
|
|
293
|
+
return c.json({
|
|
294
|
+
success: false,
|
|
295
|
+
error: 'MISSING_AGENT_ID',
|
|
296
|
+
message: 'Agent ID is required'
|
|
297
|
+
}, 400);
|
|
298
|
+
}
|
|
299
|
+
const appAccess = await validateAppAccess(c, true);
|
|
300
|
+
if (!appAccess.valid) {
|
|
301
|
+
return c.json({
|
|
302
|
+
success: false,
|
|
303
|
+
error: appAccess.error,
|
|
304
|
+
message: 'Authentication required'
|
|
305
|
+
}, (appAccess.status || 401));
|
|
306
|
+
}
|
|
307
|
+
const result = await resetReputation(c.env.SESSIONS, c.env.AGENTS, agentId, appAccess.appId);
|
|
308
|
+
if (!result.success) {
|
|
309
|
+
const status = result.error?.includes('not found') ? 404
|
|
310
|
+
: result.error?.includes('does not belong') ? 403
|
|
311
|
+
: 500;
|
|
312
|
+
return c.json({
|
|
313
|
+
success: false,
|
|
314
|
+
error: 'REPUTATION_RESET_FAILED',
|
|
315
|
+
message: result.error
|
|
316
|
+
}, status);
|
|
317
|
+
}
|
|
318
|
+
const s = result.score;
|
|
319
|
+
return c.json({
|
|
320
|
+
success: true,
|
|
321
|
+
agent_id: s.agent_id,
|
|
322
|
+
score: s.score,
|
|
323
|
+
tier: s.tier,
|
|
324
|
+
message: 'Reputation reset to default. All event history cleared.',
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
console.error('Reputation reset error:', error);
|
|
329
|
+
return c.json({
|
|
330
|
+
success: false,
|
|
331
|
+
error: 'INTERNAL_ERROR',
|
|
332
|
+
message: 'Internal server error'
|
|
333
|
+
}, 500);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
export default {
|
|
337
|
+
getReputationRoute,
|
|
338
|
+
recordReputationEventRoute,
|
|
339
|
+
listReputationEventsRoute,
|
|
340
|
+
resetReputationRoute,
|
|
341
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TAP Agent Reputation Scoring
|
|
3
|
+
*
|
|
4
|
+
* The "credit score" for AI agents. Persistent identity enables behavioral
|
|
5
|
+
* tracking over time, producing trust scores that unlock higher rate limits,
|
|
6
|
+
* faster verification, and access to sensitive APIs.
|
|
7
|
+
*
|
|
8
|
+
* Scoring model:
|
|
9
|
+
* - Base score: 500 (neutral, no history)
|
|
10
|
+
* - Range: 0..1000
|
|
11
|
+
* - Events adjust score via weighted deltas
|
|
12
|
+
* - Decay: scores trend toward 500 over time without activity (mean reversion)
|
|
13
|
+
* - Tiers: untrusted (0-199), low (200-399), neutral (400-599),
|
|
14
|
+
* good (600-799), excellent (800-1000)
|
|
15
|
+
*
|
|
16
|
+
* Event categories:
|
|
17
|
+
* - verification: challenge solved, auth success/failure
|
|
18
|
+
* - attestation: issued, verified, revoked
|
|
19
|
+
* - delegation: granted, received, revoked
|
|
20
|
+
* - session: created, expired normally, force-terminated
|
|
21
|
+
* - violation: rate limit exceeded, invalid token, abuse detected
|
|
22
|
+
* - endorsement: explicit trust signal from another agent or app
|
|
23
|
+
*
|
|
24
|
+
* KV storage (SESSIONS namespace):
|
|
25
|
+
* - reputation:{agent_id} — ReputationScore record
|
|
26
|
+
* - reputation_events:{agent_id} — Array of event IDs (index)
|
|
27
|
+
* - reputation_event:{event_id} — Individual ReputationEvent (with TTL)
|
|
28
|
+
*/
|
|
29
|
+
import type { KVNamespace } from './agents.js';
|
|
30
|
+
export type ReputationTier = 'untrusted' | 'low' | 'neutral' | 'good' | 'excellent';
|
|
31
|
+
export type ReputationEventCategory = 'verification' | 'attestation' | 'delegation' | 'session' | 'violation' | 'endorsement';
|
|
32
|
+
export type ReputationEventAction = 'challenge_solved' | 'challenge_failed' | 'auth_success' | 'auth_failure' | 'attestation_issued' | 'attestation_verified' | 'attestation_revoked' | 'delegation_granted' | 'delegation_received' | 'delegation_revoked' | 'session_created' | 'session_expired' | 'session_terminated' | 'rate_limit_exceeded' | 'invalid_token' | 'abuse_detected' | 'endorsement_received' | 'endorsement_given';
|
|
33
|
+
export interface ReputationEvent {
|
|
34
|
+
event_id: string;
|
|
35
|
+
agent_id: string;
|
|
36
|
+
app_id: string;
|
|
37
|
+
category: ReputationEventCategory;
|
|
38
|
+
action: ReputationEventAction;
|
|
39
|
+
delta: number;
|
|
40
|
+
score_before: number;
|
|
41
|
+
score_after: number;
|
|
42
|
+
source_agent_id?: string;
|
|
43
|
+
metadata?: Record<string, string>;
|
|
44
|
+
created_at: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ReputationScore {
|
|
47
|
+
agent_id: string;
|
|
48
|
+
app_id: string;
|
|
49
|
+
score: number;
|
|
50
|
+
tier: ReputationTier;
|
|
51
|
+
event_count: number;
|
|
52
|
+
positive_events: number;
|
|
53
|
+
negative_events: number;
|
|
54
|
+
last_event_at: number | null;
|
|
55
|
+
created_at: number;
|
|
56
|
+
updated_at: number;
|
|
57
|
+
category_scores: {
|
|
58
|
+
verification: number;
|
|
59
|
+
attestation: number;
|
|
60
|
+
delegation: number;
|
|
61
|
+
session: number;
|
|
62
|
+
violation: number;
|
|
63
|
+
endorsement: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export interface RecordEventOptions {
|
|
67
|
+
agent_id: string;
|
|
68
|
+
category: ReputationEventCategory;
|
|
69
|
+
action: ReputationEventAction;
|
|
70
|
+
source_agent_id?: string;
|
|
71
|
+
metadata?: Record<string, string>;
|
|
72
|
+
}
|
|
73
|
+
export interface ReputationResult {
|
|
74
|
+
success: boolean;
|
|
75
|
+
score?: ReputationScore;
|
|
76
|
+
error?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface EventResult {
|
|
79
|
+
success: boolean;
|
|
80
|
+
event?: ReputationEvent;
|
|
81
|
+
score?: ReputationScore;
|
|
82
|
+
error?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface EventListResult {
|
|
85
|
+
success: boolean;
|
|
86
|
+
events?: ReputationEvent[];
|
|
87
|
+
count?: number;
|
|
88
|
+
error?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare function getTier(score: number): ReputationTier;
|
|
91
|
+
/**
|
|
92
|
+
* Apply mean-reversion decay. For every 7 days since last activity,
|
|
93
|
+
* nudge the score 1% toward BASE_SCORE.
|
|
94
|
+
*/
|
|
95
|
+
export declare function applyDecay(score: ReputationScore): ReputationScore;
|
|
96
|
+
/**
|
|
97
|
+
* Get the reputation score for an agent. Creates a default score if none exists.
|
|
98
|
+
*/
|
|
99
|
+
export declare function getReputationScore(sessions: KVNamespace, agents: KVNamespace, agentId: string, appId: string): Promise<ReputationResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Record a reputation event for an agent.
|
|
102
|
+
* Creates the reputation record if it doesn't exist yet.
|
|
103
|
+
*/
|
|
104
|
+
export declare function recordReputationEvent(sessions: KVNamespace, agents: KVNamespace, appId: string, options: RecordEventOptions): Promise<EventResult>;
|
|
105
|
+
/**
|
|
106
|
+
* List reputation events for an agent.
|
|
107
|
+
* Returns the most recent events (up to limit).
|
|
108
|
+
*/
|
|
109
|
+
export declare function listReputationEvents(sessions: KVNamespace, agentId: string, options?: {
|
|
110
|
+
category?: ReputationEventCategory;
|
|
111
|
+
limit?: number;
|
|
112
|
+
}): Promise<EventListResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Reset an agent's reputation to default. Used by app admins.
|
|
115
|
+
*/
|
|
116
|
+
export declare function resetReputation(sessions: KVNamespace, agents: KVNamespace, agentId: string, appId: string): Promise<ReputationResult>;
|
|
117
|
+
export declare function isValidCategoryAction(category: ReputationEventCategory, action: ReputationEventAction): boolean;
|
|
118
|
+
export declare function isValidCategory(category: string): category is ReputationEventCategory;
|
|
119
|
+
export declare function isValidAction(action: string): action is ReputationEventAction;
|
|
120
|
+
declare const _default: {
|
|
121
|
+
getReputationScore: typeof getReputationScore;
|
|
122
|
+
recordReputationEvent: typeof recordReputationEvent;
|
|
123
|
+
listReputationEvents: typeof listReputationEvents;
|
|
124
|
+
resetReputation: typeof resetReputation;
|
|
125
|
+
getTier: typeof getTier;
|
|
126
|
+
applyDecay: typeof applyDecay;
|
|
127
|
+
isValidCategoryAction: typeof isValidCategoryAction;
|
|
128
|
+
isValidCategory: typeof isValidCategory;
|
|
129
|
+
isValidAction: typeof isValidAction;
|
|
130
|
+
ACTION_DELTAS: Record<ReputationEventAction, number>;
|
|
131
|
+
BASE_SCORE: number;
|
|
132
|
+
MIN_SCORE: number;
|
|
133
|
+
MAX_SCORE: number;
|
|
134
|
+
};
|
|
135
|
+
export default _default;
|
|
136
|
+
//# sourceMappingURL=tap-reputation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-reputation.d.ts","sourceRoot":"","sources":["../src/tap-reputation.ts"],"names":[],"mappings":"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"}
|