Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions coverage.txt
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
ℹ start of coverage report
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ src | | | |
ℹ agent | | | |
ℹ agentRegistry.js | 92.23 | 84.00 | 100.00 | 72-73 78-79 82-83 86-87
ℹ agents | | | |
ℹ code-review.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ coding.js | 93.33 | 66.67 | 100.00 | 18-19
ℹ debug.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ documentation.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ code-review.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ coding.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ debug.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ documentation.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ index.js | 100.00 | 100.00 | 100.00 |
ℹ performance.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ research.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ search.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ security-audit.js | 93.55 | 66.67 | 100.00 | 18-19
ℹ testing.js | 93.33 | 66.67 | 100.00 | 18-19
ℹ performance.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ research.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ search.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ security-audit.js | 91.89 | 75.00 | 100.00 | 19-21
ℹ testing.js | 91.67 | 75.00 | 100.00 | 19-21
ℹ config | | | |
ℹ loader.js | 89.62 | 83.33 | 72.73 | 66-69 87-89 96 114 116 166-170 180-183
ℹ mutate.js | 54.72 | 100.00 | 0.00 | 11-15 25-37 48-53
ℹ schemas.js | 100.00 | 100.00 | 100.00 |
ℹ logger.js | 75.23 | 40.91 | 72.73 | 26-34 39 41-43 64-65 73-77 100-106 112-116 131 163-164 166-167 184-185 191-192 198-199 202-206 209-213 216
ℹ logger.js | 76.61 | 41.67 | 81.82 | 26-34 39 41-43 64-65 73-77 100-106 112-116 131 163-164 166-167 184-185 191-192 198-199 205-206 209-213 216
ℹ memory | | | |
ℹ context.js | 98.21 | 78.79 | 100.00 | 110-111
ℹ expireEphemeral.js | 94.29 | 73.68 | 100.00 | 24-25 65-66
ℹ context.js | 97.44 | 78.79 | 100.00 | 114-116
ℹ expireEphemeral.js | 93.15 | 73.68 | 100.00 | 25-27 68-69
ℹ gc.js | 99.30 | 96.00 | 100.00 | 53
ℹ profile.js | 98.87 | 96.23 | 100.00 | 76-77
ℹ profile.js | 98.48 | 96.43 | 100.00 | 82-84
ℹ prompts.js | 100.00 | 100.00 | 100.00 |
ℹ reader.js | 96.36 | 81.25 | 100.00 | 22-23
ℹ reader.js | 95.16 | 82.35 | 100.00 | 23-25
ℹ provider | | | |
ℹ openai.js | 100.00 | 100.00 | 100.00 |
ℹ sandbox | | | |
ℹ capability.js | 100.00 | 100.00 | 100.00 |
ℹ envInjector.js | 100.00 | 100.00 | 100.00 |
ℹ pathResolver.js | 100.00 | 100.00 | 100.00 |
ℹ runner.js | 92.23 | 63.46 | 88.89 | 30 63 65 69 77 79 84 86 88 90 94-95 132-133 181
ℹ runner.js | 91.58 | 62.96 | 88.89 | 31 70 72 76 84 86 91 93 95 97 101-102 139-140 142-143 190
ℹ timeoutHandler.js | 100.00 | 100.00 | 100.00 |
ℹ urlFilter.js | 100.00 | 93.75 | 100.00 |
ℹ scheduler | | | |
ℹ cron.js | 30.19 | 100.00 | 5.88 | 29-30 52-53 65 73-79 87-96 103-105 114-134 145-185 196-230 240-283 291-326 334-370 379-398 408-432 441-464 480-571
ℹ cron.js | 30.41 | 100.00 | 5.26 | 29-53 62-73 83-84 107-109 121 129-135 143-151 158-160 169-189 200-240 251-285 295-338 346-381 389-425 434-453 463-487 496-519 535-626
ℹ index.js | 100.00 | 100.00 | 100.00 |
ℹ scheduler.js | 99.09 | 95.00 | 85.00 | 217-218
ℹ scheduler.js | 93.64 | 93.65 | 85.00 | 192-204 234-235
ℹ session | | | |
ℹ checkpointer.js | 82.22 | 87.50 | 50.00 | 22 24 39-43 45
ℹ factory.js | 100.00 | 100.00 | 100.00 |
Expand All @@ -52,8 +52,8 @@
ℹ stateManager.js | 100.00 | 100.00 | 100.00 |
ℹ window.js | 100.00 | 91.67 | 100.00 |
ℹ skills | | | |
ℹ discoverer.js | 96.35 | 87.93 | 100.00 | 61-66 173-174
ℹ registry.js | 76.69 | 46.15 | 47.37 | 46-49 52-54 106-107 128-129 146-147 155-164 175-177 180-182 208-214 222-226 234-238 245-246 260-267 276-283 291-295
ℹ discoverer.js | 96.52 | 88.33 | 100.00 | 62-67 177-178
ℹ registry.js | 76.61 | 46.15 | 47.37 | 45-48 51-53 105-106 127-128 145-146 154-163 174-176 179-181 207-213 221-225 233-237 244-245 259-266 275-282 290-294
ℹ types.js | 100.00 | 100.00 | 100.00 |
ℹ validator.js | 89.78 | 76.32 | 80.00 | 19-20 27-28 68 70 72-73 105-107 119-121
ℹ tools | | | |
Expand All @@ -64,13 +64,13 @@
ℹ cron.js | 94.41 | 88.46 | 73.68 | 93-94 106-107 228-229 231-242 246-252 325-326
ℹ date.js | 100.00 | 100.00 | 100.00 |
ℹ image.js | 97.50 | 91.67 | 50.00 | 95-97
ℹ index.js | 100.00 | 94.29 | 100.00 |
ℹ index.js | 100.00 | 94.44 | 100.00 |
ℹ memory.js | 96.52 | 83.56 | 93.33 | 55 98-99 194-198 298-300
ℹ moa.js | 100.00 | 94.44 | 84.62 |
ℹ sampling.js | 94.97 | 81.82 | 80.00 | 27 180-188
ℹ scanAgents.js | 100.00 | 80.00 | 100.00 |
ℹ session_search.js | 97.06 | 71.19 | 94.12 | 71-72 118-119 128 181-182
ℹ shell.js | 92.58 | 76.47 | 86.67 | 41-44 81 110-111 197-198 204-206 212-213 220-221 228-229 231
ℹ shell.js | 92.61 | 76.47 | 86.67 | 42-45 82 111-112 198-199 205-207 213-214 221-222 229-230 232
ℹ skills.js | 77.47 | 85.48 | 60.00 | 40-60 81-114 168-169 196-197 224-232 243-250 270-277 293-295 310-311
ℹ tts.js | 100.00 | 88.00 | 50.00 |
ℹ vision.js | 100.00 | 84.21 | 80.00 |
Expand All @@ -82,14 +82,14 @@
ℹ conversationPanel.js | 100.00 | 100.00 | 100.00 |
ℹ inputPanel.js | 100.00 | 100.00 | 100.00 |
ℹ markdownText.js | 94.74 | 90.91 | 72.73 | 75 90-91 99-100 123-126
ℹ messageBubble.js | 85.20 | 59.26 | 69.23 | 138-143 163-172 177-184 189-196 236-240
ℹ messageList.js | 83.17 | 59.46 | 57.69 | 74 91-94 119-136 146-162 171 178-182 221 229 237 246-252 260-265 284-286 306 354-355
ℹ messageBubble.js | 83.21 | 53.33 | 69.23 | 142-147 167-176 181-188 193-200 206-213 253-257
ℹ messageList.js | 82.81 | 59.46 | 57.69 | 74 91-94 120-140 150-166 175 182-186 228 236 244 253-259 267-272 291-293 313 361-362
ℹ messages.js | 100.00 | 94.44 | 100.00 |
ℹ panels.js | 100.00 | 100.00 | 100.00 |
ℹ statusBar.js | 91.89 | 84.21 | 100.00 | 36-37 48-54
ℹ workspace | | | |
ℹ loadAgents.js | 100.00 | 87.50 | 100.00 |
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ all files | 86.32 | 83.32 | 79.44 |
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ all files | 85.90 | 83.25 | 79.71 |
ℹ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ℹ end of coverage report
54 changes: 45 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,28 +181,64 @@ async function callProvider(_name, _providerConfig, message, streamingCallback,
};

