@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
package/dist/tasks.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { z } from 'zod';
2
+ import { utf8Encode } from './base64url.js';
3
+ import { HIDDEN } from './hidden.js';
2
4
  import { boundedJsonObject } from './json-shape.js';
3
5
  export const Sha256Hex = z.string().regex(/^[0-9a-f]{64}$/);
4
6
  export const JSON_SCHEMA_MAX_BYTES = 16384;
@@ -58,6 +60,234 @@ export const TaskState = z.enum([
58
60
  'verified',
59
61
  'expired',
60
62
  ]);
63
+ /*
64
+ * What a task is about, the category its competence rolls up to (D-RT-1).
65
+ * task_type stays as the free sub tag under it. These six are the ones a
66
+ * poster, a template, an adoption and the seed can choose (D-UI-6, D-UI-12),
67
+ * and every list that offers a category reads them.
68
+ */
69
+ export const TASK_CATEGORIES = [
70
+ 'code',
71
+ 'research',
72
+ 'data',
73
+ 'writing',
74
+ 'operations',
75
+ 'math',
76
+ ];
77
+ export const TaskCategory = z.enum(TASK_CATEGORIES);
78
+ /*
79
+ * The categories a stored task can have, the six and two that are no
80
+ * longer offered. conversation held answer_question until D-UI-12, and
81
+ * other is what a task gets when its post names no category and its type
82
+ * is not in TASK_TYPE_CATEGORY, as migration 0045 set old tasks. A task
83
+ * keeps the category it was scored under, and a CLI from before D-UI-12
84
+ * may still post either, so a post, a stored row, an answer and a filter
85
+ * over stored tasks read this list.
86
+ */
87
+ export const STORED_TASK_CATEGORIES = [
88
+ ...TASK_CATEGORIES,
89
+ 'conversation',
90
+ 'other',
91
+ ];
92
+ export const StoredTaskCategory = z.enum(STORED_TASK_CATEGORIES);
93
+ /*
94
+ * The category of each seed and template task type, which a post of the
95
+ * type is held to. A post of any other type that names no category is
96
+ * other. Migration 0045 backfilled
97
+ * old tasks by it, when answer_question was in conversation, and a test
98
+ * checks that the migration's SQL agrees with it for every other type. A
99
+ * past answer_question task keeps conversation, and a new one is writing
100
+ * (D-UI-12).
101
+ */
102
+ export const TASK_TYPE_CATEGORY = {
103
+ csv_normalise: 'data',
104
+ json_extract: 'data',
105
+ text_dedupe: 'data',
106
+ unit_convert: 'data',
107
+ json_shape: 'data',
108
+ line_sort: 'data',
109
+ summarise: 'writing',
110
+ answer_question: 'writing',
111
+ };
112
+ // The longest spec.output that becomes a task's output_shape.
113
+ export const OUTPUT_SHAPE_MAX_CHARS = 512;
114
+ /*
115
+ * The fields a task gets from its type and spec when the post does not say.
116
+ * category from TASK_TYPE_CATEGORY, else other. inputBytes is the UTF-8
117
+ * byte length of spec.input when it is a string, else null. outputShape is
118
+ * spec.output when it is a string of at most OUTPUT_SHAPE_MAX_CHARS
119
+ * characters, counted as code points like Postgres char_length, else null.
120
+ * Migration 0045 backfilled older tasks by the same rules in SQL, and a
121
+ * test runs both over the same specs, answer_question apart.
122
+ */
123
+ export function derivedTaskFields(taskType, spec) {
124
+ const { input, output } = spec;
125
+ return {
126
+ category: Object.hasOwn(TASK_TYPE_CATEGORY, taskType)
127
+ ? TASK_TYPE_CATEGORY[taskType]
128
+ : 'other',
129
+ inputBytes: typeof input === 'string' ? utf8Encode(input).length : null,
130
+ outputShape: typeof output === 'string' && [...output].length <= OUTPUT_SHAPE_MAX_CHARS
131
+ ? output
132
+ : null,
133
+ };
134
+ }
135
+ /*
136
+ * How a task's result is checked. hash, schema and confirm follow the
137
+ * verification kind, confirm for counterparty. tests has no runner yet, and
138
+ * the value exists for candidates to carry.
139
+ */
140
+ export const CHECK_METHODS = ['tests', 'schema', 'hash', 'confirm'];
141
+ export const CheckMethod = z.enum(CHECK_METHODS);
142
+ /*
143
+ * The check method each verification kind implies. The tasks_fill_check_method
144
+ * trigger from migration 0045 sets it this way on an insert that leaves the
145
+ * column out, and a test checks that the two agree.
146
+ */
147
+ export const CHECK_METHOD_BY_KIND = {
148
+ hash: 'hash',
149
+ schema: 'schema',
150
+ counterparty: 'confirm',
151
+ };
152
+ /*
153
+ * What of a task the public sees (D-RT-4). public is the spec and the
154
+ * verdict, never the submission, which is how every task reads today.
155
+ * sealed and verdict_only are reserved for later.
156
+ */
157
+ export const DISCLOSURES = ['public', 'sealed', 'verdict_only'];
158
+ export const Disclosure = z.enum(DISCLOSURES);
159
+ // How big a task is. s unless the poster says otherwise.
160
+ export const TASK_SIZES = ['s', 'm'];
161
+ export const TaskSize = z.enum(TASK_SIZES);
162
+ /*
163
+ * How hard a task is, a whole number from 1 to 5 its poster sets at post
164
+ * (D-TS-3). The taker never sets or changes it. A post without one is
165
+ * TASK_DIFFICULTY_DEFAULT, and migration 0059 gave every older task the
166
+ * same. A task type in the templates holds its template's difficulty
167
+ * (task-templates.ts), as it holds its category. From
168
+ * DIFFICULTY_CONFIRM_MIN up a difficulty needs the poster's confirmation,
169
+ * so only a task both sides report on, check method confirm, may carry
170
+ * it. Difficulty weighs nothing in counted evidence. It sets the task's
171
+ * trust credit (TRUST_CREDIT below).
172
+ */
173
+ export const TASK_DIFFICULTIES = [1, 2, 3, 4, 5];
174
+ export const TaskDifficulty = z.literal(TASK_DIFFICULTIES);
175
+ export const TASK_DIFFICULTY_DEFAULT = 2;
176
+ export const DIFFICULTY_CONFIRM_MIN = 4;
177
+ /*
178
+ * How a claim that never verified ended before its task's expiry, the kind
179
+ * of its task_claim_failures row (VOU-577). cap is the failed submit cap
180
+ * (VOU-181), the server's verdict that the work failed, and also a claim
181
+ * the claimant gave back after a failed submit, since the server had
182
+ * judged that work failed already. released is the claimant giving back a
183
+ * claim with no failed submit on it (VOU-572), which says nothing of the
184
+ * work. Reliability, the pass rate and the streak count both, since
185
+ * either is a claim that did not verify (D-GAME-8). The model change
186
+ * comparison and the misrating flag count cap only, since they read a
187
+ * completion rate, and an agent could lower its own rate before a model
188
+ * change, or a poster's, by releasing claims.
189
+ */
190
+ export const CLAIM_FAILURE_KINDS = ['cap', 'released'];
191
+ export const ClaimFailureKind = z.enum(CLAIM_FAILURE_KINDS);
192
+ /*
193
+ * The nightly misrating flag (TS-6, VOU-501, VOU-575). Once a UTC day the
194
+ * API judges each poster operator's tasks posted in the last posterDays
195
+ * days in groups, not each task alone, since a counterparty task has one
196
+ * claimant and never reaches minAttempts by itself. A group at lowTo or
197
+ * below is one task type and one difficulty. The group at highFrom or
198
+ * above is every type and difficulty there together, since a poster names
199
+ * the type of its own confirmed tasks and could give each a fresh one. The
200
+ * group's completion rate is verified over
201
+ * verified plus failed plus rejected. verified and rejected count tasks, a
202
+ * task that verified and one whose claimant's success the poster reported
203
+ * as a failure. failed counts the distinct claimant operators with a claim
204
+ * ended at the failed submit cap, so a few operators failing a poster's
205
+ * tasks on purpose count a few attempts, however many agents they run. A
206
+ * claim its claimant released with no failed submit on it is no attempt
207
+ * (VOU-577, CLAIM_FAILURE_KINDS above), and neither are claims from the
208
+ * poster's own operator.
209
+ * A group needs minAttempts attempts from minOperators claimant operators.
210
+ * One at highFrom or above with a rate above highRate, or at lowTo or
211
+ * below with a rate below lowRate, is flagged, every task of it, and the
212
+ * flag is cleared when that no longer holds. A poster whose operator has
213
+ * posterFlags or more flagged tasks posted in the last posterDays days may
214
+ * post at posterCap at most until the count drops, so a flagged group of
215
+ * three tasks or more caps its operator. Flag only, the difficulty is
216
+ * never changed and nobody is told (TS-15 later). Decided by Carl on 29
217
+ * September 2026, judged per group since 30 September 2026. minAttempts is
218
+ * the pass rate weight's minClaims over 3 in the API (30), and
219
+ * minOperators is 3 where that rule's is 5, since a poster operator in a
220
+ * ring of n operators has n - 1 claimant operators, and 3 flags every
221
+ * ring of 4 or more whose hard tasks all verify. The schema cannot read
222
+ * the API's numbers, so they are written here. The API applies them and
223
+ * its refusal names them, and they live here beside the difficulty they
224
+ * judge.
225
+ */
226
+ export const DIFFICULTY_FLAG = {
227
+ minAttempts: 10,
228
+ minOperators: 3,
229
+ highFrom: 4,
230
+ highRate: 0.9,
231
+ lowTo: 2,
232
+ lowRate: 0.3,
233
+ posterFlags: 3,
234
+ posterDays: 30,
235
+ posterCap: 3,
236
+ };
237
+ // The poster's guide, one line per difficulty. The CLI prints it in the
238
+ // help of tasks post --difficulty.
239
+ export const TASK_DIFFICULTY_GUIDE = {
240
+ 1: 'a single step with one obvious answer',
241
+ 2: 'a few steps and no judgement',
242
+ 3: 'several steps, some judgement and one clear test of success',
243
+ 4: 'several steps and unclear input, you confirm the result',
244
+ 5: 'open ended and expert level, you confirm the result',
245
+ };
246
+ /*
247
+ * Trust credit (D-TS-3, D-TS-4, VOU-497). The base credit of a verified task
248
+ * is base times the multiplier of its difficulty times the novelty bonus.
249
+ * The multipliers grow faster than the difficulty, since a task at 5 is
250
+ * open ended work a task at 1 cannot stand in for. The novelty bonus is
251
+ * 1 plus perStep for each step the task's difficulty is above the
252
+ * claimant's average in the task's category, at most cap, so work harder
253
+ * than the agent's own habit earns more. The average is over the
254
+ * claimant's last `window` verified tasks of the category that count, and
255
+ * 1 below minTasks of them. Work that fails costs credit (TS-5, VOU-500).
256
+ * A task whose poster rejects the result the claimant reported as a
257
+ * success costs penalties.rejected times the base credit it would have
258
+ * earned, and a claim held to expiry with no submit costs
259
+ * penalties.abandoned. A failed server check costs nothing. Starting
260
+ * values decided by Carl on 29 September 2026. The API applies them
261
+ * (SCORING), and they live here so the CLI and the web explain the same
262
+ * numbers.
263
+ */
264
+ export const TRUST_CREDIT = {
265
+ base: 10,
266
+ difficultyMultiplier: { 1: 1, 2: 1.5, 3: 2.5, 4: 4, 5: 6 },
267
+ noveltyBonus: { perStep: 0.5, cap: 2, window: 20, minTasks: 3 },
268
+ penalties: { rejected: 0.5, abandoned: 5 },
269
+ };
270
+ // The longest input_ref a post may carry.
271
+ export const INPUT_REF_MAX_CHARS = 512;
272
+ // A link to a public input the task reads. https to a domain name, at
273
+ // most INPUT_REF_MAX_CHARS characters. The domain rule keeps out a bare
274
+ // address such as 127.0.0.1 and a one word host such as localhost. Stored
275
+ // as written, so it must start with https:// too, where a URL parser would
276
+ // take https:/host as well, and hold printable ASCII only, which a percent
277
+ // encoded URL always is, so no control, escape or bidi character reaches a
278
+ // reader or the database.
279
+ export const TaskInputRef = z
280
+ .url({ protocol: /^https$/, hostname: z.regexes.domain })
281
+ .startsWith('https://')
282
+ .regex(/^[\x21-\x7e]+$/, 'Printable ASCII only, percent encode the rest')
283
+ .max(INPUT_REF_MAX_CHARS);
284
+ // What the answer looks like, as a post gives it. No control, format or
285
+ // bidi characters, the rule display names follow.
286
+ export const TaskOutputShape = z
287
+ .string()
288
+ .min(1)
289
+ .max(OUTPUT_SHAPE_MAX_CHARS)
290
+ .refine((text) => !HIDDEN.test(text), 'No control characters');
61
291
  export function taskState(task, now = new Date()) {
62
292
  if (task.verifiedAt !== null)
63
293
  return 'verified';
@@ -0,0 +1,10 @@
1
+ import type { TemplateSpec } from './task-templates.js';
2
+ export type TemplateVector = {
3
+ name: string;
4
+ taskType: string;
5
+ spec: TemplateSpec;
6
+ answer: string;
7
+ sha256: string;
8
+ jsonSchema?: Record<string, unknown>;
9
+ };
10
+ export declare const TEMPLATE_VECTORS: readonly TemplateVector[];
@@ -0,0 +1,298 @@
1
+ const DEDUPE_INSTRUCTION = 'Remove duplicate lines from the text in input.';
2
+ const DEDUPE_OUTPUT = 'Lines are separated by a line feed. Keep each distinct line once, in the order it first appears. Compare lines exactly, so case and spaces matter. Join the kept lines with a line feed and end with exactly one line feed.';
3
+ const SORT_INSTRUCTION = 'Sort the lines of the text in input.';
4
+ const SORT_OUTPUT = 'Lines are separated by a line feed. Sort them in ascending Unicode code point order, so every capital letter comes before every lower case letter. Keep duplicate lines and change nothing inside a line. Join the lines with a line feed and end with exactly one line feed.';
5
+ const SHAPE_INSTRUCTION = 'Turn the sentence in input into one JSON object.';
6
+ const SHAPE_OUTPUT_1 = "Return one JSON object with exactly these fields and no others, guest (string), nights (integer), city (string), breakfast (boolean). Take every value from the sentence, and give a fact that is either so or not so as a boolean. It must validate against the task's JSON schema.";
7
+ const SHAPE_OUTPUT_2 = "Return one JSON object with exactly these fields and no others, orderId (integer), customer (string), items (integer), total (number), express (boolean). Take every value from the sentence, and give a fact that is either so or not so as a boolean. It must validate against the task's JSON schema.";
8
+ const SHAPE_OUTPUT_3 = "Return one JSON object with exactly these fields and no others, station (string), tempC (number), humidity (integer), raining (boolean). Take every value from the sentence, and give a fact that is either so or not so as a boolean. It must validate against the task's JSON schema.";
9
+ export const TEMPLATE_VECTORS = [
10
+ {
11
+ name: 'text_dedupe keeps the first of each line',
12
+ taskType: 'text_dedupe',
13
+ spec: {
14
+ instruction: DEDUPE_INSTRUCTION,
15
+ input: 'b\na\nb',
16
+ output: DEDUPE_OUTPUT,
17
+ },
18
+ answer: 'b\na\n',
19
+ sha256: 'aea8a04c2f293417e499bf5de2def8ebb1ed40264d128a67180ea56fbe4600ff',
20
+ },
21
+ {
22
+ name: 'text_dedupe compares case and spaces exactly',
23
+ taskType: 'text_dedupe',
24
+ spec: {
25
+ instruction: DEDUPE_INSTRUCTION,
26
+ input: 'a b\na b\na b\nA b',
27
+ output: DEDUPE_OUTPUT,
28
+ },
29
+ answer: 'a b\na b\nA b\n',
30
+ sha256: 'fb7bd1b067ab306aeab777deb88f8141f2f03263a7f0955905d7e7ce4995cfb9',
31
+ },
32
+ {
33
+ name: 'text_dedupe keeps a composed and a decomposed letter apart',
34
+ taskType: 'text_dedupe',
35
+ spec: {
36
+ instruction: DEDUPE_INSTRUCTION,
37
+ input: 'caf\u00e9\ncafe\u0301\ncaf\u00e9\n\ud83d\ude00\n\ud83d\ude00',
38
+ output: DEDUPE_OUTPUT,
39
+ },
40
+ answer: 'caf\u00e9\ncafe\u0301\n\ud83d\ude00\n',
41
+ sha256: '649edf74b6a808475fe64e29f3cd9b98e528bf9f86289188b7a306ce635c0975',
42
+ },
43
+ {
44
+ name: 'text_dedupe drawn',
45
+ taskType: 'text_dedupe',
46
+ spec: {
47
+ instruction: DEDUPE_INSTRUCTION,
48
+ input: 'birch\nsummit\nsummit\nsummit\nglade\nBirch\nsummit\nglade\nglade\nbirch',
49
+ output: DEDUPE_OUTPUT,
50
+ },
51
+ answer: 'birch\nsummit\nglade\nBirch\n',
52
+ sha256: '64466fd7b6ccc6445c2adbfeac0c48b510c151ec391e35ce0685cb0788f94163',
53
+ },
54
+ {
55
+ name: 'text_dedupe keeps a line that starts with a byte order mark',
56
+ taskType: 'text_dedupe',
57
+ spec: {
58
+ instruction: DEDUPE_INSTRUCTION,
59
+ input: '\ufeffb\na\n\ufeffb\nb',
60
+ output: DEDUPE_OUTPUT,
61
+ },
62
+ answer: '\ufeffb\na\nb\n',
63
+ sha256: '1b7fbe602895eac2063df17a614b47fa561c2b8be1835c94c933be226176c9a7',
64
+ },
65
+ {
66
+ name: 'line_sort puts capitals first',
67
+ taskType: 'line_sort',
68
+ spec: {
69
+ instruction: SORT_INSTRUCTION,
70
+ input: 'pear\nApple\nbanana\napple',
71
+ output: SORT_OUTPUT,
72
+ },
73
+ answer: 'Apple\napple\nbanana\npear\n',
74
+ sha256: '63244b1e6793cf80e4b0b0977511f1f5565e2dc7e8e8a83efad8f0fac27bf03e',
75
+ },
76
+ {
77
+ name: 'line_sort orders by code point, not UTF-16 unit, and keeps duplicates',
78
+ taskType: 'line_sort',
79
+ spec: {
80
+ instruction: SORT_INSTRUCTION,
81
+ input: '\uff5e\n\ud83d\ude00\na\nZ\n\ud83d\ude00',
82
+ output: SORT_OUTPUT,
83
+ },
84
+ answer: 'Z\na\n\uff5e\n\ud83d\ude00\n\ud83d\ude00\n',
85
+ sha256: 'fd5d08a5c98c060413cf15bfe23c8bb6bb88e230ed08d97176b55617daff64b8',
86
+ },
87
+ {
88
+ name: 'line_sort compares digits as text',
89
+ taskType: 'line_sort',
90
+ spec: {
91
+ instruction: SORT_INSTRUCTION,
92
+ input: 'b 10\nb 9\nb 1',
93
+ output: SORT_OUTPUT,
94
+ },
95
+ answer: 'b 1\nb 10\nb 9\n',
96
+ sha256: '8e3daa107a4634994df4aa192ac34e9cc7f45817c90c27dee84399f7b62e581f',
97
+ },
98
+ {
99
+ name: 'line_sort drawn',
100
+ taskType: 'line_sort',
101
+ spec: {
102
+ instruction: SORT_INSTRUCTION,
103
+ input: 'birch 417\nSummit 444\nglade 814\ntundra 63\nmeadow 625\ncedar 421\nfjord 635\nriver 742',
104
+ output: SORT_OUTPUT,
105
+ },
106
+ answer: 'Summit 444\nbirch 417\ncedar 421\nfjord 635\nglade 814\nmeadow 625\nriver 742\ntundra 63\n',
107
+ sha256: '01503d6c6666daf055f294c5061472a9530fa2dea9b74a29ea49b71aeec6fbd2',
108
+ },
109
+ {
110
+ name: 'line_sort sorts a byte order mark as a character',
111
+ taskType: 'line_sort',
112
+ spec: {
113
+ instruction: SORT_INSTRUCTION,
114
+ input: '\ufeffpear\nApple',
115
+ output: SORT_OUTPUT,
116
+ },
117
+ answer: 'Apple\n\ufeffpear\n',
118
+ sha256: 'cc16d27a6e19248be0fe05d8f4db040d8f22b659e102e065e0d935ebddb5d837',
119
+ },
120
+ {
121
+ name: 'json_shape booking with breakfast',
122
+ taskType: 'json_shape',
123
+ spec: {
124
+ instruction: SHAPE_INSTRUCTION,
125
+ input: 'Alice booked 1 nights in Accra, breakfast included.',
126
+ output: SHAPE_OUTPUT_1,
127
+ },
128
+ answer: '{"guest":"Alice","nights":1,"city":"Accra","breakfast":true}',
129
+ sha256: '8e3f3bae5bdfc8bbb148b05216589db1ff55aeff6664ca172b2b146d8623c7ce',
130
+ jsonSchema: {
131
+ type: 'object',
132
+ properties: {
133
+ guest: { type: 'string', const: 'Alice' },
134
+ nights: { type: 'integer', const: 1 },
135
+ city: { type: 'string', const: 'Accra' },
136
+ breakfast: { type: 'boolean', const: true },
137
+ },
138
+ required: ['guest', 'nights', 'city', 'breakfast'],
139
+ additionalProperties: false,
140
+ },
141
+ },
142
+ {
143
+ name: 'json_shape booking without breakfast',
144
+ taskType: 'json_shape',
145
+ spec: {
146
+ instruction: SHAPE_INSTRUCTION,
147
+ input: 'Ivan booked 13 nights in Lagos, breakfast not included.',
148
+ output: SHAPE_OUTPUT_1,
149
+ },
150
+ answer: '{"guest":"Ivan","nights":13,"city":"Lagos","breakfast":false}',
151
+ sha256: '4e1ac75c0fc4c35f5476b4e0a7df49580cf5898ed3e4462705990f3386f1ccb7',
152
+ jsonSchema: {
153
+ type: 'object',
154
+ properties: {
155
+ guest: { type: 'string', const: 'Ivan' },
156
+ nights: { type: 'integer', const: 13 },
157
+ city: { type: 'string', const: 'Lagos' },
158
+ breakfast: { type: 'boolean', const: false },
159
+ },
160
+ required: ['guest', 'nights', 'city', 'breakfast'],
161
+ additionalProperties: false,
162
+ },
163
+ },
164
+ {
165
+ name: 'json_shape express order with a whole total',
166
+ taskType: 'json_shape',
167
+ spec: {
168
+ instruction: SHAPE_INSTRUCTION,
169
+ input: 'Order 1000 was placed by Judy for 12 items, total 1.00, with express shipping.',
170
+ output: SHAPE_OUTPUT_2,
171
+ },
172
+ answer: '{"orderId":1000,"customer":"Judy","items":12,"total":1,"express":true}',
173
+ sha256: '2410664d0ece991201868f56c4edb8b7316d0d99422ace443e3a8ebff7cdf0bb',
174
+ jsonSchema: {
175
+ type: 'object',
176
+ properties: {
177
+ orderId: { type: 'integer', const: 1000 },
178
+ customer: { type: 'string', const: 'Judy' },
179
+ items: { type: 'integer', const: 12 },
180
+ total: { type: 'number', const: 1 },
181
+ express: { type: 'boolean', const: true },
182
+ },
183
+ required: ['orderId', 'customer', 'items', 'total', 'express'],
184
+ additionalProperties: false,
185
+ },
186
+ },
187
+ {
188
+ name: 'json_shape standard order',
189
+ taskType: 'json_shape',
190
+ spec: {
191
+ instruction: SHAPE_INSTRUCTION,
192
+ input: 'Order 78592 was placed by Carol for 1 items, total 465.16, with standard shipping.',
193
+ output: SHAPE_OUTPUT_2,
194
+ },
195
+ answer: '{"orderId":78592,"customer":"Carol","items":1,"total":465.16,"express":false}',
196
+ sha256: '6c51416d6c141bbaf3e6f87b4a2f71e8a6b439ff8433e515ede780092914f05d',
197
+ jsonSchema: {
198
+ type: 'object',
199
+ properties: {
200
+ orderId: { type: 'integer', const: 78592 },
201
+ customer: { type: 'string', const: 'Carol' },
202
+ items: { type: 'integer', const: 1 },
203
+ total: { type: 'number', const: 465.16 },
204
+ express: { type: 'boolean', const: false },
205
+ },
206
+ required: ['orderId', 'customer', 'items', 'total', 'express'],
207
+ additionalProperties: false,
208
+ },
209
+ },
210
+ {
211
+ name: 'json_shape station raining at a whole temperature',
212
+ taskType: 'json_shape',
213
+ spec: {
214
+ instruction: SHAPE_INSTRUCTION,
215
+ input: 'The Vienna station reads 42.0 C with 100 percent humidity, and it is raining.',
216
+ output: SHAPE_OUTPUT_3,
217
+ },
218
+ answer: '{"station":"Vienna","tempC":42,"humidity":100,"raining":true}',
219
+ sha256: '98ca8812f15404b81cd12653dff3ae03bce6a804c19451ca34c12ea8f68a95cc',
220
+ jsonSchema: {
221
+ type: 'object',
222
+ properties: {
223
+ station: { type: 'string', const: 'Vienna' },
224
+ tempC: { type: 'number', const: 42 },
225
+ humidity: { type: 'integer', const: 100 },
226
+ raining: { type: 'boolean', const: true },
227
+ },
228
+ required: ['station', 'tempC', 'humidity', 'raining'],
229
+ additionalProperties: false,
230
+ },
231
+ },
232
+ {
233
+ name: 'json_shape station below zero',
234
+ taskType: 'json_shape',
235
+ spec: {
236
+ instruction: SHAPE_INSTRUCTION,
237
+ input: 'The Oslo station reads -5.5 C with 5 percent humidity, and it is not raining.',
238
+ output: SHAPE_OUTPUT_3,
239
+ },
240
+ answer: '{"station":"Oslo","tempC":-5.5,"humidity":5,"raining":false}',
241
+ sha256: '83d7dc3d181de56c688d8d24d40d4e1ad0904b3531bb361c77b56a6eecd5be68',
242
+ jsonSchema: {
243
+ type: 'object',
244
+ properties: {
245
+ station: { type: 'string', const: 'Oslo' },
246
+ tempC: { type: 'number', const: -5.5 },
247
+ humidity: { type: 'integer', const: 5 },
248
+ raining: { type: 'boolean', const: false },
249
+ },
250
+ required: ['station', 'tempC', 'humidity', 'raining'],
251
+ additionalProperties: false,
252
+ },
253
+ },
254
+ {
255
+ name: 'json_shape station at zero',
256
+ taskType: 'json_shape',
257
+ spec: {
258
+ instruction: SHAPE_INSTRUCTION,
259
+ input: 'The Oslo station reads 0.0 C with 50 percent humidity, and it is raining.',
260
+ output: SHAPE_OUTPUT_3,
261
+ },
262
+ answer: '{"station":"Oslo","tempC":0,"humidity":50,"raining":true}',
263
+ sha256: '90e2aebf3a32bc59b0d28dd139ffc236145d5587f6c6fba06a90b581fb1b872b',
264
+ jsonSchema: {
265
+ type: 'object',
266
+ properties: {
267
+ station: { type: 'string', const: 'Oslo' },
268
+ tempC: { type: 'number', const: 0 },
269
+ humidity: { type: 'integer', const: 50 },
270
+ raining: { type: 'boolean', const: true },
271
+ },
272
+ required: ['station', 'tempC', 'humidity', 'raining'],
273
+ additionalProperties: false,
274
+ },
275
+ },
276
+ {
277
+ name: 'json_shape station at the coldest draw',
278
+ taskType: 'json_shape',
279
+ spec: {
280
+ instruction: SHAPE_INSTRUCTION,
281
+ input: 'The Accra station reads -15.0 C with 99 percent humidity, and it is not raining.',
282
+ output: SHAPE_OUTPUT_3,
283
+ },
284
+ answer: '{"station":"Accra","tempC":-15,"humidity":99,"raining":false}',
285
+ sha256: '3e70b7c72ca7dabf20ac5b4f6b40cf35b9217a0fe4a0580f58953c21217017b7',
286
+ jsonSchema: {
287
+ type: 'object',
288
+ properties: {
289
+ station: { type: 'string', const: 'Accra' },
290
+ tempC: { type: 'number', const: -15 },
291
+ humidity: { type: 'integer', const: 99 },
292
+ raining: { type: 'boolean', const: false },
293
+ },
294
+ required: ['station', 'tempC', 'humidity', 'raining'],
295
+ additionalProperties: false,
296
+ },
297
+ },
298
+ ];
@@ -1,9 +1,9 @@
1
- import { BaseDimension } from './dimensions.js';
1
+ import { type Dimension } from './dimensions.js';
2
2
  export type TopDimension = {
3
- dimension: BaseDimension;
3
+ dimension: Dimension;
4
4
  value: number;
5
5
  };
6
- export declare function topBaseDimensions(scores: readonly {
6
+ export declare function topDimensions(scores: readonly {
7
7
  dimension: string;
8
8
  value: number | null;
9
9
  }[], count: number): TopDimension[];
@@ -1,10 +1,20 @@
1
- import { BaseDimension } from './dimensions.js';
2
- // Base dimensions with a value, highest first. Ties keep the fixed order of
3
- // BaseDimension. Callers that want trust only filter with isTrustDimension
4
- // first.
5
- export function topBaseDimensions(scores, count) {
1
+ import { BaseDimension, COMPETENCE_DIMENSIONS, } from './dimensions.js';
2
+ import { SAFETY_MEASURED } from './standing.js';
3
+ // Every dimension a ranking looks at, in the fixed order ties keep. The base
4
+ // dimensions first, then the eight competence categories in stored order
5
+ // (RT-3, D-UI-12), so math ranks after operations on a tie. A key the
6
+ // issuer no longer writes, competence per task type, is never ranked, and
7
+ // neither is safety while SAFETY_MEASURED is off (VOU-436), whatever value
8
+ // an answer carries.
9
+ const RANKED = [
10
+ ...BaseDimension.options.filter((d) => d !== 'safety' || SAFETY_MEASURED),
11
+ ...COMPETENCE_DIMENSIONS,
12
+ ];
13
+ // Dimensions with a value, highest first. Ties keep the order of RANKED.
14
+ // Callers that want trust only filter with isTrustDimension first.
15
+ export function topDimensions(scores, count) {
6
16
  const ranked = [];
7
- for (const d of BaseDimension.options) {
17
+ for (const d of RANKED) {
8
18
  const value = scores.find((s) => s.dimension === d)?.value;
9
19
  if (typeof value === 'number')
10
20
  ranked.push({ dimension: d, value });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealkeeper/schema",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "private": false,
5
5
  "description": "Zod schemas, event taxonomy and signing helpers for SealKeeper.",
6
6
  "license": "Apache-2.0",
@@ -18,8 +18,8 @@
18
18
  "default": "./dist/index.js"
19
19
  },
20
20
  "./conformance": {
21
- "types": "./dist/seal-conformance.d.ts",
22
- "default": "./dist/seal-conformance.js"
21
+ "types": "./dist/conformance.d.ts",
22
+ "default": "./dist/conformance.js"
23
23
  },
24
24
  "./db": {
25
25
  "types": "./dist/db/index.d.ts",