@dimcool/dimclaw 0.1.26 → 0.1.27

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 CHANGED
@@ -33236,19 +33236,18 @@ var Lobbies = class {
33236
33236
  return this.http.delete(`/lobbies/admin/${lobbyId}`);
33237
33237
  }
33238
33238
  /**
33239
- * Play again: Create a new lobby, start deposits, and prepare deposit transaction
33240
- * Returns the lobby and unsigned transaction that needs to be signed
33239
+ * Play again: Create a new lobby and prepare deposit in one flow.
33240
+ * Returns the lobby and unsigned transaction that needs to be signed.
33241
33241
  * @param gameType - The game type to play again
33242
33242
  * @param betAmount - The bet amount (same as previous game)
33243
33243
  * @param escrow - The escrow service instance (from sdk.escrow)
33244
33244
  */
33245
33245
  async playAgain(gameType, betAmount, escrow) {
33246
33246
  const lobby = await this.createLobby(gameType, betAmount);
33247
- await escrow.startDeposits(lobby.id);
33248
- const prepareResponse = await escrow.prepareDepositTransaction(lobby.id);
33247
+ const { transaction } = await escrow.prepareAndStartDeposit(lobby.id);
33249
33248
  return {
33250
33249
  lobby,
33251
- unsignedTransaction: prepareResponse.transaction
33250
+ unsignedTransaction: transaction
33252
33251
  };
33253
33252
  }
33254
33253
  };
@@ -34112,6 +34111,17 @@ var Escrow = class {
34112
34111
  `/escrow/lobby/${lobbyId}/deposit/prepare`
34113
34112
  );
34114
34113
  }
