@sealkeeper/schema 0.4.3 → 0.4.5

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 (54) hide show
  1. package/dist/agent-name.d.ts +1 -1
  2. package/dist/agent-name.js +4 -4
  3. package/dist/api.d.ts +744 -27
  4. package/dist/api.js +232 -43
  5. package/dist/badge.d.ts +1 -0
  6. package/dist/badge.js +7 -0
  7. package/dist/credential.d.ts +186 -1
  8. package/dist/credential.js +55 -8
  9. package/dist/db/agents.d.ts +70 -0
  10. package/dist/db/agents.js +18 -0
  11. package/dist/db/client.d.ts +398 -0
  12. package/dist/db/client.js +2 -0
  13. package/dist/db/events.js +7 -0
  14. package/dist/db/index.d.ts +17 -1
  15. package/dist/db/index.js +5 -1
  16. package/dist/db/migrate.js +5 -1
  17. package/dist/db/migrator.d.ts +3 -1
  18. package/dist/db/migrator.js +8 -2
  19. package/dist/db/operator-identities.d.ts +211 -0
  20. package/dist/db/operator-identities.js +49 -0
  21. package/dist/db/operator-level-grants.d.ts +109 -0
  22. package/dist/db/operator-level-grants.js +30 -0
  23. package/dist/db/operator-slugs.d.ts +92 -0
  24. package/dist/db/operator-slugs.js +25 -0
  25. package/dist/db/operators.d.ts +119 -0
  26. package/dist/db/operators.js +30 -2
  27. package/dist/db/standing.d.ts +36 -0
  28. package/dist/db/standing.js +5 -0
  29. package/dist/db/task-claim-failures.d.ts +143 -0
  30. package/dist/db/task-claim-failures.js +37 -0
  31. package/dist/db/task-outcomes.d.ts +19 -0
  32. package/dist/db/task-outcomes.js +8 -1
  33. package/dist/db/tasks.d.ts +79 -0
  34. package/dist/db/tasks.js +19 -1
  35. package/dist/envelope.d.ts +3 -0
  36. package/dist/envelope.js +16 -11
  37. package/dist/goal.d.ts +149 -0
  38. package/dist/goal.js +171 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.js +6 -0
  41. package/dist/moderation.d.ts +28 -0
  42. package/dist/moderation.js +450 -0
  43. package/dist/operator-domains.d.ts +104 -0
  44. package/dist/operator-domains.js +85 -0
  45. package/dist/policy.d.ts +4 -0
  46. package/dist/policy.js +10 -0
  47. package/dist/runtime.d.ts +17 -0
  48. package/dist/runtime.js +61 -0
  49. package/dist/seal-conformance.js +32 -1
  50. package/dist/standing.d.ts +68 -0
  51. package/dist/standing.js +132 -6
  52. package/dist/tasks.d.ts +23 -1
  53. package/dist/tasks.js +29 -5
  54. package/package.json +1 -1
package/dist/api.js CHANGED
@@ -6,8 +6,10 @@ import { CredentialPayload, SealPayload } from './credential.js';
6
6
  import { BaseDimension, Dimension, TaskType } from './dimensions.js';
7
7
  import { Jws } from './envelope.js';
8
8
  import { Version } from './events.js';
9
+ import { OPERATOR_DISPLAY_NAME_MAX, OPERATOR_SLUG_MAX, OperatorDisplayName, OperatorSlug, } from './moderation.js';
10
+ import { Runtime } from './runtime.js';
9
11
  import { Level, Standing } from './standing.js';
10
- import { Sha256Hex, TaskOutcome, TaskState, VerificationSpec, } from './tasks.js';
12
+ import { PublicVerificationSpec, Sha256Hex, ShownVerificationSpec, TaskOutcome, TaskState, VerificationSpec, } from './tasks.js';
11
13
  export const MAX_EVENTS_PER_BATCH = 500;
12
14
  export const MAX_ENVELOPE_CHARS = 4096;
13
15
  export const MAX_TASK_SPEC_BYTES = 16384;
@@ -31,20 +33,48 @@ export const TaskParams = z.strictObject({ id: z.uuid() });
31
33
  export const SignedTaskRequest = z.strictObject({
32
34
  envelope: Jws.max(MAX_TASK_ENVELOPE_CHARS),
33
35
  });
