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

@snack-kit/porygon

v0.8.0

Published

Unified programmatic interface for LLM Agent CLI tools

Readme

Porygon

统一的 LLM Agent CLI 编程接口框架。通过 Facade + Adapter 模式,将 Claude Code、OpenCode 等 CLI 工具封装为一致的 TypeScript API。

安装

npm add @snack-kit/porygon

前置条件:需要安装至少一个 CLI 后端:

快速上手

import { createPorygon } from "@snack-kit/porygon";

const porygon = createPorygon();

// 简单调用:发送 prompt,返回最终文本
const answer = await porygon.run({ prompt: "什么是闭包?" });
console.log(answer);

// 释放资源
await porygon.dispose();

API 速查

createPorygon(config?): Porygon

工厂函数,创建 Porygon 实例。

const porygon = createPorygon({
  defaultBackend: "claude",           // "claude" | "opencode"
  backends: {
    claude: {
      model: "sonnet",
      interactive: false,             // false = --dangerously-skip-permissions
      cliPath: "/usr/local/bin/claude", // 自定义 CLI 路径
      appendSystemPrompt: "用中文回答",
      cwd: "/path/to/project",
      proxy: { url: "http://127.0.0.1:7897" },
    },
    opencode: {
      serverUrl: "http://localhost:39393",
      apiKey: "sk-xxx",
    },
  },
  defaults: {
    timeoutMs: 300_000,
    maxTurns: 50,
    appendSystemPrompt: "请简洁回答",
    disallowedTools: ["WebSearch"],  // 全局禁用的工具
  },
  proxy: { url: "http://proxy:8080", noProxy: "localhost" },
});

PorygonConfig 字段:

| 字段 | 类型 | 说明 | |------|------|------| | defaultBackend | string | 默认后端名称 | | backends | Record<string, BackendConfig> | 各后端独立配置 | | defaults | { appendSystemPrompt?, timeoutMs?, maxTurns?, disallowedTools? } | 全局默认参数 | | proxy | { url: string, noProxy?: string } | 全局代理(后端级 proxy 优先) |

BackendConfig 字段:

| 字段 | 类型 | 说明 | |------|------|------| | model | string | 模型名称 | | interactive | boolean | 是否交互模式,false 时 Claude 添加 --dangerously-skip-permissions | | cliPath | string | CLI 可执行文件路径(如 Claude CLI 的自定义安装路径) | | serverUrl | string | 远程服务地址(OpenCode serve 模式) | | apiKey | string | API Key 认证 | | appendSystemPrompt | string | 追加系统提示词 | | proxy | ProxyConfig | 后端专属代理 | | cwd | string | 工作目录 | | disallowedTools | string[] | 禁止使用的工具黑名单(后端级) | | options | Record<string, unknown> | 透传给后端的额外选项(向后兼容,推荐使用上方的显式字段) |


porygon.run(request): Promise<string>

发送 prompt,等待完成,返回最终结果文本。适合简单的一次性调用。

const result = await porygon.run({
  prompt: "写一个 HTTP 服务器",
  backend: "claude",
  model: "opus",
});

porygon.query(request): AsyncGenerator<AgentMessage>

流式查询,逐条 yield AgentMessage。适合需要实时输出或监控工具调用的场景。

for await (const msg of porygon.query({ prompt: "解释快速排序" })) {
  switch (msg.type) {
    case "system":
      console.log("模型:", msg.model);
      break;
    case "stream_chunk":
      process.stdout.write(msg.text); // 增量文本,实时输出
      break;
    case "assistant":
      // turnComplete=true: 与前面 stream_chunk 内容重复,流式消费者应跳过
      // turnComplete=false/undefined: 独立文本(如 run() 模式),需要处理
      if (!msg.turnComplete) {
        console.log("回复:", msg.text);
      }
      break;
    case "tool_use":
      console.log("工具调用:", msg.toolName, msg.input);
      if (msg.output) console.log("工具结果:", msg.output);
      break;
    case "result":
      console.log("完成", { cost: msg.costUsd, tokens: msg.inputTokens });
      break;
    case "error":
      console.error("错误:", msg.message);
      break;
  }
}

消息流顺序(两个后端统一):

system → stream_chunk* → assistant(turnComplete) → tool_use* → ... → result
  • stream_chunk — 增量文本片段,用于实时展示
  • assistant — 单个 turn 的完整文本汇总。turnComplete: true 表示内容与 stream_chunk 重复
  • tool_use — 工具调用;若带 output 字段则为工具执行结果
  • result — 最终结果,包含完整文本和用量统计

