@kortix/ui 0.14.4 → 0.14.5

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 (118) hide show
  1. package/README.md +8 -4
  2. package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
  3. package/dist/customize/sections/llm-provider/constants.js +0 -2
  4. package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
  5. package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
  6. package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
  7. package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
  8. package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
  9. package/dist/customize/sections/llm-provider/models-tab.js +1 -10
  10. package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
  11. package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
  12. package/dist/customize/sections/llm-provider/utils.js +7 -14
  13. package/dist/customize/sections/view/gateway/_shared.js +1 -2
  14. package/dist/customize/sections/view/gateway/gateway-routing.js +3 -2
  15. package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
  16. package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
  17. package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
  18. package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
  19. package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
  20. package/dist/customize/shared/entity/entity-modal.js +7 -4
  21. package/dist/customize/skills/remove-skill-button.d.ts +16 -0
  22. package/dist/customize/skills/remove-skill-button.js +42 -0
  23. package/dist/customize/skills/skills-page.js +1 -1
  24. package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
  25. package/dist/customize/triggers/schedule/trigger-list.js +1 -18
  26. package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
  27. package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
  28. package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
  29. package/dist/files/project/hooks/use-change-requests.js +44 -25
  30. package/dist/files/renderers/docx/docx-viewer.js +4 -1
  31. package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
  32. package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
  33. package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
  34. package/dist/files/renderers/pdf/pdf-viewer.js +15 -6
  35. package/dist/files/viewer/file-content-renderer.d.ts +4 -1
  36. package/dist/files/viewer/file-content-renderer.js +2 -2
  37. package/dist/i18n/en.d.ts +26 -35
  38. package/dist/i18n/en.js +35 -44
  39. package/dist/index.d.ts +1 -1
  40. package/dist/internal/hooks/use-notification-center.js +21 -15
  41. package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
  42. package/dist/internal/hooks/use-review-session-summary.js +11 -0
  43. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  44. package/dist/internal/lib/browser-error-noise.js +1 -1
  45. package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
  46. package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
  47. package/dist/internal/lib/provider-branding.js +14 -3
  48. package/dist/internal/lib/trigger-clock.d.ts +42 -0
  49. package/dist/internal/lib/trigger-clock.js +34 -0
  50. package/dist/internal/lib/web-notifications.d.ts +6 -0
  51. package/dist/internal/lib/web-notifications.js +57 -7
  52. package/dist/internal/notifications/notification-bell.d.ts +13 -0
  53. package/dist/internal/notifications/notification-bell.js +1 -1
  54. package/dist/internal/reminders/reminder-filter.d.ts +12 -0
  55. package/dist/internal/reminders/reminder-filter.js +34 -0
  56. package/dist/internal/reminders/reminder-format.d.ts +6 -0
  57. package/dist/internal/reminders/reminder-format.js +20 -2
  58. package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
  59. package/dist/internal/stores/session-hover-store.d.ts +20 -5
  60. package/dist/internal/stores/session-hover-store.js +31 -13
  61. package/dist/internal/stores/session-panel-memory-store.js +2 -0
  62. package/dist/primitives/count-badge.d.ts +3 -1
  63. package/dist/primitives/count-badge.js +3 -3
  64. package/dist/primitives/glyph-paths-source.js +1 -1
  65. package/dist/primitives/glyph-paths.generated.js +1 -1
  66. package/dist/providers/ui-provider.d.ts +57 -0
  67. package/dist/providers/ui-provider.js +4 -0
  68. package/dist/session/composer/ComposerCard.d.ts +2 -0
  69. package/dist/session/composer/ComposerCard.js +5 -1
  70. package/dist/session/composer/agent-selector.js +12 -5
  71. package/dist/session/composer/composer-toolbar.js +4 -2
  72. package/dist/session/composer/composer-underbar.js +3 -1
  73. package/dist/session/composer/composer.js +34 -5
  74. package/dist/session/composer/menus/slash-actions.d.ts +22 -2
  75. package/dist/session/composer/menus/slash-actions.js +20 -0
  76. package/dist/session/composer/menus/slash-items.js +19 -3
  77. package/dist/session/header/reminder-parts.d.ts +285 -0
  78. package/dist/session/header/reminder-parts.js +115 -0
  79. package/dist/session/header/session-reminders-indicator.d.ts +13 -0
  80. package/dist/session/header/session-reminders-indicator.js +142 -35
  81. package/dist/session/models/model-availability.d.ts +23 -0
  82. package/dist/session/models/model-availability.js +20 -0
  83. package/dist/session/models/model-grouping.js +1 -4
  84. package/dist/session/models/model-selector.js +23 -14
  85. package/dist/session/models/use-model-connection-gate.js +4 -1
  86. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  87. package/dist/session/outcomes/session-outcomes-provider.js +31 -3
  88. package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
  89. package/dist/session/overrides/session-overrides-composer.js +5 -1
  90. package/dist/session/panel/mobile-tool-drawer.js +4 -2
  91. package/dist/session/panel/open-session-quick-view.js +2 -1
  92. package/dist/session/panel/panel-fallback.js +8 -4
  93. package/dist/session/panel/session-panel-provider.d.ts +1 -0
  94. package/dist/session/panel/session-panel-provider.js +13 -1
  95. package/dist/session/panel/session-reminders-panel.d.ts +4 -0
  96. package/dist/session/panel/session-reminders-panel.js +109 -0
  97. package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
  98. package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
  99. package/dist/session/print/session-print-header.d.ts +0 -10
  100. package/dist/session/print/session-print-header.js +16 -1
  101. package/dist/session/status/session-surface.js +1 -1
  102. package/dist/session/thread/optimistic-turn.d.ts +5 -1
  103. package/dist/session/thread/optimistic-turn.js +8 -6
  104. package/dist/session/thread/session-chat.d.ts +6 -1
  105. package/dist/session/thread/session-chat.js +22 -3
  106. package/dist/session/thread/session-thread.d.ts +10 -1
  107. package/dist/session/thread/session-thread.js +2 -2
  108. package/dist/session/turn/burst-step-body.js +4 -0
  109. package/dist/styles.css +1 -1
  110. package/dist/workspace/sidebar/project-session-list.js +52 -55
  111. package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
  112. package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
  113. package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
  114. package/dist/workspace/sidebar/session-status-mark.js +63 -17
  115. package/package.json +7 -3
  116. package/skills/kortix-ui/SKILL.md +1 -1
  117. package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
  118. package/dist/customize/sections/view/sandbox-provider-result.js +0 -79
