@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.
- package/README.md +496 -0
- package/bin/intel.mjs +134 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
- package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
- package/dist/adapters/cloudflare/cloudflare.js +416 -0
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
- package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
- package/dist/adapters/content/content.d.ts +3 -0
- package/dist/adapters/content/content.js +128 -0
- package/dist/adapters/content/content.types.d.ts +38 -0
- package/dist/adapters/content/content.types.js +1 -0
- package/dist/adapters/db/db-audit.d.ts +62 -0
- package/dist/adapters/db/db-audit.js +150 -0
- package/dist/adapters/db/db-boards.d.ts +6 -0
- package/dist/adapters/db/db-boards.js +324 -0
- package/dist/adapters/db/db-feed.d.ts +30 -0
- package/dist/adapters/db/db-feed.js +276 -0
- package/dist/adapters/db/db-flows.d.ts +42 -0
- package/dist/adapters/db/db-flows.js +1311 -0
- package/dist/adapters/db/db-grants.d.ts +133 -0
- package/dist/adapters/db/db-grants.js +269 -0
- package/dist/adapters/db/db-indexing.d.ts +3 -0
- package/dist/adapters/db/db-indexing.js +200 -0
- package/dist/adapters/db/db-oauth.d.ts +3 -0
- package/dist/adapters/db/db-oauth.js +26 -0
- package/dist/adapters/db/db-prompts.d.ts +46 -0
- package/dist/adapters/db/db-prompts.js +144 -0
- package/dist/adapters/db/db.d.ts +6 -0
- package/dist/adapters/db/db.js +1687 -0
- package/dist/adapters/db/db.types.d.ts +16 -0
- package/dist/adapters/db/db.types.js +1 -0
- package/dist/adapters/document-converter/document-converter.d.ts +2 -0
- package/dist/adapters/document-converter/document-converter.js +26 -0
- package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
- package/dist/adapters/document-converter/document-converter.types.js +1 -0
- package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
- package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
- package/dist/adapters/index-queue/index-queue.d.ts +10 -0
- package/dist/adapters/index-queue/index-queue.js +12 -0
- package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
- package/dist/adapters/index-queue/index-queue.types.js +1 -0
- package/dist/adapters/json-schema/json-schema.d.ts +2 -0
- package/dist/adapters/json-schema/json-schema.js +77 -0
- package/dist/adapters/openid/openid.d.ts +5 -0
- package/dist/adapters/openid/openid.js +301 -0
- package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
- package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
- package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
- package/dist/adapters/remote-tools/remote-tools.js +129 -0
- package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
- package/dist/adapters/semantic-index/semantic-index.js +139 -0
- package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
- package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
- package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
- package/dist/adapters/session-cookie/session-cookie.js +87 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
- package/dist/audit/audit.d.ts +2 -0
- package/dist/audit/audit.js +87 -0
- package/dist/audit/audit.types.d.ts +23 -0
- package/dist/audit/audit.types.js +1 -0
- package/dist/auth/auth.d.ts +2 -0
- package/dist/auth/auth.js +284 -0
- package/dist/auth/auth.types.d.ts +108 -0
- package/dist/auth/auth.types.js +1 -0
- package/dist/boards/boards.d.ts +44 -0
- package/dist/boards/boards.js +447 -0
- package/dist/boards/boards.types.d.ts +117 -0
- package/dist/boards/boards.types.js +1 -0
- package/dist/build/build.d.ts +2 -0
- package/dist/build/build.js +192 -0
- package/dist/build/build.types.d.ts +17 -0
- package/dist/build/build.types.js +1 -0
- package/dist/bundle/bundle.d.ts +4 -0
- package/dist/bundle/bundle.js +1194 -0
- package/dist/bundle/bundle.types.d.ts +33 -0
- package/dist/bundle/bundle.types.js +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.js +262 -0
- package/dist/cli/cli.types.d.ts +14 -0
- package/dist/cli/cli.types.js +1 -0
- package/dist/contract/audit.d.ts +50 -0
- package/dist/contract/audit.js +72 -0
- package/dist/contract/board.d.ts +150 -0
- package/dist/contract/board.js +262 -0
- package/dist/contract/bundle.d.ts +147 -0
- package/dist/contract/bundle.js +139 -0
- package/dist/contract/contract.d.ts +112 -0
- package/dist/contract/contract.js +143 -0
- package/dist/contract/feed.d.ts +126 -0
- package/dist/contract/feed.js +149 -0
- package/dist/contract/flow-run.d.ts +346 -0
- package/dist/contract/flow-run.js +194 -0
- package/dist/contract/flow.d.ts +1057 -0
- package/dist/contract/flow.js +552 -0
- package/dist/contract/node.d.ts +551 -0
- package/dist/contract/node.js +456 -0
- package/dist/contract/share.d.ts +216 -0
- package/dist/contract/share.js +134 -0
- package/dist/contract/table.d.ts +171 -0
- package/dist/contract/table.js +117 -0
- package/dist/contract/tool.d.ts +122 -0
- package/dist/contract/tool.js +172 -0
- package/dist/feed/feed.d.ts +2 -0
- package/dist/feed/feed.js +77 -0
- package/dist/feed/feed.types.d.ts +23 -0
- package/dist/feed/feed.types.js +1 -0
- package/dist/flows/flows.d.ts +41 -0
- package/dist/flows/flows.js +2216 -0
- package/dist/flows/flows.types.d.ts +339 -0
- package/dist/flows/flows.types.js +1 -0
- package/dist/http/http.d.ts +9 -0
- package/dist/http/http.js +874 -0
- package/dist/http/http.types.d.ts +22 -0
- package/dist/http/http.types.js +1 -0
- package/dist/indexing/indexing.d.ts +6 -0
- package/dist/indexing/indexing.js +239 -0
- package/dist/indexing/indexing.types.d.ts +11 -0
- package/dist/indexing/indexing.types.js +1 -0
- package/dist/intel/intel.d.ts +3 -0
- package/dist/intel/intel.js +194 -0
- package/dist/intel/intel.types.d.ts +24 -0
- package/dist/intel/intel.types.js +1 -0
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +1292 -0
- package/dist/mcp/mcp.types.d.ts +25 -0
- package/dist/mcp/mcp.types.js +1 -0
- package/dist/nodes/document-links/document-links.d.ts +20 -0
- package/dist/nodes/document-links/document-links.js +57 -0
- package/dist/nodes/nodes.d.ts +2 -0
- package/dist/nodes/nodes.js +1667 -0
- package/dist/nodes/nodes.types.d.ts +511 -0
- package/dist/nodes/nodes.types.js +1 -0
- package/dist/prepare/prepare.d.ts +4 -0
- package/dist/prepare/prepare.js +16 -0
- package/dist/prepare/prepare.types.d.ts +9 -0
- package/dist/prepare/prepare.types.js +1 -0
- package/dist/prompts/prompts.d.ts +2 -0
- package/dist/prompts/prompts.js +65 -0
- package/dist/prompts/prompts.types.d.ts +71 -0
- package/dist/prompts/prompts.types.js +1 -0
- package/dist/shared/callers/callers.d.ts +28 -0
- package/dist/shared/callers/callers.js +30 -0
- package/dist/shared/csv/csv.d.ts +13 -0
- package/dist/shared/csv/csv.js +85 -0
- package/dist/shared/document-text/document-text.d.ts +21 -0
- package/dist/shared/document-text/document-text.js +31 -0
- package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
- package/dist/shared/gate-authorization/gate-authorization.js +26 -0
- package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
- package/dist/shared/grant-expiry/grant-expiry.js +26 -0
- package/dist/shared/intel-error/intel-error.d.ts +22 -0
- package/dist/shared/intel-error/intel-error.js +27 -0
- package/dist/shared/plain-title/plain-title.d.ts +16 -0
- package/dist/shared/plain-title/plain-title.js +45 -0
- package/dist/shared/problem-details/problem-details.d.ts +30 -0
- package/dist/shared/problem-details/problem-details.js +18 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
- package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
- package/dist/shared/safe-return-path/safe-return-path.js +11 -0
- package/dist/shared/sha256/sha256.d.ts +2 -0
- package/dist/shared/sha256/sha256.js +14 -0
- package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
- package/dist/tools/tool-servers/tool-servers.js +177 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +207 -0
- package/dist/tools/tools.types.d.ts +56 -0
- package/dist/tools/tools.types.js +1 -0
- package/examples/branding/favicon.svg +5 -0
- package/examples/branding/logo.svg +5 -0
- package/examples/branding/theme.css +16 -0
- package/examples/dev.vars.example +7 -0
- package/examples/intel.json +9 -0
- package/migrations/0000_intel_foundation.sql +203 -0
- package/migrations/0001_portal_native_tools.sql +21 -0
- package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
- package/migrations/0003_folder_permissions.sql +211 -0
- package/migrations/0004_subflow_runs.sql +14 -0
- package/migrations/0005_flow_node_cleanup.sql +28 -0
- package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
- package/migrations/0006_links_are_written_in_the_text.sql +20 -0
- package/migrations/0007_no_node_waits.sql +69 -0
- package/migrations/0008_three_layers.sql +130 -0
- package/migrations/0009_no_context_policy.sql +48 -0
- package/migrations/0010_no_run_waits_a_year.sql +27 -0
- package/migrations/0011_one_name_for_the_tree.sql +53 -0
- package/migrations/0012_table_snapshots.sql +29 -0
- package/migrations/0017_a_vector_per_card.sql +38 -0
- package/migrations/0018_no_context_policy_at_last.sql +97 -0
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/migrations/0020_cascade_purge_replay.sql +11 -0
- package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
- package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
- package/migrations/0023_a_board_and_its_tasks.sql +116 -0
- package/migrations/0024_the_archive_is_a_column.sql +33 -0
- package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
- package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
- package/migrations/0027_the_runs_of_every_flow.sql +27 -0
- package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
- package/package.json +118 -0
- package/src/contract/CLAUDE.md +152 -0
- package/src/contract/README.md +102 -0
- package/src/contract/audit.ts +85 -0
- package/src/contract/board.ts +306 -0
- package/src/contract/bundle.ts +157 -0
- package/src/contract/contract.ts +161 -0
- package/src/contract/feed.ts +165 -0
- package/src/contract/flow-run.ts +241 -0
- package/src/contract/flow.ts +675 -0
- package/src/contract/node.ts +583 -0
- package/src/contract/share.ts +196 -0
- package/src/contract/table.ts +178 -0
- package/src/contract/tool.ts +202 -0
- package/ui/README.md +110 -0
- package/ui/components.json +21 -0
- package/ui/index.html +13 -0
- package/ui/package.json +11 -0
- package/ui/scripts/lint-tokens.mjs +187 -0
- package/ui/src/access-summary/access-summary.tsx +385 -0
- package/ui/src/app/action-slot/action-slot.tsx +27 -0
- package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
- package/ui/src/app/app-tree/app-tree.tsx +1175 -0
- package/ui/src/app/app.tsx +87 -0
- package/ui/src/app/header-search/header-search.tsx +294 -0
- package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
- package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
- package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
- package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
- package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
- package/ui/src/app/tree-move/tree-move.tsx +427 -0
- package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
- package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
- package/ui/src/app/user-footer/user-footer.tsx +188 -0
- package/ui/src/app/view-menu/view-menu.tsx +115 -0
- package/ui/src/app-root/app-root.tsx +109 -0
- package/ui/src/app-root/app-root.types.ts +27 -0
- package/ui/src/archive/archive.tsx +417 -0
- package/ui/src/attachment-detail/attachment-detail.ts +52 -0
- package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
- package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
- package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
- package/ui/src/board/board-assignee/board-assignee.ts +48 -0
- package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
- package/ui/src/board/board-chip/board-chip.tsx +46 -0
- package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
- package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
- package/ui/src/board/board-data/board-data.ts +195 -0
- package/ui/src/board/board-data/board-data.types.ts +38 -0
- package/ui/src/board/board-dates/board-dates.ts +40 -0
- package/ui/src/board/board-dates/board-dates.tsx +105 -0
- package/ui/src/board/board-kanban/board-kanban.ts +162 -0
- package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
- package/ui/src/board/board-open-task/board-open-task.ts +32 -0
- package/ui/src/board/board-panel/board-panel.tsx +259 -0
- package/ui/src/board/board-settings/board-settings.tsx +226 -0
- package/ui/src/board/board-status/board-status.tsx +60 -0
- package/ui/src/board/board-stripes/board-stripes.ts +128 -0
- package/ui/src/board/board-table/board-table.ts +53 -0
- package/ui/src/board/board-table/board-table.tsx +610 -0
- package/ui/src/board/board-task/board-task.ts +108 -0
- package/ui/src/board/board-task/board-task.tsx +335 -0
- package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
- package/ui/src/branding/branding.tsx +17 -0
- package/ui/src/branding/custom-logo.ts +1 -0
- package/ui/src/branding/favicon.default.svg +5 -0
- package/ui/src/branding/favicon.svg +5 -0
- package/ui/src/components/ui/avatar.tsx +39 -0
- package/ui/src/components/ui/button.tsx +64 -0
- package/ui/src/components/ui/collapsible.tsx +20 -0
- package/ui/src/components/ui/command.tsx +160 -0
- package/ui/src/components/ui/dialog.tsx +143 -0
- package/ui/src/components/ui/dropdown-menu.tsx +187 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/popover.tsx +41 -0
- package/ui/src/components/ui/select.tsx +163 -0
- package/ui/src/components/ui/separator.tsx +26 -0
- package/ui/src/components/ui/sheet.tsx +136 -0
- package/ui/src/components/ui/sidebar.tsx +693 -0
- package/ui/src/components/ui/skeleton.tsx +13 -0
- package/ui/src/components/ui/switch.tsx +25 -0
- package/ui/src/components/ui/table.tsx +82 -0
- package/ui/src/components/ui/tabs.tsx +52 -0
- package/ui/src/components/ui/tooltip.tsx +51 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
- package/ui/src/data/query-client.ts +18 -0
- package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
- package/ui/src/data/request-refusal/request-refusal.ts +51 -0
- package/ui/src/data/sign-in/sign-in.ts +97 -0
- package/ui/src/data/sign-in/sign-in.types.ts +15 -0
- package/ui/src/document-link/document-link.tsx +114 -0
- package/ui/src/document-markdown/document-markdown.ts +195 -0
- package/ui/src/editor-schema/editor-schema.ts +23 -0
- package/ui/src/entry-picker/entry-picker.tsx +238 -0
- package/ui/src/feed/feed.tsx +344 -0
- package/ui/src/file-preview/file-preview-view.tsx +496 -0
- package/ui/src/file-preview/file-preview.tsx +28 -0
- package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
- package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
- package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
- package/ui/src/file-preview/word-file-preview.tsx +5 -0
- package/ui/src/flow-runs/flow-runs.tsx +238 -0
- package/ui/src/flows/flows.tsx +1646 -0
- package/ui/src/flows/node-icon/node-icon.ts +34 -0
- package/ui/src/flows/node-palette/node-palette.tsx +214 -0
- package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
- package/ui/src/folder-contents/folder-contents.tsx +321 -0
- package/ui/src/folder-index/folder-index.ts +55 -0
- package/ui/src/frontmatter/frontmatter.tsx +193 -0
- package/ui/src/graph-pane/graph-pane.tsx +53 -0
- package/ui/src/hooks/use-mobile.ts +19 -0
- package/ui/src/i18n/custom.json +4 -0
- package/ui/src/i18n/de.json +609 -0
- package/ui/src/i18n/en.json +609 -0
- package/ui/src/i18n/es.json +609 -0
- package/ui/src/i18n/i18n-context.tsx +65 -0
- package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
- package/ui/src/i18n/i18n.ts +32 -0
- package/ui/src/i18n/i18n.types.ts +49 -0
- package/ui/src/kind-icon.ts +97 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +68 -0
- package/ui/src/modal/modal.tsx +53 -0
- package/ui/src/node-details/node-details.tsx +545 -0
- package/ui/src/node-editor/node-editor.tsx +304 -0
- package/ui/src/node-graph/graph-notice.tsx +21 -0
- package/ui/src/node-graph/node-graph.ts +214 -0
- package/ui/src/node-graph/node-graph.tsx +295 -0
- package/ui/src/node-import/node-import.tsx +418 -0
- package/ui/src/node-save-error.ts +46 -0
- package/ui/src/node-source/node-source.tsx +128 -0
- package/ui/src/node-table/node-table.tsx +107 -0
- package/ui/src/nodes/nodes.tsx +240 -0
- package/ui/src/rename-resource/rename-resource.ts +80 -0
- package/ui/src/resolved-names/resolved-names.tsx +67 -0
- package/ui/src/resource-error.ts +33 -0
- package/ui/src/resource-menu/resource-menu.tsx +1156 -0
- package/ui/src/router/router-context.ts +6 -0
- package/ui/src/router/router.tsx +67 -0
- package/ui/src/router/router.types.ts +8 -0
- package/ui/src/router/selection-search.ts +88 -0
- package/ui/src/save-button/save-button.tsx +94 -0
- package/ui/src/section-hint/section-hint.tsx +40 -0
- package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
- package/ui/src/styles.css +189 -0
- package/ui/src/table-columns/table-columns.tsx +379 -0
- package/ui/src/theme/custom.css +1 -0
- package/ui/src/theme/theme-context.tsx +89 -0
- package/ui/src/theme/theme.ts +70 -0
- package/ui/src/time/relative-time.tsx +41 -0
- package/ui/src/time/time-context.tsx +78 -0
- package/ui/src/time/time.ts +165 -0
- package/ui/src/title-row/title-row.tsx +185 -0
- package/ui/src/tools/tools.tsx +546 -0
- package/ui/src/user-name/user-name.ts +61 -0
- package/ui/src/verbatim/verbatim.tsx +68 -0
- package/ui/tsconfig.json +17 -0
- package/ui/vite.config.ts +129 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
-- Two more node kinds, `board` and `task`, and the two side tables that carry what a node row must
|
|
2
|
+
-- not (D66, anchrd/intel#376).
|
|
3
|
+
--
|
|
4
|
+
-- ⚠️ BOTH words in ONE rebuild. Adding them in two migrations would be the same risky operation
|
|
5
|
+
-- twice, and this one has gone wrong before: `0005` committed with every row of `node_links` gone.
|
|
6
|
+
--
|
|
7
|
+
-- The split this migration encodes: IDENTITY through `kind`, FIELDS through a side table.
|
|
8
|
+
-- * `kind` because the tree has to hide `task` on EVERY level query. As a kind that is a condition
|
|
9
|
+
-- on a column the query already carries; as a `document` plus a side-table row it would be a
|
|
10
|
+
-- join on the hottest path in the interface.
|
|
11
|
+
-- * the side table because `status`, `assignee` and the dates are FILTER columns. In the `nodes`
|
|
12
|
+
-- row they would stand empty for every other kind.
|
|
13
|
+
--
|
|
14
|
+
-- The recipe below is the one in `packages/intel/CLAUDE.md`, *Rebuilding `nodes` is a recipe*. All
|
|
15
|
+
-- four points, and each is one a previous migration got wrong first:
|
|
16
|
+
-- 1. the new table is created under its FINAL name — never `ALTER TABLE … RENAME`, which rewrites
|
|
17
|
+
-- every `REFERENCES` clause and drags the children onto the table about to be dropped;
|
|
18
|
+
-- 2. `node_links` is carried out and back with `INSERT OR IGNORE`, because it is the one child
|
|
19
|
+
-- declared `ON DELETE CASCADE` and `DROP TABLE nodes` fires an implicit `DELETE FROM`;
|
|
20
|
+
-- 3. `PRAGMA defer_foreign_keys`, never `foreign_keys = OFF` — D1 ignores the latter over its HTTP
|
|
21
|
+
-- API while miniflare honours it, so a green test here would prove nothing about production;
|
|
22
|
+
-- 4. the columns are named on BOTH sides of `INSERT … SELECT`, so a future reordering cannot turn
|
|
23
|
+
-- every title into an owner id and still commit.
|
|
24
|
+
PRAGMA defer_foreign_keys = TRUE;
|
|
25
|
+
|
|
26
|
+
CREATE TABLE nodes_carry AS SELECT * FROM nodes;
|
|
27
|
+
CREATE TABLE node_links_carry AS SELECT * FROM node_links;
|
|
28
|
+
|
|
29
|
+
DROP TABLE nodes;
|
|
30
|
+
|
|
31
|
+
CREATE TABLE nodes (
|
|
32
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
33
|
+
parent_id TEXT REFERENCES nodes(id),
|
|
34
|
+
kind TEXT NOT NULL CHECK (kind IN ('folder', 'document', 'attachment', 'table', 'board', 'task')),
|
|
35
|
+
title TEXT NOT NULL CHECK (length(title) BETWEEN 1 AND 240),
|
|
36
|
+
description TEXT CHECK (description IS NULL OR length(description) <= 2000),
|
|
37
|
+
owner_id TEXT NOT NULL,
|
|
38
|
+
current_version_id TEXT,
|
|
39
|
+
created_at TEXT NOT NULL,
|
|
40
|
+
updated_at TEXT NOT NULL,
|
|
41
|
+
archived_at TEXT
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
INSERT INTO nodes (
|
|
45
|
+
id, parent_id, kind, title, description, owner_id,
|
|
46
|
+
current_version_id, created_at, updated_at, archived_at
|
|
47
|
+
)
|
|
48
|
+
SELECT
|
|
49
|
+
id, parent_id, kind, title, description, owner_id,
|
|
50
|
+
current_version_id, created_at, updated_at, archived_at
|
|
51
|
+
FROM nodes_carry;
|
|
52
|
+
|
|
53
|
+
INSERT OR IGNORE INTO node_links SELECT * FROM node_links_carry;
|
|
54
|
+
|
|
55
|
+
DROP TABLE nodes_carry;
|
|
56
|
+
DROP TABLE node_links_carry;
|
|
57
|
+
|
|
58
|
+
CREATE INDEX nodes_parent_idx ON nodes(parent_id, archived_at, title);
|
|
59
|
+
CREATE INDEX nodes_owner_idx ON nodes(owner_id, archived_at);
|
|
60
|
+
|
|
61
|
+
-- The board's own configuration: which columns it has and in what order. It is one row per board
|
|
62
|
+
-- node, and it exists because a list of statuses is not a node property — every other kind would
|
|
63
|
+
-- carry it empty.
|
|
64
|
+
--
|
|
65
|
+
-- ⚠️ `ON DELETE CASCADE` here and on `board_tasks` below, and that is deliberate in a repository
|
|
66
|
+
-- whose rule is *refuse rather than cascade*. The rule protects a CONTAINER from taking its contents
|
|
67
|
+
-- down with it; these two rows are not contents but the node's own fields, split off only because
|
|
68
|
+
-- SQLite has no per-kind columns. A board whose row here outlived it would be a configuration for a
|
|
69
|
+
-- board that does not exist.
|
|
70
|
+
CREATE TABLE boards (
|
|
71
|
+
node_id TEXT PRIMARY KEY NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
|
72
|
+
statuses_json TEXT NOT NULL DEFAULT '[]',
|
|
73
|
+
created_at TEXT NOT NULL,
|
|
74
|
+
updated_at TEXT NOT NULL
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
-- What a task carries beyond a node. Every column here is one somebody filters or sorts by — that
|
|
78
|
+
-- is the whole reason they are not in the body.
|
|
79
|
+
--
|
|
80
|
+
-- ⚠️ `position` is REAL, not INTEGER. Dragging a card between two neighbours then needs no rewrite
|
|
81
|
+
-- of the whole column: the new value is the midpoint of the two. With integers every drop would
|
|
82
|
+
-- renumber the rows below it, and two people dropping at once would fight over rows neither touched.
|
|
83
|
+
CREATE TABLE board_tasks (
|
|
84
|
+
node_id TEXT PRIMARY KEY NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
|
85
|
+
board_id TEXT NOT NULL REFERENCES nodes(id),
|
|
86
|
+
status TEXT NOT NULL,
|
|
87
|
+
assignee_id TEXT,
|
|
88
|
+
labels_json TEXT NOT NULL DEFAULT '[]',
|
|
89
|
+
start_date TEXT,
|
|
90
|
+
due_date TEXT,
|
|
91
|
+
-- "waits for", as a COLUMN and not as a sentence in the body. The DoD of #376 says it in one
|
|
92
|
+
-- line: relations are columns the text points at, never the other way round. A dependency living
|
|
93
|
+
-- in prose cannot be asked "what is blocked right now", and `node_links` cannot answer it either
|
|
94
|
+
-- — that table says two nodes are related, not that one waits for the other.
|
|
95
|
+
--
|
|
96
|
+
-- ⚠️ It points at `nodes`, not at `board_tasks`. A task may wait for something that is not a
|
|
97
|
+
-- task: a document that has to be approved first, a board that has to be finished. Narrowing it
|
|
98
|
+
-- to the side table would decide today that only tasks can block, and nothing asked for that.
|
|
99
|
+
depends_on TEXT REFERENCES nodes(id),
|
|
100
|
+
position REAL NOT NULL DEFAULT 0,
|
|
101
|
+
created_at TEXT NOT NULL,
|
|
102
|
+
updated_at TEXT NOT NULL
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
-- The board view reads one column at a time, ordered. Both columns of the sort are in the index, so
|
|
106
|
+
-- the read needs no sorting pass — the same reason `audit_events_feed_idx` carries its tie-break
|
|
107
|
+
-- (anchrd/intel#620).
|
|
108
|
+
CREATE INDEX board_tasks_column_idx ON board_tasks(board_id, status, position);
|
|
109
|
+
|
|
110
|
+
-- "What is assigned to me, soonest first" — across every board, which is why `assignee_id` leads
|
|
111
|
+
-- and `board_id` does not appear.
|
|
112
|
+
CREATE INDEX board_tasks_assignee_idx ON board_tasks(assignee_id, due_date);
|
|
113
|
+
|
|
114
|
+
-- "What is blocked by this node" — the direction the interface asks in. Without it the question is
|
|
115
|
+
-- a scan of every task on every board.
|
|
116
|
+
CREATE INDEX board_tasks_depends_idx ON board_tasks(depends_on) WHERE depends_on IS NOT NULL;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- The archive as a column every board has (D68, #674).
|
|
2
|
+
--
|
|
3
|
+
-- ⚠️ Only the VISIBILITY is stored. Which cards are in the archive is `nodes.archived_at` and
|
|
4
|
+
-- nothing else — a second column holding "is archived" would be the two truths D68 exists against.
|
|
5
|
+
-- A card is in the archive column BECAUSE it is archived; dropping it there archives it, and
|
|
6
|
+
-- pulling it out restores it into the working column `board_tasks.status` still names.
|
|
7
|
+
--
|
|
8
|
+
-- ⚠️ Default 1: the column is there on every board unless somebody hides it. A board that predates
|
|
9
|
+
-- this migration therefore shows it too, which is the decided behaviour rather than an accident —
|
|
10
|
+
-- an archive nobody can see is an archive nobody empties.
|
|
11
|
+
ALTER TABLE boards ADD COLUMN archive_visible INTEGER NOT NULL DEFAULT 1;
|
|
12
|
+
|
|
13
|
+
-- ⚠️ **Existing data.** Until this migration `BoardColumn.id` was a free string: a board could
|
|
14
|
+
-- configure a column called `archived` itself. From here the name is reserved, and without this
|
|
15
|
+
-- step such a board would show TWO columns with one id — the stored one and the derived one — and
|
|
16
|
+
-- the cards of both would be indistinguishable.
|
|
17
|
+
--
|
|
18
|
+
-- ⚠️ **Every row with that status, not only the ones whose board still configures it.** Columns are
|
|
19
|
+
-- REPLACED rather than merged, so a board may have used the column and dropped it again; those
|
|
20
|
+
-- cards still carry `status = 'archived'` while `statuses_json` has long forgotten it. Left alone
|
|
21
|
+
-- they would surface in the archive column with `archived_at` still `null` — a card that looks
|
|
22
|
+
-- archived, is not, and cannot be restored, because there is nothing to restore.
|
|
23
|
+
--
|
|
24
|
+
-- The new name is deliberately ugly, because it is meant to be seen: whoever reads it knows
|
|
25
|
+
-- something was renamed here and can put it right in the settings.
|
|
26
|
+
UPDATE board_tasks SET status = 'archived_column_renamed' WHERE status = 'archived';
|
|
27
|
+
|
|
28
|
+
UPDATE boards
|
|
29
|
+
SET statuses_json = replace(statuses_json, '"id":"archived"', '"id":"archived_column_renamed"')
|
|
30
|
+
WHERE node_id IN (
|
|
31
|
+
SELECT b.node_id FROM boards b, json_each(b.statuses_json)
|
|
32
|
+
WHERE json_extract(json_each.value, '$.id') = 'archived'
|
|
33
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Archiving takes the whole subtree, and this column is what lets it come back (D71, #733).
|
|
2
|
+
--
|
|
3
|
+
-- ⚠️ **The two steps were the wrong way round.** `purge` cascaded over the subtree since #492 while
|
|
4
|
+
-- `archive` touched a single row, so a folder archived over live children handed those children to
|
|
5
|
+
-- the one step that cannot be undone. Cascading the REVERSIBLE step instead closes the whole chain:
|
|
6
|
+
-- nothing live sits under an archived node any more, so the purge can keep cascading without ever
|
|
7
|
+
-- reaching something alive.
|
|
8
|
+
--
|
|
9
|
+
-- What this column holds is the OPERATION a node fell with, not a flag. A restore brings back
|
|
10
|
+
-- exactly the nodes that fell together, and a child that was already archived before keeps its own
|
|
11
|
+
-- older operation id — and stays archived.
|
|
12
|
+
--
|
|
13
|
+
-- ⚠️ **Existing data keeps `NULL` here**, and that is not the same as "fell alone": it means
|
|
14
|
+
-- "archived before this migration, and nobody recorded what it fell with". A restore therefore
|
|
15
|
+
-- treats `NULL` as itself only, never as a group — otherwise restoring one old node would revive
|
|
16
|
+
-- every node ever archived before today.
|
|
17
|
+
ALTER TABLE nodes ADD COLUMN archived_with TEXT;
|
|
18
|
+
|
|
19
|
+
-- The restore reads this column for one node and then finds its siblings by it. Without the index
|
|
20
|
+
-- that is a scan of the whole table on every restore of a large folder.
|
|
21
|
+
CREATE INDEX IF NOT EXISTS nodes_archived_with ON nodes (archived_with) WHERE archived_with IS NOT NULL;
|
|
22
|
+
|
|
23
|
+
-- ⚠️ **Flows fall with the tree, so they need the same column.** They hang in the folder tree by
|
|
24
|
+
-- `parent_id` and carry their own `archived_at`, and `inspectPurgeTree` collects them by that same
|
|
25
|
+
-- parent. A cascade that took only `nodes` would leave exactly one kind of live thing under an
|
|
26
|
+
-- archived folder — the kind this repository's own flows are filed as.
|
|
27
|
+
ALTER TABLE flows ADD COLUMN archived_with TEXT;
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS flows_archived_with ON flows (archived_with) WHERE archived_with IS NOT NULL;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- The feed reads both kinds at once — nodes and flows — and `audit_events_feed_idx` cannot serve
|
|
2
|
+
-- that. It leads with `resource_type` because every reader until now named exactly one kind, so for
|
|
3
|
+
-- a query saying `resource_type IN ('node', 'flow')` that column is no longer an equality prefix but
|
|
4
|
+
-- an ordinary filter, and the ordering falls off the index.
|
|
5
|
+
--
|
|
6
|
+
-- ⚠️ Measured against the real `feedPageQuery` before this file existed, not assumed
|
|
7
|
+
-- (anchrd/intel#763):
|
|
8
|
+
--
|
|
9
|
+
-- SEARCH e USING INDEX audit_events_feed_idx (resource_type=?)
|
|
10
|
+
-- … | USE TEMP B-TREE FOR ORDER BY
|
|
11
|
+
--
|
|
12
|
+
-- That last line is a sorting pass over the whole table. It costs nothing on the first page and
|
|
13
|
+
-- everything far down — which is exactly where an endless scroll goes.
|
|
14
|
+
--
|
|
15
|
+
-- ⚠️ Both columns, in the order the cursor compares them, for the reason `0022` gives: two events
|
|
16
|
+
-- written in the same millisecond are the normal case inside one batch, and without the tie-break
|
|
17
|
+
-- on `id` a reader skips the second one with no error and no log.
|
|
18
|
+
--
|
|
19
|
+
-- ⚠️ This does NOT replace `audit_events_feed_idx`. That one still serves `audit_list`, which names
|
|
20
|
+
-- one kind and is the contract `anchrd/signals` builds on; dropping it would put a sorting pass into
|
|
21
|
+
-- the reader that has none today. Two readers, two shapes, two indexes.
|
|
22
|
+
CREATE INDEX audit_events_time_idx
|
|
23
|
+
ON audit_events(occurred_at, id);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- `flow_run_list` may now be asked without a flow (#774): "did anything run at all", over every
|
|
2
|
+
-- flow the caller may read. `flow_runs_flow_idx (flow_id, created_at DESC)` cannot serve that — it
|
|
3
|
+
-- leads with `flow_id`, and without an equality on that column the ordering falls off the index and
|
|
4
|
+
-- SQLite sorts the whole table instead.
|
|
5
|
+
--
|
|
6
|
+
-- ⚠️ Measured against the real `flowRunsPageQuery({ scoped: false })` before this file existed, on
|
|
7
|
+
-- the same engine and the same schema:
|
|
8
|
+
--
|
|
9
|
+
-- SCAN run
|
|
10
|
+
-- … | USE TEMP B-TREE FOR ORDER BY
|
|
11
|
+
--
|
|
12
|
+
-- That last line is a sorting pass over every run in the installation. It costs nothing while a
|
|
13
|
+
-- customer has fifty runs and everything once an agent has been running for a month.
|
|
14
|
+
--
|
|
15
|
+
-- ⚠️ Both columns, in the order the cursor compares them, for the reason `0022` and `0026` give:
|
|
16
|
+
-- two runs created in the same millisecond are not exotic, and without the tie-break on `id` a
|
|
17
|
+
-- reader continuing on `created_at` alone skips the second one with no error and no log.
|
|
18
|
+
--
|
|
19
|
+
-- ⚠️ `DESC` on both, because this page is newest-first. An ASC index can be walked backwards by
|
|
20
|
+
-- SQLite, but only as long as EVERY term agrees on the direction — matching the `ORDER BY` exactly
|
|
21
|
+
-- is the one shape that keeps that true when a third term is added later.
|
|
22
|
+
--
|
|
23
|
+
-- ⚠️ This does NOT replace `flow_runs_flow_idx`. That one still serves the scoped page — the runs
|
|
24
|
+
-- of ONE flow — which is the older and by far the more frequent question; dropping it would put a
|
|
25
|
+
-- full scan into the call that has none today. Two questions, two shapes, two indexes.
|
|
26
|
+
CREATE INDEX flow_runs_time_idx
|
|
27
|
+
ON flow_runs(created_at DESC, id DESC);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- A document and a published flow can both be offered as a slash command (#775). What they need is
|
|
2
|
+
-- one short name, and the name has to be unique across BOTH of them: they land in the same
|
|
3
|
+
-- `prompts/list` catalogue, so a document and a flow can take each other's name.
|
|
4
|
+
--
|
|
5
|
+
-- ⚠️ **One field, not two.** `prompt_name` set means offered, `NULL` means not. A boolean beside it
|
|
6
|
+
-- would allow the state "on, without a name", and that is not a thing `prompts/list` could answer.
|
|
7
|
+
--
|
|
8
|
+
-- ⚠️ **No third table, and the choice is deliberate.** A `prompt_names(name PRIMARY KEY, …)`
|
|
9
|
+
-- catalogue would carry the cross-kind uniqueness as a real constraint, and it would cost two
|
|
10
|
+
-- things this schema is not willing to pay: a second truth about the name of one node — every
|
|
11
|
+
-- `SELECT` over `nodes` would have to remember a join, and the one that forgets it answers `NULL`
|
|
12
|
+
-- in silence — and a fourth child table for the `nodes` rebuild recipe to carry (see the package
|
|
13
|
+
-- CLAUDE.md; `node_links` is the one that already has to be carried out and back). The uniqueness
|
|
14
|
+
-- WITHIN one kind is an index here; the uniqueness ACROSS the two is a condition on the write, on
|
|
15
|
+
-- every statement that sets a name, in the same batch that writes it.
|
|
16
|
+
--
|
|
17
|
+
-- ⚠️ The condition on the write is not a nicety. A `SELECT` before an `UPDATE` in a separate call
|
|
18
|
+
-- decides on a state that may be gone by the time the write lands; the check has to be part of the
|
|
19
|
+
-- statement so that the two cannot come apart. What the caller is told — WHICH node or flow holds
|
|
20
|
+
-- the name — is a second, cheap read that only runs once the write has already refused.
|
|
21
|
+
|
|
22
|
+
-- ⚠️ **This form is MEASURED against real D1, not only against miniflare.** No other file in this
|
|
23
|
+
-- folder adds a `CHECK` through `ALTER TABLE … ADD COLUMN` that reads a DIFFERENT column, and the
|
|
24
|
+
-- package CLAUDE.md is explicit that a green run under the emulator proves nothing about the
|
|
25
|
+
-- deployment — `foreign_keys = OFF` is honoured there and ignored by D1 over its HTTP API. A
|
|
26
|
+
-- migration file is immutable after release, so the failure would surface at the deploy.
|
|
27
|
+
--
|
|
28
|
+
-- Run against a throwaway D1 (`intel-check-0028`, `--remote`) on 2026-08-25 and then deleted. Both
|
|
29
|
+
-- statements applied, `sqlite_master` shows the CHECK on the column, and all six refusals fire with
|
|
30
|
+
-- `SQLITE_CONSTRAINT_CHECK` / `SQLITE_CONSTRAINT_UNIQUE`: a folder and a table with a name, an
|
|
31
|
+
-- uppercase name, a name with a space, a 41-character name, and a second row taking a name that is
|
|
32
|
+
-- already held. A document with a valid name and two rows with none go through.
|
|
33
|
+
--
|
|
34
|
+
-- ⚠️ `kind = 'document'` is in the CHECK, not only in the service. A prompt is an instruction, not
|
|
35
|
+
-- material: a folder, a table, an attachment, a board or a task never carries one (#775). The
|
|
36
|
+
-- service refuses it with a sentence, and this line is what makes that refusal true even for a
|
|
37
|
+
-- write that never passes the service.
|
|
38
|
+
--
|
|
39
|
+
-- ⚠️ `NOT GLOB '*[^a-z0-9-]*'` is the grammar `^[a-z0-9-]{1,40}$` says on the contract, spelled the
|
|
40
|
+
-- one way SQLite can spell it: "no character outside the class anywhere". The obvious
|
|
41
|
+
-- `GLOB '[a-z0-9-]*'` is NOT that check — it constrains the first character and lets every other
|
|
42
|
+
-- one through, so `a_B C` passes it. The hyphen sits last inside the class, where it is a literal
|
|
43
|
+
-- rather than a range.
|
|
44
|
+
ALTER TABLE nodes ADD COLUMN prompt_name TEXT
|
|
45
|
+
CHECK (
|
|
46
|
+
prompt_name IS NULL
|
|
47
|
+
OR (
|
|
48
|
+
kind = 'document'
|
|
49
|
+
AND length(prompt_name) BETWEEN 1 AND 40
|
|
50
|
+
AND prompt_name NOT GLOB '*[^a-z0-9-]*'
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
ALTER TABLE flows ADD COLUMN prompt_name TEXT
|
|
55
|
+
CHECK (
|
|
56
|
+
prompt_name IS NULL
|
|
57
|
+
OR (length(prompt_name) BETWEEN 1 AND 40 AND prompt_name NOT GLOB '*[^a-z0-9-]*')
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
-- ⚠️ Partial, and that is the whole reason they work. A plain `UNIQUE` index would let several
|
|
61
|
+
-- rows carry `NULL` too — SQLite treats NULLs as distinct — so the `WHERE` clause buys no
|
|
62
|
+
-- correctness here; it buys the SIZE. Almost every node has no prompt name, and an index over
|
|
63
|
+
-- them all would be one entry per node in the tree to serve a handful of rows.
|
|
64
|
+
--
|
|
65
|
+
-- ⚠️ These two carry the uniqueness WITHIN a kind and nothing more. Two documents cannot share a
|
|
66
|
+
-- name and two flows cannot; a document and a flow can, as far as these indexes are concerned, and
|
|
67
|
+
-- the condition on the write is the only thing that stops them. Reading one of these index names in
|
|
68
|
+
-- a query plan is therefore not evidence that the cross-kind rule held.
|
|
69
|
+
CREATE UNIQUE INDEX nodes_prompt_name_idx ON nodes(prompt_name) WHERE prompt_name IS NOT NULL;
|
|
70
|
+
CREATE UNIQUE INDEX flows_prompt_name_idx ON flows(prompt_name) WHERE prompt_name IS NOT NULL;
|
package/package.json
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anchrd/intel",
|
|
3
|
+
"version": "0.59.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/anchrd/intel.git",
|
|
9
|
+
"directory": "packages/intel"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"intel": "bin/intel.mjs"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/intel/intel.d.ts",
|
|
20
|
+
"default": "./dist/intel/intel.js"
|
|
21
|
+
},
|
|
22
|
+
"./cloudflare": {
|
|
23
|
+
"types": "./dist/adapters/cloudflare/cloudflare.d.ts",
|
|
24
|
+
"default": "./dist/adapters/cloudflare/cloudflare.js"
|
|
25
|
+
},
|
|
26
|
+
"./cloudflare-workflow": {
|
|
27
|
+
"types": "./dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts",
|
|
28
|
+
"default": "./dist/adapters/cloudflare/cloudflare-flow-workflow.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"bin",
|
|
33
|
+
"dist",
|
|
34
|
+
"examples",
|
|
35
|
+
"migrations",
|
|
36
|
+
"src/contract",
|
|
37
|
+
"!src/contract/**/*.unit.ts",
|
|
38
|
+
"ui/package.json",
|
|
39
|
+
"ui/README.md",
|
|
40
|
+
"ui/index.html",
|
|
41
|
+
"ui/vite.config.ts",
|
|
42
|
+
"ui/tsconfig.json",
|
|
43
|
+
"ui/components.json",
|
|
44
|
+
"ui/scripts",
|
|
45
|
+
"ui/src",
|
|
46
|
+
"!ui/src/**/*.unit.ts",
|
|
47
|
+
"!ui/src/**/*.unit.tsx",
|
|
48
|
+
"!ui/src/**/*.int.ts",
|
|
49
|
+
"!ui/src/**/*.int.tsx",
|
|
50
|
+
"!ui/src/**/*.e2e.ts",
|
|
51
|
+
"!ui/src/**/*.e2e.tsx"
|
|
52
|
+
],
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc -p tsconfig.build.json && node ../../scripts/pack/fix-dts.mjs",
|
|
55
|
+
"dev": "vite ui",
|
|
56
|
+
"lint:tokens": "node ui/scripts/lint-tokens.mjs ui/src",
|
|
57
|
+
"prepack": "bun run build && node ../../scripts/pack/to-dist-manifest.mjs",
|
|
58
|
+
"postpack": "node ../../scripts/pack/restore-manifest.mjs",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p ui/tsconfig.json",
|
|
61
|
+
"typecheck:core": "tsc --noEmit"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@anchrd/gate-sdk": "^0.28.0",
|
|
65
|
+
"@blocknote/core": "^0.52.1",
|
|
66
|
+
"@blocknote/react": "^0.52.1",
|
|
67
|
+
"@blocknote/shadcn": "^0.52.1",
|
|
68
|
+
"@cfworker/json-schema": "^4.1.1",
|
|
69
|
+
"@dnd-kit/core": "^6.3.1",
|
|
70
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
71
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
72
|
+
"@file-viewer/react": "2.3.0",
|
|
73
|
+
"@file-viewer/renderer-pdf": "2.3.0",
|
|
74
|
+
"@file-viewer/renderer-presentation": "2.3.0",
|
|
75
|
+
"@file-viewer/renderer-spreadsheet": "2.3.2",
|
|
76
|
+
"@file-viewer/renderer-word": "2.3.1",
|
|
77
|
+
"@file-viewer/vite-plugin": "2.3.1",
|
|
78
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
79
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
80
|
+
"@tanstack/react-query": "^5.101.4",
|
|
81
|
+
"@tanstack/react-router": "^1.170.18",
|
|
82
|
+
"@tanstack/react-table": "^9.1.2",
|
|
83
|
+
"@vitejs/plugin-react": "^6.0.4",
|
|
84
|
+
"@xyflow/react": "^12.11.2",
|
|
85
|
+
"class-variance-authority": "^0.7.1",
|
|
86
|
+
"clsx": "^2.1.1",
|
|
87
|
+
"cmdk": "^1.1.1",
|
|
88
|
+
"fflate": "^0.8.3",
|
|
89
|
+
"fractional-indexing": "^4.0.0",
|
|
90
|
+
"hono": "^4.12.32",
|
|
91
|
+
"lucide-react": "^1.25.0",
|
|
92
|
+
"openid-client": "^6.8.4",
|
|
93
|
+
"radix-ui": "^1.6.7",
|
|
94
|
+
"react": "^19.2.0",
|
|
95
|
+
"react-aria-components": "^1.19.0",
|
|
96
|
+
"react-dom": "^19.2.0",
|
|
97
|
+
"readable-stream": "2.3.8",
|
|
98
|
+
"tailwind-merge": "^3.6.0",
|
|
99
|
+
"tailwindcss": "^4.3.3",
|
|
100
|
+
"ulid": "^3.0.2",
|
|
101
|
+
"vite": "^8.1.5",
|
|
102
|
+
"zod": "^4.4.3"
|
|
103
|
+
},
|
|
104
|
+
"devDependencies": {
|
|
105
|
+
"@cloudflare/vitest-pool-workers": "^0.19.0",
|
|
106
|
+
"@testing-library/dom": "^10.4.1",
|
|
107
|
+
"@testing-library/react": "^16.3.2",
|
|
108
|
+
"@types/node": "^24.13.3",
|
|
109
|
+
"@types/react": "^19.2.17",
|
|
110
|
+
"@types/react-dom": "^19.2.3",
|
|
111
|
+
"jsdom": "^29.1.1",
|
|
112
|
+
"vitest": "^4.1.10",
|
|
113
|
+
"wrangler": "^4.115.0"
|
|
114
|
+
},
|
|
115
|
+
"main": "./dist/intel/intel.js",
|
|
116
|
+
"module": "./dist/intel/intel.js",
|
|
117
|
+
"types": "./dist/intel/intel.d.ts"
|
|
118
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# The wire contract
|
|
2
|
+
|
|
3
|
+
Public, runtime-validated wire formats shared by the Intel server and the browser application.
|
|
4
|
+
|
|
5
|
+
⚠️ **This was `@anchrd/intel-contract` until #827** (**D84**). It is no longer a package of its own:
|
|
6
|
+
it lives here, inside `@anchrd/intel`, and it has **two** readers with two ways in.
|
|
7
|
+
|
|
8
|
+
| reader | how it imports |
|
|
9
|
+
|---|---|
|
|
10
|
+
| the server, `packages/intel/src` | **relative** — `../contract/node.ts`; it is the same TypeScript program |
|
|
11
|
+
| the surface, `packages/intel/ui/src` | the **`@contract` alias** — `@contract/node.ts` |
|
|
12
|
+
|
|
13
|
+
The alias exists because the surface is a separate Vite program that merely shares the directory. It
|
|
14
|
+
resolves to `../src/contract` in `ui/vite.config.ts`, `ui/vitest.config.ts` and `ui/tsconfig.json`,
|
|
15
|
+
and that one relative path is correct **both** in this tree and inside the published tarball — the
|
|
16
|
+
surface sits at `<package>/ui` in both. ⚠️ That is also why `src/contract` is in the `files`
|
|
17
|
+
whitelist as source and not only as built `dist`: a customer's `intel build` compiles these files.
|
|
18
|
+
|
|
19
|
+
⚠️ **The alias is the surface's ONLY door out of `ui/`**, and `bun run check:boundaries` enforces it
|
|
20
|
+
by name: a relative path climbing out of `ui/src`, or an import of `@anchrd/intel`, is a violation.
|
|
21
|
+
Before #827 the package boundary did that on its own.
|
|
22
|
+
|
|
23
|
+
- Export schemas and inferred types from the file of their domain; do not add barrels.
|
|
24
|
+
- Inputs are `z.strictObject` schemas. A misspelled field must fail at the boundary.
|
|
25
|
+
- Keep business rules and authorization out of these files.
|
|
26
|
+
- Add a schema only when at least two surfaces consume it — the server, the browser application, or
|
|
27
|
+
an external caller.
|
|
28
|
+
- Every schema change needs parsing tests for valid, malformed, and unknown-field cases.
|
|
29
|
+
- **A field in a tool input schema without `.describe()` is unfinished**, and `mcp.e2e.ts` says so.
|
|
30
|
+
|
|
31
|
+
## ⚠️ The description belongs to the field, and the field lives here
|
|
32
|
+
|
|
33
|
+
A good tool name gets a model to the right **tool**; a described parameter gets it to the right
|
|
34
|
+
**call**. The distance between those two is where the failures live, and they arrive as empty
|
|
35
|
+
results rather than as exceptions — which is why nobody reports them and why this went unnoticed
|
|
36
|
+
until #398: **one** of 119 fields carried a description, and the one that did had been written eight
|
|
37
|
+
months earlier for #126 with the reasoning spelled out beside it.
|
|
38
|
+
|
|
39
|
+
The description goes on the **Zod schema in this package**, never on the tool registration in
|
|
40
|
+
`mcp.ts`. Then HTTP validation, the MCP schema and the generated types all carry the same sentence
|
|
41
|
+
and there is no second place to drift. `mcp.e2e.ts` asserts it over the whole surface at once, so a
|
|
42
|
+
new field is red on the day it lands rather than when somebody remembers to extend a list.
|
|
43
|
+
|
|
44
|
+
**What a description does:**
|
|
45
|
+
|
|
46
|
+
- says what the field **means**, not what type it is — the type is already there;
|
|
47
|
+
- says **where the value comes from** when it is another call's answer ("the `versionId` from
|
|
48
|
+
`node_version_list`");
|
|
49
|
+
- says **what happens if it is left out**, for an optional field;
|
|
50
|
+
- says **what goes wrong** where that is not obvious — the exact place a model would otherwise guess.
|
|
51
|
+
|
|
52
|
+
**What it must not do** is restate the field name in prose. `nodeId: "The ID of the node"` costs
|
|
53
|
+
bytes and teaches nothing. ⚠️ **An honest gap beats a filler sentence**: `tools/list` is sent on
|
|
54
|
+
every connection, and the descriptions added in #398 grew it from 47 222 to 65 026 bytes. That is
|
|
55
|
+
worth paying for 118 sentences that answer a question; it is not worth paying for paraphrase.
|
|
56
|
+
|
|
57
|
+
⚠️ **A word that means the same thing everywhere gets ONE described primitive**, not the same
|
|
58
|
+
sentence twenty times — `IdempotencyKey` in `contract.ts` is the example. Twenty copies are twenty
|
|
59
|
+
places to drift, and every one of them ships in the listing.
|
|
60
|
+
|
|
61
|
+
## ⚠️ One domain, one file — and why the split has to stay
|
|
62
|
+
|
|
63
|
+
There is **no barrel**, and `biome.json` enforces it (`noBarrelFile: error`, `noReExportAll: error`).
|
|
64
|
+
Every domain owns a file, and every reader names that file:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
contract.ts the shared primitives, and nothing else
|
|
68
|
+
node.ts table.ts share.ts tool.ts flow.ts flow-run.ts bundle.ts audit.ts board.ts feed.ts
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Until anchrd/intel#394 there was one file with one door, and a package with one door and no barrel
|
|
72
|
+
**must** put everything in it. `contract.ts` was 2219 lines, and it was not carelessness — it was the
|
|
73
|
+
bill.
|
|
74
|
+
|
|
75
|
+
⚠️ **Until #827 those files were also `exports` subpaths of a published package** — `@anchrd/intel-
|
|
76
|
+
contract/node` and its siblings — and a comment about "a subpath is not a barrel" belonged here. They
|
|
77
|
+
are not exported any more, because nothing outside `@anchrd/intel` reads them: `agents` and `signals`
|
|
78
|
+
talk to Intel over HTTP and MCP (ADR-0007), and the installation only ever imported
|
|
79
|
+
`@anchrd/intel/cloudflare`. Adding a public `./contract*` export back is a YAGNI violation until a
|
|
80
|
+
consumer exists; adding one then is one line.
|
|
81
|
+
|
|
82
|
+
Three things follow, and none is optional:
|
|
83
|
+
|
|
84
|
+
- **A new schema goes in the file of its domain**, and a domain with no file gets one. Adding it to
|
|
85
|
+
`contract.ts` because that is where the imports already point re-grows the file this split paid
|
|
86
|
+
for.
|
|
87
|
+
- **`contract.ts` holds what every domain needs and nothing else** — `IntelId`, `IsoDateTime`, the
|
|
88
|
+
handful of primitives. It re-exports nothing. If a schema in it is used by exactly one domain, it
|
|
89
|
+
is in the wrong file.
|
|
90
|
+
- **An import names the narrowest file that has the symbol.** `flow-run.ts` over `flow.ts`,
|
|
91
|
+
`flow.ts` over `contract.ts`. That is what makes a change to the flow schemas cost a rebuild of the
|
|
92
|
+
files that read flows, and nothing else.
|
|
93
|
+
|
|
94
|
+
⚠️ **The dependency graph between these files has to stay acyclic**, and it is easy to break by
|
|
95
|
+
accident: a name mentioned in a COMMENT looks like a dependency to anything that scans for one. The
|
|
96
|
+
graph today is `table → node`, `flow → node`, `flow-run → flow`, and everything → `contract.ts`. If
|
|
97
|
+
two domains genuinely need each other, the shared part belongs in `contract.ts`.
|
|
98
|
+
|
|
99
|
+
⚠️ `flow → node` was added in #376 and is worth the edge it costs: `RelationNodeKind` used to write
|
|
100
|
+
the node kinds out by hand, and when D66 added two of them the copy did not follow — the relation
|
|
101
|
+
graph would have gone on answering about four kinds while the tree held six, silently. It is now
|
|
102
|
+
`z.enum([...NodeKind.options, "flow"])`. **A derived union beats an edge**, and the extra member is
|
|
103
|
+
exactly why `check:boundaries` never caught the copy: the rule there refuses a union that only
|
|
104
|
+
restates `NodeKind`, and this one had one word more.
|
|
105
|
+
|
|
106
|
+
⚠️ **These files ship twice, and both copies are load-bearing.** `tsc -p tsconfig.build.json` emits
|
|
107
|
+
them into `dist/contract/` for the server, and the `files` whitelist ships `src/contract/` verbatim
|
|
108
|
+
for the surface's own Vite build. A new file here needs neither entry changed — both are directory
|
|
109
|
+
whitelists — but `bun run test:pack` installs the real tarball and names it if one of them stops
|
|
110
|
+
arriving.
|
|
111
|
+
|
|
112
|
+
## ⚠️ `nullable()` is not `optional()`, and the caller who confuses them reads the wrong sentence
|
|
113
|
+
|
|
114
|
+
A field declared `nullable()` and not `optional()` is **required**: `null` is a value it takes, and
|
|
115
|
+
leaving it out is refused. Both halves are deliberate wherever a write names the version it was made
|
|
116
|
+
against — `SaveNodeVersionInput`, `SaveAttachmentInput`, `SaveFlowVersionInput`. `null` is the only
|
|
117
|
+
way to write the first content of a node that has none, and the field stays required so that a
|
|
118
|
+
caller who simply forgot it is refused instead of overwriting whatever somebody else wrote in
|
|
119
|
+
between.
|
|
120
|
+
|
|
121
|
+
⚠️ **The trap is not the rule, it is what the refusal says.** Zod answers a missing required field
|
|
122
|
+
with `Invalid input: expected string, received undefined`, and to somebody holding a node created one
|
|
123
|
+
call earlier that reads as "this field needs an id" — about a field that cannot have one yet. #437
|
|
124
|
+
was opened as "the first version of a document cannot be set through the API" for exactly that
|
|
125
|
+
reason, and it was never true: the caller had tried `undefined` and `""`, never `null`.
|
|
126
|
+
|
|
127
|
+
**A required-nullable field therefore carries its own refusal**, one sentence naming both values it
|
|
128
|
+
takes, on the schema in this package so that HTTP, MCP and the generated types all say it:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
z.union([IntelId.min(1, { error: rule }), z.null()], { error: rule })
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
⚠️ **It is `baseVersion(rule)` in `contract.ts`, and all three writers take it** (#459). It lived
|
|
135
|
+
beside the two node writers until the flow graph became the third — the point at which this
|
|
136
|
+
repository stops copying and names the thing. `flow.ts` could not have imported it from `node.ts`
|
|
137
|
+
anyway: the graph is `table → node`, `flow-run → flow`, and everything → `contract.ts`, so the move
|
|
138
|
+
was the only shape that did not either break the graph or leave one sentence in two places to drift.
|
|
139
|
+
|
|
140
|
+
⚠️ **The sentence says what the field TAKES, not what the caller did wrong.** It answers a wrong
|
|
141
|
+
type as well as an absent field, and "is required" would be false about the first — the same kind of
|
|
142
|
+
misdirection one corner further on. `baseVersionId takes the currentVersionId from node_get, or null
|
|
143
|
+
when the node has no version yet — and it has to be sent.`
|
|
144
|
+
|
|
145
|
+
The repeated `min(1)` adds no bound — it puts the sentence beside Zod's own `Too small` for the empty
|
|
146
|
+
string, which is the second thing a caller tries after `undefined`. The JSON Schema it produces is
|
|
147
|
+
the one `IntelId.nullable()` produced (`anyOf` of the string with its bounds and `null`, still
|
|
148
|
+
`required`), so nothing on the MCP surface moves.
|
|
149
|
+
|
|
150
|
+
⚠️ **Making the field `optional()` instead is the repair that breaks the guard.** An absent field
|
|
151
|
+
would then have to mean "there is no version", and the forgetful caller silently replaces the newest
|
|
152
|
+
one — the very overwrite the field exists to refuse.
|