Drei vom Architekten abgenommene Tasks, gebündelt als Checkpoint: - TSK-0242: Agent-Alias-Mapping (kimi-ah → kimi kanonisiert, Rollen → preferredAgent), reopenTask räumt claimedBy ab, fsWatch reindiziert direkte Datei-Edits, work-Default 300s → 50s, task_list mit Limit. - TSK-0245: zwei Agent-Klassen (dispatch loop|architect). Watchdog mahnt architekt-getriebene Agenten nur noch EINMAL statt im Minutentakt; `task dispatch` startet sie explizit, `task record` trägt extern erledigte Arbeit mit origin=external nach. - TSK-0249: Lifecycle wird serverseitig erzwungen (open→review scheitert mit klarer Meldung), claimedBy/doneBy überleben bis done, Presence pro Agent, Review-Watchdog, GET /architect/pulse (1.4 kB statt 34 kB, since-Cursor, omitted statt stillem Abschneiden), unbekannter Agent → 400 statt 500. Alle Punkte live am laufenden Hub nachgemessen, nicht aus Agenten-Logs übernommen. Tests: 242 → 279 grün, tsc sauber. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
import { loadConfig } from '../config.js';
|
|
|
|
export interface AgentIdentity {
|
|
canonical: string;
|
|
names: Set<string>;
|
|
}
|
|
|
|
/**
|
|
* Resolve an agent, configured alias, or role name to the canonical roster
|
|
* agent. Projects without an explicit roster keep accepting free-form agent
|
|
* names for backward compatibility.
|
|
*/
|
|
export function resolveAgentName(cwd: string, recipient: string): string {
|
|
const raw = recipient?.trim();
|
|
if (!raw) throw new Error('Agent recipient is required');
|
|
const key = raw.toLowerCase();
|
|
let config;
|
|
try {
|
|
config = loadConfig(cwd);
|
|
} catch {
|
|
return raw;
|
|
}
|
|
|
|
const role = Object.entries(config.roles).find(([name]) => name.toLowerCase() === key);
|
|
if (role) return role[1].preferredAgent;
|
|
|
|
const agents = config.agents;
|
|
if (!agents || Object.keys(agents).length === 0) return raw;
|
|
|
|
for (const [canonical, entry] of Object.entries(agents)) {
|
|
if (canonical.toLowerCase() === key || entry.aliases.some((alias) => alias.toLowerCase() === key)) {
|
|
return canonical;
|
|
}
|
|
}
|
|
throw new Error(`Unknown agent, alias, or role: "${raw}"`);
|
|
}
|
|
|
|
/** All names that should compare equal for task/handoff/inbox matching. */
|
|
export function agentIdentity(cwd: string, recipient: string): AgentIdentity {
|
|
const canonical = resolveAgentName(cwd, recipient);
|
|
const config = loadConfig(cwd);
|
|
const names = new Set<string>([canonical.toLowerCase()]);
|
|
const entry = config.agents?.[canonical];
|
|
for (const alias of entry?.aliases ?? []) names.add(alias.toLowerCase());
|
|
for (const [role, roleConfig] of Object.entries(config.roles)) {
|
|
if (roleConfig.preferredAgent.toLowerCase() === canonical.toLowerCase()) names.add(role.toLowerCase());
|
|
}
|
|
return { canonical, names };
|
|
}
|