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

Viewing files at commit 81afd7789b27Commit detailsView latest

packages/protocol/src/index.ts 846 lines · 29.8 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 ProgressCheckpointSchema = z
348
  .object({
349
    state: z.enum(['active', 'blocked', 'ready_to_finalize', 'done', 'cancelled']),
350
    progress: z.number().min(0).max(1),
351
    criteriaMet: z.array(z.string().min(1).max(1000)).max(100).default([]),
352
    criteriaRemaining: z.array(z.string().min(1).max(1000)).max(100).default([]),
353
    blockerCodes: z.array(SessionDetailKeySchema).max(32).default([]),
354
    topicStatus: z.enum(['in_scope', 'drifting', 'changed']),
355
    expectedRemainingTurns: z.number().int().nonnegative().max(1000).optional(),
356
    needsHuman: z.boolean().default(false),
357
    confidence: z.number().min(0).max(1),
358
  })
359
  .strict();
360
361
export const LiveSessionEventSchema = z
362
  .object({
363
    eventId: z.string().uuid(),
364
    interactionId: z.string().uuid(),
365
    agentId: z.string().min(1),
366
    sequence: z.number().int().nonnegative(),
367
    type: z.enum([
368
      'session_started',
369
      'message_sent',
370
      'progress_checkpoint',
371
      'claim',
372
      'evidence',
373
      'correction',
374
      'constraint',
375
      'task_result',
376
      'session_completed',
377
      'session_failed',
378
    ]),
379
    occurredAt: z.string().datetime(),
380
    messageHash: z
381
      .string()
382
      .regex(/^[a-zA-Z0-9_-]{43}$/)
383
      .optional(),
384
    evidenceReferences: z.array(z.string()).default([]),
385
    outcome: z.enum(['success', 'failure', 'partial']).optional(),
386
    checkpoint: ProgressCheckpointSchema.optional(),
387
    details: StructuredSessionDetailsSchema,
388
  })
389
  .superRefine((event, context) => {
390
    if (event.type === 'progress_checkpoint' && !event.checkpoint)
391
      context.addIssue({
392
        code: 'custom',
393
        path: ['checkpoint'],
394
        message: 'Progress checkpoint events require structured checkpoint data',
395
      });
396
    if (event.type !== 'progress_checkpoint' && event.checkpoint)
397
      context.addIssue({
398
        code: 'custom',
399
        path: ['checkpoint'],
400
        message: 'Checkpoint data is only valid on progress checkpoint events',
401
      });
402
  });
403
404
export const CompletionOutcomeSchema = z.enum(['success', 'partial', 'failure', 'cancelled']);
405
406
export const SuccessCriterionAssessmentSchema = z
407
  .object({
408
    criterion: z.string().min(1).max(1000),
409
    status: z.enum(['met', 'partially_met', 'missed', 'unknown']),
410
    explanation: z.string().max(1000).optional(),
411
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
412
  })
413
  .strict();
414
415
export const InteractionCompletionReportSchema = z
416
  .object({
417
    reportId: z.string().uuid(),
418
    interactionId: z.string().uuid(),
419
    contractHash: z.string().min(1),
420
    reportingAgentId: z.string().min(1),
421
    counterpartyAgentId: z.string().min(1),
422
    agentVersion: z.string().min(1),
423
    outcome: CompletionOutcomeSchema,
424
    summary: z.string().min(1).max(2000),
425
    requestedOutcome: z.string().min(1).max(1000),
426
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
427
    deliverables: z.array(z.string().min(1).max(1000)).max(100).default([]),
428
    actionsTaken: z.array(z.string().min(1).max(1000)).max(100).default([]),
429
    blockers: z.array(z.string().min(1).max(1000)).max(100).default([]),
430
    scopeChanges: z.array(z.string().min(1).max(1000)).max(100).default([]),
431
    corrections: z.array(z.string().min(1).max(1000)).max(100).default([]),
432
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
433
    startedAt: z.string().datetime().optional(),
434
    completedAt: z.string().datetime(),
435
    confidence: z.number().min(0).max(1),
436
    dataSharingMode: DataSharingModeSchema.default('structured_only'),
437
    signature: SignatureSchema.optional(),
438
    submissionMethod: z
439
      .enum(['agent_signature', 'oauth_installation', 'agent_access_token', 'runtime_session'])
440
      .optional(),
441
    platformAttestation: RecordAttestationSchema.optional(),
442
  })
443
  .strict()
