fix(boot): stabilize profile module fallbacks

This commit is contained in:
Dudu-0223
2026-08-26 00:13:36 +08:00
committed by fengsy
parent fd53e479b4
commit 6e4fabdc1f
7 changed files with 12 additions and 235 deletions
@@ -62,7 +62,7 @@ Worktree isolation is not a harness runtime behavior. A deployment or prompt may
## Testing
Package tests cover identity, name and authority checks, provider selection, reserved-id persistence collisions, child-before-Lead flush ordering, durable provisioning failure and pending-inbox JSONL/SQLite reconciliation, concurrent target-local ordering, pending/history de-duplication, mailbox limits, post-flush notification, bounded disposal with in-flight creation and dispatch cancellation, failed-member cleanup, task CAS and DAG validation, write-scope warnings, wait cancellation/timeout, inbox-preserving interruption, ordinary-fork isolation, legacy-control shadowing, compact declared-schema result rendering, and scoped registration HMR at per-file 100% coverage. A keyless headless Loader snapshot assembles the real Team plugins and exercises two teammates, dependent tasks, peer delivery, waiting, completion, and Lead aggregation. A CLI product test loads the private Agent Teams profile bundle, starts `dsh run` through the real Loader and direct core front door, and verifies normal process exit with persisted Team and child logs.
Package tests cover identity, name and authority checks, provider selection, reserved-id persistence collisions, child-before-Lead flush ordering, durable provisioning failure and pending-inbox JSONL/SQLite reconciliation, concurrent target-local ordering, pending/history de-duplication, mailbox limits, post-flush notification, bounded disposal with in-flight creation and dispatch cancellation, failed-member cleanup, task CAS and DAG validation, write-scope warnings, wait cancellation/timeout, inbox-preserving interruption, ordinary-fork isolation, legacy-control shadowing, compact declared-schema result rendering, and scoped registration HMR at per-file 100% coverage. A keyless product snapshot loads the private Agent Teams profile bundle through `dsh run` and pins its complete model-visible tool list, Team policy, and durable workflow projection for two teammates, dependent tasks, peer delivery, waiting, completion, and aggregation. A CLI e2e reuses the same deterministic adapter and verifies normal process exit with persisted Team and child logs.
## Consequences
@@ -62,7 +62,7 @@ Worktree isolation 不是 harness runtime 行为。deployment 或 prompt 可以
## Testing
Package test 以逐文件 100% coverage 覆盖身份、名字与权限检查、provider 选择、预留 id 持久化冲突、child-before-Lead flush 顺序、持久 provisioning 失败与 pending-inbox JSONLSQLite 对账、target-local 并发顺序、pendinghistory 去重、mailbox 限额、flush 后 notification、取消在途创建与 dispatch 的有界 dispose、failed member cleanup、task CAS 与 DAG 校验、write-scope warning、wait canceltimeout、保留 inbox 的 interrupt、普通 fork 隔离、旧 control shadowing、声明 schema 的紧凑结果渲染与 scoped registration HMR。一条 keyless headless Loader 快照会组合真实 Team 插件,并运行两个 teammate、依赖任务、peer 投递、等待、完成与 Lead 汇总。CLI 产品测试会加载私有 Agent Teams profile bundle,通过真实 Loader 与直接 core front door 启动 `dsh run`,并验证带持久 Team 与 child 日志的正常退出。
Package test 以逐文件 100% coverage 覆盖身份、名字与权限检查、provider 选择、预留 id 持久化冲突、child-before-Lead flush 顺序、持久 provisioning 失败与 pending-inbox JSONLSQLite 对账、target-local 并发顺序、pendinghistory 去重、mailbox 限额、flush 后 notification、取消在途创建与 dispatch 的有界 dispose、failed member cleanup、task CAS 与 DAG 校验、write-scope warning、wait canceltimeout、保留 inbox 的 interrupt、普通 fork 隔离、旧 control shadowing、声明 schema 的紧凑结果渲染与 scoped registration HMR。一条 keyless 产品快照会通过 `dsh run` 加载私有 Agent Teams profile bundle,并为两个 teammate、依赖任务、peer 投递、等待、完成和汇总固定完整的面向模型工具列表、Team policy 与持久 workflow 投影。CLI e2e 会复用同一个确定性 adapter,并验证带持久 Team 与 child 日志的正常退出。
## Consequences
+3 -1
View File
@@ -8,7 +8,9 @@ import { resolveExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
const dshBinScript = fileURLToPath(new URL('../src/bin.ts', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
const fixturePlugin = pathToFileURL(fileURLToPath(new URL('./fixtures/team-llm.mjs', import.meta.url))).href
const fixturePlugin = pathToFileURL(fileURLToPath(
new URL('../../../examples/headless-agent/tests/fixtures/team-llm.mjs', import.meta.url),
)).href
function records(content: string): Record<string, unknown>[] {
return content.split('\n').filter(Boolean).map(line => JSON.parse(line) as Record<string, unknown>)
-194
View File
@@ -1,194 +0,0 @@
/** Deterministic keyless Agent Teams adapter for the real `dsh run` product test. */
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
let nextCall = 0
function calls(messages) {
return messages.flatMap(message => message.role === 'assistant'
? message.content.filter(block => block.type === 'tool-call').map(block => block.name)
: [])
}
function latestAssistantCalls(messages) {
const assistant = messages.findLast(message => message.role === 'assistant')
return assistant?.content.filter(block => block.type === 'tool-call').map(block => block.name) ?? []
}
function hasTaskAction(messages, action) {
return messages.some(message => message.role === 'assistant'
&& message.content.some((block) => {
if (block.type !== 'tool-call' || block.name !== 'team_task_update') return false
try {
return JSON.parse(block.arguments).action === action
} catch {
return false
}
}))
}
function latestToolText(messages) {
const message = messages.findLast(candidate => candidate.content.some(block => block.type === 'tool-result'))
if (message === undefined) return ''
return message.content.flatMap(block => block.type === 'tool-result'
? block.content.filter(item => item.type === 'text').map(item => item.text)
: []).join('\n')
}
function toolChunks(specs) {
const chunks = []
for (const [index, spec] of specs.entries()) {
const id = CallId(`team-fixture-${++nextCall}`)
const args = JSON.stringify(spec.args)
chunks.push(
{ type: 'block-start', index, blockType: 'tool-call' },
{ type: 'tool-call-delta', index, id, name: spec.name, argumentsDelta: args },
{ type: 'block-end', index, block: { type: 'tool-call', id, name: spec.name, arguments: args } },
)
}
chunks.push(
{ type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } },
{ type: 'finish', reason: { kind: 'tool-calls' } },
)
return chunks
}
function textChunks(text) {
return [
{ type: 'block-start', index: 0, blockType: 'text' },
{ type: 'text-delta', index: 0, text },
{ type: 'block-end', index: 0, block: { type: 'text', text } },
{ type: 'usage', usage: { inputTokens: 10, outputTokens: 3 } },
{ type: 'finish', reason: { kind: 'stop' } },
]
}
function researcher(messages) {
const names = calls(messages)
if (!names.includes('team_task_create')) {
return toolChunks([{ name: 'team_task_create', args: {
subject: 'Research', description: 'Collect the deterministic finding.', write_scopes: ['research'],
} }])
}
if (!names.includes('team_task_update')) {
return toolChunks([{ name: 'team_task_update', args: {
task_id: 'task-1', expected_revision: 1, action: 'claim',
} }])
}
if (!names.includes('send_message')) {
return toolChunks([
{ name: 'team_task_update', args: { task_id: 'task-1', expected_revision: 2, action: 'complete' } },
{ name: 'send_message', args: { target: 'implementer', message: 'Research complete: use the deterministic finding.' } },
])
}
return textChunks('Research teammate complete.')
}
function implementer(messages) {
const names = calls(messages)
const last = latestAssistantCalls(messages)
const text = latestToolText(messages)
if (!names.includes('team_task_create')) {
if (last.includes('team_task_get') && text.includes('"subject":"Research"')) {
return toolChunks([{ name: 'team_task_create', args: {
subject: 'Implementation',
description: 'Apply the deterministic finding.',
blocked_by: ['task-1'],
write_scopes: ['implementation'],
} }])
}
if (last.includes('wait_agent')) {
return toolChunks([{ name: 'team_task_get', args: { task_id: 'task-1' } }])
}
return toolChunks([{ name: 'wait_agent', args: { timeout_ms: 10000 } }])
}
if (!hasTaskAction(messages, 'claim')) {
if (last.includes('team_task_get') && text.includes('"status":"completed"')) {
return toolChunks([{ name: 'team_task_update', args: {
task_id: 'task-2', expected_revision: 1, action: 'claim',
} }])
}
if (last.includes('team_task_get')) {
return toolChunks([{ name: 'team_task_get', args: { task_id: 'task-1' } }])
}
if (last.includes('wait_agent')) {
return toolChunks([{ name: 'team_task_get', args: { task_id: 'task-1' } }])
}
return toolChunks([{ name: 'team_task_get', args: { task_id: 'task-1' } }])
}
if (!names.includes('send_message')) {
return toolChunks([
{ name: 'team_task_update', args: { task_id: 'task-2', expected_revision: 2, action: 'complete' } },
{ name: 'send_message', args: { target: 'lead', message: 'Implementation complete and verified.' } },
])
}
return textChunks('Implementation teammate complete.')
}
function lead(messages) {
const names = calls(messages)
const last = latestAssistantCalls(messages)
const spawned = names.filter(name => name === 'spawn_teammate').length
if (spawned === 0) {
return toolChunks([{
name: 'spawn_teammate',
args: {
name: 'implementer',
description: 'Own deterministic implementation.',
prompt: 'IMPLEMENTER_MARK: wait for research, complete dependent task 2, report to lead.',
context: 'fresh',
},
}])
}
if (spawned === 1) {
return toolChunks([{
name: 'spawn_teammate',
args: {
name: 'researcher',
description: 'Own deterministic research.',
prompt: 'RESEARCHER_MARK: complete research task 1, message implementer, then finish.',
context: 'fresh',
},
}])
}
const result = latestToolText(messages)
if (last.includes('team_task_list')) {
const completed = result.match(/"status":"completed"/gu)?.length ?? 0
if (completed >= 2) return toolChunks([{ name: 'list_agents', args: {} }])
return toolChunks([{ name: 'team_task_list', args: {} }])
}
if (last.includes('list_agents')) {
const inactive = result.match(/"status":"inactive"/gu)?.length ?? 0
if (inactive >= 2) return textChunks('TEAM_WORKFLOW_OK: both teammates and dependent tasks completed.')
return toolChunks([{ name: 'list_agents', args: {} }])
}
if (last.includes('wait_agent')) return toolChunks([{ name: 'team_task_list', args: {} }])
return toolChunks([{ name: 'wait_agent', args: { timeout_ms: 10000 } }])
}
class TeamFixtureAdapter extends LlmAdapter {
async * stream(options) {
const userText = options.messages.flatMap(message => message.role === 'user'
? message.content.filter(block => block.type === 'text').map(block => block.text)
: []).join('\n')
const chunks = userText.includes('RESEARCHER_MARK')
? researcher(options.messages)
: userText.includes('IMPLEMENTER_MARK')
? implementer(options.messages)
: lead(options.messages)
for (const chunk of chunks) {
options.signal?.throwIfAborted()
yield chunk
}
}
}
/** Cordis plugin name. */
export const name = 'team-fixture-llm'
/** LLM registry dependency. */
export const inject = ['llm']
/** Register the keyless adapter on the shipped default provider route. */
export function apply(ctx) {
ctx.llm.registerAdapter(['deepseek-official'], new TeamFixtureAdapter())
}
@@ -1,36 +1,6 @@
# Keyless Agent Teams composition over the real headless app and deterministic fixture adapter.
- id: base
name: '@deepseek-ai/cordis-plugin-include'
config:
path: ./cordis.yml
patches:
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- id: tool-subagent-control
name: '@deepseek-ai/dsh-tool-subagent-control'
disabled: true
- id: tool-subagent-report
name: '@deepseek-ai/dsh-tool-subagent-report'
disabled: true
- id: tool-subagent
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: spawn
toolName: subagent
backgroundMode: one-shot
maxDepth: 1
- id: tool-subagent-fork
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: fork
toolName: subagent_fork
backgroundMode: one-shot
maxDepth: 1
- insert:
- id: agent-team
name: '@deepseek-ai/dsh-experimental-agent-team'
- id: tool-agent-team
name: '@deepseek-ai/dsh-experimental-tool-agent-team'
- id: team-fixture-llm
name: './tests/fixtures/team-llm.mjs'
# Keyless overlay for the real Agent Teams profile bundle.
- id: llm-deepseek
disabled: true
- insert:
- id: team-fixture-llm
name: './tests/fixtures/team-llm.mjs'
@@ -1,4 +1,4 @@
/** Deterministic keyless Agent Teams adapter for the real headless Loader snapshot. */
/** Deterministic keyless Agent Teams adapter shared by profile snapshot and CLI e2e. */
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
@@ -46,7 +46,6 @@
},
"devDependencies": {
"@deepseek-ai/cordis-plugin-include": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"js-yaml": "^4.2.0"