{"_id":"@amigo-llm/frontend","_rev":"2-1548eba2325d82b45ebbff62ee527a4c","name":"@amigo-llm/frontend","dist-tags":{"latest":"1.0.3"},"versions":{"0.0.2":{"name":"@amigo-llm/frontend","version":"0.0.2","_id":"@amigo-llm/frontend@0.0.2","maintainers":[{"name":"kb666","email":"kaiqingliu6@gmail.com"}],"dist":{"shasum":"c2a5f2c4095a083434578285d76fe4af1b31503d","tarball":"https://registry.npmjs.org/@amigo-llm/frontend/-/frontend-0.0.2.tgz","fileCount":71,"integrity":"sha512-Y6nA454rH3wzQwC94KgL948JZTUf667EwkjkCufjkJECVtdvNaqTmOUUySA2Wsq8ZojOBgB+a58UijmpbCSW8A==","signatures":[{"sig":"MEUCIQDElfsytpNfjVAS2z6B+KH5AEioWHcCXx1yNywLEFugtwIgJ8qTnlS9Nf4G730pCpGiJKwSaL4QUVHDahNSECXH7Cg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40223607},"type":"module","gitHead":"2cb05a98eb528001bd2f99301eee062246529898","scripts":{"dev":"bun run src/index.ts --watch","lint":"eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0","build":"bun run build.ts","start":"bun run src/index.ts","preview":"vite preview"},"_npmUser":{"name":"kb666","email":"kaiqingliu6@gmail.com"},"_npmVersion":"10.2.4","directories":{},"_nodeVersion":"20.11.1","dependencies":{"uuid":"^9.0.1","react":"^18.2.0","lodash":"^4.17.21","zustand":"^5.0.8","tippy.js":"^6.3.7","react-dom":"^18.2.0","streamdown":"^1.3.0","react-icons":"^4.11.0","lucide-react":"^0.544.0","@tiptap/react":"^2.1.0","react-hot-toast":"^2.6.0","@tiptap/suggestion":"^2.7.0","@tiptap/starter-kit":"^2.1.0","bun-plugin-tailwind":"^0.1.2","@tiptap/extension-mention":"^2.27.1","@tiptap/extension-placeholder":"^2.1.0"},"_hasShrinkwrap":false,"devDependencies":{"daisyui":"^5.1.25","typescript":"^5.0.2","@types/uuid":"^9.0.4","tailwindcss":"^4.1.17","@types/react":"^18.2.15","autoprefixer":"^10.4.15","@types/lodash":"^4.17.20","@amigo-llm/types":"workspace:*","@types/react-dom":"^18.2.7"},"_npmOperationalInternal":{"tmp":"tmp/frontend_0.0.2_1764752686633_0.27714568578941323","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@amigo-llm/frontend","version":"1.0.3","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","import":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"},"./styles":{"bun":"./src/index.css","import":"./src/index.css","default":"./dist/index.css"}},"scripts":{"test":"bun test","build":"bun run build.ts && bun run build:types","build:types":"tsc -p ./tsconfig.declarations.json"},"peerDependencies":{"react":"^18.0.0","react-dom":"^18.0.0"},"dependencies":{"@tiptap/extension-mention":"^2.27.1","@tiptap/extension-placeholder":"^2.1.0","@tiptap/react":"^2.1.0","@tiptap/starter-kit":"^2.1.0","@tiptap/suggestion":"^2.7.0","bun-plugin-tailwind":"^0.1.2","lodash":"^4.17.21","lucide-react":"^0.544.0","react":"^18.2.0","react-dom":"^18.2.0","react-hot-toast":"^2.6.0","react-icons":"^4.11.0","react-router-dom":"^7.12.0","react-virtuoso":"^4.12.3","streamdown":"^1.3.0","tippy.js":"^6.3.7","uuid":"^9.0.1","zustand":"^5.0.8"},"devDependencies":{"@amigo-llm/types":"workspace:*","@testing-library/react":"^16.3.1","@testing-library/react-hooks":"^8.0.1","@types/lodash":"^4.17.20","@types/react":"^18.2.15","@types/react-dom":"^18.2.7","@types/uuid":"^9.0.4","autoprefixer":"^10.4.15","daisyui":"^5.1.25","fast-check":"^4.5.2","happy-dom":"^20.0.11","tailwindcss":"^4.1.17","typescript":"^5.0.2"},"_id":"@amigo-llm/frontend@1.0.3","gitHead":"810d7e1464b914703310d80367082a4774a2a99f","description":"`@amigo-llm/frontend` 是 Amigo 的 React SDK，提供：","_nodeVersion":"20.11.1","_npmVersion":"10.2.4","dist":{"integrity":"sha512-26EHCAiKYGOKMcwQKiu5Y5unSau+xbijQj7zTyw1TYk/15T288JhnyoDZKj89wHmCyTOYKg3BcHqQekvdc6r1A==","shasum":"ff3fcc56a6799dc7400719891f4bd0a40eceab91","tarball":"https://registry.npmjs.org/@amigo-llm/frontend/-/frontend-1.0.3.tgz","fileCount":111,"unpackedSize":330673,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDCasyvfbGgGhLrM6apl1nkXJLfEOgGmT4igQhKl2/5pgIhAIhdixOHuX9y6ob3T20pRiKoH3WPZTfBvikwzkUgqPrA"}]},"_npmUser":{"name":"kb666","email":"kaiqingliu6@gmail.com"},"directories":{},"maintainers":[{"name":"kb666","email":"kaiqingliu6@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/frontend_1.0.3_1773737364800_0.6302076176878275"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-03T09:04:46.546Z","modified":"2026-03-17T08:49:25.061Z","0.0.2":"2025-12-03T09:04:46.931Z","1.0.3":"2026-03-17T08:49:24.946Z"},"maintainers":[{"name":"kb666","email":"kaiqingliu6@gmail.com"}],"readme":"# @amigo-llm/frontend\n\n`@amigo-llm/frontend` 是 Amigo 的 React SDK，提供：\n\n- WebSocket 连接管理\n- 任务与消息状态管理\n- 聊天窗口与消息输入框\n- mention、工具确认、任务切换等交互\n- 自定义消息渲染器\n\n它适合嵌入到已有 React 应用里，快速接一个多任务 agent UI。\n\n## 安装\n\n```bash\nbun add @amigo-llm/frontend react react-dom\n```\n\n## 引入样式\n\n```ts\nimport \"@amigo-llm/frontend/styles\";\n```\n\n建议在应用入口引入一次。\n\n## 最小示例\n\n```tsx\nimport { ChatWindow, MessageInput, WebSocketProvider } from \"@amigo-llm/frontend\";\nimport \"@amigo-llm/frontend/styles\";\n\nexport default function App() {\n  return (\n    <WebSocketProvider url=\"ws://localhost:10013\" autoConnect>\n      <div style={{ height: \"100vh\", display: \"flex\", flexDirection: \"column\" }}>\n        <ChatWindow />\n        <MessageInput placeholder=\"输入消息...\" />\n      </div>\n    </WebSocketProvider>\n  );\n}\n```\n\n前提：\n\n- WebSocket 服务端已启动\n- 前后端协议与 `@amigo-llm/types` 一致\n\n## 核心概念\n\n### WebSocketProvider\n\n`WebSocketProvider` 是 SDK 入口，负责：\n\n- 创建内部 Zustand store\n- 建立 WebSocket 连接\n- 提供 hooks 和组件上下文\n- 挂载自定义 renderers 与事件回调\n\n所有 SDK hooks 和组件都必须在 `WebSocketProvider` 内使用。\n\n### Task 驱动的状态模型\n\nSDK 不是只维护一条聊天流，而是按 `taskId` 组织状态：\n\n- 每个 task 都有自己的 `rawMessages` 和 `displayMessages`\n- 有 `mainTaskId` 和当前激活 task\n- 可以在主任务和子任务之间切换\n\n## Provider 参数\n\n```tsx\n<WebSocketProvider\n  url=\"ws://localhost:10013\"\n  autoConnect={true}\n  reconnect={true}\n  reconnectInterval={3000}\n  reconnectAttempts={5}\n  onConnect={() => console.log(\"connected\")}\n  onDisconnect={() => console.log(\"disconnected\")}\n  onError={(error) => console.error(error)}\n>\n  <App />\n</WebSocketProvider>\n```\n\n### 主要 props\n\n| Prop | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `url` | `string` | `ws://localhost:10013` | WebSocket 地址 |\n| `autoConnect` | `boolean` | `true` | Provider 挂载后自动连接 |\n| `reconnect` | `boolean` | `true` | 是否自动重连 |\n| `reconnectInterval` | `number` | `3000` | 重连间隔，毫秒 |\n| `reconnectAttempts` | `number` | `5` | 最大重连次数，`< 0` 可视为无限 |\n| `renderers` | `Partial<MessageRendererMap>` | - | 自定义消息渲染器 |\n| `initialState` | `WebSocketStoreConfig[\"initialState\"]` | - | 初始状态注入 |\n| `onConnect` / `onDisconnect` / `onError` | 回调 | - | 连接生命周期钩子 |\n\n`url` 会直接传递到底层连接切片。\n\n## 公开组件\n\n### ChatWindow\n\n用于展示某个 task 的消息列表：\n\n```tsx\n<ChatWindow taskId=\"task-123\" />\n```\n\n常用 props：\n\n- `taskId`\n- `className`\n- `showHeader`\n- `headerContent`\n\n### MessageInput\n\n用于发送消息、处理中断 / 恢复、mention、附件上传、工具确认。\n\n```tsx\n<MessageInput\n  taskId=\"task-123\"\n  placeholder=\"输入需求...\"\n  createTaskContext={{ repoUrl: \"https://github.com/owner/repo\" }}\n/>\n```\n\n常用 props：\n\n- `taskId`\n- `placeholder`\n- `onSend`\n- `createTaskContext`\n- `disabled`\n- `showMentions`\n\n内置能力：\n\n- `Enter` 发送，`Shift + Enter` 换行\n- 当任务处于 `streaming` / `interrupted` 时自动切换按钮状态\n- mention 建议\n- 文件附件上传\n- 工具确认卡片\n\n注意：附件上传依赖服务端的 `/api/uploads/oss/*` 接口；如果服务端没配 OSS，消息仍可发送，但附件上传会失败。\n\n## Hooks\n\n### useConnection\n\n```tsx\nconst { status, isConnected, isConnecting, isDisconnected } = useConnection();\n```\n\n### useWebSocket\n\n```tsx\nconst { status, connect, disconnect, reconnect, send, subscribe } = useWebSocket();\n```\n\n### useMessages\n\n```tsx\nconst { messages, rawMessages, sendMessage, clearMessages } = useMessages(taskId);\n```\n\n### useSendMessage\n\n```tsx\nconst {\n  sendMessage,\n  sendCreateTask,\n  sendInterrupt,\n  sendResume,\n  sendLoadTask,\n  sendConfirm,\n  sendReject,\n  sendDeleteTask,\n  sendUpdateAutoApproveTools,\n} = useSendMessage();\n```\n\n### useTasks\n\n```tsx\nconst {\n  tasks,\n  currentTaskId,\n  mainTaskId,\n  taskStatusMaps,\n  taskAutoApproveToolNameMaps,\n  switchTask,\n  getTaskStatus,\n} = useTasks();\n```\n\n### useMentions\n\n```tsx\nconst { mentions, getMentionSuggestions, followupQueue, pendingMention } = useMentions();\n```\n\n## 自定义渲染器\n\n你可以覆盖指定消息类型的 UI：\n\n```tsx\n<WebSocketProvider\n  url=\"ws://localhost:10013\"\n  renderers={{\n    tool: ({ message }) => <pre>{JSON.stringify(message, null, 2)}</pre>,\n  }}\n>\n  <App />\n</WebSocketProvider>\n```\n\n默认导出包括：\n\n- `defaultRenderers`\n- `DefaultMessageRenderer`\n- `DefaultToolRenderer`\n- `DefaultUserMessageRenderer`\n- `DefaultErrorRenderer`\n- `DefaultAlertRenderer`\n- `DefaultInterruptRenderer`\n- `DefaultAskFollowupQuestionRenderer`\n- `DefaultBrowserSearchRenderer`\n\n## 高级用法\n\n### 读取底层上下文\n\n```tsx\nimport { useWebSocketContext } from \"@amigo-llm/frontend\";\n\nconst { store, config, renderers, handlers } = useWebSocketContext();\n```\n\n适合做：\n\n- 应用层页面路由联动\n- 自定义工具卡片\n- 直接操作 store\n\n### 自定义工具结果 UI\n\n应用可以基于 taskId、自己的 HTTP 路由和 renderers 体系，给工具结果补充 editor、preview、跳转按钮等 UI。下面是一个简单例子：\n\n```tsx\nimport {\n  DefaultToolRenderer,\n  ToolAccordion,\n  type ToolMessageRendererProps,\n  useTasks,\n  useWebSocketContext,\n} from \"@amigo-llm/frontend\";\n\nconst AppToolRenderer = ({ message }: ToolMessageRendererProps<any>) => {\n  const { mainTaskId, currentTaskId } = useTasks();\n  const { config } = useWebSocketContext();\n  const taskId = mainTaskId || currentTaskId;\n\n  if (message.toolName === \"updateDevServer\" && taskId) {\n    const baseUrl = config.url.replace(/^ws/, \"http\").replace(/\\/$/, \"\");\n    const previewUrl = `${baseUrl}/api/tasks/${encodeURIComponent(taskId)}/preview`;\n\n    return (\n      <ToolAccordion title=\"更新开发预览\">\n        <a href={previewUrl} target=\"_blank\" rel=\"noreferrer\">\n          打开 Preview\n        </a>\n      </ToolAccordion>\n    );\n  }\n\n  return <DefaultToolRenderer message={message} />;\n};\n```\n\n## 注意事项\n\n- 所有 hooks 必须在 `WebSocketProvider` 内使用\n- `MessageInput` 的附件上传需要服务端 OSS 接口\n- SDK 提供的是组件和状态能力，不包含仓库应用里的路由、侧边栏、设计页等完整壳层\n- 如果你直接复用仓库应用，请再参考根 README 的部署约束，尤其是 `10013` 端口与 `wss` 的假设\n","readmeFilename":"README.md","description":"`@amigo-llm/frontend` 是 Amigo 的 React SDK，提供："}