akanjs 3.1.0-rc.2 → 3.1.0-rc.21

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 (392) hide show
  1. package/base/baseEnv.ts +5 -2
  2. package/client/device.ts +1 -1
  3. package/client/frameConfig.ts +2 -2
  4. package/client/href.ts +35 -0
  5. package/client/index.ts +1 -0
  6. package/client/makePageProto.tsx +3 -2
  7. package/client/router.ts +6 -4
  8. package/client/rscNavigation.ts +1 -1
  9. package/client/transitionTypes.ts +0 -4
  10. package/client/translator.ts +16 -0
  11. package/client/types.ts +3 -2
  12. package/client/viewportMeta.ts +23 -8
  13. package/client/webManifest.ts +5 -3
  14. package/common/TrustedProxy.ts +14 -0
  15. package/common/codeAgentWire.ts +8 -1
  16. package/common/index.ts +1 -0
  17. package/common/interpolateTranslation.ts +17 -3
  18. package/common/isEmail.ts +1 -1
  19. package/common/localeConfig.ts +10 -4
  20. package/common/markdownSpans.ts +1 -1
  21. package/common/restClient.ts +3 -1
  22. package/common/routeConvention.ts +4 -4
  23. package/common/trimSlashes.ts +11 -0
  24. package/common/types.ts +1 -1
  25. package/constant/fieldInfo.ts +2 -2
  26. package/constant/fieldPlan.ts +80 -0
  27. package/constant/index.ts +3 -0
  28. package/constant/inputParser.ts +148 -0
  29. package/constant/invalidInputError.ts +13 -0
  30. package/constant/serialize.ts +36 -20
  31. package/constant/types.ts +1 -1
  32. package/constant/via.ts +2 -0
  33. package/dictionary/base.dictionary.ts +4 -0
  34. package/dictionary/trans.ts +8 -0
  35. package/document/database.ts +3 -6
  36. package/document/documentSchema.ts +1 -1
  37. package/document/noDocumentError.ts +6 -1
  38. package/fetch/client/fetchClient.ts +2 -1
  39. package/fetch/client/sliceInitHandle.ts +2 -3
  40. package/fetch/fetchType/appliedReturn.type.ts +3 -1
  41. package/fetch/types.ts +4 -2
  42. package/index.ts +2 -0
  43. package/package.json +31 -21
  44. package/server/akanApp.ts +15 -3
  45. package/server/akanServer.ts +34 -14
  46. package/server/artifact/routeClientCache.ts +4 -5
  47. package/server/artifact/routeSeedIndexStore.ts +3 -20
  48. package/server/artifact/routeSeedMatcher.ts +70 -0
  49. package/server/console.ts +1 -1
  50. package/server/di/diLifecycle.ts +16 -7
  51. package/server/imageOptimizer.ts +1 -1
  52. package/server/index.ts +1 -0
  53. package/server/machine/machineAccount.ts +28 -3
  54. package/server/machine/machineToken.ts +27 -3
  55. package/server/mcp/McpDispatcher.ts +2 -3
  56. package/server/mcp/McpExecutionContext.ts +9 -3
  57. package/server/migration/docPath.ts +121 -0
  58. package/server/migration/index.ts +15 -0
  59. package/server/migration/migrationChangeLog.ts +86 -0
  60. package/server/migration/migrationChanges.ts +70 -0
  61. package/server/migration/migrationCommand.ts +179 -0
  62. package/server/migration/migrationContext.ts +53 -0
  63. package/server/migration/migrationDefinition.ts +87 -0
  64. package/server/migration/migrationError.ts +45 -0
  65. package/server/migration/migrationLedger.ts +172 -0
  66. package/server/migration/migrationLock.ts +103 -0
  67. package/server/migration/migrationParams.ts +122 -0
  68. package/server/migration/migrationPulse.ts +39 -0
  69. package/server/migration/migrationRunner.ts +525 -0
  70. package/server/migration/migrationScope.ts +93 -0
  71. package/server/migration/migrationTable.ts +258 -0
  72. package/server/migration/migrations.ts +57 -0
  73. package/server/ops/opsCommand.ts +30 -1
  74. package/server/resolver/CascadeRunner.ts +15 -2
  75. package/server/resolver/signal.resolver.ts +35 -55
  76. package/server/routeState.ts +0 -6
  77. package/server/rscClient.tsx +22 -37
  78. package/server/rscNavigationState.ts +3 -5
  79. package/server/rscScrollRestoration.ts +116 -0
  80. package/server/rscWorker.tsx +12 -19
  81. package/server/sitemap.ts +4 -4
  82. package/server/tunnel/TunnelAgent.ts +9 -1
  83. package/server/webRouter.ts +17 -2
  84. package/service/agent.service.ts +21 -1
  85. package/service/endpointCacheKeys.ts +35 -0
  86. package/service/index.ts +1 -0
  87. package/service/injectInfo.ts +2 -2
  88. package/service/predefinedAdaptor/anthropicLlm.ts +37 -17
  89. package/service/predefinedAdaptor/database.adaptor.ts +15 -0
  90. package/service/predefinedAdaptor/documentTransfer.ts +50 -0
  91. package/service/predefinedAdaptor/endpointCacheControl.adaptor.ts +19 -0
  92. package/service/predefinedAdaptor/index.ts +1 -0
  93. package/service/predefinedAdaptor/llm.adaptor.ts +7 -0
  94. package/service/predefinedAdaptor/openaiDialect.ts +53 -17
  95. package/service/predefinedAdaptor/openaiLlm.ts +1 -1
  96. package/service/predefinedAdaptor/sql/MigrationTables.ts +90 -0
  97. package/service/predefinedAdaptor/sql/RawDocumentTable.ts +200 -0
  98. package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +6 -2
  99. package/service/predefinedAdaptor/sql/SqliteInsightSession.ts +4 -1
  100. package/service/predefinedAdaptor/sql/dialect/postgres.ts +0 -1
  101. package/service/predefinedAdaptor/sql/dialect/sqlite.ts +0 -1
  102. package/service/predefinedAdaptor/sql/types.ts +3 -1
  103. package/service/predefinedAdaptor/storage.adaptor.ts +19 -0
  104. package/signal/endpointCache.ts +23 -7
  105. package/signal/endpointErrorContract.ts +37 -0
  106. package/signal/endpointIdempotency.ts +5 -3
  107. package/signal/endpointInfo.ts +4 -1
  108. package/signal/endpointRateLimit.ts +6 -2
  109. package/signal/exception.ts +4 -1
  110. package/signal/index.ts +1 -0
  111. package/signal/mcp/McpDocument.ts +16 -1
  112. package/signal/mcp/McpUriTemplate.ts +1 -1
  113. package/signal/middlewareChain.ts +105 -0
  114. package/signal/openapi/openapi.ts +46 -2
  115. package/signal/serializer/fetch.serializer.ts +1 -0
  116. package/signal/signalContext.ts +110 -82
  117. package/signal/slice.ts +2 -2
  118. package/signal/trace.ts +8 -2
  119. package/signal/types.ts +13 -0
  120. package/store/action.ts +102 -42
  121. package/store/agent/ScreenImage.ts +166 -0
  122. package/store/agent/ScreenReader.ts +2 -1
  123. package/store/agent/StoreSurfaceSource.ts +41 -2
  124. package/store/agent/index.ts +2 -1
  125. package/store/databaseStateNames.ts +1 -0
  126. package/store/formUploadOf.ts +41 -0
  127. package/store/index.ts +1 -0
  128. package/store/liveInsert.ts +2 -2
  129. package/store/state.ts +9 -1
  130. package/store/stateBuilder.ts +8 -2
  131. package/store/storeInstance.ts +12 -3
  132. package/store/withSelector.ts +2 -2
  133. package/types/client/href.d.ts +11 -0
  134. package/types/client/index.d.ts +1 -0
  135. package/types/client/rscNavigation.d.ts +1 -1
  136. package/types/client/transitionTypes.d.ts +0 -2
  137. package/types/client/translator.d.ts +2 -0
  138. package/types/common/codeAgentWire.d.ts +7 -0
  139. package/types/common/index.d.ts +1 -0
  140. package/types/common/trimSlashes.d.ts +2 -0
  141. package/types/common/types.d.ts +1 -1
  142. package/types/constant/fieldInfo.d.ts +2 -2
  143. package/types/constant/fieldPlan.d.ts +33 -0
  144. package/types/constant/index.d.ts +3 -0
  145. package/types/constant/inputParser.d.ts +11 -0
  146. package/types/constant/invalidInputError.d.ts +8 -0
  147. package/types/constant/types.d.ts +1 -1
  148. package/types/dictionary/base.dictionary.d.ts +1 -1
  149. package/types/dictionary/dictionary.d.ts +3 -3
  150. package/types/dictionary/trans.d.ts +3 -0
  151. package/types/document/database.d.ts +2 -3
  152. package/types/document/documentSchema.d.ts +1 -1
  153. package/types/document/noDocumentError.d.ts +6 -1
  154. package/types/fetch/fetchType/appliedReturn.type.d.ts +3 -1
  155. package/types/fetch/types.d.ts +2 -2
  156. package/types/index.d.ts +5 -0
  157. package/types/server/akanServer.d.ts +1 -1
  158. package/types/server/artifact/routeSeedMatcher.d.ts +8 -0
  159. package/types/server/di/diLifecycle.d.ts +1 -0
  160. package/types/server/index.d.ts +1 -0
  161. package/types/server/machine/machineAccount.d.ts +1 -0
  162. package/types/server/machine/machineToken.d.ts +9 -0
  163. package/types/server/migration/docPath.d.ts +20 -0
  164. package/types/server/migration/index.d.ts +15 -0
  165. package/types/server/migration/migrationChangeLog.d.ts +27 -0
  166. package/types/server/migration/migrationChanges.d.ts +27 -0
  167. package/types/server/migration/migrationCommand.d.ts +33 -0
  168. package/types/server/migration/migrationContext.d.ts +28 -0
  169. package/types/server/migration/migrationDefinition.d.ts +42 -0
  170. package/types/server/migration/migrationError.d.ts +15 -0
  171. package/types/server/migration/migrationLedger.d.ts +51 -0
  172. package/types/server/migration/migrationLock.d.ts +34 -0
  173. package/types/server/migration/migrationParams.d.ts +41 -0
  174. package/types/server/migration/migrationPulse.d.ts +15 -0
  175. package/types/server/migration/migrationRunner.d.ts +93 -0
  176. package/types/server/migration/migrationScope.d.ts +42 -0
  177. package/types/server/migration/migrationTable.d.ts +52 -0
  178. package/types/server/migration/migrations.d.ts +12 -0
  179. package/types/server/migration.d.ts +1 -0
  180. package/types/server/ops/opsCommand.d.ts +10 -1
  181. package/types/server/routeState.d.ts +1 -4
  182. package/types/server/rscNavigationState.d.ts +2 -3
  183. package/types/server/rscScrollRestoration.d.ts +14 -0
  184. package/types/server/sitemap.d.ts +1 -1
  185. package/types/service/agent.service.d.ts +2 -0
  186. package/types/service/endpointCacheKeys.d.ts +8 -0
  187. package/types/service/index.d.ts +1 -0
  188. package/types/service/injectInfo.d.ts +2 -2
  189. package/types/service/predefinedAdaptor/anthropicLlm.d.ts +9 -4
  190. package/types/service/predefinedAdaptor/database.adaptor.d.ts +8 -1
  191. package/types/service/predefinedAdaptor/documentTransfer.d.ts +1 -0
  192. package/types/service/predefinedAdaptor/endpointCacheControl.adaptor.d.ts +9 -0
  193. package/types/service/predefinedAdaptor/index.d.ts +1 -0
  194. package/types/service/predefinedAdaptor/llm.adaptor.d.ts +7 -0
  195. package/types/service/predefinedAdaptor/openaiDialect.d.ts +20 -5
  196. package/types/service/predefinedAdaptor/sql/MigrationTables.d.ts +17 -0
  197. package/types/service/predefinedAdaptor/sql/RawDocumentTable.d.ts +38 -0
  198. package/types/service/predefinedAdaptor/sql/types.d.ts +1 -0
  199. package/types/service/predefinedAdaptor/storage.adaptor.d.ts +8 -0
  200. package/types/signal/endpointCache.d.ts +2 -0
  201. package/types/signal/endpointErrorContract.d.ts +10 -0
  202. package/types/signal/endpointIdempotency.d.ts +1 -0
  203. package/types/signal/endpointRateLimit.d.ts +1 -1
  204. package/types/signal/exception.d.ts +4 -1
  205. package/types/signal/index.d.ts +1 -0
  206. package/types/signal/middlewareChain.d.ts +11 -0
  207. package/types/signal/signalContext.d.ts +2 -6
  208. package/types/signal/slice.d.ts +1 -1
  209. package/types/signal/trace.d.ts +1 -1
  210. package/types/signal/types.d.ts +12 -0
  211. package/types/store/action.d.ts +2 -0
  212. package/types/store/agent/ScreenImage.d.ts +27 -0
  213. package/types/store/agent/StoreSurfaceSource.d.ts +1 -1
  214. package/types/store/agent/index.d.ts +2 -1
  215. package/types/store/databaseStateNames.d.ts +1 -0
  216. package/types/store/formUploadOf.d.ts +9 -0
  217. package/types/store/index.d.ts +1 -0
  218. package/types/store/state.d.ts +7 -1
  219. package/types/store/stateBuilder.d.ts +2 -1
  220. package/types/store/storeInstance.d.ts +2 -2
  221. package/types/store/withSelector.d.ts +1 -1
  222. package/types/ui/Agent/Composer.d.ts +2 -0
  223. package/types/ui/Agent/Menu.d.ts +2 -0
  224. package/types/ui/CsrImage.d.ts +2 -0
  225. package/types/ui/Data/CardList.d.ts +1 -1
  226. package/types/ui/Data/Dashboard.d.ts +1 -1
  227. package/types/ui/Data/Insight.d.ts +1 -1
  228. package/types/ui/Data/Item.d.ts +2 -2
  229. package/types/ui/Data/ListContainer.d.ts +1 -1
  230. package/types/ui/Data/Pagination.d.ts +1 -1
  231. package/types/ui/Data/TableList.d.ts +1 -1
  232. package/types/ui/Field/Relation.d.ts +13 -10
  233. package/types/ui/Field/index.d.ts +4 -4
  234. package/types/ui/Field/useRelationOptions.d.ts +3 -1
  235. package/types/ui/Field/useRelationWindow.d.ts +14 -0
  236. package/types/ui/Image.d.ts +3 -1
  237. package/types/ui/Load/View.d.ts +1 -1
  238. package/types/ui/Pagination.d.ts +1 -1
  239. package/types/ui/Signal/Arg.d.ts +1 -1
  240. package/types/ui/System/CSR.d.ts +1 -1
  241. package/types/ui/System/Client.d.ts +1 -1
  242. package/types/ui/System/SSR.d.ts +1 -1
  243. package/types/vendor/@akanjs/native/packages/core/src/plugin.d.ts +3 -3
  244. package/types/vendor/@akanjs/native/packages/desktop/src/page-veto.d.ts +1 -1
  245. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +1 -1
  246. package/types/vendor/@akanjs/native/plugins/auth-session/src/desktop.d.ts +2 -2
  247. package/types/vendor/@akanjs/native/plugins/auth-session/src/index.d.ts +1 -1
  248. package/types/vendor/@akanjs/native/plugins/autostart/src/desktop.d.ts +2 -2
  249. package/types/vendor/@akanjs/native/plugins/autostart/src/index.d.ts +1 -1
  250. package/types/vendor/@akanjs/native/plugins/badge/src/desktop.d.ts +1 -1
  251. package/types/vendor/@akanjs/native/plugins/badge/src/index.d.ts +1 -1
  252. package/types/vendor/@akanjs/native/plugins/badge/src/web.d.ts +1 -1
  253. package/types/vendor/@akanjs/native/plugins/biometric/src/index.d.ts +1 -1
  254. package/types/vendor/@akanjs/native/plugins/camera/src/desktop.d.ts +1 -1
  255. package/types/vendor/@akanjs/native/plugins/camera/src/index.d.ts +1 -1
  256. package/types/vendor/@akanjs/native/plugins/camera/src/web.d.ts +1 -1
  257. package/types/vendor/@akanjs/native/plugins/clipboard/src/desktop.d.ts +2 -2
  258. package/types/vendor/@akanjs/native/plugins/clipboard/src/index.d.ts +1 -1
  259. package/types/vendor/@akanjs/native/plugins/clipboard/src/web.d.ts +1 -1
  260. package/types/vendor/@akanjs/native/plugins/contacts/src/index.d.ts +1 -1
  261. package/types/vendor/@akanjs/native/plugins/device/src/desktop.d.ts +2 -2
  262. package/types/vendor/@akanjs/native/plugins/device/src/index.d.ts +1 -1
  263. package/types/vendor/@akanjs/native/plugins/device/src/web.d.ts +1 -1
  264. package/types/vendor/@akanjs/native/plugins/dialog/src/desktop.d.ts +1 -1
  265. package/types/vendor/@akanjs/native/plugins/dialog/src/index.d.ts +1 -1
  266. package/types/vendor/@akanjs/native/plugins/dialog/src/web.d.ts +1 -1
  267. package/types/vendor/@akanjs/native/plugins/dock/src/desktop.d.ts +1 -1
  268. package/types/vendor/@akanjs/native/plugins/dock/src/index.d.ts +1 -1
  269. package/types/vendor/@akanjs/native/plugins/file-picker/src/desktop.d.ts +2 -2
  270. package/types/vendor/@akanjs/native/plugins/file-picker/src/web.d.ts +1 -1
  271. package/types/vendor/@akanjs/native/plugins/filesystem/src/desktop.d.ts +2 -2
  272. package/types/vendor/@akanjs/native/plugins/filesystem/src/web.d.ts +1 -1
  273. package/types/vendor/@akanjs/native/plugins/haptics/src/index.d.ts +1 -1
  274. package/types/vendor/@akanjs/native/plugins/haptics/src/web.d.ts +1 -1
  275. package/types/vendor/@akanjs/native/plugins/http/src/desktop.d.ts +2 -2
  276. package/types/vendor/@akanjs/native/plugins/http/src/web.d.ts +1 -1
  277. package/types/vendor/@akanjs/native/plugins/keep-awake/src/desktop.d.ts +2 -2
  278. package/types/vendor/@akanjs/native/plugins/keep-awake/src/index.d.ts +1 -1
  279. package/types/vendor/@akanjs/native/plugins/keep-awake/src/web.d.ts +1 -1
  280. package/types/vendor/@akanjs/native/plugins/opener/src/desktop.d.ts +2 -2
  281. package/types/vendor/@akanjs/native/plugins/opener/src/index.d.ts +1 -1
  282. package/types/vendor/@akanjs/native/plugins/opener/src/web.d.ts +1 -1
  283. package/types/vendor/@akanjs/native/plugins/preferences/src/desktop.d.ts +1 -1
  284. package/types/vendor/@akanjs/native/plugins/preferences/src/index.d.ts +1 -1
  285. package/types/vendor/@akanjs/native/plugins/preferences/src/web.d.ts +1 -1
  286. package/types/vendor/@akanjs/native/plugins/secure-storage/src/desktop.d.ts +2 -2
  287. package/types/vendor/@akanjs/native/plugins/secure-storage/src/index.d.ts +1 -1
  288. package/types/vendor/@akanjs/native/plugins/share/src/index.d.ts +1 -1
  289. package/types/vendor/@akanjs/native/plugins/share/src/web.d.ts +1 -1
  290. package/types/vendor/@akanjs/native/plugins/single-instance/src/desktop.d.ts +2 -2
  291. package/types/vendor/@akanjs/native/plugins/splash-screen/src/index.d.ts +1 -1
  292. package/types/vendor/@akanjs/native/plugins/splash-screen/src/web.d.ts +1 -1
  293. package/types/vendor/@akanjs/native/plugins/sqlite/src/desktop.d.ts +2 -2
  294. package/types/vendor/@akanjs/native/plugins/toast/src/index.d.ts +1 -1
  295. package/types/vendor/@akanjs/native/plugins/toast/src/web.d.ts +1 -1
  296. package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +2 -2
  297. package/types/vendor/@akanjs/native/plugins/window-state/src/desktop.d.ts +2 -2
  298. package/types/vendor/@akanjs/native/plugins/window-state/src/index.d.ts +1 -1
  299. package/types/vendor/use-agentic/Compaction.d.ts +3 -1
  300. package/types/vendor/use-agentic/Reference.d.ts +9 -1
  301. package/types/vendor/use-agentic/ToolOutput.d.ts +6 -1
  302. package/types/vendor/use-agentic/ToolReturn.d.ts +18 -0
  303. package/types/vendor/use-agentic/Transcript.d.ts +7 -0
  304. package/types/vendor/use-agentic/index.d.ts +1 -0
  305. package/types/vendor/use-agentic/types.d.ts +6 -0
  306. package/types/webkit/useDebounce.d.ts +1 -1
  307. package/ui/Agent/Chat.tsx +1 -0
  308. package/ui/Agent/Composer.tsx +2 -0
  309. package/ui/Agent/Menu.tsx +5 -1
  310. package/ui/Agent/agentSessionOf.ts +15 -8
  311. package/ui/Agent/mentionDraft.ts +2 -3
  312. package/ui/Agent/useReferenceMenu.ts +1 -0
  313. package/ui/Agent/useSlashMenu.ts +1 -0
  314. package/ui/Clipboard.tsx +1 -0
  315. package/ui/CsrImage.tsx +3 -1
  316. package/ui/Data/CardList.tsx +1 -1
  317. package/ui/Data/Dashboard.tsx +1 -1
  318. package/ui/Data/Insight.tsx +1 -1
  319. package/ui/Data/Item.tsx +5 -2
  320. package/ui/Data/ListContainer.tsx +2 -2
  321. package/ui/Data/Pagination.tsx +1 -1
  322. package/ui/Data/TableList.tsx +1 -1
  323. package/ui/DraggableList.tsx +1 -0
  324. package/ui/Field/Collection.tsx +4 -0
  325. package/ui/Field/Relation.tsx +33 -14
  326. package/ui/Field/useRelationOptions.ts +33 -5
  327. package/ui/Field/useRelationWindow.ts +50 -0
  328. package/ui/Image.tsx +4 -4
  329. package/ui/Input.tsx +1 -0
  330. package/ui/Layout/Sider.tsx +1 -1
  331. package/ui/Link/Anchor.tsx +7 -5
  332. package/ui/Load/Units.tsx +17 -7
  333. package/ui/Load/View.tsx +2 -2
  334. package/ui/Model/Edit.tsx +1 -1
  335. package/ui/Model/EditModal.tsx +11 -5
  336. package/ui/Model/New.tsx +1 -1
  337. package/ui/Model/SureToRemove.tsx +1 -0
  338. package/ui/Model/ViewEditModal.tsx +17 -5
  339. package/ui/Model/ViewModal.tsx +2 -2
  340. package/ui/ObjectId.tsx +5 -1
  341. package/ui/Pagination.tsx +1 -1
  342. package/ui/Signal/Arg.tsx +1 -3
  343. package/ui/Signal/Doc.tsx +5 -1
  344. package/ui/System/CSR.tsx +1 -1
  345. package/ui/System/Client.tsx +1 -1
  346. package/ui/System/Reconnect.tsx +1 -1
  347. package/ui/System/SSR.tsx +3 -36
  348. package/ui/ToggleSelect.tsx +2 -0
  349. package/ui/styles.css +10 -103
  350. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeBridge.swift +3 -2
  351. package/vendor/@akanjs/native/packages/cli/src/lib/addons.ts +4 -1
  352. package/vendor/@akanjs/native/packages/cli/src/lib/apk.ts +2 -2
  353. package/vendor/@akanjs/native/packages/cli/src/lib/codegen.ts +1 -1
  354. package/vendor/@akanjs/native/packages/cli/src/lib/csp.ts +4 -3
  355. package/vendor/@akanjs/native/packages/cli/src/lib/googleservices.ts +7 -1
  356. package/vendor/@akanjs/native/packages/cli/src/lib/hmr.ts +8 -5
  357. package/vendor/@akanjs/native/packages/cli/src/lib/html.ts +18 -3
  358. package/vendor/@akanjs/native/packages/cli/src/lib/manifestmerge.ts +1 -1
  359. package/vendor/@akanjs/native/packages/cli/src/lib/plist.ts +2 -4
  360. package/vendor/@akanjs/native/packages/cli/src/lib/toolchains.ts +1 -1
  361. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +10 -4
  362. package/vendor/@akanjs/native/packages/cli/src/lib/xml.ts +4 -3
  363. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +3 -3
  364. package/vendor/@akanjs/native/packages/core/src/kernel.ts +3 -3
  365. package/vendor/@akanjs/native/packages/core/src/live.ts +1 -0
  366. package/vendor/@akanjs/native/packages/core/src/plugin.ts +5 -3
  367. package/vendor/@akanjs/native/packages/desktop/src/host.ts +13 -5
  368. package/vendor/@akanjs/native/packages/desktop/src/lifecycle.ts +1 -0
  369. package/vendor/@akanjs/native/packages/desktop/src/page-veto.ts +1 -1
  370. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +4 -1
  371. package/vendor/@akanjs/native/plugins/camera/src/web.ts +3 -3
  372. package/vendor/@akanjs/native/plugins/file-picker/src/index.ts +4 -1
  373. package/vendor/@akanjs/native/plugins/local-notifications/src/web.ts +1 -1
  374. package/vendor/@akanjs/native/plugins/secure-storage/src/desktop.ts +1 -1
  375. package/vendor/@akanjs/native/plugins/share/src/options.ts +1 -1
  376. package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +3 -2
  377. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +4 -1
  378. package/vendor/@akanjs/native/plugins/window/src/index.ts +2 -2
  379. package/vendor/use-agentic/Compaction.ts +38 -7
  380. package/vendor/use-agentic/Reference.ts +16 -9
  381. package/vendor/use-agentic/ToolOutput.ts +25 -3
  382. package/vendor/use-agentic/ToolReturn.ts +32 -0
  383. package/vendor/use-agentic/ToolRunner.ts +15 -13
  384. package/vendor/use-agentic/Transcript.ts +38 -2
  385. package/vendor/use-agentic/WIRE.md +20 -0
  386. package/vendor/use-agentic/index.ts +1 -0
  387. package/vendor/use-agentic/types.ts +6 -0
  388. package/webkit/McpAppAgent.ts +12 -2
  389. package/webkit/RenderLayer.tsx +23 -4
  390. package/webkit/useDebounce.ts +23 -14
  391. package/client/viewTransition.ts +0 -10
  392. package/types/client/viewTransition.d.ts +0 -3