444
  .refine((report) => report.reportingAgentId !== report.counterpartyAgentId, {
445
    message: 'A completion report must describe an interaction with another agent',
446
    path: ['counterpartyAgentId'],
447
  })
448
  .refine(
449
    (report) => !report.startedAt || Date.parse(report.completedAt) >= Date.parse(report.startedAt),
450
    { message: 'Completion cannot precede the start time', path: ['completedAt'] },
451
  );
452
453
export const FeedbackDimensionSchema = z.enum([
454
  'overall_satisfaction',
455
  'outcome_satisfaction',
456
  'communication',
457
  'timeliness',
458
  'scope_adherence',
459
  'evidence_quality',
460
  'correction_handling',
461
  'reliability',
462
]);
463
464
export const FeedbackRequestSchema = z
465
  .object({
466
    requestId: z.string().uuid(),
467
    interactionId: z.string().uuid(),
468
    reviewerAgentId: z.string().min(1),
469
    subjectAgentId: z.string().min(1),
470
    status: z.enum(['pending', 'submitted', 'expired', 'waived']),
471
    requestedDimensions: z.array(FeedbackDimensionSchema).min(1),
472
    requestedAt: z.string().datetime(),
473
    dueAt: z.string().datetime(),
474
    platformAttestation: RecordAttestationSchema.optional(),
475
  })
476
  .strict()
477
  .refine((request) => request.reviewerAgentId !== request.subjectAgentId, {
478
    message: 'Feedback must concern another agent',
479
    path: ['subjectAgentId'],
480
  })
481
  .refine((request) => Date.parse(request.dueAt) > Date.parse(request.requestedAt), {
482
    message: 'Feedback due date must follow its request date',
483
    path: ['dueAt'],
484
  });
485
486
export const InteractionFeedbackSchema = z
487
  .object({
488
    feedbackId: z.string().uuid(),
489
    requestId: z.string().uuid(),
490
    interactionId: z.string().uuid(),
491
    reviewerAgentId: z.string().min(1),
492
    subjectAgentId: z.string().min(1),
493
    reviewerAgentVersion: z.string().min(1),
494
    ratings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)),
495
    wouldWorkAgain: z.enum(['yes', 'no', 'unsure']),
496
    reasonCodes: z.array(z.string().min(1).max(128)).max(32).default([]),
497
    privateComment: z.string().max(1000).optional(),
498
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(50).default([]),
499
    confidence: z.number().min(0).max(1),
500
    submittedAt: z.string().datetime(),
501
    signature: SignatureSchema.optional(),
502
    submissionMethod: z
503
      .enum(['agent_signature', 'oauth_installation', 'agent_access_token', 'runtime_session'])
504
      .optional(),
505
    platformAttestation: RecordAttestationSchema.optional(),
506
  })
507
  .strict()
508
  .refine((feedback) => feedback.reviewerAgentId !== feedback.subjectAgentId, {
509
    message: 'Feedback must concern another agent',
510
    path: ['subjectAgentId'],
511
  })
512
  .refine((feedback) => Object.keys(feedback.ratings).length > 0, {
513
    message: 'At least one feedback rating is required',
514
    path: ['ratings'],
515
  });
516
517
export const InteractionConclusionSchema = z
518
  .object({
519
    conclusionId: z.string().uuid(),
520
    interactionId: z.string().uuid(),
521
    contractHash: z.string().min(1),
522
    outcome: CompletionOutcomeSchema,
523
    consensus: z.enum([
524
      'bilateral_agreement',
525
      'bilateral_partial_agreement',
526
      'conflicting',
527
      'unilateral',
528
      'insufficient',
529
    ]),
530
    lifecycle: z.enum(['provisional', 'final']).optional(),
531
    confidence: z.number().min(0).max(1).optional(),
532
    missingReportAgentIds: z.array(z.string().min(1)).max(2).optional(),
533
    pendingFeedbackAgentIds: z.array(z.string().min(1)).max(2).optional(),
534
    peerReportStatus: z.enum(['awaiting', 'unreachable', 'received', 'timed_out']).optional(),
535
    summary: z.string().min(1).max(2000),
536
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
537
    reportIds: z.array(z.string().uuid()).max(2),
538
    feedbackIds: z.array(z.string().uuid()).max(2),
539
    averageRatings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)).default({}),
540
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
541
    generatedAt: z.string().datetime(),
542
    platformAttestation: RecordAttestationSchema.optional(),
543
  })
544
  .strict();