PromptRequest(请求参数)

run()query() 共用同一参数类型:

| 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | prompt | string | 是 | 提示词 | | backend | string | 否 | 后端名称(默认取 config.defaultBackend) | | model | string | 否 | 模型名称 | | resume | string | 否 | 恢复会话的 session ID | | systemPrompt | string | 否 | 系统提示词(替换模式,设置后 appendSystemPrompt 失效) | | appendSystemPrompt | string | 否 | 系统提示词(追加模式,与 config 中的追加内容合并) | | onlyTools | string[] | 否 | 仅允许使用的工具白名单(内部通过 getTools() 差集转为黑名单,与 disallowedTools 互斥,优先级更高) | | disallowedTools | string[] | 否 | 禁止使用的工具黑名单 | | maxTurns | number | 否 | 最大对话轮次 | | timeoutMs | number | 否 | 超时毫秒数 | | cwd | string | 否 | 工作目录 | | envVars | Record<string, string> | 否 | 注入子进程的环境变量 | | mcpServers | Record<string, McpServerConfig> | 否 | MCP 服务器配置 | | backendOptions | Record<string, unknown> | 否 | 透传给后端的额外选项 |

配置合并策略mergeRequest):

| 字段 | 策略 | |------|------| | model | request > backendConfig > 不设置 | | timeoutMs | request > defaults | | maxTurns | request > defaults | | cwd | request > backendConfig | | onlyTools | 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(设置后忽略 disallowedTools) | | disallowedTools | defaults + backendConfig + request 三层叠加去重 | | appendSystemPrompt | defaults + backendConfig + request 三层追加拼接(换行分隔)。若 systemPrompt 已设置则忽略全部 append |


AgentMessage(消息类型)

porygon.query() 返回的 AgentMessage 是一个联合类型。所有消息均包含 timestamp: number,可选 sessionId?: stringraw?: unknown(后端原始数据)。

| type | 接口 | 关键字段 | |------|------|----------| | "system" | AgentSystemMessage | model?, tools?, cwd? | | "assistant" | AgentAssistantMessage | text, turnComplete? | | "tool_use" | AgentToolUseMessage | toolName, input, output? | | "stream_chunk" | AgentStreamChunkMessage | text | | "result" | AgentResultMessage | text, durationMs?, costUsd?, inputTokens?, outputTokens? | | "error" | AgentErrorMessage | message, code? |


porygon.checkBackend(backend, options?): Promise<HealthCheckResult>

检查单个后端的健康状态。支持两种模式:

  • 轻量检测(默认):仅检查 CLI 是否存在 + 版本兼容性
  • 深度检测deep: true):额外向模型发送一条测试消息,验证 Token/模型/配额是否真正可用
// 轻量检测:仅检查 CLI 存在性和版本
const result = await porygon.checkBackend("claude");
// { available: true, version: "1.2.0", supported: true }

// 深度检测:真实调用模型验证可用性
const deep = await porygon.checkBackend("claude", { deep: true, model: "haiku" });
// { available: true, version: "1.2.0", supported: true, modelVerified: true }
// { available: true, version: "1.2.0", supported: true, modelVerified: false, warnings: ["模型验证失败: ..."] }

CheckBackendOptions:

| 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | deep | boolean | false | 启用深度检测,向模型发送测试消息 | | model | string? | 后端默认 | 深度检测使用的模型 | | timeoutMs | number | 15000 | 深度检测超时(毫秒) |

HealthCheckResult:

| 字段 | 类型 | 说明 | |------|------|------| | available | boolean | 后端是否可用 | | version | string? | CLI 版本号 | | supported | boolean? | 版本是否在测试范围内 | | warnings | string[]? | 兼容性警告 | | error | string? | 错误信息 | | modelVerified | boolean? | 深度检测时模型是否真正响应 |


porygon.healthCheck(): Promise<Record<string, HealthCheckResult>>

对所有已注册后端进行健康检查(并行执行)。

const health = await porygon.healthCheck();
// { claude: { available: true, version: "1.2.0", supported: true }, opencode: { available: false, error: "..." } }

porygon.use(direction, fn): () => void

注册拦截器,返回取消注册函数。

direction: "input" | "output"

InterceptorFn 签名:

type InterceptorFn = (
  text: string,
  context: InterceptorContext
) => string | boolean | undefined | Promise<string | boolean | undefined>;
  • 返回 string:替换文本
  • 返回 false:拒绝消息(抛出 InterceptorRejectedError
  • 返回 true / undefined:不修改,传递原始文本

示例:

// 输入拦截:追加指令
const unuse = porygon.use("input", (text) => text + "\n请用 Markdown 格式回答");

// 输出拦截:过滤敏感信息
porygon.use("output", (text) => text.replace(/sk-[a-zA-Z0-9]{48}/g, "[REDACTED]"));

// 拒绝过长输入
porygon.use("input", (text) => { if (text.length > 10000) return false; });

// 取消注册
unuse();

防护拦截器

内置的 prompt 注入防护工具函数。

createInputGuard(options?): InterceptorFn

检测并阻止常见 prompt 注入攻击(内置 13 条中英文模式)。

import { createInputGuard } from "@snack-kit/porygon";

// 使用内置规则
porygon.use("input", createInputGuard());

// 自定义配置
porygon.use("input", createInputGuard({
  blockedPatterns: [/我是管理员/, /sudo mode/i],
  action: "reject",       // "reject"(拒绝)| "redact"(替换为 [REDACTED])
  backends: ["claude"],    // 仅对指定后端生效
  customCheck: (text) => text.includes("bypass"),
}));

createOutputGuard(options?): InterceptorFn

检测输出中的敏感关键词泄露。

import { createOutputGuard } from "@snack-kit/porygon";

porygon.use("output", createOutputGuard({
  sensitiveKeywords: ["内部API密钥", "sk-xxxx"],
  action: "redact",
}));

GuardOptions:

| 字段 | 类型 | 说明 | |------|------|------| | blockedPatterns | RegExp[] | 额外阻止模式(输入侧) | | sensitiveKeywords | string[] | 敏感关键词列表(输出侧) | | action | "reject" | "redact" | 触发时的处理动作,输入默认 reject,输出默认 redact | | customCheck | (text, context) => boolean | 自定义判定函数 | | backends | string[] | 仅对指定后端生效 |


porygon.listSessions(backend?, options?): Promise<SessionInfo[]>

列出指定后端的历史会话。

const sessions = await porygon.listSessions("claude", { limit: 10 });
// SessionInfo: { sessionId, backend, summary?, lastModified, cwd?, metadata? }

porygon.abort(backend, sessionId): void

中止正在运行的查询。


porygon.settings(backend, newSettings?): Promise<Record<string, unknown>>

读取或更新后端设置。

const settings = await porygon.settings("claude");
await porygon.settings("claude", { model: "opus" });

porygon.listModels(backend?): Promise<ModelInfo[]>

获取后端可用模型列表。

const models = await porygon.listModels("claude");
// ModelInfo: { id: string, name?: string, provider?: string }

porygon.getTools(force?): Promise<string[]>

获取当前可用的工具列表。首次调用通过发起最小化对话(maxTurns: 1)从 system 事件中提取,结果会被缓存,后续调用直接返回缓存。

const tools = await porygon.getTools();
console.log(tools);
// ["Bash", "Read", "Edit", "Write", "Glob", "Grep", "mcp__xxx__yyy", ...]

// 强制刷新缓存
const fresh = await porygon.getTools(true);

仅 Claude 后端支持此能力,其他后端返回空数组。


porygon.getCapabilities(backend?): AdapterCapabilities

获取后端能力声明。

const caps = porygon.getCapabilities("claude");
// {
//   features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | ...>,
//   streamingMode: "delta" | "chunked",
//   outputFormats: string[],
//   testedVersionRange: string,
// }

streamingMode

  • "delta" — 后端原生产生增量 stream_chunk 事件(OpenCode)
  • "chunked" — 适配器将完整 assistant 拆分为 stream_chunk + assistant(Claude)

porygon.dispose(): Promise<void>

终止所有进程、中止所有查询、清空缓存。使用完毕后必须调用。


错误处理

所有错误继承自 PorygonError,包含 code 字段:

import { PorygonError, AdapterNotFoundError } from "@snack-kit/porygon";

try {
  await porygon.run({ prompt: "hi", backend: "nonexistent" });
} catch (err) {
  if (err instanceof AdapterNotFoundError) {
    console.log(err.code); // "ADAPTER_NOT_FOUND"
  }
}

| 错误类 | code | 场景 | |--------|------|------| | AdapterNotFoundError | ADAPTER_NOT_FOUND | 后端未注册 | | AdapterNotAvailableError | ADAPTER_NOT_AVAILABLE | CLI 不可用 | | AdapterIncompatibleError | ADAPTER_INCOMPATIBLE | 版本不兼容 | | SessionNotFoundError | SESSION_NOT_FOUND | 会话不存在 | | InterceptorRejectedError | INTERCEPTOR_REJECTED | 拦截器拒绝 | | AgentExecutionError | AGENT_EXECUTION_ERROR | 执行异常 | | AgentTimeoutError | AGENT_TIMEOUT | 超时 | | ConfigValidationError | CONFIG_VALIDATION_ERROR | 配置校验失败 |

事件

Porygon 继承自 EventEmitter:

porygon.on("health:degraded", (backend: string, warning: string) => {
  console.warn(`${backend} 降级: ${warning}`);
});

| 事件 | 参数 | 说明 | |------|------|------| | health:degraded | (backend, warning) | 后端版本不兼容时触发 |

完整导出列表

// 核心
export { Porygon, createPorygon } from "@snack-kit/porygon";
export type { PorygonEvents, HealthCheckResult, CheckBackendOptions } from "@snack-kit/porygon";

// 类型
export type {
  PorygonConfig, BackendConfig, ProxyConfig,
  PromptRequest, McpServerConfig,
  AgentMessage, AgentSystemMessage, AgentAssistantMessage,
  AgentToolUseMessage, AgentStreamChunkMessage, AgentResultMessage, AgentErrorMessage,
  AdapterCapabilities, CompatibilityResult, SessionInfo, SessionListOptions, ModelInfo,
  InterceptorFn, InterceptorDirection, InterceptorContext,
  GuardOptions, GuardAction,
  IAgentAdapter,
} from "@snack-kit/porygon";

// 错误
export {
  PorygonError, AdapterNotFoundError, AdapterNotAvailableError,
  AdapterIncompatibleError, SessionNotFoundError, InterceptorRejectedError,
  AgentExecutionError, AgentTimeoutError, ConfigValidationError,
} from "@snack-kit/porygon";

// 防护拦截器
export { createInputGuard, createOutputGuard } from "@snack-kit/porygon";

// 交互式会话
export { InteractiveSession } from "@snack-kit/porygon";

// 适配器(自定义扩展用)
export { AbstractAgentAdapter, ClaudeAdapter, OpenCodeAdapter } from "@snack-kit/porygon";

典型使用模式

带防护的流式调用

import { createPorygon, createInputGuard, createOutputGuard } from "@snack-kit/porygon";

const porygon = createPorygon({
  defaultBackend: "claude",
  backends: {
    claude: { model: "sonnet", interactive: false },
  },
});

porygon.use("input", createInputGuard());
porygon.use("output", createOutputGuard({ sensitiveKeywords: ["SECRET"] }));

for await (const msg of porygon.query({ prompt: "解释 async/await" })) {
  if (msg.type === "stream_chunk") process.stdout.write(msg.text);
  if (msg.type === "result") console.log("\n完成", msg.costUsd);
}

await porygon.dispose();

恢复会话

const sessions = await porygon.listSessions("claude", { limit: 1 });
if (sessions.length > 0) {
  const answer = await porygon.run({
    prompt: "继续上次的工作",
    resume: sessions[0].sessionId,
  });
}

注入环境变量与 MCP 服务器

const answer = await porygon.run({
  prompt: "查找相关文档后回答",
  envVars: { CLAUDE_CODE_OAUTH_TOKEN: "token-xxx" },
  mcpServers: {
    "context7": { command: "npx", args: ["-y", "@context7/mcp"] },
  },
});

工具控制

// 查询所有可用工具
const tools = await porygon.getTools();
console.log(tools); // ["Bash", "Read", "Edit", ...]

// 黑名单:禁用危险工具
await porygon.run({
  prompt: "分析代码",
  disallowedTools: ["Bash", "Edit", "Write"],
});

// 白名单:只允许只读工具(内部自动转为黑名单,getTools 结果有缓存)
await porygon.run({
  prompt: "阅读代码并回答",
  onlyTools: ["Read", "Grep", "Glob"],
});

// 配置级黑名单:全局 + 后端级 + 请求级三层叠加
const porygon = createPorygon({
  defaults: { disallowedTools: ["WebSearch"] },
  backends: {
    claude: {
      interactive: false,
      disallowedTools: ["Bash"],
    },
  },
});
// 此时 disallowedTools = ["WebSearch", "Bash"],无需每次请求重复传入

健康检查后选择可用后端

// 轻量检查:CLI 存在性 + 版本
const result = await porygon.checkBackend("claude");
if (!result.available) console.error(result.error);

// 深度检查:验证模型真正可用(Token/配额/模型名)
const deep = await porygon.checkBackend("claude", { deep: true, model: "haiku" });
if (deep.modelVerified) console.log("模型验证通过");

// 批量检查所有后端
const health = await porygon.healthCheck();
const backend = health.claude?.available ? "claude" : "opencode";
const answer = await porygon.run({ prompt: "hello", backend });

架构

Porygon (Facade)
├── InterceptorManager     # 输入/输出拦截器流水线
├── ProcessManager         # 子进程生命周期管理
├── SessionManager         # 会话缓存与委托
└── Adapters
    ├── ClaudeAdapter      # claude -p --output-format stream-json
    └── OpenCodeAdapter    # opencode serve + REST API + SSE

适配器能力对比:

| 能力 | Claude | OpenCode | |------|--------|----------| | streaming | chunked | delta | | session-resume | ✓ | ✓ | | system-prompt | ✓ | ✓ | | tool-restriction | ✓ | - | | mcp | ✓ | - | | subagents | ✓ | - | | worktree | ✓ | - | | serve-mode | - | ✓ |

开发

npm install
npm run build       # 构建(ESM + CJS + DTS)
npm test            # 运行测试
npm run dev         # watch 模式构建
npm run playground  # 启动 Playground

Changelog

v0.8.0

改进

  • timeoutMs 语义变更为空闲超时timeoutMs 不再表示进程的绝对超时时间,而是"空闲超时":进程无任何输出(stdout/stderr)超过此时间才触发终止。每次收到输出会重置计时器,更适合 LLM 调用耗时不可预测的场景
  • 默认空闲超时调整 — 默认值从 300_000(5 分钟)调整为 120_000(2 分钟)

破坏性变更

  • timeoutMs 语义变更 — 从绝对超时改为空闲超时(idle timeout)。如果依赖旧的绝对超时行为,需要调整使用方式。对于模型思考时间可能较长的场景,建议适当调大此值
  • 默认超时时间缩短 — 从 5 分钟缩短至 2 分钟。如需更长超时,请在 defaults.timeoutMs 或请求参数中显式设置

v0.7.0

新特性

  • InteractiveSession 多轮对话封装 — 新增 InteractiveSession 类,封装 per-turn spawn + --resume 模式,提供透明的多轮对话体验。通过 porygon.session() 创建,调用 session.send(prompt) 自动管理 sessionId 续接
  • EphemeralProcess 超时错误明确化 — 进程超时终止后抛出明确的 Process timed out after ${timeoutMs}ms 错误,替代之前模糊的退出码错误信息

改进

  • EphemeralProcess 内部新增 timedOut 状态标记,区分正常退出与超时终止场景

v0.6.0

改进

  • Windows 跨平台兼容 — 使用 cross-spawn 替换原生 child_process.spawn,解决 Windows 上因 PATH 解析、PATHEXT 扩展名(.exe.cmd 等)导致的 ENOENT 错误。所有进程启动(EphemeralProcessPersistentProcess)均已适配
  • isAvailable() Windows 适配 — Claude adapter 的 CLI 存在性检测在 Windows 上使用 where 命令替代 which

新增依赖

  • cross-spawn — Node.js 生态标准的跨平台进程启动库,正确处理 Windows 上的命令解析、参数转义和 PATHEXT 查找

v0.5.0

新特性

  • porygon.getTools(force?) — 获取当前可用的工具列表(包括内置工具和 MCP 插件工具)。首次调用通过最小化对话获取并缓存,后续直接返回缓存,传入 force: true 强制刷新
  • IAgentAdapter.getTools?(force?) — 适配器接口新增可选方法,支持缓存与强制刷新
  • onlyTools(请求参数) — 仅允许使用的工具白名单,内部通过 getTools() 获取全量列表后差集计算转为 disallowedTools,工具缓存避免重复对话开销
  • 配置级工具黑名单BackendConfigdefaults 新增 disallowedTools 字段,支持全局或后端级统一禁用工具,三层叠加去重
  • checkBackend(backend, options?) 深度检测模式 — 新增第二个参数 CheckBackendOptions,传入 { deep: true } 时在基础 CLI/版本检查通过后,向模型发送一条极短测试消息(maxTurns: 1),验证 Token、模型、配额是否真正可用
  • CheckBackendOptions — 新增选项类型,支持 deep(启用深度检测)、model(指定验证模型)、timeoutMs(深度检测超时,默认 15s)
  • HealthCheckResult.modelVerified — 新增字段,深度检测时标识模型是否真正响应

破坏性变更

  • PromptRequest.allowedTools 移除 — Claude CLI 的 --allowedTools 实际语义为"免确认"而非"限制范围",已移除避免误用。替代方案:onlyTools(白名单,自动转黑名单)或 disallowedTools(黑名单)
  • porygon.session() 返回类型变更 — 从 InteractiveSession 改为 Promise<InteractiveSession>,调用方需 await

Bug Fixes

  • Claude adapter 类型定义: 修复 ClaudeResultEvent 缺少 total_cost_usd 字段导致的 TS2322 类型错误

v0.4.0

Bug Fixes

  • Claude adapter: 修复 result 事件中 inputTokensoutputTokens 始终为 0 的问题。Claude CLI 的 token 统计位于 event.usage.input_tokens / event.usage.output_tokens,而非顶层的 event.input_tokens / event.output_tokens
  • Claude adapter: 修复 costUsd 始终为 undefined 的问题。Claude CLI 使用 total_cost_usd 字段名,而非 cost_usd

v0.3.0

Bug Fixes

  • Claude adapter: 将 prompt 从 CLI 参数 (-p <prompt>) 改为通过 stdin 传递 (--print + stdin pipe),修复超长 prompt 导致的偶发 403 "Request not allowed" 错误

改进

  • SpawnOptions 新增 stdinData 字段,EphemeralProcess.executeStreaming 支持向子进程 stdin 写入数据

v0.2.0

新特性

  • checkBackend(backend) — 新增单后端健康检查方法,无需检查全部后端
  • HealthCheckResult — 健康检查返回扁平化结构 { available, version?, supported?, warnings?, error? }
  • BackendConfig.cliPath — Claude CLI 自定义路径,有类型提示
  • BackendConfig.interactive — 布尔值控制是否跳过权限确认
  • BackendConfig.serverUrl — OpenCode 远程服务地址(顶级字段)
  • BackendConfig.apiKey — API Key 认证(顶级字段)
  • AgentAssistantMessage.turnComplete — 标记 assistant 消息为 turn 完整文本汇总,流式消费者可据此跳过重复文本
  • AdapterCapabilities.streamingMode — 声明后端的流式模式("delta""chunked"
  • IAgentAdapter.deleteSession? — 可选的会话删除方法(接口已定义,适配器待实现)
  • tool_result 映射 — Claude 的 tool_result 内容块现在映射为带 output 字段的 tool_use 消息
  • session_id 自动提取 — Claude 原始事件中的 session_id 自动映射到 AgentMessage.sessionId
  • CJS 输出 — 同时构建 ESM 和 CJS 格式,支持 Electron 等 CJS 环境

破坏性变更

  • mapClaudeEvent() 返回类型变更 — 从 AgentMessage | null 改为 AgentMessage[](仅影响直接调用 mapper 的代码)
  • Claude 消息流变更assistant 事件拆分为 stream_chunk[] + assistant(turnComplete: true)。之前同时累加 assistantstream_chunk 文本的代码需要调整:只累加 stream_chunk,或检查 turnComplete 标记
  • healthCheck() 返回类型变更 — 从 { available, compatibility: CompatibilityResult | null, error? } 改为 HealthCheckResult{ available, version?, supported?, warnings?, error? }),移除嵌套的 compatibility 字段
  • AdapterCapabilities 新增必填字段streamingMode: "delta" | "chunked" 为必填,自定义适配器需添加

改进

  • mergeRequest() 添加详细 JSDoc 注释说明配置合并策略
  • mapAssistantContent() 支持多内容块拆分,不再丢失 tool_use 块

v0.1.0 — 初始版本

  • 基础 Facade + Adapter 架构
  • Claude Code CLI 适配器(claude -p --output-format stream-json
  • OpenCode 适配器(opencode serve + REST API + SSE)
  • 输入/输出拦截器流水线
  • 防护拦截器(prompt 注入检测、敏感信息过滤)
  • 进程生命周期管理(EphemeralProcess / PersistentProcess)
  • 会话管理与恢复
  • Zod 配置校验
  • ESM 输出