feat(session-query): expose projectSessions batch projection

Public SessionQueryService.projectSessions wraps the existing corpus
projectMany: one persistence listing, bounded persisted-inspect
concurrency, per-id failure isolation, and a synchronous projector over
a borrowed source with no replay validation or cloning.
readTitleSnapshots now routes through it; LogicalSessionSource and
LogicalProjectionResult are exported and documented.
This commit is contained in:
Turtle
2026-07-31 16:09:05 +08:00
parent 66f8cc074e
commit 6e577843c8
12 changed files with 143 additions and 11 deletions

View File

@@ -1442,6 +1442,23 @@ async readTitleSnapshot( sessionId: SessionId, signal?: AbortSignal, ): Promise<
*/
async readTitleSnapshots( sessionIds: readonly SessionId[], signal?: AbortSignal, ): Promise<SessionTitleObservationResult[]>
/**
* Project unique logical sessions synchronously from one cancellable corpus
* observation.
*
* Each source is a borrowed raw log without replay validation or cloning, so
* a batch summary costs one bounded read per persisted session instead of a
* full validated copy; the projector must clone anything it retains beyond
* its own call. Results preserve first-occurrence input order. Operational
* failures stay isolated per session, while cancellation rejects the
* complete operation.
* @param sessionIds - live or persisted session ids to observe.
* @param project - synchronous fold that owns/clones every retained value.
* @param signal - optional cancellation shared by all source reads.
* @returns one fulfilled or rejected result per unique requested id.
*/
async projectSessions<Value>( sessionIds: readonly SessionId[], project: (source: LogicalSessionSource) => Value, signal?: AbortSignal, ): Promise<LogicalProjectionResult<Value>[]>
/**
* List lightweight raw-log event records for one logical session.
* @param sessionId - live-preferred session id to read.
@@ -1492,9 +1509,9 @@ async traceEvent(request: SessionEventTraceRequest, signal?: AbortSignal): Promi
async readEvent(request: SessionEventReadRequest, signal?: AbortSignal): Promise<SessionEventWindow>
```
Types: [SessionEventReadRequest](../core-data-structures/session-query.md) · [SessionEventRecord](../core-data-structures/session-query.md) · [SessionEventResultFilter](../core-data-structures/session-query.md) · [SessionEventSearchDocument](../core-data-structures/session-query.md) · [SessionEventSearchPage](../core-data-structures/session-query.md) · [SessionEventSearchRequest](../core-data-structures/session-query.md) · [SessionEventTraceObservation](../core-data-structures/session-query.md) · [SessionEventTraceRequest](../core-data-structures/session-query.md) · [SessionEventWindow](../core-data-structures/session-query.md) · [SessionId](../core-data-structures/core.md) · [SessionLineageTrace](../core-data-structures/session-query.md) · [SessionLogSnapshot](../core-data-structures/session-query.md) · [SessionRecord](../core-data-structures/session-query.md) · [SessionResultFilter](../core-data-structures/session-query.md) · [SessionSearchExecContext](../core-data-structures/session-query.md) · [SessionSearchHit](../core-data-structures/session-query.md) · [SessionSearchPage](../core-data-structures/session-query.md) · [SessionSearchRequest](../core-data-structures/session-query.md) · [SessionSurfaceSnapshot](../core-data-structures/session-query.md) · [SessionTitleObservation](../core-data-structures/session-query.md) · [SessionTitleObservationResult](../core-data-structures/session-query.md) · [SessionTitleSnapshot](../core-data-structures/session-title.md)
Types: [LogicalProjectionResult](../core-data-structures/session-query.md) · [LogicalSessionSource](../core-data-structures/session-query.md) · [SessionEventReadRequest](../core-data-structures/session-query.md) · [SessionEventRecord](../core-data-structures/session-query.md) · [SessionEventResultFilter](../core-data-structures/session-query.md) · [SessionEventSearchDocument](../core-data-structures/session-query.md) · [SessionEventSearchPage](../core-data-structures/session-query.md) · [SessionEventSearchRequest](../core-data-structures/session-query.md) · [SessionEventTraceObservation](../core-data-structures/session-query.md) · [SessionEventTraceRequest](../core-data-structures/session-query.md) · [SessionEventWindow](../core-data-structures/session-query.md) · [SessionId](../core-data-structures/core.md) · [SessionLineageTrace](../core-data-structures/session-query.md) · [SessionLogSnapshot](../core-data-structures/session-query.md) · [SessionRecord](../core-data-structures/session-query.md) · [SessionResultFilter](../core-data-structures/session-query.md) · [SessionSearchExecContext](../core-data-structures/session-query.md) · [SessionSearchHit](../core-data-structures/session-query.md) · [SessionSearchPage](../core-data-structures/session-query.md) · [SessionSearchRequest](../core-data-structures/session-query.md) · [SessionSurfaceSnapshot](../core-data-structures/session-query.md) · [SessionTitleObservation](../core-data-structures/session-query.md) · [SessionTitleObservationResult](../core-data-structures/session-query.md) · [SessionTitleSnapshot](../core-data-structures/session-title.md)
Source: [`packages/session-query/session-query/src/index.ts:81`](../../packages/session-query/session-query/src/index.ts)
Source: [`packages/session-query/session-query/src/index.ts:82`](../../packages/session-query/session-query/src/index.ts)
## `ctx.sessionReferences` — `SessionReferenceService`

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
session-query.md: d92af4bac34f7d41457e9e193111c3a53fe8022e
session-query.zh.md: ecf330b0a361ffae352a91c0d35524444936606d
# pnpm run verify-translation-pairing --write docs/core-data-structures/session-query.md
session-query.md: cffbd792e8cab6e79365ceb4e0b1d996e7e93cf5
session-query.zh.md: 2b67761ec3bcd96ee9de15511ec44516f1fe525d

View File

@@ -84,6 +84,25 @@ type SessionTitleObservationResult =
}
```
`projectSessions` batches arbitrary synchronous folds over the same live-preferred corpus: each `LogicalSessionSource` is a borrowed raw log — never replay-validated or cloned — that is valid only for the projector call, so a batch summary costs one bounded read per persisted session. Each `LogicalProjectionResult` settles per unique requested id under the same isolation and cancellation rules as batch title reads.
```ts type-equiv
/** Borrowed source visible only during one synchronous batch projection. */
interface LogicalSessionSource {
/** Header selected with `events`; callers must clone retained output. */
readonly header: SessionHeader
/** Raw events selected with `header`; valid only for the projection call. */
readonly events: readonly SessionEvent[]
}
```
```ts type-equiv
/** One source-projection result in a batch logical-corpus observation. */
type LogicalProjectionResult<Value> =
| { sessionId: SessionId; status: 'fulfilled'; value: Value }
| { sessionId: SessionId; status: 'rejected'; reason: unknown }
```
```ts type-equiv
/** Lightweight metadata for one event within a logical session. */
interface SessionEventRecord {

View File

@@ -84,6 +84,25 @@ type SessionTitleObservationResult =
}
```
`projectSessions` 在同一实时优先语料库上批量执行任意同步折叠:每个 `LogicalSessionSource` 都是借用的原始日志——从不做回放验证,也从不克隆——仅在投影函数调用期间有效,因此一次批量摘要对每个持久化会话只需一次有界读取。每个 `LogicalProjectionResult` 按唯一请求 id 结算,其失败隔离与取消规则与批量标题读取一致。
```ts type-equiv
/** Borrowed source visible only during one synchronous batch projection. */
interface LogicalSessionSource {
/** Header selected with `events`; callers must clone retained output. */
readonly header: SessionHeader
/** Raw events selected with `header`; valid only for the projection call. */
readonly events: readonly SessionEvent[]
}
```
```ts type-equiv
/** One source-projection result in a batch logical-corpus observation. */
type LogicalProjectionResult<Value> =
| { sessionId: SessionId; status: 'fulfilled'; value: Value }
| { sessionId: SessionId; status: 'rejected'; reason: unknown }
```
```ts type-equiv
/** Lightweight metadata for one event within a logical session. */
interface SessionEventRecord {