akanjs 3.0.0-alpha.7 → 3.0.0-alpha.71

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 (719) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/base/baseEnv.ts +7 -4
  5. package/base/primitiveRegistry.ts +59 -12
  6. package/base/symbols.ts +4 -0
  7. package/client/capacitor.ts +34 -0
  8. package/client/clientRuntime.ts +5 -0
  9. package/client/csrTypes.ts +4 -3
  10. package/client/index.ts +1 -0
  11. package/client/locale.ts +2 -2
  12. package/client/router.ts +7 -7
  13. package/client/sharedContext.ts +31 -0
  14. package/client/translator.ts +2 -2
  15. package/client/types.ts +3 -3
  16. package/common/Logger.ts +280 -67
  17. package/common/clientAddress.ts +40 -0
  18. package/common/deepObjectify.ts +24 -9
  19. package/common/index.ts +42 -1
  20. package/common/logContext.ts +27 -0
  21. package/common/mcpExposure.ts +114 -0
  22. package/common/pathGetLoose.ts +31 -0
  23. package/common/pathSet.ts +17 -5
  24. package/common/plainFieldsOf.ts +13 -0
  25. package/common/routeConvention.ts +22 -12
  26. package/common/websocketBinaryFrame.ts +46 -0
  27. package/constant/crystalize.ts +94 -28
  28. package/constant/dateSlot.ts +62 -0
  29. package/constant/deserialize.ts +3 -2
  30. package/constant/fieldInfo.ts +43 -3
  31. package/constant/fieldQueryMeta.ts +35 -0
  32. package/constant/getDefault.ts +16 -11
  33. package/constant/hydrationPlan.ts +72 -0
  34. package/constant/immerify.ts +4 -6
  35. package/constant/index.ts +5 -0
  36. package/constant/labelOf.ts +26 -0
  37. package/constant/mask.ts +67 -0
  38. package/constant/purify.ts +17 -11
  39. package/constant/textFieldPaths.ts +13 -7
  40. package/constant/types.ts +19 -13
  41. package/constant/via.ts +24 -17
  42. package/dictionary/DictionaryLookup.ts +30 -0
  43. package/dictionary/agent.dictionary.ts +31 -0
  44. package/dictionary/agentTurn.dictionary.ts +29 -0
  45. package/dictionary/base.dictionary.ts +81 -0
  46. package/dictionary/dictInfo.ts +11 -2
  47. package/dictionary/dictionary.ts +9 -3
  48. package/dictionary/index.ts +1 -0
  49. package/dictionary/trans.ts +2 -2
  50. package/document/documentQuery.ts +1 -0
  51. package/document/filterMeta.ts +77 -12
  52. package/document/index.ts +1 -0
  53. package/document/noDocumentError.ts +12 -0
  54. package/document/types.ts +0 -1
  55. package/fetch/agentTurn.ts +19 -0
  56. package/fetch/client/fetchClient.ts +137 -37
  57. package/fetch/client/httpClient.ts +95 -20
  58. package/fetch/client/wsClient.ts +27 -12
  59. package/fetch/fetchType/appliedReturn.type.ts +65 -44
  60. package/fetch/fetchType/endpointFetch.type.ts +17 -8
  61. package/fetch/fetchType/sliceFetch.type.ts +27 -50
  62. package/fetch/index.ts +1 -0
  63. package/index.ts +80 -6
  64. package/local/apps/akan/akan-local.db +0 -0
  65. package/local/apps/akan/akan-local.db-shm +0 -0
  66. package/local/apps/akan/akan-local.db-wal +0 -0
  67. package/local/apps/akan/akan-local_solid.db +0 -0
  68. package/local/apps/akan/akan-local_solid.db-shm +0 -0
  69. package/local/apps/akan/akan-local_solid.db-wal +0 -0
  70. package/local/apps/serverLifecycle/serverLifecycle-local.db +0 -0
  71. package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
  72. package/local/apps/serverLifecycle/serverLifecycle-local.db-wal +0 -0
  73. package/local/apps/serverLifecycle/serverLifecycle-local_solid.db +0 -0
  74. package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
  75. package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-wal +0 -0
  76. package/package.json +27 -18
  77. package/server/akanApp.ts +278 -138
  78. package/server/akanAppHeaders.ts +16 -2
  79. package/server/akanOption.ts +63 -8
  80. package/server/akanServer.ts +387 -32
  81. package/server/animatedImage.ts +83 -0
  82. package/server/assetEncoding.ts +4 -24
  83. package/server/binaryPubsub.ts +55 -0
  84. package/server/console.ts +110 -101
  85. package/server/consoleEvaluator.ts +51 -0
  86. package/server/consolePasteFilter.ts +91 -0
  87. package/server/consoleSession.ts +242 -0
  88. package/server/contentEncoding.ts +83 -0
  89. package/server/devtools/signalSerializer.ts +2 -2
  90. package/server/devtools/types.ts +4 -4
  91. package/server/di/diLifecycle.ts +123 -13
  92. package/server/di/predefinedAdaptor.ts +6 -0
  93. package/server/di/utils.ts +40 -0
  94. package/server/imageOptimizer.ts +154 -57
  95. package/server/lifecycle/runtimeDir.ts +14 -0
  96. package/server/lifecycle/shutdownManager.ts +1 -1
  97. package/server/logging/childOutputReader.ts +137 -0
  98. package/server/logging/hubFileSink.ts +39 -0
  99. package/server/logging/logControlSocket.ts +173 -0
  100. package/server/logging/logForwarder.ts +131 -0
  101. package/server/logging/logHub.ts +0 -0
  102. package/server/logging/logQuery.ts +130 -0
  103. package/server/logging/logStdoutWriter.ts +39 -0
  104. package/server/logging/logStreamRoute.ts +87 -0
  105. package/server/logging/logTailClient.ts +161 -0
  106. package/server/mcp/McpAuth.ts +177 -0
  107. package/server/mcp/McpDispatcher.ts +289 -0
  108. package/server/mcp/McpEventStream.ts +24 -0
  109. package/server/mcp/McpExecutionContext.ts +105 -0
  110. package/server/mcp/McpRouter.ts +621 -0
  111. package/server/mcp/index.ts +5 -0
  112. package/server/processMetricsCollector.ts +1 -1
  113. package/server/resolver/database.resolver.ts +5 -4
  114. package/server/resolver/signal.resolver.ts +208 -86
  115. package/server/routeTreeBuilder.ts +10 -8
  116. package/server/routing/apiRouter.ts +44 -14
  117. package/server/routing/appWsData.ts +24 -2
  118. package/server/routing/eventStream.ts +93 -0
  119. package/server/routing/soloAppRoutes.ts +50 -0
  120. package/server/rscWorker.tsx +36 -22
  121. package/server/rscWorkerHost.ts +59 -8
  122. package/server/semaphore.ts +22 -0
  123. package/server/subRouteIndexDocument.tsx +61 -0
  124. package/server/systemPageDocument.tsx +3 -3
  125. package/server/systemPages.tsx +49 -16
  126. package/server/types.tsx +28 -0
  127. package/server/vendor/akanjs-fetch.ts +1 -0
  128. package/server/webRouter.ts +96 -39
  129. package/service/adapt.ts +4 -4
  130. package/service/agent.service.ts +81 -0
  131. package/service/base.service.ts +2 -1
  132. package/service/index.ts +1 -0
  133. package/service/injectInfo.ts +8 -3
  134. package/service/ipcTypes.ts +6 -0
  135. package/service/predefinedAdaptor/compress.adaptor.ts +5 -1
  136. package/service/predefinedAdaptor/database.adaptor.ts +68 -37
  137. package/service/predefinedAdaptor/deepseekLlm.ts +271 -0
  138. package/service/predefinedAdaptor/index.ts +3 -0
  139. package/service/predefinedAdaptor/insightQuery.ts +183 -0
  140. package/service/predefinedAdaptor/llm.adaptor.ts +115 -0
  141. package/service/predefinedAdaptor/logging.adaptor.ts +1 -1
  142. package/service/predefinedAdaptor/queue.adaptor.ts +5 -2
  143. package/service/predefinedAdaptor/role.adaptor.ts +2 -0
  144. package/service/predefinedAdaptor/schedule.adaptor.ts +12 -5
  145. package/service/predefinedAdaptor/solidPubsub.adaptor.ts +10 -6
  146. package/service/predefinedAdaptor/solidQueue.adaptor.ts +6 -2
  147. package/service/predefinedAdaptor/solidSqlite.ts +11 -9
  148. package/service/predefinedAdaptor/websocket.adaptor.ts +6 -1
  149. package/service/serve.ts +16 -9
  150. package/service/toolNames.ts +119 -0
  151. package/signal/agent/AgentCatalogue.ts +165 -0
  152. package/signal/agent/index.ts +1 -0
  153. package/signal/agent.signal.ts +38 -0
  154. package/signal/agentTurn.ts +8 -0
  155. package/signal/agentTurnStream.ts +60 -0
  156. package/signal/base.signal.ts +2 -1
  157. package/signal/endpointInfo.ts +38 -2
  158. package/signal/guard.ts +28 -1
  159. package/signal/guards.ts +49 -1
  160. package/signal/index.ts +6 -0
  161. package/signal/intercept.ts +4 -4
  162. package/signal/internalArg.ts +21 -4
  163. package/signal/mcp/McpDocument.ts +326 -0
  164. package/signal/mcp/McpProgress.ts +106 -0
  165. package/signal/mcp/McpUriTemplate.ts +80 -0
  166. package/signal/mcp/Msg.ts +368 -0
  167. package/signal/mcp/index.ts +5 -0
  168. package/signal/mcp/mcpProtocol.ts +120 -0
  169. package/signal/middleware.ts +3 -3
  170. package/signal/openapi/openapi.ts +36 -173
  171. package/signal/schema/JsonSchemaBuilder.ts +221 -0
  172. package/signal/schema/index.ts +1 -0
  173. package/signal/serializer/fetch.serializer.ts +51 -0
  174. package/signal/signalContext.ts +324 -76
  175. package/signal/slice.ts +62 -27
  176. package/signal/trace.ts +268 -9
  177. package/signal/types.ts +55 -4
  178. package/store/action.ts +34 -41
  179. package/store/actionTag.ts +33 -0
  180. package/store/agent/AgentBridge.ts +87 -0
  181. package/store/agent/AgentContext.ts +71 -0
  182. package/store/agent/AgentPrompts.ts +111 -0
  183. package/store/agent/ScreenReader.ts +349 -0
  184. package/store/agent/ScreenSettle.ts +45 -0
  185. package/store/agent/ScreenTarget.ts +135 -0
  186. package/store/agent/StoreCatalogue.ts +116 -0
  187. package/store/agent/StoreSurfaceSource.ts +225 -0
  188. package/store/agent/index.ts +13 -0
  189. package/store/agent/storeSurface.ts +28 -0
  190. package/store/agent/types.ts +16 -0
  191. package/store/agentic/AgentValue.ts +119 -0
  192. package/store/agentic/StExposeBuilder.ts +47 -0
  193. package/store/agentic/StExposeDraft.ts +24 -0
  194. package/store/agentic/StStateBuilder.ts +85 -0
  195. package/store/agentic/StStateDraft.ts +23 -0
  196. package/store/agentic/StToolBuilder.ts +279 -0
  197. package/store/agentic/StToolDraft.ts +22 -0
  198. package/store/agentic/attachAgentic.ts +33 -0
  199. package/store/agentic/formFields.ts +194 -0
  200. package/store/agentic/index.ts +12 -0
  201. package/store/agentic/useFieldTool.ts +183 -0
  202. package/store/agentic/useFormTools.ts +77 -0
  203. package/store/agentic/useRelationFieldTool.ts +113 -0
  204. package/store/baseSt.ts +1 -1
  205. package/store/databaseStateNames.ts +31 -0
  206. package/store/formSetterNames.ts +35 -0
  207. package/store/index.ts +8 -0
  208. package/store/rootStore.ts +3 -1
  209. package/store/sliceRole.ts +36 -0
  210. package/store/state.ts +2 -12
  211. package/store/store.ts +9 -0
  212. package/store/storeInstance.ts +185 -20
  213. package/store/storeRegistry.ts +39 -1
  214. package/store/types.ts +5 -0
  215. package/store/withSelector.ts +5 -4
  216. package/test/index.ts +1 -1
  217. package/test/registerDom.ts +4 -0
  218. package/test/sampleOf.ts +2 -0
  219. package/test/signalTestRuntime.ts +5 -5
  220. package/test/testServer.ts +14 -11
  221. package/types/base/baseEnv.d.ts +1 -1
  222. package/types/base/primitiveRegistry.d.ts +20 -8
  223. package/types/base/symbols.d.ts +4 -0
  224. package/types/client/capacitor.d.ts +39 -0
  225. package/types/client/clientRuntime.d.ts +7 -0
  226. package/types/client/index.d.ts +1 -0
  227. package/types/client/sharedContext.d.ts +21 -0
  228. package/types/client/types.d.ts +5 -3
  229. package/types/common/Logger.d.ts +88 -6
  230. package/types/common/clientAddress.d.ts +19 -0
  231. package/types/common/deepObjectify.d.ts +4 -2
  232. package/types/common/index.d.ts +8 -2
  233. package/types/common/logContext.d.ts +17 -0
  234. package/types/common/mcpExposure.d.ts +64 -0
  235. package/types/common/pathGetLoose.d.ts +8 -0
  236. package/types/common/plainFieldsOf.d.ts +6 -0
  237. package/types/common/routeConvention.d.ts +6 -0
  238. package/types/common/websocketBinaryFrame.d.ts +17 -0
  239. package/types/constant/constantRegistry.d.ts +2 -10
  240. package/types/constant/crystalize.d.ts +5 -0
  241. package/types/constant/dateSlot.d.ts +14 -0
  242. package/types/constant/fieldInfo.d.ts +20 -3
  243. package/types/constant/fieldQueryMeta.d.ts +17 -0
  244. package/types/constant/getDefault.d.ts +8 -0
  245. package/types/constant/hydrationPlan.d.ts +24 -0
  246. package/types/constant/index.d.ts +5 -0
  247. package/types/constant/labelOf.d.ts +7 -0
  248. package/types/constant/mask.d.ts +41 -0
  249. package/types/constant/purify.d.ts +2 -2
  250. package/types/constant/types.d.ts +5 -5
  251. package/types/dictionary/DictionaryLookup.d.ts +14 -0
  252. package/types/dictionary/agent.dictionary.d.ts +1 -0
  253. package/types/dictionary/agentTurn.dictionary.d.ts +1 -0
  254. package/types/dictionary/base.dictionary.d.ts +1 -1
  255. package/types/dictionary/dictInfo.d.ts +7 -1
  256. package/types/dictionary/dictionary.d.ts +11 -9
  257. package/types/dictionary/index.d.ts +1 -0
  258. package/types/document/filterMeta.d.ts +24 -4
  259. package/types/document/index.d.ts +1 -0
  260. package/types/document/noDocumentError.d.ts +12 -0
  261. package/types/document/types.d.ts +0 -1
  262. package/types/fetch/agentTurn.d.ts +475 -0
  263. package/types/fetch/client/fetchClient.d.ts +12 -0
  264. package/types/fetch/client/httpClient.d.ts +2 -1
  265. package/types/fetch/fetchType/appliedReturn.type.d.ts +37 -18
  266. package/types/fetch/fetchType/endpointFetch.type.d.ts +8 -5
  267. package/types/fetch/fetchType/sliceFetch.type.d.ts +11 -15
  268. package/types/fetch/index.d.ts +1 -0
  269. package/types/index.d.ts +74 -10
  270. package/types/server/akanApp.d.ts +12 -0
  271. package/types/server/akanAppHeaders.d.ts +7 -1
  272. package/types/server/akanOption.d.ts +36 -6
  273. package/types/server/akanServer.d.ts +67 -9
  274. package/types/server/animatedImage.d.ts +7 -0
  275. package/types/server/assetEncoding.d.ts +1 -1
  276. package/types/server/binaryPubsub.d.ts +18 -0
  277. package/types/server/console.d.ts +11 -1
  278. package/types/server/consoleEvaluator.d.ts +2 -0
  279. package/types/server/consolePasteFilter.d.ts +11 -0
  280. package/types/server/consoleSession.d.ts +22 -0
  281. package/types/server/contentEncoding.d.ts +28 -0
  282. package/types/server/devtools/types.d.ts +4 -4
  283. package/types/server/di/diLifecycle.d.ts +10 -2
  284. package/types/server/di/predefinedAdaptor.d.ts +4 -1
  285. package/types/server/di/utils.d.ts +16 -0
  286. package/types/server/lifecycle/runtimeDir.d.ts +5 -0
  287. package/types/server/logging/childOutputReader.d.ts +40 -0
  288. package/types/server/logging/hubFileSink.d.ts +18 -0
  289. package/types/server/logging/logControlSocket.d.ts +60 -0
  290. package/types/server/logging/logForwarder.d.ts +37 -0
  291. package/types/server/logging/logHub.d.ts +70 -0
  292. package/types/server/logging/logQuery.d.ts +31 -0
  293. package/types/server/logging/logStdoutWriter.d.ts +35 -0
  294. package/types/server/logging/logStreamRoute.d.ts +20 -0
  295. package/types/server/logging/logTailClient.d.ts +42 -0
  296. package/types/server/mcp/McpAuth.d.ts +62 -0
  297. package/types/server/mcp/McpDispatcher.d.ts +61 -0
  298. package/types/server/mcp/McpEventStream.d.ts +17 -0
  299. package/types/server/mcp/McpExecutionContext.d.ts +44 -0
  300. package/types/server/mcp/McpRouter.d.ts +65 -0
  301. package/types/server/mcp/index.d.ts +5 -0
  302. package/types/server/mcp.d.ts +1 -0
  303. package/types/server/resolver/signal.resolver.d.ts +7 -4
  304. package/types/server/routing/apiRouter.d.ts +2 -1
  305. package/types/server/routing/appWsData.d.ts +13 -1
  306. package/types/server/routing/eventStream.d.ts +23 -0
  307. package/types/server/routing/soloAppRoutes.d.ts +17 -0
  308. package/types/server/rscWorkerHost.d.ts +4 -1
  309. package/types/server/semaphore.d.ts +5 -0
  310. package/types/server/subRouteIndexDocument.d.ts +8 -0
  311. package/types/server/systemPageDocument.d.ts +1 -0
  312. package/types/server/systemPages.d.ts +5 -0
  313. package/types/server/types.d.ts +16 -0
  314. package/types/server/vendor/akanjs-fetch.d.ts +1 -0
  315. package/types/server/webRouter.d.ts +15 -4
  316. package/types/service/adapt.d.ts +2 -2
  317. package/types/service/agent.service.d.ts +32 -0
  318. package/types/service/base.service.d.ts +4 -0
  319. package/types/service/index.d.ts +1 -0
  320. package/types/service/injectInfo.d.ts +2 -2
  321. package/types/service/ipcTypes.d.ts +12 -0
  322. package/types/service/predefinedAdaptor/compress.adaptor.d.ts +1 -1
  323. package/types/service/predefinedAdaptor/database.adaptor.d.ts +9 -3
  324. package/types/service/predefinedAdaptor/deepseekLlm.d.ts +76 -0
  325. package/types/service/predefinedAdaptor/index.d.ts +3 -0
  326. package/types/service/predefinedAdaptor/insightQuery.d.ts +50 -0
  327. package/types/service/predefinedAdaptor/llm.adaptor.d.ts +105 -0
  328. package/types/service/predefinedAdaptor/queue.adaptor.d.ts +3 -2
  329. package/types/service/predefinedAdaptor/role.adaptor.d.ts +2 -0
  330. package/types/service/predefinedAdaptor/schedule.adaptor.d.ts +10 -6
  331. package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +6 -2
  332. package/types/service/predefinedAdaptor/solidSqlite.d.ts +2 -2
  333. package/types/service/serve.d.ts +3 -3
  334. package/types/service/toolNames.d.ts +35 -0
  335. package/types/signal/agent/AgentCatalogue.d.ts +100 -0
  336. package/types/signal/agent/index.d.ts +1 -0
  337. package/types/signal/agent.d.ts +1 -0
  338. package/types/signal/agent.signal.d.ts +24 -0
  339. package/types/signal/agentTurn.d.ts +9 -0
  340. package/types/signal/agentTurnStream.d.ts +24 -0
  341. package/types/signal/base.signal.d.ts +1 -1
  342. package/types/signal/endpointInfo.d.ts +21 -3
  343. package/types/signal/guard.d.ts +12 -0
  344. package/types/signal/guards.d.ts +23 -1
  345. package/types/signal/index.d.ts +6 -0
  346. package/types/signal/intercept.d.ts +2 -2
  347. package/types/signal/internalArg.d.ts +22 -6
  348. package/types/signal/mcp/McpDocument.d.ts +84 -0
  349. package/types/signal/mcp/McpProgress.d.ts +40 -0
  350. package/types/signal/mcp/McpUriTemplate.d.ts +25 -0
  351. package/types/signal/mcp/Msg.d.ts +143 -0
  352. package/types/signal/mcp/index.d.ts +5 -0
  353. package/types/signal/mcp/mcpProtocol.d.ts +107 -0
  354. package/types/signal/mcp.d.ts +1 -0
  355. package/types/signal/middleware.d.ts +7 -7
  356. package/types/signal/openapi/openapi.d.ts +3 -3
  357. package/types/signal/schema/JsonSchemaBuilder.d.ts +51 -0
  358. package/types/signal/schema/index.d.ts +1 -0
  359. package/types/signal/schema.d.ts +1 -0
  360. package/types/signal/signalContext.d.ts +67 -8
  361. package/types/signal/slice.d.ts +42 -15
  362. package/types/signal/trace.d.ts +52 -2
  363. package/types/signal/types.d.ts +53 -4
  364. package/types/store/action.d.ts +7 -5
  365. package/types/store/actionTag.d.ts +21 -0
  366. package/types/store/agent/AgentBridge.d.ts +37 -0
  367. package/types/store/agent/AgentContext.d.ts +16 -0
  368. package/types/store/agent/AgentPrompts.d.ts +31 -0
  369. package/types/store/agent/ScreenReader.d.ts +29 -0
  370. package/types/store/agent/ScreenSettle.d.ts +22 -0
  371. package/types/store/agent/ScreenTarget.d.ts +34 -0
  372. package/types/store/agent/StoreCatalogue.d.ts +26 -0
  373. package/types/store/agent/StoreSurfaceSource.d.ts +30 -0
  374. package/types/store/agent/index.d.ts +11 -0
  375. package/types/store/agent/storeSurface.d.ts +14 -0
  376. package/types/store/agent/types.d.ts +15 -0
  377. package/types/store/agent.d.ts +1 -0
  378. package/types/store/agentic/AgentValue.d.ts +43 -0
  379. package/types/store/agentic/StExposeBuilder.d.ts +21 -0
  380. package/types/store/agentic/StExposeDraft.d.ts +13 -0
  381. package/types/store/agentic/StStateBuilder.d.ts +21 -0
  382. package/types/store/agentic/StStateDraft.d.ts +12 -0
  383. package/types/store/agentic/StToolBuilder.d.ts +56 -0
  384. package/types/store/agentic/StToolDraft.d.ts +13 -0
  385. package/types/store/agentic/attachAgentic.d.ts +25 -0
  386. package/types/store/agentic/formFields.d.ts +53 -0
  387. package/types/store/agentic/index.d.ts +12 -0
  388. package/types/store/agentic/useFieldTool.d.ts +22 -0
  389. package/types/store/agentic/useFormTools.d.ts +19 -0
  390. package/types/store/agentic/useRelationFieldTool.d.ts +27 -0
  391. package/types/store/agentic.d.ts +1 -0
  392. package/types/store/baseSt.d.ts +35 -39
  393. package/types/store/databaseStateNames.d.ts +25 -0
  394. package/types/store/formSetterNames.d.ts +30 -0
  395. package/types/store/index.d.ts +7 -0
  396. package/types/store/rootStore.d.ts +5 -1
  397. package/types/store/sliceRole.d.ts +25 -0
  398. package/types/store/store.d.ts +5 -1
  399. package/types/store/storeInstance.d.ts +33 -1
  400. package/types/store/storeRegistry.d.ts +5 -0
  401. package/types/store/types.d.ts +4 -0
  402. package/types/store/withSelector.d.ts +5 -4
  403. package/types/test/index.d.ts +1 -1
  404. package/types/test/registerDom.d.ts +1 -0
  405. package/types/test/signalTestRuntime.d.ts +2 -3
  406. package/types/test/testServer.d.ts +6 -4
  407. package/types/ui/Agent/Approval.d.ts +8 -0
  408. package/types/ui/Agent/Attach.d.ts +16 -0
  409. package/types/ui/Agent/Bubble.d.ts +26 -0
  410. package/types/ui/Agent/Chat.d.ts +91 -0
  411. package/types/ui/Agent/ChatCommands.d.ts +34 -0
  412. package/types/ui/Agent/Composer.d.ts +23 -0
  413. package/types/ui/Agent/Context.d.ts +13 -0
  414. package/types/ui/Agent/Dock.d.ts +17 -0
  415. package/types/ui/Agent/Guide.d.ts +9 -0
  416. package/types/ui/Agent/History.d.ts +26 -0
  417. package/types/ui/Agent/Launcher.d.ts +16 -0
  418. package/types/ui/Agent/Markdown.d.ts +19 -0
  419. package/types/ui/Agent/Menu.d.ts +18 -0
  420. package/types/ui/Agent/Mic.d.ts +8 -0
  421. package/types/ui/Agent/Question.d.ts +14 -0
  422. package/types/ui/Agent/Section.d.ts +10 -0
  423. package/types/ui/Agent/Skip.d.ts +22 -0
  424. package/types/ui/Agent/StateKey.d.ts +16 -0
  425. package/types/ui/Agent/Tool.d.ts +15 -0
  426. package/types/ui/Agent/Transcript.d.ts +13 -0
  427. package/types/ui/Agent/Zone.d.ts +48 -0
  428. package/types/ui/Agent/agentSessionOf.d.ts +27 -0
  429. package/types/ui/Agent/attachment.d.ts +34 -0
  430. package/types/ui/Agent/fetchRunner.d.ts +12 -0
  431. package/types/ui/Agent/index.d.ts +19 -0
  432. package/types/ui/Agent/index_.d.ts +1 -0
  433. package/types/ui/Agent/markdownBlocks.d.ts +35 -0
  434. package/types/ui/Agent/markdownSpans.d.ts +2 -0
  435. package/types/ui/Agent/markdownTable.d.ts +16 -0
  436. package/types/ui/Agent/sessionHistory.d.ts +14 -0
  437. package/types/ui/Agent/sessionView.d.ts +15 -0
  438. package/types/ui/Agent/tokenCount.d.ts +6 -0
  439. package/types/ui/Agent/useChatAttachments.d.ts +24 -0
  440. package/types/ui/Agent/useChatVoice.d.ts +30 -0
  441. package/types/ui/Agent/useDraftRecall.d.ts +14 -0
  442. package/types/ui/Agent/useKeyboardInset.d.ts +8 -0
  443. package/types/ui/Agent/useSlashMenu.d.ts +24 -0
  444. package/types/ui/Agent/voice.d.ts +71 -0
  445. package/types/ui/Agent.d.ts +1 -0
  446. package/types/ui/Badge.d.ts +1 -1
  447. package/types/ui/Button.d.ts +1 -1
  448. package/types/ui/Constant/Graph.d.ts +10 -0
  449. package/types/ui/Constant/index.d.ts +2 -1
  450. package/types/ui/Constant/schemaGraph.d.ts +39 -0
  451. package/types/ui/Data/Dashboard.d.ts +16 -3
  452. package/types/ui/Data/Insight.d.ts +1 -1
  453. package/types/ui/Data/ListContainer.d.ts +15 -7
  454. package/types/ui/Data/QueryMaker.d.ts +45 -3
  455. package/types/ui/Data/RefPicker.d.ts +16 -0
  456. package/types/ui/Data/dataExport.d.ts +5 -0
  457. package/types/ui/Data/dataText.d.ts +6 -0
  458. package/types/ui/Data/index.d.ts +2 -0
  459. package/types/ui/Data/index_.d.ts +1 -0
  460. package/types/ui/DatePicker.d.ts +15 -11
  461. package/types/ui/Dialog/LegacyModal.d.ts +13 -0
  462. package/types/ui/Dialog/Provider.d.ts +3 -1
  463. package/types/ui/Dialog/context.d.ts +9 -1
  464. package/types/ui/Dialog/index.d.ts +1 -0
  465. package/types/ui/DraggableList.d.ts +1 -1
  466. package/types/ui/Dropdown.d.ts +5 -1
  467. package/types/ui/Field.d.ts +13 -12
  468. package/types/ui/Layout/BottomTab.d.ts +3 -2
  469. package/types/ui/LegacyModal.d.ts +24 -0
  470. package/types/ui/Loading/ProgressBar.d.ts +4 -0
  471. package/types/ui/Loading/Spin.d.ts +2 -1
  472. package/types/ui/Model/AdminPanel.d.ts +15 -3
  473. package/types/ui/Model/EditModal.d.ts +1 -1
  474. package/types/ui/Model/New.d.ts +3 -1
  475. package/types/ui/Model/NewWrapper.d.ts +2 -0
  476. package/types/ui/Model/NewWrapper_Client.d.ts +2 -1
  477. package/types/ui/Pagination.d.ts +2 -2
  478. package/types/ui/Radio.d.ts +1 -1
  479. package/types/ui/RecentTime.d.ts +18 -1
  480. package/types/ui/Reference/Collapse.d.ts +11 -0
  481. package/types/ui/Reference/Panel.d.ts +29 -0
  482. package/types/ui/Reference/Segmented.d.ts +17 -0
  483. package/types/ui/Reference/Summary.d.ts +13 -0
  484. package/types/ui/Reference/Toolbar.d.ts +21 -0
  485. package/types/ui/Reference/dictText.d.ts +6 -0
  486. package/types/ui/Reference/index.d.ts +7 -0
  487. package/types/ui/Reference/style.d.ts +20 -0
  488. package/types/ui/Reference.d.ts +1 -0
  489. package/types/ui/ScreenNavigator.d.ts +3 -1
  490. package/types/ui/Signal/Arg.d.ts +7 -3
  491. package/types/ui/Signal/Doc.d.ts +4 -6
  492. package/types/ui/Signal/Object.d.ts +2 -1
  493. package/types/ui/Signal/RestApi.d.ts +2 -1
  494. package/types/ui/Signal/WebSocket.d.ts +2 -1
  495. package/types/ui/Signal/endpointEntries.d.ts +20 -0
  496. package/types/ui/Signal/style.d.ts +9 -8
  497. package/types/ui/System/Client.d.ts +1 -2
  498. package/types/ui/System/Common.d.ts +3 -4
  499. package/types/ui/System/Messages.d.ts +1 -1
  500. package/types/ui/Tab/Provider.d.ts +3 -1
  501. package/types/ui/Tab/context.d.ts +4 -2
  502. package/types/ui/ToggleSelect.d.ts +1 -0
  503. package/types/ui/UiOverride/context.d.ts +18 -1
  504. package/types/ui/agentAttrs.d.ts +14 -0
  505. package/types/ui/index.d.ts +22 -1
  506. package/types/ui/overlayLayer.d.ts +13 -0
  507. package/types/ui/overlayPosition.d.ts +28 -0
  508. package/types/ui/recipe/badgeRecipe.d.ts +2 -2
  509. package/types/ui/recipe/buttonRecipe.d.ts +2 -2
  510. package/types/vendor/use-agentic/AgentAbort.d.ts +17 -0
  511. package/types/vendor/use-agentic/AgentProgress.d.ts +27 -0
  512. package/types/vendor/use-agentic/AgentProvider.d.ts +17 -0
  513. package/types/vendor/use-agentic/AgentScope.d.ts +9 -0
  514. package/types/vendor/use-agentic/AgentSession.d.ts +163 -0
  515. package/types/vendor/use-agentic/Agentic.d.ts +21 -0
  516. package/types/vendor/use-agentic/AgenticSurface.d.ts +58 -0
  517. package/types/vendor/use-agentic/Compaction.d.ts +48 -0
  518. package/types/vendor/use-agentic/ToolOutput.d.ts +21 -0
  519. package/types/vendor/use-agentic/Transcript.d.ts +18 -0
  520. package/types/vendor/use-agentic/httpRunner.d.ts +15 -0
  521. package/types/vendor/use-agentic/index.d.ts +19 -0
  522. package/types/vendor/use-agentic/sharedContext.d.ts +15 -0
  523. package/types/vendor/use-agentic/surfaceContext.d.ts +5 -0
  524. package/types/vendor/use-agentic/types.d.ts +182 -0
  525. package/types/vendor/use-agentic/useAgent.d.ts +4 -0
  526. package/types/vendor/use-agentic/useAgentGuide.d.ts +6 -0
  527. package/types/vendor/use-agentic/useAgentResource.d.ts +12 -0
  528. package/types/vendor/use-agentic/useAgentState.d.ts +16 -0
  529. package/types/vendor/use-agentic/useAgentTool.d.ts +16 -0
  530. package/types/vendor/use-agentic.d.ts +1 -0
  531. package/types/webkit/index.d.ts +4 -0
  532. package/types/webkit/useBodyScrollLock.d.ts +2 -0
  533. package/types/webkit/usePageTool.d.ts +19 -0
  534. package/types/webkit/useScreenScope.d.ts +25 -0
  535. package/ui/Agent/Approval.tsx +32 -0
  536. package/ui/Agent/Attach.tsx +77 -0
  537. package/ui/Agent/Bubble.tsx +192 -0
  538. package/ui/Agent/Chat.tsx +525 -0
  539. package/ui/Agent/ChatCommands.ts +141 -0
  540. package/ui/Agent/Composer.tsx +94 -0
  541. package/ui/Agent/Context.tsx +49 -0
  542. package/ui/Agent/Dock.tsx +77 -0
  543. package/ui/Agent/Guide.tsx +16 -0
  544. package/ui/Agent/History.tsx +49 -0
  545. package/ui/Agent/Launcher.tsx +60 -0
  546. package/ui/Agent/Markdown.tsx +142 -0
  547. package/ui/Agent/Menu.tsx +55 -0
  548. package/ui/Agent/Mic.tsx +27 -0
  549. package/ui/Agent/Question.tsx +63 -0
  550. package/ui/Agent/Section.tsx +24 -0
  551. package/ui/Agent/Skip.tsx +30 -0
  552. package/ui/Agent/StateKey.tsx +46 -0
  553. package/ui/Agent/Tool.tsx +59 -0
  554. package/ui/Agent/Transcript.tsx +33 -0
  555. package/ui/Agent/Zone.tsx +116 -0
  556. package/ui/Agent/agentSessionOf.ts +60 -0
  557. package/ui/Agent/attachment.ts +73 -0
  558. package/ui/Agent/fetchRunner.ts +44 -0
  559. package/ui/Agent/index.ts +27 -0
  560. package/ui/Agent/index_.tsx +4 -0
  561. package/ui/Agent/markdownBlocks.ts +137 -0
  562. package/ui/Agent/markdownSpans.tsx +49 -0
  563. package/ui/Agent/markdownTable.ts +54 -0
  564. package/ui/Agent/sessionHistory.ts +62 -0
  565. package/ui/Agent/sessionView.ts +38 -0
  566. package/ui/Agent/tokenCount.ts +10 -0
  567. package/ui/Agent/useChatAttachments.ts +84 -0
  568. package/ui/Agent/useChatVoice.ts +129 -0
  569. package/ui/Agent/useDraftRecall.ts +42 -0
  570. package/ui/Agent/useKeyboardInset.ts +26 -0
  571. package/ui/Agent/useSlashMenu.ts +58 -0
  572. package/ui/Agent/voice.ts +177 -0
  573. package/ui/Badge.tsx +1 -1
  574. package/ui/BottomSheet.tsx +32 -36
  575. package/ui/Button.tsx +3 -1
  576. package/ui/Clipboard.tsx +3 -6
  577. package/ui/Constant/Doc.tsx +265 -254
  578. package/ui/Constant/Graph.tsx +151 -0
  579. package/ui/Constant/index.ts +3 -2
  580. package/ui/Constant/schemaGraph.ts +232 -0
  581. package/ui/Data/CardList.tsx +14 -3
  582. package/ui/Data/Dashboard.tsx +70 -52
  583. package/ui/Data/Insight.tsx +18 -12
  584. package/ui/Data/Item.tsx +42 -56
  585. package/ui/Data/ListContainer.tsx +246 -132
  586. package/ui/Data/Pagination.tsx +10 -3
  587. package/ui/Data/QueryMaker.tsx +160 -11
  588. package/ui/Data/RefPicker.tsx +202 -0
  589. package/ui/Data/TableList.tsx +22 -13
  590. package/ui/Data/dataExport.ts +54 -0
  591. package/ui/Data/dataText.ts +22 -0
  592. package/ui/Data/index.ts +13 -1
  593. package/ui/Data/index_.tsx +1 -0
  594. package/ui/DatePicker.tsx +110 -128
  595. package/ui/Dialog/Close.tsx +3 -7
  596. package/ui/Dialog/LegacyModal.tsx +241 -0
  597. package/ui/Dialog/Modal.tsx +62 -195
  598. package/ui/Dialog/Provider.tsx +47 -3
  599. package/ui/Dialog/Trigger.tsx +3 -7
  600. package/ui/Dialog/context.ts +14 -2
  601. package/ui/Dialog/index.tsx +2 -0
  602. package/ui/DragAction.tsx +3 -2
  603. package/ui/DraggableList.tsx +9 -4
  604. package/ui/Dropdown.tsx +89 -27
  605. package/ui/Empty.tsx +6 -4
  606. package/ui/Field.tsx +128 -58
  607. package/ui/Input.tsx +27 -5
  608. package/ui/KeyboardAvoiding.tsx +2 -2
  609. package/ui/Layout/BottomTab.tsx +32 -37
  610. package/ui/Layout/Sider.tsx +37 -14
  611. package/ui/LegacyModal.tsx +53 -0
  612. package/ui/Link/CsrLink.tsx +1 -1
  613. package/ui/Load/Pagination.tsx +15 -8
  614. package/ui/Load/Units.tsx +30 -7
  615. package/ui/Load/View.tsx +10 -3
  616. package/ui/Loading/Area.tsx +7 -6
  617. package/ui/Loading/Button.tsx +7 -9
  618. package/ui/Loading/Input.tsx +7 -9
  619. package/ui/Loading/ProgressBar.tsx +16 -7
  620. package/ui/Loading/Skeleton.tsx +8 -13
  621. package/ui/Loading/Spin.tsx +20 -14
  622. package/ui/Menu.tsx +1 -1
  623. package/ui/Model/AdminPanel.tsx +52 -12
  624. package/ui/Model/EditModal.tsx +28 -6
  625. package/ui/Model/EditWrapper.tsx +16 -4
  626. package/ui/Model/New.tsx +4 -0
  627. package/ui/Model/NewWrapper.tsx +2 -0
  628. package/ui/Model/NewWrapper_Client.tsx +20 -6
  629. package/ui/Model/Remove.tsx +18 -6
  630. package/ui/Model/RemoveWrapper.tsx +12 -2
  631. package/ui/Model/SureToRemove.tsx +18 -6
  632. package/ui/Model/ViewEditModal.tsx +64 -15
  633. package/ui/Model/ViewModal.tsx +25 -11
  634. package/ui/Model/ViewWrapper.tsx +15 -4
  635. package/ui/Model/index_.tsx +1 -1
  636. package/ui/Pagination.tsx +51 -66
  637. package/ui/Popconfirm.tsx +88 -78
  638. package/ui/Radio.tsx +53 -24
  639. package/ui/RecentTime.tsx +83 -35
  640. package/ui/Reference/Collapse.tsx +26 -0
  641. package/ui/Reference/Panel.tsx +79 -0
  642. package/ui/Reference/Segmented.tsx +41 -0
  643. package/ui/Reference/Summary.tsx +25 -0
  644. package/ui/Reference/Toolbar.tsx +44 -0
  645. package/ui/Reference/dictText.ts +9 -0
  646. package/ui/Reference/index.ts +7 -0
  647. package/ui/Reference/style.ts +45 -0
  648. package/ui/ScreenNavigator.tsx +17 -2
  649. package/ui/Select.tsx +96 -57
  650. package/ui/Signal/Arg.tsx +83 -68
  651. package/ui/Signal/Doc.tsx +163 -129
  652. package/ui/Signal/Listener.tsx +40 -29
  653. package/ui/Signal/Message.tsx +87 -135
  654. package/ui/Signal/Object.tsx +79 -57
  655. package/ui/Signal/PubSub.tsx +72 -113
  656. package/ui/Signal/Request.tsx +2 -22
  657. package/ui/Signal/Response.tsx +19 -45
  658. package/ui/Signal/RestApi.tsx +170 -200
  659. package/ui/Signal/WebSocket.tsx +29 -26
  660. package/ui/Signal/endpointEntries.ts +59 -0
  661. package/ui/Signal/makeExample.ts +10 -3
  662. package/ui/Signal/style.ts +34 -29
  663. package/ui/Switch.tsx +4 -0
  664. package/ui/System/CSR.tsx +1 -1
  665. package/ui/System/Client.tsx +31 -15
  666. package/ui/System/Common.tsx +3 -4
  667. package/ui/System/DevModeToggle.tsx +1 -1
  668. package/ui/System/Gtag.tsx +2 -2
  669. package/ui/System/Messages.tsx +52 -31
  670. package/ui/System/Reconnect.tsx +22 -56
  671. package/ui/System/SelectLanguage.tsx +24 -15
  672. package/ui/System/ThemeToggle.tsx +25 -13
  673. package/ui/Tab/Menu.tsx +24 -12
  674. package/ui/Tab/Menus.tsx +6 -3
  675. package/ui/Tab/Provider.tsx +30 -3
  676. package/ui/Tab/context.ts +8 -4
  677. package/ui/Table.tsx +66 -60
  678. package/ui/ToggleSelect.tsx +8 -7
  679. package/ui/Tooltip.tsx +15 -1
  680. package/ui/UiOverride/context.ts +21 -2
  681. package/ui/Unauthorized.tsx +6 -4
  682. package/ui/agentAttrs.ts +19 -0
  683. package/ui/index.ts +49 -1
  684. package/ui/overlayLayer.ts +12 -2
  685. package/ui/overlayPosition.ts +104 -0
  686. package/ui/recipe/buttonRecipe.ts +2 -2
  687. package/ui/styles.css +31 -0
  688. package/vendor/use-agentic/AgentAbort.ts +29 -0
  689. package/vendor/use-agentic/AgentProgress.ts +40 -0
  690. package/vendor/use-agentic/AgentProvider.tsx +39 -0
  691. package/vendor/use-agentic/AgentScope.tsx +20 -0
  692. package/vendor/use-agentic/AgentSession.ts +750 -0
  693. package/vendor/use-agentic/Agentic.tsx +44 -0
  694. package/vendor/use-agentic/AgenticSurface.ts +333 -0
  695. package/vendor/use-agentic/Compaction.ts +120 -0
  696. package/vendor/use-agentic/ToolOutput.ts +60 -0
  697. package/vendor/use-agentic/Transcript.ts +50 -0
  698. package/vendor/use-agentic/WIRE.md +86 -0
  699. package/vendor/use-agentic/httpRunner.ts +131 -0
  700. package/vendor/use-agentic/index.ts +20 -0
  701. package/vendor/use-agentic/sharedContext.ts +25 -0
  702. package/vendor/use-agentic/surfaceContext.ts +10 -0
  703. package/vendor/use-agentic/types.ts +192 -0
  704. package/vendor/use-agentic/useAgent.ts +18 -0
  705. package/vendor/use-agentic/useAgentGuide.ts +16 -0
  706. package/vendor/use-agentic/useAgentResource.ts +36 -0
  707. package/vendor/use-agentic/useAgentState.ts +64 -0
  708. package/vendor/use-agentic/useAgentTool.ts +55 -0
  709. package/webkit/bootCsr.tsx +10 -8
  710. package/webkit/index.ts +4 -0
  711. package/webkit/lazy.tsx +5 -1
  712. package/webkit/useBodyScrollLock.tsx +29 -0
  713. package/webkit/useFrameRuntime.ts +1 -1
  714. package/webkit/usePageTool.tsx +37 -0
  715. package/webkit/useScreenScope.tsx +60 -0
  716. package/types/ui/Constant/Mermaid.d.ts +0 -8
  717. package/types/ui/Signal/Collapse.d.ts +0 -12
  718. package/ui/Constant/Mermaid.tsx +0 -149
  719. package/ui/Signal/Collapse.tsx +0 -29
