@sealkeeper/schema 0.4.3 → 0.4.5

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 (54) hide show
  1. package/dist/agent-name.d.ts +1 -1
  2. package/dist/agent-name.js +4 -4
  3. package/dist/api.d.ts +744 -27
  4. package/dist/api.js +232 -43
  5. package/dist/badge.d.ts +1 -0
  6. package/dist/badge.js +7 -0
  7. package/dist/credential.d.ts +186 -1
  8. package/dist/credential.js +55 -8
  9. package/dist/db/agents.d.ts +70 -0
  10. package/dist/db/agents.js +18 -0
  11. package/dist/db/client.d.ts +398 -0
  12. package/dist/db/client.js +2 -0
  13. package/dist/db/events.js +7 -0
  14. package/dist/db/index.d.ts +17 -1
  15. package/dist/db/index.js +5 -1
  16. package/dist/db/migrate.js +5 -1
  17. package/dist/db/migrator.d.ts +3 -1
  18. package/dist/db/migrator.js +8 -2
  19. package/dist/db/operator-identities.d.ts +211 -0
  20. package/dist/db/operator-identities.js +49 -0
  21. package/dist/db/operator-level-grants.d.ts +109 -0
  22. package/dist/db/operator-level-grants.js +30 -0
  23. package/dist/db/operator-slugs.d.ts +92 -0
  24. package/dist/db/operator-slugs.js +25 -0
  25. package/dist/db/operators.d.ts +119 -0
  26. package/dist/db/operators.js +30 -2
  27. package/dist/db/standing.d.ts +36 -0
  28. package/dist/db/standing.js +5 -0
  29. package/dist/db/task-claim-failures.d.ts +143 -0
  30. package/dist/db/task-claim-failures.js +37 -0
  31. package/dist/db/task-outcomes.d.ts +19 -0
  32. package/dist/db/task-outcomes.js +8 -1
  33. package/dist/db/tasks.d.ts +79 -0
  34. package/dist/db/tasks.js +19 -1
  35. package/dist/envelope.d.ts +3 -0
  36. package/dist/envelope.js +16 -11
  37. package/dist/goal.d.ts +149 -0
  38. package/dist/goal.js +171 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.js +6 -0
  41. package/dist/moderation.d.ts +28 -0
  42. package/dist/moderation.js +450 -0
  43. package/dist/operator-domains.d.ts +104 -0
  44. package/dist/operator-domains.js +85 -0
  45. package/dist/policy.d.ts +4 -0
  46. package/dist/policy.js +10 -0
  47. package/dist/runtime.d.ts +17 -0
  48. package/dist/runtime.js +61 -0
  49. package/dist/seal-conformance.js +32 -1
  50. package/dist/standing.d.ts +68 -0
  51. package/dist/standing.js +132 -6
  52. package/dist/tasks.d.ts +23 -1
  53. package/dist/tasks.js +29 -5
  54. package/package.json +1 -1
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ // What an agent runs in, such as Claude Code or Codex. Declared by the
3
+ // operator at registration or with a signed PATCH, never inferred from
4
+ // events, since events do not say which adapter sent them. `other` is a
5
+ // runtime not on this list, `unknown` is an agent that has not said, which
6
+ // is every agent registered before runtimes existed and every agent a CLI
7
+ // without runtimes registers.
8
+ export const RUNTIMES = [
9
+ 'claude-code',
10
+ 'codex',
11
+ 'cursor',
12
+ 'gemini-cli',
13
+ 'openclaw',
14
+ 'mastra',
15
+ 'other',
16
+ 'unknown',
17
+ ];
18
+ export const Runtime = z.enum(RUNTIMES);
19
+ // The runtime an agent has until it says otherwise.
20
+ export const DEFAULT_RUNTIME = 'unknown';
21
+ // How a runtime reads in prose, on the web and in the CLI.
22
+ export const RUNTIME_LABELS = {
23
+ 'claude-code': 'Claude Code',
24
+ codex: 'Codex',
25
+ cursor: 'Cursor',
26
+ 'gemini-cli': 'Gemini CLI',
27
+ openclaw: 'OpenClaw',
28
+ mastra: 'Mastra',
29
+ other: 'Other',
30
+ unknown: 'Unknown',
31
+ };
32
+ // Names that say what an agent runs in rather than which agent it is, such
33
+ // as claude-code or codex. A soft list. The CLI nudges once when an agent
34
+ // is about to be registered under one, and keeping it is allowed. The API
35
+ // never refuses one, so the list can grow without breaking anybody.
36
+ export const RUNTIME_NAMES = new Set([
37
+ 'aider',
38
+ 'amp',
39
+ 'claude',
40
+ 'claude-code',
41
+ 'cline',
42
+ 'codex',
43
+ 'codex-cli',
44
+ 'continue',
45
+ 'copilot',
46
+ 'cursor',
47
+ 'cursor-agent',
48
+ 'devin',
49
+ 'gemini',
50
+ 'gemini-cli',
51
+ 'goose',
52
+ 'mastra',
53
+ 'openclaw',
54
+ 'opencode',
55
+ 'roo-code',
56
+ 'windsurf',
57
+ ]);
58
+ // True when name is on the soft list above.
59
+ export function isRuntimeName(name) {
60
+ return RUNTIME_NAMES.has(name);
61
+ }
@@ -9,6 +9,15 @@ const KID = 'vouched-conformance-1';
9
9
  const NOW = 1_790_812_800;
