The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 77f5465edca6Commit detailsView latest

packages/protocol/src/index.ts 1017 lines · 35.5 KB · typescript
1
import { createHash, generateKeyPairSync, randomUUID, sign, verify } from 'node:crypto';
2
import { canonicalize } from 'json-canonicalize';
3
import { z } from 'zod';
4
5
export const PROTOCOL_VERSION = '0.1' as const;
6
export const DEFAULT_EXTENSION_URI = 'https://openclasp.vercel.app/extensions/trust/v0.1';
7
8
export const ProvenanceSchema = z.enum([
9
  'self_declared',
10
  'operator_attested',
11
  'cryptographically_verified',
12
  'domain_verified',
13
  'third_party_verified',
14
  'observed',
15
  'disputed',
16
]);
17
export const VisibilitySchema = z.enum([
18
  'local_only',
19
  'private_requester',
20
  'private_responder',
21
  'shared_participants',
22
  'network_aggregate',
23
]);
24
export const DataSharingModeSchema = z.enum([
25
  'local_only',
26
  'structured_only',
27
  'permitted_evidence',
28
]);
29
export const SignatureSchema = z.object({
30
  algorithm: z.literal('Ed25519'),
31
  keyId: z.string().min(1),
32
  value: z.string().min(1),
33
});
34
35
export const RecordAttestationSchema = SignatureSchema.extend({
36
  digest: z.string().regex(/^[a-zA-Z0-9_-]{43}$/),
37
}).strict();
38
39
export const RetentionClassSchema = z.enum(['account', 'audit', 'operational', 'temporary']);
40
export const LearningScopeSchema = z.enum([
41
  'not_evaluated',
42
  'excluded',
43
  'local_only',
44
  'network_aggregate',
45
]);
46
47
export const SourceRecordEnvelopeSchema = z
48
  .object({
49
    eventId: z.string().uuid(),
50
    operatorId: z.string().min(1),
51
    kind: z.string().regex(/^[a-z][a-z0-9_]*$/),
52
    recordId: z.string().min(1),
53
    schemaName: z.string().min(1),
54
    schemaVersion: z.string().min(1),
55
    payload: z.json(),
56
    payloadDigest: z.string().regex(/^[a-zA-Z0-9_-]{43}$/),
57
    entityRefs: z.record(z.string(), z.string().min(1)).default({}),
58
    provenance: ProvenanceSchema,
59
    visibility: VisibilitySchema,
60
    retentionClass: RetentionClassSchema,
61
    learningScope: LearningScopeSchema,
62
    reportedAt: z.string().datetime(),
63
    ingestedAt: z.string().datetime(),
64
  })
65
  .strict();
