10xgraph-client 0.0.0-stage → 0.6.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 (125) hide show
  1. package/CHANGELOG.md +276 -0
  2. package/LICENSE +21 -0
  3. package/README.md +400 -2
  4. package/dist/agent.d.ts +22 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/client.d.ts +479 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/compat.d.ts +28 -0
  9. package/dist/compat.d.ts.map +1 -0
  10. package/dist/endpoints/addThreadMessages.d.ts +22 -0
  11. package/dist/endpoints/addThreadMessages.d.ts.map +1 -0
  12. package/dist/endpoints/clearThreadState.d.ts +15 -0
  13. package/dist/endpoints/clearThreadState.d.ts.map +1 -0
  14. package/dist/endpoints/deleteMemory.d.ts +19 -0
  15. package/dist/endpoints/deleteMemory.d.ts.map +1 -0
  16. package/dist/endpoints/deleteThread.d.ts +19 -0
  17. package/dist/endpoints/deleteThread.d.ts.map +1 -0
  18. package/dist/endpoints/deleteThreadMessage.d.ts +20 -0
  19. package/dist/endpoints/deleteThreadMessage.d.ts.map +1 -0
  20. package/dist/endpoints/files.d.ts +83 -0
  21. package/dist/endpoints/files.d.ts.map +1 -0
  22. package/dist/endpoints/fixGraph.d.ts +29 -0
  23. package/dist/endpoints/fixGraph.d.ts.map +1 -0
  24. package/dist/endpoints/forgetMemories.d.ts +22 -0
  25. package/dist/endpoints/forgetMemories.d.ts.map +1 -0
  26. package/dist/endpoints/getMemory.d.ts +19 -0
  27. package/dist/endpoints/getMemory.d.ts.map +1 -0
  28. package/dist/endpoints/graph.d.ts +39 -0
  29. package/dist/endpoints/graph.d.ts.map +1 -0
  30. package/dist/endpoints/graphTools.d.ts +32 -0
  31. package/dist/endpoints/graphTools.d.ts.map +1 -0
  32. package/dist/endpoints/invoke.d.ts +59 -0
  33. package/dist/endpoints/invoke.d.ts.map +1 -0
  34. package/dist/endpoints/listMemories.d.ts +19 -0
  35. package/dist/endpoints/listMemories.d.ts.map +1 -0
  36. package/dist/endpoints/metadata.d.ts +6 -0
  37. package/dist/endpoints/metadata.d.ts.map +1 -0
  38. package/dist/endpoints/observability.d.ts +58 -0
  39. package/dist/endpoints/observability.d.ts.map +1 -0
  40. package/dist/endpoints/ping.d.ts +10 -0
  41. package/dist/endpoints/ping.d.ts.map +1 -0
  42. package/dist/endpoints/realtime.d.ts +134 -0
  43. package/dist/endpoints/realtime.d.ts.map +1 -0
  44. package/dist/endpoints/searchMemory.d.ts +57 -0
  45. package/dist/endpoints/searchMemory.d.ts.map +1 -0
  46. package/dist/endpoints/stateSchema.d.ts +50 -0
  47. package/dist/endpoints/stateSchema.d.ts.map +1 -0
  48. package/dist/endpoints/stopGraph.d.ts +26 -0
  49. package/dist/endpoints/stopGraph.d.ts.map +1 -0
  50. package/dist/endpoints/storeMemory.d.ts +33 -0
  51. package/dist/endpoints/storeMemory.d.ts.map +1 -0
  52. package/dist/endpoints/stream.d.ts +60 -0
  53. package/dist/endpoints/stream.d.ts.map +1 -0
  54. package/dist/endpoints/threadDetails.d.ts +19 -0
  55. package/dist/endpoints/threadDetails.d.ts.map +1 -0
  56. package/dist/endpoints/threadMessage.d.ts +17 -0
  57. package/dist/endpoints/threadMessage.d.ts.map +1 -0
  58. package/dist/endpoints/threadMessages.d.ts +23 -0
  59. package/dist/endpoints/threadMessages.d.ts.map +1 -0
  60. package/dist/endpoints/threadState.d.ts +14 -0
  61. package/dist/endpoints/threadState.d.ts.map +1 -0
  62. package/dist/endpoints/threads.d.ts +30 -0
  63. package/dist/endpoints/threads.d.ts.map +1 -0
  64. package/dist/endpoints/updateMemory.d.ts +21 -0
  65. package/dist/endpoints/updateMemory.d.ts.map +1 -0
  66. package/dist/endpoints/updateThreadState.d.ts +18 -0
  67. package/dist/endpoints/updateThreadState.d.ts.map +1 -0
  68. package/dist/endpoints/wsStream.d.ts +33 -0
  69. package/dist/endpoints/wsStream.d.ts.map +1 -0
  70. package/dist/errors.d.ts +155 -0
  71. package/dist/errors.d.ts.map +1 -0
  72. package/dist/index.cjs +6 -0
  73. package/dist/index.cjs.map +1 -0
  74. package/dist/index.d.ts +39 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +2664 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/message.d.ts +174 -0
  79. package/dist/message.d.ts.map +1 -0
  80. package/dist/request.d.ts +33 -0
  81. package/dist/request.d.ts.map +1 -0
  82. package/dist/tools.d.ts +67 -0
  83. package/dist/tools.d.ts.map +1 -0
  84. package/dist/ws.d.ts +48 -0
  85. package/dist/ws.d.ts.map +1 -0
  86. package/package.json +99 -4
  87. package/src/agent.ts +43 -0
  88. package/src/client.ts +1044 -0
  89. package/src/compat.ts +35 -0
  90. package/src/endpoints/addThreadMessages.ts +83 -0
  91. package/src/endpoints/clearThreadState.ts +75 -0
  92. package/src/endpoints/deleteMemory.ts +91 -0
  93. package/src/endpoints/deleteThread.ts +78 -0
  94. package/src/endpoints/deleteThreadMessage.ts +83 -0
  95. package/src/endpoints/files.ts +274 -0
  96. package/src/endpoints/fixGraph.ts +91 -0
  97. package/src/endpoints/forgetMemories.ts +95 -0
  98. package/src/endpoints/getMemory.ts +82 -0
  99. package/src/endpoints/graph.ts +97 -0
  100. package/src/endpoints/graphTools.ts +90 -0
  101. package/src/endpoints/invoke.ts +318 -0
  102. package/src/endpoints/listMemories.ts +97 -0
  103. package/src/endpoints/metadata.ts +6 -0
  104. package/src/endpoints/observability.ts +107 -0
  105. package/src/endpoints/ping.ts +58 -0
  106. package/src/endpoints/realtime.ts +326 -0
  107. package/src/endpoints/searchMemory.ts +132 -0
  108. package/src/endpoints/stateSchema.ts +106 -0
  109. package/src/endpoints/stopGraph.ts +85 -0
  110. package/src/endpoints/storeMemory.ts +98 -0
  111. package/src/endpoints/stream.ts +440 -0
  112. package/src/endpoints/threadDetails.ts +78 -0
  113. package/src/endpoints/threadMessage.ts +76 -0
  114. package/src/endpoints/threadMessages.ts +97 -0
  115. package/src/endpoints/threadState.ts +74 -0
  116. package/src/endpoints/threads.ts +112 -0
  117. package/src/endpoints/updateMemory.ts +90 -0
  118. package/src/endpoints/updateThreadState.ts +82 -0
  119. package/src/endpoints/wsStream.ts +251 -0
  120. package/src/errors.ts +553 -0
  121. package/src/index.ts +108 -0
  122. package/src/message.ts +436 -0
  123. package/src/request.ts +168 -0
  124. package/src/tools.ts +225 -0
  125. package/src/ws.ts +100 -0
