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

Viewing files at commit 66a0492edc4aCommit detailsView latest

tests/gateway.test.ts 129 lines · 4.5 KB · typescript
1
import { describe, expect, it } from 'vitest';
2
import {
3
  decryptGatewayPayload,
4
  encryptGatewayPayload,
5
  attestSessionRecord,
6
  getSessionVerificationKey,
7
  issueGatewayGrant,
8
  issueSessionGrant,
9
  verifyGatewayGrant,
10
  verifySessionGrant,
11
  verifySessionRecordAttestation,
12
} from '../packages/persistence/src/relay.js';
13
import {
14
  ContractRevisionSchema,
15
  canonicalHash,
16
  verifyRecordAttestation,
17
} from '../packages/protocol/src/index.js';
18
19
describe('hosted runtime and session cryptography', () => {
20
  const secret = 'a-test-secret-that-is-at-least-thirty-two-bytes';
21
22
  it('encrypts message bodies and detects tampering', () => {
23
    const encrypted = encryptGatewayPayload(secret, { message: 'private task' });
24
    expect(JSON.stringify(encrypted)).not.toContain('private task');
25
    expect(decryptGatewayPayload(secret, encrypted)).toEqual({ message: 'private task' });
26
    expect(() =>
27
      decryptGatewayPayload(secret, { ...encrypted, ciphertext: `${encrypted.ciphertext}x` }),
28
    ).toThrow();
29
  });
30
31
  it('issues scoped, expiring delivery grants', () => {
32
    const token = issueGatewayGrant(secret, {
33
      interactionId: 'interaction-1',
34
      senderAgentId: 'agent-a',
35
      recipientAgentId: 'agent-b',
36
      expiresAt: Date.now() + 60_000,
37
    });
38
    expect(verifyGatewayGrant(secret, token).recipientAgentId).toBe('agent-b');
39
    expect(() => verifyGatewayGrant(secret, `${token}x`)).toThrow('Invalid gateway token');
40
  });
41
42
  it('issues platform-signed live-session credentials', () => {
43
    const token = issueSessionGrant(secret, {
44
      interactionId: 'interaction-1',
45
      senderAgentId: 'agent-a',
46
      recipientAgentId: 'agent-b',
47
      expiresAt: Date.now() + 60_000,
48
    });
49
    expect(verifySessionGrant(secret, token)).toMatchObject({
50
      interactionId: 'interaction-1',
51
      senderAgentId: 'agent-a',
52
      recipientAgentId: 'agent-b',
53
    });
54
    expect(() => verifySessionGrant(secret, `${token}x`)).toThrow(
55
      'Invalid live-session credential',
56
    );
57
  });
58
59
  it('attests structured records and rejects changed outcomes', () => {
60
    const report = { interactionId: 'interaction-1', outcome: 'success' };
61
    const attestation = attestSessionRecord(secret, report);
62
    const publicKey = getSessionVerificationKey(secret);
63
    expect(verifySessionRecordAttestation(publicKey, report, attestation)).toBe(true);
64
    expect(
65
      verifySessionRecordAttestation(publicKey, { ...report, outcome: 'failure' }, attestation),
66
    ).toBe(false);
67
  });
68
69
  it('creates a verifiable, hash-linked accepted contract revision', () => {
70
    const interactionId = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb';
71
    const now = '2026-08-30T00:00:00.000Z';
72
    const contract = {
73
      protocolVersion: '0.1' as const,
74
      interactionId,
75
      purpose: 'Buy paper',
76
      parties: ['agent-a', 'agent-b'],
77
      taskCategory: 'procurement',
78
      requestedOutcome: 'Five tonnes delivered',
79
      successCriteria: ['80 GSM verified'],
80
      allowedActions: ['negotiate'],
81
      prohibitedActions: ['exceed_budget'],
82
      allowedData: [],
83
      prohibitedData: ['credentials'],
84
      evidenceRequirements: ['invoice'],
85
      delegationRules: ['explicit_contract_scope'],
86
      humanApprovalRequirements: [],
87
      factCheckingPolicy: 'important_claims',
88
      mediationPolicy: 'mutual_consent' as const,
89
      retentionDays: 30,
90
      completionConditions: ['delivery accepted'],
91
      cancellationConditions: ['either party before acceptance'],
92
      signatures: {},
93
    };
94
    const termsHash = canonicalHash(contract);
95
    const base = ContractRevisionSchema.parse({
96
      revisionId: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
97
      interactionId,
98
      revision: 2,
99
      previousTermsHash: 'previous-hash',
100
      termsHash,
101
      contract,
102
      proposedByAgentId: 'agent-b',
103
      status: 'accepted',
104
      acceptances: {
105
        'agent-a': {
106
          agentId: 'agent-a',
107
          method: 'oauth_installation',
108
          termsHash,
109
          acceptedAt: now,
110
        },
111
        'agent-b': {
112
          agentId: 'agent-b',
113
          method: 'oauth_installation',
114
          termsHash,
115
          acceptedAt: now,
116
        },
117
      },
118
      createdAt: now,
119
      updatedAt: now,
120
    });
121
    const revision = ContractRevisionSchema.parse({
122
      ...base,
123
      platformAttestation: attestSessionRecord(secret, base),
124
    });
125
    const publicKey = getSessionVerificationKey(secret);
126
    expect(verifyRecordAttestation(revision, publicKey)).toBe(true);
127
    expect(verifyRecordAttestation({ ...revision, termsHash: 'tampered' }, publicKey)).toBe(false);
128
  });
129
});