glirastes 0.3.0 → 0.3.2

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 (310) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +154 -9
  3. package/codegen/package.json +5 -0
  4. package/dist/cjs/codegen/asset-sync.d.ts +20 -0
  5. package/dist/cjs/codegen/asset-sync.js +28 -0
  6. package/dist/cjs/codegen/coverage.d.ts +66 -0
  7. package/dist/cjs/codegen/coverage.js +408 -0
  8. package/dist/cjs/codegen/endpoint-scanner.d.ts +16 -0
  9. package/dist/cjs/codegen/endpoint-scanner.js +197 -0
  10. package/dist/cjs/codegen/format.d.ts +17 -0
  11. package/dist/cjs/codegen/format.js +34 -0
  12. package/dist/cjs/codegen/fs-utils.d.ts +45 -0
  13. package/dist/cjs/codegen/fs-utils.js +165 -0
  14. package/dist/cjs/codegen/index.d.ts +13 -0
  15. package/dist/cjs/codegen/index.js +33 -0
  16. package/dist/cjs/codegen/mcp-generator.d.ts +50 -0
  17. package/dist/cjs/codegen/mcp-generator.js +803 -0
  18. package/dist/cjs/codegen/module-generator.d.ts +11 -0
  19. package/dist/cjs/codegen/module-generator.js +353 -0
  20. package/dist/cjs/codegen/nestjs-scanner.d.ts +43 -0
  21. package/dist/cjs/codegen/nestjs-scanner.js +241 -0
  22. package/dist/cjs/codegen/output-recommender.d.ts +23 -0
  23. package/dist/cjs/codegen/output-recommender.js +33 -0
  24. package/dist/cjs/codegen/scaffolder.d.ts +36 -0
  25. package/dist/cjs/codegen/scaffolder.js +262 -0
  26. package/dist/cjs/codegen/skill-generator.d.ts +49 -0
  27. package/dist/cjs/codegen/skill-generator.js +967 -0
  28. package/dist/cjs/codegen/types.d.ts +52 -0
  29. package/dist/cjs/codegen/types.js +16 -0
  30. package/dist/cjs/codegen/ui-scanner.d.ts +24 -0
  31. package/dist/cjs/codegen/ui-scanner.js +202 -0
  32. package/dist/cjs/codegen/validator.d.ts +35 -0
  33. package/dist/cjs/codegen/validator.js +292 -0
  34. package/dist/cjs/index.d.ts +1 -0
  35. package/dist/cjs/index.js +23 -0
  36. package/dist/cjs/openapi/index.d.ts +78 -0
  37. package/dist/cjs/openapi/index.js +347 -0
  38. package/dist/cjs/package.json +1 -0
  39. package/dist/cjs/react/audio-worklet-processor.d.ts +16 -0
  40. package/dist/cjs/react/audio-worklet-processor.js +27 -0
  41. package/dist/cjs/react/bridges/rest-session-bridge.d.ts +113 -0
  42. package/dist/cjs/react/bridges/rest-session-bridge.js +209 -0
  43. package/dist/cjs/react/components/ai-chat-panel.d.ts +12 -0
  44. package/dist/cjs/react/components/ai-chat-panel.js +67 -0
  45. package/dist/cjs/react/components/ai-trigger-button.d.ts +2 -0
  46. package/dist/cjs/react/components/ai-trigger-button.js +239 -0
  47. package/dist/cjs/react/components/approval-card.d.ts +9 -0
  48. package/dist/cjs/react/components/approval-card.js +24 -0
  49. package/dist/cjs/react/components/bulk-approval-card.d.ts +9 -0
  50. package/dist/cjs/react/components/bulk-approval-card.js +36 -0
  51. package/dist/cjs/react/components/chat-input.d.ts +10 -0
  52. package/dist/cjs/react/components/chat-input.js +60 -0
  53. package/dist/cjs/react/components/chat-window.d.ts +2 -0
  54. package/dist/cjs/react/components/chat-window.js +134 -0
  55. package/dist/cjs/react/components/confirm-dialog.d.ts +32 -0
  56. package/dist/cjs/react/components/confirm-dialog.js +48 -0
  57. package/dist/cjs/react/components/floating-chat-ui.d.ts +90 -0
  58. package/dist/cjs/react/components/floating-chat-ui.js +163 -0
  59. package/dist/cjs/react/components/mention-chip.d.ts +16 -0
  60. package/dist/cjs/react/components/mention-chip.js +42 -0
  61. package/dist/cjs/react/components/mention-input.d.ts +8 -0
  62. package/dist/cjs/react/components/mention-input.js +117 -0
  63. package/dist/cjs/react/components/message-bubble.d.ts +11 -0
  64. package/dist/cjs/react/components/message-bubble.js +171 -0
  65. package/dist/cjs/react/components/message-list.d.ts +8 -0
  66. package/dist/cjs/react/components/message-list.js +52 -0
  67. package/dist/cjs/react/components/pipeline-timeline.d.ts +7 -0
  68. package/dist/cjs/react/components/pipeline-timeline.js +72 -0
  69. package/dist/cjs/react/components/recording-bar.d.ts +2 -0
  70. package/dist/cjs/react/components/recording-bar.js +81 -0
  71. package/dist/cjs/react/components/recording-controls.d.ts +18 -0
  72. package/dist/cjs/react/components/recording-controls.js +38 -0
  73. package/dist/cjs/react/components/rich-mention-input.d.ts +2 -0
  74. package/dist/cjs/react/components/rich-mention-input.js +571 -0
  75. package/dist/cjs/react/components/session-switcher.d.ts +14 -0
  76. package/dist/cjs/react/components/session-switcher.js +133 -0
  77. package/dist/cjs/react/components/suggestion-bar.d.ts +9 -0
  78. package/dist/cjs/react/components/suggestion-bar.js +47 -0
  79. package/dist/cjs/react/components/suggestion-chip.d.ts +9 -0
  80. package/dist/cjs/react/components/suggestion-chip.js +14 -0
  81. package/dist/cjs/react/components/voice-input-addon.d.ts +13 -0
  82. package/dist/cjs/react/components/voice-input-addon.js +31 -0
  83. package/dist/cjs/react/components/voice-input-button.d.ts +2 -0
  84. package/dist/cjs/react/components/voice-input-button.js +55 -0
  85. package/dist/cjs/react/components/wavesurfer-loader.d.ts +5 -0
  86. package/dist/cjs/react/components/wavesurfer-loader.js +36 -0
  87. package/dist/cjs/react/hooks/use-ai-chat.d.ts +22 -0
  88. package/dist/cjs/react/hooks/use-ai-chat.js +51 -0
  89. package/dist/cjs/react/hooks/use-approvals.d.ts +23 -0
  90. package/dist/cjs/react/hooks/use-approvals.js +63 -0
  91. package/dist/cjs/react/hooks/use-deepgram-transcription.d.ts +2 -0
  92. package/dist/cjs/react/hooks/use-deepgram-transcription.js +329 -0
  93. package/dist/cjs/react/hooks/use-drag-resize-panel.d.ts +48 -0
  94. package/dist/cjs/react/hooks/use-drag-resize-panel.js +308 -0
  95. package/dist/cjs/react/hooks/use-draggable-position.d.ts +25 -0
  96. package/dist/cjs/react/hooks/use-draggable-position.js +134 -0
  97. package/dist/cjs/react/hooks/use-keyboard-shortcut.d.ts +20 -0
  98. package/dist/cjs/react/hooks/use-keyboard-shortcut.js +93 -0
  99. package/dist/cjs/react/hooks/use-mentions.d.ts +23 -0
  100. package/dist/cjs/react/hooks/use-mentions.js +42 -0
  101. package/dist/cjs/react/hooks/use-suggestions.d.ts +22 -0
  102. package/dist/cjs/react/hooks/use-suggestions.js +39 -0
  103. package/dist/cjs/react/index.d.ts +43 -0
  104. package/dist/cjs/react/index.js +111 -0
  105. package/dist/cjs/react/provider/ai-chat-provider.d.ts +45 -0
  106. package/dist/cjs/react/provider/ai-chat-provider.js +1024 -0
  107. package/dist/cjs/react/provider/chat-context.d.ts +84 -0
  108. package/dist/cjs/react/provider/chat-context.js +13 -0
  109. package/dist/cjs/react/template.d.ts +84 -0
  110. package/dist/cjs/react/template.js +90 -0
  111. package/dist/cjs/react/themes.d.ts +24 -0
  112. package/dist/cjs/react/themes.js +273 -0
  113. package/dist/cjs/react/transports/langgraph/index.d.ts +2 -0
  114. package/dist/cjs/react/transports/langgraph/index.js +8 -0
  115. package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.d.ts +62 -0
  116. package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.js +94 -0
  117. package/dist/cjs/react/transports/langgraph.d.ts +71 -0
  118. package/dist/cjs/react/transports/langgraph.js +369 -0
  119. package/dist/cjs/react/transports/vercel/index.d.ts +2 -0
  120. package/dist/cjs/react/transports/vercel/index.js +8 -0
  121. package/dist/cjs/react/transports/vercel/vercel-ai-chat.d.ts +49 -0
  122. package/dist/cjs/react/transports/vercel/vercel-ai-chat.js +81 -0
  123. package/dist/cjs/react/transports/vercel.d.ts +30 -0
  124. package/dist/cjs/react/transports/vercel.js +69 -0
  125. package/dist/cjs/react/types.d.ts +760 -0
  126. package/dist/cjs/react/types.js +2 -0
  127. package/dist/cjs/react/ui.d.ts +73 -0
  128. package/dist/cjs/react/ui.js +183 -0
  129. package/dist/cjs/react/utils/create-approval-description.d.ts +53 -0
  130. package/dist/cjs/react/utils/create-approval-description.js +122 -0
  131. package/dist/cjs/react/utils/create-tool-result-handler.d.ts +90 -0
  132. package/dist/cjs/react/utils/create-tool-result-handler.js +168 -0
  133. package/dist/cjs/react/utils/mention-markup.d.ts +13 -0
  134. package/dist/cjs/react/utils/mention-markup.js +117 -0
  135. package/dist/cjs/react/utils/merge-pipeline-steps.d.ts +19 -0
  136. package/dist/cjs/react/utils/merge-pipeline-steps.js +81 -0
  137. package/dist/cjs/react/utils/sanitize-stored-messages.d.ts +23 -0
  138. package/dist/cjs/react/utils/sanitize-stored-messages.js +52 -0
  139. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.d.ts +10 -0
  140. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.js +42 -0
  141. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.d.ts +32 -0
  142. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.js +4 -0
  143. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.d.ts +11 -0
  144. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.js +80 -0
  145. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.d.ts +9 -0
  146. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.js +119 -0
  147. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.d.ts +13 -0
  148. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.js +131 -0
  149. package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.d.ts +7 -0
  150. package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.js +52 -0
  151. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.d.ts +19 -0
  152. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.js +108 -0
  153. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.d.ts +8 -0
  154. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.js +59 -0
  155. package/dist/cjs/server/adapters/nestjs/chat/dto/index.d.ts +3 -0
  156. package/dist/cjs/server/adapters/nestjs/chat/dto/index.js +9 -0
  157. package/dist/cjs/server/adapters/nestjs/chat/index.d.ts +3 -0
  158. package/dist/cjs/server/adapters/nestjs/chat/index.js +9 -0
  159. package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +4 -0
  160. package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.js +73 -0
  161. package/dist/cjs/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
  162. package/dist/cjs/server/adapters/nestjs/chat/ws-loader.js +72 -0
  163. package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.d.ts +6 -0
  164. package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.js +13 -0
  165. package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.d.ts +5 -0
  166. package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.js +15 -0
  167. package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.d.ts +6 -0
  168. package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.js +22 -0
  169. package/dist/cjs/server/adapters/nestjs/decorators/constants.d.ts +4 -0
  170. package/dist/cjs/server/adapters/nestjs/decorators/constants.js +7 -0
  171. package/dist/cjs/server/adapters/nestjs/index.d.ts +14 -0
  172. package/dist/cjs/server/adapters/nestjs/index.js +42 -0
  173. package/dist/cjs/server/adapters/nestjs/metadata.d.ts +39 -0
  174. package/dist/cjs/server/adapters/nestjs/metadata.js +36 -0
  175. package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.d.ts +36 -0
  176. package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.js +159 -0
  177. package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.d.ts +37 -0
  178. package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.js +44 -0
  179. package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.d.ts +28 -0
  180. package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.js +41 -0
  181. package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.d.ts +16 -0
  182. package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.js +117 -0
  183. package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.d.ts +26 -0
  184. package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.js +259 -0
  185. package/dist/cjs/server/adapters/nestjs/swagger.d.ts +7 -0
  186. package/dist/cjs/server/adapters/nestjs/swagger.js +39 -0
  187. package/dist/cjs/server/adapters/nextjs/context-helpers.d.ts +22 -0
  188. package/dist/cjs/server/adapters/nextjs/context-helpers.js +60 -0
  189. package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.d.ts +94 -0
  190. package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.js +965 -0
  191. package/dist/cjs/server/adapters/nextjs/filter-fallback-error.d.ts +11 -0
  192. package/dist/cjs/server/adapters/nextjs/filter-fallback-error.js +41 -0
  193. package/dist/cjs/server/adapters/nextjs/index.d.ts +8 -0
  194. package/dist/cjs/server/adapters/nextjs/index.js +22 -0
  195. package/dist/cjs/server/adapters/nextjs/prepare-model-messages.d.ts +58 -0
  196. package/dist/cjs/server/adapters/nextjs/prepare-model-messages.js +114 -0
  197. package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.d.ts +64 -0
  198. package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.js +489 -0
  199. package/dist/cjs/server/adapters/nextjs/server-action-helper.d.ts +7 -0
  200. package/dist/cjs/server/adapters/nextjs/server-action-helper.js +40 -0
  201. package/dist/cjs/server/adapters/nextjs/types.d.ts +246 -0
  202. package/dist/cjs/server/adapters/nextjs/types.js +2 -0
  203. package/dist/cjs/server/adapters/nextjs/with-ai-tool.d.ts +9 -0
  204. package/dist/cjs/server/adapters/nextjs/with-ai-tool.js +12 -0
  205. package/dist/cjs/server/core/followup-tool.d.ts +9 -0
  206. package/dist/cjs/server/core/followup-tool.js +82 -0
  207. package/dist/cjs/server/core/index.d.ts +139 -0
  208. package/dist/cjs/server/core/index.js +818 -0
  209. package/dist/cjs/server/core/intent-classifier.d.ts +94 -0
  210. package/dist/cjs/server/core/intent-classifier.js +324 -0
  211. package/dist/cjs/server/core/model-selector.d.ts +12 -0
  212. package/dist/cjs/server/core/model-selector.js +48 -0
  213. package/dist/cjs/server/core/module-router.d.ts +14 -0
  214. package/dist/cjs/server/core/module-router.js +110 -0
  215. package/dist/cjs/server/core/zod-compact.d.ts +18 -0
  216. package/dist/cjs/server/core/zod-compact.js +129 -0
  217. package/dist/cjs/server/index.d.ts +6 -0
  218. package/dist/cjs/server/index.js +28 -0
  219. package/dist/cjs/server/lancer/cache.d.ts +9 -0
  220. package/dist/cjs/server/lancer/cache.js +33 -0
  221. package/dist/cjs/server/lancer/client.d.ts +2 -0
  222. package/dist/cjs/server/lancer/client.js +279 -0
  223. package/dist/cjs/server/lancer/errors.d.ts +13 -0
  224. package/dist/cjs/server/lancer/errors.js +21 -0
  225. package/dist/cjs/server/lancer/index.d.ts +3 -0
  226. package/dist/cjs/server/lancer/index.js +7 -0
  227. package/dist/cjs/server/lancer/telemetry-buffer.d.ts +15 -0
  228. package/dist/cjs/server/lancer/telemetry-buffer.js +64 -0
  229. package/dist/cjs/server/lancer/types.d.ts +201 -0
  230. package/dist/cjs/server/lancer/types.js +5 -0
  231. package/dist/cjs/server/pii-shield/aegis-detector.d.ts +26 -0
  232. package/dist/cjs/server/pii-shield/aegis-detector.js +26 -0
  233. package/dist/cjs/server/pii-shield/create-pii-shield.d.ts +2 -0
  234. package/dist/cjs/server/pii-shield/create-pii-shield.js +191 -0
  235. package/dist/cjs/server/pii-shield/deep-walk.d.ts +1 -0
  236. package/dist/cjs/server/pii-shield/deep-walk.js +19 -0
  237. package/dist/cjs/server/pii-shield/fake-data.d.ts +5 -0
  238. package/dist/cjs/server/pii-shield/fake-data.js +170 -0
  239. package/dist/cjs/server/pii-shield/index.d.ts +4 -0
  240. package/dist/cjs/server/pii-shield/index.js +9 -0
  241. package/dist/cjs/server/pii-shield/leakage.d.ts +1 -0
  242. package/dist/cjs/server/pii-shield/leakage.js +15 -0
  243. package/dist/cjs/server/pii-shield/session-mapping.d.ts +12 -0
  244. package/dist/cjs/server/pii-shield/session-mapping.js +70 -0
  245. package/dist/cjs/server/pipeline.d.ts +26 -0
  246. package/dist/cjs/server/pipeline.js +127 -0
  247. package/dist/cjs/server/testing/approvals.d.ts +11 -0
  248. package/dist/cjs/server/testing/approvals.js +41 -0
  249. package/dist/cjs/server/testing/combination.d.ts +3 -0
  250. package/dist/cjs/server/testing/combination.js +83 -0
  251. package/dist/cjs/server/testing/create-test-suite.d.ts +29 -0
  252. package/dist/cjs/server/testing/create-test-suite.js +81 -0
  253. package/dist/cjs/server/testing/edge-cases.d.ts +8 -0
  254. package/dist/cjs/server/testing/edge-cases.js +277 -0
  255. package/dist/cjs/server/testing/guardrails.d.ts +14 -0
  256. package/dist/cjs/server/testing/guardrails.js +152 -0
  257. package/dist/cjs/server/testing/helpers.d.ts +14 -0
  258. package/dist/cjs/server/testing/helpers.js +119 -0
  259. package/dist/cjs/server/testing/index.d.ts +12 -0
  260. package/dist/cjs/server/testing/index.js +37 -0
  261. package/dist/cjs/server/testing/mock-executor.d.ts +8 -0
  262. package/dist/cjs/server/testing/mock-executor.js +40 -0
  263. package/dist/cjs/server/testing/regression.d.ts +7 -0
  264. package/dist/cjs/server/testing/regression.js +27 -0
  265. package/dist/cjs/server/testing/routing.d.ts +12 -0
  266. package/dist/cjs/server/testing/routing.js +63 -0
  267. package/dist/cjs/server/testing/schema-consistency.d.ts +6 -0
  268. package/dist/cjs/server/testing/schema-consistency.js +56 -0
  269. package/dist/cjs/server/testing/smoke.d.ts +11 -0
  270. package/dist/cjs/server/testing/smoke.js +64 -0
  271. package/dist/cjs/server/testing/types.d.ts +156 -0
  272. package/dist/cjs/server/testing/types.js +2 -0
  273. package/dist/cjs/types.d.ts +696 -0
  274. package/dist/cjs/types.js +91 -0
  275. package/dist/cli/commands/install-skills.d.ts +17 -0
  276. package/dist/cli/commands/install-skills.js +185 -0
  277. package/dist/cli/commands/install-skills.js.map +1 -0
  278. package/dist/cli/index.js +25 -0
  279. package/dist/cli/index.js.map +1 -1
  280. package/dist/react/components/ai-trigger-button.js +4 -6
  281. package/dist/react/components/ai-trigger-button.js.map +1 -1
  282. package/dist/react/components/recording-bar.js +4 -6
  283. package/dist/react/components/recording-bar.js.map +1 -1
  284. package/dist/react/components/wavesurfer-loader.d.ts +5 -0
  285. package/dist/react/components/wavesurfer-loader.js +34 -0
  286. package/dist/react/components/wavesurfer-loader.js.map +1 -0
  287. package/dist/server/adapters/nestjs/chat/ai-chat.module.d.ts +1 -1
  288. package/dist/server/adapters/nestjs/chat/ai-chat.module.js +3 -2
  289. package/dist/server/adapters/nestjs/chat/ai-chat.module.js.map +1 -1
  290. package/dist/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +1 -1
  291. package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js +10 -8
  292. package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js.map +1 -1
  293. package/dist/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
  294. package/dist/server/adapters/nestjs/chat/ws-loader.js +37 -0
  295. package/dist/server/adapters/nestjs/chat/ws-loader.js.map +1 -0
  296. package/openapi/package.json +5 -0
  297. package/package.json +59 -16
  298. package/react/langgraph/package.json +5 -0
  299. package/react/package.json +5 -0
  300. package/react/vercel/package.json +5 -0
  301. package/server/nestjs/package.json +5 -0
  302. package/server/nextjs/package.json +5 -0
  303. package/server/package.json +5 -0
  304. package/server/testing/package.json +5 -0
  305. package/skills/README.md +107 -0
  306. package/skills/building-glirastes-chat-ui/SKILL.md +378 -0
  307. package/skills/integrating-glirastes-nestjs/SKILL.md +346 -0
  308. package/skills/integrating-glirastes-nextjs/SKILL.md +312 -0
  309. package/skills/integrating-glirastes-nextjs-with-nestjs-backend/SKILL.md +359 -0
  310. package/skills/maintaining-glirastes-tools/SKILL.md +467 -0
