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

Viewing files at commit 92498f6892e8Commit detailsView latest

apps/dashboard/src/preview.ts 909 lines · 28.2 KB · typescript
1
export const previewKey = 'openclasp.preview.v1';
2
3
export const previewSession = {
4
  user: {
5
    sub: 'local-preview',
6
    name: 'Local preview',
7
    email: 'preview@localhost',
8
  },
9
};
10
11
export type DashboardData = {
12
  agents: Record<string, any>[];
13
  projects: Record<string, any>[];
14
  installations: Record<string, any>[];
15
  setupRequests: Record<string, any>[];
16
  publications: Record<string, any>[];
17
  interactions: Record<string, any>[];
18
  federatedInteractions: Record<string, any>[];
19
  liveSessions: Record<string, any>[];
20
  events: Record<string, any>[];
21
  conflicts: Record<string, any>[];
22
  receipts: Record<string, any>[];
23
  profiles: Record<string, any>[];
24
  counterpartyBriefs: Record<string, any>[];
25
  completionReports: Record<string, any>[];
26
  feedbackRequests: Record<string, any>[];
27
  interactionFeedback: Record<string, any>[];
28
  interactionConclusions: Record<string, any>[];
29
  learningEligibility: Record<string, any>[];
30
  profileDeltas: Record<string, any>[];
31
  intelligenceSummaries: Record<string, any>[];
32
  runtimes: Record<string, any>[];
33
  accessTokens: Record<string, any>[];
34
  assuranceAssessments: Record<string, any>[];
35
  assurancePredictions: Record<string, any>[];
36
  assuranceSafeguards: Record<string, any>[];
37
  assuranceEvaluations: Record<string, any>[];
38
  assuranceProbePlans: Record<string, any>[];
39
  assuranceProbeResponses: Record<string, any>[];
40
};
41
42
export type Settings = {
43
  displayName: string;
44
  contributionEnabled: boolean;
45
  rawConversationsStored: false;
46
};
47
48
export const defaultPreviewSettings: Settings = {
49
  displayName: 'Local operator',
50
  contributionEnabled: false,
51
  rawConversationsStored: false,
52
};
53
54
const ago = (minutes: number) => new Date(Date.now() - minutes * 60_000).toISOString();
55
const ahead = (minutes: number) => new Date(Date.now() + minutes * 60_000).toISOString();
56
57
export function createPreviewData(): DashboardData {
58
  return {
59
    projects: [
60
      { projectId: 'proj_ops', name: 'Operations', createdAt: ago(21 * 24 * 60) },
61
      { projectId: 'proj_research', name: 'Research', createdAt: ago(12 * 24 * 60) },
62
    ],
63
    agents: [
64
      {
65
        agentId: 'agent_atlas',
66
        projectId: 'proj_research',
67
        name: 'Atlas Research',
68
        description: 'Literature review and planning',
69
        framework: 'Claude',
70
        agentVersion: '1.4.2',
71
        agentMode: 'persistent_runtime',
72
        a2aEndpoint: 'https://atlas.example.com/a2a',
73
        autoPublish: true,
74
        autoAcceptPolicy: 'safe_matching',
75
        autoAcceptTaskCategories: ['research', 'planning'],
76
        capabilities: ['research', 'planning', 'summarization'],
77
        limitations: ['no payments'],
78
        identityMode: 'oauth_installation',
79
        status: 'active',
80
        createdAt: ago(12 * 24 * 60),
81
        updatedAt: ago(8),
82
        presence: { status: 'online', lastSeenAt: ago(1) },
83
      },
84
      {
85
        agentId: 'agent_harbor',
86
        projectId: 'proj_ops',
87
        name: 'Harbor Ops',
88
        description: 'Incident triage and runbooks',
89
        framework: 'GPT',
90
        agentVersion: '2.1.0',
91
        agentMode: 'persistent_runtime',
92
        autoPublish: false,
93
        autoAcceptPolicy: 'off',
94
        autoAcceptTaskCategories: [],
95
        capabilities: ['ops', 'triage'],
96
        limitations: ['no production writes'],
97
        identityMode: 'oauth_installation',
98
        status: 'active',
99
        createdAt: ago(21 * 24 * 60),
100
        updatedAt: ago(180),
101
        presence: { status: 'offline', lastSeenAt: ago(180) },
102
      },
103
    ],
104
    installations: [
105
      {
106
        installationId: 'inst_atlas',
107
        clientId: 'mcp_atlas',
108
        agentId: 'agent_atlas',
109
        projectId: 'proj_research',
110
        connectedAt: ago(12 * 24 * 60),
111
        updatedAt: ago(8),
112
      },
113
    ],
114
    setupRequests: [
115
      {
116
        requestId: 'setup_lumen',
117
        clientId: 'mcp_lumen',
118
        action: 'connect',
119
        status: 'pending',
120
        agentName: 'Lumen Writer',
121
        projectName: 'Content',
122
        framework: 'Claude',
123
        agentVersion: '0.9.0',
124
        autoPublish: true,
125
        autoAcceptPolicy: 'safe_matching',
126
        autoAcceptTaskCategories: ['writing', 'editing'],
127
        capabilities: ['writing', 'editing', 'research'],
128
        limitations: ['no outbound email'],
129
        requestedAt: ago(18),
130
      },
131
    ],
132
    publications: [{ agentId: 'agent_atlas', published: true, updatedAt: ago(40) }],
133
    interactions: [
134
      {
135
        interactionId: 'ix_local_brief',
136
        agentId: 'agent_atlas',
137
        status: 'completed',
138
        contract: {
139
          purpose: 'Produce a source-backed research brief',
140
          taskCategory: 'research',
141
        },
142
        createdAt: ago(240),
143
        completedAt: ago(220),
144
      },
145
    ],
146
    federatedInteractions: [
147
      {
148
        interactionId: 'ix_shared_review',
149
        initiatorAgentId: 'agent_peer_nova',
150
        responderAgentId: 'agent_atlas',
151
        status: 'pending',
152
        termsHash: '7f3c91a0b2e84d11c6aa09f1d3e8b470',
153
        contractRevision: 1,
154
        createdAt: ago(26),
155
        contract: {
156
          purpose: 'Review a research brief for scope and sources',
157
          taskCategory: 'research',
158
          parties: ['agent_peer_nova', 'agent_atlas'],
159
        },
160
        contractRevisions: [
161
          {
162
            revisionId: '17c3a10b-2e84-4d11-86aa-09f1d3e8b470',
163
            revision: 1,
164
            termsHash: '7f3c91a0b2e84d11c6aa09f1d3e8b470',
165
            proposedByAgentId: 'agent_peer_nova',
166
            status: 'proposed',
167
            acceptances: { agent_peer_nova: { method: 'oauth_installation' } },
168
          },
169
        ],
170
      },
171
      {
172
        interactionId: 'ix_shared_ops',
173
        initiatorAgentId: 'agent_atlas',
174
        responderAgentId: 'agent_peer_keel',
175
        status: 'active',
176
        termsHash: '12ab44c0e91f77d0aa3310bc44ee9012',
177
        contractRevision: 1,
178
        createdAt: ago(90),
179
        contract: {
180
          purpose: 'Share a sanitized incident timeline',
181
          taskCategory: 'coordination',
182
          parties: ['agent_atlas', 'agent_peer_keel'],
183
        },
184
        acceptances: {
185
          agent_peer_keel: { method: 'policy_auto_accept' },
186
        },
187
        contractRevisions: [
188
          {
189
            revisionId: '22ab44c0-e91f-47d0-aa33-10bc44ee9012',
190
            revision: 1,
191
            termsHash: '12ab44c0e91f77d0aa3310bc44ee9012',
192
            proposedByAgentId: 'agent_atlas',
193
            status: 'accepted',
194
            acceptances: {
195
              agent_atlas: { method: 'oauth_installation' },
196
              agent_peer_keel: { method: 'policy_auto_accept' },
197
            },
198
          },
199
        ],
200
      },
201
    ],
202
    liveSessions: [
203
      {
204
        interactionId: 'ix_shared_ops',
205
        initiatorAgentId: 'agent_atlas',
206
        responderAgentId: 'agent_peer_keel',
207
        status: 'active',
208
        createdAt: ago(88),
209
        activatedAt: ago(87),
210
        expiresAt: ahead(12),
211
      },
212
    ],
213
    events: [
214
      {
215
        eventId: 'evt_1',
216
        eventType: 'interaction_started',
217
        agentId: 'agent_atlas',
218
        interactionId: 'ix_shared_ops',
219
        timestamp: ago(88),
220
        visibility: 'participants',
221
      },
222
      {
223
        eventId: 'evt_2',
224
        eventType: 'policy_warning',
225
        agentId: 'agent_harbor',
226
        interactionId: 'ix_local_brief',
227
        timestamp: ago(200),
228
        visibility: 'owner',
229
      },
230
      {
231
        eventId: 'evt_3',
232
        eventType: 'claim_checked',
233
        agentId: 'agent_atlas',
234
        interactionId: 'ix_local_brief',
235
        timestamp: ago(230),
236
        visibility: 'participants',
237
      },
238
      {
239
        eventId: 'evt_4',
240
        eventType: 'session_activated',
241
        agentId: 'agent_atlas',
242
        interactionId: 'ix_shared_ops',
243
        timestamp: ago(87),
244
        visibility: 'participants',
245
      },
246
    ],
247
    conflicts: [
248
      {
249
        conflictId: 'dsp_1',
250
        interactionId: 'ix_local_brief',
251
        status: 'open',
252
        createdAt: ago(190),
253
      },
254
    ],
255
    receipts: [
256
      {
257
        receiptId: 'rcpt_1',
258
        interactionId: 'ix_local_brief',
259
        agentId: 'agent_atlas',
260
        outcome: 'success',
261
        completedAt: ago(220),
262
      },
263
      {
264
        receiptId: 'rcpt_ops_provisional',
265
        interactionId: 'ix_shared_ops',
266
        outcome: 'partial',
267
        unilateral: true,
268
        provisional: true,
269
        confidence: 0.42,
270
        completedAt: ago(12),
271
      },
272
    ],
273
    counterpartyBriefs: [
274
      {
275
        briefId: 'brief_1',
276
        interactionId: 'ix_local_brief',
277
        recipientAgentId: 'agent_atlas',
278
        subjectAgentId: 'agent_peer_nova',
279
        decision: 'CHALLENGE',
280
        relevantSampleSize: 3,
281
        historyConfidence: 0.38,
282
        requirements: [
283
          { requirement: 'Provide source evidence', status: 'partial', confidence: 0.7 },
284
        ],
285
        generatedAt: ago(241),
286
      },
287
    ],
288
    completionReports: [
289
      {
290
        reportId: 'report_1',
291
        interactionId: 'ix_local_brief',
292
        reportingAgentId: 'agent_atlas',
293
        counterpartyAgentId: 'agent_peer_nova',
294
        outcome: 'success',
295
        summary: 'Research brief delivered with sources and scope notes.',
296
        criteria: [{ criterion: 'Brief delivered', status: 'met' }],
297
        completedAt: ago(222),
298
      },
299
      {
300
        reportId: 'report_2',
301
        interactionId: 'ix_local_brief',
302
        reportingAgentId: 'agent_peer_nova',
303
        counterpartyAgentId: 'agent_atlas',
304
        outcome: 'success',
305
        summary: 'Received and accepted the requested brief.',
306
        criteria: [{ criterion: 'Brief delivered', status: 'met' }],
307
        completedAt: ago(221),
308
      },
309
      {
310
        reportId: 'report_ops_1',
311
        interactionId: 'ix_shared_ops',
312
        reportingAgentId: 'agent_atlas',
313
        counterpartyAgentId: 'agent_peer_keel',
314
        outcome: 'partial',
315
        summary:
316
          'The requester ended after the peer stopped responding before the task was complete.',
317
        criteria: [{ criterion: 'Timeline shared', status: 'partially_met' }],
318
        confidence: 0.7,
319
        completedAt: ago(12),
320
      },
321
    ],
322
    feedbackRequests: [
323
      {
324
        requestId: 'feedback_request_1',
325
        interactionId: 'ix_local_brief',
326
        reviewerAgentId: 'agent_atlas',
327
        subjectAgentId: 'agent_peer_nova',
328
        status: 'submitted',
329
        requestedAt: ago(221),
330
        dueAt: ago(197),
331
      },
332
      {
333
        requestId: 'feedback_request_ops_1',
334
        interactionId: 'ix_shared_ops',
335
        reviewerAgentId: 'agent_atlas',
336
        subjectAgentId: 'agent_peer_keel',
337
        status: 'pending',
338
        requestedAt: ago(12),
339
        dueAt: ahead(24 * 60 - 12),
340
      },
341
      {
342
        requestId: 'feedback_request_ops_2',
343
        interactionId: 'ix_shared_ops',
344
        reviewerAgentId: 'agent_peer_keel',
345
        subjectAgentId: 'agent_atlas',
346
        status: 'pending',
347
        requestedAt: ago(12),
348
        dueAt: ahead(24 * 60 - 12),
349
      },
350
      {
351
        requestId: 'feedback_request_2',
352
        interactionId: 'ix_local_brief',
353
        reviewerAgentId: 'agent_peer_nova',
354
        subjectAgentId: 'agent_atlas',
355
        status: 'submitted',
356
        requestedAt: ago(221),
357
        dueAt: ago(197),
358
      },
359
    ],
360
    interactionFeedback: [
361
      {
362
        feedbackId: 'feedback_1',
363
        interactionId: 'ix_local_brief',
364
        reviewerAgentId: 'agent_atlas',
365
        subjectAgentId: 'agent_peer_nova',
366
        ratings: { outcome_satisfaction: 0.9, communication: 0.8, reliability: 0.9 },
367
        wouldWorkAgain: 'yes',
368
        submittedAt: ago(219),
369
      },
370
    ],
371
    interactionConclusions: [
372
      {
373
        conclusionId: 'conclusion_1',
374
        interactionId: 'ix_local_brief',
375
        outcome: 'success',
376
        consensus: 'bilateral_agreement',
377
        summary: 'Both agents reported successful delivery and acceptance.',
378
        criteria: [{ criterion: 'Brief delivered', status: 'met' }],
379
        averageRatings: { outcome_satisfaction: 0.88, communication: 0.82, reliability: 0.9 },
380
        generatedAt: ago(218),
381
      },
382
      {
383
        conclusionId: 'conclusion_ops_provisional',
384
        interactionId: 'ix_shared_ops',
385
        outcome: 'partial',
386
        consensus: 'unilateral',
387
        lifecycle: 'provisional',
388
        confidence: 0.42,
389
        missingReportAgentIds: ['agent_peer_keel'],
390
        pendingFeedbackAgentIds: ['agent_atlas', 'agent_peer_keel'],
391
        peerReportStatus: 'awaiting',
392
        summary:
393
          'Provisional one-sided outcome: the requester ended after the peer stopped responding before the task was complete.',
394
        criteria: [{ criterion: 'Timeline shared', status: 'partially_met' }],
395
        reportIds: ['report_ops_1'],
396
        averageRatings: {},
397
        generatedAt: ago(11),
398
      },
399
    ],
400
    learningEligibility: [
401
      {
402
        decisionId: 'eligibility_1',
403
        interactionId: 'ix_local_brief',
404
        eligible: true,
405
        sampleWeight: 0.87,
406
        contributionMode: 'local_only',
407
        structuredDataOnly: true,
408
        reasons: ['Bilateral reports provide outcome corroboration'],
409
        decidedAt: ago(218),
410
      },
411
    ],
412
    profileDeltas: [
413
      {
414
        deltaId: 'delta_1',
415
        interactionId: 'ix_local_brief',
416
        agentId: 'agent_atlas',
417
        agentVersion: '1.4.2',
418
        taskCategory: 'research',
419
        sampleWeight: 0.87,
420
        dimensionDeltas: { completion: 0.02, evidence: 0.03, communication: -0.01 },
421
        appliedAt: ago(218),
422
      },
423
    ],
424
    profiles: [
425
      {
426
        agentId: 'agent_atlas',
427
        taskCategory: 'research',
428
        agentVersion: '1.4.2',
429
        sampleSize: 14,
430
        effectiveSampleSize: 10.8,
431
        dimensionSampleSizes: {
432
          completion: 10.8,
433
          acceptance: 8.9,
434
          specification: 10.1,
435
          deadline: 9.4,
436
          communication: 10.8,
437
          evidence: 9.7,
438
          scope: 10.2,
439
          correction: 5.1,
440
          limitations: 4.4,
441
          disputes: 10.8,
442
        },
443
        updatedAt: ago(218),
444
        completion: 0.92,
445
        acceptance: 0.87,
446
        specification: 0.86,
447
        scope: 0.84,
448
        evidence: 0.88,
449
        communication: 0.79,
450
        deadline: 0.9,
451
        correction: 0.82,
452
        limitations: 0.77,
453
        disputes: 0.08,
454
      },
455
      {
456
        agentId: 'agent_harbor',
457
        taskCategory: 'ops',
458
        agentVersion: '2.1.0',
459
        sampleSize: 6,
460
        effectiveSampleSize: 4.1,
461
        dimensionSampleSizes: {
462
          completion: 4.1,
463
          acceptance: 3.2,
464
          specification: 3.8,
465
          deadline: 3.5,
466
          communication: 4.1,
467
          evidence: 3.9,
468
          scope: 3.7,
469
          correction: 1.8,
470
          limitations: 1.2,
471
          disputes: 4.1,
472
        },
473
        updatedAt: ago(3 * 24 * 60),
474
        completion: 0.71,
475
        acceptance: 0.68,
476
        specification: 0.63,
477
        scope: 0.66,
478
        evidence: 0.58,
479
        communication: 0.74,
480
        deadline: 0.62,
481
        correction: 0.69,
482
        limitations: 0.54,
483
        disputes: 0.22,
484
      },
485
    ],
486
    intelligenceSummaries: [
487
      {
488
        agentId: 'agent_atlas',
489
        agentVersion: '1.4.2',
490
        taskCategory: 'research',
491
        score: 0.86,
492
        confidence: {
493
          level: 'high',
494
          value: 0.72,
495
          evidenceCount: 14,
496
          effectiveSampleSize: 10.8,
497
        },
498
        trend: { direction: 'improving', delta: 0.018 },
499
        strengths: [
500
          { dimension: 'completion', score: 0.92 },
501
          { dimension: 'deadline', score: 0.9 },
502
          { dimension: 'evidence', score: 0.88 },
503
        ],
504
        risks: [],
505
        versionStatus: {
506
          currentVersion: '1.4.2',
507
          evidenceVersion: '1.4.2',
508
          status: 'current',
509
        },
510
        source: 'private_verified_history',
511
        updatedAt: ago(218),
512
      },
513
      {
514
        agentId: 'agent_harbor',
515
        agentVersion: '2.1.0',
516
        taskCategory: 'ops',
517
        score: 0.66,
518
        confidence: {
519
          level: 'medium',
520
          value: 0.49,
521
          evidenceCount: 6,
522
          effectiveSampleSize: 4.1,
523
        },
524
        trend: { direction: 'declining', delta: -0.022 },
525
        strengths: [{ dimension: 'communication', score: 0.74 }],
526
        risks: [
527
          {
528
            dimension: 'evidence',
529
            score: 0.58,
530
            reason: 'Claims are not consistently supported by evidence.',
531
          },
532
        ],
533
        versionStatus: {
534
          currentVersion: '2.1.0',
535
          evidenceVersion: '2.1.0',
536
          status: 'current',
537
        },
538
        source: 'private_verified_history',
539
        updatedAt: ago(3 * 24 * 60),
540
      },
541
    ],
542
    runtimes: [
543
      {
544
        agentId: 'agent_atlas',
545
        endpoint: 'https://atlas.example.com/openclasp',
546
        a2aEndpoint: 'https://atlas.example.com/a2a',
547
        status: 'verified',
548
        verifiedAt: ago(40),
549
      },
550
    ],
551
    accessTokens: [],
552
    assuranceAssessments: [
553
      {
554
        assessmentId: '11111111-1111-4111-8111-111111111111',
555
        interactionId: 'ix_shared_ops',
556
        phase: 'pre_task',
557
        round: 1,
558
        generatedForAgentId: 'agent_atlas',
559
        targetAgentId: 'agent_peer_keel',
560
        targetAgentVersion: '1.2.0',
561
        selectedProbeId: '22222222-2222-4222-8222-222222222222',
562
        risks: [
563
          {
564
            riskCode: 'evidence_delivery',
565
            title: 'Evidence delivery is unproven',
566
            likelihood: 0.42,
567
            impact: 0.76,
568
          },
569
          {
570
            riskCode: 'tool_dependency',
571
            title: 'Required tool access may be unavailable',
572
            likelihood: 0.31,
573
            impact: 0.68,
574
          },
575
        ],
576
        generation: {
577
          mode: 'ai',
578
          model: 'anthropic/claude-sonnet-5',
579
          promptVersion: 'assurance-decision-v2',
580
        },
581
        createdAt: ago(84),
582
      },
583
    ],
584
    assurancePredictions: [
585
      {
586
        predictionId: '33333333-3333-4333-8333-333333333333',
587
        interactionId: 'ix_shared_ops',
588
        targetAgentId: 'agent_peer_keel',
589
        targetAgentVersion: '1.2.0',
590
        stage: 'baseline',
591
        successProbability: 0.64,
592
        confidence: 0.42,
593
        basis: 'cold_start_hybrid',
594
        sampleSize: 0,
595
        createdAt: ago(84),
596
      },
597
    ],
598
    assuranceSafeguards: [
599
      {
600
        safeguardId: '44444444-4444-4444-8444-444444444444',
601
        assessmentId: '11111111-1111-4111-8111-111111111111',
602
        interactionId: 'ix_shared_ops',
603
        safeguardCode: 'require_evidence',
604
        status: 'recommended',
605
        description: 'Require evidence references for every material claim.',
606
        rationale: 'The agreement requires an inspectable sanitized incident timeline.',
607
        expectedImpact: 0.1,
608
        createdAt: ago(84),
609
      },
610
    ],
611
    assuranceEvaluations: [],
612
    assuranceProbePlans: [
613
      {
614
        planId: '55555555-5555-4555-8555-555555555555',
615
        assessmentId: '11111111-1111-4111-8111-111111111111',
616
        interactionId: 'ix_shared_ops',
617
        round: 1,
618
        questions: [
619
          {
620
            probeId: '22222222-2222-4222-8222-222222222222',
621
            questionCode: 'evidence_capability',
622
            prompt: 'Can you return inspectable evidence for every material claim?',
623
          },
624
        ],
625
        generatedAt: ago(84),
626
      },
627
    ],
628
    assuranceProbeResponses: [],
629
  };
630
}
631
632
export function applyPreviewRequest(
633
  data: DashboardData,
634
  settings: Settings,
635
  path: string,
636
  init?: RequestInit,
637
): { data: DashboardData; settings: Settings; result: unknown } {
638
  const method = (init?.method ?? 'GET').toUpperCase();
639
  const body = init?.body ? (JSON.parse(String(init.body)) as Record<string, unknown>) : {};
640
641
  if (path === '/v0.1/dashboard' && method === 'GET') return { data, settings, result: data };
642
  if (path === '/v0.1/settings' && method === 'GET') return { data, settings, result: settings };
643
  if (path === '/v0.1/settings' && method === 'PUT') {
644
    const next = {
645
      ...settings,
646
      ...body,
647
      rawConversationsStored: false as const,
648
    } as Settings;
649
    return { data, settings: next, result: next };
650
  }
651
652
  if (path === '/v0.1/provider-connections/botpress' && method === 'POST') {
653
    const now = new Date();
654
    return {
655
      data,
656
      settings,
657
      result: {
658
        connectionId: crypto.randomUUID(),
659
        provider: 'botpress',
660
        agentName: String(body.agentName ?? 'Botpress agent'),
661
        status: 'pending',
662
        code: 'oc_bp_preview_pairing_code',
663
        createdAt: now.toISOString(),
664
        expiresAt: new Date(now.getTime() + 15 * 60_000).toISOString(),
665
      },
666
    };
667
  }
668
669
  if (path === '/v0.1/provider-connections' && method === 'POST') {
670
    const suffix = crypto.randomUUID();
671
    const projectName = String(body.projectName ?? 'Hosted agents');
672
    const existingProject = data.projects.find(
673
      (project) => String(project.name).toLowerCase() === projectName.toLowerCase(),
674
    );
675
    const project = existingProject ?? {
676
      projectId: `project_${suffix}`,
677
      name: projectName,
678
      createdAt: new Date().toISOString(),
679
    };
680
    const agentId = `agent_${suffix}`;
681
    const createdAt = new Date();
682
    const tokenId = suffix.replaceAll('-', '').slice(0, 16);
683
    const provider = body.provider === 'custom' ? 'custom' : 'botpress';
684
    const agent = {
685
      agentId,
686
      projectId: project.projectId,
687
      name: String(body.agentName ?? 'Botpress agent'),
688
      description: String(body.description ?? ''),
689
      framework: provider === 'botpress' ? 'Botpress' : 'Custom runtime',
690
      agentVersion: '1.0.0',
691
      agentMode: 'persistent_runtime',
692
      transport: 'direct_a2a',
693
      autoPublish: false,
694
      autoAcceptPolicy: 'off',
695
      autoAcceptTaskCategories: [],
696
      capabilities: body.capabilities ?? [],
697
      limitations: body.limitations ?? [],
698
      identityMode: 'owner_managed',
699
      status: 'active',
700
      createdAt: createdAt.toISOString(),
701
      updatedAt: createdAt.toISOString(),
702
      presence: { status: 'offline' },
703
    };
704
    const accessToken = {
705
      tokenId,
706
      token: `oc_at_${tokenId}.preview_agent_access_token_secret_not_for_production`,
707
      agentId,
708
      name: provider === 'botpress' ? 'Botpress' : 'Custom runtime',
709
      scopes: ['mcp:access', 'runtime:connect'],
710
      createdAt: createdAt.toISOString(),
711
      expiresAt: new Date(
712
        createdAt.getTime() + Number(body.expiresInDays ?? 365) * 86_400_000,
713
      ).toISOString(),
714
    };
715
    return {
716
      data: {
717
        ...data,
718
        projects: existingProject ? data.projects : [...data.projects, project],
719
        agents: [...data.agents, agent],
720
        accessTokens: [accessToken, ...data.accessTokens],
721
      },
722
      settings,
723
      result: { agent, project, provider, accessToken },
724
    };
725
  }
726
727
  const automation = path.match(/^\/v0\.1\/agents\/([^/]+)\/automation$/);
728
  if (automation && method === 'PUT') {
729
    const agentId = decodeURIComponent(automation[1] ?? '');
730
    const next = {
731
      ...data,
732
      agents: data.agents.map((agent) =>
733
        agent.agentId === agentId
734
          ? {
735
              ...agent,
736
              autoPublish: Boolean(body.autoPublish),
737
              autoAcceptPolicy: body.autoAcceptPolicy,
738
              autoAcceptTaskCategories: body.autoAcceptTaskCategories,
739
            }
740
          : agent,
741
      ),
742
      publications: body.autoPublish
743
        ? [
744
            ...data.publications.filter((item) => item.agentId !== agentId),
745
            { agentId, published: true, updatedAt: new Date().toISOString() },
746
          ]
747
        : data.publications.filter((item) => item.agentId !== agentId),
748
    };
749
    return { data: next, settings, result: { ok: true } };
750
  }
751
752
  const runtime = path.match(/^\/v0\.1\/agents\/([^/]+)\/runtime$/);
753
  if (runtime && method === 'PUT') {
754
    const agentId = decodeURIComponent(runtime[1] ?? '');
755
    const endpoint = String(body.endpoint ?? '');
756
    const record = {
757
      agentId,
758
      endpoint,
759
      a2aEndpoint: endpoint,
760
      status: 'verified',
761
      verifiedAt: new Date().toISOString(),
762
    };
763
    return {
764
      data: {
765
        ...data,
766
        runtimes: [...data.runtimes.filter((item) => item.agentId !== agentId), record],
767
      },
768
      settings,
769
      result: record,
770
    };
771
  }
772
  const tokenCollection = path.match(/^\/v0\.1\/agents\/([^/]+)\/access-tokens$/);
773
  if (tokenCollection && method === 'POST') {
774
    const agentId = decodeURIComponent(tokenCollection[1] ?? '');
775
    const tokenId = crypto.randomUUID().replaceAll('-', '').slice(0, 16);
776
    const createdAt = new Date();
777
    const expiresInDays = Number(body.expiresInDays ?? 365);
778
    const record = {
779
      tokenId,
780
      agentId,
781
      name: String(body.name ?? 'Hosted provider'),
782
      scopes: ['mcp:access', 'runtime:connect'],
783
      createdAt: createdAt.toISOString(),
784
      expiresAt: new Date(createdAt.getTime() + expiresInDays * 86_400_000).toISOString(),
785
    };
786
    return {
787
      data: { ...data, accessTokens: [record, ...data.accessTokens] },
788
      settings,
789
      result: {
790
        ...record,
791
        token: `oc_at_${tokenId}.preview_agent_access_token_secret_not_for_production`,
792
      },
793
    };
794
  }
795
  const tokenItem = path.match(/^\/v0\.1\/agents\/([^/]+)\/access-tokens\/([^/]+)$/);
796
  if (tokenItem && method === 'DELETE') {
797
    const tokenId = decodeURIComponent(tokenItem[2] ?? '');
798
    return {
799
      data: {
800
        ...data,
801
        accessTokens: data.accessTokens.map((token) =>
802
          token.tokenId === tokenId ? { ...token, revokedAt: new Date().toISOString() } : token,
803
        ),
804
      },
805
      settings,
806
      result: { tokenId, revokedAt: new Date().toISOString() },
807
    };
808
  }
809
  const agentDelete = path.match(/^\/v0\.1\/agents\/([^/]+)$/);
810
  if (agentDelete && method === 'DELETE') {
811
    const agentId = decodeURIComponent(agentDelete[1] ?? '');
812
    return {
813
      data: {
814
        ...data,
815
        agents: data.agents.filter((item) => item.agentId !== agentId),
816
        publications: data.publications.filter((item) => item.agentId !== agentId),
817
        runtimes: data.runtimes.filter((item) => item.agentId !== agentId),
818
        installations: data.installations.filter((item) => item.agentId !== agentId),
819
        accessTokens: data.accessTokens.filter((item) => item.agentId !== agentId),
820
      },
821
      settings,
822
      result: { agentId, deleted: true },
823
    };
824
  }
825
826
  if (runtime && method === 'DELETE') {
827
    const agentId = decodeURIComponent(runtime[1] ?? '');
828
    return {
829
      data: {
830
        ...data,
831
        runtimes: data.runtimes.filter((item) => item.agentId !== agentId),
832
      },
833
      settings,
834
      result: { agentId, status: 'disabled' },
835
    };
836
  }
837
838
  const federated = path.match(/^\/v0\.1\/federated-interactions\/([^/]+)\/respond$/);
839
  if (federated && method === 'POST') {
840
    const interactionId = decodeURIComponent(federated[1] ?? '');
841
    const decision = body.decision === 'accept' ? 'active' : 'rejected';
842
    return {
843
      data: {
844
        ...data,
845
        federatedInteractions: data.federatedInteractions.map((item) =>
846
          item.interactionId === interactionId ? { ...item, status: decision } : item,
847
        ),
848
      },
849
      settings,
850
      result: { ok: true },
851
    };
852
  }
853
854
  const safeguardDecision = path.match(
855
    /^\/v0\.1\/federated-interactions\/([^/]+)\/assurance-safeguards\/([^/]+)\/decision$/,
856
  );
857
  if (safeguardDecision && method === 'POST') {
858
    const safeguardId = decodeURIComponent(safeguardDecision[2] ?? '');
859
    const status = body.status === 'accepted' ? 'accepted' : 'rejected';
860
    return {
861
      data: {
862
        ...data,
863
        assuranceSafeguards: data.assuranceSafeguards.map((item) =>
864
          item.safeguardId === safeguardId
865
            ? { ...item, status, decidedAt: new Date().toISOString() }
866
            : item,
867
        ),
868
      },
869
      settings,
870
      result: { safeguard: { safeguardId, status } },
871
    };
872
  }
873
874
  const onboarding = path.match(/^\/v0\.1\/onboarding\/([^/]+)\/(approve|reject)$/);
875
  if (onboarding && method === 'POST') {
876
    const requestId = decodeURIComponent(onboarding[1] ?? '');
877
    const decision = onboarding[2] === 'approve' ? 'approved' : 'rejected';
878
    return {
879
      data: {
880
        ...data,
881
        setupRequests: data.setupRequests.map((item) =>
882
          item.requestId === requestId
883
            ? { ...item, status: decision, decidedAt: new Date().toISOString() }
884
            : item,
885
        ),
886
      },
887
      settings,
888
      result: { ok: true },
889
    };
890
  }
891
892
  throw new Error('Preview cannot perform this action');
893
}
894
895
export function isPreviewActive() {
896
  return (
897
    import.meta.env.DEV &&
898
    (sessionStorage.getItem(previewKey) === '1' ||
899
      new URLSearchParams(location.search).get('preview') === '1')
900
  );
901
}
902
903
export function enablePreview() {
904
  sessionStorage.setItem(previewKey, '1');
905
}
906
907
export function disablePreview() {
908
  sessionStorage.removeItem(previewKey);
909
}