@inkeep/agents-run-api 0.0.0-dev-20250910232631

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 (152) hide show
  1. package/LICENSE.md +49 -0
  2. package/README.md +117 -0
  3. package/dist/__tests__/setup.d.ts +4 -0
  4. package/dist/__tests__/setup.d.ts.map +1 -0
  5. package/dist/__tests__/setup.js +80 -0
  6. package/dist/__tests__/utils/testProject.d.ts +18 -0
  7. package/dist/__tests__/utils/testProject.d.ts.map +1 -0
  8. package/dist/__tests__/utils/testProject.js +26 -0
  9. package/dist/__tests__/utils/testRequest.d.ts +8 -0
  10. package/dist/__tests__/utils/testRequest.d.ts.map +1 -0
  11. package/dist/__tests__/utils/testRequest.js +32 -0
  12. package/dist/__tests__/utils/testTenant.d.ts +64 -0
  13. package/dist/__tests__/utils/testTenant.d.ts.map +1 -0
  14. package/dist/__tests__/utils/testTenant.js +71 -0
  15. package/dist/a2a/client.d.ts +182 -0
  16. package/dist/a2a/client.d.ts.map +1 -0
  17. package/dist/a2a/client.js +645 -0
  18. package/dist/a2a/handlers.d.ts +4 -0
  19. package/dist/a2a/handlers.d.ts.map +1 -0
  20. package/dist/a2a/handlers.js +656 -0
  21. package/dist/a2a/transfer.d.ts +18 -0
  22. package/dist/a2a/transfer.d.ts.map +1 -0
  23. package/dist/a2a/transfer.js +22 -0
  24. package/dist/a2a/types.d.ts +63 -0
  25. package/dist/a2a/types.d.ts.map +1 -0
  26. package/dist/a2a/types.js +1 -0
  27. package/dist/agents/Agent.d.ts +151 -0
  28. package/dist/agents/Agent.d.ts.map +1 -0
  29. package/dist/agents/Agent.js +1164 -0
  30. package/dist/agents/ModelFactory.d.ts +62 -0
  31. package/dist/agents/ModelFactory.d.ts.map +1 -0
  32. package/dist/agents/ModelFactory.js +208 -0
  33. package/dist/agents/SystemPromptBuilder.d.ts +14 -0
  34. package/dist/agents/SystemPromptBuilder.d.ts.map +1 -0
  35. package/dist/agents/SystemPromptBuilder.js +62 -0
  36. package/dist/agents/ToolSessionManager.d.ts +53 -0
  37. package/dist/agents/ToolSessionManager.d.ts.map +1 -0
  38. package/dist/agents/ToolSessionManager.js +106 -0
  39. package/dist/agents/artifactTools.d.ts +30 -0
  40. package/dist/agents/artifactTools.d.ts.map +1 -0
  41. package/dist/agents/artifactTools.js +463 -0
  42. package/dist/agents/generateTaskHandler.d.ts +41 -0
  43. package/dist/agents/generateTaskHandler.d.ts.map +1 -0
  44. package/dist/agents/generateTaskHandler.js +350 -0
  45. package/dist/agents/relationTools.d.ts +35 -0
  46. package/dist/agents/relationTools.d.ts.map +1 -0
  47. package/dist/agents/relationTools.js +246 -0
  48. package/dist/agents/types.d.ts +23 -0
  49. package/dist/agents/types.d.ts.map +1 -0
  50. package/dist/agents/types.js +1 -0
  51. package/dist/agents/versions/V1Config.d.ts +21 -0
  52. package/dist/agents/versions/V1Config.d.ts.map +1 -0
  53. package/dist/agents/versions/V1Config.js +285 -0
  54. package/dist/app.d.ts +5 -0
  55. package/dist/app.d.ts.map +1 -0
  56. package/dist/app.js +219 -0
  57. package/dist/data/agentGraph.d.ts +4 -0
  58. package/dist/data/agentGraph.d.ts.map +1 -0
  59. package/dist/data/agentGraph.js +73 -0
  60. package/dist/data/agents.d.ts +4 -0
  61. package/dist/data/agents.d.ts.map +1 -0
  62. package/dist/data/agents.js +78 -0
  63. package/dist/data/conversations.d.ts +59 -0
  64. package/dist/data/conversations.d.ts.map +1 -0
  65. package/dist/data/conversations.js +216 -0
  66. package/dist/data/db/clean.d.ts +6 -0
  67. package/dist/data/db/clean.d.ts.map +1 -0
  68. package/dist/data/db/clean.js +77 -0
  69. package/dist/data/db/dbClient.d.ts +3 -0
  70. package/dist/data/db/dbClient.d.ts.map +1 -0
  71. package/dist/data/db/dbClient.js +13 -0
  72. package/dist/env.d.ts +45 -0
  73. package/dist/env.d.ts.map +1 -0
  74. package/dist/env.js +64 -0
  75. package/dist/handlers/executionHandler.d.ts +36 -0
  76. package/dist/handlers/executionHandler.d.ts.map +1 -0
  77. package/dist/handlers/executionHandler.js +415 -0
  78. package/dist/index.d.ts +11 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +28 -0
  81. package/dist/instrumentation.d.ts +13 -0
  82. package/dist/instrumentation.d.ts.map +1 -0
  83. package/dist/instrumentation.js +66 -0
  84. package/dist/logger.d.ts +4 -0
  85. package/dist/logger.d.ts.map +1 -0
  86. package/dist/logger.js +32 -0
  87. package/dist/middleware/api-key-auth.d.ts +22 -0
  88. package/dist/middleware/api-key-auth.d.ts.map +1 -0
  89. package/dist/middleware/api-key-auth.js +139 -0
  90. package/dist/middleware/index.d.ts +2 -0
  91. package/dist/middleware/index.d.ts.map +1 -0
  92. package/dist/middleware/index.js +1 -0
  93. package/dist/openapi.d.ts +2 -0
  94. package/dist/openapi.d.ts.map +1 -0
  95. package/dist/openapi.js +36 -0
  96. package/dist/routes/agents.d.ts +10 -0
  97. package/dist/routes/agents.d.ts.map +1 -0
  98. package/dist/routes/agents.js +158 -0
  99. package/dist/routes/chat.d.ts +10 -0
  100. package/dist/routes/chat.d.ts.map +1 -0
  101. package/dist/routes/chat.js +307 -0
  102. package/dist/routes/chatDataStream.d.ts +10 -0
  103. package/dist/routes/chatDataStream.d.ts.map +1 -0
  104. package/dist/routes/chatDataStream.js +185 -0
  105. package/dist/routes/mcp.d.ts +10 -0
  106. package/dist/routes/mcp.d.ts.map +1 -0
  107. package/dist/routes/mcp.js +500 -0
  108. package/dist/tracer.d.ts +24 -0
  109. package/dist/tracer.d.ts.map +1 -0
  110. package/dist/tracer.js +107 -0
  111. package/dist/types/chat.d.ts +25 -0
  112. package/dist/types/chat.d.ts.map +1 -0
  113. package/dist/types/chat.js +1 -0
  114. package/dist/types/execution-context.d.ts +14 -0
  115. package/dist/types/execution-context.d.ts.map +1 -0
  116. package/dist/types/execution-context.js +14 -0
  117. package/dist/utils/agent-operations.d.ts +93 -0
  118. package/dist/utils/agent-operations.d.ts.map +1 -0
  119. package/dist/utils/agent-operations.js +78 -0
  120. package/dist/utils/artifact-component-schema.d.ts +29 -0
  121. package/dist/utils/artifact-component-schema.d.ts.map +1 -0
  122. package/dist/utils/artifact-component-schema.js +119 -0
  123. package/dist/utils/artifact-parser.d.ts +71 -0
  124. package/dist/utils/artifact-parser.d.ts.map +1 -0
  125. package/dist/utils/artifact-parser.js +253 -0
  126. package/dist/utils/cleanup.d.ts +19 -0
  127. package/dist/utils/cleanup.d.ts.map +1 -0
  128. package/dist/utils/cleanup.js +66 -0
  129. package/dist/utils/data-component-schema.d.ts +6 -0
  130. package/dist/utils/data-component-schema.d.ts.map +1 -0
  131. package/dist/utils/data-component-schema.js +43 -0
  132. package/dist/utils/graph-session.d.ts +230 -0
  133. package/dist/utils/graph-session.d.ts.map +1 -0
  134. package/dist/utils/graph-session.js +1199 -0
  135. package/dist/utils/incremental-stream-parser.d.ts +62 -0
  136. package/dist/utils/incremental-stream-parser.d.ts.map +1 -0
  137. package/dist/utils/incremental-stream-parser.js +330 -0
  138. package/dist/utils/response-formatter.d.ts +26 -0
  139. package/dist/utils/response-formatter.d.ts.map +1 -0
  140. package/dist/utils/response-formatter.js +158 -0
  141. package/dist/utils/stream-helpers.d.ts +186 -0
  142. package/dist/utils/stream-helpers.d.ts.map +1 -0
  143. package/dist/utils/stream-helpers.js +603 -0
  144. package/dist/utils/stream-registry.d.ts +18 -0
  145. package/dist/utils/stream-registry.d.ts.map +1 -0
  146. package/dist/utils/stream-registry.js +33 -0
  147. package/package.json +95 -0
  148. package/templates/v1/artifact.xml +7 -0
  149. package/templates/v1/data-component.xml +9 -0
  150. package/templates/v1/system-prompt.xml +52 -0
  151. package/templates/v1/thinking-preparation.xml +34 -0
  152. package/templates/v1/tool.xml +12 -0
