refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:36:22 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 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/session-title/README.md
README.md: e7004eb2e03757cb28b4b5756a876e1dee4b6229
README.zh.md: a5f29426e460266d347c9c86564cf9a84428405d
README.md: e923bd9700180214f235c4971d4b020565bfee43
README.zh.md: 8e84ac27d9f1509c8530b838eeb5eb2216d87199

View File

@@ -15,7 +15,7 @@ Only text blocks from human `user/message` events are eligible. The first eligib
Automatic work never delays the main agent response. A provider starts only after a marked loop-built request's exact route matches the current logged `request/header`, including when the unchanged header needs no new snapshot. Its late completion appends a standalone log-only event directly through `Session` without opening a turn. Persistence observes that event eagerly and drains on ordinary lifecycle checkpoints; title publication itself does not force a flush. Automatic failures warn and retain the latest title. New all-message revisions, provider disposal, session disposal, and explicit refresh abort older work, and a stale completion cannot append. Concurrent explicit refreshes reserve their revision before provider work, while overlapping automatic and explicit fallback requests share one session-local in-flight append. The service and bundled model provider each append their own literal event type, so no generic title-write marker, cast, or settlement queue is needed. Service teardown cancels queued work and drains calls that ignore cancellation before unloading completes.
Forks inherit title events in their seed unchanged. The first-message cadence does not automatically retitle a child; the all-messages cadence may append a new revision after the child receives a later human prompt.
Forks inherit title events in their seed unchanged. The first-prompt cadence does not automatically retitle a child; the all-messages cadence may append a new revision after the child receives a later human prompt.
## Configuration
@@ -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 the optional provider/model route used to generate it. The service normalizes and validates the result before it becomes durable.
A provider supplies a branded stable id, automatic mode (`first-prompt` or `all-prompts`), 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).

View File

@@ -29,7 +29,7 @@ fork 出的会话会原样继承种子中的标题事件。首消息节奏不会
## 提供方约定
提供方会提供带品牌类型的稳定 id、自动模式`first-message``all-user-messages`)和 `generate(request)`。请求携带活跃会话、截至一次固定修订的所有符合条件消息、可用时当前已记录的主请求路由,以及取消信号。结果包含非空标题、该请求中互不重复且有序的来源消息 seq以及生成该标题时使用的可选提供方模型路由。服务会在结果持久保存前进行规范化和验证。
提供方会提供带品牌类型的稳定 id、自动模式`first-prompt``all-prompts`)和 `generate(request)`。请求携带活跃会话、截至一次固定修订的所有符合条件消息、可用时当前已记录的主请求路由,以及取消信号。结果包含非空标题、该请求中互不重复且有序的来源消息 seq以及生成该标题时使用的可选提供方模型路由。服务会在结果持久保存前进行规范化和验证。
参见[会话标题数据结构](../../../docs/subsystems/session-title.md)与[已实现决策](../../../.agents/notes/implemented/feature/2026-07-21-log-backed-session-titles.md)。

View File

