@erdoai/server 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ npm install @erdoai/server
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Basic Invocation
13
+ ### Basic Invocation (Server-Only)
14
14
 
15
15
  ```typescript
16
16
  import { ErdoClient } from '@erdoai/server';
@@ -28,7 +28,9 @@ const result = await client.invoke('data-analyst', {
28
28
  console.log(result.messages);
29
29
  ```
30
30
 
31
- ### Streaming
31
+ > **Note**: `invoke()` and `invokeStream()` are for server-side processing only. They use `/bots/{key}/invoke` which returns raw SSE events. For React UI rendering with `@erdoai/ui` components, use thread-based messaging (`sendMessage()` or the `useThread` hook).
32
+
33
+ ### Streaming (Server-Only)
32
34
 
33
35
  ```typescript
34
36
  for await (const event of client.invokeStream('data-analyst', {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ErdoClientConfig, CreateTokenParams, TokenResponse, CreateThreadParams, Thread, ListThreadsResponse, SendMessageParams, SSEEvent, InvokeParams, InvokeResult } from '@erdoai/types';
1
+ import { ErdoClientConfig, CreateTokenParams, TokenResponse, CreateThreadParams, Thread, ListThreadsResponse, ListThreadMessagesResponse, SendMessageParams, SSEEvent, InvokeParams, InvokeResult } from '@erdoai/types';
2
2
 
3
3
  /**
4
4
  * Erdo Client for invoking agents
@@ -22,7 +22,36 @@ declare class ErdoClient {
22
22
  * The returned token can be passed to a client-side ErdoClient for
23
23
  * scoped access to specific bots.
24
24
  *
25
- * @example
25
+ * ## External User IDs
26
+ *
27
+ * Use `externalUserId` to associate threads with your users. This enables:
28
+ * - `listThreads()` returns only that user's threads
29
+ * - Thread access is automatically scoped to the user
30
+ *
31
+ * **Important**: Store the external user ID in your database so you can
32
+ * create tokens with the same ID when the user returns:
33
+ *
34
+ * ```typescript
35
+ * // In your database (e.g., users table):
36
+ * // { id: 'user-abc', email: '...', erdo_external_user_id: 'ext-123' }
37
+ *
38
+ * // When creating tokens, use the stored ID:
39
+ * const user = await db.users.find(userId);
40
+ * const { token } = await serverClient.createToken({
41
+ * botKeys: ['my-org.data-analyst'],
42
+ * externalUserId: user.erdo_external_user_id, // Persisted ID
43
+ * expiresInSeconds: 3600,
44
+ * });
45
+ *
46
+ * // User can now list their threads from previous sessions
47
+ * const { threads } = await clientClient.listThreads();
48
+ * ```
49
+ *
50
+ * The external user ID can be your own user ID or a separate UUID you generate
51
+ * once per user and store. It's embedded in the token and never exposed to
52
+ * the client - users cannot access other users' threads.
53
+ *
54
+ * @example Basic token creation
26
55
  * ```typescript
27
56
  * // Server-side: Create a token for the frontend
28
57
  * const serverClient = new ErdoClient({ authToken: process.env.ERDO_AUTH_TOKEN });
