@sealkeeper/schema 0.4.4 → 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 (50) 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 +604 -27
  4. package/dist/api.js +207 -43
  5. package/dist/badge.d.ts +1 -0
  6. package/dist/badge.js +7 -0
  7. package/dist/db/agents.d.ts +70 -0
  8. package/dist/db/agents.js +18 -0
  9. package/dist/db/client.d.ts +341 -0
  10. package/dist/db/client.js +2 -0
  11. package/dist/db/events.js +7 -0
  12. package/dist/db/index.d.ts +16 -0
  13. package/dist/db/index.js +4 -0
  14. package/dist/db/migrate.js +5 -1
  15. package/dist/db/migrator.d.ts +3 -1
  16. package/dist/db/migrator.js +8 -2
  17. package/dist/db/operator-identities.d.ts +211 -0
  18. package/dist/db/operator-identities.js +49 -0
  19. package/dist/db/operator-level-grants.d.ts +109 -0
  20. package/dist/db/operator-level-grants.js +30 -0
  21. package/dist/db/operator-slugs.d.ts +92 -0
  22. package/dist/db/operator-slugs.js +25 -0
  23. package/dist/db/operators.d.ts +119 -0
  24. package/dist/db/operators.js +30 -2
  25. package/dist/db/standing.d.ts +1 -0
  26. package/dist/db/standing.js +2 -0
  27. package/dist/db/task-claim-failures.d.ts +143 -0
  28. package/dist/db/task-claim-failures.js +37 -0
  29. package/dist/db/tasks.d.ts +60 -0
  30. package/dist/db/tasks.js +13 -1
  31. package/dist/envelope.d.ts +3 -0
  32. package/dist/envelope.js +16 -11
  33. package/dist/goal.d.ts +62 -1
  34. package/dist/goal.js +70 -11
  35. package/dist/index.d.ts +5 -0
  36. package/dist/index.js +5 -0
  37. package/dist/moderation.d.ts +28 -0
  38. package/dist/moderation.js +450 -0
  39. package/dist/operator-domains.d.ts +104 -0
  40. package/dist/operator-domains.js +85 -0
  41. package/dist/policy.d.ts +4 -0
  42. package/dist/policy.js +10 -0
  43. package/dist/runtime.d.ts +17 -0
  44. package/dist/runtime.js +61 -0
  45. package/dist/seal-conformance.js +2 -0
  46. package/dist/standing.d.ts +27 -8
  47. package/dist/standing.js +59 -22
  48. package/dist/tasks.d.ts +23 -1
  49. package/dist/tasks.js +29 -5
  50. package/package.json +1 -1