66
67
export const ExpectationManifestSchema = z.object({
68
  requiredInputs: z.array(z.string()).default([]),
69
  supportedTaskCategories: z.array(z.string()).min(1),
70
  responseTimeMs: z.number().int().positive().optional(),
71
  dataRequirements: z.array(z.string()).default([]),
72
  prohibitedData: z.array(z.string()).default([]),
73
  humanApprovalThresholds: z.array(z.string()).default([]),
74
  delegationPolicy: z.string().default('explicit'),
75
  evidenceRequirements: z.array(z.string()).default([]),
76
  factCheckingPreference: z
77
    .enum(['none', 'important_claims', 'all_objective_claims'])
78
    .default('important_claims'),
79
  mediationPreference: z.enum(['never', 'mutual_consent']).default('mutual_consent'),
80
  retentionDays: z.number().int().nonnegative().default(30),
81
  communicationFormats: z.array(z.string()).default(['text/plain']),
82
  cancellationPolicy: z.string().default('either_party'),
83
  provenance: ProvenanceSchema,
84
});
85
86
export const AgentIdentitySchema = z.object({
87
  protocolVersion: z.literal(PROTOCOL_VERSION),
88
  agentId: z.string().min(1),
89
  publicKey: z.string().min(1),
90
  keyId: z.string().min(1),
91
  operatorRef: z.string().min(1),
92
  assurance: z.enum(['pseudonymous', 'domain_associated', 'organization_associated']),
93
  agentVersion: z.string().min(1),
94
  capabilities: z.array(z.string()),
95
  supportedProtocols: z.array(z.string()),
96
  parentAgentId: z.string().optional(),
97
  rootControllerId: z.string().min(1),
98
  revoked: z.boolean().default(false),
99
  provenance: ProvenanceSchema,
100
  createdAt: z.string().datetime(),
101
  signature: SignatureSchema.optional(),
102
});
103
104
export const DelegationCredentialSchema = z.object({
105
  protocolVersion: z.literal(PROTOCOL_VERSION),
106
  delegationId: z.string().uuid(),
107
  parentAgentId: z.string(),
108
  childAgentId: z.string(),
109
  rootControllerId: z.string(),
110
  capabilities: z.array(z.string()).min(1),
111
  issuedAt: z.string().datetime(),
112
  expiresAt: z.string().datetime(),
113
  revoked: z.boolean().default(false),
114
  signature: SignatureSchema.optional(),
115
});
116
117
export const InteractionContractSchema = z.object({
118
  protocolVersion: z.literal(PROTOCOL_VERSION),
119
  interactionId: z.string().uuid(),
120
  purpose: z.string(),
121
  parties: z.array(z.string()).min(1),
122
  taskCategory: z.string(),
123
  requestedOutcome: z.string(),
124
  successCriteria: z.array(z.string()),
125
  allowedActions: z.array(z.string()),
126
  prohibitedActions: z.array(z.string()),
127
  allowedData: z.array(z.string()),
128
  prohibitedData: z.array(z.string()),
129
  deadline: z.string().datetime().optional(),
130
  evidenceRequirements: z.array(z.string()),
131
  delegationRules: z.array(z.string()),
132
  humanApprovalRequirements: z.array(z.string()),
133
  factCheckingPolicy: z.string(),
134
  mediationPolicy: z.enum(['none', 'mutual_consent']),
135
  retentionDays: z.number().int().nonnegative(),
136
  completionConditions: z.array(z.string()),
137
  cancellationConditions: z.array(z.string()),
138
  signatures: z.record(z.string(), SignatureSchema).default({}),
139
});
140
141
export const AgentTransportSchema = z.object({
142
  protocol: z.literal('A2A/1.0'),
143
  protocolBinding: z.string().min(1).default('JSONRPC'),
144
  endpoint: z.string().url(),
145
  managedBy: z.enum(['agent', 'openclasp']).default('agent'),
146
});
147
148
export const AgentModeSchema = z.enum(['persistent_runtime', 'temporary_chat']);
149
150
export const AgentPresenceSchema = z.object({
151
  status: z.enum(['online', 'offline']),
152
  lastSeenAt: z.string().datetime().optional(),
153
  checkedAt: z.string().datetime(),
154
});
155
156
export const PublicAgentVerificationSchema = z.object({
157
  status: z.literal('verified'),
158
  method: z.literal('openclasp_oauth_account'),
159
  verifiedAt: z.string().datetime(),
160
  verificationKeyUrl: z.string().url().optional(),
161
});
162
163
export const PublicAgentCardSchema = z.object({
164
  protocolVersion: z.literal(PROTOCOL_VERSION),
165
  agentId: z.string().min(1),
166
  name: z.string().min(1),
167
  description: z.string(),
168
  framework: z.string().min(1),
169
  agentVersion: z.string().min(1),
170
  capabilities: z.array(z.string()),
171
  limitations: z.array(z.string()),
172
  assurance: z.enum(['oauth_authenticated', 'cryptographically_verified']),
173
  agentMode: AgentModeSchema.default('persistent_runtime'),
174
  transports: z.array(AgentTransportSchema),
175
  slug: z
176
    .string()
177
    .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
178
    .optional(),
179
  profileUrl: z.string().url().optional(),
180
  cardUrl: z.string().url(),
181
  a2aAgentCardUrl: z.string().url(),
182
  extensionUri: z.string().url(),
183
  presence: AgentPresenceSchema.optional(),
184
  verification: PublicAgentVerificationSchema.optional(),
185
  publishedAt: z.string().datetime(),
186
  updatedAt: z.string().datetime(),
187
  platformAttestation: RecordAttestationSchema.optional(),
188
});
189
190
export const AgentResolutionSchema = z.object({
191
  reference: z.string().min(1),
192
  matchedBy: z.enum(['agent_id', 'slug', 'profile_url', 'card_url', 'a2a_card_url']),
193
  verified: z.literal(true),
194
  card: PublicAgentCardSchema,
195
  resolvedAt: z.string().datetime(),
196
});
197
198
export const HostedThreadStatusSchema = z.enum(['open', 'closed']);
199
200
export const HostedMessageSchema = z.object({
201
  messageId: z.string().uuid(),
202
  threadId: z.string().uuid(),
203
  interactionId: z.string().uuid(),
204
  senderAgentId: z.string().min(1),
205
  recipientAgentId: z.string().min(1),
206
  contentType: z.literal('text/plain'),
207
  content: z.string().min(1).max(20_000),
208
  contentHash: z.string().regex(/^[a-zA-Z0-9_-]{43}$/),
209
  delivery: z.enum(['accepted', 'delivered', 'read']),
210
  createdAt: z.string().datetime(),
211
  readAt: z.string().datetime().optional(),
212
});
213
214
export const HostedThreadSchema = z.object({
215
  threadId: z.string().uuid(),
216
  interactionId: z.string().uuid(),
217
  participantAgentIds: z.tuple([z.string().min(1), z.string().min(1)]),
218
  status: HostedThreadStatusSchema,
219
  privacyMode: z.literal('openclasp_hosted_temporary'),
220
  unreadCount: z.number().int().nonnegative().default(0),
221
  createdAt: z.string().datetime(),
222
  updatedAt: z.string().datetime(),
223
  expiresAt: z.string().datetime(),
224
});
225
226
export const ContractAcceptanceSchema = z.object({
227
  agentId: z.string().min(1),
228
  method: z.enum(['oauth_installation', 'oauth_account', 'policy_auto_accept', 'ed25519']),
229
  termsHash: z.string().min(1),
230
  acceptedAt: z.string().datetime(),
231
  signature: SignatureSchema.optional(),
232
});
233
234
export const ContractRevisionSchema = z.object({
235
  revisionId: z.string().uuid(),
236
  interactionId: z.string().uuid(),
237
  revision: z.number().int().positive(),
238
  previousTermsHash: z.string().min(1).optional(),
239
  termsHash: z.string().min(1),
240
  contract: InteractionContractSchema,
241
  proposedByAgentId: z.string().min(1),
242
  status: z.enum(['proposed', 'accepted', 'rejected', 'superseded']),
243
  acceptances: z.record(z.string(), ContractAcceptanceSchema),
244
  createdAt: z.string().datetime(),
245
  updatedAt: z.string().datetime(),
246
  expiresAt: z.string().datetime().optional(),
247
  platformAttestation: RecordAttestationSchema.optional(),
248
});
249
250
export const FederatedInteractionStatusSchema = z.enum([
251
  'pending',
252
  'active',
253
  'rejected',
254
  'expired',
255
  'cancelled',
256
  'completed',
257
]);
258
259
export const FederatedInteractionSchema = z.object({
260
  protocolVersion: z.literal(PROTOCOL_VERSION),
261
  interactionId: z.string().uuid(),
262
  initiatorAgentId: z.string().min(1),
263
  responderAgentId: z.string().min(1),
264
  status: FederatedInteractionStatusSchema,
265
  contract: InteractionContractSchema,
266
  termsHash: z.string().min(1),
267
  acceptances: z.record(z.string(), ContractAcceptanceSchema),
268
  contractRevision: z.number().int().positive().default(1),
269
  contractRevisions: z.array(ContractRevisionSchema).default([]),
270
  initiatorTransport: AgentTransportSchema.optional(),
271
  responderTransport: AgentTransportSchema,
272
  createdAt: z.string().datetime(),
273
  updatedAt: z.string().datetime(),
274
  expiresAt: z.string().datetime(),
275
});
276
277
export const LiveSessionInsightSchema = z
278
  .object({
279
    code: z.string().min(1),
280
    severity: z.enum(['info', 'caution', 'high']),
281
    message: z.string().min(1),
282
    evidenceReferences: z.array(z.string()).default([]),
283
    requirementReferences: z.array(z.string()).default([]),
284
    confidence: z.number().min(0).max(1).optional(),
285
  })
