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

Viewing files at commit 66a0492edc4aCommit detailsView latest

tests/automation.test.ts 128 lines · 4.0 KB · typescript
1
import { describe, expect, it } from 'vitest';
2
import { canonicalHash, type FederatedInteraction } from '@openclasp/protocol';
3
import {
4
  buildPublicAgentCard,
5
  canAutoAcceptInteraction,
6
} from '../packages/persistence/src/hosted.js';
7
import type { AgentProfile } from '../packages/persistence/src/onboarding.js';
8
9
function fixture(): { agent: AgentProfile; interaction: FederatedInteraction } {
10
  const interactionId = crypto.randomUUID();
11
  const now = new Date().toISOString();
12
  const contract = {
13
    protocolVersion: '0.1' as const,
14
    interactionId,
15
    purpose: 'Research a competitor',
16
    parties: ['agent:a', 'agent:b'],
17
    taskCategory: 'research',
18
    requestedOutcome: 'A short brief',
19
    successCriteria: ['Directly answer the task'],
20
    allowedActions: [],
21
    prohibitedActions: [],
22
    allowedData: [],
23
    prohibitedData: [],
24
    evidenceRequirements: [],
25
    delegationRules: ['explicit_contract_scope'],
26
    humanApprovalRequirements: [],
27
    factCheckingPolicy: 'important_claims',
28
    mediationPolicy: 'mutual_consent' as const,
29
    retentionDays: 30,
30
    completionConditions: ['Directly answer the task'],
31
    cancellationConditions: ['either_party_before_completion'],
32
    signatures: {},
33
  };
34
  const termsHash = canonicalHash(contract);
35
  return {
36
    agent: {
37
      agentId: 'agent:b',
38
      projectId: 'project:b',
39
      name: 'Research agent',
40
      description: '',
41
      framework: 'Codex',
42
      agentVersion: '1.0.0',
43
      a2aEndpoint: 'https://agent-b.example/a2a',
44
      autoPublish: true,
45
      autoAcceptPolicy: 'safe_matching',
46
      autoAcceptTaskCategories: ['research'],
47
      capabilities: ['research'],
48
      limitations: [],
49
      identityMode: 'oauth_installation',
50
      status: 'active',
51
      createdAt: now,
52
      updatedAt: now,
53
    },
54
    interaction: {
55
      protocolVersion: '0.1',
56
      interactionId,
57
      initiatorAgentId: 'agent:a',
58
      responderAgentId: 'agent:b',
59
      status: 'pending',
60
      contract,
61
      termsHash,
62
      acceptances: {
63
        'agent:a': {
64
          agentId: 'agent:a',
65
          method: 'oauth_installation',
66
          termsHash,
67
          acceptedAt: now,
68
        },
69
      },
70
      contractRevision: 1,
71
      contractRevisions: [],
72
      responderTransport: {
73
        protocol: 'A2A/1.0',
74
        protocolBinding: 'JSONRPC',
75
        endpoint: 'https://agent-b.example/a2a',
76
        managedBy: 'agent',
77
      },
78
      createdAt: now,
79
      updatedAt: now,
80
      expiresAt: new Date(Date.now() + 60_000).toISOString(),
81
    },
82
  };
83
}
84
85
describe('safe connection automation', () => {
86
  it('auto-accepts only matching, data-free requests within the owner policy', () => {
87
    const { agent, interaction } = fixture();
88
    expect(canAutoAcceptInteraction(agent, interaction)).toBe(true);
89
    expect(
90
      canAutoAcceptInteraction(agent, {
91
        ...interaction,
92
        contract: { ...interaction.contract, allowedData: ['customer_records'] },
93
      }),
94
    ).toBe(false);
95
    expect(
96
      canAutoAcceptInteraction(agent, {
97
        ...interaction,
98
        contract: { ...interaction.contract, purpose: 'Find a customer access token' },
99
      }),
100
    ).toBe(false);
101
    expect(
102
      canAutoAcceptInteraction(agent, {
103
        ...interaction,
104
        contract: { ...interaction.contract, taskCategory: 'payments' },
105
      }),
106
    ).toBe(false);
107
    expect(canAutoAcceptInteraction({ ...agent, autoAcceptPolicy: 'off' }, interaction)).toBe(
108
      false,
109
    );
110
  });
111
112
  it('publishes only an agent-managed runtime endpoint', () => {
113
    const { agent } = fixture();
114
    const persistent = buildPublicAgentCard(
115
      { ...agent, agentMode: 'persistent_runtime' },
116
      'https://openclasp.example',
117
    );
118
    expect(persistent).toMatchObject({
119
      slug: expect.stringMatching(/^research-agent-/),
120
      profileUrl: expect.stringContaining('/a/research-agent-'),
121
      verification: { status: 'verified', method: 'openclasp_oauth_account' },
122
    });
123
    expect(persistent.transports[0]).toMatchObject({
124
      endpoint: 'https://agent-b.example/a2a',
125
      managedBy: 'agent',
126
    });
127
  });
128
});