@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,192 @@
1
+ /**
2
+ * Palace Dynamics - 后宫势力与关系
3
+ *
4
+ * Manages alliances, betrayals, factions, and influence calculations
5
+ * within the palace hierarchy.
6
+ */
7
+ /**
8
+ * Manages the complex political dynamics of the palace.
9
+ */
10
+ export class PalaceDynamics {
11
+ graph;
12
+ registry;
13
+ resources;
14
+ allianceHistory;
15
+ betrayalHistory;
16
+ constructor(graph, registry, resources) {
17
+ this.graph = graph;
18
+ this.registry = registry;
19
+ this.resources = resources;
20
+ this.allianceHistory = [];
21
+ this.betrayalHistory = [];
22
+ }
23
+ /** Form an alliance between two agents */
24
+ formAlliance(agent1, agent2, reason) {
25
+ // Verify both agents exist and are active
26
+ const citizen1 = this.registry.get(agent1);
27
+ const citizen2 = this.registry.get(agent2);
28
+ if (!citizen1 || !citizen2)
29
+ return false;
30
+ if (citizen1.status !== 'active' && citizen1.status !== 'idle')
31
+ return false;
32
+ if (citizen2.status !== 'active' && citizen2.status !== 'idle')
33
+ return false;
34
+ // Check if they are already allies
35
+ const allies = this.graph.getAllies(agent1);
36
+ if (allies.includes(agent2))
37
+ return false;
38
+ // Add mutual ally relationships
39
+ this.graph.addRelationship({
40
+ from: agent1,
41
+ to: agent2,
42
+ type: 'ally',
43
+ weight: 0.7,
44
+ mutable: true,
45
+ });
46
+ this.graph.addRelationship({
47
+ from: agent2,
48
+ to: agent1,
49
+ type: 'ally',
50
+ weight: 0.7,
51
+ mutable: true,
52
+ });
53
+ this.allianceHistory.push({
54
+ agent1,
55
+ agent2,
56
+ reason,
57
+ formedAt: new Date().toISOString(),
58
+ });
59
+ return true;
60
+ }
61
+ /** Betray an alliance, creating a rivalry */
62
+ betrayAlliance(betrayer, betrayed, reason) {
63
+ // Verify they were allies
64
+ const allies = this.graph.getAllies(betrayer);
65
+ if (!allies.includes(betrayed))
66
+ return false;
67
+ // Remove ally relationships by setting weight to 0
68
+ this.graph.updateWeight(betrayer, betrayed, 'ally', 0, `背叛: ${reason}`);
69
+ this.graph.updateWeight(betrayed, betrayer, 'ally', 0, `被背叛: ${reason}`);
70
+ // Create rivalry
71
+ this.graph.addRelationship({
72
+ from: betrayer,
73
+ to: betrayed,
74
+ type: 'rival',
75
+ weight: 0.9,
76
+ mutable: true,
77
+ });
78
+ this.graph.addRelationship({
79
+ from: betrayed,
80
+ to: betrayer,
81
+ type: 'rival',
82
+ weight: 0.9,
83
+ mutable: true,
84
+ });
85
+ this.betrayalHistory.push({
86
+ betrayer,
87
+ betrayed,
88
+ reason,
89
+ occurredAt: new Date().toISOString(),
90
+ });
91
+ return true;
92
+ }
93
+ /** Calculate an agent's influence based on rank, allies, and favor */
94
+ calculateInfluence(agentId) {
95
+ const citizen = this.registry.get(agentId);
96
+ if (!citizen)
97
+ return 0;
98
+ // Base influence from rank (0-100)
99
+ const rankInfluence = (citizen.definition.rank ?? 0) * 1;
100
+ // Influence from allies (each ally adds proportional to their rank)
101
+ const allies = this.graph.getAllies(agentId);
102
+ let allyInfluence = 0;
103
+ for (const allyId of allies) {
104
+ const ally = this.registry.get(allyId);
105
+ if (ally) {
106
+ allyInfluence += (ally.definition.rank ?? 0) * 0.3;
107
+ }
108
+ }
109
+ // Influence from favor (圣宠)
110
+ const favor = this.resources.getBalance(agentId, '圣宠');
111
+ const favorInfluence = Math.min(favor * 0.1, 50);
112
+ // Penalty from rivals
113
+ const rivals = this.graph.getRivals(agentId);
114
+ const rivalPenalty = rivals.length * 5;
115
+ return Math.max(0, rankInfluence + allyInfluence + favorInfluence - rivalPenalty);
116
+ }
117
+ /** Get all factions (groups of allied agents) */
118
+ getFactions() {
119
+ const allAgents = this.registry.getAllIds();
120
+ const visited = new Set();
121
+ const factions = [];
122
+ let factionCounter = 0;
123
+ for (const agentId of allAgents) {
124
+ if (visited.has(agentId))
125
+ continue;
126
+ const citizen = this.registry.get(agentId);
127
+ if (!citizen || citizen.status === 'eliminated' || citizen.status === 'suspended') {
128
+ continue;
129
+ }
130
+ // BFS to find all connected allies
131
+ const faction = [];
132
+ const queue = [agentId];
133
+ while (queue.length > 0) {
134
+ const current = queue.shift();
135
+ if (visited.has(current))
136
+ continue;
137
+ visited.add(current);
138
+ faction.push(current);
139
+ const allies = this.graph.getAllies(current);
140
+ for (const ally of allies) {
141
+ if (!visited.has(ally)) {
142
+ const allyCitizen = this.registry.get(ally);
143
+ if (allyCitizen && allyCitizen.status !== 'eliminated' && allyCitizen.status !== 'suspended') {
144
+ queue.push(ally);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ if (faction.length > 1) {
150
+ // Find the leader (highest rank in the faction)
151
+ const leader = faction.reduce((best, id) => {
152
+ const bestCitizen = this.registry.get(best);
153
+ const currentCitizen = this.registry.get(id);
154
+ const bestRank = bestCitizen?.definition.rank ?? 0;
155
+ const currentRank = currentCitizen?.definition.rank ?? 0;
156
+ return currentRank > bestRank ? id : best;
157
+ });
158
+ const influence = faction.reduce((sum, id) => sum + this.calculateInfluence(id), 0);
159
+ factions.push({
160
+ id: `faction-${++factionCounter}`,
161
+ members: faction,
162
+ leader,
163
+ influence,
164
+ });
165
+ }
166
+ }
167
+ return factions.sort((a, b) => b.influence - a.influence);
168
+ }
169
+ /** Check if a challenger can challenge a target (adjacent ranks only) */
170
+ canChallenge(challengerId, targetId) {
171
+ const challenger = this.registry.get(challengerId);
172
+ const target = this.registry.get(targetId);
173
+ if (!challenger || !target)
174
+ return false;
175
+ if (challenger.status !== 'active' && challenger.status !== 'idle')
176
+ return false;
177
+ if (target.status !== 'active' && target.status !== 'idle')
178
+ return false;
179
+ const challengerRank = challenger.definition.rank ?? 0;
180
+ const targetRank = target.definition.rank ?? 0;
181
+ // Can only challenge adjacent ranks (within 1 level)
182
+ return Math.abs(challengerRank - targetRank) <= 1 && challengerRank !== targetRank;
183
+ }
184
+ /** Get alliance history */
185
+ getAllianceHistory() {
186
+ return [...this.allianceHistory];
187
+ }
188
+ /** Get betrayal history */
189
+ getBetrayalHistory() {
190
+ return [...this.betrayalHistory];
191
+ }
192
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Palace Rank Hierarchy - 后宫品级制度
3
+ *
4
+ * Defines the complete rank structure of the imperial harem,
5
+ * from 答应 (lowest) to 皇后 (highest).
6
+ */
7
+ export interface PalaceRank {
8
+ /** Rank level (1 = lowest, 8 = highest) */
9
+ level: number;
10
+ /** Chinese title */
11
+ title: string;
12
+ /** Maximum number of agents allowed at this rank */
13
+ maxSlots: number;
14
+ /** Monthly resource allowance */
15
+ monthlyAllowance: number;
16
+ /** Minimum ELO rating required to hold this rank */
17
+ minElo: number;
18
+ }
19
+ export declare const PALACE_RANKS: readonly PalaceRank[];
20
+ /** Get a rank by its level number */
21
+ export declare function getRankByLevel(level: number): PalaceRank | undefined;
22
+ /** Get a rank by its Chinese title */
23
+ export declare function getRankByTitle(title: string): PalaceRank | undefined;
24
+ /** Get the next higher rank */
25
+ export declare function getNextRank(currentLevel: number): PalaceRank | undefined;
26
+ /** Get the next lower rank */
27
+ export declare function getPreviousRank(currentLevel: number): PalaceRank | undefined;
28
+ /** Check if an agent can be promoted to the next rank based on ELO and slot availability */
29
+ export declare function canPromote(currentLevel: number, elo: number): boolean;
30
+ /** Check if a rank has reached its maximum number of occupants */
31
+ export declare function isRankFull(level: number, currentOccupants: number): boolean;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Palace Rank Hierarchy - 后宫品级制度
3
+ *
4
+ * Defines the complete rank structure of the imperial harem,
5
+ * from 答应 (lowest) to 皇后 (highest).
6
+ */
7
+ export const PALACE_RANKS = [
8
+ { level: 1, title: '答应', maxSlots: 999, monthlyAllowance: 10, minElo: 0 },
9
+ { level: 2, title: '常在', maxSlots: 999, monthlyAllowance: 20, minElo: 1000 },
10
+ { level: 3, title: '贵人', maxSlots: 6, monthlyAllowance: 50, minElo: 1100 },
11
+ { level: 4, title: '嫔', maxSlots: 6, monthlyAllowance: 100, minElo: 1200 },
12
+ { level: 5, title: '妃', maxSlots: 4, monthlyAllowance: 200, minElo: 1300 },
13
+ { level: 6, title: '贵妃', maxSlots: 2, monthlyAllowance: 400, minElo: 1400 },
14
+ { level: 7, title: '皇贵妃', maxSlots: 1, monthlyAllowance: 800, minElo: 1500 },
15
+ { level: 8, title: '皇后', maxSlots: 1, monthlyAllowance: 1000, minElo: 1600 },
16
+ ];
17
+ /** Get a rank by its level number */
18
+ export function getRankByLevel(level) {
19
+ return PALACE_RANKS.find(r => r.level === level);
20
+ }
21
+ /** Get a rank by its Chinese title */
22
+ export function getRankByTitle(title) {
23
+ return PALACE_RANKS.find(r => r.title === title);
24
+ }
25
+ /** Get the next higher rank */
26
+ export function getNextRank(currentLevel) {
27
+ return PALACE_RANKS.find(r => r.level === currentLevel + 1);
28
+ }
29
+ /** Get the next lower rank */
30
+ export function getPreviousRank(currentLevel) {
31
+ return PALACE_RANKS.find(r => r.level === currentLevel - 1);
32
+ }
33
+ /** Check if an agent can be promoted to the next rank based on ELO and slot availability */
34
+ export function canPromote(currentLevel, elo) {
35
+ const nextRank = getNextRank(currentLevel);
36
+ if (!nextRank)
37
+ return false;
38
+ return elo >= nextRank.minElo;
39
+ }
40
+ /** Check if a rank has reached its maximum number of occupants */
41
+ export function isRankFull(level, currentOccupants) {
42
+ const rank = getRankByLevel(level);
43
+ if (!rank)
44
+ return true;
45
+ return currentOccupants >= rank.maxSlots;
46
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Palace Resource Manager - 后宫资源管理
3
+ *
4
+ * Wraps agents-uni-core ResourcePool with palace-specific semantics:
5
+ * - 圣宠 (favor): finite, competitive, decays over time
6
+ * - 月例 (allowance): renewable, distributed by hierarchy
7
+ * - 宫殿 (palaces): positional, 12 total palaces
8
+ * - 侍女 (maids): finite, distributed by merit, 100 total
9
+ */
10
+ import { ResourcePool } from '@agents-uni/core';
11
+ import type { ResourceDefinition } from '@agents-uni/core';
12
+ /** Palace-specific resource definitions */
13
+ export declare const PALACE_RESOURCE_DEFINITIONS: ResourceDefinition[];
14
+ /** Palace names for the 12 available palaces */
15
+ export declare const PALACE_NAMES: readonly ["景仁宫", "承乾宫", "钟粹宫", "延禧宫", "永和宫", "景阳宫", "翊坤宫", "永寿宫", "启祥宫", "长春宫", "咸福宫", "储秀宫"];
16
+ export interface ResourceSummary {
17
+ agentId: string;
18
+ favor: number;
19
+ allowance: number;
20
+ palace: string | null;
21
+ maids: number;
22
+ }
23
+ /**
24
+ * Manages palace-specific resources on top of the core ResourcePool.
25
+ */
26
+ export declare class PalaceResourceManager {
27
+ private pool;
28
+ private palaceAssignments;
29
+ constructor(pool?: ResourcePool);
30
+ /** Get the underlying ResourcePool */
31
+ getPool(): ResourcePool;
32
+ /** Grant imperial favor to an agent */
33
+ grantFavor(agentId: string, amount: number, reason: string): boolean;
34
+ /** Revoke favor from an agent */
35
+ revokeFavor(agentId: string, amount: number, reason: string): boolean;
36
+ /** Distribute monthly allowance based on each agent's rank level */
37
+ distributeMonthlyAllowance(agents: Array<{
38
+ id: string;
39
+ rankLevel: number;
40
+ }>): Map<string, number>;
41
+ /** Assign a palace residence to an agent */
42
+ assignPalace(agentId: string, palaceName: string): boolean;
43
+ /** Get the palace assigned to an agent */
44
+ getPalace(agentId: string): string | null;
45
+ /** Release an agent's palace assignment */
46
+ releasePalace(agentId: string): void;
47
+ /** Get available (unassigned) palaces */
48
+ getAvailablePalaces(): string[];
49
+ /** Get a summary of all resources for an agent */
50
+ getResourceSummary(agentId: string): ResourceSummary;
51
+ /** Apply favor decay to all agents */
52
+ applyFavorDecay(): void;
53
+ /** Allocate maids to an agent */
54
+ allocateMaids(agentId: string, count: number, reason: string): boolean;
55
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Palace Resource Manager - 后宫资源管理
3
+ *
4
+ * Wraps agents-uni-core ResourcePool with palace-specific semantics:
5
+ * - 圣宠 (favor): finite, competitive, decays over time
6
+ * - 月例 (allowance): renewable, distributed by hierarchy
7
+ * - 宫殿 (palaces): positional, 12 total palaces
8
+ * - 侍女 (maids): finite, distributed by merit, 100 total
9
+ */
10
+ import { ResourcePool } from '@agents-uni/core';
11
+ import { getRankByLevel } from './ranks.js';
12
+ /** Palace-specific resource definitions */
13
+ export const PALACE_RESOURCE_DEFINITIONS = [
14
+ {
15
+ name: '圣宠',
16
+ type: 'finite',
17
+ total: 1000,
18
+ distribution: 'competitive',
19
+ decayRate: 0.05,
20
+ description: 'Imperial favor - won through competition, decays over time',
21
+ },
22
+ {
23
+ name: '月例',
24
+ type: 'renewable',
25
+ total: 10000,
26
+ distribution: 'hierarchy',
27
+ refreshInterval: 30 * 24 * 60 * 60 * 1000, // 30 days
28
+ description: 'Monthly allowance - distributed based on rank',
29
+ },
30
+ {
31
+ name: '宫殿',
32
+ type: 'positional',
33
+ total: 12,
34
+ distribution: 'hierarchy',
35
+ description: 'Palace residences - tied to rank and position',
36
+ },
37
+ {
38
+ name: '侍女',
39
+ type: 'finite',
40
+ total: 100,
41
+ distribution: 'merit',
42
+ description: 'Maids - assigned based on merit and performance',
43
+ },
44
+ ];
45
+ /** Palace names for the 12 available palaces */
46
+ export const PALACE_NAMES = [
47
+ '景仁宫', '承乾宫', '钟粹宫', '延禧宫',
48
+ '永和宫', '景阳宫', '翊坤宫', '永寿宫',
49
+ '启祥宫', '长春宫', '咸福宫', '储秀宫',
50
+ ];
51
+ /**
52
+ * Manages palace-specific resources on top of the core ResourcePool.
53
+ */
54
+ export class PalaceResourceManager {
55
+ pool;
56
+ palaceAssignments; // agentId -> palaceName
57
+ constructor(pool) {
58
+ this.pool = pool ?? new ResourcePool(PALACE_RESOURCE_DEFINITIONS);
59
+ this.palaceAssignments = new Map();
60
+ }
61
+ /** Get the underlying ResourcePool */
62
+ getPool() {
63
+ return this.pool;
64
+ }
65
+ /** Grant imperial favor to an agent */
66
+ grantFavor(agentId, amount, reason) {
67
+ return this.pool.allocate(agentId, '圣宠', amount, `恩赐圣宠: ${reason}`);
68
+ }
69
+ /** Revoke favor from an agent */
70
+ revokeFavor(agentId, amount, reason) {
71
+ return this.pool.revoke(agentId, '圣宠', amount, `收回圣宠: ${reason}`);
72
+ }
73
+ /** Distribute monthly allowance based on each agent's rank level */
74
+ distributeMonthlyAllowance(agents) {
75
+ const distributions = new Map();
76
+ for (const agent of agents) {
77
+ const rank = getRankByLevel(agent.rankLevel);
78
+ if (!rank)
79
+ continue;
80
+ const amount = rank.monthlyAllowance;
81
+ const success = this.pool.allocate(agent.id, '月例', amount, `月例发放 - ${rank.title}`);
82
+ if (success) {
83
+ distributions.set(agent.id, amount);
84
+ }
85
+ }
86
+ return distributions;
87
+ }
88
+ /** Assign a palace residence to an agent */
89
+ assignPalace(agentId, palaceName) {
90
+ // Check if the palace name is valid
91
+ if (!PALACE_NAMES.includes(palaceName)) {
92
+ return false;
93
+ }
94
+ // Check if the palace is already assigned
95
+ for (const [existingAgent, existingPalace] of this.palaceAssignments) {
96
+ if (existingPalace === palaceName && existingAgent !== agentId) {
97
+ return false;
98
+ }
99
+ }
100
+ // Remove agent's previous palace assignment
101
+ this.palaceAssignments.delete(agentId);
102
+ // Assign the new palace
103
+ this.palaceAssignments.set(agentId, palaceName);
104
+ // Track in the resource pool (1 unit = 1 palace)
105
+ const currentBalance = this.pool.getBalance(agentId, '宫殿');
106
+ if (currentBalance === 0) {
107
+ this.pool.allocate(agentId, '宫殿', 1, `入住${palaceName}`);
108
+ }
109
+ return true;
110
+ }
111
+ /** Get the palace assigned to an agent */
112
+ getPalace(agentId) {
113
+ return this.palaceAssignments.get(agentId) ?? null;
114
+ }
115
+ /** Release an agent's palace assignment */
116
+ releasePalace(agentId) {
117
+ const palace = this.palaceAssignments.get(agentId);
118
+ if (palace) {
119
+ this.palaceAssignments.delete(agentId);
120
+ this.pool.revoke(agentId, '宫殿', 1, `迁出${palace}`);
121
+ }
122
+ }
123
+ /** Get available (unassigned) palaces */
124
+ getAvailablePalaces() {
125
+ const assigned = new Set(this.palaceAssignments.values());
126
+ return PALACE_NAMES.filter(p => !assigned.has(p));
127
+ }
128
+ /** Get a summary of all resources for an agent */
129
+ getResourceSummary(agentId) {
130
+ return {
131
+ agentId,
132
+ favor: this.pool.getBalance(agentId, '圣宠'),
133
+ allowance: this.pool.getBalance(agentId, '月例'),
134
+ palace: this.palaceAssignments.get(agentId) ?? null,
135
+ maids: this.pool.getBalance(agentId, '侍女'),
136
+ };
137
+ }
138
+ /** Apply favor decay to all agents */
139
+ applyFavorDecay() {
140
+ this.pool.applyDecay('圣宠');
141
+ }
142
+ /** Allocate maids to an agent */
143
+ allocateMaids(agentId, count, reason) {
144
+ return this.pool.allocate(agentId, '侍女', count, `分配侍女: ${reason}`);
145
+ }
146
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Zhen Huan Palace Server - 后宫服务器
3
+ *
4
+ * HTTP API server for the palace competition system.
5
+ * Integrates agents-uni-core Dashboard as the homepage,
6
+ * and injects palace-specific extension panels (ELO, race history, factions, etc.)
7
+ * Uses Hono for lightweight, high-performance routing.
8
+ */
9
+ export interface ServerConfig {
10
+ port: number;
11
+ specPath: string;
12
+ openclawDir?: string;
13
+ }
14
+ export declare function startServer(config: ServerConfig): Promise<void>;