286
  .strict();
287
288
export const RequirementAssessmentSchema = z
289
  .object({
290
    requirementId: z.string().min(1).max(128),
291
    requirement: z.string().min(1).max(1000),
292
    kind: z.enum([
293
      'capability',
294
      'limitation',
295
      'success_criterion',
296
      'deadline',
297
      'evidence',
298
      'scope',
299
      'data',
300
      'communication',
301
      'delegation',
302
    ]),
303
    status: z.enum(['match', 'partial', 'mismatch', 'unknown']),
304
    reason: z.string().min(1).max(1000),
305
    confidence: z.number().min(0).max(1),
306
    sources: z
307
      .array(
308
        z.enum(['contract', 'self_declared', 'eligible_history', 'evidence', 'version_change']),
309
      )
310
      .min(1),
311
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
312
  })
313
  .strict();
314
315
export const CounterpartyBriefSchema = z
316
  .object({
317
    briefId: z.string().uuid(),
318
    interactionId: z.string().uuid(),
319
    contractHash: z.string().min(1),
320
    recipientAgentId: z.string().min(1),
321
    subjectAgentId: z.string().min(1),
322
    taskCategory: z.string().min(1),
323
    decision: z.enum(['ALLOW', 'CHALLENGE', 'DENY']),
324
    requirements: z.array(RequirementAssessmentSchema).max(100),
325
    insights: z.array(LiveSessionInsightSchema).max(100),
326
    relevantSampleSize: z.number().int().nonnegative(),
327
    historyConfidence: z.number().min(0).max(1),
328
    subjectAgentVersion: z.string().min(1),
329
    recommendedContractChanges: z.array(z.string().min(1).max(1000)).max(50).default([]),
330
    generatedAt: z.string().datetime(),
331
    expiresAt: z.string().datetime(),
332
    signature: SignatureSchema.optional(),
333
  })
334
  .strict()
335
  .refine((brief) => brief.recipientAgentId !== brief.subjectAgentId, {
336
    message: 'A counterparty brief must describe another agent',
337
    path: ['subjectAgentId'],
338
  })
339
  .refine((brief) => Date.parse(brief.expiresAt) > Date.parse(brief.generatedAt), {
340
    message: 'Counterparty brief expiry must be after generation',
341
    path: ['expiresAt'],
342
  });
343
344
export const LiveSessionOfferSchema = z.object({
345
  type: z.literal('openclasp.session.offer'),
346
  version: z.literal('1'),
347
  offerId: z.string().uuid(),
348
  interactionId: z.string().uuid(),
349
  agentId: z.string().min(1),
350
  role: z.enum(['initiator', 'responder']),
351
  counterparty: z.object({
352
    agentId: z.string().min(1),
353
    name: z.string().min(1),
354
    agentVersion: z.string().min(1),
355
    capabilities: z.array(z.string()),
356
  }),
357
  contract: InteractionContractSchema,
358
  contractHash: z.string().min(1),
359
  privateInsights: z.array(LiveSessionInsightSchema).default([]),
360
  counterpartyBrief: CounterpartyBriefSchema.optional(),
361
  issuedAt: z.string().datetime(),
362
  expiresAt: z.string().datetime(),
363
});
364
365
export const LiveSessionAcceptanceSchema = z.object({
366
  type: z.literal('openclasp.session.accepted'),
367
  version: z.literal('1'),
368
  offerId: z.string().uuid(),
369
  interactionId: z.string().uuid(),
370
  agentId: z.string().min(1),
371
  sessionId: z.string().uuid(),
372
  a2aEndpoint: z.string().url(),
373
  expiresAt: z.string().datetime(),
374
});
375
376
export const LiveSessionActivationSchema = z.object({
377
  type: z.literal('openclasp.session.activation'),
378
  version: z.literal('1'),
379
  activationId: z.string().uuid(),
380
  interactionId: z.string().uuid(),
381
  agentId: z.string().min(1),
382
  sessionId: z.string().uuid(),
383
  role: z.enum(['initiator', 'responder']),
384
  peer: z.object({
385
    agentId: z.string().min(1),
386
    sessionId: z.string().uuid(),
387
    endpoint: z.string().url(),
388
    bearerToken: z.string().min(1),
389
    verificationKey: z.string().min(1),
390
  }),
391
  reporting: z.object({
392
    endpoint: z.string().url(),
393
    completionEndpoint: z.string().url().optional(),
394
    feedbackEndpoint: z.string().url().optional(),
395
    bearerToken: z.string().min(1),
396
  }),
397
  privateInsights: z.array(LiveSessionInsightSchema).optional(),
398
  counterpartyBrief: CounterpartyBriefSchema.optional(),
399
  contractHash: z.string().min(1),
400
  activatedAt: z.string().datetime(),
401
  expiresAt: z.string().datetime(),
402
});
403
404
const SessionDetailKeySchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,63}$/);
405
406
export const StructuredSessionDetailsSchema = z
407
  .object({
408
    labels: z.array(SessionDetailKeySchema).max(32).default([]),
409
    metrics: z.record(SessionDetailKeySchema, z.number().finite()).default({}),
410
    flags: z.record(SessionDetailKeySchema, z.boolean()).default({}),
411
  })
