akanjs 3.0.2 → 3.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/base/baseEnv.ts +0 -4
  3. package/base/primitiveRegistry.ts +2 -2
  4. package/client/cookie.ts +5 -1
  5. package/client/csrLocation.ts +122 -0
  6. package/client/csrTypes.ts +32 -4
  7. package/client/device.ts +2 -1
  8. package/client/frameConfig.ts +78 -7
  9. package/client/index.ts +3 -0
  10. package/client/makePageProto.tsx +3 -2
  11. package/client/native/external-view.ts +1 -0
  12. package/client/route/RootLayoutDefinition.ts +7 -1
  13. package/client/route/RouteHead.ts +108 -0
  14. package/client/route/index.ts +1 -0
  15. package/client/router.ts +36 -23
  16. package/client/rscNavigation.ts +8 -2
  17. package/client/viewportMeta.ts +60 -0
  18. package/client/webManifest.ts +18 -0
  19. package/common/Logger.ts +7 -2
  20. package/common/PipelineNames.ts +31 -0
  21. package/common/PipelinePattern.ts +70 -0
  22. package/common/PipelinePlanSchema.ts +490 -0
  23. package/common/backoff.ts +14 -0
  24. package/common/index.ts +39 -0
  25. package/common/pipelinePlan.ts +259 -0
  26. package/common/routeConvention.ts +1 -0
  27. package/common/types.ts +17 -0
  28. package/constant/constantRegistry.ts +26 -6
  29. package/dictionary/base.dictionary.ts +8 -0
  30. package/dictionary/trans.ts +9 -1
  31. package/document/into.ts +5 -0
  32. package/fetch/client/fetchClient.ts +135 -31
  33. package/fetch/client/httpClient.ts +45 -0
  34. package/fetch/client/wsClient.ts +125 -16
  35. package/fetch/fetchType/buildFetch.type.ts +1 -0
  36. package/index.ts +243 -10
  37. package/package.json +1 -5
  38. package/server/akanApp.ts +5 -2
  39. package/server/akanServer.ts +44 -8
  40. package/server/artifact/builderRpc.ts +8 -3
  41. package/server/artifact/ipcTypes.ts +5 -2
  42. package/server/di/diLifecycle.ts +39 -3
  43. package/server/di/utils.ts +28 -1
  44. package/server/ed25519Key.ts +48 -0
  45. package/server/index.ts +1 -0
  46. package/server/machine/index.ts +2 -0
  47. package/server/machine/machineAccount.ts +27 -0
  48. package/server/machine/machineToken.ts +151 -0
  49. package/server/mcp/McpApps.ts +256 -0
  50. package/server/mcp/McpDispatcher.ts +31 -7
  51. package/server/mcp/McpRouter.ts +209 -46
  52. package/server/ops/opsToken.ts +3 -10
  53. package/server/resolver/database.resolver.ts +3 -0
  54. package/server/resolver/signal.resolver.ts +40 -3
  55. package/server/routeElementComposer.tsx +78 -2
  56. package/server/routeState.ts +6 -0
  57. package/server/routeTreeBuilder.ts +26 -9
  58. package/server/routing/apiRouter.ts +6 -3
  59. package/server/routing/roomReplay.ts +88 -0
  60. package/server/rscClient.tsx +53 -8
  61. package/server/rscWorker.tsx +141 -27
  62. package/server/rscWorkerHost.ts +18 -5
  63. package/server/sitemap.ts +14 -0
  64. package/server/ssrFromRscRenderer.tsx +12 -6
  65. package/server/ssrTypes.ts +3 -1
  66. package/server/systemPageDocument.tsx +38 -30
  67. package/server/types.tsx +2 -0
  68. package/server/webRouter.ts +74 -5
  69. package/service/injectInfo.ts +9 -1
  70. package/service/ipcTypes.ts +2 -1
  71. package/service/predefinedAdaptor/database.adaptor.ts +1 -146
  72. package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
  73. package/service/predefinedAdaptor/queueBackoff.ts +12 -0
  74. package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
  75. package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
  76. package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
  77. package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
  78. package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
  79. package/service/predefinedAdaptor/sql/types.ts +8 -6
  80. package/service/predefinedAdaptor/sql/values.ts +0 -23
  81. package/service/serve.ts +5 -1
  82. package/signal/agent.signal.ts +1 -1
  83. package/signal/base.signal.ts +2 -2
  84. package/signal/endpointIdempotency.ts +53 -0
  85. package/signal/internalArg.ts +14 -0
  86. package/signal/mcp/McpDocument.ts +38 -1
  87. package/signal/mcp/mcpProtocol.ts +18 -0
  88. package/signal/signalContext.ts +58 -30
  89. package/signal/signalRegistry.ts +14 -4
  90. package/signal/types.ts +18 -3
  91. package/store/agent/StoreSurfaceSource.ts +1 -1
  92. package/store/agentic/StToolBuilder.ts +3 -0
  93. package/test/conformance.ts +9 -33
  94. package/types/base/baseEnv.d.ts +0 -4
  95. package/types/base/primitiveRegistry.d.ts +1 -1
  96. package/types/client/csrLocation.d.ts +32 -0
  97. package/types/client/csrTypes.d.ts +40 -4
  98. package/types/client/frameConfig.d.ts +14 -2
  99. package/types/client/index.d.ts +3 -0
  100. package/types/client/native/external-view.d.ts +1 -0
  101. package/types/client/route/RootLayoutDefinition.d.ts +2 -1
  102. package/types/client/route/RouteHead.d.ts +13 -0
  103. package/types/client/route/index.d.ts +1 -0
  104. package/types/client/router.d.ts +6 -0
  105. package/types/client/rscNavigation.d.ts +4 -0
  106. package/types/client/viewportMeta.d.ts +18 -0
  107. package/types/client/webManifest.d.ts +4 -0
  108. package/types/common/PipelineNames.d.ts +6 -0
  109. package/types/common/PipelinePattern.d.ts +14 -0
  110. package/types/common/PipelinePlanSchema.d.ts +9 -0
  111. package/types/common/backoff.d.ts +10 -0
  112. package/types/common/index.d.ts +5 -0
  113. package/types/common/pipelinePlan.d.ts +224 -0
  114. package/types/common/types.d.ts +16 -0
  115. package/types/constant/constantRegistry.d.ts +5 -2
  116. package/types/dictionary/base.dictionary.d.ts +1 -1
  117. package/types/dictionary/dictionary.d.ts +8 -8
  118. package/types/document/into.d.ts +9 -0
  119. package/types/fetch/client/fetchClient.d.ts +31 -4
  120. package/types/fetch/client/httpClient.d.ts +8 -0
  121. package/types/fetch/client/wsClient.d.ts +26 -2
  122. package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
  123. package/types/index.d.ts +230 -9
  124. package/types/server/akanServer.d.ts +16 -1
  125. package/types/server/artifact/builderRpc.d.ts +7 -2
  126. package/types/server/artifact/ipcTypes.d.ts +5 -1
  127. package/types/server/di/utils.d.ts +1 -0
  128. package/types/server/ed25519Key.d.ts +13 -0
  129. package/types/server/index.d.ts +1 -0
  130. package/types/server/machine/index.d.ts +2 -0
  131. package/types/server/machine/machineAccount.d.ts +5 -0
  132. package/types/server/machine/machineToken.d.ts +29 -0
  133. package/types/server/machine.d.ts +1 -0
  134. package/types/server/mcp/McpApps.d.ts +50 -0
  135. package/types/server/mcp/McpDispatcher.d.ts +4 -1
  136. package/types/server/mcp/McpRouter.d.ts +4 -0
  137. package/types/server/resolver/signal.resolver.d.ts +2 -1
  138. package/types/server/routeElementComposer.d.ts +24 -4
  139. package/types/server/routeState.d.ts +4 -1
  140. package/types/server/routing/roomReplay.d.ts +24 -0
  141. package/types/server/rscWorkerHost.d.ts +5 -2
  142. package/types/server/sitemap.d.ts +2 -0
  143. package/types/server/ssrFromRscRenderer.d.ts +3 -3
  144. package/types/server/ssrTypes.d.ts +4 -1
  145. package/types/server/systemPageDocument.d.ts +2 -0
  146. package/types/server/types.d.ts +4 -1
  147. package/types/server/webRouter.d.ts +3 -1
  148. package/types/service/ipcTypes.d.ts +2 -0
  149. package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
  150. package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
  151. package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
  152. package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
  153. package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
  154. package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
  155. package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
  156. package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
  157. package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
  158. package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
  159. package/types/service/serve.d.ts +3 -0
  160. package/types/signal/endpointIdempotency.d.ts +7 -0
  161. package/types/signal/internalArg.d.ts +10 -0
  162. package/types/signal/mcp/McpDocument.d.ts +8 -0
  163. package/types/signal/mcp/mcpProtocol.d.ts +17 -0
  164. package/types/signal/signalContext.d.ts +5 -3
  165. package/types/signal/signalRegistry.d.ts +4 -4
  166. package/types/signal/types.d.ts +21 -1
  167. package/types/store/agentic/StToolBuilder.d.ts +2 -0
  168. package/types/test/conformance.d.ts +7 -10
  169. package/types/ui/Agent/Chat.d.ts +5 -1
  170. package/types/ui/Agent/EmbedApproval.d.ts +1 -0
  171. package/types/ui/Agent/index.d.ts +3 -0
  172. package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
  173. package/types/ui/Agent/useScreenActions.d.ts +18 -0
  174. package/types/ui/BottomSheet.d.ts +1 -0
  175. package/types/ui/Constant/Doc.d.ts +11 -17
  176. package/types/ui/Constant/index.d.ts +7 -2
  177. package/types/ui/Constant/schemaDoc.d.ts +11 -0
  178. package/types/ui/Dialog/Modal.d.ts +6 -1
  179. package/types/ui/Dialog/index.d.ts +1 -1
  180. package/types/ui/Dialog/modalSurface.d.ts +12 -0
  181. package/types/ui/Layout/BottomTab.d.ts +11 -2
  182. package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
  183. package/types/ui/Layout/Navbar.d.ts +4 -1
  184. package/types/ui/Layout/TopInset.d.ts +1 -1
  185. package/types/ui/Layout/index.d.ts +3 -3
  186. package/types/ui/Link/Action.d.ts +5 -1
  187. package/types/ui/Link/Anchor.d.ts +5 -3
  188. package/types/ui/Link/activePath.d.ts +13 -0
  189. package/types/ui/Link/index.d.ts +1 -1
  190. package/types/ui/Modal.d.ts +6 -1
  191. package/types/ui/Model/EditModal.d.ts +6 -1
  192. package/types/ui/Model/New.d.ts +6 -1
  193. package/types/ui/Model/ViewEditModal.d.ts +6 -1
  194. package/types/ui/Model/ViewModal.d.ts +6 -1
  195. package/types/ui/Model/newFormOrigin.d.ts +7 -0
  196. package/types/ui/Portal.d.ts +3 -1
  197. package/types/ui/Reference/origin.d.ts +12 -0
  198. package/types/ui/Signal/Doc.d.ts +23 -18
  199. package/types/ui/Signal/Listener.d.ts +2 -6
  200. package/types/ui/Signal/Object.d.ts +6 -10
  201. package/types/ui/Signal/Response.d.ts +3 -6
  202. package/types/ui/Signal/RestApi.d.ts +11 -15
  203. package/types/ui/Signal/WebSocket.d.ts +11 -21
  204. package/types/ui/Signal/endpointEntries.d.ts +8 -0
  205. package/types/ui/Signal/index.d.ts +35 -12
  206. package/types/ui/Signal/signalText.d.ts +178 -0
  207. package/types/ui/System/CSR.d.ts +2 -2
  208. package/types/ui/System/Client.d.ts +4 -3
  209. package/types/ui/System/Common.d.ts +27 -6
  210. package/types/ui/System/SSR.d.ts +6 -6
  211. package/types/ui/Tooltip.d.ts +4 -2
  212. package/types/ui/index.d.ts +2 -1
  213. package/types/ui/overlayLayer.d.ts +6 -0
  214. package/types/ui/overlayPosition.d.ts +17 -0
  215. package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
  216. package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
  217. package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
  218. package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
  219. package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
  220. package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
  221. package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
  222. package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
  223. package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
  224. package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
  225. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +7 -0
  226. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
  227. package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
  228. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
  229. package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
  230. package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
  231. package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
  232. package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
  233. package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
  234. package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
  235. package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
  236. package/types/vendor/use-agentic/types.d.ts +2 -0
  237. package/types/webkit/CsrStack.d.ts +1 -1
  238. package/types/webkit/McpAppAgent.d.ts +14 -0
  239. package/types/webkit/McpAppApprovals.d.ts +8 -0
  240. package/types/webkit/McpAppBridge.d.ts +101 -0
  241. package/types/webkit/McpAppView.d.ts +21 -0
  242. package/types/webkit/index.d.ts +1 -0
  243. package/types/webkit/useLocation.d.ts +1 -1
  244. package/types/webkit/useVisualViewportFrame.d.ts +13 -0
  245. package/ui/Agent/Chat.tsx +29 -10
  246. package/ui/Agent/Composer.tsx +2 -1
  247. package/ui/Agent/EmbedApproval.tsx +17 -0
  248. package/ui/Agent/RichInput.tsx +1 -1
  249. package/ui/Agent/index.ts +2 -0
  250. package/ui/Agent/useKeyboardFrame.ts +10 -0
  251. package/ui/Agent/useScreenActions.ts +45 -0
  252. package/ui/BottomSheet.tsx +82 -53
  253. package/ui/Constant/Doc.tsx +113 -60
  254. package/ui/Constant/index.ts +2 -2
  255. package/ui/Constant/schemaDoc.ts +86 -26
  256. package/ui/Dialog/LegacyModal.tsx +53 -52
  257. package/ui/Dialog/Modal.tsx +93 -49
  258. package/ui/Dialog/modalSurface.ts +61 -0
  259. package/ui/DragAction.tsx +1 -1
  260. package/ui/DraggableList.tsx +1 -1
  261. package/ui/Dropdown.tsx +4 -6
  262. package/ui/KeyboardAvoiding.tsx +1 -1
  263. package/ui/Layout/BottomInset.tsx +20 -14
  264. package/ui/Layout/BottomTab.tsx +42 -16
  265. package/ui/Layout/FrameSlotFallback.tsx +59 -0
  266. package/ui/Layout/Navbar.tsx +37 -11
  267. package/ui/Layout/Sider.tsx +1 -1
  268. package/ui/Layout/TopInset.tsx +15 -14
  269. package/ui/Link/Action.tsx +15 -5
  270. package/ui/Link/Anchor.tsx +25 -13
  271. package/ui/Link/activePath.ts +25 -0
  272. package/ui/Link/index.tsx +1 -1
  273. package/ui/Load/Pagination.tsx +1 -1
  274. package/ui/Load/Units.tsx +1 -1
  275. package/ui/Loading/ProgressBar.tsx +1 -1
  276. package/ui/Modal.tsx +9 -0
  277. package/ui/Model/EditModal.tsx +16 -1
  278. package/ui/Model/New.tsx +25 -2
  279. package/ui/Model/NewWrapper_Client.tsx +2 -0
  280. package/ui/Model/ViewEditModal.tsx +9 -0
  281. package/ui/Model/ViewModal.tsx +16 -1
  282. package/ui/Model/newFormOrigin.ts +45 -0
  283. package/ui/Popconfirm.tsx +5 -7
  284. package/ui/Portal.tsx +4 -7
  285. package/ui/Reference/origin.ts +31 -0
  286. package/ui/ScreenNavigator.tsx +1 -1
  287. package/ui/Select.tsx +2 -7
  288. package/ui/Signal/Arg.tsx +2 -2
  289. package/ui/Signal/Doc.tsx +125 -63
  290. package/ui/Signal/Endpoint.tsx +11 -10
  291. package/ui/Signal/Listener.tsx +7 -10
  292. package/ui/Signal/Object.tsx +7 -14
  293. package/ui/Signal/Response.tsx +24 -24
  294. package/ui/Signal/RestApi.tsx +63 -52
  295. package/ui/Signal/WebSocket.tsx +65 -72
  296. package/ui/Signal/endpointEntries.ts +25 -0
  297. package/ui/Signal/index.ts +23 -6
  298. package/ui/Signal/signalText.ts +59 -0
  299. package/ui/System/CSR.tsx +34 -8
  300. package/ui/System/Client.tsx +36 -5
  301. package/ui/System/Common.tsx +76 -32
  302. package/ui/System/SSR.tsx +73 -31
  303. package/ui/Toast.tsx +1 -1
  304. package/ui/Tooltip.tsx +84 -30
  305. package/ui/index.ts +11 -1
  306. package/ui/overlayLayer.ts +25 -2
  307. package/ui/overlayPosition.ts +90 -0
  308. package/ui/recipe/inputRecipe.ts +8 -1
  309. package/ui/styles.css +152 -0
  310. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
  311. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
  312. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
  313. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
  314. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
  315. package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
  316. package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
  317. package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
  318. package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
  319. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
  320. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
  321. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
  322. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
  323. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
  324. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
  325. package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
  326. package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
  327. package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
  328. package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
  329. package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
  330. package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
  331. package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
  332. package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
  333. package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
  334. package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
  335. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
  336. package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
  337. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
  338. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
  339. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
  340. package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
  341. package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +25 -11
  342. package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
  343. package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
  344. package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
  345. package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
  346. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
  347. package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
  348. package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
  349. package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
  350. package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
  351. package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
  352. package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
  353. package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
  354. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
  355. package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
  356. package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
  357. package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
  358. package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
  359. package/vendor/use-agentic/ToolRunner.ts +8 -1
  360. package/vendor/use-agentic/types.ts +2 -0
  361. package/webkit/CsrRouteTable.ts +1 -0
  362. package/webkit/CsrStack.ts +9 -2
  363. package/webkit/McpAppAgent.ts +138 -0
  364. package/webkit/McpAppApprovals.ts +56 -0
  365. package/webkit/McpAppBridge.ts +291 -0
  366. package/webkit/McpAppView.ts +258 -0
  367. package/webkit/bootCsr.tsx +54 -5
  368. package/webkit/index.ts +1 -0
  369. package/webkit/useCsrValues.ts +16 -10
  370. package/webkit/useFrameRuntime.ts +10 -1
  371. package/webkit/useLocation.ts +4 -3
  372. package/webkit/useVisualViewportFrame.tsx +52 -0
  373. package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
  374. package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
  375. package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
  376. package/types/ui/ServerPortal.d.ts +0 -15
  377. package/ui/Agent/useKeyboardInset.ts +0 -19
  378. package/ui/ServerPortal.tsx +0 -49
