datanotes 2.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/NOTICE +9 -0
  2. package/README.md +203 -28
  3. package/SECURITY.md +92 -45
  4. package/dist/THIRD-PARTY-NOTICES.txt +682 -0
  5. package/dist/api/catalog.d.cts +69 -0
  6. package/dist/api/catalog.d.ts +50 -2
  7. package/dist/api/cli-options.d.cts +39 -0
  8. package/dist/api/cli-options.d.ts +39 -0
  9. package/dist/api/client.d.cts +122 -0
  10. package/dist/api/client.d.ts +56 -34
  11. package/dist/api/docs.d.cts +211 -0
  12. package/dist/api/docs.d.ts +211 -0
  13. package/dist/api/guidance.d.cts +81 -0
  14. package/dist/api/guidance.d.ts +81 -0
  15. package/dist/api/index.cjs +7470 -2771
  16. package/dist/api/index.d.cts +14 -0
  17. package/dist/api/index.d.ts +11 -7
  18. package/dist/api/index.js +7470 -2771
  19. package/dist/api/local.cjs +118 -6
  20. package/dist/api/local.d.cts +95 -0
  21. package/dist/api/local.d.ts +70 -6
  22. package/dist/api/local.js +104 -6
  23. package/dist/api/mcp.cjs +17387 -0
  24. package/dist/api/mcp.d.cts +108 -0
  25. package/dist/api/mcp.d.ts +108 -0
  26. package/dist/api/mcp.js +17337 -0
  27. package/dist/api/otlp.d.cts +113 -0
  28. package/dist/api/otlp.d.ts +63 -15
  29. package/dist/api/redact.d.cts +42 -0
  30. package/dist/api/redact.d.ts +42 -0
  31. package/dist/api/requests.d.cts +26 -0
  32. package/dist/api/requests.d.ts +14 -16
  33. package/dist/api/results.d.cts +16 -0
  34. package/dist/api/results.d.ts +2 -1
  35. package/dist/api/tokens.d.cts +12 -0
  36. package/dist/api/tokens.d.ts +5 -0
  37. package/dist/api/tracing.d.cts +107 -0
  38. package/dist/api/tracing.d.ts +107 -0
  39. package/dist/datanotes.cjs +69597 -28329
  40. package/dist/docs/administration/configuration.md +59 -0
  41. package/dist/docs/administration/docker.md +80 -0
  42. package/dist/docs/administration/git-sync.md +71 -0
  43. package/dist/docs/administration/limits.md +153 -0
  44. package/dist/docs/administration/run-the-service.md +237 -0
  45. package/dist/docs/administration/status.md +61 -0
  46. package/dist/docs/administration/use-with-obsidian.md +75 -0
  47. package/dist/docs/api/operations/add_column.md +49 -0
  48. package/dist/docs/api/operations/add_constraint.md +55 -0
  49. package/dist/docs/api/operations/adopt_rows.md +50 -0
  50. package/dist/docs/api/operations/align_relations.md +41 -0
  51. package/dist/docs/api/operations/alter_column.md +46 -0
  52. package/dist/docs/api/operations/alter_table.md +67 -0
  53. package/dist/docs/api/operations/batch.md +48 -0
  54. package/dist/docs/api/operations/changes_stream.md +28 -0
  55. package/dist/docs/api/operations/check_table.md +29 -0
  56. package/dist/docs/api/operations/clone_table.md +48 -0
  57. package/dist/docs/api/operations/comment_on.md +42 -0
  58. package/dist/docs/api/operations/configure_database.md +52 -0
  59. package/dist/docs/api/operations/create_table.md +78 -0
  60. package/dist/docs/api/operations/create_view.md +54 -0
  61. package/dist/docs/api/operations/create_webhook.md +37 -0
  62. package/dist/docs/api/operations/define_base.md +52 -0
  63. package/dist/docs/api/operations/delete_rows.md +43 -0
  64. package/dist/docs/api/operations/delete_webhook.md +31 -0
  65. package/dist/docs/api/operations/describe_database.md +30 -0
  66. package/dist/docs/api/operations/describe_history.md +40 -0
  67. package/dist/docs/api/operations/describe_semantic_index.md +24 -0
  68. package/dist/docs/api/operations/describe_table.md +38 -0
  69. package/dist/docs/api/operations/docs.md +13 -0
  70. package/dist/docs/api/operations/drop_column.md +41 -0
  71. package/dist/docs/api/operations/drop_constraint.md +39 -0
  72. package/dist/docs/api/operations/drop_table.md +43 -0
  73. package/dist/docs/api/operations/drop_view.md +38 -0
  74. package/dist/docs/api/operations/engine_status.md +23 -0
  75. package/dist/docs/api/operations/graph_neighbors.md +34 -0
  76. package/dist/docs/api/operations/graph_path.md +32 -0
  77. package/dist/docs/api/operations/graph_traverse.md +43 -0
  78. package/dist/docs/api/operations/identify_session.md +24 -0
  79. package/dist/docs/api/operations/infer_table.md +43 -0
  80. package/dist/docs/api/operations/insert_row.md +48 -0
  81. package/dist/docs/api/operations/list_changes.md +35 -0
  82. package/dist/docs/api/operations/list_folder.md +28 -0
  83. package/dist/docs/api/operations/list_link_targets.md +44 -0
  84. package/dist/docs/api/operations/list_migrations.md +35 -0
  85. package/dist/docs/api/operations/list_tables.md +32 -0
  86. package/dist/docs/api/operations/list_webhooks.md +23 -0
  87. package/dist/docs/api/operations/manual_index.md +14 -0
  88. package/dist/docs/api/operations/manual_page.md +22 -0
  89. package/dist/docs/api/operations/merge_rows.md +44 -0
  90. package/dist/docs/api/operations/move_rows.md +52 -0
  91. package/dist/docs/api/operations/openapi.md +13 -0
  92. package/dist/docs/api/operations/openapi_post.md +20 -0
  93. package/dist/docs/api/operations/read_note.md +29 -0
  94. package/dist/docs/api/operations/recompute_columns.md +44 -0
  95. package/dist/docs/api/operations/record_tool_call.md +37 -0
  96. package/dist/docs/api/operations/rename_column.md +44 -0
  97. package/dist/docs/api/operations/rename_value.md +45 -0
  98. package/dist/docs/api/operations/restore_database.md +51 -0
  99. package/dist/docs/api/operations/restore_table.md +46 -0
  100. package/dist/docs/api/operations/search_notes.md +28 -0
  101. package/dist/docs/api/operations/select_rows.md +61 -0
  102. package/dist/docs/api/operations/semantic_search.md +34 -0
  103. package/dist/docs/api/operations/semantic_search_docs.md +26 -0
  104. package/dist/docs/api/operations/set_table_bases.md +44 -0
  105. package/dist/docs/api/operations/status.md +13 -0
  106. package/dist/docs/api/operations/stop_engine.md +23 -0
  107. package/dist/docs/api/operations/sync_semantic_index.md +33 -0
  108. package/dist/docs/api/operations/table_changes.md +31 -0
  109. package/dist/docs/api/operations/tags/auxiliary.md +14 -0
  110. package/dist/docs/api/operations/tags/change-feed.md +8 -0
  111. package/dist/docs/api/operations/tags/history.md +17 -0
  112. package/dist/docs/api/operations/tags/queries.md +15 -0
  113. package/dist/docs/api/operations/tags/rows-dml.md +16 -0
  114. package/dist/docs/api/operations/tags/schema-ddl.md +22 -0
  115. package/dist/docs/api/operations/tags/service.md +15 -0
  116. package/dist/docs/api/operations/tags/tokens.md +13 -0
  117. package/dist/docs/api/operations/tokens_create.md +34 -0
  118. package/dist/docs/api/operations/tokens_delete.md +29 -0
  119. package/dist/docs/api/operations/tokens_list.md +21 -0
  120. package/dist/docs/api/operations/tokens_me.md +20 -0
  121. package/dist/docs/api/operations/tokens_rotate.md +29 -0
  122. package/dist/docs/api/operations/tokens_update.md +34 -0
  123. package/dist/docs/api/operations/undo_migration.md +46 -0
  124. package/dist/docs/api/operations/update_row.md +60 -0
  125. package/dist/docs/api/operations/update_rows.md +46 -0
  126. package/dist/docs/api/operations/vacuum_table.md +37 -0
  127. package/dist/docs/api.md +135 -0
  128. package/dist/docs/develop/build-an-app.md +320 -0
  129. package/dist/docs/develop/build-on-datanotes.md +138 -0
  130. package/dist/docs/develop/change-feed-and-webhooks.md +70 -0
  131. package/dist/docs/develop/client.md +116 -0
  132. package/dist/docs/develop/concurrency.md +188 -0
  133. package/dist/docs/develop/http-api.md +89 -0
  134. package/dist/docs/develop/telemetry.md +285 -0
  135. package/dist/docs/guides/agents/docs-offline.md +99 -0
  136. package/dist/docs/guides/agents/how-agents-use-datanotes.md +124 -0
  137. package/dist/docs/guides/agents/prompts.md +495 -0
  138. package/dist/docs/guides/agents/reading-errors.md +112 -0
  139. package/dist/docs/guides/data-quality/check-constraints.md +379 -0
  140. package/dist/docs/guides/data-quality/check-table.md +63 -0
  141. package/dist/docs/guides/data-quality/hand-edits.md +507 -0
  142. package/dist/docs/guides/data-quality/sync-between-devices.md +51 -0
  143. package/dist/docs/guides/data-quality/validation.md +90 -0
  144. package/dist/docs/guides/database-objects/bases.md +480 -0
  145. package/dist/docs/guides/database-objects/catalogs-and-schemas.md +897 -0
  146. package/dist/docs/guides/database-objects/columns.md +1332 -0
  147. package/dist/docs/guides/database-objects/relations.md +1454 -0
  148. package/dist/docs/guides/database-objects/tables.md +1340 -0
  149. package/dist/docs/guides/database-objects/views.md +1003 -0
  150. package/dist/docs/guides/history/back-up.md +203 -0
  151. package/dist/docs/guides/history/change-data-feed.md +247 -0
  152. package/dist/docs/guides/history/journal-and-undo.md +262 -0
  153. package/dist/docs/guides/history/restore.md +341 -0
  154. package/dist/docs/guides/history/table-history.md +657 -0
  155. package/dist/docs/guides/history/vacuum.md +26 -0
  156. package/dist/docs/guides/read-and-write/automatic-links.md +768 -0
  157. package/dist/docs/guides/read-and-write/batch.md +139 -0
  158. package/dist/docs/guides/read-and-write/import-notes.md +896 -0
  159. package/dist/docs/guides/read-and-write/joins-and-aggregates.md +251 -0
  160. package/dist/docs/guides/read-and-write/read-and-filter.md +536 -0
  161. package/dist/docs/guides/read-and-write/write-rows.md +573 -0
  162. package/dist/docs/guides/search/link-graph.md +241 -0
  163. package/dist/docs/guides/search/semantic.md +133 -0
  164. package/dist/docs/guides/search/text.md +142 -0
  165. package/dist/docs/guides/security/security-model.md +110 -0
  166. package/dist/docs/guides/security/tokens.md +60 -0
  167. package/dist/docs/guides/security/who-did-what.md +44 -0
  168. package/dist/docs/index.md +113 -0
  169. package/dist/docs/llms.txt +33 -0
  170. package/dist/docs/reference/cli.md +143 -0
  171. package/dist/docs/reference/compatibility.md +83 -0
  172. package/dist/docs/reference/errors.md +72 -0
  173. package/dist/docs/reference/glossary.md +161 -0
  174. package/dist/docs/reference/operations/auxiliary.md +116 -0
  175. package/dist/docs/reference/operations/history.md +168 -0
  176. package/dist/docs/reference/operations/queries.md +169 -0
  177. package/dist/docs/reference/operations/rows.md +185 -0
  178. package/dist/docs/reference/operations/schema.md +316 -0
  179. package/dist/docs/reference/operations.md +96 -0
  180. package/dist/docs/reference/settings.md +43 -0
  181. package/dist/docs/release-notes.md +188 -0
  182. package/dist/docs/start/connect-agents.md +204 -0
  183. package/dist/docs/start/how-it-works.md +349 -0
  184. package/dist/docs/start/quick-start.md +128 -0
  185. package/package.json +60 -15
