@anchrd/intel 0.59.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 (364) hide show
  1. package/README.md +496 -0
  2. package/bin/intel.mjs +134 -0
  3. package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
  4. package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
  5. package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
  6. package/dist/adapters/cloudflare/cloudflare.js +416 -0
  7. package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
  8. package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
  9. package/dist/adapters/content/content.d.ts +3 -0
  10. package/dist/adapters/content/content.js +128 -0
  11. package/dist/adapters/content/content.types.d.ts +38 -0
  12. package/dist/adapters/content/content.types.js +1 -0
  13. package/dist/adapters/db/db-audit.d.ts +62 -0
  14. package/dist/adapters/db/db-audit.js +150 -0
  15. package/dist/adapters/db/db-boards.d.ts +6 -0
  16. package/dist/adapters/db/db-boards.js +324 -0
  17. package/dist/adapters/db/db-feed.d.ts +30 -0
  18. package/dist/adapters/db/db-feed.js +276 -0
  19. package/dist/adapters/db/db-flows.d.ts +42 -0
  20. package/dist/adapters/db/db-flows.js +1311 -0
  21. package/dist/adapters/db/db-grants.d.ts +133 -0
  22. package/dist/adapters/db/db-grants.js +269 -0
  23. package/dist/adapters/db/db-indexing.d.ts +3 -0
  24. package/dist/adapters/db/db-indexing.js +200 -0
  25. package/dist/adapters/db/db-oauth.d.ts +3 -0
  26. package/dist/adapters/db/db-oauth.js +26 -0
  27. package/dist/adapters/db/db-prompts.d.ts +46 -0
  28. package/dist/adapters/db/db-prompts.js +144 -0
  29. package/dist/adapters/db/db.d.ts +6 -0
  30. package/dist/adapters/db/db.js +1687 -0
  31. package/dist/adapters/db/db.types.d.ts +16 -0
  32. package/dist/adapters/db/db.types.js +1 -0
  33. package/dist/adapters/document-converter/document-converter.d.ts +2 -0
  34. package/dist/adapters/document-converter/document-converter.js +26 -0
  35. package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
  36. package/dist/adapters/document-converter/document-converter.types.js +1 -0
  37. package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
  38. package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
  39. package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
  40. package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
  41. package/dist/adapters/index-queue/index-queue.d.ts +10 -0
  42. package/dist/adapters/index-queue/index-queue.js +12 -0
  43. package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
  44. package/dist/adapters/index-queue/index-queue.types.js +1 -0
  45. package/dist/adapters/json-schema/json-schema.d.ts +2 -0
  46. package/dist/adapters/json-schema/json-schema.js +77 -0
  47. package/dist/adapters/openid/openid.d.ts +5 -0
  48. package/dist/adapters/openid/openid.js +301 -0
  49. package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
  50. package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
  51. package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
  52. package/dist/adapters/remote-tools/remote-tools.js +129 -0
  53. package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
  54. package/dist/adapters/semantic-index/semantic-index.js +139 -0
  55. package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
  56. package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
  57. package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
  58. package/dist/adapters/session-cookie/session-cookie.js +87 -0
  59. package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
  60. package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
  61. package/dist/audit/audit.d.ts +2 -0
  62. package/dist/audit/audit.js +87 -0
  63. package/dist/audit/audit.types.d.ts +23 -0
  64. package/dist/audit/audit.types.js +1 -0
  65. package/dist/auth/auth.d.ts +2 -0
  66. package/dist/auth/auth.js +284 -0
  67. package/dist/auth/auth.types.d.ts +108 -0
  68. package/dist/auth/auth.types.js +1 -0
  69. package/dist/boards/boards.d.ts +44 -0
  70. package/dist/boards/boards.js +447 -0
  71. package/dist/boards/boards.types.d.ts +117 -0
  72. package/dist/boards/boards.types.js +1 -0
  73. package/dist/build/build.d.ts +2 -0
  74. package/dist/build/build.js +192 -0
  75. package/dist/build/build.types.d.ts +17 -0
  76. package/dist/build/build.types.js +1 -0
  77. package/dist/bundle/bundle.d.ts +4 -0
  78. package/dist/bundle/bundle.js +1194 -0
  79. package/dist/bundle/bundle.types.d.ts +33 -0
  80. package/dist/bundle/bundle.types.js +1 -0
  81. package/dist/cli/cli.d.ts +4 -0
  82. package/dist/cli/cli.js +262 -0
  83. package/dist/cli/cli.types.d.ts +14 -0
  84. package/dist/cli/cli.types.js +1 -0
  85. package/dist/contract/audit.d.ts +50 -0
  86. package/dist/contract/audit.js +72 -0
  87. package/dist/contract/board.d.ts +150 -0
  88. package/dist/contract/board.js +262 -0
  89. package/dist/contract/bundle.d.ts +147 -0
  90. package/dist/contract/bundle.js +139 -0
  91. package/dist/contract/contract.d.ts +112 -0
  92. package/dist/contract/contract.js +143 -0
  93. package/dist/contract/feed.d.ts +126 -0
  94. package/dist/contract/feed.js +149 -0
  95. package/dist/contract/flow-run.d.ts +346 -0
  96. package/dist/contract/flow-run.js +194 -0
  97. package/dist/contract/flow.d.ts +1057 -0
  98. package/dist/contract/flow.js +552 -0
  99. package/dist/contract/node.d.ts +551 -0
  100. package/dist/contract/node.js +456 -0
  101. package/dist/contract/share.d.ts +216 -0
  102. package/dist/contract/share.js +134 -0
  103. package/dist/contract/table.d.ts +171 -0
  104. package/dist/contract/table.js +117 -0
  105. package/dist/contract/tool.d.ts +122 -0
  106. package/dist/contract/tool.js +172 -0
  107. package/dist/feed/feed.d.ts +2 -0
  108. package/dist/feed/feed.js +77 -0
  109. package/dist/feed/feed.types.d.ts +23 -0
  110. package/dist/feed/feed.types.js +1 -0
  111. package/dist/flows/flows.d.ts +41 -0
  112. package/dist/flows/flows.js +2216 -0
  113. package/dist/flows/flows.types.d.ts +339 -0
  114. package/dist/flows/flows.types.js +1 -0
  115. package/dist/http/http.d.ts +9 -0
  116. package/dist/http/http.js +874 -0
  117. package/dist/http/http.types.d.ts +22 -0
  118. package/dist/http/http.types.js +1 -0
  119. package/dist/indexing/indexing.d.ts +6 -0
  120. package/dist/indexing/indexing.js +239 -0
  121. package/dist/indexing/indexing.types.d.ts +11 -0
  122. package/dist/indexing/indexing.types.js +1 -0
  123. package/dist/intel/intel.d.ts +3 -0
  124. package/dist/intel/intel.js +194 -0
  125. package/dist/intel/intel.types.d.ts +24 -0
  126. package/dist/intel/intel.types.js +1 -0
  127. package/dist/mcp/mcp.d.ts +2 -0
  128. package/dist/mcp/mcp.js +1292 -0
  129. package/dist/mcp/mcp.types.d.ts +25 -0
  130. package/dist/mcp/mcp.types.js +1 -0
  131. package/dist/nodes/document-links/document-links.d.ts +20 -0
  132. package/dist/nodes/document-links/document-links.js +57 -0
  133. package/dist/nodes/nodes.d.ts +2 -0
  134. package/dist/nodes/nodes.js +1667 -0
  135. package/dist/nodes/nodes.types.d.ts +511 -0
  136. package/dist/nodes/nodes.types.js +1 -0
  137. package/dist/prepare/prepare.d.ts +4 -0
  138. package/dist/prepare/prepare.js +16 -0
  139. package/dist/prepare/prepare.types.d.ts +9 -0
  140. package/dist/prepare/prepare.types.js +1 -0
  141. package/dist/prompts/prompts.d.ts +2 -0
  142. package/dist/prompts/prompts.js +65 -0
  143. package/dist/prompts/prompts.types.d.ts +71 -0
  144. package/dist/prompts/prompts.types.js +1 -0
  145. package/dist/shared/callers/callers.d.ts +28 -0
  146. package/dist/shared/callers/callers.js +30 -0
  147. package/dist/shared/csv/csv.d.ts +13 -0
  148. package/dist/shared/csv/csv.js +85 -0
  149. package/dist/shared/document-text/document-text.d.ts +21 -0
  150. package/dist/shared/document-text/document-text.js +31 -0
  151. package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
  152. package/dist/shared/gate-authorization/gate-authorization.js +26 -0
  153. package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
  154. package/dist/shared/grant-expiry/grant-expiry.js +26 -0
  155. package/dist/shared/intel-error/intel-error.d.ts +22 -0
  156. package/dist/shared/intel-error/intel-error.js +27 -0
  157. package/dist/shared/plain-title/plain-title.d.ts +16 -0
  158. package/dist/shared/plain-title/plain-title.js +45 -0
  159. package/dist/shared/problem-details/problem-details.d.ts +30 -0
  160. package/dist/shared/problem-details/problem-details.js +18 -0
  161. package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
  162. package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
  163. package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
  164. package/dist/shared/safe-return-path/safe-return-path.js +11 -0
  165. package/dist/shared/sha256/sha256.d.ts +2 -0
  166. package/dist/shared/sha256/sha256.js +14 -0
  167. package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
  168. package/dist/tools/tool-servers/tool-servers.js +177 -0
  169. package/dist/tools/tools.d.ts +2 -0
  170. package/dist/tools/tools.js +207 -0
  171. package/dist/tools/tools.types.d.ts +56 -0
  172. package/dist/tools/tools.types.js +1 -0
  173. package/examples/branding/favicon.svg +5 -0
  174. package/examples/branding/logo.svg +5 -0
  175. package/examples/branding/theme.css +16 -0
  176. package/examples/dev.vars.example +7 -0
  177. package/examples/intel.json +9 -0
  178. package/migrations/0000_intel_foundation.sql +203 -0
  179. package/migrations/0001_portal_native_tools.sql +21 -0
  180. package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
  181. package/migrations/0003_folder_permissions.sql +211 -0
  182. package/migrations/0004_subflow_runs.sql +14 -0
  183. package/migrations/0005_flow_node_cleanup.sql +28 -0
  184. package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
  185. package/migrations/0006_links_are_written_in_the_text.sql +20 -0
  186. package/migrations/0007_no_node_waits.sql +69 -0
  187. package/migrations/0008_three_layers.sql +130 -0
  188. package/migrations/0009_no_context_policy.sql +48 -0
  189. package/migrations/0010_no_run_waits_a_year.sql +27 -0
  190. package/migrations/0011_one_name_for_the_tree.sql +53 -0
  191. package/migrations/0012_table_snapshots.sql +29 -0
  192. package/migrations/0017_a_vector_per_card.sql +38 -0
  193. package/migrations/0018_no_context_policy_at_last.sql +97 -0
  194. package/migrations/0019_one_name_for_the_grants.sql +52 -0
  195. package/migrations/0020_cascade_purge_replay.sql +11 -0
  196. package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
  197. package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
  198. package/migrations/0023_a_board_and_its_tasks.sql +116 -0
  199. package/migrations/0024_the_archive_is_a_column.sql +33 -0
  200. package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
  201. package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
  202. package/migrations/0027_the_runs_of_every_flow.sql +27 -0
  203. package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
  204. package/package.json +118 -0
  205. package/src/contract/CLAUDE.md +152 -0
  206. package/src/contract/README.md +102 -0
  207. package/src/contract/audit.ts +85 -0
  208. package/src/contract/board.ts +306 -0
  209. package/src/contract/bundle.ts +157 -0
  210. package/src/contract/contract.ts +161 -0
  211. package/src/contract/feed.ts +165 -0
  212. package/src/contract/flow-run.ts +241 -0
  213. package/src/contract/flow.ts +675 -0
  214. package/src/contract/node.ts +583 -0
  215. package/src/contract/share.ts +196 -0
  216. package/src/contract/table.ts +178 -0
  217. package/src/contract/tool.ts +202 -0
  218. package/ui/README.md +110 -0
  219. package/ui/components.json +21 -0
  220. package/ui/index.html +13 -0
  221. package/ui/package.json +11 -0
  222. package/ui/scripts/lint-tokens.mjs +187 -0
  223. package/ui/src/access-summary/access-summary.tsx +385 -0
  224. package/ui/src/app/action-slot/action-slot.tsx +27 -0
  225. package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
  226. package/ui/src/app/app-tree/app-tree.tsx +1175 -0
  227. package/ui/src/app/app.tsx +87 -0
  228. package/ui/src/app/header-search/header-search.tsx +294 -0
  229. package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
  230. package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
  231. package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
  232. package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
  233. package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
  234. package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
  235. package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
  236. package/ui/src/app/tree-move/tree-move.tsx +427 -0
  237. package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
  238. package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
  239. package/ui/src/app/user-footer/user-footer.tsx +188 -0
  240. package/ui/src/app/view-menu/view-menu.tsx +115 -0
  241. package/ui/src/app-root/app-root.tsx +109 -0
  242. package/ui/src/app-root/app-root.types.ts +27 -0
  243. package/ui/src/archive/archive.tsx +417 -0
  244. package/ui/src/attachment-detail/attachment-detail.ts +52 -0
  245. package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
  246. package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
  247. package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
  248. package/ui/src/board/board-assignee/board-assignee.ts +48 -0
  249. package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
  250. package/ui/src/board/board-chip/board-chip.tsx +46 -0
  251. package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
  252. package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
  253. package/ui/src/board/board-data/board-data.ts +195 -0
  254. package/ui/src/board/board-data/board-data.types.ts +38 -0
  255. package/ui/src/board/board-dates/board-dates.ts +40 -0
  256. package/ui/src/board/board-dates/board-dates.tsx +105 -0
  257. package/ui/src/board/board-kanban/board-kanban.ts +162 -0
  258. package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
  259. package/ui/src/board/board-open-task/board-open-task.ts +32 -0
  260. package/ui/src/board/board-panel/board-panel.tsx +259 -0
  261. package/ui/src/board/board-settings/board-settings.tsx +226 -0
  262. package/ui/src/board/board-status/board-status.tsx +60 -0
  263. package/ui/src/board/board-stripes/board-stripes.ts +128 -0
  264. package/ui/src/board/board-table/board-table.ts +53 -0
  265. package/ui/src/board/board-table/board-table.tsx +610 -0
  266. package/ui/src/board/board-task/board-task.ts +108 -0
  267. package/ui/src/board/board-task/board-task.tsx +335 -0
  268. package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
  269. package/ui/src/branding/branding.tsx +17 -0
  270. package/ui/src/branding/custom-logo.ts +1 -0
  271. package/ui/src/branding/favicon.default.svg +5 -0
  272. package/ui/src/branding/favicon.svg +5 -0
  273. package/ui/src/components/ui/avatar.tsx +39 -0
  274. package/ui/src/components/ui/button.tsx +64 -0
  275. package/ui/src/components/ui/collapsible.tsx +20 -0
  276. package/ui/src/components/ui/command.tsx +160 -0
  277. package/ui/src/components/ui/dialog.tsx +143 -0
  278. package/ui/src/components/ui/dropdown-menu.tsx +187 -0
  279. package/ui/src/components/ui/input.tsx +21 -0
  280. package/ui/src/components/ui/popover.tsx +41 -0
  281. package/ui/src/components/ui/select.tsx +163 -0
  282. package/ui/src/components/ui/separator.tsx +26 -0
  283. package/ui/src/components/ui/sheet.tsx +136 -0
  284. package/ui/src/components/ui/sidebar.tsx +693 -0
  285. package/ui/src/components/ui/skeleton.tsx +13 -0
  286. package/ui/src/components/ui/switch.tsx +25 -0
  287. package/ui/src/components/ui/table.tsx +82 -0
  288. package/ui/src/components/ui/tabs.tsx +52 -0
  289. package/ui/src/components/ui/tooltip.tsx +51 -0
  290. package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
  291. package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
  292. package/ui/src/data/query-client.ts +18 -0
  293. package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
  294. package/ui/src/data/request-refusal/request-refusal.ts +51 -0
  295. package/ui/src/data/sign-in/sign-in.ts +97 -0
  296. package/ui/src/data/sign-in/sign-in.types.ts +15 -0
  297. package/ui/src/document-link/document-link.tsx +114 -0
  298. package/ui/src/document-markdown/document-markdown.ts +195 -0
  299. package/ui/src/editor-schema/editor-schema.ts +23 -0
  300. package/ui/src/entry-picker/entry-picker.tsx +238 -0
  301. package/ui/src/feed/feed.tsx +344 -0
  302. package/ui/src/file-preview/file-preview-view.tsx +496 -0
  303. package/ui/src/file-preview/file-preview.tsx +28 -0
  304. package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
  305. package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
  306. package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
  307. package/ui/src/file-preview/word-file-preview.tsx +5 -0
  308. package/ui/src/flow-runs/flow-runs.tsx +238 -0
  309. package/ui/src/flows/flows.tsx +1646 -0
  310. package/ui/src/flows/node-icon/node-icon.ts +34 -0
  311. package/ui/src/flows/node-palette/node-palette.tsx +214 -0
  312. package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
  313. package/ui/src/folder-contents/folder-contents.tsx +321 -0
  314. package/ui/src/folder-index/folder-index.ts +55 -0
  315. package/ui/src/frontmatter/frontmatter.tsx +193 -0
  316. package/ui/src/graph-pane/graph-pane.tsx +53 -0
  317. package/ui/src/hooks/use-mobile.ts +19 -0
  318. package/ui/src/i18n/custom.json +4 -0
  319. package/ui/src/i18n/de.json +609 -0
  320. package/ui/src/i18n/en.json +609 -0
  321. package/ui/src/i18n/es.json +609 -0
  322. package/ui/src/i18n/i18n-context.tsx +65 -0
  323. package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
  324. package/ui/src/i18n/i18n.ts +32 -0
  325. package/ui/src/i18n/i18n.types.ts +49 -0
  326. package/ui/src/kind-icon.ts +97 -0
  327. package/ui/src/lib/utils.ts +6 -0
  328. package/ui/src/main.tsx +68 -0
  329. package/ui/src/modal/modal.tsx +53 -0
  330. package/ui/src/node-details/node-details.tsx +545 -0
  331. package/ui/src/node-editor/node-editor.tsx +304 -0
  332. package/ui/src/node-graph/graph-notice.tsx +21 -0
  333. package/ui/src/node-graph/node-graph.ts +214 -0
  334. package/ui/src/node-graph/node-graph.tsx +295 -0
  335. package/ui/src/node-import/node-import.tsx +418 -0
  336. package/ui/src/node-save-error.ts +46 -0
  337. package/ui/src/node-source/node-source.tsx +128 -0
  338. package/ui/src/node-table/node-table.tsx +107 -0
  339. package/ui/src/nodes/nodes.tsx +240 -0
  340. package/ui/src/rename-resource/rename-resource.ts +80 -0
  341. package/ui/src/resolved-names/resolved-names.tsx +67 -0
  342. package/ui/src/resource-error.ts +33 -0
  343. package/ui/src/resource-menu/resource-menu.tsx +1156 -0
  344. package/ui/src/router/router-context.ts +6 -0
  345. package/ui/src/router/router.tsx +67 -0
  346. package/ui/src/router/router.types.ts +8 -0
  347. package/ui/src/router/selection-search.ts +88 -0
  348. package/ui/src/save-button/save-button.tsx +94 -0
  349. package/ui/src/section-hint/section-hint.tsx +40 -0
  350. package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
  351. package/ui/src/styles.css +189 -0
  352. package/ui/src/table-columns/table-columns.tsx +379 -0
  353. package/ui/src/theme/custom.css +1 -0
  354. package/ui/src/theme/theme-context.tsx +89 -0
  355. package/ui/src/theme/theme.ts +70 -0
  356. package/ui/src/time/relative-time.tsx +41 -0
  357. package/ui/src/time/time-context.tsx +78 -0
  358. package/ui/src/time/time.ts +165 -0
  359. package/ui/src/title-row/title-row.tsx +185 -0
  360. package/ui/src/tools/tools.tsx +546 -0
  361. package/ui/src/user-name/user-name.ts +61 -0
  362. package/ui/src/verbatim/verbatim.tsx +68 -0
  363. package/ui/tsconfig.json +17 -0
  364. package/ui/vite.config.ts +129 -0