@@ -22,6 +22,7 @@ import type { ServerSignal, ServerSignalCls } from "../../signal/serverSignal";
22
22
  import { SignalRegistry } from "../../signal/signalRegistry";
23
23
  import type { AkanLib, DatabaseModule, ScalarModule, ServiceModule } from "../akanLib";
24
24
  import { createDefaultAkanOption } from "../akanOption";
25
+ import { MachineAccount } from "../machine/machineAccount";
25
26
  import type { WebProxyRegistration } from "../proxy";
26
27
  import { CascadeRunner, DatabaseResolver, ServiceResolver, SignalResolver } from "../resolver";
27
28
  import type { SignalRoutes, WebsocketRoutes } from "../types";
@@ -32,6 +33,7 @@ import {
32
33
  type DiModuleCandidate,
33
34
  getModuleCascadeRefNames,
34
35
  getModuleDependencyRefNames,
36
+ getModuleRelationRefNames,
35
37
  isDestroyableUse,
36
38
  normalizeAdaptorRefName,
37
39
  normalizeServiceRefName,
@@ -168,6 +170,8 @@ export class DiLifecycle {
168
170
  this.#scalar.set(mod.constant.refName, mod);
169
171
  });
170
172
  });
173
+
174
+ this.#middleware.set(MachineAccount.refName, MachineAccount);
171
175
  const disabledModules = this.#resolveDisabledModules({
