Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification

# Conflicts:
#	apps/web/tests/markdown-images.e2e.ts
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/session.i18n.yaml
#	docs/event-producer-consumer.md
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	packages/compact/compact-basic/tests/compact-basic.spec.ts
#	packages/context/time-context/tests/invariant.spec.ts
#	packages/context/time-context/tests/time-context.spec.ts
#	packages/core/agent-loop/src/invariant.ts
#	packages/core/agent-loop/tests/contract-regressions.spec.ts
#	packages/core/agent-loop/tests/request-reconstruction.spec.ts
#	packages/core/agent/tests/agent.spec.ts
#	packages/core/session/README.i18n.yaml
#	packages/core/session/src/index.ts
#	packages/core/session/tests/derived-cache.spec.ts
#	packages/core/session/tests/request-header.spec.ts
#	packages/core/session/tests/session.spec.ts
#	packages/core/session/tests/surface.spec.ts
#	packages/fs/tool-str-replace-editor/tests/tools.spec.ts
#	packages/goal/goal/tests/goal.spec.ts
#	packages/plan/plan-mode/tests/plan-mode.spec.ts
#	packages/pty/pty-local/tests/index.spec.ts
#	packages/pty/pty-local/tests/local.spec.ts
#	packages/pty/pty/tests/service.spec.ts
#	packages/pty/tool-bash-persistent/tests/loader-composition.spec.ts
#	packages/pty/tool-bash-persistent/tests/tools.spec.ts
#	packages/pty/tool-pty/tests/loader-composition.spec.ts
#	packages/pty/tool-pty/tests/tools.spec.ts
#	packages/session-title/session-title-all-messages-llm/tests/provider.spec.ts
#	packages/tasks/tasks-local/tests/tasks.spec.ts
#	packages/ui/user-approval/tests/approval.spec.ts
This commit is contained in:
_Kerman
2026-08-05 13:53:35 +08:00
75 changed files with 1087 additions and 358 deletions
@@ -75,7 +75,7 @@ describe('foldRequestHeader', () => {
})
it('takes the latest full snapshot and skips unrelated events', () => {
const session = new Session(SessionId('fold'))
const session = Session.create(SessionId('fold'))
session.append('turn/start', { turn: 1 })
session.append('request/header', { header: { config: CONFIG, system: 'first' }, reason: 'initial' })
session.append('user/message', createUserMessage({
@@ -91,9 +91,9 @@ describe('legacy request-header format', () => {
const legacy = [{
type: 'request/header-delta', seq: 0, time: 1, data: { config: CONFIG },
}] as unknown as SessionEvent[]
expect(() => new Session(SessionId('legacy'), legacy)).toThrow(/unsupported legacy request\/header-delta/)
expect(() => Session.create(SessionId('legacy'), legacy)).toThrow(/unsupported legacy request\/header-delta/)
const session = new Session(SessionId('legacy-append-delta'))
const session = Session.create(SessionId('legacy-append-delta'))
const appendLegacy = session.append.bind(session) as (type: string, data: unknown) => SessionEvent
expect(() => appendLegacy('request/header-delta', { config: CONFIG }))
.toThrow(/unsupported legacy request\/header-delta/)
@@ -104,10 +104,10 @@ describe('legacy request-header format', () => {
const legacy = [{
type: 'request/header', seq: 0, time: 1, data: { header: { config: CONFIG }, reason: 'fallback' },
}] as unknown as SessionEvent[]
expect(() => new Session(SessionId('legacy-seed-reason'), legacy))
expect(() => Session.create(SessionId('legacy-seed-reason'), legacy))
.toThrow('unsupported legacy request/header reason "fallback"')
const session = new Session(SessionId('legacy-append-reason'))
const session = Session.create(SessionId('legacy-append-reason'))
const appendLegacy = session.append.bind(session) as (type: string, data: unknown) => SessionEvent
expect(() => appendLegacy('request/header', { header: { config: CONFIG }, reason: 'fallback' }))
.toThrow('unsupported legacy request/header reason "fallback"')
@@ -130,13 +130,13 @@ describe('Session.requestContext', () => {
}
it('reads undefined before any record exists', () => {
expect(new Session(SessionId('no-capacity')).requestContext()).toBeUndefined()
expect(Session.create(SessionId('no-capacity')).requestContext()).toBeUndefined()
})
it('folds a seeded log on first read, taking the last record', () => {
// The fold watermark starts at 0 with the seed already in the log, so the
// first read must consume the whole seed rather than skip it.
const session = new Session(SessionId('seeded-capacity'), seedWith(
const session = Session.create(SessionId('seeded-capacity'), seedWith(
CAPACITY,
{ ...CAPACITY, model: 'later', contextWindow: 256_000 },
))
@@ -144,7 +144,7 @@ describe('Session.requestContext', () => {
})
it('advances incrementally across appends and skips unrelated events', () => {
const session = new Session(SessionId('incremental-capacity'), seedWith(CAPACITY))
const session = Session.create(SessionId('incremental-capacity'), seedWith(CAPACITY))
expect(session.requestContext()).toEqual(CAPACITY)
session.append('todo/write', { todos: [] })
expect(session.requestContext()).toEqual(CAPACITY)
@@ -155,7 +155,7 @@ describe('Session.requestContext', () => {
})
it('folds a batch appended between two reads', () => {
const session = new Session(SessionId('batched-capacity'), seedWith(CAPACITY))
const session = Session.create(SessionId('batched-capacity'), seedWith(CAPACITY))
expect(session.requestContext()).toEqual(CAPACITY)
session.append('request/context', { ...CAPACITY, contextWindow: 200_000 })
session.append('todo/write', { todos: [] })
@@ -164,7 +164,7 @@ describe('Session.requestContext', () => {
})
it('exposes a frozen record so a reader cannot desync later comparisons', () => {
const session = new Session(SessionId('frozen-capacity'), seedWith(CAPACITY))
const session = Session.create(SessionId('frozen-capacity'), seedWith(CAPACITY))
const held = session.requestContext()
if (held === undefined) throw new Error('expected a folded capacity record')
expect(Object.isFrozen(held)).toBe(true)