@@ -1,11 +1,39 @@
1
- import { bigint, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
1
+ import { bigint, pgTable, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
2
2
  import { timestamps } from './timestamps.js';
3
+ // github_id is the identity, since logins change. login is kept fresh from
4
+ // GitHub and shows only in the operator section of a profile. slug is what
5
+ // the operator is known by, the first half of every handle, as in
6
+ // alice/claude-code. It defaults to the login lowercased and changes on the
7
+ // web only (PATCH /v1/me). display_name is free text, and display_name_key
8
+ // is its normalised form (displayNameKey in @sealkeeper/schema), unique so
9
+ // two operators never hold names that read the same, even under concurrent
10
+ // writes. slug_changed_at is when the slug last changed, null until the
11
+ // first change, for the one change every SLUG_CHANGE_DAYS.
12
+ // slug_notice_dismissed_at is when the operator dismissed the one time
13
+ // notice on /me that says what its slug is and that it can change, null
14
+ // until then (VOU-190). Held here, not in the browser, so it shows once
15
+ // across devices. terms_version and privacy_version are the "Last updated"
16
+ // dates of the terms and the privacy policy (POLICY_VERSIONS in
17
+ // @sealkeeper/schema) in force when the API created the row, which is when
18
+ // the operator accepted them (VOU-121). Null for operators created before.
3
19
  export const operators = pgTable('operators', {
4
20
  id: uuid('id').primaryKey().defaultRandom(),
5
21
  githubId: bigint('github_id', { mode: 'number' }).notNull().unique(),
6
22
  login: text('login').notNull(),
23
+ slug: text('slug').notNull(),
24
+ displayName: text('display_name').notNull(),
25
+ displayNameKey: text('display_name_key').notNull(),
26
+ slugChangedAt: timestamp('slug_changed_at', { withTimezone: true }),
27
+ slugNoticeDismissedAt: timestamp('slug_notice_dismissed_at', {
28
+ withTimezone: true,
29
+ }),
7
30
  accountCreatedAt: timestamp('account_created_at', {
8
31
  withTimezone: true,
9
32
  }).notNull(),
33
+ termsVersion: text('terms_version'),
34
+ privacyVersion: text('privacy_version'),
10
35
  ...timestamps,
11
- });
36
+ }, (t) => [
37
+ uniqueIndex('operators_slug_unique').on(t.slug),
38
+ uniqueIndex('operators_display_name_key_unique').on(t.displayNameKey),
39
+ ]);
@@ -10,6 +10,7 @@ export type StoredCounts = {
10
10
  confirmed_operators: number;
11
11
  history_span_days: number;
12
12
  safety_incidents_180d: number;
13
+ clean_days: number;
13
14
  gold_confirmed_tasks: number;
14
15
  gold_confirmed_operators: number;
15
16
  counted_verified_tasks: number;
@@ -28,5 +28,7 @@ export const standing = pgTable('standing', {
28
28
  ...timestamps,
29
29
  }, (t) => [
30
30
  primaryKey({ columns: [t.agentId, t.version] }),
31
+ // The issued levels only. Platinum is reserved on LADDER and has no
32
+ // standing row until it is issued with a SEAL ver bump.
31
33
  check('standing_level', sql `${t.level} in ('none', 'bronze', 'silver', 'gold')`),
32
34
  ]);
@@ -0,0 +1,143 @@
1
+ export declare const taskClaimFailures: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "task_claim_failures";
3
+ schema: undefined;
4
+ columns: {
5
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "created_at";
7
+ tableName: "task_claim_failures";
8
+ dataType: "date";
9
+ columnType: "PgTimestamp";
10
+ data: Date;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: true;
14
+ isPrimaryKey: false;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "updated_at";
24
+ tableName: "task_claim_failures";
25
+ dataType: "date";
26
+ columnType: "PgTimestamp";
27
+ data: Date;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: true;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: undefined;
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ id: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "id";
41
+ tableName: "task_claim_failures";
42
+ dataType: "string";
43
+ columnType: "PgUUID";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: true;
48
+ isPrimaryKey: true;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: undefined;
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ taskId: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "task_id";
58
+ tableName: "task_claim_failures";
59
+ dataType: "string";
60
+ columnType: "PgUUID";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ agentId: import("drizzle-orm/pg-core").PgColumn<{
74
+ name: "agent_id";
75
+ tableName: "task_claim_failures";
76
+ dataType: "string";
77
+ columnType: "PgText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ version: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "version";
92
+ tableName: "task_claim_failures";
93
+ dataType: "string";
94
+ columnType: "PgText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: true;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ claimedAt: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "claimed_at";
109
+ tableName: "task_claim_failures";
110
+ dataType: "date";
111
+ columnType: "PgTimestamp";
112
+ data: Date;
113
+ driverParam: string;
114
+ notNull: true;
115
+ hasDefault: false;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: undefined;
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ failedAt: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "failed_at";
126
+ tableName: "task_claim_failures";
127
+ dataType: "date";
128
+ columnType: "PgTimestamp";
129
+ data: Date;
130
+ driverParam: string;
131
+ notNull: true;
132
+ hasDefault: true;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: undefined;
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ };
142
+ dialect: 'pg';
143
+ }>;
@@ -0,0 +1,37 @@
1
+ import { index, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
2
+ import { agents } from './agents.js';
3
+ import { tasks } from './tasks.js';
4
+ import { timestamps } from './timestamps.js';
5
+ /*
6
+ * Claims released at the failed submit cap (TASK_MAX_FAILED_SUBMITS in
7
+ * apps/api/src/routes/tasks.ts). The release nulls the task's claimant and
8
+ * claimed_at, so without this row the claim would drop out of the agent's
9
+ * reliability. The scoring run counts each row as a claim that never
10
+ * verified, like a claim held until expiry.
11
+ *
12
+ * claimed_at is the released claim's, and version the claimant's version
13
+ * at that claim, worked out at the release the same way the run credits a
14
+ * task (claimVersion in apps/api/src/scoring/run.ts). failed_at is the
15
+ * submit that reached the cap. Deleting the agent or the task deletes the
16
+ * row (deleteAgents in apps/api/src/account.ts).
17
+ */
18
+ export const taskClaimFailures = pgTable('task_claim_failures', {
19
+ id: uuid('id').primaryKey().defaultRandom(),
20
+ taskId: uuid('task_id')
21
+ .notNull()
22
+ .references(() => tasks.id),
23
+ agentId: text('agent_id')
24
+ .notNull()
25
+ .references(() => agents.id),
26
+ version: text('version').notNull(),
27
+ claimedAt: timestamp('claimed_at', { withTimezone: true }).notNull(),
28
+ failedAt: timestamp('failed_at', { withTimezone: true })
29
+ .notNull()
30
+ .defaultNow(),
31
+ ...timestamps,
32
+ }, (t) => [
33
+ // The scoring run's read, one agent's released claims by claim time.
34
+ index('task_claim_failures_agent_claimed_at_idx').on(t.agentId, t.claimedAt),
35
+ // A deletion's rows by task.
36
+ index('task_claim_failures_task_id_idx').on(t.taskId),
37
+ ]);
@@ -299,6 +299,66 @@ export declare const tasks: import("drizzle-orm/pg-core").PgTableWithColumns<{
299
299
  text: string;
300
300
  };
301
301
  }>;
302
+ failedSubmits: import("drizzle-orm/pg-core").PgColumn<{
303
+ name: "failed_submits";
304
+ tableName: "tasks";
305
+ dataType: "number";
306
+ columnType: "PgInteger";
307
+ data: number;
308
+ driverParam: string | number;
309
+ notNull: true;
310
+ hasDefault: true;
311
+ isPrimaryKey: false;
312
+ isAutoincrement: false;
313
+ hasRuntimeDefault: false;
314
+ enumValues: undefined;
315
+ baseColumn: never;
316
+ identity: undefined;
317
+ generated: undefined;
318
+ }, {}, {}>;
319
+ barredAgentIds: import("drizzle-orm/pg-core").PgColumn<{
320
+ name: "barred_agent_ids";
321
+ tableName: "tasks";
322
+ dataType: "array";
323
+ columnType: "PgArray";
324
+ data: string[];
325
+ driverParam: string | string[];
326
+ notNull: true;
327
+ hasDefault: true;
328
+ isPrimaryKey: false;
329
+ isAutoincrement: false;
330
+ hasRuntimeDefault: false;
331
+ enumValues: [string, ...string[]];
332
+ baseColumn: import("drizzle-orm").Column<{
333
+ name: "barred_agent_ids";
334
+ tableName: "tasks";
335
+ dataType: "string";
336
+ columnType: "PgText";
337
+ data: string;
338
+ driverParam: string;
339
+ notNull: false;
340
+ hasDefault: false;
341
+ isPrimaryKey: false;
342
+ isAutoincrement: false;
343
+ hasRuntimeDefault: false;
344
+ enumValues: [string, ...string[]];
345
+ baseColumn: never;
346
+ identity: undefined;
347
+ generated: undefined;
348
+ }, {}, {}>;
349
+ identity: undefined;
350
+ generated: undefined;
351
+ }, {}, {
352
+ baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
353
+ name: "barred_agent_ids";
354
+ dataType: 'string';
355
+ columnType: 'PgText';
356
+ data: string;
357
+ enumValues: [string, ...string[]];
358
+ driverParam: string;
359
+ }, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
360
+ size: undefined;
361
+ }>;
302
362
  expiresAt: import("drizzle-orm/pg-core").PgColumn<{
303
363
  name: "expires_at";
304
364
  tableName: "tasks";
package/dist/db/tasks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { sql } from 'drizzle-orm';
2
- import { boolean, check, index, jsonb, pgTable, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
2
+ import { boolean, check, index, integer, jsonb, pgTable, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
3
3
  import { agents } from './agents.js';
4
4
  import { timestamps } from './timestamps.js';
5
5
  export const tasks = pgTable('tasks', {
@@ -42,6 +42,18 @@ export const tasks = pgTable('tasks', {
42
42
  // does not survive a read. postgres-js parses jsonb and Drizzle then
43
43
  // JSON.parses any string it gets back, so "123" would return as 123.
44
44
  submission: jsonb('submission').$type(),
45
+ // Failed submits on the current claim of a hash or schema task. The
46
+ // submit route counts them up, and at the cap (TASK_MAX_FAILED_SUBMITS
47
+ // in apps/api/src/routes/tasks.ts) it releases the claim and sets this
48
+ // back to 0 for the next claimant.
49
+ failedSubmits: integer('failed_submits').notNull().default(0),
50
+ // Agents whose claim was released at the failed submit cap. None of them
51
+ // can claim this task again, so guessing at a public hash costs a claim
52
+ // each three tries. Deleting an agent takes its id out.
53
+ barredAgentIds: text('barred_agent_ids')
54
+ .array()
55
+ .notNull()
56
+ .default(sql `'{}'::text[]`),
45
57
  expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
46
58
  ...timestamps,
47
59
  },
@@ -5,8 +5,11 @@ export declare const JwsHeader: z.ZodObject<{
5
5
  kid: z.ZodString;
6
6
  }, z.core.$strict>;
7
7
  export type JwsHeader = z.infer<typeof JwsHeader>;
8
+ export type EnvelopeErrorCode = 'malformed' | 'bad_signature' | 'invalid_input';
8
9
  export declare class EnvelopeError extends Error {
10
+ readonly code: EnvelopeErrorCode;
9
11
  name: string;
12
+ constructor(code: EnvelopeErrorCode, message: string);
10
13
  }
11
14
  export declare function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string>;
12
15
  export declare function decodeHeader(jws: string): JwsHeader;
package/dist/envelope.js CHANGED
@@ -11,11 +11,16 @@ export const JwsHeader = z.strictObject({
11
11
  kid: z.string().min(1).max(128),
12
12
  });
13
13
  export class EnvelopeError extends Error {
14
+ code;
14
15
  name = 'EnvelopeError';
16
+ constructor(code, message) {
17
+ super(message);
18
+ this.code = code;
19
+ }
15
20
  }
16
21
  function split(jws) {
17
22
  if (!Jws.safeParse(jws).success) {
18
- throw new EnvelopeError('Envelope is not three base64url parts');
23
+ throw new EnvelopeError('malformed', 'Envelope is not three base64url parts');
19
24
  }
20
25
  const [header, payload, signature] = jws.split('.');
21
26
  return { header, payload, signature };
@@ -25,13 +30,13 @@ function parseJson(part, what) {
25
30
  return JSON.parse(utf8Decode(base64urlDecode(part)));
26
31
  }
27
32
  catch {
28
- throw new EnvelopeError(`Envelope ${what} is not base64url JSON`);
33
+ throw new EnvelopeError('malformed', `Envelope ${what} is not base64url JSON`);
29
34
  }
30
35
  }
31
36
  function parseHeader(part) {
32
37
  const result = JwsHeader.safeParse(parseJson(part, 'header'));
33
38
  if (!result.success) {
34
- throw new EnvelopeError('Envelope header must be alg EdDSA with a kid');
39
+ throw new EnvelopeError('malformed', 'Envelope header must be alg EdDSA with a kid');
35
40
  }
36
41
  return result.data;
37
42
  }
@@ -39,7 +44,7 @@ export async function sign(payload, privateKey, kid) {
39
44
  const header = JwsHeader.parse({ alg: 'EdDSA', kid });
40
45
  const body = JSON.stringify(payload);
41
46
  if (body === undefined)
42
- throw new EnvelopeError('Payload is not JSON');
47
+ throw new EnvelopeError('invalid_input', 'Payload is not JSON');
43
48
  const signingInput = `${base64urlEncode(utf8Encode(JSON.stringify(header)))}.${base64urlEncode(utf8Encode(body))}`;
44
49
  const signature = await signAsync(utf8Encode(signingInput), privateKey);
45
50
  return `${signingInput}.${base64urlEncode(signature)}`;
@@ -64,7 +69,7 @@ export async function verify(jws, publicKey) {
64
69
  ok = false;
65
70
  }
66
71
  if (!ok)
67
- throw new EnvelopeError('Envelope signature is invalid');
72
+ throw new EnvelopeError('bad_signature', 'Envelope signature is invalid');
68
73
  const header = parseHeader(parts.header);
69
74
  return { header, payload: parseJson(parts.payload, 'payload') };
70
75
  }
@@ -95,26 +100,26 @@ export function audienceOf(apiUrl) {
95
100
  url = new URL(apiUrl);
96
101
  }
97
102
  catch {
98
- throw new EnvelopeError('API URL is not a URL');
103
+ throw new EnvelopeError('invalid_input', 'API URL is not a URL');
99
104
  }
100
105
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
101
- throw new EnvelopeError('API URL must be http or https');
106
+ throw new EnvelopeError('invalid_input', 'API URL must be http or https');
102
107
  }
103
108
  if (url.origin === 'null') {
104
- throw new EnvelopeError('API URL has no origin');
109
+ throw new EnvelopeError('invalid_input', 'API URL has no origin');
105
110
  }
106
111
  return url.origin;
107
112
  }
108
113
  // A copy of the payload with `aud` added. Never mutates the input.
109
114
  export function withAudience(payload, aud) {
110
115
  if (!Audience.safeParse(aud).success) {
111
- throw new EnvelopeError('aud must be an origin');
116
+ throw new EnvelopeError('invalid_input', 'aud must be an origin');
112
117
  }
113
118
  if (Array.isArray(payload)) {
114
- throw new EnvelopeError('A signed request payload must be an object');
119
+ throw new EnvelopeError('invalid_input', 'A signed request payload must be an object');
115
120
  }
116
121
  if (Object.hasOwn(payload, 'aud')) {
117
- throw new EnvelopeError('Payload already has an aud');
122
+ throw new EnvelopeError('invalid_input', 'Payload already has an aud');
118
123
  }
119
124
  return { ...payload, aud };
120
125
  }
package/dist/goal.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { z } from 'zod';
2
- export declare const GOAL_ACTION_CODES: readonly ['dormant', 'confirm_outcomes', 'addressed_waiting', 'claim_seed_tasks', 'claim_tasks', 'counterparty_tasks', 'need_operators', 'history_days', 'reliability_below', 'safety_below', 'safety_incident_window', 'provenance_below', 'declare_model', 'operator_unverified', 'need_ratings', 'version_cap', 'report_as_poster'];
2
+ export declare const GOAL_ACTION_CODES: readonly ['dormant', 'operator_verification_lapsing', 'confirm_outcomes', 'addressed_waiting', 'claim_seed_tasks', 'claim_tasks', 'counterparty_tasks', 'need_operators', 'history_days', 'reliability_below', 'safety_below', 'safety_incident_window', 'clean_days', 'provenance_below', 'declare_model', 'operator_unverified', 'need_ratings', 'version_cap', 'operator_silver_cap', 'report_as_poster'];
3
3
  export declare const GoalActionCode: z.ZodEnum<{
4
4
  addressed_waiting: "addressed_waiting";
5
5
  claim_seed_tasks: "claim_seed_tasks";
6
6
  claim_tasks: "claim_tasks";
7
+ clean_days: "clean_days";
7
8
  confirm_outcomes: "confirm_outcomes";
8
9
  counterparty_tasks: "counterparty_tasks";
9
10
  declare_model: "declare_model";
@@ -11,7 +12,9 @@ export declare const GoalActionCode: z.ZodEnum<{
11
12
  history_days: "history_days";
12
13
  need_operators: "need_operators";
13
14
  need_ratings: "need_ratings";
15
+ operator_silver_cap: "operator_silver_cap";
14
16
  operator_unverified: "operator_unverified";
17
+ operator_verification_lapsing: "operator_verification_lapsing";
15
18
  provenance_below: "provenance_below";
16
19
  reliability_below: "reliability_below";
17
20
  report_as_poster: "report_as_poster";
@@ -31,8 +34,42 @@ export type GoalThreshold = z.infer<typeof GoalThreshold>;
31
34
  export declare const GoalAction: z.ZodObject<{
32
35
  code: z.ZodString;
33
36
  count: z.ZodNullable<z.ZodInt>;
37
+ until: z.ZodOptional<z.ZodISODateTime>;
34
38
  }, z.core.$strict>;
35
39
  export type GoalAction = z.infer<typeof GoalAction>;
40
+ export declare const GOAL_LADDER_STATES: readonly ['reached', 'next', 'locked', 'reserved'];
41
+ export declare const GoalLadderState: z.ZodEnum<{
42
+ locked: "locked";
43
+ next: "next";
44
+ reached: "reached";
45
+ reserved: "reserved";
46
+ }>;
47
+ export type GoalLadderState = z.infer<typeof GoalLadderState>;
48
+ export declare const GoalLadderStep: z.ZodObject<{
49
+ level: z.ZodEnum<{
50
+ bronze: "bronze";
51
+ gold: "gold";
52
+ none: "none";
53
+ platinum: "platinum";
54
+ silver: "silver";
55
+ }>;
56
+ state: z.ZodEnum<{
57
+ locked: "locked";
58
+ next: "next";
59
+ reached: "reached";
60
+ reserved: "reserved";
61
+ }>;
62
+ }, z.core.$strict>;
63
+ export type GoalLadderStep = z.infer<typeof GoalLadderStep>;
64
+ export declare const GoalStep: z.ZodObject<{
65
+ code: z.ZodString;
66
+ done: z.ZodBoolean;
67
+ progress: z.ZodNullable<z.ZodObject<{
68
+ current: z.ZodNumber;
69
+ required: z.ZodNumber;
70
+ }, z.core.$strict>>;
71
+ }, z.core.$strict>;
72
+ export type GoalStep = z.infer<typeof GoalStep>;
36
73
  export declare const GoalToday: z.ZodObject<{
37
74
  day: z.ZodISODate;
38
75
  counted: z.ZodInt;
@@ -61,6 +98,21 @@ export declare const GoalResponse: z.ZodObject<{
61
98
  none: "none";
62
99
  silver: "silver";
63
100
  }>>;
101
+ ladder: z.ZodArray<z.ZodObject<{
102
+ level: z.ZodEnum<{
103
+ bronze: "bronze";
104
+ gold: "gold";
105
+ none: "none";
106
+ platinum: "platinum";
107
+ silver: "silver";
108
+ }>;
109
+ state: z.ZodEnum<{
110
+ locked: "locked";
111
+ next: "next";
112
+ reached: "reached";
113
+ reserved: "reserved";
114
+ }>;
115
+ }, z.core.$strict>>;
64
116
  thresholds: z.ZodArray<z.ZodObject<{
65
117
  name: z.ZodString;
66
118
  current: z.ZodNumber;
@@ -68,9 +120,18 @@ export declare const GoalResponse: z.ZodObject<{
68
120
  met: z.ZodBoolean;
69
121
  raw: z.ZodNullable<z.ZodNumber>;
70
122
  }, z.core.$strict>>;
123
+ steps: z.ZodArray<z.ZodObject<{
124
+ code: z.ZodString;
125
+ done: z.ZodBoolean;
126
+ progress: z.ZodNullable<z.ZodObject<{
127
+ current: z.ZodNumber;
128
+ required: z.ZodNumber;
129
+ }, z.core.$strict>>;
130
+ }, z.core.$strict>>;
71
131
  actions: z.ZodArray<z.ZodObject<{
72
132
  code: z.ZodString;
73
133
  count: z.ZodNullable<z.ZodInt>;
134
+ until: z.ZodOptional<z.ZodISODateTime>;
74
135
  }, z.core.$strict>>;
75
136
  pending: z.ZodObject<{
76
137
  addressed: z.ZodInt;