@tambo-ai/react 0.67.0 → 0.68.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.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -4
  3. package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
  4. package/dist/context-helpers/current-interactables-context-helper.js +4 -1
  5. package/dist/context-helpers/current-interactables-context-helper.js.map +1 -1
  6. package/dist/hoc/with-tambo-interactable.d.ts +50 -4
  7. package/dist/hoc/with-tambo-interactable.d.ts.map +1 -1
  8. package/dist/hoc/with-tambo-interactable.js +20 -5
  9. package/dist/hoc/with-tambo-interactable.js.map +1 -1
  10. package/dist/hooks/use-component-state.d.ts +3 -8
  11. package/dist/hooks/use-component-state.d.ts.map +1 -1
  12. package/dist/hooks/use-component-state.js +8 -0
  13. package/dist/hooks/use-component-state.js.map +1 -1
  14. package/dist/hooks/use-component-state.test.js +37 -0
  15. package/dist/hooks/use-component-state.test.js.map +1 -1
  16. package/dist/hooks/use-tambo-threads.d.ts +3 -8
  17. package/dist/hooks/use-tambo-threads.d.ts.map +1 -1
  18. package/dist/hooks/use-tambo-threads.js +5 -3
  19. package/dist/hooks/use-tambo-threads.js.map +1 -1
  20. package/dist/hooks/use-tambo-threads.test.js +12 -2
  21. package/dist/hooks/use-tambo-threads.test.js.map +1 -1
  22. package/dist/mcp/mcp-constants.d.ts +19 -0
  23. package/dist/mcp/mcp-constants.d.ts.map +1 -0
  24. package/dist/mcp/mcp-constants.js +21 -0
  25. package/dist/mcp/mcp-constants.js.map +1 -0
  26. package/dist/mcp/mcp-hooks.d.ts +32 -3
  27. package/dist/mcp/mcp-hooks.d.ts.map +1 -1
  28. package/dist/mcp/mcp-hooks.js +40 -29
  29. package/dist/mcp/mcp-hooks.js.map +1 -1
  30. package/dist/mcp/mcp-hooks.test.js +8 -5
  31. package/dist/mcp/mcp-hooks.test.js.map +1 -1
  32. package/dist/mcp/tambo-mcp-provider.d.ts +7 -0
  33. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  34. package/dist/mcp/tambo-mcp-provider.js +202 -155
  35. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  36. package/dist/model/component-metadata.d.ts +1 -1
  37. package/dist/model/component-metadata.d.ts.map +1 -1
  38. package/dist/model/component-metadata.js.map +1 -1
  39. package/dist/model/tambo-interactable.d.ts +7 -5
  40. package/dist/model/tambo-interactable.d.ts.map +1 -1
  41. package/dist/model/tambo-interactable.js.map +1 -1
  42. package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
  43. package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
  44. package/dist/providers/__tests__/thread-input-resource-resolution.test.js +592 -0
  45. package/dist/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
  46. package/dist/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
  47. package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  48. package/dist/providers/tambo-interactable-provider.d.ts +3 -2
  49. package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
  50. package/dist/providers/tambo-interactable-provider.js +98 -14
  51. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  52. package/dist/providers/tambo-interactable-provider.test.js +242 -0
  53. package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
  54. package/dist/providers/tambo-provider.d.ts +1 -2
  55. package/dist/providers/tambo-provider.d.ts.map +1 -1
  56. package/dist/providers/tambo-provider.js +10 -8
  57. package/dist/providers/tambo-provider.js.map +1 -1
  58. package/dist/providers/tambo-stubs.d.ts.map +1 -1
  59. package/dist/providers/tambo-stubs.js +1 -0
  60. package/dist/providers/tambo-stubs.js.map +1 -1
  61. package/dist/providers/tambo-stubs.test.js +1 -1
  62. package/dist/providers/tambo-stubs.test.js.map +1 -1
  63. package/dist/providers/tambo-thread-input-provider.d.ts +1 -6
  64. package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
  65. package/dist/providers/tambo-thread-input-provider.js +25 -8
  66. package/dist/providers/tambo-thread-input-provider.js.map +1 -1
  67. package/dist/providers/tambo-thread-provider.d.ts +5 -0
  68. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  69. package/dist/providers/tambo-thread-provider.js +5 -2
  70. package/dist/providers/tambo-thread-provider.js.map +1 -1
  71. package/dist/schema/index.d.ts +1 -1
  72. package/dist/schema/index.d.ts.map +1 -1
  73. package/dist/schema/index.js +2 -1
  74. package/dist/schema/index.js.map +1 -1
  75. package/dist/schema/json-schema.d.ts +7 -0
  76. package/dist/schema/json-schema.d.ts.map +1 -1
  77. package/dist/schema/json-schema.js +11 -0
  78. package/dist/schema/json-schema.js.map +1 -1
  79. package/dist/schema/json-schema.test.d.ts +2 -0
  80. package/dist/schema/json-schema.test.d.ts.map +1 -0
  81. package/dist/schema/json-schema.test.js +204 -0
  82. package/dist/schema/json-schema.test.js.map +1 -0
  83. package/dist/setupTests.js +3 -0
  84. package/dist/setupTests.js.map +1 -1
  85. package/dist/util/message-builder.d.ts +3 -1
  86. package/dist/util/message-builder.d.ts.map +1 -1
  87. package/dist/util/message-builder.js +20 -3
  88. package/dist/util/message-builder.js.map +1 -1
  89. package/dist/util/message-builder.test.js +269 -0
  90. package/dist/util/message-builder.test.js.map +1 -1
  91. package/dist/util/resource-content-resolver.d.ts +20 -0
  92. package/dist/util/resource-content-resolver.d.ts.map +1 -0
  93. package/dist/util/resource-content-resolver.js +93 -0
  94. package/dist/util/resource-content-resolver.js.map +1 -0
  95. package/dist/util/resource-content-resolver.test.d.ts +2 -0
  96. package/dist/util/resource-content-resolver.test.d.ts.map +1 -0
  97. package/dist/util/resource-content-resolver.test.js +254 -0
  98. package/dist/util/resource-content-resolver.test.js.map +1 -0
  99. package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
  100. package/esm/context-helpers/current-interactables-context-helper.js +4 -1
  101. package/esm/context-helpers/current-interactables-context-helper.js.map +1 -1
  102. package/esm/hoc/with-tambo-interactable.d.ts +50 -4
  103. package/esm/hoc/with-tambo-interactable.d.ts.map +1 -1
  104. package/esm/hoc/with-tambo-interactable.js +20 -5
  105. package/esm/hoc/with-tambo-interactable.js.map +1 -1
  106. package/esm/hooks/use-component-state.d.ts +3 -8
  107. package/esm/hooks/use-component-state.d.ts.map +1 -1
  108. package/esm/hooks/use-component-state.js +8 -0
  109. package/esm/hooks/use-component-state.js.map +1 -1
  110. package/esm/hooks/use-component-state.test.js +37 -0
  111. package/esm/hooks/use-component-state.test.js.map +1 -1
  112. package/esm/hooks/use-tambo-threads.d.ts +3 -8
  113. package/esm/hooks/use-tambo-threads.d.ts.map +1 -1
  114. package/esm/hooks/use-tambo-threads.js +5 -3
  115. package/esm/hooks/use-tambo-threads.js.map +1 -1
  116. package/esm/hooks/use-tambo-threads.test.js +12 -2
  117. package/esm/hooks/use-tambo-threads.test.js.map +1 -1
  118. package/esm/mcp/mcp-constants.d.ts +19 -0
  119. package/esm/mcp/mcp-constants.d.ts.map +1 -0
  120. package/esm/mcp/mcp-constants.js +18 -0
  121. package/esm/mcp/mcp-constants.js.map +1 -0
  122. package/esm/mcp/mcp-hooks.d.ts +32 -3
  123. package/esm/mcp/mcp-hooks.d.ts.map +1 -1
  124. package/esm/mcp/mcp-hooks.js +40 -30
  125. package/esm/mcp/mcp-hooks.js.map +1 -1
  126. package/esm/mcp/mcp-hooks.test.js +8 -5
  127. package/esm/mcp/mcp-hooks.test.js.map +1 -1
  128. package/esm/mcp/tambo-mcp-provider.d.ts +7 -0
  129. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  130. package/esm/mcp/tambo-mcp-provider.js +201 -154
  131. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  132. package/esm/model/component-metadata.d.ts +1 -1
  133. package/esm/model/component-metadata.d.ts.map +1 -1
  134. package/esm/model/component-metadata.js.map +1 -1
  135. package/esm/model/tambo-interactable.d.ts +7 -5
  136. package/esm/model/tambo-interactable.d.ts.map +1 -1
  137. package/esm/model/tambo-interactable.js.map +1 -1
  138. package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
  139. package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
  140. package/esm/providers/__tests__/thread-input-resource-resolution.test.js +587 -0
  141. package/esm/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
  142. package/esm/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
  143. package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  144. package/esm/providers/tambo-interactable-provider.d.ts +3 -2
  145. package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
  146. package/esm/providers/tambo-interactable-provider.js +98 -14
  147. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  148. package/esm/providers/tambo-interactable-provider.test.js +242 -0
  149. package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
  150. package/esm/providers/tambo-provider.d.ts +1 -2
  151. package/esm/providers/tambo-provider.d.ts.map +1 -1
  152. package/esm/providers/tambo-provider.js +11 -9
  153. package/esm/providers/tambo-provider.js.map +1 -1
  154. package/esm/providers/tambo-stubs.d.ts.map +1 -1
  155. package/esm/providers/tambo-stubs.js +1 -0
  156. package/esm/providers/tambo-stubs.js.map +1 -1
  157. package/esm/providers/tambo-stubs.test.js +1 -1
  158. package/esm/providers/tambo-stubs.test.js.map +1 -1
  159. package/esm/providers/tambo-thread-input-provider.d.ts +1 -6
  160. package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
  161. package/esm/providers/tambo-thread-input-provider.js +25 -8
  162. package/esm/providers/tambo-thread-input-provider.js.map +1 -1
  163. package/esm/providers/tambo-thread-provider.d.ts +5 -0
  164. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  165. package/esm/providers/tambo-thread-provider.js +5 -2
  166. package/esm/providers/tambo-thread-provider.js.map +1 -1
  167. package/esm/schema/index.d.ts +1 -1
  168. package/esm/schema/index.d.ts.map +1 -1
  169. package/esm/schema/index.js +1 -1
  170. package/esm/schema/index.js.map +1 -1
  171. package/esm/schema/json-schema.d.ts +7 -0
  172. package/esm/schema/json-schema.d.ts.map +1 -1
  173. package/esm/schema/json-schema.js +10 -0
  174. package/esm/schema/json-schema.js.map +1 -1
  175. package/esm/schema/json-schema.test.d.ts +2 -0
  176. package/esm/schema/json-schema.test.d.ts.map +1 -0
  177. package/esm/schema/json-schema.test.js +202 -0
  178. package/esm/schema/json-schema.test.js.map +1 -0
  179. package/esm/setupTests.js +3 -0
  180. package/esm/setupTests.js.map +1 -1
  181. package/esm/util/message-builder.d.ts +3 -1
  182. package/esm/util/message-builder.d.ts.map +1 -1
  183. package/esm/util/message-builder.js +20 -3
  184. package/esm/util/message-builder.js.map +1 -1
  185. package/esm/util/message-builder.test.js +269 -0
  186. package/esm/util/message-builder.test.js.map +1 -1
  187. package/esm/util/resource-content-resolver.d.ts +20 -0
  188. package/esm/util/resource-content-resolver.d.ts.map +1 -0
  189. package/esm/util/resource-content-resolver.js +89 -0
  190. package/esm/util/resource-content-resolver.js.map +1 -0
  191. package/esm/util/resource-content-resolver.test.d.ts +2 -0
  192. package/esm/util/resource-content-resolver.test.d.ts.map +1 -0
  193. package/esm/util/resource-content-resolver.test.js +252 -0
  194. package/esm/util/resource-content-resolver.test.js.map +1 -0
  195. package/package.json +8 -6
