118 lines
3.1 KiB
TypeScript
118 lines
3.1 KiB
TypeScript
import {
|
|
AGENT_MODEL_MAX_LENGTH,
|
|
AGENT_STATUS_MAX_SUBAGENTS,
|
|
AGENT_STATUS_TOOL_INPUT_MAX_LENGTH,
|
|
AGENT_TYPE_MAX_LENGTH,
|
|
type AgentSubagentSnapshot
|
|
} from './agent-status-types'
|
|
import { normalizeOptionalField } from './agent-status-field-normalization'
|
|
|
|
const CODEX_SUBAGENT_ID_MAX_LENGTH = 64
|
|
|
|
export type CodexSubagentRoster = Map<string, TrackedCodexSubagent>
|
|
|
|
type TrackedCodexSubagent = {
|
|
agentType?: string
|
|
description?: string
|
|
model?: string
|
|
state: 'working' | 'waiting'
|
|
startedAt: number
|
|
}
|
|
|
|
export function upsertCodexSubagent(
|
|
roster: CodexSubagentRoster,
|
|
id: string,
|
|
fields: {
|
|
agentType?: string
|
|
description?: string
|
|
model?: string
|
|
state: 'working' | 'waiting'
|
|
},
|
|
now: number
|
|
): void {
|
|
const normalizedId = id.trim()
|
|
if (normalizedId.length === 0 || normalizedId.length > CODEX_SUBAGENT_ID_MAX_LENGTH) {
|
|
return
|
|
}
|
|
const agentType = normalizeOptionalField(fields.agentType, AGENT_TYPE_MAX_LENGTH)
|
|
const description = normalizeOptionalField(fields.description, AGENT_STATUS_TOOL_INPUT_MAX_LENGTH)
|
|
const model = normalizeOptionalField(fields.model, AGENT_MODEL_MAX_LENGTH)
|
|
const existing = roster.get(normalizedId)
|
|
if (existing) {
|
|
existing.agentType = agentType ?? existing.agentType
|
|
existing.description = description ?? existing.description
|
|
existing.model = model ?? existing.model
|
|
existing.state = fields.state
|
|
return
|
|
}
|
|
if (roster.size >= AGENT_STATUS_MAX_SUBAGENTS) {
|
|
return
|
|
}
|
|
roster.set(normalizedId, {
|
|
agentType,
|
|
description,
|
|
model,
|
|
state: fields.state,
|
|
startedAt: now
|
|
})
|
|
}
|
|
|
|
export function finishCodexSubagent(roster: CodexSubagentRoster, id: string): void {
|
|
roster.delete(id.trim())
|
|
}
|
|
|
|
export function seedCodexSubagentRoster(
|
|
roster: CodexSubagentRoster,
|
|
snapshots: readonly AgentSubagentSnapshot[]
|
|
): void {
|
|
for (const snapshot of snapshots) {
|
|
if (snapshot.state !== 'working' && snapshot.state !== 'waiting') {
|
|
continue
|
|
}
|
|
upsertCodexSubagent(
|
|
roster,
|
|
snapshot.id,
|
|
{
|
|
agentType: snapshot.agentType,
|
|
description: snapshot.description,
|
|
model: snapshot.model,
|
|
state: snapshot.state
|
|
},
|
|
snapshot.startedAt
|
|
)
|
|
}
|
|
}
|
|
|
|
export function codexRosterToSnapshots(
|
|
roster: CodexSubagentRoster | undefined
|
|
): AgentSubagentSnapshot[] | undefined {
|
|
if (!roster || roster.size === 0) {
|
|
return undefined
|
|
}
|
|
const snapshots = Array.from(roster, ([id, tracked]) => ({
|
|
id,
|
|
agentType: tracked.agentType,
|
|
description: tracked.description,
|
|
model: tracked.model,
|
|
state: tracked.state,
|
|
startedAt: tracked.startedAt
|
|
}))
|
|
snapshots.sort((a, b) => a.startedAt - b.startedAt || a.id.localeCompare(b.id))
|
|
return snapshots
|
|
}
|
|
|
|
export function codexRosterEffectiveState(
|
|
roster: CodexSubagentRoster | undefined,
|
|
leadState: 'working' | 'waiting' | 'done'
|
|
): 'working' | 'waiting' | 'done' {
|
|
if (!roster || roster.size === 0) {
|
|
return leadState
|
|
}
|
|
for (const tracked of roster.values()) {
|
|
if (tracked.state === 'waiting') {
|
|
return 'waiting'
|
|
}
|
|
}
|
|
return leadState === 'done' ? 'working' : leadState
|
|
}
|