@@ -0,0 +1,583 @@
1
+ import { z } from "zod";
2
+ import { baseVersion, IdempotencyKey, IntelId, IsoDateTime, PromptName } from "./contract.ts";
3
+
4
+ // What this installation is equipped to do — deployment facts, never the caller's permissions
5
+ // (those stay behind each door, where `/session` deliberately does not carry them). `agentRuntime`
6
+ // says whether an agent Worker is bound at all (#190): without it the UI offers no "New agent" and
7
+ // an agent node explains itself instead of rendering views that could only end in a 503.
8
+ // The fourth kind is `table` (#40); `board` and `task` are the fifth and sixth (**D66**, #376).
9
+ // Each is a kind of node, not a kind of thing: it hangs in the same folder tree, inherits the same
10
+ // folder grants, carries the same immutable versions and the same R2 body as a document
11
+ // (ADR-0004 §1). Only the media type and the operations below differ. `agent` is NOT among them —
12
+ // D65 moved agents out of Intel entirely (ADR-0007).
13
+ //
14
+ // ⚠️ **The sentence that stood here said the opposite, and it was right for its day.** Until
15
+ // 2026-08-20 this comment read *"a board is one node carrying its tasks and not a folder that only
16
+ // tasks may live in (#285)"*. D66 reverses it, and the reason is not taste:
17
+ //
18
+ // * **The tree has to hide `task` on EVERY level query.** As a kind that is a condition on a
19
+ // column already in the statement; as a `document` plus a side-table row it would be a join on
20
+ // the hottest path. And with tasks hidden a board has no visible children, so the chevron falls
21
+ // away with no rule of its own.
22
+ // * **`audit_list` exists since #620.** A task that is a node makes "task assigned to Paul" an
23
+ // event `anchrd/signals` can pick up. Inside one board's content every change looks the same
24
+ // from outside — "the board changed" — and nothing can act on it.
25
+ //
26
+ // ⚠️ What did NOT change: `status`, `assignee`, the dates and the ordering live in `board_tasks`,
27
+ // not on the node. They are filter columns and would stand empty on every other kind.
28
+ export const NodeKind = z.enum(["folder", "document", "attachment", "table", "board", "task"]);
29
+ export type NodeKind = z.infer<typeof NodeKind>;
30
+
31
+ export const Node = z.strictObject({
32
+ id: IntelId,
33
+ parentId: IntelId.nullable(),
34
+ kind: NodeKind,
35
+ title: z.string().min(1).max(240),
36
+ description: z.string().max(2_000).nullable(),
37
+ ownerId: IntelId,
38
+ currentVersionId: IntelId.nullable(),
39
+ createdAt: IsoDateTime,
40
+ updatedAt: IsoDateTime,
41
+ archivedAt: IsoDateTime.nullable(),
42
+ // The slash command this document is offered under, or `null` for the overwhelming majority that
43
+ // are not offered at all (#775).
44
+ //
45
+ // ⚠️ Required-and-nullable rather than optional, and every reader of a node pays one field for
46
+ // it. The alternative — filling it in only where a surface needs it — is the shape that answers
47
+ // `undefined` on the query which forgot its join, and then "not offered" and "nobody asked" are
48
+ // the same value. Only a `document` can carry one; the other five kinds are held to `null` by a
49
+ // CHECK in `0028`, not merely by habit.
50
+ promptName: PromptName.nullable(),
51
+ });
52
+ export type Node = z.infer<typeof Node>;
53
+
54
+ // What one table version carries (#135). An `append` holds only the rows one write added; a
55
+ // `snapshot` holds the complete table — header and every row — so reading starts at the newest
56
+ // snapshot and everything before it is history rather than content. Defining a table writes the
57
+ // first snapshot; updating, deleting, and redefining write the later ones. Documents and
58
+ // attachments carry `null`: each of their versions is complete by construction, and the word would
59
+ // say nothing about them.
60
+ export const NodeVersionSegment = z.enum(["append", "snapshot"]);
61
+ export type NodeVersionSegment = z.infer<typeof NodeVersionSegment>;
62
+
63
+ export const NodeVersion = z.strictObject({
64
+ id: IntelId,
65
+ nodeId: IntelId,
66
+ sequence: z.number().int().positive(),
67
+ contentKey: z.string().min(1),
68
+ mediaType: z.string().min(1).max(160),
69
+ contentHash: z.string().regex(/^[a-f0-9]{64}$/),
70
+ size: z.number().int().nonnegative(),
71
+ segment: NodeVersionSegment.nullable(),
72
+ createdBy: IntelId,
73
+ createdAt: IsoDateTime,
74
+ });
75
+ export type NodeVersion = z.infer<typeof NodeVersion>;
76
+
77
+ export const ListNodesInput = z.strictObject({
78
+ parentId: IntelId.nullable()
79
+ .default(null)
80
+ .describe("Folder to list the direct children of. `null` lists the top level."),
81
+ includeArchived: z
82
+ .boolean()
83
+ .default(false)
84
+ .describe("Include archived nodes beside the live ones instead of hiding them."),
85
+ // ⚠️ Overrides `parentId` rather than narrowing beside it: what is archived is asked for across
86
+ // the whole tree, because that is the only useful question. Somebody looking for what they threw
87
+ // away does not know which folder it was in — if they did, they would not be looking (#113).
88
+ // A separate flag rather than a third state on `includeArchived`, so no existing caller changes
89
+ // meaning — and `.optional()` rather than `.default(false)` for the same reason `ListFlowsInput`
90
+ // carries it that way: a default makes the field required in the PARSED type, and every existing
91
+ // caller would have to answer a question it is not asking.
92
+ archivedOnly: z
93
+ .boolean()
94
+ .optional()
95
+ .describe(
96
+ "List only archived nodes, across the whole tree. Overrides parentId rather than narrowing it: somebody looking for what they threw away does not know which folder it was in.",
97
+ ),
98
+ });
99
+ export type ListNodesInput = z.infer<typeof ListNodesInput>;
100
+
101
+ export const GetNodeInput = z.strictObject({
102
+ nodeId: IntelId.describe("Node to read, from node_list or search."),
103
+ });
104
+ export type GetNodeInput = z.infer<typeof GetNodeInput>;
105
+
106
+ /**
107
+ * An attachment, with the option of its bytes.
108
+ *
109
+ * ⚠️ One tool rather than a second one beside `node_attachment_get`, because the grammar in
110
+ * `packages/intel/CLAUDE.md` gives one verb one meaning: `get` reads one, and metadata and bytes are
111
+ * two halves of the same one. It mirrors HTTP, where `GET /nodes/:nodeId/attachment` and its
112
+ * `/meta` sibling have always been the same tool.
113
+ *
114
+ * ⚠️ The default is `false` on purpose. The bytes travel as base64 in the answer, so asking for
115
+ * them by accident would put a whole file into a model's context; a caller that wants the file says
116
+ * so. `SaveAttachmentInput` has carried bytes the other way since the beginning — this closes the
117
+ * asymmetry that let a model upload a PDF it could never read back (#773).
118
+ */
119
+ export const GetAttachmentInput = z.strictObject({
120
+ nodeId: IntelId.describe("Attachment node to read, from node_list or search."),
121
+ includeContent: z
122
+ .boolean()
123
+ .default(false)
124
+ .describe(
125
+ "Return the file itself beside its metadata. Default false answers metadata only. Files above 10 MB are refused here and take the HTTP door, which streams.",
126
+ ),
127
+ });
128
+ export type GetAttachmentInput = z.infer<typeof GetAttachmentInput>;
129
+
130
+ // One pinned version of one node (#147). Both IDs, always: a version ID alone would let anyone
131
+ // holding an ID read content whose node-level ACL they never passed, and a citation names both.
132
+ export const GetNodeVersionInput = z.strictObject({
133
+ nodeId: IntelId.describe(
134
+ "Node the version belongs to. Required even though the version id is unique: access is decided on the node.",
135
+ ),
136
+ versionId: IntelId.describe("Version to read, from node_version_list or a search citation."),
137
+ });
138
+ export type GetNodeVersionInput = z.infer<typeof GetNodeVersionInput>;
139
+
140
+ export const CreateNodeInput = z.strictObject({
141
+ parentId: IntelId.nullable()
142
+ .default(null)
143
+ .describe("Folder to file the new node in. `null` puts it at the top level."),
144
+ kind: NodeKind.describe(
145
+ "What the node is. A folder and a board hold others and have no content of their own; document, task, attachment and table each carry their own body, written afterwards by node_version_create (document and task), node_attachment_create or node_table_create. A task created under a board lands on that board.",
146
+ ),
147
+ title: z
148
+ .string()
149
+ .trim()
150
+ .min(1)
151
+ .max(240)
152
+ .describe(
153
+ "What the node is called. Shown in the tree and searched. ⚠️ `index` inside a folder is reserved: a document of that name (`index`, `Index` or `index.md`) is the TEXT OF THAT FOLDER, and a browser shows it in the folder's details rather than as a row under it. Naming a document that for another reason changes how its folder reads.",
154
+ ),
155
+ description: z
156
+ .string()
157
+ .trim()
158
+ .max(2_000)
159
+ .nullable()
160
+ .default(null)
161
+ .describe("Optional sentence about the node, for readers rather than for search ranking."),
162
+ idempotencyKey: IdempotencyKey,
163
+ });
164
+ export type CreateNodeInput = z.infer<typeof CreateNodeInput>;
165
+
166
+ export const SaveNodeVersionInput = z.strictObject({
167
+ nodeId: IntelId.describe(
168
+ "Document or board card to append a version to. A card's text is a node version like any other, so this is how a ticket gets its context, steps and definition of done instead of a one-line description. Folders, boards, attachments and tables carry their content another way and refuse.",
169
+ ),
170
+ baseVersionId: baseVersion(
171
+ "baseVersionId takes the `currentVersionId` from node_get, or null when the node has no version yet — and it has to be sent.",
172
+ ).describe(
173
+ "The version this edit was made against — `currentVersionId` from node_get. If the node has moved on since, the call is refused rather than overwriting the newer version; pass `null` only for a node that has no version yet.",
174
+ ),
175
+ content: z
176
+ .string()
177
+ .max(10_000_000)
178
+ .describe("The complete new content. Versions are whole documents, not patches."),
179
+ mediaType: z
180
+ .string()
181
+ .min(1)
182
+ .max(160)
183
+ .default("text/markdown")
184
+ .describe("Media type of `content`. Leave at text/markdown unless writing another format."),
185
+ idempotencyKey: IdempotencyKey,
186
+ });
187
+ export type SaveNodeVersionInput = z.infer<typeof SaveNodeVersionInput>;
188
+
189
+ export const SaveAttachmentInput = z.strictObject({
190
+ nodeId: IntelId.describe("Attachment node to append bytes to."),
191
+ baseVersionId: baseVersion(
192
+ "baseVersionId takes the `currentVersionId` from node_get, or null for the first upload — and it has to be sent.",
193
+ ).describe(
194
+ "The version these bytes replace — `currentVersionId` from node_get, or `null` for the first upload. A stale value is refused rather than overwriting.",
195
+ ),
196
+ contentBase64: z
197
+ .string()
198
+ .min(1)
199
+ .max(20_000_000)
200
+ .describe(
201
+ "The file, base64-encoded — around 15 MB of bytes at most. Encoding grows a file by a third and this door holds the whole of it in memory, encoded and decoded at once. A bigger file goes to `POST /nodes/:nodeId/attachment/bytes`, whose body IS the file and which streams it to storage.",
202
+ ),
203
+ mediaType: z
204
+ .string()
205
+ .min(1)
206
+ .max(160)
207
+ .describe("Media type of the decoded bytes, e.g. application/pdf."),
208
+ idempotencyKey: IdempotencyKey,
209
+ });
210
+ export type SaveAttachmentInput = z.infer<typeof SaveAttachmentInput>;
211
+
212
+ /**
213
+ * How many bytes an attachment may carry through the streaming door (#821).
214
+ *
215
+ * ⚠️ **This is our number, not the platform's.** A Worker refuses a request body over 100 MB on the
216
+ * Free and Pro plans (200 MB on Business, 500 MB on Enterprise by default), and the isolate that
217
+ * receives it has 128 MB of memory for everything it does at once. 50 MB is the presentation and
218
+ * PDF size this was raised for, and it leaves the hard ceiling a factor of two away — which is what
219
+ * makes a file over the limit a sentence the reader gets rather than a connection that drops.
220
+ *
221
+ * ⚠️ It is not the limit of the base64 door beside it. That one stays where it was, because there
222
+ * the file exists twice in memory before a single byte is stored; `contentBase64` says so.
223
+ */
224
+ /**
225
+ * The name a document takes to become the TEXT OF ITS FOLDER (#824).
226
+ *
227
+ * ⚠️ It is a convention that already existed: nearly every folder in this installation holds a
228
+ * document called `index` describing what is in it, and every person had to be told so. A surface
229
+ * that serves the convention makes the telling unnecessary — and nothing about the model changes:
230
+ * the document stays a document, in full text and in the vectors, with its versions, without a
231
+ * length limit and with version-pinned citations.
232
+ *
233
+ * ⚠️ **The comparison is case-insensitive, and `index.md` counts too.** The three spellings the
234
+ * decision names — `index`, `Index`, `index.md` — are all covered by it, and so are the ones nobody
235
+ * thought of. A list of exact spellings would make `INDEX` a different document from `index` for no
236
+ * reason a reader could guess.
237
+ *
238
+ * ⚠️ It lives in the contract because three surfaces need the same answer: the browser hides the
239
+ * row, the MCP tool description explains the name to a model, and neither may disagree with the
240
+ * other about what counts.
241
+ */
242
+ export function isFolderIndexTitle(title: string): boolean {
243
+ const name = title.trim().toLowerCase();
244
+ return name === "index" || name === "index.md";
245
+ }
246
+
247
+ export const MaxAttachmentBytes = 50_000_000;
248
+
249
+ /**
250
+ * The largest attachment whose CONTENTS are read and converted for the index (#821).
251
+ *
252
+ * ⚠️ A bigger file is stored, opened and downloaded exactly like any other; what it does not get is
253
+ * a search over its text. The indexing pass reads an attachment into memory whole and hands it to
254
+ * the converter as a blob beside it, so at 50 MB it would meet the isolate's 128 MB — and an upload
255
+ * that succeeds and then dies while being indexed is the worst of both: the file is filed and
256
+ * cannot be found.
257
+ *
258
+ * ⚠️ The number is 15 MB because that is what the old upload limit was. Every file that could be
259
+ * uploaded before this ticket therefore behaves exactly as it did; only the sizes that were
260
+ * impossible until now fall into the new case. Raising it is a measurement, not a decision to take
261
+ * in passing.
262
+ */
263
+ export const MaxIndexedAttachmentBytes = 15_000_000;
264
+
265
+ /**
266
+ * The streaming door for one attachment: everything ABOUT the bytes, while the bytes are the
267
+ * request body (#821).
268
+ *
269
+ * ⚠️ It has no MCP twin, and that is the one place where "every capability has an MCP equivalent"
270
+ * is answered by the door beside it instead: MCP carries JSON, so a tool cannot hand over a body.
271
+ * `SaveAttachmentInput` IS the MCP form of this capability, and the size is what separates them.
272
+ *
273
+ * ⚠️ `mediaType` travels as the request's own `content-type`, and `size` as its `content-length`.
274
+ * Both are read from the headers rather than invented here, because the body they describe is the
275
+ * body being sent; a second, hand-written statement about the same bytes is a second truth.
276
+ */
277
+ export const SaveAttachmentBytesInput = z.strictObject({
278
+ nodeId: IntelId.describe("Attachment node to append bytes to."),
279
+ baseVersionId: baseVersion(
280
+ "baseVersionId takes the `currentVersionId` from node_get, or null for the first upload — and it has to be sent.",
281
+ ),
282
+ mediaType: z.string().min(1).max(160),
283
+ size: z
284
+ .number()
285
+ .int()
286
+ .positive()
287
+ .max(MaxAttachmentBytes)
288
+ .describe("Length of the body in bytes, as `content-length` announces it."),
289
+ idempotencyKey: IdempotencyKey,
290
+ });
291
+ export type SaveAttachmentBytesInput = z.infer<typeof SaveAttachmentBytesInput>;
292
+
293
+ export const UpdateNodeInput = z
294
+ .strictObject({
295
+ nodeId: IntelId.describe("Node to change."),
296
+ baseUpdatedAt: IsoDateTime.describe(
297
+ "`updatedAt` as node_get last reported it. A newer value on the server means somebody else changed the node first and the call is refused.",
298
+ ),
299
+ title: z
300
+ .string()
301
+ .trim()
302
+ .min(1)
303
+ .max(240)
304
+ .optional()
305
+ .describe("New title. Omit to leave it as it is."),
306
+ description: z
307
+ .string()
308
+ .trim()
309
+ .max(2_000)
310
+ .nullable()
311
+ .optional()
312
+ .describe("New description, or `null` to clear it. Omit to leave it as it is."),
313
+ parentId: IntelId.nullable()
314
+ .optional()
315
+ .describe(
316
+ "Folder to move the node into, or `null` for the top level. Omit to leave it where it is.",
317
+ ),
318
+ // ⚠️ Three states, and all three are used: a name offers the document, `null` withdraws it, and
319
+ // omitting the field leaves whatever is there. A `promptEnabled` boolean beside a name would
320
+ // have a fourth — on, without a name — and `prompts/list` could not answer it (#775).
321
+ promptName: PromptName.nullable()
322
+ .optional()
323
+ .describe(
324
+ "Offer this document as a slash command under this name, or `null` to stop offering it. Omit to leave it as it is. Only a `document` can be offered: a folder, table, attachment, board or task is refused rather than quietly ignored.",
325
+ ),
326
+ idempotencyKey: IdempotencyKey,
327
+ })
328
+ .refine(
329
+ (input) =>
330
+ input.title !== undefined ||
331
+ input.description !== undefined ||
332
+ input.parentId !== undefined ||
333
+ input.promptName !== undefined,
334
+ { message: "At least one change is required" },
335
+ );
336
+ export type UpdateNodeInput = z.infer<typeof UpdateNodeInput>;
337
+
338
+ export const ArchiveNodeInput = z.strictObject({
339
+ nodeId: IntelId.describe("Node to archive or restore."),
340
+ baseUpdatedAt: IsoDateTime.describe(
341
+ "`updatedAt` as node_get last reported it. A newer value on the server means somebody else changed the node first and the call is refused.",
342
+ ),
343
+ archived: z
344
+ .boolean()
345
+ .describe(
346
+ "`true` archives the node, `false` restores it. Archiving hides a node from listings and search; nothing is deleted and every version stays readable.",
347
+ ),
348
+ idempotencyKey: IdempotencyKey,
349
+ });
350
+ export type ArchiveNodeInput = z.infer<typeof ArchiveNodeInput>;
351
+
352
+ // ⚠️ The one input in this contract that describes an IRREVERSIBLE act (#457). Everything else
353
+ // called "delete" in Intel means `archived_at`.
354
+ //
355
+ // It carries no `baseUpdatedAt` and no `idempotencyKey`, and both absences are decisions:
356
+ // · There is nothing to conflict with. The node must already be archived — an archived node is
357
+ // not being edited by anybody, and a restore between the read and the call is caught by the
358
+ // statement itself rather than by a timestamp the caller had to carry.
359
+ // · A replay has nothing to replay. Deleting twice is deleting once and then a 404, which is the
360
+ // honest answer: the second caller really did not delete anything.
361
+ export const PurgeNodeInput = z.strictObject({
362
+ nodeId: IntelId.describe(
363
+ "Node to delete for good. It must already be archived — a living node has no path into nothing.",
364
+ ),
365
+ idempotencyKey: IdempotencyKey,
366
+ });
367
+ export type PurgeNodeInput = z.infer<typeof PurgeNodeInput>;
368
+
369
+ // The question `PurgeNodeInput` cannot be asked afterwards: what goes with it, while it is all
370
+ // still there to be counted. Described since #586, because the count reached MCP that day — and a
371
+ // surface that may delete without being able to look first is the state `destructive.md` calls a
372
+ // confirmation that cannot name what disappears.
373
+ export const PurgeNodePreviewInput = z.strictObject({
374
+ nodeId: IntelId.describe(
375
+ "Archived node a purge would delete. Answers how many items would go with it and how many documents link to it from outside, and changes nothing. A node that is not archived is refused here for the same reason node_purge refuses it.",
376
+ ),
377
+ });
378
+ export type PurgeNodePreviewInput = z.infer<typeof PurgeNodePreviewInput>;
379
+
380
+ /**
381
+ * ⚠️ **A count is not an answer before a deletion that cannot be undone** (D71, #733). `totalItems`
382
+ * alone reads as "82 items" for a folder holding somebody's whole memory, and `destructive.md` asks
383
+ * for the number to be named BEFORE the confirmation — one step more precise is the difference
384
+ * between a number and something a person can decide on.
385
+ *
386
+ * `items` is capped, and the cap is visible rather than silent: `totalItems` keeps counting past it,
387
+ * so a caller can always say "and 40 more". A list of ten thousand rows would be an answer nobody
388
+ * reads and a response nobody can render.
389
+ */
390
+ export const PurgeNodePreview = z.strictObject({
391
+ inboundLinks: z.number().int().nonnegative(),
392
+ totalItems: z.number().int().positive(),
393
+ // ⚠️ `flow` beside the node kinds, because a flow filed in the folder goes with it and is counted
394
+ // in `totalItems`. Naming everything except flows would leave out the one thing the cascade had to
395
+ // be widened for (#733).
396
+ items: z.array(
397
+ z.strictObject({
398
+ id: IntelId,
399
+ kind: z.union([NodeKind, z.literal("flow")]),
400
+ title: z.string(),
401
+ }),
402
+ ),
403
+ });
404
+ export type PurgeNodePreview = z.infer<typeof PurgeNodePreview>;
405
+
406
+ // The title travels back because after this call nothing can look it up any more — not the row, not
407
+ // a version, not the index. A caller that wants to say what it just deleted has this one chance.
408
+ export const PurgeNodeResult = z.strictObject({
409
+ purged: z.literal(true),
410
+ title: z.string(),
411
+ });
412
+ export type PurgeNodeResult = z.infer<typeof PurgeNodeResult>;
413
+
414
+ // ⚠️ `withChildren` belongs to the LEVEL, not to the node (#59). Whether something has children
415
+ // THIS reader may see is not a property of the thing — two readers get different answers. As a
416
+ // field on the node, every other place that returns a node would have to compute it as well or
417
+ // lie; as a list beside the entries it costs only the one answer that needs it.
418
+ export const NodeList = z.strictObject({
419
+ items: z.array(Node),
420
+ withChildren: z.array(IntelId).default([]),
421
+ });
422
+ export type NodeList = z.infer<typeof NodeList>;
423
+
424
+ export const NodeVersionList = z.strictObject({ items: z.array(NodeVersion) });
425
+ export type NodeVersionList = z.infer<typeof NodeVersionList>;
426
+
427
+ export const NodeDocument = z.strictObject({
428
+ node: Node,
429
+ version: NodeVersion.nullable(),
430
+ content: z.string().nullable(),
431
+ });
432
+ export type NodeDocument = z.infer<typeof NodeDocument>;
433
+
434
+ export const NodeAttachment = z.strictObject({
435
+ node: Node,
436
+ version: NodeVersion,
437
+ resourceUri: z.string().regex(/^intel:\/\/nodes\/[^/]+\/attachment$/),
438
+ });
439
+ export type NodeAttachment = z.infer<typeof NodeAttachment>;
440
+
441
+ // The table as a grid rather than as text: the server owns the one CSV reader, so no surface has to
442
+ // grow a second one that would disagree with it about quoting.
443
+ export const NodeTable = z.strictObject({
444
+ node: Node,
445
+ columns: z.array(z.string()),
446
+ rows: z.array(z.array(z.string())),
447
+ // The newest append, or `null` while the table has no header yet.
448
+ versionId: IntelId.nullable(),
449
+ });
450
+ export type NodeTable = z.infer<typeof NodeTable>;
451
+
452
+ export const NodeLinkRelation = z.enum(["references", "related", "depends_on", "implements"]);
453
+ export type NodeLinkRelation = z.infer<typeof NodeLinkRelation>;
454
+
455
+ // Where the link came from. `text` links are derived from a document's content and are rewritten
456
+ // whenever it is saved; `manual` links were made in the dialog #41 removed and are now history.
457
+ //
458
+ // ⚠️ This is provenance, never a second sort of relationship. Nothing offers the reader a choice
459
+ // between them, and nothing may start writing `manual` again — that would be the two ways of saying
460
+ // one thing that #41 exists to end. It exists so that saving a document cannot delete a link
461
+ // somebody made before there was another way to make one.
462
+ export const NodeLinkOrigin = z.enum(["text", "manual"]);
463
+ export type NodeLinkOrigin = z.infer<typeof NodeLinkOrigin>;
464
+
465
+ export const NodeLink = z.strictObject({
466
+ id: IntelId,
467
+ sourceNodeId: IntelId,
468
+ targetNodeId: IntelId,
469
+ relation: NodeLinkRelation,
470
+ origin: NodeLinkOrigin,
471
+ label: z.string().trim().min(1).max(120).nullable(),
472
+ createdBy: IntelId,
473
+ createdAt: IsoDateTime,
474
+ });
475
+ export type NodeLink = z.infer<typeof NodeLink>;
476
+
477
+ export const NodeLinkList = z.strictObject({ items: z.array(NodeLink) });
478
+ export type NodeLinkList = z.infer<typeof NodeLinkList>;
479
+
480
+ // The inline element a document link is, inside a BlockNote document (#41).
481
+ //
482
+ // ⚠️ The ID and nothing else. No title and no path travel with it: a stored title would go stale
483
+ // the moment the target is renamed, a stored path the moment it is moved — and either one would
484
+ // put a name the reader may not see into a document they may.
485
+ export const DocumentLinkInlineType = "documentLink";
486
+
487
+ export const ResolveNodeLinksInput = z.strictObject({
488
+ nodeIds: z
489
+ .array(IntelId)
490
+ .min(1)
491
+ .max(200)
492
+ .describe(
493
+ "The link targets to name, as found in a document's documentLink elements. Unreachable and deleted targets are simply absent from the answer, so a shorter list than asked for is normal.",
494
+ ),
495
+ });
496
+ export type ResolveNodeLinksInput = z.infer<typeof ResolveNodeLinksInput>;
497
+
498
+ export const ResolvedNodeLink = z.strictObject({
499
+ nodeId: IntelId,
500
+ title: z.string().min(1).max(240),
501
+ });
502
+ export type ResolvedNodeLink = z.infer<typeof ResolvedNodeLink>;
503
+
504
+ // ⚠️ Only what the asking reader may see is in here, and an unreachable target is simply absent —
505
+ // never a row with an empty title, never a count, never a "restricted" marker. The list is what one
506
+ // side of a document link is drawn from, and an entry that says "something is here" is exactly the
507
+ // leak this schema has to make impossible to write by accident. Deleted and unreadable therefore
508
+ // look identical from the outside, which is the point.
509
+ export const ResolveNodeLinksResult = z.strictObject({
510
+ items: z.array(ResolvedNodeLink),
511
+ });
512
+ export type ResolveNodeLinksResult = z.infer<typeof ResolveNodeLinksResult>;
513
+
514
+ export const NodeGraphInput = z.strictObject({
515
+ limit: z
516
+ .number()
517
+ .int()
518
+ .min(1)
519
+ .max(500)
520
+ .default(250)
521
+ .describe("How many nodes the graph may carry. The links returned are the ones between them."),
522
+ });
523
+ export type NodeGraphInput = z.infer<typeof NodeGraphInput>;
524
+
525
+ export const NodeGraph = z.strictObject({
526
+ nodes: z.array(Node),
527
+ links: z.array(NodeLink),
528
+ });
529
+ export type NodeGraph = z.infer<typeof NodeGraph>;
530
+
531
+ export const BlockNoteMediaType = "application/vnd.anchrd.intel.blocknote+json";
532
+ export const BlockNoteDocument = z.strictObject({
533
+ format: z.literal("blocknote"),
534
+ schemaVersion: z.literal(1),
535
+ blocks: z.array(z.record(z.string(), z.unknown())),
536
+ markdown: z.string(),
537
+ });
538
+ export type BlockNoteDocument = z.infer<typeof BlockNoteDocument>;
539
+
540
+ // `scopeId` is a cut, never a grant (#126): it narrows an answer the actor is already entitled to
541
+ // and can only ever remove rows. Without it the search stays global over everything visible, which
542
+ // is why it is optional rather than nullable — an absent field and `null` would otherwise be two
543
+ // spellings of the same request.
544
+ export const SearchInput = z.strictObject({
545
+ query: z
546
+ .string()
547
+ .trim()
548
+ .min(1)
549
+ .max(500)
550
+ .describe(
551
+ "What to look for, in the reader's own words. Matched both lexically and semantically, so a question works as well as keywords.",
552
+ ),
553
+ limit: z
554
+ .number()
555
+ .int()
556
+ .min(1)
557
+ .max(50)
558
+ .default(10)
559
+ .describe("How many citations to return, best first."),
560
+ scopeId: IntelId.optional().describe(
561
+ "Optional folder node id. When given, only nodes filed in that folder or beneath it are searched.",
562
+ ),
563
+ });
564
+ export type SearchInput = z.infer<typeof SearchInput>;
565
+
566
+ export const NodeCitation = z.strictObject({
567
+ nodeId: IntelId,
568
+ versionId: IntelId,
569
+ title: z.string(),
570
+ passage: z.string(),
571
+ source: z.string(),
572
+ freshness: IsoDateTime,
573
+ score: z.number().min(0).max(1),
574
+ match: z.enum(["lexical", "semantic", "hybrid"]),
575
+ });
576
+ export type NodeCitation = z.infer<typeof NodeCitation>;
577
+
578
+ export const SearchResult = z.strictObject({
579
+ items: z.array(NodeCitation),
580
+ });
581
+ export type SearchResult = z.infer<typeof SearchResult>;
582
+ export const ReindexResult = z.strictObject({ queued: z.number().int().nonnegative() });
583
+ export type ReindexResult = z.infer<typeof ReindexResult>;