@@ -0,0 +1,200 @@
1
+ import {
2
+ type AkanSqlClient,
3
+ type QueryLeafOps,
4
+ REF_NAME_RE,
5
+ RESERVED_RE,
6
+ type SqlDialect,
7
+ type SqlFrag,
8
+ type SqliteDocumentRow,
9
+ type TransferRow,
10
+ } from "./types";
11
+ import { assertStorableJson, BASE_COLUMN_LEAF, decodeDateValue, isPlainObject, jsonStr, quoteIdent } from "./values";
12
+
13
+ export interface RawFilterOperators {
14
+ $ne?: unknown;
15
+ $in?: unknown[];
16
+ $nin?: unknown[];
17
+ $exists?: boolean;
18
+ $gt?: unknown;
19
+ $gte?: unknown;
20
+ $lt?: unknown;
21
+ $lte?: unknown;
22
+ }
23
+
24
+ /** Dot paths into `_doc`, or a base column, each to a value or to `RawFilterOperators` over it. */
25
+ export type RawFilter = Record<string, unknown>;
26
+
27
+ export interface RawFindOptions {
28
+ /** Only rows whose id sorts after this one: rows come in id order, so a page resumes where the last one ended. */
29
+ after?: string;
30
+ limit?: number;
31
+ /** Removed rows are read too unless this is `false`. */
32
+ withRemoved?: boolean;
33
+ /** Holds the rows read until the transaction ends; SQLite's one writer already does. */
34
+ lock?: boolean;
35
+ }
36
+
37
+ /**
38
+ * A table read and written as stored rows, beneath its model: a field the model no longer declares, or a table no model
39
+ * owns any more, stays reachable. Writes run no hook and derive nothing.
40
+ */
41
+ export class RawDocumentTable {
42
+ static readonly #baseColumns = new Set(["id", "createdAt", "updatedAt", "removedAt"]);
43
+ static readonly #columns = `"id", "createdAt", "updatedAt", "removedAt", "_doc"`;
44
+ readonly name: string;
45
+ readonly #connection: () => AkanSqlClient;
46
+ readonly #dialect: SqlDialect;
47
+ #exists: boolean | undefined;
48
+
49
+ constructor(name: string, connection: () => AkanSqlClient, dialect: SqlDialect) {
50
+ if (!REF_NAME_RE.test(name) || RESERVED_RE.test(name)) throw new Error(`"${name}" names no model table`);
51
+ this.name = name;
52
+ this.#connection = connection;
53
+ this.#dialect = dialect;
54
+ }
55
+
56
+ async exists() {
57
+ if (this.#exists) return true;
58
+ const isPostgres = this.#dialect.name === "postgres";
59
+ const row = await this.#connection()
60
+ .prepare(
61
+ isPostgres
62
+ ? `SELECT to_regclass(?) IS NOT NULL AS "found"`
63
+ : `SELECT COUNT(*) AS "found" FROM sqlite_master WHERE "type" = 'table' AND "name" = ?`,
64
+ )
65
+ .get<{ found: unknown }>(isPostgres ? quoteIdent(this.name) : this.name);
66
+ this.#exists = !!Number(row?.found ?? 0);
67
+ return this.#exists;
68
+ }
69
+
70
+ async find(filter?: RawFilter, options: RawFindOptions = {}) {
71
+ if (!(await this.exists())) return [];
72
+ const where = this.#where(filter, options);
73
+ const limit = options.limit === undefined ? "" : ` LIMIT ${Math.max(0, Math.trunc(options.limit))}`;
74
+ const rows = await this.#connection()
75
+ .prepare(
76
+ `SELECT ${RawDocumentTable.#columns} FROM ${quoteIdent(this.name)} WHERE ${where.sql} ORDER BY "id"${limit}${this.#forUpdate(options.lock)}`,
77
+ )
78
+ .all<SqliteDocumentRow>(...where.params);
79
+ return rows.map(RawDocumentTable.#toRow);
80
+ }
81
+
82
+ async count(filter?: RawFilter, options: Omit<RawFindOptions, "limit"> = {}) {
83
+ if (!(await this.exists())) return 0;
84
+ const where = this.#where(filter, options);
85
+ const row = await this.#connection()
86
+ .prepare(`SELECT COUNT(*) AS "count" FROM ${quoteIdent(this.name)} WHERE ${where.sql}`)
87
+ .get<{ count: unknown }>(...where.params);
88
+ return Number(row?.count ?? 0);
89
+ }
90
+
91
+ async get(id: string, { lock = false }: Pick<RawFindOptions, "lock"> = {}) {
92
+ if (!(await this.exists())) return null;
93
+ const row = await this.#connection()
94
+ .prepare(
95
+ `SELECT ${RawDocumentTable.#columns} FROM ${quoteIdent(this.name)} WHERE "id" = ?${this.#forUpdate(lock)}`,
96
+ )
97
+ .get<SqliteDocumentRow>(id);
98
+ return row ? RawDocumentTable.#toRow(row) : null;
99
+ }
100
+
101
+ async insert(row: TransferRow) {
102
+ await this.#assertExists();
103
+ await this.#connection()
104
+ .prepare(
105
+ `INSERT INTO ${quoteIdent(this.name)} (${RawDocumentTable.#columns}) VALUES (?, ?, ?, ?, ${this.#dialect.docValuePlaceholder()})`,
106
+ )
107
+ .run(row.id, row.createdAt, row.updatedAt, row.removedAt, this.#json(row._doc));
108
+ }
109
+
110
+ async write(id: string, { _doc, removedAt }: Pick<TransferRow, "_doc" | "removedAt">) {
111
+ await this.#assertExists();
112
+ const result = await this.#connection()
113
+ .prepare(
114
+ `UPDATE ${quoteIdent(this.name)} SET "_doc" = ${this.#dialect.docValuePlaceholder()}, "removedAt" = ? WHERE "id" = ?`,
115
+ )
116
+ .run(this.#json(_doc), removedAt, id);
117
+ return this.#dialect.affectedRows(result) > 0;
118
+ }
119
+
120
+ async delete(id: string) {
121
+ await this.#assertExists();
122
+ const result = await this.#connection()
123
+ .prepare(`DELETE FROM ${quoteIdent(this.name)} WHERE "id" = ?`)
124
+ .run(id);
125
+ return this.#dialect.affectedRows(result) > 0;
126
+ }
127
+
128
+ #forUpdate(lock = false) {
129
+ return lock && this.#dialect.name === "postgres" ? " FOR UPDATE" : "";
130
+ }
131
+
132
+ async #assertExists() {
133
+ if (!(await this.exists())) throw new Error(`Table "${this.name}" does not exist`);
134
+ }
135
+
136
+ #json(doc: Record<string, unknown>) {
137
+ return assertStorableJson(jsonStr(doc), this.name);
138
+ }
139
+
140
+ #where(filter: RawFilter = {}, { after, withRemoved = true }: RawFindOptions): SqlFrag {
141
+ const parts = Object.entries(filter).flatMap(([path, value]) => this.#leaves(path, value));
142
+ if (after !== undefined) parts.push({ sql: `"id" > ?`, params: [after] });
143
+ if (!withRemoved) parts.push({ sql: `"removedAt" IS NULL`, params: [] });
144
+ if (!parts.length) return { sql: "1 = 1", params: [] };
145
+ return { sql: parts.map((part) => `(${part.sql})`).join(" AND "), params: parts.flatMap((part) => part.params) };
146
+ }
147
+
148
+ #leaves(path: string, value: unknown): SqlFrag[] {
149
+ RawDocumentTable.#assertPath(path);
150
+ if (value === undefined) throw new Error(`Filter value for "${path}" is undefined`);
151
+ const leaf: QueryLeafOps = RawDocumentTable.#baseColumns.has(path) ? BASE_COLUMN_LEAF : this.#dialect;
152
+ if (!isPlainObject(value)) {
153
+ if (Array.isArray(value))
154
+ throw new Error(`Filter "${path}" compares an array; match it with $in or by its items`);
155
+ return [leaf.eq(path, value)];
156
+ }
157
+ const operators = Object.entries(value);
158
+ if (!operators.length || operators.some(([key]) => !key.startsWith("$")))
159
+ throw new Error(`Filter "${path}" compares an object; match its leaves by their own paths`);
160
+ return operators.map(([operator, operand]) => {
161
+ switch (operator) {
162
+ case "$ne":
163
+ return leaf.ne(path, operand);
164
+ case "$in":
165
+ case "$nin": {
166
+ if (!Array.isArray(operand)) throw new Error(`Filter "${path}" ${operator} takes an array`);
167
+ if (!operand.length) return { sql: operator === "$in" ? "0 = 1" : "1 = 1", params: [] };
168
+ return operator === "$in" ? leaf.inList(path, operand) : leaf.notInList(path, operand);
169
+ }
170
+ case "$exists":
171
+ return operand ? leaf.exists(path) : leaf.missing(path);
172
+ case "$gt":
173
+ case "$gte":
174
+ case "$lt":
175
+ case "$lte":
176
+ return leaf.compare(path, operator.slice(1) as "gt" | "gte" | "lt" | "lte", operand);
177
+ default:
178
+ throw new Error(`Unknown filter operator "${operator}" on "${path}"`);
179
+ }
180
+ });
181
+ }
182
+
183
+ static #assertPath(path: string) {
184
+ const segments = path.split(".");
185
+
186
+ if (segments.some((segment) => !/^[\w$-]+$/.test(segment) || /^-?\d+$/.test(segment)))
187
+ throw new Error(`Filter path "${path}" must be dot-separated names, without array indexes`);
188
+ }
189
+
190
+ static #toRow(row: SqliteDocumentRow): TransferRow {
191
+ const epoch = (value: unknown) => decodeDateValue(value)?.valueOf() ?? null;
192
+ return {
193
+ id: row.id,
194
+ createdAt: epoch(row.createdAt) ?? 0,
195
+ updatedAt: epoch(row.updatedAt) ?? 0,
196
+ removedAt: epoch(row.removedAt),
197
+ _doc: (typeof row._doc === "string" ? JSON.parse(row._doc) : row._doc) as Record<string, unknown>,
198
+ };
199
+ }
200
+ }
@@ -586,11 +586,11 @@ export class SqlDocumentStore {
586
586
  const values = Array.isArray(doc[key]) ? doc[key] : [doc[key]];
587
587
  const fieldEnum = props.enum as { has: (value: unknown) => boolean } | undefined;
588
588
  const invalidValue = fieldEnum ? values.find((value: unknown) => !fieldEnum.has(value)) : undefined;
589
- if (invalidValue !== undefined) throw new Error(`Invalid enum value for ${key}: ${invalidValue}`);
589
+ if (invalidValue !== undefined) throw SqlDocumentStore.#invalidField(key, this.table);
590
590
  }
