@aigne/aos 0.2.0-beta

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 (533) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE.md +26 -0
  3. package/dist/agent-dsl.cjs +116 -0
  4. package/dist/agent-dsl.d.cts +31 -0
  5. package/dist/agent-dsl.d.cts.map +1 -0
  6. package/dist/agent-dsl.d.mts +31 -0
  7. package/dist/agent-dsl.d.mts.map +1 -0
  8. package/dist/agent-dsl.mjs +115 -0
  9. package/dist/agent-dsl.mjs.map +1 -0
  10. package/dist/arc-compat.cjs +125 -0
  11. package/dist/arc-compat.d.cts +17 -0
  12. package/dist/arc-compat.d.cts.map +1 -0
  13. package/dist/arc-compat.d.mts +17 -0
  14. package/dist/arc-compat.d.mts.map +1 -0
  15. package/dist/arc-compat.mjs +123 -0
  16. package/dist/arc-compat.mjs.map +1 -0
  17. package/dist/auto-surface/compiler.cjs +36 -0
  18. package/dist/auto-surface/compiler.d.cts +7 -0
  19. package/dist/auto-surface/compiler.d.cts.map +1 -0
  20. package/dist/auto-surface/compiler.d.mts +7 -0
  21. package/dist/auto-surface/compiler.d.mts.map +1 -0
  22. package/dist/auto-surface/compiler.mjs +37 -0
  23. package/dist/auto-surface/compiler.mjs.map +1 -0
  24. package/dist/auto-surface/definition-dsl.cjs +333 -0
  25. package/dist/auto-surface/definition-dsl.d.cts +41 -0
  26. package/dist/auto-surface/definition-dsl.d.cts.map +1 -0
  27. package/dist/auto-surface/definition-dsl.d.mts +41 -0
  28. package/dist/auto-surface/definition-dsl.d.mts.map +1 -0
  29. package/dist/auto-surface/definition-dsl.mjs +332 -0
  30. package/dist/auto-surface/definition-dsl.mjs.map +1 -0
  31. package/dist/auto-surface/fragments.cjs +177 -0
  32. package/dist/auto-surface/fragments.d.cts +23 -0
  33. package/dist/auto-surface/fragments.d.cts.map +1 -0
  34. package/dist/auto-surface/fragments.d.mts +23 -0
  35. package/dist/auto-surface/fragments.d.mts.map +1 -0
  36. package/dist/auto-surface/fragments.mjs +175 -0
  37. package/dist/auto-surface/fragments.mjs.map +1 -0
  38. package/dist/auto-surface/index.d.mts +9 -0
  39. package/dist/auto-surface/layering.cjs +43 -0
  40. package/dist/auto-surface/layering.d.cts +7 -0
  41. package/dist/auto-surface/layering.d.cts.map +1 -0
  42. package/dist/auto-surface/layering.d.mts +7 -0
  43. package/dist/auto-surface/layering.d.mts.map +1 -0
  44. package/dist/auto-surface/layering.mjs +43 -0
  45. package/dist/auto-surface/layering.mjs.map +1 -0
  46. package/dist/auto-surface/projection.cjs +11 -0
  47. package/dist/auto-surface/projection.d.cts +11 -0
  48. package/dist/auto-surface/projection.d.cts.map +1 -0
  49. package/dist/auto-surface/projection.d.mts +11 -0
  50. package/dist/auto-surface/projection.d.mts.map +1 -0
  51. package/dist/auto-surface/projection.mjs +12 -0
  52. package/dist/auto-surface/projection.mjs.map +1 -0
  53. package/dist/auto-surface/projectors/settings-shell.cjs +114 -0
  54. package/dist/auto-surface/projectors/settings-shell.d.cts +10 -0
  55. package/dist/auto-surface/projectors/settings-shell.d.cts.map +1 -0
  56. package/dist/auto-surface/projectors/settings-shell.d.mts +10 -0
  57. package/dist/auto-surface/projectors/settings-shell.d.mts.map +1 -0
  58. package/dist/auto-surface/projectors/settings-shell.mjs +114 -0
  59. package/dist/auto-surface/projectors/settings-shell.mjs.map +1 -0
  60. package/dist/auto-surface/scenario.cjs +38 -0
  61. package/dist/auto-surface/scenario.d.cts +31 -0
  62. package/dist/auto-surface/scenario.d.cts.map +1 -0
  63. package/dist/auto-surface/scenario.d.mts +31 -0
  64. package/dist/auto-surface/scenario.d.mts.map +1 -0
  65. package/dist/auto-surface/scenario.mjs +37 -0
  66. package/dist/auto-surface/scenario.mjs.map +1 -0
  67. package/dist/auto-surface/types.cjs +9 -0
  68. package/dist/auto-surface/types.d.cts +153 -0
  69. package/dist/auto-surface/types.d.cts.map +1 -0
  70. package/dist/auto-surface/types.d.mts +153 -0
  71. package/dist/auto-surface/types.d.mts.map +1 -0
  72. package/dist/auto-surface/types.mjs +9 -0
  73. package/dist/auto-surface/types.mjs.map +1 -0
  74. package/dist/auto-surface/value-dsl.cjs +80 -0
  75. package/dist/auto-surface/value-dsl.d.cts +24 -0
  76. package/dist/auto-surface/value-dsl.d.cts.map +1 -0
  77. package/dist/auto-surface/value-dsl.d.mts +24 -0
  78. package/dist/auto-surface/value-dsl.d.mts.map +1 -0
  79. package/dist/auto-surface/value-dsl.mjs +79 -0
  80. package/dist/auto-surface/value-dsl.mjs.map +1 -0
  81. package/dist/blocklet/activation.cjs +94 -0
  82. package/dist/blocklet/activation.d.cts +30 -0
  83. package/dist/blocklet/activation.d.cts.map +1 -0
  84. package/dist/blocklet/activation.d.mts +30 -0
  85. package/dist/blocklet/activation.d.mts.map +1 -0
  86. package/dist/blocklet/activation.mjs +94 -0
  87. package/dist/blocklet/activation.mjs.map +1 -0
  88. package/dist/blocklet/did-space-registry.cjs +282 -0
  89. package/dist/blocklet/did-space-registry.d.cts +82 -0
  90. package/dist/blocklet/did-space-registry.d.cts.map +1 -0
  91. package/dist/blocklet/did-space-registry.d.mts +82 -0
  92. package/dist/blocklet/did-space-registry.d.mts.map +1 -0
  93. package/dist/blocklet/did-space-registry.mjs +281 -0
  94. package/dist/blocklet/did-space-registry.mjs.map +1 -0
  95. package/dist/blocklet/handlers.cjs +66 -0
  96. package/dist/blocklet/handlers.d.cts +16 -0
  97. package/dist/blocklet/handlers.d.cts.map +1 -0
  98. package/dist/blocklet/handlers.d.mts +16 -0
  99. package/dist/blocklet/handlers.d.mts.map +1 -0
  100. package/dist/blocklet/handlers.mjs +65 -0
  101. package/dist/blocklet/handlers.mjs.map +1 -0
  102. package/dist/blocklet/index.d.mts +7 -0
  103. package/dist/blocklet/manifest.d.cts +2 -0
  104. package/dist/blocklet/manifest.d.mts +2 -0
  105. package/dist/blocklet/manifest.mjs +3 -0
  106. package/dist/blocklet/registry.cjs +447 -0
  107. package/dist/blocklet/registry.d.cts +197 -0
  108. package/dist/blocklet/registry.d.cts.map +1 -0
  109. package/dist/blocklet/registry.d.mts +197 -0
  110. package/dist/blocklet/registry.d.mts.map +1 -0
  111. package/dist/blocklet/registry.mjs +441 -0
  112. package/dist/blocklet/registry.mjs.map +1 -0
  113. package/dist/blocklet/static-render.cjs +183 -0
  114. package/dist/blocklet/static-render.d.cts +55 -0
  115. package/dist/blocklet/static-render.d.cts.map +1 -0
  116. package/dist/blocklet/static-render.d.mts +55 -0
  117. package/dist/blocklet/static-render.d.mts.map +1 -0
  118. package/dist/blocklet/static-render.mjs +181 -0
  119. package/dist/blocklet/static-render.mjs.map +1 -0
  120. package/dist/blocklet/types.d.cts +70 -0
  121. package/dist/blocklet/types.d.cts.map +1 -0
  122. package/dist/blocklet/types.d.mts +70 -0
  123. package/dist/blocklet/types.d.mts.map +1 -0
  124. package/dist/devices/chain.cjs +127 -0
  125. package/dist/devices/chain.d.cts +48 -0
  126. package/dist/devices/chain.d.cts.map +1 -0
  127. package/dist/devices/chain.d.mts +48 -0
  128. package/dist/devices/chain.d.mts.map +1 -0
  129. package/dist/devices/chain.mjs +125 -0
  130. package/dist/devices/chain.mjs.map +1 -0
  131. package/dist/dsl-shared.cjs +290 -0
  132. package/dist/dsl-shared.d.cts +54 -0
  133. package/dist/dsl-shared.d.cts.map +1 -0
  134. package/dist/dsl-shared.d.mts +54 -0
  135. package/dist/dsl-shared.d.mts.map +1 -0
  136. package/dist/dsl-shared.mjs +287 -0
  137. package/dist/dsl-shared.mjs.map +1 -0
  138. package/dist/http/afs-mcp.cjs +85 -0
  139. package/dist/http/afs-mcp.d.cts +14 -0
  140. package/dist/http/afs-mcp.d.cts.map +1 -0
  141. package/dist/http/afs-mcp.d.mts +14 -0
  142. package/dist/http/afs-mcp.d.mts.map +1 -0
  143. package/dist/http/afs-mcp.mjs +86 -0
  144. package/dist/http/afs-mcp.mjs.map +1 -0
  145. package/dist/http/afs-raw.cjs +264 -0
  146. package/dist/http/afs-raw.d.cts +31 -0
  147. package/dist/http/afs-raw.d.cts.map +1 -0
  148. package/dist/http/afs-raw.d.mts +31 -0
  149. package/dist/http/afs-raw.d.mts.map +1 -0
  150. package/dist/http/afs-raw.mjs +264 -0
  151. package/dist/http/afs-raw.mjs.map +1 -0
  152. package/dist/http/afs-rest.cjs +30 -0
  153. package/dist/http/afs-rest.d.cts +21 -0
  154. package/dist/http/afs-rest.d.cts.map +1 -0
  155. package/dist/http/afs-rest.d.mts +21 -0
  156. package/dist/http/afs-rest.d.mts.map +1 -0
  157. package/dist/http/afs-rest.mjs +31 -0
  158. package/dist/http/afs-rest.mjs.map +1 -0
  159. package/dist/http/afs-rpc-wire.cjs +271 -0
  160. package/dist/http/afs-rpc-wire.d.cts +195 -0
  161. package/dist/http/afs-rpc-wire.d.cts.map +1 -0
  162. package/dist/http/afs-rpc-wire.d.mts +195 -0
  163. package/dist/http/afs-rpc-wire.d.mts.map +1 -0
  164. package/dist/http/afs-rpc-wire.mjs +262 -0
  165. package/dist/http/afs-rpc-wire.mjs.map +1 -0
  166. package/dist/http/afs-rpc.cjs +729 -0
  167. package/dist/http/afs-rpc.d.cts +103 -0
  168. package/dist/http/afs-rpc.d.cts.map +1 -0
  169. package/dist/http/afs-rpc.d.mts +103 -0
  170. package/dist/http/afs-rpc.d.mts.map +1 -0
  171. package/dist/http/afs-rpc.mjs +729 -0
  172. package/dist/http/afs-rpc.mjs.map +1 -0
  173. package/dist/http/afs-upload.cjs +116 -0
  174. package/dist/http/afs-upload.d.cts +40 -0
  175. package/dist/http/afs-upload.d.cts.map +1 -0
  176. package/dist/http/afs-upload.d.mts +40 -0
  177. package/dist/http/afs-upload.d.mts.map +1 -0
  178. package/dist/http/afs-upload.mjs +116 -0
  179. package/dist/http/afs-upload.mjs.map +1 -0
  180. package/dist/http/aup-client.cjs +60 -0
  181. package/dist/http/aup-client.d.cts +22 -0
  182. package/dist/http/aup-client.d.cts.map +1 -0
  183. package/dist/http/aup-client.d.mts +22 -0
  184. package/dist/http/aup-client.d.mts.map +1 -0
  185. package/dist/http/aup-client.mjs +59 -0
  186. package/dist/http/aup-client.mjs.map +1 -0
  187. package/dist/http/aup-event.cjs +100 -0
  188. package/dist/http/aup-event.d.cts +38 -0
  189. package/dist/http/aup-event.d.cts.map +1 -0
  190. package/dist/http/aup-event.d.mts +38 -0
  191. package/dist/http/aup-event.d.mts.map +1 -0
  192. package/dist/http/aup-event.mjs +101 -0
  193. package/dist/http/aup-event.mjs.map +1 -0
  194. package/dist/http/auth-context.cjs +66 -0
  195. package/dist/http/auth-context.d.cts +64 -0
  196. package/dist/http/auth-context.d.cts.map +1 -0
  197. package/dist/http/auth-context.d.mts +64 -0
  198. package/dist/http/auth-context.d.mts.map +1 -0
  199. package/dist/http/auth-context.mjs +66 -0
  200. package/dist/http/auth-context.mjs.map +1 -0
  201. package/dist/http/csrf.cjs +50 -0
  202. package/dist/http/csrf.d.cts +8 -0
  203. package/dist/http/csrf.d.cts.map +1 -0
  204. package/dist/http/csrf.d.mts +8 -0
  205. package/dist/http/csrf.d.mts.map +1 -0
  206. package/dist/http/csrf.mjs +49 -0
  207. package/dist/http/csrf.mjs.map +1 -0
  208. package/dist/http/handlers.cjs +106 -0
  209. package/dist/http/handlers.d.cts +39 -0
  210. package/dist/http/handlers.d.cts.map +1 -0
  211. package/dist/http/handlers.d.mts +39 -0
  212. package/dist/http/handlers.d.mts.map +1 -0
  213. package/dist/http/handlers.mjs +104 -0
  214. package/dist/http/handlers.mjs.map +1 -0
  215. package/dist/http/index.d.mts +1 -0
  216. package/dist/http/ingest-facade.cjs +315 -0
  217. package/dist/http/ingest-facade.mjs +314 -0
  218. package/dist/http/ingest-facade.mjs.map +1 -0
  219. package/dist/http/raw-url.cjs +136 -0
  220. package/dist/http/raw-url.d.cts +54 -0
  221. package/dist/http/raw-url.d.cts.map +1 -0
  222. package/dist/http/raw-url.d.mts +54 -0
  223. package/dist/http/raw-url.d.mts.map +1 -0
  224. package/dist/http/raw-url.mjs +136 -0
  225. package/dist/http/raw-url.mjs.map +1 -0
  226. package/dist/http/request-context.cjs +178 -0
  227. package/dist/http/request-context.d.cts +76 -0
  228. package/dist/http/request-context.d.cts.map +1 -0
  229. package/dist/http/request-context.d.mts +76 -0
  230. package/dist/http/request-context.d.mts.map +1 -0
  231. package/dist/http/request-context.mjs +179 -0
  232. package/dist/http/request-context.mjs.map +1 -0
  233. package/dist/index.cjs +197 -0
  234. package/dist/index.d.cts +63 -0
  235. package/dist/index.d.mts +68 -0
  236. package/dist/index.mjs +61 -0
  237. package/dist/mcp/index.cjs +9 -0
  238. package/dist/mcp/index.d.cts +5 -0
  239. package/dist/mcp/index.d.mts +5 -0
  240. package/dist/mcp/index.mjs +6 -0
  241. package/dist/mcp/prompts.cjs +17 -0
  242. package/dist/mcp/prompts.d.cts +8 -0
  243. package/dist/mcp/prompts.d.cts.map +1 -0
  244. package/dist/mcp/prompts.d.mts +8 -0
  245. package/dist/mcp/prompts.d.mts.map +1 -0
  246. package/dist/mcp/prompts.mjs +17 -0
  247. package/dist/mcp/prompts.mjs.map +1 -0
  248. package/dist/mcp/resources.cjs +22 -0
  249. package/dist/mcp/resources.d.cts +8 -0
  250. package/dist/mcp/resources.d.cts.map +1 -0
  251. package/dist/mcp/resources.d.mts +8 -0
  252. package/dist/mcp/resources.d.mts.map +1 -0
  253. package/dist/mcp/resources.mjs +22 -0
  254. package/dist/mcp/resources.mjs.map +1 -0
  255. package/dist/mcp/server.cjs +51 -0
  256. package/dist/mcp/server.d.cts +20 -0
  257. package/dist/mcp/server.d.cts.map +1 -0
  258. package/dist/mcp/server.d.mts +20 -0
  259. package/dist/mcp/server.d.mts.map +1 -0
  260. package/dist/mcp/server.mjs +52 -0
  261. package/dist/mcp/server.mjs.map +1 -0
  262. package/dist/mcp/tools.cjs +218 -0
  263. package/dist/mcp/tools.d.cts +8 -0
  264. package/dist/mcp/tools.d.cts.map +1 -0
  265. package/dist/mcp/tools.d.mts +8 -0
  266. package/dist/mcp/tools.d.mts.map +1 -0
  267. package/dist/mcp/tools.mjs +219 -0
  268. package/dist/mcp/tools.mjs.map +1 -0
  269. package/dist/mcp/utils.cjs +75 -0
  270. package/dist/mcp/utils.mjs +69 -0
  271. package/dist/mcp/utils.mjs.map +1 -0
  272. package/dist/mount/index.cjs +4 -0
  273. package/dist/mount/index.d.cts +3 -0
  274. package/dist/mount/index.d.mts +3 -0
  275. package/dist/mount/index.mjs +3 -0
  276. package/dist/mount/materializer.cjs +167 -0
  277. package/dist/mount/materializer.d.cts +97 -0
  278. package/dist/mount/materializer.d.cts.map +1 -0
  279. package/dist/mount/materializer.d.mts +97 -0
  280. package/dist/mount/materializer.d.mts.map +1 -0
  281. package/dist/mount/materializer.mjs +167 -0
  282. package/dist/mount/materializer.mjs.map +1 -0
  283. package/dist/mount/types.d.cts +90 -0
  284. package/dist/mount/types.d.cts.map +1 -0
  285. package/dist/mount/types.d.mts +90 -0
  286. package/dist/mount/types.d.mts.map +1 -0
  287. package/dist/mount-config.cjs +61 -0
  288. package/dist/mount-config.d.cts +19 -0
  289. package/dist/mount-config.d.cts.map +1 -0
  290. package/dist/mount-config.d.mts +19 -0
  291. package/dist/mount-config.d.mts.map +1 -0
  292. package/dist/mount-config.mjs +61 -0
  293. package/dist/mount-config.mjs.map +1 -0
  294. package/dist/projectors/aup-projector.cjs +190 -0
  295. package/dist/projectors/aup-projector.d.cts +59 -0
  296. package/dist/projectors/aup-projector.d.cts.map +1 -0
  297. package/dist/projectors/aup-projector.d.mts +59 -0
  298. package/dist/projectors/aup-projector.d.mts.map +1 -0
  299. package/dist/projectors/aup-projector.mjs +188 -0
  300. package/dist/projectors/aup-projector.mjs.map +1 -0
  301. package/dist/projectors/index.cjs +6 -0
  302. package/dist/projectors/index.d.cts +2 -0
  303. package/dist/projectors/index.d.mts +2 -0
  304. package/dist/projectors/index.mjs +3 -0
  305. package/dist/scaffold/afs.cjs +195 -0
  306. package/dist/scaffold/afs.d.cts +16 -0
  307. package/dist/scaffold/afs.d.cts.map +1 -0
  308. package/dist/scaffold/afs.d.mts +16 -0
  309. package/dist/scaffold/afs.d.mts.map +1 -0
  310. package/dist/scaffold/afs.mjs +195 -0
  311. package/dist/scaffold/afs.mjs.map +1 -0
  312. package/dist/scaffold/composition.cjs +92 -0
  313. package/dist/scaffold/composition.d.cts +10 -0
  314. package/dist/scaffold/composition.d.cts.map +1 -0
  315. package/dist/scaffold/composition.d.mts +10 -0
  316. package/dist/scaffold/composition.d.mts.map +1 -0
  317. package/dist/scaffold/composition.mjs +92 -0
  318. package/dist/scaffold/composition.mjs.map +1 -0
  319. package/dist/scaffold/dsl-error.cjs +21 -0
  320. package/dist/scaffold/dsl-error.d.cts +11 -0
  321. package/dist/scaffold/dsl-error.d.cts.map +1 -0
  322. package/dist/scaffold/dsl-error.d.mts +11 -0
  323. package/dist/scaffold/dsl-error.d.mts.map +1 -0
  324. package/dist/scaffold/dsl-error.mjs +20 -0
  325. package/dist/scaffold/dsl-error.mjs.map +1 -0
  326. package/dist/scaffold/index.d.mts +12 -0
  327. package/dist/scaffold/manifest-dsl.cjs +188 -0
  328. package/dist/scaffold/manifest-dsl.d.cts +8 -0
  329. package/dist/scaffold/manifest-dsl.d.cts.map +1 -0
  330. package/dist/scaffold/manifest-dsl.d.mts +8 -0
  331. package/dist/scaffold/manifest-dsl.d.mts.map +1 -0
  332. package/dist/scaffold/manifest-dsl.mjs +188 -0
  333. package/dist/scaffold/manifest-dsl.mjs.map +1 -0
  334. package/dist/scaffold/module-dsl.cjs +284 -0
  335. package/dist/scaffold/module-dsl.d.cts +8 -0
  336. package/dist/scaffold/module-dsl.d.cts.map +1 -0
  337. package/dist/scaffold/module-dsl.d.mts +8 -0
  338. package/dist/scaffold/module-dsl.d.mts.map +1 -0
  339. package/dist/scaffold/module-dsl.mjs +284 -0
  340. package/dist/scaffold/module-dsl.mjs.map +1 -0
  341. package/dist/scaffold/resolver.cjs +156 -0
  342. package/dist/scaffold/resolver.d.cts +7 -0
  343. package/dist/scaffold/resolver.d.cts.map +1 -0
  344. package/dist/scaffold/resolver.d.mts +7 -0
  345. package/dist/scaffold/resolver.d.mts.map +1 -0
  346. package/dist/scaffold/resolver.mjs +156 -0
  347. package/dist/scaffold/resolver.mjs.map +1 -0
  348. package/dist/scaffold/runtime.cjs +24 -0
  349. package/dist/scaffold/runtime.d.cts +19 -0
  350. package/dist/scaffold/runtime.d.cts.map +1 -0
  351. package/dist/scaffold/runtime.d.mts +19 -0
  352. package/dist/scaffold/runtime.d.mts.map +1 -0
  353. package/dist/scaffold/runtime.mjs +25 -0
  354. package/dist/scaffold/runtime.mjs.map +1 -0
  355. package/dist/scaffold/shell-dsl.cjs +279 -0
  356. package/dist/scaffold/shell-dsl.d.cts +8 -0
  357. package/dist/scaffold/shell-dsl.d.cts.map +1 -0
  358. package/dist/scaffold/shell-dsl.d.mts +8 -0
  359. package/dist/scaffold/shell-dsl.d.mts.map +1 -0
  360. package/dist/scaffold/shell-dsl.mjs +279 -0
  361. package/dist/scaffold/shell-dsl.mjs.map +1 -0
  362. package/dist/scaffold/surface-dsl.cjs +182 -0
  363. package/dist/scaffold/surface-dsl.d.cts +8 -0
  364. package/dist/scaffold/surface-dsl.d.cts.map +1 -0
  365. package/dist/scaffold/surface-dsl.d.mts +8 -0
  366. package/dist/scaffold/surface-dsl.d.mts.map +1 -0
  367. package/dist/scaffold/surface-dsl.mjs +182 -0
  368. package/dist/scaffold/surface-dsl.mjs.map +1 -0
  369. package/dist/scaffold/surfaces.cjs +84 -0
  370. package/dist/scaffold/surfaces.d.cts +23 -0
  371. package/dist/scaffold/surfaces.d.cts.map +1 -0
  372. package/dist/scaffold/surfaces.d.mts +24 -0
  373. package/dist/scaffold/surfaces.d.mts.map +1 -0
  374. package/dist/scaffold/surfaces.mjs +84 -0
  375. package/dist/scaffold/surfaces.mjs.map +1 -0
  376. package/dist/scaffold/types.d.cts +264 -0
  377. package/dist/scaffold/types.d.cts.map +1 -0
  378. package/dist/scaffold/types.d.mts +265 -0
  379. package/dist/scaffold/types.d.mts.map +1 -0
  380. package/dist/scaffold/workspace-dsl.cjs +339 -0
  381. package/dist/scaffold/workspace-dsl.d.cts +49 -0
  382. package/dist/scaffold/workspace-dsl.d.cts.map +1 -0
  383. package/dist/scaffold/workspace-dsl.d.mts +49 -0
  384. package/dist/scaffold/workspace-dsl.d.mts.map +1 -0
  385. package/dist/scaffold/workspace-dsl.mjs +337 -0
  386. package/dist/scaffold/workspace-dsl.mjs.map +1 -0
  387. package/dist/scope/index.d.mts +3 -0
  388. package/dist/scope/read-blocklet-scope.cjs +29 -0
  389. package/dist/scope/read-blocklet-scope.d.cts +8 -0
  390. package/dist/scope/read-blocklet-scope.d.cts.map +1 -0
  391. package/dist/scope/read-blocklet-scope.d.mts +8 -0
  392. package/dist/scope/read-blocklet-scope.d.mts.map +1 -0
  393. package/dist/scope/read-blocklet-scope.mjs +30 -0
  394. package/dist/scope/read-blocklet-scope.mjs.map +1 -0
  395. package/dist/scope/resolve-scoped-afs.cjs +87 -0
  396. package/dist/scope/resolve-scoped-afs.d.cts +7 -0
  397. package/dist/scope/resolve-scoped-afs.d.cts.map +1 -0
  398. package/dist/scope/resolve-scoped-afs.d.mts +7 -0
  399. package/dist/scope/resolve-scoped-afs.d.mts.map +1 -0
  400. package/dist/scope/resolve-scoped-afs.mjs +88 -0
  401. package/dist/scope/resolve-scoped-afs.mjs.map +1 -0
  402. package/dist/scope/types.d.cts +25 -0
  403. package/dist/scope/types.d.cts.map +1 -0
  404. package/dist/scope/types.d.mts +25 -0
  405. package/dist/scope/types.d.mts.map +1 -0
  406. package/dist/session/backends.cjs +90 -0
  407. package/dist/session/backends.d.cts +35 -0
  408. package/dist/session/backends.d.cts.map +1 -0
  409. package/dist/session/backends.d.mts +35 -0
  410. package/dist/session/backends.d.mts.map +1 -0
  411. package/dist/session/backends.mjs +89 -0
  412. package/dist/session/backends.mjs.map +1 -0
  413. package/dist/session/replication-resolver.cjs +323 -0
  414. package/dist/session/replication-resolver.d.cts +71 -0
  415. package/dist/session/replication-resolver.d.cts.map +1 -0
  416. package/dist/session/replication-resolver.d.mts +71 -0
  417. package/dist/session/replication-resolver.d.mts.map +1 -0
  418. package/dist/session/replication-resolver.mjs +323 -0
  419. package/dist/session/replication-resolver.mjs.map +1 -0
  420. package/dist/session/role-level.cjs +36 -0
  421. package/dist/session/role-level.d.cts +24 -0
  422. package/dist/session/role-level.d.cts.map +1 -0
  423. package/dist/session/role-level.d.mts +24 -0
  424. package/dist/session/role-level.d.mts.map +1 -0
  425. package/dist/session/role-level.mjs +35 -0
  426. package/dist/session/role-level.mjs.map +1 -0
  427. package/dist/session/session-dir-providers.cjs +254 -0
  428. package/dist/session/session-dir-providers.d.cts +32 -0
  429. package/dist/session/session-dir-providers.d.cts.map +1 -0
  430. package/dist/session/session-dir-providers.d.mts +32 -0
  431. package/dist/session/session-dir-providers.d.mts.map +1 -0
  432. package/dist/session/session-dir-providers.mjs +252 -0
  433. package/dist/session/session-dir-providers.mjs.map +1 -0
  434. package/dist/session/session-id.cjs +20 -0
  435. package/dist/session/session-id.d.cts +15 -0
  436. package/dist/session/session-id.d.cts.map +1 -0
  437. package/dist/session/session-id.d.mts +15 -0
  438. package/dist/session/session-id.d.mts.map +1 -0
  439. package/dist/session/session-id.mjs +20 -0
  440. package/dist/session/session-id.mjs.map +1 -0
  441. package/dist/session/session-user-afs.cjs +899 -0
  442. package/dist/session/session-user-afs.d.cts +296 -0
  443. package/dist/session/session-user-afs.d.cts.map +1 -0
  444. package/dist/session/session-user-afs.d.mts +296 -0
  445. package/dist/session/session-user-afs.d.mts.map +1 -0
  446. package/dist/session/session-user-afs.mjs +896 -0
  447. package/dist/session/session-user-afs.mjs.map +1 -0
  448. package/dist/session/settings-cascade.cjs +69 -0
  449. package/dist/session/settings-cascade.d.cts +81 -0
  450. package/dist/session/settings-cascade.d.cts.map +1 -0
  451. package/dist/session/settings-cascade.d.mts +81 -0
  452. package/dist/session/settings-cascade.d.mts.map +1 -0
  453. package/dist/session/settings-cascade.mjs +69 -0
  454. package/dist/session/settings-cascade.mjs.map +1 -0
  455. package/dist/session/settings-resolver.cjs +770 -0
  456. package/dist/session/settings-resolver.d.cts +177 -0
  457. package/dist/session/settings-resolver.d.cts.map +1 -0
  458. package/dist/session/settings-resolver.d.mts +177 -0
  459. package/dist/session/settings-resolver.d.mts.map +1 -0
  460. package/dist/session/settings-resolver.mjs +771 -0
  461. package/dist/session/settings-resolver.mjs.map +1 -0
  462. package/dist/session/settings-schema-store.cjs +0 -0
  463. package/dist/session/settings-schema-store.d.cts +72 -0
  464. package/dist/session/settings-schema-store.d.cts.map +1 -0
  465. package/dist/session/settings-schema-store.d.mts +72 -0
  466. package/dist/session/settings-schema-store.d.mts.map +1 -0
  467. package/dist/session/settings-schema-store.mjs +0 -0
  468. package/dist/session/settings-schema-store.mjs.map +1 -0
  469. package/dist/system-mounts/index.cjs +14 -0
  470. package/dist/system-mounts/index.d.cts +6 -0
  471. package/dist/system-mounts/index.d.mts +6 -0
  472. package/dist/system-mounts/index.mjs +6 -0
  473. package/dist/system-mounts/install.cjs +101 -0
  474. package/dist/system-mounts/install.d.cts +36 -0
  475. package/dist/system-mounts/install.d.cts.map +1 -0
  476. package/dist/system-mounts/install.d.mts +37 -0
  477. package/dist/system-mounts/install.d.mts.map +1 -0
  478. package/dist/system-mounts/install.mjs +102 -0
  479. package/dist/system-mounts/install.mjs.map +1 -0
  480. package/dist/system-mounts/installers.cjs +247 -0
  481. package/dist/system-mounts/installers.d.cts +13 -0
  482. package/dist/system-mounts/installers.d.cts.map +1 -0
  483. package/dist/system-mounts/installers.d.mts +14 -0
  484. package/dist/system-mounts/installers.d.mts.map +1 -0
  485. package/dist/system-mounts/installers.mjs +244 -0
  486. package/dist/system-mounts/installers.mjs.map +1 -0
  487. package/dist/system-mounts/lists.cjs +122 -0
  488. package/dist/system-mounts/lists.d.cts +34 -0
  489. package/dist/system-mounts/lists.d.cts.map +1 -0
  490. package/dist/system-mounts/lists.d.mts +34 -0
  491. package/dist/system-mounts/lists.d.mts.map +1 -0
  492. package/dist/system-mounts/lists.mjs +122 -0
  493. package/dist/system-mounts/lists.mjs.map +1 -0
  494. package/dist/system-mounts/types.d.cts +124 -0
  495. package/dist/system-mounts/types.d.cts.map +1 -0
  496. package/dist/system-mounts/types.d.mts +125 -0
  497. package/dist/system-mounts/types.d.mts.map +1 -0
  498. package/dist/system-mounts/validate.cjs +56 -0
  499. package/dist/system-mounts/validate.d.cts +34 -0
  500. package/dist/system-mounts/validate.d.cts.map +1 -0
  501. package/dist/system-mounts/validate.d.mts +34 -0
  502. package/dist/system-mounts/validate.d.mts.map +1 -0
  503. package/dist/system-mounts/validate.mjs +56 -0
  504. package/dist/system-mounts/validate.mjs.map +1 -0
  505. package/dist/terminal-llm.cjs +90 -0
  506. package/dist/terminal-llm.d.cts +50 -0
  507. package/dist/terminal-llm.d.cts.map +1 -0
  508. package/dist/terminal-llm.d.mts +50 -0
  509. package/dist/terminal-llm.d.mts.map +1 -0
  510. package/dist/terminal-llm.mjs +90 -0
  511. package/dist/terminal-llm.mjs.map +1 -0
  512. package/dist/terminal-repl.cjs +371 -0
  513. package/dist/terminal-repl.d.cts +69 -0
  514. package/dist/terminal-repl.d.cts.map +1 -0
  515. package/dist/terminal-repl.d.mts +69 -0
  516. package/dist/terminal-repl.d.mts.map +1 -0
  517. package/dist/terminal-repl.mjs +370 -0
  518. package/dist/terminal-repl.mjs.map +1 -0
  519. package/dist/transport/idle-timer.cjs +136 -0
  520. package/dist/transport/idle-timer.d.cts +92 -0
  521. package/dist/transport/idle-timer.d.cts.map +1 -0
  522. package/dist/transport/idle-timer.d.mts +92 -0
  523. package/dist/transport/idle-timer.d.mts.map +1 -0
  524. package/dist/transport/idle-timer.mjs +132 -0
  525. package/dist/transport/idle-timer.mjs.map +1 -0
  526. package/dist/web-component-dsl.cjs +153 -0
  527. package/dist/web-component-dsl.d.cts +27 -0
  528. package/dist/web-component-dsl.d.cts.map +1 -0
  529. package/dist/web-component-dsl.d.mts +27 -0
  530. package/dist/web-component-dsl.d.mts.map +1 -0
  531. package/dist/web-component-dsl.mjs +152 -0
  532. package/dist/web-component-dsl.mjs.map +1 -0
  533. package/package.json +81 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-user-afs.mjs","names":["result"],"sources":["../../src/session/session-user-afs.ts"],"sourcesContent":["/**\n * SessionUserAFS — per-session proxy that overlays a caller's own per-user\n * data at `/user`, an optional caller DID Space root at `/space`, AND the\n * session's private scratch space at `/tmp` on top of a blocklet's (shared,\n * per-instance) Small World runtime AFS.\n *\n * Why: the live session binds the per-instance Small World AFS (which owns\n * blocklet execution — actions, agent runtime, /instance, /packages). Two\n * logical roots the architecture (blocklet-structure.md §6.2 + the\n * did-space-alignment P4 decision) places at the blocklet's view root are NOT\n * present on that Small World:\n *\n * /user → the caller's own per-user data (DID Space role:\"user\"), mounted\n * on the HOST flat surface as `/blocklets/<name>/users/<did>`.\n * /tmp → the session's persistent scratch, created by joinSession via\n * sessionTmpBackend and mounted on the HOST globalAFS at\n * `/blocklets/<name>/instance/sessions/<sid>/tmp`.\n * /space → when explicitly enabled by the runtime for `scope:user`, the\n * caller's whole DID Space root (normally readonly).\n *\n * The Small World owns its own `/instance` projection and does NOT see\n * globalAFS mounts under `/blocklets/<name>/`, so these roots are not reachable\n * through it. Mounting them into the shared Small World would leak across\n * concurrent sessions of different callers.\n *\n * SessionUserAFS solves this by being constructed PER SESSION with exactly one\n * caller's `/user` provider, optional `/space` root provider, and that session's\n * `/tmp` provider:\n *\n * session.read(\"/user/app/n.txt\") → userProvider.read(\"/app/n.txt\")\n * session.read(\"/tmp/foo\") → tmpProvider.read(\"/foo\")\n * session.read(\"/space/blocklets\") → spaceProvider.read(\"/blocklets\")\n * session.read(\"/instance/...\") → smallWorld.read(\"/instance/...\") (delegate)\n *\n * A session only ever sees its own `/user` + `/space` + `/tmp` — there is no\n * `/users/<did>` enumeration surface in-session, so cross-DID isolation is\n * structural, not enforced by a filter. Anonymous sessions get no\n * `userProvider`, so `/user` resolves to NotFound (write rejected) — matching\n * the \"anonymous skips users/\" contract. When the runtime explicitly enables\n * `/space` for `scope:user` but no caller/root is available, `/space` also\n * resolves to NotFound rather than falling through to base. `/tmp` is present\n * for every joined session.\n *\n * Root overlays are first-class: enumerable via `list(\"/\")`, statable as\n * directories at their root, and routable for read/write/delete/exec — honoring\n * the \"inspection is a contract\" rule (anything stored MUST be enumerable).\n *\n * INVARIANT — single construction site: this class MUST be instantiated only\n * via `buildSessionView()` in `core/commands/daemon.ts`, which every request\n * entry point that serves a blocklet AFS to a caller routes through (WS session\n * bind + HTTP `/api/afs/rpc` overlay today). Do NOT `new SessionUserAFS(...)`\n * directly at a new entry point — call `buildSessionView` so the per-caller /\n * per-session resolution stays in one place (a divergent call site is exactly\n * what dropped `/user`+`/tmp` for named instances before).\n */\n\nimport type {\n AFSBatchDeleteEntry,\n AFSBatchDeleteResult,\n AFSBatchReadEntry,\n AFSBatchReadResult,\n AFSBatchWriteEntry,\n AFSBatchWriteResult,\n AFSCollectionQueryResult,\n AFSCollectionQuerySpec,\n AFSContext,\n AFSDeleteOptions,\n AFSDeleteResult,\n AFSEntry,\n AFSEventCallback,\n AFSEventFilter,\n AFSExecOptions,\n AFSExecResult,\n AFSExplainOptions,\n AFSExplainResult,\n AFSListOptions,\n AFSListResult,\n AFSModule,\n AFSQueryOptions,\n AFSReadOptions,\n AFSReadResult,\n AFSRoot,\n AFSSearchOptions,\n AFSSearchResult,\n AFSStatOptions,\n AFSStatResult,\n AFSUnsubscribe,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n AFSWriteResult,\n MountInfo,\n} from \"@aigne/afs\";\nimport {\n AFSDependencyError,\n AFSForbiddenError,\n AFSNotFoundError,\n AFSUnsupportedError,\n AFSValidationError,\n enforceEffectGate,\n execReflexiveRootDataAction,\n isReflexiveRootDataAction,\n makeNsLog,\n rootActionEffect,\n toBatchEntryFailure,\n} from \"@aigne/afs\";\nimport { joinURL } from \"ufo\";\nimport { computeReplicationMountPrefix, ReplicationResolver } from \"./replication-resolver.js\";\nimport { SettingsResolver } from \"./settings-resolver.js\";\n\nconst log = makeNsLog(\"aos\");\n\n/**\n * Concurrency cap for hydrating index-search results with their real\n * `read()`-backed content (P5 search→index forward). Mirrors the existing\n * `SEARCH_CONCURRENCY = 8` in `packages/core/src/afs.ts` — same fan-out\n * shape (N paths → N reads), same bound (avoid pathological fan-out against\n * the per-caller DID Space backend on a single search).\n */\nconst INDEX_SEARCH_HYDRATE_CONCURRENCY = 8;\n\n/**\n * Minimal concurrency-bounded map — same small helper duplicated at each of\n * its other call sites (`packages/core/src/afs.ts`, `provider/base.ts`,\n * `blocklet/data-seed.ts`, `aos/src/http/handlers.ts`); not exported from a\n * shared module today, so this follows the existing convention rather than\n * introducing a new one.\n */\nasync function mapWithConcurrency<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let nextIndex = 0;\n const workerCount = Math.min(Math.max(1, concurrency), items.length);\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n while (nextIndex < items.length) {\n const index = nextIndex++;\n results[index] = await fn(items[index]!, index);\n }\n }),\n );\n return results;\n}\n\ninterface Overlay {\n /** Mount prefix, e.g. `/user` or `/tmp`. */\n prefix: string;\n /** Synthetic entry id used for the root directory entry, e.g. `user`. */\n id: string;\n /** Backing provider, or null when explicitly unavailable (anonymous `/user`/`/space`). */\n provider: AFSModule | null;\n /**\n * Whether this overlay is a ROOT-level mount (`/user`, `/tmp`) that should\n * be injected into `list(\"/\")` and reported by `getMounts`. A nested-path\n * interceptor like `/instance/settings` (which lives *under* a base mount)\n * sets this false — it's reachable by traversing base's `/instance`, not as a\n * sibling of `/instance`, so injecting it at root would fabricate a bogus\n * top-level entry.\n */\n rootVisible: boolean;\n}\n\n/**\n * STATIC full set of overlay prefixes a SessionUserAFS may mount — the\n * anonymous-bearer denylist for signed raw URLs (afs-preview design §3).\n *\n * Deliberately NOT derived from an instance's `overlays` array: `/space` and\n * `/instance/settings` are conditionally mounted, so per-instance derivation\n * would drift with wiring. Fail-closed: an anonymous raw URL is refused for\n * these prefixes even when the issuing session didn't mount them.\n *\n * KEEP IN SYNC with the constructor below — the unit test enumerates this\n * set against the constructor's possible mounts, table-driven.\n */\nexport const SESSION_OVERLAY_PREFIXES = [\n \"/user/index\",\n \"/user\",\n \"/tmp\",\n \"/space\",\n \"/instance/settings\",\n] as const;\n\n/**\n * Whether a (sanitized) path falls under any session-private overlay prefix.\n * Used by the raw-URL primitives to refuse anonymous bearer URLs for\n * session-private content (issue-side denial + verify-side second defense).\n */\nexport function isSessionPrivatePath(path: string): boolean {\n return SESSION_OVERLAY_PREFIXES.some((p) => path === p || path.startsWith(`${p}/`));\n}\n\n/**\n * Guard the first-match-by-prefix invariant: no overlay may be preceded by\n * another whose prefix is a proper prefix of it (which would swallow its\n * paths). `/instance/settings` MUST come before any future `/instance` or `/`\n * overlay. Extracted + exported so the ordering contract is unit-testable\n * without reaching into the class. Throws on violation (a wiring bug).\n */\nexport function assertOverlayOrder(prefixes: string[]): void {\n for (let i = 0; i < prefixes.length; i++) {\n const current = prefixes[i];\n if (current === undefined) continue;\n for (let j = 0; j < i; j++) {\n const earlier = prefixes[j];\n if (earlier === undefined) continue;\n // Root \"/\" is a prefix of every path; otherwise compare against `${earlier}/`.\n const earlierAsDir = earlier === \"/\" ? \"/\" : `${earlier}/`;\n if (current === earlier || current.startsWith(earlierAsDir)) {\n throw new Error(\n `SessionUserAFS overlay order bug: \"${earlier}\" precedes and shadows \"${current}\" (first-match routing)`,\n );\n }\n }\n }\n}\n\n/** Strip an overlay prefix → provider-relative path (always leading-slash). */\nfunction subPath(normalized: string, prefix: string): string {\n if (normalized === prefix) return \"/\";\n const rest = normalized.slice(prefix.length);\n return rest.startsWith(\"/\") ? rest : `/${rest}`;\n}\n\n/**\n * Synthetic directory entry for an overlay root, mirroring a real mount root.\n * `childrenCount: -1` (unknown) + `accessMode` live under `meta`, matching the\n * shape a real mount returns in `list(\"/\")` / `stat()`.\n */\nfunction overlayRootEntry(overlay: Overlay): AFSEntry {\n return {\n id: overlay.id,\n path: overlay.prefix,\n meta: { childrenCount: -1, accessMode: overlay.provider?.accessMode ?? \"readwrite\" },\n };\n}\n\n/**\n * Optional render-snapshot boundary an overlay provider may implement\n * (settings-layering §2.B). Kept structural so SessionUserAFS can proxy to a\n * SettingsResolver without importing its concrete type in the signature.\n */\ninterface RenderBoundary {\n beginRender?(renderId: string): Promise<void> | void;\n endRender?(renderId: string): void;\n}\n\nexport class SessionUserAFS implements AFSRoot {\n readonly name: string;\n readonly accessMode: AFSRoot[\"accessMode\"] = \"readwrite\";\n\n private readonly overlays: Overlay[];\n\n /**\n * Named reference to the settings resolver overlay (the overlay table only\n * stores it anonymously) so `beginRender`/`endRender` can proxy to it —\n * settings-layering §2.B render boundary.\n */\n private readonly settingsResolver: (AFSModule & RenderBoundary) | null;\n\n constructor(\n private readonly base: AFSRoot,\n /** The caller's per-user provider (userDataBackend output), or null for anonymous. */\n userProvider: AFSModule | null,\n /** The session's `/tmp` provider (joinSession's sessionTmpBackend output), or null. */\n tmpProvider: AFSModule | null,\n sessionId: string,\n /**\n * The per-session SettingsResolver intercepting `/instance/settings/**`\n * (instance+user settings cascade), or null when settings aren't wired.\n */\n settingsResolver: AFSModule | null = null,\n /**\n * The caller's whole DID Space root provider. Omit to leave `/space`\n * untouched; pass null to explicitly reserve `/space` and fail closed.\n */\n spaceProvider?: AFSModule | null,\n /**\n * The caller's per-user semantic index (`@aigne/afs-index`) at `/user/index`.\n * Omit to leave `/user/index` untouched (falls through to the `/user`\n * provider); pass null to reserve it and fail closed. Mounted BEFORE `/user`\n * so child-first routing sends `/user/index/**` here, mirroring how\n * `/instance/settings` precedes `/instance`.\n */\n indexProvider?: AFSModule | null,\n /**\n * Replicated-collection overlays built by createSessionView when the blocklet\n * manifest declares `replicated` collections. Each entry is mounted at its\n * `prefix` (e.g. `/instance`) AFTER the `/instance/settings` interceptor so\n * child-first routing sends `/instance/settings/**` to the SettingsResolver\n * and everything else in the prefix to the ReplicationResolver.\n */\n replicationOverlays?: Array<{ prefix: string; id: string; provider: AFSModule | null }>,\n /**\n * True when the blocklet manifest declares `index:` and `/user` search is\n * expected to be served by `/user/index`. Kept separate from the\n * `/user/index` overlay itself because runtimes reserve that path with a\n * null provider even for no-index blocklets to make direct `/user/index`\n * access fail closed.\n */\n private readonly indexSearchRequired: boolean = indexProvider != null,\n ) {\n this.name = `session-user:${sessionId}`;\n this.overlays = [];\n // /user/index MUST precede /user (child-first): assertOverlayOrder rejects a\n // parent prefix appearing before its child, and matchOverlay is first-match.\n if (indexProvider !== undefined) {\n this.overlays.push({\n prefix: \"/user/index\",\n id: \"user-index\",\n provider: indexProvider,\n rootVisible: false,\n });\n }\n this.overlays.push(\n { prefix: \"/user\", id: \"user\", provider: userProvider, rootVisible: true },\n { prefix: \"/tmp\", id: \"tmp\", provider: tmpProvider, rootVisible: true },\n );\n if (spaceProvider !== undefined) {\n this.overlays.push({\n prefix: \"/space\",\n id: \"space\",\n provider: spaceProvider,\n rootVisible: true,\n });\n }\n this.settingsResolver = settingsResolver;\n // The /instance/settings interceptor is added ONLY when a resolver is wired.\n // Unlike /user (where a null provider intentionally rejects anonymous\n // writes), an unwired settings overlay must be transparent — its paths fall\n // through to base, preserving pre-P2 behavior.\n if (settingsResolver) {\n this.overlays.push({\n prefix: \"/instance/settings\",\n id: \"instance-settings\",\n provider: settingsResolver,\n rootVisible: false,\n });\n }\n // Replicated-collection overlays come AFTER /instance/settings so\n // child-first routing preserves: /instance/settings → SettingsResolver,\n // /instance/<collection>/** → ReplicationResolver (assertOverlayOrder\n // verifies /instance/settings precedes /instance).\n for (const ro of replicationOverlays ?? []) {\n this.overlays.push({ ...ro, rootVisible: false });\n }\n assertOverlayOrder(this.overlays.map((o) => o.prefix));\n }\n\n /** Match a path against the overlay table. Returns the overlay + sub-path. */\n private matchOverlay(path: string): { overlay: Overlay; sub: string } | null {\n const normalized = path.startsWith(\"/\") ? path : `/${path}`;\n for (const overlay of this.overlays) {\n if (normalized === overlay.prefix || normalized.startsWith(`${overlay.prefix}/`)) {\n return { overlay, sub: subPath(normalized, overlay.prefix) };\n }\n }\n return null;\n }\n\n /** Overlays that are actually backed (provider present). */\n private get activeOverlays(): Overlay[] {\n return this.overlays.filter((o) => o.provider != null);\n }\n\n /** Backed AND root-level overlays — the ones surfaced at `/` / in getMounts. */\n private get rootOverlays(): Overlay[] {\n return this.overlays.filter((o) => o.provider != null && o.rootVisible);\n }\n\n private rejectMissing(path: string): never {\n throw new AFSNotFoundError(path);\n }\n\n /**\n * Whether a write context originates from an untrusted network client.\n * `callerOrigin` is a host-stamped, unforgeable marker (strip + trusted reset,\n * see FORGEABLE_CONTEXT_FIELDS); internal code (seeding / cron / agent /\n * operator CLI) carries no `callerOrigin`. Mirrors the reflexive effect gate's\n * network check (`exec-effect-gate.ts`).\n */\n private isNetworkOrigin(context: AFSContext | undefined): boolean {\n return context?.callerOrigin === \"network\";\n }\n\n /**\n * Caller-origin base-write gate (instance-write-authz §5.1). A network client\n * may never bare-write a BASE-fallthrough path — any path NOT matched by an\n * overlay (`/instance/<non-mediated>`, `/packages`, `/dev`, `/registry`, `/`).\n * Authorized `/instance` writes must go through a resolver overlay\n * (`/instance/settings` → SettingsResolver, future `/instance/app` →\n * ReplicationResolver — each carries its own role/minRole gate) or internal\n * trusted code (`callerOrigin: undefined`).\n *\n * Called ONLY on the base-fallthrough branch (overlay-matched paths never\n * reach here). STRICTER than the effect gate: it rejects AUTHENTICATED network\n * callers too (the effect gate lets `caller.did` through) — that authed-but-\n * network reflexive base write is exactly Finding 1's live path.\n */\n private forbiddenBaseWrite(path: string): AFSForbiddenError {\n return new AFSForbiddenError(\n path,\n \"network clients cannot write base paths; use a resolver overlay or internal code\",\n );\n }\n\n private enforceBaseWriteGate(path: string, context: AFSContext | undefined): void {\n if (this.isNetworkOrigin(context)) throw this.forbiddenBaseWrite(path);\n }\n\n /** Re-prefix a provider-relative path back into the overlay namespace. */\n private reprefix(prefix: string, childPath: string): string {\n return childPath === \"/\" ? prefix : joinURL(prefix, childPath);\n }\n\n async list(path: string, options?: AFSListOptions): Promise<AFSListResult> {\n const match = this.matchOverlay(path);\n if (match) {\n const { overlay, sub } = match;\n if (!overlay.provider?.list) this.rejectMissing(path);\n const result = await overlay.provider.list(sub, options);\n // Re-prefix child paths back into the overlay namespace.\n return {\n ...result,\n data: (result.data ?? []).map((e) => ({\n ...e,\n path: this.reprefix(overlay.prefix, e.path),\n })),\n };\n }\n const result = await this.base.list(path, options);\n // At the session root, inject the overlay roots so they are enumerable\n // alongside the Small World mounts (inspection-is-a-contract).\n const normalized = path.startsWith(\"/\") ? path : `/${path}`;\n if (normalized === \"/\") {\n const existing = new Set((result.data ?? []).map((e) => e.path));\n const injected = this.rootOverlays\n .filter((o) => !existing.has(o.prefix))\n .map((o) => overlayRootEntry(o));\n if (injected.length) {\n return { ...result, data: [...(result.data ?? []), ...injected] };\n }\n }\n return result;\n }\n\n async read(path: string, options?: AFSReadOptions): Promise<AFSReadResult> {\n options = this.withSelfContext(options);\n const match = this.matchOverlay(path);\n if (match) {\n if (!match.overlay.provider?.read) this.rejectMissing(path);\n return match.overlay.provider.read(match.sub, options);\n }\n if (!this.base.read) throw new Error(\"base.read not supported\");\n return this.base.read(path, options);\n }\n\n /**\n * Overlay-dispatch for `presignRead` (preview-r2-direct §5a.2 / T1.2). Same\n * routing as `read`: a matched overlay whose provider implements `presignRead`\n * gets the sub-path; a base-fallthrough path (or an overlay provider without\n * the capability) → `null` (fail-soft → the raw handler serves bytes itself).\n *\n * The meta-only ACL and the `null`-on-everything fail-soft live DEEPER (the\n * `/user` overlay provider is a `ProjectionProvider` → core `AFS` (globalAFS),\n * where the authoritative meta guard runs — §5a.2 \"routing to the root\n * computation\"); this layer only forwards.\n *\n * This is the HANDLER-FACING surface (`scoped.afs`), so it owns the §5a.5\n * \"NEVER throws to the handler\" guarantee end-to-end: a deeper layer that\n * throws (e.g. a read-disabled `ProjectionProvider.checkOp(\"read\")` →\n * `AFSAccessModeError`) is caught here → `null` → the raw handler serves bytes\n * itself (and the worker-serve read then re-applies that same gate). No URL\n * can leak past a read-disabled / failing projection.\n */\n async presignRead(\n path: string,\n opts?: { responseContentType?: string },\n ): Promise<{ url: string; expiresAt: string } | null> {\n try {\n const match = this.matchOverlay(path);\n if (match) {\n const provider = match.overlay.provider;\n if (typeof provider?.presignRead !== \"function\") return null;\n return (await provider.presignRead(match.sub, opts)) ?? null;\n }\n if (typeof this.base.presignRead !== \"function\") return null;\n return (await this.base.presignRead(path, opts)) ?? null;\n } catch {\n return null;\n }\n }\n\n async write(\n path: string,\n content: AFSWriteEntryPayload,\n options?: AFSWriteOptions,\n ): Promise<AFSWriteResult> {\n options = this.withSelfContext(options);\n const match = this.matchOverlay(path);\n if (match) {\n if (!match.overlay.provider?.write) this.rejectMissing(path);\n return match.overlay.provider.write(match.sub, content, options);\n }\n // Base fallthrough (no overlay matched): gate network clients out of bare\n // base writes (instance-write-authz §5.1).\n this.enforceBaseWriteGate(path, (options as { context?: AFSContext })?.context);\n if (!this.base.write) throw new Error(\"base.write not supported\");\n return this.base.write(path, content, options);\n }\n\n async delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult> {\n options = this.withSelfContext(options);\n const match = this.matchOverlay(path);\n if (match) {\n if (!match.overlay.provider?.delete) this.rejectMissing(path);\n return match.overlay.provider.delete(match.sub, options);\n }\n // Base fallthrough: same network gate as write (instance-write-authz §5.1).\n this.enforceBaseWriteGate(path, (options as { context?: AFSContext })?.context);\n if (!this.base.delete) throw new Error(\"base.delete not supported\");\n return this.base.delete(path, options);\n }\n\n // ── batch surface (afs-rpc-batch P2) ──────────────────────────────────\n //\n // The L2 wire switch sends batch ops to `afs.batchWrite/...` on THIS façade\n // (the per-request overlay is what serves /api/afs/rpc). Without these\n // forwards the overlay would 501 every batch — and worse, the DID-Space L3\n // pushdown (P3) could never be reached through `/user`. Semantics per run:\n // contiguous entries matching the SAME overlay go to that provider's native\n // batch method when it exists (pushdown), else loop the façade's own\n // single-op methods (exact single-op parity, including overlay rejection\n // semantics); non-overlay runs delegate to `base.batch*` (the core AFS —\n // full routing/permission/event machinery).\n\n /** Plan contiguous runs of entry indices by overlay identity (null = base). */\n private planOverlayRuns(paths: string[]): Array<{ overlay: Overlay | null; indices: number[] }> {\n const runs: Array<{ overlay: Overlay | null; indices: number[] }> = [];\n for (let i = 0; i < paths.length; i++) {\n const overlay = this.matchOverlay(paths[i]!)?.overlay ?? null;\n const last = runs[runs.length - 1];\n if (last && last.overlay === overlay) last.indices.push(i);\n else runs.push({ overlay, indices: [i] });\n }\n return runs;\n }\n\n async batchWrite(\n entries: AFSBatchWriteEntry[],\n options?: import(\"@aigne/afs\").AFSOperationOptions,\n ): Promise<AFSBatchWriteResult> {\n const opts = this.withSelfContext(options ?? {});\n const ctx = (opts as { context?: import(\"@aigne/afs\").AFSContext }).context;\n const results: AFSBatchWriteResult[\"results\"] = new Array(entries.length);\n\n for (const run of this.planOverlayRuns(entries.map((e) => e.path))) {\n const provider = run.overlay?.provider;\n if (run.overlay && provider && typeof provider.batchWrite === \"function\") {\n // Native pushdown through the overlay provider (sub-paths in, overlay\n // namespace back out) — the P3 DID-Space merge rides this.\n const subEntries = run.indices.map((i) => ({\n ...entries[i]!,\n path: subPath(entries[i]!.path, run.overlay!.prefix),\n options: { ...entries[i]!.options, context: ctx },\n }));\n try {\n const r = await provider.batchWrite(subEntries, { context: ctx });\n if (!Array.isArray(r?.results) || r.results.length !== subEntries.length) {\n throw new Error(\n `Provider '${provider.name}' batchWrite returned ${r?.results?.length ?? 0} results for ${subEntries.length} entries`,\n );\n }\n run.indices.forEach((i, j) => {\n const entryResult = r.results[j]!;\n const data = entryResult.data\n ? {\n ...entryResult.data,\n path: this.reprefix(run.overlay!.prefix, entryResult.data.path),\n }\n : entryResult.data;\n results[i] = { ...entryResult, path: entries[i]!.path, data };\n });\n } catch (err) {\n for (const i of run.indices) results[i] = toBatchEntryFailure(entries[i]!.path, err);\n }\n continue;\n }\n if (!run.overlay && typeof this.base.batchWrite === \"function\") {\n // Network clients are gated out of base writes PER-ENTRY (not whole-batch\n // throw): a mixed batch may carry both `/user` overlay runs and base\n // `/instance` runs — failing the whole batch would wrongly kill the\n // `/user` writes (instance-write-authz §5.1 / T1.3). The native\n // base.batchWrite branch must check explicitly; the loop fallback below\n // already routes through `this.write` (gated by T1.2).\n if (this.isNetworkOrigin(ctx)) {\n for (const i of run.indices) {\n results[i] = toBatchEntryFailure(\n entries[i]!.path,\n this.forbiddenBaseWrite(entries[i]!.path),\n );\n }\n continue;\n }\n // Base run → core AFS batch (its own routing/permission/events).\n const baseEntries = run.indices.map((i) => entries[i]!);\n const r = await this.base.batchWrite(baseEntries, { context: ctx });\n run.indices.forEach((i, j) => {\n results[i] = r.results[j]!;\n });\n continue;\n }\n // Loop fallback: exact single-op semantics (incl. overlay rejection).\n for (const i of run.indices) {\n const entry = entries[i]!;\n try {\n const payload: AFSWriteEntryPayload = entry.content ?? {};\n if (entry.patches) payload.patches = entry.patches;\n const res = await this.write(entry.path, payload, {\n ...entry.options,\n mode: entry.mode ?? entry.options?.mode,\n context: ctx,\n });\n results[i] = { path: entry.path, success: true, data: res.data };\n } catch (err) {\n results[i] = toBatchEntryFailure(entry.path, err);\n }\n }\n }\n\n const succeeded = results.filter((r) => r?.success).length;\n return { results, succeeded, failed: results.length - succeeded };\n }\n\n async batchDelete(\n entries: AFSBatchDeleteEntry[],\n options?: import(\"@aigne/afs\").AFSOperationOptions,\n ): Promise<AFSBatchDeleteResult> {\n const opts = this.withSelfContext(options ?? {});\n const ctx = (opts as { context?: import(\"@aigne/afs\").AFSContext }).context;\n const results: AFSBatchDeleteResult[\"results\"] = new Array(entries.length);\n\n for (const run of this.planOverlayRuns(entries.map((e) => e.path))) {\n const provider = run.overlay?.provider;\n if (run.overlay && provider && typeof provider.batchDelete === \"function\") {\n const subEntries = run.indices.map((i) => ({\n ...entries[i]!,\n path: subPath(entries[i]!.path, run.overlay!.prefix),\n options: { ...entries[i]!.options, context: ctx },\n }));\n try {\n const r = await provider.batchDelete(subEntries, { context: ctx });\n if (!Array.isArray(r?.results) || r.results.length !== subEntries.length) {\n throw new Error(\n `Provider '${provider.name}' batchDelete returned ${r?.results?.length ?? 0} results for ${subEntries.length} entries`,\n );\n }\n run.indices.forEach((i, j) => {\n results[i] = { ...r.results[j]!, path: entries[i]!.path };\n });\n } catch (err) {\n for (const i of run.indices) results[i] = toBatchEntryFailure(entries[i]!.path, err);\n }\n continue;\n }\n if (!run.overlay && typeof this.base.batchDelete === \"function\") {\n // Same per-entry network gate as batchWrite (instance-write-authz §5.1 /\n // T1.3) — preserve mixed-batch contract; loop fallback below is gated via\n // `this.delete` (T1.2).\n if (this.isNetworkOrigin(ctx)) {\n for (const i of run.indices) {\n results[i] = toBatchEntryFailure(\n entries[i]!.path,\n this.forbiddenBaseWrite(entries[i]!.path),\n );\n }\n continue;\n }\n const baseEntries = run.indices.map((i) => entries[i]!);\n const r = await this.base.batchDelete(baseEntries, { context: ctx });\n run.indices.forEach((i, j) => {\n results[i] = r.results[j]!;\n });\n continue;\n }\n for (const i of run.indices) {\n const entry = entries[i]!;\n try {\n await this.delete(entry.path, {\n ...entry.options,\n recursive: entry.recursive ?? entry.options?.recursive,\n context: ctx,\n });\n results[i] = { path: entry.path, success: true };\n } catch (err) {\n results[i] = toBatchEntryFailure(entry.path, err);\n }\n }\n }\n\n const succeeded = results.filter((r) => r?.success).length;\n return { results, succeeded, failed: results.length - succeeded };\n }\n\n async batchRead(\n entries: AFSBatchReadEntry[],\n options?: import(\"@aigne/afs\").AFSOperationOptions,\n ): Promise<AFSBatchReadResult> {\n const ctx = (\n this.withSelfContext(options ?? {}) as {\n context?: import(\"@aigne/afs\").AFSContext;\n }\n ).context;\n const results: AFSBatchReadResult[\"results\"] = [];\n let succeeded = 0;\n for (const entry of entries) {\n try {\n const res = await this.read(entry.path, { ...entry.options, context: ctx });\n results.push({ path: entry.path, success: true, data: res.data });\n succeeded++;\n } catch (err) {\n results.push(toBatchEntryFailure(entry.path, err));\n }\n }\n return { results, succeeded, failed: results.length - succeeded };\n }\n\n async query(\n path: string,\n spec: AFSCollectionQuerySpec,\n options?: AFSQueryOptions,\n ): Promise<AFSCollectionQueryResult> {\n const match = this.matchOverlay(path);\n if (match) {\n const { overlay, sub } = match;\n // Same guard shape as read/list: an overlay path never falls through\n // to base (anonymous `/user` has a null provider → NotFound), and a\n // backing provider without the hook is UNSUPPORTED, not a silent base\n // re-resolve — that re-resolve is exactly the `/user` isolation bypass.\n if (!overlay.provider) this.rejectMissing(path);\n if (!overlay.provider.query) {\n throw new AFSUnsupportedError(\n \"query\",\n `Provider for ${overlay.prefix} does not declare the collection query capability`,\n );\n }\n const result = await overlay.provider.query(sub, spec, options);\n if (!result?.entries?.length) return result;\n return {\n ...result,\n entries: result.entries.map((e) => ({\n ...e,\n path: this.reprefix(overlay.prefix, e.path),\n })),\n };\n }\n if (!this.base.query) {\n throw new AFSUnsupportedError(\"query\", \"base.query not supported\");\n }\n return this.base.query(path, spec, options);\n }\n\n async stat(path: string, options?: AFSStatOptions): Promise<AFSStatResult> {\n options = this.withSelfContext(options);\n const match = this.matchOverlay(path);\n if (match) {\n const { overlay, sub } = match;\n if (!overlay.provider) this.rejectMissing(path);\n // The overlay root is a synthetic mount point — report it as a directory\n // even when the backing store is empty/not-yet-materialized (a fresh\n // per-user DID Space has no physical dir until the first write).\n if (sub === \"/\") return { data: overlayRootEntry(overlay) };\n if (!overlay.provider.stat) this.rejectMissing(path);\n return overlay.provider.stat(sub, options);\n }\n if (!this.base.stat) throw new Error(\"base.stat not supported\");\n return this.base.stat(path, options);\n }\n\n async explain(path: string, options?: AFSExplainOptions): Promise<AFSExplainResult> {\n const match = this.matchOverlay(path);\n if (match) {\n // An overlay path MUST stay within the overlay — never fall through to\n // base with the raw `/user|/tmp|/space/...` path (anonymous `/user` has a null\n // provider; the Small World has no such mount → misleading errors).\n // Matches the guard every other op uses.\n if (!match.overlay.provider?.explain) this.rejectMissing(path);\n return match.overlay.provider.explain(match.sub, options);\n }\n if (!this.base.explain) throw new Error(\"base.explain not supported\");\n return this.base.explain(path, options);\n }\n\n /**\n * Find the `/user/index` overlay's backing provider, or undefined when\n * unmounted (no `index:` manifest section) OR reserved-null (blocklet\n * declares `index:` but this caller has no DID / no callerDid — see\n * `createSessionView`'s `indexProvider` doc).\n */\n private findIndexOverlay(): Overlay | undefined {\n return this.overlays.find((o) => o.prefix === \"/user/index\" && o.provider != null);\n }\n\n private isAfsValidationError(err: unknown): boolean {\n const error = err as { code?: string; name?: string } | undefined;\n return error?.code === \"AFS_VALIDATION_ERROR\" || error?.name === \"AFSValidationError\";\n }\n\n private isAfsDependencyError(err: unknown): boolean {\n const error = err as { code?: string; name?: string } | undefined;\n return error?.code === \"AFS_DEPENDENCY_MISSING\" || error?.name === \"AFSDependencyError\";\n }\n\n private searchDiagnostics(\n path: string,\n query: string,\n options: AFSSearchOptions | undefined,\n ): Record<string, unknown> {\n const context = options?.context as\n | {\n requestId?: unknown;\n userId?: unknown;\n instanceDid?: unknown;\n caller?: { did?: unknown; role?: unknown; roles?: unknown };\n }\n | undefined;\n const filters = Array.isArray(options?.filters) ? options.filters : [];\n return {\n path,\n queryLength: query.length,\n limit: options?.limit,\n cursor: Boolean(options?.cursor),\n filterCount: filters.length,\n filterTypes: filters.map((f) => f.type),\n requestId: context?.requestId,\n callerDid: context?.caller?.did ?? context?.userId,\n callerRole: context?.caller?.role ?? context?.caller?.roles,\n instanceDid: context?.instanceDid,\n };\n }\n\n private rejectIndexSearchFallback(\n path: string,\n detail: string,\n diagnostics: Record<string, unknown>,\n ): never {\n log.warn(\"[search] /user/index unavailable; refusing DID Space native fallback\", {\n ...diagnostics,\n path,\n fallback: false,\n detail,\n });\n throw new AFSDependencyError(\n \"session-user-afs\",\n \"search\",\n \"/user/index\",\n `${detail}; refusing DID Space native search fallback for indexed path ${path}`,\n );\n }\n\n /**\n * Parse a `read()`-returned raw content string as JSON, matching the exact\n * fail-soft convention `IndexFollower.indexEntry` uses when hydrating\n * changelog rows (`providers/basic/index/src/follower.ts`): non-string\n * content is passed through untouched; a string that isn't valid JSON is\n * kept as the raw string rather than dropped. afs-list's `performSearch`\n * (P5 forward contract) expects a resolved `content` object for JSON\n * entries — this is what makes that true without a second copy of the\n * parse-or-passthrough logic living in the hydration loop below.\n */\n private parseEntryContent(raw: unknown): unknown {\n if (typeof raw !== \"string\") return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n\n /**\n * Hydrate index-query hits (`{ entryPath, score, matchType }` — path only,\n * no content) into full `AFSEntry` objects by reading each `entryPath`\n * through THIS session view (so the caller's own `/user` overlay + its\n * per-caller isolation applies a second time, on top of the index query's\n * own `boundDomains` isolation — belt-and-suspenders, not redundant: see\n * design.md P5 section). A stale entry (index has it, storage doesn't —\n * e.g. a delete that hasn't reached the follower yet) fails the `read()`\n * and is skipped rather than surfaced as a broken result.\n */\n private async hydrateSearchEntries(\n results: Array<{\n entryPath: string;\n score?: number;\n matchType?: string;\n explanation?: string;\n anchors?: unknown[];\n }>,\n ): Promise<AFSEntry[]> {\n if (results.length === 0) return [];\n\n // Merge a read `AFSEntry` with the index result's ranking metadata.\n // `explanation`/`anchors` only present when the caller asked for `explain`\n // (issue #1069) — spread conditionally so a normal search keeps meta\n // byte-identical (score + matchType only).\n const build = (r: (typeof results)[number], data: AFSEntry): AFSEntry => ({\n ...data,\n id: r.entryPath,\n path: r.entryPath,\n content: this.parseEntryContent(data.content),\n meta: {\n ...(data.meta ?? {}),\n score: r.score,\n matchType: r.matchType,\n ...(r.explanation !== undefined ? { explanation: r.explanation } : {}),\n ...(r.anchors !== undefined ? { anchors: r.anchors } : {}),\n },\n });\n\n // Fast path: index hits all live under ONE overlay (`/user`), whose\n // did-space provider implements a pushdown `batchRead` — the whole path set\n // resolves via a single chunked `IN(…)` (`treeIndex.getMany`), inline\n // content served with zero R2. Collapsing N per-entry `read()`s (each a D1\n // subrequest) into one query is what keeps a ~50-result search under\n // Cloudflare's 50-subrequest free-tier budget, where the old loop alone\n // could exhaust it.\n const matches = results.map((r) => this.matchOverlay(r.entryPath));\n const first = matches[0];\n const provider = first?.overlay.provider;\n if (\n first &&\n provider?.batchRead &&\n matches.every((m) => m?.overlay.prefix === first.overlay.prefix)\n ) {\n try {\n const batch = await provider.batchRead(matches.map((m) => ({ path: m!.sub })));\n // did-space batchRead returns results index-aligned to input; a stale\n // entry (index has it, storage doesn't — a delete not yet followed)\n // comes back as a failure and is skipped rather than surfaced broken.\n const out: AFSEntry[] = [];\n for (let i = 0; i < results.length; i++) {\n const res = batch.results[i];\n if (res?.success && res.data) out.push(build(results[i]!, res.data));\n }\n return out;\n } catch (err) {\n log.warn(\"[search] batch hydrate failed, falling back to per-entry read:\", err);\n }\n }\n\n // Fallback: per-entry read (provider without batchRead, or mixed overlays).\n const hydrated = await mapWithConcurrency(\n results,\n INDEX_SEARCH_HYDRATE_CONCURRENCY,\n async (r): Promise<AFSEntry | null> => {\n try {\n const { data } = await this.read(r.entryPath);\n if (!data) return null;\n return build(r, data);\n } catch (err) {\n log.warn(`[search] index hit \"${r.entryPath}\" failed to read (stale?), skipping:`, err);\n return null;\n }\n },\n );\n return hydrated.filter((e): e is AFSEntry => e != null);\n }\n\n /**\n * Search-verb forward contract (P5, design.md \"search verb forwarding\"):\n * a `/user/**` search first tries the caller's semantic `/user/index`\n * (when mounted with a real provider), scoping hits to the requested\n * subtree and hydrating them with real content via `read()`. For blocklets\n * whose manifest declares `index:`, the index is authoritative: failures are\n * surfaced and DID Space native search is refused because it is an O(N)\n * content scan. Legacy/no-index blocklets keep the old direct overlay search\n * behavior. A successful empty index result is authoritative and returns an\n * empty array. Index results and fallback results are never unioned. Any other\n * overlay (`/user/index` addressed directly, `/tmp`, `/space`,\n * `/instance/settings`, replication overlays) keeps its pre-existing\n * single-overlay-search behavior unchanged.\n */\n async search(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult> {\n const match = this.matchOverlay(path);\n if (!match) return this.base.search(path, query, options);\n\n const { overlay, sub } = match;\n if (overlay.prefix === \"/user\") {\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n const diagnostics = this.searchDiagnostics(normalizedPath, query, options);\n const indexProvider = this.findIndexOverlay()?.provider;\n if (indexProvider?.exec) {\n try {\n const limit = options?.limit ?? 50;\n // Scope the index query to the SEARCHED subtree via an entry_path\n // prefix, pushed into the backend BEFORE its `ORDER BY … LIMIT`. This\n // is what makes a `/user/items` search return items' top-K instead of\n // being crowded out of the cross-collection top-K (`/user` indexes\n // items + tags + collections in ONE domain). `pathPrefix` filters the\n // MATCH/anchor-bounded rows over the `(scope, entry_path)` index, so\n // it stays index-friendly as the per-user dataset grows.\n const prefix = normalizedPath.endsWith(\"/\") ? normalizedPath : `${normalizedPath}/`;\n // Search mode precedence: the CALLER's `options.mode` wins (a search box\n // can opt a single query into `semantic`); when omitted it's governed by\n // the `/user/index` provider's `defaultSearchMode` (the\n // `AFS_INDEX_SEARCH_MODE` env var). Unset → `DEFAULT_SEARCH_MODE`\n // (\"combined\": anchor + FTS fused via RRF, NO embedding pass — keyword +\n // facet recall without the query-embed round-trip), so the default `/user`\n // search stays byte-identical. `semantic` (anchor + FTS + embedding,\n // cross-lingual — \"技能\" matches \"skill\", Epic #932) adds vector recall at\n // the cost of one query-embed round-trip. A client-driven toggle\n // (localStorage) is the primary switch; the env is the deployment-wide\n // default — see planning/afs-ui/semantic-search-toggle/design.md.\n //\n // Structured facet filters (Epic #932 S2/S3) → the query's `anchors`\n // plus `anchorFilter: true` so they act as a HARD filter (results MUST\n // match the facets AND the text), not a soft fusion signal.\n const filters = options?.filters;\n const hasFilters = Array.isArray(filters) && filters.length > 0;\n // Per-result \"why did this match\" debug info (issue #1069). Opt-in:\n // the index only builds `explanation`/`anchors` (and pays the extra\n // per-anchor `getAnchorData` lookup) when `explain: true` is passed,\n // so the default `search()` stays byte-identical and zero-cost.\n const explain = options?.explain === true;\n const execResult = await indexProvider.exec(\n \"/.actions/query\",\n {\n text: query,\n ...(hasFilters ? { anchors: filters } : {}),\n // Caller-provided mode wins; omitted → index `defaultSearchMode`\n // (env `AFS_INDEX_SEARCH_MODE`, \"combined\" when unset) — byte-identical.\n ...(options?.mode ? { mode: options.mode } : {}),\n opts: {\n limit,\n ...(options?.cursor ? { cursor: options.cursor } : {}),\n pathPrefix: prefix,\n ...(hasFilters ? { anchorFilter: true } : {}),\n ...(explain ? { explain: true } : {}),\n },\n },\n { context: options?.context },\n );\n if (execResult.success) {\n const raw = (execResult.data ?? {}) as {\n results?: Array<{\n entryPath: string;\n score?: number;\n matchType?: string;\n explanation?: string;\n anchors?: unknown[];\n }>;\n meta?: AFSSearchResult[\"meta\"];\n };\n const scoped = (raw.results ?? []).filter(\n (r) => r.entryPath === normalizedPath || r.entryPath.startsWith(prefix),\n );\n if (scoped.length > 0) {\n const hydrated = await this.hydrateSearchEntries(scoped);\n log.debug(\"[search] /user/index query served\", {\n ...diagnostics,\n rawResults: raw.results?.length ?? 0,\n scopedResults: scoped.length,\n hydratedResults: hydrated.length,\n fallback: false,\n });\n return {\n data: hydrated,\n ...(raw.meta ? { meta: raw.meta } : {}),\n };\n }\n log.debug(\"[search] /user/index query returned no scoped results\", {\n ...diagnostics,\n rawResults: raw.results?.length ?? 0,\n scopedResults: 0,\n fallback: false,\n });\n return { data: [], ...(raw.meta ? { meta: raw.meta } : {}) };\n }\n const indexError = execResult.error as\n | { code?: string; message?: string }\n | string\n | undefined;\n const failedCode =\n typeof indexError === \"object\" && indexError ? indexError.code : undefined;\n const failedMessage =\n typeof indexError === \"object\" && indexError\n ? (indexError.message ?? \"index query failed\")\n : (indexError ?? \"index query failed\");\n if (failedCode === \"VALIDATION_ERROR\" || failedCode === \"AFS_VALIDATION_ERROR\") {\n log.warn(\"[search] /user/index query validation failed\", {\n ...diagnostics,\n code: failedCode,\n error: failedMessage,\n fallback: false,\n });\n throw new AFSValidationError(failedMessage);\n }\n if (options?.cursor) {\n throw new AFSValidationError(failedMessage);\n }\n if (this.indexSearchRequired) {\n this.rejectIndexSearchFallback(\n normalizedPath,\n `index query failed${failedCode ? ` (${failedCode})` : \"\"}: ${failedMessage}`,\n diagnostics,\n );\n }\n log.warn(\"[search] /user/index query failed, falling back to native search\", {\n ...diagnostics,\n code: failedCode,\n error: failedMessage,\n fallback: true,\n });\n } catch (err) {\n if (options?.cursor) throw err;\n if (this.isAfsValidationError(err) || this.isAfsDependencyError(err)) throw err;\n const message = err instanceof Error ? err.message : String(err);\n if (this.indexSearchRequired) {\n this.rejectIndexSearchFallback(\n normalizedPath,\n `index query threw: ${message}`,\n diagnostics,\n );\n }\n log.warn(\"[search] /user/index query threw, falling back to native search\", {\n ...diagnostics,\n error: message,\n fallback: true,\n });\n }\n } else if (this.indexSearchRequired) {\n this.rejectIndexSearchFallback(normalizedPath, \"index provider unavailable\", diagnostics);\n }\n }\n if (!overlay.provider?.search) return { data: [] };\n return overlay.provider.search(sub, query, options);\n }\n\n async exec(\n path: string,\n args: Record<string, unknown>,\n options: AFSExecOptions,\n ): Promise<AFSExecResult> {\n // Inject `context.afs = this` so blocklet handlers downstream call back\n // INTO this overlaid view (not the bare runtime root they were originally\n // mounted into). Without this, an aistro `@Actions.Exec` that does\n // `ctx.afs.write(\"/user/profile.json\")` writes to the GLOBAL /user fs\n // mount instead of the caller's DID-Space-overlaid /user. The\n // ProjectionProvider already does this for its own mountRoot; here the\n // session view forces the same view on whatever provider chain runs\n // below.\n options = this.withSelfContext(options);\n const match = this.matchOverlay(path);\n if (match) {\n if (!match.overlay.provider?.exec) this.rejectMissing(path);\n return match.overlay.provider.exec(match.sub, args, options);\n }\n // Root data-actions (/.actions/{read,list,write,delete}) are reflexive: the\n // inner `args.path` must resolve against THIS view, so an overlay-addressed\n // write/delete reaches `/user` | `/tmp` instead of silently re-resolving\n // on the base (which has no overlay). The 0-LOC blocklet UI drives every\n // add/toggle/clear through these actions, so without this a `/user`-backed\n // list is unwritable. Mount/unmount + provider `.actions/*` stay on base —\n // they operate on the host topology, not overlay data.\n if (isReflexiveRootDataAction(path)) {\n // Gate ③ of the exec auth gate (exec-auth-gate §4.1): this is the\n // `/user` overlay's REAL write path (the 0-LOC blocklet UI drives every\n // add/toggle/clear through these root data actions), and it never\n // passes through AFS.execRootAction — so the gate must sit here too.\n // Effect comes from the same static table as face ② built-ins.\n const context = options?.context;\n enforceEffectGate({\n effect: rootActionEffect(path.slice(\"/.actions/\".length)) ?? \"write\",\n context,\n face: \"session-reflexive\",\n path,\n });\n // instance-write-authz §5.1 / T1.4 (base-write gate, reflexive arm):\n // a network `exec('/.actions/write|delete', {path:'/instance/<non-settings>'})`\n // that PASSES the effect gate above (authenticated caller) must still be\n // rejected with AFS_FORBIDDEN. The inner this.write/this.delete WOULD throw\n // it, but execRootWriteAction wraps inner throws into a `{success:false,\n // code:\"WRITE_ERROR\"}` envelope — swallowing the forbidden signal and never\n // surfacing as 403. So gate HERE (symmetric with the effect gate, which\n // also throws from exec() before the reflexive action) when the inner path\n // falls through to base. Overlay-addressed reflexive writes (`/user`,\n // `/instance/settings`) are NOT gated — they never reach base. The inner\n // this.write still gates as defense-in-depth.\n const reflexiveAction = path.slice(\"/.actions/\".length);\n if (\n (reflexiveAction === \"write\" || reflexiveAction === \"delete\") &&\n typeof (args as { path?: unknown }).path === \"string\" &&\n !this.matchOverlay((args as { path: string }).path)\n ) {\n this.enforceBaseWriteGate((args as { path: string }).path, context);\n }\n // Thread the exec's context into the inner ops — without this the\n // overlay write loses sessionId/requestId and propBind echo suppression\n // + the effect probe both go blind (external review #1 / r6).\n const ctxOpts = context ? { context } : undefined;\n return execReflexiveRootDataAction(path, args as Record<string, unknown>, {\n read: (p) => this.read(p, ctxOpts),\n list: (p) => this.list(p, ctxOpts),\n query: (p, spec, o) => this.query(p, spec, { ...o, ...(ctxOpts ?? {}) }),\n write: (p, c, o) => this.write(p, c, { ...o, ...(ctxOpts ?? {}) }),\n delete: (p, o) => this.delete(p, { ...o, ...(ctxOpts ?? {}) }),\n });\n }\n if (!this.base.exec) throw new Error(\"base.exec not supported\");\n return this.base.exec(path, args, options);\n }\n\n /**\n * Inject `context.afs = this` so downstream provider handlers that call\n * back into AFS (`ctx.context.afs.read`/`.write`/`.exec`/...) hit this\n * overlaid session view — not the bare runtime root the provider was\n * mounted into. Without this, the `/user` overlay (and any other per-\n * caller overlay) is invisible to handler-internal AFS calls, so writes\n * silently land on the global `/user` fs mount instead of the caller's\n * DID-Space-projected `/user`.\n */\n private withSelfContext<T extends { context?: import(\"@aigne/afs\").AFSContext }>(\n options: T | undefined,\n ): T {\n const ctx = (options as { context?: Record<string, unknown> } | undefined)?.context ?? {};\n // TS 5: generic-conditional struct widening requires the unknown\n // detour. The runtime shape `{ ...options, context: {...ctx, afs} }`\n // genuinely satisfies T's constraint, but the compiler can't prove\n // that `this` (SessionUserAFS) lines up with whatever concrete\n // `context.afs` type T expects, so the two-step cast is required.\n return {\n ...((options as object) ?? {}),\n context: { ...ctx, afs: this },\n } as unknown as T;\n }\n\n subscribe(filter: AFSEventFilter, callback: AFSEventCallback): AFSUnsubscribe {\n // Symmetric with read/list/write: route by the same overlay table. `/user`,\n // `/space`, and `/tmp` events live on the per-user / per-session DID Space event bus\n // (the overlay provider is a ProjectionProvider that bridges them via its\n // own `subscribe`). The base (Small World) bus NEVER sees them, so blindly\n // forwarding an overlay-path filter to `base.subscribe` would silently drop\n // every `/user`/`/tmp` event — the write lands on one bus, the listener\n // sits on another. Re-prefix bridged event paths back into the overlay\n // namespace, exactly as `list` does for child entries.\n const rawPath = typeof filter.path === \"string\" ? filter.path : \"\";\n const normalized = rawPath ? (rawPath.startsWith(\"/\") ? rawPath : `/${rawPath}`) : \"\";\n\n // Specific path → exactly one owner (an overlay, or the base).\n if (normalized && normalized !== \"/\") {\n const match = this.matchOverlay(normalized);\n if (match)\n return this.subscribeOverlay(match.overlay, { ...filter, path: match.sub }, callback);\n if (!this.base.subscribe) return () => {};\n return this.base.subscribe(filter, callback);\n }\n\n // Root / path-less filter (\"observe everything\") → union of the base and\n // every active overlay, so a wildcard subscriber sees cross-mount events\n // the same way `AFS.subscribe` fans out to its own mounts.\n const unsubs: AFSUnsubscribe[] = [];\n if (this.base.subscribe) unsubs.push(this.base.subscribe(filter, callback));\n for (const overlay of this.activeOverlays) {\n unsubs.push(this.subscribeOverlay(overlay, { ...filter, path: \"/\" }, callback));\n }\n return () => {\n for (const u of unsubs) {\n try {\n u();\n } catch {\n // tolerate downstream unsub errors\n }\n }\n };\n }\n\n /**\n * Subscribe one overlay's backing provider, re-prefixing bridged event paths\n * back into the overlay namespace. No-op when the overlay is unbacked\n * (anonymous `/user`) or its provider lacks `subscribe`.\n */\n private subscribeOverlay(\n overlay: Overlay,\n filter: AFSEventFilter,\n callback: AFSEventCallback,\n ): AFSUnsubscribe {\n const subscribe = overlay.provider?.subscribe;\n if (!subscribe) return () => {};\n return subscribe.call(overlay.provider, filter, (evt) => {\n callback({ ...evt, path: this.reprefix(overlay.prefix, evt.path) });\n });\n }\n\n // ── render snapshot boundary (settings-layering §2.B) ─────────────────────\n // AutoSurfaceAFS-optional methods, proxied to the SettingsResolver when one\n // is wired. A view without a resolver (or a resolver predating the snapshot\n // API) is a transparent no-op — callers use undefined-checks, never assume.\n\n async beginRender(renderId: string): Promise<void> {\n await this.settingsResolver?.beginRender?.(renderId);\n }\n\n endRender(renderId: string): void {\n this.settingsResolver?.endRender?.(renderId);\n }\n\n getMounts(namespace?: string | null): MountInfo[] {\n const baseGetMounts = (\n this.base as unknown as { getMounts?: (ns?: string | null) => MountInfo[] }\n ).getMounts;\n // Copy: base.getMounts may hand back a reference to its internal array.\n const mounts = [...(baseGetMounts ? baseGetMounts.call(this.base, namespace) : [])];\n for (const overlay of this.rootOverlays) {\n mounts.push({\n namespace: namespace ?? null,\n path: overlay.prefix,\n module: overlay.provider as AFSModule,\n status: \"ready\",\n });\n }\n return mounts;\n }\n\n // Host lifecycle APIs are outside the per-session boundary — delegate to base\n // so anything the runtime expects on the AFSRoot surface keeps working.\n initializePhysicalPath(...args: Parameters<AFSRoot[\"initializePhysicalPath\"]>) {\n return this.base.initializePhysicalPath(...args);\n }\n\n cleanupPhysicalPath(...args: Parameters<AFSRoot[\"cleanupPhysicalPath\"]>) {\n return this.base.cleanupPhysicalPath(...args);\n }\n\n setEventSink(...args: Parameters<NonNullable<AFSModule[\"setEventSink\"]>>): void {\n this.base.setEventSink?.(...args);\n }\n\n setSecretCapability(...args: Parameters<NonNullable<AFSModule[\"setSecretCapability\"]>>): void {\n this.base.setSecretCapability?.(...args);\n }\n}\n\n/**\n * Assemble a caller's per-session view: a SettingsResolver (instance+user\n * settings cascade, role-aware) wrapped by a SessionUserAFS (`/user` +\n * optional `/space` + `/tmp` + `/instance/settings`). This is the construction the daemon's\n * `buildSessionView` chokepoint delegates to — extracted so the role→resolver\n * wiring is unit-testable in isolation (the daemon's only remaining job is to\n * RESOLVE `role` from the verified session before calling this).\n *\n * The resolver always exists (even for anonymous / no role) so\n * `/instance/settings` carries correct capability hints + enforcement for every\n * caller — there is no bypass path.\n */\nexport function createSessionView(opts: {\n base: AFSRoot;\n userProvider: AFSModule | null;\n tmpProvider: AFSModule | null;\n /**\n * Omitted → no `/space` overlay. null → `/space` reserved and NotFound\n * (fail-closed). Provider → mounted caller DID Space root.\n */\n spaceProvider?: AFSModule | null;\n /** Caller role from the verified session; undefined → non-admin (fail-closed). */\n role: string | undefined;\n sessionId: string;\n /**\n * Derived package-schema handle for this blocklet (settings-layering\n * Phase 1c), pre-bound to identity + base by the runtime. Optional — absent\n * means no derived index (legacy resolution; existing call sites/tests\n * unchanged).\n */\n settingsSchema?: import(\"./settings-schema-store.js\").SettingsSchemaHandle | null;\n /**\n * Caller's per-user semantic index provider mounted at `/user/index`.\n * Omitted → no overlay (path falls through to `/user`); null → reserved.\n */\n indexProvider?: AFSModule | null;\n /**\n * Whether `/user` search must be served by `/user/index` because the\n * blocklet manifest declares `index:`. Kept distinct from `indexProvider`\n * because runtimes may reserve `/user/index` with null for no-index\n * blocklets while direct path access still fails closed.\n */\n indexSearchRequired?: boolean;\n /**\n * Verified caller DID (from the session, never client-supplied). Forwarded\n * to ReplicationResolver for authorship tagging and ownItem delete gate.\n * null/undefined → anonymous (no copy-side writes).\n */\n callerDid?: string | null;\n /**\n * Replicated-collection declarations parsed from the blocklet manifest.\n * When non-empty, a ReplicationResolver is built and mounted at the computed\n * common ancestor prefix (e.g. \"/instance\"). null/omitted → no overlay.\n */\n replicated?: Record<string, import(\"@aigne/afs\").ReplicatedCollection> | null;\n}): SessionUserAFS {\n const settingsResolver = new SettingsResolver({\n base: opts.base,\n userProvider: opts.userProvider,\n role: opts.role,\n sessionId: opts.sessionId,\n schema: opts.settingsSchema,\n });\n\n let replicationOverlays:\n | Array<{ prefix: string; id: string; provider: AFSModule | null }>\n | undefined;\n if (opts.replicated && Object.keys(opts.replicated).length > 0) {\n const mountPrefix = computeReplicationMountPrefix(opts.replicated);\n const resolver = new ReplicationResolver({\n collections: opts.replicated,\n instanceBackend: opts.base,\n userBackend: opts.userProvider,\n settingsResolver,\n callerDid: opts.callerDid ?? undefined,\n callerRole: opts.role,\n sessionId: opts.sessionId,\n mountPrefix,\n });\n replicationOverlays = [{ prefix: `/${mountPrefix}`, id: \"replication\", provider: resolver }];\n }\n\n return new SessionUserAFS(\n opts.base,\n opts.userProvider,\n opts.tmpProvider,\n opts.sessionId,\n settingsResolver,\n opts.spaceProvider,\n opts.indexProvider,\n replicationOverlays,\n opts.indexSearchRequired ?? opts.indexProvider != null,\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+