@@ -0,0 +1,98 @@
1
+ import { ResponseMetadata } from './metadata.js';
2
+ import { createErrorFromResponse } from '../errors.js';
3
+ import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
4
+
5
+ export interface StoreMemoryContext extends RequestContext {}
6
+
7
+ /**
8
+ * Types of memories that can be stored
9
+ */
10
+ export enum MemoryType {
11
+ EPISODIC = 'episodic', // Conversation memories
12
+ SEMANTIC = 'semantic', // Facts and knowledge
13
+ PROCEDURAL = 'procedural', // How-to knowledge
14
+ ENTITY = 'entity', // Entity-based memories
15
+ RELATIONSHIP = 'relationship', // Entity relationships
16
+ CUSTOM = 'custom', // Custom memory types
17
+ DECLARATIVE = 'declarative', // Explicit facts and events
18
+ }
19
+
20
+ export interface StoreMemoryRequest {
21
+ config?: Record<string, any>;
22
+ options?: Record<string, any>;
23
+ content: string;
24
+ memory_type: MemoryType;
25
+ category: string;
26
+ metadata?: Record<string, any>;
27
+ }
28
+
29
+ export interface StoreMemoryData {
30
+ memory_id: string;
31
+ }
32
+
33
+ export interface StoreMemoryResponse {
34
+ data: StoreMemoryData;
35
+ metadata: ResponseMetadata;
36
+ }
37
+
38
+ export async function storeMemory(
39
+ context: StoreMemoryContext,
40
+ request: StoreMemoryRequest
41
+ ): Promise<StoreMemoryResponse> {
42
+ try {
43
+ if (context.debug) {
44
+ console.debug('TenxGraphClient: Storing memory with type', request.memory_type);
45
+ }
46
+
47
+ const controller = new AbortController();
48
+ const timeoutId = setTimeout(() => controller.abort(), context.timeout);
49
+
50
+ const url = `${context.baseUrl}/v1/store/memories`;
51
+
52
+ // Prepare request body
53
+ const body = {
54
+ config: request.config || {},
55
+ options: request.options || {},
56
+ content: request.content,
57
+ memory_type: request.memory_type,
58
+ category: request.category,
59
+ metadata: request.metadata || {},
60
+ };
61
+
62
+ const response = await fetch(url, {
63
+ method: 'POST',
64
+ headers: buildHeaders(context as RequestContext, {
65
+ 'Content-Type': 'application/json',
66
+ }),
67
+ ...getRequestCredentials(context as RequestContext),
68
+ body: JSON.stringify(body),
69
+ signal: controller.signal,
70
+ });
71
+
72
+ clearTimeout(timeoutId);
73
+
74
+ if (!response.ok) {
75
+ console.warn(`TenxGraphClient: Store memory failed with HTTP ${response.status}`);
76
+ const error = await createErrorFromResponse(
77
+ response,
78
+ 'Store memory request failed',
79
+ '/v1/memory',
80
+ 'POST'
81
+ );
82
+ throw error;
83
+ }
84
+
85
+ const data: StoreMemoryResponse = await response.json();
86
+
87
+ if (context.debug) {
88
+ console.info('TenxGraphClient: Memory stored successfully', data);
89
+ }
90
+
91
+ return data;
92
+ } catch (error) {
93
+ if (context.debug) {
94
+ console.debug('TenxGraphClient: Store memory failed:', error);
95
+ }
96
+ throw error;
97
+ }
98
+ }
@@ -0,0 +1,440 @@
1
+ import { Message } from '../message.js';
2
+ import { AgentState } from '../agent.js';
3
+ import { ToolExecutor } from '../tools.js';
4
+ import { createErrorFromResponse } from '../errors.js';
5
+ import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
6
+
7
+ export interface StreamContext extends RequestContext {
8
+ toolExecutor?: ToolExecutor;
9
+ }
10
+
11
+ // Stream Request payload (same as Invoke)
12
+ export interface StreamRequest {
13
+ messages: any[]; // Will be serialized Message objects
14
+ initial_state?: Record<string, any>;
15
+ config?: Record<string, any>;
16
+ recursion_limit?: number;
17
+ response_granularity?: 'full' | 'partial' | 'low';
18
+ }
19
+
20
+ // Stream Events
21
+ export enum StreamEventType {
22
+ MESSAGE = 'message',
23
+ UPDATES = 'updates',
24
+ STATE = 'state',
25
+ ERROR = 'error',
26
+ }
27
+
28
+ // Response metadata
29
+ export interface StreamMetadata {
30
+ is_new_thread: boolean;
31
+ thread_id: string;
32
+ [key: string]: any;
33
+ }
34
+
35
+ // Individual stream chunk from server
36
+ export interface StreamChunk {
37
+ event: StreamEventType | string;
38
+ message?: Message | null;
39
+ state?: AgentState | null;
40
+ data?: any;
41
+ thread_id?: string;
42
+ run_id?: string;
43
+ metadata?: StreamMetadata | Record<string, any>;
44
+ timestamp?: number;
45
+ }
46
+
47
+ /**
48
+ * Parse NDJSON stream and yield individual chunks immediately as they arrive
49
+ * Handles streaming response body as async iterable
50
+ * Supports both newline-separated and concatenated JSON objects
51
+ */
52
+ async function* parseStreamChunks(
53
+ reader: ReadableStreamDefaultReader<Uint8Array>,
54
+ debug: boolean
55
+ ): AsyncGenerator<StreamChunk, void, unknown> {
56
+ const decoder = new TextDecoder();
57
+ let buffer = '';
58
+
59
+ try {
60
+ while (true) {
61
+ const { done, value } = await reader.read();
62
+
63
+ if (done) {
64
+ // Process any remaining data in buffer
65
+ if (buffer.trim()) {
66
+ yield* processBuffer(buffer, debug, true);
67
+ }
68
+ break;
69
+ }
70
+
71
+ // Append new data to buffer
72
+ buffer += decoder.decode(value, { stream: true });
73
+
74
+ // Immediately process and yield all complete chunks
75
+ const result = yield* processBuffer(buffer, debug, false);
76
+ buffer = result.remaining;
77
+ }
78
+ } finally {
79
+ reader.releaseLock();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Process buffer and yield all complete JSON chunks
85
+ * Returns the remaining incomplete buffer
86
+ */
87
+ function* processBuffer(
88
+ buffer: string,
89
+ debug: boolean,
90
+ isFinal: boolean
91
+ ): Generator<StreamChunk, { remaining: string }, unknown> {
92
+ // First, try to process newline-separated lines (NDJSON format)
93
+ while (buffer.includes('\n')) {
94
+ const newlineIndex = buffer.indexOf('\n');
95
+ const line = buffer.slice(0, newlineIndex).trim();
96
+ buffer = buffer.slice(newlineIndex + 1);
97
+
98
+ if (line) {
99
+ try {
100
+ const chunk = JSON.parse(line) as StreamChunk;
101
+ if (debug) {
102
+ console.debug('TenxGraphClient: Stream chunk received (NDJSON):', chunk.event);
103
+ }
104
+ yield chunk;
105
+ } catch (error) {
106
+ if (debug) {
107
+ console.warn('TenxGraphClient: Failed to parse NDJSON line:', line.slice(0, 100), error);
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ // Then, try to extract concatenated JSON objects (no newlines)
114
+ while (buffer.trim()) {
115
+ const extracted = extractFirstJSON(buffer);
116
+ if (extracted) {
117
+ try {
118
+ const chunk = JSON.parse(extracted.json) as StreamChunk;
119
+ if (debug) {
120
+ console.debug('TenxGraphClient: Stream chunk received (concat):', chunk.event);
121
+ }
122
+ yield chunk;
123
+ buffer = extracted.remaining;
124
+ } catch (error) {
125
+ if (debug) {
126
+ console.warn(
127
+ 'TenxGraphClient: Failed to parse concatenated JSON:',
128
+ extracted.json.slice(0, 100),
129
+ error
130
+ );
131
+ }
132
+ break;
133
+ }
134
+ } else {
135
+ // Incomplete JSON, keep in buffer
136
+ if (isFinal && buffer.trim()) {
137
+ // On final read, try to parse what's left
138
+ try {
139
+ const chunk = JSON.parse(buffer.trim()) as StreamChunk;
140
+ if (debug) {
141
+ console.debug('TenxGraphClient: Final stream chunk:', chunk.event);
142
+ }
143
+ yield chunk;
144
+ buffer = '';
145
+ } catch (error) {
146
+ if (debug) {
147
+ console.warn(
148
+ 'TenxGraphClient: Failed to parse final buffer:',
149
+ buffer.slice(0, 100),
150
+ error
151
+ );
152
+ }
153
+ }
154
+ }
155
+ break;
156
+ }
157
+ }
158
+
159
+ return { remaining: buffer };
160
+ }
161
+
162
+ /**
163
+ * Extract the first complete JSON object from a string that may contain multiple concatenated JSON objects
164
+ * Returns the JSON string and the remaining buffer
165
+ */
166
+ function extractFirstJSON(buffer: string): { json: string; remaining: string } | null {
167
+ buffer = buffer.trim();
168
+ if (!buffer || buffer[0] !== '{') {
169
+ return null;
170
+ }
171
+
172
+ let depth = 0;
173
+ let inString = false;
174
+ let escaped = false;
175
+
176
+ for (let i = 0; i < buffer.length; i++) {
177
+ const char = buffer[i];
178
+
179
+ if (escaped) {
180
+ escaped = false;
181
+ continue;
182
+ }
183
+
184
+ if (char === '\\') {
185
+ escaped = true;
186
+ continue;
187
+ }
188
+
189
+ if (char === '"') {
190
+ inString = !inString;
191
+ continue;
192
+ }
193
+
194
+ if (inString) {
195
+ continue;
196
+ }
197
+
198
+ if (char === '{') {
199
+ depth++;
200
+ } else if (char === '}') {
201
+ depth--;
202
+ if (depth === 0) {
203
+ // Found complete JSON object
204
+ return {
205
+ json: buffer.slice(0, i + 1),
206
+ remaining: buffer.slice(i + 1).trim(),
207
+ };
208
+ }
209
+ }
210
+ }
211
+
212
+ // Incomplete JSON object
213
+ return null;
214
+ }
215
+
216
+ /**
217
+ * Check if messages contain remote tool calls
218
+ */
219
+ function hasRemoteToolCalls(messages: Message[]): boolean {
220
+ return messages.some(
221
+ (msg: Message) =>
222
+ msg.content && msg.content.some((block: any) => block.type === 'remote_tool_call')
223
+ );
224
+ }
225
+
226
+ /**
227
+ * Stream invoke to the graph with automatic tool execution loop
228
+ * Returns an async iterable that yields stream chunks as they arrive
229
+ *
230
+ * This implements the same tool execution loop as invoke(), but uses
231
+ * HTTP streaming instead of REST API calls.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * const stream = streamInvoke(context, request);
236
+ * for await (const chunk of stream) {
237
+ * if (chunk.event === 'message') {
238
+ * console.log('Message:', chunk.message);
239
+ * } else if (chunk.event === 'updates') {
240
+ * console.log('State:', chunk.state);
241
+ * }
242
+ * }
243
+ * ```
244
+ */
245
+ export async function* streamInvoke(
246
+ context: StreamContext,
247
+ request: StreamRequest
248
+ ): AsyncGenerator<StreamChunk, void, unknown> {
249
+ const recursion_limit = request.recursion_limit || 25;
250
+
251
+ if (context.debug) {
252
+ console.debug('TenxGraphClient: Starting stream invoke with recursion_limit:', recursion_limit);
253
+ console.debug('TenxGraphClient: Initial request:', JSON.stringify(request, null, 2));
254
+ }
255
+
256
+ let currentMessages = request.messages;
257
+ let iterations = 0;
258
+ const initial_state = request.initial_state;
259
+ const config = request.config;
260
+ const response_granularity = request.response_granularity;
261
+
262
+ // Main recursion loop (same as invoke, but using streaming)
263
+ while (iterations < recursion_limit) {
264
+ iterations++;
265
+
266
+ if (context.debug) {
267
+ console.debug(`TenxGraphClient: Stream iteration ${iterations}/${recursion_limit}`);
268
+ }
269
+
270
+ // Prepare request for this iteration
271
+ const iterationRequest: StreamRequest = {
272
+ messages: currentMessages,
273
+ initial_state: iterations === 1 ? initial_state : undefined,
274
+ config,
275
+ recursion_limit,
276
+ response_granularity,
277
+ };
278
+
279
+ if (context.debug) {
280
+ console.debug(
281
+ 'TenxGraphClient: Stream request payload:',
282
+ JSON.stringify(iterationRequest, null, 2)
283
+ );
284
+ }
285
+
286
+ // Open the stream and yield all chunks
287
+ const controller = new AbortController();
288
+ const timeoutId = setTimeout(() => controller.abort(), context.timeout);
289
+
290
+ try {
291
+ const response = await fetch(`${context.baseUrl}/v1/graph/stream`, {
292
+ method: 'POST',
293
+ headers: buildHeaders(context as RequestContext, {
294
+ 'Content-Type': 'application/json',
295
+ Accept: 'application/x-ndjson, application/json',
296
+ }),
297
+ ...getRequestCredentials(context as RequestContext),
298
+ body: JSON.stringify(iterationRequest),
299
+ signal: controller.signal,
300
+ });
301
+
302
+ clearTimeout(timeoutId);
303
+
304
+ if (!response.ok) {
305
+ console.warn(`TenxGraphClient: Stream failed with HTTP ${response.status}`);
306
+ const error = await createErrorFromResponse(
307
+ response,
308
+ 'Stream request failed',
309
+ '/v1/stream',
310
+ 'POST'
311
+ );
312
+ throw error;
313
+ }
314
+
315
+ if (!response.body) {
316
+ throw new Error('No response body for stream');
317
+ }
318
+
319
+ // Collect messages while yielding chunks
320
+ const responseMessages: Message[] = [];
321
+ const reader = response.body.getReader();
322
+
323
+ for await (const chunk of parseStreamChunks(reader, context.debug)) {
324
+ // Yield every chunk to the caller
325
+ yield chunk;
326
+
327
+ // Also collect messages for tool execution check
328
+ if (chunk.message) {
329
+ responseMessages.push(chunk.message);
330
+ }
331
+ }
332
+
333
+ if (context.debug) {
334
+ console.info('TenxGraphClient: Stream iteration completed');
335
+ console.debug('TenxGraphClient: Received', responseMessages.length, 'messages');
336
+ }
337
+
338
+ // Check if there are remote tool calls to execute
339
+ const hasToolCalls = hasRemoteToolCalls(responseMessages);
340
+
341
+ if (hasToolCalls && context.toolExecutor) {
342
+ if (context.debug) {
343
+ console.debug('TenxGraphClient: Found remote tool calls, executing...');
344
+ }
345
+
346
+ // Execute all tool calls using the ToolExecutor
347
+ const toolResults = await context.toolExecutor.executeToolCalls(responseMessages);
348
+
349
+ if (context.debug) {
350
+ console.debug(`TenxGraphClient: Executed ${toolResults.length} tool calls`);
351
+ }
352
+
353
+ // Serialize tool results for next iteration
354
+ currentMessages = toolResults.map((msg) => serializeMessage(msg));
355
+
356
+ // Continue the loop with tool results
357
+ continue;
358
+ } else {
359
+ // No more tool calls, we're done
360
+ if (context.debug) {
361
+ console.debug('TenxGraphClient: No remote tool calls found, finishing');
362
+ }
363
+ break;
364
+ }
365
+ } catch (error) {
366
+ clearTimeout(timeoutId);
367
+
368
+ if ((error as Error).name === 'AbortError') {
369
+ console.warn(`TenxGraphClient: Stream timeout after ${context.timeout}ms`);
370
+ throw new Error(`Request timeout after ${context.timeout}ms`);
371
+ }
372
+
373
+ console.error('TenxGraphClient: Stream failed:', error);
374
+ throw error;
375
+ }
376
+ }
377
+
378
+ // Check if we hit the recursion limit
379
+ if (iterations >= recursion_limit) {
380
+ if (context.debug) {
381
+ console.warn(`TenxGraphClient: Recursion limit of ${recursion_limit} reached`);
382
+ }
383
+ }
384
+
385
+ if (context.debug) {
386
+ console.debug(`TenxGraphClient: Stream invoke completed after ${iterations} iterations`);
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Clean content blocks by removing empty arrays and undefined values
392
+ * Server requires array of content blocks, not simplified string
393
+ */
394
+ function cleanContent(content: any[]): any[] {
395
+ // Always return as array of blocks (server requires this format)
396
+ return content.map((block) => {
397
+ const cleaned: any = {};
398
+
399
+ for (const [key, value] of Object.entries(block)) {
400
+ // Skip empty arrays
401
+ if (Array.isArray(value) && value.length === 0) {
402
+ continue;
403
+ }
404
+ // Skip undefined values
405
+ if (value === undefined) {
406
+ continue;
407
+ }
408
+ cleaned[key] = value;
409
+ }
410
+
411
+ return cleaned;
412
+ });
413
+ }
414
+
415
+ /**
416
+ * Serialize a Message object for API transmission
417
+ */
418
+ export function serializeMessage(message: Message): any {
419
+ const serialized: any = {
420
+ role: message.role,
421
+ content: cleanContent(message.content),
422
+ };
423
+
424
+ // message_id: use "0" if not set (server will generate one)
425
+ if (message.message_id !== null && message.message_id !== undefined) {
426
+ serialized.message_id = String(message.message_id);
427
+ } else {
428
+ serialized.message_id = '0';
429
+ }
430
+
431
+ // Only include optional fields if they are explicitly set
432
+ if (message.tools_calls) {
433
+ serialized.tools_calls = message.tools_calls;
434
+ }
435
+ if (message.metadata && Object.keys(message.metadata).length > 0) {
436
+ serialized.metadata = message.metadata;
437
+ }
438
+
439
+ return serialized;
440
+ }
@@ -0,0 +1,78 @@
1
+ import { ResponseMetadata } from './metadata.js';
2
+ import { createErrorFromResponse } from '../errors.js';
3
+ import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
4
+
5
+ export interface ThreadDetailsContext extends RequestContext {}
6
+
7
+ export interface ThreadDetailsData {
8
+ thread_data: {
9
+ thread: Record<string, any>;
10
+ };
11
+ }
12
+
13
+ export interface ThreadDetailsResponse {
14
+ data: ThreadDetailsData;
15
+ metadata: ResponseMetadata;
16
+ }
17
+
18
+ /**
19
+ * Fetch details for a specific thread.
20
+ * GET /v1/threads/{threadId}
21
+ */
22
+ export async function threadDetails(
23
+ context: ThreadDetailsContext,
24
+ threadId: string | number
25
+ ): Promise<ThreadDetailsResponse> {
26
+ try {
27
+ if (context.debug) {
28
+ console.debug(`TenxGraphClient: Fetching thread details for thread ${threadId}`);
29
+ }
30
+
31
+ const controller = new AbortController();
32
+ const timeoutId = setTimeout(() => controller.abort(), context.timeout);
33
+
34
+ const url = `${context.baseUrl}/v1/threads/${threadId}`;
35
+
36
+ const response = await fetch(url, {
37
+ method: 'GET',
38
+ headers: buildHeaders(context as RequestContext, {
39
+ 'Content-Type': 'application/json',
40
+ accept: 'application/json',
41
+ }),
42
+ ...getRequestCredentials(context as RequestContext),
43
+ signal: controller.signal,
44
+ });
45
+
46
+ clearTimeout(timeoutId);
47
+
48
+ if (!response.ok) {
49
+ console.warn(`TenxGraphClient: Thread details fetch failed with HTTP ${response.status}`);
50
+ const error = await createErrorFromResponse(
51
+ response,
52
+ 'Thread details fetch failed',
53
+ '/v1/threads/{thread_id}',
54
+ 'GET'
55
+ );
56
+ throw error;
57
+ }
58
+
59
+ const data: ThreadDetailsResponse = await response.json();
60
+
61
+ if (context.debug) {
62
+ console.info('TenxGraphClient: Thread details fetched successfully', data);
63
+ }
64
+
65
+ return data;
66
+ } catch (error) {
67
+ if ((error as Error).name === 'AbortError') {
68
+ console.warn(`TenxGraphClient: Thread details fetch timeout after ${context.timeout}ms`);
69
+ throw new Error(`Request timeout after ${context.timeout}ms`);
70
+ }
71
+
72
+ if (context.debug) {
73
+ console.debug('TenxGraphClient: Thread details fetch failed:', error);
74
+ }
75
+
76
+ throw error;
77
+ }
78
+ }
@@ -0,0 +1,76 @@
1
+ import { ResponseMetadata } from './metadata.js';
2
+ import { Message } from '../message.js';
3
+ import { createErrorFromResponse } from '../errors.js';
4
+ import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
5
+
6
+ export interface ThreadMessageContext extends RequestContext {}
7
+
8
+ export interface ThreadMessageRequest {
9
+ threadId: string | number;
10
+ messageId: string;
11
+ }
12
+
13
+ export interface ThreadMessageData extends Message {
14
+ // This extends the Message class to ensure type compatibility
15
+ // The API returns a message object directly as data
16
+ }
17
+
18
+ export interface ThreadMessageResponse {
19
+ data: ThreadMessageData;
20
+ metadata: ResponseMetadata;
21
+ }
22
+
23
+ export async function threadMessage(
24
+ context: ThreadMessageContext,
25
+ request: ThreadMessageRequest
26
+ ): Promise<ThreadMessageResponse> {
27
+ try {
28
+ if (context.debug) {
29
+ console.debug(
30
+ 'TenxGraphClient: Fetching thread message',
31
+ `thread: ${request.threadId}`,
32
+ `message: ${request.messageId}`
33
+ );
34
+ }
35
+
36
+ const controller = new AbortController();
37
+ const timeoutId = setTimeout(() => controller.abort(), context.timeout);
38
+
39
+ const url = `${context.baseUrl}/v1/threads/${request.threadId}/messages/${request.messageId}`;
40
+
41
+ const response = await fetch(url, {
42
+ method: 'GET',
43
+ headers: buildHeaders(context as RequestContext, {
44
+ 'Content-Type': 'application/json',
45
+ }),
46
+ ...getRequestCredentials(context as RequestContext),
47
+ signal: controller.signal,
48
+ });
49
+
50
+ clearTimeout(timeoutId);
51
+
52
+ if (!response.ok) {
53
+ console.warn(`TenxGraphClient: Thread message fetch failed with HTTP ${response.status}`);
54
+ const error = await createErrorFromResponse(
55
+ response,
56
+ 'Thread message fetch failed',
57
+ '/v1/threads/{thread_id}/messages/{message_id}',
58
+ 'GET'
59
+ );
60
+ throw error;
61
+ }
62
+
63
+ const data: ThreadMessageResponse = await response.json();
64
+
65
+ if (context.debug) {
66
+ console.info('TenxGraphClient: Thread message fetched successfully', data);
67
+ }
68
+
69
+ return data;
70
+ } catch (error) {
71
+ if (context.debug) {
72
+ console.debug('TenxGraphClient: Thread message fetch failed:', error);
73
+ }
74
+ throw error;
75
+ }
76
+ }