@agentmessier/openclaw-agent-messier 0.3.1 → 0.3.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/index.ts CHANGED
@@ -116,13 +116,16 @@ export default function register(api: OpenClawPluginApi) {
116
116
  else ctx.logger.info(`[${label}] idle — ask me to join or create a game.`);
117
117
  } catch (e) { ctx.logger.error(`[${label}] startup seating failed: ${String(e)}`); }
118
118
 
119
- // Seat poller: a seat may be taken from ANOTHER process (a chat turn, the
120
- // generated *_join tool, the dashboard). Poll the venue's lobby for a
121
- // non-ended room that references our agentId and aim the loop at it. Driven
122
- // by spec.client.lobby.route — no hardcoded /matches path. Skipped for
123
- // venues with no lobby in their spec.
124
- if (lobbyRoute) {
119
+ // Seat poller: when the agent is IDLE, a seat may be taken from ANOTHER
120
+ // process (a chat turn, the generated *_join tool, the dashboard). Poll the
121
+ // venue's lobby for a non-ended room referencing our agentId and adopt it.
122
+ // Driven by spec.client.lobby.route — no hardcoded /matches path. Skipped
123
+ // when a match is pinned (explicit room wins) or the venue has no lobby.
124
+ // Critically it only adopts while idle (session.matchId empty): once we're
125
+ // in a match it must NOT yank us into some other (e.g. stale) room.
126
+ if (lobbyRoute && !cfg.matchId) {
125
127
  poller = setInterval(async () => {
128
+ if (session.matchId) return; // already seated/playing → nothing to adopt
126
129
  try {
127
130
  const res = await fetch(`${base}${lobbyRoute}`);
128
131
  if (!res.ok) return;
@@ -130,7 +133,7 @@ export default function register(api: OpenClawPluginApi) {
130
133
  const rows = (data.matches ?? data.rows ?? []) as Record<string, any>[];
131
134
  const mine = rows.find((r) => !ENDED.has(String(r.status ?? "")) && referencesAgent(r, agentId));
132
135
  const id = mine?.id ?? mine?.[roomIdField];
133
- if (id && id !== session.matchId) {
136
+ if (id) {
134
137
  ctx.logger.info(`[${label}] found my seat in ${id} (taken elsewhere) — starting to play`);
135
138
  await session.joinAndWatch!(id);
136
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentmessier/openclaw-agent-messier",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Agent Messier multi-venue client for OpenClaw \u2014 play games and work tasks on the AgentNet platform (soccer today; venues discovered from the marketplace registry)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -86,6 +86,17 @@ describe("joinVenue — the one spec-driven seating path (tool + service share i
86
86
  expect(url).toBe("http://golf.test/rounds/r7/join");
87
87
  });
88
88
 
89
+ it("rejoin response without the seat-id field falls back to the matchId joined with", async () => {
90
+ _resetSeats();
91
+ // per-room join responses omit matchId (you already know it from the URL) —
92
+ // seat.id must still resolve, or the observe loop 404s and reclaim-loops.
93
+ vi.stubGlobal("fetch", vi.fn(async () =>
94
+ ({ ok: true, json: async () => ({ token: "t", playerIds: ["p1", "p2", "p3"], started: true }) }) as any));
95
+ const seat = await joinVenue(GOLF_VENUE, GOLF_SPEC, cfg(), { matchId: "r7" });
96
+ expect(seat.id).toBe("r7");
97
+ expect(session.matchId).toBe("r7");
98
+ });
99
+
89
100
  it("service extras (teamSize/identity) ride along but tool calls omit them", async () => {
90
101
  let body: any = null;
91
102
  vi.stubGlobal("fetch", vi.fn(async (_u: any, init?: any) => {
package/src/generate.ts CHANGED
@@ -85,7 +85,10 @@ export async function joinVenue(
85
85
  const d = r.data;
86
86
  if (typeof d.did === "string") rememberDid(cfg, d.did); // cross-process identity
87
87
  if (typeof d.token === "string") rememberToken(cfg, d.token); // cross-process seat token
88
- const seat: Seat = { id: d[sm.id], token: d[sm.token], controls: d[sm.controls] ?? [], agentId: d.did ?? meId, started: d.started, managerUrl: d.managerUrl };
88
+ // A rejoin via seatRoute already KNOWS the room (it's in the URL), so the
89
+ // server's response omits it — fall back to the matchId we joined with, or
90
+ // seat.id ends up undefined and the observe loop hits /…//… → 404 → reclaim.
91
+ const seat: Seat = { id: d[sm.id] ?? opts.matchId, token: d[sm.token], controls: d[sm.controls] ?? [], agentId: d.did ?? meId, started: d.started, managerUrl: d.managerUrl };
89
92
  seats.set(venue.id, seat);
90
93
  // soccer back-compat: the service watcher + seat-poller read `session`.
91
94
  session.matchId = seat.id ?? null; session.players = seat.controls ?? []; session.token = seat.token ?? null; session.did = seat.agentId ?? null;