545
546
export const LearningEligibilityDecisionSchema = z
547
  .object({
548
    decisionId: z.string().uuid(),
549
    interactionId: z.string().uuid(),
550
    eligible: z.boolean(),
551
    reasons: z.array(z.string().min(1).max(500)).min(1).max(50),
552
    sampleWeight: z.number().min(0).max(1),
553
    reportIds: z.array(z.string().uuid()).max(2),
554
    feedbackIds: z.array(z.string().uuid()).max(2),
555
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
556
    contributionMode: z.enum(['local_only', 'network_aggregate']),
557
    structuredDataOnly: z.literal(true),
558
    decidedAt: z.string().datetime(),
559
    platformAttestation: RecordAttestationSchema.optional(),
560
  })
561
  .strict();
562
563
export const BehaviouralProfileDeltaSchema = z
564
  .object({
565
    deltaId: z.string().uuid(),
566
    interactionId: z.string().uuid(),
567
    agentId: z.string().min(1),
568
    agentVersion: z.string().min(1),
569
    taskCategory: z.string().min(1),
570
    sampleWeight: z.number().min(0).max(1),
571
    dimensionDeltas: z.record(z.string().min(1).max(128), z.number().min(-1).max(1)),
572
    explanation: z.string().min(1).max(2000),
573
    appliedAt: z.string().datetime(),
574
    platformAttestation: RecordAttestationSchema.optional(),
575
  })
576
  .strict();
