@dupecom/botcha-cloudflare 0.16.0 → 0.19.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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/auth.d.ts +48 -3
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +89 -21
  5. package/dist/dashboard/docs.d.ts +15 -0
  6. package/dist/dashboard/docs.d.ts.map +1 -0
  7. package/dist/dashboard/docs.js +556 -0
  8. package/dist/dashboard/layout.d.ts +12 -0
  9. package/dist/dashboard/layout.d.ts.map +1 -1
  10. package/dist/dashboard/layout.js +12 -5
  11. package/dist/dashboard/showcase.d.ts.map +1 -1
  12. package/dist/dashboard/showcase.js +2 -1
  13. package/dist/dashboard/whitepaper.d.ts.map +1 -1
  14. package/dist/dashboard/whitepaper.js +3 -3
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +125 -13
  18. package/dist/static.d.ts +592 -2
  19. package/dist/static.d.ts.map +1 -1
  20. package/dist/static.js +422 -9
  21. package/dist/tap-attestation-routes.d.ts +204 -0
  22. package/dist/tap-attestation-routes.d.ts.map +1 -0
  23. package/dist/tap-attestation-routes.js +396 -0
  24. package/dist/tap-attestation.d.ts +178 -0
  25. package/dist/tap-attestation.d.ts.map +1 -0
  26. package/dist/tap-attestation.js +416 -0
  27. package/dist/tap-delegation-routes.d.ts +236 -0
  28. package/dist/tap-delegation-routes.d.ts.map +1 -0
  29. package/dist/tap-delegation-routes.js +378 -0
  30. package/dist/tap-delegation.d.ts +127 -0
  31. package/dist/tap-delegation.d.ts.map +1 -0
  32. package/dist/tap-delegation.js +490 -0
  33. package/dist/tap-jwks.d.ts +2 -1
  34. package/dist/tap-jwks.d.ts.map +1 -1
  35. package/dist/tap-jwks.js +31 -7
  36. package/dist/tap-reputation-routes.d.ts +154 -0
  37. package/dist/tap-reputation-routes.d.ts.map +1 -0
  38. package/dist/tap-reputation-routes.js +341 -0
  39. package/dist/tap-reputation.d.ts +136 -0
  40. package/dist/tap-reputation.d.ts.map +1 -0
  41. package/dist/tap-reputation.js +346 -0
  42. package/package.json +1 -1
package/dist/tap-jwks.js CHANGED
@@ -3,6 +3,7 @@
3
3
  * Implements .well-known/jwks for TAP agent public key discovery
4
4
  * Per Visa TAP spec: https://developer.visa.com/capabilities/trusted-agent-protocol
5
5
  */
6
+ import { getSigningPublicKeyJWK } from './auth.js';
6
7
  // ============ PEM <-> JWK CONVERSION ============
7
8
  /**
8
9
  * Convert PEM public key to JWK format
@@ -122,27 +123,49 @@ function arrayBufferToPem(buffer) {
122
123
  // ============ JWKS ENDPOINT HANDLERS ============
123
124
  /**
124
125
  * GET /.well-known/jwks
125
- * Returns JWK Set for app's TAP-enabled agents
126
+ * Returns JWK Set for app's TAP-enabled agents.
127
+ * Also includes BOTCHA's own signing public key when JWT_SIGNING_KEY is configured.
126
128
  */
127
129
  export async function jwksRoute(c) {
128
130
  try {
131
+ const allKeys = [];
132
+ // Always include BOTCHA's own signing public key if configured
133
+ const jwtSigningKeyEnv = c.env.JWT_SIGNING_KEY;
134
+ if (jwtSigningKeyEnv) {
135
+ try {
136
+ const privateKeyJwk = JSON.parse(jwtSigningKeyEnv);
137
+ const publicKeyJwk = getSigningPublicKeyJWK(privateKeyJwk);
138
+ allKeys.push({
139
+ kty: publicKeyJwk.kty,
140
+ crv: publicKeyJwk.crv,
141
+ x: publicKeyJwk.x,
142
+ y: publicKeyJwk.y,
143
+ kid: 'botcha-signing-1',
144
+ use: 'sig',
145
+ alg: 'ES256',
146
+ });
147
+ }
148
+ catch (error) {
149
+ console.error('Failed to derive BOTCHA signing public key for JWKS:', error);
150
+ }
151
+ }
129
152
  const appId = c.req.query('app_id');
130
- // Security: Don't expose all keys globally
153
+ // If no app_id, return just the BOTCHA signing key (if any)
131
154
  if (!appId) {
132
- return c.json({ keys: [] }, 200, {
155
+ return c.json({ keys: allKeys }, 200, {
133
156
  'Cache-Control': 'public, max-age=3600',
134
157
  });
135
158
  }
136
159
  const agents = c.env.AGENTS;
137
160
  if (!agents) {
138
161
  console.error('AGENTS KV namespace not available');
139
- return c.json({ keys: [] }, 200);
162
+ return c.json({ keys: allKeys }, 200);
140
163
  }
141
164
  // Get agent list for this app
142
165
  const agentIndexKey = `app_agents:${appId}`;
143
166
  const agentIdsData = await agents.get(agentIndexKey, 'text');
144
167
  if (!agentIdsData) {
145
- return c.json({ keys: [] }, 200, {
168
+ return c.json({ keys: allKeys }, 200, {
146
169
  'Cache-Control': 'public, max-age=3600',
147
170
  });
148
171
  }
@@ -174,8 +197,9 @@ export async function jwksRoute(c) {
174
197
  return null;
175
198
  }
176
199
  });
177
- const jwks = (await Promise.all(jwkPromises)).filter((jwk) => jwk !== null);
178
- return c.json({ keys: jwks }, 200, {
200
+ const agentJwks = (await Promise.all(jwkPromises)).filter((jwk) => jwk !== null);
201
+ allKeys.push(...agentJwks);
202
+ return c.json({ keys: allKeys }, 200, {
179
203
  'Cache-Control': 'public, max-age=3600',
180
204
  });
181
205
  }
@@ -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
+ };