Merge latest master into subagent policy inheritance

Retarget the feature branch to the current master tip without rewriting its existing review history. Keeping this as a dedicated merge checkpoint makes the later simplification diff attributable to the stacked child rather than mixing base movement with design changes.

Resolve the identified-message API drift in the feature tests by constructing complete user messages, reading the nested tool-result message shape, and adapting the prompt-submit listener signature. Preserve both sides of the user-approval conflict: master’s createUserMessage wrapper and the feature’s inherited-policy attribution.

Regenerate the Cordis and persistence catalogs, re-record the session README pair, and refresh the affected ACP/headless fixtures so derived artifacts describe the merged source rather than either parent in isolation.

Validated with the focused policy/session/persistence/query suites (430 tests), focused ACP/headless snapshots (3 tests), build, doc-sync (25 gates), lint, hygiene, and git diff checks.
This commit is contained in:
Tianyi Cui
2026-07-28 21:11:20 +08:00
425 changed files with 7530 additions and 3109 deletions

View File

@@ -14,6 +14,7 @@ import { randomUUID } from 'node:crypto'
import { isAbsolute } from 'node:path'
import { Readable, Writable } from 'node:stream'
import Schema from 'schemastery'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import {
AgentSideConnection,
ndJsonStream,
@@ -146,7 +147,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
if (record === undefined || record.agent.session !== session) return
try {
if (event.type === 'assistant/message') {
for (const block of event.data.content) {
for (const block of event.data.message.content) {
if (block.type === 'text' && block.text.length > 0) {
notify({
sessionId: record.agent.session.id,
@@ -274,7 +275,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
}
record.inflight = inflight
try {
record.agent.followup({ content: [{ type: 'text', text }], source: { kind: 'user' } })
record.agent.followup(createUserMessage({ content: [{ type: 'text', text }], source: { kind: 'user' } }))
// The machine's send() contains listener failures and accepts
// any typed input; this guards a future synchronous throw so the
// slot cannot wedge.

View File

@@ -1,6 +1,6 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { PROTOCOL_VERSION } from '@agentclientprotocol/sdk'
import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
import { createUserMessage, CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session'
import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import { makeBridgeHarness, textResponse, type BridgeHarness } from './harness.ts'
@@ -49,7 +49,7 @@ describe('ACP automation output boundary', () => {
sessionId: SessionId('foreign'),
agentOptions: { provider: 'mock', model: 'mock' },
})
agent.followup({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } }))
await agent.whenIdle()
expect(harness.updates).toHaveLength(0)
})

View File

@@ -1,3 +1,4 @@
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { PROTOCOL_VERSION } from '@agentclientprotocol/sdk'
import { SessionId } from '@deepseek-ai/dsh-session'
@@ -72,7 +73,7 @@ describe('ACP prompt lifecycle', () => {
harness.ctx.on('agent/inbox/enqueue', (subject) => {
if (subject === agent && !injected) {
injected = true
agent.inject({ content: [{ type: 'text', text: 'context' }], source: { kind: 'plugin', plugin: 'test' } })
agent.inject(createUserMessage({ content: [{ type: 'text', text: 'context' }], source: { kind: 'plugin', plugin: 'test' } }))
}
})
@@ -91,10 +92,10 @@ describe('ACP prompt lifecycle', () => {
inserted = true
const source = { kind: 'plugin', plugin: 'test' } as const
agent.session.append('turn/start', { turn: 1, trigger: { kind: 'message', source } })
agent.session.append('user/message', {
agent.session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'autonomous work' }],
source,
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
agent.session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
})