10
10
  const LEGACY_ISSUER = LEGACY_ISSUERS[0];
11
11
  const HOUR = 3600;
12
+ // The counted evidence of the base SEAL's counts, for the version 2 cases.
13
+ // 25 seed tasks of one type count 17, one server checked task counts 0.98,
14
+ // rounded down to 0.
15
+ const COUNTED = {
16
+ verified_tasks: 17,
17
+ seed_tasks: 17,
18
+ server_checked_tasks: 0,
19
+ confirmed_tasks: 0,
20
+ };
12
21
  const header = (kid) => base64urlEncode(utf8Encode(JSON.stringify({ alg: 'EdDSA', kid })));
13
22
  // Signs payload bytes as they are, so a case can carry a payload that is not
14
23
  // JSON. sign() in envelope.ts always writes JSON.
@@ -79,12 +88,34 @@ export async function sealConformanceCases() {
79
88
  }),
80
89
  'not_yet_valid',
81
90
  ],
82
- ['an unknown ver', signed({ ...base, ver: 2 }), 'unsupported_version'],
91
+ [
92
+ 'version 2 with counted evidence',
93
+ signed({ ...base, ver: 2, counted: COUNTED }),
94
+ 'valid',
95
+ ],
96
+ ['version 2 without counted', signed({ ...base, ver: 2 }), 'malformed'],
97
+ [
98
+ 'version 1 with counted',
99
+ signed({ ...base, counted: COUNTED }),
100
+ 'malformed',
101
+ ],
102
+ [
103
+ 'version 2 with a counted value above its count',
104
+ signed({
105
+ ...base,
106
+ ver: 2,
107
+ counted: { ...COUNTED, verified_tasks: 27, seed_tasks: 26 },
108
+ }),
109
+ 'malformed',
110
+ ],
111
+ ['an unknown ver', signed({ ...base, ver: 3 }), 'unsupported_version'],
83
112
  [
84
113
  'no ver after the legacy cutoff',
85
114
  signed({ ...base, ver: undefined }),
86
115
  'unsupported_version',
87
116
  ],