@@ -1,4 +1,4 @@
1
- export { looksLikeJSONSchema } from "./json-schema";
1
+ export { looksLikeJSONSchema, makeJsonSchemaPartial } from "./json-schema";
2
2
  export { getParametersFromToolSchema, hasInputSchema, safeSchemaToJsonSchema, schemaToJsonSchema, } from "./schema";
3
3
  export { isStandardSchema } from "./standard-schema";
4
4
  export { assertNoRecordSchema } from "./validate";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,kBAAkB,GAEnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAC","sourcesContent":["export { looksLikeJSONSchema } from \"./json-schema\";\nexport {\n getParametersFromToolSchema,\n hasInputSchema,\n safeSchemaToJsonSchema,\n schemaToJsonSchema,\n type SupportedSchema,\n} from \"./schema\";\nexport { isStandardSchema } from \"./standard-schema\";\nexport { assertNoRecordSchema } from \"./validate\";\nexport { getZodFunctionArgs, getZodFunctionReturns } from \"./zod\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EACL,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,kBAAkB,GAEnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAC","sourcesContent":["export { looksLikeJSONSchema, makeJsonSchemaPartial } from \"./json-schema\";\nexport {\n getParametersFromToolSchema,\n hasInputSchema,\n safeSchemaToJsonSchema,\n schemaToJsonSchema,\n type SupportedSchema,\n} from \"./schema\";\nexport { isStandardSchema } from \"./standard-schema\";\nexport { assertNoRecordSchema } from \"./validate\";\nexport { getZodFunctionArgs, getZodFunctionReturns } from \"./zod\";\n"]}