172
176
  databaseCandidates,
173
177
  serviceCandidates,
@@ -235,11 +239,18 @@ export class DiLifecycle {
235
239
  const candidates = new Map<string, DiModuleCandidate>([...databaseCandidates, ...serviceCandidates]);
236
240
  const disabledReasons = new Map<string, string>();
237
241
 
242
+ const { operationMode } = getEnv();
238
243
  candidates.forEach(({ refName, module }) => {
239
- if (!module.service.srv.enabled) disabledReasons.set(refName, "service disabled");
244
+ const modes = module.service.srv.operationMode;
245
+ if (modes && !modes.includes(operationMode))
246
+ disabledReasons.set(refName, `operationMode "${operationMode}" is not in [${modes.join(", ")}]`);
247
+ else if (!module.service.srv.enabled) disabledReasons.set(refName, "service disabled");
240
248
  });
241
249
 
242
- const selected = this.#resolveSelectedModules(candidates, modules);
250
+ const selected = this.#resolveSelectedModules(
251
+ candidates,
252
+ DiLifecycle.#expandSelectors(modules, candidates, "modules"),
253
+ );
243
254
  if (selected) {
244
255
  candidates.forEach(({ refName }) => {
245
256
  if (!selected.has(refName) && !disabledReasons.has(refName))
@@ -247,7 +258,12 @@ export class DiLifecycle {
247
258
  });
248
259
  }
249
260
 
250
- const excluded = this.#resolveExcludedModules({ candidates, disableModules, disableLibs, moduleLibs });
261
+ const excluded = this.#resolveExcludedModules({
262
+ candidates,
263
+ disableModules: DiLifecycle.#expandSelectors(disableModules, candidates, "disableModules"),
264
+ disableLibs,
265
+ moduleLibs,
266
+ });
251
267
 
