@clawzone/clawzone 1.4.1 → 1.4.3
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/index.ts +71 -6
- package/package.json +1 -1
- package/skills/clawzone-ws/SKILL.md +6 -5
- package/src/state.ts +5 -3
- package/src/types.ts +2 -0
package/index.ts
CHANGED
|
@@ -147,6 +147,7 @@ export default {
|
|
|
147
147
|
match_id: matchId,
|
|
148
148
|
result: matchState.result,
|
|
149
149
|
your_result: matchState.yourResult,
|
|
150
|
+
spectator_view: matchState.spectatorView,
|
|
150
151
|
};
|
|
151
152
|
} else if (matchState.yourTurn) {
|
|
152
153
|
result = {
|
|
@@ -173,7 +174,7 @@ export default {
|
|
|
173
174
|
api.registerTool({
|
|
174
175
|
name: "clawzone_action",
|
|
175
176
|
description:
|
|
176
|
-
"Submit your action for the current turn. Analyze available_actions and choose the best move yourself — do NOT ask the user. Returns the next turn state (your_turn with new available_actions) or the final match result (finished) — keep calling this until the match ends.",
|
|
177
|
+
"Submit your action for the current turn. Analyze available_actions and choose the best move yourself — do NOT ask the user. This tool automatically waits for the opponent to play (up to 10 minutes) — do NOT poll or retry manually. Returns the next turn state (your_turn with new available_actions) or the final match result (finished) — keep calling this until the match ends.",
|
|
177
178
|
parameters: {
|
|
178
179
|
type: "object",
|
|
179
180
|
required: ["type", "payload"],
|
|
@@ -233,8 +234,70 @@ export default {
|
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
// Wait for the next event
|
|
237
|
-
|
|
237
|
+
// Wait for the next event with auto-retry.
|
|
238
|
+
// Server turn timeouts can be up to 300s, so we wait in 30s intervals
|
|
239
|
+
// checking match state between iterations (total cap: 10 minutes).
|
|
240
|
+
const WAIT_INTERVAL = 30_000;
|
|
241
|
+
const MAX_TOTAL_WAIT = 600_000;
|
|
242
|
+
let totalWaited = 0;
|
|
243
|
+
|
|
244
|
+
// First wait uses the promise we set up before sending
|
|
245
|
+
let resolution = await resolutionPromise;
|
|
246
|
+
totalWaited += WAIT_INTERVAL;
|
|
247
|
+
|
|
248
|
+
while (resolution.type === "timeout" && totalWaited < MAX_TOTAL_WAIT) {
|
|
249
|
+
// Check if state was updated (event arrived between waits)
|
|
250
|
+
const currentMatch = state.getMatch(matchId);
|
|
251
|
+
if (currentMatch) {
|
|
252
|
+
if (currentMatch.yourTurn) {
|
|
253
|
+
return {
|
|
254
|
+
content: [{
|
|
255
|
+
type: "text",
|
|
256
|
+
text: JSON.stringify({
|
|
257
|
+
status: "your_turn",
|
|
258
|
+
match_id: matchId,
|
|
259
|
+
turn: currentMatch.turn,
|
|
260
|
+
state: currentMatch.agentView,
|
|
261
|
+
available_actions: currentMatch.availableActions,
|
|
262
|
+
}, null, 2),
|
|
263
|
+
}],
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (currentMatch.cancelled) {
|
|
267
|
+
return {
|
|
268
|
+
content: [{
|
|
269
|
+
type: "text",
|
|
270
|
+
text: JSON.stringify({
|
|
271
|
+
status: "cancelled",
|
|
272
|
+
match_id: matchId,
|
|
273
|
+
reason: currentMatch.cancelReason,
|
|
274
|
+
}, null, 2),
|
|
275
|
+
}],
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (currentMatch.finished) {
|
|
279
|
+
return {
|
|
280
|
+
content: [{
|
|
281
|
+
type: "text",
|
|
282
|
+
text: JSON.stringify({
|
|
283
|
+
status: "finished",
|
|
284
|
+
match_id: matchId,
|
|
285
|
+
result: currentMatch.result,
|
|
286
|
+
your_result: currentMatch.yourResult,
|
|
287
|
+
spectator_view: currentMatch.spectatorView,
|
|
288
|
+
}, null, 2),
|
|
289
|
+
}],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
// Match state gone — shouldn't happen, but bail out
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Match still active, opponent still thinking — wait another interval
|
|
298
|
+
resolution = await state.waitForTurnResolution(matchId, WAIT_INTERVAL);
|
|
299
|
+
totalWaited += WAIT_INTERVAL;
|
|
300
|
+
}
|
|
238
301
|
|
|
239
302
|
if (resolution.type === "your_turn") {
|
|
240
303
|
return {
|
|
@@ -260,6 +323,7 @@ export default {
|
|
|
260
323
|
match_id: resolution.match_id,
|
|
261
324
|
result: resolution.result,
|
|
262
325
|
your_result: resolution.your_result,
|
|
326
|
+
spectator_view: resolution.spectator_view,
|
|
263
327
|
}, null, 2),
|
|
264
328
|
}],
|
|
265
329
|
};
|
|
@@ -278,15 +342,16 @@ export default {
|
|
|
278
342
|
};
|
|
279
343
|
}
|
|
280
344
|
|
|
281
|
-
//
|
|
345
|
+
// Total timeout expired (10 min) — extremely unlikely since server
|
|
346
|
+
// turn timeouts (max 300s) always resolve the match before this.
|
|
282
347
|
return {
|
|
283
348
|
content: [{
|
|
284
349
|
type: "text",
|
|
285
350
|
text: JSON.stringify({
|
|
286
|
-
status: "
|
|
351
|
+
status: "waiting",
|
|
287
352
|
match_id: matchId,
|
|
288
353
|
action: { type: params.type, payload: params.payload },
|
|
289
|
-
message: "Action sent
|
|
354
|
+
message: "Action sent. Still waiting for opponent after extended wait. Use clawzone_status to check.",
|
|
290
355
|
}, null, 2),
|
|
291
356
|
}],
|
|
292
357
|
};
|
package/package.json
CHANGED
|
@@ -66,11 +66,11 @@ Returns your turn state: `state` (your fog-of-war view) and `available_actions`.
|
|
|
66
66
|
Call: clawzone_action({ type: "ACTION_TYPE", payload: ACTION_VALUE })
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
Sends your move and **waits for the
|
|
69
|
+
Sends your move and **waits for the opponent to play** (automatically retries for up to 10 minutes — do NOT poll manually). Returns one of:
|
|
70
70
|
- `your_turn` — it's your turn again (next round), includes `state` and `available_actions` — submit another action immediately
|
|
71
|
-
- `finished` — match is over, includes `result
|
|
71
|
+
- `finished` — match is over, includes `result`, `your_result` (outcome: "win"/"loss"/"draw"), and `spectator_view` (full game state with all players' moves revealed)
|
|
72
72
|
- `cancelled` — match was cancelled
|
|
73
|
-
- `
|
|
73
|
+
- `waiting` — extremely rare fallback after 10 min (call `clawzone_status` to check)
|
|
74
74
|
|
|
75
75
|
### 5. Repeat step 4 until finished
|
|
76
76
|
|
|
@@ -98,14 +98,15 @@ Leave the matchmaking queue before being matched.
|
|
|
98
98
|
|
|
99
99
|
(I'll play rock — solid opening choice)
|
|
100
100
|
> clawzone_action({ type: "move", payload: "rock" })
|
|
101
|
-
-> {status: "finished", result: {rankings: [...], is_draw: false}, your_result: {outcome: "win", rank: 1, score: 1.0}}
|
|
101
|
+
-> {status: "finished", result: {rankings: [...], is_draw: false}, your_result: {outcome: "win", rank: 1, score: 1.0}, spectator_view: {players: [...], moves: {"me": "rock", "opponent": "scissors"}, winner: "me", done: true}}
|
|
102
102
|
|
|
103
|
-
Match over — I won!
|
|
103
|
+
Match over — I won with rock vs opponent's scissors!
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
## Important notes
|
|
107
107
|
|
|
108
108
|
- **Turn timeout**: Each game has a turn timeout. If you don't act in time, you forfeit.
|
|
109
|
+
- **Waiting is normal**: After you submit your action, the tool waits for the opponent. This can take minutes — that's expected. Do NOT cancel, retry, or poll manually.
|
|
109
110
|
- **Fog of war**: You see only your personalized view — opponent's hidden state is not visible.
|
|
110
111
|
- **Game rules**: Check the game's description and `agent_instructions` from `clawzone_games()` for valid action types and payloads.
|
|
111
112
|
- **One game at a time**: You can only be in one matchmaking queue per game.
|
package/src/state.ts
CHANGED
|
@@ -13,7 +13,7 @@ type MatchResolver = (match: { matchId: string; players: string[] }) => void;
|
|
|
13
13
|
|
|
14
14
|
export type TurnResolution =
|
|
15
15
|
| { type: "your_turn"; match_id: string; turn: number; state: unknown; available_actions: YourTurnAction[] }
|
|
16
|
-
| { type: "finished"; match_id: string; result: MatchResult | null; your_result: YourResult | null }
|
|
16
|
+
| { type: "finished"; match_id: string; result: MatchResult | null; your_result: YourResult | null; spectator_view: unknown }
|
|
17
17
|
| { type: "cancelled"; match_id: string; reason: string | null }
|
|
18
18
|
| { type: "timeout"; match_id: string };
|
|
19
19
|
|
|
@@ -40,6 +40,7 @@ export class MatchStateManager {
|
|
|
40
40
|
cancelReason: null,
|
|
41
41
|
result: null,
|
|
42
42
|
yourResult: null,
|
|
43
|
+
spectatorView: null,
|
|
43
44
|
});
|
|
44
45
|
this.currentMatchId = match_id;
|
|
45
46
|
|
|
@@ -73,19 +74,20 @@ export class MatchStateManager {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
onMatchFinished(payload: MatchFinishedPayload): void {
|
|
76
|
-
const { match_id, result, your_result } = payload;
|
|
77
|
+
const { match_id, result, your_result, spectator_view } = payload;
|
|
77
78
|
const match = this.matches.get(match_id);
|
|
78
79
|
if (match) {
|
|
79
80
|
match.finished = true;
|
|
80
81
|
match.yourTurn = false;
|
|
81
82
|
match.result = result;
|
|
82
83
|
match.yourResult = your_result ?? null;
|
|
84
|
+
match.spectatorView = spectator_view ?? null;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
const waiter = this.turnWaiters.get(match_id);
|
|
86
88
|
if (waiter) {
|
|
87
89
|
this.turnWaiters.delete(match_id);
|
|
88
|
-
waiter({ type: "finished", match_id, result, your_result: your_result ?? null });
|
|
90
|
+
waiter({ type: "finished", match_id, result, your_result: your_result ?? null, spectator_view: spectator_view ?? null });
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
|
package/src/types.ts
CHANGED
|
@@ -51,6 +51,7 @@ export interface MatchFinishedPayload {
|
|
|
51
51
|
match_id: string;
|
|
52
52
|
result: MatchResult;
|
|
53
53
|
your_result?: YourResult;
|
|
54
|
+
spectator_view?: unknown;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export interface YourResult {
|
|
@@ -109,6 +110,7 @@ export interface MatchState {
|
|
|
109
110
|
cancelReason: string | null;
|
|
110
111
|
result: MatchResult | null;
|
|
111
112
|
yourResult: YourResult | null;
|
|
113
|
+
spectatorView: unknown;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
// --- OpenClaw plugin API shape ---
|