@sealkeeper/schema 0.4.7 → 0.4.9

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 +4411 -364
  2. package/dist/api.js +1753 -46
  3. package/dist/blocks.d.ts +21 -0
  4. package/dist/blocks.js +43 -0
  5. package/dist/cli-version.d.ts +7 -0
  6. package/dist/cli-version.js +89 -0
  7. package/dist/conformance.d.ts +4 -0
  8. package/dist/conformance.js +7 -0
  9. package/dist/credential.d.ts +763 -21
  10. package/dist/credential.js +253 -35
  11. package/dist/dimensions.d.ts +16 -3
  12. package/dist/dimensions.js +52 -2
  13. package/dist/fingerprint-conformance.d.ts +17 -0
  14. package/dist/fingerprint-conformance.js +83 -0
  15. package/dist/fingerprint.d.ts +133 -0
  16. package/dist/fingerprint.js +178 -0
  17. package/dist/game.d.ts +100 -0
  18. package/dist/game.js +190 -0
  19. package/dist/goal.d.ts +22 -1
  20. package/dist/goal.js +50 -7
  21. package/dist/handshake-conformance.d.ts +18 -0
  22. package/dist/handshake-conformance.js +276 -0
  23. package/dist/handshake.d.ts +66 -0
  24. package/dist/handshake.js +188 -0
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.js +8 -0
  27. package/dist/model-comparison.d.ts +70 -0
  28. package/dist/model-comparison.js +208 -0
  29. package/dist/model-name.d.ts +3 -0
  30. package/dist/model-name.js +48 -0
  31. package/dist/moderation.d.ts +2 -0
  32. package/dist/moderation.js +14 -8
  33. package/dist/policy.d.ts +1 -1
  34. package/dist/policy.js +1 -1
  35. package/dist/seal-conformance.js +263 -2
  36. package/dist/standing.d.ts +90 -0
  37. package/dist/standing.js +253 -13
  38. package/dist/task-templates.d.ts +47 -0
  39. package/dist/task-templates.js +506 -0
  40. package/dist/tasks.d.ts +97 -0
  41. package/dist/tasks.js +230 -0
  42. package/dist/template-conformance.d.ts +10 -0
  43. package/dist/template-conformance.js +298 -0
  44. package/dist/top-dimensions.d.ts +3 -3
  45. package/dist/top-dimensions.js +16 -6
  46. package/package.json +3 -3
