feat(check): fail closed on tools and servers in inline check payloads - #62
Open
scott-lowe-vapi wants to merge 1 commit into
Open
scott-lowe-vapi wants to merge 1 commit into
scott-lowe-vapi wants to merge 1 commit into
Conversation
A PR check runs the branch's agents in a real org, so nothing it sends may reach a real server by default. The payload builder's last pass now: - classifies every tool at a handled position (model.tools, tools:append in overrides, hook do[], same-org knowledge bases in model.toolIds): endCall/dtmf/voicemail/output are sent as written; function tools by function.name and apiRequest by name are mocked; transferCall becomes a mocked dead-server function; handoffs pass only to squad members or inline assistants; everything else (sms, sipRequest, code, mcp, integrations, unknown types) fails the build naming the tool; - fails any tool-bearing key (TOOL_BEARING_KEYS) outside those positions, so a new API field can't slip through unclassified; - replaces servers with https://vapi-gitops-ci.invalid on every assistant and function tool rather than deleting them (a deleted server falls back to the phone number's or org's), clears serverMessages, and dead-ends scenario webhook hooks; - adds a default error mock for every mocked tool to each scenario's toolMocks, replacing disabled ones, and warns about mocks that match no tool; - fails custom knowledge bases, model.knowledgeBaseId, personality tools with side effects, hook transfer actions, and cross-org query or knowledge-base tools. `toolMocks: off` skips the tool rules; `stripWebhooks: false` keeps assistant servers while tool servers are still replaced. Refs TEST-141 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 1, 2026
Contributor
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This was referenced Oct 1, 2026
scott-lowe-vapi
marked this pull request as ready for review
October 1, 2026 23:52
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.

Value
V.A.L.U.E. tier: project — PR 6 of 10 for inline simulation PR checks (TEST-141); this is the safety-critical PR, so it's kept separate for review. Still offline: nothing is sent until PR 7.
src/check-mocks.tsruns as the last pass ofcheckPayloadBuildundertoolMocks: strict(the default).endCall,dtmf,voicemail,output;query(same org only)model.toolIdsby UUIDhandoffdynamic,squad, non-members: failfunctionbyfunction.name;apiRequestby top-levelname(urlset to the dead host){"error":"vapi-gitops-ci: <tool> is not mocked in this scenario"}transferCallfunctionunder its own name, so it can never connectsms,sipRequest,code,mcp,bash,computer,textEditor,transferCancel,transferSuccessful,google.*,slack.*,gohighlevel.*,ghl,make, unknownTOOL_BEARING_KEYSoutside a handled position fails with "unsupported tool position". Examples:model.functions,model.toolRefs, reasonerskills,declineTool,tools:appendoutside overrides. A future API field that carries tools fails instead of slipping through.server: {url: "https://vapi-gitops-ci.invalid", timeoutSeconds: 1}andserverMessages: [];serverUrl/serverUrlSecretare dropped;webhookhooks get the dead server.toolMocks, never in assistant metadata, because handoffs rebuild the assistant. A user mock withenabled: falseis replaced, and a mock naming no tool in the target produces a warning.model.knowledgeBaseId, and custom-provider knowledge bases;transferactions, and hooktoolIds;scenarioIdentries, and non-stockpersonalityIds.hooks[].do[]) probably bypass scenariotoolMocks, which apply on the LLM tool-call path. They're classified the same way and get the dead server, which is the real safeguard there. This is documented in the module header and goes intosimulations.mdin PR 8.toolMocks: offskips the tool rules, for a dedicated CI org;stripWebhooks: falsekeeps assistant servers, while tool servers are still replaced under strict mocks.Evidence of value
Dry run of the TEST-141 parity squad. In a copy of the fixture:
example.comservers;book_appointmentmock;--print-payloadoutput was then inspected:example.comURLs left in the payloadtools:appendone)server/serverMessages{"url":"https://vapi-gitops-ci.invalid","timeoutSeconds":1}/[]lookup_patient,check_availabilityfrom the scenario;book_appointment= default error mocksmstool on the receptionisttarget.squad.members[0].assistant.model.tools[1]: sms tools can't be mocked; remove it, or set toolMocks: off with a dedicated CI orgTOOL_BEARING_KEYSaudit against the API's OpenAPI schema (apps/dashboard/src/api/schema.jsonin the monorepo), listing every property whose schema references a tool DTO or is named like a tool reference:tools(all model DTOs andTransferAssistantModel);tools:append(AssistantOverrides);toolIdsandtoolRefs(all model DTOs);declineTool/declineToolId(RecordingConsentPlanVerbal);skills(OpenAIReasoner);assistantDestinations(SquadMemberDTO).tool/toolId(ToolCallHookAction) andfunction(FunctionCallHookAction) are hook actions;functionalso appears on handoff DTOs, as the tool's own definition.functionsandforwardingPhoneNumber(s)aren't in the current schema, and stay listed as fail-closed.Tests:
npm testgoes from 430 to 450 passing;npm run buildis clean.Testing plan
tests/check-mocks.test.ts(20 tests):queryand knowledge bases, same org vs cross-org;TOOL_BEARING_KEYSentry at an unhandled position, plusfunctions/toolRefs/assistantDestinationsplacement;parameters/ judgeschemaproperties namedtoolsnot tripping the rule;toolMocks: offandstripWebhooks: false.tests/check-payload.test.tsexpectations were updated for the dead servers the policy now adds, and the parity end-to-end test still passes.toolMocksintercept every function andapiRequestcall is shown for the parity run only, and the full transcript scan is PR 7.transferCalland integration mock names. They're rewritten or refused rather than relied on.Stacked on #61.
Refs TEST-141
🤖 Generated with Claude Code