refactor(agent-team): narrow projection fields and task views

This commit is contained in:
Dudu-0223 committed 2026-09-23 14:54:21 +08:00
1 parent f546068553
commit df7fa8945c
19 files changed
+49 -69

No files matched your search

+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/config-catalog.md
config-catalog.md: a396e48b0bc5017a71b4a8c6bb5d85d0d8af7644
config-catalog.zh.md: 999173a8b36357d0a5f4ee91da2582e16ea279b5
config-catalog.md: 17eff10706c7eda9fc2b81453d198b1ceb2dbec9
config-catalog.zh.md: 5bad6a73466816d33dcac6ec8ab10ffdcedb117e
+1 -1
View File
@@ -746,7 +746,7 @@ export interface Config {
}
```
Source: [`packages/experimental/agent-team/src/types.ts:155`](../packages/experimental/agent-team/src/types.ts)
Source: [`packages/experimental/agent-team/src/types.ts:152`](../packages/experimental/agent-team/src/types.ts)
<a id="deepseek-aidsh-experimental-api-speech-to-text"></a>
+1 -1
View File
@@ -748,7 +748,7 @@ export interface Config {
}
```
来源: [`packages/experimental/agent-team/src/types.ts:155`](../packages/experimental/agent-team/src/types.ts)
来源: [`packages/experimental/agent-team/src/types.ts:152`](../packages/experimental/agent-team/src/types.ts)
<a id="deepseek-aidsh-experimental-api-speech-to-text"></a>
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/persistence-catalog.md
persistence-catalog.md: 7f926ed76beb96e0d31aacf08842629d30c0817c
persistence-catalog.zh.md: ed39ff4bd76f23f56a738a71ae18a098f37f5f5e
persistence-catalog.md: 200e7156f65c4cba6fab6808a84326e85df69ddd
persistence-catalog.zh.md: fa48684d4456c6338b67d58bc051c3672d6ee2c9
+10 -10
View File
@@ -975,7 +975,7 @@ Source: [`packages/core/session/src/types.ts:330`](../packages/core/session/src/
Types: [TeamId](subsystems/agent-team.md) · [TeamMemberSnapshot](subsystems/agent-team.md)
Source: [`packages/experimental/agent-team/src/types.ts:235`](../packages/experimental/agent-team/src/types.ts)
Source: [`packages/experimental/agent-team/src/types.ts:232`](../packages/experimental/agent-team/src/types.ts)
<a id="teammessagedelivered--log-only"></a>
@@ -993,7 +993,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:235`](../packages/experi
Types: [TeamId](subsystems/agent-team.md) · [TeamMessageId](subsystems/agent-team.md)
Source: [`packages/experimental/agent-team/src/types.ts:241`](../packages/experimental/agent-team/src/types.ts)
Source: [`packages/experimental/agent-team/src/types.ts:238`](../packages/experimental/agent-team/src/types.ts)
<a id="teammessagequeued--log-only"></a>
@@ -1006,7 +1006,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:241`](../packages/experi
Types: [TeamId](subsystems/agent-team.md) · [TeamMessageSnapshot](subsystems/agent-team.md)
Source: [`packages/experimental/agent-team/src/types.ts:239`](../packages/experimental/agent-team/src/types.ts)
Source: [`packages/experimental/agent-team/src/types.ts:236`](../packages/experimental/agent-team/src/types.ts)
<a id="teamtask--log-only"></a>
@@ -1019,7 +1019,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:239`](../packages/experi
Types: [TeamId](subsystems/agent-team.md) · [TeamTaskSnapshot](subsystems/agent-team.md)
Source: [`packages/experimental/agent-team/src/types.ts:237`](../packages/experimental/agent-team/src/types.ts)
Source: [`packages/experimental/agent-team/src/types.ts:234`](../packages/experimental/agent-team/src/types.ts)
### `todo/*`
@@ -4713,7 +4713,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:47`](../packages/experi
SHA-256: `2eafefdc6f79a4922d68a787a09eaf4dd2b2369c3777fba4c05e800b5678ac8e`
Sources: [`packages/experimental/agent-team/src/types.ts:131`](../packages/experimental/agent-team/src/types.ts)
Sources: [`packages/experimental/agent-team/src/types.ts:128`](../packages/experimental/agent-team/src/types.ts)
| Property | Presence | Type |
|---|---|---|
@@ -4733,7 +4733,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:131`](../packages/exper
SHA-256: `2152caf1a71e588e4df2358a806dea24936c1c47131134f01a12939d12cdc0c0`
Sources: [`packages/experimental/agent-team/src/types.ts:140`](../packages/experimental/agent-team/src/types.ts)
Sources: [`packages/experimental/agent-team/src/types.ts:137`](../packages/experimental/agent-team/src/types.ts)
| Property | Presence | Type |
|---|---|---|
@@ -7373,7 +7373,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:61`](../packages/web
SHA-256: `b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1`
Sources: [`packages/experimental/agent-team/src/types.ts:235`](../packages/experimental/agent-team/src/types.ts)
Sources: [`packages/experimental/agent-team/src/types.ts:232`](../packages/experimental/agent-team/src/types.ts)
| Property | Presence | Type |
|---|---|---|
@@ -7401,7 +7401,7 @@ Sources: [`packages/core/session/src/types.ts:330`](../packages/core/session/src
SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403`
Sources: [`packages/experimental/agent-team/src/types.ts:239`](../packages/experimental/agent-team/src/types.ts)
Sources: [`packages/experimental/agent-team/src/types.ts:236`](../packages/experimental/agent-team/src/types.ts)
| Property | Presence | Type |
|---|---|---|
@@ -7415,7 +7415,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:239`](../packages/exper
SHA-256: `0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1`
Sources: [`packages/experimental/agent-team/src/types.ts:241`](../packages/experimental/agent-team/src/types.ts)
Sources: [`packages/experimental/agent-team/src/types.ts:238`](../packages/experimental/agent-team/src/types.ts)
| Property | Presence | Type |
|---|---|---|
@@ -7561,7 +7561,7 @@ Sources: [`packages/compaction/compaction-image-offload/src/projection.ts:25`](.
SHA-256: `a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c`
Sources: [`packages/experimental/agent-team/src/types.ts:237`](../packages/experimental/agent-team/src/types.ts)
Sources: [`packages/experimental/agent-team/src/types.ts:234`](../packages/experimental/agent-team/src/types.ts)
| Property | Presence | Type |
|---|---|---|
+10 -10
View File
@@ -977,7 +977,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMemberSnapshot](subsystems/agent-team.zh.md)
来源:[`packages/experimental/agent-team/src/types.ts:235`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:232`](../packages/experimental/agent-team/src/types.ts)
<a id="teammessagedelivered--log-only"></a>
@@ -995,7 +995,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMessageId](subsystems/agent-team.zh.md)
来源:[`packages/experimental/agent-team/src/types.ts:241`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:238`](../packages/experimental/agent-team/src/types.ts)
<a id="teammessagequeued--log-only"></a>
@@ -1008,7 +1008,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMessageSnapshot](subsystems/agent-team.zh.md)
来源:[`packages/experimental/agent-team/src/types.ts:239`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:236`](../packages/experimental/agent-team/src/types.ts)
<a id="teamtask--log-only"></a>
@@ -1021,7 +1021,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
类型:[TeamId](subsystems/agent-team.zh.md) · [TeamTaskSnapshot](subsystems/agent-team.zh.md)
来源:[`packages/experimental/agent-team/src/types.ts:237`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:234`](../packages/experimental/agent-team/src/types.ts)
### `todo/*`
@@ -4715,7 +4715,7 @@ SHA-256: `6598cd6da253948d6b207c6374a45335804c79b576866d6215649c2d1bc85e25`
SHA-256: `2eafefdc6f79a4922d68a787a09eaf4dd2b2369c3777fba4c05e800b5678ac8e`
来源:[`packages/experimental/agent-team/src/types.ts:131`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:128`](../packages/experimental/agent-team/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
@@ -4735,7 +4735,7 @@ SHA-256: `2eafefdc6f79a4922d68a787a09eaf4dd2b2369c3777fba4c05e800b5678ac8e`
SHA-256: `2152caf1a71e588e4df2358a806dea24936c1c47131134f01a12939d12cdc0c0`
来源:[`packages/experimental/agent-team/src/types.ts:140`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:137`](../packages/experimental/agent-team/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
@@ -7375,7 +7375,7 @@ SHA-256: `930a6567a10bb62ddd157bd8abdf4b182810c8c3b49d91309b3b009a5fed9731`
SHA-256: `b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1`
来源:[`packages/experimental/agent-team/src/types.ts:235`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:232`](../packages/experimental/agent-team/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
@@ -7403,7 +7403,7 @@ SHA-256: `6bc8e51124d0874bc89e1d0ceff381a9c3cd0fc240667d69f841ce4f60f65fe5`
SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403`
来源:[`packages/experimental/agent-team/src/types.ts:239`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:236`](../packages/experimental/agent-team/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
@@ -7417,7 +7417,7 @@ SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403`
SHA-256: `0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1`
来源:[`packages/experimental/agent-team/src/types.ts:241`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:238`](../packages/experimental/agent-team/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
@@ -7563,7 +7563,7 @@ SHA-256: `477614bca595a9e7adb5a1870be446b9a8684f3ba8950259d6289f2d3b74f028`
SHA-256: `a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c`
来源:[`packages/experimental/agent-team/src/types.ts:237`](../packages/experimental/agent-team/src/types.ts)
来源:[`packages/experimental/agent-team/src/types.ts:234`](../packages/experimental/agent-team/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
+6 -6
View File
@@ -22575,7 +22575,7 @@
},
"names": [],
"sources": [
"packages/experimental/agent-team/src/types.ts:241"
"packages/experimental/agent-team/src/types.ts:238"
]
},
{
@@ -31770,7 +31770,7 @@
"packages/experimental/agent-team/src/types.ts#TeamMessageSource"
],
"sources": [
"packages/experimental/agent-team/src/types.ts:140"
"packages/experimental/agent-team/src/types.ts:137"
]
},
{
@@ -33739,7 +33739,7 @@
"packages/experimental/agent-team/src/types.ts#TeamMessageSnapshot"
],
"sources": [
"packages/experimental/agent-team/src/types.ts:131"
"packages/experimental/agent-team/src/types.ts:128"
]
},
{
@@ -69181,7 +69181,7 @@
},
"names": [],
"sources": [
"packages/experimental/agent-team/src/types.ts:237"
"packages/experimental/agent-team/src/types.ts:234"
]
},
{
@@ -71163,7 +71163,7 @@
},
"names": [],
"sources": [
"packages/experimental/agent-team/src/types.ts:235"
"packages/experimental/agent-team/src/types.ts:232"
]
},
{
@@ -83593,7 +83593,7 @@
},
"names": [],
"sources": [
"packages/experimental/agent-team/src/types.ts:239"
"packages/experimental/agent-team/src/types.ts:236"
]
},
{
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/agent-team.md
agent-team.md: dc86afa2d682996eed0d11be42f91736e3675ae5
agent-team.zh.md: 1ed2164ffbb7cf35d7e8d508e7cde2ca774e7496
agent-team.md: 6cb503d130727b4c4694877f83c02f2e0983c8af
agent-team.zh.md: 083b9fafb6f45cd09ded84807da3c8602193b3d4
-3
View File
@@ -87,9 +87,6 @@ interface TeamMemberProjection {
readonly role: 'lead' | 'teammate'
/** Durable lifecycle; the Lead row is always `active`. Turn activity comes from Session status. */
readonly phase: TeamMemberPhase
readonly description?: string
readonly provider?: string
readonly context?: 'fresh' | 'fork'
readonly error?: string
}
```
-3
View File
@@ -87,9 +87,6 @@ interface TeamMemberProjection {
readonly role: 'lead' | 'teammate'
/** Durable lifecycle; the Lead row is always `active`. Turn activity comes from Session status. */
readonly phase: TeamMemberPhase
readonly description?: string
readonly provider?: string
readonly context?: 'fresh' | 'fork'
readonly error?: string
}
```
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/experimental/agent-team/README.md
README.md: 7295ff0c15ba441ab07b786de1958acc218a2759
README.zh.md: 07fe1a237a8c5dafa8e9640114d22aa76223f2e0
README.md: 6d2aa6041039022e6508541fdb738181bbb54045
README.zh.md: 9ea2977a976583aac08ca8bc53d61ff4812e1766
+1 -1
View File
@@ -174,7 +174,7 @@ Read these pages when the package-level contract is not enough. They move from t
### Browser projection
The `agentTeam` Session projection publishes the Lead Session's durable roster, non-deleted task views, and any `failure` beside the last valid state. Its `apply` replaces only the touched collection; mailbox-only changes retain the client view reference and produce no frame. The [subsystem reference](../../../docs/subsystems/agent-team.md#web-projection) defines the wire types.
The `agentTeam` Session projection publishes the Lead Session's durable roster identities and phases, member errors, non-deleted task views, and any `failure` beside the last valid state. Its `apply` replaces only the touched collection; mailbox-only changes retain the client view reference and produce no frame. The [subsystem reference](../../../docs/subsystems/agent-team.md#web-projection) defines the wire types.
The [Web UI](../client-ui-agent-team/README.md) reads the shared Session projections and overlays activity from Session status. Task creation and updates belong to Team agents through the service and model tools. The `./client` export supplies browser-safe roster, task, and projection types.
@@ -174,7 +174,7 @@ dispose 会关闭准入、中止并等待已获准的创建与 mailbox dispatch
### 浏览器投影
`agentTeam` Session 投影发布 Lead Session 的持久 roster、未删除任务视图,以及最后有效状态旁的 `failure`。其 `apply` 只替换被触及的集合;仅邮箱的变化保留客户端视图引用,不产生 frame。[子系统参考](../../../docs/subsystems/agent-team.zh.md#web-projection) 定义传输类型。
`agentTeam` Session 投影发布 Lead Session 的持久成员身份与阶段、成员错误、未删除任务视图,以及最后有效状态旁的 `failure`。其 `apply` 只替换被触及的集合;仅邮箱的变化保留客户端视图引用,不产生 frame。[子系统参考](../../../docs/subsystems/agent-team.zh.md#web-projection) 定义传输类型。
[Web UI](../client-ui-agent-team/README.zh.md) 读取共享 Session 投影,并从 Session 状态叠加活动信息。任务创建与更新由 Team agent 通过服务和模型工具完成。`./client` 导出可供浏览器使用的 roster、任务与投影类型。
@@ -314,9 +314,6 @@ const teamMemberProjectionSchema = z.object({
name: z.string(),
role: z.enum(['lead', 'teammate']),
phase: z.enum(['provisioning', 'active', 'failed']),
description: z.string().optional(),
provider: z.string().optional(),
context: z.enum(['fresh', 'fork']).optional(),
error: z.string().optional(),
}).strict() as z.ZodType<TeamMemberProjection>
@@ -351,9 +348,6 @@ function buildTeamProjection(state: TeamProjectionState): TeamProjection {
name: member.name,
role: 'teammate',
phase: member.phase,
description: member.description,
provider: member.provider,
context: member.context,
...member.error === undefined ? {} : { error: member.error },
})
}
@@ -361,7 +355,7 @@ function buildTeamProjection(state: TeamProjectionState): TeamProjection {
members,
tasks: state.tasks
.filter(task => task.status !== 'deleted')
.map(task => projectTaskView(rootId, state, task)),
.map(task => projectTaskView(state, task)),
...state.failure === undefined ? {} : { failure: state.failure },
}
}
@@ -64,7 +64,7 @@ export class TeamTaskBoard {
}
this.assertTaskGraph(state, task)
await this.journal.appendAndFlush(root, 'team/task', { version: 2, teamId: TeamId(root.id), task })
return this.taskView(root, state, task)
return projectTaskView(state, task)
})
}
@@ -79,7 +79,7 @@ export class TeamTaskBoard {
const state = this.journal.state(root)
const task = state.tasks.find(candidate => candidate.id === id)
if (task === undefined) throw new TeamError(`team task "${id}" not found`, 'TEAM_TASK_NOT_FOUND')
return this.taskView(root, state, task)
return projectTaskView(state, task)
}
/**
@@ -92,7 +92,7 @@ export class TeamTaskBoard {
const state = this.journal.state(root)
return state.tasks
.filter(task => task.status !== 'deleted')
.map(task => this.taskView(root, state, task))
.map(task => projectTaskView(state, task))
}
/**
@@ -206,7 +206,7 @@ export class TeamTaskBoard {
}
this.assertTaskGraph(state, task)
await this.journal.appendAndFlush(root, 'team/task', { version: 2, teamId: TeamId(root.id), task })
return this.taskView(root, state, task)
return projectTaskView(state, task)
})
}
@@ -252,9 +252,4 @@ export class TeamTaskBoard {
const { ownerId: _ownerId, ...without } = task
return without
}
/** View one task against the supplied state through the shared derivation. */
private taskView(root: Agent, state: TeamState, task: TeamTaskSnapshot): TeamTaskView {
return projectTaskView(root.id, state, task)
}
}
@@ -1,5 +1,6 @@
/** Pure task-view derivation shared by the task board and the client projection. */
import { brandString } from '@deepseek-ai/dsh-brand'
import type { SessionId } from '@deepseek-ai/dsh-session'
import type { TeamState } from './projection.ts'
import type { TeamTaskSnapshot, TeamTaskView } from './types.ts'
@@ -29,15 +30,14 @@ export function taskReady(state: TeamState, task: TeamTaskSnapshot): boolean {
* A committing caller may pass its pre-append state because `task` supplies the
* new value explicitly; owner names, blocker readiness, and other task scopes
* do not change when that snapshot is appended.
* @param rootId - Team Lead Session identity named `lead` in owner names.
* @param state - Team state supplying members and sibling tasks.
* @param task - durable task snapshot to view.
* @returns a detached task view.
*/
export function projectTaskView(rootId: SessionId, state: TeamState, task: TeamTaskSnapshot): TeamTaskView {
export function projectTaskView(state: TeamState, task: TeamTaskSnapshot): TeamTaskView {
const ownerName = task.ownerId === undefined
? undefined
: task.ownerId === rootId
: task.ownerId === brandString<SessionId>(state.id)
? 'lead'
: state.members.find(member => member.id === task.ownerId)?.name
const warnings = new Set<string>()
@@ -103,9 +103,6 @@ export interface TeamMemberProjection {
readonly role: 'lead' | 'teammate'
/** Durable lifecycle; the Lead row is always `active`. Turn activity comes from Session status. */
readonly phase: TeamMemberPhase
readonly description?: string
readonly provider?: string
readonly context?: 'fresh' | 'fork'
readonly error?: string
}
@@ -496,7 +496,7 @@ describe('Agent Teams projection events', () => {
expect(view).toEqual({
members: [
{ id: ROOT, name: 'lead', role: 'lead', phase: 'active' },
{ id: CHILD, name: 'worker-a', role: 'teammate', phase: 'active', description: 'worker', provider: 'spawn', context: 'fresh' },
{ id: CHILD, name: 'worker-a', role: 'teammate', phase: 'active' },
],
tasks: [
{
@@ -520,7 +520,7 @@ describe('Agent Teams projection events', () => {
const memberView = teamProjectionView(withMember)
expect(memberView).not.toBe(view)
expect(memberView.members.at(-1)).toEqual({
id: 'child-b', name: 'worker-b', role: 'teammate', phase: 'provisioning', description: 'worker', provider: 'spawn', context: 'fresh',
id: 'child-b', name: 'worker-b', role: 'teammate', phase: 'provisioning',
})
const failedMember = event('team/member', {
@@ -35,7 +35,7 @@ const task: TeamTask = {
}
const lead: TeamMemberProjection = { id: SESSION, name: 'lead', role: 'lead', phase: 'active' }
const worker: TeamMemberProjection = {
id: WORKER, name: 'worker', role: 'teammate', phase: 'active', description: 'worker', provider: 'spawn', context: 'fresh',
id: WORKER, name: 'worker', role: 'teammate', phase: 'active',
}
const team: TeamProjection = { members: [lead, worker], tasks: [task] }