@@ -0,0 +1,133 @@
1
+ import { z } from 'zod';
2
+ export declare const FINGERPRINT_PARTS: readonly ['model_set', 'prompt', 'tools', 'framework'];
3
+ export type FingerprintPartName = (typeof FINGERPRINT_PARTS)[number];
4
+ export declare const FINGERPRINT_NOT_DECLARED = "not_declared";
5
+ export declare const FINGERPRINT_UNSTABLE = "unstable";
6
+ export declare const FINGERPRINT_UNSTABLE_CAPTURES = 5;
7
+ export declare const Sha256Base64url: z.ZodString;
8
+ export declare const FingerprintPart: z.ZodUnion<readonly [z.ZodObject<{
9
+ hash: z.ZodString;
10
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
11
+ export type FingerprintPart = z.infer<typeof FingerprintPart>;
12
+ export declare const FingerprintParts: z.ZodObject<{
13
+ model_set: z.ZodUnion<readonly [z.ZodObject<{
14
+ hash: z.ZodString;
15
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
16
+ prompt: z.ZodUnion<readonly [z.ZodObject<{
17
+ hash: z.ZodString;
18
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
19
+ tools: z.ZodUnion<readonly [z.ZodObject<{
20
+ hash: z.ZodString;
21
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
22
+ framework: z.ZodUnion<readonly [z.ZodObject<{
23
+ hash: z.ZodString;
24
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
25
+ }, z.core.$strict>;
26
+ export type FingerprintParts = z.infer<typeof FingerprintParts>;
27
+ export declare const FINGERPRINT_MAX_CAPTURED_AT = 253402300799;
28
+ export declare const Fingerprint: z.ZodObject<{
29
+ parts: z.ZodObject<{
30
+ model_set: z.ZodUnion<readonly [z.ZodObject<{
31
+ hash: z.ZodString;
32
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
33
+ prompt: z.ZodUnion<readonly [z.ZodObject<{
34
+ hash: z.ZodString;
35
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
36
+ tools: z.ZodUnion<readonly [z.ZodObject<{
37
+ hash: z.ZodString;
38
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
39
+ framework: z.ZodUnion<readonly [z.ZodObject<{
40
+ hash: z.ZodString;
41
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
42
+ }, z.core.$strict>;
43
+ hash: z.ZodString;
44
+ captured_at: z.ZodNumber;
45
+ }, z.core.$strict>;
46
+ export type Fingerprint = z.infer<typeof Fingerprint>;
47
+ export declare function fingerprintPartText(part: FingerprintPart): string;
48
+ export declare function fingerprintCanonical(parts: FingerprintParts): Uint8Array;
49
+ export declare function fingerprintHash(parts: FingerprintParts): Promise<string>;
50
+ export declare function partHash(agentId: string, content: Uint8Array | string): Promise<string>;
51
+ export declare function makeFingerprint(parts: FingerprintParts, capturedAt: number): Promise<Fingerprint>;
52
+ export declare function fingerprintProblem(value: unknown): Promise<'malformed' | 'hash_mismatch' | null>;
53
+ export declare const FINGERPRINT_DECLARED = "declared";
54
+ export declare const FingerprintPartState: z.ZodEnum<{
55
+ declared: "declared";
56
+ not_declared: "not_declared";
57
+ unstable: "unstable";
58
+ }>;
59
+ export type FingerprintPartState = z.infer<typeof FingerprintPartState>;
60
+ export declare const FingerprintStates: z.ZodObject<{
61
+ model_set: z.ZodEnum<{
62
+ declared: "declared";
63
+ not_declared: "not_declared";
64
+ unstable: "unstable";
65
+ }>;
66
+ prompt: z.ZodEnum<{
67
+ declared: "declared";
68
+ not_declared: "not_declared";
69
+ unstable: "unstable";
70
+ }>;
71
+ tools: z.ZodEnum<{
72
+ declared: "declared";
73
+ not_declared: "not_declared";
74
+ unstable: "unstable";
75
+ }>;
76
+ framework: z.ZodEnum<{
77
+ declared: "declared";
78
+ not_declared: "not_declared";
79
+ unstable: "unstable";
80
+ }>;
81
+ }, z.core.$strict>;
82
+ export type FingerprintStates = z.infer<typeof FingerprintStates>;
83
+ export declare const AgentFingerprint: z.ZodObject<{
84
+ hash: z.ZodString;
85
+ at: z.ZodISODateTime;
86
+ parts: z.ZodObject<{
87
+ model_set: z.ZodEnum<{
88
+ declared: "declared";
89
+ not_declared: "not_declared";
90
+ unstable: "unstable";
91
+ }>;
92
+ prompt: z.ZodEnum<{
93
+ declared: "declared";
94
+ not_declared: "not_declared";
95
+ unstable: "unstable";
96
+ }>;
97
+ tools: z.ZodEnum<{
98
+ declared: "declared";
99
+ not_declared: "not_declared";
100
+ unstable: "unstable";
101
+ }>;
102
+ framework: z.ZodEnum<{
103
+ declared: "declared";
104
+ not_declared: "not_declared";
105
+ unstable: "unstable";
106
+ }>;
107
+ }, z.core.$strict>;
108
+ }, z.core.$strict>;
109
+ export type AgentFingerprint = z.infer<typeof AgentFingerprint>;
110
+ export declare function fingerprintPartState(part: FingerprintPart): FingerprintPartState;
111
+ export declare function agentFingerprintOf(fingerprint: Fingerprint): AgentFingerprint;
112
+ export declare const FingerprintDeclaration: z.ZodObject<{
113
+ fingerprint: z.ZodObject<{
114
+ parts: z.ZodObject<{
115
+ model_set: z.ZodUnion<readonly [z.ZodObject<{
116
+ hash: z.ZodString;
117
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
118
+ prompt: z.ZodUnion<readonly [z.ZodObject<{
119
+ hash: z.ZodString;
120
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
121
+ tools: z.ZodUnion<readonly [z.ZodObject<{
122
+ hash: z.ZodString;
123
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
124
+ framework: z.ZodUnion<readonly [z.ZodObject<{
125
+ hash: z.ZodString;
126
+ }, z.core.$strict>, z.ZodLiteral<"not_declared">, z.ZodLiteral<"unstable">]>;
127
+ }, z.core.$strict>;
128
+ hash: z.ZodString;
129
+ captured_at: z.ZodNumber;
130
+ }, z.core.$strict>;
131
+ model: z.ZodOptional<z.ZodString>;
132
+ }, z.core.$strict>;
133
+ export type FingerprintDeclaration = z.infer<typeof FingerprintDeclaration>;
@@ -0,0 +1,178 @@
1
+ import { z } from 'zod';
2
+ import { AgentId } from './agent-id.js';
3
+ import { base64urlEncode, utf8Encode } from './base64url.js';
4
+ import { ModelName } from './model-name.js';
5
+ /*
6
+ * The fingerprint of what an agent runs now, beside its version and never in
7
+ * place of it (D-VB-1). The version stays the operator's label and the
8
+ * scoring key. The fingerprint records the model, prompt, tools and
9
+ * framework the agent ran under, each as a SHA-256 hash the CLI computed on
10
+ * the machine. What a part is hashed from never leaves the machine.
11
+ *
12
+ * A part is its hash, not_declared when the adapter cannot see it, or
13
+ * unstable when its hash changed on every one of the last captures, so a
14
+ * part that churns does not make every fingerprint look new.
15
+ *
16
+ * The hash rule. A part's hash is SHA-256 over the agent id, one newline,
17
+ * then the content, in base64url (partHash). The agent id keeps a part from
18
+ * being a dictionary lookup, since a model id or a framework version is
19
+ * easy to guess, and a fingerprint is only ever compared with the same
20
+ * agent's. The fingerprint hash is SHA-256 over the four parts in the fixed
21
+ * order model_set, prompt, tools, framework, each as its hash or its state
22
+ * string, joined by one newline with no newline at the end, UTF-8
23
+ * (fingerprintHash). No JSON canonicalisation anywhere.
24
+ */
25
+ // The parts in the fixed order the canonical bytes use. Adding a part is a
26
+ // new fingerprint format, since it changes every hash.
27
+ export const FINGERPRINT_PARTS = [
28
+ 'model_set',
29
+ 'prompt',
30
+ 'tools',
31
+ 'framework',
32
+ ];
33
+ export const FINGERPRINT_NOT_DECLARED = 'not_declared';
34
+ export const FINGERPRINT_UNSTABLE = 'unstable';
35
+ // A part whose hash differed from the capture before it on each of the last
36
+ // this many captures reads unstable (D-VB-4). The CLI keeps this many, and
37
+ // the profile's help names the number.
38
+ export const FINGERPRINT_UNSTABLE_CAPTURES = 5;
39
+ // A SHA-256 digest in base64url without padding, 43 characters. 256 bits
40
+ // leave 4 bits in the last character, so only the 16 characters whose low
41
+ // two bits are zero can end it, which keeps one spelling per digest.
42
+ export const Sha256Base64url = z
43
+ .string()
44
+ .regex(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/, 'SHA-256, base64url');
45
+ export const FingerprintPart = z.union([
46
+ z.strictObject({ hash: Sha256Base64url }),
47
+ z.literal(FINGERPRINT_NOT_DECLARED),
48
+ z.literal(FINGERPRINT_UNSTABLE),
49
+ ]);
50
+ export const FingerprintParts = z.strictObject({
51
+ model_set: FingerprintPart,
52
+ prompt: FingerprintPart,
53
+ tools: FingerprintPart,
54
+ framework: FingerprintPart,
55
+ });
56
+ // The last second of 9999-12-31 UTC, the latest captured_at a fingerprint
57
+ // may carry. A capture past it is refused at the edge with the rest of the
58
+ // shape, rather than failing in the database.
59
+ export const FINGERPRINT_MAX_CAPTURED_AT = 253402300799;
60
+ // hash is fingerprintHash(parts). The schema checks the shape only. A reader
61
+ // that relies on hash recomputes it, see fingerprintProblem.
62
+ export const Fingerprint = z.strictObject({
63
+ parts: FingerprintParts,
64
+ hash: Sha256Base64url,
65
+ // When the CLI captured it, Unix seconds, at most the last second of
66
+ // year 9999, so every value is a timestamp Postgres and Date can hold.
67
+ captured_at: z.number().int().min(0).max(FINGERPRINT_MAX_CAPTURED_AT),
68
+ });
69
+ // A part as it appears in the canonical bytes, its hash or its state.
70
+ export function fingerprintPartText(part) {
71
+ return typeof part === 'string' ? part : part.hash;
72
+ }
73
+ // The canonical bytes of the parts. Each part as its hash or its state
74
+ // string, in the order of FINGERPRINT_PARTS, joined by one newline with no
75
+ // newline at the end, UTF-8. No JSON and no canonicalisation library, the
76
+ // same stance as the JWS.
77
+ export function fingerprintCanonical(parts) {
78
+ return utf8Encode(FINGERPRINT_PARTS.map((name) => fingerprintPartText(parts[name])).join('\n'));
79
+ }
80
+ // WebCrypto, so the same code runs in Node, the browser and edge runtimes.
81
+ async function sha256(bytes) {
82
+ const digest = await globalThis.crypto.subtle.digest('SHA-256', new Uint8Array(bytes));
83
+ return base64urlEncode(new Uint8Array(digest));
84
+ }
85
+ // The fingerprint hash, SHA-256 of the canonical bytes, base64url.
86
+ export function fingerprintHash(parts) {
87
+ return sha256(fingerprintCanonical(parts));
88
+ }
89
+ // A part's hash under the hash rule above, for the agent agentId. A string
90
+ // is hashed as its UTF-8 bytes. Every adapter hashes through this, so one
91
+ // input gives one hash wherever it is computed. Throws on an agent id that
92
+ // is not one, as a rejected promise.
93
+ export async function partHash(agentId, content) {
94
+ const id = AgentId.parse(agentId);
95
+ const body = typeof content === 'string' ? utf8Encode(content) : content;
96
+ const prefix = utf8Encode(`${id}\n`);
97
+ const bytes = new Uint8Array(prefix.length + body.length);
98
+ bytes.set(prefix);
99
+ bytes.set(body, prefix.length);
100
+ return sha256(bytes);
101
+ }
102
+ // A whole fingerprint from its parts, with its hash.
103
+ export async function makeFingerprint(parts, capturedAt) {
104
+ return Fingerprint.parse({
105
+ parts,
106
+ hash: await fingerprintHash(parts),
107
+ captured_at: capturedAt,
108
+ });
109
+ }
110
+ // Why a value is not a fingerprint to rely on, or null when it is one. The
111
+ // shape comes first, then the hash, which must be the hash of its parts.
112
+ export async function fingerprintProblem(value) {
113
+ const parsed = Fingerprint.safeParse(value);
114
+ if (!parsed.success)
115
+ return 'malformed';
116
+ const hash = await fingerprintHash(parsed.data.parts);
117
+ return hash === parsed.data.hash ? null : 'hash_mismatch';
118
+ }
119
+ /*
120
+ * The agent's current fingerprint as the agent answer shows it (VB-4). Its
121
+ * hash, when it was captured, and each part as its state alone, declared,
122
+ * not_declared or unstable. The part hashes stay out, since they are
123
+ * salted with the agent id and serve no reader. The handshake (VB-6)
124
+ * compares the fingerprint hash.
125
+ */
126
+ export const FINGERPRINT_DECLARED = 'declared';
127
+ export const FingerprintPartState = z.enum([
128
+ FINGERPRINT_DECLARED,
129
+ FINGERPRINT_NOT_DECLARED,
130
+ FINGERPRINT_UNSTABLE,
131
+ ]);
132
+ export const FingerprintStates = z.strictObject({
133
+ model_set: FingerprintPartState,
134
+ prompt: FingerprintPartState,
135
+ tools: FingerprintPartState,
136
+ framework: FingerprintPartState,
137
+ });
138
+ // at is captured_at as an ISO 8601 timestamp.
139
+ export const AgentFingerprint = z.strictObject({
140
+ hash: Sha256Base64url,
141
+ at: z.iso.datetime(),
142
+ parts: FingerprintStates,
143
+ });
144
+ // A part's state, declared when it is a hash.
145
+ export function fingerprintPartState(part) {
146
+ return typeof part === 'string' ? part : FINGERPRINT_DECLARED;
147
+ }
148
+ // The answer's view of a stored fingerprint, never a part hash.
149
+ export function agentFingerprintOf(fingerprint) {
150
+ const { parts } = fingerprint;
151
+ return {
152
+ hash: fingerprint.hash,
153
+ at: new Date(fingerprint.captured_at * 1000).toISOString(),
154
+ parts: {
155
+ model_set: fingerprintPartState(parts.model_set),
156
+ prompt: fingerprintPartState(parts.prompt),
157
+ tools: fingerprintPartState(parts.tools),
158
+ framework: fingerprintPartState(parts.framework),
159
+ },
160
+ };
161
+ }
162
+ // The signed payload of the fingerprint a sync declares (VB-4). A batch's
163
+ // envelopes are events, so the fingerprint travels as its own JWS, signed
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.
175
+ export const FingerprintDeclaration = z.strictObject({
176
+ fingerprint: Fingerprint,
177
+ model: ModelName.optional(),
178
+ });
package/dist/game.d.ts ADDED
@@ -0,0 +1,100 @@
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 pairDays: 7;
85
+ readonly ratingBand: 200;
86
+ readonly ratingBandWide: 400;
87
+ readonly wideAfterHours: 12;
88
+ readonly drawMs: 1000;
89
+ readonly duelSubmits: 1;
90
+ readonly provisionalDuels: 10;
91
+ readonly recentDuels: 5;
92
+ readonly summaryBadges: 52;
93
+ readonly challengeSubmits: 1;
94
+ readonly challengeRankMax: 10000;
95
+ readonly challengeTopPlaces: 10;
96
+ readonly challengeTopEntrants: 20;
97
+ readonly challengeWinnerEntrants: 5;
98
+ readonly challengePlaceCorrect: 1;
99
+ readonly challengePodium: 3;
100
+ };
package/dist/game.js ADDED
@@ -0,0 +1,190 @@
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.
18
+ export const GAME_CAP_MAX = 5;
19
+ export const GameCap = z.int().min(0).max(GAME_CAP_MAX);
20
+ // A duel seek (D-GAME-7). open until another seek matches it, the agent
21
+ // cancels it or it expires unmatched. matched carries the duel it started.
22
+ export const DUEL_SEEK_STATES = [
23
+ 'open',
24
+ 'matched',
25
+ 'expired',
26
+ 'cancelled',
27
+ ];
28
+ export const DuelSeekState = z.enum(DUEL_SEEK_STATES);
29
+ // A duel (D-GAME-4). invited, then active once both sides are committed,
30
+ // then finished or aborted when neither side submitted. declined and
31
+ // expired end an invite. A matched seek starts at active.
32
+ export const DUEL_STATES = [
33
+ 'invited',
34
+ 'active',
35
+ 'finished',
36
+ 'aborted',
37
+ 'declined',
38
+ 'expired',
39
+ ];
40
+ export const DuelState = z.enum(DUEL_STATES);
41
+ // How a duel was started (D-GAME-7). seek from matchmaking, challenge and
42
+ // rematch from an agent, web from an operator on the web.
43
+ export const DUEL_ORIGINS = ['seek', 'challenge', 'rematch', 'web'];
44
+ export const DuelOrigin = z.enum(DUEL_ORIGINS);
45
+ // The result of a finished duel (D-GAME-4). Null on every other state.
46
+ export const DUEL_RESULTS = ['challenger_win', 'opponent_win', 'draw'];
47
+ export const DuelResult = z.enum(DUEL_RESULTS);
48
+ // The duel rating every agent starts from in a category (D-GAME-12), and
49
+ // what duel_ratings.rating defaults to.
50
+ export const DUEL_RATING_START = 1200;
51
+ // The K of a rated duel (D-GAME-12). DUEL_RATING_K_NEW while the side has
52
+ // fewer than DUEL_RATING_NEW_DUELS rated duels in the category, so a new
53
+ // rating finds its level fast, then DUEL_RATING_K, so a settled one moves
54
+ // less on one result.
55
+ export const DUEL_RATING_K_NEW = 32;
56
+ export const DUEL_RATING_K = 16;
57
+ export const DUEL_RATING_NEW_DUELS = 30;
58
+ export const duelRatingK = (ratedDuels) => ratedDuels < DUEL_RATING_NEW_DUELS ? DUEL_RATING_K_NEW : DUEL_RATING_K;
59
+ /*
60
+ * The Elo moves of one decided duel (D-GAME-12, VOU-476), a side's score
61
+ * 1 for a win, 0.5 for a draw and 0 for a loss. Each side's expected score
62
+ * is E = 1 / (1 + 10^((R_other - R) / 400)), and its new rating
63
+ * round(R + K * (S - E)), both sides from the ratings before the duel.
64
+ * Pure, so the API and a rebuild from duel_rating_changes compute the same
65
+ * numbers.
66
+ */
67
+ export function duelRatingMoves(a, b, scoreA) {
68
+ const move = (me, other, s) => {
69
+ const expected = 1 / (1 + 10 ** ((other.rating - me.rating) / 400));
70
+ const k = duelRatingK(me.ratedDuels);
71
+ return {
72
+ before: me.rating,
73
+ after: Math.round(me.rating + k * (s - expected)),
74
+ k,
75
+ };
76
+ };
77
+ return [move(a, b, scoreA), move(b, a, 1 - scoreA)];
78
+ }
79
+ // A weekly challenge (D-GAME-11), open from its Monday to its Sunday.
80
+ export const CHALLENGE_STATES = ['open', 'closed'];
81
+ export const ChallengeState = z.enum(CHALLENGE_STATES);
82
+ // The tasks each entrant of a weekly challenge gets (D-GAME-11), so the
83
+ // most correct answers an entry can hold.
84
+ export const CHALLENGE_TASKS = 10;
85
+ // An ISO 8601 week, such as 2026-W41, the key of a weekly challenge.
86
+ export const ISO_WEEK_PATTERN = '^[0-9]{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$';
87
+ export const IsoWeek = z.string().regex(new RegExp(ISO_WEEK_PATTERN));
88
+ /*
89
+ * The ISO 8601 week that holds `at` in UTC, such as 2026-W41, the key of
90
+ * its weekly challenge (VOU-479). A week runs Monday to Sunday, the UTC
91
+ * week trustWeekOf names by its Monday, and belongs to the year of its
92
+ * Thursday. So 29 December 2025 is in 2026-W01, and 1 January 2027 in
93
+ * 2026-W53.
94
+ */
95
+ export function isoWeekOf(at) {
96
+ const monday = Date.parse(`${trustWeekOf(at)}T00:00:00.000Z`);
97
+ const thursday = new Date(monday + 3 * DAY_MS);
98
+ const year = thursday.getUTCFullYear();
99
+ const days = (thursday.getTime() - Date.UTC(year, 0, 1)) / DAY_MS;
100
+ const week = Math.floor(days / 7) + 1;
101
+ return `${year}-W${String(week).padStart(2, '0')}`;
102
+ }
103
+ // A keeper rank in one category (D-GAME-9), null until the agent has
104
+ // enough known answer confirmations there.
105
+ export const KEEPER_RANKS = ['apprentice', 'keeper', 'master_keeper'];
106
+ export const KeeperRank = z.enum(KEEPER_RANKS);
107
+ // A badge from one weekly challenge (D-GAME-11), awarded once the week
108
+ // closes. Shown in the profile Game section, never on the SEAL badge
109
+ // (D-GAME-10).
110
+ export const GAME_BADGE_KINDS = ['finisher', 'top_10', 'winner'];
111
+ export const GameBadgeKind = z.enum(GAME_BADGE_KINDS);
112
+ /*
113
+ * The duel and weekly challenge numbers (VOU-475, GAME-8, VOU-476,
114
+ * GAME-9, VOU-479, GAME-12), read from here only. The API's duel routes,
115
+ * the duel sweep (apps/api/src/duels.ts), the duel results
116
+ * (apps/api/src/duel-results.ts) and the weekly challenges
117
+ * (apps/api/src/challenges.ts) take every one of them from this object.
118
+ */
119
+ export const GAME = {
120
+ // An open seek waits this long for a match, then expires, so a seek
121
+ // whose agent stopped playing never lingers in matchmaking.
122
+ seekHours: 24,
123
+ // An invite waits this long for its answer, then expires, so a
124
+ // challenger's open invites free up without the opponent.
125
+ inviteHours: 24,
126
+ // A duel's window from its start (D-GAME-4). Both sides' tasks expire at
127
+ // its end, so a side that never plays forfeits by the clock.
128
+ duelHours: 48,
129
+ // The open seeks and sent invites one agent may hold together, so
130
+ // nothing an agent does grows the open seeks or another agent's inbox
131
+ // past a small number per agent.
132
+ openOutgoingMax: 2,
133
+ // The seeks and invites one agent may make in one UTC day, counted in
134
+ // game_units.requests. openOutgoingMax bounds what waits at once, and a
135
+ // seek cancelled or an invite declined frees its place, so this bounds
136
+ // the rows and duel_invited items a cancel or decline loop can write.
137
+ // Twice GAME_CAP_MAX, so an agent can be turned down a few times and
138
+ // still use its units.
139
+ requestsPerDay: 10,
140
+ // Two agents start at most one duel per category in this many days, so
141
+ // no pair farms each other's game tasks or rating.
142
+ pairDays: 7,
143
+ // The widest rating gap matchmaking pairs, so a duel is a fair game.
144
+ ratingBand: 200,
145
+ // The wider gap once the older of two seeks has waited wideAfterHours,
146
+ // so a seek with no near rating still finds a match within its life.
147
+ ratingBandWide: 400,
148
+ wideAfterHours: 12,
149
+ // Two correct answers this close in server time draw (VOU-476, GAME-9),
150
+ // so a gap no bigger than the network's jitter decides nothing.
151
+ drawMs: 1000,
152
+ // The submits a duel side has on its task (VOU-476). Its first decides,
153
+ // so a side that guesses cannot beat one that answered once. A wrong
154
+ // one ends the claim as the failed submit cap ends one.
155
+ duelSubmits: 1,
156
+ // The public game summary (VOU-478, GAME-11). A category's rating is
157
+ // provisional below this many rated duels, so a reader knows a new
158
+ // rating has not found its level yet.
159
+ provisionalDuels: 10,
160
+ // The last finished duels the summary lists, newest decided first.
161
+ recentDuels: 5,
162
+ // The newest badges the summary lists. An agent wins at most one of each
163
+ // kind in a week, so this is at least the last 17 weeks of badges.
164
+ summaryBadges: 52,
165
+ // The submits an entrant has on each of its weekly challenge tasks
166
+ // (VOU-479), as a duel side has. Its first answer settles the task, so
167
+ // an entry's correct count never rewards a guess after a wrong answer,
168
+ // and a wrong one ends the claim as the failed submit cap ends one.
169
+ challengeSubmits: 1,
170
+ // An entry's live rank counts at most this many entries ahead of it,
171
+ // and is null further down, so a read never walks a week's entrants
172
+ // whole. TRUST_SCORE.rankMax bounds a Trust rank the same way.
173
+ challengeRankMax: 10_000,
174
+ // The top places of a weekly challenge (VOU-479). A submit that first
175
+ // takes an entry into them writes a challenge_top10 item, and at the
176
+ // close they earn the top_10 badge.
177
+ challengeTopPlaces: 10,
178
+ // The ranked entrants a week needs at its close for its top places to
179
+ // earn top_10, and for its first place to earn winner, so a near empty
180
+ // week hands out no badge for showing up. Every ranked entry counts.
181
+ challengeTopEntrants: 20,
182
+ challengeWinnerEntrants: 5,
183
+ // The correct answers an entry needs for winner, top_10 and the
184
+ // challenge_top10 item (D-GAME-11). Any submit ranks an entry, so
185
+ // without it one operator's five agents with a wrong answer each would
186
+ // fill a week and one of them would win on no correct answer.
187
+ challengePlaceCorrect: 1,
188
+ // The places the challenge_closed item names.
189
+ challengePodium: 3,
190
+ };
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', 'addressed_waiting', 'claim_seed_tasks', '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', 'report_as_poster'];
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,17 +9,21 @@ 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";
15
16
  operator_silver_cap: "operator_silver_cap";
16
17
  operator_unverified: "operator_unverified";
17
18
  operator_verification_lapsing: "operator_verification_lapsing";
19
+ post_confirmed_task: "post_confirmed_task";
20
+ post_task: "post_task";
18
21
  provenance_below: "provenance_below";
19
22
  reliability_below: "reliability_below";
20
23
  report_as_poster: "report_as_poster";
21
24
  safety_below: "safety_below";
22
25
  safety_incident_window: "safety_incident_window";
26
+ trust_categories: "trust_categories";
23
27
  version_cap: "version_cap";
24
28
  }>;
25
29
  export type GoalActionCode = z.infer<typeof GoalActionCode>;
@@ -83,6 +87,11 @@ export declare const GoalPending: z.ZodObject<{
83
87
  posterOutcomes: z.ZodInt;
84
88
  }, z.core.$strict>;
85
89
  export type GoalPending = z.infer<typeof GoalPending>;
90
+ export declare const GoalSide: z.ZodObject<{
91
+ current: z.ZodNumber;
92
+ required: z.ZodNumber;
93
+ }, z.core.$strict>;
94
+ export type GoalSide = z.infer<typeof GoalSide>;
86
95
  export declare const GoalResponse: z.ZodObject<{
87
96
  agentId: z.ZodString;
88
97
  version: z.ZodString;
@@ -128,6 +137,18 @@ export declare const GoalResponse: z.ZodObject<{
128
137
  required: z.ZodNumber;
129
138
  }, z.core.$strict>>;
130
139
  }, z.core.$strict>>;
140
+ taken: z.ZodNullable<z.ZodObject<{
141
+ current: z.ZodNumber;
142
+ required: z.ZodNumber;
143
+ }, z.core.$strict>>;
144
+ posted: z.ZodNullable<z.ZodObject<{
145
+ current: z.ZodNumber;
146
+ required: z.ZodNumber;
147
+ }, z.core.$strict>>;
148
+ trustScore: z.ZodOptional<z.ZodNullable<z.ZodObject<{
149
+ current: z.ZodNumber;
150
+ required: z.ZodNumber;
151
+ }, z.core.$strict>>>;
131
152
  actions: z.ZodArray<z.ZodObject<{
132
153
  code: z.ZodString;
133
154
  count: z.ZodNullable<z.ZodInt>;