@@ -0,0 +1,62 @@
1
+ import { type StreamPart } from './artifact-parser';
2
+ import type { StreamHelper } from './stream-helpers';
3
+ /**
4
+ * Incremental parser that processes streaming text and formats artifacts/objects as they become complete
5
+ * Uses the unified ArtifactParser to eliminate redundancy
6
+ */
7
+ export declare class IncrementalStreamParser {
8
+ private buffer;
9
+ private pendingTextBuffer;
10
+ private streamHelper;
11
+ private artifactParser;
12
+ private hasStartedRole;
13
+ private collectedParts;
14
+ private contextId;
15
+ private lastChunkWasToolResult;
16
+ constructor(streamHelper: StreamHelper, tenantId: string, contextId: string);
17
+ /**
18
+ * Mark that a tool result just completed, so next text should have spacing
19
+ */
20
+ markToolResult(): void;
21
+ /**
22
+ * Process a new text chunk for text streaming (handles artifact markers)
23
+ */
24
+ processTextChunk(chunk: string): Promise<void>;
25
+ /**
26
+ * Process a new object chunk for object streaming (handles JSON objects with artifact references)
27
+ */
28
+ processObjectChunk(chunk: string): Promise<void>;
29
+ /**
30
+ * Process tool call stream for structured output, streaming components as they complete
31
+ */
32
+ processToolCallStream(stream: AsyncIterable<any>, targetToolName: string): Promise<void>;
33
+ /**
34
+ * Legacy method for backward compatibility - defaults to text processing
35
+ */
36
+ processChunk(chunk: string): Promise<void>;
37
+ /**
38
+ * Process any remaining buffer content at the end of stream
39
+ */
40
+ finalize(): Promise<void>;
41
+ /**
42
+ * Get all collected parts for building the final response
43
+ */
44
+ getCollectedParts(): StreamPart[];
45
+ /**
46
+ * Parse buffer for complete artifacts and text parts (for text streaming)
47
+ */
48
+ private parseTextBuffer;
49
+ /**
50
+ * Parse buffer for complete JSON objects with artifact references (for object streaming)
51
+ */
52
+ private parseObjectBuffer;
53
+ /**
54
+ * Check if text might be the start of an artifact marker
55
+ */
56
+ private mightBeArtifactStart;
57
+ /**
58
+ * Stream a formatted part (text or data) with smart buffering
59
+ */
60
+ private streamPart;
61
+ }
62
+ //# sourceMappingURL=incremental-stream-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-stream-parser.d.ts","sourceRoot":"","sources":["../../src/utils/incremental-stream-parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AASrD;;;GAGG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,cAAc,CAAoB;IAC1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,sBAAsB,CAAS;gBAE3B,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAM3E;;OAEG;IACH,cAAc,IAAI,IAAI;IAItB;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpD;;OAEG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IActD;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8G9F;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC/B;;OAEG;IACH,iBAAiB,IAAI,UAAU,EAAE;IAIjC;;OAEG;YACW,eAAe;IAoD7B;;OAEG;YACW,iBAAiB;IA4B/B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;OAEG;YACW,UAAU;CAqDzB"}
@@ -0,0 +1,330 @@
1
+ import { getLogger } from '../logger';
2
+ import { ArtifactParser } from './artifact-parser';
3
+ const logger = getLogger('IncrementalStreamParser');
4
+ /**
5
+ * Incremental parser that processes streaming text and formats artifacts/objects as they become complete
6
+ * Uses the unified ArtifactParser to eliminate redundancy
7
+ */
8
+ export class IncrementalStreamParser {
9
+ buffer = '';
10
+ pendingTextBuffer = '';
11
+ streamHelper;
12
+ artifactParser;
13
+ hasStartedRole = false;
14
+ collectedParts = [];
15
+ contextId;
16
+ lastChunkWasToolResult = false;
17
+ constructor(streamHelper, tenantId, contextId) {
18
+ this.streamHelper = streamHelper;
19
+ this.contextId = contextId;
20
+ this.artifactParser = new ArtifactParser(tenantId);
21
+ }
22
+ /**
23
+ * Mark that a tool result just completed, so next text should have spacing
24
+ */
25
+ markToolResult() {
26
+ this.lastChunkWasToolResult = true;
27
+ }
28
+ /**
29
+ * Process a new text chunk for text streaming (handles artifact markers)
30
+ */
31
+ async processTextChunk(chunk) {
32
+ // If this text follows a tool result and we haven't added any text yet, add spacing
33
+ if (this.lastChunkWasToolResult && this.buffer === '' && chunk.trim()) {
34
+ chunk = '\n\n' + chunk;
35
+ this.lastChunkWasToolResult = false;
36
+ }
37
+ this.buffer += chunk;
38
+ const parseResult = await this.parseTextBuffer();
39
+ // Stream complete parts
40
+ for (const part of parseResult.completeParts) {
41
+ await this.streamPart(part);
42
+ }
43
+ // Update buffer with remaining content
44
+ this.buffer = parseResult.remainingBuffer;
45
+ }
46
+ /**
47
+ * Process a new object chunk for object streaming (handles JSON objects with artifact references)
48
+ */
49
+ async processObjectChunk(chunk) {
50
+ this.buffer += chunk;
51
+ const parseResult = await this.parseObjectBuffer();
52
+ // Stream complete parts
53
+ for (const part of parseResult.completeParts) {
54
+ await this.streamPart(part);
55
+ }
56
+ // Update buffer with remaining content
57
+ this.buffer = parseResult.remainingBuffer;
58
+ }
59
+ /**
60
+ * Process tool call stream for structured output, streaming components as they complete
61
+ */
62
+ async processToolCallStream(stream, targetToolName) {
63
+ let jsonBuffer = '';
64
+ let componentBuffer = '';
65
+ let depth = 0;
66
+ let _inDataComponents = false;
67
+ let componentsStreamed = 0;
68
+ const MAX_BUFFER_SIZE = 5 * 1024 * 1024; // 5MB max buffer size
69
+ for await (const part of stream) {
70
+ // Look for tool call deltas with incremental JSON
71
+ if (part.type === 'tool-call-delta' && part.toolName === targetToolName) {
72
+ const delta = part.argsTextDelta || '';
73
+ // Prevent JSON buffer from growing too large
74
+ if (jsonBuffer.length + delta.length > MAX_BUFFER_SIZE) {
75
+ logger.warn('JSON buffer exceeded maximum size, truncating');
76
+ jsonBuffer = jsonBuffer.slice(-MAX_BUFFER_SIZE / 2); // Keep last half
77
+ }
78
+ jsonBuffer += delta;
79
+ // Parse character by character to detect complete components
80
+ for (const char of delta) {
81
+ // Prevent component buffer from growing too large
82
+ if (componentBuffer.length > MAX_BUFFER_SIZE) {
83
+ logger.warn('Component buffer exceeded maximum size, resetting');
84
+ componentBuffer = '';
85
+ depth = 0; // Reset depth tracking
86
+ continue;
87
+ }
88
+ componentBuffer += char;
89
+ // Track JSON depth
90
+ if (char === '{') {
91
+ depth++;
92
+ }
93
+ else if (char === '}') {
94
+ depth--;
95
+ // At depth 2, we're inside the dataComponents array
96
+ // When we return to depth 2, we have a complete component
97
+ if (depth === 2 && componentBuffer.includes('"id"')) {
98
+ // Extract just the component object with size limit
99
+ const componentMatch = componentBuffer.match(/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/);
100
+ if (componentMatch) {
101
+ // Size limit check - prevent parsing extremely large JSON objects
102
+ const MAX_COMPONENT_SIZE = 1024 * 1024; // 1MB limit per component
103
+ if (componentMatch[0].length > MAX_COMPONENT_SIZE) {
104
+ logger.warn({
105
+ size: componentMatch[0].length,
106
+ maxSize: MAX_COMPONENT_SIZE,
107
+ }, 'Component exceeds size limit, skipping');
108
+ componentBuffer = ''; // Reset and skip this component
109
+ continue;
110
+ }
111
+ try {
112
+ const component = JSON.parse(componentMatch[0]);
113
+ // Validate component structure before processing
114
+ if (typeof component !== 'object' || !component.id) {
115
+ logger.warn('Invalid component structure, skipping');
116
+ componentBuffer = '';
117
+ continue;
118
+ }
119
+ // Stream this individual component
120
+ const parts = await this.artifactParser.parseObject({
121
+ dataComponents: [component],
122
+ });
123
+ for (const part of parts) {
124
+ await this.streamPart(part);
125
+ }
126
+ componentsStreamed++;
127
+ componentBuffer = ''; // Reset for next component
128
+ }
129
+ catch (e) {
130
+ // Not valid JSON yet, keep accumulating
131
+ logger.debug({ error: e }, 'Failed to parse component, continuing to accumulate');
132
+ }
133
+ }
134
+ }
135
+ }
136
+ // Detect when we enter dataComponents array
137
+ if (componentBuffer.includes('"dataComponents"') && componentBuffer.includes('[')) {
138
+ _inDataComponents = true;
139
+ }
140
+ }
141
+ }
142
+ // Alternative: if the SDK provides complete tool calls (not deltas)
143
+ else if (part.type === 'tool-call' && part.toolName === targetToolName) {
144
+ // Process the complete args at once
145
+ if (part.args?.dataComponents) {
146
+ const parts = await this.artifactParser.parseObject(part.args);
147
+ for (const part of parts) {
148
+ await this.streamPart(part);
149
+ }
150
+ }
151
+ break; // Tool call complete
152
+ }
153
+ }
154
+ logger.debug({ componentsStreamed }, 'Finished streaming components');
155
+ }
156
+ /**
157
+ * Legacy method for backward compatibility - defaults to text processing
158
+ */
159
+ async processChunk(chunk) {
160
+ await this.processTextChunk(chunk);
161
+ }
162
+ /**
163
+ * Process any remaining buffer content at the end of stream
164
+ */
165
+ async finalize() {
166
+ if (this.buffer.trim()) {
167
+ // Process remaining buffer as final text
168
+ const part = {
169
+ kind: 'text',
170
+ text: this.buffer.trim(),
171
+ };
172
+ await this.streamPart(part);
173
+ }
174
+ // Flush any remaining buffered text
175
+ if (this.pendingTextBuffer.trim()) {
176
+ // Clean up any artifact-related tags or remnants before final flush
177
+ // Use safe, non-backtracking regex patterns to prevent ReDoS attacks
178
+ const cleanedText = this.pendingTextBuffer
179
+ .replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, '') // Remove artifact:ref tags safely
180
+ .replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, '') // Remove artifact tags safely
181
+ .replace(/<\/(?:\w+:)?artifact>/g, '') // Remove closing artifact tags safely
182
+ .trim();
183
+ if (cleanedText) {
184
+ this.collectedParts.push({
185
+ kind: 'text',
186
+ text: cleanedText,
187
+ });
188
+ await this.streamHelper.streamText(cleanedText, 50);
189
+ }
190
+ this.pendingTextBuffer = '';
191
+ }
192
+ }
193
+ /**
194
+ * Get all collected parts for building the final response
195
+ */
196
+ getCollectedParts() {
197
+ return [...this.collectedParts];
198
+ }
199
+ /**
200
+ * Parse buffer for complete artifacts and text parts (for text streaming)
201
+ */
202
+ async parseTextBuffer() {
203
+ const completeParts = [];
204
+ const workingBuffer = this.buffer;
205
+ // Check if we have incomplete artifact markers
206
+ if (this.artifactParser.hasIncompleteArtifact(workingBuffer)) {
207
+ // Find safe boundary to stream text before incomplete artifact
208
+ const safeEnd = this.artifactParser.findSafeTextBoundary(workingBuffer);
209
+ if (safeEnd > 0) {
210
+ const safeText = workingBuffer.slice(0, safeEnd);
211
+ // Parse the safe portion for complete artifacts
212
+ const parts = await this.artifactParser.parseText(safeText);
213
+ completeParts.push(...parts);
214
+ return {
215
+ completeParts,
216
+ remainingBuffer: workingBuffer.slice(safeEnd),
217
+ };
218
+ }
219
+ // Buffer contains only incomplete artifact
220
+ return {
221
+ completeParts: [],
222
+ remainingBuffer: workingBuffer,
223
+ };
224
+ }
225
+ // No incomplete artifacts, parse the entire buffer
226
+ const parts = await this.artifactParser.parseText(workingBuffer);
227
+ // Check last part - if it's text, it might be incomplete
228
+ if (parts.length > 0 && parts[parts.length - 1].kind === 'text') {
229
+ const lastPart = parts[parts.length - 1];
230
+ const lastText = lastPart.text || '';
231
+ // Keep some text in buffer if it might be start of artifact
232
+ if (this.mightBeArtifactStart(lastText)) {
233
+ parts.pop(); // Remove last text part
234
+ return {
235
+ completeParts: parts,
236
+ remainingBuffer: lastText,
237
+ };
238
+ }
239
+ }
240
+ return {
241
+ completeParts: parts,
242
+ remainingBuffer: '',
243
+ };
244
+ }
245
+ /**
246
+ * Parse buffer for complete JSON objects with artifact references (for object streaming)
247
+ */
248
+ async parseObjectBuffer() {
249
+ const completeParts = [];
250
+ try {
251
+ // Try to parse as complete JSON
252
+ const parsed = JSON.parse(this.buffer);
253
+ const parts = await this.artifactParser.parseObject(parsed);
254
+ return {
255
+ completeParts: parts,
256
+ remainingBuffer: '',
257
+ };
258
+ }
259
+ catch {
260
+ // JSON is incomplete, try partial parsing
261
+ const { complete, remaining } = this.artifactParser.parsePartialJSON(this.buffer);
262
+ for (const obj of complete) {
263
+ const parts = await this.artifactParser.parseObject(obj);
264
+ completeParts.push(...parts);
265
+ }
266
+ return {
267
+ completeParts,
268
+ remainingBuffer: remaining,
269
+ };
270
+ }
271
+ }
272
+ /**
273
+ * Check if text might be the start of an artifact marker
274
+ */
275
+ mightBeArtifactStart(text) {
276
+ const lastChars = text.slice(-20); // Check last 20 chars
277
+ return lastChars.includes('<') && !lastChars.includes('/>');
278
+ }
279
+ /**
280
+ * Stream a formatted part (text or data) with smart buffering
281
+ */
282
+ async streamPart(part) {
283
+ // Collect for final response
284
+ this.collectedParts.push({ ...part });
285
+ if (!this.hasStartedRole) {
286
+ await this.streamHelper.writeRole('assistant');
287
+ this.hasStartedRole = true;
288
+ }
289
+ if (part.kind === 'text' && part.text) {
290
+ // Add to pending buffer
291
+ this.pendingTextBuffer += part.text;
292
+ // Flush if safe to do so
293
+ if (!this.artifactParser.hasIncompleteArtifact(this.pendingTextBuffer)) {
294
+ // Clean up any artifact-related tags or remnants before flushing
295
+ // Use safe, non-backtracking regex patterns to prevent ReDoS attacks
296
+ const cleanedText = this.pendingTextBuffer
297
+ .replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, '') // Remove artifact:ref tags safely
298
+ .replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, '') // Remove artifact tags safely
299
+ .replace(/<\/(?:\w+:)?artifact>/g, ''); // Remove closing artifact tags safely
300
+ if (cleanedText.trim()) {
301
+ await this.streamHelper.streamText(cleanedText, 50);
302
+ }
303
+ this.pendingTextBuffer = '';
304
+ }
305
+ }
306
+ else if (part.kind === 'data' && part.data) {
307
+ // Flush any pending text before streaming data
308
+ if (this.pendingTextBuffer) {
309
+ // Clean up any artifact-related tags or remnants before flushing
310
+ // Use safe, non-backtracking regex patterns to prevent ReDoS attacks
311
+ const cleanedText = this.pendingTextBuffer
312
+ .replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, '') // Remove artifact:ref tags safely
313
+ .replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, '') // Remove artifact tags safely
314
+ .replace(/<\/(?:\w+:)?artifact>/g, ''); // Remove closing artifact tags safely
315
+ if (cleanedText.trim()) {
316
+ await this.streamHelper.streamText(cleanedText, 50);
317
+ }
318
+ this.pendingTextBuffer = '';
319
+ }
320
+ // Determine if this is an artifact or regular data component
321
+ const isArtifact = part.data.artifactId && part.data.taskId;
322
+ if (isArtifact) {
323
+ await this.streamHelper.writeData('data-artifact', part.data);
324
+ }
325
+ else {
326
+ await this.streamHelper.writeData('data-component', part.data);
327
+ }
328
+ }
329
+ }
330
+ }
@@ -0,0 +1,26 @@
1
+ import type { MessageContent } from '@inkeep/agents-core';
2
+ /**
3
+ * Response formatter that uses the unified ArtifactParser to convert artifact markers
4
+ * into data parts for consistent artifact handling across all agent responses
5
+ */
6
+ export declare class ResponseFormatter {
7
+ private artifactParser;
8
+ constructor(tenantId: string);
9
+ /**
10
+ * Process structured object response and replace artifact markers with actual artifacts
11
+ */
12
+ formatObjectResponse(responseObject: any, contextId: string): Promise<MessageContent>;
13
+ /**
14
+ * Process agent response and convert artifact markers to data parts
15
+ */
16
+ formatResponse(responseText: string, contextId: string): Promise<MessageContent>;
17
+ /**
18
+ * Count unique artifacts in parts array
19
+ */
20
+ private countUniqueArtifacts;
21
+ /**
22
+ * Log artifacts found in parts to span
23
+ */
24
+ private logArtifacts;
25
+ }
26
+ //# sourceMappingURL=response-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-formatter.d.ts","sourceRoot":"","sources":["../../src/utils/response-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAgB1D;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,cAAc,CAAiB;gBAE3B,QAAQ,EAAE,MAAM;IAI5B;;OAEG;IACG,oBAAoB,CAAC,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA0C3F;;OAEG;IACG,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAgEtF;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;OAEG;IACH,OAAO,CAAC,YAAY;CAwBrB"}
@@ -0,0 +1,158 @@
1
+ import { trace } from '@opentelemetry/api';
2
+ import { getLogger } from '../logger';
3
+ import { SERVICE_VERSION } from '../tracer';
4
+ import { ArtifactParser } from './artifact-parser';
5
+ const logger = getLogger('ResponseFormatter');
6
+ // Service name for response formatter tracer
7
+ const RESPONSE_FORMATTER_SERVICE = 'responseFormatter';
8
+ function getResponseFormatterTracer() {
9
+ const tracerProvider = trace.getTracerProvider();
10
+ return tracerProvider.getTracer(RESPONSE_FORMATTER_SERVICE, SERVICE_VERSION);
11
+ }
12
+ /**
13
+ * Response formatter that uses the unified ArtifactParser to convert artifact markers
14
+ * into data parts for consistent artifact handling across all agent responses
15
+ */
16
+ export class ResponseFormatter {
17
+ artifactParser;
18
+ constructor(tenantId) {
19
+ this.artifactParser = new ArtifactParser(tenantId);
20
+ }
21
+ /**
22
+ * Process structured object response and replace artifact markers with actual artifacts
23
+ */
24
+ async formatObjectResponse(responseObject, contextId) {
25
+ const tracer = getResponseFormatterTracer();
26
+ return tracer.startActiveSpan('response.formatObject', async (span) => {
27
+ try {
28
+ // Get all artifacts available in this context
29
+ const artifactMap = await this.artifactParser.getContextArtifacts(contextId);
30
+ span.setAttributes({
31
+ 'response.type': 'object',
32
+ 'response.availableArtifacts': artifactMap.size,
33
+ });
34
+ // Parse the object using unified parser
35
+ const parts = await this.artifactParser.parseObject(responseObject, artifactMap);
36
+ // Count and log metrics
37
+ const uniqueArtifacts = this.countUniqueArtifacts(parts);
38
+ span.setAttributes({
39
+ 'response.dataPartsCount': parts.length,
40
+ 'response.artifactsCount': uniqueArtifacts,
41
+ 'response.totalPartsCount': parts.length,
42
+ });
43
+ this.logArtifacts(span, parts);
44
+ span.addEvent('response.parts.final_output', {
45
+ final_parts_array: JSON.stringify(parts, null, 2),
46
+ });
47
+ return { parts };
48
+ }
49
+ catch (error) {
50
+ span.recordException(error);
51
+ logger.error({ error, responseObject }, 'Error formatting object response');
52
+ return {
53
+ parts: [{ kind: 'data', data: responseObject }],
54
+ };
55
+ }
56
+ finally {
57
+ span.end();
58
+ }
59
+ });
60
+ }
61
+ /**
62
+ * Process agent response and convert artifact markers to data parts
63
+ */
64
+ async formatResponse(responseText, contextId) {
65
+ const tracer = getResponseFormatterTracer();
66
+ return tracer.startActiveSpan('response.format', async (span) => {
67
+ try {
68
+ span.setAttributes({
69
+ 'response.hasArtifactMarkers': this.artifactParser.hasArtifactMarkers(responseText),
70
+ 'response.contextId': contextId,
71
+ 'response.textLength': responseText.length,
72
+ });
73
+ // Check if the response contains artifact markers
74
+ if (!this.artifactParser.hasArtifactMarkers(responseText)) {
75
+ span.setAttributes({
76
+ 'response.result': 'no_markers_found',
77
+ });
78
+ return { parts: [{ kind: 'text', text: responseText }] };
79
+ }
80
+ // Get all artifacts available in this context
81
+ const artifactMap = await this.artifactParser.getContextArtifacts(contextId);
82
+ span.setAttributes({
83
+ 'response.type': 'text',
84
+ 'response.availableArtifacts': artifactMap.size,
85
+ });
86
+ // Parse text using unified parser
87
+ const parts = await this.artifactParser.parseText(responseText, artifactMap);
88
+ // If only one text part, return as plain text
89
+ if (parts.length === 1 && parts[0].kind === 'text') {
90
+ return { text: parts[0].text };
91
+ }
92
+ // Count and log metrics
93
+ const textParts = parts.filter((p) => p.kind === 'text').length;
94
+ const dataParts = parts.filter((p) => p.kind === 'data').length;
95
+ const uniqueArtifacts = this.countUniqueArtifacts(parts);
96
+ span.setAttributes({
97
+ 'response.textPartsCount': textParts,
98
+ 'response.dataPartsCount': dataParts,
99
+ 'response.artifactsCount': uniqueArtifacts,
100
+ 'response.totalPartsCount': parts.length,
101
+ });
102
+ this.logArtifacts(span, parts);
103
+ span.addEvent('response.parts.final_output', {
104
+ final_parts_array: JSON.stringify(parts, null, 2),
105
+ });
106
+ return { parts };
107
+ }
108
+ catch (error) {
109
+ span.recordException(error);
110
+ span.setStatus({ code: 2, message: error.message });
111
+ logger.error({ error, responseText }, 'Error formatting response');
112
+ return { text: responseText };
113
+ }
114
+ finally {
115
+ span.end();
116
+ }
117
+ });
118
+ }
119
+ /**
120
+ * Count unique artifacts in parts array
121
+ */
122
+ countUniqueArtifacts(parts) {
123
+ const uniqueKeys = new Set(parts
124
+ .filter((p) => p.kind === 'data')
125
+ .map((p) => {
126
+ const data = p.data;
127
+ if (data?.artifactId && data?.taskId) {
128
+ return `${data.artifactId}:${data.taskId}`;
129
+ }
130
+ return null;
131
+ })
132
+ .filter((key) => key !== null));
133
+ return uniqueKeys.size;
134
+ }
135
+ /**
136
+ * Log artifacts found in parts to span
137
+ */
138
+ logArtifacts(span, parts) {
139
+ const artifacts = parts
140
+ .filter((p) => p.kind === 'data')
141
+ .map((p) => {
142
+ const data = p.data;
143
+ return {
144
+ artifactId: data?.artifactId,
145
+ name: data?.name,
146
+ taskId: data?.taskId,
147
+ };
148
+ })
149
+ .filter((art) => art.artifactId && art.taskId);
150
+ if (artifacts.length > 0) {
151
+ const uniqueArtifactsList = Array.from(new Map(artifacts.map((art) => [`${art.artifactId}:${art.taskId}`, art])).values());
152
+ span.addEvent('artifacts.found', {
153
+ 'artifacts.count': uniqueArtifactsList.length,
154
+ 'artifacts.list': JSON.stringify(uniqueArtifactsList),
155
+ });
156
+ }
157
+ }
158
+ }