From 03e3fc071e1b98ef659e294ee3a7d65b36587364 Mon Sep 17 00:00:00 2001 From: neubig Date: Sun, 30 Aug 2026 19:32:05 +0000 Subject: [PATCH] chore: move TypeScript client to software-agent-sdk Co-authored-by: openhands --- .agent/skills/frontend.md | 548 - .github/dependabot.yml | 31 - .github/pull_request_template.md | 43 - .github/release.yml | 14 - .../scripts/post-endpoint-audit-report.cjs | 121 - .github/workflows/README-RELEASE.md | 140 - .../workflows/agent-server-sdk-main-audit.yml | 200 - .github/workflows/ci.yml | 180 - .github/workflows/endpoint-audit.yml | 61 - .github/workflows/integration-tests.yml | 215 - .github/workflows/npm-publish.yml | 121 - .github/workflows/pr.yml | 21 - .github/workflows/publish-github-packages.yml | 76 - .github/workflows/release-ready.yml | 25 - .github/workflows/release.yml | 22 - .gitignore | 95 - .openhands/pre-commit.sh | 10 - .openhands/setup.sh | 18 - .pr/live_aggregate_clients.mjs | 175 - .prettierrc | 8 - .release-please-manifest.json | 3 - AGENTS.md | 472 - CODE_OF_CONDUCT.md | 158 - CONTRIBUTING.md | 78 - LICENSE | 21 - PUBLISHING.md | 127 - README.md | 561 +- VIBE.md | 23 - babel.config.cjs | 5 - config/public-type-budget.json | 541 - docs/public-type-budget.md | 31 - endpoint-audit.config.json | 56 - eslint.config.mjs | 30 - example/README.md | 94 - example/index.html | 13 - example/package-lock.json | 4750 ---- example/package.json | 33 - example/postcss.config.js | 6 - example/src/App.tsx | 42 - .../src/components/ConversationManager.tsx | 724 - example/src/components/ServerStatus.tsx | 177 - example/src/components/SettingsModal.tsx | 217 - example/src/contexts/SettingsContext.tsx | 73 - example/src/index.css | 14 - example/src/main.tsx | 13 - example/src/utils/serverStatus.ts | 135 - example/src/utils/settings.ts | 86 - example/tailwind.config.js | 11 - example/tsconfig.json | 25 - example/tsconfig.node.json | 11 - example/vite.config.ts | 24 - examples/basic-usage.ts | 126 - examples/local-agent/README.md | 184 - examples/local-agent/index.html | 15 - examples/local-agent/package-lock.json | 2895 -- examples/local-agent/package.json | 23 - examples/local-agent/src/App.tsx | 153 - .../src/components/AgentChatInterface.tsx | 454 - .../local-agent/src/components/AuthScreen.tsx | 109 - .../src/components/SettingsModal.tsx | 91 - examples/local-agent/src/main.tsx | 39 - examples/local-agent/src/styles.css | 603 - examples/local-agent/src/vite-env.d.ts | 1 - examples/local-agent/tsconfig.json | 24 - examples/local-agent/vite.config.ts | 17 - examples/local/README.md | 161 - examples/local/index.html | 15 - examples/local/package-lock.json | 2895 -- examples/local/package.json | 23 - examples/local/src/App.tsx | 154 - examples/local/src/components/AuthScreen.tsx | 108 - .../local/src/components/ChatInterface.tsx | 174 - .../local/src/components/SettingsModal.tsx | 91 - examples/local/src/main.tsx | 40 - examples/local/src/styles.css | 513 - examples/local/src/vite-env.d.ts | 1 - examples/local/tsconfig.json | 24 - examples/local/vite.config.ts | 17 - jest.config.cjs | 44 - jest.integration.config.cjs | 39 - package-lock.json | 7957 ------ package.json | 118 - release-please-config.json | 12 - scripts/agent-server-openapi.mjs | 146 - scripts/check-acp-drift.py | 83 - scripts/check-agent-server-api.mjs | 35 - scripts/check-public-type-budget.mjs | 417 - scripts/copy-json-assets.mjs | 39 - scripts/endpoint-audit.mjs | 248 - scripts/generate-agent-server-api.mjs | 56 - scripts/render-agent-server-drift-report.mjs | 54 - scripts/requirements-acp-check.txt | 6 - scripts/rewrite-relative-imports.mjs | 72 - scripts/smoke-test-git-dependency.mjs | 54 - scripts/summarize-agent-server-api.mjs | 90 - scripts/test-agent-server-api-tooling.mjs | 179 - scripts/test-endpoint-audit.mjs | 138 - src/__tests__/acp-providers.test.ts | 105 - .../agent-server-contract-types.test.ts | 132 - src/__tests__/api-clients.test.ts | 2721 -- src/__tests__/derive-switch-plan.test.ts | 211 - src/__tests__/device-flow-client.test.ts | 74 - src/__tests__/event-types.test.ts | 430 - src/__tests__/hooks.test.ts | 355 - src/__tests__/index.test.ts | 94 - .../api-compatibility.integration.test.ts | 467 - .../integration/bash.integration.test.ts | 96 - .../conversation.integration.test.ts | 634 - .../deterministic-api.integration.test.ts | 572 - .../integration/e2e.integration.test.ts | 607 - .../http-client.integration.test.ts | 297 - src/__tests__/integration/index.ts | 8 - src/__tests__/integration/setup.ts | 60 - src/__tests__/integration/test-config.ts | 69 - src/__tests__/integration/test-utils.ts | 158 - .../integration/websocket.integration.test.ts | 574 - .../integration/workspace.integration.test.ts | 394 - src/__tests__/llm-token-callbacks.test.ts | 182 - src/__tests__/mcp-settings-client.test.ts | 220 - src/__tests__/package-import.test.ts | 132 - src/__tests__/remote-state.test.ts | 108 - src/__tests__/remote-workspace-git.test.ts | 74 - src/__tests__/secret-registry.test.ts | 331 - src/__tests__/security.test.ts | 319 - src/__tests__/stuck-detector.test.ts | 318 - src/__tests__/test-utils.test.ts | 180 - src/__tests__/workspace-session.test.ts | 102 - src/agent/agent.ts | 45 - src/agent/index.ts | 1 - src/client/agent-profiles-client.ts | 155 - src/client/agent-server-compatibility.ts | 164 - src/client/bash-client.ts | 123 - src/client/cloud-client.ts | 874 - src/client/conversation-client.ts | 395 - src/client/desktop-client.ts | 35 - src/client/device-flow-client.ts | 250 - src/client/file-client.ts | 110 - src/client/hooks-client.ts | 38 - src/client/http-client.ts | 369 - src/client/llm-client.ts | 92 - src/client/mcp-client.ts | 103 - src/client/meta-profiles-client.ts | 77 - src/client/openhands-client.ts | 213 - src/client/plugins-client.ts | 87 - src/client/profiles-client.ts | 117 - src/client/server-client.ts | 56 - src/client/settings-client.ts | 199 - src/client/shared-client.ts | 58 - src/client/skills-client.ts | 93 - src/client/sub-agents-client.ts | 45 - src/client/tool-client.ts | 32 - src/client/vscode-client.ts | 48 - src/client/workspaces-client.ts | 95 - src/clients.ts | 160 - src/conversation/base.ts | 167 - src/conversation/conversation-manager.ts | 418 - src/conversation/conversation.ts | 139 - src/conversation/local-conversation.ts | 979 - src/conversation/remote-conversation.ts | 485 - src/conversation/remote-state.ts | 245 - src/conversation/secret-registry.ts | 286 - src/conversation/stuck-detector.ts | 414 - src/events/bash-websocket-client.ts | 163 - src/events/remote-events-list.ts | 196 - src/events/types.ts | 258 - src/events/websocket-client.ts | 175 - src/generated/agent-server-schema.ts | 23411 ---------------- src/hooks/config.ts | 236 - src/hooks/index.ts | 34 - src/hooks/types.ts | 92 - src/index.ts | 577 - src/llm/base.ts | 260 - src/llm/index.ts | 32 - src/llm/llm.ts | 114 - src/llm/openrouter-llm.ts | 414 - src/models/acp-providers.json | 150 - src/models/acp.ts | 134 - src/models/agent-profile.ts | 154 - src/models/agent-server-api.ts | 41 - src/models/api.ts | 664 - src/models/conversation.ts | 246 - src/models/mcp-settings.ts | 50 - src/models/workspace.ts | 95 - src/profiles/derive-switch-plan.ts | 161 - src/prompts/index.ts | 12 - src/prompts/system-prompt.ts | 207 - src/security/confirmation-policy.ts | 153 - src/security/index.ts | 25 - src/security/security-analyzer.ts | 268 - src/types/base.ts | 148 - src/workspace/base.ts | 154 - src/workspace/local-workspace.ts | 152 - src/workspace/remote-workspace.ts | 316 - src/workspace/workspace.ts | 129 - tsconfig.json | 27 - 195 files changed, 1 insertion(+), 75853 deletions(-) delete mode 100644 .agent/skills/frontend.md delete mode 100644 .github/dependabot.yml delete mode 100644 .github/pull_request_template.md delete mode 100644 .github/release.yml delete mode 100644 .github/scripts/post-endpoint-audit-report.cjs delete mode 100644 .github/workflows/README-RELEASE.md delete mode 100644 .github/workflows/agent-server-sdk-main-audit.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/endpoint-audit.yml delete mode 100644 .github/workflows/integration-tests.yml delete mode 100644 .github/workflows/npm-publish.yml delete mode 100644 .github/workflows/pr.yml delete mode 100644 .github/workflows/publish-github-packages.yml delete mode 100644 .github/workflows/release-ready.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .gitignore delete mode 100755 .openhands/pre-commit.sh delete mode 100755 .openhands/setup.sh delete mode 100644 .pr/live_aggregate_clients.mjs delete mode 100644 .prettierrc delete mode 100644 .release-please-manifest.json delete mode 100644 AGENTS.md delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 LICENSE delete mode 100644 PUBLISHING.md delete mode 100644 VIBE.md delete mode 100644 babel.config.cjs delete mode 100644 config/public-type-budget.json delete mode 100644 docs/public-type-budget.md delete mode 100644 endpoint-audit.config.json delete mode 100644 eslint.config.mjs delete mode 100644 example/README.md delete mode 100644 example/index.html delete mode 100644 example/package-lock.json delete mode 100644 example/package.json delete mode 100644 example/postcss.config.js delete mode 100644 example/src/App.tsx delete mode 100644 example/src/components/ConversationManager.tsx delete mode 100644 example/src/components/ServerStatus.tsx delete mode 100644 example/src/components/SettingsModal.tsx delete mode 100644 example/src/contexts/SettingsContext.tsx delete mode 100644 example/src/index.css delete mode 100644 example/src/main.tsx delete mode 100644 example/src/utils/serverStatus.ts delete mode 100644 example/src/utils/settings.ts delete mode 100644 example/tailwind.config.js delete mode 100644 example/tsconfig.json delete mode 100644 example/tsconfig.node.json delete mode 100644 example/vite.config.ts delete mode 100644 examples/basic-usage.ts delete mode 100644 examples/local-agent/README.md delete mode 100644 examples/local-agent/index.html delete mode 100644 examples/local-agent/package-lock.json delete mode 100644 examples/local-agent/package.json delete mode 100644 examples/local-agent/src/App.tsx delete mode 100644 examples/local-agent/src/components/AgentChatInterface.tsx delete mode 100644 examples/local-agent/src/components/AuthScreen.tsx delete mode 100644 examples/local-agent/src/components/SettingsModal.tsx delete mode 100644 examples/local-agent/src/main.tsx delete mode 100644 examples/local-agent/src/styles.css delete mode 100644 examples/local-agent/src/vite-env.d.ts delete mode 100644 examples/local-agent/tsconfig.json delete mode 100644 examples/local-agent/vite.config.ts delete mode 100644 examples/local/README.md delete mode 100644 examples/local/index.html delete mode 100644 examples/local/package-lock.json delete mode 100644 examples/local/package.json delete mode 100644 examples/local/src/App.tsx delete mode 100644 examples/local/src/components/AuthScreen.tsx delete mode 100644 examples/local/src/components/ChatInterface.tsx delete mode 100644 examples/local/src/components/SettingsModal.tsx delete mode 100644 examples/local/src/main.tsx delete mode 100644 examples/local/src/styles.css delete mode 100644 examples/local/src/vite-env.d.ts delete mode 100644 examples/local/tsconfig.json delete mode 100644 examples/local/vite.config.ts delete mode 100644 jest.config.cjs delete mode 100644 jest.integration.config.cjs delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 release-please-config.json delete mode 100644 scripts/agent-server-openapi.mjs delete mode 100644 scripts/check-acp-drift.py delete mode 100644 scripts/check-agent-server-api.mjs delete mode 100644 scripts/check-public-type-budget.mjs delete mode 100644 scripts/copy-json-assets.mjs delete mode 100644 scripts/endpoint-audit.mjs delete mode 100644 scripts/generate-agent-server-api.mjs delete mode 100644 scripts/render-agent-server-drift-report.mjs delete mode 100644 scripts/requirements-acp-check.txt delete mode 100644 scripts/rewrite-relative-imports.mjs delete mode 100644 scripts/smoke-test-git-dependency.mjs delete mode 100644 scripts/summarize-agent-server-api.mjs delete mode 100644 scripts/test-agent-server-api-tooling.mjs delete mode 100644 scripts/test-endpoint-audit.mjs delete mode 100644 src/__tests__/acp-providers.test.ts delete mode 100644 src/__tests__/agent-server-contract-types.test.ts delete mode 100644 src/__tests__/api-clients.test.ts delete mode 100644 src/__tests__/derive-switch-plan.test.ts delete mode 100644 src/__tests__/device-flow-client.test.ts delete mode 100644 src/__tests__/event-types.test.ts delete mode 100644 src/__tests__/hooks.test.ts delete mode 100644 src/__tests__/index.test.ts delete mode 100644 src/__tests__/integration/api-compatibility.integration.test.ts delete mode 100644 src/__tests__/integration/bash.integration.test.ts delete mode 100644 src/__tests__/integration/conversation.integration.test.ts delete mode 100644 src/__tests__/integration/deterministic-api.integration.test.ts delete mode 100644 src/__tests__/integration/e2e.integration.test.ts delete mode 100644 src/__tests__/integration/http-client.integration.test.ts delete mode 100644 src/__tests__/integration/index.ts delete mode 100644 src/__tests__/integration/setup.ts delete mode 100644 src/__tests__/integration/test-config.ts delete mode 100644 src/__tests__/integration/test-utils.ts delete mode 100644 src/__tests__/integration/websocket.integration.test.ts delete mode 100644 src/__tests__/integration/workspace.integration.test.ts delete mode 100644 src/__tests__/llm-token-callbacks.test.ts delete mode 100644 src/__tests__/mcp-settings-client.test.ts delete mode 100644 src/__tests__/package-import.test.ts delete mode 100644 src/__tests__/remote-state.test.ts delete mode 100644 src/__tests__/remote-workspace-git.test.ts delete mode 100644 src/__tests__/secret-registry.test.ts delete mode 100644 src/__tests__/security.test.ts delete mode 100644 src/__tests__/stuck-detector.test.ts delete mode 100644 src/__tests__/test-utils.test.ts delete mode 100644 src/__tests__/workspace-session.test.ts delete mode 100644 src/agent/agent.ts delete mode 100644 src/agent/index.ts delete mode 100644 src/client/agent-profiles-client.ts delete mode 100644 src/client/agent-server-compatibility.ts delete mode 100644 src/client/bash-client.ts delete mode 100644 src/client/cloud-client.ts delete mode 100644 src/client/conversation-client.ts delete mode 100644 src/client/desktop-client.ts delete mode 100644 src/client/device-flow-client.ts delete mode 100644 src/client/file-client.ts delete mode 100644 src/client/hooks-client.ts delete mode 100644 src/client/http-client.ts delete mode 100644 src/client/llm-client.ts delete mode 100644 src/client/mcp-client.ts delete mode 100644 src/client/meta-profiles-client.ts delete mode 100644 src/client/openhands-client.ts delete mode 100644 src/client/plugins-client.ts delete mode 100644 src/client/profiles-client.ts delete mode 100644 src/client/server-client.ts delete mode 100644 src/client/settings-client.ts delete mode 100644 src/client/shared-client.ts delete mode 100644 src/client/skills-client.ts delete mode 100644 src/client/sub-agents-client.ts delete mode 100644 src/client/tool-client.ts delete mode 100644 src/client/vscode-client.ts delete mode 100644 src/client/workspaces-client.ts delete mode 100644 src/clients.ts delete mode 100644 src/conversation/base.ts delete mode 100644 src/conversation/conversation-manager.ts delete mode 100644 src/conversation/conversation.ts delete mode 100644 src/conversation/local-conversation.ts delete mode 100644 src/conversation/remote-conversation.ts delete mode 100644 src/conversation/remote-state.ts delete mode 100644 src/conversation/secret-registry.ts delete mode 100644 src/conversation/stuck-detector.ts delete mode 100644 src/events/bash-websocket-client.ts delete mode 100644 src/events/remote-events-list.ts delete mode 100644 src/events/types.ts delete mode 100644 src/events/websocket-client.ts delete mode 100644 src/generated/agent-server-schema.ts delete mode 100644 src/hooks/config.ts delete mode 100644 src/hooks/index.ts delete mode 100644 src/hooks/types.ts delete mode 100644 src/index.ts delete mode 100644 src/llm/base.ts delete mode 100644 src/llm/index.ts delete mode 100644 src/llm/llm.ts delete mode 100644 src/llm/openrouter-llm.ts delete mode 100644 src/models/acp-providers.json delete mode 100644 src/models/acp.ts delete mode 100644 src/models/agent-profile.ts delete mode 100644 src/models/agent-server-api.ts delete mode 100644 src/models/api.ts delete mode 100644 src/models/conversation.ts delete mode 100644 src/models/mcp-settings.ts delete mode 100644 src/models/workspace.ts delete mode 100644 src/profiles/derive-switch-plan.ts delete mode 100644 src/prompts/index.ts delete mode 100644 src/prompts/system-prompt.ts delete mode 100644 src/security/confirmation-policy.ts delete mode 100644 src/security/index.ts delete mode 100644 src/security/security-analyzer.ts delete mode 100644 src/types/base.ts delete mode 100644 src/workspace/base.ts delete mode 100644 src/workspace/local-workspace.ts delete mode 100644 src/workspace/remote-workspace.ts delete mode 100644 src/workspace/workspace.ts delete mode 100644 tsconfig.json diff --git a/.agent/skills/frontend.md b/.agent/skills/frontend.md deleted file mode 100644 index 46889ae8..00000000 --- a/.agent/skills/frontend.md +++ /dev/null @@ -1,548 +0,0 @@ -# Skill: React Chat Frontend with OpenHands TypeScript Client - -Build a React chat frontend that connects to an OpenHands agent server, sends messages, and displays a live conversation including all event types. - -## Basic structure -You will probably want the following features: -* A settings modal/page where the user can set: - * host and password for agent-server - * LLM model and key -* a chat input -* the ability to display both messages and events from the agent - * events typically come in action/observation pairs. it's usually best to only show one UI element per pair, depending on status - * events can contain a lot of information. good to make the display collapsed by default, with a small summary, but expandable to see full details - * condensation events should get displayed like other events -* a list of conversations the user can navigate between - * there are APIs for automatically setting a conversation title after the first user message is sent, use them - -## Packages - -```bash -npm install @openhands/typescript-client -# or from local repo: -npm install /path/to/typescript-client -``` - -Peer deps: `react`, `react-dom` (18+). No extra WebSocket library needed in browsers. - ---- - -## 1. Core Concepts - -- **`Conversation`** — creates/resumes a conversation on the agent server -- **`Agent`** — LLM config (model + api_key) -- **`Workspace`** — points at the agent server host -- **`WebSocketCallbackClient`** — streams events in real-time via WS -- Events arrive with a `kind` discriminator field; render each kind differently - -The agent server WebSocket endpoint is: -``` -ws[s]:///sockets/events/[?session_api_key=] -``` - ---- - -## 2. Minimal Hook: `useAgentConversation` - -```tsx -import { useEffect, useRef, useState, useCallback } from 'react'; -import { - Conversation, - Agent, - Workspace, - ConversationExecutionStatus, -} from '@openhands/typescript-client'; -import type { Event } from '@openhands/typescript-client'; - -interface UseAgentConversationOptions { - serverUrl: string; // e.g. "http://localhost:8000" - model: string; // e.g. "claude-sonnet-4-6" - apiKey: string; // LLM API key - sessionKey?: string; // Agent server session key (if secured) -} - -export function useAgentConversation({ - serverUrl, - model, - apiKey, - sessionKey, -}: UseAgentConversationOptions) { - const [events, setEvents] = useState([]); - const [status, setStatus] = useState(null); - const [conversationId, setConversationId] = useState(null); - const convRef = useRef(null); - - // Append incoming events (WebSocket + initial load) - const onEvent = useCallback((event: Event) => { - setEvents(prev => [...prev, event]); - }, []); - - const start = useCallback(async (initialMessage: string) => { - const agent = new Agent({ llm: { model, api_key: apiKey } }); - const workspace = new Workspace({ - host: serverUrl, - workingDir: '/workspace', - apiKey: sessionKey, - }); - - const conv = new Conversation(agent, workspace, { callback: onEvent }); - convRef.current = conv; - - await conv.start({ initialMessage }); - setConversationId(conv.id); - - // Stream events live - await conv.startWebSocketClient(); - - // Kick the agent - await conv.run(); - - setStatus(await conv.state.getAgentStatus()); - }, [serverUrl, model, apiKey, sessionKey, onEvent]); - - const sendMessage = useCallback(async (text: string) => { - const conv = convRef.current; - if (!conv) return; - await conv.sendMessage(text); - await conv.run(); - }, []); - - const pause = useCallback(async () => { - await convRef.current?.pause(); - }, []); - - // Poll status while running - useEffect(() => { - if (status !== ConversationExecutionStatus.RUNNING) return; - const t = setInterval(async () => { - const s = await convRef.current?.state.getAgentStatus(); - if (s) setStatus(s); - }, 1500); - return () => clearInterval(t); - }, [status]); - - // Cleanup - useEffect(() => { - return () => { convRef.current?.stopWebSocketClient(); }; - }, []); - - return { events, status, conversationId, start, sendMessage, pause }; -} -``` - ---- - -## 3. Event Renderer - -Handle every `kind` the server can emit. The `kind` field is the discriminator. - -```tsx -import type { Event } from '@openhands/typescript-client'; - -interface EventBubbleProps { event: Event } - -export function EventBubble({ event }: EventBubbleProps) { - switch (event.kind) { - - // ── Visible chat messages ───────────────────────────────────────────── - case 'MessageEvent': { - const msg = (event as any).llm_message; - const role: string = msg?.role ?? (event as any).source ?? 'unknown'; - const text = extractText(msg?.content); - const isUser = role === 'user'; - return ( -
- {isUser ? 'You' : 'Agent'} -

