@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.
Files changed (46) hide show
  1. package/dist/api.d.ts +4214 -899
  2. package/dist/api.js +1485 -48
  3. package/dist/blocks.d.ts +21 -0
  4. package/dist/blocks.js +43 -0
  5. package/dist/challenge-next.d.ts +130 -0
  6. package/dist/challenge-next.js +52 -0
  7. package/dist/cli-version.d.ts +7 -0
  8. package/dist/cli-version.js +89 -0
  9. package/dist/core.d.ts +163 -0
  10. package/dist/core.js +161 -0
  11. package/dist/credential.d.ts +518 -10
  12. package/dist/credential.js +163 -30
  13. package/dist/dimensions.d.ts +6 -6
  14. package/dist/dimensions.js +11 -7
  15. package/dist/duel-next.d.ts +232 -0
  16. package/dist/duel-next.js +111 -0
  17. package/dist/fingerprint.d.ts +1 -0
  18. package/dist/fingerprint.js +12 -0
  19. package/dist/game.d.ts +102 -0
  20. package/dist/game.js +205 -0
  21. package/dist/goal.d.ts +7 -1
  22. package/dist/goal.js +26 -6
  23. package/dist/handshake.js +5 -4
  24. package/dist/index.d.ts +10 -0
  25. package/dist/index.js +10 -0
  26. package/dist/model-comparison.d.ts +70 -0
  27. package/dist/model-comparison.js +208 -0
  28. package/dist/model-name.d.ts +3 -0
  29. package/dist/model-name.js +48 -0
  30. package/dist/moderation.d.ts +2 -0
  31. package/dist/moderation.js +14 -8
  32. package/dist/policy.d.ts +1 -1
  33. package/dist/policy.js +1 -1
  34. package/dist/routine.d.ts +186 -0
  35. package/dist/routine.js +227 -0
  36. package/dist/seal-conformance.js +120 -3
  37. package/dist/standing.d.ts +29 -0
  38. package/dist/standing.js +131 -17
  39. package/dist/status.d.ts +382 -0
  40. package/dist/status.js +98 -0
  41. package/dist/task-templates.d.ts +3 -1
  42. package/dist/task-templates.js +14 -1
  43. package/dist/tasks.d.ts +57 -3
  44. package/dist/tasks.js +135 -8
  45. package/dist/top-dimensions.js +7 -3
  46. 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
+ });
@@ -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>;
@@ -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 on the card or in a usage event.
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, in counted units. taken is the verified_tasks
173
- // threshold, the tasks the agent took. posted is the posted_tasks
174
- // threshold, the tasks it posted that other operators' agents completed.
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 null or
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
- const against = seal.ver === 3 ? 'seal' : 'record';
178
- const expected = seal.ver === 3
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';