Repository: weaviate/agents-typescript-client
Pull request #1: Query agent
Author: login: augustas1
Created: 2025-04-29T08:06:38Z | Merged: 2025-05-06T09:45:12Z

Initial Query Agent pull request.

Depends on https://github.com/weaviate/typescript-client/pull/292.

Has `weaviate-client` as a peer dependency and can be consumed like this:
```shell
# once published
npm install weaviate-client @weaviate/agents
```
```ts
import { connectToWeaviateCloud } from "weaviate-client";
import { QueryAgent } from "@weaviate/agents";

const client = await connectToWeaviateCloud("cluster-url", { authCredentials: "api-key" });
const queryAgent = new QueryAgent(client, ["Emails"]);

const response = await queryAgent.run("What is a common topic in the emails?");
response.display();
```


Diff:
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..76add87
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+dist
\ No newline at end of file
diff --git a/package.json b/package.json
index 2ddb5ec..41c80fb 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,21 @@
 {
-  "name": "@weaviate/agents-client",
+  "name": "@weaviate/agents",
   "version": "0.0.1",
   "description": "JS/TS client for Weaviate Agents",
-  "scripts": {},
+  "exports": "./dist/index.js",
+  "type": "module",
+  "scripts": {
+    "build": "tsc"
+  },
   "keywords": [
     "weaviate"
   ],
   "author": "Weaviate",
-  "license": "SEE LICENSE IN LICENSE"
+  "license": "SEE LICENSE IN LICENSE",
+  "peerDependencies": {
+    "weaviate-client": "^3.5.2"
+  },
+  "devDependencies": {
+    "typescript": "^5.8.3"
+  }
 }
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..a8ff365
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,393 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      weaviate-client:
+        specifier: ^3.5.2
+        version: 3.5.2
+    devDependencies:
+      typescript:
+        specifier: ^5.8.3
+        version: 5.8.3
+
+packages:
+
+  '@graphql-typed-document-node/core@3.2.0':
+    resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
+    peerDependencies:
+      graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@grpc/grpc-js@1.13.3':
+    resolution: {integrity: sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==}
+    engines: {node: '>=12.10.0'}
+
+  '@grpc/proto-loader@0.7.15':
+    resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  '@js-sdsl/ordered-map@4.4.2':
+    resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
+
+  '@protobufjs/aspromise@1.1.2':
+    resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
+
+  '@protobufjs/base64@1.1.2':
+    resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
+
+  '@protobufjs/codegen@2.0.4':
+    resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
+
+  '@protobufjs/eventemitter@1.1.0':
+    resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
+
+  '@protobufjs/fetch@1.1.0':
+    resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
+
+  '@protobufjs/float@1.0.2':
+    resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
+
+  '@protobufjs/inquire@1.1.0':
+    resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
+
+  '@protobufjs/path@1.1.2':
+    resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
+
+  '@protobufjs/pool@1.1.0':
+    resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
+
+  '@protobufjs/utf8@1.1.0':
+    resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
+
+  '@types/node@22.15.3':
+    resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==}
+
+  abort-controller-x@0.4.3:
+    resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==}
+
+  ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  cliui@8.0.1:
+    resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+    engines: {node: '>=12'}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  cross-fetch@3.2.0:
+    resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
+
+  emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+
+  graphql-request@6.1.0:
+    resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==}
+    peerDependencies:
+      graphql: 14 - 16
+
+  graphql@16.11.0:
+    resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==}
+    engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
+
+  is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+
+  lodash.camelcase@4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+  long@5.3.2:
+    resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
+
+  nice-grpc-client-middleware-retry@3.1.11:
+    resolution: {integrity: sha512-xW/imz/kNG2g0DwTfH2eYEGrg1chSLrXtvGp9fg2qkhTgGFfAS/Pq3+t+9G8KThcC4hK/xlEyKvZWKk++33S6A==}
+
+  nice-grpc-common@2.0.2:
+    resolution: {integrity: sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==}
+
+  nice-grpc@2.1.12:
+    resolution: {integrity: sha512-J1n4Wg+D3IhRhGQb+iqh2OpiM0GzTve/kf2lnlW4S+xczmIEd0aHUDV1OsJ5a3q8GSTqJf+s4Rgg1M8uJltarw==}
+
+  node-fetch@2.7.0:
+    resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+
+  protobufjs@7.5.0:
+    resolution: {integrity: sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==}
+    engines: {node: '>=12.0.0'}
+
+  require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+
+  string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+
+  strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+
+  tr46@0.0.3:
+    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+  ts-error@1.0.6:
+    resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==}
+
+  typescript@5.8.3:
+    resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  undici-types@6.21.0:
+    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+  uuid@9.0.1:
+    resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+    hasBin: true
+
+  weaviate-client@3.5.2:
+    resolution: {integrity: sha512-FpBdljOkVqSBUk7rYxEGdeQ0NmvV7p5skZ8Hc5MInThQSGlKLr+TltR/b8Ivtd8JkSErwVvmyriXfBK5ubNiXg==}
+    engines: {node: '>=18.0.0'}
+
+  webidl-conversions@3.0.1:
+    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+  whatwg-url@5.0.0:
+    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+  wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+
+  y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+
+  yargs-parser@21.1.1:
+    resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+    engines: {node: '>=12'}
+
+  yargs@17.7.2:
+    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+    engines: {node: '>=12'}
+
+snapshots:
+
+  '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)':
+    dependencies:
+      graphql: 16.11.0
+
+  '@grpc/grpc-js@1.13.3':
+    dependencies:
+      '@grpc/proto-loader': 0.7.15
+      '@js-sdsl/ordered-map': 4.4.2
+
+  '@grpc/proto-loader@0.7.15':
+    dependencies:
+      lodash.camelcase: 4.3.0
+      long: 5.3.2
+      protobufjs: 7.5.0
+      yargs: 17.7.2
+
+  '@js-sdsl/ordered-map@4.4.2': {}
+
+  '@protobufjs/aspromise@1.1.2': {}
+
+  '@protobufjs/base64@1.1.2': {}
+
+  '@protobufjs/codegen@2.0.4': {}
+
+  '@protobufjs/eventemitter@1.1.0': {}
+
+  '@protobufjs/fetch@1.1.0':
+    dependencies:
+      '@protobufjs/aspromise': 1.1.2
+      '@protobufjs/inquire': 1.1.0
+
+  '@protobufjs/float@1.0.2': {}
+
+  '@protobufjs/inquire@1.1.0': {}
+
+  '@protobufjs/path@1.1.2': {}
+
+  '@protobufjs/pool@1.1.0': {}
+
+  '@protobufjs/utf8@1.1.0': {}
+
+  '@types/node@22.15.3':
+    dependencies:
+      undici-types: 6.21.0
+
+  abort-controller-x@0.4.3: {}
+
+  ansi-regex@5.0.1: {}
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  cliui@8.0.1:
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.4: {}
+
+  cross-fetch@3.2.0:
+    dependencies:
+      node-fetch: 2.7.0
+    transitivePeerDependencies:
+      - encoding
+
+  emoji-regex@8.0.0: {}
+
+  escalade@3.2.0: {}
+
+  get-caller-file@2.0.5: {}
+
+  graphql-request@6.1.0(graphql@16.11.0):
+    dependencies:
+      '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0)
+      cross-fetch: 3.2.0
+      graphql: 16.11.0
+    transitivePeerDependencies:
+      - encoding
+
+  graphql@16.11.0: {}
+
+  is-fullwidth-code-point@3.0.0: {}
+
+  lodash.camelcase@4.3.0: {}
+
+  long@5.3.2: {}
+
+  nice-grpc-client-middleware-retry@3.1.11:
+    dependencies:
+      abort-controller-x: 0.4.3
+      nice-grpc-common: 2.0.2
+
+  nice-grpc-common@2.0.2:
+    dependencies:
+      ts-error: 1.0.6
+
+  nice-grpc@2.1.12:
+    dependencies:
+      '@grpc/grpc-js': 1.13.3
+      abort-controller-x: 0.4.3
+      nice-grpc-common: 2.0.2
+
+  node-fetch@2.7.0:
+    dependencies:
+      whatwg-url: 5.0.0
+
+  protobufjs@7.5.0:
+    dependencies:
+      '@protobufjs/aspromise': 1.1.2
+      '@protobufjs/base64': 1.1.2
+      '@protobufjs/codegen': 2.0.4
+      '@protobufjs/eventemitter': 1.1.0
+      '@protobufjs/fetch': 1.1.0
+      '@protobufjs/float': 1.0.2
+      '@protobufjs/inquire': 1.1.0
+      '@protobufjs/path': 1.1.2
+      '@protobufjs/pool': 1.1.0
+      '@protobufjs/utf8': 1.1.0
+      '@types/node': 22.15.3
+      long: 5.3.2
+
+  require-directory@2.1.1: {}
+
+  string-width@4.2.3:
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+
+  strip-ansi@6.0.1:
+    dependencies:
+      ansi-regex: 5.0.1
+
+  tr46@0.0.3: {}
+
+  ts-error@1.0.6: {}
+
+  typescript@5.8.3: {}
+
+  undici-types@6.21.0: {}
+
+  uuid@9.0.1: {}
+
+  weaviate-client@3.5.2:
+    dependencies:
+      abort-controller-x: 0.4.3
+      graphql: 16.11.0
+      graphql-request: 6.1.0(graphql@16.11.0)
+      long: 5.3.2
+      nice-grpc: 2.1.12
+      nice-grpc-client-middleware-retry: 3.1.11
+      nice-grpc-common: 2.0.2
+      uuid: 9.0.1
+    transitivePeerDependencies:
+      - encoding
+
+  webidl-conversions@3.0.1: {}
+
+  whatwg-url@5.0.0:
+    dependencies:
+      tr46: 0.0.3
+      webidl-conversions: 3.0.1
+
+  wrap-ansi@7.0.0:
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+
+  y18n@5.0.8: {}
+
+  yargs-parser@21.1.1: {}
+
+  yargs@17.7.2:
+    dependencies:
+      cliui: 8.0.1
+      escalade: 3.2.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 21.1.1
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..3fa76c2
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1 @@
+export * from "./query/index.js";
diff --git a/src/query/agent.ts b/src/query/agent.ts
new file mode 100644
index 0000000..ee04c2e
--- /dev/null
+++ b/src/query/agent.ts
@@ -0,0 +1,61 @@
+import { WeaviateClient } from "weaviate-client";
+import { QueryAgentResponse } from "./response/response.js";
+import { mapResponse } from "./response/response-mapping.js";
+
+export class QueryAgent {
+  private systemPrompt?: string;
+  private agentsHost: string;
+
+  constructor(
+    private client: WeaviateClient,
+    private collections: string[],
+    {
+      systemPrompt,
+      agentsHost = "https://api.agents.weaviate.io",
+    }: QueryAgentOptions = {}
+  ) {
+    this.systemPrompt = systemPrompt;
+    this.agentsHost = agentsHost;
+  }
+
+  async run(
+    query: string,
+    { viewProperties }: QueryAgentRunOptions = {}
+  ): Promise<QueryAgentResponse> {
+    const { host, bearerToken, headers } =
+      await this.client.getConnectionDetails();
+
+    const response = await fetch(`${this.agentsHost}/agent/query`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: bearerToken!,
+        "X-Weaviate-Cluster-Url": host,
+      },
+      body: JSON.stringify({
+        headers,
+        query,
+        collection_names: this.collections,
+        collection_view_properties: viewProperties,
+        system_prompt: this.systemPrompt,
+      }),
+    });
+
+    const responseBody = await response.json();
+
+    if (!response.ok) {
+      throw Error(`Query agent failed. ${JSON.stringify(responseBody)}`);
+    }
+
+    return mapResponse(responseBody);
+  }
+}
+
+export type QueryAgentOptions = {
+  systemPrompt?: string;
+  agentsHost?: string;
+};
+
+export type QueryAgentRunOptions = {
+  viewProperties?: string[];
+};
diff --git a/src/query/index.ts b/src/query/index.ts
new file mode 100644
index 0000000..88dcfbd
--- /dev/null
+++ b/src/query/index.ts
@@ -0,0 +1,2 @@
+export * from "./agent.js";
+export * from "./response/index.js";
diff --git a/src/query/response/api-response.ts b/src/query/response/api-response.ts
new file mode 100644
index 0000000..a8daee1
--- /dev/null
+++ b/src/query/response/api-response.ts
@@ -0,0 +1,94 @@
+import {
+  NumericMetrics,
+  TextMetrics,
+  BooleanMetrics,
+  ComparisonOperator,
+} from "./response.js";
+
+export type ApiQueryAgentResponse = {
+  original_query: string;
+  collection_names: string[];
+  searches: ApiSearchResult[][];
+  aggregations: ApiAggregationResult[][];
+  usage: ApiUsage;
+  total_time: number;
+  aggregation_answer?: string;
+  has_aggregation_answer: boolean;
+  has_search_answer: boolean;
+  is_partial_answer: boolean;
+  missing_information: string[];
+  final_answer: string;
+  sources: ApiSource[];
+};
+
+export type ApiSearchResult = {
+  collection: string;
+  queries: string[];
+  filters: ApiPropertyFilter[];
+  filter_operators: "AND" | "OR";
+};
+
+export type ApiPropertyFilter =
+  | ApiIntegerPropertyFilter
+  | ApiTextPropertyFilter
+  | ApiBooleanPropertyFilter;
+
+type ApiPropertyFilterBase = {
+  property_name: string;
+  operator: ComparisonOperator;
+};
+
+export type ApiIntegerPropertyFilter = ApiPropertyFilterBase & {
+  value: number;
+};
+
+export type ApiTextPropertyFilter = ApiPropertyFilterBase & {
+  value: string;
+};
+
+export type ApiBooleanPropertyFilter = ApiPropertyFilterBase & {
+  value: boolean;
+};
+
+export type ApiAggregationResult = {
+  collection: string;
+  search_query?: string;
+  groupby_property?: string;
+  aggregations: ApiPropertyAggregation[];
+  filters: ApiPropertyFilter[];
+};
+
+export type ApiPropertyAggregation =
+  | ApiIntegerPropertyAggregation
+  | ApiTextPropertyAggregation
+  | ApiBooleanPropertyAggregation;
+
+type ApiPropertyAggregationBase = {
+  property_name: string;
+};
+
+export type ApiIntegerPropertyAggregation = ApiPropertyAggregationBase & {
+  metrics: NumericMetrics;
+};
+
+export type ApiTextPropertyAggregation = ApiPropertyAggregationBase & {
+  metrics: TextMetrics;
+  top_occurrences_limit?: number;
+};
+
+export type ApiBooleanPropertyAggregation = ApiPropertyAggregationBase & {
+  metrics: BooleanMetrics;
+};
+
+export type ApiUsage = {
+  requests: number;
+  request_tokens?: number;
+  response_tokens?: number;
+  total_tokens?: number;
+  details?: Record<string, number>;
+};
+
+export type ApiSource = {
+  object_id: string;
+  collection: string;
+};
diff --git a/src/query/response/index.ts b/src/query/response/index.ts
new file mode 100644
index 0000000..acec77d
--- /dev/null
+++ b/src/query/response/index.ts
@@ -0,0 +1 @@
+export * from "./response.js";
diff --git a/src/query/response/response-mapping.ts b/src/query/response/response-mapping.ts
new file mode 100644
index 0000000..98d68eb
--- /dev/null
+++ b/src/query/response/response-mapping.ts
@@ -0,0 +1,104 @@
+import {
+  QueryAgentResponse,
+  SearchResult,
+  PropertyFilter,
+  AggregationResult,
+  PropertyAggregation,
+  Usage,
+  Source,
+} from "./response.js";
+
+import {
+  ApiQueryAgentResponse,
+  ApiSearchResult,
+  ApiPropertyFilter,
+  ApiAggregationResult,
+  ApiPropertyAggregation,
+  ApiUsage,
+  ApiSource,
+} from "./api-response.js";
+
+export const mapResponse = (
+  response: ApiQueryAgentResponse
+): QueryAgentResponse => {
+  const properties: ResponseProperties = {
+    originalQuery: response.original_query,
+    collectionNames: response.collection_names,
+    searches: mapSearches(response.searches),
+    aggregations: mapAggregations(response.aggregations),
+    usage: mapUsage(response.usage),
+    totalTime: response.total_time,
+    aggregationAnswer: response.aggregation_answer,
+    hasAggregationAnswer: response.has_aggregation_answer,
+    hasSearchAnswer: response.has_search_answer,
+    isPartialAnswer: response.is_partial_answer,
+    missingInformation: response.missing_information,
+    finalAnswer: response.final_answer,
+    sources: mapSources(response.sources),
+  };
+
+  return {
+    ...properties,
+    display: () => display(properties),
+  };
+};
+
+const mapSearches = (searches: ApiSearchResult[][]): SearchResult[][] =>
+  searches.map((searchGroup) =>
+    searchGroup.map((result) => ({
+      collection: result.collection,
+      queries: result.queries,
+      filters: result.filters.map(mapPropertyFilter),
+      filterOperators: result.filter_operators,
+    }))
+  );
+
+const mapPropertyFilter = (filter: ApiPropertyFilter): PropertyFilter => ({
+  propertyName: filter.property_name,
+  operator: filter.operator,
+  value: filter.value,
+});
+
+const mapAggregations = (
+  aggregations: ApiAggregationResult[][]
+): AggregationResult[][] =>
+  aggregations.map((aggGroup) =>
+    aggGroup.map((result) => ({
+      collection: result.collection,
+      searchQuery: result.search_query,
+      groupbyProperty: result.groupby_property,
+      aggregations: result.aggregations.map(mapPropertyAggregation),
+      filters: result.filters.map(mapPropertyFilter),
+    }))
+  );
+
+const mapPropertyAggregation = (
+  aggregation: ApiPropertyAggregation
+): PropertyAggregation => ({
+  propertyName: aggregation.property_name,
+  metrics: aggregation.metrics,
+  topOccurrencesLimit:
+    "top_occurrences_limit" in aggregation
+      ? aggregation.top_occurrences_limit
+      : undefined,
+});
+
+const mapUsage = (usage: ApiUsage): Usage => ({
+  requests: usage.requests,
+  requestTokens: usage.request_tokens,
+  responseTokens: usage.response_tokens,
+  totalTokens: usage.total_tokens,
+  details: usage.details,
+});
+
+const mapSources = (sources: ApiSource[]): Source[] =>
+  sources.map((source) => ({
+    objectId: source.object_id,
+    collection: source.collection,
+  }));
+
+const display = (response: ResponseProperties) => {
+  console.log(JSON.stringify(response, undefined, 2));
+};
+
+type ResponseProperties = Omit<QueryAgentResponse, "display">;
diff --git a/src/query/response/response.ts b/src/query/response/response.ts
new file mode 100644
index 0000000..5780ebf
--- /dev/null
+++ b/src/query/response/response.ts
@@ -0,0 +1,124 @@
+export type QueryAgentResponse = {
+  originalQuery: string;
+  collectionNames: string[];
+  searches: SearchResult[][];
+  aggregations: AggregationResult[][];
+  usage: Usage;
+  totalTime: number;
+  aggregationAnswer?: string;
+  hasAggregationAnswer: boolean;
+  hasSearchAnswer: boolean;
+  isPartialAnswer: boolean;
+  missingInformation: string[];
+  finalAnswer: string;
+  sources: Source[];
+  display(): void;
+};
+
+export type SearchResult = {
+  collection: string;
+  queries: string[];
+  filters: PropertyFilter[];
+  filterOperators: "AND" | "OR";
+};
+
+export type PropertyFilter =
+  | IntegerPropertyFilter
+  | TextPropertyFilter
+  | BooleanPropertyFilter;
+
+type PropertyFilterBase = {
+  propertyName: string;
+  operator: ComparisonOperator;
+};
+
+export type IntegerPropertyFilter = PropertyFilterBase & {
+  value: number;
+};
+
+export type TextPropertyFilter = PropertyFilterBase & {
+  value: string;
+};
+
+export type BooleanPropertyFilter = PropertyFilterBase & {
+  value: boolean;
+};
+
+export enum ComparisonOperator {
+  Equals = "=",
+  LessThan = "<",
+  GreaterThan = ">",
+  LessEqual = "<=",
+  GreaterEqual = ">=",
+  NotEquals = "!=",
+  Like = "LIKE",
+}
+
+export type AggregationResult = {
+  collection: string;
+  searchQuery?: string;
+  groupbyProperty?: string;
+  aggregations: PropertyAggregation[];
+  filters: PropertyFilter[];
+};
+
+export type PropertyAggregation =
+  | IntegerPropertyAggregation
+  | TextPropertyAggregation
+  | BooleanPropertyAggregation;
+
+type PropertyAggregationBase = {
+  propertyName: string;
+};
+
+export type IntegerPropertyAggregation = PropertyAggregationBase & {
+  metrics: NumericMetrics;
+};
+
+export type TextPropertyAggregation = PropertyAggregationBase & {
+  metrics: TextMetrics;
+  topOccurrencesLimit?: number;
+};
+
+export type BooleanPropertyAggregation = PropertyAggregationBase & {
+  metrics: BooleanMetrics;
+};
+
+export enum NumericMetrics {
+  Count = "COUNT",
+  Maximum = "MAXIMUM",
+  Mean = "MEAN",
+  Median = "MEDIAN",
+  Minimum = "MINIMUM",
+  Mode = "MODE",
+  Sum = "SUM",
+  Type = "TYPE",
+}
+
+export enum TextMetrics {
+  Count = "COUNT",
+  Type = "TYPE",
+  TopOccurrences = "TOP_OCCURRENCES",
+}
+
+export enum BooleanMetrics {
+  Count = "COUNT",
+  Type = "TYPE",
+  TotalTrue = "TOTAL_TRUE",
+  TotalFalse = "TOTAL_FALSE",
+  PercentageTrue = "PERCENTAGE_TRUE",
+  PercentageFalse = "PERCENTAGE_FALSE",
+}
+
+export type Usage = {
+  requests: number;
+  requestTokens?: number;
+  responseTokens?: number;
+  totalTokens?: number;
+  details?: Record<string, number>;
+};
+
+export type Source = {
+  objectId: string;
+  collection: string;
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..50085bd
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,12 @@
+{
+  "compilerOptions": {
+    "target": "ES6",
+    "module": "NodeNext",
+    "declaration": true,
+    "outDir": "dist",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "skipLibCheck": true
+  }
+}


Reviews:
user: login: orca-security-eu[bot]
body: ### Orca Security Scan Summary
| Status  | Check | Issues by priority |   |
| ------- | ----- | ------------------ | - |
| <img width="16" alt="Passed" src="https://raw.githubusercontent.com/orcasecurity/orca-cli/main/resources/images/prcomment/status_v1/passed.png" title="Passed"> Passed | Secrets | <img width="12" alt="high" src="https://raw.githubusercontent.com/orcasecurity/orca-cli/main/resources/images/prcomment/priority/high.png" title="High"> 0 &emsp; <img width="12" alt="medium" src="https://raw.githubusercontent.com/orcasecurity/orca-cli/main/resources/images/prcomment/priority/medium.png" title="Medium"> 0 &emsp; <img width="12" alt="low" src="https://raw.githubusercontent.com/orcasecurity/orca-cli/main/resources/images/prcomment/priority/low.png" title="Low"> 0 &emsp; <img width="12" alt="info" src="https://raw.githubusercontent.com/orcasecurity/orca-cli/main/resources/images/prcomment/priority/info.png" title="Info"> 0 | <a href="https://eu.app.orcasecurity.io/shift-left/file_system_secret_detection/scan-log/ec911626-09f0-4361-96aa-c5246d24c6b1" target="_blank">View in Orca</a> |
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-29T08:06:46Z
user: login: malgamves
body: I couple files are missing, LICENSE, README etc, I could help with some of these as I work on examples if you'd like.
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-29T16:59:23Z
user: login: augustas1
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T08:17:56Z
user: login: tsmith023
body: For the CI, feel free to copy what you want/need from the TS client's workflow [file](https://github.com/weaviate/typescript-client/blob/main/.github/workflows/main.yaml)
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-05T09:21:20Z
user: login: augustas1
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T06:21:40Z

Review comments:
diff_hunk: @@ -1,11 +1,21 @@
 {
-  "name": "@weaviate/agents-client",
+  "name": "@weaviate/agents",
   "version": "0.0.1",
   "description": "JS/TS client for Weaviate Agents",
-  "scripts": {},
+  "exports": "./dist/index.js",
+  "type": "module",
+  "scripts": {
+    "build": "tsc"
+  },
   "keywords": [
     "weaviate"
   ],
   "author": "Weaviate",
-  "license": "SEE LICENSE IN LICENSE"
+  "license": "SEE LICENSE IN LICENSE",
path: package.json
user: login: malgamves
body: I think the LICENSE file is missing @augustas1 
created_at: 2025-04-29T16:56:59Z
author_association: NONE
diff_hunk: @@ -1,11 +1,21 @@
 {
-  "name": "@weaviate/agents-client",
+  "name": "@weaviate/agents",
   "version": "0.0.1",
   "description": "JS/TS client for Weaviate Agents",
-  "scripts": {},
+  "exports": "./dist/index.js",
+  "type": "module",
+  "scripts": {
+    "build": "tsc"
+  },
   "keywords": [
     "weaviate"
   ],
   "author": "Weaviate",
-  "license": "SEE LICENSE IN LICENSE"
+  "license": "SEE LICENSE IN LICENSE",
path: package.json
user: login: augustas1
body: it's already on `main`
created_at: 2025-04-30T08:17:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,61 @@
+import { WeaviateClient } from "weaviate-client";
+import { QueryAgentResponse } from "./response/response.js";
+import { mapResponse } from "./response/response-mapping.js";
+
+export class QueryAgent {
+  private systemPrompt?: string;
+  private agentsHost: string;
+
+  constructor(
+    private client: WeaviateClient,
+    private collections: string[],
+    {
+      systemPrompt,
+      agentsHost = "https://api.agents.weaviate.io",
+    }: QueryAgentOptions = {}
+  ) {
+    this.systemPrompt = systemPrompt;
+    this.agentsHost = agentsHost;
+  }
+
+  async run(
+    query: string,
+    { viewProperties }: QueryAgentRunOptions = {}
+  ): Promise<QueryAgentResponse> {
+    const { host, bearerToken, headers } =
+      await this.client.getConnectionDetails();
+
+    const response = await fetch(`${this.agentsHost}/agent/query`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: bearerToken!,
+        "X-Weaviate-Cluster-Url": host,
+      },
+      body: JSON.stringify({
+        headers,
+        query,
+        collection_names: this.collections,
+        collection_view_properties: viewProperties,
+        system_prompt: this.systemPrompt,
+      }),
+    });
+
+    const responseBody = await response.json();
+
+    if (!response.ok) {
+      throw Error(`Query agent failed. ${JSON.stringify(responseBody)}`);
+    }
+
+    return mapResponse(responseBody);
+  }
+}
+
+export interface QueryAgentOptions {
path: src/query/agent.ts
user: login: tsmith023
body: IMO, it is a better UX if `type` is used rather than `interface` to improve IDE auto-completion and code discovering. In VScode, for example, hovering an interface won't show its fields
created_at: 2025-05-05T09:19:50Z
author_association: NONE
diff_hunk: @@ -0,0 +1,124 @@
+export interface QueryAgentResponse {
path: src/query/response/response.ts
user: login: tsmith023
body: IMO, it is a better UX if `type` is used rather than `interface` to improve IDE auto-completion and code discovering. In VScode, for example, hovering an interface won't show its fields
created_at: 2025-05-05T09:19:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,61 @@
+import { WeaviateClient } from "weaviate-client";
+import { QueryAgentResponse } from "./response/response.js";
+import { mapResponse } from "./response/response-mapping.js";
+
+export class QueryAgent {
+  private systemPrompt?: string;
+  private agentsHost: string;
+
+  constructor(
+    private client: WeaviateClient,
+    private collections: string[],
+    {
+      systemPrompt,
+      agentsHost = "https://api.agents.weaviate.io",
+    }: QueryAgentOptions = {}
+  ) {
+    this.systemPrompt = systemPrompt;
+    this.agentsHost = agentsHost;
+  }
+
+  async run(
+    query: string,
+    { viewProperties }: QueryAgentRunOptions = {}
+  ): Promise<QueryAgentResponse> {
+    const { host, bearerToken, headers } =
+      await this.client.getConnectionDetails();
+
+    const response = await fetch(`${this.agentsHost}/agent/query`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: bearerToken!,
+        "X-Weaviate-Cluster-Url": host,
+      },
+      body: JSON.stringify({
+        headers,
+        query,
+        collection_names: this.collections,
+        collection_view_properties: viewProperties,
+        system_prompt: this.systemPrompt,
+      }),
+    });
+
+    const responseBody = await response.json();
+
+    if (!response.ok) {
+      throw Error(`Query agent failed. ${JSON.stringify(responseBody)}`);
+    }
+
+    return mapResponse(responseBody);
+  }
+}
+
+export interface QueryAgentOptions {
path: src/query/agent.ts
user: login: augustas1
body: huh, didn't notice the hovering before. Makes sense then, can switch to `type`s
created_at: 2025-05-06T06:21:40Z
author_association: COLLABORATOR

PR comments:
user: login: github-advanced-security[bot]
created_at: 2025-04-29T08:06:40Z
body: This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on [this overview](/weaviate/agents-typescript-client/security/code-scanning?query=pr%3A1+is%3Aopen). Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out [the documentation](https://docs.github.com/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning). 
author_association: NONE
user: login: malgamves
created_at: 2025-04-29T16:55:37Z
body: Everything looks good @augustas1 ! Do you have any tests planned for the repo? 
author_association: NONE
user: login: augustas1
created_at: 2025-04-30T08:19:15Z
body: Yes, I'll add README, tests and CI later on. Didn't want to have a mega PR :).
Re examples, I think we usually have a link to the docs (once we have them).
author_association: COLLABORATOR
user: login: IchordeDionysos
created_at: 2025-05-05T06:21:12Z
body: @augustas1 is there a rough timeline for when we could starting using the QueryAgent in TypeScript? 😍
author_association: NONE
user: login: augustas1
created_at: 2025-05-06T06:23:57Z
body: > @augustas1 is there a rough timeline for when we could starting using the QueryAgent in TypeScript? 😍

@IchordeDionysos I think Query Agent should be there in 1-2 weeks
author_association: COLLABORATOR