@sealkeeper/schema 0.4.4 → 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.
- package/dist/agent-name.d.ts +1 -1
- package/dist/agent-name.js +4 -4
- package/dist/api.d.ts +604 -27
- package/dist/api.js +207 -43
- package/dist/badge.d.ts +1 -0
- package/dist/badge.js +7 -0
- package/dist/db/agents.d.ts +70 -0
- package/dist/db/agents.js +18 -0
- package/dist/db/client.d.ts +341 -0
- package/dist/db/client.js +2 -0
- package/dist/db/events.js +7 -0
- package/dist/db/index.d.ts +16 -0
- package/dist/db/index.js +4 -0
- package/dist/db/migrate.js +5 -1
- package/dist/db/migrator.d.ts +3 -1
- package/dist/db/migrator.js +8 -2
- package/dist/db/operator-identities.d.ts +211 -0
- package/dist/db/operator-identities.js +49 -0
- package/dist/db/operator-level-grants.d.ts +109 -0
- package/dist/db/operator-level-grants.js +30 -0
- package/dist/db/operator-slugs.d.ts +92 -0
- package/dist/db/operator-slugs.js +25 -0
- package/dist/db/operators.d.ts +119 -0
- package/dist/db/operators.js +30 -2
- package/dist/db/standing.d.ts +1 -0
- package/dist/db/standing.js +2 -0
- package/dist/db/task-claim-failures.d.ts +143 -0
- package/dist/db/task-claim-failures.js +37 -0
- package/dist/db/tasks.d.ts +60 -0
- package/dist/db/tasks.js +13 -1
- package/dist/envelope.d.ts +3 -0
- package/dist/envelope.js +16 -11
- package/dist/goal.d.ts +62 -1
- package/dist/goal.js +70 -11
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/moderation.d.ts +28 -0
- package/dist/moderation.js +450 -0
- package/dist/operator-domains.d.ts +104 -0
- package/dist/operator-domains.js +85 -0
- package/dist/policy.d.ts +4 -0
- package/dist/policy.js +10 -0
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.js +61 -0
- package/dist/seal-conformance.js +2 -0
- package/dist/standing.d.ts +27 -8
- package/dist/standing.js +59 -22
- package/dist/tasks.d.ts +23 -1
- package/dist/tasks.js +29 -5
- 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
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
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.
|
|
124
|
-
// one signed request, never
|
|
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 ||
|
|
132
|
-
|
|
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
|
-
//
|
|
156
|
-
//
|
|
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
|
-
|
|
213
|
+
slug: HandleSlug,
|
|
159
214
|
name: AgentName,
|
|
160
215
|
});
|
|
161
|
-
// The 404 for a handle
|
|
162
|
-
// RENAME_REDIRECT_DAYS
|
|
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
|
|
216
|
-
operator:
|
|
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:
|
|
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,17 +332,17 @@ 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
|
|
277
|
-
// a
|
|
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 [
|
|
341
|
+
const [slug, name, ...rest] = ref.split('/');
|
|
283
342
|
return (rest.length === 0 &&
|
|
284
|
-
|
|
343
|
+
HandleSlug.safeParse(slug).success &&
|
|
285
344
|
AgentName.safeParse(name).success);
|
|
286
|
-
}, 'Use an agent id or a handle
|
|
345
|
+
}, 'Use an agent id or a handle slug/name'),
|
|
287
346
|
]);
|
|
288
347
|
// Where a task post or an outcome report came from, declared by the
|
|
289
348
|
// poster's or reporter's CLI inside the signed payload. manual is a person
|
|
@@ -310,6 +369,9 @@ export const PostTaskRequest = z.strictObject({
|
|
|
310
369
|
export const TaskAssignee = z.strictObject({
|
|
311
370
|
id: AgentId,
|
|
312
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(),
|
|
313
375
|
});
|
|
314
376
|
export const TaskResponse = z.strictObject({
|
|
315
377
|
id: z.uuid(),
|
|
@@ -321,7 +383,9 @@ export const TaskResponse = z.strictObject({
|
|
|
321
383
|
assignee: TaskAssignee.nullable().optional(),
|
|
322
384
|
taskType: TaskType,
|
|
323
385
|
spec: z.record(z.string(), z.unknown()),
|
|
324
|
-
|
|
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,
|
|
325
389
|
state: TaskState,
|
|
326
390
|
postedAt: Timestamp,
|
|
327
391
|
claimedAt: Timestamp.nullable(),
|
|
@@ -410,6 +474,12 @@ export const AgentTasksQuery = z.strictObject({
|
|
|
410
474
|
export const TaskParty = z.strictObject({
|
|
411
475
|
id: AgentId,
|
|
412
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(),
|
|
413
483
|
// True for an agent SealKeeper runs itself, the seed agent.
|
|
414
484
|
operatedBySealKeeper: z.boolean(),
|
|
415
485
|
});
|
|
@@ -417,8 +487,8 @@ export const TaskView = z.strictObject({
|
|
|
417
487
|
id: z.uuid(),
|
|
418
488
|
taskType: TaskType,
|
|
419
489
|
spec: z.record(z.string(), z.unknown()),
|
|
420
|
-
// The public spec, never the full schema.
|
|
421
|
-
verification:
|
|
490
|
+
// The public spec, never the full schema and never a hash digest.
|
|
491
|
+
verification: PublicVerificationSpec,
|
|
422
492
|
state: TaskState,
|
|
423
493
|
poster: TaskParty,
|
|
424
494
|
// Null while open, and after the claimant agent was deleted.
|
|
@@ -509,7 +579,7 @@ export const MeAgent = z.strictObject({
|
|
|
509
579
|
id: AgentId,
|
|
510
580
|
name: Name,
|
|
511
581
|
handle: AgentHandle,
|
|
512
|
-
operator:
|
|
582
|
+
operator: OperatorRef,
|
|
513
583
|
version: Version,
|
|
514
584
|
createdAt: Timestamp,
|
|
515
585
|
lastSeenAt: Timestamp.nullable(),
|
|
@@ -518,17 +588,39 @@ export const MeAgent = z.strictObject({
|
|
|
518
588
|
seedTasks: Count,
|
|
519
589
|
level: Level.optional(),
|
|
520
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,
|
|
521
607
|
});
|
|
522
|
-
// operator.createdAt is when the operator first registered or signed in,
|
|
523
|
-
// shown as operator since on the account page.
|
|
524
608
|
export const MeResponse = z.strictObject({
|
|
525
|
-
operator:
|
|
526
|
-
login: z.string().min(1).max(39),
|
|
527
|
-
githubId: z.int().positive(),
|
|
528
|
-
createdAt: Timestamp,
|
|
529
|
-
}),
|
|
609
|
+
operator: MeOperator,
|
|
530
610
|
agents: z.array(MeAgent),
|
|
531
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
|
+
});
|
|
532
624
|
// DELETE /v1/me. The operator types their GitHub login to confirm, and it
|
|
533
625
|
// must equal the signed in login exactly.
|
|
534
626
|
export const DeleteMeRequest = z.strictObject({
|
|
@@ -554,8 +646,11 @@ export const LeaderboardEntry = z.strictObject({
|
|
|
554
646
|
agentId: AgentId,
|
|
555
647
|
name: Name,
|
|
556
648
|
handle: AgentHandle,
|
|
557
|
-
operator:
|
|
649
|
+
operator: OperatorRef,
|
|
558
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(),
|
|
559
654
|
value: z.number().min(0).max(1),
|
|
560
655
|
// Tasks this agent claimed that reached verified and count toward trust,
|
|
561
656
|
// across all versions. The same rule as AgentCounts.verifiedTasks.
|
|
@@ -655,8 +750,13 @@ const feedItemOf = (kind) => z.strictObject({
|
|
|
655
750
|
agentId: AgentId.nullable(),
|
|
656
751
|
// The agent the payload names, as it is now. Looked up when the item is
|
|
657
752
|
// read, so a renamed agent's old items link to where it lives today.
|
|
658
|
-
operator:
|
|
753
|
+
operator: OperatorRef,
|
|
659
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(),
|
|
660
760
|
payload: FeedPayloads[kind],
|
|
661
761
|
createdAt: Timestamp,
|
|
662
762
|
});
|
|
@@ -684,7 +784,7 @@ export const StatsResponse = z.strictObject({
|
|
|
684
784
|
verifiedTasks: z.int().min(0),
|
|
685
785
|
eventsLast24h: z.int().min(0),
|
|
686
786
|
});
|
|
687
|
-
// GET /v1/check/:
|
|
787
|
+
// GET /v1/check/:slug/:name. One call for a caller that is about to
|
|
688
788
|
// delegate work and wants a yes or no on the agent's track record. Query
|
|
689
789
|
// values arrive as text and are parsed exactly, so an empty or odd value is
|
|
690
790
|
// a 400 and never quietly becomes 0.
|
|
@@ -800,6 +900,70 @@ export const SeedRunResponse = z.strictObject({
|
|
|
800
900
|
open: z.int().min(0),
|
|
801
901
|
added: z.int().min(0),
|
|
802
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
|
+
});
|
|
803
967
|
export const ErrorIssue = z.strictObject({
|
|
804
968
|
path: z.array(z.union([z.string(), z.number()])),
|
|
805
969
|
code: z.string(),
|
package/dist/badge.d.ts
ADDED
|
@@ -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";
|
package/dist/db/agents.d.ts
CHANGED
|
@@ -138,6 +138,42 @@ export declare const agents: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
138
138
|
identity: undefined;
|
|
139
139
|
generated: undefined;
|
|
140
140
|
}, {}, {}>;
|
|
141
|
+
runtime: import("drizzle-orm/pg-core").PgColumn<{
|
|
142
|
+
name: "runtime";
|
|
143
|
+
tableName: "agents";
|
|
144
|
+
dataType: "string";
|
|
145
|
+
columnType: "PgText";
|
|
146
|
+
data: "claude-code" | "codex" | "cursor" | "gemini-cli" | "mastra" | "openclaw" | "other" | "unknown";
|
|
147
|
+
driverParam: string;
|
|
148
|
+
notNull: true;
|
|
149
|
+
hasDefault: true;
|
|
150
|
+
isPrimaryKey: false;
|
|
151
|
+
isAutoincrement: false;
|
|
152
|
+
hasRuntimeDefault: false;
|
|
153
|
+
enumValues: [string, ...string[]];
|
|
154
|
+
baseColumn: never;
|
|
155
|
+
identity: undefined;
|
|
156
|
+
generated: undefined;
|
|
157
|
+
}, {}, {
|
|
158
|
+
$type: "claude-code" | "codex" | "cursor" | "gemini-cli" | "mastra" | "openclaw" | "other" | "unknown";
|
|
159
|
+
}>;
|
|
160
|
+
runtimeIssuedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
161
|
+
name: "runtime_issued_at";
|
|
162
|
+
tableName: "agents";
|
|
163
|
+
dataType: "date";
|
|
164
|
+
columnType: "PgTimestamp";
|
|
165
|
+
data: Date;
|
|
166
|
+
driverParam: string;
|
|
167
|
+
notNull: false;
|
|
168
|
+
hasDefault: false;
|
|
169
|
+
isPrimaryKey: false;
|
|
170
|
+
isAutoincrement: false;
|
|
171
|
+
hasRuntimeDefault: false;
|
|
172
|
+
enumValues: undefined;
|
|
173
|
+
baseColumn: never;
|
|
174
|
+
identity: undefined;
|
|
175
|
+
generated: undefined;
|
|
176
|
+
}, {}, {}>;
|
|
141
177
|
dirtySince: import("drizzle-orm/pg-core").PgColumn<{
|
|
142
178
|
name: "dirty_since";
|
|
143
179
|
tableName: "agents";
|
|
@@ -189,6 +225,40 @@ export declare const agents: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
189
225
|
identity: undefined;
|
|
190
226
|
generated: undefined;
|
|
191
227
|
}, {}, {}>;
|
|
228
|
+
termsVersion: import("drizzle-orm/pg-core").PgColumn<{
|
|
229
|
+
name: "terms_version";
|
|
230
|
+
tableName: "agents";
|
|
231
|
+
dataType: "string";
|
|
232
|
+
columnType: "PgText";
|
|
233
|
+
data: string;
|
|
234
|
+
driverParam: string;
|
|
235
|
+
notNull: false;
|
|
236
|
+
hasDefault: false;
|
|
237
|
+
isPrimaryKey: false;
|
|
238
|
+
isAutoincrement: false;
|
|
239
|
+
hasRuntimeDefault: false;
|
|
240
|
+
enumValues: [string, ...string[]];
|
|
241
|
+
baseColumn: never;
|
|
242
|
+
identity: undefined;
|
|
243
|
+
generated: undefined;
|
|
244
|
+
}, {}, {}>;
|
|
245
|
+
privacyVersion: import("drizzle-orm/pg-core").PgColumn<{
|
|
246
|
+
name: "privacy_version";
|
|
247
|
+
tableName: "agents";
|
|
248
|
+
dataType: "string";
|
|
249
|
+
columnType: "PgText";
|
|
250
|
+
data: string;
|
|
251
|
+
driverParam: string;
|
|
252
|
+
notNull: false;
|
|
253
|
+
hasDefault: false;
|
|
254
|
+
isPrimaryKey: false;
|
|
255
|
+
isAutoincrement: false;
|
|
256
|
+
hasRuntimeDefault: false;
|
|
257
|
+
enumValues: [string, ...string[]];
|
|
258
|
+
baseColumn: never;
|
|
259
|
+
identity: undefined;
|
|
260
|
+
generated: undefined;
|
|
261
|
+
}, {}, {}>;
|
|
192
262
|
};
|
|
193
263
|
dialect: 'pg';
|
|
194
264
|
}>;
|
package/dist/db/agents.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm';
|
|
2
2
|
import { bigint, check, index, jsonb, pgTable, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
|
|
3
|
+
import { RUNTIMES } from '../runtime.js';
|
|
3
4
|
import { operators } from './operators.js';
|
|
4
5
|
import { timestamps } from './timestamps.js';
|
|
5
6
|
// How a Drizzle table is declared.
|
|
@@ -31,6 +32,15 @@ export const agents = pgTable('agents', {
|
|
|
31
32
|
// sent again cannot move the version back.
|
|
32
33
|
versionIssuedAt: timestamp('version_issued_at', { withTimezone: true }),
|
|
33
34
|
card: jsonb('card'),
|
|
35
|
+
// What the agent runs in, one of RUNTIMES. Set by the signed
|
|
36
|
+
// registration payload and changed by the signed PATCH, unknown until
|
|
37
|
+
// then. Migration 0024 backfilled unknown for every agent, since events
|
|
38
|
+
// do not say which adapter sent them.
|
|
39
|
+
runtime: text('runtime').$type().notNull().default('unknown'),
|
|
40
|
+
// The signed issuedAt of the last runtime change, null until the first.
|
|
41
|
+
// Same rule as versionIssuedAt, so an old envelope sent again cannot
|
|
42
|
+
// move the runtime back.
|
|
43
|
+
runtimeIssuedAt: timestamp('runtime_issued_at', { withTimezone: true }),
|
|
34
44
|
// When the agent was first marked dirty since the scoring run last
|
|
35
45
|
// cleared it. Every write that can move the agent's standing marks it,
|
|
36
46
|
// in the same transaction as that write, but a mark sets this only when
|
|
@@ -47,9 +57,17 @@ export const agents = pgTable('agents', {
|
|
|
47
57
|
// standing, a dormancy rung or evidence leaving the window. Written by
|
|
48
58
|
// the scoring run only, null when nothing is due.
|
|
49
59
|
recomputeAt: timestamp('recompute_at', { withTimezone: true }),
|
|
60
|
+
// The POLICY_VERSIONS the operator accepted by registering this agent,
|
|
61
|
+
// stamped by the API at registration, never sent by the client
|
|
62
|
+
// (VOU-121). Null for agents registered before.
|
|
63
|
+
termsVersion: text('terms_version'),
|
|
64
|
+
privacyVersion: text('privacy_version'),
|
|
50
65
|
...timestamps,
|
|
51
66
|
}, (t) => [
|
|
52
67
|
check('agents_id_length', sql `length(${t.id}) = 43`),
|
|
68
|
+
// Built from RUNTIMES, so a runtime added there shows up as a changed
|
|
69
|
+
// check in the next drizzle-kit generate.
|
|
70
|
+
check('agents_runtime', sql `${t.runtime} in (${sql.raw(RUNTIMES.map((r) => `'${r}'`).join(', '))})`),
|
|
53
71
|
// The handle login/name points at one agent. Names are lowercase, so a
|
|
54
72
|
// plain unique index is enough.
|
|
55
73
|
uniqueIndex('agents_operator_name_unique').on(t.operatorId, t.name),
|