Skip to content

Commit 77d9ac3

Browse files
authored
refactor: reuse shared coercion helpers (#86419)
* refactor: share talk event metric extraction * refactor: reuse shared coercion helpers * refactor: reuse shared primitive guards * refactor: reuse shared record guard * refactor: reuse shared primitive helpers * refactor: reuse shared string guards * refactor: reuse shared non-empty string guard * refactor: share plugin primitive coercion helpers * refactor: reuse plugin coercion helpers * refactor: reuse plugin coercion helpers in more plugins * refactor: reuse channel coercion helpers * refactor: reuse monitor coercion helpers * refactor: reuse provider coercion helpers * refactor: reuse core coercion helpers * refactor: reuse runtime coercion helpers * refactor: reuse helper coercion in codex paths * refactor: reuse helper coercion in runtime paths * refactor: reuse codex app-server coercion helpers * refactor: reuse codex record helpers * refactor: reuse migration and qa record helpers * refactor: reuse feishu and core helper guards * refactor: reuse browser and policy coercion helpers * refactor: reuse memory wiki record helper * refactor: share boolean coercion helpers * refactor: reuse finite number coercion * refactor: reuse trimmed string list helpers * refactor: reuse string list normalization * refactor: reuse remaining string list helpers * refactor: reuse string entry normalizer * refactor: share sorted string helpers * refactor: share string list normalization * test: preserve command registry browser imports * refactor: reuse trimmed list helpers * refactor: reuse string dedupe helpers * refactor: reuse local dedupe helpers * refactor: reuse more string dedupe helpers * refactor: reuse command string dedupe helpers * refactor: dedupe memory path lists with helper * refactor: expose string dedupe helpers to plugins * refactor: reuse core string dedupe helpers * refactor: reuse shared unique value helpers * refactor: reuse unique helpers in agent utilities * refactor: reuse unique helpers in config plumbing * refactor: reuse unique helpers in extensions * refactor: reuse unique helpers in core utilities * refactor: reuse unique helpers in qa plugins * refactor: reuse unique helpers in memory plugins * refactor: reuse unique helpers in channel plugins * refactor: reuse unique helpers in core tails * refactor: reuse unique helper in comfy workflow * refactor: reuse unique helpers in test utilities * refactor: expose unique value helper to plugins * refactor: reuse unique helpers for numeric lists * refactor: replace index dedupe filters * refactor: reuse string entry normalization * refactor: reuse string normalization in plugin helpers * refactor: reuse string normalization in extension helpers * refactor: reuse string normalization in channel parsers * refactor: reuse string normalization in memory search * refactor: reuse string normalization in provider parsers * refactor: reuse string normalization in qa helpers * refactor: reuse string normalization in infra parsers * refactor: reuse string normalization in messaging parsers * refactor: reuse string normalization in core parsers * refactor: reuse string normalization in extension parsers * refactor: reuse string normalization in remaining parsers * refactor: reuse string normalization in final parser spots * refactor: reuse string normalization in qa media helpers * refactor: reuse normalization in provider and media lists * refactor: reuse normalization for remaining set filters * refactor: reuse normalization in policy allowlists * refactor: reuse normalization in session and owner lists * refactor: centralize primitive string lists * refactor: reuse lowercase entry helpers * refactor: reuse sorted string helpers * refactor: reuse unique trimmed helpers * refactor: reuse string normalization helpers * refactor: reuse catalog string helpers * refactor: reuse remaining string helpers * refactor: simplify remaining list normalization * refactor: reuse codex auth order normalization * chore: refresh plugin sdk api baseline * fix: make shared string sorting deterministic * chore: refresh plugin sdk api baseline * fix: align host env security ordering
1 parent a98660e commit 77d9ac3

730 files changed

Lines changed: 2576 additions & 3788 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ Skills own workflows; root owns hard policy and routing.
154154
- Inline simple one-use objects/spreads when clearer. Extract only when it removes duplication or hard logic.
155155
- Tests prove behavior/regressions, not every internal branch.
156156
- For non-trivial refactors, check `git diff --numstat` before closeout. If LOC grew, trim or explain why.
157+
- Prefer existing narrow helpers over repeated casts/guards. Add local helpers when 2+ nearby call sites share real boundary logic.
158+
- Prefer ctor parameter properties for injected deps/config. Do not ban them for erasable-syntax purity.
159+
- Prefer `satisfies` for registries/config maps; derive types from schemas when a runtime schema already exists.
160+
- Table-drive repetitive tests when it reduces code and keeps failure names clear.
157161
- Dynamic import: no static+dynamic import for same prod module. Use `*.runtime.ts` lazy boundary. After edits: `pnpm build`; check `[INEFFECTIVE_DYNAMIC_IMPORT]`.
158162
- Cycles: keep `pnpm check:import-cycles` + architecture/madge green.
159163
- Classes: no prototype mixins/mutations. Prefer inheritance/composition. Tests prefer per-instance stubs.

apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import Foundation
66

77
enum HostEnvSecurityPolicy {
88
static let blockedInheritedKeys: Set<String> = [
9-
"_JAVA_OPTIONS",
109
"AMQP_URL",
1110
"ANSIBLE_CALLBACK_PLUGINS",
1211
"ANSIBLE_COLLECTIONS_PATH",
@@ -31,12 +30,11 @@ enum HostEnvSecurityPolicy {
3130
"AZURE_CLIENT_SECRET",
3231
"BASH_ENV",
3332
"BROWSER",
34-
"BUN_CONFIG_REGISTRY",
3533
"BUNDLE_GEMFILE",
34+
"BUN_CONFIG_REGISTRY",
3635
"BZR_EDITOR",
3736
"BZR_PLUGIN_PATH",
3837
"BZR_SSH",
39-
"C_INCLUDE_PATH",
4038
"CARGO_BUILD_RUSTC",
4139
"CARGO_BUILD_RUSTC_WRAPPER",
4240
"CARGO_HOME",
@@ -46,8 +44,8 @@ enum HostEnvSecurityPolicy {
4644
"CGO_CFLAGS",
4745
"CGO_LDFLAGS",
4846
"CLASSPATH",
49-
"CMAKE_C_COMPILER",
5047
"CMAKE_CXX_COMPILER",
48+
"CMAKE_C_COMPILER",
5149
"CMAKE_TOOLCHAIN_FILE",
5250
"COMPOSER_HOME",
5351
"CONFIG_SHELL",
@@ -58,6 +56,7 @@ enum HostEnvSecurityPolicy {
5856
"CPLUS_INCLUDE_PATH",
5957
"CURL_HOME",
6058
"CXX",
59+
"C_INCLUDE_PATH",
6160
"DATABASE_URL",
6261
"DENO_DIR",
6362
"DOTNET_ADDITIONAL_DEPS",
@@ -75,6 +74,8 @@ enum HostEnvSecurityPolicy {
7574
"GEM_HOME",
7675
"GEM_PATH",
7776
"GH_TOKEN",
77+
"GITHUB_TOKEN",
78+
"GITLAB_TOKEN",
7879
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
7980
"GIT_ASKPASS",
8081
"GIT_COMMON_DIR",
@@ -95,8 +96,6 @@ enum HostEnvSecurityPolicy {
9596
"GIT_SSL_NO_VERIFY",
9697
"GIT_TEMPLATE_DIR",
9798
"GIT_WORK_TREE",
98-
"GITHUB_TOKEN",
99-
"GITLAB_TOKEN",
10099
"GLIBC_TUNABLES",
101100
"GOENV",
102101
"GOFLAGS",
@@ -145,8 +144,8 @@ enum HostEnvSecurityPolicy {
145144
"PERL5DBCMD",
146145
"PERL5LIB",
147146
"PERL5OPT",
148-
"PHP_INI_SCAN_DIR",
149147
"PHPRC",
148+
"PHP_INI_SCAN_DIR",
150149
"PIP_CONFIG_FILE",
151150
"PIP_EXTRA_INDEX_URL",
152151
"PIP_FIND_LINKS",
@@ -160,17 +159,17 @@ enum HostEnvSecurityPolicy {
160159
"PYTHONPATH",
161160
"PYTHONSTARTUP",
162161
"PYTHONUSERBASE",
163-
"R_ENVIRON",
164-
"R_ENVIRON_USER",
165-
"R_LIBS_USER",
166-
"R_PROFILE",
167-
"R_PROFILE_USER",
168162
"REDIS_URL",
169163
"RUBYLIB",
170164
"RUBYOPT",
171165
"RUBYSHELL",
172166
"RUSTC_WRAPPER",
173167
"RUSTFLAGS",
168+
"R_ENVIRON",
169+
"R_ENVIRON_USER",
170+
"R_LIBS_USER",
171+
"R_PROFILE",
172+
"R_PROFILE_USER",
174173
"SBT_OPTS",
175174
"SHELL",
176175
"SHELLOPTS",
@@ -192,7 +191,8 @@ enum HostEnvSecurityPolicy {
192191
"VIRTUAL_ENV",
193192
"VISUAL",
194193
"WGETRC",
195-
"YARN_RC_FILENAME"
194+
"YARN_RC_FILENAME",
195+
"_JAVA_OPTIONS"
196196
]
197197

198198
static let blockedInheritedPrefixes: [String] = [
@@ -202,7 +202,6 @@ enum HostEnvSecurityPolicy {
202202
]
203203

204204
static let blockedKeys: Set<String> = [
205-
"_JAVA_OPTIONS",
206205
"ANT_OPTS",
207206
"BASH_ENV",
208207
"BROWSER",
@@ -213,8 +212,8 @@ enum HostEnvSecurityPolicy {
213212
"CARGO_BUILD_RUSTC_WRAPPER",
214213
"CATALINA_OPTS",
215214
"CC",
216-
"CMAKE_C_COMPILER",
217215
"CMAKE_CXX_COMPILER",
216+
"CMAKE_C_COMPILER",
218217
"CMAKE_TOOLCHAIN_FILE",
219218
"CONFIG_SHELL",
220219
"CONFIG_SITE",
@@ -275,14 +274,14 @@ enum HostEnvSecurityPolicy {
275274
"PYTHONBREAKPOINT",
276275
"PYTHONHOME",
277276
"PYTHONPATH",
278-
"R_ENVIRON",
279-
"R_ENVIRON_USER",
280-
"R_PROFILE",
281-
"R_PROFILE_USER",
282277
"RUBYLIB",
283278
"RUBYOPT",
284279
"RUBYSHELL",
285280
"RUSTC_WRAPPER",
281+
"R_ENVIRON",
282+
"R_ENVIRON_USER",
283+
"R_PROFILE",
284+
"R_PROFILE_USER",
286285
"SBT_OPTS",
287286
"SHELL",
288287
"SHELLOPTS",
@@ -291,7 +290,8 @@ enum HostEnvSecurityPolicy {
291290
"SVN_EDITOR",
292291
"SVN_SSH",
293292
"VAGRANT_VAGRANTFILE",
294-
"VIMINIT"
293+
"VIMINIT",
294+
"_JAVA_OPTIONS"
295295
]
296296

297297
static let blockedOverrideKeys: Set<String> = [
@@ -321,9 +321,8 @@ enum HostEnvSecurityPolicy {
321321
"AZURE_AUTH_LOCATION",
322322
"AZURE_CLIENT_ID",
323323
"AZURE_CLIENT_SECRET",
324-
"BUN_CONFIG_REGISTRY",
325324
"BUNDLE_GEMFILE",
326-
"C_INCLUDE_PATH",
325+
"BUN_CONFIG_REGISTRY",
327326
"CARGO_BUILD_RUSTC_WRAPPER",
328327
"CARGO_HOME",
329328
"CFLAGS",
@@ -336,6 +335,7 @@ enum HostEnvSecurityPolicy {
336335
"CPLUS_INCLUDE_PATH",
337336
"CURL_CA_BUNDLE",
338337
"CURL_HOME",
338+
"C_INCLUDE_PATH",
339339
"DATABASE_URL",
340340
"DENO_DIR",
341341
"DOCKER_CERT_PATH",
@@ -347,6 +347,8 @@ enum HostEnvSecurityPolicy {
347347
"GEM_HOME",
348348
"GEM_PATH",
349349
"GH_TOKEN",
350+
"GITHUB_TOKEN",
351+
"GITLAB_TOKEN",
350352
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
351353
"GIT_ASKPASS",
352354
"GIT_COMMON_DIR",
@@ -362,8 +364,6 @@ enum HostEnvSecurityPolicy {
362364
"GIT_SSL_CAPATH",
363365
"GIT_SSL_NO_VERIFY",
364366
"GIT_WORK_TREE",
365-
"GITHUB_TOKEN",
366-
"GITLAB_TOKEN",
367367
"GOENV",
368368
"GOFLAGS",
369369
"GONOPROXY",
@@ -378,8 +378,8 @@ enum HostEnvSecurityPolicy {
378378
"HGRCPATH",
379379
"HISTFILE",
380380
"HOME",
381-
"HTTP_PROXY",
382381
"HTTPS_PROXY",
382+
"HTTP_PROXY",
383383
"KUBECONFIG",
384384
"LDFLAGS",
385385
"LESSCLOSE",
@@ -391,19 +391,19 @@ enum HostEnvSecurityPolicy {
391391
"MANPAGER",
392392
"MFLAGS",
393393
"MONGODB_URI",
394-
"NO_PROXY",
395394
"NODE_AUTH_TOKEN",
396395
"NODE_EXTRA_CA_CERTS",
397396
"NODE_TLS_REJECT_UNAUTHORIZED",
397+
"NO_PROXY",
398398
"NPM_TOKEN",
399399
"OBJC_INCLUDE_PATH",
400400
"OPENSSL_CONF",
401401
"OPENSSL_ENGINES",
402402
"PAGER",
403403
"PERL5DB",
404404
"PERL5DBCMD",
405-
"PHP_INI_SCAN_DIR",
406405
"PHPRC",
406+
"PHP_INI_SCAN_DIR",
407407
"PIP_CONFIG_FILE",
408408
"PIP_EXTRA_INDEX_URL",
409409
"PIP_FIND_LINKS",
@@ -413,11 +413,11 @@ enum HostEnvSecurityPolicy {
413413
"PROMPT_COMMAND",
414414
"PYTHONSTARTUP",
415415
"PYTHONUSERBASE",
416-
"R_LIBS_USER",
417416
"REDIS_URL",
418417
"REQUESTS_CA_BUNDLE",
419418
"RUSTC_WRAPPER",
420419
"RUSTFLAGS",
420+
"R_LIBS_USER",
421421
"SSH_ASKPASS",
422422
"SSH_AUTH_SOCK",
423423
"SSL_CERT_DIR",
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
1d3e6177eeac57fc43736f7d5f76d8f825e1859ca625d268e97dc30b5567ea34 plugin-sdk-api-baseline.json
2-
6c093ff7c10bd81ee9d2c4fc5d07b206bc3a1f5acd0bad491cfc9e0df6689f6b plugin-sdk-api-baseline.jsonl
1+
374f1fec7d6fa8c00865dcb58b68d89ec10e85e81ef536c5746167a83d10bcc7 plugin-sdk-api-baseline.json
2+
ffc6a2faf381d1bb118845e010b2798397c3d41fff400f52ee57b6dc197c8af3 plugin-sdk-api-baseline.jsonl

extensions/acpx/src/runtime.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import {
1919
type AcpRuntimeTurnResult,
2020
} from "acpx/runtime";
2121
import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
22+
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
2223
import { AcpRuntimeError, type AcpRuntime, type AcpRuntimeErrorCode } from "../runtime-api.js";
24+
import { splitCommandParts } from "./command-line.js";
2325
import {
2426
createAcpxProcessLeaseId,
2527
hashAcpxProcessCommand,
@@ -32,7 +34,6 @@ import {
3234
isOpenClawLeaseAwareAcpxProcessCommand,
3335
type AcpxProcessCleanupDeps,
3436
} from "./process-reaper.js";
35-
import { splitCommandParts } from "./command-line.js";
3637

3738
type AcpSessionStore = AcpRuntimeOptions["sessionStore"];
3839
type AcpSessionRecord = Parameters<AcpSessionStore["save"]>[0];
@@ -189,7 +190,7 @@ function selectCurrentSessionLease(params: {
189190
sessionKeys: string[];
190191
rootPid?: number;
191192
}): AcpxProcessLease | undefined {
192-
const sessionKeys = new Set(params.sessionKeys.map((entry) => entry.trim()).filter(Boolean));
193+
const sessionKeys = new Set(normalizeStringEntries(params.sessionKeys));
193194
const candidates = params.leases.filter((lease) => sessionKeys.has(lease.sessionKey));
194195
if (params.rootPid) {
195196
return candidates.find((lease) => lease.rootPid === params.rootPid);

extensions/active-memory/index.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ import {
2020
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
2121
import { parseAgentSessionKey, parseThreadSessionSuffix } from "openclaw/plugin-sdk/routing";
2222
import { isPathInside, replaceFileAtomic } from "openclaw/plugin-sdk/security-runtime";
23+
import {
24+
asOptionalRecord as asRecord,
25+
normalizeOptionalString,
26+
normalizeStringEntries,
27+
uniqueStrings,
28+
} from "openclaw/plugin-sdk/string-coerce-runtime";
2329
import { tempWorkspace, resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
2430

2531
const DEFAULT_TIMEOUT_MS = 15_000;
@@ -313,11 +319,6 @@ function withToggleStoreLock<T>(statePath: string, task: () => Promise<T>): Prom
313319
return withLock(task);
314320
}
315321

316-
function asRecord(value: unknown): Record<string, unknown> | undefined {
317-
return value && typeof value === "object" && !Array.isArray(value)
318-
? (value as Record<string, unknown>)
319-
: undefined;
320-
}
321322
type ActiveMemoryThinkingLevel =
322323
| "off"
323324
| "minimal"
@@ -571,10 +572,6 @@ function resolveCanonicalSessionKeyFromSessionId(params: {
571572
}
572573
}
573574

574-
function normalizeOptionalString(value: unknown): string | undefined {
575-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
576-
}
577-
578575
function formatRuntimeToolsAllowSource(toolsAllow: readonly string[]): string {
579576
return `runtime toolsAllow: ${toolsAllow.join(", ")}`;
580577
}
@@ -877,9 +874,7 @@ function normalizePluginConfig(
877874
: [];
878875
return {
879876
enabled: raw.enabled !== false,
880-
agents: Array.isArray(raw.agents)
881-
? raw.agents.map((agentId) => agentId.trim()).filter(Boolean)
882-
: [],
877+
agents: Array.isArray(raw.agents) ? normalizeStringEntries(raw.agents) : [],
883878
model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : undefined,
884879
modelFallback:
885880
typeof raw.modelFallback === "string" && raw.modelFallback.trim()
@@ -1518,11 +1513,11 @@ function buildPluginDebugLine(params: {
15181513
warning && action && !cleaned
15191514
? `${warning} ${action}`
15201515
: [warning, action && !cleaned ? action : ""]
1521-
.filter((value, index, values) => Boolean(value) && values.indexOf(value) === index)
1516+
.filter((value): value is string => Boolean(value))
15221517
.join(" | ");
1523-
const messages = [warningAction, cleaned]
1524-
.filter((value, index, values) => Boolean(value) && values.indexOf(value) === index)
1525-
.join(" | ");
1518+
const messages = uniqueStrings(
1519+
[warningAction, cleaned].filter((value): value is string => Boolean(value)),
1520+
).join(" | ");
15261521
const trailing = messages;
15271522
if (prefix && trailing) {
15281523
return `${ACTIVE_MEMORY_DEBUG_PREFIX} ${prefix} | ${trailing}`;

extensions/admin-http-rpc/src/handler.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { randomUUID } from "node:crypto";
22
import type { IncomingMessage, ServerResponse } from "node:http";
33
import { dispatchGatewayMethod } from "openclaw/plugin-sdk/gateway-method-runtime";
4+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
45
import { isAdminHttpRpcAllowedMethod, listAdminHttpRpcAllowedMethods } from "./methods.js";
56

67
const DEFAULT_RPC_BODY_BYTES = 1024 * 1024;
@@ -38,10 +39,6 @@ type ParsedRequest = {
3839
params?: unknown;
3940
};
4041

41-
function isRecord(value: unknown): value is Record<string, unknown> {
42-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
43-
}
44-
4542
function createError(code: string, message: string): RpcError {
4643
return { code, message };
4744
}

extensions/amazon-bedrock/embedding-provider.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
type MemoryEmbeddingProvider,
55
type MemoryEmbeddingProviderCreateOptions,
66
} from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
7-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
7+
import {
8+
asOptionalRecord as asRecord,
9+
normalizeLowercaseStringOrEmpty,
10+
} from "openclaw/plugin-sdk/string-coerce-runtime";
811
import { refreshAwsSharedConfigCacheForBedrock } from "./aws-credential-refresh.js";
912

1013
// ---------------------------------------------------------------------------
@@ -258,12 +261,6 @@ function asNumberArray(value: unknown): number[] {
258261
return value;
259262
}
260263

261-
function asRecord(value: unknown): Record<string, unknown> | undefined {
262-
return typeof value === "object" && value !== null && !Array.isArray(value)
263-
? (value as Record<string, unknown>)
264-
: undefined;
265-
}
266-
267264
function asNumberArrayBatch(value: unknown): number[][] {
268265
if (!Array.isArray(value)) {
269266
throw malformedBedrockEmbeddingResponse();

0 commit comments

Comments
 (0)