@harperfast/harper 5.1.20 → 5.2.0-alpha.5

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 (395) hide show
  1. package/agent/operations.ts +13 -16
  2. package/components/Application.ts +188 -7
  3. package/components/ApplicationScope.ts +3 -0
  4. package/components/Scope.ts +12 -0
  5. package/components/anthropic/index.ts +9 -2
  6. package/components/bedrock/index.ts +25 -2
  7. package/components/componentLoader.ts +56 -7
  8. package/components/componentSecrets.ts +451 -0
  9. package/components/deploymentRecorder.ts +7 -1
  10. package/components/mcp/adapters/fastify.ts +3 -0
  11. package/components/mcp/adapters/harperHttp.ts +3 -0
  12. package/components/mcp/audit.ts +21 -6
  13. package/components/mcp/customResourceRegistry.ts +17 -38
  14. package/components/mcp/listChanged.ts +37 -20
  15. package/components/mcp/quota.ts +145 -0
  16. package/components/mcp/rateLimit.ts +152 -14
  17. package/components/mcp/resources.ts +2 -4
  18. package/components/mcp/toolRegistry.ts +73 -4
  19. package/components/mcp/tools/application.ts +77 -8
  20. package/components/mcp/tools/operations.ts +106 -36
  21. package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
  22. package/components/mcp/tools/schemas/operations.ts +5 -0
  23. package/components/mcp/transport.ts +55 -4
  24. package/components/ollama/index.ts +6 -2
  25. package/components/openai/index.ts +9 -2
  26. package/components/operations.js +204 -10
  27. package/components/operationsValidation.js +173 -0
  28. package/components/secretOperations.ts +498 -0
  29. package/config/configUtils.ts +25 -1
  30. package/config-app.schema.json +81 -0
  31. package/config-root.schema.json +8 -1
  32. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  33. package/dataLayer/readAuditLog.ts +14 -0
  34. package/dataLayer/schemaDescribe.ts +1 -0
  35. package/dist/agent/operations.d.ts +7 -4
  36. package/dist/agent/operations.js +13 -15
  37. package/dist/agent/operations.js.map +1 -1
  38. package/dist/components/Application.d.ts +25 -2
  39. package/dist/components/Application.js +166 -7
  40. package/dist/components/Application.js.map +1 -1
  41. package/dist/components/ApplicationScope.d.ts +2 -0
  42. package/dist/components/ApplicationScope.js +3 -0
  43. package/dist/components/ApplicationScope.js.map +1 -1
  44. package/dist/components/Scope.d.ts +8 -0
  45. package/dist/components/Scope.js +11 -0
  46. package/dist/components/Scope.js.map +1 -1
  47. package/dist/components/anthropic/index.d.ts +4 -1
  48. package/dist/components/anthropic/index.js +4 -2
  49. package/dist/components/anthropic/index.js.map +1 -1
  50. package/dist/components/bedrock/index.d.ts +4 -1
  51. package/dist/components/bedrock/index.js +22 -2
  52. package/dist/components/bedrock/index.js.map +1 -1
  53. package/dist/components/componentLoader.d.ts +2 -0
  54. package/dist/components/componentLoader.js +52 -10
  55. package/dist/components/componentLoader.js.map +1 -1
  56. package/dist/components/componentSecrets.d.ts +63 -0
  57. package/dist/components/componentSecrets.js +405 -0
  58. package/dist/components/componentSecrets.js.map +1 -0
  59. package/dist/components/deploymentRecorder.d.ts +3 -0
  60. package/dist/components/deploymentRecorder.js +4 -3
  61. package/dist/components/deploymentRecorder.js.map +1 -1
  62. package/dist/components/mcp/adapters/fastify.d.ts +2 -0
  63. package/dist/components/mcp/adapters/fastify.js +1 -0
  64. package/dist/components/mcp/adapters/fastify.js.map +1 -1
  65. package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
  66. package/dist/components/mcp/adapters/harperHttp.js +1 -0
  67. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  68. package/dist/components/mcp/audit.d.ts +2 -2
  69. package/dist/components/mcp/audit.js +20 -5
  70. package/dist/components/mcp/audit.js.map +1 -1
  71. package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
  72. package/dist/components/mcp/customResourceRegistry.js +15 -30
  73. package/dist/components/mcp/customResourceRegistry.js.map +1 -1
  74. package/dist/components/mcp/listChanged.d.ts +7 -2
  75. package/dist/components/mcp/listChanged.js +28 -0
  76. package/dist/components/mcp/listChanged.js.map +1 -1
  77. package/dist/components/mcp/quota.d.ts +31 -0
  78. package/dist/components/mcp/quota.js +155 -0
  79. package/dist/components/mcp/quota.js.map +1 -0
  80. package/dist/components/mcp/rateLimit.d.ts +19 -3
  81. package/dist/components/mcp/rateLimit.js +129 -13
  82. package/dist/components/mcp/rateLimit.js.map +1 -1
  83. package/dist/components/mcp/resources.js.map +1 -1
  84. package/dist/components/mcp/toolRegistry.d.ts +39 -1
  85. package/dist/components/mcp/toolRegistry.js +60 -5
  86. package/dist/components/mcp/toolRegistry.js.map +1 -1
  87. package/dist/components/mcp/tools/application.d.ts +8 -1
  88. package/dist/components/mcp/tools/application.js +65 -5
  89. package/dist/components/mcp/tools/application.js.map +1 -1
  90. package/dist/components/mcp/tools/operations.d.ts +15 -3
  91. package/dist/components/mcp/tools/operations.js +99 -38
  92. package/dist/components/mcp/tools/operations.js.map +1 -1
  93. package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
  94. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  95. package/dist/components/mcp/tools/schemas/operations.js +4 -0
  96. package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
  97. package/dist/components/mcp/transport.d.ts +6 -0
  98. package/dist/components/mcp/transport.js +47 -3
  99. package/dist/components/mcp/transport.js.map +1 -1
  100. package/dist/components/ollama/index.d.ts +4 -1
  101. package/dist/components/ollama/index.js +4 -2
  102. package/dist/components/ollama/index.js.map +1 -1
  103. package/dist/components/openai/index.d.ts +4 -1
  104. package/dist/components/openai/index.js +4 -2
  105. package/dist/components/openai/index.js.map +1 -1
  106. package/dist/components/operations.d.ts +39 -0
  107. package/dist/components/operations.js +194 -2
  108. package/dist/components/operations.js.map +1 -1
  109. package/dist/components/operationsValidation.d.ts +38 -0
  110. package/dist/components/operationsValidation.js +155 -0
  111. package/dist/components/operationsValidation.js.map +1 -1
  112. package/dist/components/secretOperations.d.ts +113 -0
  113. package/dist/components/secretOperations.js +475 -0
  114. package/dist/components/secretOperations.js.map +1 -0
  115. package/dist/config/configUtils.d.ts +4 -1
  116. package/dist/config/configUtils.js +18 -1
  117. package/dist/config/configUtils.js.map +1 -1
  118. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  119. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  120. package/dist/dataLayer/readAuditLog.js +11 -0
  121. package/dist/dataLayer/readAuditLog.js.map +1 -1
  122. package/dist/dataLayer/schemaDescribe.js +2 -0
  123. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  124. package/dist/globals.d.ts +1 -1
  125. package/dist/globals.js +1 -0
  126. package/dist/globals.js.map +1 -1
  127. package/dist/index.d.ts +3 -0
  128. package/dist/index.js +1 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/json/systemSchema.json +38 -0
  131. package/dist/resources/DatabaseTransaction.d.ts +31 -3
  132. package/dist/resources/DatabaseTransaction.js +190 -29
  133. package/dist/resources/DatabaseTransaction.js.map +1 -1
  134. package/dist/resources/LMDBTransaction.d.ts +1 -1
  135. package/dist/resources/LMDBTransaction.js +43 -5
  136. package/dist/resources/LMDBTransaction.js.map +1 -1
  137. package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
  138. package/dist/resources/PrimaryRocksDatabase.js +194 -0
  139. package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
  140. package/dist/resources/RecordEncoder.d.ts +1 -0
  141. package/dist/resources/RecordEncoder.js +81 -75
  142. package/dist/resources/RecordEncoder.js.map +1 -1
  143. package/dist/resources/RequestTarget.d.ts +19 -0
  144. package/dist/resources/RequestTarget.js.map +1 -1
  145. package/dist/resources/Resource.d.ts +0 -1
  146. package/dist/resources/Resource.js +103 -16
  147. package/dist/resources/Resource.js.map +1 -1
  148. package/dist/resources/ResourceInterface.d.ts +6 -2
  149. package/dist/resources/ResourceInterface.js.map +1 -1
  150. package/dist/resources/Resources.js.map +1 -1
  151. package/dist/resources/Table.d.ts +6 -4
  152. package/dist/resources/Table.js +247 -24
  153. package/dist/resources/Table.js.map +1 -1
  154. package/dist/resources/analytics/read.js +25 -28
  155. package/dist/resources/analytics/read.js.map +1 -1
  156. package/dist/resources/blob.js +165 -22
  157. package/dist/resources/blob.js.map +1 -1
  158. package/dist/resources/crdt.d.ts +2 -0
  159. package/dist/resources/crdt.js +45 -36
  160. package/dist/resources/crdt.js.map +1 -1
  161. package/dist/resources/databases.d.ts +1 -0
  162. package/dist/resources/databases.js +134 -7
  163. package/dist/resources/databases.js.map +1 -1
  164. package/dist/resources/graphql.js +8 -0
  165. package/dist/resources/graphql.js.map +1 -1
  166. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
  167. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
  168. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  169. package/dist/resources/jsResource.js +6 -0
  170. package/dist/resources/jsResource.js.map +1 -1
  171. package/dist/resources/loadEnv.js +30 -3
  172. package/dist/resources/loadEnv.js.map +1 -1
  173. package/dist/resources/login.js +2 -2
  174. package/dist/resources/login.js.map +1 -1
  175. package/dist/resources/models/Models.js +14 -3
  176. package/dist/resources/models/Models.js.map +1 -1
  177. package/dist/resources/models/embedHook.js +10 -2
  178. package/dist/resources/models/embedHook.js.map +1 -1
  179. package/dist/resources/models/openaiStream.d.ts +56 -0
  180. package/dist/resources/models/openaiStream.js +94 -0
  181. package/dist/resources/models/openaiStream.js.map +1 -0
  182. package/dist/resources/replayLogs.js +4 -3
  183. package/dist/resources/replayLogs.js.map +1 -1
  184. package/dist/resources/search.d.ts +1 -1
  185. package/dist/resources/search.js +104 -11
  186. package/dist/resources/search.js.map +1 -1
  187. package/dist/resources/secretDecryptor.d.ts +54 -0
  188. package/dist/resources/secretDecryptor.js +131 -0
  189. package/dist/resources/secretDecryptor.js.map +1 -0
  190. package/dist/resources/tracked.js +15 -5
  191. package/dist/resources/tracked.js.map +1 -1
  192. package/dist/security/auth.js +71 -8
  193. package/dist/security/auth.js.map +1 -1
  194. package/dist/security/jsLoader.js +11 -0
  195. package/dist/security/jsLoader.js.map +1 -1
  196. package/dist/security/tokenAuthentication.d.ts +9 -1
  197. package/dist/security/tokenAuthentication.js +31 -1
  198. package/dist/security/tokenAuthentication.js.map +1 -1
  199. package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
  200. package/dist/server/DurableSubscriptionsSession.js +8 -4
  201. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  202. package/dist/server/REST.js +28 -9
  203. package/dist/server/REST.js.map +1 -1
  204. package/dist/server/fastifyRoutes.js +11 -1
  205. package/dist/server/fastifyRoutes.js.map +1 -1
  206. package/dist/server/graphqlQuerying.js +4 -3
  207. package/dist/server/graphqlQuerying.js.map +1 -1
  208. package/dist/server/http.d.ts +40 -2
  209. package/dist/server/http.js +560 -77
  210. package/dist/server/http.js.map +1 -1
  211. package/dist/server/itc/serverHandlers.js +64 -0
  212. package/dist/server/itc/serverHandlers.js.map +1 -1
  213. package/dist/server/liveSubscriptionAuth.d.ts +12 -0
  214. package/dist/server/liveSubscriptionAuth.js +140 -0
  215. package/dist/server/liveSubscriptionAuth.js.map +1 -0
  216. package/dist/server/loadRootComponents.js +2 -1
  217. package/dist/server/loadRootComponents.js.map +1 -1
  218. package/dist/server/middlewareChain.d.ts +44 -1
  219. package/dist/server/middlewareChain.js +86 -10
  220. package/dist/server/middlewareChain.js.map +1 -1
  221. package/dist/server/mqtt.js +2 -1
  222. package/dist/server/mqtt.js.map +1 -1
  223. package/dist/server/operationsServer.js +1 -1
  224. package/dist/server/operationsServer.js.map +1 -1
  225. package/dist/server/serverHelpers/Headers.d.ts +9 -0
  226. package/dist/server/serverHelpers/Headers.js +22 -0
  227. package/dist/server/serverHelpers/Headers.js.map +1 -1
  228. package/dist/server/serverHelpers/JSONStream.js +3 -3
  229. package/dist/server/serverHelpers/JSONStream.js.map +1 -1
  230. package/dist/server/serverHelpers/Request.d.ts +52 -1
  231. package/dist/server/serverHelpers/Request.js +122 -1
  232. package/dist/server/serverHelpers/Request.js.map +1 -1
  233. package/dist/server/serverHelpers/contentTypes.js +3 -8
  234. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  235. package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
  236. package/dist/server/serverHelpers/progressEmitter.js +52 -4
  237. package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
  238. package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
  239. package/dist/server/serverHelpers/registeredOperations.js +286 -0
  240. package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
  241. package/dist/server/serverHelpers/serverHandlers.js +11 -5
  242. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  243. package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
  244. package/dist/server/serverHelpers/serverUtilities.js +67 -5
  245. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  246. package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
  247. package/dist/server/serverHelpers/uwsServer.js +433 -0
  248. package/dist/server/serverHelpers/uwsServer.js.map +1 -0
  249. package/dist/server/static.d.ts +0 -15
  250. package/dist/server/static.js +161 -2
  251. package/dist/server/static.js.map +1 -1
  252. package/dist/server/status/index.d.ts +4 -1
  253. package/dist/server/status/index.js +64 -4
  254. package/dist/server/status/index.js.map +1 -1
  255. package/dist/server/threads/manageThreads.d.ts +10 -0
  256. package/dist/server/threads/manageThreads.js +107 -4
  257. package/dist/server/threads/manageThreads.js.map +1 -1
  258. package/dist/server/threads/socketRouter.js +5 -1
  259. package/dist/server/threads/socketRouter.js.map +1 -1
  260. package/dist/server/threads/threadServer.js +92 -14
  261. package/dist/server/threads/threadServer.js.map +1 -1
  262. package/dist/upgrade/directives/5-2-0.d.ts +7 -0
  263. package/dist/upgrade/directives/5-2-0.js +108 -0
  264. package/dist/upgrade/directives/5-2-0.js.map +1 -0
  265. package/dist/upgrade/directives/directivesController.js +2 -1
  266. package/dist/upgrade/directives/directivesController.js.map +1 -1
  267. package/dist/utility/common_utils.js +5 -0
  268. package/dist/utility/common_utils.js.map +1 -1
  269. package/dist/utility/envFile.d.ts +41 -0
  270. package/dist/utility/envFile.js +221 -0
  271. package/dist/utility/envFile.js.map +1 -0
  272. package/dist/utility/globalSchema.d.ts +9 -0
  273. package/dist/utility/hdbTerms.d.ts +25 -0
  274. package/dist/utility/hdbTerms.js +31 -0
  275. package/dist/utility/hdbTerms.js.map +1 -1
  276. package/dist/utility/logging/harper_logger.d.ts +21 -0
  277. package/dist/utility/logging/harper_logger.js +160 -8
  278. package/dist/utility/logging/harper_logger.js.map +1 -1
  279. package/dist/utility/logging/logRotator.js +29 -17
  280. package/dist/utility/logging/logRotator.js.map +1 -1
  281. package/dist/utility/logging/readLog.d.ts +1 -1
  282. package/dist/utility/logging/readLog.js +305 -2
  283. package/dist/utility/logging/readLog.js.map +1 -1
  284. package/dist/utility/operationPermissions.d.ts +18 -0
  285. package/dist/utility/operationPermissions.js +35 -1
  286. package/dist/utility/operationPermissions.js.map +1 -1
  287. package/dist/utility/operation_authorization.d.ts +17 -0
  288. package/dist/utility/operation_authorization.js +38 -0
  289. package/dist/utility/operation_authorization.js.map +1 -1
  290. package/dist/utility/secretEnvelope.d.ts +30 -0
  291. package/dist/utility/secretEnvelope.js +94 -0
  292. package/dist/utility/secretEnvelope.js.map +1 -0
  293. package/dist/utility/signalling.d.ts +7 -0
  294. package/dist/utility/signalling.js +16 -0
  295. package/dist/utility/signalling.js.map +1 -1
  296. package/dist/validation/configValidator.js +70 -24
  297. package/dist/validation/configValidator.js.map +1 -1
  298. package/index.ts +4 -0
  299. package/json/systemSchema.json +38 -0
  300. package/npm-shrinkwrap.json +15066 -9347
  301. package/package.json +14 -9
  302. package/resources/DESIGN.md +15 -12
  303. package/resources/DatabaseTransaction.ts +206 -37
  304. package/resources/LMDBTransaction.ts +43 -9
  305. package/resources/PrimaryRocksDatabase.ts +201 -0
  306. package/resources/RecordEncoder.ts +81 -69
  307. package/resources/RequestTarget.ts +21 -0
  308. package/resources/Resource.ts +109 -25
  309. package/resources/ResourceInterface.ts +6 -2
  310. package/resources/Resources.ts +1 -3
  311. package/resources/Table.ts +238 -29
  312. package/resources/analytics/read.ts +25 -30
  313. package/resources/blob.ts +159 -23
  314. package/resources/crdt.ts +37 -31
  315. package/resources/databases.ts +143 -6
  316. package/resources/graphql.ts +8 -0
  317. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
  318. package/resources/jsResource.ts +6 -0
  319. package/resources/loadEnv.ts +34 -3
  320. package/resources/login.ts +2 -2
  321. package/resources/models/Models.ts +19 -3
  322. package/resources/models/embedHook.ts +13 -2
  323. package/resources/models/openaiStream.ts +133 -0
  324. package/resources/replayLogs.ts +4 -3
  325. package/resources/search.ts +113 -11
  326. package/resources/secretDecryptor.ts +159 -0
  327. package/resources/tracked.ts +24 -17
  328. package/security/auth.ts +70 -10
  329. package/security/jsLoader.ts +12 -0
  330. package/security/tokenAuthentication.ts +42 -1
  331. package/server/DESIGN.md +10 -0
  332. package/server/DurableSubscriptionsSession.ts +8 -4
  333. package/server/REST.ts +26 -9
  334. package/server/fastifyRoutes.ts +10 -1
  335. package/server/graphqlQuerying.ts +4 -3
  336. package/server/http.ts +563 -77
  337. package/server/itc/serverHandlers.js +70 -0
  338. package/server/liveSubscriptionAuth.ts +152 -0
  339. package/server/loadRootComponents.js +2 -1
  340. package/server/middlewareChain.ts +107 -17
  341. package/server/mqtt.ts +2 -1
  342. package/server/operationsServer.ts +2 -2
  343. package/server/serverHelpers/Headers.ts +24 -0
  344. package/server/serverHelpers/JSONStream.ts +3 -3
  345. package/server/serverHelpers/Request.ts +127 -0
  346. package/server/serverHelpers/contentTypes.ts +3 -8
  347. package/server/serverHelpers/progressEmitter.ts +55 -5
  348. package/server/serverHelpers/registeredOperations.ts +259 -0
  349. package/server/serverHelpers/serverHandlers.js +11 -5
  350. package/server/serverHelpers/serverUtilities.ts +88 -5
  351. package/server/serverHelpers/uwsServer.ts +488 -0
  352. package/server/static.ts +180 -2
  353. package/server/status/index.ts +72 -4
  354. package/server/threads/manageThreads.js +102 -4
  355. package/server/threads/socketRouter.ts +5 -1
  356. package/server/threads/threadServer.js +93 -10
  357. package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
  358. package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
  359. package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
  360. package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
  361. package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
  362. package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
  363. package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
  364. package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
  365. package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
  366. package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
  367. package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
  368. package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
  369. package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
  370. package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
  371. package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
  372. package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
  373. package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
  374. package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
  375. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
  376. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
  377. package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
  378. package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
  379. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
  380. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
  381. package/studio/web/index.html +1 -1
  382. package/upgrade/directives/5-2-0.ts +82 -0
  383. package/upgrade/directives/directivesController.ts +2 -1
  384. package/utility/common_utils.ts +5 -0
  385. package/utility/envFile.ts +218 -0
  386. package/utility/hdbTerms.ts +31 -0
  387. package/utility/logging/harper_logger.ts +156 -8
  388. package/utility/logging/logRotator.ts +24 -14
  389. package/utility/logging/readLog.ts +323 -2
  390. package/utility/operationPermissions.ts +35 -1
  391. package/utility/operation_authorization.ts +74 -1
  392. package/utility/secretEnvelope.ts +113 -0
  393. package/utility/signalling.ts +15 -0
  394. package/validation/configValidator.ts +78 -25
  395. package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
