@agent-native/toolkit 0.198.2 → 0.198.3-nightly-20260930124743

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  2. package/dist/app/agentkit/react/components.js +4 -1
  3. package/dist/app/agentkit/react/components.js.map +1 -1
  4. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  5. package/dist/app/chat/AgentKitAssistantChat.js +20 -6
  6. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  7. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  8. package/dist/app/i18n/catalogs/ar-SA.js +211 -0
  9. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  10. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  11. package/dist/app/i18n/catalogs/de-DE.js +147 -0
  12. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  13. package/dist/app/i18n/catalogs/en-US.d.ts +147 -0
  14. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  15. package/dist/app/i18n/catalogs/en-US.js +147 -0
  16. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  17. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  18. package/dist/app/i18n/catalogs/es-ES.js +163 -0
  19. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  20. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  21. package/dist/app/i18n/catalogs/fr-FR.js +163 -0
  22. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  23. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  24. package/dist/app/i18n/catalogs/hi-IN.js +147 -0
  25. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  26. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  27. package/dist/app/i18n/catalogs/ja-JP.js +131 -0
  28. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  29. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  30. package/dist/app/i18n/catalogs/ko-KR.js +131 -0
  31. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  32. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/pt-BR.js +163 -0
  34. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  35. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  36. package/dist/app/i18n/catalogs/zh-CN.js +131 -0
  37. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  38. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  39. package/dist/app/i18n/catalogs/zh-TW.js +131 -0
  40. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  41. package/dist/app/observability/ObservabilityDashboard.d.ts.map +1 -1
  42. package/dist/app/observability/ObservabilityDashboard.js +101 -109
  43. package/dist/app/observability/ObservabilityDashboard.js.map +1 -1
  44. package/dist/app/observability/RunInsights.d.ts +22 -0
  45. package/dist/app/observability/RunInsights.d.ts.map +1 -0
  46. package/dist/app/observability/RunInsights.js +658 -0
  47. package/dist/app/observability/RunInsights.js.map +1 -0
  48. package/package.json +5 -5
  49. package/src/app/agent-page/ConnectionsTab.spec.tsx +0 -3
  50. package/src/app/agentkit/react/components.tsx +4 -1
  51. package/src/app/agentkit/react/composer-context.spec.tsx +4 -1
  52. package/src/app/chat/AgentKitAssistantChat.spec.tsx +57 -0
  53. package/src/app/chat/AgentKitAssistantChat.tsx +44 -8
  54. package/src/app/chat/AgentPanel.header.spec.ts +22 -22
  55. package/src/app/chat/agentkit-chat/action-widget.spec.tsx +17 -2
  56. package/src/app/chat/agentkit-chat/approval-card.spec.tsx +2 -2
  57. package/src/app/chat/agentkit-chat/connections.spec.tsx +1 -1
  58. package/src/app/chat/chat/markdown-renderer.spec.tsx +1 -1
  59. package/src/app/chat/chat/tool-call-display.spec.tsx +25 -2
  60. package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +38 -26
  61. package/src/app/chat/chat/widgets/agent-team-progress-widget.spec.tsx +7 -1
  62. package/src/app/chat/chat/widgets/builtin-connect-required-renderer.spec.tsx +24 -1
  63. package/src/app/chat/chat/widgets/record-change-widget.spec.tsx +16 -1
  64. package/src/app/chat/thinking-display.spec.tsx +1 -1
  65. package/src/app/db-admin/useAgentSync.spec.tsx +1 -1
  66. package/src/app/extensions/ExtensionViewer.spec.tsx +2 -1
  67. package/src/app/i18n/catalogs/ar-SA.ts +282 -0
  68. package/src/app/i18n/catalogs/de-DE.ts +198 -0
  69. package/src/app/i18n/catalogs/en-US.ts +187 -0
  70. package/src/app/i18n/catalogs/es-ES.ts +221 -0
  71. package/src/app/i18n/catalogs/fr-FR.ts +219 -0
  72. package/src/app/i18n/catalogs/hi-IN.ts +182 -0
  73. package/src/app/i18n/catalogs/ja-JP.ts +160 -0
  74. package/src/app/i18n/catalogs/ko-KR.ts +160 -0
  75. package/src/app/i18n/catalogs/pt-BR.ts +220 -0
  76. package/src/app/i18n/catalogs/zh-CN.ts +154 -0
  77. package/src/app/i18n/catalogs/zh-TW.ts +154 -0
  78. package/src/app/integrations/BuilderIntegrationPage.spec.tsx +3 -3
  79. package/src/app/integrations/ChannelsPage.spec.tsx +3 -3
  80. package/src/app/integrations/IntegrationsPage.spec.tsx +3 -3
  81. package/src/app/integrations/IntegrationsPanel.spec.tsx +7 -4
  82. package/src/app/observability/ObservabilityDashboard.review.spec.tsx +211 -52
  83. package/src/app/observability/ObservabilityDashboard.spec.tsx +80 -20
  84. package/src/app/observability/ObservabilityDashboard.tsx +240 -400
  85. package/src/app/observability/RunInsights.tsx +1603 -0
  86. package/src/app/observability/ThumbsFeedback.spec.tsx +29 -12
  87. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +25 -5
  88. package/src/app/onboarding/first-run-startup-gate.spec.tsx +12 -7
  89. package/src/app/resources/McpConnectionSuggestion.render.spec.tsx +43 -5
  90. package/src/app/resources/McpIntegrationDialog.spec.tsx +20 -14
  91. package/src/app/resources/ResourcesPanel.spec.ts +9 -7
  92. package/src/app/review/ReviewThreadPanel.spec.tsx +2 -2
  93. package/src/app/settings/SecretsSection.spec.tsx +12 -3
  94. package/src/app/settings/SettingsTabsPage.spec.tsx +4 -1
  95. package/src/app/settings/VoiceTranscriptionSection.spec.tsx +12 -1
  96. package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -1
  97. package/src/app/settings/account/ProfileSettings.spec.tsx +13 -1
  98. package/src/app/settings/account/SecuritySettings.spec.tsx +12 -1
  99. package/src/app/settings/shell/SettingsShell.spec.tsx +35 -15
  100. package/src/app/settings/shell/pages/app-group-pages.spec.tsx +7 -0
  101. package/src/app/settings/shell/pages/audit.spec.tsx +5 -2
  102. package/src/app/settings/shell/pages/resource-pages.spec.tsx +25 -16
  103. package/src/app/settings/shell/pages/sub-agents.spec.tsx +24 -15
  104. package/src/app/settings/usage/UsagePage.spec.tsx +4 -0
  105. package/src/app/setup-connections/BuilderConnectCard.spec.tsx +27 -8
  106. package/src/app/sharing/ShareButton.spec.tsx +7 -3
  107. package/src/app/sharing/ShareDialog.spec.tsx +4 -4
  108. package/src/app/sharing/useShareButtonController.spec.tsx +1 -1
  109. package/src/app/sharing/useShareDialogController.spec.tsx +2 -2
  110. package/src/app/transcription/BuilderTranscriptionCta.spec.tsx +1 -1