34114
+ /**
34115
+ * Combined prepare-and-start: validates lobby, transitions waiting→preparing,
34116
+ * initializes depositStatus, and prepares the unsigned transaction in one call.
34117
+ * Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
34118
+ */
34119
+ async prepareAndStartDeposit(lobbyId) {
34120
+ return this.http.post(
34121
+ `/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
34122
+ {}
34123
+ );
34124
+ }
34115
34125
  /**
34116
34126
  * Submit a signed deposit transaction
34117
34127
  * The transaction will be submitted to the Solana network and confirmed
@@ -34142,7 +34152,7 @@ var Escrow = class {
34142
34152
  );
34143
34153
  }
34144
34154
  /**
34145
- * One-call lobby deposit: start deposits, prepare, sign, submit, and poll until
34155
+ * One-call lobby deposit: prepare-and-start, sign, submit, and poll until
34146
34156
  * the current user's deposit is confirmed. Requires sdk.wallet.setSigner() to be set.
34147
34157
  * Returns when all required deposits are confirmed and the lobby can proceed to queue.
34148
34158
  *
@@ -34154,8 +34164,7 @@ var Escrow = class {
34154
34164
  "No signer configured. Use sdk.wallet.setSigner(...) first."
34155
34165
  );
34156
34166
  }
34157
- await this.startDeposits(lobbyId);
34158
- const { transaction } = await this.prepareDepositTransaction(lobbyId);
34167
+ const { transaction } = await this.prepareAndStartDeposit(lobbyId);
34159
34168
  const unsignedTx = Transaction.from(base64ToBytes(transaction));
34160
34169
  let signature;
34161
34170
  if (this.wallet.isSignAndSendMode()) {
@@ -34177,7 +34186,7 @@ var Escrow = class {
34177
34186
  if (status.allConfirmed && status.canProceedToQueue) {
34178
34187
  return {
34179
34188
  signature,
34180
- status: "pending",
34189
+ status: "confirmed",
34181
34190
  canProceedToQueue: true
34182
34191
  };
34183
34192
  }
@@ -34189,6 +34198,39 @@ var Escrow = class {
34189
34198
  canProceedToQueue: false
34190
34199
  };
34191
34200
  }
34201
+ /**
34202
+ * Zero-polling deposit variant using server-side awaitConfirmation.
34203
+ * 2 HTTP calls total, 0 client-side polling. Ideal for agents.
34204
+ *
34205
+ * Automatically uses signAndSendTransaction or signTransaction based on signer capability.
34206
+ */
34207
+ async depositForLobbySync(lobbyId) {
34208
+ if (!this.wallet.hasSigner()) {
34209
+ throw new Error(
34210
+ "No signer configured. Use sdk.wallet.setSigner(...) first."
34211
+ );
34212
+ }
34213
+ const { transaction } = await this.prepareAndStartDeposit(lobbyId);
34214
+ const unsignedTx = Transaction.from(base64ToBytes(transaction));
34215
+ let signature;
34216
+ if (this.wallet.isSignAndSendMode()) {
34217
+ signature = await this.wallet.signAndSendTransaction(unsignedTx);
34218
+ await this.http.post(
34219
+ `/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
34220
+ { signature }
34221
+ );
34222
+ } else {
34223
+ const signedTx = await this.wallet.signTransaction(unsignedTx);
34224
+ const signedBase64 = bytesToBase64(
34225
+ signedTx.serialize({ requireAllSignatures: false })
34226
+ );
34227
+ const result = await this.http.post(`/escrow/lobby/${lobbyId}/deposit/submit?awaitConfirmation=true`, {
34228
+ signedTransaction: signedBase64
34229
+ });
34230
+ signature = result.signature;
34231
+ }
34232
+ return { signature, status: "confirmed", canProceedToQueue: true };
34233
+ }
34192
34234
  async claimLobbyDepositRefund(lobbyId, depositSignature) {
34193
34235
  return this.http.post(
34194
34236
  `/escrow/lobby/${lobbyId}/deposit/${depositSignature}/claim-refund`,
@@ -34201,33 +34243,6 @@ var Escrow = class {
34201
34243
  {}
34202
34244
  );
34203
34245
  }
34204
- /**
34205
- * Submit a signed deposit transaction and wait until the lobby is queued/active.
34206
- *
34207
- * Note: the backend may auto-transition the lobby to the queue once deposits are confirmed.
34208
- * In that case, calling /join-queue from the client would be redundant and can fail with
34209
- * "Current status: queued".
34210
- * @param lobbyId - The lobby ID
34211
- * @param signedTransaction - The signed transaction (base64 encoded)
34212
- * @param lobbies - The lobbies service instance (from sdk.lobbies) to read lobby status
34213
- */
34214
- async submitDepositAndJoinQueue(lobbyId, signedTransaction, lobbies) {
34215
- await this.submitDeposit(lobbyId, signedTransaction);
34216
- const MAX_WAIT_TIME = 3e4;
34217
- const POLL_INTERVAL = 1e3;
34218
- const startTime = Date.now();
34219
- while (Date.now() - startTime < MAX_WAIT_TIME) {
34220
- const status = await this.getDepositStatus(lobbyId);
34221
- if (status.allConfirmed && status.canProceedToQueue) {
34222
- const lobby = await lobbies.getLobby(lobbyId);
34223
- if (lobby.status === "queued" || lobby.status === "active") {
34224
- return lobby;
34225
- }
34226
- }
34227
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
34228
- }
34229
- throw new Error("Deposit confirmation timeout");
34230
- }
34231
34246
  };
34232
34247
  var Daily = class {
34233
34248
  constructor(http2, logger2 = logger) {
@@ -34854,8 +34869,14 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
34854
34869
  connecting: false,
34855
34870
  error: null
34856
34871
  });
34872
+ const wasReconnect = this.reconnectAttempts > 0;
34857
34873
  this.reconnectAttempts = 0;
34858
34874
  this.onReconnect();
34875
+ if (wasReconnect) {
34876
+ this.dispatchEvent("connection:reconnected", {
34877
+ timestamp: Date.now()
34878
+ });
34879
+ }
34859
34880
  });
