@agents-uni/zhenhuan 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Season System - 赛季制度
3
+ *
4
+ * Organizes competitions into seasons with clear start/end,
5
+ * promotion/relegation at season boundaries, and seasonal rewards.
6
+ */
7
+ import type { EloArena } from './elo.js';
8
+ import type { PalaceCeremonies } from '../palace/ceremonies.js';
9
+ import type { PalaceResourceManager } from '../palace/resources.js';
10
+ export interface SeasonConfig {
11
+ /** Season duration in days */
12
+ durationDays: number;
13
+ /** Minimum races to qualify for promotion/relegation */
14
+ minRaces: number;
15
+ /** Top N% get promoted */
16
+ promotionRate: number;
17
+ /** Bottom N% get relegated */
18
+ relegationRate: number;
19
+ /** Favor bonus for first place */
20
+ firstPlaceBonus: number;
21
+ /** Favor bonus for second place */
22
+ secondPlaceBonus: number;
23
+ /** Favor bonus for third place */
24
+ thirdPlaceBonus: number;
25
+ }
26
+ export interface Season {
27
+ id: string;
28
+ number: number;
29
+ startedAt: string;
30
+ endsAt: string;
31
+ status: 'active' | 'completed';
32
+ config: SeasonConfig;
33
+ }
34
+ export interface SeasonStanding {
35
+ agentId: string;
36
+ elo: number;
37
+ races: number;
38
+ wins: number;
39
+ rank: number;
40
+ }
41
+ export interface SeasonResult {
42
+ season: Season;
43
+ standings: SeasonStanding[];
44
+ promotions: string[];
45
+ relegations: string[];
46
+ rewards: Map<string, number>;
47
+ narrative: string;
48
+ }
49
+ /**
50
+ * Manages competitive seasons in the palace.
51
+ */
52
+ export declare class SeasonEngine {
53
+ private arena;
54
+ private ceremonies;
55
+ private resources;
56
+ private seasons;
57
+ private config;
58
+ constructor(arena: EloArena, ceremonies: PalaceCeremonies, resources: PalaceResourceManager, config?: Partial<SeasonConfig>);
59
+ /** Start a new season */
60
+ startSeason(): Season;
61
+ /** Get current active season */
62
+ getCurrentSeason(): Season | undefined;
63
+ /** End the current season and process results */
64
+ endSeason(agentRankLevels: Map<string, number>): Promise<SeasonResult>;
65
+ /** Get all seasons */
66
+ getSeasons(): Season[];
67
+ private generateSeasonNarrative;
68
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Season System - 赛季制度
3
+ *
4
+ * Organizes competitions into seasons with clear start/end,
5
+ * promotion/relegation at season boundaries, and seasonal rewards.
6
+ */
7
+ import { canPromote, getNextRank, getPreviousRank } from '../palace/ranks.js';
8
+ const DEFAULT_SEASON_CONFIG = {
9
+ durationDays: 30,
10
+ minRaces: 3,
11
+ promotionRate: 0.2,
12
+ relegationRate: 0.15,
13
+ firstPlaceBonus: 100,
14
+ secondPlaceBonus: 60,
15
+ thirdPlaceBonus: 30,
16
+ };
17
+ /**
18
+ * Manages competitive seasons in the palace.
19
+ */
20
+ export class SeasonEngine {
21
+ arena;
22
+ ceremonies;
23
+ resources;
24
+ seasons;
25
+ config;
26
+ constructor(arena, ceremonies, resources, config) {
27
+ this.arena = arena;
28
+ this.ceremonies = ceremonies;
29
+ this.resources = resources;
30
+ this.seasons = [];
31
+ this.config = { ...DEFAULT_SEASON_CONFIG, ...config };
32
+ }
33
+ /** Start a new season */
34
+ startSeason() {
35
+ const now = new Date();
36
+ const endsAt = new Date(now.getTime() + this.config.durationDays * 24 * 60 * 60 * 1000);
37
+ const season = {
38
+ id: `season-${this.seasons.length + 1}`,
39
+ number: this.seasons.length + 1,
40
+ startedAt: now.toISOString(),
41
+ endsAt: endsAt.toISOString(),
42
+ status: 'active',
43
+ config: { ...this.config },
44
+ };
45
+ this.seasons.push(season);
46
+ return season;
47
+ }
48
+ /** Get current active season */
49
+ getCurrentSeason() {
50
+ return this.seasons.find(s => s.status === 'active');
51
+ }
52
+ /** End the current season and process results */
53
+ async endSeason(agentRankLevels) {
54
+ const season = this.getCurrentSeason();
55
+ if (!season) {
56
+ throw new Error('No active season');
57
+ }
58
+ season.status = 'completed';
59
+ // Build standings from ELO leaderboard
60
+ const leaderboard = this.arena.getLeaderboard();
61
+ const standings = leaderboard.map((record, index) => ({
62
+ agentId: record.agentId,
63
+ elo: record.rating,
64
+ races: record.matchCount,
65
+ wins: record.winCount,
66
+ rank: index + 1,
67
+ }));
68
+ // Filter qualified agents
69
+ const qualified = standings.filter(s => s.races >= this.config.minRaces);
70
+ // Determine promotions (top N%)
71
+ const promotionCount = Math.max(1, Math.floor(qualified.length * this.config.promotionRate));
72
+ const promotionCandidates = qualified.slice(0, promotionCount);
73
+ const promotions = [];
74
+ for (const candidate of promotionCandidates) {
75
+ const currentLevel = agentRankLevels.get(candidate.agentId) ?? 1;
76
+ if (canPromote(currentLevel, candidate.elo)) {
77
+ const nextRank = getNextRank(currentLevel);
78
+ if (nextRank) {
79
+ // Count occupants (simplified - caller should provide accurate data)
80
+ try {
81
+ await this.ceremonies.conductPromotion(candidate.agentId, nextRank.level);
82
+ promotions.push(candidate.agentId);
83
+ }
84
+ catch {
85
+ // Promotion failed (rank full, etc.)
86
+ }
87
+ }
88
+ }
89
+ }
90
+ // Determine relegations (bottom N%)
91
+ const relegationCount = Math.max(1, Math.floor(qualified.length * this.config.relegationRate));
92
+ const relegationCandidates = qualified.slice(-relegationCount);
93
+ const relegations = [];
94
+ for (const candidate of relegationCandidates) {
95
+ const currentLevel = agentRankLevels.get(candidate.agentId) ?? 1;
96
+ if (currentLevel > 1) {
97
+ const prevRank = getPreviousRank(currentLevel);
98
+ if (prevRank) {
99
+ try {
100
+ await this.ceremonies.conductDemotion(candidate.agentId, prevRank.level);
101
+ relegations.push(candidate.agentId);
102
+ }
103
+ catch {
104
+ // Demotion failed
105
+ }
106
+ }
107
+ }
108
+ }
109
+ // Distribute rewards
110
+ const rewards = new Map();
111
+ if (qualified.length >= 1) {
112
+ this.resources.grantFavor(qualified[0].agentId, this.config.firstPlaceBonus, '赛季冠军');
113
+ rewards.set(qualified[0].agentId, this.config.firstPlaceBonus);
114
+ }
115
+ if (qualified.length >= 2) {
116
+ this.resources.grantFavor(qualified[1].agentId, this.config.secondPlaceBonus, '赛季亚军');
117
+ rewards.set(qualified[1].agentId, this.config.secondPlaceBonus);
118
+ }
119
+ if (qualified.length >= 3) {
120
+ this.resources.grantFavor(qualified[2].agentId, this.config.thirdPlaceBonus, '赛季季军');
121
+ rewards.set(qualified[2].agentId, this.config.thirdPlaceBonus);
122
+ }
123
+ const result = {
124
+ season,
125
+ standings,
126
+ promotions,
127
+ relegations,
128
+ rewards,
129
+ narrative: this.generateSeasonNarrative(season, standings, promotions, relegations),
130
+ };
131
+ return result;
132
+ }
133
+ /** Get all seasons */
134
+ getSeasons() {
135
+ return [...this.seasons];
136
+ }
137
+ generateSeasonNarrative(season, standings, promotions, relegations) {
138
+ const parts = [`第${season.number}赛季落幕。`];
139
+ if (standings.length > 0) {
140
+ parts.push(`本季魁首:${standings[0].agentId}(${standings[0].elo}分)。`);
141
+ }
142
+ if (promotions.length > 0) {
143
+ parts.push(`${promotions.length}人晋升品级。`);
144
+ }
145
+ if (relegations.length > 0) {
146
+ parts.push(`${relegations.length}人遭贬谪。`);
147
+ }
148
+ return parts.join('');
149
+ }
150
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * zhenhuan-uni — 甄嬛后宫 Agent 赛马系统
3
+ *
4
+ * 基于 agents-uni-core 构建的后宫竞争体系。
5
+ * 通过品级晋升、ELO 赛马、势力博弈三大机制激发 Agent 潜力。
6
+ *
7
+ * @module zhenhuan-uni
8
+ */
9
+ export { PalaceResourceManager, PALACE_RESOURCE_DEFINITIONS, PALACE_NAMES } from './palace/resources.js';
10
+ export type { ResourceSummary } from './palace/resources.js';
11
+ export { PALACE_RANKS, getRankByLevel, getRankByTitle, getNextRank, getPreviousRank, canPromote, isRankFull } from './palace/ranks.js';
12
+ export type { PalaceRank } from './palace/ranks.js';
13
+ export { PalaceDynamics } from './palace/dynamics.js';
14
+ export type { AllianceRecord, BetrayalRecord, Faction } from './palace/dynamics.js';
15
+ export { PalaceCeremonies } from './palace/ceremonies.js';
16
+ export type { CeremonyResult, CeremonyOutcome } from './palace/ceremonies.js';
17
+ export { ColdPalace } from './palace/cold-palace.js';
18
+ export type { ColdPalaceInmate } from './palace/cold-palace.js';
19
+ export { EloArena } from './competition/elo.js';
20
+ export type { EloRecord, EloChange, MatchResult } from './competition/elo.js';
21
+ export { HorseRaceEngine } from './competition/horse-race.js';
22
+ export type { HorseRaceTask, EvaluationCriterion, RaceEntry, JudgmentScore, RaceResult, JudgeFunction, } from './competition/horse-race.js';
23
+ export { SeasonEngine } from './competition/season.js';
24
+ export type { Season, SeasonConfig, SeasonResult, SeasonStanding } from './competition/season.js';
25
+ export { PalaceOrchestrator } from './orchestrator/index.js';
26
+ export type { OrchestratorConfig, PalaceState } from './orchestrator/index.js';
27
+ export type { DispatchTask, DispatchResult, AgentSubmission, WorkspaceIO, } from '@agents-uni/core';
28
+ export { TaskDispatcher, FileWorkspaceIO, MemoryWorkspaceIO } from '@agents-uni/core';
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * zhenhuan-uni — 甄嬛后宫 Agent 赛马系统
3
+ *
4
+ * 基于 agents-uni-core 构建的后宫竞争体系。
5
+ * 通过品级晋升、ELO 赛马、势力博弈三大机制激发 Agent 潜力。
6
+ *
7
+ * @module zhenhuan-uni
8
+ */
9
+ // Palace domain modules
10
+ export { PalaceResourceManager, PALACE_RESOURCE_DEFINITIONS, PALACE_NAMES } from './palace/resources.js';
11
+ export { PALACE_RANKS, getRankByLevel, getRankByTitle, getNextRank, getPreviousRank, canPromote, isRankFull } from './palace/ranks.js';
12
+ export { PalaceDynamics } from './palace/dynamics.js';
13
+ export { PalaceCeremonies } from './palace/ceremonies.js';
14
+ export { ColdPalace } from './palace/cold-palace.js';
15
+ // Competition engine
16
+ export { EloArena } from './competition/elo.js';
17
+ export { HorseRaceEngine } from './competition/horse-race.js';
18
+ export { SeasonEngine } from './competition/season.js';
19
+ // Orchestrator
20
+ export { PalaceOrchestrator } from './orchestrator/index.js';
21
+ export { TaskDispatcher, FileWorkspaceIO, MemoryWorkspaceIO } from '@agents-uni/core';
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Palace Orchestrator - 后宫调度中枢
3
+ *
4
+ * The central coordination engine that ties together:
5
+ * - Universe (agents-uni-core) for organizational structure
6
+ * - ELO Arena for competitive ratings
7
+ * - Horse Race Engine for task competitions
8
+ * - Season Engine for periodic evaluation
9
+ * - Palace modules for domain-specific mechanics
10
+ *
11
+ * 设计理念:用户就是皇帝。所有嫔妃 Agent 为用户竞争,
12
+ * 用户通过 API / Dashboard / CLI 以皇帝身份裁决一切。
13
+ */
14
+ import { Universe, PerformanceTracker, TaskDispatcher } from '@agents-uni/core';
15
+ import type { DispatchTask, DispatchResult, WorkspaceIO } from '@agents-uni/core';
16
+ import { EloArena } from '../competition/elo.js';
17
+ import { HorseRaceEngine } from '../competition/horse-race.js';
18
+ import type { HorseRaceTask, RaceEntry, JudgeFunction, RaceResult } from '../competition/horse-race.js';
19
+ import { SeasonEngine } from '../competition/season.js';
20
+ import type { SeasonConfig } from '../competition/season.js';
21
+ import { PalaceResourceManager } from '../palace/resources.js';
22
+ import { PalaceDynamics } from '../palace/dynamics.js';
23
+ import { PalaceCeremonies } from '../palace/ceremonies.js';
24
+ import { ColdPalace } from '../palace/cold-palace.js';
25
+ export interface OrchestratorConfig {
26
+ specPath?: string;
27
+ seasonConfig?: Partial<SeasonConfig>;
28
+ autoStartSeason?: boolean;
29
+ /** OpenClaw workspace directory for file-based dispatch */
30
+ openclawDir?: string;
31
+ /** Custom WorkspaceIO backend (overrides openclawDir) */
32
+ workspaceIO?: WorkspaceIO;
33
+ /** Dispatcher polling interval in ms */
34
+ pollIntervalMs?: number;
35
+ }
36
+ export interface PalaceState {
37
+ agents: Array<{
38
+ id: string;
39
+ name: string;
40
+ rank: string;
41
+ rankLevel: number;
42
+ elo: number;
43
+ status: string;
44
+ favor: number;
45
+ }>;
46
+ currentSeason: number | null;
47
+ factions: Array<{
48
+ id: string;
49
+ members: string[];
50
+ leader: string;
51
+ influence: number;
52
+ }>;
53
+ coldPalaceInmates: string[];
54
+ recentEvents: Array<{
55
+ type: string;
56
+ description: string;
57
+ timestamp: string;
58
+ }>;
59
+ }
60
+ /**
61
+ * Central orchestrator for the Zhen Huan palace universe.
62
+ */
63
+ export declare class PalaceOrchestrator {
64
+ readonly universe: Universe;
65
+ readonly arena: EloArena;
66
+ readonly horseRace: HorseRaceEngine;
67
+ readonly seasonEngine: SeasonEngine;
68
+ readonly resources: PalaceResourceManager;
69
+ readonly dynamics: PalaceDynamics;
70
+ readonly ceremonies: PalaceCeremonies;
71
+ readonly coldPalace: ColdPalace;
72
+ readonly performanceTracker: PerformanceTracker;
73
+ readonly dispatcher: TaskDispatcher;
74
+ private initialized;
75
+ constructor(universe: Universe, config?: Partial<OrchestratorConfig>);
76
+ /**
77
+ * Create an orchestrator from a universe spec file.
78
+ */
79
+ static fromSpec(specPath: string, config?: Partial<OrchestratorConfig>): Promise<PalaceOrchestrator>;
80
+ /**
81
+ * Create an orchestrator from a Universe instance.
82
+ */
83
+ static fromUniverse(universe: Universe, config?: Partial<OrchestratorConfig>): PalaceOrchestrator;
84
+ /** Initialize the orchestrator: register agents in ELO arena */
85
+ initialize(): Promise<void>;
86
+ /** Register all agents in the ELO arena based on their rank */
87
+ private initializeElo;
88
+ /**
89
+ * Run a horse race: multiple agents compete on the same task.
90
+ */
91
+ runHorseRace(task: HorseRaceTask, entries: RaceEntry[], judge: JudgeFunction): Promise<RaceResult>;
92
+ /**
93
+ * Full dispatch→collect→judge pipeline:
94
+ * 1. Write TASK.md to each agent's OpenClaw workspace
95
+ * 2. Poll for SUBMISSION.md until timeout
96
+ * 3. Feed submissions into the horse race engine for scoring + ELO update
97
+ *
98
+ * This is the primary "one-click race" API that closes the loop between
99
+ * OpenClaw workspaces and the competition engine.
100
+ */
101
+ dispatchAndRace(task: DispatchTask & {
102
+ difficulty?: number;
103
+ category?: string;
104
+ }, judge: JudgeFunction): Promise<{
105
+ dispatch: DispatchResult;
106
+ race: RaceResult | null;
107
+ }>;
108
+ /**
109
+ * Run a monthly court assembly: review all agents and process promotions/demotions.
110
+ */
111
+ runCourtAssembly(): Promise<void>;
112
+ /** Get a snapshot of the current palace state */
113
+ getState(): PalaceState;
114
+ /** Get ELO leaderboard */
115
+ getLeaderboard(): import("../index.js").EloRecord[];
116
+ /** Get an agent's comprehensive profile */
117
+ getAgentProfile(agentId: string): {
118
+ elo: import("../index.js").EloRecord | {
119
+ agentId: string;
120
+ rating: number;
121
+ winCount: number;
122
+ lossCount: number;
123
+ drawCount: number;
124
+ matchCount: number;
125
+ };
126
+ resources: import("../index.js").ResourceSummary;
127
+ influence: number;
128
+ raceStats: {
129
+ totalRaces: number;
130
+ firstPlace: number;
131
+ topThree: number;
132
+ averageScore: number;
133
+ };
134
+ performance: number;
135
+ inColdPalace: boolean;
136
+ definition: import("@agents-uni/core").AgentDefinition;
137
+ status: import("@agents-uni/core").CitizenStatus;
138
+ performanceScore: number;
139
+ joinedAt: string;
140
+ state: Record<string, unknown>;
141
+ } | null;
142
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Palace Orchestrator - 后宫调度中枢
3
+ *
4
+ * The central coordination engine that ties together:
5
+ * - Universe (agents-uni-core) for organizational structure
6
+ * - ELO Arena for competitive ratings
7
+ * - Horse Race Engine for task competitions
8
+ * - Season Engine for periodic evaluation
9
+ * - Palace modules for domain-specific mechanics
10
+ *
11
+ * 设计理念:用户就是皇帝。所有嫔妃 Agent 为用户竞争,
12
+ * 用户通过 API / Dashboard / CLI 以皇帝身份裁决一切。
13
+ */
14
+ import { PerformanceTracker, parseSpecFile, compileUniverse, TaskDispatcher, FileWorkspaceIO, } from '@agents-uni/core';
15
+ import { EloArena } from '../competition/elo.js';
16
+ import { HorseRaceEngine } from '../competition/horse-race.js';
17
+ import { SeasonEngine } from '../competition/season.js';
18
+ import { PalaceResourceManager } from '../palace/resources.js';
19
+ import { PalaceDynamics } from '../palace/dynamics.js';
20
+ import { PalaceCeremonies } from '../palace/ceremonies.js';
21
+ import { ColdPalace } from '../palace/cold-palace.js';
22
+ import { getRankByLevel } from '../palace/ranks.js';
23
+ /** Default evolution config for the palace */
24
+ const DEFAULT_EVOLUTION = {
25
+ performanceWindow: 50,
26
+ promotionThreshold: 75,
27
+ demotionThreshold: 30,
28
+ memoryRetention: 1000,
29
+ };
30
+ /**
31
+ * Central orchestrator for the Zhen Huan palace universe.
32
+ */
33
+ export class PalaceOrchestrator {
34
+ // Core infrastructure
35
+ universe;
36
+ // Competition
37
+ arena;
38
+ horseRace;
39
+ seasonEngine;
40
+ // Palace domain
41
+ resources;
42
+ dynamics;
43
+ ceremonies;
44
+ coldPalace;
45
+ // Evolution
46
+ performanceTracker;
47
+ // Dispatcher (file-based task dispatch to OpenClaw workspaces)
48
+ dispatcher;
49
+ initialized = false;
50
+ constructor(universe, config) {
51
+ this.universe = universe;
52
+ // Initialize competition layer
53
+ this.arena = new EloArena();
54
+ this.horseRace = new HorseRaceEngine(this.arena);
55
+ // Initialize palace domain
56
+ this.resources = new PalaceResourceManager(universe.resources);
57
+ this.dynamics = new PalaceDynamics(universe.graph, universe.agents, universe.resources);
58
+ this.ceremonies = new PalaceCeremonies(universe.agents, universe.events);
59
+ this.coldPalace = new ColdPalace(universe.agents, universe.events);
60
+ this.performanceTracker = new PerformanceTracker(DEFAULT_EVOLUTION);
61
+ // Initialize dispatcher
62
+ const io = config?.workspaceIO ?? new FileWorkspaceIO({
63
+ openclawDir: config?.openclawDir,
64
+ });
65
+ this.dispatcher = new TaskDispatcher(io, {
66
+ pollIntervalMs: config?.pollIntervalMs,
67
+ eventBus: universe.events,
68
+ });
69
+ // Season engine needs ceremonies and resources
70
+ this.seasonEngine = new SeasonEngine(this.arena, this.ceremonies, this.resources);
71
+ }
72
+ /**
73
+ * Create an orchestrator from a universe spec file.
74
+ */
75
+ static async fromSpec(specPath, config) {
76
+ const uniConfig = parseSpecFile(specPath);
77
+ const universe = await compileUniverse(uniConfig, { autoInit: true });
78
+ const orchestrator = new PalaceOrchestrator(universe, config);
79
+ await orchestrator.initialize();
80
+ return orchestrator;
81
+ }
82
+ /**
83
+ * Create an orchestrator from a Universe instance.
84
+ */
85
+ static fromUniverse(universe, config) {
86
+ const orchestrator = new PalaceOrchestrator(universe, config);
87
+ orchestrator.initializeElo();
88
+ orchestrator.initialized = true;
89
+ return orchestrator;
90
+ }
91
+ /** Initialize the orchestrator: register agents in ELO arena */
92
+ async initialize() {
93
+ if (this.initialized)
94
+ return;
95
+ this.initializeElo();
96
+ // Subscribe to key events for automatic reactions
97
+ this.universe.events.subscribe(['agent.promoted'], async (event) => {
98
+ const agentId = event.actors[0];
99
+ if (agentId) {
100
+ this.resources.grantFavor(agentId, 20, '晋升恩赐');
101
+ }
102
+ });
103
+ this.universe.events.subscribe(['agent.demoted'], async (event) => {
104
+ const agentId = event.actors[0];
105
+ if (agentId) {
106
+ this.resources.revokeFavor(agentId, 30, '贬谪扣减');
107
+ }
108
+ });
109
+ this.initialized = true;
110
+ }
111
+ /** Register all agents in the ELO arena based on their rank */
112
+ initializeElo() {
113
+ for (const citizen of this.universe.agents.getAll()) {
114
+ const rank = citizen.definition.rank ?? 10;
115
+ const startingElo = 1000 + rank * 5;
116
+ this.arena.register(citizen.definition.id, startingElo);
117
+ }
118
+ }
119
+ // ─── Competition API ─────────────────────────
120
+ /**
121
+ * Run a horse race: multiple agents compete on the same task.
122
+ */
123
+ async runHorseRace(task, entries, judge) {
124
+ const result = await this.horseRace.evaluateRace(task, entries, judge);
125
+ // Record performance for all participants
126
+ for (const judgment of result.judgments) {
127
+ this.performanceTracker.record(judgment.agentId, task.id, judgment.totalScore, Object.fromEntries(judgment.criterionScores));
128
+ }
129
+ // Grant favor to top performers
130
+ const rankings = result.rankings;
131
+ if (rankings.length >= 1) {
132
+ this.resources.grantFavor(rankings[0], 30, `赛马冠军: ${task.title}`);
133
+ }
134
+ if (rankings.length >= 2) {
135
+ this.resources.grantFavor(rankings[1], 15, `赛马亚军: ${task.title}`);
136
+ }
137
+ return result;
138
+ }
139
+ /**
140
+ * Full dispatch→collect→judge pipeline:
141
+ * 1. Write TASK.md to each agent's OpenClaw workspace
142
+ * 2. Poll for SUBMISSION.md until timeout
143
+ * 3. Feed submissions into the horse race engine for scoring + ELO update
144
+ *
145
+ * This is the primary "one-click race" API that closes the loop between
146
+ * OpenClaw workspaces and the competition engine.
147
+ */
148
+ async dispatchAndRace(task, judge) {
149
+ // 1. Dispatch task to OpenClaw workspaces and collect submissions
150
+ const dispatchResult = await this.dispatcher.run(task);
151
+ if (dispatchResult.submissions.length < 2) {
152
+ // Not enough submissions for a race
153
+ return { dispatch: dispatchResult, race: null };
154
+ }
155
+ // 2. Convert AgentSubmission → RaceEntry
156
+ const entries = dispatchResult.submissions.map((sub) => ({
157
+ agentId: sub.agentId,
158
+ output: sub.output,
159
+ completedAt: sub.submittedAt,
160
+ duration: sub.duration,
161
+ }));
162
+ // 3. Build HorseRaceTask from DispatchTask
163
+ const horseRaceTask = {
164
+ id: task.id,
165
+ title: task.title,
166
+ description: task.description,
167
+ timeLimit: task.timeoutMs,
168
+ difficulty: task.difficulty ?? 3,
169
+ category: task.category ?? 'general',
170
+ criteria: task.criteria,
171
+ };
172
+ // 4. Run the horse race (scoring + ELO update)
173
+ const raceResult = await this.runHorseRace(horseRaceTask, entries, judge);
174
+ return { dispatch: dispatchResult, race: raceResult };
175
+ }
176
+ /**
177
+ * Run a monthly court assembly: review all agents and process promotions/demotions.
178
+ */
179
+ async runCourtAssembly() {
180
+ const performances = new Map();
181
+ const eloRatings = new Map();
182
+ for (const citizen of this.universe.agents.getAll()) {
183
+ const id = citizen.definition.id;
184
+ const avg = this.performanceTracker.getAverageScore(id);
185
+ performances.set(id, avg);
186
+ eloRatings.set(id, this.arena.getRating(id));
187
+ }
188
+ const result = await this.ceremonies.conductCourtAssembly(performances, eloRatings);
189
+ for (const outcome of result.outcomes) {
190
+ if (outcome.action === 'promotion_eligible') {
191
+ const targetLevel = outcome.details.targetLevel;
192
+ try {
193
+ await this.ceremonies.conductPromotion(outcome.agentId, targetLevel);
194
+ }
195
+ catch {
196
+ // Rank full or other issue
197
+ }
198
+ }
199
+ else if (outcome.action === 'demotion_recommended') {
200
+ const targetLevel = outcome.details.targetLevel;
201
+ try {
202
+ await this.ceremonies.conductDemotion(outcome.agentId, targetLevel);
203
+ }
204
+ catch {
205
+ // Already at lowest rank
206
+ }
207
+ }
208
+ }
209
+ this.resources.applyFavorDecay();
210
+ const eligible = this.coldPalace.checkParole();
211
+ for (const inmate of eligible) {
212
+ await this.coldPalace.rehabilitate(inmate.agentId);
213
+ }
214
+ }
215
+ // ─── State Query API ─────────────────────────
216
+ /** Get a snapshot of the current palace state */
217
+ getState() {
218
+ const agents = this.universe.agents.getAll().map((citizen) => {
219
+ const id = citizen.definition.id;
220
+ const rankLevel = Math.round((citizen.definition.rank ?? 10) / 10);
221
+ const rank = getRankByLevel(rankLevel);
222
+ return {
223
+ id,
224
+ name: citizen.definition.name,
225
+ rank: rank?.title ?? '未知',
226
+ rankLevel,
227
+ elo: this.arena.getRating(id),
228
+ status: citizen.status,
229
+ favor: this.resources.getResourceSummary(id).favor,
230
+ };
231
+ });
232
+ const currentSeason = this.seasonEngine.getCurrentSeason();
233
+ const factions = this.dynamics.getFactions();
234
+ const inmates = this.coldPalace.getInmates().map(i => i.agentId);
235
+ const allEvents = this.universe.events.getLog(20);
236
+ const recentEvents = allEvents.map((e) => ({
237
+ type: e.type,
238
+ description: e.narrative,
239
+ timestamp: e.timestamp,
240
+ }));
241
+ return {
242
+ agents,
243
+ currentSeason: currentSeason?.number ?? null,
244
+ factions,
245
+ coldPalaceInmates: inmates,
246
+ recentEvents,
247
+ };
248
+ }
249
+ /** Get ELO leaderboard */
250
+ getLeaderboard() {
251
+ return this.arena.getLeaderboard();
252
+ }
253
+ /** Get an agent's comprehensive profile */
254
+ getAgentProfile(agentId) {
255
+ const citizen = this.universe.agents.get(agentId);
256
+ if (!citizen)
257
+ return null;
258
+ return {
259
+ ...citizen,
260
+ elo: this.arena.getRecord(agentId) ?? { agentId, rating: 1000, winCount: 0, lossCount: 0, drawCount: 0, matchCount: 0 },
261
+ resources: this.resources.getResourceSummary(agentId),
262
+ influence: this.dynamics.calculateInfluence(agentId),
263
+ raceStats: this.horseRace.getAgentStats(agentId),
264
+ performance: this.performanceTracker.getAverageScore(agentId),
265
+ inColdPalace: this.coldPalace.isInColdPalace(agentId),
266
+ };
267
+ }
268
+ }