@morlay/session-branch 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,50 +1,27 @@
1
1
  # @morlay/session-branch
2
2
 
3
- 分支式会话编辑的**契约层**:定义数据层分支原语 `SessionBranchProvider`
4
- (rewind / forkFrom / readBranchPrefix)与高层服务 `SessionBranch`
5
- (`ctx.sessionBranch`)。
3
+ 分支式会话编辑的**契约层**:数据层分支原语 `SessionBranchProvider`(`readBranchPrefix` / `forkFrom` /
4
+ `rewind`)与高层服务 `SessionBranch`(`ctx.sessionBranch`),接口形状见
5
+ [`src/provider.ts`](./src/provider.ts)。
6
6
 
7
- 跨包术语见根 [`.agents/CONTEXT.md`](../../../.agents/CONTEXT.md),本包的决策见 [`.agents/adrs/`](./.agents/adrs)。
7
+ ## 用法
8
8
 
9
- ## 装配
10
-
11
- 本包没有自己的 bundle patch(不作为独立 bundle 安装):装配入口是聚合层
12
- [`@morlay/better-session`](../../bundles/better-session/README.md),`session-branch` 行由它的 patch 插入。
13
-
14
- ## 与上游 `SessionHandle` 的关系
15
-
16
- 上游持久化模型是 `SessionHandle`(append-only:`create` / `open` / `append` /
17
- `flush` / `stat` / `list`),没有显式回退原语;本包在其旁边定义**分支面**
18
- provider,负责显式回退与闭合边界派生:
19
-
20
- | 面 | 上游 `SessionHandle` | 本包 `SessionBranchProvider` |
21
- | ------ | -------------------------------------- | ------------------------------------------------------ |
22
- | 覆盖 | 持久读写(append-only) | 显式回退 + 闭合边界派生(分支面) |
23
- | 原语 | `create` / `open` / `append` / `flush` | `readBranchPrefix` / `forkFrom` / `rewind` |
24
- | 实现方 | JSONL / RDB 等 | RDB 等(`@morlay/session-rdb` 同时实现两者,形成闭环) |
25
-
26
- ## Provider 抽象
9
+ 装配入口是聚合层 [`@morlay/better-session`](../../bundles/better-session/README.md)——本包没有自己的 bundle
10
+ patch,`session-branch` 行由它的 patch 插入;实现方是 [`@morlay/session-rdb`](../session-rdb/README.md)
11
+ (同一个后端也实现上游 `SessionHandle`)。
27
12
 
28
13
  ```ts
29
- interface SessionBranchProvider {
30
- readonly name: string;
31
- readBranchPrefix(id, atSeq?, mode?, signal?): Promise<BranchBoundary>;
32
- forkFrom(sourceId, options?, signal?): Promise<SessionId>;
33
- rewind(id, toBoundary, signal?): Promise<SessionPersistenceSnapshot>;
34
- }
14
+ const boundary = await ctx.sessionBranch.readBranchPrefix(sessionId, userEventSeq, "before");
15
+ await ctx.sessionBranch.rewind(sessionId, boundary.seq);
35
16
  ```
36
17
 
37
- - `readBranchPrefix`:定位 `atSeq` 锚定的闭合 `turn/end` 边界并返回前缀;
38
- `mode: "after"`(包含目标轮,fork 语义)/ `"before"`(排除目标轮,编辑 /
39
- 重掷 / 重试语义)。
40
- - `forkFrom`:纯 append——新会话(`parentSession` / `seedLength`),seed =
41
- 边界前缀 + `seedSuffix`,不触碰源会话。
42
- - `rewind`:唯一的显式回退原语,事务整体提交或回滚;**支持 live 会话**
43
- (内存 log 截断 + 派生缓存复位 + handle cursor / 继承前缀对齐,详见
44
- `@morlay/session-rdb` 的 [分支能力](../session-rdb/.agents/designs/20260917-分支能力.md))。
45
-
46
- ## 版本效果(历史形状)
18
+ - `readBranchPrefix(id, atSeq?, mode?)`:定位 `atSeq` 锚定的闭合 `turn/end` 边界并返回前缀;`mode: "after"`
19
+ 含目标轮(fork 语义)/ `"before"` 排除目标轮(编辑 / 重掷 / 重试语义)。
20
+ - `forkFrom(sourceId, options?)`:纯 append 派生新会话(`parentSession` / `seedLength`,seed = 边界前缀 +
21
+ `seedSuffix`),不触碰源会话。
22
+ - `rewind(id, toBoundary)`:唯一的显式回退原语,事务整体提交或回滚;支持 live 会话(内存 log 截断 +
23
+ 派生缓存复位 + handle cursor / 继承前缀对齐,机制见 session-rdb 的
24
+ [设计 20260917-分支能力](../session-rdb/.agents/designs/20260917-分支能力.md))。
47
25
 
