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

# Conflicts:
#	apps/cli/src/web.ts
#	apps/web/tests/smoke-fixture.e2e.ts
#	docs/architecture.i18n.yaml
#	packages/client/connection/src/client/fixture.ts
#	packages/client/ui-conversation/README.md
#	packages/client/ui-conversation/package.json
#	packages/client/ui-conversation/src/client/apply.ts
#	packages/client/ui-conversation/src/client/chat/ChatView.tsx
#	packages/client/ui-conversation/src/client/chat/register.ts
#	packages/client/ui-conversation/src/client/contract/slots.ts
#	packages/client/ui-conversation/src/client/contract/views.ts
#	packages/client/ui-conversation/src/client/service.ts
#	packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx
#	packages/client/ui-conversation/src/client/stores.ts
#	packages/client/ui-conversation/tests/skeleton-branches.spec.tsx
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/host/runtime/src/boot.ts
#	packages/host/runtime/tests/host-runtime.spec.ts
#	pnpm-lock.yaml
This commit is contained in:
Yichen Jiang
2026-07-23 19:51:50 +08:00
390 changed files with 18322 additions and 5565 deletions

View File

@@ -6,7 +6,7 @@ import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { defineTool } from '@deepseek-ai/dsh-tools'
import ToolRegistry, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import type { Agent, RequestErrorDecision } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
@@ -166,7 +166,7 @@ describe('bounded transient retry policy', () => {
])
;({ ctx: context } = await harness(adapter))
let toolExecutions = 0
context.tools.register(defineTool({
context.tools.register(defineContentToolFixture({
name: 'danger',
description: 'must not run for a failed provider attempt',
parameters: {},

View File

@@ -58,7 +58,8 @@ export function isAgentLoopRequest(request: GenerateOptions): boolean {
}
/**
* Deep-freeze a value in place, guarding cycles, so later mutation throws.
* Deep-freeze a value in place with an iterative traversal, guarding cycles,
* so later mutation throws without imposing a JavaScript call-stack depth cap.
* {@link AbortSignal} objects are deliberately skipped because they are the
* request's live cancellation channel and freezing them breaks abort.
* @param value - the value to freeze in place.
@@ -66,16 +67,31 @@ export function isAgentLoopRequest(request: GenerateOptions): boolean {
*/
export function deepFreeze<T>(value: T): T {
const seen = new WeakSet<object>()
const walk = (node: unknown): void => {
if (node === null || typeof node !== 'object') return
if (node instanceof AbortSignal) return
if (seen.has(node)) return
const pending: (
| { kind: 'visit'; node: unknown }
| { kind: 'property'; source: Record<string, unknown>; key: string }
)[] = [{ kind: 'visit', node: value }]
while (pending.length > 0) {
const task = pending.pop()
/* v8 ignore next -- the loop condition guarantees one pending task. */
if (task === undefined) continue
if (task.kind === 'property') {
pending.push({ kind: 'visit', node: task.source[task.key] })
continue
}
const node = task.node
if (node === null || typeof node !== 'object') continue
if (node instanceof AbortSignal) continue
if (seen.has(node)) continue
seen.add(node)
Object.freeze(node)
for (const key of Object.keys(node)) {
walk((node as Record<string, unknown>)[key])
const keys = Object.keys(node)
for (let index = keys.length - 1; index >= 0; index--) {
const key = keys[index]
/* v8 ignore next -- the loop is bounded by the captured key count. */
if (key === undefined) continue
pending.push({ kind: 'property', source: node as Record<string, unknown>, key })
}
}
walk(value)
return value
}

View File

@@ -56,6 +56,26 @@ describe('deepFreeze', () => {
deepFreeze(cyclic)
expect(Object.isFrozen(cyclic)).toBe(true)
})
it('freezes nesting deeper than the JavaScript call stack', () => {
const depth = 5_000
const root: unknown[] = []
let cursor = root
for (let index = 0; index < depth; index++) {
const child: unknown[] = []
cursor.push(child)
cursor = child
}
deepFreeze(root)
cursor = root
for (let index = 0; index < depth; index++) {
expect(Object.isFrozen(cursor)).toBe(true)
cursor = cursor[0] as unknown[]
}
expect(Object.isFrozen(cursor)).toBe(true)
})
})
describe('agent-loop request identity', () => {