docs: replace vague provenance prose with recorded facts
This commit is contained in:
@@ -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/session-title/README.md
|
||||
README.md: 13a2a1c298c3abb8c8987b9373edc46a071ce9db
|
||||
README.zh.md: 075e9e31fc7acbc76f0e3fd7a2642b4d879569d3
|
||||
README.md: e7004eb2e03757cb28b4b5756a876e1dee4b6229
|
||||
README.zh.md: 45c1aafdf214f63ae7a071d2558eaefec13ea70f
|
||||
|
||||
@@ -29,7 +29,7 @@ All limits are required; the library supplies no defaults.
|
||||
|
||||
## Provider contract
|
||||
|
||||
A provider supplies a branded stable id, automatic mode (`first-message` or `all-user-messages`), and `generate(request)`. The request carries the live session, all eligible messages through one fixed revision, the current logged main-request route when available, and cancellation. The result identifies a non-empty title, unique ordered source-message seqs from that request, and optional model provenance. The service normalizes and validates the result before it becomes durable.
|
||||
A provider supplies a branded stable id, automatic mode (`first-message` or `all-user-messages`), and `generate(request)`. The request carries the live session, all eligible messages through one fixed revision, the current logged main-request route when available, and cancellation. The result identifies a non-empty title, unique ordered source-message seqs from that request, and the optional provider/model route used to generate it. The service normalizes and validates the result before it becomes durable.
|
||||
|
||||
See the [session-title data structures](../../../docs/subsystems/session-title.md) and [implemented decision](../../../.agents/notes/implemented/feature/2026-07-21-log-backed-session-titles.md).
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Fork 出的会话会原样继承种子中的标题事件。首消息节奏不会
|
||||
|
||||
## 提供方约定
|
||||
|
||||
提供方会提供带品牌类型的稳定 id、自动模式(`first-message` 或 `all-user-messages`)和 `generate(request)`。请求携带活跃会话、截至一次固定修订的所有符合条件消息、可用时当前已记录的主请求路由,以及取消信号。结果包含非空标题、该请求中唯一且有序的来源消息 seq,以及可选的模型来源信息。服务会在结果持久保存前进行规范化和验证。
|
||||
提供方会提供带品牌类型的稳定 id、自动模式(`first-message` 或 `all-user-messages`)和 `generate(request)`。请求携带活跃会话、截至一次固定修订的所有符合条件消息、可用时当前已记录的主请求路由,以及取消信号。结果包含非空标题、该请求中唯一且有序的来源消息 seq,以及生成该标题时使用的可选提供方/模型路由。服务会在结果持久保存前进行规范化和验证。
|
||||
|
||||
参见[会话标题数据结构](../../../docs/subsystems/session-title.md)与[已实现决策](../../../.agents/notes/implemented/feature/2026-07-21-log-backed-session-titles.md)。
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export interface SessionTitleEventData {
|
||||
readonly title: string
|
||||
/** Exact human `user/message` seqs used to derive this title; empty for an explicit user rename. */
|
||||
readonly messageSeqs: number[]
|
||||
/** Built-in fallback, registered-provider, or explicit-user provenance. */
|
||||
/** Whether the built-in fallback, a registered provider, or the user supplied the title. */
|
||||
readonly source: SessionTitleSource
|
||||
}
|
||||
|
||||
@@ -146,14 +146,14 @@ export interface SessionTitleProviderResult {
|
||||
|
||||
/** One optional asynchronous title implementation registered with the service. */
|
||||
export interface SessionTitleProvider {
|
||||
/** Stable provider identity recorded in title provenance. */
|
||||
/** Stable id of the provider recorded with the title. */
|
||||
readonly id: SessionTitleProviderId
|
||||
/** When new human prompts start automatic generation. */
|
||||
readonly automatic: SessionTitleAutomaticMode
|
||||
/**
|
||||
* Produce one title revision.
|
||||
* @param request - message snapshot, current route, session, and cancellation.
|
||||
* @returns proposed title plus exact input seqs and optional model provenance.
|
||||
* @returns proposed title plus exact input seqs and the optional provider/model route used to generate it.
|
||||
*/
|
||||
generate(request: SessionTitleProviderRequest): Promise<SessionTitleProviderResult>
|
||||
}
|
||||
@@ -615,12 +615,12 @@ export class SessionTitleService extends Service {
|
||||
let model: SessionTitleModelProvenance | undefined
|
||||
if (modelCandidate !== undefined) {
|
||||
if (modelCandidate === null || typeof modelCandidate !== 'object') {
|
||||
throw new Error('session-title provider model provenance requires non-empty provider and model')
|
||||
throw new Error('session-title provider result model must contain non-empty provider and model strings')
|
||||
}
|
||||
const record = modelCandidate as Record<string, unknown>
|
||||
if (typeof record.provider !== 'string' || record.provider.length === 0
|
||||
|| typeof record.model !== 'string' || record.model.length === 0) {
|
||||
throw new Error('session-title provider model provenance requires non-empty provider and model')
|
||||
throw new Error('session-title provider result model must contain non-empty provider and model strings')
|
||||
}
|
||||
model = { provider: record.provider, model: record.model }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export const name = 'session-title-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* Durable title-provenance invariant: an automatic title always cites at
|
||||
* Durable title-source invariant: an automatic title always cites at
|
||||
* least one human `user/message` seq, and an explicit user rename cites none
|
||||
* — `messageSeqs` is empty iff `source.kind` is `user`. Provider revisions
|
||||
* are validated by the service before their append; this checks the durable
|
||||
@@ -32,7 +32,8 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
|
||||
if (event.type !== 'session/title') return
|
||||
const { source, messageSeqs } = event.data
|
||||
if ((messageSeqs.length === 0) !== (source.kind === 'user')) {
|
||||
fail(`session/title event ${String(event.seq)} breaks provenance: source "${source.kind}" with ${String(messageSeqs.length)} cited message seq(s)`)
|
||||
const requirement = source.kind === 'user' ? 'cite no message seqs' : 'cite at least one message seq'
|
||||
fail(`session/title event ${String(event.seq)} with source "${source.kind}" must ${requirement}; got ${String(messageSeqs.length)}`)
|
||||
}
|
||||
}, { global: true })
|
||||
}, { inject: ['sessions'] })
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Title-provenance invariant: messageSeqs is empty iff source.kind is 'user'
|
||||
// Title-source invariant: `messageSeqs` is empty iff `source.kind` is `user`.
|
||||
// — the durable relationship every appended session/title event must keep.
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
@@ -14,7 +14,7 @@ async function setup(): Promise<Context> {
|
||||
return ctx
|
||||
}
|
||||
|
||||
describe('session-title provenance invariant', () => {
|
||||
describe('session-title source invariant', () => {
|
||||
it('accepts cited automatic titles and citation-free user renames', async () => {
|
||||
const ctx = await setup()
|
||||
const session = ctx.sessions.create(SessionId('title-invariant-valid'))
|
||||
|
||||
@@ -436,12 +436,12 @@ describe('SessionTitleService provider validation and stale scheduling', () => {
|
||||
{ value: { title: 'valid', messageSeqs: [999] }, error: /unique, ordered seqs/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq, first.seq] }, error: /unique, ordered seqs/ },
|
||||
{ value: { title: 'valid', messageSeqs: [second.seq, first.seq] }, error: /unique, ordered seqs/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: null }, error: /model provenance/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: 'route' }, error: /model provenance/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 1, model: 'm' } }, error: /model provenance/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: '', model: 'm' } }, error: /model provenance/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: 1 } }, error: /model provenance/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: '' } }, error: /model provenance/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: null }, error: /provider result model/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: 'route' }, error: /provider result model/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 1, model: 'm' } }, error: /provider result model/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: '', model: 'm' } }, error: /provider result model/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: 1 } }, error: /provider result model/ },
|
||||
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: '' } }, error: /provider result model/ },
|
||||
]
|
||||
for (const item of cases) {
|
||||
result = item.value
|
||||
|
||||
Reference in New Issue
Block a user