@guidify-ai/vapi-studio 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/agent/AGENTS.md +42 -0
  4. package/agent/CLAUDE.md +15 -0
  5. package/agent/claude/rules/ui-and-api-identity.md +9 -0
  6. package/agent/claude/rules/vapi-studio-best-practices.md +36 -0
  7. package/agent/cursor/ui-and-api-identity.mdc +16 -0
  8. package/agent/cursor/vapi-studio-best-practices.mdc +43 -0
  9. package/dist/adapters/vapi/vapi-sse.compiler.d.ts +61 -0
  10. package/dist/adapters/vapi/vapi-sse.compiler.d.ts.map +1 -0
  11. package/dist/adapters/vapi/vapi-sse.compiler.js +396 -0
  12. package/dist/adapters/vapi/vapi-sse.compiler.js.map +1 -0
  13. package/dist/analytics/analytics-tags.d.ts +60 -0
  14. package/dist/analytics/analytics-tags.d.ts.map +1 -0
  15. package/dist/analytics/analytics-tags.js +27 -0
  16. package/dist/analytics/analytics-tags.js.map +1 -0
  17. package/dist/brain/adapters/chatgpt-brain.adapter.d.ts +27 -0
  18. package/dist/brain/adapters/chatgpt-brain.adapter.d.ts.map +1 -0
  19. package/dist/brain/adapters/chatgpt-brain.adapter.js +94 -0
  20. package/dist/brain/adapters/chatgpt-brain.adapter.js.map +1 -0
  21. package/dist/brain/adapters/claude-brain.adapter.d.ts +24 -0
  22. package/dist/brain/adapters/claude-brain.adapter.d.ts.map +1 -0
  23. package/dist/brain/adapters/claude-brain.adapter.js +96 -0
  24. package/dist/brain/adapters/claude-brain.adapter.js.map +1 -0
  25. package/dist/brain/adapters/gemini-brain.adapter.d.ts +24 -0
  26. package/dist/brain/adapters/gemini-brain.adapter.d.ts.map +1 -0
  27. package/dist/brain/adapters/gemini-brain.adapter.js +106 -0
  28. package/dist/brain/adapters/gemini-brain.adapter.js.map +1 -0
  29. package/dist/brain/adapters/grok-brain.adapter.d.ts +24 -0
  30. package/dist/brain/adapters/grok-brain.adapter.d.ts.map +1 -0
  31. package/dist/brain/adapters/grok-brain.adapter.js +90 -0
  32. package/dist/brain/adapters/grok-brain.adapter.js.map +1 -0
  33. package/dist/brain/adapters/json-llm-brain.base.d.ts +63 -0
  34. package/dist/brain/adapters/json-llm-brain.base.d.ts.map +1 -0
  35. package/dist/brain/adapters/json-llm-brain.base.js +511 -0
  36. package/dist/brain/adapters/json-llm-brain.base.js.map +1 -0
  37. package/dist/brain/adapters/mock-brain.adapter.d.ts +32 -0
  38. package/dist/brain/adapters/mock-brain.adapter.d.ts.map +1 -0
  39. package/dist/brain/adapters/mock-brain.adapter.js +201 -0
  40. package/dist/brain/adapters/mock-brain.adapter.js.map +1 -0
  41. package/dist/brain/adapters/mock-provider-brains.adapter.d.ts +22 -0
  42. package/dist/brain/adapters/mock-provider-brains.adapter.d.ts.map +1 -0
  43. package/dist/brain/adapters/mock-provider-brains.adapter.js +48 -0
  44. package/dist/brain/adapters/mock-provider-brains.adapter.js.map +1 -0
  45. package/dist/brain/brain-clarify.d.ts +75 -0
  46. package/dist/brain/brain-clarify.d.ts.map +1 -0
  47. package/dist/brain/brain-clarify.js +146 -0
  48. package/dist/brain/brain-clarify.js.map +1 -0
  49. package/dist/brain/brain-config.d.ts +20 -0
  50. package/dist/brain/brain-config.d.ts.map +1 -0
  51. package/dist/brain/brain-config.js +20 -0
  52. package/dist/brain/brain-config.js.map +1 -0
  53. package/dist/brain/brain-judge.d.ts +68 -0
  54. package/dist/brain/brain-judge.d.ts.map +1 -0
  55. package/dist/brain/brain-judge.js +111 -0
  56. package/dist/brain/brain-judge.js.map +1 -0
  57. package/dist/brain/brain-ranking.d.ts +41 -0
  58. package/dist/brain/brain-ranking.d.ts.map +1 -0
  59. package/dist/brain/brain-ranking.js +103 -0
  60. package/dist/brain/brain-ranking.js.map +1 -0
  61. package/dist/brain/brain-usage.tracker.d.ts +48 -0
  62. package/dist/brain/brain-usage.tracker.d.ts.map +1 -0
  63. package/dist/brain/brain-usage.tracker.js +106 -0
  64. package/dist/brain/brain-usage.tracker.js.map +1 -0
  65. package/dist/brain/brain.port.d.ts +63 -0
  66. package/dist/brain/brain.port.d.ts.map +1 -0
  67. package/dist/brain/brain.port.js +7 -0
  68. package/dist/brain/brain.port.js.map +1 -0
  69. package/dist/brain/brain.service.d.ts +14 -0
  70. package/dist/brain/brain.service.d.ts.map +1 -0
  71. package/dist/brain/brain.service.js +27 -0
  72. package/dist/brain/brain.service.js.map +1 -0
  73. package/dist/brain/claude-cheap-models.d.ts +18 -0
  74. package/dist/brain/claude-cheap-models.d.ts.map +1 -0
  75. package/dist/brain/claude-cheap-models.js +53 -0
  76. package/dist/brain/claude-cheap-models.js.map +1 -0
  77. package/dist/brain/gemini-cheap-models.d.ts +18 -0
  78. package/dist/brain/gemini-cheap-models.d.ts.map +1 -0
  79. package/dist/brain/gemini-cheap-models.js +53 -0
  80. package/dist/brain/gemini-cheap-models.js.map +1 -0
  81. package/dist/brain/grok-cheap-models.d.ts +18 -0
  82. package/dist/brain/grok-cheap-models.d.ts.map +1 -0
  83. package/dist/brain/grok-cheap-models.js +53 -0
  84. package/dist/brain/grok-cheap-models.js.map +1 -0
  85. package/dist/brain/llm-model-pricing.d.ts +29 -0
  86. package/dist/brain/llm-model-pricing.d.ts.map +1 -0
  87. package/dist/brain/llm-model-pricing.js +38 -0
  88. package/dist/brain/llm-model-pricing.js.map +1 -0
  89. package/dist/brain/openai-cheap-models.d.ts +34 -0
  90. package/dist/brain/openai-cheap-models.d.ts.map +1 -0
  91. package/dist/brain/openai-cheap-models.js +72 -0
  92. package/dist/brain/openai-cheap-models.js.map +1 -0
  93. package/dist/brain/prompt-injection-guard.d.ts +34 -0
  94. package/dist/brain/prompt-injection-guard.d.ts.map +1 -0
  95. package/dist/brain/prompt-injection-guard.js +109 -0
  96. package/dist/brain/prompt-injection-guard.js.map +1 -0
  97. package/dist/channel/channel-tools.d.ts +72 -0
  98. package/dist/channel/channel-tools.d.ts.map +1 -0
  99. package/dist/channel/channel-tools.js +131 -0
  100. package/dist/channel/channel-tools.js.map +1 -0
  101. package/dist/conversation/call-turn-queue.d.ts +64 -0
  102. package/dist/conversation/call-turn-queue.d.ts.map +1 -0
  103. package/dist/conversation/call-turn-queue.js +156 -0
  104. package/dist/conversation/call-turn-queue.js.map +1 -0
  105. package/dist/conversation/conversation-bootstrap.service.d.ts +60 -0
  106. package/dist/conversation/conversation-bootstrap.service.d.ts.map +1 -0
  107. package/dist/conversation/conversation-bootstrap.service.js +383 -0
  108. package/dist/conversation/conversation-bootstrap.service.js.map +1 -0
  109. package/dist/conversation/conversation-entry.d.ts +38 -0
  110. package/dist/conversation/conversation-entry.d.ts.map +1 -0
  111. package/dist/conversation/conversation-entry.js +17 -0
  112. package/dist/conversation/conversation-entry.js.map +1 -0
  113. package/dist/conversation/conversation-history.d.ts +31 -0
  114. package/dist/conversation/conversation-history.d.ts.map +1 -0
  115. package/dist/conversation/conversation-history.js +53 -0
  116. package/dist/conversation/conversation-history.js.map +1 -0
  117. package/dist/conversation/conversation-limits.d.ts +49 -0
  118. package/dist/conversation/conversation-limits.d.ts.map +1 -0
  119. package/dist/conversation/conversation-limits.js +44 -0
  120. package/dist/conversation/conversation-limits.js.map +1 -0
  121. package/dist/conversation/conversation-resume.service.d.ts +39 -0
  122. package/dist/conversation/conversation-resume.service.d.ts.map +1 -0
  123. package/dist/conversation/conversation-resume.service.js +234 -0
  124. package/dist/conversation/conversation-resume.service.js.map +1 -0
  125. package/dist/conversation/conversation-schema.d.ts +23 -0
  126. package/dist/conversation/conversation-schema.d.ts.map +1 -0
  127. package/dist/conversation/conversation-schema.js +3 -0
  128. package/dist/conversation/conversation-schema.js.map +1 -0
  129. package/dist/conversation/listen-expectation.d.ts +145 -0
  130. package/dist/conversation/listen-expectation.d.ts.map +1 -0
  131. package/dist/conversation/listen-expectation.js +219 -0
  132. package/dist/conversation/listen-expectation.js.map +1 -0
  133. package/dist/conversation/listen-timeout.d.ts +42 -0
  134. package/dist/conversation/listen-timeout.d.ts.map +1 -0
  135. package/dist/conversation/listen-timeout.js +57 -0
  136. package/dist/conversation/listen-timeout.js.map +1 -0
  137. package/dist/conversation/supervised-conversation.d.ts +64 -0
  138. package/dist/conversation/supervised-conversation.d.ts.map +1 -0
  139. package/dist/conversation/supervised-conversation.js +198 -0
  140. package/dist/conversation/supervised-conversation.js.map +1 -0
  141. package/dist/conversation/supervised-conversation.registry.d.ts +14 -0
  142. package/dist/conversation/supervised-conversation.registry.d.ts.map +1 -0
  143. package/dist/conversation/supervised-conversation.registry.js +47 -0
  144. package/dist/conversation/supervised-conversation.registry.js.map +1 -0
  145. package/dist/conversation/types.d.ts +52 -0
  146. package/dist/conversation/types.d.ts.map +1 -0
  147. package/dist/conversation/types.js +3 -0
  148. package/dist/conversation/types.js.map +1 -0
  149. package/dist/events/conversation-console.d.ts +17 -0
  150. package/dist/events/conversation-console.d.ts.map +1 -0
  151. package/dist/events/conversation-console.js +863 -0
  152. package/dist/events/conversation-console.js.map +1 -0
  153. package/dist/events/daily-log.driver.d.ts +35 -0
  154. package/dist/events/daily-log.driver.d.ts.map +1 -0
  155. package/dist/events/daily-log.driver.js +206 -0
  156. package/dist/events/daily-log.driver.js.map +1 -0
  157. package/dist/events/event.service.d.ts +24 -0
  158. package/dist/events/event.service.d.ts.map +1 -0
  159. package/dist/events/event.service.js +141 -0
  160. package/dist/events/event.service.js.map +1 -0
  161. package/dist/events/route-forensics.d.ts +38 -0
  162. package/dist/events/route-forensics.d.ts.map +1 -0
  163. package/dist/events/route-forensics.js +85 -0
  164. package/dist/events/route-forensics.js.map +1 -0
  165. package/dist/events/studio-event-bus.d.ts +17 -0
  166. package/dist/events/studio-event-bus.d.ts.map +1 -0
  167. package/dist/events/studio-event-bus.js +37 -0
  168. package/dist/events/studio-event-bus.js.map +1 -0
  169. package/dist/events/studio-event.d.ts +44 -0
  170. package/dist/events/studio-event.d.ts.map +1 -0
  171. package/dist/events/studio-event.js +27 -0
  172. package/dist/events/studio-event.js.map +1 -0
  173. package/dist/flow/condition.d.ts +35 -0
  174. package/dist/flow/condition.d.ts.map +1 -0
  175. package/dist/flow/condition.js +246 -0
  176. package/dist/flow/condition.js.map +1 -0
  177. package/dist/flow/flow-loader.d.ts +71 -0
  178. package/dist/flow/flow-loader.d.ts.map +1 -0
  179. package/dist/flow/flow-loader.js +182 -0
  180. package/dist/flow/flow-loader.js.map +1 -0
  181. package/dist/forms/form-html.d.ts +30 -0
  182. package/dist/forms/form-html.d.ts.map +1 -0
  183. package/dist/forms/form-html.js +130 -0
  184. package/dist/forms/form-html.js.map +1 -0
  185. package/dist/forms/form.tokens.d.ts +3 -0
  186. package/dist/forms/form.tokens.d.ts.map +1 -0
  187. package/dist/forms/form.tokens.js +6 -0
  188. package/dist/forms/form.tokens.js.map +1 -0
  189. package/dist/forms/form.types.d.ts +81 -0
  190. package/dist/forms/form.types.d.ts.map +1 -0
  191. package/dist/forms/form.types.js +36 -0
  192. package/dist/forms/form.types.js.map +1 -0
  193. package/dist/forms/forms.service.d.ts +52 -0
  194. package/dist/forms/forms.service.d.ts.map +1 -0
  195. package/dist/forms/forms.service.js +434 -0
  196. package/dist/forms/forms.service.js.map +1 -0
  197. package/dist/forms/noop-form-dispose.adapter.d.ts +12 -0
  198. package/dist/forms/noop-form-dispose.adapter.d.ts.map +1 -0
  199. package/dist/forms/noop-form-dispose.adapter.js +27 -0
  200. package/dist/forms/noop-form-dispose.adapter.js.map +1 -0
  201. package/dist/forms/twilio-sms-form-dispose.adapter.d.ts +33 -0
  202. package/dist/forms/twilio-sms-form-dispose.adapter.d.ts.map +1 -0
  203. package/dist/forms/twilio-sms-form-dispose.adapter.js +125 -0
  204. package/dist/forms/twilio-sms-form-dispose.adapter.js.map +1 -0
  205. package/dist/forms/twilio-sms.env.d.ts +62 -0
  206. package/dist/forms/twilio-sms.env.d.ts.map +1 -0
  207. package/dist/forms/twilio-sms.env.js +147 -0
  208. package/dist/forms/twilio-sms.env.js.map +1 -0
  209. package/dist/forms/twilio-sms.sender.d.ts +27 -0
  210. package/dist/forms/twilio-sms.sender.d.ts.map +1 -0
  211. package/dist/forms/twilio-sms.sender.js +75 -0
  212. package/dist/forms/twilio-sms.sender.js.map +1 -0
  213. package/dist/identity/email-parse.d.ts +20 -0
  214. package/dist/identity/email-parse.d.ts.map +1 -0
  215. package/dist/identity/email-parse.js +102 -0
  216. package/dist/identity/email-parse.js.map +1 -0
  217. package/dist/identity/index.d.ts +17 -0
  218. package/dist/identity/index.d.ts.map +1 -0
  219. package/dist/identity/index.js +23 -0
  220. package/dist/identity/index.js.map +1 -0
  221. package/dist/index.d.ts +108 -0
  222. package/dist/index.d.ts.map +1 -0
  223. package/dist/index.js +300 -0
  224. package/dist/index.js.map +1 -0
  225. package/dist/integrations/integration-client.d.ts +41 -0
  226. package/dist/integrations/integration-client.d.ts.map +1 -0
  227. package/dist/integrations/integration-client.js +152 -0
  228. package/dist/integrations/integration-client.js.map +1 -0
  229. package/dist/integrations/jwt-hmac.d.ts +33 -0
  230. package/dist/integrations/jwt-hmac.d.ts.map +1 -0
  231. package/dist/integrations/jwt-hmac.js +108 -0
  232. package/dist/integrations/jwt-hmac.js.map +1 -0
  233. package/dist/intention/code-intention.d.ts +94 -0
  234. package/dist/intention/code-intention.d.ts.map +1 -0
  235. package/dist/intention/code-intention.js +89 -0
  236. package/dist/intention/code-intention.js.map +1 -0
  237. package/dist/intention/intention-constants.d.ts +37 -0
  238. package/dist/intention/intention-constants.d.ts.map +1 -0
  239. package/dist/intention/intention-constants.js +37 -0
  240. package/dist/intention/intention-constants.js.map +1 -0
  241. package/dist/intentions/standard-intentions.d.ts +26 -0
  242. package/dist/intentions/standard-intentions.d.ts.map +1 -0
  243. package/dist/intentions/standard-intentions.js +28 -0
  244. package/dist/intentions/standard-intentions.js.map +1 -0
  245. package/dist/node/agent-node.d.ts +156 -0
  246. package/dist/node/agent-node.d.ts.map +1 -0
  247. package/dist/node/agent-node.js +179 -0
  248. package/dist/node/agent-node.js.map +1 -0
  249. package/dist/node/catch-directive.d.ts +33 -0
  250. package/dist/node/catch-directive.d.ts.map +1 -0
  251. package/dist/node/catch-directive.js +41 -0
  252. package/dist/node/catch-directive.js.map +1 -0
  253. package/dist/output/conversation-output.d.ts +113 -0
  254. package/dist/output/conversation-output.d.ts.map +1 -0
  255. package/dist/output/conversation-output.js +100 -0
  256. package/dist/output/conversation-output.js.map +1 -0
  257. package/dist/persistence/conversation.entity.d.ts +27 -0
  258. package/dist/persistence/conversation.entity.d.ts.map +1 -0
  259. package/dist/persistence/conversation.entity.js +115 -0
  260. package/dist/persistence/conversation.entity.js.map +1 -0
  261. package/dist/persistence/conversation.repository.d.ts +82 -0
  262. package/dist/persistence/conversation.repository.d.ts.map +1 -0
  263. package/dist/persistence/conversation.repository.js +277 -0
  264. package/dist/persistence/conversation.repository.js.map +1 -0
  265. package/dist/persistence/project.entity.d.ts +12 -0
  266. package/dist/persistence/project.entity.d.ts.map +1 -0
  267. package/dist/persistence/project.entity.js +46 -0
  268. package/dist/persistence/project.entity.js.map +1 -0
  269. package/dist/persistence/project.repository.d.ts +17 -0
  270. package/dist/persistence/project.repository.d.ts.map +1 -0
  271. package/dist/persistence/project.repository.js +69 -0
  272. package/dist/persistence/project.repository.js.map +1 -0
  273. package/dist/persistence/provider-ingress.entity.d.ts +18 -0
  274. package/dist/persistence/provider-ingress.entity.d.ts.map +1 -0
  275. package/dist/persistence/provider-ingress.entity.js +91 -0
  276. package/dist/persistence/provider-ingress.entity.js.map +1 -0
  277. package/dist/persistence/provider-ingress.repository.d.ts +21 -0
  278. package/dist/persistence/provider-ingress.repository.d.ts.map +1 -0
  279. package/dist/persistence/provider-ingress.repository.js +76 -0
  280. package/dist/persistence/provider-ingress.repository.js.map +1 -0
  281. package/dist/studio-ui/assets/index-BGd2OhLv.js +84 -0
  282. package/dist/studio-ui/assets/index-BGd2OhLv.js.map +1 -0
  283. package/dist/studio-ui/assets/index-DnJos5x2.css +1 -0
  284. package/dist/studio-ui/index.html +13 -0
  285. package/dist/studio-ui-mount/index.d.ts +6 -0
  286. package/dist/studio-ui-mount/index.d.ts.map +1 -0
  287. package/dist/studio-ui-mount/index.js +13 -0
  288. package/dist/studio-ui-mount/index.js.map +1 -0
  289. package/dist/studio-ui-mount/resolve-studio-ui-root.d.ts +6 -0
  290. package/dist/studio-ui-mount/resolve-studio-ui-root.d.ts.map +1 -0
  291. package/dist/studio-ui-mount/resolve-studio-ui-root.js +25 -0
  292. package/dist/studio-ui-mount/resolve-studio-ui-root.js.map +1 -0
  293. package/dist/studio-ui-mount/studio-spa.controller.d.ts +18 -0
  294. package/dist/studio-ui-mount/studio-spa.controller.d.ts.map +1 -0
  295. package/dist/studio-ui-mount/studio-spa.controller.js +57 -0
  296. package/dist/studio-ui-mount/studio-spa.controller.js.map +1 -0
  297. package/dist/studio-ui-mount/studio-ui.module.d.ts +15 -0
  298. package/dist/studio-ui-mount/studio-ui.module.d.ts.map +1 -0
  299. package/dist/studio-ui-mount/studio-ui.module.js +47 -0
  300. package/dist/studio-ui-mount/studio-ui.module.js.map +1 -0
  301. package/dist/studio-ui-mount/studio-ui.tokens.d.ts +2 -0
  302. package/dist/studio-ui-mount/studio-ui.tokens.d.ts.map +1 -0
  303. package/dist/studio-ui-mount/studio-ui.tokens.js +5 -0
  304. package/dist/studio-ui-mount/studio-ui.tokens.js.map +1 -0
  305. package/dist/supervisor/supervisor-cascade.d.ts +55 -0
  306. package/dist/supervisor/supervisor-cascade.d.ts.map +1 -0
  307. package/dist/supervisor/supervisor-cascade.js +261 -0
  308. package/dist/supervisor/supervisor-cascade.js.map +1 -0
  309. package/dist/supervisor/supervisor-engine.d.ts +9 -0
  310. package/dist/supervisor/supervisor-engine.d.ts.map +1 -0
  311. package/dist/supervisor/supervisor-engine.js +64 -0
  312. package/dist/supervisor/supervisor-engine.js.map +1 -0
  313. package/dist/supervisor/supervisor-limits.d.ts +8 -0
  314. package/dist/supervisor/supervisor-limits.d.ts.map +1 -0
  315. package/dist/supervisor/supervisor-limits.js +36 -0
  316. package/dist/supervisor/supervisor-limits.js.map +1 -0
  317. package/dist/supervisor/supervisor-node-exec.d.ts +39 -0
  318. package/dist/supervisor/supervisor-node-exec.d.ts.map +1 -0
  319. package/dist/supervisor/supervisor-node-exec.js +201 -0
  320. package/dist/supervisor/supervisor-node-exec.js.map +1 -0
  321. package/dist/supervisor/supervisor-orchestration.d.ts +24 -0
  322. package/dist/supervisor/supervisor-orchestration.d.ts.map +1 -0
  323. package/dist/supervisor/supervisor-orchestration.js +395 -0
  324. package/dist/supervisor/supervisor-orchestration.js.map +1 -0
  325. package/dist/supervisor/supervisor-portal.d.ts +75 -0
  326. package/dist/supervisor/supervisor-portal.d.ts.map +1 -0
  327. package/dist/supervisor/supervisor-portal.js +320 -0
  328. package/dist/supervisor/supervisor-portal.js.map +1 -0
  329. package/dist/supervisor/supervisor-route.d.ts +53 -0
  330. package/dist/supervisor/supervisor-route.d.ts.map +1 -0
  331. package/dist/supervisor/supervisor-route.js +308 -0
  332. package/dist/supervisor/supervisor-route.js.map +1 -0
  333. package/dist/supervisor/supervisor-special-turns.d.ts +34 -0
  334. package/dist/supervisor/supervisor-special-turns.d.ts.map +1 -0
  335. package/dist/supervisor/supervisor-special-turns.js +308 -0
  336. package/dist/supervisor/supervisor-special-turns.js.map +1 -0
  337. package/dist/supervisor/supervisor.d.ts +44 -0
  338. package/dist/supervisor/supervisor.d.ts.map +1 -0
  339. package/dist/supervisor/supervisor.js +79 -0
  340. package/dist/supervisor/supervisor.js.map +1 -0
  341. package/dist/supervisor/supervisor.types.d.ts +71 -0
  342. package/dist/supervisor/supervisor.types.d.ts.map +1 -0
  343. package/dist/supervisor/supervisor.types.js +8 -0
  344. package/dist/supervisor/supervisor.types.js.map +1 -0
  345. package/dist/tasks/studio-task-queue.d.ts +93 -0
  346. package/dist/tasks/studio-task-queue.d.ts.map +1 -0
  347. package/dist/tasks/studio-task-queue.js +226 -0
  348. package/dist/tasks/studio-task-queue.js.map +1 -0
  349. package/dist/util/studio-env.d.ts +7 -0
  350. package/dist/util/studio-env.d.ts.map +1 -0
  351. package/dist/util/studio-env.js +32 -0
  352. package/dist/util/studio-env.js.map +1 -0
  353. package/dist/vapi-studio.module.d.ts +55 -0
  354. package/dist/vapi-studio.module.d.ts.map +1 -0
  355. package/dist/vapi-studio.module.js +166 -0
  356. package/dist/vapi-studio.module.js.map +1 -0
  357. package/dist/workflow/workflow-handoff.service.d.ts +29 -0
  358. package/dist/workflow/workflow-handoff.service.d.ts.map +1 -0
  359. package/dist/workflow/workflow-handoff.service.js +165 -0
  360. package/dist/workflow/workflow-handoff.service.js.map +1 -0
  361. package/dist/workflow/workflow-loader.d.ts +48 -0
  362. package/dist/workflow/workflow-loader.d.ts.map +1 -0
  363. package/dist/workflow/workflow-loader.js +87 -0
  364. package/dist/workflow/workflow-loader.js.map +1 -0
  365. package/docs/README.md +69 -0
  366. package/docs/assets/deterministic-assistant-flow.svg +128 -0
  367. package/docs/assets/vapi-studio-stack.svg +26 -0
  368. package/docs/best-practices/README.md +37 -0
  369. package/docs/best-practices/brain-and-prompt-injection.md +70 -0
  370. package/docs/best-practices/conversation-design.md +152 -0
  371. package/docs/best-practices/debugging-and-observability.md +49 -0
  372. package/docs/best-practices/documentation-layers.md +31 -0
  373. package/docs/best-practices/identity-and-pii.md +66 -0
  374. package/docs/best-practices/nodes-and-listens.md +116 -0
  375. package/docs/best-practices/ui-and-api-identity.md +51 -0
  376. package/docs/building-apps/creating-an-app.md +59 -0
  377. package/docs/building-apps/example-apps.md +23 -0
  378. package/docs/building-apps/overview.md +27 -0
  379. package/docs/getting-started/installation.md +52 -0
  380. package/docs/getting-started/introduction.md +23 -0
  381. package/docs/getting-started/quick-start.md +23 -0
  382. package/docs/guide/concepts.md +103 -0
  383. package/docs/guide/forms.md +102 -0
  384. package/docs/guide/handbook.md +21 -0
  385. package/docs/guide/introduction.md +56 -0
  386. package/docs/guide/module-setup.md +68 -0
  387. package/docs/guide/vapi-adapter.md +70 -0
  388. package/docs/guide/workflow-squad.md +59 -0
  389. package/docs/guides/agents.md +85 -0
  390. package/docs/guides/docker-compose.events.stub.yaml +27 -0
  391. package/docs/guides/documentation.md +13 -0
  392. package/docs/guides/extending-events.md +155 -0
  393. package/docs/guides/node-conventions.md +59 -0
  394. package/docs/guides/speckit.md +56 -0
  395. package/docs/reference/data-model.md +79 -0
  396. package/docs/reference/environment-variables.md +60 -0
  397. package/docs/reference/events-and-logging.md +137 -0
  398. package/docs/reference/flow-schema.md +79 -0
  399. package/docs/reference/runtime-api.md +708 -0
  400. package/docs/reference/security.md +41 -0
  401. package/docs/reference/standard-intentions.md +49 -0
  402. package/docs/reference/testing.md +15 -0
  403. package/docs/samples/events/nest-listener.example.ts +17 -0
  404. package/docs/samples/events/redis-bridge.example.ts +44 -0
  405. package/package.json +81 -0
  406. package/scripts/fix-access-modifiers.mjs +85 -0
  407. package/scripts/install-agent-refs.cjs +196 -0
