Merge remote-tracking branch 'origin/master' into feat/read-image-context

This commit is contained in:
creatixchu
2026-08-11 11:54:22 +08:00
65 changed files with 2004 additions and 226 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 docs/subsystems/persistence.md
persistence.md: 0266d17393d07c258036f7054a02c4ab9d3c74a2
persistence.zh.md: ced83440160ae91ae37025d8024068fb8148b0c6
persistence.md: 7deaa9b30b5a6b1e3cbdcc38255b3974b5abf477
persistence.zh.md: c5afcf67319da408b739d41b2b7ad3eb434ffbad

View File

@@ -87,6 +87,10 @@ interface SessionHeader {
}
```
## Format refusal — logs a build cannot faithfully read
A backend refuses a log it cannot faithfully interpret with `SessionFormatUnsupportedError`, distinct from `SessionPersistenceCorruptionError` because nothing is damaged. A header `version` ahead of `SESSION_FORMAT_VERSION` names the direction ("written by a newer harness — upgrade the harness to open it"); one behind it states that this build ships no upgrade path. After legacy-shape normalization, an event type outside this build's generated vocabulary (`KNOWN_SESSION_EVENT_TYPES`, emitted by `gen-persistence-catalog`) refuses the same way unless the event's envelope carries `ignorable: true` — silently skipping an unrecognized required event could change how the rest of the log must be read. The message appends the raw log path when the backend keeps one artifact per session, so the refused text stays reachable. The JSONL backend refuses a foreign version straight from the raw header line, before validating today's header shape or decoding any event row — a structurally different future format still reports the upgrade direction, never "corrupt"; SQLite gates whole-file structure through its own `SCHEMA_VERSION` pragma first. Design rationale and the deferred upgrader chain live in the [session-log-version-mechanism note](../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md).
## `CreateSessionOptions` — seeding and metadata
Creating a `Session` through the store takes a `seed` (initial replay or fork history) and `meta` (the storage-level fields the store folds into a `SessionHeader`). The store fills in `version`/`id` and defaults `createdAt`; the caller may supply the validated absolute `cwd`, the `parentSession` lineage, the `seedLength` seed boundary, the optional coarse `origin`, the `delegationDepth`, the `agentPreset` the agent was composed from, and an existing `createdAt`. `origin: 'subagent'` lets product navigation hide duplicate child rows; it does not prove that a descriptor is valid or that the child can resume.
@@ -342,5 +346,5 @@ abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot
Types: [SessionEvent](session.md) · [SessionId](core.md)
Source: [`packages/session/session-persistence/src/index.ts:72`](../../packages/session/session-persistence/src/index.ts)
Source: [`packages/session/session-persistence/src/index.ts:74`](../../packages/session/session-persistence/src/index.ts)
<!-- END GENERATED cordis-surface -->

View File

@@ -87,6 +87,10 @@ interface SessionHeader {
}
```
## 格式拒绝:本构建无法可靠读取的日志
后端用 `SessionFormatUnsupportedError` 拒绝无法可靠解读的日志,它与 `SessionPersistenceCorruptionError` 区分因为数据没有损坏。header 的 `version` 比 `SESSION_FORMAT_VERSION` 新时,消息说明方向("由更新的 harness 写入,请升级 harness 后打开");比它旧时说明本构建没有升级路径。经过 legacy 形状归一化后,本构建生成词汇表(`KNOWN_SESSION_EVENT_TYPES`,由 `gen-persistence-catalog` 生成)之外的事件类型同样被拒绝,除非该事件的信封带 `ignorable: true`静默跳过一个不认识的必需事件可能改变日志其余部分的解读方式。后端为每个会话保留独立文件时消息附上原始日志路径被拒绝的文本仍然可读。JSONL 后端直接从原始 header 行拒绝外来版本,先于当前 header 形状校验和任何事件行解码,因此结构完全不同的未来格式仍会报告升级方向,绝不会报"损坏"SQLite 则先由自己的 `SCHEMA_VERSION` pragma 把关整个文件的结构。设计理由与推迟建设的升级器链见 [session-log 版本机制 Agent Note](../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md)。
## `CreateSessionOptions`seed 与元数据
通过 store 创建 `Session` 时会接收 `seed`(初始回放或 fork 历史)与 `meta`store 折叠进 `SessionHeader` 的存储层字段。store 填充 `version`/`id` 并为 `createdAt` 提供默认值;调用方可以提供已校验的绝对 `cwd`、`parentSession` 谱系、`seedLength` 种子边界、可选的粗粒度 `origin`、`delegationDepth`、该 agent 所依据组装的 `agentPreset` 以及已有的 `createdAt`。`origin: 'subagent'` 让产品导航能够隐藏重复的 child 行;它不证明描述符有效,也不证明 child 可以恢复。
@@ -342,5 +346,5 @@ abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot
Types: [SessionEvent](session.md) · [SessionId](core.md)
Source: [`packages/session/session-persistence/src/index.ts:72`](../../packages/session/session-persistence/src/index.ts)
Source: [`packages/session/session-persistence/src/index.ts:74`](../../packages/session/session-persistence/src/index.ts)
<!-- END GENERATED cordis-surface -->

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 docs/subsystems/session.md
session.md: 0b78e51ebf6e2ad5c312268ad4bfb4392b0486df
session.zh.md: d1e91f684a835e08406f524efe876baa1a6a72cb
session.md: 990b249cde9f02343f2c668aee5d7c000837df56
session.zh.md: 39e8ff1e8831fd75c8929c93e263622bb5aa6ea4

