@sealkeeper/schema 0.4.0 → 0.4.1

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/NOTICE CHANGED
@@ -1,2 +1,2 @@
1
1
  Vouched schema
2
- Copyright 2026 Carel Meyer
2
+ Copyright 2026 The SealKeeper Authors
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @sealkeeper/schema
2
2
 
3
- Shared schemas and helpers for [Vouched](https://vouched.run), the reputation layer for AI agents.
3
+ Shared schemas and helpers for [SealKeeper](https://sealkeeper.run), the reputation layer for AI agents.
4
4
 
5
- - `@sealkeeper/schema` exports the Zod schemas, the event taxonomy, the JWS signing helpers, credential verification and the agent id helper. No database dependency. The `vouched` CLI bundles this entrypoint.
6
- - `@sealkeeper/schema/db` exports the Drizzle tables the Vouched API uses. It needs `drizzle-orm` and `postgres`, which are optional peer dependencies, so installing the package for verification pulls neither. Install both yourself to use this entrypoint.
5
+ - `@sealkeeper/schema` exports the Zod schemas, the event taxonomy, the JWS signing helpers, credential verification and the agent id helper. No database dependency. The `sealkeeper` CLI bundles this entrypoint.
6
+ - `@sealkeeper/schema/db` exports the Drizzle tables the SealKeeper API uses. It needs `drizzle-orm` and `postgres`, which are optional peer dependencies, so installing the package for verification pulls neither. Install both yourself to use this entrypoint.
7
7
 
8
8
  ```sh
9
9
  npm install @sealkeeper/schema
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  // An agent's name, unique per operator. Together with the operator's GitHub
3
- // login it makes the handle, as in carelmeyer/claude-code. The agent id (the
3
+ // login it makes the handle, as in alice/claude-code. The agent id (the
4
4
  // public key) stays the permanent key, the name can change.
5
5
  //
6
6
  // Lowercase letters, digits and hyphens, 2 to 39 characters, starting and
package/dist/api.d.ts CHANGED
@@ -42,6 +42,7 @@ export declare const AgentResponse: z.ZodObject<{
42
42
  login: z.ZodString;
43
43
  }, z.core.$strict>;
44
44
  createdAt: z.ZodISODateTime;
45
+ operatedBySealKeeper: z.ZodOptional<z.ZodBoolean>;
45
46
  operatedByVouched: z.ZodOptional<z.ZodBoolean>;
46
47
  handle: z.ZodOptional<z.ZodString>;
47
48
  previousName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -156,7 +157,8 @@ export declare const AgentSummary: z.ZodObject<{
156
157
  operator: z.ZodObject<{
157
158
  login: z.ZodString;
158
159
  }, z.core.$strict>;
159
- operatedByVouched: z.ZodBoolean;
160
+ operatedBySealKeeper: z.ZodBoolean;
161
+ operatedByVouched: z.ZodOptional<z.ZodBoolean>;
160
162
  createdAt: z.ZodISODateTime;
161
163
  lastSeenAt: z.ZodNullable<z.ZodISODateTime>;
162
164
  topScores: z.ZodArray<z.ZodObject<{
@@ -203,7 +205,8 @@ export declare const AgentsListResponse: z.ZodObject<{
203
205
  operator: z.ZodObject<{
204
206
  login: z.ZodString;
205
207
  }, z.core.$strict>;
206
- operatedByVouched: z.ZodBoolean;
208
+ operatedBySealKeeper: z.ZodBoolean;
209
+ operatedByVouched: z.ZodOptional<z.ZodBoolean>;
207
210
  createdAt: z.ZodISODateTime;
208
211
  lastSeenAt: z.ZodNullable<z.ZodISODateTime>;
209
212
  topScores: z.ZodArray<z.ZodObject<{
@@ -626,7 +629,8 @@ export declare const LeaderboardEntry: z.ZodObject<{
626
629
  value: z.ZodNumber;
627
630
  verifiedTasks: z.ZodInt;
628
631
  seedTasks: z.ZodOptional<z.ZodInt>;
629
- operatedByVouched: z.ZodBoolean;
632
+ operatedBySealKeeper: z.ZodBoolean;
633
+ operatedByVouched: z.ZodOptional<z.ZodBoolean>;
630
634
  level: z.ZodOptional<z.ZodEnum<{
631
635
  bronze: "bronze";
632
636
  gold: "gold";
@@ -670,7 +674,8 @@ export declare const LeaderboardResponse: z.ZodObject<{
670
674
  value: z.ZodNumber;
671
675
  verifiedTasks: z.ZodInt;
672
676
  seedTasks: z.ZodOptional<z.ZodInt>;
673
- operatedByVouched: z.ZodBoolean;
677
+ operatedBySealKeeper: z.ZodBoolean;
678
+ operatedByVouched: z.ZodOptional<z.ZodBoolean>;
674
679
  level: z.ZodOptional<z.ZodEnum<{
675
680
  bronze: "bronze";
676
681
  gold: "gold";
package/dist/api.js CHANGED
@@ -41,7 +41,7 @@ export const RegisterAgentRequest = z.strictObject({
41
41
  // loose so a client never refuses an answer over a name.
42
42
  const Name = z.string().min(1).max(64);
43
43
  const Operator = z.strictObject({ login: z.string().min(1).max(39) });
44
- // login/name, as in carelmeyer/claude-code. Built by the API from the
44
+ // login/name, as in alice/claude-code. Built by the API from the
45
45
  // operator's current login and the agent's current name, so clients show
46
46
  // and link it as they got it and never assemble it.
47
47
  export const AgentHandle = z.string().min(3).max(79);
@@ -69,13 +69,17 @@ export const AgentResponse = z.strictObject({
69
69
  version: Version,
70
70
  operator: Operator,
71
71
  createdAt: Timestamp,
72
- // True for an agent Vouched runs itself, such as the one that posts the
72
+ // True for an agent SealKeeper runs itself, such as the one that posts the
73
73
  // seed tasks. GET /v1/agents/:id always sends it. The registration answer
74
74
  // leaves it out, because CLI 0.1.0 parses that answer strictly and would
75
75
  // refuse a key it does not know.
76
+ operatedBySealKeeper: z.boolean().optional(),
77
+ // Deprecated. The old name of operatedBySealKeeper, sent with the same
78
+ // value wherever the new one is, because CLI 0.4.2 and earlier read only
79
+ // this one. Removed in VOU-77.
76
80
  operatedByVouched: z.boolean().optional(),
77
81
  // Sent by every read and by rename, left out of the registration answer
78
- // for the same reason as operatedByVouched.
82
+ // for the same reason as operatedBySealKeeper.
79
83
  handle: AgentHandle.optional(),
80
84
  // The name before the latest rename, when that rename was in the last
81
85
  // RENAME_REDIRECT_DAYS days, else null. Sent with handle.
@@ -215,7 +219,10 @@ export const AgentSummary = z.strictObject({
215
219
  handle: AgentHandle,
216
220
  version: Version,
217
221
  operator: Operator,
218
- operatedByVouched: z.boolean(),
222
+ // True for an agent SealKeeper runs itself. See AgentResponse.
223
+ operatedBySealKeeper: z.boolean(),
224
+ // Deprecated, the old name with the same value. Removed in VOU-77.
225
+ operatedByVouched: z.boolean().optional(),
219
226
  createdAt: Timestamp,
220
227
  lastSeenAt: Timestamp.nullable(),
221
228
  topScores: z.array(TopScore).max(2),
@@ -405,8 +412,10 @@ export const LeaderboardEntry = z.strictObject({
405
412
  // How many of verifiedTasks the seed agent posted. The API always sends
406
413
  // it. Optional so the web still reads an answer from an older API.
407
414
  seedTasks: Count.optional(),
408
- // True for an agent Vouched runs itself. See AgentResponse.
409
- operatedByVouched: z.boolean(),
415
+ // True for an agent SealKeeper runs itself. See AgentResponse.
416
+ operatedBySealKeeper: z.boolean(),
417
+ // Deprecated, the old name with the same value. Removed in VOU-77.
418
+ operatedByVouched: z.boolean().optional(),
410
419
  // As on AgentResponse. Optional so the web reads an older API's answer.
411
420
  level: Level.optional(),
412
421
  standing: Standing.optional(),
@@ -1,5 +1,8 @@
1
1
  import postgres from 'postgres';
2
- export declare function createDb(url: string): import("drizzle-orm/postgres-js").PostgresJsDatabase<{
2
+ export type DbOptions = {
3
+ max?: number;
4
+ };
5
+ export declare function createDb(url: string, options?: DbOptions): import("drizzle-orm/postgres-js").PostgresJsDatabase<{
3
6
  operators: import("drizzle-orm/pg-core").PgTableWithColumns<{
4
7
  name: "operators";
5
8
  schema: undefined;
@@ -1887,7 +1890,7 @@ export declare function createDb(url: string): import("drizzle-orm/postgres-js")
1887
1890
  dialect: 'pg';
1888
1891
  }>;
1889
1892
  }> & {
1890
- $client: postgres.Sql<never>;
1893
+ $client: postgres.Sql<{}>;
1891
1894
  };
1892
1895
  export type Db = ReturnType<typeof createDb>;
1893
1896
  export type Tx = Parameters<Parameters<Db['transaction']>[0]>[0];
package/dist/db/client.js CHANGED
@@ -29,6 +29,8 @@ const schema = {
29
29
  agentRenames,
30
30
  deletedOperators,
31
31
  };
32
- export function createDb(url) {
33
- return drizzle(postgres(...pgArgs(url)), { schema });
32
+ export function createDb(url, options = {}) {
33
+ const [conn, pgOptions] = pgArgs(url);
34
+ const max = options.max === undefined ? {} : { max: options.max };
35
+ return drizzle(postgres(conn, { ...pgOptions, ...max }), { schema });
34
36
  }
@@ -15,7 +15,7 @@ export { DrizzleQueryError } from 'drizzle-orm/errors';
15
15
  export { agentRenames } from './agent-renames.js';
16
16
  export type { Agent, NewAgent } from './agents.js';
17
17
  export { agents } from './agents.js';
18
- export { createDb, type Db, type Tx } from './client.js';
18
+ export { createDb, type Db, type DbOptions, type Tx } from './client.js';
19
19
  export { credentials } from './credentials.js';
20
20
  export { deletedOperators } from './deleted-operators.js';
21
21
  export { events } from './events.js';
package/dist/tasks.js CHANGED
@@ -72,9 +72,39 @@ function redactSubschema(node) {
72
72
  return node;
73
73
  return isJsonObject(node) ? redactJsonSchema(node) : {};
74
74
  }
75
+ const BOUNDS = [
76
+ 'minimum',
77
+ 'maximum',
78
+ 'exclusiveMinimum',
79
+ 'exclusiveMaximum',
80
+ ];
81
+ const finite = (v) => typeof v === 'number' && Number.isFinite(v) ? v : undefined;
82
+ // True when the numeric bounds leave room for one value only, such as
83
+ // minimum 42 with maximum 42, or an integer above 41 and below 43. Those
84
+ // pin the answer as surely as const, so the redacted copy drops all four
85
+ // bounds (VOU-107). Not exported, so the schema package's exports stay
86
+ // the ones on npm.
87
+ function boundsPinOneValue(schema) {
88
+ const [min, max, xmin, xmax] = BOUNDS.map((k) => finite(schema[k]));
89
+ if (min !== undefined && min === max)
90
+ return true;
91
+ // Null never meets a numeric bound, so a nullable integer leaks the
92
+ // same one value as a plain integer.
93
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
94
+ const numeric = types.filter((t) => t !== 'null');
95
+ if (numeric.length === 0 || !numeric.every((t) => t === 'integer')) {
96
+ return false;
97
+ }
98
+ const lo = Math.max(min === undefined ? -Infinity : Math.ceil(min), xmin === undefined ? -Infinity : Math.floor(xmin) + 1);
99
+ const hi = Math.min(max === undefined ? Infinity : Math.floor(max), xmax === undefined ? Infinity : Math.ceil(xmax) - 1);
100
+ return lo === hi;
101
+ }
75
102
  export function redactJsonSchema(schema) {
103
+ const pinned = boundsPinOneValue(schema);
76
104
  const out = {};
77
105
  for (const [key, value] of Object.entries(schema)) {
106
+ if (pinned && BOUNDS.includes(key))
107
+ continue;
78
108
  if (SHAPE_DATA.has(key)) {
79
109
  out[key] = Array.isArray(value) ? [...value] : value;
80
110
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@sealkeeper/schema",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
- "description": "Zod schemas, event taxonomy and signing helpers for Vouched.",
5
+ "description": "Zod schemas, event taxonomy and signing helpers for SealKeeper.",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://sealkeeper.run/seal/standard",
8
8
  "repository": {