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

Viewing files at commit 89b58109a0e6Commit detailsView latest

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