@linxin666/dsh-pet 0.2.3 → 0.2.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.i18n.yaml +2 -2
- package/README.md +64 -1
- package/README.zh.md +64 -1
- package/assets/decorations/whale/decoration.json +20 -0
- package/assets/decorations/whale/whale-frames.png +0 -0
- package/contracts/pet-manifest-v2.schema.json +281 -0
- package/contracts/status-decoration-v1.schema.json +144 -0
- package/contracts/voice-pack-v1.schema.json +234 -0
- package/lib/client.js +144 -18
- package/lib/client.js.map +1 -1
- package/lib/index.js +979 -70
- package/lib/types/chatter.d.ts +61 -3
- package/lib/types/chatter.d.ts.map +1 -1
- package/lib/types/chatter.js +71 -12
- package/lib/types/client/PetSettingsCard.d.ts +4 -0
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +3 -1
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +103 -4
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +4 -0
- package/lib/types/client/renderers/live2d.d.ts.map +1 -1
- package/lib/types/client/renderers/live2d.js +13 -1
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +9 -6
- package/lib/types/contracts/status-decoration.d.ts +85 -0
- package/lib/types/contracts/status-decoration.d.ts.map +1 -0
- package/lib/types/contracts/status-decoration.js +21 -0
- package/lib/types/decoration.d.ts +39 -0
- package/lib/types/decoration.d.ts.map +1 -0
- package/lib/types/decoration.js +210 -0
- package/lib/types/event-projection.d.ts +8 -3
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +9 -4
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +2 -0
- package/lib/types/registry.d.ts +55 -2
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +198 -16
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +131 -3
- package/lib/types/service.d.ts +33 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +42 -3
- package/lib/types/voice-pack.d.ts +98 -0
- package/lib/types/voice-pack.d.ts.map +1 -0
- package/lib/types/voice-pack.js +384 -0
- package/package.json +11 -10
- package/src/chatter.test.ts +89 -2
- package/src/chatter.ts +120 -14
- package/src/client/PetSettingsCard.tsx +18 -0
- package/src/client/PetSprite.test.tsx +254 -12
- package/src/client/PetSprite.tsx +142 -25
- package/src/client/locales.ts +4 -0
- package/src/client/renderers/live2d.test.ts +23 -2
- package/src/client/renderers/live2d.ts +14 -1
- package/src/client/settings-form.ts +8 -6
- package/src/contracts/status-decoration.ts +78 -0
- package/src/decoration.test.ts +178 -0
- package/src/decoration.ts +220 -0
- package/src/event-projection.ts +10 -5
- package/src/index.ts +2 -0
- package/src/registry.test.ts +223 -0
- package/src/registry.ts +235 -15
- package/src/routes.ts +128 -3
- package/src/service.ts +59 -3
- package/src/voice-pack.test.ts +216 -0
- package/src/voice-pack.ts +413 -0
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# node scripts/verify-docs.mjs --write <dir>
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: a0adc3fe2cb2767a8e372ed32cec6e8f7bb74660
|
|
6
|
+
README.zh.md: e048ab6aece36283648b95ceddf705b3c6783f7e
|
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ Re-implemented from the pet feature of the Codex desktop app, as an official DSH
|
|
|
26
26
|
| Status bubbles | Only the most recently active top-level session speaks by default — when several sessions run at once, the rest collapse behind a +N badge on the main bubble instead of stacking a tall column; hover the bubble (or tap the badge, for touch) to fan every session's bubble out above it and click one to jump to its session; subagent sessions report through their spawning conversation and never occupy a bubble of their own; transient interaction feedback temporarily takes priority. Bubble copy comes from generous rotating pools per scene (waiting / thinking / writing / done / failed...), tool calls map onto per-family witty lines carrying the real argument hint (e.g. 跑跑 npm test), and a long-lived scene re-phrases itself every few seconds |
|
|
27
27
|
| Inner whispers | 碎碎念: while the model streams, the pet occasionally speaks its inner voice through its own bubble — a fresh whisper takes over the display session's bubble and marks it with 「」 quotes — sharing the same DeepSeek-blue glass as every status bubble, so stacked bubbles never clash — instead of stacking a second bubble — keyword moods woken by the model output (errors, test greens, plans, victories...) plus ambient whispers earned by output volume; paced by a cooldown, the status copy returns after a few seconds |
|
|
28
28
|
| Multi-session activity | The pet is host-global: the most recent meaningful event drives the sprite animation while every active top-level session reports its own state in a separate bubble; completed turns from every session (subagents included) contribute affinity and treats |
|
|
29
|
+
| Voice packs and panel DIY | A per-pet voice.json plus the global $DSH_HOME/pets/.voice.json override replace every bubble word and the hover panel (button labels, stat formats, button visibility); merge precedence per-pet > global > built-in, broken packs warn and never reject a pet |
|
|
29
30
|
|
|
30
31
|
## Pet contract
|
|
31
32
|
|
|
@@ -80,13 +81,47 @@ Where pets come from (later sources override earlier ones on id collision):
|
|
|
80
81
|
Validate and install a pet directory with the CLI (no build step, no npm publish):
|
|
81
82
|
|
|
82
83
|
```sh
|
|
83
|
-
node scripts/dsh-pet validate <dir> # manifest + assets + Live2D reference closure
|
|
84
|
+
node scripts/dsh-pet validate <dir> # manifest + assets + Live2D reference closure + voice.json
|
|
84
85
|
node scripts/dsh-pet install <dir> # validate, then copy into $DSH_HOME/pets/<id>/
|
|
85
86
|
node scripts/dsh-pet install <dir> --force # overwrite an existing same-id install
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
Invalid entries never override a working pet: they are skipped with a diagnostic listed in the settings (Pet section). The registry is built once at host startup; add or change a pet, then restart `dsh web`.
|
|
89
90
|
|
|
91
|
+
## Voice packs and panel chrome (voice.json, pet-center M4, #677)
|
|
92
|
+
|
|
93
|
+
Every word in the thought bubble (status / tool / whisper copy) and the hover panel (button labels, stat formats, button visibility) can be replaced by a pet — or by you — without touching plugin code. Pets ship an optional voice.json in their directory; a global override at $DSH_HOME/pets/.voice.json re-voices pets without editing their directories.
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
{
|
|
97
|
+
"voicePackVersion": 1, // optional; absent reads as v1
|
|
98
|
+
"status": { // status pools keyed by scene id; per-key override
|
|
99
|
+
"done": ["Done for today!", "Another one down"]
|
|
100
|
+
},
|
|
101
|
+
"tools": { // tool pools keyed by tool family; {tool} / {hint} allowed
|
|
102
|
+
"shell": ["Running {hint}", "Hit enter: {hint}"]
|
|
103
|
+
},
|
|
104
|
+
"toolRemaining": ["{n} helpers still at work"], // {n} allowed
|
|
105
|
+
"whispers": { // murmur pools; each section replaces the built-in one
|
|
106
|
+
"generic": ["On it", "Almost there"], // ambient pool; an explicit empty array mutes it
|
|
107
|
+
"rules": [ // ordered keyword rules; given rules replace the built-ins
|
|
108
|
+
{ "keywords": ["all tests pass"], "pool": ["All green!"] }
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"panel": { // hover panel; unset slots keep the plugin i18n copy
|
|
112
|
+
"labels": { "feed": "Treat", "hide": "Dive", "rename": "Rename me", "confirm": "Sure" },
|
|
113
|
+
"stats": { "rank": "Affinity {rank}", "treats": "Treats x{n}", "points": "{points} pts" },
|
|
114
|
+
"actions": ["feed", "rename", "hide"] // subset in canonical order; absent = all; [] = stats only
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- Merge precedence (per slot): the pet voice.json > the global .voice.json > built-in copy. status/tools merge per key, whispers replace per section, panel merges per slot; any slot a layer misses falls through.
|
|
120
|
+
- Placeholder whitelist: tools accept {tool} / {hint}; toolRemaining accepts {n}; panel.stats accept {rank} / {n} / {points}; status, whisper and panel-label lines accept no placeholders (lines carrying one are dropped with a warning).
|
|
121
|
+
- Caps (warn-and-drop): at most 64 lines per pool and 160 characters per line; at most 32 rules and 16 keywords (40 characters) per rule; panel labels 40 and stats 80 characters.
|
|
122
|
+
- A broken pack never breaks the pet: voice.json that is not valid JSON or whose root is not an object is ignored with a warning; every other issue drops its slot only. Diagnostics appear under Settings > Pet directory diagnostics. node scripts/dsh-pet validate <dir> fails installs on structure errors and lists content issues as warnings.
|
|
123
|
+
- Semantics: an empty status/tools pool falls back to the built-in copy (a scene line always renders); an explicit empty whisper pool mutes that channel; an empty panel actions array hides all three buttons; uncovered buttons and stats keep the plugin bilingual dictionary.
|
|
124
|
+
|
|
90
125
|
## Live2D pets (renderer: live2d)
|
|
91
126
|
|
|
92
127
|
Live2D pets render through PixiJS/WebGL: the MIT pixi.js + untitled-pixi-live2d-engine stack ships inside this plugin as a lazily loaded vendor bundle, so sprite-only installations never download or parse it. **The Cubism Core runtime is never bundled or downloaded by this plugin** — Live2D's proprietary license forbids redistributing it. To enable a Live2D pet:
|
|
@@ -122,6 +157,34 @@ A Live2D manifest maps the seven activity phases onto the model's motion groups:
|
|
|
122
157
|
|
|
123
158
|
Model licensing: the official Live2D sample models (Hiyori, Haru, and friends) are evaluation-only and must not be redistributed — ship only models you have rights to (original creations or permissively licensed ones).
|
|
124
159
|
|
|
160
|
+
## Status decorations (decoration.json, pet-center M5, #567)
|
|
161
|
+
|
|
162
|
+
A status bubble can carry a small ornament ahead of its text (built-in: the spouting whale), driven by the ActivityPhase stream. Decorations are independent of pets: own descriptor, own id, own directory — switching pets never switches decorations. Entry assets are PNG/WebP single-row sprite strips only (no SVG/CSS); the bubble always keeps its role=status/aria-live (or session-bubble button semantics), the ornament is aria-hidden; prefers-reduced-motion holds the segment first frame, and a broken asset only removes the ornament — the text stays.
|
|
163
|
+
|
|
164
|
+
```jsonc
|
|
165
|
+
{
|
|
166
|
+
"decorationManifestVersion": 1,
|
|
167
|
+
"id": "whale", // unique lowercase kebab id
|
|
168
|
+
"displayName": "Spouting whale", // optional
|
|
169
|
+
"license": "MIT", // required: asset provenance
|
|
170
|
+
"entry": "whale-frames.png", // PNG/WebP strip, relative to this directory
|
|
171
|
+
"cell": { "width": 64, "height": 48 },
|
|
172
|
+
"columns": 4, // strip frames (1..16)
|
|
173
|
+
"frameMs": 160, // constant frame duration; or "durations": [..] per frame
|
|
174
|
+
"loop": true,
|
|
175
|
+
"phases": { // ActivityPhase -> inclusive frame segment; "hide" = none; default hide
|
|
176
|
+
"idle": "hide",
|
|
177
|
+
"waiting": { "from": 0, "to": 1 },
|
|
178
|
+
"thinking": { "from": 0, "to": 3 },
|
|
179
|
+
"done": { "from": 2, "to": 3 },
|
|
180
|
+
"failed": { "from": 3, "to": 3 }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
- Structure is fail-closed (unknown fields, out-of-range geometry, non-PNG/WebP entries reject with diagnostics); segment content is warn-and-drop. The machine-readable twin lives at contracts/status-decoration-v1.schema.json; the authoritative validator is src/decoration.ts.
|
|
186
|
+
- Sources: built-in assets/decorations/ plus the user directory $DSH_HOME/pets/decorations/<id>/ (same id overrides the built-in). Assets ride /api/pet/decoration/<id>/<file> with the same containment and allow-lists as pet assets.
|
|
187
|
+
- Switch: Settings > Pet > Status decoration (on by default). The built-in whale derives from the DeepSeek wordmark (MIT); see THIRD_PARTY_NOTICES.md.
|
|
125
188
|
## Built-in pets
|
|
126
189
|
|
|
127
190
|
| Registry id | Selector label | Source |
|
package/README.zh.md
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
| 状态气泡 | 默认只有最近活动的顶层会话说话——多会话并行时,其余会话收进主气泡右上角的 +N 角标,不再叠出一长列;悬停气泡(触屏点按角标)即可向上展开所有会话的气泡,点击任一气泡跳转到对应会话;子代理会话借由其发起会话体现,不占用独立气泡;瞬时互动反馈临时优先。气泡文案按场景准备了大量轮换词库(等待 / 思考 / 整理 / 完成 / 失败……),工具调用按工具族映射俏皮文案并带上真实参数(如 跑跑 npm test),同一场景持续数秒会自动换一种说法 |
|
|
27
27
|
| 碎碎念 | 模型流式输出期间,宠物会偶尔借自己的气泡说出内心独白——新鲜的碎碎念会接管展示会话的气泡并以「」引号标记——与状态气泡共用同一片 DeepSeek 蓝黑玻璃,气泡栈内颜色统一不再色差——不再叠出第二只气泡——由模型输出里的关键词触发对应心境(报错、测试全绿、做计划、打胜仗……),输出量累积也会换来日常碎碎念;有冷却节制,数秒后气泡恢复状态文案 |
|
|
28
28
|
| 多会话活动 | 宠物是宿主全局的:最近一次有意义事件驱动精灵动画,同时每个活动的顶层会话用自己的气泡报告各自状态;每个会话(含子代理)完成的轮次都计入亲密度与小鱼干 |
|
|
29
|
+
| 语音包与面板 DIY | 宠物目录 voice.json + 全局 $DSH_HOME/pets/.voice.json 覆盖气泡全部文案与悬浮面板(按钮标签/统计格式/按钮显隐);合并优先级 宠物自带 > 全局 > 内置,坏包警告不拒载 |
|
|
29
30
|
|
|
30
31
|
## 宠物契约
|
|
31
32
|
|
|
@@ -80,13 +81,47 @@
|
|
|
80
81
|
用 CLI 校验并安装宠物目录(零构建、零发布):
|
|
81
82
|
|
|
82
83
|
```sh
|
|
83
|
-
node scripts/dsh-pet validate <dir> # 清单 + 资产 + Live2D 引用闭包
|
|
84
|
+
node scripts/dsh-pet validate <dir> # 清单 + 资产 + Live2D 引用闭包 + voice.json
|
|
84
85
|
node scripts/dsh-pet install <dir> # 校验通过后拷入 $DSH_HOME/pets/<id>/
|
|
85
86
|
node scripts/dsh-pet install <dir> --force # 覆盖同名已安装宠物
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
非法条目永不覆盖可用宠物:它们被跳过并在设置(宠物栏目)给出诊断。注册表在宿主启动时构建一次;新增或修改宠物后重启 `dsh web` 生效。
|
|
89
90
|
|
|
91
|
+
## 语音包与面板定制(voice.json,宠物中心 M4,#677)
|
|
92
|
+
|
|
93
|
+
气泡里的状态/工具/碎碎念文案与悬浮面板的按钮/统计文案,都可以被一只宠物(或你本人)整体替换——宠物不再只能换图,还能换「话」与「面板」。载体是宠物目录内的可选 voice.json(随宠物分发),以及全局覆盖文件 $DSH_HOME/pets/.voice.json(不改宠物目录即可换词)。
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
{
|
|
97
|
+
"voicePackVersion": 1, // 可选;缺省视为 1
|
|
98
|
+
"status": { // 状态池:键 = 场景 id,逐键覆盖内置池
|
|
99
|
+
"done": ["搞定收工~", "交差!下一位"]
|
|
100
|
+
},
|
|
101
|
+
"tools": { // 工具池:键 = 工具族;允许 {tool} / {hint}
|
|
102
|
+
"shell": ["跑跑 {hint}", "敲回车!{hint}"]
|
|
103
|
+
},
|
|
104
|
+
"toolRemaining": ["后台还有 {n} 位小助手"], // 允许 {n}
|
|
105
|
+
"whispers": { // 碎碎念:按节替换(非逐条合并)
|
|
106
|
+
"generic": ["冲了冲了", "稳"], // 环境池;显式空数组 = 静音
|
|
107
|
+
"rules": [ // 关键词规则(有序;给出即整体替换内置规则)
|
|
108
|
+
{ "keywords": ["测试通过"], "pool": ["全绿!"] }
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"panel": { // 悬浮面板(每槽未声明时回落插件 i18n 文案)
|
|
112
|
+
"labels": { "feed": "投喂", "hide": "藏起来", "rename": "起名字", "confirm": "好的" },
|
|
113
|
+
"stats": { "rank": "好感 {rank}", "treats": "鱼干 ×{n}", "points": "{points} 分" },
|
|
114
|
+
"actions": ["feed", "rename", "hide"] // 子集(按规范顺序);省略 = 全部;[] = 只显示统计行
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- 合并优先级(逐槽):宠物自带 voice.json > 全局 .voice.json > 内置文案。status/tools 逐键合并、whispers 按节替换、panel 逐槽合并,任何层缺失的槽位回落下一层。
|
|
120
|
+
- 占位符白名单:tools 允许 {tool} / {hint};toolRemaining 允许 {n};panel.stats 允许 {rank} / {n} / {points};status、碎碎念与面板标签不允许任何占位符(含非法占位符的行被警告丢弃)。
|
|
121
|
+
- 上限(warn-and-drop):每池 64 行以内、每行 160 字符以内;规则 32 条以内、每规则关键词 16 条以内(40 字符以内);面板标签 40 以内、统计 80 字符以内。
|
|
122
|
+
- 坏包不影响宠物:voice.json 不是合法 JSON 或根不是对象 → 警告并整体忽略;其余问题逐槽警告丢弃。诊断显示在设置 → 宠物目录诊断。node scripts/dsh-pet validate <dir> 会把结构错误判为安装失败、内容问题列为警告。
|
|
123
|
+
- 语义细节:status/tools 的空池回落内置文案(场景行始终有话说);whispers 的显式空数组是静音(关掉该通道);面板 actions 为空数组 = 三个按钮全部隐藏;未覆盖的按钮/统计继续使用插件双语字典。
|
|
124
|
+
|
|
90
125
|
## Live2D 宠物(renderer: live2d)
|
|
91
126
|
|
|
92
127
|
Live2D 宠物经 PixiJS/WebGL 渲染:MIT 许可的 pixi.js + untitled-pixi-live2d-engine 栈以按需加载的 vendor 分包随插件内置,纯 sprite 用户永不下载、永不解析它。**本插件永不内置、永不代下 Cubism Core 运行时**——Live2D 专有许可不允许再分发。启用 Live2D 宠物:
|
|
@@ -122,6 +157,34 @@ Live2D 清单把七个活动相位映射到模型的动作组:
|
|
|
122
157
|
|
|
123
158
|
模型授权:Live2D 官方示例模型(Hiyori、Haru 等)仅供评估、禁止再分发——只发布你有权的模型(原创作品或宽松授权的模型)。
|
|
124
159
|
|
|
160
|
+
## 状态装饰(decoration.json,宠物中心 M5,#567)
|
|
161
|
+
|
|
162
|
+
宠物状态气泡里的文字之前可以有一个小的状态装饰(内置:喷水鲸鱼),由 ActivityPhase 流驱动换帧。装饰与宠物相互独立:独立描述符、独立 id、独立目录,换宠物不换装饰。入口资产只收 PNG/WebP 单行精灵条带(不收 SVG/CSS);气泡自身的 role=status/aria-live(或会话气泡按钮语义)永远保留,装饰 aria-hidden;prefers-reduced-motion 时停在帧段首帧,资产加载失败只消失装饰、文字照常。
|
|
163
|
+
|
|
164
|
+
```jsonc
|
|
165
|
+
{
|
|
166
|
+
"decorationManifestVersion": 1,
|
|
167
|
+
"id": "whale", // 唯一小写 kebab id
|
|
168
|
+
"displayName": "喷水鲸鱼", // 可选
|
|
169
|
+
"license": "MIT", // 必填:资产授权(社区装饰必须携带来源声明)
|
|
170
|
+
"entry": "whale-frames.png", // PNG/WebP 单行条带,相对本目录
|
|
171
|
+
"cell": { "width": 64, "height": 48 },
|
|
172
|
+
"columns": 4, // 条带帧数(1..16)
|
|
173
|
+
"frameMs": 160, // 常量帧时长;或用 "durations": [..] 逐帧覆盖
|
|
174
|
+
"loop": true,
|
|
175
|
+
"phases": { // ActivityPhase 七态 -> 帧段(含端点);"hide" = 不显示;缺省 = hide
|
|
176
|
+
"idle": "hide",
|
|
177
|
+
"waiting": { "from": 0, "to": 1 },
|
|
178
|
+
"thinking": { "from": 0, "to": 3 },
|
|
179
|
+
"done": { "from": 2, "to": 3 },
|
|
180
|
+
"failed": { "from": 3, "to": 3 }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
- 结构 fail-closed(未知字段、越界尺寸、非 PNG/WebP 入口直接拒载并进诊断),帧段内容 warn-and-drop。机器可读 schema 见 contracts/status-decoration-v1.schema.json,权威校验器为 src/decoration.ts。
|
|
186
|
+
- 来源:内置 assets/decorations/ + 用户目录 $DSH_HOME/pets/decorations/<id>/(同 id 覆盖内置)。资产经 /api/pet/decoration/<id>/<file> 路由,containment 与白名单与宠物资产同构。
|
|
187
|
+
- 开关:设置 → 宠物 → 状态装饰(默认开)。内置鲸鱼素材派生自 DeepSeek wordmark(MIT),完整声明见 THIRD_PARTY_NOTICES.md。
|
|
125
188
|
## 内置宠物
|
|
126
189
|
|
|
127
190
|
| 注册表 id | 选择器名称 | 来源 |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"decorationManifestVersion": 1,
|
|
3
|
+
"id": "whale",
|
|
4
|
+
"displayName": "喷水鲸鱼",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"entry": "whale-frames.png",
|
|
7
|
+
"cell": { "width": 64, "height": 48 },
|
|
8
|
+
"columns": 4,
|
|
9
|
+
"frameMs": 160,
|
|
10
|
+
"loop": true,
|
|
11
|
+
"phases": {
|
|
12
|
+
"idle": "hide",
|
|
13
|
+
"waiting": { "from": 0, "to": 1 },
|
|
14
|
+
"thinking": { "from": 0, "to": 3 },
|
|
15
|
+
"tool": { "from": 0, "to": 3 },
|
|
16
|
+
"review": { "from": 0, "to": 1 },
|
|
17
|
+
"done": { "from": 2, "to": 3 },
|
|
18
|
+
"failed": { "from": 3, "to": 3 }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.linxin666.org/dsh-pet/pet-manifest-v2.schema.json",
|
|
4
|
+
"title": "DSH Pet Manifest v2",
|
|
5
|
+
"description": "Pet-center manifest v2 (issue #623). Pure-asset pet directory contract: renderer type declaration, license metadata, and renderer-specific blocks. Top level and renderer blocks are fail-closed; v1 manifests (no petManifestVersion) are compat-read as sprite2d by the registry.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"petManifestVersion",
|
|
9
|
+
"id",
|
|
10
|
+
"displayName",
|
|
11
|
+
"license"
|
|
12
|
+
],
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"properties": {
|
|
15
|
+
"$schema": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
"petManifestVersion": {
|
|
19
|
+
"const": 2
|
|
20
|
+
},
|
|
21
|
+
"id": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
24
|
+
"maxLength": 64,
|
|
25
|
+
"description": "Globally unique lowercase kebab-case pet id."
|
|
26
|
+
},
|
|
27
|
+
"displayName": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"minLength": 1,
|
|
30
|
+
"maxLength": 64
|
|
31
|
+
},
|
|
32
|
+
"description": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"maxLength": 500
|
|
35
|
+
},
|
|
36
|
+
"version": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
39
|
+
},
|
|
40
|
+
"author": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"maxLength": 128
|
|
43
|
+
},
|
|
44
|
+
"license": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"minLength": 1,
|
|
47
|
+
"maxLength": 128,
|
|
48
|
+
"description": "Asset license identifier (SPDX id or free text). Required so community pets always carry provenance."
|
|
49
|
+
},
|
|
50
|
+
"homepage": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"maxLength": 500,
|
|
53
|
+
"description": "Optional provenance URL for the pet assets."
|
|
54
|
+
},
|
|
55
|
+
"renderer": {
|
|
56
|
+
"enum": [
|
|
57
|
+
"sprite2d",
|
|
58
|
+
"live2d"
|
|
59
|
+
],
|
|
60
|
+
"default": "sprite2d",
|
|
61
|
+
"description": "Renderer kind; omitted means sprite2d (v1 compatibility). Unknown values are rejected."
|
|
62
|
+
},
|
|
63
|
+
"sprite2d": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"required": [
|
|
67
|
+
"spritesheetPath"
|
|
68
|
+
],
|
|
69
|
+
"description": "Spritesheet renderer block; required when renderer=sprite2d.",
|
|
70
|
+
"properties": {
|
|
71
|
+
"spritesheetPath": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"minLength": 1,
|
|
74
|
+
"description": "Atlas path relative to the manifest directory (safe segments only)."
|
|
75
|
+
},
|
|
76
|
+
"cell": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"additionalProperties": false,
|
|
79
|
+
"description": "Atlas cell size in px; defaults to the Codex contract 192x208.",
|
|
80
|
+
"properties": {
|
|
81
|
+
"width": {
|
|
82
|
+
"type": "integer",
|
|
83
|
+
"minimum": 1,
|
|
84
|
+
"maximum": 2048
|
|
85
|
+
},
|
|
86
|
+
"height": {
|
|
87
|
+
"type": "integer",
|
|
88
|
+
"minimum": 1,
|
|
89
|
+
"maximum": 2048
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"columns": {
|
|
94
|
+
"type": "integer",
|
|
95
|
+
"minimum": 1,
|
|
96
|
+
"maximum": 32,
|
|
97
|
+
"default": 8
|
|
98
|
+
},
|
|
99
|
+
"atlasRows": {
|
|
100
|
+
"type": "integer",
|
|
101
|
+
"minimum": 1,
|
|
102
|
+
"maximum": 32,
|
|
103
|
+
"description": "Total atlas rows; 9 for the classic contract, 11 for v2 look-row atlases."
|
|
104
|
+
},
|
|
105
|
+
"frames": {
|
|
106
|
+
"type": "array",
|
|
107
|
+
"maxItems": 32,
|
|
108
|
+
"items": {
|
|
109
|
+
"type": "integer",
|
|
110
|
+
"minimum": 0,
|
|
111
|
+
"maximum": 32
|
|
112
|
+
},
|
|
113
|
+
"description": "Per-row used frame counts; omitted rows fall back to the hatch-pet contract table."
|
|
114
|
+
},
|
|
115
|
+
"tracks": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"description": "Per-track rhythm overrides (durations/loop/fallback), v1 structure unchanged."
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"live2d": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"additionalProperties": false,
|
|
124
|
+
"required": [
|
|
125
|
+
"model",
|
|
126
|
+
"motions"
|
|
127
|
+
],
|
|
128
|
+
"description": "Live2D renderer block; required when renderer=live2d. The Cubism Core runtime is always user-supplied; the plugin never bundles or downloads it.",
|
|
129
|
+
"properties": {
|
|
130
|
+
"model": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"minLength": 1,
|
|
133
|
+
"pattern": "\\.model3\\.json$",
|
|
134
|
+
"description": "Path of the .model3.json relative to the manifest directory."
|
|
135
|
+
},
|
|
136
|
+
"scale": {
|
|
137
|
+
"type": "number",
|
|
138
|
+
"exclusiveMinimum": 0,
|
|
139
|
+
"maximum": 10,
|
|
140
|
+
"default": 1
|
|
141
|
+
},
|
|
142
|
+
"translate": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"additionalProperties": false,
|
|
145
|
+
"properties": {
|
|
146
|
+
"x": {
|
|
147
|
+
"type": "number"
|
|
148
|
+
},
|
|
149
|
+
"y": {
|
|
150
|
+
"type": "number"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"motions": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"additionalProperties": false,
|
|
157
|
+
"required": [
|
|
158
|
+
"idle"
|
|
159
|
+
],
|
|
160
|
+
"description": "ActivityPhase -> model motion group name. Unmapped phases fall back to the idle group; missing groups degrade to the first frame with a diagnostic.",
|
|
161
|
+
"properties": {
|
|
162
|
+
"idle": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"minLength": 1
|
|
165
|
+
},
|
|
166
|
+
"waiting": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"minLength": 1
|
|
169
|
+
},
|
|
170
|
+
"thinking": {
|
|
171
|
+
"type": "string",
|
|
172
|
+
"minLength": 1
|
|
173
|
+
},
|
|
174
|
+
"tool": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"minLength": 1
|
|
177
|
+
},
|
|
178
|
+
"review": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"minLength": 1
|
|
181
|
+
},
|
|
182
|
+
"done": {
|
|
183
|
+
"type": "string",
|
|
184
|
+
"minLength": 1
|
|
185
|
+
},
|
|
186
|
+
"failed": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"minLength": 1
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"expressions": {
|
|
193
|
+
"type": "object",
|
|
194
|
+
"additionalProperties": false,
|
|
195
|
+
"description": "Optional ActivityPhase -> expression name (layered over the motion).",
|
|
196
|
+
"properties": {
|
|
197
|
+
"idle": {
|
|
198
|
+
"type": "string",
|
|
199
|
+
"minLength": 1
|
|
200
|
+
},
|
|
201
|
+
"waiting": {
|
|
202
|
+
"type": "string",
|
|
203
|
+
"minLength": 1
|
|
204
|
+
},
|
|
205
|
+
"thinking": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"minLength": 1
|
|
208
|
+
},
|
|
209
|
+
"tool": {
|
|
210
|
+
"type": "string",
|
|
211
|
+
"minLength": 1
|
|
212
|
+
},
|
|
213
|
+
"review": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"minLength": 1
|
|
216
|
+
},
|
|
217
|
+
"done": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"minLength": 1
|
|
220
|
+
},
|
|
221
|
+
"failed": {
|
|
222
|
+
"type": "string",
|
|
223
|
+
"minLength": 1
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"hitAreas": {
|
|
228
|
+
"type": "array",
|
|
229
|
+
"items": {
|
|
230
|
+
"type": "string",
|
|
231
|
+
"minLength": 1
|
|
232
|
+
},
|
|
233
|
+
"description": "Hit area names that trigger pet.interact; defaults to every HitArea the model declares."
|
|
234
|
+
},
|
|
235
|
+
"lipSync": {
|
|
236
|
+
"type": "boolean",
|
|
237
|
+
"default": false,
|
|
238
|
+
"description": "Optional whisper-paced lip sync (post-M3)."
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"sequences": {
|
|
243
|
+
"type": "object",
|
|
244
|
+
"description": "Renderer-agnostic ActivityPhase sequence table (v1 structure; every declared sequence has at least 5 items)."
|
|
245
|
+
},
|
|
246
|
+
"remarks": {
|
|
247
|
+
"type": "object",
|
|
248
|
+
"description": "Per-slot witty-remark pools (v1 structure; a slot replaces the built-in pool for that slot only)."
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"allOf": [
|
|
252
|
+
{
|
|
253
|
+
"if": {
|
|
254
|
+
"properties": {
|
|
255
|
+
"renderer": {
|
|
256
|
+
"const": "sprite2d"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"then": {
|
|
261
|
+
"required": [
|
|
262
|
+
"sprite2d"
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"if": {
|
|
268
|
+
"properties": {
|
|
269
|
+
"renderer": {
|
|
270
|
+
"const": "live2d"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
"then": {
|
|
275
|
+
"required": [
|
|
276
|
+
"live2d"
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.linxin666.org/dsh-pet/status-decoration-v1.schema.json",
|
|
4
|
+
"title": "DSH Pet Status Decoration v1",
|
|
5
|
+
"description": "Status-decoration descriptor (issue #623 M5, protocol #567). A pure-content entry ornamenting the pet center's status bubble chrome with a PNG/WebP sprite strip driven by the ActivityPhase stream. Structure is fail-closed; phase-binding content issues drop that binding only. Entry assets are PNG/WebP strips only (no SVG/CSS) per the adopted disciplines.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"decorationManifestVersion",
|
|
9
|
+
"id",
|
|
10
|
+
"license",
|
|
11
|
+
"entry",
|
|
12
|
+
"cell",
|
|
13
|
+
"columns"
|
|
14
|
+
],
|
|
15
|
+
"additionalProperties": false,
|
|
16
|
+
"properties": {
|
|
17
|
+
"$schema": {
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"decorationManifestVersion": {
|
|
21
|
+
"const": 1
|
|
22
|
+
},
|
|
23
|
+
"id": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
26
|
+
"maxLength": 64
|
|
27
|
+
},
|
|
28
|
+
"displayName": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"maxLength": 64
|
|
31
|
+
},
|
|
32
|
+
"license": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"minLength": 1,
|
|
35
|
+
"maxLength": 128,
|
|
36
|
+
"description": "Asset license identifier (SPDX id or free text). Required so community decorations always carry provenance."
|
|
37
|
+
},
|
|
38
|
+
"entry": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"maxLength": 256,
|
|
41
|
+
"description": "PNG/WebP single-row sprite strip, relative to the descriptor directory."
|
|
42
|
+
},
|
|
43
|
+
"cell": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": [
|
|
46
|
+
"width",
|
|
47
|
+
"height"
|
|
48
|
+
],
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"properties": {
|
|
51
|
+
"width": {
|
|
52
|
+
"type": "integer",
|
|
53
|
+
"minimum": 1,
|
|
54
|
+
"maximum": 256
|
|
55
|
+
},
|
|
56
|
+
"height": {
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"minimum": 1,
|
|
59
|
+
"maximum": 256
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"columns": {
|
|
64
|
+
"type": "integer",
|
|
65
|
+
"minimum": 1,
|
|
66
|
+
"maximum": 16
|
|
67
|
+
},
|
|
68
|
+
"frameMs": {
|
|
69
|
+
"type": "integer",
|
|
70
|
+
"minimum": 1,
|
|
71
|
+
"maximum": 2000,
|
|
72
|
+
"description": "Constant per-frame duration; 'durations' overrides per frame."
|
|
73
|
+
},
|
|
74
|
+
"durations": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": {
|
|
77
|
+
"type": "integer",
|
|
78
|
+
"minimum": 1,
|
|
79
|
+
"maximum": 2000
|
|
80
|
+
},
|
|
81
|
+
"description": "Per-frame durations; length must equal 'columns'."
|
|
82
|
+
},
|
|
83
|
+
"loop": {
|
|
84
|
+
"type": "boolean",
|
|
85
|
+
"default": true
|
|
86
|
+
},
|
|
87
|
+
"phases": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"additionalProperties": false,
|
|
90
|
+
"description": "ActivityPhase -> frame segment; 'hide' (default) shows no ornament.",
|
|
91
|
+
"properties": {
|
|
92
|
+
"idle": {
|
|
93
|
+
"$ref": "#/definitions/phaseBinding"
|
|
94
|
+
},
|
|
95
|
+
"waiting": {
|
|
96
|
+
"$ref": "#/definitions/phaseBinding"
|
|
97
|
+
},
|
|
98
|
+
"thinking": {
|
|
99
|
+
"$ref": "#/definitions/phaseBinding"
|
|
100
|
+
},
|
|
101
|
+
"tool": {
|
|
102
|
+
"$ref": "#/definitions/phaseBinding"
|
|
103
|
+
},
|
|
104
|
+
"review": {
|
|
105
|
+
"$ref": "#/definitions/phaseBinding"
|
|
106
|
+
},
|
|
107
|
+
"done": {
|
|
108
|
+
"$ref": "#/definitions/phaseBinding"
|
|
109
|
+
},
|
|
110
|
+
"failed": {
|
|
111
|
+
"$ref": "#/definitions/phaseBinding"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"definitions": {
|
|
117
|
+
"phaseBinding": {
|
|
118
|
+
"oneOf": [
|
|
119
|
+
{
|
|
120
|
+
"const": "hide"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"type": "object",
|
|
124
|
+
"required": [
|
|
125
|
+
"from",
|
|
126
|
+
"to"
|
|
127
|
+
],
|
|
128
|
+
"additionalProperties": false,
|
|
129
|
+
"properties": {
|
|
130
|
+
"from": {
|
|
131
|
+
"type": "integer",
|
|
132
|
+
"minimum": 0
|
|
133
|
+
},
|
|
134
|
+
"to": {
|
|
135
|
+
"type": "integer",
|
|
136
|
+
"minimum": 0
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"description": "Inclusive frame segment into the strip; must satisfy from <= to < columns."
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|