@agent-native/toolkit 0.204.0 → 0.204.1-nightly-20261008065100

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 (168) hide show
  1. package/agent-native.eject.json +2 -0
  2. package/dist/app/agent-page/AgentJobsTab.d.ts.map +1 -1
  3. package/dist/app/agent-page/AgentJobsTab.js +4 -2
  4. package/dist/app/agent-page/AgentJobsTab.js.map +1 -1
  5. package/dist/app/agent-page/AutomationDetailsDialog.d.ts +2 -1
  6. package/dist/app/agent-page/AutomationDetailsDialog.d.ts.map +1 -1
  7. package/dist/app/agent-page/AutomationDetailsDialog.js +8 -4
  8. package/dist/app/agent-page/AutomationDetailsDialog.js.map +1 -1
  9. package/dist/app/agent-page/ScheduledTriggerNotice.d.ts.map +1 -1
  10. package/dist/app/agent-page/ScheduledTriggerNotice.js +41 -19
  11. package/dist/app/agent-page/ScheduledTriggerNotice.js.map +1 -1
  12. package/dist/app/agent-page/use-jobs.d.ts +1 -1
  13. package/dist/app/agent-page/use-jobs.d.ts.map +1 -1
  14. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  15. package/dist/app/agentkit/react/components.js +124 -45
  16. package/dist/app/agentkit/react/components.js.map +1 -1
  17. package/dist/app/agentkit/react/context.d.ts +22 -1
  18. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  19. package/dist/app/agentkit/react/context.js +14 -1
  20. package/dist/app/agentkit/react/context.js.map +1 -1
  21. package/dist/app/agentkit/react/root.d.ts +3 -2
  22. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  23. package/dist/app/agentkit/react/root.js +2 -2
  24. package/dist/app/agentkit/react/root.js.map +1 -1
  25. package/dist/app/agentkit/react/styles.css +32 -2
  26. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  27. package/dist/app/chat/AgentKitAssistantChat.js +74 -7
  28. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  29. package/dist/app/chat/AgentPanel.js +1 -1
  30. package/dist/app/chat/AgentPanel.js.map +1 -1
  31. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  32. package/dist/app/chat/MultiTabAssistantChat.js +10 -9
  33. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  34. package/dist/app/chat/RunStuckBanner.d.ts +8 -1
  35. package/dist/app/chat/RunStuckBanner.d.ts.map +1 -1
  36. package/dist/app/chat/RunStuckBanner.js +68 -6
  37. package/dist/app/chat/RunStuckBanner.js.map +1 -1
  38. package/dist/app/chat/chat/run-recovery.d.ts +4 -2
  39. package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
  40. package/dist/app/chat/chat/run-recovery.js +3 -3
  41. package/dist/app/chat/chat/run-recovery.js.map +1 -1
  42. package/dist/app/chat/chat/surface-types.d.ts +1 -0
  43. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  44. package/dist/app/chat/composer/use-mention-search.d.ts +1 -0
  45. package/dist/app/chat/composer/use-mention-search.d.ts.map +1 -1
  46. package/dist/app/chat/fallback-chat-title.d.ts +2 -0
  47. package/dist/app/chat/fallback-chat-title.d.ts.map +1 -0
  48. package/dist/app/chat/fallback-chat-title.js +11 -0
  49. package/dist/app/chat/fallback-chat-title.js.map +1 -0
  50. package/dist/app/chat/feedback-report.d.ts +12 -0
  51. package/dist/app/chat/feedback-report.d.ts.map +1 -0
  52. package/dist/app/chat/feedback-report.js +21 -0
  53. package/dist/app/chat/feedback-report.js.map +1 -0
  54. package/dist/app/chat/reconcile-settled-run.d.ts +15 -0
  55. package/dist/app/chat/reconcile-settled-run.d.ts.map +1 -0
  56. package/dist/app/chat/reconcile-settled-run.js +41 -0
  57. package/dist/app/chat/reconcile-settled-run.js.map +1 -0
  58. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  59. package/dist/app/i18n/catalogs/ar-SA.js +9 -0
  60. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  61. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  62. package/dist/app/i18n/catalogs/de-DE.js +9 -0
  63. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  64. package/dist/app/i18n/catalogs/en-US.d.ts +9 -0
  65. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  66. package/dist/app/i18n/catalogs/en-US.js +9 -0
  67. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  68. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  69. package/dist/app/i18n/catalogs/es-ES.js +9 -0
  70. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  71. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  72. package/dist/app/i18n/catalogs/fr-FR.js +9 -0
  73. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  74. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  75. package/dist/app/i18n/catalogs/hi-IN.js +9 -0
  76. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  77. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  78. package/dist/app/i18n/catalogs/ja-JP.js +9 -0
  79. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  80. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  81. package/dist/app/i18n/catalogs/ko-KR.js +9 -0
  82. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  83. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  84. package/dist/app/i18n/catalogs/pt-BR.js +9 -0
  85. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  86. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  87. package/dist/app/i18n/catalogs/zh-CN.js +9 -0
  88. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  89. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  90. package/dist/app/i18n/catalogs/zh-TW.js +9 -0
  91. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  92. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  93. package/dist/app/providers/AppProviders.js +7 -1
  94. package/dist/app/providers/AppProviders.js.map +1 -1
  95. package/dist/app/settings/shell/pages/audit.d.ts.map +1 -1
  96. package/dist/app/settings/shell/pages/audit.js +11 -2
  97. package/dist/app/settings/shell/pages/audit.js.map +1 -1
  98. package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
  99. package/dist/app/shared/AppShellSkeleton.js +4 -0
  100. package/dist/app/shared/AppShellSkeleton.js.map +1 -1
  101. package/dist/composer/ComposerContextMenu.d.ts +14 -1
  102. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  103. package/dist/composer/ComposerContextMenu.js +65 -19
  104. package/dist/composer/ComposerContextMenu.js.map +1 -1
  105. package/dist/composer/MentionPopover.d.ts.map +1 -1
  106. package/dist/composer/MentionPopover.js +3 -3
  107. package/dist/composer/MentionPopover.js.map +1 -1
  108. package/dist/composer/PromptComposer.d.ts +1 -1
  109. package/dist/composer/PromptComposer.d.ts.map +1 -1
  110. package/dist/composer/TiptapComposer.d.ts +2 -2
  111. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  112. package/dist/composer/TiptapComposer.js +92 -36
  113. package/dist/composer/TiptapComposer.js.map +1 -1
  114. package/dist/composer/use-mention-search.d.ts +1 -0
  115. package/dist/composer/use-mention-search.d.ts.map +1 -1
  116. package/dist/composer/use-mention-search.js +8 -1
  117. package/dist/composer/use-mention-search.js.map +1 -1
  118. package/package.json +4 -4
  119. package/src/app/agent-page/AgentJobsTab.blocked.spec.tsx +43 -0
  120. package/src/app/agent-page/AgentJobsTab.tsx +14 -5
  121. package/src/app/agent-page/AutomationDetailsDialog.tsx +22 -8
  122. package/src/app/agent-page/ScheduledTriggerNotice.spec.tsx +45 -3
  123. package/src/app/agent-page/ScheduledTriggerNotice.tsx +52 -31
  124. package/src/app/agent-page/use-jobs.ts +1 -1
  125. package/src/app/agentkit/react/components.interaction.spec.tsx +311 -1
  126. package/src/app/agentkit/react/components.spec.tsx +106 -2
  127. package/src/app/agentkit/react/components.tsx +217 -80
  128. package/src/app/agentkit/react/context.tsx +36 -0
  129. package/src/app/agentkit/react/root.tsx +4 -0
  130. package/src/app/agentkit/react/styles.css +32 -2
  131. package/src/app/chat/AgentKitAssistantChat.spec.tsx +140 -18
  132. package/src/app/chat/AgentKitAssistantChat.tsx +123 -7
  133. package/src/app/chat/AgentPanel.header.spec.ts +43 -7
  134. package/src/app/chat/AgentPanel.tsx +1 -1
  135. package/src/app/chat/MultiTabAssistantChat.spec.tsx +34 -0
  136. package/src/app/chat/MultiTabAssistantChat.tsx +10 -9
  137. package/src/app/chat/RunStuckBanner.spec.tsx +258 -17
  138. package/src/app/chat/RunStuckBanner.tsx +107 -5
  139. package/src/app/chat/chat/run-recovery.tsx +10 -2
  140. package/src/app/chat/chat/surface-types.ts +1 -0
  141. package/src/app/chat/fallback-chat-title.ts +10 -0
  142. package/src/app/chat/feedback-report.spec.ts +45 -0
  143. package/src/app/chat/feedback-report.ts +27 -0
  144. package/src/app/chat/reconcile-settled-run.spec.ts +112 -0
  145. package/src/app/chat/reconcile-settled-run.ts +57 -0
  146. package/src/app/i18n/catalogs/ar-SA.ts +12 -0
  147. package/src/app/i18n/catalogs/de-DE.ts +12 -0
  148. package/src/app/i18n/catalogs/en-US.ts +12 -0
  149. package/src/app/i18n/catalogs/es-ES.ts +12 -0
  150. package/src/app/i18n/catalogs/fr-FR.ts +12 -0
  151. package/src/app/i18n/catalogs/hi-IN.ts +12 -0
  152. package/src/app/i18n/catalogs/ja-JP.ts +12 -0
  153. package/src/app/i18n/catalogs/ko-KR.ts +12 -0
  154. package/src/app/i18n/catalogs/pt-BR.ts +12 -0
  155. package/src/app/i18n/catalogs/zh-CN.ts +10 -0
  156. package/src/app/i18n/catalogs/zh-TW.ts +10 -0
  157. package/src/app/providers/AppProviders.spec.tsx +29 -0
  158. package/src/app/providers/AppProviders.tsx +13 -0
  159. package/src/app/settings/shell/pages/audit.spec.tsx +30 -0
  160. package/src/app/settings/shell/pages/audit.tsx +12 -1
  161. package/src/app/shared/AppShellSkeleton.tsx +4 -0
  162. package/src/composer/ComposerContextMenu.tsx +94 -22
  163. package/src/composer/ComposerPlusMenu.parity.spec.tsx +218 -233
  164. package/src/composer/MentionPopover.tsx +12 -3
  165. package/src/composer/PromptComposer.context.spec.tsx +527 -195
  166. package/src/composer/PromptComposer.tsx +1 -1
  167. package/src/composer/TiptapComposer.tsx +105 -37
  168. package/src/composer/use-mention-search.ts +8 -1
