mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
Merge pull request #11667 from Harbor404/fix/studio-11605-copy-session-info
feat(studio): copy session handoff information
This commit is contained in:
6 files changed
+107
No files matched your search
@@ -81,6 +81,7 @@ export const ACTIONS: UIAction[] = [
|
||||
// refused while a turn runs, so it never stops work.
|
||||
{ id: "session.close", kind: "kernel-mutation", target: "entity", proof: "interaction" },
|
||||
{ id: "session.archive", kind: "kernel-mutation", target: "entity", proof: "authority-effect" },
|
||||
{ id: "session.copy-info", kind: "shell-native", target: "entity", proof: "interaction" },
|
||||
{ id: "session.export", kind: "shell-native", target: "entity", proof: "browser" },
|
||||
{ id: "session.delete", kind: "destructive", target: "entity", proof: "authority-effect" },
|
||||
|
||||
|
||||
@@ -1124,6 +1124,11 @@ export const EN: Record<string, string> = {
|
||||
"读取": "Read",
|
||||
"本地工作区": "Local workspace",
|
||||
"暂停执行": "Pause run",
|
||||
"复制会话信息": "Copy session information",
|
||||
"会话 ID": "Session ID",
|
||||
"会话上下文路径": "Session context path",
|
||||
"任务路径": "Task path",
|
||||
"任务日志": "Task log",
|
||||
"导出会话": "Export session",
|
||||
"取消置顶": "Unpin",
|
||||
"置顶会话": "Pin session",
|
||||
|
||||
@@ -8,6 +8,8 @@ import { pinToViewport } from "./place";
|
||||
import { useRailQuery } from "./railsearch";
|
||||
import { StudioIcon } from "./StudioIcon";
|
||||
import { Cross } from "./glyphs";
|
||||
import { copyText } from "./CopyButton";
|
||||
import { sessionInfoText } from "./sessionInfo";
|
||||
import { download } from "../port/download";
|
||||
import { host } from "../port/host";
|
||||
import { useTreeKeys } from "./tree";
|
||||
@@ -591,6 +593,9 @@ function WorkspacesView({ hub, tree, treeRead, runtimes, active, folded, reload,
|
||||
</div>
|
||||
<div className="session-pop-divider" />
|
||||
<div className="session-pop-group">
|
||||
<button role="menuitem" data-action="session.copy-info" data-target={session.path} onClick={() => { void copyText(sessionInfoText(session, ws.root)).catch(onError); setSessionMenu(""); }}>
|
||||
<StudioIcon name="copy" /><span>{t("复制会话信息")}</span>
|
||||
</button>
|
||||
<button role="menuitem" data-action="session.export" data-target={session.path} disabled={busy === "export:" + session.path} onClick={() => void saveSession(session)}>
|
||||
<StudioIcon name="download" /><span>{t("导出会话")}</span><small>JSON</small>
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// @vitest-environment jsdom
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import "./testkit";
|
||||
import { Workspaces } from "./Workspaces";
|
||||
import type { TreeWorkspace } from "../port/hub";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const SESSION = "/w/.reasonix/sessions/20260901-120000.jsonl";
|
||||
|
||||
it("copies the four handoff fields from the session menu", async () => {
|
||||
const user = userEvent.setup();
|
||||
const write = vi.spyOn(navigator.clipboard, "writeText").mockResolvedValue();
|
||||
const tree: TreeWorkspace[] = [
|
||||
{ root: "/w/project", name: "project", sessions: [{ path: SESSION, name: "20260901-120000", title: "handoff me" }] },
|
||||
];
|
||||
render(
|
||||
<Workspaces
|
||||
hub={{} as never}
|
||||
tree={tree}
|
||||
treeRead
|
||||
runtimes={[]}
|
||||
active=""
|
||||
folded={new Set()}
|
||||
onFold={() => {}}
|
||||
reload={async () => {}}
|
||||
onOpen={async () => {}}
|
||||
onFocus={() => {}}
|
||||
onClose={async () => {}}
|
||||
liveIds={() => []}
|
||||
runs={{}}
|
||||
onRename={() => {}}
|
||||
onError={() => {}}
|
||||
adder={{ add: () => {}, close: () => {}, at: null } as never}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.pointer({ keys: "[MouseRight]", target: screen.getByRole("treeitem", { name: /handoff me/ }) });
|
||||
await user.click(screen.getByRole("menuitem", { name: "复制会话信息" }));
|
||||
|
||||
expect(write).toHaveBeenCalledWith([
|
||||
"会话 ID: 20260901-120000",
|
||||
"会话上下文路径: /w/.reasonix/sessions",
|
||||
"任务路径: /w/project",
|
||||
"任务日志: /w/.reasonix/sessions/20260901-120000.jsonl",
|
||||
].join("\n"));
|
||||
expect(screen.queryByRole("menu")).toBeNull();
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sessionInfoText } from "./sessionInfo";
|
||||
|
||||
describe("session handoff information", () => {
|
||||
it("names the four paths another agent needs, with the context directory kept distinct from the task log", () => {
|
||||
expect(sessionInfoText(
|
||||
{ name: "20261002-handoff", path: "/home/me/.reasonix/sessions/20261002-handoff.jsonl" },
|
||||
"/home/me/project",
|
||||
)).toBe([
|
||||
"会话 ID: 20261002-handoff",
|
||||
"会话上下文路径: /home/me/.reasonix/sessions",
|
||||
"任务路径: /home/me/project",
|
||||
"任务日志: /home/me/.reasonix/sessions/20261002-handoff.jsonl",
|
||||
].join("\n"));
|
||||
});
|
||||
|
||||
it("keeps a Windows context directory intact", () => {
|
||||
expect(sessionInfoText(
|
||||
{ name: "20261002-win", path: String.raw`C:\Users\me\.reasonix\sessions\20261002-win.jsonl` },
|
||||
String.raw`C:\work\project`,
|
||||
)).toContain(String.raw`会话上下文路径: C:\Users\me\.reasonix\sessions`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { t } from "../i18n";
|
||||
|
||||
interface SessionInfo {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
function parentPath(path: string): string {
|
||||
const at = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
||||
return at > 0 ? path.slice(0, at) : path;
|
||||
}
|
||||
|
||||
export function sessionInfoText(session: SessionInfo, taskPath: string): string {
|
||||
return [
|
||||
`${t("会话 ID")}: ${session.name}`,
|
||||
`${t("会话上下文路径")}: ${parentPath(session.path)}`,
|
||||
`${t("任务路径")}: ${taskPath}`,
|
||||
`${t("任务日志")}: ${session.path}`,
|
||||
].join("\n");
|
||||
}
|
||||
Reference in new issue
Block a user