@sealkeeper/schema 0.0.1 → 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/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +14 -1
- package/dist/agent-id.d.ts +13 -0
- package/dist/agent-id.js +29 -0
- package/dist/agent-name.d.ts +9 -0
- package/dist/agent-name.js +58 -0
- package/dist/api.d.ts +1212 -0
- package/dist/api.js +633 -0
- package/dist/base64url.d.ts +4 -0
- package/dist/base64url.js +43 -0
- package/dist/credential.d.ts +392 -0
- package/dist/credential.js +287 -0
- package/dist/db/agent-renames.d.ts +126 -0
- package/dist/db/agent-renames.js +20 -0
- package/dist/db/agents.d.ts +145 -0
- package/dist/db/agents.js +40 -0
- package/dist/db/client.d.ts +1896 -0
- package/dist/db/client.js +36 -0
- package/dist/db/credentials.d.ts +143 -0
- package/dist/db/credentials.js +16 -0
- package/dist/db/deleted-operators.d.ts +75 -0
- package/dist/db/deleted-operators.js +13 -0
- package/dist/db/events.d.ts +160 -0
- package/dist/db/events.js +34 -0
- package/dist/db/feed-items.d.ts +109 -0
- package/dist/db/feed-items.js +16 -0
- package/dist/db/index.d.ts +56 -0
- package/dist/db/index.js +23 -0
- package/dist/db/migrate.d.ts +1 -0
- package/dist/db/migrate.js +30 -0
- package/dist/db/migrator.d.ts +2 -0
- package/dist/db/migrator.js +20 -0
- package/dist/db/operators.d.ts +109 -0
- package/dist/db/operators.js +11 -0
- package/dist/db/quota-counters.d.ts +109 -0
- package/dist/db/quota-counters.js +17 -0
- package/dist/db/ratings.d.ts +160 -0
- package/dist/db/ratings.js +27 -0
- package/dist/db/scores.d.ts +160 -0
- package/dist/db/scores.js +15 -0
- package/dist/db/standing.d.ts +228 -0
- package/dist/db/standing.js +29 -0
- package/dist/db/task-outcomes.d.ts +126 -0
- package/dist/db/task-outcomes.js +18 -0
- package/dist/db/tasks.d.ts +251 -0
- package/dist/db/tasks.js +49 -0
- package/dist/db/timestamps.d.ts +4 -0
- package/dist/db/timestamps.js +24 -0
- package/dist/db/url.d.ts +12 -0
- package/dist/db/url.js +25 -0
- package/dist/dimensions.d.ts +21 -0
- package/dist/dimensions.js +15 -0
- package/dist/envelope.d.ts +16 -0
- package/dist/envelope.js +70 -0
- package/dist/events.d.ts +153 -0
- package/dist/events.js +98 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/seal-conformance.d.ts +14 -0
- package/dist/seal-conformance.js +176 -0
- package/dist/standing.d.ts +46 -0
- package/dist/standing.js +79 -0
- package/dist/tasks.d.ts +49 -0
- package/dist/tasks.js +132 -0
- package/dist/top-dimensions.d.ts +9 -0
- package/dist/top-dimensions.js +13 -0
- package/package.json +69 -5
package/dist/db/tasks.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { index, jsonb, pgTable, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
|
|
3
|
+
import { agents } from './agents.js';
|
|
4
|
+
import { timestamps } from './timestamps.js';
|
|
5
|
+
export const tasks = pgTable('tasks', {
|
|
6
|
+
id: uuid('id').primaryKey(),
|
|
7
|
+
posterAgentId: text('poster_agent_id')
|
|
8
|
+
.notNull()
|
|
9
|
+
.references(() => agents.id),
|
|
10
|
+
claimantAgentId: text('claimant_agent_id').references(() => agents.id),
|
|
11
|
+
taskType: text('task_type').notNull(),
|
|
12
|
+
spec: jsonb('spec').notNull(),
|
|
13
|
+
verification: jsonb('verification').notNull(),
|
|
14
|
+
postedAt: timestamp('posted_at', { withTimezone: true })
|
|
15
|
+
.notNull()
|
|
16
|
+
.defaultNow(),
|
|
17
|
+
claimedAt: timestamp('claimed_at', { withTimezone: true }),
|
|
18
|
+
submittedAt: timestamp('submitted_at', { withTimezone: true }),
|
|
19
|
+
verifiedAt: timestamp('verified_at', { withTimezone: true }),
|
|
20
|
+
// What the claimant submitted, null until submit. Only the poster and the
|
|
21
|
+
// claimant may read it. Wrapped in an object because a bare jsonb string
|
|
22
|
+
// does not survive a read. postgres-js parses jsonb and Drizzle then
|
|
23
|
+
// JSON.parses any string it gets back, so "123" would return as 123.
|
|
24
|
+
submission: jsonb('submission').$type(),
|
|
25
|
+
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
|
|
26
|
+
...timestamps,
|
|
27
|
+
},
|
|
28
|
+
/*
|
|
29
|
+
* Secondary indexes, one per hot read. A partial index, shown once, keeps
|
|
30
|
+
* only the rows its where clause matches, so the open and claimed lists
|
|
31
|
+
* stay small however many tasks have settled. The where clauses match the
|
|
32
|
+
* state derivation in apps/api/src/routes/tasks.ts (stateWhere).
|
|
33
|
+
*/
|
|
34
|
+
(t) => [
|
|
35
|
+
// The open task cap per poster, and a deletion's tasks by poster.
|
|
36
|
+
index('tasks_poster_agent_id_idx').on(t.posterAgentId),
|
|
37
|
+
// The claim cap per claimant, activity counts, scoring and evidence.
|
|
38
|
+
index('tasks_claimant_agent_id_idx').on(t.claimantAgentId),
|
|
39
|
+
// GET /v1/tasks?state=open, oldest first.
|
|
40
|
+
index('tasks_open_posted_at_idx')
|
|
41
|
+
.on(t.postedAt, t.id)
|
|
42
|
+
.where(sql `${t.claimedAt} is null and ${t.verifiedAt} is null`),
|
|
43
|
+
// GET /v1/tasks?state=claimed, oldest first.
|
|
44
|
+
index('tasks_claimed_posted_at_idx')
|
|
45
|
+
.on(t.postedAt, t.id)
|
|
46
|
+
.where(sql `${t.claimedAt} is not null and ${t.submittedAt} is null and ${t.verifiedAt} is null`),
|
|
47
|
+
// Evidence counts over the window and the verified task count in stats.
|
|
48
|
+
index('tasks_verified_at_idx').on(t.verifiedAt),
|
|
49
|
+
]);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const timestamps: {
|
|
2
|
+
createdAt: import("drizzle-orm").HasDefault<import("drizzle-orm").NotNull<import("drizzle-orm/pg-core").PgTimestampBuilderInitial<"created_at">>>;
|
|
3
|
+
updatedAt: import("drizzle-orm").HasDefault<import("drizzle-orm").NotNull<import("drizzle-orm/pg-core").PgTimestampBuilderInitial<"updated_at">>>;
|
|
4
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { timestamp } from 'drizzle-orm/pg-core';
|
|
2
|
+
// Shared columns for every table except events.
|
|
3
|
+
//
|
|
4
|
+
// A column builder like timestamp('created_at', ...) describes a column but
|
|
5
|
+
// is not bound to a table yet. pgTable binds it when the table is declared,
|
|
6
|
+
// so one plain object of builders can be spread into many tables.
|
|
7
|
+
//
|
|
8
|
+
// export const agents = pgTable('agents', {
|
|
9
|
+
// id: text('id').primaryKey(),
|
|
10
|
+
// ...timestamps,
|
|
11
|
+
// });
|
|
12
|
+
//
|
|
13
|
+
// Both columns are set by the database. defaultNow() fills them on insert and
|
|
14
|
+
// the set_updated_at() trigger in the second migration bumps updated_at on
|
|
15
|
+
// every update. We do not use Drizzle's $onUpdate, because that only runs
|
|
16
|
+
// when the update goes through Drizzle.
|
|
17
|
+
export const timestamps = {
|
|
18
|
+
createdAt: timestamp('created_at', { withTimezone: true })
|
|
19
|
+
.notNull()
|
|
20
|
+
.defaultNow(),
|
|
21
|
+
updatedAt: timestamp('updated_at', { withTimezone: true })
|
|
22
|
+
.notNull()
|
|
23
|
+
.defaultNow(),
|
|
24
|
+
};
|
package/dist/db/url.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Options } from 'postgres';
|
|
2
|
+
/**
|
|
3
|
+
* Turns DATABASE_URL into arguments for postgres.js.
|
|
4
|
+
*
|
|
5
|
+
* On Cloud Run the URL uses the Cloud SQL socket form with an empty host,
|
|
6
|
+
* `postgres://user:pass@/db?host=/cloudsql/project:region:instance`.
|
|
7
|
+
* postgres.js cannot parse an empty host and ignores `?host=`, so this moves
|
|
8
|
+
* the socket directory into the `path` option. Other URLs pass through.
|
|
9
|
+
*
|
|
10
|
+
* Errors never include the URL, because it carries the password.
|
|
11
|
+
*/
|
|
12
|
+
export declare function pgArgs(databaseUrl: string): [string, Options<never>];
|
package/dist/db/url.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns DATABASE_URL into arguments for postgres.js.
|
|
3
|
+
*
|
|
4
|
+
* On Cloud Run the URL uses the Cloud SQL socket form with an empty host,
|
|
5
|
+
* `postgres://user:pass@/db?host=/cloudsql/project:region:instance`.
|
|
6
|
+
* postgres.js cannot parse an empty host and ignores `?host=`, so this moves
|
|
7
|
+
* the socket directory into the `path` option. Other URLs pass through.
|
|
8
|
+
*
|
|
9
|
+
* Errors never include the URL, because it carries the password.
|
|
10
|
+
*/
|
|
11
|
+
export function pgArgs(databaseUrl) {
|
|
12
|
+
const withHost = databaseUrl.replace(/^(postgres(?:ql)?:\/\/[^/@]*@)\//, '$1localhost/');
|
|
13
|
+
let u;
|
|
14
|
+
try {
|
|
15
|
+
u = new URL(withHost);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new Error('DATABASE_URL is not a valid postgres URL');
|
|
19
|
+
}
|
|
20
|
+
const socketDir = u.searchParams.get('host');
|
|
21
|
+
if (!socketDir)
|
|
22
|
+
return [databaseUrl, {}];
|
|
23
|
+
u.searchParams.delete('host');
|
|
24
|
+
return [u.toString(), { path: `${socketDir}/.s.PGSQL.${u.port || 5432}` }];
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const TaskType: z.ZodString;
|
|
3
|
+
export type TaskType = z.infer<typeof TaskType>;
|
|
4
|
+
export declare const BaseDimension: z.ZodEnum<{
|
|
5
|
+
cost_latency: "cost_latency";
|
|
6
|
+
provenance: "provenance";
|
|
7
|
+
reliability: "reliability";
|
|
8
|
+
safety: "safety";
|
|
9
|
+
}>;
|
|
10
|
+
export type BaseDimension = z.infer<typeof BaseDimension>;
|
|
11
|
+
export declare const CompetenceDimension: z.ZodTemplateLiteral<`competence:${string}`>;
|
|
12
|
+
export type CompetenceDimension = z.infer<typeof CompetenceDimension>;
|
|
13
|
+
export declare const Dimension: z.ZodUnion<readonly [z.ZodEnum<{
|
|
14
|
+
cost_latency: "cost_latency";
|
|
15
|
+
provenance: "provenance";
|
|
16
|
+
reliability: "reliability";
|
|
17
|
+
safety: "safety";
|
|
18
|
+
}>, z.ZodTemplateLiteral<`competence:${string}`>]>;
|
|
19
|
+
export type Dimension = z.infer<typeof Dimension>;
|
|
20
|
+
export declare const competence: (taskType: TaskType) => CompetenceDimension;
|
|
21
|
+
export declare const isTrustDimension: (dimension: string) => boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const TaskType = z.string().regex(/^[a-z0-9_-]{1,32}$/);
|
|
3
|
+
export const BaseDimension = z.enum([
|
|
4
|
+
'reliability',
|
|
5
|
+
'safety',
|
|
6
|
+
'cost_latency',
|
|
7
|
+
'provenance',
|
|
8
|
+
]);
|
|
9
|
+
export const CompetenceDimension = z.templateLiteral(['competence:', TaskType]);
|
|
10
|
+
export const Dimension = z.union([BaseDimension, CompetenceDimension]);
|
|
11
|
+
export const competence = (taskType) => CompetenceDimension.parse(`competence:${taskType}`);
|
|
12
|
+
// Reliability, safety, provenance and every competence dimension are trust.
|
|
13
|
+
// They need verified work from another operator before they get a value.
|
|
14
|
+
// cost_latency comes from usage events and is information, not trust.
|
|
15
|
+
export const isTrustDimension = (dimension) => dimension !== 'cost_latency';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const Jws: z.ZodString;
|
|
3
|
+
export declare const JwsHeader: z.ZodObject<{
|
|
4
|
+
alg: z.ZodLiteral<"EdDSA">;
|
|
5
|
+
kid: z.ZodString;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
export type JwsHeader = z.infer<typeof JwsHeader>;
|
|
8
|
+
export declare class EnvelopeError extends Error {
|
|
9
|
+
name: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string>;
|
|
12
|
+
export declare function decodeHeader(jws: string): JwsHeader;
|
|
13
|
+
export declare function verify(jws: string, publicKey: Uint8Array): Promise<{
|
|
14
|
+
header: JwsHeader;
|
|
15
|
+
payload: unknown;
|
|
16
|
+
}>;
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { signAsync, verifyAsync } from '@noble/ed25519';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { base64urlDecode, base64urlEncode, utf8Decode, utf8Encode, } from './base64url.js';
|
|
4
|
+
// The outer cap matches the API body limit. Routes set a tighter one.
|
|
5
|
+
export const Jws = z
|
|
6
|
+
.string()
|
|
7
|
+
.max(262144)
|
|
8
|
+
.regex(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/, 'Expected JWS compact');
|
|
9
|
+
export const JwsHeader = z.strictObject({
|
|
10
|
+
alg: z.literal('EdDSA'),
|
|
11
|
+
kid: z.string().min(1).max(128),
|
|
12
|
+
});
|
|
13
|
+
export class EnvelopeError extends Error {
|
|
14
|
+
name = 'EnvelopeError';
|
|
15
|
+
}
|
|
16
|
+
function split(jws) {
|
|
17
|
+
if (!Jws.safeParse(jws).success) {
|
|
18
|
+
throw new EnvelopeError('Envelope is not three base64url parts');
|
|
19
|
+
}
|
|
20
|
+
const [header, payload, signature] = jws.split('.');
|
|
21
|
+
return { header, payload, signature };
|
|
22
|
+
}
|
|
23
|
+
function parseJson(part, what) {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(utf8Decode(base64urlDecode(part)));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new EnvelopeError(`Envelope ${what} is not base64url JSON`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function parseHeader(part) {
|
|
32
|
+
const result = JwsHeader.safeParse(parseJson(part, 'header'));
|
|
33
|
+
if (!result.success) {
|
|
34
|
+
throw new EnvelopeError('Envelope header must be alg EdDSA with a kid');
|
|
35
|
+
}
|
|
36
|
+
return result.data;
|
|
37
|
+
}
|
|
38
|
+
export async function sign(payload, privateKey, kid) {
|
|
39
|
+
const header = JwsHeader.parse({ alg: 'EdDSA', kid });
|
|
40
|
+
const body = JSON.stringify(payload);
|
|
41
|
+
if (body === undefined)
|
|
42
|
+
throw new EnvelopeError('Payload is not JSON');
|
|
43
|
+
const signingInput = `${base64urlEncode(utf8Encode(JSON.stringify(header)))}.${base64urlEncode(utf8Encode(body))}`;
|
|
44
|
+
const signature = await signAsync(utf8Encode(signingInput), privateKey);
|
|
45
|
+
return `${signingInput}.${base64urlEncode(signature)}`;
|
|
46
|
+
}
|
|
47
|
+
export function decodeHeader(jws) {
|
|
48
|
+
return parseHeader(split(jws).header);
|
|
49
|
+
}
|
|
50
|
+
export async function verify(jws, publicKey) {
|
|
51
|
+
const parts = split(jws);
|
|
52
|
+
const signingInput = utf8Encode(`${parts.header}.${parts.payload}`);
|
|
53
|
+
let ok = false;
|
|
54
|
+
try {
|
|
55
|
+
const signature = base64urlDecode(parts.signature);
|
|
56
|
+
ok =
|
|
57
|
+
signature.length === 64 &&
|
|
58
|
+
publicKey.length === 32 &&
|
|
59
|
+
(await verifyAsync(signature, signingInput, publicKey, {
|
|
60
|
+
zip215: false,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
ok = false;
|
|
65
|
+
}
|
|
66
|
+
if (!ok)
|
|
67
|
+
throw new EnvelopeError('Envelope signature is invalid');
|
|
68
|
+
const header = parseHeader(parts.header);
|
|
69
|
+
return { header, payload: parseJson(parts.payload, 'payload') };
|
|
70
|
+
}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const Version: z.ZodString;
|
|
3
|
+
export declare const EventType: z.ZodEnum<{
|
|
4
|
+
incident: "incident";
|
|
5
|
+
"session.end": "session.end";
|
|
6
|
+
"session.start": "session.start";
|
|
7
|
+
"task.claimed": "task.claimed";
|
|
8
|
+
"task.outcome": "task.outcome";
|
|
9
|
+
"task.submitted": "task.submitted";
|
|
10
|
+
"tool.call": "tool.call";
|
|
11
|
+
usage: "usage";
|
|
12
|
+
}>;
|
|
13
|
+
export type EventType = z.infer<typeof EventType>;
|
|
14
|
+
export declare const IncidentKind: z.ZodEnum<{
|
|
15
|
+
flagged: "flagged";
|
|
16
|
+
leak_suspected: "leak_suspected";
|
|
17
|
+
scope: "scope";
|
|
18
|
+
tool_denied: "tool_denied";
|
|
19
|
+
}>;
|
|
20
|
+
export type IncidentKind = z.infer<typeof IncidentKind>;
|
|
21
|
+
export declare const EventPayload: {
|
|
22
|
+
'session.start': z.ZodObject<{
|
|
23
|
+
session_id: z.ZodString;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
'session.end': z.ZodObject<{
|
|
26
|
+
session_id: z.ZodString;
|
|
27
|
+
duration_ms: z.ZodInt;
|
|
28
|
+
}, z.core.$strict>;
|
|
29
|
+
'tool.call': z.ZodObject<{
|
|
30
|
+
tool: z.ZodString;
|
|
31
|
+
duration_ms: z.ZodInt;
|
|
32
|
+
ok: z.ZodBoolean;
|
|
33
|
+
error_class: z.ZodOptional<z.ZodString>;
|
|
34
|
+
}, z.core.$strict>;
|
|
35
|
+
'task.claimed': z.ZodObject<{
|
|
36
|
+
task_id: z.ZodUUID;
|
|
37
|
+
task_type: z.ZodString;
|
|
38
|
+
}, z.core.$strict>;
|
|
39
|
+
'task.submitted': z.ZodObject<{
|
|
40
|
+
task_id: z.ZodUUID;
|
|
41
|
+
task_type: z.ZodString;
|
|
42
|
+
}, z.core.$strict>;
|
|
43
|
+
'task.outcome': z.ZodObject<{
|
|
44
|
+
task_id: z.ZodUUID;
|
|
45
|
+
outcome: z.ZodEnum<{
|
|
46
|
+
failure: "failure";
|
|
47
|
+
success: "success";
|
|
48
|
+
}>;
|
|
49
|
+
evidence_hash: z.ZodOptional<z.ZodString>;
|
|
50
|
+
}, z.core.$strict>;
|
|
51
|
+
incident: z.ZodObject<{
|
|
52
|
+
kind: z.ZodEnum<{
|
|
53
|
+
flagged: "flagged";
|
|
54
|
+
leak_suspected: "leak_suspected";
|
|
55
|
+
scope: "scope";
|
|
56
|
+
tool_denied: "tool_denied";
|
|
57
|
+
}>;
|
|
58
|
+
detail_hash: z.ZodOptional<z.ZodString>;
|
|
59
|
+
}, z.core.$strict>;
|
|
60
|
+
usage: z.ZodObject<{
|
|
61
|
+
tokens_in: z.ZodInt;
|
|
62
|
+
tokens_out: z.ZodInt;
|
|
63
|
+
latency_ms: z.ZodOptional<z.ZodInt>;
|
|
64
|
+
model: z.ZodOptional<z.ZodString>;
|
|
65
|
+
}, z.core.$strict>;
|
|
66
|
+
};
|
|
67
|
+
export type EventPayload<T extends EventType> = z.infer<(typeof EventPayload)[T]>;
|
|
68
|
+
export declare const Event: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
69
|
+
event_id: z.ZodUUID;
|
|
70
|
+
type: z.ZodLiteral<"session.start">;
|
|
71
|
+
occurred_at: z.ZodISODateTime;
|
|
72
|
+
version: z.ZodString;
|
|
73
|
+
payload: z.ZodObject<{
|
|
74
|
+
session_id: z.ZodString;
|
|
75
|
+
}, z.core.$strict>;
|
|
76
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
77
|
+
event_id: z.ZodUUID;
|
|
78
|
+
type: z.ZodLiteral<"session.end">;
|
|
79
|
+
occurred_at: z.ZodISODateTime;
|
|
80
|
+
version: z.ZodString;
|
|
81
|
+
payload: z.ZodObject<{
|
|
82
|
+
session_id: z.ZodString;
|
|
83
|
+
duration_ms: z.ZodInt;
|
|
84
|
+
}, z.core.$strict>;
|
|
85
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
86
|
+
event_id: z.ZodUUID;
|
|
87
|
+
type: z.ZodLiteral<"tool.call">;
|
|
88
|
+
occurred_at: z.ZodISODateTime;
|
|
89
|
+
version: z.ZodString;
|
|
90
|
+
payload: z.ZodObject<{
|
|
91
|
+
tool: z.ZodString;
|
|
92
|
+
duration_ms: z.ZodInt;
|
|
93
|
+
ok: z.ZodBoolean;
|
|
94
|
+
error_class: z.ZodOptional<z.ZodString>;
|
|
95
|
+
}, z.core.$strict>;
|
|
96
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
97
|
+
event_id: z.ZodUUID;
|
|
98
|
+
type: z.ZodLiteral<"task.claimed">;
|
|
99
|
+
occurred_at: z.ZodISODateTime;
|
|
100
|
+
version: z.ZodString;
|
|
101
|
+
payload: z.ZodObject<{
|
|
102
|
+
task_id: z.ZodUUID;
|
|
103
|
+
task_type: z.ZodString;
|
|
104
|
+
}, z.core.$strict>;
|
|
105
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
106
|
+
event_id: z.ZodUUID;
|
|
107
|
+
type: z.ZodLiteral<"task.submitted">;
|
|
108
|
+
occurred_at: z.ZodISODateTime;
|
|
109
|
+
version: z.ZodString;
|
|
110
|
+
payload: z.ZodObject<{
|
|
111
|
+
task_id: z.ZodUUID;
|
|
112
|
+
task_type: z.ZodString;
|
|
113
|
+
}, z.core.$strict>;
|
|
114
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
115
|
+
event_id: z.ZodUUID;
|
|
116
|
+
type: z.ZodLiteral<"task.outcome">;
|
|
117
|
+
occurred_at: z.ZodISODateTime;
|
|
118
|
+
version: z.ZodString;
|
|
119
|
+
payload: z.ZodObject<{
|
|
120
|
+
task_id: z.ZodUUID;
|
|
121
|
+
outcome: z.ZodEnum<{
|
|
122
|
+
failure: "failure";
|
|
123
|
+
success: "success";
|
|
124
|
+
}>;
|
|
125
|
+
evidence_hash: z.ZodOptional<z.ZodString>;
|
|
126
|
+
}, z.core.$strict>;
|
|
127
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
128
|
+
event_id: z.ZodUUID;
|
|
129
|
+
type: z.ZodLiteral<"incident">;
|
|
130
|
+
occurred_at: z.ZodISODateTime;
|
|
131
|
+
version: z.ZodString;
|
|
132
|
+
payload: z.ZodObject<{
|
|
133
|
+
kind: z.ZodEnum<{
|
|
134
|
+
flagged: "flagged";
|
|
135
|
+
leak_suspected: "leak_suspected";
|
|
136
|
+
scope: "scope";
|
|
137
|
+
tool_denied: "tool_denied";
|
|
138
|
+
}>;
|
|
139
|
+
detail_hash: z.ZodOptional<z.ZodString>;
|
|
140
|
+
}, z.core.$strict>;
|
|
141
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
142
|
+
event_id: z.ZodUUID;
|
|
143
|
+
type: z.ZodLiteral<"usage">;
|
|
144
|
+
occurred_at: z.ZodISODateTime;
|
|
145
|
+
version: z.ZodString;
|
|
146
|
+
payload: z.ZodObject<{
|
|
147
|
+
tokens_in: z.ZodInt;
|
|
148
|
+
tokens_out: z.ZodInt;
|
|
149
|
+
latency_ms: z.ZodOptional<z.ZodInt>;
|
|
150
|
+
model: z.ZodOptional<z.ZodString>;
|
|
151
|
+
}, z.core.$strict>;
|
|
152
|
+
}, z.core.$strict>], "type">;
|
|
153
|
+
export type Event = z.infer<typeof Event>;
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TaskType } from './dimensions.js';
|
|
3
|
+
import { Sha256Hex, TaskOutcome } from './tasks.js';
|
|
4
|
+
/*
|
|
5
|
+
* The Zod pattern, shown once. Every schema file in this package follows it.
|
|
6
|
+
*
|
|
7
|
+
* 1. A strict object schema rejects unknown keys, so a payload cannot carry
|
|
8
|
+
* anything the taxonomy does not name.
|
|
9
|
+
*
|
|
10
|
+
* const ToolCall = z.strictObject({ tool: Name, ok: z.boolean() });
|
|
11
|
+
* ToolCall.parse({ tool: 'Bash', ok: true, prompt: 'hi' }); // throws
|
|
12
|
+
*
|
|
13
|
+
* 2. A discriminated union picks the member schema by one literal field, so
|
|
14
|
+
* `type` decides which payload schema runs and errors name the right one.
|
|
15
|
+
*
|
|
16
|
+
* const Event = z.discriminatedUnion('type', [
|
|
17
|
+
* z.strictObject({ type: z.literal('tool.call'), payload: ToolCall }),
|
|
18
|
+
* z.strictObject({ type: z.literal('usage'), payload: Usage }),
|
|
19
|
+
* ]);
|
|
20
|
+
*
|
|
21
|
+
* 3. z.infer derives the TypeScript type from the schema. The schema and the
|
|
22
|
+
* type share one name, so `Event` works as a value and as a type.
|
|
23
|
+
*
|
|
24
|
+
* export type Event = z.infer<typeof Event>;
|
|
25
|
+
*
|
|
26
|
+
* Use parse to throw on bad input and safeParse to get { success, data, error }.
|
|
27
|
+
*/
|
|
28
|
+
export const Version = z.string().min(1).max(32);
|
|
29
|
+
const Name = z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1)
|
|
32
|
+
.max(64)
|
|
33
|
+
.regex(/^[A-Za-z0-9._:/@-]+$/);
|
|
34
|
+
const Millis = z.int().min(0).max(604_800_000);
|
|
35
|
+
const Count = z.int().min(0).max(100_000_000);
|
|
36
|
+
export const EventType = z.enum([
|
|
37
|
+
'session.start',
|
|
38
|
+
'session.end',
|
|
39
|
+
'tool.call',
|
|
40
|
+
'task.claimed',
|
|
41
|
+
'task.submitted',
|
|
42
|
+
'task.outcome',
|
|
43
|
+
'incident',
|
|
44
|
+
'usage',
|
|
45
|
+
]);
|
|
46
|
+
export const IncidentKind = z.enum([
|
|
47
|
+
'scope',
|
|
48
|
+
'tool_denied',
|
|
49
|
+
'leak_suspected',
|
|
50
|
+
'flagged',
|
|
51
|
+
]);
|
|
52
|
+
const TaskRef = z.strictObject({ task_id: z.uuid(), task_type: TaskType });
|
|
53
|
+
export const EventPayload = {
|
|
54
|
+
'session.start': z.strictObject({ session_id: Name }),
|
|
55
|
+
'session.end': z.strictObject({ session_id: Name, duration_ms: Millis }),
|
|
56
|
+
'tool.call': z.strictObject({
|
|
57
|
+
tool: Name,
|
|
58
|
+
duration_ms: Millis,
|
|
59
|
+
ok: z.boolean(),
|
|
60
|
+
error_class: Name.optional(),
|
|
61
|
+
}),
|
|
62
|
+
'task.claimed': TaskRef,
|
|
63
|
+
'task.submitted': TaskRef,
|
|
64
|
+
'task.outcome': z.strictObject({
|
|
65
|
+
task_id: z.uuid(),
|
|
66
|
+
outcome: TaskOutcome,
|
|
67
|
+
evidence_hash: Sha256Hex.optional(),
|
|
68
|
+
}),
|
|
69
|
+
incident: z.strictObject({
|
|
70
|
+
kind: IncidentKind,
|
|
71
|
+
detail_hash: Sha256Hex.optional(),
|
|
72
|
+
}),
|
|
73
|
+
// latency_ms and model are optional, since not every framework reports
|
|
74
|
+
// them. A usage event without latency_ms is no signal for cost_latency.
|
|
75
|
+
usage: z.strictObject({
|
|
76
|
+
tokens_in: Count,
|
|
77
|
+
tokens_out: Count,
|
|
78
|
+
latency_ms: Millis.optional(),
|
|
79
|
+
model: Name.optional(),
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
const eventOf = (type) => z.strictObject({
|
|
83
|
+
event_id: z.uuid(),
|
|
84
|
+
type: z.literal(type),
|
|
85
|
+
occurred_at: z.iso.datetime(),
|
|
86
|
+
version: Version,
|
|
87
|
+
payload: EventPayload[type],
|
|
88
|
+
});
|
|
89
|
+
export const Event = z.discriminatedUnion('type', [
|
|
90
|
+
eventOf('session.start'),
|
|
91
|
+
eventOf('session.end'),
|
|
92
|
+
eventOf('tool.call'),
|
|
93
|
+
eventOf('task.claimed'),
|
|
94
|
+
eventOf('task.submitted'),
|
|
95
|
+
eventOf('task.outcome'),
|
|
96
|
+
eventOf('incident'),
|
|
97
|
+
eventOf('usage'),
|
|
98
|
+
]);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './agent-id.js';
|
|
2
|
+
export * from './agent-name.js';
|
|
3
|
+
export * from './api.js';
|
|
4
|
+
export * from './base64url.js';
|
|
5
|
+
export * from './credential.js';
|
|
6
|
+
export * from './dimensions.js';
|
|
7
|
+
export * from './envelope.js';
|
|
8
|
+
export * from './events.js';
|
|
9
|
+
export * from './standing.js';
|
|
10
|
+
export * from './tasks.js';
|
|
11
|
+
export * from './top-dimensions.js';
|
|
12
|
+
export declare const SCHEMA_VERSION = 1;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './agent-id.js';
|
|
2
|
+
export * from './agent-name.js';
|
|
3
|
+
export * from './api.js';
|
|
4
|
+
export * from './base64url.js';
|
|
5
|
+
export * from './credential.js';
|
|
6
|
+
export * from './dimensions.js';
|
|
7
|
+
export * from './envelope.js';
|
|
8
|
+
export * from './events.js';
|
|
9
|
+
export * from './standing.js';
|
|
10
|
+
export * from './tasks.js';
|
|
11
|
+
export * from './top-dimensions.js';
|
|
12
|
+
export const SCHEMA_VERSION = 1;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SealBrokenReason } from './api.js';
|
|
2
|
+
import { type WellKnown } from './credential.js';
|
|
3
|
+
export type SealConformanceCase = {
|
|
4
|
+
name: string;
|
|
5
|
+
jws: string;
|
|
6
|
+
expected: 'valid' | SealBrokenReason;
|
|
7
|
+
nowSeconds?: number;
|
|
8
|
+
};
|
|
9
|
+
export type SealConformance = {
|
|
10
|
+
nowSeconds: number;
|
|
11
|
+
wellKnown: WellKnown;
|
|
12
|
+
cases: SealConformanceCase[];
|
|
13
|
+
};
|
|
14
|
+
export declare function sealConformanceCases(): Promise<SealConformance>;
|