@@ -1,13 +1,3 @@
1
- /**
2
- * The masthead of a printed conversation.
3
- *
4
- * A PDF of a session is read away from the app — attached to a ticket, mailed
5
- * to a customer, filed as a record — so it has to say what it is on its own
6
- * face. On screen this renders nothing (`print.css` keeps `[data-print-header]`
7
- * display:none until the print media query), and it is in the DOM at all times
8
- * so that printing needs no re-render: `window.print()` is synchronous, and a
9
- * React commit scheduled beside it is not guaranteed to land first.
10
- */
11
1
  export interface SessionPrintHeaderProps {
12
2
  title: string;
13
3
  projectName?: string | null;
@@ -1,5 +1,19 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * The masthead of a printed conversation.
5
+ *
6
+ * A PDF of a session is read away from the app — attached to a ticket, mailed
7
+ * to a customer, filed as a record — so it has to say what it is on its own
8
+ * face. On screen this renders nothing (`print.css` keeps `[data-print-header]`
9
+ * display:none until the print media query), and it is in the DOM at all times
10
+ * so that printing needs no re-render: `window.print()` is synchronous, and a
11
+ * React commit scheduled beside it is not guaranteed to land first.
12
+ *
13
+ * The agent is named the way the composer names it: a host's `agentLabels`
14
+ * entry wins over the raw agent name.
15
+ */
16
+ import { useUiHostConfig } from '../../providers/ui-provider.js';
3
17
  /** `16 September 2026 at 20:41` — unambiguous across locales, no slashes. */
4
18
  export function formatPrintedAt(date) {
5
19
  return new Intl.DateTimeFormat('en-GB', {
@@ -19,9 +33,10 @@ export function printHeaderMeta(input) {
19
33
  .join(' · ');
20
34
  }
21
35
  export function SessionPrintHeader({ title, projectName, agentName, now, }) {
36
+ const { agentLabels } = useUiHostConfig();
22
37
  const meta = printHeaderMeta({
23
38
  projectName,
24
- agentName,
39
+ agentName: (agentName && agentLabels?.[agentName]) || agentName,
25
40
  printedAt: formatPrintedAt(now ?? new Date()),
26
41
  });
27
42
  return (
@@ -113,7 +113,7 @@ export function resolveResumeOverlay(input) {
113
113
  * skeleton rows instead of a restart card or the boot screen.
114
114
  */
115
115
  export function hasOrExpectsTranscript(input) {
116
- return input.hasTranscript || input.savedTranscript === 'loading';
116
+ return input.hasTranscript || input.savedTranscript !== 'none';
117
117
  }
118
118
  /**
119
119
  * Should the local "brand new session" hint be forgotten?
@@ -1,4 +1,5 @@
1
1
  import { type ReactNode } from 'react';
2
+ import { parseSessionReferences } from './message-parsing.js';
2
3
  import type { SentAttachment } from '../composer/sent-attachment-previews.js';
3
4
  import { type AttachmentUploadStatus } from '../turn/user-message.js';
4
5
  /**
@@ -82,8 +83,11 @@ busy, deliveryStatus, className, }: {
82
83
  * app: chip in the composer, underline in the optimistic bubble, underline
83
84
  * again in the server turn.
84
85
  */
85
- export declare function HighlightMentions({ text, agentNames, onFileClick, }: {
86
+ export declare function HighlightMentions({ text, sessions: parsedSessions, agentNames, onFileClick, }: {
86
87
  text: string;
88
+ /** The session refs the caller already stripped from `text`. Then `text` is
89
+ * not parsed again: a second parse finds no refs and loses every title. */
90
+ sessions?: ReturnType<typeof parseSessionReferences>['sessions'];
87
91
  agentNames?: string[];
88
92
  onFileClick?: (path: string) => void;
89
93
  }): import("react").JSX.Element;
@@ -85,7 +85,7 @@ function OptimisticUserBubble({ text, agentNames, onFileClick, deferPreview, sta
85
85
  // Strip every ref block the composer folded into the prompt, in the order it
86
86
  // folded them in, so the bubble shows the sentence the user typed and the
87
87
  // attachments as tiles — never raw XML.
88
- const { quotes, files, cleanText, pastes } = useMemo(() => {
88
+ const { quotes, files, cleanText, pastes, sessions } = useMemo(() => {
89
89
  // Pastes first, as on the sent turn: a ref inside a paste is paste text.
90
90
  const { text: afterPastes, pastes } = splitPastedContent(text);
91
91
  const { cleanText: afterReply, quotes } = parseReplyContexts(afterPastes);
@@ -93,8 +93,8 @@ function OptimisticUserBubble({ text, agentNames, onFileClick, deferPreview, sta
93
93
  const { cleanText: afterProjects } = parseProjectReferences(afterFiles);
94
94
  const { cleanText: afterFileMentions } = parseFileMentionReferences(afterProjects);
95
95
  const { cleanText: afterAgentMentions } = parseAgentMentionReferences(afterFileMentions);
96
- const { cleanText } = parseSessionReferences(afterAgentMentions);
97
- return { quotes, files, cleanText, pastes };
96
+ const { cleanText, sessions } = parseSessionReferences(afterAgentMentions);
97
+ return { quotes, files, cleanText, pastes, sessions };
98
98
  }, [text]);
99
99
  // Quotes are drawn where they were written, by the same component the sent
100
100
  // message uses — see `QuotedMessageBody`.
@@ -129,7 +129,7 @@ function OptimisticUserBubble({ text, agentNames, onFileClick, deferPreview, sta
129
129
  }));
130
130
  return [...pastes.map(pastedAttachment), ...fromText, ...fromStaged];
131
131
  }, [pastes, files, deferPreview, staged]);
132
- return (_jsxs("div", { className: "ml-auto flex w-full max-w-[80%] flex-col items-end gap-2 self-end", children: [attachments.length > 0 && (_jsx(MessageAttachments, { attachments: attachments, status: uploadStatus })), (cleanText || quotedPieces) && (_jsx("div", { className: cn(BUBBLE_SURFACE, 'w-fit overflow-hidden'), children: quotedPieces ? (_jsx(QuotedMessageBody, { pieces: quotedPieces, renderText: (runText) => (_jsx(HighlightMentions, { text: runText, agentNames: agentNames, onFileClick: onFileClick })) })) : (_jsx("p", { className: BUBBLE_TEXT, children: _jsx(HighlightMentions, { text: cleanText, agentNames: agentNames, onFileClick: onFileClick }) })) })), _jsx(UserMessageActions, { timestamp: null, copyText: expandPastedContent(text), deliveryStatus: deliveryStatus })] }));
132
+ return (_jsxs("div", { className: "ml-auto flex w-full max-w-[80%] flex-col items-end gap-2 self-end", children: [attachments.length > 0 && (_jsx(MessageAttachments, { attachments: attachments, status: uploadStatus })), (cleanText || quotedPieces) && (_jsx("div", { className: cn(BUBBLE_SURFACE, 'w-fit overflow-hidden'), children: quotedPieces ? (_jsx(QuotedMessageBody, { pieces: quotedPieces, renderText: (runText) => (_jsx(HighlightMentions, { text: runText, sessions: sessions, agentNames: agentNames, onFileClick: onFileClick })) })) : (_jsx("p", { className: BUBBLE_TEXT, children: _jsx(HighlightMentions, { text: cleanText, sessions: sessions, agentNames: agentNames, onFileClick: onFileClick }) })) })), _jsx(UserMessageActions, { timestamp: null, copyText: expandPastedContent(text), deliveryStatus: deliveryStatus })] }));
133
133
  }
134
134
  /**
135
135
  * Highlight @mentions in plain text (for optimistic & user messages).
@@ -141,11 +141,13 @@ function OptimisticUserBubble({ text, agentNames, onFileClick, deferPreview, sta
141
141
  * app: chip in the composer, underline in the optimistic bubble, underline
142
142
  * again in the server turn.
143
143
  */
144
- export function HighlightMentions({ text, agentNames, onFileClick, }) {
144
+ export function HighlightMentions({ text, sessions: parsedSessions, agentNames, onFileClick, }) {
145
145
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
146
146
  // Strip every ref block (project/file/agent/session) before processing
147
147
  // inline @ mentions so the visible text never shows raw XML.
148
148
  const { cleanText, sessions } = useMemo(() => {
149
+ if (parsedSessions)
150
+ return { cleanText: text, sessions: parsedSessions };
149
151
  const a = parseProjectReferences(text);
150
152
  const b = parseFileMentionReferences(a.cleanText);
151
153
  const c = parseAgentMentionReferences(b.cleanText);
@@ -154,7 +156,7 @@ export function HighlightMentions({ text, agentNames, onFileClick, }) {
154
156
  cleanText: d.cleanText,
155
157
  sessions: d.sessions,
156
158
  };
157
- }, [text]);
159
+ }, [text, parsedSessions]);
158
160
  const sessionTitles = useMemo(() => sessions.map((s) => s.title), [sessions]);
159
161
  const segments = useMemo(() => {
160
162
  const built = buildMentionSegments({ text: cleanText, sessionTitles, agentNames });
@@ -2,6 +2,11 @@ import { type UseSessionResult } from '@kortix/sdk/react';
2
2
  export { SessionReportCard, deriveTurnErrorAbortState, deriveTurnErrorPresentation } from './transcript.js';
3
3
  interface SessionChatProps {
4
4
  sessionId: string;
5
+ /** A host's reply quote (`SessionThreadProps.quote`): each new id adds its text to the composer's quotes. */
6
+ hostQuote?: {
7
+ id: number;
8
+ text: string;
9
+ } | null;
5
10
  /** Durable Kortix project session id used by project-session APIs. */
6
11
  projectSessionId?: string;
7
12
  /** Complete SDK state for the root session. Omit for a read-only child session. */
@@ -59,4 +64,4 @@ interface SessionChatProps {
59
64
  */
60
65
  deferComposerFocus?: boolean;
61
66
  }
62
- export declare function SessionChat({ sessionId, projectSessionId, sessionState, projectId, boundAgentName, headerLeadingAction, hideHeader, readOnly, inputReplacement, initialScrollTop, savedHistoryScope, onContentReady, deferComposerFocus, }: SessionChatProps): import("react").JSX.Element;
67
+ export declare function SessionChat({ sessionId, projectSessionId, sessionState, projectId, boundAgentName, headerLeadingAction, hideHeader, readOnly, inputReplacement, initialScrollTop, savedHistoryScope, onContentReady, deferComposerFocus, hostQuote, }: SessionChatProps): import("react").JSX.Element;
@@ -171,7 +171,7 @@ const TRANSCRIPT_THROTTLE_MS = 50;
171
171
  /** `useSessionMessages` input when no `useSession` owns this chat: reads nothing. */
172
172
  const DETACHED_SESSION_MESSAGES = { projectId: '', sessionId: '', runtimeSessionId: null };
173
173
  const NO_FULL_TEXTS = new Map();
174
- export function SessionChat({ sessionId, projectSessionId, sessionState, projectId, boundAgentName, headerLeadingAction, hideHeader, readOnly, inputReplacement, initialScrollTop, savedHistoryScope, onContentReady, deferComposerFocus, }) {
174
+ export function SessionChat({ sessionId, projectSessionId, sessionState, projectId, boundAgentName, headerLeadingAction, hideHeader, readOnly, inputReplacement, initialScrollTop, savedHistoryScope, onContentReady, deferComposerFocus, hostQuote, }) {
175
175
  const tHardcodedUi = useTranslations('hardcodedUi');
176
176
  const tQueue = useTranslations('threads');
177
177
  const tComposerAttachments = useTranslations('hardcodedUi.composerAttachments');
@@ -283,6 +283,17 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
283
283
  }
284
284
  lastScrollTopRef.current = top;
285
285
  }, []);
286
+ // A host's quote: each new id is one more request, like a "Reply" click.
287
+ const hostQuoteIdRef = useRef(null);
288
+ useEffect(() => {
289
+ const text = hostQuote?.text.trim();
290
+ if (!hostQuote || !text || hostQuoteIdRef.current === hostQuote.id)
291
+ return;
292
+ hostQuoteIdRef.current = hostQuote.id;
293
+ quoteRequestIdRef.current += 1;
294
+ const request = { id: quoteRequestIdRef.current, text };
295
+ setQuoteRequests((current) => [...current, request]);
296
+ }, [hostQuote]);
286
297
  // When user clicks "Reply" in the popup
287
298
  const handleSelectionReply = useCallback(() => {
288
299
  if (!selectionPopup)
@@ -1477,6 +1488,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
1477
1488
  held: heldTurnIds,
1478
1489
  pricing: pricingLookup,
1479
1490
  hideOutOfContext,
1491
+ foldCompactions: true,
1480
1492
  }), [lastTurnWorking, heldTurnIds, pricingLookup, hideOutOfContext]);
1481
1493
  // WHICH turn carries the shimmer (`live`), and which user bubbles are still
1482
1494
  // queued at the agent (`queued`): the working projection's turn, else this
@@ -1948,7 +1960,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
1948
1960
  // A compaction turn of any state (`turn.compaction`), so a failed attempt's
1949
1961
  // turn suppresses the optimistic "Compacting…" marker instead of leaving it
1950
1962
  // shimmering beside the failure row.
1951
- const hasCompactionTurn = useMemo(() => turns.some((turn) => turn.compaction !== null), [turns]);
1963
+ const hasCompactionTurn = useMemo(() => turns.some((turn) => turn.compaction !== null || turn.blocks.some((block) => block.type === 'compaction')), [turns]);
1952
1964
  // Index of the LAST compaction turn (any state). Every FAILED attempt
1953
1965
  // before it is history the reader retried past — a run of retries renders
1954
1966
  // as ONE failure row (the latest), not a stack of near-identical lines.
@@ -3382,6 +3394,13 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
3382
3394
  // when the host router observes history and routes through it otherwise.
3383
3395
  shallowNavigate(qs ? `${pathname}?${qs}` : pathname, { replace: true });
3384
3396
  }, [readOnly, pathname, searchParams, shallowNavigate]);
3397
+ // A session tab stays mounted, so a later `?cr=` (the turn-done toast's
3398
+ // Review) must open the dialog too, not only the one read at mount.
3399
+ const crParam = readOnly ? null : (searchParams?.get('cr') ?? null);
3400
+ useEffect(() => {
3401
+ if (crParam)
3402
+ setOpenCrIdState(crParam);
3403
+ }, [crParam]);
3385
3404
  const handleOpenOutcome = useCallback((outcome) => {
3386
3405
  // Change requests are the only kind a turn produces. `external` exists on
3387
3406
  // the union for `setup-links/setup-link-button.tsx`, which renders this
@@ -3448,7 +3467,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
3448
3467
  // it — the "extra inset at the bottom of the session".
3449
3468
  className: SESSION_TRANSCRIPT_CLASS, children: [_jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx(SessionPrintHeader, { title: session?.title || 'Untitled', agentName: composerAgentName }), (hasOlder || showOlderLoading) && (_jsxs("div", { className: "mb-6 flex flex-col items-center gap-2", children: [hasOlder && (_jsx("div", { ref: olderSentinelRef, "aria-hidden": true, className: "h-px w-full" })), showOlderLoading && (_jsxs("div", { role: "status", className: "text-muted-foreground flex items-center gap-2 py-1 text-xs", children: [_jsx(Loading, { className: "size-3.5 shrink-0" }), tHardcodedUi.raw('i18nComplete.text85bf890776a7')] })), !showOlderLoading &&
3450
3469
  !olderPullFailed &&
3451
- olderAutoloadExhausted({ hasOlder, autoLoadedPages }) && (_jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.textf17671d83db0') })), olderPullFailed && !showOlderLoading && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: tHardcodedUi.raw('i18nComplete.textb03a0041ce33') }), _jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') })] }))] })), restoreNotice && !restoreDismissed && (_jsx(RestoreNotice, { notice: restoreNotice, onDismiss: () => dismissRestore(historyKey, restoreNotice.generation) })), _jsx(SessionOutcomesProvider, { projectSessionId: projectSessionId, turnSpans: turnSpans, onOpen: handleOpenOutcome, children: _jsxs(ToolActivateContext.Provider, { value: toolActivate, children: [showFirstPromptPreview &&
3470
+ olderAutoloadExhausted({ hasOlder, autoLoadedPages }) && (_jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.textf17671d83db0') })), olderPullFailed && !showOlderLoading && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: tHardcodedUi.raw('i18nComplete.textb03a0041ce33') }), _jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') })] }))] })), restoreNotice && !restoreDismissed && (_jsx(RestoreNotice, { notice: restoreNotice, onDismiss: () => dismissRestore(historyKey, restoreNotice.generation) })), _jsx(SessionOutcomesProvider, { projectSessionId: projectSessionId, turnSpans: turnSpans, busy: effectiveBusy, onOpen: handleOpenOutcome, children: _jsxs(ToolActivateContext.Provider, { value: toolActivate, children: [showFirstPromptPreview &&
3452
3471
  firstPromptSource &&
3453
3472
  queuedSyntheticMessages.length === 0 && (_jsx(OptimisticTurn, { text: buildOptimisticPromptTextWithUploads(firstPromptSource.text, firstPromptSource.files),
3454
3473
  // Preserve a real failed-send status through the
@@ -21,6 +21,15 @@ export interface SessionThreadProps {
21
21
  hideHeader?: boolean;
22
22
  /** Hide the composer: a transcript to read, not to answer. */
23
23
  readOnly?: boolean;
24
+ /**
25
+ * A quote the host adds to the composer's reply quotes (the card above the
26
+ * input), as a transcript selection's "Reply" does. Each new `id` adds its
27
+ * `text` once.
28
+ */
29
+ quote?: {
30
+ id: number;
31
+ text: string;
32
+ } | null;
24
33
  className?: string;
25
34
  }
26
- export declare function SessionThread({ session, hideHeader, readOnly, className }: SessionThreadProps): import("react").JSX.Element;
35
+ export declare function SessionThread({ session, hideHeader, readOnly, quote, className }: SessionThreadProps): import("react").JSX.Element;
@@ -7,7 +7,7 @@ import { SessionPanelFallback } from '../panel/panel-fallback.js';
7
7
  import { SessionChat } from './session-chat.js';
8
8
  import { SessionStartingLoader } from '../status/session-starting-loader.js';
9
9
  import '../tool/tools/register.js';
10
- export function SessionThread({ session, hideHeader = false, readOnly = false, className }) {
10
+ export function SessionThread({ session, hideHeader = false, readOnly = false, quote, className }) {
11
11
  const { projectId, sessionId, runtimeSessionId } = session;
12
- return (_jsx(ProjectScope, { projectId: projectId, sessionId: sessionId, children: _jsx(ProviderPoolDraftBoundary, { identity: `${projectId}/${sessionId}`, children: _jsxs("div", { "data-slot": "session-thread", className: cn('relative flex h-full min-h-0 flex-col', className), children: [runtimeSessionId ? (_jsx(SessionChat, { sessionId: runtimeSessionId, projectSessionId: sessionId, projectId: projectId, sessionState: session, hideHeader: hideHeader, readOnly: readOnly }, runtimeSessionId)) : (_jsx(SessionStartingLoader, { stage: session.stage === 'starting' ? 'starting' : 'provisioning', projectId: projectId, sessionId: sessionId })), _jsx(SessionPanelFallback, { projectId: projectId, projectSessionId: sessionId, runtimeSessionId: runtimeSessionId })] }) }) }));
12
+ return (_jsx(ProjectScope, { projectId: projectId, sessionId: sessionId, children: _jsx(ProviderPoolDraftBoundary, { identity: `${projectId}/${sessionId}`, children: _jsxs("div", { "data-slot": "session-thread", className: cn('relative flex h-full min-h-0 flex-col', className), children: [runtimeSessionId ? (_jsx(SessionChat, { sessionId: runtimeSessionId, projectSessionId: sessionId, projectId: projectId, sessionState: session, hideHeader: hideHeader, readOnly: readOnly, hostQuote: quote }, runtimeSessionId)) : (_jsx(SessionStartingLoader, { stage: session.stage === 'starting' ? 'starting' : 'provisioning', projectId: projectId, sessionId: sessionId })), _jsx(SessionPanelFallback, { projectId: projectId, projectSessionId: sessionId, runtimeSessionId: runtimeSessionId })] }) }) }));
13
13
  }
@@ -5,6 +5,7 @@ import { ActivityGroupStep } from './activity-group-step.js';
5
5
  import { ActivityStep, StepLabelRow } from './activity-step.js';
6
6
  import { AnsweredQuestionStep, isAnsweredQuestionCall } from './answered-question-step.js';
7
7
  import { stepLabel } from '@kortix/sdk';
8
+ import { CompactionMarker } from './compaction-card.js';
8
9
  import { ToolRegistry } from '../tool/shared/registry.js';
9
10
  /** A host's `ToolRegistry.register` row for this tool wins over the chip and question rows. */
10
11
  const hostRow = (call) => ToolRegistry.replaced(call.name, call.kind);
@@ -44,6 +45,9 @@ export function StepBody({ step, bareRow = false, running, sessionId, disableNav
44
45
  const calls = step.blocks;
45
46
  return isFileChipRun(calls) ? (_jsx(ActivityFileChipStep, { calls: calls, sessionId: sessionId, disableNavigation: disableNavigation })) : (_jsx(ActivityGroupStep, { step: { id: calls[0].call_id, family: step.family, label: step.label, parts: calls, status: step.status, durationMs: step.durationMs }, sessionId: sessionId, disableNavigation: disableNavigation }));
46
47
  }
48
+ if (step.block.type === 'compaction') {
49
+ return _jsx(CompactionMarker, { running: running && step.block.summary === null, summary: step.block.summary ?? undefined });
50
+ }
47
51
  // Neither tool nor reasoning: the label row only. The turn modules never
48
52
  // silently drop a block (`stepLabel` falls back to a generic 'Used' label).
49
53
  if (step.block.type !== 'tool_call') {