mindoodb-app-sdk 0.0.62 → 0.0.64

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/TESTING.md CHANGED
@@ -344,6 +344,7 @@ The page shows the app next to a control panel:
344
344
  - **Databases**: one checkbox per capability for every mapped database, plus **Enforce capabilities**. Changing a capability relaunches the app, as Haven does after a permission change. With enforcement on, the mock rejects every call the database was not granted with a `forbidden` error (and `documents.canCreate()` and friends answer `allowed: false`), so you can check the app's hints for missing rights.
345
345
  - **Directory**: fills the tenant directory with 0 to 500 generated users (`CN=Test User 001/O=Test`, …) for recipient pickers and paging.
346
346
  - **Scanner**: what `attachments.scan()` returns — cancel, a generated sample page, or a file you choose. The file is written to the document's attachments like Haven's scanner does, so the app can read it back.
347
+ - **Agent tools**: the tools the app declared with `session.agent.registerTools()` (with the prefix agents see, `read-only` and `asks first` tags, description and input schema), a form to call one with a JSON input as an agent would, and the result as the agent gets it (images in the result are shown). Tools marked `consequentialHint` go through a confirm, like Haven's consent dialog. A checkbox switches agent access off. **Agent files** imports files for the app (each gets a `fileRef` for `takeFile`) and lists the files the app handed over with `provideFile` for download. **Agent context** shows what the app last passed to `setContext`.
347
348
  - A live log of notifications, attachment previews, scans and every RPC request. Click a request for its parameters, its result or error, and how long it took; binary values show as size plus a hex preview.
348
349
 
349
350
  `mockDatabasesFromDefinition` gives every database an in-memory attachment store (`attachments: "none"` restores the stateless default) and takes overrides for testing other mappings than the definition requests:
@@ -377,6 +378,21 @@ await app.getByRole("button", { name: "Scan" }).click();
377
378
  expect(await page.evaluate(() => window.__havenTestHost!.log.scans)).toHaveLength(1);
