npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@done-coding/forge-agent

v0.5.0

Published

AI 对话 / 智能体 UI 库

Readme

@done-coding/forge-agent

AI 对话 / 智能体 UI 库:OpenAI 协议 SSE 对话驱动的组件 + hooksAiDialogue 全流程对话 / AiMessageList 受控展示 / AiSender 受控输入 / 会话持久化 / 移动端适配),经 vue-element-plus-x 封装,让 AI 对话更容易接入。

协议层(C 阶段已交付,2026-08-02):SSE 分两块——传输层为 OpenAI Chat Completions 协议(POST {model, messages, stream:true} + SSE 帧解析 + delta.content 累积 + [DONE]),业务数据经 转换器(frame: AiSSEFrame) => AiMessagePart[])由业务方注入(缺省恒等)。function call 仅立结构(AiToolPart v5 状态机 + delta.tool_calls 增量解析),不实现调用逻辑。历史 Dify 契约(response_mode:"streaming" + workflow 事件序)已移除。

安装

pnpm add @done-coding/forge-agent

样式必须显式引入(SFC scoped CSS 被抽取到独立 es/style.css,漏引会导致 scoped 选择器失效、布局塌陷):

import "@done-coding/forge-agent/style.css";

peer 依赖需消费方自装:vue / element-plus / @element-plus/icons-vue / lodash

快速开始(最小可用)

① 受控展示:零后端依赖,接入最快

静态消息展示(markdown 渲染 / 业务 data part),结构对齐业界 UIMessage:

// 参考 apps/reference/src/pages/integration/agent/Index.vue
import { AiConfigProvider, AiMessageList, type AiMessage } from "@done-coding/forge-agent";
import "@done-coding/forge-agent/style.css";

const messages: AiMessage[] = [
  { id: "m1", role: "user", content: "你好" },
  { id: "m2", role: "assistant", content: "**你好!** 我是 forge-agent。" },
];
<AiConfigProvider :is-dark-theme="false">
  <AiMessageList :messages="messages" />
</AiConfigProvider>

要点:

  • 消息形态:{ id, role: "user"|"assistant", parts | content }——content 是纯文本 shorthand,等价 parts:[{type:"text",text}]parts 存在时以 parts 为准
  • 业务数据走 data-<类型> part({ type:"data-card", data: {...} }),经 #part 插槽由消费方渲染(缺省平台占位兜底)
  • AiConfigProvider 提供主题变量(--dc-agent-*)与 expose 通道,[MUST] 包裹对话组件

② 完整对话:自管全流程(持久化 + SSE + 输入)

// 参考 demo-src/demos/DialogueDemo.vue
import { AiDialogue, AiDialogueGroup } from "@done-coding/forge-agent";

// AiDialogue:单会话对话(userName 必填——派生命名空间化的 IndexedDB 库)
<AiDialogue userName="demo-user" />

// AiDialogueGroup:多会话 + 会话列表操作条(操作条默认折叠)
<AiDialogueGroup :user-name="'demo-user'" />

要点:

  • userName 必填:派生 createAiDb(userName)(IndexedDB 持久化会话 + 消息)——不传 db prop 即自动派生
  • SSE 端点:sseUrl [MUST] 显式传入(OpenAI Chat Completions 协议;库不读宿主 env,未传时发送提示「未配置 SSE 端点」)
  • AiDialogue 自带:打字机流式 / markdown(含代码块复制)/ 思考折叠 / 中止 / 复制 / 历史记录加载 / 键盘避让
  • 业务自定义协议数据:传 sseConverter(转换器)把帧映射到 AiMessagePart[](如自定义事件 / delta 扩展字段 / tool_calls → tool-call part)

③ 浮层形态:Teleport 悬浮面板

// AiDialogueWrap:fixed 右下角浮层(拉手开合 + 移动端全屏),AI 模块挂载点
<AiDialogueWrap v-model:show="show" density="auto">
  <AiDialogueGroup :user-name="'demo-user'" />
</AiDialogueWrap>

组件族索引

| 族 | 组件 | 文档 | | --- | --- | --- | | dialogue 对话族 | AiDialogue(全流程单会话)/ AiDialogueGroup(多会话+操作条)/ AiDialogueWrap(浮层)/ AiMessageList(受控展示) | README | | sender 输入族 | AiSender(受控输入原语)|内部件 AiSenderHeader | README | | config 配置族 | AiConfigProvider(主题 + expose 通道根) | README | | operation 操作族 | 内部件:AiOperationBar / AiOperationBtns | README | | bubble 气泡族 | 内部件:AiBubbleContent / AiBubbleFooter / AiCodeHeaderControl / AiContentSplit / AiThinkBox / AiTypewriter | README |

hooks 模块索引

全部经顶层 barrel 导出(import { useAiExposeInject } from "@done-coding/forge-agent"):

