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

Viewing files at commit 479efea741d5Commit detailsView latest

packages/protocol/src/index.ts 806 lines · 28.0 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 ExpectationManifestSchema = z.object({
40
  requiredInputs: z.array(z.string()).default([]),
41
  supportedTaskCategories: z.array(z.string()).min(1),
42
  responseTimeMs: z.number().int().positive().optional(),
43
  dataRequirements: z.array(z.string()).default([]),
44
  prohibitedData: z.array(z.string()).default([]),
45
  humanApprovalThresholds: z.array(z.string()).default([]),
46
  delegationPolicy: z.string().default('explicit'),
47
  evidenceRequirements: z.array(z.string()).default([]),
48
  factCheckingPreference: z
49
    .enum(['none', 'important_claims', 'all_objective_claims'])
50
    .default('important_claims'),
51
  mediationPreference: z.enum(['never', 'mutual_consent']).default('mutual_consent'),
52
  retentionDays: z.number().int().nonnegative().default(30),
53
  communicationFormats: z.array(z.string()).default(['text/plain']),
54
  cancellationPolicy: z.string().default('either_party'),
55
  provenance: ProvenanceSchema,
56
});
57
58
export const AgentIdentitySchema = z.object({
59
  protocolVersion: z.literal(PROTOCOL_VERSION),
60
  agentId: z.string().min(1),
61
  publicKey: z.string().min(1),
62
  keyId: z.string().min(1),
63
  operatorRef: z.string().min(1),
64
  assurance: z.enum(['pseudonymous', 'domain_associated', 'organization_associated']),
65
  agentVersion: z.string().min(1),
66
  capabilities: z.array(z.string()),
67
  supportedProtocols: z.array(z.string()),
68
  parentAgentId: z.string().optional(),
69
  rootControllerId: z.string().min(1),
70
  revoked: z.boolean().default(false),
71
  provenance: ProvenanceSchema,
72
  createdAt: z.string().datetime(),
73
  signature: SignatureSchema.optional(),
74
});
75
76
export const DelegationCredentialSchema = z.object({
77
  protocolVersion: z.literal(PROTOCOL_VERSION),
78
  delegationId: z.string().uuid(),
79
  parentAgentId: z.string(),
80
  childAgentId: z.string(),
81
  rootControllerId: z.string(),
82
  capabilities: z.array(z.string()).min(1),
83
  issuedAt: z.string().datetime(),
84
  expiresAt: z.string().datetime(),
85
  revoked: z.boolean().default(false),
86
  signature: SignatureSchema.optional(),
87
});
88
89
export const InteractionContractSchema = z.object({
90
  protocolVersion: z.literal(PROTOCOL_VERSION),
91
  interactionId: z.string().uuid(),
92
  purpose: z.string(),
93
  parties: z.array(z.string()).min(1),
94
  taskCategory: z.string(),
95
  requestedOutcome: z.string(),
96
  successCriteria: z.array(z.string()),
97
  allowedActions: z.array(z.string()),
98
  prohibitedActions: z.array(z.string()),
99
  allowedData: z.array(z.string()),
100
  prohibitedData: z.array(z.string()),
101
  deadline: z.string().datetime().optional(),
102
  evidenceRequirements: z.array(z.string()),
103
  delegationRules: z.array(z.string()),
104
  humanApprovalRequirements: z.array(z.string()),
105
  factCheckingPolicy: z.string(),
106
  mediationPolicy: z.enum(['none', 'mutual_consent']),
107
  retentionDays: z.number().int().nonnegative(),
108
  completionConditions: z.array(z.string()),
109
  cancellationConditions: z.array(z.string()),
110
  signatures: z.record(z.string(), SignatureSchema).default({}),
111
});
112
113
export const AgentTransportSchema = z.object({
114
  protocol: z.literal('A2A/1.0'),
115
  protocolBinding: z.string().min(1).default('JSONRPC'),
116
  endpoint: z.string().url(),
117
  managedBy: z.enum(['agent', 'openclasp']).default('agent'),
118
});
119
120
export const AgentModeSchema = z.enum(['persistent_runtime', 'temporary_chat']);
121
122
export const AgentPresenceSchema = z.object({
123
  status: z.enum(['online', 'offline']),
124
  lastSeenAt: z.string().datetime().optional(),
125
  checkedAt: z.string().datetime(),
126
});
127
128
export const PublicAgentCardSchema = z.object({
129
  protocolVersion: z.literal(PROTOCOL_VERSION),
130
  agentId: z.string().min(1),
131
  name: z.string().min(1),
132
  description: z.string(),
133
  framework: z.string().min(1),
134
  agentVersion: z.string().min(1),
135
  capabilities: z.array(z.string()),
136
  limitations: z.array(z.string()),
137
  assurance: z.enum(['oauth_authenticated', 'cryptographically_verified']),
138
  agentMode: AgentModeSchema.default('persistent_runtime'),
139
  transports: z.array(AgentTransportSchema),
140
  cardUrl: z.string().url(),
141
  a2aAgentCardUrl: z.string().url(),
142
  extensionUri: z.string().url(),
143
  presence: AgentPresenceSchema.optional(),
144
  publishedAt: z.string().datetime(),
145
  updatedAt: z.string().datetime(),
146
});
147
148
export const HostedThreadStatusSchema = z.enum(['open', 'closed']);
149
150
export const HostedMessageSchema = z.object({
151
  messageId: z.string().uuid(),
152
  threadId: z.string().uuid(),
153
  interactionId: z.string().uuid(),
154
  senderAgentId: z.string().min(1),
155
  recipientAgentId: z.string().min(1),
156
  contentType: z.literal('text/plain'),
157
  content: z.string().min(1).max(20_000),
158
  contentHash: z.string().regex(/^[a-zA-Z0-9_-]{43}$/),
159
  delivery: z.enum(['accepted', 'delivered', 'read']),
160
  createdAt: z.string().datetime(),
161
  readAt: z.string().datetime().optional(),
162
});
163
164
export const HostedThreadSchema = z.object({
165
  threadId: z.string().uuid(),
166
  interactionId: z.string().uuid(),
167
  participantAgentIds: z.tuple([z.string().min(1), z.string().min(1)]),
168
  status: HostedThreadStatusSchema,
169
  privacyMode: z.literal('openclasp_hosted_temporary'),
170
  unreadCount: z.number().int().nonnegative().default(0),
171
  createdAt: z.string().datetime(),
172
  updatedAt: z.string().datetime(),
173
  expiresAt: z.string().datetime(),
174
});
175
176
export const ContractAcceptanceSchema = z.object({
177
  agentId: z.string().min(1),
178
  method: z.enum(['oauth_installation', 'oauth_account', 'policy_auto_accept', 'ed25519']),
179
  termsHash: z.string().min(1),
180
  acceptedAt: z.string().datetime(),
181
  signature: SignatureSchema.optional(),
182
});
183
184
export const FederatedInteractionStatusSchema = z.enum([
185
  'pending',
186
  'active',
187
  'rejected',
188
  'expired',
189
  'cancelled',
190
  'completed',
191
]);
192
193
export const FederatedInteractionSchema = z.object({
194
  protocolVersion: z.literal(PROTOCOL_VERSION),
195
  interactionId: z.string().uuid(),
196
  initiatorAgentId: z.string().min(1),
197
  responderAgentId: z.string().min(1),
198
  status: FederatedInteractionStatusSchema,
199
  contract: InteractionContractSchema,
200
  termsHash: z.string().min(1),
201
  acceptances: z.record(z.string(), ContractAcceptanceSchema),
202
  initiatorTransport: AgentTransportSchema.optional(),
203
  responderTransport: AgentTransportSchema,
204
  createdAt: z.string().datetime(),
205
  updatedAt: z.string().datetime(),
206
  expiresAt: z.string().datetime(),
207
});
208
209
export const LiveSessionInsightSchema = z
210
  .object({
211
    code: z.string().min(1),
212
    severity: z.enum(['info', 'caution', 'high']),
213
    message: z.string().min(1),
214
    evidenceReferences: z.array(z.string()).default([]),
215
    requirementReferences: z.array(z.string()).default([]),
216
    confidence: z.number().min(0).max(1).optional(),
217
  })
