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

推荐订阅源

Martin Fowler
Martin Fowler
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
量子位
Jina AI
Jina AI
Microsoft Azure Blog
Microsoft Azure Blog
博客园_首页
L
LangChain Blog
A
About on SuperTechFans
人人都是产品经理
人人都是产品经理
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
美团技术团队
博客园 - 三生石上(FineUI控件)
N
Netflix TechBlog - Medium
D
DataBreaches.Net
P
Proofpoint News Feed
小众软件
小众软件
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
WordPress大学
WordPress大学
雷峰网
雷峰网
G
Google Developers 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
refactor(agents): share session truncation warnings · ope...
vincentkoc · 2026-06-23 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -14,7 +14,13 @@ import { ensureTool } from "../../utils/tools-manager.js";

1414

import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";

1515

import { appendBoundedTextTail, normalizePositiveLimit } from "./limits.js";

1616

import { resolveToCwd } from "./path-utils.js";

17-

import { formatSessionToolOutput, invalidArgText, shortenPath, str } from "./render-utils.js";

17+

import {

18+

appendSessionToolTruncationWarning,

19+

formatSessionToolOutput,

20+

invalidArgText,

21+

shortenPath,

22+

str,

23+

} from "./render-utils.js";

1824

import type { FindToolDetails } from "./tool-contracts.js";

1925

import { wrapToolDefinition } from "./tool-definition-wrapper.js";

2026

import { DEFAULT_MAX_BYTES, formatSize, truncateHead } from "./truncate.js";

@@ -91,21 +97,15 @@ function formatFindResult(

9197

theme: typeof import("../../modes/interactive/theme/theme.js").theme,

9298

showImages: boolean,

9399

): string {

94-

let text = formatSessionToolOutput(result, options, theme, showImages, 20);

95-
96100

const resultLimit = result.details?.resultLimitReached;

97-

const truncation = result.details?.truncation;

98-

if (resultLimit || truncation?.truncated) {

99-

const warnings: string[] = [];

100-

if (resultLimit) {

101-

warnings.push(`${resultLimit} results limit`);

102-

}

103-

if (truncation?.truncated) {

104-

warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);

105-

}

106-

text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;

107-

}

108-

return text;

101+

return appendSessionToolTruncationWarning(

102+

formatSessionToolOutput(result, options, theme, showImages, 20),

103+

theme,

104+

{

105+

limit: resultLimit ? { count: resultLimit, noun: "results" } : undefined,

106+

truncation: result.details?.truncation,

107+

},

108+

);

109109

}

110110
111111

