Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/tui/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,13 @@ const renderer = await createCliRenderer({ exitOnCtrlC: false, targetFps: 30, au
// OpenTUI 默认透明背景不会擦除变空的 cell,滚动/重排后会残留上一帧字符。
renderer.setBackgroundColor(batonTheme.overlayBackground ?? "#24283b");
const root = createRoot(renderer);
const quit = () => {
const quit = (sessionId?: string) => {
// OpenTUI owns raw mode and mouse tracking; restore both before process.exit,
// whose forced exit does not run OpenTUI's beforeExit cleanup handler.
renderer.destroy();
if (sessionId) {
console.log(`\nResume this session with:\nbaton resume ${sessionId}\n\nFork this session with:\nbaton fork ${sessionId}`);
}
process.exit(0);
};

Expand Down
6 changes: 3 additions & 3 deletions src/tui/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class BatonChatProtocol implements ChatProtocol {
private readonly store: SessionStore,
private readonly config: BatonConfig,
opened: { session: SessionHandle; resumed: boolean; recovered?: boolean },
private readonly quit: () => void,
private readonly quit: (sessionId?: string) => void,
) {
this.session = opened.session;
this.agent = config.defaultAgent;
Expand Down Expand Up @@ -256,7 +256,7 @@ export class BatonChatProtocol implements ChatProtocol {
await this.runtime.close();
this.unsubscribeSession();
this.session.releaseLock();
this.quit();
this.quit(this.session.id);
}

resolvePicker(id: string, value: string | null): void {
Expand Down Expand Up @@ -492,7 +492,7 @@ export class BatonChatProtocol implements ChatProtocol {
}
: null,
status: this.status,
footer: `in:${v.usage.inputTokens} out:${v.usage.outputTokens} turns:${v.turnSummaries.length} queue:${this.runtime.queueLength}${planActive ? ` plan:${planEntries.filter((entry) => entry.status === "completed").length}/${planEntries.length}` : ""} cwd:${this.session.meta.cwd}`,
footer: `session:${this.session.id} in:${v.usage.inputTokens} out:${v.usage.outputTokens} turns:${v.turnSummaries.length} queue:${this.runtime.queueLength}${planActive ? ` plan:${planEntries.filter((entry) => entry.status === "completed").length}/${planEntries.length}` : ""} cwd:${this.session.meta.cwd}`,
// ↑ 召回提示只在"可召回"时出现:交互发生地是 composer(placeholder 天然只在空输入时可见)
// busy 且可 steer 时提示 Enter 的实际语义(design §3.2:delivery 对用户可见、可预期)
composerPlaceholder: `Message ${this.agent} (/ commands, @ mentions, ${
Expand Down
7 changes: 4 additions & 3 deletions tests/tui-protocol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ describe("BatonChatProtocol exit", () => {
const store = new SessionStore(root);
const session = store.createSession({ cwd: "/repo" });
const calls: string[] = [];
const protocol = new BatonChatProtocol(store, DEFAULT_CONFIG, { session, resumed: false }, () => {
calls.push("quit");
const protocol = new BatonChatProtocol(store, DEFAULT_CONFIG, { session, resumed: false }, (sessionId) => {
calls.push(`quit:${sessionId}`);
});

const internals = protocol as unknown as {
Expand All @@ -31,7 +31,7 @@ describe("BatonChatProtocol exit", () => {
};

await protocol.exit();
expect(calls).toEqual(["runtime", "lock", "quit"]);
expect(calls).toEqual(["runtime", "lock", `quit:${session.id}`]);
} finally {
rmSync(root, { recursive: true, force: true });
}
Expand Down Expand Up @@ -138,6 +138,7 @@ describe("BatonChatProtocol view projection", () => {
expect(view.runStatus?.[0]).toMatchObject({ author: "codex", label: "default" });
expect(view.runStatus?.[0]?.startedAt).toBeUndefined();
expect(view.runStatus?.[0]?.hint).toBeUndefined();
expect(view.footer).toStartWith(`session:${session.id} `);
expect(view.composerPlaceholder).toContain("Ctrl+J newline");
await protocol.exit();
} finally {
Expand Down