412
  .strict()
413
  .default({ labels: [], metrics: {}, flags: {} });
414
415
export const ProgressCheckpointSchema = z
416
  .object({
417
    state: z.enum(['active', 'blocked', 'ready_to_finalize', 'done', 'cancelled']),
418
    progress: z.number().min(0).max(1),
419
    criteriaMet: z.array(z.string().min(1).max(1000)).max(100).default([]),
420
    criteriaRemaining: z.array(z.string().min(1).max(1000)).max(100).default([]),
421
    blockerCodes: z.array(SessionDetailKeySchema).max(32).default([]),
422
    topicStatus: z.enum(['in_scope', 'drifting', 'changed']),
423
    expectedRemainingTurns: z.number().int().nonnegative().max(1000).optional(),
424
    needsHuman: z.boolean().default(false),
425
    confidence: z.number().min(0).max(1),
426
  })
427
  .strict();
428
429
export const LiveSessionEventSchema = z
430
  .object({
431
    eventId: z.string().uuid(),
432
    interactionId: z.string().uuid(),
433
    agentId: z.string().min(1),
434
    sequence: z.number().int().nonnegative(),
435
    type: z.enum([
436
      'session_started',
437
      'message_sent',
438
      'progress_checkpoint',
439
      'claim',
440
      'evidence',
441
      'correction',
442
      'constraint',
443
      'task_result',
444
      'session_completed',
445
      'session_failed',
446
    ]),
447
    occurredAt: z.string().datetime(),
448
    messageHash: z
449
      .string()
450
      .regex(/^[a-zA-Z0-9_-]{43}$/)
451
      .optional(),
452
    evidenceReferences: z.array(z.string()).default([]),
453
    outcome: z.enum(['success', 'failure', 'partial']).optional(),
454
    checkpoint: ProgressCheckpointSchema.optional(),
455
    details: StructuredSessionDetailsSchema,
456
  })
457
  .superRefine((event, context) => {
458
    if (event.type === 'progress_checkpoint' && !event.checkpoint)
459
      context.addIssue({
460
        code: 'custom',
461
        path: ['checkpoint'],
462
        message: 'Progress checkpoint events require structured checkpoint data',
463
      });
464
    if (event.type !== 'progress_checkpoint' && event.checkpoint)
465
      context.addIssue({
466
        code: 'custom',
467
        path: ['checkpoint'],
468
        message: 'Checkpoint data is only valid on progress checkpoint events',
469
      });
470
  });
471
472
export const LiveSessionStateRecordSchema = z
473
  .object({
474
    interactionId: z.string().uuid(),
475
    initiatorAgentId: z.string().min(1),
476
    responderAgentId: z.string().min(1),
477
    status: z.enum(['preparing', 'active', 'completed', 'failed']),
478
    expiresAt: z.string().datetime(),
479
    createdAt: z.string().datetime(),
480
    activatedAt: z.string().datetime().optional(),
481
    completedAt: z.string().datetime().optional(),
482
    failureCode: z.literal('session_failed').optional(),
483
  })
484
  .strict();
485
486
export const CompletionOutcomeSchema = z.enum(['success', 'partial', 'failure', 'cancelled']);
487
488
export const SuccessCriterionAssessmentSchema = z
489
  .object({
490
    criterion: z.string().min(1).max(1000),
491
    status: z.enum(['met', 'partially_met', 'missed', 'unknown']),
492
    explanation: z.string().max(1000).optional(),
493
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
494
  })
495
  .strict();
496
497
export const InteractionCompletionReportSchema = z
498
  .object({
499
    reportId: z.string().uuid(),
500
    interactionId: z.string().uuid(),
501
    contractHash: z.string().min(1),
502
    reportingAgentId: z.string().min(1),
503
    counterpartyAgentId: z.string().min(1),
504
    agentVersion: z.string().min(1),
505
    outcome: CompletionOutcomeSchema,
506
    summary: z.string().min(1).max(2000),
507
    requestedOutcome: z.string().min(1).max(1000),
508
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
509
    deliverables: z.array(z.string().min(1).max(1000)).max(100).default([]),
510
    actionsTaken: z.array(z.string().min(1).max(1000)).max(100).default([]),
511
    blockers: z.array(z.string().min(1).max(1000)).max(100).default([]),
512
    scopeChanges: z.array(z.string().min(1).max(1000)).max(100).default([]),
513
    corrections: z.array(z.string().min(1).max(1000)).max(100).default([]),
514
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
515
    startedAt: z.string().datetime().optional(),
516
    completedAt: z.string().datetime(),
517
    confidence: z.number().min(0).max(1),
518
    dataSharingMode: DataSharingModeSchema.default('structured_only'),
519
    signature: SignatureSchema.optional(),
520
    submissionMethod: z
521
      .enum(['agent_signature', 'oauth_installation', 'agent_access_token', 'runtime_session'])
522
      .optional(),
523
    platformAttestation: RecordAttestationSchema.optional(),
524
  })
