docs: rebalance prose cleanup and add trimming skill
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
/** Derived-message cache behavior against a from-scratch replay oracle. */
|
||||
/**
|
||||
* Derived-message cache contract against a scratch oracle: project new nodes
|
||||
* once, rebuild on surface generation changes, return fresh arrays over shared
|
||||
* frozen messages, and remain value-equal to replay at every step.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
|
||||
@@ -13,8 +13,8 @@ import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEventMap, SessionEventType, SurfaceIntent } from '@deepseek-ai/dsh-session'
|
||||
|
||||
// An appendable event: its type/data plus, for surface-eligible types, the explicit surface
|
||||
// intent the generator declares (mirroring how a real caller passes it).
|
||||
// Each arbitrary supplies its own surface intent; `build` must not synthesize
|
||||
// one or the property would fail to exercise malformed fixture choices.
|
||||
type Appendable = {
|
||||
[T in SessionEventType]: { type: T; data: SessionEventMap[T]; intent?: SurfaceIntent }
|
||||
}[SessionEventType]
|
||||
|
||||
@@ -155,9 +155,8 @@ describe('interruptedTurnClosers', () => {
|
||||
})
|
||||
|
||||
it('handles tool/call without a matching assistant/message entry gracefully', () => {
|
||||
// A tool/call event exists in the log but no assistant/message registered the callId in
|
||||
// pendingCalls (e.g., a plugin appended it directly, or the assistant/message from a prior
|
||||
// step didn't have this call).
|
||||
// A raw tool/call with no assistant-registered pending call has nothing to
|
||||
// answer; repair still closes the step and turn without synthesizing a result.
|
||||
const events: SessionEvent[] = [
|
||||
userTurnStart(1, 0),
|
||||
{ type: 'step/start', seq: 1, time: 1, data: { turn: 1, step: 1 } },
|
||||
|
||||
@@ -129,8 +129,8 @@ describe('Session', () => {
|
||||
it('rejects a surface-eligible append with no surfaceOp marker (runtime guard for the union-widening loophole)', () => {
|
||||
const session = new Session(SessionId('s5b'))
|
||||
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
// The typed overload makes surfaceOp mandatory only when the type argument is a SPECIFIC
|
||||
// SurfaceEventType literal.
|
||||
// A widened SessionEventType bypasses the overload's conditional requirement,
|
||||
// so the runtime guard must still reject the missing surface marker.
|
||||
const widenedType = 'user/message' as SessionEventType
|
||||
expect(() => session.append(widenedType, { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }))
|
||||
.toThrow(/surface-eligible and requires a surfaceOp marker/)
|
||||
@@ -657,8 +657,8 @@ describe('SessionStore', () => {
|
||||
})
|
||||
|
||||
it('enter() rejects a stale prepared session whose id is already live (no overwrite)', async () => {
|
||||
// prepare()/enter() are public cross-package primitives that a caller may separate with
|
||||
// arbitrary work.
|
||||
// A stale prepared object must not replace the live same-id entry; its later
|
||||
// detach would otherwise remove the wrong session.
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
const stale = ctx.sessions.prepare(SessionId('racy'))
|
||||
|
||||
@@ -70,14 +70,11 @@ describe('SurfaceManager', () => {
|
||||
|
||||
it('rebuild with replace operation splices out shadowed nodes', () => {
|
||||
const s = surfaceSession()
|
||||
// seq: 0=turn/start, 1=user, 2=assistant, 3=turn/end
|
||||
// Surface nodes: seq 1 (user), seq 2 (assistant).
|
||||
// Replace both with a compaction marker. Both 1 and 2 are valid surface seqs.
|
||||
// Replace surface seqs 1 (user) and 2 (assistant) with the summary.
|
||||
s.append('assistant/message',
|
||||
{ turn: 2, step: 1, content: [{ type: 'text', text: 'summary' }] },
|
||||
{ surfaceOp: { op: 'replace', start: 1, end: 2 }, sourceEventSeqs: [1, 2] },
|
||||
)
|
||||
// Now the surface should have just the compaction node.
|
||||
expect(s.surface.nodes.length).toBe(1)
|
||||
expect(s.surface.nodes[0]!.seq).toBe(4) // seq of the compaction marker
|
||||
expect(s.surface.nodes[0]!.prev).toBeNull()
|
||||
|
||||
@@ -4,7 +4,9 @@ import { Session, SessionId, isToolPairingBalanced } from '../src/index.ts'
|
||||
import type { SessionEvent, SurfaceNode } from '../src/index.ts'
|
||||
|
||||
/**
|
||||
* Unit coverage for the tool-pairing balance check.
|
||||
* Unit coverage for compaction-cut safety: a cut is balanced only when it
|
||||
* separates no assistant tool call from its result. Non-step nodes are neutral,
|
||||
* and replace operations prove surface order—not raw log order—is authoritative.
|
||||
*/
|
||||
|
||||
const SURFACE = { surfaceOp: 'append' as const }
|
||||
@@ -165,8 +167,8 @@ describe('isToolPairingBalanced — multiple tool calls in one assistant message
|
||||
})
|
||||
|
||||
describe('isToolPairingBalanced — a mid-step injection context/message', () => {
|
||||
// A background task-done inject() lands a context/message inside an open step, between the
|
||||
// assistant (with a tool-call) and its tool/result.
|
||||
// The injected context is pairing-neutral, but both adjacent cuts remain
|
||||
// unbalanced because the tool call is still open across them.
|
||||
function midStepInjection(): Session {
|
||||
const s = new Session(SessionId('mid-inject'))
|
||||
s.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
@@ -217,7 +219,8 @@ describe('isToolPairingBalanced on an injection turn (no step)', () => {
|
||||
})
|
||||
|
||||
describe('isToolPairingBalanced — CBR-001: a head checkpoint left by a replace op', () => {
|
||||
// The case the log-position scan got wrong.
|
||||
// A replacement checkpoint has a high log seq but sits at the surface head;
|
||||
// its cuts are balanced regardless of later raw-log neighbors.
|
||||
function checkpointHeadedSession(): Session {
|
||||
const s = new Session(SessionId('checkpoint'))
|
||||
// A closed turn with a tool step → surface [u1, asst(call), result].
|
||||
|
||||
Reference in New Issue
Block a user