218
  .strict();
219
220
export const RequirementAssessmentSchema = z
221
  .object({
222
    requirementId: z.string().min(1).max(128),
223
    requirement: z.string().min(1).max(1000),
224
    kind: z.enum([
225
      'capability',
226
      'limitation',
227
      'success_criterion',
228
      'deadline',
229
      'evidence',
230
      'scope',
231
      'data',
232
      'communication',
233
      'delegation',
234
    ]),
235
    status: z.enum(['match', 'partial', 'mismatch', 'unknown']),
236
    reason: z.string().min(1).max(1000),
237
    confidence: z.number().min(0).max(1),
238
    sources: z
239
      .array(
240
        z.enum(['contract', 'self_declared', 'eligible_history', 'evidence', 'version_change']),
241
      )
242
      .min(1),
243
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
244
  })
245
  .strict();
246
247
export const CounterpartyBriefSchema = z
248
  .object({
249
    briefId: z.string().uuid(),
250
    interactionId: z.string().uuid(),
251
    contractHash: z.string().min(1),
252
    recipientAgentId: z.string().min(1),
253
    subjectAgentId: z.string().min(1),
254
    taskCategory: z.string().min(1),
255
    decision: z.enum(['ALLOW', 'CHALLENGE', 'DENY']),
256
    requirements: z.array(RequirementAssessmentSchema).max(100),
257
    insights: z.array(LiveSessionInsightSchema).max(100),
258
    relevantSampleSize: z.number().int().nonnegative(),
259
    historyConfidence: z.number().min(0).max(1),
260
    subjectAgentVersion: z.string().min(1),
261
    recommendedContractChanges: z.array(z.string().min(1).max(1000)).max(50).default([]),
262
    generatedAt: z.string().datetime(),
263
    expiresAt: z.string().datetime(),
264
    signature: SignatureSchema.optional(),
265
  })
