@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/credential.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { AgentId, Ed25519PublicKey } from './agent-id.js';
|
|
3
|
-
import { Dimension } from './dimensions.js';
|
|
3
|
+
import { AcceptedDimension, Dimension } from './dimensions.js';
|
|
4
4
|
import { Jws } from './envelope.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { StoredVersion } from './events.js';
|
|
6
|
+
import { HANDSHAKE_MAX_CHARS } from './handshake.js';
|
|
7
|
+
import { CountedCounts, CountedCountsV3, Level, StandingCounts, StandingCountsV3, } from './standing.js';
|
|
7
8
|
// The issuer every SEAL carries as iss from the SealKeeper cutover on.
|
|
8
9
|
export const CREDENTIAL_ISSUER = 'sealkeeper.run';
|
|
9
10
|
// Issuers a verifier still accepts for a while after the cutover. A SEAL
|
|
@@ -29,7 +30,6 @@ export function acceptedIssuer(iss, nowSeconds) {
|
|
|
29
30
|
export const WELL_KNOWN_PATH = '/.well-known/seal.json';
|
|
30
31
|
export const LEGACY_WELL_KNOWN_PATH = '/.well-known/vouched.json';
|
|
31
32
|
export const WELL_KNOWN_URL = `https://${CREDENTIAL_ISSUER}${WELL_KNOWN_PATH}`;
|
|
32
|
-
export const LEGACY_WELL_KNOWN_URL = `https://vouched.run${LEGACY_WELL_KNOWN_PATH}`;
|
|
33
33
|
// The card extension a SEAL travels under. The two old URIs are kept beside
|
|
34
34
|
// it for one release, VOU-77 drops them.
|
|
35
35
|
export const SEAL_EXTENSION_URI = 'https://sealkeeper.run/ext/seal/v1';
|
|
@@ -51,17 +51,14 @@ const Count = z.int().min(0);
|
|
|
51
51
|
// The version of the SEAL standard this issuer writes, carried as ver.
|
|
52
52
|
export const SEAL_VERSION = 1;
|
|
53
53
|
// Every version a verifier understands. Version 2 is version 1 plus
|
|
54
|
-
// `counted`, the counted evidence the level read (VOU-139).
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
// ver is broken, unsupported_version. Unix seconds, the first second no
|
|
63
|
-
// longer accepted.
|
|
64
|
-
export const LEGACY_UNTIL = Date.UTC(2026, 8, 26) / 1000;
|
|
54
|
+
// `counted`, the counted evidence the level read (VOU-139). Version 3 is
|
|
55
|
+
// version 2 plus the posted counts, `fingerprint` and `state`, with the
|
|
56
|
+
// deprecated `version` dropped (VOU-77). CLIs before 0.4.5 understand
|
|
57
|
+
// version 1 only, and CLIs before 0.4.11 do not understand version 3, and
|
|
58
|
+
// each refuses a ver it does not know. So the issuer keeps writing version
|
|
59
|
+
// 1 until every CLI before 0.4.11 is gone. Verifiers accept all three from
|
|
60
|
+
// CLI 0.4.11 on. SEAL standard section 9.
|
|
61
|
+
export const SEAL_VERSIONS = [1, 2, 3];
|
|
65
62
|
// One identity attestation reference, SEAL standard section 4a. A pointer
|
|
66
63
|
// to an attestation an external provider made, never the attestation, a
|
|
67
64
|
// name or a tenant id. kind is one of the four names or a URL for anything
|
|
@@ -83,8 +80,16 @@ export const IdentityReference = z.strictObject({
|
|
|
83
80
|
// revoked, so every reference it still holds is current.
|
|
84
81
|
export const operatorVerified = (identity) => identity.some((r) => r.scope === 'operator');
|
|
85
82
|
// Each score is 0 to 1, or null when unearned (standard section 2). A
|
|
86
|
-
// payload with a score outside that is malformed.
|
|
87
|
-
|
|
83
|
+
// payload with a score outside that is malformed. The issuer writes the
|
|
84
|
+
// base dimensions and competence by category only (Dimension). A verifier
|
|
85
|
+
// also accepts competence by task type (AcceptedDimension), the key SEALs
|
|
86
|
+
// carried before RT-3, so a SEAL issued before the change stays valid until
|
|
87
|
+
// it expires and a verifier never has to know which keys an issuer writes
|
|
88
|
+
// today. The API reads its stored SEALs back with the issued shape, so a
|
|
89
|
+
// stored SEAL with a task type key fails that parse and is reissued.
|
|
90
|
+
const Score = z.number().min(0).max(1).nullable();
|
|
91
|
+
const IssuedScores = z.partialRecord(Dimension, Score);
|
|
92
|
+
const VerifiedScores = z.partialRecord(AcceptedDimension, Score);
|
|
88
93
|
// A SEAL lives at most 24 hours from issue, standard section 2. The issuer's
|
|
89
94
|
// CREDENTIAL_TTL_HOURS may not go above it, and a payload whose exp is
|
|
90
95
|
// further from its iat is malformed.
|
|
@@ -99,6 +104,26 @@ export const SEAL_CLOCK_SKEW_SECONDS = 300;
|
|
|
99
104
|
// and parseSealPayload checks the clock with acceptedIssuer first.
|
|
100
105
|
const IssuedIssuer = z.literal(CREDENTIAL_ISSUER);
|
|
101
106
|
const VerifiedIssuer = z.enum([CREDENTIAL_ISSUER, ...LEGACY_ISSUERS]);
|
|
107
|
+
// The state of a version 3 SEAL, how the agent's current fingerprint stands
|
|
108
|
+
// against the one its level was last confirmed under. The set is closed.
|
|
109
|
+
// matches is the only value the issuer writes until the re-check flow
|
|
110
|
+
// exists. changed and provisional are reserved for it, so a verifier of
|
|
111
|
+
// version 3 already reads them.
|
|
112
|
+
export const SealState = z.enum(['matches', 'changed', 'provisional']);
|
|
113
|
+
// The fingerprint of a version 3 SEAL. hash is SHA-256 over bytes a later
|
|
114
|
+
// change to the standard defines, base64url, and a verifier treats it as
|
|
115
|
+
// opaque until then. at is Unix seconds of when the level was last
|
|
116
|
+
// confirmed under it, never after iat. null when the agent has sent no
|
|
117
|
+
// fingerprint. A 32 byte value in base64url has 43 characters and only
|
|
118
|
+
// some last characters, the rule agent-id.ts uses for a key.
|
|
119
|
+
export const SealFingerprint = z
|
|
120
|
+
.strictObject({
|
|
121
|
+
hash: z
|
|
122
|
+
.string()
|
|
123
|
+
.regex(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/, 'SHA-256, base64url'),
|
|
124
|
+
at: Seconds,
|
|
125
|
+
})
|
|
126
|
+
.nullable();
|
|
102
127
|
const withinMaxTtl = (c) => c.exp - c.iat <= SEAL_MAX_TTL_SECONDS;
|
|
103
128
|
const MAX_TTL_MESSAGE = 'exp must be at most 24 hours after iat';
|
|
104
129
|
/*
|
|
@@ -111,17 +136,17 @@ const MAX_TTL_MESSAGE = 'exp must be at most 24 hours after iat';
|
|
|
111
136
|
* Unix seconds of the agent's newest accepted event, null when it has sent
|
|
112
137
|
* none, and dormant_days whole days since then at issue, null with it.
|
|
113
138
|
*/
|
|
114
|
-
const v1Payload = (iss) => z
|
|
139
|
+
const v1Payload = (iss, scores) => z
|
|
115
140
|
.strictObject({
|
|
116
141
|
iss,
|
|
117
142
|
sub: AgentId,
|
|
118
143
|
ver: z.literal(SEAL_VERSION),
|
|
119
144
|
iat: Seconds,
|
|
120
145
|
exp: Seconds,
|
|
121
|
-
agent_version:
|
|
122
|
-
version:
|
|
146
|
+
agent_version: StoredVersion,
|
|
147
|
+
version: StoredVersion,
|
|
123
148
|
level: Level,
|
|
124
|
-
scores
|
|
149
|
+
scores,
|
|
125
150
|
counts: StandingCounts,
|
|
126
151
|
operator: z.strictObject({ verified: z.boolean() }),
|
|
127
152
|
identity: z.array(IdentityReference).max(16),
|
|
@@ -136,17 +161,17 @@ const v1Payload = (iss) => z
|
|
|
136
161
|
* counted evidence the level read (CountedCounts). counts stay the raw
|
|
137
162
|
* facts. Each counted value is at most its raw count.
|
|
138
163
|
*/
|
|
139
|
-
const v2Payload = (iss) => z
|
|
164
|
+
const v2Payload = (iss, scores) => z
|
|
140
165
|
.strictObject({
|
|
141
166
|
iss,
|
|
142
167
|
sub: AgentId,
|
|
143
168
|
ver: z.literal(2),
|
|
144
169
|
iat: Seconds,
|
|
145
170
|
exp: Seconds,
|
|
146
|
-
agent_version:
|
|
147
|
-
version:
|
|
171
|
+
agent_version: StoredVersion,
|
|
172
|
+
version: StoredVersion,
|
|
148
173
|
level: Level,
|
|
149
|
-
scores
|
|
174
|
+
scores,
|
|
150
175
|
counts: StandingCounts,
|
|
151
176
|
counted: CountedCounts,
|
|
152
177
|
operator: z.strictObject({ verified: z.boolean() }),
|
|
@@ -160,65 +185,80 @@ const v2Payload = (iss) => z
|
|
|
160
185
|
.refine((c) => c.counted.seed_tasks <= c.counts.seed_tasks &&
|
|
161
186
|
c.counted.server_checked_tasks <= c.counts.server_checked_tasks &&
|
|
162
187
|
c.counted.confirmed_tasks <= c.counts.confirmed_tasks, 'counted must not exceed counts');
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
188
|
+
/*
|
|
189
|
+
* Version 3. Every version 2 field with the same meaning except version,
|
|
190
|
+
* which is dropped (VOU-77), so agent_version alone names the agent
|
|
191
|
+
* version. counts gains the three posted counts (StandingCountsV3), counted
|
|
192
|
+
* gains the counted twins of posted_tasks and posted_confirmed_tasks
|
|
193
|
+
* (CountedCountsV3), and fingerprint and state are new. Each counted value
|
|
194
|
+
* is at most its raw count, and fingerprint.at is not after iat. Nothing
|
|
195
|
+
* issues it yet.
|
|
196
|
+
*/
|
|
197
|
+
const v3Payload = (iss, scores) => z
|
|
167
198
|
.strictObject({
|
|
168
199
|
iss,
|
|
169
200
|
sub: AgentId,
|
|
201
|
+
ver: z.literal(3),
|
|
170
202
|
iat: Seconds,
|
|
171
203
|
exp: Seconds,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
204
|
+
agent_version: StoredVersion,
|
|
205
|
+
level: Level,
|
|
206
|
+
scores,
|
|
207
|
+
counts: StandingCountsV3,
|
|
208
|
+
counted: CountedCountsV3,
|
|
209
|
+
fingerprint: SealFingerprint,
|
|
210
|
+
state: SealState,
|
|
211
|
+
operator: z.strictObject({ verified: z.boolean() }),
|
|
212
|
+
identity: z.array(IdentityReference).max(16),
|
|
213
|
+
last_active: Seconds.nullable(),
|
|
214
|
+
dormant_days: Count.nullable(),
|
|
179
215
|
})
|
|
180
216
|
.refine((c) => c.exp > c.iat, 'exp must be after iat')
|
|
181
|
-
.refine(withinMaxTtl, MAX_TTL_MESSAGE)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
217
|
+
.refine(withinMaxTtl, MAX_TTL_MESSAGE)
|
|
218
|
+
.refine((c) => c.counted.seed_tasks <= c.counts.seed_tasks &&
|
|
219
|
+
c.counted.server_checked_tasks <= c.counts.server_checked_tasks &&
|
|
220
|
+
c.counted.confirmed_tasks <= c.counts.confirmed_tasks &&
|
|
221
|
+
c.counted.posted_tasks <= c.counts.posted_tasks &&
|
|
222
|
+
c.counted.posted_confirmed_tasks <= c.counts.posted_confirmed_tasks, 'counted must not exceed counts')
|
|
223
|
+
.refine((c) => c.fingerprint === null || c.fingerprint.at <= c.iat, 'fingerprint.at must not be after iat');
|
|
224
|
+
// The issuer side. iss is CREDENTIAL_ISSUER only, and scores carry
|
|
225
|
+
// competence by category only.
|
|
226
|
+
export const CredentialPayload = v1Payload(IssuedIssuer, IssuedScores);
|
|
227
|
+
export const CredentialPayloadV2 = v2Payload(IssuedIssuer, IssuedScores);
|
|
228
|
+
export const CredentialPayloadV3 = v3Payload(IssuedIssuer, IssuedScores);
|
|
229
|
+
// The verifier side. iss may also be a LEGACY_ISSUERS entry, and scores may
|
|
230
|
+
// also carry competence by task type. The shape has
|
|
187
231
|
// no clock, so read these through parseSealPayload, never on their own.
|
|
188
|
-
export const VerifiedCredentialPayload = v1Payload(VerifiedIssuer);
|
|
189
|
-
export const VerifiedCredentialPayloadV2 = v2Payload(VerifiedIssuer);
|
|
190
|
-
export const
|
|
191
|
-
// Any payload a verifier accepts today, version 1,
|
|
192
|
-
//
|
|
232
|
+
export const VerifiedCredentialPayload = v1Payload(VerifiedIssuer, VerifiedScores);
|
|
233
|
+
export const VerifiedCredentialPayloadV2 = v2Payload(VerifiedIssuer, VerifiedScores);
|
|
234
|
+
export const VerifiedCredentialPayloadV3 = v3Payload(VerifiedIssuer, VerifiedScores);
|
|
235
|
+
// Any payload a verifier accepts today, version 1, 2 or 3, from the current
|
|
236
|
+
// issuer or a legacy one. Each version has every field of the one before
|
|
237
|
+
// with the same meaning, except version, which version 3 drops. A reader
|
|
238
|
+
// takes the agent version from agent_version, which all three carry.
|
|
193
239
|
export const SealPayload = z.union([
|
|
194
240
|
VerifiedCredentialPayload,
|
|
195
241
|
VerifiedCredentialPayloadV2,
|
|
196
|
-
|
|
242
|
+
VerifiedCredentialPayloadV3,
|
|
197
243
|
]);
|
|
198
|
-
// A versioned payload carries every legacy field too, so the guard is on
|
|
199
|
-
// ver, the narrower of the two. Its false branch is the legacy shape. The
|
|
200
|
-
// name is kept from when version 1 was the only one.
|
|
201
|
-
export const isSealV1 = (p) => 'ver' in p;
|
|
202
244
|
// The counted evidence a SEAL carries, version 2 on, null before.
|
|
203
245
|
export const sealCounted = (p) => 'counted' in p ? p.counted : null;
|
|
204
246
|
const hasVer = (raw) => typeof raw === 'object' && raw !== null && 'ver' in raw;
|
|
205
247
|
/*
|
|
206
248
|
* Whether a verifier understands the version of a signed payload. Read
|
|
207
|
-
* after the signature and the issuer check out and before the shape. ver 1
|
|
208
|
-
* and
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
249
|
+
* after the signature and the issuer check out and before the shape. ver 1,
|
|
250
|
+
* 2 and 3 (SEAL_VERSIONS) are understood. Any other ver is
|
|
251
|
+
* unsupported_version, as section 9 of the standard says, never valid with
|
|
252
|
+
* an unknown meaning. So is no ver at all, a SEAL issued before version 1,
|
|
253
|
+
* whose last one expired on 26 September 2026. Anything that is not an
|
|
254
|
+
* object is left to the shape check.
|
|
212
255
|
*/
|
|
213
|
-
export function sealVersionProblem(raw
|
|
256
|
+
export function sealVersionProblem(raw) {
|
|
214
257
|
if (typeof raw !== 'object' || raw === null)
|
|
215
258
|
return null;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
: 'unsupported_version';
|
|
220
|
-
}
|
|
221
|
-
return nowSeconds < LEGACY_UNTIL ? null : 'unsupported_version';
|
|
259
|
+
return hasVer(raw) && SEAL_VERSIONS.includes(raw.ver)
|
|
260
|
+
? null
|
|
261
|
+
: 'unsupported_version';
|
|
222
262
|
}
|
|
223
263
|
/*
|
|
224
264
|
* The iat half of step 4 of the standard, section 3. "Check exp is in the
|
|
@@ -232,9 +272,8 @@ export function sealIatProblem(iat, nowSeconds) {
|
|
|
232
272
|
return iat - nowSeconds > SEAL_CLOCK_SKEW_SECONDS ? 'not_yet_valid' : null;
|
|
233
273
|
}
|
|
234
274
|
// The issuer, then the version check above, then the shape for that
|
|
235
|
-
// version, in the standard's order.
|
|
236
|
-
//
|
|
237
|
-
// acceptedIssuer and sealVersionProblem itself.
|
|
275
|
+
// version, in the standard's order. verifySeal reads a verified payload
|
|
276
|
+
// through this.
|
|
238
277
|
export function parseSealPayload(raw, nowSeconds) {
|
|
239
278
|
const iss = typeof raw === 'object' && raw !== null && 'iss' in raw
|
|
240
279
|
? raw.iss
|
|
@@ -242,12 +281,13 @@ export function parseSealPayload(raw, nowSeconds) {
|
|
|
242
281
|
if (!acceptedIssuer(iss, nowSeconds)) {
|
|
243
282
|
return { ok: false, reason: 'wrong_issuer' };
|
|
244
283
|
}
|
|
245
|
-
const problem = sealVersionProblem(raw
|
|
284
|
+
const problem = sealVersionProblem(raw);
|
|
246
285
|
if (problem)
|
|
247
286
|
return { ok: false, reason: problem };
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
287
|
+
const ver = hasVer(raw) ? raw.ver : undefined;
|
|
288
|
+
const parsed = (ver === 3
|
|
289
|
+
? VerifiedCredentialPayloadV3
|
|
290
|
+
: ver === 2
|
|
251
291
|
? VerifiedCredentialPayloadV2
|
|
252
292
|
: VerifiedCredentialPayload).safeParse(raw);
|
|
253
293
|
return parsed.success
|
|
@@ -278,10 +318,17 @@ export const LegacySealExtension = z.strictObject({
|
|
|
278
318
|
description: z.string().min(1).max(512),
|
|
279
319
|
params: z.strictObject({ credential: Jws }),
|
|
280
320
|
});
|
|
321
|
+
// The current extension may also carry handshake, the agent's signed
|
|
322
|
+
// handshake (VB-6, ./handshake.ts) beside the SEAL, refreshed each time the
|
|
323
|
+
// card is written. A reader takes params loosely, so one that does not know
|
|
324
|
+
// handshake ignores it. The two old URIs carry the SEAL alone.
|
|
281
325
|
export const SealExtension = z.strictObject({
|
|
282
326
|
uri: z.literal(SEAL_EXTENSION_URI),
|
|
283
327
|
description: z.string().min(1).max(512),
|
|
284
|
-
params: z.strictObject({
|
|
328
|
+
params: z.strictObject({
|
|
329
|
+
credential: Jws,
|
|
330
|
+
handshake: Jws.max(HANDSHAKE_MAX_CHARS).optional(),
|
|
331
|
+
}),
|
|
285
332
|
});
|
|
286
333
|
export const credentialExtension = (jws) => ({
|
|
287
334
|
uri: CREDENTIAL_EXTENSION_URI,
|
|
@@ -293,15 +340,17 @@ export const legacySealExtension = (jws) => ({
|
|
|
293
340
|
description: EXTENSION_DESCRIPTION,
|
|
294
341
|
params: { credential: jws },
|
|
295
342
|
});
|
|
296
|
-
export const sealExtension = (jws) => ({
|
|
343
|
+
export const sealExtension = (jws, handshake) => ({
|
|
297
344
|
uri: SEAL_EXTENSION_URI,
|
|
298
345
|
description: EXTENSION_DESCRIPTION,
|
|
299
|
-
params:
|
|
346
|
+
params: handshake === undefined
|
|
347
|
+
? { credential: jws }
|
|
348
|
+
: { credential: jws, handshake },
|
|
300
349
|
});
|
|
301
350
|
// The extensions a card carries for one SEAL, in SEAL_EXTENSION_URIS order,
|
|
302
|
-
// the current URI first.
|
|
303
|
-
export const sealExtensions = (jws) => [
|
|
304
|
-
sealExtension(jws),
|
|
351
|
+
// the current URI first. A handshake goes on the current one only.
|
|
352
|
+
export const sealExtensions = (jws, handshake) => [
|
|
353
|
+
sealExtension(jws, handshake),
|
|
305
354
|
legacySealExtension(jws),
|
|
306
355
|
credentialExtension(jws),
|
|
307
356
|
];
|
|
@@ -314,9 +363,8 @@ export const AgentSkill = z.strictObject({
|
|
|
314
363
|
examples: z.array(z.string().min(1).max(1024)).max(32).optional(),
|
|
315
364
|
});
|
|
316
365
|
const Modes = z.array(z.string().min(1).max(128)).max(32).default(['text']);
|
|
317
|
-
// The subset of the A2A agent card that
|
|
318
|
-
// never writes a field it did not mean to.
|
|
319
|
-
// the loose AgentCardResponse instead.
|
|
366
|
+
// The subset of the A2A agent card that SealKeeper emits, strict so the
|
|
367
|
+
// CLI never writes a field it did not mean to.
|
|
320
368
|
export const AgentCard = z.strictObject({
|
|
321
369
|
protocolVersion: z.literal(A2A_PROTOCOL_VERSION),
|
|
322
370
|
name: z.string().min(1).max(128),
|
package/dist/dimensions.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { TaskCategory } from './tasks.js';
|
|
2
3
|
export declare const TaskType: z.ZodString;
|
|
3
4
|
export type TaskType = z.infer<typeof TaskType>;
|
|
4
5
|
export declare const BaseDimension: z.ZodEnum<{
|
|
@@ -8,14 +9,26 @@ export declare const BaseDimension: z.ZodEnum<{
|
|
|
8
9
|
safety: "safety";
|
|
9
10
|
}>;
|
|
10
11
|
export type BaseDimension = z.infer<typeof BaseDimension>;
|
|
11
|
-
export declare const CompetenceDimension: z.ZodTemplateLiteral
|
|
12
|
+
export declare const CompetenceDimension: z.ZodTemplateLiteral<"competence:code" | "competence:conversation" | "competence:data" | "competence:operations" | "competence:other" | "competence:research" | "competence:writing">;
|
|
12
13
|
export type CompetenceDimension = z.infer<typeof CompetenceDimension>;
|
|
14
|
+
export declare const COMPETENCE_TYPES_MAX = 32;
|
|
15
|
+
export declare const COMPETENCE_DIMENSIONS: readonly CompetenceDimension[];
|
|
13
16
|
export declare const Dimension: z.ZodUnion<readonly [z.ZodEnum<{
|
|
14
17
|
cost_latency: "cost_latency";
|
|
15
18
|
provenance: "provenance";
|
|
16
19
|
reliability: "reliability";
|
|
17
20
|
safety: "safety";
|
|
18
|
-
}>, z.ZodTemplateLiteral
|
|
21
|
+
}>, z.ZodTemplateLiteral<"competence:code" | "competence:conversation" | "competence:data" | "competence:operations" | "competence:other" | "competence:research" | "competence:writing">]>;
|
|
19
22
|
export type Dimension = z.infer<typeof Dimension>;
|
|
20
|
-
export declare const
|
|
23
|
+
export declare const TypeCompetenceDimension: z.ZodTemplateLiteral<`competence:${string}`>;
|
|
24
|
+
export type TypeCompetenceDimension = z.infer<typeof TypeCompetenceDimension>;
|
|
25
|
+
export declare const AcceptedDimension: z.ZodUnion<readonly [z.ZodEnum<{
|
|
26
|
+
cost_latency: "cost_latency";
|
|
27
|
+
provenance: "provenance";
|
|
28
|
+
reliability: "reliability";
|
|
29
|
+
safety: "safety";
|
|
30
|
+
}>, z.ZodTemplateLiteral<"competence:code" | "competence:conversation" | "competence:data" | "competence:operations" | "competence:other" | "competence:research" | "competence:writing">, z.ZodTemplateLiteral<`competence:${string}`>]>;
|
|
31
|
+
export type AcceptedDimension = z.infer<typeof AcceptedDimension>;
|
|
32
|
+
export declare const competence: (category: TaskCategory) => CompetenceDimension;
|
|
33
|
+
export declare const categoryOf: (dimension: string) => TaskCategory | null;
|
|
21
34
|
export declare const isTrustDimension: (dimension: string) => boolean;
|
package/dist/dimensions.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { TASK_CATEGORIES, TaskCategory } from './tasks.js';
|
|
2
3
|
export const TaskType = z.string().regex(/^[a-z0-9_-]{1,32}$/);
|
|
3
4
|
export const BaseDimension = z.enum([
|
|
4
5
|
'reliability',
|
|
@@ -6,9 +7,54 @@ export const BaseDimension = z.enum([
|
|
|
6
7
|
'cost_latency',
|
|
7
8
|
'provenance',
|
|
8
9
|
]);
|
|
9
|
-
|
|
10
|
+
/*
|
|
11
|
+
* Competence rolls up to the task's category (RT-3), one dimension per
|
|
12
|
+
* category, competence:data and so on. The per task type values stay a
|
|
13
|
+
* breakdown under the category on the score answer and are never a
|
|
14
|
+
* dimension of their own.
|
|
15
|
+
*/
|
|
16
|
+
export const CompetenceDimension = z.templateLiteral([
|
|
17
|
+
'competence:',
|
|
18
|
+
TaskCategory,
|
|
19
|
+
]);
|
|
20
|
+
// The most task types a competence entry's breakdown lists, the most
|
|
21
|
+
// claimed first, so the answer stays small however many free task types an
|
|
22
|
+
// agent took.
|
|
23
|
+
export const COMPETENCE_TYPES_MAX = 32;
|
|
24
|
+
// The seven competence dimensions in category order, the order every list
|
|
25
|
+
// shows them in.
|
|
26
|
+
export const COMPETENCE_DIMENSIONS = TASK_CATEGORIES.map((c) => CompetenceDimension.parse(`competence:${c}`));
|
|
27
|
+
// What the issuer writes and what a request may name, the base dimensions
|
|
28
|
+
// and the seven competence categories.
|
|
10
29
|
export const Dimension = z.union([BaseDimension, CompetenceDimension]);
|
|
11
|
-
|
|
30
|
+
/*
|
|
31
|
+
* competence:<task_type>, the competence key SEALs carried before RT-3,
|
|
32
|
+
* when competence was per task type. The issuer no longer writes it. Every category is also a valid task type, so this pattern
|
|
33
|
+
* takes both kinds of key.
|
|
34
|
+
*/
|
|
35
|
+
export const TypeCompetenceDimension = z.templateLiteral([
|
|
36
|
+
'competence:',
|
|
37
|
+
TaskType,
|
|
38
|
+
]);
|
|
39
|
+
/*
|
|
40
|
+
* Any dimension a reader accepts, issued today or before RT-3. A verifier
|
|
41
|
+
* reads SEAL scores with it, so a SEAL issued with a task type key stays
|
|
42
|
+
* valid for the rest of its 24 hours, and so do stored feed items that
|
|
43
|
+
* name one. Never used for what the issuer writes or a request names.
|
|
44
|
+
*/
|
|
45
|
+
export const AcceptedDimension = z.union([
|
|
46
|
+
BaseDimension,
|
|
47
|
+
CompetenceDimension,
|
|
48
|
+
TypeCompetenceDimension,
|
|
49
|
+
]);
|
|
50
|
+
export const competence = (category) => CompetenceDimension.parse(`competence:${category}`);
|
|
51
|
+
// The category inside a competence dimension, null for any other.
|
|
52
|
+
export const categoryOf = (dimension) => {
|
|
53
|
+
const parsed = CompetenceDimension.safeParse(dimension);
|
|
54
|
+
return parsed.success
|
|
55
|
+
? parsed.data.slice('competence:'.length)
|
|
56
|
+
: null;
|
|
57
|
+
};
|
|
12
58
|
// Reliability, safety, provenance and every competence dimension are trust.
|
|
13
59
|
// They need verified work from another operator before they get a value.
|
|
14
60
|
// cost_latency comes from usage events and is information, not trust.
|
package/dist/events.d.ts
CHANGED
package/dist/events.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { TaskType } from './dimensions.js';
|
|
3
|
+
import { HIDDEN } from './hidden.js';
|
|
3
4
|
import { Sha256Hex, TaskOutcome } from './tasks.js';
|
|
4
5
|
/*
|
|
5
6
|
* The Zod pattern, shown once. Every schema file in this package follows it.
|
|
@@ -25,7 +26,18 @@ import { Sha256Hex, TaskOutcome } from './tasks.js';
|
|
|
25
26
|
*
|
|
26
27
|
* Use parse to throw on bad input and safeParse to get { success, data, error }.
|
|
27
28
|
*/
|
|
28
|
-
|
|
29
|
+
/*
|
|
30
|
+
* An agent version as it is stored and read back. Only the length rule,
|
|
31
|
+
* since a row written before Version had a character rule may hold
|
|
32
|
+
* anything. Every answer and every read of a stored row uses this, so an
|
|
33
|
+
* old stored version never throws. New input is parsed with Version.
|
|
34
|
+
*/
|
|
35
|
+
export const StoredVersion = z.string().min(1).max(32);
|
|
36
|
+
// An agent version as a caller sends it, in an event, a registration or a
|
|
37
|
+
// version change. It is signed into the SEAL and shown on public pages, so
|
|
38
|
+
// it refuses the characters OperatorDisplayName refuses (HIDDEN) and a
|
|
39
|
+
// space at either end, on top of the length rule.
|
|
40
|
+
export const Version = StoredVersion.refine((v) => !HIDDEN.test(v), 'No control characters').refine((v) => v === v.trim(), 'No space at the start or end');
|
|
29
41
|
const Name = z
|
|
30
42
|
.string()
|
|
31
43
|
.min(1)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FingerprintParts } from './fingerprint.js';
|
|
2
|
+
export type PartHashVector = {
|
|
3
|
+
name: string;
|
|
4
|
+
sub: string;
|
|
5
|
+
content: string;
|
|
6
|
+
hash: string;
|
|
7
|
+
};
|
|
8
|
+
export type FingerprintVector = {
|
|
9
|
+
name: string;
|
|
10
|
+
sub: string;
|
|
11
|
+
parts: FingerprintParts;
|
|
12
|
+
canonical: string;
|
|
13
|
+
hash: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const FINGERPRINT_VECTOR_SUB = "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo";
|
|
16
|
+
export declare const PART_HASH_VECTORS: readonly PartHashVector[];
|
|
17
|
+
export declare const FINGERPRINT_VECTORS: readonly FingerprintVector[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export const FINGERPRINT_VECTOR_SUB = '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo';
|
|
2
|
+
const sub = FINGERPRINT_VECTOR_SUB;
|
|
3
|
+
const MODEL = '6soeG-P3CXqRWuQ6H4urtEb45deX7u3mbPL5-qAKQLo';
|
|
4
|
+
const PROMPT = 'mLFX469k6BHZ3-B4a8L_jTSM2ZxJY3u4indqI00En-c';
|
|
5
|
+
const TOOLS = 'otJ7Lv3rmxSYzTJO3fcRNAFpmWHzzovNQQJ7HT9pIO8';
|
|
6
|
+
const FRAMEWORK = 'fZr0sK5GawVx1whZ8jTP7ZEHoteH22nOQS0fH4-UeOw';
|
|
7
|
+
export const PART_HASH_VECTORS = [
|
|
8
|
+
{
|
|
9
|
+
name: 'empty content',
|
|
10
|
+
sub,
|
|
11
|
+
content: '',
|
|
12
|
+
hash: '92JJjLzUuso4nM82PbsP-RKqxI1cEBakXLzWFqVoYjg',
|
|
13
|
+
},
|
|
14
|
+
{ name: 'one model id', sub, content: 'claude-sonnet-4-5', hash: MODEL },
|
|
15
|
+
{
|
|
16
|
+
name: 'a prompt',
|
|
17
|
+
sub,
|
|
18
|
+
content: 'You are the research agent alice runs.',
|
|
19
|
+
hash: PROMPT,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'sorted tool lines',
|
|
23
|
+
sub,
|
|
24
|
+
content: 'mcp:github\nmcp:linear\ntool:mcp__github__create_issue',
|
|
25
|
+
hash: TOOLS,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'a framework and its version',
|
|
29
|
+
sub,
|
|
30
|
+
content: 'claude-code@2.1.283',
|
|
31
|
+
hash: FRAMEWORK,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
export const FINGERPRINT_VECTORS = [
|
|
35
|
+
{
|
|
36
|
+
name: 'every part declared',
|
|
37
|
+
sub,
|
|
38
|
+
parts: {
|
|
39
|
+
model_set: { hash: MODEL },
|
|
40
|
+
prompt: { hash: PROMPT },
|
|
41
|
+
tools: { hash: TOOLS },
|
|
42
|
+
framework: { hash: FRAMEWORK },
|
|
43
|
+
},
|
|
44
|
+
canonical: `${MODEL}\n${PROMPT}\n${TOOLS}\n${FRAMEWORK}`,
|
|
45
|
+
hash: 'unFXnGMKWNOP4f_NnbearJn0prLlxoGia2LaZcqnzy8',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'prompt not declared',
|
|
49
|
+
sub,
|
|
50
|
+
parts: {
|
|
51
|
+
model_set: { hash: MODEL },
|
|
52
|
+
prompt: 'not_declared',
|
|
53
|
+
tools: { hash: TOOLS },
|
|
54
|
+
framework: { hash: FRAMEWORK },
|
|
55
|
+
},
|
|
56
|
+
canonical: `${MODEL}\nnot_declared\n${TOOLS}\n${FRAMEWORK}`,
|
|
57
|
+
hash: 'BHAfx6dALmCdt3aXz-g6iAjLLGDurK95DZm15ZjIVZg',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'tools unstable, prompt not declared',
|
|
61
|
+
sub,
|
|
62
|
+
parts: {
|
|
63
|
+
model_set: { hash: MODEL },
|
|
64
|
+
prompt: 'not_declared',
|
|
65
|
+
tools: 'unstable',
|
|
66
|
+
framework: { hash: FRAMEWORK },
|
|
67
|
+
},
|
|
68
|
+
canonical: `${MODEL}\nnot_declared\nunstable\n${FRAMEWORK}`,
|
|
69
|
+
hash: 'u50ZFfkaGkhaMqciRzxv8meCaFxXycHCr9IasIEPwgk',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'nothing declared',
|
|
73
|
+
sub,
|
|
74
|
+
parts: {
|
|
75
|
+
model_set: 'not_declared',
|
|
76
|
+
prompt: 'not_declared',
|
|
77
|
+
tools: 'not_declared',
|
|
78
|
+
framework: 'not_declared',
|
|
79
|
+
},
|
|
80
|
+
canonical: 'not_declared\nnot_declared\nnot_declared\nnot_declared',
|
|
81
|
+
hash: 'GupgdwZ9bk92irIz_drScpdSxs6gX2CrSEL9A-ndiHI',
|
|
82
|
+
},
|
|
83
|
+
];
|