| 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 | });
|