266
  .strict()
267
  .refine((brief) => brief.recipientAgentId !== brief.subjectAgentId, {
268
    message: 'A counterparty brief must describe another agent',
269
    path: ['subjectAgentId'],
270
  })
271
  .refine((brief) => Date.parse(brief.expiresAt) > Date.parse(brief.generatedAt), {
272
    message: 'Counterparty brief expiry must be after generation',
273
    path: ['expiresAt'],
274
  });
275
276
export const LiveSessionOfferSchema = z.object({
277
  type: z.literal('openclasp.session.offer'),
278
  version: z.literal('1'),
279
  offerId: z.string().uuid(),
280
  interactionId: z.string().uuid(),
281
  agentId: z.string().min(1),
282
  role: z.enum(['initiator', 'responder']),
283
  counterparty: z.object({
284
    agentId: z.string().min(1),
285
    name: z.string().min(1),
286
    agentVersion: z.string().min(1),
287
    capabilities: z.array(z.string()),
288
  }),
289
  contract: InteractionContractSchema,
290
  contractHash: z.string().min(1),
291
  privateInsights: z.array(LiveSessionInsightSchema).default([]),
292
  counterpartyBrief: CounterpartyBriefSchema.optional(),
293
  issuedAt: z.string().datetime(),
294
  expiresAt: z.string().datetime(),
295
});
296
297
export const LiveSessionAcceptanceSchema = z.object({
298
  type: z.literal('openclasp.session.accepted'),
299
  version: z.literal('1'),
300
  offerId: z.string().uuid(),
301
  interactionId: z.string().uuid(),
302
  agentId: z.string().min(1),
303
  sessionId: z.string().uuid(),
304
  a2aEndpoint: z.string().url(),
305
  expiresAt: z.string().datetime(),
306
});
307
308
export const LiveSessionActivationSchema = z.object({
309
  type: z.literal('openclasp.session.activation'),
310
  version: z.literal('1'),
311
  activationId: z.string().uuid(),
312
  interactionId: z.string().uuid(),
313
  agentId: z.string().min(1),
314
  sessionId: z.string().uuid(),
315
  role: z.enum(['initiator', 'responder']),
316
  peer: z.object({
317
    agentId: z.string().min(1),
318
    sessionId: z.string().uuid(),
319
    endpoint: z.string().url(),
320
    bearerToken: z.string().min(1),
321
    verificationKey: z.string().min(1),
322
  }),
323
  reporting: z.object({
324
    endpoint: z.string().url(),
325
    completionEndpoint: z.string().url().optional(),
326
    feedbackEndpoint: z.string().url().optional(),
327
    bearerToken: z.string().min(1),
328
  }),
329
  privateInsights: z.array(LiveSessionInsightSchema).optional(),
330
  counterpartyBrief: CounterpartyBriefSchema.optional(),
331
  contractHash: z.string().min(1),
332
  activatedAt: z.string().datetime(),
333
  expiresAt: z.string().datetime(),
334
});
335
336
const SessionDetailKeySchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,63}$/);
337
338
export const StructuredSessionDetailsSchema = z
339
  .object({
340
    labels: z.array(SessionDetailKeySchema).max(32).default([]),
341
    metrics: z.record(SessionDetailKeySchema, z.number().finite()).default({}),
342
    flags: z.record(SessionDetailKeySchema, z.boolean()).default({}),
343
  })
