mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts: # .agents/notes/implemented/architecture/2026-07-22-unified-send-and-coalesced-user-messages.i18n.yaml # .agents/notes/implemented/architecture/2026-07-22-unified-send-and-coalesced-user-messages.md # .agents/notes/implemented/architecture/2026-07-22-unified-send-and-coalesced-user-messages.zh.md # .agents/notes/implemented/feature/2026-07-21-tui-skill-slash-command.i18n.yaml # .agents/notes/implemented/feature/2026-07-21-tui-skill-slash-command.md # .agents/notes/implemented/feature/2026-07-21-tui-skill-slash-command.zh.md # .agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.i18n.yaml # .agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.md # .agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.zh.md # docs/architecture.i18n.yaml # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/core.md # docs/core-data-structures/core.zh.md # docs/defensive-patterns.i18n.yaml # packages/client/runtime/src/client/sessions/session.ts # packages/client/runtime/tests/queue-store.spec.ts # packages/context/time-context/tests/time-context.spec.ts # packages/context/workspace-context/tests/workspace-context.spec.ts # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-loop/README.i18n.yaml # packages/core/agent-loop/README.md # packages/core/agent-loop/README.zh.md # packages/core/agent-loop/src/agent.ts # packages/core/agent/README.i18n.yaml # packages/core/agent/README.md # packages/core/agent/README.zh.md # packages/core/agent/src/types.ts # packages/core/agent/tests/agent.spec.ts # packages/core/scope/src/scoped-events.generated.ts # packages/goal/command-goal/tests/command-goal.spec.ts # packages/goal/goal-session/src/index.ts # packages/goal/goal-session/tests/goal-session.spec.ts # packages/goal/goal/tests/goal.spec.ts # packages/goal/goal/tests/projection.spec.ts # packages/goal/tool-goal/tests/tool-goal.spec.ts # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/events.schema.ts # packages/host/apiproxy/src/api/events.ts # packages/host/apiproxy/tests/api-proxy-workspace.spec.ts # packages/llm/llm/README.i18n.yaml # packages/llm/llm/README.zh.md # packages/llm/llm/src/index.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-pty/tests/loader-composition.spec.ts # packages/pty/tool-pty/tests/tools.spec.ts # packages/skill/tool-skill/tests/tool-skill.spec.ts # packages/tasks/tasks-local/tests/tasks.spec.ts # packages/ui/tui/src/index.ts # packages/ui/tui/tests/harness.ts # packages/ui/tui/tests/tui.spec.ts # scripts/gen-cordis-catalog.ts # scripts/type-equiv.manifest.json
This commit is contained in:
@@ -191,7 +191,7 @@ export class BasicCompactService extends CompactService {
|
||||
// A model-free prune can land before later summary work fails. That
|
||||
// durable reduction is sufficient retry proof; do not discard it just
|
||||
// because the optional second phase threw. Cancellation still wins.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the signal can abort while recovery is awaited.
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-condition -- the signal can abort while recovery is awaited.
|
||||
if (!signal.aborted && agent.session.surface.replaceGeneration > generation) {
|
||||
ctx.logger.warn(
|
||||
`context-overflow compaction failed after durable surface progress: ${message}; `
|
||||
@@ -201,14 +201,14 @@ export class BasicCompactService extends CompactService {
|
||||
return { kind: 'retry' }
|
||||
}
|
||||
ctx.logger.warn(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the signal can abort while recovery is awaited.
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-condition -- the signal can abort while recovery is awaited.
|
||||
`context-overflow compaction failed: ${message}; ${signal.aborted
|
||||
? 'cancellation prevents retry'
|
||||
: 'preserving the original request error'}`,
|
||||
)
|
||||
return next()
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the signal can abort while compaction is awaited.
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-condition -- the signal can abort while compaction is awaited.
|
||||
if (signal.aborted
|
||||
|| agent.session.surface.replaceGeneration <= generation) return next()
|
||||
if (result !== null) logResult(result, 'context overflow recovery')
|
||||
|
||||
@@ -49,7 +49,7 @@ export function selectCompactableRange(
|
||||
let accumulated = 0
|
||||
let keepFromIdx = pricedNodes.length
|
||||
for (let index = pricedNodes.length - 1; index >= 0; index -= 1) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
accumulated += pricedNodes[index]!.tokens
|
||||
keepFromIdx = index
|
||||
if (accumulated >= retainTokens) break
|
||||
@@ -57,15 +57,15 @@ export function selectCompactableRange(
|
||||
if (keepFromIdx === 0) return null
|
||||
|
||||
while (keepFromIdx > 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
if (toolPairingBalancedBefore(session, surfaceNodes[keepFromIdx]!)) break
|
||||
keepFromIdx -= 1
|
||||
}
|
||||
if (keepFromIdx === 0) return null
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
const first = surfaceNodes[0]!
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
const cutoff = surfaceNodes[keepFromIdx - 1]!
|
||||
return { start: first, end: cutoff }
|
||||
}
|
||||
@@ -98,11 +98,11 @@ export async function compactSurfaceRegion(
|
||||
`compactRegion: start seq ${start} (position ${startIdx}) is after end seq ${end} (position ${endIdx}) on the surface`,
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
if (!toolPairingBalancedBefore(session, nodes[startIdx]!)) {
|
||||
throw new Error(`compactRegion: start seq ${start} is not a balanced boundary (would split a step's tool-call/result pair)`)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
if (!toolPairingBalancedAfter(session, nodes[endIdx]!)) {
|
||||
throw new Error(`compactRegion: end seq ${end} is not a balanced boundary (would split a step, or the step is still open)`)
|
||||
}
|
||||
@@ -126,7 +126,9 @@ export async function compactSurfaceRegion(
|
||||
}
|
||||
const shadowedTokenCount = selected.reduce((total, node) => total + node.tokens, 0)
|
||||
const summarizationInput = buildSummarizationInput(session, shadowedSeqs)
|
||||
const { summary, provider, model, maxTokens } = await dependencies.summarize(summarizationInput, agent, signal)
|
||||
const {
|
||||
summary, rawOutput, provider, model, maxTokens, usage,
|
||||
} = await dependencies.summarize(summarizationInput, agent, signal)
|
||||
|
||||
const currentMeasurement = dependencies.meter.measure(session)
|
||||
if (!isDeepStrictEqual(currentMeasurement.nodes, lockedMeasurement.nodes)) {
|
||||
@@ -146,12 +148,14 @@ export async function compactSurfaceRegion(
|
||||
|
||||
const summaryEvent = session.append('compact/summary', {
|
||||
summary,
|
||||
...rawOutput === undefined ? {} : { rawOutput },
|
||||
shadowedRange: { start, end },
|
||||
shadowedSeqs,
|
||||
shadowedTokenCount,
|
||||
provider,
|
||||
model,
|
||||
...maxTokens === undefined ? {} : { maxTokens },
|
||||
...usage === undefined ? {} : { usage },
|
||||
})
|
||||
session.append('user/message', checkpointMessage, {
|
||||
surfaceOp: { op: 'replace', start, end },
|
||||
@@ -192,7 +196,7 @@ function buildSummarizationInput(
|
||||
const events = session.events
|
||||
const regionMessages = shadowedSeqs
|
||||
// shadowedSeqs are current surface seqs, so each is a valid log index.
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
.map(seq => session.deriveEventMessage(events[seq]!))
|
||||
.filter((message): message is Message => message !== null)
|
||||
return {
|
||||
@@ -209,7 +213,7 @@ function inspectTurnTail(
|
||||
let compactionInProgress = false
|
||||
let compactionStateKnown = false
|
||||
for (let index = events.length - 1; index >= 0; index -= 1) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
const event = events[index]!
|
||||
if (!compactionStateKnown) {
|
||||
if (event.type === 'compact/start') {
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
import type { Context } from 'cordis'
|
||||
import { createUserMessage, BlockAssembler } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock, FinishReason, GenerateOptions, Message, ToolSchema } from '@deepseek-ai/dsh-llm'
|
||||
import type {
|
||||
ContentBlock, FinishReason, GenerateOptions, Message, TokenUsage, ToolSchema,
|
||||
} from '@deepseek-ai/dsh-llm'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
|
||||
interface SummaryConfig {
|
||||
@@ -85,9 +87,13 @@ export interface SummarizationInput {
|
||||
/** Safe summary content plus the exact auxiliary call envelope recorded in provenance. */
|
||||
export interface SummaryResult {
|
||||
summary: ContentBlock[]
|
||||
/** Complete provider output before the text-only summary projection. */
|
||||
rawOutput?: ContentBlock[]
|
||||
provider: string
|
||||
model: string
|
||||
maxTokens?: number
|
||||
/** Provider-reported usage for this summarization request. */
|
||||
usage?: TokenUsage
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,15 +154,18 @@ export async function summarizeWithLlm(
|
||||
const error = finishError(assembler.finish)
|
||||
if (error !== undefined) throw error
|
||||
|
||||
const summary = textOnly(assembler.blocks())
|
||||
const rawOutput = assembler.blocks()
|
||||
const summary = textOnly(rawOutput)
|
||||
if (!summary.some(block => block.text.trim().length > 0)) {
|
||||
throw new Error('summarization produced no text summary content')
|
||||
}
|
||||
return {
|
||||
summary,
|
||||
rawOutput,
|
||||
provider: options.provider,
|
||||
model: options.model,
|
||||
maxTokens: config.maxTokens,
|
||||
...(assembler.usage === undefined ? {} : { usage: assembler.usage }),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user