591
591
  const validate = props.validate as ((value: unknown, doc: MutableDocumentRecord) => boolean) | undefined;
592
592
  if (validate && doc[key] !== undefined && doc[key] !== null && !validate(doc[key], doc)) {
593
- throw new Error(`Invalid field value: ${key}`);
593
+ throw SqlDocumentStore.#invalidField(key, this.table);
594
594
  }
595
595
  }
596
596
  return { ...data, ...doc };
@@ -868,6 +868,10 @@ export class SqlDocumentStore {
868
868
  );
869
869
  }
870
870
 
871
+ static #invalidField(key: string, table: string) {
872
+ return new Err.BadRequest("base.error.invalidField", { field: key }, { details: { model: table } });
873
+ }
874
+
871
875
  #assertImmutableUnchanged(prepared: DocumentRecord, originalData: DocumentRecord) {
872
876
  this.#immutableKeys ??= Object.entries(this.database.doc[FIELD_META] as unknown as FieldMap)
873
877
  .filter(([, fieldMeta]) => fieldMeta.getProps().immutable)
@@ -3,6 +3,7 @@ import { randomBytes } from "node:crypto";
3
3
  import { mkdtemp, rm, writeFile } from "node:fs/promises";
4
4
  import { tmpdir } from "node:os";
5
5
  import path from "node:path";
