@agent-os-lab/agent-game-sdk 0.1.3 → 0.1.4
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 +33 -5
- package/USAGE.md +37 -7
- package/package.json +1 -1
- package/src/office/core/types.ts +7 -0
- package/src/office/index.ts +1 -0
- package/src/office/layout/config.ts +76 -0
- package/src/office/layout/index.ts +2 -0
- package/src/office/layout/resolver.ts +978 -0
- package/src/office/mount.ts +6 -0
- package/src/office/react/AgentGameOfficeView.ts +25 -1
- package/src/office/renderers/three/agent-body-instancing.ts +1 -0
- package/src/office/renderers/three/agent-effect-instancing.ts +1 -0
- package/src/office/renderers/three/agent-layout.ts +12 -56
- package/src/office/renderers/three/mount.ts +8 -6
- package/src/office/renderers/three/scene.ts +256 -84
- package/src/runtime-client.ts +10 -0
package/README.md
CHANGED
|
@@ -20,6 +20,13 @@ const client = new AgentGameRuntimeBrowserClient({
|
|
|
20
20
|
|
|
21
21
|
const view = await mountAgentGameOffice(container, {
|
|
22
22
|
renderer: "three",
|
|
23
|
+
office: {
|
|
24
|
+
rooms: [
|
|
25
|
+
{ type: "office" },
|
|
26
|
+
{ type: "auditorium", capacity: 40 },
|
|
27
|
+
{ type: "gym" },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
23
30
|
source: {
|
|
24
31
|
type: "runtime",
|
|
25
32
|
client,
|
|
@@ -27,13 +34,16 @@ const view = await mountAgentGameOffice(container, {
|
|
|
27
34
|
});
|
|
28
35
|
|
|
29
36
|
view.focusAgent("agent-1");
|
|
37
|
+
view.refreshAgents();
|
|
30
38
|
view.destroy();
|
|
31
39
|
```
|
|
32
40
|
|
|
33
41
|
React wrapper:
|
|
34
42
|
|
|
35
43
|
```tsx
|
|
44
|
+
import { useState } from "react";
|
|
36
45
|
import { AgentGameRuntimeBrowserClient } from "@agent-os-lab/agent-game-sdk";
|
|
46
|
+
import type { AgentGameOfficeController } from "@agent-os-lab/agent-game-sdk/office";
|
|
37
47
|
import { AgentGameOfficeView } from "@agent-os-lab/agent-game-sdk/office/react";
|
|
38
48
|
|
|
39
49
|
const client = new AgentGameRuntimeBrowserClient({
|
|
@@ -41,18 +51,36 @@ const client = new AgentGameRuntimeBrowserClient({
|
|
|
41
51
|
});
|
|
42
52
|
|
|
43
53
|
export function Office() {
|
|
54
|
+
const [view, setView] = useState<AgentGameOfficeController | null>(null);
|
|
55
|
+
|
|
44
56
|
return (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
<>
|
|
58
|
+
<button type="button" onClick={() => view?.refreshAgents()}>
|
|
59
|
+
Refresh agents
|
|
60
|
+
</button>
|
|
61
|
+
<AgentGameOfficeView
|
|
62
|
+
renderer="three"
|
|
63
|
+
office={{
|
|
64
|
+
rooms: [
|
|
65
|
+
{ type: "office" },
|
|
66
|
+
{ type: "auditorium" },
|
|
67
|
+
{ type: "gym" },
|
|
68
|
+
],
|
|
69
|
+
}}
|
|
70
|
+
source={{ type: "runtime", client }}
|
|
71
|
+
style={{ height: 640 }}
|
|
72
|
+
onReady={setView}
|
|
73
|
+
onDestroy={() => setView(null)}
|
|
74
|
+
/>
|
|
75
|
+
</>
|
|
50
76
|
);
|
|
51
77
|
}
|
|
52
78
|
```
|
|
53
79
|
|
|
54
80
|
`agent-game-sdk/office` is renderer and framework neutral. `agent-game-sdk/office/react` is the React-only entrypoint.
|
|
55
81
|
|
|
82
|
+
The built-in 3D renderer supports semantic room configuration. If `office` is omitted, the SDK renders the default `office + auditorium + gym` layout. Consumers choose room types and optional capacity; component coordinates and Three.js objects stay internal to the SDK.
|
|
83
|
+
|
|
56
84
|
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:
|
|
57
85
|
|
|
58
86
|
```ts
|
package/USAGE.md
CHANGED
|
@@ -146,6 +146,13 @@ const client = new AgentGameRuntimeBrowserClient({
|
|
|
146
146
|
|
|
147
147
|
const view = await mountAgentGameOffice(container, {
|
|
148
148
|
renderer: "three",
|
|
149
|
+
office: {
|
|
150
|
+
rooms: [
|
|
151
|
+
{ type: "office", capacity: 12 },
|
|
152
|
+
{ type: "auditorium", capacity: 40 },
|
|
153
|
+
{ type: "gym", capacity: 8 },
|
|
154
|
+
],
|
|
155
|
+
},
|
|
149
156
|
source: {
|
|
150
157
|
type: "runtime",
|
|
151
158
|
client,
|
|
@@ -153,17 +160,24 @@ const view = await mountAgentGameOffice(container, {
|
|
|
153
160
|
});
|
|
154
161
|
|
|
155
162
|
view.focusAgent("agent-1");
|
|
163
|
+
view.refreshAgents();
|
|
156
164
|
view.destroy();
|
|
157
165
|
```
|
|
158
166
|
|
|
159
167
|
`renderer: "three"` is the built-in renderer and the default renderer. The container must have a stable size; the React wrapper supplies a default `minHeight`, but framework-neutral callers should size the container in CSS.
|
|
160
168
|
|
|
169
|
+
`office.rooms` configures the 3D office by semantic room type. The built-in room types are `office`, `auditorium`, and `gym`. If `office` is omitted, the SDK renders all three rooms. `capacity` is optional and influences the generated anchors used for agent placement. Do not configure desks, walls, whiteboards, fitness equipment, or raw coordinates from application code; those components remain SDK-managed presets.
|
|
170
|
+
|
|
171
|
+
`refreshAgents` asks Agent Game Runtime to refresh the tenant roster through the existing bootstrap flow. It is intended for UI controls such as a refresh button after an Agent was created or deleted in another surface.
|
|
172
|
+
|
|
161
173
|
## React Office View
|
|
162
174
|
|
|
163
175
|
Use `AgentGameOfficeView` when embedding the office view in a React application.
|
|
164
176
|
|
|
165
177
|
```tsx
|
|
178
|
+
import { useState } from "react";
|
|
166
179
|
import { AgentGameRuntimeBrowserClient } from "@agent-os-lab/agent-game-sdk";
|
|
180
|
+
import type { AgentGameOfficeController } from "@agent-os-lab/agent-game-sdk/office";
|
|
167
181
|
import { AgentGameOfficeView } from "@agent-os-lab/agent-game-sdk/office/react";
|
|
168
182
|
|
|
169
183
|
const client = new AgentGameRuntimeBrowserClient({
|
|
@@ -171,18 +185,34 @@ const client = new AgentGameRuntimeBrowserClient({
|
|
|
171
185
|
});
|
|
172
186
|
|
|
173
187
|
export function Office() {
|
|
188
|
+
const [view, setView] = useState<AgentGameOfficeController | null>(null);
|
|
189
|
+
|
|
174
190
|
return (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
191
|
+
<>
|
|
192
|
+
<button type="button" onClick={() => view?.refreshAgents()}>
|
|
193
|
+
Refresh agents
|
|
194
|
+
</button>
|
|
195
|
+
<AgentGameOfficeView
|
|
196
|
+
renderer="three"
|
|
197
|
+
office={{
|
|
198
|
+
rooms: [
|
|
199
|
+
{ type: "office" },
|
|
200
|
+
{ type: "auditorium" },
|
|
201
|
+
{ type: "gym" },
|
|
202
|
+
],
|
|
203
|
+
}}
|
|
204
|
+
source={{ type: "runtime", client }}
|
|
205
|
+
focusedAgentId="agent-1"
|
|
206
|
+
style={{ height: 640 }}
|
|
207
|
+
onReady={setView}
|
|
208
|
+
onDestroy={() => setView(null)}
|
|
209
|
+
/>
|
|
210
|
+
</>
|
|
181
211
|
);
|
|
182
212
|
}
|
|
183
213
|
```
|
|
184
214
|
|
|
185
|
-
Keep `source` object identity stable across renders when possible. Recreating `source` every render can remount the view because the React wrapper treats
|
|
215
|
+
Keep `source` object identity stable across renders when possible. Recreating `source` every render can remount the view because the React wrapper treats it as an effect dependency. Equivalent inline `office` objects do not remount the view, but memoizing office config is still preferable when constructing it dynamically.
|
|
186
216
|
|
|
187
217
|
## Snapshot Source
|
|
188
218
|
|
package/package.json
CHANGED
package/src/office/core/types.ts
CHANGED
|
@@ -5,6 +5,10 @@ import type {
|
|
|
5
5
|
AgentPresenceStatus,
|
|
6
6
|
GameRuntimeSubscription,
|
|
7
7
|
} from "../../runtime-client";
|
|
8
|
+
import type {
|
|
9
|
+
AgentGameOfficeConfig,
|
|
10
|
+
ResolvedOfficeLayout,
|
|
11
|
+
} from "../layout";
|
|
8
12
|
|
|
9
13
|
export type {
|
|
10
14
|
AgentActivityDisplay,
|
|
@@ -95,16 +99,19 @@ export type AgentGameOfficeRenderer = {
|
|
|
95
99
|
export type AgentGameOfficeMountOptions = {
|
|
96
100
|
renderer?: AgentGameOfficeRendererKind | AgentGameOfficeRenderer;
|
|
97
101
|
source: AgentGameOfficeSourceInput;
|
|
102
|
+
office?: AgentGameOfficeConfig;
|
|
98
103
|
focusedAgentId?: string | null;
|
|
99
104
|
className?: string;
|
|
100
105
|
};
|
|
101
106
|
|
|
102
107
|
export type AgentGameOfficeResolvedOptions = Omit<AgentGameOfficeMountOptions, "renderer"> & {
|
|
103
108
|
renderer: AgentGameOfficeRendererKind | AgentGameOfficeRenderer;
|
|
109
|
+
officeLayout: ResolvedOfficeLayout;
|
|
104
110
|
};
|
|
105
111
|
|
|
106
112
|
export type AgentGameOfficeController = {
|
|
107
113
|
updateAgents(agents: AgentPresence[]): void;
|
|
108
114
|
focusAgent(agentId: string | null): void;
|
|
115
|
+
refreshAgents(): void;
|
|
109
116
|
destroy(): void;
|
|
110
117
|
};
|
package/src/office/index.ts
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export type AgentGameOfficeRoomType = "office" | "auditorium" | "gym";
|
|
2
|
+
|
|
3
|
+
export type AgentGameOfficeRoomConfig = {
|
|
4
|
+
type: AgentGameOfficeRoomType;
|
|
5
|
+
capacity?: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type AgentGameOfficeConfig = {
|
|
9
|
+
rooms: AgentGameOfficeRoomConfig[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type NormalizedOfficeRoomConfig = {
|
|
13
|
+
id: string;
|
|
14
|
+
type: AgentGameOfficeRoomType;
|
|
15
|
+
capacity: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type NormalizedOfficeConfig = {
|
|
19
|
+
rooms: NormalizedOfficeRoomConfig[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const DEFAULT_ROOM_CAPACITY = {
|
|
23
|
+
office: 12,
|
|
24
|
+
auditorium: 40,
|
|
25
|
+
gym: 8,
|
|
26
|
+
} satisfies Record<AgentGameOfficeRoomType, number>;
|
|
27
|
+
|
|
28
|
+
const MAX_ROOM_CAPACITY = {
|
|
29
|
+
office: 48,
|
|
30
|
+
auditorium: 120,
|
|
31
|
+
gym: 24,
|
|
32
|
+
} satisfies Record<AgentGameOfficeRoomType, number>;
|
|
33
|
+
|
|
34
|
+
export const DEFAULT_OFFICE_CONFIG: AgentGameOfficeConfig = {
|
|
35
|
+
rooms: [
|
|
36
|
+
{ type: "office" },
|
|
37
|
+
{ type: "auditorium" },
|
|
38
|
+
{ type: "gym" },
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function normalizeOfficeConfig(
|
|
43
|
+
config: AgentGameOfficeConfig | undefined = DEFAULT_OFFICE_CONFIG,
|
|
44
|
+
): NormalizedOfficeConfig {
|
|
45
|
+
if (!Array.isArray(config.rooms) || config.rooms.length === 0) {
|
|
46
|
+
throw new Error("Agent game office config requires at least one room");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const counts = new Map<AgentGameOfficeRoomType, number>();
|
|
50
|
+
return {
|
|
51
|
+
rooms: config.rooms.map((room) => {
|
|
52
|
+
assertRoomType(room.type);
|
|
53
|
+
const nextCount = (counts.get(room.type) ?? 0) + 1;
|
|
54
|
+
counts.set(room.type, nextCount);
|
|
55
|
+
return {
|
|
56
|
+
id: `${room.type}-${nextCount}`,
|
|
57
|
+
type: room.type,
|
|
58
|
+
capacity: normalizeRoomCapacity(room.type, room.capacity),
|
|
59
|
+
};
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function assertRoomType(value: string): asserts value is AgentGameOfficeRoomType {
|
|
65
|
+
if (value !== "office" && value !== "auditorium" && value !== "gym") {
|
|
66
|
+
throw new Error(`Unsupported agent game office room type: ${value}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeRoomCapacity(type: AgentGameOfficeRoomType, capacity: number | undefined): number {
|
|
71
|
+
const value = capacity ?? DEFAULT_ROOM_CAPACITY[type];
|
|
72
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
73
|
+
throw new Error(`Agent game office room ${type} capacity must be a positive integer`);
|
|
74
|
+
}
|
|
75
|
+
return Math.min(value, MAX_ROOM_CAPACITY[type]);
|
|
76
|
+
}
|