@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.
- package/dist/api.d.ts +4411 -364
- package/dist/api.js +1753 -46
- package/dist/blocks.d.ts +21 -0
- package/dist/blocks.js +43 -0
- package/dist/cli-version.d.ts +7 -0
- package/dist/cli-version.js +89 -0
- package/dist/conformance.d.ts +4 -0
- package/dist/conformance.js +7 -0
- package/dist/credential.d.ts +763 -21
- package/dist/credential.js +253 -35
- package/dist/dimensions.d.ts +16 -3
- package/dist/dimensions.js +52 -2
- package/dist/fingerprint-conformance.d.ts +17 -0
- package/dist/fingerprint-conformance.js +83 -0
- package/dist/fingerprint.d.ts +133 -0
- package/dist/fingerprint.js +178 -0
- package/dist/game.d.ts +100 -0
- package/dist/game.js +190 -0
- package/dist/goal.d.ts +22 -1
- package/dist/goal.js +50 -7
- package/dist/handshake-conformance.d.ts +18 -0
- package/dist/handshake-conformance.js +276 -0
- package/dist/handshake.d.ts +66 -0
- package/dist/handshake.js +188 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -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/seal-conformance.js +263 -2
- package/dist/standing.d.ts +90 -0
- package/dist/standing.js +253 -13
- package/dist/task-templates.d.ts +47 -0
- package/dist/task-templates.js +506 -0
- package/dist/tasks.d.ts +97 -0
- package/dist/tasks.js +230 -0
- package/dist/template-conformance.d.ts +10 -0
- package/dist/template-conformance.js +298 -0
- package/dist/top-dimensions.d.ts +3 -3
- package/dist/top-dimensions.js +16 -6
- 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'
|
|
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>;
|