wiki-viewer 1.4.3 → 1.5.1

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 (218) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +5 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/routes-manifest.json +34 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
  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 +8 -7
  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/app-paths-manifest.json +3 -0
  26. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route/build-manifest.json +9 -0
  27. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route/server-reference-manifest.json +4 -0
  28. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +17 -0
  29. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.map +5 -0
  30. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -0
  31. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route_client-reference-manifest.js +3 -0
  32. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route/app-paths-manifest.json +3 -0
  33. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route/build-manifest.json +9 -0
  34. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route/server-reference-manifest.json +4 -0
  35. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js +15 -0
  36. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.map +5 -0
  37. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -0
  38. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route_client-reference-manifest.js +3 -0
  39. package/.next/standalone/.next/server/app/api/agent/fs/move/route/app-paths-manifest.json +3 -0
  40. package/.next/standalone/.next/server/app/api/agent/fs/move/route/build-manifest.json +9 -0
  41. package/.next/standalone/.next/server/app/api/agent/fs/move/route/server-reference-manifest.json +4 -0
  42. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +15 -0
  43. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.map +5 -0
  44. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -0
  45. package/.next/standalone/.next/server/app/api/agent/fs/move/route_client-reference-manifest.js +3 -0
  46. package/.next/standalone/.next/server/app/api/agent/fs/search/route/app-paths-manifest.json +3 -0
  47. package/.next/standalone/.next/server/app/api/agent/fs/search/route/build-manifest.json +9 -0
  48. package/.next/standalone/.next/server/app/api/agent/fs/search/route/server-reference-manifest.json +4 -0
  49. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +15 -0
  50. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.map +5 -0
  51. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -0
  52. package/.next/standalone/.next/server/app/api/agent/fs/search/route_client-reference-manifest.js +3 -0
  53. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/api/agent/settings/route.js +1 -1
  55. package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js +1 -1
  58. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/api/wiki/content/route.js +2 -2
  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/file/route.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/api/wiki/move/route.js +3 -3
  72. package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/wiki/presence/route/app-paths-manifest.json +3 -0
  76. package/.next/standalone/.next/server/app/api/wiki/presence/route/build-manifest.json +9 -0
  77. package/.next/standalone/.next/server/app/api/wiki/presence/route/server-reference-manifest.json +4 -0
  78. package/.next/standalone/.next/server/app/api/wiki/presence/route.js +14 -0
  79. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.map +5 -0
  80. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -0
  81. package/.next/standalone/.next/server/app/api/wiki/presence/route_client-reference-manifest.js +3 -0
  82. package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/index.html +1 -1
  87. package/.next/standalone/.next/server/app/index.rsc +2 -2
  88. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  89. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  90. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/app-paths-manifest.json +5 -0
  96. package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0d2jbo5.js +3 -0
  97. package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0ytral2.js +3 -0
  98. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-ao6a_._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0269d2e._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05f6x0i._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06-dtd0._.js +3 -0
  102. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06sek6_._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/[root-of-the-server]__09s9qkd._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d75a5q._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_da6q._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e7si9p._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0hhhvm7._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kk26wc._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kur4ro._.js +15 -0
  110. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0p12-6~._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qc2~6l._.js +15 -0
  112. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s~i040._.js +3 -0
  113. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w47wil._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xq124z._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l..bxz._.js → [root-of-the-server]__0xz-_0v._.js} +2 -2
  116. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10w2q~o._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/_079xuln._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_file_[___path]_route_actions_0gb11t~.js +3 -0
  119. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_ls_[[___path]]_route_actions_0wfg4nl.js +3 -0
  120. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_move_route_actions_0z34mqq.js +3 -0
  121. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_search_route_actions_0ut_ug..js +3 -0
  122. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wiki_presence_route_actions_0x9d7f4.js +3 -0
  123. package/.next/standalone/.next/server/chunks/ssr/{0~w0_katex_dist_katex_mjs_055boxx._.js → 0~w0_katex_dist_katex_mjs_00650~e._.js} +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_diagram-OG6HWLK6_mjs_0t5sus6._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_sankeyDiagram-5OEKKPKP_mjs_11ifrnu._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__134_q7l._.js → [root-of-the-server]__12ahxbl._.js} +2 -2
  127. package/.next/standalone/.next/server/chunks/ssr/_0pqaawz._.js +3 -3
  128. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0rc1vh5._.js → node_modules__pnpm_02gx8km._.js} +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0t4unpg._.js → node_modules__pnpm_02mew~s._.js} +2 -2
  130. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_12-ni1n._.js → node_modules__pnpm_03ij5.g._.js} +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_12~a4c_._.js → node_modules__pnpm_03v21p-._.js} +2 -2
  132. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0f~_mm3._.js → node_modules__pnpm_05qsply._.js} +2 -2
  133. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0alwr5e._.js → node_modules__pnpm_06pgebo._.js} +2 -2
  134. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_02zxe7o._.js → node_modules__pnpm_08ibh0w._.js} +2 -2
  135. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hr027i._.js → node_modules__pnpm_099cv.b._.js} +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0znncy9._.js → node_modules__pnpm_0cwpt9p._.js} +2 -2
  137. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-pisfi._.js → node_modules__pnpm_0f9fpy5._.js} +1 -1
  138. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0zhbnni._.js → node_modules__pnpm_0f_uzgy._.js} +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-w-d-.._.js → node_modules__pnpm_0fixtms._.js} +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_03z0vkh._.js → node_modules__pnpm_0gofu~q._.js} +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_10i6lh6._.js → node_modules__pnpm_0iojvye._.js} +2 -2
  142. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-hfev1._.js → node_modules__pnpm_0li2vzs._.js} +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_13kfy_z._.js → node_modules__pnpm_0m5-azh._.js} +1 -1
  144. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_071f6vl._.js → node_modules__pnpm_0n1kfcp._.js} +1 -1
  145. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_07uh2kc._.js → node_modules__pnpm_0rebktf._.js} +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0okw1r.._.js → node_modules__pnpm_0tkiej3._.js} +2 -2
  147. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xuz.8f._.js → node_modules__pnpm_0v_nwsz._.js} +1 -1
  148. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0zb6573._.js → node_modules__pnpm_0wffzo0._.js} +1 -1
  149. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0d7jxsf._.js → node_modules__pnpm_0wouz19._.js} +1 -1
  150. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0egenev._.js → node_modules__pnpm_0xxwmv2._.js} +1 -1
  151. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0j9bq9s._.js → node_modules__pnpm_0zzzsrj._.js} +1 -1
  152. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0irsd.b._.js → node_modules__pnpm_0~.6dgb._.js} +1 -1
  153. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0erwv_m._.js → node_modules__pnpm_0~2~c30._.js} +1 -1
  154. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0y9k6h~._.js → node_modules__pnpm_10413dq._.js} +2 -2
  155. package/.next/standalone/.next/server/functions-config-manifest.json +5 -0
  156. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  157. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  158. package/.next/standalone/.next/server/pages/404.html +1 -1
  159. package/.next/standalone/.next/server/pages/500.html +1 -1
  160. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  162. package/.next/standalone/.next/static/chunks/{11s9zwr7edrs7.js → 17~r3.8awh3qg.js} +12 -12
  163. package/.next/standalone/README.md +79 -16
  164. package/.next/standalone/agents/bootstrap-prompt.md +12 -1
  165. package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +199 -6
  166. package/.next/standalone/docs/file-vs-collab-authority.md +124 -0
  167. package/.next/standalone/package.json +1 -1
  168. package/.next/standalone/packages/wiki-viewer-mcp/README.md +194 -0
  169. package/.next/standalone/packages/wiki-viewer-mcp/dist/http-client.js +220 -0
  170. package/.next/standalone/packages/wiki-viewer-mcp/dist/index.js +472 -0
  171. package/.next/standalone/packages/wiki-viewer-mcp/dist/register.js +94 -0
  172. package/.next/standalone/packages/wiki-viewer-mcp/dist/state-cache.js +26 -0
  173. package/.next/standalone/packages/wiki-viewer-mcp/package-lock.json +1716 -0
  174. package/.next/standalone/packages/wiki-viewer-mcp/package.json +41 -0
  175. package/.next/standalone/packages/wiki-viewer-mcp/src/http-client.ts +344 -0
  176. package/.next/standalone/packages/wiki-viewer-mcp/src/index.ts +575 -0
  177. package/.next/standalone/packages/wiki-viewer-mcp/src/register.ts +144 -0
  178. package/.next/standalone/packages/wiki-viewer-mcp/src/state-cache.ts +45 -0
  179. package/.next/standalone/packages/wiki-viewer-mcp/tsconfig.json +19 -0
  180. package/.next/standalone/pnpm-lock.yaml +9 -9
  181. package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +11 -1
  182. package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +344 -0
  183. package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +147 -0
  184. package/.next/standalone/src/app/api/agent/fs/move/route.ts +125 -0
  185. package/.next/standalone/src/app/api/agent/fs/search/route.ts +236 -0
  186. package/.next/standalone/src/app/api/agent/register/route.ts +3 -3
  187. package/.next/standalone/src/app/api/agents/install/route.ts +44 -2
  188. package/.next/standalone/src/app/api/system/auth-settings/route.ts +1 -0
  189. package/.next/standalone/src/app/api/wiki/move/route.ts +10 -1
  190. package/.next/standalone/src/app/api/wiki/presence/route.ts +55 -0
  191. package/.next/standalone/src/components/ai-panel/ai-panel.tsx +98 -103
  192. package/.next/standalone/src/components/ai-panel/token-section.tsx +8 -7
  193. package/.next/standalone/src/components/auth-settings-sheet.tsx +81 -22
  194. package/.next/standalone/src/components/editor/editor.tsx +38 -0
  195. package/.next/standalone/src/lib/proof/audit.ts +79 -0
  196. package/.next/standalone/src/lib/proof/auth.ts +2 -2
  197. package/.next/standalone/src/lib/proof/collab-state.ts +79 -0
  198. package/.next/standalone/src/lib/proof/lease.ts +104 -0
  199. package/.next/standalone/src/lib/proof/ops-applier.ts +74 -25
  200. package/.next/standalone/src/lib/proof/raw-fs.ts +198 -0
  201. package/.next/standalone/src/lib/proof/registry.ts +1 -1
  202. package/.next/standalone/src/lib/proof/sidecar.ts +38 -1
  203. package/.next/standalone/src/lib/proof/types.ts +13 -1
  204. package/.next/standalone/src/tests/proof/agent-fs.test.ts +824 -0
  205. package/.next/standalone/src/tests/proof/agents-install.test.ts +53 -0
  206. package/.next/standalone/src/tests/proof/collab-state.test.ts +499 -0
  207. package/.next/standalone/src/tests/proof/reconcile-sidecar.test.ts +195 -0
  208. package/.next/standalone/src/tests/proof/sidecar-lifecycle.test.ts +177 -0
  209. package/.next/standalone/tsconfig.tsbuildinfo +1 -1
  210. package/README.md +79 -16
  211. package/package.json +1 -1
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__09~~-nd._.js +0 -3
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w87kiz._.js +0 -3
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12ssbhv._.js +0 -3
  215. package/.next/standalone/.next/server/chunks/node_modules__pnpm_0g0fhfk._.js +0 -3
  216. /package/.next/standalone/.next/static/{kzOuSAQArtvD7uiAJ6ZLh → 3G_3qm2a9nOX7oi5k0mPV}/_buildManifest.js +0 -0
  217. /package/.next/standalone/.next/static/{kzOuSAQArtvD7uiAJ6ZLh → 3G_3qm2a9nOX7oi5k0mPV}/_clientMiddlewareManifest.js +0 -0
  218. /package/.next/standalone/.next/static/{kzOuSAQArtvD7uiAJ6ZLh → 3G_3qm2a9nOX7oi5k0mPV}/_ssgManifest.js +0 -0