344
  .strict()
345
  .default({ labels: [], metrics: {}, flags: {} });
346
347
export const LiveSessionEventSchema = z.object({
348
  eventId: z.string().uuid(),
349
  interactionId: z.string().uuid(),
350
  agentId: z.string().min(1),
351
  sequence: z.number().int().nonnegative(),
352
  type: z.enum([
353
    'session_started',
354
    'message_sent',
355
    'claim',
356
    'evidence',
357
    'correction',
358
    'constraint',
359
    'task_result',
360
    'session_completed',
361
    'session_failed',
362
  ]),
363
  occurredAt: z.string().datetime(),
364
  messageHash: z
365
    .string()
366
    .regex(/^[a-zA-Z0-9_-]{43}$/)
367
    .optional(),
368
  evidenceReferences: z.array(z.string()).default([]),
369
  outcome: z.enum(['success', 'failure', 'partial']).optional(),
370
  details: StructuredSessionDetailsSchema,
371
});
372
373
export const CompletionOutcomeSchema = z.enum(['success', 'partial', 'failure', 'cancelled']);
374
375
export const SuccessCriterionAssessmentSchema = z
376
  .object({
377
    criterion: z.string().min(1).max(1000),
378
    status: z.enum(['met', 'partially_met', 'missed', 'unknown']),
379
    explanation: z.string().max(1000).optional(),
380
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
381
  })
382
  .strict();
383
384
export const InteractionCompletionReportSchema = z
385
  .object({
386
    reportId: z.string().uuid(),
387
    interactionId: z.string().uuid(),
388
    contractHash: z.string().min(1),
389
    reportingAgentId: z.string().min(1),
390
    counterpartyAgentId: z.string().min(1),
391
    agentVersion: z.string().min(1),
392
    outcome: CompletionOutcomeSchema,
393
    summary: z.string().min(1).max(2000),
394
    requestedOutcome: z.string().min(1).max(1000),
395
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
396
    deliverables: z.array(z.string().min(1).max(1000)).max(100).default([]),
397
    actionsTaken: z.array(z.string().min(1).max(1000)).max(100).default([]),
398
    blockers: z.array(z.string().min(1).max(1000)).max(100).default([]),
399
    scopeChanges: z.array(z.string().min(1).max(1000)).max(100).default([]),
400
    corrections: z.array(z.string().min(1).max(1000)).max(100).default([]),
401
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
402
    startedAt: z.string().datetime().optional(),
403
    completedAt: z.string().datetime(),
404
    confidence: z.number().min(0).max(1),
405
    dataSharingMode: DataSharingModeSchema.default('structured_only'),
406
    signature: SignatureSchema.optional(),
407
    submissionMethod: z
408
      .enum(['agent_signature', 'oauth_installation', 'agent_access_token', 'runtime_session'])
409
      .optional(),
410
    platformAttestation: RecordAttestationSchema.optional(),
411
  })
412
  .strict()
413
  .refine((report) => report.reportingAgentId !== report.counterpartyAgentId, {
414
    message: 'A completion report must describe an interaction with another agent',
415
    path: ['counterpartyAgentId'],
416
  })
417
  .refine(
418
    (report) => !report.startedAt || Date.parse(report.completedAt) >= Date.parse(report.startedAt),
419
    { message: 'Completion cannot precede the start time', path: ['completedAt'] },
420
  );
