Implementation:Openclaw Openclaw ApplyAgentBindings
ApplyAgentBindings
ApplyAgentBindings documents the applyAgentBindings function in src/commands/agents.bindings.ts and the resolveAgentRoute function in src/routing/resolve-route.ts. Together, these implement the two sides of binding configuration: applying bindings to the config (write-time) and resolving bindings at message routing time (read-time).
Principle:Openclaw_Openclaw_Binding_Configuration
Type: API Doc
Source Locations
| File | Lines | Description |
|---|---|---|
src/commands/agents.bindings.ts |
L39-90 | applyAgentBindings() -- applies bindings with deduplication and conflict detection
|
src/routing/resolve-route.ts |
L167-260 | resolveAgentRoute() -- resolves inbound messages to an agent via binding evaluation
|
src/commands/agents.bindings.ts |
L9-19 | bindingMatchKey() -- computes the deduplication key for a binding
|
src/commands/agents.bindings.ts |
L21-37 | describeBinding() -- produces a human-readable description of a binding
|
applyAgentBindings
Signature
export function applyAgentBindings(
cfg: OpenClawConfig,
bindings: AgentBinding[],
): {
config: OpenClawConfig;
added: AgentBinding[];
skipped: AgentBinding[];
conflicts: Array<{ binding: AgentBinding; existingAgentId: string }>;
}
Parameters
| Parameter | Type | Description |
|---|---|---|
cfg |
OpenClawConfig |
The current configuration object containing existing bindings. |
bindings |
AgentBinding[] |
The new bindings to apply. |
Return Value
| Field | Type | Description |
|---|---|---|
config |
OpenClawConfig |
Updated config with new bindings appended. Unchanged if no bindings were added. |
added |
AgentBinding[] |
Bindings that were successfully added (new match keys). |
skipped |
AgentBinding[] |
Bindings that were idempotent (same match key, same agent). |
conflicts |
Array<{ binding, existingAgentId }> |
Bindings that conflict with an existing binding for a different agent. |
Algorithm
- Build a map of existing match keys to agent ids from
cfg.bindings. - For each new binding:
- Compute the match key via
bindingMatchKey(). - If the key exists for the same agent, add to
skipped. - If the key exists for a different agent, add to
conflicts. - Otherwise, add to
addedand update the map.
- Compute the match key via
- If no bindings were added, return the original config unchanged.
- Otherwise, return a new config with the added bindings appended to the existing array.
Match Key Format
The bindingMatchKey() function produces a pipe-delimited string:
function bindingMatchKey(match: AgentBinding["match"]): string {
const accountId = match.accountId?.trim() || DEFAULT_ACCOUNT_ID;
return [
match.channel,
accountId,
match.peer?.kind ?? "",
match.peer?.id ?? "",
match.guildId ?? "",
match.teamId ?? "",
].join("|");
}
This ensures that two bindings with the same channel, account, peer, guild, and team are treated as duplicates.
resolveAgentRoute
Signature
export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentRoute
Input Type
export type ResolveAgentRouteInput = {
cfg: OpenClawConfig;
channel: string;
accountId?: string | null;
peer?: RoutePeer | null;
parentPeer?: RoutePeer | null;
guildId?: string | null;
teamId?: string | null;
};
Output Type
export type ResolvedAgentRoute = {
agentId: string;
channel: string;
accountId: string;
sessionKey: string;
mainSessionKey: string;
matchedBy:
| "binding.peer"
| "binding.peer.parent"
| "binding.guild"
| "binding.team"
| "binding.account"
| "binding.channel"
| "default";
};
Resolution Algorithm
The function evaluates bindings in strict priority order:
- Filter bindings to those matching the channel and account id.
- Peer match: Find a binding whose
match.peermatches the inbound peer. Returns withmatchedBy: "binding.peer". - Parent peer match: If the message is a thread, check the parent peer. Returns with
matchedBy: "binding.peer.parent". - Guild match: Find a binding whose
match.guildIdmatches. Returns withmatchedBy: "binding.guild". - Team match: Find a binding whose
match.teamIdmatches. Returns withmatchedBy: "binding.team". - Account match: Find a binding with no peer/guild/team qualifiers and a specific (non-wildcard) account id. Returns with
matchedBy: "binding.account". - Channel match: Find a binding with
accountId: "*"and no peer/guild/team qualifiers. Returns withmatchedBy: "binding.channel". - Default: Falls back to
resolveDefaultAgentId(cfg). Returns withmatchedBy: "default".
Each match constructs a session key via buildAgentSessionKey() and a main session key via buildAgentMainSessionKey(), both lowercased for consistency.
Account Matching
The matchesAccountId() helper has three behaviors:
- Empty/missing match: only matches the default account id.
- Wildcard (
"*"): matches any account id. - Specific value: exact match only.
Supporting Functions
| Function | File | Purpose |
|---|---|---|
describeBinding(binding) |
src/commands/agents.bindings.ts |
Returns a human-readable string like "whatsapp accountId=personal peer=dm:+15551234567".
|
buildChannelBindings(params) |
src/commands/agents.bindings.ts |
Creates bindings from channel selections during agent creation. |
parseBindingSpecs(params) |
src/commands/agents.bindings.ts |
Parses CLI --bind flag values into AgentBinding[].
|
listBindings(cfg) |
src/routing/bindings.ts |
Retrieves the bindings array from config. |
pickFirstExistingAgentId(cfg, agentId) |
src/routing/resolve-route.ts |
Validates the agent id exists in config, falls back to default. |