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

Viewing files at commit c2c9d5ba27afCommit detailsView latest

tests/core.test.ts 147 lines · 5.3 KB · typescript
1
import { describe, expect, it } from 'vitest';
2
import { createIdentity, FixtureFactCheckProvider, TrustEngine } from '@openclasp/core';
3
import { signNamed, type InteractionContract, type Receipt } from '@openclasp/protocol';
4
import { createSignedEvent } from '@openclasp/sdk';
5
6
describe('general assurance behavior', () => {
7
  it('requires every contract party to sign before the agreement is active', () => {
8
    const engine = new TrustEngine();
9
    const first = createIdentity({
10
      agentId: 'agent:first',
11
      operatorRef: 'operator:a',
12
      capabilities: ['coordinate'],
13
    });
14
    const second = createIdentity({
15
      agentId: 'agent:second',
16
      operatorRef: 'operator:b',
17
      capabilities: ['coordinate'],
18
    });
19
    engine.registerAgent(first.identity);
20
    engine.registerAgent(second.identity);
21
    const unsigned: InteractionContract = {
22
      protocolVersion: '0.1',
23
      interactionId: crypto.randomUUID(),
24
      purpose: 'Coordinate a task',
25
      parties: [first.identity.agentId, second.identity.agentId],
26
      taskCategory: 'coordination',
27
      requestedOutcome: 'Agreed plan',
28
      successCriteria: ['Plan accepted'],
29
      allowedActions: ['coordinate'],
30
      prohibitedActions: [],
31
      allowedData: ['public'],
32
      prohibitedData: ['private'],
33
      evidenceRequirements: [],
34
      delegationRules: [],
35
      humanApprovalRequirements: [],
36
      factCheckingPolicy: 'important_claims',
37
      mediationPolicy: 'mutual_consent',
38
      retentionDays: 30,
39
      completionConditions: ['plan_delivered'],
40
      cancellationConditions: ['either_party'],
41
      signatures: {},
42
    };
43
    const oneSignature = signNamed(
44
      unsigned as unknown as Record<string, unknown>,
45
      first.identity.agentId,
46
      first.keyPair,
47
    ) as unknown as InteractionContract;
48
    expect(() => engine.saveContract(oneSignature)).toThrow('every party');
49
    const complete = signNamed(
50
      oneSignature as unknown as Record<string, unknown>,
51
      second.identity.agentId,
52
      second.keyPair,
53
    ) as unknown as InteractionContract;
54
    expect(engine.saveContract(complete).interactionId).toBe(unsigned.interactionId);
55
  });
56
57
  it('does not fact-check opinions as objective truth', async () => {
58
    const provider = new FixtureFactCheckProvider();
59
    expect((await provider.check('I think green is the best color')).status).toBe(
60
      'not_fact_checkable',
61
    );
62
  });
63
64
  it('verifies a receipt without recording it', () => {
65
    const engine = new TrustEngine();
66
    const agent = createIdentity({
67
      agentId: 'agent:receipt',
68
      operatorRef: 'operator:a',
69
      capabilities: ['work'],
70
    });
71
    engine.registerAgent(agent.identity);
72
    const base: Receipt = {
73
      receiptId: crypto.randomUUID(),
74
      interactionId: crypto.randomUUID(),
75
      participants: [agent.identity.agentId],
76
      agentVersions: { [agent.identity.agentId]: '1.0.0' },
77
      contractHash: 'contract',
78
      startedAt: new Date(Date.now() - 1000).toISOString(),
79
      completedAt: new Date().toISOString(),
80
      outcome: 'success',
81
      commitmentsFulfilled: ['done'],
82
      commitmentsMissed: [],
83
      evidenceHashes: [],
84
      policyWarnings: [],
85
      policyViolations: [],
86
      disputeStatus: 'none',
87
      delegationChainHash: 'none',
88
      unilateral: false,
89
      signatures: {},
90
    };
91
    const signed = signNamed(
92
      base as unknown as Record<string, unknown>,
93
      agent.identity.agentId,
94
      agent.keyPair,
95
    ) as unknown as Receipt;
96
    expect(engine.verifyReceipt(signed).receiptId).toBe(base.receiptId);
97
    expect(engine.receipts.size).toBe(0);
98
  });
99
100
  it('requires opt-in and strips payloads from network contributions', () => {
101
    const engine = new TrustEngine();
102
    const agent = createIdentity({
103
      agentId: 'agent:a',
104
      operatorRef: 'operator:a',
105
      capabilities: ['chat'],
106
    });
107
    engine.registerAgent(agent.identity);
108
    const event = createSignedEvent(
109
      {
110
        protocolVersion: '0.1',
111
        eventId: crypto.randomUUID(),
112
        interactionId: crypto.randomUUID(),
113
        eventType: 'claim',
114
        agentId: agent.identity.agentId,
115
        agentVersion: '1.0.0',
116
        timestamp: new Date().toISOString(),
117
        visibility: 'network_aggregate',
118
        provenance: 'observed',
119
        payload: { rawMessage: 'private conversation', claim: 'secret' },
120
      },
121
      agent.keyPair,
122
    );
123
    expect(engine.networkContribution(event)).toBeNull();
124
    engine.setContributionConsent(agent.identity.agentId, true);
125
    const contribution = engine.networkContribution(event);
126
    expect(contribution).not.toHaveProperty('payload');
127
    expect(JSON.stringify(contribution)).not.toContain('private conversation');
128
  });
129
130
  it('requires mutual mediation consent', () => {
131
    const engine = new TrustEngine();
132
    const conflict = engine.createConflict({
133
      interactionId: crypto.randomUUID(),
134
      issue: 'ambiguous requirement',
135
      participants: ['a', 'b'],
136
      positions: {},
137
      evidence: [],
138
      contractClauses: [],
139
      missingInformation: [],
140
      possibleResolutions: ['clarify'],
141
    });
142
    engine.permitMediation(conflict.conflictId, 'a');
143
    expect(() => engine.resolveConflict(conflict.conflictId, 'clarified')).toThrow('Mutual');
144
    engine.permitMediation(conflict.conflictId, 'b');
145
    expect(engine.resolveConflict(conflict.conflictId, 'clarified').status).toBe('resolved');
146
  });
147
});