# @littletree/sirius-agent-core-sdk

Sirius Agent Core SDK — 独立的 Agent 运行时 SDK，支持多 provider、工具管理、权限、Hook 生命周期、Telemetry、Subagent、Memory、Workflow 编排。

## 安装

```bash
bun add @littletree/sirius-agent-core-sdk
# 或
npm install @littletree/sirius-agent-core-sdk
```

## 快速开始

```ts
import { Agent } from "@littletree/sirius-agent-core-sdk"

const agent = new Agent({
  gatewayConfig: {
    apiKey: "sk-...",
    baseURL: "https://api.openai.com/v1",
  },
  modelConfig: { model: "gpt-4o" },
  workdir: "/repo",
})

const result = await agent.run({
  messages: [{ role: "user", content: "Hello!" }],
  sessionId: "my-session",
})

console.log(result.text)
```

---

## 核心概念

### Agent.run vs Agent.query

SDK 提供两个层次的调用入口：

| 方法 | 用途 | 循环 | 返回值 |
|---|---|---|---|
| `agent.query(input)` | **单次 LLM 请求**（一问一答） | 不循环 | `AgentQueryResult` |
| `agent.run(input)` | **多轮对话循环**（自动处理 tool calls + 继续） | 自动循环 | `AgentRunResult`（含 session/turns/摘要） |

```ts
// query：单次调用，适合一次性的 LLM 问答
const q = await agent.query({
  messages: [{ role: "user", content: "What is 2+2?" }],
})
console.log(q.text) // "4"

// run：多轮循环，适合需要 tool calls 的复杂任务
const r = await agent.run({
  messages: [{ role: "user", content: "Read README.md and summarize" }],
  sessionId: "task-1",
})
// r.turns — 每轮结果
// r.toolCallCount — 全程工具调用数
// r.stopReason — 终止原因
```

---

## Agent.run 完整参数

```ts
interface AgentRunInput {
  // === 必填 ===
  messages: AgentQueryMessage[]   // 对话历史

  // === 模型配置 ===
  model?: string                  // 覆盖 AgentOptions 的 model
  temperature?: number            // 采样温度（0-2），低→确定，高→随机
  topP?: number                   // 核采样阈值（0-1）
  maxTokens?: number              // 最大输出 token 数

  // === 会话控制 ===
  sessionId?: string              // 会话 ID（省略时自动生成 UUID）
  recursionDepth?: number         // 递归深度基数（默认 0）
  toolChoice?: "auto" | "required" | "none" | { type: "function"; function: { name: string } }
  allowedRules?: string[]         // 临时权限规则（追加到已有规则）
  abortSignal?: AbortSignal       // 取消信号

  // === 上下文注入 ===
  systemPrompt?: string           // 系统提示词（覆盖 AgentOptions）
  tools?: ChatCompletionFunctionTool[]  // 额外工具（追加到 ToolManager）
  toolContext?: ToolContext       // 工具上下文（workdir/sessionId 等）

  // === Memory 记忆系统（自动召回） ===
  memory?: AgentMemoryHook        // 传则每轮 query 前自动 recall 注入 systemPrompt

  // === 循环控制 ===
  continuationPrompt?: string     // 截断续写提示（默认自动）
  textLoopRecovery?: {           // 文本循环检测与恢复
    triggerCount?: number        // 触发阈值（连续重复次数，默认 3）
    maxRecoveries?: number       // 最大恢复次数（默认 2）
    mildPrompt?: string          // 轻度恢复提示词
    strongPrompt?: string        // 强度恢复提示词
  }

  // === 实时流回调 ===
  onStreamEvent?: (event: NativeStreamEvent) => void | Promise<void>
}
```

### 温度（temperature）使用指南

| 场景 | 建议值 | 说明 |
|---|---|---|
| 代码生成 / 精确提取 | 0 ~ 0.1 | 输出确定性高，减少幻觉 |
| 代码理解 / 分析 | 0 ~ 0.2 | 结构化分析保持一致性 |
| 多轮 Tool calling | 0 ~ 0.1 | 子任务降低随机性 |
| 创意写作 | 0.7 ~ 1.0 | 增加多样性和创意 |
| 通用对话 | 0.5 ~ 0.7 | 平衡准确性与多样性 |
| 最大随机性 | 1.0 ~ 2.0 | 很少使用，仅限创意场景 |