@@ -22,8 +22,10 @@
22
22
  Originally a zero-config single-user tool, it now supports:
23
23
 
24
24
  - Multi-user sign-in (Google OAuth or email + password) for teams of 3 to 10 sharing a VPS.
25
- - An HTTP collaboration API that lets AI agents read and edit Markdown files alongside you, with provenance marks, comments, suggestions, and revision-checked mutations.
25
+ - An HTTP API for AI agents with **two tiers**: a raw filesystem (read/write/edit/list/search/move/delete for **all file types**) for fast filework, and a Markdown collaboration layer with provenance marks, comments, suggestions, and revision-checked mutations.
26
+ - A **working-vs-collaborating** safety model: when you have a Markdown doc open, agents automatically defer to the reviewable collab path instead of overwriting it.
26
27
  - Per-agent registration and scoped tokens. No shared bearer secret.
28
+ - An optional `npx wiki-viewer-mcp` adapter so MCP-capable agents (Claude Code, Cursor, Codex) get native file tools against a remote instance.
27
29
 
28
30
  Single-user, no-auth mode still works. Auth turns on automatically once anyone signs up.
29
31
 
@@ -279,7 +281,23 @@ The Markdown editor now sends a `baseRevision` with every save. If another user
279
281
 
280
282
  ## Working with AI agents
