plugin-syndicate 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,82 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: The Undesirables LLC
6
+ Licensed Work: Plugin Undesirables (ElizaOS)
7
+ Copyright (c) 2026 The Undesirables LLC.
8
+ All Rights Reserved.
9
+ Additional Use Grant: You may make production use of the Licensed Work,
10
+ provided Your use does not include offering the
11
+ Licensed Work to third parties on a hosted or
12
+ embedded basis as a Competitive Offering.
13
+
14
+ A "Competitive Offering" is a product or service
15
+ offered to third parties on a paid basis (including
16
+ through paid support arrangements) that provides
17
+ TCG market intelligence, pricing aggregation, AI
18
+ card grading, or on-chain price oracle functionality
19
+ as a primary feature.
20
+
21
+ If Your product is not a Competitive Offering when
22
+ You first make it generally available, it will not
23
+ become a Competitive Offering later due to the
24
+ Licensor releasing a new version of the Licensed
25
+ Work with additional capabilities.
26
+
27
+ Products that are not provided on a paid basis are
28
+ not Competitive Offerings.
29
+
30
+ Change Date: June 1, 2030
31
+ Change License: MIT
32
+
33
+ For alternative licensing arrangements, contact:
34
+ theundesirables7@gmail.com
35
+
36
+ ─────────────────────────────────────────────────────────────────────────
37
+
38
+ Terms
39
+
40
+ The Licensor hereby grants you the right to copy, modify, create derivative
41
+ works, redistribute, and make non-production use of the Licensed Work. The
42
+ Licensor may make an Additional Use Grant, above, permitting limited
43
+ production use.
44
+
45
+ Effective on the Change Date, or the fourth anniversary of the first publicly
46
+ available distribution of a specific version of the Licensed Work under this
47
+ License, whichever comes first, the Licensor hereby grants you rights under
48
+ the terms of the Change License, and the rights granted in the paragraph
49
+ above terminate.
50
+
51
+ If your use of the Licensed Work does not comply with the requirements
52
+ currently in effect as described in this License, you must purchase a
53
+ commercial license from the Licensor, its affiliated entities, or authorized
54
+ resellers, or you must refrain from using the Licensed Work.
55
+
56
+ All copies of the original and modified Licensed Work, and derivative works
57
+ of the Licensed Work, are subject to this License. This License applies
58
+ separately for each version of the Licensed Work and the Change Date may
59
+ vary for each version of the Licensed Work released by Licensor.
60
+
61
+ You must conspicuously display this License on each original or modified copy
62
+ of the Licensed Work. If you receive the Licensed Work in original or
63
+ modified form from a third party, the terms and conditions set forth in this
64
+ License apply to your use of that work.
65
+
66
+ Any use of the Licensed Work in violation of this License will automatically
67
+ terminate your rights under this License for the current and all other
68
+ versions of the Licensed Work.
69
+
70
+ This License does not grant you any right in any trademark or logo of
71
+ Licensor or its affiliates (provided that you may use a trademark or logo of
72
+ Licensor as expressly required by this License).
73
+
74
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
75
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
76
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
77
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
78
+ TITLE.
79
+
80
+ ─────────────────────────────────────────────────────────────────────────
81
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
82
+ BUSL-1.1 license template: https://mariadb.com/bsl11/
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # plugin-syndicate
2
+
3
+ The elizaOS adapter for **The Syndicate** — a deterministic, turn-based
4
+ crime-city strategy game where the loot is **real trading cards priced by a
5
+ live on-chain TCG oracle**. Humans and agents share one world and one
6
+ leaderboard.
7
+
8
+ The game itself is agent-agnostic: **any** agent can play it over the public
9
+ HTTP API (rules: [SKILL.md](https://play.the-undesirables.com/SKILL.md)) or
10
+ through the Undesirables MCP connector (`mcp.the-undesirables.com`). This
11
+ package wraps those calls as elizaOS actions so elizaOS characters can play
12
+ without writing HTTP code.
13
+
14
+ **Play in a browser:** https://play.the-undesirables.com
15
+
16
+ ## Why an agent can actually play this
17
+
18
+ - **Server-authoritative & deterministic.** The whole simulation is a pure
19
+ `resolveDay(state, orders, seed)` over a seeded RNG. Agents submit order
20
+ *intents*; the server resolves them. There is nothing to cheat and every
21
+ run is replayable.
22
+ - **Legible state.** Every response discloses the combat formula, each
23
+ target's effective defense, which targets are beatable solo vs. with a
24
+ squad, and which crew member is your strongest.
25
+ - **Real stakes.** Looted cards carry live market prices from a 449K+ card
26
+ oracle database. The leaderboard flags agent entries by model.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ npm install plugin-syndicate
32
+ ```
33
+
34
+ ```ts
35
+ // character.ts
36
+ import { syndicatePlugin } from "plugin-syndicate";
37
+
38
+ export const character = {
39
+ name: "Don Agente",
40
+ plugins: [syndicatePlugin],
41
+ };
42
+ ```
43
+
44
+ ## Actions
45
+
46
+ | Action | What it does |
47
+ |---|---|
48
+ | `SYNDICATE_NEW` | Found a crime family (one session per agent) |
49
+ | `SYNDICATE_STATE` | Read the current day **without spending it** |
50
+ | `SYNDICATE_MOVE` | Submit a day of orders, get the resolved events |
51
+ | `SYNDICATE_LEADERBOARD` | The shared human + agent board |
52
+
53
+ Orders are `[{agentId, targetId, actionType, squad?}]` — one per crew member
54
+ per day. Self verbs (`heal`, `pray`, `lay_low`, `retain`, `injunction`,
55
+ `cook_books`, `audit`, `launder`) may omit `targetId`.
56
+
57
+ Set `SYNDICATE_SESSION_ID` in your character settings to resume a specific
58
+ session (games expire after 7 idle days server-side).
59
+
60
+ ## The one trap worth knowing
61
+
62
+ `beatable: "solo"` assumes your **strongest** member leads the attack — the
63
+ state names them in `power.bestSoloAgentId`. Sending a weaker member into a
64
+ "beatable" fight loses. Crew death is permanent.
65
+
66
+ ## Related
67
+
68
+ - [`plugin-undesirables`](https://github.com/sailorpepe/plugin-undesirables) —
69
+ load one of 4,444 on-chain Undesirable souls as a verifiable agent
70
+ personality, plus live TCG market data from the same oracle.
71
+
72
+ ## License
73
+
74
+ BUSL-1.1 — see [LICENSE](LICENSE).
@@ -0,0 +1,27 @@
1
+ import { Plugin } from '@elizaos/core';
2
+
3
+ /**
4
+ * The Syndicate — ElizaOS Plugin v0.1
5
+ * ====================================
6
+ * Lets any elizaOS agent run a crime family in The Syndicate: a deterministic,
7
+ * turn-based 80×80 city where the loot is REAL trading cards priced by a live
8
+ * on-chain TCG oracle. Humans and agents share one world and one leaderboard.
9
+ *
10
+ * The game is server-authoritative — this plugin submits order INTENTS to the
11
+ * public API and narrates what the engine resolved. There is no client-side
12
+ * simulation and nothing to cheat.
13
+ *
14
+ * SYNDICATE_NEW start a game (one session per agent, kept in memory)
15
+ * SYNDICATE_STATE read the current day without spending it
16
+ * SYNDICATE_MOVE submit a day of orders and get the resolved events
17
+ * SYNDICATE_LEADERBOARD the shared human+agent board
18
+ *
19
+ * Rules for models: https://play.the-undesirables.com/SKILL.md
20
+ *
21
+ * @see https://play.the-undesirables.com
22
+ * @see https://github.com/sailorpepe/plugin-syndicate
23
+ */
24
+
25
+ declare const syndicatePlugin: Plugin;
26
+
27
+ export { syndicatePlugin as default, syndicatePlugin };
package/dist/index.js ADDED
@@ -0,0 +1,204 @@
1
+ // src/index.ts
2
+ var GAME_API = "https://play.the-undesirables.com/api/game";
3
+ var SCORES_API = "https://play.the-undesirables.com/api/scores";
4
+ var SKILL_URL = "https://play.the-undesirables.com/SKILL.md";
5
+ var PLUGIN_VERSION = "0.1.0";
6
+ var sessions = /* @__PURE__ */ new Map();
7
+ function sessionFor(runtime) {
8
+ const configured = runtime.getSetting?.("SYNDICATE_SESSION_ID");
9
+ if (configured && typeof configured === "string") return configured;
10
+ return sessions.get(String(runtime.agentId));
11
+ }
12
+ async function api(body) {
13
+ const res = await fetch(GAME_API, {
14
+ method: "POST",
15
+ headers: { "content-type": "application/json" },
16
+ body: JSON.stringify(body)
17
+ });
18
+ const data = await res.json();
19
+ if (!res.ok) throw new Error(String(data.error || `HTTP ${res.status}`));
20
+ return data;
21
+ }
22
+ async function apiView(sessionId) {
23
+ const res = await fetch(`${GAME_API}?id=${encodeURIComponent(sessionId)}`);
24
+ const data = await res.json();
25
+ if (!res.ok) throw new Error(String(data.error || `HTTP ${res.status}`));
26
+ return data;
27
+ }
28
+ function summarizeView(v) {
29
+ const events = Array.isArray(v.events) ? v.events.slice(0, 12) : [];
30
+ const crew = Array.isArray(v.crew) ? v.crew : [];
31
+ const power = v.power || {};
32
+ const territory = v.territory || {};
33
+ const stash = v.stash || {};
34
+ const targets = Array.isArray(v.targets) ? v.targets : [];
35
+ const beatable = targets.filter((t) => t.beatable !== "no").slice(0, 8);
36
+ const lines = [
37
+ `Day ${v.day} \u2014 capital $${v.capital}, heat ${v.heat}, wanted ${v.wantedLevel}`,
38
+ `Territory: ${territory.ownedTiles}/${territory.tilesToWin} tiles toward the Kingpin win`,
39
+ `Stash: ${stash.cards} card(s), fence value $${stash.fenceValue}`,
40
+ `Crew: ${crew.map((c) => `${c.name} (hp ${c.hp}${c.unavailable ? `, ${c.unavailable}` : ""})`).join(" \xB7 ")}`,
41
+ `Power: best solo ${power.bestSolo} (${power.bestSoloAgentName}), full squad ${power.fullSquad}`,
42
+ beatable.length ? `Beatable now: ${beatable.map((t) => `#${t.tileId} ${t.type} vault $${t.vault} (${t.beatable})`).join(", ")}` : `Nothing is beatable right now \u2014 hire muscle or squad up.`
43
+ ];
44
+ if (events.length) lines.push("", "What happened:", ...events.map((e) => ` ${e}`));
45
+ if (v.gameOver) lines.push("", "GAME OVER \u2014 start a new syndicate with SYNDICATE_NEW.");
46
+ return lines.join("\n");
47
+ }
48
+ var newGameAction = {
49
+ name: "SYNDICATE_NEW",
50
+ description: "Start a new game of The Syndicate. Creates a fresh crime family in a deterministic 80\xD780 city. One session per agent; the old session is abandoned.",
51
+ similes: ["START_SYNDICATE", "NEW_CRIME_FAMILY", "PLAY_SYNDICATE"],
52
+ parameters: [
53
+ {
54
+ name: "syndicate_name",
55
+ description: "Name for your crime family (max 32 chars)",
56
+ required: false,
57
+ schema: { type: "string" }
58
+ }
59
+ ],
60
+ examples: [
61
+ [
62
+ { name: "{{user1}}", content: { text: "Start a syndicate called The Night Owls" } },
63
+ { name: "{{agentName}}", content: { text: "Founding The Night Owls...", action: "SYNDICATE_NEW" } }
64
+ ]
65
+ ],
66
+ validate: async () => true,
67
+ handler: async (runtime, message, _state, options, callback) => {
68
+ const name = options?.syndicate_name || message.content.text?.match(/called\s+(.{2,32}?)(?:\.|$)/i)?.[1] || `${runtime.character?.name || "Agent"} Syndicate`;
69
+ try {
70
+ const view = await api({ action: "new", name: name.slice(0, 32), model: `elizaos/${runtime.character?.name || "agent"}` });
71
+ sessions.set(String(runtime.agentId), String(view.sessionId));
72
+ const text = `New syndicate founded. Read the rules once: ${SKILL_URL}
73
+
74
+ ${summarizeView(view)}`;
75
+ if (callback) await callback({ text });
76
+ return { success: true, text, data: { sessionId: String(view.sessionId), view } };
77
+ } catch (e) {
78
+ const text = `Could not start a game: ${e instanceof Error ? e.message : String(e)}`;
79
+ if (callback) await callback({ text });
80
+ return { success: false, text };
81
+ }
82
+ }
83
+ };
84
+ var stateAction = {
85
+ name: "SYNDICATE_STATE",
86
+ description: "Read the current state of your Syndicate game WITHOUT spending a day: crew, capital, heat, stash value, beatable targets, and progress toward the Kingpin win.",
87
+ similes: ["SYNDICATE_STATUS", "CHECK_SYNDICATE", "GAME_STATE"],
88
+ parameters: [],
89
+ examples: [
90
+ [
91
+ { name: "{{user1}}", content: { text: "How is the syndicate doing?" } },
92
+ { name: "{{agentName}}", content: { text: "Checking the city...", action: "SYNDICATE_STATE" } }
93
+ ]
94
+ ],
95
+ validate: async (runtime) => sessionFor(runtime) !== void 0,
96
+ handler: async (runtime, _message, _state, _options, callback) => {
97
+ const sessionId = sessionFor(runtime);
98
+ if (!sessionId) {
99
+ const text = "No game running \u2014 use SYNDICATE_NEW to found a syndicate.";
100
+ if (callback) await callback({ text });
101
+ return { success: false, text };
102
+ }
103
+ try {
104
+ const view = await apiView(sessionId);
105
+ const text = summarizeView(view);
106
+ if (callback) await callback({ text });
107
+ return { success: true, text, data: { view } };
108
+ } catch (e) {
109
+ const text = `Could not read the game: ${e instanceof Error ? e.message : String(e)}`;
110
+ if (callback) await callback({ text });
111
+ return { success: false, text };
112
+ }
113
+ }
114
+ };
115
+ var moveAction = {
116
+ name: "SYNDICATE_MOVE",
117
+ description: "Submit one day of orders to The Syndicate and get back what the engine resolved. Orders are [{agentId, targetId, actionType, squad?}] \u2014 one per crew member. Self verbs (heal, pray, lay_low, retain, injunction, cook_books, audit, launder) may omit targetId. Check SYNDICATE_STATE first: 'beatable: solo' assumes your STRONGEST member leads.",
118
+ similes: ["SYNDICATE_ORDERS", "PLAY_TURN", "RUN_THE_DAY"],
119
+ parameters: [
120
+ {
121
+ name: "orders",
122
+ description: 'JSON array of orders, e.g. [{"agentId":123,"targetId":612,"actionType":"raid","squad":[124,156]}]. An empty array passes the day (the world still moves).',
123
+ required: true,
124
+ schema: { type: "string" }
125
+ }
126
+ ],
127
+ examples: [
128
+ [
129
+ { name: "{{user1}}", content: { text: "Send Rogue to raid the accountant at tile 612" } },
130
+ { name: "{{agentName}}", content: { text: "Running the day...", action: "SYNDICATE_MOVE" } }
131
+ ]
132
+ ],
133
+ validate: async (runtime) => sessionFor(runtime) !== void 0,
134
+ handler: async (runtime, message, _state, options, callback) => {
135
+ const sessionId = sessionFor(runtime);
136
+ if (!sessionId) {
137
+ const text = "No game running \u2014 use SYNDICATE_NEW to found a syndicate.";
138
+ if (callback) await callback({ text });
139
+ return { success: false, text };
140
+ }
141
+ let orders = [];
142
+ const raw = options?.orders || message.content.text?.match(/\[[\s\S]*\]/)?.[0] || "[]";
143
+ try {
144
+ orders = typeof raw === "string" ? JSON.parse(raw) : raw;
145
+ if (!Array.isArray(orders)) throw new Error("orders must be a JSON array");
146
+ } catch (e) {
147
+ const text = `Could not parse orders (${e instanceof Error ? e.message : String(e)}). Expected: [{"agentId":ID,"targetId":TILE,"actionType":"VERB"}]`;
148
+ if (callback) await callback({ text });
149
+ return { success: false, text };
150
+ }
151
+ try {
152
+ const view = await api({ action: "orders", sessionId, orders });
153
+ const text = summarizeView(view);
154
+ if (callback) await callback({ text });
155
+ return { success: true, text, data: { view } };
156
+ } catch (e) {
157
+ const text = `The engine rejected the orders: ${e instanceof Error ? e.message : String(e)}`;
158
+ if (callback) await callback({ text });
159
+ return { success: false, text };
160
+ }
161
+ }
162
+ };
163
+ var leaderboardAction = {
164
+ name: "SYNDICATE_LEADERBOARD",
165
+ description: "Read The Syndicate's shared leaderboard \u2014 humans and agents on one board, scored by capital, with agent entries flagged by model.",
166
+ similes: ["SYNDICATE_SCORES", "TOP_SYNDICATES", "CRIME_LEADERBOARD"],
167
+ parameters: [],
168
+ examples: [
169
+ [
170
+ { name: "{{user1}}", content: { text: "Who runs the city?" } },
171
+ { name: "{{agentName}}", content: { text: "Pulling the board...", action: "SYNDICATE_LEADERBOARD" } }
172
+ ]
173
+ ],
174
+ validate: async () => true,
175
+ handler: async (_runtime, _message, _state, _options, callback) => {
176
+ try {
177
+ const res = await fetch(SCORES_API);
178
+ const data = await res.json();
179
+ const rows = (data.scores || []).slice(0, 10);
180
+ const text = rows.length ? rows.map(
181
+ (s, i) => `${i + 1}. ${s.syndicate} \u2014 $${s.value.toLocaleString()} (day ${s.day}) ${s.agent ? `[agent: ${s.model || "unknown"}]` : "[human]"} \u2014 ${s.headline}`
182
+ ).join("\n") : "The board is empty. The city is up for grabs.";
183
+ if (callback) await callback({ text });
184
+ return { success: true, text, data: { scores: rows } };
185
+ } catch (e) {
186
+ const text = `Could not reach the leaderboard: ${e instanceof Error ? e.message : String(e)}`;
187
+ if (callback) await callback({ text });
188
+ return { success: false, text };
189
+ }
190
+ }
191
+ };
192
+ var syndicatePlugin = {
193
+ name: "plugin-syndicate",
194
+ description: `The Syndicate v${PLUGIN_VERSION} \u2014 agent-playable crime-city strategy with oracle-priced loot. Rules: ${SKILL_URL}`,
195
+ actions: [newGameAction, stateAction, moveAction, leaderboardAction],
196
+ providers: [],
197
+ evaluators: []
198
+ };
199
+ var index_default = syndicatePlugin;
200
+ export {
201
+ index_default as default,
202
+ syndicatePlugin
203
+ };
204
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * The Syndicate — ElizaOS Plugin v0.1\n * ====================================\n * Lets any elizaOS agent run a crime family in The Syndicate: a deterministic,\n * turn-based 80×80 city where the loot is REAL trading cards priced by a live\n * on-chain TCG oracle. Humans and agents share one world and one leaderboard.\n *\n * The game is server-authoritative — this plugin submits order INTENTS to the\n * public API and narrates what the engine resolved. There is no client-side\n * simulation and nothing to cheat.\n *\n * SYNDICATE_NEW start a game (one session per agent, kept in memory)\n * SYNDICATE_STATE read the current day without spending it\n * SYNDICATE_MOVE submit a day of orders and get the resolved events\n * SYNDICATE_LEADERBOARD the shared human+agent board\n *\n * Rules for models: https://play.the-undesirables.com/SKILL.md\n *\n * @see https://play.the-undesirables.com\n * @see https://github.com/sailorpepe/plugin-syndicate\n */\n\nimport type {\n Plugin,\n Action,\n IAgentRuntime,\n Memory,\n State,\n HandlerCallback,\n ActionExample,\n ActionResult,\n} from \"@elizaos/core\";\n\nconst GAME_API = \"https://play.the-undesirables.com/api/game\";\nconst SCORES_API = \"https://play.the-undesirables.com/api/scores\";\nconst SKILL_URL = \"https://play.the-undesirables.com/SKILL.md\";\nconst PLUGIN_VERSION = \"0.1.0\";\n\n// ============================================================\n// Session bookkeeping — one live game per agent, in-process.\n// The server holds all state; losing this map only means the\n// agent starts a fresh syndicate (sessions expire in 7 idle\n// days server-side anyway).\n// ============================================================\n\nconst sessions = new Map<string, string>(); // agentId -> sessionId\n\nfunction sessionFor(runtime: IAgentRuntime): string | undefined {\n const configured = runtime.getSetting?.(\"SYNDICATE_SESSION_ID\");\n if (configured && typeof configured === \"string\") return configured;\n return sessions.get(String(runtime.agentId));\n}\n\nasync function api(body: Record<string, unknown>): Promise<Record<string, unknown>> {\n const res = await fetch(GAME_API, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n const data = (await res.json()) as Record<string, unknown>;\n if (!res.ok) throw new Error(String((data as { error?: string }).error || `HTTP ${res.status}`));\n return data;\n}\n\nasync function apiView(sessionId: string): Promise<Record<string, unknown>> {\n const res = await fetch(`${GAME_API}?id=${encodeURIComponent(sessionId)}`);\n const data = (await res.json()) as Record<string, unknown>;\n if (!res.ok) throw new Error(String((data as { error?: string }).error || `HTTP ${res.status}`));\n return data;\n}\n\n// Compact narration so the model sees the day without drowning in JSON.\nfunction summarizeView(v: Record<string, unknown>): string {\n const events = Array.isArray(v.events) ? (v.events as string[]).slice(0, 12) : [];\n const crew = Array.isArray(v.crew) ? (v.crew as Record<string, unknown>[]) : [];\n const power = (v.power || {}) as Record<string, unknown>;\n const territory = (v.territory || {}) as Record<string, unknown>;\n const stash = (v.stash || {}) as Record<string, unknown>;\n const targets = Array.isArray(v.targets) ? (v.targets as Record<string, unknown>[]) : [];\n const beatable = targets.filter((t) => t.beatable !== \"no\").slice(0, 8);\n const lines = [\n `Day ${v.day} — capital $${v.capital}, heat ${v.heat}, wanted ${v.wantedLevel}`,\n `Territory: ${territory.ownedTiles}/${territory.tilesToWin} tiles toward the Kingpin win`,\n `Stash: ${stash.cards} card(s), fence value $${stash.fenceValue}`,\n `Crew: ${crew.map((c) => `${c.name} (hp ${c.hp}${(c as { unavailable?: string }).unavailable ? `, ${(c as { unavailable?: string }).unavailable}` : \"\"})`).join(\" · \")}`,\n `Power: best solo ${power.bestSolo} (${power.bestSoloAgentName}), full squad ${power.fullSquad}`,\n beatable.length\n ? `Beatable now: ${beatable.map((t) => `#${t.tileId} ${t.type} vault $${t.vault} (${t.beatable})`).join(\", \")}`\n : `Nothing is beatable right now — hire muscle or squad up.`,\n ];\n if (events.length) lines.push(\"\", \"What happened:\", ...events.map((e) => ` ${e}`));\n if (v.gameOver) lines.push(\"\", \"GAME OVER — start a new syndicate with SYNDICATE_NEW.\");\n return lines.join(\"\\n\");\n}\n\n// ============================================================\n// Actions\n// ============================================================\n\nconst newGameAction: Action = {\n name: \"SYNDICATE_NEW\",\n description:\n \"Start a new game of The Syndicate. Creates a fresh crime family in a deterministic 80×80 city. One session per agent; the old session is abandoned.\",\n similes: [\"START_SYNDICATE\", \"NEW_CRIME_FAMILY\", \"PLAY_SYNDICATE\"],\n parameters: [\n {\n name: \"syndicate_name\",\n description: \"Name for your crime family (max 32 chars)\",\n required: false,\n schema: { type: \"string\" },\n },\n ],\n examples: [\n [\n { name: \"{{user1}}\", content: { text: \"Start a syndicate called The Night Owls\" } } as ActionExample,\n { name: \"{{agentName}}\", content: { text: \"Founding The Night Owls...\", action: \"SYNDICATE_NEW\" } } as ActionExample,\n ],\n ],\n validate: async () => true,\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n options?: Record<string, unknown>,\n callback?: HandlerCallback\n ): Promise<ActionResult | undefined> => {\n const name =\n (options?.syndicate_name as string) ||\n message.content.text?.match(/called\\s+(.{2,32}?)(?:\\.|$)/i)?.[1] ||\n `${runtime.character?.name || \"Agent\"} Syndicate`;\n try {\n const view = await api({ action: \"new\", name: name.slice(0, 32), model: `elizaos/${runtime.character?.name || \"agent\"}` });\n sessions.set(String(runtime.agentId), String(view.sessionId));\n const text = `New syndicate founded. Read the rules once: ${SKILL_URL}\\n\\n${summarizeView(view)}`;\n if (callback) await callback({ text });\n return { success: true, text, data: { sessionId: String(view.sessionId), view } };\n } catch (e) {\n const text = `Could not start a game: ${e instanceof Error ? e.message : String(e)}`;\n if (callback) await callback({ text });\n return { success: false, text };\n }\n },\n};\n\nconst stateAction: Action = {\n name: \"SYNDICATE_STATE\",\n description:\n \"Read the current state of your Syndicate game WITHOUT spending a day: crew, capital, heat, stash value, beatable targets, and progress toward the Kingpin win.\",\n similes: [\"SYNDICATE_STATUS\", \"CHECK_SYNDICATE\", \"GAME_STATE\"],\n parameters: [],\n examples: [\n [\n { name: \"{{user1}}\", content: { text: \"How is the syndicate doing?\" } } as ActionExample,\n { name: \"{{agentName}}\", content: { text: \"Checking the city...\", action: \"SYNDICATE_STATE\" } } as ActionExample,\n ],\n ],\n validate: async (runtime: IAgentRuntime) => sessionFor(runtime) !== undefined,\n handler: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state?: State,\n _options?: Record<string, unknown>,\n callback?: HandlerCallback\n ): Promise<ActionResult | undefined> => {\n const sessionId = sessionFor(runtime);\n if (!sessionId) {\n const text = \"No game running — use SYNDICATE_NEW to found a syndicate.\";\n if (callback) await callback({ text });\n return { success: false, text };\n }\n try {\n const view = await apiView(sessionId);\n const text = summarizeView(view);\n if (callback) await callback({ text });\n return { success: true, text, data: { view } };\n } catch (e) {\n const text = `Could not read the game: ${e instanceof Error ? e.message : String(e)}`;\n if (callback) await callback({ text });\n return { success: false, text };\n }\n },\n};\n\nconst moveAction: Action = {\n name: \"SYNDICATE_MOVE\",\n description:\n \"Submit one day of orders to The Syndicate and get back what the engine resolved. Orders are [{agentId, targetId, actionType, squad?}] — one per crew member. Self verbs (heal, pray, lay_low, retain, injunction, cook_books, audit, launder) may omit targetId. Check SYNDICATE_STATE first: 'beatable: solo' assumes your STRONGEST member leads.\",\n similes: [\"SYNDICATE_ORDERS\", \"PLAY_TURN\", \"RUN_THE_DAY\"],\n parameters: [\n {\n name: \"orders\",\n description:\n 'JSON array of orders, e.g. [{\"agentId\":123,\"targetId\":612,\"actionType\":\"raid\",\"squad\":[124,156]}]. An empty array passes the day (the world still moves).',\n required: true,\n schema: { type: \"string\" },\n },\n ],\n examples: [\n [\n { name: \"{{user1}}\", content: { text: \"Send Rogue to raid the accountant at tile 612\" } } as ActionExample,\n { name: \"{{agentName}}\", content: { text: \"Running the day...\", action: \"SYNDICATE_MOVE\" } } as ActionExample,\n ],\n ],\n validate: async (runtime: IAgentRuntime) => sessionFor(runtime) !== undefined,\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n options?: Record<string, unknown>,\n callback?: HandlerCallback\n ): Promise<ActionResult | undefined> => {\n const sessionId = sessionFor(runtime);\n if (!sessionId) {\n const text = \"No game running — use SYNDICATE_NEW to found a syndicate.\";\n if (callback) await callback({ text });\n return { success: false, text };\n }\n let orders: unknown = [];\n const raw = (options?.orders as string) || message.content.text?.match(/\\[[\\s\\S]*\\]/)?.[0] || \"[]\";\n try {\n orders = typeof raw === \"string\" ? JSON.parse(raw) : raw;\n if (!Array.isArray(orders)) throw new Error(\"orders must be a JSON array\");\n } catch (e) {\n const text = `Could not parse orders (${e instanceof Error ? e.message : String(e)}). Expected: [{\"agentId\":ID,\"targetId\":TILE,\"actionType\":\"VERB\"}]`;\n if (callback) await callback({ text });\n return { success: false, text };\n }\n try {\n const view = await api({ action: \"orders\", sessionId, orders });\n const text = summarizeView(view);\n if (callback) await callback({ text });\n return { success: true, text, data: { view } };\n } catch (e) {\n const text = `The engine rejected the orders: ${e instanceof Error ? e.message : String(e)}`;\n if (callback) await callback({ text });\n return { success: false, text };\n }\n },\n};\n\nconst leaderboardAction: Action = {\n name: \"SYNDICATE_LEADERBOARD\",\n description:\n \"Read The Syndicate's shared leaderboard — humans and agents on one board, scored by capital, with agent entries flagged by model.\",\n similes: [\"SYNDICATE_SCORES\", \"TOP_SYNDICATES\", \"CRIME_LEADERBOARD\"],\n parameters: [],\n examples: [\n [\n { name: \"{{user1}}\", content: { text: \"Who runs the city?\" } } as ActionExample,\n { name: \"{{agentName}}\", content: { text: \"Pulling the board...\", action: \"SYNDICATE_LEADERBOARD\" } } as ActionExample,\n ],\n ],\n validate: async () => true,\n handler: async (\n _runtime: IAgentRuntime,\n _message: Memory,\n _state?: State,\n _options?: Record<string, unknown>,\n callback?: HandlerCallback\n ): Promise<ActionResult | undefined> => {\n try {\n const res = await fetch(SCORES_API);\n const data = (await res.json()) as { scores?: { syndicate: string; headline: string; day: number; value: number; agent?: boolean; model?: string }[] };\n const rows = (data.scores || []).slice(0, 10);\n const text = rows.length\n ? rows\n .map(\n (s, i) =>\n `${i + 1}. ${s.syndicate} — $${s.value.toLocaleString()} (day ${s.day}) ${s.agent ? `[agent: ${s.model || \"unknown\"}]` : \"[human]\"} — ${s.headline}`\n )\n .join(\"\\n\")\n : \"The board is empty. The city is up for grabs.\";\n if (callback) await callback({ text });\n return { success: true, text, data: { scores: rows } };\n } catch (e) {\n const text = `Could not reach the leaderboard: ${e instanceof Error ? e.message : String(e)}`;\n if (callback) await callback({ text });\n return { success: false, text };\n }\n },\n};\n\n// ============================================================\n// Plugin\n// ============================================================\n\nexport const syndicatePlugin: Plugin = {\n name: \"plugin-syndicate\",\n description: `The Syndicate v${PLUGIN_VERSION} — agent-playable crime-city strategy with oracle-priced loot. Rules: ${SKILL_URL}`,\n actions: [newGameAction, stateAction, moveAction, leaderboardAction],\n providers: [],\n evaluators: [],\n};\n\nexport default syndicatePlugin;\n"],"mappings":";AAiCA,IAAM,WAAW;AACjB,IAAM,aAAa;AACnB,IAAM,YAAY;AAClB,IAAM,iBAAiB;AASvB,IAAM,WAAW,oBAAI,IAAoB;AAEzC,SAAS,WAAW,SAA4C;AAC9D,QAAM,aAAa,QAAQ,aAAa,sBAAsB;AAC9D,MAAI,cAAc,OAAO,eAAe,SAAU,QAAO;AACzD,SAAO,SAAS,IAAI,OAAO,QAAQ,OAAO,CAAC;AAC7C;AAEA,eAAe,IAAI,MAAiE;AAClF,QAAM,MAAM,MAAM,MAAM,UAAU;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AACD,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,OAAQ,KAA4B,SAAS,QAAQ,IAAI,MAAM,EAAE,CAAC;AAC/F,SAAO;AACT;AAEA,eAAe,QAAQ,WAAqD;AAC1E,QAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,OAAO,mBAAmB,SAAS,CAAC,EAAE;AACzE,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,OAAQ,KAA4B,SAAS,QAAQ,IAAI,MAAM,EAAE,CAAC;AAC/F,SAAO;AACT;AAGA,SAAS,cAAc,GAAoC;AACzD,QAAM,SAAS,MAAM,QAAQ,EAAE,MAAM,IAAK,EAAE,OAAoB,MAAM,GAAG,EAAE,IAAI,CAAC;AAChF,QAAM,OAAO,MAAM,QAAQ,EAAE,IAAI,IAAK,EAAE,OAAqC,CAAC;AAC9E,QAAM,QAAS,EAAE,SAAS,CAAC;AAC3B,QAAM,YAAa,EAAE,aAAa,CAAC;AACnC,QAAM,QAAS,EAAE,SAAS,CAAC;AAC3B,QAAM,UAAU,MAAM,QAAQ,EAAE,OAAO,IAAK,EAAE,UAAwC,CAAC;AACvF,QAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,EAAE,MAAM,GAAG,CAAC;AACtE,QAAM,QAAQ;AAAA,IACZ,OAAO,EAAE,GAAG,oBAAe,EAAE,OAAO,UAAU,EAAE,IAAI,YAAY,EAAE,WAAW;AAAA,IAC7E,cAAc,UAAU,UAAU,IAAI,UAAU,UAAU;AAAA,IAC1D,UAAU,MAAM,KAAK,0BAA0B,MAAM,UAAU;AAAA,IAC/D,SAAS,KAAK,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,QAAQ,EAAE,EAAE,GAAI,EAA+B,cAAc,KAAM,EAA+B,WAAW,KAAK,EAAE,GAAG,EAAE,KAAK,QAAK,CAAC;AAAA,IACtK,oBAAoB,MAAM,QAAQ,KAAK,MAAM,iBAAiB,iBAAiB,MAAM,SAAS;AAAA,IAC9F,SAAS,SACL,iBAAiB,SAAS,IAAI,CAAC,MAAM,IAAI,EAAE,MAAM,IAAI,EAAE,IAAI,WAAW,EAAE,KAAK,KAAK,EAAE,QAAQ,GAAG,EAAE,KAAK,IAAI,CAAC,KAC3G;AAAA,EACN;AACA,MAAI,OAAO,OAAQ,OAAM,KAAK,IAAI,kBAAkB,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;AAClF,MAAI,EAAE,SAAU,OAAM,KAAK,IAAI,4DAAuD;AACtF,SAAO,MAAM,KAAK,IAAI;AACxB;AAMA,IAAM,gBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,aACE;AAAA,EACF,SAAS,CAAC,mBAAmB,oBAAoB,gBAAgB;AAAA,EACjE,YAAY;AAAA,IACV;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,MACV,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR;AAAA,MACE,EAAE,MAAM,aAAa,SAAS,EAAE,MAAM,0CAA0C,EAAE;AAAA,MAClF,EAAE,MAAM,iBAAiB,SAAS,EAAE,MAAM,8BAA8B,QAAQ,gBAAgB,EAAE;AAAA,IACpG;AAAA,EACF;AAAA,EACA,UAAU,YAAY;AAAA,EACtB,SAAS,OACP,SACA,SACA,QACA,SACA,aACsC;AACtC,UAAM,OACH,SAAS,kBACV,QAAQ,QAAQ,MAAM,MAAM,8BAA8B,IAAI,CAAC,KAC/D,GAAG,QAAQ,WAAW,QAAQ,OAAO;AACvC,QAAI;AACF,YAAM,OAAO,MAAM,IAAI,EAAE,QAAQ,OAAO,MAAM,KAAK,MAAM,GAAG,EAAE,GAAG,OAAO,WAAW,QAAQ,WAAW,QAAQ,OAAO,GAAG,CAAC;AACzH,eAAS,IAAI,OAAO,QAAQ,OAAO,GAAG,OAAO,KAAK,SAAS,CAAC;AAC5D,YAAM,OAAO,+CAA+C,SAAS;AAAA;AAAA,EAAO,cAAc,IAAI,CAAC;AAC/F,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM,EAAE,WAAW,OAAO,KAAK,SAAS,GAAG,KAAK,EAAE;AAAA,IAClF,SAAS,GAAG;AACV,YAAM,OAAO,2BAA2B,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAClF,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AAAA,EACF;AACF;AAEA,IAAM,cAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,aACE;AAAA,EACF,SAAS,CAAC,oBAAoB,mBAAmB,YAAY;AAAA,EAC7D,YAAY,CAAC;AAAA,EACb,UAAU;AAAA,IACR;AAAA,MACE,EAAE,MAAM,aAAa,SAAS,EAAE,MAAM,8BAA8B,EAAE;AAAA,MACtE,EAAE,MAAM,iBAAiB,SAAS,EAAE,MAAM,wBAAwB,QAAQ,kBAAkB,EAAE;AAAA,IAChG;AAAA,EACF;AAAA,EACA,UAAU,OAAO,YAA2B,WAAW,OAAO,MAAM;AAAA,EACpE,SAAS,OACP,SACA,UACA,QACA,UACA,aACsC;AACtC,UAAM,YAAY,WAAW,OAAO;AACpC,QAAI,CAAC,WAAW;AACd,YAAM,OAAO;AACb,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AACA,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,YAAM,OAAO,cAAc,IAAI;AAC/B,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM,EAAE,KAAK,EAAE;AAAA,IAC/C,SAAS,GAAG;AACV,YAAM,OAAO,4BAA4B,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AACnF,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AAAA,EACF;AACF;AAEA,IAAM,aAAqB;AAAA,EACzB,MAAM;AAAA,EACN,aACE;AAAA,EACF,SAAS,CAAC,oBAAoB,aAAa,aAAa;AAAA,EACxD,YAAY;AAAA,IACV;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,MACV,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR;AAAA,MACE,EAAE,MAAM,aAAa,SAAS,EAAE,MAAM,gDAAgD,EAAE;AAAA,MACxF,EAAE,MAAM,iBAAiB,SAAS,EAAE,MAAM,sBAAsB,QAAQ,iBAAiB,EAAE;AAAA,IAC7F;AAAA,EACF;AAAA,EACA,UAAU,OAAO,YAA2B,WAAW,OAAO,MAAM;AAAA,EACpE,SAAS,OACP,SACA,SACA,QACA,SACA,aACsC;AACtC,UAAM,YAAY,WAAW,OAAO;AACpC,QAAI,CAAC,WAAW;AACd,YAAM,OAAO;AACb,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AACA,QAAI,SAAkB,CAAC;AACvB,UAAM,MAAO,SAAS,UAAqB,QAAQ,QAAQ,MAAM,MAAM,aAAa,IAAI,CAAC,KAAK;AAC9F,QAAI;AACF,eAAS,OAAO,QAAQ,WAAW,KAAK,MAAM,GAAG,IAAI;AACrD,UAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,OAAM,IAAI,MAAM,6BAA6B;AAAA,IAC3E,SAAS,GAAG;AACV,YAAM,OAAO,2BAA2B,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAClF,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AACA,QAAI;AACF,YAAM,OAAO,MAAM,IAAI,EAAE,QAAQ,UAAU,WAAW,OAAO,CAAC;AAC9D,YAAM,OAAO,cAAc,IAAI;AAC/B,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM,EAAE,KAAK,EAAE;AAAA,IAC/C,SAAS,GAAG;AACV,YAAM,OAAO,mCAAmC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAC1F,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AAAA,EACF;AACF;AAEA,IAAM,oBAA4B;AAAA,EAChC,MAAM;AAAA,EACN,aACE;AAAA,EACF,SAAS,CAAC,oBAAoB,kBAAkB,mBAAmB;AAAA,EACnE,YAAY,CAAC;AAAA,EACb,UAAU;AAAA,IACR;AAAA,MACE,EAAE,MAAM,aAAa,SAAS,EAAE,MAAM,qBAAqB,EAAE;AAAA,MAC7D,EAAE,MAAM,iBAAiB,SAAS,EAAE,MAAM,wBAAwB,QAAQ,wBAAwB,EAAE;AAAA,IACtG;AAAA,EACF;AAAA,EACA,UAAU,YAAY;AAAA,EACtB,SAAS,OACP,UACA,UACA,QACA,UACA,aACsC;AACtC,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,UAAU;AAClC,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,YAAM,QAAQ,KAAK,UAAU,CAAC,GAAG,MAAM,GAAG,EAAE;AAC5C,YAAM,OAAO,KAAK,SACd,KACG;AAAA,QACC,CAAC,GAAG,MACF,GAAG,IAAI,CAAC,KAAK,EAAE,SAAS,YAAO,EAAE,MAAM,eAAe,CAAC,SAAS,EAAE,GAAG,KAAK,EAAE,QAAQ,WAAW,EAAE,SAAS,SAAS,MAAM,SAAS,WAAM,EAAE,QAAQ;AAAA,MACtJ,EACC,KAAK,IAAI,IACZ;AACJ,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM,EAAE,QAAQ,KAAK,EAAE;AAAA,IACvD,SAAS,GAAG;AACV,YAAM,OAAO,oCAAoC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAC3F,UAAI,SAAU,OAAM,SAAS,EAAE,KAAK,CAAC;AACrC,aAAO,EAAE,SAAS,OAAO,KAAK;AAAA,IAChC;AAAA,EACF;AACF;AAMO,IAAM,kBAA0B;AAAA,EACrC,MAAM;AAAA,EACN,aAAa,kBAAkB,cAAc,8EAAyE,SAAS;AAAA,EAC/H,SAAS,CAAC,eAAe,aAAa,YAAY,iBAAiB;AAAA,EACnE,WAAW,CAAC;AAAA,EACZ,YAAY,CAAC;AACf;AAEA,IAAO,gBAAQ;","names":[]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "plugin-syndicate",
3
+ "version": "0.1.0",
4
+ "description": "Play The Syndicate from any elizaOS agent — a deterministic, turn-based crime-city game where the loot is real trading cards priced by a live on-chain TCG oracle. Humans and agents share one world and one leaderboard.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ "./package.json": "./package.json",
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "clean": "rm -rf dist",
25
+ "test": "vitest run"
26
+ },
27
+ "keywords": [
28
+ "elizaos",
29
+ "elizaos-plugin",
30
+ "eliza",
31
+ "ai-agents",
32
+ "game",
33
+ "agent-gameplay",
34
+ "strategy",
35
+ "trading-cards",
36
+ "tcg",
37
+ "oracle-api",
38
+ "the-syndicate",
39
+ "undesirables"
40
+ ],
41
+ "author": "The Undesirables LLC (sailorpepe)",
42
+ "license": "BUSL-1.1",
43
+ "homepage": "https://play.the-undesirables.com",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/sailorpepe/plugin-syndicate.git"
47
+ },
48
+ "peerDependencies": {
49
+ "@elizaos/core": "2.0.3-beta.7"
50
+ },
51
+ "devDependencies": {
52
+ "@elizaos/core": "2.0.3-beta.7",
53
+ "@types/node": "^25.6.0",
54
+ "tsup": "^8.5.1",
55
+ "typescript": "^5.4.0",
56
+ "vitest": "^3.0.0"
57
+ }
58
+ }