@@ -120,7 +120,7 @@ export interface SessionTitleUserMessage {
}
/** Automatic generation cadence owned by a registered provider. */
export type SessionTitleAutomaticMode = 'first-message' | 'all-user-messages'
export type SessionTitleAutomaticMode = 'first-prompt' | 'all-prompts'
/** Immutable input supplied to one title-provider call. */
export interface SessionTitleProviderRequest {
@@ -467,7 +467,7 @@ export class SessionTitleService extends Service {
const registration = this.registration
if (registration !== undefined && !registration.closing) {
const messages = collectSessionTitleMessages(session.events, event.seq)
const shouldSchedule = registration.provider.automatic === 'all-user-messages'
const shouldSchedule = registration.provider.automatic === 'all-prompts'
|| (session.header.parentSession === undefined && messages.length === 1 && this.get(session) === undefined)
if (shouldSchedule) {
const state = this.stateFor(session)
@@ -726,7 +726,7 @@ export class SessionTitleService extends Service {
if (typeof candidate.id !== 'string' || candidate.id.length === 0) {
throw new Error('session-title provider id must be a non-empty string')
}
if (candidate.automatic !== 'first-message' && candidate.automatic !== 'all-user-messages') {
if (candidate.automatic !== 'first-prompt' && candidate.automatic !== 'all-prompts') {
throw new Error('session-title provider automatic mode is invalid')
}
if (typeof candidate.generate !== 'function') {

View File

@@ -61,7 +61,7 @@ export function normalizeSessionTitle(input: string, maxBytes: number): string {
}
/**
* Derive the deterministic first-message fallback.
* Derive the deterministic first-prompt fallback.
* @param input - text from the first eligible human message.
* @param maxWords - positive whitespace-delimited word cap.
* @param maxBytes - positive UTF-8 byte cap.

View File

@@ -3,13 +3,13 @@
import { describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import * as SessionTitleInvariantCompanion from '@deepseek-ai/dsh-session-title/invariant'
import InvariantService, { InvariantError } from '@deepseek-ai/dsh-invariants'
import InvariantRegistry, { InvariantError } from '@deepseek-ai/dsh-invariants'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
async function setup(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(InvariantService, { enabled: true })
await ctx.plugin(InvariantRegistry, { enabled: true })
await ctx.plugin(SessionTitleInvariantCompanion)
return ctx
}

View File

@@ -5,8 +5,8 @@ import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import SessionPersistenceSqlite from '@deepseek-ai/dsh-session-persistence-sqlite'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import SqliteSessionPersistence from '@deepseek-ai/dsh-session-persistence-sqlite'
import SessionTitleService, { foldSessionTitle } from '@deepseek-ai/dsh-session-title'
const CONFIG = {
@@ -57,14 +57,14 @@ describe('session title persistence round trips', () => {
const id = SessionId('title-jsonl')
const writer = new Context()
await writer.plugin(SessionStore)
await writer.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await writer.plugin(JsonlSessionPersistence, { root, compression: 'none' })
await writer.plugin(SessionTitleService, CONFIG)
await appendPersistedTitle(writer, id)
await writer.fiber.dispose()
const reader = new Context()
await reader.plugin(SessionStore)
await reader.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await reader.plugin(JsonlSessionPersistence, { root, compression: 'none' })
await expectPersistedTitle(reader, id)
await reader.fiber.dispose()
})
@@ -76,14 +76,14 @@ describe('session title persistence round trips', () => {
const id = SessionId('title-sqlite')
const writer = new Context()
await writer.plugin(SessionStore)
await writer.plugin(SessionPersistenceSqlite, { path })
await writer.plugin(SqliteSessionPersistence, { path })
await writer.plugin(SessionTitleService, CONFIG)
await appendPersistedTitle(writer, id)
await writer.fiber.dispose()
const reader = new Context()
await reader.plugin(SessionStore)
await reader.plugin(SessionPersistenceSqlite, { path })
await reader.plugin(SqliteSessionPersistence, { path })
await expectPersistedTitle(reader, id)
await reader.fiber.dispose()
})

View File

@@ -1,6 +1,6 @@
import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it, vi } from 'vitest'
import LlmService, { createUserMessage, deepFreeze, markAgentLoopRequest } from '@deepseek-ai/dsh-llm'
import LlmRuntime, { createUserMessage, deepFreeze, markAgentLoopRequest } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import SessionTitleService, {
SessionTitleProviderId,
@@ -48,7 +48,7 @@ function appendRoute(session: ReturnType<Context['sessions']['create']>, reason:
}
describe('SessionTitleService provider lifecycle', () => {
it('inherits title events across forks, skips first-message retitling, and lets all-messages update later', async () => {
it('inherits title events across forks, skips first-prompt retitling, and lets all-messages update later', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionTitleService, CONFIG)
@@ -71,7 +71,7 @@ describe('SessionTitleService provider lifecycle', () => {
}))
const disposeFirst = ctx.sessionTitle.register({
id: SessionTitleProviderId('fork-first'),
automatic: 'first-message',
automatic: 'first-prompt',
generate: firstGenerate,
})
child.append('turn/start', {
@@ -91,7 +91,7 @@ describe('SessionTitleService provider lifecycle', () => {
}))
ctx.sessionTitle.register({
id: SessionTitleProviderId('fork-all'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate: allGenerate,
})
child.append('turn/start', {
@@ -112,14 +112,14 @@ describe('SessionTitleService provider lifecycle', () => {
expect(ctx.sessionTitle.get(parent)?.title).toBe('Inherited title prompt')
})
it('runs a first-message provider once after the routed request and retries only through refresh', async () => {
it('runs a first-prompt provider once after the routed request and retries only through refresh', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionTitleService, CONFIG)
const requests: SessionTitleProviderRequest[] = []
const provider: SessionTitleProvider = {
id: SessionTitleProviderId('first-model'),
automatic: 'first-message',
automatic: 'first-prompt',
async generate(request) {
requests.push(request)
return {
@@ -175,7 +175,7 @@ describe('SessionTitleService provider lifecycle', () => {
let observedSignal: AbortSignal | undefined
const first: SessionTitleProvider = {
id: SessionTitleProviderId('winner'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate(request) {
observedSignal = request.signal
return pending.promise
@@ -184,7 +184,7 @@ describe('SessionTitleService provider lifecycle', () => {
const dispose = ctx.sessionTitle.register(first)
expect(() => ctx.sessionTitle.register({
id: SessionTitleProviderId('duplicate'),
automatic: 'first-message',
automatic: 'first-prompt',
generate: async () => ({ title: 'duplicate', messageSeqs: [0] }),
})).toThrow(/already registered/)
@@ -211,7 +211,7 @@ describe('SessionTitleService provider lifecycle', () => {
const replacement: SessionTitleProvider = {
id: SessionTitleProviderId('replacement'),
automatic: 'first-message',
automatic: 'first-prompt',
generate: async () => ({ title: 'replacement', messageSeqs: [message.seq] }),
}
const disposeReplacement = ctx.sessionTitle.register(replacement)
@@ -226,7 +226,7 @@ describe('SessionTitleService provider lifecycle', () => {
const requests: SessionTitleProviderRequest[] = []
const provider: SessionTitleProvider = {
id: SessionTitleProviderId('all-model'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate(request) {
requests.push(request)
if (requests.length === 1) return firstResult.promise
@@ -262,13 +262,13 @@ describe('SessionTitleService provider lifecycle', () => {
it('runs an all-messages revision when the next main request reuses its logged header', async () => {
const ctx = new Context()
await ctx.plugin(LlmService)
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionTitleService, CONFIG)
const requests: SessionTitleProviderRequest[] = []
ctx.sessionTitle.register({
id: SessionTitleProviderId('unchanged-route'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
async generate(request) {
requests.push(request)
return {
@@ -316,7 +316,7 @@ describe('SessionTitleService provider lifecycle', () => {
it('ignores model streams that are not a matching loop request', async () => {
const ctx = new Context()
await ctx.plugin(LlmService)
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionTitleService, CONFIG)
const generate = vi.fn(async (request: SessionTitleProviderRequest): Promise<SessionTitleProviderResult> => ({
@@ -325,7 +325,7 @@ describe('SessionTitleService provider lifecycle', () => {
}))
ctx.sessionTitle.register({
id: SessionTitleProviderId('request-filter'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate,
})
const options = { provider: 'main-route', model: 'chat-model', messages: [] }
@@ -353,7 +353,7 @@ describe('SessionTitleService provider lifecycle', () => {
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
const provider: SessionTitleProvider = {
id: SessionTitleProviderId('failing'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate: async () => { throw new Error('title backend failed') },
}
ctx.sessionTitle.register(provider)

View File

@@ -75,7 +75,7 @@ describe('SessionTitleService.rename', () => {
}))
ctx.sessionTitle.register({
id: SessionTitleProviderId('pin-provider'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate,
})
const session = ctx.sessions.create(SessionId('rename-pin'))
@@ -139,7 +139,7 @@ describe('SessionTitleService.rename', () => {
})
ctx.sessionTitle.register({
id: SessionTitleProviderId('deferred-provider'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate,
})
const session = ctx.sessions.create(SessionId('rename-supersede'))

View File

@@ -74,7 +74,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => {
}))
withProvider.sessionTitle.register({
id: SessionTitleProviderId('empty-provider'),
automatic: 'first-message',
automatic: 'first-prompt',
generate,
})
const providerEmpty = withProvider.sessions.create(SessionId('empty-provider'))
@@ -94,7 +94,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => {
let observed: SessionTitleProviderRequest | undefined
ctx.sessionTitle.register({
id: SessionTitleProviderId('explicit-no-route'),
automatic: 'first-message',
automatic: 'first-prompt',
async generate(request) {
observed = request
return { title: 'Explicit title', messageSeqs: [request.messages[0]!.seq] }
@@ -117,7 +117,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => {
let callerSignal: AbortSignal | undefined
callerCtx.sessionTitle.register({
id: SessionTitleProviderId('caller-cancel'),
automatic: 'first-message',
automatic: 'first-prompt',
generate(request) {
callerSignal = request.signal
return callerPending.promise
@@ -139,7 +139,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => {
let disposeSignal: AbortSignal | undefined
disposeCtx.sessionTitle.register({
id: SessionTitleProviderId('session-dispose'),
automatic: 'first-message',
automatic: 'first-prompt',
generate(request) {
disposeSignal = request.signal
return disposePending.promise
@@ -215,7 +215,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => {
const results: Array<ReturnType<typeof deferred<SessionTitleProviderResult>>> = []
ctx.sessionTitle.register({
id: SessionTitleProviderId('refresh-order'),
automatic: 'first-message',
automatic: 'first-prompt',
generate(request) {
requests.push(request)
const result = deferred<SessionTitleProviderResult>()
@@ -293,7 +293,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => {
const requests: SessionTitleProviderRequest[] = []
ctx.sessionTitle.register({
id: SessionTitleProviderId('service-unload'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate(request) {
requests.push(request)
return result.promise
@@ -362,12 +362,12 @@ describe('SessionTitleService provider validation and stale scheduling', () => {
expect(() => ctx.sessionTitle.register('provider' as never)).toThrow(/must be an object/)
expect(() => ctx.sessionTitle.register({
id: 1,
automatic: 'first-message',
automatic: 'first-prompt',
generate,
} as unknown as SessionTitleProvider)).toThrow(/id must be a non-empty string/)
expect(() => ctx.sessionTitle.register({
id: SessionTitleProviderId(''),
automatic: 'first-message',
automatic: 'first-prompt',
generate,
})).toThrow(/id must be a non-empty string/)
expect(() => ctx.sessionTitle.register({
@@ -377,7 +377,7 @@ describe('SessionTitleService provider validation and stale scheduling', () => {
})).toThrow(/automatic mode is invalid/)
expect(() => ctx.sessionTitle.register({
id: SessionTitleProviderId('missing-generate'),
automatic: 'first-message',
automatic: 'first-prompt',
generate: undefined,
} as unknown as SessionTitleProvider)).toThrow(/requires generate/)
})
@@ -390,7 +390,7 @@ describe('SessionTitleService provider validation and stale scheduling', () => {
}))
const dispose = ctx.sessionTitle.register({
id: SessionTitleProviderId('queued-dispose'),
automatic: 'all-user-messages',
automatic: 'all-prompts',
generate,
})
const session = startSession(ctx, 'queued-dispose')
@@ -414,7 +414,7 @@ describe('SessionTitleService provider validation and stale scheduling', () => {
let result: unknown
ctx.sessionTitle.register({
id: SessionTitleProviderId('invalid-results'),
automatic: 'first-message',
automatic: 'first-prompt',
generate: async () => result as SessionTitleProviderResult,
})
const session = startSession(ctx, 'invalid-results')

View File

@@ -21,7 +21,7 @@
"path": "../../util/brand"
},
{
"path": "../../support/invariants"
"path": "../../runtime-diagnostics/invariants"
},
{
"path": "../../llm/llm"