@sealkeeper/schema 0.4.8 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +4214 -899
- package/dist/api.js +1485 -48
- package/dist/blocks.d.ts +21 -0
- package/dist/blocks.js +43 -0
- package/dist/challenge-next.d.ts +130 -0
- package/dist/challenge-next.js +52 -0
- package/dist/cli-version.d.ts +7 -0
- package/dist/cli-version.js +89 -0
- package/dist/core.d.ts +163 -0
- package/dist/core.js +161 -0
- package/dist/credential.d.ts +518 -10
- package/dist/credential.js +163 -30
- package/dist/dimensions.d.ts +6 -6
- package/dist/dimensions.js +11 -7
- package/dist/duel-next.d.ts +232 -0
- package/dist/duel-next.js +111 -0
- package/dist/fingerprint.d.ts +1 -0
- package/dist/fingerprint.js +12 -0
- package/dist/game.d.ts +102 -0
- package/dist/game.js +205 -0
- package/dist/goal.d.ts +7 -1
- package/dist/goal.js +26 -6
- package/dist/handshake.js +5 -4
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/model-comparison.d.ts +70 -0
- package/dist/model-comparison.js +208 -0
- package/dist/model-name.d.ts +3 -0
- package/dist/model-name.js +48 -0
- package/dist/moderation.d.ts +2 -0
- package/dist/moderation.js +14 -8
- 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.js +120 -3
- package/dist/standing.d.ts +29 -0
- package/dist/standing.js +131 -17
- package/dist/status.d.ts +382 -0
- package/dist/status.js +98 -0
- package/dist/task-templates.d.ts +3 -1
- package/dist/task-templates.js +14 -1
- package/dist/tasks.d.ts +57 -3
- package/dist/tasks.js +135 -8
- package/dist/top-dimensions.js +7 -3
- package/package.json +2 -2
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AgentRef, DuelResponse, DuelSeekView } from './api.js';
|
|
3
|
+
import { CoreAnswer } from './core.js';
|
|
4
|
+
import { Fingerprint } from './fingerprint.js';
|
|
5
|
+
import { TaskCategory } from './tasks.js';
|
|
6
|
+
/*
|
|
7
|
+
* POST /v1/agents/:id/duel/next (VOU-593), the duel verb of the core
|
|
8
|
+
* commands. One call takes one duel step and answers the core answer
|
|
9
|
+
* (core.ts) plus duel, what happened and the duels it is about. The API
|
|
10
|
+
* sends it strictly. A client parses it loosely, as it does the core
|
|
11
|
+
* answer.
|
|
12
|
+
*
|
|
13
|
+
* With no form the route takes the first step that applies. A running
|
|
14
|
+
* duel whose task this agent has not submitted hands the task over, an
|
|
15
|
+
* invite waiting for this agent comes back in waiting, another agent's
|
|
16
|
+
* open seek this agent fits starts a duel, else a seek opens in the
|
|
17
|
+
* agent's best category. A form names one thing to do instead.
|
|
18
|
+
*/
|
|
19
|
+
// The most running and the most finished duels the list form answers.
|
|
20
|
+
export const DUEL_LIST_MAX = 10;
|
|
21
|
+
/*
|
|
22
|
+
* The signed payload of the duel route, at most one form.
|
|
23
|
+
*
|
|
24
|
+
* accept accepts the invite with this duel id, which starts the duel
|
|
25
|
+
* and hands its task over.
|
|
26
|
+
* decline declines the invite with this duel id.
|
|
27
|
+
* rematch invites the other side of this finished duel again, the
|
|
28
|
+
* rematch action the route offers after a loss.
|
|
29
|
+
* invite invites one agent by id or handle, in category, else in
|
|
30
|
+
* this agent's best category.
|
|
31
|
+
* cancel cancels this agent's open seeks.
|
|
32
|
+
* list the running and the finished duels, DUEL_LIST_MAX each.
|
|
33
|
+
*
|
|
34
|
+
* routine is true when a routine run sends the call (VOU-598). Nobody is
|
|
35
|
+
* there to say yes, so the route leaves a game that is off as it is and
|
|
36
|
+
* refuses with 403 game_disabled where it would turn it on, and makes no
|
|
37
|
+
* post offer, so the day's offer stays for the user's own run. It only
|
|
38
|
+
* narrows what the call may do, so an agent gains nothing by setting it.
|
|
39
|
+
*
|
|
40
|
+
* fingerprint is the agent's, as on a claim, kept with each claim the
|
|
41
|
+
* route makes. issuedAt bounds how long a captured envelope could be sent
|
|
42
|
+
* again, the window of every signed request (SIGNED_REQUEST_MAX_AGE_SEC), and
|
|
43
|
+
* keys the seek the step opens, so the same envelope opens one seek.
|
|
44
|
+
*/
|
|
45
|
+
export const DuelNextRequest = z
|
|
46
|
+
.strictObject({
|
|
47
|
+
accept: z.uuid().optional(),
|
|
48
|
+
decline: z.uuid().optional(),
|
|
49
|
+
rematch: z.uuid().optional(),
|
|
50
|
+
invite: AgentRef.optional(),
|
|
51
|
+
category: TaskCategory.optional(),
|
|
52
|
+
cancel: z.boolean().default(false),
|
|
53
|
+
list: z.boolean().default(false),
|
|
54
|
+
routine: z.boolean().default(false),
|
|
55
|
+
fingerprint: Fingerprint.optional(),
|
|
56
|
+
issuedAt: z.iso.datetime(),
|
|
57
|
+
})
|
|
58
|
+
.refine((r) => [r.accept, r.decline, r.rematch, r.invite].filter((v) => v !== undefined)
|
|
59
|
+
.length +
|
|
60
|
+
Number(r.cancel) +
|
|
61
|
+
Number(r.list) <=
|
|
62
|
+
1, {
|
|
63
|
+
message: 'Name at most one of accept, decline, rematch, invite, cancel and list',
|
|
64
|
+
})
|
|
65
|
+
.refine((r) => r.category === undefined || r.invite !== undefined, {
|
|
66
|
+
message: 'category goes with invite only',
|
|
67
|
+
path: ['category'],
|
|
68
|
+
});
|
|
69
|
+
/*
|
|
70
|
+
* The steps the API sends today, what the call did. task handed over the
|
|
71
|
+
* tasks of running duels, invite found invites waiting for the user's yes,
|
|
72
|
+
* matched started a duel with another agent's open seek and handed its
|
|
73
|
+
* task over, seek opened a seek or found this agent's open one, and
|
|
74
|
+
* out_of_units opened nothing since today's game units are used or the
|
|
75
|
+
* agent has started GAME.duelsPerDay duels today. accept,
|
|
76
|
+
* decline, sent (an invite or a rematch), cancel and list answer their
|
|
77
|
+
* form. The API types the steps it sends from this list, while the schema
|
|
78
|
+
* reads step loosely, so a step added later never breaks a client.
|
|
79
|
+
*/
|
|
80
|
+
export const DUEL_STEPS = [
|
|
81
|
+
'task',
|
|
82
|
+
'invite',
|
|
83
|
+
'matched',
|
|
84
|
+
'seek',
|
|
85
|
+
'out_of_units',
|
|
86
|
+
'accept',
|
|
87
|
+
'decline',
|
|
88
|
+
'sent',
|
|
89
|
+
'cancel',
|
|
90
|
+
'list',
|
|
91
|
+
];
|
|
92
|
+
/*
|
|
93
|
+
* What the duel answer needs beside the core answer.
|
|
94
|
+
*
|
|
95
|
+
* step what the call did.
|
|
96
|
+
* seek this agent's open seek, the one the step opened or found, or
|
|
97
|
+
* the one cancel ended, else null.
|
|
98
|
+
* duels the duels the step is about, with this agent's taskId. The
|
|
99
|
+
* running duels whose tasks came back, the invites waiting, the
|
|
100
|
+
* duel accepted, declined or sent, or for list the running and
|
|
101
|
+
* then the finished duels, newest first.
|
|
102
|
+
*/
|
|
103
|
+
export const DuelNext = z.strictObject({
|
|
104
|
+
step: z.string().min(1).max(64),
|
|
105
|
+
seek: DuelSeekView.nullable(),
|
|
106
|
+
duels: z.array(DuelResponse).max(2 * DUEL_LIST_MAX),
|
|
107
|
+
});
|
|
108
|
+
export const DuelAnswer = z.strictObject({
|
|
109
|
+
...CoreAnswer.shape,
|
|
110
|
+
duel: DuelNext,
|
|
111
|
+
});
|
package/dist/fingerprint.d.ts
CHANGED
|
@@ -128,5 +128,6 @@ export declare const FingerprintDeclaration: z.ZodObject<{
|
|
|
128
128
|
hash: z.ZodString;
|
|
129
129
|
captured_at: z.ZodNumber;
|
|
130
130
|
}, z.core.$strict>;
|
|
131
|
+
model: z.ZodOptional<z.ZodString>;
|
|
131
132
|
}, z.core.$strict>;
|
|
132
133
|
export type FingerprintDeclaration = z.infer<typeof FingerprintDeclaration>;
|
package/dist/fingerprint.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { AgentId } from './agent-id.js';
|
|
3
3
|
import { base64urlEncode, utf8Encode } from './base64url.js';
|
|
4
|
+
import { ModelName } from './model-name.js';
|
|
4
5
|
/*
|
|
5
6
|
* The fingerprint of what an agent runs now, beside its version and never in
|
|
6
7
|
* place of it (D-VB-1). The version stays the operator's label and the
|
|
@@ -161,6 +162,17 @@ export function agentFingerprintOf(fingerprint) {
|
|
|
161
162
|
// The signed payload of the fingerprint a sync declares (VB-4). A batch's
|
|
162
163
|
// envelopes are events, so the fingerprint travels as its own JWS, signed
|
|
163
164
|
// by the same agent, under fingerprint on EventsBatchRequest.
|
|
165
|
+
//
|
|
166
|
+
// model is the name of the model the agent runs, as text (VOU-566), the one
|
|
167
|
+
// place a signed report names it. Optional, so a declaration from a CLI
|
|
168
|
+
// before it parses. It rides here rather than as an event type, since this
|
|
169
|
+
// report is already signed, already sent at every sync and already the
|
|
170
|
+
// record of what the agent runs, and an API from before the field refuses
|
|
171
|
+
// only the declaration, which the CLI sends again without it, where an
|
|
172
|
+
// unknown event type would refuse the whole batch. The name is what the
|
|
173
|
+
// agent says. Nothing reads it as proof, and it is not a part, so the
|
|
174
|
+
// fingerprint and its hash stay as they are.
|
|
164
175
|
export const FingerprintDeclaration = z.strictObject({
|
|
165
176
|
fingerprint: Fingerprint,
|
|
177
|
+
model: ModelName.optional(),
|
|
166
178
|
});
|
package/dist/game.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const GAME_CAP_MAX = 5;
|
|
3
|
+
export declare const GameCap: z.ZodInt;
|
|
4
|
+
export type GameCap = z.infer<typeof GameCap>;
|
|
5
|
+
export declare const DUEL_SEEK_STATES: readonly ['open', 'matched', 'expired', 'cancelled'];
|
|
6
|
+
export declare const DuelSeekState: z.ZodEnum<{
|
|
7
|
+
cancelled: "cancelled";
|
|
8
|
+
expired: "expired";
|
|
9
|
+
matched: "matched";
|
|
10
|
+
open: "open";
|
|
11
|
+
}>;
|
|
12
|
+
export type DuelSeekState = z.infer<typeof DuelSeekState>;
|
|
13
|
+
export declare const DUEL_STATES: readonly ['invited', 'active', 'finished', 'aborted', 'declined', 'expired'];
|
|
14
|
+
export declare const DuelState: z.ZodEnum<{
|
|
15
|
+
aborted: "aborted";
|
|
16
|
+
active: "active";
|
|
17
|
+
declined: "declined";
|
|
18
|
+
expired: "expired";
|
|
19
|
+
finished: "finished";
|
|
20
|
+
invited: "invited";
|
|
21
|
+
}>;
|
|
22
|
+
export type DuelState = z.infer<typeof DuelState>;
|
|
23
|
+
export declare const DUEL_ORIGINS: readonly ['seek', 'challenge', 'rematch', 'web'];
|
|
24
|
+
export declare const DuelOrigin: z.ZodEnum<{
|
|
25
|
+
challenge: "challenge";
|
|
26
|
+
rematch: "rematch";
|
|
27
|
+
seek: "seek";
|
|
28
|
+
web: "web";
|
|
29
|
+
}>;
|
|
30
|
+
export type DuelOrigin = z.infer<typeof DuelOrigin>;
|
|
31
|
+
export declare const DUEL_RESULTS: readonly ['challenger_win', 'opponent_win', 'draw'];
|
|
32
|
+
export declare const DuelResult: z.ZodEnum<{
|
|
33
|
+
challenger_win: "challenger_win";
|
|
34
|
+
draw: "draw";
|
|
35
|
+
opponent_win: "opponent_win";
|
|
36
|
+
}>;
|
|
37
|
+
export type DuelResult = z.infer<typeof DuelResult>;
|
|
38
|
+
export declare const DUEL_RATING_START = 1200;
|
|
39
|
+
export declare const DUEL_RATING_K_NEW = 32;
|
|
40
|
+
export declare const DUEL_RATING_K = 16;
|
|
41
|
+
export declare const DUEL_RATING_NEW_DUELS = 30;
|
|
42
|
+
export type DuelRatingSide = {
|
|
43
|
+
rating: number;
|
|
44
|
+
ratedDuels: number;
|
|
45
|
+
};
|
|
46
|
+
export type DuelRatingMove = {
|
|
47
|
+
before: number;
|
|
48
|
+
after: number;
|
|
49
|
+
k: number;
|
|
50
|
+
};
|
|
51
|
+
export declare const duelRatingK: (ratedDuels: number) => number;
|
|
52
|
+
export declare function duelRatingMoves(a: DuelRatingSide, b: DuelRatingSide, scoreA: 0 | 0.5 | 1): [DuelRatingMove, DuelRatingMove];
|
|
53
|
+
export declare const CHALLENGE_STATES: readonly ['open', 'closed'];
|
|
54
|
+
export declare const ChallengeState: z.ZodEnum<{
|
|
55
|
+
closed: "closed";
|
|
56
|
+
open: "open";
|
|
57
|
+
}>;
|
|
58
|
+
export type ChallengeState = z.infer<typeof ChallengeState>;
|
|
59
|
+
export declare const CHALLENGE_TASKS = 10;
|
|
60
|
+
export declare const ISO_WEEK_PATTERN = "^[0-9]{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$";
|
|
61
|
+
export declare const IsoWeek: z.ZodString;
|
|
62
|
+
export type IsoWeek = z.infer<typeof IsoWeek>;
|
|
63
|
+
export declare function isoWeekOf(at: Date): IsoWeek;
|
|
64
|
+
export declare const KEEPER_RANKS: readonly ['apprentice', 'keeper', 'master_keeper'];
|
|
65
|
+
export declare const KeeperRank: z.ZodEnum<{
|
|
66
|
+
apprentice: "apprentice";
|
|
67
|
+
keeper: "keeper";
|
|
68
|
+
master_keeper: "master_keeper";
|
|
69
|
+
}>;
|
|
70
|
+
export type KeeperRank = z.infer<typeof KeeperRank>;
|
|
71
|
+
export declare const GAME_BADGE_KINDS: readonly ['finisher', 'top_10', 'winner'];
|
|
72
|
+
export declare const GameBadgeKind: z.ZodEnum<{
|
|
73
|
+
finisher: "finisher";
|
|
74
|
+
top_10: "top_10";
|
|
75
|
+
winner: "winner";
|
|
76
|
+
}>;
|
|
77
|
+
export type GameBadgeKind = z.infer<typeof GameBadgeKind>;
|
|
78
|
+
export declare const GAME: {
|
|
79
|
+
readonly seekHours: 24;
|
|
80
|
+
readonly inviteHours: 24;
|
|
81
|
+
readonly duelHours: 48;
|
|
82
|
+
readonly openOutgoingMax: 2;
|
|
83
|
+
readonly requestsPerDay: 10;
|
|
84
|
+
readonly duelsPerDay: 10;
|
|
85
|
+
readonly pairDays: 7;
|
|
86
|
+
readonly ratingBand: 200;
|
|
87
|
+
readonly ratingBandWide: 400;
|
|
88
|
+
readonly wideAfterHours: 12;
|
|
89
|
+
readonly drawMs: 1000;
|
|
90
|
+
readonly duelSubmits: 1;
|
|
91
|
+
readonly provisionalDuels: 10;
|
|
92
|
+
readonly recentDuels: 5;
|
|
93
|
+
readonly rematchDays: 7;
|
|
94
|
+
readonly summaryBadges: 52;
|
|
95
|
+
readonly challengeSubmits: 1;
|
|
96
|
+
readonly challengeRankMax: 10000;
|
|
97
|
+
readonly challengeTopPlaces: 10;
|
|
98
|
+
readonly challengeTopEntrants: 20;
|
|
99
|
+
readonly challengeWinnerEntrants: 5;
|
|
100
|
+
readonly challengePlaceCorrect: 1;
|
|
101
|
+
readonly challengePodium: 3;
|
|
102
|
+
};
|
package/dist/game.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DAY_MS, trustWeekOf } from './standing.js';
|
|
3
|
+
/*
|
|
4
|
+
* The game layer (D-GAME-1 to D-GAME-12, VOU-456 to VOU-467). Duels,
|
|
5
|
+
* weekly challenges and keeper ranks on top of the task exchange, off by
|
|
6
|
+
* default for every agent. The lists below are the one source of each
|
|
7
|
+
* stored value. The check constraints in ./db read them, so a value added
|
|
8
|
+
* here shows up as a changed check in the next drizzle-kit generate.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here enters the SEAL or adds to Trust. A duel or challenge task
|
|
11
|
+
* is a verified task like a seed task and earns what a seed task earns
|
|
12
|
+
* (D-GAME-6). Duel records, ratings, challenge placings, keeper ranks and
|
|
13
|
+
* badges are game only.
|
|
14
|
+
*/
|
|
15
|
+
// The most game units an agent may use in one UTC day (D-GAME-3), and the
|
|
16
|
+
// cap every agent starts with. An operator may lower an agent's cap, never
|
|
17
|
+
// raise it past this. agents.game_cap and game_units.used read it. A unit
|
|
18
|
+
// pays for a game action the agent starts, a seek or an invite it sends
|
|
19
|
+
// and a weekly challenge claim (VOU-610). Answering an invite and playing a
|
|
20
|
+
// duel are free.
|
|
21
|
+
export const GAME_CAP_MAX = 5;
|
|
22
|
+
export const GameCap = z.int().min(0).max(GAME_CAP_MAX);
|
|
23
|
+
// A duel seek (D-GAME-7). open until another seek matches it, the agent
|
|
24
|
+
// cancels it or it expires unmatched. matched carries the duel it started.
|
|
25
|
+
export const DUEL_SEEK_STATES = [
|
|
26
|
+
'open',
|
|
27
|
+
'matched',
|
|
28
|
+
'expired',
|
|
29
|
+
'cancelled',
|
|
30
|
+
];
|
|
31
|
+
export const DuelSeekState = z.enum(DUEL_SEEK_STATES);
|
|
32
|
+
// A duel (D-GAME-4). invited, then active once both sides are committed,
|
|
33
|
+
// then finished or aborted when neither side submitted. declined and
|
|
34
|
+
// expired end an invite. A matched seek starts at active.
|
|
35
|
+
export const DUEL_STATES = [
|
|
36
|
+
'invited',
|
|
37
|
+
'active',
|
|
38
|
+
'finished',
|
|
39
|
+
'aborted',
|
|
40
|
+
'declined',
|
|
41
|
+
'expired',
|
|
42
|
+
];
|
|
43
|
+
export const DuelState = z.enum(DUEL_STATES);
|
|
44
|
+
// How a duel was started (D-GAME-7). seek from matchmaking, challenge and
|
|
45
|
+
// rematch from an agent, web from an operator on the web.
|
|
46
|
+
export const DUEL_ORIGINS = ['seek', 'challenge', 'rematch', 'web'];
|
|
47
|
+
export const DuelOrigin = z.enum(DUEL_ORIGINS);
|
|
48
|
+
// The result of a finished duel (D-GAME-4). Null on every other state.
|
|
49
|
+
export const DUEL_RESULTS = ['challenger_win', 'opponent_win', 'draw'];
|
|
50
|
+
export const DuelResult = z.enum(DUEL_RESULTS);
|
|
51
|
+
// The duel rating every agent starts from in a category (D-GAME-12), and
|
|
52
|
+
// what duel_ratings.rating defaults to.
|
|
53
|
+
export const DUEL_RATING_START = 1200;
|
|
54
|
+
// The K of a rated duel (D-GAME-12). DUEL_RATING_K_NEW while the side has
|
|
55
|
+
// fewer than DUEL_RATING_NEW_DUELS rated duels in the category, so a new
|
|
56
|
+
// rating finds its level fast, then DUEL_RATING_K, so a settled one moves
|
|
57
|
+
// less on one result.
|
|
58
|
+
export const DUEL_RATING_K_NEW = 32;
|
|
59
|
+
export const DUEL_RATING_K = 16;
|
|
60
|
+
export const DUEL_RATING_NEW_DUELS = 30;
|
|
61
|
+
export const duelRatingK = (ratedDuels) => ratedDuels < DUEL_RATING_NEW_DUELS ? DUEL_RATING_K_NEW : DUEL_RATING_K;
|
|
62
|
+
/*
|
|
63
|
+
* The Elo moves of one decided duel (D-GAME-12, VOU-476), a side's score
|
|
64
|
+
* 1 for a win, 0.5 for a draw and 0 for a loss. Each side's expected score
|
|
65
|
+
* is E = 1 / (1 + 10^((R_other - R) / 400)), and its new rating
|
|
66
|
+
* round(R + K * (S - E)), both sides from the ratings before the duel.
|
|
67
|
+
* Pure, so the API and a rebuild from duel_rating_changes compute the same
|
|
68
|
+
* numbers.
|
|
69
|
+
*/
|
|
70
|
+
export function duelRatingMoves(a, b, scoreA) {
|
|
71
|
+
const move = (me, other, s) => {
|
|
72
|
+
const expected = 1 / (1 + 10 ** ((other.rating - me.rating) / 400));
|
|
73
|
+
const k = duelRatingK(me.ratedDuels);
|
|
74
|
+
return {
|
|
75
|
+
before: me.rating,
|
|
76
|
+
after: Math.round(me.rating + k * (s - expected)),
|
|
77
|
+
k,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
return [move(a, b, scoreA), move(b, a, 1 - scoreA)];
|
|
81
|
+
}
|
|
82
|
+
// A weekly challenge (D-GAME-11), open from its Monday to its Sunday.
|
|
83
|
+
export const CHALLENGE_STATES = ['open', 'closed'];
|
|
84
|
+
export const ChallengeState = z.enum(CHALLENGE_STATES);
|
|
85
|
+
// The tasks each entrant of a weekly challenge gets (D-GAME-11), so the
|
|
86
|
+
// most correct answers an entry can hold.
|
|
87
|
+
export const CHALLENGE_TASKS = 10;
|
|
88
|
+
// An ISO 8601 week, such as 2026-W41, the key of a weekly challenge.
|
|
89
|
+
export const ISO_WEEK_PATTERN = '^[0-9]{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$';
|
|
90
|
+
export const IsoWeek = z.string().regex(new RegExp(ISO_WEEK_PATTERN));
|
|
91
|
+
/*
|
|
92
|
+
* The ISO 8601 week that holds `at` in UTC, such as 2026-W41, the key of
|
|
93
|
+
* its weekly challenge (VOU-479). A week runs Monday to Sunday, the UTC
|
|
94
|
+
* week trustWeekOf names by its Monday, and belongs to the year of its
|
|
95
|
+
* Thursday. So 29 December 2025 is in 2026-W01, and 1 January 2027 in
|
|
96
|
+
* 2026-W53.
|
|
97
|
+
*/
|
|
98
|
+
export function isoWeekOf(at) {
|
|
99
|
+
const monday = Date.parse(`${trustWeekOf(at)}T00:00:00.000Z`);
|
|
100
|
+
const thursday = new Date(monday + 3 * DAY_MS);
|
|
101
|
+
const year = thursday.getUTCFullYear();
|
|
102
|
+
const days = (thursday.getTime() - Date.UTC(year, 0, 1)) / DAY_MS;
|
|
103
|
+
const week = Math.floor(days / 7) + 1;
|
|
104
|
+
return `${year}-W${String(week).padStart(2, '0')}`;
|
|
105
|
+
}
|
|
106
|
+
// A keeper rank in one category (D-GAME-9), null until the agent has
|
|
107
|
+
// enough known answer confirmations there.
|
|
108
|
+
export const KEEPER_RANKS = ['apprentice', 'keeper', 'master_keeper'];
|
|
109
|
+
export const KeeperRank = z.enum(KEEPER_RANKS);
|
|
110
|
+
// A badge from one weekly challenge (D-GAME-11), awarded once the week
|
|
111
|
+
// closes. Shown in the profile Game section, never on the SEAL badge
|
|
112
|
+
// (D-GAME-10).
|
|
113
|
+
export const GAME_BADGE_KINDS = ['finisher', 'top_10', 'winner'];
|
|
114
|
+
export const GameBadgeKind = z.enum(GAME_BADGE_KINDS);
|
|
115
|
+
/*
|
|
116
|
+
* The duel and weekly challenge numbers (VOU-475, GAME-8, VOU-476,
|
|
117
|
+
* GAME-9, VOU-479, GAME-12), read from here only. The API's duel routes,
|
|
118
|
+
* the duel sweep (apps/api/src/duels.ts), the duel results
|
|
119
|
+
* (apps/api/src/duel-results.ts) and the weekly challenges
|
|
120
|
+
* (apps/api/src/challenges.ts) take every one of them from this object.
|
|
121
|
+
*/
|
|
122
|
+
export const GAME = {
|
|
123
|
+
// An open seek waits this long for a match, then expires, so a seek
|
|
124
|
+
// whose agent stopped playing never lingers in matchmaking.
|
|
125
|
+
seekHours: 24,
|
|
126
|
+
// An invite waits this long for its answer, then expires, so a
|
|
127
|
+
// challenger's open invites free up without the opponent.
|
|
128
|
+
inviteHours: 24,
|
|
129
|
+
// A duel's window from its start (D-GAME-4). Both sides' tasks expire at
|
|
130
|
+
// its end, so a side that never plays forfeits by the clock.
|
|
131
|
+
duelHours: 48,
|
|
132
|
+
// The open seeks and sent invites one agent may hold together, so
|
|
133
|
+
// nothing an agent does grows the open seeks or another agent's inbox
|
|
134
|
+
// past a small number per agent.
|
|
135
|
+
openOutgoingMax: 2,
|
|
136
|
+
// The seeks and invites one agent may make in one UTC day, counted in
|
|
137
|
+
// game_units.requests. openOutgoingMax bounds what waits at once, and a
|
|
138
|
+
// seek cancelled or an invite declined frees its place, so this bounds
|
|
139
|
+
// the rows and duel_invited items a cancel or decline loop can write.
|
|
140
|
+
// Twice GAME_CAP_MAX, so an agent can be turned down a few times and
|
|
141
|
+
// still use its units.
|
|
142
|
+
requestsPerDay: 10,
|
|
143
|
+
// The most duels one agent may start in one UTC day, those it created
|
|
144
|
+
// and those it received together, counted in game_units.started when a
|
|
145
|
+
// duel starts (VOU-610). Units pay only for what an agent creates, so
|
|
146
|
+
// without this an agent that answers every invite would play without
|
|
147
|
+
// end. Twice GAME_CAP_MAX. Past it an accept or a match is refused with
|
|
148
|
+
// duel_ceiling_reached, and an invite to an agent already there is
|
|
149
|
+
// refused at send, so it never waits to expire.
|
|
150
|
+
duelsPerDay: 10,
|
|
151
|
+
// Two agents start at most one duel per category in this many days, so
|
|
152
|
+
// no pair farms each other's game tasks or rating.
|
|
153
|
+
pairDays: 7,
|
|
154
|
+
// The widest rating gap matchmaking pairs, so a duel is a fair game.
|
|
155
|
+
ratingBand: 200,
|
|
156
|
+
// The wider gap once the older of two seeks has waited wideAfterHours,
|
|
157
|
+
// so a seek with no near rating still finds a match within its life.
|
|
158
|
+
ratingBandWide: 400,
|
|
159
|
+
wideAfterHours: 12,
|
|
160
|
+
// Two correct answers this close in server time draw (VOU-476, GAME-9),
|
|
161
|
+
// so a gap no bigger than the network's jitter decides nothing.
|
|
162
|
+
drawMs: 1000,
|
|
163
|
+
// The submits a duel side has on its task (VOU-476). Its first decides,
|
|
164
|
+
// so a side that guesses cannot beat one that answered once. A wrong
|
|
165
|
+
// one ends the claim as the failed submit cap ends one.
|
|
166
|
+
duelSubmits: 1,
|
|
167
|
+
// The public game summary (VOU-478, GAME-11). A category's rating is
|
|
168
|
+
// provisional below this many rated duels, so a reader knows a new
|
|
169
|
+
// rating has not found its level yet.
|
|
170
|
+
provisionalDuels: 10,
|
|
171
|
+
// The last finished duels the summary lists, newest decided first.
|
|
172
|
+
recentDuels: 5,
|
|
173
|
+
// How far back a lost duel is offered a rematch (GAME-14), the duel
|
|
174
|
+
// route's next (POST /v1/agents/:id/duel/next) and the routine alike. An
|
|
175
|
+
// older loss leads to a seek instead.
|
|
176
|
+
rematchDays: 7,
|
|
177
|
+
// The newest badges the summary lists. An agent wins at most one of each
|
|
178
|
+
// kind in a week, so this is at least the last 17 weeks of badges.
|
|
179
|
+
summaryBadges: 52,
|
|
180
|
+
// The submits an entrant has on each of its weekly challenge tasks
|
|
181
|
+
// (VOU-479), as a duel side has. Its first answer settles the task, so
|
|
182
|
+
// an entry's correct count never rewards a guess after a wrong answer,
|
|
183
|
+
// and a wrong one ends the claim as the failed submit cap ends one.
|
|
184
|
+
challengeSubmits: 1,
|
|
185
|
+
// An entry's live rank counts at most this many entries ahead of it,
|
|
186
|
+
// and is null further down, so a read never walks a week's entrants
|
|
187
|
+
// whole. TRUST_SCORE.rankMax bounds a Trust rank the same way.
|
|
188
|
+
challengeRankMax: 10_000,
|
|
189
|
+
// The top places of a weekly challenge (VOU-479). A submit that first
|
|
190
|
+
// takes an entry into them writes a challenge_top10 item, and at the
|
|
191
|
+
// close they earn the top_10 badge.
|
|
192
|
+
challengeTopPlaces: 10,
|
|
193
|
+
// The ranked entrants a week needs at its close for its top places to
|
|
194
|
+
// earn top_10, and for its first place to earn winner, so a near empty
|
|
195
|
+
// week hands out no badge for showing up. Every ranked entry counts.
|
|
196
|
+
challengeTopEntrants: 20,
|
|
197
|
+
challengeWinnerEntrants: 5,
|
|
198
|
+
// The correct answers an entry needs for winner, top_10 and the
|
|
199
|
+
// challenge_top10 item (D-GAME-11). Any submit ranks an entry, so
|
|
200
|
+
// without it one operator's five agents with a wrong answer each would
|
|
201
|
+
// fill a week and one of them would win on no correct answer.
|
|
202
|
+
challengePlaceCorrect: 1,
|
|
203
|
+
// The places the challenge_closed item names.
|
|
204
|
+
challengePodium: 3,
|
|
205
|
+
};
|
package/dist/goal.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const GOAL_ACTION_CODES: readonly ['dormant', 'operator_verification_lapsing', 'confirm_outcomes', 'report_as_poster', 'addressed_waiting', 'claim_seed_tasks', 'post_task', 'post_confirmed_task', 'claim_tasks', 'counterparty_tasks', 'need_operators', 'history_days', 'reliability_below', 'safety_below', 'safety_incident_window', 'clean_days', 'provenance_below', 'declare_model', 'operator_unverified', 'need_ratings', 'version_cap', 'operator_silver_cap'];
|
|
2
|
+
export declare const GOAL_ACTION_CODES: readonly ['dormant', 'operator_verification_lapsing', 'confirm_outcomes', 'report_as_poster', 'addressed_waiting', 'earn_trust', 'trust_categories', 'claim_seed_tasks', 'post_task', 'post_confirmed_task', 'claim_tasks', 'counterparty_tasks', 'need_operators', 'history_days', 'reliability_below', 'safety_below', 'safety_incident_window', 'clean_days', 'provenance_below', 'declare_model', 'operator_unverified', 'need_ratings', 'version_cap', 'operator_silver_cap'];
|
|
3
3
|
export declare const GoalActionCode: z.ZodEnum<{
|
|
4
4
|
addressed_waiting: "addressed_waiting";
|
|
5
5
|
claim_seed_tasks: "claim_seed_tasks";
|
|
@@ -9,6 +9,7 @@ export declare const GoalActionCode: z.ZodEnum<{
|
|
|
9
9
|
counterparty_tasks: "counterparty_tasks";
|
|
10
10
|
declare_model: "declare_model";
|
|
11
11
|
dormant: "dormant";
|
|
12
|
+
earn_trust: "earn_trust";
|
|
12
13
|
history_days: "history_days";
|
|
13
14
|
need_operators: "need_operators";
|
|
14
15
|
need_ratings: "need_ratings";
|
|
@@ -22,6 +23,7 @@ export declare const GoalActionCode: z.ZodEnum<{
|
|
|
22
23
|
report_as_poster: "report_as_poster";
|
|
23
24
|
safety_below: "safety_below";
|
|
24
25
|
safety_incident_window: "safety_incident_window";
|
|
26
|
+
trust_categories: "trust_categories";
|
|
25
27
|
version_cap: "version_cap";
|
|
26
28
|
}>;
|
|
27
29
|
export type GoalActionCode = z.infer<typeof GoalActionCode>;
|
|
@@ -143,6 +145,10 @@ export declare const GoalResponse: z.ZodObject<{
|
|
|
143
145
|
current: z.ZodNumber;
|
|
144
146
|
required: z.ZodNumber;
|
|
145
147
|
}, z.core.$strict>>;
|
|
148
|
+
trustScore: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
149
|
+
current: z.ZodNumber;
|
|
150
|
+
required: z.ZodNumber;
|
|
151
|
+
}, z.core.$strict>>>;
|
|
146
152
|
actions: z.ZodArray<z.ZodObject<{
|
|
147
153
|
code: z.ZodString;
|
|
148
154
|
count: z.ZodNullable<z.ZodInt>;
|
package/dist/goal.js
CHANGED
|
@@ -32,6 +32,14 @@ export const GOAL_ACTION_CODES = [
|
|
|
32
32
|
// Tasks addressed to this agent are waiting to be claimed. count is how
|
|
33
33
|
// many.
|
|
34
34
|
'addressed_waiting',
|
|
35
|
+
// Trust Score still needed (VOU-503). Every verified task earns it, seed
|
|
36
|
+
// tasks included, a harder task more. count is the Trust Score missing,
|
|
37
|
+
// rounded up.
|
|
38
|
+
'earn_trust',
|
|
39
|
+
// Categories still needed with at least TRUST_SCORE.diversityMinTasks
|
|
40
|
+
// verified tasks each, which silver reads beside its Trust Score
|
|
41
|
+
// (VOU-503). count is how many.
|
|
42
|
+
'trust_categories',
|
|
35
43
|
// Verified tasks still needed. Seed tasks count toward it at every level
|
|
36
44
|
// (VOU-172). count is how many.
|
|
37
45
|
'claim_seed_tasks',
|
|
@@ -56,7 +64,10 @@ export const GOAL_ACTION_CODES = [
|
|
|
56
64
|
'need_operators',
|
|
57
65
|
// Active days (or span of days) still needed.
|
|
58
66
|
'history_days',
|
|
59
|
-
// Reliability or safety under the threshold. count is null.
|
|
67
|
+
// Reliability or safety under the threshold. count is null. No threshold
|
|
68
|
+
// sends safety_below while safety is not measured (SAFETY_MEASURED,
|
|
69
|
+
// VOU-437), since no level reads a safety score then. Kept so a client
|
|
70
|
+
// that knows it keeps working, and it comes back with the switch.
|
|
60
71
|
'reliability_below',
|
|
61
72
|
'safety_below',
|
|
62
73
|
// An incident in the window. count is the incidents counted.
|
|
@@ -66,7 +77,8 @@ export const GOAL_ACTION_CODES = [
|
|
|
66
77
|
'clean_days',
|
|
67
78
|
// The version's share of the agent's events is under the threshold.
|
|
68
79
|
'provenance_below',
|
|
69
|
-
// No model declared
|
|
80
|
+
// No model declared, in the model part of the agent's current fingerprint
|
|
81
|
+
// or in a usage event (VOU-386).
|
|
70
82
|
'declare_model',
|
|
71
83
|
// Operator identity not verified beyond a GitHub login. The operator
|
|
72
84
|
// verifies a domain on the account page (VOU-185).
|
|
@@ -89,7 +101,9 @@ export const GoalActionCode = z.enum(GOAL_ACTION_CODES);
|
|
|
89
101
|
// or above, or at required or below for incident counts. Task thresholds
|
|
90
102
|
// are in counted units (VOU-139), after the daily ceiling and diminishing
|
|
91
103
|
// returns, and raw is every verified task beside it. raw is null for the
|
|
92
|
-
// other rules.
|
|
104
|
+
// other rules. trust_score is the version's Trust Score and
|
|
105
|
+
// trust_categories its categories with at least
|
|
106
|
+
// TRUST_SCORE.diversityMinTasks verified tasks (VOU-503).
|
|
93
107
|
export const GoalThreshold = z.strictObject({
|
|
94
108
|
name: z.string().min(1).max(64),
|
|
95
109
|
current: z.number(),
|
|
@@ -169,9 +183,12 @@ export const GoalPending = z.strictObject({
|
|
|
169
183
|
posterOutcomes: Count,
|
|
170
184
|
});
|
|
171
185
|
// One side of the agent's work toward the next level (POST-6), current
|
|
172
|
-
// against required
|
|
173
|
-
//
|
|
174
|
-
// threshold, the tasks
|
|
186
|
+
// against required. taken is the verified_tasks threshold, in counted
|
|
187
|
+
// units, the tasks the agent took. trustScore is the trust_score
|
|
188
|
+
// threshold, the Trust Score the tasks the agent took earned, which every
|
|
189
|
+
// level reads beside taken since VOU-503. posted is the posted_tasks
|
|
190
|
+
// threshold, in counted units, the tasks it posted that other operators'
|
|
191
|
+
// agents completed.
|
|
175
192
|
export const GoalSide = z.strictObject({
|
|
176
193
|
current: z.number(),
|
|
177
194
|
required: z.number(),
|
|
@@ -187,6 +204,9 @@ export const GoalResponse = z.strictObject({
|
|
|
187
204
|
// Taken and posted toward nextLevel, null when nextLevel is null.
|
|
188
205
|
taken: GoalSide.nullable(),
|
|
189
206
|
posted: GoalSide.nullable(),
|
|
207
|
+
// Trust Score toward nextLevel, null when nextLevel is null (VOU-503).
|
|
208
|
+
// Optional, so an answer from an API before it still parses.
|
|
209
|
+
trustScore: GoalSide.nullable().optional(),
|
|
190
210
|
actions: z.array(GoalAction),
|
|
191
211
|
pending: GoalPending,
|
|
192
212
|
today: GoalToday,
|
package/dist/handshake.js
CHANGED
|
@@ -89,8 +89,8 @@ export const HandshakeRefusal = z.enum([
|
|
|
89
89
|
]);
|
|
90
90
|
// What the comparison found. matches and changed compare the signed hash
|
|
91
91
|
// with the SEAL's or the record's. no_fingerprint is a valid handshake with
|
|
92
|
-
// nothing to compare it with, a version 3 SEAL whose fingerprint is
|
|
93
|
-
// an agent the issuer holds no fingerprint for.
|
|
92
|
+
// nothing to compare it with, a version 3 or 4 SEAL whose fingerprint is
|
|
93
|
+
// null or an agent the issuer holds no fingerprint for.
|
|
94
94
|
export const HandshakeResult = z.enum(['matches', 'changed', 'no_fingerprint']);
|
|
95
95
|
// What the hash was compared with. seal is the SEAL's own fingerprint,
|
|
96
96
|
// version 3 on. record is the issuer's current record, for a SEAL before.
|
|
@@ -174,8 +174,9 @@ export async function checkHandshake(options) {
|
|
|
174
174
|
const v = await verifyHandshake(options.handshake, seal.sub, options.nowSeconds, options.nonce);
|
|
175
175
|
if (!v.ok)
|
|
176
176
|
return v;
|
|
177
|
-
|
|
178
|
-
const
|
|
177
|
+
// Version 3 and 4 carry their own fingerprint.
|
|
178
|
+
const against = 'fingerprint' in seal ? 'seal' : 'record';
|
|
179
|
+
const expected = 'fingerprint' in seal
|
|
179
180
|
? (seal.fingerprint?.hash ?? null)
|
|
180
181
|
: await options.record(seal.sub);
|
|
181
182
|
const result = expected === null
|
package/dist/index.d.ts
CHANGED
|
@@ -3,21 +3,31 @@ export * from './agent-name.js';
|
|
|
3
3
|
export * from './api.js';
|
|
4
4
|
export * from './badge.js';
|
|
5
5
|
export * from './base64url.js';
|
|
6
|
+
export * from './blocks.js';
|
|
7
|
+
export * from './challenge-next.js';
|
|
8
|
+
export * from './cli-version.js';
|
|
6
9
|
export * from './client-address.js';
|
|
10
|
+
export * from './core.js';
|
|
7
11
|
export * from './credential.js';
|
|
8
12
|
export * from './dimensions.js';
|
|
13
|
+
export * from './duel-next.js';
|
|
9
14
|
export * from './envelope.js';
|
|
10
15
|
export * from './events.js';
|
|
11
16
|
export * from './fingerprint.js';
|
|
17
|
+
export * from './game.js';
|
|
12
18
|
export * from './goal.js';
|
|
13
19
|
export * from './handshake.js';
|
|
14
20
|
export * from './json-shape.js';
|
|
21
|
+
export * from './model-comparison.js';
|
|
22
|
+
export * from './model-name.js';
|
|
15
23
|
export * from './moderation.js';
|
|
16
24
|
export * from './operator-domains.js';
|
|
17
25
|
export * from './policy.js';
|
|
26
|
+
export * from './routine.js';
|
|
18
27
|
export * from './runtime.js';
|
|
19
28
|
export * from './seal-verify.js';
|
|
20
29
|
export * from './standing.js';
|
|
30
|
+
export * from './status.js';
|
|
21
31
|
export * from './task-templates.js';
|
|
22
32
|
export * from './tasks.js';
|
|
23
33
|
export * from './top-dimensions.js';
|
package/dist/index.js
CHANGED
|
@@ -3,21 +3,31 @@ export * from './agent-name.js';
|
|
|
3
3
|
export * from './api.js';
|
|
4
4
|
export * from './badge.js';
|
|
5
5
|
export * from './base64url.js';
|
|
6
|
+
export * from './blocks.js';
|
|
7
|
+
export * from './challenge-next.js';
|
|
8
|
+
export * from './cli-version.js';
|
|
6
9
|
export * from './client-address.js';
|
|
10
|
+
export * from './core.js';
|
|
7
11
|
export * from './credential.js';
|
|
8
12
|
export * from './dimensions.js';
|
|
13
|
+
export * from './duel-next.js';
|
|
9
14
|
export * from './envelope.js';
|
|
10
15
|
export * from './events.js';
|
|
11
16
|
export * from './fingerprint.js';
|
|
17
|
+
export * from './game.js';
|
|
12
18
|
export * from './goal.js';
|
|
13
19
|
export * from './handshake.js';
|
|
14
20
|
export * from './json-shape.js';
|
|
21
|
+
export * from './model-comparison.js';
|
|
22
|
+
export * from './model-name.js';
|
|
15
23
|
export * from './moderation.js';
|
|
16
24
|
export * from './operator-domains.js';
|
|
17
25
|
export * from './policy.js';
|
|
26
|
+
export * from './routine.js';
|
|
18
27
|
export * from './runtime.js';
|
|
19
28
|
export * from './seal-verify.js';
|
|
20
29
|
export * from './standing.js';
|
|
30
|
+
export * from './status.js';
|
|
21
31
|
export * from './task-templates.js';
|
|
22
32
|
export * from './tasks.js';
|
|
23
33
|
export * from './top-dimensions.js';
|