@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,41 @@
1
+ # Security
2
+
3
+ ## Framework repository
4
+
5
+ This repo should contain **no secrets**:
6
+
7
+ - No LLM API keys (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `XAI_API_KEY`) or Vapi tokens in source or committed config
8
+ - No real caller PII in fixtures or tests
9
+ - Use obvious placeholders in docs (e.g. fictional phone numbers in best-practice examples)
10
+
11
+ ## Consuming applications
12
+
13
+ Your NestJS application should:
14
+
15
+ - Keep secrets in `.env` — **gitignored**
16
+ - Ship `.env.example` with empty values only
17
+ - Treat runtime logs (`LOG_DIR`) as sensitive in production
18
+
19
+ ## Prompt injection (caller speech)
20
+
21
+ Caller audio / ASR is **untrusted**. A user asking for a pancake recipe, jailbreak, or “ignore previous instructions” must not change agent behavior or produce spoken off-topic answers.
22
+
23
+ Vapi Studio’s deterministic model:
24
+
25
+ - **Brain** returns JSON only (intention scores + allowlisted extract fields) — never caller-facing prose
26
+ - **Supervisor + agent steps** own routing and **all spoken copy**
27
+ - Framework Brain adapters apply [`brainUntrustedInputRules`](../best-practices/brain-and-prompt-injection.md), candidate allowlists, extract sanitization, and a **pre-LLM fail-closed gate** on injection-shaped ASR (all stock JSON-LLM providers share this path)
28
+
29
+ App authors must not echo raw `userText` or Brain `reason` to the caller, and must not add unconstrained sidecar LLM chat on the same call.
30
+
31
+ Full guide: **[Brain and prompt injection](../best-practices/brain-and-prompt-injection.md)**.
32
+
33
+ ## Test doubles
34
+
35
+ Framework and app tests may use values like `sk-mock-not-real` — never real API keys.
36
+
37
+ ## Related
38
+
39
+ - [Environment variables](./environment-variables.md)
40
+ - [Creating an app](../building-apps/creating-an-app.md)
41
+ - [Brain and prompt injection](../best-practices/brain-and-prompt-injection.md)
@@ -0,0 +1,49 @@
1
+ # Standard intentions
2
+
3
+ Framework-provided intention constants (`STANDARD_INTENTIONS`).
4
+
5
+ ## Portal / global
6
+
7
+ | Constant | Typical portal node |
8
+ | --- | --- |
9
+ | `studio.isGoodbye` | Goodbye → `endCall` |
10
+ | `studio.isTransferToHuman` | Transfer (re-engage once, then transfer) |
11
+ | `studio.isStillThere` | Still-there (ghosting / silence) |
12
+ | `studio.isMad` | Mad escalation |
13
+ | `studio.isPause` | Pause |
14
+ | `studio.isUnknownTransition` | Unknown recovery (route fallback) |
15
+ | `studio.isContinue` | Resume / continue prior conversation |
16
+
17
+ ## Polarity
18
+
19
+ | Constant | Use |
20
+ | --- | --- |
21
+ | `studio.isPositive` | Yes / affirm |
22
+ | `studio.isNegative` | No / decline |
23
+
24
+ ## App naming conventions
25
+
26
+ From [Node conventions](../guides/node-conventions.md):
27
+
28
+ - **Actions:** `is{VerbPast}…` — `isCollectedFirstName`, `isAcceptedAppointment`
29
+ - **Polarity:** `studio.isPositive` / `studio.isNegative` when appropriate
30
+ - **Framework prefix:** `studio.*` for standard intentions only
31
+
32
+ ## Code intentions
33
+
34
+ Extend **`CodeIntention`** (register via `VapiStudioModule.forRoot({ intentions })`). Cascade phases (`INTENTION_CASCADE_PHASE`):
35
+
36
+ | Phase | When |
37
+ | --- | --- |
38
+ | `Force` | Preempt before current node (e.g. need SMS consent) |
39
+ | `Match` | Node eligibility |
40
+ | `Scan` | Brain candidate |
41
+
42
+ ## Application examples
43
+
44
+ Your app defines intentions in `flow.yaml`, e.g. `isCollectedFirstName`, `isAcceptedAppointment`, `isDidNotReceiveForm`.
45
+
46
+ ## Related
47
+
48
+ - [Flow schema](./flow-schema.md)
49
+ - [Concepts](../guide/concepts.md)
@@ -0,0 +1,15 @@
1
+ # Testing
2
+
3
+ ```bash
4
+ yarn install
5
+ yarn build
6
+ yarn test # tsc + node:test under test/*.test.mjs
7
+ ```
8
+
9
+ Mocked unit tests — no live Vapi or paid Brain in the default suite.
10
+
11
+ Application integration tests live in **your app repository**, not here.
12
+
13
+ ## Related
14
+
15
+ - [Installation](../getting-started/installation.md)
@@ -0,0 +1,17 @@
1
+ /**
2
+ * EXAMPLE — Nest DI listener. Register via:
3
+ * VapiStudioModule.forRoot({ eventListeners: [AuditEventListener] })
4
+ */
5
+ import { Injectable, Logger } from '@nestjs/common';
6
+ import type { StudioEvent, StudioEventListener } from '@guidify-ai/vapi-studio';
7
+
8
+ @Injectable()
9
+ export class AuditEventListener implements StudioEventListener {
10
+ private readonly log = new Logger(AuditEventListener.name);
11
+
12
+ async handle(event: StudioEvent): Promise<void> {
13
+ if (event.type === 'ROUTE_DECISION' || event.type === 'ANALYTICS_TAG') {
14
+ this.log.debug(`${event.type} ${event.conversationId ?? ''}`);
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * EXAMPLE — copy into your Nest app (e.g. src/events/redis-event-bridge.ts).
3
+ * Not executed by the framework package.
4
+ *
5
+ * Bridges onStudioEvent → Redis Streams so a separate consumer (analytics,
6
+ * warehouse, …) can read the same way Roofr → ecosystem-analytics does.
7
+ *
8
+ * Requires: yarn add ioredis
9
+ * Env: STUDIO_EVENTS_REDIS_URL, STUDIO_EVENTS_REDIS_STREAM (optional)
10
+ */
11
+ import { onStudioEvent, type StudioEvent } from '@guidify-ai/vapi-studio';
12
+
13
+ // import Redis from 'ioredis';
14
+
15
+ export function registerRedisEventBridge(): void {
16
+ const url = process.env.STUDIO_EVENTS_REDIS_URL?.trim();
17
+ const stream =
18
+ process.env.STUDIO_EVENTS_REDIS_STREAM?.trim() || 'studio:events';
19
+ if (!url) {
20
+ // eslint-disable-next-line no-console
21
+ console.warn(
22
+ '[events] STUDIO_EVENTS_REDIS_URL unset — Studio events stay in-process only',
23
+ );
24
+ return;
25
+ }
26
+
27
+ // const redis = new Redis(url);
28
+ onStudioEvent(async (event: StudioEvent) => {
29
+ // await redis.xadd(
30
+ // stream,
31
+ // '*',
32
+ // 'id', event.id,
33
+ // 'type', event.type,
34
+ // 'ts', event.ts,
35
+ // 'level', event.level,
36
+ // 'conversationId', event.conversationId ?? '',
37
+ // 'runtimeInstanceId', event.runtimeInstanceId ?? '',
38
+ // 'providerCallId', event.providerCallId ?? '',
39
+ // 'payload', JSON.stringify(event.payload ?? {}),
40
+ // );
41
+ void event;
42
+ void stream;
43
+ });
44
+ }
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@guidify-ai/vapi-studio",
3
+ "version": "0.1.0",
4
+ "description": "Vapi Studio — deterministic voice agents for Vapi (NestJS)",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/guidify-ai/vapi-studio.git"
9
+ },
10
+ "homepage": "https://github.com/guidify-ai/vapi-studio#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/guidify-ai/vapi-studio/issues"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "main": "dist/index.js",
18
+ "types": "dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "./identity": {
25
+ "types": "./dist/identity/index.d.ts",
26
+ "default": "./dist/identity/index.js"
27
+ },
28
+ "./docs/best-practices/*": "./docs/best-practices/*",
29
+ "./agent/AGENTS.md": "./agent/AGENTS.md"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "docs",
34
+ "agent",
35
+ "scripts",
36
+ "LICENSE"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json && yarn build:studio-ui",
40
+ "build:studio-ui": "yarn --cwd studio-ui install && yarn --cwd studio-ui build",
41
+ "clean": "rm -rf dist",
42
+ "lint": "eslint src",
43
+ "lint:fix": "eslint src --fix",
44
+ "test": "yarn lint && yarn build && node --test test/*.test.mjs",
45
+ "postinstall": "node -e \"const fs=require('fs');const p=require('path');const s=p.join(__dirname,'scripts','install-agent-refs.cjs');if(fs.existsSync(s)) require('child_process').execFileSync(process.execPath,[s],{stdio:'inherit',env:process.env});\"",
46
+ "install-agent-refs": "node scripts/install-agent-refs.cjs"
47
+ },
48
+ "dependencies": {
49
+ "uuid": "^11.1.0",
50
+ "yaml": "^2.7.0"
51
+ },
52
+ "peerDependencies": {
53
+ "@nestjs/common": "^11.0.0",
54
+ "@nestjs/core": "^11.0.0",
55
+ "@nestjs/typeorm": "^11.0.0",
56
+ "reflect-metadata": "^0.2.2",
57
+ "rxjs": "^7.8.1",
58
+ "typeorm": "^0.3.21",
59
+ "twilio": "^5.0.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "twilio": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "devDependencies": {
67
+ "@eslint/js": "^10.0.1",
68
+ "@nestjs/common": "^11.0.12",
69
+ "@nestjs/core": "^11.0.12",
70
+ "@nestjs/typeorm": "^11.0.0",
71
+ "@types/node": "^22.13.10",
72
+ "@types/uuid": "^10.0.0",
73
+ "eslint": "^10.9.1",
74
+ "reflect-metadata": "^0.2.2",
75
+ "rxjs": "^7.8.2",
76
+ "ts-morph": "^28.0.0",
77
+ "typeorm": "^0.3.21",
78
+ "typescript": "^5.8.2",
79
+ "typescript-eslint": "^8.69.0"
80
+ }
81
+ }
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * One-time / maintenance codemod: explicit public|private|protected on class members;
4
+ * explicit types on properties when missing (from initializer inference).
5
+ */
6
+ import { Project, SyntaxKind, Node } from 'ts-morph';
7
+ import path from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
11
+
12
+ const project = new Project({
13
+ tsConfigFilePath: path.join(root, 'tsconfig.json'),
14
+ });
15
+
16
+ function hasAccessModifier(node) {
17
+ return (
18
+ node.hasModifier(SyntaxKind.PublicKeyword) ||
19
+ node.hasModifier(SyntaxKind.PrivateKeyword) ||
20
+ node.hasModifier(SyntaxKind.ProtectedKeyword)
21
+ );
22
+ }
23
+
24
+ function ensurePublic(node) {
25
+ if (!hasAccessModifier(node)) {
26
+ node.toggleModifier('public', true);
27
+ }
28
+ }
29
+
30
+ function typeTextFromInitializer(init) {
31
+ if (!init) return null;
32
+ if (init.getKind() === SyntaxKind.TrueKeyword || init.getKind() === SyntaxKind.FalseKeyword) {
33
+ return 'boolean';
34
+ }
35
+ if (Node.isNumericLiteral(init)) return 'number';
36
+ if (Node.isStringLiteral(init)) {
37
+ const parentInit = init.getParent();
38
+ if (
39
+ parentInit &&
40
+ Node.isAsExpression(parentInit) &&
41
+ parentInit.getTypeNode()?.getText() === 'const'
42
+ ) {
43
+ return init.getLiteralText(); // keep literal for `as const` fields
44
+ }
45
+ return 'string';
46
+ }
47
+ const t = init.getType();
48
+ const text = t.getText(init, undefined, undefined);
49
+ if (!text || text === 'any' || text.includes('import(')) return null;
50
+ if (text === 'true' || text === 'false') return 'boolean';
51
+ return text;
52
+ }
53
+
54
+ for (const sf of project.getSourceFiles('src/**/*.ts')) {
55
+ let changed = false;
56
+
57
+ for (const cls of sf.getClasses()) {
58
+ for (const member of cls.getMembers()) {
59
+ if (
60
+ Node.isPropertyDeclaration(member) ||
61
+ Node.isMethodDeclaration(member) ||
62
+ Node.isGetAccessorDeclaration(member) ||
63
+ Node.isSetAccessorDeclaration(member) ||
64
+ Node.isConstructorDeclaration(member)
65
+ ) {
66
+ ensurePublic(member);
67
+ changed = true;
68
+ }
69
+
70
+ if (Node.isPropertyDeclaration(member) && !member.getTypeNode()) {
71
+ const inferred = typeTextFromInitializer(member.getInitializer());
72
+ if (inferred) {
73
+ member.setType(inferred);
74
+ changed = true;
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ if (changed) {
81
+ sf.saveSync();
82
+ }
83
+ }
84
+
85
+ console.log('Access modifiers and property types updated under src/');
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Runs on `@guidify-ai/vapi-studio` install. Ensures the consuming app has agent-visible
4
+ * pointers to docs/best-practices (Cursor rule + stamped AGENTS.md block).
5
+ *
6
+ * Yarn/npm set INIT_CWD to the project that invoked install.
7
+ */
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const pkgRoot = path.resolve(__dirname, '..');
12
+ const consumerRoot = path.resolve(process.env.INIT_CWD || process.cwd());
13
+ const PKG = '@guidify-ai/vapi-studio';
14
+
15
+ function isSameDir(a, b) {
16
+ return path.resolve(a) === path.resolve(b);
17
+ }
18
+
19
+ /** Skip when installing the framework package itself. */
20
+ if (isSameDir(consumerRoot, pkgRoot)) {
21
+ process.exit(0);
22
+ }
23
+
24
+ /** Skip if this does not look like an app that depends on us. */
25
+ const consumerPkgPath = path.join(consumerRoot, 'package.json');
26
+ if (!fs.existsSync(consumerPkgPath)) {
27
+ process.exit(0);
28
+ }
29
+
30
+ let consumerPkg;
31
+ try {
32
+ consumerPkg = JSON.parse(fs.readFileSync(consumerPkgPath, 'utf8'));
33
+ } catch {
34
+ process.exit(0);
35
+ }
36
+
37
+ const deps = {
38
+ ...(consumerPkg.dependencies || {}),
39
+ ...(consumerPkg.devDependencies || {}),
40
+ };
41
+ if (!deps[PKG]) {
42
+ process.exit(0);
43
+ }
44
+
45
+ const BEGIN = '<!-- VAPI-STUDIO-BEST-PRACTICES:BEGIN -->';
46
+ const END = '<!-- VAPI-STUDIO-BEST-PRACTICES:END -->';
47
+
48
+ const agentsBlock = `${BEGIN}
49
+ ## Vapi Studio best practices
50
+
51
+ This app depends on \`@guidify-ai/vapi-studio\`. CLI / coding agents **must** read the
52
+ framework best-practice guides before changing conversation copy, agent steps,
53
+ listens, extracts, or identity flows:
54
+
55
+ - \`node_modules/@guidify-ai/vapi-studio/docs/best-practices/README.md\`
56
+ - \`node_modules/@guidify-ai/vapi-studio/agent/AGENTS.md\`
57
+ - Cursor rule: \`.cursor/rules/vapi-studio-best-practices.mdc\` (installed with the package)
58
+ - Cursor rule: \`.cursor/rules/ui-and-api-identity.mdc\` (UUID externally; \`id\`+\`uuid\` in DB; human \`label\`)
59
+ - Claude Code: \`CLAUDE.md\` + \`.claude/rules/\` (same doctrine; installed with the package)
60
+
61
+ Hard rules (summary): **one CTA per turn**; conversations **must end** (limits always on);
62
+ constrained fields **fail closed**; short listen timeouts for digits; never store ASR junk
63
+ as PII; soft affirmatives on multi-choice → local re-ask; silent handoffs;
64
+ **async prep/teardown in before()/after()** (do not overload run()); FE uses **uuid**
65
+ only (never internal id) and DTOs include human **label**; update the matching doc layer in the
66
+ same change. Runtime contracts: \`docs/reference/runtime-api.md\`.
67
+ ${END}
68
+ `;
69
+
70
+ const CLAUDE_BEGIN = '<!-- VAPI-STUDIO-CLAUDE:BEGIN -->';
71
+ const CLAUDE_END = '<!-- VAPI-STUDIO-CLAUDE:END -->';
72
+
73
+ const claudeBlock = `${CLAUDE_BEGIN}
74
+ # Vapi Studio (Claude Code)
75
+
76
+ Import the shared agent handbook (same content Cursor loads via AGENTS.md):
77
+
78
+ @node_modules/@guidify-ai/vapi-studio/agent/AGENTS.md
79
+
80
+ Rules installed by postinstall:
81
+
82
+ - \`.claude/rules/vapi-studio-best-practices.md\`
83
+ - \`.claude/rules/ui-and-api-identity.md\`
84
+
85
+ Doctrine index: \`node_modules/@guidify-ai/vapi-studio/docs/best-practices/README.md\`.
86
+ ${CLAUDE_END}
87
+ `;
88
+
89
+ function writeCursorRules() {
90
+ const destDir = path.join(consumerRoot, '.cursor', 'rules');
91
+ fs.mkdirSync(destDir, { recursive: true });
92
+ const files = [
93
+ 'vapi-studio-best-practices.mdc',
94
+ 'ui-and-api-identity.mdc',
95
+ ];
96
+ for (const name of files) {
97
+ const src = path.join(pkgRoot, 'agent', 'cursor', name);
98
+ const dest = path.join(destDir, name);
99
+ if (!fs.existsSync(src)) {
100
+ console.warn('[vapi-studio] agent cursor rule template missing:', name);
101
+ continue;
102
+ }
103
+ fs.copyFileSync(src, dest);
104
+ console.log('[vapi-studio] wrote', path.relative(consumerRoot, dest) || dest);
105
+ }
106
+ }
107
+
108
+ function writeClaudeRules() {
109
+ const destDir = path.join(consumerRoot, '.claude', 'rules');
110
+ fs.mkdirSync(destDir, { recursive: true });
111
+ const files = [
112
+ 'vapi-studio-best-practices.md',
113
+ 'ui-and-api-identity.md',
114
+ ];
115
+ for (const name of files) {
116
+ const src = path.join(pkgRoot, 'agent', 'claude', 'rules', name);
117
+ const dest = path.join(destDir, name);
118
+ if (!fs.existsSync(src)) {
119
+ console.warn('[vapi-studio] agent claude rule template missing:', name);
120
+ continue;
121
+ }
122
+ fs.copyFileSync(src, dest);
123
+ console.log('[vapi-studio] wrote', path.relative(consumerRoot, dest) || dest);
124
+ }
125
+ }
126
+
127
+ function upsertMarkdownBlock(destPath, begin, end, block, emptyTitle) {
128
+ let existing = '';
129
+ if (fs.existsSync(destPath)) {
130
+ existing = fs.readFileSync(destPath, 'utf8');
131
+ }
132
+
133
+ const blockPattern = new RegExp(
134
+ `${escapeRegExp(begin)}[\\s\\S]*?${escapeRegExp(end)}`,
135
+ );
136
+
137
+ if (blockPattern.test(existing)) {
138
+ const next = existing.replace(blockPattern, block.trim());
139
+ fs.writeFileSync(destPath, ensureTrailingNewline(next));
140
+ console.log('[vapi-studio] refreshed', path.basename(destPath), 'block');
141
+ return;
142
+ }
143
+
144
+ if (!existing.trim()) {
145
+ fs.writeFileSync(
146
+ destPath,
147
+ ensureTrailingNewline(`${emptyTitle}\n\n${block}\n`),
148
+ );
149
+ console.log('[vapi-studio] created', path.basename(destPath));
150
+ return;
151
+ }
152
+
153
+ fs.writeFileSync(
154
+ destPath,
155
+ ensureTrailingNewline(`${existing.trimEnd()}\n\n${block}\n`),
156
+ );
157
+ console.log('[vapi-studio] appended block to', path.basename(destPath));
158
+ }
159
+
160
+ function upsertAgentsMd() {
161
+ upsertMarkdownBlock(
162
+ path.join(consumerRoot, 'AGENTS.md'),
163
+ BEGIN,
164
+ END,
165
+ agentsBlock,
166
+ '# Agents\n\nProject-specific agent notes for this app.',
167
+ );
168
+ }
169
+
170
+ function upsertClaudeMd() {
171
+ upsertMarkdownBlock(
172
+ path.join(consumerRoot, 'CLAUDE.md'),
173
+ CLAUDE_BEGIN,
174
+ CLAUDE_END,
175
+ claudeBlock,
176
+ '# Agents\n\nProject notes for Claude Code. Shared handbook is imported below.',
177
+ );
178
+ }
179
+
180
+ function escapeRegExp(s) {
181
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
182
+ }
183
+
184
+ function ensureTrailingNewline(s) {
185
+ return s.endsWith('\n') ? s : `${s}\n`;
186
+ }
187
+
188
+ try {
189
+ writeCursorRules();
190
+ writeClaudeRules();
191
+ upsertAgentsMd();
192
+ upsertClaudeMd();
193
+ } catch (err) {
194
+ console.warn('[vapi-studio] install-agent-refs failed (non-fatal):', err.message);
195
+ process.exit(0);
196
+ }