```ts
// 精确代码任务
const r = await agent.run({
  messages: [{ role: "user", content: "Refactor this module" }],
  temperature: 0.1,
})

// 创意写作
const r = await agent.run({
  messages: [{ role: "user", content: "Write a blog post" }],
  temperature: 0.8,
  topP: 0.9,
})
```

---

## 工具调用钩子（onToolCall / onToolResult）

Agent.run 提供两个工具调用钩子，用于外部观察和干预每次工具调用：

```ts
const result = await agent.run({
  messages: [...],
  // 工具调用前拦截（可干预）
  onToolCall: (ctx) => {
    // ctx.toolName — 工具名
    // ctx.args — 原始参数
    // 返回 void → 正常执行
    // 返回 { allow: false, reason } → 阻止执行
    // 返回 { modifiedArgs } → 改写参数后执行
  },
  // 工具调用后观察（只读）
  onToolResult: (ctx) => {
    // ctx.toolName — 工具名
    // ctx.args — 实际执行的参数
    // ctx.result — 成功时的返回值
    // ctx.error — 失败时的错误
    // ctx.durationMs — 执行耗时（ms）
    // ctx.startedAt — 开始时间戳
  },
})
```

### 典型用例

```ts
// 1. 自定义入参校验
agent.run({
  messages: [...],
  onToolCall: (ctx) => {
    if (ctx.toolName === "write") {
      const args = ctx.args as { content: string }
      if (!args.content?.trim()) {
        return { allow: false, reason: "content 不能为空" }
      }
    }
  },
})

// 2. 路径安全加固
agent.run({
  messages: [...],
  onToolCall: (ctx) => {
    if (ctx.toolName === "write" || ctx.toolName === "edit") {
      const args = ctx.args as { filePath: string }
      if (args.filePath?.includes("../")) {
        return { allow: false, reason: "路径穿越被拦截" }
      }
    }
  },
})

// 3. 可观测性：记录所有工具调用耗时
const spans: Array<{ toolName: string; status: string; durationMs: number }> = []
agent.run({
  messages: [...],
  onToolResult: (ctx) => {
    spans.push({
      toolName: ctx.toolName,
      status: ctx.error ? "error" : "ok",
      durationMs: ctx.durationMs,
    })
  },
})
```

### 语义对照

| 特性 | onToolCall | onToolResult |
|---|---|---|
| 可干预 | ✅（阻止/改写参数） | ❌（纯观察，副作用已产生） |
| 异常处理 | 钩子自身抛错 → SDK catch + log warning，不阻断工具执行 | 同左 |
| 性能 | 同步调用，无显著开销 | 同步调用，无显著开销 |
| 触发时机 | 每次工具执行前 | 每次工具执行后（包括阻止） |

---

## AgentRunResult 返回结构

```ts
interface AgentRunResult {
  // === 文本输出 ===
  text: string                    // 全程累积文本
  reasoning: string              // 全程累积推理内容

  // === 执行摘要（诊断信号） ===
  turnCount?: number             // 总轮数 = turns.length
  toolCallCount?: number         // 全程工具调用次数
  toolNames?: string[]           // 全程调用的工具名（按序，不去重）
  stopReason?: AgentRunStopReason // 终止原因

  // === 详细数据 ===
  session: AgentSession          // 会话摘要（id/status/steps）
  turns: AgentQueryResult[]      // 每轮详细结果
  events: AgentEvent[]           // 全部事件流（onStreamEvent 模式下为空,见下文）
  usage?: { inputTokens; outputTokens; totalTokens }
  toolCalls: AgentEvent[]        // 全部工具调用（独立累积,不受 onStreamEvent 影响）
  toolResults: AgentEvent[]      // 全部工具结果
  toolErrors: AgentEvent[]       // 全部工具错误
}
```

### 内存优化:onStreamEvent 流式消费

长会话/多轮 tool calling 场景下,`events[]` 全量累积是 OOM 主因(每轮的 text-delta/reasoning-delta/tool-input-delta 都进数组,且与 turns 三份重叠)。

