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

Viewing files at commit 2a4cc60d2506Commit detailsView latest

packages/protocol/src/index.ts 839 lines · 29.3 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
    summary: z.string().min(1).max(2000),
531
    criteria: z.array(SuccessCriterionAssessmentSchema).max(100),
532
    reportIds: z.array(z.string().uuid()).max(2),
533
    feedbackIds: z.array(z.string().uuid()).max(2),
534
    averageRatings: z.partialRecord(FeedbackDimensionSchema, z.number().min(0).max(1)).default({}),
535
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
536
    generatedAt: z.string().datetime(),
537
    platformAttestation: RecordAttestationSchema.optional(),
538
  })
539
  .strict();
540
541
export const LearningEligibilityDecisionSchema = z
542
  .object({
543
    decisionId: z.string().uuid(),
544
    interactionId: z.string().uuid(),
545
    eligible: z.boolean(),
546
    reasons: z.array(z.string().min(1).max(500)).min(1).max(50),
547
    sampleWeight: z.number().min(0).max(1),
548
    reportIds: z.array(z.string().uuid()).max(2),
549
    feedbackIds: z.array(z.string().uuid()).max(2),
550
    evidenceReferences: z.array(z.string().min(1).max(2048)).max(100).default([]),
551
    contributionMode: z.enum(['local_only', 'network_aggregate']),
552
    structuredDataOnly: z.literal(true),
553
    decidedAt: z.string().datetime(),
554
    platformAttestation: RecordAttestationSchema.optional(),
555
  })
556
  .strict();
557
558
export const BehaviouralProfileDeltaSchema = z
559
  .object({
560
    deltaId: z.string().uuid(),
561
    interactionId: z.string().uuid(),
562
    agentId: z.string().min(1),
563
    agentVersion: z.string().min(1),
564
    taskCategory: z.string().min(1),
565
    sampleWeight: z.number().min(0).max(1),
566
    dimensionDeltas: z.record(z.string().min(1).max(128), z.number().min(-1).max(1)),
567
    explanation: z.string().min(1).max(2000),
568
    appliedAt: z.string().datetime(),
569
    platformAttestation: RecordAttestationSchema.optional(),
570
  })
571
  .strict();
