agent-world 0.13.0 → 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 +90 -17
- package/dist/cli/commands.d.ts +7 -1
- package/dist/cli/commands.js +27 -10
- package/dist/cli/hitl.d.ts +4 -1
- package/dist/cli/hitl.js +55 -20
- package/dist/cli/index.js +249 -97
- package/dist/cli/system-events.d.ts +27 -0
- package/dist/cli/system-events.js +63 -0
- package/dist/core/activity-tracker.d.ts +26 -0
- package/dist/core/activity-tracker.d.ts.map +1 -1
- package/dist/core/activity-tracker.js +21 -4
- 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 +5 -0
- package/dist/core/create-agent-tool.d.ts.map +1 -1
- package/dist/core/create-agent-tool.js +57 -34
- 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 +203 -36
- 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 -35
- package/dist/core/events/persistence.js.map +1 -1
- package/dist/core/events/publishers.d.ts +13 -7
- package/dist/core/events/publishers.d.ts.map +1 -1
- package/dist/core/events/publishers.js +53 -37
- 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 +61 -148
- 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 +6 -12
- package/dist/core/hitl-tool.d.ts.map +1 -1
- package/dist/core/hitl-tool.js +66 -88
- package/dist/core/hitl-tool.js.map +1 -1
- package/dist/core/hitl.d.ts +61 -4
- package/dist/core/hitl.d.ts.map +1 -1
- package/dist/core/hitl.js +324 -60
- package/dist/core/hitl.js.map +1 -1
- package/dist/core/index.d.ts +11 -7
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +10 -6
- package/dist/core/index.js.map +1 -1
- package/dist/core/llm-manager.d.ts +15 -0
- package/dist/core/llm-manager.d.ts.map +1 -1
- package/dist/core/llm-manager.js +325 -40
- 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 +18 -50
- package/dist/core/managers.d.ts.map +1 -1
- package/dist/core/managers.js +340 -502
- package/dist/core/managers.js.map +1 -1
- package/dist/core/mcp-server-registry.d.ts +16 -1
- package/dist/core/mcp-server-registry.d.ts.map +1 -1
- package/dist/core/mcp-server-registry.js +162 -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 +7 -2
- package/dist/core/tool-utils.d.ts.map +1 -1
- package/dist/core/tool-utils.js +81 -17
- package/dist/core/tool-utils.js.map +1 -1
- package/dist/core/types.d.ts +67 -19
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/utils.d.ts +7 -0
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +71 -21
- 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 +260 -18
- 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 +11 -1
- package/dist/server/sse-handler.js +194 -34
- 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-BW41BxMy.css +0 -1
- package/dist/public/assets/index-kO6UJFwK.js +0 -96
|
@@ -20,6 +20,27 @@
|
|
|
20
20
|
* - storage (runtime)
|
|
21
21
|
*
|
|
22
22
|
* Changes:
|
|
23
|
+
* - 2026-03-13: Phase 1 — weak fallback no-commit: `pickFallbackTitle` returns '' instead of 'Chat Session' so low-signal LLM results keep the chat in 'New Chat' state.
|
|
24
|
+
* - 2026-03-13: Phase 2 — bounded context window: `buildTitlePromptMessages` collects up to TITLE_CONTEXT_WINDOW_TURNS*2 recent user+assistant messages for richer prompt context.
|
|
25
|
+
* - 2026-03-13: Improved title-gen prompt: explicit @mention semantics, no-verbatim-copy rule, noun-phrase Title Case format constraint.
|
|
26
|
+
* - 2026-03-13: Title-generation LLM calls now strip world `reasoning_effort` so background title requests omit provider reasoning params by default.
|
|
27
|
+
* - 2026-03-06: Required explicit chat scope in memory-save/continuation/assistant-response paths; removed `world.currentChatId` fallback from agent event routing.
|
|
28
|
+
* - 2026-03-06: Normalized shell continuation parse/validation/policy failures through explicit canonical shell failure reasons and updated continuation comments to reflect bounded-preview tool persistence.
|
|
29
|
+
* - 2026-03-06: Collapsed shell continuation result-mode selection to one bounded-preview mode and normalized persisted shell tool failures through the canonical shell-result formatter.
|
|
30
|
+
* - 2026-02-28: Added canonical `message.publish` logs for assistant publish events in direct and continuation response paths.
|
|
31
|
+
* - 2026-02-27: Passed explicit chat scope to continuation system events (`publishEvent`) to prevent fallback routing to `world.currentChatId` during chat switches.
|
|
32
|
+
* - 2026-02-27: Suppress repeated identical `load_skill` tool calls within the same continuation run once a prior same-run load succeeded.
|
|
33
|
+
* - 2026-02-27: Added per-chat/agent continuation run lock in `continueLLMAfterToolExecution` to skip concurrent duplicate continuation runs while tools are pending/executing (prevents duplicate HITL approval prompts).
|
|
34
|
+
* - 2026-03-01: Expanded script-like shell command detection to treat path-based interpreter executables (for example `.venv/bin/python`) as script hosts so continuation prefers smart shell result mode after skill-driven script calls.
|
|
35
|
+
* - 2026-03-01: Generalized script-host detection for smart shell continuation mode to include additional interpreter families (`bash`, `node`, `deno`, `bun`, `ruby`, `perl`, `php`, `pwsh`) and `env <interpreter> script` invocation patterns.
|
|
36
|
+
* - 2026-03-01: Updated duplicate `shell_cmd` matching to ignore `output_format`/`output_detail` differences and redact those fields from continuation tool telemetry payloads.
|
|
37
|
+
* - 2026-02-26: Replaced `resumePendingToolCallsForChat` console traces with categorized structured logger events (`chat.restore.resume.tools`) for env-controlled restore diagnostics.
|
|
38
|
+
* - 2026-02-24: Commented out hardcoded Infinite-Etude handoff safeguard to respect separation of concerns (logic moved to agent prompt).
|
|
39
|
+
* - 2026-02-25: Added detailed resume tracing in `resumePendingToolCallsForChat` (start/skip/execute/error/continue) for cross-layer restore diagnostics.
|
|
40
|
+
* - 2026-02-25: Added duplicate messageId guard in `saveIncomingMessageToMemory` so chat-restore replay can re-emit pending user messages without duplicating persisted agent memory.
|
|
41
|
+
* - 2026-02-25: Added `resumePendingToolCallsForChat` to restore unresolved persisted tool calls (e.g. `load_skill`) on chat load/switch and continue the LLM loop.
|
|
42
|
+
* - 2026-02-21: Shell tool continuation context now requests minimal LLM result mode (`status`/`exit_code`) and passes agent name for assistant-stream shell SSE attribution.
|
|
43
|
+
* - 2026-02-20: Added Infinite-Etude handoff safeguard to enforce Pedagogue -> Engraver final mention when missing.
|
|
23
44
|
* - 2026-02-16: Added plain-text tool-intent fallback parser in continuation to synthesize executable `tool_calls` when providers return `Calling tool: ...` text.
|
|
24
45
|
* - 2026-02-16: Max tool-hop guardrail now emits UI/tool errors and injects transient LLM context, then continues loop instead of returning.
|
|
25
46
|
* - 2026-02-16: Removed plain-text tool-intent reminder/retry path; continuation now relies only on tool-call loop + hop guardrail.
|
|
@@ -47,6 +68,8 @@ import { createCategoryLogger } from '../logger.js';
|
|
|
47
68
|
import { beginWorldActivity } from '../activity-tracker.js';
|
|
48
69
|
import { createStorageWithWrappers } from '../storage/storage-factory.js';
|
|
49
70
|
import { generateAgentResponse } from '../llm-manager.js';
|
|
71
|
+
import { formatShellToolErrorEnvelopeContent, } from '../shell-cmd-tool.js';
|
|
72
|
+
import { createTextToolPreview, getToolEventPreviewPayload, parseToolExecutionEnvelopeContent, serializeToolExecutionEnvelope, stringifyToolExecutionResult, } from '../tool-execution-envelope.js';
|
|
50
73
|
import { isMessageProcessingCanceledError, throwIfMessageProcessingStopped } from '../message-processing-control.js';
|
|
51
74
|
import { shouldAutoMention, addAutoMention, hasAnyMentionAtBeginning, removeSelfMentions } from './mention-logic.js';
|
|
52
75
|
import { publishMessageWithId, publishSSE, publishEvent, publishToolEvent, isStreamingEnabled } from './publishers.js';
|
|
@@ -56,8 +79,10 @@ const loggerAgent = createCategoryLogger('agent');
|
|
|
56
79
|
const loggerTurnLimit = createCategoryLogger('turnlimit');
|
|
57
80
|
const loggerChatTitle = createCategoryLogger('chattitle');
|
|
58
81
|
const loggerAutoMention = createCategoryLogger('automention');
|
|
59
|
-
const
|
|
82
|
+
const loggerRestoreResumeTools = createCategoryLogger('chat.restore.resume.tools');
|
|
83
|
+
const loggerMessagePublish = createCategoryLogger('message.publish');
|
|
60
84
|
const TITLE_PROMPT_MAX_CHARS_PER_TURN = 240;
|
|
85
|
+
const TITLE_CONTEXT_WINDOW_TURNS = 3;
|
|
61
86
|
// Storage wrapper instance - initialized lazily
|
|
62
87
|
let storageWrappers = null;
|
|
63
88
|
async function getStorageWrappers() {
|
|
@@ -66,6 +91,103 @@ async function getStorageWrappers() {
|
|
|
66
91
|
}
|
|
67
92
|
return storageWrappers;
|
|
68
93
|
}
|
|
94
|
+
const activeContinuationRuns = new Map();
|
|
95
|
+
const continuationRunLoadedSkills = new Map();
|
|
96
|
+
const continuationRunShellCommandResults = new Map();
|
|
97
|
+
function normalizeContinuationChatId(chatId) {
|
|
98
|
+
if (chatId === undefined || chatId === null) {
|
|
99
|
+
return '__null__';
|
|
100
|
+
}
|
|
101
|
+
const normalized = String(chatId).trim();
|
|
102
|
+
return normalized || '__null__';
|
|
103
|
+
}
|
|
104
|
+
function getContinuationScopeKey(worldId, agentId, chatId) {
|
|
105
|
+
return `${worldId}::${agentId}::${normalizeContinuationChatId(chatId)}`;
|
|
106
|
+
}
|
|
107
|
+
function enterContinuationScope(scopeKey, runId) {
|
|
108
|
+
const activeRun = activeContinuationRuns.get(scopeKey);
|
|
109
|
+
if (!activeRun) {
|
|
110
|
+
activeContinuationRuns.set(scopeKey, { runId, depth: 1 });
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
if (activeRun.runId !== runId) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
activeRun.depth += 1;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
function leaveContinuationScope(scopeKey, runId) {
|
|
120
|
+
const activeRun = activeContinuationRuns.get(scopeKey);
|
|
121
|
+
if (!activeRun || activeRun.runId !== runId) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
activeRun.depth -= 1;
|
|
125
|
+
if (activeRun.depth <= 0) {
|
|
126
|
+
activeContinuationRuns.delete(scopeKey);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function isContinuationRunActive(runId) {
|
|
130
|
+
for (const activeRun of activeContinuationRuns.values()) {
|
|
131
|
+
if (activeRun.runId === runId) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
function getLoadedSkillsForContinuationRun(runId) {
|
|
138
|
+
const existing = continuationRunLoadedSkills.get(runId);
|
|
139
|
+
if (existing) {
|
|
140
|
+
return existing;
|
|
141
|
+
}
|
|
142
|
+
const created = new Set();
|
|
143
|
+
continuationRunLoadedSkills.set(runId, created);
|
|
144
|
+
return created;
|
|
145
|
+
}
|
|
146
|
+
function getShellCommandResultsForContinuationRun(runId) {
|
|
147
|
+
const existing = continuationRunShellCommandResults.get(runId);
|
|
148
|
+
if (existing) {
|
|
149
|
+
return existing;
|
|
150
|
+
}
|
|
151
|
+
const created = new Map();
|
|
152
|
+
continuationRunShellCommandResults.set(runId, created);
|
|
153
|
+
return created;
|
|
154
|
+
}
|
|
155
|
+
function normalizeShellCommandParameterList(parameters) {
|
|
156
|
+
if (!Array.isArray(parameters)) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
return parameters.map((parameter) => String(parameter));
|
|
160
|
+
}
|
|
161
|
+
function buildShellCommandSignature(toolArgs, trustedWorkingDirectory) {
|
|
162
|
+
const command = String(toolArgs?.command || '').trim();
|
|
163
|
+
const parameters = normalizeShellCommandParameterList(toolArgs?.parameters);
|
|
164
|
+
const requestedDirectory = typeof toolArgs?.directory === 'string' && toolArgs.directory.trim()
|
|
165
|
+
? toolArgs.directory.trim()
|
|
166
|
+
: trustedWorkingDirectory;
|
|
167
|
+
return JSON.stringify({
|
|
168
|
+
command,
|
|
169
|
+
parameters,
|
|
170
|
+
directory: requestedDirectory,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function sanitizeToolArgsForEventPayload(toolName, toolArgs) {
|
|
174
|
+
if (toolName !== 'shell_cmd' || !toolArgs || typeof toolArgs !== 'object') {
|
|
175
|
+
return toolArgs;
|
|
176
|
+
}
|
|
177
|
+
const sanitized = { ...toolArgs };
|
|
178
|
+
delete sanitized.output_format;
|
|
179
|
+
delete sanitized.output_detail;
|
|
180
|
+
return sanitized;
|
|
181
|
+
}
|
|
182
|
+
function cleanupContinuationRunState(runId) {
|
|
183
|
+
if (isContinuationRunActive(runId)) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
continuationRunLoadedSkills.delete(runId);
|
|
187
|
+
continuationRunShellCommandResults.delete(runId);
|
|
188
|
+
}
|
|
189
|
+
// Fallback title candidates longer than this are likely full sentences/commands, not titles.
|
|
190
|
+
const FALLBACK_TITLE_MAX_CHARS = 60;
|
|
69
191
|
const GENERIC_TITLES = new Set([
|
|
70
192
|
'chat',
|
|
71
193
|
'new chat',
|
|
@@ -74,7 +196,9 @@ const GENERIC_TITLES = new Set([
|
|
|
74
196
|
'title',
|
|
75
197
|
'assistant chat',
|
|
76
198
|
'user chat',
|
|
77
|
-
'chat title'
|
|
199
|
+
'chat title',
|
|
200
|
+
'chat session',
|
|
201
|
+
'session'
|
|
78
202
|
]);
|
|
79
203
|
function normalizeTitlePromptText(content) {
|
|
80
204
|
return content
|
|
@@ -88,34 +212,62 @@ function clipTitlePromptText(content) {
|
|
|
88
212
|
}
|
|
89
213
|
return `${content.substring(0, TITLE_PROMPT_MAX_CHARS_PER_TURN - 3)}...`;
|
|
90
214
|
}
|
|
91
|
-
function
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (typeof message.content !== 'string') {
|
|
215
|
+
function selectTitleSourceUserMessage(messages, content) {
|
|
216
|
+
const contentCandidate = normalizeTitlePromptText(content || '');
|
|
217
|
+
if (contentCandidate) {
|
|
218
|
+
return clipTitlePromptText(contentCandidate);
|
|
219
|
+
}
|
|
220
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
221
|
+
const message = messages[index];
|
|
222
|
+
if (!message || message.role !== 'user' || typeof message.content !== 'string') {
|
|
99
223
|
continue;
|
|
100
224
|
}
|
|
101
225
|
const normalized = normalizeTitlePromptText(message.content);
|
|
102
226
|
if (!normalized) {
|
|
103
227
|
continue;
|
|
104
228
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
229
|
+
return clipTitlePromptText(normalized);
|
|
230
|
+
}
|
|
231
|
+
return '';
|
|
232
|
+
}
|
|
233
|
+
function buildTitlePromptMessages(messages, content) {
|
|
234
|
+
// Collect up to TITLE_CONTEXT_WINDOW_TURNS * 2 most recent eligible messages (user + assistant only).
|
|
235
|
+
const maxMessages = TITLE_CONTEXT_WINDOW_TURNS * 2;
|
|
236
|
+
const window = [];
|
|
237
|
+
for (let i = messages.length - 1; i >= 0 && window.length < maxMessages; i -= 1) {
|
|
238
|
+
const message = messages[i];
|
|
239
|
+
if (!message)
|
|
110
240
|
continue;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
241
|
+
const role = String(message.role || '').toLowerCase();
|
|
242
|
+
if (role !== 'user' && role !== 'assistant')
|
|
243
|
+
continue;
|
|
244
|
+
if (typeof message.content !== 'string')
|
|
245
|
+
continue;
|
|
246
|
+
const normalized = normalizeTitlePromptText(message.content);
|
|
247
|
+
if (!normalized)
|
|
248
|
+
continue;
|
|
249
|
+
window.unshift({
|
|
250
|
+
role: role,
|
|
251
|
+
content: clipTitlePromptText(normalized)
|
|
116
252
|
});
|
|
117
253
|
}
|
|
118
|
-
|
|
254
|
+
// Apply explicit content override to the last user slot (or append if none found).
|
|
255
|
+
const overrideText = normalizeTitlePromptText(content || '');
|
|
256
|
+
if (overrideText) {
|
|
257
|
+
const clipped = clipTitlePromptText(overrideText);
|
|
258
|
+
let replaced = false;
|
|
259
|
+
for (let i = window.length - 1; i >= 0; i -= 1) {
|
|
260
|
+
if (window[i].role === 'user') {
|
|
261
|
+
window[i] = { role: 'user', content: clipped };
|
|
262
|
+
replaced = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!replaced) {
|
|
267
|
+
window.push({ role: 'user', content: clipped });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return window;
|
|
119
271
|
}
|
|
120
272
|
function sanitizeGeneratedTitle(rawTitle) {
|
|
121
273
|
const firstLine = String(rawTitle || '').split(/\r?\n/).find((line) => line.trim()) || '';
|
|
@@ -125,13 +277,26 @@ function sanitizeGeneratedTitle(rawTitle) {
|
|
|
125
277
|
.replace(/^[-*]\s+/, '')
|
|
126
278
|
.replace(/^\d+[.)]\s+/, '')
|
|
127
279
|
.replace(/^title\s*[:\-]\s*/i, '')
|
|
128
|
-
.replace(
|
|
280
|
+
.replace(/\\"/g, '') // strip \" sequences before quote removal
|
|
281
|
+
.replace(/"/g, '') // strip all remaining double quotes
|
|
282
|
+
.replace(/^['`]+|['`]+$/g, '') // strip leading/trailing single quotes and backticks
|
|
129
283
|
.replace(/[\r\n\*`_]+/g, ' ')
|
|
130
284
|
.replace(/\s+/g, ' ')
|
|
131
285
|
.trim();
|
|
132
286
|
title = title.replace(/[.!?]+$/g, '').trim();
|
|
133
287
|
return title;
|
|
134
288
|
}
|
|
289
|
+
function removeEnvVariableFromText(variablesText, key) {
|
|
290
|
+
const targetKey = String(key || '').trim();
|
|
291
|
+
if (!targetKey) {
|
|
292
|
+
return String(variablesText || '');
|
|
293
|
+
}
|
|
294
|
+
return String(variablesText || '')
|
|
295
|
+
.split(/\r?\n/)
|
|
296
|
+
.map((line) => line.trim())
|
|
297
|
+
.filter((line) => line && !line.startsWith(`${targetKey}=`))
|
|
298
|
+
.join('\n');
|
|
299
|
+
}
|
|
135
300
|
function isLowQualityTitle(title) {
|
|
136
301
|
if (!title)
|
|
137
302
|
return true;
|
|
@@ -146,18 +311,20 @@ function isLowQualityTitle(title) {
|
|
|
146
311
|
}
|
|
147
312
|
function pickFallbackTitle(content, promptMessages) {
|
|
148
313
|
const contentCandidate = sanitizeGeneratedTitle(content);
|
|
149
|
-
if (!isLowQualityTitle(contentCandidate)) {
|
|
314
|
+
if (!isLowQualityTitle(contentCandidate) && contentCandidate.length <= FALLBACK_TITLE_MAX_CHARS) {
|
|
150
315
|
return contentCandidate;
|
|
151
316
|
}
|
|
152
317
|
for (const message of promptMessages) {
|
|
153
318
|
if (message.role !== 'user')
|
|
154
319
|
continue;
|
|
155
320
|
const candidate = sanitizeGeneratedTitle(message.content);
|
|
156
|
-
if (!isLowQualityTitle(candidate)) {
|
|
321
|
+
if (!isLowQualityTitle(candidate) && candidate.length <= FALLBACK_TITLE_MAX_CHARS) {
|
|
157
322
|
return candidate;
|
|
158
323
|
}
|
|
159
324
|
}
|
|
160
|
-
return 'Chat
|
|
325
|
+
// No quality title found — return empty string so the caller retains 'New Chat'
|
|
326
|
+
// and the chat remains eligible for a future auto-title attempt.
|
|
327
|
+
return '';
|
|
161
328
|
}
|
|
162
329
|
function isTitleGenerationCanceledError(error) {
|
|
163
330
|
if (!error)
|
|
@@ -196,6 +363,406 @@ function parseToolCallArguments(rawArguments) {
|
|
|
196
363
|
}
|
|
197
364
|
return {};
|
|
198
365
|
}
|
|
366
|
+
function getLoadSkillIdFromToolArgs(toolArgs) {
|
|
367
|
+
const skillId = typeof toolArgs?.skill_id === 'string' ? toolArgs.skill_id.trim() : '';
|
|
368
|
+
return skillId || null;
|
|
369
|
+
}
|
|
370
|
+
function getLoadSkillIdFromRawToolArguments(rawArguments) {
|
|
371
|
+
const parsed = parseToolCallArguments(rawArguments);
|
|
372
|
+
return getLoadSkillIdFromToolArgs(parsed);
|
|
373
|
+
}
|
|
374
|
+
function isSuccessfulLoadSkillResult(toolResult) {
|
|
375
|
+
const envelope = parseToolExecutionEnvelopeContent(toolResult);
|
|
376
|
+
const normalized = envelope
|
|
377
|
+
? stringifyToolExecutionResult(envelope.result)
|
|
378
|
+
: String(toolResult || '');
|
|
379
|
+
return /<skill_context\b/i.test(normalized) && !/<error>/i.test(normalized);
|
|
380
|
+
}
|
|
381
|
+
function formatToolErrorContent(options) {
|
|
382
|
+
if (options.toolName === 'shell_cmd') {
|
|
383
|
+
return formatShellToolErrorEnvelopeContent({
|
|
384
|
+
command: options.toolArgs?.command,
|
|
385
|
+
parameters: options.toolArgs?.parameters,
|
|
386
|
+
error: options.error,
|
|
387
|
+
failureReason: options.failureReason,
|
|
388
|
+
toolCallId: options.toolCallId,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
const message = `Error executing tool: ${options.error instanceof Error ? options.error.message : String(options.error)}`;
|
|
392
|
+
if (options.toolName === 'load_skill') {
|
|
393
|
+
return serializeToolExecutionEnvelope({
|
|
394
|
+
__type: 'tool_execution_envelope',
|
|
395
|
+
version: 1,
|
|
396
|
+
tool: 'load_skill',
|
|
397
|
+
tool_call_id: options.toolCallId,
|
|
398
|
+
status: 'failed',
|
|
399
|
+
preview: createTextToolPreview(message),
|
|
400
|
+
result: message,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return message;
|
|
404
|
+
}
|
|
405
|
+
function resolveShellContinuationLlmResultMode(options) {
|
|
406
|
+
if (options.toolName !== 'shell_cmd') {
|
|
407
|
+
return 'verbose';
|
|
408
|
+
}
|
|
409
|
+
return 'minimal';
|
|
410
|
+
}
|
|
411
|
+
function getLatestUnresolvedToolCallForChat(agent, chatId) {
|
|
412
|
+
const chatMessages = agent.memory.filter((message) => message.chatId === chatId);
|
|
413
|
+
if (!chatMessages.length) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
const completedToolCallIds = new Set();
|
|
417
|
+
for (const message of chatMessages) {
|
|
418
|
+
if (message.role === 'tool' && typeof message.tool_call_id === 'string' && message.tool_call_id.trim()) {
|
|
419
|
+
completedToolCallIds.add(message.tool_call_id.trim());
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
for (let index = chatMessages.length - 1; index >= 0; index--) {
|
|
423
|
+
const message = chatMessages[index];
|
|
424
|
+
if (message.role !== 'assistant' || !Array.isArray(message.tool_calls) || message.tool_calls.length === 0) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
for (const toolCall of message.tool_calls) {
|
|
428
|
+
const toolCallId = String(toolCall?.id || '').trim();
|
|
429
|
+
const toolName = String(toolCall?.function?.name || '').trim();
|
|
430
|
+
if (!toolCallId || !toolName) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (completedToolCallIds.has(toolCallId)) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
assistantMessage: message,
|
|
438
|
+
toolCall: {
|
|
439
|
+
id: toolCallId,
|
|
440
|
+
function: {
|
|
441
|
+
name: toolName,
|
|
442
|
+
arguments: toolCall?.function?.arguments,
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
export async function resumePendingToolCallsForChat(world, chatId, targetAssistantMessageId) {
|
|
451
|
+
if (!chatId) {
|
|
452
|
+
return 0;
|
|
453
|
+
}
|
|
454
|
+
const resumeStartedAt = Date.now();
|
|
455
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls started', {
|
|
456
|
+
worldId: world.id,
|
|
457
|
+
chatId,
|
|
458
|
+
targetAssistantMessageId: targetAssistantMessageId || null,
|
|
459
|
+
});
|
|
460
|
+
const { getMCPToolsForWorld } = await import('../mcp-server-registry.js');
|
|
461
|
+
const mcpTools = await getMCPToolsForWorld(world.id);
|
|
462
|
+
const storage = await getStorageWrappers();
|
|
463
|
+
let resumedCount = 0;
|
|
464
|
+
for (const agent of world.agents.values()) {
|
|
465
|
+
const pending = getLatestUnresolvedToolCallForChat(agent, chatId);
|
|
466
|
+
if (!pending) {
|
|
467
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls skipped agent with no pending tool call', {
|
|
468
|
+
worldId: world.id,
|
|
469
|
+
chatId,
|
|
470
|
+
agentId: agent.id,
|
|
471
|
+
});
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
if (targetAssistantMessageId
|
|
475
|
+
&& pending.assistantMessage.messageId
|
|
476
|
+
&& pending.assistantMessage.messageId !== targetAssistantMessageId) {
|
|
477
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls skipped assistant target mismatch', {
|
|
478
|
+
worldId: world.id,
|
|
479
|
+
chatId,
|
|
480
|
+
agentId: agent.id,
|
|
481
|
+
pendingAssistantMessageId: pending.assistantMessage.messageId || null,
|
|
482
|
+
targetAssistantMessageId,
|
|
483
|
+
});
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
const { assistantMessage, toolCall } = pending;
|
|
487
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls found pending tool call', {
|
|
488
|
+
worldId: world.id,
|
|
489
|
+
chatId,
|
|
490
|
+
agentId: agent.id,
|
|
491
|
+
toolName: toolCall.function.name,
|
|
492
|
+
toolCallId: toolCall.id,
|
|
493
|
+
assistantMessageId: assistantMessage.messageId || null,
|
|
494
|
+
});
|
|
495
|
+
let toolArgs = {};
|
|
496
|
+
try {
|
|
497
|
+
toolArgs = parseToolCallArguments(toolCall.function.arguments);
|
|
498
|
+
}
|
|
499
|
+
catch (parseError) {
|
|
500
|
+
loggerRestoreResumeTools.warn('Resume pending tool calls failed to parse tool arguments', {
|
|
501
|
+
worldId: world.id,
|
|
502
|
+
chatId,
|
|
503
|
+
agentId: agent.id,
|
|
504
|
+
toolName: toolCall.function.name,
|
|
505
|
+
toolCallId: toolCall.id,
|
|
506
|
+
error: parseError instanceof Error ? parseError.message : String(parseError),
|
|
507
|
+
});
|
|
508
|
+
const errorContent = toolCall.function.name === 'shell_cmd' || toolCall.function.name === 'load_skill'
|
|
509
|
+
? formatToolErrorContent({
|
|
510
|
+
toolName: toolCall.function.name,
|
|
511
|
+
toolCallId: toolCall.id,
|
|
512
|
+
toolArgs,
|
|
513
|
+
error: `Invalid JSON in tool arguments: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
|
514
|
+
failureReason: 'validation_error',
|
|
515
|
+
})
|
|
516
|
+
: `Error executing tool: Invalid JSON in tool arguments: ${parseError instanceof Error ? parseError.message : String(parseError)}`;
|
|
517
|
+
const toolErrorMessage = {
|
|
518
|
+
role: 'tool',
|
|
519
|
+
content: errorContent,
|
|
520
|
+
tool_call_id: toolCall.id,
|
|
521
|
+
sender: agent.id,
|
|
522
|
+
createdAt: new Date(),
|
|
523
|
+
chatId,
|
|
524
|
+
messageId: generateId(),
|
|
525
|
+
replyToMessageId: assistantMessage.messageId,
|
|
526
|
+
agentId: agent.id,
|
|
527
|
+
};
|
|
528
|
+
agent.memory.push(toolErrorMessage);
|
|
529
|
+
if (assistantMessage.toolCallStatus) {
|
|
530
|
+
assistantMessage.toolCallStatus[toolCall.id] = { complete: true, result: errorContent };
|
|
531
|
+
}
|
|
532
|
+
await storage.saveAgent(world.id, agent);
|
|
533
|
+
await continueLLMAfterToolExecution(world, agent, chatId);
|
|
534
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls continued after parse error', {
|
|
535
|
+
worldId: world.id,
|
|
536
|
+
chatId,
|
|
537
|
+
agentId: agent.id,
|
|
538
|
+
toolCallId: toolCall.id,
|
|
539
|
+
});
|
|
540
|
+
resumedCount += 1;
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const toolDef = mcpTools[toolCall.function.name];
|
|
544
|
+
if (!toolDef) {
|
|
545
|
+
loggerRestoreResumeTools.warn('Resume pending tool calls tool definition missing', {
|
|
546
|
+
worldId: world.id,
|
|
547
|
+
chatId,
|
|
548
|
+
agentId: agent.id,
|
|
549
|
+
toolName: toolCall.function.name,
|
|
550
|
+
toolCallId: toolCall.id,
|
|
551
|
+
});
|
|
552
|
+
const errorContent = toolCall.function.name === 'shell_cmd' || toolCall.function.name === 'load_skill'
|
|
553
|
+
? formatToolErrorContent({
|
|
554
|
+
toolName: toolCall.function.name,
|
|
555
|
+
toolCallId: toolCall.id,
|
|
556
|
+
toolArgs,
|
|
557
|
+
error: `Tool not found: ${toolCall.function.name}`,
|
|
558
|
+
failureReason: 'execution_error',
|
|
559
|
+
})
|
|
560
|
+
: `Error executing tool: Tool not found: ${toolCall.function.name}`;
|
|
561
|
+
const toolErrorMessage = {
|
|
562
|
+
role: 'tool',
|
|
563
|
+
content: errorContent,
|
|
564
|
+
tool_call_id: toolCall.id,
|
|
565
|
+
sender: agent.id,
|
|
566
|
+
createdAt: new Date(),
|
|
567
|
+
chatId,
|
|
568
|
+
messageId: generateId(),
|
|
569
|
+
replyToMessageId: assistantMessage.messageId,
|
|
570
|
+
agentId: agent.id,
|
|
571
|
+
};
|
|
572
|
+
agent.memory.push(toolErrorMessage);
|
|
573
|
+
if (assistantMessage.toolCallStatus) {
|
|
574
|
+
assistantMessage.toolCallStatus[toolCall.id] = { complete: true, result: errorContent };
|
|
575
|
+
}
|
|
576
|
+
publishToolEvent(world, {
|
|
577
|
+
agentName: agent.id,
|
|
578
|
+
type: 'tool-error',
|
|
579
|
+
messageId: toolCall.id,
|
|
580
|
+
chatId,
|
|
581
|
+
toolExecution: {
|
|
582
|
+
toolName: toolCall.function.name,
|
|
583
|
+
toolCallId: toolCall.id,
|
|
584
|
+
input: toolArgs,
|
|
585
|
+
error: `Tool not found: ${toolCall.function.name}`,
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
await storage.saveAgent(world.id, agent);
|
|
589
|
+
await continueLLMAfterToolExecution(world, agent, chatId);
|
|
590
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls continued after missing tool definition', {
|
|
591
|
+
worldId: world.id,
|
|
592
|
+
chatId,
|
|
593
|
+
agentId: agent.id,
|
|
594
|
+
toolCallId: toolCall.id,
|
|
595
|
+
});
|
|
596
|
+
resumedCount += 1;
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls executing tool', {
|
|
600
|
+
worldId: world.id,
|
|
601
|
+
chatId,
|
|
602
|
+
agentId: agent.id,
|
|
603
|
+
toolName: toolCall.function.name,
|
|
604
|
+
toolCallId: toolCall.id,
|
|
605
|
+
});
|
|
606
|
+
let seededLoadSkillIdForContinuation = null;
|
|
607
|
+
publishToolEvent(world, {
|
|
608
|
+
agentName: agent.id,
|
|
609
|
+
type: 'tool-start',
|
|
610
|
+
messageId: toolCall.id,
|
|
611
|
+
chatId,
|
|
612
|
+
toolExecution: {
|
|
613
|
+
toolName: toolCall.function.name,
|
|
614
|
+
toolCallId: toolCall.id,
|
|
615
|
+
input: toolArgs,
|
|
616
|
+
},
|
|
617
|
+
});
|
|
618
|
+
try {
|
|
619
|
+
const trustedWorkingDirectory = String(getEnvValueFromText(world.variables, 'working_directory') || getDefaultWorkingDirectory()).trim() || getDefaultWorkingDirectory();
|
|
620
|
+
const toolContext = {
|
|
621
|
+
world,
|
|
622
|
+
messages: agent.memory,
|
|
623
|
+
toolCallId: toolCall.id,
|
|
624
|
+
chatId,
|
|
625
|
+
workingDirectory: trustedWorkingDirectory,
|
|
626
|
+
agentName: agent.id,
|
|
627
|
+
llmResultMode: resolveShellContinuationLlmResultMode({
|
|
628
|
+
toolName: toolCall.function.name,
|
|
629
|
+
}),
|
|
630
|
+
persistToolEnvelope: toolCall.function.name === 'shell_cmd' || toolCall.function.name === 'load_skill',
|
|
631
|
+
};
|
|
632
|
+
const toolResult = await toolDef.execute(toolArgs, undefined, undefined, toolContext);
|
|
633
|
+
const serializedToolResult = typeof toolResult === 'string'
|
|
634
|
+
? toolResult
|
|
635
|
+
: JSON.stringify(toolResult) ?? String(toolResult);
|
|
636
|
+
if (toolCall.function.name === 'load_skill') {
|
|
637
|
+
const requestedSkillId = getLoadSkillIdFromToolArgs(toolArgs);
|
|
638
|
+
if (requestedSkillId && isSuccessfulLoadSkillResult(serializedToolResult)) {
|
|
639
|
+
seededLoadSkillIdForContinuation = requestedSkillId;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const toolResultMessage = {
|
|
643
|
+
role: 'tool',
|
|
644
|
+
content: serializedToolResult,
|
|
645
|
+
tool_call_id: toolCall.id,
|
|
646
|
+
sender: agent.id,
|
|
647
|
+
createdAt: new Date(),
|
|
648
|
+
chatId,
|
|
649
|
+
messageId: generateId(),
|
|
650
|
+
replyToMessageId: assistantMessage.messageId,
|
|
651
|
+
agentId: agent.id,
|
|
652
|
+
};
|
|
653
|
+
agent.memory.push(toolResultMessage);
|
|
654
|
+
if (assistantMessage.toolCallStatus) {
|
|
655
|
+
assistantMessage.toolCallStatus[toolCall.id] = {
|
|
656
|
+
complete: true,
|
|
657
|
+
result: serializedToolResult,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
const toolEnvelope = parseToolExecutionEnvelopeContent(serializedToolResult);
|
|
661
|
+
const toolEventPreview = getToolEventPreviewPayload(serializedToolResult);
|
|
662
|
+
const toolEventResult = toolEnvelope ? toolEnvelope.result : toolResult;
|
|
663
|
+
publishToolEvent(world, {
|
|
664
|
+
agentName: agent.id,
|
|
665
|
+
type: 'tool-result',
|
|
666
|
+
messageId: toolCall.id,
|
|
667
|
+
chatId,
|
|
668
|
+
toolExecution: {
|
|
669
|
+
toolName: toolCall.function.name,
|
|
670
|
+
toolCallId: toolCall.id,
|
|
671
|
+
input: toolArgs,
|
|
672
|
+
...(toolEventPreview !== undefined ? { preview: toolEventPreview } : {}),
|
|
673
|
+
result: toolEventResult,
|
|
674
|
+
resultType: Array.isArray(toolEventResult)
|
|
675
|
+
? 'array'
|
|
676
|
+
: toolEventResult === null
|
|
677
|
+
? 'null'
|
|
678
|
+
: typeof toolEventResult === 'string'
|
|
679
|
+
? 'string'
|
|
680
|
+
: 'object',
|
|
681
|
+
resultSize: serializedToolResult.length,
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls tool execution result persisted', {
|
|
685
|
+
worldId: world.id,
|
|
686
|
+
chatId,
|
|
687
|
+
agentId: agent.id,
|
|
688
|
+
toolName: toolCall.function.name,
|
|
689
|
+
toolCallId: toolCall.id,
|
|
690
|
+
resultSize: serializedToolResult.length,
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
catch (toolError) {
|
|
694
|
+
loggerRestoreResumeTools.warn('Resume pending tool calls tool execution failed', {
|
|
695
|
+
worldId: world.id,
|
|
696
|
+
chatId,
|
|
697
|
+
agentId: agent.id,
|
|
698
|
+
toolName: toolCall.function.name,
|
|
699
|
+
toolCallId: toolCall.id,
|
|
700
|
+
error: toolError instanceof Error ? toolError.message : String(toolError),
|
|
701
|
+
});
|
|
702
|
+
const errorContent = formatToolErrorContent({
|
|
703
|
+
toolName: toolCall.function.name,
|
|
704
|
+
toolCallId: toolCall.id,
|
|
705
|
+
toolArgs,
|
|
706
|
+
error: toolError,
|
|
707
|
+
});
|
|
708
|
+
const toolErrorMessage = {
|
|
709
|
+
role: 'tool',
|
|
710
|
+
content: errorContent,
|
|
711
|
+
tool_call_id: toolCall.id,
|
|
712
|
+
sender: agent.id,
|
|
713
|
+
createdAt: new Date(),
|
|
714
|
+
chatId,
|
|
715
|
+
messageId: generateId(),
|
|
716
|
+
replyToMessageId: assistantMessage.messageId,
|
|
717
|
+
agentId: agent.id,
|
|
718
|
+
};
|
|
719
|
+
agent.memory.push(toolErrorMessage);
|
|
720
|
+
if (assistantMessage.toolCallStatus) {
|
|
721
|
+
assistantMessage.toolCallStatus[toolCall.id] = {
|
|
722
|
+
complete: true,
|
|
723
|
+
result: errorContent,
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
publishToolEvent(world, {
|
|
727
|
+
agentName: agent.id,
|
|
728
|
+
type: 'tool-error',
|
|
729
|
+
messageId: toolCall.id,
|
|
730
|
+
chatId,
|
|
731
|
+
toolExecution: {
|
|
732
|
+
toolName: toolCall.function.name,
|
|
733
|
+
toolCallId: toolCall.id,
|
|
734
|
+
input: toolArgs,
|
|
735
|
+
error: toolError instanceof Error ? toolError.message : String(toolError),
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
await storage.saveAgent(world.id, agent);
|
|
740
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls saved agent state', {
|
|
741
|
+
worldId: world.id,
|
|
742
|
+
chatId,
|
|
743
|
+
agentId: agent.id,
|
|
744
|
+
toolCallId: toolCall.id,
|
|
745
|
+
});
|
|
746
|
+
await continueLLMAfterToolExecution(world, agent, chatId, {
|
|
747
|
+
...(seededLoadSkillIdForContinuation ? { preloadedSkillIds: [seededLoadSkillIdForContinuation] } : {}),
|
|
748
|
+
});
|
|
749
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls continued LLM after tool execution', {
|
|
750
|
+
worldId: world.id,
|
|
751
|
+
chatId,
|
|
752
|
+
agentId: agent.id,
|
|
753
|
+
toolCallId: toolCall.id,
|
|
754
|
+
...(seededLoadSkillIdForContinuation ? { seededLoadSkillIdForContinuation } : {}),
|
|
755
|
+
});
|
|
756
|
+
resumedCount += 1;
|
|
757
|
+
}
|
|
758
|
+
loggerRestoreResumeTools.debug('Resume pending tool calls completed', {
|
|
759
|
+
worldId: world.id,
|
|
760
|
+
chatId,
|
|
761
|
+
resumedCount,
|
|
762
|
+
elapsedMs: Date.now() - resumeStartedAt,
|
|
763
|
+
});
|
|
764
|
+
return resumedCount;
|
|
765
|
+
}
|
|
199
766
|
function decodeControlTokens(value) {
|
|
200
767
|
return value.replace(/<ctrl(\d+)>/gi, (_match, codeRaw) => {
|
|
201
768
|
const code = Number(codeRaw);
|
|
@@ -341,15 +908,29 @@ export async function saveIncomingMessageToMemory(world, agent, messageEvent) {
|
|
|
341
908
|
}
|
|
342
909
|
// Derive chatId from the message event for concurrency-safe processing
|
|
343
910
|
// This ensures messages stay bound to their originating session
|
|
344
|
-
const targetChatId = messageEvent.chatId
|
|
911
|
+
const targetChatId = typeof messageEvent.chatId === 'string' ? messageEvent.chatId.trim() : '';
|
|
345
912
|
if (!targetChatId) {
|
|
346
|
-
loggerMemory.
|
|
913
|
+
loggerMemory.error('Saving message without explicit chatId', {
|
|
914
|
+
worldId: world.id,
|
|
347
915
|
agentId: agent.id,
|
|
348
916
|
messageId: messageEvent.messageId
|
|
349
917
|
});
|
|
918
|
+
return;
|
|
350
919
|
}
|
|
351
920
|
// Parse message content to detect enhanced format (e.g., tool results)
|
|
352
921
|
const { message: parsedMessage } = parseMessageContent(messageEvent.content, 'user');
|
|
922
|
+
if (messageEvent.messageId && targetChatId) {
|
|
923
|
+
const duplicate = agent.memory.some((message) => message.chatId === targetChatId && message.messageId === messageEvent.messageId);
|
|
924
|
+
if (duplicate) {
|
|
925
|
+
loggerMemory.debug('Skipping duplicate incoming message memory save', {
|
|
926
|
+
worldId: world.id,
|
|
927
|
+
agentId: agent.id,
|
|
928
|
+
chatId: targetChatId,
|
|
929
|
+
messageId: messageEvent.messageId,
|
|
930
|
+
});
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
353
934
|
const userMessage = {
|
|
354
935
|
...parsedMessage,
|
|
355
936
|
sender: messageEvent.sender,
|
|
@@ -382,7 +963,37 @@ export async function saveIncomingMessageToMemory(world, agent, messageEvent) {
|
|
|
382
963
|
* Used for auto-execution flow where tools are executed automatically
|
|
383
964
|
*/
|
|
384
965
|
export async function continueLLMAfterToolExecution(world, agent, chatId, options) {
|
|
385
|
-
const
|
|
966
|
+
const continuationChatId = typeof chatId === 'string' ? chatId.trim() : '';
|
|
967
|
+
if (!continuationChatId) {
|
|
968
|
+
throw new Error(`continueLLMAfterToolExecution: explicit chatId is required for agent ${agent.id}`);
|
|
969
|
+
}
|
|
970
|
+
const targetChatId = continuationChatId;
|
|
971
|
+
const continuationRunId = String(options?.continuationRunId || '').trim() || generateId();
|
|
972
|
+
const continuationScopeKey = getContinuationScopeKey(world.id, agent.id, continuationChatId);
|
|
973
|
+
const enteredScope = enterContinuationScope(continuationScopeKey, continuationRunId);
|
|
974
|
+
if (!enteredScope) {
|
|
975
|
+
loggerAgent.debug('Skipping duplicate continuation run while another run is active', {
|
|
976
|
+
worldId: world.id,
|
|
977
|
+
agentId: agent.id,
|
|
978
|
+
chatId: continuationChatId,
|
|
979
|
+
});
|
|
980
|
+
logToolBridge('CONTINUE SKIP_INFLIGHT', {
|
|
981
|
+
worldId: world.id,
|
|
982
|
+
agentId: agent.id,
|
|
983
|
+
chatId: continuationChatId,
|
|
984
|
+
responseType: 'skipped',
|
|
985
|
+
});
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const completeActivity = beginWorldActivity(world, `agent:${agent.id}`, targetChatId);
|
|
989
|
+
const loadedSkillsForRun = getLoadedSkillsForContinuationRun(continuationRunId);
|
|
990
|
+
const shellCommandResultsForRun = getShellCommandResultsForContinuationRun(continuationRunId);
|
|
991
|
+
for (const preloadedSkillId of options?.preloadedSkillIds || []) {
|
|
992
|
+
const normalizedSkillId = String(preloadedSkillId || '').trim();
|
|
993
|
+
if (normalizedSkillId) {
|
|
994
|
+
loadedSkillsForRun.add(normalizedSkillId);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
386
997
|
try {
|
|
387
998
|
let hopCount = options?.hopCount ?? 0;
|
|
388
999
|
const maxToolHops = 50;
|
|
@@ -390,25 +1001,25 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
390
1001
|
const maxEmptyTextRetries = 2;
|
|
391
1002
|
const emptyToolCallRetryCount = options?.emptyToolCallRetryCount ?? 0;
|
|
392
1003
|
const maxEmptyToolCallRetries = 2;
|
|
393
|
-
let transientGuardrailError;
|
|
1004
|
+
let transientGuardrailError = options?.transientContinuationInstruction;
|
|
394
1005
|
if (hopCount > maxToolHops) {
|
|
395
1006
|
const guardrailErrorMessage = `[Error] Tool continuation exceeded ${maxToolHops} hops. Guardrail triggered; reporting error and continuing.`;
|
|
396
1007
|
const guardrailToolCallId = generateId();
|
|
397
1008
|
loggerAgent.error('Tool continuation hop limit reached; reporting error and continuing loop', {
|
|
398
1009
|
agentId: agent.id,
|
|
399
|
-
chatId:
|
|
1010
|
+
chatId: targetChatId,
|
|
400
1011
|
hopCount,
|
|
401
1012
|
maxToolHops,
|
|
402
1013
|
});
|
|
403
1014
|
publishEvent(world, 'system', {
|
|
404
1015
|
message: guardrailErrorMessage,
|
|
405
1016
|
type: 'error',
|
|
406
|
-
});
|
|
1017
|
+
}, targetChatId);
|
|
407
1018
|
publishToolEvent(world, {
|
|
408
1019
|
agentName: agent.id,
|
|
409
1020
|
type: 'tool-error',
|
|
410
1021
|
messageId: guardrailToolCallId,
|
|
411
|
-
chatId:
|
|
1022
|
+
chatId: targetChatId,
|
|
412
1023
|
toolExecution: {
|
|
413
1024
|
toolName: '__tool_continuation_guardrail__',
|
|
414
1025
|
toolCallId: guardrailToolCallId,
|
|
@@ -418,7 +1029,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
418
1029
|
logToolBridge('CONTINUE HOP_GUARDRAIL', {
|
|
419
1030
|
worldId: world.id,
|
|
420
1031
|
agentId: agent.id,
|
|
421
|
-
chatId:
|
|
1032
|
+
chatId: targetChatId,
|
|
422
1033
|
hopCount,
|
|
423
1034
|
maxToolHops,
|
|
424
1035
|
guardrailToolCallId,
|
|
@@ -428,14 +1039,11 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
428
1039
|
hopCount = 0;
|
|
429
1040
|
}
|
|
430
1041
|
throwIfMessageProcessingStopped(options?.abortSignal);
|
|
431
|
-
// Use explicit chatId when provided, fallback to world.currentChatId.
|
|
432
|
-
const targetChatId = chatId !== undefined ? chatId : world.currentChatId;
|
|
433
1042
|
// Filter memory to current chat only
|
|
434
1043
|
const currentChatMessages = agent.memory.filter(m => m.chatId === targetChatId);
|
|
435
1044
|
loggerAgent.debug('Continuing LLM execution with tool result in memory', {
|
|
436
1045
|
agentId: agent.id,
|
|
437
1046
|
targetChatId,
|
|
438
|
-
worldCurrentChatId: world.currentChatId,
|
|
439
1047
|
totalMemoryLength: agent.memory.length,
|
|
440
1048
|
currentChatLength: currentChatMessages.length,
|
|
441
1049
|
lastFewMessages: currentChatMessages.slice(-5).map(m => ({
|
|
@@ -446,7 +1054,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
446
1054
|
}))
|
|
447
1055
|
});
|
|
448
1056
|
// Tool execution already happened before this function was called
|
|
449
|
-
// The tool result is already in memory with
|
|
1057
|
+
// The canonical tool result is already in memory with bounded preview fields for shell output
|
|
450
1058
|
// Now prepare messages for LLM - loads fresh data from storage
|
|
451
1059
|
// Prepare messages with system prompt and complete conversation history
|
|
452
1060
|
const messages = await prepareMessagesForLLM(world.id, agent, targetChatId ?? null);
|
|
@@ -485,6 +1093,8 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
485
1093
|
}
|
|
486
1094
|
catch (error) {
|
|
487
1095
|
loggerAgent.error('Failed to save agent after LLM call increment', {
|
|
1096
|
+
worldId: world.id,
|
|
1097
|
+
chatId: targetChatId,
|
|
488
1098
|
agentId: agent.id,
|
|
489
1099
|
error: error instanceof Error ? error.message : error
|
|
490
1100
|
});
|
|
@@ -603,7 +1213,15 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
603
1213
|
? firstInvalidToolCall.function.arguments
|
|
604
1214
|
: '{}';
|
|
605
1215
|
const malformedToolErrorContent = rawToolName
|
|
606
|
-
?
|
|
1216
|
+
? rawToolName === 'shell_cmd' || rawToolName === 'load_skill'
|
|
1217
|
+
? formatToolErrorContent({
|
|
1218
|
+
toolName: rawToolName,
|
|
1219
|
+
toolCallId,
|
|
1220
|
+
toolArgs: {},
|
|
1221
|
+
error: `Invalid tool call payload for '${rawToolName}'`,
|
|
1222
|
+
failureReason: 'validation_error',
|
|
1223
|
+
})
|
|
1224
|
+
: `Error executing tool: Invalid tool call payload for '${rawToolName}'`
|
|
607
1225
|
: 'Error executing tool: Invalid tool call payload - empty or missing tool name';
|
|
608
1226
|
loggerAgent.warn('Continuation returned tool_calls without executable tool; reporting tool error back to LLM context', {
|
|
609
1227
|
agentId: agent.id,
|
|
@@ -689,7 +1307,10 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
689
1307
|
}
|
|
690
1308
|
catch (error) {
|
|
691
1309
|
loggerMemory.error('Failed to save malformed continuation tool error context', {
|
|
1310
|
+
worldId: world.id,
|
|
1311
|
+
chatId: targetChatId,
|
|
692
1312
|
agentId: agent.id,
|
|
1313
|
+
toolCallId,
|
|
693
1314
|
error: error instanceof Error ? error.message : String(error),
|
|
694
1315
|
});
|
|
695
1316
|
}
|
|
@@ -699,12 +1320,40 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
699
1320
|
...options,
|
|
700
1321
|
hopCount: hopCount + 1,
|
|
701
1322
|
emptyToolCallRetryCount: emptyToolCallRetryCount + 1,
|
|
1323
|
+
continuationRunId,
|
|
702
1324
|
});
|
|
703
1325
|
return;
|
|
704
1326
|
}
|
|
705
1327
|
publishEvent(world, 'system', {
|
|
706
1328
|
message: '[Warning] Agent repeatedly returned invalid tool calls after tool execution. Please refine the prompt.',
|
|
707
1329
|
type: 'warning',
|
|
1330
|
+
}, targetChatId);
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
let requestedLoadSkillId = null;
|
|
1334
|
+
if (toolCall.function.name === 'load_skill') {
|
|
1335
|
+
try {
|
|
1336
|
+
requestedLoadSkillId = getLoadSkillIdFromRawToolArguments(toolCall.function.arguments);
|
|
1337
|
+
}
|
|
1338
|
+
catch {
|
|
1339
|
+
requestedLoadSkillId = null;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (requestedLoadSkillId && loadedSkillsForRun.has(requestedLoadSkillId)) {
|
|
1343
|
+
loggerAgent.debug('Suppressing duplicate load_skill call in continuation run', {
|
|
1344
|
+
worldId: world.id,
|
|
1345
|
+
agentId: agent.id,
|
|
1346
|
+
chatId: targetChatId,
|
|
1347
|
+
continuationRunId,
|
|
1348
|
+
skillId: requestedLoadSkillId,
|
|
1349
|
+
toolCallId: toolCall.id,
|
|
1350
|
+
});
|
|
1351
|
+
throwIfMessageProcessingStopped(options?.abortSignal);
|
|
1352
|
+
await continueLLMAfterToolExecution(world, agent, targetChatId, {
|
|
1353
|
+
...options,
|
|
1354
|
+
hopCount: hopCount + 1,
|
|
1355
|
+
continuationRunId,
|
|
1356
|
+
transientContinuationInstruction: `System notice: Suppressed duplicate load_skill("${requestedLoadSkillId}") in this run because the skill was already loaded. Continue the task using the existing skill context without calling load_skill again for this skill.`,
|
|
708
1357
|
});
|
|
709
1358
|
return;
|
|
710
1359
|
}
|
|
@@ -729,7 +1378,10 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
729
1378
|
}
|
|
730
1379
|
catch (error) {
|
|
731
1380
|
loggerMemory.error('Failed to save assistant tool_call message during continuation', {
|
|
1381
|
+
worldId: world.id,
|
|
1382
|
+
chatId: targetChatId,
|
|
732
1383
|
agentId: agent.id,
|
|
1384
|
+
toolCallId: toolCall.id,
|
|
733
1385
|
error: error instanceof Error ? error.message : String(error),
|
|
734
1386
|
});
|
|
735
1387
|
}
|
|
@@ -751,7 +1403,15 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
751
1403
|
if (!toolDef) {
|
|
752
1404
|
const missingToolResult = {
|
|
753
1405
|
role: 'tool',
|
|
754
|
-
content:
|
|
1406
|
+
content: toolCall.function.name === 'shell_cmd' || toolCall.function.name === 'load_skill'
|
|
1407
|
+
? formatToolErrorContent({
|
|
1408
|
+
toolName: toolCall.function.name,
|
|
1409
|
+
toolCallId: toolCall.id,
|
|
1410
|
+
toolArgs: {},
|
|
1411
|
+
error: `Tool not found: ${toolCall.function.name}`,
|
|
1412
|
+
failureReason: 'execution_error',
|
|
1413
|
+
})
|
|
1414
|
+
: `Error executing tool: Tool not found: ${toolCall.function.name}`,
|
|
755
1415
|
tool_call_id: toolCall.id,
|
|
756
1416
|
sender: agent.id,
|
|
757
1417
|
createdAt: new Date(),
|
|
@@ -783,17 +1443,27 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
783
1443
|
await continueLLMAfterToolExecution(world, agent, targetChatId, {
|
|
784
1444
|
...options,
|
|
785
1445
|
hopCount: hopCount + 1,
|
|
1446
|
+
continuationRunId,
|
|
786
1447
|
});
|
|
787
1448
|
return;
|
|
788
1449
|
}
|
|
789
1450
|
let toolArgs = {};
|
|
790
1451
|
try {
|
|
791
1452
|
toolArgs = parseToolCallArguments(toolCall.function.arguments);
|
|
1453
|
+
requestedLoadSkillId = requestedLoadSkillId || getLoadSkillIdFromToolArgs(toolArgs);
|
|
792
1454
|
}
|
|
793
1455
|
catch (parseError) {
|
|
794
1456
|
const parseErrorResult = {
|
|
795
1457
|
role: 'tool',
|
|
796
|
-
content:
|
|
1458
|
+
content: toolCall.function.name === 'shell_cmd' || toolCall.function.name === 'load_skill'
|
|
1459
|
+
? formatToolErrorContent({
|
|
1460
|
+
toolName: toolCall.function.name,
|
|
1461
|
+
toolCallId: toolCall.id,
|
|
1462
|
+
toolArgs,
|
|
1463
|
+
error: `Invalid JSON in tool arguments: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
|
1464
|
+
failureReason: 'validation_error',
|
|
1465
|
+
})
|
|
1466
|
+
: `Error executing tool: Invalid JSON in tool arguments: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
|
797
1467
|
tool_call_id: toolCall.id,
|
|
798
1468
|
sender: agent.id,
|
|
799
1469
|
createdAt: new Date(),
|
|
@@ -825,9 +1495,77 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
825
1495
|
await continueLLMAfterToolExecution(world, agent, targetChatId, {
|
|
826
1496
|
...options,
|
|
827
1497
|
hopCount: hopCount + 1,
|
|
1498
|
+
continuationRunId,
|
|
828
1499
|
});
|
|
829
1500
|
return;
|
|
830
1501
|
}
|
|
1502
|
+
let shellCommandSignature = null;
|
|
1503
|
+
const sanitizedToolArgsForEventPayload = sanitizeToolArgsForEventPayload(toolCall.function.name, toolArgs);
|
|
1504
|
+
if (toolCall.function.name === 'shell_cmd') {
|
|
1505
|
+
shellCommandSignature = buildShellCommandSignature(toolArgs, trustedWorkingDirectory);
|
|
1506
|
+
const reusedShellCommandResult = shellCommandResultsForRun.get(shellCommandSignature);
|
|
1507
|
+
if (reusedShellCommandResult !== undefined) {
|
|
1508
|
+
loggerAgent.debug('Suppressing duplicate shell_cmd call in continuation run', {
|
|
1509
|
+
worldId: world.id,
|
|
1510
|
+
agentId: agent.id,
|
|
1511
|
+
chatId: targetChatId,
|
|
1512
|
+
continuationRunId,
|
|
1513
|
+
toolCallId: toolCall.id,
|
|
1514
|
+
});
|
|
1515
|
+
const reusedToolResultMessage = {
|
|
1516
|
+
role: 'tool',
|
|
1517
|
+
content: reusedShellCommandResult,
|
|
1518
|
+
tool_call_id: toolCall.id,
|
|
1519
|
+
sender: agent.id,
|
|
1520
|
+
createdAt: new Date(),
|
|
1521
|
+
chatId: targetChatId,
|
|
1522
|
+
messageId: generateId(),
|
|
1523
|
+
replyToMessageId: messageId,
|
|
1524
|
+
agentId: agent.id,
|
|
1525
|
+
};
|
|
1526
|
+
agent.memory.push(reusedToolResultMessage);
|
|
1527
|
+
if (assistantToolCallMessage.toolCallStatus) {
|
|
1528
|
+
assistantToolCallMessage.toolCallStatus[toolCall.id] = {
|
|
1529
|
+
complete: true,
|
|
1530
|
+
result: reusedShellCommandResult,
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
const reusedToolEnvelope = parseToolExecutionEnvelopeContent(reusedShellCommandResult);
|
|
1534
|
+
const reusedToolEventPreview = getToolEventPreviewPayload(reusedShellCommandResult);
|
|
1535
|
+
const reusedToolEventResult = reusedToolEnvelope ? reusedToolEnvelope.result : reusedShellCommandResult;
|
|
1536
|
+
publishToolEvent(world, {
|
|
1537
|
+
agentName: agent.id,
|
|
1538
|
+
type: 'tool-result',
|
|
1539
|
+
messageId: toolCall.id,
|
|
1540
|
+
chatId: targetChatId,
|
|
1541
|
+
toolExecution: {
|
|
1542
|
+
toolName: toolCall.function.name,
|
|
1543
|
+
toolCallId: toolCall.id,
|
|
1544
|
+
input: sanitizedToolArgsForEventPayload,
|
|
1545
|
+
...(reusedToolEventPreview !== undefined ? { preview: reusedToolEventPreview } : {}),
|
|
1546
|
+
result: reusedToolEventResult,
|
|
1547
|
+
resultType: Array.isArray(reusedToolEventResult)
|
|
1548
|
+
? 'array'
|
|
1549
|
+
: reusedToolEventResult === null
|
|
1550
|
+
? 'null'
|
|
1551
|
+
: typeof reusedToolEventResult === 'string'
|
|
1552
|
+
? 'string'
|
|
1553
|
+
: 'object',
|
|
1554
|
+
resultSize: reusedShellCommandResult.length,
|
|
1555
|
+
metadata: { reusedFromContinuationRun: true },
|
|
1556
|
+
},
|
|
1557
|
+
});
|
|
1558
|
+
const storage = await getStorageWrappers();
|
|
1559
|
+
await storage.saveAgent(world.id, agent);
|
|
1560
|
+
await continueLLMAfterToolExecution(world, agent, targetChatId, {
|
|
1561
|
+
...options,
|
|
1562
|
+
hopCount: hopCount + 1,
|
|
1563
|
+
continuationRunId,
|
|
1564
|
+
transientContinuationInstruction: 'System notice: Suppressed duplicate shell_cmd call in this continuation run and reused its previous result. Continue from the existing command output without rerunning the same command.',
|
|
1565
|
+
});
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
831
1569
|
publishToolEvent(world, {
|
|
832
1570
|
agentName: agent.id,
|
|
833
1571
|
type: 'tool-start',
|
|
@@ -836,7 +1574,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
836
1574
|
toolExecution: {
|
|
837
1575
|
toolName: toolCall.function.name,
|
|
838
1576
|
toolCallId: toolCall.id,
|
|
839
|
-
input:
|
|
1577
|
+
input: sanitizedToolArgsForEventPayload,
|
|
840
1578
|
metadata: {
|
|
841
1579
|
isStreaming: isStreamingEnabled(),
|
|
842
1580
|
},
|
|
@@ -850,11 +1588,24 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
850
1588
|
chatId: targetChatId,
|
|
851
1589
|
abortSignal: options?.abortSignal,
|
|
852
1590
|
workingDirectory: trustedWorkingDirectory,
|
|
1591
|
+
agentName: agent.id,
|
|
1592
|
+
llmResultMode: resolveShellContinuationLlmResultMode({
|
|
1593
|
+
toolName: toolCall.function.name,
|
|
1594
|
+
}),
|
|
1595
|
+
persistToolEnvelope: toolCall.function.name === 'shell_cmd' || toolCall.function.name === 'load_skill',
|
|
853
1596
|
};
|
|
854
1597
|
const toolResult = await toolDef.execute(toolArgs, undefined, undefined, toolContext);
|
|
855
1598
|
const serializedToolResult = typeof toolResult === 'string'
|
|
856
1599
|
? toolResult
|
|
857
1600
|
: JSON.stringify(toolResult) ?? String(toolResult);
|
|
1601
|
+
if (toolCall.function.name === 'load_skill'
|
|
1602
|
+
&& requestedLoadSkillId
|
|
1603
|
+
&& isSuccessfulLoadSkillResult(serializedToolResult)) {
|
|
1604
|
+
loadedSkillsForRun.add(requestedLoadSkillId);
|
|
1605
|
+
}
|
|
1606
|
+
if (toolCall.function.name === 'shell_cmd' && shellCommandSignature) {
|
|
1607
|
+
shellCommandResultsForRun.set(shellCommandSignature, serializedToolResult);
|
|
1608
|
+
}
|
|
858
1609
|
const toolResultMessage = {
|
|
859
1610
|
role: 'tool',
|
|
860
1611
|
content: serializedToolResult,
|
|
@@ -873,6 +1624,9 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
873
1624
|
result: serializedToolResult,
|
|
874
1625
|
};
|
|
875
1626
|
}
|
|
1627
|
+
const toolEnvelope = parseToolExecutionEnvelopeContent(serializedToolResult);
|
|
1628
|
+
const toolEventPreview = getToolEventPreviewPayload(serializedToolResult);
|
|
1629
|
+
const toolEventResult = toolEnvelope ? toolEnvelope.result : toolResult;
|
|
876
1630
|
publishToolEvent(world, {
|
|
877
1631
|
agentName: agent.id,
|
|
878
1632
|
type: 'tool-result',
|
|
@@ -881,21 +1635,27 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
881
1635
|
toolExecution: {
|
|
882
1636
|
toolName: toolCall.function.name,
|
|
883
1637
|
toolCallId: toolCall.id,
|
|
884
|
-
input:
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1638
|
+
input: sanitizedToolArgsForEventPayload,
|
|
1639
|
+
...(toolEventPreview !== undefined ? { preview: toolEventPreview } : {}),
|
|
1640
|
+
result: toolEventResult,
|
|
1641
|
+
resultType: Array.isArray(toolEventResult)
|
|
1642
|
+
? 'array'
|
|
1643
|
+
: toolEventResult === null
|
|
1644
|
+
? 'null'
|
|
1645
|
+
: typeof toolEventResult === 'string'
|
|
1646
|
+
? 'string'
|
|
892
1647
|
: 'object',
|
|
893
1648
|
resultSize: serializedToolResult.length,
|
|
894
1649
|
},
|
|
895
1650
|
});
|
|
896
1651
|
}
|
|
897
1652
|
catch (toolError) {
|
|
898
|
-
const errorContent =
|
|
1653
|
+
const errorContent = formatToolErrorContent({
|
|
1654
|
+
toolName: toolCall.function.name,
|
|
1655
|
+
toolCallId: toolCall.id,
|
|
1656
|
+
toolArgs,
|
|
1657
|
+
error: toolError,
|
|
1658
|
+
});
|
|
899
1659
|
const toolErrorMessage = {
|
|
900
1660
|
role: 'tool',
|
|
901
1661
|
content: errorContent,
|
|
@@ -922,7 +1682,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
922
1682
|
toolExecution: {
|
|
923
1683
|
toolName: toolCall.function.name,
|
|
924
1684
|
toolCallId: toolCall.id,
|
|
925
|
-
input:
|
|
1685
|
+
input: sanitizedToolArgsForEventPayload,
|
|
926
1686
|
error: toolError instanceof Error ? toolError.message : String(toolError),
|
|
927
1687
|
},
|
|
928
1688
|
});
|
|
@@ -933,7 +1693,10 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
933
1693
|
}
|
|
934
1694
|
catch (error) {
|
|
935
1695
|
loggerMemory.error('Failed to save continuation tool result to memory', {
|
|
1696
|
+
worldId: world.id,
|
|
1697
|
+
chatId: targetChatId,
|
|
936
1698
|
agentId: agent.id,
|
|
1699
|
+
toolCallId: toolCall.id,
|
|
937
1700
|
error: error instanceof Error ? error.message : String(error),
|
|
938
1701
|
});
|
|
939
1702
|
}
|
|
@@ -941,6 +1704,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
941
1704
|
await continueLLMAfterToolExecution(world, agent, targetChatId, {
|
|
942
1705
|
...options,
|
|
943
1706
|
hopCount: hopCount + 1,
|
|
1707
|
+
continuationRunId,
|
|
944
1708
|
});
|
|
945
1709
|
return;
|
|
946
1710
|
}
|
|
@@ -964,6 +1728,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
964
1728
|
await continueLLMAfterToolExecution(world, agent, targetChatId, {
|
|
965
1729
|
...options,
|
|
966
1730
|
emptyTextRetryCount: emptyTextRetryCount + 1,
|
|
1731
|
+
continuationRunId,
|
|
967
1732
|
});
|
|
968
1733
|
return;
|
|
969
1734
|
}
|
|
@@ -981,7 +1746,7 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
981
1746
|
publishEvent(world, 'system', {
|
|
982
1747
|
message: '[Warning] Agent returned empty follow-up after tool execution. Please retry or refine the prompt.',
|
|
983
1748
|
type: 'warning'
|
|
984
|
-
});
|
|
1749
|
+
}, targetChatId);
|
|
985
1750
|
logToolBridge('CONTINUE EMPTY_TEXT_STOP', {
|
|
986
1751
|
worldId: world.id,
|
|
987
1752
|
agentId: agent.id,
|
|
@@ -1015,12 +1780,24 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
1015
1780
|
}
|
|
1016
1781
|
catch (error) {
|
|
1017
1782
|
loggerMemory.error('Failed to save agent response after tool execution', {
|
|
1783
|
+
worldId: world.id,
|
|
1784
|
+
chatId: targetChatId,
|
|
1018
1785
|
agentId: agent.id,
|
|
1786
|
+
messageId,
|
|
1019
1787
|
error: error instanceof Error ? error.message : String(error)
|
|
1020
1788
|
});
|
|
1021
1789
|
}
|
|
1022
1790
|
// Publish the response message using the same messageId from streaming
|
|
1023
1791
|
publishMessageWithId(world, sanitizedResponse, agent.id, messageId, targetChatId, undefined);
|
|
1792
|
+
loggerMessagePublish.debug('Published assistant response message', {
|
|
1793
|
+
worldId: world.id,
|
|
1794
|
+
chatId: targetChatId,
|
|
1795
|
+
agentId: agent.id,
|
|
1796
|
+
messageId,
|
|
1797
|
+
turnId: messageId,
|
|
1798
|
+
responseLength: sanitizedResponse.length,
|
|
1799
|
+
source: 'continuation',
|
|
1800
|
+
});
|
|
1024
1801
|
loggerAgent.debug('Agent response published after tool execution', {
|
|
1025
1802
|
agentId: agent.id,
|
|
1026
1803
|
messageId,
|
|
@@ -1031,46 +1808,61 @@ export async function continueLLMAfterToolExecution(world, agent, chatId, option
|
|
|
1031
1808
|
if (isMessageProcessingCanceledError(error) || options?.abortSignal?.aborted) {
|
|
1032
1809
|
loggerAgent.info('Skipped continuation after stop request', {
|
|
1033
1810
|
agentId: agent.id,
|
|
1034
|
-
chatId:
|
|
1811
|
+
chatId: targetChatId,
|
|
1035
1812
|
error: error instanceof Error ? error.message : String(error)
|
|
1036
1813
|
});
|
|
1037
1814
|
logToolBridge('CONTINUE CANCELED', {
|
|
1038
1815
|
worldId: world.id,
|
|
1039
1816
|
agentId: agent.id,
|
|
1040
|
-
chatId:
|
|
1817
|
+
chatId: targetChatId,
|
|
1041
1818
|
error: error instanceof Error ? error.message : String(error),
|
|
1042
1819
|
});
|
|
1043
1820
|
return;
|
|
1044
1821
|
}
|
|
1045
1822
|
loggerAgent.error('Failed to continue LLM after tool execution', {
|
|
1823
|
+
worldId: world.id,
|
|
1824
|
+
chatId: targetChatId,
|
|
1046
1825
|
agentId: agent.id,
|
|
1047
1826
|
error: error instanceof Error ? error.message : error
|
|
1048
1827
|
});
|
|
1049
1828
|
publishEvent(world, 'system', {
|
|
1050
1829
|
message: `[Error] ${error.message}`,
|
|
1051
1830
|
type: 'error'
|
|
1052
|
-
});
|
|
1831
|
+
}, targetChatId);
|
|
1053
1832
|
logToolBridge('CONTINUE ERROR', {
|
|
1054
1833
|
worldId: world.id,
|
|
1055
1834
|
agentId: agent.id,
|
|
1056
|
-
chatId:
|
|
1835
|
+
chatId: targetChatId,
|
|
1057
1836
|
error: error instanceof Error ? error.message : String(error),
|
|
1058
1837
|
});
|
|
1059
1838
|
}
|
|
1060
1839
|
finally {
|
|
1840
|
+
leaveContinuationScope(continuationScopeKey, continuationRunId);
|
|
1841
|
+
cleanupContinuationRunState(continuationRunId);
|
|
1061
1842
|
completeActivity();
|
|
1062
1843
|
}
|
|
1063
1844
|
}
|
|
1064
1845
|
/**
|
|
1065
1846
|
* Handle text response from LLM (extracted for clarity)
|
|
1066
|
-
* @param chatId - Explicit chat ID for concurrency-safe processing.
|
|
1847
|
+
* @param chatId - Explicit chat ID for concurrency-safe processing. When omitted, `messageEvent.chatId` is used.
|
|
1067
1848
|
*/
|
|
1068
1849
|
export async function handleTextResponse(world, agent, responseText, messageId, messageEvent, chatId) {
|
|
1069
|
-
|
|
1070
|
-
const
|
|
1850
|
+
const explicitChatId = typeof chatId === 'string' ? chatId.trim() : '';
|
|
1851
|
+
const messageChatId = typeof messageEvent.chatId === 'string' ? messageEvent.chatId.trim() : '';
|
|
1852
|
+
const targetChatId = explicitChatId || messageChatId;
|
|
1853
|
+
if (!targetChatId) {
|
|
1854
|
+
throw new Error(`handleTextResponse: explicit chatId is required for agent ${agent.id}`);
|
|
1855
|
+
}
|
|
1071
1856
|
const sanitizedResponse = removeSelfMentions(responseText, agent.id);
|
|
1857
|
+
// const needsInfiniteEtudePedagogueHandoff =
|
|
1858
|
+
// world.id === 'infinite-etude' &&
|
|
1859
|
+
// agent.id === 'madame-pedagogue' &&
|
|
1860
|
+
// !/^\s*@monsieur-engraver\b/im.test(sanitizedResponse);
|
|
1861
|
+
// const responseWithRequiredHandoff = needsInfiniteEtudePedagogueHandoff
|
|
1862
|
+
// ? `${sanitizedResponse.trimEnd()}\n\n@monsieur-engraver please render this.`
|
|
1863
|
+
// : sanitizedResponse;
|
|
1072
1864
|
// Apply auto-mention logic if needed
|
|
1073
|
-
let finalResponse = sanitizedResponse;
|
|
1865
|
+
let finalResponse = sanitizedResponse; // responseWithRequiredHandoff;
|
|
1074
1866
|
if (agent.autoReply !== false && shouldAutoMention(sanitizedResponse, messageEvent.sender, agent.id)) {
|
|
1075
1867
|
finalResponse = addAutoMention(sanitizedResponse, messageEvent.sender);
|
|
1076
1868
|
loggerAutoMention.debug('Auto-mention applied', {
|
|
@@ -1108,12 +1900,25 @@ export async function handleTextResponse(world, agent, responseText, messageId,
|
|
|
1108
1900
|
}
|
|
1109
1901
|
catch (error) {
|
|
1110
1902
|
loggerMemory.error('Failed to save agent response', {
|
|
1903
|
+
worldId: world.id,
|
|
1904
|
+
chatId: targetChatId,
|
|
1111
1905
|
agentId: agent.id,
|
|
1906
|
+
messageId,
|
|
1112
1907
|
error: error instanceof Error ? error.message : String(error)
|
|
1113
1908
|
});
|
|
1114
1909
|
}
|
|
1115
1910
|
// Publish the response message using the same messageId from streaming
|
|
1116
1911
|
publishMessageWithId(world, finalResponse, agent.id, messageId, targetChatId, messageEvent.messageId);
|
|
1912
|
+
loggerMessagePublish.debug('Published assistant response message', {
|
|
1913
|
+
worldId: world.id,
|
|
1914
|
+
chatId: targetChatId,
|
|
1915
|
+
agentId: agent.id,
|
|
1916
|
+
messageId,
|
|
1917
|
+
turnId: messageId,
|
|
1918
|
+
replyToMessageId: messageEvent.messageId,
|
|
1919
|
+
responseLength: finalResponse.length,
|
|
1920
|
+
source: 'direct',
|
|
1921
|
+
});
|
|
1117
1922
|
loggerAgent.debug('Agent response published', {
|
|
1118
1923
|
agentId: agent.id,
|
|
1119
1924
|
messageId,
|
|
@@ -1156,10 +1961,7 @@ export async function generateChatTitleFromMessages(world, content, targetChatId
|
|
|
1156
1961
|
const storage = await getStorageWrappers();
|
|
1157
1962
|
// Load messages for the target chat only, not all messages.
|
|
1158
1963
|
messages = targetChatId ? await storage.getMemory(world.id, targetChatId) : [];
|
|
1159
|
-
|
|
1160
|
-
messages.push({ role: 'user', content });
|
|
1161
|
-
}
|
|
1162
|
-
promptMessages = buildTitlePromptMessages(messages);
|
|
1964
|
+
promptMessages = buildTitlePromptMessages(messages, content);
|
|
1163
1965
|
loggerChatTitle.debug('Calling LLM for title generation', {
|
|
1164
1966
|
messageCount: messages.length,
|
|
1165
1967
|
promptMessageCount: promptMessages.length,
|
|
@@ -1170,19 +1972,22 @@ export async function generateChatTitleFromMessages(world, content, targetChatId
|
|
|
1170
1972
|
const tempAgent = {
|
|
1171
1973
|
provider: world.chatLLMProvider || firstAgent?.provider || 'openai',
|
|
1172
1974
|
model: world.chatLLMModel || firstAgent?.model || 'gpt-4',
|
|
1173
|
-
systemPrompt: 'You are a
|
|
1975
|
+
systemPrompt: 'You are a concise title generator. Given a conversation snippet, output a short noun-phrase title (3–6 words, Title Case). Rules: output the title only — no explanation, no punctuation at the end; never copy the user message verbatim; if the user message begins with @agentname, that is an agent mention — base the title on the topic or task after it, not on the mention itself.',
|
|
1174
1976
|
maxTokens: 20,
|
|
1175
1977
|
};
|
|
1176
1978
|
const userPrompt = {
|
|
1177
1979
|
role: 'user',
|
|
1178
|
-
content: `
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1980
|
+
content: `Generate a short title (3–6 words, Title Case) for this conversation.\nRules:\n- Do NOT copy the user message word-for-word.\n- An @name prefix (e.g. "@gemini") is an agent mention — base the title on the topic or task, not the mention.\n- Output the title only.\n\n${promptMessages.map(msg => `-${msg.role}: ${msg.content}`).join('\n')}`
|
|
1981
|
+
};
|
|
1982
|
+
const titleGenerationWorld = {
|
|
1983
|
+
...world,
|
|
1984
|
+
// Force reasoning_effort=none so thinking models (e.g. Gemini 2.5 Flash)
|
|
1985
|
+
// don't exhaust the maxOutputTokens budget on thinking before outputting text.
|
|
1986
|
+
variables: removeEnvVariableFromText(world.variables, 'reasoning_effort') + '\nreasoning_effort=none',
|
|
1182
1987
|
};
|
|
1183
|
-
const { response: titleResponse } = await generateAgentResponse(
|
|
1184
|
-
//
|
|
1185
|
-
title =
|
|
1988
|
+
const { response: titleResponse } = await generateAgentResponse(titleGenerationWorld, tempAgent, [userPrompt], undefined, true, targetChatId); // skipTools = true for title generation
|
|
1989
|
+
// LLMResponse is an object {type, content?} — extract the text content.
|
|
1990
|
+
title = titleResponse?.type === 'text' ? (titleResponse?.content ?? '') : '';
|
|
1186
1991
|
loggerChatTitle.debug('LLM generated title', { rawTitle: title });
|
|
1187
1992
|
}
|
|
1188
1993
|
catch (error) {
|