返回源码地图

packages/experimental/agent-team/src/task-graph.ts

main snapshot · da00f7f5358f · 正文引用章节 13;完整原文可核对,不声称全文件人工逐行审计

完整原文供逐行核对;页面收录不代表每行都经过人工语义审核。MIT 许可见 许可证。

1/** Complete dependency validation for current Team task snapshots. */
2
3import type { TeamTaskId, TeamTaskSnapshot } from './types.ts'
4
5/** Task dependency relation rejected by the shared graph validator. */
6export type TeamTaskGraphViolation = 'missing' | 'duplicate' | 'cycle'
7
8/** Package-private task dependency failure retained for command error mapping. */
9export class TeamTaskGraphError extends Error {
10 /**
11 * @param message - concrete invalid dependency relation.
12 * @param violation - stable relation category used by Team commands.
13 */
14 constructor(message: string, readonly violation: TeamTaskGraphViolation) {
15 super(message)
16 this.name = 'TeamTaskGraphError'
17 }
18}
19
20/**
21 * Validate the complete active task graph after replacing one candidate snapshot.
22 * @param current - current task snapshots before the candidate event.
23 * @param candidate - new or next-revision task snapshot.
24 * @throws {TeamTaskGraphError} when an active dependency is missing, duplicated, self-referential, or cyclic.
25 */
26export function assertTaskGraphCandidate(
27 current: readonly TeamTaskSnapshot[],
28 candidate: TeamTaskSnapshot,
29): void {
30 const tasks = new Map(current.map(task => [task.id, task]))
31 tasks.set(candidate.id, candidate)
32
33 for (const task of tasks.values()) {
34 if (task.status === 'deleted') continue
35 const seen = new Set<TeamTaskId>()
36 for (const blockerId of task.blockedBy) {
37 if (blockerId === task.id) {
38 throw new TeamTaskGraphError(`team task "${task.id}" cannot block itself`, 'cycle')
39 }
40 if (seen.has(blockerId)) {
41 throw new TeamTaskGraphError(`team task "${task.id}" repeats blocker "${blockerId}"`, 'duplicate')
42 }
43 const blocker = tasks.get(blockerId)
44 if (blocker === undefined || blocker.status === 'deleted') {
45 throw new TeamTaskGraphError(
46 `blocker task "${blockerId}" for "${task.id}" is missing or deleted`,
47 'missing',
48 )
49 }
50 seen.add(blockerId)
51 }
52 }
53
54 const visiting = new Set<TeamTaskId>()
55 const visited = new Set<TeamTaskId>()
56 const visit = (id: TeamTaskId): void => {
57 if (visiting.has(id)) {
58 throw new TeamTaskGraphError(`task dependency cycle includes "${id}"`, 'cycle')
59 }
60 if (visited.has(id)) return
61 const task = tasks.get(id)
62 if (task === undefined || task.status === 'deleted') return
63 visiting.add(id)
64 for (const blockerId of task.blockedBy) visit(blockerId)
65 visiting.delete(id)
66 visited.add(id)
67 }
68 for (const task of tasks.values()) visit(task.id)
69}