@sealkeeper/schema 0.4.9 → 0.5.1
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.js +5 -1
- package/dist/api.d.ts +190 -6
- package/dist/api.js +168 -49
- package/dist/challenge-next.d.ts +130 -0
- package/dist/challenge-next.js +52 -0
- package/dist/conformance.d.ts +0 -1
- package/dist/conformance.js +3 -4
- package/dist/core.d.ts +163 -0
- package/dist/core.js +161 -0
- package/dist/credential.d.ts +1 -0
- package/dist/credential.js +8 -2
- package/dist/duel-next.d.ts +232 -0
- package/dist/duel-next.js +114 -0
- package/dist/envelope.d.ts +41 -1
- package/dist/envelope.js +71 -4
- package/dist/game.d.ts +2 -0
- package/dist/game.js +31 -12
- package/dist/handshake-conformance.d.ts +1 -0
- package/dist/handshake-conformance.js +41 -0
- package/dist/handshake.d.ts +15 -2
- package/dist/handshake.js +63 -14
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/policy.d.ts +1 -1
- package/dist/policy.js +1 -1
- package/dist/routine.d.ts +186 -0
- package/dist/routine.js +227 -0
- package/dist/seal-conformance.d.ts +1 -0
- package/dist/seal-conformance.js +23 -1
- package/dist/seal-verify.d.ts +1 -1
- package/dist/seal-verify.js +7 -1
- package/dist/standing.js +7 -4
- package/dist/status.d.ts +382 -0
- package/dist/status.js +98 -0
- package/dist/task-templates.d.ts +3 -22
- package/dist/task-templates.js +102 -469
- package/package.json +2 -2
- package/dist/template-conformance.d.ts +0 -10
- package/dist/template-conformance.js +0 -298
package/dist/api.js
CHANGED
|
@@ -9,12 +9,14 @@ import { Jws } from './envelope.js';
|
|
|
9
9
|
import { StoredVersion, Version } from './events.js';
|
|
10
10
|
import { AgentFingerprint, Fingerprint } from './fingerprint.js';
|
|
11
11
|
import { CHALLENGE_TASKS, ChallengeState, DuelOrigin, DuelResult, DuelSeekState, DuelState, GAME, GAME_CAP_MAX, GameBadgeKind, GameCap, IsoWeek, KeeperRank, } from './game.js';
|
|
12
|
-
import { HANDSHAKE_MAX_CHARS, HandshakeAgainst, HandshakeNonce, HandshakeRefusal, HandshakeResult, } from './handshake.js';
|
|
12
|
+
import { HANDSHAKE_MAX_CHARS, HandshakeAgainst, HandshakeAudience, HandshakeNonce, HandshakeProof, HandshakeRefusal, HandshakeResult, } from './handshake.js';
|
|
13
13
|
import { boundedJsonObject } from './json-shape.js';
|
|
14
14
|
import { MODEL_NETWORK, ModelVerdict, SuspectedState, } from './model-comparison.js';
|
|
15
|
+
import { ModelName } from './model-name.js';
|
|
15
16
|
import { OPERATOR_DISPLAY_NAME_MAX, OPERATOR_SLUG_MAX, OperatorDisplayName, OperatorSlug, } from './moderation.js';
|
|
16
17
|
import { Runtime } from './runtime.js';
|
|
17
18
|
import { DAY_MS, Level, Standing, TRUST_SCORE } from './standing.js';
|
|
19
|
+
import { TASK_TEMPLATE_IDS, TEMPLATE_MAX_INPUT_CHARS, } from './task-templates.js';
|
|
18
20
|
import { CheckMethod, Disclosure, PublicVerificationSpec, Sha256Hex, ShownVerificationSpec, STORED_TASK_CATEGORIES, StoredTaskCategory, TASK_CATEGORIES, TASK_DIFFICULTIES, TaskCategory, TaskDifficulty, TaskInputRef, TaskOutcome, TaskOutputShape, TaskSize, TaskState, VerificationSpec, } from './tasks.js';
|
|
19
21
|
export const MAX_EVENTS_PER_BATCH = 500;
|
|
20
22
|
// The window an event's occurred_at must fall in for the API to take it, at
|
|
@@ -24,6 +26,12 @@ export const MAX_EVENTS_PER_BATCH = 500;
|
|
|
24
26
|
// the two cannot drift.
|
|
25
27
|
export const EVENT_MAX_AGE_DAYS = 7;
|
|
26
28
|
export const EVENT_MAX_FUTURE_SKEW_SEC = 300;
|
|
29
|
+
// How long the API keeps an event, in days by its received_at (VOU-641).
|
|
30
|
+
// The scoring window (180 days) plus a margin, so every read of the window
|
|
31
|
+
// finds its events. A sweep on the scoring schedule deletes older ones,
|
|
32
|
+
// except each agent's newest usage event, which names its model
|
|
33
|
+
// (apps/api/src/retention.ts). The privacy page reads it.
|
|
34
|
+
export const EVENT_KEEP_DAYS = 200;
|
|
27
35
|
export const MAX_ENVELOPE_CHARS = 4096;
|
|
28
36
|
export const MAX_TASK_SPEC_BYTES = 16384;
|
|
29
37
|
// Depth and key caps on a spec, checked before its bytes (VOU-214). The top
|
|
@@ -67,7 +75,9 @@ export const SignedTaskRequest = z.strictObject({
|
|
|
67
75
|
// and its agent is unknown. gameEnabled is the operator's game switch at
|
|
68
76
|
// init (VOU-469, D-GAME-2), optional too, and absent means off. Both are
|
|
69
77
|
// read on a new agent only. Registering a key that is already registered
|
|
70
|
-
// changes nothing, runtime and game switch included.
|
|
78
|
+
// changes nothing, runtime and game switch included. issuedAt (VOU-637)
|
|
79
|
+
// bounds how long a captured envelope could be sent again, checked by the
|
|
80
|
+
// API when present. Optional, since CLI 0.5.0 and older send none.
|
|
71
81
|
export const RegisterAgentRequest = z.strictObject({
|
|
72
82
|
publicKey: AgentId,
|
|
73
83
|
githubToken: z.string().min(1).max(256),
|
|
@@ -75,6 +85,7 @@ export const RegisterAgentRequest = z.strictObject({
|
|
|
75
85
|
version: Version,
|
|
76
86
|
runtime: Runtime.optional(),
|
|
77
87
|
gameEnabled: z.boolean().optional(),
|
|
88
|
+
issuedAt: Timestamp.optional(),
|
|
78
89
|
});
|
|
79
90
|
// A name as the API sends it. Every stored name is an AgentName, this stays
|
|
80
91
|
// loose so a client never refuses an answer over a name.
|
|
@@ -200,8 +211,12 @@ export const RENAME_REDIRECT_DAYS = 90;
|
|
|
200
211
|
export const SLUG_REDIRECT_DAYS = 90;
|
|
201
212
|
// An operator can change its slug once in this many days.
|
|
202
213
|
export const SLUG_CHANGE_DAYS = 30;
|
|
203
|
-
//
|
|
204
|
-
|
|
214
|
+
// Every signed request that carries an issuedAt is accepted for this long
|
|
215
|
+
// after it and EVENT_MAX_FUTURE_SKEW_SEC ahead of it, else 400
|
|
216
|
+
// issued_at_out_of_window. It bounds how long a captured envelope could be
|
|
217
|
+
// sent again. One window for every route, checked by checkIssuedAt in the
|
|
218
|
+
// API's envelope.ts.
|
|
219
|
+
export const SIGNED_REQUEST_MAX_AGE_SEC = 300;
|
|
205
220
|
// PATCH /v1/agents/:id, signed by the agent's own key. issuedAt is signed
|
|
206
221
|
// with the name, so an old envelope sent again changes nothing.
|
|
207
222
|
export const RenameAgentRequest = z.strictObject({
|
|
@@ -236,9 +251,6 @@ export const UpdateAgentRequest = z
|
|
|
236
251
|
export const SignedRenameRequest = z.strictObject({
|
|
237
252
|
envelope: Jws.max(MAX_ENVELOPE_CHARS),
|
|
238
253
|
});
|
|
239
|
-
// A signed delete is accepted for this long after its issuedAt, the same
|
|
240
|
-
// window as rename.
|
|
241
|
-
export const DELETE_AGENT_MAX_AGE_SEC = RENAME_MAX_AGE_SEC;
|
|
242
254
|
// DELETE /v1/agents/:id from the CLI, signed by the agent's own key.
|
|
243
255
|
// issuedAt bounds how long a captured envelope could be sent again.
|
|
244
256
|
export const DeleteAgentRequest = z.strictObject({
|
|
@@ -264,9 +276,6 @@ const GAME_CHANGE_REQUIRED = { message: 'enabled or cap is required' };
|
|
|
264
276
|
export const GameSettingsRequest = z
|
|
265
277
|
.strictObject(gameChange)
|
|
266
278
|
.refine(someGameChange, GAME_CHANGE_REQUIRED);
|
|
267
|
-
// A signed settings change or status read is accepted for this long after
|
|
268
|
-
// its issuedAt, the window of rename.
|
|
269
|
-
export const GAME_REQUEST_MAX_AGE_SEC = RENAME_MAX_AGE_SEC;
|
|
270
279
|
// PUT /v1/game/settings, signed by the agent's own key. issuedAt is signed
|
|
271
280
|
// with the change, so an old envelope sent again cannot undo a newer one.
|
|
272
281
|
export const SignedGameSettingsRequest = z
|
|
@@ -287,18 +296,46 @@ export const GameSettingsView = z.strictObject({
|
|
|
287
296
|
enabled: z.boolean(),
|
|
288
297
|
cap: GameCap,
|
|
289
298
|
});
|
|
290
|
-
// The answer of the status read and of both settings writes
|
|
291
|
-
//
|
|
292
|
-
// next 00:00:00 UTC, when it starts
|
|
299
|
+
// The answer of the status read and of both settings writes, and the game
|
|
300
|
+
// part of the status answer. usedToday is the game units the agent used in
|
|
301
|
+
// the current UTC day, and resetAt the next 00:00:00 UTC, when it starts
|
|
302
|
+
// again from 0. duelsStartedToday is the duels the agent started that day,
|
|
303
|
+
// created and received together, against the ceiling duelsPerDay,
|
|
304
|
+
// GAME.duelsPerDay (VOU-618). Both optional, as every field added to an
|
|
305
|
+
// existing answer, so a reader takes an answer from an API before them.
|
|
293
306
|
export const GameStatusResponse = z.strictObject({
|
|
294
307
|
enabled: z.boolean(),
|
|
295
308
|
cap: GameCap,
|
|
296
309
|
usedToday: z.int().min(0).max(GAME_CAP_MAX),
|
|
297
310
|
resetAt: Timestamp,
|
|
311
|
+
duelsStartedToday: z.int().min(0).max(GAME.duelsPerDay).optional(),
|
|
312
|
+
duelsPerDay: z.int().min(0).optional(),
|
|
313
|
+
});
|
|
314
|
+
// What turning the game off closed (VOU-618), counts of the rows the
|
|
315
|
+
// change moved in its own transaction. seeks the agent's open seeks
|
|
316
|
+
// cancelled, invitesSent the invites it sent that were withdrawn and
|
|
317
|
+
// invitesReceived the invites it received that were declined. The live
|
|
318
|
+
// ones are bounded by GAME.openOutgoingMax for seeks and sent invites
|
|
319
|
+
// together, and by each sender's GAME.openOutgoingMax for invites
|
|
320
|
+
// received. A seek or invite that lapsed and the sweep has not marked yet
|
|
321
|
+
// is closed and counted too, so the counts carry no max.
|
|
322
|
+
export const GameClosed = z.strictObject({
|
|
323
|
+
seeks: z.int().min(0),
|
|
324
|
+
invitesSent: z.int().min(0),
|
|
325
|
+
invitesReceived: z.int().min(0),
|
|
326
|
+
});
|
|
327
|
+
// The answer of both settings writes, the game status after the change,
|
|
328
|
+
// and closed only when the change turned the switch from on to off. A
|
|
329
|
+
// change that leaves the switch as it was, a replay of the same signed
|
|
330
|
+
// change included, has no closed. Optional, so a reader takes an answer
|
|
331
|
+
// from an API before it.
|
|
332
|
+
export const GameSettingsResponse = z.strictObject({
|
|
333
|
+
...GameStatusResponse.shape,
|
|
334
|
+
closed: GameClosed.optional(),
|
|
298
335
|
});
|
|
299
336
|
// GET /v1/game/categories (VOU-474), the categories a duel or a weekly
|
|
300
337
|
// challenge can be in, in the order of TASK_CATEGORIES. A category is
|
|
301
|
-
// duelable when
|
|
338
|
+
// duelable when one of the API's game templates is in it, whose tasks
|
|
302
339
|
// count as seed tasks (GAME_TEMPLATES in apps/api/src/game/tasks.ts).
|
|
303
340
|
// Wrapped in an object, so a later field can be added beside the list.
|
|
304
341
|
export const GameCategoriesResponse = z.strictObject({
|
|
@@ -306,6 +343,17 @@ export const GameCategoriesResponse = z.strictObject({
|
|
|
306
343
|
.array(z.strictObject({ category: TaskCategory }))
|
|
307
344
|
.max(TASK_CATEGORIES.length),
|
|
308
345
|
});
|
|
346
|
+
// One finished duel from one agent's side, the opponent as its handle and
|
|
347
|
+
// the result as this agent had it. The game summary lists the last few,
|
|
348
|
+
// and the status answer the last one (POST /v1/agents/:id/status).
|
|
349
|
+
export const RecentDuel = z.strictObject({
|
|
350
|
+
id: z.uuid(),
|
|
351
|
+
opponent: AgentHandle,
|
|
352
|
+
category: TaskCategory,
|
|
353
|
+
result: z.enum(['win', 'loss', 'draw']),
|
|
354
|
+
forfeit: z.boolean(),
|
|
355
|
+
decidedAt: Timestamp,
|
|
356
|
+
});
|
|
309
357
|
/*
|
|
310
358
|
* GET /v1/agents/:id/game and GET /v1/agents/:slug/:name/game (VOU-478,
|
|
311
359
|
* GAME-11), the public game summary of one agent, for the profile's Game
|
|
@@ -350,16 +398,7 @@ export const GameSummaryResponse = z.strictObject({
|
|
|
350
398
|
forfeitWins: z.int().min(0),
|
|
351
399
|
forfeitLosses: z.int().min(0),
|
|
352
400
|
}),
|
|
353
|
-
recentDuels: z
|
|
354
|
-
.array(z.strictObject({
|
|
355
|
-
id: z.uuid(),
|
|
356
|
-
opponent: AgentHandle,
|
|
357
|
-
category: TaskCategory,
|
|
358
|
-
result: z.enum(['win', 'loss', 'draw']),
|
|
359
|
-
forfeit: z.boolean(),
|
|
360
|
-
decidedAt: Timestamp,
|
|
361
|
-
}))
|
|
362
|
-
.max(GAME.recentDuels),
|
|
401
|
+
recentDuels: z.array(RecentDuel).max(GAME.recentDuels),
|
|
363
402
|
badges: z
|
|
364
403
|
.array(z.strictObject({ kind: GameBadgeKind, isoWeek: IsoWeek }))
|
|
365
404
|
.max(GAME.summaryBadges),
|
|
@@ -673,23 +712,49 @@ export const AdoptTaskRequest = z.strictObject({
|
|
|
673
712
|
expiresAt: Timestamp.optional(),
|
|
674
713
|
origin: z.enum(['template', 'routine']).optional(),
|
|
675
714
|
});
|
|
715
|
+
/*
|
|
716
|
+
* A template post (VOU-640), the third payload POST /v1/tasks takes. The
|
|
717
|
+
* agent names a template and, for one that takes it, the operator's input,
|
|
718
|
+
* and the API makes the spec, works out the digest or schema itself and
|
|
719
|
+
* posts it as the agent's own template task, so the solver never ships in
|
|
720
|
+
* the public CLI. The answer to the post carries the task the API made.
|
|
721
|
+
* input is the operator's text as cleanTemplateInput gives it, at most
|
|
722
|
+
* TEMPLATE_MAX_INPUT_CHARS, and the API runs the template's rules on it
|
|
723
|
+
* again. taskId, expiresAt and assignee work as in PostTaskRequest, origin
|
|
724
|
+
* as in AdoptTaskRequest, template when absent. The category, size and
|
|
725
|
+
* difficulty are the template's, so the payload has none of them. A CLI
|
|
726
|
+
* from before it posts the made task as a PostTaskRequest with its own
|
|
727
|
+
* digest, which the API still checks against its solver (refuseUnsolvable).
|
|
728
|
+
*/
|
|
729
|
+
export const TemplatePostRequest = z.strictObject({
|
|
730
|
+
taskId: z.uuid(),
|
|
731
|
+
template: z.enum(TASK_TEMPLATE_IDS),
|
|
732
|
+
input: z.string().min(1).max(TEMPLATE_MAX_INPUT_CHARS).optional(),
|
|
733
|
+
expiresAt: Timestamp.optional(),
|
|
734
|
+
assignee: AgentRef.optional(),
|
|
735
|
+
origin: z.enum(['template', 'routine']).optional(),
|
|
736
|
+
});
|
|
676
737
|
// A task post always has a task type, a spec and a verification, and an
|
|
677
|
-
// adoption
|
|
738
|
+
// adoption and a template post have none of them. A template post names
|
|
739
|
+
// its template.
|
|
678
740
|
const ADOPTION_ABSENT = ['taskType', 'spec', 'verification'];
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
741
|
+
const isObject = (value) => typeof value === 'object' && value !== null;
|
|
742
|
+
const isAdoption = (value) => isObject(value) && ADOPTION_ABSENT.every((key) => !Object.hasOwn(value, key));
|
|
743
|
+
const isTemplatePost = (value) => isAdoption(value) && Object.hasOwn(value, 'template');
|
|
682
744
|
/*
|
|
683
745
|
* The payload POST /v1/tasks parses. One with none of taskType, spec and
|
|
684
|
-
* verification is
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
746
|
+
* verification is a template post when it names a template and parses as
|
|
747
|
+
* TemplatePostRequest, else an adoption and parses as AdoptTaskRequest, and
|
|
748
|
+
* any other parses as PostTaskRequest, so each failure names its own fields
|
|
749
|
+
* rather than a union's. A transform that picks the schema, shown once. The
|
|
750
|
+
* issues of the schema it picked are copied onto this one, paths and all.
|
|
688
751
|
*/
|
|
689
752
|
export const PostTaskPayload = z.unknown().transform((value, ctx) => {
|
|
690
|
-
const parsed =
|
|
691
|
-
?
|
|
692
|
-
:
|
|
753
|
+
const parsed = isTemplatePost(value)
|
|
754
|
+
? TemplatePostRequest.safeParse(value)
|
|
755
|
+
: isAdoption(value)
|
|
756
|
+
? AdoptTaskRequest.safeParse(value)
|
|
757
|
+
: PostTaskRequest.safeParse(value);
|
|
693
758
|
if (parsed.success)
|
|
694
759
|
return parsed.data;
|
|
695
760
|
for (const issue of parsed.error.issues) {
|
|
@@ -775,6 +840,11 @@ export const TaskResponse = z.strictObject({
|
|
|
775
840
|
.optional(),
|
|
776
841
|
// Present only in responses to the poster or the claimant.
|
|
777
842
|
submission: z.string().optional(),
|
|
843
|
+
// The model the claimant said solved the task, sent with the submit
|
|
844
|
+
// (VOU-615). Only where submission is, and absent when the submit named
|
|
845
|
+
// none. Any text, so a reader never fails on a name an older or newer
|
|
846
|
+
// rule would refuse.
|
|
847
|
+
submissionModelName: z.string().optional(),
|
|
778
848
|
// True in the answer to POST /v1/tasks/:id/release, whose signing agent's
|
|
779
849
|
// claim on the task has ended (VOU-572). Absent everywhere else.
|
|
780
850
|
// Optional, so the CLI still parses every other answer and one from an
|
|
@@ -805,13 +875,21 @@ export const SubmitTaskRequest = z.strictObject({
|
|
|
805
875
|
.string()
|
|
806
876
|
.refine((text) => utf8Encode(JSON.stringify(text)).length <= MAX_SUBMISSION_BYTES, `submission must be at most ${MAX_SUBMISSION_BYTES} bytes as JSON`),
|
|
807
877
|
fingerprint: Fingerprint.optional(),
|
|
878
|
+
// The name of the model that solved the task, as the agent says
|
|
879
|
+
// (VOU-615), ModelName in ./model-name.ts. The API stores it once with
|
|
880
|
+
// the submission and nothing reads it as proof, so it earns nothing.
|
|
881
|
+
// Absent means not said, as from a CLI from before it.
|
|
882
|
+
modelName: ModelName.optional(),
|
|
808
883
|
});
|
|
809
884
|
// POST /v1/tasks/:id/release, the claimant giving its claim back
|
|
810
885
|
// (VOU-572). Named for its task like claim and submit, checked against the
|
|
811
886
|
// path. A captured envelope cannot end a later claim, since the release
|
|
812
|
-
// bars its agent from the task.
|
|
887
|
+
// bars its agent from the task. issuedAt (VOU-637) bounds how long a
|
|
888
|
+
// captured envelope could be sent again, checked by the API when present.
|
|
889
|
+
// Optional, since CLI 0.5.0 and older send none.
|
|
813
890
|
export const ReleaseTaskRequest = z.strictObject({
|
|
814
891
|
taskId: z.uuid(),
|
|
892
|
+
issuedAt: Timestamp.optional(),
|
|
815
893
|
});
|
|
816
894
|
// Named for its task like claim and submit, checked against the path.
|
|
817
895
|
// origin is the reporting side's, TaskOrigin above.
|
|
@@ -825,8 +903,7 @@ export const TaskOutcomeRequest = z.strictObject({
|
|
|
825
903
|
// POST /v1/tasks/:id/submission, the poster's signed read of its task with
|
|
826
904
|
// the submission and both outcome reports. The public GET leaves the
|
|
827
905
|
// submission out. issuedAt bounds how long a captured envelope could be sent
|
|
828
|
-
// again
|
|
829
|
-
export const TASK_READ_MAX_AGE_SEC = RENAME_MAX_AGE_SEC;
|
|
906
|
+
// again (SIGNED_REQUEST_MAX_AGE_SEC).
|
|
830
907
|
export const TaskSubmissionRequest = z.strictObject({
|
|
831
908
|
taskId: z.uuid(),
|
|
832
909
|
issuedAt: Timestamp,
|
|
@@ -883,7 +960,8 @@ export const ListTasksQuery = z.strictObject({
|
|
|
883
960
|
// less the tasks this agent is barred from after its failed submits. Which
|
|
884
961
|
// tasks those are is the agent's own business, so it is a signed read and
|
|
885
962
|
// the public GET stays as it is. issuedAt bounds how long a captured
|
|
886
|
-
// envelope could be sent again, the window of
|
|
963
|
+
// envelope could be sent again, the window of every signed request
|
|
964
|
+
// (SIGNED_REQUEST_MAX_AGE_SEC).
|
|
887
965
|
export const OpenTasksRequest = z.strictObject({
|
|
888
966
|
taskType: TaskType.optional(),
|
|
889
967
|
seed: z.boolean().optional(),
|
|
@@ -902,7 +980,7 @@ export const ListTasksResponse = z.strictObject({
|
|
|
902
980
|
* agent route is signed with the envelope of the other game routes, body
|
|
903
981
|
* SignedGameRequest { envelope }, and each payload carries issuedAt, which
|
|
904
982
|
* bounds how long a captured envelope could be sent again
|
|
905
|
-
* (
|
|
983
|
+
* (SIGNED_REQUEST_MAX_AGE_SEC). A write names the seek or duel it was signed
|
|
906
984
|
* for, checked against the path. The reads are POSTs, as every signed read
|
|
907
985
|
* is, since a GET has no body for the envelope.
|
|
908
986
|
*/
|
|
@@ -985,6 +1063,16 @@ export const ListDuelsResponse = z.strictObject({
|
|
|
985
1063
|
duels: z.array(DuelResponse),
|
|
986
1064
|
nextCursor: z.string().nullable(),
|
|
987
1065
|
});
|
|
1066
|
+
// GET /v1/agents/:id/duels and GET /v1/agents/:slug/:name/duels
|
|
1067
|
+
// (VOU-486), the public list of one agent's duels that started, active
|
|
1068
|
+
// and finished, newest first on (created_at, id), paged with the task
|
|
1069
|
+
// list's cursor. An invite, answered or not, is never in it.
|
|
1070
|
+
export const AgentDuelsQuery = z.strictObject({
|
|
1071
|
+
limit: Limit,
|
|
1072
|
+
cursor: TasksCursorParam.optional(),
|
|
1073
|
+
});
|
|
1074
|
+
// Each duel as GET /v1/duels/:id answers it, no side with a taskId.
|
|
1075
|
+
export const AgentDuelsResponse = ListDuelsResponse;
|
|
988
1076
|
// A seek as its agent sees it. duelId is the duel a match started.
|
|
989
1077
|
export const DuelSeekView = z.strictObject({
|
|
990
1078
|
id: z.uuid(),
|
|
@@ -1003,7 +1091,7 @@ export const SeekDuelResponse = z.strictObject({
|
|
|
1003
1091
|
/*
|
|
1004
1092
|
* Weekly challenges (VOU-479, GAME-12, D-GAME-11), under /v1/challenges.
|
|
1005
1093
|
* The agent routes are signed like the duel routes, body SignedGameRequest
|
|
1006
|
-
* { envelope }, each payload with issuedAt within
|
|
1094
|
+
* { envelope }, each payload with issuedAt within SIGNED_REQUEST_MAX_AGE_SEC.
|
|
1007
1095
|
*/
|
|
1008
1096
|
// POST /v1/challenges/current, the agent's signed read of the current
|
|
1009
1097
|
// week's challenge, and POST /v1/challenges/current/enter, its entry.
|
|
@@ -1024,7 +1112,8 @@ export const ChallengeTaskView = z.strictObject({
|
|
|
1024
1112
|
});
|
|
1025
1113
|
// The current week's challenge as its agent sees it. entered says whether
|
|
1026
1114
|
// the agent has an entry, and tasks are its own, empty without one. rank
|
|
1027
|
-
// is its live place, null until it has submitted
|
|
1115
|
+
// is its live place, null until it has submitted and while another entry
|
|
1116
|
+
// of its operator is ahead of it (VOU-644).
|
|
1028
1117
|
export const CurrentChallengeResponse = z.strictObject({
|
|
1029
1118
|
isoWeek: IsoWeek,
|
|
1030
1119
|
category: TaskCategory,
|
|
@@ -1048,8 +1137,9 @@ export const ChallengeBoardRow = z.strictObject({
|
|
|
1048
1137
|
serverMs: z.int().min(0),
|
|
1049
1138
|
});
|
|
1050
1139
|
// The week's board, live while it is open and its final ranks once it
|
|
1051
|
-
// closed.
|
|
1052
|
-
// at most
|
|
1140
|
+
// closed. One row per operator, its best entry (VOU-644). entrants counts
|
|
1141
|
+
// the places, the operators with a ranked entry, at most
|
|
1142
|
+
// GAME.challengeRankMax.
|
|
1053
1143
|
export const ChallengeBoardResponse = z.strictObject({
|
|
1054
1144
|
isoWeek: IsoWeek,
|
|
1055
1145
|
category: TaskCategory,
|
|
@@ -1061,7 +1151,8 @@ export const ChallengeBoardResponse = z.strictObject({
|
|
|
1061
1151
|
// One of the operator's agents with an entry in the week, its rank and its
|
|
1062
1152
|
// tasks as CurrentChallengeResponse shows them to the agent. rank is the
|
|
1063
1153
|
// live place while the week is open and the final one once it closed, null
|
|
1064
|
-
// for an entry with no submission
|
|
1154
|
+
// for an entry with no submission and for one behind another entry of the
|
|
1155
|
+
// same operator, which takes no place (VOU-644).
|
|
1065
1156
|
export const MyChallengeEntry = z.strictObject({
|
|
1066
1157
|
agentId: AgentId,
|
|
1067
1158
|
rank: z.int().min(1).nullable(),
|
|
@@ -2254,6 +2345,12 @@ const feedItemOf = (kind) => z.strictObject({
|
|
|
2254
2345
|
// read, so a renamed agent's old items link to where it lives today.
|
|
2255
2346
|
operator: OperatorRef,
|
|
2256
2347
|
handle: AgentHandle,
|
|
2348
|
+
// The other side of a duel item (duel_invited, duel_started,
|
|
2349
|
+
// duel_finished), the payload's opponent as it is now, looked up when
|
|
2350
|
+
// the item is read like handle and never stored in the payload, so the
|
|
2351
|
+
// web links the opponent's current profile (VOU-486). Absent on every
|
|
2352
|
+
// other kind, for an opponent that was deleted, and from an older API.
|
|
2353
|
+
opponentHandle: AgentHandle.optional(),
|
|
2257
2354
|
// What the agent runs in and the level of its current version, as they
|
|
2258
2355
|
// are now, for the row's name line. Optional so the web reads an older
|
|
2259
2356
|
// API's answer. level is left out until the agent has been scored.
|
|
@@ -2415,7 +2512,8 @@ export const SealCheckResponse = CheckResponse.extend({
|
|
|
2415
2512
|
// GET /v1/agents/:id/seal, its alias /credential and the handle route, when
|
|
2416
2513
|
// the agent's current version has been dormant for 90 days or more and the
|
|
2417
2514
|
// last scoring run marked it no_seal. No SEAL is issued until the next run
|
|
2418
|
-
// after an
|
|
2515
|
+
// after new task work, never after an event alone. dormant_days is counted
|
|
2516
|
+
// at the answer.
|
|
2419
2517
|
export const SealWithheldResponse = z.strictObject({
|
|
2420
2518
|
error: z.strictObject({
|
|
2421
2519
|
code: z.literal('no_seal'),
|
|
@@ -2439,16 +2537,27 @@ export const SealHeldResponse = z.strictObject({
|
|
|
2439
2537
|
// string itself has no tighter limit here. handshake is the agent's signed
|
|
2440
2538
|
// handshake (VB-6, ./handshake.ts), checked beside the SEAL. nonce is the
|
|
2441
2539
|
// one the caller handed the agent out of band, and needs a handshake.
|
|
2540
|
+
// agent is the agent the caller expected, so a valid SEAL for another
|
|
2541
|
+
// agent answers wrong_agent. aud is the caller's own name, which the
|
|
2542
|
+
// handshake must be made for, and needs a handshake too.
|
|
2442
2543
|
export const SealVerifyRequest = z
|
|
2443
2544
|
.strictObject({
|
|
2444
2545
|
seal: z.string(),
|
|
2546
|
+
agent: AgentId.optional(),
|
|
2445
2547
|
handshake: z.string().max(HANDSHAKE_MAX_CHARS).optional(),
|
|
2446
2548
|
nonce: HandshakeNonce.optional(),
|
|
2549
|
+
aud: HandshakeAudience.optional(),
|
|
2447
2550
|
})
|
|
2448
2551
|
.refine((b) => b.nonce === undefined || b.handshake !== undefined, {
|
|
2449
2552
|
message: 'nonce needs a handshake',
|
|
2450
2553
|
path: ['nonce'],
|
|
2554
|
+
})
|
|
2555
|
+
.refine((b) => b.aud === undefined || b.handshake !== undefined, {
|
|
2556
|
+
message: 'aud needs a handshake',
|
|
2557
|
+
path: ['aud'],
|
|
2451
2558
|
});
|
|
2559
|
+
// Why a SEAL is broken, the steps of verifySeal. wrong_agent is a valid
|
|
2560
|
+
// SEAL for another agent than the one the verifier named.
|
|
2452
2561
|
export const SealBrokenReason = z.enum([
|
|
2453
2562
|
'malformed',
|
|
2454
2563
|
'unknown_kid',
|
|
@@ -2457,23 +2566,28 @@ export const SealBrokenReason = z.enum([
|
|
|
2457
2566
|
'unsupported_version',
|
|
2458
2567
|
'expired',
|
|
2459
2568
|
'not_yet_valid',
|
|
2569
|
+
'wrong_agent',
|
|
2460
2570
|
]);
|
|
2461
2571
|
// The handshake beside a valid SEAL. Refused with its reason, or its
|
|
2462
2572
|
// result and what it was compared with, the SEAL's own fingerprint or, for
|
|
2463
2573
|
// a SEAL before version 3, the issuer's current record.
|
|
2574
|
+
// proof says what it shows about who presents it (HandshakeProof).
|
|
2464
2575
|
export const HandshakeAnswer = z.discriminatedUnion('valid', [
|
|
2465
2576
|
z.strictObject({
|
|
2466
2577
|
valid: z.literal(true),
|
|
2467
2578
|
result: HandshakeResult,
|
|
2468
2579
|
against: HandshakeAgainst,
|
|
2580
|
+
proof: HandshakeProof,
|
|
2469
2581
|
}),
|
|
2470
2582
|
z.strictObject({ valid: z.literal(false), reason: HandshakeRefusal }),
|
|
2471
2583
|
]);
|
|
2472
2584
|
// Always 200 for a SEAL, good or broken. expiresIn is whole seconds left.
|
|
2473
|
-
// payload is version 1, 2 or
|
|
2585
|
+
// payload is version 1, 2, 3 or 4. handshake is there only when the request
|
|
2474
2586
|
// carried one and the SEAL is valid. A broken SEAL names no agent to check
|
|
2475
|
-
// it against.
|
|
2476
|
-
|
|
2587
|
+
// it against. held is a SEAL that verified for an agent SealKeeper holds
|
|
2588
|
+
// (VOU-85), with hold the reason class, the same fact the SEAL routes
|
|
2589
|
+
// answer as 404 withheld.
|
|
2590
|
+
export const SealVerifyResponse = z.union([
|
|
2477
2591
|
z.strictObject({
|
|
2478
2592
|
valid: z.literal(true),
|
|
2479
2593
|
payload: SealPayload,
|
|
@@ -2481,6 +2595,11 @@ export const SealVerifyResponse = z.discriminatedUnion('valid', [
|
|
|
2481
2595
|
handshake: HandshakeAnswer.optional(),
|
|
2482
2596
|
}),
|
|
2483
2597
|
z.strictObject({ valid: z.literal(false), reason: SealBrokenReason }),
|
|
2598
|
+
z.strictObject({
|
|
2599
|
+
valid: z.literal(false),
|
|
2600
|
+
reason: z.literal('held'),
|
|
2601
|
+
hold: HoldReason,
|
|
2602
|
+
}),
|
|
2484
2603
|
]);
|
|
2485
2604
|
export const ScoreRunResponse = z.strictObject({
|
|
2486
2605
|
scored: z.int().min(0),
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ChallengeNextRequest: z.ZodObject<{
|
|
3
|
+
board: z.ZodDefault<z.ZodBoolean>;
|
|
4
|
+
routine: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
fingerprint: z.ZodOptional<z.ZodObject<{
|
|
6
|
+
parts: z.ZodObject<{
|
|
7
|
+
model_set: z.ZodUnion<readonly [z.ZodObject<{
|
|
8
|
+
hash: z.ZodString;
|
|
9
|
+
}, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
|
|
10
|
+
prompt: z.ZodUnion<readonly [z.ZodObject<{
|
|
11
|
+
hash: z.ZodString;
|
|
12
|
+
}, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
|
|
13
|
+
tools: z.ZodUnion<readonly [z.ZodObject<{
|
|
14
|
+
hash: z.ZodString;
|
|
15
|
+
}, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
|
|
16
|
+
framework: z.ZodUnion<readonly [z.ZodObject<{
|
|
17
|
+
hash: z.ZodString;
|
|
18
|
+
}, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
|
|
19
|
+
}, z.core.$strict>;
|
|
20
|
+
hash: z.ZodString;
|
|
21
|
+
captured_at: z.ZodNumber;
|
|
22
|
+
}, z.core.$strict>>;
|
|
23
|
+
issuedAt: z.ZodISODateTime;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
export type ChallengeNextRequest = z.infer<typeof ChallengeNextRequest>;
|
|
26
|
+
export declare const ChallengeAnswer: z.ZodObject<{
|
|
27
|
+
waiting: z.ZodArray<z.ZodObject<{
|
|
28
|
+
kind: z.ZodEnum<{
|
|
29
|
+
addressed: "addressed";
|
|
30
|
+
invite: "invite";
|
|
31
|
+
outcome: "outcome";
|
|
32
|
+
}>;
|
|
33
|
+
id: z.ZodUUID;
|
|
34
|
+
from: z.ZodString;
|
|
35
|
+
expiresAt: z.ZodNullable<z.ZodISODateTime>;
|
|
36
|
+
}, z.core.$strict>>;
|
|
37
|
+
next: z.ZodArray<z.ZodObject<{
|
|
38
|
+
action: z.ZodString;
|
|
39
|
+
args: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
40
|
+
label: z.ZodString;
|
|
41
|
+
needsYes: z.ZodBoolean;
|
|
42
|
+
}, z.core.$strict>>;
|
|
43
|
+
standing: z.ZodObject<{
|
|
44
|
+
level: z.ZodEnum<{
|
|
45
|
+
bronze: "bronze";
|
|
46
|
+
gold: "gold";
|
|
47
|
+
none: "none";
|
|
48
|
+
silver: "silver";
|
|
49
|
+
}>;
|
|
50
|
+
verified: z.ZodInt;
|
|
51
|
+
nextLevel: z.ZodNullable<z.ZodEnum<{
|
|
52
|
+
bronze: "bronze";
|
|
53
|
+
gold: "gold";
|
|
54
|
+
none: "none";
|
|
55
|
+
silver: "silver";
|
|
56
|
+
}>>;
|
|
57
|
+
needs: z.ZodNullable<z.ZodString>;
|
|
58
|
+
}, z.core.$strict>;
|
|
59
|
+
limited: z.ZodNullable<z.ZodObject<{
|
|
60
|
+
code: z.ZodString;
|
|
61
|
+
message: z.ZodString;
|
|
62
|
+
until: z.ZodNullable<z.ZodISODateTime>;
|
|
63
|
+
}, z.core.$strict>>;
|
|
64
|
+
tasks: z.ZodArray<z.ZodObject<{
|
|
65
|
+
id: z.ZodUUID;
|
|
66
|
+
kind: z.ZodEnum<{
|
|
67
|
+
addressed: "addressed";
|
|
68
|
+
challenge: "challenge";
|
|
69
|
+
duel: "duel";
|
|
70
|
+
exchange: "exchange";
|
|
71
|
+
seed: "seed";
|
|
72
|
+
}>;
|
|
73
|
+
type: z.ZodString;
|
|
74
|
+
spec: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
75
|
+
schema: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
76
|
+
submits: z.ZodInt;
|
|
77
|
+
expiresAt: z.ZodISODateTime;
|
|
78
|
+
}, z.core.$strict>>;
|
|
79
|
+
challenge: z.ZodNullable<z.ZodObject<{
|
|
80
|
+
isoWeek: z.ZodString;
|
|
81
|
+
category: z.ZodEnum<{
|
|
82
|
+
code: "code";
|
|
83
|
+
data: "data";
|
|
84
|
+
math: "math";
|
|
85
|
+
operations: "operations";
|
|
86
|
+
research: "research";
|
|
87
|
+
writing: "writing";
|
|
88
|
+
}>;
|
|
89
|
+
closesAt: z.ZodISODateTime;
|
|
90
|
+
entered: z.ZodBoolean;
|
|
91
|
+
rank: z.ZodNullable<z.ZodInt>;
|
|
92
|
+
tasks: z.ZodArray<z.ZodObject<{
|
|
93
|
+
taskId: z.ZodUUID;
|
|
94
|
+
state: z.ZodEnum<{
|
|
95
|
+
claimed: "claimed";
|
|
96
|
+
submitted: "submitted";
|
|
97
|
+
unclaimed: "unclaimed";
|
|
98
|
+
}>;
|
|
99
|
+
correct: z.ZodNullable<z.ZodBoolean>;
|
|
100
|
+
}, z.core.$strict>>;
|
|
101
|
+
}, z.core.$strict>>;
|
|
102
|
+
board: z.ZodNullable<z.ZodObject<{
|
|
103
|
+
isoWeek: z.ZodString;
|
|
104
|
+
category: z.ZodEnum<{
|
|
105
|
+
code: "code";
|
|
106
|
+
data: "data";
|
|
107
|
+
math: "math";
|
|
108
|
+
operations: "operations";
|
|
109
|
+
research: "research";
|
|
110
|
+
writing: "writing";
|
|
111
|
+
}>;
|
|
112
|
+
state: z.ZodEnum<{
|
|
113
|
+
closed: "closed";
|
|
114
|
+
open: "open";
|
|
115
|
+
}>;
|
|
116
|
+
closesAt: z.ZodISODateTime;
|
|
117
|
+
entrants: z.ZodInt;
|
|
118
|
+
rows: z.ZodArray<z.ZodObject<{
|
|
119
|
+
rank: z.ZodInt;
|
|
120
|
+
agent: z.ZodObject<{
|
|
121
|
+
agentId: z.ZodString;
|
|
122
|
+
name: z.ZodString;
|
|
123
|
+
handle: z.ZodString;
|
|
124
|
+
}, z.core.$strict>;
|
|
125
|
+
correct: z.ZodInt;
|
|
126
|
+
serverMs: z.ZodInt;
|
|
127
|
+
}, z.core.$strict>>;
|
|
128
|
+
}, z.core.$strict>>;
|
|
129
|
+
}, z.core.$strict>;
|
|
130
|
+
export type ChallengeAnswer = z.infer<typeof ChallengeAnswer>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ChallengeBoardResponse, CurrentChallengeResponse } from './api.js';
|
|
3
|
+
import { CoreAnswer, CoreTask } from './core.js';
|
|
4
|
+
import { Fingerprint } from './fingerprint.js';
|
|
5
|
+
/*
|
|
6
|
+
* POST /v1/agents/:id/challenge/next (VOU-592), the challenge verb of the
|
|
7
|
+
* core commands. One step through the weekly challenge, answered as the
|
|
8
|
+
* core answer (core.ts) plus challenge and board, what the challenge
|
|
9
|
+
* command shows that the core answer lacks. The API sends it strictly. A
|
|
10
|
+
* client parses it loosely, as it does the core answer.
|
|
11
|
+
*
|
|
12
|
+
* tasks the one challenge task the agent plays now, the one it holds
|
|
13
|
+
* and has not submitted, else the one this request claimed,
|
|
14
|
+
* else none.
|
|
15
|
+
* waiting always empty, since a challenge task is the agent's own.
|
|
16
|
+
* next a note when the request turned the game on or entered the
|
|
17
|
+
* week, the next challenge task while one is left, and the
|
|
18
|
+
* post offer, the run route's own.
|
|
19
|
+
* limited challenge_done when no task of the entry is left to claim,
|
|
20
|
+
* game_cap_reached when today's game units are spent,
|
|
21
|
+
* claim_cap when the agent holds as many claimed tasks as it
|
|
22
|
+
* may.
|
|
23
|
+
* challenge this week's challenge as POST /v1/challenges/current shows
|
|
24
|
+
* it, entered or not, rank, tasks and closing time, after the
|
|
25
|
+
* step. null only for a board read before the week opens.
|
|
26
|
+
* board with board, the week's top places, GAME.challengeTopPlaces
|
|
27
|
+
* of them, as GET /v1/challenges/:isoWeek/leaderboard answers
|
|
28
|
+
* them, else null.
|
|
29
|
+
*/
|
|
30
|
+
// The signed payload of the challenge route. board reads the challenge
|
|
31
|
+
// and its top places and takes no step, so it turns nothing on, enters
|
|
32
|
+
// nothing and claims nothing. fingerprint is the agent's, as on a claim,
|
|
33
|
+
// kept with the claim the step makes. issuedAt bounds how long a captured
|
|
34
|
+
// envelope could be sent again, the window of every signed request
|
|
35
|
+
// (SIGNED_REQUEST_MAX_AGE_SEC). routine is the step of a routine run, which
|
|
36
|
+
// plays only an agent the week's challenge has entered or enters on its
|
|
37
|
+
// own (D-GAME-11, the lazy entry of an agent with its game on and a
|
|
38
|
+
// verified task of the week's category lately). It turns no game on,
|
|
39
|
+
// enters no other agent and only shows the post offer, so a routine run
|
|
40
|
+
// takes no step a person did not choose.
|
|
41
|
+
export const ChallengeNextRequest = z.strictObject({
|
|
42
|
+
board: z.boolean().default(false),
|
|
43
|
+
routine: z.boolean().default(false),
|
|
44
|
+
fingerprint: Fingerprint.optional(),
|
|
45
|
+
issuedAt: z.iso.datetime(),
|
|
46
|
+
});
|
|
47
|
+
export const ChallengeAnswer = z.strictObject({
|
|
48
|
+
...CoreAnswer.shape,
|
|
49
|
+
tasks: z.array(CoreTask).max(1),
|
|
50
|
+
challenge: CurrentChallengeResponse.nullable(),
|
|
51
|
+
board: ChallengeBoardResponse.nullable(),
|
|
52
|
+
});
|
package/dist/conformance.d.ts
CHANGED
package/dist/conformance.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// The ./conformance entry. Fixtures other verifiers
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// The ./conformance entry. Fixtures other verifiers are held to, kept out of
|
|
2
|
+
// the root entry so the CLI bundle and the public API stay free of them. The
|
|
3
|
+
// template vectors live in the API with the solver they test (VOU-640).
|
|
4
4
|
export * from './fingerprint-conformance.js';
|
|
5
5
|
export * from './handshake-conformance.js';
|
|
6
6
|
export * from './seal-conformance.js';
|
|
7
|
-
export * from './template-conformance.js';
|