package/server/akanApp.ts CHANGED
@@ -2,15 +2,23 @@
2
2
 
3
3
  import { mkdir, readdir, rm } from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { Logger } from "akanjs/common";
5
+ import { Logger, logSeverity } from "akanjs/common";
6
6
  import type { AkanChildRole, AkanChildStatus, AkanIpcMessage, AkanMetricsReport, AkanUpstream } from "akanjs/service";
7
- import { isTraceEnabled } from "akanjs/signal";
7
+ import { isTraceEnabled } from "../signal/trace";
8
8
  import { makeAkanChildProxyHeaders } from "./akanAppHeaders";
9
9
  import type { BuilderCsrReq, BuilderCsrRes, BuilderMessage, BuilderReq, BuilderRes } from "./artifact";
10
10
  import { resolveEncodedSidecar } from "./assetEncoding";
11
+ import { compressResponse } from "./contentEncoding";
11
12
  import { isPortInUseError } from "./lifecycle/portInUse";
13
+ import { resolveRuntimeDir } from "./lifecycle/runtimeDir";
14
+ import { ChildOutputReader } from "./logging/childOutputReader";
15
+ import { HubFileSink } from "./logging/hubFileSink";
16
+ import { LogControlSocket } from "./logging/logControlSocket";
17
+ import { LogHub } from "./logging/logHub";
18
+ import { LogStreamRoute } from "./logging/logStreamRoute";
12
19
  import { RotatingLogWriter } from "./logging/rotatingLogWriter";