525
  .strict()
526
  .refine((report) => report.reportingAgentId !== report.counterpartyAgentId, {
527
    message: 'A completion report must describe an interaction with another agent',
528
    path: ['counterpartyAgentId'],
529
  })
530
  .refine(
531
    (report) => !report.startedAt || Date.parse(report.completedAt) >= Date.parse(report.startedAt),
532
    { message: 'Completion cannot precede the start time', path: ['completedAt'] },
533
  );
534
535
export const FeedbackDimensionSchema = z.enum([
536
  'overall_satisfaction',
537
  'outcome_satisfaction',
538
  'communication',
539
  'timeliness',
540
  'scope_adherence',
541
  'evidence_quality',
542
  'correction_handling',
543
  'limitation_disclosure',
544
  'reliability',
545
]);
546
547
export const FeedbackRequestSchema = z
548
  .object({
549
    requestId: z.string().uuid(),
550
    interactionId: z.string().uuid(),
551
    reviewerAgentId: z.string().min(1),
552
    subjectAgentId: z.string().min(1),
553
    status: z.enum(['pending', 'submitted', 'expired', 'waived']),
554
    requestedDimensions: z.array(FeedbackDimensionSchema).min(1),
555
    requestedAt: z.string().datetime(),
556
    dueAt: z.string().datetime(),
557
    platformAttestation: RecordAttestationSchema.optional(),
558
  })
559
  .strict()
560
  .refine((request) => request.reviewerAgentId !== request.subjectAgentId, {
561
    message: 'Feedback must concern another agent',
562
    path: ['subjectAgentId'],
563
  })
564
  .refine((request) => Date.parse(request.dueAt) > Date.parse(request.requestedAt), {
565
    message: 'Feedback due date must follow its request date',
566
    path: ['dueAt'],
567
  });
568
569
export const InteractionFeedbackSchema = z
570
  .object({
571
    feedbackId: z.string().uuid(),
572
    requestId: z.string().uuid(),
573
    interactionId: z.string().uuid(),
574
    reviewerAgentId: z.string().min(1),
575
    subjectAgentId: z.string().min(1),
576
    reviewerAgentVersion: z.string().min(1),
577
    ratings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)),
578
    wouldWorkAgain: z.enum(['yes', 'no', 'unsure']),
579
    reasonCodes: z.array(z.string().min(1).max(128)).max(32).default([]),
580
    privateComment: z.string().max(1000).optional(),
581
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
582
    confidence: z.number().min(0).max(1),
583
    submittedAt: z.string().datetime(),
584
    signature: SignatureSchema.optional(),
585
    submissionMethod: z
586
      .enum(['agent_signature', 'oauth_installation', 'agent_access_token', 'runtime_session'])
587
      .optional(),
588
    platformAttestation: RecordAttestationSchema.optional(),
589
  })
590
  .strict()
591
  .refine((feedback) => feedback.reviewerAgentId !== feedback.subjectAgentId, {
592
    message: 'Feedback must concern another agent',
593
    path: ['subjectAgentId'],
594
  })
595
  .refine((feedback) => Object.keys(feedback.ratings).length > 0, {
596
    message: 'At least one feedback rating is required',
597
    path: ['ratings'],
598
  });
599
600
export const InteractionConclusionSchema = z
601
  .object({
602
    conclusionId: z.string().uuid(),
603
    interactionId: z.string().uuid(),
604
    contractHash: z.string().min(1),
605
    outcome: CompletionOutcomeSchema,
606
    consensus: z.enum([
607
      'bilateral_agreement',
608
      'bilateral_partial_agreement',
609
      'conflicting',
610
      'unilateral',
611
      'insufficient',
612
    ]),
613
    lifecycle: z.enum(['provisional', 'final']).optional(),
614
    confidence: z.number().min(0).max(1).optional(),
615
    missingReportAgentIds: z.array(z.string().min(1)).max(2).optional(),
616
    pendingFeedbackAgentIds: z.array(z.string().min(1)).max(2).optional(),
617
    peerReportStatus: z.enum(['awaiting', 'unreachable', 'received', 'timed_out']).optional(),
618
    summary: z.string().min(1).max(2000),
619
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
620
    reportIds: z.array(z.string().uuid()).max(2),
621
    feedbackIds: z.array(z.string().uuid()).max(2),
622
    averageRatings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)).default({}),
623
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
624
    generatedAt: z.string().datetime(),
625
    platformAttestation: RecordAttestationSchema.optional(),
626
  })
627
  .strict();
