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