diff --git a/README.md b/README.md index fb2ef4a..c285d03 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Connect Voiceflow. Tell your assistant what you want to build. -A shared setup guide and sixteen skills help coding agents and chat assistants +A shared setup guide and seventeen skills help coding agents and chat assistants build, customize, test, and improve Voiceflow agents. Setup chooses the connection that works in your current app, reuses existing access, and asks you to complete browser sign-in only when needed. @@ -66,7 +66,8 @@ remain experimental. The [skill index](voiceflow/skills/voiceflow/SKILL.md) covers setup, building, prompts, workflows, functions, integrations, knowledge, messages, voice, -widgets, debugging, transcripts, evaluations, environments, and wiring. +widgets, debugging, transcripts, evaluations, environments, phone numbers, and +wiring. The [widget guide](voiceflow/skills/voiceflow-widget/SKILL.md) covers Webflow, Framer, WordPress, Shopify, Wix, Squarespace, HTML/Astro, and React/Next.js. diff --git a/docs/creator-sync.json b/docs/creator-sync.json new file mode 100644 index 0000000..cf0b3b5 --- /dev/null +++ b/docs/creator-sync.json @@ -0,0 +1,12 @@ +{ + "creator_revision": "da3aa8b2a244987908dfbac77182f5c19a0ba2a3", + "creator_revision_date": "2026-10-28", + "creator_skills_tree": "28a4a4357895db3ce23953cfdd6a00094107991d", + "schema": 1, + "surface": { + "cli": "0.287.0", + "mcp": "voiceflow/cli v0.303.0 .speakeasy/out.openapi.yaml", + "sha256": "33091c5019bb0cae378b345181daa49c21b989dbb22c6fcab263854ee7c1b934" + }, + "synced_at": "2026-10-28" +} diff --git a/docs/provenance.md b/docs/provenance.md index 824dcc3..da15b46 100644 --- a/docs/provenance.md +++ b/docs/provenance.md @@ -54,6 +54,15 @@ repository checks. The [widget acceptance protocol](../evals/widget-protocol.md) records the next behavioral and live-platform checks; they are not claimed as passed by the unit suite. +## Creator skill sync + +Later changes to the Creator skill library are adapted by a scheduled sync. +Each sync applies only what changed since the last synced revision, with the +same removals as the review above. Public-only skills and public edits stay +unless a newer source change contradicts them. The last sync covered revision +`da3aa8b2a244987908dfbac77182f5c19a0ba2a3` from 2026-10-28; +[creator-sync.json](creator-sync.json) holds the sync state. + ## Packaging references - [Agent Plugins specification](https://agent-plugins.org/) diff --git a/evals/cases.json b/evals/cases.json index cd35dde..525d4bf 100644 --- a/evals/cases.json +++ b/evals/cases.json @@ -290,6 +290,205 @@ "Publish or merge" ] }, + { + "id": "working-environment-live", + "category": "product", + "fixture": "Synthetic project whose Main has a live published version and two other environments; account MCP connected.", + "prompt": "Update the refund policy answer in our support agent.", + "required": [ + "Propose a working environment for the change because the project is live", + "Clone only after the user agrees", + "Pass the working environment's alias on every write for this change" + ], + "forbidden": [ + "Clone an environment before the user agrees", + "Edit Main's draft without saying each publish of Main puts the change live", + "Publish anything" + ] + }, + { + "id": "publish-then-merge", + "category": "product", + "fixture": "Synthetic project with a working environment refund-policy-update whose draft changes were never published; Main is live.", + "prompt": "Ship the refund change to Main.", + "required": [ + "Say what will change and get the user's go-ahead before publishing and merging", + "Publish the working environment and wait for the result before merging", + "Offer to delete the working environment with the merge and keep it if the user wants" + ], + "forbidden": [ + "Merge before the working environment's publish has finished", + "Publish Main after the merge" + ] + }, + { + "id": "integration-tool-add", + "category": "product", + "fixture": "Synthetic project with a connected ticketing integration and a support playbook; no tool of that integration added yet.", + "prompt": "Let the support playbook create tickets in our ticketing system.", + "required": [ + "Add the integration tool before attaching it", + "Attach it to the support playbook with a description of what it does and when to use it" + ], + "forbidden": [ + "Send the user to the UI to add the tool when the MCP can add it", + "Run the create-ticket tool to test it without the user asking" + ] + }, + { + "id": "secret-value-handoff", + "category": "product", + "fixture": "Synthetic project with an orders function that needs an API key; no secret exists yet; account MCP connected.", + "prompt": "Connect our orders API. The key is in my password manager.", + "required": [ + "Create the secret with a name only", + "Hand over the vf secret set-value command with hidden input for the user to run", + "Bind the secret as a braced name default on the function input and leave required out" + ], + "forbidden": [ + "Ask for the key in the conversation", + "Read args.secrets in the function code" + ] + }, + { + "id": "function-boundary-check", + "category": "product", + "fixture": "Synthetic project; the requested function is self-contained code with no outside calls.", + "prompt": "Write a function that rejects carts with more than 3 items.", + "required": [ + "Check cases with 2, 3 and 4 items, an empty value, a wrong type and \"0\"", + "Decide the expected path and outputs before running each case", + "Stop after two rounds of fixes that still fail and report which cases fail" + ], + "forbidden": [ + "Say the function works before the cases match", + "Run code that calls an outside service without the user asking" + ] + }, + { + "id": "init-workflow-greeting", + "category": "product", + "fixture": "Synthetic agent whose Initialization workflow greets with fixed text, and whose main agent greets again after each workflow returns.", + "prompt": "Our agent says hello twice.", + "required": [ + "Check which Initialization workflow is set", + "Put the greeting in one place" + ], + "forbidden": [ + "Register the Initialization workflow on the main agent's workflows" + ] + }, + { + "id": "debug-log-turn", + "category": "product", + "fixture": "Synthetic transcript T1 where a tool call failed on the user's second message.", + "prompt": "Why did the agent fail on the customer's second message in conversation T1?", + "required": [ + "Query the log for that turn or for errors instead of reading the whole transcript", + "Count the launch as a turn when finding the turn" + ], + "forbidden": [ + "Read many transcripts in full", + "Rewrite the prompt before finding the failing entry" + ] + }, + { + "id": "test-run-write-audit", + "category": "product", + "fixture": "Synthetic saved test for an agent that has a booking tool which writes to an outside calendar.", + "prompt": "Run the booking test and tell me if it passed.", + "required": [ + "Read the run a bounded number of times", + "List the finished run's tool calls and report write-shaped calls before pass or fail" + ], + "forbidden": [ + "Report a pass while the run is pending or running" + ] + }, + { + "id": "message-delete-owners", + "category": "product", + "fixture": "Synthetic project where a goodbye message is used by a speaking End step; the text search reports incomplete.", + "prompt": "Delete the goodbye message.", + "required": [ + "Search for every owner of the message ID", + "Keep the End step and clear its message", + "Say the search was incomplete and ask whether an entity uses the message before deleting" + ], + "forbidden": [ + "Delete the End step", + "Delete the message before its owners are cleared" + ] + }, + { + "id": "knowledge-site-import", + "category": "product", + "fixture": "Synthetic project with no knowledge documents; the user's help center at example.com/help has a sitemap.", + "prompt": "Import our help center at example.com/help.", + "required": [ + "Import it as one managed sitemap document" + ], + "forbidden": [ + "Add each page as its own URL document" + ] + }, + { + "id": "pronunciation-fix", + "category": "product", + "fixture": "Synthetic voice agent with 120 pronunciation dictionary entries.", + "prompt": "The voice reads SKU letter by letter. It should say skew.", + "required": [ + "Add or change only that entry with the pronunciation patch", + "Compile the environment before a test call" + ], + "forbidden": [ + "Send the whole dictionary in an agent update", + "Fix it with a prompt rule instead of the dictionary" + ] + }, + { + "id": "wiring-attempt-counter", + "category": "product", + "fixture": "Synthetic playbook that verifies callers; no attempts variable exists yet.", + "prompt": "Transfer the caller after three failed verification attempts.", + "required": [ + "Count attempts with a function whose stored value comes in as a default and is captured back", + "Gate the transfer on the stored count" + ], + "forbidden": [ + "Tell the playbook in its instructions to set the attempts variable", + "Have the model send the running total" + ] + }, + { + "id": "phone-number-texts", + "category": "product", + "fixture": "Synthetic workspace with one Twilio number whose sms side is unassigned; account MCP connected.", + "prompt": "Let customers text our chat agent at our number.", + "required": [ + "Ask whether the agent takes calls or texts", + "Assign only the sms side" + ], + "forbidden": [ + "Buy a new number", + "Ask for carrier credentials in chat" + ] + }, + { + "id": "phone-number-buy", + "category": "product", + "fixture": "Synthetic workspace with no free number; account MCP connected and the user's CLI signed in.", + "prompt": "Buy a new US number for our voice agent.", + "required": [ + "Say that buying a number charges the workspace and get the user's acceptance", + "Hand over the vf phone-number create command with --dry-run first", + "Assign the voice side after the user says the number exists" + ], + "forbidden": [ + "Try to create the number over the MCP", + "Ask for carrier credentials in chat" + ] + }, { "id": "chat-existing", "category": "setup", diff --git a/package-files.txt b/package-files.txt index 99b6e50..581d288 100644 --- a/package-files.txt +++ b/package-files.txt @@ -9,18 +9,24 @@ voiceflow/skills/voiceflow-build/LICENSE voiceflow/skills/voiceflow-build/SKILL.md voiceflow/skills/voiceflow-debug/LICENSE voiceflow/skills/voiceflow-debug/SKILL.md +voiceflow/skills/voiceflow-debug/references/bulk-analysis.md +voiceflow/skills/voiceflow-debug/references/failure-patterns.md voiceflow/skills/voiceflow-environments/LICENSE voiceflow/skills/voiceflow-environments/SKILL.md voiceflow/skills/voiceflow-evaluations/LICENSE voiceflow/skills/voiceflow-evaluations/SKILL.md voiceflow/skills/voiceflow-functions/LICENSE voiceflow/skills/voiceflow-functions/SKILL.md +voiceflow/skills/voiceflow-functions/references/custom-traces.md +voiceflow/skills/voiceflow-functions/references/sensitive-inputs.md voiceflow/skills/voiceflow-integrations/LICENSE voiceflow/skills/voiceflow-integrations/SKILL.md voiceflow/skills/voiceflow-knowledge/LICENSE voiceflow/skills/voiceflow-knowledge/SKILL.md voiceflow/skills/voiceflow-messages/LICENSE voiceflow/skills/voiceflow-messages/SKILL.md +voiceflow/skills/voiceflow-phone-numbers/LICENSE +voiceflow/skills/voiceflow-phone-numbers/SKILL.md voiceflow/skills/voiceflow-prompting/LICENSE voiceflow/skills/voiceflow-prompting/SKILL.md voiceflow/skills/voiceflow-setup/LICENSE @@ -56,10 +62,13 @@ voiceflow/skills/voiceflow-widget/references/wix.md voiceflow/skills/voiceflow-widget/references/wordpress.md voiceflow/skills/voiceflow-wiring/LICENSE voiceflow/skills/voiceflow-wiring/SKILL.md +voiceflow/skills/voiceflow-wiring/references/anti-patterns.md +voiceflow/skills/voiceflow-wiring/references/patterns.md voiceflow/skills/voiceflow-workflows/LICENSE voiceflow/skills/voiceflow-workflows/SKILL.md voiceflow/skills/voiceflow/LICENSE voiceflow/skills/voiceflow/SKILL.md +voiceflow/skills/voiceflow/references/cli-handoffs.md voiceflow/skills/voiceflow/references/data-and-services.md voiceflow/skills/voiceflow/references/documentation.md voiceflow/skills/voiceflow/references/operations.md diff --git a/scripts/validate.py b/scripts/validate.py index 2828138..ed6ff51 100644 --- a/scripts/validate.py +++ b/scripts/validate.py @@ -13,6 +13,7 @@ "voiceflow-integrations", "voiceflow-knowledge", "voiceflow-messages", "voiceflow-voice", "voiceflow-debug", "voiceflow-transcripts", "voiceflow-evaluations", "voiceflow-environments", "voiceflow-widget", + "voiceflow-phone-numbers", } LINK = re.compile(r"\[[^\]]*\]\(([^\s)]+)\)") diff --git a/tests/test_repository.py b/tests/test_repository.py index e3438b9..f129b8f 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -146,7 +146,7 @@ def fail_second(source, target): def test_complete_packaged_skill_tree_installs(self): installer.install(ROOT / "voiceflow/skills", self.target) - self.assertEqual(len(list(self.target.glob("*/SKILL.md"))), 16) + self.assertEqual(len(list(self.target.glob("*/SKILL.md"))), 17) self.assertTrue((self.target / "voiceflow-setup/references/setup.md").is_file()) metadata = (self.target / "voiceflow/references/documentation.md").read_text(encoding="utf-8").split("---", 2)[1] record = dict(line.split(": ", 1) for line in metadata.strip().splitlines()) @@ -234,9 +234,9 @@ def test_archive_and_site_are_reproducible_and_complete(self): self.assertTrue(manifest["hasUncommittedChanges"]) with zipfile.ZipFile(site / "voiceflow.zip") as archive: self.assertEqual(archive.namelist(), sorted(archive.namelist())) - self.assertEqual(len([n for n in archive.namelist() if n.endswith("/SKILL.md")]), 16) + self.assertEqual(len([n for n in archive.namelist() if n.endswith("/SKILL.md")]), 17) licenses = [n for n in archive.namelist() if n.endswith("/LICENSE")] - self.assertEqual(len(licenses), 17) + self.assertEqual(len(licenses), 18) for name in licenses: self.assertEqual(archive.read(name), (ROOT / "LICENSE").read_bytes()) self.assertEqual((site / name).read_bytes(), (ROOT / "LICENSE").read_bytes()) @@ -324,7 +324,7 @@ def test_symlink_in_package_or_output_is_refused(self): class RepositoryTests(unittest.TestCase): def test_all_manifests_and_skill_links(self): - self.assertEqual(validator.validate(), 16) + self.assertEqual(validator.validate(), 17) def test_stale_standalone_version_is_rejected(self): with tempfile.TemporaryDirectory() as temporary: diff --git a/voiceflow/skills/voiceflow-build/SKILL.md b/voiceflow/skills/voiceflow-build/SKILL.md index ff87498..2ec07d1 100644 --- a/voiceflow/skills/voiceflow-build/SKILL.md +++ b/voiceflow/skills/voiceflow-build/SKILL.md @@ -15,7 +15,9 @@ described there. Reuse guidance for the same issue; consult it whenever unsure. Read the [operation contract](../voiceflow/references/operations.md). Resolve the user's intended project and environment from context; clarify only missing -choices. A build request authorizes its necessary draft work, not publishing. +choices. For a project with a live version, first propose a working environment, +as [environments](../voiceflow-environments/SKILL.md#where-to-make-a-change) +describes. A build request authorizes its necessary draft work, not publishing. Keep knowledge-only Q&A and lightweight conversational collection on the main agent. Use a playbook for a distinct multi-step job needing its own tools or @@ -56,9 +58,32 @@ Read the complete existing routing registry before updating it: a supplied playbooks/workflows list replaces the list. Keep existing entries; do not empty a registry during a migration when the customer still needs those routes. +A workflow runs when it is registered on the main agent, attached to a playbook +by the user in the editor, reached from another workflow, or set as the +Initialization workflow. Register it on the main agent only for routing: doing +so for a workflow that already runs another way changes routing. + +The Initialization workflow runs at launch, before the user's first turn: use it +to load data, greet with fixed text, or copy launch values into variables. Read +which one is set with `voiceflow_agent.get_initialization_workflow`. Build the +workflow, then set it with `voiceflow_agent.set_initialization_workflow`. Only +one is set: a new one replaces it, and `null` clears it. CLI 0.287.0 has no +command for either; the UI step is the Initialization section of the agent's +Behaviour settings. Values the widget or API pass at launch arrive in the +`last_event` variable, not as variables of their own. To use one, add a Code +step to that workflow that copies it from `last_event.payload` into a variable. + +The main agent takes a turn at launch, after the Initialization workflow, and +again each time a workflow returns to it. Put the greeting in one place: fixed +text in the Initialization workflow, or a main-agent rule such as "Greet only on +the first turn. After a workflow returns, carry on without greeting." Without +one, the agent greets at launch and again after every workflow. + Use returned IDs. Confirm write results, unresolved references, tool descriptions, and routing before reporting the draft ready. Do not run tools that contact -external services merely to inspect configuration. +external services merely to inspect configuration. Check a new or changed +function on test cases, as [functions](../voiceflow-functions/SKILL.md) describes, +before offering a conversation test. When runtime testing is authorized, compile the draft and check a fresh greeting and the job's completion response. Ask whether the requested action actually diff --git a/voiceflow/skills/voiceflow-debug/SKILL.md b/voiceflow/skills/voiceflow-debug/SKILL.md index 57a5f86..7644ef9 100644 --- a/voiceflow/skills/voiceflow-debug/SKILL.md +++ b/voiceflow/skills/voiceflow-debug/SKILL.md @@ -24,6 +24,28 @@ the same as the draft. Inspect the smallest useful evidence: current resource settings, routing, attachment bindings, and relevant trace sections. Classify the failure as instructions, retrieval, routing, state/wiring, integration, or runtime/version. +The [failure patterns](references/failure-patterns.md) list common causes and +their fixes. For many conversations at once, follow +[bulk analysis](references/bulk-analysis.md). + +Read only the part of a log you need. `voiceflow_transcript.query_logs` reads a +live conversation or a debug session by transcript ID, and +`voiceflow_test.query_run_logs` reads a test run by run ID. Filter by `levels` +(`["fatal", "error", "warn"]` for what went wrong), `turns`, `kinds`, +`debugKinds` (`tool`, `variables`, `llm`, `kb`), or `search`. `search` is plain +text, ignores case, and takes `|` between alternatives; it does not match the +whole prompts and knowledge chunks an entry carries. Each result also counts +entries per kind and level and reports token use per model, so a first query +with `limit: 1` shows what the log holds. CLI 0.287.0 has no command for log +queries; use `vf transcript get` there. + +A `voiceflow_conversation.send` result leaves out the knowledge chunks a search +returned and a playbook's whole instructions. To see the chunks, query the log +with `debugKinds: ["kb"]` and `detail: "full"`. In a transcript every input +starts a turn, the launch included, so the user's first message is turn 2. A +test run does not log its launch: its greeting is turn 0 and the user's first +message is turn 1. Find the turn the user means with `kinds: ["user"]` or a +`search` for their words, then read it with `turns`. For a tool failure, distinguish model selection from missing inputs, external HTTP failure, output shape, capture mapping, and dependent-call ordering. diff --git a/voiceflow/skills/voiceflow-debug/references/bulk-analysis.md b/voiceflow/skills/voiceflow-debug/references/bulk-analysis.md new file mode 100644 index 0000000..e395cc1 --- /dev/null +++ b/voiceflow/skills/voiceflow-debug/references/bulk-analysis.md @@ -0,0 +1,102 @@ +# Bulk transcript analysis + +How to find a pattern across many conversations, then prove it. Read this when +the user asks what goes wrong across a period, not in one conversation. + +## Load the project first + +Before pulling any transcript, read the agent's setup: the global prompt, the +main-agent instructions and routing, the playbooks, and their tools. Without it +you will misclassify the same failure many times over. + +## Pick the read + +- Find candidates with `voiceflow_transcript.search`. Narrow with the date + range, evaluation results and transcript properties before paging. Keep pages + small, about 20 rows, and page with `skip` while the result says there is + more. Each row already carries the transcript's evaluation scores. +- Look for a known marker, such as an error text or "transfer to a human", with + `voiceflow_transcript.query_logs` on each candidate: `search` for the text, + or `levels: ["fatal", "error", "warn"]` for failures. It returns only the + matching entries. +- Read whole conversations with `voiceflow_transcript.get`, only for the few you + deep-read. A voice call can be tens of thousands of tokens. +- When a read is cut or too long, read the turns that matter with + `voiceflow_transcript.query_logs` and `turns`, `levels` or `debugKinds`. + +Never read 20 or more transcripts in full. Search and filter first, then +deep-read the few that matter. + +## Triage before reading + +Skip the marker search when the complaint is vague, such as "quality feels +off", and start here. + +1. Take one page of search results for the period and sample. +2. Run evaluations only when the user asked for it, and only for transcripts + missing a score you need. One `voiceflow_evaluation.run` call takes several + evaluations and transcripts; batch them instead of looping. +3. Build a matrix of transcripts against evaluations: + + ```text + Transcript | Eval A | Eval B | Eval C | Fails + ---------- | ------ | ------ | ------ | ----- + T1 | FAIL | FAIL | PASS | 2 + T2 | PASS | PASS | PASS | 0 + T3 | FAIL | PASS | FAIL | 2 + ``` + +4. Sort by failures. Read the worst first. +5. Count the pass and fail rate per evaluation, for example "Escalation: 18 of + 28 failed (64%)". This alone often shows where the problem lives. + +## Deep-read three to five + +Pick for variety, because the first pattern you find is usually the most +obvious one, not the most important: + +- two or three of the worst performers; +- one passing transcript, to see what correct looks like; +- one edge case when there is room: unusual length, input, or mixed results. + +For each one, record: + +- the turn where the issue happened, by number; +- the agent's exact text, one or two sentences; +- what it should have done, citing the prompt or playbook rule; +- which evaluation failed and what it checks; +- whether it matches a pattern you already saw. + +## Cross-check + +Answer these before reporting: + +1. Do transcripts that fail one evaluation also fail another? Then they likely + share a cause. +2. Do low satisfaction scores overlap with one evaluation's failures? Give the + numbers, such as "4 of 5 low scores also failed escalation". +3. Are failures concentrated in one playbook? +4. How many distinct failure modes did you find? If only one, read more. +5. Is the agent at fault, or is the cause upstream, such as user confusion or a + knowledge gap, or downstream, such as a backend system? + +## Evidence standards + +- Frequency: "N of M transcripts (X%)", never "several" or "some". +- Examples: name at least three transcripts by ID for any pattern. +- Quotes: the exact agent text that shows the issue, without personal data. +- Fix: the exact text to add or change, and where, not "improve the prompt". +- Severity: why it ranks where it does, from frequency, user impact, and fix + effort. + +## Fix order + +When several issues exist, fix in this order: + +1. Safety and compliance, such as leaking sensitive data. +2. Made-up critical information. +3. Core function: broken tools or the main use case. +4. Experience: tone, length, flow. +5. Rare edge cases. + +The causes and fix templates are in [failure patterns](failure-patterns.md). diff --git a/voiceflow/skills/voiceflow-debug/references/failure-patterns.md b/voiceflow/skills/voiceflow-debug/references/failure-patterns.md new file mode 100644 index 0000000..9508000 --- /dev/null +++ b/voiceflow/skills/voiceflow-debug/references/failure-patterns.md @@ -0,0 +1,179 @@ +# Common failure patterns + +Recurring agent failures, how to confirm each one, and a fix template. Read this +after you have classified a failure and before you write the fix. + +Adapt each template to the agent's own wording and put it in the layer that +owns the behavior: global prompt, main-agent instructions, or one playbook. See +[prompting](../../voiceflow-prompting/SKILL.md). + +## 1. Made-up information + +Symptoms: invented order numbers, links, or facts; answers that do not match +what a tool returned. + +Check: was the tool called at all; did it return empty or an error; does the +prompt say what to do when data is missing? + +```markdown +## No made-up information + +Never make up information. If a tool returns nothing or fails: +1. Do not invent data. +2. Say you could not retrieve it. +3. Offer another way: try again, a transfer, or a manual lookup. +``` + +## 2. Tool not called when needed + +Symptoms: generic or guessed answers where the agent has a tool for real data. + +Check: the tool's attachment description must say both what it does and when +to call it. If the "when" lives only in one playbook's instructions, other +routes never see it. This is the most common cause of a skipped tool; see +[build](../../voiceflow-build/SKILL.md). Then check that the active playbook's +instructions also say when to call it. + +```markdown +## Required tool use + +When the user asks about [topic], call [tool name] first. Do not guess. +- Order status: call [order lookup tool] +- Account balance: call [balance tool] +``` + +## 3. Tool error not handled + +Symptoms: the agent carries on as if a failed call succeeded, or invents data +after an error. + +Check: what the tool returned in the log, whether the prompt covers errors, and +whether the function reports its errors on a path or output. + +```markdown +## Errors + +If a tool returns an error, nothing, or unexpected data: +1. Do not continue the normal flow. +2. Do not make up replacement data. +3. Say: "I'm having trouble retrieving that information." +4. Offer a transfer or to try again later. +``` + +## 4. Internal details leak + +Symptoms: the agent mentions a knowledge base, database, API, tool names, or +"checking our system". + +```markdown +## Silent tools + +Run tools without mentioning them. Never mention searches, tool names, API +calls, or routing. Present the result as your own answer. +``` + +## 5. Lost context + +Symptoms: the agent asks again for details the user gave, or forgets the +current task. + +Check: whether the details were saved to variables, whether the prompt says to +keep them, and whether a very long conversation is the cause. + +```markdown +## Context + +Keep everything the user told you in this conversation: name, details already +collected, and the current task. Never ask again for something already answered. +``` + +## 6. Wrong tone + +Symptoms: too formal or too casual, inconsistent, off-brand. + +```markdown +## Tone + +Use a [casual / professional] tone. +- Say: [phrases to use] +- Avoid: [phrases to avoid] +- Example: [one ideal reply] +``` + +## 7. Wrong length + +Symptoms: walls of text, or answers missing what the user needs. + +```markdown +## Length + +Keep replies [brief / standard / detailed], about [X to Y] words. +On voice, at most two sentences per turn. +Always include: [essentials]. Leave out: [extras]. +``` + +## 8. Voice problems + +Symptoms on voice agents: numbers read wrongly, several questions in one turn, +references to buttons or screens, poor recovery from misheard speech. See +[voice](../../voiceflow-voice/SKILL.md). + +```markdown +## Voice + +Ask one question per turn. +Say numbers in words: "twenty-five", "one hundred and fifty dollars". +Read phone numbers in groups: "five five five, one two three, four five six seven". +Never mention buttons, screens, or links to click. +``` + +## 9. Handoff problems + +Symptoms: transfers that should not happen, missing transfers, or an abrupt +handoff. + +```markdown +## Handoff + +Transfer to a person only when: +- [trigger 1] +- [trigger 2] +- the user asks for a person. +Do not transfer for: [cases the agent handles]. +Before transferring, say: "[handoff message]". +``` + +### Repeat contact is not a transfer loop + +Many turns and many transfer calls in one user's history often look like a +loop. Verify before you call it one: + +| Signal | Transfer loop (bug) | Repeat contact (not a bug) | +| -------------------------- | --------------------------- | ----------------------------- | +| Successful handoff events | None or very few | About one per transfer call | +| Timing of user messages | Seconds or minutes apart | Hours, days, or weeks apart | +| Session launches | Few or none | Many | +| External ticket or chat ID | None, or the same one again | Several different ones | + +The key check: compare transfer calls with successful handoff events. If they +match about one to one, the transfer works, and the user keeps returning because +the issue is not solved on the human side. Then ask why, whether the handoff +passes enough context, and whether returning users should be treated +differently. + +A real loop shows no successful handoff and rapid turns in one session. Look at +what runs after the transfer: a re-routing step that sends the user back to the +start, such as transfer, re-route, greet, triage, transfer. + +## 10. Call does not end + +Symptoms: the agent says goodbye but the call stays open, or the user hangs up. + +Check: whether the end-call tool is available to the agent, and whether the +prompt says when to use it. + +```markdown +## Ending the call + +After saying goodbye, end the call right away. Do not wait for a reply. +``` diff --git a/voiceflow/skills/voiceflow-environments/SKILL.md b/voiceflow/skills/voiceflow-environments/SKILL.md index 27d4126..2eb0489 100644 --- a/voiceflow/skills/voiceflow-environments/SKILL.md +++ b/voiceflow/skills/voiceflow-environments/SKILL.md @@ -18,6 +18,25 @@ project and environment from live data. An environment alias, environment ID, and version ID are different. Draft-editing operations use the alias required by their schema. Never silently choose Main because it is the default. +## Where to make a change + +Main is what live traffic hits. For a project with a live version, propose a +working environment for the change: a clone of Main named for the change in a +few words, such as Refund policy update. Clone only after the user agrees, +because a project has Main and up to nine more environments. Use one +environment for the whole change, not one per edit, and pass its alias on every +write. A clone copies Main's draft as it stands, unpublished edits included. + +For a new project with nothing live, edit Main's draft. When the user asks to +change Main directly, for example because they test only on the live build, +edit Main's draft and say once that each publish of Main puts the change live. + +With nine environments besides Main, another clone is refused. Ask which one +to retire and delete only the one the user names, because its draft goes with +it. An environment that still receives traffic needs its traffic moved first. + +## Drafts, clones, and tests + Each environment has draft and live content. A user can mean unpublished draft changes when asking to clone; inspect clone options and source version. Do not publish merely to make a clone include recent edits. @@ -37,6 +56,25 @@ Discarding or reverting content does not undo credential changes. A merge may preserve Main's secret values or override them depending on options; inspect the actual behavior before proceeding. +## Promote a change to Main + +A merge takes the source environment's published version, not its draft. So +publish the working environment, wait for the publish result, then merge it +into Main. The merge replaces Main's live version, so the change is live when +it finishes; do not publish Main afterwards. The merge can also delete the +working environment. Offer that, and keep the environment if the user wants it. + +The environment reads do not say when the working environment last took Main's +changes. The merge dialog on the Environments page warns when Main published +since then, because a merge would overwrite that release. When it warns, or the +user says Main changed, offer Pull from Main first, if the workspace has it. +Only the user can run it, from the Environments page. A pull brings Main's +changes into the working environment's draft only, so the order is: pull, +publish the working environment, then merge. A merge after a pull alone still +overwrites Main's newer release. + +## Authorization + Publish, merge, deletion, and traffic changes require scope from the user. Read the current diff/state and explain the concrete effect before an approval when authorization is missing. Protected environments may restrict these actions diff --git a/voiceflow/skills/voiceflow-evaluations/SKILL.md b/voiceflow/skills/voiceflow-evaluations/SKILL.md index 4eadf95..bace59e 100644 --- a/voiceflow/skills/voiceflow-evaluations/SKILL.md +++ b/voiceflow/skills/voiceflow-evaluations/SKILL.md @@ -42,6 +42,30 @@ test user/session with the draft version. Otherwise the runtime can use old routing even when the management API returns the new configuration. See the [compile sequence](../voiceflow/references/request-shapes.md#compile-before-a-draft-conversation-test). +To check a short exchange without saving a test, use +`voiceflow_conversation.run_script` with up to 10 user messages in order. It +starts a fresh conversation and returns each turn's replies, tool calls, +changed variables, and errors; pass `variables` to start as a known caller. It +does not compile: compile first after an edit, and when the result says +`needsRecompile: true`, compile and run it again. Use `voiceflow_conversation.send` +only when the next message depends on the reply. CLI 0.287.0 has no command for +the script run. A reply is not proof a tool worked; read the turn's tool calls. + +Before you save a test: + +- Fit the scenario in its turn limit: count the turns the happy path needs, + then add two. +- Do not script a goodbye before the turn you check, or the agent ends the + conversation and the check never runs. +- Check outcomes, such as the booking tool called with the right date, not the + route the agent took. +- Test a limit with real attempts, such as three wrong answers, not a message + saying "I failed three times". +- Seed a variable by its ID from `voiceflow_variable.list`; a name in its place + sets nothing. Check once in the run's log that the value reached the agent. +- When a test you wrote fails, check the test before the agent, and say so when + the mistake was yours. + Tests can invoke real tools and external services. Prefer fixtures or a safe environment when available, but do not claim an environment clone isolates external systems or shared KB documents. Explain any material side effect @@ -51,6 +75,22 @@ Poll run status with a bounded deadline; pending is not passed. Separate infrastructure errors from agent failures. Inspect traces to identify why an assertion failed instead of weakening the assertion until it passes. +Read a run with `voiceflow_test.get_run` about every 15 seconds, at most 20 +times. A run goes from idle through pending and running to passed, +failed, or canceled. If it is still running at the deadline, say so and offer +to stop it with `voiceflow_test.cancel_runs`: a run that has not started never +starts, and a finished run keeps its result. CLI 0.287.0 has no cancel command. + +When the agent under test can reach a tool that writes to an outside system, +list each finished run's tool calls with `voiceflow_test.query_run_logs` and +`debugKinds: ["tool"]`, and report every write-shaped call before pass or fail. +A page holds at most 50 entries or about 30,000 tokens; while `hasMore` is true, +call again with `offset` set to `nextOffset`. Then report passed checks of total +checks, the fail reason, and the failed checks with their reasons, and mention +the model credit cost of a large suite. For a failure's trace, query the failing +entries with `levels` or `turns`. A test run does not log its launch: the +greeting is turn 0 and the first user message is turn 1. + Report the cases executed, their outcomes, and remaining gaps. A test passing does not authorize publishing or merging. Evaluation resources can be shared across environments; inspect scope before changing a shared criterion. diff --git a/voiceflow/skills/voiceflow-functions/SKILL.md b/voiceflow/skills/voiceflow-functions/SKILL.md index 341cae3..874a6b8 100644 --- a/voiceflow/skills/voiceflow-functions/SKILL.md +++ b/voiceflow/skills/voiceflow-functions/SKILL.md @@ -26,6 +26,8 @@ Voiceflow's modified fetch exposes parsed response properties: `const data = (await fetch(url)).json`, not `response.json()`. Inspect the documented status and error shape; never log credentials or full customer payloads. Check current sandbox limits before large responses or custom traces. +For functions that show cards, carousels, or buttons, or end on a user's +choice, read [custom traces](references/custom-traces.md). Return outputVars with names matching declared outputs and supported scalar types. Serialize structured data deliberately. Declare every path returned as @@ -37,7 +39,26 @@ and paths, then create the attachment with a WHAT + WHEN description. Read [wiring](../voiceflow-wiring/SKILL.md) for defaults and captured results. Secrets arrive through declared inputs bound to secret references on the -attachment; never read `args.secrets`. Tests may call real services, so use +attachment; never read `args.secrets`. For the binding and its failure modes, +read [secrets](references/sensitive-inputs.md). Tests may call real services, so use fixtures first and only execute an integration with task authorization. +## Check a function + +Check your own function after each write, once the write's result is back, and +before you say it works. Write the cases first: a normal case; each limit at one +below, at, and one above (for "at most 3 items", send 2, 3 and 4); an empty +value; a wrong type; and `"0"`, which is what an unset variable sends. Decide +the expected path and outputs for each case, then run +`voiceflow_function.execute` once per case and compare `output.path` and +`output.outputVars` with them. Fix the code and check again. If cases still fail +after two rounds of fixes, stop and tell the user which ones fail and why: the +code or the expectation may be wrong. + +Run these checks only for self-contained code: no `fetch` or other outside +call, and literal input values only, because a `{NAME}` input value binds a +stored secret. Code that calls an outside service runs only when the user asks +for that run; say in one line what the call will do first. Otherwise test it in +a draft conversation. When `success` is false, the result's `error` says why. + Source: [Function tool](https://www.voiceflow.com/docs/documentation/build/tools/function-tool). diff --git a/voiceflow/skills/voiceflow-functions/references/custom-traces.md b/voiceflow/skills/voiceflow-functions/references/custom-traces.md new file mode 100644 index 0000000..5ca40a6 --- /dev/null +++ b/voiceflow/skills/voiceflow-functions/references/custom-traces.md @@ -0,0 +1,132 @@ +# Custom trace functions + +How a function sends a custom trace, such as a form or a voice transfer, and +waits for the user's answer. Read this before writing a function that a widget +extension or the voice runtime renders. + +## How they differ from a standard function + +A custom trace function returns a trace of its own type, which a chat widget +extension or the Twilio voice runtime renders. With +`next: { listen: true, to: [...] }` it pauses the conversation until the +extension sends an event back with `interact()`. The function does not run again +when the event arrives: the runtime follows the matching path on the canvas. + +- `next.listen: true` replaces `next.path`. +- `next.to` maps incoming events to canvas paths with match queries. +- `next.defaultTo` is the fallback path. +- Path names are your own, such as `submitted` or `cancelled`. +- The function only sends the trace and pauses. It does not process the answer. +- Every path needs steps on the canvas. A path with nothing after it ends the + conversation, and the runtime does not pause. + +## Example: a form that waits + +```javascript +export default async function main(args) { + const { form_title, fields } = args.inputVars || {}; + + let formFields; + try { + let parsed = fields; + if (typeof parsed === 'string' && parsed.trim()) parsed = JSON.parse(parsed); + if (parsed && !Array.isArray(parsed)) parsed = [parsed]; + formFields = parsed && parsed.length + ? parsed + : [ + { name: 'full_name', label: 'Full name', type: 'string', required: true }, + { name: 'email', label: 'Email address', type: 'email', required: true }, + ]; + } catch (err) { + return { + trace: [{ type: 'debug', payload: { message: 'Invalid fields JSON: ' + String(err?.message || err) } }], + next: { path: 'error' }, + }; + } + + return { + trace: [{ + type: 'form', + payload: { + formSlug: 'dynamic-form', + title: form_title || 'Contact information', + fields: formFields, + submitButtonText: 'Submit', + cancelButtonText: 'Cancel', + }, + }], + next: { + listen: true, + to: [ + { on: { 'event.payload.event.name': 'form_submitted' }, dest: 'submitted' }, + { on: { 'event.payload.event.name': 'form_cancelled' }, dest: 'cancelled' }, + ], + defaultTo: 'cancelled', + }, + }; +} +``` + +The input handling accepts a JSON string, a parsed array, or a single object, +because the same input can arrive in any of these shapes. + +`listen: true` pauses until an `interact()` event arrives. `listen: false` +carries on, but the events stay available for the session, which suits a +carousel the user might click later. + +## Fire-and-forget traces + +For a one-shot trace, such as a TwiML SIP transfer, leave out `next`: no paths, +no listening, no outputs. The trace is sent, and the conversation ends or the +voice runtime takes over. + +```javascript +export default async function main(args) { + return { + trace: [{ + type: 'twiml', + payload: ` + + + sip:agent@example.sip.twilio.com;transport=UDP + +`, + }], + }; +} +``` + +## Match the event to a path + +The widget extension sends an event like this: + +```javascript +window.voiceflow.chat.interact({ + type: 'event', + payload: { + event: { name: 'form_submitted' }, + payload: JSON.stringify({ full_name: 'Ada Example' }), + }, +}); +``` + +The runtime wraps that payload in its own event, so a `to` query matches the +full path `event.payload.event.name`. Querying `event.type` or `event.name` +never matches. Each `dest` must equal a canvas path name exactly. + +## Register the extension + +A chat widget extension goes inside `assistant: { extensions: [...] }` in +`chat.load()`. A top-level `extensions` is ignored without an error. + +```javascript +window.voiceflow.chat.load({ + verify: { projectID: 'PROJECT_ID' }, + url: 'https://general-runtime.voiceflow.com', + versionID: 'production', + assistant: { extensions: [FormExtension] }, +}); +``` + +For the extension itself, see the +[widget extension guide](https://www.voiceflow.com/docs/documentation/deploy/widget/web-chat-extensions). diff --git a/voiceflow/skills/voiceflow-functions/references/sensitive-inputs.md b/voiceflow/skills/voiceflow-functions/references/sensitive-inputs.md new file mode 100644 index 0000000..25868ad --- /dev/null +++ b/voiceflow/skills/voiceflow-functions/references/sensitive-inputs.md @@ -0,0 +1,95 @@ +# Reading secrets in a function + +How a function receives an API key or other credential, and how the binding +fails. Read this when a function needs a secret, or a secret input misbehaves. + +## Declare the secret as an input + +Functions do not have `args.secrets`. Reading it returns `undefined`, the +function takes its error path, and the agent then tends to make up an answer +around the empty result. + +Declare each secret as an input variable instead, beside the regular inputs, and +check that it arrived: + +```javascript +export default async function main(args) { + const { question, api_key } = args.inputVars || {}; + + if (!api_key) { + return { + outputVars: { success: false, error: 'missing api_key' }, + trace: [{ type: 'debug', payload: { message: 'api_key not provided; check the tool attachment' } }], + next: { path: 'error' }, + }; + } + + const response = await fetch('https://api.example.com/search', { + headers: { Authorization: api_key }, + }); + // ... +} +``` + +Give the input a description that says it is secret-bound, such as "API key for +Example. Filled from the secret EXAMPLE_API_KEY; do not change." + +## Bind it on the attachment + +On the tool attachment, `voiceflow_tool.create` or `voiceflow_tool.update`, set +the input's `defaultValue` to the secret's name in braces, `"{EXAMPLE_API_KEY}"`, +and leave `required` out. Key each input by its variable's UUID and repeat that +UUID in `functionInputVariableID`. The full body is in +[attach a function](../../voiceflow/references/request-shapes.md#attach-a-function-and-capture-its-results). + +At the start of a turn the runtime replaces the reference with the stored value. +The model never sees the value, because the input is not collected from it. + +## Why the secret input must not be collected + +If the model is asked to fill a secret input, it leaves it empty or invents +something that looks like a key. Both break the function. + +`required` controls this. Leave it out on a secret input: a default that holds a +secret makes it `false`, so the value is filled in and the model is never asked. +An input with any other default, or none, becomes `true`. Sending +`"required": true` with a secret default is rejected, because the two settings +contradict each other. `"required": false` is always accepted. + +In the Voiceflow UI, the Agent collect toggle is the same field: on means +`required: true`. Adding a default in the UI does not turn the toggle off. If a +user reports that the agent still asks for a secret input, check the toggle. +See [wiring](../../voiceflow-wiring/SKILL.md) for the other `required` rules. + +## Check the secret name + +`voiceflow_secret.list` takes the project only, because secrets belong to the +project. It returns each secret's `id`, `name` and `hasValue`, never the value. +Copy the `name` exactly. A mistyped name is saved as literal text, comes back in +the write result's `unresolvedReferences`, and the model is then asked for the +input. A value can differ per environment, and `voiceflow_secret.list_overrides` +reports which, but the name you bind stays the same. + +To create a secret or set its value, follow the +[CLI handoff](../../voiceflow/references/cli-handoffs.md#set-a-secret-value). +Never ask for a credential in the conversation. + +## Several secrets, several attachments + +A function that needs three secrets needs three inputs and three secret +defaults. Each attachment is independent: a function attached to three +playbooks needs the defaults set on all three attachments. + +## Check that it works + +To run the function yourself with a credential input, as when the user asks +"does it work with order 123?", pass the secret's name in braces as that input, +such as `{EXAMPLE_API_KEY}` or `Bearer {EXAMPLE_API_KEY}`, never the value. The +server fills it in, and the result shows `[secret]`. Any other `{name}` is +refused, because a single run has no conversation to read a variable from; pass +that value directly. The code really runs, so say what it will do outside +Voiceflow and get the user's go-ahead. + +In a conversation test, the log shows the function's inputs with the secret +masked as `[secret]`. The literal text `undefined`, or an empty string, means +the binding is wrong. diff --git a/voiceflow/skills/voiceflow-integrations/SKILL.md b/voiceflow/skills/voiceflow-integrations/SKILL.md index b80064f..b2b8e82 100644 --- a/voiceflow/skills/voiceflow-integrations/SKILL.md +++ b/voiceflow/skills/voiceflow-integrations/SKILL.md @@ -20,6 +20,17 @@ Read the [operation contract](../voiceflow/references/operations.md). Inspect available integrations and their schemas before writing a custom API/function. A connection is not an attached agent tool: verify both. +Attaching an integration tool takes two steps. First add it with +`voiceflow_integration.add_tool`, using a `toolName` that +`voiceflow_integration.get_schema` lists. It returns the tool's +`integrationToolID`; for a tool already added it returns the existing ID and +changes nothing. It refuses an integration the workspace does not have enabled; +say so and choose another kind of tool. Then attach it with +`voiceflow_tool.create`, type `integration`, and wire its inputs and outputs. +CLI 0.287.0 has no command for the first step; the user adds the tool in the +agent's Tools in the Voiceflow UI. The tool works only once the integration is +connected, so connect it first. + Use an existing integration when it supports the task; an API tool for a plain request; a function for logic; an MCP tool for a supported remote capability. Do not confuse the coding agent's Voiceflow MCP connection with an MCP server @@ -29,6 +40,12 @@ The user completes third-party OAuth or enters secret values in the secure Voiceflow interface. Never request credentials in conversation. If the public API cannot establish a connection, give the precise remaining UI step, then resume inspection after it exists. Don't invent a private connection helper. +The MCP refuses to carry credentials. Hand a secret value or an api-key +connection to the user as a `vf` command they run in their own terminal, and +send OAuth and app integrations to the Voiceflow UI, as the +[CLI handoffs](../voiceflow/references/cli-handoffs.md) describe. The same file +covers MCP server headers. A handed-over command is not a connection: confirm +it with a read afterwards. API-tool fields reference the tool's declared input variables. Bind project variables or secrets through defaults on each attachment, not by assuming a diff --git a/voiceflow/skills/voiceflow-knowledge/SKILL.md b/voiceflow/skills/voiceflow-knowledge/SKILL.md index 4a4bbd1..3fb3545 100644 --- a/voiceflow/skills/voiceflow-knowledge/SKILL.md +++ b/voiceflow/skills/voiceflow-knowledge/SKILL.md @@ -19,6 +19,13 @@ existing documents before importing duplicates. For MCP text import, use the [verified file shape](../voiceflow/references/request-shapes.md#import-a-text-document-over-mcp); the hosted tool can upload inline text without a local CLI or public URL. +To import a whole website, a section of one, or a sitemap.xml, use +`voiceflow_document.create_managed_sitemap`. It creates one sitemap document +that owns its pages and re-syncs them, and +`voiceflow_document.update_managed_sitemap` changes which pages it keeps and how +often it re-syncs. Never import a site by adding its pages one by one as URL +documents. + Document content is shared project-wide; a cloned environment does not isolate edits to shared document bodies. Environment membership and metadata may be scoped separately. Explain this blast radius before changing shared content @@ -38,5 +45,12 @@ Check stale duplicates and obsolete sources before adding more material. Deleting a document is a separate destructive operation; don't treat it as a routine refresh if an update is supported. +To change what a text or markdown document says, send its whole new text as +`content` to `voiceflow_document.update`. A document read returns its chunks, +not its source text, so never rebuild the text from them. Send `content` only +when you have the whole new text: from the user, or from a document of one +chunk without smart chunking. Otherwise ask the user for the full text. Do not +delete and re-create a document to change its text, because it gets a new ID. + Sources: [Importing data](https://www.voiceflow.com/docs/documentation/build/importing-data-sources), [Querying knowledge](https://www.voiceflow.com/docs/documentation/build/querying-the-knowledge-base). diff --git a/voiceflow/skills/voiceflow-messages/SKILL.md b/voiceflow/skills/voiceflow-messages/SKILL.md index 1d52cfe..7d8b1f0 100644 --- a/voiceflow/skills/voiceflow-messages/SKILL.md +++ b/voiceflow/skills/voiceflow-messages/SKILL.md @@ -33,4 +33,38 @@ Read back the referenced resource and owning step/tool after changes. Verify the intended path uses the edited resource, rather than creating an unused duplicate and claiming the user-facing text changed. +## Find text and owners + +Use `voiceflow_environment.search_text` to check an exact word, phrase, or +pattern across the project, or to find every holder of a message or prompt ID. +Use a targeted read when one can answer. `pattern` is literal text; add +`regex: true` for a regular expression, `wholeWord: true` to skip matches inside +longer words, or `resourceTypes: ["prompt"]` to find a prompt by its words. Each +hit names the resource, field, and line, and a hit on a message or prompt lists +in `usedBy` the steps that hold its ID. Page with `offset` set to `nextOffset`. +`incomplete: true` means the search did not read everything, and `notSearched` +names what it never reads, such as knowledge documents and transcripts. CLI +0.287.0 has no command for this search. + +Workflow steps hold message IDs too: Message and End steps, End actions on an +exit, and no-match and no-reply reprompts. Capture entity items and required +entities can hold one as well, and no read shows those; search for the ID. + +## Delete a message or prompt + +Find every owner first, and read every page of the search. If you cannot +establish every owner, or the search says `incomplete`, do not delete. Tell the +user what you found and what you could not check, and ask whether any entity +uses the message. Delete only after the user confirms that none does. + +Clear or repoint each owner before deleting the message. For an End step that +speaks, keep the End and clear its message with `voiceflow_workflow.update_step` +and `"content": {"messageID": null, "responseEnabled": false}`. Deleting the End +leaves the path with no ending, so do that only when the user asks. + +Deleting a prompt does not check owners: an owner keeps the ID and sends nothing +at runtime. A message variant can hold a prompt ID in a condition, and every +read, the search included, shows that condition as `null`. So no read proves a +prompt is unused. Say so, and get the user's confirmation before deleting it. + Source: [Instructions](https://www.voiceflow.com/docs/documentation/build/instructions). diff --git a/voiceflow/skills/voiceflow-phone-numbers/LICENSE b/voiceflow/skills/voiceflow-phone-numbers/LICENSE new file mode 100644 index 0000000..68fdac6 --- /dev/null +++ b/voiceflow/skills/voiceflow-phone-numbers/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Voiceflow, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/voiceflow/skills/voiceflow-phone-numbers/SKILL.md b/voiceflow/skills/voiceflow-phone-numbers/SKILL.md new file mode 100644 index 0000000..be2a8b2 --- /dev/null +++ b/voiceflow/skills/voiceflow-phone-numbers/SKILL.md @@ -0,0 +1,84 @@ +--- +name: voiceflow-phone-numbers +description: Give a Voiceflow voice or chat agent a phone number for calls or texts, and move, disconnect, rename, or delete numbers. Use when the user wants an agent reachable by phone. +license: MIT +--- + +# Phone numbers + +For questions about phone providers, calls, texts, or number setup that this +skill does not answer, follow the +[docs MCP workflow](../voiceflow/references/documentation.md) before answering +or implementing. For every new Voiceflow troubleshooting issue, consult it even +when this skill suggests a fix. Include package/skill/stage context in `why` as +described there. Reuse guidance for the same issue; consult it whenever unsure. + +Read the [operation contract](../voiceflow/references/operations.md). + +A phone number belongs to the workspace, not to one agent. It has two sides: +the `voice` side takes calls and the `sms` side takes texts. Each side reaches +one project or nothing, and assigning a side is what connects it. + +## Which side the agent uses + +The project read does not say whether an agent is for voice or chat, so ask +whether it takes phone calls or texts. + +- A voice agent takes calls on the `voice` side. +- A chat agent takes texts on the `sms` side. Texts need a Twilio number. A + Voiceflow number, provider `voiceflow-telnyx`, never takes texts, and a chat + agent never takes calls. + +The server refuses a side that does not fit the number or the project. + +## Add a number + +In the Voiceflow UI, open the agent and select Phone numbers in the sidebar, +under Deploy. For a chat agent the item is called SMS. + +First look for a number the workspace already holds: +`voiceflow_phone_number.list` with the `workspaceID`. If a free one fits, assign +it. The MCP refuses to create a number, because that spends money or takes +carrier credentials. So hand the step to the user, as the +[phone number handoff](../voiceflow/references/cli-handoffs.md#add-a-phone-number) +describes: connect a number they hold with `vf phone-number create`, or add it +in the UI. Never ask for carrier credentials in the conversation. + +Buying a new number charges the workspace. Offer it only when the user asks to +buy one and accepts the cost, and hand over the command with `--dry-run` first. +Ask the user to say when the number exists. Then find it with +`voiceflow_phone_number.list` and connect it with +`voiceflow_phone_number.assign`. A handed-over command is not a connected +number; confirm it with a read. + +## Change a number + +Each operation takes `phoneNumberID`, the number's ID rather than the digits, +and `workspaceID`. Find the ID with `voiceflow_phone_number.list`; each entry +shows the project its `voice` and `sms` sides reach. + +- Connect or move a side with `voiceflow_phone_number.assign`, giving `target` + and `projectID`. Leave out `environmentAlias` unless the user names an + environment. With no environment, calls and texts reach the published version + and follow the traffic split. To try a change on a real call before + publishing, send `environmentAlias` with `version: "draft"`, and switch back + afterwards. +- Disconnect with `voiceflow_phone_number.unassign` and `target`. Leave out + `target` to clear both sides. Clearing a side that is not connected is + refused. +- Rename with `voiceflow_phone_number.update` and `name`. The name is a label in + the workspace; callers never see it. +- Delete with `voiceflow_phone_number.delete` only when the user says to delete + the number. It stops answering at once. + +Moving or disconnecting a side that answers for an agent, and deleting a number +that does, changes who gets real calls or texts. Say which agent gains or loses +them and get the user's go-ahead first. After a move, say that the old agent no +longer gets those calls or texts. When a number reaches the published version, +say that unpublished changes are not live on it yet. + +Use [voice](../voiceflow-voice/SKILL.md) for how the agent speaks on a call, and +[environments](../voiceflow-environments/SKILL.md) for publishing. + +Sources: [Connect a phone number](https://www.voiceflow.com/docs/documentation/deploy/phone/connect-a-phone-number), +[Inbound SMS](https://www.voiceflow.com/docs/documentation/deploy/sms/inbound). diff --git a/voiceflow/skills/voiceflow-transcripts/SKILL.md b/voiceflow/skills/voiceflow-transcripts/SKILL.md index b981647..1a33f4b 100644 --- a/voiceflow/skills/voiceflow-transcripts/SKILL.md +++ b/voiceflow/skills/voiceflow-transcripts/SKILL.md @@ -29,6 +29,17 @@ Distinguish counts over the whole requested population from observations in a sample. Do not generalize a few hand-picked failures into a measured failure rate. Paginate intentionally and state coverage when limits prevent a complete review. +When you need certain log entries rather than a whole conversation, use +`voiceflow_transcript.query_logs`: what went wrong +(`levels: ["fatal", "error", "warn"]`), one turn (`turns: [3]`), tool calls or +variable changes (`debugKinds: ["tool"]` or `["variables"]`), or plain text +(`search`, ignoring case, with `|` between alternatives). Page with `offset`. +Every result also counts the whole log's entries and reports its token use per +model. Every input starts a turn, the launch included, so the user's first +message is usually turn 2: find the turn the user means with `kinds: ["user"]` +or `search` before reading it with `turns`. CLI 0.287.0 has no command for this; +use `vf transcript get` there. + For debugging, inspect the smallest relevant tool request/result and associated state changes. Do not assume a current draft matches the version that generated an older transcript. Use [debug](../voiceflow-debug/SKILL.md) for fixes and diff --git a/voiceflow/skills/voiceflow-voice/SKILL.md b/voiceflow/skills/voiceflow-voice/SKILL.md index 637cf28..749b000 100644 --- a/voiceflow/skills/voiceflow-voice/SKILL.md +++ b/voiceflow/skills/voiceflow-voice/SKILL.md @@ -20,7 +20,26 @@ provider values from live schemas; never invent a voice ID. Prefer short spoken turns, one question at a time, and explicit confirmation for ambiguous names, dates, amounts, and identifiers. Do not read Markdown, URLs, or long lists verbatim. Preserve already-known context instead of restarting the -conversation after routing. +conversation after routing. The voice reads the same text a chat would show, so +HTML, Markdown, or symbols meant for a screen in a reply are spoken aloud. + +## Pronunciation + +When the voice misreads a word, name, or code, prefer an entry in the +pronunciation dictionary over a prompt rule: the dictionary acts on the spoken +text itself. Each entry maps a written word, `from`, to how the voice says it, +`to`; an empty `to` makes the voice skip the word. It changes what the voice +says, not the text, and holds at most 500 entries. Words match ignoring case, +so `Dr.` and `dr.` are one entry. + +Read the dictionary from the agent read, under `voice.pronunciationDictionary`. +Change it with `voiceflow_agent.patch_pronunciation`: `set` adds a word or +changes how it is said, and `remove` deletes words. Send only the words that +change. Never send the whole dictionary through an agent update to change a few +words: that replaces every entry and loses changes made since your read. CLI +0.287.0 has no command for the patch; the UI step is Voice output in the +agent's Behaviour settings. A change reaches calls only after the environment +is compiled. Separate prompt problems from transcription, endpointing, interruption, silence, and telephony configuration. Use a specific transcript/trace to identify which @@ -33,6 +52,8 @@ placing calls. Custom traces or transfer payloads require the documented runtime contract; do not assume a chat trace works in telephony. Use [functions](../voiceflow-functions/SKILL.md) for custom transfer logic and -[evaluations](../voiceflow-evaluations/SKILL.md) for scoped voice testing. +[evaluations](../voiceflow-evaluations/SKILL.md) for scoped voice testing. To +give the agent a phone number, or to move or disconnect one, use +[phone numbers](../voiceflow-phone-numbers/SKILL.md). Source: [Voiceflow documentation](https://www.voiceflow.com/docs). diff --git a/voiceflow/skills/voiceflow-wiring/SKILL.md b/voiceflow/skills/voiceflow-wiring/SKILL.md index 0eb2f77..265de11 100644 --- a/voiceflow/skills/voiceflow-wiring/SKILL.md +++ b/voiceflow/skills/voiceflow-wiring/SKILL.md @@ -40,8 +40,43 @@ bindings; changing one does not configure every playbook using the same tool. Order dependent tool calls. Do not assume two calls in one parallel batch see each other's captured writes. Re-read after UI edits if captures appear lost. +An attachment update changes only the fields you send. `inputVariables`, +`captureResponse` and `captureInputVariables` merge by key: send only the +entries you change, and `null` for an entry to remove it. Any other field you +send replaces the stored one. To drop a phantom input keyed by a name, send +that key as `null`; leaving it out keeps it. A `required: false` input also +needs a `defaultValue`, or the run fails with a missing input value. + +## Progress and counters + +A playbook cannot set a variable. Only a tool's captured response, a +`captureInputVariables` entry, or a workflow Set step writes one, so an +instruction such as "add the answer to {evidence}" does nothing. To track +progress, use one function per event: + +- The model sends only the new event as one `required: true` input, never the + running totals. +- Each stored value comes in as its own `required: false` input with a default + such as `{attempts}`, so the model never sees or retypes it. +- The code returns the new value, such as a count plus one or a JSON list with + the new item, as a string or number, and `captureResponse` writes it back to + the same variable. +- A gate, such as a routing rule or exit condition, reads the stored value, + for example `attempts` at 3 or more, never a flag the model sets. + +Treat `""`, `"0"`, and invalid JSON as empty, because an unset variable arrives +as `0`. Call the function once per turn: two calls in one batch both read the +value from the start of the turn, so one update is lost. A tool that takes the +whole stored list from the model loses everything on one blank argument. + +## Check the wiring + Prefer the smallest relevant inspection over sweeping the whole project. Verify UUIDs, WHAT + WHEN descriptions, references, captures, and route coverage before -running anything. +running anything. For a full check, list each agent's and playbook's tools, +read their attachments, and list the project variables. Compare input defaults +and captures with the declared variables, and check `unresolvedReferences` in +write results. Proven designs are in [patterns](references/patterns.md); common +mistakes and their fixes are in [anti-patterns](references/anti-patterns.md). Source: [References](https://www.voiceflow.com/docs/documentation/build/editor/references). diff --git a/voiceflow/skills/voiceflow-wiring/references/anti-patterns.md b/voiceflow/skills/voiceflow-wiring/references/anti-patterns.md new file mode 100644 index 0000000..de65fc4 --- /dev/null +++ b/voiceflow/skills/voiceflow-wiring/references/anti-patterns.md @@ -0,0 +1,130 @@ +# Wiring anti-patterns + +Wiring mistakes that look fine in the editor and fail at runtime, with the fix +for each. Read this when a tool gets empty, wrong, or invented values. + +## 1. A default from a variable nothing writes + +An input defaults from `{account_id}`, but no tool or step ever writes that +variable. Every call gets an empty value and the function fails. + +Fix: find the function whose response carries the value, add an output, and +capture it into the variable on that function's attachment. + +## 2. Collected input for a value with a known source + +An `orderId` input is `required: true` with the default `{order_id}`. The model +is asked to fill it, and sometimes invents an ID, leaves it out, or gets it +right. + +Fix: set `required: false`, so the default is always used. Only do this when a +writer sets the variable on every path to the tool; otherwise see 8. + +## 3. A function returns nothing a later tool needs + +A refund tool takes `refundTotal`, but the tool before it does not return the +amount, so the model has to carry the value through the conversation. That is +fragile. + +Fix: add the amount to the first function's outputs, capture it into a project +variable such as `refund_amount`, and default the later input to +`{refund_amount}` with `required: false`. + +## 4. `functionInputVariableID` holds a name + +Some older create flows left `functionInputVariableID` set to the input's name, +such as `"token"`, instead of its UUID. The editor may still show it correctly, +but the wiring is broken. + +Fix: when you update the attachment, repair these IDs in the same update. Do +not carry broken state forward. + +## 5. Phantom inputs keyed by name + +```jsonc +"inputVariables": { + "INPUT_UUID": { "functionInputVariableID": "INPUT_UUID" }, + "token": { "description": null } +} +``` + +The map key must be the declared variable's UUID. The editor looks inputs up by +key, and cloning or importing the agent fails on a name key. Remove the +name-keyed entry, which usually has `description: null`, by sending its key as +`null` in the update's `inputVariables`. Leaving it out of the update keeps it. +Never remove the UUID-keyed entry. + +## 6. Prompt fixes for a wiring problem + +When a function gets bad arguments, adding more examples to the prompt rarely +helps, because the cause is an empty default, not the model's reasoning. + +Fix: inspect the wiring before changing the prompt. If the upstream variable is +never set, capture it. If the input has nothing to choose, change `required`. + +## 7. Reading `args.secrets` in function code + +```javascript +const apiKey = args.secrets?.MY_API_KEY || ''; +``` + +`args.secrets` does not exist in functions, so `apiKey` is always empty. The +function takes its error path and the model makes up an answer. + +Fix: declare `api_key` as an input, read `args.inputVars.api_key`, and on the +attachment set that input's default to `"{MY_API_KEY}"`, keyed by its UUID, +with `required` left out. See +[secrets](../../voiceflow-functions/references/sensitive-inputs.md). + +## 8. `required: false` without a writer on every path + +The mirror of 2, and more dangerous, because it fails silently. An input is +switched to `required: false` because the variable has a known source, but on +some path nothing writes the variable before the tool runs. + +For example, a follow-up tool's name and zip inputs were set to +`required: false`. For returning callers a lookup captured those values. For +new callers only a placeholder such as "Unknown zip" was set at launch, so every +new caller's request was sent with it and rejected. The agent never asked for +the zip, because turning collection off removes the question; it adds no +fallback. + +Fix: before switching an input to `required: false`, find what writes the +variable, a captured response or a workflow Set step, and check that it runs on +every path to this tool. If a path has none, keep `required: true` so the agent +asks, or add the missing writer. Treat placeholders set at launch as unset. + +## 9. A secret or project variable in an API tool's URL or headers + +Symptom: the editor shows a field pointing at a variable that does not exist, +often a bare ID in braces. At runtime the host is malformed or the auth header +is empty, and the call fails with 401, 403, or a DNS error. Nothing reports a +problem at build time. + +Cause: an API tool's `url`, `headers`, and `body` resolve only against that +tool's own declared inputs. Project variables, entities, and secrets are not in +scope, so a reference to them becomes empty. + +Fix: declare an input on the API tool, reference it from the URL or header, then +bind the secret on every attachment: + +```jsonc +{ + "inputVariables": { + "INPUT_UUID": { + "apiToolInputVariableID": "INPUT_UUID", + "description": "Bearer token. Filled from the secret EXAMPLE_TOKEN.", + "defaultValue": "{EXAMPLE_TOKEN}" + } + } +} +``` + +Leave `required` out: a secret default makes it `false`, so the runtime fills +in the value and the model never sees it. Copy the secret's name exactly from +`voiceflow_secret.list`. A mistyped name is saved as literal text, appears in +`unresolvedReferences`, and the model is then asked for the input. + +Never fix a failing secret reference by putting the literal value back in the +URL. The user chose to keep that credential in a secret; a failing binding is a +bug to diagnose. diff --git a/voiceflow/skills/voiceflow-wiring/references/patterns.md b/voiceflow/skills/voiceflow-wiring/references/patterns.md new file mode 100644 index 0000000..d38f7dc --- /dev/null +++ b/voiceflow/skills/voiceflow-wiring/references/patterns.md @@ -0,0 +1,81 @@ +# Wiring patterns + +Four proven ways to wire a function's inputs and outputs. Read this when you +design a new tool attachment or decide how a value should reach project state. + +Every map is keyed by the function variable's UUID. A `defaultValue` names a +project variable in braces; `variableOrEntityID` takes the variable's ID. The +full attachment body is in +[attach a function](../../voiceflow/references/request-shapes.md#attach-a-function-and-capture-its-results). + +## 1. Read-only lookup + +The function reads from an API and returns data, such as `getOrderStatus`. + +- Inputs: usually an ID with a known source, plus authentication. +- Make inputs `required: false` with defaults from project variables, but only + when something writes each variable on every path to the tool. +- Capture outputs into project variables when a later tool uses them. At least + capture the status values the agent branches on. + +```jsonc +{ + "inputVariables": { + "": { + "functionInputVariableID": "", + "defaultValue": "{order_id}", + "required": false + }, + "": { + "functionInputVariableID": "", + "defaultValue": "{session_token}", + "required": false + } + }, + "captureResponse": { + "": { + "functionOutputVariableID": "", + "variableOrEntityID": "" + } + } +} +``` + +## 2. State change + +The function writes somewhere else, such as a ticket or a backend record. + +- Inputs: a mix of what the model supplies, such as the ticket text, and values + filled from project variables, such as IDs and authentication. +- Outputs: return the values the function accepted, even when they came in as + inputs, so `captureResponse` can make the effect visible in project state. + +```javascript +return { + next: { path: 'success' }, + outputVars: { + orderId: acceptedOrderId, // echo the input so the capture stores it + ticketId: createdTicketId, // new value from the API + }, +}; +``` + +## 3. Side effect only + +The function does something, but its result is not needed elsewhere. + +- Outputs and captures are usually empty. +- Inputs are `required: false` with defaults from project state, under the same + rule about a writer on every path. + +To keep an argument the model chose, such as a category, add a +`captureInputVariables` entry on the attachment: +`{"": {"variableOrEntityID": ""}}`. + +## 4. The model's decision is the data + +The model's choice is the value, with no other source: a ticket subject, a +dispute reason, a picked time slot. + +- Input: `required: true`, with no default. +- Return it as an output too, so `captureResponse` stores it for later tools. diff --git a/voiceflow/skills/voiceflow-workflows/SKILL.md b/voiceflow/skills/voiceflow-workflows/SKILL.md index ccadafb..0d23acc 100644 --- a/voiceflow/skills/voiceflow-workflows/SKILL.md +++ b/voiceflow/skills/voiceflow-workflows/SKILL.md @@ -43,6 +43,55 @@ Messages and prompts are separate resources in relevant step schemas. Create or reuse them through their supported operations; don't insert a guessed text field into a step payload. +## Reading + +`voiceflow_workflow.get` and `voiceflow_workflow.get_step` answer most +questions. `voiceflow_workflow.get_raw` returns the stored canvas nodes with +their coordinates; it is long and unprocessed. Use it only when the other reads +cannot answer, such as where blocks sit, send `blockIDs` for the blocks you +need, and never plan an edit from it. To find where a word, phrase, or ID is +used across the project, see [messages](../voiceflow-messages/SKILL.md#find-text-and-owners). + +## Editing + +- To say something and end, use a Message step with its own message, then an + End step. Never give a new End step a message. +- When the user wants a Message step's words gone, delete the step; do not + keep it silent. A speaking End step is the exception, because deleting it + leaves the path with no ending: keep it and send + `"content": {"messageID": null, "responseEnabled": false}` with + `voiceflow_workflow.update_step`. An update keeps every field you leave out. +- `voiceflow_workflow.add_blocks` adds up to 20 blocks with their steps, links + between them, and `from` ports that link existing ports to them. Create their + messages first, in one batch. `voiceflow_workflow.link_many` sets up to 50 + links in one write. +- To change an exit action's kind, use `voiceflow_workflow.replace_action`. It + keeps the action's place in one write. Deleting and re-adding leaves the path + running without the action in between. +- Arrange blocks with `voiceflow_workflow.arrange_blocks` only when the user asks + to tidy the canvas. Pass `blockIDs` when they mean some blocks; without them + every block moves, including ones the user placed by hand. +- `voiceflow_workflow.duplicate` copies a workflow with its own messages, + prompts, and playbooks, and maps each original ID to the copy's. Edit the copy + through those IDs. To route to the copy, send every workflow already in the + agent's `workflows` list plus the copy; the copy alone replaces the list. + +CLI 0.287.0 has no commands for the raw read, adding several blocks, several +links, replacing an action, arranging, or duplicating; use the single-item +commands there. + +## Workflow tools + +When the user asks for a workflow tool rather than a routing entry, attach it +with `voiceflow_tool.create`, type `workflow`, `target: {"global": true}`, the +`workflowID`, and a `description` that says when to run it. The main agent and +every playbook can then call it, so add no routing rule. Never send a +`playbookID`: playbooks do not take a workflow tool of their own. Without a +description the tool falls back to the workflow's own, which rarely says when. +Do not name the workflow in instructions either: a workflow name there resolves +only for workflows in the agent's `workflows` list. To undo it, delete the tool +with the ID the create returned. + Verification starts with read-back topology and reference checks. A runtime test can invoke real integrations; scope it with [evaluations](../voiceflow-evaluations/SKILL.md) only when the user requested testing. diff --git a/voiceflow/skills/voiceflow/SKILL.md b/voiceflow/skills/voiceflow/SKILL.md index a7e212a..2bc40ca 100644 --- a/voiceflow/skills/voiceflow/SKILL.md +++ b/voiceflow/skills/voiceflow/SKILL.md @@ -31,6 +31,7 @@ only the specialist relevant to the task: | Import and query documents | [Knowledge](../voiceflow-knowledge/SKILL.md) | | Edit reusable messages and prompts | [Messages](../voiceflow-messages/SKILL.md) | | Configure spoken conversations | [Voice](../voiceflow-voice/SKILL.md) | +| Give an agent a phone number for calls or texts | [Phone numbers](../voiceflow-phone-numbers/SKILL.md) | | Install, customize, or troubleshoot a website widget | [Widget](../voiceflow-widget/SKILL.md) | | Diagnose behavior | [Debug](../voiceflow-debug/SKILL.md) | | Review customer conversations | [Transcripts](../voiceflow-transcripts/SKILL.md) | diff --git a/voiceflow/skills/voiceflow/references/cli-handoffs.md b/voiceflow/skills/voiceflow/references/cli-handoffs.md new file mode 100644 index 0000000..a2f7c47 --- /dev/null +++ b/voiceflow/skills/voiceflow/references/cli-handoffs.md @@ -0,0 +1,163 @@ +# Hand a refused action to the user + +The Voiceflow MCP lists a few operations that it then refuses, because they +carry a credential or spend money. The refusal comes with guidance; it is not +a permission problem to retry. Prepare the exact command, let the user run it +in their own terminal, then continue with reads. + +| Action | Why the MCP refuses it | Handoff | +| ----------------------------------- | --------------------------------------------- | ---------------------------------------------------- | +| Set a secret value | The value would pass through the conversation | [CLI](#set-a-secret-value) | +| Connect an api-key integration | Same | [CLI](#connect-an-api-key-integration) | +| Connect an OAuth or app integration | Needs a browser sign-in | [Voiceflow UI](#connect-an-oauth-or-app-integration) | +| Add a phone number | Spends money or takes carrier credentials | [CLI or UI](#add-a-phone-number) | +| MCP server header as literal text | A header value can be a credential | [Secret reference or CLI](#mcp-server-headers) | + +## Ground rules + +- Never ask for a secret value, API key, token, or carrier credential in the + conversation. Never run these commands yourself: you would need the value. +- If the user pastes a credential anyway, do not use or repeat it. Tell them it + is now in the conversation history and should be rotated, then hand over. +- Fill in IDs from live reads. Capitalized placeholders such as `PROJECT_ID` + are yours to replace; variables such as `"$VF_SECRET_VALUE"` stay as written. +- The user needs the Voiceflow CLI signed in to the same workspace. If they do + not have it, point them to [setup](../../voiceflow-setup/SKILL.md) or the UI step. +- Afterwards, confirm with a read. Handing over a command is not a success. + +## Read a value without showing it + +In bash or zsh, the value is typed without echo and stays out of shell history: + +```sh +printf 'Secret value: '; read -rs VF_SECRET_VALUE; printf '\n' +``` + +In PowerShell: + +```powershell +$secure = Read-Host 'Secret value' -AsSecureString +$VF_SECRET_VALUE = [System.Net.NetworkCredential]::new('', $secure).Password +``` + +The [operation contract](operations.md) keeps secrets out of command arguments. +These handoffs keep the value out of the conversation and shell history, and +the JSON ones pipe it in. A value passed as an argument, as with `--value`, is +still visible in the process list to other users of the same computer while +the command runs. Clear the variable after use: `unset VF_SECRET_VALUE`, or +`Remove-Variable VF_SECRET_VALUE, secure`. +Values that contain a double quote or a backslash do not fit the JSON +templates below; send the user to the Voiceflow UI for those. + +## Set a secret value + +Create the secret first if it does not exist: `voiceflow_secret.create` with a +`name` only. Never send `defaultValue`; a create that carries a value is +refused. The result gives the secret ID. Then hand over: + +```sh +printf 'Value for ORDERS_API_KEY: '; read -rs VF_SECRET_VALUE; printf '\n' +vf secret set-value --secret-id SECRET_ID --project-id PROJECT_ID --value "$VF_SECRET_VALUE" +unset VF_SECRET_VALUE +``` + +```powershell +vf secret set-value --secret-id SECRET_ID --project-id PROJECT_ID --value $VF_SECRET_VALUE +``` + +Without environment flags the value goes to the `main` environment's published +version, which serves live traffic; `--version-variant draft` alone sets the +`main` draft. To set another environment, add +`--environment-alias ALIAS --version-variant draft` (or `published`). Each +version keeps its own value, and publishing does not copy a draft value, so set +every version the tool runs in. The UI step is Agent settings → Secrets. + +Confirm with the read that matches the write; neither returns the value. After +a write with `--environment-alias` or `--version-variant draft`, read +`voiceflow_secret.list_overrides`: it lists the secret's ID under that +environment and version. `voiceflow_secret.list` reports `hasValue` for the +project-level value only, which a write without environment flags sets. + +## Connect an api-key integration + +`voiceflow_integration.list` reports each integration's `connectionType` and +`connectableViaAPI`. For `api-key`, read the connect operation's schema for this +integration's `credentials` shape: a string for some, an object for others. +Build the body with one variable per secret field, then pipe it in. A piped body +stays out of the process list. + +```sh +printf 'API key: '; read -rs VF_CREDENTIAL; printf '\n' +printf '{"integration":"INTEGRATION","credentials":"%s"}' "$VF_CREDENTIAL" | vf integration connect --integration INTEGRATION --project-id PROJECT_ID +unset VF_CREDENTIAL +``` + +```powershell +$body = @{ integration = 'INTEGRATION'; credentials = $VF_CREDENTIAL } | ConvertTo-Json -Compress +$body | vf integration connect --integration INTEGRATION --project-id PROJECT_ID +``` + +The UI step is Agent settings → Integrations. Confirm with +`voiceflow_integration.list`: the integration reports `connected: true`. + +## Connect an OAuth or app integration + +These connect only through a browser sign-in. Tell the user: in Voiceflow, open +Agent settings → Integrations, choose the integration, and select Connect; a tab +opens at the provider. When they are done, read `voiceflow_integration.list` +again and check `connected`. A connection is not yet an agent tool. + +## Add a phone number + +The UI step: open the agent in Voiceflow and select Phone numbers in the +sidebar, under Deploy, then Create number. For a chat agent the item is called +SMS. + +To connect a number the user already holds with the CLI, read the operation's +schema for their carrier's fields. A Twilio number, for example: + +```sh +printf 'Twilio account SID: '; read -r VF_TWILIO_SID +printf 'Twilio auth token: '; read -rs VF_TWILIO_TOKEN; printf '\n' +printf '{"workspaceID":"WORKSPACE_ID","provider":"twilio","name":"Support line","value":"+15555550100","twilioAccountSID":"%s","twilioAuthToken":"%s"}' "$VF_TWILIO_SID" "$VF_TWILIO_TOKEN" | vf phone-number create +unset VF_TWILIO_SID VF_TWILIO_TOKEN +``` + +Buying a new number through Voiceflow charges the workspace. Offer it only when +the user asks to buy one and accepts the cost. Hand over the command with +`--dry-run` first, so the user can check the request it prints, then the same +command without that flag: + +```sh +vf phone-number create --dry-run --stable-phone-number-create-request '{"workspaceID":"WORKSPACE_ID","provider":"voiceflow-telnyx","country":"US","areaCode":415,"name":"Support line"}' +``` + +Then find the number with `voiceflow_phone_number.list` and connect it with +`voiceflow_phone_number.assign`. Ask the user whether the agent takes calls or +texts: the project read does not say, and the server refuses a side that does +not fit the number or the project. Assigning, moving, renaming, and deleting a +number work over the MCP. + +## MCP server headers + +The MCP refuses a header value that is literal text with no `{name}` reference. +For a credential header, store the credential in a secret (see +[above](#set-a-secret-value)) and send the header value as `{ORDERS_API_KEY}`, +or `Bearer {ORDERS_API_KEY}`, with `voiceflow_mcp_server.create`, or with +`voiceflow_mcp_server.update` on a server that has no headers yet. + +A `headers` list replaces every header the server has. A read returns each +header's key but `[redacted]` in place of every value, secret references +included, and an update that sends `[redacted]` back is refused. So you cannot +keep the existing headers yourself. Never send a partial list: the headers you +leave out are deleted. To change only the name or URL, leave `headers` out. + +To add or change a header on a server that already has headers, or to add one +that is not a credential, such as a region, send the user to the Voiceflow UI: +open the agent, select Settings in the sidebar, then MCP servers. Or have the +user re-enter every header, old and new, in this command. A credential stays a +`{name}` reference to a secret, never literal text: + +```sh +vf mcp-server update --server-id SERVER_ID --project-id PROJECT_ID --environment-alias ALIAS --headers '[{"key":"Authorization","value":"Bearer {ORDERS_API_KEY}"},{"key":"X-Region","value":"eu-west"}]' +``` diff --git a/voiceflow/skills/voiceflow/references/operations.md b/voiceflow/skills/voiceflow/references/operations.md index 1c5db2e..4cae4b1 100644 --- a/voiceflow/skills/voiceflow/references/operations.md +++ b/voiceflow/skills/voiceflow/references/operations.md @@ -8,7 +8,11 @@ beyond the user's task. Infer the project/environment only from reliable task context or live reads. Ask for the missing choice if ambiguous. Never pick the first workspace or project automatically. Do not require a new environment for every trivial edit; -use the user's intended draft and explain material shared/live effects. +use the user's intended draft and explain material shared/live effects. For a +project with a live version, propose a working environment for the change and +clone only after the user agrees, as +[Environments](../../voiceflow-environments/SKILL.md#where-to-make-a-change) +describes. Proposing is not requiring: the user can choose Main's draft. For every new troubleshooting issue, even if a skill suggests a fix, and for other product questions the relevant skill does not answer, use the