feat(security): opt-in destination policy for the built-in transfer_call tool#206
Merged
Merged
Conversation
…all tool The transfer destination is chosen by the LLM, which is steerable by caller speech, and the only gate was E.164 format -- a successful prompt injection could direct a billable outbound leg to any attacker-chosen (premium-rate) number (GH #205). New opt-in Agent options transfer_allowed_numbers / transfer_allowed_prefixes (Py) -- transferAllowedNumbers / transferAllowedPrefixes (TS): exact-number allowlist and/or E.164 prefix allowlist (union), enforced at every guard site BEFORE the carrier REST call (OpenAI Realtime function_call, Pipeline built-in handler, ElevenLabs ConvAI client tool) with the standard rejection envelope so the agent keeps the call. Unset (default) preserves current behaviour; an empty list denies all transfers. Entries validated at agent() construction. 17 new tests per SDK.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Integrates releases #202 (0.7.1) and #208 (low-latency pipeline) now on main. Keeps this PR's transfer-allowlist Unreleased entry alongside #208's entries under the single Unreleased section, adopts 0.7.1 across version files (no extra bump), and regenerates package-lock.json from the merged package.json. Full suites green post-merge: Python 3083, TypeScript 2493, tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D52f6rBsvzicXg45C6MKVM
MDX v3 parses `{...}` as a JS expression; `{far-field + tuned server_vad}`
(space-separated identifiers) is invalid JS, so acorn failed at openclaw.mdx
818:24 and the whole Mintlify docs deploy errored on every docs PR. Wrap the
two set-notation snippets in backticks (consistent with the doc's inline-code
style for config terms) so they render literally. Pre-existing issue on main
(introduced with #207); `mint broken-links` now reports no errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D52f6rBsvzicXg45C6MKVM
3 tasks
nicolotognoni
added a commit
that referenced
this pull request
Jul 3, 2026
Version files already bumped to 0.7.1 by #202; #206 (transfer allowlist) and #208 (low-latency voice pipeline) then merged onto main at 0.7.1 but their entries sat under Unreleased. Fold them into the 0.7.1 section so the published release notes match the code that ships. No version change (already 0.7.1); no code change. Merge this, then tag v0.7.1 to publish to npm + PyPI. Claude-Session: https://claude.ai/code/session_01D52f6rBsvzicXg45C6MKVM Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
transfer_call's destination is chosen by the LLM (steerable by caller speech), and the only gate was E.164 format: a successful prompt injection could direct a billable outbound leg to any attacker-chosen premium-rate / international number (toll fraud billed to the operator).transfer_allowed_numbers/transfer_allowed_prefixes(Python) —transferAllowedNumbers/transferAllowedPrefixes(TypeScript). Exact-number allowlist and/or E.164 prefix allowlist, union semantics.function_callpath, Pipeline built-in handler, ElevenLabs ConvAI client-tool path. A denied destination gets the standard{"error": "Transfer destination not allowed by policy", "status": "rejected"}envelope, so the agent keeps the call and can tell the caller.Implementation
_transfer_destination_allowed(libraries/python/getpatter/telephony/common.py) /isTransferDestinationAllowed(libraries/typescript/src/stream-handler.ts). Semantics: no policy → unrestricted (byte-identical to today); either list set → exact-number OR prefix match required; configured-but-empty → deny all (explicit lockdown).Agentfields (models.py/types.ts), threaded throughPatter.agent()with fail-fast validation at construction (allowlist numbers must be E.164; prefixes must be'+'+ 1-14 digits) — a typo'd entry fails at build time, not silently mid-call.self.agent/this.currentAgent), so the policy follows multi-agent handoffs; pipeline mode threads the policy into_augment_with_builtin_handoff_tools/augmentWithBuiltinHandoffTools, which is rebuilt after each handoff.transfer_call rejected: destination not allowed by policy ***1234warning with the number masked (PII-safe).control.transfer(...)) are intentionally NOT gated — the policy targets LLM-chosen destinations, not developer-initiated ones.libraries/typescript/package-lock.json's version stanza to 0.7.0 (it was left at 0.6.9 by the release bump).Breaking change?
No. Both options are opt-in with
None/undefineddefaults; unset keeps today's behaviour byte-identical (verified by a dedicated no-policy regression test in each SDK).Test plan
pytest tests/— 2918 passed (17 new intests/test_transfer_allowlist.py: helper semantics, factory validation, pipeline handler, Realtime function_call, ConvAI client tool)npm test(2328 passed, 17 new intests/transfer-allowlist.test.ts) +npm run lint+npm run build— all cleanDocs updates
docs/python-sdk/tools.mdx— new "Restricting transfer destinations" sectiondocs/typescript-sdk/tools.mdx— same, TS invocationCHANGELOG.md—## Unreleased→### Added