13
20
  import { ProcessMetricsCollector } from "./processMetricsCollector";
21
+ import { getWebConfigFromEnv } from "./types";
14
22
 
15
23
  interface ChildState {
16
24
  idx: number;
@@ -35,9 +43,13 @@ interface ChildState {
35
43
  lastErrorMessage?: string;
36
44
  }
37
45
 
46
+ /** The shape `#startSolo` needs off `server.ts`; the module itself is loaded by path, so it has no type here. */
47
+ interface SoloServer {
48
+ start: (options: { listen: boolean }) => Promise<unknown>;
49
+ }
50
+
38
51
  interface GatewayWsData {
39
52
  childIdx: number;
40
- socketId: string;
41
53
  upstream: WebSocket;
42
54
  }
43
55
 
@@ -46,6 +58,19 @@ type GatewayUpstream = {
46
58
  ws?: Extract<AkanUpstream, { type: "tcp" }>;
47
59
  };
48
60
 
61
+ /**
62
+ * Received-only close codes cannot be sent in a close frame. The gateway deliberately normalizes
63
+ * every unsendable code, including semantically distinct 1005 and 1006 events, to 1001 in both
64
+ * relay directions. In particular, Bun's global client `WebSocket.close()` throws an
65
+ * InvalidAccessError for these codes at the client-to-upstream relay; normalization at the
66
+ * upstream-to-client `Bun.ServerWebSocket.close()` relay is defensive and keeps behavior symmetric.
67
+ */
68
+ const relayableCloseCode = (code: number): number => {
69
+ if (code >= 3000 && code <= 4999) return code;
70
+ if (code >= 1000 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006) return code;
71
+ return 1001;
72
+ };
73
+
49
74
  /** Options for the Akan gateway that launches child server replicas and listens for traffic. */
50
75
  export interface AkanAppOptions {
51
76
  replica?: number | string;
@@ -54,6 +79,18 @@ export interface AkanAppOptions {
54
79
  port?: number;
55
80
  wsBasePort?: number;
56
81
  openapi?: boolean;
82
+ /**
83
+ * Boot only these modules and the ones they reach, in every child. Omitted or empty mounts every enabled
84
+ * module. Handed down as `AKAN_MODULES`, since each replica builds its own container.
85
+ */
86
+ modules?: string[];
87
+ /**
88
+ * Run the one replica in this process instead of spawning it — see `#startSolo`. Defaults on for a single
89
+ * traffic replica the environment configured, and off whenever `replica` is passed here, because code that
90
+ * states a topology is asking for the gateway that serves it. `AKAN_SOLO=false` turns it off; like
91
+ * `AKAN_SSR`, the env can only narrow, so it cannot force a gateway's replicas into one process.
92
+ */
93
+ solo?: boolean;
57
94
  }
58
95
 
59
96
  interface AkanReplicaConfig {
@@ -76,6 +113,9 @@ export class AkanApp {
76
113
  /** Hosted by `akan start`: crash loops should yield to the dev host, which restarts on file edits. */
77
114
  readonly #devHosted = process.env.AKAN_COMMAND_TYPE === "start";
78
115
  readonly #healthTimeoutMs = AkanApp.#parseHealthTimeoutMs();
116
+ readonly #upstreamWaitMs = AkanApp.#parseUpstreamWaitMs();
117
+ /** Child stderr is bundler/runtime noise the gateway cannot act on; it still reaches the rotating log file. */
118
+ readonly #printChildStderr = process.env.AKAN_CHILD_STDERR === "1";
79
119
  readonly #serverPath: string;
80
120
  readonly #artifactDir: string;
81
121
  readonly #replica: AkanReplicaConfig;
@@ -84,6 +124,10 @@ export class AkanApp {
84
124
  readonly #port: number;
85
125
  readonly #wsBasePort: number;
86
126
  readonly #openapi?: boolean;
127
+ /** The gateway hands `/_akan/client|styles|fonts` straight off disk, so it needs the same answer its children do. */
128
+ readonly #web = getWebConfigFromEnv();
129
+ readonly #modules: string[];
130
+ readonly #solo: boolean;
87
131
  readonly #children = new Map<number, ChildState>();
88
132
  readonly #roomChildren = new Map<string, Set<number>>();
89
133
  readonly #childRooms = new Map<number, Set<string>>();
@@ -98,9 +142,10 @@ export class AkanApp {
98
142
  #metricsTimer: Timer | null = null;
99
143
  #logWriter: RotatingLogWriter | null = null;
100
144
  #removeLogSink: (() => void) | null = null;
101
- readonly #childOutputBuffers = new Map<string, string>();
102
- readonly #childStderrBlockBuffers = new Map<string, string[]>();
103
- readonly #childStderrBlockTimers = new Map<string, ReturnType<typeof setTimeout>>();
145
+ #logHub: LogHub | null = null;
146
+ #logControl: LogControlSocket | null = null;
147
+ #hubFileSink: HubFileSink | null = null;
148
+ #logStream: LogStreamRoute | null = null;
104
149
  static readonly #ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
105
150
  #gatewayMetrics: AkanMetricsReport = {};
106
151
  #proxyHopCount = 0;
@@ -115,16 +160,30 @@ export class AkanApp {
115
160
  this.#serverPath = AkanApp.#resolveServerPath(resolvedOptions.serverPath ?? serverPath);
116
161
  this.#artifactDir = path.resolve(path.dirname(this.#serverPath), ".akan", "artifact");
117
162
  this.#replica = AkanApp.#parseReplicaConfig(resolvedOptions.replica);
118
- this.#runtimeDir = path.resolve(
119
- resolvedOptions.runtimeDir ??
120
- process.env.AKAN_RUNTIME_DIR ??
121
- (process.env.NODE_ENV === "production"
122
- ? path.resolve(process.cwd(), "runtime")
123
- : path.resolve(process.cwd(), "local", "apps", process.env.AKAN_PUBLIC_APP_NAME ?? "unknown", "runtime")),
124
- );
163
+ this.#runtimeDir = resolveRuntimeDir(resolvedOptions.runtimeDir);
125
164
  this.#port = Number(resolvedOptions.port ?? process.env.PORT ?? 8282);
126
165
  this.#wsBasePort = Number(resolvedOptions.wsBasePort ?? process.env.AKAN_WS_BASE_PORT ?? this.#port + 10_000);
127
166
  this.#openapi = resolvedOptions.openapi;
167
+ this.#modules = resolvedOptions.modules ?? [];
168
+ this.#solo = AkanApp.#resolveSolo(resolvedOptions, this.#replica);
169
+ this.#logHub = LogHub.attach();
170
+ }
171
+
172
+ /**
173
+ * One replica that serves traffic means there is nothing to balance and nothing to fan pubsub out to, so the
174
+ * gateway is a second Bun runtime and a proxy hop in front of the only server there is. Measured on
175
+ * `apps/akan`: 28MB of RSS and half the requests per second.
176
+ *
177
+ * A batch-only replica is excluded because it never listens — keeping the gateway is what leaves something
178
+ * bound to answer `/_akan/app/health`. `akan start` is excluded because the gateway is also the dev host's
179
+ * builder relay, its crash page, and what holds the port across a child restart.
180
+ */
181
+ static #resolveSolo(options: AkanAppOptions, replica: AkanReplicaConfig) {
182
+ if (options.solo !== undefined) return options.solo;
183
+ if (process.env.AKAN_SOLO === "false" || process.env.AKAN_SOLO === "0") return false;
184
+ if (process.env.AKAN_COMMAND_TYPE === "start") return false;
185
+ if (options.replica !== undefined) return false;
186
+ return replica.total === 1 && replica.batch === 0;
128
187
  }
129
188
 
130
189
  static #resolveServerPath(serverPath: string) {
@@ -157,10 +216,16 @@ export class AkanApp {
157
216
  return Math.max(min, parsed);
158
217
  }
159
218
 
219
+ /**
220
+ * The command that started this gateway outranks an inherited `NODE_ENV`: the command is a statement about
221
+ * this run, while the env var can arrive from a shell export, a CI image, or the workspace `.env` Bun loads
222
+ * on its own. Handing a dev child `NODE_ENV=production` makes it serve from the production route cache and
223
+ * throw on every route, since only `akan build` writes the routes manifest that cache expects.
224
+ */
160
225
  static #defaultChildNodeEnv() {
161
- if (process.env.NODE_ENV) return process.env.NODE_ENV;
162
226
  if (process.env.AKAN_COMMAND_TYPE === "start") return "development";
163
227
  if (process.env.AKAN_COMMAND_TYPE === "build") return "production";
228
+ if (process.env.NODE_ENV) return process.env.NODE_ENV;
164
229
  return Bun.main.endsWith(".js") ? "production" : "development";
165
230
  }
166
231
 
@@ -174,6 +239,17 @@ export class AkanApp {
174
239
  return process.env.AKAN_COMMAND_TYPE === "start" ? 15_000 : 5_000;
175
240
  }
176
241
 
242
+ /**
243
+ * How long a request waits for a replica that is booting or restarting before the gateway answers
244
+ * 503. Dev pays first-touch transpiles on the boot path, so it gets the wider budget; `0` restores
245
+ * the old fail-immediately behavior.
246
+ */
247
+ static #parseUpstreamWaitMs() {
248
+ const configured = Number(process.env.AKAN_UPSTREAM_WAIT_MS);
249
+ if (Number.isFinite(configured) && configured >= 0) return configured;
250
+ return process.env.AKAN_COMMAND_TYPE === "start" ? 15_000 : 5_000;
251
+ }
252
+
177
253
  /**
178
254
  * Must exceed the child's own shutdown timeout (see `AkanServer.#defaultShutdownTimeoutMs`) so
179
255
  * children always get to exit on their own before this gateway stops waiting.
@@ -185,7 +261,10 @@ export class AkanApp {
185
261
  }
186
262
 
187
263
  async start() {
264
+ if (this.#solo) return await this.#startSolo();
265
+ Logger.role = "gateway";
188
266
  await this.#prepareRuntimeDir();
267
+ await this.#startLogHub();
189
268
  this.#startFileLogging();
190
269
  for (let idx = 0; idx < this.#replica.total; idx++) this.#spawn(idx);
191
270
  try {
@@ -212,6 +291,25 @@ export class AkanApp {
212
291
  });
213
292
  }
214
293
 
294
+ async #startSolo() {
295
+ const role = this.#getRole(0);
296
+
297
+ Object.assign(process.env, {
298
+ NODE_ENV: AkanApp.#defaultChildNodeEnv(),
299
+ AKAN_REPLICA: this.#replica.value,
300
+ AKAN_REPLICA_IDX: "0",
301
+ AKAN_APP_DIR: path.dirname(this.#serverPath),
302
+ SERVER_MODE: role,
303
+ ...(this.#openapi === undefined ? {} : { AKAN_OPENAPI: this.#openapi ? "true" : "false" }),
304
+ ...(this.#modules.length ? { AKAN_MODULES: this.#modules.join(",") } : {}),
305
+ });
306
+ this.logger.info(`Starting ${role} replica in this process (solo); set AKAN_SOLO=false for the gateway`);
307
+ const mod = (await import(this.#serverPath)) as { server?: SoloServer; app?: SoloServer };
308
+ const server = mod.server ?? mod.app;
309
+ if (!server?.start) throw new Error("server.ts must export server or app with start()");
310
+ await server.start({ listen: true });
311
+ }
312
+
215
313
  async stop(signal = "SIGTERM") {
216
314
  if (this.#stopping) return;
217
315
  this.#stopping = true;
@@ -245,6 +343,7 @@ export class AkanApp {
245
343
  for (const child of stragglers) child.proc.kill("SIGKILL");
246
344
  await Promise.all(stragglers.map((child) => child.proc.exited.catch(() => undefined)));
247
345
  this.#children.clear();
346
+ await this.#stopLogHub();
248
347
  await this.#stopFileLogging();
249
348
  this.#resolveStopped?.();
250
349
  this.#resolveStopped = null;
@@ -289,6 +388,7 @@ export class AkanApp {
289
388
  AKAN_CHILD_SOCKET: upstream.http.socketPath,
290
389
  AKAN_CHILD_WS_PORT: upstream.ws ? String(upstream.ws.port) : "",
291
390
  ...(this.#openapi === undefined ? {} : { AKAN_OPENAPI: this.#openapi ? "true" : "false" }),
391
+ ...(this.#modules.length ? { AKAN_MODULES: this.#modules.join(",") } : {}),
292
392
  },
293
393
  ipc: (message) => this.#handleMessage(idx, message as AkanIpcMessage, proc),
294
394
  stdout: "pipe",
@@ -312,8 +412,12 @@ export class AkanApp {
312
412
  lastRestartReason: previous?.lastRestartReason,
313
413
  });
314
414
  this.#invalidateFederationChildCache();
315
- this.#pipeOutput(idx, role, proc.stdout, "stdout");
316
- this.#pipeOutput(idx, role, proc.stderr, "stderr");
415
+ const output = new ChildOutputReader({
416
+ onLine: (line) => this.#writeChildLine(idx, role, proc.pid, "stdout", line),
417
+ onBlock: (lines) => this.#writeChildStderrBlock(idx, role, proc.pid, lines),
418
+ });
419
+ void output.pipe(proc.stdout, "stdout");
420
+ void output.pipe(proc.stderr, "stderr");
317
421
  proc.exited.then((code) => this.#handleChildExit(idx, proc, code));
318
422
  }
319
423
 
@@ -463,7 +567,7 @@ export class AkanApp {
463
567
  const entries = await readdir(this.#runtimeDir).catch(() => []);
464
568
  await Promise.all(
465
569
  entries
466
- .filter((name) => /^akan-child-.*\.sock$/.test(name))
570
+ .filter((name) => /^akan-(child-.*|control)\.sock$/.test(name))
467
571
  .map((name) => rm(path.join(this.#runtimeDir, name), { force: true }).catch(() => undefined)),
468
572
  );
469
573
  }
@@ -479,6 +583,13 @@ export class AkanApp {
479
583
  #startFileLogging() {
480
584
  this.#logWriter = RotatingLogWriter.fromRuntimeDir(this.#runtimeDir);
481
585
  if (!this.#logWriter) return;
586
+ if (this.#logHub && Logger.isNdjson) {
587
+ this.#hubFileSink = new HubFileSink(this.#logHub, this.#logWriter, {
588
+ minSev: logSeverity[Logger.fileLevel],
589
+ json: Logger.format === "ndjson-only",
590
+ });
591
+ return;
592
+ }
482
593
  this.#removeLogSink = Logger.addSink((entry) => {
483
594
  this.#logWriter?.write("gateway", entry.plainMessage);
484
595
  });
@@ -487,11 +598,38 @@ export class AkanApp {
487
598
  async #stopFileLogging() {
488
599
  this.#removeLogSink?.();
489
600
  this.#removeLogSink = null;
601
+ this.#hubFileSink?.close();
602
+ this.#hubFileSink = null;
490
603
  const writer = this.#logWriter;
491
604
  this.#logWriter = null;
492
605
  await writer?.close();
493
606
  }
494
607
 
608
+ /** The journal every child forwards into, and the socket `akan logs` reads it from. Additive to the text file. */
609
+ async #startLogHub() {
610
+ const hub = LogHub.attach();
611
+ this.#logHub = hub;
612
+ hub.onFloorChange((minSev) => this.#fanoutToAll({ type: "log.level", minSev }));
613
+ this.#logStream = LogStreamRoute.fromEnv(() => this.#logHub);
614
+ const control = new LogControlSocket(hub, this.#runtimeDir);
615
+ try {
616
+ await control.start();
617
+ this.#logControl = control;
618
+ } catch (error) {
619
+ this.logger.warn(
620
+ `Log control socket unavailable at ${control.path}: ${error instanceof Error ? error.message : String(error)}`,
621
+ );
622
+ }
623
+ }
624
+
625
+ async #stopLogHub() {
626
+ await this.#logControl?.stop();
627
+ this.#logControl = null;
628
+ this.#logStream = null;
629
+ this.#logHub?.close();
630
+ this.#logHub = null;
631
+ }
632
+
495
633
  #listen() {
496
634
  this.#server = Bun.serve({
497
635
  idleTimeout: 0,
@@ -515,11 +653,12 @@ export class AkanApp {
515
653
  const url = new URL(req.url);
516
654
  if (url.pathname === "/_akan/app/health") return Response.json(this.#getHealthStatus());
517
655
  if (url.pathname === "/_akan/app/metrics") return Response.json(this.#getMetricsStatus());
656
+ if (url.pathname === LogStreamRoute.path && this.#logStream) return this.#logStream.handle(req);
518
657
  if (url.pathname === "/_akan/bench/ping") return new Response("ok");
519
658
  if (this.#isWebSocketPath(url.pathname)) return this.#upgradeWebSocket(req, server);
520
659
  const assetResponse = await this.#serveImmutableArtifact(req, url);
521
660
  if (assetResponse) return assetResponse;
522
- return await this.#proxyHttp(req);
661
+ return await this.#proxyHttp(req, server);
523
662
  }
524
663
 
525
664
  #isWebSocketPath(pathname: string) {
@@ -527,6 +666,7 @@ export class AkanApp {
527
666
  }
528
667
 
529
668
  async #serveImmutableArtifact(req: Request, url: URL): Promise<Response | null> {
669
+ if (!this.#web.ssr) return null;
530
670
  const clientPrefix = "/_akan/client/";
531
671
  if (url.pathname.startsWith(clientPrefix)) {
532
672
  const filePath = this.#safeResolve(
@@ -564,10 +704,10 @@ export class AkanApp {
564
704
  if (!child || !upstream) return new Response("No websocket upstream is ready", { status: 503 });
565
705
  const url = new URL(req.url);
566
706
  const upstreamWs = new WebSocket(`ws://${upstream.host}:${upstream.port}${url.pathname}${url.search}`, {
567
- headers: this.#makeProxyHeaders(req, child.idx),
707
+ headers: this.#makeProxyHeaders(req, child.idx, server),
568
708
  } as unknown as string[]);
569
- const socketId = crypto.randomUUID();
570
- const upgraded = server.upgrade(req, { data: { childIdx: child.idx, socketId, upstream: upstreamWs } });
709
+
710
+ const upgraded = server.upgrade(req, { data: { childIdx: child.idx, upstream: upstreamWs } });
571
711
  if (!upgraded) {
572
712
  upstreamWs.close();
573
713
  return new Response("WebSocket upgrade failed", { status: 500 });
@@ -586,7 +726,9 @@ export class AkanApp {
586
726
  const result = ws.send(event.data as string | ArrayBuffer);
587
727
  if (result === 0) upstream.close();
588
728
  });
589
- upstream.addEventListener("close", (event) => ws.close(event.code, event.reason));
729
+ upstream.addEventListener("close", (event) => {
730
+ ws.close(relayableCloseCode(event.code), event.reason);
731
+ });
590
732
  upstream.addEventListener("error", () => ws.close(1011, "upstream websocket error"));
591
733
  Object.assign(ws.data, { pending });
592
734
  }
@@ -607,7 +749,7 @@ export class AkanApp {
607
749
  }
608
750
 
609
751
  #handleWsClose(ws: Bun.ServerWebSocket<GatewayWsData>, code: number, reason: string) {
610
- ws.data.upstream.close(code, reason);
752
+ ws.data.upstream.close(relayableCloseCode(code), reason);
611
753
  const child = this.#children.get(ws.data.childIdx);
612
754
  if (child) child.metrics.activeWebSockets = Math.max(0, (child.metrics.activeWebSockets ?? 1) - 1);
613
755
  }
@@ -661,14 +803,12 @@ export class AkanApp {
661
803
  };
662
804
  }
663
805
 
664
- async #proxyHttp(req: Request): Promise<Response> {
665
- const child = this.#pickFederationChild();
666
- if (!child?.upstream || child.upstream.type !== "unix") {
667
- return this.#respondWithCrashPage(req) ?? new Response("No healthy federation child is ready", { status: 503 });
668
- }
806
+ async #proxyHttp(req: Request, server: Bun.Server<GatewayWsData>): Promise<Response> {
807
+ const child = await this.#pickReadyFederationChild(req);
808
+ if (!child?.upstream || child.upstream.type !== "unix") return this.#respondWithUnavailable(req);
669
809
  const url = new URL(req.url);
670
810
  const upstreamUrl = `http://akan-child${url.pathname}${url.search}`;
671
- const headers = this.#makeProxyHeaders(req, child.idx);
811
+ const headers = this.#makeProxyHeaders(req, child.idx, server);
672
812
  child.metrics.activeRequests = (child.metrics.activeRequests ?? 0) + 1;
673
813
  child.metrics.totalRequests = (child.metrics.totalRequests ?? 0) + 1;
674
814
  const traced = isTraceEnabled();
@@ -682,14 +822,14 @@ export class AkanApp {
682
822
  signal: req.signal,
683
823
  redirect: "manual",
684
824
  });
685
- return this.#proxyResponse(upstreamRes);
825
+ return await this.#proxyResponse(req, upstreamRes);
686
826
  } catch (error) {
687
827
  if (AkanApp.#isUpstreamOpenFailure(error)) {
688
828
  this.logger.error(
689
829
  `Child ${child.idx}/${child.role} upstream is unreachable (${child.upstream.socketPath}); restarting`,
690
830
  );
691
831
  this.#scheduleChildRestart(child, child.proc, "upstream-open-failed");
692
- return new Response("Federation child upstream is unreachable; restarting", { status: 503 });
832
+ return AkanApp.#unavailableResponse(req, "Federation child upstream is unreachable; restarting");
693
833
  }
694
834
  throw error;
695
835
  } finally {
@@ -705,26 +845,82 @@ export class AkanApp {
705
845
  }
706
846
 
707
847
  /**
708
- * Dev-only: every traffic replica is in the crashed terminal state, so a bare 503 would hide the
709
- * boot error from the browser. Surface it, and reload once a fixed gateway takes over the port.
848
+ * A replica that is booting or restarting is normally back within a second or two, and a browser
849
+ * handed a 503 stays on that dead page until somebody reloads by hand — so a request waits for the
850
+ * upstream instead of failing at it. A dev crash loop is terminal, so it is answered immediately.
710
851
  */
711
- #respondWithCrashPage(req: Request): Response | null {
852
+ async #pickReadyFederationChild(req: Request): Promise<ChildState | null> {
853
+ const ready = this.#pickFederationChild();
854
+ if (ready?.upstream) return ready;
855
+ const deadline = performance.now() + this.#upstreamWaitMs;
856
+ while (performance.now() < deadline) {
857
+ if (this.#stopping || req.signal.aborted || this.#getCrashLoopDetail()) return null;
858
+ await Bun.sleep(50);
859
+ const child = this.#pickFederationChild();
860
+ if (child?.upstream) return child;
861
+ }
862
+ return null;
863
+ }
864
+
865
+ /** Dev-only terminal state: every traffic replica gave up booting. The detail is the boot error. */
866
+ #getCrashLoopDetail(): string | null {
712
867
  if (!this.#devHosted) return null;
713
868
  const trafficChildren = [...this.#children.values()].filter((child) => child.role !== "batch");
714
869
  if (trafficChildren.length === 0) return null;
715
870
  if (!trafficChildren.every((child) => child.status === "crashed")) return null;
716
- const detail =
871
+ return (
717
872
  trafficChildren.map((child) => child.lastErrorMessage ?? child.lastRestartReason).find(Boolean) ??
718
- "unknown boot error";
719
- const message = `Backend failed to start after ${AkanApp.#devMaxChildBootFailures} boot attempts: ${detail}`;
720
- if (!req.headers.get("accept")?.includes("text/html")) {
721
- return new Response(message, { status: 503, headers: { "cache-control": "no-store" } });
873
+ "unknown boot error"
874
+ );
875
+ }
876
+
877
+ /**
878
+ * Both states answer 503, so an ingress or CDN reads them exactly as before; the HTML body is what
879
+ * a browser navigation needs, since nothing on a bare-text 503 can bring the page back on its own.
880
+ */
881
+ #respondWithUnavailable(req: Request): Response {
882
+ const crashDetail = this.#getCrashLoopDetail();
883
+ const page = crashDetail
884
+ ? {
885
+ heading: "Backend failed to start",
886
+ detail: crashDetail,
887
+ text: `Backend failed to start after ${AkanApp.#devMaxChildBootFailures} boot attempts: ${crashDetail}`,
888
+ note: `The dev server stopped retrying after ${AkanApp.#devMaxChildBootFailures} failed boots. Fix the error and save — this page reloads automatically.`,
889
+ }
890
+ : {
891
+ heading: "Backend is starting",
892
+ detail: "No healthy federation child is ready",
893
+ text: "No healthy federation child is ready",
894
+ note: "A replica is booting or restarting — this page reloads itself as soon as it answers.",
895
+ };
896
+ if (req.headers.get("accept")?.includes("text/html")) {
897
+ return new Response(AkanApp.#statusPageHtml(page), {
898
+ status: 503,
899
+ headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
900
+ });
722
901
  }
723
- const html = `<!doctype html>
902
+ return AkanApp.#unavailableResponse(req, page.text);
903
+ }
904
+
905
+ /**
906
+ * An API caller asks for JSON, so it is answered with the payload `HttpClient` restores into an `Err` —
907
+ * a bare-text 503 reaches the browser as a JSON parse error naming the page body instead of the outage.
908
+ */
909
+ static #unavailableResponse(req: Request, detail: string): Response {
910
+ const headers = { "cache-control": "no-store" };
911
+ if (!req.headers.get("accept")?.includes("application/json")) return new Response(detail, { status: 503, headers });
912
+ return Response.json(
913
+ { error: "base.error.serverUnavailable", statusCode: 503, data: { status: 503 }, details: detail },
914
+ { status: 503, headers },
915
+ );
916
+ }
917
+
918
+ static #statusPageHtml({ heading, detail, note }: { heading: string; detail: string; note: string }) {
919
+ return `<!doctype html>
724
920
  <html>
725
921
  <head>
726
922
  <meta charset="utf-8" />
727
- <title>Backend failed to start</title>
923
+ <title>${AkanApp.#escapeHtml(heading)}</title>
728
924
  <style>
729
925
  body { margin: 0; padding: 48px 24px; background: #111827; color: #e5e7eb; font-family: ui-sans-serif, system-ui, sans-serif; }
730
926
  main { max-width: 720px; margin: 0 auto; }
@@ -735,9 +931,9 @@ export class AkanApp {
735
931
  </head>
736
932
  <body>
737
933
  <main>
738
- <h1>Backend failed to start</h1>
934
+ <h1>${AkanApp.#escapeHtml(heading)}</h1>
739
935
  <pre>${AkanApp.#escapeHtml(detail)}</pre>
740
- <p>The dev server stopped retrying after ${AkanApp.#devMaxChildBootFailures} failed boots. Fix the error and save &mdash; this page reloads automatically.</p>
936
+ <p>${AkanApp.#escapeHtml(note)}</p>
741
937
  </main>
742
938
  <script>
743
939
  const poll = async () => {
@@ -751,10 +947,6 @@ export class AkanApp {
751
947
  </script>
752
948
  </body>
753
949
  </html>`;
754
- return new Response(html, {
755
- status: 503,
756
- headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
757
- });
758
950
  }
759
951
 
760
952
  static #escapeHtml(text: string) {
@@ -778,17 +970,23 @@ export class AkanApp {
778
970
  this.#proxyHopMaxMs = Math.max(this.#proxyHopMaxMs, durationMs);
779
971
  }
780
972
 
781
- #proxyResponse(upstreamRes: Response): Response {
973
+ async #proxyResponse(req: Request, upstreamRes: Response): Promise<Response> {
782
974
  const headers = new Headers(upstreamRes.headers);
783
975
 
784
976
  headers.delete("content-encoding");
785
977
  headers.delete("content-length");
786
978
  this.#rewriteInternalLocation(headers);
787
- return new Response(upstreamRes.body, {
979
+ const proxied = new Response(upstreamRes.body, {
788
980
  status: upstreamRes.status,
789
981
  statusText: upstreamRes.statusText,
790
982
  headers,
791
983
  });
984
+
985
+ return AkanApp.#isProxiedJson(headers) ? await compressResponse(req, proxied) : proxied;
986
+ }
987
+
988
+ static #isProxiedJson(headers: Headers): boolean {
989
+ return (headers.get("content-type") ?? "").split(";")[0]?.trim().toLowerCase() === "application/json";
792
990
  }
793
991
 
794
992
  #rewriteInternalLocation(headers: Headers) {
@@ -837,8 +1035,8 @@ export class AkanApp {
837
1035
  return resolved;
838
1036
  }
839
1037
 
840
- #makeProxyHeaders(req: Request, childIdx: number) {
841
- return makeAkanChildProxyHeaders(req, childIdx);
1038
+ #makeProxyHeaders(req: Request, childIdx: number, server?: Bun.Server<GatewayWsData>) {
1039
+ return makeAkanChildProxyHeaders(req, childIdx, server?.requestIP(req));
842
1040
  }
843
1041
 
844
1042
  #invalidateFederationChildCache() {
@@ -902,6 +1100,10 @@ export class AkanApp {
902
1100
  case "metrics.report":
903
1101
  this.#updateMetrics(idx, message.metrics);
904
1102
  return;
1103
+ case "log.records":
1104
+ this.#logHub?.ingestMany(message.records);
1105
+ if (message.dropped) this.logger.warn(`Child ${idx} dropped ${message.dropped} log records (ipc backpressure)`);
1106
+ return;
905
1107
  case "health.pong":
906
1108
  this.#markHealthy(idx);
907
1109
  return;
@@ -948,6 +1150,9 @@ export class AkanApp {
948
1150
  child.healthPath = message.healthPath;
949
1151
  child.lastPongAtMono = performance.now();
950
1152
  child.restartAttempts = 0;
1153
+
1154
+ if (this.#logHub && this.#logHub.floor !== null)
1155
+ this.#sendToChild(child, { type: "log.level", minSev: this.#logHub.floor });
951
1156
  child.restartPending = false;
952
1157
  child.lastErrorMessage = undefined;
953
1158
  this.#invalidateFederationChildCache();
@@ -1140,6 +1345,10 @@ export class AkanApp {
1140
1345
  }
1141
1346
  }
1142
1347
 
1348
+ #fanoutToAll(message: AkanIpcMessage) {
1349
+ for (const child of this.#children.values()) this.#sendToChild(child, message);
1350
+ }
1351
+
1143
1352
  #fanoutToBatch(message: AkanIpcMessage) {
1144
1353
  for (const child of this.#children.values()) {
1145
1354
  if (child.role === "batch" || child.role === "all") {
@@ -1165,99 +1374,30 @@ export class AkanApp {
1165
1374
  }
1166
1375
  }
1167
1376
 
1168
- async #pipeOutput(
1169
- idx: number,
1170
- role: AkanChildRole,
1171
- stream: ReadableStream<Uint8Array> | null,
1172
- type: "stdout" | "stderr",
1173
- ) {
1174
- if (!stream) return;
1175
- const bufferKey = `${idx}:${type}`;
1176
- const reader = stream.getReader();
1177
- const decoder = new TextDecoder();
1178
- try {
1179
- for (;;) {
1180
- const { done, value } = await reader.read();
1181
- if (done) break;
1182
- const text = decoder.decode(value, { stream: true });
1183
- this.#writeChildOutput(idx, role, type, bufferKey, text);
1184
- }
1185
- const remaining = decoder.decode();
1186
- if (remaining) this.#writeChildOutput(idx, role, type, bufferKey, remaining);
1187
- } finally {
1188
- this.#flushChildOutput(idx, role, type, bufferKey);
1189
- if (type === "stderr") this.#flushChildStderrBlock(idx, role, AkanApp.#childStderrBlockKey(idx, role));
1190
- }
1191
- }
1192
-
1193
- #writeChildOutput(idx: number, role: AkanChildRole, type: "stdout" | "stderr", bufferKey: string, text: string) {
1194
- let buffered = `${this.#childOutputBuffers.get(bufferKey) ?? ""}${text}`;
1195
- for (;;) {
1196
- const newlineIdx = buffered.indexOf("\n");
1197
- if (newlineIdx === -1) break;
1198
- const line = buffered.slice(0, newlineIdx + 1);
1199
- buffered = buffered.slice(newlineIdx + 1);
1200
- this.#writeChildOutputLine(idx, role, type, line);
1377
+ #writeChildLine(idx: number, role: AkanChildRole, pid: number | null, type: "stdout" | "stderr", line: string) {
1378
+ if (Logger.isNdjson) {
1379
+ this.#logHub?.ingest(ChildOutputReader.toRecord({ type, text: line, name: "child", role, replicaIdx: idx, pid }));
1380
+ return;
1201
1381
  }
1202
- if (buffered) this.#childOutputBuffers.set(bufferKey, buffered);
1203
- else this.#childOutputBuffers.delete(bufferKey);
1204
- }
1205
-
1206
- #flushChildOutput(idx: number, role: AkanChildRole, type: "stdout" | "stderr", bufferKey: string) {
1207
- const buffered = this.#childOutputBuffers.get(bufferKey);
1208
- if (!buffered) return;
1209
- this.#childOutputBuffers.delete(bufferKey);
1210
- this.#writeChildOutputLine(idx, role, type, `${buffered}\n`);
1211
- }
1212
-
1213
- #writeChildOutputLine(idx: number, role: AkanChildRole, type: "stdout" | "stderr", line: string) {
1214
- if (type === "stderr" && this.#bufferChildStderrLine(idx, role, line)) return;
1215
- this.#writeChildOutputLineRaw(idx, role, type, line);
1216
- }
1217
-
1218
- #writeChildOutputLineRaw(idx: number, role: AkanChildRole, type: "stdout" | "stderr", line: string) {
1219
1382
  const prefixedLine = `[child:${idx} ${role}] [${type}] ${line}`;
1220
- process[type].write(prefixedLine);
1383
+ if (type === "stdout" || this.#printChildStderr) process[type].write(prefixedLine);
1221
1384
  this.#logWriter?.write(`${idx}-${role}`, AkanApp.#stripAnsi(prefixedLine));
1222
1385
  }
1223
1386
 
1224
- #bufferChildStderrLine(idx: number, role: AkanChildRole, line: string): boolean {
1225
- const key = AkanApp.#childStderrBlockKey(idx, role);
1226
- const block = this.#childStderrBlockBuffers.get(key) ?? [];
1227
- block.push(line);
1228
- this.#childStderrBlockBuffers.set(key, block);
1229
-
1230
- const existingTimer = this.#childStderrBlockTimers.get(key);
1231
- if (existingTimer) clearTimeout(existingTimer);
1232
-
1233
- if (line.trim() === "" || block.length >= 64) {
1234
- this.#flushChildStderrBlock(idx, role, key);
1235
- return true;
1236
- }
1237
-
1238
- this.#childStderrBlockTimers.set(
1239
- key,
1240
- setTimeout(() => this.#flushChildStderrBlock(idx, role, key), 50),
1241
- );
1242
- return true;
1243
- }
1244
-
1245
- #flushChildStderrBlock(idx: number, role: AkanChildRole, key: string) {
1246
- const timer = this.#childStderrBlockTimers.get(key);
1247
- if (timer) clearTimeout(timer);
1248
- this.#childStderrBlockTimers.delete(key);
1249
-
1250
- const block = this.#childStderrBlockBuffers.get(key);
1251
- if (!block?.length) return;
1252
- this.#childStderrBlockBuffers.delete(key);
1253
-
1254
- const text = block.join("");
1387
+ /**
1388
+ * In ndjson mode a stack trace is one record, and `AKAN_CHILD_STDERR` does not apply: the line that killed a
1389
+ * child is the one the collector must not miss.
1390
+ */
1391
+ #writeChildStderrBlock(idx: number, role: AkanChildRole, pid: number | null, lines: string[]) {
1392
+ const text = lines.join("");
1255
1393
  if (AkanApp.#isBenignRsdwConnectionClosedBlock(text)) return;
1256
- for (const blockLine of block) this.#writeChildOutputLineRaw(idx, role, "stderr", blockLine);
1257
- }
1258
-
1259
- static #childStderrBlockKey(idx: number, role: AkanChildRole): string {
1260
- return `${idx}:${role}:stderr`;
1394
+ if (Logger.isNdjson) {
1395
+ this.#logHub?.ingest(
1396
+ ChildOutputReader.toRecord({ type: "stderr", text, name: "child", role, replicaIdx: idx, pid }),
1397
+ );
1398
+ return;
1399
+ }
1400
+ for (const line of lines) this.#writeChildLine(idx, role, pid, "stderr", line);
1261
1401
  }
1262
1402
 
1263
1403
  static #isBenignRsdwConnectionClosedBlock(text: string): boolean {