The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.
Commit f87848a4
f87848a4fcb8Merged
aryaamin <[email protected]> · 2 months ago · 20 files · +11 added · ~9 modified
Description
feat: add interaction intelligence protocol
Details
- Commit
- f87848a4fcb8
- Parent
- 1a7911da5a29
packages/core/src/index.tsmodifiedView file
@@ -1,6 +1,7 @@
11
import { randomUUID } from 'node:crypto';22
import {33
AgentIdentitySchema,4+
CounterpartyBriefSchema,45
canonicalHash,56
createKeyPair,67
DelegationCredentialSchema,@@ -14,17 +15,195 @@
1415
verifyNamed,1516
verifyObject,1617
type AgentIdentity,18+
type CounterpartyBrief,1719
type DelegationCredential,1820
type FactCheckResult,1921
type Feedback,2022
type InteractionContract,2123
type InteractionEvent,2224
type KeyPair,25+
type LiveSessionInsight,26+
type PublicAgentCard,2327
type Receipt,2428
type RiskDecision,2529
type TrustEnvelope,2630
} from '../../protocol/src/index.js';2731
32+
const REQUIREMENT_STOP_WORDS = new Set([33+
'about',34+
'agent',35+
'available',36+
'from',37+
'have',38+
'into',39+
'must',40+
'provide',41+
'return',42+
'should',43+
'that',44+
'their',45+
'this',46+
'with',47+
]);48+
49+
function normalized(value: string): string {50+
return value51+
.trim()52+
.toLowerCase()53+
.replace(/[^a-z0-9]+/g, ' ')54+
.trim();55+
}56+
57+
function meaningfulTerms(value: string): string[] {58+
return [59+
...new Set(60+
normalized(value)61+
.split(' ')62+
.filter((term) => term.length >= 4 && !REQUIREMENT_STOP_WORDS.has(term)),63+
),64+
];65+
}66+
67+
function assessDeclaration(68+
requirement: string,69+
declarations: string[],70+
): { status: 'match' | 'partial' | 'mismatch' | 'unknown'; reason: string; confidence: number } {71+
const normalizedRequirement = normalized(requirement);72+
const normalizedDeclarations = declarations.map(normalized);73+
if (74+
normalizedDeclarations.some(75+
(declaration) => declaration === '*' || declaration === normalizedRequirement,76+
)77+
)78+
return {79+
status: 'match',80+
reason: 'The counterparty explicitly declares this capability.',81+
confidence: 0.95,82+
};83+
84+
const terms = meaningfulTerms(requirement);85+
const negative = normalizedDeclarations.find((declaration) =>86+
terms.some(87+
(term) =>88+
declaration.includes(`no ${term}`) ||89+
declaration.includes(`not ${term}`) ||90+
declaration.includes(`cannot ${term}`),91+
),92+
);93+
if (negative)94+
return {95+
status: 'mismatch',96+
reason: `The counterparty declares a conflicting limitation: ${negative}`,97+
confidence: 0.95,98+
};99+
100+
if (terms.length) {101+
const declarationText = normalizedDeclarations.join(' ');102+
const overlap = terms.filter((term) => declarationText.includes(term)).length / terms.length;103+
if (overlap >= 0.6)104+
return {105+
status: 'match',106+
reason: 'The counterparty declarations strongly match this requirement.',107+
confidence: 0.8,108+
};109+
if (overlap > 0)110+
return {111+
status: 'partial',112+
reason: 'The counterparty declarations only partially cover this requirement.',113+
confidence: 0.65,114+
};115+
}116+
return {117+
status: 'unknown',118+
reason: 'The published agent card does not establish this requirement.',119+
confidence: 0.35,120+
};121+
}122+
123+
export function buildCounterpartyBrief(input: {124+
interactionId: string;125+
contractHash: string;126+
contract: InteractionContract;127+
recipientAgentId: string;128+
subject: PublicAgentCard;129+
historyInsights: LiveSessionInsight[];130+
relevantSampleSize: number;131+
historyConfidence: number;132+
generatedAt?: string;133+
expiresAt: string;134+
}): CounterpartyBrief {135+
const declarations = [136+
input.subject.description,137+
...input.subject.capabilities,138+
...input.subject.limitations,139+
];140+
const requirements = [141+
{142+
requirementId: 'task-category',143+
requirement: input.contract.taskCategory,144+
kind: 'capability' as const,145+
},146+
{147+
requirementId: 'requested-outcome',148+
requirement: input.contract.requestedOutcome,149+
kind: 'scope' as const,150+
},151+
...input.contract.allowedActions.map((requirement, index) => ({152+
requirementId: `allowed-action-${index + 1}`,153+
requirement,154+
kind: 'capability' as const,155+
})),156+
...input.contract.successCriteria.map((requirement, index) => ({157+
requirementId: `success-criterion-${index + 1}`,158+
requirement,159+
kind: 'success_criterion' as const,160+
})),161+
...input.contract.evidenceRequirements.map((requirement, index) => ({162+
requirementId: `evidence-${index + 1}`,163+
requirement,164+
kind: 'evidence' as const,165+
})),166+
].map((requirement) => ({167+
...requirement,168+
...assessDeclaration(requirement.requirement, declarations),169+
sources: ['contract', 'self_declared'] as const,170+
evidenceReferences: [input.subject.cardUrl],171+
}));172+
const requirementReferences = requirements173+
.filter((requirement) => requirement.status !== 'match')174+
.map((requirement) => requirement.requirementId);175+
const insights = input.historyInsights.map((insight) => ({176+
...insight,177+
requirementReferences:178+
insight.requirementReferences.length > 0179+
? insight.requirementReferences180+
: requirementReferences,181+
}));182+
const needsChallenge =183+
input.relevantSampleSize === 0 ||184+
requirements.some((requirement) => requirement.status === 'mismatch') ||185+
insights.some((insight) => insight.severity === 'high');186+
return CounterpartyBriefSchema.parse({187+
briefId: randomUUID(),188+
interactionId: input.interactionId,189+
contractHash: input.contractHash,190+
recipientAgentId: input.recipientAgentId,191+
subjectAgentId: input.subject.agentId,192+
taskCategory: input.contract.taskCategory,193+
decision: needsChallenge ? 'CHALLENGE' : 'ALLOW',194+
requirements,195+
insights,196+
relevantSampleSize: input.relevantSampleSize,197+
historyConfidence: input.historyConfidence,198+
subjectAgentVersion: input.subject.agentVersion,199+
recommendedContractChanges: requirements200+
.filter((requirement) => requirement.status === 'mismatch')201+
.map((requirement) => `Resolve mismatch: ${requirement.requirement}`),202+
generatedAt: input.generatedAt ?? new Date().toISOString(),203+
expiresAt: input.expiresAt,204+
});205+
}206+
28207
export interface AuditStore {29208
append(kind: string, id: string, value: unknown): void;30209
list(kind: string): unknown[];packages/persistence/src/hosted.tsmodifiedView file
Loading diff…
packages/protocol/src/index.tsmodifiedView file
Loading diff…
schemas/v0.1/behavioural_profile_delta.schema.jsonaddedView file
Loading diff…
schemas/v0.1/completion_outcome.schema.jsonaddedView file
Loading diff…
schemas/v0.1/counterparty_brief.schema.jsonaddedView file
Loading diff…
schemas/v0.1/feedback_dimension.schema.jsonaddedView file
Loading diff…
schemas/v0.1/feedback_request.schema.jsonaddedView file
Loading diff…
schemas/v0.1/interaction_completion_report.schema.jsonaddedView file
Loading diff…
schemas/v0.1/interaction_conclusion.schema.jsonaddedView file
Loading diff…
schemas/v0.1/interaction_feedback.schema.jsonaddedView file
Loading diff…
schemas/v0.1/learning_eligibility_decision.schema.jsonaddedView file
Loading diff…
schemas/v0.1/live_session_activation.schema.jsonmodifiedView file
Loading diff…
schemas/v0.1/live_session_offer.schema.jsonmodifiedView file
Loading diff…
schemas/v0.1/requirement_assessment.schema.jsonaddedView file
Loading diff…
schemas/v0.1/success_criterion_assessment.schema.jsonaddedView file
Loading diff…
scripts/export-schemas.tsmodifiedView file
Loading diff…
tests/api.test.tsmodifiedView file
Loading diff…
tests/core.test.tsmodifiedView file
Loading diff…
tests/protocol.test.tsmodifiedView file
Loading diff…