@@ -78,6 +107,21 @@ declare class ErdoClient {
78
107
  * ```
79
108
  */
80
109
  getThread(threadId: string): Promise<Thread>;
110
+ /**
111
+ * Get messages for a thread (chat history)
112
+ *
113
+ * Requires token authentication.
114
+ * Access is granted via RBAC external_user or explicit thread scope.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const { messages } = await client.getThreadMessages(threadId);
119
+ * for (const msg of messages) {
120
+ * console.log(msg.role, msg.contents.length, 'content items');
121
+ * }
122
+ * ```
123
+ */
124
+ getThreadMessages(threadId: string): Promise<ListThreadMessagesResponse>;
81
125
  /**
82
126
  * Send a message to a thread and stream the bot response
83
127
  *
@@ -106,11 +150,48 @@ declare class ErdoClient {
106
150
  */
107
151
  sendMessageAndWait(threadId: string, params: SendMessageParams): Promise<SSEEvent[]>;
108
152
  /**
109
- * Invoke an agent and wait for the complete result
153
+ * Invoke an agent and wait for the complete result.
154
+ *
155
+ * **Server-only**: This method uses the `/bots/{key}/invoke` endpoint which returns
156
+ * raw SSE events without message wrapping. Use this for server-side processing where
157
+ * you don't need React UI components.
158
+ *
159
+ * **For React UI rendering**, use thread-based messaging instead:
160
+ * - `useThread` hook (client-side with token auth)
161
+ * - `sendMessage()` / `sendMessageAndWait()` (server-side proxy)
162
+ *
163
+ * The thread endpoint wraps events with message metadata required by `handleSSEEvent`
164
+ * and the `Content` component for proper rendering.
165
+ *
166
+ * @example Server-side processing (no React)
167
+ * ```typescript
168
+ * const result = await client.invoke('data-analyst', {
169
+ * messages: [{ role: 'user', content: 'Analyze Q4 sales' }],
170
+ * });
171
+ * // Process result.messages, result.events, etc.
172
+ * ```
110
173
  */
111
174
  invoke(botKey: string, params?: InvokeParams): Promise<InvokeResult>;
112
175
  /**
113
- * Invoke an agent and stream SSE events
176
+ * Invoke an agent and stream SSE events.
177
+ *
178
+ * **Server-only**: This method uses the `/bots/{key}/invoke` endpoint which returns
179
+ * raw SSE events without message wrapping. Use this for server-side processing or
180
+ * proxying to custom clients.
181
+ *
182
+ * **For React UI rendering**, use thread-based messaging instead:
183
+ * - `useThread` hook (client-side with token auth)
184
+ * - `sendMessage()` (server-side proxy to client)
185
+ *
186
+ * The thread endpoint wraps events with message metadata required by `handleSSEEvent`
187
+ * and the `Content` component for proper rendering.
188
+ *
189
+ * @example Server-side streaming (no React)
190
+ * ```typescript
191
+ * for await (const event of client.invokeStream('data-analyst', params)) {
192
+ * console.log(event.type, event.payload);
193
+ * }
194
+ * ```
114
195
  */
115
196
  invokeStream(botKey: string, params?: InvokeParams): AsyncGenerator<SSEEvent, void, unknown>;
116
197
  /**
@@ -129,7 +210,10 @@ declare class ErdoClient {
129
210
  private extractResult;
130
211
  }
131
212
  /**
132
- * Invoke an agent with a clean API
213
+ * Invoke an agent with a clean API.
214
+ *
215
+ * **Server-only**: Uses `/bots/{key}/invoke` which returns raw SSE events.
216
+ * For React UI rendering, use thread-based messaging (`useThread` hook or `sendMessage()`).
133
217
  *
134
218
  * @example
135
219
  * ```typescript
@@ -143,7 +227,10 @@ declare class ErdoClient {
143
227
  */
144
228
  declare function invoke(botKey: string, params?: InvokeParams): Promise<InvokeResult>;
145
229
  /**
146
- * Invoke an agent and stream events
230
+ * Invoke an agent and stream events.
231
+ *
232
+ * **Server-only**: Uses `/bots/{key}/invoke` which returns raw SSE events.
233
+ * For React UI rendering, use thread-based messaging (`useThread` hook or `sendMessage()`).
147
234
  *
148
235
  * @example
149
236
  * ```typescript
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- var m="https://api.erdo.ai";function w(){let i=typeof process<"u"?process.env:{};return {endpoint:i.ERDO_ENDPOINT||m,authToken:i.ERDO_AUTH_TOKEN}}function k(i){let e=w(),t=i?.endpoint||e.endpoint||m,s=i?.authToken||e.authToken,n=i?.token;return {endpoint:t,authToken:s,token:n}}async function*f(i){if(!i.body)throw new Error("Response body is null");let e=i.body.getReader(),t=new TextDecoder,s="",n,o;try{for(;;){let{done:a,value:c}=await e.read();if(a){if(o!==void 0){let r=E(n,o);r&&(yield r);}break}s+=t.decode(c,{stream:!0});let u=s.split(`
2
- `);s=u.pop()||"";for(let r of u){let h=r.trim();if(!h){if(o!==void 0){let d=E(n,o);d&&(yield d),n=void 0,o=void 0;}continue}if(h.startsWith("event:"))n=h.slice(6).trim();else if(h.startsWith("data:")){let d=h.slice(5).trim();o===void 0?o=d:o+=`
3
- `+d;}}}}finally{e.releaseLock();}}function E(i,e){if(!e)return null;try{let t=JSON.parse(e);return i&&(t.type=i),t}catch{return {type:i,raw:e}}}async function l(i){let e=[];for await(let t of f(i))e.push(t);return e}var p=class{endpoint;authToken;token;constructor(e){let t=k(e);if(this.endpoint=t.endpoint,this.authToken=t.authToken,this.token=t.token,!this.authToken&&!this.token)throw new Error("Either authToken or token is required")}async createToken(e){if(!this.authToken)throw new Error("createToken requires authToken (API key) authentication");let t;if(e.botKeys&&e.botKeys.length>0){let c=`${this.endpoint}/resolve-bot-keys`,u=await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify({keys:e.botKeys})});if(!u.ok){let d=await u.text();throw new Error(`Failed to resolve bot keys: ${u.status}: ${d}`)}let r=await u.json();t=Object.values(r.bots);let h=e.botKeys.filter(d=>!r.bots[d]);if(h.length>0)throw new Error(`Bot keys not found or access denied: ${h.join(", ")}`)}let s=`${this.endpoint}/tokens`,n={};t&&(n.bot_ids=t),e.datasetIds&&(n.dataset_ids=e.datasetIds),e.threadIds&&(n.thread_ids=e.threadIds),e.externalUserId&&(n.external_user_id=e.externalUserId),e.expiresInSeconds&&(n.expires_in_seconds=e.expiresInSeconds);let o=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify(n)});if(!o.ok){let c=await o.text();throw new Error(`Failed to create token: ${o.status}: ${c}`)}let a=await o.json();return {tokenId:a.token_id,token:a.token,expiresAt:a.expires_at}}async createThread(e={}){if(!this.token)throw new Error("createThread requires token authentication");let t=`${this.endpoint}/threads`,s={};e.name&&(s.name=e.name),e.datasetIds&&(s.dataset_ids=e.datasetIds);let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify(s)});if(!n.ok){let a=await n.text();throw new Error(`Failed to create thread: ${n.status}: ${a}`)}let o=await n.json();return {id:o.id,name:o.name,createdAt:o.created_at,updatedAt:o.updated_at}}async listThreads(){if(!this.token)throw new Error("listThreads requires token authentication");let e=`${this.endpoint}/threads-user`,t=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!t.ok){let n=await t.text();throw new Error(`Failed to list threads: ${t.status}: ${n}`)}return {threads:(await t.json()).threads.map(n=>({id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}))}}async getThread(e){if(!this.token)throw new Error("getThread requires token authentication");let t=`${this.endpoint}/threads/${e}`,s=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!s.ok){let o=await s.text();throw new Error(`Failed to get thread: ${s.status}: ${o}`)}let n=await s.json();return {id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}}async*sendMessage(e,t){if(!this.token)throw new Error("sendMessage requires token authentication");let s=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let o=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!o.ok){let a=await o.text();throw new Error(`Failed to send message: ${o.status}: ${a}`)}yield*f(o);}async sendMessageAndWait(e,t){if(!this.token)throw new Error("sendMessageAndWait requires token authentication");let s=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let o=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!o.ok){let a=await o.text();throw new Error(`Failed to send message: ${o.status}: ${a}`)}return l(o)}async invoke(e,t={}){let s=await this.collectEvents(e,t);return this.extractResult(e,s)}async*invokeStream(e,t={}){let s=await this.makeRequest(e,t);yield*f(s);}async collectEvents(e,t){let s=await this.makeRequest(e,t);return l(s)}async makeRequest(e,t){let s=`${this.endpoint}/bots/${e}/invoke`,n={};t.messages&&(n.messages=t.messages),t.parameters&&(n.parameters=t.parameters),t.datasets&&(n.dataset_slugs=t.datasets),t.mode&&(n.mode=t.mode),t.manualMocks&&(n.manual_mocks=t.manualMocks);let o=this.token||this.authToken,a=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!a.ok){let c=await a.text();throw new Error(`API request failed with status ${a.status}: ${c}`)}return a}extractResult(e,t){let s=[],n=[],o=new Set,a,c;for(let u of t){if(!u)continue;let r=u.payload,h=u.metadata;if(r&&typeof r=="object"&&"invocation_id"in r&&!c&&(c=r.invocation_id),r&&typeof r=="object"&&"action_type"in r&&"key"in r){let d=r.key;o.has(d)||(o.add(d),n.push({key:d,action:r.action_type,status:"completed"}));}if(r&&typeof r=="object"&&"output"in r&&h?.user_visibility==="visible"){let d=r.output;if(d&&typeof d=="object"&&"content"in d){let y=d.content;if(Array.isArray(y)){for(let v of y)if(v.content_type==="text"){let T=h?.role||"assistant";s.push({role:T,content:v.content||""});}}}}r&&typeof r=="object"&&"status"in r&&"output"in r&&(a={status:r.status,parameters:r.parameters,output:r.output,message:r.message,error:r.error});}return {success:true,botKey:e,invocationId:c,result:a,messages:s,events:t,steps:n}}},g=null;function S(){return g||(g=new p),g}async function x(i,e={}){return S().invoke(i,e)}async function*b(i,e={}){yield*S().invokeStream(i,e);}export{p as ErdoClient,l as collectSSEEvents,w as getConfigFromEnv,x as invoke,b as invokeStream,f as parseSSEStream,k as resolveConfig};
1
+ var w="https://api.erdo.ai";function m(){let a=typeof process<"u"?process.env:{};return {endpoint:a.ERDO_ENDPOINT||w,authToken:a.ERDO_AUTH_TOKEN}}function g(a){let e=m(),t=a?.endpoint||e.endpoint||w,o=a?.authToken||e.authToken,n=a?.token;return {endpoint:t,authToken:o,token:n}}async function*l(a){if(!a.body)throw new Error("Response body is null");let e=a.body.getReader(),t=new TextDecoder,o="",n,s;try{for(;;){let{done:i,value:c}=await e.read();if(i){if(s!==void 0){let r=E(n,s);r&&(yield r);}break}o+=t.decode(c,{stream:!0});let u=o.split(`
2
+ `);o=u.pop()||"";for(let r of u){let h=r.trim();if(!h){if(s!==void 0){let d=E(n,s);d&&(yield d),n=void 0,s=void 0;}continue}if(h.startsWith("event:"))n=h.slice(6).trim();else if(h.startsWith("data:")){let d=h.slice(5).trim();s===void 0?s=d:s+=`
3
+ `+d;}}}}finally{e.releaseLock();}}function E(a,e){if(!e)return null;try{let t=JSON.parse(e);return a&&(t.type=a),t}catch{return {type:a,raw:e}}}async function f(a){let e=[];for await(let t of l(a))e.push(t);return e}var p=class{endpoint;authToken;token;constructor(e){let t=g(e);if(this.endpoint=t.endpoint,this.authToken=t.authToken,this.token=t.token,!this.authToken&&!this.token)throw new Error("Either authToken or token is required")}async createToken(e){if(!this.authToken)throw new Error("createToken requires authToken (API key) authentication");let t;if(e.botKeys&&e.botKeys.length>0){let c=`${this.endpoint}/resolve-bot-keys`,u=await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify({keys:e.botKeys})});if(!u.ok){let d=await u.text();throw new Error(`Failed to resolve bot keys: ${u.status}: ${d}`)}let r=await u.json();t=Object.values(r.bots);let h=e.botKeys.filter(d=>!r.bots[d]);if(h.length>0)throw new Error(`Bot keys not found or access denied: ${h.join(", ")}`)}let o=`${this.endpoint}/tokens`,n={};t&&(n.bot_ids=t),e.datasetIds&&(n.dataset_ids=e.datasetIds),e.threadIds&&(n.thread_ids=e.threadIds),e.externalUserId&&(n.external_user_id=e.externalUserId),e.expiresInSeconds&&(n.expires_in_seconds=e.expiresInSeconds);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify(n)});if(!s.ok){let c=await s.text();throw new Error(`Failed to create token: ${s.status}: ${c}`)}let i=await s.json();return {tokenId:i.token_id,token:i.token,expiresAt:i.expires_at}}async createThread(e={}){if(!this.token)throw new Error("createThread requires token authentication");let t=`${this.endpoint}/threads`,o={};e.name&&(o.name=e.name),e.datasetIds&&(o.dataset_ids=e.datasetIds);let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let i=await n.text();throw new Error(`Failed to create thread: ${n.status}: ${i}`)}let s=await n.json();return {id:s.id,name:s.name,createdAt:s.created_at,updatedAt:s.updated_at}}async listThreads(){if(!this.token)throw new Error("listThreads requires token authentication");let e=`${this.endpoint}/threads-user`,t=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!t.ok){let n=await t.text();throw new Error(`Failed to list threads: ${t.status}: ${n}`)}return {threads:(await t.json()).threads.map(n=>({id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}))}}async getThread(e){if(!this.token)throw new Error("getThread requires token authentication");let t=`${this.endpoint}/threads/${e}`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread: ${o.status}: ${s}`)}let n=await o.json();return {id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}}async getThreadMessages(e){if(!this.token)throw new Error("getThreadMessages requires token authentication");let t=`${this.endpoint}/threads/${e}/messages`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread messages: ${o.status}: ${s}`)}return {messages:(await o.json()).messages.map(s=>({id:s.id,thread_id:s.thread_id,role:s.role,created_at:s.created_at,updated_at:s.updated_at,contents:s.contents.map(i=>({id:i.id,content_type:i.content_type,ui_content_type:i.ui_content_type,content:i.content,user_visibility:i.user_visibility,bot_visibility:i.bot_visibility,created_at:i.created_at}))}))}}async*sendMessage(e,t){if(!this.token)throw new Error("sendMessage requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let i=await s.text();throw new Error(`Failed to send message: ${s.status}: ${i}`)}yield*l(s);}async sendMessageAndWait(e,t){if(!this.token)throw new Error("sendMessageAndWait requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let i=await s.text();throw new Error(`Failed to send message: ${s.status}: ${i}`)}return f(s)}async invoke(e,t={}){let o=await this.collectEvents(e,t);return this.extractResult(e,o)}async*invokeStream(e,t={}){let o=await this.makeRequest(e,t);yield*l(o);}async collectEvents(e,t){let o=await this.makeRequest(e,t);return f(o)}async makeRequest(e,t){let o=`${this.endpoint}/bots/${e}/invoke`,n={};t.messages&&(n.messages=t.messages),t.parameters&&(n.parameters=t.parameters),t.datasets&&(n.dataset_slugs=t.datasets),t.mode&&(n.mode=t.mode),t.manualMocks&&(n.manual_mocks=t.manualMocks);let s=this.token||this.authToken,i=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!i.ok){let c=await i.text();throw new Error(`API request failed with status ${i.status}: ${c}`)}return i}extractResult(e,t){let o=[],n=[],s=new Set,i,c;for(let u of t){if(!u)continue;let r=u.payload,h=u.metadata;if(r&&typeof r=="object"&&"invocation_id"in r&&!c&&(c=r.invocation_id),r&&typeof r=="object"&&"action_type"in r&&"key"in r){let d=r.key;s.has(d)||(s.add(d),n.push({key:d,action:r.action_type,status:"completed"}));}if(r&&typeof r=="object"&&"output"in r&&h?.user_visibility==="visible"){let d=r.output;if(d&&typeof d=="object"&&"content"in d){let y=d.content;if(Array.isArray(y)){for(let v of y)if(v.content_type==="text"){let S=h?.role||"assistant";o.push({role:S,content:v.content||""});}}}}r&&typeof r=="object"&&"status"in r&&"output"in r&&(i={status:r.status,parameters:r.parameters,output:r.output,message:r.message,error:r.error});}return {success:true,botKey:e,invocationId:c,result:i,messages:o,events:t,steps:n}}},k=null;function T(){return k||(k=new p),k}async function _(a,e={}){return T().invoke(a,e)}async function*b(a,e={}){yield*T().invokeStream(a,e);}export{p as ErdoClient,f as collectSSEEvents,m as getConfigFromEnv,_ as invoke,b as invokeStream,l as parseSSEStream,g as resolveConfig};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/server",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Erdo server SDK for invoking agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,6 +21,7 @@
21
21
  },
22
22
  "scripts": {
23
23
  "build": "tsup",
24
+ "build:check": "tsup --outDir dist-check",
24
25
  "dev": "tsup --watch",
25
26
  "typecheck": "tsc --noEmit",
26
27
  "test": "vitest run",
@@ -39,7 +40,7 @@
39
40
  "directory": "packages/server"
40
41
  },
41
42
  "dependencies": {
42
- "@erdoai/types": "^0.1.7"
43
+ "@erdoai/types": "^0.1.9"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@types/node": "^24.10.1",