@@ -115,6 +115,10 @@ function bisectInsert(arr: Candidate[], distance: number): number {
115
115
  const ENTRY_POINT = Symbol.for('entryPoint');
116
116
  const KEY_PREFIX = Symbol.for('key');
117
117
  const MAX_LEVEL = 10; // should give good high-level skip list performance up to trillions of nodes
118
+ // Visit budget for the post-delete connectivity probe (repairSeveredNeighbors, #1712). Severed
119
+ // islands are small (bounded by the deleted node's neighborhood), so a probe that visits this many
120
+ // nodes without reaching the entry point is treated as connected-but-far and repaired conservatively.
121
+ const PROBE_VISIT_LIMIT = 256;
118
122
  type Connection = {
119
123
  id: number;
120
124
  distance: number;
@@ -142,7 +146,12 @@ export class HierarchicalNavigableSmallWorld {
142
146
  // Index options that only affect search, not the stored graph — changing them must not trigger a
143
147
  // reindex (databases.ts persists the new value but skips rebuilding). efConstructionSearch is the
144
148
  // search-time candidate-list size; the build uses efConstruction/M/distance, which are structural.
145
- static searchOnlyOptions = ['efConstructionSearch'];
149
+ // filterExpansion is the visit-budget multiplier for predicate-aware (filtered) traversal.
150
+ static searchOnlyOptions = ['efConstructionSearch', 'filterExpansion'];
151
+ // Signals to search.ts that this index accepts a per-record predicate in search() and applies it
152
+ // during traversal (predicate-aware / ACORN-style filtering), so companion conditions and RBAC can
153
+ // be pushed down instead of post-filtering an under-filled candidate set (#1241).
154
+ filteredSearch = true;
146
155
  indexStore: any;
147
156
  M: number = 16; // max number of connections per layer
148
157
  efConstruction: number = 100; // size of dynamic candidate list
@@ -152,6 +161,17 @@ export class HierarchicalNavigableSmallWorld {
152
161
  // a value of 1 is extremely aggressive.
153
162
  optimizeRouting = 0.5;
154
163
  nodesVisitedCount = 0;
164
+ // Visit-budget multiplier for predicate-aware traversal (#1241). When a filter is selective enough
165
+ // that layer-0 results never fill `ef`, the "closest candidate worse than worst result" stop rule
166
+ // never triggers, so maxVisits = ef * filterExpansion caps how many nodes an under-filled filtered
167
+ // search visits before returning what it has (approximate, like all ANN). It does NOT bound a filter
168
+ // that fills `ef` — that terminates naturally. Default 24 (not the 8 the design sketch assumed):
169
+ // this HNSW visits a large fraction of the graph per query, so filling `ef` at selectivity `s` needs
170
+ // ~ef/s visits; a multiplier of 24 fills down to ~4% selectivity before the budget bites, keeping
171
+ // recall at or above post-filtering across the range the query planner routes to traversal. Raising
172
+ // it is nearly free for condition-derived filters (they fill and self-terminate); it mainly trades
173
+ // latency for recall on selective *function* predicates. Per-query override via the search options.
174
+ filterExpansion = 24;
155
175
 
156
176
  idIncrementer: BigInt64Array | undefined;
157
177
  distance: (a: number[], b: number[]) => number;
@@ -188,6 +208,7 @@ export class HierarchicalNavigableSmallWorld {
188
208
  if (options.efConstructionSearch !== undefined) this.efConstructionSearch = options.efConstructionSearch;
189
209
  if (options.mL !== undefined) this.mL = options.mL;
190
210
  if (options.optimizeRouting !== undefined) this.optimizeRouting = options.optimizeRouting;
211
+ if (options.filterExpansion !== undefined) this.filterExpansion = options.filterExpansion;
191
212
  }
192
213
  }
193
214
  index(primaryKey: Id, vector: number[], existingVector?: number[], options: any = {}) {
@@ -497,6 +518,10 @@ export class HierarchicalNavigableSmallWorld {
497
518
  this.indexStore.remove(safeKey, options);
498
519
  }
499
520
  const needsReindexing = new Map();
521
+ // On DELETE, track every neighbor that loses an edge: the empty-list check below only
522
+ // catches fully-orphaned nodes, but a cluster can stay internally connected while losing
523
+ // its only bridges to the entry point — see repairSeveredNeighbors (#1712).
524
+ const edgeLosingNeighborIds = vector ? undefined : new Set<number>();
500
525
  // remove connections to this node that are no longer valid
501
526
  if (oldNode.level !== undefined) {
502
527
  for (let l = 0; l <= oldNode.level; l++) {
@@ -505,6 +530,7 @@ export class HierarchicalNavigableSmallWorld {
505
530
  // get and copy the neighbor node so we can modify it
506
531
  const neighborNode = updateNode(neighborId, this.safeGetSync(neighborId, options));
507
532
  if (!neighborNode) continue;
533
+ edgeLosingNeighborIds?.add(neighborId);
508
534
  // On an UPDATE (vector != null), only remove the reverse edge at
509
535
  // the exact level l where the old connection existed. Sweeping 0..l would destroy reverse
510
536
  // edges at lower levels that were just re-added by addConnection or preserved by the
@@ -580,9 +606,92 @@ export class HierarchicalNavigableSmallWorld {
580
606
  }
581
607
  this.index(key, orphanVector, orphanVector, options);
582
608
  }
609
+ if (edgeLosingNeighborIds?.size) this.repairSeveredNeighbors(edgeLosingNeighborIds, options);
583
610
  this.checkSymmetry(nodeId, this.safeGetSync(nodeId, options), options);
584
611
  }
585
612
 
613
+ /**
614
+ * After a delete, a neighbor that lost an edge can be severed from the entry point even with
615
+ * non-empty connection lists: a locally-connected cluster whose only bridges ran through the
616
+ * deleted node becomes a disconnected island, unreachable by search at any ef (#1712). The
617
+ * empty-list orphan check in index() can't see this, so probe each edge-losing neighbor's
618
+ * connectivity and reindex the ones that are cut off.
619
+ *
620
+ * The probe is a level-prioritized best-first traversal (hubs first, since the entry point is
621
+ * a high-level node), treating edges as undirected — the same assumption validateConnectivity
622
+ * makes. Note the direction difference: the probe walks source→entry-point over stored
623
+ * (outgoing) edges while search walks entry-point→outward, so an asymmetric edge could make
624
+ * the probe claim connectivity that search doesn't have. Edges are bidirectional by
625
+ * construction (checkSymmetry logs violations), and reverse-reachability from the entry point
626
+ * would be O(N) per delete, so this is a deliberate residual. In a connected component the
627
+ * probe reaches the known-connected set within a few hops; in an island it exhausts the
628
+ * island quickly. If it exceeds PROBE_VISIT_LIMIT without an answer (pathologically distant
629
+ * entry point — or an island larger than the budget), reinsert just the probe source: a
630
+ * wasted reinsert in the connected case, and in the oversized-island case a partial repair
631
+ * (the source relinks to the live graph, the rest of the island stays severed — reindexing
632
+ * replaces the source's edges rather than bridging through them, and reindexing all 256+
633
+ * probed nodes on what is usually just a distant entry point would be far too expensive).
634
+ */
635
+ private repairSeveredNeighbors(neighborIds: Set<number>, options: any) {
636
+ const entryPointId = this.indexStore.getSync(ENTRY_POINT, options);
637
+ if (entryPointId === undefined) return;
638
+ const knownConnected = new Set<number>([entryPointId]);
639
+ for (const sourceId of neighborIds) {
640
+ if (knownConnected.has(sourceId)) continue;
641
+ const sourceNode = this.safeGetSync(sourceId, options);
642
+ if (!sourceNode || sourceNode.level === undefined) continue; // already removed or stale reference
643
+ const visited = new Map<number, Node>([[sourceId, sourceNode]]);
644
+ const frontier = new MinHeap();
645
+ frontier.push({ id: sourceId, distance: -sourceNode.level, node: sourceNode });
646
+ let verdict: 'connected' | 'island' | 'inconclusive' = 'island';
647
+ probe: for (;;) {
648
+ const current = frontier.pop();
649
+ if (!current) break; // exhausted the reachable set without touching the connected component
650
+ for (let l = 0; l <= current.node.level; l++) {
651
+ for (const { id: neighborId } of current.node[l] || []) {
652
+ if (neighborId === undefined) continue;
653
+ if (knownConnected.has(neighborId)) {
654
+ verdict = 'connected';
655
+ break probe;
656
+ }
657
+ if (visited.has(neighborId)) continue;
658
+ const node = this.safeGetSync(neighborId, options);
659
+ if (!node || node.level === undefined) continue; // stale reference
660
+ visited.set(neighborId, node);
661
+ frontier.push({ id: neighborId, distance: -node.level, node });
662
+ }
663
+ }
664
+ if (visited.size > PROBE_VISIT_LIMIT) {
665
+ verdict = 'inconclusive';
666
+ break;
667
+ }
668
+ }
669
+ if (verdict === 'connected') {
670
+ // every visited node connects to the source, which reaches the connected component
671
+ for (const id of visited.keys()) knownConnected.add(id);
672
+ } else {
673
+ // island: reinsert every member so the whole cluster relinks to the live graph;
674
+ // inconclusive: reinsert only the source (partial repair — see docstring)
675
+ if (verdict === 'inconclusive') {
676
+ logger.warn?.('connectivity probe exceeded visit budget after a delete, reinserting only', sourceId);
677
+ }
678
+ const toReindex = verdict === 'island' ? visited.values() : [sourceNode];
679
+ logger.debug?.('reindexing nodes severed from the entry point by a delete', sourceId);
680
+ for (const node of toReindex) {
681
+ const nodeVector =
682
+ node.scale !== undefined ? dequantizeInt8(node.vector as Int8Array, node.scale) : (node.vector as number[]);
683
+ this.index(node.primaryKey, nodeVector, nodeVector, options);
684
+ }
685
+ if (verdict === 'island') {
686
+ // the whole reindexed island is now relinked; skip re-probing its members
687
+ for (const id of visited.keys()) knownConnected.add(id);
688
+ } else {
689
+ knownConnected.add(sourceId);
690
+ }
691
+ }
692
+ }
693
+ }
694
+
586
695
  private safeGetSync(key: any, options?: any): any {
587
696
  try {
588
697
  let node = this.indexStore.getSync(key, options);
@@ -643,7 +752,13 @@ export class HierarchicalNavigableSmallWorld {
643
752
  ef: number,
644
753
  level: number,
645
754
  options: { transaction?: any } = {},
646
- distanceFunction = this.distance
755
+ distanceFunction = this.distance,
756
+ // Predicate-aware traversal (#1241, layer 0 only). `filter` decides result admission by primary
757
+ // key; non-matching nodes still route (they stay in the candidate/visited sets) so the graph
758
+ // remains navigable under selective filters (ACORN). `filterState` carries the visit budget and
759
+ // per-query counters. Both are undefined for routing layers and for unfiltered searches.
760
+ filter?: (primaryKey: Id) => boolean,
761
+ filterState?: FilterState
647
762
  ): SearchResults {
648
763
  // Pre-compute query magnitude for cosine; use cached invMag on stored nodes to skip sqrt per neighbor.
649
764
  // Asymmetric distance: the query stays full-precision float; a stored neighbor may be int8
@@ -697,13 +812,62 @@ export class HierarchicalNavigableSmallWorld {
697
812
 
698
813
  const candidates = new MinHeap();
699
814
  candidates.push(initialCandidate);
700
- const results = [initialCandidate] as SearchResults;
701
815
 
816
+ if (!filter) {
817
+ // Unfiltered path (unchanged): results are seeded with the entry point and every visited
818
+ // node is admitted; the stop rule is "closest remaining candidate worse than worst result".
819
+ const results = [initialCandidate] as SearchResults;
820
+ while (candidates.size > 0) {
821
+ const current = candidates.pop()!;
822
+ const furthestDistance = results[results.length - 1].distance;
823
+
824
+ if (current.distance > furthestDistance) break;
825
+
826
+ for (const { id: neighborId } of current.node[level] || []) {
827
+ if (visited.has(neighborId) || neighborId === undefined) continue;
828
+ visited.add(neighborId);
829
+
830
+ const neighbor = this.safeGetSync(neighborId, options);
831
+ if (!neighbor) continue;
832
+ this.nodesVisitedCount++;
833
+ const distance = computeDistance(neighbor.vector, neighbor.invMag, neighbor.scale);
834
+
835
+ if (distance < furthestDistance || results.length < ef) {
836
+ const candidate: Candidate = { id: neighborId, distance, node: neighbor };
837
+ candidates.push(candidate);
838
+ results.splice(bisectInsert(results, distance), 0, candidate);
839
+ if (results.length > ef) results.pop();
840
+ }
841
+ }
842
+ }
843
+ results.visited = visited.size;
844
+ return results;
845
+ }
846
+
847
+ // Predicate-aware path (#1241). `results` holds only nodes the filter admits, but every visited
848
+ // node still routes: it enters the candidate heap and can be expanded, preserving connectivity
849
+ // through non-matching regions. Because matches accrue slower than visits, the distance stop rule
850
+ // only applies once `ef` matches are in hand; until then we keep expanding.
851
+ //
852
+ // maxVisits = ef * filterExpansion is a hard ceiling on layer-0 node visits. It has to be
853
+ // generous enough NOT to truncate a non-selective filter before it fills `ef` (filling at
854
+ // selectivity `s` costs ~ef/s visits) — that is why the default filterExpansion is larger than a
855
+ // standard-HNSW rule of thumb, since this index visits a large fraction of the graph per query.
856
+ // It matters as a genuine bound in two cases: a selective filter that can never fill `ef` (where
857
+ // the alternative is crawling the whole graph — the same regime the planner diverts to the exact
858
+ // brute-force path for condition filters, leaving function predicates the real beneficiary), and
859
+ // a filter that fills `ef` with distant matches (a loose worst-match bound would otherwise let
860
+ // the distance rule explore almost everything).
861
+ // search() always supplies filterState alongside filter; default one for any direct caller that doesn't.
862
+ if (!filterState) filterState = { maxVisits: Infinity, nodesVisited: 0, filterEvaluations: 0 };
863
+ const results = [] as unknown as SearchResults;
864
+ if (this.admit(filter, filterState, entryPoint.primaryKey)) results.push(initialCandidate);
865
+ let budgetExhausted = false;
702
866
  while (candidates.size > 0) {
703
867
  const current = candidates.pop()!;
704
- const furthestDistance = results[results.length - 1].distance;
705
-
706
- if (current.distance > furthestDistance) break;
868
+ // Once we have ef matches, the worst of them bounds useful exploration; before that, keep going.
869
+ const furthestDistance = results.length >= ef ? results[results.length - 1].distance : Infinity;
870
+ if (results.length >= ef && current.distance > furthestDistance) break;
707
871
 
708
872
  for (const { id: neighborId } of current.node[level] || []) {
709
873
  if (visited.has(neighborId) || neighborId === undefined) continue;
@@ -712,20 +876,41 @@ export class HierarchicalNavigableSmallWorld {
712
876
  const neighbor = this.safeGetSync(neighborId, options);
713
877
  if (!neighbor) continue;
714
878
  this.nodesVisitedCount++;
879
+ filterState.nodesVisited++;
715
880
  const distance = computeDistance(neighbor.vector, neighbor.invMag, neighbor.scale);
716
881
 
882
+ // Route through any node that could still improve the result set (under-filled or nearer
883
+ // than the current worst match) — filtering does not prune the graph, only admission.
717
884
  if (distance < furthestDistance || results.length < ef) {
718
885
  const candidate: Candidate = { id: neighborId, distance, node: neighbor };
719
886
  candidates.push(candidate);
720
- results.splice(bisectInsert(results, distance), 0, candidate);
721
- if (results.length > ef) results.pop();
887
+ if (this.admit(filter, filterState, neighbor.primaryKey)) {
888
+ results.splice(bisectInsert(results, distance), 0, candidate);
889
+ if (results.length > ef) results.pop();
890
+ }
891
+ }
892
+ if (filterState.nodesVisited >= filterState.maxVisits) {
893
+ budgetExhausted = true;
894
+ break;
722
895
  }
723
896
  }
897
+ if (budgetExhausted) break;
724
898
  }
725
899
  results.visited = visited.size;
726
900
  return results;
727
901
  }
728
902
 
903
+ /**
904
+ * Evaluate the traversal predicate for one node, counting the evaluation for `explain`. Kept as a
905
+ * tiny helper so both the entry-point seed and the neighbor loop share the counting path. The filter
906
+ * itself memoizes verdicts per query (a node can be reached from multiple neighbors), so this stays
907
+ * cheap even when the predicate loads a record.
908
+ */
909
+ private admit(filter: (primaryKey: Id) => boolean, filterState: FilterState | undefined, primaryKey: Id): boolean {
910
+ if (filterState) filterState.filterEvaluations++;
911
+ return filter(primaryKey);
912
+ }
913
+
729
914
  /**
730
915
  * This the main entry from Harper's query functionality, where we actually search for an ordered list of nearest
731
916
  * neighbors, using the provided sort/order definition object and performing the multi-layer skip-list search.
@@ -745,6 +930,7 @@ export class HierarchicalNavigableSmallWorld {
745
930
  distance,
746
931
  comparator,
747
932
  ef,
933
+ filterExpansion,
748
934
  }: {
749
935
  target: number[];
750
936
  value: number;
@@ -752,8 +938,14 @@ export class HierarchicalNavigableSmallWorld {
752
938
  distance: string;
753
939
  comparator: string;
754
940
  ef?: number;
941
+ filterExpansion?: number;
755
942
  },
756
- context: any
943
+ context: any,
944
+ // Predicate-aware traversal (#1241). When provided, only nodes for which `filter(primaryKey)`
945
+ // returns true are admitted to the result list at layer 0; routing is unaffected. Composed by
946
+ // search.ts from companion AND conditions, a caller-supplied `vectorFilter`, and record-level
947
+ // RBAC. Must be synchronous and side-effect free. JS-API only (never from a REST query string).
948
+ filter?: (primaryKey: Id) => boolean
757
949
  ) {
758
950
  let limit: number | undefined; // only set for threshold comparators; 0 is a valid threshold (e.g. dotProduct)
759
951
  let limitInclusive = false; // true for `le`, false for `lt`
@@ -793,14 +985,34 @@ export class HierarchicalNavigableSmallWorld {
793
985
  : (this.indexStore.getStats?.()?.entryCount ?? 0);
794
986
  effectiveEf = autoScaleEf(nodeCount);
795
987
  }
988
+ // Predicate-aware traversal budget (#1241): matches accrue slower than visits under a selective
989
+ // filter, so bound layer-0 work at ef * filterExpansion nodes. Only built when a filter is active.
990
+ const filterState: FilterState | undefined = filter
991
+ ? {
992
+ maxVisits: effectiveEf * (filterExpansion && filterExpansion > 0 ? filterExpansion : this.filterExpansion),
993
+ nodesVisited: 0,
994
+ filterEvaluations: 0,
995
+ }
996
+ : undefined;
796
997
  let entryPoint = this.getEntryPoint(options);
797
- if (!entryPoint) return [];
998
+ if (!entryPoint) return withStats([], filterState);
798
999
  let entryPointId = entryPoint.id;
799
1000
  let results: Candidate[] = [];
800
- // For each level from top to bottom
1001
+ // For each level from top to bottom. The filter applies only at layer 0 (result admission);
1002
+ // upper layers route unfiltered so non-matching hubs still guide the descent.
801
1003
  for (let l = entryPoint.level; l >= 0; l--) {
802
1004
  // Search for closest neighbors at current level
803
- results = this.searchLayer(target, entryPointId, entryPoint, effectiveEf, l, options, distanceFunction);
1005
+ results = this.searchLayer(
1006
+ target,
1007
+ entryPointId,
1008
+ entryPoint,
1009
+ effectiveEf,
1010
+ l,
1011
+ options,
1012
+ distanceFunction,
1013
+ l === 0 ? filter : undefined,
1014
+ l === 0 ? filterState : undefined
1015
+ );
804
1016
 
805
1017
  if (results.length > 0) {
806
1018
  const neighbor = results[0]; // closest neighbor becomes new entry point
@@ -812,11 +1024,14 @@ export class HierarchicalNavigableSmallWorld {
812
1024
  results = results.filter((candidate) =>
813
1025
  limitInclusive ? candidate.distance <= limit : candidate.distance < limit
814
1026
  );
815
- return results.map((candidate) => ({
816
- // we return the result as an entry so we can provide distance as metadata
817
- key: candidate.node.primaryKey, // return value
818
- distance: candidate.distance,
819
- }));
1027
+ return withStats(
1028
+ results.map((candidate) => ({
1029
+ // we return the result as an entry so we can provide distance as metadata
1030
+ key: candidate.node.primaryKey, // return value
1031
+ distance: candidate.distance,
1032
+ })),
1033
+ filterState
1034
+ );
820
1035
  }
821
1036
  /**
822
1037
  * Exact distance between a query and a record's FULL-precision vector, mirroring search()'s metric
@@ -1052,3 +1267,22 @@ type Candidate = {
1052
1267
  node: Node;
1053
1268
  };
1054
1269
  type SearchResults = Candidate[] & { visited: number };
1270
+ // Per-query state for predicate-aware traversal (#1241): the visit budget plus counters surfaced for
1271
+ // tuning (nodesVisited / filterEvaluations). One instance per search(), threaded to the layer-0 searchLayer.
1272
+ type FilterState = {
1273
+ maxVisits: number;
1274
+ nodesVisited: number;
1275
+ filterEvaluations: number;
1276
+ };
1277
+ /**
1278
+ * Attach filtered-traversal counters to the returned entries array so callers (search.ts / explain) can
1279
+ * report how much of the graph a filtered query touched. No-op for unfiltered searches. Non-enumerable so
1280
+ * the stats don't leak into iteration/serialization of the result entries.
1281
+ */
1282
+ function withStats<T extends any[]>(entries: T, filterState: FilterState | undefined): T {
1283
+ if (filterState) {
1284
+ Object.defineProperty(entries, 'nodesVisited', { value: filterState.nodesVisited, enumerable: false });
1285
+ Object.defineProperty(entries, 'filterEvaluations', { value: filterState.filterEvaluations, enumerable: false });
1286
+ }
1287
+ return entries;
1288
+ }
@@ -1,5 +1,6 @@
1
1
  import { Scope } from '../components/Scope.ts';
2
2
  import { dirname } from 'path';
3
+ import { signalResourcesRegistered } from '../utility/signalling.ts';
3
4
 
4
5
  function isResource(value: any) {
5
6
  return (
@@ -96,6 +97,11 @@ export async function handleApplication(scope: Scope) {
96
97
  scope.logger.debug?.(`Registered root resource: ${path}`);
97
98
  }
98
99
  recurseForResources(scope, resourceModule, root);
100
+ // A JS resource that extends an exported @table is the one carrying author opt-ins
101
+ // (`static mcpTools`/`mcpPrompts`), and it registers here — after the schema-derived
102
+ // table class and after the MCP component's boot scan. Signal so listing surfaces
103
+ // (MCP application tools) rebuild against the now-settled registry (#1448).
104
+ signalResourcesRegistered();
99
105
  } catch (error) {
100
106
  // Rethrow with more context
101
107
  throw new ResourceLoadError(entryEvent.absolutePath, error);
@@ -1,6 +1,8 @@
1
1
  import { parse } from 'dotenv';
2
2
  import logger from '../utility/logging/harper_logger.ts';
3
3
  import { Scope } from '../components/Scope.ts';
4
+ import { isEncryptedEnvValue } from '../utility/envFile.ts';
5
+ import { deferEncryptedEnvValue, getSecretDecryptor } from './secretDecryptor.ts';
4
6
  import { CONFIG_SHAPING_ENV_VARS } from '../config/componentEnvPrepass.ts';
5
7
 
6
8
  export function handleApplication(scope: Scope) {
@@ -11,7 +13,36 @@ export function handleApplication(scope: Scope) {
11
13
  return;
12
14
  }
13
15
  logger.debug(`Loading env file: ${entry.absolutePath}`);
14
- for (const [key, value] of Object.entries(parse(entry.contents))) {
16
+ for (const [key, rawValue] of Object.entries(parse(entry.contents))) {
17
+ let value = rawValue;
18
+ // Encrypted (`enc:v1:`) values are decrypted via the Pro env-secrets component if it has
19
+ // registered a decryptor; otherwise the secret stays opaque and is skipped (so the app
20
+ // fails loudly on a missing var rather than receiving ciphertext as the value).
21
+ if (isEncryptedEnvValue(rawValue)) {
22
+ const decryptor = getSecretDecryptor();
23
+ // Logged at error level (not warn) so an undecryptable secret is never silent — but
24
+ // skipped rather than fatal, so the node still boots and the bad value can be fixed via
25
+ // set_env_value, and a non-Pro node isn't crashed by a replicated encrypted value. The
26
+ // entry is also queued so a decryptor that registers later (custody can come up after
27
+ // component `.env` loading) replays it into process.env; until then the app sees a
28
+ // missing var (and should fail on it) rather than receiving ciphertext.
29
+ if (!decryptor) {
30
+ logger.error(
31
+ `Environment variable ${key} from ${entry.absolutePath} is encrypted but no env-secret decryptor is registered yet (the Harper Pro env-secrets component is required); deferring`
32
+ );
33
+ deferEncryptedEnvValue({ key, rawValue, sourcePath: entry.absolutePath, override });
34
+ continue;
35
+ }
36
+ try {
37
+ value = decryptor(rawValue);
38
+ } catch (error) {
39
+ logger.error(
40
+ `Failed to decrypt environment variable ${key} from ${entry.absolutePath}: ${(error as Error).message}; skipping`
41
+ );
42
+ continue;
43
+ }
44
+ }
45
+
15
46
  if (CONFIG_SHAPING_ENV_VARS.includes(key)) {
16
47
  // covers components deployed after boot too — the boot-time pre-pass only sees
17
48
  // components present when the config is composed (#1513)
@@ -20,8 +51,8 @@ export function handleApplication(scope: Scope) {
20
51
  );
21
52
  // Enforce at the injection point: the trio must never reach process.env
22
53
  // from a component .env — anything downstream that (re)composes config
23
- // from process.env would otherwise silently honor it, re-inverting the
24
- // top-down config relationship.
54
+ // from process.env (e.g. #1618/#1726's OptionsWatcher) would otherwise
55
+ // silently honor it, re-inverting the top-down config relationship.
25
56
  continue;
26
57
  }
27
58
  if (process.env[key] !== undefined) {
@@ -12,9 +12,9 @@ class Login extends Resource {
12
12
  // TODO: Return a login page
13
13
  }
14
14
  static async post(_id, body, request) {
15
- const { username, password } = body;
15
+ const { username, password, token } = body;
16
16
  return {
17
- data: await request.login(username, password),
17
+ data: await request.login(username, password, token),
18
18
  };
19
19
  }
20
20
  }
@@ -112,7 +112,7 @@ export class Models implements ModelsContract {
112
112
  signal?.throwIfAborted();
113
113
  const attemptStart = performance.now();
114
114
  try {
115
- const backendOpts: BackendOpts<EmbedOpts> = { ...opts, signal, accounting };
115
+ const backendOpts = toBackendOpts(opts, signal, accounting);
116
116
  const result = await backend.embed!(input, backendOpts);
117
117
  // Throw on `pending` BEFORE recording success — otherwise we'd write a
118
118
  // success row followed by a failure row from the catch (duplicate).
@@ -166,7 +166,7 @@ export class Models implements ModelsContract {
166
166
  signal?.throwIfAborted();
167
167
  const attemptStart = performance.now();
168
168
  try {
169
- const backendOpts: BackendOpts<GenerateOpts> = { ...opts, signal, accounting };
169
+ const backendOpts = toBackendOpts(opts, signal, accounting);
170
170
  const result = await backend.generate!(input, backendOpts);
171
171
  if (result.status !== 'completed') throw new ModelPendingNotSupportedError(backend.name);
172
172
  this.#record(backend, 'generate', opts.model, accounting, opts, result, attemptStart);
@@ -210,7 +210,7 @@ export class Models implements ModelsContract {
210
210
  }
211
211
  // First candidate only — mid-stream fallback would mean replaying already-yielded chunks.
212
212
  const backend = resolved.candidates[0];
213
- const backendOpts: BackendOpts<GenerateOpts> = { ...opts, signal, accounting };
213
+ const backendOpts = toBackendOpts(opts, signal, accounting);
214
214
  return this.#wrapStream(backend, input, backendOpts, opts, accounting, startedAt);
215
215
  }
216
216
 
@@ -352,6 +352,22 @@ type Resolution = { candidates: ModelBackend[] } | { error: Error; backend: Mode
352
352
  * against a backend that actually supports the call (the default router never returns
353
353
  * empty for a satisfying primary; it only drops on name or capability miss).
354
354
  */
355
+ /**
356
+ * Builds the options passed to a backend call. `opts.model` is the LOGICAL name — it selects
357
+ * the configured entry via resolveCandidates and must not reach the backend, where it would
358
+ * override the entry's configured wire model id (#1593: `@embed(model: "default")` sent the
359
+ * literal string "default" to the provider). Backends always use their own configured model.
360
+ */
361
+ function toBackendOpts<TOpts extends { model?: string; signal?: AbortSignal }>(
362
+ opts: TOpts,
363
+ signal: AbortSignal | undefined,
364
+ accounting: AccountingContext
365
+ ): BackendOpts<TOpts> {
366
+ const backendOpts = { ...opts, signal, accounting };
367
+ delete backendOpts.model;
368
+ return backendOpts;
369
+ }
370
+
355
371
  function resolveCandidates(kind: ResolveKind, model: string | undefined, requires: Capability[]): Resolution {
356
372
  const logicalName = model ?? 'default';
357
373
  const candidates = getRouter().route({ kind, logicalName, requires });
@@ -118,9 +118,20 @@ export function buildEmbedBefore(
118
118
  try {
119
119
  vector = await embedder(record);
120
120
  } catch (err) {
121
- // Backend errors can carry URLs / key tails; log raw, rethrow sanitized.
121
+ // Backend errors can carry URLs / key tails; log raw, rethrow sanitized. Include
122
+ // only safe identifiers (error class name, upstream HTTP status) so the failure is
123
+ // diagnosable without hunting through server logs (#1593). upstreamStatus is the
124
+ // provider's status — NOT ServerError's statusCode, which is Harper's own response
125
+ // status and would misleadingly read 500 here.
122
126
  getLogger().error?.(`Embedder for attribute "${attr.name}" failed:`, err);
123
- throw new Error(`Failed to compute embedding for attribute "${attr.name}"`);
127
+ const status = (err as any)?.upstreamStatus;
128
+ const errName = (err as any)?.name;
129
+ const detail =
130
+ (errName && errName !== 'Error' ? ` [${errName}]` : '') +
131
+ (typeof status === 'number' ? ` (backend HTTP ${status})` : '');
132
+ throw new Error(
133
+ `Failed to compute embedding for attribute "${attr.name}"${detail} — see server log for details`
134
+ );
124
135
  }
125
136
  record[attr.name] = normalizeVector(vector);
126
137
  })