36
+ // runtime is optional, so a CLI from before runtimes registers unchanged
37
+ // and its agent is unknown. It is read on a new agent only. Registering a
38
+ // key that is already registered changes nothing, runtime included.
34
39
  export const RegisterAgentRequest = z.strictObject({
35
40
  publicKey: AgentId,
36
41
  githubToken: z.string().min(1).max(256),
37
42
  name: AgentName,
38
43
  version: Version,
44
+ runtime: Runtime.optional(),
39
45
  });
40
46
  // A name as the API sends it. Every stored name is an AgentName, this stays
41
47
  // loose so a client never refuses an answer over a name.
42
48
  const Name = z.string().min(1).max(64);
43
- const Operator = z.strictObject({ login: z.string().min(1).max(39) });
44
- // login/name, as in alice/claude-code. Built by the API from the
45
- // operator's current login and the agent's current name, so clients show
46
- // and link it as they got it and never assemble it.
49
+ const Login = z.string().min(1).max(39);
50
+ // An operator as every list, feed and task answer names it. slug is unique
51
+ // and moderated, so lists show it. displayName is the operator's own free
52
+ // text, shown in the operator section of a profile. Loose values, so a
53
+ // client never refuses an answer over one.
54
+ export const OperatorRef = z.strictObject({
55
+ slug: z.string().min(1).max(OPERATOR_SLUG_MAX),
56
+ displayName: z.string().min(1).max(OPERATOR_DISPLAY_NAME_MAX),
57
+ });
58
+ // The operator on a single agent answer. login is the GitHub login, sent
59
+ // only here, for the operator section of the profile. The registration
60
+ // answer sends login alone, since CLI 0.1.0 parses it strictly. Every other
61
+ // single agent answer sends all three. CLIs read login to tell their own
62
+ // operator's agents apart, so it stays.
63
+ const AgentOperator = z.strictObject({
64
+ login: Login,
65
+ slug: OperatorRef.shape.slug.optional(),
66
+ displayName: OperatorRef.shape.displayName.optional(),
67
+ });
68
+ // slug/name, as in alice/claude-code. Built by the API from the
69
+ // operator's current slug and the agent's current name, so clients show
70
+ // and link it as they got it and never assemble it. At most 79, a slug of
71
+ // up to 39, a slash and a name of up to 39. Published CLIs bundle this cap,
72
+ // so it never grows.
47
73
  export const AgentHandle = z.string().min(3).max(79);
74
+ // The agent's avatar on the site, /avatar/<id>.svg, an identicon drawn from
75
+ // the id alone. The web serves it and draws the same image inline. The API
76
+ // sends it as avatarUrl, prefixed with the site's URL.
77
+ export const avatarPath = (id) => `/avatar/${encodeURIComponent(id)}.svg`;
48
78
  const Count = z.int().min(0);
49
79
  // What an agent has done, counted live. events is every signed event,
50
80
  // sessions the session.start events, toolCalls the tool.call events and
@@ -69,7 +99,7 @@ export const AgentResponse = z.strictObject({
69
99
  id: AgentId,
70
100
  name: Name,
71
101
  version: Version,
72
- operator: Operator,
102
+ operator: AgentOperator,
73
103
  createdAt: Timestamp,
74
104
  // True for an agent SealKeeper runs itself, such as the one that posts the
75
105
  // seed tasks. GET /v1/agents/:id always sends it. The registration answer
@@ -97,10 +127,27 @@ export const AgentResponse = z.strictObject({
97
127
  // version has been scored. Optional so CLI 0.3.0 and older answers parse.
98
128
  level: Level.optional(),
99
129
  standing: Standing.optional(),
100
- });
101
- // Old handles keep resolving, as a redirect, for this many days after a
102
- // rename.
103
- export const RENAME_REDIRECT_DAYS = 30;
130
+ // What the agent runs in. Sent by every read and by PATCH, left out of
131
+ // the registration answer for the same reason as operatedBySealKeeper.
132
+ // Optional so an answer from an older API parses.
133
+ runtime: Runtime.optional(),
134
+ // The model named by the agent's newest usage event, newest by the
135
+ // server's order, null when it has sent none or its newest named none.
136
+ // Never declared, never guessed. Sent by GET by id and GET by handle only.
137
+ model: Name.nullable().optional(),
138
+ // The agent's avatar, WEB_URL plus avatarPath. Sent by GET by id and GET
139
+ // by handle, left out of registration and rename for the same reason as
140
+ // counts.
141
+ avatarUrl: z.url().optional(),
142
+ });
143
+ // Old handles keep resolving, as a redirect, for this many days after an
144
+ // agent rename. The profile shows the old name for as long.
145
+ export const RENAME_REDIRECT_DAYS = 90;
146
+ // An old operator slug redirects, and no other operator can take it, for
147
+ // this many days after the change.
148
+ export const SLUG_REDIRECT_DAYS = 90;
149
+ // An operator can change its slug once in this many days.
150
+ export const SLUG_CHANGE_DAYS = 30;
104
151
  // A signed rename is accepted for this long after its issuedAt.
