mindoodb-app-sdk 0.0.70 → 0.0.72

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 CHANGED
@@ -400,6 +400,42 @@ In tests, `host.setEmbedAgentTools(embedId, ["text_insert"])` acts as if the com
400
400
  registered tools, and an app under test launched with `launchContext.embed` offers only
401
401
  its document tools, as in Haven.
402
402
 
403
+ #### Agent profile: what agents learn before opening the app
404
+
405
+ Agents only see an app's tools while it runs. To pick the right app first, they call Haven's `haven_apps_describe`, which answers from the `agent` block of `haven-app.json`:
406
+
407
+ ```json
408
+ "agent": {
409
+ "summary": { "en": "Mind maps whose nodes are also tasks; the same nodes as Kanban, Gantt and spreadsheet.", "de": "…" },
410
+ "capabilities": [
411
+ { "id": "maps", "title": { "en": "Find, open and create maps", "de": "…" }, "tools": ["maps_list", "map_get", "map_create", "map_open"] },
412
+ { "id": "nodes", "title": { "en": "Edit nodes and task fields", "de": "…" }, "tools": ["nodes_add", "nodes_update", "ops_guide"] }
413
+ ],
414
+ "examples": { "en": ["Turn these meeting notes into a mind map"], "de": ["…"] },
415
+ "limits": { "en": ["Maps are trees: a node has one parent"], "de": ["…"] },
416
+ "guide": { "en": "agent/agent.en.md" }
417
+ }
418
+ ```
419
+
420
+ - **`summary`**: neutral, 1-3 sentences, at most 400 characters per locale; `en` is required.
421
+ - **`capabilities`**: 1-8 entries; `tools` names the tools as in `registerTools` (no prefix, no `haven_*`). Together they should cover every tool.
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.
424
+
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
+
427
+ ```ts
428
+ import { assertAgentManifest } from "mindoodb-app-sdk/testing";
429
+
430
+ const tools = createMyAgentTools(deps);
431
+ const warnings = assertAgentManifest({
432
+ definition: JSON.parse(readFileSync("public/haven-app.json", "utf8")),
433
+ tools,
434
+ guideTexts: { "agent.en.md": readFileSync("public/agent/agent.en.md", "utf8") },
435
+ });
436
+ expect(warnings).toEqual([]); // every tool belongs to a capability
437
+ ```
438
+
403
439
  `session.agent` is absent on hosts without agent tools, so check for it. Test tools by calling `execute` directly in unit tests; the reference implementation is `src/features/agent/` in [mindoodb-app-vega](https://github.com/klehmann/mindoodb-app-vega). 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").
404
440
 
405
441
  ### Host-owned drag
@@ -489,9 +525,12 @@ only database. The launch context then carries `embed` (`docId`, `databaseId`,
489
525
  `intent`, `hostAppLabel`): open that document, hide "New"/"Open", and finish with
490
526
 
491
527
  ```ts
492
- await session.embedding.complete({ title }); // or session.embedding.cancel()
528
+ await session.embedding.complete({ subject, summary, stats: { words } }); // or session.embedding.cancel()
493
529
  ```
494
530
 
531
+ `complete` takes plain JSON; `MindooDBAppEmbeddingResult` (`subject`, optional
532
+ `summary`, `thumbnail` as a small data URL, `stats`) is the shape hosts expect.
533
+
495
534
  Haven writes every change back to the host's database as it happens and only lets the
496
535
  component create documents that are children of the root.
497
536
 
@@ -592,6 +631,72 @@ never does is closed without asking; `embed.onDirtyChange` lets the host mark it
592
631
  dot in its tab). `embed.save()` rejects with `unsupported` when the component has no save
593
632
  handler.
594
633
 
