@iniesta8888/agent-live 0.3.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.
Files changed (182) hide show
  1. package/CODE_OF_CONDUCT.md +7 -0
  2. package/CONTRIBUTING.md +56 -0
  3. package/LICENSE +21 -0
  4. package/README.md +92 -0
  5. package/SECURITY.md +13 -0
  6. package/dist/adapter-sdk/index.d.ts +45 -0
  7. package/dist/adapter-sdk/index.d.ts.map +1 -0
  8. package/dist/adapter-sdk/index.js +106 -0
  9. package/dist/adapter-sdk/index.js.map +1 -0
  10. package/dist/content/compiler.d.ts +31 -0
  11. package/dist/content/compiler.d.ts.map +1 -0
  12. package/dist/content/compiler.js +218 -0
  13. package/dist/content/compiler.js.map +1 -0
  14. package/dist/content/graph-validator.d.ts +56 -0
  15. package/dist/content/graph-validator.d.ts.map +1 -0
  16. package/dist/content/graph-validator.js +226 -0
  17. package/dist/content/graph-validator.js.map +1 -0
  18. package/dist/content/library.d.ts +4 -0
  19. package/dist/content/library.d.ts.map +1 -0
  20. package/dist/content/library.js +45 -0
  21. package/dist/content/library.js.map +1 -0
  22. package/dist/content/registry.d.ts +34 -0
  23. package/dist/content/registry.d.ts.map +1 -0
  24. package/dist/content/registry.js +130 -0
  25. package/dist/content/registry.js.map +1 -0
  26. package/dist/content/runtime-content.d.ts +63 -0
  27. package/dist/content/runtime-content.d.ts.map +1 -0
  28. package/dist/content/runtime-content.js +78 -0
  29. package/dist/content/runtime-content.js.map +1 -0
  30. package/dist/content/schema.d.ts +142 -0
  31. package/dist/content/schema.d.ts.map +1 -0
  32. package/dist/content/schema.js +280 -0
  33. package/dist/content/schema.js.map +1 -0
  34. package/dist/content/validator.d.ts +35 -0
  35. package/dist/content/validator.d.ts.map +1 -0
  36. package/dist/content/validator.js +205 -0
  37. package/dist/content/validator.js.map +1 -0
  38. package/dist/core/agents.d.ts +56 -0
  39. package/dist/core/agents.d.ts.map +1 -0
  40. package/dist/core/agents.js +148 -0
  41. package/dist/core/agents.js.map +1 -0
  42. package/dist/core/limits.d.ts +15 -0
  43. package/dist/core/limits.d.ts.map +1 -0
  44. package/dist/core/limits.js +16 -0
  45. package/dist/core/limits.js.map +1 -0
  46. package/dist/core/mapping.d.ts +17 -0
  47. package/dist/core/mapping.d.ts.map +1 -0
  48. package/dist/core/mapping.js +94 -0
  49. package/dist/core/mapping.js.map +1 -0
  50. package/dist/core/protocol.d.ts +105 -0
  51. package/dist/core/protocol.d.ts.map +1 -0
  52. package/dist/core/protocol.js +6 -0
  53. package/dist/core/protocol.js.map +1 -0
  54. package/dist/core/state.d.ts +49 -0
  55. package/dist/core/state.d.ts.map +1 -0
  56. package/dist/core/state.js +216 -0
  57. package/dist/core/state.js.map +1 -0
  58. package/dist/creator/commands.d.ts +19 -0
  59. package/dist/creator/commands.d.ts.map +1 -0
  60. package/dist/creator/commands.js +44 -0
  61. package/dist/creator/commands.js.map +1 -0
  62. package/dist/creator/mode.d.ts +10 -0
  63. package/dist/creator/mode.d.ts.map +1 -0
  64. package/dist/creator/mode.js +23 -0
  65. package/dist/creator/mode.js.map +1 -0
  66. package/dist/creator/service.d.ts +199 -0
  67. package/dist/creator/service.d.ts.map +1 -0
  68. package/dist/creator/service.js +159 -0
  69. package/dist/creator/service.js.map +1 -0
  70. package/dist/index.d.ts +14 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +10 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/runtime/agent-live-runtime.d.ts +17 -0
  75. package/dist/runtime/agent-live-runtime.d.ts.map +1 -0
  76. package/dist/runtime/agent-live-runtime.js +50 -0
  77. package/dist/runtime/agent-live-runtime.js.map +1 -0
  78. package/dist/runtime/content-service.d.ts +75 -0
  79. package/dist/runtime/content-service.d.ts.map +1 -0
  80. package/dist/runtime/content-service.js +60 -0
  81. package/dist/runtime/content-service.js.map +1 -0
  82. package/dist/runtime/resource-guard.d.ts +17 -0
  83. package/dist/runtime/resource-guard.d.ts.map +1 -0
  84. package/dist/runtime/resource-guard.js +54 -0
  85. package/dist/runtime/resource-guard.js.map +1 -0
  86. package/dist/runtime/server.d.ts +39 -0
  87. package/dist/runtime/server.d.ts.map +1 -0
  88. package/dist/runtime/server.js +286 -0
  89. package/dist/runtime/server.js.map +1 -0
  90. package/dist/web/app.d.ts +1 -0
  91. package/dist/web/app.js +1317 -0
  92. package/dist/web/index.html +45 -0
  93. package/dist/web/office.js +350 -0
  94. package/dist/web/sprites.js +185 -0
  95. package/dist/web/style.css +288 -0
  96. package/dist/web/v2/bootstrap.js +276 -0
  97. package/dist/web/v2/content/README.md +17 -0
  98. package/dist/web/v2/content/agent-skins/studio-team.json +33 -0
  99. package/dist/web/v2/content/agent-skins/tiny-developers.json +33 -0
  100. package/dist/web/v2/content/atmospheres/default.json +13 -0
  101. package/dist/web/v2/content/atmospheres/rainy-night.json +37 -0
  102. package/dist/web/v2/content/catalog.json +14 -0
  103. package/dist/web/v2/content/component-library/activity-implementations.json +873 -0
  104. package/dist/web/v2/content/component-library/activity-recipes.json +25 -0
  105. package/dist/web/v2/content/component-library/agent-profile-templates.json +18 -0
  106. package/dist/web/v2/content/component-library/catalog.json +38 -0
  107. package/dist/web/v2/content/component-library/npc-templates.json +42 -0
  108. package/dist/web/v2/content/component-library/props.json +41 -0
  109. package/dist/web/v2/content/environments/local-office.json +31 -0
  110. package/dist/web/v2/content/environments/rainy-night.json +28 -0
  111. package/dist/web/v2/content/environments/static-office.json +27 -0
  112. package/dist/web/v2/content/layouts/boardroom-office.json +122 -0
  113. package/dist/web/v2/content/layouts/demo-office.json +88 -0
  114. package/dist/web/v2/content/layouts/lively-office.json +96 -0
  115. package/dist/web/v2/content/layouts/old-school-office.json +154 -0
  116. package/dist/web/v2/content/layouts/tech-open-office.json +132 -0
  117. package/dist/web/v2/content/life-activities/boardroom-routines.json +67 -0
  118. package/dist/web/v2/content/life-activities/none.json +10 -0
  119. package/dist/web/v2/content/life-activities/office-basics.json +39 -0
  120. package/dist/web/v2/content/life-activities/old-school-routines.json +74 -0
  121. package/dist/web/v2/content/life-activities/tech-office-routines.json +148 -0
  122. package/dist/web/v2/content/npcs/boardroom-staff.json +54 -0
  123. package/dist/web/v2/content/npcs/none.json +10 -0
  124. package/dist/web/v2/content/npcs/office-staff.json +25 -0
  125. package/dist/web/v2/content/npcs/old-school-staff.json +39 -0
  126. package/dist/web/v2/content/npcs/tech-office-staff.json +40 -0
  127. package/dist/web/v2/content/official-offices/boardroom-office.json +25 -0
  128. package/dist/web/v2/content/official-offices/old-school-office.json +25 -0
  129. package/dist/web/v2/content/official-offices/tech-open-office.json +25 -0
  130. package/dist/web/v2/content/presets/boardroom-office.json +21 -0
  131. package/dist/web/v2/content/presets/cozy-studio.json +19 -0
  132. package/dist/web/v2/content/presets/demo-office.json +19 -0
  133. package/dist/web/v2/content/presets/lively-office.json +19 -0
  134. package/dist/web/v2/content/presets/night-shift.json +19 -0
  135. package/dist/web/v2/content/presets/old-school-office.json +20 -0
  136. package/dist/web/v2/content/presets/refined-demo.json +20 -0
  137. package/dist/web/v2/content/presets/tech-open-office.json +21 -0
  138. package/dist/web/v2/content/props/default-office.json +23 -0
  139. package/dist/web/v2/content/props/extended-office.json +39 -0
  140. package/dist/web/v2/content/props/tech-office.json +28 -0
  141. package/dist/web/v2/content/styles/pixel-classic.json +160 -0
  142. package/dist/web/v2/content/styles/warm-studio.json +150 -0
  143. package/dist/web/v2/environment-runtime.d.ts +1 -0
  144. package/dist/web/v2/environment-runtime.js +138 -0
  145. package/dist/web/v2/graph-validator.d.ts +55 -0
  146. package/dist/web/v2/graph-validator.js +225 -0
  147. package/dist/web/v2/i18n.js +54 -0
  148. package/dist/web/v2/locales/en.json +209 -0
  149. package/dist/web/v2/locales/zh-CN.json +52 -0
  150. package/dist/web/v2/office-engine.d.ts +1 -0
  151. package/dist/web/v2/office-engine.js +145 -0
  152. package/dist/web/v2/office-renderer.d.ts +1 -0
  153. package/dist/web/v2/office-renderer.js +843 -0
  154. package/dist/web/v2/parity.html +10 -0
  155. package/dist/web/v2/parity.js +82 -0
  156. package/dist/web/v2/sprite-renderer.d.ts +1 -0
  157. package/dist/web/v2/sprite-renderer.js +242 -0
  158. package/dist/web/v2/static-content.js +48 -0
  159. package/dist/web/v2/style.css +140 -0
  160. package/dist/web/v2.html +68 -0
  161. package/docs/ADAPTER-DEVELOPMENT.md +328 -0
  162. package/docs/ADAPTER-SDK.md +109 -0
  163. package/docs/ARCHITECTURE.md +223 -0
  164. package/docs/COMPONENT-LIBRARY.md +212 -0
  165. package/docs/CUSTOMIZATION.md +163 -0
  166. package/docs/DEVELOPER.md +186 -0
  167. package/docs/DSH-ADAPTER-DESIGN.md +173 -0
  168. package/docs/OFFICE-THEMES.md +516 -0
  169. package/docs/PRESET-CONFIG.md +460 -0
  170. package/docs/RELEASE-CHECK.md +23 -0
  171. package/docs/TECHNICAL.md +515 -0
  172. package/docs/USING-CODEX.md +24 -0
  173. package/docs/USING-DSH.md +44 -0
  174. package/docs/USING-PI.md +50 -0
  175. package/docs/VISION.md +146 -0
  176. package/examples/adapter/local-office.mjs +52 -0
  177. package/package.json +89 -0
  178. package/skills/agent-live-adapter-builder/SKILL.md +30 -0
  179. package/skills/agent-live-adapter-builder/agents/openai.yaml +4 -0
  180. package/skills/agent-live-adapter-builder/references/investigation.md +9 -0
  181. package/skills/agent-live-adapter-builder/references/review.md +7 -0
  182. package/skills/agent-live-adapter-builder/scripts/create-adapter.mjs +26 -0