421
422
export const FeedbackDimensionSchema = z.enum([
423
  'overall_satisfaction',
424
  'outcome_satisfaction',
425
  'communication',
426
  'timeliness',
427
  'scope_adherence',
428
  'evidence_quality',
429
  'correction_handling',
430
  'reliability',
431
]);
432
433
export const FeedbackRequestSchema = z
434
  .object({
435
    requestId: z.string().uuid(),
436
    interactionId: z.string().uuid(),
437
    reviewerAgentId: z.string().min(1),
438
    subjectAgentId: z.string().min(1),
439
    status: z.enum(['pending', 'submitted', 'expired', 'waived']),
440
    requestedDimensions: z.array(FeedbackDimensionSchema).min(1),
441
    requestedAt: z.string().datetime(),
442
    dueAt: z.string().datetime(),
443
    platformAttestation: RecordAttestationSchema.optional(),
444
  })
445
  .strict()
446
  .refine((request) => request.reviewerAgentId !== request.subjectAgentId, {
447
    message: 'Feedback must concern another agent',
448
    path: ['subjectAgentId'],
449
  })
450
  .refine((request) => Date.parse(request.dueAt) > Date.parse(request.requestedAt), {
451
    message: 'Feedback due date must follow its request date',
452
    path: ['dueAt'],
453
  });
454
455
export const InteractionFeedbackSchema = z
456
  .object({
457
    feedbackId: z.string().uuid(),
458
    requestId: z.string().uuid(),
459
    interactionId: z.string().uuid(),
460
    reviewerAgentId: z.string().min(1),
461
    subjectAgentId: z.string().min(1),
462
    reviewerAgentVersion: z.string().min(1),
463
    ratings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)),
464
    wouldWorkAgain: z.enum(['yes', 'no', 'unsure']),
465
    reasonCodes: z.array(z.string().min(1).max(128)).max(32).default([]),
466
    privateComment: z.string().max(1000).optional(),
467
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
468
    confidence: z.number().min(0).max(1),
469
    submittedAt: z.string().datetime(),
470
    signature: SignatureSchema.optional(),
471
    submissionMethod: z
472
      .enum(['agent_signature', 'oauth_installation', 'agent_access_token', 'runtime_session'])
473
      .optional(),
474
    platformAttestation: RecordAttestationSchema.optional(),
475
  })
476
  .strict()
477
  .refine((feedback) => feedback.reviewerAgentId !== feedback.subjectAgentId, {
478
    message: 'Feedback must concern another agent',
479
    path: ['subjectAgentId'],
480
  })
481
  .refine((feedback) => Object.keys(feedback.ratings).length > 0, {
482
    message: 'At least one feedback rating is required',
483
    path: ['ratings'],
484
  });
485
486
export const InteractionConclusionSchema = z
487
  .object({
488
    conclusionId: z.string().uuid(),
489
    interactionId: z.string().uuid(),
490
    contractHash: z.string().min(1),
491
    outcome: CompletionOutcomeSchema,
492
    consensus: z.enum([
493
      'bilateral_agreement',
494
      'bilateral_partial_agreement',
495
      'conflicting',
496
      'unilateral',
497
      'insufficient',
498
    ]),
499
    summary: z.string().min(1).max(2000),
500
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
501
    reportIds: z.array(z.string().uuid()).max(2),
502
    feedbackIds: z.array(z.string().uuid()).max(2),
503
    averageRatings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)).default({}),
504
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
505
    generatedAt: z.string().datetime(),
506
    platformAttestation: RecordAttestationSchema.optional(),
507
  })
508
  .strict();
509
510
export const LearningEligibilityDecisionSchema = z
511
  .object({
512
    decisionId: z.string().uuid(),
513
    interactionId: z.string().uuid(),
514
    eligible: z.boolean(),
515
    reasons: z.array(z.string().min(1).max(500)).min(1).max(50),
516
    sampleWeight: z.number().min(0).max(1),
517
    reportIds: z.array(z.string().uuid()).max(2),
518
    feedbackIds: z.array(z.string().uuid()).max(2),
519
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
520
    contributionMode: z.enum(['local_only', 'network_aggregate']),
521
    structuredDataOnly: z.literal(true),
522
    decidedAt: z.string().datetime(),
523
  })