传入 `onStreamEvent` 后,SDK **跳过 `events[]` 累积**——事件流式交付给回调即丢弃,不再保留。`toolCalls`/`toolResults`/`toolErrors` 改为独立累积,不依赖 events,故诊断字段照常可用。

```ts
// 流式消费 + 省内存:events 为空,但 toolCalls/stopReason 等诊断字段仍可用
const result = await agent.run({
  messages: [...],
  onStreamEvent: (event) => {
    // 实时处理:text-delta/tool-call/tool-result...
    // 事件在此消费后,SDK 不再保留(events[] 为空)
  },
})
expect(result.events).toHaveLength(0)      // 省内存
expect(result.toolCalls).toHaveLength(3)  // 仍可用
expect(result.stopReason).toBe("completed")
```

> 不传 `onStreamEvent` 时行为不变,`events[]` 正常累积(向后兼容)。

### stopReason 含义

| 值 | 含义 |
|---|---|
| `"completed"` | 正常结束 |
| `"max-turns"` | 触顶 maxTurns |
| `"aborted"` | abortSignal 触发 / 空 run |
| `"error"` | 异常终止 |
| `"truncated"` | finishReason="length" 且未续跑 |
| `"text-loop-recovery"` | 文本循环兜底退出 |

```ts
// 利用摘要判断是否正常工作
const r = await agent.run({ messages, maxTurns: 10 })
if (r.toolCallCount === 0 && r.turnCount === 1) {
  console.log("模型直接回答了，没有调工具")
}
if (r.stopReason === "text-loop-recovery") {
  console.log("模型进入了循环，已被自动恢复")
}
```

---

## 工具管理（ToolManager）

```ts
import { ToolManager } from "@littletree/sirius-agent-core-sdk"

const tools = new ToolManager()

// 注册自定义工具
tools.register({
  name: "get_weather",
  config: {
    type: "function",
    function: {
      name: "get_weather",
      description: "Get weather for a city",
      parameters: {
        type: "object",
        properties: { city: { type: "string" } },
        required: ["city"],
      },
    },
  },
  execute: async (args) => ({ success: true, content: `Weather in ${args.city}: sunny` }),
})

// 并发安全标记
tools.register({
  name: "read",
  config: { type: "function", function: { name: "read", parameters: { type: "object" } } },
  isConcurrencySafe: true,  // 只读工具，可并行
  execute: async () => ({ success: true, content: "..." }),
})

// 延迟暴露工具（工具数超阈值时不全量注入 schema）
tools.register({
  name: "specialized_tool",
  config: { type: "function", function: { name: "specialized_tool", parameters: {} } },
  deferred: true,  // 非核心工具，通过 tool_search 按需检索
  execute: async () => ({ success: true, content: "..." }),
})

const agent = new Agent({
  gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
  modelConfig: { model: "gpt-4o" },
  toolManager: tools,
  workdir: "/repo",
})
```

### ToolPlugin 接口参考

```ts
interface ToolPlugin {
  name: string
  config: ChatCompletionFunctionTool
  execute: (args: Record<string, unknown>, context: ToolContext) => Promise<ToolResult>
  permissionGate?: "sdk" | "host"     // 权限门控
  isConcurrencySafe?: boolean         // 并发安全标记
  deferred?: boolean                  // 延迟暴露
  formatCompactParams?: (params, ctx) => string
  prompt?: (args?) => string
}
```

---

## 权限管理（PermissionManager）

```ts
import { PermissionManager } from "@littletree/sirius-agent-core-sdk"

const permission = new PermissionManager({
  workdir: "/repo",
  permissionMode: "default",
  // 自定义权限规则
  rules: [
    { permission: "Bash", pattern: "git status*", action: "allow" },
    { permission: "Bash", pattern: "npm install*", action: "ask" },
    { permission: "Write", pattern: "/repo/src/*", action: "allow" },
  ],
})

const agent = new Agent({
  gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
  modelConfig: { model: "gpt-4o" },
  permissionManager: permission,
  workdir: "/repo",
})
```

### 五种权限模式

| 模式 | 行为 |
|---|---|
| `default` | 默认模式：受限工具询问用户 |
| `acceptEdits` | 接受所有编辑操作 |
| `bypassPermissions` | 跳过所有权限检查 |
| `plan` | Plan 模式：只读 + 仅可编辑 plan 文件 |
| `dontAsk` | 永不询问，自动决策 |