function buildFindResult(params: {

Original file line numberDiff line numberDiff line change

@@ -14,7 +14,13 @@ import { ensureTool } from "../../utils/tools-manager.js";

1414

import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";

1515

import { appendBoundedTextTail, normalizePositiveLimit } from "./limits.js";

1616

import { resolveToCwd } from "./path-utils.js";

17-

import { formatSessionToolOutput, invalidArgText, shortenPath, str } from "./render-utils.js";

17+

import {

18+

appendSessionToolTruncationWarning,

19+

formatSessionToolOutput,

20+

invalidArgText,

21+

shortenPath,

22+

str,

23+

} from "./render-utils.js";

1824

import type { GrepToolDetails } from "./tool-contracts.js";

1925

import { wrapToolDefinition } from "./tool-definition-wrapper.js";

2026

import {

@@ -107,25 +113,17 @@ function formatGrepResult(

107113

theme: typeof import("../../modes/interactive/theme/theme.js").theme,

108114

showImages: boolean,

109115

): string {

110-

let text = formatSessionToolOutput(result, options, theme, showImages, 15);

111-
112116

const matchLimit = result.details?.matchLimitReached;

113-

const truncation = result.details?.truncation;

114117

const linesTruncated = result.details?.linesTruncated;

115-

if (matchLimit || truncation?.truncated || linesTruncated) {

116-

const warnings: string[] = [];

117-

if (matchLimit) {

118-

warnings.push(`${matchLimit} matches limit`);

119-

}

120-

if (truncation?.truncated) {

121-

warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);

122-

}

123-

if (linesTruncated) {

124-

warnings.push("some lines truncated");

125-

}

126-

text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;

127-

}

128-

return text;

118+

return appendSessionToolTruncationWarning(

119+

formatSessionToolOutput(result, options, theme, showImages, 15),

120+

theme,

121+

{

122+

limit: matchLimit ? { count: matchLimit, noun: "matches" } : undefined,

123+

truncation: result.details?.truncation,

124+

additionalWarnings: linesTruncated ? ["some lines truncated"] : undefined,

125+

},

126+

);

129127

}

130128
131129

export function createGrepToolDefinition(

Original file line numberDiff line numberDiff line change

@@ -12,7 +12,13 @@ import type { AgentTool } from "../../runtime/index.js";

1212

import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js";

1313

import { normalizePositiveLimit } from "./limits.js";

1414

import { resolveToCwd } from "./path-utils.js";

15-

import { formatSessionToolOutput, invalidArgText, shortenPath, str } from "./render-utils.js";

15+

import {

16+

appendSessionToolTruncationWarning,

17+

formatSessionToolOutput,

18+

invalidArgText,

19+

shortenPath,

20+

str,

21+

} from "./render-utils.js";

1622

import type { LsToolDetails } from "./tool-contracts.js";

1723

import { wrapToolDefinition } from "./tool-definition-wrapper.js";

1824

import { DEFAULT_MAX_BYTES, formatSize, truncateHead } from "./truncate.js";

@@ -79,21 +85,15 @@ function formatLsResult(

7985

theme: typeof import("../../modes/interactive/theme/theme.js").theme,

8086

showImages: boolean,

8187

): string {

82-

let text = formatSessionToolOutput(result, options, theme, showImages, 20);

83-
8488

const entryLimit = result.details?.entryLimitReached;

85-

const truncation = result.details?.truncation;

86-

if (entryLimit || truncation?.truncated) {

87-

const warnings: string[] = [];

88-

if (entryLimit) {

89-

warnings.push(`${entryLimit} entries limit`);

90-

}

91-

if (truncation?.truncated) {

92-

warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);

93-

}

94-

text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;

95-

}

96-

return text;

89+

return appendSessionToolTruncationWarning(

90+

formatSessionToolOutput(result, options, theme, showImages, 20),

91+

theme,

92+

{

93+

limit: entryLimit ? { count: entryLimit, noun: "entries" } : undefined,

94+

truncation: result.details?.truncation,

95+

},

96+

);

9797

}

9898
9999

export function createLsToolDefinition(

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,24 @@

1+

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

2+

import { appendSessionToolTruncationWarning } from "./render-utils.js";

3+
4+

const theme = {

5+

fg: (color: string, text: string) => `<${color}>${text}</${color}>`,

6+

};

7+
8+

describe("appendSessionToolTruncationWarning", () => {

9+

it("leaves output unchanged when no truncation metadata is present", () => {

10+

expect(appendSessionToolTruncationWarning("output", theme, {})).toBe("output");

11+

});

12+
13+

it("combines limit, byte, and additional warnings in order", () => {

14+

expect(

15+

appendSessionToolTruncationWarning("output", theme, {

16+

limit: { count: 5, noun: "matches" },

17+

truncation: { truncated: true, maxBytes: 1024 },

18+

additionalWarnings: ["some lines truncated"],

19+

}),

20+

).toBe(

21+

"output\n<warning>[Truncated: 5 matches limit, 1.0KB limit, some lines truncated]</warning>",

22+

);

23+

});

24+

});

Original file line numberDiff line numberDiff line change

@@ -10,6 +10,7 @@ import type { Theme } from "../../modes/interactive/theme/theme.js";

1010

import { sanitizeBinaryOutput } from "../../shell-utils.js";

1111

import { stripAnsi } from "../../utils/ansi.js";

1212

import type { ToolRenderResultOptions } from "../extensions/types.js";

13+

import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult } from "./truncate.js";

1314
1415

/** Shortens paths under the current home directory for display. */

1516

export function shortenPath(path: unknown): string {

@@ -105,6 +106,29 @@ export function formatSessionToolOutput(

105106

return text;

106107

}

107108
109+

export function appendSessionToolTruncationWarning(

110+

text: string,

111+

theme: Pick<Theme, "fg">,

112+

options: {

113+

limit?: { count: number; noun: string };

114+

truncation?: Pick<TruncationResult, "truncated" | "maxBytes">;

115+

additionalWarnings?: readonly string[];

116+

},

117+

): string {

118+

const warnings: string[] = [];

119+

if (options.limit) {

120+

warnings.push(`${options.limit.count} ${options.limit.noun} limit`);

121+

}

122+

if (options.truncation?.truncated) {

123+

warnings.push(`${formatSize(options.truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);

124+

}

125+

warnings.push(...(options.additionalWarnings ?? []));

126+

if (warnings.length === 0) {

127+

return text;

128+

}

129+

return `${text}\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;

130+

}

131+
108132

/** Formats the invalid-argument marker with the active theme. */

109133

export function invalidArgText(theme: Pick<Theme, "fg">): string {

110134

return theme.fg("error", "[invalid arg]");