| hook | 职责 | | --- | --- | | useAiExposeProvide / useAiExposeInject | 对话 expose 通道(AiConfigProvider provide / 消费方 inject:sender/bubbleList/ask/sessionOperate/sessionExposeMap)。ask({ value?, parts? }) 两者至少给一个——纯 parts = 以用户名义发数据卡 | | useSessionExpose / useSessionOperateMount | 单会话 expose 注册 / 会话级操作挂载 | | useSenderScoped | 输入框全流程作用域(提交 → SSE → 气泡追加 → 中止),AiDialogue 内部消费 | | useBubbleListScoped / useSessionListScoped / createNewSessionInfo | 气泡 / 会话列表状态机(内存 + IndexedDB 分页) | | useAiModuleTheme | 主题变量挂值:--dc-agent-* 三级 fallback 链(var(--dc-core-*, var(--el-*, 默认))) | | useCodeXSlot | XMarkdown 代码块头部控制(复制 + 折叠) | | useFixBubbleListBug | BubbleList 滚动动画屏蔽(历史加载 / 首问) | | useAiSSE + 协议层工具 | OpenAI 协议 SSE 对话流 + 传输层/转换器(parseOpenAIChunk / parseSSEEventLine / mergeToolCallsDeltas / defaultSSEConverter) |

详情见 hooks 引导

主题与移动端

  • 主题变量域:--dc-agent-*(色 / 间距 / 字体 / 圆角 / 面板尺寸 / 安全区 token)。值 = var(--dc-core-*, var(--el-*, 默认)) 三级 fallback——宿主已接入 admin-core 主题时随主题风格 + 亮暗自适配,未接入时退 element-plus 色系。useAiModuleTheme(getIsDark) 把默认值挂到 :root bodyAiConfigProvider 已内置)。
  • 移动端适配:AiDialogueWrap 内置 useAgentUiDensity(scoped-rem 密度缩放 + 面板全屏形态)+ useKeyboardInset(visualViewport 键盘避让);消费方唯一必做 = 入口 HTML 加 viewport-fit=cover meta(详见 docs/mobile-adaptation.md)。

协议契约(C 阶段已落地,2026-08-02 拍板方案)

AiMessage 结构(对齐 AI SDK v5 状态机 + Anthropic content blocks 收敛集)

type AiMessageRole = "system" | "user" | "assistant";
interface AiMessage {
  id: string;
  role: AiMessageRole;
  parts?: AiMessagePart[];  // parts 存在则以其为准(content 忽略)
  content?: string;         // shorthand = [{type:"text",text:content}],兼容 OpenAI
}
type AiMessagePart =
  | AiTextPart        // { type:"text", text }——markdown 渲染
  | AiReasoningPart   // { type:"reasoning", text }——thinking 痕迹
  | AiToolPart        // 状态机:input-streaming → input-available → output-available/error/denied
  | AiFilePart        // { type:"file", mediaType, filename?, url? }——图片/附件
  | AiDataPart        // { type:`data-${string}`, data: unknown }——业务卡片(现状保留)
  • AiToolPart{ type:"tool-call", id(增量合并锚), name, state, input?, output?, errorText? }——result 并入 output(v5 风格,不分离 tool-result);Anthropic tool_result 由转换器映射为 output-available 态
  • AiDataPart 保持 data-${string} + unknown(AI SDK data-{typeName} 同款;UI 兜底占位 + #part 插槽业务自渲染)
  • content shorthand 保留(OpenAI content 兼容)

SSE 分两块(传输协议层 + 业务转换层)

  • 传输协议层(通用 · 业务无关):OpenAI Chat Completions——POST body 构造({ model, messages, stream:true })+ SSE 帧解析(data: JSON + [DONE])+ choices[0].delta.content 累积 + finish_reasondelta.tool_calls 增量解析类型预留(function call 只立结构,不做调用逻辑)
  • 业务转换层(业务方注入):转换器签名 (frame: 归一化帧) => AiMessagePart[]——逐帧增量调用支撑流式渲染;输入 = 解析后的协议帧对象(含标准字段 + 原始自定义字段);缺省恒等(content → AiTextPart)。业务数据通道:content 混 markdown / 自定义 SSE 事件 / delta 扩展字段
  • 移除 Dify 事件序(workflow_started / node_started / text_chunk / node_finished / workflow_finished)

出站序列化(AiMessage[] → wire messages[]

与入站转换器成对的另一半:气泡历史 → AiMessage[](正文做首个 text part,data-* 卡片随后) → 序列化器body.messages[]。默认 toOpenAIMessages(公开纯函数)是有损映射:

| part | 默认映射 | | --- | --- | | text | 拼进 content | | data-* | 序列化成 [<type>]\n<JSON> 文本段,并入同一条 content(多段以空行分隔) | | reasoning | 丢弃(thinking 痕迹不回发,业界通例) | | tool-call / file | 不映射(前者的回发由 agent 循环在本轮内自持 tool_calls + role:"tool";后者需多模态 content 数组) |

映射后 content 为空串的消息整条剔除。形状不合意就整条换掉——AiDialoguesseMessagesSerializer prop 一次性拿到「历史 + 本轮」全部消息,返回什么就发什么; aiIsDataPart / aiDataPartToWireText 随包导出,自写序列化器时当积木用。 [MUST NOT] 去改 toOpenAIMessages 的输出再回填。

文档约定

  • 组件文档 README-<PascalName>.md 位于各族目录 docs/;内部件仅 API 段(不导出,无消费向叙事)
  • API 以 src/types/index.ts 与组件源码为真相源;文档与其冲突时以源码为准
  • 迭代惯例(与 core 同款):README API 模块先行 → 实现 → demo/单测/e2e → 非 API 文档