feat: integrate Mock Service Worker (MSW) for API mocking and testing

Currently just used for demo page

- Added MSW as a dependency in package.json.
- Configured MSW in the demo HTML to start the worker and handle API requests.
- Created mockServiceWorker.js to manage service worker lifecycle and request handling.
- Implemented browser.ts to set up the MSW worker with defined request handlers.
- Developed handlers.ts to simulate API responses and manage application state for testing.
diff --git a/webui/src/web-components/demo/mocks/browser.ts b/webui/src/web-components/demo/mocks/browser.ts
new file mode 100644
index 0000000..bcd82e4
--- /dev/null
+++ b/webui/src/web-components/demo/mocks/browser.ts
@@ -0,0 +1,4 @@
+import { setupWorker } from "msw/browser";
+import { handlers } from "./handlers";
+
+export const worker = setupWorker(...handlers);
diff --git a/webui/src/web-components/demo/mocks/fixtures/dummy.ts b/webui/src/web-components/demo/mocks/fixtures/dummy.ts
new file mode 100644
index 0000000..5dcf509
--- /dev/null
+++ b/webui/src/web-components/demo/mocks/fixtures/dummy.ts
@@ -0,0 +1,372 @@
+import { AgentMessage, State } from "../../../../types";
+
+export const initialMessages: AgentMessage[] = [
+  {
+    type: "user",
+    end_of_turn: false,
+    content:
+      "add a line to dummy.txt. Doesn't matter what it is and don't bother running tests it's just a dummy repo. Please commit afterward",
+    timestamp: "2025-04-24T10:32:00.679873+01:00",
+    conversation_id: "",
+    idx: 0,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content:
+      "I'll help you add a line to dummy.txt and commit the change. Let me first check if this file exists and create it if needed.",
+    tool_calls: [
+      {
+        name: "title",
+        input: '{"title":"Add a line to dummy.txt and commit the change"}',
+        tool_call_id: "toolu_01HSQ8xb9Ba8oP1gJPv1eD2s",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:03.988439+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 4,
+      cache_creation_input_tokens: 2228,
+      cache_read_input_tokens: 0,
+      output_tokens: 92,
+      cost_usd: 0.009747,
+    },
+    start_time: "2025-04-24T10:32:00.680125+01:00",
+    end_time: "2025-04-24T10:32:03.988434+01:00",
+    elapsed: 3308355125,
+    idx: 1,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "title",
+    input: '{"title":"Add a line to dummy.txt and commit the change"}',
+    tool_result: "Title set to: Add a line to dummy.txt and commit the change",
+    tool_call_id: "toolu_01HSQ8xb9Ba8oP1gJPv1eD2s",
+    timestamp: "2025-04-24T10:32:03.988567+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:03.988553+01:00",
+    end_time: "2025-04-24T10:32:03.988566+01:00",
+    elapsed: 13500,
+    idx: 2,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content: "",
+    tool_calls: [
+      {
+        name: "bash",
+        input: '{"command":"ls -la"}',
+        tool_call_id: "toolu_01RtkhetY8bqC6U1QBfVbukS",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:06.146332+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 6,
+      cache_creation_input_tokens: 116,
+      cache_read_input_tokens: 2228,
+      output_tokens: 54,
+      cost_usd: 0.0019314,
+    },
+    start_time: "2025-04-24T10:32:04.007226+01:00",
+    end_time: "2025-04-24T10:32:06.146325+01:00",
+    elapsed: 2139128375,
+    idx: 3,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "bash",
+    input: '{"command":"ls -la"}',
+    tool_result:
+      "total 1712\ndrwxr-xr-x   29 pokey  staff     928 Apr 15 13:06 .\ndrwxr-xr-x  397 pokey  staff   12704 Apr 23 09:53 ..\n-rw-r--r--@   1 pokey  staff     292 Jan  8  2024 .editorconfig\ndrwxr-xr-x   17 pokey  staff     544 Apr 24 10:32 .git\ndrwxr-xr-x@   3 pokey  staff      96 Jan  8  2024 .github\n-rw-r--r--@   1 pokey  staff     777 Jan  8  2024 .gitignore\n-rw-r--r--@   1 pokey  staff       0 Apr 15 13:06 .PROMPT_DRAFT.md\n-rw-------    1 pokey  staff   11811 Oct  4  2022 .tags\n-rw-r--r--@   1 pokey  staff     762 Jan  8  2024 .travis.yml\ndrwxr-xr-x    3 pokey  staff      96 Apr  9 10:51 .vscode\n-rw-r--r--@   1 pokey  staff     156 Jan  8  2024 AUTHORS.rst\n-rw-r--r--@   1 pokey  staff    3249 Jan  8  2024 CONTRIBUTING.rst\ndrwxr-xr-x@  12 pokey  staff     384 Jan  8  2024 docs\n-rw-r--r--    1 pokey  staff    6644 Apr 23 15:31 dummy.txt\n-rw-r--r--@   1 pokey  staff      89 Jan  8  2024 HISTORY.rst\n-rw-r--r--@   1 pokey  staff    1070 Jan  8  2024 LICENSE\n-rw-r--r--@   1 pokey  staff    2303 Jan  8  2024 Makefile\n-rw-r--r--@   1 pokey  staff     264 Jan  8  2024 MANIFEST.in\n-rw-r--r--    1 pokey  staff  794413 Mar  7  2017 myVim.log\n-rw-r--r--@   1 pokey  staff    1043 Apr  8 12:21 README.rst\n-rw-r--r--@   1 pokey  staff     145 Jan  8  2024 requirements_dev.txt\n-rw-r--r--@   1 pokey  staff     342 Jan  8  2024 setup.cfg\n-rw-r--r--@   1 pokey  staff    1559 Jan  8  2024 setup.py\ndrwxr-xr-x    6 pokey  staff     192 Jan  8  2024 spaghetti\ndrwxr-xr-x    9 pokey  staff     288 Feb 26  2017 spaghetti.egg-info\ndrwxr-xr-x@   5 pokey  staff     160 Apr  4 19:07 tests\n-rw-r--r--@   1 pokey  staff     430 Jan  8  2024 tox.ini\n-rw-r--r--@   1 pokey  staff    3753 Jan  8  2024 travis_pypi_setup.py\ndrwxr-xr-x    2 pokey  staff      64 Mar 11  2017 UltiSnips\n",
+    tool_call_id: "toolu_01RtkhetY8bqC6U1QBfVbukS",
+    timestamp: "2025-04-24T10:32:06.176176+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:06.146414+01:00",
+    end_time: "2025-04-24T10:32:06.176175+01:00",
+    elapsed: 29760750,
+    idx: 4,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content:
+      "I see that dummy.txt already exists. Let me add a new line to it.",
+    tool_calls: [
+      {
+        name: "patch",
+        input:
+          '{"path":"/Users/pokey/src/spaghetti/dummy.txt","patches":[{"operation":"append_eof","newText":"\\nAdding a new dummy line here - requested change"}]}',
+        tool_call_id: "toolu_01KjY2a2MWhfvwy6Pmci8SX2",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:09.724348+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 6,
+      cache_creation_input_tokens: 970,
+      cache_read_input_tokens: 2344,
+      output_tokens: 141,
+      cost_usd: 0.0064737,
+    },
+    start_time: "2025-04-24T10:32:06.195746+01:00",
+    end_time: "2025-04-24T10:32:09.724335+01:00",
+    elapsed: 3528638042,
+    idx: 5,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "patch",
+    input:
+      '{"path":"/Users/pokey/src/spaghetti/dummy.txt","patches":[{"operation":"append_eof","newText":"\\nAdding a new dummy line here - requested change"}]}',
+    tool_result: "- Applied all patches\n",
+    tool_call_id: "toolu_01KjY2a2MWhfvwy6Pmci8SX2",
+    timestamp: "2025-04-24T10:32:09.728267+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:09.725039+01:00",
+    end_time: "2025-04-24T10:32:09.728265+01:00",
+    elapsed: 3225875,
+    idx: 6,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content: "Now let me commit this change:",
+    tool_calls: [
+      {
+        name: "bash",
+        input:
+          '{"command":"GIT_AUTHOR_NAME=\\"Pokey Rule\\" GIT_AUTHOR_EMAIL=\\"755842+pokey@users.noreply.github.com\\" git add dummy.txt && git commit -m \\"Add a new line to dummy.txt\\" -m \\"Co-Authored-By: sketch\\" -m \\"Add a line to dummy.txt. Doesn\'t matter what it is and don\'t bother running tests it\'s just a dummy repo.\\""}',
+        tool_call_id: "toolu_01NQXuuU4JdUHn29S35oGLxF",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:13.306331+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 6,
+      cache_creation_input_tokens: 146,
+      cache_read_input_tokens: 3314,
+      output_tokens: 160,
+      cost_usd: 0.0039597,
+    },
+    start_time: "2025-04-24T10:32:09.76053+01:00",
+    end_time: "2025-04-24T10:32:13.306203+01:00",
+    elapsed: 3545723083,
+    idx: 7,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "bash",
+    input:
+      '{"command":"GIT_AUTHOR_NAME=\\"Pokey Rule\\" GIT_AUTHOR_EMAIL=\\"755842+pokey@users.noreply.github.com\\" git add dummy.txt && git commit -m \\"Add a new line to dummy.txt\\" -m \\"Co-Authored-By: sketch\\" -m \\"Add a line to dummy.txt. Doesn\'t matter what it is and don\'t bother running tests it\'s just a dummy repo.\\""}',
+    tool_result:
+      "branchless: processing 2 updates: branch main, ref HEAD\nbranchless: processed commit: 898178a Add a new line to dummy.txt\n[main 898178a] Add a new line to dummy.txt\n 1 file changed, 2 insertions(+), 1 deletion(-)\n",
+    tool_call_id: "toolu_01NQXuuU4JdUHn29S35oGLxF",
+    timestamp: "2025-04-24T10:32:13.652683+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:13.307345+01:00",
+    end_time: "2025-04-24T10:32:13.652678+01:00",
+    elapsed: 345337875,
+    idx: 8,
+  },
+  {
+    type: "commit",
+    end_of_turn: false,
+    content: "",
+    commits: [
+      {
+        hash: "898178a0c6681ce2b641cbe8994c22ed8bbb7dd7",
+        subject: "Add a new line to dummy.txt",
+        body: "Co-Authored-By: sketch\n\nAdd a line to dummy.txt. Doesn't matter what it is and don't bother running tests it's just a dummy repo.",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:13.690825+01:00",
+    conversation_id: "",
+    idx: 9,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content: "",
+    tool_calls: [
+      {
+        name: "done",
+        input:
+          '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"not applicable","description":"The change was trivial - just adding a line to a dummy.txt file."}}}',
+        tool_call_id: "toolu_01YMBDmBoCcE5tfZvbXAgWEa",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:19.07007+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 6,
+      cache_creation_input_tokens: 244,
+      cache_read_input_tokens: 3460,
+      output_tokens: 241,
+      cost_usd: 0.005586,
+    },
+    start_time: "2025-04-24T10:32:13.74753+01:00",
+    end_time: "2025-04-24T10:32:19.070058+01:00",
+    elapsed: 5322601792,
+    idx: 10,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "done",
+    input:
+      '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"not applicable","description":"The change was trivial - just adding a line to a dummy.txt file."}}}',
+    tool_result:
+      "codereview tool has not been run for commit 898178a0c6681ce2b641cbe8994c22ed8bbb7dd7",
+    tool_error: true,
+    tool_call_id: "toolu_01YMBDmBoCcE5tfZvbXAgWEa",
+    timestamp: "2025-04-24T10:32:19.121365+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:19.070249+01:00",
+    end_time: "2025-04-24T10:32:19.121358+01:00",
+    elapsed: 51109583,
+    idx: 11,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content: "Let me run a code review as required:",
+    tool_calls: [
+      {
+        name: "codereview",
+        input: "{}",
+        tool_call_id: "toolu_01Fgg5vaURskyxGcbwHoBu7z",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:21.918149+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 7,
+      cache_creation_input_tokens: 253,
+      cache_read_input_tokens: 3704,
+      output_tokens: 54,
+      cost_usd: 0.0028909499999999998,
+    },
+    start_time: "2025-04-24T10:32:19.14049+01:00",
+    end_time: "2025-04-24T10:32:21.918133+01:00",
+    elapsed: 2777681917,
+    idx: 12,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "codereview",
+    input: "{}",
+    tool_result: "OK",
+    tool_call_id: "toolu_01Fgg5vaURskyxGcbwHoBu7z",
+    timestamp: "2025-04-24T10:32:22.516658+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:21.918261+01:00",
+    end_time: "2025-04-24T10:32:22.516654+01:00",
+    elapsed: 598402125,
+    idx: 13,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content: "Now let me try the done call again:",
+    tool_calls: [
+      {
+        name: "done",
+        input:
+          '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"yes","description":"Code review completed and no issues found. The change was trivial - just adding a line to a dummy.txt file."}}}',
+        tool_call_id: "toolu_016TBxz5Ma8y3if3rY4DZBXE",
+      },
+    ],
+    timestamp: "2025-04-24T10:32:26.926407+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 6,
+      cache_creation_input_tokens: 60,
+      cache_read_input_tokens: 3957,
+      output_tokens: 217,
+      cost_usd: 0.0046851,
+    },
+    start_time: "2025-04-24T10:32:22.534359+01:00",
+    end_time: "2025-04-24T10:32:26.926391+01:00",
+    elapsed: 4392093250,
+    idx: 14,
+  },
+  {
+    type: "tool",
+    end_of_turn: false,
+    content: "",
+    tool_name: "done",
+    input:
+      '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"yes","description":"Code review completed and no issues found. The change was trivial - just adding a line to a dummy.txt file."}}}',
+    tool_result:
+      "Please ask the user to review your work. Be concise - users are more likely to read shorter comments.",
+    tool_call_id: "toolu_016TBxz5Ma8y3if3rY4DZBXE",
+    timestamp: "2025-04-24T10:32:26.978771+01:00",
+    conversation_id: "37s-g6xg",
+    start_time: "2025-04-24T10:32:26.92661+01:00",
+    end_time: "2025-04-24T10:32:26.978758+01:00",
+    elapsed: 52149125,
+    idx: 15,
+  },
+  {
+    type: "agent",
+    end_of_turn: false,
+    content:
+      'I\'ve completed your request:\n\n1. Added a new line to dummy.txt: "Adding a new dummy line here - requested change"\n2. Created a git commit with the change using the specified author information\n3. Included "Co-Authored-By: sketch" in the commit message as required\n\nThe change has been successfully committed to the repository. Is there anything else you\'d like me to help with?',
+    timestamp: "2025-04-24T10:32:29.072661+01:00",
+    conversation_id: "37s-g6xg",
+    usage: {
+      input_tokens: 5,
+      cache_creation_input_tokens: 250,
+      cache_read_input_tokens: 4017,
+      output_tokens: 92,
+      cost_usd: 0.0035376,
+    },
+    start_time: "2025-04-24T10:32:26.99749+01:00",
+    end_time: "2025-04-24T10:32:29.072654+01:00",
+    elapsed: 2075193375,
+    turnDuration: 28393844125,
+    idx: 16,
+  },
+];
+
+export const initialState: State = {
+  message_count: initialMessages.length,
+  total_usage: {
+    start_time: "2025-04-24T10:32:00.679057+01:00",
+    messages: 8,
+    input_tokens: 46,
+    output_tokens: 1051,
+    cache_read_input_tokens: 23024,
+    cache_creation_input_tokens: 4267,
+    total_cost_usd: 0.038811450000000004,
+    tool_uses: {
+      bash: 2,
+      codereview: 1,
+      done: 2,
+      patch: 1,
+      title: 1,
+    },
+  },
+  initial_commit: "a6c5a08a451ef1082774a7affb6af58775e7bc16",
+  title: "Add a line to dummy.txt and commit the change",
+  hostname: "MacBook-Pro-9.local",
+  working_dir: "/Users/pokey/src/spaghetti",
+  os: "darwin",
+  git_origin: "git@github.com:pokey/spaghetti.git",
+  inside_hostname: "MacBook-Pro-9.local",
+  inside_os: "darwin",
+  inside_working_dir: "/Users/pokey/src/spaghetti",
+};
diff --git a/webui/src/web-components/demo/mocks/handlers.ts b/webui/src/web-components/demo/mocks/handlers.ts
new file mode 100644
index 0000000..c112efd
--- /dev/null
+++ b/webui/src/web-components/demo/mocks/handlers.ts
@@ -0,0 +1,63 @@
+import { http, HttpResponse, delay } from "msw";
+import { initialState, initialMessages } from "./fixtures/dummy";
+
+// Mock state updates for long-polling simulation
+let currentState = { ...initialState };
+const messages = [...initialMessages];
+const ADD_NEW_MESSAGES =
+  new URL(window.location.href).searchParams.get("addNewMessages") === "1";
+
+export const handlers = [
+  // Unified state endpoint that handles both regular and polling requests
+  http.get("*/state", async ({ request }) => {
+    const url = new URL(request.url);
+    const isPoll = url.searchParams.get("poll") === "true";
+
+    if (!isPoll) {
+      // Regular state request
+      return HttpResponse.json(currentState);
+    }
+
+    // This is a long-polling request
+    await delay(ADD_NEW_MESSAGES ? 2000 : 60000); // Simulate waiting for changes
+
+    if (ADD_NEW_MESSAGES) {
+      // Simulate adding new messages
+      messages.push({
+        type: "agent",
+        end_of_turn: false,
+        content: "Here's a message",
+        timestamp: "2025-04-24T10:32:29.072661+01:00",
+        conversation_id: "37s-g6xg",
+        usage: {
+          input_tokens: 5,
+          cache_creation_input_tokens: 250,
+          cache_read_input_tokens: 4017,
+          output_tokens: 92,
+          cost_usd: 0.0035376,
+        },
+        start_time: "2025-04-24T10:32:26.99749+01:00",
+        end_time: "2025-04-24T10:32:29.072654+01:00",
+        elapsed: 2075193375,
+        turnDuration: 28393844125,
+        idx: messages.length,
+      });
+
+      // Update the state with new messages
+      currentState = {
+        ...currentState,
+        message_count: messages.length,
+      };
+    }
+
+    return HttpResponse.json(currentState);
+  }),
+
+  // Messages endpoint
+  http.get("*/messages", ({ request }) => {
+    const url = new URL(request.url);
+    const startIndex = parseInt(url.searchParams.get("start") || "0");
+
+    return HttpResponse.json(messages.slice(startIndex));
+  }),
+];
diff --git a/webui/src/web-components/demo/sketch-app-shell.demo.html b/webui/src/web-components/demo/sketch-app-shell.demo.html
index 48fc100..8c4458b 100644
--- a/webui/src/web-components/demo/sketch-app-shell.demo.html
+++ b/webui/src/web-components/demo/sketch-app-shell.demo.html
@@ -5,7 +5,11 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>sketch coding assistant</title>
     <link rel="stylesheet" href="sketch-app-shell.css" />
-    <script src="../sketch-app-shell.ts" type="module"></script>
+    <script type="module">
+      const { worker } = await import("./mocks/browser");
+      await worker.start();
+      await import("../sketch-app-shell.ts");
+    </script>
   </head>
   <body>
     <sketch-app-shell></sketch-app-shell>