Implement tracking of outstanding LLM and Tool calls
This commit implements a listener pattern between ant.convo and the Agent for tracking outstanding calls.
* Added fields to the Agent struct to track outstanding LLM calls and Tool calls
* Implemented the listener methods to properly track and update these fields
* Added methods to retrieve the counts and names
* Updated the State struct in loophttp.go to expose this information
* Added a unit test to verify the tracking functionality
* Created UI components with lightbulb and wrench icons to display call status
* Added numerical indicators that always show when there are active calls
Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/webui/src/web-components/sketch-call-status.test.ts b/webui/src/web-components/sketch-call-status.test.ts
new file mode 100644
index 0000000..9706319
--- /dev/null
+++ b/webui/src/web-components/sketch-call-status.test.ts
@@ -0,0 +1,145 @@
+import { test, expect } from "@sand4rt/experimental-ct-web";
+import { SketchCallStatus } from "./sketch-call-status";
+
+test("initializes with zero LLM calls and empty tool calls by default", async ({
+ mount,
+}) => {
+ const component = await mount(SketchCallStatus, {});
+
+ // Check properties via component's evaluate method
+ const llmCalls = await component.evaluate(
+ (el: SketchCallStatus) => el.llmCalls,
+ );
+ expect(llmCalls).toBe(0);
+
+ const toolCalls = await component.evaluate(
+ (el: SketchCallStatus) => el.toolCalls,
+ );
+ expect(toolCalls).toEqual([]);
+
+ // Check that badges are not shown
+ await expect(component.locator(".count-badge")).toHaveCount(0);
+});
+
+test("displays the correct state for active LLM calls", async ({ mount }) => {
+ const component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 3,
+ toolCalls: [],
+ },
+ });
+
+ // Check that LLM indicator is active
+ await expect(component.locator(".llm-indicator")).toHaveClass(/active/);
+
+ // Check that badge shows correct count
+ await expect(component.locator(".llm-indicator .count-badge")).toHaveText(
+ "3",
+ );
+
+ // Check that tool indicator is not active
+ await expect(component.locator(".tool-indicator")).not.toHaveClass(/active/);
+});
+
+test("displays the correct state for active tool calls", async ({ mount }) => {
+ const component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 0,
+ toolCalls: ["bash", "think"],
+ },
+ });
+
+ // Check that tool indicator is active
+ await expect(component.locator(".tool-indicator")).toHaveClass(/active/);
+
+ // Check that badge shows correct count
+ await expect(component.locator(".tool-indicator .count-badge")).toHaveText(
+ "2",
+ );
+
+ // Check that LLM indicator is not active
+ await expect(component.locator(".llm-indicator")).not.toHaveClass(/active/);
+});
+
+test("displays both indicators when both call types are active", async ({
+ mount,
+}) => {
+ const component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 1,
+ toolCalls: ["patch"],
+ },
+ });
+
+ // Check that both indicators are active
+ await expect(component.locator(".llm-indicator")).toHaveClass(/active/);
+ await expect(component.locator(".tool-indicator")).toHaveClass(/active/);
+
+ // Check that badges show correct counts
+ await expect(component.locator(".llm-indicator .count-badge")).toHaveText(
+ "1",
+ );
+ await expect(component.locator(".tool-indicator .count-badge")).toHaveText(
+ "1",
+ );
+});
+
+test("has correct tooltip text for LLM calls", async ({ mount }) => {
+ // Test with singular
+ let component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 1,
+ toolCalls: [],
+ },
+ });
+
+ await expect(component.locator(".llm-indicator")).toHaveAttribute(
+ "title",
+ "1 LLM call in progress",
+ );
+
+ await component.unmount();
+
+ // Test with plural
+ component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 2,
+ toolCalls: [],
+ },
+ });
+
+ await expect(component.locator(".llm-indicator")).toHaveAttribute(
+ "title",
+ "2 LLM calls in progress",
+ );
+});
+
+test("has correct tooltip text for tool calls", async ({ mount }) => {
+ // Test with singular
+ let component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 0,
+ toolCalls: ["bash"],
+ },
+ });
+
+ await expect(component.locator(".tool-indicator")).toHaveAttribute(
+ "title",
+ "1 tool call in progress: bash",
+ );
+
+ await component.unmount();
+
+ // Test with plural
+ component = await mount(SketchCallStatus, {
+ props: {
+ llmCalls: 0,
+ toolCalls: ["bash", "think"],
+ },
+ });
+
+ await expect(component.locator(".tool-indicator")).toHaveAttribute(
+ "title",
+ "2 tool calls in progress: bash, think",
+ );
+});