@inkeep/agents-run-api 0.39.5 → 0.41.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.
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/system-prompt.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/thinking-preparation.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/tool.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/data-component.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/data-components.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/system-prompt.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/shared/artifact-retrieval-guidance.js +5 -0
- package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/shared/artifact.js +5 -0
- package/dist/a2a/client.d.ts +184 -0
- package/dist/a2a/client.js +510 -0
- package/dist/a2a/handlers.d.ts +7 -0
- package/dist/a2a/handlers.js +576 -0
- package/dist/a2a/transfer.d.ts +22 -0
- package/dist/a2a/transfer.js +46 -0
- package/dist/a2a/types.d.ts +79 -0
- package/dist/a2a/types.js +22 -0
- package/dist/agents/Agent.d.ts +268 -0
- package/dist/agents/Agent.js +1932 -0
- package/dist/agents/ModelFactory.d.ts +63 -0
- package/dist/agents/ModelFactory.js +194 -0
- package/dist/agents/SystemPromptBuilder.d.ts +21 -0
- package/dist/agents/SystemPromptBuilder.js +48 -0
- package/dist/agents/ToolSessionManager.d.ts +63 -0
- package/dist/agents/ToolSessionManager.js +146 -0
- package/dist/agents/generateTaskHandler.d.ts +49 -0
- package/dist/agents/generateTaskHandler.js +523 -0
- package/dist/agents/relationTools.d.ts +57 -0
- package/dist/agents/relationTools.js +262 -0
- package/dist/agents/types.d.ts +28 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/versions/v1/Phase1Config.d.ts +27 -0
- package/dist/agents/versions/v1/Phase1Config.js +424 -0
- package/dist/agents/versions/v1/Phase2Config.d.ts +31 -0
- package/dist/agents/versions/v1/Phase2Config.js +330 -0
- package/dist/constants/execution-limits/defaults.d.ts +51 -0
- package/dist/constants/execution-limits/defaults.js +52 -0
- package/dist/constants/execution-limits/index.d.ts +6 -0
- package/dist/constants/execution-limits/index.js +21 -0
- package/dist/create-app.d.ts +9 -0
- package/dist/create-app.js +195 -0
- package/dist/data/agent.d.ts +7 -0
- package/dist/data/agent.js +72 -0
- package/dist/data/agents.d.ts +34 -0
- package/dist/data/agents.js +139 -0
- package/dist/data/conversations.d.ts +128 -0
- package/dist/data/conversations.js +522 -0
- package/dist/data/db/dbClient.d.ts +6 -0
- package/dist/data/db/dbClient.js +17 -0
- package/dist/env.d.ts +57 -0
- package/dist/env.js +1 -2
- package/dist/handlers/executionHandler.d.ts +41 -0
- package/dist/handlers/executionHandler.js +457 -0
- package/dist/index.d.ts +8 -29
- package/dist/index.js +5 -11386
- package/dist/instrumentation.d.ts +1 -2
- package/dist/instrumentation.js +66 -3
- package/dist/{logger2.js → logger.d.ts} +1 -2
- package/dist/logger.js +1 -1
- package/dist/middleware/api-key-auth.d.ts +26 -0
- package/dist/middleware/api-key-auth.js +240 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.js +3 -0
- package/dist/openapi.d.ts +4 -0
- package/dist/openapi.js +54 -0
- package/dist/routes/agents.d.ts +12 -0
- package/dist/routes/agents.js +147 -0
- package/dist/routes/chat.d.ts +13 -0
- package/dist/routes/chat.js +305 -0
- package/dist/routes/chatDataStream.d.ts +13 -0
- package/dist/routes/chatDataStream.js +365 -0
- package/dist/routes/mcp.d.ts +13 -0
- package/dist/routes/mcp.js +495 -0
- package/dist/services/AgentSession.d.ts +356 -0
- package/dist/services/AgentSession.js +1208 -0
- package/dist/services/ArtifactParser.d.ts +105 -0
- package/dist/services/ArtifactParser.js +338 -0
- package/dist/services/ArtifactService.d.ts +123 -0
- package/dist/services/ArtifactService.js +612 -0
- package/dist/services/BaseCompressor.d.ts +183 -0
- package/dist/services/BaseCompressor.js +500 -0
- package/dist/services/ConversationCompressor.d.ts +32 -0
- package/dist/services/ConversationCompressor.js +91 -0
- package/dist/services/IncrementalStreamParser.d.ts +98 -0
- package/dist/services/IncrementalStreamParser.js +327 -0
- package/dist/services/MidGenerationCompressor.d.ts +63 -0
- package/dist/services/MidGenerationCompressor.js +104 -0
- package/dist/services/PendingToolApprovalManager.d.ts +62 -0
- package/dist/services/PendingToolApprovalManager.js +133 -0
- package/dist/services/ResponseFormatter.d.ts +39 -0
- package/dist/services/ResponseFormatter.js +152 -0
- package/dist/tools/NativeSandboxExecutor.d.ts +38 -0
- package/dist/tools/NativeSandboxExecutor.js +432 -0
- package/dist/tools/SandboxExecutorFactory.d.ts +36 -0
- package/dist/tools/SandboxExecutorFactory.js +80 -0
- package/dist/tools/VercelSandboxExecutor.d.ts +71 -0
- package/dist/tools/VercelSandboxExecutor.js +340 -0
- package/dist/tools/distill-conversation-history-tool.d.ts +62 -0
- package/dist/tools/distill-conversation-history-tool.js +206 -0
- package/dist/tools/distill-conversation-tool.d.ts +41 -0
- package/dist/tools/distill-conversation-tool.js +141 -0
- package/dist/tools/sandbox-utils.d.ts +18 -0
- package/dist/tools/sandbox-utils.js +53 -0
- package/dist/types/chat.d.ts +27 -0
- package/dist/types/chat.js +1 -0
- package/dist/types/execution-context.d.ts +46 -0
- package/dist/types/execution-context.js +27 -0
- package/dist/types/xml.d.ts +5 -0
- package/dist/utils/SchemaProcessor.d.ts +52 -0
- package/dist/utils/SchemaProcessor.js +182 -0
- package/dist/utils/agent-operations.d.ts +62 -0
- package/dist/utils/agent-operations.js +53 -0
- package/dist/utils/artifact-component-schema.d.ts +42 -0
- package/dist/utils/artifact-component-schema.js +186 -0
- package/dist/utils/cleanup.d.ts +21 -0
- package/dist/utils/cleanup.js +59 -0
- package/dist/utils/data-component-schema.d.ts +2 -0
- package/dist/utils/data-component-schema.js +3 -0
- package/dist/utils/default-status-schemas.d.ts +20 -0
- package/dist/utils/default-status-schemas.js +24 -0
- package/dist/utils/json-postprocessor.d.ts +13 -0
- package/dist/{json-postprocessor.cjs → utils/json-postprocessor.js} +2 -3
- package/dist/utils/model-context-utils.d.ts +39 -0
- package/dist/utils/model-context-utils.js +181 -0
- package/dist/utils/model-resolver.d.ts +6 -0
- package/dist/utils/model-resolver.js +34 -0
- package/dist/utils/schema-validation.d.ts +44 -0
- package/dist/utils/schema-validation.js +97 -0
- package/dist/utils/stream-helpers.d.ts +197 -0
- package/dist/utils/stream-helpers.js +518 -0
- package/dist/utils/stream-registry.d.ts +22 -0
- package/dist/utils/stream-registry.js +34 -0
- package/dist/utils/token-estimator.d.ts +69 -0
- package/dist/utils/token-estimator.js +53 -0
- package/dist/utils/tracer.d.ts +7 -0
- package/dist/utils/tracer.js +7 -0
- package/package.json +10 -26
- package/dist/SandboxExecutorFactory.cjs +0 -895
- package/dist/SandboxExecutorFactory.js +0 -893
- package/dist/SandboxExecutorFactory.js.map +0 -1
- package/dist/chunk-VBDAOXYI.cjs +0 -927
- package/dist/chunk-VBDAOXYI.js +0 -832
- package/dist/chunk-VBDAOXYI.js.map +0 -1
- package/dist/chunk.cjs +0 -34
- package/dist/conversations.cjs +0 -7
- package/dist/conversations.js +0 -7
- package/dist/conversations2.cjs +0 -209
- package/dist/conversations2.js +0 -180
- package/dist/conversations2.js.map +0 -1
- package/dist/dbClient.cjs +0 -9676
- package/dist/dbClient.js +0 -9670
- package/dist/dbClient.js.map +0 -1
- package/dist/dbClient2.cjs +0 -5
- package/dist/dbClient2.js +0 -5
- package/dist/env.cjs +0 -59
- package/dist/env.js.map +0 -1
- package/dist/execution-limits.cjs +0 -260
- package/dist/execution-limits.js +0 -63
- package/dist/execution-limits.js.map +0 -1
- package/dist/index.cjs +0 -11411
- package/dist/index.d.cts +0 -36
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/instrumentation.cjs +0 -12
- package/dist/instrumentation.d.cts +0 -18
- package/dist/instrumentation.d.cts.map +0 -1
- package/dist/instrumentation.d.ts.map +0 -1
- package/dist/instrumentation2.cjs +0 -116
- package/dist/instrumentation2.js +0 -69
- package/dist/instrumentation2.js.map +0 -1
- package/dist/json-postprocessor.js +0 -20
- package/dist/json-postprocessor.js.map +0 -1
- package/dist/logger.cjs +0 -5
- package/dist/logger2.cjs +0 -1
- package/dist/nodefs.cjs +0 -29
- package/dist/nodefs.js +0 -27
- package/dist/nodefs.js.map +0 -1
- package/dist/opfs-ahp.cjs +0 -367
- package/dist/opfs-ahp.js +0 -368
- package/dist/opfs-ahp.js.map +0 -1
|
@@ -0,0 +1,1208 @@
|
|
|
1
|
+
import { getLogger } from "../logger.js";
|
|
2
|
+
import dbClient_default from "../data/db/dbClient.js";
|
|
3
|
+
import { ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STATUS_UPDATE_DEFAULT_NUM_EVENTS } from "../constants/execution-limits/index.js";
|
|
4
|
+
import { toolSessionManager } from "../agents/ToolSessionManager.js";
|
|
5
|
+
import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
|
|
6
|
+
import { getFormattedConversationHistory } from "../data/conversations.js";
|
|
7
|
+
import { defaultStatusSchemas } from "../utils/default-status-schemas.js";
|
|
8
|
+
import { getStreamHelper } from "../utils/stream-registry.js";
|
|
9
|
+
import { ArtifactService } from "./ArtifactService.js";
|
|
10
|
+
import { ArtifactParser } from "./ArtifactParser.js";
|
|
11
|
+
import { z } from "@hono/zod-openapi";
|
|
12
|
+
import { CONVERSATION_HISTORY_DEFAULT_LIMIT, CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT, ModelFactory, getLedgerArtifacts, getSubAgentById } from "@inkeep/agents-core";
|
|
13
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
14
|
+
import { Output, generateText } from "ai";
|
|
15
|
+
|
|
16
|
+
//#region src/services/AgentSession.ts
|
|
17
|
+
const logger = getLogger("AgentSession");
|
|
18
|
+
/**
|
|
19
|
+
* Tracks all agent operations and interactions for a single message
|
|
20
|
+
* Now includes intelligent status update functionality
|
|
21
|
+
*/
|
|
22
|
+
var AgentSession = class {
|
|
23
|
+
events = [];
|
|
24
|
+
statusUpdateState;
|
|
25
|
+
statusUpdateTimer;
|
|
26
|
+
previousSummaries = [];
|
|
27
|
+
isEnded = false;
|
|
28
|
+
isTextStreaming = false;
|
|
29
|
+
isGeneratingUpdate = false;
|
|
30
|
+
pendingArtifacts = /* @__PURE__ */ new Set();
|
|
31
|
+
artifactProcessingErrors = /* @__PURE__ */ new Map();
|
|
32
|
+
MAX_ARTIFACT_RETRIES = ARTIFACT_GENERATION_MAX_RETRIES;
|
|
33
|
+
MAX_PENDING_ARTIFACTS = ARTIFACT_SESSION_MAX_PENDING;
|
|
34
|
+
scheduledTimeouts;
|
|
35
|
+
artifactCache = /* @__PURE__ */ new Map();
|
|
36
|
+
artifactService;
|
|
37
|
+
artifactParser;
|
|
38
|
+
isEmitOperations = false;
|
|
39
|
+
constructor(sessionId, messageId, agentId, tenantId, projectId, contextId) {
|
|
40
|
+
this.sessionId = sessionId;
|
|
41
|
+
this.messageId = messageId;
|
|
42
|
+
this.agentId = agentId;
|
|
43
|
+
this.tenantId = tenantId;
|
|
44
|
+
this.projectId = projectId;
|
|
45
|
+
this.contextId = contextId;
|
|
46
|
+
logger.debug({
|
|
47
|
+
sessionId,
|
|
48
|
+
messageId,
|
|
49
|
+
agentId
|
|
50
|
+
}, "AgentSession created");
|
|
51
|
+
if (tenantId && projectId) {
|
|
52
|
+
toolSessionManager.createSessionWithId(sessionId, tenantId, projectId, contextId || "default", `task_${contextId}-${messageId}`);
|
|
53
|
+
this.artifactService = new ArtifactService({
|
|
54
|
+
tenantId,
|
|
55
|
+
projectId,
|
|
56
|
+
sessionId,
|
|
57
|
+
contextId,
|
|
58
|
+
taskId: `task_${contextId}-${messageId}`,
|
|
59
|
+
streamRequestId: sessionId
|
|
60
|
+
});
|
|
61
|
+
this.artifactParser = new ArtifactParser(tenantId, {
|
|
62
|
+
projectId,
|
|
63
|
+
sessionId,
|
|
64
|
+
contextId,
|
|
65
|
+
taskId: `task_${contextId}-${messageId}`,
|
|
66
|
+
streamRequestId: sessionId,
|
|
67
|
+
artifactService: this.artifactService
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Enable emit operations to send data operations
|
|
73
|
+
*/
|
|
74
|
+
enableEmitOperations() {
|
|
75
|
+
this.isEmitOperations = true;
|
|
76
|
+
logger.info({ sessionId: this.sessionId }, "🔍 DEBUG: Emit operations enabled for AgentSession");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Send data operation to stream when emit operations is enabled
|
|
80
|
+
*/
|
|
81
|
+
async sendDataOperation(event) {
|
|
82
|
+
try {
|
|
83
|
+
const streamHelper = getStreamHelper(this.sessionId);
|
|
84
|
+
if (streamHelper) {
|
|
85
|
+
const formattedOperation = {
|
|
86
|
+
type: event.eventType,
|
|
87
|
+
label: this.generateEventLabel(event),
|
|
88
|
+
details: {
|
|
89
|
+
timestamp: event.timestamp,
|
|
90
|
+
subAgentId: event.subAgentId,
|
|
91
|
+
data: event.data
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
await streamHelper.writeOperation(formattedOperation);
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger.error({
|
|
98
|
+
sessionId: this.sessionId,
|
|
99
|
+
eventType: event.eventType,
|
|
100
|
+
error: error instanceof Error ? error.message : error
|
|
101
|
+
}, "❌ DEBUG: Failed to send data operation");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Generate human-readable labels for events
|
|
106
|
+
*/
|
|
107
|
+
generateEventLabel(event) {
|
|
108
|
+
switch (event.eventType) {
|
|
109
|
+
case "agent_generate": return `Agent ${event.subAgentId} generating response`;
|
|
110
|
+
case "agent_reasoning": return `Agent ${event.subAgentId} reasoning through request`;
|
|
111
|
+
case "tool_call": return `Tool call: ${event.data.toolName || "unknown"}`;
|
|
112
|
+
case "tool_result": {
|
|
113
|
+
const status = event.data.error ? "failed" : "completed";
|
|
114
|
+
return `Tool result: ${event.data.toolName || "unknown"} (${status})`;
|
|
115
|
+
}
|
|
116
|
+
case "error": return `Error: ${event.data.message}`;
|
|
117
|
+
case "transfer": return `Agent transfer: ${event.data.fromSubAgent} → ${event.data.targetSubAgent}`;
|
|
118
|
+
case "delegation_sent": return `Task delegated: ${event.data.fromSubAgent} → ${event.data.targetSubAgent}`;
|
|
119
|
+
case "delegation_returned": return `Task completed: ${event.data.targetSubAgent} → ${event.data.fromSubAgent}`;
|
|
120
|
+
case "artifact_saved": return `Artifact saved: ${event.data.artifactType || "unknown type"}`;
|
|
121
|
+
case "compression": return `Compressed ${event.data.messageCount} messages and ${event.data.artifactCount} artifacts (${event.data.reason})`;
|
|
122
|
+
default: return `${event.eventType} event`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Initialize status updates for this session
|
|
127
|
+
*/
|
|
128
|
+
initializeStatusUpdates(config, summarizerModel, baseModel) {
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
this.statusUpdateState = {
|
|
131
|
+
lastUpdateTime: now,
|
|
132
|
+
lastEventCount: 0,
|
|
133
|
+
startTime: now,
|
|
134
|
+
summarizerModel,
|
|
135
|
+
baseModel,
|
|
136
|
+
config: {
|
|
137
|
+
numEvents: config.numEvents || STATUS_UPDATE_DEFAULT_NUM_EVENTS,
|
|
138
|
+
timeInSeconds: config.timeInSeconds || STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS,
|
|
139
|
+
...config
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
if (this.statusUpdateState.config.timeInSeconds) {
|
|
143
|
+
this.statusUpdateTimer = setInterval(async () => {
|
|
144
|
+
if (!this.statusUpdateState || this.isEnded) {
|
|
145
|
+
logger.debug({ sessionId: this.sessionId }, "Timer triggered but session already cleaned up or ended");
|
|
146
|
+
if (this.statusUpdateTimer) {
|
|
147
|
+
clearInterval(this.statusUpdateTimer);
|
|
148
|
+
this.statusUpdateTimer = void 0;
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
await this.checkAndSendTimeBasedUpdate();
|
|
153
|
+
}, this.statusUpdateState.config.timeInSeconds * 1e3);
|
|
154
|
+
logger.info({
|
|
155
|
+
sessionId: this.sessionId,
|
|
156
|
+
intervalMs: this.statusUpdateState.config.timeInSeconds * 1e3
|
|
157
|
+
}, "Time-based status update timer started");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Record an event in the session and trigger status updates if configured
|
|
162
|
+
* Generic type parameter T ensures eventType and data are correctly paired
|
|
163
|
+
*/
|
|
164
|
+
recordEvent(eventType, subAgentId, data) {
|
|
165
|
+
if (this.isEmitOperations) {
|
|
166
|
+
const dataOpEvent = {
|
|
167
|
+
timestamp: Date.now(),
|
|
168
|
+
eventType,
|
|
169
|
+
subAgentId,
|
|
170
|
+
data
|
|
171
|
+
};
|
|
172
|
+
this.sendDataOperation(dataOpEvent);
|
|
173
|
+
}
|
|
174
|
+
if (this.isEnded) {
|
|
175
|
+
logger.debug({
|
|
176
|
+
sessionId: this.sessionId,
|
|
177
|
+
eventType,
|
|
178
|
+
subAgentId
|
|
179
|
+
}, "Event received after session ended - ignoring");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const event = {
|
|
183
|
+
timestamp: Date.now(),
|
|
184
|
+
eventType,
|
|
185
|
+
subAgentId,
|
|
186
|
+
data
|
|
187
|
+
};
|
|
188
|
+
this.events.push(event);
|
|
189
|
+
if (eventType === "artifact_saved") {
|
|
190
|
+
const artifactData = data;
|
|
191
|
+
if (artifactData.pendingGeneration) {
|
|
192
|
+
const artifactId = artifactData.artifactId;
|
|
193
|
+
if (this.pendingArtifacts.size >= this.MAX_PENDING_ARTIFACTS) {
|
|
194
|
+
logger.warn({
|
|
195
|
+
sessionId: this.sessionId,
|
|
196
|
+
artifactId,
|
|
197
|
+
pendingCount: this.pendingArtifacts.size,
|
|
198
|
+
maxAllowed: this.MAX_PENDING_ARTIFACTS
|
|
199
|
+
}, "Too many pending artifacts, skipping processing");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.pendingArtifacts.add(artifactId);
|
|
203
|
+
setImmediate(() => {
|
|
204
|
+
const artifactDataWithAgent = {
|
|
205
|
+
...artifactData,
|
|
206
|
+
subAgentId
|
|
207
|
+
};
|
|
208
|
+
this.processArtifact(artifactDataWithAgent).then(() => {
|
|
209
|
+
this.pendingArtifacts.delete(artifactId);
|
|
210
|
+
this.artifactProcessingErrors.delete(artifactId);
|
|
211
|
+
}).catch((error) => {
|
|
212
|
+
const errorCount = (this.artifactProcessingErrors.get(artifactId) || 0) + 1;
|
|
213
|
+
this.artifactProcessingErrors.set(artifactId, errorCount);
|
|
214
|
+
if (errorCount >= this.MAX_ARTIFACT_RETRIES) {
|
|
215
|
+
this.pendingArtifacts.delete(artifactId);
|
|
216
|
+
logger.error({
|
|
217
|
+
sessionId: this.sessionId,
|
|
218
|
+
artifactId,
|
|
219
|
+
errorCount,
|
|
220
|
+
maxRetries: this.MAX_ARTIFACT_RETRIES,
|
|
221
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
222
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
223
|
+
}, "Artifact processing failed after max retries, giving up");
|
|
224
|
+
} else logger.warn({
|
|
225
|
+
sessionId: this.sessionId,
|
|
226
|
+
artifactId,
|
|
227
|
+
errorCount,
|
|
228
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
229
|
+
}, "Artifact processing failed, may retry");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!this.isEnded) this.checkStatusUpdates();
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Check and send status updates if configured (async, non-blocking)
|
|
238
|
+
*/
|
|
239
|
+
checkStatusUpdates() {
|
|
240
|
+
if (this.isEnded) {
|
|
241
|
+
logger.debug({ sessionId: this.sessionId }, "Session has ended - skipping status update check");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (!this.statusUpdateState) {
|
|
245
|
+
logger.debug({ sessionId: this.sessionId }, "No status update state - skipping check");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const statusUpdateState = this.statusUpdateState;
|
|
249
|
+
this.scheduleStatusUpdateCheck(statusUpdateState);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Check and send time-based status updates
|
|
253
|
+
*/
|
|
254
|
+
async checkAndSendTimeBasedUpdate() {
|
|
255
|
+
if (this.isEnded) {
|
|
256
|
+
logger.debug({ sessionId: this.sessionId }, "Session has ended - skipping time-based update");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (!this.statusUpdateState) {
|
|
260
|
+
logger.debug({ sessionId: this.sessionId }, "No status updates configured for time-based check");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (this.events.length - this.statusUpdateState.lastEventCount === 0) return;
|
|
264
|
+
try {
|
|
265
|
+
await this.generateAndSendUpdate();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
logger.error({
|
|
268
|
+
sessionId: this.sessionId,
|
|
269
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
270
|
+
}, "Failed to send time-based status update");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get all events in chronological order
|
|
275
|
+
*/
|
|
276
|
+
getEvents() {
|
|
277
|
+
return [...this.events];
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get events filtered by type
|
|
281
|
+
*/
|
|
282
|
+
getEventsByType(eventType) {
|
|
283
|
+
return this.events.filter((event) => event.eventType === eventType);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get events filtered by agent
|
|
287
|
+
*/
|
|
288
|
+
getEventsByAgent(subAgentId) {
|
|
289
|
+
return this.events.filter((event) => event.subAgentId === subAgentId);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Get summary of session activity
|
|
293
|
+
*/
|
|
294
|
+
getSummary() {
|
|
295
|
+
const eventCounts = this.events.reduce((counts, event) => {
|
|
296
|
+
counts[event.eventType] = (counts[event.eventType] || 0) + 1;
|
|
297
|
+
return counts;
|
|
298
|
+
}, {});
|
|
299
|
+
const agentCounts = this.events.reduce((counts, event) => {
|
|
300
|
+
counts[event.subAgentId] = (counts[event.subAgentId] || 0) + 1;
|
|
301
|
+
return counts;
|
|
302
|
+
}, {});
|
|
303
|
+
return {
|
|
304
|
+
sessionId: this.sessionId,
|
|
305
|
+
messageId: this.messageId,
|
|
306
|
+
agentId: this.agentId,
|
|
307
|
+
totalEvents: this.events.length,
|
|
308
|
+
eventCounts,
|
|
309
|
+
agentCounts,
|
|
310
|
+
startTime: this.events[0]?.timestamp,
|
|
311
|
+
endTime: this.events[this.events.length - 1]?.timestamp,
|
|
312
|
+
duration: this.events.length > 0 ? this.events[this.events.length - 1].timestamp - this.events[0].timestamp : 0
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Mark that text streaming has started (to suppress status updates)
|
|
317
|
+
*/
|
|
318
|
+
setTextStreaming(isStreaming) {
|
|
319
|
+
this.isTextStreaming = isStreaming;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Check if text is currently being streamed
|
|
323
|
+
*/
|
|
324
|
+
isCurrentlyStreaming() {
|
|
325
|
+
return this.isTextStreaming;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Clean up status update resources when session ends
|
|
329
|
+
*/
|
|
330
|
+
async cleanup() {
|
|
331
|
+
this.isEnded = true;
|
|
332
|
+
if (this.statusUpdateTimer) {
|
|
333
|
+
clearInterval(this.statusUpdateTimer);
|
|
334
|
+
this.statusUpdateTimer = void 0;
|
|
335
|
+
}
|
|
336
|
+
this.statusUpdateState = void 0;
|
|
337
|
+
if (this.pendingArtifacts.size > 0) {
|
|
338
|
+
const maxWaitTime = 1e4;
|
|
339
|
+
const startTime = Date.now();
|
|
340
|
+
while (this.pendingArtifacts.size > 0 && Date.now() - startTime < maxWaitTime) await new Promise((resolve) => setTimeout(resolve, 100));
|
|
341
|
+
if (this.pendingArtifacts.size > 0) logger.warn({
|
|
342
|
+
sessionId: this.sessionId,
|
|
343
|
+
pendingCount: this.pendingArtifacts.size,
|
|
344
|
+
pendingIds: Array.from(this.pendingArtifacts)
|
|
345
|
+
}, "Cleanup proceeding with pending artifacts still processing");
|
|
346
|
+
}
|
|
347
|
+
this.pendingArtifacts.clear();
|
|
348
|
+
this.artifactProcessingErrors.clear();
|
|
349
|
+
this.artifactCache.clear();
|
|
350
|
+
if (this.sessionId) toolSessionManager.endSession(this.sessionId);
|
|
351
|
+
if (this.scheduledTimeouts) {
|
|
352
|
+
for (const timeoutId of this.scheduledTimeouts) clearTimeout(timeoutId);
|
|
353
|
+
this.scheduledTimeouts.clear();
|
|
354
|
+
}
|
|
355
|
+
if (this.artifactService) {
|
|
356
|
+
this.artifactService.constructor.clearCaches();
|
|
357
|
+
this.artifactService = void 0;
|
|
358
|
+
} else ArtifactService.clearCaches();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Generate and send a status update using agent-level summarizer
|
|
362
|
+
*/
|
|
363
|
+
async generateAndSendUpdate() {
|
|
364
|
+
if (this.isEnded) {
|
|
365
|
+
logger.debug({ sessionId: this.sessionId }, "Session has ended - not generating update");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (this.isTextStreaming) {
|
|
369
|
+
logger.debug({ sessionId: this.sessionId }, "Text is currently streaming - skipping status update");
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (this.isGeneratingUpdate) {
|
|
373
|
+
logger.debug({ sessionId: this.sessionId }, "Update already in progress - skipping duplicate generation");
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (!this.statusUpdateState) {
|
|
377
|
+
logger.warn({ sessionId: this.sessionId }, "No status update state - cannot generate update");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (!this.agentId) {
|
|
381
|
+
logger.warn({ sessionId: this.sessionId }, "No agent ID - cannot generate update");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (this.events.length - this.statusUpdateState.lastEventCount === 0) return;
|
|
385
|
+
this.isGeneratingUpdate = true;
|
|
386
|
+
const statusUpdateState = this.statusUpdateState;
|
|
387
|
+
try {
|
|
388
|
+
const streamHelper = getStreamHelper(this.sessionId);
|
|
389
|
+
if (!streamHelper) {
|
|
390
|
+
logger.warn({ sessionId: this.sessionId }, "No stream helper found - cannot send status update");
|
|
391
|
+
this.isGeneratingUpdate = false;
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const now = Date.now();
|
|
395
|
+
const elapsedTime = now - statusUpdateState.startTime;
|
|
396
|
+
const statusComponents = statusUpdateState.config.statusComponents && statusUpdateState.config.statusComponents.length > 0 ? statusUpdateState.config.statusComponents : defaultStatusSchemas;
|
|
397
|
+
const result = await this.generateStructuredStatusUpdate(this.events.slice(statusUpdateState.lastEventCount), elapsedTime, statusComponents, statusUpdateState.summarizerModel, this.previousSummaries);
|
|
398
|
+
if (result.summaries && result.summaries.length > 0) {
|
|
399
|
+
for (const summary of result.summaries) {
|
|
400
|
+
if (!summary || !summary.type || !summary.data || !summary.data.label || Object.keys(summary.data).length === 0) {
|
|
401
|
+
logger.warn({
|
|
402
|
+
sessionId: this.sessionId,
|
|
403
|
+
summary
|
|
404
|
+
}, "Skipping empty or invalid structured operation");
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const summaryToSend = {
|
|
408
|
+
type: summary.data.type || summary.type,
|
|
409
|
+
label: summary.data.label,
|
|
410
|
+
details: Object.fromEntries(Object.entries(summary.data).filter(([key]) => !["label", "type"].includes(key)))
|
|
411
|
+
};
|
|
412
|
+
await streamHelper.writeSummary(summaryToSend);
|
|
413
|
+
}
|
|
414
|
+
const summaryTexts = result.summaries.map((summary) => JSON.stringify({
|
|
415
|
+
type: summary.type,
|
|
416
|
+
data: summary.data
|
|
417
|
+
}));
|
|
418
|
+
this.previousSummaries.push(...summaryTexts);
|
|
419
|
+
if (this.statusUpdateState) {
|
|
420
|
+
this.statusUpdateState.lastUpdateTime = now;
|
|
421
|
+
this.statusUpdateState.lastEventCount = this.events.length;
|
|
422
|
+
}
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (this.previousSummaries.length > ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES) this.previousSummaries.shift();
|
|
426
|
+
if (this.statusUpdateState) {
|
|
427
|
+
this.statusUpdateState.lastUpdateTime = now;
|
|
428
|
+
this.statusUpdateState.lastEventCount = this.events.length;
|
|
429
|
+
}
|
|
430
|
+
} catch (error) {
|
|
431
|
+
logger.error({
|
|
432
|
+
sessionId: this.sessionId,
|
|
433
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
434
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
435
|
+
}, "❌ Failed to generate status update");
|
|
436
|
+
} finally {
|
|
437
|
+
this.isGeneratingUpdate = false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Schedule status update check without setImmediate race conditions
|
|
442
|
+
*/
|
|
443
|
+
scheduleStatusUpdateCheck(statusUpdateState) {
|
|
444
|
+
const timeoutId = setTimeout(async () => {
|
|
445
|
+
try {
|
|
446
|
+
if (this.isEnded || !this.statusUpdateState) return;
|
|
447
|
+
if (!this.acquireUpdateLock()) return;
|
|
448
|
+
try {
|
|
449
|
+
if (this.isEnded || !statusUpdateState || this.isTextStreaming) return;
|
|
450
|
+
const currentEventCount = this.events.length;
|
|
451
|
+
const numEventsThreshold = statusUpdateState.config.numEvents;
|
|
452
|
+
if (numEventsThreshold && currentEventCount >= statusUpdateState.lastEventCount + numEventsThreshold) await this.generateAndSendUpdate();
|
|
453
|
+
} finally {
|
|
454
|
+
this.releaseUpdateLock();
|
|
455
|
+
}
|
|
456
|
+
} catch (error) {
|
|
457
|
+
logger.error({
|
|
458
|
+
sessionId: this.sessionId,
|
|
459
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
460
|
+
}, "Failed to check status updates during event recording");
|
|
461
|
+
this.releaseUpdateLock();
|
|
462
|
+
}
|
|
463
|
+
}, 0);
|
|
464
|
+
if (!this.scheduledTimeouts) this.scheduledTimeouts = /* @__PURE__ */ new Set();
|
|
465
|
+
this.scheduledTimeouts.add(timeoutId);
|
|
466
|
+
setTimeout(() => {
|
|
467
|
+
if (this.scheduledTimeouts) this.scheduledTimeouts.delete(timeoutId);
|
|
468
|
+
}, 1e3);
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Acquire update lock with atomic check
|
|
472
|
+
*/
|
|
473
|
+
acquireUpdateLock() {
|
|
474
|
+
if (this.statusUpdateState?.updateLock) return false;
|
|
475
|
+
if (this.statusUpdateState) this.statusUpdateState.updateLock = true;
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Release update lock
|
|
480
|
+
*/
|
|
481
|
+
releaseUpdateLock() {
|
|
482
|
+
if (this.statusUpdateState) this.statusUpdateState.updateLock = false;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Generate structured status update using configured data components
|
|
486
|
+
*/
|
|
487
|
+
async generateStructuredStatusUpdate(newEvents, elapsedTime, statusComponents, summarizerModel, previousSummaries = []) {
|
|
488
|
+
return tracer.startActiveSpan("agent_session.generate_structured_update", { attributes: {
|
|
489
|
+
"agent_session.id": this.sessionId,
|
|
490
|
+
"events.count": newEvents.length,
|
|
491
|
+
"elapsed_time.seconds": Math.round(elapsedTime / 1e3),
|
|
492
|
+
"llm.model": summarizerModel?.model,
|
|
493
|
+
"status_components.count": statusComponents.length,
|
|
494
|
+
"previous_summaries.count": previousSummaries.length
|
|
495
|
+
} }, async (span) => {
|
|
496
|
+
try {
|
|
497
|
+
const userVisibleActivities = this.extractUserVisibleActivities(newEvents);
|
|
498
|
+
let conversationContext = "";
|
|
499
|
+
if (this.tenantId && this.projectId) try {
|
|
500
|
+
const conversationHistory = await getFormattedConversationHistory({
|
|
501
|
+
tenantId: this.tenantId,
|
|
502
|
+
projectId: this.projectId,
|
|
503
|
+
conversationId: this.contextId || "default",
|
|
504
|
+
options: {
|
|
505
|
+
limit: CONVERSATION_HISTORY_DEFAULT_LIMIT,
|
|
506
|
+
maxOutputTokens: CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT,
|
|
507
|
+
includeInternal: true,
|
|
508
|
+
messageTypes: ["chat", "tool-result"]
|
|
509
|
+
},
|
|
510
|
+
filters: {}
|
|
511
|
+
});
|
|
512
|
+
conversationContext = conversationHistory.trim() ? `\nUser's Question/Context:\n${conversationHistory}\n` : "";
|
|
513
|
+
} catch (error) {
|
|
514
|
+
logger.warn({
|
|
515
|
+
sessionId: this.sessionId,
|
|
516
|
+
error
|
|
517
|
+
}, "Failed to fetch conversation history for structured status update");
|
|
518
|
+
}
|
|
519
|
+
const previousSummaryContext = previousSummaries.length > 0 ? `\nPrevious updates sent to user:\n${previousSummaries.map((s, i) => `${i + 1}. ${s}`).join("\n")}\n` : "";
|
|
520
|
+
const selectionSchema = z.object(Object.fromEntries([["no_relevant_updates", z.object({ no_updates: z.boolean().default(true) }).optional().describe("Use when nothing substantially new to report. Should only use on its own.")], ...statusComponents.map((component) => [component.type, this.getComponentSchema(component).optional().describe(component.description || component.type)])]));
|
|
521
|
+
const prompt = `Generate status updates for relevant components based on what the user has asked for.${conversationContext}${previousSummaries.length > 0 ? `\n${previousSummaryContext}` : ""}
|
|
522
|
+
|
|
523
|
+
Activities:\n${userVisibleActivities.join("\n") || "No New Activities"}
|
|
524
|
+
|
|
525
|
+
Available components: no_relevant_updates, ${statusComponents.map((c) => c.type).join(", ")}
|
|
526
|
+
|
|
527
|
+
Rules:
|
|
528
|
+
- Fill in data for relevant components only
|
|
529
|
+
- Use 'no_relevant_updates' if nothing substantially new to report. DO NOT WRITE LABELS OR USE OTHER COMPONENTS IF YOU USE THIS COMPONENT.
|
|
530
|
+
- Never repeat previous values, make every update EXTREMELY unique. If you cannot do that the update is not worth mentioning.
|
|
531
|
+
- Labels MUST be short 3-7 word phrases with ACTUAL information discovered. NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION.
|
|
532
|
+
- Use sentence case: only capitalize the first word and proper nouns (e.g., "Admin permissions required", not "Admin Permissions Required"). ALWAYS capitalize the first word of the label.
|
|
533
|
+
- DO NOT use action words like "Searching", "Processing", "Analyzing" - state what was FOUND
|
|
534
|
+
- Include specific details, numbers, requirements, or insights discovered
|
|
535
|
+
- Examples: "Admin permissions required", "Three OAuth steps found", "Token expires daily"
|
|
536
|
+
|
|
537
|
+
CRITICAL - HIDE ALL INTERNAL SYSTEM OPERATIONS:
|
|
538
|
+
- You are ONE unified AI system presenting results to the user
|
|
539
|
+
- ABSOLUTELY FORBIDDEN WORDS/PHRASES: "transfer", "transferring", "delegation", "delegating", "delegate", "agent", "routing", "route", "artifact", "saving artifact", "stored artifact", "artifact saved", "continuing", "passing to", "handing off", "switching to"
|
|
540
|
+
- NEVER reveal internal architecture: No mentions of different agents, components, systems, or modules working together
|
|
541
|
+
- NEVER mention artifact operations: Users don't need to know about data being saved, stored, or organized internally
|
|
542
|
+
- NEVER describe transfers or transitions: Present everything as one seamless operation
|
|
543
|
+
- If you see "transfer", "delegation_sent", "delegation_returned", or "artifact_saved" events - IGNORE THEM or translate to user-facing information only
|
|
544
|
+
- Focus ONLY on actual discoveries, findings, and results that matter to the user
|
|
545
|
+
|
|
546
|
+
- Bad examples:
|
|
547
|
+
* "Transferring to search agent"
|
|
548
|
+
* "Delegating research task"
|
|
549
|
+
* "Routing to QA specialist"
|
|
550
|
+
* "Artifact saved successfully"
|
|
551
|
+
* "Storing results for later"
|
|
552
|
+
* "Passing request to tool handler"
|
|
553
|
+
* "Continuing with analysis"
|
|
554
|
+
* "Handing off to processor"
|
|
555
|
+
- Good examples:
|
|
556
|
+
* "Slack bot needs admin privileges"
|
|
557
|
+
* "Found 3-step OAuth flow required"
|
|
558
|
+
* "Channel limit is 500 per workspace"
|
|
559
|
+
* Use no_relevant_updates if nothing new to report
|
|
560
|
+
|
|
561
|
+
CRITICAL ANTI-HALLUCINATION RULES:
|
|
562
|
+
- NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION. EVERY SINGLE UPDATE MUST BE BACKED UP WITH ACTUAL INFORMATION.
|
|
563
|
+
- DO NOT MAKE UP PEOPLE, NAMES, PLACES, THINGS, ORGANIZATIONS, OR INFORMATION. IT IS OBVIOUS WHEN A PERSON/ENTITY DOES NOT EXIST.
|
|
564
|
+
- Only report facts that are EXPLICITLY mentioned in the activities or tool results
|
|
565
|
+
- If you don't have concrete information about something, DO NOT mention it
|
|
566
|
+
- Never invent names like "John Doe", "Alice", "Bob", or any other placeholder names
|
|
567
|
+
- Never create fictional companies, products, or services
|
|
568
|
+
- If a tool returned no results or an error, DO NOT pretend it found something
|
|
569
|
+
- Every detail in your status update must be traceable back to the actual activities provided
|
|
570
|
+
|
|
571
|
+
REMEMBER YOU CAN ONLY USE 'no_relevant_updates' ALONE! IT CANNOT BE CONCATENATED WITH OTHER STATUS UPDATES!
|
|
572
|
+
|
|
573
|
+
${this.statusUpdateState?.config.prompt?.trim() || ""}`;
|
|
574
|
+
let modelToUse = summarizerModel;
|
|
575
|
+
if (!summarizerModel?.model?.trim()) {
|
|
576
|
+
if (!this.statusUpdateState?.baseModel?.model?.trim()) throw new Error("Either summarizer or base model is required for status update generation. Please configure models at the project level.");
|
|
577
|
+
modelToUse = this.statusUpdateState.baseModel;
|
|
578
|
+
}
|
|
579
|
+
if (!modelToUse) throw new Error("No model configuration available");
|
|
580
|
+
const { output: object } = await generateText({
|
|
581
|
+
model: ModelFactory.createModel(modelToUse),
|
|
582
|
+
prompt,
|
|
583
|
+
output: Output.object({ schema: selectionSchema }),
|
|
584
|
+
experimental_telemetry: {
|
|
585
|
+
isEnabled: true,
|
|
586
|
+
functionId: `structured_update_${this.sessionId}`,
|
|
587
|
+
recordInputs: true,
|
|
588
|
+
recordOutputs: true,
|
|
589
|
+
metadata: {
|
|
590
|
+
operation: "structured_status_update_generation",
|
|
591
|
+
sessionId: this.sessionId
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
const result = object;
|
|
596
|
+
logger.info({ result: JSON.stringify(result) }, "DEBUG: Result");
|
|
597
|
+
const summaries = [];
|
|
598
|
+
for (const [componentId, data] of Object.entries(result)) {
|
|
599
|
+
logger.info({
|
|
600
|
+
componentId,
|
|
601
|
+
data: JSON.stringify(data)
|
|
602
|
+
}, "DEBUG: Component data");
|
|
603
|
+
if (componentId === "no_relevant_updates") continue;
|
|
604
|
+
if (data && typeof data === "object" && Object.keys(data).length > 0) summaries.push({
|
|
605
|
+
type: componentId,
|
|
606
|
+
data
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
span.setAttributes({
|
|
610
|
+
"summaries.count": summaries.length,
|
|
611
|
+
"user_activities.count": userVisibleActivities.length,
|
|
612
|
+
"result_keys.count": Object.keys(result).length
|
|
613
|
+
});
|
|
614
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
615
|
+
return { summaries };
|
|
616
|
+
} catch (error) {
|
|
617
|
+
setSpanWithError$1(span, error instanceof Error ? error : new Error(String(error)));
|
|
618
|
+
logger.error({ error }, "Failed to generate structured update, using fallback");
|
|
619
|
+
return { summaries: [] };
|
|
620
|
+
} finally {
|
|
621
|
+
span.end();
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Build Zod schema from JSON schema configuration or use pre-defined schemas
|
|
627
|
+
*/
|
|
628
|
+
getComponentSchema(component) {
|
|
629
|
+
if (component.detailsSchema && "properties" in component.detailsSchema) return this.buildZodSchemaFromJson(component.detailsSchema);
|
|
630
|
+
return z.object({ label: z.string().describe("A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The ACTUAL finding or result, not the action. What specific information was discovered? (e.g., \"Slack requires OAuth 2.0 setup\", \"Found 5 integration methods\", \"API rate limit is 100/minute\"). Include the actual detail or insight, not just that you searched or processed. CRITICAL: Only use facts explicitly found in the activities - NEVER invent names, people, organizations, or details that are not present in the actual tool results.") });
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Build Zod schema from JSON schema with improved type handling
|
|
634
|
+
*/
|
|
635
|
+
buildZodSchemaFromJson(jsonSchema) {
|
|
636
|
+
const properties = {};
|
|
637
|
+
properties.label = z.string().describe("A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The SPECIFIC finding, result, or insight discovered (e.g., \"Slack bot needs workspace admin role\", \"Found ingestion requires 3 steps\", \"Channel history limited to 10k messages\"). State the ACTUAL information found, not that you searched. What did you LEARN or DISCOVER? What specific detail is now known? CRITICAL: Only use facts explicitly found in the activities - NEVER invent names, people, organizations, or details that are not present in the actual tool results.");
|
|
638
|
+
for (const [key, value] of Object.entries(jsonSchema.properties)) {
|
|
639
|
+
let zodType;
|
|
640
|
+
if (value.enum && Array.isArray(value.enum)) if (value.enum.length === 1) zodType = z.literal(value.enum[0]);
|
|
641
|
+
else {
|
|
642
|
+
const [first, ...rest] = value.enum;
|
|
643
|
+
zodType = z.enum([first, ...rest]);
|
|
644
|
+
}
|
|
645
|
+
else if (value.type === "string") {
|
|
646
|
+
zodType = z.string();
|
|
647
|
+
if (value.minLength) zodType = zodType.min(value.minLength);
|
|
648
|
+
if (value.maxLength) zodType = zodType.max(value.maxLength);
|
|
649
|
+
if (value.format === "email") zodType = zodType.email();
|
|
650
|
+
if (value.format === "url" || value.format === "uri") zodType = zodType.url();
|
|
651
|
+
} else if (value.type === "number" || value.type === "integer") {
|
|
652
|
+
zodType = value.type === "integer" ? z.number().int() : z.number();
|
|
653
|
+
if (value.minimum !== void 0) zodType = zodType.min(value.minimum);
|
|
654
|
+
if (value.maximum !== void 0) zodType = zodType.max(value.maximum);
|
|
655
|
+
} else if (value.type === "boolean") zodType = z.boolean();
|
|
656
|
+
else if (value.type === "array") {
|
|
657
|
+
if (value.items) if (value.items.enum && Array.isArray(value.items.enum)) {
|
|
658
|
+
const [first, ...rest] = value.items.enum;
|
|
659
|
+
zodType = z.array(z.enum([first, ...rest]));
|
|
660
|
+
} else if (value.items.type === "string") zodType = z.array(z.string());
|
|
661
|
+
else if (value.items.type === "number") zodType = z.array(z.number());
|
|
662
|
+
else if (value.items.type === "boolean") zodType = z.array(z.boolean());
|
|
663
|
+
else if (value.items.type === "object") zodType = z.array(z.record(z.string(), z.any()));
|
|
664
|
+
else zodType = z.array(z.any());
|
|
665
|
+
else zodType = z.array(z.any());
|
|
666
|
+
if (value.minItems) zodType = zodType.min(value.minItems);
|
|
667
|
+
if (value.maxItems) zodType = zodType.max(value.maxItems);
|
|
668
|
+
} else if (value.type === "object") zodType = z.record(z.string(), z.any());
|
|
669
|
+
else zodType = z.any();
|
|
670
|
+
if (value.description) zodType = zodType.describe(value.description);
|
|
671
|
+
if (!jsonSchema.required?.includes(key) || value.optional === true) zodType = zodType.optional();
|
|
672
|
+
properties[key] = zodType;
|
|
673
|
+
}
|
|
674
|
+
return z.object(properties);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Extract user-visible activities with rich formatting and complete information
|
|
678
|
+
*/
|
|
679
|
+
extractUserVisibleActivities(events) {
|
|
680
|
+
const activities = [];
|
|
681
|
+
for (const event of events) switch (event.eventType) {
|
|
682
|
+
case "tool_call":
|
|
683
|
+
activities.push(`🔧 **${event.data.toolName}** (called)\n 📥 Input: ${JSON.stringify(event.data.input)}`);
|
|
684
|
+
break;
|
|
685
|
+
case "tool_result": {
|
|
686
|
+
const resultStr = event.data.error ? `❌ Error: ${event.data.error}` : JSON.stringify(event.data.output);
|
|
687
|
+
activities.push(`🔧 **${event.data.toolName}** ${event.data.duration ? `(${event.data.duration}ms)` : ""}\n 📤 Output: ${resultStr}`);
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
case "error":
|
|
691
|
+
activities.push(`❌ **Error**: ${event.data.message}\n 🔍 Code: ${event.data.code || "unknown"}\n 📊 Severity: ${event.data.severity || "error"}`);
|
|
692
|
+
break;
|
|
693
|
+
case "transfer":
|
|
694
|
+
case "delegation_sent":
|
|
695
|
+
case "delegation_returned":
|
|
696
|
+
case "artifact_saved": break;
|
|
697
|
+
case "agent_reasoning":
|
|
698
|
+
activities.push(`⚙️ **Analyzing request**\n Details: ${JSON.stringify(event.data.parts, null, 2)}`);
|
|
699
|
+
break;
|
|
700
|
+
case "agent_generate":
|
|
701
|
+
activities.push(`⚙️ **Preparing response**\n Details: ${JSON.stringify(event.data.parts, null, 2)}`);
|
|
702
|
+
break;
|
|
703
|
+
default: {
|
|
704
|
+
const safeEvent = event;
|
|
705
|
+
activities.push(`📋 **${safeEvent.eventType}**: ${JSON.stringify(safeEvent.data, null, 2)}`);
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return activities;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Process a single artifact to generate name and description using conversation context
|
|
713
|
+
*/
|
|
714
|
+
async processArtifact(artifactData) {
|
|
715
|
+
return tracer.startActiveSpan("agent_session.process_artifact", { attributes: {
|
|
716
|
+
"agent_session.id": this.sessionId,
|
|
717
|
+
"artifact.id": artifactData.artifactId,
|
|
718
|
+
"artifact.type": artifactData.artifactType || "unknown",
|
|
719
|
+
"subAgent.id": artifactData.subAgentId || "unknown",
|
|
720
|
+
"subAgent.name": artifactData.subAgentName || "unknown",
|
|
721
|
+
"artifact.tool_call_id": artifactData.metadata?.toolCallId || "unknown",
|
|
722
|
+
"artifact.data": JSON.stringify(artifactData.data, null, 2),
|
|
723
|
+
"tenant.id": artifactData.tenantId || "unknown",
|
|
724
|
+
"project.id": artifactData.projectId || "unknown",
|
|
725
|
+
"context.id": artifactData.contextId || "unknown",
|
|
726
|
+
has_tenant_id: !!artifactData.tenantId,
|
|
727
|
+
has_project_id: !!artifactData.projectId,
|
|
728
|
+
has_context_id: !!artifactData.contextId,
|
|
729
|
+
has_metadata: !!artifactData.metadata,
|
|
730
|
+
tool_call_id: artifactData.metadata?.toolCallId || "missing",
|
|
731
|
+
pending_generation: !!artifactData.pendingGeneration,
|
|
732
|
+
"schema_validation.schema_found": artifactData.schemaValidation?.schemaFound || false,
|
|
733
|
+
"schema_validation.summary.has_expected_fields": artifactData.schemaValidation?.summary?.hasExpectedFields || true,
|
|
734
|
+
"schema_validation.summary.missing_fields_count": artifactData.schemaValidation?.summary?.missingFields?.length || 0,
|
|
735
|
+
"schema_validation.summary.extra_fields_count": artifactData.schemaValidation?.summary?.extraFields?.length || 0,
|
|
736
|
+
"schema_validation.summary.expected_fields": JSON.stringify(artifactData.schemaValidation?.summary?.expectedFields || []),
|
|
737
|
+
"schema_validation.summary.actual_fields": JSON.stringify(artifactData.schemaValidation?.summary?.actualFields || []),
|
|
738
|
+
"schema_validation.summary.missing_fields": JSON.stringify(artifactData.schemaValidation?.summary?.missingFields || []),
|
|
739
|
+
"schema_validation.summary.extra_fields": JSON.stringify(artifactData.schemaValidation?.summary?.extraFields || []),
|
|
740
|
+
"schema_validation.summary.has_required_fields": artifactData.schemaValidation?.summary?.hasRequiredFields || true,
|
|
741
|
+
"schema_validation.summary.missing_required_count": artifactData.schemaValidation?.summary?.missingRequired?.length || 0,
|
|
742
|
+
"schema_validation.summary.missing_required": JSON.stringify(artifactData.schemaValidation?.summary?.missingRequired || []),
|
|
743
|
+
"schema_validation.full.has_expected_fields": artifactData.schemaValidation?.full?.hasExpectedFields || true,
|
|
744
|
+
"schema_validation.full.missing_fields_count": artifactData.schemaValidation?.full?.missingFields?.length || 0,
|
|
745
|
+
"schema_validation.full.extra_fields_count": artifactData.schemaValidation?.full?.extraFields?.length || 0,
|
|
746
|
+
"schema_validation.full.expected_fields": JSON.stringify(artifactData.schemaValidation?.full?.expectedFields || []),
|
|
747
|
+
"schema_validation.full.actual_fields": JSON.stringify(artifactData.schemaValidation?.full?.actualFields || []),
|
|
748
|
+
"schema_validation.full.missing_fields": JSON.stringify(artifactData.schemaValidation?.full?.missingFields || []),
|
|
749
|
+
"schema_validation.full.extra_fields": JSON.stringify(artifactData.schemaValidation?.full?.extraFields || []),
|
|
750
|
+
"schema_validation.full.has_required_fields": artifactData.schemaValidation?.full?.hasRequiredFields || true,
|
|
751
|
+
"schema_validation.full.missing_required_count": artifactData.schemaValidation?.full?.missingRequired?.length || 0,
|
|
752
|
+
"schema_validation.full.missing_required": JSON.stringify(artifactData.schemaValidation?.full?.missingRequired || [])
|
|
753
|
+
} }, async (span) => {
|
|
754
|
+
try {
|
|
755
|
+
if (!artifactData.tenantId || !artifactData.projectId || !artifactData.contextId) {
|
|
756
|
+
span.setAttributes({
|
|
757
|
+
"validation.failed": true,
|
|
758
|
+
missing_tenant_id: !artifactData.tenantId,
|
|
759
|
+
missing_project_id: !artifactData.projectId,
|
|
760
|
+
missing_context_id: !artifactData.contextId
|
|
761
|
+
});
|
|
762
|
+
throw new Error("Missing required session info (tenantId, projectId, or contextId) for artifact processing");
|
|
763
|
+
}
|
|
764
|
+
span.setAttributes({ "validation.passed": true });
|
|
765
|
+
let mainSaveSucceeded = false;
|
|
766
|
+
const conversationHistory = await getFormattedConversationHistory({
|
|
767
|
+
tenantId: artifactData.tenantId,
|
|
768
|
+
projectId: artifactData.projectId,
|
|
769
|
+
conversationId: artifactData.contextId,
|
|
770
|
+
options: {
|
|
771
|
+
limit: 10,
|
|
772
|
+
includeInternal: false,
|
|
773
|
+
messageTypes: ["chat"]
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
const toolCallEvent = this.events.find((event) => event.eventType === "tool_result" && event.data && "toolCallId" in event.data && event.data.toolCallId === artifactData.metadata?.toolCallId);
|
|
777
|
+
const toolContext = toolCallEvent ? {
|
|
778
|
+
toolName: toolCallEvent.data.toolName,
|
|
779
|
+
args: toolCallEvent.data.args
|
|
780
|
+
} : null;
|
|
781
|
+
let existingNames = [];
|
|
782
|
+
try {
|
|
783
|
+
if (artifactData.tenantId && artifactData.projectId && artifactData.taskId) existingNames = (await getLedgerArtifacts(dbClient_default)({
|
|
784
|
+
scopes: {
|
|
785
|
+
tenantId: artifactData.tenantId,
|
|
786
|
+
projectId: artifactData.projectId
|
|
787
|
+
},
|
|
788
|
+
taskId: artifactData.taskId
|
|
789
|
+
})).map((a) => a.name).filter(Boolean);
|
|
790
|
+
} catch (error) {
|
|
791
|
+
logger.warn({
|
|
792
|
+
sessionId: this.sessionId,
|
|
793
|
+
artifactId: artifactData.artifactId,
|
|
794
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
795
|
+
}, "Failed to fetch existing artifact names for context");
|
|
796
|
+
}
|
|
797
|
+
const toolName = artifactData.metadata?.toolName || "unknown";
|
|
798
|
+
const toolCallId = artifactData.metadata?.toolCallId || "unknown";
|
|
799
|
+
const prompt = `Create a unique name and description for this tool result artifact.
|
|
800
|
+
|
|
801
|
+
CRITICAL: Your name must be different from these existing artifacts: ${existingNames.length > 0 ? existingNames.join(", ") : "None yet"}
|
|
802
|
+
|
|
803
|
+
Tool Context: ${toolContext ? JSON.stringify(toolContext.args, null, 2) : "No args"}
|
|
804
|
+
Context: ${conversationHistory?.slice(-200) || "No context"}
|
|
805
|
+
Type: ${artifactData.artifactType || "data"}
|
|
806
|
+
Data: ${JSON.stringify(artifactData.data || artifactData.summaryData || {}, null, 2)}
|
|
807
|
+
|
|
808
|
+
Requirements:
|
|
809
|
+
- Name: Max 50 chars, be extremely specific to THIS EXACT tool execution
|
|
810
|
+
- Description: Max 150 chars, describe what THIS SPECIFIC tool call returned
|
|
811
|
+
- Focus on the unique aspects of this particular tool execution result
|
|
812
|
+
- Be descriptive about the actual content returned, not just the tool type
|
|
813
|
+
|
|
814
|
+
BAD Examples (too generic):
|
|
815
|
+
- "Search Results"
|
|
816
|
+
- "Tool Results"
|
|
817
|
+
- "${toolName} Results"
|
|
818
|
+
- "Data from ${toolName}"
|
|
819
|
+
- "Tool Output"
|
|
820
|
+
- "Search Data"
|
|
821
|
+
|
|
822
|
+
GOOD Examples:
|
|
823
|
+
- "GitHub API Rate Limits & Auth Methods"
|
|
824
|
+
- "React Component Props Documentation"
|
|
825
|
+
- "Database Schema for User Tables"
|
|
826
|
+
- "Pricing Tiers with Enterprise Features"
|
|
827
|
+
|
|
828
|
+
Make the name extremely specific to what this tool call actually returned, not generic.`;
|
|
829
|
+
let modelToUse = this.statusUpdateState?.summarizerModel;
|
|
830
|
+
if (!modelToUse?.model?.trim()) if (!this.statusUpdateState?.baseModel?.model?.trim()) {
|
|
831
|
+
if (artifactData.subAgentId && artifactData.tenantId && artifactData.projectId) try {
|
|
832
|
+
const agentData = await getSubAgentById(dbClient_default)({
|
|
833
|
+
scopes: {
|
|
834
|
+
tenantId: artifactData.tenantId,
|
|
835
|
+
projectId: artifactData.projectId,
|
|
836
|
+
agentId: this.agentId || ""
|
|
837
|
+
},
|
|
838
|
+
subAgentId: artifactData.subAgentId
|
|
839
|
+
});
|
|
840
|
+
if (agentData && "models" in agentData && agentData.models?.base?.model) {
|
|
841
|
+
modelToUse = agentData.models.base;
|
|
842
|
+
logger.info({
|
|
843
|
+
sessionId: this.sessionId,
|
|
844
|
+
artifactId: artifactData.artifactId,
|
|
845
|
+
subAgentId: artifactData.subAgentId,
|
|
846
|
+
model: modelToUse.model
|
|
847
|
+
}, "Using agent model configuration for artifact name generation");
|
|
848
|
+
}
|
|
849
|
+
} catch (error) {
|
|
850
|
+
logger.warn({
|
|
851
|
+
sessionId: this.sessionId,
|
|
852
|
+
artifactId: artifactData.artifactId,
|
|
853
|
+
subAgentId: artifactData.subAgentId,
|
|
854
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
855
|
+
}, "Failed to get agent model configuration");
|
|
856
|
+
}
|
|
857
|
+
if (!modelToUse?.model?.trim()) {
|
|
858
|
+
logger.warn({
|
|
859
|
+
sessionId: this.sessionId,
|
|
860
|
+
artifactId: artifactData.artifactId
|
|
861
|
+
}, "No model configuration available for artifact name generation, will use fallback names");
|
|
862
|
+
modelToUse = void 0;
|
|
863
|
+
}
|
|
864
|
+
} else modelToUse = this.statusUpdateState.baseModel;
|
|
865
|
+
let result;
|
|
866
|
+
if (!modelToUse) {
|
|
867
|
+
const toolCallSuffix = artifactData.metadata?.toolCallId?.slice(-8) || Date.now().toString().slice(-8);
|
|
868
|
+
result = {
|
|
869
|
+
name: `${artifactData.artifactType || "Artifact"} ${toolCallSuffix}`,
|
|
870
|
+
description: `${artifactData.artifactType || "Data"} from ${artifactData.metadata?.toolName || "tool"} (${artifactData.metadata?.toolCallId || "tool results"})`
|
|
871
|
+
};
|
|
872
|
+
} else {
|
|
873
|
+
const model = ModelFactory.createModel(modelToUse);
|
|
874
|
+
const schema = z.object({
|
|
875
|
+
name: z.string().describe("Concise, descriptive name for the artifact"),
|
|
876
|
+
description: z.string().describe("Brief description of the artifact's relevance to the user's question")
|
|
877
|
+
});
|
|
878
|
+
const { output: object } = await tracer.startActiveSpan("agent_session.generate_artifact_metadata", { attributes: {
|
|
879
|
+
"llm.model": this.statusUpdateState?.summarizerModel?.model,
|
|
880
|
+
"llm.operation": "generate_object",
|
|
881
|
+
"artifact.id": artifactData.artifactId,
|
|
882
|
+
"artifact.type": artifactData.artifactType,
|
|
883
|
+
"artifact.summary": JSON.stringify(artifactData.summaryData, null, 2),
|
|
884
|
+
"artifact.full": JSON.stringify(artifactData.data || artifactData.summaryData, null, 2),
|
|
885
|
+
"prompt.length": prompt.length
|
|
886
|
+
} }, async (generationSpan) => {
|
|
887
|
+
const maxRetries = 3;
|
|
888
|
+
let lastError = null;
|
|
889
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) try {
|
|
890
|
+
const result$1 = await generateText({
|
|
891
|
+
model,
|
|
892
|
+
prompt,
|
|
893
|
+
output: Output.object({ schema }),
|
|
894
|
+
experimental_telemetry: {
|
|
895
|
+
isEnabled: true,
|
|
896
|
+
functionId: `artifact_processing_${artifactData.artifactId}`,
|
|
897
|
+
recordInputs: true,
|
|
898
|
+
recordOutputs: true,
|
|
899
|
+
metadata: {
|
|
900
|
+
operation: "artifact_name_description_generation",
|
|
901
|
+
sessionId: this.sessionId,
|
|
902
|
+
attempt
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
generationSpan.setAttributes({
|
|
907
|
+
"artifact.id": artifactData.artifactId,
|
|
908
|
+
"artifact.type": artifactData.artifactType,
|
|
909
|
+
"artifact.name": result$1.output.name,
|
|
910
|
+
"artifact.description": result$1.output.description,
|
|
911
|
+
"artifact.summary": JSON.stringify(artifactData.summaryData, null, 2),
|
|
912
|
+
"artifact.full": JSON.stringify(artifactData.data || artifactData.summaryData, null, 2),
|
|
913
|
+
"generation.name_length": result$1.output.name.length,
|
|
914
|
+
"generation.description_length": result$1.output.description.length,
|
|
915
|
+
"generation.attempts": attempt
|
|
916
|
+
});
|
|
917
|
+
generationSpan.setStatus({ code: SpanStatusCode.OK });
|
|
918
|
+
return result$1;
|
|
919
|
+
} catch (error) {
|
|
920
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
921
|
+
logger.warn({
|
|
922
|
+
sessionId: this.sessionId,
|
|
923
|
+
artifactId: artifactData.artifactId,
|
|
924
|
+
attempt,
|
|
925
|
+
maxRetries,
|
|
926
|
+
error: lastError.message
|
|
927
|
+
}, `Artifact name/description generation failed, attempt ${attempt}/${maxRetries}`);
|
|
928
|
+
if (attempt < maxRetries) {
|
|
929
|
+
const backoffMs = Math.min(ARTIFACT_GENERATION_BACKOFF_INITIAL_MS * 2 ** (attempt - 1), ARTIFACT_GENERATION_BACKOFF_MAX_MS);
|
|
930
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
setSpanWithError$1(generationSpan, lastError instanceof Error ? lastError : new Error(String(lastError)));
|
|
934
|
+
throw new Error(`Artifact name/description generation failed after ${maxRetries} attempts: ${lastError?.message}`);
|
|
935
|
+
});
|
|
936
|
+
result = object;
|
|
937
|
+
}
|
|
938
|
+
if (existingNames.includes(result.name)) {
|
|
939
|
+
const toolCallSuffix = toolCallId.slice(-8);
|
|
940
|
+
const originalName = result.name;
|
|
941
|
+
result.name = result.name.length + toolCallSuffix.length + 1 <= 50 ? `${result.name} ${toolCallSuffix}` : `${result.name.substring(0, 50 - toolCallSuffix.length - 1)} ${toolCallSuffix}`;
|
|
942
|
+
logger.info({
|
|
943
|
+
sessionId: this.sessionId,
|
|
944
|
+
artifactId: artifactData.artifactId,
|
|
945
|
+
originalName,
|
|
946
|
+
uniqueName: result.name,
|
|
947
|
+
reason: "Name conflict resolved with toolCallId suffix"
|
|
948
|
+
}, "Updated artifact name for uniqueness");
|
|
949
|
+
}
|
|
950
|
+
try {
|
|
951
|
+
if (!this.artifactService) throw new Error("ArtifactService is not initialized");
|
|
952
|
+
await this.artifactService.saveArtifact({
|
|
953
|
+
artifactId: artifactData.artifactId,
|
|
954
|
+
name: result.name,
|
|
955
|
+
description: result.description,
|
|
956
|
+
type: artifactData.artifactType || "source",
|
|
957
|
+
data: artifactData.data || {},
|
|
958
|
+
summaryData: artifactData.summaryData,
|
|
959
|
+
metadata: artifactData.metadata || {},
|
|
960
|
+
toolCallId: artifactData.toolCallId
|
|
961
|
+
});
|
|
962
|
+
mainSaveSucceeded = true;
|
|
963
|
+
span.setAttributes({
|
|
964
|
+
"artifact.name": result.name,
|
|
965
|
+
"artifact.description": result.description,
|
|
966
|
+
"processing.success": true
|
|
967
|
+
});
|
|
968
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
969
|
+
} catch (saveError) {
|
|
970
|
+
logger.error({
|
|
971
|
+
sessionId: this.sessionId,
|
|
972
|
+
artifactId: artifactData.artifactId,
|
|
973
|
+
error: saveError instanceof Error ? saveError.message : "Unknown error",
|
|
974
|
+
errorName: saveError instanceof Error ? saveError.name : void 0,
|
|
975
|
+
errorCause: saveError instanceof Error ? saveError.cause : void 0,
|
|
976
|
+
errorCode: saveError?.code || saveError?.errno || void 0,
|
|
977
|
+
artifactType: artifactData.artifactType,
|
|
978
|
+
dataKeys: artifactData.data ? Object.keys(artifactData.data) : [],
|
|
979
|
+
metadataKeys: artifactData.metadata ? Object.keys(artifactData.metadata) : []
|
|
980
|
+
}, "Main artifact save failed, will attempt fallback");
|
|
981
|
+
}
|
|
982
|
+
if (!mainSaveSucceeded) try {
|
|
983
|
+
if (artifactData.tenantId && artifactData.projectId) {
|
|
984
|
+
await new ArtifactService({
|
|
985
|
+
tenantId: artifactData.tenantId,
|
|
986
|
+
projectId: artifactData.projectId,
|
|
987
|
+
contextId: artifactData.contextId || "unknown",
|
|
988
|
+
taskId: artifactData.taskId,
|
|
989
|
+
sessionId: this.sessionId
|
|
990
|
+
}).saveArtifact({
|
|
991
|
+
artifactId: artifactData.artifactId,
|
|
992
|
+
name: `Artifact ${artifactData.artifactId.substring(0, 8)}`,
|
|
993
|
+
description: `${artifactData.artifactType || "Data"} from ${artifactData.metadata?.toolName || "tool results"}`,
|
|
994
|
+
type: artifactData.artifactType || "source",
|
|
995
|
+
data: artifactData.data || {},
|
|
996
|
+
summaryData: artifactData.summaryData,
|
|
997
|
+
metadata: artifactData.metadata || {},
|
|
998
|
+
toolCallId: artifactData.toolCallId
|
|
999
|
+
});
|
|
1000
|
+
logger.info({
|
|
1001
|
+
sessionId: this.sessionId,
|
|
1002
|
+
artifactId: artifactData.artifactId
|
|
1003
|
+
}, "Saved artifact with fallback name/description after main save failed");
|
|
1004
|
+
}
|
|
1005
|
+
} catch (fallbackError) {
|
|
1006
|
+
if (fallbackError instanceof Error && (fallbackError.message?.includes("UNIQUE") || fallbackError.message?.includes("duplicate"))) {} else logger.error({
|
|
1007
|
+
sessionId: this.sessionId,
|
|
1008
|
+
artifactId: artifactData.artifactId,
|
|
1009
|
+
error: fallbackError instanceof Error ? fallbackError.message : "Unknown error",
|
|
1010
|
+
errorName: fallbackError instanceof Error ? fallbackError.name : void 0,
|
|
1011
|
+
errorCause: fallbackError instanceof Error ? fallbackError.cause : void 0,
|
|
1012
|
+
errorCode: fallbackError?.code || fallbackError?.errno || void 0,
|
|
1013
|
+
artifactType: artifactData.artifactType,
|
|
1014
|
+
dataKeys: artifactData.data ? Object.keys(artifactData.data) : [],
|
|
1015
|
+
metadataKeys: artifactData.metadata ? Object.keys(artifactData.metadata) : []
|
|
1016
|
+
}, "Failed to save artifact even with fallback");
|
|
1017
|
+
}
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
setSpanWithError$1(span, error instanceof Error ? error : new Error(String(error)));
|
|
1020
|
+
logger.error({
|
|
1021
|
+
sessionId: this.sessionId,
|
|
1022
|
+
artifactId: artifactData.artifactId,
|
|
1023
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1024
|
+
}, "Failed to process artifact (name/description generation failed)");
|
|
1025
|
+
} finally {
|
|
1026
|
+
span.end();
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Cache an artifact in this session for immediate access
|
|
1032
|
+
*/
|
|
1033
|
+
setArtifactCache(key, artifact) {
|
|
1034
|
+
this.artifactCache.set(key, artifact);
|
|
1035
|
+
logger.debug({
|
|
1036
|
+
sessionId: this.sessionId,
|
|
1037
|
+
key
|
|
1038
|
+
}, "Artifact cached in session");
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Get session-scoped ArtifactService instance
|
|
1042
|
+
*/
|
|
1043
|
+
getArtifactService() {
|
|
1044
|
+
return this.artifactService || null;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Get session-scoped ArtifactParser instance
|
|
1048
|
+
*/
|
|
1049
|
+
getArtifactParser() {
|
|
1050
|
+
return this.artifactParser || null;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Get an artifact from this session cache
|
|
1054
|
+
*/
|
|
1055
|
+
getArtifactCache(key) {
|
|
1056
|
+
const artifact = this.artifactCache.get(key);
|
|
1057
|
+
logger.debug({
|
|
1058
|
+
sessionId: this.sessionId,
|
|
1059
|
+
key,
|
|
1060
|
+
found: !!artifact
|
|
1061
|
+
}, "Artifact cache lookup");
|
|
1062
|
+
return artifact || null;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Update artifact components in the shared ArtifactService
|
|
1066
|
+
*/
|
|
1067
|
+
updateArtifactComponents(artifactComponents) {
|
|
1068
|
+
if (this.artifactService) this.artifactService.updateArtifactComponents(artifactComponents);
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
/**
|
|
1072
|
+
* Manages AgentSession instances for message-level tracking
|
|
1073
|
+
*/
|
|
1074
|
+
var AgentSessionManager = class {
|
|
1075
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1076
|
+
/**
|
|
1077
|
+
* Create a new session for a message
|
|
1078
|
+
*/
|
|
1079
|
+
createSession(messageId, agentId, tenantId, projectId, contextId) {
|
|
1080
|
+
const sessionId = messageId;
|
|
1081
|
+
const session = new AgentSession(sessionId, messageId, agentId, tenantId, projectId, contextId);
|
|
1082
|
+
this.sessions.set(sessionId, session);
|
|
1083
|
+
logger.info({
|
|
1084
|
+
sessionId,
|
|
1085
|
+
messageId,
|
|
1086
|
+
agentId,
|
|
1087
|
+
tenantId,
|
|
1088
|
+
projectId,
|
|
1089
|
+
contextId
|
|
1090
|
+
}, "AgentSession created");
|
|
1091
|
+
return sessionId;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Initialize status updates for a session
|
|
1095
|
+
*/
|
|
1096
|
+
initializeStatusUpdates(sessionId, config, summarizerModel, baseModel) {
|
|
1097
|
+
const session = this.sessions.get(sessionId);
|
|
1098
|
+
if (session) session.initializeStatusUpdates(config, summarizerModel, baseModel);
|
|
1099
|
+
else logger.error({
|
|
1100
|
+
sessionId,
|
|
1101
|
+
availableSessions: Array.from(this.sessions.keys())
|
|
1102
|
+
}, "Session not found for status updates initialization");
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Enable emit operations for a session to send data operations
|
|
1106
|
+
*/
|
|
1107
|
+
enableEmitOperations(sessionId) {
|
|
1108
|
+
const session = this.sessions.get(sessionId);
|
|
1109
|
+
if (session) session.enableEmitOperations();
|
|
1110
|
+
else logger.error({
|
|
1111
|
+
sessionId,
|
|
1112
|
+
availableSessions: Array.from(this.sessions.keys())
|
|
1113
|
+
}, "Session not found for emit operations enablement");
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Get an existing session
|
|
1117
|
+
*/
|
|
1118
|
+
getSession(sessionId) {
|
|
1119
|
+
return this.sessions.get(sessionId) || null;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Record an event in a session
|
|
1123
|
+
* Generic type parameter T ensures eventType and data are correctly paired
|
|
1124
|
+
*/
|
|
1125
|
+
recordEvent(sessionId, eventType, subAgentId, data) {
|
|
1126
|
+
const session = this.sessions.get(sessionId);
|
|
1127
|
+
if (!session) {
|
|
1128
|
+
logger.warn({ sessionId }, "Attempted to record event in non-existent session");
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
session.recordEvent(eventType, subAgentId, data);
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* End a session and return the final event data
|
|
1135
|
+
*/
|
|
1136
|
+
async endSession(sessionId) {
|
|
1137
|
+
const session = this.sessions.get(sessionId);
|
|
1138
|
+
if (!session) {
|
|
1139
|
+
logger.warn({ sessionId }, "Attempted to end non-existent session");
|
|
1140
|
+
return [];
|
|
1141
|
+
}
|
|
1142
|
+
const events = session.getEvents();
|
|
1143
|
+
const summary = session.getSummary();
|
|
1144
|
+
logger.info({
|
|
1145
|
+
sessionId,
|
|
1146
|
+
summary
|
|
1147
|
+
}, "AgentSession ended");
|
|
1148
|
+
await session.cleanup();
|
|
1149
|
+
this.sessions.delete(sessionId);
|
|
1150
|
+
return events;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Set text streaming state for a session
|
|
1154
|
+
*/
|
|
1155
|
+
setTextStreaming(sessionId, isStreaming) {
|
|
1156
|
+
const session = this.sessions.get(sessionId);
|
|
1157
|
+
if (session) session.setTextStreaming(isStreaming);
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Get summary of all active sessions
|
|
1161
|
+
*/
|
|
1162
|
+
getActiveSessions() {
|
|
1163
|
+
return Array.from(this.sessions.values()).map((session) => ({
|
|
1164
|
+
sessionId: session.sessionId,
|
|
1165
|
+
messageId: session.messageId,
|
|
1166
|
+
eventCount: session.getEvents().length
|
|
1167
|
+
}));
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Cache an artifact in the specified session
|
|
1171
|
+
*/
|
|
1172
|
+
async setArtifactCache(sessionId, key, artifact) {
|
|
1173
|
+
const session = this.sessions.get(sessionId);
|
|
1174
|
+
if (session) session.setArtifactCache(key, artifact);
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Get an artifact from the specified session cache
|
|
1178
|
+
*/
|
|
1179
|
+
async getArtifactCache(sessionId, key) {
|
|
1180
|
+
const session = this.sessions.get(sessionId);
|
|
1181
|
+
return session ? session.getArtifactCache(key) : null;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Get session-scoped ArtifactService instance
|
|
1185
|
+
*/
|
|
1186
|
+
getArtifactService(sessionId) {
|
|
1187
|
+
const session = this.sessions.get(sessionId);
|
|
1188
|
+
return session ? session.getArtifactService() : null;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Get session-scoped ArtifactParser instance
|
|
1192
|
+
*/
|
|
1193
|
+
getArtifactParser(sessionId) {
|
|
1194
|
+
const session = this.sessions.get(sessionId);
|
|
1195
|
+
return session ? session.getArtifactParser() : null;
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Update artifact components for a session
|
|
1199
|
+
*/
|
|
1200
|
+
updateArtifactComponents(sessionId, artifactComponents) {
|
|
1201
|
+
const session = this.sessions.get(sessionId);
|
|
1202
|
+
if (session) session.updateArtifactComponents(artifactComponents);
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
const agentSessionManager = new AgentSessionManager();
|
|
1206
|
+
|
|
1207
|
+
//#endregion
|
|
1208
|
+
export { AgentSession, AgentSessionManager, agentSessionManager };
|