@oscharko-dev/keiko-server 0.2.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 (509) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/assistant-response.d.ts +6 -0
  3. package/dist/assistant-response.d.ts.map +1 -0
  4. package/dist/assistant-response.js +12 -0
  5. package/dist/browser.d.ts +11 -0
  6. package/dist/browser.d.ts.map +1 -0
  7. package/dist/browser.js +245 -0
  8. package/dist/chat-handlers.d.ts +48 -0
  9. package/dist/chat-handlers.d.ts.map +1 -0
  10. package/dist/chat-handlers.js +821 -0
  11. package/dist/chat-stream-handlers.d.ts +4 -0
  12. package/dist/chat-stream-handlers.d.ts.map +1 -0
  13. package/dist/chat-stream-handlers.js +136 -0
  14. package/dist/conversation-prompt.d.ts +8 -0
  15. package/dist/conversation-prompt.d.ts.map +1 -0
  16. package/dist/conversation-prompt.js +36 -0
  17. package/dist/conversation-validation.d.ts +26 -0
  18. package/dist/conversation-validation.d.ts.map +1 -0
  19. package/dist/conversation-validation.js +125 -0
  20. package/dist/credentialPersistence.d.ts +23 -0
  21. package/dist/credentialPersistence.d.ts.map +1 -0
  22. package/dist/credentialPersistence.js +93 -0
  23. package/dist/credentialVault.d.ts +30 -0
  24. package/dist/credentialVault.d.ts.map +1 -0
  25. package/dist/credentialVault.js +206 -0
  26. package/dist/csp.d.ts +3 -0
  27. package/dist/csp.d.ts.map +1 -0
  28. package/dist/csp.js +75 -0
  29. package/dist/deps.d.ts +78 -0
  30. package/dist/deps.d.ts.map +1 -0
  31. package/dist/deps.js +457 -0
  32. package/dist/editor/agentRoutes.d.ts +7 -0
  33. package/dist/editor/agentRoutes.d.ts.map +1 -0
  34. package/dist/editor/agentRoutes.js +197 -0
  35. package/dist/editor/assuredGateRunner.d.ts +36 -0
  36. package/dist/editor/assuredGateRunner.d.ts.map +1 -0
  37. package/dist/editor/assuredGateRunner.js +100 -0
  38. package/dist/editor/assuredPreFilter.d.ts +34 -0
  39. package/dist/editor/assuredPreFilter.d.ts.map +1 -0
  40. package/dist/editor/assuredPreFilter.js +134 -0
  41. package/dist/editor/assuredPreFilterRunner.d.ts +31 -0
  42. package/dist/editor/assuredPreFilterRunner.d.ts.map +1 -0
  43. package/dist/editor/assuredPreFilterRunner.js +312 -0
  44. package/dist/editor/builtinLanguageProviders.d.ts +6 -0
  45. package/dist/editor/builtinLanguageProviders.d.ts.map +1 -0
  46. package/dist/editor/builtinLanguageProviders.js +221 -0
  47. package/dist/editor/codingContext.d.ts +12 -0
  48. package/dist/editor/codingContext.d.ts.map +1 -0
  49. package/dist/editor/codingContext.js +121 -0
  50. package/dist/editor/codingContextEvidence.d.ts +7 -0
  51. package/dist/editor/codingContextEvidence.d.ts.map +1 -0
  52. package/dist/editor/codingContextEvidence.js +52 -0
  53. package/dist/editor/codingContextProviders.d.ts +36 -0
  54. package/dist/editor/codingContextProviders.d.ts.map +1 -0
  55. package/dist/editor/codingContextProviders.js +348 -0
  56. package/dist/editor/completionModelEvidence.d.ts +16 -0
  57. package/dist/editor/completionModelEvidence.d.ts.map +1 -0
  58. package/dist/editor/completionModelEvidence.js +50 -0
  59. package/dist/editor/completionRoutes.d.ts +37 -0
  60. package/dist/editor/completionRoutes.d.ts.map +1 -0
  61. package/dist/editor/completionRoutes.js +411 -0
  62. package/dist/editor/contextRoutes.d.ts +6 -0
  63. package/dist/editor/contextRoutes.d.ts.map +1 -0
  64. package/dist/editor/contextRoutes.js +411 -0
  65. package/dist/editor/disposableAssuredExecution.d.ts +22 -0
  66. package/dist/editor/disposableAssuredExecution.d.ts.map +1 -0
  67. package/dist/editor/disposableAssuredExecution.js +57 -0
  68. package/dist/editor/editorCompletionModel.d.ts +47 -0
  69. package/dist/editor/editorCompletionModel.d.ts.map +1 -0
  70. package/dist/editor/editorCompletionModel.js +156 -0
  71. package/dist/editor/editorInlineCompletionModel.d.ts +34 -0
  72. package/dist/editor/editorInlineCompletionModel.d.ts.map +1 -0
  73. package/dist/editor/editorInlineCompletionModel.js +112 -0
  74. package/dist/editor/editorModelTokenBudget.d.ts +46 -0
  75. package/dist/editor/editorModelTokenBudget.d.ts.map +1 -0
  76. package/dist/editor/editorModelTokenBudget.js +121 -0
  77. package/dist/editor/inlineCompletionRateLimiter.d.ts +19 -0
  78. package/dist/editor/inlineCompletionRateLimiter.d.ts.map +1 -0
  79. package/dist/editor/inlineCompletionRateLimiter.js +46 -0
  80. package/dist/editor/inlineCompletionRoutes.d.ts +26 -0
  81. package/dist/editor/inlineCompletionRoutes.d.ts.map +1 -0
  82. package/dist/editor/inlineCompletionRoutes.js +404 -0
  83. package/dist/editor/inlineCompletionTelemetryEvidence.d.ts +5 -0
  84. package/dist/editor/inlineCompletionTelemetryEvidence.d.ts.map +1 -0
  85. package/dist/editor/inlineCompletionTelemetryEvidence.js +42 -0
  86. package/dist/editor/languageCancellation.d.ts +19 -0
  87. package/dist/editor/languageCancellation.d.ts.map +1 -0
  88. package/dist/editor/languageCancellation.js +48 -0
  89. package/dist/editor/languageProvider.d.ts +39 -0
  90. package/dist/editor/languageProvider.d.ts.map +1 -0
  91. package/dist/editor/languageProvider.js +11 -0
  92. package/dist/editor/languageRoutes.d.ts +15 -0
  93. package/dist/editor/languageRoutes.d.ts.map +1 -0
  94. package/dist/editor/languageRoutes.js +106 -0
  95. package/dist/editor/languageSanitize.d.ts +8 -0
  96. package/dist/editor/languageSanitize.d.ts.map +1 -0
  97. package/dist/editor/languageSanitize.js +101 -0
  98. package/dist/editor/languageService.d.ts +36 -0
  99. package/dist/editor/languageService.d.ts.map +1 -0
  100. package/dist/editor/languageService.js +93 -0
  101. package/dist/editor/languageServiceHost.d.ts +14 -0
  102. package/dist/editor/languageServiceHost.d.ts.map +1 -0
  103. package/dist/editor/languageServiceHost.js +242 -0
  104. package/dist/editor/localKnowledgeRetrieval.d.ts +21 -0
  105. package/dist/editor/localKnowledgeRetrieval.d.ts.map +1 -0
  106. package/dist/editor/localKnowledgeRetrieval.js +44 -0
  107. package/dist/editor/patchApplyEvidence.d.ts +21 -0
  108. package/dist/editor/patchApplyEvidence.d.ts.map +1 -0
  109. package/dist/editor/patchApplyEvidence.js +87 -0
  110. package/dist/editor/patchApplyRoutes.d.ts +16 -0
  111. package/dist/editor/patchApplyRoutes.d.ts.map +1 -0
  112. package/dist/editor/patchApplyRoutes.js +307 -0
  113. package/dist/editor/postApplyVerification.d.ts +42 -0
  114. package/dist/editor/postApplyVerification.d.ts.map +1 -0
  115. package/dist/editor/postApplyVerification.js +177 -0
  116. package/dist/editor/testGenerationEvidence.d.ts +6 -0
  117. package/dist/editor/testGenerationEvidence.d.ts.map +1 -0
  118. package/dist/editor/testGenerationEvidence.js +72 -0
  119. package/dist/editor/testGenerationPatch.d.ts +10 -0
  120. package/dist/editor/testGenerationPatch.d.ts.map +1 -0
  121. package/dist/editor/testGenerationPatch.js +66 -0
  122. package/dist/editor/testGenerationRoutes.d.ts +21 -0
  123. package/dist/editor/testGenerationRoutes.d.ts.map +1 -0
  124. package/dist/editor/testGenerationRoutes.js +254 -0
  125. package/dist/editor/testGenerationRunner.d.ts +23 -0
  126. package/dist/editor/testGenerationRunner.d.ts.map +1 -0
  127. package/dist/editor/testGenerationRunner.js +120 -0
  128. package/dist/editor/textOffsets.d.ts +6 -0
  129. package/dist/editor/textOffsets.d.ts.map +1 -0
  130. package/dist/editor/textOffsets.js +82 -0
  131. package/dist/editor/typescriptLanguageProvider.d.ts +3 -0
  132. package/dist/editor/typescriptLanguageProvider.d.ts.map +1 -0
  133. package/dist/editor/typescriptLanguageProvider.js +217 -0
  134. package/dist/evidence.d.ts +28 -0
  135. package/dist/evidence.d.ts.map +1 -0
  136. package/dist/evidence.js +145 -0
  137. package/dist/files-deny.d.ts +3 -0
  138. package/dist/files-deny.d.ts.map +1 -0
  139. package/dist/files-deny.js +12 -0
  140. package/dist/files.d.ts +97 -0
  141. package/dist/files.d.ts.map +1 -0
  142. package/dist/files.js +733 -0
  143. package/dist/gateway-setup.d.ts +10 -0
  144. package/dist/gateway-setup.d.ts.map +1 -0
  145. package/dist/gateway-setup.js +896 -0
  146. package/dist/governed-workflow.d.ts +17 -0
  147. package/dist/governed-workflow.d.ts.map +1 -0
  148. package/dist/governed-workflow.js +147 -0
  149. package/dist/grounded-answer.d.ts +12 -0
  150. package/dist/grounded-answer.d.ts.map +1 -0
  151. package/dist/grounded-answer.js +69 -0
  152. package/dist/grounded-context-index.d.ts +25 -0
  153. package/dist/grounded-context-index.d.ts.map +1 -0
  154. package/dist/grounded-context-index.js +169 -0
  155. package/dist/grounded-document-evidence.d.ts +28 -0
  156. package/dist/grounded-document-evidence.d.ts.map +1 -0
  157. package/dist/grounded-document-evidence.js +430 -0
  158. package/dist/grounded-handoff.d.ts +4 -0
  159. package/dist/grounded-handoff.d.ts.map +1 -0
  160. package/dist/grounded-handoff.js +445 -0
  161. package/dist/grounded-orchestrator.d.ts +43 -0
  162. package/dist/grounded-orchestrator.d.ts.map +1 -0
  163. package/dist/grounded-orchestrator.js +1445 -0
  164. package/dist/grounded-prompt.d.ts +2 -0
  165. package/dist/grounded-prompt.d.ts.map +1 -0
  166. package/dist/grounded-prompt.js +17 -0
  167. package/dist/grounded-qa-hybrid.d.ts +36 -0
  168. package/dist/grounded-qa-hybrid.d.ts.map +1 -0
  169. package/dist/grounded-qa-hybrid.js +762 -0
  170. package/dist/grounded-qa-multi-source.d.ts +38 -0
  171. package/dist/grounded-qa-multi-source.d.ts.map +1 -0
  172. package/dist/grounded-qa-multi-source.js +461 -0
  173. package/dist/grounded-qa.d.ts +45 -0
  174. package/dist/grounded-qa.d.ts.map +1 -0
  175. package/dist/grounded-qa.js +877 -0
  176. package/dist/grounded-rerank.d.ts +26 -0
  177. package/dist/grounded-rerank.d.ts.map +1 -0
  178. package/dist/grounded-rerank.js +72 -0
  179. package/dist/grounded-turn-registry.d.ts +23 -0
  180. package/dist/grounded-turn-registry.d.ts.map +1 -0
  181. package/dist/grounded-turn-registry.js +102 -0
  182. package/dist/headers.d.ts +3 -0
  183. package/dist/headers.d.ts.map +1 -0
  184. package/dist/headers.js +22 -0
  185. package/dist/host-check.d.ts +3 -0
  186. package/dist/host-check.d.ts.map +1 -0
  187. package/dist/host-check.js +58 -0
  188. package/dist/index.d.ts +26 -0
  189. package/dist/index.d.ts.map +1 -0
  190. package/dist/index.js +33 -0
  191. package/dist/load-csp.d.ts +3 -0
  192. package/dist/load-csp.d.ts.map +1 -0
  193. package/dist/load-csp.js +100 -0
  194. package/dist/local-knowledge-grounded-qa.d.ts +42 -0
  195. package/dist/local-knowledge-grounded-qa.d.ts.map +1 -0
  196. package/dist/local-knowledge-grounded-qa.js +678 -0
  197. package/dist/local-knowledge-handlers.d.ts +24 -0
  198. package/dist/local-knowledge-handlers.d.ts.map +1 -0
  199. package/dist/local-knowledge-handlers.js +1285 -0
  200. package/dist/local-knowledge-indexing-registry.d.ts +13 -0
  201. package/dist/local-knowledge-indexing-registry.d.ts.map +1 -0
  202. package/dist/local-knowledge-indexing-registry.js +53 -0
  203. package/dist/localKnowledgeKeyProvider.d.ts +11 -0
  204. package/dist/localKnowledgeKeyProvider.d.ts.map +1 -0
  205. package/dist/localKnowledgeKeyProvider.js +48 -0
  206. package/dist/memory-audit-event-builders.d.ts +21 -0
  207. package/dist/memory-audit-event-builders.d.ts.map +1 -0
  208. package/dist/memory-audit-event-builders.js +187 -0
  209. package/dist/memory-audit-handler.d.ts +23 -0
  210. package/dist/memory-audit-handler.d.ts.map +1 -0
  211. package/dist/memory-audit-handler.js +191 -0
  212. package/dist/memory-capture-policy.d.ts +10 -0
  213. package/dist/memory-capture-policy.d.ts.map +1 -0
  214. package/dist/memory-capture-policy.js +44 -0
  215. package/dist/memory-consolidation-handlers.d.ts +6 -0
  216. package/dist/memory-consolidation-handlers.d.ts.map +1 -0
  217. package/dist/memory-consolidation-handlers.js +491 -0
  218. package/dist/memory-consolidation-registry.d.ts +47 -0
  219. package/dist/memory-consolidation-registry.d.ts.map +1 -0
  220. package/dist/memory-consolidation-registry.js +106 -0
  221. package/dist/memory-conv-handlers.d.ts +8 -0
  222. package/dist/memory-conv-handlers.d.ts.map +1 -0
  223. package/dist/memory-conv-handlers.js +369 -0
  224. package/dist/memory-conversation-context.d.ts +13 -0
  225. package/dist/memory-conversation-context.d.ts.map +1 -0
  226. package/dist/memory-conversation-context.js +22 -0
  227. package/dist/memory-diagnostics.d.ts +29 -0
  228. package/dist/memory-diagnostics.d.ts.map +1 -0
  229. package/dist/memory-diagnostics.js +122 -0
  230. package/dist/memory-embedding.d.ts +21 -0
  231. package/dist/memory-embedding.d.ts.map +1 -0
  232. package/dist/memory-embedding.js +264 -0
  233. package/dist/memory-handlers.d.ts +19 -0
  234. package/dist/memory-handlers.d.ts.map +1 -0
  235. package/dist/memory-handlers.js +1204 -0
  236. package/dist/memory-maintenance-handlers.d.ts +35 -0
  237. package/dist/memory-maintenance-handlers.d.ts.map +1 -0
  238. package/dist/memory-maintenance-handlers.js +219 -0
  239. package/dist/memory-record-builders.d.ts +4 -0
  240. package/dist/memory-record-builders.d.ts.map +1 -0
  241. package/dist/memory-record-builders.js +19 -0
  242. package/dist/memory-retention.d.ts +31 -0
  243. package/dist/memory-retention.d.ts.map +1 -0
  244. package/dist/memory-retention.js +151 -0
  245. package/dist/memory-retrieval-signals.d.ts +12 -0
  246. package/dist/memory-retrieval-signals.d.ts.map +1 -0
  247. package/dist/memory-retrieval-signals.js +100 -0
  248. package/dist/memory-salience.d.ts +12 -0
  249. package/dist/memory-salience.d.ts.map +1 -0
  250. package/dist/memory-salience.js +154 -0
  251. package/dist/memory-scope-sanitizer.d.ts +6 -0
  252. package/dist/memory-scope-sanitizer.d.ts.map +1 -0
  253. package/dist/memory-scope-sanitizer.js +106 -0
  254. package/dist/memory-target-resolver.d.ts +4 -0
  255. package/dist/memory-target-resolver.d.ts.map +1 -0
  256. package/dist/memory-target-resolver.js +73 -0
  257. package/dist/memory-workflow-port.d.ts +14 -0
  258. package/dist/memory-workflow-port.d.ts.map +1 -0
  259. package/dist/memory-workflow-port.js +186 -0
  260. package/dist/private-json.d.ts +3 -0
  261. package/dist/private-json.d.ts.map +1 -0
  262. package/dist/private-json.js +62 -0
  263. package/dist/promptEnhancer/index.d.ts +3 -0
  264. package/dist/promptEnhancer/index.d.ts.map +1 -0
  265. package/dist/promptEnhancer/index.js +5 -0
  266. package/dist/promptEnhancer/orchestrate.d.ts +2 -0
  267. package/dist/promptEnhancer/orchestrate.d.ts.map +1 -0
  268. package/dist/promptEnhancer/orchestrate.js +5 -0
  269. package/dist/promptEnhancer/routes.d.ts +9 -0
  270. package/dist/promptEnhancer/routes.d.ts.map +1 -0
  271. package/dist/promptEnhancer/routes.js +205 -0
  272. package/dist/qualityIntelligence/capsuleAdapter.d.ts +27 -0
  273. package/dist/qualityIntelligence/capsuleAdapter.d.ts.map +1 -0
  274. package/dist/qualityIntelligence/capsuleAdapter.js +57 -0
  275. package/dist/qualityIntelligence/connectorAuthorization.d.ts +22 -0
  276. package/dist/qualityIntelligence/connectorAuthorization.d.ts.map +1 -0
  277. package/dist/qualityIntelligence/connectorAuthorization.js +35 -0
  278. package/dist/qualityIntelligence/connectorErrors.d.ts +16 -0
  279. package/dist/qualityIntelligence/connectorErrors.d.ts.map +1 -0
  280. package/dist/qualityIntelligence/connectorErrors.js +56 -0
  281. package/dist/qualityIntelligence/connectorRoutes.d.ts +7 -0
  282. package/dist/qualityIntelligence/connectorRoutes.d.ts.map +1 -0
  283. package/dist/qualityIntelligence/connectorRoutes.js +167 -0
  284. package/dist/qualityIntelligence/editRoutes.d.ts +5 -0
  285. package/dist/qualityIntelligence/editRoutes.d.ts.map +1 -0
  286. package/dist/qualityIntelligence/editRoutes.js +293 -0
  287. package/dist/qualityIntelligence/exportAssembly.d.ts +22 -0
  288. package/dist/qualityIntelligence/exportAssembly.d.ts.map +1 -0
  289. package/dist/qualityIntelligence/exportAssembly.js +352 -0
  290. package/dist/qualityIntelligence/exportRoutes.d.ts +5 -0
  291. package/dist/qualityIntelligence/exportRoutes.d.ts.map +1 -0
  292. package/dist/qualityIntelligence/exportRoutes.js +320 -0
  293. package/dist/qualityIntelligence/figma/figmaConcurrency.d.ts +8 -0
  294. package/dist/qualityIntelligence/figma/figmaConcurrency.d.ts.map +1 -0
  295. package/dist/qualityIntelligence/figma/figmaConcurrency.js +34 -0
  296. package/dist/qualityIntelligence/figma/figmaConnector.d.ts +65 -0
  297. package/dist/qualityIntelligence/figma/figmaConnector.d.ts.map +1 -0
  298. package/dist/qualityIntelligence/figma/figmaConnector.js +184 -0
  299. package/dist/qualityIntelligence/figma/figmaConnectorAudit.d.ts +52 -0
  300. package/dist/qualityIntelligence/figma/figmaConnectorAudit.d.ts.map +1 -0
  301. package/dist/qualityIntelligence/figma/figmaConnectorAudit.js +63 -0
  302. package/dist/qualityIntelligence/figma/figmaConnectorErrors.d.ts +31 -0
  303. package/dist/qualityIntelligence/figma/figmaConnectorErrors.d.ts.map +1 -0
  304. package/dist/qualityIntelligence/figma/figmaConnectorErrors.js +220 -0
  305. package/dist/qualityIntelligence/figma/figmaConnectorMetrics.d.ts +44 -0
  306. package/dist/qualityIntelligence/figma/figmaConnectorMetrics.d.ts.map +1 -0
  307. package/dist/qualityIntelligence/figma/figmaConnectorMetrics.js +49 -0
  308. package/dist/qualityIntelligence/figma/figmaConsent.d.ts +39 -0
  309. package/dist/qualityIntelligence/figma/figmaConsent.d.ts.map +1 -0
  310. package/dist/qualityIntelligence/figma/figmaConsent.js +62 -0
  311. package/dist/qualityIntelligence/figma/figmaHttpPort.d.ts +28 -0
  312. package/dist/qualityIntelligence/figma/figmaHttpPort.d.ts.map +1 -0
  313. package/dist/qualityIntelligence/figma/figmaHttpPort.js +70 -0
  314. package/dist/qualityIntelligence/figma/figmaObservedActions.d.ts +49 -0
  315. package/dist/qualityIntelligence/figma/figmaObservedActions.d.ts.map +1 -0
  316. package/dist/qualityIntelligence/figma/figmaObservedActions.js +89 -0
  317. package/dist/qualityIntelligence/figma/figmaReadiness.d.ts +32 -0
  318. package/dist/qualityIntelligence/figma/figmaReadiness.d.ts.map +1 -0
  319. package/dist/qualityIntelligence/figma/figmaReadiness.js +67 -0
  320. package/dist/qualityIntelligence/figma/figmaRenderPort.d.ts +29 -0
  321. package/dist/qualityIntelligence/figma/figmaRenderPort.d.ts.map +1 -0
  322. package/dist/qualityIntelligence/figma/figmaRenderPort.js +93 -0
  323. package/dist/qualityIntelligence/figma/figmaResnapshot.d.ts +28 -0
  324. package/dist/qualityIntelligence/figma/figmaResnapshot.d.ts.map +1 -0
  325. package/dist/qualityIntelligence/figma/figmaResnapshot.js +38 -0
  326. package/dist/qualityIntelligence/figma/figmaRetry.d.ts +31 -0
  327. package/dist/qualityIntelligence/figma/figmaRetry.d.ts.map +1 -0
  328. package/dist/qualityIntelligence/figma/figmaRetry.js +62 -0
  329. package/dist/qualityIntelligence/figma/figmaScopeRef.d.ts +9 -0
  330. package/dist/qualityIntelligence/figma/figmaScopeRef.d.ts.map +1 -0
  331. package/dist/qualityIntelligence/figma/figmaScopeRef.js +18 -0
  332. package/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts +86 -0
  333. package/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts.map +1 -0
  334. package/dist/qualityIntelligence/figma/figmaScopedPagination.js +308 -0
  335. package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts +31 -0
  336. package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts.map +1 -0
  337. package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.js +314 -0
  338. package/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts +18 -0
  339. package/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts.map +1 -0
  340. package/dist/qualityIntelligence/figma/figmaSnapshotHash.js +63 -0
  341. package/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts +65 -0
  342. package/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts.map +1 -0
  343. package/dist/qualityIntelligence/figma/figmaSnapshotTypes.js +13 -0
  344. package/dist/qualityIntelligence/figma/figmaTokenSource.d.ts +9 -0
  345. package/dist/qualityIntelligence/figma/figmaTokenSource.d.ts.map +1 -0
  346. package/dist/qualityIntelligence/figma/figmaTokenSource.js +61 -0
  347. package/dist/qualityIntelligence/figma/figmaTokenStore.d.ts +19 -0
  348. package/dist/qualityIntelligence/figma/figmaTokenStore.d.ts.map +1 -0
  349. package/dist/qualityIntelligence/figma/figmaTokenStore.js +156 -0
  350. package/dist/qualityIntelligence/figma/figmaUrl.d.ts +6 -0
  351. package/dist/qualityIntelligence/figma/figmaUrl.d.ts.map +1 -0
  352. package/dist/qualityIntelligence/figma/figmaUrl.js +36 -0
  353. package/dist/qualityIntelligence/figma/index.d.ts +20 -0
  354. package/dist/qualityIntelligence/figma/index.d.ts.map +1 -0
  355. package/dist/qualityIntelligence/figma/index.js +26 -0
  356. package/dist/qualityIntelligence/figmaCodegenRoutes.d.ts +28 -0
  357. package/dist/qualityIntelligence/figmaCodegenRoutes.d.ts.map +1 -0
  358. package/dist/qualityIntelligence/figmaCodegenRoutes.js +165 -0
  359. package/dist/qualityIntelligence/figmaSnapshotAdapter.d.ts +55 -0
  360. package/dist/qualityIntelligence/figmaSnapshotAdapter.d.ts.map +1 -0
  361. package/dist/qualityIntelligence/figmaSnapshotAdapter.js +219 -0
  362. package/dist/qualityIntelligence/figmaSnapshotOrchestration.d.ts +64 -0
  363. package/dist/qualityIntelligence/figmaSnapshotOrchestration.d.ts.map +1 -0
  364. package/dist/qualityIntelligence/figmaSnapshotOrchestration.js +203 -0
  365. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +112 -0
  366. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -0
  367. package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1063 -0
  368. package/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts +19 -0
  369. package/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts.map +1 -0
  370. package/dist/qualityIntelligence/figmaSnapshotScreenIds.js +75 -0
  371. package/dist/qualityIntelligence/generationPort.d.ts +15 -0
  372. package/dist/qualityIntelligence/generationPort.d.ts.map +1 -0
  373. package/dist/qualityIntelligence/generationPort.js +185 -0
  374. package/dist/qualityIntelligence/handoffErrors.d.ts +9 -0
  375. package/dist/qualityIntelligence/handoffErrors.d.ts.map +1 -0
  376. package/dist/qualityIntelligence/handoffErrors.js +21 -0
  377. package/dist/qualityIntelligence/handoffRoutes.d.ts +15 -0
  378. package/dist/qualityIntelligence/handoffRoutes.d.ts.map +1 -0
  379. package/dist/qualityIntelligence/handoffRoutes.js +341 -0
  380. package/dist/qualityIntelligence/index.d.ts +17 -0
  381. package/dist/qualityIntelligence/index.d.ts.map +1 -0
  382. package/dist/qualityIntelligence/index.js +36 -0
  383. package/dist/qualityIntelligence/judgePort.d.ts +30 -0
  384. package/dist/qualityIntelligence/judgePort.d.ts.map +1 -0
  385. package/dist/qualityIntelligence/judgePort.js +326 -0
  386. package/dist/qualityIntelligence/modelSelection.d.ts +58 -0
  387. package/dist/qualityIntelligence/modelSelection.d.ts.map +1 -0
  388. package/dist/qualityIntelligence/modelSelection.js +148 -0
  389. package/dist/qualityIntelligence/reCheckRoutes.d.ts +6 -0
  390. package/dist/qualityIntelligence/reCheckRoutes.d.ts.map +1 -0
  391. package/dist/qualityIntelligence/reCheckRoutes.js +1157 -0
  392. package/dist/qualityIntelligence/retentionEnforcement.d.ts +13 -0
  393. package/dist/qualityIntelligence/retentionEnforcement.d.ts.map +1 -0
  394. package/dist/qualityIntelligence/retentionEnforcement.js +47 -0
  395. package/dist/qualityIntelligence/retentionRoutes.d.ts +8 -0
  396. package/dist/qualityIntelligence/retentionRoutes.d.ts.map +1 -0
  397. package/dist/qualityIntelligence/retentionRoutes.js +74 -0
  398. package/dist/qualityIntelligence/reviewRoutes.d.ts +5 -0
  399. package/dist/qualityIntelligence/reviewRoutes.d.ts.map +1 -0
  400. package/dist/qualityIntelligence/reviewRoutes.js +145 -0
  401. package/dist/qualityIntelligence/reviewStore.d.ts +75 -0
  402. package/dist/qualityIntelligence/reviewStore.d.ts.map +1 -0
  403. package/dist/qualityIntelligence/reviewStore.js +170 -0
  404. package/dist/qualityIntelligence/runExecution.d.ts +36 -0
  405. package/dist/qualityIntelligence/runExecution.d.ts.map +1 -0
  406. package/dist/qualityIntelligence/runExecution.js +180 -0
  407. package/dist/qualityIntelligence/runIngestion.d.ts +70 -0
  408. package/dist/qualityIntelligence/runIngestion.d.ts.map +1 -0
  409. package/dist/qualityIntelligence/runIngestion.js +1235 -0
  410. package/dist/qualityIntelligence/runRegistry.d.ts +31 -0
  411. package/dist/qualityIntelligence/runRegistry.d.ts.map +1 -0
  412. package/dist/qualityIntelligence/runRegistry.js +66 -0
  413. package/dist/qualityIntelligence/runRoutes.d.ts +16 -0
  414. package/dist/qualityIntelligence/runRoutes.d.ts.map +1 -0
  415. package/dist/qualityIntelligence/runRoutes.js +357 -0
  416. package/dist/qualityIntelligence/traceabilityRoutes.d.ts +5 -0
  417. package/dist/qualityIntelligence/traceabilityRoutes.d.ts.map +1 -0
  418. package/dist/qualityIntelligence/traceabilityRoutes.js +173 -0
  419. package/dist/qualityIntelligence/uiRoutes.d.ts +7 -0
  420. package/dist/qualityIntelligence/uiRoutes.d.ts.map +1 -0
  421. package/dist/qualityIntelligence/uiRoutes.js +336 -0
  422. package/dist/read-handlers.d.ts +9 -0
  423. package/dist/read-handlers.d.ts.map +1 -0
  424. package/dist/read-handlers.js +265 -0
  425. package/dist/relationship-handlers.d.ts +191 -0
  426. package/dist/relationship-handlers.d.ts.map +1 -0
  427. package/dist/relationship-handlers.js +0 -0
  428. package/dist/routes.d.ts +37 -0
  429. package/dist/routes.d.ts.map +1 -0
  430. package/dist/routes.js +507 -0
  431. package/dist/run-engine.d.ts +25 -0
  432. package/dist/run-engine.d.ts.map +1 -0
  433. package/dist/run-engine.js +385 -0
  434. package/dist/run-handlers.d.ts +9 -0
  435. package/dist/run-handlers.d.ts.map +1 -0
  436. package/dist/run-handlers.js +465 -0
  437. package/dist/run-request.d.ts +17 -0
  438. package/dist/run-request.d.ts.map +1 -0
  439. package/dist/run-request.js +219 -0
  440. package/dist/runs.d.ts +47 -0
  441. package/dist/runs.d.ts.map +1 -0
  442. package/dist/runs.js +100 -0
  443. package/dist/server.d.ts +13 -0
  444. package/dist/server.d.ts.map +1 -0
  445. package/dist/server.js +152 -0
  446. package/dist/sink.d.ts +28 -0
  447. package/dist/sink.d.ts.map +1 -0
  448. package/dist/sink.js +80 -0
  449. package/dist/sse-write.d.ts +9 -0
  450. package/dist/sse-write.d.ts.map +1 -0
  451. package/dist/sse-write.js +26 -0
  452. package/dist/sse.d.ts +8 -0
  453. package/dist/sse.d.ts.map +1 -0
  454. package/dist/sse.js +27 -0
  455. package/dist/static.d.ts +5 -0
  456. package/dist/static.d.ts.map +1 -0
  457. package/dist/static.js +76 -0
  458. package/dist/store/chats.d.ts +17 -0
  459. package/dist/store/chats.d.ts.map +1 -0
  460. package/dist/store/chats.js +624 -0
  461. package/dist/store/db.d.ts +11 -0
  462. package/dist/store/db.d.ts.map +1 -0
  463. package/dist/store/db.js +203 -0
  464. package/dist/store/errors.d.ts +13 -0
  465. package/dist/store/errors.d.ts.map +1 -0
  466. package/dist/store/errors.js +30 -0
  467. package/dist/store/index.d.ts +7 -0
  468. package/dist/store/index.d.ts.map +1 -0
  469. package/dist/store/index.js +6 -0
  470. package/dist/store/messages.d.ts +8 -0
  471. package/dist/store/messages.d.ts.map +1 -0
  472. package/dist/store/messages.js +149 -0
  473. package/dist/store/paths.d.ts +5 -0
  474. package/dist/store/paths.d.ts.map +1 -0
  475. package/dist/store/paths.js +84 -0
  476. package/dist/store/projects.d.ts +8 -0
  477. package/dist/store/projects.d.ts.map +1 -0
  478. package/dist/store/projects.js +59 -0
  479. package/dist/store/relationship-audit.d.ts +42 -0
  480. package/dist/store/relationship-audit.d.ts.map +1 -0
  481. package/dist/store/relationship-audit.js +155 -0
  482. package/dist/store/relationships.d.ts +191 -0
  483. package/dist/store/relationships.d.ts.map +1 -0
  484. package/dist/store/relationships.js +724 -0
  485. package/dist/store/schema.d.ts +4 -0
  486. package/dist/store/schema.d.ts.map +1 -0
  487. package/dist/store/schema.js +220 -0
  488. package/dist/store/types.d.ts +29 -0
  489. package/dist/store/types.d.ts.map +1 -0
  490. package/dist/store/types.js +8 -0
  491. package/dist/store/validation.d.ts +7 -0
  492. package/dist/store/validation.d.ts.map +1 -0
  493. package/dist/store/validation.js +117 -0
  494. package/dist/store-handlers.d.ts +17 -0
  495. package/dist/store-handlers.d.ts.map +1 -0
  496. package/dist/store-handlers.js +872 -0
  497. package/dist/terminal-errors.d.ts +22 -0
  498. package/dist/terminal-errors.d.ts.map +1 -0
  499. package/dist/terminal-errors.js +45 -0
  500. package/dist/terminal-evidence.d.ts +21 -0
  501. package/dist/terminal-evidence.d.ts.map +1 -0
  502. package/dist/terminal-evidence.js +65 -0
  503. package/dist/terminal-routes.d.ts +10 -0
  504. package/dist/terminal-routes.d.ts.map +1 -0
  505. package/dist/terminal-routes.js +219 -0
  506. package/dist/terminal.d.ts +68 -0
  507. package/dist/terminal.d.ts.map +1 -0
  508. package/dist/terminal.js +855 -0
  509. package/package.json +52 -0