117
+ // Intended. Platinum is reserved on LADDER and is not a Level under ver
118
+ // 1 or 2 (standard, section 9). It becomes valid only with a ver bump.
88
119
  [
89
120
  'the wrong shape, a level not in the standard',
90
121
  signed({ ...base, level: 'platinum' }),
@@ -7,6 +7,25 @@ export declare const Level: z.ZodEnum<{
7
7
  }>;
8
8
  export type Level = z.infer<typeof Level>;
9
9
  export declare const LEVEL_RANK: Record<Level, number>;
10
+ export declare const LADDER: readonly [{
11
+ readonly level: 'none';
12
+ readonly state: 'issued';
13
+ }, {
14
+ readonly level: 'bronze';
15
+ readonly state: 'issued';
16
+ }, {
17
+ readonly level: 'silver';
18
+ readonly state: 'issued';
19
+ }, {
20
+ readonly level: 'gold';
21
+ readonly state: 'issued';
22
+ }, {
23
+ readonly level: 'platinum';
24
+ readonly state: 'reserved';
25
+ }];
26
+ export type LadderLevel = (typeof LADDER)[number]['level'];
27
+ export type LadderState = (typeof LADDER)[number]['state'];
28
+ export declare const ISSUED_LEVELS: readonly Level[];
10
29
  export declare const StandingCounts: z.ZodObject<{
11
30
  events: z.ZodInt;
12
31
  history_days: z.ZodInt;
@@ -18,6 +37,17 @@ export declare const StandingCounts: z.ZodObject<{
18
37
  safety_incidents_90d: z.ZodInt;
19
38
  }, z.core.$strict>;
20
39
  export type StandingCounts = z.infer<typeof StandingCounts>;
40
+ export declare const CountedCounts: z.ZodObject<{
41
+ verified_tasks: z.ZodInt;
42
+ seed_tasks: z.ZodInt;
43
+ server_checked_tasks: z.ZodInt;
44
+ confirmed_tasks: z.ZodInt;
45
+ }, z.core.$strict>;
46
+ export type CountedCounts = z.infer<typeof CountedCounts>;
47
+ export declare const COUNTED_EVIDENCE: {
48
+ readonly dailyCeiling: 20;
49
+ readonly diminishingK: 25;
50
+ };
21
51
  export declare const DAY_MS = 86400000;
22
52
  export declare const DORMANCY: {
23
53
  readonly quietDays: 14;
@@ -25,6 +55,38 @@ export declare const DORMANCY: {
25
55
  readonly dropTwoDays: 60;
26
56
  readonly noneDays: 90;
27
57
  };
58
+ export declare const LEVEL_THRESHOLDS: {
59
+ readonly bronze: {
60
+ readonly verifiedTasks: 25;
61
+ readonly historyDays: 3;
62
+ readonly reliability: 0.8;
63
+ readonly incidents90d: 0;
64
+ };
65
+ readonly silver: {
66
+ readonly verifiedTasks: 200;
67
+ readonly historyDays: 30;
68
+ readonly reliability: 0.9;
69
+ readonly safety: 0.9;
70
+ readonly incidents90d: 0;
71
+ readonly provenance: 0.8;
72
+ readonly modelDeclared: true;
73
+ };
74
+ readonly gold: {
75
+ readonly verifiedTasks: 200;
76
+ readonly confirmedTasks: 25;
77
+ readonly confirmedOperators: 3;
78
+ readonly historySpanDays: 90;
79
+ readonly activeDays: 60;
80
+ readonly reliability: 0.95;
81
+ readonly safety: 0.95;
82
+ readonly cleanDays: 180;
83
+ readonly operatorVerified: true;
84
+ };
85
+ };
86
+ export declare const OPERATOR_SILVER_CAP: {
87
+ readonly agents: 5;
88
+ readonly days: 30;
89
+ };
28
90
  export declare function dormantDays(lastActive: Date | null, now: Date): number | null;
29
91
  export declare const isQuiet: (days: number | null) => boolean;
30
92
  export declare const Standing: z.ZodObject<{
@@ -38,6 +100,12 @@ export declare const Standing: z.ZodObject<{
38
100
  distinct_operators: z.ZodInt;
39
101
  safety_incidents_90d: z.ZodInt;
40
102
  }, z.core.$strict>;
103
+ counted: z.ZodObject<{
104
+ verified_tasks: z.ZodInt;
105
+ seed_tasks: z.ZodInt;
106
+ server_checked_tasks: z.ZodInt;
107
+ confirmed_tasks: z.ZodInt;
108
+ }, z.core.$strict>;
41
109
  history_days: z.ZodInt;
42
110
  last_active: z.ZodNullable<z.ZodISODateTime>;
43
111
  dormant_days: z.ZodNullable<z.ZodInt>;
package/dist/standing.js CHANGED
@@ -2,13 +2,33 @@ import { z } from 'zod';
2
2
  // Standing levels from the SEAL standard, section 5, lowest first. none is
3
3
  // below bronze. Every scored agent version has one.
4
4
  export const Level = z.enum(['none', 'bronze', 'silver', 'gold']);
5
- // Higher is better. The leaderboard sorts on it before the score.
5
+ // Higher is better. The leaderboard sorts on it before the score. Equal to
6
+ // the level's index on LADDER.
6
7
  export const LEVEL_RANK = {
7
8
  none: 0,
8
9
  bronze: 1,
9
10
  silver: 2,
10
11
  gold: 3,
11
12
  };
13
+ /*
14
+ * Every level of the standard, lowest first. issued levels are the ones a
15
+ * SEAL can carry. A reserved level is named and ordered but never issued,
16
+ * so no code may treat the highest issued level as the top of the ladder.
17
+ * Platinum is reserved. Level, LEVEL_RANK and the standing.level check
18
+ * constraint stay at the issued four, because the payload fields of a SEAL
19
+ * version are closed (standard, section 9) and published CLIs and offline
20
+ * verifiers reject a level they do not know. Platinum enters Level with a
21
+ * ver bump when it is issued.
22
+ */
23
+ export const LADDER = [
24
+ { level: 'none', state: 'issued' },
25
+ { level: 'bronze', state: 'issued' },
26
+ { level: 'silver', state: 'issued' },
27
+ { level: 'gold', state: 'issued' },
28
+ { level: 'platinum', state: 'reserved' },
29
+ ];
30
+ // The issued levels of LADDER in order, the same values as Level.options.
31
+ export const ISSUED_LEVELS = LADDER.flatMap((step) => step.state === 'issued' ? [Level.parse(step.level)] : []);
12
32
  const Count = z.int().min(0);
13
33
  // The evidence counts of the SEAL standard, section 4, over the scoring
14
34
  // window (180 days) for one agent version. The names are the standard's, so
@@ -44,18 +64,121 @@ export const StandingCounts = z
44
64
  message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
45
65
  path: ['verified_tasks'],
46
66
  });
67
+ // The counted evidence (VOU-139), what the levels read. The same verified
68
+ // tasks as the counts after the daily ceiling (at most 20 per agent per UTC
69
+ // day count, the most valuable first) and diminishing returns (within a
70
+ // seed task type, or another operator's tasks, n tasks count 25 * ln(1 +
71
+ // n / 25)), each kind rounded down. The counts stay the raw facts.
72
+ //
73
+ // verified_tasks seed_tasks + server_checked_tasks + confirmed_tasks
74
+ // seed_tasks counted seed tasks
75
+ // server_checked_tasks counted server checked tasks
76
+ // confirmed_tasks counted confirmed tasks
77
+ //
78
+ // Each is at most its raw count, and verified_tasks is always the sum.
79
+ export const CountedCounts = z
80
+ .strictObject({
81
+ verified_tasks: Count,
82
+ seed_tasks: Count,
83
+ server_checked_tasks: Count,
84
+ confirmed_tasks: Count,
85
+ })
86
+ .refine((c) => c.verified_tasks ===
87
+ c.seed_tasks + c.server_checked_tasks + c.confirmed_tasks, {
88
+ message: 'verified_tasks must equal seed_tasks + server_checked_tasks + confirmed_tasks',
89
+ path: ['verified_tasks'],
90
+ });
91
+ // The two numbers of counted evidence (VOU-139), starting values set on 26
92
+ // September 2026. At most dailyCeiling verified tasks per agent per UTC day
93
+ // count, and n tasks of one category count diminishingK * ln(1 + n /
94
+ // diminishingK). The scoring job applies them (SCORING in the API) and the
95
+ // CLI explains them, so they live here.
96
+ export const COUNTED_EVIDENCE = {
97
+ dailyCeiling: 20,
98
+ diminishingK: 25,
99
+ };
47
100
  export const DAY_MS = 86_400_000;
48
101
  // The dormancy ladder of the SEAL standard, section 5, in whole days since
49
- // the agent's last accepted event. At quietDays the badge shows quiet and
50
- // the level stays. At dropOneDays and dropTwoDays the level drops one step
51
- // each. At noneDays the level is none and no SEAL is issued. The scoring
52
- // job applies it and the CLI explains it, so it lives here.
102
+ // the agent's last accepted event. At quietDays the level stays and the
103
+ // agent is quiet, which the agent answers carry (Standing.quiet, isQuiet)
104
+ // and the badge and profile show. At dropOneDays and dropTwoDays the level
105
+ // drops one step each. At noneDays the level is none and the scoring run
106
+ // stores no_seal, so the SEAL routes withhold the SEAL until a run after
107
+ // the agent is active again. An agent at none for any other reason still gets a SEAL
108
+ // that says none. The scoring job applies the ladder and the CLI explains
109
+ // it, so it lives here.
53
110
  export const DORMANCY = {
54
111
  quietDays: 14,
55
112
  dropOneDays: 30,
56
113
  dropTwoDays: 60,
57
114
  noneDays: 90,
58
115
  };
116
+ // SEAL standard levels, version 1 thresholds, section 5. Every one is a
117
+ // minimum and all of a level's must hold. The scoring job applies them
118
+ // (SCORING.levels in the API) and the CLI explains them, so they live here.
119
+ // Task counts are counted evidence (CountedCounts), recalibrated on 26
120
+ // September 2026 (VOU-139) from the simulation in
121
+ // apps/api/src/scoring/simulation.ts. Seed tasks count in verifiedTasks at
122
+ // every level (VOU-172, 26 September 2026), so an agent that does seed
123
+ // tasks every day reaches silver on them alone. Silver dropped its
124
+ // checked or confirmed, other operator and confirmed task clauses then,
125
+ // and gold moved from 1000 verified and 250 confirmed from 25 operators to
126
+ // the step-ups below, with ratings left out until a later change.
127
+ export const LEVEL_THRESHOLDS = {
128
+ bronze: {
129
+ verifiedTasks: 25,
130
+ historyDays: 3,
131
+ reliability: 0.8,
132
+ // Confirmed incidents in the last 90 days, at most.
133
+ incidents90d: 0,
134
+ },
135
+ silver: {
136
+ verifiedTasks: 200,
137
+ historyDays: 30,
138
+ reliability: 0.9,
139
+ safety: 0.9,
140
+ incidents90d: 0,
141
+ // Provenance of at least this, meaning the share of the agent's events
142
+ // in the window, received since this version was first seen, that carry
143
+ // this version. Measured from first seen, so an agent that ships often
144
+ // and cuts over cleanly still passes.
145
+ provenance: 0.8,
146
+ // A declared model, meaning the card names one or the agent sent a
147
+ // usage event with a model in the window.
148
+ modelDeclared: true,
149
+ },
150
+ gold: {
151
+ verifiedTasks: 200,
152
+ // Confirmed tasks posted by hand with no routine report, from at least
153
+ // confirmedOperators other operators.
154
+ confirmedTasks: 25,
155
+ confirmedOperators: 3,
156
+ // Span from the first accepted event in the window, with activity on at
157
+ // least activeDays of those days.
158
+ historySpanDays: 90,
159
+ activeDays: 60,
160
+ reliability: 0.95,
161
+ safety: 0.95,
162
+ // Whole days since the later of the agent's first accepted event and
163
+ // its last incident, the clean_days count, which stops here.
164
+ cleanDays: 180,
165
+ operatorVerified: true,
166
+ },
167
+ };
168
+ // The operator silver cap (VOU-182, 26 September 2026). At most `agents`
169
+ // agents of one operator reach silver for the first time in any `days`
170
+ // days. The first time an agent earns silver it takes a grant, one of the
171
+ // operator's slots. An agent that finds no free slot is held at bronze
172
+ // until the oldest grant in the window is `days` days old. An agent keeps
173
+ // its grant through dormancy drops and version changes, and deleting it
174
+ // does not free the slot. Gold is above silver, so it needs the grant too.
175
+ // The scoring job applies it and the CLI explains it, so it lives here. A
176
+ // higher number for verified operators (20 proposed) is open. It would sit
177
+ // beside `agents` as its own key.
178
+ export const OPERATOR_SILVER_CAP = {
179
+ agents: 5,
180
+ days: 30,
181
+ };
59
182
  // Whole days from lastActive to now, never below zero. null when the agent
60
183
  // has sent no accepted event. The one place dormancy is counted.
61
184
  export function dormantDays(lastActive, now) {
@@ -66,12 +189,15 @@ export function dormantDays(lastActive, now) {
66
189
  // True at quietDays or more. Never active is not quiet.
67
190
  export const isQuiet = (days) => days !== null && days >= DORMANCY.quietDays;
68
191
  // What the agent answers carry beside level, read from the standing table
69
- // for the current version. last_active is the newest accepted event of the
192
+ // 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
70
194
  // agent on any version, null when it has sent none. dormant_days is whole
71
195
  // days since last_active at the time of the answer, null with it. quiet is
72
196
  // true when dormant_days is at DORMANCY.quietDays or more.
73
197
  export const Standing = z.strictObject({
74
198
  counts: StandingCounts,
199
+ // The counted evidence the level stands on (VOU-139).
200
+ counted: CountedCounts,
75
201
  history_days: Count,
76
202
  last_active: z.iso.datetime().nullable(),
77
203
  dormant_days: Count.nullable(),
package/dist/tasks.d.ts CHANGED
@@ -22,6 +22,28 @@ export declare const VerificationSpec: z.ZodDiscriminatedUnion<[z.ZodObject<{
22
22
  kind: z.ZodLiteral<"counterparty">;
23
23
  }, z.core.$strict>], "kind">;
24
24
  export type VerificationSpec = z.infer<typeof VerificationSpec>;
25
+ export declare const PublicHashVerification: z.ZodObject<{
26
+ kind: z.ZodLiteral<"hash">;
27
+ }, z.core.$strict>;
28
+ export declare const PublicVerificationSpec: z.ZodDiscriminatedUnion<[z.ZodObject<{
29
+ kind: z.ZodLiteral<"hash">;
30
+ }, z.core.$strict>, z.ZodObject<{
31
+ kind: z.ZodLiteral<"schema">;
32
+ jsonSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
33
+ }, z.core.$strict>, z.ZodObject<{
34
+ kind: z.ZodLiteral<"counterparty">;
35
+ }, z.core.$strict>], "kind">;
36
+ export type PublicVerificationSpec = z.infer<typeof PublicVerificationSpec>;
37
+ export declare const ShownVerificationSpec: z.ZodDiscriminatedUnion<[z.ZodObject<{
38
+ kind: z.ZodLiteral<"hash">;
39
+ sha256: z.ZodOptional<z.ZodString>;
40
+ }, z.core.$strict>, z.ZodObject<{
41
+ kind: z.ZodLiteral<"schema">;
42
+ jsonSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
43
+ }, z.core.$strict>, z.ZodObject<{
44
+ kind: z.ZodLiteral<"counterparty">;
45
+ }, z.core.$strict>], "kind">;
46
+ export type ShownVerificationSpec = z.infer<typeof ShownVerificationSpec>;
25
47
  export declare const TaskOutcome: z.ZodEnum<{
26
48
  failure: "failure";
27
49
  success: "success";
@@ -45,5 +67,5 @@ export type TaskTimestamps = {
45
67
  export declare function taskState(task: TaskTimestamps, now?: Date): TaskState;
46
68
  type JsonObject = Record<string, unknown>;
47
69
  export declare function redactJsonSchema(schema: JsonObject): JsonObject;
48
- export declare function publicVerification(spec: VerificationSpec): VerificationSpec;
70
+ export declare function publicVerification(spec: VerificationSpec): PublicVerificationSpec;
49
71
  export {};
package/dist/tasks.js CHANGED
@@ -20,6 +20,26 @@ export const VerificationSpec = z.discriminatedUnion('kind', [
20
20
  SchemaVerification,
21
21
  CounterpartyVerification,
22
22
  ]);
23
+ // A hash task as everyone but its poster sees it, without the digest. With
24
+ // the digest in hand a claimant could hash candidate answers offline until
25
+ // one matched and submit only that one (VOU-181).
26
+ export const PublicHashVerification = z.strictObject({
27
+ kind: z.literal('hash'),
28
+ });
29
+ // The spec every public read shows, from publicVerification below.
30
+ export const PublicVerificationSpec = z.discriminatedUnion('kind', [
31
+ PublicHashVerification,
32
+ SchemaVerification,
33
+ CounterpartyVerification,
34
+ ]);
35
+ // The spec a task response carries. The poster's own post and its signed
36
+ // submission read show the full spec, every other response the public one,
37
+ // so the digest is optional here and required in a post.
38
+ export const ShownVerificationSpec = z.discriminatedUnion('kind', [
39
+ z.strictObject({ kind: z.literal('hash'), sha256: Sha256Hex.optional() }),
40
+ SchemaVerification,
41
+ CounterpartyVerification,
42
+ ]);
23
43
  export const TaskOutcome = z.enum(['success', 'failure']);
24
44
  export const TaskState = z.enum([
25
45
  'open',
@@ -123,10 +143,14 @@ export function redactJsonSchema(schema) {
123
143
  }
124
144
  return out;
125
145
  }
126
- // What GET /v1/tasks and GET /v1/tasks/:id show. Hash and counterparty specs
127
- // are already safe to show, a sha256 is one way.
146
+ // What GET /v1/tasks and GET /v1/tasks/:id show. A hash task drops its
147
+ // digest, which only the poster sees (VOU-181). A schema task shows its
148
+ // redacted schema. A counterparty spec has nothing to hide.
128
149
  export function publicVerification(spec) {
129
- if (spec.kind !== 'schema')
130
- return spec;
131
- return { kind: 'schema', jsonSchema: redactJsonSchema(spec.jsonSchema) };
150
+ if (spec.kind === 'hash')
151
+ return { kind: 'hash' };
152
+ if (spec.kind === 'schema') {
153
+ return { kind: 'schema', jsonSchema: redactJsonSchema(spec.jsonSchema) };
154
+ }
155
+ return spec;
132
156
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealkeeper/schema",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "private": false,
5
5
  "description": "Zod schemas, event taxonomy and signing helpers for SealKeeper.",
6
6
  "license": "Apache-2.0",