@@ -22,7 +22,7 @@ vi.mock("../resources/McpIntegrationDialog.js", () => ({
22
22
  McpIntegrationDialog: () => null,
23
23
  }));
24
24
 
25
- vi.mock("../resources/mcp-integration-catalog.js", () => ({
25
+ vi.mock("@agent-native/core/client/resources/mcp-integration-catalog", () => ({
26
26
  isMcpIntegrationCatalogAvailable: () => false,
27
27
  getDefaultMcpIntegrations: () => [
28
28
  {
@@ -54,11 +54,14 @@ vi.mock("../resources/mcp-integration-catalog.js", () => ({
54
54
  ],
55
55
  }));
56
56
 
57
- vi.mock("../resources/use-mcp-servers.js", () => mcpMocks);
57
+ vi.mock("@agent-native/core/client/resources/use-mcp-servers", () => mcpMocks);
58
58
 
59
- vi.mock("./useIntegrationStatus.js", () => integrationMocks);
59
+ vi.mock(
60
+ "@agent-native/core/client/integrations/useIntegrationStatus",
61
+ () => integrationMocks,
62
+ );
60
63
 
61
- vi.mock("../i18n.js", () => ({
64
+ vi.mock("@agent-native/core/client/i18n", () => ({
62
65
  useT: () => (key: string, options?: Record<string, unknown>) => {
63
66
  const messages: Record<string, string> = {
64
67
  "mcpIntegrations.connectionError": "Connection error",
@@ -12,7 +12,6 @@ const {
12
12
  mockSubmitFeedback,
13
13
  mockSaveInstructionUpdate,
14
14
  mockSendToAgentChat,
15
- mockTraces,
16
15
  mockTraceDetail,
17
16
  mockOpenThread,
18
17
  mockUseActionQuery,
@@ -24,7 +23,6 @@ const {
24
23
  mockSubmitFeedback: vi.fn(),
25
24
  mockSaveInstructionUpdate: vi.fn(),
26
25
  mockSendToAgentChat: vi.fn(),
27
- mockTraces: vi.fn(),
28
26
  mockTraceDetail: vi.fn(),
29
27
  mockOpenThread: vi.fn(),
30
28
  mockUseActionQuery: vi.fn(),
@@ -76,7 +74,6 @@ vi.mock("./useObservability.js", () => ({
76
74
  },
77
75
  isLoading: false,
78
76
  }),
79
- useTraces: (...args: unknown[]) => mockTraces(...args),
80
77
  useTraceDetail: (...args: unknown[]) => mockTraceDetail(...args),
81
78
  usePromoteTraceEval: () => ({
82
79
  mutate: vi.fn(),
@@ -125,12 +122,15 @@ vi.mock("./useObservability.js", () => ({
125
122
 
126
123
  import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
127
124
 
125
+ import { createToolkitI18nCatalog } from "../i18n.js";
128
126
  import {
129
127
  ObservabilityDashboard,
130
128
  resolveReviewArtifactHref,
131
129
  resolveReviewArtifactOpenHref,
132
130
  } from "./ObservabilityDashboard.js";
133
131
 
132
+ const toolkitI18nCatalog = createToolkitI18nCatalog({ messages: {} });
133
+
134
134
  describe("human review artifact links", () => {
135
135
  it("keeps artifact links on the matching first-party app and environment", () => {
136
136
  expect(
@@ -442,50 +442,84 @@ describe("ObservabilityDashboard human review", () => {
442
442
  vi.unstubAllGlobals();
443
443
  });
444
444
 
445
- it("opens span details, the full conversation, and tab documentation", async () => {
446
- mockTraces.mockReturnValue({
447
- isLoading: false,
448
- data: [
445
+ it("explains a prompt in plain language and still exposes its raw spans and conversation", async () => {
446
+ const run = {
447
+ runId: "run-1",
448
+ threadId: "thread-1",
449
+ createdAt: Date.now(),
450
+ ownerEmail: "owner@example.com",
451
+ label: "chat",
452
+ model: "test-model",
453
+ prompt: "Find the latest releases",
454
+ status: "success",
455
+ tokens: {
456
+ inputTokens: 2,
457
+ outputTokens: 3,
458
+ cacheReadTokens: 0,
459
+ cacheWriteTokens: 0,
460
+ },
461
+ cost: {
462
+ cacheReadCents: 0,
463
+ cacheWriteCents: 0,
464
+ uncachedInputCents: 1,
465
+ outputCents: 1,
466
+ totalCents: 2,
467
+ estimatedCents: 2,
468
+ noCacheCents: 2,
469
+ },
470
+ modelCalls: 1,
471
+ tools: [
472
+ { name: "search", calls: 1, failed: 0, error: null },
449
473
  {
450
- runId: "run-1",
451
- threadId: "thread-1",
452
- totalSpans: 2,
453
- llmCalls: 1,
454
- toolCalls: 1,
455
- successfulTools: 1,
456
- failedTools: 0,
457
- totalDurationMs: 100,
458
- totalCostCentsX100: 0,
459
- totalInputTokens: 2,
460
- totalOutputTokens: 3,
461
- model: "test-model",
462
- createdAt: Date.now(),
474
+ name: "broken-search",
475
+ calls: 1,
476
+ failed: 1,
477
+ error: "Search provider returned 503",
463
478
  },
464
479
  ],
465
- });
480
+ restarts: {
481
+ count: 0,
482
+ cents: 0,
483
+ byCause: {
484
+ "tool-lookup": { count: 0, cents: 0 },
485
+ "prefix-changed": { count: 0, cents: 0 },
486
+ },
487
+ },
488
+ parallel: { calls: 0, savedMs: 0 },
489
+ recoveredErrors: 1,
490
+ durationMs: 100,
491
+ feedback: null,
492
+ };
493
+ mockUseActionQuery.mockImplementation((actionName) =>
494
+ actionName === "get-usage-insights"
495
+ ? {
496
+ data: {
497
+ sinceDays: 7,
498
+ current: { runs: 1, tokens: run.tokens, cost: run.cost },
499
+ previous: { runs: 0, tokens: run.tokens, cost: run.cost },
500
+ runs: [run],
501
+ },
502
+ }
503
+ : actionName === "get-usage-run"
504
+ ? {
505
+ isLoading: false,
506
+ data: {
507
+ ...run,
508
+ reply: "Here are the three latest releases.",
509
+ turns: [],
510
+ scores: [],
511
+ },
512
+ }
513
+ : { data: undefined, isError: false, isSuccess: false },
514
+ );
466
515
  mockTraceDetail.mockReturnValue({
467
516
  isLoading: false,
468
517
  data: {
469
- summary: {
470
- runId: "run-1",
471
- threadId: "thread-1",
472
- totalSpans: 2,
473
- llmCalls: 1,
474
- toolCalls: 1,
475
- successfulTools: 1,
476
- failedTools: 0,
477
- totalDurationMs: 100,
478
- totalCostCentsX100: 0,
479
- totalInputTokens: 2,
480
- totalOutputTokens: 3,
481
- model: "test-model",
482
- createdAt: Date.now(),
483
- },
518
+ summary: { runId: "run-1", threadId: "thread-1" },
484
519
  spans: [
485
520
  {
486
521
  id: "span-success",
487
522
  runId: "run-1",
488
- orgId: "org-a",
489
523
  threadId: "thread-1",
490
524
  parentSpanId: null,
491
525
  spanType: "tool_call",
@@ -529,13 +563,21 @@ describe("ObservabilityDashboard human review", () => {
529
563
  await act(async () => {
530
564
  root.render(
531
565
  <QueryClientProvider client={queryClient}>
532
- <AgentNativeI18nProvider persistPreference={false}>
566
+ <AgentNativeI18nProvider
567
+ catalog={toolkitI18nCatalog}
568
+ persistPreference={false}
569
+ >
533
570
  <ObservabilityDashboard />
534
571
  </AgentNativeI18nProvider>
535
572
  </QueryClientProvider>,
536
573
  );
537
574
  });
538
575
 
576
+ const buttonWithText = (text: string) =>
577
+ Array.from(document.querySelectorAll<HTMLButtonElement>("button")).find(
578
+ (button) => button.textContent?.includes(text),
579
+ );
580
+
539
581
  const experimentsTab = Array.from(
540
582
  container.querySelectorAll<HTMLButtonElement>("button"),
541
583
  ).find((button) => button.textContent?.includes("Experiments"));
@@ -552,33 +594,150 @@ describe("ObservabilityDashboard human review", () => {
552
594
  container.querySelector<HTMLAnchorElement>('a[href*="#conversations"]'),
553
595
  ).toBeTruthy();
554
596
 
555
- const runRow = Array.from(container.querySelectorAll("tr")).find((row) =>
556
- row.textContent?.includes("run-1"),
597
+ await act(async () => buttonWithText("Find the latest releases")?.click());
598
+ expect(document.body.textContent).toContain(
599
+ "Here are the three latest releases.",
600
+ );
601
+ expect(document.body.textContent).toContain(
602
+ "but the agent kept going and finished",
557
603
  );
558
- await act(async () => (runRow as HTMLTableRowElement | undefined)?.click());
559
604
 
605
+ await act(async () => buttonWithText("Raw trace")?.click());
560
606
  const detailsButton = (spanName: string) =>
561
607
  Array.from(
562
- container.querySelectorAll<HTMLButtonElement>(
608
+ document.querySelectorAll<HTMLButtonElement>(
563
609
  'button[aria-label="View details"]',
564
610
  ),
565
611
  ).find((button) => button.closest("tr")?.textContent?.includes(spanName));
566
612
 
567
613
  await act(async () => detailsButton("search")?.click());
568
- expect(container.textContent).toContain('"latest releases"');
569
- expect(container.textContent).toContain("Found 3 results");
614
+ expect(document.body.textContent).toContain('"latest releases"');
615
+ expect(document.body.textContent).toContain("Found 3 results");
570
616
 
571
617
  await act(async () => detailsButton("broken-search")?.click());
572
- expect(container.textContent).toContain("Search provider returned 503");
618
+ expect(document.body.textContent).toContain("Search provider returned 503");
573
619
 
574
- await act(async () =>
575
- Array.from(container.querySelectorAll<HTMLButtonElement>("button"))
576
- .find((button) =>
577
- button.textContent?.includes("Open full conversation"),
578
- )
579
- ?.click(),
580
- );
620
+ await act(async () => buttonWithText("Open full conversation")?.click());
581
621
  expect(mockOpenThread).toHaveBeenCalledWith({ threadId: "thread-1" });
622
+ expect(buttonWithText("Open full conversation")).toBeUndefined();
623
+ });
624
+
625
+ it("reports a failed insights load with a retry instead of loading forever", async () => {
626
+ const refetch = vi.fn();
627
+ mockUseActionQuery.mockImplementation((actionName) =>
628
+ actionName === "get-usage-insights"
629
+ ? {
630
+ data: undefined,
631
+ isError: true,
632
+ isSuccess: false,
633
+ isFetching: false,
634
+ refetch,
635
+ }
636
+ : { data: undefined, isError: false, isSuccess: false },
637
+ );
638
+
639
+ await act(async () => {
640
+ root.render(
641
+ <QueryClientProvider client={queryClient}>
642
+ <AgentNativeI18nProvider
643
+ catalog={toolkitI18nCatalog}
644
+ persistPreference={false}
645
+ >
646
+ <ObservabilityDashboard />
647
+ </AgentNativeI18nProvider>
648
+ </QueryClientProvider>,
649
+ );
650
+ });
651
+ expect(container.querySelector('[role="alert"]')?.textContent).toBe(
652
+ "Couldn't load this. Please try again.",
653
+ );
654
+
655
+ const buttonWithText = (text: string) =>
656
+ Array.from(container.querySelectorAll("button")).find((button) =>
657
+ button.textContent?.includes(text),
658
+ );
659
+ await act(async () => buttonWithText("Retry")?.click());
660
+ expect(refetch).toHaveBeenCalledTimes(1);
661
+
662
+ await act(async () => activateTab(buttonWithText("Conversations")));
663
+ expect(container.querySelector('[role="alert"]')?.textContent).toBe(
664
+ "Couldn't load this. Please try again.",
665
+ );
666
+ expect(buttonWithText("Retry")).toBeTruthy();
667
+ });
668
+
669
+ it("says the overview stats cover only the latest prompts when the period has more", async () => {
670
+ const tokens = {
671
+ inputTokens: 2,
672
+ outputTokens: 3,
673
+ cacheReadTokens: 0,
674
+ cacheWriteTokens: 0,
675
+ };
676
+ const cost = {
677
+ cacheReadCents: 0,
678
+ cacheWriteCents: 0,
679
+ uncachedInputCents: 1,
680
+ outputCents: 1,
681
+ totalCents: 2,
682
+ estimatedCents: 2,
683
+ noCacheCents: 2,
684
+ };
685
+ mockUseActionQuery.mockImplementation((actionName) =>
686
+ actionName === "get-usage-insights"
687
+ ? {
688
+ data: {
689
+ sinceDays: 7,
690
+ current: { runs: 30, tokens, cost },
691
+ previous: { runs: 0, tokens, cost },
692
+ runs: [
693
+ {
694
+ runId: "run-1",
695
+ threadId: "thread-1",
696
+ createdAt: Date.now(),
697
+ ownerEmail: "owner@example.com",
698
+ label: "chat",
699
+ model: "test-model",
700
+ prompt: "Find the latest releases",
701
+ status: "success",
702
+ tokens,
703
+ cost,
704
+ modelCalls: 1,
705
+ tools: [],
706
+ restarts: {
707
+ count: 0,
708
+ cents: 0,
709
+ byCause: {
710
+ "tool-lookup": { count: 0, cents: 0 },
711
+ "prefix-changed": { count: 0, cents: 0 },
712
+ },
713
+ },
714
+ parallel: { calls: 0, savedMs: 0 },
715
+ recoveredErrors: 0,
716
+ durationMs: 100,
717
+ feedback: null,
718
+ },
719
+ ],
720
+ },
721
+ }
722
+ : { data: undefined, isError: false, isSuccess: false },
723
+ );
724
+
725
+ await act(async () => {
726
+ root.render(
727
+ <QueryClientProvider client={queryClient}>
728
+ <AgentNativeI18nProvider
729
+ catalog={toolkitI18nCatalog}
730
+ persistPreference={false}
731
+ >
732
+ <ObservabilityDashboard />
733
+ </AgentNativeI18nProvider>
734
+ </QueryClientProvider>,
735
+ );
736
+ });
737
+
738
+ expect(container.textContent).toContain(
739
+ "Completion, typical time, what Agent-Native handled and the findings use the latest 1 of 30 prompts.",
740
+ );
582
741
  });
583
742
 
584
743
  it("hides organization human review outside the admin settings surface", async () => {
@@ -24,6 +24,46 @@ const TRACE = {
24
24
  createdAt: Date.now(),
25
25
  };
26
26
 
27
+ const RUN = {
28
+ runId: TRACE.runId,
29
+ threadId: TRACE.threadId,
30
+ createdAt: TRACE.createdAt,
31
+ ownerEmail: "owner@example.com",
32
+ label: "chat",
33
+ model: TRACE.model,
34
+ prompt: "Say hello",
35
+ status: "success",
36
+ tokens: {
37
+ inputTokens: 8,
38
+ outputTokens: 4,
39
+ cacheReadTokens: 0,
40
+ cacheWriteTokens: 0,
41
+ },
42
+ cost: {
43
+ cacheReadCents: 0,
44
+ cacheWriteCents: 0,
45
+ uncachedInputCents: 0.05,
46
+ outputCents: 0.05,
47
+ totalCents: 0.1,
48
+ estimatedCents: 0.1,
49
+ noCacheCents: 0.1,
50
+ },
51
+ modelCalls: 1,
52
+ tools: [{ name: "search-docs", calls: 1, failed: 0, error: null }],
53
+ restarts: {
54
+ count: 0,
55
+ cents: 0,
56
+ byCause: {
57
+ "tool-lookup": { count: 0, cents: 0 },
58
+ "prefix-changed": { count: 0, cents: 0 },
59
+ },
60
+ },
61
+ parallel: { calls: 0, savedMs: 0 },
62
+ recoveredErrors: 0,
63
+ durationMs: TRACE.totalDurationMs,
64
+ feedback: null,
65
+ };
66
+
27
67
  function jsonResponse(body: unknown, status = 200): Response {
28
68
  return new Response(JSON.stringify(body), {
29
69
  status,
@@ -42,6 +82,21 @@ beforeEach(() => {
42
82
  (input: RequestInfo | URL, init?: RequestInit) => {
43
83
  const url = String(input);
44
84
  const method = (init?.method ?? "GET").toUpperCase();
85
+ if (url.includes("/_agent-native/actions/get-usage-insights")) {
86
+ return Promise.resolve(
87
+ jsonResponse({
88
+ sinceDays: 7,
89
+ current: { runs: 1, tokens: RUN.tokens, cost: RUN.cost },
90
+ previous: { runs: 0, tokens: RUN.tokens, cost: RUN.cost },
91
+ runs: [RUN],
92
+ }),
93
+ );
94
+ }
95
+ if (url.includes("/_agent-native/actions/get-usage-run")) {
96
+ return Promise.resolve(
97
+ jsonResponse({ ...RUN, reply: "hello", turns: [], scores: [] }),
98
+ );
99
+ }
45
100
  if (url.includes("/traces/run-promote-1/promote") && method === "POST") {
46
101
  return Promise.resolve(
47
102
  jsonResponse({
@@ -119,6 +174,12 @@ afterEach(() => {
119
174
  queryClient.clear();
120
175
  });
121
176
 
177
+ function bodyButton(text: string) {
178
+ return Array.from(document.body.querySelectorAll("button")).find((button) =>
179
+ button.textContent?.startsWith(text),
180
+ );
181
+ }
182
+
122
183
  function renderDashboard() {
123
184
  act(() => {
124
185
  root.render(
@@ -154,38 +215,37 @@ describe("ObservabilityDashboard promote control", () => {
154
215
  );
155
216
 
156
217
  await vi.waitFor(() => {
157
- expect(container.textContent).toContain("run-prom");
218
+ expect(container.textContent).toContain("Say hello");
158
219
  });
159
220
 
160
- const row = container.querySelector("tbody tr") as HTMLTableRowElement;
161
- expect(row).toBeTruthy();
162
- act(() => row.click());
221
+ const promptRow = Array.from(container.querySelectorAll("button")).find(
222
+ (button) => button.textContent?.startsWith("Say hello"),
223
+ );
224
+ act(() => promptRow!.click());
225
+
226
+ await vi.waitFor(() => {
227
+ expect(bodyButton("Raw trace")).toBeTruthy();
228
+ });
229
+ act(() => bodyButton("Raw trace")!.click());
163
230
 
164
231
  await vi.waitFor(() => {
165
- const promote = Array.from(container.querySelectorAll("button")).find(
166
- (button) => button.textContent === "Promote to eval",
167
- );
232
+ const promote = bodyButton("Promote to eval");
168
233
  expect(promote).toBeTruthy();
169
234
  expect(promote?.disabled).toBe(false);
170
235
  });
171
236
 
172
- const promote = Array.from(container.querySelectorAll("button")).find(
173
- (button) => button.textContent === "Promote to eval",
237
+ const promote = bodyButton("Promote to eval");
238
+ const mustContain = document.body.querySelector<HTMLInputElement>(
239
+ 'input[aria-label="Text to check for in the promoted eval reply"]',
240
+ );
241
+ expect(mustContain?.placeholder).toBe(
242
+ "Optional text to check for in the reply…",
174
243
  );
175
- expect(promote).toBeTruthy();
176
- expect(
177
- container.querySelector<HTMLInputElement>("input")?.placeholder,
178
- ).toBe("Optional text to check for in the reply…");
179
- expect(
180
- container
181
- .querySelector<HTMLInputElement>("input")
182
- ?.getAttribute("aria-label"),
183
- ).toBe("Text to check for in the promoted eval reply");
184
244
  act(() => promote!.click());
185
245
 
186
246
  await vi.waitFor(() => {
187
- expect(container.textContent).toContain("Eval dataset ds-99");
188
- expect(container.textContent).toContain(
247
+ expect(document.body.textContent).toContain("Eval dataset ds-99");
248
+ expect(document.body.textContent).toContain(
189
249
  "agent-native eval promote run-promote-1 --write evals/from-trace.eval.ts",
190
250
  );
191
251
  });