```ts
// 安全区路径（v0.5.2+ 通过 realpath 解析符号链接，防 symlink 逃逸）
permission.isPathInSafeZone("/repo/src/file.ts")  // true
permission.isPathInSafeZone("/etc/passwd")         // false
permission.isPathInSafeZone("/repo/escape-link")   // false（symlink → /etc 被 realpath 捕获）

// 临时规则
permission.addTemporaryRules(["Bash(git log*)"])
permission.clearTemporaryRules()

// 更新工作目录
permission.updateWorkdir("/new-project", ["/tmp/build"])
```

---

## Memory 记忆系统

Memory 提供 **自动召回** 能力：每轮 query 前从 MemoryStore 检索相关内容并注入 systemPrompt。

```ts
import { FileMemoryStore } from "@littletree/sirius-agent-core-sdk"

const store = new FileMemoryStore("/repo/.sirius/memory")

const result = await agent.run({
  messages: [{ role: "user", content: "继续之前的重构工作" }],
  memory: {
    store,
    // recallQuery: 自定义召回查询（默认取最后一条 user 消息）
    recallQuery: (messages) => messages[messages.length - 1].content,
    // format: 自定义注入格式（默认 <recalled_memories> 块）
    format: (hits) => `历史记忆：\n${hits.map(h => h.content).join("\n")}`,
  },
})
```

### MemoryStore 接口

```ts
interface MemoryStore {
  search(query: string): Promise<MemoryRecord[]>
  recall(query: string): Promise<MemoryRecord[]>   // Agent 自动召回
  save(record: MemoryRecord): Promise<void>
  delete(id: string): Promise<void>
}

interface MemoryRecord {
  id: string
  content: string
  metadata?: Record<string, unknown>
}
```

---

## Hook 生命周期

```ts
import { HookManager } from "@littletree/sirius-agent-core-sdk"

const hooks = new HookManager()

// 生命周期通知
hooks.on("SessionStart", (e) => console.log("启动:", e.sessionId))
hooks.on("SessionStop", (e) => console.log("停止:", e.status))

// 工具拦截
hooks.on("PreToolUse", (e) => console.log("调用前:", e.toolName))
hooks.on("PostToolUse", (e) => console.log("调用后:", e.toolName, e.status))

// Transform 类：修改请求参数
hooks.on("ChatParams", (e) => ({
  payload: { ...e.payload, temperature: 0.2 }
}))

hooks.on("ChatHeaders", (e) => ({
  payload: { headers: { ...e.payload.headers, "X-Custom": "value" } }
}))

// 系统提示词注入
hooks.on("SystemTransform", (e) => ({
  payload: { system: [...e.payload.system, "额外指令: ..."] }
}))

// 停止决策
hooks.on("Stop", (e) => {
  if (e.blockCount >= 3) return { preventContinuation: true }
  return { blockingError: "每次运行完测试再停" }
})

// 通知类（纯监听不决策）
hooks.on("PreCompact", (e) => console.log("压缩前:", e.trigger))
hooks.on("PostCompact", (e) => console.log("压缩后:", e.trigger))
hooks.on("PermissionDenied", (e) => console.log("权限被拒:", e.reason))

const agent = new Agent({
  gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
  modelConfig: { model: "gpt-4o" },
  hooks,
  workdir: "/repo",
})
```

### 完整 Hook 事件类型

| 事件 | 类型 | 说明 |
|---|---|---|
| SessionStart | 通知 | 会话启动 |
| StepStart | 通知 | 每轮开始 |
| PreToolUse | 拦截 | 工具调用前（可取消/改参数） |
| PostToolUse | 通知 | 工具调用后 |
| StepFinish | 通知 | 每轮结束 |
| SessionStop | 通知 | 会话结束 |
| ChatParams | Transform | 修改模型参数（temperature/topP/maxTokens） |
| ChatHeaders | Transform | 注入自定义 HTTP 头 |
| ChatMessage | Transform | 修改消息内容 |
| MessagesTransform | Transform | 修改完整消息数组 |
| SystemTransform | Transform | 修改系统提示词 |
| ToolDefinition | Transform | 修改工具定义 |
| SessionCompacting | Transform | 修改压缩上下文 |
| TextComplete | Transform | 修改补全文本 |
| ShellEnv | Config | 修改 Shell 环境变量 |
| CommandExecuteBefore | Config | 命令执行前预处理 |
| PermissionAsk | Decision | 权限询问决策 |
| ActorPreStop | Actor | 子代理停止前投票 |
| ActorPostStop | Actor | 子代理停止后投票 |
| Stop | Decision | 停止决策（block/continue） |
| PreCompact | 通知 | 压缩前通知 |
| PostCompact | 通知 | 压缩后通知 |
| SessionEnd | 通知 | 会话销毁 |
| PermissionDenied | 通知 | 权限被拒绝 |

