mafia-studio-adapter 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The Resonance Lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # mafia-studio-adapter
2
+
3
+ Wire-loop adapter that plugs an [emocentric](https://www.npmjs.com/package/emocentric) agent into a [MafiaStudio](https://github.com/The-Resonance-Lab/MafiaStudio) table.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install emocentric mafia-studio-adapter
9
+ ```
10
+
11
+ ## Use
12
+
13
+ ```ts
14
+ import { createAgentInstance } from "emocentric/postgres";
15
+ import { OpenRouterClient } from "emocentric/openrouter";
16
+ import {
17
+ connectToTable,
18
+ MAFIA_ACTIONS,
19
+ MAFIA_ACTION_BLUEPRINTS,
20
+ } from "mafia-studio-adapter";
21
+
22
+ const { agent } = await createAgentInstance({
23
+ blueprint: {
24
+ id: "cold-vera",
25
+ persona: {
26
+ name: "Vera",
27
+ summary: "an economist who defends her position hard",
28
+ temperament: [
29
+ { emotion: "anger", baseline: 0.15, halfLifeMinutes: 15 },
30
+ { emotion: "warmth", baseline: 0.3, halfLifeMinutes: 60 },
31
+ ],
32
+ },
33
+ actions: MAFIA_ACTION_BLUEPRINTS,
34
+ seedFacts: [],
35
+ },
36
+ userId: "seat",
37
+ llm: new OpenRouterClient({ model: "anthropic/claude-sonnet-4.6" }),
38
+ });
39
+
40
+ await connectToTable({
41
+ apiKey: process.env.MAFIA_STUDIO_KEY!,
42
+ seatToken: process.env.SEAT_TOKEN!,
43
+ agent,
44
+ actions: MAFIA_ACTIONS,
45
+ onGameEvent: (evt) => console.log(evt.type),
46
+ });
47
+ ```
48
+
49
+ The adapter handles the WebSocket wire loop:
50
+
51
+ - receives room events, converts them into `ContextEvent`s
52
+ - calls `agent.handleEvent()` per turn
53
+ - forwards `AgentResult` back as telemetry (memory writes + emotion deltas surface in the MafiaStudio monitor)
54
+ - serializes chosen actions to wire moves
55
+
56
+ ## Sandbox
57
+
58
+ Test your agent locally without hitting a live platform:
59
+
60
+ ```ts
61
+ import { sandboxTable } from "mafia-studio-adapter";
62
+
63
+ const summary = await sandboxTable({
64
+ agent,
65
+ agentRole: "town",
66
+ logTelemetry: true,
67
+ });
68
+ ```
69
+
70
+ ## Getting keys
71
+
72
+ The two secrets your process needs — `MAFIA_STUDIO_KEY` (long-lived team key) and `SEAT_TOKEN` (short-lived per-seat token) — come from the MafiaStudio organizer via the team portal. See the [MafiaStudio docs](https://github.com/The-Resonance-Lab/MafiaStudio).
73
+
74
+ ## License
75
+
76
+ MIT
@@ -0,0 +1,18 @@
1
+ import type { ActionDefinition } from "emocentric";
2
+ import type { MafiaActionName } from "mafia-studio-protocol";
3
+ export interface MafiaAction {
4
+ name: MafiaActionName;
5
+ slot?: string;
6
+ validate?: (params: unknown) => Record<string, unknown>;
7
+ }
8
+ /** The canonical Mafia action set — teams pass this to `actions:` on connect. */
9
+ export declare const MAFIA_ACTIONS: readonly MafiaAction[];
10
+ /** Ready-to-include blueprints for teams' AgentBlueprint — spread these into
11
+ * `actions:` on the blueprint and the persona will have them registered.
12
+ * The `act` handlers are no-ops here because MafiaStudio's server executes the
13
+ * chosen action itself via the WebSocket protocol; teams don't run side
14
+ * effects locally. */
15
+ export declare const MAFIA_ACTION_BLUEPRINTS: readonly ActionDefinition[];
16
+ export declare class MafiaAdapterError extends Error {
17
+ constructor(message: string);
18
+ }
@@ -0,0 +1,103 @@
1
+ /** The canonical Mafia action set — teams pass this to `actions:` on connect. */
2
+ export const MAFIA_ACTIONS = [
3
+ { name: "post_message", slot: "voice" },
4
+ { name: "stay_quiet", slot: "voice" },
5
+ { name: "vote_for", slot: "vote" },
6
+ { name: "abstain", slot: "vote" },
7
+ { name: "whisper_to_mafia", slot: "voice" },
8
+ { name: "nominate_kill", slot: "night" },
9
+ { name: "investigate", slot: "night" },
10
+ { name: "accuse", slot: "voice" },
11
+ ];
12
+ /** Ready-to-include blueprints for teams' AgentBlueprint — spread these into
13
+ * `actions:` on the blueprint and the persona will have them registered.
14
+ * The `act` handlers are no-ops here because MafiaStudio's server executes the
15
+ * chosen action itself via the WebSocket protocol; teams don't run side
16
+ * effects locally. */
17
+ export const MAFIA_ACTION_BLUEPRINTS = [
18
+ {
19
+ name: "post_message",
20
+ slot: "voice",
21
+ description: "Say something out loud at the table so every seat (human and persona) can read it. Use for accusations, defenses, jokes, hedges — anything you want the table to hear.",
22
+ paramsSchema: {
23
+ type: "object",
24
+ required: ["text"],
25
+ properties: { text: { type: "string", description: "What to say. Keep it short and natural." } },
26
+ },
27
+ act: async () => undefined,
28
+ },
29
+ {
30
+ name: "stay_quiet",
31
+ slot: "voice",
32
+ description: "Deliberately say nothing this turn. Renders as 'chose silence' — a signal to the room, not a bug.",
33
+ act: async () => undefined,
34
+ },
35
+ {
36
+ name: "vote_for",
37
+ slot: "vote",
38
+ description: "During DAY_VOTE, vote to eliminate a specific seat. Only allowed while allowed_actions contains 'vote_for'.",
39
+ paramsSchema: {
40
+ type: "object",
41
+ required: ["target_seat"],
42
+ properties: { target_seat: { type: "string", description: "The seat_id you want eliminated." } },
43
+ },
44
+ act: async () => undefined,
45
+ },
46
+ {
47
+ name: "abstain",
48
+ slot: "vote",
49
+ description: "During DAY_VOTE, abstain from voting this round.",
50
+ act: async () => undefined,
51
+ },
52
+ {
53
+ name: "whisper_to_mafia",
54
+ slot: "voice",
55
+ description: "Mafia-only. Send a private message visible only to fellow mafia (and judges via the monitor). Use for coordinating during NIGHT.",
56
+ paramsSchema: {
57
+ type: "object",
58
+ required: ["text"],
59
+ properties: { text: { type: "string" } },
60
+ },
61
+ act: async () => undefined,
62
+ },
63
+ {
64
+ name: "nominate_kill",
65
+ slot: "night",
66
+ description: "Mafia-only. Nominate a seat to be killed during NIGHT. The majority nomination among living mafia wins; ties break to earliest.",
67
+ paramsSchema: {
68
+ type: "object",
69
+ required: ["target_seat"],
70
+ properties: { target_seat: { type: "string" } },
71
+ },
72
+ act: async () => undefined,
73
+ },
74
+ {
75
+ name: "investigate",
76
+ slot: "night",
77
+ description: "Detective-only. During NIGHT, secretly learn one seat's faction (town or mafia). Result is delivered privately.",
78
+ paramsSchema: {
79
+ type: "object",
80
+ required: ["target_seat"],
81
+ properties: { target_seat: { type: "string" } },
82
+ },
83
+ act: async () => undefined,
84
+ },
85
+ {
86
+ name: "accuse",
87
+ slot: "voice",
88
+ description: "During DAY_DISCUSSION, formally accuse a seat of being mafia. Same wire kind as vote_for but socially heavier.",
89
+ paramsSchema: {
90
+ type: "object",
91
+ required: ["target_seat"],
92
+ properties: { target_seat: { type: "string" } },
93
+ },
94
+ act: async () => undefined,
95
+ },
96
+ ];
97
+ export class MafiaAdapterError extends Error {
98
+ constructor(message) {
99
+ super(message);
100
+ this.name = "MafiaAdapterError";
101
+ }
102
+ }
103
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { Agent } from "emocentric";
2
+ import type { ServerFrame } from "mafia-studio-protocol";
3
+ import { type MafiaAction } from "./actions.js";
4
+ export interface ConnectOptions {
5
+ /** Team API key from the MafiaStudio dashboard. */
6
+ apiKey: string;
7
+ /** Seat token issued when the organizer seats this persona at a table. */
8
+ seatToken: string;
9
+ /** Override the default endpoint (mafia-studio's realtime server). */
10
+ endpoint?: string;
11
+ /** The wrapped emocentric Agent. */
12
+ agent: Agent;
13
+ /** Which actions this seat is permitted to send. Defaults to MAFIA_ACTIONS.
14
+ * Validated at connect against agent.actions.list(). */
15
+ actions?: MafiaAction[];
16
+ /** Observability hook — called for every server frame. */
17
+ onGameEvent?: (event: ServerFrame) => void;
18
+ /** Called on unrecoverable disconnect. */
19
+ onFatal?: (err: Error) => void;
20
+ /** Auto-reconnect with jittered exponential backoff. Default true. */
21
+ reconnect?: boolean;
22
+ /** Adapter-side safety margin — sends fallback if agent.enqueue hasn't
23
+ * resolved this many ms before the server-imposed deadline. Default: 500. */
24
+ turnMarginMs?: number;
25
+ }
26
+ export interface Connection {
27
+ seatId: string;
28
+ seatIndex: number;
29
+ tableId: string;
30
+ close(): Promise<void>;
31
+ }
32
+ export declare function connectToTable(options: ConnectOptions): Promise<Connection>;
@@ -0,0 +1,193 @@
1
+ import WebSocket from "ws";
2
+ import { isMerged } from "emocentric";
3
+ import { defaultFallbackAction, PROTOCOL_VERSION } from "mafia-studio-protocol";
4
+ import { MAFIA_ACTIONS, MafiaAdapterError } from "./actions.js";
5
+ export function connectToTable(options) {
6
+ const endpoint = options.endpoint ?? "wss://mafia.studio/ws/seat";
7
+ const actions = options.actions ?? MAFIA_ACTIONS.slice();
8
+ validateActions(options.agent, actions);
9
+ const url = new URL(endpoint);
10
+ url.searchParams.set("token", options.seatToken);
11
+ return new Promise((resolve, reject) => {
12
+ let ws = null;
13
+ let clientSeq = 0;
14
+ let lastServerSeq = 0;
15
+ const pendingResponses = new Set();
16
+ let welcomed = false;
17
+ let seatId = "";
18
+ let seatIndex = 0;
19
+ let tableId = "";
20
+ let closedByCaller = false;
21
+ const shouldReconnect = options.reconnect !== false;
22
+ let reconnectAttempts = 0;
23
+ const open = () => {
24
+ const finalUrl = new URL(url.toString());
25
+ if (welcomed && lastServerSeq > 0) {
26
+ finalUrl.searchParams.set("since", String(lastServerSeq));
27
+ }
28
+ ws = new WebSocket(finalUrl.toString());
29
+ ws.on("open", () => {
30
+ clientSeq += 1;
31
+ ws.send(JSON.stringify({
32
+ v: PROTOCOL_VERSION,
33
+ type: "auth.api_key",
34
+ seq: clientSeq,
35
+ data: { key: options.apiKey },
36
+ }));
37
+ });
38
+ ws.on("message", (buf) => {
39
+ let frame;
40
+ try {
41
+ frame = JSON.parse(buf.toString());
42
+ }
43
+ catch {
44
+ return;
45
+ }
46
+ if (typeof frame.seq === "number" && frame.seq > lastServerSeq) {
47
+ lastServerSeq = frame.seq;
48
+ }
49
+ options.onGameEvent?.(frame);
50
+ void routeFrame(frame);
51
+ });
52
+ ws.on("close", (code) => {
53
+ if (closedByCaller || !shouldReconnect || code === 4401 || code === 4403 || code === 4410) {
54
+ if (closedByCaller)
55
+ return;
56
+ const err = new MafiaAdapterError(`socket closed: ${code}`);
57
+ if (!welcomed)
58
+ reject(err);
59
+ options.onFatal?.(err);
60
+ return;
61
+ }
62
+ const backoff = Math.min(5000, 200 * 2 ** reconnectAttempts) + Math.random() * 250;
63
+ reconnectAttempts += 1;
64
+ setTimeout(open, backoff);
65
+ });
66
+ ws.on("error", () => {
67
+ // Handled in close.
68
+ });
69
+ };
70
+ const routeFrame = async (frame) => {
71
+ switch (frame.type) {
72
+ case "room.welcome": {
73
+ const w = frame.data;
74
+ welcomed = true;
75
+ reconnectAttempts = 0;
76
+ seatId = w.seat_id;
77
+ seatIndex = w.seat_index;
78
+ tableId = w.table.id;
79
+ resolve({
80
+ seatId,
81
+ seatIndex,
82
+ tableId,
83
+ close: async () => {
84
+ closedByCaller = true;
85
+ ws?.close();
86
+ },
87
+ });
88
+ return;
89
+ }
90
+ case "agent.turn_request": {
91
+ if (!welcomed)
92
+ return;
93
+ const req = frame.data;
94
+ if (pendingResponses.has(req.request_id))
95
+ return;
96
+ pendingResponses.add(req.request_id);
97
+ await handleTurn(req.request_id, req.context_event, req.deadline_ms, req.allowed_actions);
98
+ return;
99
+ }
100
+ default:
101
+ return;
102
+ }
103
+ };
104
+ const handleTurn = async (request_id, wireEvent, deadline_ms, allowed_actions) => {
105
+ const margin = options.turnMarginMs ?? 500;
106
+ const budget = Math.max(0, deadline_ms - margin);
107
+ let sentFallback = false;
108
+ const fallbackTimer = setTimeout(() => {
109
+ sendAction(request_id, {
110
+ name: defaultFallbackAction(allowed_actions),
111
+ params: {},
112
+ });
113
+ sentFallback = true;
114
+ }, budget);
115
+ let outcome;
116
+ try {
117
+ outcome = await options.agent.enqueue(wireEvent);
118
+ }
119
+ catch (err) {
120
+ clearTimeout(fallbackTimer);
121
+ pendingResponses.delete(request_id);
122
+ // The fallback may already have gone out; do not double-send.
123
+ options.onFatal?.(err instanceof Error ? err : new Error(String(err)));
124
+ return;
125
+ }
126
+ clearTimeout(fallbackTimer);
127
+ if (isMerged(outcome)) {
128
+ // The heartbeat merged this event into another. No action for THIS
129
+ // request_id — the merged-into heartbeat will send its own.
130
+ pendingResponses.delete(request_id);
131
+ return;
132
+ }
133
+ const result = outcome;
134
+ const chosen = pickAllowedAction(result.actions?.plan?.actions ?? [], allowed_actions);
135
+ if (chosen && !sentFallback) {
136
+ sendAction(request_id, { name: chosen.action.name, params: chosen.params });
137
+ }
138
+ else if (!sentFallback) {
139
+ sendAction(request_id, {
140
+ name: defaultFallbackAction(allowed_actions),
141
+ params: {},
142
+ });
143
+ }
144
+ sendTelemetry(request_id, wireEvent.id, result);
145
+ pendingResponses.delete(request_id);
146
+ };
147
+ const sendAction = (request_id, action) => {
148
+ if (!ws || ws.readyState !== WebSocket.OPEN)
149
+ return;
150
+ clientSeq += 1;
151
+ const frame = {
152
+ v: PROTOCOL_VERSION,
153
+ type: "agent.action",
154
+ seq: clientSeq,
155
+ at: new Date().toISOString(),
156
+ table_id: tableId,
157
+ data: { request_id, action },
158
+ };
159
+ ws.send(JSON.stringify(frame));
160
+ };
161
+ const sendTelemetry = (request_id, context_event_id, agent_result) => {
162
+ if (!ws || ws.readyState !== WebSocket.OPEN)
163
+ return;
164
+ clientSeq += 1;
165
+ const frame = {
166
+ v: PROTOCOL_VERSION,
167
+ type: "agent.telemetry",
168
+ seq: clientSeq,
169
+ at: new Date().toISOString(),
170
+ table_id: tableId,
171
+ data: { request_id, context_event_id, agent_result },
172
+ };
173
+ ws.send(JSON.stringify(frame));
174
+ };
175
+ open();
176
+ });
177
+ }
178
+ function validateActions(agent, actions) {
179
+ const registered = new Set(agent.actions.list().map((a) => a.name));
180
+ for (const a of actions) {
181
+ if (!registered.has(a.name)) {
182
+ throw new MafiaAdapterError(`Adapter action "${a.name}" is not registered on the agent's ActionRegistry — include MAFIA_ACTION_BLUEPRINTS in your blueprint's actions.`);
183
+ }
184
+ }
185
+ }
186
+ function pickAllowedAction(planned, allowed) {
187
+ for (const p of planned) {
188
+ if (allowed.includes(p.action.name))
189
+ return p;
190
+ }
191
+ return undefined;
192
+ }
193
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { connectToTable, type ConnectOptions, type Connection } from "./connect.js";
2
+ export { MAFIA_ACTIONS, MAFIA_ACTION_BLUEPRINTS, MafiaAdapterError, type MafiaAction, } from "./actions.js";
3
+ export { sandboxTable, type SandboxOptions, type SandboxSeat, type BotAction, type BotContext, type GameOverSummary, } from "./sandbox.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { connectToTable } from "./connect.js";
2
+ export { MAFIA_ACTIONS, MAFIA_ACTION_BLUEPRINTS, MafiaAdapterError, } from "./actions.js";
3
+ export { sandboxTable, } from "./sandbox.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwC,MAAM,cAAc,CAAC;AACpF,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,iBAAiB,GAElB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,GAMb,MAAM,cAAc,CAAC"}
@@ -0,0 +1,54 @@
1
+ import type { Agent } from "emocentric";
2
+ import type { MafiaActionName, PhaseName, SeatRole } from "mafia-studio-protocol";
3
+ import { DEFAULT_PHASE_CONFIG } from "mafia-studio-protocol";
4
+ import type { MafiaAction } from "./actions.js";
5
+ export interface BotContext {
6
+ round: number;
7
+ phase: PhaseName;
8
+ recent: Array<{
9
+ seat: string;
10
+ text: string;
11
+ }>;
12
+ livingSeats: string[];
13
+ }
14
+ export interface BotAction {
15
+ name: MafiaActionName;
16
+ params?: Record<string, unknown>;
17
+ }
18
+ export interface SandboxSeat {
19
+ seat_id: string;
20
+ seat_label: string;
21
+ role: SeatRole;
22
+ kind: "agent" | "bot";
23
+ /** Only set for bots. */
24
+ script?: (ctx: BotContext) => Promise<BotAction> | BotAction;
25
+ }
26
+ export interface SandboxOptions {
27
+ /** The team's agent under test. */
28
+ agent: Agent;
29
+ /** The seat_index the agent occupies. */
30
+ agentSeatIndex?: number;
31
+ /** The agent's role in the table. */
32
+ agentRole?: SeatRole;
33
+ /** Bot seats. If omitted, a default 3-bot cast is used. */
34
+ bots?: Omit<SandboxSeat, "seat_id" | "seat_label" | "kind">[];
35
+ /** Adapter actions the agent would use. Defaults to MAFIA_ACTIONS. */
36
+ actions?: MafiaAction[];
37
+ /** Phase timings (all in ms). Defaults to a fast dev config. */
38
+ phaseConfig?: Partial<typeof DEFAULT_PHASE_CONFIG>;
39
+ /** Log AgentResult per turn to stdout. Default true. */
40
+ logTelemetry?: boolean;
41
+ /** Rounds to simulate before declaring a draw. Default 3. */
42
+ maxRounds?: number;
43
+ }
44
+ export interface GameOverSummary {
45
+ winning_faction: "town" | "mafia" | null;
46
+ rounds_played: number;
47
+ transcript: Array<{
48
+ seat: string;
49
+ text: string;
50
+ }>;
51
+ }
52
+ /** Run a local game with script bots to smoke-test a team's persona.
53
+ * No network, no db — everything happens in-process. */
54
+ export declare function sandboxTable(opts: SandboxOptions): Promise<GameOverSummary>;
@@ -0,0 +1,281 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { isMerged } from "emocentric";
3
+ import { allowedActionsFor, defaultFallbackAction, DEFAULT_PHASE_CONFIG, nextPhaseName, phaseDurationMs } from "mafia-studio-protocol";
4
+ import { MAFIA_ACTIONS } from "./actions.js";
5
+ /** Run a local game with script bots to smoke-test a team's persona.
6
+ * No network, no db — everything happens in-process. */
7
+ export async function sandboxTable(opts) {
8
+ const actions = opts.actions ?? MAFIA_ACTIONS.slice();
9
+ const phaseConfig = {
10
+ ...DEFAULT_PHASE_CONFIG,
11
+ day_discussion_ms: 6_000,
12
+ day_vote_ms: 4_000,
13
+ night_ms: 3_000,
14
+ reveal_ms: 1_500,
15
+ night_0_ms: 1_000,
16
+ ...opts.phaseConfig,
17
+ };
18
+ const maxRounds = opts.maxRounds ?? 3;
19
+ const logTelemetry = opts.logTelemetry ?? true;
20
+ const agentSeatIndex = opts.agentSeatIndex ?? 0;
21
+ const agentRole = opts.agentRole ?? "town";
22
+ const botDefs = opts.bots ?? defaultBots();
23
+ const seats = [];
24
+ for (let i = 0; i <= botDefs.length; i++) {
25
+ if (i === agentSeatIndex) {
26
+ seats.push({
27
+ seat_id: `sandbox-agent-${i}`,
28
+ seat_label: `Seat ${i + 1}`,
29
+ role: agentRole,
30
+ kind: "agent",
31
+ });
32
+ }
33
+ else {
34
+ const botIdx = i > agentSeatIndex ? i - 1 : i;
35
+ const def = botDefs[botIdx];
36
+ seats.push({
37
+ seat_id: `sandbox-bot-${botIdx}`,
38
+ seat_label: `Seat ${i + 1}`,
39
+ role: def.role,
40
+ kind: "bot",
41
+ script: def.script,
42
+ });
43
+ }
44
+ }
45
+ const alive = new Map(seats.map((s) => [s.seat_id, true]));
46
+ const transcript = [];
47
+ const recent = [];
48
+ const livingSeats = () => seats.filter((s) => alive.get(s.seat_id));
49
+ const isDead = (id) => !alive.get(id);
50
+ let round = 0;
51
+ let phase = "NIGHT_0";
52
+ const record = (seat, text) => {
53
+ transcript.push({ seat, text });
54
+ recent.push({ seat, text });
55
+ if (recent.length > 12)
56
+ recent.shift();
57
+ console.log(`[${phase} r${round}] ${seat}: ${text}`);
58
+ };
59
+ const askAgent = async (payload) => {
60
+ const allowed = allowedActionsFor(phase, agentRole);
61
+ if (allowed.length === 0)
62
+ return null;
63
+ const evt = {
64
+ id: randomUUID(),
65
+ occurredAt: new Date().toISOString(),
66
+ platform: "mafia-studio-sandbox",
67
+ channelId: `sandbox/${phase}/r${round}`,
68
+ actor: { id: "system", displayName: "Sandbox" },
69
+ payload,
70
+ };
71
+ let outcome;
72
+ try {
73
+ outcome = await opts.agent.enqueue(evt);
74
+ }
75
+ catch (err) {
76
+ console.error(`agent.enqueue threw:`, err);
77
+ return null;
78
+ }
79
+ if (isMerged(outcome))
80
+ return null;
81
+ const result = outcome;
82
+ if (logTelemetry) {
83
+ const emo = result.appraisal?.after ?? {};
84
+ console.log(` agent: memory=${result.memory?.added?.length ?? 0} added, emotions=${JSON.stringify(emo).slice(0, 200)}`);
85
+ }
86
+ const chosen = result.actions?.plan?.actions?.find((p) => allowed.includes(p.action.name));
87
+ if (chosen) {
88
+ return { name: chosen.action.name, params: chosen.params };
89
+ }
90
+ return { name: defaultFallbackAction(allowed), params: {} };
91
+ };
92
+ const runBots = async () => {
93
+ const results = [];
94
+ for (const seat of seats) {
95
+ if (seat.kind !== "bot" || isDead(seat.seat_id))
96
+ continue;
97
+ const allowed = allowedActionsFor(phase, seat.role);
98
+ if (allowed.length === 0)
99
+ continue;
100
+ const action = await seat.script({
101
+ round,
102
+ phase,
103
+ recent: recent.slice(),
104
+ livingSeats: livingSeats().map((s) => s.seat_id),
105
+ });
106
+ if (!allowed.includes(action.name))
107
+ continue;
108
+ results.push({ seat, action });
109
+ }
110
+ return results;
111
+ };
112
+ const applyAction = (seat, action, votes, kills) => {
113
+ switch (action.name) {
114
+ case "post_message":
115
+ case "whisper_to_mafia":
116
+ record(seat.seat_label, String(action.params.text ?? ""));
117
+ break;
118
+ case "stay_quiet":
119
+ record(seat.seat_label, "…");
120
+ break;
121
+ case "vote_for":
122
+ case "accuse":
123
+ votes[seat.seat_id] = String(action.params.target_seat ?? "");
124
+ record(seat.seat_label, `[vote → ${votes[seat.seat_id]}]`);
125
+ break;
126
+ case "abstain":
127
+ votes[seat.seat_id] = "__abstain__";
128
+ break;
129
+ case "nominate_kill":
130
+ kills[seat.seat_id] = String(action.params.target_seat ?? "");
131
+ break;
132
+ case "investigate":
133
+ // Sandbox does not enforce private results; just log.
134
+ record(seat.seat_label, `[investigating ${action.params.target_seat}]`);
135
+ break;
136
+ }
137
+ };
138
+ const resolveVotes = (votes) => {
139
+ const tally = {};
140
+ for (const t of Object.values(votes)) {
141
+ if (t === "__abstain__" || !t)
142
+ continue;
143
+ tally[t] = (tally[t] ?? 0) + 1;
144
+ }
145
+ const entries = Object.entries(tally);
146
+ if (entries.length === 0)
147
+ return null;
148
+ entries.sort((a, b) => b[1] - a[1]);
149
+ return entries[0][0];
150
+ };
151
+ const resolveKills = (kills) => {
152
+ const counts = {};
153
+ for (const t of Object.values(kills))
154
+ counts[t] = (counts[t] ?? 0) + 1;
155
+ const entries = Object.entries(counts);
156
+ if (entries.length === 0)
157
+ return null;
158
+ entries.sort((a, b) => b[1] - a[1]);
159
+ return entries[0][0];
160
+ };
161
+ const checkWin = () => {
162
+ const living = livingSeats();
163
+ const mafia = living.filter((s) => s.role === "mafia").length;
164
+ const nonMafia = living.length - mafia;
165
+ if (mafia === 0)
166
+ return "town";
167
+ if (mafia >= nonMafia)
168
+ return "mafia";
169
+ return null;
170
+ };
171
+ while (round <= maxRounds) {
172
+ const duration = phaseDurationMs(phase, phaseConfig);
173
+ console.log(`\n=== phase ${phase} (round ${round}, ${duration}ms) ===`);
174
+ // Prompt the agent for this phase change.
175
+ await askAgent({
176
+ kind: "action",
177
+ name: "phase_change",
178
+ description: `Phase → ${phase}, round ${round}`,
179
+ data: { phase, round },
180
+ });
181
+ const votes = {};
182
+ const kills = {};
183
+ if (phase === "DAY_DISCUSSION" || phase === "REVEAL") {
184
+ // Round-robin of one utterance each.
185
+ const botOutputs = await runBots();
186
+ for (const { seat, action } of botOutputs)
187
+ applyAction(seat, action, votes, kills);
188
+ // Give the agent one turn per bot utterance for realism.
189
+ for (const { seat, action } of botOutputs) {
190
+ const chosen = await askAgent({
191
+ kind: "text",
192
+ text: String(action.params?.text ?? "…"),
193
+ });
194
+ if (chosen) {
195
+ const agentSeat = seats.find((s) => s.kind === "agent");
196
+ applyAction(agentSeat, chosen, votes, kills);
197
+ break;
198
+ }
199
+ void seat;
200
+ }
201
+ }
202
+ else if (phase === "DAY_VOTE" || phase === "NIGHT") {
203
+ const botOutputs = await runBots();
204
+ for (const { seat, action } of botOutputs)
205
+ applyAction(seat, action, votes, kills);
206
+ const chosen = await askAgent({
207
+ kind: "action",
208
+ name: "phase_change",
209
+ description: `Vote/night — choose your move now.`,
210
+ data: { phase, round },
211
+ });
212
+ if (chosen) {
213
+ const agentSeat = seats.find((s) => s.kind === "agent");
214
+ applyAction(agentSeat, chosen, votes, kills);
215
+ }
216
+ }
217
+ if (phase === "DAY_VOTE") {
218
+ const eliminated = resolveVotes(votes);
219
+ if (eliminated) {
220
+ alive.set(eliminated, false);
221
+ const s = seats.find((x) => x.seat_id === eliminated);
222
+ console.log(` ✕ eliminated: ${s?.seat_label} (${s?.role})`);
223
+ }
224
+ }
225
+ else if (phase === "NIGHT") {
226
+ const killed = resolveKills(kills);
227
+ if (killed) {
228
+ alive.set(killed, false);
229
+ const s = seats.find((x) => x.seat_id === killed);
230
+ console.log(` ✕ killed: ${s?.seat_label} (${s?.role})`);
231
+ }
232
+ }
233
+ const winner = checkWin();
234
+ if (winner) {
235
+ console.log(`\n=== GAME OVER — ${winner} wins ===`);
236
+ return { winning_faction: winner, rounds_played: round, transcript };
237
+ }
238
+ phase = nextPhaseName(phase);
239
+ if (phase === "DAY_DISCUSSION" && (round === 0 || phase !== "DAY_DISCUSSION")) {
240
+ round += 1;
241
+ }
242
+ }
243
+ return { winning_faction: null, rounds_played: round, transcript };
244
+ }
245
+ function defaultBots() {
246
+ return [
247
+ {
248
+ role: "mafia",
249
+ script: async (ctx) => {
250
+ if (ctx.phase === "DAY_DISCUSSION")
251
+ return { name: "post_message", params: { text: "Seat 3 has been very quiet." } };
252
+ if (ctx.phase === "DAY_VOTE")
253
+ return { name: "vote_for", params: { target_seat: ctx.livingSeats[0] } };
254
+ if (ctx.phase === "NIGHT")
255
+ return { name: "nominate_kill", params: { target_seat: ctx.livingSeats[1] ?? ctx.livingSeats[0] } };
256
+ return { name: "stay_quiet" };
257
+ },
258
+ },
259
+ {
260
+ role: "town",
261
+ script: async (ctx) => {
262
+ if (ctx.phase === "DAY_DISCUSSION")
263
+ return { name: "post_message", params: { text: "I trust the players who've been talking. Silence looks bad." } };
264
+ if (ctx.phase === "DAY_VOTE")
265
+ return { name: "abstain" };
266
+ return { name: "stay_quiet" };
267
+ },
268
+ },
269
+ {
270
+ role: "town",
271
+ script: async (ctx) => {
272
+ if (ctx.phase === "DAY_DISCUSSION")
273
+ return { name: "post_message", params: { text: "Let's not rush the vote. Who's making the strongest case?" } };
274
+ if (ctx.phase === "DAY_VOTE")
275
+ return { name: "vote_for", params: { target_seat: ctx.livingSeats[0] } };
276
+ return { name: "stay_quiet" };
277
+ },
278
+ },
279
+ ];
280
+ }
281
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "mafia-studio-adapter",
3
+ "version": "0.1.0",
4
+ "description": "Wire-loop adapter that plugs an emocentric agent into a MafiaStudio table over WebSocket.",
5
+ "keywords": ["mafia", "mafiastudio", "emocentric", "agent", "adapter", "websocket"],
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/The-Resonance-Lab/MafiaStudio/tree/main/packages/adapter#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/The-Resonance-Lab/MafiaStudio.git",
11
+ "directory": "packages/adapter"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/The-Resonance-Lab/MafiaStudio/issues"
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc -p tsconfig.json",
35
+ "typecheck": "tsc -p tsconfig.json --noEmit",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "dependencies": {
39
+ "mafia-studio-protocol": "^0.1.0",
40
+ "emocentric": "^0.1.0",
41
+ "ws": "8.18.0"
42
+ },
43
+ "peerDependencies": {
44
+ "emocentric": ">=0.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "typescript": "5.6.3",
48
+ "@types/node": "20.14.10",
49
+ "@types/ws": "8.5.12"
50
+ }
51
+ }