wiki-viewer 1.14.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (559) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  4. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  13. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/api/agent/activity/route.js +6 -6
  20. package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js +8 -8
  22. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +9 -9
  24. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +8 -8
  26. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js +8 -8
  28. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +8 -8
  30. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +9 -9
  32. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js +8 -8
  34. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/agent/settings/route.js +7 -7
  36. package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js +6 -6
  38. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js +5 -5
  41. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/share/[token]/route.js +6 -5
  43. package/.next/standalone/.next/server/app/api/share/[token]/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/share/route.js +4 -4
  45. package/.next/standalone/.next/server/app/api/share/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/system/reveal/route.js +6 -6
  49. package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/system/set-root/route.js +6 -5
  52. package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js +5 -4
  54. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js +6 -5
  56. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js +5 -4
  58. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/system/workspaces/route.js +7 -6
  60. package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js +5 -5
  62. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/wiki/app/route.js +7 -7
  64. package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/api/wiki/backlinks/route.js +7 -7
  66. package/.next/standalone/.next/server/app/api/wiki/backlinks/route.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/api/wiki/content/route.js +7 -7
  68. package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/api/wiki/download/route.js +6 -6
  70. package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/api/wiki/file/route.js +5 -5
  72. package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/wiki/folder/route.js +5 -5
  74. package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/wiki/git-branches/route.js +4 -4
  76. package/.next/standalone/.next/server/app/api/wiki/git-branches/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js +6 -6
  78. package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js +4 -4
  80. package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js +4 -4
  82. package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/wiki/git-history/route.js +4 -4
  84. package/.next/standalone/.next/server/app/api/wiki/git-history/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js +8 -8
  86. package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/api/wiki/move/route.js +6 -6
  88. package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js +5 -5
  90. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/api/wiki/outlinks/route.js +7 -7
  92. package/.next/standalone/.next/server/app/api/wiki/outlinks/route.js.nft.json +1 -1
  93. package/.next/standalone/.next/server/app/api/wiki/page/route.js +5 -5
  94. package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
  95. package/.next/standalone/.next/server/app/api/wiki/presence/route.js +6 -6
  96. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/api/wiki/route.js +5 -5
  98. package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
  99. package/.next/standalone/.next/server/app/api/wiki/search/route.js +5 -5
  100. package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js +5 -5
  102. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
  103. package/.next/standalone/.next/server/app/api/wiki/upload/route.js +5 -5
  104. package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/api/wiki/watch/route.js +5 -5
  106. package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/index.html +1 -1
  108. package/.next/standalone/.next/server/app/index.rsc +3 -3
  109. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  110. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  111. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  113. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  114. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  116. package/.next/standalone/.next/server/app/s/[token]/page_client-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-jzw2_._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.-oq-7._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/[root-of-the-server]__000spch._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01oin-a._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01qcera._.js +16 -0
  123. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02.uul~._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/[root-of-the-server]__024967n._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06booxp._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/[root-of-the-server]__071_pmz._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0bqpg._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_1f2cw._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0a6j6r4._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0aoqat-._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0as-u6z._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bvtbax._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0c-ee48._.js +5 -0
  134. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e_9pc5._.js +3 -0
  135. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0esmskc._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fshk._._.js +20 -0
  137. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fu72o7._.js +3 -0
  138. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0k9mili._.js +3 -0
  139. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-wu5u._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lvz22t._.js +20 -0
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0n-chxk._.js +16 -0
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0n9zdf0._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nmx3k2._.js +12 -0
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nzlmkb._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o0v6hf._.js +3 -0
  146. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s38pid._.js +3 -0
  147. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s_8poz._.js +3 -0
  148. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sfrx~.._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sggstu._.js +3 -0
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0u81k~2._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uibxf_._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wfv_by._.js +3 -0
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wlj-yw._.js +3 -0
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w~__xz._.js +3 -0
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yat~ul._.js +3 -0
  156. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yxauu6._.js +3 -0
  157. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zbkgnr._.js +3 -0
  158. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zv_j05._.js +3 -0
  159. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~rntxj._.js +3 -0
  160. package/.next/standalone/.next/server/chunks/src_lib_search_0d-_j3u._.js +4 -4
  161. package/.next/standalone/.next/server/chunks/src_lib_search_0ymmp0c._.js +3 -3
  162. package/.next/standalone/.next/server/chunks/src_lib_workspaces_ts_0qon40u._.js +4 -0
  163. package/.next/standalone/.next/server/chunks/ssr/{0~w0_katex_dist_katex_mjs_029cdbg._.js → 0~w0_katex_dist_katex_mjs_0s4yb1t._.js} +1 -1
  164. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0z41nrr._.js → [root-of-the-server]__10te1ye._.js} +2 -2
  165. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_13p348f._.js → node_modules__pnpm_0-a_zjq._.js} +1 -1
  166. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0gos._c._.js → node_modules__pnpm_02l_1_8._.js} +1 -1
  167. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ru1i2-._.js → node_modules__pnpm_03rzttw._.js} +2 -2
  168. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_08xpp3w._.js → node_modules__pnpm_04frxp9._.js} +1 -1
  169. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0b8odbc._.js → node_modules__pnpm_057-t6m._.js} +1 -1
  170. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0f_n_zp._.js → node_modules__pnpm_05_oahi._.js} +1 -1
  171. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0~i3a.i._.js → node_modules__pnpm_05ans~m._.js} +1 -1
  172. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0i2m0b.._.js → node_modules__pnpm_06-2ia.._.js} +2 -2
  173. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0aha1~u._.js → node_modules__pnpm_086.ow4._.js} +1 -1
  174. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0w.kwue._.js → node_modules__pnpm_0cahog3._.js} +1 -1
  175. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0e0r_qk._.js → node_modules__pnpm_0cxanql._.js} +2 -2
  176. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ajqqlq._.js → node_modules__pnpm_0e09gmf._.js} +1 -1
  177. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hzzx9d._.js → node_modules__pnpm_0eqjo2f._.js} +1 -1
  178. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_02bje_g._.js → node_modules__pnpm_0f-yazg._.js} +1 -1
  179. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xk042~._.js → node_modules__pnpm_0iu44jv._.js} +1 -1
  180. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_11ede0p._.js → node_modules__pnpm_0jh8q66._.js} +2 -2
  181. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0k-7uzm._.js → node_modules__pnpm_0m39f~t._.js} +1 -1
  182. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xr.ztu._.js → node_modules__pnpm_0ma-8l.._.js} +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0i6_kfe._.js → node_modules__pnpm_0met6sx._.js} +2 -2
  184. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_04v1_ty._.js → node_modules__pnpm_0mtrsi0._.js} +2 -2
  185. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0607~kg._.js → node_modules__pnpm_0n_swwn._.js} +2 -2
  186. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0t4l89_._.js → node_modules__pnpm_0ndyy93._.js} +1 -1
  187. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-_58b8._.js → node_modules__pnpm_0s-cj7c._.js} +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0y2h_cz._.js → node_modules__pnpm_0tr41-t._.js} +1 -1
  189. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_08edl1n._.js → node_modules__pnpm_0u.uija._.js} +1 -1
  190. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0026mgr._.js → node_modules__pnpm_0wiyn6d._.js} +1 -1
  191. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_09o_spj._.js → node_modules__pnpm_0ygzt82._.js} +1 -1
  192. package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_0ss2.w7._.js +2 -2
  193. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  194. package/.next/standalone/.next/server/middleware-manifest.json +1 -1
  195. package/.next/standalone/.next/server/pages/404.html +1 -1
  196. package/.next/standalone/.next/server/pages/500.html +1 -1
  197. package/.next/standalone/.next/static/chunks/0ci7khnv7i~7_.js +17 -0
  198. package/.next/standalone/.next/static/chunks/0sx~65~lg4mgl.css +5 -0
  199. package/.next/standalone/package.json +1 -1
  200. package/README.md +88 -17
  201. package/bin/postbuild.js +21 -0
  202. package/bin/wiki-viewer.js +198 -6
  203. package/package.json +1 -1
  204. package/.next/standalone/.github/workflows/publish.yml +0 -37
  205. package/.next/standalone/.handoffs/archive/handoff-20260612-113332.md +0 -98
  206. package/.next/standalone/.handoffs/archive/handoff-20260612-141032.md +0 -73
  207. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-.y6im._.js +0 -3
  208. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-3_t2y._.js +0 -3
  209. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-kjkzt._.js +0 -3
  210. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-puax4._.js +0 -4
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01h996g._.js +0 -3
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0329ktu._.js +0 -3
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03_xj4r._.js +0 -3
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05398dt._.js +0 -4
  215. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06lfvxh._.js +0 -3
  216. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06w0aet._.js +0 -3
  217. package/.next/standalone/.next/server/chunks/[root-of-the-server]__070_p2a._.js +0 -4
  218. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0905hi1._.js +0 -21
  219. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0aa63~j._.js +0 -5
  220. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0avcplk._.js +0 -4
  221. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b8_kyo._.js +0 -16
  222. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bticq1._.js +0 -3
  223. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bwevvu._.js +0 -3
  224. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0c__k3q._.js +0 -4
  225. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cp~z4x._.js +0 -3
  226. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fmdv9h._.js +0 -20
  227. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0h0qbwr._.js +0 -3
  228. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jk~oyi._.js +0 -3
  229. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ndq77b._.js +0 -3
  230. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nvgpuk._.js +0 -3
  231. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ocvng2._.js +0 -3
  232. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ps4h~.._.js +0 -3
  233. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q7jqwp._.js +0 -12
  234. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qew~6p._.js +0 -3
  235. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s_9f-a._.js +0 -3
  236. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uv7b8u._.js +0 -3
  237. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uv8.9i._.js +0 -3
  238. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vfe2io._.js +0 -4
  239. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w11kjy._.js +0 -3
  240. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wc-.e4._.js +0 -3
  241. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ymovmp._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~c6ouv._.js +0 -3
  243. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~rhs0u._.js +0 -16
  244. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10h9tid._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10yk-u1._.js +0 -3
  246. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11105ew._.js +0 -3
  247. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11a3dhw._.js +0 -3
  248. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13420tt._.js +0 -3
  249. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13h~pdf._.js +0 -3
  250. package/.next/standalone/.next/static/chunks/02kg.lbkfi1yc.css +0 -5
  251. package/.next/standalone/.next/static/chunks/0rcwd6-.iu~nd.js +0 -17
  252. package/.next/standalone/.pi/ownership.json +0 -3
  253. package/.next/standalone/AGENTS.md +0 -114
  254. package/.next/standalone/DESIGN.md +0 -521
  255. package/.next/standalone/README.md +0 -821
  256. package/.next/standalone/TODO.md +0 -20
  257. package/.next/standalone/agents/bootstrap-prompt.md +0 -14
  258. package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +0 -456
  259. package/.next/standalone/bin/postbuild.js +0 -78
  260. package/.next/standalone/bin/wiki-viewer.js +0 -922
  261. package/.next/standalone/certificates/localhost-key.pem +0 -28
  262. package/.next/standalone/certificates/localhost.pem +0 -24
  263. package/.next/standalone/docs/agent-collab-plan.md +0 -1615
  264. package/.next/standalone/docs/agent-collab-v2-plan.md +0 -771
  265. package/.next/standalone/docs/agent-fs-plan.md +0 -232
  266. package/.next/standalone/docs/editorial-skin-spec.md +0 -242
  267. package/.next/standalone/docs/file-vs-collab-authority.md +0 -124
  268. package/.next/standalone/docs/mobile-readiness-spec.md +0 -229
  269. package/.next/standalone/docs/workspacing-plan.md +0 -364
  270. package/.next/standalone/packages/wiki-viewer-mcp/README.md +0 -195
  271. package/.next/standalone/packages/wiki-viewer-mcp/bench-e2e.mjs +0 -166
  272. package/.next/standalone/packages/wiki-viewer-mcp/bench-latency.mjs +0 -87
  273. package/.next/standalone/packages/wiki-viewer-mcp/bench-payload.mjs +0 -45
  274. package/.next/standalone/packages/wiki-viewer-mcp/dist/http-client.js +0 -331
  275. package/.next/standalone/packages/wiki-viewer-mcp/dist/index.js +0 -531
  276. package/.next/standalone/packages/wiki-viewer-mcp/dist/register.js +0 -94
  277. package/.next/standalone/packages/wiki-viewer-mcp/dist/state-cache.js +0 -28
  278. package/.next/standalone/packages/wiki-viewer-mcp/package-lock.json +0 -1716
  279. package/.next/standalone/packages/wiki-viewer-mcp/package.json +0 -41
  280. package/.next/standalone/packages/wiki-viewer-mcp/src/http-client.ts +0 -459
  281. package/.next/standalone/packages/wiki-viewer-mcp/src/index.ts +0 -638
  282. package/.next/standalone/packages/wiki-viewer-mcp/src/register.ts +0 -144
  283. package/.next/standalone/packages/wiki-viewer-mcp/src/state-cache.ts +0 -55
  284. package/.next/standalone/packages/wiki-viewer-mcp/tsconfig.json +0 -19
  285. package/.next/standalone/pnpm-lock.yaml +0 -6697
  286. package/.next/standalone/pnpm-workspace.yaml +0 -8
  287. package/.next/standalone/postcss.config.mjs +0 -2
  288. package/.next/standalone/src/app/api/agent/activity/route.ts +0 -59
  289. package/.next/standalone/src/app/api/agent/admin/agents/[agentId]/revoke/route.ts +0 -40
  290. package/.next/standalone/src/app/api/agent/admin/agents/route.ts +0 -33
  291. package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/approve/route.ts +0 -83
  292. package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/deny/route.ts +0 -36
  293. package/.next/standalone/src/app/api/agent/admin/registrations/route.ts +0 -27
  294. package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +0 -148
  295. package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +0 -219
  296. package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +0 -446
  297. package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +0 -152
  298. package/.next/standalone/src/app/api/agent/fs/move/route.ts +0 -130
  299. package/.next/standalone/src/app/api/agent/fs/search/route.ts +0 -272
  300. package/.next/standalone/src/app/api/agent/internal/span/route.ts +0 -121
  301. package/.next/standalone/src/app/api/agent/register/[regId]/route.ts +0 -50
  302. package/.next/standalone/src/app/api/agent/register/route.ts +0 -118
  303. package/.next/standalone/src/app/api/agent/settings/route.ts +0 -34
  304. package/.next/standalone/src/app/api/agent/settings/token/regenerate/route.ts +0 -20
  305. package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +0 -53
  306. package/.next/standalone/src/app/api/agents/install/route.ts +0 -143
  307. package/.next/standalone/src/app/api/agents/skill/route.ts +0 -20
  308. package/.next/standalone/src/app/api/agents/skill.tar.gz/route.ts +0 -87
  309. package/.next/standalone/src/app/api/app-proxy/[...path]/route.ts +0 -247
  310. package/.next/standalone/src/app/api/assets/[...path]/route.ts +0 -75
  311. package/.next/standalone/src/app/api/auth/[...all]/route.ts +0 -7
  312. package/.next/standalone/src/app/api/owner/init/route.ts +0 -14
  313. package/.next/standalone/src/app/api/share/[token]/route.ts +0 -269
  314. package/.next/standalone/src/app/api/share/route.ts +0 -127
  315. package/.next/standalone/src/app/api/system/admins/route.ts +0 -103
  316. package/.next/standalone/src/app/api/system/auth-config/route.ts +0 -22
  317. package/.next/standalone/src/app/api/system/auth-settings/route.ts +0 -86
  318. package/.next/standalone/src/app/api/system/browse/route.ts +0 -82
  319. package/.next/standalone/src/app/api/system/clear-root/route.ts +0 -16
  320. package/.next/standalone/src/app/api/system/config/route.ts +0 -14
  321. package/.next/standalone/src/app/api/system/pins/route.ts +0 -38
  322. package/.next/standalone/src/app/api/system/reveal/route.ts +0 -36
  323. package/.next/standalone/src/app/api/system/root-status/route.ts +0 -13
  324. package/.next/standalone/src/app/api/system/set-root/route.ts +0 -53
  325. package/.next/standalone/src/app/api/system/users/route.ts +0 -120
  326. package/.next/standalone/src/app/api/system/workspaces/[id]/open/route.ts +0 -34
  327. package/.next/standalone/src/app/api/system/workspaces/[id]/refresh/route.ts +0 -32
  328. package/.next/standalone/src/app/api/system/workspaces/[id]/route.ts +0 -78
  329. package/.next/standalone/src/app/api/system/workspaces/route.ts +0 -128
  330. package/.next/standalone/src/app/api/upload/[...path]/route.ts +0 -85
  331. package/.next/standalone/src/app/api/wiki/app/route.ts +0 -60
  332. package/.next/standalone/src/app/api/wiki/backlinks/route.ts +0 -29
  333. package/.next/standalone/src/app/api/wiki/content/route.ts +0 -159
  334. package/.next/standalone/src/app/api/wiki/download/route.ts +0 -100
  335. package/.next/standalone/src/app/api/wiki/file/route.ts +0 -57
  336. package/.next/standalone/src/app/api/wiki/folder/route.ts +0 -34
  337. package/.next/standalone/src/app/api/wiki/git-branches/route.ts +0 -32
  338. package/.next/standalone/src/app/api/wiki/git-checkout/route.ts +0 -45
  339. package/.next/standalone/src/app/api/wiki/git-diff/route.ts +0 -33
  340. package/.next/standalone/src/app/api/wiki/git-file-info/route.ts +0 -27
  341. package/.next/standalone/src/app/api/wiki/git-history/route.ts +0 -27
  342. package/.next/standalone/src/app/api/wiki/git-pull/route.ts +0 -48
  343. package/.next/standalone/src/app/api/wiki/move/route.ts +0 -61
  344. package/.next/standalone/src/app/api/wiki/new-file/route.ts +0 -56
  345. package/.next/standalone/src/app/api/wiki/outlinks/route.ts +0 -76
  346. package/.next/standalone/src/app/api/wiki/page/route.ts +0 -92
  347. package/.next/standalone/src/app/api/wiki/presence/route.ts +0 -56
  348. package/.next/standalone/src/app/api/wiki/route.ts +0 -116
  349. package/.next/standalone/src/app/api/wiki/search/route.ts +0 -44
  350. package/.next/standalone/src/app/api/wiki/slugs/route.ts +0 -54
  351. package/.next/standalone/src/app/api/wiki/upload/route.ts +0 -95
  352. package/.next/standalone/src/app/api/wiki/watch/route.ts +0 -67
  353. package/.next/standalone/src/app/globals.css +0 -1249
  354. package/.next/standalone/src/app/icon.svg +0 -11
  355. package/.next/standalone/src/app/layout.tsx +0 -78
  356. package/.next/standalone/src/app/manifest.ts +0 -30
  357. package/.next/standalone/src/app/page.tsx +0 -3260
  358. package/.next/standalone/src/app/s/[token]/page.tsx +0 -357
  359. package/.next/standalone/src/app/signin/page.tsx +0 -16
  360. package/.next/standalone/src/app/signin/signin-form.tsx +0 -238
  361. package/.next/standalone/src/components/ai-panel/activity-row.tsx +0 -284
  362. package/.next/standalone/src/components/ai-panel/ai-panel.tsx +0 -311
  363. package/.next/standalone/src/components/ai-panel/token-section.tsx +0 -313
  364. package/.next/standalone/src/components/auth-settings-sheet.tsx +0 -474
  365. package/.next/standalone/src/components/confirm-dialog.tsx +0 -56
  366. package/.next/standalone/src/components/dir-picker.tsx +0 -606
  367. package/.next/standalone/src/components/editor/backlinks-panel.tsx +0 -139
  368. package/.next/standalone/src/components/editor/bubble-menu.tsx +0 -346
  369. package/.next/standalone/src/components/editor/callout-extension.ts +0 -36
  370. package/.next/standalone/src/components/editor/comment-pip.tsx +0 -56
  371. package/.next/standalone/src/components/editor/comment-thread.tsx +0 -254
  372. package/.next/standalone/src/components/editor/csv-viewer.tsx +0 -379
  373. package/.next/standalone/src/components/editor/document-outline.tsx +0 -230
  374. package/.next/standalone/src/components/editor/editor-toolbar.tsx +0 -564
  375. package/.next/standalone/src/components/editor/editor.tsx +0 -1327
  376. package/.next/standalone/src/components/editor/extensions/drag-handle.ts +0 -325
  377. package/.next/standalone/src/components/editor/extensions/heading-anchors.ts +0 -55
  378. package/.next/standalone/src/components/editor/extensions/proof-span.ts +0 -60
  379. package/.next/standalone/src/components/editor/extensions/resizable-image.tsx +0 -159
  380. package/.next/standalone/src/components/editor/extensions.ts +0 -150
  381. package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +0 -75
  382. package/.next/standalone/src/components/editor/folder-index.tsx +0 -236
  383. package/.next/standalone/src/components/editor/google-doc-viewer.tsx +0 -73
  384. package/.next/standalone/src/components/editor/image-viewer.tsx +0 -57
  385. package/.next/standalone/src/components/editor/large-file-gate.tsx +0 -108
  386. package/.next/standalone/src/components/editor/link-popover.tsx +0 -111
  387. package/.next/standalone/src/components/editor/media-popover.tsx +0 -212
  388. package/.next/standalone/src/components/editor/media-viewer.tsx +0 -70
  389. package/.next/standalone/src/components/editor/mermaid-viewer.tsx +0 -286
  390. package/.next/standalone/src/components/editor/node-app-viewer.tsx +0 -328
  391. package/.next/standalone/src/components/editor/notebook-viewer.tsx +0 -371
  392. package/.next/standalone/src/components/editor/office/docx-viewer.tsx +0 -93
  393. package/.next/standalone/src/components/editor/office/office-chrome.tsx +0 -83
  394. package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +0 -104
  395. package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +0 -192
  396. package/.next/standalone/src/components/editor/pdf-viewer.tsx +0 -32
  397. package/.next/standalone/src/components/editor/proof-span-popover.tsx +0 -162
  398. package/.next/standalone/src/components/editor/slash-commands.tsx +0 -629
  399. package/.next/standalone/src/components/editor/source-viewer.tsx +0 -289
  400. package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +0 -229
  401. package/.next/standalone/src/components/editor/suggestion-card.tsx +0 -206
  402. package/.next/standalone/src/components/editor/table-menu.tsx +0 -232
  403. package/.next/standalone/src/components/editor/view-mode-comment-button.tsx +0 -77
  404. package/.next/standalone/src/components/editor/website-viewer.tsx +0 -75
  405. package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +0 -189
  406. package/.next/standalone/src/components/editor/wiki-link-decorator.ts +0 -79
  407. package/.next/standalone/src/components/editor/wiki-link-extension.ts +0 -120
  408. package/.next/standalone/src/components/editor/wiki-link-picker.tsx +0 -250
  409. package/.next/standalone/src/components/layout/viewer-toolbar.tsx +0 -54
  410. package/.next/standalone/src/components/search/search-command-dialog.tsx +0 -246
  411. package/.next/standalone/src/components/search/sidebar-search-box.tsx +0 -121
  412. package/.next/standalone/src/components/search/snippet-text.tsx +0 -48
  413. package/.next/standalone/src/components/share-dialog.tsx +0 -307
  414. package/.next/standalone/src/components/theme-provider.tsx +0 -16
  415. package/.next/standalone/src/components/theme-toggle.tsx +0 -24
  416. package/.next/standalone/src/components/ui/badge.tsx +0 -35
  417. package/.next/standalone/src/components/ui/button.tsx +0 -56
  418. package/.next/standalone/src/components/ui/card.tsx +0 -63
  419. package/.next/standalone/src/components/ui/collapsible.tsx +0 -9
  420. package/.next/standalone/src/components/ui/command.tsx +0 -151
  421. package/.next/standalone/src/components/ui/context-menu.tsx +0 -191
  422. package/.next/standalone/src/components/ui/dialog.tsx +0 -119
  423. package/.next/standalone/src/components/ui/dropdown-menu.tsx +0 -194
  424. package/.next/standalone/src/components/ui/input.tsx +0 -22
  425. package/.next/standalone/src/components/ui/label.tsx +0 -25
  426. package/.next/standalone/src/components/ui/popover.tsx +0 -33
  427. package/.next/standalone/src/components/ui/scroll-area.tsx +0 -47
  428. package/.next/standalone/src/components/ui/select.tsx +0 -156
  429. package/.next/standalone/src/components/ui/separator.tsx +0 -30
  430. package/.next/standalone/src/components/ui/sheet.tsx +0 -140
  431. package/.next/standalone/src/components/ui/skeleton.tsx +0 -15
  432. package/.next/standalone/src/components/ui/switch.tsx +0 -28
  433. package/.next/standalone/src/components/ui/tabs.tsx +0 -54
  434. package/.next/standalone/src/components/ui/textarea.tsx +0 -21
  435. package/.next/standalone/src/components/ui/tip.tsx +0 -23
  436. package/.next/standalone/src/components/ui/toggle-group.tsx +0 -41
  437. package/.next/standalone/src/components/ui/tooltip.tsx +0 -27
  438. package/.next/standalone/src/components/view-width-toggle.tsx +0 -70
  439. package/.next/standalone/src/components/wiki/frontmatter-header.tsx +0 -164
  440. package/.next/standalone/src/hooks/use-is-mobile.ts +0 -14
  441. package/.next/standalone/src/lib/app-runner.ts +0 -272
  442. package/.next/standalone/src/lib/auth/admin.ts +0 -120
  443. package/.next/standalone/src/lib/auth/allowlist.ts +0 -50
  444. package/.next/standalone/src/lib/auth/client.ts +0 -4
  445. package/.next/standalone/src/lib/auth/csrf.ts +0 -68
  446. package/.next/standalone/src/lib/auth/server.ts +0 -215
  447. package/.next/standalone/src/lib/cabinets/tree.ts +0 -75
  448. package/.next/standalone/src/lib/config.ts +0 -114
  449. package/.next/standalone/src/lib/embeds/detect.ts +0 -142
  450. package/.next/standalone/src/lib/git-secrets.ts +0 -65
  451. package/.next/standalone/src/lib/git.ts +0 -503
  452. package/.next/standalone/src/lib/google/detect.ts +0 -107
  453. package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +0 -104
  454. package/.next/standalone/src/lib/markdown/remark-wikilinks.ts +0 -100
  455. package/.next/standalone/src/lib/markdown/sanitize-schema.ts +0 -106
  456. package/.next/standalone/src/lib/markdown/to-html.ts +0 -282
  457. package/.next/standalone/src/lib/markdown/to-markdown.ts +0 -252
  458. package/.next/standalone/src/lib/proof/activity-shared.ts +0 -31
  459. package/.next/standalone/src/lib/proof/activity.ts +0 -74
  460. package/.next/standalone/src/lib/proof/audit.ts +0 -88
  461. package/.next/standalone/src/lib/proof/auth.ts +0 -143
  462. package/.next/standalone/src/lib/proof/block-refs.ts +0 -133
  463. package/.next/standalone/src/lib/proof/blocks.ts +0 -73
  464. package/.next/standalone/src/lib/proof/client-auth.ts +0 -23
  465. package/.next/standalone/src/lib/proof/collab-state.ts +0 -81
  466. package/.next/standalone/src/lib/proof/event-bus.ts +0 -47
  467. package/.next/standalone/src/lib/proof/file-lock.ts +0 -43
  468. package/.next/standalone/src/lib/proof/glob.ts +0 -51
  469. package/.next/standalone/src/lib/proof/idempotency.ts +0 -37
  470. package/.next/standalone/src/lib/proof/lease.ts +0 -117
  471. package/.next/standalone/src/lib/proof/mutex.ts +0 -36
  472. package/.next/standalone/src/lib/proof/ops-applier.ts +0 -727
  473. package/.next/standalone/src/lib/proof/owner-auth.ts +0 -2
  474. package/.next/standalone/src/lib/proof/pending.ts +0 -97
  475. package/.next/standalone/src/lib/proof/proof-span.ts +0 -141
  476. package/.next/standalone/src/lib/proof/rate-limit.ts +0 -53
  477. package/.next/standalone/src/lib/proof/raw-fs.ts +0 -197
  478. package/.next/standalone/src/lib/proof/register-rate-limit.ts +0 -53
  479. package/.next/standalone/src/lib/proof/registry.ts +0 -195
  480. package/.next/standalone/src/lib/proof/sidecar.ts +0 -103
  481. package/.next/standalone/src/lib/proof/suggest-capture.ts +0 -69
  482. package/.next/standalone/src/lib/proof/types.ts +0 -182
  483. package/.next/standalone/src/lib/proof-config.ts +0 -14
  484. package/.next/standalone/src/lib/root-dir.ts +0 -35
  485. package/.next/standalone/src/lib/search/indexable-exts.ts +0 -25
  486. package/.next/standalone/src/lib/search/indexer.ts +0 -634
  487. package/.next/standalone/src/lib/search/sanitize.ts +0 -35
  488. package/.next/standalone/src/lib/search/search-db.ts +0 -57
  489. package/.next/standalone/src/lib/search/watcher-pool.ts +0 -79
  490. package/.next/standalone/src/lib/shared-docs/db.ts +0 -177
  491. package/.next/standalone/src/lib/toast.ts +0 -9
  492. package/.next/standalone/src/lib/utils.ts +0 -6
  493. package/.next/standalone/src/lib/wiki-helpers.ts +0 -36
  494. package/.next/standalone/src/lib/workspace-client.ts +0 -82
  495. package/.next/standalone/src/lib/workspace-context.ts +0 -173
  496. package/.next/standalone/src/lib/workspaces.ts +0 -400
  497. package/.next/standalone/src/middleware.ts +0 -44
  498. package/.next/standalone/src/stores/ai-panel-store.ts +0 -79
  499. package/.next/standalone/src/stores/editor-store.ts +0 -439
  500. package/.next/standalone/src/stores/pin-store.ts +0 -56
  501. package/.next/standalone/src/stores/proof-store.ts +0 -124
  502. package/.next/standalone/src/stores/recent-store.ts +0 -51
  503. package/.next/standalone/src/stores/search-store.ts +0 -104
  504. package/.next/standalone/src/stores/show-hidden-store.ts +0 -26
  505. package/.next/standalone/src/stores/sidebar-width-store.ts +0 -39
  506. package/.next/standalone/src/stores/skin-store.ts +0 -42
  507. package/.next/standalone/src/stores/tree-store.ts +0 -52
  508. package/.next/standalone/src/stores/view-width-store.ts +0 -101
  509. package/.next/standalone/src/stores/wiki-slugs-store.ts +0 -75
  510. package/.next/standalone/src/tests/proof/activity-aggregator.test.ts +0 -146
  511. package/.next/standalone/src/tests/proof/admin-create-user.test.ts +0 -43
  512. package/.next/standalone/src/tests/proof/agent-fs.test.ts +0 -973
  513. package/.next/standalone/src/tests/proof/agent-ownership.test.ts +0 -140
  514. package/.next/standalone/src/tests/proof/agent-workspace-scope.test.ts +0 -134
  515. package/.next/standalone/src/tests/proof/agents-install.test.ts +0 -110
  516. package/.next/standalone/src/tests/proof/bench-write.ts +0 -179
  517. package/.next/standalone/src/tests/proof/better-auth.test.ts +0 -68
  518. package/.next/standalone/src/tests/proof/block-refs.test.ts +0 -108
  519. package/.next/standalone/src/tests/proof/blocks.test.ts +0 -92
  520. package/.next/standalone/src/tests/proof/collab-state.test.ts +0 -501
  521. package/.next/standalone/src/tests/proof/comments-ops.test.ts +0 -177
  522. package/.next/standalone/src/tests/proof/editor-roundtrip.test.ts +0 -85
  523. package/.next/standalone/src/tests/proof/external-edit.test.ts +0 -58
  524. package/.next/standalone/src/tests/proof/file-lock.test.ts +0 -80
  525. package/.next/standalone/src/tests/proof/git-branches-checkout.test.ts +0 -136
  526. package/.next/standalone/src/tests/proof/git-read-features.test.ts +0 -473
  527. package/.next/standalone/src/tests/proof/git-secrets.test.ts +0 -138
  528. package/.next/standalone/src/tests/proof/git-sparse.test.ts +0 -257
  529. package/.next/standalone/src/tests/proof/git-workspace.test.ts +0 -135
  530. package/.next/standalone/src/tests/proof/git.test.ts +0 -243
  531. package/.next/standalone/src/tests/proof/glob.test.ts +0 -82
  532. package/.next/standalone/src/tests/proof/helpers/auth-session.ts +0 -27
  533. package/.next/standalone/src/tests/proof/markdown-to-html.test.ts +0 -46
  534. package/.next/standalone/src/tests/proof/ops-applier.test.ts +0 -368
  535. package/.next/standalone/src/tests/proof/owner-init.test.ts +0 -2
  536. package/.next/standalone/src/tests/proof/parse-frontmatter.test.ts +0 -60
  537. package/.next/standalone/src/tests/proof/preload.ts +0 -21
  538. package/.next/standalone/src/tests/proof/proof-span.test.ts +0 -98
  539. package/.next/standalone/src/tests/proof/rate-limit.test.ts +0 -54
  540. package/.next/standalone/src/tests/proof/readonly-enforcement.test.ts +0 -161
  541. package/.next/standalone/src/tests/proof/reconcile-sidecar.test.ts +0 -195
  542. package/.next/standalone/src/tests/proof/registration-flow.test.ts +0 -330
  543. package/.next/standalone/src/tests/proof/registry.test.ts +0 -169
  544. package/.next/standalone/src/tests/proof/routes.test.ts +0 -516
  545. package/.next/standalone/src/tests/proof/search-index.test.ts +0 -376
  546. package/.next/standalone/src/tests/proof/sidecar-lifecycle.test.ts +0 -177
  547. package/.next/standalone/src/tests/proof/sidecar-trim.test.ts +0 -58
  548. package/.next/standalone/src/tests/proof/suggestion-ops.test.ts +0 -280
  549. package/.next/standalone/src/tests/proof/wiki-content-put.test.ts +0 -140
  550. package/.next/standalone/src/tests/proof/wiki-routes-auth.test.ts +0 -208
  551. package/.next/standalone/src/tests/proof/workspace-isolation.test.ts +0 -87
  552. package/.next/standalone/src/tests/proof/workspaces-api.test.ts +0 -227
  553. package/.next/standalone/src/types/index.ts +0 -47
  554. package/.next/standalone/tailwind.config.ts +0 -191
  555. package/.next/standalone/tsconfig.json +0 -41
  556. package/.next/standalone/tsconfig.tsbuildinfo +0 -1
  557. /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → ecpoWt_SP3WkUTh0NsnuT}/_buildManifest.js +0 -0
  558. /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → ecpoWt_SP3WkUTh0NsnuT}/_clientMiddlewareManifest.js +0 -0
  559. /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → ecpoWt_SP3WkUTh0NsnuT}/_ssgManifest.js +0 -0