let collectedContent = "";
let toolCallCount = 0;
const input = {
messages: [{ role: "user", content: message }],
};

for await (const [_namespace, chunk] of await agent.stream(input, {
for await (const event of agent.streamEvents(input, {
...config,
...options,
streamMode: "messages",
version: "v2",
subgraphs: true,
})) {
const [message] = chunk;
const text = message?.text ?? "";
// Transform LangChain streamEvents format to match streamingCallback expectations
const transformedEvent = {
type: event.event,
name: event.name,
data: event.data,
metadata: event.metadata,
};

// Forward the transformed event to the streaming callback
if (streamingCallback) {
streamingCallback(transformedEvent);
}

// Count tool calls
if (event.event === "on_tool_start") {
toolCallCount++;
}

if (text) {
collectedContent += text;
if (streamingCallback) {
streamingCallback({ type: "message", text });
// Accumulate text content from message events
if (event.event === "on_chat_model_stream" && event.data?.chunk) {
const chunk = event.data.chunk;
// streamEvents v2 chunks are AIMessageChunk objects — content is direct
const content = chunk.content ?? "";
if (content) {
collectedContent += content;
}
}
if (event.event === "message" && event.data?.content) {
const text =
typeof event.data.content === "string"
? event.data.content
: (event.data.content?.text ?? "");
if (text) {
collectedContent += text;
}
}
if (event.event === "on_chat_model_end" && event.data?.output?.kwargs?.content) {
collectedContent += event.data.output.kwargs.content;
}
}

return { provider: providerName, content: collectedContent, tokens: { input: 0, output: 0 } };
return {
provider: providerName,
content: collectedContent,
tokens: { input: 0, output: 0 },
toolCallCount,
};
}

// Conversation handler
Expand Down
110 changes: 68 additions & 42 deletions src/tui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,17 @@ export default function App({
}

const assistantTime = getTimestamp();
const assistantStartTime = Date.now();
streamingMsgIdRef.current = messageListRef.current.addMessage("assistant", "", {
time: assistantTime,
startTime: assistantStartTime,
streaming: true,
});

let committedContentRef = { current: "" };
let committedReasoning = "";
let lastToolCallDisplay = "";
let toolCallCount = 0;
let todoStatusLines = "";

// Set up abort controller for this stream
Expand Down Expand Up @@ -296,6 +299,7 @@ export default function App({
committedReasoning,
lastToolCallDisplay,
todoStatusLines,
toolCallCount,
);

// Persist assistant response to session state
Expand Down Expand Up @@ -378,14 +382,17 @@ export default function App({
}

const assistantTime = getTimestamp();
const assistantStartTime = Date.now();
streamingMsgIdRef.current = messageListRef.current.addMessage("assistant", "", {
time: assistantTime,
startTime: assistantStartTime,
streaming: true,
});

let committedContentRef = { current: "" };
let committedReasoning = "";
let lastToolCallDisplay = "";
let toolCallCount = 0;
let todoStatusLines = "";

// Set up abort controller for this stream
Expand Down Expand Up @@ -470,7 +477,13 @@ export default function App({
sessionState.addExchange({ role: "user", content: text });
}

finalizeStreaming(responseContent, committedReasoning, lastToolCallDisplay, todoStatusLines);
finalizeStreaming(
responseContent,
committedReasoning,
lastToolCallDisplay,
todoStatusLines,
toolCallCount,
);

// Persist assistant message and recalculate context
if (sessionState) {
Expand Down Expand Up @@ -702,65 +715,66 @@ export default function App({
});

if (event.type === "message") {
committedContentRef.current = (committedContentRef.current || "") + event.text;
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
content: committedContentRef.current + (config?.tui?.cursorChar || "\u2588"),
streaming: true,
});
if (onTextReceived) onTextReceived();
// streamMode "events" carries content in data.content, not data.text
const msgText =
event.data?.content && typeof event.data.content === "string"
? event.data.content
: (event.data?.content?.text ?? event.text ?? "");
if (msgText) {
committedContentRef.current = (committedContentRef.current || "") + msgText;
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
content: committedContentRef.current + (config?.tui?.cursorChar || "\u2588"),
streaming: true,
});
if (onTextReceived) onTextReceived();
}
}

// Handle on_chat_model_stream — accumulate content and reasoning
if (event.type === "on_chat_model_stream") {
if (event.data?.chunk?.content) {
committedContentRef.current =
(committedContentRef.current || "") + event.data.chunk.content;
// deepagents streamEvents v2 chunks are AIMessageChunk objects
const chunk = event.data?.chunk;
const content = chunk?.content ?? "";
if (content) {
committedContentRef.current = (committedContentRef.current || "") + content;
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
content: committedContentRef.current + (config?.tui?.cursorChar || "\u2588"),
streaming: true,
});
}
if (event.data?.chunk?.reasoning) {
committedReasoningRef.current =
(committedReasoningRef.current || "") + event.data.chunk.reasoning;
// Count tool calls from tool_call_chunks (streaming) and tool_calls (final)
if (chunk?.tool_call_chunks?.length) {
toolCallCount += chunk.tool_call_chunks.length;
for (const tc of chunk.tool_call_chunks) {
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
activeToolCall: {
name: tc.name,
status: "running",
},
});
}
}
if (chunk?.tool_calls?.length) {
toolCallCount += chunk.tool_calls.length;
}
if (chunk?.reasoning) {
committedReasoningRef.current = (committedReasoningRef.current || "") + chunk.reasoning;
}
}

// Handle on_tool_start — set activeToolCall
if (event.type === "on_tool_start") {
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
activeToolCall: {
name: event.name,
input: event.data?.input,
status: "running",
},
});
}

// Handle on_tool_end — clear activeToolCall, set toolCallDisplay
if (event.type === "on_tool_end") {
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
activeToolCall: null,
});
if (event.data?.output) {
// Handle on_chat_model_end — finalize tool call display
if (event.type === "on_chat_model_end") {
const output = event.data?.output;
if (output?.kwargs?.tool_calls?.length) {
lastToolCallDisplayRef.current =
(lastToolCallDisplayRef.current || "") + event.data.output;
(lastToolCallDisplayRef.current || "") +
output.kwargs.tool_calls.map((tc) => `[${tc.name}]`).join(" ");
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
toolCallDisplay: lastToolCallDisplayRef.current,
activeToolCall: null,
});
}
}

