@kortix/ui 0.14.13 → 0.14.15
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/dist/customize/agents/agent-tabs.d.ts +1 -0
- package/dist/customize/agents/agent-tabs.js +1 -0
- package/dist/customize/shared/capability-library.js +7 -2
- package/dist/files/drives/drive-storage.d.ts +47 -0
- package/dist/files/drives/drive-storage.js +144 -0
- package/dist/files/drives/session-drives-chip.js +20 -2
- package/dist/files/index.d.ts +1 -0
- package/dist/files/index.js +1 -0
- package/dist/files/project/components/drive-explorer.js +13 -5
- package/dist/files/project/components/drive-upload-tray.js +1 -1
- package/dist/files/project/upload-batch.d.ts +3 -1
- package/dist/files/project/upload-batch.js +5 -2
- package/dist/i18n/en.d.ts +32 -12
- package/dist/i18n/en.js +33 -13
- package/dist/internal/chrome/use-chrome-offered.d.ts +14 -5
- package/dist/internal/chrome/use-chrome-offered.js +15 -6
- package/dist/session/composer/context-usage.d.ts +2 -0
- package/dist/session/composer/context-usage.js +7 -0
- package/dist/session/header/compact-modal.js +5 -2
- package/dist/session/header/session-site-header.js +5 -2
- package/dist/session/thread/transcript.js +2 -2
- package/dist/session/tool/tools/kortix-tool-card.js +1 -1
- package/dist/session/turn/burst-step-body.js +2 -2
- package/dist/session/turn/compaction-card.d.ts +23 -1
- package/dist/session/turn/compaction-card.js +20 -8
- package/dist/styles.css +1 -1
- package/dist/workspace/sidebar/project-nav-items.d.ts +0 -11
- package/dist/workspace/sidebar/project-nav-items.js +10 -77
- package/dist/workspace/sidebar/project-sidebar.js +4 -3
- package/package.json +3 -7
- package/skills/kortix-ui/references/modules.md +1 -1
- package/dist/internal/stores/sidebar-nav-store.d.ts +0 -78
- package/dist/internal/stores/sidebar-nav-store.js +0 -125
- package/dist/workspace/sidebar/customize-sidebar-modal.d.ts +0 -9
- package/dist/workspace/sidebar/customize-sidebar-modal.js +0 -198
- package/dist/workspace/sidebar/nav-row-meta.d.ts +0 -14
- package/dist/workspace/sidebar/nav-row-meta.js +0 -94
|
@@ -11,6 +11,8 @@ export interface ContextBreakdown {
|
|
|
11
11
|
/**
|
|
12
12
|
* The context size: the usage of the last reply (an assistant message, or a
|
|
13
13
|
* compaction, whose usage is its summary reply's) with a positive token total.
|
|
14
|
+
* A reset onto the record (a compaction block with `mode: 'record'`) reads 0
|
|
15
|
+
* until the next reply.
|
|
14
16
|
* `sessionLogThreadMetrics(...).context` picks the same message, but prices the
|
|
15
17
|
* whole thread to do it.
|
|
16
18
|
*
|
|
@@ -8,6 +8,8 @@ const EMPTY_BREAKDOWN = {
|
|
|
8
8
|
/**
|
|
9
9
|
* The context size: the usage of the last reply (an assistant message, or a
|
|
10
10
|
* compaction, whose usage is its summary reply's) with a positive token total.
|
|
11
|
+
* A reset onto the record (a compaction block with `mode: 'record'`) reads 0
|
|
12
|
+
* until the next reply.
|
|
11
13
|
* `sessionLogThreadMetrics(...).context` picks the same message, but prices the
|
|
12
14
|
* whole thread to do it.
|
|
13
15
|
*
|
|
@@ -23,6 +25,11 @@ export function getLastAssistantTokenBreakdown(messages) {
|
|
|
23
25
|
const msg = messages[i];
|
|
24
26
|
if (msg.role !== 'assistant' && msg.kind !== 'compaction')
|
|
25
27
|
continue;
|
|
28
|
+
// A reset onto the record made no model call: the context is near empty from here on, and
|
|
29
|
+
// the next reply measures it. Reading past it would show the size before the reset.
|
|
30
|
+
if (msg.kind === 'compaction' && msg.blocks.some((block) => block.type === 'compaction' && block.mode === 'record')) {
|
|
31
|
+
return EMPTY_BREAKDOWN;
|
|
32
|
+
}
|
|
26
33
|
const t = msg.usage;
|
|
27
34
|
if (!t)
|
|
28
35
|
continue;
|
|
@@ -3,11 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Button } from '../../primitives/button.js';
|
|
4
4
|
import { Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, } from '../../primitives/modal.js';
|
|
5
5
|
import { errorToast } from '../../primitives/toast.js';
|
|
6
|
-
import { useSummarizeRuntimeSession } from '@kortix/sdk/react';
|
|
6
|
+
import { useRuntimeSupports, useSummarizeRuntimeSession } from '@kortix/sdk/react';
|
|
7
7
|
import { useTranslations } from '../../i18n/index.js';
|
|
8
8
|
import { useCallback } from 'react';
|
|
9
9
|
export function CompactModal({ sessionId, open, onOpenChange, onCompactStart, }) {
|
|
10
10
|
const tHardcodedUi = useTranslations('hardcodedUi');
|
|
11
|
+
const tReset = useTranslations('contextReset');
|
|
12
|
+
// A runtime that resets onto the record summarizes nothing: the dialog says what happens there.
|
|
13
|
+
const record = useRuntimeSupports('session.context_record');
|
|
11
14
|
const summarize = useSummarizeRuntimeSession();
|
|
12
15
|
const handleCompact = useCallback(() => {
|
|
13
16
|
onCompactStart?.();
|
|
@@ -20,5 +23,5 @@ export function CompactModal({ sessionId, open, onOpenChange, onCompactStart, })
|
|
|
20
23
|
errorToast(error instanceof Error ? error.message : tHardcodedUi.raw('i18nComplete.texta4a83d949891'));
|
|
21
24
|
});
|
|
22
25
|
}, [onCompactStart, onOpenChange, summarize, sessionId, tHardcodedUi]);
|
|
23
|
-
return (_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsxs(ModalContent, { className: "lg:max-w-md", children: [_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: tHardcodedUi.raw('componentsSessionCompactDialog.line47JsxTextCompactSession') }), _jsx(ModalDescription, { children: tHardcodedUi.raw('componentsSessionCompactDialog.line50JsxTextThisWillSummarizeOlderMessagesUsingAiTo') })] }), _jsx(ModalBody, { children: _jsxs("div", { className: "text-muted-foreground space-y-1.5 text-sm", children: [_jsx("p", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line56JsxTextWhatHappensDuringCompaction') }), _jsxs("ul", { className: "list-inside list-disc space-y-0.5 pl-1", children: [_jsx("li", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line58JsxTextOlderMessagesAreSummarizedIntoAConciseRecap') }), _jsx("li", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line59JsxTextToolOutputsAndFileChangesArePreservedAs') }), _jsx("li", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line60JsxTextRecentMessagesRemainUnchanged') })] })] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "outline-ghost", size: "sm", className: "w-full sm:w-auto", onClick: () => onOpenChange(false), children: tHardcodedUi.raw('i18nComplete.text19766ed6ccb2') }), _jsx(Button, { size: "sm", className: "w-full sm:w-auto", onClick: handleCompact, children: tHardcodedUi.raw('i18nComplete.text99452646e34b') })] })] }) }));
|
|
26
|
+
return (_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsxs(ModalContent, { className: "lg:max-w-md", children: [record ? (_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: tReset('dialogTitle') }), _jsx(ModalDescription, { children: tReset('dialogDescription') })] })) : (_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: tHardcodedUi.raw('componentsSessionCompactDialog.line47JsxTextCompactSession') }), _jsx(ModalDescription, { children: tHardcodedUi.raw('componentsSessionCompactDialog.line50JsxTextThisWillSummarizeOlderMessagesUsingAiTo') })] })), !record && (_jsx(ModalBody, { children: _jsxs("div", { className: "text-muted-foreground space-y-1.5 text-sm", children: [_jsx("p", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line56JsxTextWhatHappensDuringCompaction') }), _jsxs("ul", { className: "list-inside list-disc space-y-0.5 pl-1", children: [_jsx("li", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line58JsxTextOlderMessagesAreSummarizedIntoAConciseRecap') }), _jsx("li", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line59JsxTextToolOutputsAndFileChangesArePreservedAs') }), _jsx("li", { children: tHardcodedUi.raw('componentsSessionCompactDialog.line60JsxTextRecentMessagesRemainUnchanged') })] })] }) })), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "outline-ghost", size: "sm", className: "w-full sm:w-auto", onClick: () => onOpenChange(false), children: tHardcodedUi.raw('i18nComplete.text19766ed6ccb2') }), _jsx(Button, { size: "sm", className: "w-full sm:w-auto", onClick: handleCompact, children: record ? tReset('dialogAction') : tHardcodedUi.raw('i18nComplete.text99452646e34b') })] })] }) }));
|
|
24
27
|
}
|
|
@@ -87,6 +87,9 @@ export function SessionSiteHeader({ sessionId, sessionTitle, isMobileView, leadi
|
|
|
87
87
|
const [compactOpen, setCompactOpen] = useState(false);
|
|
88
88
|
// E1: a runtime without on-demand compaction (pi) offers no Compact item.
|
|
89
89
|
const canCompact = useRuntimeSupports('session.compact');
|
|
90
|
+
// A runtime with the context record starts a fresh context: nothing is summarized.
|
|
91
|
+
const contextRecord = useRuntimeSupports('session.context_record');
|
|
92
|
+
const tReset = useTranslations('contextReset');
|
|
90
93
|
const [shareOpen, setShareOpen] = useState(false);
|
|
91
94
|
const { ShareSessionModal } = useUiComponents();
|
|
92
95
|
// The name edits in place: the name button swaps for a field. The menu's
|
|
@@ -258,7 +261,7 @@ export function SessionSiteHeader({ sessionId, sessionTitle, isMobileView, leadi
|
|
|
258
261
|
? err.message
|
|
259
262
|
: tI18nHardcoded.raw('i18nComplete.text32ad3abe4479'));
|
|
260
263
|
},
|
|
261
|
-
}), children: [forkSession.isPending ? _jsx(Loading, {}) : _jsx(GitForkIcon, {}), tI18nHardcoded.raw('i18nComplete.text0e5f7f6732e0')] }))] })), outOfContextPresent && (_jsx(DropdownMenuCheckboxItem, { className: "cursor-pointer", checked: !outOfContextHidden, onCheckedChange: (checked) => setOutOfContextHidden(historyKey, !checked), children: tHistory('showOutOfContext') })), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => setExportOpen(true), children: [_jsx(FileDown, {}), tI18nHardcoded.raw('i18nComplete.text5d974f9e80c3')] }), canCompact && (_jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => setCompactOpen(true), children: [_jsx(Layers, {}), tI18nHardcoded.raw('i18nComplete.textca838377bb5a')] })), isProjectSession && canManageLifecycle && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => setDeleteOpen(true), children: [_jsx(TrashIcon, {}), tI18nHardcoded.raw('i18nComplete.texte2d0a54968ea')] })] }))] }));
|
|
264
|
+
}), children: [forkSession.isPending ? _jsx(Loading, {}) : _jsx(GitForkIcon, {}), tI18nHardcoded.raw('i18nComplete.text0e5f7f6732e0')] }))] })), outOfContextPresent && (_jsx(DropdownMenuCheckboxItem, { className: "cursor-pointer", checked: !outOfContextHidden, onCheckedChange: (checked) => setOutOfContextHidden(historyKey, !checked), children: tHistory('showOutOfContext') })), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => setExportOpen(true), children: [_jsx(FileDown, {}), tI18nHardcoded.raw('i18nComplete.text5d974f9e80c3')] }), canCompact && (_jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => setCompactOpen(true), children: [_jsx(Layers, {}), contextRecord ? tReset('dialogAction') : tI18nHardcoded.raw('i18nComplete.textca838377bb5a')] })), isProjectSession && canManageLifecycle && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => setDeleteOpen(true), children: [_jsx(TrashIcon, {}), tI18nHardcoded.raw('i18nComplete.texte2d0a54968ea')] })] }))] }));
|
|
262
265
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative z-50 w-full", children: [_jsxs("div", { className: cn('flex items-center justify-between p-2 px-3.5 pt-2 pr-4',
|
|
263
266
|
// Unconditional on the shell. The LEFT indent depends on the
|
|
264
267
|
// sidebar (expanded, it covers the macOS lights itself) and is
|
|
@@ -287,7 +290,7 @@ export function SessionSiteHeader({ sessionId, sessionTitle, isMobileView, leadi
|
|
|
287
290
|
return;
|
|
288
291
|
renameFromMenu.current = false;
|
|
289
292
|
e.preventDefault();
|
|
290
|
-
}, children: sessionActionItems })] })] }), leadingAction] }), _jsxs("div", { className: cn('pointer-events-auto flex items-center gap-1.5', sidebarHidden && 'h-[var(--kx-titlebar-control-size)]'), children: [_jsx(SessionChangesIndicator, { sessionId: sessionId }), _jsx(SessionPendingApprovalsIndicator, { sessionId: sessionId }), isProjectSession ? _jsx(SessionGoalIndicator, {}) : null, isProjectSession ? _jsx(SessionRemindersIndicator, { runtimeSessionId: sessionId }) : null, isProjectSession && (_jsx(SessionConfigIndicator, { projectId: projectId, sessionId: projectSessionId, chatSessionId: sessionId, baseRef: projectSession?.base_ref, reload: reloadConfig.reload, isPending: reloadConfig.isPending, phase: reloadConfig.phase, canReload: canManageLifecycle })), _jsx("div", { className: "hidden items-center gap-1.5 lg:flex", children: devTools.map(({ view, label, Icon }) => (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: label, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": label, onClick: () => openSessionQuickView(view, 'header'), className: "text-foreground
|
|
293
|
+
}, children: sessionActionItems })] })] }), leadingAction] }), _jsxs("div", { className: cn('pointer-events-auto flex items-center gap-1.5', sidebarHidden && 'h-[var(--kx-titlebar-control-size)]'), children: [_jsx(SessionChangesIndicator, { sessionId: sessionId }), _jsx(SessionPendingApprovalsIndicator, { sessionId: sessionId }), isProjectSession ? _jsx(SessionGoalIndicator, {}) : null, isProjectSession ? _jsx(SessionRemindersIndicator, { runtimeSessionId: sessionId }) : null, isProjectSession && (_jsx(SessionConfigIndicator, { projectId: projectId, sessionId: projectSessionId, chatSessionId: sessionId, baseRef: projectSession?.base_ref, reload: reloadConfig.reload, isPending: reloadConfig.isPending, phase: reloadConfig.phase, canReload: canManageLifecycle })), _jsx("div", { className: "hidden items-center gap-1.5 lg:flex", children: devTools.map(({ view, label, Icon }) => (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: label, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": label, onClick: () => openSessionQuickView(view, 'header'), className: "text-muted-foreground hover:text-foreground cursor-pointer transition-colors active:scale-[0.96]", children: _jsx(Icon, { className: "h-4 w-4" }) }) }, view))) }), _jsxs(DropdownMenu, { children: [_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: tI18nHardcoded.raw('i18nComplete.text96f0c06bbcb7'), children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nHardcoded.raw('i18nComplete.text96f0c06bbcb7'), className: "text-muted-foreground hover:text-foreground cursor-pointer transition-colors active:scale-[0.96] lg:hidden", children: _jsx(Code2, { className: "h-4 w-4" }) }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-44", children: devTools.map(({ view, label, Icon }) => (_jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openSessionQuickView(view, 'header'), children: [_jsx(Icon, {}), label] }, view))) })] }), isProjectSession && projectSession && (_jsx(SessionParticipantStack, { participants: sessionParticipants })), isProjectSession && projectSession && ShareSessionModal && (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: shareLabel, open: isMobileViewport ? undefined : false, children: _jsxs(Button, { variant: "ghost", size: "xs", "aria-label": shareLabel, onClick: () => setShareOpen(true), className: "cursor-pointer rounded-md active:scale-[0.96] max-md:w-7 max-md:has-[>svg]:px-0", children: [_jsx(Share, {}), _jsx("span", { className: "hidden md:inline", children: tI18nHardcoded.raw('i18nComplete.text29887a5ff984') })] }) })), isMobileViewport && (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: tI18nHardcoded.raw('i18nComplete.text2534086de9fe'), children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nHardcoded.raw('i18nComplete.text2534086de9fe'), "aria-expanded": isActionPanelOpen, onClick: toggleActionPanel, className: "text-muted-foreground hover:text-foreground cursor-pointer transition-colors active:scale-[0.96]", children: _jsxs("span", { className: "relative inline-flex", children: [_jsx(CaretDoubleLeftIcon, { className: "size-4" }), readyChip?.sessionId === sessionId && !isActionPanelOpen && (_jsx("span", { className: "bg-kortix-green ring-background absolute -top-1 -right-1 size-2 rounded-full ring-2", "aria-hidden": true }))] }) }) }))] })] }), isProjectSession && _jsx(PreviousRepositoryNotice, {})] }), _jsx(ExportTranscriptModal, { sessionId: sessionId, kortixSessionScope: isProjectSession ? `${projectId}/${projectSessionId}` : undefined, open: exportOpen, onOpenChange: setExportOpen }), _jsx(CompactModal, { sessionId: sessionId, open: compactOpen, onOpenChange: setCompactOpen }), isProjectSession && (_jsxs(_Fragment, { children: [_jsx(SessionConfigReloadConfirm, { busyReason: reloadConfig.busyReason, isPending: reloadConfig.isPending, onConfirm: () => reloadConfig.reload({ force: true }), onDismiss: reloadConfig.clearBusy }), ShareSessionModal && (_jsx(ShareSessionModal, { projectId: projectId, session: projectSession, open: shareOpen, onOpenChange: setShareOpen, onSaved: () => queryClient.invalidateQueries({
|
|
291
294
|
queryKey: qk.project.sessionsScope(projectId ?? ''),
|
|
292
295
|
}) })), _jsx(SessionDeleteModal, { projectId: projectId, sessionId: projectSessionId, sessionLabel: projectTitle, open: deleteOpen, onOpenChange: setDeleteOpen, onDeleted: () => router.push(uiRoutes().project(projectId)) })] }))] }));
|
|
293
296
|
}
|
|
@@ -29,7 +29,7 @@ import { UserAvatar } from '../../primitives/user-avatar.js';
|
|
|
29
29
|
import { stripSystemPtyText, } from './message-parsing.js';
|
|
30
30
|
import { ActivityBurst } from '../turn/activity-burst.js';
|
|
31
31
|
import { TurnWorkAccordion } from '../turn/turn-work-accordion.js';
|
|
32
|
-
import { CompactionFailedRow, CompactionMarker } from '../turn/compaction-card.js';
|
|
32
|
+
import { CompactionFailedRow, CompactionMarker, compactionReset } from '../turn/compaction-card.js';
|
|
33
33
|
import { ExpandableOutput } from '../turn/expandable-output.js';
|
|
34
34
|
import { isPlanWriteTool } from '../turn/plan-anchor.js';
|
|
35
35
|
import { TurnFallbackRows } from '../turn/fallback-row.js';
|
|
@@ -962,7 +962,7 @@ function TurnCompactionOutcome(props) {
|
|
|
962
962
|
}, [onOpenCompactionSummary, turn.id, response]);
|
|
963
963
|
const compactionRunning = working || compactionInfo.inFlight;
|
|
964
964
|
if (compactionRunning || response || compactionInfo.hasContent) {
|
|
965
|
-
return (_jsx("div", { className: "group/turn", children: _jsx(CompactionMarker, { running: compactionRunning, summary: response, onOpenSummary: onOpenCompactionSummary ? openCompactionSummary : undefined }) }));
|
|
965
|
+
return (_jsx("div", { className: "group/turn", children: _jsx(CompactionMarker, { running: compactionRunning, summary: response, onOpenSummary: onOpenCompactionSummary ? openCompactionSummary : undefined, reset: compactionReset(compactionInfo.block) }) }));
|
|
966
966
|
}
|
|
967
967
|
// An attempt that produced nothing (errored, or stopped before the first
|
|
968
968
|
// token) collapses to one slim row. Falling through to the normal turn
|
|
@@ -180,7 +180,7 @@ export function parseKortixToolResult(raw, callName) {
|
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
/** The actions that only read; every other action changes something (the SDK narration uses the same set). */
|
|
183
|
-
const READ_ACTIONS = new Set(['', 'list', 'get', 'read', 'find', 'show', 'view', 'events', 'search', 'check', 'status', 'pending', 'who']);
|
|
183
|
+
const READ_ACTIONS = new Set(['', 'list', 'get', 'read', 'find', 'show', 'view', 'events', 'runs', 'test', 'search', 'check', 'status', 'pending', 'who']);
|
|
184
184
|
export function isReadAction(action) {
|
|
185
185
|
return READ_ACTIONS.has(typeof action === 'string' ? action : '');
|
|
186
186
|
}
|
|
@@ -5,7 +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
|
+
import { CompactionMarker, compactionReset } from './compaction-card.js';
|
|
9
9
|
import { ToolRegistry } from '../tool/shared/registry.js';
|
|
10
10
|
/** A host's `ToolRegistry.register` row for this tool wins over the chip and question rows. */
|
|
11
11
|
const hostRow = (call) => ToolRegistry.replaced(call.name, call.kind);
|
|
@@ -46,7 +46,7 @@ export function StepBody({ step, bareRow = false, running, sessionId, disableNav
|
|
|
46
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 }));
|
|
47
47
|
}
|
|
48
48
|
if (step.block.type === 'compaction') {
|
|
49
|
-
return _jsx(CompactionMarker, { running: running && step.block.summary === null, summary: step.block.summary ?? undefined });
|
|
49
|
+
return _jsx(CompactionMarker, { running: running && step.block.summary === null, summary: step.block.summary ?? undefined, reset: compactionReset(step.block) });
|
|
50
50
|
}
|
|
51
51
|
// Neither tool nor reasoning: the label row only. The turn modules never
|
|
52
52
|
// silently drop a block (`stepLabel` falls back to a generic 'Used' label).
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reset onto the session's searchable record (kortixd `extensions/context-record.ts`): the block's
|
|
3
|
+
* `mode: 'record'`. Nothing was summarized, so the pill explains the reset instead of opening the
|
|
4
|
+
* model's text.
|
|
5
|
+
*/
|
|
6
|
+
export interface CompactionReset {
|
|
7
|
+
/** The person started it (`/compact`, the Compact dialog). */
|
|
8
|
+
manual: boolean;
|
|
9
|
+
/** The context size when it reset. */
|
|
10
|
+
tokensBefore?: number;
|
|
11
|
+
/** The entries the record held at the reset. */
|
|
12
|
+
entries?: number;
|
|
13
|
+
}
|
|
14
|
+
/** The reset block's fields as the marker takes them; null for a summary compaction. */
|
|
15
|
+
export declare function compactionReset(block: {
|
|
16
|
+
mode?: 'record';
|
|
17
|
+
trigger?: string;
|
|
18
|
+
tokens_before?: number;
|
|
19
|
+
entries?: number;
|
|
20
|
+
} | null | undefined): CompactionReset | null;
|
|
1
21
|
/**
|
|
2
22
|
* The summary prose — ONE component whether it renders in the panel detail
|
|
3
23
|
* (the normal path) or inline under the pill (the no-panel fallback), so the
|
|
@@ -6,7 +26,9 @@
|
|
|
6
26
|
export declare function CompactionSummaryBody({ summary }: {
|
|
7
27
|
summary: string;
|
|
8
28
|
}): import("react").JSX.Element;
|
|
9
|
-
declare function CompactionMarkerImpl({ running, summary, onOpenSummary, }: {
|
|
29
|
+
declare function CompactionMarkerImpl({ running, summary, onOpenSummary, reset, }: {
|
|
30
|
+
/** A reset onto the record: the pill explains it inline, and `summary` is not shown. */
|
|
31
|
+
reset?: CompactionReset | null;
|
|
10
32
|
/** The summarize is still producing (optimistic, or the summary message is open). */
|
|
11
33
|
running: boolean;
|
|
12
34
|
/** The landed summary markdown, if any. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useTranslations } from '../../i18n/index.js';
|
|
3
|
+
import { useLocale, useTranslations } from '../../i18n/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Compaction, as ONE minimal marker — the divider pill IS the whole UI:
|
|
6
6
|
*
|
|
7
|
-
* 1. running (optimistic or streaming) → rule ── [⛁
|
|
7
|
+
* 1. running (optimistic or streaming) → rule ── [⛁ Freeing up context…] ── rule
|
|
8
8
|
* (shimmer label; the summary text is deliberately NOT streamed into the
|
|
9
9
|
* transcript — a wall of live markdown read as noise, grew the transcript
|
|
10
10
|
* under the reader, and made a slow summarize FEEL slower)
|
|
@@ -28,22 +28,34 @@ import { SandboxUrlDetector } from '../media/sandbox-url-detector.js';
|
|
|
28
28
|
const PILL_CLASS = 'bg-muted/80 flex shrink-0 items-center gap-2 rounded-md px-3 py-1.5';
|
|
29
29
|
const PILL_LABEL_CLASS = 'text-muted-foreground text-xs tracking-wide';
|
|
30
30
|
/** Transcript copy — sentence case, one idea per line (kortix-brand voice-and-tone.md). */
|
|
31
|
-
const COMPACTION_LABEL_LOADING = 'Compacting context…';
|
|
32
31
|
const COMPACTION_LABEL_DONE = 'Context automatically compacted';
|
|
32
|
+
/** The reset block's fields as the marker takes them; null for a summary compaction. */
|
|
33
|
+
export function compactionReset(block) {
|
|
34
|
+
if (block?.mode !== 'record')
|
|
35
|
+
return null;
|
|
36
|
+
return { manual: block.trigger === 'manual', tokensBefore: block.tokens_before, entries: block.entries };
|
|
37
|
+
}
|
|
33
38
|
/**
|
|
34
39
|
* The summary prose — ONE component whether it renders in the panel detail
|
|
35
40
|
* (the normal path) or inline under the pill (the no-panel fallback), so the
|
|
36
41
|
* two can never drift in typography.
|
|
37
42
|
*/
|
|
38
43
|
export function CompactionSummaryBody({ summary }) {
|
|
39
|
-
return (_jsx("div", { className: "text-muted-foreground
|
|
44
|
+
return (_jsx("div", { className: "text-muted-foreground [&_h1]:text-foreground [&_h2]:text-foreground [&_h3]:text-foreground [&_strong]:text-foreground p-6 text-sm", children: _jsx(SandboxUrlDetector, { content: summary, isStreaming: false }) }));
|
|
40
45
|
}
|
|
41
|
-
function CompactionMarkerImpl({ running, summary, onOpenSummary, }) {
|
|
46
|
+
function CompactionMarkerImpl({ running, summary, onOpenSummary, reset, }) {
|
|
42
47
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
48
|
+
const tReset = useTranslations('contextReset');
|
|
49
|
+
const locale = useLocale();
|
|
43
50
|
const [open, setOpen] = useState(false);
|
|
51
|
+
if (reset && !running) {
|
|
52
|
+
const tokens = new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 0 }).format(reset.tokensBefore ?? 0);
|
|
53
|
+
const detail = tReset(reset.manual ? 'detailManual' : 'detail', { tokens, entries: reset.entries ?? 0 });
|
|
54
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: "my-3 flex items-center gap-2 py-4", children: [_jsx("div", { className: "bg-border h-px flex-1" }), _jsxs("button", { type: "button", "aria-expanded": open, "aria-label": open ? tReset('hideDetail') : tReset('showDetail'), onClick: () => setOpen((v) => !v), className: cn(PILL_CLASS, 'hit-area-1 hover:bg-accent cursor-pointer transition-[background-color,scale] active:scale-[0.96]'), children: [_jsx(Layers, { weight: "duotone", className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("span", { className: PILL_LABEL_CLASS, children: tReset('pill') }), _jsx(CaretDownIcon, { className: cn('text-muted-foreground size-3 shrink-0 transition-transform', open && 'rotate-180') })] }), _jsx("div", { className: "bg-border h-px flex-1" })] }), open && _jsx("p", { className: "text-muted-foreground mx-auto max-w-prose px-6 pb-2 text-center text-sm", children: detail })] }));
|
|
55
|
+
}
|
|
44
56
|
const hasSummary = !running && Boolean(summary?.trim());
|
|
45
57
|
const opensDetail = hasSummary && Boolean(onOpenSummary);
|
|
46
|
-
return (_jsxs("div", { children: [_jsxs("div", { className: "my-3 flex items-center gap-2 py-4", children: [_jsx("div", { className: "bg-border h-px flex-1" }), running ? (_jsxs("div", { className: PILL_CLASS, children: [_jsx(Loading, { variant: "spokes", className: "text-muted-foreground size-3.5 shrink-0" }), _jsx(TextShimmer, { className: "text-xs tracking-wide", children:
|
|
58
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: "my-3 flex items-center gap-2 py-4", children: [_jsx("div", { className: "bg-border h-px flex-1" }), running ? (_jsxs("div", { className: PILL_CLASS, children: [_jsx(Loading, { variant: "spokes", className: "text-muted-foreground size-3.5 shrink-0" }), _jsx(TextShimmer, { className: "text-xs tracking-wide", children: tReset('pillRunning') })] })) : hasSummary ? (_jsxs("button", { type: "button", "aria-expanded": opensDetail ? undefined : open, "aria-label": opensDetail
|
|
47
59
|
? tI18nComplete.raw('textab742f4db494')
|
|
48
60
|
: open
|
|
49
61
|
? tI18nComplete.raw('text92bfa8a05e5a')
|
|
@@ -56,7 +68,7 @@ function CompactionMarkerImpl({ running, summary, onOpenSummary, }) {
|
|
|
56
68
|
}, className: cn(PILL_CLASS, 'hit-area-1 hover:bg-accent cursor-pointer transition-[background-color,scale] active:scale-[0.96]'), children: [_jsx(Layers, { weight: "duotone", className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("span", { className: PILL_LABEL_CLASS, children: COMPACTION_LABEL_DONE }), opensDetail ? (
|
|
57
69
|
// A right caret, not a chevron-down: the summary opens ELSEWHERE
|
|
58
70
|
// (the panel detail), the same grammar as a row that navigates.
|
|
59
|
-
_jsx(CaretRightIcon, { className: "text-muted-foreground
|
|
71
|
+
_jsx(CaretRightIcon, { className: "text-muted-foreground size-3 shrink-0" })) : (_jsx(CaretDownIcon, { className: cn('text-muted-foreground size-3 shrink-0 transition-transform', open && 'rotate-180') }))] })) : (_jsxs("div", { className: PILL_CLASS, children: [_jsx(Layers, { weight: "duotone", className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("span", { className: PILL_LABEL_CLASS, children: COMPACTION_LABEL_DONE })] })), _jsx("div", { className: "bg-border h-px flex-1" })] }), open && hasSummary && !opensDetail && (_jsx("div", { className: "pb-2", children: _jsx(CompactionSummaryBody, { summary: summary }) }))] }));
|
|
60
72
|
}
|
|
61
73
|
/** Primitive props, so per-token `summary` growth while running is a DOM no-op. */
|
|
62
74
|
export const CompactionMarker = memo(CompactionMarkerImpl);
|
|
@@ -76,7 +88,7 @@ function CompactionFailedRowImpl({ error, isAbort, }) {
|
|
|
76
88
|
: error
|
|
77
89
|
? 'Compaction failed'
|
|
78
90
|
: 'Compaction incomplete';
|
|
79
|
-
return (_jsxs(Checkpoint, { children: [_jsx(CheckpointIcon, { children: _jsx(Layers, { className: "text-muted-foreground size-4 shrink-0" }) }), _jsxs(CheckpointLabel, { title: !isAbort && error ? error : undefined, children: [label, !isAbort && error ? (_jsxs("span", { className: "text-muted-foreground
|
|
91
|
+
return (_jsxs(Checkpoint, { children: [_jsx(CheckpointIcon, { children: _jsx(Layers, { className: "text-muted-foreground size-4 shrink-0" }) }), _jsxs(CheckpointLabel, { title: !isAbort && error ? error : undefined, children: [label, !isAbort && error ? (_jsxs("span", { className: "text-muted-foreground font-normal", children: [" \u00B7 ", error] })) : null] })] }));
|
|
80
92
|
}
|
|
81
93
|
export const CompactionFailedRow = memo(CompactionFailedRowImpl);
|
|
82
94
|
CompactionFailedRow.displayName = 'CompactionFailedRow';
|