@linxin666/dsh-pet 0.3.18 → 0.3.20
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 +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +70 -33
- package/lib/client.js.map +1 -1
- package/lib/index.js +75 -57
- package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
- package/lib/types/client/renderers/frames2d.js +77 -29
- package/lib/types/event-projection.d.ts +14 -0
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +32 -18
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +11 -1
- package/package.json +14 -14
- package/src/client/renderers/frames2d.test.ts +94 -4
- package/src/client/renderers/frames2d.ts +76 -26
- package/src/event-projection.ts +37 -18
- package/src/service.ts +11 -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: 8b92fda99dc04c8f95ff2e42bf39b42a6a4c1f6f
|
|
6
|
+
README.zh.md: d684b6381a9515c6bc7cef55f418ab4e3ab7ee0c
|
package/README.md
CHANGED
|
@@ -289,7 +289,7 @@ global React root (createRoot → document.body) <-- polling 2s -- pet-client (b
|
|
|
289
289
|
PetSprite floating layer (portal + rAF)
|
|
290
290
|
```
|
|
291
291
|
|
|
292
|
-
- **Status source**: the host projects official `turn/start`, `step/start`, `assistant/
|
|
292
|
+
- **Status source**: the host projects official `turn/start`, `step/start`, `assistant/message`, `tool/call`, `tool/result`, and `turn/end` events plus live `agent/assistant-stream` chunks into waiting/thinking/tool/review/done/failed states. Optional legacy `activity/status` events remain a compatibility input.
|
|
293
293
|
- **Registry**: the host normalizes every manifest into a full render definition (geometry, per-row frame counts, per-track durations) and serves it over `/api/pet/pets`; the browser half renders any entry from that definition and carries no per-pet code.
|
|
294
294
|
- **Selection & naming**: `petId` lives in the settings namespace; per-pet names live in `pet.json` under `names`, edited through the hover-panel rename of the active pet. Legacy installs migrate their flat `name` onto the whale girl.
|
|
295
295
|
- **Multi-session semantics**: the API and browser mount are host-global and expose no foreground-session identity. Concurrent sessions each keep their own projected state: the most recent meaningful event drives the sprite animation, while every active TOP-LEVEL session reports its stage in its own bubble (the state view's sessions list, capped at 12 most-recent). Subagent children are tracked for animation, rewards, and the single display bubble but render no bubble of their own, so N conversations never multiply into an N-plus-subagents stack. Every session's completed turns are still rewarded independently; disposing a session removes its bubble, and disposing the display session falls back to the most recent remaining one.
|
package/README.zh.md
CHANGED
|
@@ -289,7 +289,7 @@ dsh-pet/
|
|
|
289
289
|
PetSprite 浮层(portal + rAF)
|
|
290
290
|
```
|
|
291
291
|
|
|
292
|
-
- **状态来源**:宿主把官方 `turn/start`、`step/start`、`assistant/
|
|
292
|
+
- **状态来源**:宿主把官方 `turn/start`、`step/start`、`assistant/message`、`tool/call`、`tool/result`、`turn/end` 事件与实时 `agent/assistant-stream` 增量投影为 waiting/thinking/tool/review/done/failed 状态。可选兼容 `activity/status` 事件仍作为输入。
|
|
293
293
|
- **注册表**:宿主把每份 manifest 归一化为完整渲染定义(几何、每行帧数、每轨时长),经 `/api/pet/pets` 下发;浏览器半区用该定义渲染任意条目,不携带任何宠物专属代码。
|
|
294
294
|
- **选择与命名**:`petId` 存于设置命名空间;每只宠物的名字存于 `pet.json` 的 `names`,通过悬浮面板对当前宠物改名编辑。旧版安装的平铺 `name` 自动迁移到鲸鱼娘名下。
|
|
295
295
|
- **多会话语义**:API 与浏览器挂载都是宿主全局的,不暴露前台会话身份。并行会话各自保留投影状态:最近一次有意义事件驱动精灵动画,同时每个活动的顶层会话在独立气泡里报告自己的阶段(state 视图的 sessions 列表,最多保留最近 12 个)。子代理会话仍参与动画、计奖与单一显示气泡,但不占独立气泡位——N 个对话不会变成"N + 子代理数"的气泡堆。每个会话完成的轮次仍独立计奖;销毁会话移除它的气泡,销毁当前显示会话则回退到最近仍在活动的会话。
|
package/lib/client.js
CHANGED
|
@@ -2119,40 +2119,72 @@ window.__ModuleLoader__.load({
|
|
|
2119
2119
|
}
|
|
2120
2120
|
const decoding = /* @__PURE__ */ new Map();
|
|
2121
2121
|
const decodedAll = [];
|
|
2122
|
-
const
|
|
2122
|
+
const FRAME_POOL_LIMIT = 8;
|
|
2123
|
+
const frameQueue = [];
|
|
2124
|
+
let activeFrames = 0;
|
|
2125
|
+
const decodeFrame = async (url) => {
|
|
2126
|
+
try {
|
|
2127
|
+
const response = await fetch(url);
|
|
2128
|
+
if (!response.ok) throw new Error("http " + response.status);
|
|
2129
|
+
const bitmap = await createImageBitmap(await response.blob());
|
|
2130
|
+
return {
|
|
2131
|
+
source: bitmap,
|
|
2132
|
+
width: bitmap.width,
|
|
2133
|
+
height: bitmap.height
|
|
2134
|
+
};
|
|
2135
|
+
} catch {
|
|
2136
|
+
return await new Promise((resolve) => {
|
|
2137
|
+
try {
|
|
2138
|
+
const pre = new Image();
|
|
2139
|
+
pre.onload = () => {
|
|
2140
|
+
resolve(pre.naturalWidth > 0 ? {
|
|
2141
|
+
source: pre,
|
|
2142
|
+
width: pre.naturalWidth,
|
|
2143
|
+
height: pre.naturalHeight
|
|
2144
|
+
} : void 0);
|
|
2145
|
+
};
|
|
2146
|
+
pre.onerror = () => resolve(void 0);
|
|
2147
|
+
pre.src = url;
|
|
2148
|
+
} catch {
|
|
2149
|
+
resolve(void 0);
|
|
2150
|
+
}
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
};
|
|
2154
|
+
const pumpFrames = () => {
|
|
2155
|
+
while (activeFrames < FRAME_POOL_LIMIT && frameQueue.length > 0) {
|
|
2156
|
+
const queued = frameQueue.shift();
|
|
2157
|
+
activeFrames += 1;
|
|
2158
|
+
queued.release();
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
const loadFrame = (url, jump = false) => {
|
|
2162
|
+
if (jump) {
|
|
2163
|
+
const index = frameQueue.findIndex((queued) => queued.url === url);
|
|
2164
|
+
if (index > 0) frameQueue.unshift(frameQueue.splice(index, 1)[0]);
|
|
2165
|
+
}
|
|
2123
2166
|
const cached = decoding.get(url);
|
|
2124
2167
|
if (cached !== void 0) return cached;
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
return await new Promise((resolve) => {
|
|
2137
|
-
try {
|
|
2138
|
-
const pre = new Image();
|
|
2139
|
-
pre.onload = () => {
|
|
2140
|
-
resolve(pre.naturalWidth > 0 ? {
|
|
2141
|
-
source: pre,
|
|
2142
|
-
width: pre.naturalWidth,
|
|
2143
|
-
height: pre.naturalHeight
|
|
2144
|
-
} : void 0);
|
|
2145
|
-
};
|
|
2146
|
-
pre.onerror = () => resolve(void 0);
|
|
2147
|
-
pre.src = url;
|
|
2148
|
-
} catch {
|
|
2149
|
-
resolve(void 0);
|
|
2150
|
-
}
|
|
2151
|
-
});
|
|
2152
|
-
}
|
|
2153
|
-
})();
|
|
2168
|
+
let release;
|
|
2169
|
+
const job = new Promise((resolve) => {
|
|
2170
|
+
release = resolve;
|
|
2171
|
+
}).then(() => disposed ? void 0 : decodeFrame(url));
|
|
2172
|
+
job.then((frame) => {
|
|
2173
|
+
if (frame === void 0) decoding.delete(url);
|
|
2174
|
+
}, () => decoding.delete(url));
|
|
2175
|
+
job.finally(() => {
|
|
2176
|
+
activeFrames -= 1;
|
|
2177
|
+
pumpFrames();
|
|
2178
|
+
});
|
|
2154
2179
|
decoding.set(url, job);
|
|
2155
2180
|
decodedAll.push(job.then(() => void 0, () => void 0));
|
|
2181
|
+
const entry = {
|
|
2182
|
+
url,
|
|
2183
|
+
release
|
|
2184
|
+
};
|
|
2185
|
+
if (jump) frameQueue.unshift(entry);
|
|
2186
|
+
else frameQueue.push(entry);
|
|
2187
|
+
pumpFrames();
|
|
2156
2188
|
return job;
|
|
2157
2189
|
};
|
|
2158
2190
|
let disposed = false;
|
|
@@ -2175,7 +2207,7 @@ window.__ModuleLoader__.load({
|
|
|
2175
2207
|
const paintCanvas = (url) => {
|
|
2176
2208
|
if (context2d === null || canvas === null) return;
|
|
2177
2209
|
const myToken = ++drawToken;
|
|
2178
|
-
loadFrame(url).then((frame) => {
|
|
2210
|
+
loadFrame(url, true).then((frame) => {
|
|
2179
2211
|
if (disposed || frame === void 0 || myToken !== drawToken) return;
|
|
2180
2212
|
if (lastDrawnUrl === url) return;
|
|
2181
2213
|
lastDrawnUrl = url;
|
|
@@ -2245,7 +2277,11 @@ window.__ModuleLoader__.load({
|
|
|
2245
2277
|
const expected = (def.durations[frameIndex] ?? 200) + WATCHDOG_MS;
|
|
2246
2278
|
if (Date.now() - lastAdvance > expected) tick();
|
|
2247
2279
|
}, WATCHDOG_MS);
|
|
2248
|
-
|
|
2280
|
+
const warmTrackIds = [.../* @__PURE__ */ new Set([...Object.values(config.phases), config.phases.idle])];
|
|
2281
|
+
for (const warmTrack of [...warmTrackIds, ...Object.keys(config.tracks)].map((id) => config.tracks[id])) {
|
|
2282
|
+
if (warmTrack === void 0) continue;
|
|
2283
|
+
for (const warmUrl of warmTrack.frames) loadFrame(warmUrl);
|
|
2284
|
+
}
|
|
2249
2285
|
play(track);
|
|
2250
2286
|
let disposedOnce = false;
|
|
2251
2287
|
const dispose = () => {
|
|
@@ -2255,6 +2291,7 @@ window.__ModuleLoader__.load({
|
|
|
2255
2291
|
unsubscribe();
|
|
2256
2292
|
if (timer !== void 0) clearTimeout(timer);
|
|
2257
2293
|
if (watchdog !== void 0) clearInterval(watchdog);
|
|
2294
|
+
for (const queued of frameQueue.splice(0)) queued.release();
|
|
2258
2295
|
Promise.allSettled(decodedAll).then(() => {
|
|
2259
2296
|
for (const job of decoding.values()) job.then((frame) => {
|
|
2260
2297
|
try {
|
|
@@ -3619,7 +3656,7 @@ window.__ModuleLoader__.load({
|
|
|
3619
3656
|
/** The building package's version, when the bundle carries it. */
|
|
3620
3657
|
function bakedVersion() {
|
|
3621
3658
|
try {
|
|
3622
|
-
return "0.3.
|
|
3659
|
+
return "0.3.20";
|
|
3623
3660
|
} catch {
|
|
3624
3661
|
return;
|
|
3625
3662
|
}
|