@colin999n9/gworld-agent 0.1.0-alpha.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 +127 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +256 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-installer.d.ts +20 -0
- package/dist/client-installer.js +48 -0
- package/dist/client-installer.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.js +184 -0
- package/dist/config.js.map +1 -0
- package/dist/data/indexer-client.d.ts +34 -0
- package/dist/data/indexer-client.js +172 -0
- package/dist/data/indexer-client.js.map +1 -0
- package/dist/data/rpc-reader.d.ts +69 -0
- package/dist/data/rpc-reader.js +163 -0
- package/dist/data/rpc-reader.js.map +1 -0
- package/dist/domain.d.ts +204 -0
- package/dist/domain.js +2 -0
- package/dist/domain.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -0
- package/dist/factory.d.ts +4 -0
- package/dist/factory.js +25 -0
- package/dist/factory.js.map +1 -0
- package/dist/fs.d.ts +3 -0
- package/dist/fs.js +35 -0
- package/dist/fs.js.map +1 -0
- package/dist/generated/abis.d.ts +7446 -0
- package/dist/generated/abis.js +4861 -0
- package/dist/generated/abis.js.map +1 -0
- package/dist/generated/deployments.d.ts +145 -0
- package/dist/generated/deployments.js +150 -0
- package/dist/generated/deployments.js.map +1 -0
- package/dist/generated/protocol.d.ts +165 -0
- package/dist/generated/protocol.js +132 -0
- package/dist/generated/protocol.js.map +1 -0
- package/dist/geometry.d.ts +24 -0
- package/dist/geometry.js +31 -0
- package/dist/geometry.js.map +1 -0
- package/dist/json.d.ts +3 -0
- package/dist/json.js +14 -0
- package/dist/json.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +134 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +2 -0
- package/dist/mcp/stdio.js +23 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/runner/audit-store.d.ts +27 -0
- package/dist/runner/audit-store.js +61 -0
- package/dist/runner/audit-store.js.map +1 -0
- package/dist/runner/file-state.d.ts +7 -0
- package/dist/runner/file-state.js +23 -0
- package/dist/runner/file-state.js.map +1 -0
- package/dist/runner/legal-actions.d.ts +22 -0
- package/dist/runner/legal-actions.js +171 -0
- package/dist/runner/legal-actions.js.map +1 -0
- package/dist/runner/observation.d.ts +12 -0
- package/dist/runner/observation.js +47 -0
- package/dist/runner/observation.js.map +1 -0
- package/dist/runner/policy.d.ts +10 -0
- package/dist/runner/policy.js +68 -0
- package/dist/runner/policy.js.map +1 -0
- package/dist/runner/runtime.d.ts +73 -0
- package/dist/runner/runtime.js +236 -0
- package/dist/runner/runtime.js.map +1 -0
- package/dist/runner/strategy.d.ts +2 -0
- package/dist/runner/strategy.js +19 -0
- package/dist/runner/strategy.js.map +1 -0
- package/dist/runner/transaction-executor.d.ts +58 -0
- package/dist/runner/transaction-executor.js +114 -0
- package/dist/runner/transaction-executor.js.map +1 -0
- package/dist/runner/writer-lock.d.ts +14 -0
- package/dist/runner/writer-lock.js +81 -0
- package/dist/runner/writer-lock.js.map +1 -0
- package/dist/wallet/keystore.d.ts +18 -0
- package/dist/wallet/keystore.js +111 -0
- package/dist/wallet/keystore.js.map +1 -0
- package/dist/wallet/password-store.d.ts +27 -0
- package/dist/wallet/password-store.js +118 -0
- package/dist/wallet/password-store.js.map +1 -0
- package/package.json +41 -0
- package/skill/gworld/SKILL.md +58 -0
- package/skill/gworld/references/rules.md +33 -0
- package/skill/gworld/references/troubleshooting.md +22 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { AgentError } from '../errors.js';
|
|
3
|
+
import { applyDistanceFactor, adjacent } from '../geometry.js';
|
|
4
|
+
import { protocolConfig } from '../generated/protocol.js';
|
|
5
|
+
function availableAp(observation, policy) {
|
|
6
|
+
const protocolRemaining = protocolConfig.apRoundSpendCap > observation.agent.spentThisRoundCap
|
|
7
|
+
? protocolConfig.apRoundSpendCap - observation.agent.spentThisRoundCap
|
|
8
|
+
: 0n;
|
|
9
|
+
const policyRemaining = policy.maxApPerRoundCap > observation.agent.spentThisRoundCap
|
|
10
|
+
? policy.maxApPerRoundCap - observation.agent.spentThisRoundCap
|
|
11
|
+
: 0n;
|
|
12
|
+
return [observation.agent.apBalanceCap, protocolRemaining, policyRemaining].reduce((minimum, value) => value < minimum ? value : minimum);
|
|
13
|
+
}
|
|
14
|
+
function movementCost(tile) {
|
|
15
|
+
const move = applyDistanceFactor(protocolConfig.moveCostCap, tile.coord);
|
|
16
|
+
return tile.ownerAgentId == null ? move : move + move * protocolConfig.tollBps / protocolConfig.bpsBase;
|
|
17
|
+
}
|
|
18
|
+
export class ActionCatalog {
|
|
19
|
+
ttlMs;
|
|
20
|
+
now;
|
|
21
|
+
tickets = new Map();
|
|
22
|
+
constructor(ttlMs, now = Date.now) {
|
|
23
|
+
this.ttlMs = ttlMs;
|
|
24
|
+
this.now = now;
|
|
25
|
+
}
|
|
26
|
+
issue(observation, candidates) {
|
|
27
|
+
this.prune();
|
|
28
|
+
return candidates.map((candidate) => {
|
|
29
|
+
const createdAt = this.now();
|
|
30
|
+
const ticket = {
|
|
31
|
+
actionId: randomUUID(),
|
|
32
|
+
...candidate,
|
|
33
|
+
chainId: observation.chainId,
|
|
34
|
+
round: observation.round,
|
|
35
|
+
nonce: observation.agent.actionNonce,
|
|
36
|
+
createdAt,
|
|
37
|
+
expiresAt: createdAt + this.ttlMs,
|
|
38
|
+
};
|
|
39
|
+
this.tickets.set(ticket.actionId, ticket);
|
|
40
|
+
return ticket;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
resolve(actionId, observation) {
|
|
44
|
+
const ticket = this.tickets.get(actionId);
|
|
45
|
+
if (!ticket)
|
|
46
|
+
throw new AgentError('action_not_found', `Unknown actionId: ${actionId}`);
|
|
47
|
+
if (ticket.expiresAt <= this.now()) {
|
|
48
|
+
this.tickets.delete(actionId);
|
|
49
|
+
throw new AgentError('action_expired', 'Action ticket has expired; observe and plan again');
|
|
50
|
+
}
|
|
51
|
+
if (ticket.chainId !== observation.chainId || ticket.round !== observation.round || ticket.nonce !== observation.agent.actionNonce) {
|
|
52
|
+
throw new AgentError('action_stale', 'Action ticket no longer matches current chain, round, or nonce');
|
|
53
|
+
}
|
|
54
|
+
if ('agentId' in ticket.action && ticket.action.agentId !== observation.agent.id) {
|
|
55
|
+
throw new AgentError('action_stale', 'Action ticket no longer matches the selected Agent');
|
|
56
|
+
}
|
|
57
|
+
return ticket;
|
|
58
|
+
}
|
|
59
|
+
consume(actionId, observation) {
|
|
60
|
+
const ticket = this.resolve(actionId, observation);
|
|
61
|
+
this.tickets.delete(actionId);
|
|
62
|
+
return ticket;
|
|
63
|
+
}
|
|
64
|
+
prune() {
|
|
65
|
+
const now = this.now();
|
|
66
|
+
for (const [id, ticket] of this.tickets)
|
|
67
|
+
if (ticket.expiresAt <= now)
|
|
68
|
+
this.tickets.delete(id);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export class LegalActionPlanner {
|
|
72
|
+
catalog;
|
|
73
|
+
constructor(catalog) {
|
|
74
|
+
this.catalog = catalog;
|
|
75
|
+
}
|
|
76
|
+
plan(observation, policy) {
|
|
77
|
+
const candidates = [];
|
|
78
|
+
const agent = observation.agent;
|
|
79
|
+
const ap = availableAp(observation, policy);
|
|
80
|
+
const checkinReady = agent.lastCheckinAt === 0n
|
|
81
|
+
|| agent.blockTimestamp >= agent.lastCheckinAt + protocolConfig.checkinCooldownSeconds;
|
|
82
|
+
if (checkinReady && agent.apBalanceCap < protocolConfig.apCheckinCap) {
|
|
83
|
+
candidates.push({ action: { kind: 'checkin', agentId: agent.id }, risk: 'low', reason: 'Check-in is ready and AP is below the faucet threshold.' });
|
|
84
|
+
}
|
|
85
|
+
const fragment = observation.fragments.find((item) => item.agentId === agent.id && item.status === 'pending');
|
|
86
|
+
if (fragment) {
|
|
87
|
+
candidates.push({
|
|
88
|
+
action: { kind: 'continueFragmentResolution', agentId: agent.id, maxSteps: protocolConfig.maxFragmentBatchSteps },
|
|
89
|
+
risk: 'low',
|
|
90
|
+
reason: `Fragment resolution job ${fragment.jobId} is locking world actions.`,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (!fragment && agent.position) {
|
|
94
|
+
const nearby = observation.tiles.filter((tile) => tile.activated && adjacent(tile.coord, agent.position));
|
|
95
|
+
for (const tile of nearby) {
|
|
96
|
+
if (tile.ownerAgentId == null) {
|
|
97
|
+
const cost = applyDistanceFactor(protocolConfig.claimCostCap, tile.coord);
|
|
98
|
+
if (cost <= ap)
|
|
99
|
+
candidates.push({
|
|
100
|
+
action: { kind: 'claim', agentId: agent.id, tileKey: tile.key, apCostCap: cost },
|
|
101
|
+
risk: 'low',
|
|
102
|
+
reason: `Activated neutral tile (${tile.coord.q},${tile.coord.r}) is adjacent and costs ${cost} cAP.`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else if (tile.ownerAgentId !== agent.id) {
|
|
106
|
+
const costPerPoint = applyDistanceFactor(protocolConfig.capPerAp, tile.coord);
|
|
107
|
+
const apCap = BigInt(tile.effectiveDefense) * costPerPoint;
|
|
108
|
+
if (apCap > 0n && apCap <= ap)
|
|
109
|
+
candidates.push({
|
|
110
|
+
action: { kind: 'attack', agentId: agent.id, tileKey: tile.key, apCap },
|
|
111
|
+
risk: 'high',
|
|
112
|
+
reason: `Adjacent enemy tile can be captured with an estimated ${apCap} cAP cap.`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const cost = movementCost(tile);
|
|
116
|
+
if (cost <= ap)
|
|
117
|
+
candidates.push({
|
|
118
|
+
action: {
|
|
119
|
+
kind: 'move', agentId: agent.id, expectedFrom: agent.position,
|
|
120
|
+
path: [tile.key], expectedNonce: agent.actionNonce, apCostCap: cost,
|
|
121
|
+
},
|
|
122
|
+
risk: tile.ownerAgentId != null && tile.ownerAgentId !== agent.id ? 'medium' : 'low',
|
|
123
|
+
reason: `Move one step to (${tile.coord.q},${tile.coord.r}) for at most ${cost} cAP including toll.`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const owned = observation.tiles
|
|
127
|
+
.filter((tile) => tile.ownerAgentId === agent.id && tile.effectiveDefense < Number(protocolConfig.defenseMax))
|
|
128
|
+
.sort((left, right) => left.effectiveDefense - right.effectiveDefense || Number(left.key - right.key));
|
|
129
|
+
const target = owned.find((tile) => tile.coord.q === agent.position?.q && tile.coord.r === agent.position?.r) ?? owned[0];
|
|
130
|
+
if (target) {
|
|
131
|
+
const local = target.coord.q === agent.position.q && target.coord.r === agent.position.r;
|
|
132
|
+
const apCap = applyDistanceFactor(local ? protocolConfig.reinforceLocalCap : protocolConfig.reinforceRemoteCap, target.coord);
|
|
133
|
+
if (apCap <= ap)
|
|
134
|
+
candidates.push({
|
|
135
|
+
action: { kind: 'reinforce', agentId: agent.id, tileKey: target.key, apCap },
|
|
136
|
+
risk: 'low',
|
|
137
|
+
reason: `Weakest owned tile has ${target.effectiveDefense} defense; add one defense point for up to ${apCap} cAP.`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (observation.marketRequest.state === 'available') {
|
|
142
|
+
candidates.push({ action: { kind: 'requestMarket' }, risk: 'low', reason: 'This round can request its permissionless market.' });
|
|
143
|
+
}
|
|
144
|
+
else if (observation.marketRequest.state === 'finalizable') {
|
|
145
|
+
candidates.push({ action: { kind: 'finalizeMarket', round: observation.marketRequest.round }, risk: 'low', reason: 'Market randomness is ready to finalize.' });
|
|
146
|
+
}
|
|
147
|
+
for (const market of observation.markets) {
|
|
148
|
+
if (market.status !== 'settled' && observation.round > market.settleRound) {
|
|
149
|
+
candidates.push({ action: { kind: 'settleMarket', marketId: market.id }, risk: 'low', reason: `Market ${market.id} is permissionlessly settleable.` });
|
|
150
|
+
}
|
|
151
|
+
const position = market.walletPosition;
|
|
152
|
+
if (market.status === 'settled' && position && !position.claimed
|
|
153
|
+
&& (market.refundable || position.optionIndex === market.winningOption)) {
|
|
154
|
+
candidates.push({ action: { kind: 'claimMarket', marketId: market.id }, risk: 'low', reason: `Market ${market.id} has a claimable wallet position.` });
|
|
155
|
+
}
|
|
156
|
+
if (market.status === 'open' && market.writeState === 'current_writable' && !position
|
|
157
|
+
&& policy.allowedActions.includes('bet') && policy.maxGBetPerAction >= protocolConfig.minBetG
|
|
158
|
+
&& policy.maxGSpendPerDay >= protocolConfig.minBetG && observation.walletGBalance >= protocolConfig.minBetG) {
|
|
159
|
+
const option = [...market.options].sort((left, right) => left.poolG < right.poolG ? -1 : left.poolG > right.poolG ? 1 : Number(left.index - right.index))[0];
|
|
160
|
+
if (option)
|
|
161
|
+
candidates.push({
|
|
162
|
+
action: { kind: 'bet', marketId: market.id, agentId: agent.id, optionIndex: option.index, amountG: protocolConfig.minBetG },
|
|
163
|
+
risk: 'medium',
|
|
164
|
+
reason: `Smallest-pool option ${option.index} is available at the minimum configured stake.`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return this.catalog.issue(observation, candidates.filter((candidate) => policy.allowedActions.includes(candidate.action.kind)));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=legal-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legal-actions.js","sourceRoot":"","sources":["../../src/runner/legal-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Address, Observation } from '../domain.js';
|
|
2
|
+
import type { AgentIndexerClient } from '../data/indexer-client.js';
|
|
3
|
+
import type { AgentRpcReader } from '../data/rpc-reader.js';
|
|
4
|
+
export declare class ObservationService {
|
|
5
|
+
private readonly chainId;
|
|
6
|
+
private readonly wallet;
|
|
7
|
+
private readonly radius;
|
|
8
|
+
private readonly indexer;
|
|
9
|
+
private readonly rpc;
|
|
10
|
+
constructor(chainId: number, wallet: Address, radius: number, indexer: AgentIndexerClient, rpc: AgentRpcReader);
|
|
11
|
+
observe(agentId: bigint): Promise<Observation>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AgentError } from '../errors.js';
|
|
2
|
+
export class ObservationService {
|
|
3
|
+
chainId;
|
|
4
|
+
wallet;
|
|
5
|
+
radius;
|
|
6
|
+
indexer;
|
|
7
|
+
rpc;
|
|
8
|
+
constructor(chainId, wallet, radius, indexer, rpc) {
|
|
9
|
+
this.chainId = chainId;
|
|
10
|
+
this.wallet = wallet;
|
|
11
|
+
this.radius = radius;
|
|
12
|
+
this.indexer = indexer;
|
|
13
|
+
this.rpc = rpc;
|
|
14
|
+
}
|
|
15
|
+
async observe(agentId) {
|
|
16
|
+
await this.rpc.assertNetwork();
|
|
17
|
+
const [indexedAgent, strong] = await Promise.all([this.indexer.getAgent(agentId), this.rpc.agent(agentId)]);
|
|
18
|
+
if (strong.owner !== this.wallet || indexedAgent.owner !== this.wallet) {
|
|
19
|
+
throw new AgentError('not_agent_owner', `Wallet ${this.wallet} does not own Agent ${agentId}`);
|
|
20
|
+
}
|
|
21
|
+
if (!strong.active || !strong.position)
|
|
22
|
+
throw new AgentError('agent_pending', `Agent ${agentId} is not active`);
|
|
23
|
+
const [world, asset] = await Promise.all([
|
|
24
|
+
this.indexer.world(strong.position, this.radius),
|
|
25
|
+
this.rpc.walletAsset(this.wallet),
|
|
26
|
+
]);
|
|
27
|
+
const [markets, marketRequest] = await Promise.all([
|
|
28
|
+
this.indexer.markets(this.wallet),
|
|
29
|
+
this.indexer.marketRequest(strong.currentRound),
|
|
30
|
+
]);
|
|
31
|
+
return {
|
|
32
|
+
chainId: this.chainId,
|
|
33
|
+
wallet: this.wallet,
|
|
34
|
+
round: strong.currentRound,
|
|
35
|
+
asOfBlock: world.asOfBlock,
|
|
36
|
+
sync: world.sync,
|
|
37
|
+
walletGBalance: asset.balance,
|
|
38
|
+
agent: { ...indexedAgent, ...strong, id: agentId, owner: this.wallet, lifecycle: 'active', position: strong.position },
|
|
39
|
+
agents: world.agents,
|
|
40
|
+
tiles: world.tiles,
|
|
41
|
+
fragments: world.fragments,
|
|
42
|
+
markets,
|
|
43
|
+
marketRequest,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=observation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observation.js","sourceRoot":"","sources":["../../src/runner/observation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI1C,MAAM,OAAO,kBAAkB;IAEV,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,GAAG;IALtB,YACmB,OAAe,EACf,MAAe,EACf,MAAc,EACd,OAA2B,EAC3B,GAAmB;uBAJnB,OAAO;sBACP,MAAM;sBACN,MAAM;uBACN,OAAO;mBACP,GAAG;IACnB,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC/B,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5G,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YACvE,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,UAAU,IAAI,CAAC,MAAM,uBAAuB,OAAO,EAAE,CAAC,CAAC;QACjG,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,eAAe,EAAE,SAAS,OAAO,gBAAgB,CAAC,CAAC;QAChH,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;YAChD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;SAClC,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC;SAChD,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,MAAM,CAAC,YAAY;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,cAAc,EAAE,KAAK,CAAC,OAAO;YAC7B,KAAK,EAAE,EAAE,GAAG,YAAY,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;YACtH,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO;YACP,aAAa;SACd,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LegalAction, Observation, RunnerPolicy } from '../domain.js';
|
|
2
|
+
import type { AuditLog } from './audit-store.js';
|
|
3
|
+
export declare class PolicyEngine {
|
|
4
|
+
private readonly policy;
|
|
5
|
+
private readonly audit;
|
|
6
|
+
private readonly now;
|
|
7
|
+
constructor(policy: RunnerPolicy, audit: AuditLog, now?: () => Date);
|
|
8
|
+
current(): RunnerPolicy;
|
|
9
|
+
assertAllowed(ticket: LegalAction, observation: Observation, automatic?: boolean): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { AgentError } from '../errors.js';
|
|
2
|
+
import { protocolConfig } from '../generated/protocol.js';
|
|
3
|
+
const RISK_SCORE = { low: 0, medium: 1, high: 2 };
|
|
4
|
+
function agentIdOf(action) {
|
|
5
|
+
return 'agentId' in action ? action.agentId : null;
|
|
6
|
+
}
|
|
7
|
+
function apCost(action) {
|
|
8
|
+
switch (action.kind) {
|
|
9
|
+
case 'claim':
|
|
10
|
+
case 'move':
|
|
11
|
+
return action.apCostCap;
|
|
12
|
+
case 'attack':
|
|
13
|
+
case 'reinforce':
|
|
14
|
+
return action.apCap;
|
|
15
|
+
default:
|
|
16
|
+
return 0n;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class PolicyEngine {
|
|
20
|
+
policy;
|
|
21
|
+
audit;
|
|
22
|
+
now;
|
|
23
|
+
constructor(policy, audit, now = () => new Date()) {
|
|
24
|
+
this.policy = policy;
|
|
25
|
+
this.audit = audit;
|
|
26
|
+
this.now = now;
|
|
27
|
+
}
|
|
28
|
+
current() {
|
|
29
|
+
return structuredClone(this.policy);
|
|
30
|
+
}
|
|
31
|
+
async assertAllowed(ticket, observation, automatic = true) {
|
|
32
|
+
const action = ticket.action;
|
|
33
|
+
if (!this.policy.allowedActions.includes(action.kind)) {
|
|
34
|
+
throw new AgentError('policy_denied', `${action.kind} is not allowed by Runner policy`);
|
|
35
|
+
}
|
|
36
|
+
if (automatic && RISK_SCORE[ticket.risk] > RISK_SCORE[this.policy.autoExecuteRisk]) {
|
|
37
|
+
throw new AgentError('policy_denied', `${ticket.risk} risk exceeds ${this.policy.autoExecuteRisk} auto-execution policy`);
|
|
38
|
+
}
|
|
39
|
+
const agentId = agentIdOf(action);
|
|
40
|
+
if (agentId != null && this.policy.allowedAgentIds.length > 0 && !this.policy.allowedAgentIds.includes(agentId)) {
|
|
41
|
+
throw new AgentError('policy_denied', `Agent ${agentId} is not in the allowed Agent list`);
|
|
42
|
+
}
|
|
43
|
+
const cost = apCost(action);
|
|
44
|
+
const roundLimit = this.policy.maxApPerRoundCap < protocolConfig.apRoundSpendCap
|
|
45
|
+
? this.policy.maxApPerRoundCap
|
|
46
|
+
: protocolConfig.apRoundSpendCap;
|
|
47
|
+
if (observation.agent.spentThisRoundCap + cost > roundLimit) {
|
|
48
|
+
throw new AgentError('policy_denied', `Action would exceed the ${roundLimit} cAP per-round policy`);
|
|
49
|
+
}
|
|
50
|
+
if (cost > observation.agent.apBalanceCap)
|
|
51
|
+
throw new AgentError('policy_denied', 'Action would exceed the Agent AP balance');
|
|
52
|
+
if (await this.audit.transactionCount(observation.round) >= this.policy.maxTransactionsPerRound) {
|
|
53
|
+
throw new AgentError('policy_denied', 'Per-round transaction limit is exhausted');
|
|
54
|
+
}
|
|
55
|
+
if (action.kind === 'bet') {
|
|
56
|
+
if (action.amountG > this.policy.maxGBetPerAction)
|
|
57
|
+
throw new AgentError('policy_denied', 'Bet exceeds the per-action G limit');
|
|
58
|
+
const since = new Date(this.now());
|
|
59
|
+
since.setUTCHours(0, 0, 0, 0);
|
|
60
|
+
const spent = await this.audit.gSpentSince(since.toISOString());
|
|
61
|
+
if (spent + action.amountG > this.policy.maxGSpendPerDay)
|
|
62
|
+
throw new AgentError('policy_denied', 'Bet exceeds the daily G limit');
|
|
63
|
+
if (action.amountG > observation.walletGBalance)
|
|
64
|
+
throw new AgentError('policy_denied', 'Bet exceeds wallet G balance');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/runner/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AgentConfig } from '../config.js';
|
|
2
|
+
import type { Address, AuditEvent, LegalAction, Observation, RunnerPolicy, TransactionResult } from '../domain.js';
|
|
3
|
+
import type { AgentIndexerClient } from '../data/indexer-client.js';
|
|
4
|
+
import type { AgentRpcReader } from '../data/rpc-reader.js';
|
|
5
|
+
import type { AuditLog } from './audit-store.js';
|
|
6
|
+
import type { TransactionExecutor } from './transaction-executor.js';
|
|
7
|
+
export type RunnerStatus = {
|
|
8
|
+
state: 'not_joined' | 'pending' | 'ready' | 'running' | 'paused';
|
|
9
|
+
chainId: number;
|
|
10
|
+
wallet: Address;
|
|
11
|
+
agentId: bigint | null;
|
|
12
|
+
blockNumber: bigint;
|
|
13
|
+
sync?: Observation['sync'];
|
|
14
|
+
};
|
|
15
|
+
export type JoinResult = {
|
|
16
|
+
wallet: Address;
|
|
17
|
+
agentId: bigint;
|
|
18
|
+
lifecycle: 'active';
|
|
19
|
+
position: {
|
|
20
|
+
q: number;
|
|
21
|
+
r: number;
|
|
22
|
+
};
|
|
23
|
+
transactionHashes: string[];
|
|
24
|
+
};
|
|
25
|
+
export type RuntimeStateStore = {
|
|
26
|
+
paused(): Promise<boolean>;
|
|
27
|
+
setPaused(paused: boolean): Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
export declare class MemoryRuntimeState implements RuntimeStateStore {
|
|
30
|
+
private value;
|
|
31
|
+
paused(): Promise<boolean>;
|
|
32
|
+
setPaused(paused: boolean): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export declare class AgentRuntime {
|
|
35
|
+
readonly wallet: Address;
|
|
36
|
+
private readonly indexer;
|
|
37
|
+
private readonly rpc;
|
|
38
|
+
private readonly executor;
|
|
39
|
+
private readonly audit;
|
|
40
|
+
private readonly state;
|
|
41
|
+
private readonly persistConfig;
|
|
42
|
+
private readonly sleep;
|
|
43
|
+
private config;
|
|
44
|
+
private policy;
|
|
45
|
+
private readonly catalog;
|
|
46
|
+
private readonly planner;
|
|
47
|
+
private readonly observation;
|
|
48
|
+
private loop;
|
|
49
|
+
private loopInFlight;
|
|
50
|
+
private consecutiveLoopFailures;
|
|
51
|
+
constructor(config: AgentConfig, wallet: Address, indexer: AgentIndexerClient, rpc: AgentRpcReader, executor: TransactionExecutor, audit: AuditLog, state?: RuntimeStateStore, persistConfig?: (config: AgentConfig) => Promise<void>, sleep?: (milliseconds: number) => Promise<void>);
|
|
52
|
+
status(): Promise<RunnerStatus>;
|
|
53
|
+
private selectAgent;
|
|
54
|
+
private waitForBlock;
|
|
55
|
+
join(): Promise<JoinResult>;
|
|
56
|
+
private requireAgentId;
|
|
57
|
+
observe(): Promise<Observation>;
|
|
58
|
+
getLegalActions(): Promise<LegalAction[]>;
|
|
59
|
+
private assertIndexerUsable;
|
|
60
|
+
simulateAction(actionId: string): Promise<{
|
|
61
|
+
action: LegalAction;
|
|
62
|
+
steps: string[];
|
|
63
|
+
deferredUntilApproval: boolean;
|
|
64
|
+
}>;
|
|
65
|
+
executeAction(actionId: string): Promise<TransactionResult>;
|
|
66
|
+
getPolicy(): RunnerPolicy;
|
|
67
|
+
setPolicy(next: RunnerPolicy): Promise<RunnerPolicy>;
|
|
68
|
+
activity(limit?: number): Promise<AuditEvent[]>;
|
|
69
|
+
runOnce(): Promise<TransactionResult | null>;
|
|
70
|
+
start(): Promise<RunnerStatus>;
|
|
71
|
+
pause(): Promise<RunnerStatus>;
|
|
72
|
+
private scheduleLoop;
|
|
73
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { parseConfig, saveConfig } from '../config.js';
|
|
2
|
+
import { AgentError, sanitizedError } from '../errors.js';
|
|
3
|
+
import { stringifyJson } from '../json.js';
|
|
4
|
+
import { ActionCatalog, LegalActionPlanner } from './legal-actions.js';
|
|
5
|
+
import { ObservationService } from './observation.js';
|
|
6
|
+
import { PolicyEngine } from './policy.js';
|
|
7
|
+
import { conservativeAction } from './strategy.js';
|
|
8
|
+
export class MemoryRuntimeState {
|
|
9
|
+
value = true;
|
|
10
|
+
async paused() { return this.value; }
|
|
11
|
+
async setPaused(paused) { this.value = paused; }
|
|
12
|
+
}
|
|
13
|
+
export class AgentRuntime {
|
|
14
|
+
wallet;
|
|
15
|
+
indexer;
|
|
16
|
+
rpc;
|
|
17
|
+
executor;
|
|
18
|
+
audit;
|
|
19
|
+
state;
|
|
20
|
+
persistConfig;
|
|
21
|
+
sleep;
|
|
22
|
+
config;
|
|
23
|
+
policy;
|
|
24
|
+
catalog;
|
|
25
|
+
planner;
|
|
26
|
+
observation;
|
|
27
|
+
loop = null;
|
|
28
|
+
loopInFlight = false;
|
|
29
|
+
consecutiveLoopFailures = 0;
|
|
30
|
+
constructor(config, wallet, indexer, rpc, executor, audit, state = new MemoryRuntimeState(), persistConfig = saveConfig, sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))) {
|
|
31
|
+
this.wallet = wallet;
|
|
32
|
+
this.indexer = indexer;
|
|
33
|
+
this.rpc = rpc;
|
|
34
|
+
this.executor = executor;
|
|
35
|
+
this.audit = audit;
|
|
36
|
+
this.state = state;
|
|
37
|
+
this.persistConfig = persistConfig;
|
|
38
|
+
this.sleep = sleep;
|
|
39
|
+
this.config = structuredClone(config);
|
|
40
|
+
this.policy = new PolicyEngine(this.config.policy, audit);
|
|
41
|
+
this.catalog = new ActionCatalog(this.config.runner.actionTtlMs);
|
|
42
|
+
this.planner = new LegalActionPlanner(this.catalog);
|
|
43
|
+
this.observation = new ObservationService(config.network.chainId, wallet, config.runner.mapRadius, indexer, rpc);
|
|
44
|
+
}
|
|
45
|
+
async status() {
|
|
46
|
+
await this.rpc.assertNetwork();
|
|
47
|
+
const block = await this.rpc.block();
|
|
48
|
+
const agents = await this.indexer.walletAgents(this.wallet);
|
|
49
|
+
const selected = this.selectAgent(agents);
|
|
50
|
+
if (!selected)
|
|
51
|
+
return { state: 'not_joined', chainId: this.config.network.chainId, wallet: this.wallet, agentId: null, blockNumber: block.number };
|
|
52
|
+
if (selected.lifecycle === 'pending')
|
|
53
|
+
return { state: 'pending', chainId: this.config.network.chainId, wallet: this.wallet, agentId: selected.id, blockNumber: block.number };
|
|
54
|
+
const paused = await this.state.paused();
|
|
55
|
+
return {
|
|
56
|
+
state: this.loop && !paused ? 'running' : paused ? 'paused' : 'ready',
|
|
57
|
+
chainId: this.config.network.chainId,
|
|
58
|
+
wallet: this.wallet,
|
|
59
|
+
agentId: selected.id,
|
|
60
|
+
blockNumber: block.number,
|
|
61
|
+
sync: await this.indexer.status(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
selectAgent(agents) {
|
|
65
|
+
if (this.config.runner.agentId != null)
|
|
66
|
+
return agents.find((agent) => agent.id === this.config.runner.agentId);
|
|
67
|
+
return agents.find((agent) => agent.lifecycle === 'active') ?? agents[0];
|
|
68
|
+
}
|
|
69
|
+
async waitForBlock(target) {
|
|
70
|
+
while ((await this.rpc.block()).number <= target)
|
|
71
|
+
await this.sleep(this.config.runner.pollIntervalMs);
|
|
72
|
+
}
|
|
73
|
+
async join() {
|
|
74
|
+
await this.rpc.assertNetwork();
|
|
75
|
+
const hashes = [];
|
|
76
|
+
let agents = await this.indexer.walletAgents(this.wallet);
|
|
77
|
+
if (agents.length === 0) {
|
|
78
|
+
const created = await this.executor.execute({ kind: 'createAgent' });
|
|
79
|
+
hashes.push(...created.hashes);
|
|
80
|
+
agents = await this.indexer.walletAgents(this.wallet);
|
|
81
|
+
}
|
|
82
|
+
let selected = this.selectAgent(agents) ?? agents[0];
|
|
83
|
+
if (!selected)
|
|
84
|
+
throw new AgentError('agent_missing', 'Agent creation completed but Indexer returned no wallet Agent');
|
|
85
|
+
while (selected.lifecycle === 'pending') {
|
|
86
|
+
const pending = (await this.indexer.getAgent(selected.id)).pendingRequest;
|
|
87
|
+
if (!pending)
|
|
88
|
+
throw new AgentError('agent_pending', `Pending Agent ${selected.id} has no randomness request`);
|
|
89
|
+
if (pending.state === 'expired') {
|
|
90
|
+
const refreshed = await this.executor.execute({ kind: 'refreshSpawnRandomness', agentId: selected.id });
|
|
91
|
+
hashes.push(...refreshed.hashes);
|
|
92
|
+
}
|
|
93
|
+
else if (pending.state === 'waiting') {
|
|
94
|
+
await this.waitForBlock(pending.targetBlock);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const spawned = await this.executor.execute({ kind: 'spawnAgent', agentId: selected.id });
|
|
98
|
+
hashes.push(...spawned.hashes);
|
|
99
|
+
}
|
|
100
|
+
selected = await this.indexer.getAgent(selected.id);
|
|
101
|
+
}
|
|
102
|
+
this.config = { ...this.config, runner: { ...this.config.runner, agentId: selected.id } };
|
|
103
|
+
if (this.config.policy.allowedAgentIds.length === 0) {
|
|
104
|
+
this.config = { ...this.config, policy: { ...this.config.policy, allowedAgentIds: [selected.id] } };
|
|
105
|
+
this.policy = new PolicyEngine(this.config.policy, this.audit);
|
|
106
|
+
}
|
|
107
|
+
await this.persistConfig(this.config);
|
|
108
|
+
await this.audit.record({ type: 'lifecycle', action: undefined, status: 'joined', detail: { agentId: selected.id.toString() } });
|
|
109
|
+
const observed = await this.observation.observe(selected.id);
|
|
110
|
+
return { wallet: this.wallet, agentId: selected.id, lifecycle: 'active', position: observed.agent.position, transactionHashes: hashes };
|
|
111
|
+
}
|
|
112
|
+
requireAgentId() {
|
|
113
|
+
if (this.config.runner.agentId == null)
|
|
114
|
+
throw new AgentError('agent_missing', 'Runner has not joined GWorld');
|
|
115
|
+
return this.config.runner.agentId;
|
|
116
|
+
}
|
|
117
|
+
observe() {
|
|
118
|
+
return this.observation.observe(this.requireAgentId());
|
|
119
|
+
}
|
|
120
|
+
async getLegalActions() {
|
|
121
|
+
const observation = await this.observe();
|
|
122
|
+
this.assertIndexerUsable(observation);
|
|
123
|
+
const actions = this.planner.plan(observation, this.config.policy);
|
|
124
|
+
const allowed = [];
|
|
125
|
+
for (const action of actions) {
|
|
126
|
+
try {
|
|
127
|
+
await this.policy.assertAllowed(action, observation);
|
|
128
|
+
allowed.push(action);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (!(error instanceof AgentError) || error.code !== 'policy_denied')
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return allowed;
|
|
136
|
+
}
|
|
137
|
+
assertIndexerUsable(observation) {
|
|
138
|
+
const lag = observation.sync.chainHeadBlock > observation.sync.indexedBlock
|
|
139
|
+
? observation.sync.chainHeadBlock - observation.sync.indexedBlock
|
|
140
|
+
: 0n;
|
|
141
|
+
const ahead = observation.sync.indexedBlock > observation.sync.chainHeadBlock
|
|
142
|
+
? observation.sync.indexedBlock - observation.sync.chainHeadBlock
|
|
143
|
+
: 0n;
|
|
144
|
+
if (observation.sync.state === 'rebuilding' || lag > 1n || ahead > 0n) {
|
|
145
|
+
const distance = ahead > 0n ? `${ahead} block(s) ahead` : `${lag} block(s) behind`;
|
|
146
|
+
throw new AgentError('indexer_lagging', `Indexer is ${observation.sync.state} and ${distance}; writes are paused`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async simulateAction(actionId) {
|
|
150
|
+
const observation = await this.observe();
|
|
151
|
+
this.assertIndexerUsable(observation);
|
|
152
|
+
const ticket = this.catalog.resolve(actionId, observation);
|
|
153
|
+
await this.policy.assertAllowed(ticket, observation);
|
|
154
|
+
const simulation = await this.executor.simulate(ticket.action);
|
|
155
|
+
await this.audit.record({ type: 'simulation', actionId, action: ticket.action.kind, round: observation.round.toString(), status: 'passed' });
|
|
156
|
+
return { action: ticket, ...simulation };
|
|
157
|
+
}
|
|
158
|
+
async executeAction(actionId) {
|
|
159
|
+
const observation = await this.observe();
|
|
160
|
+
this.assertIndexerUsable(observation);
|
|
161
|
+
const ticket = this.catalog.resolve(actionId, observation);
|
|
162
|
+
await this.policy.assertAllowed(ticket, observation);
|
|
163
|
+
this.catalog.consume(actionId, observation);
|
|
164
|
+
try {
|
|
165
|
+
const result = await this.executor.execute(ticket.action);
|
|
166
|
+
await this.audit.record({
|
|
167
|
+
type: 'transaction', actionId, action: ticket.action.kind, round: observation.round.toString(),
|
|
168
|
+
amountG: ticket.action.kind === 'bet' ? ticket.action.amountG.toString() : undefined,
|
|
169
|
+
status: 'complete', detail: { hashes: result.hashes, blockNumber: result.blockNumber.toString(), indexing: result.indexing },
|
|
170
|
+
});
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
await this.audit.record({ type: 'error', actionId, action: ticket.action.kind, round: observation.round.toString(), status: 'failed' });
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
getPolicy() {
|
|
179
|
+
return this.policy.current();
|
|
180
|
+
}
|
|
181
|
+
async setPolicy(next) {
|
|
182
|
+
const validated = parseConfig(JSON.parse(stringifyJson({ ...this.config, policy: next })));
|
|
183
|
+
await this.persistConfig(validated);
|
|
184
|
+
this.config = validated;
|
|
185
|
+
this.policy = new PolicyEngine(validated.policy, this.audit);
|
|
186
|
+
await this.audit.record({ type: 'policy', status: 'updated' });
|
|
187
|
+
return this.getPolicy();
|
|
188
|
+
}
|
|
189
|
+
activity(limit) {
|
|
190
|
+
return this.audit.recent(limit);
|
|
191
|
+
}
|
|
192
|
+
async runOnce() {
|
|
193
|
+
if (await this.state.paused())
|
|
194
|
+
throw new AgentError('runner_paused', 'Runner is paused');
|
|
195
|
+
const action = conservativeAction(await this.getLegalActions());
|
|
196
|
+
return action ? this.executeAction(action.actionId) : null;
|
|
197
|
+
}
|
|
198
|
+
async start() {
|
|
199
|
+
await this.state.setPaused(false);
|
|
200
|
+
if (!this.loop)
|
|
201
|
+
this.scheduleLoop(0);
|
|
202
|
+
return this.status();
|
|
203
|
+
}
|
|
204
|
+
async pause() {
|
|
205
|
+
await this.state.setPaused(true);
|
|
206
|
+
if (this.loop)
|
|
207
|
+
clearTimeout(this.loop);
|
|
208
|
+
this.loop = null;
|
|
209
|
+
return this.status();
|
|
210
|
+
}
|
|
211
|
+
scheduleLoop(delay) {
|
|
212
|
+
this.loop = setTimeout(async () => {
|
|
213
|
+
if (this.loopInFlight || await this.state.paused())
|
|
214
|
+
return;
|
|
215
|
+
this.loopInFlight = true;
|
|
216
|
+
let nextDelay = this.config.runner.pollIntervalMs;
|
|
217
|
+
try {
|
|
218
|
+
await this.runOnce();
|
|
219
|
+
this.consecutiveLoopFailures = 0;
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
this.consecutiveLoopFailures += 1;
|
|
223
|
+
const safe = sanitizedError(error);
|
|
224
|
+
nextDelay = Math.min(this.config.runner.pollIntervalMs * (2 ** Math.min(this.consecutiveLoopFailures, 8)), 60_000);
|
|
225
|
+
await this.audit.record({ type: 'error', status: 'loop_failed', detail: { code: safe.code, message: safe.message, retryInMs: nextDelay } });
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
this.loopInFlight = false;
|
|
229
|
+
if (!(await this.state.paused()))
|
|
230
|
+
this.scheduleLoop(nextDelay);
|
|
231
|
+
}
|
|
232
|
+
}, delay);
|
|
233
|
+
this.loop.unref?.();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=runtime.js.map
|