agent-world 0.12.3 → 0.15.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/README.md +105 -17
- package/dist/cli/commands.d.ts +7 -1
- package/dist/cli/commands.js +27 -10
- package/dist/cli/hitl.d.ts +9 -2
- package/dist/cli/hitl.js +61 -20
- package/dist/cli/index.js +250 -96
- package/dist/cli/system-events.d.ts +27 -0
- package/dist/cli/system-events.js +63 -0
- package/dist/core/activity-tracker.d.ts +38 -2
- package/dist/core/activity-tracker.d.ts.map +1 -1
- package/dist/core/activity-tracker.js +62 -9
- package/dist/core/activity-tracker.js.map +1 -1
- package/dist/core/anthropic-direct.d.ts +2 -0
- package/dist/core/anthropic-direct.d.ts.map +1 -1
- package/dist/core/anthropic-direct.js +43 -1
- package/dist/core/anthropic-direct.js.map +1 -1
- package/dist/core/chat-constants.d.ts +12 -0
- package/dist/core/chat-constants.d.ts.map +1 -1
- package/dist/core/chat-constants.js +5 -0
- package/dist/core/chat-constants.js.map +1 -1
- package/dist/core/create-agent-tool.d.ts +28 -25
- package/dist/core/create-agent-tool.d.ts.map +1 -1
- package/dist/core/create-agent-tool.js +264 -141
- package/dist/core/create-agent-tool.js.map +1 -1
- package/dist/core/events/index.d.ts +5 -2
- package/dist/core/events/index.d.ts.map +1 -1
- package/dist/core/events/index.js +5 -2
- package/dist/core/events/index.js.map +1 -1
- package/dist/core/events/memory-manager.d.ts +26 -1
- package/dist/core/events/memory-manager.d.ts.map +1 -1
- package/dist/core/events/memory-manager.js +877 -72
- package/dist/core/events/memory-manager.js.map +1 -1
- package/dist/core/events/orchestrator.d.ts +8 -0
- package/dist/core/events/orchestrator.d.ts.map +1 -1
- package/dist/core/events/orchestrator.js +214 -38
- package/dist/core/events/orchestrator.js.map +1 -1
- package/dist/core/events/persistence.d.ts +21 -14
- package/dist/core/events/persistence.d.ts.map +1 -1
- package/dist/core/events/persistence.js +100 -61
- package/dist/core/events/persistence.js.map +1 -1
- package/dist/core/events/publishers.d.ts +13 -16
- package/dist/core/events/publishers.d.ts.map +1 -1
- package/dist/core/events/publishers.js +54 -55
- package/dist/core/events/publishers.js.map +1 -1
- package/dist/core/events/subscribers.d.ts +17 -14
- package/dist/core/events/subscribers.d.ts.map +1 -1
- package/dist/core/events/subscribers.js +68 -147
- package/dist/core/events/subscribers.js.map +1 -1
- package/dist/core/events/title-scheduler.d.ts +27 -0
- package/dist/core/events/title-scheduler.d.ts.map +1 -0
- package/dist/core/events/title-scheduler.js +135 -0
- package/dist/core/events/title-scheduler.js.map +1 -0
- package/dist/core/events/tool-bridge-logging.d.ts +4 -1
- package/dist/core/events/tool-bridge-logging.d.ts.map +1 -1
- package/dist/core/events/tool-bridge-logging.js +112 -13
- package/dist/core/events/tool-bridge-logging.js.map +1 -1
- package/dist/core/events-metadata.d.ts.map +1 -1
- package/dist/core/events-metadata.js +8 -4
- package/dist/core/events-metadata.js.map +1 -1
- package/dist/core/export.d.ts +1 -1
- package/dist/core/export.d.ts.map +1 -1
- package/dist/core/export.js +2 -15
- package/dist/core/export.js.map +1 -1
- package/dist/core/feature-path-logging.d.ts +50 -0
- package/dist/core/feature-path-logging.d.ts.map +1 -0
- package/dist/core/feature-path-logging.js +130 -0
- package/dist/core/feature-path-logging.js.map +1 -0
- package/dist/core/file-tools.d.ts +57 -1
- package/dist/core/file-tools.d.ts.map +1 -1
- package/dist/core/file-tools.js +329 -29
- package/dist/core/file-tools.js.map +1 -1
- package/dist/core/google-direct.d.ts +6 -1
- package/dist/core/google-direct.d.ts.map +1 -1
- package/dist/core/google-direct.js +76 -7
- package/dist/core/google-direct.js.map +1 -1
- package/dist/core/heartbeat.d.ts +34 -0
- package/dist/core/heartbeat.d.ts.map +1 -0
- package/dist/core/heartbeat.js +153 -0
- package/dist/core/heartbeat.js.map +1 -0
- package/dist/core/hitl-tool.d.ts +73 -0
- package/dist/core/hitl-tool.d.ts.map +1 -0
- package/dist/core/hitl-tool.js +284 -0
- package/dist/core/hitl-tool.js.map +1 -0
- package/dist/core/hitl.d.ts +85 -8
- package/dist/core/hitl.d.ts.map +1 -1
- package/dist/core/hitl.js +375 -61
- package/dist/core/hitl.js.map +1 -1
- package/dist/core/index.d.ts +12 -7
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +11 -6
- package/dist/core/index.js.map +1 -1
- package/dist/core/llm-manager.d.ts +17 -0
- package/dist/core/llm-manager.d.ts.map +1 -1
- package/dist/core/llm-manager.js +335 -43
- package/dist/core/llm-manager.js.map +1 -1
- package/dist/core/load-skill-tool.d.ts +36 -3
- package/dist/core/load-skill-tool.d.ts.map +1 -1
- package/dist/core/load-skill-tool.js +807 -93
- package/dist/core/load-skill-tool.js.map +1 -1
- package/dist/core/logger.d.ts +14 -0
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +15 -0
- package/dist/core/logger.js.map +1 -1
- package/dist/core/managers.d.ts +41 -52
- package/dist/core/managers.d.ts.map +1 -1
- package/dist/core/managers.js +422 -533
- package/dist/core/managers.js.map +1 -1
- package/dist/core/mcp-server-registry.d.ts +19 -2
- package/dist/core/mcp-server-registry.d.ts.map +1 -1
- package/dist/core/mcp-server-registry.js +168 -12
- package/dist/core/mcp-server-registry.js.map +1 -1
- package/dist/core/message-cutoff.d.ts +29 -0
- package/dist/core/message-cutoff.d.ts.map +1 -0
- package/dist/core/message-cutoff.js +63 -0
- package/dist/core/message-cutoff.js.map +1 -0
- package/dist/core/message-edit-manager.d.ts +54 -0
- package/dist/core/message-edit-manager.d.ts.map +1 -0
- package/dist/core/message-edit-manager.js +602 -0
- package/dist/core/message-edit-manager.js.map +1 -0
- package/dist/core/message-prep.d.ts +2 -0
- package/dist/core/message-prep.d.ts.map +1 -1
- package/dist/core/message-prep.js +39 -12
- package/dist/core/message-prep.js.map +1 -1
- package/dist/core/message-processing-control.d.ts +1 -0
- package/dist/core/message-processing-control.d.ts.map +1 -1
- package/dist/core/message-processing-control.js +23 -6
- package/dist/core/message-processing-control.js.map +1 -1
- package/dist/core/openai-direct.d.ts +9 -3
- package/dist/core/openai-direct.d.ts.map +1 -1
- package/dist/core/openai-direct.js +267 -33
- package/dist/core/openai-direct.js.map +1 -1
- package/dist/core/optional-tracers/opik-runtime.d.ts +32 -0
- package/dist/core/optional-tracers/opik-runtime.d.ts.map +1 -0
- package/dist/core/optional-tracers/opik-runtime.js +141 -0
- package/dist/core/optional-tracers/opik-runtime.js.map +1 -0
- package/dist/core/queue-manager.d.ts +84 -0
- package/dist/core/queue-manager.d.ts.map +1 -0
- package/dist/core/queue-manager.js +814 -0
- package/dist/core/queue-manager.js.map +1 -0
- package/dist/core/reasoning-controls.d.ts +30 -0
- package/dist/core/reasoning-controls.d.ts.map +1 -0
- package/dist/core/reasoning-controls.js +118 -0
- package/dist/core/reasoning-controls.js.map +1 -0
- package/dist/core/reliability-config.d.ts +82 -0
- package/dist/core/reliability-config.d.ts.map +1 -0
- package/dist/core/reliability-config.js +106 -0
- package/dist/core/reliability-config.js.map +1 -0
- package/dist/core/reliability-runtime.d.ts +53 -0
- package/dist/core/reliability-runtime.d.ts.map +1 -0
- package/dist/core/reliability-runtime.js +92 -0
- package/dist/core/reliability-runtime.js.map +1 -0
- package/dist/core/security/guardrails.d.ts +21 -0
- package/dist/core/security/guardrails.d.ts.map +1 -0
- package/dist/core/security/guardrails.js +111 -0
- package/dist/core/security/guardrails.js.map +1 -0
- package/dist/core/send-message-tool.d.ts +79 -0
- package/dist/core/send-message-tool.d.ts.map +1 -0
- package/dist/core/send-message-tool.js +222 -0
- package/dist/core/send-message-tool.js.map +1 -0
- package/dist/core/shell-cmd-tool.d.ts +82 -1
- package/dist/core/shell-cmd-tool.d.ts.map +1 -1
- package/dist/core/shell-cmd-tool.js +854 -42
- package/dist/core/shell-cmd-tool.js.map +1 -1
- package/dist/core/skill-registry.d.ts +2 -0
- package/dist/core/skill-registry.d.ts.map +1 -1
- package/dist/core/skill-registry.js +52 -2
- package/dist/core/skill-registry.js.map +1 -1
- package/dist/core/storage/eventStorage/fileEventStorage.d.ts +5 -0
- package/dist/core/storage/eventStorage/fileEventStorage.d.ts.map +1 -1
- package/dist/core/storage/eventStorage/fileEventStorage.js +61 -0
- package/dist/core/storage/eventStorage/fileEventStorage.js.map +1 -1
- package/dist/core/storage/eventStorage/memoryEventStorage.d.ts +5 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.d.ts.map +1 -1
- package/dist/core/storage/eventStorage/memoryEventStorage.js +34 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.js.map +1 -1
- package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts +1 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts.map +1 -1
- package/dist/core/storage/eventStorage/sqliteEventStorage.js +19 -2
- package/dist/core/storage/eventStorage/sqliteEventStorage.js.map +1 -1
- package/dist/core/storage/eventStorage/types.d.ts +6 -0
- package/dist/core/storage/eventStorage/types.d.ts.map +1 -1
- package/dist/core/storage/eventStorage/types.js +1 -0
- package/dist/core/storage/eventStorage/types.js.map +1 -1
- package/dist/core/storage/eventStorage/validation.d.ts.map +1 -1
- package/dist/core/storage/eventStorage/validation.js +2 -1
- package/dist/core/storage/eventStorage/validation.js.map +1 -1
- package/dist/core/storage/github-world-import.d.ts +84 -0
- package/dist/core/storage/github-world-import.d.ts.map +1 -0
- package/dist/core/storage/github-world-import.js +365 -0
- package/dist/core/storage/github-world-import.js.map +1 -0
- package/dist/core/storage/memory-storage.d.ts +19 -8
- package/dist/core/storage/memory-storage.d.ts.map +1 -1
- package/dist/core/storage/memory-storage.js +147 -49
- package/dist/core/storage/memory-storage.js.map +1 -1
- package/dist/core/storage/queue-storage.d.ts +1 -0
- package/dist/core/storage/queue-storage.d.ts.map +1 -1
- package/dist/core/storage/queue-storage.js +3 -2
- package/dist/core/storage/queue-storage.js.map +1 -1
- package/dist/core/storage/sqlite-storage.d.ts +14 -9
- package/dist/core/storage/sqlite-storage.d.ts.map +1 -1
- package/dist/core/storage/sqlite-storage.js +131 -154
- package/dist/core/storage/sqlite-storage.js.map +1 -1
- package/dist/core/storage/storage-factory.d.ts +3 -0
- package/dist/core/storage/storage-factory.d.ts.map +1 -1
- package/dist/core/storage/storage-factory.js +175 -89
- package/dist/core/storage/storage-factory.js.map +1 -1
- package/dist/core/storage/world-storage.d.ts +1 -1
- package/dist/core/storage/world-storage.d.ts.map +1 -1
- package/dist/core/storage/world-storage.js +5 -1
- package/dist/core/storage/world-storage.js.map +1 -1
- package/dist/core/storage-init.d.ts +11 -0
- package/dist/core/storage-init.d.ts.map +1 -0
- package/dist/core/storage-init.js +122 -0
- package/dist/core/storage-init.js.map +1 -0
- package/dist/core/subscription.d.ts +8 -1
- package/dist/core/subscription.d.ts.map +1 -1
- package/dist/core/subscription.js +130 -23
- package/dist/core/subscription.js.map +1 -1
- package/dist/core/tool-approval.d.ts +45 -0
- package/dist/core/tool-approval.d.ts.map +1 -0
- package/dist/core/tool-approval.js +223 -0
- package/dist/core/tool-approval.js.map +1 -0
- package/dist/core/tool-execution-envelope.d.ts +87 -0
- package/dist/core/tool-execution-envelope.d.ts.map +1 -0
- package/dist/core/tool-execution-envelope.js +168 -0
- package/dist/core/tool-execution-envelope.js.map +1 -0
- package/dist/core/tool-utils.d.ts +9 -2
- package/dist/core/tool-utils.d.ts.map +1 -1
- package/dist/core/tool-utils.js +122 -28
- package/dist/core/tool-utils.js.map +1 -1
- package/dist/core/types.d.ts +69 -36
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +3 -2
- package/dist/core/types.js.map +1 -1
- package/dist/core/utils.d.ts +16 -0
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +99 -24
- package/dist/core/utils.js.map +1 -1
- package/dist/core/web-fetch-tool.d.ts +72 -0
- package/dist/core/web-fetch-tool.d.ts.map +1 -0
- package/dist/core/web-fetch-tool.js +491 -0
- package/dist/core/web-fetch-tool.js.map +1 -0
- package/dist/core/world-registry.d.ts +84 -0
- package/dist/core/world-registry.d.ts.map +1 -0
- package/dist/core/world-registry.js +247 -0
- package/dist/core/world-registry.js.map +1 -0
- package/dist/public/assets/index-Be-1xtV-.js +104 -0
- package/dist/public/assets/index-tsDdiXDU.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/public/mcp-sandbox-proxy.html +148 -0
- package/dist/server/api.js +288 -58
- package/dist/server/error-response.d.ts +27 -0
- package/dist/server/error-response.js +77 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +6 -2
- package/dist/server/sse-handler.d.ts +13 -2
- package/dist/server/sse-handler.js +194 -26
- package/migrations/0015_add_message_queue.sql +36 -0
- package/migrations/0016_add_world_heartbeat.sql +13 -0
- package/migrations/0017_add_title_provenance.sql +7 -0
- package/package.json +31 -10
- package/dist/public/assets/index-BO20H4xt.js +0 -96
- package/dist/public/assets/index-ETY7W5_S.css +0 -1
package/dist/core/llm-manager.js
CHANGED
|
@@ -94,6 +94,16 @@
|
|
|
94
94
|
* - Queue-based serialization prevents API rate limits and resource conflicts
|
|
95
95
|
*
|
|
96
96
|
* Recent Changes:
|
|
97
|
+
* - 2026-03-13: Added streamed `reasoningContent` forwarding and world-variable reasoning-effort propagation for OpenAI-compatible and Google direct providers.
|
|
98
|
+
* - 2026-03-06: Moved `shell_cmd` working-directory prompt guidance into tool-aware system-message injection.
|
|
99
|
+
* - 2026-03-06: Widened queue timeout field typing to `number` so runtime timeout overrides compile cleanly.
|
|
100
|
+
* - 2026-03-05: Added chat-scoped LLM timeout status system events (`taking too long` warning + hard-timeout event), enforced timeout-triggered abort signaling in queue processing, and classified queue timeouts separately from user cancellations.
|
|
101
|
+
* - 2026-03-05: Switched LLM queue timeout defaults to shared reliability config.
|
|
102
|
+
* - 2026-03-04: Azure client creation now maps `agent.model` to Azure deployment name (with config deployment fallback) so world/agent model selection controls deployment URL routing.
|
|
103
|
+
* - 2026-02-28: Added canonical feature-path diagnostics (`llm.prep`, `llm.request.*`, `llm.response.*`) with opt-in raw payload logging and correlation metadata.
|
|
104
|
+
* - 2026-02-24: Required explicit chatId for streaming SSE emission and propagated chatId through start/chunk/end/error events for strict chat-scoped frontend filtering.
|
|
105
|
+
* - 2026-02-20: Switched injected tool-usage guidance to shared `buildToolUsagePromptSection()` so HITL and other tool rules are centralized in one utility.
|
|
106
|
+
* - 2026-02-20: Updated injected tool-usage guidance to direct LLMs to use `human_intervention_request` for human clarifications and confirmations.
|
|
97
107
|
* - 2026-02-13: Reclassified stop-triggered aborts as cancellation/info logs (not errors) in queue and non-streaming paths.
|
|
98
108
|
* - 2026-02-13: Added merged external+queue abort-signal support so chat stop requests can cancel follow-up continuation calls.
|
|
99
109
|
* - 2026-02-13: Added chat-scoped LLM cancellation controls so Electron stop requests can abort active and queued calls by `worldId` + `chatId`.
|
|
@@ -117,9 +127,11 @@ import { filterClientSideMessages } from './message-prep.js';
|
|
|
117
127
|
import { createClientForProvider, streamOpenAIResponse, generateOpenAIResponse } from './openai-direct.js';
|
|
118
128
|
import { createAnthropicClientForAgent, streamAnthropicResponse, generateAnthropicResponse } from './anthropic-direct.js';
|
|
119
129
|
import { createGoogleClientForAgent, streamGoogleResponse, generateGoogleResponse } from './google-direct.js';
|
|
120
|
-
import { generateId } from './utils.js';
|
|
130
|
+
import { buildToolUsagePromptSection, generateId, getDefaultWorkingDirectory, getEnvValueFromText } from './utils.js';
|
|
121
131
|
import { createCategoryLogger } from './logger.js';
|
|
132
|
+
import { buildFeaturePathCorrelation, mergeFeaturePathData, sanitizeRawPayloadForLog, shouldEmitRawLog } from './feature-path-logging.js';
|
|
122
133
|
import { createStorageWithWrappers } from './storage/storage-factory.js';
|
|
134
|
+
import { RELIABILITY_CONFIG } from './reliability-config.js';
|
|
123
135
|
// Granular function-specific loggers for detailed debugging control
|
|
124
136
|
const loggerQueue = createCategoryLogger('llm.queue');
|
|
125
137
|
const loggerStreaming = createCategoryLogger('llm.streaming');
|
|
@@ -127,6 +139,11 @@ const loggerGeneration = createCategoryLogger('llm.generation');
|
|
|
127
139
|
const loggerProvider = createCategoryLogger('llm.provider');
|
|
128
140
|
const loggerMCP = createCategoryLogger('llm.mcp');
|
|
129
141
|
const loggerUtil = createCategoryLogger('llm.util');
|
|
142
|
+
const loggerPrep = createCategoryLogger('llm.prep');
|
|
143
|
+
const loggerRequestMeta = createCategoryLogger('llm.request.meta');
|
|
144
|
+
const loggerRequestRaw = createCategoryLogger('llm.request.raw');
|
|
145
|
+
const loggerResponseMeta = createCategoryLogger('llm.response.meta');
|
|
146
|
+
const loggerResponseRaw = createCategoryLogger('llm.response.raw');
|
|
130
147
|
import { getLLMProviderConfig } from './llm-config.js';
|
|
131
148
|
// LLM Integration Utilities
|
|
132
149
|
function stripCustomFields(message) {
|
|
@@ -142,17 +159,90 @@ function stripCustomFieldsFromMessages(messages) {
|
|
|
142
159
|
// Then strip custom fields
|
|
143
160
|
return filteredMessages.map(stripCustomFields);
|
|
144
161
|
}
|
|
162
|
+
function summarizeMessagesForLLM(messages) {
|
|
163
|
+
return {
|
|
164
|
+
messageCount: messages.length,
|
|
165
|
+
systemMessages: messages.filter(m => m.role === 'system').length,
|
|
166
|
+
userMessages: messages.filter(m => m.role === 'user').length,
|
|
167
|
+
assistantMessages: messages.filter(m => m.role === 'assistant').length,
|
|
168
|
+
toolMessages: messages.filter(m => m.role === 'tool').length,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function emitLLMRequestDiagnostics(params) {
|
|
172
|
+
const correlation = buildFeaturePathCorrelation({
|
|
173
|
+
worldId: params.world.id,
|
|
174
|
+
chatId: params.chatId,
|
|
175
|
+
agentId: params.agent.id,
|
|
176
|
+
messageId: params.messageId,
|
|
177
|
+
turnId: params.messageId,
|
|
178
|
+
});
|
|
179
|
+
const messageSummary = summarizeMessagesForLLM(params.preparedMessages);
|
|
180
|
+
const toolNames = Object.keys(params.mcpTools);
|
|
181
|
+
loggerPrep.debug('Prepared messages for LLM request', mergeFeaturePathData(correlation, {
|
|
182
|
+
...messageSummary,
|
|
183
|
+
toolCount: toolNames.length,
|
|
184
|
+
}));
|
|
185
|
+
loggerRequestMeta.debug('LLM request ready', mergeFeaturePathData(correlation, {
|
|
186
|
+
provider: params.agent.provider,
|
|
187
|
+
model: params.agent.model,
|
|
188
|
+
...messageSummary,
|
|
189
|
+
toolCount: toolNames.length,
|
|
190
|
+
toolNames,
|
|
191
|
+
}));
|
|
192
|
+
if (shouldEmitRawLog('llm.request.raw')) {
|
|
193
|
+
loggerRequestRaw.debug('LLM request payload', mergeFeaturePathData(correlation, {
|
|
194
|
+
provider: params.agent.provider,
|
|
195
|
+
model: params.agent.model,
|
|
196
|
+
payload: sanitizeRawPayloadForLog({
|
|
197
|
+
messages: params.preparedMessages,
|
|
198
|
+
tools: params.mcpTools,
|
|
199
|
+
}),
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function emitLLMResponseDiagnostics(params) {
|
|
204
|
+
const correlation = buildFeaturePathCorrelation({
|
|
205
|
+
worldId: params.world.id,
|
|
206
|
+
chatId: params.chatId,
|
|
207
|
+
agentId: params.agent.id,
|
|
208
|
+
messageId: params.messageId,
|
|
209
|
+
turnId: params.messageId,
|
|
210
|
+
});
|
|
211
|
+
loggerResponseMeta.debug('LLM response received', mergeFeaturePathData(correlation, {
|
|
212
|
+
provider: params.agent.provider,
|
|
213
|
+
model: params.agent.model,
|
|
214
|
+
responseType: params.response.type,
|
|
215
|
+
contentLength: params.response.content?.length || 0,
|
|
216
|
+
toolCallCount: params.response.tool_calls?.length || 0,
|
|
217
|
+
}));
|
|
218
|
+
if (shouldEmitRawLog('llm.response.raw')) {
|
|
219
|
+
loggerResponseRaw.debug('LLM response payload', mergeFeaturePathData(correlation, {
|
|
220
|
+
provider: params.agent.provider,
|
|
221
|
+
model: params.agent.model,
|
|
222
|
+
payload: sanitizeRawPayloadForLog(params.response),
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
145
226
|
/**
|
|
146
227
|
* Append tool usage guidance to system message when tools are available
|
|
147
228
|
* Returns a new array with updated system message (doesn't mutate original)
|
|
148
229
|
*/
|
|
149
|
-
function appendToolRulesToSystemMessage(messages,
|
|
150
|
-
if (
|
|
230
|
+
export function appendToolRulesToSystemMessage(messages, toolNames, options) {
|
|
231
|
+
if (messages.length === 0 || messages[0].role !== 'system') {
|
|
151
232
|
return messages;
|
|
152
233
|
}
|
|
153
234
|
const systemMessage = messages[0];
|
|
154
|
-
|
|
155
|
-
const
|
|
235
|
+
const normalizedToolNames = new Set(toolNames.map((toolName) => String(toolName || '').trim().toLowerCase()).filter(Boolean));
|
|
236
|
+
const toolUsageSection = buildToolUsagePromptSection({ toolNames });
|
|
237
|
+
const workingDirectory = typeof options?.workingDirectory === 'string' ? options.workingDirectory.trim() : '';
|
|
238
|
+
const shellExecutionRule = normalizedToolNames.has('shell_cmd') && workingDirectory
|
|
239
|
+
? 'When using `shell_cmd`, execute commands only within this trusted working directory scope: ' + workingDirectory
|
|
240
|
+
: '';
|
|
241
|
+
const injectedSections = [shellExecutionRule, toolUsageSection].filter(Boolean);
|
|
242
|
+
if (injectedSections.length === 0) {
|
|
243
|
+
return messages;
|
|
244
|
+
}
|
|
245
|
+
const toolRules = `\n\n${injectedSections.join('\n\n')}`;
|
|
156
246
|
return [
|
|
157
247
|
{ ...systemMessage, content: systemMessage.content + toolRules },
|
|
158
248
|
...messages.slice(1)
|
|
@@ -171,13 +261,25 @@ function normalizeChatId(chatId) {
|
|
|
171
261
|
return '__none__';
|
|
172
262
|
return String(chatId);
|
|
173
263
|
}
|
|
264
|
+
function createLLMQueueTimeoutError(agentId, timeoutMs) {
|
|
265
|
+
const error = new Error(`LLM call timeout after ${timeoutMs}ms for agent ${agentId}`);
|
|
266
|
+
error.name = 'LLMQueueTimeoutError';
|
|
267
|
+
error.code = 'LLM_QUEUE_TIMEOUT';
|
|
268
|
+
return error;
|
|
269
|
+
}
|
|
270
|
+
function isLLMQueueTimeoutError(error) {
|
|
271
|
+
return Boolean(error &&
|
|
272
|
+
typeof error === 'object' &&
|
|
273
|
+
'code' in error &&
|
|
274
|
+
error.code === 'LLM_QUEUE_TIMEOUT');
|
|
275
|
+
}
|
|
174
276
|
class LLMQueue {
|
|
175
277
|
queue = [];
|
|
176
278
|
processing = false;
|
|
177
279
|
activeItem = null;
|
|
178
280
|
maxQueueSize = 100; // Prevent memory issues
|
|
179
|
-
processingTimeoutMs =
|
|
180
|
-
async add(agentId, worldId, chatId, task) {
|
|
281
|
+
processingTimeoutMs = RELIABILITY_CONFIG.llm.processingTimeoutMs; // 15 minute max processing time per call (for long-running tools)
|
|
282
|
+
async add(agentId, worldId, chatId, task, options) {
|
|
181
283
|
// Prevent queue overflow
|
|
182
284
|
if (this.queue.length >= this.maxQueueSize) {
|
|
183
285
|
throw new Error(`LLM queue is full (${this.maxQueueSize} items). Please try again later.`);
|
|
@@ -192,6 +294,8 @@ class LLMQueue {
|
|
|
192
294
|
abortController: new AbortController(),
|
|
193
295
|
canceled: false,
|
|
194
296
|
execute: task,
|
|
297
|
+
onTakingTooLong: options?.onTakingTooLong,
|
|
298
|
+
onTimedOut: options?.onTimedOut,
|
|
195
299
|
resolve,
|
|
196
300
|
reject
|
|
197
301
|
};
|
|
@@ -214,13 +318,13 @@ class LLMQueue {
|
|
|
214
318
|
this.activeItem = item;
|
|
215
319
|
const taskStartTime = Date.now();
|
|
216
320
|
loggerQueue.debug(`LLMQueue: Processing task for agent=${item.agentId}, world=${item.worldId}, chat=${normalizeChatId(item.chatId)}, queueItemId=${item.id}`);
|
|
217
|
-
// Add processing timeout to prevent stuck queue
|
|
321
|
+
// Add processing timeout to prevent stuck queue.
|
|
218
322
|
const processPromise = item.execute(item.abortController.signal);
|
|
219
|
-
// Store timeout
|
|
323
|
+
// Store timeout IDs so we can cancel them on all exits.
|
|
220
324
|
let timeoutId;
|
|
221
325
|
let warningTimeoutId;
|
|
222
|
-
// Warn if processing
|
|
223
|
-
const warningThreshold = this.processingTimeoutMs *
|
|
326
|
+
// Warn if processing exceeds configured threshold ratio of timeout.
|
|
327
|
+
const warningThreshold = this.processingTimeoutMs * RELIABILITY_CONFIG.llm.warningThresholdRatio;
|
|
224
328
|
warningTimeoutId = setTimeout(() => {
|
|
225
329
|
const elapsed = Date.now() - taskStartTime;
|
|
226
330
|
loggerQueue.warn(`LLM task is taking longer than expected`, {
|
|
@@ -230,22 +334,74 @@ class LLMQueue {
|
|
|
230
334
|
timeoutMs: this.processingTimeoutMs,
|
|
231
335
|
percentComplete: Math.round((elapsed / this.processingTimeoutMs) * 100)
|
|
232
336
|
});
|
|
337
|
+
try {
|
|
338
|
+
item.onTakingTooLong?.({
|
|
339
|
+
elapsedMs: elapsed,
|
|
340
|
+
timeoutMs: this.processingTimeoutMs,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
catch (callbackError) {
|
|
344
|
+
loggerQueue.warn('LLM queue taking-too-long callback failed', {
|
|
345
|
+
agentId: item.agentId,
|
|
346
|
+
worldId: item.worldId,
|
|
347
|
+
chatId: normalizeChatId(item.chatId),
|
|
348
|
+
queueItemId: item.id,
|
|
349
|
+
error: callbackError instanceof Error ? callbackError.message : String(callbackError)
|
|
350
|
+
});
|
|
351
|
+
}
|
|
233
352
|
}, warningThreshold);
|
|
234
353
|
const timeoutPromise = new Promise((_, reject) => {
|
|
235
354
|
timeoutId = setTimeout(() => {
|
|
236
|
-
|
|
355
|
+
const elapsed = Date.now() - taskStartTime;
|
|
356
|
+
if (!item.abortController.signal.aborted) {
|
|
357
|
+
item.abortController.abort();
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
item.onTimedOut?.({
|
|
361
|
+
elapsedMs: elapsed,
|
|
362
|
+
timeoutMs: this.processingTimeoutMs,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
catch (callbackError) {
|
|
366
|
+
loggerQueue.warn('LLM queue timeout callback failed', {
|
|
367
|
+
agentId: item.agentId,
|
|
368
|
+
worldId: item.worldId,
|
|
369
|
+
chatId: normalizeChatId(item.chatId),
|
|
370
|
+
queueItemId: item.id,
|
|
371
|
+
error: callbackError instanceof Error ? callbackError.message : String(callbackError)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
reject(createLLMQueueTimeoutError(item.agentId, this.processingTimeoutMs));
|
|
237
375
|
}, this.processingTimeoutMs);
|
|
238
376
|
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
377
|
+
let result;
|
|
378
|
+
try {
|
|
379
|
+
result = await Promise.race([processPromise, timeoutPromise]);
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
if (timeoutId) {
|
|
383
|
+
clearTimeout(timeoutId);
|
|
384
|
+
}
|
|
385
|
+
if (warningTimeoutId) {
|
|
386
|
+
clearTimeout(warningTimeoutId);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
243
389
|
item.resolve(result);
|
|
244
390
|
loggerQueue.debug(`LLMQueue: Finished processing task for agent=${item.agentId}, world=${item.worldId}, queueItemId=${item.id}`);
|
|
245
391
|
}
|
|
246
392
|
catch (error) {
|
|
247
|
-
const
|
|
248
|
-
|
|
393
|
+
const isTimeout = isLLMQueueTimeoutError(error);
|
|
394
|
+
const wasCanceled = !isTimeout && (item.canceled || item.abortController.signal.aborted || isAbortError(error));
|
|
395
|
+
if (isTimeout) {
|
|
396
|
+
loggerQueue.warn('LLM queue call timed out', {
|
|
397
|
+
agentId: item.agentId,
|
|
398
|
+
worldId: item.worldId,
|
|
399
|
+
chatId: normalizeChatId(item.chatId),
|
|
400
|
+
queueItemId: item.id,
|
|
401
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
else if (wasCanceled) {
|
|
249
405
|
loggerQueue.info('LLM queue call canceled', {
|
|
250
406
|
agentId: item.agentId,
|
|
251
407
|
worldId: item.worldId,
|
|
@@ -322,8 +478,8 @@ class LLMQueue {
|
|
|
322
478
|
}
|
|
323
479
|
// Set processing timeout (useful for testing or adjusting for long-running operations)
|
|
324
480
|
setProcessingTimeout(timeoutMs) {
|
|
325
|
-
if (timeoutMs <
|
|
326
|
-
throw new Error(
|
|
481
|
+
if (timeoutMs < RELIABILITY_CONFIG.llm.minProcessingTimeoutMs) {
|
|
482
|
+
throw new Error(`Processing timeout must be at least ${RELIABILITY_CONFIG.llm.minProcessingTimeoutMs}ms`);
|
|
327
483
|
}
|
|
328
484
|
this.processingTimeoutMs = timeoutMs;
|
|
329
485
|
loggerQueue.info('LLM queue processing timeout updated', { timeoutMs });
|
|
@@ -345,6 +501,18 @@ function isAbortError(error) {
|
|
|
345
501
|
const message = error instanceof Error ? error.message : String(error);
|
|
346
502
|
return message.toLowerCase().includes('abort');
|
|
347
503
|
}
|
|
504
|
+
function emitLLMTimeoutSystemStatus(world, chatId, content) {
|
|
505
|
+
const scopedChatId = typeof chatId === 'string' ? chatId.trim() : '';
|
|
506
|
+
if (!scopedChatId) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
world.eventEmitter.emit('system', {
|
|
510
|
+
content,
|
|
511
|
+
timestamp: new Date(),
|
|
512
|
+
messageId: generateId(),
|
|
513
|
+
chatId: scopedChatId,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
348
516
|
function createCombinedAbortSignal(first, second) {
|
|
349
517
|
const signals = [first, second].filter((value) => Boolean(value));
|
|
350
518
|
if (signals.length === 0) {
|
|
@@ -380,24 +548,36 @@ export async function streamAgentResponse(world, agent, messages, publishSSE, ch
|
|
|
380
548
|
if (abortSignal?.aborted) {
|
|
381
549
|
throw new DOMException(`LLM call aborted before queue for agent ${agent.id}`, 'AbortError');
|
|
382
550
|
}
|
|
551
|
+
const normalizedChatId = typeof chatId === 'string' ? chatId.trim() : '';
|
|
552
|
+
const resolvedChatId = normalizedChatId || null;
|
|
553
|
+
if (!resolvedChatId) {
|
|
554
|
+
throw new Error(`streamAgentResponse: chatId is required for agent ${agent.id}`);
|
|
555
|
+
}
|
|
383
556
|
// Queue the LLM call to ensure serialized execution
|
|
384
|
-
return llmQueue.add(agent.id, world.id,
|
|
557
|
+
return llmQueue.add(agent.id, world.id, resolvedChatId, async (queueAbortSignal) => {
|
|
385
558
|
const { signal: mergedAbortSignal, dispose } = createCombinedAbortSignal(queueAbortSignal, abortSignal);
|
|
386
559
|
try {
|
|
387
560
|
if (mergedAbortSignal?.aborted) {
|
|
388
561
|
throw new DOMException(`LLM call aborted before execution for agent ${agent.id}`, 'AbortError');
|
|
389
562
|
}
|
|
390
|
-
return await executeStreamAgentResponse(world, agent, messages, publishSSE, mergedAbortSignal);
|
|
563
|
+
return await executeStreamAgentResponse(world, agent, messages, publishSSE, resolvedChatId, mergedAbortSignal);
|
|
391
564
|
}
|
|
392
565
|
finally {
|
|
393
566
|
dispose();
|
|
394
567
|
}
|
|
568
|
+
}, {
|
|
569
|
+
onTakingTooLong: ({ elapsedMs, timeoutMs }) => {
|
|
570
|
+
emitLLMTimeoutSystemStatus(world, resolvedChatId, `LLM processing taking too long for ${agent.id} (elapsed ${Math.floor(elapsedMs / 1000)}s, timeout ${Math.floor(timeoutMs / 1000)}s).`);
|
|
571
|
+
},
|
|
572
|
+
onTimedOut: ({ timeoutMs }) => {
|
|
573
|
+
emitLLMTimeoutSystemStatus(world, resolvedChatId, `LLM processing timed out for ${agent.id} after ${Math.floor(timeoutMs / 1000)}s.`);
|
|
574
|
+
},
|
|
395
575
|
});
|
|
396
576
|
}
|
|
397
577
|
/**
|
|
398
578
|
* Internal streaming implementation (executed within queue)
|
|
399
579
|
*/
|
|
400
|
-
async function executeStreamAgentResponse(world, agent, messages, publishSSE, abortSignal) {
|
|
580
|
+
async function executeStreamAgentResponse(world, agent, messages, publishSSE, chatId, abortSignal) {
|
|
401
581
|
const messageId = generateId();
|
|
402
582
|
try {
|
|
403
583
|
if (abortSignal?.aborted) {
|
|
@@ -407,7 +587,8 @@ async function executeStreamAgentResponse(world, agent, messages, publishSSE, ab
|
|
|
407
587
|
publishSSE(world, {
|
|
408
588
|
agentName: agent.id,
|
|
409
589
|
type: 'start',
|
|
410
|
-
messageId
|
|
590
|
+
messageId,
|
|
591
|
+
chatId
|
|
411
592
|
});
|
|
412
593
|
loggerStreaming.debug(`LLM: Starting streaming response for agent=${agent.id}, world=${world.id}, messageId=${messageId}`);
|
|
413
594
|
// Convert messages for LLM (strip custom fields)
|
|
@@ -415,9 +596,11 @@ async function executeStreamAgentResponse(world, agent, messages, publishSSE, ab
|
|
|
415
596
|
let preparedMessages = stripCustomFieldsFromMessages(messages);
|
|
416
597
|
// Get MCP tools for this world
|
|
417
598
|
const mcpTools = await getMCPToolsForWorld(world.id);
|
|
418
|
-
const
|
|
599
|
+
const mcpToolNames = Object.keys(mcpTools);
|
|
600
|
+
const hasMCPTools = mcpToolNames.length > 0;
|
|
601
|
+
const workingDirectory = getEnvValueFromText(world.variables, 'working_directory') || getDefaultWorkingDirectory();
|
|
419
602
|
// Add tool usage instructions to system message when tools are available
|
|
420
|
-
preparedMessages = appendToolRulesToSystemMessage(preparedMessages,
|
|
603
|
+
preparedMessages = appendToolRulesToSystemMessage(preparedMessages, mcpToolNames, { workingDirectory });
|
|
421
604
|
if (hasMCPTools) {
|
|
422
605
|
loggerMCP.debug(`LLM: Including ${Object.keys(mcpTools).length} MCP tools for agent=${agent.id}, world=${world.id}`);
|
|
423
606
|
// Debug: Log complete tool definitions being sent to LLM
|
|
@@ -430,28 +613,71 @@ async function executeStreamAgentResponse(world, agent, messages, publishSSE, ab
|
|
|
430
613
|
});
|
|
431
614
|
}
|
|
432
615
|
}
|
|
616
|
+
emitLLMRequestDiagnostics({
|
|
617
|
+
world,
|
|
618
|
+
agent,
|
|
619
|
+
chatId,
|
|
620
|
+
messageId,
|
|
621
|
+
preparedMessages,
|
|
622
|
+
mcpTools,
|
|
623
|
+
});
|
|
433
624
|
// Use direct OpenAI integration for OpenAI providers
|
|
434
625
|
if (isOpenAIProvider(agent.provider)) {
|
|
435
626
|
const client = createOpenAIClientForAgent(agent);
|
|
436
|
-
const response = await streamOpenAIResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (
|
|
627
|
+
const response = await streamOpenAIResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (chunk) => publishSSE(world, {
|
|
628
|
+
agentName: agent.id,
|
|
629
|
+
type: 'chunk',
|
|
630
|
+
content: chunk.content,
|
|
631
|
+
reasoningContent: chunk.reasoningContent,
|
|
632
|
+
messageId,
|
|
633
|
+
chatId,
|
|
634
|
+
}), messageId, abortSignal);
|
|
635
|
+
emitLLMResponseDiagnostics({
|
|
636
|
+
world,
|
|
637
|
+
agent,
|
|
638
|
+
chatId,
|
|
639
|
+
messageId,
|
|
640
|
+
response,
|
|
641
|
+
});
|
|
437
642
|
// Emit end event after streaming completes
|
|
438
|
-
publishSSE(world, { agentName: agent.id, type: 'end', messageId });
|
|
643
|
+
publishSSE(world, { agentName: agent.id, type: 'end', messageId, chatId });
|
|
439
644
|
return { response, messageId };
|
|
440
645
|
}
|
|
441
646
|
// Use direct Anthropic integration for Anthropic provider
|
|
442
647
|
if (isAnthropicProvider(agent.provider)) {
|
|
443
648
|
const client = createAnthropicClientForAgent(agent);
|
|
444
|
-
const response = await streamAnthropicResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (content) => publishSSE(world, { agentName: agent.id, type: 'chunk', content, messageId }), messageId, abortSignal);
|
|
649
|
+
const response = await streamAnthropicResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (content) => publishSSE(world, { agentName: agent.id, type: 'chunk', content, messageId, chatId }), messageId, abortSignal);
|
|
650
|
+
emitLLMResponseDiagnostics({
|
|
651
|
+
world,
|
|
652
|
+
agent,
|
|
653
|
+
chatId,
|
|
654
|
+
messageId,
|
|
655
|
+
response,
|
|
656
|
+
});
|
|
445
657
|
// Emit end event after streaming completes
|
|
446
|
-
publishSSE(world, { agentName: agent.id, type: 'end', messageId });
|
|
658
|
+
publishSSE(world, { agentName: agent.id, type: 'end', messageId, chatId });
|
|
447
659
|
return { response, messageId };
|
|
448
660
|
}
|
|
449
661
|
// Use direct Google integration for Google provider
|
|
450
662
|
if (isGoogleProvider(agent.provider)) {
|
|
451
663
|
const client = createGoogleClientForAgent(agent);
|
|
452
|
-
const response = await streamGoogleResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (
|
|
664
|
+
const response = await streamGoogleResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (chunk) => publishSSE(world, {
|
|
665
|
+
agentName: agent.id,
|
|
666
|
+
type: 'chunk',
|
|
667
|
+
content: chunk.content,
|
|
668
|
+
reasoningContent: chunk.reasoningContent,
|
|
669
|
+
messageId,
|
|
670
|
+
chatId,
|
|
671
|
+
}), messageId, abortSignal);
|
|
672
|
+
emitLLMResponseDiagnostics({
|
|
673
|
+
world,
|
|
674
|
+
agent,
|
|
675
|
+
chatId,
|
|
676
|
+
messageId,
|
|
677
|
+
response,
|
|
678
|
+
});
|
|
453
679
|
// Emit end event after streaming completes
|
|
454
|
-
publishSSE(world, { agentName: agent.id, type: 'end', messageId });
|
|
680
|
+
publishSSE(world, { agentName: agent.id, type: 'end', messageId, chatId });
|
|
455
681
|
return { response, messageId };
|
|
456
682
|
}
|
|
457
683
|
// All providers now use direct integrations - no AI SDK needed
|
|
@@ -462,7 +688,8 @@ async function executeStreamAgentResponse(world, agent, messages, publishSSE, ab
|
|
|
462
688
|
publishSSE(world, {
|
|
463
689
|
agentName: agent.id,
|
|
464
690
|
type: 'end',
|
|
465
|
-
messageId
|
|
691
|
+
messageId,
|
|
692
|
+
chatId
|
|
466
693
|
});
|
|
467
694
|
loggerStreaming.info(`LLM: Streaming response canceled for agent=${agent.id}, world=${world.id}, messageId=${messageId}`);
|
|
468
695
|
throw new Error(`LLM call canceled for agent ${agent.id}`);
|
|
@@ -472,9 +699,16 @@ async function executeStreamAgentResponse(world, agent, messages, publishSSE, ab
|
|
|
472
699
|
agentName: agent.id,
|
|
473
700
|
type: 'error',
|
|
474
701
|
error: error.message,
|
|
475
|
-
messageId
|
|
702
|
+
messageId,
|
|
703
|
+
chatId
|
|
704
|
+
});
|
|
705
|
+
loggerStreaming.error('LLM: Error during streaming response', {
|
|
706
|
+
agentId: agent.id,
|
|
707
|
+
worldId: world.id,
|
|
708
|
+
chatId,
|
|
709
|
+
messageId,
|
|
710
|
+
error: error instanceof Error ? error.message : String(error)
|
|
476
711
|
});
|
|
477
|
-
loggerStreaming.error(`LLM: Error during streaming response for agent=${agent.id}, world=${world.id}, messageId=${messageId}, error=${error.message}`);
|
|
478
712
|
throw error;
|
|
479
713
|
}
|
|
480
714
|
}
|
|
@@ -485,24 +719,36 @@ export async function generateAgentResponse(world, agent, messages, _publishSSE,
|
|
|
485
719
|
if (abortSignal?.aborted) {
|
|
486
720
|
throw new DOMException(`LLM call aborted before queue for agent ${agent.id}`, 'AbortError');
|
|
487
721
|
}
|
|
722
|
+
const normalizedChatId = typeof chatId === 'string' ? chatId.trim() : '';
|
|
723
|
+
const resolvedChatId = normalizedChatId || null;
|
|
724
|
+
if (!resolvedChatId) {
|
|
725
|
+
throw new Error(`generateAgentResponse: chatId is required for agent ${agent.id}`);
|
|
726
|
+
}
|
|
488
727
|
// Queue the LLM call to ensure serialized execution
|
|
489
|
-
return llmQueue.add(agent.id, world.id,
|
|
728
|
+
return llmQueue.add(agent.id, world.id, resolvedChatId, async (queueAbortSignal) => {
|
|
490
729
|
const { signal: mergedAbortSignal, dispose } = createCombinedAbortSignal(queueAbortSignal, abortSignal);
|
|
491
730
|
try {
|
|
492
731
|
if (mergedAbortSignal?.aborted) {
|
|
493
732
|
throw new DOMException(`LLM call aborted before execution for agent ${agent.id}`, 'AbortError');
|
|
494
733
|
}
|
|
495
|
-
return await executeGenerateAgentResponse(world, agent, messages, skipTools, mergedAbortSignal);
|
|
734
|
+
return await executeGenerateAgentResponse(world, agent, messages, skipTools, resolvedChatId, mergedAbortSignal);
|
|
496
735
|
}
|
|
497
736
|
finally {
|
|
498
737
|
dispose();
|
|
499
738
|
}
|
|
739
|
+
}, {
|
|
740
|
+
onTakingTooLong: ({ elapsedMs, timeoutMs }) => {
|
|
741
|
+
emitLLMTimeoutSystemStatus(world, resolvedChatId, `LLM processing taking too long for ${agent.id} (elapsed ${Math.floor(elapsedMs / 1000)}s, timeout ${Math.floor(timeoutMs / 1000)}s).`);
|
|
742
|
+
},
|
|
743
|
+
onTimedOut: ({ timeoutMs }) => {
|
|
744
|
+
emitLLMTimeoutSystemStatus(world, resolvedChatId, `LLM processing timed out for ${agent.id} after ${Math.floor(timeoutMs / 1000)}s.`);
|
|
745
|
+
},
|
|
500
746
|
});
|
|
501
747
|
}
|
|
502
748
|
/**
|
|
503
749
|
* Internal generation implementation (executed within queue)
|
|
504
750
|
*/
|
|
505
|
-
async function executeGenerateAgentResponse(world, agent, messages, skipTools, abortSignal) {
|
|
751
|
+
async function executeGenerateAgentResponse(world, agent, messages, skipTools, chatId = null, abortSignal) {
|
|
506
752
|
if (abortSignal?.aborted) {
|
|
507
753
|
throw new DOMException('LLM call aborted before start', 'AbortError');
|
|
508
754
|
}
|
|
@@ -512,9 +758,19 @@ async function executeGenerateAgentResponse(world, agent, messages, skipTools, a
|
|
|
512
758
|
let preparedMessages = stripCustomFieldsFromMessages(messages);
|
|
513
759
|
// Get MCP tools for this world (skip if requested, e.g., for title generation)
|
|
514
760
|
const mcpTools = skipTools ? {} : await getMCPToolsForWorld(world.id);
|
|
515
|
-
const
|
|
761
|
+
const mcpToolNames = Object.keys(mcpTools);
|
|
762
|
+
const hasMCPTools = mcpToolNames.length > 0;
|
|
763
|
+
const workingDirectory = getEnvValueFromText(world.variables, 'working_directory') || getDefaultWorkingDirectory();
|
|
516
764
|
// Add tool usage instructions to system message when tools are available
|
|
517
|
-
preparedMessages = appendToolRulesToSystemMessage(preparedMessages,
|
|
765
|
+
preparedMessages = appendToolRulesToSystemMessage(preparedMessages, mcpToolNames, { workingDirectory });
|
|
766
|
+
emitLLMRequestDiagnostics({
|
|
767
|
+
world,
|
|
768
|
+
agent,
|
|
769
|
+
chatId,
|
|
770
|
+
messageId,
|
|
771
|
+
preparedMessages,
|
|
772
|
+
mcpTools,
|
|
773
|
+
});
|
|
518
774
|
if (hasMCPTools) {
|
|
519
775
|
loggerMCP.debug(`LLM: Including ${Object.keys(mcpTools).length} MCP tools for agent=${agent.id}, world=${world.id}`);
|
|
520
776
|
// Debug: Log complete tool definitions being sent to LLM
|
|
@@ -548,6 +804,13 @@ async function executeGenerateAgentResponse(world, agent, messages, skipTools, a
|
|
|
548
804
|
agent.lastActive = new Date();
|
|
549
805
|
agent.llmCallCount++;
|
|
550
806
|
agent.lastLLMCall = new Date();
|
|
807
|
+
emitLLMResponseDiagnostics({
|
|
808
|
+
world,
|
|
809
|
+
agent,
|
|
810
|
+
chatId,
|
|
811
|
+
messageId,
|
|
812
|
+
response,
|
|
813
|
+
});
|
|
551
814
|
loggerGeneration.debug(`LLM: Finished non-streaming OpenAI response for agent=${agent.id}, world=${world.id}`, {
|
|
552
815
|
responseType: response.type,
|
|
553
816
|
contentLength: response.content?.length || 0,
|
|
@@ -565,6 +828,13 @@ async function executeGenerateAgentResponse(world, agent, messages, skipTools, a
|
|
|
565
828
|
agent.lastActive = new Date();
|
|
566
829
|
agent.llmCallCount++;
|
|
567
830
|
agent.lastLLMCall = new Date();
|
|
831
|
+
emitLLMResponseDiagnostics({
|
|
832
|
+
world,
|
|
833
|
+
agent,
|
|
834
|
+
chatId,
|
|
835
|
+
messageId,
|
|
836
|
+
response,
|
|
837
|
+
});
|
|
568
838
|
loggerGeneration.debug(`LLM: Finished non-streaming Anthropic response for agent=${agent.id}, world=${world.id}`, {
|
|
569
839
|
responseType: response.type,
|
|
570
840
|
contentLength: response.content?.length || 0,
|
|
@@ -582,6 +852,13 @@ async function executeGenerateAgentResponse(world, agent, messages, skipTools, a
|
|
|
582
852
|
agent.lastActive = new Date();
|
|
583
853
|
agent.llmCallCount++;
|
|
584
854
|
agent.lastLLMCall = new Date();
|
|
855
|
+
emitLLMResponseDiagnostics({
|
|
856
|
+
world,
|
|
857
|
+
agent,
|
|
858
|
+
chatId,
|
|
859
|
+
messageId,
|
|
860
|
+
response,
|
|
861
|
+
});
|
|
585
862
|
loggerGeneration.debug(`LLM: Finished non-streaming Google response for agent=${agent.id}, world=${world.id}`, {
|
|
586
863
|
responseType: response.type,
|
|
587
864
|
contentLength: response.content?.length || 0,
|
|
@@ -599,7 +876,13 @@ async function executeGenerateAgentResponse(world, agent, messages, skipTools, a
|
|
|
599
876
|
loggerGeneration.info(`LLM: Non-streaming response canceled for agent=${agent.id}, world=${world.id}, messageId=${messageId}`);
|
|
600
877
|
throw new Error(`LLM call canceled for agent ${agent.id}`);
|
|
601
878
|
}
|
|
602
|
-
loggerGeneration.error(
|
|
879
|
+
loggerGeneration.error('LLM: Error during non-streaming response', {
|
|
880
|
+
agentId: agent.id,
|
|
881
|
+
worldId: world.id,
|
|
882
|
+
chatId,
|
|
883
|
+
messageId,
|
|
884
|
+
error: error instanceof Error ? error.message : String(error)
|
|
885
|
+
});
|
|
603
886
|
throw error;
|
|
604
887
|
}
|
|
605
888
|
}
|
|
@@ -654,8 +937,17 @@ function createOpenAIClientForAgent(agent) {
|
|
|
654
937
|
switch (agent.provider) {
|
|
655
938
|
case LLMProvider.OPENAI:
|
|
656
939
|
return createClientForProvider('openai', config);
|
|
657
|
-
case LLMProvider.AZURE:
|
|
658
|
-
|
|
940
|
+
case LLMProvider.AZURE: {
|
|
941
|
+
const configuredDeployment = typeof config.deployment === 'string'
|
|
942
|
+
? config.deployment.trim()
|
|
943
|
+
: '';
|
|
944
|
+
const modelDeployment = typeof agent.model === 'string' ? agent.model.trim() : '';
|
|
945
|
+
// For Azure, deployment is selected in the URL path. Prefer runtime model when provided.
|
|
946
|
+
return createClientForProvider('azure', {
|
|
947
|
+
...config,
|
|
948
|
+
deployment: modelDeployment || configuredDeployment,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
659
951
|
case LLMProvider.OPENAI_COMPATIBLE:
|
|
660
952
|
return createClientForProvider('openai-compatible', config);
|
|
661
953
|
case LLMProvider.XAI:
|