628
629
export const LearningEligibilityDecisionSchema = z
630
  .object({
631
    decisionId: z.string().uuid(),
632
    interactionId: z.string().uuid(),
633
    eligible: z.boolean(),
634
    reasons: z.array(z.string().min(1).max(500)).min(1).max(50),
635
    sampleWeight: z.number().min(0).max(1),
636
    reportIds: z.array(z.string().uuid()).max(2),
637
    feedbackIds: z.array(z.string().uuid()).max(2),
638
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
639
    manipulationSignals: z.array(z.string().min(1).max(128)).max(20).default([]),
640
    contributionMode: z.enum(['local_only', 'network_aggregate']),
641
    structuredDataOnly: z.literal(true),
642
    decidedAt: z.string().datetime(),
643
    platformAttestation: RecordAttestationSchema.optional(),
644
  })
645
  .strict();
646
647
export const BehaviouralProfileDeltaSchema = z
648
  .object({
649
    deltaId: z.string().uuid(),
650
    interactionId: z.string().uuid(),
651
    agentId: z.string().min(1),
652
    agentVersion: z.string().min(1),
653
    taskCategory: z.string().min(1),
654
    sampleWeight: z.number().min(0).max(1),
655
    dimensionDeltas: z.record(z.string().min(1).max(128), z.number().min(-1).max(1)),
656
    explanation: z.string().min(1).max(2000),
657
    appliedAt: z.string().datetime(),
658
    platformAttestation: RecordAttestationSchema.optional(),
659
  })
660
  .strict();
661
662
export const BehaviouralDimensionNameSchema = z.enum([
663
  'completion',
664
  'acceptance',
665
  'specification',
666
  'deadline',
667
  'communication',
668
  'evidence',
669
  'scope',
670
  'correction',
671
  'limitations',
672
  'disputes',
673
]);
674
675
export const ContextualReliabilitySummarySchema = z
676
  .object({
677
    agentId: z.string().min(1),
678
    agentVersion: z.string().min(1),
679
    taskCategory: z.string().min(1),
680
    score: z.number().min(0).max(1),
681
    confidence: z.object({
682
      level: z.enum(['low', 'medium', 'high']),
683
      value: z.number().min(0).max(1),
684
      evidenceCount: z.number().int().nonnegative(),
685
      effectiveSampleSize: z.number().nonnegative(),
686
    }),
687
    trend: z.object({
688
      direction: z.enum(['improving', 'stable', 'declining']),
689
      delta: z.number().min(-1).max(1),
690
    }),
691
    strengths: z
692
      .array(
693
        z.object({
694
          dimension: BehaviouralDimensionNameSchema,
695
          score: z.number().min(0).max(1),
696
        }),
697
      )
698
      .max(3),
699
    risks: z
700
      .array(
701
        z.object({
702
          dimension: BehaviouralDimensionNameSchema,
703
          score: z.number().min(0).max(1),
704
          reason: z.string().min(1).max(300),
705
        }),
706
      )
707
      .max(3),
708
    versionStatus: z.object({
709
      currentVersion: z.string().min(1),
710
      evidenceVersion: z.string().min(1),
711
      status: z.enum(['current', 'reduced_confidence']),
712
    }),
713
    source: z.literal('private_verified_history'),
714
    updatedAt: z.string().datetime(),
715
  })
716
  .strict();
