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

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

Recent Commits to openclaw:main

fix(gateway): make bare reset commands fast docs: update crabbox skill cache-volume guidance docs: clarify message-tool-only visible replies feat: scope group mention patterns by channel fix(xiaomi): support MiMo voicedesign TTS fix(cron): accept sub-second --at datetimes resolved in a timezone (#… fix(infra): guard against overwriting corrupt target session store du… feat(workboard): add worker dispatch CLI fix(memory): respect QMD status timeout · openclaw/openclaw@1d55caa fix(ui): prefer Talk source-reply final text · openclaw/openclaw@17c2e95 fix(scripts): timeout crabbox wrapper sanity checks · openclaw/openclaw@a0d2feb refactor(telegram): keep topic thread mapping plugin-local fix(memory): fail open when embedding recall stalls feat(doctor): expose UI freshness health findings · openclaw/openclaw@2d3fa48 docs: expand MCP operator guide chore(ui): refresh fa control ui locale chore(ui): refresh nl control ui locale · openclaw/openclaw@53e0639 chore(ui): refresh vi control ui locale chore(ui): refresh pl control ui locale · openclaw/openclaw@ee39aa8 chore(ui): refresh th control ui locale chore(ui): refresh id control ui locale · openclaw/openclaw@fce45a2 chore(ui): refresh uk control ui locale chore(ui): refresh tr control ui locale · openclaw/openclaw@b335018 chore(ui): refresh ar control ui locale chore(ui): refresh it control ui locale · openclaw/openclaw@20ab73e chore(ui): refresh fr control ui locale · openclaw/openclaw@a041e39 chore(ui): refresh ko control ui locale · openclaw/openclaw@2e0d191 chore(ui): refresh ja-JP control ui locale · openclaw/openclaw@ec949a8 chore(ui): refresh es control ui locale · openclaw/openclaw@0b9193c chore(ui): refresh pt-BR control ui locale · openclaw/openclaw@aa56f59 chore(ui): refresh zh-TW control ui locale · openclaw/openclaw@10b4057 chore(ui): refresh zh-CN control ui locale · openclaw/openclaw@ecef6ae chore(ui): refresh de control ui locale · openclaw/openclaw@f456114 feat: improve MCP operator controls (#88536) · openclaw/openclaw@617c658 chore(lint): clean sms lint fallout · openclaw/openclaw@3258338 fix(ci): repair sms channel checks · openclaw/openclaw@3a4943e fix(e2e): make plugin sweep wrappers executable · openclaw/openclaw@84b025e fix(auto-reply): redact secrets in config show output (#88496) · openclaw/openclaw@a776de2 fix(ci): repair copilot sdk drift chore(lint): enable object and reassignment rules · openclaw/openclaw@f5eca3f docs: expand SMS channel setup guide · openclaw/openclaw@ea11b8a ci: harden Crabbox Testbox runs · openclaw/openclaw@d4f78c9 fix(webchat): preserve refresh-visible history and composer state (#8… fix(check): restore core typecheck feat: add Twilio SMS channel fix(telegram): support media message edits chore: remove stale unsafe assertion suppressions Revert "fix(ci): annotate unsafe boundary casts" fix(ci): keep unsafe assertion lint disabled Revert "fix(ci): clean core unsafe assertions" · openclaw/openclaw@4471335 test: remove channel test isolation hack fix(agents): clear stale compaction bindings fix(agents): skip below-target CLI compaction failures · openclaw/openclaw@5e1e029 chore: update dependencies · openclaw/openclaw@48ccc50 docs: add Skill Workshop guide docs: remove divider comments (#88115) fix(ci): clean core unsafe assertions fix(discord): deliver same-session channel replies fix(ci): annotate unsafe boundary casts · openclaw/openclaw@8a40f90 docs: add permission modes page · openclaw/openclaw@ae651e7 fix(ci): repair extension type drift · openclaw/openclaw@4d95ae3 perf: avoid blocking gateway bind on control ui build · openclaw/openclaw@e678225 chore(lint): enable structured clone rules · openclaw/openclaw@59694e8 Fix iMessage startup watch replay (#88406) · openclaw/openclaw@87664ed fix(ci): isolate timer-sensitive tests perf: lazy-load agent reply payload formatter · openclaw/openclaw@122ae5d refactor(telegram): persist plugin state in sqlite · openclaw/openclaw@930b371 chore(lint): enable additional cleanup rules · openclaw/openclaw@b9fe089 chore: remove stale dead code · openclaw/openclaw@444562b fix(ci): stabilize ui paste and telegram types · openclaw/openclaw@ce547bf fix(ci): satisfy strict nullish guards · openclaw/openclaw@d4d7fdb build(OpenClawKit): make ElevenLabsKit optional behind Talk trait · openclaw/openclaw@096bd13 chore(lint): enable more readability rules fix(ci): stop channel timers holding vitest open chore(lint): enable readability lint rules · openclaw/openclaw@deb7bc6 fix(ci): restore main validation · openclaw/openclaw@0211a3a perf: prewarm gateway runtime plugins · openclaw/openclaw@ade6e77 fix(slack): keep DM thread turns out of active steering · openclaw/openclaw@f1cb9f2 fix(commands): make /skill load workspace skills · openclaw/openclaw@667393b fix(telegram): align DM topic session routing · openclaw/openclaw@72c61bc test: keep timeout clamp checks under one second · openclaw/openclaw@b372af6 test: keep vitest cases under one second test: restore marketplace cleanup coverage · openclaw/openclaw@9f99acf fix(shared): restore number coercion barrel fix: clarify generated media reply prompts (#88458) · openclaw/openclaw@8a46790 fix(qa): clamp transport wait intervals fix(qa): clamp gateway restart polling fix(channels): clamp typing timers · openclaw/openclaw@92f1d90 fix(agents): clamp embedded run drain polling fix(sqlite): clamp WAL checkpoint intervals · openclaw/openclaw@287f531 fix(onboard): clamp gateway reachability polling · openclaw/openclaw@bb680a8 fix(nostr): bound seen tracker capacity · openclaw/openclaw@5b0036f fix(agents): clamp session suspension TTLs · openclaw/openclaw@11c050d fix(memory): clamp batch timeout minutes · openclaw/openclaw@84061c1 fix(gateway): clamp auth limiter prune intervals · openclaw/openclaw@a1d7a75 fix(memory): clamp sync interval timers · openclaw/openclaw@5c38c0c fix(gateway): clamp auth limiter durations fix(qa): clamp cron run poll intervals · openclaw/openclaw@83597b7 fix(gateway): bound health monitor intervals fix(auto-reply): clamp typing timers · openclaw/openclaw@db94eac
fix(agents): route per-turn media task hints below the cache boundary… · openclaw/openclaw@778c4f9
nxmxbbd · 2026-05-31 · via Recent Commits to openclaw:main

@@ -0,0 +1,149 @@

1+

// Regression guard for #85203: per-turn media-generation task hints must sit BELOW

2+

// the system-prompt cache boundary so the cacheable prefix stays byte-identical

3+

// turn-to-turn. Mirrors the composition order used at attempt.ts (embedded runner)

4+

// and cli-runner/prepare.ts: hook prependSystemContext stays in the cacheable prefix,

5+

// media task hints are routed below the boundary via prependSystemPromptAddition.

6+

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

7+8+

const imageGenerationTaskStatusMocks = vi.hoisted(() => ({

9+

buildActiveImageGenerationTaskPromptContextForSession: vi.fn(),

10+

buildImageGenerationTaskStatusDetails: vi.fn(() => ({})),

11+

buildImageGenerationTaskStatusText: vi.fn(() => "Image generation task status"),

12+

findActiveImageGenerationTaskForSession: vi.fn(),

13+

getImageGenerationTaskProviderId: vi.fn(),

14+

isActiveImageGenerationTask: vi.fn(() => false),

15+

IMAGE_GENERATION_TASK_KIND: "image_generation",

16+

}));

17+

const videoGenerationTaskStatusMocks = vi.hoisted(() => ({

18+

buildActiveVideoGenerationTaskPromptContextForSession: vi.fn(),

19+

buildVideoGenerationTaskStatusDetails: vi.fn(() => ({})),

20+

buildVideoGenerationTaskStatusText: vi.fn(() => "Video generation task status"),

21+

findActiveVideoGenerationTaskForSession: vi.fn(),

22+

getVideoGenerationTaskProviderId: vi.fn(),

23+

isActiveVideoGenerationTask: vi.fn(() => false),

24+

VIDEO_GENERATION_TASK_KIND: "video_generation",

25+

}));

26+

const musicGenerationTaskStatusMocks = vi.hoisted(() => ({

27+

buildActiveMusicGenerationTaskPromptContextForSession: vi.fn(),

28+

buildMusicGenerationTaskStatusDetails: vi.fn(() => ({})),

29+

buildMusicGenerationTaskStatusText: vi.fn(() => "Music generation task status"),

30+

findActiveMusicGenerationTaskForSession: vi.fn(),

31+

MUSIC_GENERATION_TASK_KIND: "music_generation",

32+

}));

33+34+

vi.mock("../../image-generation-task-status.js", () => imageGenerationTaskStatusMocks);

35+

vi.mock("../../music-generation-task-status.js", () => musicGenerationTaskStatusMocks);

36+

vi.mock("../../video-generation-task-status.js", () => videoGenerationTaskStatusMocks);

37+38+

import {

39+

ensureSystemPromptCacheBoundary,

40+

SYSTEM_PROMPT_CACHE_BOUNDARY,

41+

splitSystemPromptCacheBoundary,

42+

} from "../../system-prompt-cache-boundary.js";

43+

import {

44+

appendModelIdentitySystemPrompt,

45+

buildModelIdentityPromptLine,

46+

} from "../../system-prompt.js";

47+

import {

48+

prependSystemPromptAddition,

49+

resolveAttemptMediaTaskSystemPromptAddition,

50+

} from "./attempt.prompt-helpers.js";

51+

import { composeSystemPromptWithHookContext } from "./attempt.thread-helpers.js";

52+53+

const MEDIA_HINT = "Active image generation task in progress";

54+

const HOOK = "Static plugin guidance"; // documented static-cacheable hook field, constant per turn

55+

const BASE = `Stable workspace prefix${SYSTEM_PROMPT_CACHE_BOUNDARY}Dynamic channel guidance`;

56+

const MODEL = "test-model-x"; // any non-empty model yields a "Current model identity:" line

57+

const MODEL_IDENTITY_FRAGMENT = "Current model identity:";

58+59+

// Mirror the production composition order at attempt.ts / cli-runner/prepare.ts:

60+

// 1) compose base with the static hook prepend/append (above-boundary, cacheable),

61+

// 2) route the per-turn media task hints below the cache boundary (when a task is active),

62+

// 3) before appending the model identity line, ensure a cache boundary exists (covers

63+

// marker-free hook systemPrompt overrides) so the identity lands below it, not in the

64+

// cached prefix.

65+

function composeTurn(opts: { activeImageTask: boolean; base?: string; hook?: string }): string {

66+

imageGenerationTaskStatusMocks.buildActiveImageGenerationTaskPromptContextForSession.mockReturnValue(

67+

opts.activeImageTask ? MEDIA_HINT : undefined,

68+

);

69+

videoGenerationTaskStatusMocks.buildActiveVideoGenerationTaskPromptContextForSession.mockReturnValue(

70+

undefined,

71+

);

72+

musicGenerationTaskStatusMocks.buildActiveMusicGenerationTaskPromptContextForSession.mockReturnValue(

73+

undefined,

74+

);

75+

const base = opts.base ?? BASE;

76+

const composed =

77+

composeSystemPromptWithHookContext({

78+

baseSystemPrompt: base,

79+

prependSystemContext: opts.hook ?? HOOK,

80+

}) ?? base;

81+

const mediaTaskSystemPromptAddition = resolveAttemptMediaTaskSystemPromptAddition({

82+

sessionKey: "agent:main:discord:direct:123",

83+

trigger: "user",

84+

});

85+

const routed = mediaTaskSystemPromptAddition

86+

? prependSystemPromptAddition({

87+

systemPrompt: ensureSystemPromptCacheBoundary(composed),

88+

systemPromptAddition: mediaTaskSystemPromptAddition,

89+

})

90+

: composed;

91+

// Production appends the model identity line after media routing; ensure the boundary first

92+

// (when an identity line will be added) so it lands below the boundary, not in the cached

93+

// prefix — the regression the marker-free idle case caught.

94+

const withIdentityBoundary =

95+

buildModelIdentityPromptLine(MODEL) && routed.trim().length > 0

96+

? ensureSystemPromptCacheBoundary(routed)

97+

: routed;

98+

return appendModelIdentitySystemPrompt({ systemPrompt: withIdentityBoundary, model: MODEL });

99+

}

100+101+

describe("#85203 media task hints stay below the system-prompt cache boundary", () => {

102+

it("cached stablePrefix is identical across a media-active turn and a media-idle turn", () => {

103+

const withMedia = splitSystemPromptCacheBoundary(composeTurn({ activeImageTask: true }));

104+

const withoutMedia = splitSystemPromptCacheBoundary(composeTurn({ activeImageTask: false }));

105+

expect(withMedia?.stablePrefix).toBe(withoutMedia?.stablePrefix);

106+

});

107+108+

it("documented static hook guidance stays in the cacheable prefix (use-case coverage)", () => {

109+

const split = splitSystemPromptCacheBoundary(composeTurn({ activeImageTask: true }));

110+

expect(split?.stablePrefix).toContain(HOOK);

111+

});

112+113+

it("media hint lands below the boundary (dynamic suffix), not in the cached prefix", () => {

114+

const split = splitSystemPromptCacheBoundary(composeTurn({ activeImageTask: true }));

115+

expect(split?.dynamicSuffix).toContain(MEDIA_HINT);

116+

expect(split?.stablePrefix ?? "").not.toContain(MEDIA_HINT);

117+

});

118+119+

// A hook that returns a full systemPrompt override produces a marker-free base; the

120+

// ensureSystemPromptCacheBoundary wrap inserts a boundary so media still routes below it.

121+

it("inserts a boundary for a marker-free hook systemPrompt override so media stays uncached", () => {

122+

const OVERRIDE = "Custom hook system prompt override without a cache boundary";

123+

const split = splitSystemPromptCacheBoundary(

124+

composeTurn({ activeImageTask: true, base: OVERRIDE, hook: "" }),

125+

);

126+

expect(split).toBeDefined();

127+

expect(split?.stablePrefix).toBe(OVERRIDE);

128+

expect(split?.stablePrefix ?? "").not.toContain(MEDIA_HINT);

129+

expect(split?.dynamicSuffix).toContain(MEDIA_HINT);

130+

});

131+132+

// Without ensuring the boundary on idle turns too, a marker-free override has the later

133+

// model-identity append land above the (absent) boundary, so the idle cached prefix

134+

// diverges from the active turn and prompt caching breaks across active/idle transitions.

135+

it("marker-free override: idle cached prefix matches the active turn after model identity is appended", () => {

136+

const OVERRIDE = "Custom hook system prompt override without a cache boundary";

137+

const active = splitSystemPromptCacheBoundary(

138+

composeTurn({ activeImageTask: true, base: OVERRIDE, hook: "" }),

139+

);

140+

const idle = splitSystemPromptCacheBoundary(

141+

composeTurn({ activeImageTask: false, base: OVERRIDE, hook: "" }),

142+

);

143+

expect(active?.stablePrefix).toBe(OVERRIDE);

144+

expect(idle).toBeDefined();

145+

expect(idle?.stablePrefix).toBe(active?.stablePrefix);

146+

expect(idle?.stablePrefix ?? "").not.toContain(MODEL_IDENTITY_FRAGMENT);

147+

expect(idle?.dynamicSuffix).toContain(MODEL_IDENTITY_FRAGMENT);

148+

});

149+

});