572
573
export const TrustEnvelopeSchema = z.object({
574
  protocolVersion: z.literal(PROTOCOL_VERSION),
575
  interactionId: z.string().uuid(),
576
  requestingAgentId: z.string(),
577
  respondingAgentId: z.string(),
578
  rootControllerId: z.string(),
579
  parentAgentId: z.string().optional(),
580
  agentVersion: z.string(),
581
  delegationId: z.string().uuid().optional(),
582
  requestedCapability: z.string(),
583
  taskCategory: z.string(),
584
  contractHash: z.string(),
585
  dataSharingMode: DataSharingModeSchema,
586
  evidenceRequirements: z.array(z.string()),
587
  timestamp: z.string().datetime(),
588
  expiresAt: z.string().datetime(),
589
  nonce: z.string().min(12),
590
  signature: SignatureSchema.optional(),
591
});
592
593
export const EventTypeSchema = z.enum([
594
  'claim',
595
  'evidence',
596
  'constraint',
597
  'commitment',
598
  'proposal',
599
  'objection',
600
  'policy_warning',
601
  'policy_violation',
602
  'private_suggestion',
603
  'shared_intervention',
604
  'delegation',
605
  'task_result',
606
  'resolution',
607
  'receipt',
608
  'feedback',
609
  'dispute',
610
]);
611
export const InteractionEventSchema = z.object({
612
  protocolVersion: z.literal(PROTOCOL_VERSION),
613
  eventId: z.string().uuid(),
614
  interactionId: z.string().uuid(),
615
  eventType: EventTypeSchema,
616
  agentId: z.string(),
617
  agentVersion: z.string(),
618
  timestamp: z.string().datetime(),
619
  visibility: VisibilitySchema,
620
  provenance: ProvenanceSchema,
621
  payloadHash: z.string(),
622
  payload: z.record(z.string(), z.unknown()),
623
  evidenceRefs: z.array(z.string()).optional(),
624
  signature: SignatureSchema.optional(),
625
});
626
627
export const FeedbackSchema = z.object({
628
  feedbackId: z.string().uuid(),
629
  interactionId: z.string().uuid(),
630
  receiptId: z.string().uuid(),
631
  reviewerAgentId: z.string(),
632
  subjectAgentId: z.string(),
633
  taskCompleted: z.boolean(),
634
  outputAccepted: z.boolean(),
635
  specificationMatched: z.boolean(),
636
  deadlineMet: z.boolean(),
637
  communicationQuality: z.number().min(0).max(1),
638
  evidenceProvided: z.boolean(),
639
  scopeRespected: z.boolean(),
640
  disputeRaised: z.boolean(),
641
  comment: z.string().max(1000).optional(),
642
  submittedAt: z.string().datetime(),
643
  signature: SignatureSchema.optional(),
644
});
645
646
export const ReceiptSchema = z.object({
647
  receiptId: z.string().uuid(),
648
  interactionId: z.string().uuid(),
649
  participants: z.array(z.string()),
650
  agentVersions: z.record(z.string(), z.string()),
651
  contractHash: z.string(),
652
  startedAt: z.string().datetime(),
653
  completedAt: z.string().datetime(),
654
  outcome: z.enum(['success', 'partial', 'failure', 'cancelled']),
655
  commitmentsFulfilled: z.array(z.string()),
656
  commitmentsMissed: z.array(z.string()),
657
  evidenceHashes: z.array(z.string()),
658
  policyWarnings: z.array(z.string()),
659
  policyViolations: z.array(z.string()),
660
  disputeStatus: z.enum(['none', 'open', 'resolved']),
661
  delegationChainHash: z.string(),
662
  unilateral: z.boolean(),
663
  signatures: z.record(z.string(), SignatureSchema).default({}),
664
  completionReportIds: z.array(z.string().uuid()).max(2).optional(),
665
  conclusionId: z.string().uuid().optional(),
666
  platformAttestation: RecordAttestationSchema.optional(),
667
});
668
669
export const RiskDecisionSchema = z.object({
670
  decision: z.enum(['ALLOW', 'CHALLENGE', 'DENY']),
671
  confidence: z.number().min(0).max(1),
672
  taskCategory: z.string(),
673
  sampleSize: z.number().int().nonnegative(),
674
  dimensions: z.record(z.string(), z.number()),
675
  reasons: z.array(z.string()),
676
  warnings: z.array(z.string()),
677
  dataFreshness: z.record(z.string(), z.string()),
678
  requiredChallenges: z.array(z.string()),
679
});
680
681
export const FactCheckResultSchema = z.object({
682
  claim: z.string(),
683
  claimType: z.enum(['objective', 'subjective', 'prediction']),
684
  status: z.enum([
685
    'verified',
686
    'supported',
687
    'contradicted',
688
    'unverified',
689
    'not_fact_checkable',
690
    'insufficient_permission',
691
  ]),
692
  confidence: z.number().min(0).max(1),
693
  evidenceReferences: z.array(z.string()),
694
  sourceAuthority: z.string(),
695
  sourceFreshness: z.string(),
696
  contradictingEvidence: z.array(z.string()),
697
  suggestedNextAction: z.string(),
698
});
699
700
export type AgentIdentity = z.infer<typeof AgentIdentitySchema>;
701
export type DelegationCredential = z.infer<typeof DelegationCredentialSchema>;
702
export type InteractionContract = z.infer<typeof InteractionContractSchema>;
703
export type TrustEnvelope = z.infer<typeof TrustEnvelopeSchema>;
704
export type InteractionEvent = z.infer<typeof InteractionEventSchema>;
705
export type Feedback = z.infer<typeof FeedbackSchema>;
706
export type Receipt = z.infer<typeof ReceiptSchema>;
707
export type RiskDecision = z.infer<typeof RiskDecisionSchema>;
708
export type FactCheckResult = z.infer<typeof FactCheckResultSchema>;
709
export type ExpectationManifest = z.infer<typeof ExpectationManifestSchema>;
710
export type AgentTransport = z.infer<typeof AgentTransportSchema>;
711
export type AgentMode = z.infer<typeof AgentModeSchema>;
712
export type AgentPresence = z.infer<typeof AgentPresenceSchema>;
713
export type PublicAgentCard = z.infer<typeof PublicAgentCardSchema>;
714
export type HostedMessage = z.infer<typeof HostedMessageSchema>;
715
export type HostedThread = z.infer<typeof HostedThreadSchema>;
716
export type ContractAcceptance = z.infer<typeof ContractAcceptanceSchema>;
717
export type FederatedInteraction = z.infer<typeof FederatedInteractionSchema>;
718
export type LiveSessionInsight = z.infer<typeof LiveSessionInsightSchema>;
719
export type LiveSessionOffer = z.infer<typeof LiveSessionOfferSchema>;
720
export type LiveSessionAcceptance = z.infer<typeof LiveSessionAcceptanceSchema>;
721
export type LiveSessionActivation = z.infer<typeof LiveSessionActivationSchema>;
722
export type LiveSessionEvent = z.infer<typeof LiveSessionEventSchema>;
723
export type RequirementAssessment = z.infer<typeof RequirementAssessmentSchema>;
724
export type CounterpartyBrief = z.infer<typeof CounterpartyBriefSchema>;
725
export type CompletionOutcome = z.infer<typeof CompletionOutcomeSchema>;
726
export type SuccessCriterionAssessment = z.infer<typeof SuccessCriterionAssessmentSchema>;
727
export type InteractionCompletionReport = z.infer<typeof InteractionCompletionReportSchema>;
728
export type FeedbackDimension = z.infer<typeof FeedbackDimensionSchema>;
729
export type FeedbackRequest = z.infer<typeof FeedbackRequestSchema>;
730
export type InteractionFeedback = z.infer<typeof InteractionFeedbackSchema>;
731
export type InteractionConclusion = z.infer<typeof InteractionConclusionSchema>;
732
export type LearningEligibilityDecision = z.infer<typeof LearningEligibilityDecisionSchema>;
733
export type BehaviouralProfileDelta = z.infer<typeof BehaviouralProfileDeltaSchema>;
734
export type RecordAttestation = z.infer<typeof RecordAttestationSchema>;
735
736
export interface KeyPair {
737
  keyId: string;
738
  publicKey: string;
739
  privateKey: string;
740
}
741
742
export function createKeyPair(keyId: string = randomUUID()): KeyPair {
743
  const pair = generateKeyPairSync('ed25519');
744
  return {
745
    keyId,
746
    publicKey: pair.publicKey.export({ type: 'spki', format: 'der' }).toString('base64url'),
747
    privateKey: pair.privateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64url'),
748
  };
749
}
750
751
export function canonicalHash(value: unknown): string {
752
  return createHash('sha256').update(canonicalize(value)).digest('base64url');
753
}
754
755
function unsigned(value: Record<string, unknown>): Record<string, unknown> {
756
  const copy = structuredClone(value);
757
  delete copy.signature;
758
  delete copy.signatures;
759
  return copy;
760
}
761
762
export function signObject<T extends Record<string, unknown>>(
763
  value: T,
764
  key: KeyPair,
765
): T & { signature: z.infer<typeof SignatureSchema> } {
766
  const payload = Buffer.from(canonicalize(unsigned(value)));
767
  const privateKey = {
768
    key: Buffer.from(key.privateKey, 'base64url'),
769
    type: 'pkcs8' as const,
770
    format: 'der' as const,
771
  };
772
  return {
773
    ...value,
774
    signature: {
775
      algorithm: 'Ed25519',
776
      keyId: key.keyId,
777
      value: sign(null, payload, privateKey).toString('base64url'),
778
    },
779
  };
780
}
781
782
export function verifyObject(value: Record<string, unknown>, publicKey: string): boolean {
783
  const parsed = SignatureSchema.safeParse(value.signature);
784
  if (!parsed.success) return false;
785
  const key = {
786
    key: Buffer.from(publicKey, 'base64url'),
787
    type: 'spki' as const,
788
    format: 'der' as const,
789
  };
790
  return verify(
791
    null,
792
    Buffer.from(canonicalize(unsigned(value))),
793
    key,
794
    Buffer.from(parsed.data.value, 'base64url'),
795
  );
796
}
797
798
export function signNamed<T extends Record<string, unknown>>(
799
  value: T,
800
  signerId: string,
801
  key: KeyPair,
802
): T & { signatures: Record<string, z.infer<typeof SignatureSchema>> } {
803
  const payload = Buffer.from(canonicalize(unsigned(value)));
804
  const privateKey = {
805
    key: Buffer.from(key.privateKey, 'base64url'),
806
    type: 'pkcs8' as const,
807
    format: 'der' as const,
808
  };
809
  const signature = {
810
    algorithm: 'Ed25519' as const,
811
    keyId: key.keyId,
812
    value: sign(null, payload, privateKey).toString('base64url'),
813
  };
814
  return {
815
    ...value,
816
    signatures: { ...((value.signatures as object) ?? {}), [signerId]: signature },
817
  };
818
}
819
820
export function verifyNamed(
821
  value: Record<string, unknown>,
822
  signerId: string,
823
  publicKey: string,
824
): boolean {
825
  const signatures = value.signatures as Record<string, unknown> | undefined;
826
  const parsed = SignatureSchema.safeParse(signatures?.[signerId]);
827
  if (!parsed.success) return false;
828
  const key = {
829
    key: Buffer.from(publicKey, 'base64url'),
830
    type: 'spki' as const,
831
    format: 'der' as const,
832
  };
833
  return verify(
834
    null,
835
    Buffer.from(canonicalize(unsigned(value))),
836
    key,
837
    Buffer.from(parsed.data.value, 'base64url'),
838
  );
839
}