View File

@@ -215,6 +215,17 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
/** Unix epoch milliseconds. */
time: number
data: SessionEventMap[K]
/**
* Marks an event a reader may safely skip when it does not recognize
* `type`. Absent means required: a reader meeting an unrecognized type
* without this marker MUST refuse to reconstruct the session instead of
* silently dropping the event, because an unrecognized required event may
* change how the rest of the log is interpreted. A writer sets `true` only
* on purely informational records whose loss cannot affect reconstruction;
* defaulting to required means a forgotten marker over-refuses (an
* inconvenience) rather than silently resuming a gutted session.
*/
ignorable?: true
} & (K extends SurfaceEventType ? {
/**
* Seq numbers of earlier events that this event cites as sources
@@ -733,7 +744,7 @@ fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId):
Types: [CreateSessionOptions](persistence.md) · [PrepareSessionOptions](persistence.md) · [SessionId](core.md)
Source: [`packages/core/session/src/index.ts:810`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:813`](../../packages/core/session/src/index.ts)
<a id="session-events"></a>
@@ -762,7 +773,7 @@ Creation announcement during session publication. A synchronous throw vetoes and
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:74`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:75`](../../packages/core/session/src/index.ts)
<a id="sessiondisposed--emit"></a>
@@ -785,7 +796,7 @@ Emitted once when an announced session leaves the store, including publication r
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:84`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:85`](../../packages/core/session/src/index.ts)
<a id="sessionevent--emit"></a>
@@ -810,7 +821,7 @@ Post-commit, fire-and-forget append feed. The listener snapshot resolves before
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:96`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:97`](../../packages/core/session/src/index.ts)
<a id="sessionflush--parallel"></a>
@@ -832,5 +843,5 @@ Awaited parallel durability checkpoint: every listener runs and the caller await
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:105`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:106`](../../packages/core/session/src/index.ts)
<!-- END GENERATED cordis-surface -->

View File

@@ -217,6 +217,17 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
/** Unix epoch milliseconds. */
time: number
data: SessionEventMap[K]
/**
* Marks an event a reader may safely skip when it does not recognize
* `type`. Absent means required: a reader meeting an unrecognized type
* without this marker MUST refuse to reconstruct the session instead of
* silently dropping the event, because an unrecognized required event may
* change how the rest of the log is interpreted. A writer sets `true` only
* on purely informational records whose loss cannot affect reconstruction;
* defaulting to required means a forgotten marker over-refuses (an
* inconvenience) rather than silently resuming a gutted session.
*/
ignorable?: true
} & (K extends SurfaceEventType ? {
/**
* Seq numbers of earlier events that this event cites as sources
@@ -737,7 +748,7 @@ fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId):
Types: [CreateSessionOptions](persistence.md) · [PrepareSessionOptions](persistence.md) · [SessionId](core.md)
Source: [`packages/core/session/src/index.ts:810`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:813`](../../packages/core/session/src/index.ts)
<a id="session-events"></a>
@@ -766,7 +777,7 @@ Creation announcement during session publication. A synchronous throw vetoes and
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:74`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:75`](../../packages/core/session/src/index.ts)
<a id="sessiondisposed--emit"></a>
@@ -789,7 +800,7 @@ Emitted once when an announced session leaves the store, including publication r
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:84`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:85`](../../packages/core/session/src/index.ts)
<a id="sessionevent--emit"></a>
@@ -814,7 +825,7 @@ Post-commit, fire-and-forget append feed. The listener snapshot resolves before
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:96`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:97`](../../packages/core/session/src/index.ts)
<a id="sessionflush--parallel"></a>
@@ -836,5 +847,5 @@ Awaited parallel durability checkpoint: every listener runs and the caller await
Types: [Scoped](scope.md)
Source: [`packages/core/session/src/index.ts:105`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:106`](../../packages/core/session/src/index.ts)
<!-- END GENERATED cordis-surface -->