@@ -0,0 +1,965 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SAFETY_MAX_STEPS = void 0;
4
+ exports.resolveStepLimit = resolveStepLimit;
5
+ exports.createAiChatHandler = createAiChatHandler;
6
+ const index_js_1 = require("../../core/index.js");
7
+ const types_js_1 = require("../../../types.js");
8
+ const ai_1 = require("ai");
9
+ const context_helpers_js_1 = require("./context-helpers.js");
10
+ const prepare_model_messages_js_1 = require("./prepare-model-messages.js");
11
+ const filter_fallback_error_js_1 = require("./filter-fallback-error.js");
12
+ /**
13
+ * Default soft cap on reasoning steps when no module-level or explicit
14
+ * `maxSteps` is configured. Acts as a last-line safety against runaway
15
+ * tool loops; not as a normal-operation cap (modules default to 4 via
16
+ * EXECUTION_DEFAULTS). Override per request via `createAiChatHandler({
17
+ * safetyMaxSteps })`.
18
+ */
19
+ exports.DEFAULT_SAFETY_MAX_STEPS = 24;
20
+ function asPositiveInt(value) {
21
+ if (typeof value !== 'number' || !Number.isFinite(value))
22
+ return null;
23
+ const normalized = Math.floor(value);
24
+ return normalized > 0 ? normalized : null;
25
+ }
26
+ function resolveStepLimit(options) {
27
+ const moduleMaxSteps = asPositiveInt(options.moduleMaxSteps);
28
+ if (moduleMaxSteps) {
29
+ return { maxSteps: moduleMaxSteps, stepLimitSource: 'module' };
30
+ }
31
+ const configuredMaxSteps = asPositiveInt(options.configuredMaxSteps);
32
+ if (configuredMaxSteps) {
33
+ return { maxSteps: configuredMaxSteps, stepLimitSource: 'explicit' };
34
+ }
35
+ return {
36
+ maxSteps: asPositiveInt(options.safetyMaxSteps) ?? exports.DEFAULT_SAFETY_MAX_STEPS,
37
+ stepLimitSource: 'safety',
38
+ };
39
+ }
40
+ function compactToolNames(toolCalls) {
41
+ const unique = new Set();
42
+ for (const call of toolCalls) {
43
+ if (typeof call.toolName === 'string' && call.toolName.length > 0) {
44
+ unique.add(call.toolName);
45
+ }
46
+ }
47
+ return Array.from(unique);
48
+ }
49
+ function shorten(text, maxLength = 120) {
50
+ const normalized = text.trim().replace(/\s+/g, ' ');
51
+ if (normalized.length <= maxLength)
52
+ return normalized;
53
+ return `${normalized.slice(0, maxLength - 1)}…`;
54
+ }
55
+ function buildStepSummary(args) {
56
+ if (args.requiresApproval) {
57
+ if (args.toolNames.length > 0) {
58
+ return `Awaiting approval: ${args.toolNames.join(', ')}`;
59
+ }
60
+ return 'Awaiting approval.';
61
+ }
62
+ if (args.toolCallCount > 0) {
63
+ const label = args.toolNames.length > 0
64
+ ? args.toolNames.join(', ')
65
+ : `${args.toolCallCount} tool call(s)`;
66
+ return `Executed tools: ${label}`;
67
+ }
68
+ if (args.text.trim().length > 0) {
69
+ return shorten(args.text);
70
+ }
71
+ return `Finished step (${args.finishReason}).`;
72
+ }
73
+ function buildStepReport(step) {
74
+ const toolNames = compactToolNames(step.toolCalls);
75
+ const toolCallCount = step.toolCalls.length;
76
+ const toolResultCount = step.toolResults.length;
77
+ const pendingApprovals = Math.max(toolCallCount - toolResultCount, 0);
78
+ const requiresApproval = step.finishReason === 'tool-calls' && pendingApprovals > 0;
79
+ return {
80
+ stepNumber: step.stepNumber,
81
+ finishReason: step.finishReason,
82
+ toolCalls: toolNames,
83
+ toolResults: toolResultCount,
84
+ requiresApproval,
85
+ pendingApprovals,
86
+ summary: buildStepSummary({
87
+ finishReason: step.finishReason,
88
+ text: step.text,
89
+ toolNames,
90
+ toolCallCount,
91
+ toolResultCount,
92
+ requiresApproval,
93
+ }),
94
+ usage: {
95
+ inputTokens: step.usage.inputTokens ?? 0,
96
+ outputTokens: step.usage.outputTokens ?? 0,
97
+ totalTokens: step.usage.totalTokens ?? 0,
98
+ },
99
+ createdAt: new Date().toISOString(),
100
+ };
101
+ }
102
+ function createPipelineState(options) {
103
+ return {
104
+ status: options.status,
105
+ totalSteps: options.totalSteps,
106
+ finishReason: options.finishReason,
107
+ maxSteps: options.maxSteps,
108
+ stepLimitSource: options.stepLimitSource,
109
+ message: options.message,
110
+ updatedAt: new Date().toISOString(),
111
+ };
112
+ }
113
+ /**
114
+ * Built-in default stream handler.
115
+ *
116
+ * Converts UI messages → model messages, wraps tools, calls streamText,
117
+ * and returns a UI message stream response.
118
+ */
119
+ async function defaultStreamHandler(ctx, onFinish) {
120
+ const vercelTools = (0, prepare_model_messages_js_1.wrapToolsForVercelAi)(ctx.tools);
121
+ const modelMessages = await (0, prepare_model_messages_js_1.prepareModelMessages)(ctx.messages, { maxContextMessages: ctx.maxContextMessages });
122
+ let wasTruncated = false;
123
+ let stoppedByLoop = false;
124
+ // Stop condition: step count OR consecutive identical tool calls (loop detection).
125
+ const LOOP_REPEAT_THRESHOLD = 3;
126
+ const shouldStop = (event) => {
127
+ if (event.steps.length >= ctx.maxSteps)
128
+ return true;
129
+ if (event.steps.length >= LOOP_REPEAT_THRESHOLD) {
130
+ const recent = event.steps.slice(-LOOP_REPEAT_THRESHOLD);
131
+ const signatures = recent.map((s) => s.toolCalls
132
+ .map((c) => c.toolName)
133
+ .sort()
134
+ .join(','));
135
+ if (signatures[0].length > 0 &&
136
+ signatures.every((sig) => sig === signatures[0])) {
137
+ stoppedByLoop = true;
138
+ return true;
139
+ }
140
+ }
141
+ return false;
142
+ };
143
+ const stream = (0, ai_1.createUIMessageStream)({
144
+ execute: async ({ writer }) => {
145
+ let totalSteps = 0;
146
+ let finalStateWritten = false;
147
+ const writePipelineState = (state, transient = false) => {
148
+ writer.write({
149
+ type: 'data-pipeline-state',
150
+ data: state,
151
+ transient,
152
+ });
153
+ };
154
+ writePipelineState(createPipelineState({
155
+ status: 'running',
156
+ totalSteps: 0,
157
+ maxSteps: ctx.maxSteps,
158
+ stepLimitSource: ctx.stepLimitSource,
159
+ }), true);
160
+ const result = (0, ai_1.streamText)({
161
+ model: ctx.model,
162
+ messages: modelMessages,
163
+ system: ctx.systemPrompt,
164
+ tools: vercelTools,
165
+ maxOutputTokens: ctx.maxOutputTokens,
166
+ temperature: ctx.temperature,
167
+ stopWhen: shouldStop,
168
+ onStepFinish: (event) => {
169
+ const report = buildStepReport({
170
+ stepNumber: event.stepNumber,
171
+ finishReason: event.finishReason,
172
+ toolCalls: event.toolCalls.map((call) => ({
173
+ toolName: call.toolName,
174
+ })),
175
+ toolResults: event.toolResults,
176
+ text: event.text,
177
+ usage: {
178
+ inputTokens: event.usage.inputTokens,
179
+ outputTokens: event.usage.outputTokens,
180
+ totalTokens: event.usage.totalTokens,
181
+ },
182
+ });
183
+ totalSteps = Math.max(totalSteps, report.stepNumber + 1);
184
+ if (ctx.emitStepReports) {
185
+ writer.write({
186
+ type: 'data-step-report',
187
+ data: report,
188
+ });
189
+ }
190
+ ctx.onStepReport?.(report);
191
+ },
192
+ onAbort: (event) => {
193
+ if (finalStateWritten)
194
+ return;
195
+ totalSteps = Math.max(totalSteps, event.steps.length);
196
+ writePipelineState(createPipelineState({
197
+ status: 'aborted',
198
+ totalSteps,
199
+ finishReason: 'stop',
200
+ maxSteps: ctx.maxSteps,
201
+ stepLimitSource: ctx.stepLimitSource,
202
+ message: 'Stopped by user.',
203
+ }));
204
+ finalStateWritten = true;
205
+ },
206
+ onFinish: (event) => {
207
+ wasTruncated =
208
+ event.finishReason === 'tool-calls' &&
209
+ (event.steps.length >= ctx.maxSteps || stoppedByLoop);
210
+ totalSteps = Math.max(totalSteps, event.steps.length);
211
+ const isSafetyStop = wasTruncated && ctx.stepLimitSource === 'safety' && !stoppedByLoop;
212
+ const status = stoppedByLoop
213
+ ? 'safety-stop'
214
+ : isSafetyStop
215
+ ? 'safety-stop'
216
+ : event.finishReason === 'error'
217
+ ? 'error'
218
+ : 'completed';
219
+ if (!finalStateWritten) {
220
+ writePipelineState(createPipelineState({
221
+ status,
222
+ totalSteps,
223
+ finishReason: event.finishReason,
224
+ maxSteps: ctx.maxSteps,
225
+ stepLimitSource: ctx.stepLimitSource,
226
+ message: stoppedByLoop
227
+ ? 'Stopped: repeated tool call loop detected.'
228
+ : wasTruncated
229
+ ? isSafetyStop
230
+ ? 'Paused at step safety cap. You can continue or stop.'
231
+ : 'Paused at configured step cap.'
232
+ : undefined,
233
+ }));
234
+ finalStateWritten = true;
235
+ }
236
+ onFinish?.({
237
+ totalSteps: event.steps.length,
238
+ finishReason: event.finishReason,
239
+ usage: {
240
+ inputTokens: event.totalUsage.inputTokens ?? 0,
241
+ outputTokens: event.totalUsage.outputTokens ?? 0,
242
+ totalTokens: event.totalUsage.totalTokens ?? 0,
243
+ },
244
+ wasTruncated,
245
+ maxSteps: ctx.maxSteps,
246
+ stepLimitSource: ctx.stepLimitSource,
247
+ });
248
+ },
249
+ });
250
+ writer.merge(result.toUIMessageStream());
251
+ try {
252
+ await result.response;
253
+ }
254
+ catch (error) {
255
+ if (finalStateWritten)
256
+ return;
257
+ writePipelineState(createPipelineState({
258
+ status: 'error',
259
+ totalSteps,
260
+ finishReason: 'error',
261
+ maxSteps: ctx.maxSteps,
262
+ stepLimitSource: ctx.stepLimitSource,
263
+ message: 'Pipeline failed.',
264
+ }));
265
+ finalStateWritten = true;
266
+ throw error;
267
+ }
268
+ },
269
+ });
270
+ // Wrap the outer stream so the filter sees BOTH the inner result chunks AND
271
+ // the error chunk that createUIMessageStream emits when execute() throws —
272
+ // the latter is where the duplicate "No output generated" event comes from.
273
+ return (0, ai_1.createUIMessageStreamResponse)({ stream: (0, filter_fallback_error_js_1.filterFallbackError)(stream) });
274
+ }
275
+ /**
276
+ * Creates a complete Next.js App Router POST handler for AI chat.
277
+ *
278
+ * Supports two modes:
279
+ *
280
+ * **Pipeline mode** (Pro): When `pipeline` is provided, uses the server-pro
281
+ * pipeline for guardrails, intent classification, tool selection, and model
282
+ * selection.
283
+ *
284
+ * **Legacy mode**: When no `pipeline` is provided, uses the original
285
+ * `guardrails` + `intentRouter` flow.
286
+ *
287
+ * The SDK does not perform authentication or authorization. Tools forward the
288
+ * caller's cookies to backend APIs via `runtime.cookieHeader`; permission
289
+ * checks happen on the backend.
290
+ */
291
+ function createAiChatHandler(config) {
292
+ const { tools: staticTools, pipeline, followups, loadTools,
293
+ // PII Shield
294
+ piiShield,
295
+ // Audit & Telemetry
296
+ onAudit, telemetry,
297
+ // Lancer (Pro: runtime prompt + model overrides)
298
+ lancer, modelDefaults, modelInstanceFactory,
299
+ // Shared
300
+ model: modelConfig, systemPrompt: systemPromptConfig, locale = 'en-US', uiActionSchema, maxOutputTokens = 600, temperature = 0.2, maxSteps: configuredMaxSteps, safetyMaxSteps = exports.DEFAULT_SAFETY_MAX_STEPS, emitStepReports = true, maxContextMessages = 12, maxConversationHistoryPairs = 4, guardrails, intentRouter, wrapHandler, onError, onToolError, onPipelineResult, onStepReport, onFinish, streamHandler: customStreamHandler, } = config;
301
+ // Report model defaults to Glirastes (fire-and-forget, once)
302
+ let defaultsReported = false;
303
+ function reportDefaultsOnce() {
304
+ if (defaultsReported || !lancer || !modelDefaults)
305
+ return;
306
+ defaultsReported = true;
307
+ lancer.config.reportModels(modelDefaults).catch(() => { });
308
+ }
309
+ // Build stream handler factory — per-request to allow session.ended audit
310
+ function createStreamHandler(auditContext) {
311
+ const wrappedOnFinish = (event) => {
312
+ onFinish?.(event);
313
+ if (auditContext && (onAudit || telemetry)) {
314
+ const sessionEndEvent = {
315
+ type: 'session.ended',
316
+ timestamp: new Date().toISOString(),
317
+ sessionId: auditContext.sessionId,
318
+ source: 'adapter',
319
+ details: {
320
+ totalSteps: event.totalSteps,
321
+ totalTokens: event.usage.totalTokens,
322
+ durationMs: Date.now() - auditContext.requestStart,
323
+ },
324
+ };
325
+ onAudit?.(sessionEndEvent);
326
+ try {
327
+ telemetry?.emit({
328
+ eventType: 'session.ended',
329
+ traceId: auditContext.sessionId,
330
+ tokensIn: event.usage.inputTokens,
331
+ tokensOut: event.usage.outputTokens,
332
+ latencyMs: Date.now() - auditContext.requestStart,
333
+ modelId: auditContext.modelId,
334
+ timestamp: new Date().toISOString(),
335
+ payload: sessionEndEvent.details,
336
+ actor: { sessionId: auditContext.sessionId },
337
+ });
338
+ void telemetry?.flush();
339
+ }
340
+ catch {
341
+ // Telemetry is fire-and-forget
342
+ }
343
+ }
344
+ };
345
+ return customStreamHandler
346
+ ? customStreamHandler
347
+ : (ctx) => defaultStreamHandler(ctx, wrappedOnFinish);
348
+ }
349
+ if (!staticTools && !loadTools) {
350
+ throw new Error('createAiChatHandler: either tools or loadTools must be provided');
351
+ }
352
+ async function handler(req) {
353
+ reportDefaultsOnce();
354
+ try {
355
+ const { messages } = await req.json();
356
+ if (!Array.isArray(messages)) {
357
+ return new Response(JSON.stringify({ error: 'messages must be an array' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
358
+ }
359
+ // Build tool context
360
+ const toolContext = (0, context_helpers_js_1.buildToolContext)(req, { locale });
361
+ const requestStart = Date.now();
362
+ // Derive session ID — stable per conversation turn (groups approval flows)
363
+ const sessionId = (onAudit || piiShield || telemetry) ? deriveSessionId(messages) : '';
364
+ const toolOptions = {
365
+ uiActionSchema,
366
+ onError: onToolError,
367
+ onAudit,
368
+ sessionId,
369
+ traceId: sessionId || undefined,
370
+ telemetry,
371
+ };
372
+ // Emit session.started for audit + telemetry
373
+ if (sessionId && (onAudit || telemetry)) {
374
+ const sessionStartEvent = {
375
+ type: 'session.started',
376
+ timestamp: new Date().toISOString(),
377
+ sessionId,
378
+ source: 'adapter',
379
+ details: { locale },
380
+ };
381
+ onAudit?.(sessionStartEvent);
382
+ try {
383
+ telemetry?.emit({
384
+ eventType: 'session.started',
385
+ traceId: sessionId,
386
+ timestamp: new Date().toISOString(),
387
+ payload: sessionStartEvent.details,
388
+ actor: { sessionId },
389
+ });
390
+ }
391
+ catch {
392
+ // Telemetry is fire-and-forget
393
+ }
394
+ }
395
+ // ================================================================
396
+ // Pipeline mode
397
+ // ================================================================
398
+ if (pipeline) {
399
+ // Create callEndpoint for endpoint tools (needed regardless of static/dynamic)
400
+ const callEndpoint = (0, context_helpers_js_1.createInternalApiCallerFromRequest)(req);
401
+ const endpointToolContext = { ...toolContext, callEndpoint };
402
+ // Get tools (static or dynamic)
403
+ let registry;
404
+ if (staticTools) {
405
+ registry = staticTools;
406
+ }
407
+ else {
408
+ registry = await loadTools(endpointToolContext);
409
+ }
410
+ // Add followup tool if configured
411
+ if (followups) {
412
+ const followupTool = (0, index_js_1.createFollowupTool)(followups);
413
+ if (followupTool) {
414
+ registry = { ...registry, [followupTool.id]: followupTool };
415
+ }
416
+ }
417
+ // Extract user input for pipeline
418
+ let userText = extractLastUserText(messages);
419
+ if (piiShield) {
420
+ userText = await piiShield.outbound(userText, sessionId);
421
+ }
422
+ // Build lightweight conversation history for intent classification.
423
+ // Include the last few user/assistant text exchanges (excluding the
424
+ // current message) so the classifier can understand follow-ups.
425
+ const conversationHistory = extractConversationHistory(messages, maxConversationHistoryPairs);
426
+ // Run pipeline: guardrails + classification + routing + model selection
427
+ const pipelineResult = await pipeline.process(userText, toolContext, conversationHistory.length > 0 ? conversationHistory : undefined);
428
+ // Notify consumer about pipeline decision
429
+ onPipelineResult?.(pipelineResult);
430
+ // Emit pipeline-stage telemetry events for request trace
431
+ if (telemetry && sessionId) {
432
+ const pipelineActor = { sessionId };
433
+ try {
434
+ // Use explicit blocked flag from pipeline instead of heuristic
435
+ const wardenBlocked = pipelineResult.blocked === true;
436
+ telemetry.emit({
437
+ eventType: wardenBlocked ? 'warden.block' : 'warden.pass',
438
+ traceId: sessionId,
439
+ timestamp: new Date().toISOString(),
440
+ payload: { blocked: wardenBlocked, inputLength: userText.length },
441
+ actor: pipelineActor,
442
+ });
443
+ // Primus classification
444
+ telemetry.emit({
445
+ eventType: 'primus.classify',
446
+ traceId: sessionId,
447
+ timestamp: new Date().toISOString(),
448
+ payload: {
449
+ moduleId: pipelineResult.intent.intent,
450
+ confidence: pipelineResult.intent.confidence,
451
+ module: pipelineResult.module?.id ?? null,
452
+ },
453
+ actor: pipelineActor,
454
+ });
455
+ // Tool routing
456
+ telemetry.emit({
457
+ eventType: 'pipeline.routing',
458
+ traceId: sessionId,
459
+ timestamp: new Date().toISOString(),
460
+ payload: {
461
+ toolCount: pipelineResult.tools.length,
462
+ tools: pipelineResult.tools,
463
+ module: pipelineResult.module?.id ?? null,
464
+ modelId: pipelineResult.model.modelId,
465
+ },
466
+ actor: pipelineActor,
467
+ });
468
+ }
469
+ catch {
470
+ // Telemetry is fire-and-forget
471
+ }
472
+ }
473
+ // Convert all tools to AI tools
474
+ let aiTools = await (0, index_js_1.toolsToAiTools)(registry, endpointToolContext, toolOptions);
475
+ // Scope tools to pipeline selection
476
+ if (pipelineResult.blocked) {
477
+ // Guardrails blocked — no tools allowed
478
+ aiTools = {};
479
+ }
480
+ else if (pipelineResult.tools.length > 0) {
481
+ const scoped = {};
482
+ for (const name of pipelineResult.tools) {
483
+ if (aiTools[name])
484
+ scoped[name] = aiTools[name];
485
+ }
486
+ if (Object.keys(scoped).length > 0)
487
+ aiTools = scoped;
488
+ }
489
+ // Resolve model — config callback > pipeline instance > error
490
+ let model;
491
+ if (modelConfig) {
492
+ model =
493
+ typeof modelConfig === 'function'
494
+ ? modelConfig({ pipelineResult })
495
+ : modelConfig;
496
+ }
497
+ else if (pipelineResult.model.instance) {
498
+ model = pipelineResult.model.instance;
499
+ }
500
+ else {
501
+ return new Response(JSON.stringify({
502
+ error: 'No model configured. Provide model on handler config or as instance on ModelSelectorConfig.',
503
+ }), { status: 500, headers: { 'Content-Type': 'application/json' } });
504
+ }
505
+ // Resolve system prompt
506
+ let systemPrompt = typeof systemPromptConfig === 'function'
507
+ ? systemPromptConfig({
508
+ pipelineResult,
509
+ currentDate: new Date(),
510
+ })
511
+ : systemPromptConfig;
512
+ // Track the actual model ID used (may be overridden by Glirastes config)
513
+ let resolvedModelId = pipelineResult.model.modelId;
514
+ // Apply runtime prompt + model overrides from Glirastes (Pro feature)
515
+ if (lancer) {
516
+ try {
517
+ const configResult = await lancer.config.fetch(['prompts', 'models']);
518
+ // Prompt overrides
519
+ if (pipelineResult.module) {
520
+ const prompts = configResult.prompts;
521
+ if (prompts) {
522
+ const override = prompts.find((p) => p.moduleId === pipelineResult.module.id);
523
+ if (override) {
524
+ systemPrompt = (0, types_js_1.applyPromptOverride)(systemPrompt, override);
525
+ }
526
+ }
527
+ }
528
+ // Model overrides — resolve tier from pipeline, apply override if configured
529
+ if (modelInstanceFactory) {
530
+ const modelOverrides = configResult.models;
531
+ if (modelOverrides) {
532
+ const tier = pipelineResult.module?.executionDefaults?.modelTier ?? 'standard';
533
+ const overrideModelId = modelOverrides[tier];
534
+ if (overrideModelId) {
535
+ resolvedModelId = overrideModelId;
536
+ model = modelInstanceFactory(overrideModelId);
537
+ }
538
+ }
539
+ }
540
+ }
541
+ catch {
542
+ // Graceful degradation: use local prompt/model if Glirastes is unreachable
543
+ }
544
+ }
545
+ const stepLimit = resolveStepLimit({
546
+ moduleMaxSteps: pipelineResult.module?.executionDefaults?.maxSteps,
547
+ configuredMaxSteps,
548
+ safetyMaxSteps,
549
+ });
550
+ const contextWindow = pipelineResult.module?.executionDefaults?.contextWindow ??
551
+ maxContextMessages;
552
+ let recentMessages = messages.slice(-contextWindow);
553
+ // PII Shield: anonymize message history + wrap tools
554
+ if (piiShield) {
555
+ recentMessages = await anonymizeMessages(recentMessages, piiShield, sessionId);
556
+ aiTools = wrapToolsWithShield(aiTools, piiShield, sessionId);
557
+ }
558
+ // Lancer Approvals: delegate approval decisions to Glirastes
559
+ if (lancer?.approvals) {
560
+ aiTools = wrapToolsWithLancerApprovals(aiTools, lancer.approvals, sessionId);
561
+ }
562
+ const pipelineStreamHandler = createStreamHandler((onAudit || telemetry) ? {
563
+ sessionId,
564
+ requestStart,
565
+ modelId: resolvedModelId,
566
+ } : undefined);
567
+ return pipelineStreamHandler({
568
+ tools: aiTools,
569
+ messages: recentMessages,
570
+ systemPrompt,
571
+ model,
572
+ maxSteps: stepLimit.maxSteps,
573
+ stepLimitSource: stepLimit.stepLimitSource,
574
+ emitStepReports,
575
+ onStepReport,
576
+ maxOutputTokens,
577
+ temperature,
578
+ maxContextMessages,
579
+ modelTier: pipelineResult.model.modelId,
580
+ pipelineResult,
581
+ piiShield: piiShield ? { shield: piiShield, sessionId } : undefined,
582
+ });
583
+ }
584
+ // ================================================================
585
+ // Legacy mode (without pipeline)
586
+ // ================================================================
587
+ // Guardrails
588
+ let processedMessages = messages;
589
+ if (guardrails) {
590
+ const check = await guardrails(messages);
591
+ if (!check.ok) {
592
+ return new Response(JSON.stringify({ error: check.reason ?? 'Invalid input' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
593
+ }
594
+ if (check.sanitizedMessages) {
595
+ processedMessages = check.sanitizedMessages;
596
+ }
597
+ }
598
+ // Create callEndpoint for endpoint tools (needed regardless of static/dynamic)
599
+ const callEndpoint = (0, context_helpers_js_1.createInternalApiCallerFromRequest)(req);
600
+ const endpointToolContext = { ...toolContext, callEndpoint };
601
+ // Load tools
602
+ let registry;
603
+ if (staticTools) {
604
+ registry = staticTools;
605
+ }
606
+ else {
607
+ registry = await loadTools(endpointToolContext);
608
+ }
609
+ const allToolNames = Object.keys(registry);
610
+ // PII Shield: anonymize for legacy mode
611
+ const legacySessionId = piiShield ? deriveSessionId(processedMessages) : '';
612
+ if (piiShield) {
613
+ const lastUserText = extractLastUserText(processedMessages);
614
+ await piiShield.outbound(lastUserText, legacySessionId);
615
+ }
616
+ // Intent routing
617
+ let routing;
618
+ if (intentRouter) {
619
+ const text = extractLastUserText(processedMessages);
620
+ routing = await intentRouter(text, allToolNames);
621
+ }
622
+ // Convert to AI tools
623
+ let aiTools = await (0, index_js_1.toolsToAiTools)(registry, endpointToolContext, toolOptions);
624
+ // Scope tools by intent
625
+ if (routing?.toolNames && routing.toolNames.length > 0) {
626
+ const scoped = {};
627
+ for (const name of routing.toolNames) {
628
+ if (aiTools[name])
629
+ scoped[name] = aiTools[name];
630
+ }
631
+ if (Object.keys(scoped).length > 0)
632
+ aiTools = scoped;
633
+ }
634
+ // Resolve model + system prompt (legacy mode — no pipeline fallback)
635
+ if (!modelConfig) {
636
+ return new Response(JSON.stringify({
637
+ error: 'No model configured. In legacy mode (without pipeline), model is required.',
638
+ }), { status: 500, headers: { 'Content-Type': 'application/json' } });
639
+ }
640
+ const model = typeof modelConfig === 'function'
641
+ ? modelConfig({ routing })
642
+ : modelConfig;
643
+ const systemPrompt = typeof systemPromptConfig === 'function'
644
+ ? systemPromptConfig({
645
+ routing,
646
+ currentDate: new Date(),
647
+ })
648
+ : systemPromptConfig;
649
+ const stepLimit = resolveStepLimit({
650
+ configuredMaxSteps: routing?.maxSteps ?? configuredMaxSteps,
651
+ safetyMaxSteps,
652
+ });
653
+ const contextWindow = routing?.contextWindow ?? maxContextMessages;
654
+ let recentMessages = processedMessages.slice(-contextWindow);
655
+ // PII Shield: anonymize message history + wrap tools (legacy mode)
656
+ if (piiShield) {
657
+ recentMessages = await anonymizeMessages(recentMessages, piiShield, legacySessionId);
658
+ aiTools = wrapToolsWithShield(aiTools, piiShield, legacySessionId);
659
+ }
660
+ // Lancer Approvals: delegate approval decisions to Glirastes (legacy mode)
661
+ if (lancer?.approvals) {
662
+ aiTools = wrapToolsWithLancerApprovals(aiTools, lancer.approvals, legacySessionId || sessionId);
663
+ }
664
+ const legacyStreamHandler = createStreamHandler((onAudit || telemetry) ? {
665
+ sessionId: legacySessionId || sessionId,
666
+ requestStart,
667
+ } : undefined);
668
+ // Delegate streaming to consumer's handler
669
+ return legacyStreamHandler({
670
+ tools: aiTools,
671
+ messages: recentMessages,
672
+ systemPrompt,
673
+ model,
674
+ maxSteps: stepLimit.maxSteps,
675
+ stepLimitSource: stepLimit.stepLimitSource,
676
+ emitStepReports,
677
+ onStepReport,
678
+ maxOutputTokens,
679
+ temperature,
680
+ maxContextMessages,
681
+ modelTier: routing?.modelTier,
682
+ routing,
683
+ piiShield: piiShield ? { shield: piiShield, sessionId: legacySessionId } : undefined,
684
+ });
685
+ }
686
+ catch (error) {
687
+ onError?.(error, req);
688
+ return new Response(JSON.stringify({ error: 'AI request failed' }), { status: 500, headers: { 'Content-Type': 'application/json' } });
689
+ }
690
+ }
691
+ if (wrapHandler) {
692
+ return async (req) => wrapHandler(() => handler(req), req);
693
+ }
694
+ return handler;
695
+ }
696
+ // ============================================================================
697
+ // PII Shield Helpers
698
+ // ============================================================================
699
+ function deriveSessionId(messages) {
700
+ // Use the last user message's ID — groups approval continuations (same
701
+ // last user message) but separates distinct questions (new user message).
702
+ for (let i = messages.length - 1; i >= 0; i--) {
703
+ const msg = messages[i];
704
+ if (msg?.role === 'user' && msg?.id && typeof msg.id === 'string') {
705
+ return `ai-${msg.id}`;
706
+ }
707
+ }
708
+ return `ai-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
709
+ }
710
+ async function anonymizeMessages(messages, shield, sessionId) {
711
+ const result = [];
712
+ for (const msg of messages) {
713
+ if (typeof msg !== 'object' || msg === null) {
714
+ result.push(msg);
715
+ continue;
716
+ }
717
+ const record = msg;
718
+ // Only anonymize user messages
719
+ if (record.role !== 'user') {
720
+ result.push(msg);
721
+ continue;
722
+ }
723
+ // Anonymize string content
724
+ if (typeof record.content === 'string') {
725
+ const anonymized = await shield.outbound(record.content, sessionId);
726
+ result.push({ ...record, content: anonymized });
727
+ continue;
728
+ }
729
+ // Anonymize parts array
730
+ if (Array.isArray(record.parts)) {
731
+ const anonParts = await Promise.all(record.parts.map(async (part) => {
732
+ if (typeof part === 'object' && part !== null &&
733
+ part.type === 'text') {
734
+ const p = part;
735
+ if (typeof p.text === 'string') {
736
+ const anonymized = await shield.outbound(p.text, sessionId);
737
+ return { ...p, text: anonymized };
738
+ }
739
+ }
740
+ return part;
741
+ }));
742
+ result.push({ ...record, parts: anonParts });
743
+ continue;
744
+ }
745
+ result.push(msg);
746
+ }
747
+ return result;
748
+ }
749
+ function wrapToolsWithShield(aiTools, shield, sessionId) {
750
+ const wrapped = {};
751
+ for (const [name, raw] of Object.entries(aiTools)) {
752
+ const sdkTool = raw;
753
+ const originalExecute = sdkTool.execute;
754
+ wrapped[name] = {
755
+ ...sdkTool,
756
+ execute: async (...executeArgs) => {
757
+ // Rehydrate args (first argument is the tool input).
758
+ // Uses the async path for Lancer Aegis rehydration.
759
+ // The sync rehydrateArgs is a legacy no-op kept for backward compat.
760
+ let args = executeArgs[0];
761
+ if (args && typeof args === 'object' && !Array.isArray(args)) {
762
+ if (shield.rehydrateArgsAsync) {
763
+ args = await shield.rehydrateArgsAsync(args, sessionId);
764
+ }
765
+ else {
766
+ args = shield.rehydrateArgs(args, sessionId);
767
+ }
768
+ }
769
+ // Execute with real args
770
+ const result = await originalExecute(args, ...executeArgs.slice(1));
771
+ // Anonymize result before LLM sees it
772
+ return shield.anonymizeResult(result, sessionId);
773
+ },
774
+ };
775
+ }
776
+ return wrapped;
777
+ }
778
+ /**
779
+ * Wrap tool `needsApproval` with Lancer approval checks.
780
+ *
781
+ * When Lancer approvals are configured, each tool's approval decision is
782
+ * delegated to Glirastes instead of relying on the local `needsApproval` flag.
783
+ * If Lancer is unreachable, falls back to the existing local approval check.
784
+ *
785
+ * When a tool requires approval (Lancer returns `required: true`), the
786
+ * requestId is stored so that `decide()` can be called when the user
787
+ * approves and the tool's `execute` fires.
788
+ */
789
+ function wrapToolsWithLancerApprovals(aiTools, approvals, traceId) {
790
+ // Map toolName → requestId for pending approvals (cleared after decide)
791
+ const pendingRequestIds = new Map();
792
+ // Track tools that actually required approval (survives cross-request boundary via resolve fallback)
793
+ const approvalRequired = new Set();
794
+ const wrapped = {};
795
+ for (const [name, raw] of Object.entries(aiTools)) {
796
+ const sdkTool = raw;
797
+ const originalNeedsApproval = sdkTool.needsApproval;
798
+ const originalExecute = sdkTool.execute;
799
+ // Respect local needsApproval: false — never delegate to Lancer for these tools
800
+ if (originalNeedsApproval === false) {
801
+ wrapped[name] = sdkTool;
802
+ continue;
803
+ }
804
+ wrapped[name] = {
805
+ ...sdkTool,
806
+ // Override needsApproval with Lancer check
807
+ needsApproval: async (input) => {
808
+ try {
809
+ const inputHash = approvals.hashInput((input && typeof input === 'object' ? input : {}));
810
+ const check = await approvals.check({
811
+ toolId: sdkTool._toolId ?? name,
812
+ inputHash,
813
+ ...(traceId ? { traceId } : {}),
814
+ });
815
+ if (check.required) {
816
+ // Store requestId for later decide() call
817
+ if (check.requestId) {
818
+ pendingRequestIds.set(name, check.requestId);
819
+ }
820
+ approvalRequired.add(name);
821
+ return true;
822
+ }
823
+ // Lancer says no approval required (auto-approved or no matching flow)
824
+ return false;
825
+ }
826
+ catch {
827
+ // Graceful degradation: fall back to local needsApproval
828
+ if (typeof originalNeedsApproval === 'function') {
829
+ return originalNeedsApproval(input);
830
+ }
831
+ return originalNeedsApproval === true;
832
+ }
833
+ },
834
+ // Wrap execute to call decide() when approved.
835
+ // The requestId may be missing because needsApproval and execute run in
836
+ // different HTTP requests (Vercel AI SDK approval flow), so the in-memory
837
+ // pendingRequestIds map is recreated empty on the second request.
838
+ // Fallback: resolve by toolId + inputHash via the /v1/approvals/resolve endpoint.
839
+ execute: async (...executeArgs) => {
840
+ const requestId = pendingRequestIds.get(name);
841
+ const toolId = sdkTool._toolId ?? name;
842
+ const decidedBy = 'unknown';
843
+ if (requestId) {
844
+ pendingRequestIds.delete(name);
845
+ try {
846
+ await approvals.decide(requestId, {
847
+ decision: 'approved',
848
+ decidedBy,
849
+ });
850
+ }
851
+ catch {
852
+ // Fire-and-forget: don't block tool execution if decide fails
853
+ }
854
+ }
855
+ else if (approvalRequired.has(name)) {
856
+ // Cross-request fallback: resolve pending approval by toolId + inputHash.
857
+ // Only attempt when needsApproval previously returned true (the requestId
858
+ // may be missing because needsApproval and execute run in different HTTP
859
+ // requests, recreating the in-memory map).
860
+ approvalRequired.delete(name);
861
+ try {
862
+ const input = executeArgs[0];
863
+ const inputHash = approvals.hashInput((input && typeof input === 'object' ? input : {}));
864
+ await approvals.resolve({
865
+ toolId,
866
+ inputHash,
867
+ decision: 'approved',
868
+ decidedBy,
869
+ });
870
+ }
871
+ catch {
872
+ // Fire-and-forget: don't block tool execution if resolve fails
873
+ }
874
+ }
875
+ return originalExecute(...executeArgs);
876
+ },
877
+ };
878
+ }
879
+ return wrapped;
880
+ }
881
+ function extractLastUserText(messages) {
882
+ for (let i = messages.length - 1; i >= 0; i--) {
883
+ const msg = messages[i];
884
+ if (typeof msg !== 'object' || msg === null)
885
+ continue;
886
+ const record = msg;
887
+ if (record.role !== 'user')
888
+ continue;
889
+ if (typeof record.content === 'string')
890
+ return record.content;
891
+ if (Array.isArray(record.parts)) {
892
+ for (const part of record.parts) {
893
+ if (typeof part === 'object' &&
894
+ part !== null &&
895
+ part.type === 'text') {
896
+ const text = part.text;
897
+ if (typeof text === 'string')
898
+ return text;
899
+ }
900
+ }
901
+ }
902
+ if (Array.isArray(record.content)) {
903
+ for (const part of record.content) {
904
+ if (typeof part === 'object' &&
905
+ part !== null &&
906
+ part.type === 'text') {
907
+ const text = part.text;
908
+ if (typeof text === 'string')
909
+ return text;
910
+ }
911
+ }
912
+ }
913
+ }
914
+ return '';
915
+ }
916
+ /**
917
+ * Extract a lightweight conversation history from UIMessages for intent
918
+ * classification. Returns the last `maxPairs` user/assistant text exchanges
919
+ * EXCLUDING the current (latest) user message.
920
+ *
921
+ * Tool calls/results are stripped — only human-readable text is included
922
+ * to keep the classification prompt small and fast.
923
+ */
924
+ function extractConversationHistory(messages, maxPairs) {
925
+ // Collect text from all messages except the last user message
926
+ const textMessages = [];
927
+ for (let i = 0; i < messages.length; i++) {
928
+ const msg = messages[i];
929
+ if (typeof msg !== 'object' || msg === null)
930
+ continue;
931
+ const record = msg;
932
+ const role = record.role;
933
+ if (role !== 'user' && role !== 'assistant')
934
+ continue;
935
+ const text = extractTextFromMessage(record);
936
+ if (!text.trim())
937
+ continue;
938
+ textMessages.push({ role: role, content: text });
939
+ }
940
+ // Remove the last user message (that's the current input, not history)
941
+ if (textMessages.length > 0 && textMessages[textMessages.length - 1].role === 'user') {
942
+ textMessages.pop();
943
+ }
944
+ // Return the last N messages (up to maxPairs * 2 for user+assistant pairs)
945
+ return textMessages.slice(-(maxPairs * 2));
946
+ }
947
+ function extractTextFromMessage(msg) {
948
+ if (typeof msg.content === 'string')
949
+ return msg.content;
950
+ const parts = Array.isArray(msg.parts)
951
+ ? msg.parts
952
+ : Array.isArray(msg.content)
953
+ ? msg.content
954
+ : [];
955
+ const texts = [];
956
+ for (const part of parts) {
957
+ if (typeof part === 'object' && part !== null) {
958
+ const p = part;
959
+ if (p.type === 'text' && typeof p.text === 'string') {
960
+ texts.push(p.text);
961
+ }
962
+ }
963
+ }
964
+ return texts.join(' ');
965
+ }