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

推荐订阅源

D
DataBreaches.Net
GbyAI
GbyAI
aimingoo的专栏
aimingoo的专栏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
腾讯CDC
博客园 - Franky
Engineering at Meta
Engineering at Meta
C
Check Point Blog
T
The Blog of Author Tim Ferriss
有赞技术团队
有赞技术团队
Microsoft Azure Blog
Microsoft Azure Blog
MyScale Blog
MyScale Blog
I
InfoQ
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
WordPress大学
WordPress大学

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(tests): avoid runtime discovery in routed reply check...
vincentkoc · 2026-06-16 · via Recent Commits to openclaw:main

@@ -480,6 +480,16 @@ vi.mock("../../infra/outbound/session-binding-service.js", () => ({

480480

unbind: vi.fn(async () => []),

481481

}),

482482

}));

483+

vi.mock("../../bindings/records.js", () => ({

484+

resolveConversationBindingRecord: (conversation: {

485+

channel: string;

486+

accountId: string;

487+

conversationId: string;

488+

parentConversationId?: string;

489+

}) => sessionBindingMocks.resolveByConversation(conversation),

490+

touchConversationBindingRecord: (...args: [bindingId: string, at?: number]) =>

491+

sessionBindingMocks.touch(...args),

492+

}));

483493

