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

推荐订阅源

WordPress大学
WordPress大学
博客园 - 司徒正美
宝玉的分享
宝玉的分享
阮一峰的网络日志
阮一峰的网络日志
The Cloudflare Blog
月光博客
月光博客
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
雷峰网
雷峰网
博客园 - 叶小钗
量子位
IT之家
IT之家

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 WebChat dispatch failure session status (#84352) · op...
jesse-merhi · 2026-06-23 · via Recent Commits to openclaw:main

@@ -154,6 +154,7 @@ import { ADMIN_SCOPE } from "../method-scopes.js";

154154

import { chatAbortMarkerTimestampMs, type ChatRunTiming } from "../server-chat-state.js";

155155

import { getMaxChatHistoryMessagesBytes, MAX_PAYLOAD_BYTES } from "../server-constants.js";

156156

import { resolveSessionHistoryTailReadOptions } from "../session-history-state.js";

157+

import { persistGatewaySessionLifecycleEvent } from "../session-lifecycle-state.js";

157158

import { readSessionTranscriptIndex } from "../session-transcript-index.fs.js";

158159

import {

159160

capArrayByJsonBytes,

@@ -3746,6 +3747,14 @@ export const chatHandlers: GatewayRequestHandlers = {

37463747

const deliveredReplies: Array<{ payload: ReplyPayload; kind: "block" | "final" }> = [];

37473748

let appendedWebchatAgentMedia = false;

37483749

let agentRunStarted = false;

3750+

let pendingDispatchLifecycleError:

3751+

| {

3752+

endedAt: number;

3753+

error: string;

3754+

sessionId: string;

3755+

startedAt: number;

3756+

}

3757+

| undefined;

37493758

const userTurnRecorder: UserTurnTranscriptRecorder = createUserTurnTranscriptRecorder({

37503759

input: baseUserTurnInput,

37513760

resolveInput: () => userTurnInputPromise,

@@ -4957,6 +4966,7 @@ export const chatHandlers: GatewayRequestHandlers = {

49574966

);

49584967

})

49594968

.catch(async (err: unknown) => {

4969+

const errorMessage = String(err);

49604970

const emitAfterError =

49614971

userTurnRecorder.hasPersisted() || userTurnRecorder.isBlocked()

49624972

? Promise.resolve()

@@ -4966,7 +4976,19 @@ export const chatHandlers: GatewayRequestHandlers = {

49664976

`webchat user transcript update failed after error: ${formatForLog(transcriptErr)}`,

49674977

);

49684978

});

4969-

const error = errorShape(ErrorCodes.UNAVAILABLE, String(err));

4979+

if (

4980+

!agentRunStarted &&

4981+

!activeRunAbort.controller.signal.aborted &&

4982+

!context.chatAbortedRuns.has(clientRunId)

4983+

) {

4984+

pendingDispatchLifecycleError = {

4985+

endedAt: Date.now(),

4986+

error: errorMessage,

4987+

sessionId: activeRunAbort.entry?.sessionId ?? backingSessionId ?? clientRunId,

4988+

startedAt: activeRunAbort.entry?.startedAtMs ?? now,

4989+

};

4990+

}

4991+

const error = errorShape(ErrorCodes.UNAVAILABLE, errorMessage);

49704992

setGatewayDedupeEntry({

49714993

dedupe: context.dedupe,

49724994

key: `chat:${clientRunId}`,

@@ -4976,7 +4998,7 @@ export const chatHandlers: GatewayRequestHandlers = {

49764998

payload: {

49774999

runId: clientRunId,

49785000

status: "error" as const,

4979-

summary: String(err),

5001+

summary: errorMessage,

49805002

},

49815003

error,

49825004

},

@@ -4986,14 +5008,61 @@ export const chatHandlers: GatewayRequestHandlers = {

49865008

runId: clientRunId,

49875009

sessionKey,

49885010

agentId,

4989-

errorMessage: String(err),

5011+

errorMessage,

49905012

});

49915013

})

49925014

.finally(() => {

49935015

activeRunAbort.cleanup();

49945016

clearAgentRunContext(clientRunId, lifecycleGeneration);

49955017

clearActiveChatSendDedupeRun(context.dedupe, activeChatSendDedupeKey, clientRunId);

49965018

context.removeChatRun(clientRunId, clientRunId, sessionKey);

5019+

if (!pendingDispatchLifecycleError) {

5020+

return;

5021+

}

5022+

const persistDispatchLifecycleError = async () => {

5023+

const dispatchError = pendingDispatchLifecycleError;

5024+

if (!dispatchError) {

5025+

return;

5026+

}

5027+

const hasActiveRun = hasTrackedActiveSessionRun({

5028+

context,

5029+

requestedKey: rawSessionKey,

5030+

canonicalKey: sessionKey,

5031+

...(sessionKey === "global" && agentId ? { agentId } : {}),

5032+

defaultAgentId: resolveDefaultAgentId(cfg),

5033+

});

5034+

if (hasActiveRun) {

5035+

return;

5036+

}

5037+

try {

5038+

await persistGatewaySessionLifecycleEvent({

5039+

sessionKey,

5040+

...(sessionKey === "global" && agentId ? { agentId } : {}),

5041+

event: {

5042+

runId: clientRunId,

5043+

sessionId: dispatchError.sessionId,

5044+

lifecycleGeneration,

5045+

ts: dispatchError.endedAt,

5046+

data: {

5047+

phase: "error",

5048+

startedAt: dispatchError.startedAt,

5049+

endedAt: dispatchError.endedAt,

5050+

error: dispatchError.error,

5051+

},

5052+

},

5053+

});

5054+

emitSessionsChanged(context, {

5055+

sessionKey,

5056+

...(agentId ? { agentId } : {}),

5057+

reason: "chat.dispatch-error",

5058+

});

5059+

} catch (persistErr: unknown) {

5060+

context.logGateway.warn(

5061+

`webchat session lifecycle persist failed after error: ${formatForLog(persistErr)}`,

5062+

);

5063+

}

5064+

};

5065+

void persistDispatchLifecycleError();

49975066

});

49985067

} catch (err) {

49995068

activeRunAbort.cleanup({ force: true });