378
379
  ```
379
380
 
381
+ Agent tools are scriptable through `window.__havenTestHost.agent`, the same controller the Vitest helpers expose as `createFakeBridgeHost().agent`:
382
+
383
+ ```ts
384
+ const agent = () => window.__havenTestHost!.agent;
385
+ const fileRef = await page.evaluate(() =>
386
+ agent().importFile({ name: "logo.png", mimeType: "image/png", data: new Uint8Array([137, 80, 78, 71]) }),
387
+ );
388
+ const added = await page.evaluate((ref) => agent().call("image_add", { fileRef: ref }), fileRef);
389
+ expect(added).toMatchObject({ ok: true });
390
+ const exported = await page.evaluate(() => agent().call("deck_export", { format: "pptx" }));
391
+ const file = await page.evaluate((ref) => agent().getFile(ref)?.name, (exported as any).result.files[0].fileRef);
392
+ ```
393
+
394
+ `call()` takes the tool's own name or the prefixed one and never throws: failures come back as `{ ok: false, error: { code, message, requiredAction } }`, the codes an agent sees. The mock follows Haven: tool declarations are validated (name, at most 64 tools, 2000-character descriptions, an object `inputSchema`), the prefix is the definition's `agentToolPrefix` (pass it as the `agentToolPrefix` option) or derived from the app id, an app can only `takeFile()` what was imported for it, and file refs expire after ten minutes. `setConsent("allow" | "deny" | fn)` answers the consent step in scripts, `setEnabled(false)` switches access off, and `calls()` lists every call with its outcome.
395
+
380
396
  `setNextScan()` decides only the next scan (pass `bytes` to attach real content); `setScanMode()` decides the ones after it. `setDirectoryUsers()` and `setEnforceCapabilities()` apply to the next call.
381
397
 
382
398
  The Level 1 and 2 helpers take the same options: `createMockMindooDBAppSession({ enforceCapabilities: true, directoryUsers: generateDirectoryUsers(120) })`, with `setCapabilities()`, `setEnforceCapabilities()` and `setDirectoryUsers()` on the controller, and `createMemoryAttachments()` for a database's `methods.attachments`. `createFakeBridgeHost({ onResponse })` reports every answer with its duration.
@@ -1482,7 +1482,6 @@ var MindooDBAppEmbedImpl = class {
1482
1482
  }
1483
1483
  if (this.frame) {
1484
1484
  this.frame.style.visibility = visible ? "" : "hidden";
1485
- return;
1486
1485
  }
1487
1486
  await this.rpc.call("embeds.setVisible", { embedId: this.embedId, visible: visible === true });
1488
1487
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MindooDBAppAttachmentPreviewMode, a as MindooDBAppDragBindSourceOptions, b as MindooDBAppDragStartInput, c as MindooDBAppDragStartResult, d as MindooDBAppDragPreview, e as MindooDBAppBridge, f as MindooDBAppAgentToolErrorCode, g as MindooDBAppHostShortcutBinding, h as MindooDBAppHostShortcutAction, i as MindooDBAppHostingMode, j as MindooDBAppDefinition, k as MindooDBAppDatabase, l as MindooDBAppDocument, m as MindooDBAppRichTextSpan, n as MindooDBAppRichTextSnapshot, o as MindooDBAppCreateDocumentInput } from './types-ClOh6z0L.js';
2
- export { p as MINDOODB_APP_ACCEPTS_MAX, q as MINDOODB_APP_COMPONENTS_MAX, r as MINDOODB_APP_DEFINITION_FILE_NAME, s as MINDOODB_APP_DEFINITION_FORMAT, t as MINDOODB_APP_DEFINITION_PERMISSIONS, u as MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS, v as MINDOODB_APP_DEFINITION_VERSION, w as MINDOODB_APP_LISTING_DESCRIPTION_MAX, x as MINDOODB_APP_LISTING_MAX_SCREENSHOTS, y as MINDOODB_APP_LISTING_SUMMARY_MAX, z as MINDOODB_APP_SEAL_SIGNING_DOMAIN, A as MINDOODB_APP_WELL_KNOWN_DRAG_TYPES, B as MindooDBAppAcceptSpec, C as MindooDBAppAccessDecision, D as MindooDBAppAccessTier, E as MindooDBAppAgentApi, F as MindooDBAppAgentRegistration, G as MindooDBAppAgentTool, H as MindooDBAppAgentToolAnnotations, I as MindooDBAppAgentToolDescriptor, J as MindooDBAppAttachmentApi, K as MindooDBAppAttachmentChunk, L as MindooDBAppAttachmentInfo, N as MindooDBAppAttachmentPreviewOptions, O as MindooDBAppAttachmentPreviewSession, P as MindooDBAppAutomergeChangesPatch, Q as MindooDBAppAutomergeChangesSince, R as MindooDBAppAutomergeGetOptions, S as MindooDBAppAutomergePatchResult, T as MindooDBAppAutomergeSnapshot, U as MindooDBAppBridgeAgentInvokeMessage, V as MindooDBAppBridgeAgentResultMessage, W as MindooDBAppBridgeBeforeCloseAck, X as MindooDBAppBridgeBeforeCloseMessage, Y as MindooDBAppBridgeConnectMessage, Z as MindooDBAppBridgeConnectOptions, _ as MindooDBAppBridgeConnectedMessage, $ as MindooDBAppBridgeDragDropMessage, a0 as MindooDBAppBridgeDragLeaveMessage, a1 as MindooDBAppBridgeDragOverMessage, a2 as MindooDBAppBridgeEmbedEventMessage, a3 as MindooDBAppBridgeEmbeddingRequestMessage, a4 as MindooDBAppBridgeErrorPayload, a5 as MindooDBAppBridgeHandshakeErrorMessage, a6 as MindooDBAppBridgeHostFocusChangedMessage, a7 as MindooDBAppBridgeIncomingContentMessage, a8 as MindooDBAppBridgeLocaleChangedMessage, a9 as MindooDBAppBridgePortMessage, aa as MindooDBAppBridgeQueryResultMessage, ab as MindooDBAppBridgeRpcError, ac as MindooDBAppBridgeRpcMessage, ad as MindooDBAppBridgeRpcRequest, ae as MindooDBAppBridgeRpcSuccess, af as MindooDBAppBridgeShortcutInvokedMessage, ag as MindooDBAppBridgeShortcutsChangedMessage, ah as MindooDBAppBridgeStreamAbortRequest, ai as MindooDBAppBridgeStreamAck, aj as MindooDBAppBridgeStreamCloseRequest, ak as MindooDBAppBridgeStreamError, al as MindooDBAppBridgeStreamMessage, am as MindooDBAppBridgeStreamOpenResult, an as MindooDBAppBridgeStreamReadRequest, ao as MindooDBAppBridgeStreamWriteRequest, ap as MindooDBAppBridgeThemeChangedMessage, aq as MindooDBAppBridgeUiPreferencesChangedMessage, ar as MindooDBAppBridgeViewChangedMessage, as as MindooDBAppBridgeViewportChangedMessage, at as MindooDBAppBridgeWorkspaceFocusRequestedMessage, au as MindooDBAppBrowserFeatures, av as MindooDBAppCapability, aw as MindooDBAppComponentChildren, ax as MindooDBAppComponentFieldValue, ay as MindooDBAppComponentInfo, az as MindooDBAppComponentIntent, aA as MindooDBAppComponentQuery, aB as MindooDBAppComponentSpec, aC as MindooDBAppComponentsApi, aD as MindooDBAppConfiguredViewCategorizationStyle, aE as MindooDBAppConfiguredViewColumn, aF as MindooDBAppConfiguredViewColumnRole, aG as MindooDBAppConfiguredViewExpressionMode, aH as MindooDBAppConfiguredViewFieldExpression, aI as MindooDBAppConfiguredViewFilterDefinition, aJ as MindooDBAppConfiguredViewFilterMode, aK as MindooDBAppConfiguredViewFilterRule, aL as MindooDBAppConfiguredViewFormulaExpression, aM as MindooDBAppConfiguredViewFormulaFilterDefinition, aN as MindooDBAppConfiguredViewPreviewMode, aO as MindooDBAppConfiguredViewRuleFilterDefinition, aP as MindooDBAppConfiguredViewRuleMatchMode, aQ as MindooDBAppConfiguredViewRuleOperator, aR as MindooDBAppConfiguredViewValueExpression, aS as MindooDBAppCreateKeyInfo, aT as MindooDBAppCreateViewInput, aU as MindooDBAppCreateViewNavigatorInput, aV as MindooDBAppDatabaseInfo, aW as MindooDBAppDefinitionDatabase, aX as MindooDBAppDefinitionListing, aY as MindooDBAppDefinitionPermission, aZ as MindooDBAppDefinitionRegistrationPermission, a_ as MindooDBAppDefinitionScreenshot, a$ as MindooDBAppDefinitionValidation, b0 as MindooDBAppDirectoryApi, b1 as MindooDBAppDirectoryEntry, b2 as MindooDBAppDocumentApi, b3 as MindooDBAppDocumentHeadCursorResult, b4 as MindooDBAppDocumentHistoryEntry, b5 as MindooDBAppDocumentListQuery, b6 as MindooDBAppDocumentListResult, b7 as MindooDBAppDocumentQuery, b8 as MindooDBAppDocumentRevisionId, b9 as MindooDBAppDocumentScanPreset, ba as MindooDBAppDocumentSummary, bb as MindooDBAppDragApi, bc as MindooDBAppDragCancelReason, bd as MindooDBAppDragDropEvent, be as MindooDBAppDragEffect, bf as MindooDBAppDragHoverResult, bg as MindooDBAppDragOffer, bh as MindooDBAppDragOverEvent, bi as MindooDBAppDragPointer, bj as MindooDBAppDragProfile, bk as MindooDBAppEmbed, bl as MindooDBAppEmbedCloseReason, bm as MindooDBAppEmbedClosedEvent, bn as MindooDBAppEmbedContext, bo as MindooDBAppEmbedDirtyEvent, bp as MindooDBAppEmbedOpenInput, bq as MindooDBAppEmbedRect, br as MindooDBAppEmbeddingApi, bs as MindooDBAppEmbedsApi, bt as MindooDBAppEntryAttachmentRef, bu as MindooDBAppEntryProvenance, bv as MindooDBAppExtractDocumentTextInput, bw as MindooDBAppExtractDocumentTextResult, bx as MindooDBAppExtractTextFormat, by as MindooDBAppExtractionSetup, bz as MindooDBAppFulltextSetup, bA as MindooDBAppHistoricalDocument, bB as MindooDBAppHistoryQuery, bC as MindooDBAppHostTheme, bD as MindooDBAppIdentityApi, bE as MindooDBAppInaccessibleDocument, bF as MindooDBAppInaccessibleDocumentListQuery, bG as MindooDBAppInaccessibleDocumentListResult, bH as MindooDBAppIncomingContent, bI as MindooDBAppIncomingContentHandler, bJ as MindooDBAppIncomingContentInfo, bK as MindooDBAppIncomingItem, bL as MindooDBAppIncomingItemInfo, bM as MindooDBAppIncomingResult, bN as MindooDBAppIncomingSource, bO as MindooDBAppJsonCounterIncrementPatch, bP as MindooDBAppJsonListDeletePatch, bQ as MindooDBAppJsonListInsertPatch, bR as MindooDBAppJsonPatch, bS as MindooDBAppJsonSetPatch, bT as MindooDBAppJsonTextMarkPatch, bU as MindooDBAppJsonTextSplicePatch, bV as MindooDBAppJsonTextUnmarkPatch, bW as MindooDBAppJsonUnsetPatch, bX as MindooDBAppLaunchContext, bY as MindooDBAppListUsersOptions, bZ as MindooDBAppListUsersPage, b_ as MindooDBAppLiveQuerySubscription, b$ as MindooDBAppLocalizedText, c0 as MindooDBAppMenuAnchor, c1 as MindooDBAppMenuApi, c2 as MindooDBAppMenuCommandItem, c3 as MindooDBAppMenuDismissReason, c4 as MindooDBAppMenuDismissedResult, c5 as MindooDBAppMenuItem, c6 as MindooDBAppMenuKind, c7 as MindooDBAppMenuPlacement, c8 as MindooDBAppMenuPointAnchor, c9 as MindooDBAppMenuRect, ca as MindooDBAppMenuRectAnchor, cb as MindooDBAppMenuSelectionResult, cc as MindooDBAppMenuSeparatorItem, cd as MindooDBAppNotificationSeverity, ce as MindooDBAppNotifyInput, cf as MindooDBAppNotifyResult, cg as MindooDBAppProposeAppInput, ch as MindooDBAppProposeAppResult, ci as MindooDBAppQueryCoverage, cj as MindooDBAppQueryInclude, ck as MindooDBAppQueryIncludeCardinality, cl as MindooDBAppQueryIncludeSlots, cm as MindooDBAppQueryResult, cn as MindooDBAppQueryRow, co as MindooDBAppQuerySortKey, cp as MindooDBAppQueryTextClause, cq as MindooDBAppReadableAttachmentStream, cr as MindooDBAppRecipientOptions, cs as MindooDBAppResolvedViewDefinition, ct as MindooDBAppResolvedViewSource, cu as MindooDBAppRevisionPhase, cv as MindooDBAppRevisionVerification, cw as MindooDBAppRichTextBlockSpan, cx as MindooDBAppRichTextGetOptions, cy as MindooDBAppRichTextImmutableString, cz as MindooDBAppRichTextMarkRange, cA as MindooDBAppRichTextMaterializeValue, cB as MindooDBAppRichTextPatch, cC as MindooDBAppRichTextScalar, cD as MindooDBAppRichTextSpliceStep, cE as MindooDBAppRichTextStep, cF as MindooDBAppRichTextStepPatch, cG as MindooDBAppRichTextTextSpan, cH as MindooDBAppRuntime, cI as MindooDBAppScanAttachmentOptions, cJ as MindooDBAppScanAttachmentResult, cK as MindooDBAppScopedDocId, cL as MindooDBAppScopedDocumentSummary, cM as MindooDBAppSealIntent, cN as MindooDBAppSealedChannelOpenInput, cO as MindooDBAppSealedChannelOpenResult, cP as MindooDBAppSealedChannelRequestInput, cQ as MindooDBAppSession, cR as MindooDBAppShowMenuInput, cS as MindooDBAppShowMenuResult, cT as MindooDBAppSignPreview, cU as MindooDBAppSignPreviewScope, cV as MindooDBAppSignStatementInput, cW as MindooDBAppSignStatementResult, cX as MindooDBAppStorageApi, cY as MindooDBAppSummarySetup, cZ as MindooDBAppTextCursorOptions, c_ as MindooDBAppTextCursorPosition, c$ as MindooDBAppTextCursorPositionsResult, d0 as MindooDBAppTextCursorsResult, d1 as MindooDBAppTextEdit, d2 as MindooDBAppTextPatch, d3 as MindooDBAppThemeMode, d4 as MindooDBAppTimestampApi, d5 as MindooDBAppTimestampInput, d6 as MindooDBAppTimestampProvider, d7 as MindooDBAppTimestampResult, d8 as MindooDBAppUiPreferences, d9 as MindooDBAppUpdateDocumentInput, da as MindooDBAppViewCursorDocumentListResult, db as MindooDBAppViewCursorResult, dc as MindooDBAppViewEntry, dd as MindooDBAppViewEntryKind, de as MindooDBAppViewNavigator, df as MindooDBAppViewNavigatorExpansionState, dg as MindooDBAppViewNavigatorOpenOptions, dh as MindooDBAppViewNavigatorPageOptions, di as MindooDBAppViewNavigatorPageResult, dj as MindooDBAppViewNavigatorRangeQuery, dk as MindooDBAppViewNavigatorSelectionState, dl as MindooDBAppViewUpdateStats, dm as MindooDBAppViewport, dn as MindooDBAppWellKnownDragType, dp as MindooDBAppWritableAttachmentStream, dq as mindooDBAppAcceptMatchesType, dr as mindooDBAppComponentFieldsMatch, ds as resolveMindooDBAppDefinitionUrl, dt as resolveMindooDBAppListingAssetUrl, du as resolveMindooDBAppLocalizedText, dv as validateMindooDBAppAccepts, dw as validateMindooDBAppComponents, dx as validateMindooDBAppDefinition } from './types-ClOh6z0L.js';
1
+ import { M as MindooDBAppAttachmentPreviewMode, a as MindooDBAppDragBindSourceOptions, b as MindooDBAppDragStartInput, c as MindooDBAppDragStartResult, d as MindooDBAppDragPreview, e as MindooDBAppBridge, f as MindooDBAppAgentToolErrorCode, g as MindooDBAppHostShortcutBinding, h as MindooDBAppHostShortcutAction, i as MindooDBAppHostingMode, j as MindooDBAppDefinition, k as MindooDBAppDatabase, l as MindooDBAppDocument, m as MindooDBAppRichTextSpan, n as MindooDBAppRichTextSnapshot, o as MindooDBAppCreateDocumentInput } from './types-BeRUxGKN.js';
2
+ export { p as MINDOODB_APP_ACCEPTS_MAX, q as MINDOODB_APP_COMPONENTS_MAX, r as MINDOODB_APP_DEFINITION_FILE_NAME, s as MINDOODB_APP_DEFINITION_FORMAT, t as MINDOODB_APP_DEFINITION_PERMISSIONS, u as MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS, v as MINDOODB_APP_DEFINITION_VERSION, w as MINDOODB_APP_LISTING_DESCRIPTION_MAX, x as MINDOODB_APP_LISTING_MAX_SCREENSHOTS, y as MINDOODB_APP_LISTING_SUMMARY_MAX, z as MINDOODB_APP_SEAL_SIGNING_DOMAIN, A as MINDOODB_APP_WELL_KNOWN_DRAG_TYPES, B as MindooDBAppAcceptSpec, C as MindooDBAppAccessDecision, D as MindooDBAppAccessTier, E as MindooDBAppAgentApi, F as MindooDBAppAgentRegistration, G as MindooDBAppAgentTool, H as MindooDBAppAgentToolAnnotations, I as MindooDBAppAgentToolDescriptor, J as MindooDBAppAttachmentApi, K as MindooDBAppAttachmentChunk, L as MindooDBAppAttachmentInfo, N as MindooDBAppAttachmentPreviewOptions, O as MindooDBAppAttachmentPreviewSession, P as MindooDBAppAutomergeChangesPatch, Q as MindooDBAppAutomergeChangesSince, R as MindooDBAppAutomergeGetOptions, S as MindooDBAppAutomergePatchResult, T as MindooDBAppAutomergeSnapshot, U as MindooDBAppBridgeAgentInvokeMessage, V as MindooDBAppBridgeAgentResultMessage, W as MindooDBAppBridgeBeforeCloseAck, X as MindooDBAppBridgeBeforeCloseMessage, Y as MindooDBAppBridgeConnectMessage, Z as MindooDBAppBridgeConnectOptions, _ as MindooDBAppBridgeConnectedMessage, $ as MindooDBAppBridgeDragDropMessage, a0 as MindooDBAppBridgeDragLeaveMessage, a1 as MindooDBAppBridgeDragOverMessage, a2 as MindooDBAppBridgeEmbedEventMessage, a3 as MindooDBAppBridgeEmbeddingRequestMessage, a4 as MindooDBAppBridgeErrorPayload, a5 as MindooDBAppBridgeHandshakeErrorMessage, a6 as MindooDBAppBridgeHostFocusChangedMessage, a7 as MindooDBAppBridgeIncomingContentMessage, a8 as MindooDBAppBridgeLocaleChangedMessage, a9 as MindooDBAppBridgePortMessage, aa as MindooDBAppBridgeQueryResultMessage, ab as MindooDBAppBridgeRpcError, ac as MindooDBAppBridgeRpcMessage, ad as MindooDBAppBridgeRpcRequest, ae as MindooDBAppBridgeRpcSuccess, af as MindooDBAppBridgeShortcutInvokedMessage, ag as MindooDBAppBridgeShortcutsChangedMessage, ah as MindooDBAppBridgeStreamAbortRequest, ai as MindooDBAppBridgeStreamAck, aj as MindooDBAppBridgeStreamCloseRequest, ak as MindooDBAppBridgeStreamError, al as MindooDBAppBridgeStreamMessage, am as MindooDBAppBridgeStreamOpenResult, an as MindooDBAppBridgeStreamReadRequest, ao as MindooDBAppBridgeStreamWriteRequest, ap as MindooDBAppBridgeThemeChangedMessage, aq as MindooDBAppBridgeUiPreferencesChangedMessage, ar as MindooDBAppBridgeViewChangedMessage, as as MindooDBAppBridgeViewportChangedMessage, at as MindooDBAppBridgeWorkspaceFocusRequestedMessage, au as MindooDBAppBrowserFeatures, av as MindooDBAppCapability, aw as MindooDBAppComponentChildren, ax as MindooDBAppComponentFieldValue, ay as MindooDBAppComponentInfo, az as MindooDBAppComponentIntent, aA as MindooDBAppComponentQuery, aB as MindooDBAppComponentSpec, aC as MindooDBAppComponentsApi, aD as MindooDBAppConfiguredViewCategorizationStyle, aE as MindooDBAppConfiguredViewColumn, aF as MindooDBAppConfiguredViewColumnRole, aG as MindooDBAppConfiguredViewExpressionMode, aH as MindooDBAppConfiguredViewFieldExpression, aI as MindooDBAppConfiguredViewFilterDefinition, aJ as MindooDBAppConfiguredViewFilterMode, aK as MindooDBAppConfiguredViewFilterRule, aL as MindooDBAppConfiguredViewFormulaExpression, aM as MindooDBAppConfiguredViewFormulaFilterDefinition, aN as MindooDBAppConfiguredViewPreviewMode, aO as MindooDBAppConfiguredViewRuleFilterDefinition, aP as MindooDBAppConfiguredViewRuleMatchMode, aQ as MindooDBAppConfiguredViewRuleOperator, aR as MindooDBAppConfiguredViewValueExpression, aS as MindooDBAppCreateKeyInfo, aT as MindooDBAppCreateViewInput, aU as MindooDBAppCreateViewNavigatorInput, aV as MindooDBAppDatabaseInfo, aW as MindooDBAppDefinitionDatabase, aX as MindooDBAppDefinitionListing, aY as MindooDBAppDefinitionPermission, aZ as MindooDBAppDefinitionRegistrationPermission, a_ as MindooDBAppDefinitionScreenshot, a$ as MindooDBAppDefinitionValidation, b0 as MindooDBAppDirectoryApi, b1 as MindooDBAppDirectoryEntry, b2 as MindooDBAppDocumentApi, b3 as MindooDBAppDocumentHeadCursorResult, b4 as MindooDBAppDocumentHistoryEntry, b5 as MindooDBAppDocumentListQuery, b6 as MindooDBAppDocumentListResult, b7 as MindooDBAppDocumentQuery, b8 as MindooDBAppDocumentRevisionId, b9 as MindooDBAppDocumentScanPreset, ba as MindooDBAppDocumentSummary, bb as MindooDBAppDragApi, bc as MindooDBAppDragCancelReason, bd as MindooDBAppDragDropEvent, be as MindooDBAppDragEffect, bf as MindooDBAppDragHoverResult, bg as MindooDBAppDragOffer, bh as MindooDBAppDragOverEvent, bi as MindooDBAppDragPointer, bj as MindooDBAppDragProfile, bk as MindooDBAppEmbed, bl as MindooDBAppEmbedCloseReason, bm as MindooDBAppEmbedClosedEvent, bn as MindooDBAppEmbedContext, bo as MindooDBAppEmbedDirtyEvent, bp as MindooDBAppEmbedOpenInput, bq as MindooDBAppEmbedRect, br as MindooDBAppEmbeddingApi, bs as MindooDBAppEmbedsApi, bt as MindooDBAppEntryAttachmentRef, bu as MindooDBAppEntryProvenance, bv as MindooDBAppExtractDocumentTextInput, bw as MindooDBAppExtractDocumentTextResult, bx as MindooDBAppExtractTextFormat, by as MindooDBAppExtractionSetup, bz as MindooDBAppFulltextSetup, bA as MindooDBAppHistoricalDocument, bB as MindooDBAppHistoryQuery, bC as MindooDBAppHostTheme, bD as MindooDBAppIdentityApi, bE as MindooDBAppInaccessibleDocument, bF as MindooDBAppInaccessibleDocumentListQuery, bG as MindooDBAppInaccessibleDocumentListResult, bH as MindooDBAppIncomingContent, bI as MindooDBAppIncomingContentHandler, bJ as MindooDBAppIncomingContentInfo, bK as MindooDBAppIncomingItem, bL as MindooDBAppIncomingItemInfo, bM as MindooDBAppIncomingResult, bN as MindooDBAppIncomingSource, bO as MindooDBAppJsonCounterIncrementPatch, bP as MindooDBAppJsonListDeletePatch, bQ as MindooDBAppJsonListInsertPatch, bR as MindooDBAppJsonPatch, bS as MindooDBAppJsonSetPatch, bT as MindooDBAppJsonTextMarkPatch, bU as MindooDBAppJsonTextSplicePatch, bV as MindooDBAppJsonTextUnmarkPatch, bW as MindooDBAppJsonUnsetPatch, bX as MindooDBAppLaunchContext, bY as MindooDBAppListUsersOptions, bZ as MindooDBAppListUsersPage, b_ as MindooDBAppLiveQuerySubscription, b$ as MindooDBAppLocalizedText, c0 as MindooDBAppMenuAnchor, c1 as MindooDBAppMenuApi, c2 as MindooDBAppMenuCommandItem, c3 as MindooDBAppMenuDismissReason, c4 as MindooDBAppMenuDismissedResult, c5 as MindooDBAppMenuItem, c6 as MindooDBAppMenuKind, c7 as MindooDBAppMenuPlacement, c8 as MindooDBAppMenuPointAnchor, c9 as MindooDBAppMenuRect, ca as MindooDBAppMenuRectAnchor, cb as MindooDBAppMenuSelectionResult, cc as MindooDBAppMenuSeparatorItem, cd as MindooDBAppNotificationSeverity, ce as MindooDBAppNotifyInput, cf as MindooDBAppNotifyResult, cg as MindooDBAppProposeAppInput, ch as MindooDBAppProposeAppResult, ci as MindooDBAppQueryCoverage, cj as MindooDBAppQueryInclude, ck as MindooDBAppQueryIncludeCardinality, cl as MindooDBAppQueryIncludeSlots, cm as MindooDBAppQueryResult, cn as MindooDBAppQueryRow, co as MindooDBAppQuerySortKey, cp as MindooDBAppQueryTextClause, cq as MindooDBAppReadableAttachmentStream, cr as MindooDBAppRecipientOptions, cs as MindooDBAppResolvedViewDefinition, ct as MindooDBAppResolvedViewSource, cu as MindooDBAppRevisionPhase, cv as MindooDBAppRevisionVerification, cw as MindooDBAppRichTextBlockSpan, cx as MindooDBAppRichTextGetOptions, cy as MindooDBAppRichTextImmutableString, cz as MindooDBAppRichTextMarkRange, cA as MindooDBAppRichTextMaterializeValue, cB as MindooDBAppRichTextPatch, cC as MindooDBAppRichTextScalar, cD as MindooDBAppRichTextSpliceStep, cE as MindooDBAppRichTextStep, cF as MindooDBAppRichTextStepPatch, cG as MindooDBAppRichTextTextSpan, cH as MindooDBAppRuntime, cI as MindooDBAppScanAttachmentOptions, cJ as MindooDBAppScanAttachmentResult, cK as MindooDBAppScopedDocId, cL as MindooDBAppScopedDocumentSummary, cM as MindooDBAppSealIntent, cN as MindooDBAppSealedChannelOpenInput, cO as MindooDBAppSealedChannelOpenResult, cP as MindooDBAppSealedChannelRequestInput, cQ as MindooDBAppSession, cR as MindooDBAppShowMenuInput, cS as MindooDBAppShowMenuResult, cT as MindooDBAppSignPreview, cU as MindooDBAppSignPreviewScope, cV as MindooDBAppSignStatementInput, cW as MindooDBAppSignStatementResult, cX as MindooDBAppStorageApi, cY as MindooDBAppSummarySetup, cZ as MindooDBAppTextCursorOptions, c_ as MindooDBAppTextCursorPosition, c$ as MindooDBAppTextCursorPositionsResult, d0 as MindooDBAppTextCursorsResult, d1 as MindooDBAppTextEdit, d2 as MindooDBAppTextPatch, d3 as MindooDBAppThemeMode, d4 as MindooDBAppTimestampApi, d5 as MindooDBAppTimestampInput, d6 as MindooDBAppTimestampProvider, d7 as MindooDBAppTimestampResult, d8 as MindooDBAppUiPreferences, d9 as MindooDBAppUpdateDocumentInput, da as MindooDBAppViewCursorDocumentListResult, db as MindooDBAppViewCursorResult, dc as MindooDBAppViewEntry, dd as MindooDBAppViewEntryKind, de as MindooDBAppViewNavigator, df as MindooDBAppViewNavigatorExpansionState, dg as MindooDBAppViewNavigatorOpenOptions, dh as MindooDBAppViewNavigatorPageOptions, di as MindooDBAppViewNavigatorPageResult, dj as MindooDBAppViewNavigatorRangeQuery, dk as MindooDBAppViewNavigatorSelectionState, dl as MindooDBAppViewUpdateStats, dm as MindooDBAppViewport, dn as MindooDBAppWellKnownDragType, dp as MindooDBAppWritableAttachmentStream, dq as mindooDBAppAcceptMatchesType, dr as mindooDBAppComponentFieldsMatch, ds as resolveMindooDBAppDefinitionUrl, dt as resolveMindooDBAppListingAssetUrl, du as resolveMindooDBAppLocalizedText, dv as validateMindooDBAppAccepts, dw as validateMindooDBAppComponents, dx as validateMindooDBAppDefinition } from './types-BeRUxGKN.js';
3
3
  export { M as MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME, a as MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME, b as MINDOODB_APP_BUNDLE_MANIFEST_FORMAT, c as MINDOODB_APP_BUNDLE_MANIFEST_VERSION, d as MindooDBAppBundleArchive, e as MindooDBAppBundleFileEntry, f as MindooDBAppBundleHash, g as MindooDBAppBundleManifest, h as MindooDBAppBundleManifestValidation, i as buildMindooDBAppBundleContentHashInput, j as isMindooDBAppBundleHash, n as normalizeMindooDBAppBundlePath, v as validateMindooDBAppBundleManifest } from './appBundleManifest-B0xfpaXG.js';
4
4
  import { MindooDBAppBooleanExpression } from 'mindoodb-view-language';
5
5
  export { MindooDBAppBooleanExpression, MindooDBAppExpression, MindooDBAppFilterDefinition, MindooDBAppViewCategoryChildrenPageRequest, MindooDBAppViewColumn, MindooDBAppViewColumnRole, MindooDBAppViewDefinition, MindooDBAppViewExpansionState, MindooDBAppViewExpressionBase, MindooDBAppViewExpressionDatePart, MindooDBAppViewExpressionOperation, MindooDBAppViewFieldExpression, MindooDBAppViewIfExpression, MindooDBAppViewLetExpression, MindooDBAppViewLiteralExpression, MindooDBAppViewLookupByPath, MindooDBAppViewOperationExpression, MindooDBAppViewOriginExpression, MindooDBAppViewPageRequest, MindooDBAppViewPageResult, MindooDBAppViewRow, MindooDBAppViewSortDirection, MindooDBAppViewTotalMode, MindooDBAppViewValueExpressionRef, MindooDBAppViewVariableExpression, createViewLanguage } from 'mindoodb-view-language';
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  validateMindooDBAppAccepts,
48
48
  validateMindooDBAppComponents,
49
49
  validateMindooDBAppDefinition
50
- } from "./chunk-QQRLEHFE.js";
50
+ } from "./chunk-BE7LLFFE.js";
51
51
  import {
52
52
  MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
53
53
  MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
@@ -1,4 +1,4 @@
1
- import { ay as MindooDBAppComponentInfo, bq as MindooDBAppEmbedRect, aU as MindooDBAppCreateViewNavigatorInput, de as MindooDBAppViewNavigator, ad as MindooDBAppBridgeRpcRequest, bC as MindooDBAppHostTheme, av as MindooDBAppCapability, j as MindooDBAppDefinition, J as MindooDBAppAttachmentApi, e as MindooDBAppBridge, cQ as MindooDBAppSession, cg as MindooDBAppProposeAppInput, dm as MindooDBAppViewport, d8 as MindooDBAppUiPreferences, cm as MindooDBAppQueryResult, dl as MindooDBAppViewUpdateStats, a9 as MindooDBAppBridgePortMessage, aV as MindooDBAppDatabaseInfo, bl as MindooDBAppEmbedCloseReason, a4 as MindooDBAppBridgeErrorPayload, bX as MindooDBAppLaunchContext, b2 as MindooDBAppDocumentApi, dg as MindooDBAppViewNavigatorOpenOptions, bD as MindooDBAppIdentityApi, d4 as MindooDBAppTimestampApi, b0 as MindooDBAppDirectoryApi, l as MindooDBAppDocument, bz as MindooDBAppFulltextSetup, by as MindooDBAppExtractionSetup, cY as MindooDBAppSummarySetup, ch as MindooDBAppProposeAppResult, bN as MindooDBAppIncomingSource, c as MindooDBAppDragStartResult, bj as MindooDBAppDragProfile, bM as MindooDBAppIncomingResult } from '../types-ClOh6z0L.js';
1
+ import { ay as MindooDBAppComponentInfo, bq as MindooDBAppEmbedRect, I as MindooDBAppAgentToolDescriptor, f as MindooDBAppAgentToolErrorCode, V as MindooDBAppBridgeAgentResultMessage, U as MindooDBAppBridgeAgentInvokeMessage, aU as MindooDBAppCreateViewNavigatorInput, de as MindooDBAppViewNavigator, ad as MindooDBAppBridgeRpcRequest, bC as MindooDBAppHostTheme, av as MindooDBAppCapability, j as MindooDBAppDefinition, J as MindooDBAppAttachmentApi, e as MindooDBAppBridge, cQ as MindooDBAppSession, cg as MindooDBAppProposeAppInput, dm as MindooDBAppViewport, d8 as MindooDBAppUiPreferences, cm as MindooDBAppQueryResult, dl as MindooDBAppViewUpdateStats, a9 as MindooDBAppBridgePortMessage, aV as MindooDBAppDatabaseInfo, bl as MindooDBAppEmbedCloseReason, a4 as MindooDBAppBridgeErrorPayload, bX as MindooDBAppLaunchContext, b2 as MindooDBAppDocumentApi, dg as MindooDBAppViewNavigatorOpenOptions, bD as MindooDBAppIdentityApi, d4 as MindooDBAppTimestampApi, b0 as MindooDBAppDirectoryApi, l as MindooDBAppDocument, bz as MindooDBAppFulltextSetup, by as MindooDBAppExtractionSetup, cY as MindooDBAppSummarySetup, ch as MindooDBAppProposeAppResult, bN as MindooDBAppIncomingSource, c as MindooDBAppDragStartResult, bj as MindooDBAppDragProfile, bM as MindooDBAppIncomingResult } from '../types-BeRUxGKN.js';
2
2
  import * as mindoodb from 'mindoodb';
3
3
  import 'mindoodb-view-language';
4
4
 
@@ -21,6 +21,118 @@ interface MockEmbeddingFinish {
21
21
  message?: string;
22
22
  }
23
23
 
24
+ /**
25
+ * Haven's side of agent tools, for tests: what `session.agent` talks to.
26
+ *
27
+ * It accepts the app's tool declarations and context with Haven's validation, keeps
28
+ * the files the app hands over (`provideFile`), lets a test hand the app files the way
29
+ * Haven imports them from the exchange folder (`takeFile`), and calls the app's tools
30
+ * over the bridge port like Haven's broker, including the consent step for tools
31
+ * marked `consequentialHint`.
32
+ */
33
+
34
+ /** A file in the mock exchange: handed over by the app, or imported for it by the test. */
35
+ interface MockAgentFile {
36
+ fileRef: string;
37
+ name: string;
38
+ mimeType: string;
39
+ data: Uint8Array;
40
+ /** `app`: the app produced it (`provideFile`); `host`: imported for the app (`takeFile`). */
41
+ source: "app" | "host";
42
+ createdAt: number;
43
+ expiresAt: number;
44
+ }
45
+ /** How Haven's consent dialog answers a call to a consequential tool. */
46
+ type MockAgentConsent = "allow" | "deny" | ((call: {
47
+ tool: string;
48
+ input: Record<string, unknown>;
49
+ }) => boolean | Promise<boolean>);
50
+ type MockAgentCallResult = {
51
+ ok: true;
52
+ result: unknown;
53
+ durationMs: number;
54
+ } | {
55
+ ok: false;
56
+ error: {
57
+ code: MindooDBAppAgentToolErrorCode | "LOCKED" | string;
58
+ message: string;
59
+ requiredAction?: string;
60
+ };
61
+ durationMs: number;
62
+ };
63
+ /** One tool call as the log records it. */
64
+ interface MockAgentCall {
65
+ callId: string;
66
+ tool: string;
67
+ input: Record<string, unknown>;
68
+ startedAt: number;
69
+ outcome?: MockAgentCallResult;
70
+ }
71
+ interface MockAgentHostOptions {
72
+ /**
73
+ * The prefix agents see in front of the app's tool names (`agentToolPrefix` in
74
+ * `haven-app.json`). Without one it is derived from the app id, as Haven does.
75
+ */
76
+ agentToolPrefix?: string;
77
+ /** Whether the user has agent access on (`registerTools` answers `enabled`). Default true. */
78
+ agentToolsEnabled?: boolean;
79
+ /** Answer of the consent dialog for consequential tools. Default `"allow"`. */
80
+ agentConsent?: MockAgentConsent;
81
+ /** How long a call waits for the app. Default 120 s, Haven's limit. */
82
+ agentInvokeTimeoutMs?: number;
83
+ }
84
+ interface MockAgentHostController {
85
+ /** The app's tools as it declared them (without prefix). */
86
+ tools(): MindooDBAppAgentToolDescriptor[];
87
+ /** The names agents see: `<prefix>_<name>`. */
88
+ exposedNames(): string[];
89
+ readonly prefix: string;
90
+ /** What the app last passed to `setContext`. */
91
+ context(): Record<string, unknown> | null;
92
+ isEnabled(): boolean;
93
+ setEnabled(enabled: boolean): void;
94
+ setConsent(consent: MockAgentConsent): void;
95
+ /**
96
+ * Calls one of the app's tools as an agent would, by its own name or the prefixed
97
+ * one. Never throws: failures come back as `{ ok: false, error }` with the code the
98
+ * agent would see.
99
+ */
100
+ call(tool: string, input?: Record<string, unknown>): Promise<MockAgentCallResult>;
101
+ /** Every call so far, oldest first. */
102
+ calls(): ReadonlyArray<MockAgentCall>;
103
+ /** Puts a file in the exchange for the app, as Haven's `haven_files_import`; returns its fileRef. */
104
+ importFile(file: {
105
+ name: string;
106
+ mimeType?: string;
107
+ data: Uint8Array | ArrayBuffer | Blob | string;
108
+ }): Promise<string>;
109
+ /** A file the app handed over (`provideFile`), or `null` when unknown or expired. */
110
+ getFile(fileRef: string): MockAgentFile | null;
111
+ /** Files in the exchange (both directions), newest first. */
112
+ files(): MockAgentFile[];
113
+ }
114
+ /** Haven's derivation when the app declares no prefix (`agentAppKey`). */
115
+ declare function mockAgentToolPrefix(appId: string, declared?: string): string;
116
+ /** Haven's checks on declared tools (`validateAgentToolDescriptors`). */
117
+ declare function validateMockAgentTools(raw: unknown): MindooDBAppAgentToolDescriptor[];
118
+ interface MockAgentHost {
119
+ controller: MockAgentHostController;
120
+ /** Answers `agent.*` requests; `undefined` for any other method. */
121
+ dispatch(method: string, params: unknown): Promise<{
122
+ result: unknown;
123
+ } | undefined>;
124
+ /** The app's `agent-result` message. */
125
+ handleResult(message: MindooDBAppBridgeAgentResultMessage): void;
126
+ /** Fails pending calls (the app closed or reloaded). */
127
+ reset(reason?: string): void;
128
+ }
129
+ declare function createMockAgentHost(options: MockAgentHostOptions & {
130
+ appId: () => string;
131
+ /** Posts to the app's port; `false` when no app is connected. */
132
+ post(message: MindooDBAppBridgeAgentInvokeMessage): boolean;
133
+ onChange?(): void;
134
+ }): MockAgentHost;
135
+
24
136
  type EvaluatingViewDocument = {
25
137
  origin: string;
26
138
  docId: string;
@@ -105,6 +217,12 @@ interface CreateBrowserTestHostOptions extends CreateFakeBridgeHostOptions {
105
217
  interface BrowserTestHost {
106
218
  host: FakeBridgeHostController;
107
219
  log: BrowserTestHostLog;
220
+ /**
221
+ * Haven's agent-tool side (same as `host.agent`): the app's declared tools and
222
+ * context, `call(tool, input)` as an agent would, `importFile()` to hand the app a
223
+ * file for `takeFile`, and `getFile()` for what it handed over with `provideFile`.
224
+ */
225
+ agent: MockAgentHostController;
108
226
  /**
109
227
  * Decides the outcome of the app's next `attachments.scan()`: a file to "scan", or
110
228
  * `null` to act as if the user cancelled. Later scans follow {@link setScanMode}.
@@ -451,8 +569,10 @@ type FakeBridgeRpcResponse = {
451
569
  error: MindooDBAppBridgeErrorPayload;
452
570
  durationMs: number;
453
571
  };
454
- interface CreateFakeBridgeHostOptions extends CreateMockMindooDBAppSessionOptions {
572
+ interface CreateFakeBridgeHostOptions extends CreateMockMindooDBAppSessionOptions, MockAgentHostOptions {
455
573
  windowMode?: "parent" | "opener";
574
+ /** Called when the app's agent tools, context or exchange files change, and after each tool call. */
575
+ onAgentChange?: () => void;
456
576
  requestHandlers?: Record<string, FakeBridgeRequestHandler>;
457
577
  /** Called for every RPC request the app sends, before it is answered. */
458
578
  onRequest?: (request: MindooDBAppBridgeRpcRequest) => void;
@@ -519,7 +639,12 @@ interface FakeBridgeHostController {
519
639
  * over the port and resolves or rejects with the app's answer.
520
640
  */
521
641
  requestEmbeddingSave(): Promise<void>;
642
+ /**
643
+ * Haven's agent-tool side: the tools and context the app declared, files in the
644
+ * exchange, and `call()` to run a tool as an agent would.
645
+ */
646
+ readonly agent: MockAgentHostController;
522
647
  }
523
648
  declare function createFakeBridgeHost(options?: CreateFakeBridgeHostOptions): FakeBridgeHostController;
524
649
 
525
- export { ALL_CAPABILITIES, type BrowserTestHost, type BrowserTestHostLog, type BrowserTestHostScan, type BrowserTestHostScanMode, type CreateBrowserTestHostOptions, type CreateFakeBridgeHostOptions, type CreateMockMindooDBAppSessionOptions, type EvaluatingViewDocument, type FakeBridgeHostController, type FakeBridgePortMessageHandler, type FakeBridgeRequestHandler, type FakeBridgeRpcResponse, type MockDatabaseOverride, type MockDatabasesFromDefinitionOptions, type MockEmbedState, type MockEmbeddingFinish, MockForbiddenError, type MockIncomingContentInput, type MockMindooDBAppDatabaseDefinition, type MockMindooDBAppSessionController, type MockSealedChannelOptions, type MockSeedDocument, type MountHavenTestHostOptions, TEST_HOST_LOCALES, type TestHostUrlSettings, capabilitiesForDefinitionPermissions, createBrowserTestHost, createEvaluatingViewNavigator, createFakeBridgeHost, createMemoryAttachments, createMockMindooDBAppBridge, createMockMindooDBAppSession, generateDirectoryUsers, loadMindoodbForTesting, mockDatabasesFromDefinition, mountHavenTestHost, previewJson, readTestHostUrlSettings };
650
+ export { ALL_CAPABILITIES, type BrowserTestHost, type BrowserTestHostLog, type BrowserTestHostScan, type BrowserTestHostScanMode, type CreateBrowserTestHostOptions, type CreateFakeBridgeHostOptions, type CreateMockMindooDBAppSessionOptions, type EvaluatingViewDocument, type FakeBridgeHostController, type FakeBridgePortMessageHandler, type FakeBridgeRequestHandler, type FakeBridgeRpcResponse, type MockAgentCall, type MockAgentCallResult, type MockAgentConsent, type MockAgentFile, type MockAgentHost, type MockAgentHostController, type MockAgentHostOptions, type MockDatabaseOverride, type MockDatabasesFromDefinitionOptions, type MockEmbedState, type MockEmbeddingFinish, MockForbiddenError, type MockIncomingContentInput, type MockMindooDBAppDatabaseDefinition, type MockMindooDBAppSessionController, type MockSealedChannelOptions, type MockSeedDocument, type MountHavenTestHostOptions, TEST_HOST_LOCALES, type TestHostUrlSettings, capabilitiesForDefinitionPermissions, createBrowserTestHost, createEvaluatingViewNavigator, createFakeBridgeHost, createMemoryAttachments, createMockAgentHost, createMockMindooDBAppBridge, createMockMindooDBAppSession, generateDirectoryUsers, loadMindoodbForTesting, mockAgentToolPrefix, mockDatabasesFromDefinition, mountHavenTestHost, previewJson, readTestHostUrlSettings, validateMockAgentTools };
@@ -4,7 +4,7 @@ import {
4
4
  mindooDBAppComponentFieldsMatch,
5
5
  pageUsers,
6
6
  releaseMindooDBAppBridgeSessions
7
- } from "../chunk-QQRLEHFE.js";
7
+ } from "../chunk-BE7LLFFE.js";
8
8
 
9
9
  // src/testing/index.ts
10
10
  import * as Automerge from "@automerge/automerge";
@@ -102,7 +102,7 @@ function getCategoryPath(entry) {
102
102
  }
103
103
  return path;
104
104
  }
105
- function serializeEntry(navigator, entry) {
105
+ function serializeEntry(navigator2, entry) {
106
106
  const parent = entry.getParent();
107
107
  return {
108
108
  key: entry.getPositionStr(),
@@ -122,8 +122,8 @@ function serializeEntry(navigator, entry) {
122
122
  childCategoryCount: entry.getChildCategoryCount(),
123
123
  childDocumentCount: entry.getChildDocumentCount(),
124
124
  position: entry.getPositionStr(),
125
- expanded: entry.isCategory() ? navigator.isExpanded(entry) : false,
126
- selected: navigator.isSelected(entry.origin, entry.docId),
125
+ expanded: entry.isCategory() ? navigator2.isExpanded(entry) : false,
126
+ selected: navigator2.isSelected(entry.origin, entry.docId),
127
127
  isVisible: true
128
128
  };
129
129
  }
@@ -208,21 +208,21 @@ function createCoreNavigator(mindoodb, view, options) {
208
208
  function normalizePageLimit(limit) {
209
209
  return Math.max(1, Math.min(1e3, Math.trunc(limit ?? 100)));
210
210
  }
211
- function pageEntries(navigator, direction, options = {}) {
211
+ function pageEntries(navigator2, direction, options = {}) {
212
212
  const entries = [];
213
- const startOk = options.startPosition ? navigator.gotoPos(options.startPosition) : direction === "forward" ? navigator.gotoFirst() : navigator.gotoLast();
213
+ const startOk = options.startPosition ? navigator2.gotoPos(options.startPosition) : direction === "forward" ? navigator2.gotoFirst() : navigator2.gotoLast();
214
214
  if (!startOk) {
215
215
  return { entries, nextPosition: null, hasMore: false };
216
216
  }
217
217
  const moveNext = () => {
218
218
  if (direction === "forward") {
219
- return options.selectedOnly ? navigator.gotoNextSelected() : navigator.gotoNext();
219
+ return options.selectedOnly ? navigator2.gotoNextSelected() : navigator2.gotoNext();
220
220
  }
221
- return options.selectedOnly ? navigator.gotoPrevSelected() : navigator.gotoPrev();
221
+ return options.selectedOnly ? navigator2.gotoPrevSelected() : navigator2.gotoPrev();
222
222
  };
223
223
  if (options.selectedOnly) {
224
- const current = navigator.getCurrentEntry();
225
- if (!current || !navigator.isSelected(current.origin, current.docId)) {
224
+ const current = navigator2.getCurrentEntry();
225
+ if (!current || !navigator2.isSelected(current.origin, current.docId)) {
226
226
  if (!moveNext()) return { entries, nextPosition: null, hasMore: false };
227
227
  }
228
228
  }
@@ -230,26 +230,26 @@ function pageEntries(navigator, direction, options = {}) {
230
230
  let nextPosition = null;
231
231
  let hasMore = false;
232
232
  while (true) {
233
- const current = navigator.getCurrentEntry();
233
+ const current = navigator2.getCurrentEntry();
234
234
  if (!current) break;
235
- entries.push(serializeEntry(navigator, current));
235
+ entries.push(serializeEntry(navigator2, current));
236
236
  if (entries.length >= limit) {
237
237
  hasMore = moveNext();
238
- nextPosition = hasMore ? navigator.getCurrentEntry()?.getPositionStr() ?? null : null;
238
+ nextPosition = hasMore ? navigator2.getCurrentEntry()?.getPositionStr() ?? null : null;
239
239
  break;
240
240
  }
241
241
  if (!moveNext()) break;
242
242
  }
243
243
  return { entries, nextPosition, hasMore };
244
244
  }
245
- function resolveEntry(navigator, entryKey) {
246
- const entry = navigator.getPos(entryKey);
245
+ function resolveEntry(navigator2, entryKey) {
246
+ const entry = navigator2.getPos(entryKey);
247
247
  if (!entry) {
248
248
  throw new Error(`Unknown view entry: ${entryKey}`);
249
249
  }
250
250
  return entry;
251
251
  }
252
- function wrapNavigator(definition, navigator) {
252
+ function wrapNavigator(definition, navigator2) {
253
253
  const updateListeners = /* @__PURE__ */ new Set();
254
254
  let viewCursor = null;
255
255
  return {
@@ -267,164 +267,164 @@ function wrapNavigator(definition, navigator) {
267
267
  return () => updateListeners.delete(listener);
268
268
  },
269
269
  async getCurrentEntry() {
270
- const entry = navigator.getCurrentEntry();
271
- return entry ? serializeEntry(navigator, entry) : null;
270
+ const entry = navigator2.getCurrentEntry();
271
+ return entry ? serializeEntry(navigator2, entry) : null;
272
272
  },
273
273
  async gotoFirst() {
274
- return navigator.gotoFirst();
274
+ return navigator2.gotoFirst();
275
275
  },
276
276
  async gotoLast() {
277
- return navigator.gotoLast();
277
+ return navigator2.gotoLast();
278
278
  },
279
279
  async gotoNext() {
280
- return navigator.gotoNext();
280
+ return navigator2.gotoNext();
281
281
  },
282
282
  async gotoPrev() {
283
- return navigator.gotoPrev();
283
+ return navigator2.gotoPrev();
284
284
  },
285
285
  async gotoNextSibling() {
286
- return navigator.gotoNextSibling();
286
+ return navigator2.gotoNextSibling();
287
287
  },
288
288
  async gotoPrevSibling() {
289
- return navigator.gotoPrevSibling();
289
+ return navigator2.gotoPrevSibling();
290
290
  },
291
291
  async gotoParent() {
292
- return navigator.gotoParent();
292
+ return navigator2.gotoParent();
293
293
  },
294
294
  async gotoFirstChild() {
295
- return navigator.gotoFirstChild();
295
+ return navigator2.gotoFirstChild();
296
296
  },
297
297
  async gotoLastChild() {
298
- return navigator.gotoLastChild();
298
+ return navigator2.gotoLastChild();
299
299
  },
300
300
  async gotoPos(position) {
301
- return navigator.gotoPos(position);
301
+ return navigator2.gotoPos(position);
302
302
  },
303
303
  async getPos(position) {
304
- const entry = navigator.getPos(position);
305
- return entry ? serializeEntry(navigator, entry) : null;
304
+ const entry = navigator2.getPos(position);
305
+ return entry ? serializeEntry(navigator2, entry) : null;
306
306
  },
307
307
  async findCategoryEntryByParts(parts) {
308
- const entry = navigator.findCategoryEntryByParts(parts);
309
- return entry ? serializeEntry(navigator, entry) : null;
308
+ const entry = navigator2.findCategoryEntryByParts(parts);
309
+ return entry ? serializeEntry(navigator2, entry) : null;
310
310
  },
311
311
  async entriesForward(options) {
312
- return pageEntries(navigator, "forward", options);
312
+ return pageEntries(navigator2, "forward", options);
313
313
  },
314
314
  async entriesBackward(options) {
315
- return pageEntries(navigator, "backward", options);
315
+ return pageEntries(navigator2, "backward", options);
316
316
  },
317
317
  async gotoNextSelected() {
318
- return navigator.gotoNextSelected();
318
+ return navigator2.gotoNextSelected();
319
319
  },
320
320
  async gotoPrevSelected() {
321
- return navigator.gotoPrevSelected();
321
+ return navigator2.gotoPrevSelected();
322
322
  },
323
323
  async select(origin, docId, selectParentCategories = false) {
324
- navigator.select(origin, docId, selectParentCategories);
324
+ navigator2.select(origin, docId, selectParentCategories);
325
325
  },
326
326
  async deselect(origin, docId) {
327
- navigator.deselect(origin, docId);
327
+ navigator2.deselect(origin, docId);
328
328
  },
329
329
  async selectAllEntries() {
330
- navigator.selectAllEntries();
330
+ navigator2.selectAllEntries();
331
331
  },
332
332
  async deselectAllEntries() {
333
- navigator.deselectAllEntries();
333
+ navigator2.deselectAllEntries();
334
334
  },
335
335
  async isSelected(origin, docId) {
336
- return navigator.isSelected(origin, docId);
336
+ return navigator2.isSelected(origin, docId);
337
337
  },
338
338
  async getSelectionState() {
339
339
  return {
340
- selectAllByDefault: navigator.isSelectAllByDefault(),
341
- entryKeys: Array.from(navigator.getSelectedOrDeselectedEntries())
340
+ selectAllByDefault: navigator2.isSelectAllByDefault(),
341
+ entryKeys: Array.from(navigator2.getSelectedOrDeselectedEntries())
342
342
  };
343
343
  },
344
344
  async setSelectionState(state) {
345
- if (state.selectAllByDefault) navigator.selectAllEntries();
346
- else navigator.deselectAllEntries();
347
- navigator.setSelectedOrDeselectedEntries(new Set(state.entryKeys));
345
+ if (state.selectAllByDefault) navigator2.selectAllEntries();
346
+ else navigator2.deselectAllEntries();
347
+ navigator2.setSelectedOrDeselectedEntries(new Set(state.entryKeys));
348
348
  },
349
349
  async expand(origin, docId) {
350
- navigator.expand(origin, docId);
350
+ navigator2.expand(origin, docId);
351
351
  },
352
352
  async collapse(origin, docId) {
353
- navigator.collapse(origin, docId);
353
+ navigator2.collapse(origin, docId);
354
354
  },
355
355
  async expandAll() {
356
- navigator.expandAll();
356
+ navigator2.expandAll();
357
357
  },
358
358
  async collapseAll() {
359
- navigator.collapseAll();
359
+ navigator2.collapseAll();
360
360
  },
361
361
  async expandToLevel(level) {
362
- navigator.expandToLevel(level);
362
+ navigator2.expandToLevel(level);
363
363
  },
364
364
  async isExpanded(entryKey) {
365
- const entry = navigator.getPos(entryKey);
366
- return entry ? navigator.isExpanded(entry) : false;
365
+ const entry = navigator2.getPos(entryKey);
366
+ return entry ? navigator2.isExpanded(entry) : false;
367
367
  },
368
368
  async getExpansionState() {
369
369
  return {
370
- expandAllByDefault: navigator.isExpandAllByDefault(),
371
- expandLevel: navigator.getExpandLevel(),
372
- entryKeys: Array.from(navigator.getExpandedOrCollapsedEntries())
370
+ expandAllByDefault: navigator2.isExpandAllByDefault(),
371
+ expandLevel: navigator2.getExpandLevel(),
372
+ entryKeys: Array.from(navigator2.getExpandedOrCollapsedEntries())
373
373
  };
374
374
  },
375
375
  async setExpansionState(state) {
376
- if (state.expandAllByDefault) navigator.expandAll();
377
- else navigator.collapseAll();
378
- navigator.expandToLevel(state.expandLevel);
379
- navigator.setExpandedOrCollapsedEntries(new Set(state.entryKeys));
376
+ if (state.expandAllByDefault) navigator2.expandAll();
377
+ else navigator2.collapseAll();
378
+ navigator2.expandToLevel(state.expandLevel);
379
+ navigator2.setExpandedOrCollapsedEntries(new Set(state.entryKeys));
380
380
  },
381
381
  async childEntries(entryKey, descending) {
382
- return navigator.childEntries(resolveEntry(navigator, entryKey), descending).map((entry) => serializeEntry(navigator, entry));
382
+ return navigator2.childEntries(resolveEntry(navigator2, entryKey), descending).map((entry) => serializeEntry(navigator2, entry));
383
383
  },
384
384
  async childCategories(entryKey, descending) {
385
- return navigator.childCategories(resolveEntry(navigator, entryKey), descending).map((entry) => serializeEntry(navigator, entry));
385
+ return navigator2.childCategories(resolveEntry(navigator2, entryKey), descending).map((entry) => serializeEntry(navigator2, entry));
386
386
  },
387
387
  async childDocuments(entryKey, descending) {
388
- return navigator.childDocuments(resolveEntry(navigator, entryKey), descending).map((entry) => serializeEntry(navigator, entry));
388
+ return navigator2.childDocuments(resolveEntry(navigator2, entryKey), descending).map((entry) => serializeEntry(navigator2, entry));
389
389
  },
390
390
  async childCategoriesByKey(entryKey, key, exact, descending) {
391
- return navigator.childCategoriesByKey(
392
- resolveEntry(navigator, entryKey),
391
+ return navigator2.childCategoriesByKey(
392
+ resolveEntry(navigator2, entryKey),
393
393
  String(key ?? ""),
394
394
  Boolean(exact),
395
395
  descending
396
- ).map((entry) => serializeEntry(navigator, entry));
396
+ ).map((entry) => serializeEntry(navigator2, entry));
397
397
  },
398
398
  async childDocumentsByKey(entryKey, key, exact, descending) {
399
- return navigator.childDocumentsByKey(
400
- resolveEntry(navigator, entryKey),
399
+ return navigator2.childDocumentsByKey(
400
+ resolveEntry(navigator2, entryKey),
401
401
  String(key ?? ""),
402
402
  Boolean(exact),
403
403
  descending
404
- ).map((entry) => serializeEntry(navigator, entry));
404
+ ).map((entry) => serializeEntry(navigator2, entry));
405
405
  },
406
406
  async childCategoriesBetween(entryKey, range) {
407
- return navigator.childCategoriesBetween(
408
- resolveEntry(navigator, entryKey),
407
+ return navigator2.childCategoriesBetween(
408
+ resolveEntry(navigator2, entryKey),
409
409
  range.startKey,
410
410
  range.endKey,
411
411
  range.descending
412
- ).map((entry) => serializeEntry(navigator, entry));
412
+ ).map((entry) => serializeEntry(navigator2, entry));
413
413
  },
414
414
  async childDocumentsBetween(entryKey, range) {
415
- return navigator.childDocumentsBetween(
416
- resolveEntry(navigator, entryKey),
415
+ return navigator2.childDocumentsBetween(
416
+ resolveEntry(navigator2, entryKey),
417
417
  range.startKey,
418
418
  range.endKey,
419
419
  range.descending
420
- ).map((entry) => serializeEntry(navigator, entry));
420
+ ).map((entry) => serializeEntry(navigator2, entry));
421
421
  },
422
422
  async getSortedDocIds(descending) {
423
- const page = pageEntries(navigator, descending ? "backward" : "forward", { limit: 1e3 });
423
+ const page = pageEntries(navigator2, descending ? "backward" : "forward", { limit: 1e3 });
424
424
  return collectSortedDocIds(page.entries);
425
425
  },
426
426
  async getSortedDocIdsScoped(entryKey, descending) {
427
- const docs = navigator.childDocuments(resolveEntry(navigator, entryKey), descending);
427
+ const docs = navigator2.childDocuments(resolveEntry(navigator2, entryKey), descending);
428
428
  return docs.map((entry) => ({ origin: entry.origin, docId: entry.docId }));
429
429
  },
430
430
  async dispose() {
@@ -453,8 +453,8 @@ async function createEvaluatingViewNavigator(input, documents) {
453
453
  documents,
454
454
  input.categorizationStyle
455
455
  );
456
- const navigator = createCoreNavigator(mindoodb, view, input.options);
457
- return wrapNavigator(input.definition, navigator);
456
+ const navigator2 = createCoreNavigator(mindoodb, view, input.options);
457
+ return wrapNavigator(input.definition, navigator2);
458
458
  }
459
459
 
460
460
  // src/testing/mockEmbeds.ts
@@ -828,6 +828,246 @@ function guardDatabase(database, currentInfo, enforce) {
828
828
  };
829
829
  }
830
830
 
831
+ // src/testing/mockAgentTools.ts
832
+ var TOOL_NAME_PATTERN = /^[a-z][a-z0-9_]{0,47}$/;
833
+ var PREFIX_PATTERN = /^[a-z][a-z0-9_]{0,15}$/;
834
+ var MOCK_AGENT_MAX_TOOLS = 64;
835
+ var MOCK_AGENT_MAX_DESCRIPTION = 2e3;
836
+ var MOCK_AGENT_FILE_TTL_MS = 10 * 60 * 1e3;
837
+ var MOCK_AGENT_FILE_MAX_BYTES = 50 * 1024 * 1024;
838
+ var DEFAULT_INVOKE_TIMEOUT_MS = 12e4;
839
+ function invalid(message) {
840
+ return Object.assign(new Error(message), { name: "invalid-params" });
841
+ }
842
+ function notFound(message) {
843
+ return Object.assign(new Error(message), { name: "not-found" });
844
+ }
845
+ function mockAgentToolPrefix(appId, declared) {
846
+ if (declared && PREFIX_PATTERN.test(declared) && !declared.endsWith("_") && !/^haven(?:_|$)/.test(declared)) {
847
+ return declared;
848
+ }
849
+ const key = (appId.split("\\").pop() ?? "").trim().toLowerCase().replace(/^mindoodb-app-/, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16).replace(/_+$/, "");
850
+ if (!key) return "app";
851
+ return /^[a-z]/.test(key) ? key : `app_${key}`.slice(0, 16);
852
+ }
853
+ function validateMockAgentTools(raw) {
854
+ if (!Array.isArray(raw)) throw invalid("tools must be an array.");
855
+ if (raw.length > MOCK_AGENT_MAX_TOOLS) throw invalid(`An app may offer at most ${MOCK_AGENT_MAX_TOOLS} agent tools.`);
856
+ const seen = /* @__PURE__ */ new Set();
857
+ return raw.map((entry) => {
858
+ const tool = entry;
859
+ if (!tool || typeof tool.name !== "string" || !TOOL_NAME_PATTERN.test(tool.name)) {
860
+ throw invalid(`Invalid agent tool name "${String(tool?.name)}".`);
861
+ }
862
+ if (seen.has(tool.name)) throw invalid(`Agent tool "${tool.name}" is declared twice.`);
863
+ seen.add(tool.name);
864
+ if (typeof tool.description !== "string" || !tool.description.trim()) {
865
+ throw invalid(`Agent tool "${tool.name}" needs a description.`);
866
+ }
867
+ if (tool.description.length > MOCK_AGENT_MAX_DESCRIPTION) {
868
+ throw invalid(`The description of agent tool "${tool.name}" is too long.`);
869
+ }
870
+ const schema = tool.inputSchema;
871
+ if (!schema || typeof schema !== "object" || Array.isArray(schema) || schema.type !== "object") {
872
+ throw invalid(`Agent tool "${tool.name}" needs an object inputSchema.`);
873
+ }
874
+ const annotations = tool.annotations ?? {};
875
+ for (const key of ["readOnlyHint", "consequentialHint", "untrustedContentHint"]) {
876
+ if (annotations[key] !== void 0 && typeof annotations[key] !== "boolean") {
877
+ throw invalid(`Agent tool "${tool.name}" has invalid annotations.`);
878
+ }
879
+ }
880
+ return {
881
+ name: tool.name,
882
+ description: tool.description,
883
+ inputSchema: schema,
884
+ ...tool.annotations ? { annotations: { ...tool.annotations } } : {}
885
+ };
886
+ });
887
+ }
888
+ async function bytesOf(data) {
889
+ if (typeof data === "string") return new TextEncoder().encode(data);
890
+ if (data instanceof Uint8Array) return data.slice();
891
+ if (typeof Blob !== "undefined" && data instanceof Blob) return new Uint8Array(await data.arrayBuffer());
892
+ return new Uint8Array(data);
893
+ }
894
+ function structuralBytes(data) {
895
+ if (ArrayBuffer.isView(data)) return new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice();
896
+ if (Object.prototype.toString.call(data) === "[object ArrayBuffer]") return new Uint8Array(data).slice();
897
+ return null;
898
+ }
899
+ function createMockAgentHost(options) {
900
+ const prefix = mockAgentToolPrefix(options.appId(), options.agentToolPrefix);
901
+ let tools = [];
902
+ let context = null;
903
+ let enabled = options.agentToolsEnabled ?? true;
904
+ let consent = options.agentConsent ?? "allow";
905
+ const timeoutMs = options.agentInvokeTimeoutMs ?? DEFAULT_INVOKE_TIMEOUT_MS;
906
+ const files = /* @__PURE__ */ new Map();
907
+ const calls = [];
908
+ const pending = /* @__PURE__ */ new Map();
909
+ let counter = 0;
910
+ const changed = () => options.onChange?.();
911
+ function prune(now = Date.now()) {
912
+ for (const [ref, file] of files) if (file.expiresAt <= now) files.delete(ref);
913
+ }
914
+ function addFile(file) {
915
+ prune();
916
+ if (file.data.length > MOCK_AGENT_FILE_MAX_BYTES) {
917
+ throw invalid(`Files for agents may be at most ${MOCK_AGENT_FILE_MAX_BYTES / 1024 / 1024} MB.`);
918
+ }
919
+ const now = Date.now();
920
+ counter += 1;
921
+ const entry = {
922
+ ...file,
923
+ fileRef: `file_test${counter.toString(16).padStart(4, "0")}`,
924
+ createdAt: now,
925
+ expiresAt: now + MOCK_AGENT_FILE_TTL_MS
926
+ };
927
+ files.set(entry.fileRef, entry);
928
+ changed();
929
+ return entry;
930
+ }
931
+ function registration() {
932
+ return { enabled, exposedNames: tools.map((tool) => `${prefix}_${tool.name}`) };
933
+ }
934
+ async function dispatch(method, params) {
935
+ const input = params ?? {};
936
+ if (method === "agent.registerTools") {
937
+ tools = validateMockAgentTools(input.tools);
938
+ changed();
939
+ return { result: registration() };
940
+ }
941
+ if (method === "agent.setContext") {
942
+ const next = input.context ?? null;
943
+ if (next !== null && (typeof next !== "object" || Array.isArray(next))) {
944
+ throw invalid("context must be an object or null.");
945
+ }
946
+ context = next;
947
+ changed();
948
+ return { result: null };
949
+ }
950
+ if (method === "agent.provideFile") {
951
+ if (typeof input.name !== "string" || !input.name.trim()) throw invalid("provideFile needs a file name.");
952
+ const data = structuralBytes(input.data);
953
+ if (!data) throw invalid("provideFile needs the file content as an ArrayBuffer.");
954
+ const mimeType = typeof input.mimeType === "string" && input.mimeType ? input.mimeType : "application/octet-stream";
955
+ const file = addFile({ name: input.name.trim(), mimeType, data, source: "app" });
956
+ return { result: { fileRef: file.fileRef, size: file.data.length } };
957
+ }
958
+ if (method === "agent.takeFile") {
959
+ prune();
960
+ const file = typeof input.fileRef === "string" ? files.get(input.fileRef) : void 0;
961
+ if (!file || file.source !== "host") throw notFound("Unknown or expired file reference.");
962
+ return { result: { name: file.name, mimeType: file.mimeType, data: file.data.slice().buffer } };
963
+ }
964
+ return void 0;
965
+ }
966
+ function finish(callId, outcome) {
967
+ const call2 = calls.find((entry) => entry.callId === callId);
968
+ if (call2) call2.outcome = outcome;
969
+ changed();
970
+ return outcome;
971
+ }
972
+ async function call(name, input = {}) {
973
+ counter += 1;
974
+ const callId = `test-call-${counter}`;
975
+ const startedAt = Date.now();
976
+ const toolName = name.startsWith(`${prefix}_`) && !tools.some((t) => t.name === name) ? name.slice(prefix.length + 1) : name;
977
+ calls.push({ callId, tool: toolName, input, startedAt });
978
+ changed();
979
+ const failWith = (code, message, requiredAction) => finish(callId, {
980
+ ok: false,
981
+ error: { code, message, ...requiredAction ? { requiredAction } : {} },
982
+ durationMs: Date.now() - startedAt
983
+ });
984
+ if (!enabled) return failWith("NOT_ALLOWED", "The user has agent tools switched off for this app.");
985
+ const tool = tools.find((entry) => entry.name === toolName);
986
+ if (!tool) return failWith("NOT_FOUND", `The app offers no tool "${name}".`);
987
+ if (tool.annotations?.consequentialHint) {
988
+ const allowed = typeof consent === "function" ? await consent({ tool: toolName, input }) : consent === "allow";
989
+ if (!allowed) return failWith("NOT_ALLOWED", "The user declined this call.", "ask the user before trying again");
990
+ }
991
+ return await new Promise((resolve) => {
992
+ const timer = setTimeout(() => {
993
+ pending.delete(callId);
994
+ resolve(failWith("FAILED", `The app did not answer "${toolName}" in time.`));
995
+ }, timeoutMs);
996
+ pending.set(callId, { resolve, startedAt, timer });
997
+ const posted = options.post({ protocol: "mindoodb-app-bridge", kind: "agent-invoke", callId, tool: toolName, input });
998
+ if (!posted) {
999
+ clearTimeout(timer);
1000
+ pending.delete(callId);
1001
+ resolve(failWith("INVALID_STATE", "The app is not connected."));
1002
+ }
1003
+ });
1004
+ }
1005
+ function handleResult(message) {
1006
+ const entry = pending.get(message.callId);
1007
+ if (!entry) return;
1008
+ pending.delete(message.callId);
1009
+ clearTimeout(entry.timer);
1010
+ const durationMs = Date.now() - entry.startedAt;
1011
+ entry.resolve(
1012
+ finish(
1013
+ message.callId,
1014
+ message.ok ? { ok: true, result: message.result ?? null, durationMs } : {
1015
+ ok: false,
1016
+ error: {
1017
+ code: message.error?.code ?? "FAILED",
1018
+ message: message.error?.message ?? "The app reported an error.",
1019
+ ...message.error?.requiredAction ? { requiredAction: message.error.requiredAction } : {}
1020
+ },
1021
+ durationMs
1022
+ }
1023
+ )
1024
+ );
1025
+ }
1026
+ function reset(reason = "The app was closed.") {
1027
+ for (const [callId, entry] of pending) {
1028
+ clearTimeout(entry.timer);
1029
+ entry.resolve(
1030
+ finish(callId, { ok: false, error: { code: "INVALID_STATE", message: reason }, durationMs: Date.now() - entry.startedAt })
1031
+ );
1032
+ }
1033
+ pending.clear();
1034
+ tools = [];
1035
+ context = null;
1036
+ changed();
1037
+ }
1038
+ const controller = {
1039
+ prefix,
1040
+ tools: () => tools.map((tool) => ({ ...tool })),
1041
+ exposedNames: () => registration().exposedNames,
1042
+ context: () => context,
1043
+ isEnabled: () => enabled,
1044
+ setEnabled(next) {
1045
+ enabled = next;
1046
+ changed();
1047
+ },
1048
+ setConsent(next) {
1049
+ consent = next;
1050
+ },
1051
+ call,
1052
+ calls: () => calls,
1053
+ async importFile(file) {
1054
+ const data = await bytesOf(file.data);
1055
+ const mimeType = file.mimeType || (typeof Blob !== "undefined" && file.data instanceof Blob ? file.data.type : "") || "application/octet-stream";
1056
+ return addFile({ name: file.name, mimeType, data, source: "host" }).fileRef;
1057
+ },
1058
+ getFile(fileRef) {
1059
+ prune();
1060
+ const file = files.get(fileRef);
1061
+ return file && file.source === "app" ? file : null;
1062
+ },
1063
+ files() {
1064
+ prune();
1065
+ return [...files.values()].sort((a, b) => b.createdAt - a.createdAt);
1066
+ }
1067
+ };
1068
+ return { controller, dispatch, handleResult, reset };
1069
+ }
1070
+
831
1071
  // src/testing/memoryAttachments.ts
832
1072
  function concat(chunks) {
833
1073
  const bytes = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.length, 0));
@@ -1020,6 +1260,10 @@ function createBrowserTestHost(options) {
1020
1260
  const changed = () => queueMicrotask(() => onChange?.());
1021
1261
  const host = createFakeBridgeHost({
1022
1262
  ...hostOptions,
1263
+ onAgentChange() {
1264
+ hostOptions.onAgentChange?.();
1265
+ changed();
1266
+ },
1023
1267
  onRequest(request) {
1024
1268
  log.requests.push(request);
1025
1269
  const params = request.params ?? {};
@@ -1110,6 +1354,7 @@ function createBrowserTestHost(options) {
1110
1354
  return {
1111
1355
  host,
1112
1356
  log,
1357
+ agent: host.agent,
1113
1358
  setNextScan(scan) {
1114
1359
  nextScan = scan;
1115
1360
  },
@@ -1200,6 +1445,10 @@ var STYLE = `
1200
1445
  .htest__meta{margin-left:auto;color:#5b6478;font-size:11px;white-space:nowrap}
1201
1446
  .htest__error{color:#b42318}
1202
1447
  .htest pre{margin:4px 0 2px;padding:6px 8px;background:#fff;border:1px solid #e4e8f1;border-radius:6px;font:11px/1.4 ui-monospace,monospace;white-space:pre-wrap;word-break:break-all;max-height:260px;overflow:auto}
1448
+ .htest textarea{width:100%;min-height:70px;font:11px/1.4 ui-monospace,monospace;border:1px solid #c9d0e0;border-radius:6px;padding:6px}
1449
+ .htest__tag{display:inline-block;padding:0 6px;border-radius:999px;background:#e4e8f1;color:#334155;font-size:10px;margin-left:4px}
1450
+ .htest__tag--warn{background:#fee4e2;color:#912018}
1451
+ .htest__shot{max-width:100%;border:1px solid #e4e8f1;border-radius:6px;margin-top:4px;background:#fff}
1203
1452
  @media (max-width:720px){.htest{grid-template-columns:1fr;grid-template-rows:65vh auto}.htest__panel{border-left:0;border-top:1px solid #d7dce8}}
1204
1453
  `;
1205
1454
  function previewJson(value, maxString = 400, maxItems = 30, maxDepth = 6) {
@@ -1430,14 +1679,24 @@ function mountHavenTestHost(options) {
1430
1679
  lists.scans.replaceChildren(...log.scans.map(scanEntry));
1431
1680
  lists.requests.replaceChildren(...log.requests.slice(-50).reverse().map(requestEntry));
1432
1681
  }
1682
+ let agentReady = false;
1433
1683
  const testHost = createBrowserTestHost({
1684
+ // Haven's consent dialog, as a browser confirm (scripts can switch it with agent.setConsent)
1685
+ agentConsent: ({ tool, input }) => window.confirm(`An agent wants to run "${tool}" (marked consequential).
1686
+
1687
+ ${previewJson(input, 200, 10, 3)}
1688
+
1689
+ Allow?`),
1434
1690
  ...hostOptions,
1435
1691
  databases,
1436
1692
  launchContext,
1437
1693
  enforceCapabilities: url.enforceCapabilities ?? hostOptions.enforceCapabilities,
1438
1694
  directoryUsers: url.directoryUserCount !== void 0 ? generateDirectoryUsers(url.directoryUserCount) : hostOptions.directoryUsers,
1439
1695
  frame,
1440
- onChange: render,
1696
+ onChange: () => {
1697
+ render();
1698
+ if (agentReady) renderAgent();
1699
+ },
1441
1700
  onEmbedChange(embeds) {
1442
1701
  hostOptions.onEmbedChange?.(embeds);
1443
1702
  renderEmbeds(embeds);
@@ -1574,11 +1833,213 @@ function mountHavenTestHost(options) {
1574
1833
  scanInput,
1575
1834
  hint("What attachments.scan() returns; the file is attached to the document like in Haven.")
1576
1835
  );
1836
+ const agentState = document.createElement("div");
1837
+ agentState.className = "htest__muted";
1838
+ const agentEnabled = document.createElement("label");
1839
+ agentEnabled.className = "htest__check";
1840
+ const agentEnabledBox = document.createElement("input");
1841
+ agentEnabledBox.type = "checkbox";
1842
+ agentEnabledBox.dataset.testid = "haven-test-agent-enabled";
1843
+ agentEnabledBox.checked = testHost.agent.isEnabled();
1844
+ agentEnabledBox.addEventListener("change", () => testHost.agent.setEnabled(agentEnabledBox.checked));
1845
+ agentEnabled.append(agentEnabledBox, "Agent access on (registerTools answers enabled)");
1846
+ const agentTools = document.createElement("ul");
1847
+ const toolSelect = document.createElement("select");
1848
+ toolSelect.dataset.testid = "haven-test-agent-tool";
1849
+ toolSelect.setAttribute("aria-label", "Agent tool");
1850
+ const toolInput = document.createElement("textarea");
1851
+ toolInput.dataset.testid = "haven-test-agent-input";
1852
+ toolInput.setAttribute("aria-label", "Tool input (JSON)");
1853
+ toolInput.value = "{}";
1854
+ const toolResult = document.createElement("div");
1855
+ toolResult.dataset.testid = "haven-test-agent-result";
1856
+ const callButton = button("Call tool", () => void runTool());
1857
+ callButton.dataset.testid = "haven-test-agent-call";
1858
+ const agentContext = document.createElement("pre");
1859
+ agentContext.dataset.testid = "haven-test-agent-context";
1860
+ const agentFiles = document.createElement("ul");
1861
+ const agentCalls = document.createElement("ul");
1862
+ const importInput = document.createElement("input");
1863
+ importInput.type = "file";
1864
+ importInput.multiple = true;
1865
+ importInput.dataset.testid = "haven-test-agent-import";
1866
+ importInput.setAttribute("aria-label", "Import files for the app");
1867
+ importInput.addEventListener("change", () => {
1868
+ void (async () => {
1869
+ for (const file of importInput.files ?? []) {
1870
+ await testHost.agent.importFile({ name: file.name, mimeType: file.type, data: file });
1871
+ }
1872
+ importInput.value = "";
1873
+ })();
1874
+ });
1875
+ function showOutcome(target, call) {
1876
+ const pre = document.createElement("pre");
1877
+ const outcome = call.outcome;
1878
+ if (!outcome) {
1879
+ pre.textContent = "running\u2026";
1880
+ target.replaceChildren(pre);
1881
+ return;
1882
+ }
1883
+ if (outcome.ok) {
1884
+ pre.textContent = previewJson(outcome.result, 2e3, 60, 8);
1885
+ } else {
1886
+ pre.className = "htest__error";
1887
+ pre.textContent = `${outcome.error.code}: ${outcome.error.message}${outcome.error.requiredAction ? `
1888
+ \u2192 ${outcome.error.requiredAction}` : ""}`;
1889
+ }
1890
+ const images = [];
1891
+ const collect = (value, depth) => {
1892
+ if (depth > 4 || images.length >= 4) return;
1893
+ if (typeof value === "string" && /^data:image\/(png|jpeg|gif|webp|svg\+xml);base64,/.test(value)) images.push(value);
1894
+ else if (value && typeof value === "object") for (const child of Object.values(value)) collect(child, depth + 1);
1895
+ };
1896
+ if (outcome.ok) collect(outcome.result, 0);
1897
+ target.replaceChildren(
1898
+ pre,
1899
+ ...images.map((src) => {
1900
+ const img = document.createElement("img");
1901
+ img.className = "htest__shot";
1902
+ img.src = src;
1903
+ img.alt = "image in the tool result";
1904
+ return img;
1905
+ })
1906
+ );
1907
+ }
1908
+ async function runTool() {
1909
+ let input;
1910
+ try {
1911
+ const parsed = JSON.parse(toolInput.value || "{}");
1912
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("The input must be a JSON object.");
1913
+ input = parsed;
1914
+ } catch (error) {
1915
+ const pre = document.createElement("pre");
1916
+ pre.className = "htest__error";
1917
+ pre.textContent = error instanceof Error ? error.message : String(error);
1918
+ toolResult.replaceChildren(pre);
1919
+ return;
1920
+ }
1921
+ callButton.disabled = true;
1922
+ try {
1923
+ await testHost.agent.call(toolSelect.value, input);
1924
+ const last = testHost.agent.calls().at(-1);
1925
+ if (last) showOutcome(toolResult, last);
1926
+ } finally {
1927
+ callButton.disabled = false;
1928
+ }
1929
+ }
1930
+ function download(name, mimeType, data) {
1931
+ const link = document.createElement("a");
1932
+ link.textContent = name;
1933
+ link.download = name;
1934
+ link.href = URL.createObjectURL(new Blob([data.slice().buffer], { type: mimeType }));
1935
+ return link;
1936
+ }
1937
+ const openCalls = /* @__PURE__ */ new Set();
1938
+ function renderAgent() {
1939
+ const agent = testHost.agent;
1940
+ const tools = agent.tools();
1941
+ agentState.textContent = `prefix "${agent.prefix}" \xB7 ${tools.length} tool(s) \xB7 ${agent.isEnabled() ? "access on" : "access off"}`;
1942
+ agentEnabledBox.checked = agent.isEnabled();
1943
+ agentTools.replaceChildren(
1944
+ ...tools.map((tool) => {
1945
+ const li = document.createElement("li");
1946
+ const details = document.createElement("details");
1947
+ const summary = document.createElement("summary");
1948
+ const name = document.createElement("span");
1949
+ name.textContent = `${agent.prefix}_${tool.name}`;
1950
+ summary.append(name);
1951
+ const hints = tool.annotations ?? {};
1952
+ if (hints.readOnlyHint) summary.append(Object.assign(document.createElement("span"), { className: "htest__tag", textContent: "read-only" }));
1953
+ if (hints.consequentialHint) summary.append(Object.assign(document.createElement("span"), { className: "htest__tag htest__tag--warn", textContent: "asks first" }));
1954
+ const description = document.createElement("p");
1955
+ description.textContent = tool.description;
1956
+ const schema = document.createElement("pre");
1957
+ schema.textContent = previewJson(tool.inputSchema, 400, 60, 10);
1958
+ details.append(summary, description, schema);
1959
+ li.append(details);
1960
+ return li;
1961
+ })
1962
+ );
1963
+ const selected = toolSelect.value;
1964
+ toolSelect.replaceChildren(...tools.map((tool) => new Option(`${agent.prefix}_${tool.name}`, tool.name)));
1965
+ if (tools.some((tool) => tool.name === selected)) toolSelect.value = selected;
1966
+ callButton.disabled = tools.length === 0;
1967
+ agentContext.textContent = previewJson(agent.context(), 400, 30, 6);
1968
+ agentFiles.replaceChildren(
1969
+ ...agent.files().map((file) => {
1970
+ const li = document.createElement("li");
1971
+ const ref = document.createElement("code");
1972
+ ref.textContent = file.fileRef;
1973
+ const copy = button("copy ref", () => void navigator.clipboard?.writeText(file.fileRef));
1974
+ li.append(
1975
+ file.source === "app" ? "from app: " : "for app: ",
1976
+ file.source === "app" ? download(file.name, file.mimeType, file.data) : file.name,
1977
+ ` (${file.data.length} bytes) `,
1978
+ ref,
1979
+ " ",
1980
+ copy
1981
+ );
1982
+ return li;
1983
+ })
1984
+ );
1985
+ agentCalls.replaceChildren(
1986
+ ...agent.calls().slice(-20).reverse().map((call) => {
1987
+ const li = document.createElement("li");
1988
+ const details = document.createElement("details");
1989
+ const summary = document.createElement("summary");
1990
+ const label = document.createElement("span");
1991
+ label.textContent = call.tool;
1992
+ if (call.outcome && !call.outcome.ok) label.className = "htest__error";
1993
+ const meta = document.createElement("span");
1994
+ meta.className = "htest__meta";
1995
+ meta.textContent = call.outcome ? `${call.outcome.durationMs} ms` : "\u2026";
1996
+ summary.append(label, meta);
1997
+ const body = document.createElement("div");
1998
+ const input = document.createElement("pre");
1999
+ input.textContent = previewJson(call.input);
2000
+ details.append(summary, body);
2001
+ details.addEventListener("toggle", () => {
2002
+ if (details.open) {
2003
+ openCalls.add(call.callId);
2004
+ const out = document.createElement("div");
2005
+ showOutcome(out, call);
2006
+ body.replaceChildren(input, out);
2007
+ } else openCalls.delete(call.callId);
2008
+ });
2009
+ if (openCalls.has(call.callId)) details.open = true;
2010
+ li.append(details);
2011
+ return li;
2012
+ })
2013
+ );
2014
+ }
2015
+ const callRow = document.createElement("div");
2016
+ callRow.className = "htest__row";
2017
+ callRow.append(toolSelect, callButton);
2018
+ section(
2019
+ "Agent tools",
2020
+ agentState,
2021
+ agentEnabled,
2022
+ agentTools,
2023
+ callRow,
2024
+ toolInput,
2025
+ toolResult,
2026
+ hint("Calls a tool as an agent would; tools marked \u201Casks first\u201D go through a confirm, like Haven's consent dialog.")
2027
+ );
2028
+ section(
2029
+ "Agent files",
2030
+ importInput,
2031
+ hint("Import puts a file in the exchange for the app (takeFile); pass its fileRef to a tool. Files the app hands over (provideFile) can be downloaded."),
2032
+ agentFiles
2033
+ );
2034
+ section("Agent context", agentContext);
2035
+ section("Agent calls (latest first)", agentCalls);
1577
2036
  section("Notifications", lists.notifications);
1578
2037
  section("Previews", lists.previews);
1579
2038
  section("Scans", lists.scans);
1580
2039
  section("Requests (latest first, click for details)", lists.requests);
1581
2040
  render();
2041
+ agentReady = true;
2042
+ renderAgent();
1582
2043
  syncUrl();
1583
2044
  window.__havenTestHost = testHost;
1584
2045
  return testHost;
@@ -2373,9 +2834,9 @@ function createLiveMockViewNavigator(initial, rebuild, subscribe) {
2373
2834
  listeners.clear();
2374
2835
  if (pendingNotify) clearTimeout(pendingNotify);
2375
2836
  }
2376
- const navigator = await current();
2377
- const member = navigator[property];
2378
- return typeof member === "function" ? member.apply(navigator, args) : member;
2837
+ const navigator2 = await current();
2838
+ const member = navigator2[property];
2839
+ return typeof member === "function" ? member.apply(navigator2, args) : member;
2379
2840
  };
2380
2841
  }
2381
2842
  });
@@ -4158,6 +4619,20 @@ function createFakeBridgeHost(options = {}) {
4158
4619
  );
4159
4620
  const requests = [];
4160
4621
  const connectedPorts = /* @__PURE__ */ new Set();
4622
+ const agentHost = createMockAgentHost({
4623
+ ...options.agentToolPrefix !== void 0 ? { agentToolPrefix: options.agentToolPrefix } : {},
4624
+ ...options.agentToolsEnabled !== void 0 ? { agentToolsEnabled: options.agentToolsEnabled } : {},
4625
+ ...options.agentConsent !== void 0 ? { agentConsent: options.agentConsent } : {},
4626
+ ...options.agentInvokeTimeoutMs !== void 0 ? { agentInvokeTimeoutMs: options.agentInvokeTimeoutMs } : {},
4627
+ appId: () => state.getLaunchContext().appId,
4628
+ post(message) {
4629
+ const port = [...connectedPorts].pop();
4630
+ if (!port) return false;
4631
+ port.postMessage(message);
4632
+ return true;
4633
+ },
4634
+ onChange: () => options.onAgentChange?.()
4635
+ });
4161
4636
  state.embedHost.onClosed((event) => {
4162
4637
  connectedPorts.forEach(
4163
4638
  (port) => port.postMessage({ protocol: PROTOCOL, kind: "embed-event", event: { type: "closed", ...event } })
@@ -4199,6 +4674,7 @@ function createFakeBridgeHost(options = {}) {
4199
4674
  "Expected the bridge connection to transfer a MessagePort."
4200
4675
  );
4201
4676
  }
4677
+ if (connectedPorts.size > 0) agentHost.reset("The app was reloaded.");
4202
4678
  connectedPorts.add(port);
4203
4679
  port.addEventListener("message", (event) => {
4204
4680
  void handlePortMessage(port, event.data);
@@ -4289,9 +4765,9 @@ function createFakeBridgeHost(options = {}) {
4289
4765
  if (existing) {
4290
4766
  return existing;
4291
4767
  }
4292
- const navigator = await state.openViewNavigator(viewId);
4293
- viewSessions.set(viewId, navigator);
4294
- return navigator;
4768
+ const navigator2 = await state.openViewNavigator(viewId);
4769
+ viewSessions.set(viewId, navigator2);
4770
+ return navigator2;
4295
4771
  }
4296
4772
  function postRpcSuccess(port, requestId, result) {
4297
4773
  port.postMessage({
@@ -4340,14 +4816,14 @@ function createFakeBridgeHost(options = {}) {
4340
4816
  return { ok: true };
4341
4817
  case "session.createViewNavigator": {
4342
4818
  const navigatorId = `navigator-${viewCounter += 1}`;
4343
- const navigator = await state.createViewNavigator({
4819
+ const navigator2 = await state.createViewNavigator({
4344
4820
  databaseIds: Array.isArray(params.databaseIds) ? params.databaseIds.map((entry) => String(entry)) : [],
4345
4821
  definition: params.definition,
4346
4822
  categorizationStyle: params.categorizationStyle === "category_then_document" ? "category_then_document" : "document_then_category",
4347
4823
  options: params.options
4348
4824
  });
4349
- viewSessions.set(navigatorId, navigator);
4350
- navigator.onDidUpdate((stats) => {
4825
+ viewSessions.set(navigatorId, navigator2);
4826
+ navigator2.onDidUpdate((stats) => {
4351
4827
  if (!viewSessions.has(navigatorId)) return;
4352
4828
  const payload = {
4353
4829
  protocol: PROTOCOL,
@@ -4361,11 +4837,11 @@ function createFakeBridgeHost(options = {}) {
4361
4837
  }
4362
4838
  case "session.openViewNavigator": {
4363
4839
  const navigatorId = `navigator-${viewCounter += 1}`;
4364
- const navigator = await state.openViewNavigator(
4840
+ const navigator2 = await state.openViewNavigator(
4365
4841
  String(params.viewId),
4366
4842
  params.options
4367
4843
  );
4368
- viewSessions.set(navigatorId, navigator);
4844
+ viewSessions.set(navigatorId, navigator2);
4369
4845
  return { navigatorId };
4370
4846
  }
4371
4847
  case "menus.show":
@@ -4858,8 +5334,8 @@ function createFakeBridgeHost(options = {}) {
4858
5334
  );
4859
5335
  case "viewNavigators.dispose": {
4860
5336
  const key = String(params.navigatorId);
4861
- const navigator = await resolveViewNavigator(key);
4862
- await navigator.dispose();
5337
+ const navigator2 = await resolveViewNavigator(key);
5338
+ await navigator2.dispose();
4863
5339
  viewSessions.delete(key);
4864
5340
  return void 0;
4865
5341
  }
@@ -4944,6 +5420,10 @@ function createFakeBridgeHost(options = {}) {
4944
5420
  beforeCloseWaiters.get(message.closeId)?.();
4945
5421
  return;
4946
5422
  }
5423
+ if (message.kind === "agent-result") {
5424
+ agentHost.handleResult(message);
5425
+ return;
5426
+ }
4947
5427
  if (message.kind === "workspace-focus-requested") {
4948
5428
  state.setHostFocused(true);
4949
5429
  const payload = {
@@ -4985,6 +5465,11 @@ function createFakeBridgeHost(options = {}) {
4985
5465
  succeed(result);
4986
5466
  return;
4987
5467
  }
5468
+ const agentResult = await agentHost.dispatch(message.method, message.params);
5469
+ if (agentResult) {
5470
+ succeed(agentResult.result);
5471
+ return;
5472
+ }
4988
5473
  const builtinResult = await handleBuiltinRequest(message);
4989
5474
  if (builtinResult === NOT_HANDLED) {
4990
5475
  fail({
@@ -5026,6 +5511,7 @@ function createFakeBridgeHost(options = {}) {
5026
5511
  const controller = {
5027
5512
  bridge: state.bridge,
5028
5513
  session: state.session,
5514
+ agent: agentHost.controller,
5029
5515
  get launchId() {
5030
5516
  return state.getLaunchContext().launchId;
5031
5517
  },
@@ -5183,12 +5669,15 @@ export {
5183
5669
  createEvaluatingViewNavigator,
5184
5670
  createFakeBridgeHost,
5185
5671
  createMemoryAttachments,
5672
+ createMockAgentHost,
5186
5673
  createMockMindooDBAppBridge,
5187
5674
  createMockMindooDBAppSession,
5188
5675
  generateDirectoryUsers,
5189
5676
  loadMindoodbForTesting,
5677
+ mockAgentToolPrefix,
5190
5678
  mockDatabasesFromDefinition,
5191
5679
  mountHavenTestHost,
5192
5680
  previewJson,
5193
- readTestHostUrlSettings
5681
+ readTestHostUrlSettings,
5682
+ validateMockAgentTools
5194
5683
  };
@@ -1671,7 +1671,11 @@ interface MindooDBAppEmbed {
1671
1671
  * `container`, whose rectangle the SDK follows itself.
1672
1672
  */
1673
1673
  setRect(rect: MindooDBAppEmbedRect): Promise<void>;
1674
- /** Hide without closing, e.g. while a dialog of the host covers it. */
1674
+ /**
1675
+ * Hide without closing, e.g. while a dialog of the host covers it, or (also with
1676
+ * `frame` placement) while it sits in a background tab: a hidden component gets
1677
+ * no drops of host-owned drags.
1678
+ */
1675
1679
  setVisible(visible: boolean): Promise<void>;
1676
1680
  /**
1677
1681
  * Whether the component has changes it has not saved yet, as it last reported
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindoodb-app-sdk",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "engines": {
5
5
  "node": ">=22.13.0"
6
6
  },