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

Commit 81afd778

81afd7789b27Merged

aryaamin <[email protected]> · 2 months ago · 11 files · ~11 modified

Description

fix: surface unilateral outcomes immediately

Details

Commit
81afd7789b27
Parent
2a4cc60d2506

apps/dashboard/src/main.tsxmodifiedView file

@@ -946,9 +946,9 @@
946946
947947 function History({ data }: { data: DashboardData }) {
948948 const [query, setQuery] = useState('');
949- const [status, setStatus] = useState<'all' | 'pending' | 'active' | 'finalizing' | 'completed'>(
950- 'all',
951- );
949+ const [status, setStatus] = useState<
950+ 'all' | 'pending' | 'active' | 'provisional' | 'finalizing' | 'completed'
951+ >('all');
952952 const journeys = useMemo(() => interactionJourneys(data), [data]);
953953 const filtered = useMemo(() => {
954954 const needle = query.trim().toLowerCase();
@@ -982,7 +982,7 @@
982982 aria-label="Search interaction history"
983983 />
984984 <div className="filterRow" aria-label="Interaction status filters">
985- {(['all', 'pending', 'active', 'finalizing', 'completed'] as const).map((value) => (
985+ {(['all', 'pending', 'active', 'provisional', 'completed'] as const).map((value) => (
986986 <button
987987 key={value}
988988 className={status === value ? 'filterChip active' : 'filterChip'}
@@ -1144,7 +1144,9 @@
11441144 taskCategory: String(interaction.contract?.taskCategory ?? 'general'),
11451145 status: String(
11461146 conclusion || receipt
1147- ? 'completed'
1147+ ? conclusion?.lifecycle === 'provisional' || receipt?.provisional
1148+ ? 'provisional'
1149+ : 'completed'
11481150 : reports.length > 0
11491151 ? 'finalizing'
11501152 : (session?.status ?? interaction.status ?? 'pending'),
@@ -1211,7 +1213,7 @@
12111213 {latestCheckpoint
12121214 ? `${Math.round(progress * 100)}% · ${humanize(latestCheckpoint.state)}`
12131215 : journey.reports.length
1214- ? 'Finalizing'
1216+ ? 'One side ended'
12151217 : 'Awaiting checkpoint'}
12161218 </strong>
12171219 <small>
@@ -1229,7 +1231,9 @@
12291231 </strong>
12301232 <small>
12311233 {conclusion
1232- ? humanize(conclusion.consensus)
1234+ ? conclusion.lifecycle === 'provisional'
1235+ ? `${Math.round(Number(conclusion.confidence ?? 0) * 100)}% confidence · peer ${humanize(conclusion.peerReportStatus ?? 'awaiting').toLowerCase()}`
1236+ : humanize(conclusion.consensus)
12331237 : journey.reports.length === 1
12341238 ? 'One agent finished; confirming with the peer'
12351239 : 'Conversation remains active'}
@@ -1237,7 +1241,11 @@
12371241 </section>
12381242 <section>
12391243 <span>Sealed feedback</span>
1240- <strong>{conclusion ? 'Released' : `${submittedFeedback}/2 submitted`}</strong>
1244+ <strong>
1245+ {journey.feedbackRequests.some((request) => request.status === 'pending')
1246+ ? `${submittedFeedback}/2 sealed`
1247+ : 'Window closed'}
1248+ </strong>
12411249 <small>{feedbackState(journey.feedbackRequests)}</small>
12421250 </section>
12431251 <section>
@@ -1247,7 +1255,9 @@
12471255 ? journey.eligibility.eligible
12481256 ? `${Math.round(Number(journey.eligibility.sampleWeight ?? 0) * 100)}% weight`
12491257 : 'Not eligible'
1250- : 'Not assessed'}
1258+ : conclusion?.lifecycle === 'provisional'
1259+ ? 'Pending corroboration'
1260+ : 'Not assessed'}
12511261 </strong>
12521262 <small>{humanize(journey.eligibility?.contributionMode ?? 'Local only')}</small>
12531263 </section>
@@ -1292,8 +1302,17 @@
12921302 {conclusion ? (
12931303 <section className="outcomeCard">
12941304 <div>
1295- <span className="eyebrow">WHAT HAPPENED</span>
1305+ <span className="eyebrow">
1306+ {conclusion.lifecycle === 'provisional' ? 'PROVISIONAL INSIGHT' : 'WHAT HAPPENED'}
1307+ </span>
12961308 <h3>{conclusion.summary}</h3>
1309+ {conclusion.lifecycle === 'provisional' ? (
1310+ <p className="progressWarning">
1311+ Based on {conclusion.reportIds?.length ?? 1} participant report. Missing:{' '}
1312+ {(conclusion.missingReportAgentIds ?? []).join(', ') || 'peer report'}. This is
1313+ usable now at reduced confidence and will be revised if the peer responds.
1314+ </p>
1315+ ) : null}
12971316 </div>
12981317 {!!conclusion.criteria?.length && (
12991318 <ul>
@@ -1445,10 +1464,17 @@
14451464 ? [
14461465 {
14471466 at: String(journey.conclusion.generatedAt),
1448- title: 'Bilateral conclusion released',
1467+ title:
1468+ journey.conclusion.lifecycle === 'provisional'
1469+ ? 'Provisional insight generated'
1470+ : 'Conclusion released',
14491471 detail: String(journey.conclusion.summary),
14501472 status: String(journey.conclusion.consensus),
1451- tone: journey.conclusion.consensus === 'conflicting' ? 'warn' : 'good',
1473+ tone:
1474+ journey.conclusion.lifecycle === 'provisional' ||
1475+ journey.conclusion.consensus === 'conflicting'
1476+ ? 'warn'
1477+ : 'good',
14521478 },
14531479 ]
14541480 : []),
@@ -1456,8 +1482,12 @@
14561482 ? [
14571483 {
14581484 at: timestamp(journey.receipt),
1459- title: 'Outcome receipt attested',
1460- detail: 'Contract result, commitment status, and evidence hashes were sealed.',
1485+ title: journey.receipt.provisional
1486+ ? 'Provisional outcome attested'
1487+ : 'Outcome receipt attested',
1488+ detail: journey.receipt.provisional
1489+ ? 'The available one-sided report was sealed without claiming peer agreement.'
1490+ : 'Contract result, commitment status, and evidence hashes were sealed.',
14611491 status: String(journey.receipt.outcome ?? 'signed'),
14621492 tone: journey.receipt.outcome === 'failure' ? 'danger' : 'good',
14631493 },
@@ -1739,6 +1769,9 @@
17391769 function Insights({ data }: { data: DashboardData }) {
17401770 const eligible = data.learningEligibility.filter((item) => item.eligible);
17411771 const pendingFeedback = data.feedbackRequests.filter((item) => item.status === 'pending');
1772+ const provisional = data.interactionConclusions.filter(
1773+ (item) => item.lifecycle === 'provisional',
1774+ );
17421775 return (
17431776 <>
17441777 <PageHead page="insights" />
@@ -1761,8 +1794,41 @@
17611794 <span>
17621795 <strong>{pendingFeedback.length}</strong> feedback pending
17631796 </span>
1797+ <span>
1798+ <strong>{provisional.length}</strong> provisional signals
1799+ </span>
17641800 </div>
17651801 </section>
1802+ {!!provisional.length && (
1803+ <Panel
1804+ title="Provisional interaction signals"
1805+ subtitle="Available now, kept separate from corroborated reliability history"
1806+ >
1807+ <div className="learningDecisionList">
1808+ {provisional
1809+ .slice()
1810+ .sort((left, right) => Date.parse(timestamp(right)) - Date.parse(timestamp(left)))
1811+ .map((conclusion) => (
1812+ <article key={conclusion.conclusionId}>
1813+ <StatusPill value="provisional" />
1814+ <div>
1815+ <strong>{conclusion.summary}</strong>
1816+ <small>
1817+ {Math.round(Number(conclusion.confidence ?? 0) * 100)}% confidence · peer{' '}
1818+ {humanize(conclusion.peerReportStatus ?? 'awaiting').toLowerCase()} ·{' '}
1819+ {relativeTime(timestamp(conclusion))}
1820+ </small>
1821+ <p>
1822+ Missing report from{' '}
1823+ {(conclusion.missingReportAgentIds ?? []).join(', ') || 'the counterparty'}.
1824+ The signal will be revised if more structured evidence arrives.
1825+ </p>
1826+ </div>
1827+ </article>
1828+ ))}
1829+ </div>
1830+ </Panel>
1831+ )}
17661832 <section className="insightGrid">
17671833 {data.profiles.length ? (
17681834 data.profiles.map((profile) => {
@@ -3107,8 +3173,8 @@
31073173 normalized.includes(item),
31083174 )
31093175 ? 'danger'
3110- : ['partial', 'pending', 'challenge', 'conflict', 'expired', 'excluded'].some((item) =>
3111- normalized.includes(item),
3176+ : ['partial', 'pending', 'provisional', 'challenge', 'conflict', 'expired', 'excluded'].some(
3177+ (item) => normalized.includes(item),
31123178 )
31133179 ? 'warn'
31143180 : 'neutral';