@agentmessier/openclaw-agent-messier 0.3.4 → 0.3.6
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 +8 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/generate.test.ts +12 -0
- package/src/generate.ts +1 -0
- package/src/state.ts +5 -1
- package/src/tools.test.ts +4 -2
- package/src/tools.ts +10 -2
package/index.ts
CHANGED
|
@@ -25,6 +25,14 @@ export default function register(api: OpenClawPluginApi) {
|
|
|
25
25
|
api.registerTool(tool as AnyAgentTool);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Capture the EFFECTIVE model of each decision from the gateway's llm_output
|
|
29
|
+
// hook (the provider/model of the call that just ran). vfetch sends it as
|
|
30
|
+
// x-agent-model, so the pitch records the model actually PLAYING — reflecting
|
|
31
|
+
// a mid-match /model switch, not a static configured default.
|
|
32
|
+
api.registerHook("llm_output", ((event: { provider?: string; model?: string }) => {
|
|
33
|
+
if (event?.model) session.lastModel = event.provider ? `${event.provider}/${event.model}` : event.model;
|
|
34
|
+
}) as Parameters<typeof api.registerHook>[1]);
|
|
35
|
+
|
|
28
36
|
// 2. Autoplay watcher. It drives ONE realtime venue (streams observations,
|
|
29
37
|
// seats the agent, offers hands-free play) entirely from {venue, spec} —
|
|
30
38
|
// seating via spec.client.join, observe/act endpoints via spec.routes, the
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentmessier/openclaw-agent-messier",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
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",
|
package/src/generate.test.ts
CHANGED
|
@@ -146,6 +146,18 @@ describe("join-by-id and leave (the lifecycle gap closed in VA-6)", () => {
|
|
|
146
146
|
expect(out.joined).toBe("r9");
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
it("attaches x-agent-model (effective model) to venue requests once observed", async () => {
|
|
150
|
+
session.lastModel = "anthropic/claude-sonnet-4-6";
|
|
151
|
+
let hdrs: any = null;
|
|
152
|
+
vi.stubGlobal("fetch", vi.fn(async (_u: any, init?: any) => {
|
|
153
|
+
hdrs = init?.headers;
|
|
154
|
+
return { ok: true, json: async () => ({ matchId: "r1", token: "t", playerIds: [] }) } as any;
|
|
155
|
+
}));
|
|
156
|
+
await exec("golf_join", { holes: 9 });
|
|
157
|
+
expect(hdrs["x-agent-model"]).toBe("anthropic/claude-sonnet-4-6");
|
|
158
|
+
session.lastModel = null; // no model observed → header is simply omitted
|
|
159
|
+
});
|
|
160
|
+
|
|
149
161
|
it("omitting matchId quickmatches (find-or-create) via the join route", async () => {
|
|
150
162
|
let url = "";
|
|
151
163
|
vi.stubGlobal("fetch", vi.fn(async (u: any) => {
|
package/src/generate.ts
CHANGED
|
@@ -36,6 +36,7 @@ function did(venueId: string, cfg: PluginCfg): string {
|
|
|
36
36
|
* what it needs: Bearer→DID for games, x-caller-did for work, seat token for acts). */
|
|
37
37
|
async function vfetch(base: string, path: string, opts: { method?: string; body?: unknown; cfg: PluginCfg; token?: string; did: string }): Promise<{ ok: boolean; status: number; data: any }> {
|
|
38
38
|
const headers: Record<string, string> = { "x-caller-did": opts.did, "x-agent-runtime": "openclaw-plugin/0.2.0" };
|
|
39
|
+
if (session.lastModel) headers["x-agent-model"] = session.lastModel; // effective LLM for the pitch roster
|
|
39
40
|
const key = apiKeyOf(opts.cfg); if (key) headers["Authorization"] = `Bearer ${key}`;
|
|
40
41
|
if (opts.token) headers["x-agent-token"] = opts.token;
|
|
41
42
|
if (opts.body !== undefined) headers["Content-Type"] = "application/json";
|
package/src/state.ts
CHANGED
|
@@ -19,8 +19,12 @@ export const session: {
|
|
|
19
19
|
did: string | null
|
|
20
20
|
turn: number
|
|
21
21
|
lockstep: boolean
|
|
22
|
+
/** Effective LLM (provider/model) of the most recent decision, captured from
|
|
23
|
+
* the gateway's llm_output hook — sent as x-agent-model so the pitch records
|
|
24
|
+
* the model that's actually playing (reflects a mid-match /model switch). */
|
|
25
|
+
lastModel: string | null
|
|
22
26
|
/** Installed by the service: seat into a venue room (matchId omitted = quickmatch
|
|
23
27
|
* find-or-create) and start the observe/act loop. `params` are venue join params
|
|
24
28
|
* (e.g. teamSize/team for soccer). Returns the seat the loop is now driving. */
|
|
25
29
|
joinAndWatch: ((matchId?: string, params?: Record<string, unknown>) => Promise<{ id?: string; controls?: string[]; started?: boolean }>) | null
|
|
26
|
-
} = { matchId: null, players: [], token: null, did: null, turn: 0, lockstep: false, joinAndWatch: null }
|
|
30
|
+
} = { matchId: null, players: [], token: null, did: null, turn: 0, lockstep: false, lastModel: null, joinAndWatch: null }
|
package/src/tools.test.ts
CHANGED
|
@@ -16,10 +16,12 @@ describe("soccer extension join auth", () => {
|
|
|
16
16
|
});
|
|
17
17
|
afterEach(() => vi.unstubAllGlobals());
|
|
18
18
|
|
|
19
|
-
it("apiKeyOf
|
|
19
|
+
it("apiKeyOf comes from config only — never from the environment", () => {
|
|
20
20
|
expect(apiKeyOf(cfg({ apiKey: "from-cfg" }))).toBe("from-cfg");
|
|
21
|
+
// a stray env var must NOT be picked up (config-only; avoids the env+network
|
|
22
|
+
// 'credential harvesting' scanner flag and keeps config the single channel).
|
|
21
23
|
vi.stubEnv("AGENTNET_API_KEY", "from-env");
|
|
22
|
-
expect(apiKeyOf(cfg())).
|
|
24
|
+
expect(apiKeyOf(cfg())).toBeUndefined();
|
|
23
25
|
vi.unstubAllEnvs();
|
|
24
26
|
});
|
|
25
27
|
|
package/src/tools.ts
CHANGED
|
@@ -123,7 +123,11 @@ function idKey(cfg: PluginCfg): string {
|
|
|
123
123
|
|
|
124
124
|
/** AgentNet API key for join-time verification (config, then env fallback). */
|
|
125
125
|
export function apiKeyOf(cfg: PluginCfg): string | undefined {
|
|
126
|
-
|
|
126
|
+
// Config-only — the key comes from the plugin config (openclaw config set
|
|
127
|
+
// plugins.entries.<id>.config.apiKey). We deliberately do NOT read it from the
|
|
128
|
+
// OS environment: an env read + network send in one module trips OpenClaw's
|
|
129
|
+
// "credential harvesting" scanner, and config is the portable channel anyway.
|
|
130
|
+
return cfg.apiKey ?? undefined;
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
/** The agentId the server seats us under. Once the server has verified us and
|
|
@@ -305,9 +309,13 @@ export const err = (msg: string) => ({ content: [{ type: "text" as const, text:
|
|
|
305
309
|
// ── venue-URL resolution (origin → base URL); kept — generate.ts imports it. ──
|
|
306
310
|
const VENUE_DEFAULTS: Record<string, string> = { taskmarket: "http://localhost:3030" };
|
|
307
311
|
export function venueUrl(origin: string, cfg: PluginCfg): string {
|
|
312
|
+
// A registry origin is normally a full URL (used as-is) or "pitch" (the
|
|
313
|
+
// configured serverUrl). Short names fall back to a baked default. No OS
|
|
314
|
+
// environment read here — keeps this network module config-only, so
|
|
315
|
+
// OpenClaw's env+network scanner stays quiet.
|
|
308
316
|
if (origin.startsWith("http://") || origin.startsWith("https://")) return origin;
|
|
309
317
|
if (origin === "pitch") return (cfg.serverUrl ?? "http://localhost:3010").replace(/\/$/, "");
|
|
310
|
-
return
|
|
318
|
+
return VENUE_DEFAULTS[origin] ?? (cfg.serverUrl ?? "http://localhost:3010");
|
|
311
319
|
}
|
|
312
320
|
|
|
313
321
|
// ── Member / account tools — soccer cosmetic + ownership, NOT lifecycle, so
|