惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
博客园_首页
罗磊的独立博客
云风的 BLOG
云风的 BLOG
J
Java Code Geeks
Last Week in AI
Last Week in AI
D
DataBreaches.Net
Jina AI
Jina AI
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
D
Docker
MongoDB | Blog
MongoDB | Blog
B
Blog RSS Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
宝玉的分享
宝玉的分享
Engineering at Meta
Engineering at Meta
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
H
Help Net Security
T
The Blog of Author Tim Ferriss

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(tui): refresh after external session reset (#93562) ·...
vincentkoc · 2026-06-16 · via Recent Commits to openclaw:main

@@ -2,7 +2,13 @@

22

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

33

import { MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "../shared/assistant-error-format.js";

44

import { createEventHandlers } from "./tui-event-handlers.js";

5-

import type { AgentEvent, BtwEvent, ChatEvent, TuiStateAccess } from "./tui-types.js";

5+

import type {

6+

AgentEvent,

7+

BtwEvent,

8+

ChatEvent,

9+

SessionChangedEvent,

10+

TuiStateAccess,

11+

} from "./tui-types.js";

612713

type MockFn = ReturnType<typeof vi.fn>;

814

type HandlerChatLog = {

@@ -133,6 +139,7 @@ describe("tui-event-handlers: handleAgentEvent", () => {

133139

chatLog?: HandlerChatLog;

134140

btw?: HandlerBtwPresenter;

135141

localMode?: boolean;

142+

refreshSessionInfo?: () => Promise<void>;

136143

}) => {

137144

const state = makeState(params?.state);

138145

const context = makeContext(state);

@@ -144,10 +151,12 @@ describe("tui-event-handlers: handleAgentEvent", () => {

144151

state,

145152

localMode: params?.localMode,

146153

setActivityStatus: context.setActivityStatus,

154+

refreshSessionInfo: params?.refreshSessionInfo,

147155

loadHistory: context.loadHistory,

148156

noteLocalRunId: context.noteLocalRunId,

149157

isLocalRunId: context.isLocalRunId,

150158

forgetLocalRunId: context.forgetLocalRunId,

159+

clearLocalRunIds: context.clearLocalRunIds,

151160

isLocalBtwRunId: context.isLocalBtwRunId,

152161

forgetLocalBtwRunId: context.forgetLocalBtwRunId,

153162

clearLocalBtwRunIds: context.clearLocalBtwRunIds,

@@ -873,6 +882,122 @@ describe("tui-event-handlers: handleAgentEvent", () => {

873882

expect(tui.requestRender).not.toHaveBeenCalled();

874883

});

875884885+

it("reloads the current session and clears stale run state after sessions.changed reset", () => {

886+

const refreshSessionInfo = vi.fn<() => Promise<void>>(async () => undefined);

887+

const {

888+

state,

889+

chatLog,

890+

btw,

891+

tui,

892+

loadHistory,

893+

setActivityStatus,

894+

noteLocalRunId,

895+

isLocalRunId,

896+

handleChatEvent,

897+

handleAgentEvent,

898+

handleSessionsChangedEvent,

899+

} = createHandlersHarness({

900+

state: {

901+

activeChatRunId: null,

902+

currentSessionId: "session-before",

903+

sessionInfo: { verboseLevel: "on", updatedAt: 100 },

904+

},

905+

refreshSessionInfo,

906+

});

907+908+

handleChatEvent({

909+

runId: "run-old",

910+

sessionKey: state.currentSessionKey,

911+

state: "final",

912+

message: { content: [{ type: "text", text: "done" }] },

913+

});

914+

noteLocalRunId("run-local");

915+

state.activeChatRunId = "run-stale";

916+

state.pendingChatRunId = "run-pending";

917+

state.pendingOptimisticUserMessage = true;

918+

state.pendingSubmitDraft = { runId: "run-pending", text: "pending" };

919+

state.activityStatus = "streaming";

920+

loadHistory.mockClear();

921+

refreshSessionInfo.mockClear();

922+

chatLog.startTool.mockClear();

923+

btw.clear.mockClear();

924+

tui.requestRender.mockClear();

925+

setActivityStatus.mockClear();

926+927+

handleSessionsChangedEvent({

928+

sessionKey: "main",

929+

reason: "reset",

930+

sessionId: "session-after",

931+

updatedAt: 200,

932+

} satisfies SessionChangedEvent);

933+934+

expect(state.activeChatRunId).toBeNull();

935+

expect(state.pendingChatRunId).toBeNull();

936+

expect(state.pendingSubmitDraft).toBeNull();

937+

expect(state.pendingOptimisticUserMessage).toBe(false);

938+

expect(state.activityStatus).toBe("idle");

939+

expect(state.currentSessionId).toBe("session-after");

940+

expect(state.sessionInfo.updatedAt).toBe(200);

941+

expect(isLocalRunId("run-local")).toBe(false);

942+

expect(setActivityStatus).toHaveBeenCalledWith("idle");

943+

expect(btw.clear).toHaveBeenCalledTimes(1);

944+

expect(loadHistory).toHaveBeenCalledTimes(1);

945+

expect(refreshSessionInfo).not.toHaveBeenCalled();

946+

expect(tui.requestRender).toHaveBeenCalledTimes(1);

947+948+

handleAgentEvent({

949+

runId: "run-old",

950+

stream: "tool",

951+

data: { phase: "start", toolCallId: "tc-old", name: "exec" },

952+

});

953+954+

expect(chatLog.startTool).not.toHaveBeenCalled();

955+

});

956+957+

it("ignores sessions.changed reset events for other sessions", () => {

958+

const { state, loadHistory, setActivityStatus, handleSessionsChangedEvent } =

959+

createHandlersHarness({

960+

state: { activeChatRunId: "run-current", activityStatus: "streaming" },

961+

});

962+963+

handleSessionsChangedEvent({

964+

sessionKey: "agent:other:main",

965+

reason: "reset",

966+

} satisfies SessionChangedEvent);

967+968+

expect(state.activeChatRunId).toBe("run-current");

969+

expect(state.activityStatus).toBe("streaming");

970+

expect(loadHistory).not.toHaveBeenCalled();

971+

expect(setActivityStatus).not.toHaveBeenCalledWith("idle");

972+

});

973+974+

it("ignores selected-global sessions.changed reset events from other agents", () => {

975+

const { state, loadHistory, setActivityStatus, handleSessionsChangedEvent } =

976+

createHandlersHarness({

977+

state: {

978+

agentDefaultId: "main",

979+

currentAgentId: "work",

980+

currentSessionKey: "global",

981+

activeChatRunId: "run-current",

982+

activityStatus: "streaming",

983+

},

984+

});

985+986+

handleSessionsChangedEvent({

987+

sessionKey: "global",

988+

agentId: "main",

989+

reason: "reset",

990+

sessionId: "session-other-agent",

991+

updatedAt: 300,

992+

} satisfies SessionChangedEvent);

993+994+

expect(state.activeChatRunId).toBe("run-current");

995+

expect(state.activityStatus).toBe("streaming");

996+

expect(state.currentSessionId).not.toBe("session-other-agent");

997+

expect(loadHistory).not.toHaveBeenCalled();

998+

expect(setActivityStatus).not.toHaveBeenCalledWith("idle");

999+

});

1000+8761001

it("accepts tool events after chat final for the same run", () => {

8771002

const { state, chatLog, tui, handleChatEvent, handleAgentEvent } = createHandlersHarness({

8781003

state: { activeChatRunId: null },