281
283
 
282
- wiki-viewer exposes an HTTP collaboration protocol so agents (Claude, Cursor, ChatGPT desktop, custom scripts) can read and edit Markdown files. Every AI-authored insert is wrapped in an inline `<proof-span>` mark so the human reviewer can see, accept, or revert each change.
284
+ wiki-viewer exposes an HTTP API so agents (Claude, Cursor, ChatGPT desktop, custom scripts) can work with files in a running instance locally or remotely almost as if they were on their own filesystem. There are **two tiers**, sharing one auth/scope/lock spine:
285
+
286
+ - **Tier 1 — Raw filesystem** (`/api/agent/fs/*`): `read`, `write`, `edit`, `list`, `search`, `move`, `delete` for **every file type** (code, configs, PDFs, notebooks, Markdown — anything). Fast, boring, byte-accurate. Mutations are audited.
287
+ - **Tier 2 — Markdown collaboration** (`/api/agent/files/*`): structured block-ops where every AI-authored insert is wrapped in an inline `<proof-span>` mark so the human reviewer can see, accept, or revert each change, plus comments and suggestions. Tier-2 is API-compatible in spirit with [Proof SDK](https://github.com/EveryInc/proof-sdk).
288
+
289
+ ### Which tier? Working mode vs collaborating mode
290
+
291
+ Before editing a Markdown file, an agent reads it and checks the **`X-Collab-State`** response header:
292
+
293
+ | `X-Collab-State` | Meaning | Agent uses |
294
+ | ---------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------- |
295
+ | `active` | A human has the doc open, or it has pending suggestions/comments | **Tier-2 block-ops** (reviewable). A raw write is rejected `409 COLLAB_ACTIVE`. |
296
+ | `tracked` | Has a collab sidecar, nobody editing now | Prefer Tier-2 for prose; raw OK for mechanical edits |
297
+ | `untracked` | Plain Markdown, never collaborated on | Either tier |
298
+ | `not-markdown` | Any non-`.md` file | **Tier-1 raw only** |
299
+
300
+ This is enforced, not advisory: the collab state is re-checked atomically inside the write lock, so an agent can never silently clobber a doc you just opened. The browser editor sends a presence heartbeat to drive the `active` state. See [`docs/file-vs-collab-authority.md`](docs/file-vs-collab-authority.md) for the full authority model.
283
301
 
284
302
  The protocol is intentionally API-compatible in spirit with [Proof SDK](https://github.com/EveryInc/proof-sdk).
285
303
 
@@ -306,7 +324,42 @@ npx skills add anh-chu/wiki-viewer/agents/wiki-viewer-skill
306
324
 
307
325
  For any chat agent, paste the bootstrap prompt from `<your-server>/agents/install` (also visible in the AI Panel). The agent fetches `/api/agents/install` and learns the full op vocabulary at runtime.
308
326
 
309
- ### Op vocabulary
327
+ ### Tier 1 — raw filesystem
328
+
329
+ Work with any file type. All routes take `Authorization: Bearer <token>` + `X-Agent-Id`, are scope-checked, and reject path traversal, symlink escapes, and anything under `.proof/`.
330
+
331
+ ```bash
332
+ # Read (bytes; ETag is the sha256, supports Range, returns X-Collab-State)
333
+ curl -sD- -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
334
+ https://wiki.team.com/api/agent/fs/file/src/util.ts
335
+
336
+ # Write/overwrite (atomic). If-Match: <sha256> is required to overwrite;
337
+ # omit it to create. ?mkdirs=true creates parent dirs. ?force=true overrides (audited).
338
+ curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
339
+ -H "If-Match: <sha256-from-read>" --data-binary @util.ts \
340
+ https://wiki.team.com/api/agent/fs/file/src/util.ts
341
+
342
+ # List a directory (scope-filtered)
343
+ curl -s -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
344
+ "https://wiki.team.com/api/agent/fs/ls/src?recursive=true&limit=500"
345
+
346
+ # Search (server-side; kills round-trips)
347
+ curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
348
+ -H "Content-Type: application/json" \
349
+ -d '{"kind":"grep","query":"TODO","glob":"**/*.ts"}' \
350
+ https://wiki.team.com/api/agent/fs/search
351
+
352
+ # Move and delete (delete needs the `delete` scope + If-Match)
353
+ curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
354
+ -H "Content-Type: application/json" -d '{"from":"a.md","to":"b.md"}' \
355
+ https://wiki.team.com/api/agent/fs/move
356
+ curl -s -X DELETE -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
357
+ -H "If-Match: <sha256>" https://wiki.team.com/api/agent/fs/file/old.md
358
+ ```
359
+
360
+ Mutating a Markdown file via raw write emits a `file.rawWritten` event and re-binds the collab sidecar; if the doc is `active` it is rejected `409 COLLAB_ACTIVE` (use Tier-2 instead). Use the **`npx wiki-viewer-mcp`** adapter to get all of this as standard MCP file tools — it handles `If-Match`, mode-awareness, and edit-via-read-then-write for you.
361
+
362
+ ### Tier 2 — op vocabulary (Markdown)
310
363
 
311
364
  Block-level edits (revision-checked, idempotent):
312
365
 
@@ -356,15 +409,23 @@ Owner-only (session cookie):
356
409
 
357
410
  Agent routes (bearer + `X-Agent-Id`, scope-checked):
358
411
 
359
- | Method | Path | Required scope |
360
- | ------ | ---------------------------------------- | --------------------- |
361
- | `GET` | `/api/agent/files/<path.md>` | `read` + path match |
362
- | `POST` | `/api/agent/files/<path.md>` | `mutate` + path match |
363
- | `GET` | `/api/agent/events/<path.md>?after=<id>` | `read` + path match |
364
- | `POST` | `/api/agent/events/<path.md>` | `read` + path match |
365
- | `GET` | `/api/agent/sidecar/<path.md>` | `read` + path match |
366
- | `GET` | `/api/agent/settings` | `read` |
367
- | `GET` | `/api/agent/activity` | `read` |
412
+ | Method | Path | Required scope | Tier |
413
+ | -------- | ---------------------------------------- | --------------------- | ---- |
414
+ | `GET` | `/api/agent/fs/file/<path>` | `read` + path match | 1 |
415
+ | `PUT` | `/api/agent/fs/file/<path>` | `mutate` + path match | 1 |
416
+ | `DELETE` | `/api/agent/fs/file/<path>` | `delete` + path match | 1 |
417
+ | `GET` | `/api/agent/fs/ls/<path>` | `read` + path match | 1 |
418
+ | `POST` | `/api/agent/fs/move` | `mutate` (src+dest) | 1 |
419
+ | `POST` | `/api/agent/fs/search` | `read` (per match) | 1 |
420
+ | `GET` | `/api/agent/files/<path.md>` | `read` + path match | 2 |
421
+ | `POST` | `/api/agent/files/<path.md>` | `mutate` + path match | 2 |
422
+ | `GET` | `/api/agent/events/<path.md>?after=<id>` | `read` + path match | 2 |
423
+ | `POST` | `/api/agent/events/<path.md>` | `read` + path match | 2 |
424
+ | `GET` | `/api/agent/sidecar/<path.md>` | `read` + path match | 2 |
425
+ | `GET` | `/api/agent/settings` | `read` | — |
426
+ | `GET` | `/api/agent/activity` | `read` | — |
427
+
428
+ Scopes: `paths` is a glob list (directories work natively, e.g. `notes/**`); `ops` is any of `read`, `mutate` (create/overwrite/move), `delete` (remove). Grant `["read","mutate"]` for edit-but-never-delete.
368
429
 
369
430
  ### Full curl trace
370
431
 
@@ -373,7 +434,7 @@ Agent routes (bearer + `X-Agent-Id`, scope-checked):
373
434
  curl -s -X POST https://wiki.team.com/api/agent/register \
374
435
  -H "Content-Type: application/json" \
375
436
  -d '{"id":"ai:claude","displayName":"Claude",
376
- "scope":{"paths":["**/*"],"ops":["read","mutate"]}}'
437
+ "scope":{"paths":["**/*"],"ops":["read","mutate","delete"]}}'
377
438
  # -> { "registrationId":"reg_abc","pollUrl":"/api/agent/register/reg_abc","status":"pending" }
