@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.
- package/CODE_OF_CONDUCT.md +7 -0
- package/CONTRIBUTING.md +56 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/SECURITY.md +13 -0
- package/dist/adapter-sdk/index.d.ts +45 -0
- package/dist/adapter-sdk/index.d.ts.map +1 -0
- package/dist/adapter-sdk/index.js +106 -0
- package/dist/adapter-sdk/index.js.map +1 -0
- package/dist/content/compiler.d.ts +31 -0
- package/dist/content/compiler.d.ts.map +1 -0
- package/dist/content/compiler.js +218 -0
- package/dist/content/compiler.js.map +1 -0
- package/dist/content/graph-validator.d.ts +56 -0
- package/dist/content/graph-validator.d.ts.map +1 -0
- package/dist/content/graph-validator.js +226 -0
- package/dist/content/graph-validator.js.map +1 -0
- package/dist/content/library.d.ts +4 -0
- package/dist/content/library.d.ts.map +1 -0
- package/dist/content/library.js +45 -0
- package/dist/content/library.js.map +1 -0
- package/dist/content/registry.d.ts +34 -0
- package/dist/content/registry.d.ts.map +1 -0
- package/dist/content/registry.js +130 -0
- package/dist/content/registry.js.map +1 -0
- package/dist/content/runtime-content.d.ts +63 -0
- package/dist/content/runtime-content.d.ts.map +1 -0
- package/dist/content/runtime-content.js +78 -0
- package/dist/content/runtime-content.js.map +1 -0
- package/dist/content/schema.d.ts +142 -0
- package/dist/content/schema.d.ts.map +1 -0
- package/dist/content/schema.js +280 -0
- package/dist/content/schema.js.map +1 -0
- package/dist/content/validator.d.ts +35 -0
- package/dist/content/validator.d.ts.map +1 -0
- package/dist/content/validator.js +205 -0
- package/dist/content/validator.js.map +1 -0
- package/dist/core/agents.d.ts +56 -0
- package/dist/core/agents.d.ts.map +1 -0
- package/dist/core/agents.js +148 -0
- package/dist/core/agents.js.map +1 -0
- package/dist/core/limits.d.ts +15 -0
- package/dist/core/limits.d.ts.map +1 -0
- package/dist/core/limits.js +16 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/mapping.d.ts +17 -0
- package/dist/core/mapping.d.ts.map +1 -0
- package/dist/core/mapping.js +94 -0
- package/dist/core/mapping.js.map +1 -0
- package/dist/core/protocol.d.ts +105 -0
- package/dist/core/protocol.d.ts.map +1 -0
- package/dist/core/protocol.js +6 -0
- package/dist/core/protocol.js.map +1 -0
- package/dist/core/state.d.ts +49 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +216 -0
- package/dist/core/state.js.map +1 -0
- package/dist/creator/commands.d.ts +19 -0
- package/dist/creator/commands.d.ts.map +1 -0
- package/dist/creator/commands.js +44 -0
- package/dist/creator/commands.js.map +1 -0
- package/dist/creator/mode.d.ts +10 -0
- package/dist/creator/mode.d.ts.map +1 -0
- package/dist/creator/mode.js +23 -0
- package/dist/creator/mode.js.map +1 -0
- package/dist/creator/service.d.ts +199 -0
- package/dist/creator/service.d.ts.map +1 -0
- package/dist/creator/service.js +159 -0
- package/dist/creator/service.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/agent-live-runtime.d.ts +17 -0
- package/dist/runtime/agent-live-runtime.d.ts.map +1 -0
- package/dist/runtime/agent-live-runtime.js +50 -0
- package/dist/runtime/agent-live-runtime.js.map +1 -0
- package/dist/runtime/content-service.d.ts +75 -0
- package/dist/runtime/content-service.d.ts.map +1 -0
- package/dist/runtime/content-service.js +60 -0
- package/dist/runtime/content-service.js.map +1 -0
- package/dist/runtime/resource-guard.d.ts +17 -0
- package/dist/runtime/resource-guard.d.ts.map +1 -0
- package/dist/runtime/resource-guard.js +54 -0
- package/dist/runtime/resource-guard.js.map +1 -0
- package/dist/runtime/server.d.ts +39 -0
- package/dist/runtime/server.d.ts.map +1 -0
- package/dist/runtime/server.js +286 -0
- package/dist/runtime/server.js.map +1 -0
- package/dist/web/app.d.ts +1 -0
- package/dist/web/app.js +1317 -0
- package/dist/web/index.html +45 -0
- package/dist/web/office.js +350 -0
- package/dist/web/sprites.js +185 -0
- package/dist/web/style.css +288 -0
- package/dist/web/v2/bootstrap.js +276 -0
- package/dist/web/v2/content/README.md +17 -0
- package/dist/web/v2/content/agent-skins/studio-team.json +33 -0
- package/dist/web/v2/content/agent-skins/tiny-developers.json +33 -0
- package/dist/web/v2/content/atmospheres/default.json +13 -0
- package/dist/web/v2/content/atmospheres/rainy-night.json +37 -0
- package/dist/web/v2/content/catalog.json +14 -0
- package/dist/web/v2/content/component-library/activity-implementations.json +873 -0
- package/dist/web/v2/content/component-library/activity-recipes.json +25 -0
- package/dist/web/v2/content/component-library/agent-profile-templates.json +18 -0
- package/dist/web/v2/content/component-library/catalog.json +38 -0
- package/dist/web/v2/content/component-library/npc-templates.json +42 -0
- package/dist/web/v2/content/component-library/props.json +41 -0
- package/dist/web/v2/content/environments/local-office.json +31 -0
- package/dist/web/v2/content/environments/rainy-night.json +28 -0
- package/dist/web/v2/content/environments/static-office.json +27 -0
- package/dist/web/v2/content/layouts/boardroom-office.json +122 -0
- package/dist/web/v2/content/layouts/demo-office.json +88 -0
- package/dist/web/v2/content/layouts/lively-office.json +96 -0
- package/dist/web/v2/content/layouts/old-school-office.json +154 -0
- package/dist/web/v2/content/layouts/tech-open-office.json +132 -0
- package/dist/web/v2/content/life-activities/boardroom-routines.json +67 -0
- package/dist/web/v2/content/life-activities/none.json +10 -0
- package/dist/web/v2/content/life-activities/office-basics.json +39 -0
- package/dist/web/v2/content/life-activities/old-school-routines.json +74 -0
- package/dist/web/v2/content/life-activities/tech-office-routines.json +148 -0
- package/dist/web/v2/content/npcs/boardroom-staff.json +54 -0
- package/dist/web/v2/content/npcs/none.json +10 -0
- package/dist/web/v2/content/npcs/office-staff.json +25 -0
- package/dist/web/v2/content/npcs/old-school-staff.json +39 -0
- package/dist/web/v2/content/npcs/tech-office-staff.json +40 -0
- package/dist/web/v2/content/official-offices/boardroom-office.json +25 -0
- package/dist/web/v2/content/official-offices/old-school-office.json +25 -0
- package/dist/web/v2/content/official-offices/tech-open-office.json +25 -0
- package/dist/web/v2/content/presets/boardroom-office.json +21 -0
- package/dist/web/v2/content/presets/cozy-studio.json +19 -0
- package/dist/web/v2/content/presets/demo-office.json +19 -0
- package/dist/web/v2/content/presets/lively-office.json +19 -0
- package/dist/web/v2/content/presets/night-shift.json +19 -0
- package/dist/web/v2/content/presets/old-school-office.json +20 -0
- package/dist/web/v2/content/presets/refined-demo.json +20 -0
- package/dist/web/v2/content/presets/tech-open-office.json +21 -0
- package/dist/web/v2/content/props/default-office.json +23 -0
- package/dist/web/v2/content/props/extended-office.json +39 -0
- package/dist/web/v2/content/props/tech-office.json +28 -0
- package/dist/web/v2/content/styles/pixel-classic.json +160 -0
- package/dist/web/v2/content/styles/warm-studio.json +150 -0
- package/dist/web/v2/environment-runtime.d.ts +1 -0
- package/dist/web/v2/environment-runtime.js +138 -0
- package/dist/web/v2/graph-validator.d.ts +55 -0
- package/dist/web/v2/graph-validator.js +225 -0
- package/dist/web/v2/i18n.js +54 -0
- package/dist/web/v2/locales/en.json +209 -0
- package/dist/web/v2/locales/zh-CN.json +52 -0
- package/dist/web/v2/office-engine.d.ts +1 -0
- package/dist/web/v2/office-engine.js +145 -0
- package/dist/web/v2/office-renderer.d.ts +1 -0
- package/dist/web/v2/office-renderer.js +843 -0
- package/dist/web/v2/parity.html +10 -0
- package/dist/web/v2/parity.js +82 -0
- package/dist/web/v2/sprite-renderer.d.ts +1 -0
- package/dist/web/v2/sprite-renderer.js +242 -0
- package/dist/web/v2/static-content.js +48 -0
- package/dist/web/v2/style.css +140 -0
- package/dist/web/v2.html +68 -0
- package/docs/ADAPTER-DEVELOPMENT.md +328 -0
- package/docs/ADAPTER-SDK.md +109 -0
- package/docs/ARCHITECTURE.md +223 -0
- package/docs/COMPONENT-LIBRARY.md +212 -0
- package/docs/CUSTOMIZATION.md +163 -0
- package/docs/DEVELOPER.md +186 -0
- package/docs/DSH-ADAPTER-DESIGN.md +173 -0
- package/docs/OFFICE-THEMES.md +516 -0
- package/docs/PRESET-CONFIG.md +460 -0
- package/docs/RELEASE-CHECK.md +23 -0
- package/docs/TECHNICAL.md +515 -0
- package/docs/USING-CODEX.md +24 -0
- package/docs/USING-DSH.md +44 -0
- package/docs/USING-PI.md +50 -0
- package/docs/VISION.md +146 -0
- package/examples/adapter/local-office.mjs +52 -0
- package/package.json +89 -0
- package/skills/agent-live-adapter-builder/SKILL.md +30 -0
- package/skills/agent-live-adapter-builder/agents/openai.yaml +4 -0
- package/skills/agent-live-adapter-builder/references/investigation.md +9 -0
- package/skills/agent-live-adapter-builder/references/review.md +7 -0
- package/skills/agent-live-adapter-builder/scripts/create-adapter.mjs +26 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural rules for a renderable content graph — the single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* Two consumers share this module:
|
|
5
|
+
* - the browser imports the generated `web/v2/graph-validator.js`
|
|
6
|
+
* (see `tsconfig.web-validator.json` and `scripts/web-validator.mjs`);
|
|
7
|
+
* - the local server imports the TypeScript module directly, so the same rules
|
|
8
|
+
* gate saving an Office Spec and serving `/api/office-content`.
|
|
9
|
+
*
|
|
10
|
+
* Keep this file dependency-free, platform-neutral, and side-effect-free: it is
|
|
11
|
+
* compiled for the browser and bundled into every host integration.
|
|
12
|
+
*/
|
|
13
|
+
/** Capabilities every layout must expose as a walkable work station. */
|
|
14
|
+
export const WORK_CAPABILITIES = ["research", "create", "compute", "plan", "communicate", "collaborate"];
|
|
15
|
+
/** Renderer-resolved destinations that deliberately do not belong to a layout. */
|
|
16
|
+
export const DYNAMIC_ACTIVITY_TARGETS = new Set(["near-colleague"]);
|
|
17
|
+
function namedInstance(requirement) {
|
|
18
|
+
if (typeof requirement === "string")
|
|
19
|
+
return requirement;
|
|
20
|
+
if (requirement && typeof requirement.prop === "string")
|
|
21
|
+
return requirement.prop;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves activity requirements against the props actually present, so both
|
|
26
|
+
* syntaxes stay valid while content migrates.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveActivityRequirements(requires, instances, capabilitiesOf, activity) {
|
|
29
|
+
const table = [...instances];
|
|
30
|
+
const bindings = [];
|
|
31
|
+
const issues = [];
|
|
32
|
+
for (const requirement of requires ?? []) {
|
|
33
|
+
const named = namedInstance(requirement);
|
|
34
|
+
if (named !== undefined) {
|
|
35
|
+
const present = table.some(([instanceId]) => instanceId === named);
|
|
36
|
+
bindings.push(present ? named : null);
|
|
37
|
+
if (!present)
|
|
38
|
+
issues.push({ code: "missing-activity-prop", path: "$", message: `${activity} 缺少 Prop:${named}` });
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const capability = requirement && typeof requirement.capability === "string"
|
|
42
|
+
? requirement.capability
|
|
43
|
+
: undefined;
|
|
44
|
+
if (!capability) {
|
|
45
|
+
bindings.push(null);
|
|
46
|
+
issues.push({ code: "invalid-activity-requirement", path: "$", message: `${activity} 的依赖必须是具名道具或能力需求` });
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const match = table.find(([, type]) => capabilitiesOf(type).includes(capability));
|
|
50
|
+
bindings.push(match ? match[0] : null);
|
|
51
|
+
if (!match)
|
|
52
|
+
issues.push({ code: "missing-activity-capability", path: "$", message: `${activity} 需要 ${capability} 能力,当前办公室没有提供该能力的道具` });
|
|
53
|
+
}
|
|
54
|
+
return { bindings, issues };
|
|
55
|
+
}
|
|
56
|
+
export function assertManifest(value, kind) {
|
|
57
|
+
const manifest = value;
|
|
58
|
+
if (!manifest || manifest.schemaVersion !== 1 || manifest.kind !== kind || !manifest.id || !manifest.version) {
|
|
59
|
+
throw new Error(`无效的 ${kind} 内容清单`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function manifestKindFor(key) {
|
|
63
|
+
if (key === "agentSkin")
|
|
64
|
+
return "agent-skin";
|
|
65
|
+
if (key === "lifeActivities")
|
|
66
|
+
return "life-activities";
|
|
67
|
+
return key;
|
|
68
|
+
}
|
|
69
|
+
function validClock(value) {
|
|
70
|
+
if (typeof value !== "string" || !/^\d{2}:\d{2}$/.test(value))
|
|
71
|
+
return false;
|
|
72
|
+
const [hour, minute] = value.split(":").map(Number);
|
|
73
|
+
return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59;
|
|
74
|
+
}
|
|
75
|
+
function shiftIssue(value, code, path, label) {
|
|
76
|
+
if (!value || !validClock(value.start) || !validClock(value.end))
|
|
77
|
+
return { code, path, message: `${label} 必须提供有效的 HH:MM 起止时间` };
|
|
78
|
+
if (value.endLatest !== undefined && !validClock(value.endLatest))
|
|
79
|
+
return { code, path: `${path}.endLatest`, message: `${label} 的最晚下班时间必须是有效的 HH:MM` };
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Layout structure rules. `propTypeNames` are the type names a layout may
|
|
84
|
+
* reference; the library stores props namespaced while the rendered graph does
|
|
85
|
+
* not, so both callers normalize before calling.
|
|
86
|
+
*/
|
|
87
|
+
export function layoutIssues(layout, propTypeNames) {
|
|
88
|
+
if (!layout || typeof layout !== "object")
|
|
89
|
+
return [{ code: "missing-layout", path: "$.layout", message: "Layout 缺失" }];
|
|
90
|
+
const issues = [];
|
|
91
|
+
if (layout.contract !== "single-office-v1")
|
|
92
|
+
issues.push({ code: "invalid-layout-contract", path: "$.layout", message: `不支持的 Layout 合同:${layout.contract}` });
|
|
93
|
+
if (layout.canvas?.width !== 384 || layout.canvas?.height !== 216)
|
|
94
|
+
issues.push({ code: "invalid-layout-canvas", path: "$.layout", message: "single-office-v1 必须使用 384×216 逻辑画布" });
|
|
95
|
+
if (!Array.isArray(layout.seats) || layout.seats.length !== 8)
|
|
96
|
+
issues.push({ code: "invalid-layout-seats", path: "$.layout", message: "Demo Layout 必须提供 8 个座位" });
|
|
97
|
+
if (!Array.isArray(layout.navigation?.lanes) || !layout.navigation.lanes.length)
|
|
98
|
+
issues.push({ code: "invalid-layout-navigation", path: "$.layout", message: "Layout 缺少导航通道" });
|
|
99
|
+
const propTypes = new Set(propTypeNames);
|
|
100
|
+
const textIds = new Set();
|
|
101
|
+
if (layout.textSlots !== undefined && (!Array.isArray(layout.textSlots) || layout.textSlots.length > 8)) {
|
|
102
|
+
issues.push({ code: "invalid-text-slots", path: "$.layout.textSlots", message: "maximum 8 text areas" });
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
for (const slot of layout.textSlots ?? []) {
|
|
106
|
+
const valid = slot && typeof slot.id === "string" && /^[a-z][a-z0-9-]*$/.test(slot.id) && !textIds.has(slot.id)
|
|
107
|
+
&& [slot.x, slot.y, slot.width, slot.height, slot.maxLength].every(Number.isFinite)
|
|
108
|
+
&& slot.x >= 0 && slot.y >= 0 && slot.width >= 12 && slot.height >= 9
|
|
109
|
+
&& slot.x + slot.width <= 384 && slot.y + slot.height <= 216
|
|
110
|
+
&& slot.maxLength >= 1 && slot.maxLength <= 120
|
|
111
|
+
&& [slot.text, slot.defaultText].every((text) => text === undefined || typeof text === "string" && [...text].length <= slot.maxLength);
|
|
112
|
+
if (!valid)
|
|
113
|
+
issues.push({ code: "invalid-text-slot", path: "$.layout.textSlots", message: "invalid, duplicate or out-of-bounds text area" });
|
|
114
|
+
if (slot?.id)
|
|
115
|
+
textIds.add(slot.id);
|
|
116
|
+
}
|
|
117
|
+
const propInstances = new Set();
|
|
118
|
+
for (const instance of layout.propInstances ?? []) {
|
|
119
|
+
if (!propTypes.has(instance?.type))
|
|
120
|
+
issues.push({ code: "unknown-layout-prop", path: "$.layout.propInstances", message: `未知 Prop Type:${instance?.type}` });
|
|
121
|
+
if (!instance?.id || propInstances.has(instance.id))
|
|
122
|
+
issues.push({ code: "invalid-layout-prop", path: "$.layout.propInstances", message: `重复或无效的 Prop 实例:${instance?.id ?? "—"}` });
|
|
123
|
+
propInstances.add(instance?.id);
|
|
124
|
+
}
|
|
125
|
+
for (const capability of WORK_CAPABILITIES) {
|
|
126
|
+
if (!layout.stations?.[capability])
|
|
127
|
+
issues.push({ code: "missing-layout-station", path: `$.layout.stations.${capability}`, message: `Layout 缺少工作能力:${capability}` });
|
|
128
|
+
}
|
|
129
|
+
return issues;
|
|
130
|
+
}
|
|
131
|
+
/** Every reason the browser would refuse to render this resolved content graph. */
|
|
132
|
+
export function graphIssues(content) {
|
|
133
|
+
const issues = [];
|
|
134
|
+
for (const [key, value] of Object.entries(content ?? {})) {
|
|
135
|
+
if (key === "preset" || key === "agentProfile")
|
|
136
|
+
continue;
|
|
137
|
+
try {
|
|
138
|
+
assertManifest(value, manifestKindFor(key));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
issues.push({ code: "invalid-manifest", path: `$.${key}`, message: error.message });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const profile = content?.agentProfile;
|
|
145
|
+
if (!profile || typeof profile !== "object" || typeof profile.template !== "string" || !profile.template
|
|
146
|
+
|| !profile.appearance || typeof profile.appearance !== "object" || Array.isArray(profile.appearance)) {
|
|
147
|
+
issues.push({ code: "invalid-agent-profile", path: "$.agentProfile", message: "无效的 Agent Profile" });
|
|
148
|
+
}
|
|
149
|
+
const layout = content?.layout;
|
|
150
|
+
issues.push(...layoutIssues(layout, Object.keys(content?.props?.types ?? {})));
|
|
151
|
+
const instances = (layout?.propInstances ?? []).map((instance) => [instance?.id, instance?.type]);
|
|
152
|
+
const capabilitiesOf = (type) => content?.props?.types?.[type]?.capabilities ?? [];
|
|
153
|
+
;
|
|
154
|
+
(content?.npcs?.entries ?? []).forEach((npc, index) => {
|
|
155
|
+
const path = `$.npcs.entries[${index}]`;
|
|
156
|
+
if (!npc?.id || !npc.role || !layout?.targets?.[npc.spawn])
|
|
157
|
+
issues.push({ code: "invalid-npc", path, message: `无效的 NPC:${npc?.id ?? "—"}` });
|
|
158
|
+
const shift = npc?.shift ? shiftIssue(npc.shift, "invalid-npc-shift", path, `NPC ${npc.id} 的 shift`) : null;
|
|
159
|
+
if (shift)
|
|
160
|
+
issues.push(shift);
|
|
161
|
+
});
|
|
162
|
+
;
|
|
163
|
+
(content?.lifeActivities?.entries ?? []).forEach((activity, index) => {
|
|
164
|
+
const path = `$.lifeActivities.entries[${index}]`;
|
|
165
|
+
if (!activity?.id || !["agent", "npc", "person"].includes(activity.participant?.kind) || !activity.steps?.length) {
|
|
166
|
+
issues.push({ code: "invalid-activity", path, message: `无效的 Life Activity:${activity?.id ?? "—"}` });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (activity.participant?.minAgents != null && (!Number.isInteger(activity.participant.minAgents) || activity.participant.minAgents < 2)) {
|
|
170
|
+
issues.push({ code: "invalid-activity-participant", path, message: `${activity.id} 的 minAgents 必须是至少 2 的整数` });
|
|
171
|
+
}
|
|
172
|
+
const resolved = resolveActivityRequirements(activity.requires, instances, capabilitiesOf, activity.id);
|
|
173
|
+
issues.push(...resolved.issues.map((issue) => ({ ...issue, path })));
|
|
174
|
+
for (const step of activity.steps ?? []) {
|
|
175
|
+
if (!layout?.targets?.[step?.target] && !DYNAMIC_ACTIVITY_TARGETS.has(step?.target))
|
|
176
|
+
issues.push({ code: "missing-activity-target", path, message: `${activity.id} 缺少 Target:${step?.target}` });
|
|
177
|
+
for (const target of step?.targets ?? []) {
|
|
178
|
+
if (!layout?.targets?.[target])
|
|
179
|
+
issues.push({ code: "missing-activity-target", path, message: `${activity.id} 缺少 Group Target:${target}` });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
const environment = content?.environment;
|
|
184
|
+
if (!environment || !["local", "fixed"].includes(environment.clock?.mode)) {
|
|
185
|
+
issues.push({ code: "invalid-environment", path: "$.environment", message: "Environment 的 clock.mode 必须是 local 或 fixed" });
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
if (environment.clock.mode === "fixed" && !validClock(environment.clock.fixedTime))
|
|
189
|
+
issues.push({ code: "invalid-environment", path: "$.environment.clock.fixedTime", message: "Environment 的 fixedTime 无效" });
|
|
190
|
+
if (!Array.isArray(environment.clock?.phases) || !environment.clock.phases.length)
|
|
191
|
+
issues.push({ code: "invalid-environment", path: "$.environment.clock", message: "Environment 缺少 day phases" });
|
|
192
|
+
for (const phase of environment.clock?.phases ?? []) {
|
|
193
|
+
if (!phase?.id || !validClock(phase.start))
|
|
194
|
+
issues.push({ code: "invalid-environment", path: "$.environment.clock.phases", message: "Environment 包含无效的 day phase" });
|
|
195
|
+
}
|
|
196
|
+
if (!Array.isArray(environment.weather?.allowedConditions) || !environment.weather.allowedConditions.length) {
|
|
197
|
+
issues.push({ code: "invalid-environment", path: "$.environment.weather", message: "Environment 缺少天气类型" });
|
|
198
|
+
}
|
|
199
|
+
const allowedWeather = new Set(environment.weather?.allowedConditions ?? []);
|
|
200
|
+
for (const condition of [environment.weather?.condition, environment.weather?.fallback]) {
|
|
201
|
+
if (condition != null && !allowedWeather.has(condition)) {
|
|
202
|
+
issues.push({ code: "invalid-environment", path: "$.environment.weather", message: `Environment 的天气 ${condition} 不在 allowedConditions 中` });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const shift = environment.npcSchedule?.defaultShift ? shiftIssue(environment.npcSchedule.defaultShift, "invalid-environment", "$.environment.npcSchedule", "Environment 的 NPC 默认班次") : null;
|
|
206
|
+
if (shift)
|
|
207
|
+
issues.push(shift);
|
|
208
|
+
for (const [role, value] of Object.entries(environment.npcSchedule?.roleOverrides ?? {})) {
|
|
209
|
+
const roleIssue = shiftIssue(value, "invalid-environment", `$.environment.npcSchedule.roleOverrides.${role}`, `Environment 的 ${role} 班次`);
|
|
210
|
+
if (roleIssue)
|
|
211
|
+
issues.push(roleIssue);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return issues;
|
|
215
|
+
}
|
|
216
|
+
/** Browser entry point: refuse the first structural problem, exactly as rendered. */
|
|
217
|
+
export function validateRegistry(content) {
|
|
218
|
+
const issues = graphIssues(content);
|
|
219
|
+
if (issues.length)
|
|
220
|
+
throw new Error(issues[0].message);
|
|
221
|
+
}
|
|
222
|
+
/** Server entry point: report every problem at once for the Creator surface. */
|
|
223
|
+
export function graphIssueMessages(content) {
|
|
224
|
+
return graphIssues(content).map((issue) => issue.message);
|
|
225
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const DEFAULT_LOCALE = "en";
|
|
2
|
+
const LOCALE_STORAGE_KEY = "agent-live:locale";
|
|
3
|
+
|
|
4
|
+
export async function loadI18n(query) {
|
|
5
|
+
let saved = "";
|
|
6
|
+
try { saved = localStorage.getItem(LOCALE_STORAGE_KEY) ?? ""; } catch {}
|
|
7
|
+
const requested = query.get("lang") || saved || DEFAULT_LOCALE;
|
|
8
|
+
const locale = requested.toLowerCase().startsWith("zh") ? "zh-CN" : "en";
|
|
9
|
+
const response = await fetch(`/v2/locales/${locale}.json`, { cache: "no-cache" });
|
|
10
|
+
if (!response.ok) throw new Error(`Unable to load locale: ${locale}`);
|
|
11
|
+
const bundle = await response.json();
|
|
12
|
+
const api = {
|
|
13
|
+
locale,
|
|
14
|
+
t(key, vars = {}) {
|
|
15
|
+
let value = bundle.strings?.[key] ?? key;
|
|
16
|
+
for (const [name, replacement] of Object.entries(vars)) {
|
|
17
|
+
value = value.replaceAll(`{${name}}`, String(replacement));
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
},
|
|
21
|
+
text(value) {
|
|
22
|
+
const source = String(value ?? "");
|
|
23
|
+
const exact = bundle.text?.[source];
|
|
24
|
+
if (exact) return exact;
|
|
25
|
+
for (const pattern of bundle.patterns ?? []) {
|
|
26
|
+
const match = source.match(new RegExp(pattern.source));
|
|
27
|
+
if (match) return source.replace(new RegExp(pattern.source), pattern.replace);
|
|
28
|
+
}
|
|
29
|
+
return source;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
document.documentElement.lang = locale;
|
|
33
|
+
window.AgentLiveI18n = api;
|
|
34
|
+
for (const element of document.querySelectorAll("[data-i18n]")) element.textContent = api.t(element.dataset.i18n);
|
|
35
|
+
for (const element of document.querySelectorAll("[data-i18n-placeholder]")) element.placeholder = api.t(element.dataset.i18nPlaceholder);
|
|
36
|
+
for (const element of document.querySelectorAll("[data-i18n-title]")) {
|
|
37
|
+
const value = api.t(element.dataset.i18nTitle);
|
|
38
|
+
element.title = value;
|
|
39
|
+
element.setAttribute("aria-label", value);
|
|
40
|
+
}
|
|
41
|
+
const language = document.getElementById("language");
|
|
42
|
+
if (language) {
|
|
43
|
+
language.textContent = locale === "en" ? "中文" : "EN";
|
|
44
|
+
language.setAttribute("aria-label", api.t("nav.language"));
|
|
45
|
+
language.addEventListener("click", () => {
|
|
46
|
+
const next = locale === "en" ? "zh-CN" : "en";
|
|
47
|
+
try { localStorage.setItem(LOCALE_STORAGE_KEY, next); } catch {}
|
|
48
|
+
const url = new URL(location.href);
|
|
49
|
+
url.searchParams.set("lang", next);
|
|
50
|
+
location.href = url.toString();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return api;
|
|
54
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
{
|
|
2
|
+
"locale": "en",
|
|
3
|
+
"strings": {
|
|
4
|
+
"connection.connecting": "Connecting…",
|
|
5
|
+
"connection.connected": "Connected",
|
|
6
|
+
"connection.reconnecting": "Reconnecting…",
|
|
7
|
+
"nav.preset": "Office preset",
|
|
8
|
+
"nav.soundOff": "Sound: off",
|
|
9
|
+
"nav.soundOn": "Sound: on",
|
|
10
|
+
"nav.demo": "Demo",
|
|
11
|
+
"nav.language": "Switch language",
|
|
12
|
+
"task.label": "Current task",
|
|
13
|
+
"task.empty": "Waiting for a task…",
|
|
14
|
+
"panel.crew": "Team",
|
|
15
|
+
"panel.activity": "Activity",
|
|
16
|
+
"replay.day": "{name}'s Day",
|
|
17
|
+
"replay.playing": "Replaying at 3×",
|
|
18
|
+
"replay.empty": "No history yet",
|
|
19
|
+
"replay.noActivity": "No activity to replay",
|
|
20
|
+
"replay.progress": "Replay {current}/{total} · 3×",
|
|
21
|
+
"replay.complete": "Replay complete · Idle",
|
|
22
|
+
"approval.deny": "Deny",
|
|
23
|
+
"approval.allow": "Allow",
|
|
24
|
+
"client.model": "Codex model",
|
|
25
|
+
"client.loadingModels": "Loading models…",
|
|
26
|
+
"client.prompt": "Give Codex a task…",
|
|
27
|
+
"client.stop": "Stop",
|
|
28
|
+
"client.stopping": "Stopping…",
|
|
29
|
+
"client.send": "Send",
|
|
30
|
+
"status.running": "running",
|
|
31
|
+
"state.idle": "Idle",
|
|
32
|
+
"state.thinking": "Thinking",
|
|
33
|
+
"state.working": "Working",
|
|
34
|
+
"state.waiting": "Waiting",
|
|
35
|
+
"state.talking": "Reporting",
|
|
36
|
+
"state.done": "Done",
|
|
37
|
+
"state.error": "Error",
|
|
38
|
+
"npc.arrival": "Starting my shift",
|
|
39
|
+
"npc.departure": "Heading home",
|
|
40
|
+
"agent.joined": "I'm here",
|
|
41
|
+
"agent.teammate": "{name}'s Teammate{index}",
|
|
42
|
+
"delegate.owner": "{name} will take this",
|
|
43
|
+
"delegate.assign": "Assign: {task}",
|
|
44
|
+
"config.error": "Configuration error"
|
|
45
|
+
},
|
|
46
|
+
"text": {
|
|
47
|
+
"原版基准(Legacy)": "Original Baseline (Legacy)",
|
|
48
|
+
"有生活的像素办公室": "Lively Pixel Office",
|
|
49
|
+
"雨夜加班办公室": "Rainy Night Office",
|
|
50
|
+
"暖调创意工作室": "Warm Creative Studio",
|
|
51
|
+
"啊派": "A Pi",
|
|
52
|
+
"阿派": "A Pi",
|
|
53
|
+
"科迪": "Cody",
|
|
54
|
+
"老板": "Boss",
|
|
55
|
+
"林姨": "Lin",
|
|
56
|
+
"陈姐": "Chen",
|
|
57
|
+
"老周": "Zhou",
|
|
58
|
+
"主管": "Lead",
|
|
59
|
+
"外援": "Teammate",
|
|
60
|
+
"保洁": "Cleaner",
|
|
61
|
+
"会议主持": "Meeting host",
|
|
62
|
+
"行政秘书": "Executive assistant",
|
|
63
|
+
"会议服务": "Meeting support",
|
|
64
|
+
"侦察员": "Scout",
|
|
65
|
+
"规划师": "Planner",
|
|
66
|
+
"评审员": "Reviewer",
|
|
67
|
+
"工程师": "Engineer",
|
|
68
|
+
"文案": "Writer",
|
|
69
|
+
"程序员": "Developer",
|
|
70
|
+
"设计师": "Designer",
|
|
71
|
+
"顾问": "Advisor",
|
|
72
|
+
"前台秘书": "Receptionist",
|
|
73
|
+
"后勤": "Facilities",
|
|
74
|
+
"综合办公区": "General Office",
|
|
75
|
+
"主任室": "Manager's Office",
|
|
76
|
+
"小会议室": "Meeting Room",
|
|
77
|
+
"档案室": "Records",
|
|
78
|
+
"机房": "Server Room",
|
|
79
|
+
"行政前台": "Reception",
|
|
80
|
+
"茶水间": "Break Room",
|
|
81
|
+
"上班啦": "Starting my shift",
|
|
82
|
+
"下班啦": "Heading home",
|
|
83
|
+
"聊两句?": "Quick chat?",
|
|
84
|
+
"最近进展怎么样?": "How is it going?",
|
|
85
|
+
"刚处理完一块,歇一下": "Wrapped up a piece—taking a breather",
|
|
86
|
+
"出去走走?": "Want to take a walk?",
|
|
87
|
+
"透口气": "Getting some fresh air",
|
|
88
|
+
"走一圈再回来": "One lap, then back to work",
|
|
89
|
+
"摸会儿鱼": "Taking a quick break",
|
|
90
|
+
"刷一下手机": "Checking my phone",
|
|
91
|
+
"去趟洗手间": "Heading to the restroom",
|
|
92
|
+
"稍等一下": "Back in a moment",
|
|
93
|
+
"整理一下桌面": "Tidying the desks",
|
|
94
|
+
"这边也收拾好": "This area is tidy too",
|
|
95
|
+
"休息区整理好了": "The lounge is all set",
|
|
96
|
+
"接到新需求": "New task received",
|
|
97
|
+
"构思中": "Planning",
|
|
98
|
+
"思考中": "Thinking",
|
|
99
|
+
"汇报中": "Preparing an update",
|
|
100
|
+
"继续推进": "Continuing",
|
|
101
|
+
"处理报错": "Handling an error",
|
|
102
|
+
"任务失败": "Task failed",
|
|
103
|
+
"待命": "Standing by",
|
|
104
|
+
"修改代码": "Editing code",
|
|
105
|
+
"联网检索": "Searching the web",
|
|
106
|
+
"查看图片": "Viewing an image",
|
|
107
|
+
"协调同事": "Coordinating teammates",
|
|
108
|
+
"更新任务板": "Updating the task board"
|
|
109
|
+
,"任务启动失败": "Task failed to start"
|
|
110
|
+
,"调整方案": "Adjusting the plan"
|
|
111
|
+
,"等待你的确认": "Waiting for your approval"
|
|
112
|
+
,"执行命令": "Run command"
|
|
113
|
+
,"修改文件": "Edit files"
|
|
114
|
+
,"允许执行命令?": "Allow this command?"
|
|
115
|
+
,"允许修改文件?": "Allow these file changes?"
|
|
116
|
+
,"先看今天的重点": "Let's review today's priorities"
|
|
117
|
+
,"这里需要重点推进": "This needs focused progress"
|
|
118
|
+
,"负责人按节点同步": "Owners, report at each milestone"
|
|
119
|
+
,"去接杯水": "Getting some water"
|
|
120
|
+
,"喝口水继续": "A sip, then back to it"
|
|
121
|
+
,"接杯水,歇一下": "Getting water and taking a breather"
|
|
122
|
+
,"更新议程": "Updating the agenda"
|
|
123
|
+
,"资料放桌上了": "The materials are on the table"
|
|
124
|
+
,"补充咖啡": "Refilling the coffee"
|
|
125
|
+
,"请用茶": "Tea is ready"
|
|
126
|
+
,"出去聊两句?": "Quick chat in the hall?"
|
|
127
|
+
,"这个文件你看了吗?": "Have you reviewed this file?"
|
|
128
|
+
,"刚送过来,正准备看": "It just arrived—I'm about to review it"
|
|
129
|
+
,"泡杯茶": "Making some tea"
|
|
130
|
+
,"茶缸续上水": "Refilling the tea mug"
|
|
131
|
+
,"喂,这里是办公室": "Hello, this is the office"
|
|
132
|
+
,"复印归档": "Copying and filing"
|
|
133
|
+
,"盖章登记好了": "Stamped and registered"
|
|
134
|
+
,"看看格子间": "Checking the cubicles"
|
|
135
|
+
,"整理一下": "Tidying up"
|
|
136
|
+
,"风扇别忘了关": "Remember to turn off the fan"
|
|
137
|
+
,"会议室收好了": "The meeting room is ready"
|
|
138
|
+
,"擦擦桌面": "Wiping the desks"
|
|
139
|
+
,"这边也收拾一下": "Tidying this side too"
|
|
140
|
+
,"会议区整理好啦": "The meeting area is ready"
|
|
141
|
+
,"暖调工作室团队": "Warm Studio Team"
|
|
142
|
+
,"测试员": "Tester"
|
|
143
|
+
,"原版像素员工": "Original Pixel Team"
|
|
144
|
+
,"原版环境": "Original Environment"
|
|
145
|
+
,"雨夜加班": "Rainy Night Shift"
|
|
146
|
+
,"老板主持会议": "Boss-led Meeting"
|
|
147
|
+
,"会前资料": "Meeting Materials"
|
|
148
|
+
,"接水": "Get Water"
|
|
149
|
+
,"茶水服务": "Refreshments"
|
|
150
|
+
,"清洁巡检": "Cleaning Round"
|
|
151
|
+
,"走廊聊两句": "Hallway Chat"
|
|
152
|
+
,"后勤巡检": "Facilities Round"
|
|
153
|
+
,"泡茶": "Make Tea"
|
|
154
|
+
,"文件流转": "Document Run"
|
|
155
|
+
,"老板给员工打气": "Team Pep Talk"
|
|
156
|
+
,"兄弟们加油努力干!": "Team, let's keep pushing!"
|
|
157
|
+
,"大家加油,冲一冲!": "Come on, team—let's go!"
|
|
158
|
+
,"老板巡视规划": "Boss Planning Round"
|
|
159
|
+
,"看看今天的计划": "Let's review today's plan"
|
|
160
|
+
,"老板接水": "Boss Gets Water"
|
|
161
|
+
,"接杯水": "Getting a glass of water"
|
|
162
|
+
,"保洁巡回": "Cleaner Round"
|
|
163
|
+
,"和同事聊聊天": "Chat with a colleague"
|
|
164
|
+
,"和同事出去溜达": "Take a walk with a colleague"
|
|
165
|
+
,"摸鱼玩手机": "Take a phone break"
|
|
166
|
+
,"林姨浇花": "Lin Waters the Plants"
|
|
167
|
+
,"给花浇点水": "Giving the plants some water"
|
|
168
|
+
,"喝饱水,长得精神点": "Drink up and grow strong"
|
|
169
|
+
,"这盆也不能落下": "Can't forget this one"
|
|
170
|
+
,"去洗手间": "Visit the Restroom"
|
|
171
|
+
,"本地真实环境": "Local Live Environment"
|
|
172
|
+
,"固定雨夜环境": "Fixed Rainy Night Environment"
|
|
173
|
+
,"静态兼容环境": "Static Compatibility Environment"
|
|
174
|
+
,"大会议室长桌办公布局": "Boardroom Layout"
|
|
175
|
+
,"原版办公室布局": "Original Office Layout"
|
|
176
|
+
,"有生活的原版办公室布局": "Lively Original Office Layout"
|
|
177
|
+
,"老派企业办公室布局": "Old-school Office Layout"
|
|
178
|
+
,"Tech 开放式办公室布局": "Tech Open Office Layout"
|
|
179
|
+
,"大会议室日常": "Boardroom Routines"
|
|
180
|
+
,"不启用办公室生活": "Office Life Disabled"
|
|
181
|
+
,"基础办公室生活": "Basic Office Routines"
|
|
182
|
+
,"老派办公室日常": "Old-school Office Routines"
|
|
183
|
+
,"Tech 办公室日常": "Tech Office Routines"
|
|
184
|
+
,"会议室支持人员": "Boardroom Support Staff"
|
|
185
|
+
,"秘书": "Secretary"
|
|
186
|
+
,"会务": "Meeting Attendant"
|
|
187
|
+
,"不启用 NPC": "NPCs Disabled"
|
|
188
|
+
,"办公室基础员工": "Basic Office Staff"
|
|
189
|
+
,"老派办公室职员": "Old-school Office Staff"
|
|
190
|
+
,"Tech 办公室 NPC": "Tech Office NPCs"
|
|
191
|
+
,"Agent Live 原版": "Agent Live Original"
|
|
192
|
+
,"原版办公室物件": "Original Office Props"
|
|
193
|
+
,"扩展办公室物件": "Extended Office Props"
|
|
194
|
+
,"Tech 开放式办公室物件": "Tech Open Office Props"
|
|
195
|
+
,"经典像素": "Classic Pixel"
|
|
196
|
+
},
|
|
197
|
+
"patterns": [
|
|
198
|
+
{ "source": "^(.+) 上班了$", "replace": "$1 started work" },
|
|
199
|
+
{ "source": "^(.+) 交付完成,下班$", "replace": "$1 finished and signed off" },
|
|
200
|
+
{ "source": "^(.+) 异常退出$", "replace": "$1 exited unexpectedly" },
|
|
201
|
+
{ "source": "^查阅 (.+)$", "replace": "Read $1" },
|
|
202
|
+
{ "source": "^翻找 (.+)$", "replace": "Browse $1" },
|
|
203
|
+
{ "source": "^检索 (.+)$", "replace": "Search $1" },
|
|
204
|
+
{ "source": "^新建 (.+)$", "replace": "Create $1" },
|
|
205
|
+
{ "source": "^修改 (.+)$", "replace": "Edit $1" },
|
|
206
|
+
{ "source": "^执行 (.+)$", "replace": "Run $1" },
|
|
207
|
+
{ "source": "^等待 (.+) 位同事$", "replace": "Waiting for $1 teammates" }
|
|
208
|
+
]
|
|
209
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"locale": "zh-CN",
|
|
3
|
+
"strings": {
|
|
4
|
+
"connection.connecting": "连接中…",
|
|
5
|
+
"connection.connected": "已连接",
|
|
6
|
+
"connection.reconnecting": "重连中…",
|
|
7
|
+
"nav.preset": "办公室预设",
|
|
8
|
+
"nav.soundOff": "音效: 关",
|
|
9
|
+
"nav.soundOn": "音效: 开",
|
|
10
|
+
"nav.demo": "演示",
|
|
11
|
+
"nav.language": "切换语言",
|
|
12
|
+
"task.label": "当前需求",
|
|
13
|
+
"task.empty": "等待指令…",
|
|
14
|
+
"panel.crew": "员工",
|
|
15
|
+
"panel.activity": "动态",
|
|
16
|
+
"replay.day": "{name} 的一天",
|
|
17
|
+
"replay.playing": "正在以 3× 回放",
|
|
18
|
+
"replay.empty": "暂无历史",
|
|
19
|
+
"replay.noActivity": "暂无可回放任务",
|
|
20
|
+
"replay.progress": "回放 {current}/{total} · 3×",
|
|
21
|
+
"replay.complete": "回放完成 · 空闲",
|
|
22
|
+
"approval.deny": "拒绝",
|
|
23
|
+
"approval.allow": "允许",
|
|
24
|
+
"client.model": "Codex 模型",
|
|
25
|
+
"client.loadingModels": "加载模型…",
|
|
26
|
+
"client.prompt": "给 Codex 一个任务…",
|
|
27
|
+
"client.stop": "停止",
|
|
28
|
+
"client.stopping": "正在停止…",
|
|
29
|
+
"client.send": "发送",
|
|
30
|
+
"status.running": "运行中",
|
|
31
|
+
"state.idle": "待命",
|
|
32
|
+
"state.thinking": "思考",
|
|
33
|
+
"state.working": "工作",
|
|
34
|
+
"state.waiting": "等待",
|
|
35
|
+
"state.talking": "汇报",
|
|
36
|
+
"state.done": "完成",
|
|
37
|
+
"state.error": "出错",
|
|
38
|
+
"npc.arrival": "上班啦",
|
|
39
|
+
"npc.departure": "下班啦",
|
|
40
|
+
"agent.joined": "我来了",
|
|
41
|
+
"agent.teammate": "{name} 的同事{index}",
|
|
42
|
+
"delegate.owner": "{name}来负责这块",
|
|
43
|
+
"delegate.assign": "派活:{task}",
|
|
44
|
+
"config.error": "配置错误"
|
|
45
|
+
},
|
|
46
|
+
"text": {
|
|
47
|
+
"tech": "科技",
|
|
48
|
+
"meetingroom": "会议室",
|
|
49
|
+
"oldschool": "格子间"
|
|
50
|
+
},
|
|
51
|
+
"patterns": []
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createOfficeEngine(content: any): any;
|