634
+ **Checking before saving.** A component can tell hosts more about itself in
635
+ `supports`, and hosts filter on it:
636
+
637
+ ```json
638
+ "supports": ["inspect:mindoo.inspect.document@1", "validate", "issues", "reveal"]
639
+ ```
640
+
641
+ ```ts
642
+ const [letterEditor] = await session.components.list({ requires: ["inspect:mindoo.inspect.document@1", "validate"] });
643
+ ```
644
+
645
+ - `inspect:<contract>`: the component describes its document in a shared shape:
646
+ `mindoo.inspect.document@1` (Word/Markdown: subject, outline with anchors, open
647
+ placeholders and MERGEFIELDs, open comments and tracked changes, word count, text on
648
+ request), `mindoo.inspect.spreadsheet@1` (sheets, named ranges, requested cells,
649
+ formula errors with address), `mindoo.inspect.slides@1` (slide count, titles, empty
650
+ placeholders). Anchors: `{ kind: "heading", id }`, `{ kind: "cell", ref: "Sheet1!B3" }`,
651
+ `{ kind: "slide", index }`.
652
+ - `validate`: it asks before it saves, so the host can stop a save.
653
+ - `issues` / `reveal`: it shows a host's findings and scrolls to an anchor.
654
+
655
+ Changing `supports` does not ask users again; it grants no access.
656
+
657
+ ```ts
658
+ // host
659
+ const letter = await embed.inspect<MindooDBAppDocumentInspection>(); // { outline, placeholders, … }
660
+ const rules = defineDocumentRules<MindooDBAppDocumentInspection>({
661
+ contract: MINDOODB_INSPECT_DOCUMENT,
662
+ rules: [mindooDBAppInspectRules.noOpenPlaceholders(), mindooDBAppInspectRules.noOpenTrackedChanges("warning")],
663
+ });
664
+ rules.attach(embed); // = embed.onBeforeSave(rules.beforeSave, { inspect: rules.request })
665
+ embed.onAfterSave(() => refreshTree());
666
+ await embed.showIssues(rules.checkRoot(doc.data)); // the root summary, see below
667
+ await embed.reveal({ kind: "heading", id: "h-3" });
668
+ await embed.callTool("text_insert", { text: "…" }); // any of its document agent tools
669
+
670
+ // component
671
+ session.embedding.provideInspection((request) => describeDocument(request));
672
+ session.embedding.onShowIssues((issues) => showIssuesPanel(issues));
673
+ session.embedding.onReveal((anchor) => scrollTo(anchor));
674
+ await session.embedding.save(async ({ inspection }) => {
675
+ await db.documents.update({ docId, set: { ...changes, ...(inspection ? { inspection } : {}) } });
676
+ }); // throws MindooDBAppEmbedValidationError (with .issues) when the host's check stopped it
677
+ ```
678
+
679
+ Haven never holds writes back: the component asks first (`embedding.validate`, which
680
+ `embedding.save` calls), Haven fetches the inspection the host asked for, passes it with
681
+ the root document to the host's `onBeforeSave` and answers the component: an `error`
682
+ stops the save, a `warning` asks the user whether to save anyway, and when the host does
683
+ not answer within about 5 s the component saves (`timedOut`). This covers saves the
684
+ user, the host (`embed.save()`) and agents start; an agent tool saves with
685
+ `save(flush, { interactive: false })`, where warnings do not stop it, and reports the
686
+ error's issues as its tool error. Standalone, `validate` answers `ok` at once.
687
+
688
+ `save` also hands `flush` a small summary of the inspection (at most 8 KiB, lists and
689
+ text shortened, counts exact) as an atomic value for the root's `inspection` field, so
690
+ hosts can check documents they do not have open: `rules.checkRoot(root)`, or
691
+ `readMindooDBAppInspectionSummary(root)`. Write it in standalone saves too.
692
+
693
+ In tests, the mock plays the components with `embedInspection`, `embedTools` and
694
+ `confirmSaveWarnings`, and records `getEmbedRequests(embedId)`; `validateEmbed(embedId)`
695
+ runs the host's check as if the component was about to save. For an app under test that
696
+ is a component, `embeddingValidation` plays the host, and `requestEmbeddingInspection`,
697
+ `requestEmbeddingShowIssues`, `requestEmbeddingReveal`, `getEmbeddingValidations` and
698
+ `getEmbeddingSaveCount` drive and observe it.
699
+
595
700
  Havens that predate this lay the component over the container instead, above the host's
596
701
  frame (`embed.placement === "overlay"`). The host then cannot draw on top of it: hide it
597
702
  with `embed.setVisible(false)` while a menu or dialog of the host should be visible.