Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • Session

Session

Session 是 OpenCode 中的核心会话对象,表示一个完整的对话上下文。

概述

Session 代表一次完整的对话交互,包含了从创建到当前状态的所有信息。它支持会话分支(通过 parentID)、压缩归档、分享和回滚等功能。

定义位置

packages/opencode/src/session/index.ts:42-82

属性说明

属性名类型必填说明
idstring是会话唯一标识符,格式为 sess_xxxxx
slugstring是URL友好的唯一标识符
projectIDstring是所属项目ID
directorystring是工作目录路径
parentIDstring否父会话ID(用于会话分支)
titlestring是会话标题
versionstring是OpenCode版本号
summaryobject否会话摘要信息
summary.additionsnumber-新增代码行数
summary.deletionsnumber-删除代码行数
summary.filesnumber-修改文件数
summary.diffsFileDiff[]-具体差异详情
shareobject否分享信息
share.urlstring-分享URL
timeobject是时间戳信息
time.creatednumber-创建时间(Unix时间戳)
time.updatednumber-最后更新时间(Unix时间戳)
time.compactingnumber-压缩时间(可选)
time.archivednumber-归档时间(可选)
permissionPermissionRuleset否权限规则集合
revertobject否回滚信息
revert.messageIDstring-要回滚到的消息ID
revert.partIDstring-要回滚到的Part ID(可选)
revert.snapshotstring-快照ID(可选)
revert.diffstring-差异字符串(可选)

TypeScript 类型定义

export type Session = {
  id: string
  slug: string
  projectID: string
  directory: string
  parentID?: string
  summary?: {
    additions: number
    deletions: number
    files: number
    diffs?: Array<FileDiff>
  }
  share?: {
    url: string
  }
  title: string
  version: string
  time: {
    created: number
    updated: number
    compacting?: number
    archived?: number
  }
  permission?: PermissionRuleset
  revert?: {
    messageID: string
    partID?: string
    snapshot?: string
    diff?: string
  }
}

事件

Session 支持以下事件:

事件名类型说明
session.createdinfo: Session会话创建
session.updatedinfo: Session会话更新
session.deletedinfo: Session会话删除
session.diffsessionID, diff: FileDiff[]会话差异更新
session.errorsessionID?, error会话错误

存储键结构

;["session", projectID, sessionID]

典型使用场景

1. 创建新会话

const session = await Session.create({
  title: "Fix bug in login",
  permission: permissionRuleset,
})

console.log(session.id) // sess_abc123

2. 创建子会话(分支)

const childSession = await Session.create({
  parentID: parentSession.id,
  title: "Alternative approach",
})

console.log(childSession.parentID) // sess_parent123

3. 更新会话标题

await Session.update(session.id, (draft) => {
  draft.title = "Updated title: Fix bug in login"
})

4. 获取会话的子会话

const children = await Session.children(session.id)
console.log(`Found ${children.length} child sessions`)

5. 删除会话(级联删除所有子会话)

await Session.remove(session.id)
// 这将删除会话及其所有消息、parts 和子会话

父子会话关系

OpenCode 支持会话树状结构,通过 parentID 建立父子关系。

parentID 字段

定义位置:packages/opencode/src/session/index.ts:48

parentID?: string  // 父会话 ID,根会话为空

作用:

  • 建立会话层级关系
  • 支持会话分支和嵌套
  • 创建子会话时自动设置

会话树结构

获取子会话

方法:packages/opencode/src/session/index.ts:325

export const children = fn(Identifier.schema("session"), async (parentID) => {
  const project = Instance.project
  const result = [] as Session.Info[]
  for (const item of await Storage.list(["session", project.id])) {
    const session = await Storage.read<Info>(item)
    if (session.parentID !== parentID) continue
    result.push(session)
  }
  return result
})

使用示例:

// 获取根会话的所有子会
const rootSession = await Session.get("sess_root123")
const children = await Session.children(rootSession.id)

console.log(`Found ${children.length} child sessions`)
for (const child of children) {
  console.log(`- ${child.title} (ID: ${child.id})`)
}

级联删除

删除会话时会级联删除所有子会话:

实现:packages/opencode/src/session/index.ts:336-41

export const remove = fn(Identifier.schema("session"), async (sessionID) => {
  const project = Instance.project
  const session = await get(sessionID)

  // 递归删除所有子会话
  for (const child of await children(sessionID)) {
    await remove(child.id)
  }

  // 删除会话的消息和 parts
  for (const msg of await Storage.list(["message", sessionID])) {
    for (const part of await Storage.list(["part", msg.at(-1)!])) {
      await Storage.remove(part)
    }
    await Storage.remove(msg)
  }

  // 删除会话本身
  await Storage.remove(["session", project.id, sessionID])
})

示例:

// 删除根会话,会自动删除所有子会话
await Session.remove(rootSession.id)
// 无需手动删除每个子会话

会话标题自动生成

实现:packages/opencode/src/session/index.ts:32-34

function createDefaultTitle(isChild = false) {
  return (isChild ? "Child session - " : "New session - ") + new Date().toISOString()
}