{text}

-
- ); - } - - // ── Tool call the agent is making ───────────────────────────────────── - case 'ActionEvent': { - const e = event as any; - return ( -
- - Tool call: {e.tool_name} - -
{JSON.stringify(e.action, null, 2)}
- {e.thought &&
{extractThought(e.thought)}
} -
- ); - } - - // ── Result of a tool call ───────────────────────────────────────────── - case 'ObservationEvent': { - const e = event as any; - const content = - typeof e.observation === 'string' - ? e.observation - : JSON.stringify(e.observation, null, 2); - return ( -
- Result: {e.tool_name} -
{content}
-
- ); - } - - // ── Scaffold error (sent to LLM) ────────────────────────────────────── - case 'AgentErrorEvent': { - const e = event as any; - return ( -
- Agent error ({e.tool_name}): {e.error} -
- ); - } - - // ── Conversation-level failure (NOT sent to LLM) ────────────────────── - case 'ConversationErrorEvent': { - const e = event as any; - return ( -
- Error [{e.code}]: {e.detail} -
- ); - } - - // ── User/hook rejection ─────────────────────────────────────────────── - case 'UserRejectObservation': { - const e = event as any; - return ( -
- Action {e.tool_name} rejected by {e.rejection_source}: - {' '}{e.rejection_reason} -
- ); - } - - // ── Execution paused ────────────────────────────────────────────────── - case 'PauseEvent': - return
Agent paused
; - - // ── Condensation (history compacted) ───────────────────────────────── - case 'Condensation': { - const e = event as any; - return ( -
- Context condensed ({e.forgotten_event_ids?.length ?? 0} events removed) - {e.summary &&

{e.summary}

} -
- ); - } - - case 'CondensationSummaryEvent': { - const e = event as any; - return ( -
- Summary: {e.summary} -
- ); - } - - // ── State change (use for status badge) ─────────────────────────────── - case 'ConversationStateUpdateEvent': { - const e = event as any; - if (e.key === 'execution_status') { - return
Status → {String(e.value)}
; - } - return null; // skip noisy state updates - } - - // ── System prompt (usually hide in UI) ──────────────────────────────── - case 'SystemPromptEvent': - return null; - - // ── Token IDs (VLLM internal, usually hide) ─────────────────────────── - case 'TokenEvent': - return null; - - // ── Unknown / future events ─────────────────────────────────────────── - default: - return ( -
- Event: {event.kind} -
{JSON.stringify(event, null, 2)}
-
- ); - } -} - -// Helpers -function extractText(content: unknown): string { - if (!content) return ''; - if (typeof content === 'string') return content; - if (Array.isArray(content)) { - return content - .filter((c: any) => c.type === 'text') - .map((c: any) => c.text ?? '') - .join('\n'); - } - return JSON.stringify(content); -} - -function extractThought(thought: unknown): string { - if (!thought) return ''; - if (typeof thought === 'string') return thought; - if (Array.isArray(thought)) { - return (thought as any[]) - .filter(t => t.type === 'text') - .map(t => t.text ?? '') - .join('\n'); - } - return ''; -} -``` - ---- - -## 4. Chat Shell Component - -```tsx -import { useState, useRef, useEffect } from 'react'; -import { useAgentConversation } from './useAgentConversation'; -import { EventBubble } from './EventBubble'; - -export function ChatApp() { - const [input, setInput] = useState(''); - const [started, setStarted] = useState(false); - const scrollRef = useRef(null); - - const { events, status, start, sendMessage, pause } = useAgentConversation({ - serverUrl: 'http://localhost:8000', - model: 'claude-sonnet-4-6', - apiKey: process.env.REACT_APP_API_KEY ?? '', - }); - - // Auto-scroll to bottom - useEffect(() => { - scrollRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [events]); - - const handleSend = async () => { - const text = input.trim(); - if (!text) return; - setInput(''); - if (!started) { - setStarted(true); - await start(text); - } else { - await sendMessage(text); - } - }; - - return ( -
-
-

OpenHands Chat

- - {status ?? 'idle'} - - {status === 'running' && ( - - )} -
- -
- {events.map((evt, i) => ( - - ))} -
-
- -
-