378
439
 
379
440
  # 2. Owner approves in the AI Panel.
@@ -406,9 +467,11 @@ Response codes to handle:
406
467
 
407
468
  - `401 UNAUTHORIZED` — bad token or `X-Agent-Id`.
408
469
  - `403 FORBIDDEN` — out of scope or `by` mismatches identity.
409
- - `409 STALE_REVISION` — refetch and retry. Response includes a fresh snapshot.
410
- - `409 BLOCK_NOT_FOUND` — the ref no longer exists.
411
- - `409 IDEMPOTENCY_KEY_REUSED` — same key, different body.
470
+ - `409 STALE_REVISION` — refetch and retry. Response includes a fresh snapshot. (Tier 2)
471
+ - `409 BLOCK_NOT_FOUND` — the ref no longer exists. (Tier 2)
472
+ - `409 IDEMPOTENCY_KEY_REUSED` — same key, different body. (Tier 2)
473
+ - `409 COLLAB_ACTIVE` — raw write to a Markdown doc a human is editing; use Tier-2 block-ops (response includes the snapshot URL) or `?force=true`. (Tier 1)
474
+ - `412 PRECONDITION_FAILED` — `If-Match` sha mismatch (file changed); re-read and retry. (Tier 1)
412
475
  - `429 RATE_LIMITED` — honor `Retry-After`. Default 60 ops/minute per agent.
