@agent-e/server 1.4.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.
- package/README.md +167 -0
- package/dist/AgentEServer-IF7QK2SK.mjs +7 -0
- package/dist/AgentEServer-IF7QK2SK.mjs.map +1 -0
- package/dist/chunk-VNNX52KX.mjs +468 -0
- package/dist/chunk-VNNX52KX.mjs.map +1 -0
- package/dist/index.d.mts +75 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +536 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +16 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @agent-e/server
|
|
2
|
+
|
|
3
|
+
Plug-and-play HTTP + WebSocket server for AgentE. Send your game's economy state, get back parameter adjustments.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { startServer } from '@agent-e/server';
|
|
9
|
+
|
|
10
|
+
const server = await startServer({ port: 3000 });
|
|
11
|
+
// Server is running — POST /tick, GET /health, etc.
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or with full configuration:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { AgentEServer } from '@agent-e/server';
|
|
18
|
+
|
|
19
|
+
const server = new AgentEServer({
|
|
20
|
+
port: 3000,
|
|
21
|
+
host: '0.0.0.0',
|
|
22
|
+
agentEConfig: {
|
|
23
|
+
mode: 'autonomous', // or 'advisor' for recommendations only
|
|
24
|
+
gracePeriod: 50, // no interventions before tick 50
|
|
25
|
+
checkInterval: 5, // analyze every 5 ticks
|
|
26
|
+
dominantRoles: ['Fighter'],
|
|
27
|
+
maxAdjustmentPercent: 0.15,
|
|
28
|
+
cooldownTicks: 15,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await server.start();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## API Reference
|
|
36
|
+
|
|
37
|
+
### POST /tick
|
|
38
|
+
|
|
39
|
+
Send economy state, receive parameter adjustments.
|
|
40
|
+
|
|
41
|
+
**Request:**
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"state": {
|
|
45
|
+
"tick": 100,
|
|
46
|
+
"roles": ["Fighter", "Crafter"],
|
|
47
|
+
"resources": ["ore", "weapons"],
|
|
48
|
+
"currencies": ["gold"],
|
|
49
|
+
"agentBalances": { "a1": { "gold": 150 } },
|
|
50
|
+
"agentRoles": { "a1": "Fighter" },
|
|
51
|
+
"agentInventories": { "a1": { "weapons": 2 } },
|
|
52
|
+
"marketPrices": { "gold": { "ore": 15 } },
|
|
53
|
+
"recentTransactions": []
|
|
54
|
+
},
|
|
55
|
+
"events": []
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Response (200):**
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"adjustments": [{ "key": "craftingCost", "value": 12.5 }],
|
|
63
|
+
"alerts": [{ "principle": "P1", "severity": 7 }],
|
|
64
|
+
"health": 85,
|
|
65
|
+
"decisions": [{ "id": "d_1", "parameter": "craftingCost", "result": "applied" }]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Error (400):** Invalid state returns validation errors.
|
|
70
|
+
|
|
71
|
+
### GET /health
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"health": 85,
|
|
76
|
+
"tick": 100,
|
|
77
|
+
"mode": "autonomous",
|
|
78
|
+
"activePlans": 1,
|
|
79
|
+
"uptime": 60000
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### GET /decisions
|
|
84
|
+
|
|
85
|
+
Query parameters: `?limit=50`, `?since=100`
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"decisions": [{
|
|
90
|
+
"id": "d_1",
|
|
91
|
+
"tick": 100,
|
|
92
|
+
"principle": "P1",
|
|
93
|
+
"parameter": "craftingCost",
|
|
94
|
+
"result": "applied",
|
|
95
|
+
"reasoning": "..."
|
|
96
|
+
}]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### POST /config
|
|
101
|
+
|
|
102
|
+
Lock/unlock parameters, change mode.
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{ "action": "lock", "param": "craftingCost" }
|
|
106
|
+
{ "action": "unlock", "param": "craftingCost" }
|
|
107
|
+
{ "action": "constrain", "param": "miningYield", "min": 0.5, "max": 2.0 }
|
|
108
|
+
{ "action": "mode", "mode": "advisor" }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### GET /principles
|
|
112
|
+
|
|
113
|
+
Lists all 60 built-in principles.
|
|
114
|
+
|
|
115
|
+
### POST /diagnose
|
|
116
|
+
|
|
117
|
+
Run Observer + Diagnoser without side effects (no parameter changes).
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{ "state": { ... } }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## WebSocket
|
|
124
|
+
|
|
125
|
+
Connect to the same port via WebSocket upgrade.
|
|
126
|
+
|
|
127
|
+
### Client → Server Messages
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{ "type": "tick", "state": {...}, "events": [...] }
|
|
131
|
+
{ "type": "event", "event": { "type": "trade", ... } }
|
|
132
|
+
{ "type": "health" }
|
|
133
|
+
{ "type": "diagnose", "state": {...} }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Server → Client Messages
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{ "type": "tick_result", "adjustments": [...], "health": 85 }
|
|
140
|
+
{ "type": "health_result", "health": 85, "uptime": 60000 }
|
|
141
|
+
{ "type": "diagnose_result", "diagnoses": [...] }
|
|
142
|
+
{ "type": "validation_error", "validation": {...} }
|
|
143
|
+
{ "type": "validation_warning", "warning": {...} }
|
|
144
|
+
{ "type": "error", "error": "..." }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Heartbeat: Server pings every 30 seconds.
|
|
148
|
+
|
|
149
|
+
## State Validation
|
|
150
|
+
|
|
151
|
+
All incoming state is validated before processing. Invalid state returns detailed errors with paths:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"error": "Invalid state",
|
|
156
|
+
"validation": {
|
|
157
|
+
"valid": false,
|
|
158
|
+
"errors": [{
|
|
159
|
+
"path": "agentBalances.a1.gold",
|
|
160
|
+
"expected": "number >= 0",
|
|
161
|
+
"received": "string",
|
|
162
|
+
"message": "agentBalances.a1.gold must be a non-negative number"
|
|
163
|
+
}],
|
|
164
|
+
"warnings": []
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
// src/AgentEServer.ts
|
|
2
|
+
import * as http from "http";
|
|
3
|
+
import {
|
|
4
|
+
AgentE
|
|
5
|
+
} from "@agent-e/core";
|
|
6
|
+
|
|
7
|
+
// src/routes.ts
|
|
8
|
+
import { validateEconomyState } from "@agent-e/core";
|
|
9
|
+
function setCorsHeaders(res) {
|
|
10
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
11
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
12
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
13
|
+
}
|
|
14
|
+
function json(res, status, data) {
|
|
15
|
+
setCorsHeaders(res);
|
|
16
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
17
|
+
res.end(JSON.stringify(data));
|
|
18
|
+
}
|
|
19
|
+
function readBody(req) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const chunks = [];
|
|
22
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
23
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
24
|
+
req.on("error", reject);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function createRouteHandler(server) {
|
|
28
|
+
return async (req, res) => {
|
|
29
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
30
|
+
const path = url.pathname;
|
|
31
|
+
const method = req.method?.toUpperCase() ?? "GET";
|
|
32
|
+
if (method === "OPTIONS") {
|
|
33
|
+
setCorsHeaders(res);
|
|
34
|
+
res.writeHead(204);
|
|
35
|
+
res.end();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
if (path === "/tick" && method === "POST") {
|
|
40
|
+
const body = await readBody(req);
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(body);
|
|
44
|
+
} catch {
|
|
45
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!parsed || typeof parsed !== "object") {
|
|
49
|
+
json(res, 400, { error: "Body must be a JSON object" });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const payload = parsed;
|
|
53
|
+
const state = payload["state"] ?? parsed;
|
|
54
|
+
const events = payload["events"];
|
|
55
|
+
const validation = validateEconomyState(state);
|
|
56
|
+
if (!validation.valid) {
|
|
57
|
+
json(res, 400, {
|
|
58
|
+
error: "Invalid state",
|
|
59
|
+
validation
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const result = await server.processTick(
|
|
64
|
+
state,
|
|
65
|
+
Array.isArray(events) ? events : void 0
|
|
66
|
+
);
|
|
67
|
+
json(res, 200, {
|
|
68
|
+
adjustments: result.adjustments,
|
|
69
|
+
alerts: result.alerts.map((a) => ({
|
|
70
|
+
principle: a.principle.id,
|
|
71
|
+
name: a.principle.name,
|
|
72
|
+
severity: a.violation.severity,
|
|
73
|
+
evidence: a.violation.evidence,
|
|
74
|
+
suggestedAction: a.violation.suggestedAction
|
|
75
|
+
})),
|
|
76
|
+
health: result.health,
|
|
77
|
+
decisions: result.decisions.map((d) => ({
|
|
78
|
+
id: d.id,
|
|
79
|
+
tick: d.tick,
|
|
80
|
+
principle: d.diagnosis.principle.id,
|
|
81
|
+
parameter: d.plan.parameter,
|
|
82
|
+
result: d.result,
|
|
83
|
+
reasoning: d.reasoning
|
|
84
|
+
}))
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (path === "/health" && method === "GET") {
|
|
89
|
+
const agentE = server.getAgentE();
|
|
90
|
+
json(res, 200, {
|
|
91
|
+
health: agentE.getHealth(),
|
|
92
|
+
tick: agentE.metrics.latest()?.tick ?? 0,
|
|
93
|
+
mode: agentE.getMode(),
|
|
94
|
+
activePlans: agentE.getActivePlans().length,
|
|
95
|
+
uptime: server.getUptime()
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (path === "/decisions" && method === "GET") {
|
|
100
|
+
const limit = parseInt(url.searchParams.get("limit") ?? "50", 10);
|
|
101
|
+
const since = url.searchParams.get("since");
|
|
102
|
+
const agentE = server.getAgentE();
|
|
103
|
+
let decisions;
|
|
104
|
+
if (since) {
|
|
105
|
+
decisions = agentE.getDecisions({ since: parseInt(since, 10) });
|
|
106
|
+
} else {
|
|
107
|
+
decisions = agentE.log.latest(limit);
|
|
108
|
+
}
|
|
109
|
+
json(res, 200, {
|
|
110
|
+
decisions: decisions.map((d) => ({
|
|
111
|
+
id: d.id,
|
|
112
|
+
tick: d.tick,
|
|
113
|
+
timestamp: d.timestamp,
|
|
114
|
+
principle: d.diagnosis.principle.id,
|
|
115
|
+
principeName: d.diagnosis.principle.name,
|
|
116
|
+
parameter: d.plan.parameter,
|
|
117
|
+
currentValue: d.plan.currentValue,
|
|
118
|
+
targetValue: d.plan.targetValue,
|
|
119
|
+
result: d.result,
|
|
120
|
+
reasoning: d.reasoning
|
|
121
|
+
}))
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (path === "/config" && method === "POST") {
|
|
126
|
+
const body = await readBody(req);
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(body);
|
|
130
|
+
} catch {
|
|
131
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const config = parsed;
|
|
135
|
+
if (config["action"] === "lock" && typeof config["param"] === "string") {
|
|
136
|
+
server.lock(config["param"]);
|
|
137
|
+
json(res, 200, { ok: true, action: "lock", param: config["param"] });
|
|
138
|
+
} else if (config["action"] === "unlock" && typeof config["param"] === "string") {
|
|
139
|
+
server.unlock(config["param"]);
|
|
140
|
+
json(res, 200, { ok: true, action: "unlock", param: config["param"] });
|
|
141
|
+
} else if (config["action"] === "constrain" && typeof config["param"] === "string" && typeof config["min"] === "number" && typeof config["max"] === "number") {
|
|
142
|
+
server.constrain(config["param"], { min: config["min"], max: config["max"] });
|
|
143
|
+
json(res, 200, { ok: true, action: "constrain", param: config["param"] });
|
|
144
|
+
} else if (config["action"] === "mode" && (config["mode"] === "autonomous" || config["mode"] === "advisor")) {
|
|
145
|
+
server.setMode(config["mode"]);
|
|
146
|
+
json(res, 200, { ok: true, action: "mode", mode: config["mode"] });
|
|
147
|
+
} else {
|
|
148
|
+
json(res, 400, {
|
|
149
|
+
error: "Invalid config action. Use: lock, unlock, constrain, or mode"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (path === "/principles" && method === "GET") {
|
|
155
|
+
const principles = server.getAgentE().getPrinciples();
|
|
156
|
+
json(res, 200, {
|
|
157
|
+
count: principles.length,
|
|
158
|
+
principles: principles.map((p) => ({
|
|
159
|
+
id: p.id,
|
|
160
|
+
name: p.name,
|
|
161
|
+
category: p.category,
|
|
162
|
+
description: p.description
|
|
163
|
+
}))
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (path === "/diagnose" && method === "POST") {
|
|
168
|
+
const body = await readBody(req);
|
|
169
|
+
let parsed;
|
|
170
|
+
try {
|
|
171
|
+
parsed = JSON.parse(body);
|
|
172
|
+
} catch {
|
|
173
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const payload = parsed;
|
|
177
|
+
const state = payload["state"] ?? parsed;
|
|
178
|
+
const validation = validateEconomyState(state);
|
|
179
|
+
if (!validation.valid) {
|
|
180
|
+
json(res, 400, { error: "Invalid state", validation });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const result = server.diagnoseOnly(state);
|
|
184
|
+
json(res, 200, {
|
|
185
|
+
health: result.health,
|
|
186
|
+
diagnoses: result.diagnoses.map((d) => ({
|
|
187
|
+
principle: d.principle.id,
|
|
188
|
+
name: d.principle.name,
|
|
189
|
+
severity: d.violation.severity,
|
|
190
|
+
evidence: d.violation.evidence,
|
|
191
|
+
suggestedAction: d.violation.suggestedAction
|
|
192
|
+
}))
|
|
193
|
+
});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
json(res, 404, { error: "Not found" });
|
|
197
|
+
} catch (err) {
|
|
198
|
+
json(res, 500, { error: "Internal server error" });
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/websocket.ts
|
|
204
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
205
|
+
import { validateEconomyState as validateEconomyState2 } from "@agent-e/core";
|
|
206
|
+
function send(ws, data) {
|
|
207
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
208
|
+
ws.send(JSON.stringify(data));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function createWebSocketHandler(httpServer, server) {
|
|
212
|
+
const wss = new WebSocketServer({ server: httpServer });
|
|
213
|
+
const heartbeatInterval = setInterval(() => {
|
|
214
|
+
for (const ws of wss.clients) {
|
|
215
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
216
|
+
ws.ping();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}, 3e4);
|
|
220
|
+
wss.on("connection", (ws) => {
|
|
221
|
+
ws.on("message", async (raw) => {
|
|
222
|
+
let msg;
|
|
223
|
+
try {
|
|
224
|
+
msg = JSON.parse(raw.toString());
|
|
225
|
+
} catch {
|
|
226
|
+
send(ws, { type: "error", error: "Invalid JSON" });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!msg.type || typeof msg.type !== "string") {
|
|
230
|
+
send(ws, { type: "error", error: 'Missing "type" field' });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
switch (msg.type) {
|
|
234
|
+
case "tick": {
|
|
235
|
+
const state = msg["state"];
|
|
236
|
+
const events = msg["events"];
|
|
237
|
+
const validation = validateEconomyState2(state);
|
|
238
|
+
if (!validation.valid) {
|
|
239
|
+
send(ws, { type: "validation_error", validation });
|
|
240
|
+
for (const w of validation.warnings) {
|
|
241
|
+
send(ws, { type: "validation_warning", warning: w });
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
for (const w of validation.warnings) {
|
|
246
|
+
send(ws, { type: "validation_warning", warning: w });
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const result = await server.processTick(
|
|
250
|
+
state,
|
|
251
|
+
Array.isArray(events) ? events : void 0
|
|
252
|
+
);
|
|
253
|
+
send(ws, {
|
|
254
|
+
type: "tick_result",
|
|
255
|
+
adjustments: result.adjustments,
|
|
256
|
+
alerts: result.alerts.map((a) => ({
|
|
257
|
+
principle: a.principle.id,
|
|
258
|
+
name: a.principle.name,
|
|
259
|
+
severity: a.violation.severity,
|
|
260
|
+
suggestedAction: a.violation.suggestedAction
|
|
261
|
+
})),
|
|
262
|
+
health: result.health,
|
|
263
|
+
decisions: result.decisions.map((d) => ({
|
|
264
|
+
id: d.id,
|
|
265
|
+
tick: d.tick,
|
|
266
|
+
principle: d.diagnosis.principle.id,
|
|
267
|
+
parameter: d.plan.parameter,
|
|
268
|
+
result: d.result
|
|
269
|
+
}))
|
|
270
|
+
});
|
|
271
|
+
} catch (err) {
|
|
272
|
+
send(ws, { type: "error", error: "Tick processing failed" });
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case "event": {
|
|
277
|
+
const event = msg["event"];
|
|
278
|
+
if (event) {
|
|
279
|
+
server.getAgentE().ingest(event);
|
|
280
|
+
send(ws, { type: "event_ack" });
|
|
281
|
+
} else {
|
|
282
|
+
send(ws, { type: "error", error: 'Missing "event" field' });
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
case "health": {
|
|
287
|
+
const agentE = server.getAgentE();
|
|
288
|
+
send(ws, {
|
|
289
|
+
type: "health_result",
|
|
290
|
+
health: agentE.getHealth(),
|
|
291
|
+
tick: agentE.metrics.latest()?.tick ?? 0,
|
|
292
|
+
mode: agentE.getMode(),
|
|
293
|
+
activePlans: agentE.getActivePlans().length,
|
|
294
|
+
uptime: server.getUptime()
|
|
295
|
+
});
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case "diagnose": {
|
|
299
|
+
const state = msg["state"];
|
|
300
|
+
const validation = validateEconomyState2(state);
|
|
301
|
+
if (!validation.valid) {
|
|
302
|
+
send(ws, { type: "validation_error", validation });
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const result = server.diagnoseOnly(state);
|
|
306
|
+
send(ws, {
|
|
307
|
+
type: "diagnose_result",
|
|
308
|
+
health: result.health,
|
|
309
|
+
diagnoses: result.diagnoses.map((d) => ({
|
|
310
|
+
principle: d.principle.id,
|
|
311
|
+
name: d.principle.name,
|
|
312
|
+
severity: d.violation.severity,
|
|
313
|
+
suggestedAction: d.violation.suggestedAction
|
|
314
|
+
}))
|
|
315
|
+
});
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
default:
|
|
319
|
+
send(ws, { type: "error", error: `Unknown message type: "${msg.type}"` });
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
return () => {
|
|
324
|
+
clearInterval(heartbeatInterval);
|
|
325
|
+
wss.close();
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/AgentEServer.ts
|
|
330
|
+
var AgentEServer = class {
|
|
331
|
+
constructor(config = {}) {
|
|
332
|
+
this.lastState = null;
|
|
333
|
+
this.adjustmentQueue = [];
|
|
334
|
+
this.alerts = [];
|
|
335
|
+
this.startedAt = Date.now();
|
|
336
|
+
this.cleanupWs = null;
|
|
337
|
+
this.port = config.port ?? 3e3;
|
|
338
|
+
this.host = config.host ?? "0.0.0.0";
|
|
339
|
+
const adapter = {
|
|
340
|
+
getState: () => {
|
|
341
|
+
if (!this.lastState) {
|
|
342
|
+
return {
|
|
343
|
+
tick: 0,
|
|
344
|
+
roles: [],
|
|
345
|
+
resources: [],
|
|
346
|
+
currencies: ["default"],
|
|
347
|
+
agentBalances: {},
|
|
348
|
+
agentRoles: {},
|
|
349
|
+
agentInventories: {},
|
|
350
|
+
marketPrices: {},
|
|
351
|
+
recentTransactions: []
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return this.lastState;
|
|
355
|
+
},
|
|
356
|
+
setParam: (key, value) => {
|
|
357
|
+
this.adjustmentQueue.push({ key, value });
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
const agentEConfig = {
|
|
361
|
+
adapter,
|
|
362
|
+
mode: config.agentEConfig?.mode ?? "autonomous",
|
|
363
|
+
gracePeriod: config.agentEConfig?.gracePeriod ?? 0,
|
|
364
|
+
checkInterval: config.agentEConfig?.checkInterval ?? 1,
|
|
365
|
+
...config.agentEConfig?.dominantRoles ? { dominantRoles: config.agentEConfig.dominantRoles } : {},
|
|
366
|
+
...config.agentEConfig?.idealDistribution ? { idealDistribution: config.agentEConfig.idealDistribution } : {},
|
|
367
|
+
...config.agentEConfig?.maxAdjustmentPercent !== void 0 ? { maxAdjustmentPercent: config.agentEConfig.maxAdjustmentPercent } : {},
|
|
368
|
+
...config.agentEConfig?.cooldownTicks !== void 0 ? { cooldownTicks: config.agentEConfig.cooldownTicks } : {},
|
|
369
|
+
...config.agentEConfig?.thresholds ? { thresholds: config.agentEConfig.thresholds } : {}
|
|
370
|
+
};
|
|
371
|
+
this.agentE = new AgentE(agentEConfig);
|
|
372
|
+
this.agentE.on("alert", (diagnosis) => {
|
|
373
|
+
this.alerts.push(diagnosis);
|
|
374
|
+
});
|
|
375
|
+
this.agentE.connect(adapter).start();
|
|
376
|
+
const routeHandler = createRouteHandler(this);
|
|
377
|
+
this.server = http.createServer(routeHandler);
|
|
378
|
+
}
|
|
379
|
+
async start() {
|
|
380
|
+
this.cleanupWs = createWebSocketHandler(this.server, this);
|
|
381
|
+
return new Promise((resolve) => {
|
|
382
|
+
this.server.listen(this.port, this.host, () => {
|
|
383
|
+
resolve();
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
async stop() {
|
|
388
|
+
this.agentE.stop();
|
|
389
|
+
if (this.cleanupWs) this.cleanupWs();
|
|
390
|
+
return new Promise((resolve, reject) => {
|
|
391
|
+
this.server.close((err) => {
|
|
392
|
+
if (err) reject(err);
|
|
393
|
+
else resolve();
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
getAgentE() {
|
|
398
|
+
return this.agentE;
|
|
399
|
+
}
|
|
400
|
+
getAddress() {
|
|
401
|
+
const addr = this.server.address();
|
|
402
|
+
if (addr && typeof addr === "object") {
|
|
403
|
+
return { port: addr.port, host: addr.address };
|
|
404
|
+
}
|
|
405
|
+
return { port: this.port, host: this.host };
|
|
406
|
+
}
|
|
407
|
+
getUptime() {
|
|
408
|
+
return Date.now() - this.startedAt;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Process a tick with the given state.
|
|
412
|
+
* 1. Clear adjustment queue
|
|
413
|
+
* 2. Set state
|
|
414
|
+
* 3. Ingest events
|
|
415
|
+
* 4. Run agentE.tick(state)
|
|
416
|
+
* 5. Drain adjustment queue
|
|
417
|
+
* 6. Return response
|
|
418
|
+
*/
|
|
419
|
+
async processTick(state, events) {
|
|
420
|
+
this.adjustmentQueue = [];
|
|
421
|
+
this.alerts = [];
|
|
422
|
+
this.lastState = state;
|
|
423
|
+
if (events) {
|
|
424
|
+
for (const event of events) {
|
|
425
|
+
this.agentE.ingest(event);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
await this.agentE.tick(state);
|
|
429
|
+
const adjustments = [...this.adjustmentQueue];
|
|
430
|
+
this.adjustmentQueue = [];
|
|
431
|
+
const decisions = this.agentE.getDecisions({ since: state.tick, until: state.tick });
|
|
432
|
+
return {
|
|
433
|
+
adjustments,
|
|
434
|
+
alerts: [...this.alerts],
|
|
435
|
+
health: this.agentE.getHealth(),
|
|
436
|
+
decisions
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Run Observer + Diagnoser without side effects (no execution)
|
|
441
|
+
*/
|
|
442
|
+
diagnoseOnly(state) {
|
|
443
|
+
const prevState = this.lastState;
|
|
444
|
+
this.lastState = state;
|
|
445
|
+
const diagnoser = this.agentE;
|
|
446
|
+
const diagnoses = diagnoser.diagnoseNow();
|
|
447
|
+
const health = diagnoser.getHealth();
|
|
448
|
+
this.lastState = prevState;
|
|
449
|
+
return { diagnoses, health };
|
|
450
|
+
}
|
|
451
|
+
setMode(mode) {
|
|
452
|
+
this.agentE.setMode(mode);
|
|
453
|
+
}
|
|
454
|
+
lock(param) {
|
|
455
|
+
this.agentE.lock(param);
|
|
456
|
+
}
|
|
457
|
+
unlock(param) {
|
|
458
|
+
this.agentE.unlock(param);
|
|
459
|
+
}
|
|
460
|
+
constrain(param, bounds) {
|
|
461
|
+
this.agentE.constrain(param, bounds);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
export {
|
|
466
|
+
AgentEServer
|
|
467
|
+
};
|
|
468
|
+
//# sourceMappingURL=chunk-VNNX52KX.mjs.map
|