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",
+  );
+});