fix(persistence): preserve subagent session origin

This commit is contained in:
imccyu
2026-08-01 09:41:52 +08:00
committed by Tianyi Cui
parent 9d77a50c23
commit d950150cdc
30 changed files with 53 additions and 36 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/session-persistence/session-persistence/README.md
README.md: a8a4f14c8613a7e51bcf467e816b7f7bdb7ea80b
README.zh.md: 369e8a01b8ac411ed9acfbac86b9b34db8037c1f
README.md: 1acbef838aeb8ea1b1a0e4d08b12cd00b83d85ce
README.zh.md: 74e6c62921bbcef431767a0274514e4c51f450d9

View File

@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
The abstract durable session-persistence seam (`ctx.sessionPersistence`). Defines WHAT a persistence backend does — durably store, reload, and list sessions — without saying HOW. Mirrors the `dsh-bash` capability-seam template ([capability seams](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)): an abstract service here, a concrete implementation in a sibling package, consumers that inject the interface.
The persisted unit IS the existing `SessionEvent` (event-sourced model — the log is the single source of truth), so there is no parallel "persisted message" type. Metadata that is NOT replayable conversation state (format version, cwd, lineage, seed boundary, delegation depth) travels separately as `SessionHeader`, owned by `dsh-session` and re-exported here.
The persisted unit IS the existing `SessionEvent` (event-sourced model — the log is the single source of truth), so there is no parallel "persisted message" type. Metadata that is NOT replayable conversation state (format version, cwd, lineage, seed boundary, origin, delegation depth) travels separately as `SessionHeader`, owned by `dsh-session` and re-exported here.
## Service API (`ctx.sessionPersistence`)
@@ -62,7 +62,7 @@ Three backends run these suites: an in-memory reference (in `tests/`), `dsh-sess
## Metadata and location types
Re-exported from `dsh-session`: `SessionHeader` (immutable session metadata: `version`, `id`, `createdAt`, `cwd?`, `parentSession?`, `seedLength?`, `delegationDepth?`). `SessionLocation` is `{ readonly kind: string; readonly path: string }`; its path is an absolute backend target, not proof that the artifact exists or contains an unflushed turn.
Re-exported from `dsh-session`: `SessionHeader` (immutable session metadata: `version`, `id`, `createdAt`, `cwd?`, `parentSession?`, `seedLength?`, `origin?`, `delegationDepth?`). `SessionLocation` is `{ readonly kind: string; readonly path: string }`; its path is an absolute backend target, not proof that the artifact exists or contains an unflushed turn.
## Model Experience

View File

@@ -4,7 +4,7 @@
抽象的持久会话持久化 seam`ctx.sessionPersistence`)。它定义持久化后端做什么:持久存储、重新加载和列出会话,而不规定如何实现。它与 `dsh-bash` 功能 seam 模板一致(见[功能 seam](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):本包提供抽象服务,同级包提供具体实现,消费方注入接口。
持久化单元就是现有 `SessionEvent`事件溯源模型日志是唯一真源因此不存在并行的「持久消息」类型。不可回放的对话状态元数据格式版本、cwd、血缘、种子边界、委托深度作为 `SessionHeader` 单独传输,该类型归 `dsh-session` 所有,并在此重新导出。
持久化单元就是现有 `SessionEvent`事件溯源模型日志是唯一真源因此不存在并行的「持久消息」类型。不可回放的对话状态元数据格式版本、cwd、血缘、种子边界、origin、委托深度)作为 `SessionHeader` 单独传输,该类型归 `dsh-session` 所有,并在此重新导出。
## 服务 API`ctx.sessionPersistence`
@@ -62,7 +62,7 @@
## 元数据与位置类型
`dsh-session` 重新导出:`SessionHeader`(不可变会话元数据:`version``id``createdAt``cwd?``parentSession?``seedLength?``delegationDepth?`)。`SessionLocation``{ readonly kind: string; readonly path: string }`;其 path 是绝对后端目标,不证明产物已存在或包含未 flush 轮次。
`dsh-session` 重新导出:`SessionHeader`(不可变会话元数据:`version``id``createdAt``cwd?``parentSession?``seedLength?``origin?``delegationDepth?`)。`SessionLocation``{ readonly kind: string; readonly path: string }`;其 path 是绝对后端目标,不证明产物已存在或包含未 flush 轮次。
## 模型体验

View File

@@ -276,7 +276,12 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
let session!: Session
const sessionFiber = await ctx.plugin(Object.assign((inner: Context) => {
session = inner.sessions.create(SessionId('delegated-child'), {
meta: { cwd: WORK, parentSession: SessionId('root'), delegationDepth: 2 },
meta: {
cwd: WORK,
parentSession: SessionId('root'),
origin: 'subagent',
delegationDepth: 2,
},
})
}, { inject: ['sessions'] }))
send(session, oneTurnLog())
@@ -285,6 +290,7 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
const loaded = await ctx.sessionPersistence.load(SessionId('delegated-child'))
expect(loaded.meta.delegationDepth).toBe(2)
expect(loaded.meta.origin).toBe('subagent')
} finally {
await fiber.dispose()
await fix.cleanup()