@@ -1,1615 +0,0 @@
1
- # Agent Collaboration Protocol — Implementation Plan
2
-
3
- **Status:** Ready to implement
4
- **Target:** wiki-viewer one-shot build, no v2
5
- **Audience:** An implementing agent with no prior context on this conversation
6
-
7
- ---
8
-
9
- ## 0. Read this first
10
-
11
- You are implementing an HTTP collaboration protocol that lets remote AI agents read and edit markdown files in wiki-viewer, with full provenance tracking and human-in-the-loop comments / suggestions.
12
-
13
- The design is intentionally **API-compatible with [Proof SDK](https://github.com/EveryInc/proof-sdk)** in spirit — same op vocabulary, same block-ref + revision model, same suggestion lifecycle. This is so the user's mental model of "what an agent can do" transfers from Proof (which they already use and like) to wiki-viewer (this project).
14
-
15
- **Hard constraints:**
16
-
17
- - Single-user, local-first. No multi-tenancy, no share links, no realtime CRDT.
18
- - File on disk is the source of truth. Always.
19
- - Provenance marks live **inline** in the markdown (`<proof-span>` HTML).
20
- - Comments / threads / suggestions / event log / block-ref maps live in a **sidecar directory** (`.proof/`) next to the file.
21
- - TipTap is the editor. Do not introduce Milkdown or swap editors.
22
- - No new runtime dependencies are required. Use what's in `package.json`.
23
- - Block-ref + revision is the only mutation contract. **No selectors, no quote-matching**, no fuzzy-anchor resolution.
24
- - Suggestions exist (proof has them, user wants them). Comments exist with threaded replies.
25
-
26
- **Don't build:**
27
-
28
- - Yjs, Hocuspocus, WebSockets, realtime collab.
29
- - Multiple humans editing concurrently.
30
- - Share-token creation flows, hosted-style `POST /documents` to create slugs.
31
- - A new editor. Keep TipTap.
32
- - Auth UI beyond a single bearer-token env var + a "regenerate token" button.
33
- - Anything in proof's `server/` outside the agent bridge surface (snapshots, milkdown-headless, projection guardrails, S3, sqlite).
34
-
35
- ---
36
-
37
- ## 1. Glossary
38
-
39
- | Term | Definition |
40
- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
- | **File path** | Relative path from `ROOT_DIR`. Acts as the document slug. URL-encoded in routes. |
42
- | **Block** | A top-level markdown element. Heading, paragraph, list, blockquote, code fence, table, hr, html block. Lists and tables are treated as a single block. |
43
- | **Block ref** | Stable string ID like `b7f2a1`. Persists across edits via the sidecar's ref map. |
44
- | **Revision** | Monotonic integer per file. Bumps on every successful mutation. Used for optimistic locking. |
45
- | **Provenance span** | An inline `<proof-span>` HTML element wrapping AI-authored text, with `origin`, `basis`, `by`, `at`, `id` attributes. Lives in the markdown. |
46
- | **Comment** | A discussion thread attached to a block ref. Lives in the sidecar. Has ordered turns (initial + replies). |
47
- | **Suggestion** | A proposed edit on a block ref. Pending / accepted / rejected lifecycle. Lives in the sidecar until accepted (becomes a real block edit) or rejected (deleted from sidecar). |
48
- | **Event** | Immutable log entry. New events emitted on every state change. Polled by agents. |
49
- | **Sidecar** | `<ROOT_DIR>/.proof/<file-path>.json` — one JSON file per markdown file, holds ref map, comments, suggestions, event log. |
50
-
51
- ---
52
-
53
- ## 2. Architectural overview
54
-
55
- ```
56
- ~/notes/ ROOT_DIR
57
- ├── plan.md canonical content + inline <proof-span> marks
58
- ├── specs.md
59
- ├── subdir/
60
- │ └── notes.md
61
- └── .proof/
62
- ├── plan.md.json sidecar for plan.md
63
- ├── specs.md.json
64
- └── subdir/
65
- └── notes.md.json
66
- ```
67
-
68
- Sidecar path mirrors the source path under `.proof/`. Create directories as needed. `.proof/` is a single tree at the root, **not** a per-directory dotdir.
69
-
70
- Mutation flow:
71
-
72
- ```
73
- agent ── POST /api/agent/files/<path> ──► route handler
74
-
75
- ├─ acquire per-file mutex
76
- ├─ load sidecar + file
77
- ├─ verify baseRevision matches
78
- ├─ apply ops (block.* mutate the .md,
79
- │ comment.* / suggestion.* mutate sidecar,
80
- │ suggestion.accept does both)
81
- ├─ write .md (rewraps proof-spans on inserts/replaces)
82
- ├─ write sidecar with new revision + appended events
83
- ├─ release mutex
84
- └─ return new snapshot
85
- ```
86
-
87
- Read flow:
88
-
89
- ```
90
- agent ── GET /api/agent/files/<path> ──► load .md → parse → assign refs → return blocks
91
- agent ── GET /api/agent/events/<path>?after=N ──► tail sidecar's event log
92
- ```
93
-
94
- Editor flow (browser):
95
-
96
- ```
97
- chokidar watcher (already exists) → SSE → editor reloads file
98
- → re-parses inline <proof-span> via TipTap mark
99
- → renders gutter pips for comments (loaded from sidecar via separate REST call)
100
- ```
101
-
102
- ---
103
-
104
- ## 3. File-by-file scope
105
-
106
- ```
107
- src/
108
- ├── app/api/agent/ ← NEW
109
- │ ├── files/[...path]/
110
- │ │ ├── route.ts GET = snapshot, POST = apply ops
111
- │ ├── events/[...path]/route.ts GET poll events, POST ack
112
- │ ├── sidecar/[...path]/route.ts GET sidecar (for editor UI to load comments/suggestions)
113
- │ └── settings/
114
- │ ├── route.ts GET settings, POST regenerate token
115
- │ └── token/route.ts GET masked token + scope
116
- ├── lib/proof/ ← NEW
117
- │ ├── blocks.ts markdown → blocks parser, block → markdown emitter
118
- │ ├── block-refs.ts ref generation, sidecar ref map updates
119
- │ ├── proof-span.ts mark schema + serialization helpers (shared client/server)
120
- │ ├── sidecar.ts read/write/migrate sidecar JSON
121
- │ ├── ops-applier.ts op execution against (markdown, sidecar) tuple
122
- │ ├── event-bus.ts emit/poll/ack events on sidecar
123
- │ ├── idempotency.ts LRU keyed by Idempotency-Key
124
- │ ├── mutex.ts per-file async mutex
125
- │ ├── auth.ts bearer-token check middleware
126
- │ └── types.ts Op, Block, Snapshot, Event, Comment, Suggestion
127
- ├── components/editor/
128
- │ ├── extensions/
129
- │ │ └── proof-span.ts ← NEW TipTap mark for <proof-span>
130
- │ ├── proof-span-popover.tsx ← NEW Hover popover: origin, basis, Accept/Revert
131
- │ ├── comment-pip.tsx ← NEW Gutter pip per commented block
132
- │ ├── comment-thread.tsx ← NEW Thread popover w/ reply form
133
- │ ├── suggestion-card.tsx ← NEW Inline pending-suggestion card with Accept/Reject
134
- │ └── extensions.ts ← EDIT Register ProofSpan mark
135
- ├── components/ai-panel/ ← NEW
136
- │ ├── ai-panel.tsx Right-side drawer: connections + activity + token UI
137
- │ ├── activity-row.tsx
138
- │ └── token-section.tsx
139
- ├── stores/
140
- │ ├── proof-store.ts ← NEW zustand: sidecar data per file, event tail cursor
141
- │ └── ai-panel-store.ts ← EDIT Wire to real activity feed (replace stub)
142
- ├── lib/markdown/
143
- │ └── to-markdown.ts ← EDIT Add turndown rule preserving <proof-span>
144
- └── lib/proof-config.ts ← NEW Reads AGENT_BEARER_TOKEN, rate limits, etc.
145
- ```
146
-
147
- Approx LoC: ~1900. One worker session.
148
-
149
- ---
150
-
151
- ## 4. Wire formats
152
-
153
- ### 4.1 The HTTP surface
154
-
155
- All routes mount under `/api/agent/`. Authentication: `Authorization: Bearer <token>` header. If `AGENT_BEARER_TOKEN` env var is unset, accept any request from `127.0.0.1` and reject all others.
156
-
157
- | Method | Route | Purpose |
158
- | ------ | ---------------------------------------------- | ---------------------------------------------------------- |
159
- | `GET` | `/api/agent/files/<path>` | Snapshot: blocks + revision + meta |
160
- | `POST` | `/api/agent/files/<path>` | Apply ops, return new snapshot |
161
- | `GET` | `/api/agent/events/<path>?after=<n>&limit=<n>` | Poll for events on this file |
162
- | `POST` | `/api/agent/events/<path>` | Acknowledge events (for cleanup) |
163
- | `GET` | `/api/agent/sidecar/<path>` | Browser-side UI loads comments/suggestions |
164
- | `GET` | `/api/agent/settings` | Returns `{ rateLimit, hasToken, root }` |
165
- | `POST` | `/api/agent/settings/token/regenerate` | Mints a new token (writes to `~/.wiki-viewer/agent-token`) |
166
-
167
- `<path>` uses Next.js catch-all `[...path]`. Segments are URL-encoded. Forbidden chars: `..`, leading `/`, anything resolving outside `ROOT_DIR`. Use the existing `safeRootPath` from `src/lib/root-dir.ts`.
168
-
169
- ### 4.2 Snapshot response
170
-
171
- ```json
172
- {
173
- "path": "notes/plan.md",
174
- "revision": 47,
175
- "createdAt": "2026-05-29T10:00:00Z",
176
- "updatedAt": "2026-05-29T10:30:12Z",
177
- "fingerprint": "sha256:ab12...",
178
- "blocks": [
179
- { "ref": "b1a3f0", "type": "heading", "level": 1, "markdown": "# Q2 Plan" },
180
- {
181
- "ref": "b7f2c1",
182
- "type": "paragraph",
183
- "markdown": "Ship the rewrite by end of June."
184
- },
185
- { "ref": "b9c104", "type": "heading", "level": 2, "markdown": "## Risks" },
186
- { "ref": "be0123", "type": "bulletList", "markdown": "- tbd" }
187
- ],
188
- "comments": [
189
- {
190
- "id": "c4a1",
191
- "ref": "b7f2c1",
192
- "resolved": false,
193
- "createdAt": "2026-05-29T10:00:00Z",
194
- "turns": [
195
- {
196
- "by": "human",
197
- "text": "Why end of June?",
198
- "at": "2026-05-29T10:00:00Z"
199
- },
200
- {
201
- "by": "ai:claude",
202
- "text": "Because of the API freeze.",
203
- "at": "2026-05-29T10:00:14Z"
204
- }
205
- ]
206
- }
207
- ],
208
- "suggestions": [
209
- {
210
- "id": "s3b2",
211
- "ref": "b7f2c1",
212
- "status": "pending",
213
- "by": "ai:claude",
214
- "kind": "replace",
215
- "markdown": "Ship the rewrite by July 15.",
216
- "basis": "described",
217
- "basisDetail": "user mentioned slippage in chat",
218
- "createdAt": "2026-05-29T10:01:00Z"
219
- }
220
- ],
221
- "lastEventId": 47
222
- }
223
- ```
224
-
225
- `fingerprint` is sha256 of the raw .md file on disk, hex-encoded.
226
-
227
- ### 4.3 Block type vocabulary
228
-
229
- Block parser must emit exactly one of these `type` values:
230
-
231
- - `heading` (with `level: 1..6`)
232
- - `paragraph`
233
- - `bulletList`
234
- - `orderedList`
235
- - `taskList`
236
- - `blockquote`
237
- - `codeBlock` (with optional `lang`)
238
- - `table`
239
- - `hr`
240
- - `html` (raw HTML block, e.g. embeds — kept verbatim)
241
-
242
- A "block" is anything at the root of the markdown AST. Lists are atomic — a 5-item list is one block, not 5. This matches Proof's model and keeps refs stable when items shuffle inside a list.
243
-
244
- ### 4.4 Op vocabulary
245
-
246
- Send via `POST /api/agent/files/<path>`. Body:
247
-
248
- ```json
249
- {
250
- "baseRevision": 47,
251
- "by": "ai:claude",
252
- "ops": [ <op>, <op>, ... ]
253
- }
254
- ```
255
-
256
- Headers required on mutations:
257
-
258
- - `Content-Type: application/json`
259
- - `Idempotency-Key: <uuid>` (treat as required — return 400 if absent)
260
- - `Authorization: Bearer <token>` (when token configured)
261
-
262
- The ops, applied in order (atomic — all succeed or all roll back):
263
-
264
- #### Block ops
265
-
266
- ```json
267
- { "type": "block.replace", "ref": "b7f2c1", "markdown": "New content." }
268
- { "type": "block.insertAfter", "ref": "b7f2c1", "markdown": "## New section\n\nBody." }
269
- { "type": "block.insertBefore","ref": "b7f2c1", "markdown": "..." }
270
- { "type": "block.delete", "ref": "b7f2c1" }
271
- { "type": "block.append", "markdown": "..." } // appended to file end, no ref
272
- { "type": "block.prepend", "markdown": "..." } // prepended to file start, no ref
273
- ```
274
-
275
- `markdown` may contain multiple top-level blocks. They are inserted in order at the target position.
276
-
277
- If the op originates from an AI agent (`by` starts with `ai:`), the **inserted text content is wrapped in a `<proof-span>` mark** automatically (see §5.3). The agent doesn't construct the span; the server does. Agents MAY supply additional metadata:
278
-
279
- ```json
280
- {
281
- "type": "block.insertAfter",
282
- "ref": "b7f2c1",
283
- "markdown": "The team will focus on three pillars...",
284
- "basis": "described",
285
- "basisDetail": "user asked for opening paragraph",
286
- "inResponseTo": "c4a1"
287
- }
288
- ```
289
-
290
- `basis` ∈ `"described" | "inferred" | "suggested"`. Defaults to `"inferred"` for AI ops.
291
-
292
- #### Comment ops
293
-
294
- ```json
295
- { "type": "comment.add", "ref": "b7f2c1", "text": "Why end of June?" }
296
- { "type": "comment.reply", "commentId": "c4a1", "text": "Because of API freeze." }
297
- { "type": "comment.resolve", "commentId": "c4a1" }
298
- { "type": "comment.reopen", "commentId": "c4a1" }
299
- ```
300
-
301
- `by` is taken from the request body's top-level `by` field.
302
-
303
- #### Suggestion ops
304
-
305
- ```json
306
- {
307
- "type": "suggestion.add",
308
- "ref": "b7f2c1",
309
- "kind": "replace",
310
- "markdown": "New content.",
311
- "basis": "described",
312
- "basisDetail": "user asked for clarity",
313
- "status": "pending"
314
- }
315
- ```
316
-
317
- - `kind` ∈ `"replace" | "insertAfter" | "insertBefore" | "delete"`
318
- - `status` defaults to `"pending"`. If passed as `"accepted"`, the server applies the change immediately in the same mutation (equivalent to adding then accepting in one shot — Proof-compatible behaviour).
319
-
320
- ```json
321
- { "type": "suggestion.accept", "suggestionId": "s3b2" }
322
- { "type": "suggestion.reject", "suggestionId": "s3b2" }
323
- ```
324
-
325
- On accept: apply the suggestion as the corresponding `block.*` op, then mark suggestion `accepted` in sidecar. On reject: mark `rejected` and prune from active list (move to `archivedSuggestions` array, keep for event history but don't include in default snapshot).
326
-
327
- ### 4.5 Response codes
328
-
329
- | Status | Code field | Meaning |
330
- | ------ | -------------------------------------------- | ------------------------------------------------------------------------ |
331
- | 200 | — | Success, body is new snapshot |
332
- | 400 | `INVALID_PAYLOAD` | Malformed body / missing `Idempotency-Key` |
333
- | 401 | `UNAUTHORIZED` | Bad / missing bearer token (when token configured) |
334
- | 404 | `FILE_NOT_FOUND` | Path doesn't resolve under ROOT_DIR |
335
- | 409 | `STALE_REVISION` | `baseRevision` doesn't match current. Response includes fresh snapshot. |
336
- | 409 | `BLOCK_NOT_FOUND` | A block ref in an op no longer exists. Response includes fresh snapshot. |
337
- | 409 | `COMMENT_NOT_FOUND` / `SUGGESTION_NOT_FOUND` | Same idea. |
338
- | 422 | `INVALID_MARKDOWN` | Op's `markdown` field fails to parse cleanly |
339
- | 429 | `RATE_LIMITED` | See §6.3 |
340
- | 500 | `INTERNAL` | Unexpected. Log to server. |
341
-
342
- `Idempotency-Key` replay: if the same key + same payload hash is seen within 5 minutes, return the cached response (same status code and body). If same key + different payload, return 409 with `IDEMPOTENCY_KEY_REUSED`.
343
-
344
- ### 4.6 Events
345
-
346
- Every successful mutation emits one or more events appended to the sidecar's `events` array. Event IDs are monotonic per file (not global).
347
-
348
- ```json
349
- { "id": 48, "type": "block.replaced", "ref": "b7f2c1", "by": "ai:claude", "at": "...", "newRef": "b8f3d2" }
350
- { "id": 49, "type": "block.inserted", "after": "b7f2c1", "refs": ["b8f3d2", "b8f3d3"], "by": "ai:claude", "at": "..." }
351
- { "id": 50, "type": "block.deleted", "ref": "b9c104", "by": "human", "at": "..." }
352
- { "id": 51, "type": "comment.added", "commentId": "c4a1", "ref": "b7f2c1", "text": "...", "by": "human", "at": "..." }
353
- { "id": 52, "type": "comment.replied", "commentId": "c4a1", "text": "...", "by": "ai:claude", "at": "..." }
354
- { "id": 53, "type": "comment.resolved", "commentId": "c4a1", "by": "human", "at": "..." }
355
- { "id": 54, "type": "suggestion.added", "suggestionId": "s3b2", "by": "ai:claude", "at": "..." }
356
- { "id": 55, "type": "suggestion.accepted", "suggestionId": "s3b2", "by": "human", "at": "..." }
357
- { "id": 56, "type": "suggestion.rejected", "suggestionId": "s3b2", "by": "human", "at": "..." }
358
- { "id": 57, "type": "file.externallyEdited", "fingerprint": "sha256:...", "at": "..." }
359
- { "id": 58, "type": "span.accepted", "spanId": "p1", "by": "human", "at": "..." }
360
- { "id": 59, "type": "span.reverted", "spanId": "p1", "by": "human", "at": "..." }
361
- ```
362
-
363
- `file.externallyEdited`: emitted when chokidar detects a file change that did NOT come from a mutation API call. This is how remote agents notice that the human edited in vim.
364
-
365
- Poll: `GET /api/agent/events/<path>?after=47&limit=100`. Default limit 100, max 1000. Returns `{ "events": [...], "lastEventId": 59 }`.
366
-
367
- Ack: `POST /api/agent/events/<path>` with body `{ "upToId": 59, "by": "ai:claude" }`. HTTP method dispatch: GET=poll, POST=ack.
368
-
369
- > **Note:** Route paths restructured for Next 16 catch-all compliance; semantics unchanged. **Acks are purely advisory** — events are never deleted. The ack writes a `lastAck.<by>` cursor on the sidecar, for diagnostic purposes. This is intentional: events are a small append-only log, file sizes stay bounded by §6.5 trimming.
370
-
371
- ---
372
-
373
- ## 5. Detailed implementation
374
-
375
- ### 5.1 `src/lib/proof/types.ts`
376
-
377
- ```ts
378
- export type BlockType =
379
- | "heading"
380
- | "paragraph"
381
- | "bulletList"
382
- | "orderedList"
383
- | "taskList"
384
- | "blockquote"
385
- | "codeBlock"
386
- | "table"
387
- | "hr"
388
- | "html";
389
-
390
- export interface Block {
391
- ref: string; // "b" + 6-hex
392
- type: BlockType;
393
- level?: number; // headings only
394
- lang?: string; // codeBlock only
395
- markdown: string; // canonical markdown for this block, trailing \n stripped
396
- }
397
-
398
- export interface ProvenanceMeta {
399
- origin: "human" | "ai";
400
- basis?: "described" | "inferred" | "suggested";
401
- basisDetail?: string;
402
- by?: string; // "ai:claude" or "human"
403
- at?: string; // ISO 8601
404
- spanId: string; // "p" + 4-hex
405
- inResponseTo?: string; // comment id
406
- }
407
-
408
- export interface CommentTurn {
409
- by: string; // "human" | "ai:claude"
410
- text: string;
411
- at: string;
412
- }
413
-
414
- export interface Comment {
415
- id: string; // "c" + 4-hex
416
- ref: string; // block ref it's attached to
417
- resolved: boolean;
418
- createdAt: string;
419
- turns: CommentTurn[];
420
- }
421
-
422
- export type SuggestionKind =
423
- | "replace"
424
- | "insertAfter"
425
- | "insertBefore"
426
- | "delete";
427
- export type SuggestionStatus = "pending" | "accepted" | "rejected";
428
-
429
- export interface Suggestion {
430
- id: string; // "s" + 4-hex
431
- ref: string;
432
- kind: SuggestionKind;
433
- status: SuggestionStatus;
434
- by: string;
435
- markdown?: string; // omitted for kind=delete
436
- basis?: ProvenanceMeta["basis"];
437
- basisDetail?: string;
438
- createdAt: string;
439
- resolvedAt?: string; // when accepted/rejected
440
- resolvedBy?: string;
441
- }
442
-
443
- export interface ProofEvent {
444
- id: number;
445
- type: string; // see §4.6 list
446
- at: string;
447
- by: string;
448
- [k: string]: unknown;
449
- }
450
-
451
- export interface Sidecar {
452
- schemaVersion: 1;
453
- path: string;
454
- revision: number;
455
- createdAt: string;
456
- updatedAt: string;
457
- // Map of block.ref → current text fingerprint (sha256 of block markdown, first 12 hex).
458
- // Used to detect external edits and to keep refs stable through round-trips.
459
- refMap: Record<string, { textHash: string; lastSeenAt: string }>;
460
- // History of ref renames. Old ref → new ref, kept for ONE generation.
461
- refAliases: Record<string, string>;
462
- comments: Comment[];
463
- suggestions: Suggestion[];
464
- archivedSuggestions: Suggestion[];
465
- events: ProofEvent[];
466
- nextEventId: number;
467
- lastAck: Record<string, number>; // by → eventId
468
- fingerprint: string; // last-known sha256 of the .md file
469
- }
470
-
471
- export type Op =
472
- | {
473
- type: "block.replace";
474
- ref: string;
475
- markdown: string;
476
- basis?: string;
477
- basisDetail?: string;
478
- inResponseTo?: string;
479
- }
480
- | {
481
- type: "block.insertAfter";
482
- ref: string;
483
- markdown: string;
484
- basis?: string;
485
- basisDetail?: string;
486
- inResponseTo?: string;
487
- }
488
- | {
489
- type: "block.insertBefore";
490
- ref: string;
491
- markdown: string;
492
- basis?: string;
493
- basisDetail?: string;
494
- inResponseTo?: string;
495
- }
496
- | { type: "block.delete"; ref: string }
497
- | {
498
- type: "block.append";
499
- markdown: string;
500
- basis?: string;
501
- basisDetail?: string;
502
- inResponseTo?: string;
503
- }
504
- | {
505
- type: "block.prepend";
506
- markdown: string;
507
- basis?: string;
508
- basisDetail?: string;
509
- inResponseTo?: string;
510
- }
511
- | { type: "comment.add"; ref: string; text: string }
512
- | { type: "comment.reply"; commentId: string; text: string }
513
- | { type: "comment.resolve"; commentId: string }
514
- | { type: "comment.reopen"; commentId: string }
515
- | {
516
- type: "suggestion.add";
517
- ref: string;
518
- kind: SuggestionKind;
519
- markdown?: string;
520
- basis?: string;
521
- basisDetail?: string;
522
- status?: SuggestionStatus;
523
- }
524
- | { type: "suggestion.accept"; suggestionId: string }
525
- | { type: "suggestion.reject"; suggestionId: string };
526
-
527
- export interface Snapshot {
528
- path: string;
529
- revision: number;
530
- createdAt: string;
531
- updatedAt: string;
532
- fingerprint: string;
533
- blocks: Block[];
534
- comments: Comment[]; // unresolved + resolved (separately by client)
535
- suggestions: Suggestion[]; // pending only by default
536
- lastEventId: number;
537
- }
538
- ```
539
-
540
- ### 5.2 `src/lib/proof/blocks.ts` — markdown ↔ blocks
541
-
542
- Reuse what's in `package.json`: `unified`, `remark-parse`, `remark-gfm`. **Do not add new deps.**
543
-
544
- ```ts
545
- import { unified } from "unified";
546
- import remarkParse from "remark-parse";
547
- import remarkGfm from "remark-gfm";
548
- import remarkStringify from "remark-stringify";
549
- import type { Root, RootContent } from "mdast";
550
-
551
- const PARSER = unified().use(remarkParse).use(remarkGfm);
552
- const STRINGIFY = unified()
553
- .use(remarkStringify, {
554
- bullet: "-",
555
- fence: "`",
556
- fences: true,
557
- listItemIndent: "one",
558
- rule: "-",
559
- emphasis: "*",
560
- strong: "*",
561
- })
562
- .use(remarkGfm);
563
-
564
- export function parseBlocks(markdown: string): RootContent[] {
565
- const tree = PARSER.parse(markdown) as Root;
566
- return tree.children;
567
- }
568
-
569
- export function blockToMarkdown(node: RootContent): string {
570
- const tree: Root = { type: "root", children: [node] };
571
- return (STRINGIFY.stringify(tree) as string).replace(/\n+$/, "");
572
- }
573
-
574
- export function blocksToMarkdown(nodes: RootContent[]): string {
575
- const tree: Root = { type: "root", children: nodes };
576
- return (STRINGIFY.stringify(tree) as string).replace(/\n+$/, "") + "\n";
577
- }
578
-
579
- export function blockType(node: RootContent): {
580
- type: BlockType;
581
- level?: number;
582
- lang?: string;
583
- } {
584
- switch (node.type) {
585
- case "heading":
586
- return { type: "heading", level: (node as any).depth };
587
- case "paragraph":
588
- return { type: "paragraph" };
589
- case "list": {
590
- const n = node as any;
591
- if (
592
- n.children?.some(
593
- (li: any) => li.checked !== null && li.checked !== undefined,
594
- )
595
- )
596
- return { type: "taskList" };
597
- return { type: n.ordered ? "orderedList" : "bulletList" };
598
- }
599
- case "blockquote":
600
- return { type: "blockquote" };
601
- case "code":
602
- return { type: "codeBlock", lang: (node as any).lang ?? undefined };
603
- case "table":
604
- return { type: "table" };
605
- case "thematicBreak":
606
- return { type: "hr" };
607
- case "html":
608
- return { type: "html" };
609
- default:
610
- return { type: "paragraph" }; // fallback
611
- }
612
- }
613
- ```
614
-
615
- **Important roundtrip note:** `<proof-span>` elements inside paragraphs are parsed as raw HTML by remark and preserved in the AST. They survive `parse → stringify` unchanged. **Verify this with a unit test** (see §7).
616
-
617
- ### 5.3 `src/lib/proof/proof-span.ts` — provenance marks
618
-
619
- Format (inline HTML, valid in CommonMark and GFM, ignored by markdown renderers, decorated by TipTap):
620
-
621
- ```html
622
- <proof-span
623
- id="p4a1"
624
- origin="ai"
625
- basis="described"
626
- by="ai:claude"
627
- at="2026-05-29T10:00:00Z"
628
- in-response-to="c4a1"
629
- >The text the AI wrote.</proof-span
630
- >
631
- ```
632
-
633
- ```ts
634
- export interface SpanAttrs {
635
- spanId: string;
636
- origin: "ai" | "human";
637
- basis?: string;
638
- basisDetail?: string;
639
- by: string;
640
- at: string;
641
- inResponseTo?: string;
642
- }
643
-
644
- const SPAN_OPEN = /<proof-span\b([^>]*)>/g;
645
-
646
- export function wrapAsProofSpan(markdown: string, attrs: SpanAttrs): string {
647
- // Find first non-whitespace leading content; wrap whole block's text content.
648
- // The simplest correct approach: stringify the block, then wrap the
649
- // *content portion* (everything after the leading markdown sigil) in a span.
650
- //
651
- // For paragraphs / list items: wrap the text content directly.
652
- // For headings: wrap text after the leading "#"s.
653
- // For code blocks, tables, hr: do NOT wrap — emit a separate sidecar-only
654
- // provenance record (see §5.4 ProvenanceMeta on sidecar).
655
- //
656
- // The wrapper takes care of escaping " in attrs via &quot;.
657
- ...
658
- }
659
- ```
660
-
661
- Wrap rules (be conservative — if in doubt, don't wrap and record in sidecar instead):
662
-
663
- | Block type | Wrap inline? |
664
- | ----------------------------------- | ------------------------------------------------------- |
665
- | paragraph | yes, wrap text content |
666
- | heading | yes, wrap text after `#`s |
667
- | bulletList / orderedList / taskList | yes, wrap each list item's text |
668
- | blockquote | yes, wrap text content |
669
- | codeBlock | no — store provenance in sidecar `blockProvenance[ref]` |
670
- | table | no — sidecar only |
671
- | hr | no — sidecar only |
672
- | html | no — sidecar only |
673
-
674
- Add to `Sidecar` interface:
675
-
676
- ```ts
677
- blockProvenance?: Record<string, SpanAttrs>; // for blocks we can't wrap inline
678
- ```
679
-
680
- `unwrap` is straightforward: strip `<proof-span ...>` and `</proof-span>` from a markdown string for "Accept" (keep content, drop attribution mark). For "Revert" (delete content): the editor passes the span ID, server looks up the block, replaces span content with empty string, prunes empty paragraph if result is empty.
681
-
682
- ### 5.4 `src/lib/proof/block-refs.ts` — ref stability
683
-
684
- Goal: when the agent has snapshot at revision 47 and posts `block.replace ref="b7f2c1"` based on it, the server must find that block even if the content has changed slightly (it shouldn't have, but defense-in-depth) and must give the new resulting block a stable new ref.
685
-
686
- Algorithm (one-shot, no LSM, no complex CRDT):
687
-
688
- 1. **First parse** (no sidecar exists): for each block, compute `textHash = sha256(blockMarkdown).slice(0,12)`. Ref = `"b" + textHash.slice(0,6)`. Collision? Append `_<position>`.
689
- 2. **Subsequent parse** (sidecar exists with refMap): compute `textHash` per current block. For each block:
690
- - If `textHash` matches an existing ref's `textHash` → reuse that ref.
691
- - Else: this is a new block. Mint a new ref the same way as (1).
692
- 3. **After applying ops**: regenerate refMap from the new block list. For any old ref that was replaced/changed, record in `refAliases[oldRef] = newRef`. Aliases only persist ONE revision — they're flushed on the next mutation.
693
-
694
- This gives "refs follow content when content barely changed" and "in-flight agent retries within one revision still resolve" without true content-addressable storage.
695
-
696
- Block resolution for ops:
697
-
698
- ```ts
699
- function resolveRef(
700
- sidecar: Sidecar,
701
- ref: string,
702
- currentRefs: Set<string>,
703
- ): string | null {
704
- if (currentRefs.has(ref)) return ref;
705
- if (sidecar.refAliases[ref] && currentRefs.has(sidecar.refAliases[ref])) {
706
- return sidecar.refAliases[ref];
707
- }
708
- return null;
709
- }
710
- ```
711
-
712
- ### 5.5 `src/lib/proof/sidecar.ts`
713
-
714
- ```ts
715
- export function sidecarPath(rootDir: string, mdPath: string): string {
716
- return path.join(rootDir, ".proof", mdPath + ".json");
717
- }
718
-
719
- export async function readSidecar(rootDir: string, mdPath: string): Promise<Sidecar | null> { ... }
720
- export async function writeSidecar(rootDir: string, mdPath: string, sc: Sidecar): Promise<void> {
721
- await mkdir(path.dirname(sidecarPath(rootDir, mdPath)), { recursive: true });
722
- // Atomic-ish write: write to .tmp, fsync, rename.
723
- const tmp = sidecarPath(rootDir, mdPath) + ".tmp";
724
- await writeFile(tmp, JSON.stringify(sc, null, 2), "utf-8");
725
- await rename(tmp, sidecarPath(rootDir, mdPath));
726
- }
727
- export function emptySidecar(mdPath: string): Sidecar { ... }
728
- ```
729
-
730
- Schema migrations: keep `schemaVersion: 1`. On read, if a different version → fail loudly (not silently). One-shot project, accept the constraint.
731
-
732
- ### 5.6 `src/lib/proof/ops-applier.ts` — the heart
733
-
734
- Single entry point:
735
-
736
- ```ts
737
- export async function applyOps(args: {
738
- rootDir: string;
739
- mdPath: string;
740
- baseRevision: number;
741
- by: string;
742
- ops: Op[];
743
- }): Promise<
744
- | { ok: true; snapshot: Snapshot; emittedEvents: ProofEvent[] }
745
- | {
746
- ok: false;
747
- status: number;
748
- code: string;
749
- message: string;
750
- snapshot?: Snapshot;
751
- }
752
- >;
753
- ```
754
-
755
- Implementation outline (pseudo):
756
-
757
- ```
758
- acquire perFileMutex(mdPath)
759
- try:
760
- read .md from disk
761
- fingerprint = sha256(.md)
762
- sidecar = readSidecar() ?? emptySidecar(mdPath)
763
-
764
- if sidecar.fingerprint && sidecar.fingerprint !== fingerprint:
765
- // External edit happened. Re-parse blocks (refs auto-stabilize via §5.4),
766
- // emit "file.externallyEdited", bump revision, persist, then continue.
767
-
768
- blocks = parseBlocks(.md)
769
- assignedBlocks = assignRefs(blocks, sidecar) // returns Block[] with refs
770
-
771
- if baseRevision !== sidecar.revision:
772
- return STALE_REVISION + freshSnapshot
773
-
774
- workingBlocks = [...assignedBlocks]
775
- workingSidecar = clone(sidecar)
776
- workingEvents = []
777
-
778
- for op of ops:
779
- apply op:
780
- block.* → mutate workingBlocks, regen refs for changed/new, append events
781
- comment.* → mutate workingSidecar.comments, append events
782
- suggestion.add → push to workingSidecar.suggestions
783
- if status === "accepted": apply as block.* in-place + log accepted
784
- suggestion.accept → look up suggestion, apply as block op, mark accepted
785
- suggestion.reject → move to archivedSuggestions, log rejected
786
- if op fails (ref not found etc.): rollback, return appropriate 409
787
-
788
- newMarkdown = blocksToMarkdown(workingBlocks)
789
- newFingerprint = sha256(newMarkdown)
790
- workingSidecar.revision += 1
791
- workingSidecar.updatedAt = now()
792
- workingSidecar.fingerprint = newFingerprint
793
- workingSidecar.refMap = recomputeRefMap(workingBlocks)
794
- workingSidecar.refAliases = collectedAliases // from this op batch only
795
- workingSidecar.events.push(...workingEvents)
796
- workingSidecar.nextEventId += workingEvents.length
797
-
798
- writeFile(.md, newMarkdown)
799
- writeSidecar(workingSidecar)
800
-
801
- return { ok: true, snapshot, emittedEvents }
802
- finally:
803
- release mutex
804
- ```
805
-
806
- **Important: AI wrapping happens here**, not in routes. When applying `block.insertAfter` from an `ai:*` actor:
807
-
808
- - Parse the op's `markdown` into block nodes
809
- - For each text-bearing node, wrap text in a freshly-minted `<proof-span>` with attrs from the op (`origin: "ai"`, `basis`, `basisDetail`, `by`, `at`, `spanId`, `inResponseTo`)
810
- - For non-wrappable block types (code, table, hr, html), record into `sidecar.blockProvenance[newRef]` instead
811
- - Stringify back to markdown for insertion
812
-
813
- When `by` is `"human"` (or any non-`ai:*` value), no wrap. Human edits are unmarked by default.
814
-
815
- ### 5.7 Routes — `src/app/api/agent/files/[...path]/route.ts`
816
-
817
- ```ts
818
- export const runtime = "nodejs";
819
-
820
- import { NextResponse } from "next/server";
821
- import { getRootDir, safeRootPath } from "@/lib/root-dir";
822
- import { checkAuth } from "@/lib/proof/auth";
823
- import { applyOps } from "@/lib/proof/ops-applier";
824
- import { readSnapshot } from "@/lib/proof/ops-applier"; // pure read variant
825
- import { idempotency } from "@/lib/proof/idempotency";
826
-
827
- export async function GET(
828
- req: Request,
829
- { params }: { params: { path: string[] } },
830
- ) {
831
- const auth = checkAuth(req);
832
- if (!auth.ok)
833
- return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
834
-
835
- const rel = params.path.map(decodeURIComponent).join("/");
836
- const abs = safeRootPath(rel);
837
- if (!abs)
838
- return NextResponse.json({ error: "INVALID_PATH" }, { status: 400 });
839
-
840
- const snap = await readSnapshot(getRootDir()!, rel);
841
- if (!snap)
842
- return NextResponse.json({ error: "FILE_NOT_FOUND" }, { status: 404 });
843
- return NextResponse.json(snap);
844
- }
845
-
846
- export async function POST(req: Request, { params }) {
847
- const auth = checkAuth(req);
848
- if (!auth.ok)
849
- return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
850
-
851
- const idemKey = req.headers.get("idempotency-key");
852
- if (!idemKey)
853
- return NextResponse.json(
854
- { error: "INVALID_PAYLOAD", message: "Idempotency-Key required" },
855
- { status: 400 },
856
- );
857
-
858
- const body = await req.json();
859
- const rel = params.path.map(decodeURIComponent).join("/");
860
- const payloadHash = sha256(JSON.stringify({ rel, body }));
861
-
862
- const cached = idempotency.get(idemKey);
863
- if (cached) {
864
- if (cached.payloadHash !== payloadHash) {
865
- return NextResponse.json(
866
- { error: "IDEMPOTENCY_KEY_REUSED" },
867
- { status: 409 },
868
- );
869
- }
870
- return new NextResponse(cached.body, {
871
- status: cached.status,
872
- headers: { "content-type": "application/json" },
873
- });
874
- }
875
-
876
- const result = await applyOps({
877
- rootDir: getRootDir()!,
878
- mdPath: rel,
879
- baseRevision: body.baseRevision,
880
- by: body.by,
881
- ops: body.ops,
882
- });
883
-
884
- const status = result.ok ? 200 : result.status;
885
- const respBody = JSON.stringify(
886
- result.ok
887
- ? result.snapshot
888
- : {
889
- error: result.code,
890
- message: result.message,
891
- snapshot: result.snapshot,
892
- },
893
- );
894
- idempotency.set(idemKey, { payloadHash, status, body: respBody });
895
-
896
- return new NextResponse(respBody, {
897
- status,
898
- headers: { "content-type": "application/json" },
899
- });
900
- }
901
- ```
902
-
903
- Events route: `src/app/api/agent/events/[...path]/route.ts` — GET polls `sidecar.events.filter(e => e.id > after).slice(0, limit)`; POST bumps `sidecar.lastAck[by] = upToId` and persists. (Route paths restructured for Next 16 catch-all compliance; semantics unchanged.)
904
-
905
- ### 5.8 `src/lib/proof/auth.ts`
906
-
907
- ```ts
908
- import { readFileSync, existsSync } from "node:fs";
909
- import path from "node:path";
910
- import os from "node:os";
911
-
912
- const TOKEN_PATH = path.join(os.homedir(), ".wiki-viewer", "agent-token");
913
-
914
- function configuredToken(): string | null {
915
- if (process.env.AGENT_BEARER_TOKEN) return process.env.AGENT_BEARER_TOKEN;
916
- try {
917
- if (existsSync(TOKEN_PATH))
918
- return readFileSync(TOKEN_PATH, "utf-8").trim() || null;
919
- } catch {}
920
- return null;
921
- }
922
-
923
- export function checkAuth(
924
- req: Request,
925
- ): { ok: true; localhost: boolean } | { ok: false } {
926
- const token = configuredToken();
927
-
928
- // Inspect client origin. Trust X-Forwarded-For only if explicitly behind a proxy we control.
929
- // For localhost: check Host header is localhost / 127.0.0.1 / ::1.
930
- const host = req.headers.get("host") ?? "";
931
- const isLocal =
932
- host.startsWith("localhost") ||
933
- host.startsWith("127.0.0.1") ||
934
- host.startsWith("[::1]");
935
-
936
- if (!token) {
937
- // No token configured. Allow only local requests.
938
- return isLocal ? { ok: true, localhost: true } : { ok: false };
939
- }
940
-
941
- const auth = req.headers.get("authorization") ?? "";
942
- const presented = auth.startsWith("Bearer ") ? auth.slice(7) : "";
943
- if (presented && timingSafeEqualStr(presented, token)) {
944
- return { ok: true, localhost: isLocal };
945
- }
946
- return { ok: false };
947
- }
948
- ```
949
-
950
- `timingSafeEqualStr`: pad both to equal length, use Node `crypto.timingSafeEqual`.
951
-
952
- ### 5.9 `src/lib/proof/idempotency.ts`
953
-
954
- ```ts
955
- // Tiny in-memory LRU. Lives for process lifetime. Acceptable: idempotency
956
- // guards retries within seconds, not days.
957
- const MAX = 1000;
958
- const TTL_MS = 5 * 60 * 1000;
959
-
960
- interface Entry {
961
- payloadHash: string;
962
- status: number;
963
- body: string;
964
- expiresAt: number;
965
- }
966
- const store = new Map<string, Entry>();
967
-
968
- export const idempotency = {
969
- get(key: string): Entry | null {
970
- const e = store.get(key);
971
- if (!e) return null;
972
- if (e.expiresAt < Date.now()) {
973
- store.delete(key);
974
- return null;
975
- }
976
- return e;
977
- },
978
- set(key: string, value: Omit<Entry, "expiresAt">): void {
979
- if (store.size >= MAX) {
980
- const first = store.keys().next().value;
981
- if (first) store.delete(first);
982
- }
983
- store.set(key, { ...value, expiresAt: Date.now() + TTL_MS });
984
- },
985
- };
986
- ```
987
-
988
- ### 5.10 `src/lib/proof/mutex.ts`
989
-
990
- ```ts
991
- const locks = new Map<string, Promise<void>>();
992
-
993
- export async function withFileMutex<T>(
994
- path: string,
995
- fn: () => Promise<T>,
996
- ): Promise<T> {
997
- while (locks.has(path)) await locks.get(path);
998
- let release!: () => void;
999
- const p = new Promise<void>((r) => {
1000
- release = r;
1001
- });
1002
- locks.set(path, p);
1003
- try {
1004
- return await fn();
1005
- } finally {
1006
- locks.delete(path);
1007
- release();
1008
- }
1009
- }
1010
- ```
1011
-
1012
- ### 5.11 Browser side — TipTap mark `src/components/editor/extensions/proof-span.ts`
1013
-
1014
- ```ts
1015
- import { Mark, mergeAttributes } from "@tiptap/core";
1016
-
1017
- export const ProofSpan = Mark.create({
1018
- name: "proofSpan",
1019
- priority: 900,
1020
- inclusive: false,
1021
- keepOnSplit: false,
1022
-
1023
- addAttributes() {
1024
- return {
1025
- spanId: {
1026
- default: null,
1027
- parseHTML: (el) => el.getAttribute("id"),
1028
- renderHTML: (a) => ({ id: a.spanId }),
1029
- },
1030
- origin: {
1031
- default: "ai",
1032
- parseHTML: (el) => el.getAttribute("origin"),
1033
- renderHTML: (a) => ({ origin: a.origin }),
1034
- },
1035
- basis: {
1036
- default: null,
1037
- parseHTML: (el) => el.getAttribute("basis"),
1038
- renderHTML: (a) => ({ basis: a.basis }),
1039
- },
1040
- basisDetail: {
1041
- default: null,
1042
- parseHTML: (el) => el.getAttribute("basis-detail"),
1043
- renderHTML: (a) => ({ "basis-detail": a.basisDetail }),
1044
- },
1045
- by: {
1046
- default: null,
1047
- parseHTML: (el) => el.getAttribute("by"),
1048
- renderHTML: (a) => ({ by: a.by }),
1049
- },
1050
- at: {
1051
- default: null,
1052
- parseHTML: (el) => el.getAttribute("at"),
1053
- renderHTML: (a) => ({ at: a.at }),
1054
- },
1055
- inResponseTo: {
1056
- default: null,
1057
- parseHTML: (el) => el.getAttribute("in-response-to"),
1058
- renderHTML: (a) => ({ "in-response-to": a.inResponseTo }),
1059
- },
1060
- };
1061
- },
1062
- parseHTML() {
1063
- return [{ tag: "proof-span" }];
1064
- },
1065
- renderHTML({ HTMLAttributes }) {
1066
- return [
1067
- "proof-span",
1068
- mergeAttributes(HTMLAttributes, { class: "proof-span" }),
1069
- 0,
1070
- ];
1071
- },
1072
- });
1073
- ```
1074
-
1075
- CSS in `globals.css`:
1076
-
1077
- ```css
1078
- proof-span.proof-span,
1079
- .proof-span {
1080
- background: linear-gradient(
1081
- to right,
1082
- rgba(165, 180, 252, 0.08),
1083
- rgba(165, 180, 252, 0)
1084
- );
1085
- border-left: 2px solid rgb(165, 180, 252);
1086
- padding-left: 4px;
1087
- border-radius: 2px;
1088
- cursor: pointer;
1089
- transition: background 120ms ease;
1090
- }
1091
- proof-span.proof-span[origin="human"] {
1092
- border-left-color: rgb(110, 231, 183);
1093
- background: linear-gradient(
1094
- to right,
1095
- rgba(110, 231, 183, 0.08),
1096
- rgba(110, 231, 183, 0)
1097
- );
1098
- }
1099
- proof-span.proof-span:hover {
1100
- background: linear-gradient(
1101
- to right,
1102
- rgba(165, 180, 252, 0.18),
1103
- rgba(165, 180, 252, 0.04)
1104
- );
1105
- }
1106
- .dark proof-span.proof-span {
1107
- background: linear-gradient(
1108
- to right,
1109
- rgba(165, 180, 252, 0.14),
1110
- rgba(165, 180, 252, 0)
1111
- );
1112
- }
1113
- ```
1114
-
1115
- ### 5.12 Turndown rule — `src/lib/markdown/to-markdown.ts` (EDIT)
1116
-
1117
- Add a turndown rule preserving `<proof-span>` with attrs (similar to existing `styledSpan` rule):
1118
-
1119
- ```ts
1120
- turndown.addRule("proofSpan", {
1121
- filter: (node) => node.nodeName === "PROOF-SPAN",
1122
- replacement: (content, node) => {
1123
- const el = node as HTMLElement;
1124
- const attrs: string[] = [];
1125
- for (const a of Array.from(el.attributes)) {
1126
- attrs.push(`${a.name}="${a.value.replace(/"/g, "&quot;")}"`);
1127
- }
1128
- return `<proof-span${attrs.length ? " " + attrs.join(" ") : ""}>${content}</proof-span>`;
1129
- },
1130
- });
1131
- ```
1132
-
1133
- Also: lowercase `<proof-span>` is preserved through DOM uppercasing (`nodeName === "PROOF-SPAN"` because DOM uppercases). Verify with a test.
1134
-
1135
- ### 5.13 Editor UI
1136
-
1137
- #### `proof-span-popover.tsx`
1138
-
1139
- On hover over a `.proof-span` element, show a small floating card:
1140
-
1141
- ```
1142
- ┌────────────────────────────────────┐
1143
- │ ai:claude · described · 12s ago │
1144
- │ "user asked for opening paragraph" │
1145
- │ │
1146
- │ [Accept] [Revert] [Comment] │
1147
- └────────────────────────────────────┘
1148
- ```
1149
-
1150
- - Accept → POST to a special internal endpoint that strips the span from current file (server unwraps and rewrites .md, emits `span.accepted` event)
1151
- - Revert → same endpoint with action=revert (deletes the wrapped content)
1152
- - Comment → opens `comment-thread.tsx` in "new comment" mode anchored to the parent block
1153
-
1154
- Use a dedicated internal route `POST /api/agent/internal/span` (body: `{ path, spanId, action: "accept"|"revert" }`). Only accept localhost requests on this route (it's UI-only, not part of the agent surface).
1155
-
1156
- #### `comment-pip.tsx` + `comment-thread.tsx`
1157
-
1158
- Margin pip rendered via a TipTap decoration plugin. Reads `useProofStore` for comments by ref. State pip icon based on:
1159
-
1160
- - ● dot (filled) — open thread, last turn is AI
1161
- - ○ ring — open thread, last turn is human
1162
- - ✓ check — resolved (faded)
1163
-
1164
- Click pip → opens thread popover anchored to the block. Reply form at bottom. Resolve / Reopen buttons in header.
1165
-
1166
- #### `suggestion-card.tsx`
1167
-
1168
- Pending suggestion shown inline above/below its target block, depending on `kind`:
1169
-
1170
- ```
1171
- ┌──────────────────────────────────────────┐
1172
- │ claude suggests replacing this block ▸ │
1173
- │ │
1174
- │ ─ current ────────────────────────────── │
1175
- │ Ship the rewrite by end of June. │
1176
- │ │
1177
- │ ─ proposed ───────────────────────────── │
1178
- │ Ship the rewrite by July 15. │
1179
- │ │
1180
- │ Reason: user mentioned slippage in chat │
1181
- │ │
1182
- │ [Accept] [Reject] │
1183
- └──────────────────────────────────────────┘
1184
- ```
1185
-
1186
- Renders via a TipTap decoration. Buttons hit `POST /api/agent/files/<path>` with `suggestion.accept` / `suggestion.reject`.
1187
-
1188
- ### 5.14 AI Panel — `src/components/ai-panel/ai-panel.tsx`
1189
-
1190
- Right-side slide-in drawer (similar layout to existing dialog/popover patterns).
1191
-
1192
- ```
1193
- ┌────────────────────────────────────┐
1194
- │ Agents ✕ │
1195
- ├────────────────────────────────────┤
1196
- │ Bridge endpoint │
1197
- │ http://localhost:3000 │
1198
- │ [copy curl] │
1199
- │ │
1200
- │ Token │
1201
- │ ••••••••cf12 [show] [copy] │
1202
- │ [regenerate] │
1203
- │ │
1204
- │ Active connections (last 5m) │
1205
- │ • ai:claude 12 ops / 3 msgs │
1206
- │ • ai:cursor (idle) │
1207
- │ │
1208
- │ Recent activity │
1209
- │ plan.md │
1210
- │ claude replaced b7f2c1 12s │
1211
- │ claude commented on b9c104 2m │
1212
- │ specs.md │
1213
- │ claude inserted section 8m │
1214
- │ │
1215
- │ Open AGENTS.md guide ↗ │
1216
- └────────────────────────────────────┘
1217
- ```
1218
-
1219
- Data:
1220
-
1221
- - Connections: derived from sidecar event timestamps in the last 5 minutes, grouped by `by`.
1222
- - Activity: paginated event tail across all sidecars (scan `.proof/`).
1223
- - Token: pulled from `/api/agent/settings`.
1224
-
1225
- This is read-only v1. No chat UI. Wire `useAIPanelStore` to actually drive panel open/close.
1226
-
1227
- ### 5.15 Stores — `src/stores/proof-store.ts`
1228
-
1229
- ```ts
1230
- interface ProofState {
1231
- byPath: Record<
1232
- string,
1233
- {
1234
- sidecar: Sidecar | null;
1235
- snapshotRevision: number;
1236
- lastEventId: number;
1237
- }
1238
- >;
1239
- loadSidecar(path: string): Promise<void>;
1240
- applyEvent(path: string, e: ProofEvent): void;
1241
- // For the editor, on focus / file open / SSE notification, refetch sidecar.
1242
- }
1243
- ```
1244
-
1245
- The chokidar SSE feed (existing) already broadcasts file changes. Subscribe in the editor: when our file changes, refetch sidecar via `GET /api/agent/sidecar/<path>`.
1246
-
1247
- ### 5.16 Settings UI
1248
-
1249
- Minimal addition to existing settings (if any) or a new modal:
1250
-
1251
- - Toggle: Allow remote agents (controls bind-to-host but most users will just `--host 0.0.0.0` from CLI)
1252
- - Token display + regenerate
1253
- - Rate limit (default 60 ops/min per file)
1254
- - List of recent agent identities seen (purely informational, derived from event log)
1255
-
1256
- ---
1257
-
1258
- ## 6. Edge cases and contract details
1259
-
1260
- ### 6.1 External edits (user edits in vim)
1261
-
1262
- Detected via fingerprint mismatch on next mutation OR via chokidar (already running). When detected:
1263
-
1264
- 1. Re-parse blocks
1265
- 2. Re-assign refs (preserving as many as possible via §5.4)
1266
- 3. Bump revision (counts as a system mutation)
1267
- 4. Emit `file.externallyEdited` event
1268
- 5. Persist sidecar
1269
- 6. For any block that was deleted by the external edit, related comments/suggestions stay with their now-orphan ref. They are still returned in snapshots with `"ref": "<orphan>"`. UI shows them in an "orphaned" panel.
1270
-
1271
- ### 6.2 Concurrent mutations
1272
-
1273
- The per-file mutex (§5.10) serializes mutations. The revision check (§4.5 `STALE_REVISION`) catches the case where two agents both held revision N and one beat the other.
1274
-
1275
- ### 6.3 Rate limiting
1276
-
1277
- In-memory token bucket per `by` value:
1278
-
1279
- - Bucket size 60, refill 1 op/sec
1280
- - Apply to mutation ops only (block._ + comment._ + suggestion.\*), not snapshot GETs / event polls
1281
- - Hit limit → 429 with `Retry-After: <seconds>` header
1282
-
1283
- ### 6.4 Sidecar size management
1284
-
1285
- Events grow unbounded. Trim policy:
1286
-
1287
- - Keep last 1000 events per sidecar
1288
- - When trimming, retain ALL events newer than the oldest `lastAck.*` cursor, even if it exceeds 1000
1289
- - Trim on every 100th mutation to amortize
1290
-
1291
- ### 6.5 Path traversal
1292
-
1293
- Use existing `safeRootPath` from `src/lib/root-dir.ts`. Reject any path resolving to a directory above root. Reject `.proof` itself as a content path (reserved).
1294
-
1295
- ### 6.6 Non-markdown files
1296
-
1297
- Reject mutations on non-`.md` files. Snapshot also rejects. Path must end in `.md` or `.markdown`.
1298
-
1299
- ### 6.7 Span IDs survive edits
1300
-
1301
- When `block.replace` is applied:
1302
-
1303
- - Existing `<proof-span>` elements inside the old block content that are NOT changed should retain their IDs
1304
- - New content gets a fresh span if the op's `by` is ai
1305
- - This is approximate. If the agent passes wholesale-replaced markdown, the old spans are gone. That's fine.
1306
-
1307
- ### 6.8 Suggestion conflicts
1308
-
1309
- If two pending suggestions target the same ref, both stay pending. Accepting one does NOT auto-reject the other — but accepting one bumps the revision and may invalidate the other's ref via aliasing. On accept of #1, #2 stays pending but its ref might be aliased. UI must handle: surface `suggestion.ref-stale` state.
1310
-
1311
- Keep it simple: when applying `suggestion.accept`, also iterate other pending suggestions with same ref and mark them as `status: rejected` with `resolvedBy: "system"` and event `suggestion.rejected` (reason: superseded). Document this in code comments.
1312
-
1313
- ### 6.9 Markdown roundtrip fidelity
1314
-
1315
- Wiki-viewer has a non-trivial roundtrip (turndown + custom rules for wiki-links, embeds, lucide icons, etc.). The agent protocol bypasses that: it operates directly on the raw markdown file, not on the TipTap HTML. The block parser uses remark only. **This means: the agent never sees the editor's HTML.** It sees raw markdown with raw `<proof-span>` tags.
1316
-
1317
- When the editor saves a file (via existing PUT `/api/wiki/content`), the turndown rule (§5.12) ensures `<proof-span>` is preserved. Verify with an end-to-end test.
1318
-
1319
- ### 6.10 Wiki-links and proof-spans
1320
-
1321
- `[[slug]]` syntax inside a proof-span is fine. Remark parses link before HTML wrapping; turndown emits wiki-link rule before proof-span rule (because turndown processes children first). Verify.
1322
-
1323
- ### 6.11 Empty file / new file
1324
-
1325
- `GET` on missing file → 404. Agent cannot create files through this protocol (that's a separate concern). If you want create-via-agent: extend later. Not in scope.
1326
-
1327
- ### 6.12 Hot reload during dev
1328
-
1329
- When source changes trigger Next reload, in-memory idempotency cache and mutex map are wiped. Acceptable. Disk state (sidecars) survives. Tests should not depend on cache.
1330
-
1331
- ---
1332
-
1333
- ## 7. Tests
1334
-
1335
- Minimum suite (place in `src/tests/proof/`). No test framework dependency required if `package.json` doesn't already have one — use `node --test` (built-in since Node 18) and `node:assert`.
1336
-
1337
- ### 7.1 Unit: blocks.test.ts
1338
-
1339
- - parse → stringify roundtrip preserves `<proof-span>` exactly
1340
- - list with checkboxes detected as `taskList`
1341
- - code block with lang preserved
1342
- - table preserved
1343
-
1344
- ### 7.2 Unit: block-refs.test.ts
1345
-
1346
- - First parse assigns refs deterministically
1347
- - Re-parse with same content reuses refs
1348
- - Edit one block, others keep refs
1349
- - Insert a block in middle, others keep refs
1350
- - Aliases populated after replace, cleared after next mutation
1351
-
1352
- ### 7.3 Unit: proof-span.test.ts
1353
-
1354
- - wrapAsProofSpan on paragraph wraps text content
1355
- - wrapAsProofSpan on heading wraps text after `#`s
1356
- - wrapAsProofSpan on code block returns content unchanged + records in sidecar
1357
- - Special chars in basisDetail escaped properly
1358
-
1359
- ### 7.4 Integration: ops-applier.test.ts
1360
-
1361
- For each op type:
1362
-
1363
- - Happy path: apply, verify resulting markdown + sidecar
1364
- - STALE_REVISION returned when baseRevision wrong
1365
- - BLOCK_NOT_FOUND returned for stale ref
1366
- - Idempotency: same key returns cached response
1367
-
1368
- ### 7.5 Integration: routes.test.ts
1369
-
1370
- Use `next` dev server in a temp ROOT_DIR. Hit routes with fetch.
1371
-
1372
- - GET snapshot of a real .md file
1373
- - POST insertAfter → verify file content changed
1374
- - Poll events → see new event
1375
- - Ack events → cursor advances
1376
-
1377
- ### 7.6 Roundtrip: editor-save.test.ts
1378
-
1379
- - Create .md with `<proof-span>` inline
1380
- - Load through `markdownToHtml` → TipTap state
1381
- - Serialize back through `htmlToMarkdown`
1382
- - Verify the `<proof-span>` is byte-identical
1383
-
1384
- Critical: this test catches the silent failure where Tiptap mangles the mark on save.
1385
-
1386
- ---
1387
-
1388
- ## 8. Implementation order
1389
-
1390
- Sub-agent that picks this up: execute phases in order. Do not skip ahead. Each phase ends with a working green test set.
1391
-
1392
- ### Phase A — Foundation (no UI yet)
1393
-
1394
- 1. Create `src/lib/proof/types.ts`
1395
- 2. Create `src/lib/proof/blocks.ts`, get tests green
1396
- 3. Create `src/lib/proof/block-refs.ts`, get tests green
1397
- 4. Create `src/lib/proof/proof-span.ts`, get tests green
1398
- 5. Create `src/lib/proof/sidecar.ts`
1399
- 6. Create `src/lib/proof/mutex.ts`, `idempotency.ts`, `auth.ts`
1400
- 7. Create `src/lib/proof/ops-applier.ts`, get tests green
1401
- 8. Add `<proof-span>` turndown rule in `src/lib/markdown/to-markdown.ts`
1402
-
1403
- **Acceptance:** All Phase A tests pass. No HTTP yet. No UI yet.
1404
-
1405
- ### Phase B — HTTP surface
1406
-
1407
- 1. Create `src/app/api/agent/files/[...path]/route.ts` (GET + POST)
1408
- 2. Create `src/app/api/agent/events/[...path]/route.ts` (GET=poll, POST=ack)
1409
- 3. Create `src/app/api/agent/sidecar/[...path]/route.ts`
1410
- 4. Create `src/app/api/agent/settings/route.ts` + `settings/token/regenerate/route.ts`
1411
- 5. Create `src/app/api/agent/internal/span/route.ts` (accept/revert)
1412
-
1413
- **Acceptance:** Integration tests pass. `curl` workflow from §10 works.
1414
-
1415
- ### Phase C — Editor wiring
1416
-
1417
- 1. Create `src/components/editor/extensions/proof-span.ts` TipTap mark
1418
- 2. Register in `extensions.ts`
1419
- 3. Add CSS in `globals.css`
1420
- 4. Create `src/stores/proof-store.ts`
1421
- 5. Update editor to load sidecar on file open, subscribe to chokidar SSE for sidecar refresh
1422
- 6. Create `proof-span-popover.tsx` (Accept / Revert / Comment)
1423
-
1424
- **Acceptance:** Load a .md with `<proof-span>` inline → see decoration. Hover → popover works. Accept removes mark.
1425
-
1426
- ### Phase D — Comments
1427
-
1428
- 1. Create comment pip decoration plugin
1429
- 2. Create `comment-thread.tsx`
1430
- 3. Create comment composer (for human-initiated comments)
1431
- 4. Wire to `POST /api/agent/files/<path>` with `comment.add` / `comment.reply` / `comment.resolve`
1432
-
1433
- **Acceptance:** Human can create a comment on a block. Agent reply (simulated via curl) appears. Resolve works.
1434
-
1435
- ### Phase E — Suggestions
1436
-
1437
- 1. Create `suggestion-card.tsx` decoration
1438
- 2. Wire Accept / Reject buttons
1439
-
1440
- **Acceptance:** Agent POSTs `suggestion.add`. Card appears inline. Accept → block content changes, card disappears, event emitted.
1441
-
1442
- ### Phase F — AI Panel
1443
-
1444
- 1. Create `ai-panel.tsx` + sub-components
1445
- 2. Update `ai-panel-store.ts` (remove stub, wire to data)
1446
- 3. Add panel trigger button to layout header
1447
- 4. Build activity aggregator (scans `.proof/` for recent events)
1448
-
1449
- **Acceptance:** Panel opens, shows token, shows connections, shows recent activity. Copy-curl button works.
1450
-
1451
- ### Phase G — Polish
1452
-
1453
- 1. Rate limiting in middleware
1454
- 2. Sidecar size trimming
1455
- 3. Settings UI
1456
- 4. README updates: agent protocol section
1457
- 5. Manual smoke test of full flow (see §10)
1458
-
1459
- **Acceptance:** §10 smoke test passes end-to-end.
1460
-
1461
- ---
1462
-
1463
- ## 9. Code style guardrails
1464
-
1465
- - TypeScript strict mode. No `any` except at boundaries with non-typed deps.
1466
- - Tabs for indentation (project convention — see existing files).
1467
- - Functions over classes. No inheritance hierarchies.
1468
- - Async/await, never raw `.then`.
1469
- - Error responses always `{ error: "CODE", message?: string }`. Never bare strings.
1470
- - All routes mark `export const runtime = "nodejs"` (filesystem access).
1471
- - No console.log in committed code. Use a logger or remove.
1472
- - No `// TODO` / `// FIXME` in committed code. File issues instead.
1473
- - No em-dashes in any user-facing copy or docs. Use commas / sentence restructuring (per user convention).
1474
- - No AI / agent attribution in commit messages or PR descriptions (per user convention).
1475
- - Test files mirror source layout under `src/tests/proof/`.
1476
-
1477
- ---
1478
-
1479
- ## 10. Smoke test (manual)
1480
-
1481
- Run after Phase G. This is the acceptance ritual.
1482
-
1483
- ```bash
1484
- # 0. setup
1485
- export ROOT_DIR=/tmp/wiki-smoke
1486
- mkdir -p $ROOT_DIR
1487
- echo "# Plan\n\nShip the rewrite by June." > $ROOT_DIR/plan.md
1488
- pnpm dev
1489
-
1490
- # 1. snapshot (no token, localhost)
1491
- curl -s http://localhost:3000/api/agent/files/plan.md | jq
1492
-
1493
- # Expect: blocks[] with two refs, revision: 0, comments: [], suggestions: []
1494
-
1495
- # 2. agent inserts a paragraph
1496
- REF=$(curl -s http://localhost:3000/api/agent/files/plan.md | jq -r '.blocks[1].ref')
1497
-
1498
- curl -s -X POST http://localhost:3000/api/agent/files/plan.md \
1499
- -H "Content-Type: application/json" \
1500
- -H "Idempotency-Key: $(uuidgen)" \
1501
- -d "{
1502
- \"baseRevision\": 0,
1503
- \"by\": \"ai:claude\",
1504
- \"ops\": [
1505
- { \"type\": \"block.insertAfter\", \"ref\": \"$REF\",
1506
- \"markdown\": \"The team will focus on three pillars: infra, tooling, launch.\",
1507
- \"basis\": \"described\",
1508
- \"basisDetail\": \"user asked for opening\" }
1509
- ]
1510
- }" | jq
1511
-
1512
- # Expect: revision: 1, new block in snapshot, file on disk has <proof-span> around inserted text
1513
-
1514
- cat $ROOT_DIR/plan.md
1515
- # Expect:
1516
- # # Plan
1517
- #
1518
- # Ship the rewrite by June.
1519
- #
1520
- # <proof-span id="p..." origin="ai" basis="described" by="ai:claude" at="..." basis-detail="user asked for opening">The team will focus on three pillars: infra, tooling, launch.</proof-span>
1521
-
1522
- # 3. browser: open http://localhost:3000, navigate to plan.md
1523
- # expect: lavender left-border decoration on the inserted paragraph
1524
- # hover → popover shows "ai:claude · described · just now" + Accept/Revert/Comment
1525
-
1526
- # 4. human comment via UI (right-click block → Comment)
1527
- # type: "Are launch and tooling overlapping?"
1528
-
1529
- # 5. agent polls events
1530
- curl -s http://localhost:3000/api/agent/events/plan.md?after=0 | jq
1531
- # Expect: events array with the comment.added event
1532
-
1533
- # 6. agent replies
1534
- COMMENT_ID=<from event>
1535
- curl -s -X POST http://localhost:3000/api/agent/files/plan.md \
1536
- -H "Content-Type: application/json" \
1537
- -H "Idempotency-Key: $(uuidgen)" \
1538
- -d "{ \"baseRevision\": 1, \"by\": \"ai:claude\",
1539
- \"ops\": [{ \"type\": \"comment.reply\", \"commentId\": \"$COMMENT_ID\", \"text\": \"No, tooling is internal-only.\" }] }"
1540
-
1541
- # 7. browser: open thread on the block, see agent's reply
1542
-
1543
- # 8. agent proposes a suggestion
1544
- curl -s -X POST http://localhost:3000/api/agent/files/plan.md \
1545
- -H "Content-Type: application/json" \
1546
- -H "Idempotency-Key: $(uuidgen)" \
1547
- -d "{ \"baseRevision\": 2, \"by\": \"ai:claude\",
1548
- \"ops\": [{ \"type\": \"suggestion.add\", \"ref\": \"$REF\",
1549
- \"kind\": \"replace\",
1550
- \"markdown\": \"Ship the rewrite by July 15.\",
1551
- \"basis\": \"described\",
1552
- \"basisDetail\": \"deadline slipped\" }] }"
1553
-
1554
- # 9. browser: see inline suggestion card. Click Accept.
1555
- # expect: block content changes, card disappears, file on disk updated
1556
-
1557
- # 10. token flow
1558
- # stop server, set AGENT_BEARER_TOKEN=test123, restart
1559
- # curl without token → 401
1560
- # curl with -H "Authorization: Bearer test123" → 200
1561
-
1562
- # 11. external edit
1563
- # while server running:
1564
- # echo "appended line" >> $ROOT_DIR/plan.md
1565
- # immediately:
1566
- # curl /api/agent/events/plan.md?after=<last>
1567
- # expect: file.externallyEdited event
1568
- ```
1569
-
1570
- If any step fails, fix before declaring done.
1571
-
1572
- ---
1573
-
1574
- ## 11. What to ask the user before starting
1575
-
1576
- Before implementing, agent should re-confirm only these specifics:
1577
-
1578
- 1. The bearer-token storage path `~/.wiki-viewer/agent-token` is fine, OR prefer env-only?
1579
- 2. The localhost-without-token default OK, OR token always required?
1580
- 3. Rate limit default of 60 ops/min/agent OK, OR different?
1581
-
1582
- Everything else: follow this plan as-written. Do not deviate from the API shape, op vocabulary, error codes, or sidecar schema. If a real blocker emerges during implementation, surface it as a question with concrete options rather than improvising.
1583
-
1584
- ---
1585
-
1586
- ## 12. Out of scope (do not build)
1587
-
1588
- To prevent scope creep, the following are **explicitly out** even if they seem like natural additions:
1589
-
1590
- - File create / rename / delete via agent protocol (use the existing `/api/wiki/*` for that)
1591
- - Multi-file ops (one file per request, period)
1592
- - Diff visualizer beyond the suggestion card's two-pane view
1593
- - @-mentions, agent-to-agent messaging
1594
- - Webhooks pushing to agent URLs (agents poll)
1595
- - Realtime cursors / presence indicators per agent in the editor body
1596
- - Persisting idempotency across server restarts
1597
- - Auth scopes beyond all-or-nothing
1598
- - Per-file ACLs
1599
- - Export / import of sidecars
1600
- - Migrations from / to Proof's hosted format
1601
-
1602
- All of these can be added later as separate features without conflicting with this design.
1603
-
1604
- ---
1605
-
1606
- ## 13. Definition of done
1607
-
1608
- - All phases A–G complete
1609
- - All tests in §7 green
1610
- - §10 smoke test passes end-to-end
1611
- - README updated with "Working with agents" section pointing at the agent protocol
1612
- - No console.logs, no TODOs, no commented-out code shipped
1613
- - Manual review by a `reviewer` agent run via `/feature` chain
1614
-
1615
- When done: notify the user, paste the final smoke test output as evidence.