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

推荐订阅源

大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
小众软件
小众软件
V
V2EX
博客园 - Franky
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
量子位
博客园 - 【当耐特】
雷峰网
雷峰网
WordPress大学
WordPress大学
Jina AI
Jina AI
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
阮一峰的网络日志
阮一峰的网络日志
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
V
Visual Studio Blog
Microsoft Security Blog
Microsoft Security Blog

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): keep runtime context out of visible transcr...
steipete · 2026-04-25 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai

1010
1111

### Changes

1212
13+

- WebChat/sessions: keep runtime-only prompt context out of visible transcript history and scrub legacy wrappers from session history surfaces. Thanks @91wan.

1314

- Gradium: add a bundled text-to-speech provider with voice-note and telephony output support. (#64958) Thanks @LaurentMazare.

1415

- Plugins/setup: honor explicit `setup.requiresRuntime: false` as a descriptor-only setup contract while keeping omitted values on the legacy setup-api fallback path. Thanks @vincentkoc.

1516

- Plugins/setup: report descriptor/runtime drift when setup-api registrations disagree with `setup.providers` or `setup.cliBackends`, without rejecting legacy setup plugins. Thanks @vincentkoc.

Original file line numberDiff line numberDiff line change

@@ -16,6 +16,7 @@ file is backed up alongside the session file.

1616
1717

Scope includes:

1818
19+

- Runtime-only prompt context staying out of user-visible transcript turns

1920

- Tool call id sanitization

2021

- Tool call input validation

2122

- Tool result pairing repair

@@ -30,6 +31,20 @@ If you need transcript storage details, see:

3031
3132

---

3233
34+

## Global rule: runtime context is not user transcript

35+
36+

Runtime/system context can be added to the model prompt for a turn, but it is

37+

not end-user-authored content. OpenClaw keeps a separate transcript-facing

38+

prompt body for Gateway replies, queued followups, ACP, CLI, and embedded Pi

39+

runs. Stored visible user turns use that transcript body instead of the

40+

runtime-enriched prompt.

41+
42+

For legacy sessions that already persisted runtime wrappers, Gateway history

43+

surfaces apply a display projection before returning messages to WebChat,

44+

TUI, REST, or SSE clients.

45+
46+

---

47+
3348

## Where this runs

3449
3550

All transcript hygiene is centralized in the embedded runner:

Original file line numberDiff line numberDiff line change

@@ -24,7 +24,8 @@ Status: the macOS/iOS SwiftUI chat UI talks directly to the Gateway WebSocket.

2424
2525

- The UI connects to the Gateway WebSocket and uses `chat.history`, `chat.send`, and `chat.inject`.

2626

- `chat.history` is bounded for stability: Gateway may truncate long text fields, omit heavy metadata, and replace oversized entries with `[chat.history omitted: message too large]`.

27-

- `chat.history` is also display-normalized: inline delivery directive tags

27+

- `chat.history` is also display-normalized: runtime-only OpenClaw context,

28+

inbound envelope wrappers, inline delivery directive tags

2829

such as `[[reply_to_*]]` and `[[audio_as_voice]]`, plain-text tool-call XML

2930

payloads (including `<tool_call>...</tool_call>`,

3031

`<function_call>...</function_call>`, `<tool_calls>...</tool_calls>`,

Original file line numberDiff line numberDiff line change

@@ -248,6 +248,7 @@ async function prepareAgentCommandExecution(

248248

throw new Error("Message (--message) is required");

249249

}

250250

const body = prependInternalEventContext(message, opts.internalEvents);

251+

const transcriptBody = opts.transcriptMessage ?? message;

251252

if (!opts.to && !opts.sessionId && !opts.sessionKey && !opts.agentId) {

252253

throw new Error("Pass --to <E.164>, --session-id, or --agent to choose a session");

253254

}

@@ -368,6 +369,7 @@ async function prepareAgentCommandExecution(

368369
369370

return {

370371

body,

372+

transcriptBody,

371373

cfg,

372374

normalizedSpawned,

373375

agentCfg,

@@ -402,6 +404,7 @@ async function agentCommandInternal(

402404

const prepared = await prepareAgentCommandExecution(opts, runtime);

403405

const {

404406

body,

407+

transcriptBody,

405408

cfg,

406409

normalizedSpawned,

407410

agentCfg,

@@ -523,6 +526,7 @@ async function agentCommandInternal(

523526

const { resolveAcpSessionCwd } = await loadAcpSessionIdentifiersRuntime();

524527

sessionEntry = await attemptExecutionRuntime.persistAcpTurnTranscript({

525528

body,

529+

transcriptBody,

526530

finalText: finalTextRaw,

527531

sessionId,

528532

sessionKey,

@@ -1068,6 +1072,7 @@ async function agentCommandInternal(

10681072

try {

10691073

sessionEntry = await attemptExecutionRuntime.persistCliTurnTranscript({

10701074

body,

1075+

transcriptBody,

10711076

result,

10721077

sessionId,

10731078

sessionKey: sessionKey ?? sessionId,

Original file line numberDiff line numberDiff line change

@@ -19,6 +19,7 @@ export type RunCliAgentParams = {

1919

workspaceDir: string;

2020

config?: OpenClawConfig;

2121

prompt: string;

22+

transcriptPrompt?: string;

2223

provider: string;

2324

model?: string;

2425

thinkLevel?: ThinkLevel;

Original file line numberDiff line numberDiff line change

@@ -386,6 +386,41 @@ describe("CLI attempt execution", () => {

386386

});

387387

});

388388
389+

it("persists the transcript body instead of runtime-only CLI prompt context", async () => {

390+

const sessionKey = "agent:main:subagent:cli-transcript-clean";

391+

const sessionEntry: SessionEntry = {

392+

sessionId: "session-cli-transcript-clean",

393+

updatedAt: Date.now(),

394+

};

395+

const sessionStore: Record<string, SessionEntry> = { [sessionKey]: sessionEntry };

396+

await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2), "utf-8");

397+
398+

const updatedEntry = await persistCliTurnTranscript({

399+

body: [

400+

"<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>",

401+

"secret runtime context",

402+

"<<<END_OPENCLAW_INTERNAL_CONTEXT>>>",

403+

"",

404+

"visible ask",

405+

].join("\n"),

406+

transcriptBody: "visible ask",

407+

result: makeCliResult("hello from cli"),

408+

sessionId: sessionEntry.sessionId,

409+

sessionKey,

410+

sessionEntry,

411+

sessionStore,

412+

storePath,

413+

sessionAgentId: "main",

414+

sessionCwd: tmpDir,

415+

});

416+
417+

const messages = await readSessionMessages(updatedEntry?.sessionFile ?? "");

418+

expect(messages[0]).toMatchObject({

419+

role: "user",

420+

content: "visible ask",

421+

});

422+

});

423+
389424

it("forwards user trigger and channel context to CLI runs", async () => {

390425

const sessionKey = "agent:main:direct:claude-channel-context";

391426

const sessionEntry: SessionEntry = {

Original file line numberDiff line numberDiff line change

@@ -64,6 +64,7 @@ type TranscriptUsage = {

6464
6565

type PersistTextTurnTranscriptParams = {

6666

body: string;

67+

transcriptBody?: string;

6768

finalText: string;

6869

sessionId: string;

6970

sessionKey: string;

@@ -97,7 +98,7 @@ function resolveTranscriptUsage(usage: PersistTextTurnTranscriptParams["assistan

9798

async function persistTextTurnTranscript(

9899

params: PersistTextTurnTranscriptParams,

99100

): Promise<SessionEntry | undefined> {

100-

const promptText = params.body;

101+

const promptText = params.transcriptBody ?? params.body;

101102

const replyText = params.finalText;

102103

if (!promptText && !replyText) {

103104

return params.sessionEntry;

@@ -169,6 +170,7 @@ function isClaudeCliProvider(provider: string): boolean {

169170
170171

export async function persistAcpTurnTranscript(params: {

171172

body: string;

173+

transcriptBody?: string;

172174

finalText: string;

173175

sessionId: string;

174176

sessionKey: string;

@@ -191,6 +193,7 @@ export async function persistAcpTurnTranscript(params: {

191193
192194

export async function persistCliTurnTranscript(params: {

193195

body: string;

196+

transcriptBody?: string;

194197

result: EmbeddedPiRunResult;

195198

sessionId: string;

196199

sessionKey: string;

@@ -207,6 +210,7 @@ export async function persistCliTurnTranscript(params: {

207210
208211

return await persistTextTurnTranscript({

209212

body: params.body,

213+

transcriptBody: params.transcriptBody,

210214

finalText: replyText,

211215

sessionId: params.sessionId,

212216

sessionKey: params.sessionKey,

Original file line numberDiff line numberDiff line change

@@ -27,6 +27,8 @@ export type AgentRunContext = {

2727
2828

export type AgentCommandOpts = {

2929

message: string;

30+

/** User-visible transcript body; defaults to message and excludes runtime-only context. */

31+

transcriptMessage?: string;

3032

/** Optional image attachments for multimodal messages. */

3133

images?: ImageContent[];

3234

/** Original inline/offloaded attachment order for inbound images. */

Original file line numberDiff line numberDiff line change

@@ -866,6 +866,7 @@ export async function runEmbeddedPiAgent(

866866

contextTokenBudget: ctxInfo.tokens,

867867

skillsSnapshot: params.skillsSnapshot,

868868

prompt,

869+

transcriptPrompt: params.transcriptPrompt,

869870

images: params.images,

870871

imageOrder: params.imageOrder,

871872

clientTools: params.clientTools,

Original file line numberDiff line numberDiff line change

@@ -288,6 +288,7 @@ import {

288288

PREEMPTIVE_OVERFLOW_ERROR_TEXT,

289289

shouldPreemptivelyCompactBeforePrompt,

290290

} from "./preemptive-compaction.js";

291+

import { rewriteSubmittedPromptTranscript } from "./transcript-prompt-rewrite.js";

291292

import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";

292293
293294

export {

@@ -2424,6 +2425,13 @@ export async function runEmbeddedAttempt(

24242425

} else {

24252426

await abortable(activeSession.prompt(effectivePrompt));

24262427

}

2428+

rewriteSubmittedPromptTranscript({

2429+

sessionManager,

2430+

sessionFile: params.sessionFile,

2431+

previousLeafId: transcriptLeafId,

2432+

submittedPrompt: effectivePrompt,

2433+

transcriptPrompt: params.transcriptPrompt,

2434+

});

24272435

}

24282436

} catch (err) {

24292437

yieldAborted =