使用:packages/opencode/src/session/index.ts:206

title: input.title ?? createDefaultTitle(!!input.parentID)
  • 根会话标题:New session - 2025-01-26T10:30:00.000Z
  • 子会话标题:Child session - 2025-01-26T10:30:00.000Z

上下文继承

子会话继承父会话的部分上下文,同时拥有独立的消息历史。

继承的全局上下文

所有会话(父和子)通过 Instance 单例共享以下全局上下文:

Instance 定义:packages/opencode/src/project/instance.ts

export const Instance = {
  get directory() {
    return context.use().directory // 当前工作目录
  },
  get worktree() {
    return context.use().worktree // 项目根目录
  },
  get project() {
    return context.use().project // 项目信息
  },
}

示例值:

  • Instance.directory: /home/user/project/.opencode
  • Instance.worktree: /home/user/project
  • Instance.project.id: proj_abc123

Session 创建时的上下文传递

实现:packages/opencode/src/session/index.ts:199-213

const result: Info = {
  id: Identifier.descending("session", input.id),
  slug: Slug.create(),
  version: Installation.VERSION,
  projectID: Instance.project.id, // 👈 继承项目 ID
  directory: input.directory ?? Instance.directory, // 👈 继承工作目录
  parentID: input.parentID, // 👈 父会话 ID
  title: input.title ?? createDefaultTitle(!!input.parentID),
  permission: input.permission,
  time: {
    created: Date.now(),
    updated: Date.now(),
  },
}

上下文传递对比表

上下文类型是否传递给子会话传递方式说明
parentID✅ 是直接传递建立父子关系
title✅ 是直接传递(不同)子会话有独立标题
permission✅ 是直接传递(可能受限)子会话可能有权限限制
projectID✅ 是Instance.project.id共享项目 ID
directory✅ 是Instance.directory共享工作目录
worktree✅ 是Instance.worktree共享项目根目录
summary❌ 否子会话独立每个会话有自己的代码摘要
share❌ 否子会话独立分享信息不继承
messages❌ 否子会话独立独立的消息历史
time✅ 是新创建子会话有独立时间戳

消息历史独立性

重要说明:

  • 子会话有独立的消息历史
  • 子会话不继承父会话的 Messages
  • 父 Agent 通过以下方式与子 Agent 通信:
    1. 事件监听(监听 PartUpdated 事件)
    2. Task 工具返回值(子 Agent 完成后返回结果)

示例:

// 父会话
const parentSession = await Session.create({
  title: "Main task",
})
// 父会话有 10 条消息

// 创建子会话
const childSession = await Session.create({
  parentID: parentSession.id,
  title: "Subtask",
})

// 子会话的消息历史为空(0 条消息)
// 子会话不会自动复制父会话的消息

权限规则继承

权限合并逻辑:packages/opencode/src/agent/agent.ts:76-83

Task 工具创建子会话时的默认权限限制:

permission: [
  {
    permission: "todowrite",
    pattern: "*",
    action: "deny", // 👈 防止子 Agent 管理父 Agent 的 todo
  },
  {
    permission: "todoread",
    pattern: "*",
    action: "deny", // 👈 防止子 Agent 读取父 Agent 的 todo
  },
  ...(hasTaskPermission
    ? []
    : [
        {
          permission: "task",
          pattern: "*",
          action: "deny", // 👈 防止无限递归调用
        },
      ]),
]

典型使用场景

场景 1:创建子会话分支

const parentSession = await Session.get("sess_parent123")

const childSession = await Session.create({
  parentID: parentSession.id,
  title: "Alternative implementation approach",
  permission: [
    {
      permission: "edit",
      pattern: "*",
      action: "deny", //只读权限
    },
  ],
})

console.log(childSession.parentID) // "sess_parent123"

场景 2:遍历会话树

async function printSessionTree(sessionID: string, depth: number = 0) {
  const session = await Session.get(sessionID)
  const indent = "  ".repeat(depth)
  console.log(`${indent}- ${session.title}`)

  const children = await Session.children(sessionID)
  for (const child of children) {
    await printSessionTree(child.id, depth + 1)
  }
}

// 从根会话开始打印
const rootSessions = await Storage.list(["session", Instance.project.id])
for (const item of rootSessions) {
  const session = await Storage.read<Session.Info>(item)
  if (!session.parentID) {
    // 只处理根会话
    await printSessionTree(session.id)
  }
}

场景 3:检查会话是否有子会话

async function hasChildren(sessionID: string): Promise<boolean> {
  const children = await Session.children(sessionID)
  return children.length > 0
}

const session = await Session.get("sess_abc123")
if (await hasChildren(session.id)) {
  console.log("This session has child sessions")
  console.log("Deleting it will also delete all children")
}

对象关系

变更历史

版本变更内容日期
v1初始 Session 架构-
v1.1添加 revert 字段支持回滚-

相关文档

  • Message - 消息对象
  • Part - 消息部分
  • Project - 项目对象
  • Storage - 存储架构详解