717
718
export const TrustEnvelopeSchema = z.object({
719
  protocolVersion: z.literal(PROTOCOL_VERSION),
720
  interactionId: z.string().uuid(),
721
  requestingAgentId: z.string(),
722
  respondingAgentId: z.string(),
723
  rootControllerId: z.string(),
724
  parentAgentId: z.string().optional(),
725
  agentVersion: z.string(),
726
  delegationId: z.string().uuid().optional(),
727
  requestedCapability: z.string(),
728
  taskCategory: z.string(),
729
  contractHash: z.string(),
730
  dataSharingMode: DataSharingModeSchema,
731
  evidenceRequirements: z.array(z.string()),
732
  timestamp: z.string().datetime(),
733
  expiresAt: z.string().datetime(),
734
  nonce: z.string().min(12),
735
  signature: SignatureSchema.optional(),
736
});
737
738
export const EventTypeSchema = z.enum([
739
  'claim',
740
  'evidence',
741
  'constraint',
742
  'commitment',
743
  'proposal',
744
  'objection',
745
  'policy_warning',
746
  'policy_violation',
747
  'private_suggestion',
748
  'shared_intervention',
749
  'delegation',
750
  'task_result',
751
  'resolution',
752
  'receipt',
753
  'feedback',
754
  'dispute',
755
]);
756
export const InteractionEventSchema = z.object({
757
  protocolVersion: z.literal(PROTOCOL_VERSION),
758
  eventId: z.string().uuid(),
759
  interactionId: z.string().uuid(),
760
  eventType: EventTypeSchema,
761
  agentId: z.string(),
762
  agentVersion: z.string(),
763
  timestamp: z.string().datetime(),
764
  visibility: VisibilitySchema,
765
  provenance: ProvenanceSchema,
766
  payloadHash: z.string(),
767
  payload: z.record(z.string(), z.unknown()),
768
  evidenceRefs: z.array(z.string()).optional(),
769
  signature: SignatureSchema.optional(),
770
});
771
772
export const FeedbackSchema = z.object({
773
  feedbackId: z.string().uuid(),
774
  interactionId: z.string().uuid(),
775
  receiptId: z.string().uuid(),
776
  reviewerAgentId: z.string(),
777
  subjectAgentId: z.string(),
778
  taskCompleted: z.boolean(),
779
  outputAccepted: z.boolean(),
780
  specificationMatched: z.boolean(),
781
  deadlineMet: z.boolean(),
782
  communicationQuality: z.number().min(0).max(1),
783
  evidenceProvided: z.boolean(),
784
  scopeRespected: z.boolean(),
785
  disputeRaised: z.boolean(),
786
  comment: z.string().max(1000).optional(),
787
  submittedAt: z.string().datetime(),
788
  signature: SignatureSchema.optional(),
789
});
790
791
export const ReceiptSchema = z.object({
792
  receiptId: z.string().uuid(),
793
  interactionId: z.string().uuid(),
794
  participants: z.array(z.string()),
795
  agentVersions: z.record(z.string(), z.string()),
796
  contractHash: z.string(),
797
  startedAt: z.string().datetime(),
798
  completedAt: z.string().datetime(),
799
  outcome: z.enum(['success', 'partial', 'failure', 'cancelled']),
800
  commitmentsFulfilled: z.array(z.string()),
801
  commitmentsMissed: z.array(z.string()),
802
  evidenceHashes: z.array(z.string()),
803
  policyWarnings: z.array(z.string()),
804
  policyViolations: z.array(z.string()),
805
  disputeStatus: z.enum(['none', 'open', 'resolved']),
806
  delegationChainHash: z.string(),
807
  unilateral: z.boolean(),
808
  provisional: z.boolean().optional(),
809
  confidence: z.number().min(0).max(1).optional(),
810
  signatures: z.record(z.string(), SignatureSchema).default({}),
811
  completionReportIds: z.array(z.string().uuid()).max(2).optional(),
812
  conclusionId: z.string().uuid().optional(),
813
  platformAttestation: RecordAttestationSchema.optional(),
814
});
815
816
export const RiskDecisionSchema = z.object({
817
  decision: z.enum(['ALLOW', 'CHALLENGE', 'DENY']),
818
  confidence: z.number().min(0).max(1),
819
  taskCategory: z.string(),
820
  sampleSize: z.number().int().nonnegative(),
821
  dimensions: z.record(z.string(), z.number()),
822
  reasons: z.array(z.string()),
823
  warnings: z.array(z.string()),
824
  dataFreshness: z.record(z.string(), z.string()),
825
  requiredChallenges: z.array(z.string()),
826
});
827
828
export const FactCheckResultSchema = z.object({
829
  claim: z.string(),
830
  claimType: z.enum(['objective', 'subjective', 'prediction']),
831
  status: z.enum([
832
    'verified',
833
    'supported',
834
    'contradicted',
835
    'unverified',
836
    'not_fact_checkable',
837
    'insufficient_permission',
838
  ]),
839
  confidence: z.number().min(0).max(1),
840
  evidenceReferences: z.array(z.string()),
841
  sourceAuthority: z.string(),
842
  sourceFreshness: z.string(),
843
  contradictingEvidence: z.array(z.string()),
844
  suggestedNextAction: z.string(),
845
});
846
847
export type AgentIdentity = z.infer<typeof AgentIdentitySchema>;
848
export type DelegationCredential = z.infer<typeof DelegationCredentialSchema>;
849
export type InteractionContract = z.infer<typeof InteractionContractSchema>;
850
export type TrustEnvelope = z.infer<typeof TrustEnvelopeSchema>;
851
export type InteractionEvent = z.infer<typeof InteractionEventSchema>;
852
export type Feedback = z.infer<typeof FeedbackSchema>;
853
export type Receipt = z.infer<typeof ReceiptSchema>;
854
export type RiskDecision = z.infer<typeof RiskDecisionSchema>;
855
export type FactCheckResult = z.infer<typeof FactCheckResultSchema>;
856
export type ExpectationManifest = z.infer<typeof ExpectationManifestSchema>;
857
export type AgentTransport = z.infer<typeof AgentTransportSchema>;
858
export type AgentMode = z.infer<typeof AgentModeSchema>;
859
export type AgentPresence = z.infer<typeof AgentPresenceSchema>;
860
export type PublicAgentVerification = z.infer<typeof PublicAgentVerificationSchema>;
861
export type PublicAgentCard = z.infer<typeof PublicAgentCardSchema>;
862
export type AgentResolution = z.infer<typeof AgentResolutionSchema>;
863
export type HostedMessage = z.infer<typeof HostedMessageSchema>;
864
export type HostedThread = z.infer<typeof HostedThreadSchema>;
865
export type ContractAcceptance = z.infer<typeof ContractAcceptanceSchema>;
866
export type ContractRevision = z.infer<typeof ContractRevisionSchema>;
867
export type FederatedInteraction = z.infer<typeof FederatedInteractionSchema>;
868
export type LiveSessionInsight = z.infer<typeof LiveSessionInsightSchema>;
869
export type LiveSessionOffer = z.infer<typeof LiveSessionOfferSchema>;
870
export type LiveSessionAcceptance = z.infer<typeof LiveSessionAcceptanceSchema>;
871
export type LiveSessionActivation = z.infer<typeof LiveSessionActivationSchema>;
872
export type LiveSessionEvent = z.infer<typeof LiveSessionEventSchema>;
873
export type LiveSessionStateRecord = z.infer<typeof LiveSessionStateRecordSchema>;
874
export type RequirementAssessment = z.infer<typeof RequirementAssessmentSchema>;
875
export type CounterpartyBrief = z.infer<typeof CounterpartyBriefSchema>;
876
export type CompletionOutcome = z.infer<typeof CompletionOutcomeSchema>;
877
export type SuccessCriterionAssessment = z.infer<typeof SuccessCriterionAssessmentSchema>;
878
export type InteractionCompletionReport = z.infer<typeof InteractionCompletionReportSchema>;
879
export type FeedbackDimension = z.infer<typeof FeedbackDimensionSchema>;
880
export type FeedbackRequest = z.infer<typeof FeedbackRequestSchema>;
881
export type InteractionFeedback = z.infer<typeof InteractionFeedbackSchema>;
882
export type InteractionConclusion = z.infer<typeof InteractionConclusionSchema>;
883
export type LearningEligibilityDecision = z.infer<typeof LearningEligibilityDecisionSchema>;
884
export type BehaviouralProfileDelta = z.infer<typeof BehaviouralProfileDeltaSchema>;
885
export type BehaviouralDimensionName = z.infer<typeof BehaviouralDimensionNameSchema>;
886
export type ContextualReliabilitySummary = z.infer<typeof ContextualReliabilitySummarySchema>;
887
export type RecordAttestation = z.infer<typeof RecordAttestationSchema>;
888
export type SourceRecordEnvelope = z.infer<typeof SourceRecordEnvelopeSchema>;
889
export type RetentionClass = z.infer<typeof RetentionClassSchema>;
890
export type LearningScope = z.infer<typeof LearningScopeSchema>;
891
892
export interface KeyPair {
893
  keyId: string;
894
  publicKey: string;
895
  privateKey: string;
896
}
897
898
export function createKeyPair(keyId: string = randomUUID()): KeyPair {
899
  const pair = generateKeyPairSync('ed25519');
900
  return {
901
    keyId,
902
    publicKey: pair.publicKey.export({ type: 'spki', format: 'der' }).toString('base64url'),
903
    privateKey: pair.privateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64url'),
904
  };
905
}
906
907
export function canonicalHash(value: unknown): string {
908
  return createHash('sha256').update(canonicalize(value)).digest('base64url');
909
}
910
911
export function verifyRecordAttestation(
912
  value: Record<string, unknown>,
913
  publicKey: string,
914
): boolean {
915
  const attestation = RecordAttestationSchema.safeParse(value.platformAttestation);
916
  if (!attestation.success) return false;
917
  const unsignedValue = structuredClone(value);
918
  delete unsignedValue.platformAttestation;
919
  const digest = canonicalHash(unsignedValue);
920
  if (digest !== attestation.data.digest) return false;
921
  return verify(
922
    null,
923
    Buffer.from(digest),
924
    {
925
      key: Buffer.from(publicKey, 'base64url'),
926
      type: 'spki',
927
      format: 'der',
928
    },
929
    Buffer.from(attestation.data.value, 'base64url'),
930
  );
931
}
932
933
function unsigned(value: Record<string, unknown>): Record<string, unknown> {
934
  const copy = structuredClone(value);
935
  delete copy.signature;
936
  delete copy.signatures;
937
  return copy;
938
}
939
940
export function signObject<T extends Record<string, unknown>>(
941
  value: T,
942
  key: KeyPair,
943
): T & { signature: z.infer<typeof SignatureSchema> } {
944
  const payload = Buffer.from(canonicalize(unsigned(value)));
945
  const privateKey = {
946
    key: Buffer.from(key.privateKey, 'base64url'),
947
    type: 'pkcs8' as const,
948
    format: 'der' as const,
949
  };
950
  return {
951
    ...value,
952
    signature: {
953
      algorithm: 'Ed25519',
954
      keyId: key.keyId,
955
      value: sign(null, payload, privateKey).toString('base64url'),
956
    },
957
  };
958
}
959
960
export function verifyObject(value: Record<string, unknown>, publicKey: string): boolean {
961
  const parsed = SignatureSchema.safeParse(value.signature);
962
  if (!parsed.success) return false;
963
  const key = {
964
    key: Buffer.from(publicKey, 'base64url'),
965
    type: 'spki' as const,
966
    format: 'der' as const,
967
  };
968
  return verify(
969
    null,
970
    Buffer.from(canonicalize(unsigned(value))),
971
    key,
972
    Buffer.from(parsed.data.value, 'base64url'),
973
  );
974
}
975
976
export function signNamed<T extends Record<string, unknown>>(
977
  value: T,
978
  signerId: string,
979
  key: KeyPair,
980
): T & { signatures: Record<string, z.infer<typeof SignatureSchema>> } {
981
  const payload = Buffer.from(canonicalize(unsigned(value)));
982
  const privateKey = {
983
    key: Buffer.from(key.privateKey, 'base64url'),
984
    type: 'pkcs8' as const,
985
    format: 'der' as const,
986
  };
987
  const signature = {
988
    algorithm: 'Ed25519' as const,
989
    keyId: key.keyId,
990
    value: sign(null, payload, privateKey).toString('base64url'),
991
  };
992
  return {
993
    ...value,
994
    signatures: { ...((value.signatures as object) ?? {}), [signerId]: signature },
995
  };
996
}
997
998
export function verifyNamed(
999
  value: Record<string, unknown>,
1000
  signerId: string,
1001
  publicKey: string,
1002
): boolean {
1003
  const signatures = value.signatures as Record<string, unknown> | undefined;
1004
  const parsed = SignatureSchema.safeParse(signatures?.[signerId]);
1005
  if (!parsed.success) return false;
1006
  const key = {
1007
    key: Buffer.from(publicKey, 'base64url'),
1008
    type: 'spki' as const,
1009
    format: 'der' as const,
1010
  };
1011
  return verify(
1012
    null,
1013
    Buffer.from(canonicalize(unsigned(value))),
1014
    key,
1015
    Buffer.from(parsed.data.value, 'base64url'),
1016
  );
1017
}