@dimcool/dimclaw 0.1.19 → 0.1.21
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/dist/index.js +2364 -2279
- package/index.ts +151 -2281
- package/package.json +2 -9
- package/dim-client.ts +0 -214
package/index.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @dimcool/dimclaw — OpenClaw plugin for DIM.
|
|
3
|
-
*
|
|
3
|
+
* Thin adapter: imports tool definitions from dim-agent-core and registers them with OpenClaw.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import { Keypair
|
|
8
|
+
import { Keypair } from '@solana/web3.js';
|
|
9
9
|
import bs58 from 'bs58';
|
|
10
|
-
import
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
DimAgentClient,
|
|
12
|
+
TOOL_DEFINITIONS,
|
|
13
|
+
DIM_INSTRUCTIONS,
|
|
14
|
+
} from '@dimcool/dim-agent-core';
|
|
15
|
+
import type { ToolParam, BufferedEvent } from '@dimcool/dim-agent-core';
|
|
16
|
+
|
|
17
|
+
// ── Plugin config ────────────────────────────────────────────────────
|
|
12
18
|
|
|
13
19
|
type PluginConfig = {
|
|
14
20
|
walletPrivateKey?: string;
|
|
15
21
|
walletStorePath?: string;
|
|
16
22
|
apiUrl?: string;
|
|
17
23
|
heartbeatPath?: string;
|
|
24
|
+
referralCode?: string;
|
|
18
25
|
// Autonomy scopes
|
|
19
26
|
autoAcceptFriendRequests?: boolean;
|
|
20
27
|
autoReplyDms?: boolean;
|
|
@@ -34,13 +41,7 @@ function getPluginConfig(api: { config?: unknown }): PluginConfig | null {
|
|
|
34
41
|
return dimclawEntry?.config ?? null;
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
const expanded =
|
|
39
|
-
storePath.startsWith('~/') && process.env.HOME
|
|
40
|
-
? path.join(process.env.HOME, storePath.slice(2))
|
|
41
|
-
: storePath;
|
|
42
|
-
return path.resolve(expanded);
|
|
43
|
-
}
|
|
44
|
+
// ── Wallet resolution ────────────────────────────────────────────────
|
|
44
45
|
|
|
45
46
|
interface WalletRecord {
|
|
46
47
|
version: 1;
|
|
@@ -49,6 +50,14 @@ interface WalletRecord {
|
|
|
49
50
|
createdAt: string;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
function resolveStorePath(storePath: string): string {
|
|
54
|
+
const expanded =
|
|
55
|
+
storePath.startsWith('~/') && process.env.HOME
|
|
56
|
+
? path.join(process.env.HOME, storePath.slice(2))
|
|
57
|
+
: storePath;
|
|
58
|
+
return path.resolve(expanded);
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
async function readWalletFile(storePath: string): Promise<WalletRecord | null> {
|
|
53
62
|
try {
|
|
54
63
|
const raw = await readFile(storePath, 'utf8');
|
|
@@ -90,7 +99,6 @@ function createWalletRecord(): WalletRecord {
|
|
|
90
99
|
};
|
|
91
100
|
}
|
|
92
101
|
|
|
93
|
-
/** Resolve wallet private key from config: direct key, or read/create at store path. */
|
|
94
102
|
async function resolveWalletKey(config: PluginConfig): Promise<string | null> {
|
|
95
103
|
if (config.walletPrivateKey?.trim()) return config.walletPrivateKey.trim();
|
|
96
104
|
if (!config.walletStorePath?.trim()) return null;
|
|
@@ -102,200 +110,93 @@ async function resolveWalletKey(config: PluginConfig): Promise<string | null> {
|
|
|
102
110
|
return record.walletPrivateKey;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
const templates: Record<string, string> = {
|
|
107
|
-
openclaw: `# Connect to DIM — OpenClaw Setup
|
|
108
|
-
|
|
109
|
-
## Step 1: Install the DIM plugin
|
|
110
|
-
\`\`\`bash
|
|
111
|
-
openclaw plugins install @dimcool/dimclaw
|
|
112
|
-
\`\`\`
|
|
113
|
-
|
|
114
|
-
## Step 2: Configure
|
|
115
|
-
Add to your OpenClaw config under \`plugins.entries.dimclaw.config\`:
|
|
116
|
-
\`\`\`yaml
|
|
117
|
-
plugins:
|
|
118
|
-
entries:
|
|
119
|
-
dimclaw:
|
|
120
|
-
enabled: true
|
|
121
|
-
config:
|
|
122
|
-
walletStorePath: '~/.dim/openclaw-wallet.json'
|
|
123
|
-
\`\`\`
|
|
124
|
-
A wallet will be created automatically on first run.
|
|
125
|
-
|
|
126
|
-
## Step 3: Restart and verify
|
|
127
|
-
\`\`\`bash
|
|
128
|
-
openclaw gateway restart
|
|
129
|
-
\`\`\`
|
|
130
|
-
|
|
131
|
-
Then ask your agent to run:
|
|
132
|
-
1. \`dim_login\`
|
|
133
|
-
2. \`dim_apply_referral_code\` with referralCode: \`${referrer}\`
|
|
134
|
-
3. \`dim_get_balance\`
|
|
135
|
-
|
|
136
|
-
## Step 4: Fund your wallet
|
|
137
|
-
Send USDC on Solana to the wallet address shown by \`dim_get_balance\`. Even $5 is enough to start playing.
|
|
113
|
+
// ── Heartbeat (OpenClaw-specific) ────────────────────────────────────
|
|
138
114
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
## Step 1: Initialize wallet
|
|
144
|
-
\`\`\`bash
|
|
145
|
-
npx @dimcool/mcp init-wallet
|
|
146
|
-
\`\`\`
|
|
147
|
-
Copy the wallet store path from the output.
|
|
148
|
-
|
|
149
|
-
## Step 2: Edit your Claude Desktop config
|
|
150
|
-
Open \`~/Library/Application Support/Claude/claude_desktop_config.json\` (macOS) or \`%APPDATA%\\Claude\\claude_desktop_config.json\` (Windows).
|
|
151
|
-
|
|
152
|
-
Add this to the \`mcpServers\` section:
|
|
153
|
-
\`\`\`json
|
|
154
|
-
{
|
|
155
|
-
"mcpServers": {
|
|
156
|
-
"dim": {
|
|
157
|
-
"command": "npx",
|
|
158
|
-
"args": ["@dimcool/mcp"],
|
|
159
|
-
"env": {
|
|
160
|
-
"DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
|
|
161
|
-
"DIM_API_URL": "https://api.dim.cool",
|
|
162
|
-
"DIM_REFERRAL_CODE": "${referrer}"
|
|
163
|
-
}
|
|
164
|
-
}
|
|
115
|
+
function resolveHeartbeatPath(p: string): string {
|
|
116
|
+
if (p.startsWith('~/') && process.env.HOME) {
|
|
117
|
+
return path.join(process.env.HOME, p.slice(2));
|
|
165
118
|
}
|
|
119
|
+
return path.resolve(p);
|
|
166
120
|
}
|
|
167
|
-
\`\`\`
|
|
168
|
-
Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
|
|
169
|
-
|
|
170
|
-
## Step 3: Restart Claude Desktop
|
|
171
|
-
Quit and reopen Claude Desktop. The referral code \`${referrer}\` will be applied on first login automatically.
|
|
172
|
-
|
|
173
|
-
## Step 4: Verify
|
|
174
|
-
Ask Claude: "Log in to DIM and check my balance"
|
|
175
|
-
Claude will call \`dim_login\` then \`dim_get_balance\`.
|
|
176
121
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Copy the wallet store path from the output.
|
|
189
|
-
|
|
190
|
-
## Step 2: Edit your Cursor MCP config
|
|
191
|
-
Create or edit \`.cursor/mcp.json\` in your project root:
|
|
192
|
-
\`\`\`json
|
|
193
|
-
{
|
|
194
|
-
"mcpServers": {
|
|
195
|
-
"dim": {
|
|
196
|
-
"command": "npx",
|
|
197
|
-
"args": ["@dimcool/mcp"],
|
|
198
|
-
"env": {
|
|
199
|
-
"DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
|
|
200
|
-
"DIM_API_URL": "https://api.dim.cool",
|
|
201
|
-
"DIM_REFERRAL_CODE": "${referrer}"
|
|
202
|
-
}
|
|
122
|
+
function createHeartbeatWriter(heartbeatPath: string | undefined) {
|
|
123
|
+
if (!heartbeatPath) return undefined;
|
|
124
|
+
const filePath = resolveHeartbeatPath(heartbeatPath);
|
|
125
|
+
const pending: BufferedEvent[] = [];
|
|
126
|
+
|
|
127
|
+
return async (event: BufferedEvent) => {
|
|
128
|
+
pending.push(event);
|
|
129
|
+
const lines = ['# DIM Heartbeat', ''];
|
|
130
|
+
const eventTypes = new Set(pending.map((e) => e.event));
|
|
131
|
+
if (eventTypes.has('chat:message')) {
|
|
132
|
+
lines.push('- You have new DMs — call dim_check_notifications');
|
|
203
133
|
}
|
|
204
|
-
|
|
134
|
+
if (eventTypes.has('notification')) {
|
|
135
|
+
lines.push(
|
|
136
|
+
'- New notifications (challenges, friend requests, game results) — call dim_check_notifications',
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (eventTypes.has('lobby:matched') || eventTypes.has('lobby:invitation')) {
|
|
140
|
+
lines.push('- A game match is ready — call dim_get_pending_events');
|
|
141
|
+
}
|
|
142
|
+
if (eventTypes.has('game:turn')) {
|
|
143
|
+
lines.push("- It's your turn in a game — call dim_get_pending_events");
|
|
144
|
+
}
|
|
145
|
+
if (eventTypes.has('game:completed')) {
|
|
146
|
+
lines.push('- A game has completed — call dim_get_pending_events');
|
|
147
|
+
}
|
|
148
|
+
lines.push('');
|
|
149
|
+
try {
|
|
150
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
151
|
+
const tmp = `${filePath}.tmp`;
|
|
152
|
+
await writeFile(tmp, lines.join('\n'), 'utf8');
|
|
153
|
+
await rename(tmp, filePath);
|
|
154
|
+
} catch {
|
|
155
|
+
/* best-effort */
|
|
156
|
+
}
|
|
157
|
+
};
|
|
205
158
|
}
|
|
206
|
-
\`\`\`
|
|
207
|
-
Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
|
|
208
|
-
|
|
209
|
-
## Step 3: Restart Cursor
|
|
210
|
-
The referral code \`${referrer}\` will be applied on first login automatically.
|
|
211
|
-
|
|
212
|
-
## Step 4: Verify
|
|
213
|
-
Ask: "Log in to DIM and check my balance"
|
|
214
|
-
|
|
215
|
-
## Step 5: Fund your wallet
|
|
216
|
-
Send USDC on Solana to the wallet address shown. Even $5 is enough to start.`,
|
|
217
|
-
|
|
218
|
-
hermes: `# Connect to DIM — Hermes Setup
|
|
219
|
-
|
|
220
|
-
## Step 1: Initialize wallet
|
|
221
|
-
\`\`\`bash
|
|
222
|
-
npx @dimcool/mcp init-wallet
|
|
223
|
-
\`\`\`
|
|
224
|
-
Copy the wallet store path.
|
|
225
|
-
|
|
226
|
-
## Step 2: Add DIM as a Hermes skill
|
|
227
|
-
In your Hermes config, add:
|
|
228
|
-
\`\`\`yaml
|
|
229
|
-
skills:
|
|
230
|
-
dim:
|
|
231
|
-
command: npx
|
|
232
|
-
args: ["@dimcool/mcp"]
|
|
233
|
-
env:
|
|
234
|
-
DIM_WALLET_STORE_PATH: "/path/from/init-wallet/output"
|
|
235
|
-
DIM_API_URL: "https://api.dim.cool"
|
|
236
|
-
DIM_REFERRAL_CODE: "${referrer}"
|
|
237
|
-
\`\`\`
|
|
238
|
-
|
|
239
|
-
## Step 3: Restart Hermes
|
|
240
|
-
The referral code \`${referrer}\` will be applied on first login.
|
|
241
|
-
|
|
242
|
-
## Step 4: Verify
|
|
243
|
-
Call \`dim_login\` then \`dim_get_balance\`.
|
|
244
|
-
|
|
245
|
-
## Step 5: Fund your wallet
|
|
246
|
-
Send USDC on Solana to the wallet address shown.`,
|
|
247
|
-
|
|
248
|
-
'node-sdk': `# Connect to DIM — Node.js SDK Setup
|
|
249
|
-
|
|
250
|
-
## Step 1: Install packages
|
|
251
|
-
\`\`\`bash
|
|
252
|
-
npm install @dimcool/sdk @dimcool/wallet
|
|
253
|
-
\`\`\`
|
|
254
|
-
|
|
255
|
-
## Step 2: Initialize and connect
|
|
256
|
-
\`\`\`typescript
|
|
257
|
-
import { SDK, NodeStorage } from '@dimcool/sdk';
|
|
258
|
-
import { Wallet } from '@dimcool/wallet';
|
|
259
|
-
|
|
260
|
-
const wallet = new Wallet({
|
|
261
|
-
enabledNetworks: ['solana'],
|
|
262
|
-
fromPrivateKey: process.env.DIM_WALLET_PRIVATE_KEY!,
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const sdk = new SDK({
|
|
266
|
-
appId: 'dim-agents',
|
|
267
|
-
baseUrl: 'https://api.dim.cool',
|
|
268
|
-
storage: new NodeStorage(),
|
|
269
|
-
autoPay: { enabled: true, maxAmountMinor: 20_000 },
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
sdk.wallet.setSigner(wallet.getSigner());
|
|
273
159
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
160
|
+
// ── ToolParam → JSON Schema ─────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
function paramToJsonSchema(p: ToolParam): Record<string, unknown> {
|
|
163
|
+
const schema: Record<string, unknown> = { description: p.description };
|
|
164
|
+
if (p.enum) {
|
|
165
|
+
schema.type = 'string';
|
|
166
|
+
schema.enum = p.enum;
|
|
167
|
+
} else if (p.type === 'number') {
|
|
168
|
+
schema.type = 'number';
|
|
169
|
+
if (p.min != null) schema.minimum = p.min;
|
|
170
|
+
if (p.max != null) schema.maximum = p.max;
|
|
171
|
+
} else if (p.type === 'object') {
|
|
172
|
+
schema.type = 'object';
|
|
173
|
+
} else {
|
|
174
|
+
schema.type = 'string';
|
|
175
|
+
if (p.min != null) schema.minLength = p.min;
|
|
176
|
+
if (p.max != null) schema.maxLength = p.max;
|
|
177
|
+
}
|
|
178
|
+
return schema;
|
|
179
|
+
}
|
|
291
180
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
181
|
+
function buildJsonSchema(
|
|
182
|
+
params: Record<string, ToolParam>,
|
|
183
|
+
): Record<string, unknown> {
|
|
184
|
+
const properties: Record<string, unknown> = {};
|
|
185
|
+
const required: string[] = [];
|
|
186
|
+
for (const [key, param] of Object.entries(params)) {
|
|
187
|
+
properties[key] = paramToJsonSchema(param);
|
|
188
|
+
if (param.required) required.push(key);
|
|
295
189
|
}
|
|
296
|
-
return
|
|
190
|
+
return {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties,
|
|
193
|
+
...(required.length > 0 ? { required } : {}),
|
|
194
|
+
additionalProperties: false,
|
|
195
|
+
};
|
|
297
196
|
}
|
|
298
197
|
|
|
198
|
+
// ── OpenClaw response helper ─────────────────────────────────────────
|
|
199
|
+
|
|
299
200
|
function textResult(
|
|
300
201
|
text: string,
|
|
301
202
|
isError = false,
|
|
@@ -306,6 +207,8 @@ function textResult(
|
|
|
306
207
|
};
|
|
307
208
|
}
|
|
308
209
|
|
|
210
|
+
// ── Plugin entry point ───────────────────────────────────────────────
|
|
211
|
+
|
|
309
212
|
export default function register(api: {
|
|
310
213
|
config?: unknown;
|
|
311
214
|
registerTool: (tool: {
|
|
@@ -321,45 +224,36 @@ export default function register(api: {
|
|
|
321
224
|
}>;
|
|
322
225
|
}) => void;
|
|
323
226
|
}) {
|
|
324
|
-
let client:
|
|
227
|
+
let client: DimAgentClient | null = null;
|
|
325
228
|
let pluginConfig: PluginConfig | null = null;
|
|
326
229
|
|
|
327
|
-
function
|
|
328
|
-
c: DimClient,
|
|
329
|
-
amountDollars: number,
|
|
330
|
-
isGameBet = false,
|
|
331
|
-
): string | null {
|
|
332
|
-
if (!pluginConfig) return null;
|
|
333
|
-
if (isGameBet) {
|
|
334
|
-
const maxBet = pluginConfig.maxBetPerGame ?? 1.0;
|
|
335
|
-
if (amountDollars > maxBet) {
|
|
336
|
-
return `Bet $${amountDollars.toFixed(2)} exceeds maxBetPerGame limit of $${maxBet.toFixed(2)}. Ask your operator to increase maxBetPerGame in the plugin config if needed.`;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
const limit = pluginConfig.dailySpendLimit ?? 20.0;
|
|
340
|
-
const projected = c.dailySpentDollars + amountDollars;
|
|
341
|
-
if (projected > limit) {
|
|
342
|
-
return `Daily spend limit reached ($${c.dailySpentDollars.toFixed(2)} spent of $${limit.toFixed(2)} limit). This action would cost $${amountDollars.toFixed(2)}. Ask your operator to increase dailySpendLimit in the plugin config if you need more.`;
|
|
343
|
-
}
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
async function getClient(): Promise<DimClient | null> {
|
|
230
|
+
async function getClient(): Promise<DimAgentClient | null> {
|
|
348
231
|
if (client) return client;
|
|
349
232
|
pluginConfig = getPluginConfig(api);
|
|
350
233
|
if (!pluginConfig) return null;
|
|
351
234
|
const walletPrivateKey = await resolveWalletKey(pluginConfig);
|
|
352
235
|
if (!walletPrivateKey) return null;
|
|
353
|
-
|
|
236
|
+
const heartbeatWriter = createHeartbeatWriter(pluginConfig.heartbeatPath);
|
|
237
|
+
client = new DimAgentClient({
|
|
354
238
|
walletPrivateKey,
|
|
355
239
|
apiUrl: pluginConfig.apiUrl,
|
|
356
|
-
|
|
240
|
+
referralCode: pluginConfig.referralCode,
|
|
241
|
+
agentConfig: {
|
|
242
|
+
autoAcceptFriendRequests: pluginConfig.autoAcceptFriendRequests,
|
|
243
|
+
autoReplyDms: pluginConfig.autoReplyDms,
|
|
244
|
+
autoPlayGames: pluginConfig.autoPlayGames,
|
|
245
|
+
maxBetPerGame: pluginConfig.maxBetPerGame,
|
|
246
|
+
dailySpendLimit: pluginConfig.dailySpendLimit,
|
|
247
|
+
autoJoinGlobalChat: pluginConfig.autoJoinGlobalChat,
|
|
248
|
+
autoPromoteReferrals: pluginConfig.autoPromoteReferrals,
|
|
249
|
+
},
|
|
250
|
+
onEvent: heartbeatWriter,
|
|
357
251
|
});
|
|
358
252
|
return client;
|
|
359
253
|
}
|
|
360
254
|
|
|
361
255
|
async function requireClient(): Promise<
|
|
362
|
-
|
|
256
|
+
DimAgentClient | { error: ReturnType<typeof textResult> }
|
|
363
257
|
> {
|
|
364
258
|
const c = await getClient();
|
|
365
259
|
if (c) return c;
|
|
@@ -371,1962 +265,57 @@ export default function register(api: {
|
|
|
371
265
|
};
|
|
372
266
|
}
|
|
373
267
|
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
try {
|
|
384
|
-
const result = await c.authenticate();
|
|
385
|
-
c.startEventListeners();
|
|
386
|
-
const nextSteps: string[] = [];
|
|
387
|
-
if (result.username == null || result.username === '') {
|
|
388
|
-
nextSteps.push(
|
|
389
|
-
'No username set — call dim_set_username to claim one',
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
nextSteps.push('Check your balance with dim_get_balance');
|
|
393
|
-
nextSteps.push('Explore available games with dim_list_games');
|
|
268
|
+
// Register all tools from the core registry
|
|
269
|
+
for (const tool of TOOL_DEFINITIONS) {
|
|
270
|
+
api.registerTool({
|
|
271
|
+
name: tool.name,
|
|
272
|
+
description: tool.description,
|
|
273
|
+
parameters: buildJsonSchema(tool.params),
|
|
274
|
+
async execute(_id, params) {
|
|
275
|
+
const c = await requireClient();
|
|
276
|
+
if ('error' in c) return c.error;
|
|
394
277
|
try {
|
|
395
|
-
const
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
'No referrer yet — call dim_apply_referral_code for 10% fee discount',
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
if (result.username) {
|
|
402
|
-
nextSteps.push(
|
|
403
|
-
`Share your referral code "${result.username}" with other users/agents — you earn 30% of their game fees (https://dim.cool/?ref=${result.username})`,
|
|
404
|
-
);
|
|
278
|
+
const result = await tool.execute(c, params);
|
|
279
|
+
if (result.isError || result.error) {
|
|
280
|
+
return textResult(result.error || 'Unknown error', true);
|
|
405
281
|
}
|
|
406
|
-
} catch {
|
|
407
|
-
/* non-critical */
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const booleanExists = (value: boolean | undefined, defaultValue: boolean) => typeof value === 'boolean' ? value : defaultValue;
|
|
411
|
-
const response: Record<string, unknown> = {
|
|
412
|
-
success: true,
|
|
413
|
-
userId: result.userId,
|
|
414
|
-
username: result.username ?? null,
|
|
415
|
-
walletAddress: c.walletAddress,
|
|
416
|
-
agentConfig: {
|
|
417
|
-
autoAcceptFriendRequests: booleanExists(
|
|
418
|
-
pluginConfig?.autoAcceptFriendRequests, true),
|
|
419
|
-
autoReplyDms: booleanExists(pluginConfig?.autoReplyDms, true),
|
|
420
|
-
autoPlayGames: booleanExists(pluginConfig?.autoPlayGames, true),
|
|
421
|
-
maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1.0,
|
|
422
|
-
dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20.0,
|
|
423
|
-
autoJoinGlobalChat: booleanExists(pluginConfig?.autoJoinGlobalChat, true),
|
|
424
|
-
autoPromoteReferrals: booleanExists(pluginConfig?.autoPromoteReferrals, true),
|
|
425
|
-
},
|
|
426
|
-
nextSteps,
|
|
427
|
-
};
|
|
428
|
-
return textResult(JSON.stringify(response, null, 2));
|
|
429
|
-
} catch (err) {
|
|
430
|
-
return textResult(
|
|
431
|
-
`Authentication failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
432
|
-
true,
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
api.registerTool({
|
|
439
|
-
name: 'dim_get_profile',
|
|
440
|
-
description:
|
|
441
|
-
'Get the current authenticated user profile including username, avatar, bio, and chess ELO rating.',
|
|
442
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
443
|
-
async execute() {
|
|
444
|
-
const c = await requireClient();
|
|
445
|
-
if ('error' in c) return c.error;
|
|
446
|
-
try {
|
|
447
|
-
if (!c.currentUserId)
|
|
448
|
-
return textResult('Not authenticated. Call dim_login first.', true);
|
|
449
|
-
const user = await c.sdk.users.getUserById(c.currentUserId);
|
|
450
|
-
return textResult(JSON.stringify(user, null, 2));
|
|
451
|
-
} catch (err) {
|
|
452
|
-
return textResult(
|
|
453
|
-
`Failed to get profile: ${err instanceof Error ? err.message : String(err)}`,
|
|
454
|
-
true,
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
},
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
api.registerTool({
|
|
461
|
-
name: 'dim_set_username',
|
|
462
|
-
description:
|
|
463
|
-
'Set or update the username for the authenticated user. Username must be alphanumeric, 3-20 characters.',
|
|
464
|
-
parameters: {
|
|
465
|
-
type: 'object',
|
|
466
|
-
properties: {
|
|
467
|
-
username: { type: 'string', description: 'The desired username' },
|
|
468
|
-
},
|
|
469
|
-
required: ['username'],
|
|
470
|
-
additionalProperties: false,
|
|
471
|
-
},
|
|
472
|
-
async execute(_, params) {
|
|
473
|
-
const c = await requireClient();
|
|
474
|
-
if ('error' in c) return c.error;
|
|
475
|
-
const username = String(params.username ?? '');
|
|
476
|
-
try {
|
|
477
|
-
const { available, valid } =
|
|
478
|
-
await c.sdk.users.isUsernameAvailable(username);
|
|
479
|
-
if (!valid)
|
|
480
282
|
return textResult(
|
|
481
|
-
|
|
283
|
+
typeof result.data === 'string'
|
|
284
|
+
? result.data
|
|
285
|
+
: JSON.stringify(result.data, null, 2),
|
|
286
|
+
);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
return textResult(
|
|
289
|
+
`${tool.name} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
482
290
|
true,
|
|
483
291
|
);
|
|
484
|
-
if (!available)
|
|
485
|
-
return textResult(`Username "${username}" is already taken.`, true);
|
|
486
|
-
const user = await c.sdk.users.updateUsername(username);
|
|
487
|
-
return textResult(
|
|
488
|
-
JSON.stringify({ success: true, username: user.username }, null, 2),
|
|
489
|
-
);
|
|
490
|
-
} catch (err) {
|
|
491
|
-
return textResult(
|
|
492
|
-
`Failed to set username: ${err instanceof Error ? err.message : String(err)}`,
|
|
493
|
-
true,
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
},
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
// --- Instructions (discovery) ---
|
|
500
|
-
const DIM_INSTRUCTIONS = [
|
|
501
|
-
{
|
|
502
|
-
name: 'dim_login',
|
|
503
|
-
description:
|
|
504
|
-
'Authenticate with DIM. Call first; then set username, check balance, list games, or apply referral code.',
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
name: 'dim_get_profile',
|
|
508
|
-
description:
|
|
509
|
-
'Get current user profile (username, avatar, bio, chess ELO).',
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
name: 'dim_set_username',
|
|
513
|
-
description: 'Set or update your username (alphanumeric, 3–20 chars).',
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
name: 'dim_list_instructions',
|
|
517
|
-
description:
|
|
518
|
-
'List all available DIM tools and short descriptions (this list).',
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
name: 'dim_get_balance',
|
|
522
|
-
description:
|
|
523
|
-
'Get SOL and USDC balance; usdcFormatted is always present (e.g. $0.00).',
|
|
524
|
-
},
|
|
525
|
-
{
|
|
526
|
-
name: 'dim_send_usdc',
|
|
527
|
-
description:
|
|
528
|
-
'Send USDC to a user by username or address. 1¢ fee; amount in dollars.',
|
|
529
|
-
},
|
|
530
|
-
{
|
|
531
|
-
name: 'dim_tip_user',
|
|
532
|
-
description: 'Tip a user with USDC; broadcast to global chat. 1¢ fee.',
|
|
533
|
-
},
|
|
534
|
-
{
|
|
535
|
-
name: 'dim_get_wallet_activity',
|
|
536
|
-
description: 'Get recent wallet activity (deposits, payouts, transfers).',
|
|
537
|
-
},
|
|
538
|
-
{ name: 'dim_search_users', description: 'Search users by username.' },
|
|
539
|
-
{
|
|
540
|
-
name: 'dim_send_friend_request',
|
|
541
|
-
description: 'Send friend request by user ID.',
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
name: 'dim_accept_friend_request',
|
|
545
|
-
description: 'Accept an incoming friend request.',
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
name: 'dim_list_friends',
|
|
549
|
-
description: 'List your friends with pagination.',
|
|
550
|
-
},
|
|
551
|
-
{
|
|
552
|
-
name: 'dim_get_incoming_friend_requests',
|
|
553
|
-
description: 'List pending incoming friend requests.',
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
name: 'dim_send_message',
|
|
557
|
-
description: 'Send chat in a context (lobby, game, dm, global).',
|
|
558
|
-
},
|
|
559
|
-
{
|
|
560
|
-
name: 'dim_get_chat_history',
|
|
561
|
-
description: 'Get chat history for a context.',
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
name: 'dim_send_dm',
|
|
565
|
-
description: 'Send a direct message to a user by ID.',
|
|
566
|
-
},
|
|
567
|
-
{
|
|
568
|
-
name: 'dim_list_dm_threads',
|
|
569
|
-
description: 'List DM threads with last message and unread counts.',
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
name: 'dim_list_games',
|
|
573
|
-
description: 'List available game types (RPS, Chess, Tic-Tac-Toe, etc.).',
|
|
574
|
-
},
|
|
575
|
-
{
|
|
576
|
-
name: 'dim_get_game_metrics',
|
|
577
|
-
description:
|
|
578
|
-
'Get real-time metrics: active players, live games, money in play.',
|
|
579
|
-
},
|
|
580
|
-
{
|
|
581
|
-
name: 'dim_create_lobby',
|
|
582
|
-
description: 'Create a game lobby; bet in USDC dollars.',
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
name: 'dim_join_queue',
|
|
586
|
-
description:
|
|
587
|
-
'Join matchmaking queue. If not matched immediately, poll dim_get_lobby until gameId appears.',
|
|
588
|
-
},
|
|
589
|
-
{ name: 'dim_get_lobby', description: 'Get lobby details by ID.' },
|
|
590
|
-
{
|
|
591
|
-
name: 'dim_get_game_state',
|
|
592
|
-
description: 'Get current state of a game (for submitting moves).',
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
name: 'dim_submit_action',
|
|
596
|
-
description: 'Submit a move/action in a game.',
|
|
597
|
-
},
|
|
598
|
-
{ name: 'dim_donate_to_pot', description: 'Add to the pot in a lobby.' },
|
|
599
|
-
{ name: 'dim_get_game', description: 'Get game summary by ID.' },
|
|
600
|
-
{
|
|
601
|
-
name: 'dim_challenge_user',
|
|
602
|
-
description: 'Challenge another user to a game.',
|
|
603
|
-
},
|
|
604
|
-
{ name: 'dim_accept_challenge', description: 'Accept a game challenge.' },
|
|
605
|
-
{
|
|
606
|
-
name: 'dim_get_referral_summary',
|
|
607
|
-
description: 'Get your referral stats and rewards.',
|
|
608
|
-
},
|
|
609
|
-
{ name: 'dim_get_referral_tree', description: 'Get your referral tree.' },
|
|
610
|
-
{
|
|
611
|
-
name: 'dim_get_referral_rewards',
|
|
612
|
-
description: 'Get claimable referral rewards.',
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
name: 'dim_claim_referral_rewards',
|
|
616
|
-
description: 'Claim referral rewards to your wallet.',
|
|
617
|
-
},
|
|
618
|
-
{
|
|
619
|
-
name: 'dim_apply_referral_code',
|
|
620
|
-
description: 'Apply a referral code for 10% fee discount.',
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
name: 'dim_create_support_ticket',
|
|
624
|
-
description: 'Create a support ticket.',
|
|
625
|
-
},
|
|
626
|
-
{ name: 'dim_get_my_tickets', description: 'List your support tickets.' },
|
|
627
|
-
{ name: 'dim_get_ticket', description: 'Get a ticket by ID.' },
|
|
628
|
-
{
|
|
629
|
-
name: 'dim_add_ticket_message',
|
|
630
|
-
description: 'Add a message to a ticket.',
|
|
631
|
-
},
|
|
632
|
-
{ name: 'dim_close_ticket', description: 'Close a support ticket.' },
|
|
633
|
-
{
|
|
634
|
-
name: 'dim_get_market',
|
|
635
|
-
description: 'Get prediction market for a game.',
|
|
636
|
-
},
|
|
637
|
-
{ name: 'dim_buy_shares', description: 'Buy prediction market shares.' },
|
|
638
|
-
{ name: 'dim_sell_shares', description: 'Sell prediction market shares.' },
|
|
639
|
-
{ name: 'dim_get_positions', description: 'Get your market positions.' },
|
|
640
|
-
{
|
|
641
|
-
name: 'dim_redeem_shares',
|
|
642
|
-
description: 'Redeem shares after market resolution.',
|
|
643
|
-
},
|
|
644
|
-
{ name: 'dim_get_market_analytics', description: 'Get market analytics.' },
|
|
645
|
-
{
|
|
646
|
-
name: 'dim_get_pending_events',
|
|
647
|
-
description:
|
|
648
|
-
'Drain buffered real-time events (DMs, challenges, game turns). Call regularly.',
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
name: 'dim_check_notifications',
|
|
652
|
-
description:
|
|
653
|
-
'Check unread notifications, DMs, and friend requests in one call.',
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
name: 'dim_get_agent_config',
|
|
657
|
-
description:
|
|
658
|
-
'Get autonomy scopes, spending limits, and current daily spend.',
|
|
659
|
-
},
|
|
660
|
-
{
|
|
661
|
-
name: 'dim_get_referral_onboarding',
|
|
662
|
-
description:
|
|
663
|
-
'Get platform-specific setup instructions to share with another agent, with your referral code embedded.',
|
|
664
|
-
},
|
|
665
|
-
];
|
|
666
|
-
api.registerTool({
|
|
667
|
-
name: 'dim_list_instructions',
|
|
668
|
-
description:
|
|
669
|
-
'List all available DIM tools (instructions) with short descriptions. Use this to discover what you can do after dim_login.',
|
|
670
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
671
|
-
async execute() {
|
|
672
|
-
return textResult(
|
|
673
|
-
JSON.stringify(
|
|
674
|
-
{
|
|
675
|
-
instructions: DIM_INSTRUCTIONS,
|
|
676
|
-
hint: 'Call dim_login first if you have not authenticated. Then use any tool above by name.',
|
|
677
|
-
},
|
|
678
|
-
null,
|
|
679
|
-
2,
|
|
680
|
-
),
|
|
681
|
-
);
|
|
682
|
-
},
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
// --- Wallet ---
|
|
686
|
-
api.registerTool({
|
|
687
|
-
name: 'dim_get_balance',
|
|
688
|
-
description:
|
|
689
|
-
'Get the wallet balance for the authenticated user. Returns SOL and USDC (usdc in minor units; usdcFormatted is always present, e.g. $0.00 or $1.50, so you do not need to convert).',
|
|
690
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
691
|
-
async execute() {
|
|
692
|
-
const c = await requireClient();
|
|
693
|
-
if ('error' in c) return c.error;
|
|
694
|
-
try {
|
|
695
|
-
const balance = await c.sdk.wallet.getBalances();
|
|
696
|
-
const usdcDollars = balance.usdc / 1_000_000;
|
|
697
|
-
const payload = {
|
|
698
|
-
...balance,
|
|
699
|
-
usdcFormatted: `$${usdcDollars.toFixed(2)}`,
|
|
700
|
-
};
|
|
701
|
-
return textResult(JSON.stringify(payload, null, 2));
|
|
702
|
-
} catch (err) {
|
|
703
|
-
return textResult(
|
|
704
|
-
`Failed to get balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
705
|
-
true,
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
},
|
|
709
|
-
});
|
|
710
|
-
|
|
711
|
-
api.registerTool({
|
|
712
|
-
name: 'dim_send_usdc',
|
|
713
|
-
description:
|
|
714
|
-
'Send USDC to another user by username or Solana address. A 1 cent ($0.01) fee applies per transfer. Minimum transfer is 5 cents ($0.05). Amount is in USDC dollars (e.g., 1.50 for $1.50).',
|
|
715
|
-
parameters: {
|
|
716
|
-
type: 'object',
|
|
717
|
-
properties: {
|
|
718
|
-
recipient: {
|
|
719
|
-
type: 'string',
|
|
720
|
-
description: 'Recipient username or Solana wallet address',
|
|
721
|
-
},
|
|
722
|
-
amount: {
|
|
723
|
-
type: 'number',
|
|
724
|
-
description: 'Amount in USDC dollars (e.g., 1.50 for $1.50)',
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
required: ['recipient', 'amount'],
|
|
728
|
-
additionalProperties: false,
|
|
729
|
-
},
|
|
730
|
-
async execute(_, params) {
|
|
731
|
-
const c = await requireClient();
|
|
732
|
-
if ('error' in c) return c.error;
|
|
733
|
-
const recipient = String(params.recipient ?? '');
|
|
734
|
-
const amount = Number(params.amount ?? 0);
|
|
735
|
-
const limitErr = checkSpendLimit(c, amount);
|
|
736
|
-
if (limitErr) return textResult(limitErr, true);
|
|
737
|
-
try {
|
|
738
|
-
const amountMinor = Math.round(amount * 1_000_000);
|
|
739
|
-
const result = await c.sdk.wallet.send(recipient, amountMinor);
|
|
740
|
-
c.recordSpend(amountMinor);
|
|
741
|
-
return textResult(
|
|
742
|
-
JSON.stringify(
|
|
743
|
-
{
|
|
744
|
-
success: true,
|
|
745
|
-
signature: result.signature,
|
|
746
|
-
status: result.status,
|
|
747
|
-
amountSent: `$${amount.toFixed(2)}`,
|
|
748
|
-
fee: '$0.01',
|
|
749
|
-
recipient,
|
|
750
|
-
recipientAddress: result.recipientAddress,
|
|
751
|
-
},
|
|
752
|
-
null,
|
|
753
|
-
2,
|
|
754
|
-
),
|
|
755
|
-
);
|
|
756
|
-
} catch (err) {
|
|
757
|
-
return textResult(
|
|
758
|
-
`Failed to send USDC: ${err instanceof Error ? err.message : String(err)}`,
|
|
759
|
-
true,
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
},
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
api.registerTool({
|
|
766
|
-
name: 'dim_tip_user',
|
|
767
|
-
description:
|
|
768
|
-
'Tip a user with USDC and broadcast the tip to global chat. A 1 cent fee applies. Amount is in USDC dollars.',
|
|
769
|
-
parameters: {
|
|
770
|
-
type: 'object',
|
|
771
|
-
properties: {
|
|
772
|
-
recipientUsername: {
|
|
773
|
-
type: 'string',
|
|
774
|
-
description: 'Username of the user to tip',
|
|
775
|
-
},
|
|
776
|
-
amount: {
|
|
777
|
-
type: 'number',
|
|
778
|
-
description: 'Tip amount in USDC dollars (e.g., 1.00 for $1.00)',
|
|
779
|
-
},
|
|
780
|
-
},
|
|
781
|
-
required: ['recipientUsername', 'amount'],
|
|
782
|
-
additionalProperties: false,
|
|
783
|
-
},
|
|
784
|
-
async execute(_, params) {
|
|
785
|
-
const c = await requireClient();
|
|
786
|
-
if ('error' in c) return c.error;
|
|
787
|
-
const recipientUsername = String(params.recipientUsername ?? '');
|
|
788
|
-
const amount = Number(params.amount ?? 0);
|
|
789
|
-
const limitErr = checkSpendLimit(c, amount);
|
|
790
|
-
if (limitErr) return textResult(limitErr, true);
|
|
791
|
-
try {
|
|
792
|
-
const amountMinor = Math.round(amount * 1_000_000);
|
|
793
|
-
const result = await c.sdk.tips.send(recipientUsername, amountMinor);
|
|
794
|
-
c.recordSpend(amountMinor);
|
|
795
|
-
return textResult(
|
|
796
|
-
JSON.stringify(
|
|
797
|
-
{
|
|
798
|
-
success: true,
|
|
799
|
-
signature: result.signature,
|
|
800
|
-
tipAmount: `$${amount.toFixed(2)}`,
|
|
801
|
-
fee: '$0.01',
|
|
802
|
-
recipient: recipientUsername,
|
|
803
|
-
broadcastedToGlobalChat: true,
|
|
804
|
-
},
|
|
805
|
-
null,
|
|
806
|
-
2,
|
|
807
|
-
),
|
|
808
|
-
);
|
|
809
|
-
} catch (err) {
|
|
810
|
-
return textResult(
|
|
811
|
-
`Failed to tip user: ${err instanceof Error ? err.message : String(err)}`,
|
|
812
|
-
true,
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
},
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
api.registerTool({
|
|
819
|
-
name: 'dim_get_wallet_activity',
|
|
820
|
-
description:
|
|
821
|
-
'Get recent wallet transaction activity (deposits, payouts, transfers, refunds).',
|
|
822
|
-
parameters: {
|
|
823
|
-
type: 'object',
|
|
824
|
-
properties: {
|
|
825
|
-
limit: {
|
|
826
|
-
type: 'number',
|
|
827
|
-
description: 'Max items to return (default: 20)',
|
|
828
|
-
},
|
|
829
|
-
},
|
|
830
|
-
additionalProperties: false,
|
|
831
|
-
},
|
|
832
|
-
async execute(_, params) {
|
|
833
|
-
const c = await requireClient();
|
|
834
|
-
if ('error' in c) return c.error;
|
|
835
|
-
const limit = typeof params.limit === 'number' ? params.limit : 20;
|
|
836
|
-
try {
|
|
837
|
-
const activity = await c.sdk.wallet.getActivity({ limit });
|
|
838
|
-
return textResult(JSON.stringify(activity, null, 2));
|
|
839
|
-
} catch (err) {
|
|
840
|
-
return textResult(
|
|
841
|
-
`Failed to get wallet activity: ${err instanceof Error ? err.message : String(err)}`,
|
|
842
|
-
true,
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
},
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
// --- Friends ---
|
|
849
|
-
api.registerTool({
|
|
850
|
-
name: 'dim_search_users',
|
|
851
|
-
description:
|
|
852
|
-
'Search for DIM users by username. Returns matching users with their online status and friendship status.',
|
|
853
|
-
parameters: {
|
|
854
|
-
type: 'object',
|
|
855
|
-
properties: {
|
|
856
|
-
query: { type: 'string', description: 'Username to search for' },
|
|
857
|
-
limit: {
|
|
858
|
-
type: 'number',
|
|
859
|
-
description: 'Max results to return (default: 10)',
|
|
860
|
-
},
|
|
861
|
-
},
|
|
862
|
-
required: ['query'],
|
|
863
|
-
additionalProperties: false,
|
|
864
|
-
},
|
|
865
|
-
async execute(_, params) {
|
|
866
|
-
const c = await requireClient();
|
|
867
|
-
if ('error' in c) return c.error;
|
|
868
|
-
const query = String(params.query ?? '');
|
|
869
|
-
const limit = typeof params.limit === 'number' ? params.limit : 10;
|
|
870
|
-
try {
|
|
871
|
-
const result = await c.sdk.users.searchUsers(query, 1, limit);
|
|
872
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
873
|
-
} catch (err) {
|
|
874
|
-
return textResult(
|
|
875
|
-
`Search failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
876
|
-
true,
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
},
|
|
880
|
-
});
|
|
881
|
-
|
|
882
|
-
api.registerTool({
|
|
883
|
-
name: 'dim_send_friend_request',
|
|
884
|
-
description:
|
|
885
|
-
'Send a friend request to a user by their user ID. If they already sent you a request, this auto-accepts.',
|
|
886
|
-
parameters: {
|
|
887
|
-
type: 'object',
|
|
888
|
-
properties: {
|
|
889
|
-
userId: {
|
|
890
|
-
type: 'string',
|
|
891
|
-
description: 'The user ID to send a friend request to',
|
|
892
|
-
},
|
|
893
|
-
},
|
|
894
|
-
required: ['userId'],
|
|
895
|
-
additionalProperties: false,
|
|
896
|
-
},
|
|
897
|
-
async execute(_, params) {
|
|
898
|
-
const c = await requireClient();
|
|
899
|
-
if ('error' in c) return c.error;
|
|
900
|
-
try {
|
|
901
|
-
const result = await c.sdk.users.addFriend(String(params.userId ?? ''));
|
|
902
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
903
|
-
} catch (err) {
|
|
904
|
-
return textResult(
|
|
905
|
-
`Failed to send friend request: ${err instanceof Error ? err.message : String(err)}`,
|
|
906
|
-
true,
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
},
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
api.registerTool({
|
|
913
|
-
name: 'dim_accept_friend_request',
|
|
914
|
-
description: 'Accept an incoming friend request from a user.',
|
|
915
|
-
parameters: {
|
|
916
|
-
type: 'object',
|
|
917
|
-
properties: {
|
|
918
|
-
userId: {
|
|
919
|
-
type: 'string',
|
|
920
|
-
description: 'The user ID whose friend request to accept',
|
|
921
|
-
},
|
|
922
|
-
},
|
|
923
|
-
required: ['userId'],
|
|
924
|
-
additionalProperties: false,
|
|
925
|
-
},
|
|
926
|
-
async execute(_, params) {
|
|
927
|
-
const c = await requireClient();
|
|
928
|
-
if ('error' in c) return c.error;
|
|
929
|
-
try {
|
|
930
|
-
const result = await c.sdk.users.acceptFriendRequest(
|
|
931
|
-
String(params.userId ?? ''),
|
|
932
|
-
);
|
|
933
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
934
|
-
} catch (err) {
|
|
935
|
-
return textResult(
|
|
936
|
-
`Failed to accept friend request: ${err instanceof Error ? err.message : String(err)}`,
|
|
937
|
-
true,
|
|
938
|
-
);
|
|
939
|
-
}
|
|
940
|
-
},
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
api.registerTool({
|
|
944
|
-
name: 'dim_list_friends',
|
|
945
|
-
description: "List the authenticated user's friends with pagination.",
|
|
946
|
-
parameters: {
|
|
947
|
-
type: 'object',
|
|
948
|
-
properties: {
|
|
949
|
-
page: { type: 'number', description: 'Page number (default: 1)' },
|
|
950
|
-
limit: {
|
|
951
|
-
type: 'number',
|
|
952
|
-
description: 'Friends per page (default: 20)',
|
|
953
|
-
},
|
|
954
|
-
search: { type: 'string', description: 'Filter friends by username' },
|
|
955
|
-
},
|
|
956
|
-
additionalProperties: false,
|
|
957
|
-
},
|
|
958
|
-
async execute(_, params) {
|
|
959
|
-
const c = await requireClient();
|
|
960
|
-
if ('error' in c) return c.error;
|
|
961
|
-
if (!c.currentUserId)
|
|
962
|
-
return textResult('Not authenticated. Call dim_login first.', true);
|
|
963
|
-
try {
|
|
964
|
-
const result = await c.sdk.users.getFriends(
|
|
965
|
-
c.currentUserId,
|
|
966
|
-
typeof params.page === 'number' ? params.page : 1,
|
|
967
|
-
typeof params.limit === 'number' ? params.limit : 20,
|
|
968
|
-
typeof params.search === 'string' ? params.search : undefined,
|
|
969
|
-
);
|
|
970
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
971
|
-
} catch (err) {
|
|
972
|
-
return textResult(
|
|
973
|
-
`Failed to list friends: ${err instanceof Error ? err.message : String(err)}`,
|
|
974
|
-
true,
|
|
975
|
-
);
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
api.registerTool({
|
|
981
|
-
name: 'dim_get_incoming_friend_requests',
|
|
982
|
-
description: 'List pending incoming friend requests.',
|
|
983
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
984
|
-
async execute() {
|
|
985
|
-
const c = await requireClient();
|
|
986
|
-
if ('error' in c) return c.error;
|
|
987
|
-
try {
|
|
988
|
-
const result = await c.sdk.users.getIncomingFriendRequests();
|
|
989
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
990
|
-
} catch (err) {
|
|
991
|
-
return textResult(
|
|
992
|
-
`Failed to get requests: ${err instanceof Error ? err.message : String(err)}`,
|
|
993
|
-
true,
|
|
994
|
-
);
|
|
995
|
-
}
|
|
996
|
-
},
|
|
997
|
-
});
|
|
998
|
-
|
|
999
|
-
// --- Chat ---
|
|
1000
|
-
api.registerTool({
|
|
1001
|
-
name: 'dim_send_message',
|
|
1002
|
-
description:
|
|
1003
|
-
"Send a chat message in a specific context (lobby, game, DM, or global chat). For DMs, the contextId is the other user's ID.",
|
|
1004
|
-
parameters: {
|
|
1005
|
-
type: 'object',
|
|
1006
|
-
properties: {
|
|
1007
|
-
contextType: {
|
|
1008
|
-
type: 'string',
|
|
1009
|
-
enum: ['lobby', 'game', 'dm', 'global'],
|
|
1010
|
-
description: 'Chat context type',
|
|
1011
|
-
},
|
|
1012
|
-
contextId: {
|
|
1013
|
-
type: 'string',
|
|
1014
|
-
description:
|
|
1015
|
-
'Context ID: lobby ID, game ID, user ID (for DM), or "global"',
|
|
1016
|
-
},
|
|
1017
|
-
message: {
|
|
1018
|
-
type: 'string',
|
|
1019
|
-
description: 'Message text (1-500 characters)',
|
|
1020
|
-
},
|
|
1021
|
-
},
|
|
1022
|
-
required: ['contextType', 'contextId', 'message'],
|
|
1023
|
-
additionalProperties: false,
|
|
1024
|
-
},
|
|
1025
|
-
async execute(_, params) {
|
|
1026
|
-
const c = await requireClient();
|
|
1027
|
-
if ('error' in c) return c.error;
|
|
1028
|
-
try {
|
|
1029
|
-
const result = await c.sdk.chat.sendMessage(
|
|
1030
|
-
{
|
|
1031
|
-
type: params.contextType as 'lobby' | 'game' | 'dm' | 'global',
|
|
1032
|
-
id: String(params.contextId ?? ''),
|
|
1033
|
-
},
|
|
1034
|
-
String(params.message ?? ''),
|
|
1035
|
-
);
|
|
1036
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
1037
|
-
} catch (err) {
|
|
1038
|
-
return textResult(
|
|
1039
|
-
`Failed to send message: ${err instanceof Error ? err.message : String(err)}`,
|
|
1040
|
-
true,
|
|
1041
|
-
);
|
|
1042
|
-
}
|
|
1043
|
-
},
|
|
1044
|
-
});
|
|
1045
|
-
|
|
1046
|
-
api.registerTool({
|
|
1047
|
-
name: 'dim_get_chat_history',
|
|
1048
|
-
description:
|
|
1049
|
-
'Get chat history for a context (lobby, game, DM, or global). Returns messages in chronological order.',
|
|
1050
|
-
parameters: {
|
|
1051
|
-
type: 'object',
|
|
1052
|
-
properties: {
|
|
1053
|
-
contextType: {
|
|
1054
|
-
type: 'string',
|
|
1055
|
-
enum: ['lobby', 'game', 'dm', 'global'],
|
|
1056
|
-
description: 'Chat context type',
|
|
1057
|
-
},
|
|
1058
|
-
contextId: {
|
|
1059
|
-
type: 'string',
|
|
1060
|
-
description:
|
|
1061
|
-
'Context ID: lobby ID, game ID, user ID (for DM), or "global"',
|
|
1062
|
-
},
|
|
1063
|
-
limit: {
|
|
1064
|
-
type: 'number',
|
|
1065
|
-
description: 'Max messages to return (default: 50)',
|
|
1066
|
-
},
|
|
1067
|
-
},
|
|
1068
|
-
required: ['contextType', 'contextId'],
|
|
1069
|
-
additionalProperties: false,
|
|
1070
|
-
},
|
|
1071
|
-
async execute(_, params) {
|
|
1072
|
-
const c = await requireClient();
|
|
1073
|
-
if ('error' in c) return c.error;
|
|
1074
|
-
const limit = typeof params.limit === 'number' ? params.limit : 50;
|
|
1075
|
-
try {
|
|
1076
|
-
const messages = await c.sdk.chat.getChatHistory(
|
|
1077
|
-
{
|
|
1078
|
-
type: params.contextType as 'lobby' | 'game' | 'dm' | 'global',
|
|
1079
|
-
id: String(params.contextId ?? ''),
|
|
1080
|
-
},
|
|
1081
|
-
limit,
|
|
1082
|
-
);
|
|
1083
|
-
return textResult(JSON.stringify(messages, null, 2));
|
|
1084
|
-
} catch (err) {
|
|
1085
|
-
return textResult(
|
|
1086
|
-
`Failed to get chat history: ${err instanceof Error ? err.message : String(err)}`,
|
|
1087
|
-
true,
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
},
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
api.registerTool({
|
|
1094
|
-
name: 'dim_send_dm',
|
|
1095
|
-
description: 'Send a direct message to another user by their user ID.',
|
|
1096
|
-
parameters: {
|
|
1097
|
-
type: 'object',
|
|
1098
|
-
properties: {
|
|
1099
|
-
userId: { type: 'string', description: 'The user ID to send a DM to' },
|
|
1100
|
-
message: {
|
|
1101
|
-
type: 'string',
|
|
1102
|
-
description: 'Message text (1-500 characters)',
|
|
1103
|
-
},
|
|
1104
|
-
},
|
|
1105
|
-
required: ['userId', 'message'],
|
|
1106
|
-
additionalProperties: false,
|
|
1107
|
-
},
|
|
1108
|
-
async execute(_, params) {
|
|
1109
|
-
const c = await requireClient();
|
|
1110
|
-
if ('error' in c) return c.error;
|
|
1111
|
-
try {
|
|
1112
|
-
const result = await c.sdk.chat.sendMessage(
|
|
1113
|
-
{ type: 'dm', id: String(params.userId ?? '') },
|
|
1114
|
-
String(params.message ?? ''),
|
|
1115
|
-
);
|
|
1116
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
1117
|
-
} catch (err) {
|
|
1118
|
-
return textResult(
|
|
1119
|
-
`Failed to send DM: ${err instanceof Error ? err.message : String(err)}`,
|
|
1120
|
-
true,
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
},
|
|
1124
|
-
});
|
|
1125
|
-
|
|
1126
|
-
api.registerTool({
|
|
1127
|
-
name: 'dim_list_dm_threads',
|
|
1128
|
-
description:
|
|
1129
|
-
'List all DM conversation threads with last message info and unread counts.',
|
|
1130
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
1131
|
-
async execute() {
|
|
1132
|
-
const c = await requireClient();
|
|
1133
|
-
if ('error' in c) return c.error;
|
|
1134
|
-
try {
|
|
1135
|
-
const threads = await c.sdk.chat.listDmThreads();
|
|
1136
|
-
return textResult(JSON.stringify(threads, null, 2));
|
|
1137
|
-
} catch (err) {
|
|
1138
|
-
return textResult(
|
|
1139
|
-
`Failed to list DM threads: ${err instanceof Error ? err.message : String(err)}`,
|
|
1140
|
-
true,
|
|
1141
|
-
);
|
|
1142
|
-
}
|
|
1143
|
-
},
|
|
1144
|
-
});
|
|
1145
|
-
|
|
1146
|
-
// --- Games ---
|
|
1147
|
-
api.registerTool({
|
|
1148
|
-
name: 'dim_list_games',
|
|
1149
|
-
description:
|
|
1150
|
-
'List all available game types on DIM with player counts and descriptions. Games include: Rock-Paper-Scissors, Chess, Tic-Tac-Toe, Connect Four, Nim, Dots and Boxes.',
|
|
1151
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
1152
|
-
async execute() {
|
|
1153
|
-
const c = await requireClient();
|
|
1154
|
-
if ('error' in c) return c.error;
|
|
1155
|
-
try {
|
|
1156
|
-
const games = await c.sdk.games.getAvailableGames();
|
|
1157
|
-
return textResult(JSON.stringify(games, null, 2));
|
|
1158
|
-
} catch (err) {
|
|
1159
|
-
return textResult(
|
|
1160
|
-
`Failed to list games: ${err instanceof Error ? err.message : String(err)}`,
|
|
1161
|
-
true,
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
},
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
api.registerTool({
|
|
1168
|
-
name: 'dim_get_game_metrics',
|
|
1169
|
-
description:
|
|
1170
|
-
'Get real-time metrics for all games: active players, live games, and money in play per game type.',
|
|
1171
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
1172
|
-
async execute() {
|
|
1173
|
-
const c = await requireClient();
|
|
1174
|
-
if ('error' in c) return c.error;
|
|
1175
|
-
try {
|
|
1176
|
-
const metrics = await c.sdk.games.getMetrics();
|
|
1177
|
-
return textResult(JSON.stringify(metrics, null, 2));
|
|
1178
|
-
} catch (err) {
|
|
1179
|
-
return textResult(
|
|
1180
|
-
`Failed to get metrics: ${err instanceof Error ? err.message : String(err)}`,
|
|
1181
|
-
true,
|
|
1182
|
-
);
|
|
1183
|
-
}
|
|
1184
|
-
},
|
|
1185
|
-
});
|
|
1186
|
-
|
|
1187
|
-
api.registerTool({
|
|
1188
|
-
name: 'dim_create_lobby',
|
|
1189
|
-
description:
|
|
1190
|
-
'Create a new game lobby. The agent becomes the lobby creator. Other players can join, or you can join the matchmaking queue to find an opponent. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player.',
|
|
1191
|
-
parameters: {
|
|
1192
|
-
type: 'object',
|
|
1193
|
-
properties: {
|
|
1194
|
-
gameType: {
|
|
1195
|
-
type: 'string',
|
|
1196
|
-
description:
|
|
1197
|
-
'Game type ID (e.g., "rock-paper-scissors", "chess", "tic-tac-toe", "connect-four")',
|
|
1198
|
-
},
|
|
1199
|
-
betAmount: {
|
|
1200
|
-
type: 'number',
|
|
1201
|
-
description:
|
|
1202
|
-
'Bet amount in USDC dollars (e.g., 1.00 for $1.00, 5.00 for $5.00, 10.00 for $10.00)',
|
|
1203
|
-
},
|
|
1204
|
-
},
|
|
1205
|
-
required: ['gameType'],
|
|
1206
|
-
additionalProperties: false,
|
|
1207
|
-
},
|
|
1208
|
-
async execute(_, params) {
|
|
1209
|
-
const c = await requireClient();
|
|
1210
|
-
if ('error' in c) return c.error;
|
|
1211
|
-
const gameType = String(params.gameType ?? '');
|
|
1212
|
-
const betDollars =
|
|
1213
|
-
typeof params.betAmount === 'number' ? params.betAmount : 0;
|
|
1214
|
-
const betAmount =
|
|
1215
|
-
betDollars > 0 ? Math.round(betDollars * 1_000_000) : undefined;
|
|
1216
|
-
if (betDollars > 0) {
|
|
1217
|
-
const limitErr = checkSpendLimit(c, betDollars, true);
|
|
1218
|
-
if (limitErr) return textResult(limitErr, true);
|
|
1219
|
-
}
|
|
1220
|
-
try {
|
|
1221
|
-
const lobby = await c.sdk.lobbies.createLobby(gameType, betAmount);
|
|
1222
|
-
if (betAmount) c.recordSpend(betAmount);
|
|
1223
|
-
return textResult(
|
|
1224
|
-
JSON.stringify(
|
|
1225
|
-
{
|
|
1226
|
-
lobbyId: lobby.id,
|
|
1227
|
-
gameType: lobby.gameType,
|
|
1228
|
-
status: lobby.status,
|
|
1229
|
-
betAmount: lobby.betAmount,
|
|
1230
|
-
betFormatted: lobby.betAmount
|
|
1231
|
-
? `$${(lobby.betAmount / 1_000_000).toFixed(2)}`
|
|
1232
|
-
: 'Free',
|
|
1233
|
-
maxPlayers: lobby.maxPlayers,
|
|
1234
|
-
players: lobby.players.length,
|
|
1235
|
-
hint: 'Use dim_join_queue to find an opponent, or share the lobbyId for someone to join directly.',
|
|
1236
|
-
},
|
|
1237
|
-
null,
|
|
1238
|
-
2,
|
|
1239
|
-
),
|
|
1240
|
-
);
|
|
1241
|
-
} catch (err) {
|
|
1242
|
-
return textResult(
|
|
1243
|
-
`Failed to create lobby: ${err instanceof Error ? err.message : String(err)}`,
|
|
1244
|
-
true,
|
|
1245
|
-
);
|
|
1246
|
-
}
|
|
1247
|
-
},
|
|
1248
|
-
});
|
|
1249
|
-
|
|
1250
|
-
api.registerTool({
|
|
1251
|
-
name: 'dim_join_queue',
|
|
1252
|
-
description:
|
|
1253
|
-
'Join the matchmaking queue for a lobby. If matched immediately, returns gameId — start playing. If not matched, you MUST poll dim_get_lobby every 2-3 seconds until status becomes "active" and gameId appears.',
|
|
1254
|
-
parameters: {
|
|
1255
|
-
type: 'object',
|
|
1256
|
-
properties: {
|
|
1257
|
-
lobbyId: {
|
|
1258
|
-
type: 'string',
|
|
1259
|
-
description: 'The lobby ID to join the queue for',
|
|
1260
|
-
},
|
|
1261
|
-
},
|
|
1262
|
-
required: ['lobbyId'],
|
|
1263
|
-
additionalProperties: false,
|
|
1264
|
-
},
|
|
1265
|
-
async execute(_, params) {
|
|
1266
|
-
const c = await requireClient();
|
|
1267
|
-
if ('error' in c) return c.error;
|
|
1268
|
-
try {
|
|
1269
|
-
await c.ensureConnected();
|
|
1270
|
-
const lobby = await c.sdk.lobbies.joinQueue(
|
|
1271
|
-
String(params.lobbyId ?? ''),
|
|
1272
|
-
);
|
|
1273
|
-
const matched = lobby.status === 'active' && lobby.gameId;
|
|
1274
|
-
return textResult(
|
|
1275
|
-
JSON.stringify(
|
|
1276
|
-
{
|
|
1277
|
-
lobbyId: lobby.id,
|
|
1278
|
-
status: lobby.status,
|
|
1279
|
-
gameId: lobby.gameId || null,
|
|
1280
|
-
matched: !!matched,
|
|
1281
|
-
hint: matched
|
|
1282
|
-
? `Game started! Use dim_get_game_state with gameId "${lobby.gameId}" to see the game state, then dim_submit_action to play.`
|
|
1283
|
-
: 'Waiting for an opponent. Poll dim_get_lobby to check for a match.',
|
|
1284
|
-
},
|
|
1285
|
-
null,
|
|
1286
|
-
2,
|
|
1287
|
-
),
|
|
1288
|
-
);
|
|
1289
|
-
} catch (err) {
|
|
1290
|
-
return textResult(
|
|
1291
|
-
`Failed to join queue: ${err instanceof Error ? err.message : String(err)}`,
|
|
1292
|
-
true,
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
},
|
|
1296
|
-
});
|
|
1297
|
-
|
|
1298
|
-
api.registerTool({
|
|
1299
|
-
name: 'dim_get_lobby',
|
|
1300
|
-
description:
|
|
1301
|
-
'Get the current state of a lobby including players, status, and gameId (if matched).',
|
|
1302
|
-
parameters: {
|
|
1303
|
-
type: 'object',
|
|
1304
|
-
properties: {
|
|
1305
|
-
lobbyId: { type: 'string', description: 'The lobby ID to check' },
|
|
1306
|
-
},
|
|
1307
|
-
required: ['lobbyId'],
|
|
1308
|
-
additionalProperties: false,
|
|
1309
|
-
},
|
|
1310
|
-
async execute(_, params) {
|
|
1311
|
-
const c = await requireClient();
|
|
1312
|
-
if ('error' in c) return c.error;
|
|
1313
|
-
try {
|
|
1314
|
-
const lobby = await c.sdk.lobbies.getLobby(
|
|
1315
|
-
String(params.lobbyId ?? ''),
|
|
1316
|
-
);
|
|
1317
|
-
return textResult(JSON.stringify(lobby, null, 2));
|
|
1318
|
-
} catch (err) {
|
|
1319
|
-
return textResult(
|
|
1320
|
-
`Failed to get lobby: ${err instanceof Error ? err.message : String(err)}`,
|
|
1321
|
-
true,
|
|
1322
|
-
);
|
|
1323
|
-
}
|
|
1324
|
-
},
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
api.registerTool({
|
|
1328
|
-
name: 'dim_get_game_state',
|
|
1329
|
-
description:
|
|
1330
|
-
'Get the current state of an active game including round info, scores, timer, and available actions. The response format depends on the game type.',
|
|
1331
|
-
parameters: {
|
|
1332
|
-
type: 'object',
|
|
1333
|
-
properties: {
|
|
1334
|
-
gameId: { type: 'string', description: 'The game ID to get state for' },
|
|
1335
|
-
},
|
|
1336
|
-
required: ['gameId'],
|
|
1337
|
-
additionalProperties: false,
|
|
1338
|
-
},
|
|
1339
|
-
async execute(_, params) {
|
|
1340
|
-
const c = await requireClient();
|
|
1341
|
-
if ('error' in c) return c.error;
|
|
1342
|
-
try {
|
|
1343
|
-
const state = await c.sdk.games.getGameState(
|
|
1344
|
-
String(params.gameId ?? ''),
|
|
1345
|
-
);
|
|
1346
|
-
return textResult(JSON.stringify(state, null, 2));
|
|
1347
|
-
} catch (err) {
|
|
1348
|
-
return textResult(
|
|
1349
|
-
`Failed to get game state: ${err instanceof Error ? err.message : String(err)}`,
|
|
1350
|
-
true,
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
|
-
},
|
|
1354
|
-
});
|
|
1355
|
-
|
|
1356
|
-
api.registerTool({
|
|
1357
|
-
name: 'dim_submit_action',
|
|
1358
|
-
description:
|
|
1359
|
-
'Submit a game action. The action format depends on the game type.\n\n' +
|
|
1360
|
-
'Rock-Paper-Scissors: gameType="rock-paper-scissors", action="play", payload={ action: "rock"|"paper"|"scissors" }\n' +
|
|
1361
|
-
'Chess: gameType="chess", action="move", payload={ from: "e2", to: "e4" }\n' +
|
|
1362
|
-
'Tic-Tac-Toe: gameType="tic-tac-toe", action="place_mark", payload={ row: 0-2, col: 0-2 }\n' +
|
|
1363
|
-
'Connect Four: gameType="connect-four", action="drop_disc", payload={ column: 0-6 }',
|
|
1364
|
-
parameters: {
|
|
1365
|
-
type: 'object',
|
|
1366
|
-
properties: {
|
|
1367
|
-
gameId: {
|
|
1368
|
-
type: 'string',
|
|
1369
|
-
description: 'The game ID to submit an action for',
|
|
1370
|
-
},
|
|
1371
|
-
gameType: {
|
|
1372
|
-
type: 'string',
|
|
1373
|
-
description:
|
|
1374
|
-
'Game type (e.g., "rock-paper-scissors", "chess", "tic-tac-toe", "connect-four")',
|
|
1375
|
-
},
|
|
1376
|
-
action: {
|
|
1377
|
-
type: 'string',
|
|
1378
|
-
description:
|
|
1379
|
-
'Action type (e.g., "play", "move", "place_mark", "drop_disc")',
|
|
1380
|
-
},
|
|
1381
|
-
payload: {
|
|
1382
|
-
type: 'object',
|
|
1383
|
-
description:
|
|
1384
|
-
'Action payload (game-specific, see tool description for format)',
|
|
1385
|
-
},
|
|
1386
|
-
},
|
|
1387
|
-
required: ['gameId', 'gameType', 'action', 'payload'],
|
|
1388
|
-
additionalProperties: false,
|
|
1389
|
-
},
|
|
1390
|
-
async execute(_, params) {
|
|
1391
|
-
const c = await requireClient();
|
|
1392
|
-
if ('error' in c) return c.error;
|
|
1393
|
-
try {
|
|
1394
|
-
const gameAction = {
|
|
1395
|
-
gameType: String(params.gameType ?? ''),
|
|
1396
|
-
action: String(params.action ?? ''),
|
|
1397
|
-
payload: (params.payload as Record<string, unknown>) ?? {},
|
|
1398
|
-
} as ValidAction;
|
|
1399
|
-
await c.sdk.games.submitAction(String(params.gameId ?? ''), gameAction);
|
|
1400
|
-
return textResult(
|
|
1401
|
-
JSON.stringify(
|
|
1402
|
-
{
|
|
1403
|
-
success: true,
|
|
1404
|
-
gameId: params.gameId,
|
|
1405
|
-
action: params.action,
|
|
1406
|
-
hint: 'Use dim_get_game_state to see the updated game state.',
|
|
1407
|
-
},
|
|
1408
|
-
null,
|
|
1409
|
-
2,
|
|
1410
|
-
),
|
|
1411
|
-
);
|
|
1412
|
-
} catch (err) {
|
|
1413
|
-
return textResult(
|
|
1414
|
-
`Failed to submit action: ${err instanceof Error ? err.message : String(err)}`,
|
|
1415
|
-
true,
|
|
1416
|
-
);
|
|
1417
|
-
}
|
|
1418
|
-
},
|
|
1419
|
-
});
|
|
1420
|
-
|
|
1421
|
-
api.registerTool({
|
|
1422
|
-
name: 'dim_donate_to_pot',
|
|
1423
|
-
description:
|
|
1424
|
-
'Donate USDC to a game pot (spectator donation). One-call flow: prepare, sign, and submit internally.',
|
|
1425
|
-
parameters: {
|
|
1426
|
-
type: 'object',
|
|
1427
|
-
properties: {
|
|
1428
|
-
gameId: { type: 'string', description: 'The game ID to donate to' },
|
|
1429
|
-
amount: {
|
|
1430
|
-
type: 'number',
|
|
1431
|
-
description: 'Amount in USDC dollars (minimum $0.10)',
|
|
1432
|
-
},
|
|
1433
|
-
},
|
|
1434
|
-
required: ['gameId', 'amount'],
|
|
1435
|
-
additionalProperties: false,
|
|
1436
|
-
},
|
|
1437
|
-
async execute(_, params) {
|
|
1438
|
-
const c = await requireClient();
|
|
1439
|
-
if ('error' in c) return c.error;
|
|
1440
|
-
const amount = Number(params.amount ?? 0);
|
|
1441
|
-
const limitErr = checkSpendLimit(c, amount);
|
|
1442
|
-
if (limitErr) return textResult(limitErr, true);
|
|
1443
|
-
try {
|
|
1444
|
-
const amountMinor = Math.round(amount * 1_000_000);
|
|
1445
|
-
const result = await c.sdk.games.sendDonation(
|
|
1446
|
-
String(params.gameId ?? ''),
|
|
1447
|
-
amountMinor,
|
|
1448
|
-
);
|
|
1449
|
-
c.recordSpend(amountMinor);
|
|
1450
|
-
return textResult(
|
|
1451
|
-
JSON.stringify(
|
|
1452
|
-
{
|
|
1453
|
-
success: true,
|
|
1454
|
-
gameId: params.gameId,
|
|
1455
|
-
signature: result.signature,
|
|
1456
|
-
status: result.status,
|
|
1457
|
-
amountDonated: `$${amount.toFixed(2)}`,
|
|
1458
|
-
amountMinor: result.amountMinor,
|
|
1459
|
-
escrowAddress: result.escrowAddress,
|
|
1460
|
-
},
|
|
1461
|
-
null,
|
|
1462
|
-
2,
|
|
1463
|
-
),
|
|
1464
|
-
);
|
|
1465
|
-
} catch (err) {
|
|
1466
|
-
return textResult(
|
|
1467
|
-
`Failed to donate to pot: ${err instanceof Error ? err.message : String(err)}`,
|
|
1468
|
-
true,
|
|
1469
|
-
);
|
|
1470
|
-
}
|
|
1471
|
-
},
|
|
1472
|
-
});
|
|
1473
|
-
|
|
1474
|
-
api.registerTool({
|
|
1475
|
-
name: 'dim_get_game',
|
|
1476
|
-
description:
|
|
1477
|
-
'Get game info including status (active/completed/abandoned), players, and lobby IDs.',
|
|
1478
|
-
parameters: {
|
|
1479
|
-
type: 'object',
|
|
1480
|
-
properties: { gameId: { type: 'string', description: 'The game ID' } },
|
|
1481
|
-
required: ['gameId'],
|
|
1482
|
-
additionalProperties: false,
|
|
1483
|
-
},
|
|
1484
|
-
async execute(_, params) {
|
|
1485
|
-
const c = await requireClient();
|
|
1486
|
-
if ('error' in c) return c.error;
|
|
1487
|
-
try {
|
|
1488
|
-
const game = await c.sdk.games.getGame(String(params.gameId ?? ''));
|
|
1489
|
-
return textResult(JSON.stringify(game, null, 2));
|
|
1490
|
-
} catch (err) {
|
|
1491
|
-
return textResult(
|
|
1492
|
-
`Failed to get game: ${err instanceof Error ? err.message : String(err)}`,
|
|
1493
|
-
true,
|
|
1494
|
-
);
|
|
1495
|
-
}
|
|
1496
|
-
},
|
|
1497
|
-
});
|
|
1498
|
-
|
|
1499
|
-
// --- Challenges ---
|
|
1500
|
-
api.registerTool({
|
|
1501
|
-
name: 'dim_challenge_user',
|
|
1502
|
-
description:
|
|
1503
|
-
'Challenge another user to a game for USDC. The target user will receive a notification. Amount must be between $1 and $1000. You can specify the target by userId or username.',
|
|
1504
|
-
parameters: {
|
|
1505
|
-
type: 'object',
|
|
1506
|
-
properties: {
|
|
1507
|
-
gameType: {
|
|
1508
|
-
type: 'string',
|
|
1509
|
-
description:
|
|
1510
|
-
'Game type (e.g., "rock-paper-scissors", "chess", "tic-tac-toe")',
|
|
1511
|
-
},
|
|
1512
|
-
amount: {
|
|
1513
|
-
type: 'number',
|
|
1514
|
-
description: 'Challenge amount in USDC dollars (e.g., 5.00 for $5)',
|
|
1515
|
-
},
|
|
1516
|
-
targetUsername: {
|
|
1517
|
-
type: 'string',
|
|
1518
|
-
description: 'Username of the user to challenge',
|
|
1519
|
-
},
|
|
1520
|
-
targetUserId: {
|
|
1521
|
-
type: 'string',
|
|
1522
|
-
description: 'User ID of the user to challenge',
|
|
1523
|
-
},
|
|
1524
|
-
},
|
|
1525
|
-
required: ['gameType', 'amount'],
|
|
1526
|
-
additionalProperties: false,
|
|
1527
|
-
},
|
|
1528
|
-
async execute(_, params) {
|
|
1529
|
-
const c = await requireClient();
|
|
1530
|
-
if ('error' in c) return c.error;
|
|
1531
|
-
const targetUsername =
|
|
1532
|
-
typeof params.targetUsername === 'string'
|
|
1533
|
-
? params.targetUsername
|
|
1534
|
-
: undefined;
|
|
1535
|
-
const targetUserId =
|
|
1536
|
-
typeof params.targetUserId === 'string'
|
|
1537
|
-
? params.targetUserId
|
|
1538
|
-
: undefined;
|
|
1539
|
-
if (!targetUsername && !targetUserId) {
|
|
1540
|
-
return textResult(
|
|
1541
|
-
'Must provide either targetUsername or targetUserId.',
|
|
1542
|
-
true,
|
|
1543
|
-
);
|
|
1544
|
-
}
|
|
1545
|
-
try {
|
|
1546
|
-
const amountMinor = Math.round(Number(params.amount ?? 0) * 1_000_000);
|
|
1547
|
-
const result = await c.sdk.challenges.create({
|
|
1548
|
-
gameType: String(params.gameType ?? ''),
|
|
1549
|
-
amount: amountMinor,
|
|
1550
|
-
targetUsername,
|
|
1551
|
-
targetUserId,
|
|
1552
|
-
});
|
|
1553
|
-
return textResult(
|
|
1554
|
-
JSON.stringify(
|
|
1555
|
-
{
|
|
1556
|
-
...result,
|
|
1557
|
-
amountFormatted: `$${Number(params.amount).toFixed(2)}`,
|
|
1558
|
-
hint: 'The challenged user will receive a notification. When they accept, a lobby is created automatically.',
|
|
1559
|
-
},
|
|
1560
|
-
null,
|
|
1561
|
-
2,
|
|
1562
|
-
),
|
|
1563
|
-
);
|
|
1564
|
-
} catch (err) {
|
|
1565
|
-
return textResult(
|
|
1566
|
-
`Failed to create challenge: ${err instanceof Error ? err.message : String(err)}`,
|
|
1567
|
-
true,
|
|
1568
|
-
);
|
|
1569
|
-
}
|
|
1570
|
-
},
|
|
1571
|
-
});
|
|
1572
|
-
|
|
1573
|
-
api.registerTool({
|
|
1574
|
-
name: 'dim_accept_challenge',
|
|
1575
|
-
description:
|
|
1576
|
-
'Accept a challenge that was sent to you. Creates a lobby with the challenge amount and returns the lobby/game info.',
|
|
1577
|
-
parameters: {
|
|
1578
|
-
type: 'object',
|
|
1579
|
-
properties: {
|
|
1580
|
-
challengeId: {
|
|
1581
|
-
type: 'string',
|
|
1582
|
-
description: 'The challenge ID to accept',
|
|
1583
|
-
},
|
|
1584
|
-
},
|
|
1585
|
-
required: ['challengeId'],
|
|
1586
|
-
additionalProperties: false,
|
|
1587
|
-
},
|
|
1588
|
-
async execute(_, params) {
|
|
1589
|
-
const c = await requireClient();
|
|
1590
|
-
if ('error' in c) return c.error;
|
|
1591
|
-
try {
|
|
1592
|
-
const result = await c.sdk.challenges.accept(
|
|
1593
|
-
String(params.challengeId ?? ''),
|
|
1594
|
-
);
|
|
1595
|
-
return textResult(
|
|
1596
|
-
JSON.stringify(
|
|
1597
|
-
{
|
|
1598
|
-
...result,
|
|
1599
|
-
hint: result.gameId
|
|
1600
|
-
? `Game started! Use dim_get_game_state with gameId "${result.gameId}".`
|
|
1601
|
-
: `Lobby created: ${result.lobbyId}. Use dim_join_queue to start matchmaking.`,
|
|
1602
|
-
},
|
|
1603
|
-
null,
|
|
1604
|
-
2,
|
|
1605
|
-
),
|
|
1606
|
-
);
|
|
1607
|
-
} catch (err) {
|
|
1608
|
-
return textResult(
|
|
1609
|
-
`Failed to accept challenge: ${err instanceof Error ? err.message : String(err)}`,
|
|
1610
|
-
true,
|
|
1611
|
-
);
|
|
1612
|
-
}
|
|
1613
|
-
},
|
|
1614
|
-
});
|
|
1615
|
-
|
|
1616
|
-
// --- Referrals ---
|
|
1617
|
-
api.registerTool({
|
|
1618
|
-
name: 'dim_get_referral_summary',
|
|
1619
|
-
description:
|
|
1620
|
-
'Get your referral summary including your referral code (your username), referral link, total referrals per level, and earnings (pending + claimed). DIM has a 3-level referral system: Level 1 = 30% of game fees, Level 2 = 3%, Level 3 = 2%.',
|
|
1621
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
1622
|
-
async execute() {
|
|
1623
|
-
const c = await requireClient();
|
|
1624
|
-
if ('error' in c) return c.error;
|
|
1625
|
-
try {
|
|
1626
|
-
const summary = await c.sdk.referrals.getSummary();
|
|
1627
|
-
const pendingDollars = summary.earnings.pending / 1_000_000;
|
|
1628
|
-
const claimedDollars = summary.earnings.claimed / 1_000_000;
|
|
1629
|
-
const result: Record<string, unknown> = {
|
|
1630
|
-
...summary,
|
|
1631
|
-
pendingFormatted: `$${pendingDollars.toFixed(2)}`,
|
|
1632
|
-
claimedFormatted: `$${claimedDollars.toFixed(2)}`,
|
|
1633
|
-
referralRates: {
|
|
1634
|
-
level1: '30% of referred user game fees',
|
|
1635
|
-
level2: '3% of level-2 referral game fees',
|
|
1636
|
-
level3: '2% of level-3 referral game fees',
|
|
1637
|
-
},
|
|
1638
|
-
referredUserBenefit: '10% fee discount (0.9% instead of 1%)',
|
|
1639
|
-
};
|
|
1640
|
-
if (!summary.hasReferrer) {
|
|
1641
|
-
result.applyReferralHint =
|
|
1642
|
-
'You have no referrer yet. Use dim_apply_referral_code to apply one and get a 10% fee discount.';
|
|
1643
292
|
}
|
|
1644
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
1645
|
-
} catch (err) {
|
|
1646
|
-
return textResult(
|
|
1647
|
-
`Failed to get referral summary: ${err instanceof Error ? err.message : String(err)}`,
|
|
1648
|
-
true,
|
|
1649
|
-
);
|
|
1650
|
-
}
|
|
1651
|
-
},
|
|
1652
|
-
});
|
|
1653
|
-
|
|
1654
|
-
api.registerTool({
|
|
1655
|
-
name: 'dim_get_referral_tree',
|
|
1656
|
-
description:
|
|
1657
|
-
'Get your referral tree at a specific level. Level 1 = direct referrals, Level 2 = referrals of your referrals, Level 3 = one more level deep.',
|
|
1658
|
-
parameters: {
|
|
1659
|
-
type: 'object',
|
|
1660
|
-
properties: {
|
|
1661
|
-
level: {
|
|
1662
|
-
type: 'string',
|
|
1663
|
-
enum: ['1', '2', '3'],
|
|
1664
|
-
description: 'Referral level to view (1, 2, or 3)',
|
|
1665
|
-
},
|
|
1666
|
-
limit: {
|
|
1667
|
-
type: 'number',
|
|
1668
|
-
description: 'Max results (default: 50, max: 200)',
|
|
1669
|
-
},
|
|
1670
|
-
cursor: { type: 'string', description: 'Pagination cursor' },
|
|
1671
293
|
},
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
},
|
|
1675
|
-
async execute(_, params) {
|
|
1676
|
-
const c = await requireClient();
|
|
1677
|
-
if ('error' in c) return c.error;
|
|
1678
|
-
try {
|
|
1679
|
-
const tree = await c.sdk.referrals.getTree({
|
|
1680
|
-
level: parseInt(String(params.level ?? '1'), 10) as 1 | 2 | 3,
|
|
1681
|
-
limit: typeof params.limit === 'number' ? params.limit : 50,
|
|
1682
|
-
cursor: typeof params.cursor === 'string' ? params.cursor : undefined,
|
|
1683
|
-
});
|
|
1684
|
-
return textResult(JSON.stringify(tree, null, 2));
|
|
1685
|
-
} catch (err) {
|
|
1686
|
-
return textResult(
|
|
1687
|
-
`Failed to get referral tree: ${err instanceof Error ? err.message : String(err)}`,
|
|
1688
|
-
true,
|
|
1689
|
-
);
|
|
1690
|
-
}
|
|
1691
|
-
},
|
|
1692
|
-
});
|
|
1693
|
-
|
|
1694
|
-
api.registerTool({
|
|
1695
|
-
name: 'dim_get_referral_rewards',
|
|
1696
|
-
description:
|
|
1697
|
-
'Get your referral reward history. Filter by status: PENDING (unclaimed), CLAIMED (already claimed), CANCELLED.',
|
|
1698
|
-
parameters: {
|
|
1699
|
-
type: 'object',
|
|
1700
|
-
properties: {
|
|
1701
|
-
status: {
|
|
1702
|
-
type: 'string',
|
|
1703
|
-
enum: ['PENDING', 'CLAIMED', 'CANCELLED'],
|
|
1704
|
-
description: 'Filter by reward status',
|
|
1705
|
-
},
|
|
1706
|
-
limit: {
|
|
1707
|
-
type: 'number',
|
|
1708
|
-
description: 'Max results (default: 50, max: 200)',
|
|
1709
|
-
},
|
|
1710
|
-
cursor: { type: 'string', description: 'Pagination cursor' },
|
|
1711
|
-
},
|
|
1712
|
-
additionalProperties: false,
|
|
1713
|
-
},
|
|
1714
|
-
async execute(_, params) {
|
|
1715
|
-
const c = await requireClient();
|
|
1716
|
-
if ('error' in c) return c.error;
|
|
1717
|
-
try {
|
|
1718
|
-
const rewards = await c.sdk.referrals.getRewards({
|
|
1719
|
-
status: params.status as
|
|
1720
|
-
| 'PENDING'
|
|
1721
|
-
| 'CLAIMED'
|
|
1722
|
-
| 'CANCELLED'
|
|
1723
|
-
| undefined,
|
|
1724
|
-
limit: typeof params.limit === 'number' ? params.limit : 50,
|
|
1725
|
-
cursor: typeof params.cursor === 'string' ? params.cursor : undefined,
|
|
1726
|
-
});
|
|
1727
|
-
return textResult(JSON.stringify(rewards, null, 2));
|
|
1728
|
-
} catch (err) {
|
|
1729
|
-
return textResult(
|
|
1730
|
-
`Failed to get referral rewards: ${err instanceof Error ? err.message : String(err)}`,
|
|
1731
|
-
true,
|
|
1732
|
-
);
|
|
1733
|
-
}
|
|
1734
|
-
},
|
|
1735
|
-
});
|
|
1736
|
-
|
|
1737
|
-
api.registerTool({
|
|
1738
|
-
name: 'dim_claim_referral_rewards',
|
|
1739
|
-
description:
|
|
1740
|
-
'Claim all pending referral rewards. USDC is transferred from escrow to your wallet. Returns the claimed amount and transaction signature.',
|
|
1741
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
1742
|
-
async execute() {
|
|
1743
|
-
const c = await requireClient();
|
|
1744
|
-
if ('error' in c) return c.error;
|
|
1745
|
-
try {
|
|
1746
|
-
const result = await c.sdk.referrals.claimRewards();
|
|
1747
|
-
const claimedDollars = result.claimedAmount / 1_000_000;
|
|
1748
|
-
return textResult(
|
|
1749
|
-
JSON.stringify(
|
|
1750
|
-
{ ...result, claimedFormatted: `$${claimedDollars.toFixed(2)}` },
|
|
1751
|
-
null,
|
|
1752
|
-
2,
|
|
1753
|
-
),
|
|
1754
|
-
);
|
|
1755
|
-
} catch (err) {
|
|
1756
|
-
return textResult(
|
|
1757
|
-
`Failed to claim rewards: ${err instanceof Error ? err.message : String(err)}`,
|
|
1758
|
-
true,
|
|
1759
|
-
);
|
|
1760
|
-
}
|
|
1761
|
-
},
|
|
1762
|
-
});
|
|
1763
|
-
|
|
1764
|
-
api.registerTool({
|
|
1765
|
-
name: 'dim_apply_referral_code',
|
|
1766
|
-
description:
|
|
1767
|
-
"Apply a referral code to your account (another user's username). Can only be applied once — if you already have a referrer, this will fail. Gives you a 10% fee discount.",
|
|
1768
|
-
parameters: {
|
|
1769
|
-
type: 'object',
|
|
1770
|
-
properties: {
|
|
1771
|
-
referralCode: {
|
|
1772
|
-
type: 'string',
|
|
1773
|
-
description: "The referral code to apply (another user's username)",
|
|
1774
|
-
},
|
|
1775
|
-
},
|
|
1776
|
-
required: ['referralCode'],
|
|
1777
|
-
additionalProperties: false,
|
|
1778
|
-
},
|
|
1779
|
-
async execute(_, params) {
|
|
1780
|
-
const c = await requireClient();
|
|
1781
|
-
if ('error' in c) return c.error;
|
|
1782
|
-
try {
|
|
1783
|
-
const result = await c.sdk.referrals.applyCode(
|
|
1784
|
-
String(params.referralCode ?? ''),
|
|
1785
|
-
);
|
|
1786
|
-
return textResult(
|
|
1787
|
-
JSON.stringify(
|
|
1788
|
-
{
|
|
1789
|
-
...result,
|
|
1790
|
-
message: `Referral code applied! ${result.referrerUsername} is now your referrer. You get a 10% fee discount on all games.`,
|
|
1791
|
-
},
|
|
1792
|
-
null,
|
|
1793
|
-
2,
|
|
1794
|
-
),
|
|
1795
|
-
);
|
|
1796
|
-
} catch (err) {
|
|
1797
|
-
return textResult(
|
|
1798
|
-
`Failed to apply referral code: ${err instanceof Error ? err.message : String(err)}`,
|
|
1799
|
-
true,
|
|
1800
|
-
);
|
|
1801
|
-
}
|
|
1802
|
-
},
|
|
1803
|
-
});
|
|
1804
|
-
|
|
1805
|
-
// --- Support ---
|
|
1806
|
-
api.registerTool({
|
|
1807
|
-
name: 'dim_create_support_ticket',
|
|
1808
|
-
description:
|
|
1809
|
-
'Create a support ticket to contact the DIM team. Use this when you need help with your account, payments, games, bugs, or any other issue. Categories: BUG, FEATURE_REQUEST, QUESTION, ACCOUNT, PAYMENT, GAME, TECHNICAL, OTHER.',
|
|
1810
|
-
parameters: {
|
|
1811
|
-
type: 'object',
|
|
1812
|
-
properties: {
|
|
1813
|
-
message: {
|
|
1814
|
-
type: 'string',
|
|
1815
|
-
description: 'Describe the issue or question (max 2000 chars)',
|
|
1816
|
-
},
|
|
1817
|
-
category: {
|
|
1818
|
-
type: 'string',
|
|
1819
|
-
enum: [
|
|
1820
|
-
'BUG',
|
|
1821
|
-
'FEATURE_REQUEST',
|
|
1822
|
-
'QUESTION',
|
|
1823
|
-
'ACCOUNT',
|
|
1824
|
-
'PAYMENT',
|
|
1825
|
-
'GAME',
|
|
1826
|
-
'TECHNICAL',
|
|
1827
|
-
'OTHER',
|
|
1828
|
-
],
|
|
1829
|
-
description: 'Ticket category (default: OTHER)',
|
|
1830
|
-
},
|
|
1831
|
-
subject: {
|
|
1832
|
-
type: 'string',
|
|
1833
|
-
description:
|
|
1834
|
-
'Short subject line (auto-generated from category if omitted)',
|
|
1835
|
-
},
|
|
1836
|
-
},
|
|
1837
|
-
required: ['message'],
|
|
1838
|
-
additionalProperties: false,
|
|
1839
|
-
},
|
|
1840
|
-
async execute(_, params) {
|
|
1841
|
-
const c = await requireClient();
|
|
1842
|
-
if ('error' in c) return c.error;
|
|
1843
|
-
try {
|
|
1844
|
-
const ticket = await c.sdk.support.create({
|
|
1845
|
-
message: String(params.message ?? ''),
|
|
1846
|
-
category: params.category as
|
|
1847
|
-
| 'BUG'
|
|
1848
|
-
| 'FEATURE_REQUEST'
|
|
1849
|
-
| 'QUESTION'
|
|
1850
|
-
| 'ACCOUNT'
|
|
1851
|
-
| 'PAYMENT'
|
|
1852
|
-
| 'GAME'
|
|
1853
|
-
| 'TECHNICAL'
|
|
1854
|
-
| 'OTHER'
|
|
1855
|
-
| undefined,
|
|
1856
|
-
subject:
|
|
1857
|
-
typeof params.subject === 'string' ? params.subject : undefined,
|
|
1858
|
-
});
|
|
1859
|
-
return textResult(
|
|
1860
|
-
JSON.stringify(
|
|
1861
|
-
{
|
|
1862
|
-
...ticket,
|
|
1863
|
-
hint: 'Ticket created successfully. You can check its status with dim_get_my_tickets or add follow-up messages with dim_add_ticket_message.',
|
|
1864
|
-
},
|
|
1865
|
-
null,
|
|
1866
|
-
2,
|
|
1867
|
-
),
|
|
1868
|
-
);
|
|
1869
|
-
} catch (err) {
|
|
1870
|
-
return textResult(
|
|
1871
|
-
`Failed to create support ticket: ${err instanceof Error ? err.message : String(err)}`,
|
|
1872
|
-
true,
|
|
1873
|
-
);
|
|
1874
|
-
}
|
|
1875
|
-
},
|
|
1876
|
-
});
|
|
1877
|
-
|
|
1878
|
-
api.registerTool({
|
|
1879
|
-
name: 'dim_get_my_tickets',
|
|
1880
|
-
description:
|
|
1881
|
-
'Get your support tickets. Filter by status (OPEN, IN_PROGRESS, WAITING_REPLY, RESOLVED, CLOSED) or category.',
|
|
1882
|
-
parameters: {
|
|
1883
|
-
type: 'object',
|
|
1884
|
-
properties: {
|
|
1885
|
-
status: {
|
|
1886
|
-
type: 'string',
|
|
1887
|
-
enum: ['OPEN', 'IN_PROGRESS', 'WAITING_REPLY', 'RESOLVED', 'CLOSED'],
|
|
1888
|
-
description: 'Filter by ticket status',
|
|
1889
|
-
},
|
|
1890
|
-
category: {
|
|
1891
|
-
type: 'string',
|
|
1892
|
-
enum: [
|
|
1893
|
-
'BUG',
|
|
1894
|
-
'FEATURE_REQUEST',
|
|
1895
|
-
'QUESTION',
|
|
1896
|
-
'ACCOUNT',
|
|
1897
|
-
'PAYMENT',
|
|
1898
|
-
'GAME',
|
|
1899
|
-
'TECHNICAL',
|
|
1900
|
-
'OTHER',
|
|
1901
|
-
],
|
|
1902
|
-
description: 'Filter by category',
|
|
1903
|
-
},
|
|
1904
|
-
page: { type: 'number', description: 'Page number (default: 1)' },
|
|
1905
|
-
limit: {
|
|
1906
|
-
type: 'number',
|
|
1907
|
-
description: 'Results per page (default: 10)',
|
|
1908
|
-
},
|
|
1909
|
-
},
|
|
1910
|
-
additionalProperties: false,
|
|
1911
|
-
},
|
|
1912
|
-
async execute(_, params) {
|
|
1913
|
-
const c = await requireClient();
|
|
1914
|
-
if ('error' in c) return c.error;
|
|
1915
|
-
try {
|
|
1916
|
-
const tickets = await c.sdk.support.getMyTickets({
|
|
1917
|
-
status: params.status as any,
|
|
1918
|
-
category: params.category as any,
|
|
1919
|
-
page: typeof params.page === 'number' ? params.page : undefined,
|
|
1920
|
-
limit: typeof params.limit === 'number' ? params.limit : undefined,
|
|
1921
|
-
});
|
|
1922
|
-
return textResult(JSON.stringify(tickets, null, 2));
|
|
1923
|
-
} catch (err) {
|
|
1924
|
-
return textResult(
|
|
1925
|
-
`Failed to get tickets: ${err instanceof Error ? err.message : String(err)}`,
|
|
1926
|
-
true,
|
|
1927
|
-
);
|
|
1928
|
-
}
|
|
1929
|
-
},
|
|
1930
|
-
});
|
|
1931
|
-
|
|
1932
|
-
api.registerTool({
|
|
1933
|
-
name: 'dim_get_ticket',
|
|
1934
|
-
description:
|
|
1935
|
-
'Get a specific support ticket with all messages. You can only view your own tickets.',
|
|
1936
|
-
parameters: {
|
|
1937
|
-
type: 'object',
|
|
1938
|
-
properties: {
|
|
1939
|
-
ticketId: { type: 'string', description: 'The ticket ID' },
|
|
1940
|
-
},
|
|
1941
|
-
required: ['ticketId'],
|
|
1942
|
-
additionalProperties: false,
|
|
1943
|
-
},
|
|
1944
|
-
async execute(_, params) {
|
|
1945
|
-
const c = await requireClient();
|
|
1946
|
-
if ('error' in c) return c.error;
|
|
1947
|
-
try {
|
|
1948
|
-
const ticket = await c.sdk.support.getMyTicketById(
|
|
1949
|
-
String(params.ticketId ?? ''),
|
|
1950
|
-
);
|
|
1951
|
-
return textResult(JSON.stringify(ticket, null, 2));
|
|
1952
|
-
} catch (err) {
|
|
1953
|
-
return textResult(
|
|
1954
|
-
`Failed to get ticket: ${err instanceof Error ? err.message : String(err)}`,
|
|
1955
|
-
true,
|
|
1956
|
-
);
|
|
1957
|
-
}
|
|
1958
|
-
},
|
|
1959
|
-
});
|
|
1960
|
-
|
|
1961
|
-
api.registerTool({
|
|
1962
|
-
name: 'dim_add_ticket_message',
|
|
1963
|
-
description:
|
|
1964
|
-
'Add a follow-up message to an existing support ticket. You can only message your own tickets.',
|
|
1965
|
-
parameters: {
|
|
1966
|
-
type: 'object',
|
|
1967
|
-
properties: {
|
|
1968
|
-
ticketId: { type: 'string', description: 'The ticket ID' },
|
|
1969
|
-
message: {
|
|
1970
|
-
type: 'string',
|
|
1971
|
-
description: 'Your follow-up message (max 2000 chars)',
|
|
1972
|
-
},
|
|
1973
|
-
},
|
|
1974
|
-
required: ['ticketId', 'message'],
|
|
1975
|
-
additionalProperties: false,
|
|
1976
|
-
},
|
|
1977
|
-
async execute(_, params) {
|
|
1978
|
-
const c = await requireClient();
|
|
1979
|
-
if ('error' in c) return c.error;
|
|
1980
|
-
try {
|
|
1981
|
-
const msg = await c.sdk.support.addMessage(
|
|
1982
|
-
String(params.ticketId ?? ''),
|
|
1983
|
-
String(params.message ?? ''),
|
|
1984
|
-
);
|
|
1985
|
-
return textResult(
|
|
1986
|
-
JSON.stringify(
|
|
1987
|
-
{ ...msg, hint: 'Message added. The DIM team will be notified.' },
|
|
1988
|
-
null,
|
|
1989
|
-
2,
|
|
1990
|
-
),
|
|
1991
|
-
);
|
|
1992
|
-
} catch (err) {
|
|
1993
|
-
return textResult(
|
|
1994
|
-
`Failed to add message: ${err instanceof Error ? err.message : String(err)}`,
|
|
1995
|
-
true,
|
|
1996
|
-
);
|
|
1997
|
-
}
|
|
1998
|
-
},
|
|
1999
|
-
});
|
|
2000
|
-
|
|
2001
|
-
api.registerTool({
|
|
2002
|
-
name: 'dim_close_ticket',
|
|
2003
|
-
description:
|
|
2004
|
-
'Close a support ticket. Only close if your issue has been resolved.',
|
|
2005
|
-
parameters: {
|
|
2006
|
-
type: 'object',
|
|
2007
|
-
properties: {
|
|
2008
|
-
ticketId: { type: 'string', description: 'The ticket ID to close' },
|
|
2009
|
-
},
|
|
2010
|
-
required: ['ticketId'],
|
|
2011
|
-
additionalProperties: false,
|
|
2012
|
-
},
|
|
2013
|
-
async execute(_, params) {
|
|
2014
|
-
const c = await requireClient();
|
|
2015
|
-
if ('error' in c) return c.error;
|
|
2016
|
-
try {
|
|
2017
|
-
const ticket = await c.sdk.support.closeTicket(
|
|
2018
|
-
String(params.ticketId ?? ''),
|
|
2019
|
-
);
|
|
2020
|
-
return textResult(JSON.stringify(ticket, null, 2));
|
|
2021
|
-
} catch (err) {
|
|
2022
|
-
return textResult(
|
|
2023
|
-
`Failed to close ticket: ${err instanceof Error ? err.message : String(err)}`,
|
|
2024
|
-
true,
|
|
2025
|
-
);
|
|
2026
|
-
}
|
|
2027
|
-
},
|
|
2028
|
-
});
|
|
2029
|
-
|
|
2030
|
-
// --- Markets ---
|
|
2031
|
-
api.registerTool({
|
|
2032
|
-
name: 'dim_get_market',
|
|
2033
|
-
description:
|
|
2034
|
-
'Get the prediction market state for an active game. Returns share prices (implied probabilities), total collateral locked, total volume, and resolution status.',
|
|
2035
|
-
parameters: {
|
|
2036
|
-
type: 'object',
|
|
2037
|
-
properties: {
|
|
2038
|
-
gameId: {
|
|
2039
|
-
type: 'string',
|
|
2040
|
-
description: 'The game ID to get market state for',
|
|
2041
|
-
},
|
|
2042
|
-
},
|
|
2043
|
-
required: ['gameId'],
|
|
2044
|
-
additionalProperties: false,
|
|
2045
|
-
},
|
|
2046
|
-
async execute(_, params) {
|
|
2047
|
-
const c = await requireClient();
|
|
2048
|
-
if ('error' in c) return c.error;
|
|
2049
|
-
try {
|
|
2050
|
-
const market = await c.sdk.markets.getMarket(
|
|
2051
|
-
String(params.gameId ?? ''),
|
|
2052
|
-
);
|
|
2053
|
-
return textResult(JSON.stringify(market, null, 2));
|
|
2054
|
-
} catch (err) {
|
|
2055
|
-
return textResult(
|
|
2056
|
-
`Failed to get market: ${err instanceof Error ? err.message : String(err)}`,
|
|
2057
|
-
true,
|
|
2058
|
-
);
|
|
2059
|
-
}
|
|
2060
|
-
},
|
|
2061
|
-
});
|
|
2062
|
-
|
|
2063
|
-
api.registerTool({
|
|
2064
|
-
name: 'dim_buy_shares',
|
|
2065
|
-
description:
|
|
2066
|
-
'Buy shares in a prediction market outcome. Pick which player you think will win and how much to bet. Amount is in USDC dollars (e.g., 1.00 for $1.00).',
|
|
2067
|
-
parameters: {
|
|
2068
|
-
type: 'object',
|
|
2069
|
-
properties: {
|
|
2070
|
-
gameId: { type: 'string', description: 'The game ID' },
|
|
2071
|
-
outcomeId: {
|
|
2072
|
-
type: 'string',
|
|
2073
|
-
description: 'The outcome (player user ID) to buy shares of',
|
|
2074
|
-
},
|
|
2075
|
-
amount: {
|
|
2076
|
-
type: 'number',
|
|
2077
|
-
description: 'Amount in USDC dollars (e.g., 1.00 for $1.00)',
|
|
2078
|
-
},
|
|
2079
|
-
},
|
|
2080
|
-
required: ['gameId', 'outcomeId', 'amount'],
|
|
2081
|
-
additionalProperties: false,
|
|
2082
|
-
},
|
|
2083
|
-
async execute(_, params) {
|
|
2084
|
-
const c = await requireClient();
|
|
2085
|
-
if ('error' in c) return c.error;
|
|
2086
|
-
const amount = Number(params.amount ?? 0);
|
|
2087
|
-
const gameId = String(params.gameId ?? '');
|
|
2088
|
-
const outcomeId = String(params.outcomeId ?? '');
|
|
2089
|
-
const limitErr = checkSpendLimit(c, amount);
|
|
2090
|
-
if (limitErr) return textResult(limitErr, true);
|
|
2091
|
-
try {
|
|
2092
|
-
const amountMinor = Math.round(amount * 1_000_000);
|
|
2093
|
-
const { transaction: txBase64 } = await c.sdk.markets.prepareBuyOrder(
|
|
2094
|
-
gameId,
|
|
2095
|
-
outcomeId,
|
|
2096
|
-
amountMinor,
|
|
2097
|
-
);
|
|
2098
|
-
const tx = Transaction.from(Buffer.from(txBase64, 'base64'));
|
|
2099
|
-
tx.partialSign(c.getKeypair());
|
|
2100
|
-
const signedTxBase64 = Buffer.from(tx.serialize()).toString('base64');
|
|
2101
|
-
const result = await c.sdk.markets.submitBuyOrder(
|
|
2102
|
-
gameId,
|
|
2103
|
-
signedTxBase64,
|
|
2104
|
-
outcomeId,
|
|
2105
|
-
amountMinor,
|
|
2106
|
-
);
|
|
2107
|
-
c.recordSpend(amountMinor);
|
|
2108
|
-
return textResult(
|
|
2109
|
-
JSON.stringify(
|
|
2110
|
-
{
|
|
2111
|
-
success: true,
|
|
2112
|
-
tradeId: result.tradeId,
|
|
2113
|
-
sharesReceived: result.sharesReceived,
|
|
2114
|
-
costPerShare: result.costPerShare,
|
|
2115
|
-
amountSpent: `$${amount.toFixed(2)}`,
|
|
2116
|
-
},
|
|
2117
|
-
null,
|
|
2118
|
-
2,
|
|
2119
|
-
),
|
|
2120
|
-
);
|
|
2121
|
-
} catch (err) {
|
|
2122
|
-
return textResult(
|
|
2123
|
-
`Failed to buy shares: ${err instanceof Error ? err.message : String(err)}`,
|
|
2124
|
-
true,
|
|
2125
|
-
);
|
|
2126
|
-
}
|
|
2127
|
-
},
|
|
2128
|
-
});
|
|
2129
|
-
|
|
2130
|
-
api.registerTool({
|
|
2131
|
-
name: 'dim_sell_shares',
|
|
2132
|
-
description:
|
|
2133
|
-
'Sell shares to exit a prediction market position. Shares are in minor units (1 share = 1,000,000 minor units).',
|
|
2134
|
-
parameters: {
|
|
2135
|
-
type: 'object',
|
|
2136
|
-
properties: {
|
|
2137
|
-
gameId: { type: 'string', description: 'The game ID' },
|
|
2138
|
-
outcomeId: {
|
|
2139
|
-
type: 'string',
|
|
2140
|
-
description: 'The outcome to sell shares of',
|
|
2141
|
-
},
|
|
2142
|
-
shares: {
|
|
2143
|
-
type: 'number',
|
|
2144
|
-
description:
|
|
2145
|
-
'Number of shares to sell in minor units (1 share = 1,000,000)',
|
|
2146
|
-
},
|
|
2147
|
-
},
|
|
2148
|
-
required: ['gameId', 'outcomeId', 'shares'],
|
|
2149
|
-
additionalProperties: false,
|
|
2150
|
-
},
|
|
2151
|
-
async execute(_, params) {
|
|
2152
|
-
const c = await requireClient();
|
|
2153
|
-
if ('error' in c) return c.error;
|
|
2154
|
-
try {
|
|
2155
|
-
const result = await c.sdk.markets.sellShares(
|
|
2156
|
-
String(params.gameId ?? ''),
|
|
2157
|
-
String(params.outcomeId ?? ''),
|
|
2158
|
-
Number(params.shares ?? 0),
|
|
2159
|
-
);
|
|
2160
|
-
return textResult(
|
|
2161
|
-
JSON.stringify(
|
|
2162
|
-
{
|
|
2163
|
-
success: true,
|
|
2164
|
-
tradeId: result.tradeId,
|
|
2165
|
-
amountReceived: result.amountReceived,
|
|
2166
|
-
pricePerShare: result.pricePerShare,
|
|
2167
|
-
newPrices: result.newPrices,
|
|
2168
|
-
},
|
|
2169
|
-
null,
|
|
2170
|
-
2,
|
|
2171
|
-
),
|
|
2172
|
-
);
|
|
2173
|
-
} catch (err) {
|
|
2174
|
-
return textResult(
|
|
2175
|
-
`Failed to sell shares: ${err instanceof Error ? err.message : String(err)}`,
|
|
2176
|
-
true,
|
|
2177
|
-
);
|
|
2178
|
-
}
|
|
2179
|
-
},
|
|
2180
|
-
});
|
|
2181
|
-
|
|
2182
|
-
api.registerTool({
|
|
2183
|
-
name: 'dim_get_positions',
|
|
2184
|
-
description:
|
|
2185
|
-
'Get your current prediction market positions for a game. Shows shares held, average cost, current value, and unrealized profit/loss.',
|
|
2186
|
-
parameters: {
|
|
2187
|
-
type: 'object',
|
|
2188
|
-
properties: { gameId: { type: 'string', description: 'The game ID' } },
|
|
2189
|
-
required: ['gameId'],
|
|
2190
|
-
additionalProperties: false,
|
|
2191
|
-
},
|
|
2192
|
-
async execute(_, params) {
|
|
2193
|
-
const c = await requireClient();
|
|
2194
|
-
if ('error' in c) return c.error;
|
|
2195
|
-
try {
|
|
2196
|
-
const positions = await c.sdk.markets.getMyPositions(
|
|
2197
|
-
String(params.gameId ?? ''),
|
|
2198
|
-
);
|
|
2199
|
-
return textResult(JSON.stringify(positions, null, 2));
|
|
2200
|
-
} catch (err) {
|
|
2201
|
-
return textResult(
|
|
2202
|
-
`Failed to get positions: ${err instanceof Error ? err.message : String(err)}`,
|
|
2203
|
-
true,
|
|
2204
|
-
);
|
|
2205
|
-
}
|
|
2206
|
-
},
|
|
2207
|
-
});
|
|
2208
|
-
|
|
2209
|
-
api.registerTool({
|
|
2210
|
-
name: 'dim_redeem_shares',
|
|
2211
|
-
description:
|
|
2212
|
-
'Redeem winning shares after a prediction market has been resolved. Winners split the pool pro-rata by shares held, minus a 3% fee.',
|
|
2213
|
-
parameters: {
|
|
2214
|
-
type: 'object',
|
|
2215
|
-
properties: {
|
|
2216
|
-
gameId: {
|
|
2217
|
-
type: 'string',
|
|
2218
|
-
description: 'The game ID to redeem shares for',
|
|
2219
|
-
},
|
|
2220
|
-
},
|
|
2221
|
-
required: ['gameId'],
|
|
2222
|
-
additionalProperties: false,
|
|
2223
|
-
},
|
|
2224
|
-
async execute(_, params) {
|
|
2225
|
-
const c = await requireClient();
|
|
2226
|
-
if ('error' in c) return c.error;
|
|
2227
|
-
try {
|
|
2228
|
-
const result = await c.sdk.markets.redeemShares(
|
|
2229
|
-
String(params.gameId ?? ''),
|
|
2230
|
-
);
|
|
2231
|
-
return textResult(JSON.stringify(result, null, 2));
|
|
2232
|
-
} catch (err) {
|
|
2233
|
-
return textResult(
|
|
2234
|
-
`Failed to redeem shares: ${err instanceof Error ? err.message : String(err)}`,
|
|
2235
|
-
true,
|
|
2236
|
-
);
|
|
2237
|
-
}
|
|
2238
|
-
},
|
|
2239
|
-
});
|
|
2240
|
-
|
|
2241
|
-
api.registerTool({
|
|
2242
|
-
name: 'dim_get_market_analytics',
|
|
2243
|
-
description:
|
|
2244
|
-
'Get platform analytics for prediction markets. Shows total markets, orders, volume, fees, unique traders, and daily trends. Requires admin privileges.',
|
|
2245
|
-
parameters: {
|
|
2246
|
-
type: 'object',
|
|
2247
|
-
properties: {
|
|
2248
|
-
type: {
|
|
2249
|
-
type: 'string',
|
|
2250
|
-
enum: ['overview', 'daily', 'markets'],
|
|
2251
|
-
description:
|
|
2252
|
-
'overview = aggregate totals, daily = per-day breakdown, markets = paginated list of all markets',
|
|
2253
|
-
},
|
|
2254
|
-
days: {
|
|
2255
|
-
type: 'number',
|
|
2256
|
-
description: 'Number of days for daily stats (default 30)',
|
|
2257
|
-
},
|
|
2258
|
-
page: {
|
|
2259
|
-
type: 'number',
|
|
2260
|
-
description: 'Page number for markets list (default 1)',
|
|
2261
|
-
},
|
|
2262
|
-
limit: {
|
|
2263
|
-
type: 'number',
|
|
2264
|
-
description: 'Items per page for markets list (default 20)',
|
|
2265
|
-
},
|
|
2266
|
-
},
|
|
2267
|
-
required: ['type'],
|
|
2268
|
-
additionalProperties: false,
|
|
2269
|
-
},
|
|
2270
|
-
async execute(_, params) {
|
|
2271
|
-
const c = await requireClient();
|
|
2272
|
-
if ('error' in c) return c.error;
|
|
2273
|
-
try {
|
|
2274
|
-
let data: unknown;
|
|
2275
|
-
switch (String(params.type)) {
|
|
2276
|
-
case 'overview':
|
|
2277
|
-
data = await c.sdk.markets.getAdminStats();
|
|
2278
|
-
break;
|
|
2279
|
-
case 'daily':
|
|
2280
|
-
data = await c.sdk.markets.getAdminDailyStats(
|
|
2281
|
-
typeof params.days === 'number' ? params.days : 30,
|
|
2282
|
-
);
|
|
2283
|
-
break;
|
|
2284
|
-
case 'markets':
|
|
2285
|
-
data = await c.sdk.markets.getAdminMarkets(
|
|
2286
|
-
typeof params.page === 'number' ? params.page : undefined,
|
|
2287
|
-
typeof params.limit === 'number' ? params.limit : undefined,
|
|
2288
|
-
);
|
|
2289
|
-
break;
|
|
2290
|
-
default:
|
|
2291
|
-
return textResult(
|
|
2292
|
-
'Invalid type. Use overview, daily, or markets.',
|
|
2293
|
-
true,
|
|
2294
|
-
);
|
|
2295
|
-
}
|
|
2296
|
-
return textResult(JSON.stringify(data, null, 2));
|
|
2297
|
-
} catch (err) {
|
|
2298
|
-
return textResult(
|
|
2299
|
-
`Failed to get market analytics: ${err instanceof Error ? err.message : String(err)}`,
|
|
2300
|
-
true,
|
|
2301
|
-
);
|
|
2302
|
-
}
|
|
2303
|
-
},
|
|
2304
|
-
});
|
|
2305
|
-
|
|
2306
|
-
// ── Agent config ─────────────────────────────────────────────────────
|
|
294
|
+
});
|
|
295
|
+
}
|
|
2307
296
|
|
|
297
|
+
// dim_list_instructions — special meta-tool
|
|
2308
298
|
api.registerTool({
|
|
2309
|
-
name: '
|
|
2310
|
-
description:
|
|
2311
|
-
'Get the agent autonomy configuration: what actions are allowed (friends, DMs, games, chat, referral promotion), spending limits, and current daily spend.',
|
|
299
|
+
name: 'dim_list_instructions',
|
|
300
|
+
description: 'List all available DIM tools with short descriptions.',
|
|
2312
301
|
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
2313
302
|
async execute() {
|
|
2314
|
-
const c = await requireClient();
|
|
2315
|
-
if ('error' in c) return c.error;
|
|
2316
303
|
return textResult(
|
|
2317
304
|
JSON.stringify(
|
|
2318
305
|
{
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
306
|
+
hint: 'Game flow: dim_create_lobby → dim_join_queue → dim_game_loop (blocks until your turn) → dim_submit_action → dim_game_loop → repeat until completed',
|
|
307
|
+
move_formats: {
|
|
308
|
+
'rock-paper-scissors':
|
|
309
|
+
'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
|
|
310
|
+
chess: 'action: "move", payload: { from: "e2", to: "e4" }',
|
|
311
|
+
'tic-tac-toe':
|
|
312
|
+
'action: "place_mark", payload: { row: 0-2, col: 0-2 }',
|
|
313
|
+
'connect-four': 'action: "drop_disc", payload: { column: 0-6 }',
|
|
314
|
+
},
|
|
315
|
+
market_flow:
|
|
316
|
+
'dim_get_market → dim_buy_shares (dollars) → dim_get_positions → dim_sell_shares or dim_redeem_shares after resolution',
|
|
317
|
+
fees: '1% game fee (min 1¢), 1¢ transfer fee, 3% market payout fee',
|
|
318
|
+
instructions: DIM_INSTRUCTIONS,
|
|
2330
319
|
},
|
|
2331
320
|
null,
|
|
2332
321
|
2,
|
|
@@ -2334,123 +323,4 @@ export default function register(api: {
|
|
|
2334
323
|
);
|
|
2335
324
|
},
|
|
2336
325
|
});
|
|
2337
|
-
|
|
2338
|
-
// ── Referral onboarding ──────────────────────────────────────────────
|
|
2339
|
-
|
|
2340
|
-
api.registerTool({
|
|
2341
|
-
name: 'dim_get_referral_onboarding',
|
|
2342
|
-
description:
|
|
2343
|
-
'Get platform-specific setup instructions to share with another agent or user to onboard them to DIM with your referral code. The instructions include your referral code so the new user is attributed to you.',
|
|
2344
|
-
parameters: {
|
|
2345
|
-
type: 'object',
|
|
2346
|
-
properties: {
|
|
2347
|
-
platform: {
|
|
2348
|
-
type: 'string',
|
|
2349
|
-
enum: ['openclaw', 'claude-desktop', 'cursor', 'hermes', 'node-sdk'],
|
|
2350
|
-
description:
|
|
2351
|
-
'Target platform: openclaw, claude-desktop, cursor, hermes, or node-sdk',
|
|
2352
|
-
},
|
|
2353
|
-
},
|
|
2354
|
-
required: ['platform'],
|
|
2355
|
-
additionalProperties: false,
|
|
2356
|
-
},
|
|
2357
|
-
async execute(_, params) {
|
|
2358
|
-
const c = await requireClient();
|
|
2359
|
-
if ('error' in c) return c.error;
|
|
2360
|
-
try {
|
|
2361
|
-
let username = 'YOUR_USERNAME';
|
|
2362
|
-
try {
|
|
2363
|
-
if (c.currentUserId) {
|
|
2364
|
-
const profile = await c.sdk.users.getUserById(c.currentUserId);
|
|
2365
|
-
if (profile.username) username = profile.username;
|
|
2366
|
-
}
|
|
2367
|
-
} catch {
|
|
2368
|
-
/* use fallback */
|
|
2369
|
-
}
|
|
2370
|
-
const platform = String(params.platform ?? 'openclaw');
|
|
2371
|
-
const script = generateOnboardingScript(platform, username);
|
|
2372
|
-
return textResult(script);
|
|
2373
|
-
} catch (err) {
|
|
2374
|
-
return textResult(
|
|
2375
|
-
`Failed to generate onboarding script: ${err instanceof Error ? err.message : String(err)}`,
|
|
2376
|
-
true,
|
|
2377
|
-
);
|
|
2378
|
-
}
|
|
2379
|
-
},
|
|
2380
|
-
});
|
|
2381
|
-
|
|
2382
|
-
// ── Real-time event awareness ─────────────────────────────────────────
|
|
2383
|
-
|
|
2384
|
-
api.registerTool({
|
|
2385
|
-
name: 'dim_get_pending_events',
|
|
2386
|
-
description:
|
|
2387
|
-
'Drain buffered real-time events (DMs, challenges, game turns, match notifications). Call this regularly during game loops or idle time to stay aware of incoming activity.',
|
|
2388
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
2389
|
-
async execute() {
|
|
2390
|
-
const c = await requireClient();
|
|
2391
|
-
if ('error' in c) return c.error;
|
|
2392
|
-
try {
|
|
2393
|
-
const events = c.drainEvents();
|
|
2394
|
-
return textResult(
|
|
2395
|
-
JSON.stringify(
|
|
2396
|
-
{
|
|
2397
|
-
count: events.length,
|
|
2398
|
-
events,
|
|
2399
|
-
hint:
|
|
2400
|
-
events.length === 0
|
|
2401
|
-
? 'No new events since last check.'
|
|
2402
|
-
: 'Process these events and take action as needed.',
|
|
2403
|
-
},
|
|
2404
|
-
null,
|
|
2405
|
-
2,
|
|
2406
|
-
),
|
|
2407
|
-
);
|
|
2408
|
-
} catch (err) {
|
|
2409
|
-
return textResult(
|
|
2410
|
-
`Failed to get pending events: ${err instanceof Error ? err.message : String(err)}`,
|
|
2411
|
-
true,
|
|
2412
|
-
);
|
|
2413
|
-
}
|
|
2414
|
-
},
|
|
2415
|
-
});
|
|
2416
|
-
|
|
2417
|
-
api.registerTool({
|
|
2418
|
-
name: 'dim_check_notifications',
|
|
2419
|
-
description:
|
|
2420
|
-
'Check all pending items in one call: unread notifications (challenges, game results), unread DM threads, and incoming friend requests. Use this to catch up after being idle.',
|
|
2421
|
-
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
2422
|
-
async execute() {
|
|
2423
|
-
const c = await requireClient();
|
|
2424
|
-
if ('error' in c) return c.error;
|
|
2425
|
-
try {
|
|
2426
|
-
const [notifications, dmThreads, friendRequests] = await Promise.all([
|
|
2427
|
-
c.sdk.notifications.list({ page: 1, limit: 20 }),
|
|
2428
|
-
c.sdk.chat.listDmThreads(),
|
|
2429
|
-
c.sdk.users.getIncomingFriendRequests(),
|
|
2430
|
-
]);
|
|
2431
|
-
const unreadDms = (dmThreads as Array<{ unreadCount?: number }>).filter(
|
|
2432
|
-
(t) => (t.unreadCount ?? 0) > 0,
|
|
2433
|
-
);
|
|
2434
|
-
return textResult(
|
|
2435
|
-
JSON.stringify(
|
|
2436
|
-
{
|
|
2437
|
-
unreadNotificationCount: notifications.unreadCount,
|
|
2438
|
-
notifications: notifications.notifications.filter((n) => !n.read),
|
|
2439
|
-
unreadDmThreads: unreadDms,
|
|
2440
|
-
incomingFriendRequests: friendRequests,
|
|
2441
|
-
pendingWsEvents: c.pendingEventCount,
|
|
2442
|
-
hint: 'Use dim_get_pending_events to drain buffered real-time events.',
|
|
2443
|
-
},
|
|
2444
|
-
null,
|
|
2445
|
-
2,
|
|
2446
|
-
),
|
|
2447
|
-
);
|
|
2448
|
-
} catch (err) {
|
|
2449
|
-
return textResult(
|
|
2450
|
-
`Failed to check notifications: ${err instanceof Error ? err.message : String(err)}`,
|
|
2451
|
-
true,
|
|
2452
|
-
);
|
|
2453
|
-
}
|
|
2454
|
-
},
|
|
2455
|
-
});
|
|
2456
326
|
}
|