6
+ import { MigrationTables } from "./MigrationTables";
6
7
  import type { InsightSession } from "./types";
7
8
  import { quoteIdent } from "./values";
8
9
 
@@ -14,6 +15,8 @@ import { quoteIdent } from "./values";
14
15
  export class SqliteInsightSession implements InsightSession {
15
16
 
16
17
  static readonly #unnameable = ["sqlite_dbpage", "sqlite_stat3", "sqlite_stat4"];
18
+
19
+ static readonly #ledger = [MigrationTables.run, MigrationTables.change];
17
20
  static readonly #documentColumn = "_doc";
18
21
 
19
22
  readonly #db: Database;
@@ -22,7 +25,7 @@ export class SqliteInsightSession implements InsightSession {
22
25
 
23
26
  private constructor(db: Database, source: string, cleanup: () => Promise<void>) {
24
27
  this.#db = db;
25
- this.#hidden = [source, ...SqliteInsightSession.#unnameable];
28
+ this.#hidden = [source, ...SqliteInsightSession.#unnameable, ...SqliteInsightSession.#ledger];
26
29
  this.#cleanup = cleanup;
27
30
  }
28
31
 
@@ -177,7 +177,6 @@ export class PostgresDialect implements SqlDialect {
177
177
  const textAt = `(${this.docColumn()}) #>> ${p}`;
178
178
  const arr = `COALESCE(NULLIF(${jsonbAt}, 'null'::jsonb), '[]'::jsonb)`;
179
179
  const set = (next: string) => `${PostgresDialect.#setFunction}(${acc}, ${p}, ${next})`;
180
-
181
180
  switch (op) {
182
181
  case "set":
183
182
  return { sql: set("?::text::jsonb"), params: [jsonStr(value)] };
@@ -98,7 +98,6 @@ export class SqliteDialect implements SqlDialect {
98
98
 
99
99
  const cur = `json_extract(${this.docColumn()}, ${p})`;
100
100
  const arr = `COALESCE(${cur}, json('[]'))`;
101
-
102
101
  switch (op) {
103
102
  case "set":
104
103
  return { sql: `json_set(${acc}, ${p}, json(?))`, params: [jsonStr(value)] };
@@ -115,6 +115,7 @@ export interface InsightSession {
115
115
  }
116
116
 
117
117
  export interface DatabaseAdaptor {
118
+ readonly dialect?: SqlDialect;
118
119
  getConnection(): AkanSqlClient;
119
120
  getStore(constant: ConstantModel, database: DatabaseModel, schema: DocumentSchema): DocumentStore;
120
121
  /** Every model table this process has opened a store for. */
@@ -144,7 +145,8 @@ export interface TransactionContext {
144
145
  }
145
146
 
146
147
  export const BASE_COLUMNS = baseDocumentColumns;
147
- export const RESERVED_RE = /^sqlite_|^_akan_meta$|^search_doc$|^search_fts$/i;
148
+ export const RESERVED_RE =
149
+ /^sqlite_|^_akan_meta$|^_akan_migration_run$|^_akan_migration_change$|^search_doc$|^search_fts$/i;
148
150
  export const REF_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
149
151
  export const toSafeRefName = (value: string) => value.replace(/[^A-Za-z0-9_]+/g, "_").replace(/_+/g, "_");
150
152
  export type DocumentRecord = Record<string, unknown>;
@@ -1,4 +1,5 @@
1
1
  import { renameSync } from "node:fs";
2
+ import { isAbsolute, parse, relative, resolve, sep } from "node:path";
2
3
  import { getApiPrefix, getEnv } from "akanjs/base";
3
4
  import { adapt } from "../adapt";
4
5
 
@@ -50,6 +51,13 @@ export interface StorageAdaptor {
50
51
  copyData(request: CopyRequest): Promise<string>;
51
52
  deleteData(url: string): Promise<boolean>;
52
53
  deleteDataByPath(path: string): Promise<boolean>;
54
+ /**
55
+ * Absolute path of a stored key on this machine's disk. `null` means the key is refused (rooted, NUL, or outside
56
+ * its root): reject the request, never fall back to `readData` for it. The method is absent on an adaptor that
57
+ * keeps no local files. Without `access`, a `private/` key resolves under the private root, so a caller serving
58
+ * an untrusted key passes `access: "public"`.
59
+ */
60
+ resolveLocalPath?(path: string, access?: "public" | "private"): string | null;
53
61
  }
54
62
 
55
63
  export interface BlobStorageOptions {
@@ -88,6 +96,17 @@ export class BlobStorage
88
96
  #resolveFilePath(path: string) {
89
97
  return path.startsWith("private/") ? `${this.privateRoot}/${path}` : `${this.root}/${path}`;
90
98
  }
99
+ resolveLocalPath(path: string, access?: "public" | "private") {
100
+
101
+ if (path.includes("\0") || parse(path).root) return null;
102
+ const isPrivate = access ? access === "private" : path.startsWith("private/");
103
+ const base = resolve(isPrivate ? this.privateRoot : this.root);
104
+ const target = resolve(base, path);
105
+
106
+ const within = relative(base, target);
107
+ if (!within || within === ".." || within.startsWith(`..${sep}`) || isAbsolute(within)) return null;
108
+ return target;
109
+ }
91
110
  async readData(path: string): Promise<ReadableStream> {
92
111
  const filePath = this.#resolveFilePath(path);
93
112
  return Bun.file(filePath).stream();
@@ -1,19 +1,25 @@
1
- import { type CacheAdaptor, CacheAdaptorRole } from "akanjs/service";
1
+ import { type CacheAdaptor, CacheAdaptorRole, EndpointCacheKeys } from "akanjs/service";
2
2
  import dayjs from "dayjs";
3
+ import type { EndpointInfo } from "./endpointInfo";
3
4
  import type { SignalContext } from "./signalContext";
4
5
  import { TaggedJson } from "./TaggedJson";
5
6
  import { traceCache } from "./trace";
6
7
 
7
8
  export class EndpointCache {
8
- static #topic = "cache";
9
9
  static #refused = new Set<string>();
10
10
 
11
+ static declares(endpointInfo: EndpointInfo): boolean {
12
+ const ttl = endpointInfo.signalOption.cache;
13
+ return !!ttl && Number.isFinite(ttl) && ttl > 0;
14
+ }
15
+
11
16
  static async through(context: SignalContext, run: () => Promise<unknown>) {
12
17
  const ttl = context.endpointInfo.signalOption.cache;
13
- if (!ttl || !Number.isFinite(ttl) || ttl <= 0) return await run();
18
+ if (!ttl || !EndpointCache.declares(context.endpointInfo)) return await run();
14
19
  if (!EndpointCache.#cacheable(context)) return await run();
15
20
  const cache = context.getAdaptor(CacheAdaptorRole) as unknown as CacheAdaptor;
16
- const key = `${context.key}:${JSON.stringify(context.args)}`;
21
+ const key = await EndpointCache.#key(context, cache);
22
+ if (key === undefined) return await run();
17
23
  const cached = await EndpointCache.#read(context, cache, key);
18
24
  if (cached !== undefined) {
19
25
  traceCache(true);
@@ -36,15 +42,23 @@ export class EndpointCache {
36
42
  return false;
37
43
  }
38
44
 
45
+ static async #key(context: SignalContext, cache: CacheAdaptor) {
46
+ try {
47
+ return await EndpointCacheKeys.entryKey(cache, context.key, context.args);
48
+ } catch (error) {
49
+ context.adaptor.logger.warn(`Cache read failed for ${context.key}: ${String(error)}`);
50
+ return undefined;
51
+ }
52
+ }
39
53
  static async #read(context: SignalContext, cache: CacheAdaptor, key: string) {
40
54
  try {
41
- const cached = await cache.get<string>(EndpointCache.#topic, key);
55
+ const cached = await cache.get<string>(EndpointCacheKeys.topic, key);
42
56
  if (cached == null) return undefined;
43
57
  const { returnRef, arrDepth } = context.endpointInfo.returns;
44
58
  return TaggedJson.parse(cached, { ref: returnRef, arrDepth });
45
59
  } catch (error) {
46
60
  context.adaptor.logger.warn(`Cache read failed for ${context.key}: ${String(error)}`);
47
- await cache.delete(EndpointCache.#topic, key).catch(() => undefined);
61
+ await cache.delete(EndpointCacheKeys.topic, key).catch(() => undefined);
48
62
  return undefined;
49
63
  }
50
64
  }
@@ -53,7 +67,9 @@ export class EndpointCache {
53
67
  const serialized = TaggedJson.stringify(result);
54
68
 
55
69
  if (serialized === undefined) return;
56
- await cache.set(EndpointCache.#topic, key, serialized, { expireAt: dayjs().add(ttl, "millisecond") });
70
+
71
+ const lifeMs = Math.min(ttl, EndpointCacheKeys.generationTtlMs);
72
+ await cache.set(EndpointCacheKeys.topic, key, serialized, { expireAt: dayjs().add(lifeMs, "millisecond") });
57
73
  } catch (error) {
58
74
  context.adaptor.logger.warn(`Cache write failed for ${context.key}: ${String(error)}`);
59
75
  }
@@ -0,0 +1,37 @@
1
+ import type { Adaptor } from "akanjs/service";
2
+
3
+ import type { EndpointInfo } from "./endpointInfo";
4
+ import type { EndpointErrors } from "./types";
5
+
6
+ export class EndpointErrorContract {
7
+ static readonly #warned = new Set<string>();
8
+
9
+ /** Refuses a declaration no response could carry: a status outside 400–599, or a key that is not an error key. */
10
+ static assertDeclared(errors: EndpointErrors | undefined) {
11
+ if (!errors) return;
12
+ for (const [status, keys] of Object.entries(errors) as [string, EndpointErrors[number]][]) {
13
+ const code = Number(status);
14
+ if (!Number.isInteger(code) || code < 400 || code > 599)
15
+ throw new Error(`errors declares status ${status}: an error answers with a 400–599 status`);
16
+ for (const key of keys)
17
+ if (!/^[^.]+\.error\.[^.]+$/.test(key))
18
+ throw new Error(`errors declares "${key}" under ${status}: an error key reads <module>.error.<key>`);
19
+ }
20
+ }
21
+
22
+ static keysOf(errors: EndpointErrors | undefined): string[] {
23
+ return errors ? Object.values(errors).flat() : [];
24
+ }
25
+
26
+ static checkThrown(endpoint: Adaptor, endpointInfo: EndpointInfo, key: string, thrownKey: string) {
27
+ const { errors } = endpointInfo.signalOption;
28
+ if (!errors || process.env.NODE_ENV === "production" || thrownKey.startsWith("base.error.")) return;
29
+ if (EndpointErrorContract.keysOf(errors).includes(thrownKey)) return;
30
+ const warnedKey = `${key}\0${thrownKey}`;
31
+ if (EndpointErrorContract.#warned.has(warnedKey)) return;
32
+ EndpointErrorContract.#warned.add(warnedKey);
33
+ endpoint.logger.warn(
34
+ `"${key}" threw ${thrownKey}, which its \`errors\` declaration leaves out: OpenAPI and the MCP catalogue do not publish it.`,
35
+ );
36
+ }
37
+ }
@@ -25,15 +25,17 @@ export class EndpointIdempotency {
25
25
  static readonly #topic = "idempotency";
26
26
  static readonly #tag = "\u0000akan";
27
27
 
28
+ static applies(context: SignalContext): boolean {
29
+ return context.endpointInfo.type === "mutation" && context.transport === "http" && context.origin === "http";
30
+ }
31
+
28
32
  /** A Response when the call is kept or replayed; otherwise the handler's own result, which the caller answers. */
29
33
  static async through(
30
34
  context: SignalContext,
31
35
  run: () => Promise<unknown>,
32
36
  answer: (result: unknown) => Promise<unknown>,
33
37
  ) {
34
-
35
- if (context.endpointInfo.type !== "mutation" || context.transport !== "http" || context.origin !== "http")
36
- return await run();
38
+ if (!EndpointIdempotency.applies(context)) return await run();
37
39
  const { req } = context.getHttpContext();
38
40
  const idempotencyKey = req.headers.get(EndpointIdempotency.header)?.trim();
39
41
  if (!idempotencyKey || idempotencyKey.length > 256) return await run();
@@ -7,6 +7,7 @@ import {
7
7
  isEnum,
8
8
  type PromiseOrObject,
9
9
  } from "akanjs/base";
10
+ import { trimSlashes } from "akanjs/common";
10
11
  import type {
11
12
  ConstantFieldType,
12
13
  ConstantFieldTypeInput,
@@ -18,6 +19,7 @@ import type {
18
19
  UploadableClientArg,
19
20
  } from "akanjs/constant";
20
21
  import type { ServiceModel } from "akanjs/service";
22
+ import { EndpointErrorContract } from "./endpointErrorContract";
21
23
  import type { InternalArgCls } from "./internalArg";
22
24
  import type { ArgType, SignalOption, SrvMap } from "./types";
23
25
 
@@ -103,6 +105,7 @@ export class EndpointInfo<
103
105
  }
104
106
 
105
107
  constructor(type: ReqType, returnRef: Returns, signalOption: SignalOption<Returns, Nullable> = {}) {
108
+ EndpointErrorContract.assertDeclared(signalOption.errors);
106
109
  this.type = type;
107
110
  this.returns = EndpointInfo.getReturnInfo(returnRef, signalOption);
108
111
  this.signalOption = signalOption;
@@ -335,7 +338,7 @@ export class EndpointInfo<
335
338
  if (prefix === false || prefix === "") return "";
336
339
  const resolved = prefix ?? defaultPrefix;
337
340
  if (!resolved) return "";
338
- const trimmed = resolved.trim().replace(/^\/+|\/+$/g, "");
341
+ const trimmed = trimSlashes(resolved.trim());
339
342
  return trimmed ? `/${trimmed}` : "";
340
343
  }
341
344
  }
@@ -92,9 +92,13 @@ export class EndpointRateLimit {
92
92
  }
93
93
 
94
94
  /** `ip` runs before the request body is read; `account` after the middlewares resolved the caller. */
95
- static async admit(context: SignalContext, phase: "ip" | "account") {
95
+ static admit(context: SignalContext, phase: "ip" | "account"): Promise<void> | undefined {
96
96
  const budget = EndpointRateLimit.budgetOf(context.key, context.endpointInfo);
97
- if (!budget || budget.by !== phase) return;
97
+ if (!budget || budget.by !== phase) return undefined;
98
+ return EndpointRateLimit.#consume(context, phase, budget);
99
+ }
100
+
101
+ static async #consume(context: SignalContext, phase: "ip" | "account", budget: ResolvedBudget) {
98
102
  const caller =
99
103
  (phase === "account" ? EndpointRateLimit.#accountOf(context) : null) ?? EndpointRateLimit.#ipOf(context);
100
104
  const verdict = await EndpointRateLimit.#limiterOf(context).consume(`${context.key}:${caller}`, budget);
@@ -60,7 +60,10 @@ export class Exception extends Error {
60
60
  };
61
61
  }
62
62
 
63
- /** Forwarded to the caller instead of generalized: an `Exception`, a dictionary `Err`, or a restored remote one. */
63
+ /**
64
+ * Forwarded to the caller instead of generalized: an `Exception`, a dictionary `Err`, a `NoDocumentError`, or a restored
65
+ * remote one.
66
+ */
64
67
  export interface ExceptionLike {
65
68
  statusCode: number;
66
69
  toJSON: () => object;
package/signal/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./agentTurnStream";
6
6
  export * from "./base.signal";
7
7
  export { CrossSiteGuard, type CrossSiteOption } from "./CrossSiteGuard";
8
8
  export * from "./endpoint";
9
+ export * from "./endpointErrorContract";
9
10
  export * from "./endpointInfo";
10
11
  export * from "./endpointRateLimit";
11
12
  export * from "./exception";
@@ -188,7 +188,7 @@ export class McpDocument {
188
188
  const outputSchema = this.#outputSchema(endpoint);
189
189
  return {
190
190
  name: key,
191
- ...this.#catalogue.entryTexts(refName, key),
191
+ ...this.#withErrors(this.#catalogue.entryTexts(refName, key), endpoint),
192
192
  inputSchema: {
193
193
  type: "object",
194
194
  properties,
@@ -201,6 +201,21 @@ export class McpDocument {
201
201
  };
202
202
  }
203
203
 
204
+ #withErrors(texts: { title?: string; description?: string }, endpoint: SerializedEndpoint) {
205
+ const errors = Object.entries(endpoint.errors ?? {}) as [string, readonly string[]][];
206
+ if (!errors.length) return texts;
207
+ const listed = errors
208
+ .flatMap(([status, keys]) =>
209
+ keys.map((key) => {
210
+ const message = this.#options.resolveDescription?.(key);
211
+ return `${status} ${key}${message ? ` (${message})` : ""}`;
212
+ }),
213
+ )
214
+ .join(", ");
215
+ const line = `Errors it answers with on purpose: ${listed}.`;
216
+ return { ...texts, description: texts.description ? `${texts.description}\n${line}` : line };
217
+ }
218
+
204
219
  #outputSchema(endpoint: SerializedEndpoint) {
205
220
  if (this.#options.outputSchema === "none") return undefined;
206
221
 
@@ -21,7 +21,7 @@ export class McpUriTemplate {
21
21
 
22
22
  static expand(template: string, args: Record<string, unknown>): string {
23
23
  const queryAt = template.indexOf("{?");
24
- const path = (queryAt === -1 ? template : template.slice(0, queryAt)).replace(/\{([^}]+)\}/g, (_, name: string) =>
24
+ const path = (queryAt === -1 ? template : template.slice(0, queryAt)).replace(/\{([^{}]+)\}/g, (_, name: string) =>
25
25
  encodeURIComponent(String(args[name] ?? "")),
26
26
  );
27
27
  if (queryAt === -1) return path;