mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 19:35:28 -06:00
refactor(config): retire redundant settings (#113174)
* refactor(config): retire redundant settings * style: apply current formatter * chore: update plugin sdk baseline * fix: keep Codex tool caps context-aware * chore: remove stale imports * test: align WhatsApp QA debounce config * fix(config): clean up retired config checks * fix(ci): align config cleanup checks
This commit is contained in:
committed by
GitHub
parent
1603781bb0
commit
bb657eec93
@@ -1,37 +0,0 @@
|
||||
import type { EmbeddedRunAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
||||
import { asOptionalRecord as readRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
|
||||
/** Resolves an agent override before falling back to the configured default. */
|
||||
export function resolveAgentContextLimitValue(params: {
|
||||
config: EmbeddedRunAttemptParams["config"] | undefined;
|
||||
agentId?: string;
|
||||
key: string;
|
||||
}): number | undefined {
|
||||
const agents = readRecord(params.config?.agents);
|
||||
const defaults = readRecord(readRecord(agents?.defaults)?.contextLimits);
|
||||
const defaultValue = readPositiveInteger(defaults?.[params.key]);
|
||||
if (!params.agentId) {
|
||||
return defaultValue;
|
||||
}
|
||||
const list = agents?.list;
|
||||
if (!Array.isArray(list)) {
|
||||
return defaultValue;
|
||||
}
|
||||
const normalizedAgentId = normalizeAgentId(params.agentId);
|
||||
const agent = list.find((entry) => {
|
||||
const entryId = readRecord(entry)?.id;
|
||||
return typeof entryId === "string" && normalizeAgentId(entryId) === normalizedAgentId;
|
||||
});
|
||||
const agentValue = readPositiveInteger(
|
||||
readRecord(readRecord(agent)?.contextLimits)?.[params.key],
|
||||
);
|
||||
return agentValue ?? defaultValue;
|
||||
}
|
||||
|
||||
function readPositiveInteger(value: unknown): number | undefined {
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
@@ -802,8 +802,8 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
expectNoNamespace(bridge.specs[1]);
|
||||
});
|
||||
|
||||
it("truncates configured text tool results before returning them to Codex", async () => {
|
||||
const longText = "x".repeat(400);
|
||||
it("scales oversized text tool results to the effective context window", async () => {
|
||||
const longText = "x".repeat(40_000);
|
||||
const bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({
|
||||
@@ -812,18 +812,7 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: {
|
||||
agentId: "main",
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
contextLimits: {
|
||||
toolResultMaxChars: 180,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
},
|
||||
hookContext: { contextWindowTokens: 128_000 },
|
||||
});
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
@@ -841,15 +830,43 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
throw new Error("expected inputText tool result");
|
||||
}
|
||||
const text = firstItem.text;
|
||||
expect(text.length).toBeLessThanOrEqual(180);
|
||||
expect(text.length).toBeLessThanOrEqual(32_000);
|
||||
expect(text).toContain("OpenClaw truncated dynamic tool result");
|
||||
expect(text).toContain("original 400 chars");
|
||||
expect(text).toContain("original 40000 chars");
|
||||
expect(text).toContain("rerun with narrower args");
|
||||
});
|
||||
|
||||
it("keeps a whole code point when dynamic tool text crosses the configured boundary", async () => {
|
||||
const maxChars = 180;
|
||||
const totalChars = 400;
|
||||
it("applies the context-share ceiling for small effective windows", async () => {
|
||||
const bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({
|
||||
name: "small_context_lookup",
|
||||
execute: vi.fn(async () => textToolResult("x".repeat(20_000))),
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: { contextWindowTokens: 8_000 },
|
||||
});
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
callId: "call-small-context",
|
||||
namespace: null,
|
||||
tool: "small_context_lookup",
|
||||
arguments: {},
|
||||
});
|
||||
const firstItem = result.contentItems[0];
|
||||
if (firstItem?.type !== "inputText" || typeof firstItem.text !== "string") {
|
||||
throw new Error("expected inputText tool result");
|
||||
}
|
||||
expect(firstItem.text.length).toBeLessThanOrEqual(9_600);
|
||||
expect(firstItem.text).toContain("OpenClaw truncated dynamic tool result");
|
||||
});
|
||||
|
||||
it("keeps a whole code point when dynamic tool text crosses the automatic boundary", async () => {
|
||||
const maxChars = 16_000;
|
||||
const totalChars = 20_000;
|
||||
const noticeText = `...(OpenClaw truncated dynamic tool result: original ${totalChars} chars, showing ${maxChars}; rerun with narrower args.)`;
|
||||
const textBudget = maxChars - noticeText.length - 1;
|
||||
const prefix = "a".repeat(textBudget - 1);
|
||||
@@ -862,12 +879,6 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: {
|
||||
agentId: "main",
|
||||
config: {
|
||||
agents: { defaults: { contextLimits: { toolResultMaxChars: maxChars } } },
|
||||
} as never,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
@@ -882,123 +893,21 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
expect(result.contentItems).toEqual([{ type: "inputText", text: `${prefix}\n${noticeText}` }]);
|
||||
});
|
||||
|
||||
it("honors normalized per-agent dynamic tool result caps", async () => {
|
||||
const bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({
|
||||
name: "large_lookup",
|
||||
execute: vi.fn(async () => textToolResult("x".repeat(400))),
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: {
|
||||
agentId: "research-bot",
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
contextLimits: {
|
||||
toolResultMaxChars: 1_000,
|
||||
},
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "Research Bot",
|
||||
contextLimits: {
|
||||
toolResultMaxChars: 180,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as never,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
callId: "call-1",
|
||||
namespace: null,
|
||||
tool: "large_lookup",
|
||||
arguments: {},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
const firstItem = result.contentItems[0];
|
||||
if (firstItem?.type !== "inputText" || typeof firstItem.text !== "string") {
|
||||
throw new Error("expected inputText tool result");
|
||||
}
|
||||
expect(firstItem.text.length).toBeLessThanOrEqual(180);
|
||||
expect(firstItem.text).toContain("OpenClaw truncated dynamic tool result");
|
||||
});
|
||||
|
||||
it("keeps truncation notices within tiny configured caps", async () => {
|
||||
const bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({
|
||||
name: "large_lookup",
|
||||
execute: vi.fn(async () => textToolResult("x".repeat(400))),
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: {
|
||||
agentId: "main",
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
contextLimits: {
|
||||
toolResultMaxChars: 32,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
callId: "call-1",
|
||||
namespace: null,
|
||||
tool: "large_lookup",
|
||||
arguments: {},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
const firstItem = result.contentItems[0];
|
||||
if (firstItem?.type !== "inputText" || typeof firstItem.text !== "string") {
|
||||
throw new Error("expected inputText tool result");
|
||||
}
|
||||
expect(firstItem.text.length).toBeLessThanOrEqual(32);
|
||||
expect(firstItem.text).toBe("...(OpenClaw truncated dynamic tool".slice(0, 32));
|
||||
});
|
||||
|
||||
it("budgets configured truncation across all text result blocks", async () => {
|
||||
it("budgets automatic truncation across all text result blocks", async () => {
|
||||
const bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({
|
||||
name: "large_lookup",
|
||||
execute: vi.fn(async () => ({
|
||||
content: [
|
||||
{ type: "text" as const, text: "a".repeat(200) },
|
||||
{ type: "text" as const, text: "b".repeat(200) },
|
||||
{ type: "text" as const, text: "a".repeat(10_000) },
|
||||
{ type: "text" as const, text: "b".repeat(10_000) },
|
||||
],
|
||||
details: {},
|
||||
})),
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: {
|
||||
agentId: "main",
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
contextLimits: {
|
||||
toolResultMaxChars: 180,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
@@ -1014,10 +923,10 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
const text = result.contentItems
|
||||
.map((item) => (item.type === "inputText" && typeof item.text === "string" ? item.text : ""))
|
||||
.join("");
|
||||
expect(text.length).toBeLessThanOrEqual(180);
|
||||
expect(text.length).toBeLessThanOrEqual(16_000);
|
||||
expect(text).toContain("OpenClaw truncated dynamic tool result");
|
||||
expect(text).toContain("original 400 chars");
|
||||
expect(text).not.toContain("b".repeat(100));
|
||||
expect(text).toContain("original 20000 chars");
|
||||
expect(text).not.toContain("b".repeat(10_000));
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -47,7 +47,6 @@ import type { ImageContent, TextContent } from "openclaw/plugin-sdk/llm";
|
||||
import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||
import { resolveAgentContextLimitValue } from "./agent-context-limits.js";
|
||||
import type { CodexDynamicToolsLoading } from "./config.js";
|
||||
import {
|
||||
createFailedDynamicToolResponse,
|
||||
@@ -78,6 +77,7 @@ type CodexDynamicToolHookContext = {
|
||||
runId?: string;
|
||||
channelId?: string;
|
||||
currentChannelProvider?: string;
|
||||
contextWindowTokens?: number;
|
||||
currentChannelId?: string;
|
||||
currentMessagingTarget?: string;
|
||||
currentMessageId?: string | number;
|
||||
@@ -386,6 +386,28 @@ const EXPLICIT_MESSAGE_TARGET_KEYS = ["target", "to", "channelId"];
|
||||
const EXPLICIT_MESSAGE_THREAD_KEYS = ["threadId", "thread_id", "messageThreadId", "topicId"];
|
||||
const EXPLICIT_MESSAGE_REPLY_KEYS = ["replyTo", "replyToId", "replyToIdFull"];
|
||||
const DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 16_000;
|
||||
const LARGE_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 32_000;
|
||||
const XL_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 64_000;
|
||||
|
||||
function resolveCodexDynamicToolResultMaxChars(contextWindowTokens?: number): number {
|
||||
if (
|
||||
typeof contextWindowTokens !== "number" ||
|
||||
!Number.isFinite(contextWindowTokens) ||
|
||||
contextWindowTokens <= 0
|
||||
) {
|
||||
return DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
|
||||
}
|
||||
const tokens = Math.floor(contextWindowTokens);
|
||||
const autoCap =
|
||||
tokens >= 200_000
|
||||
? XL_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS
|
||||
: tokens >= 100_000
|
||||
? LARGE_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS
|
||||
: DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
|
||||
// Match the core live-result context-share ceiling without importing core
|
||||
// internals across the bundled-plugin boundary.
|
||||
return Math.min(autoCap, Math.max(1, Math.floor(tokens * 0.3) * 4));
|
||||
}
|
||||
|
||||
function computerFrameImageIdentity(
|
||||
content: AgentToolResult<unknown>["content"] | undefined,
|
||||
@@ -431,7 +453,9 @@ export function createCodexDynamicToolBridge(params: {
|
||||
directToolNames?: Iterable<string>;
|
||||
}): CodexDynamicToolBridge {
|
||||
const toolResultHookContext = toToolResultHookContext(params.hookContext);
|
||||
const toolResultMaxChars = resolveCodexDynamicToolResultMaxChars(params.hookContext);
|
||||
const toolResultMaxChars = resolveCodexDynamicToolResultMaxChars(
|
||||
params.hookContext?.contextWindowTokens,
|
||||
);
|
||||
const availableProjection = projectCodexDynamicTools(params.tools);
|
||||
const registeredProjection = params.registeredTools
|
||||
? projectCodexDynamicTools(params.registeredTools)
|
||||
@@ -1165,16 +1189,6 @@ function toToolResultHookContext(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveCodexDynamicToolResultMaxChars(
|
||||
ctx: CodexDynamicToolHookContext | undefined,
|
||||
): number {
|
||||
const configured = resolveAgentContextLimitValue({
|
||||
config: ctx?.config,
|
||||
agentId: ctx?.agentId,
|
||||
key: "toolResultMaxChars",
|
||||
});
|
||||
return configured ?? DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
|
||||
}
|
||||
function composeAbortSignals(...signals: Array<AbortSignal | undefined>): AbortSignal {
|
||||
const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
|
||||
if (activeSignals.length === 0) {
|
||||
|
||||
@@ -229,6 +229,7 @@ export async function prepareCodexAttemptTools(runtime: CodexAttemptRuntime) {
|
||||
hookContext: {
|
||||
agentId: sessionAgentId,
|
||||
config: params.config,
|
||||
contextWindowTokens: params.contextTokenBudget ?? params.model.contextWindow,
|
||||
workspaceDir: effectiveWorkspace,
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: sandboxSessionKey,
|
||||
|
||||
@@ -1042,6 +1042,7 @@ async function createCodexSideToolBridge(input: {
|
||||
hookContext: {
|
||||
agentId: input.sessionAgentId,
|
||||
config: input.params.cfg,
|
||||
contextWindowTokens: runtimeModel.contextWindow,
|
||||
sessionId: input.params.sessionId,
|
||||
sessionKey: input.params.sessionKey,
|
||||
runId: input.runId,
|
||||
|
||||
Reference in New Issue
Block a user