@whitesands/agent-player 1.1.0 → 1.2.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/CHANGELOG.md +7 -0
- package/README.md +37 -5
- package/dist/index.cjs +141 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +139 -44
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +16 -3
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +6 -2
- package/dist/protocol.d.ts +6 -2
- package/dist/protocol.js +14 -2
- package/dist/protocol.js.map +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/{types-DXM_HZHu.d.ts → types-DXqkV7YA.d.ts} +6 -0
- package/dist/{types-D0p6GrVs.d.cts → types-Zi-2YmF2.d.cts} +6 -0
- package/package.json +1 -1
package/dist/protocol.cjs
CHANGED
|
@@ -24,11 +24,12 @@ __export(protocol_exports, {
|
|
|
24
24
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
25
25
|
SDK_VERSION: () => SDK_VERSION,
|
|
26
26
|
SESSION_RESUME_GRACE_MS: () => SESSION_RESUME_GRACE_MS,
|
|
27
|
-
assertNever: () => assertNever
|
|
27
|
+
assertNever: () => assertNever,
|
|
28
|
+
requirePlayerId: () => requirePlayerId
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(protocol_exports);
|
|
30
31
|
var PROTOCOL_VERSION = 1;
|
|
31
|
-
var SDK_VERSION = "1.
|
|
32
|
+
var SDK_VERSION = "1.2.0";
|
|
32
33
|
var SESSION_RESUME_GRACE_MS = 3e4;
|
|
33
34
|
var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
34
35
|
"You are a remote game agent. Tools are provided by the game.",
|
|
@@ -37,6 +38,17 @@ var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
37
38
|
"The view's done list is what this character has already called in this session, oldest first. Use it when choosing the next action.",
|
|
38
39
|
"If an action tool returns an error, retry once with a corrected action. A second failure ends the turn as failed."
|
|
39
40
|
].join("\n");
|
|
41
|
+
var PLAYER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42
|
+
function requirePlayerId(value) {
|
|
43
|
+
const id = value?.trim() ?? "";
|
|
44
|
+
if (id.length === 0) {
|
|
45
|
+
throw new Error("playerId is required. Copy the character ID from the web app");
|
|
46
|
+
}
|
|
47
|
+
if (!PLAYER_ID.test(id)) {
|
|
48
|
+
throw new Error(`playerId "${id}" is not a character ID. Copy the ID from the character in the web app`);
|
|
49
|
+
}
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
40
52
|
function assertNever(value) {
|
|
41
53
|
throw new Error(`Unhandled protocol message: ${JSON.stringify(value)}`);
|
|
42
54
|
}
|
|
@@ -46,6 +58,7 @@ function assertNever(value) {
|
|
|
46
58
|
PROTOCOL_VERSION,
|
|
47
59
|
SDK_VERSION,
|
|
48
60
|
SESSION_RESUME_GRACE_MS,
|
|
49
|
-
assertNever
|
|
61
|
+
assertNever,
|
|
62
|
+
requirePlayerId
|
|
50
63
|
});
|
|
51
64
|
//# sourceMappingURL=protocol.cjs.map
|
package/dist/protocol.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between AgentServer (SDK) and the platform. Version 1.\n */\n\nexport const PROTOCOL_VERSION = 1;\nexport const SDK_VERSION = \"1.
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between AgentServer (SDK) and the platform. Version 1.\n */\n\nexport const PROTOCOL_VERSION = 1;\nexport const SDK_VERSION = \"1.2.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 /** Character id. One platform key can start every character in that game. */\n playerId: 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\nconst PLAYER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Trim and check the character id `start_session` must send. */\nexport function requirePlayerId(value: string | undefined): string {\n const id = value?.trim() ?? \"\";\n if (id.length === 0) {\n throw new Error(\"playerId is required. Copy the character ID from the web app\");\n }\n if (!PLAYER_ID.test(id)) {\n throw new Error(`playerId \"${id}\" is not a character ID. Copy the ID from the character in the web app`);\n }\n return id;\n}\n\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled protocol message: ${JSON.stringify(value)}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,mBAAmB;AACzB,IAAM,cAAc;AAGpB,IAAM,0BAA0B;AAgIhC,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,YAAY;AAGX,SAAS,gBAAgB,OAAmC;AACjE,QAAM,KAAK,OAAO,KAAK,KAAK;AAC5B,MAAI,GAAG,WAAW,GAAG;AACnB,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AACA,MAAI,CAAC,UAAU,KAAK,EAAE,GAAG;AACvB,UAAM,IAAI,MAAM,aAAa,EAAE,wEAAwE;AAAA,EACzG;AACA,SAAO;AACT;AAEO,SAAS,YAAY,OAAqB;AAC/C,QAAM,IAAI,MAAM,+BAA+B,KAAK,UAAU,KAAK,CAAC,EAAE;AACxE;","names":[]}
|
package/dist/protocol.d.cts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Wire protocol between AgentServer (SDK) and the platform. Version 1.
|
|
3
3
|
*/
|
|
4
4
|
declare const PROTOCOL_VERSION = 1;
|
|
5
|
-
declare const SDK_VERSION = "1.
|
|
5
|
+
declare const SDK_VERSION = "1.2.0";
|
|
6
6
|
/** How long a disconnected session can be resumed. Shared by both sides. */
|
|
7
7
|
declare const SESSION_RESUME_GRACE_MS = 30000;
|
|
8
8
|
type JsonSchema = {
|
|
@@ -57,6 +57,8 @@ type Envelope = {
|
|
|
57
57
|
type: "start_session";
|
|
58
58
|
sessionId: string;
|
|
59
59
|
sessionToken: string;
|
|
60
|
+
/** Character id. One platform key can start every character in that game. */
|
|
61
|
+
playerId: string;
|
|
60
62
|
tools: ToolDescriptor[];
|
|
61
63
|
instructions: string;
|
|
62
64
|
goal?: string;
|
|
@@ -107,6 +109,8 @@ type Envelope = {
|
|
|
107
109
|
sessionId?: string;
|
|
108
110
|
};
|
|
109
111
|
declare const DEFAULT_AGENT_INSTRUCTIONS: string;
|
|
112
|
+
/** Trim and check the character id `start_session` must send. */
|
|
113
|
+
declare function requirePlayerId(value: string | undefined): string;
|
|
110
114
|
declare function assertNever(value: never): never;
|
|
111
115
|
|
|
112
|
-
export { type AttemptRecord, DEFAULT_AGENT_INSTRUCTIONS, type Envelope, type JsonSchema, PROTOCOL_VERSION, SDK_VERSION, SESSION_RESUME_GRACE_MS, type SessionEventName, type TokenUsage, type ToolDescriptor, type TurnReport, assertNever };
|
|
116
|
+
export { type AttemptRecord, DEFAULT_AGENT_INSTRUCTIONS, type Envelope, type JsonSchema, PROTOCOL_VERSION, SDK_VERSION, SESSION_RESUME_GRACE_MS, type SessionEventName, type TokenUsage, type ToolDescriptor, type TurnReport, assertNever, requirePlayerId };
|
package/dist/protocol.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Wire protocol between AgentServer (SDK) and the platform. Version 1.
|
|
3
3
|
*/
|
|
4
4
|
declare const PROTOCOL_VERSION = 1;
|
|
5
|
-
declare const SDK_VERSION = "1.
|
|
5
|
+
declare const SDK_VERSION = "1.2.0";
|
|
6
6
|
/** How long a disconnected session can be resumed. Shared by both sides. */
|
|
7
7
|
declare const SESSION_RESUME_GRACE_MS = 30000;
|
|
8
8
|
type JsonSchema = {
|
|
@@ -57,6 +57,8 @@ type Envelope = {
|
|
|
57
57
|
type: "start_session";
|
|
58
58
|
sessionId: string;
|
|
59
59
|
sessionToken: string;
|
|
60
|
+
/** Character id. One platform key can start every character in that game. */
|
|
61
|
+
playerId: string;
|
|
60
62
|
tools: ToolDescriptor[];
|
|
61
63
|
instructions: string;
|
|
62
64
|
goal?: string;
|
|
@@ -107,6 +109,8 @@ type Envelope = {
|
|
|
107
109
|
sessionId?: string;
|
|
108
110
|
};
|
|
109
111
|
declare const DEFAULT_AGENT_INSTRUCTIONS: string;
|
|
112
|
+
/** Trim and check the character id `start_session` must send. */
|
|
113
|
+
declare function requirePlayerId(value: string | undefined): string;
|
|
110
114
|
declare function assertNever(value: never): never;
|
|
111
115
|
|
|
112
|
-
export { type AttemptRecord, DEFAULT_AGENT_INSTRUCTIONS, type Envelope, type JsonSchema, PROTOCOL_VERSION, SDK_VERSION, SESSION_RESUME_GRACE_MS, type SessionEventName, type TokenUsage, type ToolDescriptor, type TurnReport, assertNever };
|
|
116
|
+
export { type AttemptRecord, DEFAULT_AGENT_INSTRUCTIONS, type Envelope, type JsonSchema, PROTOCOL_VERSION, SDK_VERSION, SESSION_RESUME_GRACE_MS, type SessionEventName, type TokenUsage, type ToolDescriptor, type TurnReport, assertNever, requirePlayerId };
|
package/dist/protocol.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/protocol.ts
|
|
2
2
|
var PROTOCOL_VERSION = 1;
|
|
3
|
-
var SDK_VERSION = "1.
|
|
3
|
+
var SDK_VERSION = "1.2.0";
|
|
4
4
|
var SESSION_RESUME_GRACE_MS = 3e4;
|
|
5
5
|
var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
6
6
|
"You are a remote game agent. Tools are provided by the game.",
|
|
@@ -9,6 +9,17 @@ var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
9
9
|
"The view's done list is what this character has already called in this session, oldest first. Use it when choosing the next action.",
|
|
10
10
|
"If an action tool returns an error, retry once with a corrected action. A second failure ends the turn as failed."
|
|
11
11
|
].join("\n");
|
|
12
|
+
var PLAYER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13
|
+
function requirePlayerId(value) {
|
|
14
|
+
const id = value?.trim() ?? "";
|
|
15
|
+
if (id.length === 0) {
|
|
16
|
+
throw new Error("playerId is required. Copy the character ID from the web app");
|
|
17
|
+
}
|
|
18
|
+
if (!PLAYER_ID.test(id)) {
|
|
19
|
+
throw new Error(`playerId "${id}" is not a character ID. Copy the ID from the character in the web app`);
|
|
20
|
+
}
|
|
21
|
+
return id;
|
|
22
|
+
}
|
|
12
23
|
function assertNever(value) {
|
|
13
24
|
throw new Error(`Unhandled protocol message: ${JSON.stringify(value)}`);
|
|
14
25
|
}
|
|
@@ -17,6 +28,7 @@ export {
|
|
|
17
28
|
PROTOCOL_VERSION,
|
|
18
29
|
SDK_VERSION,
|
|
19
30
|
SESSION_RESUME_GRACE_MS,
|
|
20
|
-
assertNever
|
|
31
|
+
assertNever,
|
|
32
|
+
requirePlayerId
|
|
21
33
|
};
|
|
22
34
|
//# sourceMappingURL=protocol.js.map
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between AgentServer (SDK) and the platform. Version 1.\n */\n\nexport const PROTOCOL_VERSION = 1;\nexport const SDK_VERSION = \"1.
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between AgentServer (SDK) and the platform. Version 1.\n */\n\nexport const PROTOCOL_VERSION = 1;\nexport const SDK_VERSION = \"1.2.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 /** Character id. One platform key can start every character in that game. */\n playerId: 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\nconst PLAYER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Trim and check the character id `start_session` must send. */\nexport function requirePlayerId(value: string | undefined): string {\n const id = value?.trim() ?? \"\";\n if (id.length === 0) {\n throw new Error(\"playerId is required. Copy the character ID from the web app\");\n }\n if (!PLAYER_ID.test(id)) {\n throw new Error(`playerId \"${id}\" is not a character ID. Copy the ID from the character in the web app`);\n }\n return id;\n}\n\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled protocol message: ${JSON.stringify(value)}`);\n}\n"],"mappings":";AAIO,IAAM,mBAAmB;AACzB,IAAM,cAAc;AAGpB,IAAM,0BAA0B;AAgIhC,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,YAAY;AAGX,SAAS,gBAAgB,OAAmC;AACjE,QAAM,KAAK,OAAO,KAAK,KAAK;AAC5B,MAAI,GAAG,WAAW,GAAG;AACnB,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AACA,MAAI,CAAC,UAAU,KAAK,EAAE,GAAG;AACvB,UAAM,IAAI,MAAM,aAAa,EAAE,wEAAwE;AAAA,EACzG;AACA,SAAO;AACT;AAEO,SAAS,YAAY,OAAqB;AAC/C,QAAM,IAAI,MAAM,+BAA+B,KAAK,UAAU,KAAK,CAAC,EAAE;AACxE;","names":[]}
|
package/dist/testing.cjs
CHANGED
|
@@ -51,7 +51,7 @@ function log(logger, level, message, extra) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// src/protocol.ts
|
|
54
|
-
var SDK_VERSION = "1.
|
|
54
|
+
var SDK_VERSION = "1.2.0";
|
|
55
55
|
var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
56
56
|
"You are a remote game agent. Tools are provided by the game.",
|
|
57
57
|
"On every turn: call wait first. It blocks until the game grants your turn and then returns the current view from see.",
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing.ts","../src/loop/model.ts","../src/loop/session-loop.ts","../src/logger.ts","../src/protocol.ts","../src/loop/test-platform.ts"],"sourcesContent":["export type { InlinePlatform, StartSessionRequest } from \"./types.js\";\nexport { createTestPlatform } from \"./loop/test-platform.js\";\nexport { SessionLoop, loopEventToAgentEvent, type SessionLoopOptions, type LoopEvent } from \"./loop/session-loop.js\";\nexport {\n createScriptedModel,\n type ModelClient,\n type ModelDecision,\n type ModelMessage,\n type ToolCallRequest,\n} from \"./loop/model.js\";\n","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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC6BO,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,oBAAuB;;;ACOhB,SAAS,IAAI,QAA4B,OAAqB,SAAiB,OAAuB;AAC3G,WAAS,KAAK,IAAI,SAAS,KAAK;AAClC;;;ACJO,IAAM,cAAc;AAiIpB,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;;;AF5HJ,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,qBAAO,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/testing.ts","../src/loop/model.ts","../src/loop/session-loop.ts","../src/logger.ts","../src/protocol.ts","../src/loop/test-platform.ts"],"sourcesContent":["export type { InlinePlatform, StartSessionRequest } from \"./types.js\";\nexport { createTestPlatform } from \"./loop/test-platform.js\";\nexport { SessionLoop, loopEventToAgentEvent, type SessionLoopOptions, type LoopEvent } from \"./loop/session-loop.js\";\nexport {\n createScriptedModel,\n type ModelClient,\n type ModelDecision,\n type ModelMessage,\n type ToolCallRequest,\n} from \"./loop/model.js\";\n","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.2.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 /** Character id. One platform key can start every character in that game. */\n playerId: 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\nconst PLAYER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Trim and check the character id `start_session` must send. */\nexport function requirePlayerId(value: string | undefined): string {\n const id = value?.trim() ?? \"\";\n if (id.length === 0) {\n throw new Error(\"playerId is required. Copy the character ID from the web app\");\n }\n if (!PLAYER_ID.test(id)) {\n throw new Error(`playerId \"${id}\" is not a character ID. Copy the ID from the character in the web app`);\n }\n return id;\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":[]}
|
package/dist/testing.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InlinePlatform, L as Logger, b as AgentEvent } from './types-
|
|
2
|
-
export { d as StartSessionRequest } from './types-
|
|
1
|
+
import { I as InlinePlatform, L as Logger, b as AgentEvent } from './types-Zi-2YmF2.cjs';
|
|
2
|
+
export { d as StartSessionRequest } from './types-Zi-2YmF2.cjs';
|
|
3
3
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
4
4
|
import { SessionEventName, TurnReport } from './protocol.cjs';
|
|
5
5
|
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InlinePlatform, L as Logger, b as AgentEvent } from './types-
|
|
2
|
-
export { d as StartSessionRequest } from './types-
|
|
1
|
+
import { I as InlinePlatform, L as Logger, b as AgentEvent } from './types-DXqkV7YA.js';
|
|
2
|
+
export { d as StartSessionRequest } from './types-DXqkV7YA.js';
|
|
3
3
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
4
4
|
import { SessionEventName, TurnReport } from './protocol.js';
|
|
5
5
|
|
package/dist/testing.js
CHANGED
|
@@ -22,7 +22,7 @@ function log(logger, level, message, extra) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// src/protocol.ts
|
|
25
|
-
var SDK_VERSION = "1.
|
|
25
|
+
var SDK_VERSION = "1.2.0";
|
|
26
26
|
var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
27
27
|
"You are a remote game agent. Tools are provided by the game.",
|
|
28
28
|
"On every turn: call wait first. It blocks until the game grants your turn and then returns the current view from see.",
|
package/dist/testing.js.map
CHANGED
|
@@ -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 */\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":[]}
|
|
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.2.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 /** Character id. One platform key can start every character in that game. */\n playerId: 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\nconst PLAYER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Trim and check the character id `start_session` must send. */\nexport function requirePlayerId(value: string | undefined): string {\n const id = value?.trim() ?? \"\";\n if (id.length === 0) {\n throw new Error(\"playerId is required. Copy the character ID from the web app\");\n }\n if (!PLAYER_ID.test(id)) {\n throw new Error(`playerId \"${id}\" is not a character ID. Copy the ID from the character in the web app`);\n }\n return id;\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":[]}
|
|
@@ -58,6 +58,11 @@ type AgentHandle = {
|
|
|
58
58
|
on(listener: EventListener): () => void;
|
|
59
59
|
};
|
|
60
60
|
type StartOptions = {
|
|
61
|
+
/**
|
|
62
|
+
* Character to play. Copy the id from the Players page.
|
|
63
|
+
* The platform key belongs to the game; this id selects the character.
|
|
64
|
+
*/
|
|
65
|
+
playerId: string;
|
|
61
66
|
/** Label stored with the session. Defaults to the class name. */
|
|
62
67
|
name?: string;
|
|
63
68
|
/** Added to the model prompt for this player only. */
|
|
@@ -68,6 +73,7 @@ type StartOptions = {
|
|
|
68
73
|
type StartSessionRequest = {
|
|
69
74
|
sessionId: string;
|
|
70
75
|
sessionToken: string;
|
|
76
|
+
playerId: string;
|
|
71
77
|
name?: string;
|
|
72
78
|
tools: ToolDescriptor[];
|
|
73
79
|
instructions: string;
|
|
@@ -58,6 +58,11 @@ type AgentHandle = {
|
|
|
58
58
|
on(listener: EventListener): () => void;
|
|
59
59
|
};
|
|
60
60
|
type StartOptions = {
|
|
61
|
+
/**
|
|
62
|
+
* Character to play. Copy the id from the Players page.
|
|
63
|
+
* The platform key belongs to the game; this id selects the character.
|
|
64
|
+
*/
|
|
65
|
+
playerId: string;
|
|
61
66
|
/** Label stored with the session. Defaults to the class name. */
|
|
62
67
|
name?: string;
|
|
63
68
|
/** Added to the model prompt for this player only. */
|
|
@@ -68,6 +73,7 @@ type StartOptions = {
|
|
|
68
73
|
type StartSessionRequest = {
|
|
69
74
|
sessionId: string;
|
|
70
75
|
sessionToken: string;
|
|
76
|
+
playerId: string;
|
|
71
77
|
name?: string;
|
|
72
78
|
tools: ToolDescriptor[];
|
|
73
79
|
instructions: string;
|
package/package.json
CHANGED