413
476
 
414
477
  ### Agent rate limit override
@@ -1 +1,12 @@
1
- You are connecting to a running wiki-viewer instance at $WIKI_URL (e.g. `http://localhost:3000`). Fetch `$WIKI_URL/api/agents/install`and follow the instructions there to register, wait for human approval, then read and edit markdown files via its HTTP API. Set`basis`and`basisDetail`on every content op so the human can see where your changes came from. Prefer`suggestion.add` over direct block ops unless told otherwise.
1
+ You are connecting to a running wiki-viewer instance at $WIKI_URL (e.g. `http://localhost:3000`). Fetch `$WIKI_URL/api/agents/install` and follow the instructions there to register, wait for human approval, then work with files via its HTTP API.
2
+
3
+ **Two tiers — pick by `X-Collab-State`:**
4
+
5
+ - **Tier 1 Raw FS** (`/api/agent/fs/*`) — all file types, fast read/write/ls/search. Use for code, binaries, and markdown that isn't being actively co-edited.
6
+ - **Tier 2 Collab** (`/api/agent/files/*.md`) — markdown only, reviewable proof-spans. Use when `X-Collab-State: active` (human has the doc open or it has pending suggestions).
7
+
8
+ **Mode rule:** Every file read returns `X-Collab-State`. If `active` → use Tier-2 block-ops so the human can review. Otherwise → use Tier-1 raw fs. A raw write to an `active` .md is rejected 409 `COLLAB_ACTIVE` with the Tier-2 URL.
9
+
10
+ Set `basis` and `basisDetail` on every Tier-2 content op so the human can see where your changes came from. Prefer `suggestion.add` over direct block ops unless told otherwise.
11
+
12
+ MCP-capable agents: `npx wiki-viewer-mcp` (set `WIKI_VIEWER_URL`, `WIKI_VIEWER_TOKEN`, `WIKI_VIEWER_AGENT_ID`) handles tier routing automatically.
@@ -8,6 +8,17 @@ license: MIT
8
8
 
9
9
  You are working with **wiki-viewer**, a local-first markdown viewer that exposes a Proof-SDK-compatible HTTP API for agents. Files on disk are the source of truth. Every AI-authored edit is wrapped in an inline `<proof-span>` mark so the human can see, accept, or revert your contribution.
10
10
 