2
+ //#region src/session/settings-cascade.ts
3
+ /** Prototype-pollution guard — never copied out of any parsed input. */
4
+ const DANGEROUS_KEYS = new Set([
5
+ "__proto__",
6
+ "constructor",
7
+ "prototype"
8
+ ]);
9
+ /**
10
+ * Control keys are COMPUTED by the cascade — an override row can never smuggle
11
+ * them in (a hand-crafted instance row claiming `source: "user"` or
12
+ * `visible: true` must not reach the renderer).
13
+ */
14
+ const CONTROL_KEYS = new Set([
15
+ "value",
16
+ "scope",
17
+ "source",
18
+ "overridden",
19
+ "inheritedValue",
20
+ "visible",
21
+ "editable"
22
+ ]);
23
+ /** present(x.k) — key exists and is not `undefined` (`null` IS present). */
24
+ function present(obj, key) {
25
+ return obj != null && obj[key] !== void 0;
26
+ }
27
+ function sanitizeScope(s) {
28
+ return s === "user" || s === "shared" ? s : "instance";
29
+ }
30
+ /** Copy non-dangerous, non-control keys from a (possibly absent) input. */
31
+ function safeKeys(obj) {
32
+ const out = {};
33
+ if (obj == null || typeof obj !== "object") return out;
34
+ for (const [k, v] of Object.entries(obj)) {
35
+ if (DANGEROUS_KEYS.has(k) || CONTROL_KEYS.has(k)) continue;
36
+ out[k] = v;
37
+ }
38
+ return out;
39
+ }
40
+ /**
41
+ * Merge the three layers into an {@link EffectiveField}. Pure + total: accepts
42
+ * null/undefined/empty inputs, never mutates them, performs no I/O.
43
+ */
44
+ function cascade(pkg, inst, user) {
45
+ const scope = sanitizeScope(pkg?.scope);
46
+ const userPresent = present(user, "value");
47
+ const instPresent = present(inst, "value");
48
+ const inherited = instPresent ? inst.value : pkg?.value;
49
+ const value = userPresent ? user.value : inherited;
50
+ const source = userPresent ? "user" : instPresent ? "instance" : "package";
51
+ const synth = {
52
+ ...safeKeys(pkg),
53
+ ...safeKeys(inst),
54
+ value,
55
+ scope,
56
+ source,
57
+ overridden: userPresent
58
+ };
59
+ if (userPresent) synth.inheritedValue = inherited;
60
+ return {
61
+ value,
62
+ source,
63
+ scope,
64
+ synth
65
+ };
66
+ }
67
+
68
+ //#endregion
69
+ exports.cascade = cascade;
@@ -0,0 +1,81 @@
1
+ //#region src/session/settings-cascade.d.ts
2
+ /**
3
+ * Settings three-object cascade — settings-layering Phase 0 contract (design §1.2).
4
+ *
5
+ * The new architecture's in-memory model keeps three STRICTLY SEPARATED objects
6
+ * (never mixed, never written back merged):
7
+ *
8
+ * | object | meaning | source |
9
+ * |----------------------|------------------------------------------|--------------------------------|
10
+ * | `PackageFieldSchema` | authoritative schema + default value | derived schema index (readonly)|
11
+ * | `InstanceOverrideOnly`| ONLY keys an admin wrote (sparse) | `/instance/settings` rows |
12
+ * | `UserOverrideOnly` | ONLY the caller's own value | `/user/settings` rows |
13
+ * | `EffectiveField` | cascade result for rendering | in-memory merge — NEVER persisted |
14
+ *
15
+ * Cascade rules (present semantics — key existence via `!== undefined`,
16
+ * NEVER `??`; `value: null` is legal and preserved, §1.5):
17
+ *
18
+ * effective.value = present(user.value) ? user.value
19
+ * : present(inst.value) ? inst.value
20
+ * : pkg.value
21
+ * effective.<k≠value> = present(inst.<k>) ? inst.<k> : pkg.<k>
22
+ * exists(field) = the derived schema index has the field (caller's job)
23
+ *
24
+ * `cascade()` is a TOTAL pure function: no I/O, no mutation, no throw on
25
+ * null/undefined/empty inputs. Prototype-pollution keys and control keys are
26
+ * stripped from every input; control fields (`source`/`overridden`/
27
+ * `inheritedValue`) are computed, never copied.
28
+ */
29
+ /** Where a field's value is declared (schema-level cascade tag). */
30
+ type SettingsCascadeScope = "instance" | "user" | "shared";
31
+ /**
32
+ * Authoritative package-layer field schema from the derived index — the full
33
+ * wrapper a blocklet author shipped (`label`, `description`, `type`,
34
+ * `options`, `scope`, default `value`, …). Readonly by construction.
35
+ */
36
+ interface PackageFieldSchema {
37
+ value?: unknown;
38
+ scope?: SettingsCascadeScope;
39
+ [key: string]: unknown;
40
+ }
41
+ /**
42
+ * Sparse instance-layer override: ONLY keys an admin actually wrote — usually
43
+ * `{ value }`, occasionally an explicit schema-text override (`label`, …).
44
+ * MUST NOT contain a full copied schema (that is the G0 disaster shape the
45
+ * Phase 2 write-strip eliminates).
46
+ */
47
+ interface InstanceOverrideOnly {
48
+ value?: unknown;
49
+ [key: string]: unknown;
50
+ }
51
+ /** Sparse user-layer override: ONLY the caller's own value. */
52
+ interface UserOverrideOnly {
53
+ value?: unknown;
54
+ }
55
+ /**
56
+ * The merged, render-ready result. In-memory only — writing this object (or
57
+ * `synth`) back to ANY layer is a contract violation (it would re-create
58
+ * per-instance schema copies, design §2.D).
59
+ */
60
+ interface EffectiveField {
61
+ /** Cascaded effective value (may legally be `null`). */
62
+ value: unknown;
63
+ /** Which layer produced `value`. */
64
+ source: "user" | "instance" | "package";
65
+ /** Schema-level scope (package-authoritative; overrides cannot flip it). */
66
+ scope: SettingsCascadeScope;
67
+ /**
68
+ * Full merged wrapper for presentation: package schema overlaid by instance
69
+ * schema-text overrides, plus computed `value`/`scope`/`source`/`overridden`
70
+ * (+ `inheritedValue` when the user layer overrides).
71
+ */
72
+ synth: Record<string, unknown>;
73
+ }
74
+ /**
75
+ * Merge the three layers into an {@link EffectiveField}. Pure + total: accepts
76
+ * null/undefined/empty inputs, never mutates them, performs no I/O.
77
+ */
78
+ declare function cascade(pkg: PackageFieldSchema | null | undefined, inst: InstanceOverrideOnly | null | undefined, user: UserOverrideOnly | null | undefined): EffectiveField;
79
+ //#endregion
80
+ export { EffectiveField, InstanceOverrideOnly, PackageFieldSchema, SettingsCascadeScope, UserOverrideOnly, cascade };
81
+ //# sourceMappingURL=settings-cascade.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-cascade.d.cts","names":[],"sources":["../../src/session/settings-cascade.ts"],"mappings":";;AA6BA;;;;;AAOA;;;;;;;;;;AAYA;;;;;AAMA;;;;;AASA;;KAlCY,oBAAA;;;;;;UAOK,kBAAA;EACf,KAAA;EACA,KAAA,GAAQ,oBAAA;EAAA,CACP,GAAA;AAAA;;;;;;;UASc,oBAAA;EACf,KAAA;EAAA,CACC,GAAA;AAAA;;UAIc,gBAAA;EACf,KAAA;AAAA;;;;;;UAQe,cAAA;;EAEf,KAAA;;EAEA,MAAA;;EAEA,KAAA,EAAO,oBAAA;;;;;;EAMP,KAAA,EAAO,MAAA;AAAA;;;;;iBA6CO,OAAA,CACd,GAAA,EAAK,kBAAA,qBACL,IAAA,EAAM,oBAAA,qBACN,IAAA,EAAM,gBAAA,sBACL,cAAA"}
@@ -0,0 +1,81 @@
1
+ //#region src/session/settings-cascade.d.ts
2
+ /**
3
+ * Settings three-object cascade — settings-layering Phase 0 contract (design §1.2).
4
+ *
5
+ * The new architecture's in-memory model keeps three STRICTLY SEPARATED objects
6
+ * (never mixed, never written back merged):
7
+ *
8
+ * | object | meaning | source |
9
+ * |----------------------|------------------------------------------|--------------------------------|
10
+ * | `PackageFieldSchema` | authoritative schema + default value | derived schema index (readonly)|
11
+ * | `InstanceOverrideOnly`| ONLY keys an admin wrote (sparse) | `/instance/settings` rows |
12
+ * | `UserOverrideOnly` | ONLY the caller's own value | `/user/settings` rows |
13
+ * | `EffectiveField` | cascade result for rendering | in-memory merge — NEVER persisted |
14
+ *
15
+ * Cascade rules (present semantics — key existence via `!== undefined`,
16
+ * NEVER `??`; `value: null` is legal and preserved, §1.5):
17
+ *
18
+ * effective.value = present(user.value) ? user.value
19
+ * : present(inst.value) ? inst.value
20
+ * : pkg.value
21
+ * effective.<k≠value> = present(inst.<k>) ? inst.<k> : pkg.<k>
22
+ * exists(field) = the derived schema index has the field (caller's job)
23
+ *
24
+ * `cascade()` is a TOTAL pure function: no I/O, no mutation, no throw on
25
+ * null/undefined/empty inputs. Prototype-pollution keys and control keys are
26
+ * stripped from every input; control fields (`source`/`overridden`/
27
+ * `inheritedValue`) are computed, never copied.
28
+ */
29
+ /** Where a field's value is declared (schema-level cascade tag). */
30
+ type SettingsCascadeScope = "instance" | "user" | "shared";
31
+ /**
32
+ * Authoritative package-layer field schema from the derived index — the full
33
+ * wrapper a blocklet author shipped (`label`, `description`, `type`,
34
+ * `options`, `scope`, default `value`, …). Readonly by construction.
35
+ */
36
+ interface PackageFieldSchema {
37
+ value?: unknown;
38
+ scope?: SettingsCascadeScope;
39
+ [key: string]: unknown;
40
+ }
41
+ /**
42
+ * Sparse instance-layer override: ONLY keys an admin actually wrote — usually
43
+ * `{ value }`, occasionally an explicit schema-text override (`label`, …).
44
+ * MUST NOT contain a full copied schema (that is the G0 disaster shape the
45
+ * Phase 2 write-strip eliminates).
46
+ */
47
+ interface InstanceOverrideOnly {
48
+ value?: unknown;
49
+ [key: string]: unknown;
50
+ }
51
+ /** Sparse user-layer override: ONLY the caller's own value. */
52
+ interface UserOverrideOnly {
53
+ value?: unknown;
54
+ }
55
+ /**
56
+ * The merged, render-ready result. In-memory only — writing this object (or
57
+ * `synth`) back to ANY layer is a contract violation (it would re-create
58
+ * per-instance schema copies, design §2.D).
59
+ */
60
+ interface EffectiveField {
61
+ /** Cascaded effective value (may legally be `null`). */
62
+ value: unknown;
63
+ /** Which layer produced `value`. */
64
+ source: "user" | "instance" | "package";
65
+ /** Schema-level scope (package-authoritative; overrides cannot flip it). */
66
+ scope: SettingsCascadeScope;
67
+ /**
68
+ * Full merged wrapper for presentation: package schema overlaid by instance
69
+ * schema-text overrides, plus computed `value`/`scope`/`source`/`overridden`
70
+ * (+ `inheritedValue` when the user layer overrides).
71
+ */
72
+ synth: Record<string, unknown>;
73
+ }
74
+ /**
75
+ * Merge the three layers into an {@link EffectiveField}. Pure + total: accepts
76
+ * null/undefined/empty inputs, never mutates them, performs no I/O.
77
+ */
78
+ declare function cascade(pkg: PackageFieldSchema | null | undefined, inst: InstanceOverrideOnly | null | undefined, user: UserOverrideOnly | null | undefined): EffectiveField;
79
+ //#endregion
80
+ export { EffectiveField, InstanceOverrideOnly, PackageFieldSchema, SettingsCascadeScope, UserOverrideOnly, cascade };
81
+ //# sourceMappingURL=settings-cascade.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-cascade.d.mts","names":[],"sources":["../../src/session/settings-cascade.ts"],"mappings":";;AA6BA;;;;;AAOA;;;;;;;;;;AAYA;;;;;AAMA;;;;;AASA;;KAlCY,oBAAA;;;;;;UAOK,kBAAA;EACf,KAAA;EACA,KAAA,GAAQ,oBAAA;EAAA,CACP,GAAA;AAAA;;;;;;;UASc,oBAAA;EACf,KAAA;EAAA,CACC,GAAA;AAAA;;UAIc,gBAAA;EACf,KAAA;AAAA;;;;;;UAQe,cAAA;;EAEf,KAAA;;EAEA,MAAA;;EAEA,KAAA,EAAO,oBAAA;;;;;;EAMP,KAAA,EAAO,MAAA;AAAA;;;;;iBA6CO,OAAA,CACd,GAAA,EAAK,kBAAA,qBACL,IAAA,EAAM,oBAAA,qBACN,IAAA,EAAM,gBAAA,sBACL,cAAA"}
@@ -0,0 +1,69 @@
1
+ //#region src/session/settings-cascade.ts
2
+ /** Prototype-pollution guard — never copied out of any parsed input. */
3
+ const DANGEROUS_KEYS = new Set([
4
+ "__proto__",
5
+ "constructor",
6
+ "prototype"
7
+ ]);
8
+ /**
9
+ * Control keys are COMPUTED by the cascade — an override row can never smuggle
10
+ * them in (a hand-crafted instance row claiming `source: "user"` or
11
+ * `visible: true` must not reach the renderer).
12
+ */
13
+ const CONTROL_KEYS = new Set([
14
+ "value",
15
+ "scope",
16
+ "source",
17
+ "overridden",
18
+ "inheritedValue",
19
+ "visible",
20
+ "editable"
21
+ ]);
22
+ /** present(x.k) — key exists and is not `undefined` (`null` IS present). */
23
+ function present(obj, key) {
24
+ return obj != null && obj[key] !== void 0;
25
+ }
26
+ function sanitizeScope(s) {
27
+ return s === "user" || s === "shared" ? s : "instance";
28
+ }
29
+ /** Copy non-dangerous, non-control keys from a (possibly absent) input. */
30
+ function safeKeys(obj) {
31
+ const out = {};
32
+ if (obj == null || typeof obj !== "object") return out;
33
+ for (const [k, v] of Object.entries(obj)) {
34
+ if (DANGEROUS_KEYS.has(k) || CONTROL_KEYS.has(k)) continue;
35
+ out[k] = v;
36
+ }
37
+ return out;
38
+ }
39
+ /**
40
+ * Merge the three layers into an {@link EffectiveField}. Pure + total: accepts
41
+ * null/undefined/empty inputs, never mutates them, performs no I/O.
42
+ */
43
+ function cascade(pkg, inst, user) {
44
+ const scope = sanitizeScope(pkg?.scope);
45
+ const userPresent = present(user, "value");
46
+ const instPresent = present(inst, "value");
47
+ const inherited = instPresent ? inst.value : pkg?.value;
48
+ const value = userPresent ? user.value : inherited;
49
+ const source = userPresent ? "user" : instPresent ? "instance" : "package";
50
+ const synth = {
51
+ ...safeKeys(pkg),
52
+ ...safeKeys(inst),
53
+ value,
54
+ scope,
55
+ source,
56
+ overridden: userPresent
57
+ };
58
+ if (userPresent) synth.inheritedValue = inherited;
59
+ return {
60
+ value,
61
+ source,
62
+ scope,
63
+ synth
64
+ };
65
+ }
66
+
67
+ //#endregion
68
+ export { cascade };
69
+ //# sourceMappingURL=settings-cascade.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-cascade.mjs","names":[],"sources":["../../src/session/settings-cascade.ts"],"sourcesContent":["/**\n * Settings three-object cascade — settings-layering Phase 0 contract (design §1.2).\n *\n * The new architecture's in-memory model keeps three STRICTLY SEPARATED objects\n * (never mixed, never written back merged):\n *\n * | object | meaning | source |\n * |----------------------|------------------------------------------|--------------------------------|\n * | `PackageFieldSchema` | authoritative schema + default value | derived schema index (readonly)|\n * | `InstanceOverrideOnly`| ONLY keys an admin wrote (sparse) | `/instance/settings` rows |\n * | `UserOverrideOnly` | ONLY the caller's own value | `/user/settings` rows |\n * | `EffectiveField` | cascade result for rendering | in-memory merge — NEVER persisted |\n *\n * Cascade rules (present semantics — key existence via `!== undefined`,\n * NEVER `??`; `value: null` is legal and preserved, §1.5):\n *\n * effective.value = present(user.value) ? user.value\n * : present(inst.value) ? inst.value\n * : pkg.value\n * effective.<k≠value> = present(inst.<k>) ? inst.<k> : pkg.<k>\n * exists(field) = the derived schema index has the field (caller's job)\n *\n * `cascade()` is a TOTAL pure function: no I/O, no mutation, no throw on\n * null/undefined/empty inputs. Prototype-pollution keys and control keys are\n * stripped from every input; control fields (`source`/`overridden`/\n * `inheritedValue`) are computed, never copied.\n */\n\n/** Where a field's value is declared (schema-level cascade tag). */\nexport type SettingsCascadeScope = \"instance\" | \"user\" | \"shared\";\n\n/**\n * Authoritative package-layer field schema from the derived index — the full\n * wrapper a blocklet author shipped (`label`, `description`, `type`,\n * `options`, `scope`, default `value`, …). Readonly by construction.\n */\nexport interface PackageFieldSchema {\n value?: unknown;\n scope?: SettingsCascadeScope;\n [key: string]: unknown;\n}\n\n/**\n * Sparse instance-layer override: ONLY keys an admin actually wrote — usually\n * `{ value }`, occasionally an explicit schema-text override (`label`, …).\n * MUST NOT contain a full copied schema (that is the G0 disaster shape the\n * Phase 2 write-strip eliminates).\n */\nexport interface InstanceOverrideOnly {\n value?: unknown;\n [key: string]: unknown;\n}\n\n/** Sparse user-layer override: ONLY the caller's own value. */\nexport interface UserOverrideOnly {\n value?: unknown;\n}\n\n/**\n * The merged, render-ready result. In-memory only — writing this object (or\n * `synth`) back to ANY layer is a contract violation (it would re-create\n * per-instance schema copies, design §2.D).\n */\nexport interface EffectiveField {\n /** Cascaded effective value (may legally be `null`). */\n value: unknown;\n /** Which layer produced `value`. */\n source: \"user\" | \"instance\" | \"package\";\n /** Schema-level scope (package-authoritative; overrides cannot flip it). */\n scope: SettingsCascadeScope;\n /**\n * Full merged wrapper for presentation: package schema overlaid by instance\n * schema-text overrides, plus computed `value`/`scope`/`source`/`overridden`\n * (+ `inheritedValue` when the user layer overrides).\n */\n synth: Record<string, unknown>;\n}\n\n/** Prototype-pollution guard — never copied out of any parsed input. */\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/**\n * Control keys are COMPUTED by the cascade — an override row can never smuggle\n * them in (a hand-crafted instance row claiming `source: \"user\"` or\n * `visible: true` must not reach the renderer).\n */\nconst CONTROL_KEYS = new Set([\n \"value\",\n \"scope\",\n \"source\",\n \"overridden\",\n \"inheritedValue\",\n \"visible\",\n \"editable\",\n]);\n\n/** present(x.k) — key exists and is not `undefined` (`null` IS present). */\nfunction present(obj: Record<string, unknown> | null | undefined, key: string): boolean {\n return obj != null && obj[key] !== undefined;\n}\n\nfunction sanitizeScope(s: unknown): SettingsCascadeScope {\n return s === \"user\" || s === \"shared\" ? s : \"instance\";\n}\n\n/** Copy non-dangerous, non-control keys from a (possibly absent) input. */\nfunction safeKeys(obj: Record<string, unknown> | null | undefined): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (obj == null || typeof obj !== \"object\") return out;\n for (const [k, v] of Object.entries(obj)) {\n if (DANGEROUS_KEYS.has(k) || CONTROL_KEYS.has(k)) continue;\n out[k] = v;\n }\n return out;\n}\n\n/**\n * Merge the three layers into an {@link EffectiveField}. Pure + total: accepts\n * null/undefined/empty inputs, never mutates them, performs no I/O.\n */\nexport function cascade(\n pkg: PackageFieldSchema | null | undefined,\n inst: InstanceOverrideOnly | null | undefined,\n user: UserOverrideOnly | null | undefined,\n): EffectiveField {\n const scope = sanitizeScope(pkg?.scope);\n\n // Value cascade — present semantics, user > instance > package.\n const userPresent = present(user as Record<string, unknown>, \"value\");\n const instPresent = present(inst as Record<string, unknown>, \"value\");\n const inherited = instPresent ? (inst as InstanceOverrideOnly).value : pkg?.value;\n const value = userPresent ? (user as UserOverrideOnly).value : inherited;\n const source: EffectiveField[\"source\"] = userPresent\n ? \"user\"\n : instPresent\n ? \"instance\"\n : \"package\";\n\n // Schema keys: instance schema-text overrides overlay package schema.\n const synth: Record<string, unknown> = {\n ...safeKeys(pkg),\n ...safeKeys(inst),\n value,\n scope,\n source,\n overridden: userPresent,\n };\n if (userPresent) synth.inheritedValue = inherited;\n\n return { value, source, scope, synth };\n}\n"],"mappings":";;AA+EA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAe;CAAY,CAAC;;;;;;AAOzE,MAAM,eAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,SAAS,QAAQ,KAAiD,KAAsB;AACtF,QAAO,OAAO,QAAQ,IAAI,SAAS;;AAGrC,SAAS,cAAc,GAAkC;AACvD,QAAO,MAAM,UAAU,MAAM,WAAW,IAAI;;;AAI9C,SAAS,SAAS,KAA0E;CAC1F,MAAM,MAA+B,EAAE;AACvC,KAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AACnD,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,IAAI,EAAE;AACxC,MAAI,eAAe,IAAI,EAAE,IAAI,aAAa,IAAI,EAAE,CAAE;AAClD,MAAI,KAAK;;AAEX,QAAO;;;;;;AAOT,SAAgB,QACd,KACA,MACA,MACgB;CAChB,MAAM,QAAQ,cAAc,KAAK,MAAM;CAGvC,MAAM,cAAc,QAAQ,MAAiC,QAAQ;CACrE,MAAM,cAAc,QAAQ,MAAiC,QAAQ;CACrE,MAAM,YAAY,cAAe,KAA8B,QAAQ,KAAK;CAC5E,MAAM,QAAQ,cAAe,KAA0B,QAAQ;CAC/D,MAAM,SAAmC,cACrC,SACA,cACE,aACA;CAGN,MAAM,QAAiC;EACrC,GAAG,SAAS,IAAI;EAChB,GAAG,SAAS,KAAK;EACjB;EACA;EACA;EACA,YAAY;EACb;AACD,KAAI,YAAa,OAAM,iBAAiB;AAExC,QAAO;EAAE;EAAO;EAAQ;EAAO;EAAO"}