@@ -0,0 +1,66 @@
1
+ # Identity and PII
2
+
3
+ Collecting caller identity without poisoning memory or stacking CTAs.
4
+
5
+ ## Consent before destination
6
+
7
+ SMS / form delivery is two turns when both are needed:
8
+
9
+ 1. **Consent** — “Is it okay if I text you a short form for your details?”
10
+ 2. **Destination** — calling-from suitable? **or** best mobile?
11
+
12
+ Never ask consent and phone in one prompt. Decline → voice fallback path; do not re-ask consent after it is set for this Conversation.
13
+
14
+ ## Phone numbers (NA default)
15
+
16
+ Unless the app explicitly supports other regions:
17
+
18
+ - Store **exactly 10 digits**.
19
+ - Strip leading country `1` when 11 digits.
20
+ - If more than 10 digit characters appear (trailing ASR), take the **first 10**.
21
+ - Fewer than 10 → fail and re-ask phone only.
22
+ - Use a **short** listen timeout while collecting digits.
23
+ - **Read-back confirm is mandatory** after spoken digits: “I heard 555-010-0999. Is that correct?” before SMS/form. Accepting calling-from ANI counts as confirm. Do not proceed on digits alone.
24
+ - Incomplete ASR (fewer than 10 digits) → say you only caught N digits and ask again — not a generic “sorry, what’s the number?”
25
+ - First ask for a missing field (e.g. mobile after consent) is a **gap fill**, not a re-ask — never preface with “I’m sorry, I didn’t quite get it.”
26
+
27
+ ## Names
28
+
29
+ A person name is letters (and limited punctuation), not digits, not “hello”, not a full sentence. Reject and re-ask; never place digit strings into `firstName` / profile verify summaries (“I have 2 3 6 … on file”).
30
+
31
+ **Spoken name frequency:** personal treatment is fine; saying the first name **more than once per turn** (or in consecutive sentences across `say` + `continueTo`) usually sounds wrong. At most one use per assistant turn unless there is an explicit reason (re-engage after silence, empathy, disambiguation). Prefer shared helpers and a “name already used this segment” flag — see [conversation-design.md](./conversation-design.md#personal-treatment-names).
32
+
33
+ ## Email
34
+
35
+ Boxed helpers — prefer the dedicated path:
36
+
37
+ ```ts
38
+ import {
39
+ EMAIL_EXTRACT_DESCRIPTION,
40
+ collectedEmail,
41
+ parseSpelledEmail,
42
+ } from '@guidify-ai/vapi-studio/identity';
43
+ // also: from '@guidify-ai/vapi-studio'
44
+ ```
45
+
46
+ | Export | Role |
47
+ | --- | --- |
48
+ | `EMAIL_EXTRACT_DESCRIPTION` | `extract.fields[].description` so Brain GOT knows spelled ASR (`at` / `dot` / letter-by-letter) |
49
+ | `collectedEmail(extracted, spoken)` | Prefer Brain extract, then spoken; `null` on fail |
50
+ | `parseSpelledEmail(raw)` | Direct `name@domain` **or** spelled forms |
51
+
52
+ Fail closed → re-ask the same CTA. Prefer a read-back confirm before sending mail (“I heard … Is that correct?”). Do not hardcode demo emails in production paths; PoC mocks belong behind clearly named constants and mock-only branches.
53
+
54
+ ## Forms
55
+
56
+ - Deliver with `ctx.forms.open` (live Vapi) or `expose` (Studio-friendly blocking) + a dispose adapter that makes a fillable surface available (Studio modal, **first-party HTML** at `/forms/:exposeId`, or SMS link).
57
+ - Field specs are JSON (`FormFieldSpec`); HTML is rendered from that JSON (`renderFormHtml`), not hand-written per form.
58
+ - Submit must call `FormsService.submit(exposeId, values)` so the open path can `claimSubmitted()` (or blocking `expose` resumes).
59
+ - **Local PoC without SMS:** open `/forms/inbox` on this machine — it lists exposed / unsubmitted forms; Open → fill → Submit unblocks the waiting bot.
60
+ - **Caller never got the text / link:** while the form is still open, listen for “I didn’t get it / no text / didn’t receive the link.” Then **one CTA**: resend via `ctx.forms.resend()` and ask **“Did you get it this time?”** — do not also ask them to fill in the same turn. On yes → next turn asks them to open and fill. Cap resends (e.g. 2) then voice fallback. Never stack “resent” + “fill it out” + “still waiting” in one listen.
61
+ - Form deliver/fill failure → **one CTA**: “OK to finish by voice?” then collect fields. Never stack failure notice + first-name ask in one listen.
62
+ - After submit, voice-confirm received values when the channel is unreliable; do not treat unconfirmed ASR as identity.
63
+
64
+ ## Profile verify
65
+
66
+ Only when CRM hydrated a **returning** caller (`returningCaller === true`, gated by `recognizeReturningCaller`). Never say “on file” for PII collected earlier in **this** Conversation. If recognition FF is off, treat every call as first-time for verify UX.
@@ -0,0 +1,116 @@
1
+ # Nodes and listens
2
+
3
+ How to structure Vapi Studio Nodes so agents and humans can keep flows maintainable.
4
+
5
+ ## Start → Greeting
6
+
7
+ Happy-path shape (how `/flow` should read, and how runtime should feel):
8
+
9
+ ```text
10
+ Start → Greeting (flow.start) → …
11
+ ```
12
+
13
+ | Step | What it is | Runtime |
14
+ | --- | --- | --- |
15
+ | **Start** | Call / bootstrap + inject bag + near-zero prep | `ConversationEntryPoint.createVariables` / `beforeEach` — company, email, name from LP; phone from caller; optional JWT/API warm. **Must not block first speech** (target < 1.5s to first SSE). Not a spoken Node; do not burn a Supervisor turn on silence. |
16
+ | **Greeting** | First real speak | `flow.start` Node — opening turn only (no Brain scan). Then the product DAG. |
17
+
18
+ **Do not** add Conversation-start or assistant-name bookends before Greeting on single-flow diagrams — Start is the entry. Squad/workflow lanes may still show per-module `__start` labels for handoff identity.
19
+
20
+ ## Node = one job
21
+
22
+ Each Node should own a small slice: one ask, one verification, one side effect, or one lane step. If you need two sequential CTAs, use two Nodes (or `output.continueTo({ nodeId })`) — see [conversation-design.md](./conversation-design.md).
23
+
24
+ ## Prefer `before()` / `after()` for async work
25
+
26
+ Do **not** overload `run()` with auth, CRM lookups, JWT warmups, or post-speak teardown. `run()` owns **speech + one terminal output** (`sayAndListen` / `continueTo` / `endCall` / …). Keep side effects on the lifecycle hooks:
27
+
28
+ | Hook | Role | Async guidance |
29
+ | --- | --- | --- |
30
+ | **`before(ctx)`** | CAN gate + prepare | Authorize (`return false` to reject). **Prep / warm** integrations here. Prefer **spawn without awaiting** when the token is for *later* Nodes (do not block first SSE — target < 1.5s). `await` only when the gate itself needs the result. |
31
+ | **`listen(ctx)`** | Next listen binding | Register intentions / extract **before** speech; no I/O. |
32
+ | **`run(ctx)`** | Speak + terminal action | Copy and routing only. Do not nest “fetch then greet” unless the speak **cannot** proceed without the result. |
33
+ | **`after(ctx, result)`** | Teardown | Fire-and-forget analytics stamps that are not on the critical path, release handles, log completion. Must not speak. |
34
+ | **`catch(ctx, error)`** | Recovery | Only when overridden. |
35
+
36
+ Detached promises from `before()` should land in a Nest service / `Map<conversationId, Promise<…>>` (or similar), not assume late `ctx.memory` writes persist after the turn. Later Nodes `await` that shared promise when they need the value.
37
+
38
+ ## `listen()` must match the ask
39
+
40
+ Whatever you just asked for, `listen()` should advertise:
41
+
42
+ - Intention names + boosts for that answer class
43
+ - `hints` for the Brain (short, operational)
44
+ - `extract` fields with **constraint-aware** `description`s when collecting data
45
+ - `resolveIntention` for cheap local matches (skip Brain when obvious) — include short answers like “new one” / “yes” / “ASAP” when the ask is constrained
46
+ - **Open-ended discovery:** prefer `INTENTION_CASCADE_PHASE.Scan` for the catch-all answer intention (so Brain can classify short/ambiguous turns). Use Match only for clear proceed phrases; keep FAQ as Force. `resolveIntention` should skip Brain for long substantive answers (≥ ~12 chars) and re-ask soft affirmatives locally — do **not** Match every ≥4-char utterance (that starves Scan)
47
+ - **Multi-choice soft affirmatives** (“yeah, why not”, bare “sure”) that do **not** name an option → re-ask which lane locally; never leave that to Brain/unknown ([conversation-design.md](./conversation-design.md#do-not-auto-pick-lanes))
48
+ - After a resolved binary fork (existing vs new, yes/no), **stamp memory** so the next menu advances; otherwise unknown recovery and re-prompts keep re-asking the same choice
49
+ - Unknown portal: Supervisor re-tries the **origin listen** on the next utterance before unknown’s own candidates — keep origin `resolveIntention` strong so restatements land on this turn
50
+ - `timeoutSeconds` when the answer type needs a non-default window
51
+
52
+ Do not leave a broad “collect name” listen active while you are asking for SMS consent.
53
+
54
+ ## Extracts teach the Brain
55
+
56
+ `description` is part of the product. Spell the rule:
57
+
58
+ > North American mobile: exactly 10 digits. Prefer the first 10 digit characters; ignore trailing words. Fail if fewer than 10.
59
+
60
+ Local parsers and GOT must agree. Prefer framework boxed helpers from `@guidify-ai/vapi-studio/identity` (`collectedEmail`, `EMAIL_EXTRACT_DESCRIPTION`); app helpers (`collectedPhone`, `collectedPersonName`, …) for the rest — called from `onExtracted`.
61
+
62
+ ## Memory writes are guarded
63
+
64
+ Never write PII from raw `userText` without a validator (`looksLikePersonName`, phone normalizer, email parser). Fail closed → re-ask the same CTA.
65
+
66
+ **Never speak untrusted input:** do not `say(ctx.userText)`, Brain `reason`, or judge `reasoning` to the caller. Off-topic / injection-shaped speech → `studio.isUnknownTransition` or structured recovery — see [brain-and-prompt-injection.md](./brain-and-prompt-injection.md).
67
+
68
+ **Never speak implementation labels:** node ids, class names, intention/transition names, event types, or graph state (“we were at X”) — see [conversation-design.md](./conversation-design.md) § Human-like UX. `listen()` hints/notes are Brain-only, not TTS.
69
+
70
+ ## Flow jumps
71
+
72
+ | Action | When |
73
+ | --- | --- |
74
+ | `sayAndListen` | Stay on this Node; wait for the next user turn |
75
+ | `continueTo({ nodeId })` | Same Conversation, same flow, different Node (often same turn entry speak) — **silent** unless the destination needs a new product CTA; no filler restating the last beat ([conversation-design.md](./conversation-design.md#copy-hygiene)) |
76
+ | `handoff({ to })` | Workflow module switch (Squad / multi-assistant). Same silent-handoff rule. Only when a workflow is loaded |
77
+
78
+ Keep live PoCs on one assistant + `continueTo` unless you are deliberately testing Squad.
79
+
80
+ ## Portals stay portals
81
+
82
+ Transfer, pause, mad, unknown, still-there, goodbye remain high-priority portals. Happy-path Nodes must not reimplement them. Unknown recovery copy can be shared with the unknown portal so menus stay consistent.
83
+
84
+ ### Mad: one re-engage, then human (hard rule)
85
+
86
+ Do **not** keep reasoning with, apologizing to, or negotiating angry callers across multiple turns. Mad people escalate; a bot that stays in de-escalation loops wastes the call and makes things worse.
87
+
88
+ | Policy | Detail |
89
+ | --- | --- |
90
+ | **First mad hit** | One short re-engage (acknowledge + invite them to continue productively), then listen |
91
+ | **Second mad hit** (still heated) | **Transfer to a human** (or after-hours block) — no third apology loop |
92
+ | **Calm continue / goodbye** | May leave the portal without transferring |
93
+ | **Do not** | Invent long empathy scripts, argue, or keep `sayAndListen` de-escalation forever |
94
+
95
+ Framework note: mad is sticky (other portals suppressed while active) so the next utterance stays on the mad listen — that is for routing control, not an excuse to chat with anger. App mad Nodes must enforce the one-re-engage → transfer policy.
96
+
97
+ ### Portal re-engage must end with one CTA (hard rule)
98
+
99
+ Any portal turn that **defers** the caller’s ask (transfer re-engage, mad first apology, after-hours block) must still obey **one CTA per turn**. Do not leave them on a dead-end statement.
100
+
101
+ | Bad | Good |
102
+ | --- | --- |
103
+ | “I understand you want a human, but I can resolve it for you.” *(no ask)* | “I can often take care of this without a transfer — **how can I help you?** Or say you'd still like a person.” |
104
+ | “I'm sorry you're frustrated.” *(stops)* | “I'm sorry you're dealing with this — **tell me what's going on**, and I'll help.” |
105
+
106
+ Listen for that turn must accept: insist again (transfer/mad), calm continue, **or** a product need if you offered help.
107
+
108
+ ## Flow YAML is paths (+ optional condition transitions)
109
+
110
+ `flow.yaml` lists node ids, class names, intentions, portal flags, priority, and optional **`transitions`** (pure `when` gates → `to` node, with `force` to skip Brain). Copy and extract schemas live in Node source — not in YAML, not in app README northern stars.
111
+
112
+ **Prefer code intentions** (`INTENTION_CASCADE_PHASE.Force` \| `.Match` \| `.Scan`) when a gate needs memory/history/regex freedom — register via `VapiStudioModule.forRoot({ intentions })`. YAML transitions stay useful for diagram edges; code force intentions run first in the cascade.
113
+
114
+ Use **dedicated Nodes** for identity gap-fill (consent, phone, name). Declare the same gates as `transitions` and/or force intentions when you need the graph to show *why* the bot must leave a Node — duplicating a `continueTo` is fine.
115
+
116
+ `continueTo({ text })` is spoken on the wire — do not repeat copy (especially the caller’s name) that a prior `say` in the same pass already delivered. See [conversation-design.md](./conversation-design.md#personal-treatment-names).
@@ -0,0 +1,51 @@
1
+ # UI and API identity
2
+
3
+ Hard rules for Studio UI ↔ Nest APIs and durable records. Applies to framework Studio SPA, app-served JSON, and any new FE surface.
4
+
5
+ ## Rules (summary)
6
+
7
+ 1. **External comms use UUID only** — paths, query params, request/response bodies, links, and client state identify records by `uuid`. Never send or accept numeric/`bigint` `id` from the FE (or any external client).
8
+ 2. **BE stores `id` + `uuid`** — every durable entity has an internal integer/`bigint` primary key (`id`) for joins, filters, sorts, and indexes, and a unique `uuid` for all external identity. Services may use `id` internally; repositories that face HTTP resolve by `uuid`.
9
+ 3. **Every exposed entity has a human `label`** — API DTOs return `label` so the UI can title rows, breadcrumbs, and headings **without** printing UUIDs. Prefer `label` in the UI; use `uuid` only as the opaque key (URL segment, React key, fetch param).
10
+
11
+ ## Field contract
12
+
13
+ | Layer | Internal PK | External id | Human display |
14
+ | --- | --- | --- | --- |
15
+ | DB / TypeORM | `id` (`bigint` / serial) | `uuid` (unique, indexed) | `label` (varchar) and/or derived DTO `label` |
16
+ | HTTP JSON | **omit** | `uuid` | `label` (required on list + detail) |
17
+ | Studio UI | never | URL + API only | always show `label` |
18
+
19
+ Do **not** name the external field `id` in new APIs. Use `uuid` so clients never confuse it with the internal key.
20
+
21
+ ```json
22
+ {
23
+ "uuid": "4a13e554-972b-4713-b208-15b71bff0493",
24
+ "label": "Sample Studio · ACTIVE · Sep 6, 1:30 PM",
25
+ "status": "ACTIVE"
26
+ }
27
+ ```
28
+
29
+ ```http
30
+ GET /conversations/api/:uuid
31
+ ```
32
+
33
+ Not: `GET /conversations/api/42` or `{ "id": 42 }`.
34
+
35
+ ## Labels
36
+
37
+ - **Stable enough to read** — short title a human understands (project name, caller display, “Studio call · ended”, channel + time).
38
+ - **Not a dump of the UUID** — never use the raw uuid string as `label`.
39
+ - **Computed OK** — if there is no stored column, the service may build `label` when mapping to the DTO (must still always be present on FE responses).
40
+ - **UI rule** — list rows, page titles, and toasts use `label`; monospace UUID only in operator “copy id” / debug affordances if needed.
41
+
42
+ ## Backend practice
43
+
44
+ - Joins, `ORDER BY`, range filters, analytics aggregations → `id` (and indexed columns).
45
+ - `findByUuid` / route params → `uuid`.
46
+ - Never leak `id` in JSON, logs meant for operators who share URLs, or Vapi-facing payloads that the FE might echo.
47
+ - Nested resources: parent and child both identified by `uuid` in URLs (`/…/:conversationUuid/events/:eventUuid`).
48
+
49
+ ## Migration note
50
+
51
+ Older tables may still use UUID as `@PrimaryGeneratedColumn('uuid')` named `id`. Treat that as transitional: new entities follow `id` + `uuid` + `label`; when touching persistence for an existing entity, migrate toward this shape and rename API fields to `uuid` + `label` in the same change.
@@ -0,0 +1,59 @@
1
+ # Creating an application
2
+
3
+ Start from the public starter **[vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project)**. That repo is the Nest shell for your bot: `flow.yaml`, Vapi routes, Docker, agent rules. This package (`@guidify-ai/vapi-studio`) is the framework it depends on — not an app template.
4
+
5
+ Do not put production secrets or customer flows in the framework repository.
6
+
7
+ ## 1. Clone the starter
8
+
9
+ ```bash
10
+ git clone git@github.com:guidify-ai/vapi-studio-project.git my-bot
11
+ cd my-bot
12
+ cp .env.example .env
13
+ # Edit PROJECT_NAME=… (PROJECT_SLUG optional — defaults from the name)
14
+ yarn install
15
+ yarn start # Docker Postgres + app + ngrok → prints Vapi URLs
16
+ ```
17
+
18
+ `yarn install` pulls `@guidify-ai/vapi-studio@0.1.0` and stamps Cursor/Claude rules + `AGENTS.md`. Postinstall syncs `config/project.identity.json` from `PROJECT_NAME` / `PROJECT_SLUG` (name + slug only).
19
+
20
+ ### Live voice prerequisites
21
+
22
+ | Need | Detail |
23
+ | --- | --- |
24
+ | **Vapi** | Client has Vapi org access. `VAPI_API_KEY=` in `.env.example` is **required** (leave empty in the example; fill in `.env`). **Vapi is paid** — budget for Vapi usage/plan charges as well as Twilio. |
25
+ | **Twilio only (for now)** | Vapi supports other carriers; this stack currently assumes **Twilio**. Put `TWILIO_ACCOUNT_SID` / `TWILIO_AUTH_TOKEN` / `TWILIO_FROM_NUMBER` in the app `.env`. |
26
+ | **Trust Hub / +1** | Twilio Trust Hub + voice geo must allow outbound to **+1**. Trial / blocked geo → `Account not allowed to call +1…`. |
27
+ | **Balance** | Recommend ~**$30** with auto-recharge to $30 when balance hits ~$10. |
28
+ | **Same account** | `VAPI_PHONE_NUMBER_ID` must be a number imported in Vapi from **that** Twilio account. |
29
+
30
+ ## 2. Wire Vapi
31
+
32
+ Paste the printed URLs into your Vapi assistant:
33
+
34
+ | Setting | Endpoint |
35
+ | --- | --- |
36
+ | **Webhook** | `{PUBLIC_BASE_URL}/vapi/webhook` |
37
+ | **Custom LLM** | `{PUBLIC_BASE_URL}/vapi/chat/completions` |
38
+
39
+ Each starter fork is its own deploy (own host / port / ngrok URL). Paths are host-scoped — no project UUID in the URL.
40
+
41
+ ## 3. Build your conversation
42
+
43
+ 1. Add `@Injectable()` classes extending `AgentNode<YourSchema>` under `src/conversation/`
44
+ 2. Register them in `VapiStudioModule.forRoot({ nodes: [...] })` in `app.module.ts`
45
+ 3. Reference them from `config/flow.yaml`
46
+ 4. Grow `src/vapi/` as needed (webhook strategies, tools) — see the [showcase](https://github.com/guidify-ai/vapi-studio-landing-page-sample-model) for a full Planner PoC
47
+
48
+ Operator UI (`/flow`, `/conversations`) comes from the framework SPA mounted by the starter.
49
+
50
+ ## 4. Keep going
51
+
52
+ - [Vapi adapter](../guide/vapi-adapter.md) — Custom LLM SSE + webhooks
53
+ - [Module setup](../guide/module-setup.md) — `VapiStudioModule.forRoot`
54
+ - [Best practices](../best-practices/README.md) — conversation design doctrine
55
+ - [Runtime API](../reference/runtime-api.md) — shipped contracts
56
+
57
+ ## Framework contributors
58
+
59
+ Clone [vapi-studio](https://github.com/guidify-ai/vapi-studio) only when changing the library itself (`yarn build`, `yarn test`). Apps always start from [vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project).
@@ -0,0 +1,23 @@
1
+ # Example applications
2
+
3
+ ## Starter (start here)
4
+
5
+ **[vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project)** — blank Nest shell for a new bot.
6
+
7
+ ```bash
8
+ git clone git@github.com:guidify-ai/vapi-studio-project.git my-bot
9
+ cd my-bot && cp .env.example .env
10
+ # Edit PROJECT_NAME=…
11
+ yarn install && yarn start
12
+ ```
13
+
14
+ Surface after `yarn start`:
15
+
16
+ - Operator SPA: `/flow`, `/conversations`
17
+ - Ingress: `/vapi/webhook`, `/vapi/chat/completions` (host-scoped)
18
+
19
+ Step-by-step: [Creating an app](./creating-an-app.md).
20
+
21
+ ## Showcase (reference implementation)
22
+
23
+ **[vapi-studio-landing-page-sample-model](https://github.com/guidify-ai/vapi-studio-landing-page-sample-model)** — Planner LLM demo (not a blank starter). Depends on `@guidify-ai/vapi-studio@0.1.0` (or `file:../vapi-studio` while developing the next package version). Use it to see richer Vapi strategies, funnels, and multi-node flows.
@@ -0,0 +1,27 @@
1
+ # Building applications — overview
2
+
3
+ **[@guidify-ai/vapi-studio](https://www.npmjs.com/package/@guidify-ai/vapi-studio)** is an npm library. Your bot is a NestJS app that depends on it.
4
+
5
+ **Start every new bot by cloning [vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project)** — then rename, set `PROJECT_NAME`, and extend nodes / `flow.yaml`.
6
+
7
+ ```text
8
+ my-bot/ ← clone of vapi-studio-project
9
+ ├── package.json ← "@guidify-ai/vapi-studio": "0.1.0"
10
+ ├── config/
11
+ │ ├── project.identity.json ← name + slug (from .env)
12
+ │ └── flow.yaml
13
+ ├── src/
14
+ │ ├── conversation/ ← your agent steps
15
+ │ └── vapi/ ← /vapi/webhook + /vapi/chat/completions
16
+ └── docker-compose.yaml
17
+ ```
18
+
19
+ | Repo | Role |
20
+ | --- | --- |
21
+ | [vapi-studio](https://github.com/guidify-ai/vapi-studio) | Framework → npm `@guidify-ai/vapi-studio` |
22
+ | [vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project) | **Starter** — fork this to ship a bot |
23
+ | [vapi-studio-landing-page-sample-model](https://github.com/guidify-ai/vapi-studio-landing-page-sample-model) | Showcase (Planner LLM) — study patterns, not a blank template |
24
+
25
+ Do not add customer-specific agent steps under the framework `src/` — only in your starter fork.
26
+
27
+ [Creating an app](./creating-an-app.md) · [Example apps](./example-apps.md)
@@ -0,0 +1,52 @@
1
+ # Installation
2
+
3
+ ## New bot (recommended)
4
+
5
+ You do **not** install the framework into an empty folder by hand. Clone the starter — it already depends on the package:
6
+
7
+ ```bash
8
+ git clone git@github.com:guidify-ai/vapi-studio-project.git my-bot
9
+ cd my-bot
10
+ cp .env.example .env
11
+ yarn install # installs @guidify-ai/vapi-studio@0.1.0 + stamps agent rules
12
+ yarn start
13
+ ```
14
+
15
+ Starter: [guidify-ai/vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project)
16
+ Package: [npm `@guidify-ai/vapi-studio`](https://www.npmjs.com/package/@guidify-ai/vapi-studio)
17
+
18
+ Vapi Studio apps are **self-hosted** and **Dockerized**. There is no Guidify-hosted runtime. Vapi is the cloud voice channel; your containers serve Custom LLM + webhook.
19
+
20
+ ## Requirements
21
+
22
+ - **Node.js 22+**
23
+ - **Yarn 1.x** or npm
24
+ - **Docker** + Compose
25
+ - **[ngrok](https://ngrok.com/download)** for local HTTPS to Vapi
26
+ - A **Vapi** account
27
+
28
+ On install, the package **postinstall** stamps agent rules (`AGENTS.md`, `.cursor/rules/…`) into the starter when present.
29
+
30
+ ## What is published (and what is not)
31
+
32
+ The npm tarball includes **`dist/`**, **`docs/`**, **`agent/`**, **`scripts/`**, **`LICENSE`**, **`README.md`**. It does **not** include `.env`, secrets, or application bots.
33
+
34
+ ## Contribute to the framework (optional)
35
+
36
+ Only when changing the library itself:
37
+
38
+ ```bash
39
+ git clone git@github.com:guidify-ai/vapi-studio.git
40
+ cd vapi-studio
41
+ yarn install
42
+ yarn build
43
+ yarn test
44
+ ```
45
+
46
+ ## Agent refs (manual)
47
+
48
+ ```bash
49
+ INIT_CWD=$PWD node node_modules/@guidify-ai/vapi-studio/scripts/install-agent-refs.cjs
50
+ ```
51
+
52
+ Next: [Quick start](./quick-start.md) · [Creating an app](../building-apps/creating-an-app.md)
@@ -0,0 +1,23 @@
1
+ # Introduction
2
+
3
+ **Vapi Studio** (`@guidify-ai/vapi-studio`) is a NestJS toolkit for **deterministic voice agents** on [Vapi](https://vapi.ai).
4
+
5
+ Your bot is a **separate NestJS app**. Start from **[vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project)** — clone it, set `PROJECT_NAME`, then own nodes, `flow.yaml`, secrets, and Docker.
6
+
7
+ ## What you get
8
+
9
+ - Typed agent steps (`AgentNode`), Supervisor routing, optional Brain at listen boundaries
10
+ - Forms, tasks, event bus, Studio operator SPA assets
11
+ - Docs and agent rules stamped into consumer apps on install
12
+
13
+ ## What you build
14
+
15
+ A fork of [vapi-studio-project](https://github.com/guidify-ai/vapi-studio-project) that depends on `@guidify-ai/vapi-studio@0.1.0`, with Vapi Custom LLM + webhook at `/vapi/...` on your host.
16
+
17
+ ## What this is not
18
+
19
+ - A no-code builder or open-ended LLM agent platform
20
+ - A Guidify-hosted runtime (you self-host)
21
+ - A monorepo of customer bots (each bot is its own starter fork)
22
+
23
+ Next: [Installation](./installation.md) · [Quick start](./quick-start.md) · [Concepts](../guide/concepts.md)
@@ -0,0 +1,23 @@
1
+ # Quick start
2
+
3
+ Clone the public starter, name the project, run it.
4
+
5
+ ```bash
6
+ git clone git@github.com:guidify-ai/vapi-studio-project.git my-bot
7
+ cd my-bot
8
+ cp .env.example .env
9
+ # Edit PROJECT_NAME=… (PROJECT_SLUG optional)
10
+ yarn install
11
+ yarn start # Docker + Postgres + ngrok → prints Vapi URLs
12
+ ```
13
+
14
+ Paste into your Vapi assistant:
15
+
16
+ | Setting | URL |
17
+ | --- | --- |
18
+ | Webhook | `{PUBLIC_BASE_URL}/vapi/webhook` |
19
+ | Custom LLM | `{PUBLIC_BASE_URL}/vapi/chat/completions` |
20
+
21
+ Then extend `src/conversation/` and `config/flow.yaml`. Full walkthrough: [Creating an app](../building-apps/creating-an-app.md).
22
+
23
+ Framework contributors (changing the library, not starting a bot): clone [vapi-studio](https://github.com/guidify-ai/vapi-studio), `yarn build`, `yarn test` — see [Installation](./installation.md).
@@ -0,0 +1,103 @@
1
+ # Concepts
2
+
3
+ Mental model for Vapi Studio. API detail: [Runtime API](../reference/runtime-api.md).
4
+
5
+ ## Conversation
6
+
7
+ A **Conversation** is one caller session:
8
+
9
+ - **Framework-generated `conversationId`** — canonical id (never replace with Vapi call id)
10
+ - **Variables** — seeded once at entry (channel, caller id, feature flags)
11
+ - **Memory** — mutable working state for the call
12
+ - **History** — turn log for Brain and forensics
13
+ - **Postgres row** — durable lifecycle; in-memory `SupervisedConversation` for active calls
14
+
15
+ ## Turn lifecycle
16
+
17
+ Each user utterance (or silence timeout) triggers the **Supervisor** routing cascade:
18
+
19
+ ```text
20
+ 1. Force intentions (code + YAML transitions)
21
+ 2. Condition transitions (memory expressions)
22
+ 3. Match intentions on current node
23
+ 4. Listen resolve (extract / resolveIntention — may skip Brain)
24
+ 5. Brain scan (portal + node intentions)
25
+ 6. Walk nodes (boot via before(), run)
26
+ ```
27
+
28
+ On failure to route, the framework falls back to `studio.isUnknownTransition` rather than throwing.
29
+
30
+ ## Agent steps
31
+
32
+ Agent steps are NestJS injectable classes extending `AgentNode`:
33
+
34
+ ```typescript
35
+ export class AskNameNode extends AgentNode<MySchema> {
36
+ async before(ctx) { return true; } // CAN gate
37
+ async listen(ctx) { /* register extract */ }
38
+ async run(ctx) {
39
+ return ctx.output.sayAndListen("What's your first name?", {
40
+ extract: { fields: [{ key: 'firstName', /* … */ }] },
41
+ });
42
+ }
43
+ }
44
+ ```
45
+
46
+ Declare methods in **runtime order**: `before` → `listen` → `run` → `after` → `catch`.
47
+
48
+ Agent steps speak only through `ctx.output` — never Vapi JSON directly.
49
+
50
+ ## Flow YAML
51
+
52
+ `config/flow.yaml` declares:
53
+
54
+ - **start** node id
55
+ - **nodes** — class binding, intentions, `portal`, `priority`, `terminal`
56
+ - **transitions** — conditional jumps (`when`, `force`, `reason`)
57
+ - **portals** — global interrupts (goodbye, transfer, still-there, mad)
58
+
59
+ Lane changes inside one assistant use `ctx.output.continueTo({ nodeId })`.
60
+
61
+ ## Intentions
62
+
63
+ Named signals routing the supervisor:
64
+
65
+ - **Standard** — `studio.isGoodbye`, `studio.isTransferToHuman`, `studio.isStillThere`, …
66
+ - **App** — `isAcceptedAppointment`, `isDidNotReceiveForm`, …
67
+ - **Code intentions** — `CodeIntention` with `INTENTION_CASCADE_PHASE` (`Force` \| `Match` \| `Scan`; see [Runtime API](../reference/runtime-api.md))
68
+
69
+ ## Brain
70
+
71
+ `BrainService` implements `scan` / `clarify` / `judge`. Model and confidence threshold are set in **`VapiStudioModule.forRoot({ brain })`** — not env vars.
72
+
73
+ Live-call rules: no pre-scan listen-hold on ChatGPT path; scan abort ≤ 3s; target first speech < 1.5s.
74
+
75
+ ## Vapi integration
76
+
77
+ Vapi Custom LLM and webhooks translate wire format ↔ Vapi Studio I/O.
78
+
79
+ - **Live calls** — SSE streaming, tool calls for end/transfer/handoff
80
+ - **Flow Studio** — HTTP text harness on the same supervisor (application feature for local dev)
81
+
82
+ ## Forms
83
+
84
+ `ctx.forms.open()` (live Vapi) or `expose()` (blocking Studio) + a **dispose adapter** delivers fillable surfaces. Submit → `claimSubmitted()` or blocking resume.
85
+
86
+ ## Events
87
+
88
+ Vapi Studio is **event-driven**. `EventService` emits `StudioEvent`s (`STUDIO_EVENTS` constants) onto the console, an in-process Node bus (`onStudioEvent`), and optional Nest `eventListeners`. Attach custom listeners for any logic you need — Guidify companion tools use the same hooks; OSS does not limit what you build there. Logs must explain **why** a turn routed. See [Events & logging](../reference/events-and-logging.md) · [Extending events](../guides/extending-events.md).
89
+
90
+ ## Conversation limits
91
+
92
+ Every Conversation has always-on caps (`limits.maxTurns` / `limits.maxDurationMs`, defaults 40 / 20m). Fail-closed goodbye + `endCall` when exceeded — see [Runtime API](../reference/runtime-api.md) and [conversation design](../best-practices/conversation-design.md).
93
+
94
+ ## Resume & recovery (post-MVP)
95
+
96
+ Framework ships optional helpers; **MVP apps start fresh each call** unless you explicitly opt in:
97
+
98
+ - **Cross-call resume** — `ConversationResumeService` (same caller within ~10 minutes)
99
+ - **Crash recovery** — `restoreAllActive()` on process boot (single-process; not multi-node failover)
100
+
101
+ ## Workflows (optional)
102
+
103
+ Multi-assistant **Squad** handoffs via `workflow.yaml` — see [Workflow & Squad](./workflow-squad.md). Many apps use **one assistant** and `continueTo` lanes instead.
@@ -0,0 +1,102 @@
1
+ # Forms
2
+
3
+ Structured collection outside the voice channel — SMS links, Studio modals, first-party HTML.
4
+
5
+ ## API surface (`ctx.forms`)
6
+
7
+ | Method | Use when |
8
+ | --- | --- |
9
+ | `expose(spec)` | **Blocking** — holds the channel turn until submit (Flow Studio modal) |
10
+ | `open(spec)` | **Non-blocking** — deliver + speak, finish SSE turn, wait for submit later (live Vapi) |
11
+ | `resend()` | Caller did not receive SMS/link — re-run dispose on same `exposeId` |
12
+ | `claimSubmitted()` | Take values after `open()` submit |
13
+ | `hasPending()` | Open expose awaiting submit (ghosting must no-op) |
14
+ | `hasUnclaimedSubmit()` | Submitted but not yet claimed |
15
+
16
+ ## Dispose adapter
17
+
18
+ Apps provide `FormDisposeAdapter`:
19
+
20
+ ```typescript
21
+ @Injectable()
22
+ export class LinkFormDisposeAdapter implements FormDisposeAdapter {
23
+ readonly id = 'studio-first-party-html';
24
+ readonly branch = 'html_link'; // delivery lane for forensics
25
+
26
+ async dispose(payload: FormDisposePayload): Promise<void> {
27
+ // Publish URL, send SMS, open Studio modal…
28
+ forms.ack(payload.exposeId);
29
+ }
30
+ }
31
+ ```
32
+
33
+ `disposeContext` on expose is opaque app data (e.g. `contactPhone`, `channel`) stored for `resend()`.
34
+
35
+ ### Twilio SMS (prepared)
36
+
37
+ Framework ships `TwilioSmsFormDisposeAdapter` (`branch: sms`). Wire it when you want SMS link delivery:
38
+
39
+ ```ts
40
+ VapiStudioModule.forRoot({
41
+ formDisposeAdapter: TwilioSmsFormDisposeAdapter,
42
+ // …
43
+ })
44
+ ```
45
+
46
+ Reserved env (default **dry-run on** — no live Twilio calls until you opt in):
47
+
48
+ | Variable | Purpose |
49
+ | --- | --- |
50
+ | `TWILIO_ACCOUNT_SID` | Account SID |
51
+ | `TWILIO_AUTH_TOKEN` | Auth token |
52
+ | `TWILIO_FROM_NUMBER` | E.164 from-number **or** |
53
+ | `TWILIO_MESSAGING_SERVICE_SID` | Messaging Service SID |
54
+ | `TWILIO_SMS_DRY_RUN` | Default `1` — still emits durable `OUTBOUND_NOTIFICATION` (`status: dry_run`) and ACKs without API. Set `0` for live send (requires peer package `twilio`) |
55
+
56
+ Each send (dry-run or live) goes through `EventService.persist` as `OUTBOUND_NOTIFICATION` (in-process Node bus + Nest listeners). Failures emit `OUTBOUND_NOTIFICATION_ERROR`.
57
+
58
+ `disposeContext` for this driver:
59
+
60
+ ```ts
61
+ disposeContext: {
62
+ contactPhone: '5550100999', // or E.164 / `to` / `phone`
63
+ // formUrl optional — defaults to `{PUBLIC_BASE_URL}/forms/{exposeId}`
64
+ // body?: 'optional custom SMS text'
65
+ }
66
+ ```
67
+
68
+ Set `PUBLIC_BASE_URL` (ngrok/public HTTPS origin) so the texted link is reachable. Override with an explicit `formUrl` when the fill page lives elsewhere.
69
+
70
+ ## Events
71
+
72
+ | Event | When |
73
+ | --- | --- |
74
+ | `FORM_SENDOUT` | Early — branch + delivery lane before ACK |
75
+ | `FORM_EXPOSE` / `FORM_DELIVERED` | Adapter delivered |
76
+ | `FORM_RESEND` / `FORM_RESENT` | Caller never got link |
77
+ | `FORM_SUBMITTED` | Values received |
78
+ | `FORM_DELIVER_TIMEOUT` / `FORM_FILLOUT_TIMEOUT` | Timeouts |
79
+
80
+ ## HTML helpers
81
+
82
+ - `renderFormHtml(handle)` — JSON fields → fillable page
83
+ - `renderFormThanksHtml()` / `renderFormGoneHtml()`
84
+
85
+ Apps own HTTP routes (`GET/POST /forms/:exposeId`). Document routes in the application repository.
86
+
87
+ ## Timeouts
88
+
89
+ - **ACK** — `STUDIO_FORM_ACK_MS` (default 15s) after dispose
90
+ - **Fillout** — `filloutTimeoutMs` on expose spec (open path timer)
91
+
92
+ ## Doctrine
93
+
94
+ - Form fail → **one CTA**: voice fallback
95
+ - “Didn’t get text” → resend + “did you get it this time?” — not stacked with fill prompts
96
+
97
+ Full rules: [Identity and PII](../best-practices/identity-and-pii.md)
98
+
99
+ ## Related
100
+
101
+ - [Runtime API](../reference/runtime-api.md) — forms contracts
102
+ - [Identity and PII](../best-practices/identity-and-pii.md)