// Handle on_tool_error — set activeToolCall with error
if (event.type === "on_tool_error") {
messageListRef.current?.updateMessage(streamingMsgIdRef.current, {
activeToolCall: {
name: event.name,
error: event.data?.error,
status: "error",
},
});
}
} catch (cbErr) {
logger.debug(`[streaming] callback error: ${cbErr.message}`);
}
Expand All @@ -773,13 +787,19 @@ export default function App({
* @param {string} committedReasoning - Accumulated reasoning content
* @param {string} lastToolCallDisplay - Tool call display text
* @param {string} todoStatusLines - Todo status lines
* @param {number} [toolCallCount] - Number of tool calls made during the turn
*/
const finalizeStreaming = (
responseContent,
committedReasoning,
lastToolCallDisplay,
todoStatusLines,
toolCallCount,
) => {
const msgData = messageListRef.current?.getMessageData(streamingMsgIdRef.current);
const startTime = msgData?.startTime;
const durationMs = startTime ? Date.now() - startTime : 0;

const updates = {
content: responseContent,
reasoningContent: committedReasoning || undefined,
Expand All @@ -799,6 +819,12 @@ export default function App({
updates.toolCallDisplay = todoStatusLines;
}
}
if (toolCallCount > 0) {
updates.toolCallCount = toolCallCount;
}
if (durationMs > 0) {
updates.turnDurationMs = durationMs;
}
messageListRef.current?.updateMessage(streamingMsgIdRef.current, updates);
};

Expand Down
Loading