@agent-os-lab/agent-game-sdk 0.1.1 → 0.1.2
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/README.md +3 -3
- package/package.json +2 -1
- package/src/office/renderers/three/agent-activity-effects.ts +10 -53
- package/src/office/renderers/three/agent-body-instancing.ts +2 -2
- package/src/office/renderers/three/agent-effect-instancing.ts +134 -0
- package/src/office/renderers/three/agent-mesh.ts +6 -18
- package/src/office/renderers/three/mount.ts +7 -0
- package/src/runtime-client.ts +27 -2
package/README.md
CHANGED
|
@@ -51,13 +51,13 @@ export function Office() {
|
|
|
51
51
|
|
|
52
52
|
`agent-game-sdk/office` is renderer and framework neutral. `agent-game-sdk/office/react` is the React-only entrypoint.
|
|
53
53
|
|
|
54
|
-
Browser clients should call an application BFF endpoint. Keep the AgentOS service API key on the server:
|
|
54
|
+
Browser clients should call an application BFF endpoint. Keep the AgentOS service API key on the server; the SDK server client forwards it to Agent Game Runtime:
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
57
|
import { AgentGameRuntimeServerClient } from "agent-game-sdk";
|
|
58
58
|
|
|
59
59
|
const client = new AgentGameRuntimeServerClient({
|
|
60
|
-
baseUrl: process.env.
|
|
60
|
+
baseUrl: process.env.AGENT_GAME_RUNTIME_BASE_URL!,
|
|
61
61
|
apiKey: process.env.AGENTOS_API_KEY!,
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -71,7 +71,7 @@ export async function GET() {
|
|
|
71
71
|
Run the SDK office view demo:
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
|
|
74
|
+
AGENT_GAME_RUNTIME_BASE_URL=http://localhost:3107 AGENTOS_API_KEY=... bun run demo
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
Open `http://localhost:7357`. Set `PORT=7358` or another value to change the port.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-os-lab/agent-game-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsc --noEmit -p tsconfig.json",
|
|
16
16
|
"demo": "bun --env-file=.env.development --hot demo/server.ts",
|
|
17
|
+
"demo:stop": "bun scripts/stop-demo.ts",
|
|
17
18
|
"sdk:publish": "bun scripts/publish-agent-game-sdk.ts",
|
|
18
19
|
"test": "bun test",
|
|
19
20
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
@@ -12,19 +12,12 @@ export type AgentActivityEffectMode =
|
|
|
12
12
|
|
|
13
13
|
export type AgentActivityEffects = {
|
|
14
14
|
group: THREE.Group;
|
|
15
|
-
typingDots: THREE.
|
|
16
|
-
toolBlock: THREE.
|
|
17
|
-
runningRing: THREE.
|
|
18
|
-
completionRing: THREE.
|
|
15
|
+
typingDots: THREE.Object3D[];
|
|
16
|
+
toolBlock: THREE.Object3D;
|
|
17
|
+
runningRing: THREE.Object3D;
|
|
18
|
+
completionRing: THREE.Object3D;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
const typingDotGeometry = new THREE.SphereGeometry(0.055, 12, 8);
|
|
22
|
-
const typingDotMaterial = new THREE.MeshBasicMaterial({ color: 0xe0f2fe });
|
|
23
|
-
const toolBlockGeometry = new THREE.BoxGeometry(0.34, 0.22, 0.08);
|
|
24
|
-
const toolBlockMaterial = new THREE.MeshBasicMaterial({ color: 0x0f172a });
|
|
25
|
-
const ringGeometryCache = new Map<string, THREE.RingGeometry>();
|
|
26
|
-
const ringMaterialCache = new Map<number, THREE.MeshBasicMaterial>();
|
|
27
|
-
|
|
28
21
|
export function resolveActivityEffectMode(
|
|
29
22
|
agent: Pick<AgentGameOfficeAgent, "activity" | "activityLabel" | "sceneState">,
|
|
30
23
|
): AgentActivityEffectMode {
|
|
@@ -51,26 +44,20 @@ export function resolveActivityEffectMode(
|
|
|
51
44
|
export function createAgentActivityEffects(): AgentActivityEffects {
|
|
52
45
|
const group = new THREE.Group();
|
|
53
46
|
const typingDots = [0, 1, 2].map((index) => {
|
|
54
|
-
const dot = new THREE.
|
|
55
|
-
typingDotGeometry,
|
|
56
|
-
typingDotMaterial,
|
|
57
|
-
);
|
|
47
|
+
const dot = new THREE.Object3D();
|
|
58
48
|
dot.position.set(-0.18 + index * 0.18, 1.95, 0.18);
|
|
59
49
|
dot.visible = false;
|
|
60
50
|
group.add(dot);
|
|
61
51
|
return dot;
|
|
62
52
|
});
|
|
63
53
|
|
|
64
|
-
const toolBlock = new THREE.
|
|
65
|
-
toolBlockGeometry,
|
|
66
|
-
toolBlockMaterial,
|
|
67
|
-
);
|
|
54
|
+
const toolBlock = new THREE.Object3D();
|
|
68
55
|
toolBlock.position.set(0.48, 1.18, 0.18);
|
|
69
56
|
toolBlock.visible = false;
|
|
70
57
|
group.add(toolBlock);
|
|
71
58
|
|
|
72
|
-
const runningRing = createRing(
|
|
73
|
-
const completionRing = createRing(
|
|
59
|
+
const runningRing = createRing();
|
|
60
|
+
const completionRing = createRing();
|
|
74
61
|
group.add(runningRing, completionRing);
|
|
75
62
|
|
|
76
63
|
return { group, typingDots, toolBlock, runningRing, completionRing };
|
|
@@ -113,44 +100,14 @@ export function updateAgentActivityEffects(
|
|
|
113
100
|
}
|
|
114
101
|
}
|
|
115
102
|
|
|
116
|
-
function createRing(
|
|
117
|
-
const ring = new THREE.
|
|
118
|
-
getRingGeometry(innerRadius, outerRadius),
|
|
119
|
-
getRingMaterial(color),
|
|
120
|
-
);
|
|
103
|
+
function createRing(): THREE.Object3D {
|
|
104
|
+
const ring = new THREE.Object3D();
|
|
121
105
|
ring.rotation.x = -Math.PI / 2;
|
|
122
106
|
ring.position.y = 0.04;
|
|
123
107
|
ring.visible = false;
|
|
124
108
|
return ring;
|
|
125
109
|
}
|
|
126
110
|
|
|
127
|
-
function getRingGeometry(innerRadius: number, outerRadius: number): THREE.RingGeometry {
|
|
128
|
-
const key = `${innerRadius}:${outerRadius}`;
|
|
129
|
-
const cached = ringGeometryCache.get(key);
|
|
130
|
-
if (cached) {
|
|
131
|
-
return cached;
|
|
132
|
-
}
|
|
133
|
-
const geometry = new THREE.RingGeometry(innerRadius, outerRadius, 32);
|
|
134
|
-
ringGeometryCache.set(key, geometry);
|
|
135
|
-
return geometry;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function getRingMaterial(color: number): THREE.MeshBasicMaterial {
|
|
139
|
-
const cached = ringMaterialCache.get(color);
|
|
140
|
-
if (cached) {
|
|
141
|
-
return cached;
|
|
142
|
-
}
|
|
143
|
-
const material = new THREE.MeshBasicMaterial({
|
|
144
|
-
color,
|
|
145
|
-
transparent: true,
|
|
146
|
-
opacity: 0.36,
|
|
147
|
-
side: THREE.DoubleSide,
|
|
148
|
-
depthWrite: false,
|
|
149
|
-
});
|
|
150
|
-
ringMaterialCache.set(color, material);
|
|
151
|
-
return material;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
111
|
function hideEffects(effects: AgentActivityEffects): void {
|
|
155
112
|
effects.typingDots.forEach((dot) => {
|
|
156
113
|
dot.visible = false;
|
|
@@ -41,7 +41,7 @@ export class AgentBodyInstancedLayer {
|
|
|
41
41
|
}
|
|
42
42
|
record.mesh.group.updateMatrixWorld(true);
|
|
43
43
|
resolveAgentBodyPartRenderSpecs(record.renderIndex).forEach((part) => {
|
|
44
|
-
const batch = this.ensureBatch(part, records.length);
|
|
44
|
+
const batch = this.ensureBatch(part, records.length * 2);
|
|
45
45
|
const object = record.mesh[part.key];
|
|
46
46
|
object.updateMatrixWorld(true);
|
|
47
47
|
matrixHelper.copy(object.matrixWorld);
|
|
@@ -68,7 +68,7 @@ export class AgentBodyInstancedLayer {
|
|
|
68
68
|
part: ReturnType<typeof resolveAgentBodyPartRenderSpecs>[number],
|
|
69
69
|
requiredCapacity: number,
|
|
70
70
|
): AgentBodyBatch {
|
|
71
|
-
const key = `${part.
|
|
71
|
+
const key = `${part.width}:${part.height}:${part.depth}:${part.color}`;
|
|
72
72
|
const existing = this.batches.get(key);
|
|
73
73
|
if (existing && existing.capacity >= requiredCapacity) {
|
|
74
74
|
return existing;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
|
|
3
|
+
import type { AgentMeshParts } from "./agent-mesh";
|
|
4
|
+
|
|
5
|
+
export type AgentEffectInstanceRecord = {
|
|
6
|
+
mesh: AgentMeshParts;
|
|
7
|
+
visible: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type EffectBatch = {
|
|
11
|
+
mesh: THREE.InstancedMesh;
|
|
12
|
+
capacity: number;
|
|
13
|
+
count: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const matrixHelper = new THREE.Matrix4();
|
|
17
|
+
|
|
18
|
+
export class AgentEffectInstancedLayer {
|
|
19
|
+
private readonly scene: THREE.Scene;
|
|
20
|
+
private readonly batches = new Map<string, EffectBatch>();
|
|
21
|
+
|
|
22
|
+
constructor(scene: THREE.Scene) {
|
|
23
|
+
this.scene = scene;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
update(records: AgentEffectInstanceRecord[]): void {
|
|
27
|
+
this.batches.forEach((batch) => {
|
|
28
|
+
batch.count = 0;
|
|
29
|
+
batch.mesh.count = 0;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
records.forEach((record) => {
|
|
33
|
+
if (!record.visible) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
record.mesh.group.updateMatrixWorld(true);
|
|
37
|
+
this.addObject("glow", record.mesh.activityGlow, records.length);
|
|
38
|
+
record.mesh.activityEffects.typingDots.forEach((dot) => {
|
|
39
|
+
this.addObject("typingDot", dot, records.length * 3);
|
|
40
|
+
});
|
|
41
|
+
this.addObject("toolBlock", record.mesh.activityEffects.toolBlock, records.length);
|
|
42
|
+
this.addObject("runningRing", record.mesh.activityEffects.runningRing, records.length);
|
|
43
|
+
this.addObject("completionRing", record.mesh.activityEffects.completionRing, records.length);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
this.batches.forEach((batch) => {
|
|
47
|
+
batch.mesh.count = batch.count;
|
|
48
|
+
batch.mesh.visible = batch.count > 0;
|
|
49
|
+
batch.mesh.instanceMatrix.needsUpdate = true;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dispose(): void {
|
|
54
|
+
this.batches.forEach((batch) => {
|
|
55
|
+
this.scene.remove(batch.mesh);
|
|
56
|
+
});
|
|
57
|
+
this.batches.clear();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private addObject(key: EffectKey, object: THREE.Object3D, capacity: number): void {
|
|
61
|
+
if (!object.visible) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const batch = this.ensureBatch(key, capacity);
|
|
65
|
+
object.updateMatrixWorld(true);
|
|
66
|
+
matrixHelper.copy(object.matrixWorld);
|
|
67
|
+
batch.mesh.setMatrixAt(batch.count, matrixHelper);
|
|
68
|
+
batch.count += 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private ensureBatch(key: EffectKey, requiredCapacity: number): EffectBatch {
|
|
72
|
+
const existing = this.batches.get(key);
|
|
73
|
+
if (existing && existing.capacity >= requiredCapacity) {
|
|
74
|
+
return existing;
|
|
75
|
+
}
|
|
76
|
+
if (existing) {
|
|
77
|
+
this.scene.remove(existing.mesh);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const spec = effectSpecs[key];
|
|
81
|
+
const mesh = new THREE.InstancedMesh(
|
|
82
|
+
spec.geometry,
|
|
83
|
+
spec.material,
|
|
84
|
+
Math.max(requiredCapacity, 1),
|
|
85
|
+
);
|
|
86
|
+
mesh.castShadow = false;
|
|
87
|
+
mesh.receiveShadow = false;
|
|
88
|
+
mesh.name = `agentEffect:${key}`;
|
|
89
|
+
this.scene.add(mesh);
|
|
90
|
+
|
|
91
|
+
const batch = { mesh, capacity: Math.max(requiredCapacity, 1), count: 0 };
|
|
92
|
+
this.batches.set(key, batch);
|
|
93
|
+
return batch;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createAgentEffectInstancedLayer(scene: THREE.Scene): AgentEffectInstancedLayer {
|
|
98
|
+
return new AgentEffectInstancedLayer(scene);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
type EffectKey = "glow" | "typingDot" | "toolBlock" | "runningRing" | "completionRing";
|
|
102
|
+
|
|
103
|
+
const effectSpecs: Record<EffectKey, { geometry: THREE.BufferGeometry; material: THREE.Material }> = {
|
|
104
|
+
glow: {
|
|
105
|
+
geometry: new THREE.RingGeometry(0.56, 0.92, 32),
|
|
106
|
+
material: createTransparentMaterial(0x38bdf8, 0.35),
|
|
107
|
+
},
|
|
108
|
+
typingDot: {
|
|
109
|
+
geometry: new THREE.SphereGeometry(0.055, 12, 8),
|
|
110
|
+
material: new THREE.MeshBasicMaterial({ color: 0xe0f2fe }),
|
|
111
|
+
},
|
|
112
|
+
toolBlock: {
|
|
113
|
+
geometry: new THREE.BoxGeometry(0.34, 0.22, 0.08),
|
|
114
|
+
material: new THREE.MeshBasicMaterial({ color: 0x0f172a }),
|
|
115
|
+
},
|
|
116
|
+
runningRing: {
|
|
117
|
+
geometry: new THREE.RingGeometry(0.48, 0.68, 32),
|
|
118
|
+
material: createTransparentMaterial(0x38bdf8, 0.36),
|
|
119
|
+
},
|
|
120
|
+
completionRing: {
|
|
121
|
+
geometry: new THREE.RingGeometry(0.58, 0.82, 32),
|
|
122
|
+
material: createTransparentMaterial(0x22c55e, 0.36),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
function createTransparentMaterial(color: number, opacity: number): THREE.MeshBasicMaterial {
|
|
127
|
+
return new THREE.MeshBasicMaterial({
|
|
128
|
+
color,
|
|
129
|
+
transparent: true,
|
|
130
|
+
opacity,
|
|
131
|
+
side: THREE.DoubleSide,
|
|
132
|
+
depthWrite: false,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -21,23 +21,14 @@ export type AgentMeshParts = {
|
|
|
21
21
|
rightLeg: THREE.Object3D;
|
|
22
22
|
leftFoot: THREE.Object3D;
|
|
23
23
|
rightFoot: THREE.Object3D;
|
|
24
|
-
activityGlow: THREE.
|
|
24
|
+
activityGlow: THREE.Object3D;
|
|
25
25
|
activityEffects: AgentActivityEffects;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const agentColors = [0x2563eb, 0xdc2626, 0x059669
|
|
29
|
-
const skinColors = [0xffdbac,
|
|
30
|
-
const hairColors = [0x3f2a1d, 0x111827
|
|
28
|
+
const agentColors = [0x2563eb, 0xdc2626, 0x059669];
|
|
29
|
+
const skinColors = [0xffdbac, 0xd4a574];
|
|
30
|
+
const hairColors = [0x3f2a1d, 0x111827];
|
|
31
31
|
export const AGENT_MESH_SCALE = 1.15;
|
|
32
|
-
const activityGlowGeometry = new THREE.RingGeometry(0.56, 0.92, 32);
|
|
33
|
-
const activityGlowMaterial = new THREE.MeshBasicMaterial({
|
|
34
|
-
color: 0x38bdf8,
|
|
35
|
-
transparent: true,
|
|
36
|
-
opacity: 0.35,
|
|
37
|
-
side: THREE.DoubleSide,
|
|
38
|
-
depthWrite: false,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
32
|
export type AgentBodyPartKey =
|
|
42
33
|
| "body"
|
|
43
34
|
| "leftLeg"
|
|
@@ -133,11 +124,8 @@ function addPart(
|
|
|
133
124
|
return part;
|
|
134
125
|
}
|
|
135
126
|
|
|
136
|
-
function createActivityGlow(): THREE.
|
|
137
|
-
const glow = new THREE.
|
|
138
|
-
activityGlowGeometry,
|
|
139
|
-
activityGlowMaterial,
|
|
140
|
-
);
|
|
127
|
+
function createActivityGlow(): THREE.Object3D {
|
|
128
|
+
const glow = new THREE.Object3D();
|
|
141
129
|
glow.rotation.x = -Math.PI / 2;
|
|
142
130
|
glow.position.y = 0.03;
|
|
143
131
|
glow.visible = false;
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from "../../core/types";
|
|
10
10
|
import { applyAgentPose, updateAgentMotion } from "./agent-animation";
|
|
11
11
|
import { createAgentBodyInstancedLayer } from "./agent-body-instancing";
|
|
12
|
+
import { createAgentEffectInstancedLayer } from "./agent-effect-instancing";
|
|
12
13
|
import { resolveAgentFacingTarget, resolveAgentPosition } from "./agent-layout";
|
|
13
14
|
import {
|
|
14
15
|
type AgentLabelRecord,
|
|
@@ -113,6 +114,7 @@ export function mountThreeAgentGameOffice(
|
|
|
113
114
|
addLights(scene);
|
|
114
115
|
buildOfficeScene(scene);
|
|
115
116
|
const agentBodyLayer = createAgentBodyInstancedLayer(scene);
|
|
117
|
+
const agentEffectLayer = createAgentEffectInstancedLayer(scene);
|
|
116
118
|
|
|
117
119
|
const agents = new Map<string, AgentMeshRecord>();
|
|
118
120
|
let frameId = 0;
|
|
@@ -193,6 +195,10 @@ export function mountThreeAgentGameOffice(
|
|
|
193
195
|
renderIndex: record.renderIndex,
|
|
194
196
|
visible: record.agent.sceneState !== "offline",
|
|
195
197
|
})));
|
|
198
|
+
agentEffectLayer.update(Array.from(agents.values()).map((record) => ({
|
|
199
|
+
mesh: record.mesh,
|
|
200
|
+
visible: record.agent.sceneState !== "offline",
|
|
201
|
+
})));
|
|
196
202
|
renderer.render(scene, camera);
|
|
197
203
|
labelsDirty = false;
|
|
198
204
|
frameId = requestAnimationFrame(animate);
|
|
@@ -245,6 +251,7 @@ export function mountThreeAgentGameOffice(
|
|
|
245
251
|
});
|
|
246
252
|
agents.clear();
|
|
247
253
|
agentBodyLayer.dispose();
|
|
254
|
+
agentEffectLayer.dispose();
|
|
248
255
|
disposeObject(scene);
|
|
249
256
|
renderer.dispose();
|
|
250
257
|
root.remove();
|
package/src/runtime-client.ts
CHANGED
|
@@ -158,7 +158,7 @@ export class AgentGameRuntimeBrowserClient {
|
|
|
158
158
|
signal: options.signal,
|
|
159
159
|
});
|
|
160
160
|
if (!response.ok) {
|
|
161
|
-
throw new Error(
|
|
161
|
+
throw new Error(await formatGameRuntimeTokenRequestError(response));
|
|
162
162
|
}
|
|
163
163
|
const body = await response.json() as GameRuntimeTokenResponse;
|
|
164
164
|
if (!isGameRuntimeTokenResponse(body)) {
|
|
@@ -217,7 +217,7 @@ export class AgentGameRuntimeServerClient {
|
|
|
217
217
|
signal: options.signal,
|
|
218
218
|
});
|
|
219
219
|
if (!response.ok) {
|
|
220
|
-
throw new Error(
|
|
220
|
+
throw new Error(await formatGameRuntimeTokenRequestError(response));
|
|
221
221
|
}
|
|
222
222
|
const body = await response.json() as GameRuntimeTokenResponse;
|
|
223
223
|
if (!isGameRuntimeTokenResponse(body)) {
|
|
@@ -377,6 +377,31 @@ function boundFetch(): typeof fetch {
|
|
|
377
377
|
return globalThis.fetch.bind(globalThis);
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
async function formatGameRuntimeTokenRequestError(response: Response): Promise<string> {
|
|
381
|
+
const detail = await readErrorDetail(response);
|
|
382
|
+
return `Game runtime token request failed with status ${response.status}${detail ? `: ${detail}` : ""}.`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function readErrorDetail(response: Response): Promise<string> {
|
|
386
|
+
try {
|
|
387
|
+
const body = await response.clone().json() as unknown;
|
|
388
|
+
if (!body || typeof body !== "object") {
|
|
389
|
+
return "";
|
|
390
|
+
}
|
|
391
|
+
const error = (body as { error?: unknown }).error;
|
|
392
|
+
if (typeof error === "string") {
|
|
393
|
+
return error;
|
|
394
|
+
}
|
|
395
|
+
if (error && typeof error === "object") {
|
|
396
|
+
const message = (error as { message?: unknown }).message;
|
|
397
|
+
return typeof message === "string" ? message : "";
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
return "";
|
|
401
|
+
}
|
|
402
|
+
return "";
|
|
403
|
+
}
|
|
404
|
+
|
|
380
405
|
function resolveHeaders(
|
|
381
406
|
value: AgentGameRuntimeBrowserClientOptions["headers"] | AgentGameRuntimeServerClientOptions["headers"],
|
|
382
407
|
): HeadersInit | undefined {
|