@tambo-ai/react 0.67.1 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -5
- package/dist/context-helpers/context-helpers.test.js +16 -4
- package/dist/context-helpers/context-helpers.test.js.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.d.ts +2 -2
- package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.js +31 -12
- package/dist/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/dist/context-helpers/registry.d.ts +2 -2
- package/dist/context-helpers/registry.d.ts.map +1 -1
- package/dist/context-helpers/registry.js.map +1 -1
- package/dist/context-helpers/types.d.ts +2 -2
- package/dist/context-helpers/types.d.ts.map +1 -1
- package/dist/context-helpers/types.js.map +1 -1
- package/dist/hoc/with-tambo-interactable.d.ts +50 -4
- package/dist/hoc/with-tambo-interactable.d.ts.map +1 -1
- package/dist/hoc/with-tambo-interactable.js +20 -5
- package/dist/hoc/with-tambo-interactable.js.map +1 -1
- package/dist/hooks/use-component-state.d.ts +3 -8
- package/dist/hooks/use-component-state.d.ts.map +1 -1
- package/dist/hooks/use-component-state.js +8 -0
- package/dist/hooks/use-component-state.js.map +1 -1
- package/dist/hooks/use-component-state.test.js +37 -0
- package/dist/hooks/use-component-state.test.js.map +1 -1
- package/dist/hooks/use-message-images.test.js +174 -37
- package/dist/hooks/use-message-images.test.js.map +1 -1
- package/dist/hooks/use-tambo-threads.js +1 -1
- package/dist/hooks/use-tambo-threads.js.map +1 -1
- package/dist/hooks/use-tambo-voice.d.ts +1 -1
- package/dist/hooks/use-tambo-voice.js +1 -1
- package/dist/hooks/use-tambo-voice.js.map +1 -1
- package/dist/hooks/use-tambo-voice.test.d.ts +2 -0
- package/dist/hooks/use-tambo-voice.test.d.ts.map +1 -0
- package/dist/hooks/use-tambo-voice.test.js +239 -0
- package/dist/hooks/use-tambo-voice.test.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/elicitation.d.ts.map +1 -1
- package/dist/mcp/elicitation.js +12 -0
- package/dist/mcp/elicitation.js.map +1 -1
- package/dist/mcp/elicitation.test.js +8 -1
- package/dist/mcp/elicitation.test.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +6 -10
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/mcp-constants.d.ts +19 -0
- package/dist/mcp/mcp-constants.d.ts.map +1 -0
- package/dist/mcp/mcp-constants.js +21 -0
- package/dist/mcp/mcp-constants.js.map +1 -0
- package/dist/mcp/mcp-hooks.d.ts +21 -40
- package/dist/mcp/mcp-hooks.d.ts.map +1 -1
- package/dist/mcp/mcp-hooks.js +130 -39
- package/dist/mcp/mcp-hooks.js.map +1 -1
- package/dist/mcp/mcp-hooks.test.js +431 -5
- package/dist/mcp/mcp-hooks.test.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.d.ts +7 -0
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +205 -155
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.test.js +37 -0
- package/dist/mcp/tambo-mcp-provider.test.js.map +1 -1
- package/dist/model/component-metadata.d.ts +54 -21
- package/dist/model/component-metadata.d.ts.map +1 -1
- package/dist/model/component-metadata.js.map +1 -1
- package/dist/model/tambo-interactable.d.ts +13 -5
- package/dist/model/tambo-interactable.d.ts.map +1 -1
- package/dist/model/tambo-interactable.js.map +1 -1
- package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
- package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
- package/dist/providers/__tests__/thread-input-resource-resolution.test.js +592 -0
- package/dist/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/tambo-client-provider.d.ts +8 -0
- package/dist/providers/tambo-client-provider.d.ts.map +1 -1
- package/dist/providers/tambo-client-provider.js +10 -11
- package/dist/providers/tambo-client-provider.js.map +1 -1
- package/dist/providers/tambo-client-provider.test.d.ts +2 -0
- package/dist/providers/tambo-client-provider.test.d.ts.map +1 -0
- package/dist/providers/tambo-client-provider.test.js +208 -0
- package/dist/providers/tambo-client-provider.test.js.map +1 -0
- package/dist/providers/tambo-context-attachment-provider.d.ts +34 -92
- package/dist/providers/tambo-context-attachment-provider.d.ts.map +1 -1
- package/dist/providers/tambo-context-attachment-provider.js +62 -105
- package/dist/providers/tambo-context-attachment-provider.js.map +1 -1
- package/dist/providers/tambo-context-attachment-provider.test.js +229 -463
- package/dist/providers/tambo-context-attachment-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
- package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts +5 -2
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +126 -17
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.test.js +242 -0
- package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactables-additional-context.test.js +2 -5
- package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/dist/providers/tambo-provider.d.ts +2 -3
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +6 -5
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-registry-provider.test.js +16 -0
- package/dist/providers/tambo-registry-provider.test.js.map +1 -1
- package/dist/providers/tambo-registry-schema-compat.test.js +31 -0
- package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-input-provider.js +26 -4
- package/dist/providers/tambo-thread-input-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider-initial-messages.test.js +84 -2
- package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +53 -42
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.test.js +368 -262
- package/dist/providers/tambo-thread-provider.test.js.map +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/json-schema.d.ts +7 -0
- package/dist/schema/json-schema.d.ts.map +1 -1
- package/dist/schema/json-schema.js +40 -29
- package/dist/schema/json-schema.js.map +1 -1
- package/dist/schema/json-schema.test.d.ts +2 -0
- package/dist/schema/json-schema.test.d.ts.map +1 -0
- package/dist/schema/json-schema.test.js +204 -0
- package/dist/schema/json-schema.test.js.map +1 -0
- package/dist/schema/schema.test.js +237 -0
- package/dist/schema/schema.test.js.map +1 -1
- package/dist/schema/standard-schema.d.ts +1 -0
- package/dist/schema/standard-schema.d.ts.map +1 -1
- package/dist/schema/standard-schema.js +18 -13
- package/dist/schema/standard-schema.js.map +1 -1
- package/dist/schema/standard-schema.test.d.ts +2 -0
- package/dist/schema/standard-schema.test.d.ts.map +1 -0
- package/dist/schema/standard-schema.test.js +165 -0
- package/dist/schema/standard-schema.test.js.map +1 -0
- package/dist/schema/validate.test.js +149 -0
- package/dist/schema/validate.test.js.map +1 -1
- package/dist/schema/zod.d.ts +7 -4
- package/dist/schema/zod.d.ts.map +1 -1
- package/dist/schema/zod.js +65 -22
- package/dist/schema/zod.js.map +1 -1
- package/dist/schema/zod.test.js +112 -0
- package/dist/schema/zod.test.js.map +1 -1
- package/dist/setupTests.js +3 -0
- package/dist/setupTests.js.map +1 -1
- package/dist/testing/tools.d.ts +4 -1
- package/dist/testing/tools.d.ts.map +1 -1
- package/dist/testing/tools.js +6 -1
- package/dist/testing/tools.js.map +1 -1
- package/dist/util/generate-component.d.ts.map +1 -1
- package/dist/util/generate-component.js +18 -3
- package/dist/util/generate-component.js.map +1 -1
- package/dist/util/generate-component.test.d.ts +2 -0
- package/dist/util/generate-component.test.d.ts.map +1 -0
- package/dist/util/generate-component.test.js +340 -0
- package/dist/util/generate-component.test.js.map +1 -0
- package/dist/util/is-promise.d.ts +9 -0
- package/dist/util/is-promise.d.ts.map +1 -0
- package/dist/util/is-promise.js +20 -0
- package/dist/util/is-promise.js.map +1 -0
- package/dist/util/is-promise.test.d.ts +2 -0
- package/dist/util/is-promise.test.d.ts.map +1 -0
- package/dist/util/is-promise.test.js +48 -0
- package/dist/util/is-promise.test.js.map +1 -0
- package/dist/util/message-builder.d.ts +3 -1
- package/dist/util/message-builder.d.ts.map +1 -1
- package/dist/util/message-builder.js +20 -3
- package/dist/util/message-builder.js.map +1 -1
- package/dist/util/message-builder.test.js +269 -0
- package/dist/util/message-builder.test.js.map +1 -1
- package/dist/util/query-utils.test.d.ts +2 -0
- package/dist/util/query-utils.test.d.ts.map +1 -0
- package/dist/util/query-utils.test.js +382 -0
- package/dist/util/query-utils.test.js.map +1 -0
- package/dist/util/registry-validators.d.ts.map +1 -1
- package/dist/util/registry-validators.js +7 -0
- package/dist/util/registry-validators.js.map +1 -1
- package/dist/util/registry-validators.test.js +57 -0
- package/dist/util/registry-validators.test.js.map +1 -1
- package/dist/util/registry.d.ts.map +1 -1
- package/dist/util/registry.js +9 -0
- package/dist/util/registry.js.map +1 -1
- package/dist/util/registry.test.js +323 -1
- package/dist/util/registry.test.js.map +1 -1
- package/dist/util/resource-content-resolver.d.ts +20 -0
- package/dist/util/resource-content-resolver.d.ts.map +1 -0
- package/dist/util/resource-content-resolver.js +93 -0
- package/dist/util/resource-content-resolver.js.map +1 -0
- package/dist/util/resource-content-resolver.test.d.ts +2 -0
- package/dist/util/resource-content-resolver.test.d.ts.map +1 -0
- package/dist/util/resource-content-resolver.test.js +254 -0
- package/dist/util/resource-content-resolver.test.js.map +1 -0
- package/dist/util/resource-validators.test.d.ts +2 -0
- package/dist/util/resource-validators.test.d.ts.map +1 -0
- package/dist/util/resource-validators.test.js +90 -0
- package/dist/util/resource-validators.test.js.map +1 -0
- package/dist/util/tool-caller.d.ts +2 -2
- package/dist/util/tool-caller.d.ts.map +1 -1
- package/dist/util/tool-caller.js +8 -8
- package/dist/util/tool-caller.js.map +1 -1
- package/dist/util/validate-component-name.test.d.ts +2 -0
- package/dist/util/validate-component-name.test.d.ts.map +1 -0
- package/dist/util/validate-component-name.test.js +35 -0
- package/dist/util/validate-component-name.test.js.map +1 -0
- package/esm/context-helpers/context-helpers.test.js +16 -4
- package/esm/context-helpers/context-helpers.test.js.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.d.ts +2 -2
- package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.js +31 -12
- package/esm/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/esm/context-helpers/registry.d.ts +2 -2
- package/esm/context-helpers/registry.d.ts.map +1 -1
- package/esm/context-helpers/registry.js.map +1 -1
- package/esm/context-helpers/types.d.ts +2 -2
- package/esm/context-helpers/types.d.ts.map +1 -1
- package/esm/context-helpers/types.js.map +1 -1
- package/esm/hoc/with-tambo-interactable.d.ts +50 -4
- package/esm/hoc/with-tambo-interactable.d.ts.map +1 -1
- package/esm/hoc/with-tambo-interactable.js +20 -5
- package/esm/hoc/with-tambo-interactable.js.map +1 -1
- package/esm/hooks/use-component-state.d.ts +3 -8
- package/esm/hooks/use-component-state.d.ts.map +1 -1
- package/esm/hooks/use-component-state.js +8 -0
- package/esm/hooks/use-component-state.js.map +1 -1
- package/esm/hooks/use-component-state.test.js +37 -0
- package/esm/hooks/use-component-state.test.js.map +1 -1
- package/esm/hooks/use-message-images.test.js +174 -37
- package/esm/hooks/use-message-images.test.js.map +1 -1
- package/esm/hooks/use-tambo-threads.js +1 -1
- package/esm/hooks/use-tambo-threads.js.map +1 -1
- package/esm/hooks/use-tambo-voice.d.ts +1 -1
- package/esm/hooks/use-tambo-voice.js +1 -1
- package/esm/hooks/use-tambo-voice.js.map +1 -1
- package/esm/hooks/use-tambo-voice.test.d.ts +2 -0
- package/esm/hooks/use-tambo-voice.test.d.ts.map +1 -0
- package/esm/hooks/use-tambo-voice.test.js +234 -0
- package/esm/hooks/use-tambo-voice.test.js.map +1 -0
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/mcp/elicitation.d.ts.map +1 -1
- package/esm/mcp/elicitation.js +12 -0
- package/esm/mcp/elicitation.js.map +1 -1
- package/esm/mcp/elicitation.test.js +8 -1
- package/esm/mcp/elicitation.test.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +6 -10
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/mcp-constants.d.ts +19 -0
- package/esm/mcp/mcp-constants.d.ts.map +1 -0
- package/esm/mcp/mcp-constants.js +18 -0
- package/esm/mcp/mcp-constants.js.map +1 -0
- package/esm/mcp/mcp-hooks.d.ts +21 -40
- package/esm/mcp/mcp-hooks.d.ts.map +1 -1
- package/esm/mcp/mcp-hooks.js +97 -40
- package/esm/mcp/mcp-hooks.js.map +1 -1
- package/esm/mcp/mcp-hooks.test.js +431 -5
- package/esm/mcp/mcp-hooks.test.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.d.ts +7 -0
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +204 -154
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.test.js +37 -0
- package/esm/mcp/tambo-mcp-provider.test.js.map +1 -1
- package/esm/model/component-metadata.d.ts +54 -21
- package/esm/model/component-metadata.d.ts.map +1 -1
- package/esm/model/component-metadata.js.map +1 -1
- package/esm/model/tambo-interactable.d.ts +13 -5
- package/esm/model/tambo-interactable.d.ts.map +1 -1
- package/esm/model/tambo-interactable.js.map +1 -1
- package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
- package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
- package/esm/providers/__tests__/thread-input-resource-resolution.test.js +587 -0
- package/esm/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
- package/esm/providers/index.d.ts +1 -1
- package/esm/providers/index.d.ts.map +1 -1
- package/esm/providers/index.js.map +1 -1
- package/esm/providers/tambo-client-provider.d.ts +8 -0
- package/esm/providers/tambo-client-provider.d.ts.map +1 -1
- package/esm/providers/tambo-client-provider.js +11 -12
- package/esm/providers/tambo-client-provider.js.map +1 -1
- package/esm/providers/tambo-client-provider.test.d.ts +2 -0
- package/esm/providers/tambo-client-provider.test.d.ts.map +1 -0
- package/esm/providers/tambo-client-provider.test.js +203 -0
- package/esm/providers/tambo-client-provider.test.js.map +1 -0
- package/esm/providers/tambo-context-attachment-provider.d.ts +34 -92
- package/esm/providers/tambo-context-attachment-provider.d.ts.map +1 -1
- package/esm/providers/tambo-context-attachment-provider.js +63 -106
- package/esm/providers/tambo-context-attachment-provider.js.map +1 -1
- package/esm/providers/tambo-context-attachment-provider.test.js +230 -464
- package/esm/providers/tambo-context-attachment-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
- package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts +5 -2
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +126 -17
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.test.js +242 -0
- package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactables-additional-context.test.js +2 -5
- package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/esm/providers/tambo-provider.d.ts +2 -3
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +6 -5
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-registry-provider.test.js +16 -0
- package/esm/providers/tambo-registry-provider.test.js.map +1 -1
- package/esm/providers/tambo-registry-schema-compat.test.js +31 -0
- package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-input-provider.js +26 -4
- package/esm/providers/tambo-thread-input-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider-initial-messages.test.js +84 -2
- package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +53 -42
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.test.js +368 -262
- package/esm/providers/tambo-thread-provider.test.js.map +1 -1
- package/esm/schema/index.d.ts +1 -1
- package/esm/schema/index.d.ts.map +1 -1
- package/esm/schema/index.js +1 -1
- package/esm/schema/index.js.map +1 -1
- package/esm/schema/json-schema.d.ts +7 -0
- package/esm/schema/json-schema.d.ts.map +1 -1
- package/esm/schema/json-schema.js +11 -1
- package/esm/schema/json-schema.js.map +1 -1
- package/esm/schema/json-schema.test.d.ts +2 -0
- package/esm/schema/json-schema.test.d.ts.map +1 -0
- package/esm/schema/json-schema.test.js +202 -0
- package/esm/schema/json-schema.test.js.map +1 -0
- package/esm/schema/schema.test.js +238 -1
- package/esm/schema/schema.test.js.map +1 -1
- package/esm/schema/standard-schema.d.ts +1 -0
- package/esm/schema/standard-schema.d.ts.map +1 -1
- package/esm/schema/standard-schema.js +18 -13
- package/esm/schema/standard-schema.js.map +1 -1
- package/esm/schema/standard-schema.test.d.ts +2 -0
- package/esm/schema/standard-schema.test.d.ts.map +1 -0
- package/esm/schema/standard-schema.test.js +130 -0
- package/esm/schema/standard-schema.test.js.map +1 -0
- package/esm/schema/validate.test.js +149 -0
- package/esm/schema/validate.test.js.map +1 -1
- package/esm/schema/zod.d.ts +7 -4
- package/esm/schema/zod.d.ts.map +1 -1
- package/esm/schema/zod.js +65 -22
- package/esm/schema/zod.js.map +1 -1
- package/esm/schema/zod.test.js +113 -1
- package/esm/schema/zod.test.js.map +1 -1
- package/esm/setupTests.js +3 -0
- package/esm/setupTests.js.map +1 -1
- package/esm/testing/tools.d.ts +4 -1
- package/esm/testing/tools.d.ts.map +1 -1
- package/esm/testing/tools.js +6 -1
- package/esm/testing/tools.js.map +1 -1
- package/esm/util/generate-component.d.ts.map +1 -1
- package/esm/util/generate-component.js +18 -3
- package/esm/util/generate-component.js.map +1 -1
- package/esm/util/generate-component.test.d.ts +2 -0
- package/esm/util/generate-component.test.d.ts.map +1 -0
- package/esm/util/generate-component.test.js +302 -0
- package/esm/util/generate-component.test.js.map +1 -0
- package/esm/util/is-promise.d.ts +9 -0
- package/esm/util/is-promise.d.ts.map +1 -0
- package/esm/util/is-promise.js +17 -0
- package/esm/util/is-promise.js.map +1 -0
- package/esm/util/is-promise.test.d.ts +2 -0
- package/esm/util/is-promise.test.d.ts.map +1 -0
- package/esm/util/is-promise.test.js +46 -0
- package/esm/util/is-promise.test.js.map +1 -0
- package/esm/util/message-builder.d.ts +3 -1
- package/esm/util/message-builder.d.ts.map +1 -1
- package/esm/util/message-builder.js +20 -3
- package/esm/util/message-builder.js.map +1 -1
- package/esm/util/message-builder.test.js +269 -0
- package/esm/util/message-builder.test.js.map +1 -1
- package/esm/util/query-utils.test.d.ts +2 -0
- package/esm/util/query-utils.test.d.ts.map +1 -0
- package/esm/util/query-utils.test.js +380 -0
- package/esm/util/query-utils.test.js.map +1 -0
- package/esm/util/registry-validators.d.ts.map +1 -1
- package/esm/util/registry-validators.js +7 -0
- package/esm/util/registry-validators.js.map +1 -1
- package/esm/util/registry-validators.test.js +57 -0
- package/esm/util/registry-validators.test.js.map +1 -1
- package/esm/util/registry.d.ts.map +1 -1
- package/esm/util/registry.js +9 -0
- package/esm/util/registry.js.map +1 -1
- package/esm/util/registry.test.js +324 -2
- package/esm/util/registry.test.js.map +1 -1
- package/esm/util/resource-content-resolver.d.ts +20 -0
- package/esm/util/resource-content-resolver.d.ts.map +1 -0
- package/esm/util/resource-content-resolver.js +89 -0
- package/esm/util/resource-content-resolver.js.map +1 -0
- package/esm/util/resource-content-resolver.test.d.ts +2 -0
- package/esm/util/resource-content-resolver.test.d.ts.map +1 -0
- package/esm/util/resource-content-resolver.test.js +252 -0
- package/esm/util/resource-content-resolver.test.js.map +1 -0
- package/esm/util/resource-validators.test.d.ts +2 -0
- package/esm/util/resource-validators.test.d.ts.map +1 -0
- package/esm/util/resource-validators.test.js +88 -0
- package/esm/util/resource-validators.test.js.map +1 -0
- package/esm/util/tool-caller.d.ts +2 -2
- package/esm/util/tool-caller.d.ts.map +1 -1
- package/esm/util/tool-caller.js +8 -8
- package/esm/util/tool-caller.js.map +1 -1
- package/esm/util/validate-component-name.test.d.ts +2 -0
- package/esm/util/validate-component-name.test.d.ts.map +1 -0
- package/esm/util/validate-component-name.test.js +33 -0
- package/esm/util/validate-component-name.test.js.map +1 -0
- package/package.json +15 -23
- package/dist/schema/alias.d.ts +0 -3
- package/dist/schema/alias.d.ts.map +0 -1
- package/dist/schema/alias.js +0 -6
- package/dist/schema/alias.js.map +0 -1
- package/esm/schema/alias.d.ts +0 -3
- package/esm/schema/alias.d.ts.map +0 -1
- package/esm/schema/alias.js +0 -13
- package/esm/schema/alias.js.map +0 -1
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { act, renderHook } from "@testing-library/react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { ServerType } from "../../mcp/mcp-constants";
|
|
5
|
+
import { TamboProvider } from "../tambo-provider";
|
|
6
|
+
import { useTamboThreadInput } from "../tambo-thread-input-provider";
|
|
7
|
+
// Mock the Tambo client provider to avoid needing real API credentials
|
|
8
|
+
jest.mock("../tambo-client-provider", () => {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
+
const ReactModule = require("react");
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
12
|
+
const { QueryClient: QC } = require("@tanstack/react-query");
|
|
13
|
+
const mockQueryClient = new QC();
|
|
14
|
+
const MockContext = ReactModule.createContext({
|
|
15
|
+
client: {},
|
|
16
|
+
queryClient: mockQueryClient,
|
|
17
|
+
isUpdatingToken: false,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
TamboClientProvider: ({ children }) => (React.createElement(MockContext.Provider, { value: {
|
|
21
|
+
client: {},
|
|
22
|
+
queryClient: mockQueryClient,
|
|
23
|
+
isUpdatingToken: false,
|
|
24
|
+
} }, children)),
|
|
25
|
+
TamboClientContext: MockContext,
|
|
26
|
+
useTamboClient: () => ({
|
|
27
|
+
client: {},
|
|
28
|
+
queryClient: mockQueryClient,
|
|
29
|
+
isUpdatingToken: false,
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
// Mock the thread provider to capture sendThreadMessage calls
|
|
34
|
+
const mockSendThreadMessage = jest.fn();
|
|
35
|
+
const mockContextKey = "test-context-key";
|
|
36
|
+
jest.mock("../tambo-thread-provider", () => ({
|
|
37
|
+
TamboThreadProvider: ({ children }) => children,
|
|
38
|
+
useTamboThread: () => ({
|
|
39
|
+
thread: { id: "test-thread-id" },
|
|
40
|
+
sendThreadMessage: mockSendThreadMessage,
|
|
41
|
+
contextKey: mockContextKey,
|
|
42
|
+
}),
|
|
43
|
+
}));
|
|
44
|
+
// Mock servers array - will be updated per test
|
|
45
|
+
let mockServers = [];
|
|
46
|
+
// Mock the MCP provider to avoid real MCP connections
|
|
47
|
+
jest.mock("../../mcp/tambo-mcp-provider", () => ({
|
|
48
|
+
TamboMcpProvider: ({ children }) => children,
|
|
49
|
+
useTamboMcpServers: () => mockServers,
|
|
50
|
+
}));
|
|
51
|
+
// Mock the MCP token provider
|
|
52
|
+
jest.mock("../tambo-mcp-token-provider", () => ({
|
|
53
|
+
TamboMcpTokenProvider: ({ children }) => children,
|
|
54
|
+
useTamboMcpToken: () => ({
|
|
55
|
+
mcpAccessToken: null,
|
|
56
|
+
tamboBaseUrl: null,
|
|
57
|
+
}),
|
|
58
|
+
}));
|
|
59
|
+
// Helper to create internal server
|
|
60
|
+
const createMockInternalServer = (serverKey) => ({
|
|
61
|
+
key: serverKey,
|
|
62
|
+
serverKey,
|
|
63
|
+
url: "https://api.tambo.ai/mcp",
|
|
64
|
+
name: "__tambo_internal_mcp_server__",
|
|
65
|
+
transport: "http",
|
|
66
|
+
serverType: ServerType.TAMBO_INTERNAL,
|
|
67
|
+
connectionError: undefined, // Internal servers resolved by backend
|
|
68
|
+
});
|
|
69
|
+
// Mock the message images hook
|
|
70
|
+
jest.mock("../../hooks/use-message-images", () => ({
|
|
71
|
+
useMessageImages: () => ({
|
|
72
|
+
images: [],
|
|
73
|
+
addImage: jest.fn(),
|
|
74
|
+
addImages: jest.fn(),
|
|
75
|
+
removeImage: jest.fn(),
|
|
76
|
+
clearImages: jest.fn(),
|
|
77
|
+
}),
|
|
78
|
+
}));
|
|
79
|
+
// Mock the mutation hook to execute the mutation function directly
|
|
80
|
+
jest.mock("../../hooks/react-query-hooks", () => ({
|
|
81
|
+
useTamboMutation: ({ mutationFn }) => ({
|
|
82
|
+
mutateAsync: mutationFn,
|
|
83
|
+
mutate: mutationFn,
|
|
84
|
+
isLoading: false,
|
|
85
|
+
isError: false,
|
|
86
|
+
isSuccess: false,
|
|
87
|
+
error: null,
|
|
88
|
+
reset: jest.fn(),
|
|
89
|
+
}),
|
|
90
|
+
}));
|
|
91
|
+
describe("TamboProvider - Resource Content Resolution Integration", () => {
|
|
92
|
+
let queryClient;
|
|
93
|
+
beforeEach(() => {
|
|
94
|
+
jest.clearAllMocks();
|
|
95
|
+
mockSendThreadMessage.mockResolvedValue(undefined);
|
|
96
|
+
// Default: no MCP servers (registry resources don't need a server entry)
|
|
97
|
+
mockServers = [];
|
|
98
|
+
queryClient = new QueryClient({
|
|
99
|
+
defaultOptions: {
|
|
100
|
+
queries: { retry: false },
|
|
101
|
+
mutations: { retry: false },
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
afterEach(() => {
|
|
106
|
+
queryClient.clear();
|
|
107
|
+
mockServers = [];
|
|
108
|
+
});
|
|
109
|
+
const createWrapper = (options) => {
|
|
110
|
+
function Wrapper({ children }) {
|
|
111
|
+
return (React.createElement(QueryClientProvider, { client: queryClient },
|
|
112
|
+
React.createElement(TamboProvider, { apiKey: "test-api-key", tamboUrl: "https://api.tambo.ai", listResources: options?.listResources,
|
|
113
|
+
// Cast to any because test mocks return simplified types
|
|
114
|
+
getResource: options?.getResource, resources: options?.resources, contextKey: mockContextKey }, children)));
|
|
115
|
+
}
|
|
116
|
+
return Wrapper;
|
|
117
|
+
};
|
|
118
|
+
it("should resolve registry resource content when submitting a message", async () => {
|
|
119
|
+
const mockGetResource = jest.fn().mockResolvedValue({
|
|
120
|
+
contents: [
|
|
121
|
+
{
|
|
122
|
+
uri: "file:///my-document.txt",
|
|
123
|
+
mimeType: "text/plain",
|
|
124
|
+
text: "This is the document content from the registry",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
const mockListResources = jest.fn().mockResolvedValue([
|
|
129
|
+
{
|
|
130
|
+
uri: "file:///my-document.txt",
|
|
131
|
+
name: "My Document",
|
|
132
|
+
mimeType: "text/plain",
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
136
|
+
wrapper: createWrapper({
|
|
137
|
+
listResources: mockListResources,
|
|
138
|
+
getResource: mockGetResource,
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
// Set the input value with a registry resource reference
|
|
142
|
+
act(() => {
|
|
143
|
+
result.current.setValue("Show me the contents of @registry:file:///my-document.txt in a table");
|
|
144
|
+
});
|
|
145
|
+
// Submit the message
|
|
146
|
+
await act(async () => {
|
|
147
|
+
await result.current.submit();
|
|
148
|
+
});
|
|
149
|
+
// Verify getResource was called with the correct URI (without the registry: prefix)
|
|
150
|
+
expect(mockGetResource).toHaveBeenCalledWith("file:///my-document.txt");
|
|
151
|
+
// Verify sendThreadMessage was called with the resolved content
|
|
152
|
+
expect(mockSendThreadMessage).toHaveBeenCalledTimes(1);
|
|
153
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
154
|
+
const content = options.content;
|
|
155
|
+
// Should have: text "Show me the contents of ", resource with resolved content, text " in a table"
|
|
156
|
+
expect(content).toMatchInlineSnapshot(`
|
|
157
|
+
[
|
|
158
|
+
{
|
|
159
|
+
"text": "Show me the contents of ",
|
|
160
|
+
"type": "text",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"resource": {
|
|
164
|
+
"mimeType": "text/plain",
|
|
165
|
+
"text": "This is the document content from the registry",
|
|
166
|
+
"uri": "file:///my-document.txt",
|
|
167
|
+
},
|
|
168
|
+
"type": "resource",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"text": " in a table",
|
|
172
|
+
"type": "text",
|
|
173
|
+
},
|
|
174
|
+
]
|
|
175
|
+
`);
|
|
176
|
+
});
|
|
177
|
+
it("should resolve multiple registry resources in a single message", async () => {
|
|
178
|
+
const mockGetResource = jest
|
|
179
|
+
.fn()
|
|
180
|
+
.mockImplementation(async (uri) => {
|
|
181
|
+
if (uri === "file:///doc1.txt") {
|
|
182
|
+
return { contents: [{ uri, text: "Content of doc1" }] };
|
|
183
|
+
}
|
|
184
|
+
if (uri === "file:///doc2.txt") {
|
|
185
|
+
return { contents: [{ uri, text: "Content of doc2" }] };
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
});
|
|
189
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
190
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
191
|
+
wrapper: createWrapper({
|
|
192
|
+
listResources: mockListResources,
|
|
193
|
+
getResource: mockGetResource,
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
act(() => {
|
|
197
|
+
result.current.setValue("Compare @registry:file:///doc1.txt with @registry:file:///doc2.txt");
|
|
198
|
+
});
|
|
199
|
+
await act(async () => {
|
|
200
|
+
await result.current.submit();
|
|
201
|
+
});
|
|
202
|
+
// Both resources should have been fetched
|
|
203
|
+
expect(mockGetResource).toHaveBeenCalledWith("file:///doc1.txt");
|
|
204
|
+
expect(mockGetResource).toHaveBeenCalledWith("file:///doc2.txt");
|
|
205
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
206
|
+
const content = options.content;
|
|
207
|
+
expect(content).toMatchInlineSnapshot(`
|
|
208
|
+
[
|
|
209
|
+
{
|
|
210
|
+
"text": "Compare ",
|
|
211
|
+
"type": "text",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"resource": {
|
|
215
|
+
"text": "Content of doc1",
|
|
216
|
+
"uri": "file:///doc1.txt",
|
|
217
|
+
},
|
|
218
|
+
"type": "resource",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"text": " with ",
|
|
222
|
+
"type": "text",
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"resource": {
|
|
226
|
+
"text": "Content of doc2",
|
|
227
|
+
"uri": "file:///doc2.txt",
|
|
228
|
+
},
|
|
229
|
+
"type": "resource",
|
|
230
|
+
},
|
|
231
|
+
]
|
|
232
|
+
`);
|
|
233
|
+
});
|
|
234
|
+
it("should resolve registry resource with blob content", async () => {
|
|
235
|
+
const mockGetResource = jest.fn().mockResolvedValue({
|
|
236
|
+
contents: [
|
|
237
|
+
{
|
|
238
|
+
uri: "file:///image.png",
|
|
239
|
+
mimeType: "image/png",
|
|
240
|
+
blob: "base64encodedimagedata",
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
});
|
|
244
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
245
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
246
|
+
wrapper: createWrapper({
|
|
247
|
+
listResources: mockListResources,
|
|
248
|
+
getResource: mockGetResource,
|
|
249
|
+
}),
|
|
250
|
+
});
|
|
251
|
+
act(() => {
|
|
252
|
+
result.current.setValue("Analyze @registry:file:///image.png");
|
|
253
|
+
});
|
|
254
|
+
await act(async () => {
|
|
255
|
+
await result.current.submit();
|
|
256
|
+
});
|
|
257
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
258
|
+
const content = options.content;
|
|
259
|
+
expect(content).toMatchInlineSnapshot(`
|
|
260
|
+
[
|
|
261
|
+
{
|
|
262
|
+
"text": "Analyze ",
|
|
263
|
+
"type": "text",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"resource": {
|
|
267
|
+
"blob": "base64encodedimagedata",
|
|
268
|
+
"mimeType": "image/png",
|
|
269
|
+
"uri": "file:///image.png",
|
|
270
|
+
},
|
|
271
|
+
"type": "resource",
|
|
272
|
+
},
|
|
273
|
+
]
|
|
274
|
+
`);
|
|
275
|
+
});
|
|
276
|
+
it("should continue with submission even if resource fetch fails (graceful fallback)", async () => {
|
|
277
|
+
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
278
|
+
const mockGetResource = jest
|
|
279
|
+
.fn()
|
|
280
|
+
.mockRejectedValue(new Error("Resource fetch failed"));
|
|
281
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
282
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
283
|
+
wrapper: createWrapper({
|
|
284
|
+
listResources: mockListResources,
|
|
285
|
+
getResource: mockGetResource,
|
|
286
|
+
}),
|
|
287
|
+
});
|
|
288
|
+
act(() => {
|
|
289
|
+
result.current.setValue("Check @registry:file:///missing.txt");
|
|
290
|
+
});
|
|
291
|
+
// Should not throw, should continue with submission
|
|
292
|
+
await act(async () => {
|
|
293
|
+
await result.current.submit();
|
|
294
|
+
});
|
|
295
|
+
// Resource fetch was attempted
|
|
296
|
+
expect(mockGetResource).toHaveBeenCalledWith("file:///missing.txt");
|
|
297
|
+
// Warning should be logged
|
|
298
|
+
expect(consoleSpy).toHaveBeenCalledWith("Failed to fetch resource content for registry:file:///missing.txt:", expect.any(Error));
|
|
299
|
+
// Message should still be sent (without the resolved content)
|
|
300
|
+
expect(mockSendThreadMessage).toHaveBeenCalledTimes(1);
|
|
301
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
302
|
+
const content = options.content;
|
|
303
|
+
// Resource should be present but without text/blob content
|
|
304
|
+
expect(content).toMatchInlineSnapshot(`
|
|
305
|
+
[
|
|
306
|
+
{
|
|
307
|
+
"text": "Check ",
|
|
308
|
+
"type": "text",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"resource": {
|
|
312
|
+
"uri": "file:///missing.txt",
|
|
313
|
+
},
|
|
314
|
+
"type": "resource",
|
|
315
|
+
},
|
|
316
|
+
]
|
|
317
|
+
`);
|
|
318
|
+
consoleSpy.mockRestore();
|
|
319
|
+
});
|
|
320
|
+
it("should NOT resolve internal server resources (serverType: TAMBO_INTERNAL)", async () => {
|
|
321
|
+
// Add internal server to mock servers
|
|
322
|
+
mockServers = [createMockInternalServer("tambo-abc123")];
|
|
323
|
+
const mockGetResource = jest.fn().mockResolvedValue({
|
|
324
|
+
contents: [{ uri: "tambo:test://resource/1", text: "Should not fetch" }],
|
|
325
|
+
});
|
|
326
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
327
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
328
|
+
wrapper: createWrapper({
|
|
329
|
+
listResources: mockListResources,
|
|
330
|
+
getResource: mockGetResource,
|
|
331
|
+
}),
|
|
332
|
+
});
|
|
333
|
+
act(() => {
|
|
334
|
+
result.current.setValue("Check @tambo-abc123:tambo:test://internal/resource/1");
|
|
335
|
+
});
|
|
336
|
+
await act(async () => {
|
|
337
|
+
await result.current.submit();
|
|
338
|
+
});
|
|
339
|
+
// getResource should NOT be called for internal server resources
|
|
340
|
+
expect(mockGetResource).not.toHaveBeenCalled();
|
|
341
|
+
// Message should still be sent with resource (but without resolved content)
|
|
342
|
+
expect(mockSendThreadMessage).toHaveBeenCalledTimes(1);
|
|
343
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
344
|
+
const content = options.content;
|
|
345
|
+
expect(content).toMatchInlineSnapshot(`
|
|
346
|
+
[
|
|
347
|
+
{
|
|
348
|
+
"text": "Check ",
|
|
349
|
+
"type": "text",
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"resource": {
|
|
353
|
+
"uri": "tambo:test://internal/resource/1",
|
|
354
|
+
},
|
|
355
|
+
"type": "resource",
|
|
356
|
+
},
|
|
357
|
+
]
|
|
358
|
+
`);
|
|
359
|
+
});
|
|
360
|
+
it("should handle mixed registry and internal server resources", async () => {
|
|
361
|
+
// Add internal server
|
|
362
|
+
mockServers = [createMockInternalServer("tambo-xyz")];
|
|
363
|
+
const mockGetResource = jest.fn().mockResolvedValue({
|
|
364
|
+
contents: [{ uri: "file:///registry-doc.txt", text: "Registry content" }],
|
|
365
|
+
});
|
|
366
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
367
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
368
|
+
wrapper: createWrapper({
|
|
369
|
+
listResources: mockListResources,
|
|
370
|
+
getResource: mockGetResource,
|
|
371
|
+
}),
|
|
372
|
+
});
|
|
373
|
+
act(() => {
|
|
374
|
+
result.current.setValue("@registry:file:///registry-doc.txt and @tambo-xyz:tambo:test://internal");
|
|
375
|
+
});
|
|
376
|
+
await act(async () => {
|
|
377
|
+
await result.current.submit();
|
|
378
|
+
});
|
|
379
|
+
// Only the registry resource should be fetched
|
|
380
|
+
expect(mockGetResource).toHaveBeenCalledTimes(1);
|
|
381
|
+
expect(mockGetResource).toHaveBeenCalledWith("file:///registry-doc.txt");
|
|
382
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
383
|
+
const content = options.content;
|
|
384
|
+
expect(content).toMatchInlineSnapshot(`
|
|
385
|
+
[
|
|
386
|
+
{
|
|
387
|
+
"resource": {
|
|
388
|
+
"text": "Registry content",
|
|
389
|
+
"uri": "file:///registry-doc.txt",
|
|
390
|
+
},
|
|
391
|
+
"type": "resource",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"text": " and ",
|
|
395
|
+
"type": "text",
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"resource": {
|
|
399
|
+
"uri": "tambo:test://internal",
|
|
400
|
+
},
|
|
401
|
+
"type": "resource",
|
|
402
|
+
},
|
|
403
|
+
]
|
|
404
|
+
`);
|
|
405
|
+
});
|
|
406
|
+
it("should include resource names when provided in submit options", async () => {
|
|
407
|
+
const mockGetResource = jest.fn().mockResolvedValue({
|
|
408
|
+
contents: [
|
|
409
|
+
{
|
|
410
|
+
uri: "file:///doc.txt",
|
|
411
|
+
text: "Document content",
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
});
|
|
415
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
416
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
417
|
+
wrapper: createWrapper({
|
|
418
|
+
listResources: mockListResources,
|
|
419
|
+
getResource: mockGetResource,
|
|
420
|
+
}),
|
|
421
|
+
});
|
|
422
|
+
act(() => {
|
|
423
|
+
result.current.setValue("Check @registry:file:///doc.txt");
|
|
424
|
+
});
|
|
425
|
+
await act(async () => {
|
|
426
|
+
await result.current.submit({
|
|
427
|
+
resourceNames: {
|
|
428
|
+
"registry:file:///doc.txt": "Important Document.txt",
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
433
|
+
const content = options.content;
|
|
434
|
+
expect(content).toMatchInlineSnapshot(`
|
|
435
|
+
[
|
|
436
|
+
{
|
|
437
|
+
"text": "Check ",
|
|
438
|
+
"type": "text",
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"resource": {
|
|
442
|
+
"name": "Important Document.txt",
|
|
443
|
+
"text": "Document content",
|
|
444
|
+
"uri": "file:///doc.txt",
|
|
445
|
+
},
|
|
446
|
+
"type": "resource",
|
|
447
|
+
},
|
|
448
|
+
]
|
|
449
|
+
`);
|
|
450
|
+
});
|
|
451
|
+
it("should handle message without any resource references", async () => {
|
|
452
|
+
const mockGetResource = jest.fn();
|
|
453
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
454
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
455
|
+
wrapper: createWrapper({
|
|
456
|
+
listResources: mockListResources,
|
|
457
|
+
getResource: mockGetResource,
|
|
458
|
+
}),
|
|
459
|
+
});
|
|
460
|
+
act(() => {
|
|
461
|
+
result.current.setValue("Just a regular message without resources");
|
|
462
|
+
});
|
|
463
|
+
await act(async () => {
|
|
464
|
+
await result.current.submit();
|
|
465
|
+
});
|
|
466
|
+
// No resource fetch should happen
|
|
467
|
+
expect(mockGetResource).not.toHaveBeenCalled();
|
|
468
|
+
// Message should be sent as plain text
|
|
469
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
470
|
+
const content = options.content;
|
|
471
|
+
expect(content).toMatchInlineSnapshot(`
|
|
472
|
+
[
|
|
473
|
+
{
|
|
474
|
+
"text": "Just a regular message without resources",
|
|
475
|
+
"type": "text",
|
|
476
|
+
},
|
|
477
|
+
]
|
|
478
|
+
`);
|
|
479
|
+
});
|
|
480
|
+
it("should handle null returned from getResource", async () => {
|
|
481
|
+
const mockGetResource = jest.fn().mockResolvedValue(null);
|
|
482
|
+
const mockListResources = jest.fn().mockResolvedValue([]);
|
|
483
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
484
|
+
wrapper: createWrapper({
|
|
485
|
+
listResources: mockListResources,
|
|
486
|
+
getResource: mockGetResource,
|
|
487
|
+
}),
|
|
488
|
+
});
|
|
489
|
+
act(() => {
|
|
490
|
+
result.current.setValue("Check @registry:file:///unknown.txt");
|
|
491
|
+
});
|
|
492
|
+
await act(async () => {
|
|
493
|
+
await result.current.submit();
|
|
494
|
+
});
|
|
495
|
+
expect(mockGetResource).toHaveBeenCalled();
|
|
496
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
497
|
+
const content = options.content;
|
|
498
|
+
// Resource should be present but without content
|
|
499
|
+
expect(content).toMatchInlineSnapshot(`
|
|
500
|
+
[
|
|
501
|
+
{
|
|
502
|
+
"text": "Check ",
|
|
503
|
+
"type": "text",
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"resource": {
|
|
507
|
+
"uri": "file:///unknown.txt",
|
|
508
|
+
},
|
|
509
|
+
"type": "resource",
|
|
510
|
+
},
|
|
511
|
+
]
|
|
512
|
+
`);
|
|
513
|
+
});
|
|
514
|
+
it("should warn when no resourceSource is available for registry resource", async () => {
|
|
515
|
+
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
516
|
+
// No listResources/getResource provided
|
|
517
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
518
|
+
wrapper: createWrapper(),
|
|
519
|
+
});
|
|
520
|
+
act(() => {
|
|
521
|
+
result.current.setValue("Check @registry:file:///doc.txt");
|
|
522
|
+
});
|
|
523
|
+
await act(async () => {
|
|
524
|
+
await result.current.submit();
|
|
525
|
+
});
|
|
526
|
+
expect(consoleSpy).toHaveBeenCalledWith("No resource source available to resolve registry resource: registry:file:///doc.txt");
|
|
527
|
+
consoleSpy.mockRestore();
|
|
528
|
+
});
|
|
529
|
+
it("should resolve static resources passed via resources prop", async () => {
|
|
530
|
+
// When using static resources, we need both listResources and getResource
|
|
531
|
+
// to form the resourceSource, even though the resources are static
|
|
532
|
+
const mockGetResource = jest.fn().mockResolvedValue({
|
|
533
|
+
contents: [
|
|
534
|
+
{
|
|
535
|
+
uri: "static://my-static-resource",
|
|
536
|
+
text: "Static resource content",
|
|
537
|
+
mimeType: "text/plain",
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
});
|
|
541
|
+
const { result } = renderHook(() => useTamboThreadInput(), {
|
|
542
|
+
wrapper: createWrapper({
|
|
543
|
+
resources: [
|
|
544
|
+
{
|
|
545
|
+
uri: "static://my-static-resource",
|
|
546
|
+
name: "My Static Resource",
|
|
547
|
+
mimeType: "text/plain",
|
|
548
|
+
},
|
|
549
|
+
],
|
|
550
|
+
// Need getResource to actually fetch the content
|
|
551
|
+
getResource: mockGetResource,
|
|
552
|
+
listResources: async () => [],
|
|
553
|
+
}),
|
|
554
|
+
});
|
|
555
|
+
act(() => {
|
|
556
|
+
result.current.setValue("Show me @registry:static://my-static-resource please");
|
|
557
|
+
});
|
|
558
|
+
await act(async () => {
|
|
559
|
+
await result.current.submit();
|
|
560
|
+
});
|
|
561
|
+
// getResource should be called to fetch the content
|
|
562
|
+
expect(mockGetResource).toHaveBeenCalledWith("static://my-static-resource");
|
|
563
|
+
const [, options] = mockSendThreadMessage.mock.calls[0];
|
|
564
|
+
const content = options.content;
|
|
565
|
+
expect(content).toMatchInlineSnapshot(`
|
|
566
|
+
[
|
|
567
|
+
{
|
|
568
|
+
"text": "Show me ",
|
|
569
|
+
"type": "text",
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
"resource": {
|
|
573
|
+
"mimeType": "text/plain",
|
|
574
|
+
"text": "Static resource content",
|
|
575
|
+
"uri": "static://my-static-resource",
|
|
576
|
+
},
|
|
577
|
+
"type": "resource",
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
"text": " please",
|
|
581
|
+
"type": "text",
|
|
582
|
+
},
|
|
583
|
+
]
|
|
584
|
+
`);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
//# sourceMappingURL=thread-input-resource-resolution.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-input-resource-resolution.test.js","sourceRoot":"","sources":["../../../src/providers/__tests__/thread-input-resource-resolution.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { act, renderHook } from \"@testing-library/react\";\nimport React from \"react\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { ServerType } from \"../../mcp/mcp-constants\";\nimport type { McpServer } from \"../../mcp/tambo-mcp-provider\";\nimport { TamboProvider } from \"../tambo-provider\";\nimport { useTamboThreadInput } from \"../tambo-thread-input-provider\";\n\n// Mock the Tambo client provider to avoid needing real API credentials\n\njest.mock(\"../tambo-client-provider\", () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const ReactModule = require(\"react\");\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { QueryClient: QC } = require(\"@tanstack/react-query\");\n const mockQueryClient = new QC();\n const MockContext = ReactModule.createContext({\n client: {},\n queryClient: mockQueryClient,\n isUpdatingToken: false,\n });\n return {\n TamboClientProvider: ({ children }: { children: React.ReactNode }) => (\n <MockContext.Provider\n value={{\n client: {},\n queryClient: mockQueryClient,\n isUpdatingToken: false,\n }}\n >\n {children}\n </MockContext.Provider>\n ),\n TamboClientContext: MockContext,\n useTamboClient: () => ({\n client: {},\n queryClient: mockQueryClient,\n isUpdatingToken: false,\n }),\n };\n});\n\n// Mock the thread provider to capture sendThreadMessage calls\nconst mockSendThreadMessage = jest.fn();\nconst mockContextKey = \"test-context-key\";\njest.mock(\"../tambo-thread-provider\", () => ({\n TamboThreadProvider: ({ children }: { children: React.ReactNode }) =>\n children,\n useTamboThread: () => ({\n thread: { id: \"test-thread-id\" },\n sendThreadMessage: mockSendThreadMessage,\n contextKey: mockContextKey,\n }),\n}));\n\n// Mock servers array - will be updated per test\nlet mockServers: McpServer[] = [];\n\n// Mock the MCP provider to avoid real MCP connections\njest.mock(\"../../mcp/tambo-mcp-provider\", () => ({\n TamboMcpProvider: ({ children }: { children: React.ReactNode }) => children,\n useTamboMcpServers: () => mockServers,\n}));\n\n// Mock the MCP token provider\njest.mock(\"../tambo-mcp-token-provider\", () => ({\n TamboMcpTokenProvider: ({ children }: { children: React.ReactNode }) =>\n children,\n useTamboMcpToken: () => ({\n mcpAccessToken: null,\n tamboBaseUrl: null,\n }),\n}));\n\n// Helper to create internal server\nconst createMockInternalServer = (serverKey: string): McpServer =>\n ({\n key: serverKey,\n serverKey,\n url: \"https://api.tambo.ai/mcp\",\n name: \"__tambo_internal_mcp_server__\",\n transport: \"http\",\n serverType: ServerType.TAMBO_INTERNAL,\n connectionError: undefined, // Internal servers resolved by backend\n }) as unknown as McpServer;\n\n// Mock the message images hook\njest.mock(\"../../hooks/use-message-images\", () => ({\n useMessageImages: () => ({\n images: [],\n addImage: jest.fn(),\n addImages: jest.fn(),\n removeImage: jest.fn(),\n clearImages: jest.fn(),\n }),\n}));\n\n// Mock the mutation hook to execute the mutation function directly\njest.mock(\"../../hooks/react-query-hooks\", () => ({\n useTamboMutation: ({ mutationFn }: { mutationFn: () => Promise<void> }) => ({\n mutateAsync: mutationFn,\n mutate: mutationFn,\n isLoading: false,\n isError: false,\n isSuccess: false,\n error: null,\n reset: jest.fn(),\n }),\n}));\n\ndescribe(\"TamboProvider - Resource Content Resolution Integration\", () => {\n let queryClient: QueryClient;\n\n beforeEach(() => {\n jest.clearAllMocks();\n mockSendThreadMessage.mockResolvedValue(undefined);\n // Default: no MCP servers (registry resources don't need a server entry)\n mockServers = [];\n queryClient = new QueryClient({\n defaultOptions: {\n queries: { retry: false },\n mutations: { retry: false },\n },\n });\n });\n\n afterEach(() => {\n queryClient.clear();\n mockServers = [];\n });\n\n const createWrapper = (options?: {\n listResources?: (\n search?: string,\n ) => Promise<{ uri: string; name: string; mimeType?: string }[]>;\n getResource?: (uri: string) => Promise<unknown>;\n resources?: { uri: string; name: string; mimeType?: string }[];\n }) => {\n function Wrapper({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={queryClient}>\n <TamboProvider\n apiKey=\"test-api-key\"\n tamboUrl=\"https://api.tambo.ai\"\n listResources={options?.listResources}\n // Cast to any because test mocks return simplified types\n getResource={options?.getResource as any}\n resources={options?.resources}\n contextKey={mockContextKey}\n >\n {children}\n </TamboProvider>\n </QueryClientProvider>\n );\n }\n return Wrapper;\n };\n\n it(\"should resolve registry resource content when submitting a message\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///my-document.txt\",\n mimeType: \"text/plain\",\n text: \"This is the document content from the registry\",\n },\n ],\n });\n\n const mockListResources = jest.fn().mockResolvedValue([\n {\n uri: \"file:///my-document.txt\",\n name: \"My Document\",\n mimeType: \"text/plain\",\n },\n ]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n // Set the input value with a registry resource reference\n act(() => {\n result.current.setValue(\n \"Show me the contents of @registry:file:///my-document.txt in a table\",\n );\n });\n\n // Submit the message\n await act(async () => {\n await result.current.submit();\n });\n\n // Verify getResource was called with the correct URI (without the registry: prefix)\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///my-document.txt\");\n\n // Verify sendThreadMessage was called with the resolved content\n expect(mockSendThreadMessage).toHaveBeenCalledTimes(1);\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n // Should have: text \"Show me the contents of \", resource with resolved content, text \" in a table\"\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Show me the contents of \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"mimeType\": \"text/plain\",\n \"text\": \"This is the document content from the registry\",\n \"uri\": \"file:///my-document.txt\",\n },\n \"type\": \"resource\",\n },\n {\n \"text\": \" in a table\",\n \"type\": \"text\",\n },\n ]\n `);\n });\n\n it(\"should resolve multiple registry resources in a single message\", async () => {\n const mockGetResource = jest\n .fn()\n .mockImplementation(async (uri: string) => {\n if (uri === \"file:///doc1.txt\") {\n return { contents: [{ uri, text: \"Content of doc1\" }] };\n }\n if (uri === \"file:///doc2.txt\") {\n return { contents: [{ uri, text: \"Content of doc2\" }] };\n }\n return null;\n });\n\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\n \"Compare @registry:file:///doc1.txt with @registry:file:///doc2.txt\",\n );\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n // Both resources should have been fetched\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///doc1.txt\");\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///doc2.txt\");\n\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Compare \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"text\": \"Content of doc1\",\n \"uri\": \"file:///doc1.txt\",\n },\n \"type\": \"resource\",\n },\n {\n \"text\": \" with \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"text\": \"Content of doc2\",\n \"uri\": \"file:///doc2.txt\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n });\n\n it(\"should resolve registry resource with blob content\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///image.png\",\n mimeType: \"image/png\",\n blob: \"base64encodedimagedata\",\n },\n ],\n });\n\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\"Analyze @registry:file:///image.png\");\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Analyze \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"blob\": \"base64encodedimagedata\",\n \"mimeType\": \"image/png\",\n \"uri\": \"file:///image.png\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n });\n\n it(\"should continue with submission even if resource fetch fails (graceful fallback)\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n const mockGetResource = jest\n .fn()\n .mockRejectedValue(new Error(\"Resource fetch failed\"));\n\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\"Check @registry:file:///missing.txt\");\n });\n\n // Should not throw, should continue with submission\n await act(async () => {\n await result.current.submit();\n });\n\n // Resource fetch was attempted\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///missing.txt\");\n\n // Warning should be logged\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Failed to fetch resource content for registry:file:///missing.txt:\",\n expect.any(Error),\n );\n\n // Message should still be sent (without the resolved content)\n expect(mockSendThreadMessage).toHaveBeenCalledTimes(1);\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n // Resource should be present but without text/blob content\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Check \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"uri\": \"file:///missing.txt\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should NOT resolve internal server resources (serverType: TAMBO_INTERNAL)\", async () => {\n // Add internal server to mock servers\n mockServers = [createMockInternalServer(\"tambo-abc123\")];\n\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"tambo:test://resource/1\", text: \"Should not fetch\" }],\n });\n\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\n \"Check @tambo-abc123:tambo:test://internal/resource/1\",\n );\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n // getResource should NOT be called for internal server resources\n expect(mockGetResource).not.toHaveBeenCalled();\n\n // Message should still be sent with resource (but without resolved content)\n expect(mockSendThreadMessage).toHaveBeenCalledTimes(1);\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Check \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"uri\": \"tambo:test://internal/resource/1\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n });\n\n it(\"should handle mixed registry and internal server resources\", async () => {\n // Add internal server\n mockServers = [createMockInternalServer(\"tambo-xyz\")];\n\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///registry-doc.txt\", text: \"Registry content\" }],\n });\n\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\n \"@registry:file:///registry-doc.txt and @tambo-xyz:tambo:test://internal\",\n );\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n // Only the registry resource should be fetched\n expect(mockGetResource).toHaveBeenCalledTimes(1);\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///registry-doc.txt\");\n\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"resource\": {\n \"text\": \"Registry content\",\n \"uri\": \"file:///registry-doc.txt\",\n },\n \"type\": \"resource\",\n },\n {\n \"text\": \" and \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"uri\": \"tambo:test://internal\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n });\n\n it(\"should include resource names when provided in submit options\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///doc.txt\",\n text: \"Document content\",\n },\n ],\n });\n\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\"Check @registry:file:///doc.txt\");\n });\n\n await act(async () => {\n await result.current.submit({\n resourceNames: {\n \"registry:file:///doc.txt\": \"Important Document.txt\",\n },\n });\n });\n\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Check \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"name\": \"Important Document.txt\",\n \"text\": \"Document content\",\n \"uri\": \"file:///doc.txt\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n });\n\n it(\"should handle message without any resource references\", async () => {\n const mockGetResource = jest.fn();\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\"Just a regular message without resources\");\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n // No resource fetch should happen\n expect(mockGetResource).not.toHaveBeenCalled();\n\n // Message should be sent as plain text\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Just a regular message without resources\",\n \"type\": \"text\",\n },\n ]\n `);\n });\n\n it(\"should handle null returned from getResource\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue(null);\n const mockListResources = jest.fn().mockResolvedValue([]);\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n listResources: mockListResources,\n getResource: mockGetResource,\n }),\n });\n\n act(() => {\n result.current.setValue(\"Check @registry:file:///unknown.txt\");\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n expect(mockGetResource).toHaveBeenCalled();\n\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n // Resource should be present but without content\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Check \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"uri\": \"file:///unknown.txt\",\n },\n \"type\": \"resource\",\n },\n ]\n `);\n });\n\n it(\"should warn when no resourceSource is available for registry resource\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n // No listResources/getResource provided\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper(),\n });\n\n act(() => {\n result.current.setValue(\"Check @registry:file:///doc.txt\");\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"No resource source available to resolve registry resource: registry:file:///doc.txt\",\n );\n\n consoleSpy.mockRestore();\n });\n\n it(\"should resolve static resources passed via resources prop\", async () => {\n // When using static resources, we need both listResources and getResource\n // to form the resourceSource, even though the resources are static\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"static://my-static-resource\",\n text: \"Static resource content\",\n mimeType: \"text/plain\",\n },\n ],\n });\n\n const { result } = renderHook(() => useTamboThreadInput(), {\n wrapper: createWrapper({\n resources: [\n {\n uri: \"static://my-static-resource\",\n name: \"My Static Resource\",\n mimeType: \"text/plain\",\n },\n ],\n // Need getResource to actually fetch the content\n getResource: mockGetResource,\n listResources: async () => [],\n }),\n });\n\n act(() => {\n result.current.setValue(\n \"Show me @registry:static://my-static-resource please\",\n );\n });\n\n await act(async () => {\n await result.current.submit();\n });\n\n // getResource should be called to fetch the content\n expect(mockGetResource).toHaveBeenCalledWith(\"static://my-static-resource\");\n\n const [, options] = mockSendThreadMessage.mock.calls[0];\n const content =\n options.content as TamboAI.Beta.Threads.ChatCompletionContentPart[];\n\n expect(content).toMatchInlineSnapshot(`\n [\n {\n \"text\": \"Show me \",\n \"type\": \"text\",\n },\n {\n \"resource\": {\n \"mimeType\": \"text/plain\",\n \"text\": \"Static resource content\",\n \"uri\": \"static://my-static-resource\",\n },\n \"type\": \"resource\",\n },\n {\n \"text\": \" please\",\n \"type\": \"text\",\n },\n ]\n `);\n });\n});\n"]}
|