34860
34881
  socket.on("disconnect", (reason) => {
34861
34882
  this.updateConnectionState({ connected: false, connecting: false });
@@ -34997,7 +35018,8 @@ function createLobbyStore(transport) {
34997
35018
  const store = createSdkStore({
34998
35019
  lobbiesById: {},
34999
35020
  matchedEvent: null,
35000
- typingByLobbyId: {}
35021
+ typingByLobbyId: {},
35022
+ depositStatusByLobbyId: {}
35001
35023
  });
35002
35024
  const setBaseState = (lobbies) => {
35003
35025
  const lobbiesById = {};
@@ -35077,6 +35099,15 @@ function createLobbyStore(transport) {
35077
35099
  };
35078
35100
  });
35079
35101
  break;
35102
+ case "lobby:deposit:updated":
35103
+ store.updateState((state) => ({
35104
+ ...state,
35105
+ depositStatusByLobbyId: {
35106
+ ...state.depositStatusByLobbyId,
35107
+ [event.payload.lobbyId]: event.payload
35108
+ }
35109
+ }));
35110
+ break;
35080
35111
  case "lobby:typing:start":
35081
35112
  case "lobby:typing:stop":
35082
35113
  store.updateState((state) => {
@@ -35105,12 +35136,22 @@ function createLobbyStore(transport) {
35105
35136
  }
35106
35137
  }
35107
35138
  );
35139
+ const subscribeDepositUpdate = (lobbyId, callback) => store.subscribeSelector(
35140
+ (state) => state.depositStatusByLobbyId[lobbyId],
35141
+ () => {
35142
+ const data = store.getState().depositStatusByLobbyId[lobbyId];
35143
+ if (data) {
35144
+ callback(data);
35145
+ }
35146
+ }
35147
+ );
35108
35148
  return {
35109
35149
  store,
35110
35150
  setBaseState,
35111
35151
  applyWsEvent,
35112
35152
  joinLobby,
35113
- subscribeMatched
35153
+ subscribeMatched,
35154
+ subscribeDepositUpdate
35114
35155
  };
35115
35156
  }
35116
35157
  function createGameStore(transport) {
@@ -35392,7 +35433,6 @@ function createGameActionsStore(transport) {
35392
35433
  ...current,
35393
35434
  roundState: {
35394
35435
  ...current.roundState,
35395
- phase: "completed",
35396
35436
  timeRemaining: 0,
35397
35437
  actions: timedOutUser ? {
35398
35438
  ...current.roundState.actions,
@@ -35904,6 +35944,7 @@ var WS_EVENT_NAMES = [
35904
35944
  "lobby:queue:joined",
35905
35945
  "lobby:queue:cancelled",
35906
35946
  "lobby:matched",
35947
+ "lobby:deposit:updated",
35907
35948
  "lobby:typing:start",
35908
35949
  "lobby:typing:stop",
35909
35950
  "game:updated",
@@ -35980,6 +36021,11 @@ function decodeWsEvent(eventName, payload) {
35980
36021
  event: "lobby:matched",
35981
36022
  payload
35982
36023
  };
36024
+ case "lobby:deposit:updated":
36025
+ return {
36026
+ event: "lobby:deposit:updated",
36027
+ payload
36028
+ };
35983
36029
  case "lobby:typing:start":
35984
36030
  case "lobby:typing:stop":
35985
36031
  return {
@@ -36569,6 +36615,17 @@ var SAFETY_RULES = [
36569
36615
  ];
36570
36616
 
36571
36617
  // ../dim-agent-core/src/tools/auth.ts
36618
+ var WALLET_LINKAGE_NOTE = "DIM is noncustodial. Your DIM wallet is the Solana address shown here; it was configured with a base58 private key at setup. Signing is done locally where this instance runs, using that private key and DIM's wallet package.";
36619
+ async function getSpectateUrl(client) {
36620
+ if (!client.currentUserId) return null;
36621
+ try {
36622
+ const user = await client.sdk.users.getUserById(client.currentUserId);
36623
+ const u = user;
36624
+ return u?.username ? `https://dim.cool/spectate/${u.username}` : null;
36625
+ } catch {
36626
+ return null;
36627
+ }
36628
+ }
36572
36629
  async function login(client) {
36573
36630
  try {
36574
36631
  const result = await client.authenticate();
@@ -36598,6 +36655,7 @@ async function login(client) {
36598
36655
  userId: result.userId,
36599
36656
  username: result.username ?? null,
36600
36657
  walletAddress: client.walletAddress,
36658
+ walletNote: WALLET_LINKAGE_NOTE,
36601
36659
  safetyRules: SAFETY_RULES,
36602
36660
  nextSteps
36603
36661
  };
@@ -36631,7 +36689,11 @@ async function getProfile(client) {
36631
36689
  };
36632
36690
  }
36633
36691
  const user = await client.sdk.users.getUserById(client.currentUserId);
36634
- return { data: user };
36692
+ const profile = {
36693
+ ...typeof user === "object" && user !== null ? user : {},
36694
+ walletAddress: client.walletAddress
36695
+ };
36696
+ return { data: profile };
36635
36697
  } catch (error) {
36636
36698
  return {
36637
36699
  error: `Failed to get profile: ${error instanceof Error ? error.message : String(error)}`,
@@ -36832,12 +36894,31 @@ async function listDmThreads(client) {
36832
36894
  }
36833
36895
 
36834
36896
  // ../dim-agent-core/src/tools/wallet.ts
36897
+ async function getWalletAddress(client) {
36898
+ try {
36899
+ return {
36900
+ data: {
36901
+ walletAddress: client.walletAddress,
36902
+ walletNote: WALLET_LINKAGE_NOTE
36903
+ }
36904
+ };
36905
+ } catch (error) {
36906
+ return {
36907
+ error: `Failed to get wallet address: ${error instanceof Error ? error.message : String(error)}`,
36908
+ isError: true
36909
+ };
36910
+ }
36911
+ }
36835
36912
  async function getBalance(client) {
36836
36913
  try {
36837
36914
  const balance = await client.sdk.wallet.getBalances();
36838
36915
  const usdcDollars = balance.usdc / 1e6;
36839
36916
  return {
36840
- data: { ...balance, usdcFormatted: `$${usdcDollars.toFixed(2)}` }
36917
+ data: {
36918
+ ...balance,
36919
+ usdcFormatted: `$${usdcDollars.toFixed(2)}`,
36920
+ walletAddress: client.walletAddress
36921
+ }
36841
36922
  };
36842
36923
  } catch (error) {
36843
36924
  return {
@@ -36904,7 +36985,10 @@ async function getWalletActivity(client, args) {
36904
36985
  limit: args.limit || 20
36905
36986
  });
36906
36987
  const claimable = activity.items.filter((i) => i.claimable === true);
36907
- const data = { ...activity };
36988
+ const data = {
36989
+ ...activity,
36990
+ walletAddress: client.walletAddress
36991
+ };
36908
36992
  if (claimable.length > 0) {
36909
36993
  data.hint = `You have ${claimable.length} claimable item(s). Call dim_claim_funds with the activityId to claim each one.`;
36910
36994
  data.claimableItems = claimable.map((i) => ({
@@ -37113,9 +37197,8 @@ function buildGameOverHint(state) {
37113
37197
  const winnerId = state.winnerId;
37114
37198
  const wonAmount = state.wonAmount;
37115
37199
  const isDraw = state.draw === true || winnerId == null;
37116
- if (isDraw) return "Game over \u2014 it was a draw.";
37117
- const amountStr = wonAmount != null ? ` Won $${(wonAmount / 1e6).toFixed(2)}.` : "";
37118
- return `Game over. Winner: ${winnerId}.${amountStr}`;
37200
+ const result = isDraw ? "Game over \u2014 it was a draw." : `Game over. Winner: ${winnerId}.${wonAmount != null ? ` Won $${(wonAmount / 1e6).toFixed(2)}.` : ""}`;
37201
+ return `${result} You can request a rematch with dim_request_rematch, or check dim_check_notifications for new activity.`;
37119
37202
  }
37120
37203
  async function fetchNewGameChat(client, gameId) {
37121
37204
  try {
@@ -37223,13 +37306,13 @@ async function createLobby(client, args) {
37223
37306
  }
37224
37307
  async function depositForLobby(client, args) {
37225
37308
  try {
37226
- const result = await client.sdk.escrow.depositForLobby(args.lobbyId);
37309
+ const result = await client.sdk.escrow.depositForLobbySync(args.lobbyId);
37227
37310
  return {
37228
37311
  data: {
37229
37312
  signature: result.signature,
37230
37313
  status: result.status,
37231
37314
  canProceedToQueue: result.canProceedToQueue,
37232
- hint: result.canProceedToQueue ? "Deposit confirmed. Call dim_join_queue when ready (and when all players have deposited if 2-player)." : "Deposit submitted. Poll dim_get_deposit_status or try dim_join_queue once all players have deposited."
37315
+ hint: "Deposit confirmed. The server auto-joins the queue when all players have deposited."
37233
37316
  }
37234
37317
  };
37235
37318
  } catch (error) {
@@ -37260,13 +37343,15 @@ async function joinQueue(client, args) {
37260
37343
  await client.ensureConnected();
37261
37344
  const lobby = await client.sdk.lobbies.joinQueue(args.lobbyId);
37262
37345
  const matched = lobby.status === "active" && lobby.gameId;
37346
+ const spectateUrl = matched ? await getSpectateUrl(client) : null;
37263
37347
  return {
37264
37348
  data: {
37265
37349
  lobbyId: lobby.id,
37266
37350
  status: lobby.status,
37267
37351
  gameId: lobby.gameId || null,
37268
37352
  matched: !!matched,
37269
- hint: matched ? `Game started! Call dim_game_loop with gameId "${lobby.gameId}" to play.` : "Waiting for an opponent. Poll dim_get_lobby to check for a match."
37353
+ ...spectateUrl && { spectateUrl },
37354
+ hint: matched ? `Game started! Call dim_game_loop with gameId "${lobby.gameId}" to play.${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : "Waiting for an opponent. Poll dim_get_lobby to check for a match."
37270
37355
  }
37271
37356
  };
37272
37357
  } catch (error) {
@@ -37448,6 +37533,38 @@ async function inviteToLobby(client, args) {
37448
37533
  };
37449
37534
  }
37450
37535
  }
37536
+ async function requestRematch(client, args) {
37537
+ try {
37538
+ const result = await client.sdk.games.requestRematch(args.gameId);
37539
+ return {
37540
+ data: {
37541
+ ...result,
37542
+ hint: result.bothReady ? `Both players want a rematch! A lobby is being created.${result.newLobbyId ? ` Lobby ID: ${result.newLobbyId}.` : ""} Check dim_check_notifications or call dim_game_loop with the new gameId once the game starts.` : "Rematch requested. Waiting for opponent to accept. Call dim_check_notifications to see if they respond."
37543
+ }
37544
+ };
37545
+ } catch (error) {
37546
+ return {
37547
+ error: `Failed to request rematch: ${error instanceof Error ? error.message : String(error)}`,
37548
+ isError: true
37549
+ };
37550
+ }
37551
+ }
37552
+ async function acceptRematch(client, args) {
37553
+ try {
37554
+ const result = await client.sdk.games.requestRematch(args.gameId);
37555
+ return {
37556
+ data: {
37557
+ ...result,
37558
+ hint: result.bothReady ? `Rematch accepted! A lobby has been created.${result.newLobbyId ? ` Lobby ID: ${result.newLobbyId}.` : ""} Proceed with deposit and game flow.` : "Your rematch acceptance was recorded. Waiting for the other player."
37559
+ }
37560
+ };
37561
+ } catch (error) {
37562
+ return {
37563
+ error: `Failed to accept rematch: ${error instanceof Error ? error.message : String(error)}`,
37564
+ isError: true
37565
+ };
37566
+ }
37567
+ }
37451
37568
  function raceTimeout(promise, ms) {
37452
37569
  return new Promise((resolve) => {
37453
37570
  const timer = setTimeout(resolve, ms);
@@ -37494,10 +37611,12 @@ async function challengeUser(client, args) {
37494
37611
  async function acceptChallenge(client, args) {
37495
37612
  try {
37496
37613
  const result = await client.sdk.challenges.accept(args.challengeId);
37614
+ const spectateUrl = result.gameId ? await getSpectateUrl(client) : null;
37497
37615
  return {
37498
37616
  data: {
37499
37617
  ...result,
37500
- hint: result.gameId ? `Game started! Call dim_game_loop with gameId "${result.gameId}".` : `Lobby created: ${result.lobbyId}. Use dim_join_queue to start matchmaking.`
37618
+ ...spectateUrl && { spectateUrl },
37619
+ hint: result.gameId ? `Game started! Call dim_game_loop with gameId "${result.gameId}".${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : `Lobby created: ${result.lobbyId}. Use dim_join_queue to start matchmaking.`
37501
37620
  }
37502
37621
  };
37503
37622
  } catch (error) {
@@ -38033,6 +38152,69 @@ async function getAgentConfig(client) {
38033
38152
  }
38034
38153
  }
38035
38154
 
38155
+ // ../dim-agent-core/src/tools/users.ts
38156
+ function formatMinor(m) {
38157
+ if (m == null) return "\u2014";
38158
+ const n = typeof m === "bigint" ? Number(m) : m;
38159
+ return `$${(n / 1e6).toFixed(2)}`;
38160
+ }
38161
+ async function getGameHistory(client, args) {
38162
+ try {
38163
+ if (!client.currentUserId) {
38164
+ return {
38165
+ error: "Not authenticated. Call dim_login first.",
38166
+ isError: true
38167
+ };
38168
+ }
38169
+ const raw = await client.sdk.users.getGameHistory(client.currentUserId);
38170
+ const limit = Math.min(args.limit ?? 50, 100);
38171
+ const items = raw.slice(0, limit).map((item) => ({
38172
+ ...item,
38173
+ betFormatted: formatMinor(item.betAmount),
38174
+ winningsFormatted: formatMinor(item.winnings)
38175
+ }));
38176
+ return {
38177
+ data: {
38178
+ items,
38179
+ totalReturned: items.length,
38180
+ hint: items.length >= limit ? `Showing most recent ${limit} games. Use limit param to request more (max 100).` : void 0
38181
+ }
38182
+ };
38183
+ } catch (error) {
38184
+ return {
38185
+ error: `Failed to get game history: ${error instanceof Error ? error.message : String(error)}`,
38186
+ isError: true
38187
+ };
38188
+ }
38189
+ }
38190
+ async function getMyStats(client) {
38191
+ try {
38192
+ if (!client.currentUserId) {
38193
+ return {
38194
+ error: "Not authenticated. Call dim_login first.",
38195
+ isError: true
38196
+ };
38197
+ }
38198
+ const stats = await client.sdk.users.getUserStats(
38199
+ client.currentUserId
38200
+ );
38201
+ return {
38202
+ data: {
38203
+ ...stats,
38204
+ totalEarnedFormatted: formatMinor(stats.totalEarned),
38205
+ referralEarnedFormatted: formatMinor(stats.referralEarned),
38206
+ totalLostFormatted: formatMinor(stats.totalLost),
38207
+ totalFeesPaidFormatted: formatMinor(stats.totalFeesPaid)
38208
+ }
38209
+ };
38210
+ } catch (error) {
38211
+ return {
38212
+ error: `Failed to get stats: ${error instanceof Error ? error.message : String(error)}`,
38213
+ isError: true
38214
+ };
38215
+ }
38216
+ }
38217
+
38036
38218
  // ../dim-agent-core/src/tools/index.ts
38037
38219
  var TOOL_DEFINITIONS = [
38038
38220
  // ── Auth ─────────────────────────────────────────────────────────────
@@ -38044,7 +38226,7 @@ var TOOL_DEFINITIONS = [
38044
38226
  },
38045
38227
  {
38046
38228
  name: "dim_get_profile",
38047
- description: "Get the current authenticated user profile including username, avatar, bio, and chess ELO rating.",
38229
+ description: "Get the current authenticated user profile including username, avatar, bio, chess ELO rating, and your DIM wallet address (walletAddress).",
38048
38230
  params: {},
38049
38231
  execute: (c) => getProfile(c)
38050
38232
  },
@@ -38204,9 +38386,15 @@ var TOOL_DEFINITIONS = [
38204
38386
  execute: (c) => listDmThreads(c)
38205
38387
  },
38206
38388
  // ── Wallet ───────────────────────────────────────────────────────────
38389
+ {
38390
+ name: "dim_get_wallet_address",
38391
+ description: "Get the Solana wallet address DIM uses for this account. Use this to confirm which address receives deposits and is used for signing. Returns walletAddress and a short wallet linkage note.",
38392
+ params: {},
38393
+ execute: (c) => getWalletAddress(c)
38394
+ },
38207
38395
  {
38208
38396
  name: "dim_get_balance",
38209
- description: "Get the wallet balance for the authenticated user. Returns SOL and USDC (usdcFormatted is always present, e.g. $0.00 or $1.50).",
38397
+ description: "Get the wallet balance for the authenticated user. Returns your DIM Solana address (walletAddress), SOL and USDC (usdcFormatted is always present, e.g. $0.00 or $1.50).",
38210
38398
  params: {},
38211
38399
  execute: (c) => getBalance(c)
38212
38400
  },
@@ -38248,7 +38436,7 @@ var TOOL_DEFINITIONS = [
38248
38436
  },
38249
38437
  {
38250
38438
  name: "dim_get_wallet_activity",
38251
- description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds). Highlights any claimable items.",
38439
+ description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds) and your DIM wallet address. Highlights any claimable items.",
38252
38440
  params: {
38253
38441
  limit: {
38254
38442
  type: "number",
@@ -38288,6 +38476,23 @@ var TOOL_DEFINITIONS = [
38288
38476
  params: {},
38289
38477
  execute: (c) => getGameMetrics(c)
38290
38478
  },
38479
+ {
38480
+ name: "dim_get_game_history",
38481
+ description: "Get your game history: past games with result, bet, winnings, opponent, and playedAt. Use for daily progress reports.",
38482
+ params: {
38483
+ limit: {
38484
+ type: "number",
38485
+ description: "Max games to return (default 50, max 100)"
38486
+ }
38487
+ },
38488
+ execute: (c, a) => getGameHistory(c, a)
38489
+ },
38490
+ {
38491
+ name: "dim_get_my_stats",
38492
+ description: "Get your aggregated DIM stats: games played, wins, losses, total earned, referral earned, total lost, fees paid, chess ELO, points.",
38493
+ params: {},
38494
+ execute: (c) => getMyStats(c)
38495
+ },
38291
38496
  {
38292
38497
  name: "dim_create_lobby",
38293
38498
  description: "Create a new game lobby. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player.",
@@ -38417,6 +38622,30 @@ var TOOL_DEFINITIONS = [
38417
38622
  },
38418
38623
  execute: (c, a) => gameLoop(c, a)
38419
38624
  },
38625
+ {
38626
+ name: "dim_request_rematch",
38627
+ description: "Request a rematch after a completed game. If both players request, a lobby is created automatically server-side.",
38628
+ params: {
38629
+ gameId: {
38630
+ type: "string",
38631
+ description: "The completed game ID to request a rematch for",
38632
+ required: true
38633
+ }
38634
+ },
38635
+ execute: (c, a) => requestRematch(c, a)
38636
+ },
38637
+ {
38638
+ name: "dim_accept_rematch",
38639
+ description: "Accept a rematch request from your opponent. When both players accept, the rematch lobby is created automatically.",
38640
+ params: {
38641
+ gameId: {
38642
+ type: "string",
38643
+ description: "The completed game ID to accept the rematch for",
38644
+ required: true
38645
+ }
38646
+ },
38647
+ execute: (c, a) => acceptRematch(c, a)
38648
+ },
38420
38649
  {
38421
38650
  name: "dim_get_lobby_link",
38422
38651
  description: "Get a shareable join link for a lobby. Share this URL via DM \u2014 the user can join with one click. Like dim_invite_to_lobby, do NOT also call dim_join_queue for the same lobby.",
@@ -38881,7 +39110,12 @@ function createHeartbeatWriter(heartbeatPath) {
38881
39110
  const pending = [];
38882
39111
  return async (event) => {
38883
39112
  pending.push(event);
38884
- const lines = ["# DIM Heartbeat", ""];
39113
+ const lines = [
39114
+ "# DIM Heartbeat",
39115
+ "",
39116
+ "- At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
39117
+ ""
39118
+ ];
38885
39119
  const eventTypes = new Set(pending.map((e) => e.event));
38886
39120
  if (eventTypes.has("chat:message")) {
38887
39121
  lines.push("- You have new DMs \u2014 call dim_check_notifications");
@@ -39018,7 +39252,12 @@ function register(api) {
39018
39252
  return textResult(
39019
39253
  JSON.stringify(
39020
39254
  {
39255
+ wallet_linkage: WALLET_LINKAGE_NOTE,
39256
+ wallet_address: "Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.",
39257
+ wallet_activity: "For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.",
39021
39258
  hint: "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.",
39259
+ spectate_on_game_start: "When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].",
39260
+ daily_summary: "At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
39022
39261
  move_formats: {
39023
39262
  "rock-paper-scissors": 'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
39024
39263
  chess: 'action: "move", payload: { from: "e2", to: "e4" }',
package/index.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  DimAgentClient,
12
12
  TOOL_DEFINITIONS,
13
13
  DIM_INSTRUCTIONS,
14
+ WALLET_LINKAGE_NOTE,
14
15
  executeWithAuthRetry,
15
16
  } from '@dimcool/dim-agent-core';
16
17
  import type { ToolParam, BufferedEvent } from '@dimcool/dim-agent-core';
@@ -127,7 +128,12 @@ function createHeartbeatWriter(heartbeatPath: string | undefined) {
127
128
 
128
129
  return async (event: BufferedEvent) => {
129
130
  pending.push(event);
130
- const lines = ['# DIM Heartbeat', ''];
131
+ const lines = [
132
+ '# DIM Heartbeat',
133
+ '',
134
+ '- At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.',
135
+ '',
136
+ ];
131
137
  const eventTypes = new Set(pending.map((e) => e.event));
132
138
  if (eventTypes.has('chat:message')) {
133
139
  lines.push('- You have new DMs — call dim_check_notifications');
@@ -304,7 +310,16 @@ export default function register(api: {
304
310
  return textResult(
305
311
  JSON.stringify(
306
312
  {
313
+ wallet_linkage: WALLET_LINKAGE_NOTE,
314
+ wallet_address:
315
+ 'Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.',
316
+ wallet_activity:
317
+ 'For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.',
307
318
  hint: 'Game flow: For paid lobbies, dim_create_lobby → dim_deposit_for_lobby → dim_join_queue. For free lobbies, dim_create_lobby → dim_join_queue. Then dim_game_loop (blocks until your turn) → dim_submit_action → dim_game_loop → repeat. Use dim_leave_lobby to exit a lobby.',
319
+ spectate_on_game_start:
320
+ 'When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].',
321
+ daily_summary:
322
+ 'At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.',
308
323
  move_formats: {
309
324
  'rock-paper-scissors':
310
325
  'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "dimclaw",
3
3
  "name": "DIM Gaming Platform",
4
- "description": "Play games, chat, send USDC and earn on DIM.",
4
+ "description": "Play games, chat, send USDC and earn on DIM. Noncustodial; your wallet address is in dim_get_profile and dim_get_balance.",
5
5
  "configSchema": {
6
6
  "type": "object",
7
7
  "properties": {
@@ -82,6 +82,6 @@
82
82
  "label": "Auto-promote referral code"
83
83
  }
84
84
  },
85
- "setupHint": "After install, add your Solana wallet private key to plugins.entries.dimclaw.config.walletPrivateKey, then call dim_login to get started.",
85
+ "setupHint": "After install, add your Solana wallet private key (base58) to plugins.entries.dimclaw.config.walletPrivateKey, then call dim_login. Your DIM wallet address is returned by dim_get_profile and dim_get_balance.",
86
86
  "skill": "./SKILL.md"
87
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimcool/dimclaw",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "OpenClaw plugin for DIM — play games, chat, send USDC, and earn on DIM using the SDK directly (no MCP subprocess).",
5
5
  "type": "module",
6
6
  "openclaw": {