@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/lib/index.js CHANGED
@@ -876,30 +876,6 @@ function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
876
876
  phase: "waiting",
877
877
  line: runtime.voice.scene("waiting", nowMs)
878
878
  } };
879
- case "assistant/chunk": {
880
- const { chunk } = event.data;
881
- if (chunk.type === "reasoning-delta" && chunk.text.length > 0) {
882
- const whisper = runtime.whispers.feed("thinking", nowMs);
883
- return {
884
- input: {
885
- phase: "thinking",
886
- line: runtime.voice.scene("thinking", nowMs)
887
- },
888
- ...whisper === void 0 ? {} : { whisper }
889
- };
890
- }
891
- if (chunk.type === "text-delta" && chunk.text.length > 0) {
892
- const whisper = runtime.whispers.feed("writing", nowMs);
893
- return {
894
- input: {
895
- phase: "review",
896
- line: runtime.voice.scene("review", nowMs)
897
- },
898
- ...whisper === void 0 ? {} : { whisper }
899
- };
900
- }
901
- return;
902
- }
903
879
  case "assistant/message": return { input: {
904
880
  phase: "review",
905
881
  line: runtime.voice.scene("review", nowMs)
@@ -990,6 +966,36 @@ function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
990
966
  default: return;
991
967
  }
992
968
  }
969
+ /**
970
+ * Project one live `agent/assistant-stream` publication into the pet's visual
971
+ * phases. Chunk frames are the alpha.2 replacement for the retired durable
972
+ * `assistant/chunk` event: a reasoning delta keeps the pet thinking, a text
973
+ * delta moves it to review; start, end, and non-delta chunks change nothing.
974
+ */
975
+ function projectAssistantStreamFrame(frame, runtime, nowMs = Date.now()) {
976
+ if (frame.type !== "chunk") return void 0;
977
+ const { chunk } = frame;
978
+ if (chunk.type === "reasoning-delta" && chunk.text.length > 0) {
979
+ const whisper = runtime.whispers.feed("thinking", nowMs);
980
+ return {
981
+ input: {
982
+ phase: "thinking",
983
+ line: runtime.voice.scene("thinking", nowMs)
984
+ },
985
+ ...whisper === void 0 ? {} : { whisper }
986
+ };
987
+ }
988
+ if (chunk.type === "text-delta" && chunk.text.length > 0) {
989
+ const whisper = runtime.whispers.feed("writing", nowMs);
990
+ return {
991
+ input: {
992
+ phase: "review",
993
+ line: runtime.voice.scene("review", nowMs)
994
+ },
995
+ ...whisper === void 0 ? {} : { whisper }
996
+ };
997
+ }
998
+ }
993
999
  //#endregion
994
1000
  //#region src/treats.ts