252
268
  excluded.forEach((reason, refName) => {
253
269
  if (!disabledReasons.has(refName)) disabledReasons.set(refName, reason);
@@ -334,6 +350,7 @@ export class DiLifecycle {
334
350
  const dependencies = [
335
351
  ...getModuleDependencyRefNames(candidate.module),
336
352
  ...getModuleCascadeRefNames(candidate.module),
353
+ ...getModuleRelationRefNames(candidate.module),
337
354
  ];
338
355
  for (const dependency of dependencies) if (candidates.has(dependency)) pending.push(dependency);
339
356
  }
@@ -342,6 +359,25 @@ export class DiLifecycle {
342
359
  return selected;
343
360
  }
344
361
 
362
+ static #expandSelectors(names: string[], candidates: Map<string, DiModuleCandidate>, option: string) {
363
+ return [
364
+ ...new Set(
365
+ names.flatMap((name) => {
366
+ if (!name.startsWith("@")) return [name];
367
+ const mode = name.slice(1);
368
+ const tagged = [...candidates.values()]
369
+ .filter(({ module }) => module.service.srv.operationMode?.includes(mode))
370
+ .map(({ refName }) => refName);
371
+ if (!tagged.length)
372
+ throw new Error(
373
+ `[DI:${option}] "${name}" names no module: no serve() in this app or its libs lists operationMode "${mode}".`,
374
+ );
375
+ return tagged;
376
+ }),
377
+ ),
378
+ ];
379
+ }
380
+
345
381
  static #assertKnown(names: string[], known: Set<string>, unknownLabel: string, knownLabel: string) {
346
382
  const unknown = names.filter((name) => !known.has(name));
347
383
  if (!unknown.length) return;
@@ -1,9 +1,15 @@
1
- import { INJECT_META } from "akanjs/base";
1
+ import { FIELD_META, INJECT_META } from "akanjs/base";
2
2
  import { lowerlize, toError } from "akanjs/common";
3
3
  import { ConstantRegistry } from "akanjs/constant";
4
4
  import type { InjectInfo } from "akanjs/service";
5
5
  import type { DatabaseModule, ServiceModule } from "../akanLib";
6
6
 
7
+ interface FieldMeta {
8
+ isClass: boolean;
9
+ isScalar: boolean;
10
+ modelRef: unknown;
11
+ }
12
+
7
13
  interface StageTask {
8
14
  label: string;
9
15
  run: () => Promise<unknown>;
@@ -68,6 +74,27 @@ export const getModuleDependencyRefNames = (mod: DatabaseModule | ServiceModule)
68
74
  return dependencies;
69
75
  };
70
76
 
77
+ export const getModuleRelationRefNames = (mod: DatabaseModule | ServiceModule) => {
78
+ const dependencies = new Set<string>();
79
+ if (!("constant" in mod)) return dependencies;
80
+ const visited = new Set<unknown>();
81
+ const visit = (modelRef: unknown) => {
82
+ if (visited.has(modelRef)) return;
83
+ visited.add(modelRef);
84
+ const fields = (modelRef as { [FIELD_META]?: Record<string, FieldMeta> })[FIELD_META] ?? {};
85
+ for (const field of Object.values(fields)) {
86
+ if (!field.isClass) continue;
87
+ if (field.isScalar) visit(field.modelRef);
88
+ else {
89
+ const refName = ConstantRegistry.getRefName(field.modelRef as never, { allowEmpty: true });
90
+ if (refName) dependencies.add(refName);
91
+ }
92
+ }
93
+ };
94
+ visit(mod.constant.full);
95
+ return dependencies;
96
+ };
97
+
71
98
  export const getModuleCascadeRefNames = (mod: DatabaseModule | ServiceModule) => {
72
99
  const dependencies = new Set<string>();
73
100
  if (!("constant" in mod)) return dependencies;
@@ -0,0 +1,48 @@
1
+ import { createPrivateKey, createPublicKey, generateKeyPairSync, type KeyObject } from "node:crypto";
2
+
3
+ export class Ed25519Key {
4
+ /** PEM (with `\n` escapes, as an env file holds one), a JWK, base64 SPKI DER, or a raw 32-byte base64url key. */
5
+ static parsePublic(value: string, name: string): KeyObject {
6
+ const trimmed = value.trim();
7
+ const key = (() => {
8
+ if (trimmed.startsWith("{")) return createPublicKey({ key: JSON.parse(trimmed) as JsonWebKey, format: "jwk" });
9
+ if (trimmed.includes("-----BEGIN")) return createPublicKey(trimmed.replace(/\\n/g, "\n"));
10
+ const raw = Buffer.from(trimmed, "base64url");
11
+ if (raw.length === 32)
12
+ return createPublicKey({ key: { kty: "OKP", crv: "Ed25519", x: raw.toString("base64url") }, format: "jwk" });
13
+ if (raw.length === 44) return createPublicKey({ key: raw, format: "der", type: "spki" });
14
+ throw new Error(`${name} must be a PEM, a JWK, base64 SPKI DER, or a raw 32-byte base64url key`);
15
+ })();
16
+ if (key.asymmetricKeyType !== "ed25519") throw new Error(`${name} must be an Ed25519 public key`);
17
+ return key;
18
+ }
19
+
20
+ /** PEM (with `\n` escapes), a JWK, or base64 PKCS#8 DER. */
21
+ static parsePrivate(value: string, name: string): KeyObject {
22
+ const trimmed = value.trim();
23
+ const key = (() => {
24
+ if (trimmed.startsWith("{")) return createPrivateKey({ key: JSON.parse(trimmed) as JsonWebKey, format: "jwk" });
25
+ if (trimmed.includes("-----BEGIN")) return createPrivateKey(trimmed.replace(/\\n/g, "\n"));
26
+ const raw = Buffer.from(trimmed, "base64");
27
+ if (raw.length === 48) return createPrivateKey({ key: raw, format: "der", type: "pkcs8" });
28
+ throw new Error(`${name} must be a PEM, a JWK, or base64 PKCS#8 DER private key`);
29
+ })();
30
+ if (key.asymmetricKeyType !== "ed25519") throw new Error(`${name} must be an Ed25519 private key`);
31
+ return key;
32
+ }
33
+
34
+ /** The private key as PKCS#8 PEM, and the public key as the raw 32 bytes in base64url, short enough for an env line. */
35
+ static generate() {
36
+ const { privateKey, publicKey } = generateKeyPairSync("ed25519");
37
+ return {
38
+ privatePem: privateKey.export({ format: "pem", type: "pkcs8" }).toString(),
39
+ publicKey: Ed25519Key.rawPublic(publicKey),
40
+ };
41
+ }
42
+
43
+ static rawPublic(key: KeyObject) {
44
+ const { x } = key.export({ format: "jwk" });
45
+ if (!x) throw new Error("not an Ed25519 public key");
46
+ return x;
47
+ }
48
+ }
package/server/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from "./console";
7
7
  export * from "./decorators";
8
8
  export * from "./devtools";
9
9
  export type { ChangeBatch, ChangeKind } from "./hmr/wsHub";
10
+ export * from "./machine";
10
11
  export * from "./nativeFile";
11
12
  export * from "./oauth";
12
13
  export * from "./processMetricsCollector";
@@ -0,0 +1,2 @@
1
+ export * from "./machineAccount";
2
+ export * from "./machineToken";
@@ -0,0 +1,27 @@
1
+ import { getEnv } from "akanjs/base";
2
+ import type { Middleware, SignalContext } from "akanjs/signal";
3
+ import { MachineToken } from "./machineToken";
4
+
5
+ interface AccountHolder {
6
+ headers?: Headers;
7
+ account?: Record<string, unknown>;
8
+ }
9
+
10
+ export class MachineAccount implements Middleware {
11
+ static readonly refName = "MachineAccount";
12
+
13
+ use() {
14
+ return async (context: SignalContext, next: () => Promise<unknown>) => {
15
+ const holder = (
16
+ context.transport === "http" ? context.getHttpContext().req : context.getWebSocketContext().ws.data
17
+ ) as AccountHolder;
18
+ const machine = MachineToken.fromAuthorization(holder.headers?.get("authorization"));
19
+ const { machine: _unverified, ...account } = holder.account ?? {};
20
+ if (machine) {
21
+ const { appName, environment } = getEnv();
22
+ holder.account = { ...(holder.account ? account : { appName, environment }), machine };
23
+ } else if (holder.account && "machine" in holder.account) holder.account = account;
24
+ return await next();
25
+ };
26
+ }
27
+ }
@@ -0,0 +1,151 @@
1
+ import { createPublicKey, type KeyObject, sign, verify } from "node:crypto";
2
+ import { getEnv } from "akanjs/base";
3
+ import { Logger } from "akanjs/common";
4
+ import type { MachineIdentity } from "akanjs/signal";
5
+ import { Ed25519Key } from "../ed25519Key";
6
+
7
+ export interface MachineTokenIssueOptions {
8
+ /** None by default: a machine's token lapses only when its version is bumped. */
9
+ expiresAt?: Date | { toDate(): Date };
10
+ /** The `<app>/<environment>` that accepts it; this server's own by default. */
11
+ audience?: string;
12
+ }
13
+
14
+ export type MachineTokenVerdict = { ok: true; identity: MachineIdentity } | { ok: false; reason: string };
15
+
16
+ interface KeyPair {
17
+ privateKey: KeyObject | null;
18
+ publicKey: KeyObject | null;
19
+ }
20
+
21
+ export class MachineToken {
22
+ static readonly type = "akan-machine+jwt";
23
+ static readonly #logger = new Logger("MachineToken");
24
+ static #keys: { source: string; pair: KeyPair } | null = null;
25
+ static #warnedKeyless = false;
26
+
27
+ static audience() {
28
+ const { appName, environment } = getEnv();
29
+ return `${appName}/${environment}`;
30
+ }
31
+
32
+ static #keyPair(): KeyPair {
33
+ const privateValue = process.env.AKAN_MACHINE_PRIVATE_KEY?.trim() ?? "";
34
+ const publicValue = process.env.AKAN_MACHINE_PUBLIC_KEY?.trim() ?? "";
35
+ const source = `${privateValue}\0${publicValue}`;
36
+ if (MachineToken.#keys?.source === source) return MachineToken.#keys.pair;
37
+ const privateKey = privateValue ? Ed25519Key.parsePrivate(privateValue, "AKAN_MACHINE_PRIVATE_KEY") : null;
38
+ const publicKey = publicValue
39
+ ? Ed25519Key.parsePublic(publicValue, "AKAN_MACHINE_PUBLIC_KEY")
40
+ : privateKey
41
+ ? createPublicKey(privateKey)
42
+ : null;
43
+ const pair = { privateKey, publicKey };
44
+ MachineToken.#keys = { source, pair };
45
+ return pair;
46
+ }
47
+
48
+ static canIssue() {
49
+ return !!MachineToken.#keyPair().privateKey;
50
+ }
51
+
52
+ static canVerify() {
53
+ return !!MachineToken.#keyPair().publicKey;
54
+ }
55
+
56
+ static issue(identity: MachineIdentity, { expiresAt, audience }: MachineTokenIssueOptions = {}): string {
57
+ const { privateKey } = MachineToken.#keyPair();
58
+ if (!privateKey) throw new Error("MachineToken.issue needs AKAN_MACHINE_PRIVATE_KEY on the server that issues it");
59
+ MachineToken.#assertIdentity(identity);
60
+ const iat = Math.floor(Date.now() / 1000);
61
+ const expiry = expiresAt ? ("toDate" in expiresAt ? expiresAt.toDate() : expiresAt) : null;
62
+ const claims = {
63
+ aud: audience ?? MachineToken.audience(),
64
+ sub: identity.id,
65
+ kind: identity.kind,
66
+ ver: identity.version,
67
+ iat,
68
+ ...(expiry ? { exp: Math.floor(expiry.getTime() / 1000) } : {}),
69
+ };
70
+ const signed = `${MachineToken.#encode({ alg: "EdDSA", typ: MachineToken.type })}.${MachineToken.#encode(claims)}`;
71
+ return `${signed}.${sign(null, Buffer.from(signed), privateKey).toString("base64url")}`;
72
+ }
73
+
74
+ /** By its header alone, so a staff session's bearer is told apart without a verification. */
75
+ static isMachineToken(token: string) {
76
+ const [headerPart = ""] = token.split(".");
77
+ return MachineToken.#decode(headerPart)?.typ === MachineToken.type;
78
+ }
79
+
80
+ static verify(token: string, nowSec = Math.floor(Date.now() / 1000)): MachineTokenVerdict {
81
+ const parts = token.split(".");
82
+ if (parts.length !== 3) return { ok: false, reason: "malformed token" };
83
+ const [headerPart = "", payloadPart = "", signaturePart = ""] = parts;
84
+ const header = MachineToken.#decode(headerPart);
85
+ if (header?.alg !== "EdDSA" || header.typ !== MachineToken.type)
86
+ return { ok: false, reason: `not an EdDSA ${MachineToken.type}` };
87
+ const { publicKey } = MachineToken.#keyPair();
88
+ if (!publicKey)
89
+ return { ok: false, reason: "no AKAN_MACHINE_PUBLIC_KEY or AKAN_MACHINE_PRIVATE_KEY to verify with" };
90
+ const signed = verify(
91
+ null,
92
+ Buffer.from(`${headerPart}.${payloadPart}`),
93
+ publicKey,
94
+ Buffer.from(signaturePart, "base64url"),
95
+ );
96
+ if (!signed) return { ok: false, reason: "bad signature" };
97
+ const claims = MachineToken.#decode(payloadPart);
98
+ if (!claims) return { ok: false, reason: "malformed claims" };
99
+ const { aud, sub, kind, ver, exp } = claims;
100
+ const audiences = Array.isArray(aud) ? aud : [aud];
101
+ if (!audiences.includes(MachineToken.audience())) return { ok: false, reason: "audience mismatch" };
102
+ if (exp !== undefined && (typeof exp !== "number" || exp <= nowSec)) return { ok: false, reason: "expired" };
103
+ const identity = { kind, id: sub, version: ver } as MachineIdentity;
104
+ if (!MachineToken.#isIdentity(identity)) return { ok: false, reason: "sub, kind and ver are required" };
105
+ return { ok: true, identity: { kind: identity.kind, id: identity.id, version: identity.version } };
106
+ }
107
+
108
+ /** The identity a bearer machine token proves, or `null`: absent, another kind of token, or one that fails. */
109
+ static fromAuthorization(authorization: string | null | undefined): MachineIdentity | null {
110
+ const token = /^Bearer +(\S+)$/i.exec(authorization ?? "")?.[1];
111
+ if (!token || !MachineToken.isMachineToken(token)) return null;
112
+ const verdict = MachineToken.verify(token);
113
+ if (verdict.ok) return verdict.identity;
114
+ if (!MachineToken.canVerify() && !MachineToken.#warnedKeyless) {
115
+ MachineToken.#warnedKeyless = true;
116
+ MachineToken.#logger.error(
117
+ "A machine token arrived, and this server has neither AKAN_MACHINE_PUBLIC_KEY nor AKAN_MACHINE_PRIVATE_KEY to verify it: every machine call is anonymous.",
118
+ );
119
+ } else MachineToken.#logger.verbose(`refused a machine token: ${verdict.reason}`);
120
+ return null;
121
+ }
122
+
123
+ static #isIdentity(identity: MachineIdentity) {
124
+ return (
125
+ typeof identity.kind === "string" &&
126
+ !!identity.kind &&
127
+ typeof identity.id === "string" &&
128
+ !!identity.id &&
129
+ Number.isSafeInteger(identity.version) &&
130
+ identity.version >= 0
131
+ );
132
+ }
133
+
134
+ static #assertIdentity(identity: MachineIdentity) {
135
+ if (!MachineToken.#isIdentity(identity))
136
+ throw new Error("A machine identity is { kind, id } as non-empty strings and version as an integer from 0");
137
+ }
138
+
139
+ static #encode(value: object) {
140
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
141
+ }
142
+
143
+ static #decode(part: string): Record<string, unknown> | null {
144
+ try {
145
+ const value = JSON.parse(Buffer.from(part, "base64url").toString("utf8")) as unknown;
146
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,256 @@
1
+ import { getEnv } from "akanjs/base";
2
+ import type { PromptContent, PromptMessage } from "../../signal/mcp/Msg";
3
+ import {
4
+ MCP_UI_EXTENSION,
5
+ MCP_UI_LEGACY_RESOURCE_URI,
6
+ MCP_UI_MIME_TYPE,
7
+ type McpResource,
8
+ type McpResourceContents,
9
+ type McpTool,
10
+ } from "../../signal/mcp/mcpProtocol";
11
+ import type { PagePromptEntry, PagePromptRecord, PagePromptRun } from "../../signal/mcp/pagePrompt";
12
+
13
+ /** The CSR shell a view boots from: one single-file HTML per basePath, read from the build that serves the web. */
14
+ export interface McpAppShellSource {
15
+ readonly basePaths: readonly string[];
16
+ /** `""` is the app without a basePath; `null` when this build has no single-file shell for it. */
17
+ read(basePath: string): Promise<string | null>;
18
+ }
19
+
20
+ interface McpAppSnapshotRecord {
21
+ key: string;
22
+ args: Record<string, unknown>;
23
+ value: unknown;
24
+ }
25
+
26
+ export class McpApps {
27
+ static readonly scheme = "ui://";
28
+ static readonly sessionPrefix = "ui.";
29
+
30
+ static readonly snapshotKey = "akanjs/snapshot";
31
+
32
+ static readonly rootSegment = "app";
33
+ static readonly #tokenClaims = new Set(["iss", "aud", "exp", "nbf", "iat", "jti", "client_id", "scope"]);
34
+
35
+ static readonly #runtime = `
36
+ (() => {
37
+ const memoryStorage = () => {
38
+ const items = new Map();
39
+ return {
40
+ get length() { return items.size; },
41
+ key: (idx) => [...items.keys()][idx] ?? null,
42
+ getItem: (key) => (items.has(key) ? items.get(key) : null),
43
+ setItem: (key, value) => { items.set(String(key), String(value)); },
44
+ removeItem: (key) => { items.delete(String(key)); },
45
+ clear: () => { items.clear(); },
46
+ };
47
+ };
48
+ for (const name of ["localStorage", "sessionStorage"])
49
+ Object.defineProperty(window, name, { value: memoryStorage(), configurable: true });
50
+ const jar = new Map();
51
+ Object.defineProperty(document, "cookie", {
52
+ configurable: true,
53
+ get: () => [...jar].map(([name, value]) => name + "=" + value).join("; "),
54
+ set: (raw) => {
55
+ const [pair, ...attrs] = String(raw).split(";");
56
+ const at = pair.indexOf("=");
57
+ if (at < 0) return;
58
+ const name = pair.slice(0, at).trim();
59
+ const expired = attrs.some((attr) => /^\\s*expires=/i.test(attr) && Date.parse(attr.split("=")[1]) < Date.now());
60
+ if (expired) jar.delete(name);
61
+ else jar.set(name, pair.slice(at + 1).trim());
62
+ },
63
+ });
64
+ const origin = window.__AKAN_EMBED__.origin;
65
+ if (!origin || document.baseURI.startsWith(origin + "/")) return;
66
+ const absolute = (url) => (url.startsWith("/") && !url.startsWith("//") ? origin + url : url);
67
+ const valueOf = (el, name, value) => {
68
+ if (typeof value !== "string") return value;
69
+ if (name === "src" || name === "poster" || (name === "href" && el.tagName === "LINK")) return absolute(value);
70
+ if (name !== "srcset") return value;
71
+ return value.split(",").map((part) => part.trim().replace(/^\\S+/, absolute)).join(", ");
72
+ };
73
+ const setAttribute = Element.prototype.setAttribute;
74
+ Element.prototype.setAttribute = function (name, value) {
75
+ return setAttribute.call(this, name, valueOf(this, String(name).toLowerCase(), value));
76
+ };
77
+ const fix = (el) => {
78
+ for (const name of ["src", "srcset", "poster", "href"]) {
79
+ const value = el.getAttribute(name);
80
+ if (value !== null && valueOf(el, name, value) !== value) setAttribute.call(el, name, valueOf(el, name, value));
81
+ }
82
+ };
83
+ new MutationObserver((records) => {
84
+ for (const record of records) {
85
+ if (record.type === "attributes") fix(record.target);
86
+ else for (const node of record.addedNodes) {
87
+ if (node.nodeType !== 1) continue;
88
+ fix(node);
89
+ for (const el of node.querySelectorAll("[src],[srcset],[poster],link[href]")) fix(el);
90
+ }
91
+ }
92
+ }).observe(document.documentElement, {
93
+ subtree: true,
94
+ childList: true,
95
+ attributes: true,
96
+ attributeFilter: ["src", "srcset", "poster", "href"],
97
+ });
98
+ })();
99
+ `;
100
+
101
+ readonly #shell: McpAppShellSource | null;
102
+
103
+ constructor(shell: McpAppShellSource | null) {
104
+ this.#shell = shell;
105
+ }
106
+
107
+ get rendersUi() {
108
+ return this.#shell !== null;
109
+ }
110
+
111
+ static declaresUi(capabilities: unknown) {
112
+ if (!capabilities || typeof capabilities !== "object") return false;
113
+ const extensions = (capabilities as { extensions?: unknown }).extensions;
114
+ if (!extensions || typeof extensions !== "object") return false;
115
+ const ui = (extensions as Record<string, { mimeTypes?: unknown } | undefined>)[MCP_UI_EXTENSION];
116
+ return Array.isArray(ui?.mimeTypes) && ui.mimeTypes.includes(MCP_UI_MIME_TYPE);
117
+ }
118
+
119
+ toolOf(entry: PagePromptEntry): McpTool {
120
+ const properties = Object.fromEntries(
121
+ entry.arguments.map((arg) => [
122
+ arg.name,
123
+ { type: "string", ...(arg.description ? { description: arg.description } : {}) },
124
+ ]),
125
+ );
126
+ const required = entry.arguments.filter((arg) => arg.required).map((arg) => arg.name);
127
+ return {
128
+ name: entry.name,
129
+ description: entry.description,
130
+ inputSchema: {
131
+ type: "object",
132
+ properties,
133
+ ...(required.length ? { required } : {}),
134
+ additionalProperties: false,
135
+ },
136
+ annotations: { readOnlyHint: true, openWorldHint: false },
137
+ ...(this.#shell ? { _meta: this.#toolMetaOf(this.uriOf(this.#basePathOf(entry.pattern))) } : {}),
138
+ };
139
+ }
140
+
141
+ #toolMetaOf(resourceUri: string) {
142
+ return { ui: { resourceUri }, [MCP_UI_LEGACY_RESOURCE_URI]: resourceUri };
143
+ }
144
+
145
+ resourcesOf(entries: PagePromptEntry[]): McpResource[] {
146
+ if (!this.#shell) return [];
147
+ const { appName } = getEnv();
148
+ const basePaths = [...new Set(entries.map((entry) => this.#basePathOf(entry.pattern)))];
149
+ return basePaths.map((basePath) => ({
150
+ uri: this.uriOf(basePath),
151
+ name: basePath || appName,
152
+ description: `The ${basePath || appName} screens, rendered in place of a tool result.`,
153
+ mimeType: MCP_UI_MIME_TYPE,
154
+ }));
155
+ }
156
+
157
+ uriOf(basePath: string) {
158
+ const root = `${McpApps.scheme}${getEnv().appName.toLowerCase()}/${McpApps.rootSegment}`;
159
+ return basePath ? `${root}/${basePath}` : root;
160
+ }
161
+
162
+ async read(uri: string, origin: string): Promise<McpResourceContents | null> {
163
+ const shell = this.#shell;
164
+ const basePath = shell ? this.#basePathOfUri(uri) : null;
165
+ if (!shell || basePath === null) return null;
166
+ const html = await shell.read(basePath);
167
+ if (!html) return null;
168
+ const appOrigin = McpApps.#originOf(origin);
169
+ return {
170
+ uri,
171
+ mimeType: MCP_UI_MIME_TYPE,
172
+ text: McpApps.#withHead(appOrigin ? McpApps.#absoluteCss(html, appOrigin) : html, appOrigin),
173
+ _meta: {
174
+ ui: {
175
+ prefersBorder: false,
176
+ ...(appOrigin ? { csp: { resourceDomains: [appOrigin], baseUriDomains: [appOrigin] } } : {}),
177
+ },
178
+ },
179
+ };
180
+ }
181
+
182
+ resultOf(
183
+ run: Extract<PagePromptRun, { ok: true }>,
184
+ messages: PromptMessage[],
185
+ webOrigin: string,
186
+ forView: { account: Record<string, unknown> | null } | null,
187
+ ) {
188
+ const url = new URL(run.url);
189
+ const href = `${url.pathname}${url.search}`;
190
+ const content: PromptContent[] = [
191
+ ...messages.map((message) => message.content),
192
+ { type: "text", text: `Open this screen in a browser: ${webOrigin}${href}` },
193
+ ];
194
+ if (!this.#shell || !forView) return { content, isError: false };
195
+ const _meta = { [McpApps.snapshotKey]: McpApps.#snapshotOf(run.records), "akanjs/account": forView.account };
196
+ return { content, structuredContent: { href }, _meta, isError: false };
197
+ }
198
+
199
+ static accountOf(req: Request): Record<string, unknown> | null {
200
+ const token = req.headers.get("authorization")?.replace(/^Bearer\s+/i, "");
201
+ const payload = token?.split(".")[1];
202
+ if (!payload) return null;
203
+ try {
204
+ const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8")) as Record<string, unknown>;
205
+ return Object.fromEntries(Object.entries(claims).filter(([key]) => !McpApps.#tokenClaims.has(key)));
206
+ } catch {
207
+ return null;
208
+ }
209
+ }
210
+
211
+ #basePathOf(pattern: string) {
212
+ const [, , segment] = pattern.split("/");
213
+ return segment && this.#shell?.basePaths.includes(segment) ? segment : "";
214
+ }
215
+
216
+ #basePathOfUri(uri: string) {
217
+ const match = /^ui:\/\/([^/]+)\/([^?#]*)$/i.exec(uri);
218
+ if (!match || match[1]?.toLowerCase() !== getEnv().appName.toLowerCase()) return null;
219
+ const [root, basePath, ...rest] = (match[2] ?? "").split("/");
220
+ if (root !== McpApps.rootSegment || rest.length) return null;
221
+ if (!basePath) return "";
222
+ return this.#shell?.basePaths.includes(basePath) ? basePath : null;
223
+ }
224
+
225
+ static #snapshotOf(records: PagePromptRecord[]): McpAppSnapshotRecord[] {
226
+ return records.flatMap(({ key, args, value }) => (value === undefined ? [] : [{ key, args, value }]));
227
+ }
228
+
229
+ static #originOf(origin: string) {
230
+ try {
231
+ const parsed = new URL(origin);
232
+ return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.origin : null;
233
+ } catch {
234
+ return null;
235
+ }
236
+ }
237
+
238
+ static #absoluteCss(html: string, origin: string) {
239
+ return html.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, (block) =>
240
+ block.replace(/url\((["']?)\/(?!\/)/g, `url($1${origin}/`),
241
+ );
242
+ }
243
+
244
+ static #withHead(html: string, origin: string | null) {
245
+ const embed = JSON.stringify({ kind: "mcp-app", ...(origin ? { origin } : {}) });
246
+ const preamble = `<script>\nwindow.__AKAN_EMBED__ = ${embed};${McpApps.#runtime}</script>`;
247
+ const inject = `${origin ? `<base href="${origin}/">` : ""}${preamble}`;
248
+ const head = /<head(\s[^>]*)?>/i.exec(html);
249
+ if (!head) return `${inject}${html}`;
250
+ const headEnd = head.index + head[0].length;
251
+ const scriptAt = html.indexOf("<script", headEnd);
252
+ const charset = /<meta\s[^>]*charset[^>]*>/i.exec(html.slice(headEnd, scriptAt < 0 ? undefined : scriptAt));
253
+ const at = charset ? headEnd + charset.index + charset[0].length : headEnd;
254
+ return `${html.slice(0, at)}${inject}${html.slice(at)}`;
255
+ }
256
+ }