mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
docs: document agent tool policy helpers
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
loadAuthProfileStoreForSecretsRuntime,
|
||||
} from "./auth-profiles/store.js";
|
||||
|
||||
/** Options for discovering credentials without prompting for secret material. */
|
||||
export type DiscoverAuthStorageOptions = {
|
||||
externalCli?: ExternalCliAuthDiscovery;
|
||||
readOnly?: boolean;
|
||||
@@ -25,6 +26,7 @@ export type DiscoverAuthStorageOptions = {
|
||||
syntheticAuthProviderRefs?: Iterable<string>;
|
||||
} & AgentDiscoveryAuthLookupOptions;
|
||||
|
||||
/** Resolves agent credentials from auth profiles, env, and synthetic auth hooks. */
|
||||
export function resolveAgentCredentialsForDiscovery(
|
||||
agentDir: string,
|
||||
options?: DiscoverAuthStorageOptions,
|
||||
@@ -62,6 +64,8 @@ export function resolveAgentCredentialsForDiscovery(
|
||||
if (credentials[provider]) {
|
||||
continue;
|
||||
}
|
||||
// Synthetic auth is a plugin/runtime fallback. Only fill empty providers so
|
||||
// persisted profiles and env-backed credentials remain authoritative.
|
||||
const resolved = resolveProviderSyntheticAuthWithPlugin({
|
||||
provider,
|
||||
context: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { DeliveryContext } from "../utils/delivery-context.types.js";
|
||||
import type { AnyAgentTool } from "./tools/common.js";
|
||||
|
||||
/** Applies delivery-context defaults to plugin tools before final tool policy. */
|
||||
export function applyPluginToolDeliveryDefaults(params: {
|
||||
tools: AnyAgentTool[];
|
||||
deliveryContext?: DeliveryContext;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ensureCustomApiRegistered } from "./custom-api-registry.js";
|
||||
import { createTransportAwareStreamFnForModel } from "./provider-transport-stream.js";
|
||||
import type { StreamFn } from "./runtime/index.js";
|
||||
|
||||
/** Resolves and registers the stream function for a provider-backed model. */
|
||||
export function registerProviderStreamForModel<TApi extends Api>(params: {
|
||||
model: Model<TApi>;
|
||||
cfg?: OpenClawConfig;
|
||||
@@ -38,6 +39,8 @@ export function registerProviderStreamForModel<TApi extends Api>(params: {
|
||||
if (!streamFn) {
|
||||
return undefined;
|
||||
}
|
||||
// Register custom APIs only after a concrete stream exists, so later callers
|
||||
// can route by model.api without reloading provider runtime hooks.
|
||||
ensureCustomApiRegistered(params.model.api, streamFn);
|
||||
return streamFn;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { SubagentRunRecord } from "./subagent-registry.types.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/subagent-registry-completion");
|
||||
|
||||
/** Compares subagent run outcomes, treating missing timing as compatible. */
|
||||
export function runOutcomesEqual(
|
||||
a: SubagentRunOutcome | undefined,
|
||||
b: SubagentRunOutcome | undefined,
|
||||
@@ -37,6 +38,7 @@ export function runOutcomesEqual(
|
||||
return a.startedAt === b.startedAt && a.endedAt === b.endedAt && a.elapsedMs === b.elapsedMs;
|
||||
}
|
||||
|
||||
/** Returns true when an outcome carries timing fields. */
|
||||
export function runOutcomeHasTiming(outcome: SubagentRunOutcome | undefined): boolean {
|
||||
return (
|
||||
Number.isFinite(outcome?.startedAt) ||
|
||||
@@ -45,6 +47,7 @@ export function runOutcomeHasTiming(outcome: SubagentRunOutcome | undefined): bo
|
||||
);
|
||||
}
|
||||
|
||||
/** Returns true when a run outcome update should replace current state. */
|
||||
export function shouldUpdateRunOutcome(
|
||||
current: SubagentRunOutcome | undefined,
|
||||
next: SubagentRunOutcome | undefined,
|
||||
@@ -54,6 +57,7 @@ export function shouldUpdateRunOutcome(
|
||||
);
|
||||
}
|
||||
|
||||
/** Maps registry run outcome to lifecycle event outcome. */
|
||||
export function resolveLifecycleOutcomeFromRunOutcome(
|
||||
outcome: SubagentRunOutcome | undefined,
|
||||
): SubagentLifecycleEndedOutcome {
|
||||
@@ -66,6 +70,7 @@ export function resolveLifecycleOutcomeFromRunOutcome(
|
||||
return SUBAGENT_ENDED_OUTCOME_OK;
|
||||
}
|
||||
|
||||
/** Emits the subagent_ended hook once per completed run. */
|
||||
export async function emitSubagentEndedHookOnce(params: {
|
||||
entry: SubagentRunRecord;
|
||||
reason: SubagentLifecycleEndedReason;
|
||||
@@ -87,6 +92,8 @@ export async function emitSubagentEndedHookOnce(params: {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In-flight guard prevents concurrent completion paths from double-emitting
|
||||
// the hook before endedHookEmittedAt is persisted.
|
||||
params.inFlightRunIds.add(runId);
|
||||
try {
|
||||
const hookRunner = getGlobalHookRunner();
|
||||
|
||||
@@ -4,6 +4,9 @@ import { subagentRuns } from "./subagent-registry-memory.js";
|
||||
import { getSubagentRunsSnapshotForRead } from "./subagent-registry-state.js";
|
||||
import type { SubagentRunRecord } from "./subagent-registry.types.js";
|
||||
|
||||
// Session maintenance must preserve child sessions that are active, awaiting a
|
||||
// completion announce, or suspended for later delivery. Completed cleanup rows
|
||||
// no longer need to pin their session keys.
|
||||
function isCleanupCompleteForMaintenance(entry: SubagentRunRecord): boolean {
|
||||
return typeof entry.cleanupCompletedAt === "number";
|
||||
}
|
||||
@@ -32,6 +35,7 @@ function shouldPreserveForMaintenance(entry: SubagentRunRecord): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/** Lists child session keys protected from session-store maintenance pruning. */
|
||||
export function listSessionMaintenanceProtectedSubagentSessionKeys(): string[] {
|
||||
const keys = new Set<string>();
|
||||
for (const entry of getSubagentRunsSnapshotForRead(subagentRuns).values()) {
|
||||
|
||||
@@ -10,8 +10,10 @@ import {
|
||||
UPDATE_PLAN_TOOL_DISPLAY_SUMMARY,
|
||||
} from "./tool-description-presets.js";
|
||||
|
||||
/** Built-in tool profile ids exposed in config and UI. */
|
||||
export type ToolProfileId = "minimal" | "coding" | "messaging" | "full";
|
||||
|
||||
/** Allow/deny policy generated from a built-in tool profile. */
|
||||
type ToolProfilePolicy = {
|
||||
allow?: string[];
|
||||
deny?: string[];
|
||||
@@ -36,6 +38,8 @@ type CoreToolDefinition = {
|
||||
includeInOpenClawGroup?: boolean;
|
||||
};
|
||||
|
||||
// The core tool catalog drives profile defaults, UI grouping, and group:openclaw
|
||||
// expansion. Keep ids normalized because policy matching is string-based.
|
||||
const CORE_TOOL_SECTION_ORDER: Array<{ id: string; label: string }> = [
|
||||
{ id: "fs", label: "Files" },
|
||||
{ id: "runtime", label: "Runtime" },
|
||||
@@ -385,8 +389,10 @@ function buildCoreToolGroupMap() {
|
||||
};
|
||||
}
|
||||
|
||||
/** Built-in core tool groups keyed by group id. */
|
||||
export const CORE_TOOL_GROUPS = buildCoreToolGroupMap();
|
||||
|
||||
/** Profile options shown in model/tool configuration UIs. */
|
||||
export const PROFILE_OPTIONS = [
|
||||
{ id: "minimal", label: "Minimal" },
|
||||
{ id: "coding", label: "Coding" },
|
||||
@@ -394,6 +400,7 @@ export const PROFILE_OPTIONS = [
|
||||
{ id: "full", label: "Full" },
|
||||
] as const;
|
||||
|
||||
/** Resolves the allow/deny policy for a built-in tool profile. */
|
||||
export function resolveCoreToolProfilePolicy(profile?: string): ToolProfilePolicy | undefined {
|
||||
if (!profile) {
|
||||
return undefined;
|
||||
@@ -411,6 +418,7 @@ export function resolveCoreToolProfilePolicy(profile?: string): ToolProfilePolic
|
||||
};
|
||||
}
|
||||
|
||||
/** Lists core tools grouped into UI sections. */
|
||||
export function listCoreToolSections(): CoreToolSection[] {
|
||||
return CORE_TOOL_SECTION_ORDER.map((section) => ({
|
||||
id: section.id,
|
||||
@@ -423,6 +431,7 @@ export function listCoreToolSections(): CoreToolSection[] {
|
||||
})).filter((section) => section.tools.length > 0);
|
||||
}
|
||||
|
||||
/** Lists built-in profile ids that include a core tool. */
|
||||
export function resolveCoreToolProfiles(toolId: string): ToolProfileId[] {
|
||||
const tool = CORE_TOOL_BY_ID.get(toolId);
|
||||
if (!tool) {
|
||||
@@ -431,6 +440,7 @@ export function resolveCoreToolProfiles(toolId: string): ToolProfileId[] {
|
||||
return [...tool.profiles];
|
||||
}
|
||||
|
||||
/** Returns true when a tool id is a known core tool. */
|
||||
export function isKnownCoreToolId(toolId: string): boolean {
|
||||
return CORE_TOOL_BY_ID.has(toolId);
|
||||
}
|
||||
|
||||
@@ -12,25 +12,30 @@ export {
|
||||
} from "./tool-policy-shared.js";
|
||||
export type { ToolProfileId } from "./tool-policy-shared.js";
|
||||
|
||||
/** Tool allow/deny policy shape accepted by agent and sandbox config. */
|
||||
export type ToolPolicyLike = {
|
||||
allow?: string[];
|
||||
deny?: string[];
|
||||
[IMPLICIT_ALLOW_ALL_FROM_ALSO_ALLOW]?: true;
|
||||
};
|
||||
|
||||
/** Plugin-owned tool group expansion state. */
|
||||
export type PluginToolGroups = {
|
||||
all: string[];
|
||||
byPlugin: Map<string, string[]>;
|
||||
};
|
||||
|
||||
/** Analysis of an allowlist after matching core and plugin tool ids. */
|
||||
export type AllowlistResolution = {
|
||||
policy: ToolPolicyLike | undefined;
|
||||
unknownAllowlist: string[];
|
||||
pluginOnlyAllowlist: boolean;
|
||||
};
|
||||
|
||||
/** Synthetic allowlist entry that means "use default plugin tools". */
|
||||
export const DEFAULT_PLUGIN_TOOLS_ALLOWLIST_ENTRY = "__openclaw_default_plugin_tools__";
|
||||
|
||||
/** Returns true when an allow policy is narrower than all/default plugin tools. */
|
||||
export function hasRestrictiveAllowPolicy(policy?: { allow?: string[] }): boolean {
|
||||
return (
|
||||
Array.isArray(policy?.allow) &&
|
||||
@@ -45,6 +50,7 @@ export function hasRestrictiveAllowPolicy(policy?: { allow?: string[] }): boolea
|
||||
);
|
||||
}
|
||||
|
||||
/** Replaces an allowlist with the normalized names of an effective tool array. */
|
||||
export function replaceWithEffectiveToolAllowlist(
|
||||
target: string[],
|
||||
tools: Array<{ name: string }>,
|
||||
@@ -61,6 +67,7 @@ export function replaceWithEffectiveToolAllowlist(
|
||||
}
|
||||
}
|
||||
|
||||
/** Collects explicit allow entries from layered policies. */
|
||||
export function collectExplicitAllowlist(policies: Array<ToolPolicyLike | undefined>): string[] {
|
||||
const entries: string[] = [];
|
||||
for (const policy of policies) {
|
||||
@@ -73,6 +80,8 @@ export function collectExplicitAllowlist(policies: Array<ToolPolicyLike | undefi
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (trimmed === "*" && policy[IMPLICIT_ALLOW_ALL_FROM_ALSO_ALLOW] === true) {
|
||||
// alsoAllow implicitly injects "*" for sandbox compatibility; do not
|
||||
// report that implicit wildcard as an explicit operator allow entry.
|
||||
continue;
|
||||
}
|
||||
if (trimmed) {
|
||||
@@ -86,6 +95,7 @@ export function collectExplicitAllowlist(policies: Array<ToolPolicyLike | undefi
|
||||
return uniqueStrings(entries);
|
||||
}
|
||||
|
||||
/** Collects explicit deny entries from layered policies. */
|
||||
export function collectExplicitDenylist(policies: Array<ToolPolicyLike | undefined>): string[] {
|
||||
const entries: string[] = [];
|
||||
for (const policy of policies) {
|
||||
@@ -105,6 +115,7 @@ export function collectExplicitDenylist(policies: Array<ToolPolicyLike | undefin
|
||||
return entries;
|
||||
}
|
||||
|
||||
/** Builds plugin tool groups from tool metadata. */
|
||||
export function buildPluginToolGroups<T extends { name: string }>(params: {
|
||||
tools: T[];
|
||||
toolMeta: (tool: T) => { pluginId: string } | undefined;
|
||||
@@ -129,6 +140,7 @@ export function buildPluginToolGroups<T extends { name: string }>(params: {
|
||||
return { all, byPlugin };
|
||||
}
|
||||
|
||||
/** Expands group:plugins and plugin-id entries into concrete plugin tool names. */
|
||||
export function expandPluginGroups(
|
||||
list: string[] | undefined,
|
||||
groups: PluginToolGroups,
|
||||
@@ -157,6 +169,7 @@ export function expandPluginGroups(
|
||||
return uniqueStrings(expanded);
|
||||
}
|
||||
|
||||
/** Expands plugin groups in a policy while preserving undefined policies. */
|
||||
export function expandPolicyWithPluginGroups(
|
||||
policy: ToolPolicyLike | undefined,
|
||||
groups: PluginToolGroups,
|
||||
@@ -170,6 +183,7 @@ export function expandPolicyWithPluginGroups(
|
||||
};
|
||||
}
|
||||
|
||||
/** Classifies allowlists as core, plugin-only, or unknown for diagnostics. */
|
||||
export function analyzeAllowlistByToolType(
|
||||
policy: ToolPolicyLike | undefined,
|
||||
groups: PluginToolGroups,
|
||||
@@ -210,6 +224,7 @@ export function analyzeAllowlistByToolType(
|
||||
};
|
||||
}
|
||||
|
||||
/** Merges alsoAllow entries into an existing allow policy. */
|
||||
export function mergeAlsoAllowPolicy<TPolicy extends { allow?: string[] }>(
|
||||
policy: TPolicy | undefined,
|
||||
alsoAllow?: string[],
|
||||
|
||||
Reference in New Issue
Block a user