995
1001
  const defaultTreatConfig = {
@@ -4474,39 +4480,51 @@ var PetService = class extends Service {
4474
4480
  }
4475
4481
  if (!this.enabled) return;
4476
4482
  this.disposeActivity = (() => {
4477
- const disposers = [this.ctx.on("session/event", (session, event) => {
4478
- const runtime = this.activityOf(session).runtime;
4479
- if (event.type === "activity/status") {
4480
- const payload = event.data ?? {};
4481
- if (typeof payload.phase !== "string" || !isActivityPhase(payload.phase)) return;
4482
- this.applyActivity(session, {
4483
- phase: payload.phase,
4484
- ...typeof payload.line === "string" ? { line: payload.line } : {},
4485
- ...typeof payload.phrase === "string" ? { phrase: payload.phrase } : {}
4486
- });
4487
- if (payload.phase === "done" && !runtime.officialEventsSeen) this.rewardLegacyTurn();
4488
- return;
4489
- }
4490
- const transition = projectOfficialEvent(event, runtime);
4491
- if (transition === void 0) return;
4492
- runtime.officialEventsSeen = true;
4493
- this.officialEventSessions.add(session);
4494
- this.applyActivity(session, transition.input, transition.whisper);
4495
- if (transition.completedTurn !== void 0) this.rewardTurn(String(session.id), transition.completedTurn);
4496
- }), this.ctx.on("session/disposed", (session) => {
4497
- this.ledger.forgetSession(String(session.id));
4498
- this.officialEventSessions.delete(session);
4499
- this.sessionActivity.delete(session);
4500
- if (session !== this.displaySession) return;
4501
- this.displaySession = void 0;
4502
- const remaining = [...this.sessionActivity.entries()].at(-1);
4503
- if (remaining !== void 0) {
4504
- const [nextSession, activity] = remaining;
4505
- this.displaySession = nextSession;
4506
- if (activity.lastInput !== void 0) this.machine.onActivityStatus(activity.lastInput);
4507
- this.machine.onSessionActive();
4508
- } else this.machine.onSessionDisposed();
4509
- })];
4483
+ const disposers = [
4484
+ this.ctx.on("session/event", (session, event) => {
4485
+ const runtime = this.activityOf(session).runtime;
4486
+ if (event.type === "activity/status") {
4487
+ const payload = event.data ?? {};
4488
+ if (typeof payload.phase !== "string" || !isActivityPhase(payload.phase)) return;
4489
+ this.applyActivity(session, {
4490
+ phase: payload.phase,
4491
+ ...typeof payload.line === "string" ? { line: payload.line } : {},
4492
+ ...typeof payload.phrase === "string" ? { phrase: payload.phrase } : {}
4493
+ });
4494
+ if (payload.phase === "done" && !runtime.officialEventsSeen) this.rewardLegacyTurn();
4495
+ return;
4496
+ }
4497
+ const transition = projectOfficialEvent(event, runtime);
4498
+ if (transition === void 0) return;
4499
+ runtime.officialEventsSeen = true;
4500
+ this.officialEventSessions.add(session);
4501
+ this.applyActivity(session, transition.input, transition.whisper);
4502
+ if (transition.completedTurn !== void 0) this.rewardTurn(String(session.id), transition.completedTurn);
4503
+ }),
4504
+ this.ctx.on("agent/assistant-stream", ({ agent, frame }) => {
4505
+ const session = agent.session;
4506
+ const runtime = this.activityOf(session).runtime;
4507
+ const transition = projectAssistantStreamFrame(frame, runtime);
4508
+ if (transition === void 0) return;
4509
+ runtime.officialEventsSeen = true;
4510
+ this.officialEventSessions.add(session);
4511
+ this.applyActivity(session, transition.input, transition.whisper);
4512
+ }),
4513
+ this.ctx.on("session/disposed", (session) => {
4514
+ this.ledger.forgetSession(String(session.id));
4515
+ this.officialEventSessions.delete(session);
4516
+ this.sessionActivity.delete(session);
4517
+ if (session !== this.displaySession) return;
4518
+ this.displaySession = void 0;
4519
+ const remaining = [...this.sessionActivity.entries()].at(-1);
4520
+ if (remaining !== void 0) {
4521
+ const [nextSession, activity] = remaining;
4522
+ this.displaySession = nextSession;
4523
+ if (activity.lastInput !== void 0) this.machine.onActivityStatus(activity.lastInput);
4524
+ this.machine.onSessionActive();
4525
+ } else this.machine.onSessionDisposed();
4526
+ })
4527
+ ];
4510
4528
  return () => {
4511
4529
  for (const dispose of disposers) dispose();
4512
4530
  };
@@ -1 +1 @@
1
- {"version":3,"file":"frames2d.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/frames2d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAA4B,KAAK,WAAW,EAA2B,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACzI,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;IAC3C,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACjE,wEAAwE;IACxE,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAA;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,YAAY,CAAC,EAAE,6BAA6B,EAAE,CAAA;IAC9C,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACxC;AAED,4DAA4D;AAC5D,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IACzC;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IAC7C,2DAA2D;IAC3D,YAAY,IAAI,MAAM,CAAA;CACvB;AAuFD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,iBAAiB,CA2Q3D,CAAA"}
1
+ {"version":3,"file":"frames2d.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/frames2d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAA4B,KAAK,WAAW,EAA2B,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACzI,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;IAC3C,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACjE,wEAAwE;IACxE,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAA;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,YAAY,CAAC,EAAE,6BAA6B,EAAE,CAAA;IAC9C,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACxC;AAED,4DAA4D;AAC5D,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IACzC;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IAC7C,2DAA2D;IAC3D,YAAY,IAAI,MAAM,CAAA;CACvB;AAuFD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,iBAAiB,CA6T3D,CAAA"}
@@ -159,37 +159,75 @@ export const frames2dRenderer = {
159
159
  // the last painted frame instead of breaking playback).
160
160
  const decoding = new Map();
161
161
  const decodedAll = [];
162
- const loadFrame = (url) => {
162
+ // All frame fetches funnel through a small pool. Full-library warm passes
163
+ // on large pets fire 1100+ requests at once, which trips the browser's
164
+ // in-flight request limit (net::ERR_INSUFFICIENT_RESOURCES) and fails
165
+ // whole batches of frames while starving the rest of the page. Playback
166
+ // demand jumps ahead of the warm backlog.
167
+ const FRAME_POOL_LIMIT = 8;
168
+ const frameQueue = [];
169
+ let activeFrames = 0;
170
+ const decodeFrame = async (url) => {
171
+ try {
172
+ const response = await fetch(url);
173
+ if (!response.ok)
174
+ throw new Error('http ' + response.status);
175
+ const bitmap = await createImageBitmap(await response.blob());
176
+ return { source: bitmap, width: bitmap.width, height: bitmap.height };
177
+ }
178
+ catch {
179
+ // Fail-open: classic Image decode keeps non-modern runtimes alive.
180
+ return await new Promise((resolve) => {
181
+ try {
182
+ const pre = new Image();
183
+ pre.onload = () => {
184
+ resolve(pre.naturalWidth > 0 ? { source: pre, width: pre.naturalWidth, height: pre.naturalHeight } : undefined);
185
+ };
186
+ pre.onerror = () => resolve(undefined);
187
+ pre.src = url;
188
+ }
189
+ catch {
190
+ resolve(undefined);
191
+ }
192
+ });
193
+ }
194
+ };
195
+ const pumpFrames = () => {
196
+ while (activeFrames < FRAME_POOL_LIMIT && frameQueue.length > 0) {
197
+ const queued = frameQueue.shift();
198
+ activeFrames += 1;
199
+ queued.release();
200
+ }
201
+ };
202
+ const loadFrame = (url, jump = false) => {
203
+ // Jump first: warm-enqueued frames already carry their memo, so a
204
+ // playback demand must reorder the unstarted entry before the cache
205
+ // lookup short-circuits.
206
+ if (jump) {
207
+ const index = frameQueue.findIndex((queued) => queued.url === url);
208
+ if (index > 0)
209
+ frameQueue.unshift(frameQueue.splice(index, 1)[0]);
210
+ }
163
211
  const cached = decoding.get(url);
164
212
  if (cached !== undefined)
165
213
  return cached;
166
- const job = (async () => {
167
- try {
168
- const response = await fetch(url);
169
- if (!response.ok)
170
- throw new Error('http ' + response.status);
171
- const bitmap = await createImageBitmap(await response.blob());
172
- return { source: bitmap, width: bitmap.width, height: bitmap.height };
173
- }
174
- catch {
175
- // Fail-open: classic Image decode keeps non-modern runtimes alive.
176
- return await new Promise((resolve) => {
177
- try {
178
- const pre = new Image();
179
- pre.onload = () => {
180
- resolve(pre.naturalWidth > 0 ? { source: pre, width: pre.naturalWidth, height: pre.naturalHeight } : undefined);
181
- };
182
- pre.onerror = () => resolve(undefined);
183
- pre.src = url;
184
- }
185
- catch {
186
- resolve(undefined);
187
- }
188
- });
189
- }
190
- })();
214
+ let release;
215
+ const gate = new Promise((resolve) => { release = resolve; });
216
+ const job = gate.then(() => (disposed ? undefined : decodeFrame(url)));
217
+ job.then((frame) => { if (frame === undefined)
218
+ decoding.delete(url); }, () => decoding.delete(url));
219
+ void job.finally(() => {
220
+ activeFrames -= 1;
221
+ pumpFrames();
222
+ });
191
223
  decoding.set(url, job);
192
224
  decodedAll.push(job.then(() => undefined, () => undefined));
225
+ const entry = { url, release };
226
+ if (jump)
227
+ frameQueue.unshift(entry);
228
+ else
229
+ frameQueue.push(entry);
230
+ pumpFrames();
193
231
  return job;
194
232
  };
195
233
  let disposed = false;
@@ -217,7 +255,7 @@ export const frames2dRenderer = {
217
255
  if (context2d === null || canvas === null)
218
256
  return;
219
257
  const myToken = ++drawToken;
220
- void loadFrame(url).then((frame) => {
258
+ void loadFrame(url, true).then((frame) => {
221
259
  if (disposed || frame === undefined || myToken !== drawToken)
222
260
  return;
223
261
  if (lastDrawnUrl === url)
@@ -323,7 +361,14 @@ export const frames2dRenderer = {
323
361
  // Warm pass: decode every frame up front (tiny same-origin webp files)
324
362
  // so loops and phase switches never wait on a first decode - same intent
325
363
  // as the historical Image-cache warm loop, now feeding the decode cache.
326
- for (const warmTrack of Object.values(config.tracks)) {
364
+ // Phase-reachable tracks enqueue first so early switches never trail the
365
+ // full warm backlog; demand loads jump the queue regardless.
366
+ const warmTrackIds = [
367
+ ...new Set([...Object.values(config.phases), config.phases.idle]),
368
+ ];
369
+ for (const warmTrack of [...warmTrackIds, ...Object.keys(config.tracks)].map((id) => config.tracks[id])) {
370
+ if (warmTrack === undefined)
371
+ continue;
327
372
  for (const warmUrl of warmTrack.frames)
328
373
  void loadFrame(warmUrl);
329
374
  }
@@ -340,7 +385,10 @@ export const frames2dRenderer = {
340
385
  if (watchdog !== undefined)
341
386
  clearInterval(watchdog);
342
387
  // Release decoded bitmaps after pending decodes settle; close() is
343
- // browser-only, so guard it for exotic hosts.
388
+ // browser-only, so guard it for exotic hosts. Queued-but-unstarted
389
+ // frames release immediately as no-ops so the settle barrier drains.
390
+ for (const queued of frameQueue.splice(0))
391
+ queued.release();
344
392
  void Promise.allSettled(decodedAll).then(() => {
345
393
  for (const job of decoding.values()) {
346
394
  void job.then((frame) => {
@@ -11,8 +11,15 @@
11
11
  * tool/result and turn/end — so the pet's inner voice always roughly knows
12
12
  * what is going on and never mis-fires on output text. The wall clock is
13
13
  * injected by the caller, keeping every projection reproducible.
14
+ *
15
+ * Since the 0.1.5-alpha.2 cohort the stream itself is no longer durable
16
+ * vocabulary: per-chunk phase input arrives through the process-local
17
+ * `agent/assistant-stream` publication ({@link projectAssistantStreamFrame}),
18
+ * while the durable log settles one `assistant/message` (or `assistant/attempt`)
19
+ * per attempt.
14
20
  * @module @linxin666/dsh-pet/event-projection
15
21
  */
22
+ import type { AssistantStreamFrame } from '@deepseek-ai/dsh-agent';
16
23
  import type { SessionEvent } from '@deepseek-ai/dsh-session';
17
24
  import type { PetStateInput } from './state.ts';
18
25
  import { StatusVoice, WhisperEngine, type VoicePoolsProvider } from './chatter.ts';
@@ -56,4 +63,11 @@ export declare function isActivityPhase(phase: string): phase is PetStateInput['
56
63
  * @param nowMs - injected wall clock for copy rotation and whisper pacing.
57
64
  */
58
65
  export declare function projectOfficialEvent(event: SessionEvent, runtime: ProjectionRuntime, nowMs?: number): PetActivityTransition | undefined;
66
+ /**
67
+ * Project one live `agent/assistant-stream` publication into the pet's visual
68
+ * phases. Chunk frames are the alpha.2 replacement for the retired durable
69
+ * `assistant/chunk` event: a reasoning delta keeps the pet thinking, a text
70
+ * delta moves it to review; start, end, and non-delta chunks change nothing.
71
+ */
72
+ export declare function projectAssistantStreamFrame(frame: AssistantStreamFrame, runtime: ProjectionRuntime, nowMs?: number): PetActivityTransition | undefined;
59
73
  //# sourceMappingURL=event-projection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-projection.d.ts","sourceRoot":"","sources":["../../src/event-projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EACL,WAAW,EAGX,aAAa,EAGb,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAA;AAErB,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,+EAA+E;IAC/E,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACtB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,cAAc,EAAE,OAAO,CAAA;IACvB,qEAAqE;IACrE,KAAK,EAAE,WAAW,CAAA;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,aAAa,CAAA;CACxB;AAED,kFAAkF;AAClF,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,aAAa,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CASpF;AAQD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,iBAAiB,EAC1B,KAAK,GAAE,MAAmB,GACzB,qBAAqB,GAAG,SAAS,CAmHnC"}
1
+ {"version":3,"file":"event-projection.d.ts","sourceRoot":"","sources":["../../src/event-projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EACL,WAAW,EAGX,aAAa,EAGb,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAA;AAErB,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,+EAA+E;IAC/E,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACtB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,cAAc,EAAE,OAAO,CAAA;IACvB,qEAAqE;IACrE,KAAK,EAAE,WAAW,CAAA;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,aAAa,CAAA;CACxB;AAED,kFAAkF;AAClF,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,aAAa,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CASpF;AAQD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,iBAAiB,EAC1B,KAAK,GAAE,MAAmB,GACzB,qBAAqB,GAAG,SAAS,CAiGnC;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,GAAE,MAAmB,GACzB,qBAAqB,GAAG,SAAS,CAkBnC"}
@@ -11,6 +11,12 @@
11
11
  * tool/result and turn/end — so the pet's inner voice always roughly knows
12
12
  * what is going on and never mis-fires on output text. The wall clock is
13
13
  * injected by the caller, keeping every projection reproducible.
14
+ *
15
+ * Since the 0.1.5-alpha.2 cohort the stream itself is no longer durable
16
+ * vocabulary: per-chunk phase input arrives through the process-local
17
+ * `agent/assistant-stream` publication ({@link projectAssistantStreamFrame}),
18
+ * while the durable log settles one `assistant/message` (or `assistant/attempt`)
19
+ * per attempt.
14
20
  * @module @linxin666/dsh-pet/event-projection
15
21
  */
16
22
  import { StatusVoice, toolArgHint, toolCategory, WhisperEngine, whisperCategoryOf, looksLikeTestTool, } from "./chatter.js";
@@ -55,24 +61,6 @@ export function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
55
61
  runtime.activeTools.clear();
56
62
  runtime.stepHadFailure = false;
57
63
  return { input: { phase: 'waiting', line: runtime.voice.scene('waiting', nowMs) } };
58
- case 'assistant/chunk': {
59
- const { chunk } = event.data;
60
- if (chunk.type === 'reasoning-delta' && chunk.text.length > 0) {
61
- const whisper = runtime.whispers.feed('thinking', nowMs);
62
- return {
63
- input: { phase: 'thinking', line: runtime.voice.scene('thinking', nowMs) },
64
- ...(whisper === undefined ? {} : { whisper }),
65
- };
66
- }
67
- if (chunk.type === 'text-delta' && chunk.text.length > 0) {
68
- const whisper = runtime.whispers.feed('writing', nowMs);
69
- return {
70
- input: { phase: 'review', line: runtime.voice.scene('review', nowMs) },
71
- ...(whisper === undefined ? {} : { whisper }),
72
- };
73
- }
74
- return undefined;
75
- }
76
64
  case 'assistant/message':
77
65
  return { input: { phase: 'review', line: runtime.voice.scene('review', nowMs) } };
78
66
  case 'tool/call': {
@@ -156,3 +144,29 @@ export function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
156
144
  return undefined;
157
145
  }
158
146
  }
147
+ /**
148
+ * Project one live `agent/assistant-stream` publication into the pet's visual
149
+ * phases. Chunk frames are the alpha.2 replacement for the retired durable
150
+ * `assistant/chunk` event: a reasoning delta keeps the pet thinking, a text
151
+ * delta moves it to review; start, end, and non-delta chunks change nothing.
152
+ */
153
+ export function projectAssistantStreamFrame(frame, runtime, nowMs = Date.now()) {
154
+ if (frame.type !== 'chunk')
155
+ return undefined;
156
+ const { chunk } = frame;
157
+ if (chunk.type === 'reasoning-delta' && chunk.text.length > 0) {
158
+ const whisper = runtime.whispers.feed('thinking', nowMs);
159
+ return {
160
+ input: { phase: 'thinking', line: runtime.voice.scene('thinking', nowMs) },
161
+ ...(whisper === undefined ? {} : { whisper }),
162
+ };
163
+ }
164
+ if (chunk.type === 'text-delta' && chunk.text.length > 0) {
165
+ const whisper = runtime.whispers.feed('writing', nowMs);
166
+ return {
167
+ input: { phase: 'review', line: runtime.voice.scene('review', nowMs) },
168
+ ...(whisper === undefined ? {} : { whisper }),
169
+ };
170
+ }
171
+ return undefined;
172
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { Service } from '@deepseek-ai/cordis';
15
15
  import { announcementFresh, parseAnnouncement } from "./announce.js";
16
- import { emptyProjectionRuntime, isActivityPhase, projectOfficialEvent, } from "./event-projection.js";
16
+ import { emptyProjectionRuntime, isActivityPhase, projectAssistantStreamFrame, projectOfficialEvent, } from "./event-projection.js";
17
17
  import { PetLedger } from "./ledger.js";
18
18
  import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, loadPetPersist, petHomeDir, savePetPersist, } from "./persist.js";
19
19
  import { DEFAULT_DECORATION_ID, decorationView, loadPetRegistry, petEntryView, petPackageRoot, } from "./registry.js";
@@ -236,6 +236,16 @@ export class PetService extends Service {
236
236
  this.rewardTurn(String(session.id), transition.completedTurn);
237
237
  }
238
238
  }),
239
+ this.ctx.on('agent/assistant-stream', ({ agent, frame }) => {
240
+ const session = agent.session;
241
+ const runtime = this.activityOf(session).runtime;
242
+ const transition = projectAssistantStreamFrame(frame, runtime);
243
+ if (transition === undefined)
244
+ return;
245
+ runtime.officialEventsSeen = true;
246
+ this.officialEventSessions.add(session);
247
+ this.applyActivity(session, transition.input, transition.whisper);
248
+ }),
239
249
  this.ctx.on('session/disposed', (session) => {
240
250
  this.ledger.forgetSession(String(session.id));
241
251
  this.officialEventSessions.delete(session);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-pet",
3
3
  "description": "Multi-pet companion plugin for the dsh web GUI: a registry-driven floating pet that reacts to model activity, with per-pet naming, petting/feeding interactions and an affinity score",
4
- "version": "0.3.18",
4
+ "version": "0.3.20",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -41,7 +41,7 @@
41
41
  "license": "Apache-2.0",
42
42
  "dsh": {
43
43
  "engines": {
44
- "dsh": ">=0.1.2-rc.1"
44
+ "dsh": ">=0.1.5-rc.1"
45
45
  },
46
46
  "bundle": {
47
47
  "patch": "./cordis.patch.yml"
@@ -66,18 +66,18 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@deepseek-ai/cordis": "^4.0.2",
69
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-rc.1",
70
- "@deepseek-ai/dsh-api-session-controller": "^0.1.2-rc.1",
71
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-rc.1",
72
- "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
73
- "@deepseek-ai/dsh-client-store": "^0.1.2-rc.1",
74
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
75
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
76
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
77
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
78
- "@deepseek-ai/dsh-host-webserver": "^0.1.2-rc.1",
79
- "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
80
- "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
69
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.1",
70
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.1",
71
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-rc.1",
72
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1",
73
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.1",
74
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.1",
75
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
76
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1",
77
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
78
+ "@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.1",
79
+ "@deepseek-ai/dsh-session": "^0.1.5-rc.1",
80
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
81
81
  "@testing-library/dom": "^10.4.1",
82
82
  "@testing-library/react": "^16.3.2",
83
83
  "@types/node": "^22.20.0",
@@ -182,10 +182,8 @@ describe('frames2dRenderer', () => {
182
182
  describe('frames2dRenderer canvas bitmap path', () => {
183
183
  // The canvas branch needs createImageBitmap + fetch + a real 2D context;
184
184
  // jsdom has none of them, so each test installs fakes and restores after.
185
- const flush = async (): Promise<void> => {
186
- await Promise.resolve()
187
- await Promise.resolve()
188
- await Promise.resolve()
185
+ const flush = async (rounds = 12): Promise<void> => {
186
+ for (let i = 0; i < rounds; i += 1) await Promise.resolve()
189
187
  }
190
188
 
191
189
  let draws = 0
@@ -278,4 +276,96 @@ describe('frames2dRenderer canvas bitmap path', () => {
278
276
  expect(draws).toBeGreaterThanOrEqual(3)
279
277
  handle.dispose()
280
278
  })
279
+
280
+ it('drains the warm pass through a bounded fetch pool', async () => {
281
+ let inFlight = 0
282
+ let peak = 0
283
+ const gates: Array<() => void> = []
284
+ vi.stubGlobal('fetch', vi.fn(async () => {
285
+ inFlight += 1
286
+ peak = Math.max(peak, inFlight)
287
+ await new Promise<void>((resolve) => gates.push(resolve))
288
+ inFlight -= 1
289
+ return { ok: true, blob: async () => ({}) }
290
+ }))
291
+ const bigConfig: PetFrames2dConfig = {
292
+ tracks: {
293
+ idle: { frames: Array.from({ length: 24 }, (_, i) => `/pet/miku/idle/${i}.webp`), durations: Array.from({ length: 24 }, () => 100), loop: true },
294
+ },
295
+ phases: { idle: 'idle' },
296
+ }
297
+ const { ctx } = canvasSetup()
298
+ const handle = frames2dRenderer.mount(ctx, frames2dRenderer.validateConfig(bigConfig)) as Frames2dRendererHandle
299
+ await flush()
300
+ expect(peak).toBeLessThanOrEqual(8)
301
+ expect(gates.length).toBe(8)
302
+ for (let i = 0; i < 64 && gates.length > 0; i += 1) {
303
+ gates.shift()!()
304
+ await flush()
305
+ }
306
+ expect(peak).toBeLessThanOrEqual(8)
307
+ expect(bitmaps.length).toBe(24)
308
+ handle.dispose()
309
+ })
310
+
311
+ it('jumps a playback-demand frame ahead of the warm backlog', async () => {
312
+ const fetched: string[] = []
313
+ const held = new Map<string, () => void>()
314
+ vi.stubGlobal('fetch', vi.fn(async (input: unknown) => {
315
+ const url = String(input)
316
+ fetched.push(url)
317
+ if (fetched.length <= 8) {
318
+ await new Promise<void>((resolve) => { held.set(url, resolve) })
319
+ }
320
+ return { ok: true, blob: async () => ({}) }
321
+ }))
322
+ const wideConfig: PetFrames2dConfig = {
323
+ tracks: {
324
+ idle: { frames: Array.from({ length: 10 }, (_, i) => `/pet/miku/idle/${i}.webp`), durations: Array.from({ length: 10 }, () => 100), loop: true },
325
+ happy: { frames: ['/pet/miku/happy/1.webp'], durations: [100], loop: false, fallback: 'idle' },
326
+ },
327
+ phases: { idle: 'idle', done: 'happy' },
328
+ }
329
+ const { ctx } = canvasSetup()
330
+ const handle = frames2dRenderer.mount(ctx, frames2dRenderer.validateConfig(wideConfig)) as Frames2dRendererHandle
331
+ await flush()
332
+ expect(fetched.length).toBe(8)
333
+ // The happy frame sits behind the idle backlog; playing it must pull its
334
+ // fetch ahead of the not-yet-started warm frames.
335
+ handle.setState('happy')
336
+ await flush()
337
+ // Free one pool slot: the released idle decode completes and the freed
338
+ // slot must start the jumped happy frame, not the next warm idle frame.
339
+ held.get(fetched[0])?.()
340
+ await flush()
341
+ expect(fetched[8]).toBe('/pet/miku/happy/1.webp')
342
+ for (const release of held.values()) release()
343
+ await flush()
344
+ handle.dispose()
345
+ })
346
+
347
+ it('retries a failed frame instead of memoizing the failure forever', async () => {
348
+ let failuresLeft = 1
349
+ vi.stubGlobal('fetch', vi.fn(async () => {
350
+ if (failuresLeft > 0) {
351
+ failuresLeft -= 1
352
+ throw new Error('transient')
353
+ }
354
+ return { ok: true, blob: async () => ({}) }
355
+ }))
356
+ class FailingImage {
357
+ onload: (() => void) | null = null
358
+ onerror: (() => void) | null = null
359
+ set src(_value: string) { queueMicrotask(() => this.onerror?.()) }
360
+ }
361
+ vi.stubGlobal('Image', FailingImage)
362
+ const { ctx } = canvasSetup()
363
+ const handle = frames2dRenderer.mount(ctx, frames2dRenderer.validateConfig(CONFIG)) as Frames2dRendererHandle
364
+ // Warm pass fetches fail through the pool, resolve undefined and are
365
+ // dropped from the memo; nothing throws.
366
+ await flush()
367
+ const memoFetches = (fetch as ReturnType<typeof vi.fn>).mock.calls.length
368
+ expect(memoFetches).toBeGreaterThan(0)
369
+ handle.dispose()
370
+ })
281
371
  })