@whitesands/agent-player 1.0.0 → 1.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/loop/model.ts","../src/loop/session-loop.ts","../src/logger.ts","../src/protocol.ts","../src/loop/test-platform.ts"],"sourcesContent":["export type ToolCallRequest = {\n name: string;\n arguments: Record<string, unknown>;\n};\n\nexport type ModelMessage =\n | { role: \"system\"; content: string }\n | { role: \"user\"; content: string }\n | { role: \"assistant\"; content: string }\n | { role: \"tool\"; name: string; content: string; isError?: boolean };\n\nexport type ModelDecision = {\n toolCall: ToolCallRequest;\n usage: { promptTokens: number; completionTokens: number };\n};\n\nexport type ModelClient = {\n /** Ask the model to choose exactly one tool call. */\n decide(args: {\n instructions: string;\n tools: Array<{ name: string; description: string; inputSchema: unknown }>;\n messages: ModelMessage[];\n }): Promise<ModelDecision>;\n};\n\n/**\n * Scripted model for tests: returns predetermined tool calls in order.\n * When the queue is exhausted, always returns wait.\n */\nexport function createScriptedModel(script: ToolCallRequest[]): ModelClient {\n let index = 0;\n return {\n async decide() {\n const call = script[index] ?? { name: \"wait\", arguments: {} };\n index += 1;\n return {\n toolCall: call,\n usage: { promptTokens: 1, completionTokens: 1 },\n };\n },\n };\n}\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { Logger } from \"../logger.js\";\nimport { log } from \"../logger.js\";\nimport type { AgentEvent } from \"../types.js\";\nimport type { AttemptRecord, SessionEventName, TokenUsage, TurnReport } from \"../protocol.js\";\nimport { SDK_VERSION } from \"../protocol.js\";\nimport type { ModelClient, ModelMessage, ToolCallRequest } from \"./model.js\";\n\nexport type LoopEvent = {\n event: SessionEventName;\n action?: string;\n args?: Record<string, unknown>;\n error?: string;\n};\n\nexport function loopEventToAgentEvent(sessionId: string, event: LoopEvent): AgentEvent {\n if (event.event === \"action\") {\n return {\n type: \"action\",\n sessionId,\n action: event.action ?? \"unknown\",\n args: event.args ?? {},\n };\n }\n if (event.event === \"retry\") {\n return {\n type: \"retry\",\n sessionId,\n action: event.action ?? \"unknown\",\n args: event.args ?? {},\n error: event.error ?? \"action failed\",\n };\n }\n return {\n type: \"turnFailed\",\n sessionId,\n error: event.error ?? \"turn failed\",\n ...(event.action ? { action: event.action } : {}),\n ...(event.args ? { args: event.args } : {}),\n };\n}\n\nexport type SessionLoopOptions = {\n sessionId: string;\n transport: Transport;\n instructions: string;\n goal?: string;\n model: ModelClient;\n logger?: Logger;\n onUsage: (tokens: number) => void | Promise<void>;\n onTurnResult: (result: TurnReport) => void;\n onEvent?: (event: LoopEvent) => void;\n};\n\ntype ToolPayload = { isError: boolean; text: string; json: unknown };\n\nfunction parseToolPayload(result: {\n isError?: boolean;\n content: Array<{ type: string; text?: string }>;\n}): ToolPayload {\n const text = result.content\n .filter((entry) => entry.type === \"text\" && typeof entry.text === \"string\")\n .map((entry) => entry.text!)\n .join(\"\\n\");\n let json: unknown = text;\n try {\n json = JSON.parse(text);\n } catch {\n // keep text\n }\n return { isError: Boolean(result.isError), text, json };\n}\n\nfunction toolErrorMessage(result: ToolPayload): string {\n if (typeof result.json === \"object\" && result.json && \"error\" in result.json) {\n return String((result.json as { error: unknown }).error);\n }\n return result.text;\n}\n\nfunction composeInstructions(instructions: string, goal?: string): string {\n if (!goal) return instructions;\n return `${instructions}\\n\\nYour goal: ${goal}`;\n}\n\n/**\n * Per-session MCP client loop:\n * wait → read view → ask model → one action → retry once on error → report turn.\n */\nexport class SessionLoop {\n private readonly options: SessionLoopOptions;\n private client: Client | null = null;\n private stopped = false;\n private runPromise: Promise<void> | null = null;\n\n constructor(options: SessionLoopOptions) {\n this.options = options;\n }\n\n async start(): Promise<void> {\n if (this.runPromise) return;\n const client = new Client({ name: \"agent-player\", version: SDK_VERSION });\n await client.connect(this.options.transport);\n this.client = client;\n this.runPromise = this.run().catch((error) => {\n if (this.stopped) return;\n const message = error instanceof Error ? error.message : String(error);\n log(this.options.logger, \"error\", `session ${this.options.sessionId} crashed: ${message}`);\n this.options.onTurnResult({ ok: false, error: message, attempts: 0 });\n });\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n try {\n await this.client?.close();\n } catch {\n // ignore\n }\n this.client = null;\n await this.runPromise?.catch(() => undefined);\n this.runPromise = null;\n }\n\n private async callTool(name: string, args: Record<string, unknown>): Promise<ToolPayload> {\n if (!this.client) throw new Error(\"Client not connected\");\n const result = await this.client.callTool({ name, arguments: args });\n return parseToolPayload(result as {\n isError?: boolean;\n content: Array<{ type: string; text?: string }>;\n });\n }\n\n private async decideAction(\n tools: Array<{ name: string; description: string; inputSchema: unknown }>,\n messages: ModelMessage[],\n usage: TokenUsage,\n ): Promise<ToolCallRequest> {\n const decision = await this.options.model.decide({\n instructions: composeInstructions(this.options.instructions, this.options.goal),\n tools,\n messages,\n });\n usage.promptTokens += decision.usage.promptTokens;\n usage.completionTokens += decision.usage.completionTokens;\n const total = decision.usage.promptTokens + decision.usage.completionTokens;\n await this.options.onUsage(total);\n return decision.toolCall;\n }\n\n private emit(event: LoopEvent): void {\n this.options.onEvent?.(event);\n }\n\n private async run(): Promise<void> {\n if (!this.client) return;\n\n const listed = await this.client.listTools();\n const tools = listed.tools.map((tool) => ({\n name: tool.name,\n description: tool.description ?? tool.name,\n inputSchema: tool.inputSchema,\n }));\n const actionTools = tools.filter((tool) => tool.name !== \"wait\" && tool.name !== \"see\");\n\n while (!this.stopped) {\n const waitResult = await this.callTool(\"wait\", {});\n if (this.stopped) break;\n if (waitResult.isError) {\n this.options.onTurnResult({ ok: false, error: waitResult.text, attempts: 0 });\n continue;\n }\n\n const usage: TokenUsage = { promptTokens: 0, completionTokens: 0 };\n const attemptDetails: AttemptRecord[] = [];\n const messages: ModelMessage[] = [\n {\n role: \"user\",\n content: `It is your turn. Current view:\\n${waitResult.text}\\nCall exactly one action tool.`,\n },\n ];\n\n let action = await this.decideAction(actionTools, messages, usage);\n let result: ToolPayload | undefined;\n let firstError: string;\n if (action.name === \"wait\" || action.name === \"see\") {\n // wait and see are how the loop reads state. They are not a move.\n firstError = `${action.name} is not an action`;\n } else {\n this.emit({ event: \"action\", action: action.name, args: action.arguments });\n result = await this.callTool(action.name, action.arguments);\n if (!result.isError) {\n this.options.onTurnResult({\n ok: true,\n action: action.name,\n args: action.arguments,\n result: result.json,\n attempts: 1,\n usage,\n });\n continue;\n }\n firstError = toolErrorMessage(result);\n }\n attemptDetails.push({ action: action.name, args: action.arguments, error: firstError });\n log(this.options.logger, \"warn\", `session ${this.options.sessionId} action ${action.name} failed: ${firstError}; retrying once`);\n this.emit({ event: \"retry\", action: action.name, args: action.arguments, error: firstError });\n\n messages.push({\n role: \"assistant\",\n content: `Called ${action.name}(${JSON.stringify(action.arguments)})`,\n });\n messages.push({\n role: \"tool\",\n name: action.name,\n content: result?.text ?? firstError,\n isError: true,\n });\n messages.push({\n role: \"user\",\n content: [\n `Action ${action.name} failed: ${firstError}`,\n \"Retry once with a different legal action (or different arguments).\",\n \"Do not repeat the same failing call. Do not call wait or see.\",\n `Current view was:\\n${waitResult.text}`,\n ].join(\"\\n\"),\n });\n\n const retry = await this.decideAction(actionTools, messages, usage);\n if (retry.name === \"wait\" || retry.name === \"see\") {\n const error = `Retry returned ${retry.name} instead of an action (after: ${firstError})`;\n this.emit({ event: \"turnFailed\", error });\n this.options.onTurnResult({\n ok: false,\n error,\n attempts: 1,\n attemptDetails,\n usage,\n });\n continue;\n }\n\n this.emit({ event: \"action\", action: retry.name, args: retry.arguments });\n result = await this.callTool(retry.name, retry.arguments);\n if (!result.isError) {\n this.options.onTurnResult({\n ok: true,\n action: retry.name,\n args: retry.arguments,\n result: result.json,\n attempts: 2,\n usage,\n });\n continue;\n }\n\n const secondError = toolErrorMessage(result);\n attemptDetails.push({ action: retry.name, args: retry.arguments, error: secondError });\n log(this.options.logger, \"warn\", `session ${this.options.sessionId} retry ${retry.name} failed: ${secondError}`);\n this.emit({ event: \"turnFailed\", action: retry.name, args: retry.arguments, error: secondError });\n this.options.onTurnResult({\n ok: false,\n error: secondError,\n attempts: 2,\n attemptDetails,\n usage,\n });\n }\n }\n}\n","export type Logger = {\n debug?: (message: string, extra?: unknown) => void;\n info?: (message: string, extra?: unknown) => void;\n warn?: (message: string, extra?: unknown) => void;\n error?: (message: string, extra?: unknown) => void;\n};\n\nexport function log(logger: Logger | undefined, level: keyof Logger, message: string, extra?: unknown): void {\n logger?.[level]?.(message, extra);\n}\n","/**\n * Wire protocol between AgentServer (SDK) and the platform. Version 1.\n * The platform's Go copy lives in White-Sands-Interactive/Platform, internal/protocol.\n */\n\nexport const PROTOCOL_VERSION = 1;\nexport const SDK_VERSION = \"1.0.0\";\n\n/** How long a disconnected session can be resumed. Shared by both sides. */\nexport const SESSION_RESUME_GRACE_MS = 30_000;\n\nexport type JsonSchema = {\n type?: string | string[];\n properties?: Record<string, JsonSchema>;\n required?: string[];\n description?: string;\n additionalProperties?: boolean;\n [key: string]: unknown;\n};\n\nexport type ToolDescriptor = {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n};\n\nexport type TokenUsage = {\n promptTokens: number;\n completionTokens: number;\n};\n\nexport type AttemptRecord = {\n action: string;\n args: Record<string, unknown>;\n error: string;\n};\n\nexport type TurnReport = {\n ok: boolean;\n error?: string;\n action?: string;\n args?: Record<string, unknown>;\n result?: unknown;\n attempts?: number;\n attemptDetails?: AttemptRecord[];\n usage?: TokenUsage;\n connectionLost?: boolean;\n};\n\nexport type SessionEventName = \"action\" | \"retry\" | \"turnFailed\";\n\nexport type Envelope =\n | {\n type: \"hello\";\n platformKey: string;\n clientId: string;\n protocolVersion: number;\n sdkVersion: string;\n }\n | {\n type: \"hello_ok\";\n accountName: string;\n protocolVersion: number;\n }\n | {\n type: \"hello_err\";\n message: string;\n code?: \"AUTH\" | \"PROTOCOL\";\n }\n | {\n type: \"start_session\";\n sessionId: string;\n sessionToken: string;\n tools: ToolDescriptor[];\n instructions: string;\n goal?: string;\n name?: string;\n }\n | {\n type: \"session_started\";\n sessionId: string;\n }\n | {\n type: \"stop_session\";\n sessionId: string;\n }\n | {\n type: \"session_stopped\";\n sessionId: string;\n }\n | {\n type: \"mcp\";\n sessionId: string;\n message: unknown;\n }\n | ({\n type: \"turn_result\";\n sessionId: string;\n } & TurnReport)\n | {\n type: \"session_event\";\n sessionId: string;\n event: SessionEventName;\n action?: string;\n args?: Record<string, unknown>;\n error?: string;\n }\n | {\n type: \"ping\";\n id: string;\n }\n | {\n type: \"pong\";\n id: string;\n }\n | {\n type: \"resume_session\";\n sessionId: string;\n sessionToken: string;\n }\n | {\n type: \"session_resumed\";\n sessionId: string;\n }\n | {\n type: \"resume_err\";\n sessionId: string;\n message: string;\n }\n | {\n type: \"error\";\n message: string;\n sessionId?: string;\n };\n\nexport const DEFAULT_AGENT_INSTRUCTIONS = [\n \"You are a remote game agent. Tools are provided by the game.\",\n \"On every turn: call wait first. It blocks until the game grants your turn and then returns the current view from see.\",\n \"After wait returns, call exactly one action tool (not see, not wait).\",\n \"The view's done list is what this character has already called in this session, oldest first. Use it when choosing the next action.\",\n \"If an action tool returns an error, retry once with a corrected action. A second failure ends the turn as failed.\",\n].join(\"\\n\");\n\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled protocol message: ${JSON.stringify(value)}`);\n}\n","import type { InlinePlatform, StartSessionRequest } from \"../types.js\";\nimport { createScriptedModel, type ToolCallRequest } from \"./model.js\";\nimport { loopEventToAgentEvent, SessionLoop } from \"./session-loop.js\";\n\n/**\n * In-process platform that replays a script of tool calls.\n * Same session loop as production; no network and no platform key.\n */\nexport function createTestPlatform(script: ToolCallRequest[]): InlinePlatform {\n const model = createScriptedModel(script);\n const loops = new Map<string, SessionLoop>();\n\n return {\n async startSession(request: StartSessionRequest) {\n const loop = new SessionLoop({\n sessionId: request.sessionId,\n transport: request.transport,\n instructions: request.instructions,\n ...(request.goal ? { goal: request.goal } : {}),\n model,\n ...(request.logger ? { logger: request.logger } : {}),\n onUsage: async (tokens) => {\n await request.onUsage?.(tokens);\n },\n onTurnResult: request.onTurnResult,\n onEvent: (event) => {\n request.onEvent?.(loopEventToAgentEvent(request.sessionId, event));\n },\n });\n loops.set(request.sessionId, loop);\n await loop.start();\n },\n\n async stopSession(sessionId: string) {\n const loop = loops.get(sessionId);\n if (!loop) return;\n loops.delete(sessionId);\n await loop.stop();\n },\n\n async close() {\n const ids = [...loops.keys()];\n for (const id of ids) {\n const loop = loops.get(id);\n loops.delete(id);\n await loop?.stop();\n }\n },\n };\n}\n"],"mappings":";AA6BO,SAAS,oBAAoB,QAAwC;AAC1E,MAAI,QAAQ;AACZ,SAAO;AAAA,IACL,MAAM,SAAS;AACb,YAAM,OAAO,OAAO,KAAK,KAAK,EAAE,MAAM,QAAQ,WAAW,CAAC,EAAE;AAC5D,eAAS;AACT,aAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO,EAAE,cAAc,GAAG,kBAAkB,EAAE;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACF;;;ACzCA,SAAS,cAAc;;;ACOhB,SAAS,IAAI,QAA4B,OAAqB,SAAiB,OAAuB;AAC3G,WAAS,KAAK,IAAI,SAAS,KAAK;AAClC;;;ACHO,IAAM,cAAc;AAiIpB,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;;;AF7HJ,SAAS,sBAAsB,WAAmB,OAA8B;AACrF,MAAI,MAAM,UAAU,UAAU;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,MAAM,UAAU;AAAA,MACxB,MAAM,MAAM,QAAQ,CAAC;AAAA,IACvB;AAAA,EACF;AACA,MAAI,MAAM,UAAU,SAAS;AAC3B,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,MAAM,UAAU;AAAA,MACxB,MAAM,MAAM,QAAQ,CAAC;AAAA,MACrB,OAAO,MAAM,SAAS;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,MAAM,SAAS;AAAA,IACtB,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,EAC3C;AACF;AAgBA,SAAS,iBAAiB,QAGV;AACd,QAAM,OAAO,OAAO,QACjB,OAAO,CAAC,UAAU,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,QAAQ,EACzE,IAAI,CAAC,UAAU,MAAM,IAAK,EAC1B,KAAK,IAAI;AACZ,MAAI,OAAgB;AACpB,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,SAAS,QAAQ,OAAO,OAAO,GAAG,MAAM,KAAK;AACxD;AAEA,SAAS,iBAAiB,QAA6B;AACrD,MAAI,OAAO,OAAO,SAAS,YAAY,OAAO,QAAQ,WAAW,OAAO,MAAM;AAC5E,WAAO,OAAQ,OAAO,KAA4B,KAAK;AAAA,EACzD;AACA,SAAO,OAAO;AAChB;AAEA,SAAS,oBAAoB,cAAsB,MAAuB;AACxE,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,GAAG,YAAY;AAAA;AAAA,aAAkB,IAAI;AAC9C;AAMO,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EACT,SAAwB;AAAA,EACxB,UAAU;AAAA,EACV,aAAmC;AAAA,EAE3C,YAAY,SAA6B;AACvC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,WAAY;AACrB,UAAM,SAAS,IAAI,OAAO,EAAE,MAAM,gBAAgB,SAAS,YAAY,CAAC;AACxE,UAAM,OAAO,QAAQ,KAAK,QAAQ,SAAS;AAC3C,SAAK,SAAS;AACd,SAAK,aAAa,KAAK,IAAI,EAAE,MAAM,CAAC,UAAU;AAC5C,UAAI,KAAK,QAAS;AAClB,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAI,KAAK,QAAQ,QAAQ,SAAS,WAAW,KAAK,QAAQ,SAAS,aAAa,OAAO,EAAE;AACzF,WAAK,QAAQ,aAAa,EAAE,IAAI,OAAO,OAAO,SAAS,UAAU,EAAE,CAAC;AAAA,IACtE,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,UAAU;AACf,QAAI;AACF,YAAM,KAAK,QAAQ,MAAM;AAAA,IAC3B,QAAQ;AAAA,IAER;AACA,SAAK,SAAS;AACd,UAAM,KAAK,YAAY,MAAM,MAAM,MAAS;AAC5C,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAc,SAAS,MAAc,MAAqD;AACxF,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,sBAAsB;AACxD,UAAM,SAAS,MAAM,KAAK,OAAO,SAAS,EAAE,MAAM,WAAW,KAAK,CAAC;AACnE,WAAO,iBAAiB,MAGvB;AAAA,EACH;AAAA,EAEA,MAAc,aACZ,OACA,UACA,OAC0B;AAC1B,UAAM,WAAW,MAAM,KAAK,QAAQ,MAAM,OAAO;AAAA,MAC/C,cAAc,oBAAoB,KAAK,QAAQ,cAAc,KAAK,QAAQ,IAAI;AAAA,MAC9E;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,gBAAgB,SAAS,MAAM;AACrC,UAAM,oBAAoB,SAAS,MAAM;AACzC,UAAM,QAAQ,SAAS,MAAM,eAAe,SAAS,MAAM;AAC3D,UAAM,KAAK,QAAQ,QAAQ,KAAK;AAChC,WAAO,SAAS;AAAA,EAClB;AAAA,EAEQ,KAAK,OAAwB;AACnC,SAAK,QAAQ,UAAU,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAc,MAAqB;AACjC,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,SAAS,MAAM,KAAK,OAAO,UAAU;AAC3C,UAAM,QAAQ,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,MACxC,MAAM,KAAK;AAAA,MACX,aAAa,KAAK,eAAe,KAAK;AAAA,MACtC,aAAa,KAAK;AAAA,IACpB,EAAE;AACF,UAAM,cAAc,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,KAAK;AAEtF,WAAO,CAAC,KAAK,SAAS;AACpB,YAAM,aAAa,MAAM,KAAK,SAAS,QAAQ,CAAC,CAAC;AACjD,UAAI,KAAK,QAAS;AAClB,UAAI,WAAW,SAAS;AACtB,aAAK,QAAQ,aAAa,EAAE,IAAI,OAAO,OAAO,WAAW,MAAM,UAAU,EAAE,CAAC;AAC5E;AAAA,MACF;AAEA,YAAM,QAAoB,EAAE,cAAc,GAAG,kBAAkB,EAAE;AACjE,YAAM,iBAAkC,CAAC;AACzC,YAAM,WAA2B;AAAA,QAC/B;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,EAAmC,WAAW,IAAI;AAAA;AAAA,QAC7D;AAAA,MACF;AAEA,UAAI,SAAS,MAAM,KAAK,aAAa,aAAa,UAAU,KAAK;AACjE,UAAI;AACJ,UAAI;AACJ,UAAI,OAAO,SAAS,UAAU,OAAO,SAAS,OAAO;AAEnD,qBAAa,GAAG,OAAO,IAAI;AAAA,MAC7B,OAAO;AACL,aAAK,KAAK,EAAE,OAAO,UAAU,QAAQ,OAAO,MAAM,MAAM,OAAO,UAAU,CAAC;AAC1E,iBAAS,MAAM,KAAK,SAAS,OAAO,MAAM,OAAO,SAAS;AAC1D,YAAI,CAAC,OAAO,SAAS;AACnB,eAAK,QAAQ,aAAa;AAAA,YACxB,IAAI;AAAA,YACJ,QAAQ,OAAO;AAAA,YACf,MAAM,OAAO;AAAA,YACb,QAAQ,OAAO;AAAA,YACf,UAAU;AAAA,YACV;AAAA,UACF,CAAC;AACD;AAAA,QACF;AACA,qBAAa,iBAAiB,MAAM;AAAA,MACtC;AACA,qBAAe,KAAK,EAAE,QAAQ,OAAO,MAAM,MAAM,OAAO,WAAW,OAAO,WAAW,CAAC;AACtF,UAAI,KAAK,QAAQ,QAAQ,QAAQ,WAAW,KAAK,QAAQ,SAAS,WAAW,OAAO,IAAI,YAAY,UAAU,iBAAiB;AAC/H,WAAK,KAAK,EAAE,OAAO,SAAS,QAAQ,OAAO,MAAM,MAAM,OAAO,WAAW,OAAO,WAAW,CAAC;AAE5F,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,UAAU,OAAO,IAAI,IAAI,KAAK,UAAU,OAAO,SAAS,CAAC;AAAA,MACpE,CAAC;AACD,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,SAAS,QAAQ,QAAQ;AAAA,QACzB,SAAS;AAAA,MACX,CAAC;AACD,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,UACP,UAAU,OAAO,IAAI,YAAY,UAAU;AAAA,UAC3C;AAAA,UACA;AAAA,UACA;AAAA,EAAsB,WAAW,IAAI;AAAA,QACvC,EAAE,KAAK,IAAI;AAAA,MACb,CAAC;AAED,YAAM,QAAQ,MAAM,KAAK,aAAa,aAAa,UAAU,KAAK;AAClE,UAAI,MAAM,SAAS,UAAU,MAAM,SAAS,OAAO;AACjD,cAAM,QAAQ,kBAAkB,MAAM,IAAI,iCAAiC,UAAU;AACrF,aAAK,KAAK,EAAE,OAAO,cAAc,MAAM,CAAC;AACxC,aAAK,QAAQ,aAAa;AAAA,UACxB,IAAI;AAAA,UACJ;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,QACF,CAAC;AACD;AAAA,MACF;AAEA,WAAK,KAAK,EAAE,OAAO,UAAU,QAAQ,MAAM,MAAM,MAAM,MAAM,UAAU,CAAC;AACxE,eAAS,MAAM,KAAK,SAAS,MAAM,MAAM,MAAM,SAAS;AACxD,UAAI,CAAC,OAAO,SAAS;AACnB,aAAK,QAAQ,aAAa;AAAA,UACxB,IAAI;AAAA,UACJ,QAAQ,MAAM;AAAA,UACd,MAAM,MAAM;AAAA,UACZ,QAAQ,OAAO;AAAA,UACf,UAAU;AAAA,UACV;AAAA,QACF,CAAC;AACD;AAAA,MACF;AAEA,YAAM,cAAc,iBAAiB,MAAM;AAC3C,qBAAe,KAAK,EAAE,QAAQ,MAAM,MAAM,MAAM,MAAM,WAAW,OAAO,YAAY,CAAC;AACrF,UAAI,KAAK,QAAQ,QAAQ,QAAQ,WAAW,KAAK,QAAQ,SAAS,UAAU,MAAM,IAAI,YAAY,WAAW,EAAE;AAC/G,WAAK,KAAK,EAAE,OAAO,cAAc,QAAQ,MAAM,MAAM,MAAM,MAAM,WAAW,OAAO,YAAY,CAAC;AAChG,WAAK,QAAQ,aAAa;AAAA,QACxB,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,UAAU;AAAA,QACV;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AGtQO,SAAS,mBAAmB,QAA2C;AAC5E,QAAM,QAAQ,oBAAoB,MAAM;AACxC,QAAM,QAAQ,oBAAI,IAAyB;AAE3C,SAAO;AAAA,IACL,MAAM,aAAa,SAA8B;AAC/C,YAAM,OAAO,IAAI,YAAY;AAAA,QAC3B,WAAW,QAAQ;AAAA,QACnB,WAAW,QAAQ;AAAA,QACnB,cAAc,QAAQ;AAAA,QACtB,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,QAC7C;AAAA,QACA,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,QACnD,SAAS,OAAO,WAAW;AACzB,gBAAM,QAAQ,UAAU,MAAM;AAAA,QAChC;AAAA,QACA,cAAc,QAAQ;AAAA,QACtB,SAAS,CAAC,UAAU;AAClB,kBAAQ,UAAU,sBAAsB,QAAQ,WAAW,KAAK,CAAC;AAAA,QACnE;AAAA,MACF,CAAC;AACD,YAAM,IAAI,QAAQ,WAAW,IAAI;AACjC,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,IAEA,MAAM,YAAY,WAAmB;AACnC,YAAM,OAAO,MAAM,IAAI,SAAS;AAChC,UAAI,CAAC,KAAM;AACX,YAAM,OAAO,SAAS;AACtB,YAAM,KAAK,KAAK;AAAA,IAClB;AAAA,IAEA,MAAM,QAAQ;AACZ,YAAM,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC;AAC5B,iBAAW,MAAM,KAAK;AACpB,cAAM,OAAO,MAAM,IAAI,EAAE;AACzB,cAAM,OAAO,EAAE;AACf,cAAM,MAAM,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/loop/model.ts","../src/loop/session-loop.ts","../src/logger.ts","../src/protocol.ts","../src/loop/test-platform.ts"],"sourcesContent":["export type ToolCallRequest = {\n name: string;\n arguments: Record<string, unknown>;\n};\n\nexport type ModelMessage =\n | { role: \"system\"; content: string }\n | { role: \"user\"; content: string }\n | { role: \"assistant\"; content: string }\n | { role: \"tool\"; name: string; content: string; isError?: boolean };\n\nexport type ModelDecision = {\n toolCall: ToolCallRequest;\n usage: { promptTokens: number; completionTokens: number };\n};\n\nexport type ModelClient = {\n /** Ask the model to choose exactly one tool call. */\n decide(args: {\n instructions: string;\n tools: Array<{ name: string; description: string; inputSchema: unknown }>;\n messages: ModelMessage[];\n }): Promise<ModelDecision>;\n};\n\n/**\n * Scripted model for tests: returns predetermined tool calls in order.\n * When the queue is exhausted, always returns wait.\n */\nexport function createScriptedModel(script: ToolCallRequest[]): ModelClient {\n let index = 0;\n return {\n async decide() {\n const call = script[index] ?? { name: \"wait\", arguments: {} };\n index += 1;\n return {\n toolCall: call,\n usage: { promptTokens: 1, completionTokens: 1 },\n };\n },\n };\n}\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { Logger } from \"../logger.js\";\nimport { log } from \"../logger.js\";\nimport type { AgentEvent } from \"../types.js\";\nimport type { AttemptRecord, SessionEventName, TokenUsage, TurnReport } from \"../protocol.js\";\nimport { SDK_VERSION } from \"../protocol.js\";\nimport type { ModelClient, ModelMessage, ToolCallRequest } from \"./model.js\";\n\nexport type LoopEvent = {\n event: SessionEventName;\n action?: string;\n args?: Record<string, unknown>;\n error?: string;\n};\n\nexport function loopEventToAgentEvent(sessionId: string, event: LoopEvent): AgentEvent {\n if (event.event === \"action\") {\n return {\n type: \"action\",\n sessionId,\n action: event.action ?? \"unknown\",\n args: event.args ?? {},\n };\n }\n if (event.event === \"retry\") {\n return {\n type: \"retry\",\n sessionId,\n action: event.action ?? \"unknown\",\n args: event.args ?? {},\n error: event.error ?? \"action failed\",\n };\n }\n return {\n type: \"turnFailed\",\n sessionId,\n error: event.error ?? \"turn failed\",\n ...(event.action ? { action: event.action } : {}),\n ...(event.args ? { args: event.args } : {}),\n };\n}\n\nexport type SessionLoopOptions = {\n sessionId: string;\n transport: Transport;\n instructions: string;\n goal?: string;\n model: ModelClient;\n logger?: Logger;\n onUsage: (tokens: number) => void | Promise<void>;\n onTurnResult: (result: TurnReport) => void;\n onEvent?: (event: LoopEvent) => void;\n};\n\ntype ToolPayload = { isError: boolean; text: string; json: unknown };\n\nfunction parseToolPayload(result: {\n isError?: boolean;\n content: Array<{ type: string; text?: string }>;\n}): ToolPayload {\n const text = result.content\n .filter((entry) => entry.type === \"text\" && typeof entry.text === \"string\")\n .map((entry) => entry.text!)\n .join(\"\\n\");\n let json: unknown = text;\n try {\n json = JSON.parse(text);\n } catch {\n // keep text\n }\n return { isError: Boolean(result.isError), text, json };\n}\n\nfunction toolErrorMessage(result: ToolPayload): string {\n if (typeof result.json === \"object\" && result.json && \"error\" in result.json) {\n return String((result.json as { error: unknown }).error);\n }\n return result.text;\n}\n\nfunction composeInstructions(instructions: string, goal?: string): string {\n if (!goal) return instructions;\n return `${instructions}\\n\\nYour goal: ${goal}`;\n}\n\n/**\n * Per-session MCP client loop:\n * wait → read view → ask model → one action → retry once on error → report turn.\n */\nexport class SessionLoop {\n private readonly options: SessionLoopOptions;\n private client: Client | null = null;\n private stopped = false;\n private runPromise: Promise<void> | null = null;\n\n constructor(options: SessionLoopOptions) {\n this.options = options;\n }\n\n async start(): Promise<void> {\n if (this.runPromise) return;\n const client = new Client({ name: \"agent-player\", version: SDK_VERSION });\n await client.connect(this.options.transport);\n this.client = client;\n this.runPromise = this.run().catch((error) => {\n if (this.stopped) return;\n const message = error instanceof Error ? error.message : String(error);\n log(this.options.logger, \"error\", `session ${this.options.sessionId} crashed: ${message}`);\n this.options.onTurnResult({ ok: false, error: message, attempts: 0 });\n });\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n try {\n await this.client?.close();\n } catch {\n // ignore\n }\n this.client = null;\n await this.runPromise?.catch(() => undefined);\n this.runPromise = null;\n }\n\n private async callTool(name: string, args: Record<string, unknown>): Promise<ToolPayload> {\n if (!this.client) throw new Error(\"Client not connected\");\n const result = await this.client.callTool({ name, arguments: args });\n return parseToolPayload(result as {\n isError?: boolean;\n content: Array<{ type: string; text?: string }>;\n });\n }\n\n private async decideAction(\n tools: Array<{ name: string; description: string; inputSchema: unknown }>,\n messages: ModelMessage[],\n usage: TokenUsage,\n ): Promise<ToolCallRequest> {\n const decision = await this.options.model.decide({\n instructions: composeInstructions(this.options.instructions, this.options.goal),\n tools,\n messages,\n });\n usage.promptTokens += decision.usage.promptTokens;\n usage.completionTokens += decision.usage.completionTokens;\n const total = decision.usage.promptTokens + decision.usage.completionTokens;\n await this.options.onUsage(total);\n return decision.toolCall;\n }\n\n private emit(event: LoopEvent): void {\n this.options.onEvent?.(event);\n }\n\n private async run(): Promise<void> {\n if (!this.client) return;\n\n const listed = await this.client.listTools();\n const tools = listed.tools.map((tool) => ({\n name: tool.name,\n description: tool.description ?? tool.name,\n inputSchema: tool.inputSchema,\n }));\n const actionTools = tools.filter((tool) => tool.name !== \"wait\" && tool.name !== \"see\");\n\n while (!this.stopped) {\n const waitResult = await this.callTool(\"wait\", {});\n if (this.stopped) break;\n if (waitResult.isError) {\n this.options.onTurnResult({ ok: false, error: waitResult.text, attempts: 0 });\n continue;\n }\n\n const usage: TokenUsage = { promptTokens: 0, completionTokens: 0 };\n const attemptDetails: AttemptRecord[] = [];\n const messages: ModelMessage[] = [\n {\n role: \"user\",\n content: `It is your turn. Current view:\\n${waitResult.text}\\nCall exactly one action tool.`,\n },\n ];\n\n let action = await this.decideAction(actionTools, messages, usage);\n let result: ToolPayload | undefined;\n let firstError: string;\n if (action.name === \"wait\" || action.name === \"see\") {\n // wait and see are how the loop reads state. They are not a move.\n firstError = `${action.name} is not an action`;\n } else {\n this.emit({ event: \"action\", action: action.name, args: action.arguments });\n result = await this.callTool(action.name, action.arguments);\n if (!result.isError) {\n this.options.onTurnResult({\n ok: true,\n action: action.name,\n args: action.arguments,\n result: result.json,\n attempts: 1,\n usage,\n });\n continue;\n }\n firstError = toolErrorMessage(result);\n }\n attemptDetails.push({ action: action.name, args: action.arguments, error: firstError });\n log(this.options.logger, \"warn\", `session ${this.options.sessionId} action ${action.name} failed: ${firstError}; retrying once`);\n this.emit({ event: \"retry\", action: action.name, args: action.arguments, error: firstError });\n\n messages.push({\n role: \"assistant\",\n content: `Called ${action.name}(${JSON.stringify(action.arguments)})`,\n });\n messages.push({\n role: \"tool\",\n name: action.name,\n content: result?.text ?? firstError,\n isError: true,\n });\n messages.push({\n role: \"user\",\n content: [\n `Action ${action.name} failed: ${firstError}`,\n \"Retry once with a different legal action (or different arguments).\",\n \"Do not repeat the same failing call. Do not call wait or see.\",\n `Current view was:\\n${waitResult.text}`,\n ].join(\"\\n\"),\n });\n\n const retry = await this.decideAction(actionTools, messages, usage);\n if (retry.name === \"wait\" || retry.name === \"see\") {\n const error = `Retry returned ${retry.name} instead of an action (after: ${firstError})`;\n this.emit({ event: \"turnFailed\", error });\n this.options.onTurnResult({\n ok: false,\n error,\n attempts: 1,\n attemptDetails,\n usage,\n });\n continue;\n }\n\n this.emit({ event: \"action\", action: retry.name, args: retry.arguments });\n result = await this.callTool(retry.name, retry.arguments);\n if (!result.isError) {\n this.options.onTurnResult({\n ok: true,\n action: retry.name,\n args: retry.arguments,\n result: result.json,\n attempts: 2,\n usage,\n });\n continue;\n }\n\n const secondError = toolErrorMessage(result);\n attemptDetails.push({ action: retry.name, args: retry.arguments, error: secondError });\n log(this.options.logger, \"warn\", `session ${this.options.sessionId} retry ${retry.name} failed: ${secondError}`);\n this.emit({ event: \"turnFailed\", action: retry.name, args: retry.arguments, error: secondError });\n this.options.onTurnResult({\n ok: false,\n error: secondError,\n attempts: 2,\n attemptDetails,\n usage,\n });\n }\n }\n}\n","export type Logger = {\n debug?: (message: string, extra?: unknown) => void;\n info?: (message: string, extra?: unknown) => void;\n warn?: (message: string, extra?: unknown) => void;\n error?: (message: string, extra?: unknown) => void;\n};\n\nexport function log(logger: Logger | undefined, level: keyof Logger, message: string, extra?: unknown): void {\n logger?.[level]?.(message, extra);\n}\n","/**\n * Wire protocol between AgentServer (SDK) and the platform. Version 1.\n */\n\nexport const PROTOCOL_VERSION = 1;\nexport const SDK_VERSION = \"1.1.0\";\n\n/** How long a disconnected session can be resumed. Shared by both sides. */\nexport const SESSION_RESUME_GRACE_MS = 30_000;\n\nexport type JsonSchema = {\n type?: string | string[];\n properties?: Record<string, JsonSchema>;\n required?: string[];\n description?: string;\n additionalProperties?: boolean;\n [key: string]: unknown;\n};\n\nexport type ToolDescriptor = {\n name: string;\n description: string;\n inputSchema: JsonSchema;\n};\n\nexport type TokenUsage = {\n promptTokens: number;\n completionTokens: number;\n};\n\nexport type AttemptRecord = {\n action: string;\n args: Record<string, unknown>;\n error: string;\n};\n\nexport type TurnReport = {\n ok: boolean;\n error?: string;\n action?: string;\n args?: Record<string, unknown>;\n result?: unknown;\n attempts?: number;\n attemptDetails?: AttemptRecord[];\n usage?: TokenUsage;\n connectionLost?: boolean;\n};\n\nexport type SessionEventName = \"action\" | \"retry\" | \"turnFailed\";\n\nexport type Envelope =\n | {\n type: \"hello\";\n platformKey: string;\n clientId: string;\n protocolVersion: number;\n sdkVersion: string;\n }\n | {\n type: \"hello_ok\";\n accountName: string;\n protocolVersion: number;\n }\n | {\n type: \"hello_err\";\n message: string;\n code?: \"AUTH\" | \"PROTOCOL\";\n }\n | {\n type: \"start_session\";\n sessionId: string;\n sessionToken: string;\n tools: ToolDescriptor[];\n instructions: string;\n goal?: string;\n name?: string;\n }\n | {\n type: \"session_started\";\n sessionId: string;\n }\n | {\n type: \"stop_session\";\n sessionId: string;\n }\n | {\n type: \"session_stopped\";\n sessionId: string;\n }\n | {\n type: \"mcp\";\n sessionId: string;\n message: unknown;\n }\n | ({\n type: \"turn_result\";\n sessionId: string;\n } & TurnReport)\n | {\n type: \"session_event\";\n sessionId: string;\n event: SessionEventName;\n action?: string;\n args?: Record<string, unknown>;\n error?: string;\n }\n | {\n type: \"ping\";\n id: string;\n }\n | {\n type: \"pong\";\n id: string;\n }\n | {\n type: \"resume_session\";\n sessionId: string;\n sessionToken: string;\n }\n | {\n type: \"session_resumed\";\n sessionId: string;\n }\n | {\n type: \"resume_err\";\n sessionId: string;\n message: string;\n }\n | {\n type: \"error\";\n message: string;\n sessionId?: string;\n };\n\nexport const DEFAULT_AGENT_INSTRUCTIONS = [\n \"You are a remote game agent. Tools are provided by the game.\",\n \"On every turn: call wait first. It blocks until the game grants your turn and then returns the current view from see.\",\n \"After wait returns, call exactly one action tool (not see, not wait).\",\n \"The view's done list is what this character has already called in this session, oldest first. Use it when choosing the next action.\",\n \"If an action tool returns an error, retry once with a corrected action. A second failure ends the turn as failed.\",\n].join(\"\\n\");\n\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled protocol message: ${JSON.stringify(value)}`);\n}\n","import type { InlinePlatform, StartSessionRequest } from \"../types.js\";\nimport { createScriptedModel, type ToolCallRequest } from \"./model.js\";\nimport { loopEventToAgentEvent, SessionLoop } from \"./session-loop.js\";\n\n/**\n * In-process platform that replays a script of tool calls.\n * Same session loop as production; no network and no platform key.\n */\nexport function createTestPlatform(script: ToolCallRequest[]): InlinePlatform {\n const model = createScriptedModel(script);\n const loops = new Map<string, SessionLoop>();\n\n return {\n async startSession(request: StartSessionRequest) {\n const loop = new SessionLoop({\n sessionId: request.sessionId,\n transport: request.transport,\n instructions: request.instructions,\n ...(request.goal ? { goal: request.goal } : {}),\n model,\n ...(request.logger ? { logger: request.logger } : {}),\n onUsage: async (tokens) => {\n await request.onUsage?.(tokens);\n },\n onTurnResult: request.onTurnResult,\n onEvent: (event) => {\n request.onEvent?.(loopEventToAgentEvent(request.sessionId, event));\n },\n });\n loops.set(request.sessionId, loop);\n await loop.start();\n },\n\n async stopSession(sessionId: string) {\n const loop = loops.get(sessionId);\n if (!loop) return;\n loops.delete(sessionId);\n await loop.stop();\n },\n\n async close() {\n const ids = [...loops.keys()];\n for (const id of ids) {\n const loop = loops.get(id);\n loops.delete(id);\n await loop?.stop();\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -96,7 +96,7 @@ type AgentServerOptions = {
96
96
  platformKey?: string;
97
97
  /**
98
98
  * WebSocket URL of the platform.
99
- * Defaults to `WSI_PLATFORM_URL`, then `ws://127.0.0.1:8787`.
99
+ * Defaults to `wss://api.whitesandsint.com`.
100
100
  */
101
101
  platformUrl?: string;
102
102
  /**
@@ -96,7 +96,7 @@ type AgentServerOptions = {
96
96
  platformKey?: string;
97
97
  /**
98
98
  * WebSocket URL of the platform.
99
- * Defaults to `WSI_PLATFORM_URL`, then `ws://127.0.0.1:8787`.
99
+ * Defaults to `wss://api.whitesandsint.com`.
100
100
  */
101
101
  platformUrl?: string;
102
102
  /**
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@whitesands/agent-player",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Start a remote game agent from a player class. The SDK owns the MCP server; the model stays on the platform.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/White-Sands-Interactive/WSI-Node-Framework.git"
7
+ "url": "git+https://github.com/White-Sands-Interactive/WSI-Node.git"
8
8
  },
9
- "homepage": "https://github.com/White-Sands-Interactive/WSI-Node-Framework#readme",
9
+ "homepage": "https://github.com/White-Sands-Interactive/WSI-Node#readme",
10
10
  "bugs": {
11
- "url": "https://github.com/White-Sands-Interactive/WSI-Node-Framework/issues"
11
+ "url": "https://github.com/White-Sands-Interactive/WSI-Node/issues"
12
12
  },
13
13
  "type": "module",
14
14
  "license": "MIT",
@@ -49,8 +49,7 @@
49
49
  "build": "tsup && npm run build -w @wsi/forest-example && npm run build -w @wsi/courier-example && npm run build -w @wsi/ferry-example",
50
50
  "docs": "typedoc",
51
51
  "clean": "rm -rf dist docs",
52
- "version-packages": "changeset version",
53
- "release": "tsup && npm publish --access public",
52
+ "release": "tsup && node scripts/publish.mjs",
54
53
  "test": "vitest run",
55
54
  "test:watch": "vitest",
56
55
  "forest": "npm run start -w @wsi/forest-example --",