
























@@ -0,0 +1,196 @@
1+import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
2+import { resolveSandboxRuntimeStatus } from "openclaw/plugin-sdk/sandbox";
3+import { getSessionEntry, type SessionEntry } from "openclaw/plugin-sdk/session-store-runtime";
4+5+type ExecHost = "sandbox" | "gateway" | "node";
6+type ExecTarget = "auto" | ExecHost;
7+8+type ExecHostOverride = {
9+host?: string;
10+node?: string;
11+};
12+13+type AgentEntry = NonNullable<NonNullable<OpenClawConfig["agents"]>["list"]>[number];
14+15+const DEFAULT_AGENT_ID = "main";
16+const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
17+const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
18+const LEADING_DASH_PATTERN = /^-+/;
19+const TRAILING_DASH_PATTERN = /-+$/;
20+21+export type CodexNativeExecutionPolicy = {
22+nativeToolSurfaceAllowed: boolean;
23+requestedExecHost: ExecTarget;
24+effectiveExecHost: ExecHost;
25+node?: string;
26+blockReason?: string;
27+};
28+29+export function resolveCodexNativeExecutionPolicy(params: {
30+config?: OpenClawConfig;
31+sessionEntry?: SessionEntry;
32+sessionKey?: string;
33+sessionId?: string;
34+agentId?: string;
35+execOverrides?: ExecHostOverride;
36+sandboxAvailable?: boolean;
37+readRuntimeSessionEntry?: boolean;
38+}): CodexNativeExecutionPolicy {
39+const config = params.config ?? {};
40+const sessionKey = params.sessionKey?.trim() || params.sessionId?.trim() || undefined;
41+const sessionEntry =
42+params.sessionEntry ??
43+(params.readRuntimeSessionEntry && sessionKey
44+ ? readRuntimeSessionEntryBestEffort(sessionKey)
45+ : undefined);
46+const sandboxAvailable =
47+params.sandboxAvailable ??
48+(sessionKey
49+ ? resolveSandboxRuntimeStatus({
50+cfg: config,
51+ sessionKey,
52+}).sandboxed
53+ : false);
54+const agentId = resolvePolicyAgentId({ config, sessionKey, agentId: params.agentId });
55+const agentExec = resolvePolicyAgentExec({ config, agentId });
56+const globalExec = config.tools?.exec;
57+const requestedExecHost =
58+normalizeExecTarget(params.execOverrides?.host) ??
59+normalizeExecTarget(sessionEntry?.execHost) ??
60+normalizeExecTarget(agentExec?.host) ??
61+normalizeExecTarget(globalExec?.host) ??
62+"auto";
63+const effectiveExecHost = resolveEffectiveExecHost({
64+ requestedExecHost,
65+ sandboxAvailable,
66+});
67+const node =
68+params.execOverrides?.node ?? sessionEntry?.execNode ?? agentExec?.node ?? globalExec?.node;
69+if (effectiveExecHost !== "node") {
70+return {
71+nativeToolSurfaceAllowed: true,
72+ requestedExecHost,
73+ effectiveExecHost,
74+ node,
75+};
76+}
77+return {
78+nativeToolSurfaceAllowed: false,
79+ requestedExecHost,
80+ effectiveExecHost,
81+ node,
82+blockReason:
83+"OpenClaw exec host=node is active for this session. Codex app-server native execution cannot route shell, filesystem, MCP, or app-backed work through the selected OpenClaw node.",
84+};
85+}
86+87+export function formatCodexNativeNodeExecBlock(params: {
88+surface: string;
89+reason?: string;
90+}): string {
91+return [
92+`Codex-native ${params.surface} is unavailable because OpenClaw exec host=node is active for this session.`,
93+params.reason ??
94+"Codex app-server native execution cannot route execution through the selected OpenClaw node.",
95+"Use a normal Codex harness turn so OpenClaw exec/process tools run on the node, or switch exec host to gateway for native Codex app-server execution.",
96+].join(" ");
97+}
98+99+function resolvePolicyAgentId(params: {
100+config: OpenClawConfig;
101+sessionKey?: string;
102+agentId?: string;
103+}): string {
104+const explicitAgentId = normalizeAgentIdOrDefault(params.agentId);
105+if (explicitAgentId) {
106+return explicitAgentId;
107+}
108+const sessionAgentId = parseAgentIdFromSessionKey(params.sessionKey);
109+if (sessionAgentId) {
110+return sessionAgentId;
111+}
112+const agents = listAgentEntries(params.config);
113+const defaultEntry = agents.find((entry) => entry?.default) ?? agents[0];
114+return normalizeAgentId(defaultEntry?.id);
115+}
116+117+function resolvePolicyAgentExec(params: {
118+config: OpenClawConfig;
119+agentId: string;
120+}): ExecHostOverride | undefined {
121+return listAgentEntries(params.config).find(
122+(entry) => normalizeAgentId(entry?.id) === params.agentId,
123+)?.tools?.exec;
124+}
125+126+function listAgentEntries(config: OpenClawConfig): AgentEntry[] {
127+return (config.agents?.list ?? []).filter(
128+(entry): entry is AgentEntry => entry !== null && typeof entry === "object",
129+);
130+}
131+132+function parseAgentIdFromSessionKey(sessionKey?: string): string | undefined {
133+const raw = sessionKey?.trim();
134+if (!raw) {
135+return undefined;
136+}
137+const parts = raw.toLowerCase().split(":").filter(Boolean);
138+if (parts.length < 3 || parts[0] !== "agent" || !parts[2]) {
139+return undefined;
140+}
141+return normalizeAgentIdOrDefault(parts[1]);
142+}
143+144+function normalizeAgentIdOrDefault(value?: string | null): string | undefined {
145+const normalized = normalizeAgentId(value);
146+return normalized === DEFAULT_AGENT_ID && !(value ?? "").trim() ? undefined : normalized;
147+}
148+149+function normalizeAgentId(value?: string | null): string {
150+const trimmed = (value ?? "").trim();
151+if (!trimmed) {
152+return DEFAULT_AGENT_ID;
153+}
154+const normalized = trimmed.toLowerCase();
155+if (VALID_AGENT_ID_PATTERN.test(trimmed)) {
156+return normalized;
157+}
158+return (
159+normalized
160+.replace(INVALID_AGENT_ID_CHARS_PATTERN, "-")
161+.replace(LEADING_DASH_PATTERN, "")
162+.replace(TRAILING_DASH_PATTERN, "")
163+.slice(0, 64) || DEFAULT_AGENT_ID
164+);
165+}
166+167+function normalizeExecTarget(value?: string | null): ExecTarget | undefined {
168+const normalized = value?.trim().toLowerCase();
169+if (
170+normalized === "auto" ||
171+normalized === "sandbox" ||
172+normalized === "gateway" ||
173+normalized === "node"
174+) {
175+return normalized;
176+}
177+return undefined;
178+}
179+180+function resolveEffectiveExecHost(params: {
181+requestedExecHost: ExecTarget;
182+sandboxAvailable: boolean;
183+}): ExecHost {
184+if (params.requestedExecHost === "auto") {
185+return params.sandboxAvailable ? "sandbox" : "gateway";
186+}
187+return params.requestedExecHost;
188+}
189+190+function readRuntimeSessionEntryBestEffort(sessionKey: string): SessionEntry | undefined {
191+try {
192+return getSessionEntry({ sessionKey });
193+} catch {
194+return undefined;
195+}
196+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。