diff --git a/packages/web-ui/example/index.html b/packages/web-ui/example/index.html
index e462448..66edc66 100644
--- a/packages/web-ui/example/index.html
+++ b/packages/web-ui/example/index.html
@@ -1,10 +1,12 @@
+
+
- Pi Web UI - Example
-
+ Agent JAE
+
diff --git a/packages/web-ui/example/public/apple-touch-icon.png b/packages/web-ui/example/public/apple-touch-icon.png
new file mode 100644
index 0000000..11bd06c
Binary files /dev/null and b/packages/web-ui/example/public/apple-touch-icon.png differ
diff --git a/packages/web-ui/example/public/favicon.ico b/packages/web-ui/example/public/favicon.ico
new file mode 100644
index 0000000..21648cf
Binary files /dev/null and b/packages/web-ui/example/public/favicon.ico differ
diff --git a/packages/web-ui/example/src/main.ts b/packages/web-ui/example/src/main.ts
index 4d3af13..d330e2e 100644
--- a/packages/web-ui/example/src/main.ts
+++ b/packages/web-ui/example/src/main.ts
@@ -258,22 +258,38 @@ async function createAgent(initialState?: AgentState) {
currentModel = model.modelId || "llama-3.3-70b";
currentProvider = model.provider || "venice";
- // Listen for agent messages to track state
- agent.on("message", (msg: AgentMessage) => {
- if (!hasMessages && msg.role === "assistant") {
- hasMessages = true;
- renderApp();
- }
- // Track tool usage for mood and auto-open panels
- if (msg.role === "assistant" && msg.toolCalls) {
- for (const tc of msg.toolCalls) {
- if (tc.name === "browser" || tc.name === "web_fetch" || tc.name === "navigate") {
+ // Subscribe to agent events for UI state tracking
+ agent.subscribe((event) => {
+ const typing = document.querySelector("jae-typing-indicator") as JaeTypingIndicator;
+ const mood = document.querySelector("jae-mood-indicator") as JaeMoodIndicator;
+
+ switch (event.type) {
+ case "agent_start":
+ if (typing) typing.show("high");
+ break;
+
+ case "agent_end":
+ if (typing) typing.hide();
+ break;
+
+ case "turn_start":
+ if (typing) typing.show("high");
+ break;
+
+ case "turn_end":
+ if (typing) typing.hide();
+ break;
+
+ case "tool_execution_start":
+ if (typing) typing.show("medium");
+ // Auto-open panels based on tool usage
+ if (event.toolName === "browser" || event.toolName === "web_fetch" || event.toolName === "navigate") {
if (rightPanel !== "browser") {
rightPanel = "browser";
renderApp();
}
}
- if (tc.name === "bash") {
+ if (event.toolName === "bash") {
if (rightPanel !== "terminal") {
rightPanel = "terminal";
renderApp();
@@ -283,46 +299,46 @@ async function createAgent(initialState?: AgentState) {
});
}
}
- }
- }
- // Auto-learn from user messages
- if (msg.role === "user" && typeof msg.content === "string") {
- processMessageForMemory("user", msg.content);
- }
- // Update mood based on content
- const mood = document.querySelector("jae-mood-indicator") as JaeMoodIndicator;
- if (mood && msg.role === "assistant" && typeof msg.content === "string") {
- const text = msg.content.toLowerCase();
- if (text.includes("error") || text.includes("failed") || text.includes("cannot")) {
- mood.setMood("frustrated");
- } else if (text.includes("!") || text.includes("great") || text.includes("success") || text.includes("done")) {
- mood.setMood("excited");
- } else if (text.includes("warning") || text.includes("careful") || text.includes("caution")) {
- mood.setMood("warning");
- } else {
- mood.setMood("focused");
- }
- }
- });
+ break;
- agent.on("stateChange", (state: string) => {
- const typing = document.querySelector("jae-typing-indicator") as JaeTypingIndicator;
- if (typing) {
- if (state === "thinking" || state === "running") {
- typing.show("high");
- } else if (state === "tool_calling") {
- typing.show("medium");
- } else {
- typing.hide();
+ case "message_start": {
+ const msg = event.message;
+ if (!hasMessages && msg.role === "assistant") {
+ hasMessages = true;
+ renderApp();
+ }
+ // Auto-learn from user messages
+ if (msg.role === "user" && typeof msg.content === "string") {
+ processMessageForMemory("user", msg.content);
+ }
+ break;
}
- }
- });
- // Track cost
- agent.on("usage", (usage: { inputTokens: number; outputTokens: number; totalTokens: number }) => {
- const costEl = document.querySelector("jae-cost-tracker") as CostTracker;
- if (costEl && usage) {
- costEl.addUsage(usage.inputTokens || 0, usage.outputTokens || 0);
+ case "message_end": {
+ const msg = event.message;
+ // Update mood based on assistant content
+ if (mood && msg.role === "assistant" && typeof msg.content === "string") {
+ const text = msg.content.toLowerCase();
+ if (text.includes("error") || text.includes("failed") || text.includes("cannot")) {
+ mood.setMood("frustrated");
+ } else if (text.includes("!") || text.includes("great") || text.includes("success") || text.includes("done")) {
+ mood.setMood("excited");
+ } else if (text.includes("warning") || text.includes("careful") || text.includes("caution")) {
+ mood.setMood("warning");
+ } else {
+ mood.setMood("focused");
+ }
+ }
+ // Track cost from usage data on the message
+ const usage = (msg as any).usage;
+ if (usage) {
+ const costEl = document.querySelector("jae-cost-tracker") as CostTracker;
+ if (costEl) {
+ costEl.addUsage(usage.input || 0, usage.output || 0);
+ }
+ }
+ break;
+ }
}
});