@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,102 @@
1
+ # The Intel wire contract
2
+
3
+ The Zod schemas that describe every payload crossing Intel's HTTP, MCP and browser surfaces. They
4
+ travel inside [`@anchrd/intel`](https://www.npmjs.com/package/@anchrd/intel) at `src/contract`, both
5
+ as source and as built JavaScript under `dist/contract`.
6
+
7
+ ⚠️ **They carry no `exports` subpath of their own.** They used to be the separate package
8
+ `@anchrd/intel-contract`, which is being retired in favour of this package; nothing outside
9
+ `@anchrd/intel` read
10
+ it, so nothing re-exports it now. If you write your own client, the file to read is the one for your
11
+ domain — `node.ts`, `table.ts`, `share.ts`, `tool.ts`, `flow.ts`, `flow-run.ts`, `bundle.ts`,
12
+ `audit.ts`, `board.ts`, `feed.ts` — with `contract.ts` holding only the shared primitives.
13
+
14
+ Mutation inputs are strict: an unknown field is rejected rather than silently dropped, so a typo in
15
+ a client never becomes a value that quietly fails to apply. Types are inferred from the schemas, so
16
+ `z.infer` gives you the same shape the server validates against.
17
+
18
+ ## Six things that cost a first-time caller an afternoon
19
+
20
+ Every one of these was found by somebody writing against the real API and guessing.
21
+
22
+ ### A refusal hides in one of two fields, never both
23
+
24
+ Intel answers failures as RFC 9457 problem details, and **which field carries the sentence depends
25
+ on who refused**:
26
+
27
+ | Kind of refusal | `title` | `detail` |
28
+ |---|---|---|
29
+ | Intel itself — a permission, a conflict, an invalid flow graph | the actual reason | absent |
30
+ | Schema validation — a bad or missing field | `Request validation failed` | the actual reason, field by field |
31
+
32
+ A client that logs only `detail` sees nothing for the first kind; one that logs only `title` sees
33
+ `Request validation failed` and nothing about which field. **Read both.** `code` is the stable
34
+ machine-readable half and is always present.
35
+
36
+ ### `baseVersionId` takes `null`, and it is still required
37
+
38
+ Fields declared `nullable()` and not `optional()` are **required**: `null` is a value they take, and
39
+ leaving them out is refused. That is deliberate everywhere a write names the version it was made
40
+ against — `SaveNodeVersionInput`, `SaveAttachmentInput`, `SaveFlowVersionInput`. `null` is the only
41
+ way to write the first content of a node that has none, and the field stays required so a caller who
42
+ simply forgot it is refused instead of overwriting whatever somebody else wrote in between.
43
+
44
+ Neither `undefined` nor `""` works. Send the `currentVersionId` from `node_get`, or `null`.
45
+
46
+ ### `mediaType` decides whether the browser can open what you wrote
47
+
48
+ `SaveNodeVersionInput.mediaType` defaults to `text/markdown`, and for plain text that is right. It is
49
+ **wrong for anything the node editor should be able to open**: the editor stores and reads
50
+ `BlockNoteMediaType` (`application/vnd.anchrd.intel.blocknote+json`) with a `BlockNoteDocument` body.
51
+
52
+ Content in any other shape is stored intact, indexed and searchable — and the editor opens **empty**
53
+ on it, because it cannot parse it. Nothing reports an error, and a save from that empty editor
54
+ replaces what was written. If a person is meant to edit the document afterwards, write it as a
55
+ `BlockNoteDocument`:
56
+
57
+ ```ts
58
+ // from node.ts: BlockNoteDocument, BlockNoteMediaType
59
+
60
+ const content = JSON.stringify(
61
+ BlockNoteDocument.parse({ format: "blocknote", schemaVersion: 1, blocks, markdown }),
62
+ );
63
+ // … then save with mediaType: BlockNoteMediaType
64
+ ```
65
+
66
+ ### In a flow graph, a tool is a link, not a step
67
+
68
+ Flow nodes sit in three layers. `trigger` and `output` are markers, `instruction`, `condition` and
69
+ `subflow` are steps — and `folder`, `document`, `upload`, `table` and `tool` are **links**.
70
+
71
+ A link never stands in the chain of work. It hangs off an `instruction` or a `condition` — not off a
72
+ marker and not off a `subflow` — and the edge that attaches it must carry `kind: "context"`:
73
+
74
+ ```jsonc
75
+ { "source": "<instruction id>", "target": "<tool id>", "kind": "context" }
76
+ ```
77
+
78
+ ⚠️ `FlowEdge.kind` **defaults to `"flow"`**, so an edge written without it becomes an order-of-work
79
+ edge and the tool ends up in the chain. The graph is then refused with
80
+ `Node <id> is a link and must be attached to a step` — in `title`, per the first item above. A step
81
+ may hold several links; one link belongs to exactly one step.
82
+
83
+ ### A flow is not a node, and has its own grant calls
84
+
85
+ `node_grant_create` addresses nodes; `flow_grant_create` addresses flows. A flow is reached by both:
86
+ the folder it is filed in passes its grants down, and a grant may sit on the flow itself. The two
87
+ add up — the flow grant never replaces what the folder passes down.
88
+
89
+ ⚠️ A grant on one flow reaches **that flow** and nothing else — not the folder, and not the flows it
90
+ calls. `ShareResult` says so: `unrunnable` names the sub-flows the new principal could not start,
91
+ by title where you may see them and by count where you may not.
92
+
93
+ ### Import takes its idempotency key as a header
94
+
95
+ Every mutation in this contract carries an `idempotencyKey` field. Bundle import is the one
96
+ exception, because its **body is the zip**: the key travels as the `idempotency-key` HTTP header
97
+ instead, and a request without it is refused.
98
+
99
+ ## License
100
+
101
+ Proprietary. Published publicly for installation convenience; this is not an open-source license and
102
+ grants no right to use, copy, modify or redistribute the software. Contact Anchrd for licensing.
@@ -0,0 +1,85 @@
1
+ import { z } from "zod";
2
+ import { IntelId, IsoDateTime } from "./contract.ts";
3
+
4
+ // Which kind of resource an event is about — all three the column carries, since #774.
5
+ //
6
+ // ⚠️ It was `["node"]` alone until then, and that was a decision rather than a gap (#620): each
7
+ // kind needs a DIFFERENT visibility check, and asking for one that had none was REFUSED rather than
8
+ // answered with an empty list. An empty list reads as "nothing happened", and a consumer built on
9
+ // that would miss every flow event without ever learning they exist.
10
+ //
11
+ // What changed is that the other two checks now exist. `flow` walks the folder a flow hangs in
12
+ // (`flowInSubtree`), and `flow-run` walks one join further — run → flow → folder — which #767 built
13
+ // for the feed. **Widening the enum without those walks would have been the silent answer the
14
+ // original decision refused**, so the order matters: the check first, the enum second.
15
+ export const AuditResourceType = z.enum(["node", "flow", "flow-run"]);
16
+ export type AuditResourceType = z.infer<typeof AuditResourceType>;
17
+
18
+ // One line of the change journal. `resourceId` is the node the event is about; `actorId` is who
19
+ // caused it. `metadata` is whatever the write site recorded — its shape belongs to `action` and is
20
+ // deliberately not typed here, because a schema per action would have to be kept in step with 11
21
+ // write sites and would go stale in silence.
22
+ export const AuditEvent = z.strictObject({
23
+ id: IntelId,
24
+ actorId: z.string().min(1),
25
+ action: z.string().min(1),
26
+ resourceType: AuditResourceType,
27
+ resourceId: IntelId,
28
+ metadata: z.record(z.string(), z.unknown()),
29
+ occurredAt: IsoDateTime,
30
+ });
31
+ export type AuditEvent = z.infer<typeof AuditEvent>;
32
+
33
+ // ⚠️ Opaque BY CONTRACT, not merely by encoding. The reader gets a string back and hands the same
34
+ // string in again; it must not take it apart, and it must not build one.
35
+ //
36
+ // The reason is that the cursor is a PAIR — `(occurredAt, id)` — and the pair is the whole point.
37
+ // A timestamp alone cannot separate two events written in the same millisecond, which for a batch
38
+ // is the normal case rather than the exception; a reader continuing on `occurredAt > X` skips the
39
+ // second one silently. If the cursor were two fields, a caller would eventually send only the
40
+ // timestamp, and the loss would look like nothing at all: no error, no log, just an event that
41
+ // never arrived.
42
+ export const AuditCursor = z.string().min(1).max(400);
43
+
44
+ export const AuditListRequest = z.strictObject({
45
+ resourceType: AuditResourceType.describe(
46
+ "Which kind of resource to read events about: `node` for documents, folders, tables and attachments, `flow` for changes to a flow itself, `flow-run` for its runs. One kind per call — the three are separate journals with separate cursors.",
47
+ ),
48
+ after: AuditCursor.optional().describe(
49
+ "Where to continue: the `nextCursor` of a previous answer, passed back unchanged. Omit it to start at the oldest event you may see. Do not build one — it is opaque on purpose.",
50
+ ),
51
+ limit: z
52
+ .number()
53
+ .int()
54
+ .min(1)
55
+ .max(200)
56
+ .default(50)
57
+ .describe("How many events to return at most. The answer may be shorter."),
58
+ });
59
+ export type AuditListRequest = z.infer<typeof AuditListRequest>;
60
+
61
+ // `nextCursor` is the cursor of the LAST returned row — a position, not a page number. A reader
62
+ // stores it and sends it back as `after`.
63
+ //
64
+ // ⚠️ **It answers WHERE THE READER GOT TO, never whether more is waiting.** Those are two questions,
65
+ // and for a while one field answered both: the cursor was omitted whenever no further page followed,
66
+ // so a reader that was catching up got fifty events and no position, started over on its next run,
67
+ // and would have delivered the same fifty for ever. Measured against the running installation
68
+ // (`#793`); a reader paging through never notices, because it always asks for a full page.
69
+ //
70
+ // ⚠️ **`hasMore` is the other question, and it is a MAY, not a promise.** The journal is a live table
71
+ // and the rows a reader may see can shrink between calls. Treating `hasMore` as "there is data" is a
72
+ // fair reading; treating an empty answer as "the feed ended" is not.
73
+ export const AuditListResponse = z.strictObject({
74
+ events: z.array(AuditEvent),
75
+ // `null` only when nothing was returned: no rows, no position.
76
+ nextCursor: AuditCursor.nullable(),
77
+ /**
78
+ * ⚠️ **Added rather than repurposed** (`#793`). A reader written against the old shape reads
79
+ * "cursor present" as "more may follow"; it now sees a cursor on the last page as well and asks
80
+ * once more, which answers empty. That costs one request and loses nothing — whereas changing what
81
+ * `nextCursor` MEANS would have made those readers skip.
82
+ */
83
+ hasMore: z.boolean(),
84
+ });
85
+ export type AuditListResponse = z.infer<typeof AuditListResponse>;
@@ -0,0 +1,306 @@
1
+ import { z } from "zod";
2
+ import { IdempotencyKey, IntelId, IsoDateTime } from "./contract.ts";
3
+
4
+ // One column of a board. `terminal` is what "done" means here — a rule about the column rather than
5
+ // a magic status name, so an installation may call it "Shipped" or "Abgerechnet" without anything
6
+ // downstream having to know the word.
7
+ /**
8
+ * The id of the archive column, on every board (D68, #674).
9
+ *
10
+ * ⚠️ **It is derived, never stored.** A card sits in it because `nodes.archived_at` is set, and
11
+ * `board_tasks.status` keeps naming the working column it came from — that is what lets a card
12
+ * pulled back out land where it was. A configured column may not claim this id; `board_update`
13
+ * refuses it, because two columns with one id means every card in them lands in whichever the view
14
+ * draws first.
15
+ */
16
+ export const ARCHIVE_COLUMN_ID = "archived";
17
+
18
+ /**
19
+ * What a colour picker starts on for a column that has none (#725).
20
+ *
21
+ * ⚠️ **Here rather than in the browser package, and that is not tidiness.** The token linter refuses
22
+ * a hard-coded hex in `packages/intel/ui`, and it is right to: a colour written into a component bypasses
23
+ * the theme. This one is not a theme colour, it is DATA — the value a person is about to choose
24
+ * from and the api writes as a default — so it belongs where both sides may read it, which is here.
25
+ *
26
+ * ⚠️ **It is the widget's starting point, never a written value.** `color` stays absent until
27
+ * somebody picks one, so opening the dialog does not repaint a board that predates colours.
28
+ */
29
+ export const DEFAULT_COLUMN_COLOR = "#9ca3af";
30
+
31
+ export const BoardColumn = z.strictObject({
32
+ id: z.string().min(1).max(60).describe("Stable key stored on every task in this column."),
33
+ title: z.string().min(1).max(80).describe("What the column is called on screen."),
34
+ terminal: z
35
+ .boolean()
36
+ .default(false)
37
+ .describe("Whether a task in this column counts as finished. More than one column may be."),
38
+ /**
39
+ * ⚠️ **The colour belongs to the COLUMN, never to the card** (#725, and #365 took the status dot
40
+ * off the card on purpose). A card is in a column; drawing the column's colour on it would be the
41
+ * same fact twice, and the second copy is the one that goes stale.
42
+ *
43
+ * A free value rather than a fixed palette (Jack's decision 2026-08-22). It only ever fills a
44
+ * DOT, so a badly chosen one is hard to see but makes nothing unreadable — which is what makes a
45
+ * free choice affordable here and would not make it affordable behind text.
46
+ */
47
+ color: z
48
+ .string()
49
+ .regex(/^#[0-9a-fA-F]{6}$/, "A colour is six hex digits behind a hash, like #6b7280.")
50
+ .optional()
51
+ .describe(
52
+ "The dot in front of this column's title, and in front of a linked task that sits in it. Six hex digits behind a hash. Absent means the reader's own default, so an older board is not repainted by this field appearing.",
53
+ ),
54
+ });
55
+ export type BoardColumn = z.infer<typeof BoardColumn>;
56
+
57
+ // One card, as the board view needs it: everything to draw and sort by, and NOTHING from R2. The
58
+ // body is fetched per task through `node_version_get` when somebody opens one.
59
+ //
60
+ // ⚠️ `position` is a float, not an index. Dropping a card between two neighbours is then the
61
+ // midpoint of the two, and no other row is written. With integers every drop would renumber
62
+ // everything below it, and two people dropping at once would fight over rows neither touched.
63
+ export const BoardTask = z.strictObject({
64
+ id: IntelId.describe("The card's node id — its address everywhere else in Intel."),
65
+ title: z.string().min(1).max(240).describe("What the card says."),
66
+ status: z.string().min(1).max(60).describe("Which column it sits in."),
67
+ assigneeId: z.string().min(1).nullable(),
68
+ labels: z.array(z.string().min(1).max(40)),
69
+ startDate: IsoDateTime.nullable(),
70
+ dueDate: IsoDateTime.nullable(),
71
+ dependsOn: IntelId.nullable(),
72
+ /**
73
+ * The task this one sits under, or `null` when it sits directly on the board.
74
+ *
75
+ * ⚠️ **The hierarchy is the node tree** (`nodes.parent_id`), not a second column here — a task
76
+ * under a task IS the tree Intel already has, which is why the cycle guard that refuses moving a
77
+ * node into its own descendant covers this for free.
78
+ *
79
+ * ⚠️ **Under a filter this may name a task that is not in the same answer.** `board_get` returns
80
+ * what matches; a subtask can match while its parent does not. A reader that assumes the parent
81
+ * is present will lose the row — treat an unknown parent as top level.
82
+ */
83
+ parentTaskId: IntelId.nullable(),
84
+ position: z.number(),
85
+ archivedAt: IsoDateTime.nullable(),
86
+ });
87
+ export type BoardTask = z.infer<typeof BoardTask>;
88
+
89
+ // ⚠️ Every filter here is a COLUMN in `board_tasks`, and that is the whole reason the fields are not
90
+ // in the body. A filter the application applies after reading has already fetched the rows — over
91
+ // the wire, into memory, past the point where leaving them out would have helped.
92
+ export const BoardTaskFilter = z.strictObject({
93
+ status: z
94
+ .string()
95
+ .min(1)
96
+ .max(60)
97
+ .optional()
98
+ .describe("Only tasks in this column. Omit for every column."),
99
+ assigneeId: z
100
+ .string()
101
+ .min(1)
102
+ .optional()
103
+ .describe("Only tasks assigned to this Gate principal. An agent is not a special case here."),
104
+ dueBefore: IsoDateTime.optional().describe(
105
+ "Only tasks due strictly before this moment. Tasks without a due date never match.",
106
+ ),
107
+ dependsOn: IntelId.optional().describe(
108
+ "Only tasks waiting for this node — what `board_get` answers when you ask what one thing blocks.",
109
+ ),
110
+ includeArchived: z
111
+ .boolean()
112
+ .default(false)
113
+ .describe("Include archived tasks beside the live ones instead of hiding them."),
114
+ });
115
+ export type BoardTaskFilter = z.infer<typeof BoardTaskFilter>;
116
+
117
+ export const BoardGetInput = BoardTaskFilter.extend({
118
+ boardId: IntelId.describe("The board node to read. Must be a node of kind `board`."),
119
+ });
120
+ export type BoardGetInput = z.infer<typeof BoardGetInput>;
121
+
122
+ // One answer for the whole board, not one per card.
123
+ export const BoardView = z.strictObject({
124
+ boardId: IntelId,
125
+ title: z.string().min(1).max(240),
126
+ columns: z.array(BoardColumn),
127
+ /**
128
+ * Whether the archive column is drawn (D68, #674).
129
+ *
130
+ * ⚠️ It travels even when it is `false`, because the settings dialog needs to draw the switch in
131
+ * both positions — and because "the column is not in `columns`" has two possible reasons, hidden
132
+ * and not-yet-supported, which a reader cannot tell apart from the list alone.
133
+ */
134
+ archiveVisible: z.boolean(),
135
+ tasks: z.array(BoardTask),
136
+ });
137
+ export type BoardView = z.infer<typeof BoardView>;
138
+
139
+ export const BoardUpdateInput = z.strictObject({
140
+ boardId: IntelId.describe("The board node whose columns are being set."),
141
+ columns: z
142
+ .array(BoardColumn)
143
+ .min(1)
144
+ .describe(
145
+ `The complete new column list, in order. Columns are replaced, not merged. The archive column (\`${ARCHIVE_COLUMN_ID}\`) is not part of this list and cannot be named in it — it is on every board and is only shown or hidden.`,
146
+ ),
147
+ // ⚠️ Shown or hidden, never removed. The archive is the same place on every board; a shelf that
148
+ // could be dragged between the working columns would be a shelf pretending to be a stage.
149
+ archiveVisible: z
150
+ .boolean()
151
+ .optional()
152
+ .describe("Whether the archive column is drawn. Omit to leave it as it is."),
153
+ idempotencyKey: IdempotencyKey,
154
+ });
155
+ export type BoardUpdateInput = z.infer<typeof BoardUpdateInput>;
156
+
157
+ export const BoardTaskCreateInput = z.strictObject({
158
+ boardId: IntelId.describe("The board the task is filed under. It becomes the task's parent."),
159
+ title: z.string().min(1).max(240).describe("What the card says on the board."),
160
+ // ⚠️ Optional, and absent means the FIRST column rather than an error. A caller who does not care
161
+ // where a task starts should not have to read the board first — and a task with no status would
162
+ // be a card no view can draw.
163
+ status: z
164
+ .string()
165
+ .min(1)
166
+ .max(60)
167
+ .optional()
168
+ .describe("Which column to file it in. Omit for the board's first column."),
169
+ assigneeId: z
170
+ .string()
171
+ .min(1)
172
+ .nullable()
173
+ .default(null)
174
+ .describe("Who it is for, as a Gate principal id. An agent is not a special case."),
175
+ labels: z
176
+ .array(z.string().min(1).max(40))
177
+ .default([])
178
+ .describe("Free-form tags. They are filtered on the board, not in search."),
179
+ startDate: IsoDateTime.nullable()
180
+ .default(null)
181
+ .describe("When work on it should begin. Only the timeline view draws it."),
182
+ dueDate: IsoDateTime.nullable()
183
+ .default(null)
184
+ .describe("When it is due. A card without one never matches a due-before filter."),
185
+ dependsOn: IntelId.nullable()
186
+ .default(null)
187
+ .describe("A node this task waits for. Any node, not only another task."),
188
+ // ⚠️ Creating a subtask is ONE call; moving an existing task under another is `node_update`
189
+ // with a new `parentId`. Two paths for "where a task sits" would mean two cycle guards, and the
190
+ // one on the node path is the one that already exists.
191
+ parentTaskId: IntelId.nullable()
192
+ .default(null)
193
+ .describe(
194
+ "The task this one belongs under. It must be on the same board. Omit for a card that sits directly on the board.",
195
+ ),
196
+ idempotencyKey: IdempotencyKey,
197
+ });
198
+ export type BoardTaskCreateInput = z.infer<typeof BoardTaskCreateInput>;
199
+
200
+ // ⚠️ Moving a card IS this call: `status` and `position` together. There is deliberately no
201
+ // `board_task_move` — it would be a second way to write one row, and a model reading `tools/list`
202
+ // would have to guess which of the two applies.
203
+ //
204
+ // Every field is optional and absent means "leave it": a drag sends two fields, a rename sends one.
205
+ // `null` is a value where the column is nullable, so clearing a due date is `dueDate: null` and not
206
+ // its absence.
207
+ export const BoardTaskUpdateInput = z.strictObject({
208
+ taskId: IntelId.describe("The card to change, by its node id."),
209
+ title: z
210
+ .string()
211
+ .min(1)
212
+ .max(240)
213
+ .optional()
214
+ .describe("Refused on purpose: a card's title lives on the node. Rename it with node_update."),
215
+ status: z
216
+ .string()
217
+ .min(1)
218
+ .max(60)
219
+ .optional()
220
+ .describe(
221
+ `The column to move it to. \`${ARCHIVE_COLUMN_ID}\` is not an ordinary column: it archives the card through the same path as \`node_archive\`, with the same refusals — a card that still has live subtasks is refused. Moving it to any other column while it is archived brings it back.`,
222
+ ),
223
+ position: z
224
+ .number()
225
+ .optional()
226
+ .describe("Where in the column. Use the midpoint between the two neighbours it lands between."),
227
+ assigneeId: z
228
+ .string()
229
+ .min(1)
230
+ .nullable()
231
+ .optional()
232
+ .describe("Who it is for. `null` unassigns it; leaving it out keeps whoever has it."),
233
+ labels: z
234
+ .array(z.string().min(1).max(40))
235
+ .optional()
236
+ .describe("The complete new tag list. Tags are replaced, not merged."),
237
+ startDate: IsoDateTime.nullable()
238
+ .optional()
239
+ .describe("When work should begin. `null` clears it."),
240
+ dueDate: IsoDateTime.nullable().optional().describe("When it is due. `null` clears it."),
241
+ dependsOn: IntelId.nullable()
242
+ .optional()
243
+ .describe("A node this card waits for. `null` clears the dependency."),
244
+ idempotencyKey: IdempotencyKey,
245
+ });
246
+ export type BoardTaskUpdateInput = z.infer<typeof BoardTaskUpdateInput>;
247
+
248
+ // ── Who a card can be given to ────────────────────────────────────────────────────────────────
249
+ // D70 (`~/Dev/anchrd/core/DECISIONS.md`): the people offered here are the ones who can actually open
250
+ // THIS board, never everyone who holds an account. The narrower answer is the point, and it is what
251
+ // makes the door safe to offer to anyone who can see the board.
252
+ //
253
+ // ⚠️ `email` travels because two colleagues share a first name more often than a picker can afford,
254
+ // and a list of indistinguishable "Anton" rows is not a picker. It is no wider than what gate hands
255
+ // any signed-in person at `POST /directory/search`; the narrowing this door adds is WHO appears,
256
+ // not WHAT is said about them.
257
+ //
258
+ // `isMachine` travels for the same reason gate carries it: whoever picks an assignee wants to know
259
+ // whether they are handing the card to a colleague or to an agent.
260
+ export const DirectoryPerson = z.strictObject({
261
+ id: IntelId,
262
+ name: z.string(),
263
+ email: z.email(),
264
+ isMachine: z.boolean(),
265
+ });
266
+ export type DirectoryPerson = z.infer<typeof DirectoryPerson>;
267
+
268
+ export const BoardAssigneeSearchInput = z.strictObject({
269
+ boardId: IntelId.describe(
270
+ "The board whose people to offer. Only those who can open this board appear, so the same query against two boards can give two different answers.",
271
+ ),
272
+ query: z
273
+ .string()
274
+ .describe(
275
+ "What was typed. EMPTY names up to three people who reach this board, so a picker can open with suggestions instead of nothing. One character answers empty rather than everybody: a single letter is a listing under a different name, while an empty query asks for a bounded set this board already knows.",
276
+ ),
277
+ });
278
+ export type BoardAssigneeSearchInput = z.infer<typeof BoardAssigneeSearchInput>;
279
+
280
+ // ⚠️ A DIFFERENT question from the one above, and deliberately not board-scoped. This one names
281
+ // people who are ALREADY recorded on a card, so that a circle can carry initials instead of a raw
282
+ // id (`#258`). Someone whose access was withdrawn, or whose account was switched off, still has to
283
+ // be nameable: a card that reads as unassigned is a worse answer than the truth.
284
+ export const DirectoryResolveInput = z.strictObject({
285
+ ids: z
286
+ .array(IntelId)
287
+ .min(1)
288
+ .max(100)
289
+ .describe(
290
+ "The ids to name. Ids that cannot be named are absent from the answer rather than reported, and more than a hundred is refused rather than silently shortened.",
291
+ ),
292
+ });
293
+ export type DirectoryResolveInput = z.infer<typeof DirectoryResolveInput>;
294
+
295
+ export const DirectoryPersonList = z.strictObject({
296
+ items: z.array(DirectoryPerson),
297
+ /**
298
+ * ⚠️ **How many are left over, so the cap is visible rather than silent** (#757). Three suggestions
299
+ * that look like the whole list are worse than three that say "and 4 more": the first teaches the
300
+ * reader that nobody else exists.
301
+ *
302
+ * `0` on a search answer, where the cap is gate's own and this door does not own it.
303
+ */
304
+ more: z.number().int().min(0).default(0),
305
+ });
306
+ export type DirectoryPersonList = z.infer<typeof DirectoryPersonList>;
@@ -0,0 +1,157 @@
1
+ import { z } from "zod";
2
+ import { IntelId, IsoDateTime } from "./contract.ts";
3
+
4
+ // ── Bundle export (#136) ────────────────────────────────────────────────────────────────────────
5
+
6
+ // The one name the importer looks for at the zip root. A different spelling would make a bundle a
7
+ // naked folder, so the constant lives in the contract rather than in each surface.
8
+ export const BundleManifestFilename = "manifest.json";
9
+
10
+ /**
11
+ * What a bundle entry can be. `flow` joins the node kinds because a flow shares the folder tree
12
+ * without being a node (ADR-0004), and the bundle mirrors the tree, not the tables.
13
+ *
14
+ * ⚠️ `agent` and `board` are STILL HERE, and that is the one place in this file where a value
15
+ * survives its feature (#390). A bundle is somebody else's file: an export written before Agents
16
+ * and Board were parked (#385) is a correct export, and it has to PARSE so the import can refuse it
17
+ * by name — with the entry, the kind and the branch the code is on. Take them out and the same
18
+ * bundle fails as `unexpected enum value`, which sends its holder looking for a broken file that is
19
+ * not broken.
20
+ *
21
+ * They belong to the wire format of a file that already exists, not to the product. Nothing may
22
+ * create either kind; `NodeKind` is the enum that says so.
23
+ */
24
+ export const BundleEntryKind = z.enum([
25
+ "folder",
26
+ "document",
27
+ "table",
28
+ "attachment",
29
+ "agent",
30
+ "board",
31
+ "flow",
32
+ ]);
33
+ export type BundleEntryKind = z.infer<typeof BundleEntryKind>;
34
+
35
+ /**
36
+ * Why an entry carries no bytes although its kind normally does (#534).
37
+ *
38
+ * `no-content` is a node that has never been given any: a table created but never defined has no
39
+ * version, and an attachment created but never uploaded has none either, so there is nothing to
40
+ * write. That is a different sentence from "the content is gone", which the export refuses as
41
+ * `content_missing` — and a file of zero bytes cannot say which of the two it is, which is exactly
42
+ * what it used to do.
43
+ */
44
+ export const BundleEntryAbsence = z.enum(["no-content"]);
45
+ export type BundleEntryAbsence = z.infer<typeof BundleEntryAbsence>;
46
+
47
+ // One entry of the manifest: the identity a re-import needs, next to the relative path where the
48
+ // bytes sit in the zip. A folder carries no media type — it has no bytes.
49
+ export const BundleManifestEntry = z
50
+ .strictObject({
51
+ id: IntelId,
52
+ kind: BundleEntryKind,
53
+ title: z.string().min(1).max(240),
54
+ description: z.string().max(2_000).nullable(),
55
+ mediaType: z.string().min(1).max(160).nullable(),
56
+ // Relative to the zip root, forward slashes, no leading slash. Folders end with a slash so an
57
+ // empty folder still has an address. An entry with an `absence` keeps its path even though no
58
+ // file sits there: the path is what puts the node in the tree on the way back in.
59
+ path: z.string().min(1).max(4_000),
60
+ // ⚠️ OPTIONAL rather than nullable, and that is about files that already exist. This object is
61
+ // strict, so a required key would refuse every bundle written before #534 with
62
+ // `import_invalid_manifest` — a whole format broken over a word that only ever describes a node
63
+ // with nothing in it. Absent means the entry has its file, which is what every older bundle
64
+ // means.
65
+ absence: BundleEntryAbsence.optional(),
66
+ })
67
+ /**
68
+ * ⚠️ Only a table or an attachment may declare an absence, and the guard belongs HERE rather
69
+ * than at the import.
70
+ *
71
+ * A bundle is somebody else's file, and the import reads this word as permission to skip the
72
+ * check that a manifest entry has its file in the zip. Left unrestricted, an entry saying
73
+ * `kind: "document"` with `absence: "no-content"` would walk past that check even when the file
74
+ * IS in the zip — and the document would be created empty, silently, with nothing refused.
75
+ *
76
+ * The two kinds named here are the ones whose canonical content is the whole of them: a table
77
+ * IS its CSV and an attachment IS its bytes, so for them "no content" is a state of the node
78
+ * rather than a claim about a file. A document without a version is a different case and stays
79
+ * out — an empty markdown file says the same thing about it without ambiguity (#534, #560).
80
+ *
81
+ * Refusing at the boundary is the difference between a named refusal and lost content: the
82
+ * import validates this schema before it plans anything, so a manifest like that fails as
83
+ * `import_invalid_manifest` instead of importing something the bundle does not say.
84
+ */
85
+ .refine(
86
+ (entry) => entry.absence === undefined || entry.kind === "table" || entry.kind === "attachment",
87
+ {
88
+ message: "Only a table or attachment entry may declare an absence",
89
+ path: ["absence"],
90
+ },
91
+ );
92
+ export type BundleManifestEntry = z.infer<typeof BundleManifestEntry>;
93
+
94
+ /**
95
+ * What an export leaves out on purpose, named so a bundle says it rather than a reader guessing:
96
+ * version history, grants/shares, flow runs, and archived nodes are not in any bundle (#136).
97
+ *
98
+ * ⚠️ `unpublished-flow-changes` is the FIFTH, and it is the one exception to ADR-0006's own
99
+ * headline (#585). A bundle carries the current state of everything — except a flow that has been
100
+ * published, which travels as its PUBLISHED graph. Edits made after that publication are in no
101
+ * bundle, and they are not history: they are the newest state there is. The word exists because the
102
+ * loss is only acceptable while it is named, and `version-history` does not name it — a reader who
103
+ * saw only that word would expect the newest graph and get one that is deliberately older.
104
+ *
105
+ * ⚠️ These are WIRE values: an older reader refuses a bundle naming an exclusion it does not know,
106
+ * the same way it would refuse an unknown `kind`. The enum grows only for something an installation
107
+ * genuinely leaves behind, never as a place to note a nicety (ADR-0006, #433). Growing it costs the
108
+ * direction nobody migrates in — a bundle written HERE is refused by an installation older than
109
+ * this line, while every bundle written before it still parses, which is the direction 0.22.0's
110
+ * rebuild actually runs.
111
+ */
112
+ export const BundleExclusion = z.enum([
113
+ "version-history",
114
+ "grants",
115
+ "flow-runs",
116
+ "archived-nodes",
117
+ "unpublished-flow-changes",
118
+ ]);
119
+ export type BundleExclusion = z.infer<typeof BundleExclusion>;
120
+
121
+ export const BundleManifest = z.strictObject({
122
+ version: z.literal(1),
123
+ exportedAt: IsoDateTime,
124
+ // The node the export started at; `null` is the root of the tree — the whole installation as the
125
+ // exporting caller may read it.
126
+ rootId: IntelId.nullable(),
127
+ entries: z.array(BundleManifestEntry),
128
+ excluded: z.array(BundleExclusion),
129
+ });
130
+ export type BundleManifest = z.infer<typeof BundleManifest>;
131
+
132
+ // ── Bundle import (#137) ────────────────────────────────────────────────────────────────────────
133
+
134
+ /**
135
+ * What one import made. Import always creates new nodes — no merge, no overwrite, no restored IDs
136
+ * (#137, phase 1) — so the answer is counts and the new roots, never a diff. `replayed` marks the
137
+ * idempotent second answer to the same key: nothing was created twice.
138
+ *
139
+ * ⚠️ `excluded` is what the IMPORT knows about itself, never what the bundle claimed (ADR-0006,
140
+ * #433). A subtree moved between installations arrives with one version per node, no grants and no
141
+ * runs, and until now nothing said so at the moment somebody could still act on it — the word stood
142
+ * only in a `manifest.json` inside the zip that had already been written.
143
+ *
144
+ * It is deliberately NOT read back out of that manifest. A hand-written bundle saying `excluded: []`
145
+ * would then make the import report that nothing was left behind, and the import would launder
146
+ * somebody else's claim into an answer that sounds like its own — the trap `absence` is bound
147
+ * against in `BundleManifestEntry` above (#534, #560). The five exclusions hold for every import
148
+ * this build performs, with a manifest or from a naked folder, so this is a constant of the code.
149
+ */
150
+ export const BundleImportResult = z.strictObject({
151
+ nodes: z.number().int().nonnegative(),
152
+ flows: z.number().int().nonnegative(),
153
+ rootNodeIds: z.array(IntelId),
154
+ replayed: z.boolean(),
155
+ excluded: z.array(BundleExclusion),
156
+ });
157
+ export type BundleImportResult = z.infer<typeof BundleImportResult>;