@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.
- package/README.md +8 -4
- package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
- package/dist/customize/sections/llm-provider/constants.js +0 -2
- package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
- package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
- package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
- package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
- package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
- package/dist/customize/sections/llm-provider/models-tab.js +1 -10
- package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
- package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
- package/dist/customize/sections/llm-provider/utils.js +7 -14
- package/dist/customize/sections/view/gateway/_shared.js +1 -2
- package/dist/customize/sections/view/gateway/gateway-routing.js +3 -2
- package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
- package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
- package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
- package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
- package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
- package/dist/customize/shared/entity/entity-modal.js +7 -4
- package/dist/customize/skills/remove-skill-button.d.ts +16 -0
- package/dist/customize/skills/remove-skill-button.js +42 -0
- package/dist/customize/skills/skills-page.js +1 -1
- package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
- package/dist/customize/triggers/schedule/trigger-list.js +1 -18
- package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
- package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
- package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
- package/dist/files/project/hooks/use-change-requests.js +44 -25
- package/dist/files/renderers/docx/docx-viewer.js +4 -1
- package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
- package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
- package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
- package/dist/files/renderers/pdf/pdf-viewer.js +15 -6
- package/dist/files/viewer/file-content-renderer.d.ts +4 -1
- package/dist/files/viewer/file-content-renderer.js +2 -2
- package/dist/i18n/en.d.ts +26 -35
- package/dist/i18n/en.js +35 -44
- package/dist/index.d.ts +1 -1
- package/dist/internal/hooks/use-notification-center.js +21 -15
- package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
- package/dist/internal/hooks/use-review-session-summary.js +11 -0
- package/dist/internal/lib/browser-error-noise.d.ts +1 -1
- package/dist/internal/lib/browser-error-noise.js +1 -1
- package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
- package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
- package/dist/internal/lib/provider-branding.js +14 -3
- package/dist/internal/lib/trigger-clock.d.ts +42 -0
- package/dist/internal/lib/trigger-clock.js +34 -0
- package/dist/internal/lib/web-notifications.d.ts +6 -0
- package/dist/internal/lib/web-notifications.js +57 -7
- package/dist/internal/notifications/notification-bell.d.ts +13 -0
- package/dist/internal/notifications/notification-bell.js +1 -1
- package/dist/internal/reminders/reminder-filter.d.ts +12 -0
- package/dist/internal/reminders/reminder-filter.js +34 -0
- package/dist/internal/reminders/reminder-format.d.ts +6 -0
- package/dist/internal/reminders/reminder-format.js +20 -2
- package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
- package/dist/internal/stores/session-hover-store.d.ts +20 -5
- package/dist/internal/stores/session-hover-store.js +31 -13
- package/dist/internal/stores/session-panel-memory-store.js +2 -0
- package/dist/primitives/count-badge.d.ts +3 -1
- package/dist/primitives/count-badge.js +3 -3
- package/dist/primitives/glyph-paths-source.js +1 -1
- package/dist/primitives/glyph-paths.generated.js +1 -1
- package/dist/providers/ui-provider.d.ts +57 -0
- package/dist/providers/ui-provider.js +4 -0
- package/dist/session/composer/ComposerCard.d.ts +2 -0
- package/dist/session/composer/ComposerCard.js +5 -1
- package/dist/session/composer/agent-selector.js +12 -5
- package/dist/session/composer/composer-toolbar.js +4 -2
- package/dist/session/composer/composer-underbar.js +3 -1
- package/dist/session/composer/composer.js +34 -5
- package/dist/session/composer/menus/slash-actions.d.ts +22 -2
- package/dist/session/composer/menus/slash-actions.js +20 -0
- package/dist/session/composer/menus/slash-items.js +19 -3
- package/dist/session/header/reminder-parts.d.ts +285 -0
- package/dist/session/header/reminder-parts.js +115 -0
- package/dist/session/header/session-reminders-indicator.d.ts +13 -0
- package/dist/session/header/session-reminders-indicator.js +142 -35
- package/dist/session/models/model-availability.d.ts +23 -0
- package/dist/session/models/model-availability.js +20 -0
- package/dist/session/models/model-grouping.js +1 -4
- package/dist/session/models/model-selector.js +23 -14
- package/dist/session/models/use-model-connection-gate.js +4 -1
- package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
- package/dist/session/outcomes/session-outcomes-provider.js +31 -3
- package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
- package/dist/session/overrides/session-overrides-composer.js +5 -1
- package/dist/session/panel/mobile-tool-drawer.js +4 -2
- package/dist/session/panel/open-session-quick-view.js +2 -1
- package/dist/session/panel/panel-fallback.js +8 -4
- package/dist/session/panel/session-panel-provider.d.ts +1 -0
- package/dist/session/panel/session-panel-provider.js +13 -1
- package/dist/session/panel/session-reminders-panel.d.ts +4 -0
- package/dist/session/panel/session-reminders-panel.js +109 -0
- package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
- package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
- package/dist/session/print/session-print-header.d.ts +0 -10
- package/dist/session/print/session-print-header.js +16 -1
- package/dist/session/status/session-surface.js +1 -1
- package/dist/session/thread/optimistic-turn.d.ts +5 -1
- package/dist/session/thread/optimistic-turn.js +8 -6
- package/dist/session/thread/session-chat.d.ts +6 -1
- package/dist/session/thread/session-chat.js +22 -3
- package/dist/session/thread/session-thread.d.ts +10 -1
- package/dist/session/thread/session-thread.js +2 -2
- package/dist/session/turn/burst-step-body.js +4 -0
- package/dist/styles.css +1 -1
- package/dist/workspace/sidebar/project-session-list.js +52 -55
- package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
- package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
- package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
- package/dist/workspace/sidebar/session-status-mark.js +63 -17
- package/package.json +7 -3
- package/skills/kortix-ui/SKILL.md +1 -1
- package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
- 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
|
|
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') {
|