@kortix/ui 0.14.14 → 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.
Files changed (37) hide show
  1. package/dist/customize/agents/agent-tabs.d.ts +1 -0
  2. package/dist/customize/agents/agent-tabs.js +1 -0
  3. package/dist/customize/shared/capability-library.js +7 -2
  4. package/dist/files/drives/drive-storage.d.ts +47 -0
  5. package/dist/files/drives/drive-storage.js +144 -0
  6. package/dist/files/drives/session-drives-chip.js +20 -2
  7. package/dist/files/index.d.ts +1 -0
  8. package/dist/files/index.js +1 -0
  9. package/dist/files/project/components/drive-explorer.js +13 -5
  10. package/dist/files/project/components/drive-upload-tray.js +1 -1
  11. package/dist/files/project/upload-batch.d.ts +3 -1
  12. package/dist/files/project/upload-batch.js +5 -2
  13. package/dist/i18n/en.d.ts +32 -12
  14. package/dist/i18n/en.js +33 -13
  15. package/dist/internal/chrome/use-chrome-offered.d.ts +14 -5
  16. package/dist/internal/chrome/use-chrome-offered.js +15 -6
  17. package/dist/session/composer/context-usage.d.ts +2 -0
  18. package/dist/session/composer/context-usage.js +7 -0
  19. package/dist/session/header/compact-modal.js +5 -2
  20. package/dist/session/header/session-site-header.js +5 -2
  21. package/dist/session/thread/transcript.js +2 -2
  22. package/dist/session/tool/tools/kortix-tool-card.js +1 -1
  23. package/dist/session/turn/burst-step-body.js +2 -2
  24. package/dist/session/turn/compaction-card.d.ts +23 -1
  25. package/dist/session/turn/compaction-card.js +20 -8
  26. package/dist/styles.css +1 -1
  27. package/dist/workspace/sidebar/project-nav-items.d.ts +0 -11
  28. package/dist/workspace/sidebar/project-nav-items.js +10 -77
  29. package/dist/workspace/sidebar/project-sidebar.js +4 -3
  30. package/package.json +3 -7
  31. package/skills/kortix-ui/references/modules.md +1 -1
  32. package/dist/internal/stores/sidebar-nav-store.d.ts +0 -78
  33. package/dist/internal/stores/sidebar-nav-store.js +0 -125
  34. package/dist/workspace/sidebar/customize-sidebar-modal.d.ts +0 -9
  35. package/dist/workspace/sidebar/customize-sidebar-modal.js +0 -198
  36. package/dist/workspace/sidebar/nav-row-meta.d.ts +0 -14
  37. 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/80 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-foreground/80 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-foreground/80 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({
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 ── [⛁ Compacting context…] ── 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/90 [&_h1]:text-foreground [&_h2]:text-foreground [&_h3]:text-foreground [&_strong]:text-foreground/90 p-6 text-sm", children: _jsx(SandboxUrlDetector, { content: summary, isStreaming: false }) }));
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: COMPACTION_LABEL_LOADING })] })) : hasSummary ? (_jsxs("button", { type: "button", "aria-expanded": opensDetail ? undefined : open, "aria-label": opensDetail
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/70 size-3 shrink-0" })) : (_jsx(CaretDownIcon, { className: cn('text-muted-foreground/70 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 }) }))] }));
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/70 font-normal", children: [" \u00B7 ", error] })) : null] })] }));
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';