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 +16 -0
- package/dist/{chunk-QQRLEHFE.js → chunk-BE7LLFFE.js} +0 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/testing/index.d.ts +128 -3
- package/dist/testing/index.js +583 -94
- package/dist/{types-ClOh6z0L.d.ts → types-BeRUxGKN.d.ts} +5 -1
- package/package.json +1 -1
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-
|
|
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-
|
|
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-
|
|
50
|
+
} from "./chunk-BE7LLFFE.js";
|
|
51
51
|
import {
|
|
52
52
|
MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
|
|
53
53
|
MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -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-
|
|
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 };
|
package/dist/testing/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
mindooDBAppComponentFieldsMatch,
|
|
5
5
|
pageUsers,
|
|
6
6
|
releaseMindooDBAppBridgeSessions
|
|
7
|
-
} from "../chunk-
|
|
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(
|
|
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() ?
|
|
126
|
-
selected:
|
|
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(
|
|
211
|
+
function pageEntries(navigator2, direction, options = {}) {
|
|
212
212
|
const entries = [];
|
|
213
|
-
const startOk = options.startPosition ?
|
|
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 ?
|
|
219
|
+
return options.selectedOnly ? navigator2.gotoNextSelected() : navigator2.gotoNext();
|
|
220
220
|
}
|
|
221
|
-
return options.selectedOnly ?
|
|
221
|
+
return options.selectedOnly ? navigator2.gotoPrevSelected() : navigator2.gotoPrev();
|
|
222
222
|
};
|
|
223
223
|
if (options.selectedOnly) {
|
|
224
|
-
const current =
|
|
225
|
-
if (!current || !
|
|
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 =
|
|
233
|
+
const current = navigator2.getCurrentEntry();
|
|
234
234
|
if (!current) break;
|
|
235
|
-
entries.push(serializeEntry(
|
|
235
|
+
entries.push(serializeEntry(navigator2, current));
|
|
236
236
|
if (entries.length >= limit) {
|
|
237
237
|
hasMore = moveNext();
|
|
238
|
-
nextPosition = hasMore ?
|
|
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(
|
|
246
|
-
const entry =
|
|
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,
|
|
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 =
|
|
271
|
-
return entry ? serializeEntry(
|
|
270
|
+
const entry = navigator2.getCurrentEntry();
|
|
271
|
+
return entry ? serializeEntry(navigator2, entry) : null;
|
|
272
272
|
},
|
|
273
273
|
async gotoFirst() {
|
|
274
|
-
return
|
|
274
|
+
return navigator2.gotoFirst();
|
|
275
275
|
},
|
|
276
276
|
async gotoLast() {
|
|
277
|
-
return
|
|
277
|
+
return navigator2.gotoLast();
|
|
278
278
|
},
|
|
279
279
|
async gotoNext() {
|
|
280
|
-
return
|
|
280
|
+
return navigator2.gotoNext();
|
|
281
281
|
},
|
|
282
282
|
async gotoPrev() {
|
|
283
|
-
return
|
|
283
|
+
return navigator2.gotoPrev();
|
|
284
284
|
},
|
|
285
285
|
async gotoNextSibling() {
|
|
286
|
-
return
|
|
286
|
+
return navigator2.gotoNextSibling();
|
|
287
287
|
},
|
|
288
288
|
async gotoPrevSibling() {
|
|
289
|
-
return
|
|
289
|
+
return navigator2.gotoPrevSibling();
|
|
290
290
|
},
|
|
291
291
|
async gotoParent() {
|
|
292
|
-
return
|
|
292
|
+
return navigator2.gotoParent();
|
|
293
293
|
},
|
|
294
294
|
async gotoFirstChild() {
|
|
295
|
-
return
|
|
295
|
+
return navigator2.gotoFirstChild();
|
|
296
296
|
},
|
|
297
297
|
async gotoLastChild() {
|
|
298
|
-
return
|
|
298
|
+
return navigator2.gotoLastChild();
|
|
299
299
|
},
|
|
300
300
|
async gotoPos(position) {
|
|
301
|
-
return
|
|
301
|
+
return navigator2.gotoPos(position);
|
|
302
302
|
},
|
|
303
303
|
async getPos(position) {
|
|
304
|
-
const entry =
|
|
305
|
-
return entry ? serializeEntry(
|
|
304
|
+
const entry = navigator2.getPos(position);
|
|
305
|
+
return entry ? serializeEntry(navigator2, entry) : null;
|
|
306
306
|
},
|
|
307
307
|
async findCategoryEntryByParts(parts) {
|
|
308
|
-
const entry =
|
|
309
|
-
return entry ? serializeEntry(
|
|
308
|
+
const entry = navigator2.findCategoryEntryByParts(parts);
|
|
309
|
+
return entry ? serializeEntry(navigator2, entry) : null;
|
|
310
310
|
},
|
|
311
311
|
async entriesForward(options) {
|
|
312
|
-
return pageEntries(
|
|
312
|
+
return pageEntries(navigator2, "forward", options);
|
|
313
313
|
},
|
|
314
314
|
async entriesBackward(options) {
|
|
315
|
-
return pageEntries(
|
|
315
|
+
return pageEntries(navigator2, "backward", options);
|
|
316
316
|
},
|
|
317
317
|
async gotoNextSelected() {
|
|
318
|
-
return
|
|
318
|
+
return navigator2.gotoNextSelected();
|
|
319
319
|
},
|
|
320
320
|
async gotoPrevSelected() {
|
|
321
|
-
return
|
|
321
|
+
return navigator2.gotoPrevSelected();
|
|
322
322
|
},
|
|
323
323
|
async select(origin, docId, selectParentCategories = false) {
|
|
324
|
-
|
|
324
|
+
navigator2.select(origin, docId, selectParentCategories);
|
|
325
325
|
},
|
|
326
326
|
async deselect(origin, docId) {
|
|
327
|
-
|
|
327
|
+
navigator2.deselect(origin, docId);
|
|
328
328
|
},
|
|
329
329
|
async selectAllEntries() {
|
|
330
|
-
|
|
330
|
+
navigator2.selectAllEntries();
|
|
331
331
|
},
|
|
332
332
|
async deselectAllEntries() {
|
|
333
|
-
|
|
333
|
+
navigator2.deselectAllEntries();
|
|
334
334
|
},
|
|
335
335
|
async isSelected(origin, docId) {
|
|
336
|
-
return
|
|
336
|
+
return navigator2.isSelected(origin, docId);
|
|
337
337
|
},
|
|
338
338
|
async getSelectionState() {
|
|
339
339
|
return {
|
|
340
|
-
selectAllByDefault:
|
|
341
|
-
entryKeys: Array.from(
|
|
340
|
+
selectAllByDefault: navigator2.isSelectAllByDefault(),
|
|
341
|
+
entryKeys: Array.from(navigator2.getSelectedOrDeselectedEntries())
|
|
342
342
|
};
|
|
343
343
|
},
|
|
344
344
|
async setSelectionState(state) {
|
|
345
|
-
if (state.selectAllByDefault)
|
|
346
|
-
else
|
|
347
|
-
|
|
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
|
-
|
|
350
|
+
navigator2.expand(origin, docId);
|
|
351
351
|
},
|
|
352
352
|
async collapse(origin, docId) {
|
|
353
|
-
|
|
353
|
+
navigator2.collapse(origin, docId);
|
|
354
354
|
},
|
|
355
355
|
async expandAll() {
|
|
356
|
-
|
|
356
|
+
navigator2.expandAll();
|
|
357
357
|
},
|
|
358
358
|
async collapseAll() {
|
|
359
|
-
|
|
359
|
+
navigator2.collapseAll();
|
|
360
360
|
},
|
|
361
361
|
async expandToLevel(level) {
|
|
362
|
-
|
|
362
|
+
navigator2.expandToLevel(level);
|
|
363
363
|
},
|
|
364
364
|
async isExpanded(entryKey) {
|
|
365
|
-
const entry =
|
|
366
|
-
return entry ?
|
|
365
|
+
const entry = navigator2.getPos(entryKey);
|
|
366
|
+
return entry ? navigator2.isExpanded(entry) : false;
|
|
367
367
|
},
|
|
368
368
|
async getExpansionState() {
|
|
369
369
|
return {
|
|
370
|
-
expandAllByDefault:
|
|
371
|
-
expandLevel:
|
|
372
|
-
entryKeys: Array.from(
|
|
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)
|
|
377
|
-
else
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
382
|
+
return navigator2.childEntries(resolveEntry(navigator2, entryKey), descending).map((entry) => serializeEntry(navigator2, entry));
|
|
383
383
|
},
|
|
384
384
|
async childCategories(entryKey, descending) {
|
|
385
|
-
return
|
|
385
|
+
return navigator2.childCategories(resolveEntry(navigator2, entryKey), descending).map((entry) => serializeEntry(navigator2, entry));
|
|
386
386
|
},
|
|
387
387
|
async childDocuments(entryKey, descending) {
|
|
388
|
-
return
|
|
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
|
|
392
|
-
resolveEntry(
|
|
391
|
+
return navigator2.childCategoriesByKey(
|
|
392
|
+
resolveEntry(navigator2, entryKey),
|
|
393
393
|
String(key ?? ""),
|
|
394
394
|
Boolean(exact),
|
|
395
395
|
descending
|
|
396
|
-
).map((entry) => serializeEntry(
|
|
396
|
+
).map((entry) => serializeEntry(navigator2, entry));
|
|
397
397
|
},
|
|
398
398
|
async childDocumentsByKey(entryKey, key, exact, descending) {
|
|
399
|
-
return
|
|
400
|
-
resolveEntry(
|
|
399
|
+
return navigator2.childDocumentsByKey(
|
|
400
|
+
resolveEntry(navigator2, entryKey),
|
|
401
401
|
String(key ?? ""),
|
|
402
402
|
Boolean(exact),
|
|
403
403
|
descending
|
|
404
|
-
).map((entry) => serializeEntry(
|
|
404
|
+
).map((entry) => serializeEntry(navigator2, entry));
|
|
405
405
|
},
|
|
406
406
|
async childCategoriesBetween(entryKey, range) {
|
|
407
|
-
return
|
|
408
|
-
resolveEntry(
|
|
407
|
+
return navigator2.childCategoriesBetween(
|
|
408
|
+
resolveEntry(navigator2, entryKey),
|
|
409
409
|
range.startKey,
|
|
410
410
|
range.endKey,
|
|
411
411
|
range.descending
|
|
412
|
-
).map((entry) => serializeEntry(
|
|
412
|
+
).map((entry) => serializeEntry(navigator2, entry));
|
|
413
413
|
},
|
|
414
414
|
async childDocumentsBetween(entryKey, range) {
|
|
415
|
-
return
|
|
416
|
-
resolveEntry(
|
|
415
|
+
return navigator2.childDocumentsBetween(
|
|
416
|
+
resolveEntry(navigator2, entryKey),
|
|
417
417
|
range.startKey,
|
|
418
418
|
range.endKey,
|
|
419
419
|
range.descending
|
|
420
|
-
).map((entry) => serializeEntry(
|
|
420
|
+
).map((entry) => serializeEntry(navigator2, entry));
|
|
421
421
|
},
|
|
422
422
|
async getSortedDocIds(descending) {
|
|
423
|
-
const page = pageEntries(
|
|
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 =
|
|
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
|
|
457
|
-
return wrapNavigator(input.definition,
|
|
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:
|
|
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
|
|
2377
|
-
const member =
|
|
2378
|
-
return typeof member === "function" ? member.apply(
|
|
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
|
|
4293
|
-
viewSessions.set(viewId,
|
|
4294
|
-
return
|
|
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
|
|
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,
|
|
4350
|
-
|
|
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
|
|
4840
|
+
const navigator2 = await state.openViewNavigator(
|
|
4365
4841
|
String(params.viewId),
|
|
4366
4842
|
params.options
|
|
4367
4843
|
);
|
|
4368
|
-
viewSessions.set(navigatorId,
|
|
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
|
|
4862
|
-
await
|
|
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
|
-
/**
|
|
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
|