@@ -2573,51 +2573,29 @@ function AgentMentionIcon({ icon }: { icon: string }) {
2573
2573
  }
2574
2574
  }
2575
2575
 
2576
+ // Only the composer's serialized `@[label|icon]` form is a mention. A bare
2577
+ // "@word" is whatever the user typed — an address, a handle, "@3pm" — and
2578
+ // rendering it as a chip claims a reference the message never carried.
2576
2579
  function renderUserMessageText(text: string): ReactNode[] {
2577
2580
  const richMatches = Array.from(text.matchAll(/@\[([^\]|]+)\|([^\]]+)\]/g));
2578
- if (richMatches.length) {
2579
- const parts: ReactNode[] = [];
2580
- let lastIndex = 0;
2581
- richMatches.forEach((match, index) => {
2582
- const start = match.index ?? 0;
2583
- if (start > lastIndex) parts.push(text.slice(lastIndex, start));
2584
- const label = match[1] ?? "";
2585
- parts.push(
2586
- <span
2587
- key={`rich-mention:${start}:${index}`}
2588
- className="agentkit-mention"
2589
- data-mention-label={label}
2590
- >
2591
- <AgentMentionIcon icon={match[2] ?? ""} />
2592
- <span className="agentkit-mention-label">{label}</span>
2593
- </span>,
2594
- );
2595
- lastIndex = start + match[0].length;
2596
- });
2597
- if (lastIndex < text.length) parts.push(text.slice(lastIndex));
2598
- return parts;
2599
- }
2600
-
2601
- const plainMatches = Array.from(text.matchAll(/(^|\s)@(\w+)/g));
2602
- if (!plainMatches.length) return [text];
2581
+ if (!richMatches.length) return [text];
2603
2582
  const parts: ReactNode[] = [];