@@ -0,0 +1,821 @@
1
+ // Desktop chat BFF routes for the Keiko canvas UI. These routes intentionally keep the model call
2
+ // behind the existing ModelPort/Gateway boundary: the browser sends only chat content and a registry
3
+ // model id, while provider endpoints and keys remain resolved from the local gateway config/.env.
4
+ import { randomUUID } from "node:crypto";
5
+ import { basename } from "node:path";
6
+ import { GatewayError, findCapability, findConfiguredCapability, listCapabilities, listConfiguredCapabilities, } from "@oscharko-dev/keiko-model-gateway";
7
+ import { retrieveMemoryContext } from "@oscharko-dev/keiko-memory-retrieval";
8
+ import { maybeRunAutoMaintenance, } from "./memory-maintenance-handlers.js";
9
+ import { buildConversationRetrievalSignals, conversationFusionMode, } from "./memory-retrieval-signals.js";
10
+ import { extractCandidatesFromUserText, memoryTextEgressRejectionReason, } from "@oscharko-dev/keiko-memory-capture";
11
+ import { UiStoreError, isProjectAvailable, } from "./store/index.js";
12
+ import { CONVERSATION_SYSTEM_PROMPT, composeConversationPrompt } from "./conversation-prompt.js";
13
+ import { validateConversationPayload, } from "./conversation-validation.js";
14
+ import { validateProjectPath } from "./store/validation.js";
15
+ import { redact } from "@oscharko-dev/keiko-security";
16
+ import { currentGatewayConfig, currentRedactionSecrets } from "./deps.js";
17
+ import { errorBody } from "./routes.js";
18
+ import { createMemoryTargetResolver } from "./memory-target-resolver.js";
19
+ import { isPersistableMemoryCandidate, memoryCapturePolicyForDeps, SENSITIVE_MEMORY_REJECTION_REASON, } from "./memory-capture-policy.js";
20
+ import { vaultAsQueryPort } from "./memory-conv-handlers.js";
21
+ import { conversationMemoryScopes, resolveConversationMemoryContext, } from "./memory-conversation-context.js";
22
+ import { buildMemoryRecordFromProposal } from "./memory-record-builders.js";
23
+ import { embedAndStoreMemory } from "./memory-embedding.js";
24
+ import { recordMemoryAudit } from "./memory-audit-handler.js";
25
+ import { captureSalientFromTurn } from "./memory-salience.js";
26
+ import { assertUsableAssistantContent, isLegacyEmptyAssistantPlaceholder, } from "./assistant-response.js";
27
+ const DEFAULT_CHAT_MODEL = "example-chat-model";
28
+ const DEFAULT_CHAT_TITLE = "New chat";
29
+ const MAX_BODY_BYTES = 128_000;
30
+ const MAX_CHAT_INPUT_CHARS = 16_000;
31
+ const MAX_CONTEXT_MESSAGES = 24;
32
+ class BodyTooLargeError extends Error {
33
+ constructor() {
34
+ super("request body too large");
35
+ this.name = "BodyTooLargeError";
36
+ }
37
+ }
38
+ function isRecord(value) {
39
+ return typeof value === "object" && value !== null && !Array.isArray(value);
40
+ }
41
+ function readBody(req) {
42
+ return new Promise((resolveBody, reject) => {
43
+ const chunks = [];
44
+ let total = 0;
45
+ let capped = false;
46
+ req.on("data", (chunk) => {
47
+ total += chunk.length;
48
+ if (total > MAX_BODY_BYTES) {
49
+ if (!capped) {
50
+ capped = true;
51
+ chunks.length = 0;
52
+ reject(new BodyTooLargeError());
53
+ req.resume();
54
+ }
55
+ return;
56
+ }
57
+ chunks.push(chunk);
58
+ });
59
+ req.on("end", () => {
60
+ if (!capped) {
61
+ resolveBody(Buffer.concat(chunks).toString("utf8"));
62
+ }
63
+ });
64
+ req.on("error", reject);
65
+ });
66
+ }
67
+ async function readJsonObject(req) {
68
+ let raw;
69
+ try {
70
+ raw = await readBody(req);
71
+ }
72
+ catch (error) {
73
+ if (error instanceof BodyTooLargeError) {
74
+ return {
75
+ status: 413,
76
+ body: errorBody("PAYLOAD_TOO_LARGE", "Request body exceeds the size limit."),
77
+ };
78
+ }
79
+ throw error;
80
+ }
81
+ let parsed;
82
+ try {
83
+ parsed = raw.length === 0 ? {} : JSON.parse(raw);
84
+ }
85
+ catch {
86
+ return { status: 400, body: errorBody("BAD_REQUEST", "Request body is not valid JSON.") };
87
+ }
88
+ if (!isRecord(parsed)) {
89
+ return { status: 400, body: errorBody("BAD_REQUEST", "Request body must be a JSON object.") };
90
+ }
91
+ return parsed;
92
+ }
93
+ function isRouteResult(value) {
94
+ return isRecord(value) && typeof value.status === "number" && "body" in value;
95
+ }
96
+ function chatCapability(deps, modelId) {
97
+ const config = currentGatewayConfig(deps);
98
+ return config === undefined ? findCapability(modelId) : findConfiguredCapability(config, modelId);
99
+ }
100
+ function defaultChatModelId(deps) {
101
+ const config = currentGatewayConfig(deps);
102
+ if (config === undefined) {
103
+ return DEFAULT_CHAT_MODEL;
104
+ }
105
+ const configured = listConfiguredCapabilities(config);
106
+ return ((configured.find((model) => model.id === DEFAULT_CHAT_MODEL && model.kind === "chat") ??
107
+ configured.find((model) => model.kind === "chat"))?.id ?? DEFAULT_CHAT_MODEL);
108
+ }
109
+ function modelFromBody(body, deps) {
110
+ const modelId = typeof body.modelId === "string" && body.modelId.length > 0
111
+ ? body.modelId
112
+ : defaultChatModelId(deps);
113
+ const capability = chatCapability(deps, modelId);
114
+ if (capability?.kind !== "chat") {
115
+ return {
116
+ status: 400,
117
+ body: errorBody("BAD_REQUEST", "modelId must be a configured chat model id."),
118
+ };
119
+ }
120
+ return modelId;
121
+ }
122
+ function pickProjectPath(body, deps) {
123
+ const supplied = typeof body.projectPath === "string" && body.projectPath.length > 0
124
+ ? body.projectPath
125
+ : undefined;
126
+ if (supplied !== undefined) {
127
+ return validateProjectPath(supplied, { mustExist: true });
128
+ }
129
+ const projects = deps.store.listProjects();
130
+ const preferred = projects.find((project) => project.path === deps.preferredProjectPath && isProjectAvailable(project));
131
+ if (preferred !== undefined) {
132
+ return preferred.path;
133
+ }
134
+ const available = projects.find((project) => isProjectAvailable(project));
135
+ if (available !== undefined) {
136
+ return available.path;
137
+ }
138
+ return validateProjectPath(process.cwd(), { mustExist: true });
139
+ }
140
+ function ensureProject(deps, path) {
141
+ const existing = deps.store.listProjects().find((project) => project.path === path);
142
+ if (existing !== undefined) {
143
+ deps.store.updateProject(path, {});
144
+ return existing;
145
+ }
146
+ const name = basename(path) || "Local workspace";
147
+ return deps.store.createProject(path, name);
148
+ }
149
+ function findChat(deps, projectPath, chatId) {
150
+ return deps.store.listChats(projectPath).find((chat) => chat.id === chatId);
151
+ }
152
+ function chatEnvelope(deps, project, chat) {
153
+ const projects = deps.store.listProjects().map((item) => ({
154
+ ...item,
155
+ available: isProjectAvailable(item),
156
+ }));
157
+ const chats = deps.store.listChats(project.path);
158
+ const messages = deps.store
159
+ .listMessages(chat.id)
160
+ .filter((message) => !isLegacyEmptyAssistantPlaceholder(message));
161
+ return {
162
+ project: { ...project, available: isProjectAvailable(project) },
163
+ chat,
164
+ messages,
165
+ projects,
166
+ chats,
167
+ };
168
+ }
169
+ // Issue #154 — every conversation error message is scrubbed through redact() before it can
170
+ // reach the wire. GatewayError messages may carry the provider base URL, response body excerpts,
171
+ // or `Bearer …` tokens echoed back by the provider; UiStoreError messages may carry user-controlled
172
+ // path fragments. Redaction at this single boundary keeps gateway credentials and provider endpoints
173
+ // out of conversation error envelopes (AC #2 + AC #4).
174
+ //
175
+ // Epic #177 audit: read the LIVE gateway-derived secrets via currentRedactionSecrets(deps) so
176
+ // values added through PATCH /api/gateway/config after process start are scrubbed too. The
177
+ // `deps.redactionSecrets` field is the startup snapshot frozen by buildUiHandlerDeps and would
178
+ // miss any runtime-added apiKey/baseUrl.
179
+ export function redactErrorMessage(message, deps) {
180
+ return redact(message, currentRedactionSecrets(deps));
181
+ }
182
+ function gatewayErrorResult(error, deps) {
183
+ const status = error.code === "GATEWAY_AUTHENTICATION" ? 401 : error.retryable ? 503 : 502;
184
+ return { status, body: errorBody(error.code, redactErrorMessage(error.message, deps)) };
185
+ }
186
+ export function desktopChatErrorResult(error, deps) {
187
+ if (error instanceof GatewayError) {
188
+ return gatewayErrorResult(error, deps);
189
+ }
190
+ if (error instanceof UiStoreError) {
191
+ return {
192
+ status: error.status,
193
+ body: errorBody(error.code, redactErrorMessage(error.message, deps)),
194
+ };
195
+ }
196
+ throw error;
197
+ }
198
+ function messageForGateway(message) {
199
+ if (isLegacyEmptyAssistantPlaceholder(message)) {
200
+ return null;
201
+ }
202
+ if (message.role !== "user" && message.role !== "assistant") {
203
+ return null;
204
+ }
205
+ return { role: message.role, content: message.content };
206
+ }
207
+ function conversationForGateway(messages) {
208
+ const usable = messages
209
+ .map(messageForGateway)
210
+ .filter((message) => message !== null)
211
+ .slice(-MAX_CONTEXT_MESSAGES);
212
+ return [
213
+ {
214
+ role: "system",
215
+ content: CONVERSATION_SYSTEM_PROMPT,
216
+ },
217
+ ...usable,
218
+ ];
219
+ }
220
+ function scopeLabel(scope) {
221
+ switch (scope.kind) {
222
+ case "user":
223
+ return "User memory";
224
+ case "workspace":
225
+ return "Workspace memory";
226
+ case "project":
227
+ return "Project memory";
228
+ case "workflow":
229
+ return "Workflow memory";
230
+ case "global":
231
+ return "Global memory";
232
+ }
233
+ }
234
+ function parseMemoryContext(value) {
235
+ if (!isRecord(value)) {
236
+ return { status: 400, body: errorBody("BAD_REQUEST", "memory.context must be an object.") };
237
+ }
238
+ return value;
239
+ }
240
+ function parseMemoryEnabled(raw) {
241
+ if (raw.enabled === undefined)
242
+ return true;
243
+ if (typeof raw.enabled === "boolean")
244
+ return raw.enabled;
245
+ return { status: 400, body: errorBody("BAD_REQUEST", "memory.enabled must be a boolean.") };
246
+ }
247
+ function parseMemoryBudget(raw) {
248
+ const budgetTokens = pickNumber(raw, "budgetTokens");
249
+ if (budgetTokens === undefined)
250
+ return undefined;
251
+ if (Number.isFinite(budgetTokens) && Number.isInteger(budgetTokens) && budgetTokens >= 0) {
252
+ return budgetTokens;
253
+ }
254
+ return {
255
+ status: 400,
256
+ body: errorBody("BAD_REQUEST", "memory.budgetTokens must be a non-negative integer."),
257
+ };
258
+ }
259
+ function parseMemoryRequest(value) {
260
+ if (value === undefined)
261
+ return undefined;
262
+ if (!isRecord(value)) {
263
+ return { status: 400, body: errorBody("BAD_REQUEST", "memory must be an object.") };
264
+ }
265
+ const context = parseMemoryContext(value.context);
266
+ if (isRouteResult(context))
267
+ return context;
268
+ const enabled = parseMemoryEnabled(value);
269
+ if (isRouteResult(enabled))
270
+ return enabled;
271
+ const budgetTokens = parseMemoryBudget(value);
272
+ if (isRouteResult(budgetTokens))
273
+ return budgetTokens;
274
+ return {
275
+ enabled,
276
+ ...(budgetTokens !== undefined ? { budgetTokens } : {}),
277
+ context,
278
+ };
279
+ }
280
+ const MAX_ATTACHMENT_ENTRIES = 16;
281
+ function parseAttachmentEntry(value) {
282
+ if (!isRecord(value))
283
+ return undefined;
284
+ const kind = value.kind;
285
+ if (kind !== "image" && kind !== "document")
286
+ return undefined;
287
+ const mimeType = pickString(value, "mimeType");
288
+ const sizeBytes = pickNumber(value, "sizeBytes");
289
+ if (mimeType === undefined || mimeType.length === 0)
290
+ return undefined;
291
+ if (sizeBytes === undefined ||
292
+ sizeBytes < 0 ||
293
+ !Number.isFinite(sizeBytes) ||
294
+ !Number.isInteger(sizeBytes))
295
+ return undefined;
296
+ return { kind, mimeType, sizeBytes };
297
+ }
298
+ function parseAttachments(value) {
299
+ if (!Array.isArray(value))
300
+ return [];
301
+ const out = [];
302
+ for (const entry of value.slice(0, MAX_ATTACHMENT_ENTRIES)) {
303
+ const parsed = parseAttachmentEntry(entry);
304
+ if (parsed !== undefined)
305
+ out.push(parsed);
306
+ }
307
+ return out;
308
+ }
309
+ // Snapshot of the model capability registry the validator inspects. When a gateway config is
310
+ // loaded, the configured-capabilities path takes precedence so private models registered by
311
+ // .env participate in the modality check exactly as they do at chatCapability() lookup time.
312
+ // With no config, we fall back to the static built-in capability list — matches the same
313
+ // resolution semantics chatCapability() uses for the single-id check.
314
+ function modelCapabilityRegistry(deps) {
315
+ const config = currentGatewayConfig(deps);
316
+ const capabilities = config === undefined ? listCapabilities() : listConfiguredCapabilities(config);
317
+ const registry = new Map();
318
+ for (const capability of capabilities) {
319
+ registry.set(capability.id, capability);
320
+ }
321
+ return registry;
322
+ }
323
+ const MAX_DOCUMENT_CONTEXT_ENTRIES = 16;
324
+ const MAX_DOCUMENT_CONTEXT_TEXT_BYTES = 65_536; // mirrors MAX_EXTRACTED_BYTES per doc
325
+ const MAX_DOCUMENT_DISPLAY_NAME = 256;
326
+ const MAX_DOCUMENT_TRUNCATION_MARKER_BYTES = 256;
327
+ function pickString(record, key) {
328
+ const value = record[key];
329
+ return typeof value === "string" ? value : undefined;
330
+ }
331
+ function pickNumber(record, key) {
332
+ const value = record[key];
333
+ return typeof value === "number" ? value : undefined;
334
+ }
335
+ function pickBoolean(record, key) {
336
+ const value = record[key];
337
+ return typeof value === "boolean" ? value : undefined;
338
+ }
339
+ function readDocumentContextFields(value) {
340
+ const id = pickString(value, "id");
341
+ const displayName = pickString(value, "displayName");
342
+ const mimeType = pickString(value, "mimeType");
343
+ const sizeBytes = pickNumber(value, "sizeBytes");
344
+ const extractedBytes = pickNumber(value, "extractedBytes");
345
+ const truncated = pickBoolean(value, "truncated");
346
+ const text = pickString(value, "text");
347
+ if (id === undefined ||
348
+ displayName === undefined ||
349
+ mimeType === undefined ||
350
+ sizeBytes === undefined ||
351
+ extractedBytes === undefined ||
352
+ truncated === undefined ||
353
+ text === undefined) {
354
+ return undefined;
355
+ }
356
+ return { id, displayName, mimeType, sizeBytes, extractedBytes, truncated, text };
357
+ }
358
+ function fieldsWithinCaps(fields) {
359
+ // `string.length` returns UTF-16 code units, which under-counts bytes for any non-ASCII
360
+ // content (e.g. "漢" = 1 code unit but 3 UTF-8 bytes). The model prompt is bounded in UTF-8
361
+ // bytes, so we MUST measure the same way here. Also enforce that the declared sizes are
362
+ // finite non-negative INTEGERS so callers cannot ship NaN/Infinity/1.5 and bypass the cap.
363
+ return (fields.displayName.length > 0 &&
364
+ fields.displayName.length <= MAX_DOCUMENT_DISPLAY_NAME &&
365
+ Buffer.byteLength(fields.text, "utf8") <= MAX_DOCUMENT_CONTEXT_TEXT_BYTES &&
366
+ Number.isInteger(fields.sizeBytes) &&
367
+ fields.sizeBytes >= 0 &&
368
+ Number.isInteger(fields.extractedBytes) &&
369
+ fields.extractedBytes >= 0);
370
+ }
371
+ function parseDocumentContextEntry(value) {
372
+ if (!isRecord(value))
373
+ return undefined;
374
+ const fields = readDocumentContextFields(value);
375
+ if (fields === undefined)
376
+ return undefined;
377
+ // Defence-in-depth caps. The client extractor already enforces these, but the server is
378
+ // the trust boundary for what reaches the model prompt.
379
+ if (!fieldsWithinCaps(fields))
380
+ return undefined;
381
+ const truncationMarker = typeof value.truncationMarker === "string" ? value.truncationMarker : undefined;
382
+ if (truncationMarker !== undefined &&
383
+ Buffer.byteLength(truncationMarker, "utf8") > MAX_DOCUMENT_TRUNCATION_MARKER_BYTES) {
384
+ return undefined;
385
+ }
386
+ return { ...fields, truncationMarker };
387
+ }
388
+ function parseDocumentContext(value) {
389
+ if (!Array.isArray(value))
390
+ return [];
391
+ const out = [];
392
+ for (const entry of value.slice(0, MAX_DOCUMENT_CONTEXT_ENTRIES)) {
393
+ const parsed = parseDocumentContextEntry(entry);
394
+ if (parsed !== undefined)
395
+ out.push(parsed);
396
+ }
397
+ return out;
398
+ }
399
+ // eslint-disable-next-line complexity
400
+ function sendRequestFromBody(body) {
401
+ const chatId = typeof body.chatId === "string" ? body.chatId : "";
402
+ const projectPath = typeof body.projectPath === "string" ? body.projectPath : "";
403
+ if (chatId.length === 0 || projectPath.length === 0) {
404
+ return { status: 400, body: errorBody("BAD_REQUEST", "chatId and projectPath are required.") };
405
+ }
406
+ const content = typeof body.content === "string" ? body.content.trim() : "";
407
+ if (content.length === 0 || content.length > MAX_CHAT_INPUT_CHARS) {
408
+ return {
409
+ status: 400,
410
+ body: errorBody("BAD_REQUEST", "content must be between 1 and 16000 characters."),
411
+ };
412
+ }
413
+ const memory = parseMemoryRequest(body.memory);
414
+ if (isRouteResult(memory))
415
+ return memory;
416
+ return {
417
+ chatId,
418
+ projectPath,
419
+ content,
420
+ modelId: typeof body.modelId === "string" && body.modelId.length > 0 ? body.modelId : undefined,
421
+ documentContext: parseDocumentContext(body.documentContext),
422
+ attachments: parseAttachments(body.attachments),
423
+ memory,
424
+ };
425
+ }
426
+ function invalidChatModelResult(modelId, deps) {
427
+ const capability = chatCapability(deps, modelId);
428
+ if (capability?.kind === "chat") {
429
+ return undefined;
430
+ }
431
+ return {
432
+ status: 400,
433
+ body: errorBody("BAD_REQUEST", "modelId must be a configured chat model id."),
434
+ };
435
+ }
436
+ export function createUserMessage(deps, request) {
437
+ return deps.store.createMessage({
438
+ chatId: request.chatId,
439
+ role: "user",
440
+ content: request.content,
441
+ timestamp: Date.now(),
442
+ runId: undefined,
443
+ workflowId: undefined,
444
+ workflowStatus: undefined,
445
+ shortResult: undefined,
446
+ taskType: undefined,
447
+ });
448
+ }
449
+ export function createAssistantMessage(deps, request, content, modelId) {
450
+ assertUsableAssistantContent(content, modelId);
451
+ return deps.store.createMessage({
452
+ chatId: request.chatId,
453
+ role: "assistant",
454
+ content,
455
+ timestamp: Date.now(),
456
+ runId: undefined,
457
+ workflowId: undefined,
458
+ workflowStatus: undefined,
459
+ shortResult: undefined,
460
+ taskType: undefined,
461
+ });
462
+ }
463
+ // Issue #148 — projects the latest user turn into the structured prompt form (user message +
464
+ // attached document blocks). Earlier history turns stay verbatim — the document context is a
465
+ // per-send payload and never replayed across the conversation log.
466
+ function applyDocumentContextToLatestUserTurn(history, request, memoryText) {
467
+ if (request.documentContext.length === 0 &&
468
+ (memoryText === undefined || memoryText.length === 0)) {
469
+ return Array.from(history);
470
+ }
471
+ const composed = composeConversationPrompt(request.content, request.documentContext, memoryText);
472
+ // Replace ONLY the last user turn (the one we just persisted). System and assistant turns
473
+ // are untouched. Walking from the end avoids rewriting a same-text earlier turn.
474
+ const out = Array.from(history);
475
+ for (let i = out.length - 1; i >= 0; i -= 1) {
476
+ const entry = out[i];
477
+ if (entry?.role === "user" && entry.content === request.content) {
478
+ out[i] = { role: "user", content: composed };
479
+ break;
480
+ }
481
+ }
482
+ return out;
483
+ }
484
+ export function emptyMemoryResult(enabled) {
485
+ return {
486
+ context: {
487
+ enabled,
488
+ text: "",
489
+ memories: [],
490
+ budget: { tokens: 0, used: 0 },
491
+ },
492
+ actions: [],
493
+ };
494
+ }
495
+ function recordConversationMemoryRetrieval(deps, context, memories) {
496
+ if (memories.length === 0) {
497
+ return;
498
+ }
499
+ const event = {
500
+ schemaVersion: "1",
501
+ kind: "memory:retrieved",
502
+ eventId: randomUUID(),
503
+ occurredAt: Date.now(),
504
+ initiatorSurface: "conversation-center",
505
+ summary: memories.length === 1
506
+ ? "Retrieved 1 memory for a conversation request."
507
+ : `Retrieved ${String(memories.length)} memories for a conversation request.`,
508
+ scopes: conversationMemoryScopes(context),
509
+ matchedMemoryIds: memories.map((memory) => memory.memoryId),
510
+ };
511
+ recordMemoryAudit({ evidenceStore: deps.evidenceStore }, event);
512
+ }
513
+ // The candidate-id gathering, semantic scoring, and strength projection that both this chat path and
514
+ // the BFF /api/memory/context route need now live in ONE place — memory-retrieval-signals.ts — so the
515
+ // two surfaces cannot drift (#204, O-F4). See buildConversationRetrievalSignals.
516
+ function toMemoryResult(retrieval) {
517
+ return {
518
+ context: {
519
+ enabled: true,
520
+ text: retrieval.contextBlock.text,
521
+ memories: retrieval.contextBlock.memories.map((item) => ({
522
+ memoryId: String(item.memoryId),
523
+ bodyExcerpt: item.bodyExcerpt,
524
+ inclusionReason: item.inclusionReason,
525
+ sourceKind: item.sourceKind,
526
+ ...(item.captureRationale !== undefined ? { captureRationale: item.captureRationale } : {}),
527
+ sensitivity: item.sensitivity,
528
+ confidence: item.confidence,
529
+ status: item.status,
530
+ capturedAt: item.capturedAt,
531
+ })),
532
+ budget: retrieval.budget,
533
+ },
534
+ actions: [],
535
+ };
536
+ }
537
+ // Process-lifetime rate-limit cursor for autonomous maintenance (#204, O-V4). One loopback server =
538
+ // one cursor, so the >=6h interval is honoured across chat turns. Module-scoped (not on deps) so it
539
+ // is never shared across test fixtures; auto-maintenance is opt-in (env), so tests that do not set
540
+ // the flag never advance it.
541
+ const memoryMaintenanceCursor = {};
542
+ // Opportunistic, bounded, rate-limited (#204, O-V4) maintenance fired once memory is in use. Opt-in
543
+ // via env (default off so existing behaviour is unchanged); short-circuits on the cursor almost
544
+ // every turn and never throws into the chat path.
545
+ function maybeRunChatAutoMaintenance(deps, vault) {
546
+ maybeRunAutoMaintenance(vault, deps.evidenceStore, memoryMaintenanceCursor, {
547
+ nowMs: Date.now(),
548
+ enabled: deps.env.KEIKO_MEMORY_AUTO_MAINTAIN === "1",
549
+ });
550
+ }
551
+ // Build the embedding/strength/diversity signals and run scoped retrieval — the shared pipeline the
552
+ // BFF route also uses (#204, O-F2/O-F3/O-F4/O-P1). semanticById is gated on the secondary-model
553
+ // egress check; all signals are passed only when present so a fresh vault ranks byte-identically, and
554
+ // the fusion mode is env-opt-in (default weighted-sum).
555
+ async function retrieveChatMemory(deps, vault, scopes, content, budgetTokens, nowMs) {
556
+ const safeForSecondaryModel = memoryTextEgressRejectionReason(content, memoryCapturePolicyForDeps(deps)) === null;
557
+ const signals = await buildConversationRetrievalSignals(deps, vault, content, scopes, nowMs, safeForSecondaryModel);
558
+ return retrieveMemoryContext({
559
+ scopes,
560
+ queryText: content,
561
+ ...(budgetTokens !== undefined ? { budgetTokens } : {}),
562
+ ...(signals.semanticById !== undefined ? { semanticById: signals.semanticById } : {}),
563
+ ...(signals.strengthById.size > 0 ? { strengthById: signals.strengthById } : {}),
564
+ ...(signals.embeddingById.size > 0 ? { embeddingById: signals.embeddingById } : {}),
565
+ fusion: conversationFusionMode(deps),
566
+ nowMs,
567
+ }, vaultAsQueryPort(vault));
568
+ }
569
+ export async function buildMemoryResult(request, deps, context) {
570
+ const memory = request.memory;
571
+ if (memory === undefined) {
572
+ return emptyMemoryResult(false);
573
+ }
574
+ const vault = deps.memoryVault;
575
+ if (vault === undefined || !memory.enabled) {
576
+ return emptyMemoryResult(memory.enabled);
577
+ }
578
+ const scopes = conversationMemoryScopes(context);
579
+ const budgetTokens = memory.budgetTokens;
580
+ if (budgetTokens === 0) {
581
+ return emptyMemoryResult(true);
582
+ }
583
+ const nowMs = Date.now();
584
+ const retrieval = await retrieveChatMemory(deps, vault, scopes, request.content, budgetTokens, nowMs);
585
+ // Reinforcement reflex (#204): every recall is an access. Bumping the access counter for the
586
+ // included memories feeds the decay/reinforcement maintenance cycle so frequently-recalled
587
+ // memories strengthen over time. Guarded above (vault is defined here).
588
+ const includedIds = retrieval.contextBlock.memories.map((item) => item.memoryId);
589
+ if (includedIds.length > 0) {
590
+ vault.recordAccess(includedIds, Date.now());
591
+ }
592
+ // Autonomous maintenance (#204, O-V4): now that memory is actively in use, opportunistically run
593
+ // ONE bounded, rate-limited maintenance pass so the decay/forget curve advances without a
594
+ // free-running background loop.
595
+ maybeRunChatAutoMaintenance(deps, vault);
596
+ const result = toMemoryResult(retrieval);
597
+ recordConversationMemoryRetrieval(deps, context, result.context.memories);
598
+ return result;
599
+ }
600
+ function buildCaptureContext(input) {
601
+ return {
602
+ userId: input.userId,
603
+ nowMs: Date.now(),
604
+ newMemoryId: () => randomUUID(),
605
+ newProposalId: () => randomUUID(),
606
+ workspaceId: input.workspaceId,
607
+ projectId: input.projectId,
608
+ conversationId: input.conversationId,
609
+ };
610
+ }
611
+ async function captureActionFromOutcome(outcome, deps) {
612
+ switch (outcome.kind) {
613
+ case "candidate": {
614
+ if (deps.memoryVault === undefined)
615
+ return null;
616
+ if (!isPersistableMemoryCandidate(outcome)) {
617
+ return { kind: "rejected", reason: SENSITIVE_MEMORY_REJECTION_REASON };
618
+ }
619
+ const proposalId = outcome.proposal.proposalId;
620
+ const record = buildMemoryRecordFromProposal(proposalId, outcome);
621
+ if (record === null)
622
+ return null;
623
+ const inserted = deps.memoryVault.insertMemory(record);
624
+ // Best-effort embed-on-capture (#204): swallowed on failure / no model — never breaks capture.
625
+ await embedAndStoreMemory(deps, deps.memoryVault, inserted.id, inserted.body);
626
+ return {
627
+ kind: "candidate",
628
+ proposalId: String(inserted.id),
629
+ body: inserted.body,
630
+ scopeLabel: scopeLabel(inserted.scope),
631
+ requiresApproval: outcome.requiresApproval,
632
+ };
633
+ }
634
+ case "update":
635
+ return {
636
+ kind: "update",
637
+ memoryId: String(outcome.operation.memoryId),
638
+ bodyPatch: outcome.operation.bodyPatch,
639
+ };
640
+ case "forget":
641
+ return {
642
+ kind: "forget",
643
+ memoryId: String(outcome.operation.memoryId),
644
+ requiresConfirmation: outcome.requiresConfirmation,
645
+ };
646
+ case "rejected":
647
+ return { kind: "rejected", reason: outcome.reason };
648
+ case "supersession":
649
+ return null;
650
+ }
651
+ }
652
+ async function captureMemoryActions(request, deps, context) {
653
+ if (request.memory === undefined || !request.memory.enabled || deps.memoryVault === undefined) {
654
+ return [];
655
+ }
656
+ const outcomes = extractCandidatesFromUserText(request.content, buildCaptureContext(context), {
657
+ ...memoryCapturePolicyForDeps(deps, {
658
+ resolver: createMemoryTargetResolver(deps.memoryVault),
659
+ }),
660
+ });
661
+ const actions = [];
662
+ for (const outcome of outcomes) {
663
+ const action = await captureActionFromOutcome(outcome, deps);
664
+ if (action !== null)
665
+ actions.push(action);
666
+ }
667
+ return actions;
668
+ }
669
+ // Merges the regex intent capture (synchronous) with model-assisted salience capture (async).
670
+ // Regex runs FIRST so its inserts are part of the vault state the salience extractor reads for
671
+ // dedup. Salience reuses the same `memory.enabled` gate; when memory is off, both paths no-op.
672
+ export async function collectMemoryActions(deps, request, memoryContext, modelId, assistantText) {
673
+ if (memoryContext === undefined) {
674
+ return [];
675
+ }
676
+ const regexActions = await captureMemoryActions(request, deps, memoryContext);
677
+ const salientActions = await captureSalientFromTurn(deps, request, memoryContext, modelId, assistantText);
678
+ return [...regexActions, ...salientActions];
679
+ }
680
+ // On the first turn of a freshly-created chat (still bearing the default title), adopt the user's
681
+ // message prefix as the title; otherwise just pin the selected model.
682
+ export function buildChatPatch(chat, request, modelId) {
683
+ return chat.title === DEFAULT_CHAT_TITLE
684
+ ? { selectedModel: modelId, title: request.content.slice(0, 60) }
685
+ : { selectedModel: modelId };
686
+ }
687
+ // #152 — assembles the exact gateway prompt for the latest user turn (history + document context +
688
+ // memory text). Shared by the buffered (persistModelChatTurn) and streaming
689
+ // (handleSendDesktopChatStream) paths so both send a byte-identical prompt. `memoryText` is
690
+ // `memory.context.text`.
691
+ export function buildGatewayMessages(deps, request, memoryText) {
692
+ const history = conversationForGateway(deps.store.listMessages(request.chatId));
693
+ return applyDocumentContextToLatestUserTurn(history, request, memoryText);
694
+ }
695
+ async function persistModelChatTurn(deps, request, chat, modelId, memoryContext) {
696
+ const model = deps.modelPortFactory(modelId);
697
+ if (model === undefined) {
698
+ return { status: 400, body: errorBody("NO_MODEL", "No model provider is configured.") };
699
+ }
700
+ try {
701
+ const memory = memoryContext === undefined
702
+ ? emptyMemoryResult(false)
703
+ : await buildMemoryResult(request, deps, memoryContext);
704
+ const userMessage = createUserMessage(deps, request);
705
+ const messages = buildGatewayMessages(deps, request, memory.context.text);
706
+ const response = await model.call({
707
+ modelId,
708
+ messages,
709
+ stream: false,
710
+ }, new AbortController().signal);
711
+ // Issue #631 — redact the model's raw content before persisting and before returning it to
712
+ // the browser. A model that echoes a secret from its context (e.g. an apiKey injected via
713
+ // system prompt) would otherwise surface it un-redacted on the success path, mirroring the
714
+ // grounded-QA path (grounded-qa.ts line 549) which already applies deps.redactor here.
715
+ const redactedContent = deps.redactor(response.content);
716
+ const assistantMessage = createAssistantMessage(deps, request, redactedContent, modelId);
717
+ const memoryActions = await collectMemoryActions(deps, request, memoryContext, modelId, redactedContent);
718
+ const chatPatch = buildChatPatch(chat, request, modelId);
719
+ return {
720
+ status: 200,
721
+ body: {
722
+ chat: deps.store.updateChat(request.chatId, chatPatch),
723
+ messages: [userMessage, assistantMessage],
724
+ usage: response.usage,
725
+ memory: { ...memory, actions: memoryActions },
726
+ },
727
+ };
728
+ }
729
+ catch (error) {
730
+ return desktopChatErrorResult(error, deps);
731
+ }
732
+ }
733
+ export async function handleCreateDesktopChat(ctx, deps) {
734
+ const body = await readJsonObject(ctx.req);
735
+ if (isRouteResult(body))
736
+ return body;
737
+ const modelId = modelFromBody(body, deps);
738
+ if (isRouteResult(modelId))
739
+ return modelId;
740
+ try {
741
+ const projectPath = pickProjectPath(body, deps);
742
+ const project = ensureProject(deps, projectPath);
743
+ const title = typeof body.title === "string" && body.title.trim().length > 0
744
+ ? body.title.trim()
745
+ : DEFAULT_CHAT_TITLE;
746
+ const chat = deps.store.createChat(project.path, title, modelId);
747
+ return { status: 201, body: chatEnvelope(deps, project, chat) };
748
+ }
749
+ catch (error) {
750
+ if (error instanceof UiStoreError) {
751
+ // Issue #154 — redact at the boundary so user-controlled path fragments cannot
752
+ // echo configured gateway secrets back to the client.
753
+ return {
754
+ status: error.status,
755
+ body: errorBody(error.code, redactErrorMessage(error.message, deps)),
756
+ };
757
+ }
758
+ throw error;
759
+ }
760
+ }
761
+ // Issue #623 — validate the project path, returning a typed 400 RouteResult on failure instead of
762
+ // letting validateProjectPath throw into the generic 500 handler. Kept as a helper so the send
763
+ // handler stays within the complexity budget.
764
+ function normalizeDesktopProjectPath(projectPath, deps) {
765
+ try {
766
+ return validateProjectPath(projectPath, { mustExist: false });
767
+ }
768
+ catch (error) {
769
+ return desktopChatErrorResult(error, deps);
770
+ }
771
+ }
772
+ // Resolves the optional conversation memory context, surfacing a typed RouteResult on lookup
773
+ // failure. Extracted so handleSendDesktopChat stays within the complexity budget.
774
+ function resolveDesktopMemoryContext(deps, request, normalizedProjectPath) {
775
+ if (request.memory === undefined)
776
+ return undefined;
777
+ return resolveConversationMemoryContext(deps, normalizedProjectPath, request.chatId);
778
+ }
779
+ export async function prepareDesktopChatSend(ctx, deps) {
780
+ const body = await readJsonObject(ctx.req);
781
+ if (isRouteResult(body))
782
+ return body;
783
+ const request = sendRequestFromBody(body);
784
+ if (isRouteResult(request))
785
+ return request;
786
+ const normalizedProjectPath = normalizeDesktopProjectPath(request.projectPath, deps);
787
+ if (isRouteResult(normalizedProjectPath))
788
+ return normalizedProjectPath;
789
+ const chat = findChat(deps, normalizedProjectPath, request.chatId);
790
+ if (chat === undefined) {
791
+ return { status: 404, body: errorBody("NOT_FOUND", "Chat not found.") };
792
+ }
793
+ const modelId = request.modelId ?? chat.selectedModel;
794
+ const invalidModel = invalidChatModelResult(modelId, deps);
795
+ if (invalidModel !== undefined)
796
+ return invalidModel;
797
+ // Issue #149 — server-side modality guardrails. Run BEFORE any provider adapter call so a
798
+ // text-only model cannot receive image/document payloads, an embedding/OCR model cannot be
799
+ // used on the send path, and oversized aggregate context is rejected with a typed wire code.
800
+ // The validator returns static English messages (no value echo) — safe to render verbatim.
801
+ const validation = validateConversationPayload({
802
+ modelId,
803
+ modelCapabilities: modelCapabilityRegistry(deps),
804
+ attachments: request.attachments,
805
+ documentContext: request.documentContext,
806
+ });
807
+ if (!validation.ok) {
808
+ return { status: 400, body: errorBody(validation.code, validation.message) };
809
+ }
810
+ const memoryContext = resolveDesktopMemoryContext(deps, request, normalizedProjectPath);
811
+ if (isRouteResult(memoryContext))
812
+ return memoryContext;
813
+ return { request, chat, modelId, memoryContext };
814
+ }
815
+ export async function handleSendDesktopChat(ctx, deps) {
816
+ const prepared = await prepareDesktopChatSend(ctx, deps);
817
+ if (isRouteResult(prepared))
818
+ return prepared;
819
+ const { request, chat, modelId, memoryContext } = prepared;
820
+ return persistModelChatTurn(deps, request, chat, modelId, memoryContext);
821
+ }