524
  .strict();
525
526
export const BehaviouralProfileDeltaSchema = z
527
  .object({
528
    deltaId: z.string().uuid(),
529
    interactionId: z.string().uuid(),
530
    agentId: z.string().min(1),
531
    agentVersion: z.string().min(1),
532
    taskCategory: z.string().min(1),
533
    sampleWeight: z.number().min(0).max(1),
534
    dimensionDeltas: z.record(z.string().min(1).max(128), z.number().min(-1).max(1)),
535
    explanation: z.string().min(1).max(2000),
536
    appliedAt: z.string().datetime(),
537
  })
538
  .strict();
539
540
export const TrustEnvelopeSchema = z.object({
541
  protocolVersion: z.literal(PROTOCOL_VERSION),
542
  interactionId: z.string().uuid(),
543
  requestingAgentId: z.string(),
544
  respondingAgentId: z.string(),
545
  rootControllerId: z.string(),
546
  parentAgentId: z.string().optional(),
547
  agentVersion: z.string(),
548
  delegationId: z.string().uuid().optional(),
549
  requestedCapability: z.string(),
550
  taskCategory: z.string(),
551
  contractHash: z.string(),
552
  dataSharingMode: DataSharingModeSchema,
553
  evidenceRequirements: z.array(z.string()),
554
  timestamp: z.string().datetime(),
555
  expiresAt: z.string().datetime(),
556
  nonce: z.string().min(12),
557
  signature: SignatureSchema.optional(),
558
});
559
560
export const EventTypeSchema = z.enum([
561
  'claim',
562
  'evidence',
563
  'constraint',
564
  'commitment',
565
  'proposal',
566
  'objection',
567
  'policy_warning',
568
  'policy_violation',
569
  'private_suggestion',
570
  'shared_intervention',
571
  'delegation',
572
  'task_result',
573
  'resolution',
574
  'receipt',
575
  'feedback',
576
  'dispute',
577
]);
578
export const InteractionEventSchema = z.object({
579
  protocolVersion: z.literal(PROTOCOL_VERSION),
580
  eventId: z.string().uuid(),
581
  interactionId: z.string().uuid(),
582
  eventType: EventTypeSchema,
583
  agentId: z.string(),
584
  agentVersion: z.string(),
585
  timestamp: z.string().datetime(),
586
  visibility: VisibilitySchema,
587
  provenance: ProvenanceSchema,
588
  payloadHash: z.string(),
589
  payload: z.record(z.string(), z.unknown()),
590
  evidenceRefs: z.array(z.string()).optional(),
591
  signature: SignatureSchema.optional(),
592
});
593
594
export const FeedbackSchema = z.object({
595
  feedbackId: z.string().uuid(),
596
  interactionId: z.string().uuid(),
597
  receiptId: z.string().uuid(),
598
  reviewerAgentId: z.string(),
599
  subjectAgentId: z.string(),
600
  taskCompleted: z.boolean(),
601
  outputAccepted: z.boolean(),
602
  specificationMatched: z.boolean(),
603
  deadlineMet: z.boolean(),
604
  communicationQuality: z.number().min(0).max(1),
605
  evidenceProvided: z.boolean(),
606
  scopeRespected: z.boolean(),
607
  disputeRaised: z.boolean(),
608
  comment: z.string().max(1000).optional(),
609
  submittedAt: z.string().datetime(),
610
  signature: SignatureSchema.optional(),
611
});
612
613
export const ReceiptSchema = z.object({
614
  receiptId: z.string().uuid(),
615
  interactionId: z.string().uuid(),
616
  participants: z.array(z.string()),
617
  agentVersions: z.record(z.string(), z.string()),
618
  contractHash: z.string(),
619
  startedAt: z.string().datetime(),
620
  completedAt: z.string().datetime(),
621
  outcome: z.enum(['success', 'partial', 'failure', 'cancelled']),
622
  commitmentsFulfilled: z.array(z.string()),
623
  commitmentsMissed: z.array(z.string()),
624
  evidenceHashes: z.array(z.string()),
625
  policyWarnings: z.array(z.string()),
626
  policyViolations: z.array(z.string()),
627
  disputeStatus: z.enum(['none', 'open', 'resolved']),
628
  delegationChainHash: z.string(),
629
  unilateral: z.boolean(),
630
  signatures: z.record(z.string(), SignatureSchema).default({}),
631
  completionReportIds: z.array(z.string().uuid()).max(2).optional(),
632
  conclusionId: z.string().uuid().optional(),
633
  platformAttestation: RecordAttestationSchema.optional(),
634
});
635
636
export const RiskDecisionSchema = z.object({
637
  decision: z.enum(['ALLOW', 'CHALLENGE', 'DENY']),
638
  confidence: z.number().min(0).max(1),
639
  taskCategory: z.string(),
640
  sampleSize: z.number().int().nonnegative(),
641
  dimensions: z.record(z.string(), z.number()),
642
  reasons: z.array(z.string()),
643
  warnings: z.array(z.string()),
644
  dataFreshness: z.record(z.string(), z.string()),
645
  requiredChallenges: z.array(z.string()),
646
});
647
648
export const FactCheckResultSchema = z.object({
649
  claim: z.string(),
650
  claimType: z.enum(['objective', 'subjective', 'prediction']),
651
  status: z.enum([
652
    'verified',
653
    'supported',
654
    'contradicted',
655
    'unverified',
656
    'not_fact_checkable',
657
    'insufficient_permission',
658
  ]),
659
  confidence: z.number().min(0).max(1),
660
  evidenceReferences: z.array(z.string()),
661
  sourceAuthority: z.string(),
662
  sourceFreshness: z.string(),
663
  contradictingEvidence: z.array(z.string()),
664
  suggestedNextAction: z.string(),
665
});
666
667
export type AgentIdentity = z.infer<typeof AgentIdentitySchema>;
668
export type DelegationCredential = z.infer<typeof DelegationCredentialSchema>;
669
export type InteractionContract = z.infer<typeof InteractionContractSchema>;
670
export type TrustEnvelope = z.infer<typeof TrustEnvelopeSchema>;
671
export type InteractionEvent = z.infer<typeof InteractionEventSchema>;
672
export type Feedback = z.infer<typeof FeedbackSchema>;
673
export type Receipt = z.infer<typeof ReceiptSchema>;
674
export type RiskDecision = z.infer<typeof RiskDecisionSchema>;
675
export type FactCheckResult = z.infer<typeof FactCheckResultSchema>;
676
export type ExpectationManifest = z.infer<typeof ExpectationManifestSchema>;
677
export type AgentTransport = z.infer<typeof AgentTransportSchema>;
678
export type AgentMode = z.infer<typeof AgentModeSchema>;
679
export type AgentPresence = z.infer<typeof AgentPresenceSchema>;
680
export type PublicAgentCard = z.infer<typeof PublicAgentCardSchema>;
681
export type HostedMessage = z.infer<typeof HostedMessageSchema>;
682
export type HostedThread = z.infer<typeof HostedThreadSchema>;
683
export type ContractAcceptance = z.infer<typeof ContractAcceptanceSchema>;
684
export type FederatedInteraction = z.infer<typeof FederatedInteractionSchema>;
685
export type LiveSessionInsight = z.infer<typeof LiveSessionInsightSchema>;
686
export type LiveSessionOffer = z.infer<typeof LiveSessionOfferSchema>;
687
export type LiveSessionAcceptance = z.infer<typeof LiveSessionAcceptanceSchema>;
688
export type LiveSessionActivation = z.infer<typeof LiveSessionActivationSchema>;
689
export type LiveSessionEvent = z.infer<typeof LiveSessionEventSchema>;
690
export type RequirementAssessment = z.infer<typeof RequirementAssessmentSchema>;
691
export type CounterpartyBrief = z.infer<typeof CounterpartyBriefSchema>;
692
export type CompletionOutcome = z.infer<typeof CompletionOutcomeSchema>;
693
export type SuccessCriterionAssessment = z.infer<typeof SuccessCriterionAssessmentSchema>;
694
export type InteractionCompletionReport = z.infer<typeof InteractionCompletionReportSchema>;
695
export type FeedbackDimension = z.infer<typeof FeedbackDimensionSchema>;
696
export type FeedbackRequest = z.infer<typeof FeedbackRequestSchema>;
697
export type InteractionFeedback = z.infer<typeof InteractionFeedbackSchema>;
698
export type InteractionConclusion = z.infer<typeof InteractionConclusionSchema>;
699
export type LearningEligibilityDecision = z.infer<typeof LearningEligibilityDecisionSchema>;
700
export type BehaviouralProfileDelta = z.infer<typeof BehaviouralProfileDeltaSchema>;
701
export type RecordAttestation = z.infer<typeof RecordAttestationSchema>;
702
703
export interface KeyPair {
704
  keyId: string;
705
  publicKey: string;
706
  privateKey: string;
707
}
708
709
export function createKeyPair(keyId: string = randomUUID()): KeyPair {
710
  const pair = generateKeyPairSync('ed25519');
711
  return {
712
    keyId,
713
    publicKey: pair.publicKey.export({ type: 'spki', format: 'der' }).toString('base64url'),
714
    privateKey: pair.privateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64url'),
715
  };
716
}
717
718
export function canonicalHash(value: unknown): string {
719
  return createHash('sha256').update(canonicalize(value)).digest('base64url');
720
}
721
722
function unsigned(value: Record<string, unknown>): Record<string, unknown> {
723
  const copy = structuredClone(value);
724
  delete copy.signature;
725
  delete copy.signatures;
726
  return copy;
727
}
728
729
export function signObject<T extends Record<string, unknown>>(
730
  value: T,
731
  key: KeyPair,
732
): T & { signature: z.infer<typeof SignatureSchema> } {
733
  const payload = Buffer.from(canonicalize(unsigned(value)));
734
  const privateKey = {
735
    key: Buffer.from(key.privateKey, 'base64url'),
736
    type: 'pkcs8' as const,
737
    format: 'der' as const,
738
  };
739
  return {
740
    ...value,
741
    signature: {
742
      algorithm: 'Ed25519',
743
      keyId: key.keyId,
744
      value: sign(null, payload, privateKey).toString('base64url'),
745
    },
746
  };
747
}
748
749
export function verifyObject(value: Record<string, unknown>, publicKey: string): boolean {
750
  const parsed = SignatureSchema.safeParse(value.signature);
751
  if (!parsed.success) return false;
752
  const key = {
753
    key: Buffer.from(publicKey, 'base64url'),
754
    type: 'spki' as const,
755
    format: 'der' as const,
756
  };
757
  return verify(
758
    null,
759
    Buffer.from(canonicalize(unsigned(value))),
760
    key,
761
    Buffer.from(parsed.data.value, 'base64url'),
762
  );
763
}
764
765
export function signNamed<T extends Record<string, unknown>>(
766
  value: T,
767
  signerId: string,
768
  key: KeyPair,
769
): T & { signatures: Record<string, z.infer<typeof SignatureSchema>> } {
770
  const payload = Buffer.from(canonicalize(unsigned(value)));
771
  const privateKey = {
772
    key: Buffer.from(key.privateKey, 'base64url'),
773
    type: 'pkcs8' as const,
774
    format: 'der' as const,
775
  };
776
  const signature = {
777
    algorithm: 'Ed25519' as const,
778
    keyId: key.keyId,
779
    value: sign(null, payload, privateKey).toString('base64url'),
780
  };
781
  return {
782
    ...value,
783
    signatures: { ...((value.signatures as object) ?? {}), [signerId]: signature },
784
  };
785
}
786
787
export function verifyNamed(
788
  value: Record<string, unknown>,
789
  signerId: string,
790
  publicKey: string,
791
): boolean {
792
  const signatures = value.signatures as Record<string, unknown> | undefined;
793
  const parsed = SignatureSchema.safeParse(signatures?.[signerId]);
794
  if (!parsed.success) return false;
795
  const key = {
796
    key: Buffer.from(publicKey, 'base64url'),
797
    type: 'spki' as const,
798
    format: 'der' as const,
799
  };
800
  return verify(
801
    null,
802
    Buffer.from(canonicalize(unsigned(value))),
803
    key,
804
    Buffer.from(parsed.data.value, 'base64url'),
805
  );
806
}