2604
2583
  let lastIndex = 0;
2605
- plainMatches.forEach((match, index) => {
2606
- const matchIndex = match.index ?? 0;
2607
- const start = matchIndex + (match[1]?.length ?? 0);
2608
- const end = matchIndex + match[0].length;
2584
+ richMatches.forEach((match, index) => {
2585
+ const start = match.index ?? 0;
2609
2586
  if (start > lastIndex) parts.push(text.slice(lastIndex, start));
2610
- const label = match[2] ?? "";
2587
+ const label = match[1] ?? "";
2611
2588
  parts.push(
2612
2589
  <span
2613
- key={`plain-mention:${start}:${index}`}
2614
- className="agentkit-mention agentkit-mention--plain"
2590
+ key={`rich-mention:${start}:${index}`}
2591
+ className="agentkit-mention"
2615
2592
  data-mention-label={label}
2616
2593
  >
2617
- @{label}
2594
+ <AgentMentionIcon icon={match[2] ?? ""} />
2595
+ <span className="agentkit-mention-label">{label}</span>
2618
2596
  </span>,
2619
2597
  );
2620
- lastIndex = end;
2598
+ lastIndex = start + match[0].length;
2621
2599
  });
2622
2600
  if (lastIndex < text.length) parts.push(text.slice(lastIndex));
2623
2601
  return parts;
@@ -3049,6 +3027,7 @@ export function AgentMessageActions({
3049
3027
  slots,
3050
3028
  onThreadForked,
3051
3029
  onCopyMessage,
3030
+ buildFeedbackReport,
3052
3031
  branchNavigation,
3053
3032
  loadRunUsage,
3054
3033
  } = useAgentKit();
@@ -3073,6 +3052,10 @@ export function AgentMessageActions({
3073
3052
  const [feedbackReasonSubmitted, setFeedbackReasonSubmitted] = useState(false);
3074
3053
  const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
3075
3054
  const [requestIdCopied, setRequestIdCopied] = useState(false);
3055
+ const [feedbackDetailsCopied, setFeedbackDetailsCopied] = useState(false);
3056
+ const feedbackDetailsCopiedTimer = useRef<ReturnType<
3057
+ typeof setTimeout
3058
+ > | null>(null);
3076
3059
  const [actionsMenuOpen, setActionsMenuOpen] = useState(false);
3077
3060
  const [usageDetails, setUsageDetails] = useState<
3078
3061
  | { runId: string; status: "loading" }
@@ -3163,6 +3146,8 @@ export function AgentMessageActions({
3163
3146
  if (copiedTimer.current) clearTimeout(copiedTimer.current);
3164
3147
  if (requestIdCopiedTimer.current)
3165
3148
  clearTimeout(requestIdCopiedTimer.current);
3149
+ if (feedbackDetailsCopiedTimer.current)
3150
+ clearTimeout(feedbackDetailsCopiedTimer.current);
3166
3151
  },
3167
3152
  [message.id],
3168
3153
  );
@@ -3216,6 +3201,25 @@ export function AgentMessageActions({
3216
3201
  },
3217
3202
  `${threadId}:${message.id}:request-id:${requestId ?? "unavailable"}`,
3218
3203
  );
3204
+ const copyFeedbackDetailsAction = useAgentKitMutation(async () => {
3205
+ if (!buildFeedbackReport) throw new Error(labels.copyUnavailable);
3206
+ const report = buildFeedbackReport({
3207
+ threadId,
3208
+ ...(runId ? { runId } : {}),
3209
+ messageId: message.id,
3210
+ note: feedbackReason,
3211
+ });
3212
+ if (!(await writeClipboardText(report))) {
3213
+ throw new Error(labels.copyUnavailable);
3214
+ }
3215
+ setFeedbackDetailsCopied(true);
3216
+ if (feedbackDetailsCopiedTimer.current)
3217
+ clearTimeout(feedbackDetailsCopiedTimer.current);
3218
+ feedbackDetailsCopiedTimer.current = setTimeout(
3219
+ () => setFeedbackDetailsCopied(false),
3220
+ 1_400,
3221
+ );
3222
+ }, `${threadId}:${message.id}:feedback-details`);
3219
3223
  const forkAction = useAgentKitMutation(async () => {
3220
3224
  const thread = await control.fork(message.id);
3221
3225
  onThreadForked?.(thread);
@@ -3248,6 +3252,12 @@ export function AgentMessageActions({
3248
3252
  setFeedback(previous);
3249
3253
  }
3250
3254
  };
3255
+ const addFeedbackReason = (reason: string) =>
3256
+ setFeedbackReason((current) => {
3257
+ if (current.includes(reason)) return current;
3258
+ const trimmed = current.trim();
3259
+ return (trimmed ? `${trimmed}. ${reason}` : reason).slice(0, 2_000);
3260
+ });
3251
3261
  const submitFeedbackReason = async (event: FormEvent<HTMLFormElement>) => {
3252
3262
  event.preventDefault();
3253
3263
  const reason = feedbackReason.trim();
@@ -3266,6 +3276,7 @@ export function AgentMessageActions({
3266
3276
  feedbackAction.error ??
3267
3277
  branchNavigationAction.error ??
3268
3278
  requestIdAction.error ??
3279
+ copyFeedbackDetailsAction.error ??
3269
3280
  forkAction.error ??
3270
3281
  regenerateAction.error;
3271
3282
  const usageMenuItems = hasRunUsage
@@ -3479,6 +3490,25 @@ export function AgentMessageActions({
3479
3490
  disabled={feedbackAction.pending}
3480
3491
  className="agentkit-feedback-textarea"
3481
3492
  />
3493
+ <div className="agentkit-feedback-reasons">
3494
+ {[
3495
+ labels.feedbackReasonMisread,
3496
+ labels.feedbackReasonNotDone,
3497
+ labels.feedbackReasonWrongNumbers,
3498
+ labels.feedbackReasonTooSlow,
3499
+ ].map((reason) => (
3500
+ <ActionButton
3501
+ key={reason}
3502
+ type="button"
3503
+ size="compact"
3504
+ emphasis="outline"
3505
+ disabled={feedbackAction.pending}
3506
+ onPress={() => addFeedbackReason(reason)}
3507
+ >
3508
+ {reason}
3509
+ </ActionButton>
3510
+ ))}
3511
+ </div>
3482
3512
  <div className="agentkit-feedback-footer">
3483
3513
  <span>
3484
3514
  {labels.feedbackKeyboardHint.replace(
@@ -3489,17 +3519,43 @@ export function AgentMessageActions({
3489
3519
  : "Ctrl",
3490
3520
  )}
3491
3521
  </span>
3492
- <ActionButton
3493
- type="submit"
3494
- intent="primary"
3495
- size="compact"
3496
- pending={feedbackAction.pending}
3497
- disabled={
3498
- feedbackAction.pending || !feedbackReason.trim()
3499
- }
3500
- >
3501
- {labels.feedbackSubmit}
3502
- </ActionButton>
3522
+ <div className="agentkit-feedback-actions">
3523
+ {buildFeedbackReport ? (
3524
+ <ActionButton
3525
+ type="button"
3526
+ size="compact"
3527
+ emphasis="ghost"
3528
+ leadingIcon={
3529
+ feedbackDetailsCopied ? (
3530
+ <IconCircleCheck size={14} aria-hidden="true" />
3531
+ ) : (
3532
+ <IconCopy size={14} aria-hidden="true" />
3533
+ )
3534
+ }
3535
+ pending={copyFeedbackDetailsAction.pending}
3536
+ onPress={() =>
3537
+ void copyFeedbackDetailsAction
3538
+ .execute()
3539
+ .catch(() => undefined)
3540
+ }
3541
+ >
3542
+ {feedbackDetailsCopied
3543
+ ? labels.copied
3544
+ : labels.feedbackCopyDetails}
3545
+ </ActionButton>
3546
+ ) : null}
3547
+ <ActionButton
3548
+ type="submit"
3549
+ intent="primary"
3550
+ size="compact"
3551
+ pending={feedbackAction.pending}
3552
+ disabled={
3553
+ feedbackAction.pending || !feedbackReason.trim()
3554
+ }
3555
+ >
3556
+ {labels.feedbackSubmit}
3557
+ </ActionButton>
3558
+ </div>
3503
3559
  </div>
3504
3560
  </form>
3505
3561
  </Popover>
@@ -3791,6 +3847,28 @@ export function AgentRunFailure({
3791
3847
  threadId,
3792
3848
  }: AgentRunFailureRenderProps) {
3793
3849
  const { labels } = useAgentKit();
3850
+ const control = useAgentKitControl(threadId);
3851
+ const thread = useAgentThread(threadId);
3852
+ const [continuing, setContinuing] = useState(false);
3853
+ const [continueFailed, setContinueFailed] = useState(false);
3854
+ // Continuing resumes the stopped run's own turn, so only that turn's newest
3855
+ // run can be continued; once anything ran after it, the card is history.
3856
+ const startedAt = thread.runs[runId]?.startedAt;
3857
+ const superseded = Object.values(thread.runs).some(
3858
+ (run) =>
3859
+ run.id !== runId &&
3860
+ Boolean(run.startedAt && startedAt && run.startedAt > startedAt),
3861
+ );
3862
+ const canContinue =
3863
+ error.retryable === true && control.canContinueRun && !superseded;
3864
+ const continueRun = () => {
3865
+ setContinuing(true);
3866
+ setContinueFailed(false);
3867
+ control.continueRun(runId).catch(() => {
3868
+ setContinuing(false);
3869
+ setContinueFailed(true);
3870
+ });
3871
+ };
3794
3872
  return (
3795
3873
  <div
3796
3874
  className="agentkit-run-failure"
@@ -3803,6 +3881,23 @@ export function AgentRunFailure({
3803
3881
  <div className="agentkit-run-failure-copy">
3804
3882
  <strong>{labels.runFailed}</strong>
3805
3883
  <span>{error.message}</span>
3884
+ {canContinue ? (
3885
+ <div className="agentkit-error-actions">
3886
+ <ActionButton
3887
+ emphasis="outline"
3888
+ size="compact"
3889
+ pending={continuing}
3890
+ onPress={continueRun}
3891
+ >
3892
+ {labels.continueRun}
3893
+ </ActionButton>
3894
+ </div>
3895
+ ) : null}
3896
+ {continueFailed ? (
3897
+ <span className="agentkit-command-error">
3898
+ {labels.continueRunUnavailable}
3899
+ </span>
3900
+ ) : null}
3806
3901
  </div>
3807
3902
  </div>
3808
3903
  );
@@ -5140,12 +5235,86 @@ export function AgentKitChat({
5140
5235
  />
5141
5236
  </AgentKitSurfaceBoundary>
5142
5237
  );
5238
+ const renderRunTail = (runId: RunId) => {
5239
+ const items: ReactNode[] = [];
5240
+ if (pendingRuns.has(runId)) {
5241
+ const boundary = lastAssistantMessagesByRun.get(runId);
5242
+ items.push(
5243
+ renderRunWork({
5244
+ runId,
5245
+ anchor: boundary?.id ?? "start",
5246
+ afterSequence: boundary?.sequence,
5247
+ }),
5248
+ );
5249
+ }
5250
+ if (failedRuns.has(runId)) {
5251
+ items.push(
5252
+ <AgentKitSurfaceBoundary
5253
+ key={`run-failure:${runId}`}
5254
+ surface="activity"
5255
+ resetKey={`${runId}:${thread.events.length}`}
5256
+ >
5257
+ {renderRunFailure(runId)}
5258
+ </AgentKitSurfaceBoundary>,
5259
+ );
5260
+ }
5261
+ return items;
5262
+ };
5263
+ // A run's work after its last reply, and its failure, belong to its turn: a
5264
+ // finished run's tail goes before the first message written after it ended.
5265
+ const runEndedAt = new Map<RunId, number>();
5266
+ for (const event of thread.events) {
5267
+ const occurredAt = Date.parse(event.occurredAt);
5268
+ if (!Number.isFinite(occurredAt)) continue;
5269
+ runEndedAt.set(
5270
+ event.runId,
5271
+ Math.max(runEndedAt.get(event.runId) ?? occurredAt, occurredAt),
5272
+ );
5273
+ }
5274
+ const tailRunIds = Array.from(
5275
+ new Set([
5276
+ ...pendingRunIds,
5277
+ ...Array.from(failedRuns.keys()).filter(
5278
+ (runId) => !lastAssistantMessagesByRun.has(runId),
5279
+ ),
5280
+ ]),
5281
+ );
5282
+ const tailsBeforeMessage = new Map<number, RunId[]>();
5283
+ const tailsAtEnd: RunId[] = [];
5284
+ for (const runId of tailRunIds) {
5285
+ const status = thread.runs[runId]?.status;
5286
+ const endedAt = runEndedAt.get(runId);
5287
+ const lastOwnMessage = thread.messages.findLastIndex(
5288
+ (message) => messageRunIds.get(message.id) === runId,
5289
+ );
5290
+ const before =
5291
+ endedAt !== undefined &&
5292
+ (status === "completed" || status === "failed" || status === "cancelled")
5293
+ ? thread.messages.findIndex(
5294
+ (message, index) =>
5295
+ index > lastOwnMessage &&
5296
+ messageRunIds.get(message.id) !== runId &&
5297
+ Date.parse(message.createdAt ?? "") > endedAt,
5298
+ )
5299
+ : -1;
5300
+ if (before === -1) {
5301
+ tailsAtEnd.push(runId);
5302
+ } else {
5303
+ tailsBeforeMessage.set(before, [
5304
+ ...(tailsBeforeMessage.get(before) ?? []),
5305
+ runId,
5306
+ ]);
5307
+ }
5308
+ }
5143
5309
  const transcriptItems: ReactNode[] = [];
5144
5310
  const previousAssistantByRun = new Map<
5145
5311
  RunId,
5146
5312
  { id: string; sequence: number }
5147
5313
  >();
5148
- for (const message of thread.messages) {
5314
+ for (const [messageIndex, message] of thread.messages.entries()) {
5315
+ for (const runId of tailsBeforeMessage.get(messageIndex) ?? []) {
5316
+ transcriptItems.push(...renderRunTail(runId));
5317
+ }
5149
5318
  const runId = messageRunIds.get(message.id);
5150
5319
  const sequence = messageBoundarySequences.get(message.id);
5151
5320
  const isAssistantBoundary =
@@ -5244,40 +5413,8 @@ export function AgentKitChat({
5244
5413
  );
5245
5414
  }
5246
5415
  }
5247
- for (const runId of pendingRunIds) {
5248
- const boundary = lastAssistantMessagesByRun.get(runId);
5249
- transcriptItems.push(
5250
- renderRunWork({
5251
- runId,
5252
- anchor: boundary?.id ?? "start",
5253
- afterSequence: boundary?.sequence,
5254
- }),
5255
- );
5256
- if (failedRuns.has(runId)) {
5257
- transcriptItems.push(
5258
- <AgentKitSurfaceBoundary
5259
- key={`run-failure:${runId}`}
5260
- surface="activity"
5261
- resetKey={`${runId}:${thread.events.length}`}
5262
- >
5263
- {renderRunFailure(runId)}
5264
- </AgentKitSurfaceBoundary>,
5265
- );
5266
- }
5267
- }
5268
- for (const [runId] of failedRuns) {
5269
- if (lastAssistantMessagesByRun.has(runId) || pendingRuns.has(runId)) {
5270
- continue;
5271
- }
5272
- transcriptItems.push(
5273
- <AgentKitSurfaceBoundary
5274
- key={`run-failure:${runId}`}
5275
- surface="activity"
5276
- resetKey={`${runId}:${thread.events.length}`}
5277
- >
5278
- {renderRunFailure(runId)}
5279
- </AgentKitSurfaceBoundary>,
5280
- );
5416
+ for (const runId of tailsAtEnd) {
5417
+ transcriptItems.push(...renderRunTail(runId));
5281
5418
  }
5282
5419
  return (
5283
5420
  <AgentMessageEditContext.Provider value={messageEditContext}>
@@ -257,6 +257,11 @@ export interface AgentKitLabels {
257
257
  feedbackPlaceholder: string;
258
258
  feedbackKeyboardHint: string;
259
259
  feedbackSubmit: string;
260
+ feedbackReasonMisread: string;
261
+ feedbackReasonNotDone: string;
262
+ feedbackReasonWrongNumbers: string;
263
+ feedbackReasonTooSlow: string;
264
+ feedbackCopyDetails: string;
260
265
  fork: string;
261
266
  previousBranch: string;
262
267
  nextBranch: string;
@@ -280,6 +285,8 @@ export interface AgentKitLabels {
280
285
  error: string;
281
286
  renderError: string;
282
287
  runFailed: string;
288
+ continueRun: string;
289
+ continueRunUnavailable: string;
283
290
  reconnect: string;
284
291
  reasoning: string;
285
292
  expandActivity: string;
@@ -367,6 +374,11 @@ export const defaultAgentKitLabels: AgentKitLabels = {
367
374
  feedbackPlaceholder: "Describe what went wrong",
368
375
  feedbackKeyboardHint: "Press {{shortcut}}+Enter to submit",
369
376
  feedbackSubmit: "Submit feedback",
377
+ feedbackReasonMisread: "Misread my ask",
378
+ feedbackReasonNotDone: "Said done, but wasn't",
379
+ feedbackReasonWrongNumbers: "Wrong numbers",
380
+ feedbackReasonTooSlow: "Too slow",
381
+ feedbackCopyDetails: "Copy details",
370
382
  fork: "Fork conversation",
371
383
  previousBranch: "Previous branch",
372
384
  nextBranch: "Next branch",
@@ -390,6 +402,9 @@ export const defaultAgentKitLabels: AgentKitLabels = {
390
402
  error: "Something went wrong",
391
403
  renderError: "This content couldn’t be displayed.",
392
404
  runFailed: "Run failed",
405
+ continueRun: "Continue",
406
+ continueRunUnavailable:
407
+ "This run can't be continued anymore. Send a message to keep going.",
393
408
  reconnect: "Reconnect",
394
409
  reasoning: "Thinking",
395
410
  expandActivity: "Show activity details",
@@ -433,6 +448,17 @@ export type AgentKitRunUsageLoader = (input: {
433
448
  signal: AbortSignal;
434
449
  }) => Promise<AgentKitRunUsage | null>;
435
450
 
451
+ /**
452
+ * The text behind "Copy details" in the thumbs-down popover: whatever lets
453
+ * someone else open the exact run, plus the reader's note.
454
+ */
455
+ export type AgentKitFeedbackReportBuilder = (input: {
456
+ threadId: ThreadId;
457
+ runId?: RunId;
458
+ messageId: string;
459
+ note: string;
460
+ }) => string;
461
+
436
462
  export interface AgentKitProviderProps {
437
463
  controller: AgentKitController;
438
464
  threadId: ThreadId;
@@ -443,6 +469,7 @@ export interface AgentKitProviderProps {
443
469
  onThreadForked?: (thread: AgentThread) => void;
444
470
  branchNavigation?: AgentKitBranchNavigation;
445
471
  onCopyMessage?: AgentKitCopyMessageHandler;
472
+ buildFeedbackReport?: AgentKitFeedbackReportBuilder;
446
473
  loadRunUsage?: AgentKitRunUsageLoader;
447
474
  /**
448
475
  * Resolves a provider identifier through host-owned connection setup. The
@@ -478,6 +505,7 @@ export interface AgentKitContextValue {
478
505
  onThreadForked?: (thread: AgentThread) => void;
479
506
  branchNavigation?: AgentKitBranchNavigation;
480
507
  onCopyMessage?: AgentKitCopyMessageHandler;
508
+ buildFeedbackReport?: AgentKitFeedbackReportBuilder;
481
509
  loadRunUsage?: AgentKitRunUsageLoader;
482
510
  onConnectionRequest?: AgentKitProviderProps["onConnectionRequest"];
483
511
  onRenderError?: (failure: AgentKitRenderFailure) => void;
@@ -497,6 +525,7 @@ export function AgentKitProvider({
497
525
  onThreadForked,
498
526
  branchNavigation,
499
527
  onCopyMessage,
528
+ buildFeedbackReport,
500
529
  loadRunUsage,
501
530
  onConnectionRequest,
502
531
  onRenderError,
@@ -562,6 +591,7 @@ export function AgentKitProvider({
562
591
  onThreadForked,
563
592
  branchNavigation,
564
593
  onCopyMessage,
594
+ buildFeedbackReport,
565
595
  loadRunUsage,
566
596
  onConnectionRequest,
567
597
  onRenderError,
@@ -578,6 +608,7 @@ export function AgentKitProvider({
578
608
  onThreadForked,
579
609
  branchNavigation,
580
610
  onCopyMessage,
611
+ buildFeedbackReport,
581
612
  loadRunUsage,
582
613
  onConnectionRequest,
583
614
  onRenderError,
@@ -923,6 +954,11 @@ export function useAgentKitControl(requestedThreadId?: ThreadId) {
923
954
  queueMessage: (input: Omit<SendMessageInput, "threadId">) =>
924
955
  controller.queueMessage({ ...input, threadId }),
925
956
  cancel: (runId: string) => controller.cancelRun(threadId, runId),
957
+ canContinueRun: controller.supportsRunContinuation?.() === true,
958
+ continueRun: (runId: string) =>
959
+ controller.continueRun
960
+ ? controller.continueRun(threadId, runId)
961
+ : Promise.reject(new Error(labels.error)),
926
962
  approve: (
927
963
  runId: string,
928
964
  approvalId: string,
@@ -24,6 +24,7 @@ import {
24
24
  type AgentKitLabels,
25
25
  type AgentKitBranchNavigation,
26
26
  type AgentKitCopyMessageHandler,
27
+ type AgentKitFeedbackReportBuilder,
27
28
  type AgentKitRegistry,
28
29
  type AgentKitRenderFailure,
29
30
  type AgentKitRunUsageLoader,
@@ -77,6 +78,7 @@ export interface AgentKitRootBaseProps {
77
78
  onThreadForked?: (thread: AgentThread) => void;
78
79
  branchNavigation?: AgentKitBranchNavigation;
79
80
  onCopyMessage?: AgentKitCopyMessageHandler;
81
+ buildFeedbackReport?: AgentKitFeedbackReportBuilder;
80
82
  loadRunUsage?: AgentKitRunUsageLoader;
81
83
  onConnectionRequest?: (
82
84
  request: AgentConnectionRequest,
@@ -107,6 +109,7 @@ export function AgentKitRoot({
107
109
  onThreadForked,
108
110
  branchNavigation,
109
111
  onCopyMessage,
112
+ buildFeedbackReport,
110
113
  loadRunUsage,
111
114
  onConnectionRequest,
112
115
  onRenderError,
@@ -280,6 +283,7 @@ export function AgentKitRoot({
280
283
  onThreadForked={onThreadForked}
281
284
  branchNavigation={branchNavigation}
282
285
  onCopyMessage={onCopyMessage}
286
+ buildFeedbackReport={buildFeedbackReport}
283
287
  loadRunUsage={loadRunUsage}
284
288
  onConnectionRequest={onConnectionRequest}
285
289
  onRenderError={onRenderError}
@@ -527,7 +527,7 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
527
527
 
528
528
  [data-agent-native-composer-popover="true"]
529
529
  [data-mention-index]
530
- > span:last-child:not(:first-child) {
530
+ > [data-mention-description] {
531
531
  max-inline-size: 60%;
532
532
  flex: none;
533
533
  margin-inline-start: auto;
@@ -1133,15 +1133,35 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1133
1133
  inline-size: 100%;
1134
1134
  }
1135
1135
 
1136
+ .agentkit-feedback-reasons {
1137
+ display: flex;
1138
+ flex-wrap: wrap;
1139
+ gap: 0.375rem;
1140
+ }
1141
+
1142
+ .agentkit-feedback-reasons button {
1143
+ block-size: 1.5rem;
1144
+ padding-inline: 0.5rem;
1145
+ font-size: 0.6875rem;
1146
+ }
1147
+
1136
1148
  .agentkit-feedback-footer {
1137
1149
  display: flex;
1150
+ flex-wrap: wrap;
1138
1151
  align-items: center;
1139
1152
  justify-content: space-between;
1140
- gap: 0.75rem;
1153
+ gap: 0.5rem 0.75rem;
1141
1154
  color: var(--agentkit-text-muted);
1142
1155
  font-size: 0.6875rem;
1143
1156
  }
1144
1157
 
1158
+ .agentkit-feedback-actions {
1159
+ display: flex;
1160
+ align-items: center;
1161
+ gap: 0.25rem;
1162
+ margin-inline-start: auto;
1163
+ }
1164
+
1145
1165
  .agentkit-icon {
1146
1166
  inline-size: 1rem;
1147
1167
  block-size: 1rem;
@@ -1875,6 +1895,16 @@ button.agentkit-object {
1875
1895
  color: var(--agentkit-text-muted);
1876
1896
  }
1877
1897
 
1898
+ .agentkit-run-failure-copy > .agentkit-error-actions {
1899
+ flex-basis: 100%;
1900
+ margin-block-start: 0.25rem;
1901
+ }
1902
+
1903
+ .agentkit-run-failure-copy > .agentkit-command-error {
1904
+ flex-basis: 100%;
1905
+ color: var(--agentkit-danger);
1906
+ }
1907
+
1878
1908
  :where(.agentkit-chat) :where(button, a, input, summary):focus-visible {
1879
1909
  outline: 2px solid var(--agentkit-focus);
1880
1910
  outline-offset: 2px;