Fix Jupyter workspace session handoff
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
|||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import {
|
import {
|
||||||
|
consumePendingWorkspaceSessionId,
|
||||||
readActiveSessionId,
|
readActiveSessionId,
|
||||||
writeActiveSessionId,
|
writeActiveSessionId,
|
||||||
} from "@/lib/claw-active-session";
|
} from "@/lib/claw-active-session";
|
||||||
@@ -37,10 +38,21 @@ export const Assistant = () => {
|
|||||||
const messages = options.messages as UIMessage[];
|
const messages = options.messages as UIMessage[];
|
||||||
const selectedSessionId = readActiveSessionId();
|
const selectedSessionId = readActiveSessionId();
|
||||||
const lastSessionId = getLastSessionId(messages);
|
const lastSessionId = getLastSessionId(messages);
|
||||||
|
const pendingWorkspaceSessionId =
|
||||||
|
messages.length <= 1 ? consumePendingWorkspaceSessionId() : null;
|
||||||
|
const usablePendingWorkspaceSessionId =
|
||||||
|
pendingWorkspaceSessionId &&
|
||||||
|
(!selectedSessionId ||
|
||||||
|
selectedSessionId === pendingWorkspaceSessionId)
|
||||||
|
? pendingWorkspaceSessionId
|
||||||
|
: null;
|
||||||
const selectedResumeSessionId =
|
const selectedResumeSessionId =
|
||||||
messages.length > 1 ? selectedSessionId : null;
|
messages.length > 1 ? selectedSessionId : null;
|
||||||
const outgoingSessionId =
|
const outgoingSessionId =
|
||||||
lastSessionId ?? selectedResumeSessionId ?? options.id;
|
lastSessionId ??
|
||||||
|
selectedResumeSessionId ??
|
||||||
|
usablePendingWorkspaceSessionId ??
|
||||||
|
options.id;
|
||||||
const lastUserMessage = [...messages]
|
const lastUserMessage = [...messages]
|
||||||
.reverse()
|
.reverse()
|
||||||
.find((message) => message.role === "user");
|
.find((message) => message.role === "user");
|
||||||
@@ -49,7 +61,7 @@ export const Assistant = () => {
|
|||||||
return {
|
return {
|
||||||
body: {
|
body: {
|
||||||
...body,
|
...body,
|
||||||
id: options.id,
|
id: outgoingSessionId,
|
||||||
messages: lastUserMessage ? [lastUserMessage] : [],
|
messages: lastUserMessage ? [lastUserMessage] : [],
|
||||||
resumeSessionId:
|
resumeSessionId:
|
||||||
lastSessionId ?? selectedResumeSessionId ?? undefined,
|
lastSessionId ?? selectedResumeSessionId ?? undefined,
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import {
|
|||||||
clearActiveSessionId,
|
clearActiveSessionId,
|
||||||
readActiveSessionId,
|
readActiveSessionId,
|
||||||
writeActiveSessionId,
|
writeActiveSessionId,
|
||||||
|
writePendingWorkspaceSessionId,
|
||||||
} from "@/lib/claw-active-session";
|
} from "@/lib/claw-active-session";
|
||||||
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
import { useClawSessionReplay } from "@/lib/claw-session-replay";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -437,6 +438,7 @@ function WorkspaceSwitchDialog({
|
|||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
const progressTimer = startWorkspaceProgress(setProgress);
|
const progressTimer = startWorkspaceProgress(setProgress);
|
||||||
try {
|
try {
|
||||||
|
const needsFirstMessageSession = !normalizeSessionId(sessionId);
|
||||||
const response = await fetch("/api/claw/jupyter", {
|
const response = await fetch("/api/claw/jupyter", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
@@ -455,6 +457,9 @@ function WorkspaceSwitchDialog({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writeActiveSessionId(sessionIdForRequest);
|
writeActiveSessionId(sessionIdForRequest);
|
||||||
|
if (needsFirstMessageSession) {
|
||||||
|
writePendingWorkspaceSessionId(sessionIdForRequest);
|
||||||
|
}
|
||||||
setStatus(payload as JupyterWorkspaceStatus);
|
setStatus(payload as JupyterWorkspaceStatus);
|
||||||
setProgress({ percent: 100, label: "切换完成" });
|
setProgress({ percent: 100, label: "切换完成" });
|
||||||
setMessage("工作区切换成功,后续工具会在远端 Jupyter 工作区执行。");
|
setMessage("工作区切换成功,后续工具会在远端 Jupyter 工作区执行。");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
const ACTIVE_SESSION_ID_KEY = "claw.activeSessionId";
|
const ACTIVE_SESSION_ID_KEY = "claw.activeSessionId";
|
||||||
|
const PENDING_WORKSPACE_SESSION_ID_KEY = "claw.pendingWorkspaceSessionId";
|
||||||
export const ACTIVE_SESSION_CHANGED_EVENT = "claw-active-session-changed";
|
export const ACTIVE_SESSION_CHANGED_EVENT = "claw-active-session-changed";
|
||||||
|
|
||||||
export function readActiveSessionId() {
|
export function readActiveSessionId() {
|
||||||
@@ -20,6 +21,10 @@ export function writeActiveSessionId(sessionId: string | null | undefined) {
|
|||||||
}
|
}
|
||||||
window.sessionStorage.setItem(ACTIVE_SESSION_ID_KEY, normalized);
|
window.sessionStorage.setItem(ACTIVE_SESSION_ID_KEY, normalized);
|
||||||
window.localStorage.setItem(ACTIVE_SESSION_ID_KEY, normalized);
|
window.localStorage.setItem(ACTIVE_SESSION_ID_KEY, normalized);
|
||||||
|
const pendingSessionId = readPendingWorkspaceSessionId();
|
||||||
|
if (pendingSessionId && pendingSessionId !== normalized) {
|
||||||
|
clearPendingWorkspaceSessionId();
|
||||||
|
}
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent(ACTIVE_SESSION_CHANGED_EVENT, {
|
new CustomEvent(ACTIVE_SESSION_CHANGED_EVENT, {
|
||||||
detail: { sessionId: normalized },
|
detail: { sessionId: normalized },
|
||||||
@@ -31,6 +36,7 @@ export function clearActiveSessionId() {
|
|||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
window.sessionStorage.removeItem(ACTIVE_SESSION_ID_KEY);
|
window.sessionStorage.removeItem(ACTIVE_SESSION_ID_KEY);
|
||||||
window.localStorage.removeItem(ACTIVE_SESSION_ID_KEY);
|
window.localStorage.removeItem(ACTIVE_SESSION_ID_KEY);
|
||||||
|
clearPendingWorkspaceSessionId();
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent(ACTIVE_SESSION_CHANGED_EVENT, {
|
new CustomEvent(ACTIVE_SESSION_CHANGED_EVENT, {
|
||||||
detail: { sessionId: null },
|
detail: { sessionId: null },
|
||||||
@@ -38,6 +44,43 @@ export function clearActiveSessionId() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function writePendingWorkspaceSessionId(
|
||||||
|
sessionId: string | null | undefined,
|
||||||
|
) {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
const normalized = normalizeSessionId(sessionId);
|
||||||
|
if (!normalized) {
|
||||||
|
clearPendingWorkspaceSessionId();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.sessionStorage.setItem(PENDING_WORKSPACE_SESSION_ID_KEY, normalized);
|
||||||
|
window.localStorage.setItem(PENDING_WORKSPACE_SESSION_ID_KEY, normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function consumePendingWorkspaceSessionId() {
|
||||||
|
const sessionId = readPendingWorkspaceSessionId();
|
||||||
|
clearPendingWorkspaceSessionId();
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPendingWorkspaceSessionId() {
|
||||||
|
if (typeof window === "undefined") return null;
|
||||||
|
return (
|
||||||
|
normalizeSessionId(
|
||||||
|
window.sessionStorage.getItem(PENDING_WORKSPACE_SESSION_ID_KEY),
|
||||||
|
) ??
|
||||||
|
normalizeSessionId(
|
||||||
|
window.localStorage.getItem(PENDING_WORKSPACE_SESSION_ID_KEY),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearPendingWorkspaceSessionId() {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
window.sessionStorage.removeItem(PENDING_WORKSPACE_SESSION_ID_KEY);
|
||||||
|
window.localStorage.removeItem(PENDING_WORKSPACE_SESSION_ID_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeSessionId(value: unknown) {
|
function normalizeSessionId(value: unknown) {
|
||||||
if (typeof value !== "string") return null;
|
if (typeof value !== "string") return null;
|
||||||
const trimmed = value.trim();
|
const trimmed = value.trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user