577
578
export const TrustEnvelopeSchema = z.object({
579
  protocolVersion: z.literal(PROTOCOL_VERSION),
580
  interactionId: z.string().uuid(),
581
  requestingAgentId: z.string(),
582
  respondingAgentId: z.string(),
583
  rootControllerId: z.string(),
584
  parentAgentId: z.string().optional(),
585
  agentVersion: z.string(),
586
  delegationId: z.string().uuid().optional(),
587
  requestedCapability: z.string(),
588
  taskCategory: z.string(),
589
  contractHash: z.string(),
590
  dataSharingMode: DataSharingModeSchema,
591
  evidenceRequirements: z.array(z.string()),
592
  timestamp: z.string().datetime(),
593
  expiresAt: z.string().datetime(),
594
  nonce: z.string().min(12),
595
  signature: SignatureSchema.optional(),
596
});
597
598
export const EventTypeSchema = z.enum([
599
  'claim',
600
  'evidence',
601
  'constraint',
602
  'commitment',
603
  'proposal',
604
  'objection',
605
  'policy_warning',
606
  'policy_violation',
607
  'private_suggestion',
608
  'shared_intervention',
609
  'delegation',
610
  'task_result',
611
  'resolution',
612
  'receipt',
613
  'feedback',
614
  'dispute',
615
]);
616
export const InteractionEventSchema = z.object({
617
  protocolVersion: z.literal(PROTOCOL_VERSION),
618
  eventId: z.string().uuid(),
619
  interactionId: z.string().uuid(),
620
  eventType: EventTypeSchema,
621
  agentId: z.string(),
622
  agentVersion: z.string(),
623
  timestamp: z.string().datetime(),
624
  visibility: VisibilitySchema,
625
  provenance: ProvenanceSchema,
626
  payloadHash: z.string(),
627
  payload: z.record(z.string(), z.unknown()),
628
  evidenceRefs: z.array(z.string()).optional(),
629
  signature: SignatureSchema.optional(),
630
});
631
632
export const FeedbackSchema = z.object({
633
  feedbackId: z.string().uuid(),
634
  interactionId: z.string().uuid(),
635
  receiptId: z.string().uuid(),
636
  reviewerAgentId: z.string(),
637
  subjectAgentId: z.string(),
638
  taskCompleted: z.boolean(),
639
  outputAccepted: z.boolean(),
640
  specificationMatched: z.boolean(),
641
  deadlineMet: z.boolean(),
642
  communicationQuality: z.number().min(0).max(1),
643
  evidenceProvided: z.boolean(),
644
  scopeRespected: z.boolean(),
645
  disputeRaised: z.boolean(),
646
  comment: z.string().max(1000).optional(),
647
  submittedAt: z.string().datetime(),
648
  signature: SignatureSchema.optional(),
649
});
650
651
export const ReceiptSchema = z.object({
652
  receiptId: z.string().uuid(),
653
  interactionId: z.string().uuid(),
654
  participants: z.array(z.string()),
655
  agentVersions: z.record(z.string(), z.string()),
656
  contractHash: z.string(),
657
  startedAt: z.string().datetime(),
658
  completedAt: z.string().datetime(),
659
  outcome: z.enum(['success', 'partial', 'failure', 'cancelled']),
660
  commitmentsFulfilled: z.array(z.string()),
661
  commitmentsMissed: z.array(z.string()),
662
  evidenceHashes: z.array(z.string()),
663
  policyWarnings: z.array(z.string()),
664
  policyViolations: z.array(z.string()),
665
  disputeStatus: z.enum(['none', 'open', 'resolved']),
666
  delegationChainHash: z.string(),
667
  unilateral: z.boolean(),
668
  provisional: z.boolean().optional(),
669
  confidence: z.number().min(0).max(1).optional(),
670
  signatures: z.record(z.string(), SignatureSchema).default({}),
671
  completionReportIds: z.array(z.string().uuid()).max(2).optional(),
672
  conclusionId: z.string().uuid().optional(),
673
  platformAttestation: RecordAttestationSchema.optional(),
674
});
675
676
export const RiskDecisionSchema = z.object({
677
  decision: z.enum(['ALLOW', 'CHALLENGE', 'DENY']),
678
  confidence: z.number().min(0).max(1),
679
  taskCategory: z.string(),
680
  sampleSize: z.number().int().nonnegative(),
681
  dimensions: z.record(z.string(), z.number()),
682
  reasons: z.array(z.string()),
683
  warnings: z.array(z.string()),
684
  dataFreshness: z.record(z.string(), z.string()),
685
  requiredChallenges: z.array(z.string()),
686
});
687
688
export const FactCheckResultSchema = z.object({
689
  claim: z.string(),
690
  claimType: z.enum(['objective', 'subjective', 'prediction']),
691
  status: z.enum([
692
    'verified',
693
    'supported',
694
    'contradicted',
695
    'unverified',
696
    'not_fact_checkable',
697
    'insufficient_permission',
698
  ]),
699
  confidence: z.number().min(0).max(1),
700
  evidenceReferences: z.array(z.string()),
701
  sourceAuthority: z.string(),
702
  sourceFreshness: z.string(),
703
  contradictingEvidence: z.array(z.string()),
704
  suggestedNextAction: z.string(),
705
});
706
707
export type AgentIdentity = z.infer<typeof AgentIdentitySchema>;
708
export type DelegationCredential = z.infer<typeof DelegationCredentialSchema>;
709
export type InteractionContract = z.infer<typeof InteractionContractSchema>;
710
export type TrustEnvelope = z.infer<typeof TrustEnvelopeSchema>;
711
export type InteractionEvent = z.infer<typeof InteractionEventSchema>;
712
export type Feedback = z.infer<typeof FeedbackSchema>;
713
export type Receipt = z.infer<typeof ReceiptSchema>;
714
export type RiskDecision = z.infer<typeof RiskDecisionSchema>;
715
export type FactCheckResult = z.infer<typeof FactCheckResultSchema>;
716
export type ExpectationManifest = z.infer<typeof ExpectationManifestSchema>;
717
export type AgentTransport = z.infer<typeof AgentTransportSchema>;
718
export type AgentMode = z.infer<typeof AgentModeSchema>;
719
export type AgentPresence = z.infer<typeof AgentPresenceSchema>;
720
export type PublicAgentCard = z.infer<typeof PublicAgentCardSchema>;
721
export type HostedMessage = z.infer<typeof HostedMessageSchema>;
722
export type HostedThread = z.infer<typeof HostedThreadSchema>;
723
export type ContractAcceptance = z.infer<typeof ContractAcceptanceSchema>;
724
export type FederatedInteraction = z.infer<typeof FederatedInteractionSchema>;
725
export type LiveSessionInsight = z.infer<typeof LiveSessionInsightSchema>;
726
export type LiveSessionOffer = z.infer<typeof LiveSessionOfferSchema>;
727
export type LiveSessionAcceptance = z.infer<typeof LiveSessionAcceptanceSchema>;
728
export type LiveSessionActivation = z.infer<typeof LiveSessionActivationSchema>;
729
export type LiveSessionEvent = z.infer<typeof LiveSessionEventSchema>;
730
export type RequirementAssessment = z.infer<typeof RequirementAssessmentSchema>;
731
export type CounterpartyBrief = z.infer<typeof CounterpartyBriefSchema>;
732
export type CompletionOutcome = z.infer<typeof CompletionOutcomeSchema>;
733
export type SuccessCriterionAssessment = z.infer<typeof SuccessCriterionAssessmentSchema>;
734
export type InteractionCompletionReport = z.infer<typeof InteractionCompletionReportSchema>;
735
export type FeedbackDimension = z.infer<typeof FeedbackDimensionSchema>;
736
export type FeedbackRequest = z.infer<typeof FeedbackRequestSchema>;
737
export type InteractionFeedback = z.infer<typeof InteractionFeedbackSchema>;
738
export type InteractionConclusion = z.infer<typeof InteractionConclusionSchema>;
739
export type LearningEligibilityDecision = z.infer<typeof LearningEligibilityDecisionSchema>;
740
export type BehaviouralProfileDelta = z.infer<typeof BehaviouralProfileDeltaSchema>;
741
export type RecordAttestation = z.infer<typeof RecordAttestationSchema>;
742
743
export interface KeyPair {
744
  keyId: string;
745
  publicKey: string;
746
  privateKey: string;
747
}
748
749
export function createKeyPair(keyId: string = randomUUID()): KeyPair {
750
  const pair = generateKeyPairSync('ed25519');
751
  return {
752
    keyId,
753
    publicKey: pair.publicKey.export({ type: 'spki', format: 'der' }).toString('base64url'),
754
    privateKey: pair.privateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64url'),
755
  };
756
}
757
758
export function canonicalHash(value: unknown): string {
759
  return createHash('sha256').update(canonicalize(value)).digest('base64url');
760
}
761
762
function unsigned(value: Record<string, unknown>): Record<string, unknown> {
763
  const copy = structuredClone(value);
764
  delete copy.signature;
765
  delete copy.signatures;
766
  return copy;
767
}
768
769
export function signObject<T extends Record<string, unknown>>(
770
  value: T,
771
  key: KeyPair,
772
): T & { signature: z.infer<typeof SignatureSchema> } {
773
  const payload = Buffer.from(canonicalize(unsigned(value)));
774
  const privateKey = {
775
    key: Buffer.from(key.privateKey, 'base64url'),
776
    type: 'pkcs8' as const,
777
    format: 'der' as const,
778
  };
779
  return {
780
    ...value,
781
    signature: {
782
      algorithm: 'Ed25519',
783
      keyId: key.keyId,
784
      value: sign(null, payload, privateKey).toString('base64url'),
785
    },
786
  };
787
}
788
789
export function verifyObject(value: Record<string, unknown>, publicKey: string): boolean {
790
  const parsed = SignatureSchema.safeParse(value.signature);
791
  if (!parsed.success) return false;
792
  const key = {
793
    key: Buffer.from(publicKey, 'base64url'),
794
    type: 'spki' as const,
795
    format: 'der' as const,
796
  };
797
  return verify(
798
    null,
799
    Buffer.from(canonicalize(unsigned(value))),
800
    key,
801
    Buffer.from(parsed.data.value, 'base64url'),
802
  );
803
}
804
805
export function signNamed<T extends Record<string, unknown>>(
806
  value: T,
807
  signerId: string,
808
  key: KeyPair,
809
): T & { signatures: Record<string, z.infer<typeof SignatureSchema>> } {
810
  const payload = Buffer.from(canonicalize(unsigned(value)));
811
  const privateKey = {
812
    key: Buffer.from(key.privateKey, 'base64url'),
813
    type: 'pkcs8' as const,
814
    format: 'der' as const,
815
  };
816
  const signature = {
817
    algorithm: 'Ed25519' as const,
818
    keyId: key.keyId,
819
    value: sign(null, payload, privateKey).toString('base64url'),
820
  };
821
  return {
822
    ...value,
823
    signatures: { ...((value.signatures as object) ?? {}), [signerId]: signature },
824
  };
825
}
826
827
export function verifyNamed(
828
  value: Record<string, unknown>,
829
  signerId: string,
830
  publicKey: string,
831
): boolean {
832
  const signatures = value.signatures as Record<string, unknown> | undefined;
833
  const parsed = SignatureSchema.safeParse(signatures?.[signerId]);
834
  if (!parsed.success) return false;
835
  const key = {
836
    key: Buffer.from(publicKey, 'base64url'),
837
    type: 'spki' as const,
838
    format: 'der' as const,
839
  };
840
  return verify(
841
    null,
842
    Buffer.from(canonicalize(unsigned(value))),
843
    key,
844
    Buffer.from(parsed.data.value, 'base64url'),
845
  );
846
}