---

## Subagent 管理

```ts
import { SubagentManager } from "@littletree/sirius-agent-core-sdk"

const mgr = new SubagentManager()

const handle = mgr.spawn({
  agent: new Agent({
    gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
    modelConfig: { model: "gpt-4o" },
    workdir: "/repo",
  }),
  messages: [{ role: "user", content: "Analyze the codebase" }],
  sessionId: "subagent-1",
})

console.log(mgr.status(handle.id))  // "running"
const result = await mgr.wait(handle.id)
mgr.cancel(handle.id)
console.log(mgr.list())
```

---

## MCP 服务器

```ts
import { registerMcpClient, registerMcpServer, toMcpToolName } from "@littletree/sirius-agent-core-sdk"

const tools = new ToolManager()

// 注册 MCP client
const unregister = await registerMcpClient(tools, mcpClient, {
  serverName: "my-server",
  clientName: "my-client",
})

// 工具名转换
const sdkName = toMcpToolName("server", "tool_name")
// → "mcp__server__tool_name"

unregister()
```

---

## Telemetry（OpenTelemetry）

```ts
import { TelemetryManager } from "@littletree/sirius-agent-core-sdk"

const telemetry = new TelemetryManager()

const agent = new Agent({
  gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
  modelConfig: { model: "gpt-4o" },
  telemetry,
  workdir: "/repo",
})

await agent.run({ messages: [...], sessionId: "..." })

// 查看 spans
const spans = telemetry.getSpans()
// spans 包含: session → interaction → step → llm → tool 等多层 span
```

---

## Workflow 编排

`agent.runWorkflow()` 运行动态 fan-out 子 agent 的 workflow 脚本，支持并行调度、预算控制、续跑。

```ts
const result = await agent.runWorkflow({
  script: `
export const meta = {
  name: "research",
  description: "Research a topic in parallel"
}

phase("Research")
const results = await parallel([
  () => agent("Search for topic A"),
  () => agent("Search for topic B"),
  () => agent("Search for topic C"),
])

phase("Synthesize")
const synthesis = await agent(
  "Synthesize: " + JSON.stringify(results.filter(Boolean))
)
log(synthesis)
  `,
  sessionId: "wf-1",
  budget: 500_000,           // token 预算
  maxConcurrency: 5,         // 最大并发 agent 数
  onProgress: (event) => console.log(event),
  onLog: (msg) => console.log(msg),
  // 续跑支持
  resumeFrom: savedJournal,
  onCheckpoint: (entry) => saveToDb(entry),
})

console.log(result.result)
console.log(result.phases)   // 各阶段状态
console.log(result.budget)   // 预算使用情况
```

### Workflow 工具调用钩子

