---
name: engine-design
description: Engine 职责定义：per-session-round 生命周期管理器。驱动 tool call 循环，管理单个 Session 的多轮对话。不感知树结构，不感知调度。
---

# Engine — Per-Session-Round 生命周期管理器

## 定位

Engine 是 Session 原语之上的**单 Session 多轮对话管理器**。

- Session = 一次 LLM 调用（原子操作）
- Engine = 一个 Session 的整轮交互生命周期（多次 turn，含 tool call 循环）
- Factory = 装配层（构建 Engine，内联 consolidation 触发逻辑）

Engine 不感知树结构，不知道其他 Session 的存在，**也不感知调度策略**。

---

## Engine 做什么 / 不做什么

**做**：tool call 循环、consolidate() 执行、hooks fire-and-forget、生命周期边界（enter/leave/archive/fork）、fork 编排（拓扑 + session 创建）、内置 tool 注册与执行（通过 CompositeToolRuntime 统一调度）、error 事件 emit

**不做**：调度时机判断（由 Factory hook 内联）、持有跨 Session 状态（全局反思层由应用层在 StelloAgent 之外实现）、Session 切换检测（Orchestrator）、多 Session 管理

---

## 核心设计决策

### Engine 接管 Fork 编排

Engine 负责 fork 的完整编排：创建拓扑节点（topology-first，生成 ID）→ 调用 `session.fork({ id, ... })` 创建 session 实例 → 触发事件。session.fork() 天然处理 systemPrompt 继承、context 继承（含 contextFn）、prompt 写入、LLM/tools/consolidateFn/compressFn 覆盖。Orchestrator 分离"拓扑父节点"（策略决定）与"fork 来源 session"（继承内容来源）。

fork 选项中的 `consolidateFn` 和 `compressFn` 遵循继承链：fork 时指定则用新的，不指定则继承父 session 的。Agent 配置中的 `session.consolidateFn` 作为根 session 的默认值，后续嵌套 fork 按继承链传递。这使不同 session 可以有不同的 L3→L2 提炼策略和上下文压缩策略。

内置 tool（stello_create_session、activate_skill）在 Engine 构造时通过 `createBuiltinToolEntries()` 生成 `ToolRegistryEntry` 实例，闭包捕获 Engine 上下文，与用户 tool 统一走 `CompositeToolRuntime` 调度。LLM 调用 stello_create_session 时，Engine 解析 ForkProfile（如有），合成 systemPrompt，profile 的 contextFn/llm/tools 直接映射到 fork 选项，profile.skills 白名单写入 session metadata（`_stello.allowedSkills`），再走 forkSession 完整路径。Factory 创建子 Engine 时读取 metadata，按需用 FilteredSkillRouter 包装全局 SkillRouter。

### 工具注册与内置工具

内置 tool 和用户 tool 统一走 `ToolRegistryEntry` + `CompositeToolRuntime`。Engine 构造时自动创建内置 entries（闭包捕获 Engine 实例），与用户 `EngineToolRuntime` 组合。`getToolDefinitions` 和 `executeTool` 均委托给 CompositeToolRuntime，内置 tool 优先、同名去重。用户无需手动注册内置 tool。

### Consolidation 触发内联到 Factory hook

Engine 不持有 Scheduler，也不感知"全局反思"概念。Consolidation 触发逻辑（如 `consolidateEveryNTurns`）由 Factory 构建闭包注入 EngineHooks。Engine 在事件点 fire-and-forget 调用 hooks，不知道背后有调度。

### turn() 返回值

`EngineTurnResult` 只包含 `{ turn }`。调度是 fire-and-forget 的内部副作用，结果对调用方不可见。

### 所有 hooks fire-and-forget

hooks 抛错时 emit error 事件 + 调用 onError hook，不中断对话周期。Scheduler 闭包失败同理。

### Factory 合并 hooks

用户 hooks 和 Factory 内联 hooks 在同一 key 下都能触发，由 Factory 的 mergeHooks 保证。

---

## 错误处理原则

- session.stream() 失败 → 向上抛出（核心路径；turn() 也聚合该流）
- tool.execute() 失败 → 错误信息作为 tool result 返回给 LLM，继续循环
- hook / Scheduler 闭包失败 → emit error，不影响 turn() 返回

---

## Streaming + Tool Call 策略

- `session.stream()` 是 Engine 驱动 LLM 的唯一底层原语，每个 tool continuation 都继续使用 stream。
- `stream()` 按产生顺序输出所有 LLM 子轮的文本 chunk；工具执行期间自然暂停，下一轮继续输出。
- `turn()` 复用同一个 streaming tool loop，但使用空输出 sink 聚合结果，不向调用方暴露 chunk。
- `finalContent` / `rawResponse` 始终只代表最后一个不再请求客户端 tool 的 LLM 响应；usage 聚合整轮内所有 LLM 调用。
