@agent-os-lab/agent-game-sdk 0.1.3 → 0.1.5
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 +44 -5
- package/USAGE.md +45 -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,47 @@ 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. To choose visible rooms, pass `office.rooms` in the order you want them assembled:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
office: {
|
|
86
|
+
rooms: [
|
|
87
|
+
{ type: "office" },
|
|
88
|
+
{ type: "gym" },
|
|
89
|
+
],
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Built-in room types are `office`, `auditorium`, and `gym`. `capacity` is optional and affects generated agent placement anchors; desks, walls, meeting rooms, gym equipment, windows, and raw coordinates stay internal to the SDK.
|
|
94
|
+
|
|
56
95
|
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
96
|
|
|
58
97
|
```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,32 @@ 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. If `office.rooms` is provided, only those rooms render, in the provided order:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
office: {
|
|
173
|
+
rooms: [{ type: "office" }, { type: "gym" }],
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The layout packs up to three rooms per row, then starts another row. `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.
|
|
178
|
+
|
|
179
|
+
`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.
|
|
180
|
+
|
|
161
181
|
## React Office View
|
|
162
182
|
|
|
163
183
|
Use `AgentGameOfficeView` when embedding the office view in a React application.
|
|
164
184
|
|
|
165
185
|
```tsx
|
|
186
|
+
import { useState } from "react";
|
|
166
187
|
import { AgentGameRuntimeBrowserClient } from "@agent-os-lab/agent-game-sdk";
|
|
188
|
+
import type { AgentGameOfficeController } from "@agent-os-lab/agent-game-sdk/office";
|
|
167
189
|
import { AgentGameOfficeView } from "@agent-os-lab/agent-game-sdk/office/react";
|
|
168
190
|
|
|
169
191
|
const client = new AgentGameRuntimeBrowserClient({
|
|
@@ -171,18 +193,34 @@ const client = new AgentGameRuntimeBrowserClient({
|
|
|
171
193
|
});
|
|
172
194
|
|
|
173
195
|
export function Office() {
|
|
196
|
+
const [view, setView] = useState<AgentGameOfficeController | null>(null);
|
|
197
|
+
|
|
174
198
|
return (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
199
|
+
<>
|
|
200
|
+
<button type="button" onClick={() => view?.refreshAgents()}>
|
|
201
|
+
Refresh agents
|
|
202
|
+
</button>
|
|
203
|
+
<AgentGameOfficeView
|
|
204
|
+
renderer="three"
|
|
205
|
+
office={{
|
|
206
|
+
rooms: [
|
|
207
|
+
{ type: "office" },
|
|
208
|
+
{ type: "auditorium" },
|
|
209
|
+
{ type: "gym" },
|
|
210
|
+
],
|
|
211
|
+
}}
|
|
212
|
+
source={{ type: "runtime", client }}
|
|
213
|
+
focusedAgentId="agent-1"
|
|
214
|
+
style={{ height: 640 }}
|
|
215
|
+
onReady={setView}
|
|
216
|
+
onDestroy={() => setView(null)}
|
|
217
|
+
/>
|
|
218
|
+
</>
|
|
181
219
|
);
|
|
182
220
|
}
|
|
183
221
|
```
|
|
184
222
|
|
|
185
|
-
Keep `source` object identity stable across renders when possible. Recreating `source` every render can remount the view because the React wrapper treats
|
|
223
|
+
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
224
|
|
|
187
225
|
## Snapshot Source
|
|
188
226
|
|
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
|
+
}
|