`runWorkflow` 支持转发 `onToolCall` / `onToolResult` 给内部每个子 agent 的 `agent.run()`，用于在 workflow 编排层做工具级校验和追踪。语义与 [Agent.run 工具调用钩子](#工具调用钩子ontoolcall--ontoolresult) 一致。

```ts
// workflow 中拦截所有子 agent 的 write 工具
const result = await agent.runWorkflow({
  script: `...`,
  onToolCall: (ctx) => {
    if (ctx.toolName === "write") {
      const args = ctx.args as { content: string }
      if (!args.content?.trim()) {
        return { allow: false, reason: "content 不能为空" }
      }
    }
  },
  // 追踪每个子 agent 的工具调用耗时
  onToolResult: (ctx) => {
    console.log(`[${ctx.toolName}] ${ctx.durationMs}ms ${ctx.error ? "✗" : "✓"}`)
  },
})
```

> 注意：钩子对**每个子 agent 的每次工具调用**都触发，并行 agent 会并发调用钩子。钩子自身应保持无状态或线程安全。

### Workflow agent() 返回诊断字段

`agent()` 返回 `WorkflowAgentResult`（含主要输出 `result` + `diagnostics` 诊断），编排层据 `stopReason` + `toolCallCount` 判断 Agent 是否真干活，不再靠日志猜根因。

```ts
interface WorkflowAgentResult {
  result: unknown  // 无 schema → string;schema 命中 → 结构化对象
  diagnostics: {
    toolCallCount: number   // 全程工具调用次数。0 = 模型没调工具
    toolNames: string[]     // 全程调用的工具名（按序，不去重）
    stopReason: "completed" | "max-turns" | "aborted" | "truncated" | "error" | "text-loop-recovery"
    turnCount: number       // 总轮数，判断是否接近 maxTurns 上限
    usage?: { inputTokens: number; outputTokens: number; totalTokens: number }
  }
}
```

```ts
const wfResult = await agent.runWorkflow({ script: `...` })
// wfResult.result 是脚本 return 的值（通常是 agent() 返回的 WorkflowAgentResult）
const r = wfResult.result as WorkflowAgentResult
const d = r.diagnostics

if (d.stopReason === "max-turns" && d.toolCallCount === 0) {
  // Agent 耗尽 maxTurns 但零工具调用 → 模型完全没干活
  throw new Error("Agent 未调用任何工具（可能模型配置错误或网关空响应）")
}
if (d.stopReason === "max-turns" && !d.toolNames.includes("Write")) {
  // 调了工具但没写文件 → 陷入循环或任务未完成
  console.warn("Agent 耗尽 maxTurns 且未写文件，可能陷入循环")
}
console.log(`Agent 诊断: stopReason=${d.stopReason} toolCalls=${d.toolCallCount} tools=${d.toolNames.join(",")}`)
```

> 旧代码迁移：原本 `const r = await agent(prompt)` 拿到 string/对象，现在用 `r.result` 取主要输出、`r.diagnostics` 取诊断。

### Workflow Memory 记忆透传

`runWorkflow` 支持转发 `memory`（`AgentMemoryHook`）给内部每个子 agent 的 `agent.run()`，使 workflow 子 agent 也能每轮自动召回历史记忆注入 systemPrompt。语义同 [Memory 记忆系统](#memory-记忆系统)。

```ts
import { FileMemoryStore } from "@littletree/sirius-agent-core-sdk"

const store = new FileMemoryStore("/repo/.sirius/memory")
const wfResult = await agent.runWorkflow({
  script: `...`,
  memory: {
    store,
    recallQuery: (messages) => messages[messages.length - 1]?.content,
    format: (hits) => `历史记忆:\n${hits.map((h) => h.content).join("\n")}`,
  },
})
```

---

## Context 压缩（compact 模块）

SDK 导出纯函数 + LLM 总结压缩工具,可在 `Agent.run` 内部自动跑(传 `AgentOptions.compact`),也可单独使用:

```ts
import {
  contextCollapse, snipCompact, microCompact,         // 纯本地裁剪(不调 LLM)
  autoCompact, reactiveCompact, isPromptTooLongError, // LLM 总结 + 413 恢复
  createAutoCompactState, buildCompactMessages,
  COMPACT_SYSTEM_PROMPT, type AutoCompactOptions,
} from "@littletree/sirius-agent-core-sdk"

const state = createAutoCompactState()
const opts: AutoCompactOptions = { contextWindow: 200_000, compactBuffer: 13_000 }

// 每轮前本地裁剪(不调 LLM,无副作用)
const collapsed = contextCollapse(messages)   // 渐进式折叠旧轮次
const snipped = snipCompact(messages)         // 预防性裁剪旧消息
const micro = microCompact(messages)          // 清理旧 tool result

// 阈值触发 LLM 总结(summarize 由调用方注入)
const ac = await autoCompact(messages, lastUsage, state, opts, async (msgs) => {
  const result = await agent.query({
    messages: buildCompactMessages(msgs),
    systemPrompt: COMPACT_SYSTEM_PROMPT,
    toolChoice: "none", tools: [],
  })
  return result.text
})
if (ac.wasCompacted) messages = ac.messages

// 413 prompt-too-long 恢复(尾部剥离 + 总结,含 hasAttempted 防螺旋)
if (isPromptTooLongError(err)) {
  const rc = await reactiveCompact(messages, err, state, opts, false, summarize)
  if (rc.recovered) messages = rc.messages
}
```

| 函数 | 类型 | 作用 |
|---|---|---|
| `contextCollapse` | 纯本地 | 渐进式折叠旧轮次(提取关键词,不调 LLM) |
| `snipCompact` | 纯本地 | 预防性裁剪旧消息 |
| `microCompact` | 纯本地 | 清理旧 tool result |
| `autoCompact` | LLM 总结 | 阈值(contextWindow - compactBuffer)触发 LLM 总结,含 circuit breaker(连续失败 3 次跳过) |
| `reactiveCompact` | LLM 总结 | 413 prompt-too-long 恢复(保留前 10% + 后 20%,中间丢弃,再总结) |
| `isPromptTooLongError` | 断言 | 检测错误是否为 prompt-too-long(413/context length exceeded) |

## Skill 系统

SDK 提供 skill 发现、注册与渲染:

```ts
import {
  SkillRegistry, parseSkillMarkdown, renderAvailableSkills,
  type SkillDefinition,
} from "@littletree/sirius-agent-core-sdk"

const registry = new SkillRegistry()
const skill = parseSkillMarkdown("# My Skill\n---\nname: my-skill\ndescription: ...") // 解析 frontmatter
registry.register(skill)

const skills = registry.list()                  // SkillDefinition[]
const promptSection = renderAvailableSkills(skills) // 渲染 available_skills prompt 段
```

`AgentOptions.skillRegistry` 传入后,`Agent.run` 自动把已注册 skill 列表注入 systemPrompt(让模型知道有哪些 skill 可用,配合 `createSkillTool` 让模型主动加载 skill body)。

## Structured Output（结构化输出）

`Agent.run` 支持结构化输出:强制 `toolChoice` 指向终止型工具,模型调用该工具后 run 终止(`stopAfterToolResult`),结构化结果在 tool result 的 `metadata.agentRunStop`。

```ts
const result = await agent.run({
  messages,
  toolChoice: { type: "function", function: { name: "StructuredOutput" } },
  structuredOutputTool, // 终止型 ToolPlugin:执行后 metadata.agentRunStop = true
})
// result.stopReason === "completed"(终止型工具 break)
// 结构化数据在 result.toolResults[0].output
```

`finishReason="length"` 时 `Agent.run` 自动注入 continuation prompt 续跑;`text-loop recovery` 检测连续相同输出并注入 recovery prompt。

## AgentRegistry（Agent 注册表）

```ts
import { AgentRegistry, createAgentRegistryFromConfig } from "@littletree/sirius-agent-core-sdk"

// 从配置创建 registry
const registry = createAgentRegistryFromConfig({
  agents: {
    code_reviewer: {
      description: "Expert code reviewer",
      systemPrompt: "You are a senior code reviewer...",
      model: "claude-sonnet-5",
      temperature: 0.1,
    },
    architect: {
      description: "System architect",
      systemPrompt: "You are a software architect...",
      model: "claude-opus-4-8",
    },
  },
})

// 查找 agent 配置
const reviewerConfig = registry.get("code_reviewer")
console.log(reviewerConfig?.model)        // "claude-sonnet-5"
console.log(reviewerConfig?.temperature)  // 0.1
```

---

## Plugin 系统

```ts
import { applyPluginPackage, unapplyPluginPackage } from "@littletree/sirius-agent-core-sdk"

// 应用插件包
const applied = await applyPluginPackage({
  plugin: myPlugin,
  tools: toolManager,
  hooks: hookManager,
  telemetry: telemetryManager,
})

// 卸载
unapplyPluginPackage(applied)
```

---

## 自定义网关

### 禁用 thinking（DeepSeek V4）

```ts
const agent = new Agent({
  gatewayConfig: {
    apiKey: "sk-...",
    baseURL: "http://gateway.example.com/v1",
    extraBody: { enable_thinking: false },
  },
  modelConfig: { model: "deepseek-v4" },
  workdir: "/repo",
})
```

### 自定义 headers

```ts
const agent = new Agent({
  gatewayConfig: {
    apiKey: "sk-...",
    baseURL: "http://gateway.example.com/v1",
    defaultHeaders: {
      "custom-provider-name": "base64encoded",
      "feature-phase-name": "testCaseGeneration",
    },
  },
  modelConfig: { model: "dsv4pro" },
  workdir: "/repo",
})
```

---

## Plan 模式

```ts
import { PermissionManager } from "@littletree/sirius-agent-core-sdk"

const permission = new PermissionManager({
  planFilePath: "/repo/.sirius/plans/plan.md",
})

const agent = new Agent({
  gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
  modelConfig: { model: "gpt-4o" },
  workdir: "/repo",
  permissionManager: permission,
  // 通过 permissionMode 启动 plan 模式，不推荐硬编码
})
```

---

## 流适配器（Stream Adapter）

SDK 提供多种流适配器用于集成不同的 Provider：

```ts
import {
  toChatCompletionsStream,
  nativeStream,
  serializeAgentEvents,
  raceWithAbort,
} from "@littletree/sirius-agent-core-sdk"

// chat-completions 端点适配
// nativeStream — 原生流（支持 Anthropic/OpenAI 多 provider）
// serializeAgentEvents — 事件序列化
// raceWithAbort — 竞速取消
// toolExecution — 工具执行中间件
```

---

## 常量

```ts
import {
  RESTRICTED_TOOLS,
  SAFE_COMMANDS,
  DANGEROUS_COMMANDS,
  DEFAULT_ALLOWED_RULES,
  ASK_USER_QUESTION_TOOL_NAME,
  BASH_TOOL_NAME,
} from "@littletree/sirius-agent-core-sdk"
```

---

## API 概览

| 模块 | 主要导出 |
|---|---|
| Agent | `Agent`（run/query/runWorkflow）、`AgentRunResult`、`AgentQueryResult`、`AgentSession`、`AgentStep` |
| ToolManager | `ToolManager`（register/execute/list/unregister/getToolsConfig/getToolsConfigWithDeferred） |
| PermissionManager | `PermissionManager`（checkPermission/isRestrictedTool/isPathInSafeZone） |
| HookManager | `HookManager`（on/emit）、24 种 AgentHookEvent |
| TelemetryManager | `TelemetryManager`（record/getSpans/init） |
| SubagentManager | `SubagentManager`（spawn/wait/cancel/status/list） |
| Memory | `FileMemoryStore`、`MemoryStore`、`AgentMemoryHook` |
| MCP | `registerMcpClient`、`registerMcpServer`、`toMcpToolName` |
| Workflow | `agent.runWorkflow`（fan-out 子 agent 编排） |
| AgentRegistry | `AgentRegistry`、`createAgentRegistryFromConfig` |
| Plugin | `applyPluginPackage`、`unapplyPluginPackage` |
| Stream Adapter | `toChatCompletionsStream`、`nativeStream`、`serializeAgentEvents`、`raceWithAbort` |
| Deferred Tools | `decideDeferredTools`、`searchDeferredTools` |
| Constants | 权限常量、安全命令、默认规则 |

## 配套工具包

本 SDK 提供 Agent 运行时，工具实现由 `@littletree/sirius-tools` 提供：

```bash
bun add @littletree/sirius-tools
```

```ts
import { Agent, ToolManager } from "@littletree/sirius-agent-core-sdk"
import {
  createNodeReadTool, createNodeWriteTool, createNodeEditTool,
  createNodeGrepTool, createNodeGlobTool, createSimpleBashTool,
} from "@littletree/sirius-tools"

const tools = new ToolManager()
tools.register(createNodeReadTool({ workdir: "/repo" }))
tools.register(createNodeWriteTool({ workdir: "/repo" }))
tools.register(createNodeGrepTool({ workdir: "/repo" }))
tools.register(createSimpleBashTool({ workdir: "/repo" }))

const agent = new Agent({
  gatewayConfig: { apiKey: "sk-...", baseURL: "https://api.openai.com/v1" },
  modelConfig: { model: "gpt-4o" },
  toolManager: tools,
  workdir: "/repo",
})
```

## License

MIT
