@sealkeeper/schema 0.4.6 → 0.4.8
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/README.md +2 -2
- package/dist/api.d.ts +787 -35
- package/dist/api.js +357 -44
- package/dist/conformance.d.ts +4 -0
- package/dist/conformance.js +7 -0
- package/dist/credential.d.ts +196 -27
- package/dist/credential.js +128 -80
- package/dist/dimensions.d.ts +16 -3
- package/dist/dimensions.js +48 -2
- package/dist/events.d.ts +1 -0
- package/dist/events.js +13 -1
- package/dist/fingerprint-conformance.d.ts +17 -0
- package/dist/fingerprint-conformance.js +83 -0
- package/dist/fingerprint.d.ts +132 -0
- package/dist/fingerprint.js +166 -0
- package/dist/goal.d.ts +16 -1
- package/dist/goal.js +29 -6
- 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 +187 -0
- package/dist/hidden.d.ts +1 -0
- package/dist/hidden.js +5 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/moderation.js +1 -4
- package/dist/policy.d.ts +1 -1
- package/dist/policy.js +1 -1
- package/dist/seal-conformance.js +148 -4
- package/dist/seal-verify.d.ts +21 -0
- package/dist/seal-verify.js +63 -0
- package/dist/standing.d.ts +61 -0
- package/dist/standing.js +132 -4
- package/dist/task-templates.d.ts +46 -0
- package/dist/task-templates.js +501 -0
- package/dist/tasks.d.ts +43 -0
- package/dist/tasks.js +103 -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 +12 -6
- package/package.json +4 -8
- package/dist/db/agent-milestones.d.ts +0 -75
- package/dist/db/agent-milestones.js +0 -16
- package/dist/db/agent-renames.d.ts +0 -126
- package/dist/db/agent-renames.js +0 -20
- package/dist/db/agents.d.ts +0 -351
- package/dist/db/agents.js +0 -99
- package/dist/db/client.d.ts +0 -2561
- package/dist/db/client.js +0 -61
- package/dist/db/credentials.d.ts +0 -143
- package/dist/db/credentials.js +0 -16
- package/dist/db/deleted-operators.d.ts +0 -75
- package/dist/db/deleted-operators.js +0 -13
- package/dist/db/events.d.ts +0 -160
- package/dist/db/events.js +0 -41
- package/dist/db/feed-items.d.ts +0 -109
- package/dist/db/feed-items.js +0 -21
- package/dist/db/index.d.ts +0 -76
- package/dist/db/index.js +0 -28
- package/dist/db/migrate.d.ts +0 -1
- package/dist/db/migrate.js +0 -34
- package/dist/db/migrator.d.ts +0 -6
- package/dist/db/migrator.js +0 -36
- package/dist/db/operator-identities.d.ts +0 -211
- package/dist/db/operator-identities.js +0 -49
- package/dist/db/operator-level-grants.d.ts +0 -109
- package/dist/db/operator-level-grants.js +0 -30
- package/dist/db/operator-slugs.d.ts +0 -92
- package/dist/db/operator-slugs.js +0 -25
- package/dist/db/operators.d.ts +0 -228
- package/dist/db/operators.js +0 -39
- package/dist/db/quota-counters.d.ts +0 -109
- package/dist/db/quota-counters.js +0 -17
- package/dist/db/ratings.d.ts +0 -160
- package/dist/db/ratings.js +0 -27
- package/dist/db/scores.d.ts +0 -160
- package/dist/db/scores.js +0 -15
- package/dist/db/standing.d.ts +0 -264
- package/dist/db/standing.js +0 -34
- package/dist/db/task-claim-failures.d.ts +0 -143
- package/dist/db/task-claim-failures.js +0 -37
- package/dist/db/task-outcomes.d.ts +0 -145
- package/dist/db/task-outcomes.js +0 -25
- package/dist/db/tasks.d.ts +0 -381
- package/dist/db/tasks.js +0 -106
- package/dist/db/timestamps.d.ts +0 -4
- package/dist/db/timestamps.js +0 -24
- package/dist/db/url.d.ts +0 -12
- package/dist/db/url.js +0 -25
package/dist/seal-conformance.js
CHANGED
|
@@ -4,8 +4,8 @@ import { base64urlEncode, utf8Encode } from './base64url.js';
|
|
|
4
4
|
import { CREDENTIAL_ISSUER, LEGACY_ISSUER_UNTIL, LEGACY_ISSUERS, SEAL_CLOCK_SKEW_SECONDS, SEAL_MAX_TTL_SECONDS, SEAL_VERSION, } from './credential.js';
|
|
5
5
|
import { sign } from './envelope.js';
|
|
6
6
|
const KID = 'vouched-conformance-1';
|
|
7
|
-
// 1 October 2026,
|
|
8
|
-
//
|
|
7
|
+
// 1 October 2026, before LEGACY_ISSUER_UNTIL, so a legacy issuer is still
|
|
8
|
+
// accepted.
|
|
9
9
|
const NOW = 1_790_812_800;
|
|
10
10
|
const LEGACY_ISSUER = LEGACY_ISSUERS[0];
|
|
11
11
|
const HOUR = 3600;
|
|
@@ -18,6 +18,21 @@ const COUNTED = {
|
|
|
18
18
|
server_checked_tasks: 0,
|
|
19
19
|
confirmed_tasks: 0,
|
|
20
20
|
};
|
|
21
|
+
// The three posted counts of a version 3 SEAL, and a fingerprint hash,
|
|
22
|
+
// SHA-256 of some bytes in base64url, 43 characters.
|
|
23
|
+
const POSTED = {
|
|
24
|
+
posted_tasks: 4,
|
|
25
|
+
posted_distinct_operators: 2,
|
|
26
|
+
posted_confirmed_tasks: 1,
|
|
27
|
+
};
|
|
28
|
+
const FINGERPRINT_HASH = 'BwjgHg-Z9sC4K05UnygMCN_CD7onYm12GfK83VEIpTQ';
|
|
29
|
+
// The counted evidence of a version 3 SEAL, COUNTED plus the counted twins
|
|
30
|
+
// of posted_tasks and posted_confirmed_tasks, each at most its raw count.
|
|
31
|
+
const COUNTED_V3 = {
|
|
32
|
+
...COUNTED,
|
|
33
|
+
posted_tasks: 3,
|
|
34
|
+
posted_confirmed_tasks: 1,
|
|
35
|
+
};
|
|
21
36
|
const header = (kid) => base64urlEncode(utf8Encode(JSON.stringify({ alg: 'EdDSA', kid })));
|
|
22
37
|
// Signs payload bytes as they are, so a case can carry a payload that is not
|
|
23
38
|
// JSON. sign() in envelope.ts always writes JSON.
|
|
@@ -54,6 +69,18 @@ export async function sealConformanceCases() {
|
|
|
54
69
|
last_active: NOW - HOUR,
|
|
55
70
|
dormant_days: 0,
|
|
56
71
|
};
|
|
72
|
+
// Version 3 is version 2 without version, with the posted counts,
|
|
73
|
+
// fingerprint and state.
|
|
74
|
+
const { version: _dropped, ...v2Fields } = base;
|
|
75
|
+
const v3 = {
|
|
76
|
+
...v2Fields,
|
|
77
|
+
ver: 3,
|
|
78
|
+
counts: { ...base.counts, ...POSTED },
|
|
79
|
+
counted: COUNTED_V3,
|
|
80
|
+
fingerprint: { hash: FINGERPRINT_HASH, at: NOW - 2 * HOUR },
|
|
81
|
+
state: 'matches',
|
|
82
|
+
};
|
|
83
|
+
const { state: _state, ...v3NoState } = v3;
|
|
57
84
|
const signed = (payload) => sign(payload, pair.privateKey, KID);
|
|
58
85
|
const valid = await signed(base);
|
|
59
86
|
const [h, , s] = valid.split('.');
|
|
@@ -108,14 +135,104 @@ export async function sealConformanceCases() {
|
|
|
108
135
|
}),
|
|
109
136
|
'malformed',
|
|
110
137
|
],
|
|
111
|
-
[
|
|
138
|
+
[
|
|
139
|
+
'version 3 with posted counts, fingerprint and state',
|
|
140
|
+
signed(v3),
|
|
141
|
+
'valid',
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
'version 3 with no fingerprint yet and a reserved state',
|
|
145
|
+
signed({ ...v3, fingerprint: null, state: 'provisional' }),
|
|
146
|
+
'valid',
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
'version 3 with a field it does not define',
|
|
150
|
+
signed({ ...v3, extra: true }),
|
|
151
|
+
'malformed',
|
|
152
|
+
],
|
|
153
|
+
['version 3 without state', signed(v3NoState), 'malformed'],
|
|
154
|
+
[
|
|
155
|
+
'version 3 with a state outside the set',
|
|
156
|
+
signed({ ...v3, state: 'unknown' }),
|
|
157
|
+
'malformed',
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
'version 3 with a fingerprint hash of the wrong length',
|
|
161
|
+
signed({
|
|
162
|
+
...v3,
|
|
163
|
+
fingerprint: { hash: FINGERPRINT_HASH.slice(1), at: NOW - HOUR },
|
|
164
|
+
}),
|
|
165
|
+
'malformed',
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
'version 3 with the dropped version field',
|
|
169
|
+
signed({ ...v3, version: '1.0.0' }),
|
|
170
|
+
'malformed',
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
'version 3 without the posted counts',
|
|
174
|
+
signed({ ...v3, counts: base.counts }),
|
|
175
|
+
'malformed',
|
|
176
|
+
],
|
|
177
|
+
[
|
|
178
|
+
'version 3 with a counted posted value above its count',
|
|
179
|
+
signed({
|
|
180
|
+
...v3,
|
|
181
|
+
counted: { ...COUNTED_V3, posted_tasks: 5 },
|
|
182
|
+
}),
|
|
183
|
+
'malformed',
|
|
184
|
+
],
|
|
185
|
+
[
|
|
186
|
+
'version 3 with the version 2 counted evidence',
|
|
187
|
+
signed({ ...v3, counted: COUNTED }),
|
|
188
|
+
'malformed',
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
'version 3 with a fingerprint after iat',
|
|
192
|
+
signed({
|
|
193
|
+
...v3,
|
|
194
|
+
fingerprint: { hash: FINGERPRINT_HASH, at: v3.iat + 1 },
|
|
195
|
+
}),
|
|
196
|
+
'malformed',
|
|
197
|
+
],
|
|
198
|
+
[
|
|
199
|
+
'version 3 with a fingerprint hash whose last character no SHA-256 has',
|
|
200
|
+
signed({
|
|
201
|
+
...v3,
|
|
202
|
+
fingerprint: {
|
|
203
|
+
hash: `${FINGERPRINT_HASH.slice(0, 42)}R`,
|
|
204
|
+
at: NOW - HOUR,
|
|
205
|
+
},
|
|
206
|
+
}),
|
|
207
|
+
'malformed',
|
|
208
|
+
],
|
|
209
|
+
[
|
|
210
|
+
'version 3 with more posted confirmed tasks than posted tasks',
|
|
211
|
+
signed({
|
|
212
|
+
...v3,
|
|
213
|
+
counts: { ...v3.counts, posted_confirmed_tasks: 5 },
|
|
214
|
+
}),
|
|
215
|
+
'malformed',
|
|
216
|
+
],
|
|
217
|
+
[
|
|
218
|
+
'version 2 with a fingerprint and state',
|
|
219
|
+
signed({
|
|
220
|
+
...base,
|
|
221
|
+
ver: 2,
|
|
222
|
+
counted: COUNTED,
|
|
223
|
+
fingerprint: v3.fingerprint,
|
|
224
|
+
state: 'matches',
|
|
225
|
+
}),
|
|
226
|
+
'malformed',
|
|
227
|
+
],
|
|
228
|
+
['an unknown ver', signed({ ...base, ver: 4 }), 'unsupported_version'],
|
|
112
229
|
[
|
|
113
230
|
'no ver after the legacy cutoff',
|
|
114
231
|
signed({ ...base, ver: undefined }),
|
|
115
232
|
'unsupported_version',
|
|
116
233
|
],
|
|
117
234
|
// Intended. Platinum is reserved on LADDER and is not a Level under ver
|
|
118
|
-
// 1 or
|
|
235
|
+
// 1, 2 or 3 (standard, section 9). It becomes valid only with a ver bump.
|
|
119
236
|
[
|
|
120
237
|
'the wrong shape, a level not in the standard',
|
|
121
238
|
signed({ ...base, level: 'platinum' }),
|
|
@@ -126,6 +243,33 @@ export async function sealConformanceCases() {
|
|
|
126
243
|
signed({ ...base, scores: { reliability: 1.2 } }),
|
|
127
244
|
'malformed',
|
|
128
245
|
],
|
|
246
|
+
// Competence is keyed by category from RT-3. A SEAL issued before it
|
|
247
|
+
// carries competence by task type, and a verifier accepts both
|
|
248
|
+
// (standard, section 2).
|
|
249
|
+
[
|
|
250
|
+
'competence by category',
|
|
251
|
+
signed({
|
|
252
|
+
...base,
|
|
253
|
+
scores: { ...base.scores, 'competence:data': 0.8 },
|
|
254
|
+
}),
|
|
255
|
+
'valid',
|
|
256
|
+
],
|
|
257
|
+
[
|
|
258
|
+
'competence by task type, issued before categories',
|
|
259
|
+
signed({
|
|
260
|
+
...base,
|
|
261
|
+
scores: { ...base.scores, 'competence:csv_normalise': 0.8 },
|
|
262
|
+
}),
|
|
263
|
+
'valid',
|
|
264
|
+
],
|
|
265
|
+
[
|
|
266
|
+
'a competence key that is not a task type',
|
|
267
|
+
signed({
|
|
268
|
+
...base,
|
|
269
|
+
scores: { ...base.scores, 'competence:Data Work': 0.8 },
|
|
270
|
+
}),
|
|
271
|
+
'malformed',
|
|
272
|
+
],
|
|
129
273
|
[
|
|
130
274
|
'a negative count',
|
|
131
275
|
signed({
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SealBrokenReason } from './api.js';
|
|
2
|
+
import { type SealPayload } from './credential.js';
|
|
3
|
+
export type SealKey = {
|
|
4
|
+
kid: string;
|
|
5
|
+
x: string;
|
|
6
|
+
};
|
|
7
|
+
export type SealVerified = {
|
|
8
|
+
ok: true;
|
|
9
|
+
kid: string;
|
|
10
|
+
payload: SealPayload;
|
|
11
|
+
expiresIn: number;
|
|
12
|
+
};
|
|
13
|
+
export type SealProblem = {
|
|
14
|
+
ok: false;
|
|
15
|
+
reason: SealBrokenReason;
|
|
16
|
+
kid: string | null;
|
|
17
|
+
signed?: unknown;
|
|
18
|
+
payload?: SealPayload;
|
|
19
|
+
};
|
|
20
|
+
export type SealVerification = SealVerified | SealProblem;
|
|
21
|
+
export declare function verifySeal(keys: readonly SealKey[], compact: string, nowSeconds: number): Promise<SealVerification>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { base64urlDecode } from './base64url.js';
|
|
2
|
+
import { parseSealPayload, sealIatProblem, } from './credential.js';
|
|
3
|
+
import { decodeHeader, EnvelopeError, verify } from './envelope.js';
|
|
4
|
+
/*
|
|
5
|
+
* Verifies a SEAL against the issuer's published keys, the steps of the
|
|
6
|
+
* standard (section 3) in order. The API's POST /v1/seal/verify, the web's
|
|
7
|
+
* verify tool and badge and `sealkeeper seal verify` all call this, and the
|
|
8
|
+
* SEAL conformance cases hold it to the standard's answers.
|
|
9
|
+
*
|
|
10
|
+
* 1. The header, which names the kid. Surrounding whitespace is dropped
|
|
11
|
+
* first, as a pasted SEAL often has it.
|
|
12
|
+
* 2. The key the kid names, among keys.
|
|
13
|
+
* 3. The signature over the exact header.payload bytes, and only then the
|
|
14
|
+
* payload as JSON. A signature that checks out over a payload that is
|
|
15
|
+
* not JSON is malformed, not bad_signature.
|
|
16
|
+
* 4. The issuer, then ver, then the shape for that ver, through
|
|
17
|
+
* parseSealPayload. So a SEAL from another issuer is wrong_issuer
|
|
18
|
+
* whatever its ver or shape.
|
|
19
|
+
* 5. exp, then iat, as the standard reads "Check exp is in the future and
|
|
20
|
+
* iat is not". An iat more than SEAL_CLOCK_SKEW_SECONDS ahead is
|
|
21
|
+
* not_yet_valid.
|
|
22
|
+
*
|
|
23
|
+
* nowSeconds is the verifier's clock in Unix seconds, passed in so tests
|
|
24
|
+
* can pin it.
|
|
25
|
+
*/
|
|
26
|
+
export async function verifySeal(keys, compact, nowSeconds) {
|
|
27
|
+
const jws = compact.trim();
|
|
28
|
+
let kid;
|
|
29
|
+
try {
|
|
30
|
+
kid = decodeHeader(jws).kid;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { ok: false, reason: 'malformed', kid: null };
|
|
34
|
+
}
|
|
35
|
+
const key = keys.find((k) => k.kid === kid);
|
|
36
|
+
if (!key)
|
|
37
|
+
return { ok: false, reason: 'unknown_kid', kid };
|
|
38
|
+
let signed;
|
|
39
|
+
try {
|
|
40
|
+
signed = (await verify(jws, base64urlDecode(key.x))).payload;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
reason: error instanceof EnvelopeError && error.code === 'bad_signature'
|
|
46
|
+
? 'bad_signature'
|
|
47
|
+
: 'malformed',
|
|
48
|
+
kid,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const parsed = parseSealPayload(signed, nowSeconds);
|
|
52
|
+
if (!parsed.ok)
|
|
53
|
+
return { ok: false, reason: parsed.reason, kid, signed };
|
|
54
|
+
const payload = parsed.payload;
|
|
55
|
+
const expiresIn = Math.floor(payload.exp - nowSeconds);
|
|
56
|
+
if (expiresIn <= 0) {
|
|
57
|
+
return { ok: false, reason: 'expired', kid, signed, payload };
|
|
58
|
+
}
|
|
59
|
+
const early = sealIatProblem(payload.iat, nowSeconds);
|
|
60
|
+
if (early)
|
|
61
|
+
return { ok: false, reason: early, kid, signed, payload };
|
|
62
|
+
return { ok: true, kid, payload, expiresIn };
|
|
63
|
+
}
|
package/dist/standing.d.ts
CHANGED
|
@@ -37,6 +37,20 @@ export declare const StandingCounts: z.ZodObject<{
|
|
|
37
37
|
safety_incidents_90d: z.ZodInt;
|
|
38
38
|
}, z.core.$strict>;
|
|
39
39
|
export type StandingCounts = z.infer<typeof StandingCounts>;
|
|
40
|
+
export declare const StandingCountsV3: z.ZodObject<{
|
|
41
|
+
events: z.ZodInt;
|
|
42
|
+
history_days: z.ZodInt;
|
|
43
|
+
verified_tasks: z.ZodInt;
|
|
44
|
+
seed_tasks: z.ZodInt;
|
|
45
|
+
server_checked_tasks: z.ZodInt;
|
|
46
|
+
confirmed_tasks: z.ZodInt;
|
|
47
|
+
distinct_operators: z.ZodInt;
|
|
48
|
+
safety_incidents_90d: z.ZodInt;
|
|
49
|
+
posted_tasks: z.ZodInt;
|
|
50
|
+
posted_distinct_operators: z.ZodInt;
|
|
51
|
+
posted_confirmed_tasks: z.ZodInt;
|
|
52
|
+
}, z.core.$strict>;
|
|
53
|
+
export type StandingCountsV3 = z.infer<typeof StandingCountsV3>;
|
|
40
54
|
export declare const CountedCounts: z.ZodObject<{
|
|
41
55
|
verified_tasks: z.ZodInt;
|
|
42
56
|
seed_tasks: z.ZodInt;
|
|
@@ -44,6 +58,38 @@ export declare const CountedCounts: z.ZodObject<{
|
|
|
44
58
|
confirmed_tasks: z.ZodInt;
|
|
45
59
|
}, z.core.$strict>;
|
|
46
60
|
export type CountedCounts = z.infer<typeof CountedCounts>;
|
|
61
|
+
export declare const CountedCountsV3: z.ZodObject<{
|
|
62
|
+
verified_tasks: z.ZodInt;
|
|
63
|
+
seed_tasks: z.ZodInt;
|
|
64
|
+
server_checked_tasks: z.ZodInt;
|
|
65
|
+
confirmed_tasks: z.ZodInt;
|
|
66
|
+
posted_tasks: z.ZodInt;
|
|
67
|
+
posted_confirmed_tasks: z.ZodInt;
|
|
68
|
+
}, z.core.$strict>;
|
|
69
|
+
export type CountedCountsV3 = z.infer<typeof CountedCountsV3>;
|
|
70
|
+
export declare const AnswerCounts: z.ZodObject<{
|
|
71
|
+
events: z.ZodInt;
|
|
72
|
+
history_days: z.ZodInt;
|
|
73
|
+
verified_tasks: z.ZodInt;
|
|
74
|
+
seed_tasks: z.ZodInt;
|
|
75
|
+
server_checked_tasks: z.ZodInt;
|
|
76
|
+
confirmed_tasks: z.ZodInt;
|
|
77
|
+
distinct_operators: z.ZodInt;
|
|
78
|
+
safety_incidents_90d: z.ZodInt;
|
|
79
|
+
posted_tasks: z.ZodOptional<z.ZodInt>;
|
|
80
|
+
posted_distinct_operators: z.ZodOptional<z.ZodInt>;
|
|
81
|
+
posted_confirmed_tasks: z.ZodOptional<z.ZodInt>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
export type AnswerCounts = z.infer<typeof AnswerCounts>;
|
|
84
|
+
export declare const AnswerCounted: z.ZodObject<{
|
|
85
|
+
verified_tasks: z.ZodInt;
|
|
86
|
+
seed_tasks: z.ZodInt;
|
|
87
|
+
server_checked_tasks: z.ZodInt;
|
|
88
|
+
confirmed_tasks: z.ZodInt;
|
|
89
|
+
posted_tasks: z.ZodOptional<z.ZodInt>;
|
|
90
|
+
posted_confirmed_tasks: z.ZodOptional<z.ZodInt>;
|
|
91
|
+
}, z.core.$strict>;
|
|
92
|
+
export type AnswerCounted = z.infer<typeof AnswerCounted>;
|
|
47
93
|
export declare const COUNTED_EVIDENCE: {
|
|
48
94
|
readonly dailyCeiling: 20;
|
|
49
95
|
readonly diminishingK: 25;
|
|
@@ -61,6 +107,9 @@ export declare const LEVEL_THRESHOLDS: {
|
|
|
61
107
|
readonly historyDays: 3;
|
|
62
108
|
readonly reliability: 0.8;
|
|
63
109
|
readonly incidents90d: 0;
|
|
110
|
+
readonly postedTasks: 5;
|
|
111
|
+
readonly postedOperators: 1;
|
|
112
|
+
readonly postedConfirmedTasks: 0;
|
|
64
113
|
};
|
|
65
114
|
readonly silver: {
|
|
66
115
|
readonly verifiedTasks: 200;
|
|
@@ -70,6 +119,9 @@ export declare const LEVEL_THRESHOLDS: {
|
|
|
70
119
|
readonly incidents90d: 0;
|
|
71
120
|
readonly provenance: 0.8;
|
|
72
121
|
readonly modelDeclared: true;
|
|
122
|
+
readonly postedTasks: 40;
|
|
123
|
+
readonly postedOperators: 5;
|
|
124
|
+
readonly postedConfirmedTasks: 10;
|
|
73
125
|
};
|
|
74
126
|
readonly gold: {
|
|
75
127
|
readonly verifiedTasks: 200;
|
|
@@ -81,6 +133,9 @@ export declare const LEVEL_THRESHOLDS: {
|
|
|
81
133
|
readonly safety: 0.95;
|
|
82
134
|
readonly cleanDays: 180;
|
|
83
135
|
readonly operatorVerified: true;
|
|
136
|
+
readonly postedTasks: 40;
|
|
137
|
+
readonly postedOperators: 5;
|
|
138
|
+
readonly postedConfirmedTasks: 25;
|
|
84
139
|
};
|
|
85
140
|
};
|
|
86
141
|
export declare const OPERATOR_SILVER_CAP: {
|
|
@@ -99,16 +154,22 @@ export declare const Standing: z.ZodObject<{
|
|
|
99
154
|
confirmed_tasks: z.ZodInt;
|
|
100
155
|
distinct_operators: z.ZodInt;
|
|
101
156
|
safety_incidents_90d: z.ZodInt;
|
|
157
|
+
posted_tasks: z.ZodOptional<z.ZodInt>;
|
|
158
|
+
posted_distinct_operators: z.ZodOptional<z.ZodInt>;
|
|
159
|
+
posted_confirmed_tasks: z.ZodOptional<z.ZodInt>;
|
|
102
160
|
}, z.core.$strict>;
|
|
103
161
|
counted: z.ZodObject<{
|
|
104
162
|
verified_tasks: z.ZodInt;
|
|
105
163
|
seed_tasks: z.ZodInt;
|
|
106
164
|
server_checked_tasks: z.ZodInt;
|
|
107
165
|
confirmed_tasks: z.ZodInt;
|
|
166
|
+
posted_tasks: z.ZodOptional<z.ZodInt>;
|
|
167
|
+
posted_confirmed_tasks: z.ZodOptional<z.ZodInt>;
|
|
108
168
|
}, z.core.$strict>;
|
|
109
169
|
history_days: z.ZodInt;
|
|
110
170
|
last_active: z.ZodNullable<z.ZodISODateTime>;
|
|
111
171
|
dormant_days: z.ZodNullable<z.ZodInt>;
|
|
112
172
|
quiet: z.ZodBoolean;
|
|
173
|
+
held: z.ZodOptional<z.ZodBoolean>;
|
|
113
174
|
}, z.core.$strict>;
|
|
114
175
|
export type Standing = z.infer<typeof Standing>;
|
package/dist/standing.js
CHANGED
|
@@ -64,6 +64,50 @@ export const StandingCounts = z
|
|
|
64
64
|
message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
|
|
65
65
|
path: ['verified_tasks'],
|
|
66
66
|
});
|
|
67
|
+
// The counts of SEAL version 3. The eight of StandingCounts with the same
|
|
68
|
+
// meaning, plus three posted counts, raw facts over the same window for the
|
|
69
|
+
// tasks this agent posted. StandingCounts stays the shape of versions 1 and
|
|
70
|
+
// 2 unchanged. The rules behind the posted counts are the posted evidence
|
|
71
|
+
// of the standard's section 4 (POST-1, POST-3), which every level reads,
|
|
72
|
+
// and nothing issues version 3 yet.
|
|
73
|
+
//
|
|
74
|
+
// posted_tasks tasks the agent posted that another
|
|
75
|
+
// operator's agent completed, the server
|
|
76
|
+
// checked ones plus the confirmed ones, the
|
|
77
|
+
// sum of both kinds
|
|
78
|
+
// posted_distinct_operators operators other than the agent's own whose
|
|
79
|
+
// agents completed those tasks
|
|
80
|
+
// posted_confirmed_tasks the confirmed ones among posted_tasks
|
|
81
|
+
//
|
|
82
|
+
// posted_confirmed_tasks is at most posted_tasks, and verified_tasks is the
|
|
83
|
+
// sum of the three kinds as in StandingCounts. posted_distinct_operators has
|
|
84
|
+
// no bound against posted_tasks. An addressed task that counts adds its
|
|
85
|
+
// operator in full while its weight can round the task count down to 0,
|
|
86
|
+
// the same shape distinct_operators has on the taking side. The posted
|
|
87
|
+
// rules live here and nowhere else.
|
|
88
|
+
export const StandingCountsV3 = z
|
|
89
|
+
.strictObject({
|
|
90
|
+
events: Count,
|
|
91
|
+
history_days: Count,
|
|
92
|
+
verified_tasks: Count,
|
|
93
|
+
seed_tasks: Count,
|
|
94
|
+
server_checked_tasks: Count,
|
|
95
|
+
confirmed_tasks: Count,
|
|
96
|
+
distinct_operators: Count,
|
|
97
|
+
safety_incidents_90d: Count,
|
|
98
|
+
posted_tasks: Count,
|
|
99
|
+
posted_distinct_operators: Count,
|
|
100
|
+
posted_confirmed_tasks: Count,
|
|
101
|
+
})
|
|
102
|
+
.refine((c) => c.verified_tasks ===
|
|
103
|
+
c.seed_tasks + c.server_checked_tasks + c.confirmed_tasks, {
|
|
104
|
+
message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
|
|
105
|
+
path: ['verified_tasks'],
|
|
106
|
+
})
|
|
107
|
+
.refine((c) => c.posted_confirmed_tasks <= c.posted_tasks, {
|
|
108
|
+
message: 'posted_confirmed_tasks must not exceed posted_tasks',
|
|
109
|
+
path: ['posted_confirmed_tasks'],
|
|
110
|
+
});
|
|
67
111
|
// The counted evidence (VOU-139), what the levels read. The same verified
|
|
68
112
|
// tasks as the counts after the daily ceiling (at most 20 per agent per UTC
|
|
69
113
|
// day count, the most valuable first) and diminishing returns (within a
|
|
@@ -88,6 +132,66 @@ export const CountedCounts = z
|
|
|
88
132
|
message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
|
|
89
133
|
path: ['verified_tasks'],
|
|
90
134
|
});
|
|
135
|
+
// The counted evidence of SEAL version 3. CountedCounts plus the counted
|
|
136
|
+
// twins of two posted counts, what the posting thresholds read.
|
|
137
|
+
// posted_distinct_operators has no twin, since each operator already counts
|
|
138
|
+
// once. CountedCounts stays the shape of version 2 unchanged. Each value is
|
|
139
|
+
// at most its raw count in StandingCountsV3, which the SEAL checks.
|
|
140
|
+
export const CountedCountsV3 = z
|
|
141
|
+
.strictObject({
|
|
142
|
+
verified_tasks: Count,
|
|
143
|
+
seed_tasks: Count,
|
|
144
|
+
server_checked_tasks: Count,
|
|
145
|
+
confirmed_tasks: Count,
|
|
146
|
+
posted_tasks: Count,
|
|
147
|
+
posted_confirmed_tasks: Count,
|
|
148
|
+
})
|
|
149
|
+
.refine((c) => c.verified_tasks ===
|
|
150
|
+
c.seed_tasks + c.server_checked_tasks + c.confirmed_tasks, {
|
|
151
|
+
message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
|
|
152
|
+
path: ['verified_tasks'],
|
|
153
|
+
});
|
|
154
|
+
// The counts on the agent answers (POST-3). The eight of StandingCounts
|
|
155
|
+
// with the same meaning, plus the three posted counts of StandingCountsV3,
|
|
156
|
+
// which every level reads while a version 1 SEAL cannot carry them. The
|
|
157
|
+
// posted keys are optional, so an answer from an API before them still
|
|
158
|
+
// parses. StandingCounts stays the SEAL version 1 shape.
|
|
159
|
+
export const AnswerCounts = z
|
|
160
|
+
.strictObject({
|
|
161
|
+
events: Count,
|
|
162
|
+
history_days: Count,
|
|
163
|
+
verified_tasks: Count,
|
|
164
|
+
seed_tasks: Count,
|
|
165
|
+
server_checked_tasks: Count,
|
|
166
|
+
confirmed_tasks: Count,
|
|
167
|
+
distinct_operators: Count,
|
|
168
|
+
safety_incidents_90d: Count,
|
|
169
|
+
posted_tasks: Count.optional(),
|
|
170
|
+
posted_distinct_operators: Count.optional(),
|
|
171
|
+
posted_confirmed_tasks: Count.optional(),
|
|
172
|
+
})
|
|
173
|
+
.refine((c) => c.verified_tasks ===
|
|
174
|
+
c.seed_tasks + c.server_checked_tasks + c.confirmed_tasks, {
|
|
175
|
+
message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
|
|
176
|
+
path: ['verified_tasks'],
|
|
177
|
+
});
|
|
178
|
+
// The counted evidence on the agent answers (POST-3). The four of
|
|
179
|
+
// CountedCounts plus the counted posted_tasks and posted_confirmed_tasks of
|
|
180
|
+
// CountedCountsV3, optional like the posted counts of AnswerCounts.
|
|
181
|
+
export const AnswerCounted = z
|
|
182
|
+
.strictObject({
|
|
183
|
+
verified_tasks: Count,
|
|
184
|
+
seed_tasks: Count,
|
|
185
|
+
server_checked_tasks: Count,
|
|
186
|
+
confirmed_tasks: Count,
|
|
187
|
+
posted_tasks: Count.optional(),
|
|
188
|
+
posted_confirmed_tasks: Count.optional(),
|
|
189
|
+
})
|
|
190
|
+
.refine((c) => c.verified_tasks ===
|
|
191
|
+
c.seed_tasks + c.server_checked_tasks + c.confirmed_tasks, {
|
|
192
|
+
message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
|
|
193
|
+
path: ['verified_tasks'],
|
|
194
|
+
});
|
|
91
195
|
// The two numbers of counted evidence (VOU-139), starting values set on 26
|
|
92
196
|
// September 2026. At most dailyCeiling verified tasks per agent per UTC day
|
|
93
197
|
// count, and n tasks of one category count diminishingK * ln(1 + n /
|
|
@@ -118,12 +222,22 @@ export const DORMANCY = {
|
|
|
118
222
|
// (SCORING.levels in the API) and the CLI explains them, so they live here.
|
|
119
223
|
// Task counts are counted evidence (CountedCounts), recalibrated on 26
|
|
120
224
|
// September 2026 (VOU-139) from the simulation in
|
|
121
|
-
// apps/api/src/
|
|
225
|
+
// apps/api/src/test/simulation.ts. Seed tasks count in verifiedTasks at
|
|
122
226
|
// every level (VOU-172, 26 September 2026), so an agent that does seed
|
|
123
227
|
// tasks every day reaches silver on them alone. Silver dropped its
|
|
124
228
|
// checked or confirmed, other operator and confirmed task clauses then,
|
|
125
229
|
// and gold moved from 1000 verified and 250 confirmed from 25 operators to
|
|
126
230
|
// the step-ups below, with ratings left out until a later change.
|
|
231
|
+
//
|
|
232
|
+
// Posted evidence (POST-3, D-POST-1, 27 September 2026). Every level also
|
|
233
|
+
// needs tasks the agent posted that other operators' agents completed, one
|
|
234
|
+
// posted for every five taken. postedTasks is counted posted tasks,
|
|
235
|
+
// postedOperators the operators other than the agent's own behind them and
|
|
236
|
+
// postedConfirmedTasks the counted confirmed ones among them. Bronze counts
|
|
237
|
+
// what the SealKeeper taker completed and its operator. Silver and gold
|
|
238
|
+
// leave the taker out (TAKER-4). Gold's posted confirmed tasks follow the
|
|
239
|
+
// gold origin rule, post manual and neither report routine (D-POST-5). A
|
|
240
|
+
// minimum of 0 holds for every agent, so no level reads it.
|
|
127
241
|
export const LEVEL_THRESHOLDS = {
|
|
128
242
|
bronze: {
|
|
129
243
|
verifiedTasks: 25,
|
|
@@ -131,6 +245,9 @@ export const LEVEL_THRESHOLDS = {
|
|
|
131
245
|
reliability: 0.8,
|
|
132
246
|
// Confirmed incidents in the last 90 days, at most.
|
|
133
247
|
incidents90d: 0,
|
|
248
|
+
postedTasks: 5,
|
|
249
|
+
postedOperators: 1,
|
|
250
|
+
postedConfirmedTasks: 0,
|
|
134
251
|
},
|
|
135
252
|
silver: {
|
|
136
253
|
verifiedTasks: 200,
|
|
@@ -146,6 +263,9 @@ export const LEVEL_THRESHOLDS = {
|
|
|
146
263
|
// A declared model, meaning the card names one or the agent sent a
|
|
147
264
|
// usage event with a model in the window.
|
|
148
265
|
modelDeclared: true,
|
|
266
|
+
postedTasks: 40,
|
|
267
|
+
postedOperators: 5,
|
|
268
|
+
postedConfirmedTasks: 10,
|
|
149
269
|
},
|
|
150
270
|
gold: {
|
|
151
271
|
verifiedTasks: 200,
|
|
@@ -163,6 +283,9 @@ export const LEVEL_THRESHOLDS = {
|
|
|
163
283
|
// its last incident, the clean_days count, which stops here.
|
|
164
284
|
cleanDays: 180,
|
|
165
285
|
operatorVerified: true,
|
|
286
|
+
postedTasks: 40,
|
|
287
|
+
postedOperators: 5,
|
|
288
|
+
postedConfirmedTasks: 25,
|
|
166
289
|
},
|
|
167
290
|
};
|
|
168
291
|
// The operator silver cap (VOU-182, 26 September 2026). At most `agents`
|
|
@@ -190,16 +313,21 @@ export function dormantDays(lastActive, now) {
|
|
|
190
313
|
export const isQuiet = (days) => days !== null && days >= DORMANCY.quietDays;
|
|
191
314
|
// What the agent answers carry beside level, read from the standing table
|
|
192
315
|
// for the current version. counts are the raw facts and counted what the
|
|
193
|
-
// level read. last_active is the newest accepted event of the
|
|
316
|
+
// level read, both with the posted counts (POST-3) beside the SEAL's. last_active is the newest accepted event of the
|
|
194
317
|
// agent on any version, null when it has sent none. dormant_days is whole
|
|
195
318
|
// days since last_active at the time of the answer, null with it. quiet is
|
|
196
319
|
// true when dormant_days is at DORMANCY.quietDays or more.
|
|
197
320
|
export const Standing = z.strictObject({
|
|
198
|
-
counts:
|
|
321
|
+
counts: AnswerCounts,
|
|
199
322
|
// The counted evidence the level stands on (VOU-139).
|
|
200
|
-
counted:
|
|
323
|
+
counted: AnswerCounted,
|
|
201
324
|
history_days: Count,
|
|
202
325
|
last_active: z.iso.datetime().nullable(),
|
|
203
326
|
dormant_days: Count.nullable(),
|
|
204
327
|
quiet: z.boolean(),
|
|
328
|
+
// True while a hold is in force on the agent or its operator (VOU-85),
|
|
329
|
+
// when the issuer withholds the SEAL for cause. Read live, not from the
|
|
330
|
+
// last run. The API always sends it. Optional so the web reads an older
|
|
331
|
+
// API's answer.
|
|
332
|
+
held: z.boolean().optional(),
|
|
205
333
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { TaskCategory, TaskSize } from './tasks.js';
|
|
2
|
+
export declare const TASK_TEMPLATE_IDS: readonly ['text_dedupe', 'line_sort', 'json_shape', 'summarise', 'answer_question'];
|
|
3
|
+
export type TaskTemplateId = (typeof TASK_TEMPLATE_IDS)[number];
|
|
4
|
+
export type TemplateKind = 'hash' | 'schema' | 'counterparty';
|
|
5
|
+
export type TemplateInput = 'none' | 'optional' | 'required';
|
|
6
|
+
export type TemplateSpec = {
|
|
7
|
+
instruction: string;
|
|
8
|
+
input: string;
|
|
9
|
+
output: string;
|
|
10
|
+
};
|
|
11
|
+
export type TemplateDraftVerification = {
|
|
12
|
+
kind: 'hash';
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'schema';
|
|
15
|
+
jsonSchema: Record<string, unknown>;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'counterparty';
|
|
18
|
+
};
|
|
19
|
+
export type TemplateDraft = {
|
|
20
|
+
taskType: TaskTemplateId;
|
|
21
|
+
spec: TemplateSpec;
|
|
22
|
+
verification: TemplateDraftVerification;
|
|
23
|
+
};
|
|
24
|
+
export type TemplateDraw = (lo: number, hi: number) => number;
|
|
25
|
+
export declare class TemplateInputError extends Error {
|
|
26
|
+
name: string;
|
|
27
|
+
}
|
|
28
|
+
export type TemplateSolveErrorCode = 'unknown_template' | 'no_server_check' | 'unknown_rule' | 'bad_input';
|
|
29
|
+
export declare class TemplateSolveError extends Error {
|
|
30
|
+
readonly code: TemplateSolveErrorCode;
|
|
31
|
+
name: string;
|
|
32
|
+
constructor(code: TemplateSolveErrorCode, message: string);
|
|
33
|
+
}
|
|
34
|
+
export type TaskTemplate = {
|
|
35
|
+
id: TaskTemplateId;
|
|
36
|
+
kind: TemplateKind;
|
|
37
|
+
input: TemplateInput;
|
|
38
|
+
category: TaskCategory;
|
|
39
|
+
size: TaskSize;
|
|
40
|
+
make(input: string | undefined, draw: TemplateDraw): TemplateDraft;
|
|
41
|
+
};
|
|
42
|
+
export declare const TEMPLATE_MAX_INPUT_CHARS = 8000;
|
|
43
|
+
export declare const SUMMARISE_MIN_INPUT_WORDS = 40;
|
|
44
|
+
export declare const TASK_TEMPLATES: readonly TaskTemplate[];
|
|
45
|
+
export declare function taskTemplateById(id: string): TaskTemplate | undefined;
|
|
46
|
+
export declare function solveTemplate(taskType: string, spec: unknown): string;
|