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

Commit 90ba0b08

90ba0b085978Merged

aryaamin <[email protected]> · 2 months ago · 15 files · +2 added · ~13 modified

Description

feat: add interaction progress lifecycle

Details

Commit
90ba0b085978
Parent
03be656e7804

apps/dashboard/src/main.tsxmodifiedView file

@@ -932,7 +932,9 @@
932932
933933 function History({ data }: { data: DashboardData }) {
934934 const [query, setQuery] = useState('');
935- const [status, setStatus] = useState<'all' | 'pending' | 'active' | 'completed'>('all');
935+ const [status, setStatus] = useState<'all' | 'pending' | 'active' | 'finalizing' | 'completed'>(
936+ 'all',
937+ );
936938 const journeys = useMemo(() => interactionJourneys(data), [data]);
937939 const filtered = useMemo(() => {
938940 const needle = query.trim().toLowerCase();
@@ -966,7 +968,7 @@
966968 aria-label="Search interaction history"
967969 />
968970 <div className="filterRow" aria-label="Interaction status filters">
969- {(['all', 'pending', 'active', 'completed'] as const).map((value) => (
971+ {(['all', 'pending', 'active', 'finalizing', 'completed'] as const).map((value) => (
970972 <button
971973 key={value}
972974 className={status === value ? 'filterChip active' : 'filterChip'}
@@ -1129,7 +1131,9 @@
11291131 status: String(
11301132 conclusion || receipt
11311133 ? 'completed'
1132- : (session?.status ?? interaction.status ?? 'pending'),
1134+ : reports.length > 0
1135+ ? 'finalizing'
1136+ : (session?.status ?? interaction.status ?? 'pending'),
11331137 ),
11341138 participants,
11351139 updatedAt:
@@ -1167,6 +1171,14 @@
11671171 function InteractionJourney({ journey }: { journey: InteractionJourneyModel }) {
11681172 const timeline = interactionTimeline(journey);
11691173 const conclusion = journey.conclusion;
1174+ const checkpoints = journey.events.filter(
1175+ (event) => (event.type ?? event.eventType) === 'progress_checkpoint' && event.checkpoint,
1176+ );
1177+ const latestCheckpoint = checkpoints.at(-1)?.checkpoint;
1178+ const progress = Number(latestCheckpoint?.progress ?? 0);
1179+ const submittedFeedback = new Set(
1180+ journey.feedback.map((item) => item.reviewerAgentId).filter(Boolean),
1181+ ).size;
11701182 return (
11711183 <article className="journeyDetail">
11721184 <header className="journeyHero">
@@ -1180,13 +1192,38 @@
11801192
11811193 <div className="journeySummaryGrid">
11821194 <section>
1195+ <span>Progress</span>
1196+ <strong>
1197+ {latestCheckpoint
1198+ ? `${Math.round(progress * 100)}% · ${humanize(latestCheckpoint.state)}`
1199+ : journey.reports.length
1200+ ? 'Finalizing'
1201+ : 'Awaiting checkpoint'}
1202+ </strong>
1203+ <small>
1204+ {latestCheckpoint
1205+ ? `${humanize(latestCheckpoint.topicStatus)} · ${Math.round(Number(latestCheckpoint.confidence ?? 0) * 100)}% confidence`
1206+ : 'Agents report compact progress every few exchanges'}
1207+ </small>
1208+ </section>
1209+ <section>
11831210 <span>Outcome</span>
1184- <strong>{humanize(conclusion?.outcome ?? 'In progress')}</strong>
1185- <small>{humanize(conclusion?.consensus ?? 'Awaiting bilateral conclusion')}</small>
1211+ <strong>
1212+ {conclusion
1213+ ? humanize(conclusion.outcome)
1214+ : `${journey.reports.length}/2 terminal reports`}
1215+ </strong>
1216+ <small>
1217+ {conclusion
1218+ ? humanize(conclusion.consensus)
1219+ : journey.reports.length === 1
1220+ ? 'One agent finished; confirming with the peer'
1221+ : 'Conversation remains active'}
1222+ </small>
11861223 </section>
11871224 <section>
1188- <span>Feedback</span>
1189- <strong>{conclusion ? 'Closed' : `${journey.feedback.length} submitted locally`}</strong>
1225+ <span>Sealed feedback</span>
1226+ <strong>{conclusion ? 'Released' : `${submittedFeedback}/2 submitted`}</strong>
11901227 <small>{feedbackState(journey.feedbackRequests)}</small>
11911228 </section>
11921229 <section>
@@ -1202,6 +1239,42 @@
12021239 </section>
12031240 </div>
12041241
1242+ {latestCheckpoint ? (
1243+ <section className="progressCard">
1244+ <div className="progressCardTop">
1245+ <div>
1246+ <span className="eyebrow">LATEST PRIVATE CHECKPOINT</span>
1247+ <h3>{humanize(latestCheckpoint.state)}</h3>
1248+ </div>
1249+ <strong>{Math.round(progress * 100)}%</strong>
1250+ </div>
1251+ <div
1252+ className="progressTrack"
1253+ role="progressbar"
1254+ aria-label="Interaction progress"
1255+ aria-valuemin={0}
1256+ aria-valuemax={100}
1257+ aria-valuenow={Math.round(progress * 100)}
1258+ >
1259+ <i style={{ width: `${Math.round(progress * 100)}%` }} />
1260+ </div>
1261+ <div className="progressFacts">
1262+ <span>{latestCheckpoint.criteriaMet?.length ?? 0} criteria met</span>
1263+ <span>{latestCheckpoint.criteriaRemaining?.length ?? 0} remaining</span>
1264+ <span>{latestCheckpoint.expectedRemainingTurns ?? '—'} expected turns</span>
1265+ <span>
1266+ {latestCheckpoint.needsHuman ? 'Human input needed' : 'No human input needed'}
1267+ </span>
1268+ </div>
1269+ {latestCheckpoint.topicStatus !== 'in_scope' ? (
1270+ <p className="progressWarning">
1271+ Topic {humanize(latestCheckpoint.topicStatus)}. Amend the contract or start a new
1272+ interaction before continuing.
1273+ </p>
1274+ ) : null}
1275+ </section>
1276+ ) : null}
1277+
12051278 {conclusion ? (
12061279 <section className="outcomeCard">
12071280 <div>
@@ -1228,7 +1301,11 @@
12281301 </section>
12291302 ) : (
12301303 <div className="journeyNotice">
1231- No conclusion yet. It appears after terminal reports and bilateral feedback or timeout.
1304+ {journey.reports.length === 1
1305+ ? 'Finalizing: one agent submitted its terminal report. OpenClasp has requested independent confirmation from the peer.'
1306+ : journey.reports.length >= 2
1307+ ? 'Both agents finished. Sealed feedback remains private until both respond or the feedback window expires.'
1308+ : 'Active interaction. Progress checkpoints appear after several meaningful exchanges; either agent can finalize immediately when the task is done.'}
12321309 </div>
12331310 )}
12341311
@@ -1290,17 +1367,32 @@
12901367 },
12911368 ]
12921369 : []),
1293- ...journey.events.map((event) => ({
1294- at: timestamp(event),
1295- title: humanize(event.eventType ?? 'Structured event'),
1296- detail: String(event.agentId ?? 'OpenClasp') + ' submitted structured metadata.',
1297- status: String(event.visibility ?? 'recorded'),
1298- tone: String(event.eventType).includes('violation')
1299- ? 'danger'
1300- : String(event.eventType).includes('warning')
1301- ? 'warn'
1302- : 'neutral',
1303- })),
1370+ ...journey.events.map((event) => {
1371+ const eventType = String(event.type ?? event.eventType ?? 'structured_event');
1372+ const checkpoint = event.checkpoint;
1373+ return {
1374+ at: timestamp(event),
1375+ title:
1376+ eventType === 'progress_checkpoint'
1377+ ? `${Math.round(Number(checkpoint?.progress ?? 0) * 100)}% progress checkpoint`
1378+ : humanize(eventType),
1379+ detail:
1380+ eventType === 'progress_checkpoint'
1381+ ? `${event.agentId} · ${humanize(checkpoint?.state ?? 'active')} · ${humanize(checkpoint?.topicStatus ?? 'in_scope')}`
1382+ : String(event.agentId ?? 'OpenClasp') + ' submitted structured metadata.',
1383+ status: String(checkpoint?.state ?? event.visibility ?? 'recorded'),
1384+ tone:
1385+ checkpoint?.topicStatus === 'changed' || checkpoint?.state === 'blocked'
1386+ ? 'warn'
1387+ : eventType.includes('violation')
1388+ ? 'danger'
1389+ : eventType.includes('warning')
1390+ ? 'warn'
1391+ : checkpoint
1392+ ? 'good'
1393+ : 'neutral',
1394+ };
1395+ }),
13041396 ...journey.reports.map((report) => ({
13051397 at: timestamp(report),
13061398 title: `Completion reported by ${report.reportingAgentId}`,