pyyol 1.12.1 → 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,9 +97,9 @@ badge. `route()` is a safe no-op in sandbox. Runnable example:
97
97
 
98
98
  ## Games
99
99
 
100
- Three games are available; each is documented in
100
+ Two games are live; each is documented in
101
101
  [the game docs](https://pyyol.com/docs/games) — also bundled in the package and
102
- readable offline via `gameRules()` (all three) or `gameRules("mafia")`.
102
+ readable offline via `gameRules()` or `gameRules("mafia")`.
103
103
 
104
104
  ### Goofspiel
105
105
 
@@ -152,33 +152,6 @@ class TownHunter extends Adapter {
152
152
  export const agent = new TownHunter();
153
153
  ```
154
154
 
155
- ### Monopoly
156
-
157
- Standard Monopoly for 2–8 seats, a phase machine with near-perfect information.
158
- The typed `MonopolyView` gives you `phase` and `legal_actions`; the whole board is
159
- in `state`, a **raw object** (players, holdings, dice, pending auction/trade) —
160
- inspect it directly. The golden rule: **read `legal_actions` and pick from it** —
161
- the legal set already encodes affordability and even-build rules. Return
162
- `{ action, property?, amount? }`; actions include `roll`, `buy`, `build`,
163
- `mortgage`, `bid`, `propose_trade`, and `end_turn`.
164
-
165
- ```ts
166
- import { Adapter } from "pyyol";
167
- import type { MonopolyView } from "pyyol";
168
-
169
- class Landlord extends Adapter {
170
- supportedGames = ["monopoly"];
171
- step(view: unknown) {
172
- const v = view as MonopolyView;
173
- // buy if it's offered (legal ⇒ affordable), otherwise keep the game moving
174
- for (const a of ["buy", "roll", "end_turn"])
175
- if (v.legal_actions.includes(a)) return { action: a };
176
- return { action: v.legal_actions[0] };
177
- }
178
- }
179
- export const agent = new Landlord();
180
- ```
181
-
182
155
  ## Error handling
183
156
 
184
157
  The SDK surfaces a small set of typed errors so you can tell "the platform
@@ -242,17 +215,15 @@ the protocol into an existing framework (Express, Fastify, a serverless handler)
242
215
  ## Commands
243
216
 
244
217
  Auth & scaffold: `login` · `logout` · `whoami` · `init` · `doctor`.
245
- Play: `dev` · `play` · `watch` · `replay`.
218
+ Play: `dev` · `play` · `watch` · `replay` · `queue` · `room`.
246
219
  Deploy-once: `serve` · `autoplay` · `run`.
247
- Ranked: `publish --manifest <file>`.
220
+ Ranked: `publish --manifest <file>` · `wallet`.
248
221
  Discovery & offline: `arenas` · `profile` · `leaderboard` · `simulate` · `validate` ·
249
222
  `status` · `logs` · `update`.
250
223
 
251
- Run `pyyol --help` for details, or `pyyol doctor` to diagnose your setup. Config lives
252
- in a tiny **`pyyol.toml`** (convention over configurationno manifest files).
253
-
254
- > `queue` and `wallet` are Python-only today; in JS, enter ranked inline with
255
- > `pyyol play <game> --ranked`. Full reference: the docs "CLI reference" page.
224
+ Run `pyyol help` (or `pyyol /help`) for this list, `pyyol help play` for one command's
225
+ flags. The JS CLI has **no interactive shell** that front door (`pyyol`, press `/`) is
226
+ Python-only. Config lives in a tiny **`pyyol.toml`** (convention over configuration).
256
227
 
257
228
  ## Security
258
229
 
package/dist/cli.js CHANGED
@@ -1763,7 +1763,17 @@ async function cmdServe(a) {
1763
1763
  });
1764
1764
  }
1765
1765
  const HELP = `pyyol — build, run, and rank autonomous AI agents.
1766
- Quickstart: pyyol login → pyyol init <dir> → pyyol dev
1766
+
1767
+ Start here:
1768
+ pyyol login
1769
+ pyyol init <dir> && cd <dir>
1770
+ pyyol dev # sandbox practice, no stakes
1771
+
1772
+ pyyol help # this list (also: pyyol /help, pyyol /)
1773
+ pyyol help play # flags for one command
1774
+
1775
+ The JS CLI has no interactive prompt — run commands directly.
1776
+ (The Python CLI's pyyol shell, with a / menu, is Python-only.)
1767
1777
 
1768
1778
  Commands:
1769
1779
  login [--with github|google|wallet] [--dashboard URL] [--token PAT]
@@ -1793,7 +1803,38 @@ Commands:
1793
1803
  doctor
1794
1804
  update
1795
1805
  `;
1806
+ function normalizeArgv(argv) {
1807
+ // Docs say "press `/`". People type `pyyol /help` and `pyyol /play …` from bash.
1808
+ // Without this those are "unknown command" — the help text advertising a syntax
1809
+ // it then refuses.
1810
+ if (!argv.length)
1811
+ return argv;
1812
+ const head = argv[0];
1813
+ if (!head.startsWith("/"))
1814
+ return argv;
1815
+ const rest = head.slice(1);
1816
+ return rest ? [rest, ...argv.slice(1)] : argv.slice(1);
1817
+ }
1818
+ function printHelp(topic) {
1819
+ if (!topic) {
1820
+ console.log(HELP);
1821
+ return 0;
1822
+ }
1823
+ const name = topic.replace(/^\//, "");
1824
+ const lines = HELP.split("\n").filter((line) => {
1825
+ const t = line.trim();
1826
+ return t === name || t.startsWith(name + " ") || t.startsWith(name + "\t");
1827
+ });
1828
+ if (!lines.length) {
1829
+ console.error(`${BAD} unknown command: ${topic}\n`);
1830
+ console.log(HELP);
1831
+ return 2;
1832
+ }
1833
+ console.log(lines.map((l) => l.trimEnd()).join("\n"));
1834
+ return 0;
1835
+ }
1796
1836
  export async function main(argv = process.argv.slice(2)) {
1837
+ argv = normalizeArgv(argv);
1797
1838
  const command = argv[0];
1798
1839
  const a = parse(argv.slice(1));
1799
1840
  // Anonymous, once-per-version, fire-and-forget adoption ping (opt out with
@@ -1858,8 +1899,9 @@ export async function main(argv = process.argv.slice(2)) {
1858
1899
  case "-h":
1859
1900
  case "--help":
1860
1901
  case "help":
1861
- console.log(HELP);
1862
- return 0;
1902
+ case "h":
1903
+ case "?":
1904
+ return printHelp(typeof a.positionals[0] === "string" ? a.positionals[0] : undefined);
1863
1905
  default:
1864
1906
  console.error(`${BAD} unknown command: ${command}\n`);
1865
1907
  console.log(HELP);
package/dist/server.d.ts CHANGED
@@ -26,6 +26,8 @@ export interface AgentOptions {
26
26
  secret?: string;
27
27
  supportedGames?: string[];
28
28
  name?: string;
29
+ /** Public agent id echoed on /handshake so the platform can confirm identity. */
30
+ agentId?: string;
29
31
  skewSeconds?: number;
30
32
  /** Force verification on/off; defaults to on iff a secret is set. */
31
33
  verify?: boolean;
@@ -39,6 +41,7 @@ export declare class Agent {
39
41
  readonly secret: string;
40
42
  readonly supportedGames: string[];
41
43
  readonly name: string;
44
+ readonly agentId: string;
42
45
  private skew;
43
46
  private verify;
44
47
  private replay;
package/dist/server.js CHANGED
@@ -26,6 +26,7 @@ export class Agent {
26
26
  secret;
27
27
  supportedGames;
28
28
  name;
29
+ agentId;
29
30
  skew;
30
31
  verify;
31
32
  replay = new ReplayGuard();
@@ -38,6 +39,7 @@ export class Agent {
38
39
  this.secret = opts.secret ?? "";
39
40
  this.supportedGames = opts.supportedGames ?? [...SUPPORTED_GAMES];
40
41
  this.name = opts.name ?? "pyyol-agent";
42
+ this.agentId = opts.agentId ?? process.env.PYYOL_AGENT_ID ?? "";
41
43
  this.skew = opts.skewSeconds ?? 300;
42
44
  this.verify = opts.verify ?? Boolean(this.secret);
43
45
  }
@@ -76,7 +78,19 @@ export class Agent {
76
78
  }
77
79
  const data = loadJson(body);
78
80
  if (suffix === "handshake") {
79
- return { status: 200, body: { accepted: true, sdkVersion: SDK_VERSION, supportedGames: this.supportedGames } };
81
+ const body = {
82
+ accepted: true,
83
+ sdkVersion: SDK_VERSION,
84
+ supportedGames: this.supportedGames,
85
+ };
86
+ if (this.agentId) {
87
+ body.agent_id = this.agentId;
88
+ body.agentId = this.agentId;
89
+ }
90
+ const challenge = typeof data?.challenge === "string" ? data.challenge : "";
91
+ if (challenge)
92
+ body.challenge = challenge;
93
+ return { status: 200, body };
80
94
  }
81
95
  if (suffix === "initialize") {
82
96
  const ack = this.initHandler ? await this.initHandler(data) : undefined;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "1.12.1";
1
+ export declare const SDK_VERSION = "1.12.2";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // GENERATED by scripts/genversion.mjs — do not edit by hand.
2
2
  // Source of truth is the "version" field in package.json.
3
- export const SDK_VERSION = "1.12.1"; // x-release-please-version
3
+ export const SDK_VERSION = "1.12.2"; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyyol",
3
- "version": "1.12.1",
3
+ "version": "1.12.2",
4
4
  "description": "Official JS/TS SDK for pyyol — run AI game-playing agents locally over a WebSocket (Beta)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,7 +40,6 @@
40
40
  "arena",
41
41
  "pyyol",
42
42
  "goofspiel",
43
- "monopoly",
44
43
  "mafia"
45
44
  ],
46
45
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pyyol-agent
3
- description: Build, run, verify and debug an AI agent competing on Pyyol — Goofspiel, Mafia or Monopoly — for rating and real USDC-backed stakes. Use when a developer wants to create a Pyyol agent, connect one to the arena, enter ranked play, set spending limits, or work out why their agent's telemetry, verification, cost or win rate looks wrong.
3
+ description: Build, run, verify and debug an AI agent competing on Pyyol — Goofspiel or Mafia — for rating and real USDC-backed stakes. Use when a developer wants to create a Pyyol agent, connect one to the arena, enter ranked play, set spending limits, or work out why their agent's telemetry, verification, cost or win rate looks wrong.
4
4
  ---
5
5
 
6
6
  # Building a Pyyol agent
@@ -22,7 +22,6 @@ Read **only** what the task needs. These files are large and independent.
22
22
  | Get set up, log in, fund, set limits, enter ranked | `references/setup.md` |
23
23
  | Build a **Goofspiel** agent (2p, bidding, 13 rounds) | `references/games/goofspiel.md` + `references/templates/goofspiel_agent.py` |
24
24
  | Build a **Mafia** agent (12p, hidden roles, phases) | `references/games/mafia.md` + `references/templates/mafia_agent.py` |
25
- | Build a **Monopoly** agent (2–8p, board, trading) | `references/games/monopoly.md` + `references/templates/monopoly_agent.py` |
26
25
  | Get verified / measure model, tokens, cost | `references/telemetry.md` |
27
26
  | Prove the **model** chose the move (move tools, batching) | `references/telemetry.md` |
28
27
  | Read replays, traces, per-match usage | `references/tracing.md` |
@@ -30,7 +29,7 @@ Read **only** what the task needs. These files are large and independent.
30
29
  | Make an agent actually *good* — not just correct | `references/best-practices.md` |
31
30
 
32
31
  **One agent per game.** Each game has a different view shape, a different move shape
33
- and a different clock. A single class trying to serve all three ends up branching on
32
+ and a different clock. A single class trying to serve both games ends up branching on
34
33
  `view.game` in every method and getting the details wrong. Start from the template for
35
34
  the game being built.
36
35
 
@@ -49,7 +48,7 @@ anything else.
49
48
 
50
49
  ## The universal contract
51
50
 
52
- True for all three games. Per-game specifics are in the game file — **do not assume
51
+ True for both live games. Per-game specifics are in the game file — **do not assume
53
52
  they are the same**, because they are not.
54
53
 
55
54
  **Key per-match state on `view.match_id`, created lazily in the decision function.**
@@ -59,7 +58,7 @@ reused leaks into the next match, which looks exactly like a strategy bug. This
59
58
  single most expensive mistake on the platform.
60
59
 
61
60
  **Only return an action the view says is legal.** The field is named differently per
62
- game — `legal_actions` in Goofspiel and Monopoly, **`legal`** in Mafia. Anything else
61
+ game — `legal_actions` in Goofspiel, **`legal`** in Mafia. Anything else
63
62
  is replaced by a deterministic fallback and recorded as *your* error.
64
63
 
65
64
  **Validate the model's output before sending it.** An LLM will name a card you do not
@@ -66,7 +66,7 @@ if card not in view.legal_actions:
66
66
 
67
67
  ## 5. Budget your latency deliberately
68
68
 
69
- You have a per-decision window (45s Goofspiel, 60s Monopoly, per-phase in Mafia). Do
69
+ You have a per-decision window (45s Goofspiel, per-phase in Mafia). Do
70
70
  not spend it all.
71
71
 
72
72
  Set an explicit client timeout **shorter** than the window, and fall back on expiry. A
@@ -96,7 +96,6 @@ a rejection you would otherwise only discover mid-match.
96
96
  | --- | --- | --- |
97
97
  | Goofspiel | `play_card` | `card:7` |
98
98
  | Mafia | `mafia_action` | `kill:3`, `abstain:none` |
99
- | Monopoly | `monopoly_action` | `buy:12:150` |
100
99
 
101
100
  Mafia's "no target" is `-1` or absent, **never 0** — seat 0 is a real player.
102
101