@@ -0,0 +1,495 @@
1
+ # Prompts for your agent
2
+
3
+ > Copy prompts for everyday notes, schema changes, history and imports, with the tools the agent calls and the same calls from code.
4
+
5
+ Once your agent is [connected](https://datanotes.dev/start/connect-agents/), you talk to your vault in plain words. This page gives prompts to copy, grouped by task. They all use one example: a diary of classic films with the tables `film`, `actor`, `genre` and `watch`.
6
+
7
+ For each prompt you see what happens, the [operations](https://datanotes.dev/reference/operations/) the agent calls, in order, and what you get. Each one ends with the same call from code, for when you’d rather script it.
8
+
9
+ The tool sequences and answers come from runs against a real engine through `datanotes mcp`, the Model Context Protocol (MCP) server. Your agent may add a read here and there, such as a second `describe_table`, but the writes are the ones shown.
10
+
11
+ ## Call the same operations from code
12
+
13
+ Every tool of the agent is an operation of the [HTTP API](https://datanotes.dev/develop/http-api/), so a script makes the calls the agent makes. The snippets on this page use one client of [`datanotes/client`](https://datanotes.dev/develop/client/):
14
+
15
+ ```ts
16
+ import { DatanotesClient } from "datanotes/client";
17
+
18
+
19
+ const db = new DatanotesClient({
20
+ url: "http://127.0.0.1:27150",
21
+ token: process.env.DATANOTES_TOKEN ?? "",
22
+ actor: { harness: "my-script" },
23
+ });
24
+ ```
25
+
26
+ `db.call(name, args)` takes the agent’s tool name and arguments, and sends the request the [HTTP API reference](https://datanotes.dev/api/) lists for it. With curl, you send the same JSON to the operation’s route, `POST /datanotes/operations/<operation>/`. The body holds the operation’s arguments, with the same names as the agent’s tool.
27
+
28
+ ## Prompts for everyday notes
29
+
30
+ These prompts read and write rows of your diary. They need the `watch` table and the `vw_to_watch` view, which the [schema prompts](#schema-prompts) create.
31
+
32
+ ### Log a viewing with where, when and who
33
+
34
+ ```text
35
+ I watched Metropolis tonight at 20:30 at the film club downtown, with
36
+ Anna. Live organ accompaniment. Five stars. Log it in my film diary.
37
+ ```
38
+
39
+ The agent finds the film, adds a `watch` row and marks the film watched, in one `batch`, so both writes succeed or fail together.
40
+
41
+ * **Tools**: `list_tables`, `describe_table` on `watch`, `select_rows` on `film` for `Metropolis`, then `batch` with `insert_row` and `update_row`
42
+ * **You get**: a new note, `main/public/watch/2026-10-09T2030.md`, and `Metropolis` with `status: watched`, `rating: 5` and `watched_on: 2026-10-09`; one `undo_migration` on the batch’s `migration_id` reverts both
43
+
44
+ From code, the batch is `POST /datanotes/operations/batch/`:
45
+
46
+ ```ts
47
+ await db.call("batch", {
48
+ summary: "Log the screening of Metropolis",
49
+ operations: [
50
+ {
51
+ op: "insert_row",
52
+ table: "watch",
53
+ values: {
54
+ film: "[[Metropolis]]",
55
+ occurred_at: "2026-10-09T20:30:00",
56
+ where: "cinema",
57
+ place: "the film club downtown",
58
+ with: "Anna",
59
+ },
60
+ body: "Live organ accompaniment.\n",
61
+ },
62
+ {
63
+ op: "update_row",
64
+ table: "film",
65
+ path: "main/public/film/Metropolis.md",
66
+ set: { status: "watched", rating: 5, watched_on: "2026-10-09" },
67
+ },
68
+ ],
69
+ });
70
+ ```
71
+
72
+ ### Mark a film to watch
73
+
74
+ ```text
75
+ Put Battleship Potemkin on my watchlist: 1925, 75 minutes, directed by
76
+ Sergei Eisenstein, Soviet montage.
77
+ ```
78
+
79
+ The agent matches “watchlist” against the aliases of the `film` table, checks that the film isn’t there yet, and adds it.
80
+
81
+ * **Tools**: `list_tables`, `select_rows` on `film` for the title, then `insert_row`
82
+ * **You get**: `main/public/film/Battleship Potemkin.md`, with `status: to watch` and a link to the `Soviet montage` genre
83
+
84
+ From code, `POST /datanotes/operations/insert_row/`:
85
+
86
+ ```ts
87
+ await db.call("insert_row", {
88
+ table: "film",
89
+ values: {
90
+ title: "Battleship Potemkin",
91
+ year: 1925,
92
+ minutes: 75,
93
+ director: "Sergei Eisenstein",
94
+ genre: "[[Soviet montage]]",
95
+ status: "to watch",
96
+ },
97
+ });
98
+ ```
99
+
100
+ ### Ask what to watch tonight
101
+
102
+ ```text
103
+ What should I watch tonight? I have about an hour and a half.
104
+ ```
105
+
106
+ The agent finds the view of your watchlist and narrows it to films that fit the evening.
107
+
108
+ * **Tools**: `list_tables`, which lists `vw_to_watch` under `views`, then `select_rows` on the view with a filter on `minutes`
109
+ * **You get**: Battleship Potemkin, 75 minutes; Metropolis, at 153 minutes, waits for a longer evening
110
+
111
+ From code, `POST /datanotes/operations/select_rows/`:
112
+
113
+ ```ts
114
+ const tonight = await db.call("select_rows", {
115
+ table: "vw_to_watch",
116
+ where: { minutes: { lte: 100 } },
117
+ });
118
+ ```
119
+
120
+ ### Find the films you saw with someone
121
+
122
+ ```text
123
+ Which films did I see with Anna this year?
124
+ ```
125
+
126
+ The agent reads the `watch` table, where `with` holds who came along, and matches the name case-insensitively.
127
+
128
+ * **Tools**: `list_tables`, `describe_table` on `watch`, then `select_rows` with `icontains` on `with` and `gte` on `occurred_at`
129
+ * **You get**: Sherlock Jr. at home in February, His Girl Friday at the cinema in March, Nosferatu at an outdoor screening in June, and Metropolis tonight
130
+
131
+ From code, `POST /datanotes/operations/select_rows/`:
132
+
133
+ ```ts
134
+ await db.call("select_rows", {
135
+ table: "watch",
136
+ where: { with: { icontains: "anna" }, occurred_at: { gte: "2026-01-01" } },
137
+ columns: ["occurred_at", "film", "where"],
138
+ order_by: "occurred_at",
139
+ });
140
+ ```
141
+
142
+ ### Write your review
143
+
144
+ ```text
145
+ Add my review of Charade under a "My review" heading: Grant and Hepburn
146
+ trade lines across Paris while everyone lies about the money.
147
+ ```
148
+
149
+ The agent reads the note’s body first, then writes the review under its own heading and keeps the rest of the note.
150
+
151
+ * **Tools**: `select_rows` on `film` with `include_body`, then `update_row` with `body_sections`
152
+ * **You get**: a `## My review` section at the end of `Charade.md`, below the text the note already had
153
+
154
+ From code, `POST /datanotes/operations/update_row/`:
155
+
156
+ ```ts
157
+ await db.call("update_row", {
158
+ table: "film",
159
+ path: "main/public/film/Charade.md",
160
+ body_sections: {
161
+ "My review":
162
+ "Grant and Hepburn trade lines across Paris while everyone lies " +
163
+ "about the money.",
164
+ },
165
+ });
166
+ ```
167
+
168
+ ## Schema prompts
169
+
170
+ These prompts change the tables themselves. The agent changes `metastore.yaml` only through schema operations, which check the change against the rows, journal it and make it undoable. To limit which agents may change the schema, give them a [named token](https://datanotes.dev/start/connect-agents/#give-each-agent-its-own-token) without `ddl`.
171
+
172
+ ### Create the watch table
173
+
174
+ ```text
175
+ Create a table for each time I watch a film: when, the film, where
176
+ (cinema, home or outdoor screening), the place in my words, and who came
177
+ along.
178
+ ```
179
+
180
+ The agent checks that no table records viewings yet, then creates one named by the date and time of each viewing.
181
+
182
+ * **Tools**: `list_tables`, `describe_table` on `film` to link to it, then `create_table`
183
+ * **You get**: `main.public.watch` in `metastore.yaml`, with `occurred_at` as its time column, so the engine names each note like `2026-10-09T2030.md`; its description and aliases lead an agent to it from words such as “diary”, and each column says what it holds
184
+
185
+ The engine refuses a table or a column without a description, so the agent writes one for each.
186
+
187
+ From code, `POST /datanotes/operations/create_table/`:
188
+
189
+ ```ts
190
+ await db.call("create_table", {
191
+ name: "watch",
192
+ description: "Each time I watch a film: when, where and with whom",
193
+ time_column: "occurred_at",
194
+ columns: [
195
+ {
196
+ name: "occurred_at",
197
+ type: "datetime",
198
+ required: true,
199
+ description: "When the film started",
200
+ },
201
+ {
202
+ name: "film",
203
+ type: "link",
204
+ ref: "main.public.film",
205
+ required: true,
206
+ description: "The film I watched",
207
+ },
208
+ {
209
+ name: "where",
210
+ type: "enum",
211
+ values: ["cinema", "home", "outdoor screening"],
212
+ required: true,
213
+ description: "The kind of place I watched it in",
214
+ },
215
+ { name: "place", type: "string", description: "Where, in my words" },
216
+ { name: "with", type: "string", description: "Who came along" },
217
+ ],
218
+ aliases: ["viewings", "screenings", "diary"],
219
+ });
220
+ ```
221
+
222
+ ### Add a column
223
+
224
+ ```text
225
+ Add a column to my film diary for what the ticket cost.
226
+ ```
227
+
228
+ The agent reads the table first, to reuse a column that already means the same, and adds a new one only when none does.
229
+
230
+ * **Tools**: `describe_table` on `watch`, then `add_column`
231
+ * **You get**: a `price` column, a number of 0 or more with two decimals; existing rows stay as they are, because the engine writes a sparse column only on rows that have a value
232
+
233
+ From code, `POST /datanotes/operations/add_column/`:
234
+
235
+ ```ts
236
+ await db.call("add_column", {
237
+ table: "watch",
238
+ column: {
239
+ name: "price",
240
+ type: "number",
241
+ min: 0,
242
+ step: 0.01,
243
+ description: "What the ticket cost",
244
+ },
245
+ });
246
+ ```
247
+
248
+ ### Add the CHECK rated_when_watched
249
+
250
+ ```text
251
+ A film I've watched must have a rating. Make that a rule of the film table.
252
+ ```
253
+
254
+ The agent writes the rule as a filter every row must pass: a film is either not watched, or has a rating.
255
+
256
+ * **Tools**: `describe_table` on `film`, then `add_constraint`
257
+ * **You get**: the CHECK `rated_when_watched`; the engine refuses it while a row breaks it, listing the rows, and from then on refuses any write that breaks it
258
+
259
+ Once the rule exists, the engine refuses to mark Battleship Potemkin as watched without a rating, and writes nothing:
260
+
261
+ ```text
262
+ update_row refused — nothing was written:
263
+ invalid main/public/film/Battleship Potemkin.md: rated_when_watched violates CHECK rated_when_watched: {"or":[{"status":{"ne":"watched"}},{"rating":{"exists":true}}]}
264
+ ```
265
+
266
+ The agent then asks you for a rating. From code, the CHECK is `POST /datanotes/operations/add_constraint/`:
267
+
268
+ ```bash
269
+ curl -s http://127.0.0.1:27150/datanotes/operations/add_constraint/ \
270
+ -H "Authorization: Bearer your_owner_token" \
271
+ -H "Content-Type: application/json" \
272
+ -d '{"table": "film",
273
+ "name": "rated_when_watched",
274
+ "check": {"or": [{"status": {"ne": "watched"}},
275
+ {"rating": {"exists": true}}]}}'
276
+ ```
277
+
278
+ ### Make a view of the films to watch
279
+
280
+ ```text
281
+ Make a view of the films I still have to watch, shortest first.
282
+ ```
283
+
284
+ The agent stores the query as a view, with a description and aliases so that later questions such as “what’s on my watchlist?” find it.
285
+
286
+ * **Tools**: `list_tables`, to reuse a view that exists, then `create_view`
287
+ * **You get**: `vw_to_watch` in `metastore.yaml`, which `list_tables` lists under `views` and `select_rows` reads like a table
288
+
289
+ From code, `POST /datanotes/operations/create_view/`:
290
+
291
+ ```ts
292
+ await db.call("create_view", {
293
+ name: "vw_to_watch",
294
+ query: {
295
+ table: "film",
296
+ where: { status: "to watch" },
297
+ columns: ["title", "year", "minutes", "director"],
298
+ order_by: "minutes",
299
+ },
300
+ description: "Films on my watchlist, shortest first",
301
+ aliases: ["watchlist", "what to watch"],
302
+ });
303
+ ```
304
+
305
+ ## Safety and history prompts
306
+
307
+ These prompts show what an agent changed and take it back. Every write is in the journal, so `undo_migration` works on any table. Reading a table as it was and restoring the whole database need the tables’ history. A database the engine creates has it on. A database whose `metastore.yaml` lacks the setting, such as one from a version before 3.0.0, has it off. Turn it on once:
308
+
309
+ ```text
310
+ Turn history on for every table.
311
+ ```
312
+
313
+ The agent calls `configure_database` with `history: true`. The journal doesn’t record database settings, so an agent changes them only when you ask. From code, it’s `POST /datanotes/operations/configure_database/` with `{"history": true}`.
314
+
315
+ ### Ask what the agent changed today
316
+
317
+ ```text
318
+ What did you change in my vault today?
319
+ ```
320
+
321
+ The agent reads the journal, where every operation names the agent that made it.
322
+
323
+ * **Tools**: `list_migrations` with `since`, or `describe_history` without a table for the commits of every table with their full actor
324
+ * **You get**: one line per operation, each with `by: "claude-code"`, such as `batch` “Log the screening of Metropolis” or `add_constraint` on `film`; your own writes from an editor or a script name their own source
325
+
326
+ From code, `POST /datanotes/operations/list_migrations/`:
327
+
328
+ ```ts
329
+ const today = await db.call("list_migrations", { since: "2026-10-09" });
330
+ ```
331
+
332
+ ### Undo the agent’s last change
333
+
334
+ ```text
335
+ Undo your last change.
336
+ ```
337
+
338
+ The agent finds its newest operation in the journal and reverts it.
339
+
340
+ * **Tools**: `list_migrations`, then `undo_migration` with its `migration_id`
341
+ * **You get**: the notes as they were before that operation, and the undo’s own `migration_id`, since the journal records the undo too; if you edited one of the notes since, the engine refuses and lists the conflicts instead of overwriting your edit
342
+
343
+ From code, `POST /datanotes/operations/undo_migration/`:
344
+
345
+ ```ts
346
+ await db.call("undo_migration", {
347
+ migration_id: "20261009T215306-batch-log-the-screening-of-metropoli",
348
+ });
349
+ ```
350
+
351
+ ### Put everything back as it was before the agent started
352
+
353
+ ```text
354
+ Put everything back as it was before you started this evening. Show me
355
+ what would change first.
356
+ ```
357
+
358
+ The agent finds its first operation of the session, previews the restore, and runs it once you agree.
359
+
360
+ * **Tools**: `describe_history` without a table, then `restore_database` with `before_migration` and `dry_run: true`, then `restore_database` again without `dry_run`
361
+ * **You get**: every table with history back to that moment, in one operation that `undo_migration` can revert; in the run behind this page, the restore removed Battleship Potemkin and tonight’s `watch` row, and gave Metropolis and Charade their old content back
362
+
363
+ The dry run lists what changes in each table before the engine writes anything. Here is its answer, trimmed:
364
+
365
+ ```json
366
+ {
367
+ "ok": true,
368
+ "dry_run": true,
369
+ "planned_deletes": [
370
+ "main/public/film/Battleship Potemkin.md",
371
+ "main/public/watch/2026-10-09T2030.md"
372
+ ],
373
+ "planned_texts": [
374
+ "main/public/film/Metropolis.md",
375
+ "main/public/film/Charade.md"
376
+ ],
377
+ "unchanged": ["main.public.actor", "main.public.genre"],
378
+ "skipped": []
379
+ }
380
+ ```
381
+
382
+ The restore doesn’t rewind views or database settings, and lists a table without history in `skipped`. From code, `POST /datanotes/operations/restore_database/`:
383
+
384
+ ```ts
385
+ await db.call("restore_database", {
386
+ before_migration: "20261009T215329-insert_row-insert-into-film",
387
+ dry_run: true,
388
+ });
389
+ ```
390
+
391
+ ### Read a note as it was last week
392
+
393
+ ```text
394
+ Show me Nosferatu as it was last week.
395
+ ```
396
+
397
+ The agent reads the `film` table at a past time and picks the row.
398
+
399
+ * **Tools**: `select_rows` on `film` with `timestamp`, and `describe_history` on `film` when you ask what changed since
400
+ * **You get**: the row with the values it had then, and the table `version` the engine read; if the table didn’t exist yet at that time, the engine answers `main.public.film did not exist at 2026-10-02T09:00:00Z`
401
+
402
+ From code, `POST /datanotes/operations/select_rows/`:
403
+
404
+ ```ts
405
+ await db.call("select_rows", {
406
+ table: "film",
407
+ where: { title: "Nosferatu" },
408
+ timestamp: "2026-10-02T09:00:00Z",
409
+ });
410
+ ```
411
+
412
+ ## Import prompts
413
+
414
+ This prompt turns notes you already have into rows, without moving them.
415
+
416
+ ### Turn a folder of notes into a table
417
+
418
+ ```text
419
+ Turn my Films/ folder into a table. Show me the columns you propose
420
+ before you create anything.
421
+ ```
422
+
423
+ The agent reads the notes’ front matter, proposes a table, and waits for your review. Review it: on five notes, `infer_table` marked `director` and `minutes` as `unique`, because no two notes shared a value. The engine would then refuse a sixth film by the same director, so ask the agent to drop `unique` there.
424
+
425
+ The notes can’t say what each key means, so the proposal leaves every `description` `null`. The agent writes them and asks you when a key isn’t clear. `create_table` refuses a table or a column without one.
426
+
427
+ * **Tools**: `list_folder` on `Films`, `infer_table` with `folder: "Films"`, then, after your review, `create_table` with the reviewed and described proposal, `adopt_rows` with `dry_run: true`, `adopt_rows`, and `check_table`
428
+ * **You get**: a `film` table whose rows are your notes, moved to the table’s folder with the links to them rewritten; each note gets `table` and an `id`, and keeps its body; `check_table` reports no violations
429
+
430
+ `infer_table` returns `proposal`, the arguments of `create_table`, and `adopt`, the arguments of `adopt_rows`. From code, the inference is `POST /datanotes/operations/infer_table/`:
431
+
432
+ ```ts
433
+ const inferred = await db.call("infer_table", {
434
+ folder: "Films",
435
+ name: "film",
436
+ });
437
+ const { proposal, adopt } = inferred.value as {
438
+ proposal: { columns: { name: string }[] };
439
+ adopt: Record<string, unknown>;
440
+ };
441
+ ```
442
+
443
+ After you review `proposal`, create the table. The notes keep the title in their file name, so add a `title` column with `identity: "filename"` and name the rows with `filename: "{title}"`. Write a description for every column, or `create_table` refuses the table:
444
+
445
+ ```ts
446
+ const meaning: Record<string, string> = {
447
+ year: "Year the film first came out",
448
+ minutes: "Running time in minutes",
449
+ director: "Who directed the film",
450
+ // … one for each column of the proposal
451
+ };
452
+ const created = await db.call("create_table", {
453
+ ...proposal,
454
+ description: "Classic films I watched or want to watch",
455
+ filename: "{title}",
456
+ columns: [
457
+ {
458
+ name: "title",
459
+ type: "string",
460
+ identity: "filename",
461
+ description: "The film's title, which is also its note's name",
462
+ },
463
+ ...proposal.columns.map((c) => ({
464
+ ...c,
465
+ unique: false,
466
+ description: meaning[c.name],
467
+ })),
468
+ ],
469
+ });
470
+ if (!created.ok) throw new Error(String(created.value));
471
+ ```
472
+
473
+ Then adopt the notes with `POST /datanotes/operations/adopt_rows/`. `relocate` moves them to the table’s folder and rewrites the links to them:
474
+
475
+ ```ts
476
+ const { paths, rename } = adopt as {
477
+ paths: string[];
478
+ rename: Record<string, string>;
479
+ };
480
+ const adopted = await db.call("adopt_rows", {
481
+ table: "film",
482
+ paths,
483
+ rename,
484
+ relocate: true,
485
+ });
486
+ if (!adopted.ok) throw new Error(String(adopted.value));
487
+ ```
488
+
489
+ ## Learn more about agents and operations
490
+
491
+ These pages go deeper into what the prompts use:
492
+
493
+ * [How agents work with your vault](https://datanotes.dev/guides/agents/how-agents-use-datanotes/): what the agent reads first, how it finds tables and what the journal records
494
+ * [Guides](https://datanotes.dev/guides/database-objects/tables/): every operation in use, with the engine’s real answers
495
+ * [Operations](https://datanotes.dev/reference/operations/): every tool and its arguments
@@ -0,0 +1,112 @@
1
+ # Read the engine’s errors
2
+
3
+ > Every error of the engine says what is wrong, how to fix it and where to read more: the shape of an error, the mistakes agents make most, and the operations that look up the right answer.
4
+
5
+ Every error the engine returns answers three questions, so an agent can correct its call without guessing:
6
+
7
+ 1. **What is wrong**: the column, value, rule or argument the engine refused
8
+ 2. **How to fix it**: the allowed values, the closest real name, the right shape, or the operation that looks up the answer
9
+ 3. **Where to read more**: a page of this site in Markdown, with an anchor on the section that shows the fix
10
+
11
+ This page shows how an error looks, how the engine finds the fix in your data, and the mistakes agents make most often.
12
+
13
+ ## Every error has a message, a hint and a docs link
14
+
15
+ Through the Model Context Protocol (MCP) server, `datanotes mcp`, a failed call comes back as a tool error. Its text ends with two lines, `Hint:` and `Docs:`. An agent that writes a film with the status `seen` reads:
16
+
17
+ ```text
18
+ insert_row refused — nothing was written:
19
+ invalid datanotes/main/public/film/Nosferatu.md: status must be one of: to watch (on the list, not seen yet); watched (seen at least once)
20
+ Hint: Use one of the allowed values listed in the message, written exactly; describe_table shows what each one means. To add a value, alter_column the enum. Then call insert_row again (dry_run: true checks without writing).
21
+ Docs: read_docs("guides/database-objects/columns#describe-what-each-enum-value-means")
22
+ ```
23
+
24
+ Over HTTP, the same error is JSON, with `hint` and `docs` next to `error`:
25
+
26
+ ```json
27
+ {
28
+ "error": "Table not found: flim",
29
+ "hint": "Did you mean \"film\"? Tables: film. list_tables lists them …",
30
+ "docs": "https://datanotes.dev/guides/database-objects/tables.md#find-the-table-that-matches-a-request"
31
+ }
32
+ ```
33
+
34
+ A refused write, with status 422, keeps its usual body: the operation’s result with `ok: false` and the reasons. The engine adds `hint` and `docs` to it. In code, `DatanotesClient` appends both lines to two texts:
35
+
36
+ * **`call`**: the `value` of a failed call
37
+ * **`post`**: the message of the `DatanotesError` it throws, which also has `hint` and `docs` as fields
38
+
39
+ The `docs` link always points to a `.md` page, the Markdown version of a page of this site. Agents read Markdown best. The reference of an operation is the page of its kind, followed by an anchor with the operation’s name, such as `https://datanotes.dev/reference/operations/rows.md#insert_row`.
40
+
41
+ The `Docs:` line of the text names the page in two ways:
42
+
43
+ * **`datanotes mcp`**: only the `read_docs` call, which reads the page from the docs of the installed version. Its agents always have the docs tools, so they need no internet access and no web address.
44
+ * **`DatanotesClient` and `datanotes/mcp`**: the address, then the same `read_docs` call in parentheses. Your own MCP server may not serve the docs tools, so its agents keep the address. A server that serves them passes each result to `offlineDocs`, which keeps only the call. See [Build on Datanotes](https://datanotes.dev/develop/build-on-datanotes/).
45
+
46
+ [Read the docs offline](https://datanotes.dev/guides/agents/docs-offline/) describes the docs tools.
47
+
48
+ ## The engine finds the fix in your data
49
+
50
+ When the answer is data, the hint reads it from your database:
51
+
52
+ * **A table or view that doesn’t exist**: the closest names, by spelling, by case and by the table’s `aliases`. `movies` leads to `film`, and a short database lists every table and view.
53
+ * **A column that doesn’t exist**: the closest column and the table’s columns. This works wherever a call names a column: `values`, `where`, `columns`, `order_by`, `join`, `group_by` and `aggregates`.
54
+ * **A value outside an enum**: the allowed values, each with its meaning from `value_descriptions`.
55
+ * **A wrong operator in a filter**: the operator you meant, from a typo (`gtee`) or another language’s word (`>=`, `like`, `not_in`, `is_null`). Then every operator.
56
+ * **A link to a row that doesn’t exist**: `list_link_targets`, which lists the rows the link column accepts.
57
+ * **A row that doesn’t exist**: the row with the closest note name, and how to find a row’s `path` or `id`.
58
+ * **A write to a view**: the view’s own table, since you can only read a view.
59
+
60
+ ## The MCP server checks a call before the engine runs it
61
+
62
+ `datanotes mcp` checks a call against the tool’s input schema before it sends anything. It catches four mistakes:
63
+
64
+ * A tool that doesn’t exist
65
+ * An argument the tool doesn’t take
66
+ * A required argument that is missing
67
+ * A value of the wrong type
68
+
69
+ Each comes back as a tool error that lists the tool’s arguments with their types and links the tool’s reference:
70
+
71
+ ```text
72
+ select_rows: unknown argument "wehre"
73
+ Hint: For "wehre" use "where". select_rows takes: table (string, required), columns (list of strings), where (object), …
74
+ Docs: read_docs("reference/operations/queries#select_rows")
75
+ ```
76
+
77
+ Your own MCP server gets the same checks from `callEngineTool` in `datanotes/mcp`. A program checks arguments with `checkArguments` from `datanotes/client`.
78
+
79
+ ## Fix the mistakes agents make most
80
+
81
+ | Mistake | What the agent reads | The fix |
82
+ | ------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------ |
83
+ | A misspelled table, `flim` | `Table not found: flim` and `Did you mean "film"?` | Use the name given, or call `list_tables` |
84
+ | A misspelled column, `titel` | `unknown column "titel"` and `Did you mean "title"?` | Use the name given; `describe_table` lists the columns |
85
+ | A value outside an enum, `seen` | The allowed values and their meanings | Pick one, or ask the user which one they meant |
86
+ | A wrong operator, `">="` | `For ">=" use "gte"` and every operator | Write `{"rating": {"gte": 4}}` |
87
+ | A value of the wrong type | `minutes expected a number` | Send numbers unquoted and dates as `YYYY-MM-DD` |
88
+ | A row the table’s CHECK refuses | `violates CHECK rated_when_watched` and the rule | Change the values so the rule holds |
89
+ | A row path that doesn’t exist | `film has no row at …` and the closest row | Use the path given, or find it with `select_rows` |
90
+ | An argument the tool doesn’t take, `wehre` | `For "wehre" use "where"` and the tool’s arguments | Rename the argument |
91
+ | A write to a view | The view’s table | Write to the table, and read the view |
92
+ | An undo blocked by later changes | Which notes changed since | Undo the later writes first, newest first |
93
+
94
+ ## Look up the answer before you write
95
+
96
+ The hints name these operations because each one answers a whole class of mistakes:
97
+
98
+ * **`list_tables`**: every table and view, with descriptions and aliases
99
+ * **`describe_table`**: a table’s columns, types, limits, enum values with their meanings, and CHECK constraints
100
+ * **`describe_database`**: catalogs, schemas, bases and the database’s settings
101
+ * **`list_link_targets`**: the rows a link column may point to, filtered by its `ref_where` and `ref_by`
102
+ * **`check_table`**: the rows that break the schema, such as notes edited by hand
103
+ * **`list_migrations`** and **`describe_history`**: the writes to undo, and a table’s versions
104
+
105
+ A refused write changes nothing, so the agent can fix the call and send it again. To check a write without making it, pass `dry_run: true`. To make several writes succeed or fail together, send them in one `batch`.
106
+
107
+ ## Learn more about operations and refusals
108
+
109
+ * [How agents work with your vault](https://datanotes.dev/guides/agents/how-agents-use-datanotes/): what an agent reads first, and how the journal records its writes
110
+ * [Operations and MCP tools](https://datanotes.dev/reference/operations/): every operation with its arguments
111
+ * [See how the engine refuses bad values](https://datanotes.dev/guides/data-quality/validation/#see-how-the-engine-refuses-bad-values): refusals on a real engine
112
+ * [Read errors over HTTP](https://datanotes.dev/develop/http-api/#read-errors): the status codes of the HTTP API