48
- `session-branch/version` 事件的类型定义仍在本包(`types.ts`),用于识别旧会话里
49
- 已落库的历史事件;**写侧与读侧都已删除**(不再产出新事件、没有读者),理由见
50
- [ADR-删除版本树投影并停止写版本效果](./.agents/adrs/20260920-删除版本树投影并停止写版本效果.md)。
26
+ `session-branch/version` 事件的类型仍在 `src/types.ts`:它只用于识别旧会话里已落库的该事件,本仓库当前
27
+ 没有生产方与消费者([ADR-删除版本树投影并停止写版本效果](./.agents/adrs/20260920-删除版本树投影并停止写版本效果.md))。
package/dist/index.d.mts CHANGED
@@ -2,11 +2,11 @@ import { Context, Service } from "@deepseek-ai/cordis";
2
2
  import { SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
3
3
  import { SessionPersistenceSnapshot } from "@deepseek-ai/dsh-session-persistence";
4
4
  //#region src/types.d.ts
5
- declare const SESSION_BRANCH_VERSION_SCHEMA = 1;
6
- type CascadePolicy = "truncate" | "preserve";
7
- type VersionOperation = "edit" | "reroll" | "retry" | "fork" | "rewind";
8
- type EditableBlockKind = "user" | "assistant.reasoning" | "assistant.response";
9
- interface SessionBranchEffect {
5
+ export declare const SESSION_BRANCH_VERSION_SCHEMA = 1;
6
+ export type CascadePolicy = "truncate" | "preserve";
7
+ export type VersionOperation = "edit" | "reroll" | "retry" | "fork" | "rewind";
8
+ export type EditableBlockKind = "user" | "assistant.reasoning" | "assistant.response";
9
+ export interface SessionBranchEffect {
10
10
  id: string;
11
11
  operation: VersionOperation;
12
12
  cascade: CascadePolicy;
@@ -17,11 +17,11 @@ interface SessionBranchEffect {
17
17
  before?: string;
18
18
  after?: string;
19
19
  }
20
- interface SessionBranchInverse {
20
+ export interface SessionBranchInverse {
21
21
  kind: "restore-version";
22
22
  sessionId: SessionId;
23
23
  }
24
- interface SessionBranchVersionEvent {
24
+ export interface SessionBranchVersionEvent {
25
25
  schemaVersion: typeof SESSION_BRANCH_VERSION_SCHEMA;
26
26
  effect: SessionBranchEffect;
27
27
  inverse: SessionBranchInverse;
@@ -31,40 +31,29 @@ declare module "@deepseek-ai/dsh-session" {
31
31
  "session-branch/version": SessionBranchVersionEvent;
32
32
  }
33
33
  }
34
- interface BranchBoundary {
34
+ export interface BranchBoundary {
35
35
  seq: number;
36
36
  events: readonly SessionEvent[];
37
37
  }
38
- interface BranchForkMeta {
38
+ export interface BranchForkMeta {
39
39
  cwd?: string;
40
40
  createdAt?: number;
41
41
  agentPreset?: string;
42
42
  origin?: "subagent";
43
43
  delegationDepth?: number;
44
44
  }
45
- interface ForkFromOptions {
45
+ export interface ForkFromOptions {
46
46
  atSeq?: number;
47
47
  anchorMode?: BranchAnchorMode;
48
48
  seedSuffix?: readonly SessionEvent[];
49
49
  childSessionId?: SessionId;
50
50
  meta?: BranchForkMeta;
51
51
  }
52
- type SessionBranchErrorCode = "SESSION_NOT_FOUND" | "INVALID_BOUNDARY" | "OPEN_TURN" | "FORK_UNAVAILABLE" | "REWIND_CONFLICT";
53
- declare class SessionBranchError extends Error {
52
+ export type SessionBranchErrorCode = "SESSION_NOT_FOUND" | "INVALID_BOUNDARY" | "OPEN_TURN" | "FORK_UNAVAILABLE" | "REWIND_CONFLICT";
53
+ export declare class SessionBranchError extends Error {
54
54
  readonly code: SessionBranchErrorCode;
55
55
  constructor(message: string, code: SessionBranchErrorCode);
56
56
  }
57
- interface SessionBranchVersionEventEnvelope {
58
- type: "session-branch/version";
59
- seq: number;
60
- time: number;
61
- ignorable?: true;
62
- data: SessionBranchVersionEvent;
63
- }
64
- declare function isSessionBranchVersionEvent(event: SessionEvent | {
65
- type: string;
66
- data: unknown;
67
- }): event is SessionBranchVersionEventEnvelope;
68
57
  //#endregion
69
58
  //#region src/provider.d.ts
70
59
  type BranchAnchorMode = "after" | "before";
@@ -76,7 +65,7 @@ interface SessionBranchProvider {
76
65
  }
77
66
  //#endregion
78
67
  //#region src/branch.d.ts
79
- declare abstract class SessionBranch extends Service {
68
+ export declare abstract class SessionBranch extends Service {
80
69
  constructor(ctx: import("@deepseek-ai/cordis").Context);
81
70
  abstract readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
82
71
  abstract forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
@@ -87,8 +76,8 @@ declare abstract class SessionBranch extends Service {
87
76
  interface BalanceRewindPrefixOptions {
88
77
  keepOpenTail?: boolean;
89
78
  }
90
- declare function balanceRewindPrefix(events: readonly SessionEvent[], options?: BalanceRewindPrefixOptions): SessionEvent[];
91
- declare function rewindKeepLength(types: readonly string[], rawKeepLength: number): number;
79
+ export declare function balanceRewindPrefix(events: readonly SessionEvent[], options?: BalanceRewindPrefixOptions): SessionEvent[];
80
+ export declare function rewindKeepLength(types: readonly string[], rawKeepLength: number): number;
92
81
  //#endregion
93
82
  //#region src/index.d.ts
94
83
  declare module "@deepseek-ai/cordis" {
@@ -96,6 +85,6 @@ declare module "@deepseek-ai/cordis" {
96
85
  sessionBranch: SessionBranch;
97
86
  }
98
87
  }
99
- declare function apply(_ctx: Context): void;
88
+ export declare function apply(_ctx: Context): void;
100
89
  //#endregion
101
- export { type BranchAnchorMode, BranchBoundary, BranchForkMeta, CascadePolicy, EditableBlockKind, ForkFromOptions, SESSION_BRANCH_VERSION_SCHEMA, SessionBranch, SessionBranchEffect, SessionBranchError, SessionBranchErrorCode, SessionBranchInverse, type SessionBranchProvider, SessionBranchVersionEvent, SessionBranchVersionEventEnvelope, VersionOperation, apply, balanceRewindPrefix, isSessionBranchVersionEvent, rewindKeepLength };
90
+ export type { BranchAnchorMode, SessionBranchProvider };
package/dist/index.mjs CHANGED
@@ -65,11 +65,8 @@ var SessionBranchError = class extends Error {
65
65
  this.code = code;
66
66
  }
67
67
  };
68
- function isSessionBranchVersionEvent(event) {
69
- return event.type === "session-branch/version" && event.data.schemaVersion === 1;
70
- }
71
68
  //#endregion
72
69
  //#region src/index.ts
73
70
  function apply(_ctx) {}
74
71
  //#endregion
75
- export { SESSION_BRANCH_VERSION_SCHEMA, SessionBranch, SessionBranchError, apply, balanceRewindPrefix, isSessionBranchVersionEvent, rewindKeepLength };
72
+ export { SESSION_BRANCH_VERSION_SCHEMA, SessionBranch, SessionBranchError, apply, balanceRewindPrefix, rewindKeepLength };
@@ -1,7 +1,6 @@
1
1
  import { Context } from "@deepseek-ai/cordis";
2
2
  //#region src/invariant.d.ts
3
- declare const name = "session-branch-invariant";
4
- declare const inject: string[];
5
- declare const apply: (ctx: Context) => Promise<() => void>;
6
- //#endregion
7
- export { apply, inject, name };
3
+ export declare const name = "session-branch-invariant";
4
+ export declare const inject: string[];
5
+ export declare const apply: (ctx: Context) => Promise<() => void>;
6
+ //#endregion
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "Session Branch",
4
- "description": "Provider abstraction and service for rewind / retry / fork over event-sourced sessions."
4
+ "description": "Contract layer for branch-based session editing: the data-layer provider abstraction and the high-level service for rewind / retry / fork over event-sourced sessions."
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@morlay/session-branch",
3
- "version": "0.1.1",
4
- "description": "Provider abstraction + high-level service for rewind / retry / fork over DeepSeek Harness event-sourced sessions.",
3
+ "version": "0.1.3",
4
+ "description": "Contract layer for branch-based session editing: the provider abstraction (rewind / forkFrom / readBranchPrefix) plus the high-level service over DeepSeek Harness event-sourced sessions.",
5
5
  "keywords": [
6
6
  "dsh",
7
7
  "dsh-plugin",
package/src/balance.ts CHANGED
@@ -41,9 +41,8 @@ export function balanceRewindPrefix(
41
41
  return events.slice(0, keep);
42
42
  }
43
43
 
44
- // rewind 只按 seq 读尾部窗口(`types` 是窗口内升序的事件类型),在窗口上做同样的配对修剪:
45
- // 从尾部往回扫,遇到 turn/end 即停(它之前的内容与保留前缀的尾部无关);尾部未闭合的
46
- // step/start 一并丢弃,避免它之后的追加(重放的新 step)撞上未闭合的旧 step。
44
+ // rewind 只按 seq 读尾部窗口(`types` 是窗口内升序的事件类型),在窗口上做同样的配对修剪:从尾部往回扫,
45
+ // 遇到 turn/end 即停;尾部未闭合的 step/start 一并丢弃,避免它之后的追加撞上未闭合的旧 step。
47
46
  // 调用方必须保证窗口覆盖到「最近一个 turn/end」或前缀开头,否则结果不完整。
48
47
  export function rewindKeepLength(types: readonly string[], rawKeepLength: number): number {
49
48
  let relativeKeep = types.length;
package/src/types.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
2
2
 
3
- // `session-branch/version` 的**历史形状**:写侧(就地编辑产出)与读侧(版本树投影)都已删除
4
- // (见 ADR 版本效果停止落库 / 版本树投影删除)。这里的定义只用于识别旧数据里已落库的
5
- // ignorable 事件,不再有新事件产生、也没有读者。
3
+ // `session-branch/version` 的形状定义:只用于识别既有日志里已落库的该事件,本仓库不产出它、读侧也不参与投影
4
+ // (见 ADR-删除版本树投影并停止写版本效果)。
6
5
  export const SESSION_BRANCH_VERSION_SCHEMA = 1;
7
6
 
8
7
  export type CascadePolicy = "truncate" | "preserve";
@@ -85,20 +84,3 @@ export class SessionBranchError extends Error {
85
84
  this.code = code;
86
85
  }
87
86
  }
88
-
89
- export interface SessionBranchVersionEventEnvelope {
90
- type: "session-branch/version";
91
- seq: number;
92
- time: number;
93
- ignorable?: true;
94
- data: SessionBranchVersionEvent;
95
- }
96
-
97
- export function isSessionBranchVersionEvent(
98
- event: SessionEvent | { type: string; data: unknown },
99
- ): event is SessionBranchVersionEventEnvelope {
100
- return (
101
- event.type === "session-branch/version" &&
102
- (event.data as { schemaVersion?: unknown }).schemaVersion === SESSION_BRANCH_VERSION_SCHEMA
103
- );
104
- }