@@ -32,4 +32,11 @@ export declare function isJsonSchemaTuple(schema: JSONSchema7Extended): schema i
32
32
  * @returns Array of item schemas, or undefined if not a tuple
33
33
  */
34
34
  export declare function getJsonSchemaTupleItems(schema: JSONSchema7Extended): JSONSchema7[] | undefined;
35
+ /**
36
+ * Creates a partial version of a JSON Schema by removing required constraints.
37
+ * This allows LLM to provide only the properties it wants to update.
38
+ * @param schema - The JSON Schema to make partial
39
+ * @returns A new JSON Schema with the required constraint removed
40
+ */
41
+ export declare function makeJsonSchemaPartial(schema: JSONSchema7): JSONSchema7;
35
42
  //# sourceMappingURL=json-schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/schema/json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAkD1C;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,WAAW,CAMpE;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG;IAC9C,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,mBAAmB,GAC1B,MAAM,IAAI,mBAAmB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAgBnD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,mBAAmB,GAC1B,WAAW,EAAE,GAAG,SAAS,CAgB3B"}
1
+ {"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/schema/json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAkD1C;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,WAAW,CAMpE;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG;IAC9C,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,mBAAmB,GAC1B,MAAM,IAAI,mBAAmB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAgBnD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,mBAAmB,GAC1B,WAAW,EAAE,GAAG,SAAS,CAgB3B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAGtE"}
@@ -95,4 +95,14 @@ export function getJsonSchemaTupleItems(schema) {
95
95
  }
96
96
  return undefined;
97
97
  }
98
+ /**
99
+ * Creates a partial version of a JSON Schema by removing required constraints.
100
+ * This allows LLM to provide only the properties it wants to update.
101
+ * @param schema - The JSON Schema to make partial
102
+ * @returns A new JSON Schema with the required constraint removed
103
+ */
104
+ export function makeJsonSchemaPartial(schema) {
105
+ const { required: _required, ...rest } = schema;
106
+ return rest;
107
+ }
98
108
  //# sourceMappingURL=json-schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../src/schema/json-schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { JSONSchema7 } from \"json-schema\";\nimport { z } from \"./alias\";\n\nconst jsonSchemaType = z.union([\n z.literal(\"object\"),\n z.literal(\"array\"),\n z.literal(\"string\"),\n z.literal(\"number\"),\n z.literal(\"integer\"),\n z.literal(\"boolean\"),\n z.literal(\"null\"),\n z.array(\n z.union([\n z.literal(\"object\"),\n z.literal(\"array\"),\n z.literal(\"string\"),\n z.literal(\"number\"),\n z.literal(\"integer\"),\n z.literal(\"boolean\"),\n z.literal(\"null\"),\n ]),\n ),\n]);\n\nconst jsonSchemaTopLevel = z\n .looseObject({\n type: jsonSchemaType.optional(),\n properties: z.record(z.string(), z.unknown()).optional(),\n items: z.union([z.array(z.unknown()), z.unknown()]).optional(),\n required: z.array(z.string()).optional(),\n additionalProperties: z.union([z.boolean(), z.unknown()]).optional(),\n enum: z.array(z.unknown()).optional(),\n const: z.unknown().optional(),\n $ref: z.string().optional(),\n $id: z.string().optional(),\n $schema: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n })\n .refine((value) => {\n return (\n value.type !== undefined ||\n value.properties !== undefined ||\n value.items !== undefined ||\n value.enum !== undefined ||\n value.const !== undefined ||\n value.$ref !== undefined\n );\n });\n\n/**\n * Basic heuristic to check if an object looks like a JSON Schema at the top level.\n *\n * This uses a Zod schema to verify only top-level keys (type, properties, items,\n * etc.). It intentionally does not perform full JSON Schema validation; a more\n * thorough check should be done server-side.\n * @param obj - The value to check\n * @returns True if the value appears to be a JSON Schema\n */\nexport function looksLikeJSONSchema(obj: unknown): obj is JSONSchema7 {\n if (obj === null || typeof obj !== \"object\") {\n return false;\n }\n\n return jsonSchemaTopLevel.safeParse(obj).success;\n}\n\n/**\n * Extended JSON Schema type that includes draft 2020-12 features like prefixItems.\n * The json-schema package types are from draft-07 and don't include prefixItems.\n */\nexport type JSONSchema7Extended = JSONSchema7 & {\n prefixItems?: JSONSchema7[];\n};\n\n/**\n * Checks if a JSON Schema represents a tuple (array with positional items).\n * Supports both draft-07 (items as array) and draft 2020-12 (prefixItems).\n * @param schema - The JSON Schema to check\n * @returns True if the schema represents a tuple\n */\nexport function isJsonSchemaTuple(\n schema: JSONSchema7Extended,\n): schema is JSONSchema7Extended & { type: \"array\" } {\n if (schema.type !== \"array\") {\n return false;\n }\n\n // Draft 2020-12: prefixItems array\n if (schema.prefixItems && Array.isArray(schema.prefixItems)) {\n return true;\n }\n\n // Draft-07: items as array (not object)\n if (Array.isArray(schema.items)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Gets the tuple items from a JSON Schema.\n * Supports both draft-07 (items as array) and draft 2020-12 (prefixItems).\n * @param schema - The JSON Schema tuple\n * @returns Array of item schemas, or undefined if not a tuple\n */\nexport function getJsonSchemaTupleItems(\n schema: JSONSchema7Extended,\n): JSONSchema7[] | undefined {\n if (schema.type !== \"array\") {\n return undefined;\n }\n\n // Draft 2020-12: prefixItems array\n if (schema.prefixItems && Array.isArray(schema.prefixItems)) {\n return schema.prefixItems;\n }\n\n // Draft-07: items as array\n if (Array.isArray(schema.items)) {\n return schema.items as JSONSchema7[];\n }\n\n return undefined;\n}\n"]}