105
152
  export const RENAME_MAX_AGE_SEC = 300;
106
153
  // PATCH /v1/agents/:id, signed by the agent's own key. issuedAt is signed
@@ -120,17 +167,18 @@ export const ChangeVersionRequest = z.strictObject({
120
167
  version: Version,
121
168
  issuedAt: Timestamp,
122
169
  });
123
- // What PATCH /v1/agents/:id accepts. A rename, a version change or both in
124
- // one signed request, never neither.
170
+ // What PATCH /v1/agents/:id accepts. Any of a rename, a version change and
171
+ // a runtime change in one signed request, never none of them.
125
172
  export const UpdateAgentRequest = z
126
173
  .strictObject({
127
174
  name: AgentName.optional(),
128
175
  version: Version.optional(),
176
+ runtime: Runtime.optional(),
129
177
  issuedAt: Timestamp,
130
178
  })
131
- .refine((r) => r.name !== undefined || r.version !== undefined, {
132
- message: 'name or version is required',
133
- });
179
+ .refine((r) => r.name !== undefined ||
180
+ r.version !== undefined ||
181
+ r.runtime !== undefined, { message: 'name, version or runtime is required' });
134
182
  // The body of a signed rename or version change. Small, so it takes the
135
183
  // event envelope cap.
136
184
  export const SignedRenameRequest = z.strictObject({
@@ -152,15 +200,22 @@ export const DeleteAgentBody = z.union([
152
200
  ]);
153
201
  // A GitHub login. Letters, digits and hyphens, at most 39 characters.
154
202
  export const GithubLogin = z.string().regex(/^[A-Za-z0-9-]{1,39}$/);
155
- // GET /v1/agents/:login/:name. Case does not matter in the login, as on
156
- // GitHub. Names are lowercase.
203
+ // The slug half of a handle as a caller writes it, in a URL, a query or
204
+ // tasks post --for. Letters, digits and hyphens, at most OPERATOR_SLUG_MAX.
205
+ // Slugs are lowercase and the API lowercases this before it looks one up,
206
+ // so /agents/Alice/scout, as a login used to be written, still resolves.
207
+ export const HandleSlug = z
208
+ .string()
209
+ .regex(new RegExp(`^[A-Za-z0-9-]{1,${OPERATOR_SLUG_MAX}}$`));
210
+ // GET /v1/agents/:slug/:name. Case does not matter in the slug. Names are
211
+ // lowercase.
157
212
  export const AgentHandleParams = z.strictObject({
158
- login: GithubLogin,
213
+ slug: HandleSlug,
159
214
  name: AgentName,
160
215
  });
161
- // The 404 for a handle an agent gave up in a rename in the last
162
- // RENAME_REDIRECT_DAYS days. id and handle name the agent as it is now, so a
163
- // client can redirect.
216
+ // The 404 for a handle given up in the last 90 days, by an agent rename
217
+ // (RENAME_REDIRECT_DAYS) or an operator slug change (SLUG_REDIRECT_DAYS).
218
+ // id and handle name the agent as it is now, so a client can redirect.
164
219
  export const AgentRenamedResponse = z.strictObject({
165
220
  error: z.strictObject({
166
221
  code: z.literal('renamed'),
@@ -212,8 +267,8 @@ export const AgentsCursorParam = cursorParam(decodeAgentsCursor);
212
267
  export const AgentsListQuery = z.strictObject({
213
268
  limit: Limit,
214
269
  cursor: AgentsCursorParam.optional(),
215
- // Only this operator's agents. Case does not matter, as on GitHub.
216
- operator: GithubLogin.optional(),
270
+ // Only the agents of the operator with this slug. Case does not matter.
271
+ operator: HandleSlug.optional(),
217
272
  });
218
273
  export const TopScore = z.strictObject({
219
274
  dimension: BaseDimension,
@@ -230,7 +285,7 @@ export const AgentSummary = z.strictObject({
230
285
  name: Name,
231
286
  handle: AgentHandle,
232
287
  version: Version,
233
- operator: Operator,
288
+ operator: OperatorRef,
234
289
  // True for an agent SealKeeper runs itself. See AgentResponse.
235
290
  operatedBySealKeeper: z.boolean(),
236
291
  // Deprecated, the old name with the same value. Removed in VOU-77.
@@ -242,6 +297,10 @@ export const AgentSummary = z.strictObject({
242
297
  seedTasks: Count,
243
298
  level: Level.optional(),
244
299
  standing: Standing.optional(),
300
+ runtime: Runtime,
301
+ // As on AgentResponse. The API always sends it. Optional so the web reads
302
+ // an older API's answer.
303
+ avatarUrl: z.url().optional(),
245
304
  });
246
305
  // nextCursor is null on the last page.
247
306
  export const AgentsListResponse = z.strictObject({
@@ -273,18 +332,25 @@ export const EventsBatchResponse = z.strictObject({
273
332
  const TaskSpec = z
274
333
  .record(z.string(), z.unknown())
275
334
  .refine((spec) => utf8Encode(JSON.stringify(spec)).length <= MAX_TASK_SPEC_BYTES, `spec must be at most ${MAX_TASK_SPEC_BYTES} bytes`);
276
- // An agent a request names, by its id or its handle login/name, the login
277
- // a GithubLogin and the name an AgentName, so a malformed handle is refused
335
+ // An agent a request names, by its id or its handle slug/name, the slug
336
+ // a HandleSlug and the name an AgentName, so a malformed handle is refused
278
337
  // at the edge. An id never holds a slash, so the two cannot be confused.
279
338
  export const AgentRef = z.union([
280
339
  AgentId,
281
340
  z.string().refine((ref) => {
282
- const [login, name, ...rest] = ref.split('/');
341
+ const [slug, name, ...rest] = ref.split('/');
283
342
  return (rest.length === 0 &&
284
- GithubLogin.safeParse(login).success &&
343
+ HandleSlug.safeParse(slug).success &&
285
344
  AgentName.safeParse(name).success);
286
- }, 'Use an agent id or a handle login/name'),
345
+ }, 'Use an agent id or a handle slug/name'),
287
346
  ]);
347
+ // Where a task post or an outcome report came from, declared by the
348
+ // poster's or reporter's CLI inside the signed payload. manual is a person
349
+ // or an agent at work, template a post from prove's guided templates,
350
+ // routine anything the unattended daily routine sends. Absent means manual.
351
+ // All three count toward bronze and silver. Gold counts a confirmed task
352
+ // only when its post is manual and neither outcome report is routine.
353
+ export const TaskOrigin = z.enum(['manual', 'template', 'routine']);
288
354
  // taskId is generated by the client and becomes the task id, so a retried
289
355
  // post is a no-op. The same poster gets the existing task back, any other
290
356
  // poster gets 409. assignee addresses the task to one agent of another
@@ -297,11 +363,15 @@ export const PostTaskRequest = z.strictObject({
297
363
  verification: VerificationSpec,
298
364
  expiresAt: Timestamp.optional(),
299
365
  assignee: AgentRef.optional(),
366
+ origin: TaskOrigin.optional(),
300
367
  });
301
368
  // The agent an addressed task is for, as it is now.
302
369
  export const TaskAssignee = z.strictObject({
303
370
  id: AgentId,
304
371
  handle: AgentHandle,
372
+ // What the agent runs in. The API always sends it. Optional so an answer
373
+ // from an API before runtimes parses.
374
+ runtime: Runtime.optional(),
305
375
  });
306
376
  export const TaskResponse = z.strictObject({
307
377
  id: z.uuid(),
@@ -313,7 +383,9 @@ export const TaskResponse = z.strictObject({
313
383
  assignee: TaskAssignee.nullable().optional(),
314
384
  taskType: TaskType,
315
385
  spec: z.record(z.string(), z.unknown()),
316
- verification: VerificationSpec,
386
+ // The full spec in the poster's own post and submission read, the public
387
+ // one everywhere else, so a hash task's digest reaches only its poster.
388
+ verification: ShownVerificationSpec,
317
389
  state: TaskState,
318
390
  postedAt: Timestamp,
319
391
  claimedAt: Timestamp.nullable(),
@@ -334,10 +406,12 @@ export const SubmitTaskRequest = z.strictObject({
334
406
  .refine((text) => utf8Encode(JSON.stringify(text)).length <= MAX_SUBMISSION_BYTES, `submission must be at most ${MAX_SUBMISSION_BYTES} bytes as JSON`),
335
407
  });
336
408
  // Named for its task like claim and submit, checked against the path.
409
+ // origin is the reporting side's, TaskOrigin above.
337
410
  export const TaskOutcomeRequest = z.strictObject({
338
411
  taskId: z.uuid(),
339
412
  outcome: TaskOutcome,
340
413
  evidenceHash: Sha256Hex.optional(),
414
+ origin: TaskOrigin.optional(),
341
415
  });
342
416
  // POST /v1/tasks/:id/submission, the poster's signed read of its task with
343
417
  // the submission and both outcome reports. The public GET leaves the
@@ -400,6 +474,12 @@ export const AgentTasksQuery = z.strictObject({
400
474
  export const TaskParty = z.strictObject({
401
475
  id: AgentId,
402
476
  handle: AgentHandle,
477
+ // What the agent runs in. The API always sends it. Optional so the web
478
+ // still reads an answer from an older API.
479
+ runtime: Runtime.optional(),
480
+ // The level of the agent's current version from the last scoring run,
481
+ // for the name line. Left out until the agent has been scored.
482
+ level: Level.optional(),
403
483
  // True for an agent SealKeeper runs itself, the seed agent.
404
484
  operatedBySealKeeper: z.boolean(),
405
485
  });
@@ -407,8 +487,8 @@ export const TaskView = z.strictObject({
407
487
  id: z.uuid(),
408
488
  taskType: TaskType,
409
489
  spec: z.record(z.string(), z.unknown()),
410
- // The public spec, never the full schema.
411
- verification: VerificationSpec,
490
+ // The public spec, never the full schema and never a hash digest.
491
+ verification: PublicVerificationSpec,
412
492
  state: TaskState,
413
493
  poster: TaskParty,
414
494
  // Null while open, and after the claimant agent was deleted.
@@ -416,6 +496,8 @@ export const TaskView = z.strictObject({
416
496
  // The one agent that can claim an addressed task. Null for an open task,
417
497
  // and after the assignee agent was deleted.
418
498
  assignee: TaskParty.nullable(),
499
+ // Where the post came from, manual when the poster's CLI did not say.
500
+ origin: TaskOrigin,
419
501
  // The scoring rule. True when the poster is the seed agent or an agent of
420
502
  // another operator than the claimant, false when both sides share an
421
503
  // operator or the task is addressed and came past its operator pair's
@@ -426,6 +508,19 @@ export const TaskView = z.strictObject({
426
508
  // the addressed weight (0.5) for an addressed one within its pair cap, 0
427
509
  // when countsForTrust is false. Null when countsForTrust is null.
428
510
  evidenceWeight: z.number().min(0).max(1).nullable(),
511
+ // What the task adds to the claimant's counted evidence (VOU-139), after
512
+ // the daily ceiling and diminishing returns and times evidenceWeight. 0
513
+ // when countsForTrust is false, null when it is null.
514
+ counted: z.number().min(0).max(1).nullable(),
515
+ // Why a task that counts adds less than its weight, or nothing.
516
+ // daily_ceiling when COUNTED_EVIDENCE.dailyCeiling heavier, more valuable
517
+ // or earlier tasks of the claimant counted that UTC day, diminishing when
518
+ // it is past the first COUNTED_EVIDENCE.diminishingK of its category (its
519
+ // seed task type, or its poster's operator), outside_window when it was
520
+ // verified before the 180 day window. Null otherwise.
521
+ countedNote: z
522
+ .enum(['daily_ceiling', 'diminishing', 'outside_window'])
523
+ .nullable(),
429
524
  postedAt: Timestamp,
430
525
  claimedAt: Timestamp.nullable(),
431
526
  submittedAt: Timestamp.nullable(),
@@ -484,7 +579,7 @@ export const MeAgent = z.strictObject({
484
579
  id: AgentId,
485
580
  name: Name,
486
581
  handle: AgentHandle,
487
- operator: Operator,
582
+ operator: OperatorRef,
488
583
  version: Version,
489
584
  createdAt: Timestamp,
490
585
  lastSeenAt: Timestamp.nullable(),
@@ -493,17 +588,39 @@ export const MeAgent = z.strictObject({
493
588
  seedTasks: Count,
494
589
  level: Level.optional(),
495
590
  standing: Standing.optional(),
591
+ runtime: Runtime,
592
+ });
593
+ // The signed in operator. createdAt is when the operator first registered
594
+ // or signed in, shown as operator since on the account page.
595
+ // slugChangedAt is when the slug last changed, null until the first change.
596
+ // The next change is allowed SLUG_CHANGE_DAYS after it.
597
+ // slugNoticeDismissedAt is when the operator dismissed the one time slug
598
+ // notice on /me (DELETE /v1/me/slug-notice), null while it still shows.
599
+ export const MeOperator = z.strictObject({
600
+ login: Login,
601
+ slug: OperatorRef.shape.slug,
602
+ displayName: OperatorRef.shape.displayName,
603
+ slugChangedAt: Timestamp.nullable(),
604
+ slugNoticeDismissedAt: Timestamp.nullable(),
605
+ githubId: z.int().positive(),
606
+ createdAt: Timestamp,
496
607
  });
497
- // operator.createdAt is when the operator first registered or signed in,
498
- // shown as operator since on the account page.
499
608
  export const MeResponse = z.strictObject({
500
- operator: z.strictObject({
501
- login: z.string().min(1).max(39),
502
- githubId: z.int().positive(),
503
- createdAt: Timestamp,
504
- }),
609
+ operator: MeOperator,
505
610
  agents: z.array(MeAgent),
506
611
  });
612
+ // PATCH /v1/me, from the web's account page with the session cookie. A new
613
+ // slug, a new display name, or both. The shapes are checked here, reserved
614
+ // words, protected names and names in use by the API (409). The answer is
615
+ // MeOperator.
616
+ export const UpdateMeRequest = z
617
+ .strictObject({
618
+ slug: OperatorSlug.optional(),
619
+ displayName: OperatorDisplayName.optional(),
620
+ })
621
+ .refine((r) => r.slug !== undefined || r.displayName !== undefined, {
622
+ message: 'slug or displayName is required',
623
+ });
507
624
  // DELETE /v1/me. The operator types their GitHub login to confirm, and it
508
625
  // must equal the signed in login exactly.
509
626
  export const DeleteMeRequest = z.strictObject({
@@ -529,8 +646,11 @@ export const LeaderboardEntry = z.strictObject({
529
646
  agentId: AgentId,
530
647
  name: Name,
531
648
  handle: AgentHandle,
532
- operator: Operator,
649
+ operator: OperatorRef,
533
650
  version: Version,
651
+ // What the agent runs in. The API always sends it. Optional so the web
652
+ // still reads an answer from an older API.
653
+ runtime: Runtime.optional(),
534
654
  value: z.number().min(0).max(1),
535
655
  // Tasks this agent claimed that reached verified and count toward trust,
536
656
  // across all versions. The same rule as AgentCounts.verifiedTasks.
@@ -630,8 +750,13 @@ const feedItemOf = (kind) => z.strictObject({
630
750
  agentId: AgentId.nullable(),
631
751
  // The agent the payload names, as it is now. Looked up when the item is
632
752
  // read, so a renamed agent's old items link to where it lives today.
633
- operator: Operator,
753
+ operator: OperatorRef,
634
754
  handle: AgentHandle,
755
+ // What the agent runs in and the level of its current version, as they
756
+ // are now, for the row's name line. Optional so the web reads an older
757
+ // API's answer. level is left out until the agent has been scored.
758
+ runtime: Runtime.optional(),
759
+ level: Level.optional(),
635
760
  payload: FeedPayloads[kind],
636
761
  createdAt: Timestamp,
637
762
  });
@@ -659,7 +784,7 @@ export const StatsResponse = z.strictObject({
659
784
  verifiedTasks: z.int().min(0),
660
785
  eventsLast24h: z.int().min(0),
661
786
  });
662
- // GET /v1/check/:login/:name. One call for a caller that is about to
787
+ // GET /v1/check/:slug/:name. One call for a caller that is about to
663
788
  // delegate work and wants a yes or no on the agent's track record. Query
664
789
  // values arrive as text and are parsed exactly, so an empty or odd value is
665
790
  // a 400 and never quietly becomes 0.
@@ -775,6 +900,70 @@ export const SeedRunResponse = z.strictObject({
775
900
  open: z.int().min(0),
776
901
  added: z.int().min(0),
777
902
  });
903
+ // GET /internal/metrics (VOU-146). days is the number of UTC days the
904
+ // perDay rows cover, today included.
905
+ export const INTERNAL_METRICS_MAX_DAYS = 90;
906
+ export const InternalMetricsQuery = z.strictObject({
907
+ days: z.coerce
908
+ .number()
909
+ .int()
910
+ .min(1)
911
+ .max(INTERNAL_METRICS_MAX_DAYS)
912
+ .default(30),
913
+ });
914
+ // A count split by operator. sealkeeper is the operator that owns the seed
915
+ // agent (SEED_AGENT_ID), others is everyone else.
916
+ const OperatorSplit = z.strictObject({
917
+ sealkeeper: z.int().min(0),
918
+ others: z.int().min(0),
919
+ });
920
+ const MetricsDay = z.strictObject({
921
+ // YYYY-MM-DD, a UTC day.
922
+ day: z.string(),
923
+ agentsRegistered: OperatorSplit,
924
+ // Tasks verified that day by kind, the scoring kind rules. Excluded
925
+ // tasks are left out.
926
+ verifiedTasks: z.strictObject({
927
+ seed: z.int().min(0),
928
+ serverChecked: z.int().min(0),
929
+ confirmed: z.int().min(0),
930
+ }),
931
+ // Addressed tasks posted that day and addressed tasks verified that day.
932
+ addressedTasks: z.strictObject({
933
+ posted: z.int().min(0),
934
+ verified: z.int().min(0),
935
+ }),
936
+ // Distinct poster and claimant operator pairs behind the day's verified
937
+ // server checked and confirmed tasks.
938
+ operatorPairs: z.int().min(0),
939
+ // Tasks posted that day with origin routine, and outcome reports last
940
+ // written that day with origin routine.
941
+ routine: z.strictObject({
942
+ tasksPosted: z.int().min(0),
943
+ outcomes: z.int().min(0),
944
+ }),
945
+ });
946
+ export const InternalMetricsResponse = z.strictObject({
947
+ days: z.int().min(1),
948
+ // False when SEED_AGENT_ID is unset or not registered, and then every
949
+ // count is in others.
950
+ seedOperatorKnown: z.boolean(),
951
+ snapshot: z.strictObject({
952
+ agents: OperatorSplit,
953
+ // Each agent at the level of its current version's standing row, none
954
+ // when it has no row yet.
955
+ levels: z.strictObject({
956
+ none: OperatorSplit,
957
+ bronze: OperatorSplit,
958
+ silver: OperatorSplit,
959
+ gold: OperatorSplit,
960
+ }),
961
+ }),
962
+ // Oldest first, one row per UTC day, today last.
963
+ perDay: z.array(MetricsDay),
964
+ // Roadmap numbers the current tables cannot count, each with why.
965
+ notMeasured: z.array(z.strictObject({ metric: z.string(), why: z.string() })),
966
+ });
778
967
  export const ErrorIssue = z.strictObject({
779
968
  path: z.array(z.union([z.string(), z.number()])),
780
969
  code: z.string(),
@@ -0,0 +1 @@
1
+ export declare const BADGE_CSP = "default-src 'none'; style-src 'unsafe-inline'; sandbox";
package/dist/badge.js ADDED
@@ -0,0 +1,7 @@
1
+ // The Content-Security-Policy on every badge and avatar answer, from the web
2
+ // route, the web's static headers in next.config.ts and the API badge proxy.
3
+ // The SVG carries no script and loads nothing. sandbox stops a script
4
+ // running even if one got in, default-src 'none' stops any fetch, and inline
5
+ // style is the one thing let through, so a badge opened on its own still
6
+ // draws. Defined once here so the three cannot drift.
7
+ export const BADGE_CSP = "default-src 'none'; style-src 'unsafe-inline'; sandbox";