Use shared SDK payload helpers directly in the outbound payload contract helper
and narrow ZaloUser target parsing to its session-route module. This preserves
the contract proof without loading broad extension runtime/test barrels.
Skip bundled channel discovery for plain message-action params and only resolve
plugin-owned media params when an extension field is actually present. This
keeps normal sends on the lightweight path while preserving plugin media-field
coverage.
Run setup auto-enable probes only for plugin ids made relevant by the
current config instead of loading every setup API. This keeps provider
plugin auto-enable checks from paying unrelated setup registration cost.
Lazy-load the SearXNG web-search client from provider execution and reuse
the shared contract helper for credential and selection wiring. Keep the
shared fast-path contract focused on the single bundled manifest it checks.
Keep the Minimax web-search provider artifact metadata-only and move
execution, cache, endpoint, and test helpers behind a lazy runtime import.
This keeps contract metadata tests from importing the full runtime path.
* fix(exec-approvals): escape control characters in display sanitizers
* docs(changelog): add exec approval control-char display sanitizer entry
* fix(exec-approvals): redact before escape, cover U+2028/U+2029 in display sanitizers
* fix(exec-approvals): strip invisibles before redaction and align forwarder test
* fix(exec-approvals): cover Zs bypass and preserve multi-line context on obfuscated secrets
* fix(exec-approvals): compare redaction outputs by content, not length
* fix(exec-approvals): suppress raw command on bypass; cover non-ASCII Zs in macOS sanitizer
* fix(exec-approvals): use position-bitmap bypass detection and bound input size
* style(exec-approvals): satisfy oxlint no-new-array-single-argument and SwiftFormat
* fix(exec-approvals): iterate by code point and redact before truncating
Keep the Perplexity web-search public provider artifact metadata-only and move
execution, cache, HTTP, and runtime helper tests behind a lazy runtime seam.
This keeps bundled web-search contract checks from loading runtime-only code.
Honor targeted includes in the contracts Vitest lane and compare bundled
web-search fast-path artifacts against plugin-owned runtime artifacts instead
of loading whole plugin entries. Split Google and Firecrawl runtime-only work
behind lazy seams so provider registration stays metadata-light.
Also keep Perplexity contract metadata aligned by sharing its runtime transport
resolution with the contract artifact.
* fix(gateway): enforce assistant media scopes
* changelog: require read scope for assistant media (#68175)
* skip scope enforcement for auth.mode=none
Exclude method "none" from the identity-bearing scope gate so
gateway.auth.mode=none deployments are not regressed by the new
operator.read check.
---------
Co-authored-by: Devin Robison <drobison@nvidia.com>
* fix(agents): filter bundled tools through final policy
* changelog: filter bundled tools through final policy (#68195)
* forward agentId into compaction tool-policy filter
Pass effectiveSkillAgentId to applyFinalEffectiveToolPolicy in the
compaction path so per-agent tool policies apply to bundled tools
during compaction the same way they do during normal runs.
* scope final tool-policy filter to bundled tools only
Running the full tool-policy pipeline on the merged core + bundled tool list
re-filters core tools whose plugin WeakMap metadata no longer survives the
normalize/hook wrappers applied by createOpenClawCodingTools(). Narrow the
helper to only the newly-appended bundled MCP/LSP tools so plugin-provided
core tools keep matching group:plugins and plugin-id allowlist entries.
* harden authorization signals on final tool policy
- message.action gateway handler now server-derives senderIsOwner from the
authenticated gateway client scopes (ADMIN_SCOPE on client.connect.scopes)
and ignores any senderIsOwner value on the wire, so a non-admin scoped
caller cannot spoof owner status to unlock owner-only channel actions or
owner-only tool policy. Schema keeps the field optional for wire compat
but documents that it is ignored.
- applyFinalEffectiveToolPolicy now cross-checks caller-provided groupId
against the session-derived group context resolved from sessionKey (and
spawnedBy). When they disagree, the caller groupId plus its adjacent
groupChannel/groupSpace are dropped and a warn is emitted, so a caller
that fabricates a different group id cannot reach a more permissive
group-scoped tool policy during the final bundled-tool filter. Added a
JSDoc trust invariant on the helper input describing the required
server-verified identity contract.
* align compact agentId resolution with core tools
Drop the explicit agentId on applyFinalEffectiveToolPolicy during
compaction. The core tool set produced just above via
createOpenClawCodingTools(...) also omits agentId, so resolveEffectiveToolPolicy
falls back to resolveAgentIdFromSessionKey(sessionKey) in both places.
Passing effectiveSkillAgentId only to the final filter made the two
policy lookups diverge on legacy/non-agent session keys where the
sessionKey path resolves to main but effectiveSkillAgentId follows the
configured default-agent path, which could deny or allow bundled tools
under a different per-agent policy than the already-created core tools.
* tighten trusted propagation for owner and group signals
- message.action gateway handler: full-operator callers (shared-secret
bearer or operator.admin scope) now propagate the request-provided
senderIsOwner through to channel action handlers instead of having it
hard-coded off. Previously the hardened path force-derived ownership
from ADMIN_SCOPE alone, which broke owner-gated actions when the
trusted runtime forwards them via the least-privilege gateway path
(callGatewayLeastPrivilege requests only the method scope, so even
legitimate owner senders were downgraded to senderIsOwner=false).
Narrowly-scoped callers (e.g. operator.write-only) still have the wire
value forced to false so a non-admin caller cannot assert ownership.
- applyFinalEffectiveToolPolicy: fail-closed when the session key and
spawnedBy encode no group context. Previously the helper only dropped
a caller-provided groupId that conflicted with a non-empty set of
session-derived group ids, which left an accept-caller fallback open
when the session had no group context at all (direct/cron/subagent
session keys). An attacker who could run without a group-bound session
could then supply an arbitrary groupId and reach a more permissive
group-scoped tool policy. Now: no session-derived group context plus
any caller-provided groupId drops the caller value and warns.
* suppress unavailable-core-tool warnings in bundled-only pass
applyToolPolicyPipeline infers its coreToolNames reference set from the
tools array it is filtering. The bundled-only second pass only sees the
MCP/LSP subset, so normal core allowlist entries (for example
tools.allow: ['read', 'exec']) would look "unknown" during this pass
and emit misleading warnings even when the config is valid for the full
effective tool set — polluting logs and potentially evicting real
diagnostics from the shared warning cache. Set
suppressUnavailableCoreToolWarning on every step of this pass so known
core-tool allowlist entries stay silent; genuinely unknown entries
still surface through the otherEntries warning path.
Keep explicit session-key normalization on loaded channel plugins so
unknown provider contexts pass through without cold-loading bundled channel
runtimes. This preserves active plugin behavior and removes the slow
unknown-provider test path.