vi.mock("../../infra/agent-events.js", () => ({

484494

emitAgentEvent: (params: unknown) => agentEventMocks.emitAgentEvent(params),

485495

onAgentEvent: (listener: unknown) => agentEventMocks.onAgentEvent(listener),

@@ -594,6 +604,11 @@ const automaticGroupReplyConfig = {

594604

},

595605

},

596606

} as const satisfies OpenClawConfig;

607+

const automaticDirectReplyConfig = {

608+

messages: {

609+

visibleReplies: "automatic",

610+

},

611+

} as const satisfies OpenClawConfig;

597612

let dispatchReplyFromConfig: typeof import("./dispatch-from-config.js").dispatchReplyFromConfig;

598613

let dispatchFromConfigTesting: typeof import("./dispatch-from-config.js").testing;

599614

let resetInboundDedupe: typeof import("./inbound-dedupe.js").resetInboundDedupe;

@@ -854,6 +869,28 @@ function firstRouteReplyCall(): Record<string, unknown> {

854869

return call as Record<string, unknown>;

855870

}

856871872+

function installThreadingTestPlugin(params: { defaultAccountId?: string; id: string }) {

873+

const plugin = createChannelTestPluginBase({ id: params.id });

874+

const defaultAccountId = params.defaultAccountId;

875+

setActivePluginRegistry(

876+

createTestRegistry([

877+

{

878+

pluginId: params.id,

879+

source: "test",

880+

plugin: {

881+

...plugin,

882+

config: defaultAccountId

883+

? { ...plugin.config, defaultAccountId: () => defaultAccountId }

884+

: plugin.config,

885+

threading: {

886+

resolveReplyToMode: () => "all",

887+

},

888+

},

889+

},

890+

]),

891+

);

892+

}

893+857894

function requireToolResultHandler(

858895

handler: GetReplyOptions["onToolResult"] | undefined,

859896

): NonNullable<GetReplyOptions["onToolResult"]> {

@@ -945,6 +982,25 @@ describe("dispatchReplyFromConfig", () => {

945982

),

946983

},

947984

};

985+

const passiveThreadingTestPlugins = [

986+

"slack",

987+

"telegram",

988+

"feishu",

989+

"mattermost",

990+

"imessage",

991+

].map((id) => {

992+

const plugin = createChannelTestPluginBase({ id });

993+

return {

994+

pluginId: id,

995+

source: "test" as const,

996+

plugin: {

997+

...plugin,

998+

threading: {

999+

resolveReplyToMode: () => "all" as const,

1000+

},

1001+

},

1002+

};

1003+

});

9481004

setActivePluginRegistry(

9491005

createTestRegistry([

9501006

{

@@ -957,6 +1013,7 @@ describe("dispatchReplyFromConfig", () => {

9571013

source: "test",

9581014

plugin: signalTestPlugin,

9591015

},

1016+

...passiveThreadingTestPlugins,

9601017

]),

9611018

);

9621019

clearApprovalNativeRouteStateForTest();

@@ -1209,7 +1266,8 @@ describe("dispatchReplyFromConfig", () => {

12091266

it("does not route when Provider matches OriginatingChannel (even if Surface is missing)", async () => {

12101267

setNoAbort();

12111268

mocks.routeReply.mockClear();

1212-

const cfg = emptyConfig;

1269+

installThreadingTestPlugin({ id: "slack", defaultAccountId: "work" });

1270+

const cfg = automaticDirectReplyConfig;

12131271

const dispatcher = createDispatcher();

12141272

const ctx = buildTestCtx({

12151273

Provider: "slack",

@@ -1227,11 +1285,23 @@ describe("dispatchReplyFromConfig", () => {

1227128512281286

expect(mocks.routeReply).not.toHaveBeenCalled();

12291287

expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);

1288+

const replyDispatchCall = firstMockCall(hookMocks.runner.runReplyDispatch, "reply dispatch") as

1289+

| [

1290+

{

1291+

originatingAccountId?: unknown;

1292+

shouldRouteToOriginating?: unknown;

1293+

},

1294+

unknown,

1295+

]

1296+

| undefined;

1297+

expect(replyDispatchCall?.[0]?.shouldRouteToOriginating).toBe(false);

1298+

expect(replyDispatchCall?.[0]?.originatingAccountId).toBe("work");

12301299

});

1231130012321301

it("mirrors ownerless same-channel Slack finals after successful delivery", async () => {

12331302

setNoAbort();

12341303

mocks.routeReply.mockClear();

1304+

installThreadingTestPlugin({ id: "slack" });

12351305

const dispatcher = createDispatcher();

12361306

const ctx = buildTestCtx({

12371307

Provider: "slack",

@@ -1273,6 +1343,7 @@ describe("dispatchReplyFromConfig", () => {

1273134312741344

it("mirrors reset acknowledgements into the canonically prepared Slack session", async () => {

12751345

setNoAbort();

1346+

hookMocks.runner.hasHooks.mockReturnValue(false);

12761347

const dispatcher = createDispatcher();

12771348

const sessionKey = "Agent:Main:Slack:Channel:C123";

12781349

const preparedSessionKey = "agent:main:slack:channel:c123";

@@ -1352,16 +1423,19 @@ describe("dispatchReplyFromConfig", () => {

13521423

setNoAbort();

13531424

const dispatcher = createDispatcher();

13541425

mocks.routeReply.mockClear();

1426+

hookMocks.runner.hasHooks.mockReturnValue(false);

1427+

installThreadingTestPlugin({ id: "telegram", defaultAccountId: "default" });

1355142813561429

const result = await dispatchReplyFromConfig({

13571430

ctx: buildTestCtx({

13581431

Provider: "slack",

13591432

Surface: "slack",

13601433

OriginatingChannel: "telegram",

13611434

OriginatingTo: "telegram:999",

1435+

AccountId: "default",

13621436

SessionKey: "agent:main:telegram:group:999",

13631437

}),

1364-

cfg: emptyConfig,

1438+

cfg: automaticDirectReplyConfig,

13651439

dispatcher,

13661440

replyResolver: async () =>

13671441

setReplyPayloadMetadata(

@@ -1581,6 +1655,7 @@ describe("dispatchReplyFromConfig", () => {

1581165515821656

it("keeps non-Slack routed direct turns behind the active reply operation", async () => {

15831657

setNoAbort();

1658+

installThreadingTestPlugin({ id: "telegram" });

15841659

const sessionKey = "agent:main:telegram:direct:1";

15851660

const activeOperation = createReplyOperation({

15861661

sessionKey,

@@ -1627,6 +1702,7 @@ describe("dispatchReplyFromConfig", () => {

16271702

it("routes when OriginatingChannel differs from Provider", async () => {

16281703

setNoAbort();

16291704

mocks.routeReply.mockClear();

1705+

installThreadingTestPlugin({ id: "telegram" });

16301706

const cfg = emptyConfig;

16311707

const dispatcher = createDispatcher();

16321708

const ctx = buildTestCtx({

@@ -1667,6 +1743,7 @@ describe("dispatchReplyFromConfig", () => {

16671743

it("routes exec-event replies using persisted session delivery context when current turn has no originating route", async () => {

16681744

setNoAbort();

16691745

mocks.routeReply.mockClear();

1746+

installThreadingTestPlugin({ id: "telegram" });

16701747

sessionStoreMocks.currentEntry = {

16711748

deliveryContext: {

16721749

channel: "telegram",

@@ -1724,6 +1801,7 @@ describe("dispatchReplyFromConfig", () => {

17241801

it("routes sessions_send internal webchat handoffs through persisted external delivery context", async () => {

17251802

setNoAbort();

17261803

mocks.routeReply.mockClear();

1804+

installThreadingTestPlugin({ id: "feishu" });

17271805

sessionStoreMocks.currentEntry = {

17281806

route: {

17291807

channel: "feishu",

@@ -1835,6 +1913,7 @@ describe("dispatchReplyFromConfig", () => {

18351913

it("honors sendPolicy deny for recovered exec-event delivery channel", async () => {

18361914

setNoAbort();

18371915

mocks.routeReply.mockClear();

1916+

installThreadingTestPlugin({ id: "telegram" });

18381917

sessionStoreMocks.currentEntry = {

18391918

deliveryContext: {

18401919

channel: "telegram",

@@ -1908,6 +1987,7 @@ describe("dispatchReplyFromConfig", () => {

19081987

it("uses Slack DM TransportThreadId when ReplyToId is the current message", async () => {

19091988

setNoAbort();

19101989

mocks.routeReply.mockClear();

1990+

installThreadingTestPlugin({ id: "slack" });

19111991

const cfg = emptyConfig;

19121992

const dispatcher = createDispatcher();

19131993

const ctx = buildTestCtx({

@@ -1935,6 +2015,7 @@ describe("dispatchReplyFromConfig", () => {

19352015

it("does not resurrect a cleared route thread from origin metadata", async () => {

19362016

setNoAbort();

19372017

mocks.routeReply.mockClear();

2018+

installThreadingTestPlugin({ id: "mattermost" });

19382019

// Simulate the real store: lastThreadId and deliveryContext.threadId may be normalised from

19392020

// origin.threadId on read, but a non-thread session key must still route to channel root.

19402021

sessionStoreMocks.currentEntry = {

@@ -1975,6 +2056,7 @@ describe("dispatchReplyFromConfig", () => {

1975205619762057

it("forces suppressTyping when routing to a different originating channel", async () => {

19772058

setNoAbort();

2059+

installThreadingTestPlugin({ id: "telegram" });

19782060

const cfg = emptyConfig;

19792061

const dispatcher = createDispatcher();

19802062

const ctx = buildTestCtx({

@@ -2015,6 +2097,7 @@ describe("dispatchReplyFromConfig", () => {

20152097

it("routes when provider is webchat but surface carries originating channel metadata", async () => {

20162098

setNoAbort();

20172099

mocks.routeReply.mockClear();

2100+

installThreadingTestPlugin({ id: "telegram" });

20182101

const cfg = emptyConfig;

20192102

const dispatcher = createDispatcher();

20202103

const ctx = buildTestCtx({

@@ -2036,6 +2119,7 @@ describe("dispatchReplyFromConfig", () => {

20362119

it("routes Feishu replies when provider is webchat and origin metadata points to Feishu", async () => {

20372120

setNoAbort();

20382121

mocks.routeReply.mockClear();

2122+

installThreadingTestPlugin({ id: "feishu" });

20392123

const cfg = emptyConfig;

20402124

const dispatcher = createDispatcher();

20412125

const ctx = buildTestCtx({

@@ -2076,6 +2160,7 @@ describe("dispatchReplyFromConfig", () => {

20762160

it("does not route external origin replies when current surface is internal webchat without explicit delivery", async () => {

20772161

setNoAbort();

20782162

mocks.routeReply.mockClear();

2163+

installThreadingTestPlugin({ id: "imessage" });

20792164

const cfg = emptyConfig;

20802165

const dispatcher = createDispatcher();

20812166

const ctx = buildTestCtx({

@@ -2099,6 +2184,7 @@ describe("dispatchReplyFromConfig", () => {

20992184

it("routes external origin replies for internal webchat turns when explicit delivery is set", async () => {

21002185

setNoAbort();

21012186

mocks.routeReply.mockClear();

2187+

installThreadingTestPlugin({ id: "imessage" });

21022188

const cfg = emptyConfig;

21032189

const dispatcher = createDispatcher();

21042190

const ctx = buildTestCtx({

@@ -2128,6 +2214,7 @@ describe("dispatchReplyFromConfig", () => {

21282214

it("routes media-only tool results when summaries are suppressed", async () => {

21292215

setNoAbort();

21302216

mocks.routeReply.mockClear();

2217+

installThreadingTestPlugin({ id: "telegram" });

21312218

const cfg = automaticGroupReplyConfig;

21322219

const dispatcher = createDispatcher();

21332220

const ctx = buildTestCtx({

@@ -5497,6 +5584,7 @@ describe("dispatchReplyFromConfig", () => {

5497558454985585

it("deduplicates same-agent inbound replies across main and direct session keys", async () => {

54995586

setNoAbort();

5587+

hookMocks.runner.hasHooks.mockReturnValue(false);

55005588

const cfg = emptyConfig;

55015589

const replyResolver = vi.fn(async () => ({ text: "hi" }) as ReplyPayload);

55025590

const baseCtx = buildTestCtx({

@@ -5530,6 +5618,7 @@ describe("dispatchReplyFromConfig", () => {

55305618

it("emits message_received hook with originating channel metadata", async () => {

55315619

setNoAbort();

55325620

hookMocks.runner.hasHooks.mockReturnValue(true);

5621+

installThreadingTestPlugin({ id: "telegram" });

55335622

const cfg = emptyConfig;

55345623

const dispatcher = createDispatcher();

55355624

const ctx = buildTestCtx({

@@ -5789,6 +5878,7 @@ describe("dispatchReplyFromConfig", () => {

57895878

// would receive divergent keys on every native redirect.

57905879

setNoAbort();

57915880

mocks.routeReply.mockClear();

5881+

installThreadingTestPlugin({ id: "telegram" });

57925882

const cfg = emptyConfig;

57935883

const dispatcher = createDispatcher();

57945884

const ctx = buildTestCtx({

@@ -5825,6 +5915,7 @@ describe("dispatchReplyFromConfig", () => {

58255915

// generalization of the native-redirect branch.

58265916

setNoAbort();

58275917

mocks.routeReply.mockClear();

5918+

installThreadingTestPlugin({ id: "telegram" });

58285919

const cfg = emptyConfig;

58295920

const dispatcher = createDispatcher();

58305921

const ctx = buildTestCtx({

@@ -7669,6 +7760,7 @@ describe("before_dispatch hook", () => {

76697760

it("uses canonical hook metadata and shared routed final delivery", async () => {

76707761

ttsMocks.state.synthesizeFinalAudio = true;

76717762

hookMocks.runner.runBeforeDispatch.mockResolvedValue({ handled: true, text: "Blocked" });

7763+

installThreadingTestPlugin({ id: "telegram" });

76727764

const dispatcher = createDispatcher();

76737765

const ctx = createHookCtx({

76747766

Body: "raw body",