11
+ ## Quick orientation: two tiers
12
+
13
+ | Tier | Routes | File types | When to use |
14
+ | ------------------- | ----------------------- | ----------------------------------- | --------------------------------------------------------------------------------- |
15
+ | **Tier 1 — Raw FS** | `/api/agent/fs/*` | All types (markdown, code, binary…) | Fast filework. Read, write, list, move, delete, search. Audited but no review UI. |
16
+ | **Tier 2 — Collab** | `/api/agent/files/*.md` | Markdown only | Reviewable prose. Edits become proof-spans the human can accept/revert. |
17
+
18
+ Both tiers use the same auth (TOFU token). Pick the tier based on `X-Collab-State` (see [Which tier do I use?](#which-tier-do-i-use)).
19
+
20
+ ---
21
+
11
22
  ## Discovery
12
23
 
13
24
  The server publishes everything you need at one URL:
@@ -214,6 +225,180 @@ Acks are advisory; events are never deleted.
214
225
  | 422 | INVALID_MARKDOWN | op's markdown failed to parse |
215
226
  | 429 | RATE_LIMITED | bucket exhausted, honor `Retry-After` header |
216
227
 
228
+ ---
229
+
230
+ ## Working with files (raw filesystem — Tier 1)
231
+
232
+ All Tier-1 routes require `Authorization: Bearer <token>` + `X-Agent-Id: ai:<name>` (same as Tier 2). The agent's `scope.paths` glob applies to every surface (reads, ls results, search results, move, delete).
233
+
234
+ ### Scope ops
235
+
236
+ When registering, `scope.ops` can include:
237
+
238
+ - `read` — read files + list + search
239
+ - `mutate` — create / overwrite / move
240
+ - `delete` — remove files/dirs (separate from mutate so a human can grant edit-but-never-delete)
241
+
242
+ Example registration with full access:
243
+
244
+ ```json
245
+ {
246
+ "id": "ai:claude",
247
+ "scope": { "paths": ["**/*"], "ops": ["read", "mutate", "delete"] }
248
+ }
249
+ ```
250
+
251
+ ### Read a file
252
+
253
+ ```
254
+ GET /api/agent/fs/file/<path>
255
+ ```
256
+
257
+ Returns raw bytes. Metadata in response headers:
258
+
259
+ ```
260
+ ETag: "<sha256>"
261
+ X-File-Size: <bytes>
262
+ X-File-Mtime: <ISO-8601>
263
+ Content-Type: <mime>
264
+ X-Collab-State: active | tracked | untracked | not-markdown
265
+ X-Collab-Revision: <n>
266
+ X-Collab-Snapshot: /api/agent/files/<path>.md # when applicable
267
+ ```
268
+
269
+ Supports `Range` header for large/binary files.
270
+
271
+ ### Write a file
272
+
273
+ ```
274
+ PUT /api/agent/fs/file/<path>
275
+ If-Match: "<sha256>" # required for overwrites; omit for creates
276
+ Content-Type: <mime>
277
+ <raw bytes body>
278
+ ```
279
+
280
+ - **Create** (file doesn't exist): omit `If-Match`. Creating a file that already exists without `If-Match` → 412 (no blind clobber).
281
+ - **Overwrite** (file exists): send `If-Match` with the sha256 you read. Mismatch → 412.
282
+ - `?mkdirs=true` — create parent directories.
283
+ - `?force=true` — bypass `If-Match` check (audited; use sparingly).
284
+
285
+ For `.md` files, also send:
286
+
287
+ ```
288
+ If-Collab-Match: <X-Collab-Revision> # prevents writing into a live collab session
289
+ ```
290
+
291
+ (See [Which tier do I use?](#which-tier-do-i-use).)
292
+
293
+ Response:
294
+
295
+ ```json
296
+ {
297
+ "path": "...",
298
+ "sha256": "...",
299
+ "size": 1234,
300
+ "mtime": "...",
301
+ "created": false
302
+ }
303
+ ```
304
+
305
+ ### Delete a file
306
+
307
+ ```
308
+ DELETE /api/agent/fs/file/<path>
309
+ If-Match: "<sha256>" # required — you must have read the file first
310
+ ```
311
+
312
+ Requires `delete` in `scope.ops`. Deletes the `.proof/` sidecar too for `.md` files. For directories: `?recursive=true`.
313
+
314
+ ### List a directory
315
+
316
+ ```
317
+ GET /api/agent/fs/ls/<path>?recursive=true&limit=500&depth=3
318
+ ```
319
+
320
+ Returns `{ entries: [{name, type, path, size, mtime}] }`. Unauthorized paths silently omitted. Excludes `.proof/`.
321
+
322
+ ### Move / rename
323
+
324
+ ```
325
+ POST /api/agent/fs/move
326
+ Content-Type: application/json
327
+
328
+ { "from": "notes/old.md", "to": "notes/new.md", "ifMatch": "<sha256>" }
329
+ ```
330
+
331
+ Moves the `.proof/` sidecar for `.md` files automatically.
332
+
333
+ ### Search (grep / glob)
334
+
335
+ ```
336
+ POST /api/agent/fs/search
337
+ Content-Type: application/json
338
+
339
+ { "kind": "grep", "query": "TODO", "path": "src", "glob": "*.ts", "limit": 100 }
340
+ ```
341
+
342
+ ```
343
+ POST /api/agent/fs/search
344
+ Content-Type: application/json
345
+
346
+ { "kind": "glob", "query": "**/*.test.ts" }
347
+ ```
348
+
349
+ Server-side: one call replaces dozens of `ls`+`read` round-trips. Results are scope-filtered. Glob dialect: `**`, `*`, `?` only (no braces/negation in v1).
350
+
351
+ ### Error codes (Tier 1)
352
+
353
+ | Status | Code | Meaning |
354
+ | ------ | ------------------- | --------------------------------------------------------------------------- |
355
+ | 400 | BAD_REQUEST | Invalid path or body |
356
+ | 401 | UNAUTHORIZED | Bad/missing token or X-Agent-Id |
357
+ | 403 | FORBIDDEN | Scope mismatch or missing `delete` op |
358
+ | 404 | NOT_FOUND | Path doesn't exist |
359
+ | 409 | COLLAB_ACTIVE | Raw PUT rejected — doc is active-collab; use Tier-2 or send If-Collab-Match |
360
+ | 412 | PRECONDITION_FAILED | If-Match sha mismatch (concurrent modification) |
361
+ | 413 | PAYLOAD_TOO_LARGE | File exceeds 50 MB limit |
362
+ | 429 | RATE_LIMITED | Honor `Retry-After` |
363
+
364
+ ---
365
+
366
+ ## Which tier do I use?
367
+
368
+ Every `GET /api/agent/fs/file/<path>` (and every Tier-2 snapshot read) returns `X-Collab-State`.
369
+
370
+ | `X-Collab-State` | Meaning | Use |
371
+ | ---------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
372
+ | `active` | Human has this `.md` open in the editor, OR it has pending suggestions / unresolved comments. | **Tier 2 block-ops only.** A raw `PUT` returns `409 COLLAB_ACTIVE`. |
373
+ | `tracked` | `.md` has a sidecar (prior collab history), no active session. | Prefer Tier-2 for prose/semantic edits. Raw ok for mechanical/whole-file ops (reformat, regenerate). |
374
+ | `untracked` | `.md`, no sidecar yet. | Raw is fine. Tier-2 creates a sidecar (starts provenance tracking). |
375
+ | `not-markdown` | Any non-`.md` file. | **Tier-1 raw only.** Tier 2 does not apply. |
376
+
377
+ **Rule (one sentence):** Read the file, check `X-Collab-State`, use Tier-2 if `active`, else use Tier-1. For non-markdown, always Tier-1.
378
+
379
+ **Editing a `.md` safely:**
380
+
381
+ ```
382
+ 1. GET /api/agent/fs/file/<path>.md
383
+ → note ETag (sha256) and X-Collab-Revision + X-Collab-State
384
+
385
+ 2a. If X-Collab-State == "active":
386
+ → POST /api/agent/files/<path>.md (block-ops, Tier-2)
387
+ Use the X-Collab-Snapshot URL. See "Mutating" section below.
388
+
389
+ 2b. Otherwise (tracked / untracked):
390
+ → PUT /api/agent/fs/file/<path>.md (raw Tier-1)
391
+ If-Match: "<ETag from step 1>"
392
+ If-Collab-Match: <X-Collab-Revision from step 1>
393
+
394
+ 3. If you get 409 COLLAB_ACTIVE: the doc became active after your read.
395
+ Switch to Tier-2 (block-ops) using the URL in the response body.
396
+ ```
397
+
398
+ **MCP-capable agents:** run `npx wiki-viewer-mcp` — it reads `X-Collab-State` automatically and routes to the correct tier. Set env vars `WIKI_VIEWER_URL`, `WIKI_VIEWER_TOKEN`, `WIKI_VIEWER_AGENT_ID`.
399
+
400
+ ---
401
+
217
402
  ## Working style
218
403
 
219
404
  - Read before write. Always GET a fresh snapshot to capture current `revision` and block `ref`s.
@@ -226,11 +411,19 @@ Acks are advisory; events are never deleted.
226
411
  ## Sample first interaction
227
412
 
228
413
  ```
229
- 1. GET /api/agents/install # discovery
230
- 2. POST /api/agent/register # register
414
+ 1. GET /api/agents/install # discovery
415
+ 2. POST /api/agent/register # register (include "delete" in ops if needed)
231
416
  3. tell human: "approve me in the AI Panel"
232
- 4. GET /api/agent/register/<regId> # poll until approved
233
- 5. GET /api/agent/files/<path>.md # read
234
- 6. POST /api/agent/files/<path>.md # suggest or mutate
235
- 7. GET /api/agent/events/<path>.md?after=<id> # listen for replies
417
+ 4. GET /api/agent/register/<regId> # poll until approved
418
+
419
+ # Working mode (Tier-1, e.g. code files or untracked .md)
420
+ 5. GET /api/agent/fs/ls/src?recursive=true # list
421
+ 6. GET /api/agent/fs/file/src/main.ts # read -> capture ETag
422
+ 7. PUT /api/agent/fs/file/src/main.ts (If-Match: <sha>) # write
423
+
424
+ # Collaborating mode (Tier-2, active .md)
425
+ 5. GET /api/agent/fs/file/notes/plan.md # read -> X-Collab-State: active
426
+ 6. GET /api/agent/files/notes/plan.md # read snapshot (Tier-2)
427
+ 7. POST /api/agent/files/notes/plan.md # suggest or mutate (Tier-2)
428
+ 8. GET /api/agent/events/notes/plan.md?after=<id> # listen for replies
236
429
  ```
@@ -0,0 +1,124 @@
1
+ # File vs Collab Authority Model
2
+
3
+ **TL;DR:** File on disk is truth. Tier-1 raw edits are fast + audited but unmarked. Tier-2 block-ops are slower but reviewable. The `X-Collab-State` header tells an agent which tier to use; the server enforces it atomically.
4
+
5
+ ---
6
+
7
+ ## Source of truth
8
+
9
+ The `.md` file on disk is the canonical document. The `.proof/<path>.json` sidecar holds provenance metadata (comments, suggestions, proof-span refs, fingerprint) but is never the source of truth for content. If the sidecar is lost the document is intact; if the document is lost the sidecar is orphaned.
10
+
11
+ ---
12
+
13
+ ## Two tiers
14
+
15
+ ### Tier 1 — Raw FS
16
+
17
+ - Routes: `GET/PUT/DELETE /api/agent/fs/file/<path>`, `GET /api/agent/fs/ls/...`, `POST /api/agent/fs/move`, `POST /api/agent/fs/search`.
18
+ - All file types. Raw bytes in, raw bytes out.
19
+ - Writes are **atomic** (temp-file + rename in same directory).
20
+ - **Audit:** every mutation records a row in the `agent_fs_audit` SQLite table (`path`, `op`, `agentId`, `oldSha`, `newSha`, `forced`, `at`).
21
+ - **Event:** `file.rawWritten` emitted with `{ by: "ai:<id>", path, oldSha, newSha }`. Distinct from `file.externallyEdited` (chokidar, writer unknown).
22
+ - No `<proof-span>` marks. No accept/revert UI. Audit log is the paper trail.
23
+
24
+ ### Tier 2 — Collab
25
+
26
+ - Routes: `GET/POST /api/agent/files/<path>.md`, `GET/POST /api/agent/events/<path>.md`.
27
+ - Markdown only.
28
+ - Edits are wrapped in `<proof-span>` marks inline in the `.md` file.
29
+ - Provenance (suggestions, comments, block refs) stored in `.proof/<path>.json` sidecar.
30
+ - Human can accept or revert individual spans in the editor UI.
31
+ - Mandatory `baseRevision` optimistic concurrency + `Idempotency-Key` per request.
32
+
33
+ ---
34
+
35
+ ## The `X-Collab-State` header
36
+
37
+ Every `GET /api/agent/fs/file/<path>` (Tier-1 read) and every Tier-2 snapshot read returns:
38
+
39
+ ```
40
+ X-Collab-State: active | tracked | untracked | not-markdown
41
+ X-Collab-Revision: <n>
42
+ X-Collab-Snapshot: /api/agent/files/<path>.md # present when not not-markdown
43
+ ```
44
+
45
+ ### State machine
46
+
47
+ ```
48
+ [file created]
49
+
50
+
51
+ untracked ──── first Tier-2 op ────► tracked
52
+ │ │
53
+ │ (Tier-1 writes stay untracked) │ human opens editor
54
+ │ ▼
55
+ │ active
56
+ │ │
57
+ │ editor closes +
58
+ │ no artifacts
59
+ │ │
60
+ │ ▼
61
+ └────────────────────────────────── tracked
62
+ ```
63
+
64
+ **`active`** is set by two independent sources (OR'd):
65
+
66
+ 1. **Artifacts** — sidecar has `pendingSuggestions > 0` OR `unresolvedComments > 0` OR `proofSpanCount > 0`. Cheap: always in memory when sidecar is loaded.
67
+ 2. **Human edit lease** — a short-TTL presence marker (default 90 s, heartbeat ~30 s) set via `POST /api/wiki/presence { path, action: "open" | "heartbeat" | "close" }`. Closes the false-negative: a human who opens a doc but hasn't typed a suggestion yet still reads `active`.
68
+
69
+ `X-Collab-Revision` bumps on any sidecar write OR lease open/close. Used by R6.
70
+
71
+ ---
72
+
73
+ ## R6 — atomic TOCTOU race fix
74
+
75
+ `If-Match` (sha256) protects _bytes_. But a human could open the doc between the agent's read and its raw `PUT`, making it `active` while bytes are unchanged — so `If-Match` passes but the write clobbers a live session.
76
+
77
+ **Fix:** a raw `PUT` to any `.md` re-checks `collabState` **inside the same `withFileMutex` call, immediately before writing**. If the state is now `active` and the request did not supply a matching `If-Collab-Match: <revision>`, the write is **rejected 409 `COLLAB_ACTIVE`** with the Tier-2 URL. This check is atomic with the write — not advisory.
78
+
79
+ `?force=true` bypasses both `If-Match` and `If-Collab-Match` and is recorded in the audit row as `forced: true`.
80
+
81
+ ---
82
+
83
+ ## R2 — eager sidecar reconciliation
84
+
85
+ After a raw `PUT` to a tracked/active `.md`, the sidecar must be reconciled **synchronously, inside the same mutex**, before the lock is released:
86
+
87
+ 1. Re-scan the new file bytes for surviving `<proof-span>` marks.
88
+ 2. Re-bind each proof ref to its new block offset; mark unresolvable refs `stale: true`.
89
+ 3. Set `sidecar.fingerprint = newSha` (so the lazy mismatch-trigger in `readSnapshot` does NOT re-fire — the reconcile already ran).
90
+ 4. Emit `file.rawWritten` event.
91
+
92
+ **Do not** just bump the fingerprint and let reconciliation fire lazily on the next read. The lazy trigger fires only when `sidecar.fingerprint != file sha256`; once the raw write sets fingerprint current, the mismatch is gone and rebuild never runs. Eager reconciliation is the correct invariant.
93
+
94
+ ---
95
+
96
+ ## Known v1 limitations
97
+
98
+ ### (a) Editor does not call `/api/wiki/presence` yet
99
+
100
+ The front-end editor does **not** currently issue `POST /api/wiki/presence` on open/heartbeat/close. Consequence: lease-based `active` never fires. Only artifact-based `active` works (pending suggestions, unresolved comments, proof-spans in sidecar).
101
+
102
+ **Impact:** a human who opens a doc but has no pending review artifacts will see `X-Collab-State: tracked` (or `untracked`), not `active`. An agent raw-writing that doc won't be blocked. This is a safety gap — not a correctness bug (no data loss), but the intended "lock out while human is live-editing" experience is incomplete until the editor heartbeat is wired.
103
+
104
+ **Fix:** call `POST /api/wiki/presence { path, action: "open" }` when the editor mounts the doc and `{ action: "close" }` on unmount. Heartbeat every ~30 s. Tracked as a follow-up task.
105
+
106
+ ### (b) Lease store is in-memory (single-process only)
107
+
108
+ The presence lease map lives in the Node.js process memory. In a multi-process deployment (PM2 cluster mode, multiple Next.js instances behind a load balancer) each process has its own lease state and processes do not share it.
109
+
110
+ **Impact:** a human's open lease set by process A is invisible to process B. A raw write handled by process B may not see the `active` state and proceed when it should block.
111
+
112
+ **Fix:** move the lease store to the shared SQLite (`~/.wiki-viewer/auth.db`) with a TTL column and a periodic cleanup job. The table is already shared across the process boundary. Tracked as a follow-up task.
113
+
114
+ ---
115
+
116
+ ## Sidecar lifecycle rules
117
+
118
+ | Raw-fs op | On non-`.md` | On `.md` |
119
+ | ------------------------ | ------------ | ------------------------------------------------------------------ |
120
+ | `PUT` (create/overwrite) | plain write | acquire mutex → write → reconcile sidecar → emit `file.rawWritten` |
121
+ | `DELETE` | plain delete | delete file + delete `.proof/<path>.json` sidecar |
122
+ | `POST /fs/move` | plain rename | rename file + rename `.proof/<old>.json` → `.proof/<new>.json` |
123
+
124
+ The human `wiki/move` route was previously a bare `rename` that orphaned sidecars. It now uses the same shared `moveSidecar` helper (fixed in Phase 1).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-viewer",
3
- "version": "1.4.3",
3
+ "version": "1.5.1",
4
4
  "description": "Local file viewer and wiki — view markdown, HTML, PDFs, notebooks, office docs, and more from any directory",
5
5
  "license": "MIT",
6
6
  "type": "module",