Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input

# Conflicts:
#	docs/architecture.i18n.yaml
#	docs/architecture.md
#	docs/architecture.zh.md
#	docs/config-catalog.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/module-graph.md
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	packages/README.i18n.yaml
#	packages/client/connection/src/client/fixture.ts
#	packages/client/connection/src/index.ts
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/README.md
#	packages/client/runtime/README.zh.md
#	packages/client/runtime/src/client/sessions/conversation.ts
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/src/client/apply.ts
#	packages/client/ui-conversation/src/client/chat/ChatView.tsx
#	packages/client/ui-conversation/src/client/chat/MessageItem.tsx
#	packages/client/ui-conversation/src/client/contract/slots.ts
#	packages/client/ui-trajectory/tests/views.spec.tsx
#	packages/compact/compact-basic/README.i18n.yaml
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/index.ts
#	packages/host/apiproxy/src/api/sessions.ts
#	packages/host/apiproxy/src/index.ts
#	packages/host/apiproxy/tests/fetch-carrier.spec.ts
#	packages/llm/llm-deepseek/src/adapter.ts
#	packages/llm/llm-deepseek/tests/adapter.spec.ts
#	packages/llm/llm-deepseek/tests/serialize.spec.ts
#	packages/llm/llm-pi-ai/README.i18n.yaml
#	packages/llm/llm-pi-ai/src/adapter.ts
#	packages/llm/llm-pi-ai/src/index.ts
#	packages/llm/llm-pi-ai/tests/adapter.spec.ts
#	packages/llm/llm/src/types.ts
#	packages/ui/tui/README.i18n.yaml
#	packages/ui/tui/src/index.ts
#	packages/ui/tui/tests/tui.spec.ts
This commit is contained in:
Yichen Jiang
2026-07-28 11:41:40 +08:00
1499 changed files with 48621 additions and 21956 deletions
+34 -18
View File
@@ -111,6 +111,8 @@ export class BasicCompactService extends CompactService {
readonly config: ResolvedConfig
private readonly warnedPressureConfigTargets = new Set<string>()
private readonly overflowRetries = new WeakMap<Agent, number>()
private readonly overflowAgents = new WeakMap<Session, Agent>()
constructor(ctx: Context, config: BasicCompactConfig = {}) {
super(ctx)
@@ -119,8 +121,8 @@ export class BasicCompactService extends CompactService {
}
/**
* Register the automatic post-step pressure and context-overflow recovery
* listeners. `compactIfNeeded` stays dynamically dispatched so subclass
* Register automatic between-step pressure and model-request overflow
* recovery. `compactIfNeeded` stays dynamically dispatched so subclass
* overrides are honored at event time.
*/
private _registerAutomaticCompaction(): void {
@@ -133,7 +135,7 @@ export class BasicCompactService extends CompactService {
)
}
ctx.on('agent/post-step', async (
ctx.on('agent/step', async (
agent: Agent,
_turn: number,
_step: number,
@@ -142,35 +144,47 @@ export class BasicCompactService extends CompactService {
if (signal.aborted) return
try {
const result = await this.compactIfNeeded(agent, 'pressure', signal)
if (result !== null) logResult(result, 'post-step pressure')
if (result !== null) logResult(result, 'step pressure')
} catch (error: unknown) {
if (error instanceof TargetPressureConfigError) {
if (this.warnedPressureConfigTargets.has(error.targetKey)) return
this.warnedPressureConfigTargets.add(error.targetKey)
}
const message = error instanceof Error ? error.message : String(error)
ctx.logger.warn(`post-step compaction failed: ${message}; continuing the turn`)
ctx.logger.warn(`step compaction failed: ${message}; continuing the turn`)
}
})
ctx.on('agent/settled', (agent) => {
this.overflowRetries.delete(agent)
})
// A successful response starts a fresh overflow-recovery sequence even
// when tool calls continue the same turn into another request.
ctx.on('session/event', (session, event) => {
if (event.type !== 'assistant/message') return
const agent = this.overflowAgents.get(session)
if (agent !== undefined) this.overflowRetries.delete(agent)
})
ctx.on('agent/request-error', async (
agent,
_turn,
_step,
_error,
failure,
priorFailures,
_priorFailures,
_retryPolicy,
signal,
next,
) => {
const priorOverflowFailures = priorFailures.filter(
item => item.code === CONTEXT_WINDOW_EXCEEDED_CODE,
).length
if (failure.code !== CONTEXT_WINDOW_EXCEEDED_CODE || signal.aborted) return next()
this.overflowAgents.set(agent.session, agent)
const target = routedTarget(agent.session)
if (target === undefined) return next()
const policy = resolveTargetPolicy(this.config, target)
if (priorOverflowFailures >= policy.maxOverflowRetries) return next()
const retries = this.overflowRetries.get(agent) ?? 0
if (retries >= policy.maxOverflowRetries) return next()
const generation = agent.session.surface.replaceGeneration
let result: CompactionResult | null
@@ -181,27 +195,29 @@ 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 -- signal can abort while recovery is awaited.
// eslint-disable-next-line @typescript-eslint/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}; `
+ 'retrying from the replacement surface',
)
return { action: 'retry' }
this.overflowRetries.set(agent, retries + 1)
return { kind: 'retry' }
}
ctx.logger.warn(
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- signal can abort while recovery is awaited.
// eslint-disable-next-line @typescript-eslint/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 -- signal can abort while compaction is awaited.
// eslint-disable-next-line @typescript-eslint/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')
return { action: 'retry' }
this.overflowRetries.set(agent, retries + 1)
return { kind: 'retry' }
})
}
@@ -228,12 +244,12 @@ export class BasicCompactService extends CompactService {
}
/**
* Compact for replayed post-step pressure or one provider-confirmed context
* Compact for replayed step-boundary pressure or one provider-confirmed context
* overflow. Both triggers price the latest durable routed request envelope;
* overflow bypasses the normal threshold and retained-tail policy so it can
* force one useful balanced reduction.
* @param agent - agent whose latest durable routed request is measured.
* @param trigger - normal post-step pressure or context-overflow recovery.
* @param trigger - normal step-boundary pressure or context-overflow recovery.
* @param signal - live turn cancellation signal forwarded to summarization.
* @returns the latest summary compaction result, or `null` when no summary ran.
*/
@@ -272,7 +288,7 @@ export class BasicCompactService extends CompactService {
return this.compactRegion(range.start, range.end, agent, signal)
}
const context = await this.ctx.llm.resolveModelContext(target.provider, target.model)
const context = (await this.ctx.llm.resolveModelInfo(target.provider, target.model, signal)).context
const targetKey = `${target.provider}/${target.model}`
if (context === undefined) {
throw new TargetPressureConfigError(
+5 -5
View File
@@ -177,10 +177,10 @@ export async function compactSurfaceRegion(
/**
* Reconstruct the last routed request's cacheable prefix for the shadowed
* region: its system prompt and tool schemas, then the request-only message
* prefix followed by the region's own derived messages in surface order. The
* summarizer appends only the compaction instruction after this, so the call
* is a genuine prefix of the conversation and reuses the provider's KV cache.
* region: its system prompt and tool schemas, then the region's own derived
* messages in surface order. The summarizer appends only the compaction
* instruction after this, so the call is a genuine prefix of the conversation
* and reuses the provider's KV cache.
* @param session - session supplying the request header and per-node projection.
* @param shadowedSeqs - the surface-node seqs, in order, being compacted.
* @returns the replayed conversation prefix to condense.
@@ -199,7 +199,7 @@ function buildSummarizationInput(
return {
...header?.system === undefined ? {} : { system: header.system },
...header?.tools === undefined ? {} : { tools: header.tools },
messages: [...header?.messagePrefix ?? [], ...regionMessages],
messages: regionMessages,
}
}
@@ -78,7 +78,7 @@ export interface SummarizationInput {
readonly system?: string
/** The conversation's tool schemas, reused for prefix-cache alignment; absent when the request carried none. */
readonly tools?: readonly ToolSchema[]
/** The request prefix followed by the shadowed region, in surface order, that precedes the compaction instruction. */
/** The shadowed region, in surface order, that precedes the compaction instruction. */
readonly messages: readonly Message[]
}
+1 -1
View File
@@ -38,7 +38,7 @@ export interface ModelCompactPolicyConfig extends CompactPolicyConfig {
export interface BasicCompactConfig extends CompactPolicyConfig {
/** Exact provider/model overrides; duplicate targets fail plugin load. */
modelPolicies?: ModelCompactPolicyConfig[]
/** Enable automatic post-step pressure and overflow-recovery listeners. Defaults to `true`. */
/** Enable automatic step-boundary pressure and overflow-recovery listeners. Defaults to `true`. */
auto?: boolean
}