@alivault/pico 0.13.0 → 0.14.0
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/.output/nitro.json +1 -1
- package/.output/public/assets/{index-CW1T9dk-.js → index-DBVxXDtw.js} +4 -4
- package/.output/public/assets/routes-yUxD2q-l.js +2194 -0
- package/.output/public/assets/styles-CUVz7jV9.css +2 -0
- package/.output/public/screenshots/pico-ios-dark.png +0 -0
- package/.output/public/screenshots/pico-ios-light.png +0 -0
- package/.output/public/screenshots/pico-macos-dark.jpg +0 -0
- package/.output/public/screenshots/pico-macos-light.jpg +0 -0
- package/.output/server/_chunks/ssr-renderer.mjs +3 -3
- package/.output/server/_libs/crossws.mjs +2929 -0
- package/.output/server/_libs/h3+rou3+srvx.mjs +7 -1
- package/.output/server/_libs/unctx.mjs +1 -1
- package/.output/server/_routes/api/terminal/[id]/ws.mjs +8874 -0
- package/.output/server/_ssr/{router-l__gf5Y7.mjs → router-CN4JMm2A.mjs} +1074 -316
- package/.output/server/_ssr/{routes-DySm_W8N.mjs → routes-BsQ26pbY.mjs} +738 -189
- package/.output/server/_ssr/{routes-CuWg5WTI.mjs → routes-DbjuMLFI.mjs} +19 -10
- package/.output/server/_ssr/ssr.mjs +3 -3
- package/.output/server/_ssr/start-jIczsXlR.mjs +4 -0
- package/.output/server/{_tanstack-start-manifest_v-CVoKKKc_.mjs → _tanstack-start-manifest_v-Bs4g_j1_.mjs} +7 -4
- package/.output/server/index.mjs +621 -542
- package/.output/server/node_modules/node-pty/build/Makefile +2 -2
- package/.output/server/node_modules/node-pty/build/pty.target.mk +14 -14
- package/README.md +68 -6
- package/bin/pico.mjs +103 -1
- package/package.json +4 -4
- package/public/screenshots/pico-ios-dark.png +0 -0
- package/public/screenshots/pico-ios-light.png +0 -0
- package/public/screenshots/pico-macos-dark.jpg +0 -0
- package/public/screenshots/pico-macos-light.jpg +0 -0
- package/.output/public/assets/routes-2LboeUx4.js +0 -2194
- package/.output/public/assets/styles-B1ITYh-r.css +0 -2
- package/.output/server/_ssr/start-Captdpmb.mjs +0 -4
|
@@ -2,7 +2,7 @@ import { i as __toESM } from "../_runtime.mjs";
|
|
|
2
2
|
import { $ as require_react, A as TabsTab, B as Input, C as MenuPositioner, D as MenuGroupLabel, E as MenuItem, F as DialogPopup, H as useRender, I as DialogDescription$1, L as DialogClose$1, M as DialogTitle$1, N as DialogRoot, O as MenuGroup, P as DialogPortal$1, Q as require_jsx_runtime, R as DialogBackdrop, S as MenuRoot, T as MenuPopup, V as Button, Y as mergeProps, _ as AccordionItem$1, a as ContextMenuRoot, b as MenuTrigger, c as CollapsibleRoot, d as PopoverPortal, f as PopoverTrigger$1, g as AccordionHeader, h as AccordionTrigger$1, i as ContextMenuTrigger$1, j as TabsRoot, k as TabsList$1, l as PopoverPopup, m as AccordionPanel, n as ToggleGroup, o as CollapsiblePanel, p as PopoverRoot, r as Toggle, s as CollapsibleTrigger$1, t as AlertDialogRoot, u as PopoverPositioner, v as AccordionRoot, w as MenuPortal, x as MenuSubmenuRoot, y as MenuSubmenuTrigger, z as Separator } from "../_libs/@base-ui/react+[...].mjs";
|
|
3
3
|
import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
|
|
4
4
|
import { A as Folder, B as ChevronsUpDown, C as ListStart, D as GitPullRequestArrow, E as ImagePlus, F as EllipsisVertical, G as Check, I as Download, J as ArrowUpRight, K as ArrowUp, L as Copy, M as FileDiff, N as ExternalLink, O as GitBranch, P as Ellipsis, R as Command, S as Maximize2, U as ChevronRight, V as ChevronsDownUp, W as ChevronDown, X as ArrowLeft, Z as ArrowDown, _ as PanelLeftClose, a as TriangleAlert, b as Minus, c as Square, d as Settings2, f as Search, g as PanelLeftOpen, h as PanelLeft, i as Undo2, j as FolderPlus, k as GitCommitHorizontal, l as SquareTerminal, m as PanelRight, n as WandSparkles, o as Trash2, p as Plus, q as ArrowUpToLine, r as Upload, s as Tag, t as X, u as SquarePen, w as ListEnd, x as Minimize2, y as OctagonX } from "../_libs/lucide-react.mjs";
|
|
5
|
-
import { $ as normalizeSessionSelectionKeys, A as THEME_STORAGE_KEY, At as themeFamilyDescription, B as buildItemsFromSync, C as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, Ct as rememberStoredPromptDraft, D as TERMINAL_PANEL_OPEN_STORAGE_KEY, Dt as sameContextUsage, E as TERMINAL_PANEL_HEIGHT_STORAGE_KEY, Et as safeLocalStorageSetItem, F as TooltipTrigger, Ft as thinkingSummaryText, G as exploreShellCommandNameFromTool, I as VIEWER_CONTEXT_STORAGE_KEY, It as toolArgsAreIncompleteJsonObject, L as appliedThemeClass, Lt as toolCategoryFromTool, M as Tooltip$1, Mt as themeFamilyKeywords, N as TooltipContent, Nt as themeFamilyLabel, O as THEME_COLOR_MODE_STORAGE_KEY, Ot as sessionListEntryKey, Pt as themeFamilySupportsColorMode, Q as normalizePromptImage, R as appliedThemeClassColorMode, Rt as toolCouldBecomeExploreFromTool, S as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, St as readStoredThemeColorMode, T as Spinner, Tt as safeLocalStorageGetItem, U as createContextId, V as cn, W as createInitialSessionState, X as getSessionTitle, Y as flattenTree, _ as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, _t as readStoredSessionDoneSoundEnabled, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as promptDraftKeyMatchesOwner, b as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, bt as readStoredTerminalTabs, c as Kbd, ct as readStoredCenterMessages, d as PICO_DIFF_SHIKI_THEMES, dt as readStoredHideToolBlocks, et as normalizeStoredDirectoryList, f as PICO_SHIKI_VARIABLE_DEFAULTS, ft as readStoredPinnedSessionKeys, g as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, gt as readStoredSessionDoneDesktopNotificationsEnabled, h as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ht as readStoredRightSidebarOpen, i as CENTER_MESSAGES_STORAGE_KEY, it as promptDraftKey, j as TitleTooltip, jt as themeFamilyFixedMode, k as THEME_FAMILIES, kt as themeColorModeLabel, l as KeyboardShortcutContent, lt as readStoredCollapsedDirectories, m as RECENT_DIRECTORIES_STORAGE_KEY, mt as readStoredRecentDirectories, n as APPLIED_THEME_STORAGE_KEY, nt as normalizeThemeFamily, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as rawShellCommandTextFromTool, p as PINNED_SESSIONS_STORAGE_KEY, pt as readStoredPromptDraft, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as previewUrlForImage, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredAutoScrollEnabled, tt as normalizeThemeColorMode, ut as readStoredDraftDirectory, v as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, vt as readStoredSidebarDirectories, w as SIDEBAR_DIRECTORIES_STORAGE_KEY, wt as rememberStoredTerminalTabs, x as Route, xt as readStoredTheme, y as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, yt as readStoredTerminalPanelOpen } from "./routes-
|
|
5
|
+
import { $ as normalizeSessionSelectionKeys, A as THEME_STORAGE_KEY, At as themeFamilyDescription, B as buildItemsFromSync, C as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, Ct as rememberStoredPromptDraft, D as TERMINAL_PANEL_OPEN_STORAGE_KEY, Dt as sameContextUsage, E as TERMINAL_PANEL_HEIGHT_STORAGE_KEY, Et as safeLocalStorageSetItem, F as TooltipTrigger, Ft as thinkingSummaryText, G as exploreShellCommandNameFromTool, I as VIEWER_CONTEXT_STORAGE_KEY, It as toolArgsAreIncompleteJsonObject, L as appliedThemeClass, Lt as toolCategoryFromTool, M as Tooltip$1, Mt as themeFamilyKeywords, N as TooltipContent, Nt as themeFamilyLabel, O as THEME_COLOR_MODE_STORAGE_KEY, Ot as sessionListEntryKey, Pt as themeFamilySupportsColorMode, Q as normalizePromptImage, R as appliedThemeClassColorMode, Rt as toolCouldBecomeExploreFromTool, S as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, St as readStoredThemeColorMode, T as Spinner, Tt as safeLocalStorageGetItem, U as createContextId, V as cn, W as createInitialSessionState, X as getSessionTitle, Y as flattenTree, _ as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, _t as readStoredSessionDoneSoundEnabled, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as promptDraftKeyMatchesOwner, b as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, bt as readStoredTerminalTabs, c as Kbd, ct as readStoredCenterMessages, d as PICO_DIFF_SHIKI_THEMES, dt as readStoredHideToolBlocks, et as normalizeStoredDirectoryList, f as PICO_SHIKI_VARIABLE_DEFAULTS, ft as readStoredPinnedSessionKeys, g as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, gt as readStoredSessionDoneDesktopNotificationsEnabled, h as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ht as readStoredRightSidebarOpen, i as CENTER_MESSAGES_STORAGE_KEY, it as promptDraftKey, j as TitleTooltip, jt as themeFamilyFixedMode, k as THEME_FAMILIES, kt as themeColorModeLabel, l as KeyboardShortcutContent, lt as readStoredCollapsedDirectories, m as RECENT_DIRECTORIES_STORAGE_KEY, mt as readStoredRecentDirectories, n as APPLIED_THEME_STORAGE_KEY, nt as normalizeThemeFamily, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as rawShellCommandTextFromTool, p as PINNED_SESSIONS_STORAGE_KEY, pt as readStoredPromptDraft, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as previewUrlForImage, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredAutoScrollEnabled, tt as normalizeThemeColorMode, ut as readStoredDraftDirectory, v as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, vt as readStoredSidebarDirectories, w as SIDEBAR_DIRECTORIES_STORAGE_KEY, wt as rememberStoredTerminalTabs, x as Route, xt as readStoredTheme, y as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, yt as readStoredTerminalPanelOpen } from "./routes-DbjuMLFI.mjs";
|
|
6
6
|
import { i as useQuery, n as useIsMutating, o as useQueryClient, r as useIsFetching, t as useMutation } from "../_libs/tanstack__react-query.mjs";
|
|
7
7
|
import { t as useHotkeys } from "../_libs/tanstack__react-hotkeys.mjs";
|
|
8
8
|
import { a as matchesKeyboardEvent, i as batch, o as formatForDisplay, r as createStore } from "../_libs/@tanstack/hotkeys+[...].mjs";
|
|
@@ -22,7 +22,7 @@ import { t as remarkGfm } from "../_libs/remark-gfm.mjs";
|
|
|
22
22
|
import { a as getBuiltInSpriteSheet, i as getBuiltInFileIconColor, n as FileTree, r as createFileTreeIconResolver, t as useFileTree } from "../_libs/@pierre/trees+[...].mjs";
|
|
23
23
|
import { a as closestCenter, c as useDraggable, d as useSensors, f as CSS$1, i as PointerSensor, n as DragOverlay, t as DndContext, u as useSensor } from "../_libs/@dnd-kit/core+[...].mjs";
|
|
24
24
|
import { a as verticalListSortingStrategy, i as useSortable, n as arrayMove, r as horizontalListSortingStrategy, t as SortableContext } from "../_libs/dnd-kit__sortable.mjs";
|
|
25
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/routes-
|
|
25
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/routes-BsQ26pbY.js
|
|
26
26
|
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
27
27
|
var import_jsx_runtime = require_jsx_runtime();
|
|
28
28
|
var MOBILE_BREAKPOINT = 768;
|
|
@@ -1098,7 +1098,7 @@ function directoryDialogHasExactMatch(directoryPaths, normalizedQuery) {
|
|
|
1098
1098
|
return normalizedPath === normalizedQuery || normalizedDisplayPath === normalizedQuery;
|
|
1099
1099
|
});
|
|
1100
1100
|
}
|
|
1101
|
-
function splitDisplayPath
|
|
1101
|
+
function splitDisplayPath(value) {
|
|
1102
1102
|
if (!value) return {
|
|
1103
1103
|
leading: "",
|
|
1104
1104
|
trailing: ""
|
|
@@ -1121,7 +1121,7 @@ function splitDisplayPath$1(value) {
|
|
|
1121
1121
|
}
|
|
1122
1122
|
function DirectoryPathLabel$1({ path, prefix }) {
|
|
1123
1123
|
const displayPath = formatDirectoryDisplayPath(path);
|
|
1124
|
-
const { leading, trailing } = splitDisplayPath
|
|
1124
|
+
const { leading, trailing } = splitDisplayPath(displayPath);
|
|
1125
1125
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
1126
1126
|
className: "flex min-w-0 items-center text-foreground",
|
|
1127
1127
|
children: [
|
|
@@ -2429,7 +2429,7 @@ function formatShortcutLabel(hotkey) {
|
|
|
2429
2429
|
function matchesShortcutEvent(event, hotkey) {
|
|
2430
2430
|
return matchesKeyboardEvent(event, hotkey);
|
|
2431
2431
|
}
|
|
2432
|
-
function tildePath
|
|
2432
|
+
function tildePath(value) {
|
|
2433
2433
|
return value.replace(/^\/Users\/[^/]+(?=\/|$)/, "~").replace(/^\/home\/[^/]+(?=\/|$)/, "~");
|
|
2434
2434
|
}
|
|
2435
2435
|
function normalizeDirectories(directories) {
|
|
@@ -2647,6 +2647,8 @@ function AppShellSessionsDialog(props) {
|
|
|
2647
2647
|
}
|
|
2648
2648
|
function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, currentDirectory, knownDirectories, directorySessionsByPath, sessionStatusByKey, activeSessionId, activeSessionPath, onSelectSession, onRenameSession, onDeleteSession, onError }) {
|
|
2649
2649
|
const isMobile = useIsMobile();
|
|
2650
|
+
const [isGeneratingName, setIsGeneratingName] = import_react.useState(false);
|
|
2651
|
+
const [generatedNamePath, setGeneratedNamePath] = import_react.useState("");
|
|
2650
2652
|
const [state, dispatch] = import_react.useReducer(sessionsDialogReducer, initialSessionsDialogState);
|
|
2651
2653
|
const { query, scope, stage, selectedSessionKey, renameValue, localSessionNames, loadedSessionsByPath, loadingByPath } = state;
|
|
2652
2654
|
const normalizedCurrentDirectory = currentDirectory.trim();
|
|
@@ -2675,6 +2677,7 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2675
2677
|
})));
|
|
2676
2678
|
const sortedSessionItems = flatSessions.toSorted((left, right) => sessionActivityTime(right.entry) - sessionActivityTime(left.entry));
|
|
2677
2679
|
const selectedSession = selectedSessionKey ? flatSessions.find((session) => session.key === selectedSessionKey)?.entry : void 0;
|
|
2680
|
+
const canGenerateSelectedSessionName = Boolean(selectedSession?.path && !selectedSession.name?.trim() && generatedNamePath !== selectedSession.path);
|
|
2678
2681
|
const loading = visibleGroups.some((group) => group.loading);
|
|
2679
2682
|
const browseInputRef = import_react.useRef(null);
|
|
2680
2683
|
const deletePanelRef = import_react.useRef(null);
|
|
@@ -2755,6 +2758,7 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2755
2758
|
};
|
|
2756
2759
|
const renameSelectedSession = () => {
|
|
2757
2760
|
if (!selectedSession?.path) return;
|
|
2761
|
+
setGeneratedNamePath("");
|
|
2758
2762
|
dispatch({
|
|
2759
2763
|
type: "renameRequested",
|
|
2760
2764
|
value: selectedSession.title
|
|
@@ -2771,6 +2775,28 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2771
2775
|
name: nextName
|
|
2772
2776
|
});
|
|
2773
2777
|
};
|
|
2778
|
+
const generateSelectedSessionName = async () => {
|
|
2779
|
+
const targetPath = selectedSession?.path;
|
|
2780
|
+
if (!targetPath || isGeneratingName || !canGenerateSelectedSessionName) return;
|
|
2781
|
+
setIsGeneratingName(true);
|
|
2782
|
+
try {
|
|
2783
|
+
const response = await fetchJson(buildRequestUrl("/api/session/name", { contextId: viewerContextId }), {
|
|
2784
|
+
method: "POST",
|
|
2785
|
+
headers: { "content-type": "application/json" },
|
|
2786
|
+
body: JSON.stringify({ path: targetPath })
|
|
2787
|
+
});
|
|
2788
|
+
dispatch({
|
|
2789
|
+
type: "renameValueChanged",
|
|
2790
|
+
value: response.name
|
|
2791
|
+
});
|
|
2792
|
+
setGeneratedNamePath(targetPath);
|
|
2793
|
+
if (response.source === "heuristic" && response.reason) toast.info("Using heuristic session name", { description: response.reason });
|
|
2794
|
+
} catch (error) {
|
|
2795
|
+
onError?.(error);
|
|
2796
|
+
} finally {
|
|
2797
|
+
setIsGeneratingName(false);
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2774
2800
|
const deleteSelectedSession = () => {
|
|
2775
2801
|
if (!selectedSession?.path) return;
|
|
2776
2802
|
dispatch({ type: "deleteRequested" });
|
|
@@ -2855,7 +2881,7 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2855
2881
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandList, {
|
|
2856
2882
|
className: "max-h-none min-h-0 flex-1 md:max-h-[min(70vh,32rem)]",
|
|
2857
2883
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandEmpty, { children: loading ? "Loading sessions…" : "No sessions found." }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandGroup, {
|
|
2858
|
-
heading: `${scope === "all" ? "All sessions" : `Current directory: ${tildePath
|
|
2884
|
+
heading: `${scope === "all" ? "All sessions" : `Current directory: ${tildePath(currentDirectories[0] || "")}`}${loading ? " · loading" : ""}`,
|
|
2859
2885
|
children: [loading && sortedSessionItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandItem, {
|
|
2860
2886
|
value: "loading:sessions",
|
|
2861
2887
|
disabled: true,
|
|
@@ -2891,7 +2917,7 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2891
2917
|
children: entry.title
|
|
2892
2918
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
2893
2919
|
className: "line-clamp-1 text-xs text-muted-foreground",
|
|
2894
|
-
children: tildePath
|
|
2920
|
+
children: tildePath(entry.cwd || directory)
|
|
2895
2921
|
})]
|
|
2896
2922
|
}),
|
|
2897
2923
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
@@ -2941,25 +2967,37 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2941
2967
|
children: [
|
|
2942
2968
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
2943
2969
|
className: "flex items-center gap-2 border-b border-border/70 px-3 py-2",
|
|
2944
|
-
children: [
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
className: "
|
|
2957
|
-
children: "
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2970
|
+
children: [
|
|
2971
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
2972
|
+
type: "button",
|
|
2973
|
+
variant: "ghost",
|
|
2974
|
+
size: "icon-sm",
|
|
2975
|
+
onClick: () => {
|
|
2976
|
+
dispatch({ type: "browseRequested" });
|
|
2977
|
+
},
|
|
2978
|
+
"aria-label": "Back to sessions",
|
|
2979
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowLeft, {})
|
|
2980
|
+
}),
|
|
2981
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
2982
|
+
className: "min-w-0 flex-1",
|
|
2983
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
2984
|
+
className: "truncate text-sm font-medium",
|
|
2985
|
+
children: "Rename session"
|
|
2986
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
2987
|
+
className: "truncate text-xs text-muted-foreground",
|
|
2988
|
+
children: selectedSession?.title || "Selected session"
|
|
2989
|
+
})]
|
|
2990
|
+
}),
|
|
2991
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
|
|
2992
|
+
type: "button",
|
|
2993
|
+
variant: "ghost",
|
|
2994
|
+
disabled: isGeneratingName || !canGenerateSelectedSessionName,
|
|
2995
|
+
onClick: () => {
|
|
2996
|
+
generateSelectedSessionName();
|
|
2997
|
+
},
|
|
2998
|
+
children: [isGeneratingName ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WandSparkles, {}), "Generate"]
|
|
2999
|
+
})
|
|
3000
|
+
]
|
|
2963
3001
|
}),
|
|
2964
3002
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
2965
3003
|
className: "flex min-h-0 flex-1 items-center p-3",
|
|
@@ -2978,6 +3016,12 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2978
3016
|
dispatch({ type: "browseRequested" });
|
|
2979
3017
|
return;
|
|
2980
3018
|
}
|
|
3019
|
+
if (matchesShortcutEvent(event.nativeEvent, "Control+G")) {
|
|
3020
|
+
event.preventDefault();
|
|
3021
|
+
event.stopPropagation();
|
|
3022
|
+
generateSelectedSessionName();
|
|
3023
|
+
return;
|
|
3024
|
+
}
|
|
2981
3025
|
if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
|
|
2982
3026
|
event.preventDefault();
|
|
2983
3027
|
event.stopPropagation();
|
|
@@ -2989,13 +3033,20 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
|
|
|
2989
3033
|
}),
|
|
2990
3034
|
isMobile ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
2991
3035
|
className: "hidden flex-wrap items-center gap-x-3 gap-y-1 border-t border-border/70 px-3 py-2 text-xs text-muted-foreground md:flex",
|
|
2992
|
-
children: [
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
3036
|
+
children: [
|
|
3037
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
3038
|
+
className: "inline-flex items-center gap-1",
|
|
3039
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd$1, { children: formatShortcutLabel("Control+G") }), " Generate"]
|
|
3040
|
+
}),
|
|
3041
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
3042
|
+
className: "inline-flex items-center gap-1",
|
|
3043
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd$1, { children: "Enter" }), " Save"]
|
|
3044
|
+
}),
|
|
3045
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
3046
|
+
className: "inline-flex items-center gap-1",
|
|
3047
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd$1, { children: "Esc" }), " Back"]
|
|
3048
|
+
})
|
|
3049
|
+
]
|
|
2999
3050
|
})
|
|
3000
3051
|
]
|
|
3001
3052
|
});
|
|
@@ -7578,7 +7629,7 @@ function GitCommitDialogController({ viewerContextId, cwd, openStateRef, ref })
|
|
|
7578
7629
|
function FooterKbd({ children }) {
|
|
7579
7630
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children });
|
|
7580
7631
|
}
|
|
7581
|
-
function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueChange, onRenameSession }) {
|
|
7632
|
+
function RenameSessionDialog({ open, onOpenChange, renameValue, generatingName, canGenerateName, onRenameValueChange, onGenerateSessionName, onRenameSession }) {
|
|
7582
7633
|
const isMobile = useIsMobile();
|
|
7583
7634
|
const renameInputRef = import_react.useRef(null);
|
|
7584
7635
|
import_react.useEffect(() => {
|
|
@@ -7595,13 +7646,22 @@ function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueCha
|
|
|
7595
7646
|
className: "flex min-h-0 flex-1 flex-col",
|
|
7596
7647
|
children: [
|
|
7597
7648
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
7598
|
-
className: "border-b border-border/70 px-3 py-2",
|
|
7599
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.
|
|
7600
|
-
className: "
|
|
7601
|
-
children: "
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7649
|
+
className: "flex items-center gap-2 border-b border-border/70 px-3 py-2",
|
|
7650
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
7651
|
+
className: "min-w-0 flex-1",
|
|
7652
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
7653
|
+
className: "truncate text-sm font-medium",
|
|
7654
|
+
children: "Rename session"
|
|
7655
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
7656
|
+
className: "truncate text-xs text-muted-foreground",
|
|
7657
|
+
children: "Update the display name shown in the sidebar."
|
|
7658
|
+
})]
|
|
7659
|
+
}), isMobile ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
|
|
7660
|
+
type: "button",
|
|
7661
|
+
variant: "ghost",
|
|
7662
|
+
disabled: generatingName || !canGenerateName,
|
|
7663
|
+
onClick: onGenerateSessionName,
|
|
7664
|
+
children: [generatingName ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WandSparkles, {}), "Generate"]
|
|
7605
7665
|
})]
|
|
7606
7666
|
}),
|
|
7607
7667
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -7617,6 +7677,12 @@ function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueCha
|
|
|
7617
7677
|
onOpenChange(false);
|
|
7618
7678
|
return;
|
|
7619
7679
|
}
|
|
7680
|
+
if (event.ctrlKey && event.key.toLowerCase() === "g") {
|
|
7681
|
+
event.preventDefault();
|
|
7682
|
+
event.stopPropagation();
|
|
7683
|
+
onGenerateSessionName();
|
|
7684
|
+
return;
|
|
7685
|
+
}
|
|
7620
7686
|
if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
|
|
7621
7687
|
event.preventDefault();
|
|
7622
7688
|
event.stopPropagation();
|
|
@@ -7628,13 +7694,24 @@ function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueCha
|
|
|
7628
7694
|
}),
|
|
7629
7695
|
isMobile ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
7630
7696
|
className: "hidden flex-wrap items-center gap-x-3 gap-y-1 border-t border-border/70 px-3 py-2 text-xs text-muted-foreground md:flex",
|
|
7631
|
-
children: [
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7697
|
+
children: [
|
|
7698
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
7699
|
+
className: "inline-flex items-center gap-1",
|
|
7700
|
+
children: [
|
|
7701
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Ctrl" }),
|
|
7702
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "G" }),
|
|
7703
|
+
" Generate"
|
|
7704
|
+
]
|
|
7705
|
+
}),
|
|
7706
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
7707
|
+
className: "inline-flex items-center gap-1",
|
|
7708
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Enter" }), " Save"]
|
|
7709
|
+
}),
|
|
7710
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
7711
|
+
className: "inline-flex items-center gap-1",
|
|
7712
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Esc" }), " Cancel"]
|
|
7713
|
+
})
|
|
7714
|
+
]
|
|
7638
7715
|
})
|
|
7639
7716
|
]
|
|
7640
7717
|
});
|
|
@@ -7652,14 +7729,23 @@ function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueCha
|
|
|
7652
7729
|
}),
|
|
7653
7730
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerFooter, {
|
|
7654
7731
|
className: "border-t border-border/70",
|
|
7655
|
-
children: [
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7732
|
+
children: [
|
|
7733
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
|
|
7734
|
+
variant: "outline",
|
|
7735
|
+
disabled: generatingName || !canGenerateName,
|
|
7736
|
+
onClick: onGenerateSessionName,
|
|
7737
|
+
children: [generatingName ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WandSparkles, {}), "Generate"]
|
|
7738
|
+
}),
|
|
7739
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
7740
|
+
variant: "outline",
|
|
7741
|
+
onClick: () => onOpenChange(false),
|
|
7742
|
+
children: "Cancel"
|
|
7743
|
+
}),
|
|
7744
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
7745
|
+
onClick: onRenameSession,
|
|
7746
|
+
children: "Save"
|
|
7747
|
+
})
|
|
7748
|
+
]
|
|
7663
7749
|
})
|
|
7664
7750
|
]
|
|
7665
7751
|
})
|
|
@@ -7674,9 +7760,11 @@ function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueCha
|
|
|
7674
7760
|
children: body
|
|
7675
7761
|
});
|
|
7676
7762
|
}
|
|
7677
|
-
function RenameSessionDialogController({ ref, openStateRef, onRenameSession }) {
|
|
7763
|
+
function RenameSessionDialogController({ ref, openStateRef, viewerContextId, onRenameSession }) {
|
|
7678
7764
|
const [open, setOpen] = import_react.useState(false);
|
|
7679
7765
|
const [renameValue, setRenameValue] = import_react.useState("");
|
|
7766
|
+
const [isGeneratingName, setIsGeneratingName] = import_react.useState(false);
|
|
7767
|
+
const [canGenerateName, setCanGenerateName] = import_react.useState(false);
|
|
7680
7768
|
const targetPathRef = import_react.useRef("");
|
|
7681
7769
|
const openRef = import_react.useRef(open);
|
|
7682
7770
|
const setOpenState = (nextOpen) => {
|
|
@@ -7689,8 +7777,27 @@ function RenameSessionDialogController({ ref, openStateRef, onRenameSession }) {
|
|
|
7689
7777
|
if (!nextPath) return;
|
|
7690
7778
|
targetPathRef.current = nextPath;
|
|
7691
7779
|
setRenameValue(target.title);
|
|
7780
|
+
setCanGenerateName(!target.hasGeneratedName);
|
|
7692
7781
|
setOpenState(true);
|
|
7693
7782
|
};
|
|
7783
|
+
const generateSessionName = async () => {
|
|
7784
|
+
if (!viewerContextId || !targetPathRef.current || isGeneratingName || !canGenerateName) return;
|
|
7785
|
+
setIsGeneratingName(true);
|
|
7786
|
+
try {
|
|
7787
|
+
const response = await fetchJson(buildRequestUrl("/api/session/name", { contextId: viewerContextId }), {
|
|
7788
|
+
method: "POST",
|
|
7789
|
+
headers: { "content-type": "application/json" },
|
|
7790
|
+
body: JSON.stringify({ path: targetPathRef.current })
|
|
7791
|
+
});
|
|
7792
|
+
setRenameValue(response.name);
|
|
7793
|
+
setCanGenerateName(false);
|
|
7794
|
+
if (response.source === "heuristic" && response.reason) toast.info("Using heuristic session name", { description: response.reason });
|
|
7795
|
+
} catch (error) {
|
|
7796
|
+
toast.error(error instanceof Error ? error.message : "Failed to generate session name");
|
|
7797
|
+
} finally {
|
|
7798
|
+
setIsGeneratingName(false);
|
|
7799
|
+
}
|
|
7800
|
+
};
|
|
7694
7801
|
const submitRename = async () => {
|
|
7695
7802
|
if (await onRenameSession(targetPathRef.current, renameValue) === false) return;
|
|
7696
7803
|
setOpenState(false);
|
|
@@ -7701,7 +7808,8 @@ function RenameSessionDialogController({ ref, openStateRef, onRenameSession }) {
|
|
|
7701
7808
|
if (!entry.path) return;
|
|
7702
7809
|
openTarget({
|
|
7703
7810
|
path: entry.path,
|
|
7704
|
-
title: entry.title || ""
|
|
7811
|
+
title: entry.title || "",
|
|
7812
|
+
hasGeneratedName: Boolean(entry.name?.trim())
|
|
7705
7813
|
});
|
|
7706
7814
|
},
|
|
7707
7815
|
close: () => {
|
|
@@ -7715,7 +7823,12 @@ function RenameSessionDialogController({ ref, openStateRef, onRenameSession }) {
|
|
|
7715
7823
|
setOpenState(nextOpen);
|
|
7716
7824
|
},
|
|
7717
7825
|
renameValue,
|
|
7826
|
+
generatingName: isGeneratingName,
|
|
7827
|
+
canGenerateName: canGenerateName && Boolean(viewerContextId && targetPathRef.current),
|
|
7718
7828
|
onRenameValueChange: setRenameValue,
|
|
7829
|
+
onGenerateSessionName: () => {
|
|
7830
|
+
generateSessionName();
|
|
7831
|
+
},
|
|
7719
7832
|
onRenameSession: () => {
|
|
7720
7833
|
submitRename();
|
|
7721
7834
|
}
|
|
@@ -7819,19 +7932,22 @@ function DeleteSessionsDialog({ open, onOpenChange, title, description, targets,
|
|
|
7819
7932
|
})
|
|
7820
7933
|
]
|
|
7821
7934
|
});
|
|
7822
|
-
if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
7935
|
+
if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
|
|
7823
7936
|
open,
|
|
7824
7937
|
onOpenChange,
|
|
7825
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
7826
|
-
className: "max-h-[90svh] overflow-hidden",
|
|
7938
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
|
|
7939
|
+
className: "max-h-[90svh] overflow-hidden p-0",
|
|
7827
7940
|
children: [
|
|
7828
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
7941
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, {
|
|
7942
|
+
className: "sr-only",
|
|
7943
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: title }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: description })]
|
|
7944
|
+
}),
|
|
7829
7945
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
7830
|
-
className: "flex min-h-0 flex-1 flex-col overflow-hidden
|
|
7946
|
+
className: "flex min-h-0 flex-1 flex-col overflow-hidden",
|
|
7831
7947
|
children: body
|
|
7832
7948
|
}),
|
|
7833
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
7834
|
-
className: "border-t border-border/70",
|
|
7949
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, {
|
|
7950
|
+
className: "flex-col-reverse gap-2 border-t border-border/70 p-3 sm:flex-row",
|
|
7835
7951
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
7836
7952
|
variant: "outline",
|
|
7837
7953
|
onClick: () => onOpenChange(false),
|
|
@@ -8370,10 +8486,11 @@ var AppShellMoveSessionDirectoryDialogHost = import_react.memo(function AppShell
|
|
|
8370
8486
|
})
|
|
8371
8487
|
});
|
|
8372
8488
|
});
|
|
8373
|
-
var AppShellRenameSessionDialogHost = import_react.memo(function AppShellRenameSessionDialogHost({ renameDialogRef, renameOpenRef, renameSessionPath }) {
|
|
8489
|
+
var AppShellRenameSessionDialogHost = import_react.memo(function AppShellRenameSessionDialogHost({ renameDialogRef, renameOpenRef, renameSessionPath, viewerContextId }) {
|
|
8374
8490
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RenameSessionDialogController, {
|
|
8375
8491
|
ref: renameDialogRef,
|
|
8376
8492
|
openStateRef: renameOpenRef,
|
|
8493
|
+
viewerContextId,
|
|
8377
8494
|
onRenameSession: renameSessionPath
|
|
8378
8495
|
});
|
|
8379
8496
|
});
|
|
@@ -8515,7 +8632,8 @@ var AppShellFloatingControllers = import_react.memo(function AppShellFloatingCon
|
|
|
8515
8632
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellRenameSessionDialogHost, {
|
|
8516
8633
|
renameDialogRef,
|
|
8517
8634
|
renameOpenRef,
|
|
8518
|
-
renameSessionPath
|
|
8635
|
+
renameSessionPath,
|
|
8636
|
+
viewerContextId
|
|
8519
8637
|
}),
|
|
8520
8638
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellDeleteSessionsDialogHost, {
|
|
8521
8639
|
deleteDialogRef,
|
|
@@ -8861,7 +8979,7 @@ function pendingMessageEditReducer(state, action) {
|
|
|
8861
8979
|
};
|
|
8862
8980
|
}
|
|
8863
8981
|
}
|
|
8864
|
-
var ComposerPendingMessages = import_react.memo(function ComposerPendingMessages({ currentPendingMessages, onEditPendingMessage, onRemovePendingMessage, onReorderPending }) {
|
|
8982
|
+
var ComposerPendingMessages = import_react.memo(function ComposerPendingMessages({ currentPendingMessages, canStartQueue, onEditPendingMessage, onRemovePendingMessage, onReorderPending, onStartPendingQueue }) {
|
|
8865
8983
|
const [editState, dispatchEditState] = import_react.useReducer(pendingMessageEditReducer, EMPTY_PENDING_MESSAGE_EDIT_STATE);
|
|
8866
8984
|
const [openSections, setOpenSections] = import_react.useState([]);
|
|
8867
8985
|
const { editingPendingId, editText, editError } = editState;
|
|
@@ -8900,19 +9018,26 @@ var ComposerPendingMessages = import_react.memo(function ComposerPendingMessages
|
|
|
8900
9018
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionItem, {
|
|
8901
9019
|
value: "pending-prompts",
|
|
8902
9020
|
className: "border-0",
|
|
8903
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.
|
|
8904
|
-
className: "min-h-10 items-center gap-
|
|
8905
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.
|
|
8906
|
-
className: "
|
|
8907
|
-
children:
|
|
8908
|
-
className: "
|
|
8909
|
-
children: "
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
9021
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
9022
|
+
className: "flex min-h-10 items-center gap-2 px-3 py-2",
|
|
9023
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccordionTrigger, {
|
|
9024
|
+
className: "min-h-0 flex-1 p-0 hover:no-underline",
|
|
9025
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
9026
|
+
className: "flex min-w-0 items-center gap-2 text-sm font-medium",
|
|
9027
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
9028
|
+
className: "truncate",
|
|
9029
|
+
children: "Queue"
|
|
9030
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
|
|
9031
|
+
variant: "outline",
|
|
9032
|
+
className: "shrink-0",
|
|
9033
|
+
children: currentPendingMessages.length
|
|
9034
|
+
})]
|
|
9035
|
+
})
|
|
9036
|
+
}), canStartQueue ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
9037
|
+
size: "sm",
|
|
9038
|
+
onClick: onStartPendingQueue,
|
|
9039
|
+
children: "Start"
|
|
9040
|
+
}) : null]
|
|
8916
9041
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccordionContent, {
|
|
8917
9042
|
className: "p-0",
|
|
8918
9043
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -10713,7 +10838,7 @@ function assistantBlockKey(block) {
|
|
|
10713
10838
|
case "text": return `text:${block.text}`;
|
|
10714
10839
|
case "thinking": return `thinking:${block.text}`;
|
|
10715
10840
|
case "tool": return `tool:${block.callId || block.name || "tool"}:${toolArgsKey(block.args)}`;
|
|
10716
|
-
case "compaction": return `compaction:${block.tokensBefore}:${block.summary}`;
|
|
10841
|
+
case "compaction": return `compaction:${block.tokensBefore}:${block.estimatedTokensAfter ?? ""}:${block.summary}`;
|
|
10717
10842
|
default: return "block";
|
|
10718
10843
|
}
|
|
10719
10844
|
}
|
|
@@ -11015,7 +11140,7 @@ function sameAssistantBlock(left, right) {
|
|
|
11015
11140
|
return left.callId === right.callId && left.name === right.name && left.category === right.category && left.output === right.output && left.isError === right.isError && left.running === right.running && sameUnknownValue(left.args, right.args) && sameUnknownValue(left.details, right.details);
|
|
11016
11141
|
case "compaction":
|
|
11017
11142
|
if (right.type !== "compaction") return false;
|
|
11018
|
-
return left.summary === right.summary && left.tokensBefore === right.tokensBefore;
|
|
11143
|
+
return left.summary === right.summary && left.tokensBefore === right.tokensBefore && left.estimatedTokensAfter === right.estimatedTokensAfter;
|
|
11019
11144
|
default: return false;
|
|
11020
11145
|
}
|
|
11021
11146
|
}
|
|
@@ -11352,7 +11477,8 @@ function useAssistantToolGroupBodyBlocks(store, keys, enabled) {
|
|
|
11352
11477
|
}
|
|
11353
11478
|
function compactionTriggerText(block) {
|
|
11354
11479
|
if (block.type !== "compaction") return "Compaction";
|
|
11355
|
-
|
|
11480
|
+
if (block.tokensBefore <= 0) return "Compaction";
|
|
11481
|
+
return block.estimatedTokensAfter && block.estimatedTokensAfter > 0 ? `Compaction: ${block.tokensBefore.toLocaleString()} → ~${block.estimatedTokensAfter.toLocaleString()} tokens` : `Compaction: Compacted from ${block.tokensBefore.toLocaleString()} tokens`;
|
|
11356
11482
|
}
|
|
11357
11483
|
var ANSI_16_COLORS = [
|
|
11358
11484
|
"#3f3f46",
|
|
@@ -12720,7 +12846,7 @@ function getClipboardImageFiles(data) {
|
|
|
12720
12846
|
if (itemFiles.length > 0) return itemFiles;
|
|
12721
12847
|
return Array.from(data.files).filter((file) => file.type.startsWith("image/"));
|
|
12722
12848
|
}
|
|
12723
|
-
function ComposerPanel({ activeSessionId, currentPendingMessages, composerDiffLineComments, composerImages, composerText, composerSkill, composerSyncNonce, centerMessages, contextUsageStore, displaySettingsStore, sessionStore, isSubmitting, isStreaming, awaitingFirstTurn, disabled = false, flush = false, topContent, viewerContextId, fileInputRef, onComposerTextChange, onPickImages, onRemoveComposerDiffLineComment, onRemoveComposerImage, onSubmitPrompt, onAbort, onEditPendingMessage, onRemovePendingMessage, onReorderPending, onRunBuiltinSlashCommand, onSelectModel, onSelectThinkingLevel, requestPathCompletions, requestFileCompletions, ref }) {
|
|
12849
|
+
function ComposerPanel({ activeSessionId, currentPendingMessages, composerDiffLineComments, composerImages, composerText, composerSkill, composerSyncNonce, canStartPendingQueue, centerMessages, contextUsageStore, displaySettingsStore, sessionStore, isSubmitting, isCompacting, isStreaming, awaitingFirstTurn, disabled = false, flush = false, topContent, viewerContextId, fileInputRef, onComposerTextChange, onPickImages, onRemoveComposerDiffLineComment, onRemoveComposerImage, onSubmitPrompt, onAbort, onEditPendingMessage, onRemovePendingMessage, onReorderPending, onStartPendingQueue, onRunBuiltinSlashCommand, onSelectModel, onSelectThinkingLevel, requestPathCompletions, requestFileCompletions, ref }) {
|
|
12724
12850
|
const promptRef = import_react.useRef(null);
|
|
12725
12851
|
const [pickerState, dispatchPickerState] = import_react.useReducer(composerPickerReducer, INITIAL_COMPOSER_PICKER_STATE);
|
|
12726
12852
|
const { modelPickerOpen, thinkingPickerOpen, modelQuery } = pickerState;
|
|
@@ -12747,9 +12873,11 @@ function ComposerPanel({ activeSessionId, currentPendingMessages, composerDiffLi
|
|
|
12747
12873
|
topContent,
|
|
12748
12874
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComposerPendingMessages, {
|
|
12749
12875
|
currentPendingMessages,
|
|
12876
|
+
canStartQueue: canStartPendingQueue,
|
|
12750
12877
|
onEditPendingMessage,
|
|
12751
12878
|
onRemovePendingMessage,
|
|
12752
|
-
onReorderPending
|
|
12879
|
+
onReorderPending,
|
|
12880
|
+
onStartPendingQueue
|
|
12753
12881
|
}),
|
|
12754
12882
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
12755
12883
|
className: "overflow-visible rounded-[18px] border bg-card",
|
|
@@ -12760,6 +12888,7 @@ function ComposerPanel({ activeSessionId, currentPendingMessages, composerDiffLi
|
|
|
12760
12888
|
composerSkill,
|
|
12761
12889
|
composerSyncNonce,
|
|
12762
12890
|
isSubmitting,
|
|
12891
|
+
isCompacting,
|
|
12763
12892
|
isStreaming,
|
|
12764
12893
|
awaitingFirstTurn,
|
|
12765
12894
|
disabled,
|
|
@@ -12843,7 +12972,7 @@ var ComposerAttachments = import_react.memo(function ComposerAttachments({ image
|
|
|
12843
12972
|
}, promptImageKey(image)))
|
|
12844
12973
|
});
|
|
12845
12974
|
});
|
|
12846
|
-
var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ composerDiffLineComments, composerImages, composerText, composerSkill, composerSyncNonce, isSubmitting, isStreaming, awaitingFirstTurn, disabled = false, fileInputRef, promptRef, displaySettingsStore, sessionStore, onComposerTextChange, onPickImages, onRemoveComposerDiffLineComment, onRemoveComposerImage, onSubmitPrompt, onAbort, onRunBuiltinSlashCommand, requestPathCompletions, requestFileCompletions }) {
|
|
12975
|
+
var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ composerDiffLineComments, composerImages, composerText, composerSkill, composerSyncNonce, isSubmitting, isCompacting, isStreaming, awaitingFirstTurn, disabled = false, fileInputRef, promptRef, displaySettingsStore, sessionStore, onComposerTextChange, onPickImages, onRemoveComposerDiffLineComment, onRemoveComposerImage, onSubmitPrompt, onAbort, onRunBuiltinSlashCommand, requestPathCompletions, requestFileCompletions }) {
|
|
12847
12976
|
const slashCommands = useComposerSlashCommands({
|
|
12848
12977
|
displaySettingsStore,
|
|
12849
12978
|
sessionStore
|
|
@@ -12931,7 +13060,7 @@ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ com
|
|
|
12931
13060
|
promptRef
|
|
12932
13061
|
]);
|
|
12933
13062
|
const hasSubmittableContent = hasDraftText || composerDiffLineComments.length > 0 || composerImages.length > 0;
|
|
12934
|
-
const responseInFlight = isStreaming || awaitingFirstTurn || isSubmitting;
|
|
13063
|
+
const responseInFlight = isStreaming || isCompacting || awaitingFirstTurn || isSubmitting;
|
|
12935
13064
|
const acceptFollowUps = responseInFlight;
|
|
12936
13065
|
const setAssistPointerSelectionSuppressedValue = (next) => {
|
|
12937
13066
|
assistPointerSelectionSuppressedRef.current = next;
|
|
@@ -13564,6 +13693,10 @@ var AppShellComposerController = import_react.memo(function AppShellComposerCont
|
|
|
13564
13693
|
if (actionsRef.current.reorderPendingDraftFollowUp(pendingId, direction)) return;
|
|
13565
13694
|
actionsRef.current.reorderPending(pendingId, direction);
|
|
13566
13695
|
});
|
|
13696
|
+
const onStartPendingQueue = useStableEvent(() => {
|
|
13697
|
+
if (snapshotRef.current.disabled) return;
|
|
13698
|
+
actionsRef.current.startPendingQueue();
|
|
13699
|
+
});
|
|
13567
13700
|
const onRunBuiltinSlashCommand = useStableEvent((name, args) => {
|
|
13568
13701
|
if (snapshotRef.current.disabled) return;
|
|
13569
13702
|
actionsRef.current.runBuiltinSlashCommand(name, args);
|
|
@@ -13614,11 +13747,13 @@ var AppShellComposerController = import_react.memo(function AppShellComposerCont
|
|
|
13614
13747
|
composerText: snapshot.composerText,
|
|
13615
13748
|
composerSkill: snapshot.composerSkill,
|
|
13616
13749
|
composerSyncNonce: snapshot.composerSyncNonce,
|
|
13750
|
+
canStartPendingQueue: snapshot.canStartPendingQueue,
|
|
13617
13751
|
centerMessages,
|
|
13618
13752
|
contextUsageStore,
|
|
13619
13753
|
displaySettingsStore,
|
|
13620
13754
|
sessionStore,
|
|
13621
13755
|
isSubmitting: snapshot.isSubmitting,
|
|
13756
|
+
isCompacting: snapshot.isCompacting,
|
|
13622
13757
|
isStreaming: snapshot.isStreaming,
|
|
13623
13758
|
awaitingFirstTurn: snapshot.awaitingFirstTurn,
|
|
13624
13759
|
disabled: snapshot.disabled,
|
|
@@ -13635,6 +13770,7 @@ var AppShellComposerController = import_react.memo(function AppShellComposerCont
|
|
|
13635
13770
|
onEditPendingMessage,
|
|
13636
13771
|
onRemovePendingMessage,
|
|
13637
13772
|
onReorderPending,
|
|
13773
|
+
onStartPendingQueue,
|
|
13638
13774
|
onRunBuiltinSlashCommand,
|
|
13639
13775
|
onSelectModel,
|
|
13640
13776
|
onSelectThinkingLevel,
|
|
@@ -20472,6 +20608,11 @@ function errorMessage(error) {
|
|
|
20472
20608
|
if (typeof error === "string" && error.trim()) return error;
|
|
20473
20609
|
return "Unknown terminal error";
|
|
20474
20610
|
}
|
|
20611
|
+
function buildTerminalWebSocketUrl(path, options) {
|
|
20612
|
+
const url = new URL(buildRequestUrl(path, options));
|
|
20613
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
20614
|
+
return url.toString();
|
|
20615
|
+
}
|
|
20475
20616
|
function exitedStatus(event) {
|
|
20476
20617
|
return typeof event.signal === "number" && event.signal > 0 ? {
|
|
20477
20618
|
type: "exited",
|
|
@@ -20556,14 +20697,22 @@ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, sele
|
|
|
20556
20697
|
const terminalElement = terminalElementRef.current;
|
|
20557
20698
|
if (!terminalElement) return;
|
|
20558
20699
|
let disposed = false;
|
|
20559
|
-
let
|
|
20700
|
+
let websocket = null;
|
|
20560
20701
|
let inputDisposable = null;
|
|
20561
20702
|
let fitAddon = null;
|
|
20562
20703
|
let resizeObserver = null;
|
|
20563
20704
|
let inputFlushTimer = null;
|
|
20705
|
+
let pingTimer = null;
|
|
20706
|
+
let reconnectTimer = null;
|
|
20564
20707
|
let resizeFrame = null;
|
|
20565
20708
|
let inputBuffer = "";
|
|
20566
20709
|
let lastResizeKey = "";
|
|
20710
|
+
let lastOutputSeq = 0;
|
|
20711
|
+
let nextInputSeq = 1;
|
|
20712
|
+
let reconnectAttempt = 0;
|
|
20713
|
+
let terminalExited = false;
|
|
20714
|
+
let terminalTransportReady = false;
|
|
20715
|
+
const pendingInputs = /* @__PURE__ */ new Map();
|
|
20567
20716
|
const requestOptions = sessionId ? {
|
|
20568
20717
|
contextId: viewerContextId,
|
|
20569
20718
|
sessionId
|
|
@@ -20580,18 +20729,36 @@ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, sele
|
|
|
20580
20729
|
method: "POST"
|
|
20581
20730
|
});
|
|
20582
20731
|
};
|
|
20732
|
+
const sendTerminalMessage = (message) => {
|
|
20733
|
+
if (!websocket || websocket.readyState !== WebSocket.OPEN) return false;
|
|
20734
|
+
try {
|
|
20735
|
+
websocket.send(JSON.stringify(message));
|
|
20736
|
+
return true;
|
|
20737
|
+
} catch (error) {
|
|
20738
|
+
publishStatus({
|
|
20739
|
+
type: "error",
|
|
20740
|
+
message: errorMessage(error)
|
|
20741
|
+
});
|
|
20742
|
+
return false;
|
|
20743
|
+
}
|
|
20744
|
+
};
|
|
20745
|
+
const sendPendingInputs = () => {
|
|
20746
|
+
if (!terminalTransportReady) return;
|
|
20747
|
+
for (const [inputSeq, data] of [...pendingInputs.entries()].sort(([left], [right]) => left - right)) if (!sendTerminalMessage({
|
|
20748
|
+
type: "input",
|
|
20749
|
+
data,
|
|
20750
|
+
inputSeq
|
|
20751
|
+
})) return;
|
|
20752
|
+
};
|
|
20583
20753
|
const flushInput = () => {
|
|
20584
20754
|
inputFlushTimer = null;
|
|
20585
|
-
|
|
20586
|
-
if (!id || !inputBuffer) return;
|
|
20755
|
+
if (!terminalIdRef.current || !inputBuffer || !terminalTransportReady) return;
|
|
20587
20756
|
const data = inputBuffer;
|
|
20757
|
+
const inputSeq = nextInputSeq;
|
|
20758
|
+
nextInputSeq += 1;
|
|
20588
20759
|
inputBuffer = "";
|
|
20589
|
-
|
|
20590
|
-
|
|
20591
|
-
type: "error",
|
|
20592
|
-
message: errorMessage(error)
|
|
20593
|
-
});
|
|
20594
|
-
});
|
|
20760
|
+
pendingInputs.set(inputSeq, data);
|
|
20761
|
+
sendPendingInputs();
|
|
20595
20762
|
};
|
|
20596
20763
|
const scheduleInputFlush = () => {
|
|
20597
20764
|
if (inputFlushTimer !== null) return;
|
|
@@ -20605,15 +20772,15 @@ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, sele
|
|
|
20605
20772
|
} catch {
|
|
20606
20773
|
return;
|
|
20607
20774
|
}
|
|
20608
|
-
|
|
20609
|
-
if (!id) return;
|
|
20775
|
+
if (!terminalIdRef.current || !terminalTransportReady) return;
|
|
20610
20776
|
const resizeKey = `${terminal.cols}x${terminal.rows}`;
|
|
20611
20777
|
if (resizeKey === lastResizeKey) return;
|
|
20612
|
-
|
|
20613
|
-
|
|
20778
|
+
if (!sendTerminalMessage({
|
|
20779
|
+
type: "resize",
|
|
20614
20780
|
cols: terminal.cols,
|
|
20615
20781
|
rows: terminal.rows
|
|
20616
|
-
})
|
|
20782
|
+
})) return;
|
|
20783
|
+
lastResizeKey = resizeKey;
|
|
20617
20784
|
};
|
|
20618
20785
|
const scheduleFitAndResize = () => {
|
|
20619
20786
|
if (resizeFrame !== null) return;
|
|
@@ -20623,6 +20790,116 @@ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, sele
|
|
|
20623
20790
|
});
|
|
20624
20791
|
};
|
|
20625
20792
|
fitAndResizeRef.current = scheduleFitAndResize;
|
|
20793
|
+
const clearPingTimer = () => {
|
|
20794
|
+
if (pingTimer === null) return;
|
|
20795
|
+
window.clearInterval(pingTimer);
|
|
20796
|
+
pingTimer = null;
|
|
20797
|
+
};
|
|
20798
|
+
const startPingTimer = () => {
|
|
20799
|
+
clearPingTimer();
|
|
20800
|
+
pingTimer = window.setInterval(() => {
|
|
20801
|
+
sendTerminalMessage({ type: "ping" });
|
|
20802
|
+
}, 2e4);
|
|
20803
|
+
};
|
|
20804
|
+
const scheduleWebSocketReconnect = () => {
|
|
20805
|
+
if (disposed || terminalExited || reconnectTimer !== null) return;
|
|
20806
|
+
terminalTransportReady = false;
|
|
20807
|
+
clearPingTimer();
|
|
20808
|
+
reconnectAttempt += 1;
|
|
20809
|
+
const delay = Math.min(1e4, 500 * 2 ** (reconnectAttempt - 1));
|
|
20810
|
+
reconnectTimer = window.setTimeout(() => {
|
|
20811
|
+
reconnectTimer = null;
|
|
20812
|
+
const id = terminalIdRef.current;
|
|
20813
|
+
if (!id || disposed || terminalExited) return;
|
|
20814
|
+
publishStatus({ type: "connecting" });
|
|
20815
|
+
connectTerminalWebSocket(id);
|
|
20816
|
+
}, delay);
|
|
20817
|
+
};
|
|
20818
|
+
const handleTerminalEvent = (event) => {
|
|
20819
|
+
const terminal = terminalRef.current;
|
|
20820
|
+
if (!terminal) return;
|
|
20821
|
+
if (event.type === "output") {
|
|
20822
|
+
if (typeof event.seq === "number") {
|
|
20823
|
+
if (event.seq <= lastOutputSeq) return;
|
|
20824
|
+
lastOutputSeq = event.seq;
|
|
20825
|
+
}
|
|
20826
|
+
terminal.write(event.data);
|
|
20827
|
+
return;
|
|
20828
|
+
}
|
|
20829
|
+
if (event.type === "reset") {
|
|
20830
|
+
lastOutputSeq = Math.max(0, event.firstSeq - 1);
|
|
20831
|
+
terminal.clear();
|
|
20832
|
+
terminal.writeln("Terminal output replay gap; showing retained output.");
|
|
20833
|
+
return;
|
|
20834
|
+
}
|
|
20835
|
+
if (event.type === "ready") {
|
|
20836
|
+
terminalTransportReady = true;
|
|
20837
|
+
reconnectAttempt = 0;
|
|
20838
|
+
nextInputSeq = Math.max(nextInputSeq, event.nextInputSeq ?? 1);
|
|
20839
|
+
publishStatus({
|
|
20840
|
+
type: "connected",
|
|
20841
|
+
shell: event.shell
|
|
20842
|
+
});
|
|
20843
|
+
startPingTimer();
|
|
20844
|
+
sendPendingInputs();
|
|
20845
|
+
flushInput();
|
|
20846
|
+
lastResizeKey = "";
|
|
20847
|
+
scheduleFitAndResize();
|
|
20848
|
+
return;
|
|
20849
|
+
}
|
|
20850
|
+
if (event.type === "input_ack") {
|
|
20851
|
+
pendingInputs.delete(event.inputSeq);
|
|
20852
|
+
return;
|
|
20853
|
+
}
|
|
20854
|
+
if (event.type === "pong") return;
|
|
20855
|
+
if (event.type === "exit") {
|
|
20856
|
+
terminalExited = true;
|
|
20857
|
+
terminalTransportReady = false;
|
|
20858
|
+
clearPingTimer();
|
|
20859
|
+
publishStatus(exitedStatus(event));
|
|
20860
|
+
return;
|
|
20861
|
+
}
|
|
20862
|
+
if (event.type === "error") publishStatus({
|
|
20863
|
+
type: "error",
|
|
20864
|
+
message: event.error
|
|
20865
|
+
});
|
|
20866
|
+
};
|
|
20867
|
+
function connectTerminalWebSocket(id) {
|
|
20868
|
+
if (websocket) {
|
|
20869
|
+
websocket.onclose = null;
|
|
20870
|
+
websocket.onerror = null;
|
|
20871
|
+
websocket.onmessage = null;
|
|
20872
|
+
websocket.close();
|
|
20873
|
+
}
|
|
20874
|
+
terminalTransportReady = false;
|
|
20875
|
+
const nextWebSocket = new WebSocket(buildTerminalWebSocketUrl(`/api/terminal/${encodeURIComponent(id)}/ws`, {
|
|
20876
|
+
...requestOptions,
|
|
20877
|
+
searchParams: { lastSeq: lastOutputSeq || void 0 }
|
|
20878
|
+
}));
|
|
20879
|
+
websocket = nextWebSocket;
|
|
20880
|
+
nextWebSocket.onmessage = (message) => {
|
|
20881
|
+
if (disposed || websocket !== nextWebSocket) return;
|
|
20882
|
+
try {
|
|
20883
|
+
handleTerminalEvent(JSON.parse(message.data));
|
|
20884
|
+
} catch (error) {
|
|
20885
|
+
publishStatus({
|
|
20886
|
+
type: "error",
|
|
20887
|
+
message: errorMessage(error)
|
|
20888
|
+
});
|
|
20889
|
+
}
|
|
20890
|
+
};
|
|
20891
|
+
nextWebSocket.onclose = () => {
|
|
20892
|
+
if (websocket !== nextWebSocket) return;
|
|
20893
|
+
websocket = null;
|
|
20894
|
+
terminalTransportReady = false;
|
|
20895
|
+
scheduleWebSocketReconnect();
|
|
20896
|
+
};
|
|
20897
|
+
nextWebSocket.onerror = () => {
|
|
20898
|
+
if (websocket !== nextWebSocket) return;
|
|
20899
|
+
terminalTransportReady = false;
|
|
20900
|
+
nextWebSocket.close();
|
|
20901
|
+
};
|
|
20902
|
+
}
|
|
20626
20903
|
const startTerminal = async () => {
|
|
20627
20904
|
try {
|
|
20628
20905
|
publishStatus({ type: "connecting" });
|
|
@@ -20658,45 +20935,7 @@ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, sele
|
|
|
20658
20935
|
if (disposed) return;
|
|
20659
20936
|
terminalIdRef.current = terminalResponse.id;
|
|
20660
20937
|
onTerminalIdChangeRef.current(tabId, terminalResponse.id);
|
|
20661
|
-
|
|
20662
|
-
type: "connected",
|
|
20663
|
-
shell: terminalResponse.shell
|
|
20664
|
-
});
|
|
20665
|
-
eventSource = new EventSource(buildRequestUrl(`/api/terminal/${encodeURIComponent(terminalResponse.id)}/events`, requestOptions));
|
|
20666
|
-
eventSource.onmessage = (message) => {
|
|
20667
|
-
if (disposed) return;
|
|
20668
|
-
const event = JSON.parse(message.data);
|
|
20669
|
-
if (event.type === "output") {
|
|
20670
|
-
terminal.write(event.data);
|
|
20671
|
-
return;
|
|
20672
|
-
}
|
|
20673
|
-
if (event.type === "ready") {
|
|
20674
|
-
publishStatus({
|
|
20675
|
-
type: "connected",
|
|
20676
|
-
shell: event.shell
|
|
20677
|
-
});
|
|
20678
|
-
return;
|
|
20679
|
-
}
|
|
20680
|
-
if (event.type === "exit") {
|
|
20681
|
-
publishStatus(exitedStatus(event));
|
|
20682
|
-
return;
|
|
20683
|
-
}
|
|
20684
|
-
if (event.type === "error") publishStatus({
|
|
20685
|
-
type: "error",
|
|
20686
|
-
message: event.error
|
|
20687
|
-
});
|
|
20688
|
-
};
|
|
20689
|
-
eventSource.onerror = () => {
|
|
20690
|
-
if (!disposed && terminalIdRef.current) setStatus((current) => {
|
|
20691
|
-
if (current.type === "connected") return current;
|
|
20692
|
-
const nextStatus = {
|
|
20693
|
-
type: "error",
|
|
20694
|
-
message: "Terminal connection interrupted."
|
|
20695
|
-
};
|
|
20696
|
-
onStatusChangeRef.current(tabId, nextStatus);
|
|
20697
|
-
return nextStatus;
|
|
20698
|
-
});
|
|
20699
|
-
};
|
|
20938
|
+
connectTerminalWebSocket(terminalResponse.id);
|
|
20700
20939
|
resizeObserver = new ResizeObserver(scheduleFitAndResize);
|
|
20701
20940
|
resizeObserver.observe(terminalElement);
|
|
20702
20941
|
scheduleFitAndResize();
|
|
@@ -20715,9 +20954,16 @@ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, sele
|
|
|
20715
20954
|
disposed = true;
|
|
20716
20955
|
if (inputFlushTimer !== null) window.clearTimeout(inputFlushTimer);
|
|
20717
20956
|
if (resizeFrame !== null) window.cancelAnimationFrame(resizeFrame);
|
|
20957
|
+
if (reconnectTimer !== null) window.clearTimeout(reconnectTimer);
|
|
20958
|
+
clearPingTimer();
|
|
20718
20959
|
flushInput();
|
|
20719
20960
|
resizeObserver?.disconnect();
|
|
20720
|
-
|
|
20961
|
+
if (websocket) {
|
|
20962
|
+
websocket.onclose = null;
|
|
20963
|
+
websocket.onerror = null;
|
|
20964
|
+
websocket.onmessage = null;
|
|
20965
|
+
websocket.close();
|
|
20966
|
+
}
|
|
20721
20967
|
inputDisposable?.dispose();
|
|
20722
20968
|
terminalRef.current?.dispose();
|
|
20723
20969
|
terminalRef.current = null;
|
|
@@ -22021,45 +22267,10 @@ function useDelayedTrue(value, delayMs) {
|
|
|
22021
22267
|
}, [delayMs, value]);
|
|
22022
22268
|
return delayedValue;
|
|
22023
22269
|
}
|
|
22024
|
-
function tildePath(value) {
|
|
22025
|
-
return value.replace(/^\/Users\/[^/]+(?=\/|$)/, "~").replace(/^\/home\/[^/]+(?=\/|$)/, "~");
|
|
22026
|
-
}
|
|
22027
|
-
function splitDisplayPath(value) {
|
|
22028
|
-
if (!value) return {
|
|
22029
|
-
leading: "",
|
|
22030
|
-
trailing: ""
|
|
22031
|
-
};
|
|
22032
|
-
const trimmed = value.replace(/[\\/]+$/, "");
|
|
22033
|
-
if (!trimmed) return {
|
|
22034
|
-
leading: "",
|
|
22035
|
-
trailing: value
|
|
22036
|
-
};
|
|
22037
|
-
const suffix = value.slice(trimmed.length);
|
|
22038
|
-
const separatorIndex = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
22039
|
-
if (separatorIndex < 0 || separatorIndex === trimmed.length - 1) return {
|
|
22040
|
-
leading: "",
|
|
22041
|
-
trailing: `${trimmed}${suffix}`
|
|
22042
|
-
};
|
|
22043
|
-
return {
|
|
22044
|
-
leading: trimmed.slice(0, separatorIndex + 1),
|
|
22045
|
-
trailing: `${trimmed.slice(separatorIndex + 1)}${suffix}`
|
|
22046
|
-
};
|
|
22047
|
-
}
|
|
22048
22270
|
function DirectoryPathLabel({ path }) {
|
|
22049
|
-
const displayPath = tildePath(path);
|
|
22050
|
-
const { leading, trailing } = splitDisplayPath(displayPath);
|
|
22051
22271
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
22052
|
-
className: "block min-w-0 text-sm text-sidebar-foreground",
|
|
22053
|
-
children:
|
|
22054
|
-
className: "flex min-w-0 items-center",
|
|
22055
|
-
children: [leading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
22056
|
-
className: "truncate text-sidebar-foreground/60",
|
|
22057
|
-
children: leading
|
|
22058
|
-
}) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
22059
|
-
className: "shrink-0 font-medium",
|
|
22060
|
-
children: trailing || displayPath
|
|
22061
|
-
})]
|
|
22062
|
-
})
|
|
22272
|
+
className: "block min-w-0 truncate text-sm font-medium text-sidebar-foreground",
|
|
22273
|
+
children: formatFolderName$1(path) || path
|
|
22063
22274
|
});
|
|
22064
22275
|
}
|
|
22065
22276
|
var EMPTY_DIRECTORY_SESSION_KEYS = [];
|
|
@@ -22299,7 +22510,7 @@ function SidebarSessionItem({ entryKey, directorySessionsStore, activeSessionSto
|
|
|
22299
22510
|
const timestamp = entry.lastMessageAt || entry.modified || entry.lastUserMessageAt;
|
|
22300
22511
|
const hasTimestamp = isValidSidebarTimestamp(timestamp);
|
|
22301
22512
|
const lastMessagePreview = entry.lastMessagePreview?.trim() || "";
|
|
22302
|
-
const pinnedDirectoryLabel = isPinned ?
|
|
22513
|
+
const pinnedDirectoryLabel = isPinned ? formatFolderName$1(entry.cwd?.trim() || entry.path?.trim() || "") : "";
|
|
22303
22514
|
const exactTimestamp = hasTimestamp && timestamp ? new Date(timestamp).toLocaleString() : void 0;
|
|
22304
22515
|
const showUnread = Boolean(entry.unread) && !entry.streaming;
|
|
22305
22516
|
const isOptimisticSession = Boolean(entry.optimistic || entry.id?.startsWith("optimistic:") && !entry.path);
|
|
@@ -23893,12 +24104,13 @@ function sameComposerPendingMessages(left, right) {
|
|
|
23893
24104
|
return true;
|
|
23894
24105
|
}
|
|
23895
24106
|
function sameAppShellComposerSnapshot(left, right) {
|
|
23896
|
-
return left.activeSessionId === right.activeSessionId && left.awaitingFirstTurn === right.awaitingFirstTurn && left.centerMessages === right.centerMessages && sameComposerDiffLineComments(left.composerDiffLineComments, right.composerDiffLineComments) && left.composerSkill === right.composerSkill && left.composerSyncNonce === right.composerSyncNonce && left.composerText === right.composerText && left.disabled === right.disabled && left.isStreaming === right.isStreaming && left.isSubmitting === right.isSubmitting && left.viewerContextId === right.viewerContextId && sameComposerPromptImages(left.composerImages, right.composerImages) && sameComposerPendingMessages(left.currentPendingMessages, right.currentPendingMessages);
|
|
24107
|
+
return left.activeSessionId === right.activeSessionId && left.awaitingFirstTurn === right.awaitingFirstTurn && left.canStartPendingQueue === right.canStartPendingQueue && left.centerMessages === right.centerMessages && sameComposerDiffLineComments(left.composerDiffLineComments, right.composerDiffLineComments) && left.composerSkill === right.composerSkill && left.composerSyncNonce === right.composerSyncNonce && left.composerText === right.composerText && left.disabled === right.disabled && left.isCompacting === right.isCompacting && left.isStreaming === right.isStreaming && left.isSubmitting === right.isSubmitting && left.viewerContextId === right.viewerContextId && sameComposerPromptImages(left.composerImages, right.composerImages) && sameComposerPendingMessages(left.currentPendingMessages, right.currentPendingMessages);
|
|
23897
24108
|
}
|
|
23898
24109
|
function createInitialAppShellComposerSnapshot(viewerContextId) {
|
|
23899
24110
|
return {
|
|
23900
24111
|
activeSessionId: void 0,
|
|
23901
24112
|
awaitingFirstTurn: false,
|
|
24113
|
+
canStartPendingQueue: false,
|
|
23902
24114
|
centerMessages: false,
|
|
23903
24115
|
composerDiffLineComments: EMPTY_COMPOSER_DIFF_LINE_COMMENTS,
|
|
23904
24116
|
composerImages: EMPTY_COMPOSER_IMAGES,
|
|
@@ -23907,6 +24119,7 @@ function createInitialAppShellComposerSnapshot(viewerContextId) {
|
|
|
23907
24119
|
composerText: "",
|
|
23908
24120
|
currentPendingMessages: EMPTY_COMPOSER_PENDING_MESSAGES,
|
|
23909
24121
|
disabled: false,
|
|
24122
|
+
isCompacting: false,
|
|
23910
24123
|
isStreaming: false,
|
|
23911
24124
|
isSubmitting: false,
|
|
23912
24125
|
viewerContextId
|
|
@@ -23969,6 +24182,143 @@ function createPicoLatestThrottler(options) {
|
|
|
23969
24182
|
cancel: () => throttler.cancel()
|
|
23970
24183
|
};
|
|
23971
24184
|
}
|
|
24185
|
+
var OFFLINE_PROMPT_DB_NAME = "pico-offline-prompts";
|
|
24186
|
+
var OFFLINE_PROMPT_DB_VERSION = 1;
|
|
24187
|
+
var OFFLINE_PROMPT_STORE_NAME = "prompts";
|
|
24188
|
+
var OFFLINE_PROMPT_FALLBACK_STORAGE_KEY = "pico-offline-prompts";
|
|
24189
|
+
var dbPromise;
|
|
24190
|
+
function hasIndexedDb() {
|
|
24191
|
+
return typeof indexedDB !== "undefined";
|
|
24192
|
+
}
|
|
24193
|
+
function openOfflinePromptDb() {
|
|
24194
|
+
if (!hasIndexedDb()) return Promise.resolve(null);
|
|
24195
|
+
dbPromise ??= new Promise((resolve) => {
|
|
24196
|
+
const request = indexedDB.open(OFFLINE_PROMPT_DB_NAME, OFFLINE_PROMPT_DB_VERSION);
|
|
24197
|
+
request.onupgradeneeded = () => {
|
|
24198
|
+
const db = request.result;
|
|
24199
|
+
if (!db.objectStoreNames.contains(OFFLINE_PROMPT_STORE_NAME)) db.createObjectStore(OFFLINE_PROMPT_STORE_NAME, { keyPath: "id" });
|
|
24200
|
+
};
|
|
24201
|
+
request.onerror = () => resolve(null);
|
|
24202
|
+
request.onsuccess = () => resolve(request.result);
|
|
24203
|
+
});
|
|
24204
|
+
return dbPromise;
|
|
24205
|
+
}
|
|
24206
|
+
function fallbackItems() {
|
|
24207
|
+
try {
|
|
24208
|
+
const raw = window.localStorage.getItem(OFFLINE_PROMPT_FALLBACK_STORAGE_KEY);
|
|
24209
|
+
if (!raw) return [];
|
|
24210
|
+
const parsed = JSON.parse(raw);
|
|
24211
|
+
return Array.isArray(parsed) ? normalizeItems(parsed) : [];
|
|
24212
|
+
} catch {
|
|
24213
|
+
return [];
|
|
24214
|
+
}
|
|
24215
|
+
}
|
|
24216
|
+
function rememberFallbackItems(items) {
|
|
24217
|
+
try {
|
|
24218
|
+
window.localStorage.setItem(OFFLINE_PROMPT_FALLBACK_STORAGE_KEY, JSON.stringify(items));
|
|
24219
|
+
} catch {}
|
|
24220
|
+
}
|
|
24221
|
+
function normalizeImage(value) {
|
|
24222
|
+
if (!value || typeof value !== "object") return null;
|
|
24223
|
+
const rawImage = value;
|
|
24224
|
+
if (typeof rawImage.mimeType !== "string") return null;
|
|
24225
|
+
if (typeof rawImage.data !== "string") return null;
|
|
24226
|
+
return {
|
|
24227
|
+
type: "image",
|
|
24228
|
+
mimeType: rawImage.mimeType,
|
|
24229
|
+
data: rawImage.data
|
|
24230
|
+
};
|
|
24231
|
+
}
|
|
24232
|
+
function normalizeItem(value) {
|
|
24233
|
+
if (!value || typeof value !== "object") return null;
|
|
24234
|
+
const rawItem = value;
|
|
24235
|
+
if (typeof rawItem.id !== "string" || !rawItem.id) return null;
|
|
24236
|
+
if (typeof rawItem.contextId !== "string" || !rawItem.contextId) return null;
|
|
24237
|
+
if (typeof rawItem.message !== "string") return null;
|
|
24238
|
+
if (typeof rawItem.clientRequestId !== "string") return null;
|
|
24239
|
+
const images = Array.isArray(rawItem.images) ? rawItem.images.flatMap((image) => {
|
|
24240
|
+
const normalizedImage = normalizeImage(image);
|
|
24241
|
+
return normalizedImage ? [normalizedImage] : [];
|
|
24242
|
+
}) : [];
|
|
24243
|
+
return {
|
|
24244
|
+
id: rawItem.id,
|
|
24245
|
+
contextId: rawItem.contextId,
|
|
24246
|
+
createdAt: typeof rawItem.createdAt === "number" && Number.isFinite(rawItem.createdAt) ? rawItem.createdAt : Date.now(),
|
|
24247
|
+
sessionId: typeof rawItem.sessionId === "string" ? rawItem.sessionId : void 0,
|
|
24248
|
+
targetSessionKey: typeof rawItem.targetSessionKey === "string" ? rawItem.targetSessionKey : void 0,
|
|
24249
|
+
message: rawItem.message,
|
|
24250
|
+
images,
|
|
24251
|
+
streamingBehavior: rawItem.streamingBehavior === "followUp" || rawItem.streamingBehavior === "steer" ? rawItem.streamingBehavior : void 0,
|
|
24252
|
+
pendingId: typeof rawItem.pendingId === "string" ? rawItem.pendingId : void 0,
|
|
24253
|
+
clientRequestId: rawItem.clientRequestId,
|
|
24254
|
+
thinkingLevel: typeof rawItem.thinkingLevel === "string" ? rawItem.thinkingLevel : void 0,
|
|
24255
|
+
draftOwnerKey: typeof rawItem.draftOwnerKey === "string" ? rawItem.draftOwnerKey : void 0,
|
|
24256
|
+
draftCwd: typeof rawItem.draftCwd === "string" ? rawItem.draftCwd : void 0
|
|
24257
|
+
};
|
|
24258
|
+
}
|
|
24259
|
+
function normalizeItems(values) {
|
|
24260
|
+
return values.flatMap((value) => {
|
|
24261
|
+
const normalizedItem = normalizeItem(value);
|
|
24262
|
+
return normalizedItem ? [normalizedItem] : [];
|
|
24263
|
+
}).sort((left, right) => left.createdAt - right.createdAt);
|
|
24264
|
+
}
|
|
24265
|
+
async function enqueueOfflinePrompt(item) {
|
|
24266
|
+
const db = await openOfflinePromptDb();
|
|
24267
|
+
if (!db) {
|
|
24268
|
+
const items = fallbackItems().filter((entry) => entry.id !== item.id);
|
|
24269
|
+
items.push(item);
|
|
24270
|
+
rememberFallbackItems(normalizeItems(items));
|
|
24271
|
+
return;
|
|
24272
|
+
}
|
|
24273
|
+
await new Promise((resolve, reject) => {
|
|
24274
|
+
const transaction = db.transaction(OFFLINE_PROMPT_STORE_NAME, "readwrite");
|
|
24275
|
+
const request = transaction.objectStore(OFFLINE_PROMPT_STORE_NAME).put(item);
|
|
24276
|
+
request.onerror = () => reject(request.error);
|
|
24277
|
+
transaction.oncomplete = () => resolve();
|
|
24278
|
+
transaction.onerror = () => reject(transaction.error);
|
|
24279
|
+
});
|
|
24280
|
+
}
|
|
24281
|
+
async function readOfflinePrompts(contextId) {
|
|
24282
|
+
const db = await openOfflinePromptDb();
|
|
24283
|
+
if (!db) {
|
|
24284
|
+
const items = fallbackItems();
|
|
24285
|
+
return contextId ? items.filter((item) => item.contextId === contextId) : items;
|
|
24286
|
+
}
|
|
24287
|
+
const items = await new Promise((resolve, reject) => {
|
|
24288
|
+
const transaction = db.transaction(OFFLINE_PROMPT_STORE_NAME, "readonly");
|
|
24289
|
+
const request = transaction.objectStore(OFFLINE_PROMPT_STORE_NAME).getAll();
|
|
24290
|
+
request.onerror = () => reject(request.error);
|
|
24291
|
+
request.onsuccess = () => resolve(normalizeItems(request.result));
|
|
24292
|
+
transaction.onerror = () => reject(transaction.error);
|
|
24293
|
+
});
|
|
24294
|
+
return contextId ? items.filter((item) => item.contextId === contextId) : items;
|
|
24295
|
+
}
|
|
24296
|
+
async function removeOfflinePrompt(id) {
|
|
24297
|
+
const db = await openOfflinePromptDb();
|
|
24298
|
+
if (!db) {
|
|
24299
|
+
rememberFallbackItems(fallbackItems().filter((item) => item.id !== id));
|
|
24300
|
+
return;
|
|
24301
|
+
}
|
|
24302
|
+
await new Promise((resolve, reject) => {
|
|
24303
|
+
const transaction = db.transaction(OFFLINE_PROMPT_STORE_NAME, "readwrite");
|
|
24304
|
+
const request = transaction.objectStore(OFFLINE_PROMPT_STORE_NAME).delete(id);
|
|
24305
|
+
request.onerror = () => reject(request.error);
|
|
24306
|
+
transaction.oncomplete = () => resolve();
|
|
24307
|
+
transaction.onerror = () => reject(transaction.error);
|
|
24308
|
+
});
|
|
24309
|
+
}
|
|
24310
|
+
function pendingMessagesFromResponse(response) {
|
|
24311
|
+
if (!("ok" in response) || !response.ok) return null;
|
|
24312
|
+
return response.pendingMessages.map((message) => ({
|
|
24313
|
+
pendingId: message.pendingId,
|
|
24314
|
+
text: message.text,
|
|
24315
|
+
images: message.images.flatMap((image) => {
|
|
24316
|
+
const normalizedImage = normalizePromptImage(image);
|
|
24317
|
+
return normalizedImage ? [normalizedImage] : [];
|
|
24318
|
+
}),
|
|
24319
|
+
streamingBehavior: message.streamingBehavior === "steer" ? "steer" : "followUp"
|
|
24320
|
+
}));
|
|
24321
|
+
}
|
|
23972
24322
|
function samePromptMutationSessionSnapshot(left, right) {
|
|
23973
24323
|
return left.assistantOutputSignature === right.assistantOutputSignature && left.cwd === right.cwd && left.draft === right.draft && left.sessionFile === right.sessionFile && left.sessionId === right.sessionId && left.sessionKey === right.sessionKey && left.streaming === right.streaming;
|
|
23974
24324
|
}
|
|
@@ -24008,6 +24358,15 @@ function createLocalPendingId() {
|
|
|
24008
24358
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `optimistic:${crypto.randomUUID()}`;
|
|
24009
24359
|
return `optimistic:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
24010
24360
|
}
|
|
24361
|
+
function createClientPromptRequestId() {
|
|
24362
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `prompt:${crypto.randomUUID()}`;
|
|
24363
|
+
return `prompt:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
24364
|
+
}
|
|
24365
|
+
function isOfflinePromptSubmissionError(error) {
|
|
24366
|
+
if (typeof navigator !== "undefined" && navigator.onLine === false) return true;
|
|
24367
|
+
const message = error instanceof Error ? error.message.toLowerCase() : "";
|
|
24368
|
+
return message === "failed to fetch" || message === "load failed" || message.includes("networkerror") || message.includes("network error");
|
|
24369
|
+
}
|
|
24011
24370
|
function buildCurrentSessionRequestUrl(path, { contextId, sessionState, fallbackSessionId, targetSessionKey }) {
|
|
24012
24371
|
if (targetSessionKey) return buildRequestUrl(path, {
|
|
24013
24372
|
contextId,
|
|
@@ -24021,6 +24380,25 @@ function buildCurrentSessionRequestUrl(path, { contextId, sessionState, fallback
|
|
|
24021
24380
|
searchParams: shouldUseSessionKey ? { sessionKey } : void 0
|
|
24022
24381
|
});
|
|
24023
24382
|
}
|
|
24383
|
+
function buildOfflinePromptRequestUrl(item) {
|
|
24384
|
+
return buildRequestUrl("/api/prompt", {
|
|
24385
|
+
contextId: item.contextId,
|
|
24386
|
+
sessionId: item.targetSessionKey ? void 0 : item.sessionId,
|
|
24387
|
+
searchParams: item.targetSessionKey ? { sessionKey: item.targetSessionKey } : void 0
|
|
24388
|
+
});
|
|
24389
|
+
}
|
|
24390
|
+
function buildOfflinePromptBody(item) {
|
|
24391
|
+
return {
|
|
24392
|
+
message: item.message,
|
|
24393
|
+
images: item.images,
|
|
24394
|
+
streamingBehavior: item.streamingBehavior,
|
|
24395
|
+
pendingId: item.pendingId,
|
|
24396
|
+
clientRequestId: item.clientRequestId,
|
|
24397
|
+
thinkingLevel: item.thinkingLevel,
|
|
24398
|
+
draftOwnerKey: item.draftOwnerKey,
|
|
24399
|
+
draftCwd: item.draftCwd
|
|
24400
|
+
};
|
|
24401
|
+
}
|
|
24024
24402
|
function isPendingPromptNotFoundError(error) {
|
|
24025
24403
|
return error instanceof Error && error.message.toLowerCase().includes("pending prompt not found");
|
|
24026
24404
|
}
|
|
@@ -24334,7 +24712,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24334
24712
|
updateOptimisticSidebarSessionPreview
|
|
24335
24713
|
]);
|
|
24336
24714
|
const promptMutation = useMutation({
|
|
24337
|
-
mutationFn: async ({ message, images, streamingBehavior, pendingId, thinkingLevel, targetSessionKey, draftTarget }) => {
|
|
24715
|
+
mutationFn: async ({ message, images, streamingBehavior, pendingId, clientRequestId, thinkingLevel, targetSessionKey, draftTarget }) => {
|
|
24338
24716
|
if (!viewerContextId) throw new Error("Viewer context unavailable");
|
|
24339
24717
|
return await fetchJson(buildCurrentSessionRequestUrl("/api/prompt", {
|
|
24340
24718
|
contextId: viewerContextId,
|
|
@@ -24349,6 +24727,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24349
24727
|
images,
|
|
24350
24728
|
streamingBehavior,
|
|
24351
24729
|
pendingId,
|
|
24730
|
+
clientRequestId,
|
|
24352
24731
|
thinkingLevel,
|
|
24353
24732
|
draftOwnerKey: draftTarget?.ownerKey,
|
|
24354
24733
|
draftCwd: draftTarget?.cwd
|
|
@@ -24362,6 +24741,58 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24362
24741
|
});
|
|
24363
24742
|
}
|
|
24364
24743
|
});
|
|
24744
|
+
const offlinePromptFlushInFlightRef = import_react.useRef(false);
|
|
24745
|
+
const offlinePromptFailureToastIdsRef = import_react.useRef(/* @__PURE__ */ new Set());
|
|
24746
|
+
const flushOfflinePromptQueue = import_react.useCallback(async () => {
|
|
24747
|
+
if (!viewerContextId || offlinePromptFlushInFlightRef.current) return;
|
|
24748
|
+
if (typeof navigator !== "undefined" && navigator.onLine === false) return;
|
|
24749
|
+
offlinePromptFlushInFlightRef.current = true;
|
|
24750
|
+
try {
|
|
24751
|
+
const queuedPrompts = await readOfflinePrompts(viewerContextId);
|
|
24752
|
+
if (queuedPrompts.length === 0) return;
|
|
24753
|
+
let flushedCount = 0;
|
|
24754
|
+
for (const item of queuedPrompts) try {
|
|
24755
|
+
await fetchJson(buildOfflinePromptRequestUrl(item), {
|
|
24756
|
+
method: "POST",
|
|
24757
|
+
headers: { "content-type": "application/json" },
|
|
24758
|
+
body: JSON.stringify(buildOfflinePromptBody(item))
|
|
24759
|
+
});
|
|
24760
|
+
await removeOfflinePrompt(item.id);
|
|
24761
|
+
offlinePromptFailureToastIdsRef.current.delete(item.id);
|
|
24762
|
+
flushedCount += 1;
|
|
24763
|
+
} catch (error) {
|
|
24764
|
+
if (isOfflinePromptSubmissionError(error)) break;
|
|
24765
|
+
if (!offlinePromptFailureToastIdsRef.current.has(item.id)) {
|
|
24766
|
+
offlinePromptFailureToastIdsRef.current.add(item.id);
|
|
24767
|
+
toast.error(error instanceof Error ? `Failed to send queued prompt: ${error.message}` : "Failed to send queued prompt");
|
|
24768
|
+
}
|
|
24769
|
+
break;
|
|
24770
|
+
}
|
|
24771
|
+
if (flushedCount > 0) {
|
|
24772
|
+
toast.success(flushedCount === 1 ? "Sent queued prompt" : `Sent ${flushedCount.toLocaleString()} queued prompts`);
|
|
24773
|
+
await queryClient.invalidateQueries({ refetchType: "active" });
|
|
24774
|
+
}
|
|
24775
|
+
} finally {
|
|
24776
|
+
offlinePromptFlushInFlightRef.current = false;
|
|
24777
|
+
}
|
|
24778
|
+
}, [queryClient, viewerContextId]);
|
|
24779
|
+
import_react.useEffect(() => {
|
|
24780
|
+
if (!viewerContextId) return;
|
|
24781
|
+
const flush = () => {
|
|
24782
|
+
flushOfflinePromptQueue();
|
|
24783
|
+
};
|
|
24784
|
+
window.addEventListener("online", flush);
|
|
24785
|
+
window.addEventListener("focus", flush);
|
|
24786
|
+
document.addEventListener("visibilitychange", flush);
|
|
24787
|
+
const timer = window.setInterval(flush, 3e4);
|
|
24788
|
+
flush();
|
|
24789
|
+
return () => {
|
|
24790
|
+
window.removeEventListener("online", flush);
|
|
24791
|
+
window.removeEventListener("focus", flush);
|
|
24792
|
+
document.removeEventListener("visibilitychange", flush);
|
|
24793
|
+
window.clearInterval(timer);
|
|
24794
|
+
};
|
|
24795
|
+
}, [flushOfflinePromptQueue, viewerContextId]);
|
|
24365
24796
|
const submitPrompt = import_react.useCallback(async (streamingBehavior, options) => {
|
|
24366
24797
|
if (!viewerContextId) return false;
|
|
24367
24798
|
if (draftSessionLoadingOwnerKeyRef.current) return queuePendingDraftPrompt(streamingBehavior);
|
|
@@ -24388,7 +24819,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24388
24819
|
}
|
|
24389
24820
|
return queuePendingDraftPrompt(streamingBehavior, currentDraftOwnerKey);
|
|
24390
24821
|
}
|
|
24391
|
-
const treatAsQueuedPrompt = options?.forceFirstPrompt ? false : Boolean(currentSessionState.streaming || awaitingFirstTurnRef.current);
|
|
24822
|
+
const treatAsQueuedPrompt = options?.forceFirstPrompt ? false : Boolean(currentSessionState.streaming || currentSessionState.compacting || awaitingFirstTurnRef.current);
|
|
24392
24823
|
const normalizedStreamingBehavior = treatAsQueuedPrompt ? normalizeQueuedStreamingBehavior(streamingBehavior) : void 0;
|
|
24393
24824
|
const submittedImages = composerImagesRef.current.map((image) => ({ ...image }));
|
|
24394
24825
|
const sidebarPreview = promptSidebarPreview(message, submittedImages);
|
|
@@ -24414,6 +24845,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24414
24845
|
images: submittedImages.map((image) => ({ ...image })),
|
|
24415
24846
|
streamingBehavior: normalizedStreamingBehavior
|
|
24416
24847
|
});
|
|
24848
|
+
const clientRequestId = createClientPromptRequestId();
|
|
24417
24849
|
setIsSubmitting(true);
|
|
24418
24850
|
replaceComposerDraft("", void 0, { forceSync: true });
|
|
24419
24851
|
setComposerDiffLineComments([]);
|
|
@@ -24425,6 +24857,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24425
24857
|
images: submittedImages,
|
|
24426
24858
|
streamingBehavior: normalizedStreamingBehavior,
|
|
24427
24859
|
pendingId: queuedPendingId,
|
|
24860
|
+
clientRequestId,
|
|
24428
24861
|
thinkingLevel: currentSessionState.thinkingLevel,
|
|
24429
24862
|
targetSessionKey,
|
|
24430
24863
|
draftTarget: targetDraft
|
|
@@ -24433,6 +24866,39 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24433
24866
|
if (targetSessionKey && pendingNewSessionPromptTargetRef.current?.sessionKey === targetSessionKey) pendingNewSessionPromptTargetRef.current = null;
|
|
24434
24867
|
return true;
|
|
24435
24868
|
} catch (error) {
|
|
24869
|
+
const queueSessionKey = targetSessionKey || (currentSessionState.sessionKey && !currentSessionState.sessionKey.startsWith("optimistic:") && (currentSessionState.draft || !currentSessionState.sessionId) ? currentSessionState.sessionKey : void 0);
|
|
24870
|
+
const queueSessionId = queueSessionKey ? void 0 : currentSessionState.sessionId ?? activeSessionId;
|
|
24871
|
+
if (isOfflinePromptSubmissionError(error) && (queueSessionKey || queueSessionId)) {
|
|
24872
|
+
await enqueueOfflinePrompt({
|
|
24873
|
+
id: clientRequestId,
|
|
24874
|
+
contextId: viewerContextId,
|
|
24875
|
+
createdAt: Date.now(),
|
|
24876
|
+
sessionId: queueSessionId,
|
|
24877
|
+
targetSessionKey: queueSessionKey,
|
|
24878
|
+
message,
|
|
24879
|
+
images: submittedImages.map((image) => ({
|
|
24880
|
+
type: "image",
|
|
24881
|
+
mimeType: image.mimeType,
|
|
24882
|
+
data: image.data
|
|
24883
|
+
})),
|
|
24884
|
+
streamingBehavior: normalizedStreamingBehavior,
|
|
24885
|
+
pendingId: queuedPendingId,
|
|
24886
|
+
clientRequestId,
|
|
24887
|
+
thinkingLevel: currentSessionState.thinkingLevel,
|
|
24888
|
+
draftOwnerKey: targetDraft?.ownerKey,
|
|
24889
|
+
draftCwd: targetDraft?.cwd
|
|
24890
|
+
});
|
|
24891
|
+
if (queuedPendingId) removeOptimisticPendingMessage(queuedPendingId);
|
|
24892
|
+
removeOptimisticUserMessage(optimisticId);
|
|
24893
|
+
removeOptimisticSidebarSession(optimisticSidebarSessionId);
|
|
24894
|
+
if (!treatAsQueuedPrompt) {
|
|
24895
|
+
awaitingFirstTurnAssistantOutputSignatureRef.current = null;
|
|
24896
|
+
awaitingFirstTurnRef.current = false;
|
|
24897
|
+
setAwaitingFirstTurn(false);
|
|
24898
|
+
}
|
|
24899
|
+
toast.info("Prompt queued locally and will send when reconnected");
|
|
24900
|
+
return true;
|
|
24901
|
+
}
|
|
24436
24902
|
if (queuedPendingId) removeOptimisticPendingMessage(queuedPendingId);
|
|
24437
24903
|
removeOptimisticUserMessage(optimisticId);
|
|
24438
24904
|
removeOptimisticSidebarSession(optimisticSidebarSessionId);
|
|
@@ -24455,6 +24921,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24455
24921
|
setIsSubmitting(false);
|
|
24456
24922
|
}
|
|
24457
24923
|
}, [
|
|
24924
|
+
activeSessionId,
|
|
24458
24925
|
addOptimisticPendingMessage,
|
|
24459
24926
|
addOptimisticSidebarSession,
|
|
24460
24927
|
addOptimisticUserMessage,
|
|
@@ -24499,6 +24966,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24499
24966
|
images: followUp.images,
|
|
24500
24967
|
streamingBehavior: followUp.streamingBehavior,
|
|
24501
24968
|
pendingId: followUp.optimisticId,
|
|
24969
|
+
clientRequestId: createClientPromptRequestId(),
|
|
24502
24970
|
thinkingLevel: sessionStateRef.current.thinkingLevel,
|
|
24503
24971
|
targetSessionKey,
|
|
24504
24972
|
draftTarget
|
|
@@ -24676,6 +25144,41 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24676
25144
|
updatePendingMessages,
|
|
24677
25145
|
viewerContextId
|
|
24678
25146
|
]);
|
|
25147
|
+
const startPendingQueueMutation = useMutation({
|
|
25148
|
+
mutationFn: async () => {
|
|
25149
|
+
if (!viewerContextId) throw new Error("Viewer context unavailable");
|
|
25150
|
+
return await fetchJson(buildCurrentSessionRequestUrl("/api/pending-messages/start", {
|
|
25151
|
+
contextId: viewerContextId,
|
|
25152
|
+
sessionState: sessionStateRef.current,
|
|
25153
|
+
fallbackSessionId: activeSessionId
|
|
25154
|
+
}), { method: "POST" });
|
|
25155
|
+
},
|
|
25156
|
+
onSuccess: async () => {
|
|
25157
|
+
await queryClient.invalidateQueries({
|
|
25158
|
+
queryKey: picoQueryKeys.sessionTree(viewerContextId, picoSessionScopeKey(sessionStateRef.current)),
|
|
25159
|
+
refetchType: "active"
|
|
25160
|
+
});
|
|
25161
|
+
}
|
|
25162
|
+
});
|
|
25163
|
+
const startPendingQueue = import_react.useCallback(async () => {
|
|
25164
|
+
if (!viewerContextId) return;
|
|
25165
|
+
if (pendingMessagesRef.current.length === 0) return;
|
|
25166
|
+
setIsSubmitting(true);
|
|
25167
|
+
try {
|
|
25168
|
+
const nextPendingMessages = pendingMessagesFromResponse(await startPendingQueueMutation.mutateAsync());
|
|
25169
|
+
if (nextPendingMessages) updatePendingMessages(() => nextPendingMessages);
|
|
25170
|
+
} catch (error) {
|
|
25171
|
+
toast.error(error instanceof Error ? error.message : "Failed to start queue");
|
|
25172
|
+
} finally {
|
|
25173
|
+
setIsSubmitting(false);
|
|
25174
|
+
}
|
|
25175
|
+
}, [
|
|
25176
|
+
pendingMessagesRef,
|
|
25177
|
+
setIsSubmitting,
|
|
25178
|
+
startPendingQueueMutation,
|
|
25179
|
+
updatePendingMessages,
|
|
25180
|
+
viewerContextId
|
|
25181
|
+
]);
|
|
24679
25182
|
const removePendingMessageMutation = useMutation({
|
|
24680
25183
|
mutationFn: async (pendingId) => {
|
|
24681
25184
|
if (!viewerContextId) throw new Error("Viewer context unavailable");
|
|
@@ -24737,6 +25240,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
|
|
|
24737
25240
|
updatePendingMessages,
|
|
24738
25241
|
viewerContextId
|
|
24739
25242
|
]),
|
|
25243
|
+
startPendingQueue,
|
|
24740
25244
|
submitPrompt
|
|
24741
25245
|
};
|
|
24742
25246
|
}
|
|
@@ -25338,7 +25842,7 @@ function useAppShellSessionMutations({ viewerContextId, activeSessionId, session
|
|
|
25338
25842
|
});
|
|
25339
25843
|
return {
|
|
25340
25844
|
cycleThinkingLevel,
|
|
25341
|
-
deleteSessions: import_react.useCallback(
|
|
25845
|
+
deleteSessions: import_react.useCallback((targets) => {
|
|
25342
25846
|
if (!viewerContextId || targets.length === 0) return false;
|
|
25343
25847
|
const orderedTargets = [...targets.filter((target) => target.path && target.path !== sessionStateRef.current.sessionFile), ...targets.filter((target) => target.path && target.path === sessionStateRef.current.sessionFile)];
|
|
25344
25848
|
const orderedPaths = [];
|
|
@@ -25407,6 +25911,26 @@ function useAppShellSessionMutations({ viewerContextId, activeSessionId, session
|
|
|
25407
25911
|
}
|
|
25408
25912
|
var RESUME_RECONNECT_AFTER_MS = 3e4;
|
|
25409
25913
|
var COMPACT_WORKING_LABEL = "Compacting context...";
|
|
25914
|
+
var EVENT_SOURCE_LAST_ID_STORAGE_PREFIX = "pico-events-last-id:";
|
|
25915
|
+
function eventsLastIdStorageKey(viewerContextId) {
|
|
25916
|
+
return `${EVENT_SOURCE_LAST_ID_STORAGE_PREFIX}${viewerContextId}`;
|
|
25917
|
+
}
|
|
25918
|
+
function readStoredEventsLastId(viewerContextId) {
|
|
25919
|
+
try {
|
|
25920
|
+
return window.sessionStorage.getItem(eventsLastIdStorageKey(viewerContextId));
|
|
25921
|
+
} catch {
|
|
25922
|
+
return null;
|
|
25923
|
+
}
|
|
25924
|
+
}
|
|
25925
|
+
function rememberStoredEventsLastId(viewerContextId, lastEventId) {
|
|
25926
|
+
try {
|
|
25927
|
+
window.sessionStorage.setItem(eventsLastIdStorageKey(viewerContextId), lastEventId);
|
|
25928
|
+
} catch {}
|
|
25929
|
+
}
|
|
25930
|
+
function normalizeEventSourceLastId(value) {
|
|
25931
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
25932
|
+
return /^\d+$/.test(normalized) ? normalized : void 0;
|
|
25933
|
+
}
|
|
25410
25934
|
function normalizePendingMessages(payload) {
|
|
25411
25935
|
if (!isStateSyncEvent(payload)) return;
|
|
25412
25936
|
if (!Array.isArray(payload.pendingUserMessages)) return;
|
|
@@ -25420,6 +25944,9 @@ function normalizePendingMessages(payload) {
|
|
|
25420
25944
|
streamingBehavior: message.streamingBehavior === "steer" ? "steer" : "followUp"
|
|
25421
25945
|
}));
|
|
25422
25946
|
}
|
|
25947
|
+
function autoSessionNamingErrorDescription(payload) {
|
|
25948
|
+
return payload.refinementReason || payload.heuristicReason || payload.promptPreview || "The server could not generate a session name.";
|
|
25949
|
+
}
|
|
25423
25950
|
function sameStringArray(left = [], right = []) {
|
|
25424
25951
|
if (left.length !== right.length) return false;
|
|
25425
25952
|
for (let index = 0; index < left.length; index += 1) if (left[index] !== right[index]) return false;
|
|
@@ -25507,7 +26034,7 @@ function sameSessionsEvent(left, right) {
|
|
|
25507
26034
|
function visibleAssistantBlockKey(block, options) {
|
|
25508
26035
|
switch (block.type) {
|
|
25509
26036
|
case "text": return block.text.trim() ? `text:${block.text}` : "";
|
|
25510
|
-
case "compaction": return `compaction:${block.tokensBefore}:${block.summary}`;
|
|
26037
|
+
case "compaction": return `compaction:${block.tokensBefore}:${block.estimatedTokensAfter ?? ""}:${block.summary}`;
|
|
25511
26038
|
case "thinking": return options.hideThinking ? "" : `thinking:${block.summaryLabel || ""}:${block.text}`;
|
|
25512
26039
|
case "tool": return options.hideToolBlocks ? "" : `tool:${block.callId || ""}:${block.name || ""}:${JSON.stringify(block.args ?? null)}:${block.output}:${JSON.stringify(block.details ?? null)}:${block.isError ? "1" : "0"}:${block.running ? "1" : "0"}`;
|
|
25513
26040
|
default: return "";
|
|
@@ -25584,6 +26111,7 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
|
|
|
25584
26111
|
const currentSessionIdRef = import_react.useRef(sessionId);
|
|
25585
26112
|
const draftSessionLoadingOwnerKeyRef = import_react.useRef(draftSessionLoadingOwnerKey);
|
|
25586
26113
|
const currentSourceRef = import_react.useRef(null);
|
|
26114
|
+
const eventsLastIdRef = import_react.useRef(void 0);
|
|
25587
26115
|
const hasReceivedStateSyncRef = import_react.useRef(false);
|
|
25588
26116
|
const [hasReceivedStateSync, setHasReceivedStateSync] = import_react.useState(false);
|
|
25589
26117
|
const backgroundedAtRef = import_react.useRef(null);
|
|
@@ -25595,6 +26123,9 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
|
|
|
25595
26123
|
import_react.useEffect(() => {
|
|
25596
26124
|
draftSessionLoadingOwnerKeyRef.current = draftSessionLoadingOwnerKey;
|
|
25597
26125
|
}, [draftSessionLoadingOwnerKey]);
|
|
26126
|
+
import_react.useEffect(() => {
|
|
26127
|
+
eventsLastIdRef.current = normalizeEventSourceLastId(readStoredEventsLastId(viewerContextId));
|
|
26128
|
+
}, [viewerContextId]);
|
|
25598
26129
|
import_react.useEffect(() => {
|
|
25599
26130
|
if (currentSourceRef.current) return;
|
|
25600
26131
|
initialEventsSessionIdRef.current = sessionId;
|
|
@@ -25710,6 +26241,11 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
|
|
|
25710
26241
|
]);
|
|
25711
26242
|
const handleEventSourceMessage = import_react.useCallback((source, gitInvalidationBatcher, event) => {
|
|
25712
26243
|
if (currentSourceRef.current !== source) return;
|
|
26244
|
+
const lastEventId = normalizeEventSourceLastId(event.lastEventId);
|
|
26245
|
+
if (lastEventId) {
|
|
26246
|
+
eventsLastIdRef.current = lastEventId;
|
|
26247
|
+
rememberStoredEventsLastId(viewerContextId, lastEventId);
|
|
26248
|
+
}
|
|
25713
26249
|
const payload = JSON.parse(event.data);
|
|
25714
26250
|
if (isStateSyncEvent(payload)) {
|
|
25715
26251
|
const activationRevision = payload.activationRevision;
|
|
@@ -25808,6 +26344,11 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
|
|
|
25808
26344
|
});
|
|
25809
26345
|
return;
|
|
25810
26346
|
}
|
|
26347
|
+
if (payload.type === "auto_session_naming_error") {
|
|
26348
|
+
console.warn("[pico] auto session naming failed", payload);
|
|
26349
|
+
toast.warning("Could not name session", { description: autoSessionNamingErrorDescription(payload) });
|
|
26350
|
+
return;
|
|
26351
|
+
}
|
|
25811
26352
|
if (payload.type === "request_error") {
|
|
25812
26353
|
toast.error(payload.error || "Request failed");
|
|
25813
26354
|
return;
|
|
@@ -25848,7 +26389,8 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
|
|
|
25848
26389
|
setSessionDoneEvents,
|
|
25849
26390
|
setSessionState,
|
|
25850
26391
|
setSessionsEvent,
|
|
25851
|
-
setWorkingState
|
|
26392
|
+
setWorkingState,
|
|
26393
|
+
viewerContextId
|
|
25852
26394
|
]);
|
|
25853
26395
|
import_react.useEffect(() => {
|
|
25854
26396
|
if (!viewerContextId) return;
|
|
@@ -25900,7 +26442,10 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
|
|
|
25900
26442
|
const source = new EventSource(buildRequestUrl("/events", {
|
|
25901
26443
|
contextId: viewerContextId,
|
|
25902
26444
|
sessionId: initialEventsSessionIdRef.current,
|
|
25903
|
-
searchParams: {
|
|
26445
|
+
searchParams: {
|
|
26446
|
+
lastEventId: eventsLastIdRef.current,
|
|
26447
|
+
sidebarDirectory: bootstrapSidebarDirectories
|
|
26448
|
+
}
|
|
25904
26449
|
}));
|
|
25905
26450
|
currentSourceRef.current = source;
|
|
25906
26451
|
source.onopen = () => {
|
|
@@ -27132,7 +27677,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
|
|
|
27132
27677
|
if (!currentState.sessionFile) return;
|
|
27133
27678
|
renameDialogRef.current?.open({
|
|
27134
27679
|
path: currentState.sessionFile,
|
|
27135
|
-
title: currentState.sessionName || getCurrentSessionTitleFromState(currentState)
|
|
27680
|
+
title: currentState.sessionName || getCurrentSessionTitleFromState(currentState),
|
|
27681
|
+
hasGeneratedName: Boolean(currentState.sessionName?.trim())
|
|
27136
27682
|
});
|
|
27137
27683
|
};
|
|
27138
27684
|
const openRenameDialogForEntry = (entry) => {
|
|
@@ -27525,7 +28071,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
|
|
|
27525
28071
|
const pendingDraftPrompt = pendingDraftPromptStore.state;
|
|
27526
28072
|
const pendingDraftFollowUps = pendingDraftFollowUpsStore.state;
|
|
27527
28073
|
const pendingMessages = pendingMessagesStore.state;
|
|
27528
|
-
const { abortSession, addDirectoryPath, createSession: requestCreateSession, editPendingMessage, removePendingMessage, reorderPending, submitPrompt } = useAppShellPromptMutations({
|
|
28074
|
+
const { abortSession, addDirectoryPath, createSession: requestCreateSession, editPendingMessage, removePendingMessage, reorderPending, startPendingQueue, submitPrompt } = useAppShellPromptMutations({
|
|
27529
28075
|
viewerContextId,
|
|
27530
28076
|
activeSessionId,
|
|
27531
28077
|
defaultNewSessionDirectory,
|
|
@@ -27964,6 +28510,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
|
|
|
27964
28510
|
const composerSnapshot = {
|
|
27965
28511
|
activeSessionId,
|
|
27966
28512
|
awaitingFirstTurn: composerDisabled ? false : awaitingFirstTurn,
|
|
28513
|
+
canStartPendingQueue: !composerDisabled && pendingMessages.length > 0 && !sessionStateRef.current.streaming && !sessionStateRef.current.compacting && !awaitingFirstTurn && !isSubmitting,
|
|
27967
28514
|
centerMessages: displaySettingsRef.current.centerMessages,
|
|
27968
28515
|
composerDiffLineComments: displayedDiffLineComments,
|
|
27969
28516
|
composerImages: displayedComposerImages,
|
|
@@ -27972,6 +28519,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
|
|
|
27972
28519
|
composerText: displayedComposerText,
|
|
27973
28520
|
currentPendingMessages: displayedPendingMessages,
|
|
27974
28521
|
disabled: composerDisabled,
|
|
28522
|
+
isCompacting: composerDisabled ? false : sessionStateRef.current.compacting,
|
|
27975
28523
|
isStreaming: composerDisabled ? false : sessionStateRef.current.streaming,
|
|
27976
28524
|
isSubmitting: composerDisabled ? false : isSubmitting,
|
|
27977
28525
|
viewerContextId
|
|
@@ -27994,6 +28542,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
|
|
|
27994
28542
|
removePendingMessage,
|
|
27995
28543
|
reorderPending,
|
|
27996
28544
|
reorderPendingDraftFollowUp,
|
|
28545
|
+
startPendingQueue,
|
|
27997
28546
|
runBuiltinSlashCommand,
|
|
27998
28547
|
setModel,
|
|
27999
28548
|
setThinkingLevel,
|