@@ -0,0 +1,288 @@
1
+ :root {
2
+ --bg: #0d1017;
3
+ --panel: #151a24;
4
+ --panel-2: #1c2230;
5
+ --line: #2a3243;
6
+ --ink: #d7dee9;
7
+ --dim: #7c8798;
8
+ --accent: #ffcc4d;
9
+ --ok: #6ee7a8;
10
+ --warn: #ff8f6b;
11
+ --think: #8fb8ff;
12
+ font-family: ui-monospace, SFMono-Regular, Menlo, "PingFang SC", monospace;
13
+ }
14
+
15
+ * {
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ html,
20
+ body {
21
+ margin: 0;
22
+ height: 100%;
23
+ background: var(--bg);
24
+ color: var(--ink);
25
+ overflow: hidden;
26
+ }
27
+
28
+ #app {
29
+ display: flex;
30
+ flex-direction: column;
31
+ height: 100%;
32
+ }
33
+
34
+ #bar {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 8px;
38
+ padding: 8px 12px;
39
+ background: var(--panel);
40
+ border-bottom: 2px solid var(--line);
41
+ font-size: 12px;
42
+ }
43
+
44
+ .brand {
45
+ color: var(--accent);
46
+ letter-spacing: 2px;
47
+ font-weight: 700;
48
+ }
49
+
50
+ .grow {
51
+ flex: 1;
52
+ }
53
+
54
+ .pill {
55
+ padding: 2px 8px;
56
+ border: 1px solid var(--line);
57
+ border-radius: 2px;
58
+ color: var(--dim);
59
+ white-space: nowrap;
60
+ }
61
+
62
+ .pill.online {
63
+ color: var(--ok);
64
+ border-color: #2c5c45;
65
+ }
66
+
67
+ .pill.offline {
68
+ color: var(--warn);
69
+ border-color: #5c3a2c;
70
+ }
71
+
72
+ .btn {
73
+ background: var(--panel-2);
74
+ color: var(--ink);
75
+ border: 1px solid var(--line);
76
+ border-radius: 2px;
77
+ padding: 3px 10px;
78
+ font: inherit;
79
+ font-size: 12px;
80
+ cursor: pointer;
81
+ }
82
+
83
+ .btn:hover {
84
+ border-color: var(--accent);
85
+ color: var(--accent);
86
+ }
87
+
88
+ main {
89
+ display: flex;
90
+ flex: 1;
91
+ min-height: 0;
92
+ }
93
+
94
+ #stageWrap {
95
+ position: relative;
96
+ flex: 1;
97
+ min-width: 0;
98
+ background: #07090d;
99
+ }
100
+
101
+ #stage {
102
+ display: block;
103
+ width: 100%;
104
+ height: 100%;
105
+ image-rendering: pixelated;
106
+ }
107
+
108
+ #taskbar {
109
+ position: absolute;
110
+ left: 12px;
111
+ right: 12px;
112
+ bottom: 10px;
113
+ display: flex;
114
+ gap: 8px;
115
+ align-items: baseline;
116
+ padding: 6px 10px;
117
+ background: rgba(10, 13, 19, 0.86);
118
+ border: 1px solid var(--line);
119
+ font-size: 12px;
120
+ }
121
+
122
+ #taskbar .k {
123
+ color: var(--accent);
124
+ white-space: nowrap;
125
+ }
126
+
127
+ #task {
128
+ color: var(--ink);
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ white-space: nowrap;
132
+ }
133
+
134
+ #side {
135
+ width: 330px;
136
+ display: flex;
137
+ flex-direction: column;
138
+ border-left: 2px solid var(--line);
139
+ background: var(--panel);
140
+ min-height: 0;
141
+ }
142
+
143
+ .panel {
144
+ display: flex;
145
+ flex-direction: column;
146
+ min-height: 0;
147
+ border-bottom: 1px solid var(--line);
148
+ }
149
+
150
+ .panel.grow {
151
+ flex: 1;
152
+ }
153
+
154
+ .panel h2 {
155
+ margin: 0;
156
+ padding: 7px 10px;
157
+ font-size: 11px;
158
+ letter-spacing: 1px;
159
+ color: var(--dim);
160
+ text-transform: uppercase;
161
+ background: var(--panel-2);
162
+ }
163
+
164
+ .crew {
165
+ overflow-y: auto;
166
+ max-height: 40vh;
167
+ }
168
+
169
+ .card {
170
+ display: flex;
171
+ gap: 8px;
172
+ align-items: flex-start;
173
+ padding: 8px 10px;
174
+ border-bottom: 1px solid #1e2431;
175
+ font-size: 12px;
176
+ }
177
+
178
+ .card .avatar {
179
+ width: 14px;
180
+ height: 14px;
181
+ border-radius: 2px;
182
+ flex: none;
183
+ margin-top: 2px;
184
+ }
185
+
186
+ .card .body {
187
+ min-width: 0;
188
+ flex: 1;
189
+ }
190
+
191
+ .card .name {
192
+ color: var(--ink);
193
+ font-weight: 700;
194
+ }
195
+
196
+ .card .role {
197
+ color: var(--dim);
198
+ }
199
+
200
+ .card .detail {
201
+ color: var(--think);
202
+ overflow: hidden;
203
+ text-overflow: ellipsis;
204
+ white-space: nowrap;
205
+ }
206
+
207
+ .card .meta {
208
+ color: #5c6675;
209
+ font-size: 11px;
210
+ }
211
+
212
+ .badge {
213
+ font-size: 10px;
214
+ padding: 1px 5px;
215
+ border: 1px solid var(--line);
216
+ color: var(--dim);
217
+ }
218
+
219
+ .badge.thinking {
220
+ color: var(--think);
221
+ border-color: #34456b;
222
+ }
223
+
224
+ .badge.working {
225
+ color: var(--accent);
226
+ border-color: #5c4c22;
227
+ }
228
+
229
+ .badge.waiting {
230
+ color: #c79bff;
231
+ border-color: #453163;
232
+ }
233
+
234
+ .badge.done {
235
+ color: var(--ok);
236
+ border-color: #2c5c45;
237
+ }
238
+
239
+ .badge.error {
240
+ color: var(--warn);
241
+ border-color: #5c3a2c;
242
+ }
243
+
244
+ .log {
245
+ overflow-y: auto;
246
+ flex: 1;
247
+ padding: 4px 0;
248
+ font-size: 12px;
249
+ line-height: 1.45;
250
+ }
251
+
252
+ .line {
253
+ display: flex;
254
+ gap: 6px;
255
+ padding: 2px 10px;
256
+ }
257
+
258
+ .line .who {
259
+ flex: none;
260
+ color: var(--dim);
261
+ }
262
+
263
+ .line .txt {
264
+ min-width: 0;
265
+ word-break: break-word;
266
+ }
267
+
268
+ .line.thought .txt {
269
+ color: var(--think);
270
+ }
271
+
272
+ .line.tool .txt {
273
+ color: var(--accent);
274
+ }
275
+
276
+ .line.delegate .txt {
277
+ color: #c79bff;
278
+ }
279
+
280
+ .line.say .txt {
281
+ color: var(--ink);
282
+ }
283
+
284
+ .line.join .txt,
285
+ .line.leave .txt,
286
+ .line.system .txt {
287
+ color: var(--dim);
288
+ }
@@ -0,0 +1,276 @@
1
+ import { createOfficeRenderer } from "./office-renderer.js";
2
+ import { createSpriteRenderer } from "./sprite-renderer.js";
3
+ import { createEnvironmentRuntime } from "./environment-runtime.js";
4
+ import { loadI18n } from "./i18n.js";
5
+ import { loadPresetContent } from "./static-content.js";
6
+ import { validateRegistry } from "./graph-validator.js";
7
+
8
+ /**
9
+ * Config-driven boot path for the preserved Demo.
10
+ *
11
+ * All scene and character rendering is native to V2 and is constructed from
12
+ * the selected Office Preset. The fixed event runtime is loaded only after the
13
+ * content graph has been resolved and validated.
14
+ */
15
+
16
+ const CONTENT_ROOT = "/v2/content";
17
+ const DEFAULT_PRESET = "tech-open-office";
18
+ const PRESET_STORAGE_KEY = "agent-live:selected-preset";
19
+ let officeApiAvailable;
20
+ const token = new URLSearchParams(location.search).get("token") ?? "";
21
+ const apiFetch = (url, options = {}) => fetch(url, { cache: "no-store", ...options, headers: { ...options.headers, ...(token ? { "x-agent-live-token": token } : {}) } });
22
+
23
+ async function hasOfficeApi() {
24
+ if (officeApiAvailable !== undefined) return officeApiAvailable;
25
+ try {
26
+ officeApiAvailable = (await apiFetch("/api/offices")).ok;
27
+ } catch {
28
+ officeApiAvailable = false;
29
+ }
30
+ return officeApiAvailable;
31
+ }
32
+
33
+ function savedPreset() {
34
+ try {
35
+ return localStorage.getItem(PRESET_STORAGE_KEY);
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function rememberPreset(presetId) {
42
+ try {
43
+ localStorage.setItem(PRESET_STORAGE_KEY, presetId);
44
+ } catch {
45
+ // Storage may be disabled; the selected query parameter still works.
46
+ }
47
+ }
48
+
49
+ function forgetPreset() {
50
+ try {
51
+ localStorage.removeItem(PRESET_STORAGE_KEY);
52
+ } catch {
53
+ // Nothing else to clean up.
54
+ }
55
+ }
56
+
57
+ async function readJson(relativePath) {
58
+ const response = await fetch(`${CONTENT_ROOT}/${relativePath}`);
59
+ if (!response.ok) throw new Error(`无法加载内容:${relativePath} (${response.status})`);
60
+ return response.json();
61
+ }
62
+
63
+ async function loadPreset(presetId, limits) {
64
+ const registry = await loadPresetContent(presetId, readJson);
65
+ applySceneLimits(registry, limits);
66
+ validateRegistry(registry);
67
+ return Object.freeze(registry);
68
+ }
69
+
70
+ async function loadOffice(officeId, limits) {
71
+ const params = new URLSearchParams();
72
+ if (officeId) params.set("id", officeId);
73
+ const response = await apiFetch(`/api/office-content?${params}`);
74
+ if (!response.ok) {
75
+ const body = await response.json().catch(() => ({}));
76
+ throw new Error(body.error ?? `Unable to load office ${officeId}`);
77
+ }
78
+ const registry = await response.json();
79
+ applySceneLimits(registry, limits);
80
+ validateRegistry(registry);
81
+ return Object.freeze(registry);
82
+ }
83
+
84
+ async function loadOfficialOffice(presetId, limits) {
85
+ const apiAvailable = await hasOfficeApi();
86
+ return apiAvailable ? loadOffice(`builtin/${presetId}`, limits) : loadPreset(presetId, limits);
87
+ }
88
+
89
+ function installContentReload(query) {
90
+ const officeId = query.get("office");
91
+ const events = new EventSource("/api/content-events?" + new URLSearchParams({ token }));
92
+ let reloadTimer;
93
+ events.onmessage = (event) => {
94
+ const change = JSON.parse(event.data);
95
+ if (change.type !== "office" || !change.officeId) return;
96
+ // A customization saves and selects. Coalesce its notifications into one
97
+ // navigation instead of interrupting a page that is already reloading.
98
+ clearTimeout(reloadTimer);
99
+ reloadTimer = setTimeout(() => {
100
+ if (officeId === change.officeId) return location.reload();
101
+ const next = new URL(location.href);
102
+ next.searchParams.set("office", change.officeId);
103
+ next.searchParams.delete("preset");
104
+ location.replace(next);
105
+ }, 80);
106
+ };
107
+ window.addEventListener("pagehide", () => { clearTimeout(reloadTimer); events.close(); }, { once: true });
108
+ }
109
+
110
+ function applySceneLimits(content, limits) {
111
+ const trim = (owner, key, max, label) => {
112
+ const list = owner?.[key];
113
+ if (!Array.isArray(list) || list.length <= max) return;
114
+ console.warn(`Agent Live: ${label} ${list.length} exceeds ${max}; extra items were ignored.`);
115
+ owner[key] = list.slice(0, max);
116
+ };
117
+ trim(content.layout, "propInstances", limits.props, "props");
118
+ trim(content.npcs, "entries", limits.npcs, "NPCs");
119
+ trim(content.lifeActivities, "entries", limits.activities, "life activities");
120
+ }
121
+
122
+
123
+ function applyStyle(style, atmosphere) {
124
+ const root = document.documentElement;
125
+ for (const [name, value] of Object.entries(style.tokens?.css ?? {})) root.style.setProperty(`--${name}`, value);
126
+ for (const [name, value] of Object.entries(atmosphere.styleOverrides?.css ?? {})) root.style.setProperty(`--${name}`, value);
127
+ document.documentElement.dataset.officePreset = style.id;
128
+ }
129
+
130
+ function installEnvironmentAdapter(content, query) {
131
+ const runtime = createEnvironmentRuntime(content.environment);
132
+ const overrides = {};
133
+ if (query.has("time")) overrides.time = query.get("time");
134
+ if (query.has("weather")) overrides.weather = query.get("weather");
135
+ if (Object.keys(overrides).length) runtime.update(overrides);
136
+ window.OfficeEnvironment = runtime;
137
+ window.addEventListener("agent-live:environment", (event) => {
138
+ try {
139
+ runtime.update(event.detail ?? {});
140
+ } catch (error) {
141
+ console.warn("无法应用环境更新", error);
142
+ }
143
+ });
144
+ return runtime;
145
+ }
146
+
147
+ function installOfficeAdapter(content, environment) {
148
+ window.Office = createOfficeRenderer(content, environment);
149
+ }
150
+
151
+ function installSpriteAdapter(content) {
152
+ window.Sprites = createSpriteRenderer(content);
153
+ }
154
+
155
+ async function installPresetPicker(selectedId) {
156
+ const catalog = await readJson("catalog.json");
157
+ if (catalog.schemaVersion !== 1 || catalog.kind !== "preset-catalog" || !Array.isArray(catalog.presets)) {
158
+ throw new Error("无效的 Preset Catalog");
159
+ }
160
+ const select = document.getElementById("preset");
161
+ let items = catalog.presets.filter((entry) => entry.visibility !== "internal" || entry.id === selectedId).map((entry) => ({ ...entry, value: `builtin/${entry.id}` }));
162
+ try {
163
+ if (!await hasOfficeApi()) throw new Error("office API unavailable");
164
+ const response = await apiFetch("/api/offices");
165
+ if (response.ok) {
166
+ const offices = await response.json();
167
+ for (const office of offices.filter((entry) => entry.origin === "custom")) items.push({ id: office.id, value: office.id, name: office.name });
168
+ }
169
+ } catch {
170
+ // Static preview servers do not expose local Custom Offices.
171
+ }
172
+ for (const item of items) {
173
+ const option = document.createElement("option");
174
+ option.value = item.value;
175
+ option.textContent = window.AgentLiveI18n?.text(item.name) ?? item.name;
176
+ option.selected = item.value === selectedId;
177
+ select.appendChild(option);
178
+ }
179
+ select.addEventListener("change", async () => {
180
+ if (token && await hasOfficeApi()) {
181
+ select.disabled = true;
182
+ try {
183
+ const response = await apiFetch("/api/office-selection", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ id: select.value }) });
184
+ if (!response.ok) throw new Error("Unable to select office");
185
+ const next = new URL(location.href);
186
+ next.searchParams.delete("preset");
187
+ next.searchParams.delete("office");
188
+ location.replace(next);
189
+ } catch (error) { select.disabled = false; showBootError(error); }
190
+ return;
191
+ }
192
+ const query = new URLSearchParams(location.search);
193
+ if (select.value.startsWith("local/")) {
194
+ query.set("office", select.value);
195
+ query.delete("preset");
196
+ } else {
197
+ const preset = select.value.replace(/^builtin\//, "");
198
+ rememberPreset(preset);
199
+ query.set("preset", preset);
200
+ query.delete("office");
201
+ }
202
+ location.search = query.toString();
203
+ });
204
+ }
205
+
206
+ function loadRuntime() {
207
+ return new Promise((resolve, reject) => {
208
+ const script = document.createElement("script");
209
+ script.src = "/app.js";
210
+ script.onload = resolve;
211
+ script.onerror = () => reject(new Error("Office Runtime 加载失败"));
212
+ document.body.appendChild(script);
213
+ });
214
+ }
215
+
216
+ async function loadCodexControls(query) {
217
+ if (query.get("client") !== "codex") return;
218
+ const module = await import("./codex-controls.js");
219
+ module.installCodexControls(query.get("token") ?? "");
220
+ }
221
+
222
+ function showBootError(error) {
223
+ console.error(error);
224
+ const pill = document.getElementById("conn");
225
+ pill.textContent = window.AgentLiveI18n?.t("config.error") ?? "Configuration error";
226
+ pill.className = "pill offline";
227
+ const log = document.getElementById("log");
228
+ log.innerHTML = `<div class="line system"><span class="who">系统</span><span class="txt"></span></div>`;
229
+ log.querySelector(".txt").textContent = error instanceof Error ? error.message : String(error);
230
+ }
231
+
232
+ try {
233
+ const query = new URLSearchParams(location.search);
234
+ const limitsResponse = await fetch("/api/scene-limits", { cache: "no-store" });
235
+ if (!limitsResponse.ok) throw new Error("无法加载场景容量规则");
236
+ window.SceneLimits = await limitsResponse.json();
237
+ await loadI18n(query);
238
+ const devControls = document.getElementById("devControls");
239
+ devControls.hidden = query.get("dev") !== "1" && query.get("demo") !== "1";
240
+ const requestedPreset = query.get("preset");
241
+ const requestedOffice = query.get("office");
242
+ const storedPreset = savedPreset();
243
+ let presetId = requestedPreset ?? storedPreset ?? DEFAULT_PRESET;
244
+ let content;
245
+ let selectedId;
246
+ if (!requestedOffice && !requestedPreset && await hasOfficeApi()) {
247
+ content = await loadOffice(undefined, window.SceneLimits);
248
+ selectedId = content.preset.id;
249
+ } else if (requestedOffice) {
250
+ if (!await hasOfficeApi()) throw new Error("Custom Offices require the Agent Live local runtime");
251
+ content = await loadOffice(requestedOffice, window.SceneLimits);
252
+ selectedId = requestedOffice;
253
+ } else {
254
+ try {
255
+ content = await loadOfficialOffice(presetId, window.SceneLimits);
256
+ } catch (error) {
257
+ if (requestedPreset || !storedPreset || presetId === DEFAULT_PRESET) throw error;
258
+ forgetPreset();
259
+ presetId = DEFAULT_PRESET;
260
+ content = await loadOfficialOffice(presetId, window.SceneLimits);
261
+ }
262
+ rememberPreset(presetId);
263
+ selectedId = `builtin/${presetId}`;
264
+ }
265
+ window.OfficeContent = content;
266
+ applyStyle(content.style, content.atmosphere);
267
+ await installPresetPicker(selectedId);
268
+ installContentReload(query);
269
+ const environment = installEnvironmentAdapter(content, query);
270
+ installOfficeAdapter(content, environment);
271
+ installSpriteAdapter(content);
272
+ await loadRuntime();
273
+ await loadCodexControls(query);
274
+ } catch (error) {
275
+ showBootError(error);
276
+ }
@@ -0,0 +1,17 @@
1
+ # V2 Content
2
+
3
+ 一个 Preset 通过文件名组合八类本地内容:`styles`、`layouts`、`agent-skins`、`props`、`npcs`、`life-activities`、`atmospheres` 和 `environments`。
4
+
5
+ 这是当前可运行的过渡格式。目标架构会把重复定义整理成统一 Component Library,由同一个 Office Spec Compiler 组装 Official Preset 和 Custom Office;定义见 [`docs/COMPONENT-LIBRARY.md`](../../../../../docs/COMPONENT-LIBRARY.md)。
6
+
7
+ 最短工作流:
8
+
9
+ 1. 复制最接近的 Preset 及需要修改的模块。
10
+ 2. 保持 `demo-office` 不变。
11
+ 3. 在 `catalog.json` 登记新 Preset。
12
+ 4. 执行 `npm run check`。
13
+ 5. 用 `/v2.html?demo=1&preset=<id>` 预览。
14
+
15
+ `Environment` 管理时钟、天气输入、灯光和 NPC 班次;`Atmosphere` 只管理这些状态的视觉覆盖。正式办公场景默认共用 `environments/local-office.json`。
16
+
17
+ 完整说明见 [`docs/PRESET-CONFIG.md`](../../../../../docs/PRESET-CONFIG.md) 和 [`docs/DEVELOPER.md`](../../../../../docs/DEVELOPER.md)。
@@ -0,0 +1,33 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "kind": "agent-skin",
4
+ "id": "builtin/studio-team",
5
+ "name": "暖调工作室团队",
6
+ "version": "1.0.0",
7
+ "engine": { "min": "0.2.0" },
8
+ "contract": "pixel-character-v1",
9
+ "animations": ["idle", "walk", "sit", "type", "reach", "talk"],
10
+ "palettes": {
11
+ "skin": ["#f3cfaa", "#e9b98e", "#d99d70", "#b97750"],
12
+ "hair": ["#38281d", "#273035", "#59392a", "#4a4342", "#6a421f"],
13
+ "fallbackShirts": ["#438b91", "#5d9a72", "#d58b4d", "#c56559", "#916aae", "#5ba9a0"],
14
+ "defaultTrim": "#354747",
15
+ "leadBadge": "#f2bd5b",
16
+ "agentBadge": "#91d4cf"
17
+ },
18
+ "roles": {
19
+ "scout": { "title": "侦察员", "shirt": "#5d9a72", "trim": "#3e7352" },
20
+ "planner": { "title": "规划师", "shirt": "#438b91", "trim": "#32686d" },
21
+ "reviewer": { "title": "评审员", "shirt": "#c56559", "trim": "#98483f" },
22
+ "worker": { "title": "工程师", "shirt": "#d58b4d", "trim": "#a56532" },
23
+ "tester": { "title": "测试员", "shirt": "#916aae", "trim": "#6d4d86" },
24
+ "writer": { "title": "文案", "shirt": "#cb718f", "trim": "#9f526f" },
25
+ "ceo": { "title": "CEO", "shirt": "#e1d5ba", "trim": "#aa9b7d" },
26
+ "cto": { "title": "CTO", "shirt": "#568491", "trim": "#3e626d" },
27
+ "cpo": { "title": "CPO", "shirt": "#aa78ad", "trim": "#805783" },
28
+ "programmer": { "title": "程序员", "shirt": "#d58b4d", "trim": "#a56532" },
29
+ "designer": { "title": "设计师", "shirt": "#5ba9a0", "trim": "#3f8179" },
30
+ "counselor": { "title": "顾问", "shirt": "#918a82", "trim": "#6c655e" },
31
+ "hr": { "title": "HR", "shirt": "#c49a52", "trim": "#94743b" }
32
+ }
33
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "kind": "agent-skin",
4
+ "id": "builtin/tiny-developers",
5
+ "name": "原版像素员工",
6
+ "version": "1.0.0",
7
+ "engine": { "min": "0.2.0" },
8
+ "contract": "pixel-character-v1",
9
+ "animations": ["idle", "walk", "sit", "type", "reach", "talk"],
10
+ "palettes": {
11
+ "skin": ["#f0cba3", "#e8b98f", "#d69f70", "#b87a4f"],
12
+ "hair": ["#2f2418", "#1f2430", "#4a2f22", "#3b3b45", "#5c3b1e"],
13
+ "fallbackShirts": ["#4a78c8", "#4f9d69", "#c8913f", "#b8574f", "#7b57b8", "#4fb8b0"],
14
+ "defaultTrim": "#2b3242",
15
+ "leadBadge": "#ffcc4d",
16
+ "agentBadge": "#8fb8ff"
17
+ },
18
+ "roles": {
19
+ "scout": { "title": "侦察员", "shirt": "#4f9d69", "trim": "#37714b" },
20
+ "planner": { "title": "规划师", "shirt": "#4a78c8", "trim": "#33569a" },
21
+ "reviewer": { "title": "评审员", "shirt": "#b8574f", "trim": "#8d3f38" },
22
+ "worker": { "title": "工程师", "shirt": "#c8913f", "trim": "#9a6c28" },
23
+ "tester": { "title": "测试员", "shirt": "#7b57b8", "trim": "#5b3d8d" },
24
+ "writer": { "title": "文案", "shirt": "#c85f9b", "trim": "#9a4577" },
25
+ "ceo": { "title": "CEO", "shirt": "#d8d2c4", "trim": "#a8a294" },
26
+ "cto": { "title": "CTO", "shirt": "#5f7fa8", "trim": "#455f80" },
27
+ "cpo": { "title": "CPO", "shirt": "#a87fc8", "trim": "#7f5c9a" },
28
+ "programmer": { "title": "程序员", "shirt": "#c8913f", "trim": "#9a6c28" },
29
+ "designer": { "title": "设计师", "shirt": "#4fb8b0", "trim": "#379088" },
30
+ "counselor": { "title": "顾问", "shirt": "#8f8f9f", "trim": "#6b6b7a" },
31
+ "hr": { "title": "HR", "shirt": "#c8a04f", "trim": "#9a7a33" }
32
+ }
33
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "kind": "atmosphere",
4
+ "id": "builtin/default-atmosphere",
5
+ "name": "原版环境",
6
+ "version": "1.0.0",
7
+ "engine": { "min": "0.2.0" },
8
+ "contract": "single-office-v1",
9
+ "styleOverrides": { "css": {} },
10
+ "windowScene": "classic",
11
+ "ambientEffects": [],
12
+ "audio": null
13
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "kind": "atmosphere",
4
+ "id": "builtin/rainy-night",
5
+ "name": "雨夜加班",
6
+ "version": "1.0.0",
7
+ "engine": { "min": "0.2.0" },
8
+ "contract": "single-office-v1",
9
+ "styleOverrides": {
10
+ "css": {
11
+ "bg": "#070a12",
12
+ "panel": "#101724",
13
+ "panel-2": "#172235",
14
+ "line": "#263852",
15
+ "ink": "#d6e5f5",
16
+ "dim": "#71859b",
17
+ "accent": "#ffd36a"
18
+ },
19
+ "canvas": {
20
+ "floorA": "#202c3a",
21
+ "floorB": "#243243",
22
+ "floorLine": "#19232f",
23
+ "wall": "#172334",
24
+ "wallTop": "#101927",
25
+ "wallTrim": "#2c4059",
26
+ "glass": "#254867",
27
+ "glassLite": "#315d82",
28
+ "windowSpark": "#9ed6ff",
29
+ "screen": "#09131f",
30
+ "screenIdle": "#1e3b55",
31
+ "rain": "#8bc8f2"
32
+ }
33
+ },
34
+ "windowScene": "rainy-night",
35
+ "ambientEffects": ["rain-window"],
36
+ "audio": null
37
+ }