1
+ {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../src/schema/json-schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { JSONSchema7 } from \"json-schema\";\nimport { z } from \"./alias\";\n\nconst jsonSchemaType = z.union([\n z.literal(\"object\"),\n z.literal(\"array\"),\n z.literal(\"string\"),\n z.literal(\"number\"),\n z.literal(\"integer\"),\n z.literal(\"boolean\"),\n z.literal(\"null\"),\n z.array(\n z.union([\n z.literal(\"object\"),\n z.literal(\"array\"),\n z.literal(\"string\"),\n z.literal(\"number\"),\n z.literal(\"integer\"),\n z.literal(\"boolean\"),\n z.literal(\"null\"),\n ]),\n ),\n]);\n\nconst jsonSchemaTopLevel = z\n .looseObject({\n type: jsonSchemaType.optional(),\n properties: z.record(z.string(), z.unknown()).optional(),\n items: z.union([z.array(z.unknown()), z.unknown()]).optional(),\n required: z.array(z.string()).optional(),\n additionalProperties: z.union([z.boolean(), z.unknown()]).optional(),\n enum: z.array(z.unknown()).optional(),\n const: z.unknown().optional(),\n $ref: z.string().optional(),\n $id: z.string().optional(),\n $schema: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n })\n .refine((value) => {\n return (\n value.type !== undefined ||\n value.properties !== undefined ||\n value.items !== undefined ||\n value.enum !== undefined ||\n value.const !== undefined ||\n value.$ref !== undefined\n );\n });\n\n/**\n * Basic heuristic to check if an object looks like a JSON Schema at the top level.\n *\n * This uses a Zod schema to verify only top-level keys (type, properties, items,\n * etc.). It intentionally does not perform full JSON Schema validation; a more\n * thorough check should be done server-side.\n * @param obj - The value to check\n * @returns True if the value appears to be a JSON Schema\n */\nexport function looksLikeJSONSchema(obj: unknown): obj is JSONSchema7 {\n if (obj === null || typeof obj !== \"object\") {\n return false;\n }\n\n return jsonSchemaTopLevel.safeParse(obj).success;\n}\n\n/**\n * Extended JSON Schema type that includes draft 2020-12 features like prefixItems.\n * The json-schema package types are from draft-07 and don't include prefixItems.\n */\nexport type JSONSchema7Extended = JSONSchema7 & {\n prefixItems?: JSONSchema7[];\n};\n\n/**\n * Checks if a JSON Schema represents a tuple (array with positional items).\n * Supports both draft-07 (items as array) and draft 2020-12 (prefixItems).\n * @param schema - The JSON Schema to check\n * @returns True if the schema represents a tuple\n */\nexport function isJsonSchemaTuple(\n schema: JSONSchema7Extended,\n): schema is JSONSchema7Extended & { type: \"array\" } {\n if (schema.type !== \"array\") {\n return false;\n }\n\n // Draft 2020-12: prefixItems array\n if (schema.prefixItems && Array.isArray(schema.prefixItems)) {\n return true;\n }\n\n // Draft-07: items as array (not object)\n if (Array.isArray(schema.items)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Gets the tuple items from a JSON Schema.\n * Supports both draft-07 (items as array) and draft 2020-12 (prefixItems).\n * @param schema - The JSON Schema tuple\n * @returns Array of item schemas, or undefined if not a tuple\n */\nexport function getJsonSchemaTupleItems(\n schema: JSONSchema7Extended,\n): JSONSchema7[] | undefined {\n if (schema.type !== \"array\") {\n return undefined;\n }\n\n // Draft 2020-12: prefixItems array\n if (schema.prefixItems && Array.isArray(schema.prefixItems)) {\n return schema.prefixItems;\n }\n\n // Draft-07: items as array\n if (Array.isArray(schema.items)) {\n return schema.items as JSONSchema7[];\n }\n\n return undefined;\n}\n\n/**\n * Creates a partial version of a JSON Schema by removing required constraints.\n * This allows LLM to provide only the properties it wants to update.\n * @param schema - The JSON Schema to make partial\n * @returns A new JSON Schema with the required constraint removed\n */\nexport function makeJsonSchemaPartial(schema: JSONSchema7): JSONSchema7 {\n const { required: _required, ...rest } = schema;\n return rest;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=json-schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-schema.test.d.ts","sourceRoot":"","sources":["../../src/schema/json-schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,202 @@
1
+ import { getJsonSchemaTupleItems, isJsonSchemaTuple, looksLikeJSONSchema, makeJsonSchemaPartial, } from "./json-schema";
2
+ describe("looksLikeJSONSchema", () => {
3
+ it("should return true for a valid object schema", () => {
4
+ const schema = {
5
+ type: "object",
6
+ properties: {
7
+ name: { type: "string" },
8
+ },
9
+ };
10
+ expect(looksLikeJSONSchema(schema)).toBe(true);
11
+ });
12
+ it("should return true for a schema with only properties", () => {
13
+ const schema = {
14
+ properties: {
15
+ name: { type: "string" },
16
+ },
17
+ };
18
+ expect(looksLikeJSONSchema(schema)).toBe(true);
19
+ });
20
+ it("should return true for a schema with items (array)", () => {
21
+ const schema = {
22
+ type: "array",
23
+ items: { type: "string" },
24
+ };
25
+ expect(looksLikeJSONSchema(schema)).toBe(true);
26
+ });
27
+ it("should return true for a schema with enum", () => {
28
+ const schema = {
29
+ enum: ["a", "b", "c"],
30
+ };
31
+ expect(looksLikeJSONSchema(schema)).toBe(true);
32
+ });
33
+ it("should return true for a schema with const", () => {
34
+ const schema = {
35
+ const: "fixed-value",
36
+ };
37
+ expect(looksLikeJSONSchema(schema)).toBe(true);
38
+ });
39
+ it("should return true for a schema with $ref", () => {
40
+ const schema = {
41
+ $ref: "#/definitions/Person",
42
+ };
43
+ expect(looksLikeJSONSchema(schema)).toBe(true);
44
+ });
45
+ it("should return false for null", () => {
46
+ expect(looksLikeJSONSchema(null)).toBe(false);
47
+ });
48
+ it("should return false for non-object", () => {
49
+ expect(looksLikeJSONSchema("string")).toBe(false);
50
+ expect(looksLikeJSONSchema(123)).toBe(false);
51
+ expect(looksLikeJSONSchema(undefined)).toBe(false);
52
+ });
53
+ it("should return false for empty object", () => {
54
+ expect(looksLikeJSONSchema({})).toBe(false);
55
+ });
56
+ it("should return false for object without schema keys", () => {
57
+ const notSchema = {
58
+ foo: "bar",
59
+ baz: 123,
60
+ };
61
+ expect(looksLikeJSONSchema(notSchema)).toBe(false);
62
+ });
63
+ });
64
+ describe("isJsonSchemaTuple", () => {
65
+ it("should return true for draft-07 tuple (items as array)", () => {
66
+ const schema = {
67
+ type: "array",
68
+ items: [{ type: "string" }, { type: "number" }],
69
+ };
70
+ expect(isJsonSchemaTuple(schema)).toBe(true);
71
+ });
72
+ it("should return true for draft 2020-12 tuple (prefixItems)", () => {
73
+ const schema = {
74
+ type: "array",
75
+ prefixItems: [{ type: "string" }, { type: "number" }],
76
+ };
77
+ expect(isJsonSchemaTuple(schema)).toBe(true);
78
+ });
79
+ it("should return false for regular array schema (items as object)", () => {
80
+ const schema = {
81
+ type: "array",
82
+ items: { type: "string" },
83
+ };
84
+ expect(isJsonSchemaTuple(schema)).toBe(false);
85
+ });
86
+ it("should return false for non-array schema", () => {
87
+ const schema = {
88
+ type: "object",
89
+ properties: {},
90
+ };
91
+ expect(isJsonSchemaTuple(schema)).toBe(false);
92
+ });
93
+ it("should return false for array without items or prefixItems", () => {
94
+ const schema = {
95
+ type: "array",
96
+ };
97
+ expect(isJsonSchemaTuple(schema)).toBe(false);
98
+ });
99
+ });
100
+ describe("getJsonSchemaTupleItems", () => {
101
+ it("should return items for draft-07 tuple", () => {
102
+ const items = [{ type: "string" }, { type: "number" }];
103
+ const schema = {
104
+ type: "array",
105
+ items: items,
106
+ };
107
+ expect(getJsonSchemaTupleItems(schema)).toEqual(items);
108
+ });
109
+ it("should return prefixItems for draft 2020-12 tuple", () => {
110
+ const prefixItems = [{ type: "string" }, { type: "number" }];
111
+ const schema = {
112
+ type: "array",
113
+ prefixItems: prefixItems,
114
+ };
115
+ expect(getJsonSchemaTupleItems(schema)).toEqual(prefixItems);
116
+ });
117
+ it("should return undefined for non-array schema", () => {
118
+ const schema = {
119
+ type: "object",
120
+ };
121
+ expect(getJsonSchemaTupleItems(schema)).toBeUndefined();
122
+ });
123
+ it("should return undefined for array without tuple items", () => {
124
+ const schema = {
125
+ type: "array",
126
+ items: { type: "string" },
127
+ };
128
+ expect(getJsonSchemaTupleItems(schema)).toBeUndefined();
129
+ });
130
+ it("should prefer prefixItems over items when both present", () => {
131
+ const prefixItems = [{ type: "boolean" }];
132
+ const items = [{ type: "string" }];
133
+ const schema = {
134
+ type: "array",
135
+ prefixItems: prefixItems,
136
+ items: items,
137
+ };
138
+ expect(getJsonSchemaTupleItems(schema)).toEqual(prefixItems);
139
+ });
140
+ });
141
+ describe("makeJsonSchemaPartial", () => {
142
+ it("should remove required array from schema", () => {
143
+ const schema = {
144
+ type: "object",
145
+ properties: {
146
+ name: { type: "string" },
147
+ age: { type: "number" },
148
+ },
149
+ required: ["name", "age"],
150
+ };
151
+ const partial = makeJsonSchemaPartial(schema);
152
+ expect(partial.required).toBeUndefined();
153
+ expect(partial.type).toBe("object");
154
+ expect(partial.properties).toEqual(schema.properties);
155
+ });
156
+ it("should preserve all other properties", () => {
157
+ const schema = {
158
+ type: "object",
159
+ properties: {
160
+ name: { type: "string", description: "The name" },
161
+ },
162
+ required: ["name"],
163
+ additionalProperties: false,
164
+ description: "A person schema",
165
+ title: "Person",
166
+ };
167
+ const partial = makeJsonSchemaPartial(schema);
168
+ expect(partial.required).toBeUndefined();
169
+ expect(partial.type).toBe("object");
170
+ expect(partial.properties).toEqual(schema.properties);
171
+ expect(partial.additionalProperties).toBe(false);
172
+ expect(partial.description).toBe("A person schema");
173
+ expect(partial.title).toBe("Person");
174
+ });
175
+ it("should handle schema without required array", () => {
176
+ const schema = {
177
+ type: "object",
178
+ properties: {
179
+ name: { type: "string" },
180
+ },
181
+ };
182
+ const partial = makeJsonSchemaPartial(schema);
183
+ expect(partial.required).toBeUndefined();
184
+ expect(partial.type).toBe("object");
185
+ expect(partial.properties).toEqual(schema.properties);
186
+ });
187
+ it("should not mutate the original schema", () => {
188
+ const schema = {
189
+ type: "object",
190
+ properties: {
191
+ name: { type: "string" },
192
+ },
193
+ required: ["name"],
194
+ };
195
+ const partial = makeJsonSchemaPartial(schema);
196
+ // Original should be unchanged
197
+ expect(schema.required).toEqual(["name"]);
198
+ // Partial should not have required
199
+ expect(partial.required).toBeUndefined();
200
+ });
201
+ });
202
+ //# sourceMappingURL=json-schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-schema.test.js","sourceRoot":"","sources":["../../src/schema/json-schema.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { JSONSchema7 } from \"json-schema\";\nimport {\n getJsonSchemaTupleItems,\n isJsonSchemaTuple,\n JSONSchema7Extended,\n looksLikeJSONSchema,\n makeJsonSchemaPartial,\n} from \"./json-schema\";\n\ndescribe(\"looksLikeJSONSchema\", () => {\n it(\"should return true for a valid object schema\", () => {\n const schema = {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n },\n };\n expect(looksLikeJSONSchema(schema)).toBe(true);\n });\n\n it(\"should return true for a schema with only properties\", () => {\n const schema = {\n properties: {\n name: { type: \"string\" },\n },\n };\n expect(looksLikeJSONSchema(schema)).toBe(true);\n });\n\n it(\"should return true for a schema with items (array)\", () => {\n const schema = {\n type: \"array\",\n items: { type: \"string\" },\n };\n expect(looksLikeJSONSchema(schema)).toBe(true);\n });\n\n it(\"should return true for a schema with enum\", () => {\n const schema = {\n enum: [\"a\", \"b\", \"c\"],\n };\n expect(looksLikeJSONSchema(schema)).toBe(true);\n });\n\n it(\"should return true for a schema with const\", () => {\n const schema = {\n const: \"fixed-value\",\n };\n expect(looksLikeJSONSchema(schema)).toBe(true);\n });\n\n it(\"should return true for a schema with $ref\", () => {\n const schema = {\n $ref: \"#/definitions/Person\",\n };\n expect(looksLikeJSONSchema(schema)).toBe(true);\n });\n\n it(\"should return false for null\", () => {\n expect(looksLikeJSONSchema(null)).toBe(false);\n });\n\n it(\"should return false for non-object\", () => {\n expect(looksLikeJSONSchema(\"string\")).toBe(false);\n expect(looksLikeJSONSchema(123)).toBe(false);\n expect(looksLikeJSONSchema(undefined)).toBe(false);\n });\n\n it(\"should return false for empty object\", () => {\n expect(looksLikeJSONSchema({})).toBe(false);\n });\n\n it(\"should return false for object without schema keys\", () => {\n const notSchema = {\n foo: \"bar\",\n baz: 123,\n };\n expect(looksLikeJSONSchema(notSchema)).toBe(false);\n });\n});\n\ndescribe(\"isJsonSchemaTuple\", () => {\n it(\"should return true for draft-07 tuple (items as array)\", () => {\n const schema: JSONSchema7Extended = {\n type: \"array\",\n items: [{ type: \"string\" }, { type: \"number\" }],\n };\n expect(isJsonSchemaTuple(schema)).toBe(true);\n });\n\n it(\"should return true for draft 2020-12 tuple (prefixItems)\", () => {\n const schema: JSONSchema7Extended = {\n type: \"array\",\n prefixItems: [{ type: \"string\" }, { type: \"number\" }],\n };\n expect(isJsonSchemaTuple(schema)).toBe(true);\n });\n\n it(\"should return false for regular array schema (items as object)\", () => {\n const schema: JSONSchema7Extended = {\n type: \"array\",\n items: { type: \"string\" },\n };\n expect(isJsonSchemaTuple(schema)).toBe(false);\n });\n\n it(\"should return false for non-array schema\", () => {\n const schema: JSONSchema7Extended = {\n type: \"object\",\n properties: {},\n };\n expect(isJsonSchemaTuple(schema)).toBe(false);\n });\n\n it(\"should return false for array without items or prefixItems\", () => {\n const schema: JSONSchema7Extended = {\n type: \"array\",\n };\n expect(isJsonSchemaTuple(schema)).toBe(false);\n });\n});\n\ndescribe(\"getJsonSchemaTupleItems\", () => {\n it(\"should return items for draft-07 tuple\", () => {\n const items = [{ type: \"string\" }, { type: \"number\" }];\n const schema: JSONSchema7Extended = {\n type: \"array\",\n items: items as JSONSchema7[],\n };\n expect(getJsonSchemaTupleItems(schema)).toEqual(items);\n });\n\n it(\"should return prefixItems for draft 2020-12 tuple\", () => {\n const prefixItems = [{ type: \"string\" }, { type: \"number\" }];\n const schema: JSONSchema7Extended = {\n type: \"array\",\n prefixItems: prefixItems as JSONSchema7[],\n };\n expect(getJsonSchemaTupleItems(schema)).toEqual(prefixItems);\n });\n\n it(\"should return undefined for non-array schema\", () => {\n const schema: JSONSchema7Extended = {\n type: \"object\",\n };\n expect(getJsonSchemaTupleItems(schema)).toBeUndefined();\n });\n\n it(\"should return undefined for array without tuple items\", () => {\n const schema: JSONSchema7Extended = {\n type: \"array\",\n items: { type: \"string\" },\n };\n expect(getJsonSchemaTupleItems(schema)).toBeUndefined();\n });\n\n it(\"should prefer prefixItems over items when both present\", () => {\n const prefixItems = [{ type: \"boolean\" }];\n const items = [{ type: \"string\" }];\n const schema: JSONSchema7Extended = {\n type: \"array\",\n prefixItems: prefixItems as JSONSchema7[],\n items: items as JSONSchema7[],\n };\n expect(getJsonSchemaTupleItems(schema)).toEqual(prefixItems);\n });\n});\n\ndescribe(\"makeJsonSchemaPartial\", () => {\n it(\"should remove required array from schema\", () => {\n const schema: JSONSchema7 = {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n age: { type: \"number\" },\n },\n required: [\"name\", \"age\"],\n };\n\n const partial = makeJsonSchemaPartial(schema);\n\n expect(partial.required).toBeUndefined();\n expect(partial.type).toBe(\"object\");\n expect(partial.properties).toEqual(schema.properties);\n });\n\n it(\"should preserve all other properties\", () => {\n const schema: JSONSchema7 = {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"The name\" },\n },\n required: [\"name\"],\n additionalProperties: false,\n description: \"A person schema\",\n title: \"Person\",\n };\n\n const partial = makeJsonSchemaPartial(schema);\n\n expect(partial.required).toBeUndefined();\n expect(partial.type).toBe(\"object\");\n expect(partial.properties).toEqual(schema.properties);\n expect(partial.additionalProperties).toBe(false);\n expect(partial.description).toBe(\"A person schema\");\n expect(partial.title).toBe(\"Person\");\n });\n\n it(\"should handle schema without required array\", () => {\n const schema: JSONSchema7 = {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n },\n };\n\n const partial = makeJsonSchemaPartial(schema);\n\n expect(partial.required).toBeUndefined();\n expect(partial.type).toBe(\"object\");\n expect(partial.properties).toEqual(schema.properties);\n });\n\n it(\"should not mutate the original schema\", () => {\n const schema: JSONSchema7 = {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n },\n required: [\"name\"],\n };\n\n const partial = makeJsonSchemaPartial(schema);\n\n // Original should be unchanged\n expect(schema.required).toEqual([\"name\"]);\n // Partial should not have required\n expect(partial.required).toBeUndefined();\n });\n});\n"]}
package/esm/setupTests.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import "@testing-library/jest-dom";
2
2
  import { TextDecoder, TextEncoder } from "util";
3
+ import { TransformStream, ReadableStream, WritableStream } from "stream/web";
3
4
  // Mock Date.now() to return a fixed timestamp for consistent testing
4
5
  const mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);
5
6
  global.Date.now = jest.fn(() => mockDate.getTime());
6
7
  // Add TextEncoder/TextDecoder polyfills for Node.js test environment
7
8
  Object.assign(global, { TextEncoder, TextDecoder });
9
+ // Add Web Streams API polyfills for jsdom environment (used by @modelcontextprotocol/sdk)
10
+ Object.assign(global, { TransformStream, ReadableStream, WritableStream });
8
11
  // Mock react-media-recorder for tests (browser APIs not available in Node.js)
9
12
  jest.mock("react-media-recorder", () => ({
10
13
  useReactMediaRecorder: () => ({
@@ -1 +1 @@
1
- {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAEhD,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;AAEpD,qEAAqE;AACrE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;AAEpD,8EAA8E;AAC9E,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5B,MAAM,EAAE,MAAM;QACd,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;QACzB,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE;QACxB,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,IAAI;KACZ,CAAC;CACH,CAAC,CAAC,CAAC","sourcesContent":["import \"@testing-library/jest-dom\";\nimport { TextDecoder, TextEncoder } from \"util\";\n\n// Mock Date.now() to return a fixed timestamp for consistent testing\nconst mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);\nglobal.Date.now = jest.fn(() => mockDate.getTime());\n\n// Add TextEncoder/TextDecoder polyfills for Node.js test environment\nObject.assign(global, { TextEncoder, TextDecoder });\n\n// Mock react-media-recorder for tests (browser APIs not available in Node.js)\njest.mock(\"react-media-recorder\", () => ({\n useReactMediaRecorder: () => ({\n status: \"idle\",\n startRecording: jest.fn(),\n stopRecording: jest.fn(),\n mediaBlobUrl: null,\n error: null,\n }),\n}));\n"]}
1
+ {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7E,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;AAEpD,qEAAqE;AACrE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;AAEpD,0FAA0F;AAC1F,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC,CAAC;AAE3E,8EAA8E;AAC9E,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5B,MAAM,EAAE,MAAM;QACd,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;QACzB,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE;QACxB,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,IAAI;KACZ,CAAC;CACH,CAAC,CAAC,CAAC","sourcesContent":["import \"@testing-library/jest-dom\";\nimport { TextDecoder, TextEncoder } from \"util\";\nimport { TransformStream, ReadableStream, WritableStream } from \"stream/web\";\n\n// Mock Date.now() to return a fixed timestamp for consistent testing\nconst mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);\nglobal.Date.now = jest.fn(() => mockDate.getTime());\n\n// Add TextEncoder/TextDecoder polyfills for Node.js test environment\nObject.assign(global, { TextEncoder, TextDecoder });\n\n// Add Web Streams API polyfills for jsdom environment (used by @modelcontextprotocol/sdk)\nObject.assign(global, { TransformStream, ReadableStream, WritableStream });\n\n// Mock react-media-recorder for tests (browser APIs not available in Node.js)\njest.mock(\"react-media-recorder\", () => ({\n useReactMediaRecorder: () => ({\n status: \"idle\",\n startRecording: jest.fn(),\n stopRecording: jest.fn(),\n mediaBlobUrl: null,\n error: null,\n }),\n}));\n"]}
@@ -1,3 +1,4 @@
1
+ import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
1
2
  import type TamboAI from "@tambo-ai/typescript-sdk";
2
3
  import { StagedImage } from "../hooks/use-message-images";
3
4
  /**
@@ -5,7 +6,8 @@ import { StagedImage } from "../hooks/use-message-images";
5
6
  * @param text - The text content, may include \@serverKey:uri resource references
6
7
  * @param images - Array of staged images
7
8
  * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names
9
+ * @param resourceContent - Optional map of prefixed URIs to resolved content (for client-side resources)
8
10
  * @returns Array of message content parts
9
11
  */
10
- export declare function buildMessageContent(text: string, images: StagedImage[], resourceNames?: Record<string, string>): TamboAI.Beta.Threads.ChatCompletionContentPart[];
12
+ export declare function buildMessageContent(text: string, images: StagedImage[], resourceNames?: Record<string, string>, resourceContent?: Map<string, ReadResourceResult>): TamboAI.Beta.Threads.ChatCompletionContentPart[];
11
13
  //# sourceMappingURL=message-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"message-builder.d.ts","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAsF1D;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EAAE,EACrB,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACzC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CA4BlD"}
1
+ {"version":3,"file":"message-builder.d.ts","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAwG1D;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EAAE,EACrB,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAC1C,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CA4BlD"}
@@ -22,9 +22,10 @@ const RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\S+)/g;
22
22
  * - The backend routes resources based on the thread's MCP server configuration, not client-side keys
23
23
  * @param text - Text potentially containing resource references
24
24
  * @param resourceNames - Map of full resource IDs (serverKey:uri) to their display names
25
+ * @param resourceContent - Optional map of prefixed URIs to resolved content (for client-side resources)
25
26
  * @returns Array of content parts in order (text and resource parts interleaved)
26
27
  */
27
- function parseResourceReferences(text, resourceNames) {
28
+ function parseResourceReferences(text, resourceNames, resourceContent) {
28
29
  const parts = [];
29
30
  // Use matchAll to avoid global regex state issues
30
31
  const matches = Array.from(text.matchAll(RESOURCE_REFERENCE_PATTERN));
@@ -48,6 +49,21 @@ function parseResourceReferences(text, resourceNames) {
48
49
  if (name) {
49
50
  resource.name = name;
50
51
  }
52
+ // Include resolved content for client-side resources (MCP and registry)
53
+ // Server-side resources won't be in the map - backend resolves them by URI
54
+ const resolvedContent = resourceContent?.get(fullId);
55
+ if (resolvedContent?.contents?.[0]) {
56
+ const content = resolvedContent.contents[0];
57
+ if ("text" in content && content.text) {
58
+ resource.text = content.text;
59
+ }
60
+ else if ("blob" in content && content.blob) {
61
+ resource.blob = content.blob;
62
+ }
63
+ if ("mimeType" in content && content.mimeType) {
64
+ resource.mimeType = content.mimeType;
65
+ }
66
+ }
51
67
  parts.push({ type: "resource", resource });
52
68
  if (match.index !== undefined) {
53
69
  lastIndex = match.index + fullMatch.length;
@@ -74,16 +90,17 @@ function parseResourceReferences(text, resourceNames) {
74
90
  * @param text - The text content, may include \@serverKey:uri resource references
75
91
  * @param images - Array of staged images
76
92
  * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names
93
+ * @param resourceContent - Optional map of prefixed URIs to resolved content (for client-side resources)
77
94
  * @returns Array of message content parts
78
95
  */
79
- export function buildMessageContent(text, images, resourceNames = {}) {
96
+ export function buildMessageContent(text, images, resourceNames = {}, resourceContent) {
80
97
  const content = [];
81
98
  const hasNonWhitespaceText = text.trim().length > 0;
82
99
  if (hasNonWhitespaceText) {
83
100
  // Parse resource references from the original text so that all
84
101
  // user-visible whitespace (including leading/trailing spaces and
85
102
  // internal spacing) is preserved in the resulting content parts.
86
- const parts = parseResourceReferences(text, resourceNames);
103
+ const parts = parseResourceReferences(text, resourceNames, resourceContent);
87
104
  content.push(...parts);
88
105
  }
89
106
  // Add images at the end
@@ -1 +1 @@
1
- {"version":3,"file":"message-builder.js","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { StagedImage } from \"../hooks/use-message-images\";\n\n/**\n * Regular expression to match MCP resource references in the format: \\@serverKey:uri\n *\n * Examples:\n * - \\@tambo-1hfs429:tambo:test://static/resource/1\n * - \\@linear:file://path/to/file\n *\n * Pattern breakdown:\n * - \\@ - Literal \\@ symbol\n * - ([a-zA-Z0-9-]+) - Server key (alphanumeric + hyphens, client-side routing key)\n * - : - Literal colon separator\n * - (\\S+) - URI (non-whitespace characters, actual resource URI)\n */\nconst RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\\S+)/g;\n\n/**\n * Parses text with resource references and returns interleaved content parts.\n * Resource references have the format: \\@serverKey:uri\n *\n * The serverKey prefix is stripped before sending to the backend because:\n * - It's a client-side routing key (e.g., \"tambo-1hfs429\") used by React SDK to route to the correct MCP connection\n * - The backend only needs the actual resource URI (e.g., \"tambo:test://static/resource/1\")\n * - The backend routes resources based on the thread's MCP server configuration, not client-side keys\n * @param text - Text potentially containing resource references\n * @param resourceNames - Map of full resource IDs (serverKey:uri) to their display names\n * @returns Array of content parts in order (text and resource parts interleaved)\n */\nfunction parseResourceReferences(\n text: string,\n resourceNames: Record<string, string>,\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const parts: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n // Use matchAll to avoid global regex state issues\n const matches = Array.from(text.matchAll(RESOURCE_REFERENCE_PATTERN));\n let lastIndex = 0;\n\n // Find all resource references and interleave with text\n for (const match of matches) {\n const [fullMatch, serverKey, uri] = match;\n const fullId = `${serverKey}:${uri}`;\n\n // Add text before this resource reference (preserve whitespace)\n if (match.index !== undefined && match.index > lastIndex) {\n const textBefore = text.slice(lastIndex, match.index);\n if (textBefore.length > 0) {\n parts.push({\n type: \"text\",\n text: textBefore,\n });\n }\n }\n\n const resource: TamboAI.Resource = { uri };\n const name = resourceNames[fullId];\n if (name) {\n resource.name = name;\n }\n parts.push({ type: \"resource\", resource });\n\n if (match.index !== undefined) {\n lastIndex = match.index + fullMatch.length;\n }\n }\n\n // Add remaining text after the last resource reference (preserve whitespace)\n if (lastIndex < text.length) {\n const textAfter = text.slice(lastIndex);\n if (textAfter.length > 0) {\n parts.push({\n type: \"text\",\n text: textAfter,\n });\n }\n }\n\n // If no resource references were found, return the whole text as a single text part\n if (parts.length === 0 && text.trim()) {\n parts.push({ type: \"text\", text });\n }\n\n return parts;\n}\n\n/**\n * Builds message content with text, MCP resource references, and images\n * @param text - The text content, may include \\@serverKey:uri resource references\n * @param images - Array of staged images\n * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names\n * @returns Array of message content parts\n */\nexport function buildMessageContent(\n text: string,\n images: StagedImage[],\n resourceNames: Record<string, string> = {},\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const content: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n const hasNonWhitespaceText = text.trim().length > 0;\n\n if (hasNonWhitespaceText) {\n // Parse resource references from the original text so that all\n // user-visible whitespace (including leading/trailing spaces and\n // internal spacing) is preserved in the resulting content parts.\n const parts = parseResourceReferences(text, resourceNames);\n content.push(...parts);\n }\n\n // Add images at the end\n for (const image of images) {\n content.push({\n type: \"image_url\",\n image_url: {\n url: image.dataUrl,\n },\n });\n }\n\n if (content.length === 0) {\n throw new Error(\"Message must contain text or images\");\n }\n\n return content;\n}\n"]}
1
+ {"version":3,"file":"message-builder.js","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReadResourceResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { StagedImage } from \"../hooks/use-message-images\";\n\n/**\n * Regular expression to match MCP resource references in the format: \\@serverKey:uri\n *\n * Examples:\n * - \\@tambo-1hfs429:tambo:test://static/resource/1\n * - \\@linear:file://path/to/file\n *\n * Pattern breakdown:\n * - \\@ - Literal \\@ symbol\n * - ([a-zA-Z0-9-]+) - Server key (alphanumeric + hyphens, client-side routing key)\n * - : - Literal colon separator\n * - (\\S+) - URI (non-whitespace characters, actual resource URI)\n */\nconst RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\\S+)/g;\n\n/**\n * Parses text with resource references and returns interleaved content parts.\n * Resource references have the format: \\@serverKey:uri\n *\n * The serverKey prefix is stripped before sending to the backend because:\n * - It's a client-side routing key (e.g., \"tambo-1hfs429\") used by React SDK to route to the correct MCP connection\n * - The backend only needs the actual resource URI (e.g., \"tambo:test://static/resource/1\")\n * - The backend routes resources based on the thread's MCP server configuration, not client-side keys\n * @param text - Text potentially containing resource references\n * @param resourceNames - Map of full resource IDs (serverKey:uri) to their display names\n * @param resourceContent - Optional map of prefixed URIs to resolved content (for client-side resources)\n * @returns Array of content parts in order (text and resource parts interleaved)\n */\nfunction parseResourceReferences(\n text: string,\n resourceNames: Record<string, string>,\n resourceContent?: Map<string, ReadResourceResult>,\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const parts: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n // Use matchAll to avoid global regex state issues\n const matches = Array.from(text.matchAll(RESOURCE_REFERENCE_PATTERN));\n let lastIndex = 0;\n\n // Find all resource references and interleave with text\n for (const match of matches) {\n const [fullMatch, serverKey, uri] = match;\n const fullId = `${serverKey}:${uri}`;\n\n // Add text before this resource reference (preserve whitespace)\n if (match.index !== undefined && match.index > lastIndex) {\n const textBefore = text.slice(lastIndex, match.index);\n if (textBefore.length > 0) {\n parts.push({\n type: \"text\",\n text: textBefore,\n });\n }\n }\n\n const resource: TamboAI.Resource = { uri };\n const name = resourceNames[fullId];\n if (name) {\n resource.name = name;\n }\n\n // Include resolved content for client-side resources (MCP and registry)\n // Server-side resources won't be in the map - backend resolves them by URI\n const resolvedContent = resourceContent?.get(fullId);\n if (resolvedContent?.contents?.[0]) {\n const content = resolvedContent.contents[0];\n if (\"text\" in content && content.text) {\n resource.text = content.text;\n } else if (\"blob\" in content && content.blob) {\n resource.blob = content.blob;\n }\n if (\"mimeType\" in content && content.mimeType) {\n resource.mimeType = content.mimeType;\n }\n }\n\n parts.push({ type: \"resource\", resource });\n\n if (match.index !== undefined) {\n lastIndex = match.index + fullMatch.length;\n }\n }\n\n // Add remaining text after the last resource reference (preserve whitespace)\n if (lastIndex < text.length) {\n const textAfter = text.slice(lastIndex);\n if (textAfter.length > 0) {\n parts.push({\n type: \"text\",\n text: textAfter,\n });\n }\n }\n\n // If no resource references were found, return the whole text as a single text part\n if (parts.length === 0 && text.trim()) {\n parts.push({ type: \"text\", text });\n }\n\n return parts;\n}\n\n/**\n * Builds message content with text, MCP resource references, and images\n * @param text - The text content, may include \\@serverKey:uri resource references\n * @param images - Array of staged images\n * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names\n * @param resourceContent - Optional map of prefixed URIs to resolved content (for client-side resources)\n * @returns Array of message content parts\n */\nexport function buildMessageContent(\n text: string,\n images: StagedImage[],\n resourceNames: Record<string, string> = {},\n resourceContent?: Map<string, ReadResourceResult>,\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const content: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n const hasNonWhitespaceText = text.trim().length > 0;\n\n if (hasNonWhitespaceText) {\n // Parse resource references from the original text so that all\n // user-visible whitespace (including leading/trailing spaces and\n // internal spacing) is preserved in the resulting content parts.\n const parts = parseResourceReferences(text, resourceNames, resourceContent);\n content.push(...parts);\n }\n\n // Add images at the end\n for (const image of images) {\n content.push({\n type: \"image_url\",\n image_url: {\n url: image.dataUrl,\n },\n });\n }\n\n if (content.length === 0) {\n throw new Error(\"Message must contain text or images\");\n }\n\n return content;\n}\n"]}