mindoodb-app-sdk 0.0.73 → 0.0.74
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 +52 -8
- package/dist/index.js +10 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -266,7 +266,7 @@ Haven uses the app's registration label as the headline and ignores any title fr
|
|
|
266
266
|
|
|
267
267
|
An app can offer its own operations to AI agents. Haven registers them with the browser's agent interface ([WebMCP](https://webmachinelearning.github.io/webmcp/)) next to Haven's own tools, so an agent in the browser — or a local agent connected through a WebMCP bridge — can call "create a mind map", "list open tasks" or "attach this file" instead of clicking through the UI.
|
|
268
268
|
|
|
269
|
-
The app declares tools; `execute` runs inside the app. Haven validates the declarations, prefixes the names with the app's tool prefix (`
|
|
269
|
+
The app declares tools; `execute` runs inside the app. Haven validates the declarations, prefixes the names with the app's tool prefix (`todo_list` for `"agentToolPrefix": "todo"` in `haven-app.json`; without it Haven derives one from the app id or label), checks permission, asks the user before consequential calls, and forwards each call over the bridge. The tools disappear when the app closes.
|
|
270
270
|
|
|
271
271
|
Agents only see an app's tools while the user has **Agent tools** switched on in Haven (Settings → General) **and** has allowed this app (**Offer tools to AI agents** in the app's settings). Neither can be set by `haven-app.json`. `registerTools()` works either way; `enabled` in its answer says whether agents can call the tools right now.
|
|
272
272
|
|
|
@@ -420,7 +420,7 @@ Agents only see an app's tools while it runs. To pick the right app first, they
|
|
|
420
420
|
- **`summary`**: neutral, 1-3 sentences, at most 400 characters per locale; `en` is required.
|
|
421
421
|
- **`capabilities`**: 1-8 entries; `tools` names the tools as in `registerTools` (no prefix, no `haven_*`). Together they should cover every tool.
|
|
422
422
|
- **`examples`**, **`limits`**: up to 8 strings per locale.
|
|
423
|
-
- **`guide`**: Markdown how-to per locale, a path relative to `haven-app.json` (at most 8 KB): typical steps, where ids come from, pitfalls. Parameter tables belong in the tools (or `ops_guide`), not here.
|
|
423
|
+
- **`guide`**: Markdown how-to per locale, a path relative to `haven-app.json` (at most 8 KB): typical steps, where ids come from, pitfalls. Parameter tables belong in the tools (or `ops_guide`), not here. Haven fetches it cross-origin, so serve it with `Access-Control-Allow-Origin: *` like `haven-app.json` (Cloudflare: an `/agent/*` rule in `public/_headers`).
|
|
424
424
|
|
|
425
425
|
Keep the profile honest with a unit test: `checkAgentManifest` from `mindoodb-app-sdk/testing` compares it with the registered tools and fails when a capability or the guide names a tool that does not exist (`assertAgentManifest` throws instead):
|
|
426
426
|
|
|
@@ -436,7 +436,27 @@ const warnings = assertAgentManifest({
|
|
|
436
436
|
expect(warnings).toEqual([]); // every tool belongs to a capability
|
|
437
437
|
```
|
|
438
438
|
|
|
439
|
-
`session.agent` is absent on hosts without agent tools, so check for it.
|
|
439
|
+
`session.agent` is absent on hosts without agent tools, so check for it. `registerTools()` answers `{ enabled, exposedNames }`: show from `enabled` whether agents can call the tools right now (and that **Offer tools to AI agents** switches them on), and use `exposedNames` when you display the names agents see. There is no event when the user changes the setting later; register again (e.g. when the app regains focus) to refresh it.
|
|
440
|
+
|
|
441
|
+
#### A recommended tool set for a data app
|
|
442
|
+
|
|
443
|
+
What the open-source SDK example app and TodoManager offer, from small to large:
|
|
444
|
+
|
|
445
|
+
- `get_context`: the open database, read-only or not, the signed-in user, today's date, what is selected. Agents call it first.
|
|
446
|
+
- `list` / `search`: exact criteria through `documents.query` filters on the summary buffer, topics through a full-text `text` clause, bounded output with `truncated`. Check `db.getFulltextSetup()` first: Haven rejects `text` with `fulltext-not-enabled` when the database has no index, while the mock answers anyway.
|
|
447
|
+
- `get`, `create`, `update`, `delete` over a small public model, never storage field names; `delete` with `consequentialHint`. Batch with lists so one call is one step.
|
|
448
|
+
- `undo` (and `history`/`redo` in larger apps): every write call is one undo step marked as made by an agent; undo refuses with `STATE_CHANGED` when the document changed since, so agents never overwrite user work.
|
|
449
|
+
- `users_search` when the app stores people: look names up in the tenant directory (`directory` permission) and store the directory id.
|
|
450
|
+
|
|
451
|
+
Small reference: `src/features/agent/` and `src/app/useAgentSection.ts` in [mindoodb-app-example](https://github.com/klehmann/mindoodb-app-example) (eight tools, undo, profile test). Full-size: `src/features/agent/` in [mindoodb-app-todomanager](https://github.com/klehmann/mindoodb-app-todomanager) (search, undo/redo/history, directory lookup, agent profile). Both are open source; copy from them.
|
|
452
|
+
|
|
453
|
+
#### Testing agent tools
|
|
454
|
+
|
|
455
|
+
- **Unit**: call `execute` directly with a database from `createMockMindooDBAppSession`.
|
|
456
|
+
- **End to end**: `createFakeBridgeHost({ agentToolPrefix, agentToolsEnabled, agentConsent })` connects the real bridge; `host.agent.tools()`, `host.agent.context()` and `host.agent.call(name, input)` act as the agent, and `call` answers `{ ok, result }` or `{ ok: false, error }`; `agentConsent: "deny"` plays a user who declines a `consequentialHint` call (`NOT_ALLOWED`).
|
|
457
|
+
- **In the browser**: the `/__haven-test/` page (`mountHavenTestHost`) lists the tools with a call button. Pass `agentToolPrefix: definition.agentToolPrefix`; without it the page derives the prefix from the app id, so the names differ from Haven's.
|
|
458
|
+
|
|
459
|
+
How to design tools that agents use well is in the [best practices guide](https://github.com/klehmann/MindooDB/blob/main/docs/best-practices.md) (section "Offer operations to AI agents").
|
|
440
460
|
|
|
441
461
|
### Host-owned drag
|
|
442
462
|
|
|
@@ -740,6 +760,30 @@ answers like a current Haven, so the SDK frames that URL in the container. Both
|
|
|
740
760
|
controllers simulate unsaved changes: `setEmbedDirty`, `failNextEmbedSave`, and for an
|
|
741
761
|
app under test that is a component, `getEmbeddingDirty` and `requestEmbeddingSave`.
|
|
742
762
|
|
|
763
|
+
**Host checklist.** What a host needs beyond the calls above (the "Embedded editors" tab of
|
|
764
|
+
[mindoodb-app-example](https://github.com/klehmann/mindoodb-app-example) is a small
|
|
765
|
+
complete host, `src/app/useEmbedsSection.ts`):
|
|
766
|
+
|
|
767
|
+
- `embeds.open` rejects with an `Error` whose `name` says why: `declined` (the user said
|
|
768
|
+
no), `component-not-found` (the app is not installed or no longer offers it),
|
|
769
|
+
`forbidden`, `database-not-found`, `document-not-found`, `invalid-request`,
|
|
770
|
+
`unsupported` (a Haven without components). Explain these to the user.
|
|
771
|
+
- `requestAccess` never asks again for what the user put off; Haven asks for it when the
|
|
772
|
+
document is opened. So say "Haven asks when you open one" instead of offering "ask again".
|
|
773
|
+
- `createFields` may contain nested values; copy it with a JSON round trip, not
|
|
774
|
+
`structuredClone`, when the component sits in Vue reactive state.
|
|
775
|
+
- Keep fields of your own on the root top-level and prefixed (`exampleEditor`), so
|
|
776
|
+
queries find them without `setSummarySetup`; nested fields (`crm.folders`) need a summary
|
|
777
|
+
setup first.
|
|
778
|
+
- Copy the component's `children` link onto the root (or remember it) so you can still
|
|
779
|
+
delete a document with all its children after the providing app was uninstalled.
|
|
780
|
+
- Keep the container mounted while the editor should live: removing the element (a tab
|
|
781
|
+
rendered with `v-if`) closes the editor. Hide it with `v-show` and
|
|
782
|
+
`embed.setVisible(false)` instead.
|
|
783
|
+
- With `placement === "overlay"` the SDK follows the container on resize and scroll, not
|
|
784
|
+
when content above it changes height; keep banners and confirm bars out of the space
|
|
785
|
+
above an open editor.
|
|
786
|
+
|
|
743
787
|
A host with agent tools marks the component in the front tab with `embed.setActive()`
|
|
744
788
|
and reads `embed.agentTools`; see *Agent tools of embedded components*.
|
|
745
789
|
|
|
@@ -1065,7 +1109,7 @@ An id in `dependencyIds` may name a revision the timeline does not list, because
|
|
|
1065
1109
|
|
|
1066
1110
|
### Granular JSON edits
|
|
1067
1111
|
|
|
1068
|
-
When you need to edit nested objects or lists inside a document — not just top-level fields — describe the change as a **granular JSON patch** rather than rewriting the whole document. This is the model behind structured collaborative editing in apps like
|
|
1112
|
+
When you need to edit nested objects or lists inside a document — not just top-level fields — describe the change as a **granular JSON patch** rather than rewriting the whole document. This is the model behind structured collaborative editing in apps like TeamGrid, where two users can insert different rows into the same worksheet on different devices and the merge keeps both sides cleanly.
|
|
1069
1113
|
|
|
1070
1114
|
A JSON patch is a bag of four operation kinds applied at specific JSON paths inside `document.data`, all carried with the document version (`baseHeads`) the app was looking at when it composed them:
|
|
1071
1115
|
|
|
@@ -1366,11 +1410,11 @@ Buffer API at a glance:
|
|
|
1366
1410
|
|
|
1367
1411
|
#### End-to-end sample
|
|
1368
1412
|
|
|
1369
|
-
|
|
1413
|
+
TeamEdit wires `MindooDBTextBuffer` to a Milkdown markdown editor with Save / Refresh / preview pane; its code is not public, so build on the snippets above.
|
|
1370
1414
|
|
|
1371
1415
|
### Collaborative rich-text editing
|
|
1372
1416
|
|
|
1373
|
-
For structured documents that need formatting — headings, bold/italic runs, lists, tables, fonts, colors — use the **rich-text patch API**. This is the surface behind
|
|
1417
|
+
For structured documents that need formatting — headings, bold/italic runs, lists, tables, fonts, colors — use the **rich-text patch API**. This is the surface behind TeamEdit's Word/.docx mode, where two users can edit the same document at the same time and Haven merges both formatting and structure on its side via Automerge's rich-text CRDT.
|
|
1374
1418
|
|
|
1375
1419
|
Rich-text fields are stored internally as Automerge rich-text, but apps never see Automerge directly. The bridge speaks JSON and represents a rich-text field as an ordered list of **spans** — text runs with marks for inline formatting, plus block markers for paragraphs, headings, lists, and tables:
|
|
1376
1420
|
|
|
@@ -1480,7 +1524,7 @@ Like the text buffer, the rich-text handle always sends the original `baseHeads`
|
|
|
1480
1524
|
|
|
1481
1525
|
#### End-to-end sample
|
|
1482
1526
|
|
|
1483
|
-
|
|
1527
|
+
TeamEdit's Word/.docx mode is the **only supported in-repo use case** for the Automerge binary sync API below — it needs a local replica and binary flush because `set`/`unset` and the JSON patch APIs are not sufficient for that editor. For span-only rich-text apps, use `createMindooDBRichTextHandle` instead.
|
|
1484
1528
|
|
|
1485
1529
|
### Automerge binary sync (advanced)
|
|
1486
1530
|
|
|
@@ -1589,7 +1633,7 @@ Requires the `update` capability (same as other write operations). For historica
|
|
|
1589
1633
|
|
|
1590
1634
|
#### Reference implementation (not a template)
|
|
1591
1635
|
|
|
1592
|
-
TeamEdit's Word/.docx mode is why these RPCs exist
|
|
1636
|
+
TeamEdit's Word/.docx mode is why these RPCs exist. Treat them as an unsupported edge case, not as a pattern for new apps.
|
|
1593
1637
|
|
|
1594
1638
|
### Queries & Live Queries
|
|
1595
1639
|
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
|
|
3
|
+
MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
|
|
4
|
+
MINDOODB_APP_BUNDLE_MANIFEST_FORMAT,
|
|
5
|
+
MINDOODB_APP_BUNDLE_MANIFEST_VERSION,
|
|
6
|
+
buildMindooDBAppBundleContentHashInput,
|
|
7
|
+
isMindooDBAppBundleHash,
|
|
8
|
+
normalizeMindooDBAppBundlePath,
|
|
9
|
+
validateMindooDBAppBundleManifest
|
|
10
|
+
} from "./chunk-LT4FK6LU.js";
|
|
1
11
|
import {
|
|
2
12
|
DEFAULT_DRAG_LONG_PRESS_MS,
|
|
3
13
|
DEFAULT_DRAG_POINTER_THRESHOLD_PX,
|
|
@@ -88,16 +98,6 @@ import {
|
|
|
88
98
|
validateMindooDBAppComponents,
|
|
89
99
|
validateMindooDBAppDefinition
|
|
90
100
|
} from "./chunk-FKTNDDWB.js";
|
|
91
|
-
import {
|
|
92
|
-
MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
|
|
93
|
-
MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
|
|
94
|
-
MINDOODB_APP_BUNDLE_MANIFEST_FORMAT,
|
|
95
|
-
MINDOODB_APP_BUNDLE_MANIFEST_VERSION,
|
|
96
|
-
buildMindooDBAppBundleContentHashInput,
|
|
97
|
-
isMindooDBAppBundleHash,
|
|
98
|
-
normalizeMindooDBAppBundlePath,
|
|
99
|
-
validateMindooDBAppBundleManifest
|
|
100
|
-
} from "./chunk-LT4FK6LU.js";
|
|
101
101
|
|
|
102
102
|
// src/types.ts
|
|
103
103
|
var MINDOODB_APP_WELL_KNOWN_DRAG_TYPES = [
|